diff options
91 files changed, 7472 insertions, 4486 deletions
@@ -37,6 +37,7 @@ src/cell_explorer src/ambigator src/whirligig src/geoptimiser +src/list_events src/.dirstamp *~ doc/reference/libcrystfel/* @@ -55,3 +56,5 @@ lib/dummy.lo lib/libgnu.la libcrystfel/crystfel.pc version.h +tests/*.log +tests/*.trs @@ -61,4 +61,4 @@ Hit rate calculator (doc/hitrate.{html,png}) * Keitaro Yamashita <k.yamashita@spring8.or.jp> - process_hkl bug fix + Bug fixes @@ -1,10 +1,32 @@ -Changes in this development version ------------------------------------ +CrystFEL version 0.6.0, 23rd February 2015 +------------------------------------------ -- The geometry specification was extended to allow multiple events per HDF5 file - as well as different panels in different HDF5 locations. +- The geometry specification was extended to allow multiple events per HDF5 + file as well as different panels in different HDF5 locations. +- geoptimiser, list_events and whirligig were added. +- CrystFEL unit cell files were introduced. +- Beam parameter files are no longer used. +- indexamajig auto-determination of prediction parameters was added. +- indexamajig --fix-profile-radius, --fix-bandwidth and --fix-divergence were + added. +- pattern_sim --beam-bandwidth, --photon-energy and --nphotons were added. +- partial_sim --beam-bandwidth, --photon-energy, --beam-divergence and + --profile-radius were added. +- Event serial numbers are now written in streams. +- indexamajig -e/--image was removed. +- process_hkl --stat and --min-cc were added. +- render_hkl --highres and --no-axes were added. +- partialator --max-adu was added. +- 2D profile fitting (--integration=prof2d) was fixed. +- CSPAD geometry file examples were improved. +- compare_hkl --fom=d1sig and d2sig were added. +- pattern_sim now simulates detector saturation. +- Compression was switched off when saving HDF5 files. +- The units conversion for the resolution limit written into the stream was + fixed. +- The pre-merging Poission error calculation was improved. - A bug in pattern_sim's handling of symmetry was fixed. -- The documentation for indexamajig --tolerance was updated. +- {check,compare}_hkl now report the min/max bin ranges. CrystFEL version 0.5.4, 5th September 2014 diff --git a/Makefile.am b/Makefile.am index 3aca82ed..da210d47 100644 --- a/Makefile.am +++ b/Makefile.am @@ -5,13 +5,14 @@ ACLOCAL_AMFLAGS = -I m4 bin_PROGRAMS = src/pattern_sim src/process_hkl src/get_hkl src/indexamajig \ src/compare_hkl src/partialator src/check_hkl src/partial_sim \ - src/ambigator src/geoptimiser src/whirligig + src/ambigator src/geoptimiser src/whirligig src/list_events noinst_PROGRAMS = tests/list_check tests/integration_check \ tests/pr_p_gradient_check tests/symmetry_check \ tests/centering_check tests/transformation_check \ tests/cell_check tests/ring_check \ - tests/prof2d_check tests/ambi_check + tests/prof2d_check tests/ambi_check \ + tests/prediction_gradient_check MERGE_CHECKS = tests/first_merge_check tests/second_merge_check \ tests/third_merge_check tests/fourth_merge_check @@ -24,9 +25,11 @@ PARTIAL_CHECKS = tests/partialator_merge_check_1 \ TESTS = tests/list_check $(MERGE_CHECKS) $(PARTIAL_CHECKS) \ tests/integration_check \ tests/symmetry_check tests/centering_check tests/transformation_check \ - tests/cell_check tests/ring_check tests/prof2d_check tests/ambi_check + tests/cell_check tests/ring_check tests/prof2d_check tests/ambi_check \ + tests/prediction_gradient_check EXTRA_DIST += $(MERGE_CHECKS) $(PARTIAL_CHECKS) +EXTRA_DIST += relnotes-0.6.0 if BUILD_HDFSEE bin_PROGRAMS += src/hdfsee @@ -68,7 +71,10 @@ endif src_process_hkl_SOURCES = src/process_hkl.c -src_indexamajig_SOURCES = src/indexamajig.c src/im-sandbox.c src/process_image.c +src_list_events_SOURCES = src/list_events.c + +src_indexamajig_SOURCES = src/indexamajig.c src/im-sandbox.c \ + src/process_image.c src/predict-refine.c if BUILD_HDFSEE src_hdfsee_SOURCES = src/hdfsee.c src/dw-hdfsee.c src/hdfsee-render.c @@ -89,22 +95,14 @@ src_render_hkl_SOURCES = src/render_hkl.c endif src_partialator_SOURCES = src/partialator.c src/post-refinement.c \ - src/hrs-scaling.c + src/merge.c src/rejection.c src_ambigator_SOURCES = src/ambigator.c -src_geoptimiser_SOURCES = src/geoptimiser.c +src_geoptimiser_SOURCES = src/geoptimiser.c src/hdfsee-render.c src_whirligig_SOURCES = src/whirligig.c -if HAVE_CAIRO -if HAVE_PANGO -if HAVE_PANGOCAIRO -src_partialator_SOURCES += src/scaling-report.c -endif -endif -endif - tests_list_check_SOURCES = tests/list_check.c tests_integration_check_SOURCES = tests/integration_check.c @@ -118,6 +116,8 @@ tests_ambi_check_SOURCES = tests/ambi_check.c tests_pr_p_gradient_check_SOURCES = tests/pr_p_gradient_check.c \ src/post-refinement.c +tests_prediction_gradient_check_SOURCES = tests/prediction_gradient_check.c + tests_centering_check_SOURCES = tests/centering_check.c tests_transformation_check_SOURCES = tests/transformation_check.c @@ -129,10 +129,11 @@ tests_cell_check_SOURCES = tests/cell_check.c INCLUDES = -I$(top_srcdir)/libcrystfel/src -I$(top_srcdir)/data EXTRA_DIST += src/dw-hdfsee.h src/hdfsee.h src/render_hkl.h \ - src/post-refinement.h src/hrs-scaling.h src/scaling-report.h \ + src/post-refinement.h src/merge.h \ src/cl-utils.h src/hdfsee-render.h src/diffraction.h \ src/diffraction-gpu.h src/pattern_sim.h src/list_tmp.h \ - src/im-sandbox.h src/process_image.h src/multihistogram.h + src/im-sandbox.h src/process_image.h src/multihistogram.h \ + src/rejection.h src/predict-refine.h crystfeldir = $(datadir)/crystfel crystfel_DATA = data/diffraction.cl data/hdfsee.ui @@ -143,7 +144,8 @@ man_MANS = doc/man/crystfel.7 doc/man/check_hkl.1 doc/man/compare_hkl.1 \ doc/man/crystfel_geometry.5 doc/man/get_hkl.1 doc/man/hdfsee.1 \ doc/man/indexamajig.1 doc/man/partialator.1 doc/man/partial_sim.1 \ doc/man/pattern_sim.1 doc/man/process_hkl.1 doc/man/render_hkl.1 \ - doc/man/cell_explorer.1 doc/man/ambigator.1 + doc/man/cell_explorer.1 doc/man/ambigator.1 doc/man/geoptimiser.1 \ + doc/man/whirligig.1 EXTRA_DIST += $(man_MANS) @@ -153,10 +155,11 @@ crystfeldoc_DATA = doc/twin-calculator.pdf doc/examples/lcls-dec.geom \ doc/examples/lcls-june-r0013-r0128.geom \ doc/examples/lcls-xpp-estimate.geom \ doc/examples/simple.geom \ - doc/examples/cspad-feb2011.geom \ + doc/examples/cspad-single.geom \ + doc/examples/cspad-cxiformat.geom \ doc/examples/pilatus.geom \ doc/examples/cell-example.cell \ - doc/hitrate.html doc/hitrate.png + doc/hitrate.html doc/hitrate.png EXTRA_DIST += $(crystfeldoc_DATA) doc/twin-calculator.odt \ doc/reference/libcrystfel/xml/coding-standards.xml @@ -174,7 +177,10 @@ script_DATA = scripts/alternate-stream scripts/cell-please \ scripts/Rsplit_surface scripts/Rsplit_surface.py \ scripts/clean-stream.py scripts/fg-graph scripts/truncate-stream \ scripts/gen-sfs-expand scripts/add-beam-params \ - scripts/find-pairs scripts/plot-cc-and-scale.R + scripts/find-pairs scripts/plot-cc-and-scale.R \ + scripts/ave-resolution scripts/crystal-frame-number \ + scripts/plot-radius-resolution scripts/plot-predict-refine \ + scripts/detector-shift EXTRA_DIST += $(script_DATA) @@ -1,7 +1,7 @@ CrystFEL - Crystallography with a FEL ------------------------------------- -Copyright © 2012-2014 Deutsches Elektronen-Synchrotron DESY, +Copyright © 2012-2015 Deutsches Elektronen-Synchrotron DESY, a research centre of the Helmholtz Association. Authors: @@ -18,16 +18,25 @@ Authors: Anton Barty <anton.barty@desy.de> Cornelius Gati <cornelius.gati@desy.de> Fedor Chervinskii <fedor.chervinskii@gmail.com> - Alexandra Tolstikova + Alexandra Tolstikova <alexandra.tolstikova@desy.de> Wolfgang Brehm <wolfgang.brehm@gmail.com> Valerio Mariani <valerio.mariani@desy.de> Parker de Waal <Parker.deWaal@vai.org> Takanori Nakane <nakane.t@gmail.com> Keitaro Yamashita <k.yamashita@spring8.or.jp> +CrystFEL is free software: you can redistribute it and/or modify it under the +terms of the GNU General Public License as published by the Free Software +Foundation, either version 3 of the License, or (at your option) any later +version. + +CrystFEL is distributed in the hope that it will be useful, but WITHOUT ANY +WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A +PARTICULAR PURPOSE. See the GNU General Public License for more details. + +You should have received a copy of the GNU General Public License along with +CrystFEL. If not, see <http://www.gnu.org/licenses/>. -Please read the AUTHORS file for a full list of contributions and contributors. -See the COPYING file for licence conditions. Summary: GPLv3+. Thank you for reading the documentation. :-) @@ -55,9 +64,6 @@ CrystFEL includes programs for simulating and processing patterns subject to the above characteristics. The main programs are: - indexamajig, a "batch indexer" and data reduction program. - It is used for finding hits, locating peaks, indexing - patterns and constructing lists of h,k,l,I,sigma(I) (or - similar) for each pattern. - process_hkl, for merging per-pattern lists of intensities into a single reflection list. @@ -66,6 +72,10 @@ above characteristics. The main programs are: - ambigator, a tool for resolving indexing ambiguities, + - geoptimiser, for refining detector geometry. + + - whirligig, for finding multiple shots from single crystals. + - partialator, for merging patterns more accurately (and much more slowly) using post refinement. @@ -86,6 +96,8 @@ In addition, there is also: - render_hkl, for turning reflection lists into pretty graphics. + - list_events, for creating event lists from multi-event files + There is also a folder full of scripts for achieving many related tasks. CrystFEL mostly works with images stored in HDF5 format, unit cell data in PDB diff --git a/configure.ac b/configure.ac index 96625c6e..d7ef46b1 100644 --- a/configure.ac +++ b/configure.ac @@ -1,6 +1,6 @@ dnl Process this file with autoconf to produce a configure script. -AC_INIT([crystfel],[0.5.4],[taw@physics.org]) +AC_INIT([crystfel],[0.6.0],[taw@physics.org]) AC_CONFIG_AUX_DIR([build-aux]) AC_CONFIG_HEADERS([config.h]) AM_INIT_AUTOMAKE([subdir-objects]) diff --git a/data/hdfsee.ui b/data/hdfsee.ui index 0dc4eb92..9294d932 100644 --- a/data/hdfsee.ui +++ b/data/hdfsee.ui @@ -29,8 +29,19 @@ <menuitem name="peaks" action="PeaksAction" /> </menu> + <menu name="calibration" action="CalibrationAction"> + <menuitem name="calibrationprevious" action="CalibPreviousAction" /> + <menuitem name="calibrationnext" action="CalibNextAction" /> + <menuitem name="switchcalibmode" action="SwitchCalibModeAction" /> + <menuitem name="focus" action="ToggleFocusAction" /> + <menuitem name="savegeometry" action="SaveGeometryAction" /> + </menu> + <menu name="events" action="EventsAction"> - <menuitem name="about" action="AboutAction" /> + <menuitem name="eventprevious" action="EventPreviousAction" /> + <menuitem name="eventnext" action="EventNextAction" /> + <menuitem name="gotoevent" action="GotoEventAction" /> + <menuitem name="randomevent" action="RandomEventAction" /> </menu> <menu name="help" action="HelpAction"> diff --git a/doc/examples/cspad-cxiformat.geom b/doc/examples/cspad-cxiformat.geom new file mode 100644 index 00000000..7c1baf14 --- /dev/null +++ b/doc/examples/cspad-cxiformat.geom @@ -0,0 +1,652 @@ +; Example of a geometry file for CSPAD data output by Cheetah in CXI format + +photon_energy = /LCLS/photon_energy_eV +clen = /LCLS/detector_1/EncoderValue +coffset = 0.573224 +adu_per_eV = 0.00338 +res = 9097.53 + + +; The following lines define how to interpret the three-dimensional data array +; containing the image data. The first dimension is the event number, the +; second and third are spatial dimensions. + +dim0 = % +dim1 = ss +dim2 = fs +data = /entry_1/data_1/data + + +; These following lines define where to find a "bad pixel mask" for each event, +; and how to interpret its contents. + +mask = /entry_1/data_1/mask +mask_good = 0x0000 +mask_bad = 0xffff + + +; The following lines define "rigid groups" which express the physical +; construction of the detector. This is used when refining the detector +; geometry. + +rigid_group_q0 = q0a0,q0a1,q0a2,q0a3,q0a4,q0a5,q0a6,q0a7,q0a8,q0a9,q0a10,q0a11,q0a12,q0a13,q0a14,q0a15 +rigid_group_q1 = q1a0,q1a1,q1a2,q1a3,q1a4,q1a5,q1a6,q1a7,q1a8,q1a9,q1a10,q1a11,q1a12,q1a13,q1a14,q1a15 +rigid_group_q2 = q2a0,q2a1,q2a2,q2a3,q2a4,q2a5,q2a6,q2a7,q2a8,q2a9,q2a10,q2a11,q2a12,q2a13,q2a14,q2a15 +rigid_group_q3 = q3a0,q3a1,q3a2,q3a3,q3a4,q3a5,q3a6,q3a7,q3a8,q3a9,q3a10,q3a11,q3a12,q3a13,q3a14,q3a15 + +rigid_group_a0 = q0a0,q0a1 +rigid_group_a1 = q0a2,q0a3 +rigid_group_a2 = q0a4,q0a5 +rigid_group_a3 = q0a6,q0a7 +rigid_group_a4 = q0a8,q0a9 +rigid_group_a5 = q0a10,q0a11 +rigid_group_a6 = q0a12,q0a13 +rigid_group_a7 = q0a14,q0a15 +rigid_group_a8 = q1a0,q1a1 +rigid_group_a9 = q1a2,q1a3 +rigid_group_a10 = q1a4,q1a5 +rigid_group_a11 = q1a6,q1a7 +rigid_group_a12 = q1a8,q1a9 +rigid_group_a13 = q1a10,q1a11 +rigid_group_a14 = q1a12,q1a13 +rigid_group_a15 = q1a14,q1a15 +rigid_group_a16 = q2a0,q2a1 +rigid_group_a17 = q2a2,q2a3 +rigid_group_a18 = q2a4,q2a5 +rigid_group_a19 = q2a6,q2a7 +rigid_group_a20 = q2a8,q2a9 +rigid_group_a21 = q2a10,q2a11 +rigid_group_a22 = q2a12,q2a13 +rigid_group_a23 = q2a14,q2a15 +rigid_group_a24 = q3a0,q3a1 +rigid_group_a25 = q3a2,q3a3 +rigid_group_a26 = q3a4,q3a5 +rigid_group_a27 = q3a6,q3a7 +rigid_group_a28 = q3a8,q3a9 +rigid_group_a29 = q3a10,q3a11 +rigid_group_a30 = q3a12,q3a13 +rigid_group_a31 = q3a14,q3a15 + +rigid_group_collection_quadrants = q0,q1,q2,q3 +rigid_group_collection_asics = a0,a1,a2,a3,a4,a5,a6,a7,a8,a9,a10,a11,a12,a13,a14,a15,a16,a17,a18,a19,a20,a21,a22,a23,a24,a25,a26,a27,a28,a29,a30,a31 + + +; The geometrical parameters for the panels follow. +; The above parameters can also be set for each panel individually, but usually +; it makes more sense to define them once for all panels. + +q0a0/min_fs = 0 +q0a0/min_ss = 0 +q0a0/max_fs = 193 +q0a0/max_ss = 184 +q0a0/fs = +0.004806x +0.999989y +q0a0/ss = -0.999989x +0.004806y +q0a0/corner_x = 443.819 +q0a0/corner_y = -49.8719 + +q0a1/min_fs = 194 +q0a1/min_ss = 0 +q0a1/max_fs = 387 +q0a1/max_ss = 184 +q0a1/fs = +0.004806x +0.999989y +q0a1/ss = -0.999989x +0.004806y +q0a1/corner_x = 444.766 +q0a1/corner_y = 147.126 + +q0a2/min_fs = 0 +q0a2/min_ss = 185 +q0a2/max_fs = 193 +q0a2/max_ss = 369 +q0a2/fs = +0.003265x +0.999995y +q0a2/ss = -0.999995x +0.003265y +q0a2/corner_x = 239.8 +q0a2/corner_y = -49.3504 + +q0a3/min_fs = 194 +q0a3/min_ss = 185 +q0a3/max_fs = 387 +q0a3/max_ss = 369 +q0a3/fs = +0.003265x +0.999995y +q0a3/ss = -0.999995x +0.003265y +q0a3/corner_x = 240.444 +q0a3/corner_y = 147.649 + +q0a4/min_fs = 0 +q0a4/min_ss = 370 +q0a4/max_fs = 193 +q0a4/max_ss = 554 +q0a4/fs = -0.999997x +0.002424y +q0a4/ss = -0.002424x -0.999997y +q0a4/corner_x = 872.219 +q0a4/corner_y = 342.054 + +q0a5/min_fs = 194 +q0a5/min_ss = 370 +q0a5/max_fs = 387 +q0a5/max_ss = 554 +q0a5/fs = -0.999997x +0.002424y +q0a5/ss = -0.002424x -0.999997y +q0a5/corner_x = 675.22 +q0a5/corner_y = 342.532 + +q0a6/min_fs = 0 +q0a6/min_ss = 555 +q0a6/max_fs = 193 +q0a6/max_ss = 739 +q0a6/fs = -0.999997x +0.002685y +q0a6/ss = -0.002685x -0.999997y +q0a6/corner_x = 871.381 +q0a6/corner_y = 135.836 + +q0a7/min_fs = 194 +q0a7/min_ss = 555 +q0a7/max_fs = 387 +q0a7/max_ss = 739 +q0a7/fs = -0.999997x +0.002685y +q0a7/ss = -0.002685x -0.999997y +q0a7/corner_x = 674.382 +q0a7/corner_y = 136.365 + +q0a8/min_fs = 0 +q0a8/min_ss = 740 +q0a8/max_fs = 193 +q0a8/max_ss = 924 +q0a8/fs = -0.000078x -0.999999y +q0a8/ss = +0.999999x -0.000078y +q0a8/corner_x = 480.758 +q0a8/corner_y = 769.64 + +q0a9/min_fs = 194 +q0a9/min_ss = 740 +q0a9/max_fs = 387 +q0a9/max_ss = 924 +q0a9/fs = -0.000078x -0.999999y +q0a9/ss = +0.999999x -0.000078y +q0a9/corner_x = 480.743 +q0a9/corner_y = 572.64 + +q0a10/min_fs = 0 +q0a10/min_ss = 925 +q0a10/max_fs = 193 +q0a10/max_ss = 1109 +q0a10/fs = +0.001551x -0.999999y +q0a10/ss = +0.999999x +0.001551y +q0a10/corner_x = 689.447 +q0a10/corner_y = 770.295 + +q0a11/min_fs = 194 +q0a11/min_ss = 925 +q0a11/max_fs = 387 +q0a11/max_ss = 1109 +q0a11/fs = +0.001551x -0.999999y +q0a11/ss = +0.999999x +0.001551y +q0a11/corner_x = 689.752 +q0a11/corner_y = 573.296 + +q0a12/min_fs = 0 +q0a12/min_ss = 1110 +q0a12/max_fs = 193 +q0a12/max_ss = 1294 +q0a12/fs = -0.999998x -0.002161y +q0a12/ss = +0.002161x -0.999998y +q0a12/corner_x = 445.672 +q0a12/corner_y = 751.701 + +q0a13/min_fs = 194 +q0a13/min_ss = 1110 +q0a13/max_fs = 387 +q0a13/max_ss = 1294 +q0a13/fs = -0.999998x -0.002161y +q0a13/ss = +0.002161x -0.999998y +q0a13/corner_x = 248.672 +q0a13/corner_y = 751.276 + +q0a14/min_fs = 0 +q0a14/min_ss = 1295 +q0a14/max_fs = 193 +q0a14/max_ss = 1479 +q0a14/fs = -0.999999x -0.000074y +q0a14/ss = +0.000074x -0.999999y +q0a14/corner_x = 445.151 +q0a14/corner_y = 541.081 + +q0a15/min_fs = 194 +q0a15/min_ss = 1295 +q0a15/max_fs = 387 +q0a15/max_ss = 1479 +q0a15/fs = -0.999999x -0.000074y +q0a15/ss = +0.000074x -0.999999y +q0a15/corner_x = 248.151 +q0a15/corner_y = 541.066 + +q1a0/min_fs = 388 +q1a0/min_ss = 0 +q1a0/max_fs = 581 +q1a0/max_ss = 184 +q1a0/fs = -0.999990x -0.004167y +q1a0/ss = +0.004167x -0.999990y +q1a0/corner_x = 28.4776 +q1a0/corner_y = 436.83 + +q1a1/min_fs = 582 +q1a1/min_ss = 0 +q1a1/max_fs = 775 +q1a1/max_ss = 184 +q1a1/fs = -0.999990x -0.004167y +q1a1/ss = +0.004167x -0.999990y +q1a1/corner_x = -168.52 +q1a1/corner_y = 436.009 + +q1a2/min_fs = 388 +q1a2/min_ss = 185 +q1a2/max_fs = 581 +q1a2/max_ss = 369 +q1a2/fs = -1.000001x +0.000385y +q1a2/ss = -0.000385x -1.000001y +q1a2/corner_x = 29.3559 +q1a2/corner_y = 226.978 + +q1a3/min_fs = 582 +q1a3/min_ss = 185 +q1a3/max_fs = 775 +q1a3/max_ss = 369 +q1a3/fs = -1.000001x +0.000385y +q1a3/ss = -0.000385x -1.000001y +q1a3/corner_x = -167.644 +q1a3/corner_y = 227.054 + +q1a4/min_fs = 388 +q1a4/min_ss = 370 +q1a4/max_fs = 581 +q1a4/max_ss = 554 +q1a4/fs = +0.000539x -1.000000y +q1a4/ss = +1.000000x +0.000539y +q1a4/corner_x = -364.144 +q1a4/corner_y = 859.163 + +q1a5/min_fs = 582 +q1a5/min_ss = 370 +q1a5/max_fs = 775 +q1a5/max_ss = 554 +q1a5/fs = +0.000539x -1.000000y +q1a5/ss = +1.000000x +0.000539y +q1a5/corner_x = -364.038 +q1a5/corner_y = 662.163 + +q1a6/min_fs = 388 +q1a6/min_ss = 555 +q1a6/max_fs = 581 +q1a6/max_ss = 739 +q1a6/fs = -0.000337x -1.000000y +q1a6/ss = +1.000000x -0.000337y +q1a6/corner_x = -156.511 +q1a6/corner_y = 857.902 + +q1a7/min_fs = 582 +q1a7/min_ss = 555 +q1a7/max_fs = 775 +q1a7/max_ss = 739 +q1a7/fs = -0.000337x -1.000000y +q1a7/ss = +1.000000x -0.000337y +q1a7/corner_x = -156.577 +q1a7/corner_y = 660.902 + +q1a8/min_fs = 388 +q1a8/min_ss = 740 +q1a8/max_fs = 581 +q1a8/max_ss = 924 +q1a8/fs = +0.999996x +0.002303y +q1a8/ss = -0.002303x +0.999996y +q1a8/corner_x = -786.718 +q1a8/corner_y = 463.506 + +q1a9/min_fs = 582 +q1a9/min_ss = 740 +q1a9/max_fs = 775 +q1a9/max_ss = 924 +q1a9/fs = +0.999996x +0.002303y +q1a9/ss = -0.002303x +0.999996y +q1a9/corner_x = -589.719 +q1a9/corner_y = 463.959 + +q1a10/min_fs = 388 +q1a10/min_ss = 925 +q1a10/max_fs = 581 +q1a10/max_ss = 1109 +q1a10/fs = +0.999997x +0.001741y +q1a10/ss = -0.001741x +0.999997y +q1a10/corner_x = -787.022 +q1a10/corner_y = 668.135 + +q1a11/min_fs = 582 +q1a11/min_ss = 925 +q1a11/max_fs = 775 +q1a11/max_ss = 1109 +q1a11/fs = +0.999997x +0.001741y +q1a11/ss = -0.001741x +0.999997y +q1a11/corner_x = -590.022 +q1a11/corner_y = 668.478 + +q1a12/min_fs = 388 +q1a12/min_ss = 1110 +q1a12/max_fs = 581 +q1a12/max_ss = 1294 +q1a12/fs = -0.000201x -0.999999y +q1a12/ss = +0.999999x -0.000201y +q1a12/corner_x = -761.085 +q1a12/corner_y = 428.541 + +q1a13/min_fs = 582 +q1a13/min_ss = 1110 +q1a13/max_fs = 775 +q1a13/max_ss = 1294 +q1a13/fs = -0.000201x -0.999999y +q1a13/ss = +0.999999x -0.000201y +q1a13/corner_x = -761.125 +q1a13/corner_y = 231.541 + +q1a14/min_fs = 388 +q1a14/min_ss = 1295 +q1a14/max_fs = 581 +q1a14/max_ss = 1479 +q1a14/fs = +0.003097x -0.999995y +q1a14/ss = +0.999995x +0.003097y +q1a14/corner_x = -559.624 +q1a14/corner_y = 428.347 + +q1a15/min_fs = 582 +q1a15/min_ss = 1295 +q1a15/max_fs = 775 +q1a15/max_ss = 1479 +q1a15/fs = +0.003097x -0.999995y +q1a15/ss = +0.999995x +0.003097y +q1a15/corner_x = -559.014 +q1a15/corner_y = 231.348 + +q2a0/min_fs = 776 +q2a0/min_ss = 0 +q2a0/max_fs = 969 +q2a0/max_ss = 184 +q2a0/fs = -0.004086x -0.999991y +q2a0/ss = +0.999991x -0.004086y +q2a0/corner_x = -442.346 +q2a0/corner_y = 20.3382 + +q2a1/min_fs = 970 +q2a1/min_ss = 0 +q2a1/max_fs = 1163 +q2a1/max_ss = 184 +q2a1/fs = -0.004086x -0.999991y +q2a1/ss = +0.999991x -0.004086y +q2a1/corner_x = -443.151 +q2a1/corner_y = -176.66 + +q2a2/min_fs = 776 +q2a2/min_ss = 185 +q2a2/max_fs = 969 +q2a2/max_ss = 369 +q2a2/fs = +0.000302x -1.000000y +q2a2/ss = +1.000000x +0.000302y +q2a2/corner_x = -235.519 +q2a2/corner_y = 19.2312 + +q2a3/min_fs = 970 +q2a3/min_ss = 185 +q2a3/max_fs = 1163 +q2a3/max_ss = 369 +q2a3/fs = +0.000302x -1.000000y +q2a3/ss = +1.000000x +0.000302y +q2a3/corner_x = -235.459 +q2a3/corner_y = -177.769 + +q2a4/min_fs = 776 +q2a4/min_ss = 370 +q2a4/max_fs = 969 +q2a4/max_ss = 554 +q2a4/fs = +0.999997x -0.002037y +q2a4/ss = +0.002037x +0.999997y +q2a4/corner_x = -863.817 +q2a4/corner_y = -370.344 + +q2a5/min_fs = 970 +q2a5/min_ss = 370 +q2a5/max_fs = 1163 +q2a5/max_ss = 554 +q2a5/fs = +0.999997x -0.002037y +q2a5/ss = +0.002037x +0.999997y +q2a5/corner_x = -666.817 +q2a5/corner_y = -370.746 + +q2a6/min_fs = 776 +q2a6/min_ss = 555 +q2a6/max_fs = 969 +q2a6/max_ss = 739 +q2a6/fs = +1.000000x -0.001155y +q2a6/ss = +0.001155x +1.000000y +q2a6/corner_x = -863.549 +q2a6/corner_y = -165.126 + +q2a7/min_fs = 970 +q2a7/min_ss = 555 +q2a7/max_fs = 1163 +q2a7/max_ss = 739 +q2a7/fs = +1.000000x -0.001155y +q2a7/ss = +0.001155x +1.000000y +q2a7/corner_x = -666.549 +q2a7/corner_y = -165.353 + +q2a8/min_fs = 776 +q2a8/min_ss = 740 +q2a8/max_fs = 969 +q2a8/max_ss = 924 +q2a8/fs = +0.002076x +0.999998y +q2a8/ss = -0.999998x +0.002076y +q2a8/corner_x = -473.62 +q2a8/corner_y = -793.473 + +q2a9/min_fs = 970 +q2a9/min_ss = 740 +q2a9/max_fs = 1163 +q2a9/max_ss = 924 +q2a9/fs = +0.002076x +0.999998y +q2a9/ss = -0.999998x +0.002076y +q2a9/corner_x = -473.211 +q2a9/corner_y = -596.474 + +q2a10/min_fs = 776 +q2a10/min_ss = 925 +q2a10/max_fs = 969 +q2a10/max_ss = 1109 +q2a10/fs = +0.004134x +0.999991y +q2a10/ss = -0.999991x +0.004134y +q2a10/corner_x = -676.809 +q2a10/corner_y = -792.653 + +q2a11/min_fs = 970 +q2a11/min_ss = 925 +q2a11/max_fs = 1163 +q2a11/max_ss = 1109 +q2a11/fs = +0.004134x +0.999991y +q2a11/ss = -0.999991x +0.004134y +q2a11/corner_x = -675.995 +q2a11/corner_y = -595.655 + +q2a12/min_fs = 776 +q2a12/min_ss = 1110 +q2a12/max_fs = 969 +q2a12/max_ss = 1294 +q2a12/fs = +0.999981x -0.006417y +q2a12/ss = +0.006417x +0.999981y +q2a12/corner_x = -442.034 +q2a12/corner_y = -769.447 + +q2a13/min_fs = 970 +q2a13/min_ss = 1110 +q2a13/max_fs = 1163 +q2a13/max_ss = 1294 +q2a13/fs = +0.999981x -0.006417y +q2a13/ss = +0.006417x +0.999981y +q2a13/corner_x = -245.038 +q2a13/corner_y = -770.711 + +q2a14/min_fs = 776 +q2a14/min_ss = 1295 +q2a14/max_fs = 969 +q2a14/max_ss = 1479 +q2a14/fs = +0.999996x -0.002727y +q2a14/ss = +0.002727x +0.999996y +q2a14/corner_x = -441.283 +q2a14/corner_y = -566.627 + +q2a15/min_fs = 970 +q2a15/min_ss = 1295 +q2a15/max_fs = 1163 +q2a15/max_ss = 1479 +q2a15/fs = +0.999996x -0.002727y +q2a15/ss = +0.002727x +0.999996y +q2a15/corner_x = -244.283 +q2a15/corner_y = -567.164 + +q3a0/min_fs = 1164 +q3a0/min_ss = 0 +q3a0/max_fs = 1357 +q3a0/max_ss = 184 +q3a0/fs = +0.999988x -0.004965y +q3a0/ss = +0.004965x +0.999988y +q3a0/corner_x = -33.3507 +q3a0/corner_y = -458.693 + +q3a1/min_fs = 1358 +q3a1/min_ss = 0 +q3a1/max_fs = 1551 +q3a1/max_ss = 184 +q3a1/fs = +0.999988x -0.004965y +q3a1/ss = +0.004965x +0.999988y +q3a1/corner_x = 163.647 +q3a1/corner_y = -459.671 + +q3a2/min_fs = 1164 +q3a2/min_ss = 185 +q3a2/max_fs = 1357 +q3a2/max_ss = 369 +q3a2/fs = +0.999998x -0.002316y +q3a2/ss = +0.002316x +0.999998y +q3a2/corner_x = -31.8316 +q3a2/corner_y = -254.931 + +q3a3/min_fs = 1358 +q3a3/min_ss = 185 +q3a3/max_fs = 1551 +q3a3/max_ss = 369 +q3a3/fs = +0.999998x -0.002316y +q3a3/ss = +0.002316x +0.999998y +q3a3/corner_x = 165.168 +q3a3/corner_y = -255.388 + +q3a4/min_fs = 1164 +q3a4/min_ss = 370 +q3a4/max_fs = 1357 +q3a4/max_ss = 554 +q3a4/fs = +0.002474x +0.999997y +q3a4/ss = -0.999997x +0.002474y +q3a4/corner_x = 359.553 +q3a4/corner_y = -886.512 + +q3a5/min_fs = 1358 +q3a5/min_ss = 370 +q3a5/max_fs = 1551 +q3a5/max_ss = 554 +q3a5/fs = +0.002474x +0.999997y +q3a5/ss = -0.999997x +0.002474y +q3a5/corner_x = 360.04 +q3a5/corner_y = -689.512 + +q3a6/min_fs = 1164 +q3a6/min_ss = 555 +q3a6/max_fs = 1357 +q3a6/max_ss = 739 +q3a6/fs = +0.000059x +1.000000y +q3a6/ss = -1.000000x +0.000059y +q3a6/corner_x = 154.142 +q3a6/corner_y = -884.763 + +q3a7/min_fs = 1358 +q3a7/min_ss = 555 +q3a7/max_fs = 1551 +q3a7/max_ss = 739 +q3a7/fs = +0.000059x +1.000000y +q3a7/ss = -1.000000x +0.000059y +q3a7/corner_x = 154.154 +q3a7/corner_y = -687.763 + +q3a8/min_fs = 1164 +q3a8/min_ss = 740 +q3a8/max_fs = 1357 +q3a8/max_ss = 924 +q3a8/fs = -0.999993x +0.004040y +q3a8/ss = -0.004040x -0.999993y +q3a8/corner_x = 784.877 +q3a8/corner_y = -492.935 + +q3a9/min_fs = 1358 +q3a9/min_ss = 740 +q3a9/max_fs = 1551 +q3a9/max_ss = 924 +q3a9/fs = -0.999993x +0.004040y +q3a9/ss = -0.004040x -0.999993y +q3a9/corner_x = 587.878 +q3a9/corner_y = -492.139 + +q3a10/min_fs = 1164 +q3a10/min_ss = 925 +q3a10/max_fs = 1357 +q3a10/max_ss = 1109 +q3a10/fs = -0.999971x +0.007529y +q3a10/ss = -0.007529x -0.999971y +q3a10/corner_x = 784.254 +q3a10/corner_y = -699.59 + +q3a11/min_fs = 1358 +q3a11/min_ss = 925 +q3a11/max_fs = 1551 +q3a11/max_ss = 1109 +q3a11/fs = -0.999971x +0.007529y +q3a11/ss = -0.007529x -0.999971y +q3a11/corner_x = 587.26 +q3a11/corner_y = -698.107 + +q3a12/min_fs = 1164 +q3a12/min_ss = 1110 +q3a12/max_fs = 1357 +q3a12/max_ss = 1294 +q3a12/fs = +0.004516x +0.999990y +q3a12/ss = -0.999990x +0.004516y +q3a12/corner_x = 769.176 +q3a12/corner_y = -460.51 + +q3a13/min_fs = 1358 +q3a13/min_ss = 1110 +q3a13/max_fs = 1551 +q3a13/max_ss = 1294 +q3a13/fs = +0.004516x +0.999990y +q3a13/ss = -0.999990x +0.004516y +q3a13/corner_x = 770.066 +q3a13/corner_y = -263.512 + +q3a14/min_fs = 1164 +q3a14/min_ss = 1295 +q3a14/max_fs = 1357 +q3a14/max_ss = 1479 +q3a14/fs = +0.004918x +0.999989y +q3a14/ss = -0.999989x +0.004918y +q3a14/corner_x = 554.764 +q3a14/corner_y = -460.25 + +q3a15/min_fs = 1358 +q3a15/min_ss = 1295 +q3a15/max_fs = 1551 +q3a15/max_ss = 1479 +q3a15/fs = +0.004918x +0.999989y +q3a15/ss = -0.999989x +0.004918y +q3a15/corner_x = 555.732 +q3a15/corner_y = -263.253 diff --git a/doc/examples/cspad-feb2011.geom b/doc/examples/cspad-single.geom index 21adb022..b850ca20 100644 --- a/doc/examples/cspad-feb2011.geom +++ b/doc/examples/cspad-single.geom @@ -1,896 +1,637 @@ -adu_per_eV = 1.0 +; Example of a CrystFEL geometry file for CSPAD data in single-event HDF5 +; format, with the panel data arranged as by Cheetah + +adu_per_eV = 0.00105 ; correct value for CSPAD 1.0 (i.e. old versions only) + ; for newer versions (since about 2013), use 0.00338 instead +res = 9090.91 ; pixels per metre +clen = /LCLS/detectorPosition ; camera length from HDF5 file +coffset = 0.0 ; no adjustment to camera length from HDF5 file +photon_energy = /LCLS/photon_energy_eV ; photon energy (in eV) from HDF5 file +data = /data/rawdata ; where to find the image data in the HDF5 file + + +; The following lines define "rigid groups" which express the physical +; construction of the detector. This is used when refining the detector +; geometry. + +rigid_group_q0 = q0a0,q0a1,q0a2,q0a3,q0a4,q0a5,q0a6,q0a7,q0a8,q0a9,q0a10,q0a11,q0a12,q0a13,q0a14,q0a15 +rigid_group_q1 = q1a0,q1a1,q1a2,q1a3,q1a4,q1a5,q1a6,q1a7,q1a8,q1a9,q1a10,q1a11,q1a12,q1a13,q1a14,q1a15 +rigid_group_q2 = q2a0,q2a1,q2a2,q2a3,q2a4,q2a5,q2a6,q2a7,q2a8,q2a9,q2a10,q2a11,q2a12,q2a13,q2a14,q2a15 +rigid_group_q3 = q3a0,q3a1,q3a2,q3a3,q3a4,q3a5,q3a6,q3a7,q3a8,q3a9,q3a10,q3a11,q3a12,q3a13,q3a14,q3a15 + +rigid_group_a0 = q0a0,q0a1 +rigid_group_a1 = q0a2,q0a3 +rigid_group_a2 = q0a4,q0a5 +rigid_group_a3 = q0a6,q0a7 +rigid_group_a4 = q0a8,q0a9 +rigid_group_a5 = q0a10,q0a11 +rigid_group_a6 = q0a12,q0a13 +rigid_group_a7 = q0a14,q0a15 +rigid_group_a8 = q1a0,q1a1 +rigid_group_a9 = q1a2,q1a3 +rigid_group_a10 = q1a4,q1a5 +rigid_group_a11 = q1a6,q1a7 +rigid_group_a12 = q1a8,q1a9 +rigid_group_a13 = q1a10,q1a11 +rigid_group_a14 = q1a12,q1a13 +rigid_group_a15 = q1a14,q1a15 +rigid_group_a16 = q2a0,q2a1 +rigid_group_a17 = q2a2,q2a3 +rigid_group_a18 = q2a4,q2a5 +rigid_group_a19 = q2a6,q2a7 +rigid_group_a20 = q2a8,q2a9 +rigid_group_a21 = q2a10,q2a11 +rigid_group_a22 = q2a12,q2a13 +rigid_group_a23 = q2a14,q2a15 +rigid_group_a24 = q3a0,q3a1 +rigid_group_a25 = q3a2,q3a3 +rigid_group_a26 = q3a4,q3a5 +rigid_group_a27 = q3a6,q3a7 +rigid_group_a28 = q3a8,q3a9 +rigid_group_a29 = q3a10,q3a11 +rigid_group_a30 = q3a12,q3a13 +rigid_group_a31 = q3a14,q3a15 + +rigid_group_collection_quadrants = q0,q1,q2,q3 +rigid_group_collection_asics = a0,a1,a2,a3,a4,a5,a6,a7,a8,a9,a10,a11,a12,a13,a14,a15,a16,a17,a18,a19,a20,a21,a22,a23,a24,a25,a26,a27,a28,a29,a30,a31 + + +; The geometrical parameters for the panels follow. +; The above parameters can also be set for each panel individually, but usually +; it makes more sense to define them once for all panels. q0a0/min_fs = 0 q0a0/min_ss = 0 q0a0/max_fs = 193 q0a0/max_ss = 184 -q0a0/badrow_direction = - -q0a0/res = 9090.91 -q0a0/clen = /LCLS/detectorPosition q0a0/fs = -0.0057225772x +0.9999836087y q0a0/ss = -0.9999836087x -0.0057225772y q0a0/corner_x = 424.384 q0a0/corner_y = -10.6473 -q0a0/no_index = 0 - q0a1/min_fs = 194 q0a1/min_ss = 0 q0a1/max_fs = 387 q0a1/max_ss = 184 -q0a1/badrow_direction = - -q0a1/res = 9090.91 -q0a1/clen = /LCLS/detectorPosition q0a1/fs = -0.0057225772x +0.9999836087y q0a1/ss = -0.9999836087x -0.0057225772y q0a1/corner_x = 423.257 q0a1/corner_y = 186.349 -q0a1/no_index = 0 - q1a0/min_fs = 388 q1a0/min_ss = 0 q1a0/max_fs = 581 q1a0/max_ss = 184 -q1a0/badrow_direction = - -q1a0/res = 9090.91 -q1a0/clen = /LCLS/detectorPosition q1a0/fs = -0.9999815226x +0.0060744919y q1a0/ss = -0.0060744919x -0.9999815226y q1a0/corner_x = 13.1196 q1a0/corner_y = 422.727 -q1a0/no_index = 0 - q1a1/min_fs = 582 q1a1/min_ss = 0 q1a1/max_fs = 775 q1a1/max_ss = 184 -q1a1/badrow_direction = - -q1a1/res = 9090.91 -q1a1/clen = /LCLS/detectorPosition q1a1/fs = -0.9999815226x +0.0060744919y q1a1/ss = -0.0060744919x -0.9999815226y q1a1/corner_x = -183.877 q1a1/corner_y = 423.924 -q1a1/no_index = 0 - q2a0/min_fs = 776 q2a0/min_ss = 0 q2a0/max_fs = 969 q2a0/max_ss = 184 -q2a0/badrow_direction = - -q2a0/res = 9090.91 -q2a0/clen = /LCLS/detectorPosition q2a0/fs = +0.0048825117x -0.9999880791y q2a0/ss = +0.9999880791x +0.0048825117y q2a0/corner_x = -428.519 q2a0/corner_y = 8.29208 -q2a0/no_index = 0 - q2a1/min_fs = 970 q2a1/min_ss = 0 q2a1/max_fs = 1163 q2a1/max_ss = 184 -q2a1/badrow_direction = - -q2a1/res = 9090.91 -q2a1/clen = /LCLS/detectorPosition q2a1/fs = +0.0048825117x -0.9999880791y q2a1/ss = +0.9999880791x +0.0048825117y q2a1/corner_x = -427.557 q2a1/corner_y = -188.706 -q2a1/no_index = 0 - q3a0/min_fs = 1164 q3a0/min_ss = 0 q3a0/max_fs = 1357 q3a0/max_ss = 184 -q3a0/badrow_direction = - -q3a0/res = 9090.91 -q3a0/clen = /LCLS/detectorPosition q3a0/fs = +0.9999964833x +0.0026521713y q3a0/ss = -0.0026521713x +0.9999964833y q3a0/corner_x = -12.1295 q3a0/corner_y = -424.046 -q3a0/no_index = 0 - q3a1/min_fs = 1358 q3a1/min_ss = 0 q3a1/max_fs = 1551 q3a1/max_ss = 184 -q3a1/badrow_direction = - -q3a1/res = 9090.91 -q3a1/clen = /LCLS/detectorPosition q3a1/fs = +0.9999964833x +0.0026521713y q3a1/ss = -0.0026521713x +0.9999964833y q3a1/corner_x = 184.870 q3a1/corner_y = -423.523 -q3a1/no_index = 0 - q0a2/min_fs = 0 q0a2/min_ss = 185 q0a2/max_fs = 193 q0a2/max_ss = 369 -q0a2/badrow_direction = - -q0a2/res = 9090.91 -q0a2/clen = /LCLS/detectorPosition q0a2/fs = -0.0003575958x +0.9999999404y q0a2/ss = -0.9999999404x -0.0003575958y q0a2/corner_x = 211.039 q0a2/corner_y = -10.9889 -q0a2/no_index = 0 - q0a3/min_fs = 194 q0a3/min_ss = 185 q0a3/max_fs = 387 q0a3/max_ss = 369 -q0a3/badrow_direction = - -q0a3/res = 9090.91 -q0a3/clen = /LCLS/detectorPosition q0a3/fs = -0.0003575958x +0.9999999404y q0a3/ss = -0.9999999404x -0.0003575958y q0a3/corner_x = 210.969 q0a3/corner_y = 186.011 -q0a3/no_index = 0 - q1a2/min_fs = 388 q1a2/min_ss = 185 q1a2/max_fs = 581 q1a2/max_ss = 369 -q1a2/badrow_direction = - -q1a2/res = 9090.91 -q1a2/clen = /LCLS/detectorPosition q1a2/fs = -0.9999999404x +0.0003117446y q1a2/ss = -0.0003117446x -0.9999999404y q1a2/corner_x = 13.3322 q1a2/corner_y = 208.973 -q1a2/no_index = 0 - q1a3/min_fs = 582 q1a3/min_ss = 185 q1a3/max_fs = 775 q1a3/max_ss = 369 -q1a3/badrow_direction = - -q1a3/res = 9090.91 -q1a3/clen = /LCLS/detectorPosition q1a3/fs = -0.9999999404x +0.0003117446y q1a3/ss = -0.0003117446x -0.9999999404y q1a3/corner_x = -183.668 q1a3/corner_y = 209.034 -q1a3/no_index = 0 - q2a2/min_fs = 776 q2a2/min_ss = 185 q2a2/max_fs = 969 q2a2/max_ss = 369 -q2a2/badrow_direction = - -q2a2/res = 9090.91 -q2a2/clen = /LCLS/detectorPosition q2a2/fs = -0.0004957085x -0.9999998808y q2a2/ss = +0.9999998808x -0.0004957085y q2a2/corner_x = -215.108 q2a2/corner_y = 8.41637 -q2a2/no_index = 0 - q2a3/min_fs = 970 q2a3/min_ss = 185 q2a3/max_fs = 1163 q2a3/max_ss = 369 -q2a3/badrow_direction = - -q2a3/res = 9090.91 -q2a3/clen = /LCLS/detectorPosition q2a3/fs = -0.0004957085x -0.9999998808y q2a3/ss = +0.9999998808x -0.0004957085y q2a3/corner_x = -215.206 q2a3/corner_y = -188.584 -q2a3/no_index = 0 - q3a2/min_fs = 1164 q3a2/min_ss = 185 q3a2/max_fs = 1357 q3a2/max_ss = 369 -q3a2/badrow_direction = - -q3a2/res = 9090.91 -q3a2/clen = /LCLS/detectorPosition q3a2/fs = +1.0000000000x -0.0001447762y q3a2/ss = +0.0001447762x +1.0000000000y q3a2/corner_x = -12.1720 q3a2/corner_y = -210.466 -q3a2/no_index = 0 - q3a3/min_fs = 1358 q3a3/min_ss = 185 q3a3/max_fs = 1551 q3a3/max_ss = 369 -q3a3/badrow_direction = - -q3a3/res = 9090.91 -q3a3/clen = /LCLS/detectorPosition q3a3/fs = +1.0000000000x -0.0001447762y q3a3/ss = +0.0001447762x +1.0000000000y q3a3/corner_x = 184.828 q3a3/corner_y = -210.495 -q3a3/no_index = 0 - q0a4/min_fs = 0 q0a4/min_ss = 370 q0a4/max_fs = 193 q0a4/max_ss = 554 -q0a4/badrow_direction = - -q0a4/res = 9090.91 -q0a4/clen = /LCLS/detectorPosition q0a4/fs = -0.9999362230x -0.0112929661y q0a4/ss = +0.0112929661x -0.9999362230y q0a4/corner_x = 840.468 q0a4/corner_y = 392.435 -q0a4/no_index = 0 - q0a5/min_fs = 194 q0a5/min_ss = 370 q0a5/max_fs = 387 q0a5/max_ss = 554 -q0a5/badrow_direction = - -q0a5/res = 9090.91 -q0a5/clen = /LCLS/detectorPosition q0a5/fs = -0.9999362230x -0.0112929661y q0a5/ss = +0.0112929661x -0.9999362230y q0a5/corner_x = 643.481 q0a5/corner_y = 390.210 -q0a5/no_index = 1 - q1a4/min_fs = 388 q1a4/min_ss = 370 q1a4/max_fs = 581 q1a4/max_ss = 554 -q1a4/badrow_direction = - -q1a4/res = 9090.91 -q1a4/clen = /LCLS/detectorPosition q1a4/fs = -0.0003113084x -0.9999999404y q1a4/ss = +0.9999999404x -0.0003113084y q1a4/corner_x = -387.060 q1a4/corner_y = 844.837 -q1a4/no_index = 0 - q1a5/min_fs = 582 q1a5/min_ss = 370 q1a5/max_fs = 775 q1a5/max_ss = 554 -q1a5/badrow_direction = - -q1a5/res = 9090.91 -q1a5/clen = /LCLS/detectorPosition q1a5/fs = -0.0003113084x -0.9999999404y q1a5/ss = +0.9999999404x -0.0003113084y q1a5/corner_x = -387.121 q1a5/corner_y = 647.837 -q1a5/no_index = 0 - q2a4/min_fs = 776 q2a4/min_ss = 370 q2a4/max_fs = 969 q2a4/max_ss = 554 -q2a4/badrow_direction = - -q2a4/res = 9090.91 -q2a4/clen = /LCLS/detectorPosition q2a4/fs = +0.9999818802x +0.0060196919y q2a4/ss = -0.0060196919x +0.9999818802y q2a4/corner_x = -849.683 q2a4/corner_y = -392.889 -q2a4/no_index = 0 - q2a5/min_fs = 970 q2a5/min_ss = 370 q2a5/max_fs = 1163 q2a5/max_ss = 554 -q2a5/badrow_direction = - -q2a5/res = 9090.91 -q2a5/clen = /LCLS/detectorPosition q2a5/fs = +0.9999818802x +0.0060196919y q2a5/ss = -0.0060196919x +0.9999818802y q2a5/corner_x = -652.687 q2a5/corner_y = -391.703 -q2a5/no_index = 0 - q3a4/min_fs = 1164 q3a4/min_ss = 370 q3a4/max_fs = 1357 q3a4/max_ss = 554 -q3a4/badrow_direction = - -q3a4/res = 9090.91 -q3a4/clen = /LCLS/detectorPosition q3a4/fs = -0.0023373319x +0.9999972582y q3a4/ss = -0.9999972582x -0.0023373319y q3a4/corner_x = 388.452 q3a4/corner_y = -844.271 -q3a4/no_index = 1 - q3a5/min_fs = 1358 q3a5/min_ss = 370 q3a5/max_fs = 1551 q3a5/max_ss = 554 -q3a5/badrow_direction = - -q3a5/res = 9090.91 -q3a5/clen = /LCLS/detectorPosition q3a5/fs = -0.0023373319x +0.9999972582y q3a5/ss = -0.9999972582x -0.0023373319y q3a5/corner_x = 387.991 q3a5/corner_y = -647.271 -q3a5/no_index = 0 - q0a6/min_fs = 0 q0a6/min_ss = 555 q0a6/max_fs = 193 q0a6/max_ss = 739 -q0a6/badrow_direction = - -q0a6/res = 9090.91 -q0a6/clen = /LCLS/detectorPosition q0a6/fs = -0.9999533892x -0.0096549187y q0a6/ss = +0.0096549187x -0.9999533892y q0a6/corner_x = 842.954 q0a6/corner_y = 182.508 -q0a6/no_index = 0 - q0a7/min_fs = 194 q0a7/min_ss = 555 q0a7/max_fs = 387 q0a7/max_ss = 739 -q0a7/badrow_direction = - -q0a7/res = 9090.91 -q0a7/clen = /LCLS/detectorPosition q0a7/fs = -0.9999533892x -0.0096549187y q0a7/ss = +0.0096549187x -0.9999533892y q0a7/corner_x = 645.963 q0a7/corner_y = 180.606 -q0a7/no_index = 0 - q1a6/min_fs = 388 q1a6/min_ss = 555 q1a6/max_fs = 581 q1a6/max_ss = 739 -q1a6/badrow_direction = - -q1a6/res = 9090.91 -q1a6/clen = /LCLS/detectorPosition q1a6/fs = +0.0007214849x -0.9999997616y q1a6/ss = +0.9999997616x +0.0007214849y q1a6/corner_x = -174.672 q1a6/corner_y = 844.923 -q1a6/no_index = 0 - q1a7/min_fs = 582 q1a7/min_ss = 555 q1a7/max_fs = 775 q1a7/max_ss = 739 -q1a7/badrow_direction = - -q1a7/res = 9090.91 -q1a7/clen = /LCLS/detectorPosition q1a7/fs = +0.0007214849x -0.9999997616y q1a7/ss = +0.9999997616x +0.0007214849y q1a7/corner_x = -174.530 q1a7/corner_y = 647.923 -q1a7/no_index = 0 - q2a6/min_fs = 776 q2a6/min_ss = 555 q2a6/max_fs = 969 q2a6/max_ss = 739 -q2a6/badrow_direction = - -q2a6/res = 9090.91 -q2a6/clen = /LCLS/detectorPosition q2a6/fs = +0.9999990463x +0.0013817062y q2a6/ss = -0.0013817062x +0.9999990463y q2a6/corner_x = -850.035 q2a6/corner_y = -179.939 -q2a6/no_index = 0 - q2a7/min_fs = 970 q2a7/min_ss = 555 q2a7/max_fs = 1163 q2a7/max_ss = 739 -q2a7/badrow_direction = - -q2a7/res = 9090.91 -q2a7/clen = /LCLS/detectorPosition q2a7/fs = +0.9999990463x +0.0013817062y q2a7/ss = -0.0013817062x +0.9999990463y q2a7/corner_x = -653.035 q2a7/corner_y = -179.667 -q2a7/no_index = 0 - q3a6/min_fs = 1164 q3a6/min_ss = 555 q3a6/max_fs = 1357 q3a6/max_ss = 739 -q3a6/badrow_direction = - -q3a6/res = 9090.91 -q3a6/clen = /LCLS/detectorPosition q3a6/fs = -0.0021083837x +0.9999977946y q3a6/ss = -0.9999977946x -0.0021083837y q3a6/corner_x = 175.688 q3a6/corner_y = -844.681 -q3a6/no_index = 0 - q3a7/min_fs = 1358 q3a7/min_ss = 555 q3a7/max_fs = 1551 q3a7/max_ss = 739 -q3a7/badrow_direction = - -q3a7/res = 9090.91 -q3a7/clen = /LCLS/detectorPosition q3a7/fs = -0.0021083837x +0.9999977946y q3a7/ss = -0.9999977946x -0.0021083837y q3a7/corner_x = 175.273 q3a7/corner_y = -647.681 -q3a7/no_index = 1 - q0a8/min_fs = 0 q0a8/min_ss = 740 q0a8/max_fs = 193 q0a8/max_ss = 924 -q0a8/badrow_direction = - -q0a8/res = 9090.91 -q0a8/clen = /LCLS/detectorPosition q0a8/fs = +0.0078456579x -0.9999692440y q0a8/ss = +0.9999692440x +0.0078456579y q0a8/corner_x = 440.576 q0a8/corner_y = 811.780 -q0a8/no_index = 0 - q0a9/min_fs = 194 q0a9/min_ss = 740 q0a9/max_fs = 387 q0a9/max_ss = 924 -q0a9/badrow_direction = - -q0a9/res = 9090.91 -q0a9/clen = /LCLS/detectorPosition q0a9/fs = +0.0078456579x -0.9999692440y q0a9/ss = +0.9999692440x +0.0078456579y q0a9/corner_x = 442.122 q0a9/corner_y = 614.786 -q0a9/no_index = 0 - q1a8/min_fs = 388 q1a8/min_ss = 740 q1a8/max_fs = 581 q1a8/max_ss = 924 -q1a8/badrow_direction = - -q1a8/res = 9090.91 -q1a8/clen = /LCLS/detectorPosition q1a8/fs = +0.9999817014x +0.0060460833y q1a8/ss = -0.0060460833x +0.9999817014y q1a8/corner_x = -808.912 q1a8/corner_y = 453.082 -q1a8/no_index = 0 - q1a9/min_fs = 582 q1a9/min_ss = 740 q1a9/max_fs = 775 q1a9/max_ss = 924 -q1a9/badrow_direction = - -q1a9/res = 9090.91 -q1a9/clen = /LCLS/detectorPosition q1a9/fs = +0.9999817014x +0.0060460833y q1a9/ss = -0.0060460833x +0.9999817014y q1a9/corner_x = -611.915 q1a9/corner_y = 454.273 -q1a9/no_index = 0 - q2a8/min_fs = 776 q2a8/min_ss = 740 q2a8/max_fs = 969 q2a8/max_ss = 924 -q2a8/badrow_direction = - -q2a8/res = 9090.91 -q2a8/clen = /LCLS/detectorPosition q2a8/fs = -0.0027005512x +0.9999963641y q2a8/ss = -0.9999963641x -0.0027005512y q2a8/corner_x = -451.888 q2a8/corner_y = -813.482 -q2a8/no_index = 0 - q2a9/min_fs = 970 q2a9/min_ss = 740 q2a9/max_fs = 1163 q2a9/max_ss = 924 -q2a9/badrow_direction = - -q2a9/res = 9090.91 -q2a9/clen = /LCLS/detectorPosition q2a9/fs = -0.0027005512x +0.9999963641y q2a9/ss = -0.9999963641x -0.0027005512y q2a9/corner_x = -452.420 q2a9/corner_y = -616.482 -q2a9/no_index = 0 - q3a8/min_fs = 1164 q3a8/min_ss = 740 q3a8/max_fs = 1357 q3a8/max_ss = 924 -q3a8/badrow_direction = - -q3a8/res = 9090.91 -q3a8/clen = /LCLS/detectorPosition q3a8/fs = -0.9999983311x -0.0018213630y q3a8/ss = +0.0018213630x -0.9999983311y q3a8/corner_x = 806.687 q3a8/corner_y = -442.882 -q3a8/no_index = 0 - q3a9/min_fs = 1358 q3a9/min_ss = 740 q3a9/max_fs = 1551 q3a9/max_ss = 924 -q3a9/badrow_direction = - -q3a9/res = 9090.91 -q3a9/clen = /LCLS/detectorPosition q3a9/fs = -0.9999983311x -0.0018213630y q3a9/ss = +0.0018213630x -0.9999983311y q3a9/corner_x = 609.687 q3a9/corner_y = -443.241 -q3a9/no_index = 0 - q0a10/min_fs = 0 q0a10/min_ss = 925 q0a10/max_fs = 193 q0a10/max_ss = 1109 -q0a10/badrow_direction = - -q0a10/res = 9090.91 -q0a10/clen = /LCLS/detectorPosition q0a10/fs = +0.0069182217x -0.9999760985y q0a10/ss = +0.9999760985x +0.0069182217y q0a10/corner_x = 653.083 q0a10/corner_y = 813.670 -q0a10/no_index = 0 - q0a11/min_fs = 194 q0a11/min_ss = 925 q0a11/max_fs = 387 q0a11/max_ss = 1109 -q0a11/badrow_direction = - -q0a11/res = 9090.91 -q0a11/clen = /LCLS/detectorPosition q0a11/fs = +0.0069182217x -0.9999760985y q0a11/ss = +0.9999760985x +0.0069182217y q0a11/corner_x = 654.445 q0a11/corner_y = 616.674 -q0a11/no_index = 0 - q1a10/min_fs = 388 q1a10/min_ss = 925 q1a10/max_fs = 581 q1a10/max_ss = 1109 -q1a10/badrow_direction = - -q1a10/res = 9090.91 -q1a10/clen = /LCLS/detectorPosition q1a10/fs = +0.9999817014x +0.0060461056y q1a10/ss = -0.0060461056x +0.9999817014y q1a10/corner_x = -808.912 q1a10/corner_y = 658.082 -q1a10/no_index = 0 - q1a11/min_fs = 582 q1a11/min_ss = 925 q1a11/max_fs = 775 q1a11/max_ss = 1109 -q1a11/badrow_direction = - -q1a11/res = 9090.91 -q1a11/clen = /LCLS/detectorPosition q1a11/fs = +0.9999817014x +0.0060461056y q1a11/ss = -0.0060461056x +0.9999817014y q1a11/corner_x = -611.915 q1a11/corner_y = 659.273 -q1a11/no_index = 0 - q2a10/min_fs = 776 q2a10/min_ss = 925 q2a10/max_fs = 969 q2a10/max_ss = 1109 -q2a10/badrow_direction = - -q2a10/res = 9090.91 -q2a10/clen = /LCLS/detectorPosition q2a10/fs = -0.0027005058x +0.9999963641y q2a10/ss = -0.9999963641x -0.0027005058y q2a10/corner_x = -656.888 q2a10/corner_y = -813.482 -q2a10/no_index = 0 - q2a11/min_fs = 970 q2a11/min_ss = 925 q2a11/max_fs = 1163 q2a11/max_ss = 1109 -q2a11/badrow_direction = - -q2a11/res = 9090.91 -q2a11/clen = /LCLS/detectorPosition q2a11/fs = -0.0027005058x +0.9999963641y q2a11/ss = -0.9999963641x -0.0027005058y q2a11/corner_x = -657.420 q2a11/corner_y = -616.482 -q2a11/no_index = 0 - q3a10/min_fs = 1164 q3a10/min_ss = 925 q3a10/max_fs = 1357 q3a10/max_ss = 1109 -q3a10/badrow_direction = - -q3a10/res = 9090.91 -q3a10/clen = /LCLS/detectorPosition q3a10/fs = -0.9999980927x -0.0019562324y q3a10/ss = +0.0019562324x -0.9999980927y q3a10/corner_x = 806.906 q3a10/corner_y = -655.522 -q3a10/no_index = 0 - q3a11/min_fs = 1358 q3a11/min_ss = 925 q3a11/max_fs = 1551 q3a11/max_ss = 1109 -q3a11/badrow_direction = - -q3a11/res = 9090.91 -q3a11/clen = /LCLS/detectorPosition q3a11/fs = -0.9999980927x -0.0019562324y q3a11/ss = +0.0019562324x -0.9999980927y q3a11/corner_x = 609.906 q3a11/corner_y = -655.907 -q3a11/no_index = 0 - q0a12/min_fs = 0 q0a12/min_ss = 1110 q0a12/max_fs = 193 q0a12/max_ss = 1294 -q0a12/badrow_direction = - -q0a12/res = 9090.91 -q0a12/clen = /LCLS/detectorPosition q0a12/fs = -0.9999986887x -0.0016366633y q0a12/ss = +0.0016366633x -0.9999986887y q0a12/corner_x = 416.434 q0a12/corner_y = 791.587 -q0a12/no_index = 0 - q0a13/min_fs = 194 q0a13/min_ss = 1110 q0a13/max_fs = 387 q0a13/max_ss = 1294 -q0a13/badrow_direction = - -q0a13/res = 9090.91 -q0a13/clen = /LCLS/detectorPosition q0a13/fs = -0.9999986887x -0.0016366633y q0a13/ss = +0.0016366633x -0.9999986887y q0a13/corner_x = 219.435 q0a13/corner_y = 791.265 -q0a13/no_index = 0 - q1a12/min_fs = 388 q1a12/min_ss = 1110 q1a12/max_fs = 581 q1a12/max_ss = 1294 -q1a12/badrow_direction = - -q1a12/res = 9090.91 -q1a12/clen = /LCLS/detectorPosition q1a12/fs = +0.0016421415x -0.9999986291y q1a12/ss = +0.9999986291x +0.0016421415y q1a12/corner_x = -781.411 q1a12/corner_y = 419.856 -q1a12/no_index = 0 - q1a13/min_fs = 582 q1a13/min_ss = 1110 q1a13/max_fs = 775 q1a13/max_ss = 1294 -q1a13/badrow_direction = - -q1a13/res = 9090.91 -q1a13/clen = /LCLS/detectorPosition q1a13/fs = +0.0016421415x -0.9999986291y q1a13/ss = +0.9999986291x +0.0016421415y q1a13/corner_x = -781.088 q1a13/corner_y = 222.856 -q1a13/no_index = 0 - q2a12/min_fs = 776 q2a12/min_ss = 1110 q2a12/max_fs = 969 q2a12/max_ss = 1294 -q2a12/badrow_direction = - -q2a12/res = 9090.91 -q2a12/clen = /LCLS/detectorPosition q2a12/fs = +0.9999987483x -0.0015812991y q2a12/ss = +0.0015812991x +0.9999987483y q2a12/corner_x = -423.530 q2a12/corner_y = -793.420 -q2a12/no_index = 0 - q2a13/min_fs = 970 q2a13/min_ss = 1110 q2a13/max_fs = 1163 q2a13/max_ss = 1294 -q2a13/badrow_direction = - -q2a13/res = 9090.91 -q2a13/clen = /LCLS/detectorPosition q2a13/fs = +0.9999987483x -0.0015812991y q2a13/ss = +0.0015812991x +0.9999987483y q2a13/corner_x = -226.531 q2a13/corner_y = -793.732 -q2a13/no_index = 0 - q3a12/min_fs = 1164 q3a12/min_ss = 1110 q3a12/max_fs = 1357 q3a12/max_ss = 1294 -q3a12/badrow_direction = - -q3a12/res = 9090.91 -q3a12/clen = /LCLS/detectorPosition q3a12/fs = +0.0000857039x +1.0000000000y q3a12/ss = -1.0000000000x +0.0000857039y q3a12/corner_x = 787.680 q3a12/corner_y = -418.886 -q3a12/no_index = 0 - q3a13/min_fs = 1358 q3a13/min_ss = 1110 q3a13/max_fs = 1551 q3a13/max_ss = 1294 -q3a13/badrow_direction = - -q3a13/res = 9090.91 -q3a13/clen = /LCLS/detectorPosition q3a13/fs = +0.0000857039x +1.0000000000y q3a13/ss = -1.0000000000x +0.0000857039y q3a13/corner_x = 787.697 q3a13/corner_y = -221.886 -q3a13/no_index = 0 - q0a14/min_fs = 0 q0a14/min_ss = 1295 q0a14/max_fs = 193 q0a14/max_ss = 1479 -q0a14/badrow_direction = - -q0a14/res = 9090.91 -q0a14/clen = /LCLS/detectorPosition q0a14/fs = -0.9999981523x -0.0019349456y q0a14/ss = +0.0019349456x -0.9999981523y q0a14/corner_x = 416.424 q0a14/corner_y = 579.070 -q0a14/no_index = 0 - q0a15/min_fs = 194 q0a15/min_ss = 1295 q0a15/max_fs = 387 q0a15/max_ss = 1479 -q0a15/badrow_direction = - -q0a15/res = 9090.91 -q0a15/clen = /LCLS/detectorPosition q0a15/fs = -0.9999981523x -0.0019349456y q0a15/ss = +0.0019349456x -0.9999981523y q0a15/corner_x = 219.424 q0a15/corner_y = 578.689 -q0a15/no_index = 0 - q1a14/min_fs = 388 q1a14/min_ss = 1295 q1a14/max_fs = 581 q1a14/max_ss = 1479 -q1a14/badrow_direction = - -q1a14/res = 9090.91 -q1a14/clen = /LCLS/detectorPosition q1a14/fs = +0.0016421643x -0.9999986291y q1a14/ss = +0.9999986291x +0.0016421643y q1a14/corner_x = -576.411 q1a14/corner_y = 419.856 -q1a14/no_index = 0 - q1a15/min_fs = 582 q1a15/min_ss = 1295 q1a15/max_fs = 775 q1a15/max_ss = 1479 -q1a15/badrow_direction = - -q1a15/res = 9090.91 -q1a15/clen = /LCLS/detectorPosition q1a15/fs = +0.0016421643x -0.9999986291y q1a15/ss = +0.9999986291x +0.0016421643y q1a15/corner_x = -576.088 q1a15/corner_y = 222.856 -q1a15/no_index = 0 - q2a14/min_fs = 776 q2a14/min_ss = 1295 q2a14/max_fs = 969 q2a14/max_ss = 1479 -q2a14/badrow_direction = - -q2a14/res = 9090.91 -q2a14/clen = /LCLS/detectorPosition q2a14/fs = +0.9999768734x -0.0068050129y q2a14/ss = +0.0068050129x +0.9999768734y q2a14/corner_x = -423.430 q2a14/corner_y = -582.404 -q2a14/no_index = 0 - q2a15/min_fs = 970 q2a15/min_ss = 1295 q2a15/max_fs = 1163 q2a15/max_ss = 1479 -q2a15/badrow_direction = - -q2a15/res = 9090.91 -q2a15/clen = /LCLS/detectorPosition q2a15/fs = +0.9999768734x -0.0068050129y q2a15/ss = +0.0068050129x +0.9999768734y q2a15/corner_x = -226.435 q2a15/corner_y = -583.745 -q2a15/no_index = 0 - q3a14/min_fs = 1164 q3a14/min_ss = 1295 q3a14/max_fs = 1357 q3a14/max_ss = 1479 -q3a14/badrow_direction = - -q3a14/res = 9090.91 -q3a14/clen = /LCLS/detectorPosition q3a14/fs = -0.0010058292x +0.9999995232y q3a14/ss = -0.9999995232x -0.0010058292y q3a14/corner_x = 575.673 q3a14/corner_y = -418.865 -q3a14/no_index = 0 - q3a15/min_fs = 1358 q3a15/min_ss = 1295 q3a15/max_fs = 1551 q3a15/max_ss = 1479 -q3a15/badrow_direction = - -q3a15/res = 9090.91 -q3a15/clen = /LCLS/detectorPosition q3a15/fs = -0.0010058292x +0.9999995232y q3a15/ss = -0.9999995232x -0.0010058292y q3a15/corner_x = 575.475 q3a15/corner_y = -221.866 -q3a15/no_index = 0 diff --git a/doc/man/ambigator.1 b/doc/man/ambigator.1 index d403f2aa..fad89641 100644 --- a/doc/man/ambigator.1 +++ b/doc/man/ambigator.1 @@ -25,7 +25,7 @@ The algorithm proceeds by calculating the individual correlation coefficients be Only one indexing ambiguity can be resolved at a time. In other words, each crystal is considered to be indexable in one of only two ways. If the true indexing ambiguity has more possibilities than this, the resolution must be performed by running \fBambigator\fR multiple times with a different ambiguity operator each time. -If the ambiguity operator is known (or, equivalently, the actual and apparent symmetries are both known), then the algorithm can be enhanced by including in \fIf\fR the correlation coefficients of all the crystals with the opposite indexing assignment to the current one, but after reindexing the other crystal first. Likewise, \fg\fR includes the correlation coefficients of the crystals with the same indexing assignment after reindexing. This enhances the algorithm to an extent roughly equivalent to doubling the number of crystals. +If the ambiguity operator is known (or, equivalently, the actual and apparent symmetries are both known), then the algorithm can be enhanced by including in \fIf\fR the correlation coefficients of all the crystals with the opposite indexing assignment to the current one, but after reindexing the other crystal first. Likewise, \fIg\fR includes the correlation coefficients of the crystals with the same indexing assignment after reindexing. This enhances the algorithm to an extent roughly equivalent to doubling the number of crystals. The default behaviour is to compare each crystal to every other crystal. This leads to a computational complexity proportional to the square of the number of crystals. If the number of crystals is large, the number of comparisons can be limited without compromising the algorithm much. In this case, the crystals to correlate against will be selected randomly. diff --git a/doc/man/check_hkl.1 b/doc/man/check_hkl.1 index ad66dfb2..f02b581b 100644 --- a/doc/man/check_hkl.1 +++ b/doc/man/check_hkl.1 @@ -17,7 +17,7 @@ check_hkl \- calculate figures of merit for reflection data \fBcheck_hkl --help\fR .SH DESCRIPTION -check_hkl calculates figures of merit for reflection data, such as completeness and average signal strengths, in resolution shells. check_hkl accepts a single reflection list in CrystFEL's format, and you must also provide a unit cell (in a PDB file). +check_hkl calculates figures of merit for reflection data, such as completeness and average signal strengths, in resolution shells. check_hkl accepts a single reflection list in CrystFEL's format, and you must also provide a unit cell (in a PDB file or CrystFEL unit cell format). .SH OPTIONS .PD 0 diff --git a/doc/man/compare_hkl.1 b/doc/man/compare_hkl.1 index b628f0b6..8e464d12 100644 --- a/doc/man/compare_hkl.1 +++ b/doc/man/compare_hkl.1 @@ -12,12 +12,12 @@ compare_hkl \- compare reflection data .SH SYNOPSIS .PP -\fBcompare_hkl\fR \fR [\fIoptions\fR] \fB...\fR \fIfile1.hkl\fR \fIfile2.hkl\fR +\fBcompare_hkl\fR [\fIoptions\fR] \fB... \fIfile1.hkl \fIfile2.hkl\fR -p \fIcell.pdb\fR .PP \fBcompare_hkl --help\fR .SH DESCRIPTION -compare_hkl compares two sets of reflection data and calculates figures of merit such as R-factors. Reflections will be considered equivalent according to your choice of point group. +compare_hkl compares two sets of reflection data and calculates figures of merit such as R-factors. Reflections will be considered equivalent according to your choice of point group. You need to provide a unit cell, as a PDB file or a CrystFEL unit cell file. .SH OPTIONS .PD 0 diff --git a/doc/man/crystfel.7 b/doc/man/crystfel.7 index fd3f337c..bc545069 100644 --- a/doc/man/crystfel.7 +++ b/doc/man/crystfel.7 @@ -25,7 +25,7 @@ The crystal orientations in each pattern are random and uncorrelated, which lead .RE CrystFEL includes programs for simulating and processing patterns subject to the -above characteristics. Twelve programs form the core of CrystFEL. They are: +above characteristics. Fifteen programs form the core of CrystFEL. They are: .IP \fBindexamajig\fR Batch indexing, integration and data reduction program, which produces a "stream" containing the indexing and integration results for each diffraction pattern. @@ -63,6 +63,12 @@ A tool for rendering slices of reciprocal space in two dimensions. .IP \fBgeoptimiser\fR A program to refine and optimize detector geometry. +.IP \fBlist_events\fR +A tool for creating lists of events from multi-event data files. + +.IP \fBwhirligig\fR +A tool for locating runs of crystals with similar orientations, e.g. from 'mini rotation series' arising from the use of a slow extrusion sample injector. + .PP There is also a folder full of scripts for achieving many related tasks. @@ -149,4 +155,7 @@ You should have received a copy of the GNU General Public License along with Cry .BR render_hkl (1), .BR hdfsee (1), .BR get_hkl (1), +.BR geoptimiser (1), +.BR whirligig (1), +.BR list_events (1), .BR crystfel_geometry (5). diff --git a/doc/man/crystfel_geometry.5 b/doc/man/crystfel_geometry.5 index 44fe4450..974d602b 100644 --- a/doc/man/crystfel_geometry.5 +++ b/doc/man/crystfel_geometry.5 @@ -55,14 +55,13 @@ Information about the layout of the file data can be included in the geometry fi This allows CrystFEL to unambigously identify data blocks which contain data for a specific event, and to determine the number of events that each file contains. - The geometry file should contain lines of the following form: .IP -\fIpanel\fR\fB/\fIproperty\fB = \fIvalue\fR +\fIpanel\fR/clen = 0.560 .PP -\fIpanel\fR can be any name of your choosing. You can make up names for your panels however you please, as long as the first three letters are not "\fBbad\fR" (in lower case), because this is used to identify bad regions. +\fIpanel\fR can be any name of your choosing. You can make up names for your panels however you please, as long as the first three letters are not "\fBbad\fR" or "rigid_group" (in lower case), because these are used for special purposes (see below). .PP You can also specify values without a panel name, for example: @@ -133,7 +132,7 @@ The range of pixels in the data block specified by the 'data' property that corr .PD 0 .IP \fBadu_per_eV\fR .PD -The number of detector intensity units (ADU) which will arise from one electron-Volt of photon energy. This is used to estimate Poisson errors. +The number of detector intensity units (ADU) which will arise from one electron-Volt of photon energy. This is used to estimate Poisson errors. Note that setting different values for this parameter for different panels does \fBnot\fR result in the intensities being scaled accordingly. .PD 0 .IP \fBbadrow_direction\fR @@ -149,7 +148,7 @@ The resolution (in pixels per metre) for this panel. This is one over the pixel .PD 0 .IP \fBclen\fR .PD -The camera length (in metres) for this panel. You can also specify the HDF path to a floating point data block containing the camera length in millimetres. For example: "panel0/clen = /LCLS/detectorPosition". If the HDF file contains more than one event, and the data block is scalar, the camera length value +The camera length (in metres) for this panel. You can also specify the HDF5 path to a floating point data block containing the camera length in millimetres. For example: "panel0/clen = /LCLS/detectorPosition". If the HDF5 file contains more than one event, and the data block is scalar, the camera length value it contains will be used for all events. If, however, the data block is multidimensional and the second dimension is bigger than one, the CrystFEL programs will try to match the content of the data block with the events in the file, assigning the first value in the data block to the first event in the file, the second value in the data block to the second event in the file, etc. See \fBcoffset\fR as well. @@ -187,7 +186,7 @@ Set this to 1 or "true" to ignore this panel completely. .IP \fBmask_bad\fR .PD Bitmasks for bad pixel masks. The pixel is considered good if all of the bits which are set in \fBmask_good\fR are set, \fIand\fR if none of the bits which are set in \fBmask_bad\fR are set. Example: -.br +.IP mask = /processing/hitfinder/masks .br mask_good = 0x27 @@ -197,7 +196,9 @@ mask_bad = 0x00 .SH BAD REGIONS -You can also specify bad regions. Peaks with centroid locations within such a region will not be integrated nor used for indexing. Bad regions are specified in pixel units, either in the lab coordinate system (see above) or in fast scan/slow scan coordinates (mixtures are not allowed). In the latter case, the range of pixels is specified \fIinclusively\fR. Bad regions are distinguished from normal panels by the fact that they begin with the three letters "bad". +You can also specify bad regions. Bad regions will be completely ignored by CrystFEL. Bad regions are specified in pixel units, either in the lab coordinate system (see above) or in fast scan/slow scan coordinates (mixtures are not allowed). In the latter case, the range of pixels is specified \fIinclusively\fR. Bad regions are distinguished from normal panels by the fact that they begin with the three letters "bad". +.PP +You can specify a panel name for the bad region, in which case the pixels will only be considered bad if they are within the range you specify \fIand\fR in the panel you specify. This might be necessary if your HDF5 file layout has overlapping ranges of fs/ss coordinates for different panels (e.g. if the data blocks for the panels are in different HDF5 datasets). Examples: .br @@ -218,6 +219,8 @@ badregionB/max_fs = 160 badregionB/min_ss = 256 .br badregionB/max_ss = 512 +.br +badregionB/panel = q0a1 .SH RIGID GROUPS AND RIGID GROUP COLLECTIONS @@ -248,7 +251,7 @@ The geometry file can include information about beam characteristics, using gene .PD 0 .IP \fBphoton_energy\fR .PD -The beam photon energy in eV. You can also specify the HDF path to a floating point data block value containing the photon energy in eV. For example: "photon_energy = /LCLS/photon_energy_eV". If the HDF file contains more than one event, and the data block is scalar, the photon energy value +The beam photon energy in eV. You can also specify the HDF5 path to a floating point data block value containing the photon energy in eV. For example: "photon_energy = /LCLS/photon_energy_eV". If the HDF5 file contains more than one event, and the data block is scalar, the photon energy value it contains will be used for all events. If, however, the data block is multidimensional and the second dimension is bigger than one, the CrystFEL programs will try to match the content of the data block with the events in the file, assigning the first value in the data block to the first event in the file, the second value in the data block to the second event in the file, etc. See also \fBphoton_energy_scale\fR. diff --git a/doc/man/geoptimiser.1 b/doc/man/geoptimiser.1 index 19ce4534..2a544830 100644 --- a/doc/man/geoptimiser.1 +++ b/doc/man/geoptimiser.1 @@ -14,7 +14,7 @@ geoptimiser \- detector geometry refinement .PP .BR geoptimiser \fB-i \fIinput.stream \fB-g \fIinput.geom \fB-o \fIoutput.geom \fB-c \fIconnected_rigidgroup_coll \fB-q \fI\quadrant_rigidgroup_coll\fR -[\fBoptions\fR] \fB...\fR +[\fBoptions\fR] .PP \fBgeoptimiser --help\fR @@ -33,12 +33,17 @@ the name of two rigid group collections defined in the geometry file: one descri .PP See \fBman crystfel_geometry\fR for information on how to create a file describing the detector geometry, and guidelines to define the required rigid groups and rigid groups collections. +.PP +Geoptimizer saves error maps before and after the geometry optimization. These maps show an estimation of the geometry error as average displacement, for each pixel, between the predicted and the observed positions of the Bragg peaks. The maps are color-scaled PNG files and +are named "error_map_before.png" and "error_map_after.png" respectively. In order to better visualize small local errors, the color range has been set to show all displacements bigger that 1 pixel as white (in other words, "displacement saturation" is set at 1 pixel). + + .SH OPTIONS .PD 0 .IP "\fB-i\fR \fIfilename\fR" .IP \fB--input=\fR\fIfilename\fR .PD -Read the indexed patterns from the \fIfilename\fR stream file. +Give the filename of the stream from which to read the indexed patterns. .PD 0 .IP "\fB-g\fR \fIfilename\fR" @@ -69,6 +74,10 @@ Specifies the name of the rigid group collection for detector 'quadrants'. This .sp If a given rigid group is a member of \fIname\fR, then panels which are members of that rigid group and which do not contain enough peaks for positional refinement will be moved according to the average movement of the other panels in the group. +.PD 0 +.IP \fB--no-error-maps\fR +.PD +Forces geoptimiser not to save error maps. .PD 0 .IP "\fB-x\fR \fIn\fR" @@ -108,6 +117,16 @@ to be included in the optimization process. The default maximum distance is 8 pi .PD By default, geoptimiser refines the distance between the detector and the sample. This option turns off this optimization. +.PD 0 +.IP \fB--no-cspad\fR +.PD +If a geometry file containing 64 panels (ASICs) is provided by the user, geoptimiser assumes that the detector described by the geometry file is a CSPAD, and performs some sanity-checks on the relative ditance and orientation of connected panels. If the checks fail, the geometry optimization is stopped. This option turns off these checks. + +.PD 0 +.IP \fB--enforce-cspad-layout\fR +.PD +When this option is used, geoptimiser tries to fix the CSPAD layout problems detected by the checks described in the entry for the \fB--no-cspad\fR option. Immediately after performing this operation, geoptimser saves the new detector layout in the output refined geometry file and exits. + .SH AUTHOR This page was written by Valerio Mariani, Oleksandr Yefanov and Thomas White. @@ -115,7 +134,7 @@ This page was written by Valerio Mariani, Oleksandr Yefanov and Thomas White. Report bugs to <taw@physics.org>, or visit <http://www.desy.de/~twhite/crystfel>. .SH COPYRIGHT AND DISCLAIMER -Copyright © 2012-2015 Deutsches Elektronen-Synchrotron DESY, a research centre of the Helmholtz Association. +Copyright © 2014-2015 Deutsches Elektronen-Synchrotron DESY, a research centre of the Helmholtz Association. .P geoptimiser, and this manual, are part of CrystFEL. .P diff --git a/doc/man/hdfsee.1 b/doc/man/hdfsee.1 index 1b2834c2..7a4dddb6 100644 --- a/doc/man/hdfsee.1 +++ b/doc/man/hdfsee.1 @@ -43,7 +43,11 @@ Show the image after binning down by a factor of \fIb\fR. .IP "\fB-e\fR \fIpath\fR" .IP \fB--image=\fR\fIpath\fR .PD -Get the image data to display from \fIpath\fR inside the HDF5 file. For example: \fI/data/rawdata\fR. If this is not specified, the default behaviour is to display the first two-dimensional dataset with both dimensions greater than 64. +Get the image data to display from \fIpath\fR inside the HDF5 file. Example: \fI/data/rawdata\fR. +.IP +If no geometry file is provided (see \fB-g\fR) and this option is not used, hdfsee will display the first two-dimensional dataset it finds in the file with both dimensions greater than 64. If a geometry file is provided, the data layout description from the file determines which image is displayed (see \fB man crystfel_geometry\fR). +.IP +If \fB-e\fR is used in combination with \fB-g\fR, hdfsee will attempt to show the specified dataset with the geometry applied. The data layout description from the geometry file will be ignored, the data block to be used as data source for all panels will be set to \fIpath\fR and it will be assumed to be 2-dimensional. .PD 0 .IP "\fB-g\fR \fIfilename\fR" @@ -91,9 +95,13 @@ Apply a noise filter to the image with checks 3x3 squares of pixels and sets all .SH CALIBRATION MODE Calibration mode allows you to visually adjust the locations of panels. To enter calibration mode, select Tools->Calibration Mode from the menu. The currently selected panel will be bordered in white. Press + or - to move to the next or previous panel (as listed in the geometry file). Use the arrow keys to move the current panel. Press 'f' to hide or restore the white border. Press 's' to save the geometry file with your modifications. Press 'g' to toggle between moving individual panels, rigid groups (if any are defined in the geometry file) and moving all panels together. +Most of these actions can also be accessed from the Calibration menu, which becomes aptive when calibration mode is toggled once + +.SH EVENT NAVIGATION +When multi-event files are opened, the Events menu in the menubar becomes active, and some event navigation tools become available. The title bar shows, in addition to the file name, the name of the event currently displayed (See \fBman crystfel_geometry\fR and \fBman indexamajig\fR for a description of event naming). Press 'n' to move to the next event in the file, and 'p' to move to the previous one'. Press 'e' to jump to a specific event, by providing an event name (Use the \fBlist_events\fR program to get a list of the events included in a file). Press 'r' to jump to a random event. These actions are also accessible from the Events menu in the menubar. .SH AUTHOR -This page was written by Thomas White. +This page was written by Thomas White and Valerio Mariani. .SH REPORTING BUGS Report bugs to <taw@physics.org>, or visit <http://www.desy.de/~twhite/crystfel>. diff --git a/doc/man/indexamajig.1 b/doc/man/indexamajig.1 index b46b663b..75fa3e6c 100644 --- a/doc/man/indexamajig.1 +++ b/doc/man/indexamajig.1 @@ -1,7 +1,7 @@ .\" .\" indexamajig man page .\" -.\" Copyright © 2012-2014 Deutsches Elektronen-Synchrotron DESY, +.\" Copyright © 2012-2015 Deutsches Elektronen-Synchrotron DESY, .\" a research centre of the Helmholtz Association. .\" .\" Part of CrystFEL - crystallography with a FEL @@ -26,22 +26,24 @@ For minimal basic use, you need to provide the list of diffraction patterns, the .IP \fBindexamajig\fR .PD --i mypatterns.lst -j 10 -g mygeometry.geom --indexing=mosflm,dirax --peaks=hdf5 -o test.stream -p mycell.pdb +-i mypatterns.lst -g mygeometry.geom --indexing=mosflm,dirax --peaks=hdf5 -o test.stream -p mycell.pdb .PP More typical use includes all the above, but might also include extra parameters to modify the behaviour. For example, you'll probably want to run more than one indexing job at a time (-j <n>). -See \fBman crystfel_geometry\fR for information about how to create a file describing detector geometry and beam characteristics. +See \fBman crystfel_geometry\fR for information about how to create a file describing the detector geometry and beam characteristics. .SH DIFFRACTION PATTERN LIST -Indexamajig requires an input file with a list of diffraction patterns ("events") to process. In its simplest form, this is just a text files containing a list of HDF5 filenames. The HDF5 files might be in some folder a long way from the current directory, so you might want to specify a full pathname to be added in front of each filename. The geometry file includes a description of the data layout within the HDF5 files. Indexamajig uses this description to make an educated guess at the number of diffraction patterns stored in each file, and tries to process them all. You can however single out an event contained in the file for processing, by putting a string describing the event after the file name (see below). +Indexamajig requires an input file with a list of diffraction patterns ("events") to process. In its simplest form, this is just a text files containing a list of HDF5 filenames. The HDF5 files might be in some folder a long way from the current directory, so you might want to specify a full pathname to be added in front of each filename. The geometry file includes a description of the data layout within the HDF5 files. Indexamajig uses this description to determine the number of diffraction patterns stored in each file, and tries to process them all. You can also specify explicity which event(s) you would like to process by putting a string describing the event after the file name(s) in this list. .SH PEAK DETECTION -You can control the peak detection on the command line. Firstly, you can choose the peak detection method using \fB--peaks=\fR\fImethod\fR. Currently, two values for "method" are available. \fB--peaks=hdf5\fR will take the peak locations from the HDF5 file. It expects a two dimensional array, by default at /processing/hitfinder/peakinfo, whose size in the first dimension equals the number of peaks and whose size in the second dimension is three. The first two columns contain the fast scan and slow scan coordinates, the third contains the intensity. However, the intensity will be ignored since the pattern will always be re-integrated using the unit cell provided by the indexer on the basis of the peaks. You can tell indexamajig where to find this table inside each HDF5 file using \fB--hdf5-peaks=\fR\fIpath\fR. +You can control the peak detection on the command line. Firstly, you can choose the peak detection method using \fB--peaks=\fR\fImethod\fR. There are three possibilities for "method" here. \fB--peaks=hdf5\fR will take the peak locations from the HDF5 file. It expects a two dimensional array, by default at /processing/hitfinder/peakinfo, whose size in the first dimension equals the number of peaks and whose size in the second dimension is three. The first two columns contain the fast scan and slow scan coordinates, the third contains the intensity. However, the intensity will be ignored since the pattern will always be re-integrated using the unit cell provided by the indexer on the basis of the peaks. You can tell indexamajig where to find this table inside each HDF5 file using \fB--hdf5-peaks=\fR\fIpath\fR. + +\fB--peaks=cxi\fR works similarly to this, but expects four separate HDF5 datasets beneath \fIpath\fR, \fBnPeaks\fR, \fBpeakXPosRaw\fR, \fBpeakYPosRaw\fR and \fBpeakTotalIntensity\fR. See the specification for the CXI file format at http://www.cxidb.org/ for more details. If you use \fB--peaks=zaef\fR, indexamajig will use a simple gradient search after Zaefferer (2000). You can control the overall threshold and minimum squared gradient for finding a peak using \fB--threshold\fR and \fB--min-gradient\fR. The threshold has arbitrary units matching the pixel values in the data, and the minimum gradient has the equivalent squared units. Peaks will be rejected if the 'foot point' is further away from the 'summit' of the peak by more than the inner integration radius (see below). They will also be rejected if the peak is closer than twice the inner integration radius from another peak. @@ -230,7 +232,7 @@ Prefix the filenames from the input file with \fIprefix\fR. If \fB--basename\fR .PD 0 .IP \fB--hdf5-peaks=\fR\fIpath\fR .PD -When using \fB--peaks=hdf5\fR, read the peak locations from a table in the HDF5 file located at \fIpath\fR. +When using \fB--peaks=hdf5\fR or \fB--peaks=cxi\fR, read the peak positions from location \fIpath\fR. See \fBPEAK DETECTION\fR above. .PD 0 .IP \fB--tolerance=\fR\fItol\fR @@ -263,7 +265,7 @@ Set the overall threshold for peak detection using \fB--peaks=zaef\fR to \fIthre .PD 0 .IP \fB--min-gradient=\fR\fIgrad\fR .PD -Set the gradient threshold for peak detection using \fB--peaks=zaef\fR to \fIgrad\fR, which units of "detector units per pixel". The default is \fB--min-gradient=100000\fR. +Set the square of the gradient threshold for peak detection using \fB--peaks=zaef\fR to \fIgrad\fR, which has units of "squared detector units per pixel". The default is \fB--min-gradient=100000\fR. The reason it's 'gradient squared' instead of just 'gradient' is historical. .PD 0 .IP \fB--min-snr=\fR\fIsnr\fR @@ -293,7 +295,7 @@ Normally, peaks which contain one or more pixels above max_adu (defined in the d .PD 0 .IP \fB--no-revalidate\fR .PD -When using \fB--peaks=hdf5\fR, the peaks will be put through some of the same checks as if you were using \fB--peaks=zaef\fR. These checks reject peaks which are too close to panel edges, are saturated (unless you use \fB--use-saturated\fR), have other nearby peaks (closer than twice the inner integration radius, see \fB--int-radius\fR), or have any part in a bad region. Using this option skips this validation step, and uses the peaks directly. +When using \fB--peaks=hdf5\fR or \fB--peaks=cxi\fR, the peaks will be put through some of the same checks as if you were using \fB--peaks=zaef\fR. These checks reject peaks which are too close to panel edges, are saturated (unless you use \fB--use-saturated\fR), have other nearby peaks (closer than twice the inner integration radius, see \fB--int-radius\fR), or have any part in a bad region. Using this option skips this validation step, and uses the peaks directly. .PD 0 .IP \fB--check-hdf5-snr\fR @@ -326,14 +328,25 @@ When \fBrescut\fR is in the integration method, integrate \fIn\fR nm^-1 higher t Mark all pixels on the detector higher than \fIn\fR Angstroms as bad. This might be useful when you have noisy patterns and don't expect any signal above a certain resolution. +.PD 0 +.IP \fB--fix-profile-radius=\fIn\fR +.IP \fB--fix-bandwidth=\fIn\fR +.IP \fB--fix-divergence=\fIn\fR +.PD +Fix the beam and crystal paramters to the given values. The profile radius is given in m^-1, the bandwidth as a decimal fraction and the divergence in radians (full angle). The default is to set the divergence to zero, the bandwidth to a very small value, and then to automatically determine the profile radius. +.IP +You do not have to use all three of these options together. For example, if the automatic profile radius determination is not working well for your data set, you could fix that alone and continue using the default values for the other parameters (which might be automatically determined in future versions of CrystFEL, but are not currently). + +.PD 0 +.IP \fB--no-refine +.PD +Skip the prediction refinement step. + .SH IDENTIFYING SINGLE PATTERNS IN THE INPUT FILE By default indexamajig processes all diffraction patterns ("events") in each of the data files listed in the input list. It is however, possible, to only process single events in a multi-event file, by adding in the list an event description string after the data filename. The event description always includes a first section with alphanumeric strings separated by forward slashes ("/") and a second section with integer numbers also separated by forward slashes. The two sections are in turn separated by a double forward slash ('//'). Any of the two sections can be empty, but the double forward slash separator must always be present. Indexamajig matches the strings and the numbers in the event description with the event placeholders ('%') present respectively in the 'data' and 'dim' properties defined in the geometry file, and tries to retrieve the full HDF path to the event data and the the its location in a multi-dimensional data space. Consider the following examples: -Example 1: -.br - -Assuming that the 'data' and 'dim' properties have been defined like this in the geometry file: +\fBExample 1:\fR The 'data' and 'dim' properties have been defined like this in the geometry file: .br data = /data/%/rawdata @@ -342,18 +355,15 @@ dim0 = ss .br dim1 = fs -The following line: +The event list contains the following line: .br filename.h5 event1// .br -Identifies an event in the 2-dimensional data block lying at the /data/event1/rawdata HDF path in the filename.h5 file - -Example 2: -.br +This identifies an event in the 2-dimensional data block located at /data/event1/rawdata in the HDF5 file called filename.h5. -Assuming that the 'data' and 'dim' properties have been defined like this in the geometry file: +\fBExample 2:\fR The 'data' and 'dim' properties have been defined like this in the geometry file: .br data = /data/rawdata @@ -364,18 +374,21 @@ dim1 = ss .br dim2 = fs -The following line: +The event list contains the following line: .br filename.h5 //3 .br -Identifies an event in the 3-dimensional data block lying at the /data/rawdata HDF path in the filename.h5 file, specifically the 2-dimensional data slice defined by the value 3 of the first axis of the data space. +This identifies an event in the 3-dimensional data block located at /data/rawdata in the HDF5 file called filename.h5, specifically the 2-dimensional data slice defined by the value 3 of the first axis of the data space. Indexamajig tries to match the alphanumerical strings to the placeholders in the 'dim' property defined in the geometry file. The first string is matched to the first placeholder, the second to the second placeholder, and so on. A similar strategy is followed to match integer numbers to the placeholders in the 'dim' property defined in the geometry file. For a full explanation of how the internal layout of the data file can be described in the geometry file, please see \fBman crystfel_geometry\fR. +You can use \fBlist_events\fR to prepare a list of each event in one or more input files. Note that you only need to do this if you need to perform some sorting or filtering on this list. If you want to process every event in a file, simply specify the filename in the input file. + + .SH BUGS ReAx indexing is experimental. It works very nicely for some people, and crashes for others. In a future version, it will be improved and fully supported. @@ -401,4 +414,6 @@ You should have received a copy of the GNU General Public License along with Cry .BR crystfel_geometry (5), .BR cell_explorer (1), .BR process_hkl (1), -.BR partialator (1) +.BR partialator (1), +.BR list_events (1), +.BR whirligig (1) diff --git a/doc/man/list_events.1 b/doc/man/list_events.1 index d1e080e3..900288f6 100644 --- a/doc/man/list_events.1 +++ b/doc/man/list_events.1 @@ -1,7 +1,8 @@ .\" .\" list_events man page .\" -.\" Copyright © 2015 Thomas White <taw@physics.org> +.\" Copyright © 2015 Deutsches Elektronen-Synchrotron DESY, +.\" a research centre of the Helmholtz Association. .\" .\" Part of CrystFEL - crystallography with a FEL .\" @@ -16,7 +17,7 @@ list_events \- generate event lists \fBlist_events --help\fI .SH DESCRIPTION -list_events expands a list of filenames, where each file contains events in a multi-event format (e.g. the CXI format, http://www.cxidb.org/), into a list of individual events. +list_events expands a list of filenames, where each file contains events in a multi-event format (e.g. the CXI format, http://www.cxidb.org/), into a list of individual events. This might be useful if you need to sort or filter the event list prior to processing, rather than just processing all events. .SH OPTIONS diff --git a/doc/man/partial_sim.1 b/doc/man/partial_sim.1 index 67b05edc..e11c75e7 100644 --- a/doc/man/partial_sim.1 +++ b/doc/man/partial_sim.1 @@ -15,7 +15,6 @@ partial_sim \- calculate partial reflections .BR partial_sim \fB-o\fR \fIsimulated.stream\fR \fB-g\fR \fIgeometry.geom\fR -\fB-b\fR \fIxrays.beam\fR \fB-p\fR \fIunitcell.pdb\fR [\fIoptions\fR] \fB...\fR @@ -113,7 +112,7 @@ Use \fIn\fR threads for simulation. Default: 1. .B .IP "\fB--images=\fR\fIprefix\fR" .PD -For each chunk in the output stream, write a 'sketch' image in HDF5 format to \fIprefix\fR\fB/sim-\fR\fINNN\fR\fB.h5\fR, where \fINNN\fR is the sequence number of the chunk in the output stream. This option is incompatible with \fB-j\fR. The intensities in the peaks in the sketches will have th +For each chunk in the output stream, write a 'sketch' image in HDF5 format to \fIprefix\fR\fB/sim-\fR\fINNN\fR\fB.h5\fR, where \fINNN\fR is the sequence number of the chunk in the output stream. This option is incompatible with \fB-j\fR. The intensities in the peaks in the sketches will be equal to the partial intensities in the stream, including noise and overall scaling factors. The images will also contain a random Poisson-distributed background according to \fB--background\fR. .PD 0 .B @@ -145,6 +144,10 @@ Set the radius of the scattering density surrounding each reciprocal lattice poi .PD Set the central photon energy, in eV, for the incident beam. The default is \fB--photon-energy=9000\fR, i.e. 9 keV X-rays. +.PD 0 +.IP \fB--really-random\fR +.PD +Seed the random number generator using the kernel random number generator (/dev/urandom). This means that truly random numbers for the orientation and crystal size, instead of the same sequence being used for each new run. .SH AUTHOR This page was written by Thomas White. diff --git a/doc/man/partialator.1 b/doc/man/partialator.1 index 75fc34f2..63fb062c 100644 --- a/doc/man/partialator.1 +++ b/doc/man/partialator.1 @@ -93,20 +93,29 @@ Include a reflection in the output only if it appears at least least \fIn\fR tim The available partiality models are: -.IP \fBsphere\fR +.IP \fBscsphere\fR .PD The volume of intersection between a sphere centered on each reciprocal lattice -point, and the part of reciprocal space excited by the Ewald sphere taking into -account the finite bandwidth and convergence angle. A Lorentz factor will also -be used, proportional to the distance between the limiting Ewald spheres. +point and the part of reciprocal space excited by the Ewald sphere taking into +account the finite bandwidth and convergence angle. A "source coverage factor" +will be included to take into account the spectral brightness of the effective +source for the reflection. -For a full description including diagrams, see T. A. White et al., Acta Cryst. -D69 (2013) p1231-1240. +This model is similar to that described in Acta Cryst. D69 (2013) p1231-1240, +and in Phil. Trans. Roy. Soc. B 369 (2014) 20130330, except that the "Lorentz +factor" described there is no longer treated as a separate factor. -.IP \fBunity\fR + +.IP \fBscgaussian\fR .PD -Fix all partialities at 1, and use no Lorentz factor at all. +As \fBscsphere\fR, except that the shape of the scattering density centered on +each reciprocal lattice point is taken to be a 3D Gaussian distribution instead +of a sphere. The standard deviation of the distribution will be the profile +radius (determined by indexamajig) divided by 2.6. +.IP \fBunity\fR +.PD +Fix all partialities at 1. .SH BUGS diff --git a/doc/reference/libcrystfel/CrystFEL-docs.sgml b/doc/reference/libcrystfel/CrystFEL-docs.sgml index 1aef3ddb..ae0a115a 100644 --- a/doc/reference/libcrystfel/CrystFEL-docs.sgml +++ b/doc/reference/libcrystfel/CrystFEL-docs.sgml @@ -8,7 +8,7 @@ <bookinfo> <title>CrystFEL Reference Manual</title> <releaseinfo> - For libcrystfel from CrystFEL 0.5.4. + For libcrystfel from CrystFEL 0.6.0. </releaseinfo> <abstract> This is the internal documentation for CrystFEL. Unless you are looking at @@ -43,6 +43,11 @@ </chapter> <chapter> + <title>Events</title> + <xi:include href="xml/events.xml"/> + </chapter> + + <chapter> <title>Geometry of diffraction</title> <xi:include href="xml/geometry.xml"/> </chapter> diff --git a/doc/reference/libcrystfel/CrystFEL-sections.txt b/doc/reference/libcrystfel/CrystFEL-sections.txt index f3b5dc59..7b06c2cd 100644 --- a/doc/reference/libcrystfel/CrystFEL-sections.txt +++ b/doc/reference/libcrystfel/CrystFEL-sections.txt @@ -94,6 +94,7 @@ cell_get_reciprocal cell_get_centering cell_get_lattice_type cell_get_unique_axis +cell_has_parameters <SUBSECTION> cell_set_cartesian cell_set_parameters @@ -132,6 +133,7 @@ right_handed str_lattice forbidden_reflection load_cell_from_pdb +load_cell_from_file lattice_from_str </SECTION> @@ -202,10 +204,15 @@ histogram_set_num_bins <SECTION> <FILE>image</FILE> image +beam_params +imagefeature ImageFeatureList SpectrumType +sample +<SUBSECTION> image_add_feature image_feature_closest +image_reflection_closest image_feature_count image_feature_list_free image_feature_list_new @@ -327,11 +334,18 @@ intmat_print <SECTION> <FILE>detector</FILE> +detector +panel +badregion +rigid_group +rg_collection +<SUBSECTION> copy_geom fill_in_values free_detector_geometry get_detector_geometry write_detector_geometry +write_detector_geometry_2 find_panel find_panel_by_name find_panel_number @@ -342,15 +356,22 @@ get_q get_q_for_panel get_tt smallest_q -twod_mapping reverse_2d_mapping largest_q in_bad_region mark_resolution_range_as_bad +find_orig_panel +panel_is_in_rigid_group +rigid_group_is_in_collection +single_panel_data_source +find_rigid_group_collection_by_name </SECTION> <SECTION> <FILE>events</FILE> +event +event_list +<SUBSECTION> initialize_event push_path_entry_to_event pop_path_entry_from_event @@ -358,6 +379,7 @@ push_dim_entry_to_event pop_dim_entry_from_event copy_event free_event +find_event get_event_string get_event_from_event_string event_path_placeholder_subst @@ -374,6 +396,7 @@ initialize_dim_structure default_dim_structure set_dim_structure_entry free_dim_structure_entry +free_dim_structure </SECTION> <SECTION> @@ -383,6 +406,7 @@ free_dim_structure_entry <SECTION> <FILE>hdf5-file</FILE> hdf5_read +hdf5_read2 hdf5_write hdf5_write_image hdfile @@ -394,13 +418,17 @@ hdfile_read_group hdfile_set_first_image hdfile_set_image get_value +get_ev_based_value copy_hdf5_field copy_hdf5_fields add_copy_hdf5_field new_copy_hdf5_field_list free_copy_hdf5_field_list get_peaks +get_peaks_cxi hdfile_is_scalar +check_path_existence +fill_event_list </SECTION> <SECTION> @@ -419,6 +447,7 @@ crystal_get_profile_radius crystal_get_reflections crystal_get_resolution_limit crystal_get_user_flag +crystal_get_num_implausible_reflections crystal_set_cell crystal_set_image crystal_set_mosaicity @@ -428,16 +457,20 @@ crystal_set_profile_radius crystal_set_reflections crystal_set_resolution_limit crystal_set_user_flag +crystal_set_num_implausible_reflections </SECTION> <SECTION> <FILE>geometry</FILE> PartialityModel find_intersections +find_intersections_to_res select_intersections update_partialities update_partialities_2 polarisation_correction +sphere_fraction +gaussian_fraction </SECTION> <SECTION> @@ -501,4 +534,5 @@ rewind_stream is_stream write_command write_geometry_file +extract_f_from_stuff </SECTION> diff --git a/libcrystfel/Makefile.am b/libcrystfel/Makefile.am index 79699e4a..989ce434 100644 --- a/libcrystfel/Makefile.am +++ b/libcrystfel/Makefile.am @@ -1,6 +1,6 @@ lib_LTLIBRARIES = libcrystfel.la libcrystfel_la_LIBADD = ../lib/libgnu.la @LIBCRYSTFEL_LIBS@ $(PTY_LIB) -libcrystfel_la_LDFLAGS = -version-info 5:0:0 +libcrystfel_la_LDFLAGS = -version-info 6:0:0 libcrystfel_la_SOURCES = src/reflist.c src/utils.c src/cell.c src/detector.c \ src/thread-pool.c src/image.c src/hdf5-file.c \ diff --git a/libcrystfel/src/cell-utils.c b/libcrystfel/src/cell-utils.c index 5fc885e5..75419c99 100644 --- a/libcrystfel/src/cell-utils.c +++ b/libcrystfel/src/cell-utils.c @@ -3,13 +3,13 @@ * * Unit Cell utility functions * - * Copyright © 2012-2014 Deutsches Elektronen-Synchrotron DESY, + * Copyright © 2012-2015 Deutsches Elektronen-Synchrotron DESY, * a research centre of the Helmholtz Association. * Copyright © 2012 Lorenzo Galli * * Authors: - * 2009-2012,2014 Thomas White <taw@physics.org> - * 2012 Lorenzo Galli + * 2009-2012,2014-2015 Thomas White <taw@physics.org> + * 2012 Lorenzo Galli * * This file is part of CrystFEL. * @@ -606,10 +606,12 @@ UnitCell *match_cell(UnitCell *cell_in, UnitCell *template_in, int verbose, /* "Un-center" the template unit cell to make the comparison easier */ template = uncenter_cell(template_in, &uncentering); + if ( template == NULL ) return NULL; /* The candidate cell is also uncentered, because it might be centered * if it came from (e.g.) MOSFLM */ cell = uncenter_cell(cell_in, NULL); + if ( cell == NULL ) return NULL; if ( cell_get_reciprocal(template, &asx, &asy, &asz, &bsx, &bsy, &bsz, @@ -1090,8 +1092,9 @@ static void determine_lattice(UnitCell *cell, /** * load_cell_from_pdb: + * @filename: The filename from which to load the cell * - * Loads a unit cell from a PDB file. + * Loads a unit cell from the CRYST1 line of a PDB file. * * Returns: a newly allocated %UnitCell. * @@ -1161,7 +1164,12 @@ UnitCell *load_cell_from_pdb(const char *filename) fclose(fh); - validate_cell(cell); + if ( cell != NULL ) { + validate_cell(cell); + } else { + ERROR("Failed to load cell from %s\n", filename); + } + return cell; } @@ -1225,6 +1233,7 @@ static int get_angle_rad(char **bits, int nbits, double *pl) /** * load_cell_from_file: + * @filename: The filename from which to load the cell * * Loads a unit cell from a file of any type (PDB or CrystFEL format) * diff --git a/libcrystfel/src/crystal.c b/libcrystfel/src/crystal.c index c6e0a9dd..0c707331 100644 --- a/libcrystfel/src/crystal.c +++ b/libcrystfel/src/crystal.c @@ -3,11 +3,11 @@ * * A class representing a single crystal * - * Copyright © 2013 Deutsches Elektronen-Synchrotron DESY, - * a research centre of the Helmholtz Association. + * Copyright © 2013-2015 Deutsches Elektronen-Synchrotron DESY, + * a research centre of the Helmholtz Association. * * Authors: - * 2013 Thomas White <taw@physics.org> + * 2013-2015 Thomas White <taw@physics.org> * * This file is part of CrystFEL. * @@ -56,6 +56,7 @@ struct _crystal UnitCell *cell; double m; /* Mosaicity in radians */ double osf; + double Bfac; double profile_radius; int pr_dud; double resolution_limit; @@ -68,6 +69,9 @@ struct _crystal /* User flag, e.g. for "this is a bad crystal". */ int user_flag; + + /* Text notes, which go in the stream */ + char *notes; }; @@ -91,9 +95,11 @@ Crystal *crystal_new() cryst->cell = NULL; cryst->reflections = NULL; - cryst->resolution_limit = 0.0; + cryst->resolution_limit = INFINITY; cryst->n_saturated = 0; cryst->n_implausible = 0; + cryst->notes = NULL; + cryst->user_flag = 0; return cryst; } @@ -188,6 +194,12 @@ double crystal_get_osf(Crystal *cryst) } +double crystal_get_Bfac(Crystal *cryst) +{ + return cryst->Bfac; +} + + int crystal_get_user_flag(Crystal *cryst) { return cryst->user_flag; @@ -200,6 +212,12 @@ double crystal_get_mosaicity(Crystal *cryst) } +const char *crystal_get_notes(Crystal *cryst) +{ + return cryst->notes; +} + + /********************************** Setters ***********************************/ @@ -251,6 +269,12 @@ void crystal_set_osf(Crystal *cryst, double osf) } +void crystal_set_Bfac(Crystal *cryst, double Bfac) +{ + cryst->Bfac = Bfac; +} + + void crystal_set_user_flag(Crystal *cryst, int user_flag) { cryst->user_flag = user_flag; @@ -261,3 +285,35 @@ void crystal_set_mosaicity(Crystal *cryst, double m) { cryst->m = m; } + + +void crystal_set_notes(Crystal *cryst, const char *notes) +{ + free(cryst->notes); /* free(NULL) is OK */ + cryst->notes = strdup(notes); +} + + +void crystal_add_notes(Crystal *cryst, const char *notes_add) +{ + size_t len; + char *nnotes; + + if ( cryst->notes == NULL ) { + crystal_set_notes(cryst, notes_add); + return; + } + + len = strlen(notes_add) + strlen(cryst->notes) + 2; + nnotes = malloc(len); + if ( nnotes == NULL ) { + ERROR("Failed to add notes to crystal.\n"); + return; + } + + strcpy(nnotes, cryst->notes); + strcat(nnotes, "\n"); + strcat(nnotes, notes_add); + free(cryst->notes); + cryst->notes = nnotes; +} diff --git a/libcrystfel/src/crystal.h b/libcrystfel/src/crystal.h index 987cabbf..f0d732ff 100644 --- a/libcrystfel/src/crystal.h +++ b/libcrystfel/src/crystal.h @@ -3,11 +3,11 @@ * * A class representing a single crystal * - * Copyright © 2013 Deutsches Elektronen-Synchrotron DESY, - * a research centre of the Helmholtz Association. + * Copyright © 2013-2015 Deutsches Elektronen-Synchrotron DESY, + * a research centre of the Helmholtz Association. * * Authors: - * 2013 Thomas White <taw@physics.org> + * 2013-2015 Thomas White <taw@physics.org> * * This file is part of CrystFEL. * @@ -62,8 +62,10 @@ extern long long int crystal_get_num_saturated_reflections(Crystal *cryst); extern long long int crystal_get_num_implausible_reflections(Crystal *cryst); extern int crystal_get_user_flag(Crystal *cryst); extern double crystal_get_osf(Crystal *cryst); +extern double crystal_get_Bfac(Crystal *cryst); extern struct image *crystal_get_image(Crystal *cryst); extern double crystal_get_mosaicity(Crystal *cryst); +extern const char *crystal_get_notes(Crystal *cryst); extern void crystal_set_cell(Crystal *cryst, UnitCell *cell); extern void crystal_set_profile_radius(Crystal *cryst, double r); @@ -75,8 +77,12 @@ extern void crystal_set_num_implausible_reflections(Crystal *cryst, long long int n); extern void crystal_set_user_flag(Crystal *cryst, int flag); extern void crystal_set_osf(Crystal *cryst, double osf); +extern void crystal_set_Bfac(Crystal *cryst, double B); extern void crystal_set_image(Crystal *cryst, struct image *image); extern void crystal_set_mosaicity(Crystal *cryst, double m); +extern void crystal_set_notes(Crystal *cryst, const char *notes); + +extern void crystal_add_notes(Crystal *cryst, const char *notes_add); #ifdef __cplusplus } diff --git a/libcrystfel/src/detector.c b/libcrystfel/src/detector.c index a0de3be9..7979315b 100644 --- a/libcrystfel/src/detector.c +++ b/libcrystfel/src/detector.c @@ -3,12 +3,12 @@ * * Detector properties * - * Copyright © 2012-2014 Deutsches Elektronen-Synchrotron DESY, + * Copyright © 2012-2015 Deutsches Elektronen-Synchrotron DESY, * a research centre of the Helmholtz Association. * Copyright © 2012 Richard Kirian * * Authors: - * 2009-2014 Thomas White <taw@physics.org> + * 2009-2015 Thomas White <taw@physics.org> * 2014 Valerio Mariani * 2014 Kenneth Beyerlein <kenneth.beyerlein@desy.de> * 2011 Andrew Aquila @@ -339,16 +339,30 @@ int in_bad_region(struct detector *det, double fs, double ss) struct badregion *b = &det->bad[i]; + if ( (b->panel != NULL) + && (strcmp(b->panel, p->name) != 0) ) continue; + if ( b->is_fsss ) { - if ( fs < b->min_fs ) continue; - if ( fs > b->max_fs ) continue; - if ( ss < b->min_ss ) continue; - if ( ss > b->max_ss ) continue; + + int nfs, nss; + + /* fs/ss bad regions are specified according to the + * original coordinates */ + nfs = (fs-p->min_fs) + p->orig_min_fs; + nss = (ss-p->min_ss) + p->orig_min_ss; + + if ( nfs < b->min_fs ) continue; + if ( nfs > b->max_fs ) continue; + if ( nss < b->min_ss ) continue; + if ( nss > b->max_ss ) continue; + } else { + if ( rx < b->min_x ) continue; if ( rx > b->max_x ) continue; if ( ry < b->min_y ) continue; if ( ry > b->max_y ) continue; + } return 1; @@ -386,6 +400,18 @@ double get_tt(struct image *image, double fs, double ss, int *err) } +int detector_has_clen_references(struct detector *det) +{ + int i; + + for ( i=0; i<det->n_panels; i++ ) { + if ( det->panels[i].clen_from != NULL ) return 1; + } + + return 0; +} + + void record_image(struct image *image, int do_poisson, double background, gsl_rng *rng, double beam_radius, double nphotons) { @@ -558,13 +584,17 @@ void fill_in_values(struct detector *det, struct hdfile *f, struct event* ev) if ( p->clen_from != NULL ) { + double val; + int r; - if (det->path_dim !=0 || det->dim_dim !=0 ){ - p->clen = get_ev_based_value(f, p->clen_from, - ev) * 1.0e-3; + r = hdfile_get_value(f, p->clen_from, ev, &val, + H5T_NATIVE_DOUBLE); + if ( r ) { + ERROR("Failed to read '%s'\n", p->clen_from); } else { - p->clen = get_value(f, p->clen_from) * 1.0e-3; + p->clen = val * 1.0e-3; } + } p->clen += p->coffset; @@ -681,7 +711,8 @@ static struct badregion *new_bad_region(struct detector *det, const char *name) new->max_fs = 0; new->min_ss = 0; new->max_ss = 0; - new->is_fsss = 0; + new->is_fsss = 99; /* Slightly nasty: means "unassigned" */ + new->panel = NULL; strcpy(new->name, name); return new; @@ -952,43 +983,54 @@ static int parse_field_for_panel(struct panel *panel, const char *key, } -static int parse_field_bad(struct badregion *panel, const char *key, - const char *val) +static int check_badr_fsss(struct badregion *badr, int is_fsss) +{ + /* First assignment? */ + if ( badr->is_fsss == 99 ) { + badr->is_fsss = is_fsss; + return 0; + } + + if ( is_fsss != badr->is_fsss ) { + ERROR("You can't mix x/y and fs/ss in a bad region.\n"); + return 1; + } + + return 0; +} + + +static int parse_field_bad(struct badregion *badr, const char *key, + const char *val) { int reject = 0; if ( strcmp(key, "min_x") == 0 ) { - panel->min_x = atof(val); - if ( panel->is_fsss ) { - ERROR("You can't mix x/y and fs/ss in a bad region.\n"); - } + badr->min_x = atof(val); + reject = check_badr_fsss(badr, 0); } else if ( strcmp(key, "max_x") == 0 ) { - panel->max_x = atof(val); - if ( panel->is_fsss ) { - ERROR("You can't mix x/y and fs/ss in a bad region.\n"); - } + badr->max_x = atof(val); + reject = check_badr_fsss(badr, 0); } else if ( strcmp(key, "min_y") == 0 ) { - panel->min_y = atof(val); - if ( panel->is_fsss ) { - ERROR("You can't mix x/y and fs/ss in a bad region.\n"); - } + badr->min_y = atof(val); + reject = check_badr_fsss(badr, 0); } else if ( strcmp(key, "max_y") == 0 ) { - panel->max_y = atof(val); - if ( panel->is_fsss ) { - ERROR("You can't mix x/y and fs/ss in a bad region.\n"); - } + badr->max_y = atof(val); + reject = check_badr_fsss(badr, 0); } else if ( strcmp(key, "min_fs") == 0 ) { - panel->min_fs = atof(val); - panel->is_fsss = 1; + badr->min_fs = atof(val); + reject = check_badr_fsss(badr, 1); } else if ( strcmp(key, "max_fs") == 0 ) { - panel->max_fs = atof(val); - panel->is_fsss = 1; + badr->max_fs = atof(val); + reject = check_badr_fsss(badr, 1); } else if ( strcmp(key, "min_ss") == 0 ) { - panel->min_ss = atof(val); - panel->is_fsss = 1; + badr->min_ss = atof(val); + reject = check_badr_fsss(badr, 1); } else if ( strcmp(key, "max_ss") == 0 ) { - panel->max_ss = atof(val); - panel->is_fsss = 1; + badr->max_ss = atof(val); + reject = check_badr_fsss(badr, 1); + } else if ( strcmp(key, "panel") == 0 ) { + badr->panel = strdup(val); } else { ERROR("Unrecognised field '%s'\n", key); } @@ -1026,12 +1068,14 @@ static void parse_toplevel(struct detector *det, struct beam_params *beam, det->defaults.coffset = atof(val); } else if ( strcmp(key, "photon_energy") == 0 ) { - if ( strncmp(val, "/", 1) == 0 ) { - beam->photon_energy = 0.0; - beam->photon_energy_from = strdup(val); - } else { - beam->photon_energy = atof(val); - beam->photon_energy_from = NULL; + if ( beam != NULL ) { + if ( strncmp(val, "/", 1) == 0 ) { + beam->photon_energy = 0.0; + beam->photon_energy_from = strdup(val); + } else { + beam->photon_energy = atof(val); + beam->photon_energy_from = NULL; + } } } else if ( strcmp(key, "photon_energy_scale") == 0 ) { @@ -1191,7 +1235,7 @@ struct detector *get_detector_geometry(const char *filename, det->defaults.orig_max_ss = -1; det->defaults.cnx = NAN; det->defaults.cny = NAN; - det->defaults.clen = -1.0; + det->defaults.clen = NAN; det->defaults.coffset = 0.0; det->defaults.res = -1.0; det->defaults.badrow = '-'; @@ -1464,7 +1508,7 @@ struct detector *get_detector_geometry(const char *filename, " panel %s\n", det->panels[i].name); reject = 1; } - if ( (det->panels[i].clen < 0.0) + if ( isnan(det->panels[i].clen) && (det->panels[i].clen_from == NULL) ) { ERROR("Please specify the camera length for" " panel %s\n", det->panels[i].name); @@ -1511,24 +1555,8 @@ struct detector *get_detector_geometry(const char *filename, } for ( i=0; i<det->n_bad; i++ ) { - - if ( !det->bad[i].is_fsss && isnan(det->bad[i].min_x) ) { - ERROR("Please specify the minimum x coordinate for" - " bad region %s\n", det->bad[i].name); - reject = 1; - } - if ( !det->bad[i].is_fsss && isnan(det->bad[i].min_y) ) { - ERROR("Please specify the minimum y coordinate for" - " bad region %s\n", det->bad[i].name); - reject = 1; - } - if ( !det->bad[i].is_fsss && isnan(det->bad[i].max_x) ) { - ERROR("Please specify the maximum x coordinate for" - " bad region %s\n", det->bad[i].name); - reject = 1; - } - if ( !det->bad[i].is_fsss && isnan(det->bad[i].max_y) ) { - ERROR("Please specify the maximum y coordinate for" + if ( det->bad[i].is_fsss == 99 ) { + ERROR("Please specify the coordinate ranges for" " bad region %s\n", det->bad[i].name); reject = 1; } @@ -1569,6 +1597,7 @@ out: if ( p == NULL ) { ERROR("Cannot add panel to rigid group\n"); ERROR("Panel not found: %s\n", bits[pi]); + return NULL; } add_to_rigid_group(rigidgroup, p); free(bits[pi]); @@ -1596,7 +1625,8 @@ out: r = find_rigid_group_by_name(det, bits[rgi]); if ( r == NULL ) { ERROR("Cannot add rigid group to collection\n"); - ERROR("Rigid groups not found: %s\n", bits[rgi]); + ERROR("Rigid group not found: %s\n", bits[rgi]); + return NULL; } add_to_rigid_group_coll(rgcollection, r); free(bits[rgi]); @@ -1703,6 +1733,8 @@ struct detector *copy_geom(const struct detector *in) out->n_rigid_groups = 0; out->rigid_groups = NULL; + out->n_rg_collections = 0; + out->rigid_group_collections = NULL; for ( i=0; i<out->n_panels; i++ ) { @@ -1719,7 +1751,7 @@ struct detector *copy_geom(const struct detector *in) if ( p->data != NULL ) { /* Make a copy of the data fields unique to this * copy of the structure. */ - p->clen_from = strdup(p->clen_from); + p->data = strdup(p->data); } if ( p->dim_structure != NULL ) { @@ -1753,14 +1785,25 @@ struct detector *copy_geom(const struct detector *in) int rgi; for ( rgi=0; rgi<in->n_rigid_groups; rgi++ ) { + if ( panel_is_in_rigid_group(in->rigid_groups[rgi], - &in->panels[i])) { - add_to_rigid_group(find_or_add_rg(out, - in->rigid_groups[rgi]->name), - &out->panels[i]); + &in->panels[i]) ) + { + struct rigid_group *g; + g = find_or_add_rg(out, + in->rigid_groups[rgi]->name); + add_to_rigid_group(g, &out->panels[i]); } } + if ( &in->panels[i] == in->furthest_out_panel ) { + out->furthest_out_panel = &out->panels[i]; + } + if ( &in->panels[i] == in->furthest_in_panel ) { + out->furthest_in_panel = &out->panels[i]; + } + + } for ( i=0; i<in->n_rigid_groups; i++ ) { @@ -1768,13 +1811,17 @@ struct detector *copy_geom(const struct detector *in) int rgci; for ( rgci=0; rgci<in->n_rg_collections; rgci++ ) { + + const char *n = in->rigid_group_collections[rgci]->name; + if ( rigid_group_is_in_collection( in->rigid_group_collections[rgci], in->rigid_groups[i]) ) { - add_to_rigid_group_coll(find_or_add_rg_coll(out, - in->rigid_group_collections[rgci]->name), - out->rigid_groups[i]); + struct rg_collection *rgcoll; + rgcoll = find_or_add_rg_coll(out, n); + add_to_rigid_group_coll(rgcoll, + out->rigid_groups[i]); } } } @@ -1894,7 +1941,8 @@ static void process_panel_fields(const struct panel *p, char *line, if(strstr(bits[1], "fs") != NULL && strstr(bits[1], "min_fs") == NULL && - strstr(bits[1], "max_fs") == NULL) { + strstr(bits[1], "max_fs") == NULL && + strstr(bits[1], "offset") == NULL ) { sprintf(string_to_write, "%+fx %+fy", p->fsx, p->fsy); @@ -2030,7 +2078,6 @@ int write_detector_geometry_2(const char *geometry_filename, fh = fopen(output_filename, "w"); if ( fh == NULL ) { - fclose(fh); return 1; } diff --git a/libcrystfel/src/detector.h b/libcrystfel/src/detector.h index a82134bf..acb6609f 100644 --- a/libcrystfel/src/detector.h +++ b/libcrystfel/src/detector.h @@ -3,12 +3,12 @@ * * Detector properties * - * Copyright © 2012-2014 Deutsches Elektronen-Synchrotron DESY, + * Copyright © 2012-2015 Deutsches Elektronen-Synchrotron DESY, * a research centre of the Helmholtz Association. * Copyright © 2012 Richard Kirian * * Authors: - * 2009-2014 Thomas White <taw@physics.org> + * 2009-2015 Thomas White <taw@physics.org> * 2011-2012 Richard Kirian <rkirian@asu.edu> * 2014 Valerio Mariani * 2011 Andrew Aquila @@ -38,6 +38,7 @@ #define DETECTOR_H struct rigid_group; +struct rg_collection; struct detector; struct panel; struct badregion; @@ -131,6 +132,7 @@ struct badregion { char name[1024]; int is_fsss; + char *panel; double min_x; double max_x; @@ -253,6 +255,8 @@ extern int single_panel_data_source (struct detector *det, const char *element); struct rg_collection *find_rigid_group_collection_by_name(struct detector *det, const char *name); +extern int detector_has_clen_references(struct detector *det); + #ifdef __cplusplus } #endif diff --git a/libcrystfel/src/events.c b/libcrystfel/src/events.c index b008eee6..731f39f8 100644 --- a/libcrystfel/src/events.c +++ b/libcrystfel/src/events.c @@ -292,7 +292,7 @@ char *get_event_string(struct event *ev) char *new_ret_string; int ret_string_len; - if ( ev == NULL ) return "(none)"; + if ( ev == NULL ) return strdup("(none)"); if ( ev->path_length != 0 ) { @@ -356,7 +356,7 @@ char *get_event_string(struct event *ev) strncpy(&ret_string[ret_string_len],"/", 1); strncpy(&ret_string[ret_string_len+1], num_buf, - strlen(num_buf)); + strlen(num_buf)); ret_string_len += 1+strlen(num_buf); } @@ -412,16 +412,13 @@ struct event *get_event_from_event_string(const char *ev_string) if ( ev == NULL ) return NULL; if ( strlen(buf_path) !=0 ) { - + start = buf_path; do { - - start = buf_path; - char buf[2014]; sep = strstr(start, "/"); - if ( sep != NULL ) { + if ( sep != NULL ) { strncpy(buf, start, sep-start); buf[sep-start]='\0'; push_path_entry_to_event(ev, buf); @@ -462,6 +459,7 @@ struct event *get_event_from_event_string(const char *ev_string) push_dim_entry_to_event(ev, buf_int); } + } while (sep); } diff --git a/libcrystfel/src/events.h b/libcrystfel/src/events.h index 8cb00962..7f9c6731 100644 --- a/libcrystfel/src/events.h +++ b/libcrystfel/src/events.h @@ -67,7 +67,7 @@ struct dim_structure int num_dims; }; -extern struct event *initialize_event(); +extern struct event *initialize_event(void); extern int push_path_entry_to_event(struct event *ev, const char *entry); extern int pop_path_entry_from_event(struct event *ev); extern int push_dim_entry_to_event(struct event *ev, int entry); @@ -82,22 +82,22 @@ extern char *partial_event_substitution(struct event *ev, const char *data); extern char *retrieve_full_path(struct event *ev, const char *data); -extern struct filename_plus_event *initialize_filename_plus_event(); +extern struct filename_plus_event *initialize_filename_plus_event(void); extern void free_filename_plus_event(struct filename_plus_event *fpe); -extern struct event_list *initialize_event_list(); +extern struct event_list *initialize_event_list(void); extern int append_event_to_event_list(struct event_list *ev_list, struct event *ev); -int add_non_existing_event_to_event_list(struct event_list *ev_list, +extern int add_non_existing_event_to_event_list(struct event_list *ev_list, struct event *ev); extern struct event_list *copy_event_list(struct event_list *el); extern int find_event(struct event *ev, struct event_list *el); extern void free_event_list(struct event_list *el); -extern struct dim_structure *initialize_dim_structure(); -extern struct dim_structure *default_dim_structure(); +extern struct dim_structure *initialize_dim_structure(void); +extern struct dim_structure *default_dim_structure(void); extern int set_dim_structure_entry(struct dim_structure *hsd, const char *string_dim, const char *val_string); diff --git a/libcrystfel/src/geometry.c b/libcrystfel/src/geometry.c index b55b696d..acfc08ee 100644 --- a/libcrystfel/src/geometry.c +++ b/libcrystfel/src/geometry.c @@ -47,58 +47,65 @@ #include "geometry.h" -static signed int locate_peak(double x, double y, double z, double k, - struct detector *det, double *xdap, double *ydap) +static int locate_peak_on_panel(double x, double y, double z, double k, + struct panel *p, + double *pfs, double *pss) { - int i; - signed int found = -1; const double den = k + z; + double fs, ss, plx, ply, xd, yd; - *xdap = -1; *ydap = -1; + /* Coordinates of peak relative to central beam, in m */ + xd = p->clen * x / den; + yd = p->clen * y / den; - for ( i=0; i<det->n_panels; i++ ) { + /* Convert to pixels */ + xd *= p->res; + yd *= p->res; - double xd, yd; - double fs, ss, plx, ply; - struct panel *p; + /* Convert to relative to the panel corner */ + plx = xd - p->cnx; + ply = yd - p->cny; - p = &det->panels[i]; + fs = p->xfs*plx + p->yfs*ply; + ss = p->xss*plx + p->yss*ply; + + fs += p->min_fs; + ss += p->min_ss; + + *pfs = fs; *pss = ss; - /* Coordinates of peak relative to central beam, in m */ - xd = p->clen * x / den; - yd = p->clen * y / den; + /* Now, is this on this panel? */ + if ( fs < p->min_fs ) return 0; + if ( fs > p->max_fs ) return 0; + if ( ss < p->min_ss ) return 0; + if ( ss > p->max_ss ) return 0; - /* Convert to pixels */ - xd *= p->res; - yd *= p->res; + return 1; +} - /* Convert to relative to the panel corner */ - plx = xd - p->cnx; - ply = yd - p->cny; +static signed int locate_peak(double x, double y, double z, double k, + struct detector *det, double *pfs, double *pss) +{ + int i; + + *pfs = -1; *pss = -1; + + for ( i=0; i<det->n_panels; i++ ) { - fs = p->xfs*plx + p->yfs*ply; - ss = p->xss*plx + p->yss*ply; + struct panel *p; - fs += p->min_fs; - ss += p->min_ss; + p = &det->panels[i]; - /* Now, is this on this panel? */ - if ( fs < p->min_fs ) continue; - if ( fs > p->max_fs ) continue; - if ( ss < p->min_ss ) continue; - if ( ss > p->max_ss ) continue; + if ( locate_peak_on_panel(x, y, z, k, p, pfs, pss) ) { - /* If peak appears on multiple panels, reject it */ - if ( found != -1 ) return -1; + /* Woohoo! */ + return i; - /* Woohoo! */ - found = i; - *xdap = fs; - *ydap = ss; + } } - return found; + return -1; } @@ -182,7 +189,8 @@ static double partiality(PartialityModel pmodel, static Reflection *check_reflection(struct image *image, Crystal *cryst, PartialityModel pmodel, signed int h, signed int k, signed int l, - double xl, double yl, double zl) + double xl, double yl, double zl, + Reflection *updateme) { const int output = 0; double tl; @@ -195,7 +203,7 @@ static Reflection *check_reflection(struct image *image, Crystal *cryst, double del; /* Don't predict 000 */ - if ( abs(h)+abs(k)+abs(l) == 0 ) return NULL; + if ( (updateme == NULL) && (abs(h)+abs(k)+abs(l) == 0) ) return NULL; pr = crystal_get_profile_radius(cryst); del = image->div + crystal_get_mosaicity(cryst); @@ -207,7 +215,7 @@ static Reflection *check_reflection(struct image *image, Crystal *cryst, khigh = 1.0/(image->lambda + image->lambda*image->bw/2.0); /* If the point is looking "backscattery", reject it straight away */ - if ( zl < -khigh/2.0 ) return NULL; + if ( (updateme == NULL) && (zl < -khigh/2.0) ) return NULL; tl = sqrt(xl*xl + yl*yl); @@ -220,28 +228,47 @@ static Reflection *check_reflection(struct image *image, Crystal *cryst, rlow = klow - distance(cet, cez, tl, zl); /* Loss of precision */ /* Condition for reflection to be excited at all */ - if ( (signbit(rlow) == signbit(rhigh)) + if ( (updateme == NULL) + && (signbit(rlow) == signbit(rhigh)) && (fabs(rlow) > pr) && (fabs(rhigh) > pr) ) return NULL; /* Calculate partiality */ part = partiality(pmodel, rlow, rhigh, pr); - /* Add peak to list */ - refl = reflection_new(h, k, l); + if ( updateme == NULL ) { + refl = reflection_new(h, k, l); + } else { + refl = updateme; + } + + /* If we are updating a previous reflection, assume it stays + * on the same panel and calculate the new position even if it's + * fallen off the edge of the panel. */ + if ( (image->det != NULL) && (updateme != NULL) ) { + + double fs, ss; + locate_peak_on_panel(xl, yl, zl, 1.0/image->lambda, + get_panel(updateme), &fs, &ss); + set_detector_pos(refl, fs, ss); + + } + + /* Otherwise, calculate position if we have a detector structure, and + * if we don't then just make do with partiality calculation */ + if ( (image->det != NULL) && (updateme == NULL) ) { - /* If we have detector information, check the spot is measured. - * Otherwise, we make do with calculating the partialiaty etc. */ - if ( image->det != NULL ) { - double xda, yda; /* Position on detector */ - signed int p; /* Panel number */ - p = locate_peak(xl, yl, zl, 1.0/image->lambda, image->det, - &xda, &yda); + double fs, ss; /* Position on detector */ + signed int p; /* Panel number */ + p = locate_peak(xl, yl, zl, 1.0/image->lambda, + image->det, &fs, &ss); if ( p == -1 ) { reflection_free(refl); return NULL; } - set_detector_pos(refl, 0.0, xda, yda); + set_detector_pos(refl, fs, ss); + set_panel(refl, &image->det->panels[p]); + } if ( unlikely(rlow < rhigh) ) { @@ -249,8 +276,11 @@ static Reflection *check_reflection(struct image *image, Crystal *cryst, ERROR("%3i %3i %3i rlow = %e, rhigh = %e\n", h, k, l, rlow, rhigh); ERROR("div + m = %e, R = %e, bw = %e\n", del, pr, image->bw); - reflection_free(refl); - return NULL; + /* If we are updating, this is (kind of) OK */ + if ( updateme == NULL ) { + reflection_free(refl); + return NULL; + } } set_partial(refl, rlow, rhigh, part); @@ -266,6 +296,92 @@ static Reflection *check_reflection(struct image *image, Crystal *cryst, } +double r_gradient(UnitCell *cell, int k, Reflection *refl, struct image *image) +{ + double azi; + double asx, asy, asz; + double bsx, bsy, bsz; + double csx, csy, csz; + double xl, yl, zl; + signed int hs, ks, ls; + double rlow, rhigh, p; + double philow, phihigh, phi; + double khigh, klow; + double tl, cet, cez; + + get_partial(refl, &rlow, &rhigh, &p); + + get_symmetric_indices(refl, &hs, &ks, &ls); + + cell_get_reciprocal(cell, &asx, &asy, &asz, + &bsx, &bsy, &bsz, + &csx, &csy, &csz); + xl = hs*asx + ks*bsx + ls*csx; + yl = hs*asy + ks*bsy + ls*csy; + zl = hs*asz + ks*bsz + ls*csz; + + /* "low" gives the largest Ewald sphere (wavelength short => k large) + * "high" gives the smallest Ewald sphere (wavelength long => k small) + */ + klow = 1.0/(image->lambda - image->lambda*image->bw/2.0); + khigh = 1.0/(image->lambda + image->lambda*image->bw/2.0); + + tl = sqrt(xl*xl + yl*yl); + + cet = -sin(image->div/2.0) * klow; + cez = -cos(image->div/2.0) * klow; + philow = angle_between_2d(tl-cet, zl-cez, 0.0, 1.0); + + cet = -sin(image->div/2.0) * khigh; + cez = -cos(image->div/2.0) * khigh; + phihigh = angle_between_2d(tl-cet, zl-cez, 0.0, 1.0); + + /* Approximation: philow and phihigh are very similar */ + phi = (philow + phihigh) / 2.0; + + azi = atan2(yl, xl); + + switch ( k ) { + + case GPARAM_ASX : + return - hs * sin(phi) * cos(azi); + + case GPARAM_BSX : + return - ks * sin(phi) * cos(azi); + + case GPARAM_CSX : + return - ls * sin(phi) * cos(azi); + + case GPARAM_ASY : + return - hs * sin(phi) * sin(azi); + + case GPARAM_BSY : + return - ks * sin(phi) * sin(azi); + + case GPARAM_CSY : + return - ls * sin(phi) * sin(azi); + + case GPARAM_ASZ : + return - hs * cos(phi); + + case GPARAM_BSZ : + return - ks * cos(phi); + + case GPARAM_CSZ : + return - ls * cos(phi); + + case GPARAM_DETX : + case GPARAM_DETY : + case GPARAM_CLEN : + return 0.0; + + } + + ERROR("No r gradient defined for parameter %i\n", k); + abort(); +} + + RefList *find_intersections(struct image *image, Crystal *cryst, PartialityModel pmodel) { @@ -339,7 +455,7 @@ RefList *find_intersections_to_res(struct image *image, Crystal *cryst, zl = h*asz + k*bsz + l*csz; refl = check_reflection(image, cryst, pmodel, - h, k, l, xl, yl, zl); + h, k, l, xl, yl, zl, NULL); if ( refl != NULL ) { add_refl_to_list(refl, reflections); @@ -353,67 +469,6 @@ RefList *find_intersections_to_res(struct image *image, Crystal *cryst, } -/* Deprecated: select reflections using Kirian-style pixel proximity */ -RefList *select_intersections(struct image *image, Crystal *cryst) -{ - double ax, ay, az; - double bx, by, bz; - double cx, cy, cz; - const double min_dist = 0.25; - RefList *list; - int i; - - /* Round towards nearest */ - fesetround(1); - - /* Cell basis vectors for this image */ - cell_get_cartesian(crystal_get_cell(cryst), &ax, &ay, &az, - &bx, &by, &bz, &cx, &cy, &cz); - - list = reflist_new(); - if ( list == NULL ) return NULL; - - /* Loop over peaks, checking proximity to nearest reflection */ - for ( i=0; i<image_feature_count(image->features); i++ ) { - - struct imagefeature *f; - struct rvec q; - double h, k, l, hd, kd, ld; - double dsq; - - f = image_get_feature(image->features, i); - if ( f == NULL ) continue; - - /* Reciprocal space position of found peak */ - q = get_q(image, f->fs, f->ss, NULL, 1.0/image->lambda); - - /* Decimal and fractional Miller indices of nearest - * reciprocal lattice point */ - hd = q.u * ax + q.v * ay + q.w * az; - kd = q.u * bx + q.v * by + q.w * bz; - ld = q.u * cx + q.v * cy + q.w * cz; - h = lrint(hd); - k = lrint(kd); - l = lrint(ld); - - /* Check distance */ - dsq = pow(h-hd, 2.0) + pow(k-kd, 2.0) + pow(l-ld, 2.0); - - if ( sqrt(dsq) < min_dist ) { - - Reflection *refl; - - refl = add_refl(list, h, k, l); - set_detector_pos(refl, sqrt(dsq), f->fs, f->ss); - - } - - } - - return list; -} - - static void set_unity_partialities(Crystal *cryst) { Reflection *refl; @@ -430,8 +485,7 @@ static void set_unity_partialities(Crystal *cryst) /* Calculate partialities and apply them to the image's reflections */ -void update_partialities_2(Crystal *cryst, PartialityModel pmodel, - int *n_gained, int *n_lost, double *mean_p_change) +void update_partialities(Crystal *cryst, PartialityModel pmodel) { Reflection *refl; RefListIterator *iter; @@ -439,8 +493,6 @@ void update_partialities_2(Crystal *cryst, PartialityModel pmodel, double bsx, bsy, bsz; double csx, csy, csz; struct image *image = crystal_get_image(cryst); - double total_p_change = 0.0; - int n = 0; if ( pmodel == PMODEL_UNITY ) { set_unity_partialities(cryst); @@ -454,69 +506,20 @@ void update_partialities_2(Crystal *cryst, PartialityModel pmodel, refl != NULL; refl = next_refl(refl, iter) ) { - Reflection *vals; - double r1, r2, L, p, x, y; double xl, yl, zl; signed int h, k, l; - double old_p; get_symmetric_indices(refl, &h, &k, &l); - old_p = get_partiality(refl); /* Get the coordinates of the reciprocal lattice point */ xl = h*asx + k*bsx + l*csx; yl = h*asy + k*bsy + l*csy; zl = h*asz + k*bsz + l*csz; - vals = check_reflection(image, cryst, pmodel, - h, k, l, xl, yl, zl); - - if ( vals == NULL ) { - - if ( get_redundancy(refl) != 0 ) { - (*n_lost)++; - set_partiality(refl, 0.0); - set_redundancy(refl, 0); - } - - } else { - - if ( get_redundancy(refl) == 0 ) { - (*n_gained)++; - set_redundancy(refl, 1); - } - - /* Transfer partiality stuff */ - get_partial(vals, &r1, &r2, &p); - set_partial(refl, r1, r2, p); - L = get_lorentz(vals); - set_lorentz(refl, L); - - /* Transfer detector location */ - get_detector_pos(vals, &x, &y); - set_detector_pos(refl, 0.0, x, y); - - reflection_free(vals); - - total_p_change += fabs(p - old_p); - n++; - - } + check_reflection(image, cryst, pmodel, + h, k, l, xl, yl, zl, refl); } - - *mean_p_change = total_p_change / n; -} - - -/* Wrapper to maintain API compatibility */ -void update_partialities(Crystal *cryst, PartialityModel pmodel) -{ - int n_gained = 0; - int n_lost = 0; - double mean_p_change = 0.0; - update_partialities_2(cryst, pmodel, &n_gained, &n_lost, - &mean_p_change); } diff --git a/libcrystfel/src/geometry.h b/libcrystfel/src/geometry.h index f39b6f8b..152c0e47 100644 --- a/libcrystfel/src/geometry.h +++ b/libcrystfel/src/geometry.h @@ -3,12 +3,12 @@ * * Geometry of diffraction * - * Copyright © 2013-2014 Deutsches Elektronen-Synchrotron DESY, + * Copyright © 2013-2015 Deutsches Elektronen-Synchrotron DESY, * a research centre of the Helmholtz Association. * Copyright © 2012 Richard Kirian * * Authors: - * 2010-2014 Thomas White <taw@physics.org> + * 2010-2015 Thomas White <taw@physics.org> * 2012 Richard Kirian * * This file is part of CrystFEL. @@ -61,19 +61,37 @@ typedef enum { } PartialityModel; + +/* Enumeration of parameters which may want to be refined */ +enum gparam { + GPARAM_ASX, + GPARAM_ASY, + GPARAM_ASZ, + GPARAM_BSX, + GPARAM_BSY, + GPARAM_BSZ, + GPARAM_CSX, + GPARAM_CSY, + GPARAM_CSZ, + GPARAM_R, + GPARAM_DIV, + GPARAM_DETX, + GPARAM_DETY, + GPARAM_CLEN, + GPARAM_OSF, + GPARAM_BFAC +}; + + extern RefList *find_intersections(struct image *image, Crystal *cryst, PartialityModel pmodel); extern RefList *find_intersections_to_res(struct image *image, Crystal *cryst, PartialityModel pmodel, double max_res); -/* Deprecated: select reflections using Kirian-style pixel proximity */ -extern RefList *select_intersections(struct image *image, Crystal *cryst); - +extern double r_gradient(UnitCell *cell, int k, Reflection *refl, + struct image *image); extern void update_partialities(Crystal *cryst, PartialityModel pmodel); -extern void update_partialities_2(Crystal *cryst, PartialityModel pmodel, - int *n_gained, int *n_lost, - double *mean_p_change); extern void polarisation_correction(RefList *list, UnitCell *cell, struct image *image); diff --git a/libcrystfel/src/hdf5-file.c b/libcrystfel/src/hdf5-file.c index 85d4af72..3a08884a 100644 --- a/libcrystfel/src/hdf5-file.c +++ b/libcrystfel/src/hdf5-file.c @@ -378,169 +378,170 @@ static float *read_hdf5_data(struct hdfile *f, char *path, int line) } -int get_peaks(struct image *image, struct hdfile *f, const char *p, - int cxi_format, struct filename_plus_event *fpe) +/* Get peaks from HDF5, in "CXI format" (as in "CXIDB") */ +int get_peaks_cxi(struct image *image, struct hdfile *f, const char *p, + struct filename_plus_event *fpe) { - if ( cxi_format ) { - - char path_n[1024]; - char path_x[1024]; - char path_y[1024]; - char path_i[1024]; - int r; - int pk; + char path_n[1024]; + char path_x[1024]; + char path_y[1024]; + char path_i[1024]; + int r; + int pk; - int line = 0; - int num_peaks; + int line = 0; + int num_peaks; - float *buf_x; - float *buf_y; - float *buf_i; + float *buf_x; + float *buf_y; + float *buf_i; - if ( (fpe != NULL) && (fpe->ev != NULL) - && (fpe->ev->dim_entries != NULL) ) - { - line = fpe->ev->dim_entries[0]; - } else { - ERROR("CXI format peak list format selected," - "but file has no event structure"); - return 1; - } - - snprintf(path_n, 1024, "%s/nPeaks", p); - snprintf(path_x, 1024, "%s/peakXPosRaw", p); - snprintf(path_y, 1024, "%s/peakYPosRaw", p); - snprintf(path_i, 1024, "%s/peakTotalIntensity", p); + if ( (fpe != NULL) && (fpe->ev != NULL) + && (fpe->ev->dim_entries != NULL) ) + { + line = fpe->ev->dim_entries[0]; + } else { + ERROR("CXI format peak list format selected," + "but file has no event structure"); + return 1; + } - r = read_peak_count(f, path_n, line, &num_peaks); - if ( r != 0 ) return 1; + snprintf(path_n, 1024, "%s/nPeaks", p); + snprintf(path_x, 1024, "%s/peakXPosRaw", p); + snprintf(path_y, 1024, "%s/peakYPosRaw", p); + snprintf(path_i, 1024, "%s/peakTotalIntensity", p); - buf_x = read_hdf5_data(f, path_x, line); - if ( r != 0 ) return 1; + r = read_peak_count(f, path_n, line, &num_peaks); + if ( r != 0 ) return 1; - buf_y = read_hdf5_data(f, path_y, line); - if ( r != 0 ) return 1; + buf_x = read_hdf5_data(f, path_x, line); + if ( r != 0 ) return 1; - buf_i = read_hdf5_data(f, path_i, line); - if ( r != 0 ) return 1; + buf_y = read_hdf5_data(f, path_y, line); + if ( r != 0 ) return 1; - if ( image->features != NULL ) { - image_feature_list_free(image->features); - } - image->features = image_feature_list_new(); + buf_i = read_hdf5_data(f, path_i, line); + if ( r != 0 ) return 1; - for ( pk=0; pk<num_peaks; pk++ ) { + if ( image->features != NULL ) { + image_feature_list_free(image->features); + } + image->features = image_feature_list_new(); - float fs, ss, val; - struct panel *p; + for ( pk=0; pk<num_peaks; pk++ ) { - fs = buf_x[pk]; - ss = buf_y[pk]; - val = buf_i[pk]; + float fs, ss, val; + struct panel *p; - p = find_orig_panel(image->det, fs, ss); - if ( p == NULL ) continue; - if ( p->no_index ) continue; + fs = buf_x[pk]; + ss = buf_y[pk]; + val = buf_i[pk]; - /* Convert coordinates to match rearranged - * panels in memory */ - fs = fs - p->orig_min_fs + p->min_fs; - ss = ss - p->orig_min_ss + p->min_ss; + p = find_orig_panel(image->det, fs, ss); + if ( p == NULL ) continue; + if ( p->no_index ) continue; - image_add_feature(image->features, fs, ss, image, - val, NULL); + /* Convert coordinates to match rearranged + * panels in memory */ + fs = fs - p->orig_min_fs + p->min_fs; + ss = ss - p->orig_min_ss + p->min_ss; - } + image_add_feature(image->features, fs, ss, image, + val, NULL); - } else { + } - hid_t dh, sh; - hsize_t size[2]; - hsize_t max_size[2]; - int i; - float *buf; - herr_t r; - int tw; + return 0; +} - dh = H5Dopen2(f->fh, p, H5P_DEFAULT); - if ( dh < 0 ) { - ERROR("Peak list (%s) not found.\n", p); - return 1; - } - sh = H5Dget_space(dh); - if ( sh < 0 ) { - H5Dclose(dh); - ERROR("Couldn't get dataspace for peak list.\n"); - return 1; - } +int get_peaks(struct image *image, struct hdfile *f, const char *p) +{ + hid_t dh, sh; + hsize_t size[2]; + hsize_t max_size[2]; + int i; + float *buf; + herr_t r; + int tw; - if ( H5Sget_simple_extent_ndims(sh) != 2 ) { - ERROR("Peak list has the wrong dimensionality (%i).\n", - H5Sget_simple_extent_ndims(sh)); - H5Sclose(sh); - H5Dclose(dh); - return 1; - } + dh = H5Dopen2(f->fh, p, H5P_DEFAULT); + if ( dh < 0 ) { + ERROR("Peak list (%s) not found.\n", p); + return 1; + } - H5Sget_simple_extent_dims(sh, size, max_size); + sh = H5Dget_space(dh); + if ( sh < 0 ) { + H5Dclose(dh); + ERROR("Couldn't get dataspace for peak list.\n"); + return 1; + } - tw = size[1]; - if ( (tw != 3) && (tw != 4) ) { - H5Sclose(sh); - H5Dclose(dh); - ERROR("Peak list has the wrong dimensions.\n"); - return 1; - } + if ( H5Sget_simple_extent_ndims(sh) != 2 ) { + ERROR("Peak list has the wrong dimensionality (%i).\n", + H5Sget_simple_extent_ndims(sh)); + H5Sclose(sh); + H5Dclose(dh); + return 1; + } - buf = malloc(sizeof(float)*size[0]*size[1]); - if ( buf == NULL ) { - H5Sclose(sh); - H5Dclose(dh); - ERROR("Couldn't reserve memory for the peak list.\n"); - return 1; - } - r = H5Dread(dh, H5T_NATIVE_FLOAT, H5S_ALL, H5S_ALL, - H5P_DEFAULT, buf); - if ( r < 0 ) { - ERROR("Couldn't read peak list.\n"); - free(buf); - return 1; - } + H5Sget_simple_extent_dims(sh, size, max_size); - if ( image->features != NULL ) { - image_feature_list_free(image->features); - } - image->features = image_feature_list_new(); + tw = size[1]; + if ( (tw != 3) && (tw != 4) ) { + H5Sclose(sh); + H5Dclose(dh); + ERROR("Peak list has the wrong dimensions.\n"); + return 1; + } - for ( i=0; i<size[0]; i++ ) { + buf = malloc(sizeof(float)*size[0]*size[1]); + if ( buf == NULL ) { + H5Sclose(sh); + H5Dclose(dh); + ERROR("Couldn't reserve memory for the peak list.\n"); + return 1; + } + r = H5Dread(dh, H5T_NATIVE_FLOAT, H5S_ALL, H5S_ALL, + H5P_DEFAULT, buf); + if ( r < 0 ) { + ERROR("Couldn't read peak list.\n"); + free(buf); + return 1; + } - float fs, ss, val; - struct panel *p; + if ( image->features != NULL ) { + image_feature_list_free(image->features); + } + image->features = image_feature_list_new(); - fs = buf[tw*i+0]; - ss = buf[tw*i+1]; - val = buf[tw*i+2]; + for ( i=0; i<size[0]; i++ ) { - p = find_orig_panel(image->det, fs, ss); - if ( p == NULL ) continue; - if ( p->no_index ) continue; + float fs, ss, val; + struct panel *p; - /* Convert coordinates to match rearranged panels in memory */ - fs = fs - p->orig_min_fs + p->min_fs; - ss = ss - p->orig_min_ss + p->min_ss; + fs = buf[tw*i+0]; + ss = buf[tw*i+1]; + val = buf[tw*i+2]; - image_add_feature(image->features, fs, ss, image, val, - NULL); + p = find_orig_panel(image->det, fs, ss); + if ( p == NULL ) continue; + if ( p->no_index ) continue; - } + /* Convert coordinates to match rearranged panels in memory */ + fs = fs - p->orig_min_fs + p->min_fs; + ss = ss - p->orig_min_ss + p->min_ss; - free(buf); - H5Sclose(sh); - H5Dclose(dh); + image_add_feature(image->features, fs, ss, image, val, + NULL); } + free(buf); + H5Sclose(sh); + H5Dclose(dh); + return 0; } @@ -1150,6 +1151,201 @@ static int unpack_panels(struct image *image, struct detector *det) } +static int get_scalar_value(struct hdfile *f, const char *name, void *val, + hid_t memtype) +{ + hid_t dh; + hid_t type; + hid_t class; + herr_t r; + int check; + + if ( !hdfile_is_scalar(f, name, 1) ) return 1; + + check = check_path_existence(f->fh, name); + if ( check == 0 ) { + ERROR("No such float field '%s'\n", name); + return 1; + } + + dh = H5Dopen2(f->fh, name, H5P_DEFAULT); + + type = H5Dget_type(dh); + class = H5Tget_class(type); + + if ( class != H5T_FLOAT ) { + ERROR("Not a floating point value.\n"); + H5Tclose(type); + H5Dclose(dh); + return 1; + } + + r = H5Dread(dh, H5T_NATIVE_DOUBLE, H5S_ALL, H5S_ALL, + H5P_DEFAULT, val); + if ( r < 0 ) { + ERROR("Couldn't read value.\n"); + H5Tclose(type); + H5Dclose(dh); + return 1; + } + + return 0; +} + + +static int get_ev_based_value(struct hdfile *f, const char *name, + struct event *ev, void *val, hid_t memtype) +{ + hid_t dh; + hid_t type; + hid_t class; + hid_t sh; + hid_t ms; + hsize_t *f_offset = NULL; + hsize_t *f_count = NULL; + hsize_t m_offset[1]; + hsize_t m_count[1]; + hsize_t msdims[1]; + hsize_t size[3]; + herr_t r; + herr_t check; + int check_pe; + int dim_flag; + int ndims; + int i; + char *subst_name = NULL; + + if ( ev->path_length != 0 ) { + subst_name = partial_event_substitution(ev, name); + } else { + subst_name = strdup(name); + } + + check_pe = check_path_existence(f->fh, subst_name); + if ( check_pe == 0 ) { + ERROR("No such event-based float field '%s'\n", subst_name); + return 1; + } + + dh = H5Dopen2(f->fh, subst_name, H5P_DEFAULT); + type = H5Dget_type(dh); + class = H5Tget_class(type); + + if ( class != H5T_FLOAT ) { + ERROR("Not a floating point value.\n"); + H5Tclose(type); + H5Dclose(dh); + return 1; + } + + /* Get the dimensionality. We have to cope with scalars expressed as + * arrays with all dimensions 1, as well as zero-d arrays. */ + sh = H5Dget_space(dh); + ndims = H5Sget_simple_extent_ndims(sh); + if ( ndims > 3 ) { + H5Tclose(type); + H5Dclose(dh); + return 1; + } + H5Sget_simple_extent_dims(sh, size, NULL); + + m_offset[0] = 0; + m_count[0] = 1; + msdims[0] = 1; + ms = H5Screate_simple(1,msdims,NULL); + + /* Check that the size in all dimensions is 1 */ + /* or that one of the dimensions has the same */ + /* size as the hyperplane events */ + + dim_flag = 0; + + for ( i=0; i<ndims; i++ ) { + if ( size[i] != 1 ) { + if ( i == 0 && size[i] > ev->dim_entries[0] ) { + dim_flag = 1; + } else { + H5Tclose(type); + H5Dclose(dh); + return 1; + } + } + } + + if ( dim_flag == 0 ) { + + r = H5Dread(dh, memtype, H5S_ALL, H5S_ALL, H5P_DEFAULT, val); + + if ( r < 0 ) { + ERROR("Couldn't read value.\n"); + H5Tclose(type); + H5Dclose(dh); + return 1; + } + + } else { + + f_offset = malloc(ndims*sizeof(hsize_t)); + f_count = malloc(ndims*sizeof(hsize_t)); + + for ( i=0; i<ndims; i++ ) { + + if ( i == 0 ) { + f_offset[i] = ev->dim_entries[0]; + f_count[i] = 1; + } else { + f_offset[i] = 0; + f_count[i] = 0; + } + + } + + check = H5Sselect_hyperslab(sh, H5S_SELECT_SET, + f_offset, NULL, f_count, NULL); + if ( check <0 ) { + ERROR("Error selecting dataspace for float value"); + free(f_offset); + free(f_count); + return 1; + } + + ms = H5Screate_simple(1,msdims,NULL); + check = H5Sselect_hyperslab(ms, H5S_SELECT_SET, + m_offset, NULL, m_count, NULL); + if ( check < 0 ) { + ERROR("Error selecting memory dataspace for float value"); + free(f_offset); + free(f_count); + return 1; + } + + r = H5Dread(dh, memtype, ms, sh, H5P_DEFAULT, val); + if ( r < 0 ) { + ERROR("Couldn't read value.\n"); + H5Tclose(type); + H5Dclose(dh); + return 1; + } + + } + + free(subst_name); + + return 0; +} + + +int hdfile_get_value(struct hdfile *f, const char *name, + struct event *ev, void *val, hid_t memtype) +{ + if ( ev == NULL ) { + return get_scalar_value(f, name, val, memtype); + } else { + return get_ev_based_value(f, name, ev, val, memtype); + } +} + + void fill_in_beam_parameters(struct beam_params *beam, struct hdfile *f, struct event *ev, struct image *image) { @@ -1160,15 +1356,16 @@ void fill_in_beam_parameters(struct beam_params *beam, struct hdfile *f, /* Explicit value given */ eV = beam->photon_energy; - } else if ( ev != NULL ) { - - /* Value from HDF5 file, event-based structure */ - eV = get_ev_based_value(f, beam->photon_energy_from, ev); - } else { - /* Value from HDF5 file, single-event structure */ - eV = get_value(f, beam->photon_energy_from); + int r; + + r = hdfile_get_value(f, beam->photon_energy_from, ev, &eV, + H5T_NATIVE_DOUBLE); + if ( r ) { + ERROR("Failed to read '%s'\n", + beam->photon_energy_from); + } } @@ -1208,8 +1405,15 @@ int hdf5_read(struct hdfile *f, struct image *image, const char *element, } image->data = buf; + if ( image->det != NULL ) { + ERROR("WARNING: hdf5_read() called with geometry structure.\n"); + } + image->det = simple_geometry(image); + if ( satcorr ) debodge_saturation(f, image); + unpack_panels(image, image->det); + if ( image->beam != NULL ) { fill_in_beam_parameters(image->beam, f, NULL, image); @@ -1588,255 +1792,6 @@ int hdfile_is_scalar(struct hdfile *f, const char *name, int verbose) } - - -static int get_f_value(struct hdfile *f, const char *name, double *val) -{ - hid_t dh; - hid_t type; - hid_t class; - herr_t r; - double buf; - int check; - - if ( !hdfile_is_scalar(f, name, 1) ) return 1; - - check = check_path_existence(f->fh, name); - if ( check == 0 ) { - ERROR("No such float field '%s'\n", name); - return 1; - } - - dh = H5Dopen2(f->fh, name, H5P_DEFAULT); - - type = H5Dget_type(dh); - class = H5Tget_class(type); - - if ( class != H5T_FLOAT ) { - ERROR("Not a floating point value.\n"); - H5Tclose(type); - H5Dclose(dh); - return 1; - } - - r = H5Dread(dh, H5T_NATIVE_DOUBLE, H5S_ALL, H5S_ALL, - H5P_DEFAULT, &buf); - if ( r < 0 ) { - ERROR("Couldn't read value.\n"); - H5Tclose(type); - H5Dclose(dh); - return 1; - } - - *val = buf; - return 0; -} - - -static int get_ev_based_f_value(struct hdfile *f, const char *name, - struct event *ev, double *val) -{ - hid_t dh; - hid_t type; - hid_t class; - hid_t sh; - hid_t ms; - hsize_t *f_offset = NULL; - hsize_t *f_count = NULL; - hsize_t m_offset[1]; - hsize_t m_count[1]; - hsize_t msdims[1]; - hsize_t size[3]; - herr_t r; - herr_t check; - double buf; - int check_pe; - int dim_flag; - int ndims; - int i; - char *subst_name = NULL; - - if ( ev->path_length != 0 ) { - subst_name = partial_event_substitution(ev, name); - } else { - subst_name = strdup(name); - } - - check_pe = check_path_existence(f->fh, subst_name); - if ( check_pe == 0 ) { - ERROR("No such event-based float field '%s'\n", subst_name); - return 1; - } - - dh = H5Dopen2(f->fh, name, H5P_DEFAULT); - type = H5Dget_type(dh); - class = H5Tget_class(type); - - if ( class != H5T_FLOAT ) { - ERROR("Not a floating point value.\n"); - H5Tclose(type); - H5Dclose(dh); - return 1; - } - - /* Get the dimensionality. We have to cope with scalars expressed as - * arrays with all dimensions 1, as well as zero-d arrays. */ - sh = H5Dget_space(dh); - ndims = H5Sget_simple_extent_ndims(sh); - if ( ndims > 3 ) { - H5Tclose(type); - H5Dclose(dh); - return 1; - } - H5Sget_simple_extent_dims(sh, size, NULL); - - m_offset[0] = 0; - m_count[0] = 1; - msdims[0] = 1; - ms = H5Screate_simple(1,msdims,NULL); - - /* Check that the size in all dimensions is 1 */ - /* or that one of the dimensions has the same */ - /* size as the hyperplane events */ - - dim_flag = 0; - - for ( i=0; i<ndims; i++ ) { - if ( size[i] != 1 ) { - if ( i == 0 && size[i] > ev->dim_entries[0] ) { - dim_flag = 1; - } else { - H5Tclose(type); - H5Dclose(dh); - return 1; - } - } - } - - if ( dim_flag == 0 ) { - - r = H5Dread(dh, H5T_NATIVE_DOUBLE, H5S_ALL, H5S_ALL, - H5P_DEFAULT, &buf); - - if ( r < 0 ) { - ERROR("Couldn't read value.\n"); - H5Tclose(type); - H5Dclose(dh); - return 1; - } - - } else { - - f_offset = malloc(ndims*sizeof(hsize_t)); - f_count = malloc(ndims*sizeof(hsize_t)); - - for ( i=0; i<ndims; i++ ) { - - if ( i == 0 ) { - f_offset[i] = ev->dim_entries[0]; - f_count[i] = 1; - } else { - f_offset[i] = 0; - f_count[i] = 0; - } - - } - - check = H5Sselect_hyperslab(sh, H5S_SELECT_SET, - f_offset, NULL, f_count, NULL); - if ( check <0 ) { - ERROR("Error selecting dataspace for float value"); - free(f_offset); - free(f_count); - return 1; - } - - ms = H5Screate_simple(1,msdims,NULL); - check = H5Sselect_hyperslab(ms, H5S_SELECT_SET, - m_offset, NULL, m_count, NULL); - if ( check < 0 ) { - ERROR("Error selecting memory dataspace for float value"); - free(f_offset); - free(f_count); - return 1; - } - - r = H5Dread(dh, H5T_NATIVE_DOUBLE, ms, sh, - H5P_DEFAULT, &buf); - if ( r < 0 ) { - ERROR("Couldn't read value.\n"); - H5Tclose(type); - H5Dclose(dh); - return 1; - } - - } - - free(subst_name); - *val = buf; - - return 0; -} - - -static int get_i_value(struct hdfile *f, const char *name, int *val) -{ - hid_t dh; - hid_t type; - hid_t class; - herr_t r; - int buf; - int check; - - if ( !hdfile_is_scalar(f, name, 1) ) return 1; - - check = check_path_existence(f->fh, name); - if ( check == 0 ) { - ERROR("No such integer field '%s'\n", name); - return 1; - } - - dh = H5Dopen2(f->fh, name, H5P_DEFAULT); - type = H5Dget_type(dh); - class = H5Tget_class(type); - - if ( class != H5T_INTEGER ) { - ERROR("Not an integer value.\n"); - H5Tclose(type); - H5Dclose(dh); - return 1; - } - - r = H5Dread(dh, H5T_NATIVE_INT, H5S_ALL, H5S_ALL, - H5P_DEFAULT, &buf); - if ( r < 0 ) { - ERROR("Couldn't read value.\n"); - H5Tclose(type); - H5Dclose(dh); - return 1; - } - - *val = buf; - return 0; -} - - -double get_value(struct hdfile *f, const char *name) -{ - double val = 0.0; - get_f_value(f, name, &val); - return val; -} - -double get_ev_based_value(struct hdfile *f, const char *name, - struct event *ev) -{ - double val = -1; - get_ev_based_f_value(f, name, ev, &val); - return val; -} - - struct copy_hdf5_field { char **fields; @@ -1947,9 +1902,15 @@ char *hdfile_get_string_value(struct hdfile *f, const char *name, hid_t class; int buf_i; double buf_f; - char *tmp; + char *tmp = NULL, *subst_name = NULL; - dh = H5Dopen2(f->fh, name, H5P_DEFAULT); + if (ev != NULL && ev->path_length != 0 ) { + subst_name = partial_event_substitution(ev, name); + } else { + subst_name = strdup(name); + } + + dh = H5Dopen2(f->fh, subst_name, H5P_DEFAULT); if ( dh < 0 ) return NULL; type = H5Dget_type(dh); class = H5Tget_class(type); @@ -1957,7 +1918,6 @@ char *hdfile_get_string_value(struct hdfile *f, const char *name, if ( class == H5T_STRING ) { herr_t r; - char *tmp; hid_t sh; size = H5Tget_size(type); @@ -1966,51 +1926,54 @@ char *hdfile_get_string_value(struct hdfile *f, const char *name, sh = H5Screate(H5S_SCALAR); r = H5Dread(dh, type, sh, sh, H5P_DEFAULT, tmp); - if ( r < 0 ) goto fail; - - /* Two possibilities: - * String is already zero-terminated - * String is not terminated. - * Make sure things are done properly... */ - tmp[size] = '\0'; - chomp(tmp); + if ( r < 0 ) { + free(tmp); + tmp = NULL; + } else { - return tmp; + /* Two possibilities: + * String is already zero-terminated + * String is not terminated. + * Make sure things are done properly... */ + tmp[size] = '\0'; + chomp(tmp); + } + } else { - } + int r; - switch ( class ) { + switch ( class ) { - case H5T_FLOAT : - if ( ev != NULL ) { - if ( get_ev_based_f_value(f, name, ev, &buf_f) ) goto fail; - } else { - if ( get_f_value(f, name, &buf_f) ) goto fail; + case H5T_FLOAT : + r = hdfile_get_value(f, subst_name, ev, &buf_f, + H5T_NATIVE_DOUBLE); + if ( r == 0 ) { + tmp = malloc(256); + snprintf(tmp, 255, "%f", buf_f); + } + break; + + case H5T_INTEGER : + r = hdfile_get_value(f, subst_name, ev, &buf_i, + H5T_NATIVE_INT); + if ( r == 0 ) { + tmp = malloc(256); + snprintf(tmp, 255, "%d", buf_i); + } + break; } - tmp = malloc(256); - snprintf(tmp, 255, "%f", buf_f); - return tmp; - - case H5T_INTEGER : - if ( get_i_value(f, name, &buf_i) ) goto fail; - tmp = malloc(256); - snprintf(tmp, 255, "%d", buf_i); - return tmp; - - default : - goto fail; } -fail: H5Tclose(type); H5Dclose(dh); - return NULL; + free(subst_name); + return tmp; } char **hdfile_read_group(struct hdfile *f, int *n, const char *parent, - int **p_is_group, int **p_is_image) + int **p_is_group, int **p_is_image) { hid_t gh; hsize_t num; diff --git a/libcrystfel/src/hdf5-file.h b/libcrystfel/src/hdf5-file.h index 4d7b223c..8c89eb93 100644 --- a/libcrystfel/src/hdf5-file.h +++ b/libcrystfel/src/hdf5-file.h @@ -3,11 +3,11 @@ * * Read/write HDF5 data files * - * Copyright © 2012-2014 Deutsches Elektronen-Synchrotron DESY, + * Copyright © 2012-2015 Deutsches Elektronen-Synchrotron DESY, * a research centre of the Helmholtz Association. * * Authors: - * 2009-2012 Thomas White <taw@physics.org> + * 2009-2015 Thomas White <taw@physics.org> * 2014 Valerio Mariani * @@ -76,15 +76,10 @@ extern char **hdfile_read_group(struct hdfile *f, int *n, const char *parent, extern int hdfile_set_first_image(struct hdfile *f, const char *group); extern void hdfile_close(struct hdfile *f); -extern int hdfile_is_scalar(struct hdfile *f, const char *name, int verbose); -char *hdfile_get_string_value(struct hdfile *f, const char *name, - struct event* ev); -extern int get_peaks(struct image *image, struct hdfile *f, const char *p, - int cxi_format, struct filename_plus_event *fpe); -extern double get_value(struct hdfile *f, const char *name); +extern int get_peaks(struct image *image, struct hdfile *f, const char *p); -extern double get_ev_based_value(struct hdfile *f, const char *name, - struct event *ev); +extern int get_peaks_cxi(struct image *image, struct hdfile *f, const char *p, + struct filename_plus_event *fpe); extern struct copy_hdf5_field *new_copy_hdf5_field_list(void); extern void free_copy_hdf5_field_list(struct copy_hdf5_field *f); @@ -97,6 +92,12 @@ extern void add_copy_hdf5_field(struct copy_hdf5_field *copyme, extern struct event_list *fill_event_list(struct hdfile* hdfile, struct detector* det); +extern int hdfile_get_value(struct hdfile *f, const char *name, + struct event *ev, void *val, hid_t memtype); +extern int hdfile_is_scalar(struct hdfile *f, const char *name, int verbose); +extern char *hdfile_get_string_value(struct hdfile *f, const char *name, + struct event *ev); + #ifdef __cplusplus } #endif diff --git a/libcrystfel/src/index.c b/libcrystfel/src/index.c index 97187655..6b36af3e 100644 --- a/libcrystfel/src/index.c +++ b/libcrystfel/src/index.c @@ -56,6 +56,19 @@ #include "grainspotter.h" +static int debug_index(struct image *image) +{ + Crystal *cr = crystal_new(); + UnitCell *cell = cell_new(); + cell_set_reciprocal(cell, +0.0000e9, +0.0000e9, +0.0000e9, + +0.0000e9, +0.0000e9, +0.0000e9, + +0.0000e9, +0.0000e9, +0.0000e9); + crystal_set_cell(cr, cell); + image_add_crystal(image, cr); + return 1; +} + + IndexingPrivate **prepare_indexing(IndexingMethod *indm, UnitCell *cell, struct detector *det, float *ltl) { @@ -101,6 +114,10 @@ IndexingPrivate **prepare_indexing(IndexingMethod *indm, UnitCell *cell, det, ltl); break; + case INDEXING_DEBUG : + iprivs[n] = (IndexingPrivate *)strdup("Hello!"); + break; + default : ERROR("Don't know how to prepare indexing method %i\n", indm[n]); @@ -175,6 +192,10 @@ void cleanup_indexing(IndexingMethod *indms, IndexingPrivate **privs) grainspotter_cleanup(privs[n]); break; + case INDEXING_DEBUG : + free(privs[n]); + break; + default : ERROR("Don't know how to clean up indexing method %i\n", indms[n]); @@ -244,6 +265,9 @@ static int try_indexer(struct image *image, IndexingMethod indm, return grainspotter_index(image, ipriv); break; + case INDEXING_DEBUG : + return debug_index(image); + default : ERROR("Unrecognised indexing method: %i\n", indm); break; @@ -381,8 +405,12 @@ char *indexer_str(IndexingMethod indm) strcpy(str, "simulation"); break; + case INDEXING_DEBUG : + strcpy(str, "debug"); + break; + default : - ERROR("Unrecognised indexing method %i\n", + ERROR("No test description for indexing method %i\n", indm & INDEXING_METHOD_MASK); strcpy(str, "(unknown)"); break; @@ -457,6 +485,10 @@ IndexingMethod *build_indexer_list(const char *str) list[++nmeth] = INDEXING_SIMULATION; return list; + } else if ( strcmp(methods[i], "debug") == 0) { + list[++nmeth] = INDEXING_DEBUG; + return list; + } else if ( strcmp(methods[i], "raw") == 0) { list[nmeth] = set_raw(list[nmeth]); diff --git a/libcrystfel/src/index.h b/libcrystfel/src/index.h index 709b7507..ff1cd5df 100644 --- a/libcrystfel/src/index.h +++ b/libcrystfel/src/index.h @@ -3,13 +3,13 @@ * * Perform indexing (somehow) * - * Copyright © 2012-2014 Deutsches Elektronen-Synchrotron DESY, + * Copyright © 2012-2015 Deutsches Elektronen-Synchrotron DESY, * a research centre of the Helmholtz Association. * Copyright © 2012 Richard Kirian * Copyright © 2012 Lorenzo Galli * * Authors: - * 2010-2014 Thomas White <taw@physics.org> + * 2010-2015 Thomas White <taw@physics.org> * 2010 Richard Kirian * 2012 Lorenzo Galli * @@ -72,6 +72,7 @@ * @INDEXING_GRAINSPOTTER: Invoke GrainSpotter * @INDEXING_XDS: Invoke XDS * @INDEXING_SIMULATION: Dummy value + * @INDEXING_DEBUG: Results injector for debugging * @INDEXING_CHECK_CELL_COMBINATIONS: Check linear combinations of unit cell * axes for agreement with given cell. * @INDEXING_CHECK_CELL_AXES: Check unit cell axes for agreement with given @@ -98,7 +99,8 @@ typedef enum { INDEXING_GRAINSPOTTER = 4, INDEXING_XDS = 5, INDEXING_SIMULATION = 6, - INDEXING_ASDF = 7, + INDEXING_DEBUG = 7, + INDEXING_ASDF = 8, /* Bits at the top of the IndexingMethod are flags which modify the * behaviour of the indexer. */ diff --git a/libcrystfel/src/integration.c b/libcrystfel/src/integration.c index 4a743c63..16c27bbe 100644 --- a/libcrystfel/src/integration.c +++ b/libcrystfel/src/integration.c @@ -54,94 +54,6 @@ #include "integration.h" -static void check_eigen(gsl_vector *e_val) -{ - int i; - double vmax, vmin; - const int n = e_val->size; - const double max_condition = 1e6; - const int verbose = 0; - - if ( verbose ) STATUS("Eigenvalues:\n"); - vmin = +INFINITY; - vmax = 0.0; - for ( i=0; i<n; i++ ) { - double val = gsl_vector_get(e_val, i); - if ( verbose ) STATUS("%i: %e\n", i, val); - if ( val > vmax ) vmax = val; - if ( val < vmin ) vmin = val; - } - - for ( i=0; i<n; i++ ) { - double val = gsl_vector_get(e_val, i); - if ( val < vmax/max_condition ) { - gsl_vector_set(e_val, i, 0.0); - } - } - - vmin = +INFINITY; - vmax = 0.0; - for ( i=0; i<n; i++ ) { - double val = gsl_vector_get(e_val, i); - if ( val == 0.0 ) continue; - if ( val > vmax ) vmax = val; - if ( val < vmin ) vmin = val; - } - if ( verbose ) { - STATUS("Condition number: %e / %e = %5.2f\n", - vmax, vmin, vmax/vmin); - } -} - - -static gsl_vector *solve_svd(gsl_vector *v, gsl_matrix *Mp) -{ - gsl_matrix *s_vec; - gsl_vector *s_val; - int err, n; - gsl_vector *shifts; - gsl_matrix *M; - - n = v->size; - if ( v->size != Mp->size1 ) return NULL; - if ( v->size != Mp->size2 ) return NULL; - - M = gsl_matrix_alloc(n, n); - if ( M == NULL ) return NULL; - gsl_matrix_memcpy(M, Mp); - - s_val = gsl_vector_calloc(n); - s_vec = gsl_matrix_calloc(n, n); - - err = gsl_linalg_SV_decomp_jacobi(M, s_vec, s_val); - if ( err ) { - ERROR("SVD failed: %s\n", gsl_strerror(err)); - gsl_matrix_free(s_vec); - gsl_vector_free(s_val); - return NULL; - } - /* "M" is now "U" */ - - check_eigen(s_val); - - shifts = gsl_vector_calloc(n); - err = gsl_linalg_SV_solve(M, s_vec, s_val, v, shifts); - if ( err ) { - ERROR("Matrix solution failed: %s\n", gsl_strerror(err)); - gsl_matrix_free(s_vec); - gsl_vector_free(s_val); - gsl_vector_free(shifts); - return NULL; - } - - gsl_matrix_free(s_vec); - gsl_vector_free(s_val); - gsl_matrix_free(M); - - return shifts; -} - - enum boxmask_val { BM_IG, /* "Soft" ignore */ @@ -340,14 +252,14 @@ static void show_reference_profile(struct intcontext *ic, int i) static void show_peak_box(struct intcontext *ic, struct peak_box *bx, - int results_pipe) + pthread_mutex_t *term_lock) { #ifdef HAVE_CURSES_COLOR int q; signed int h, k, l; double fs, ss; - if ( results_pipe != 0 ) write(results_pipe, "SUSPEND\n", 8); + if ( term_lock != NULL ) pthread_mutex_lock(term_lock); initscr(); clear(); @@ -411,7 +323,10 @@ static void show_peak_box(struct intcontext *ic, struct peak_box *bx, getch(); endwin(); - if ( results_pipe != 0 ) write(results_pipe, "RELEASE\n", 8); + if ( term_lock != NULL ) pthread_mutex_unlock(term_lock); +#else + STATUS("Not showing peak box because CrystFEL was compiled without " + "ncurses.\n"); #endif } @@ -442,8 +357,8 @@ static void fit_bg(struct intcontext *ic, struct peak_box *bx) } } - /* SVD is massive overkill here */ - ans = solve_svd(v, bx->bgm); + /* FIXME: SVD is massive overkill here */ + ans = solve_svd(v, bx->bgm, NULL, 0); gsl_vector_free(v); bx->a = gsl_vector_get(ans, 0); @@ -500,7 +415,7 @@ static int init_intcontext(struct intcontext *ic) } /* How many reference profiles? */ - ic->n_reference_profiles = ic->image->det->n_panels; + ic->n_reference_profiles = 1; ic->reference_profiles = calloc(ic->n_reference_profiles, sizeof(double *)); if ( ic->reference_profiles == NULL ) return 1; @@ -1255,7 +1170,7 @@ static int get_int_diag(struct intcontext *ic, Reflection *refl) static void integrate_prof2d_once(struct intcontext *ic, struct peak_box *bx, - int results_pipe) + pthread_mutex_t *term_lock) { bx->intensity = fit_intensity(ic, bx); bx->sigma = calc_sigma(ic, bx); @@ -1278,15 +1193,16 @@ static void integrate_prof2d_once(struct intcontext *ic, struct peak_box *bx, get_detector_pos(bx->refl, &pfs, &pss); pfs += bx->offs_fs; pss += bx->offs_ss; - set_detector_pos(bx->refl, 0.0, pfs, pss); + set_detector_pos(bx->refl, pfs, pss); if ( bx->intensity < -5.0*bx->sigma ) { ic->n_implausible++; set_redundancy(bx->refl, 0); } - if ( get_int_diag(ic, bx->refl) ) show_peak_box(ic, bx, - results_pipe); + if ( get_int_diag(ic, bx->refl) ) { + show_peak_box(ic, bx, term_lock); + } } else { @@ -1379,7 +1295,7 @@ static void integrate_prof2d(IntegrationMethod meth, Crystal *cr, struct image *image, IntDiag int_diag, signed int idh, signed int idk, signed int idl, double ir_inn, double ir_mid, double ir_out, - int results_pipe, int **masks) + pthread_mutex_t *term_lock, int **masks) { RefList *list; UnitCell *cell; @@ -1423,7 +1339,7 @@ static void integrate_prof2d(IntegrationMethod meth, for ( i=0; i<ic.n_boxes; i++ ) { struct peak_box *bx; bx = &ic.boxes[i]; - integrate_prof2d_once(&ic, bx, results_pipe); + integrate_prof2d_once(&ic, bx, term_lock); } //refine_rigid_groups(&ic); @@ -1436,7 +1352,7 @@ static void integrate_prof2d(IntegrationMethod meth, static void integrate_rings_once(Reflection *refl, struct image *image, struct intcontext *ic, UnitCell *cell, - int results_pipe) + pthread_mutex_t *term_lock) { double pfs, pss; struct peak_box *bx; @@ -1482,6 +1398,7 @@ static void integrate_rings_once(Reflection *refl, struct image *image, r = check_box(ic, bx, &saturated); if ( !r ) { fit_bg(ic, bx); + if ( !bg_ok(bx) ) r = 1; } bx->offs_fs = 0.0; bx->offs_ss = 0.0; @@ -1532,9 +1449,9 @@ static void integrate_rings_once(Reflection *refl, struct image *image, /* Update position */ pfs += bx->offs_fs; pss += bx->offs_ss; - set_detector_pos(refl, 0.0, pfs, pss); + set_detector_pos(refl, pfs, pss); - if ( get_int_diag(ic, refl) ) show_peak_box(ic, bx, results_pipe); + if ( get_int_diag(ic, refl) ) show_peak_box(ic, bx, term_lock); if ( intensity < -5.0*sigma ) { ic->n_implausible++; @@ -1634,7 +1551,7 @@ static void integrate_rings(IntegrationMethod meth, Crystal *cr, struct image *image, IntDiag int_diag, signed int idh, signed int idk, signed int idl, double ir_inn, double ir_mid, double ir_out, - int results_pipe, int **masks) + pthread_mutex_t *term_lock, int **masks) { RefList *list; Reflection *refl; @@ -1670,7 +1587,7 @@ static void integrate_rings(IntegrationMethod meth, refl != NULL; refl = next_refl(refl, iter) ) { - integrate_rings_once(refl, image, &ic, cell, results_pipe); + integrate_rings_once(refl, image, &ic, cell, term_lock); } //refine_rigid_groups(&ic); @@ -1687,7 +1604,7 @@ void integrate_all_4(struct image *image, IntegrationMethod meth, double ir_inn, double ir_mid, double ir_out, IntDiag int_diag, signed int idh, signed int idk, signed int idl, - int results_pipe) + pthread_mutex_t *term_lock) { int i; int *masks[image->det->n_panels]; @@ -1727,14 +1644,14 @@ void integrate_all_4(struct image *image, IntegrationMethod meth, integrate_rings(meth, cr, image, int_diag, idh, idk, idl, ir_inn, ir_mid, ir_out, - results_pipe, masks); + term_lock, masks); break; case INTEGRATION_PROF2D : integrate_prof2d(meth, cr, image, int_diag, idh, idk, idl, ir_inn, ir_mid, ir_out, - results_pipe, masks); + term_lock, masks); break; default : diff --git a/libcrystfel/src/integration.h b/libcrystfel/src/integration.h index ca04157a..a012fc14 100644 --- a/libcrystfel/src/integration.h +++ b/libcrystfel/src/integration.h @@ -3,11 +3,11 @@ * * Integration of intensities * - * Copyright © 2012-2014 Deutsches Elektronen-Synchrotron DESY, + * Copyright © 2012-2015 Deutsches Elektronen-Synchrotron DESY, * a research centre of the Helmholtz Association. * * Authors: - * 2010-2014 Thomas White <taw@physics.org> + * 2010-2015 Thomas White <taw@physics.org> * * This file is part of CrystFEL. * @@ -33,7 +33,6 @@ #include <config.h> #endif - #include "geometry.h" /** @@ -127,7 +126,7 @@ extern void integrate_all_4(struct image *image, IntegrationMethod meth, double ir_inn, double ir_mid, double ir_out, IntDiag int_diag, signed int idh, signed int idk, signed int idl, - int results_pipe); + pthread_mutex_t *term_lock); #ifdef __cplusplus diff --git a/libcrystfel/src/peaks.h b/libcrystfel/src/peaks.h index 9900c232..ba724419 100644 --- a/libcrystfel/src/peaks.h +++ b/libcrystfel/src/peaks.h @@ -3,11 +3,11 @@ * * Peak search and other image analysis * - * Copyright © 2012-2014 Deutsches Elektronen-Synchrotron DESY, + * Copyright © 2012-2015 Deutsches Elektronen-Synchrotron DESY, * a research centre of the Helmholtz Association. * * Authors: - * 2010-2014 Thomas White <taw@physics.org> + * 2010-2015 Thomas White <taw@physics.org> * * This file is part of CrystFEL. * diff --git a/libcrystfel/src/reflist-utils.c b/libcrystfel/src/reflist-utils.c index 722f800c..82943158 100644 --- a/libcrystfel/src/reflist-utils.c +++ b/libcrystfel/src/reflist-utils.c @@ -390,7 +390,7 @@ RefList *read_reflections_from_file(FILE *fh) refl = add_refl(out, h, k, l); set_intensity(refl, intensity); - set_detector_pos(refl, 0.0, fs, ss); + set_detector_pos(refl, fs, ss); set_esd_intensity(refl, sigma); set_redundancy(refl, cts); diff --git a/libcrystfel/src/reflist.c b/libcrystfel/src/reflist.c index 33c1f948..ccc421f3 100644 --- a/libcrystfel/src/reflist.c +++ b/libcrystfel/src/reflist.c @@ -75,10 +75,7 @@ struct _refldata { /* Location in image */ double fs; double ss; - - /* The distance from the exact Bragg position to the coordinates - * given above. */ - double excitation_error; + struct panel *panel; /* Non-zero if this reflection can be used for scaling */ int scalable; @@ -105,6 +102,7 @@ struct _refldata { /* User-specified temporary values */ double temp1; double temp2; + int flag; }; @@ -316,29 +314,31 @@ Reflection *next_found_refl(Reflection *refl) /********************************** Getters ***********************************/ + /** - * get_excitation_error: + * get_detector_pos: * @refl: A %Reflection + * @fs: Location at which to store the fast scan offset of the reflection + * @ss: Location at which to store the slow scan offset of the reflection * - * Returns: The excitation error for the reflection. **/ -double get_excitation_error(const Reflection *refl) +void get_detector_pos(const Reflection *refl, double *fs, double *ss) { - return refl->data.excitation_error; + *fs = refl->data.fs; + *ss = refl->data.ss; } /** - * get_detector_pos: + * get_panel: * @refl: A %Reflection - * @fs: Location at which to store the fast scan offset of the reflection - * @ss: Location at which to store the slow scan offset of the reflection + * + * Returns: the panel which the reflection appears on * **/ -void get_detector_pos(const Reflection *refl, double *fs, double *ss) +struct panel *get_panel(const Reflection *refl) { - *fs = refl->data.fs; - *ss = refl->data.ss; + return refl->data.panel; } @@ -547,6 +547,22 @@ double get_temp2(const Reflection *refl) } +/** + * get_flag: + * @refl: A %Reflection + * + * The integer flag value can be used according to the needs of the calling + * program. + * + * Returns: the flag for this reflection. + * + **/ +int get_flag(const Reflection *refl) +{ + return refl->data.flag; +} + + /********************************** Setters ***********************************/ /** @@ -570,20 +586,32 @@ void copy_data(Reflection *to, const Reflection *from) /** * set_detector_pos: * @refl: A %Reflection - * @exerr: The excitation error for this reflection * @fs: The fast scan offset of the reflection * @ss: The slow scan offset of the reflection * **/ -void set_detector_pos(Reflection *refl, double exerr, double fs, double ss) +void set_detector_pos(Reflection *refl, double fs, double ss) { - refl->data.excitation_error = exerr; refl->data.fs = fs; refl->data.ss = ss; } /** + * set_panel: + * @refl: A %Reflection + * @panel: Pointer to the panel structure on which the reflection appears + * + * Note that the pointer will be stored, not the contents of the structure. + * + **/ +void set_panel(Reflection *refl, struct panel *p) +{ + refl->data.panel = p; +} + + +/** * set_partial: * @refl: A %Reflection * @rlow: The "low" excitation error @@ -761,6 +789,21 @@ void set_temp2(Reflection *refl, double temp) } +/** + * set_flag + * @refl: A %Reflection + * @flag: New flag value + * + * @flag is an integer value which can be used according to the needs of the + * calling program. + * + **/ +void set_flag(Reflection *refl, int flag) +{ + refl->data.flag = flag; +} + + /********************************* Insertion **********************************/ static Reflection *rotate_once(Reflection *refl, int dir) diff --git a/libcrystfel/src/reflist.h b/libcrystfel/src/reflist.h index 85a87c54..dac313a4 100644 --- a/libcrystfel/src/reflist.h +++ b/libcrystfel/src/reflist.h @@ -3,11 +3,11 @@ * * Fast reflection/peak list * - * Copyright © 2012-2014 Deutsches Elektronen-Synchrotron DESY, + * Copyright © 2012-2015 Deutsches Elektronen-Synchrotron DESY, * a research centre of the Helmholtz Association. * * Authors: - * 2011-2014 Thomas White <taw@physics.org> + * 2011-2015 Thomas White <taw@physics.org> * * This file is part of CrystFEL. * @@ -84,8 +84,8 @@ extern Reflection *find_refl(const RefList *list, signed int h, signed int k, si extern Reflection *next_found_refl(Reflection *refl); /* Get */ -extern double get_excitation_error(const Reflection *refl); extern void get_detector_pos(const Reflection *refl, double *fs, double *ss); +extern struct panel *get_panel(const Reflection *refl); extern double get_partiality(const Reflection *refl); extern double get_lorentz(const Reflection *refl); extern void get_indices(const Reflection *refl, @@ -103,11 +103,12 @@ extern double get_esd_intensity(const Reflection *refl); extern double get_phase(const Reflection *refl, int *have_phase); extern double get_peak(const Reflection *refl); extern double get_mean_bg(const Reflection *refl); +extern int get_flag(const Reflection *refl); /* Set */ extern void copy_data(Reflection *to, const Reflection *from); -extern void set_detector_pos(Reflection *refl, double exerr, - double fs, double ss); +extern void set_detector_pos(Reflection *refl, double fs, double ss); +extern void set_panel(Reflection *refl, struct panel *p); extern void set_partial(Reflection *refl, double rlow, double rhigh, double p); extern void set_partiality(Reflection *refl, double p); extern void set_lorentz(Reflection *refl, double L); @@ -121,6 +122,7 @@ extern void set_peak(Reflection *refl, double peak); extern void set_mean_bg(Reflection *refl, double mean_bg); extern void set_symmetric_indices(Reflection *refl, signed int hs, signed int ks, signed int ls); +extern void set_flag(Reflection *refl, int flag); /* Insertion */ extern Reflection *add_refl(RefList *list, diff --git a/libcrystfel/src/render.c b/libcrystfel/src/render.c index 17318782..2dcb7b93 100644 --- a/libcrystfel/src/render.c +++ b/libcrystfel/src/render.c @@ -104,6 +104,84 @@ static void render_rgb(double val, double max, } +static void render_geoptimiser(double val, double max, + double *rp, double *gp, double *bp) +{ + double r; + double p; + + r = val/max; + + if ( val < 0.0 ) { + *rp = 0.0; + *gp = 0.0; + *bp = 0.0; + return; + } + + if ( r >= 0.0 && r < 0.059 ) { + p = (r-0.0)/(0.059-0.0); + *rp = 0.0; + *gp = 0.0; + *bp = ((91.0/256.0)-0.0)*p; + return; + } + + if ( r >= 0.059 && r < 0.220 ) { + p = (r-0.059)/(0.220-0.059); + *rp = ((122.0/256.0)-0.0)*p; + *gp = 0.0; + *bp = ((227.0/256.0)-(91.0/256.0))*p+(91.0/256.0); + return; + } + + if ( r >= 0.220 && r < 0.376 ) { + p = (r-0.220)/(0.376-0.220); + *rp = ((195.0/256.0)-(122.0/256.0))*p+(122.0/256.0); + *gp = 0.0; + *bp = ((93.0/256.0)-(227.0/256.0))*p+(227.0/256.0); + return; + } + + if ( r >= 0.376 && r < 0.498 ) { + p = (r-0.376)/(0.498-0.376); + *rp = ((238.0/256.0)-(195.0/256.0))*p+(195.0/256.0); + *gp = ((76.0/256.0)-0.0)*p; + *bp = (0.0-(93.0/256.0))*p+(93.0/256.0); + return; + } + + if ( r >= 0.498 && r < 0.564 ) { + p = (r-0.498)/(0.564-0.498); + *rp = (1.0-(238.0/256.0))*p+(238.0/256.0); + *gp = ((117.0/256.0)-(76.0/256.0))*p+(76.0/256.0); + *bp = 0.0; + return; + } + + if ( r >= 0.564 && r < 0.815 ) { + p = (r-0.564)/(0.815-0.564); + *rp = 1.0; + *gp = ((234.0/256.0)-(117.0/256.0))*p+(117.0/256.0); + *bp = 0.0; + return; + } + + if ( r >= 0.815 && r < 1.0 ) { + p = (r-0.815)/(1.0-0.815); + *rp = 1.0; + *gp = (1.0-(234.0/256.0))*p+(234.0/256.0); + *bp = (1.0-0.0)*p; + return; + } + + if ( r >= 1.0 ) { + *rp = 1.0; *gp = 1.0; *bp = 1.0; + return; + } +} + + static void render_ratio(double val, double max, double *rp, double *gp, double *bp) { @@ -164,5 +242,9 @@ void render_scale(double val, double max, int scale, case SCALE_RATIO : render_ratio(val, max, rp, gp, bp); break; + + case SCALE_GEOPTIMISER : + render_geoptimiser(val, max, rp, gp, bp); + break; } } diff --git a/libcrystfel/src/render.h b/libcrystfel/src/render.h index a4da922d..c2e5965a 100644 --- a/libcrystfel/src/render.h +++ b/libcrystfel/src/render.h @@ -38,7 +38,8 @@ enum { SCALE_COLOUR, SCALE_MONO, SCALE_INVMONO, - SCALE_RATIO + SCALE_RATIO, + SCALE_GEOPTIMISER }; #ifdef __cplusplus diff --git a/libcrystfel/src/stream.c b/libcrystfel/src/stream.c index 118cd782..808149e3 100644 --- a/libcrystfel/src/stream.c +++ b/libcrystfel/src/stream.c @@ -3,12 +3,12 @@ * * Stream tools * - * Copyright © 2013-2014 Deutsches Elektronen-Synchrotron DESY, + * Copyright © 2013-2015 Deutsches Elektronen-Synchrotron DESY, * a research centre of the Helmholtz Association. * Copyright © 2012 Richard Kirian * * Authors: - * 2010-2014 Thomas White <taw@physics.org> + * 2010-2015 Thomas White <taw@physics.org> * 2014 Valerio Mariani * 2011 Richard Kirian * 2011 Andrew Aquila @@ -319,7 +319,8 @@ static RefList *read_stream_reflections_2_3(FILE *fh, struct detector *det) p = find_panel_by_name(det,pn); write_fs = fs - p->orig_min_fs + p->min_fs; write_ss = ss - p->orig_min_ss + p->min_ss; - set_detector_pos(refl, 0.0, write_fs, write_ss); + set_detector_pos(refl, write_fs, write_ss); + set_panel(refl, p); } set_esd_intensity(refl, sigma); set_peak(refl, pk); @@ -383,11 +384,11 @@ static RefList *read_stream_reflections_2_1(FILE *fh, struct detector *det) p = find_orig_panel(det, fs, ss); write_fs = fs - p->orig_min_fs + p->min_fs; write_ss = ss - p->orig_min_ss + p->min_ss; - set_detector_pos(refl, 0.0, write_fs, write_ss); + set_detector_pos(refl, write_fs, write_ss); } else { - set_detector_pos(refl, 0.0, fs, ss); + set_detector_pos(refl, fs, ss); } set_esd_intensity(refl, sigma); @@ -448,11 +449,11 @@ static RefList *read_stream_reflections_2_2(FILE *fh, struct detector *det) p = find_orig_panel(det, fs, ss); write_fs = fs - p->orig_min_fs + p->min_fs; write_ss = ss - p->orig_min_ss + p->min_ss; - set_detector_pos(refl, 0.0, write_fs, write_ss); + set_detector_pos(refl, write_fs, write_ss); } else { - set_detector_pos(refl, 0.0, fs, ss); + set_detector_pos(refl, fs, ss); } @@ -708,6 +709,10 @@ static int write_crystal(Stream *st, Crystal *cr, int include_reflections) rad = crystal_get_profile_radius(cr); fprintf(st->fh, "profile_radius = %.5f nm^-1\n", rad/1e9); + if ( crystal_get_notes(cr) != NULL ) { + fprintf(st->fh, "%s\n", crystal_get_notes(cr)); + } + reflist = crystal_get_reflections(cr); if ( reflist != NULL ) { @@ -838,7 +843,10 @@ int write_chunk(Stream *st, struct image *i, struct hdfile *hdfile, } for ( j=0; j<i->n_crystals; j++ ) { - ret = write_crystal(st, i->crystals[j], include_reflections); + if ( crystal_get_user_flag(i->crystals[j]) == 0 ) { + ret = write_crystal(st, i->crystals[j], + include_reflections); + } } fprintf(st->fh, CHUNK_END_MARKER"\n"); @@ -1260,7 +1268,12 @@ Stream *open_stream_for_read(const char *filename) st = malloc(sizeof(struct _stream)); if ( st == NULL ) return NULL; - st->fh = fopen(filename, "r"); + if ( strcmp(filename, "-") == 0 ) { + st->fh = stdin; + } else { + st->fh = fopen(filename, "r"); + } + if ( st->fh == NULL ) { free(st); return NULL; @@ -1499,7 +1512,7 @@ void write_geometry_file(Stream *st, const char *geom_filename) { do { rval = fgets(line, 1023, geom_fh); - fputs(line, st->fh); + if ( rval != NULL ) fputs(line, st->fh); } while ( rval != NULL ); fclose(geom_fh); @@ -1526,27 +1539,3 @@ int rewind_stream(Stream *st) return fseek(st->fh, 0, SEEK_SET); } - - -double extract_f_from_stuff(const char *field_name, - struct stuff_from_stream* stuff) -{ - int i; - - char field_name_plus_equal[256]; - sprintf(field_name_plus_equal, "hdf5%s = ", field_name); - - - - for ( i=0; i<stuff->n_fields; i++ ) { - - if ( strncmp(stuff->fields[i], field_name_plus_equal, - strlen(field_name_plus_equal)) == 0 ) { - return atoi(stuff->fields[i]+ - strlen(field_name_plus_equal)); - } - } - - ERROR("Failed to recovery camera length from stream file\n"); - return -1; -} diff --git a/libcrystfel/src/stream.h b/libcrystfel/src/stream.h index afa9acda..a8cf4639 100644 --- a/libcrystfel/src/stream.h +++ b/libcrystfel/src/stream.h @@ -109,9 +109,6 @@ extern void write_geometry_file(Stream *st, const char *geom_filename); extern int rewind_stream(Stream *st); extern int is_stream(const char *filename); -extern double extract_f_from_stuff(const char *field_name, - struct stuff_from_stream* stuff); - #ifdef __cplusplus } #endif diff --git a/libcrystfel/src/utils.c b/libcrystfel/src/utils.c index f8f93046..af0abedc 100644 --- a/libcrystfel/src/utils.c +++ b/libcrystfel/src/utils.c @@ -40,6 +40,8 @@ #include <gsl/gsl_matrix.h> #include <gsl/gsl_vector.h> #include <gsl/gsl_blas.h> +#include <gsl/gsl_linalg.h> +#include <gsl/gsl_eigen.h> #include "utils.h" #include "image.h" @@ -108,6 +110,161 @@ void show_matrix(gsl_matrix *M) } +static int check_eigen(gsl_vector *e_val, int verbose) +{ + int i; + double vmax, vmin; + const int n = e_val->size; + const double max_condition = 1e6; + int n_filt = 0; + + if ( verbose ) STATUS("Eigenvalues:\n"); + vmin = +INFINITY; + vmax = 0.0; + for ( i=0; i<n; i++ ) { + double val = gsl_vector_get(e_val, i); + if ( verbose ) STATUS("%i: %e\n", i, val); + if ( val > vmax ) vmax = val; + if ( val < vmin ) vmin = val; + } + + for ( i=0; i<n; i++ ) { + double val = gsl_vector_get(e_val, i); + if ( val < vmax/max_condition ) { + gsl_vector_set(e_val, i, 0.0); + n_filt++; + } + } + + vmin = +INFINITY; + vmax = 0.0; + for ( i=0; i<n; i++ ) { + double val = gsl_vector_get(e_val, i); + if ( val == 0.0 ) continue; + if ( val > vmax ) vmax = val; + if ( val < vmin ) vmin = val; + } + if ( verbose ) { + STATUS("Condition number: %e / %e = %5.2f\n", + vmax, vmin, vmax/vmin); + STATUS("%i out of %i eigenvalues filtered.\n", n_filt, n); + } + + return n_filt; +} + + +/** + * solve_svd: + * v: a gsl_vector + * M: a gsl_matrix + * n_filt: pointer to store the number of filtered eigenvalues + * verbose: flag for verbosity on the terminal + * + * Solves the matrix equation M.x = v, returning x. + * Performs rescaling and eigenvalue filtering. + **/ +gsl_vector *solve_svd(gsl_vector *v, gsl_matrix *M, int *pn_filt, int verbose) +{ + gsl_matrix *s_vec; + gsl_vector *s_val; + int err, n; + gsl_vector *shifts; + gsl_vector *SB; + gsl_vector *SinvX; + gsl_matrix *S; /* rescaling matrix due to Bricogne */ + gsl_matrix *AS; + gsl_matrix *SAS; + int i; + int n_filt; + gsl_matrix *SAS_copy; + + n = v->size; + if ( v->size != M->size1 ) return NULL; + if ( v->size != M->size2 ) return NULL; + + /* Calculate the rescaling matrix S */ + S = gsl_matrix_calloc(n, n); + for ( i=0; i<n; i++ ) { + double sii = pow(gsl_matrix_get(M, i, i), -0.5); + gsl_matrix_set(S, i, i, sii); + } + + /* Calculate the matrix SAS, which we will be (not) inverting */ + AS = gsl_matrix_calloc(n, n); + SAS = gsl_matrix_calloc(n, n); + gsl_blas_dgemm(CblasNoTrans, CblasNoTrans, 1.0, M, S, 0.0, AS); + gsl_blas_dgemm(CblasNoTrans, CblasNoTrans, 1.0, S, AS, 0.0, SAS); + gsl_matrix_free(AS); + + /* Calculate the vector SB, which is the RHS of the equation */ + SB = gsl_vector_calloc(n); + gsl_blas_dgemv(CblasNoTrans, 1.0, S, v, 0.0, SB); + + if ( verbose ) { + STATUS("The equation after rescaling:\n"); + show_matrix_eqn(SAS, SB); + } + + SAS_copy = gsl_matrix_alloc(SAS->size1, SAS->size2); + gsl_matrix_memcpy(SAS_copy, SAS); + + for ( i=0; i<n; i++ ) { + int j; + if ( isnan(gsl_vector_get(SB, i)) ) gsl_vector_set(SB, i, 0.0); + for ( j=0; j<n; j++ ) { + if ( isnan(gsl_matrix_get(SAS, i, j)) ) { + gsl_matrix_set(SAS, i, j, 0.0); + } + } + } + + /* Do the SVD */ + s_val = gsl_vector_calloc(n); + s_vec = gsl_matrix_calloc(n, n); + err = gsl_linalg_SV_decomp_jacobi(SAS, s_vec, s_val); + if ( err ) { + if ( verbose ) ERROR("SVD failed: %s\n", gsl_strerror(err)); + gsl_matrix_free(s_vec); + gsl_vector_free(s_val); + gsl_matrix_free(SAS); + gsl_matrix_free(S); + return NULL; + } + /* "SAS" is now "U" */ + + /* Filter the eigenvalues */ + n_filt = check_eigen(s_val, verbose); + if ( pn_filt != NULL ) *pn_filt = n_filt; + + gsl_matrix_free(SAS_copy); + + /* Solve the equation SAS.SinvX = SB */ + SinvX = gsl_vector_calloc(n); + err = gsl_linalg_SV_solve(SAS, s_vec, s_val, SB, SinvX); + gsl_vector_free(SB); + gsl_matrix_free(SAS); + gsl_matrix_free(s_vec); + gsl_vector_free(s_val); + + if ( err ) { + ERROR("Matrix solution failed: %s\n", gsl_strerror(err)); + gsl_matrix_free(S); + gsl_vector_free(SinvX); + return NULL; + } + + /* Calculate S.SinvX to get X, the shifts */ + shifts = gsl_vector_calloc(n); + gsl_blas_dgemv(CblasNoTrans, 1.0, S, SinvX, 0.0, shifts); + + gsl_matrix_free(S); + gsl_vector_free(SinvX); + + return shifts; +} + + size_t notrail(char *s) { ssize_t i; diff --git a/libcrystfel/src/utils.h b/libcrystfel/src/utils.h index 2f2009d6..4955f875 100644 --- a/libcrystfel/src/utils.h +++ b/libcrystfel/src/utils.h @@ -103,6 +103,8 @@ extern struct rvec quat_rot(struct rvec q, struct quaternion z); extern void show_matrix_eqn(gsl_matrix *M, gsl_vector *v); extern void show_matrix(gsl_matrix *M); +extern gsl_vector *solve_svd(gsl_vector *v, gsl_matrix *M, int *n_filt, + int verbose); extern size_t notrail(char *s); extern void chomp(char *s); diff --git a/relnotes-0.6.0 b/relnotes-0.6.0 new file mode 100644 index 00000000..22efa56d --- /dev/null +++ b/relnotes-0.6.0 @@ -0,0 +1,257 @@ +CrystFEL - Crystallography with a FEL +------------------------------------- + +Release notes for version 0.6.0 + +Copyright © 2012-2015 Deutsches Elektronen-Synchrotron DESY, + a research centre of the Helmholtz Association. + +Authors: + Thomas White <taw@physics.org> + Richard Kirian <rkirian@asu.edu> + Kenneth Beyerlein <kenneth.beyerlein@desy.de> + Andrew Aquila <andrew.aquila@cfel.de> + Andrew Martin <andrew.martin@desy.de> + Lorenzo Galli <lorenzo.galli@desy.de> + Chun Hong Yoon <chun.hong.yoon@desy.de> + Kenneth Beyerlein <kenneth.beyerlein@desy.de> + Karol Nass <karol.nass@desy.de> + Nadia Zatsepin <nadia.zatsepin@asu.edu> + Anton Barty <anton.barty@desy.de> + Cornelius Gati <cornelius.gati@desy.de> + Fedor Chervinskii <fedor.chervinskii@gmail.com> + Alexandra Tolstikova <alexandra.tolstikova@desy.de> + Wolfgang Brehm <wolfgang.brehm@gmail.com> + Valerio Mariani <valerio.mariani@desy.de> + Parker de Waal <Parker.deWaal@vai.org> + Takanori Nakane <nakane.t@gmail.com> + Keitaro Yamashita <k.yamashita@spring8.or.jp> + Oleksandr Yefanov <oleksandr.yefanov@cfel.de> + +CrystFEL is free software: you can redistribute it and/or modify it under the +terms of the GNU General Public License as published by the Free Software +Foundation, either version 3 of the License, or (at your option) any later +version. + +CrystFEL is distributed in the hope that it will be useful, but WITHOUT ANY +WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A +PARTICULAR PURPOSE. See the GNU General Public License for more details. + +You should have received a copy of the GNU General Public License along with +CrystFEL. If not, see <http://www.gnu.org/licenses/>. + + +Overview +-------- + +The most important new features in this version of CrystFEL are: + +- Support for multi-event HDF5 files (e.g. CXI format as used by CXIDB) + +- geoptimiser: a new tool for precisely optimising the detector geometry + +- Removal of beam files / automatic determination of spot prediction parameters + +- whirligig: a new tool for finding clusters of similarly-oriented + crystal snapshots, e.g. for finding "mini rotation series" in data + from slow extrusion sample delivery methods. + +- Introduction of "CrystFEL unit cell files". + +These new features have individual sections below. In addition, there are many +other new developments. See the ChangeLog or the changes page on the website +for more details. There were, of course, the usual large number of smaller +refinements and bug fixes. + + +Support for multi-event HDF5 files +---------------------------------- + +CrystFEL's handling of HDF5 files has been made much more flexible. Most +importantly, it now offers the ability to handle HDF5 files which contain more +than one event (frame). Until now, CrystFEL has required that each event be +contained in its own file, which can place a lot of unnecessary strain on +filesystems. Recent versions of Cheetah allow you to create larger files which +contain many events each using the CXI format (see http://www.cxidb.org for more +details). CrystFEL now supports this as well as almost any reasonable +multi-event HDF5 layout. + +If you choose to continue using many small files, you should not notice much +difference. However, some small updates will be required. Firstly, +indexamajig's "-e" and "--image" command-line parameters have been removed. +Instead, you need to edit your geometry file and add a line such as this near +the top: + + data = /data/rawdata + +Geometry files now contain all the information needed to interpret the contents +of the HDF5 files as a physical setup, including the photon energy. Beam +parameter files have been removed. See below for more information about this. + +When using multi-event files in CXI format, the peak lists are read differently. +Use --peaks=cxi to retrieve peak lists from CXI files. + +hdfsee has been extended to support multi-event files, including navigation +between events. You should always provide a geometry file on the command line, +otherwise it won't know how to interpret the contents of your HDF5 file. + +A new tool in CrystFEL 0.6.0, list_events, is provided to simplify the process +of creating lists of individual events. However, most users will simply be +able to list the multi-event filenames themselves in the input to indexamajig, +which will then process all of the events in the file. See "man indexamajig" +and "man list_events" for more details. + +Finally, it may be necessary to update your check-near-bragg and +check-peak-detection scripts. Simply make a fresh copy from the CrystFEL +"scripts" folder or download them from the website. + + +geoptimiser: a tool for optimising the detector geometry +-------------------------------------------------------- + +CrystFEL 0.6.0 introduces a new tool, geoptimiser, for optimising the detector +geometry. You simply give it a stream containing indexing results obtained with +the approximate geometry, and it gives you a refined geometry. It can refine +panel translations, rotations and camera lengths. + +To use geoptimiser, you need to add some extra information about the +mechanical construction of your detector. This is used to constrain the +refinement appropriately, for example to ensure that panels which share sensor +silicon do not move relative to one another. If you're processing CSPAD data, +you can simply copy all the rigid_group lines from one of the CSPAD geometry +file examples (folder doc/examples) into your own geometry file. + +Refer to "man geoptimiser" and "man crystfel_geometry" for more information. + + +Removal of beam files / automatic determination of spot prediction parameters +----------------------------------------------------------------------------- + +As of version 0.6.0, "beam files" have been removed completely. This should +simplify usage for most people and remove a lot of ambiguity. Programs which +need X-ray beam parameters, such as pattern_sim, now have additional command- +-line arguments to provide them. indexamajig now determines these parameters +automatically for the purposes of spot prediction. + +We are interested in feedback about the automatic spot prediction parameter +determination. If it appears not to work well for you, you can restore the old +behaviour by using the new command-line options for indexamajig: +--fix-profile-radius, --fix-bandwidth and --fix-divergence. Simply set these +parameters to the values you had in your old beam file. + +The photon energy, or information about where to get it, now needs to be in the +geometry file. This can be done with a line like this: + + photon_energy = /LCLS/photon_energy_eV + +or, for a fixed value: + + photon_energy = 8300 + + +whirligig: a tool for finding "mini rotation series" +---------------------------------------------------- + +CrystFEL 0.6.0 introduces yet another tool, whirligig, which can be used for +locating "mini rotation series" in the output from indexamajig. This might be +used to perform an experiment similar to that described by Gati et al., IUCrJ +1 (2014) p87. In this initial version, whirligig finds runs of consecutive +frames which contain crystals in similar orientations. It writes the +corresponding filenames and event/crystal identifiers to log files, which might +be useful for further analysis. + +This is the program described by Nogly et al., IUCrJ 2 (2015). Refer to "man +whirligig" for usage information. + + +Introduction of CrystFEL unit cell files +---------------------------------------- + +CrystFEL offers you the ability to index patterns using Bravais lattice +information but without unit cell parameters, for example: "index these using +only tetragonal primitive lattices, but any parameters". However, it's awkward +to give this information using a PDB file: you essentially have to "trick" it +into interpreting the parameters correctly, then throw away the parameter +information. + +Version 0.6.0 of CrystFEL introduces a new way of specifying unit cell +information. These new unit cell files look like this: + + CrystFEL unit cell file version 1.0 + + lattice_type = cubic + centering = I + + a = 66.2 A + b = 66.2 A + c = 66.2 A + + al = 90.0 deg + be = 90.0 deg + ga = 90.0 deg + +In the event that you want to specify the lattice type information alone, you +can simply omit the cell parameters: + + CrystFEL unit cell file version 1.0 + + lattice_type = tetragonal + centering = P + unique_axis = c + +Note that a unique axis must be specified for all types of cell where this makes +sense, and can be omitted otherwise. This was done in the first example above, +which is cubic and therefore has no unique axis. + +You can, of course, continue to use PDB files just like before. + + +API changes +----------- + +The following changes have been made to the libcrystfel API: + +New functions: + - The event API (see libcrystfel/src/events.h) + - load_cell_from_file() + - cell_has_parameters() + - find_orig_panel() + - crystal_{get,set}_num_implausible_reflections() were added + - panel_is_in_rigid_group() + - rigid_group_is_in_collection() + - single_panel_data_source() + - find_rigid_group_collection_by_name() + - write_detector_geometry_2() + - find_intersections_to_res() + - sphere_fraction() + - gaussian_fraction() + - hdf5_read2() + - check_path_existence() + - get_peaks_cxi() + - hdfile_get_value() + - fill_event_list() + - image_reflection_closest() + - intmat_identity() + - extract_f_from_stuff() + +Removed functions: + - cell_set_cartesian_{a,b,c}() + - cell_{get,set}_pointgroup() + - twod_mapping() + - get_value() + +Changed function prototypes: + - record_image() + - get_detector_geometry() + - fill_in_values() + - write_detector_geometry() + - hdf5_write_image() + - hdf5_read() + - hdfile_set_image() + - hdfile_get_string_value() + - {get,set}_partial() + - write_chunk() + - prepare_indexing() and {dirax,mosflm,reax,grainspotter,xds}_prepare() + +beam-parameters.h was removed, and "struct beam_params" is now defined in +image.h. diff --git a/scripts/ave-resolution b/scripts/ave-resolution new file mode 100755 index 00000000..ff93ed68 --- /dev/null +++ b/scripts/ave-resolution @@ -0,0 +1,35 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- + +# +# Find mean diffracting resolution +# +# Copyright © 2014-2015 Deutsches Elektronen-Synchrotron DESY, +# a research centre of the Helmholtz Association. +# +# Author: +# 2014-2015 Thomas White <taw@physics.org> +# + +import sys +import numpy + +f = open(sys.argv[1]) +a = [] + +while True: + fline = f.readline() + if not fline: + break + if fline.find("diffraction_resolution_limit") != -1: + res = float(fline.split('= ')[1].split(' ')[0].rstrip("\r\n")) + a.append(res) + continue + +f.close() + +b = numpy.array(a) +print " Mean: %.2f nm^-1 = %.2f A" % (numpy.mean(b),10.0/numpy.mean(b)) +print " Best: %.2f nm^-1 = %.2f A" % (numpy.max(b),10.0/numpy.max(b)) +print "Worst: %.2f nm^-1 = %.2f A" % (numpy.min(b),10.0/numpy.min(b)) +print "Std deviation: %.2f nm^-1" % (numpy.std(b)) diff --git a/scripts/check-near-bragg b/scripts/check-near-bragg index 4077c6e2..a651d896 100755 --- a/scripts/check-near-bragg +++ b/scripts/check-near-bragg @@ -3,6 +3,8 @@ use strict; use File::Basename; +my $skip = 0; + my $args = join(" ", splice(@ARGV, 1, scalar(@ARGV)-1)); if ( !($args eq "") ) { printf("Extra arguments for hdfsee: %s\n", $args); @@ -19,6 +21,8 @@ my $in_image = 0; my $line; my $filename; my $event = ""; +my $n_seen = 0; + while ( $line = <FH> ) { chomp $line; @@ -59,10 +63,18 @@ while ( $line = <FH> ) { $ev = " --event=".$event; $evr = ", event ".$event; } - printf(STDERR "Viewing %s%s\n", $filename, $evr); - system("hdfsee ".$filename.$ev. - " --peak-overlay=list.tmp ".$args); - if ( $? != 0 ) { exit; } + + $n_seen++; + if ( $n_seen > $skip ) { + + printf(STDERR "Viewing %s%s\n", $filename, $evr); + system("hdfsee ".$filename.$ev. + " --peak-overlay=list.tmp ".$args); + if ( $? != 0 ) { exit; } + } else { + printf(STDERR "Skipping %s%s\n", $filename, $evr); + } + unlink("list.tmp"); open(TMP, "> list.tmp"); $handled = 1; diff --git a/scripts/check-peak-detection b/scripts/check-peak-detection index 537e1698..9b3e3d6f 100755 --- a/scripts/check-peak-detection +++ b/scripts/check-peak-detection @@ -3,6 +3,8 @@ use strict; use File::Basename; +my $skip = 0; + my $only; my $file; my $start; @@ -38,6 +40,8 @@ my $line; my $filename; my $event; my $indexed; +my $n_seen = 0; + while ( $line = <FH> ) { chomp $line; @@ -108,10 +112,18 @@ while ( $line = <FH> ) { $ev = " --event=".$event; $evr = ", event ".$event; } - printf(STDERR "Viewing %s%s\n", $filename, $evr); - system("hdfsee ".$filename.$ev. - " --peak-overlay=list.tmp ".$args); - if ( $? != 0 ) { exit; } + + $n_seen++; + if ( $n_seen > $skip ) { + + printf(STDERR "Viewing %s%s\n", $filename, $evr); + system("hdfsee ".$filename.$ev. + " --peak-overlay=list.tmp ".$args); + if ( $? != 0 ) { exit; } + } else { + printf(STDERR "Skipping %s%s\n", $filename, $evr); + } + unlink("list.tmp"); open(TMP, "> list.tmp"); $in_image = 0; diff --git a/scripts/compare-hkl.gp b/scripts/compare-hkl.gp index 7a86cf22..93ca5513 100644 --- a/scripts/compare-hkl.gp +++ b/scripts/compare-hkl.gp @@ -1,12 +1,13 @@ -set ylabel "R ( sum(|I1-kI2|) / sum(I1) ) (%) [blue]" +set ylabel "Rsplit (%)" +set y2label "CChalf (fraction)" set ytics nomirror -set yrange [0:120] -#set y2range [0:50000] -#set xrange [0.1:1.15] -unset key -#set y2tics +set yrange [0:90] +set y2range [0:1] +set key bottom right +set y2tics set xtics ("100" 0.1000, "50" 0.2000, "10" 1.000, "8" 1.250, "6" 1.667, "5" 2.000, "4" 2.500, "3.5" 2.857, "3" 3.333, "2.5" 4.000, "2" 5.000, "1.8" 5.556, "1.6" 6.250, "1.4" 7.143, "1.2" 8.333, "1" 10.00, "0.9" 11.11, "0.8" 12.50) set xlabel "Resolution d (= lambda/2sin(theta)) / Angstrom" -plot "shells.dat" using 1:2 w l lw 3 lc 3 axis x1y1 +plot "rsplit.dat" using 1:2 w l lw 3 axis x1y1 title "Rsplit / %" +replot "cchalf.dat" using 1:2 w l lw 3 axis x1y2 title "CChalf" diff --git a/scripts/crystal-frame-number b/scripts/crystal-frame-number new file mode 100755 index 00000000..b3ed3f1f --- /dev/null +++ b/scripts/crystal-frame-number @@ -0,0 +1,24 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- + + +import sys + +f = open(sys.argv[1]) + +frame_number = 0 +crystal_number = 0 + +while True: + fline = f.readline() + if not fline: + break + if fline.find("Image filename") != -1: + frame_number += 1 + fn = fline.split(': ')[1].split(' ')[0].rstrip("\r\n") + print 'Frame %i: %s' % (frame_number, fn) + if fline.find("diffraction_resolution_limit") != -1: + crystal_number += 1 + print ' Crystal %i: %s' % (crystal_number, fline.rstrip("\r\n")) + +f.close() diff --git a/scripts/detector-shift b/scripts/detector-shift new file mode 100755 index 00000000..f37a645a --- /dev/null +++ b/scripts/detector-shift @@ -0,0 +1,120 @@ +#!/usr/bin/env python + +# +# Determine mean detector shift based on prediction refinement results +# +# Copyright (c) 2015 Deutsches Elektronen-Synchrotron DESY, +# a research centre of the Helmholtz Association. +# +# Author: +# 2015 Thomas White <taw@physics.org> +# + +import sys +import os +import re +import matplotlib.pyplot as plt + +f = open(sys.argv[1], 'r') +if len(sys.argv) > 2: + geom = sys.argv[2] + have_geom = 1 +else: + have_geom = 0 + +# Determine the mean shifts +x_shifts = [] +y_shifts = [] +z_shifts = [] + +prog1 = re.compile("^predict_refine/det_shift\sx\s=\s([0-9\.\-]+)\sy\s=\s([0-9\.\-]+)\smm$") +prog2 = re.compile("^predict_refine/clen_shift\s=\s([0-9\.\-]+)\smm$") + +while True: + + fline = f.readline() + if not fline: + break + + match = prog1.match(fline) + if match: + xshift = float(match.group(1)) + yshift = float(match.group(2)) + x_shifts.append(xshift) + y_shifts.append(yshift) + + match = prog2.match(fline) + if match: + zshift = float(match.group(1)) + z_shifts.append(zshift) + +f.close() + +mean_x = sum(x_shifts) / len(x_shifts) +mean_y = sum(y_shifts) / len(y_shifts) +print 'Mean shifts: dx = %.2f mm, dy = %.2f mm' % (mean_x,mean_y) + +# Apply shifts to geometry +if have_geom: + + out = os.path.splitext(geom)[0]+'-predrefine.geom' + print 'Applying corrections to %s, output filename %s' % (geom,out) + g = open(geom, 'r') + h = open(out, 'w') + + prog1 = re.compile("^\s*res\s+=\s+([0-9\.]+)\s") + prog2 = re.compile("^\s*(.*)\/res\s+=\s+([0-9\.]+)\s") + prog3 = re.compile("^\s*(.*)\/corner_x\s+=\s+([0-9\.\-]+)\s") + prog4 = re.compile("^\s*(.*)\/corner_y\s+=\s+([0-9\.\-]+)\s") + default_res = 0 + while True: + + fline = g.readline() + if not fline: + break + + match = prog1.match(fline) + if match: + default_res = float(match.group(1)) + print 'default res %f' % (default_res) + h.write(fline) + continue + + match = prog2.match(fline) + if match: + panel = match.group(1) + panel_res = float(match.group(2)) + print 'panel res %s / %f' % (panel, panel_res) + h.write(fline) + continue + + match = prog3.match(fline) + if match: + panel = match.group(1) + panel_cnx = float(match.group(2)) + res = default_res # FIXME! + h.write('%s/corner_x = %f\n' % (panel,panel_cnx+(mean_x*res*1e-3))) + continue + + match = prog4.match(fline) + if match: + panel = match.group(1) + panel_cny = float(match.group(2)) + res = default_res # FIXME! + h.write('%s/corner_y = %f\n' % (panel,panel_cny+(mean_y*res*1e-3))) + continue + + h.write(fline) + + g.close() + h.close() + +plt.plot(x_shifts, y_shifts, 'rx') +plt.plot(0, 0, 'bo') +plt.axis([-2,2,-2,2]) +plt.title('Detector shifts according to prediction refinement') +plt.xlabel('x shift / mm') +plt.ylabel('y shift / mm') +plt.grid(True) +plt.show() + diff --git a/scripts/indexed-filenames b/scripts/indexed-filenames index 842d264d..e4ea443a 100755 --- a/scripts/indexed-filenames +++ b/scripts/indexed-filenames @@ -7,24 +7,35 @@ open(FH, $ARGV[0]); my $line; my $is_indexed; my $filename; +my $event = ""; while ( $line = <FH> ) { if ( $line =~ /^-----\ Begin chunk\ -----$/ ) { $is_indexed = 0; + $event = ""; } if ( $line =~ /^Image\ filename: (.*)$/ ) { $filename = $1; } + if ( $line =~ /^Event: (.*)$/ ) { + $event = $1; + } + if ( $line =~ /^Cell\ parameters/ ) { $is_indexed = 1; } if ( $line =~ /^-----\ End chunk\ -----$/ ) { if ( $is_indexed ) { - printf("%s\n", $filename); + printf("%s", $filename); + if ( $event ) { + printf(" %s\n", $event); + } else { + printf("\n"); + } } } diff --git a/scripts/plot-predict-refine b/scripts/plot-predict-refine new file mode 100755 index 00000000..5af7b39c --- /dev/null +++ b/scripts/plot-predict-refine @@ -0,0 +1,12 @@ +#!/bin/sh + +INFILE=$1 + +grep "predict_refine/R" $INFILE > plotme.dat +gnuplot -persist << EOF +set xlabel "Profile radius before refinement / nm^-1" +set ylabel "Profile radius after refinement / nm^-1" +plot "plotme.dat" using 4:7 +replot x +replot 1.2*x +EOF diff --git a/scripts/plot-radius-resolution b/scripts/plot-radius-resolution new file mode 100755 index 00000000..da7a047d --- /dev/null +++ b/scripts/plot-radius-resolution @@ -0,0 +1,12 @@ +#!/bin/sh + +INFILE=$1 + +grep "profile_radius" $INFILE > plotme1.dat +grep "diffraction_resolution_limit" $INFILE > plotme2.dat +paste plotme1.dat plotme2.dat > plotme.dat +gnuplot -persist << EOF +set xlabel "Estimated crystal diameter (= 2/profile radius) / nm" +set ylabel "Resolution limit / nm^-1" +plot "plotme.dat" using (2/\$3):7 +EOF diff --git a/src/ambigator.c b/src/ambigator.c index 39609e5f..2268b400 100644 --- a/src/ambigator.c +++ b/src/ambigator.c @@ -3,13 +3,13 @@ * * Resolve indexing ambiguities * - * Copyright © 2014 Deutsches Elektronen-Synchrotron DESY, - * a research centre of the Helmholtz Association. + * Copyright © 2014-2015 Deutsches Elektronen-Synchrotron DESY, + * a research centre of the Helmholtz Association. * Copyright © 2014 Wolfgang Brehm * * Authors: - * 2014 Thomas White <taw@physics.org> - * 2014 Wolfgang Brehm <wolfgang.brehm@gmail.com> + * 2014-2015 Thomas White <taw@physics.org> + * 2014 Wolfgang Brehm <wolfgang.brehm@gmail.com> * * This file is part of CrystFEL. * @@ -1049,18 +1049,6 @@ int main(int argc, char *argv[]) } - if ( argc != (optind+1) ) { - ERROR("Please provide exactly one stream filename.\n"); - return 1; - } - - infile = argv[optind++]; - st = open_stream_for_read(infile); - if ( st == NULL ) { - ERROR("Failed to open input stream '%s'\n", infile); - return 1; - } - if ( s_sym_str == NULL ) { ERROR("You must specify the input symmetry (with -y)\n"); return 1; @@ -1111,6 +1099,19 @@ int main(int argc, char *argv[]) } } + if ( argc != (optind+1) ) { + ERROR("Please provide exactly one stream filename.\n"); + return 1; + } + + infile = argv[optind++]; + st = open_stream_for_read(infile); + if ( st == NULL ) { + ERROR("Failed to open input stream '%s'\n", infile); + return 1; + } + + crystals = NULL; n_crystals = 0; max_crystals = 0; diff --git a/src/cell_explorer.c b/src/cell_explorer.c index 93f4a26f..923462bd 100644 --- a/src/cell_explorer.c +++ b/src/cell_explorer.c @@ -418,7 +418,7 @@ static gboolean draw_sig(GtkWidget *da, GdkEventExpose *event, HistoBox *b) cairo_text_extents_t ext; char label[256]; - cairo_select_font_face(cr, "Serif", CAIRO_FONT_SLANT_ITALIC, + cairo_select_font_face(cr, "Liberation Serif", CAIRO_FONT_SLANT_ITALIC, CAIRO_FONT_WEIGHT_BOLD); cairo_set_font_size(cr, height/10.0); @@ -695,7 +695,8 @@ static void scan_minmax(CellWindow *w) fprintf(stderr, "Too many indexing methods\n"); } else { IndexingMethod m = w->indms[i]; - w->unique_indms[w->n_unique_indms++] = m; + w->unique_indms[w->n_unique_indms] = m; + w->active_indms[w->n_unique_indms++] = 1; } } diff --git a/src/compare_hkl.c b/src/compare_hkl.c index eed92af0..abb84e43 100644 --- a/src/compare_hkl.c +++ b/src/compare_hkl.c @@ -41,6 +41,7 @@ #include <assert.h> #include <gsl/gsl_errno.h> #include <gsl/gsl_statistics.h> +#include <gsl/gsl_fit.h> #include "version.h" #include "utils.h" @@ -661,6 +662,110 @@ static int get_bin(struct shells *s, Reflection *refl, UnitCell *cell) } +static int wilson_scale(RefList *list1, RefList *list2, UnitCell *cell) +{ + Reflection *refl1; + Reflection *refl2; + RefListIterator *iter; + int max_n = 256; + int n = 0; + double *x; + double *y; + int r; + double G, B; + double c0, c1, cov00, cov01, cov11, chisq; + + x = malloc(max_n*sizeof(double)); + y = malloc(max_n*sizeof(double)); + if ( (x==NULL) || (y==NULL) ) { + ERROR("Failed to allocate memory for scaling.\n"); + return 1; + } + + for ( refl1 = first_refl(list1, &iter); + refl1 != NULL; + refl1 = next_refl(refl1, iter) ) + { + signed int h, k, l; + double Ih1, Ih2; + double res; + + get_indices(refl1, &h, &k, &l); + res = resolution(cell, h, k, l); + + refl2 = find_refl(list2, h, k, l); + assert(refl2 != NULL); + + Ih1 = get_intensity(refl1); + Ih2 = get_intensity(refl2); + + if ( (Ih1 <= 0.0) || (Ih2 <= 0.0) ) continue; + if ( isnan(Ih1) || isinf(Ih1) ) continue; + if ( isnan(Ih2) || isinf(Ih2) ) continue; + + if ( n == max_n ) { + max_n *= 2; + x = realloc(x, max_n*sizeof(double)); + y = realloc(y, max_n*sizeof(double)); + if ( (x==NULL) || (y==NULL) ) { + ERROR("Failed to allocate memory for scaling.\n"); + return 1; + } + } + + x[n] = res*res; + y[n] = log(Ih1/Ih2); + n++; + + } + + if ( n < 2 ) { + ERROR("Not enough reflections for scaling\n"); + return 1; + } + + r = gsl_fit_linear(x, 1, y, 1, n, &c0, &c1, + &cov00, &cov01, &cov11, &chisq); + + if ( r ) { + ERROR("Scaling failed.\n"); + return 1; + } + + G = exp(c0); + B = c1/2.0; + + STATUS("Relative scale factor = %f, relative B factor = %f A^2\n", + G, B*1e20); + STATUS("A scale factor greater than 1 means that the second reflection " + "list is weaker than the first.\n"); + STATUS("A positive relative B factor means that the second reflection " + "list falls off with resolution more quickly than the first.\n"); + + free(x); + free(y); + + /* Apply the scaling factor */ + for ( refl2 = first_refl(list2, &iter); + refl2 != NULL; + refl2 = next_refl(refl2, iter) ) + { + signed int h, k, l; + double res; + double corr; + + get_indices(refl2, &h, &k, &l); + res = resolution(cell, h, k, l); + + corr = G * exp(2.0*B*res*res); + set_intensity(refl2, get_intensity(refl2)*corr); + set_esd_intensity(refl2, get_esd_intensity(refl2)*corr); + + } + return 0; +} + + static void do_fom(RefList *list1, RefList *list2, UnitCell *cell, double rmin, double rmax, enum fom fom, int config_unity, int nshells, const char *filename, @@ -671,7 +776,6 @@ static void do_fom(RefList *list1, RefList *list2, UnitCell *cell, Reflection *refl1; RefListIterator *iter; FILE *fh; - double scale; struct fom_context *fctx; struct shells *shells; const char *t1, *t2; @@ -684,10 +788,9 @@ static void do_fom(RefList *list1, RefList *list2, UnitCell *cell, return; } - if ( config_unity ) { - scale = 1.0; - } else { - scale = stat_scale_intensity(list1, list2); + if ( !config_unity && wilson_scale(list1, list2, cell) ) { + ERROR("Error with scaling.\n"); + return; } /* Calculate the bins */ @@ -726,9 +829,9 @@ static void do_fom(RefList *list1, RefList *list2, UnitCell *cell, } i1 = get_intensity(refl1); - i2 = scale * get_intensity(refl2); + i2 = get_intensity(refl2); sig1 = get_esd_intensity(refl1); - sig2 = scale * get_esd_intensity(refl2); + sig2 = get_esd_intensity(refl2); if ( (fom == FOM_CCANO) || (fom == FOM_CRDANO) || (fom == FOM_RANO) || (fom == FOM_RANORSPLIT) ) @@ -754,7 +857,7 @@ static void do_fom(RefList *list1, RefList *list2, UnitCell *cell, assert(refl2_bij != NULL); i1bij = get_intensity(refl1_bij); - i2bij = scale * get_intensity(refl2_bij); + i2bij = get_intensity(refl2_bij); } else { diff --git a/src/diffraction.c b/src/diffraction.c index 1fb63ea3..5a936809 100644 --- a/src/diffraction.c +++ b/src/diffraction.c @@ -423,6 +423,40 @@ static void diffraction_at_k(struct image *image, const double *intensities, } +static void normalise_sampled_spectrum(struct sample *spec, int n, int nsamp) +{ + int i; + double total_w = 0.0; + double samp_w = 0.0; + + for ( i=0; i<n; i++ ) { + total_w += spec[i].weight; + } + STATUS("%i energies in spectrum, %i samples requested.\n", n, nsamp); + + for ( i=0; i<nsamp; i++ ) { + samp_w += spec[i].weight; + } + + if ( samp_w < 0.8 * total_w ) { + ERROR("WARNING: Only %.1f %% of the calculated spectrum " + "intensity was sampled.\n", 100.0 * samp_w / total_w); + ERROR("I've increased the weighting of the sampled points to " + "keep the final intensity constant, but the spectrum " + "might be very far from accurate. Consider increasing " + "the number of spectrum samples.\n"); + } else { + STATUS("%.1f %% of calculated spectrum intensity sampled.\n", + 100.0 * samp_w / total_w); + STATUS("Normalised to keep total intensity constant.\n"); + } + + for ( i=0; i<n; i++ ) { + spec[i].weight /= samp_w; + } +} + + static int compare_samples(const void *a, const void *b) { struct sample *sample1 = (struct sample *)a; @@ -548,6 +582,7 @@ struct sample *generate_tophat(struct image *image) } image->spectrum_size = image->nsamples; + /* No need to call normalise_sampled_spectrum() in this case */ return spectrum; } @@ -556,7 +591,6 @@ struct sample *generate_tophat(struct image *image) struct sample *generate_SASE(struct image *image, gsl_rng *rng) { struct sample *spectrum; - int i; const int spec_size = 1024; double eV_cen; /* Central photon energy for this spectrum */ const double jitter_sigma_eV = 8.0; @@ -581,21 +615,13 @@ struct sample *generate_SASE(struct image *image, gsl_rng *rng) /* Add SASE-type noise to Gaussian spectrum */ add_sase_noise(spectrum, spec_size, rng); - /* Normalise intensity (before taking restricted number of samples) */ - double total_weight = 0.0; - for ( i=0; i<spec_size; i++ ) { - total_weight += spectrum[i].weight; - } - for ( i=0; i<spec_size; i++ ) { - spectrum[i].weight /= total_weight; - } - /* Sort samples in spectrum by weight. Diffraction calculation will * take the requested number, starting from the brightest */ qsort(spectrum, spec_size, sizeof(struct sample), compare_samples); - image->spectrum_size = spec_size; + normalise_sampled_spectrum(spectrum, spec_size, image->nsamples); + image->spectrum_size = spec_size; return spectrum; } @@ -662,8 +688,9 @@ struct sample *generate_twocolour(struct image *image) * take the requested number, starting from the brightest */ qsort(spectrum, spec_size, sizeof(struct sample), compare_samples); - image->spectrum_size = spec_size; + normalise_sampled_spectrum(spectrum, spec_size, image->nsamples); + image->spectrum_size = spec_size; return spectrum; } diff --git a/src/dw-hdfsee.c b/src/dw-hdfsee.c index e92dcd1e..a899acc5 100644 --- a/src/dw-hdfsee.c +++ b/src/dw-hdfsee.c @@ -144,7 +144,7 @@ static void draw_panel_rectangle(cairo_t *cr, cairo_matrix_t *basic_m, } -int render_adsc_uint16(DisplayWindow *dw, const char *filename) +static int render_adsc_uint16(DisplayWindow *dw, const char *filename) { int x, y, fs, ss; double dfs, dss; @@ -853,9 +853,9 @@ static gint displaywindow_set_boostint(GtkWidget *widget, DisplayWindow *dw) return 0; } - if ( dw->hdfile == NULL ) { - return 0; - } + if ( dw->hdfile == NULL ) { + return 0; + } bd = malloc(sizeof(BoostIntDialog)); if ( bd == NULL ) return 0; @@ -904,6 +904,207 @@ static gint displaywindow_set_boostint(GtkWidget *widget, DisplayWindow *dw) } +static void do_filters(DisplayWindow *dw) +{ + if ( dw->median_filter > 0 ) { + filter_median(dw->image, dw->median_filter); + } + + if ( dw->noisefilter ) { + filter_noise(dw->image); + } +} + + +static gint displaywindow_newevent(DisplayWindow *dw, int new_event) +{ + int fail; + int i; + char title[1024]; + char *bn; + + if ( dw->not_ready_yet ) return 0; + + float *old_data = dw->image->data; + uint16_t *old_flags = dw->image->flags; + float **old_dp = dw->image->dp; + int **old_bad = dw->image->bad; + + fail = hdf5_read2(dw->hdfile, dw->image, + dw->ev_list->events[new_event], 0); + if ( fail ) { + ERROR("Couldn't load image"); + dw->image->data = old_data; + dw->image->flags = old_flags; + dw->image->dp = old_dp; + dw->image->bad = old_bad; + return 1; + } + + dw->curr_event = new_event; + + do_filters(dw); + displaywindow_update(dw); + + bn = safe_basename(dw->image->filename); + sprintf(title, "%s - event: %s - hdfsee", bn, + get_event_string(dw->ev_list->events[dw->curr_event])); + gtk_window_set_title(GTK_WINDOW(dw->window), title); + free(bn); + + for (i = 0; i < dw->image->det->n_panels; i++) { + free(old_dp[i]); + free(old_bad[i]); + } + free(old_data); + free(old_flags); + free(old_dp); + free(old_bad); + return 0; +} + + +static gint displaywindow_randomevent(GtkWidget *widget, DisplayWindow *dw) +{ + int rand_event; + + if ( dw->not_ready_yet ) return 0; + + rand_event = gsl_rng_uniform_int(&dw->rng, dw->ev_list->num_events); + + return displaywindow_newevent(dw, rand_event); +} + + +static gint displaywindow_set_newevent_response(GtkWidget *widget, + gint response, + DisplayWindow *dw) +{ + int ei; + int matched_event; + int done = 1; + + if ( response == GTK_RESPONSE_OK ) { + + const char *sevent; + + + + sevent = gtk_entry_get_text( + GTK_ENTRY(dw->event_dialog->entry)); + + matched_event = -1; + + for ( ei=0; ei<dw->ev_list->num_events; ei++ ) { + + char *ei_ev_string; + + ei_ev_string = get_event_string(dw->ev_list->events[ei]); + if ( strcmp(ei_ev_string, sevent) == 0 ) { + matched_event = ei; + break; + } + } + + if ( matched_event == -1 ) { + displaywindow_error(dw, "Cannot find event.\n"); + done = 0; + } else { + displaywindow_newevent(dw, matched_event); + displaywindow_update(dw); + } + } + + if ( done ) { + gtk_widget_destroy(dw->event_dialog->window); + } + + return 0; +} + + +static gint displaywindow_set_newevent_destroy(GtkWidget *widget, + DisplayWindow *dw) +{ + free(dw->event_dialog); + dw->event_dialog = NULL; + return 0; +} + + +static gint displaywindow_set_newevent_response_ac(GtkWidget *widget, + DisplayWindow *dw) +{ + return displaywindow_set_newevent_response(widget, GTK_RESPONSE_OK, dw); +} + + +/* Create a window to ask the user for a new intensity boost factor */ +static gint displaywindow_set_newevent(GtkWidget *widget, DisplayWindow *dw) +{ + EventDialog *ed; + GtkWidget *vbox; + GtkWidget *hbox; + GtkWidget *table; + GtkWidget *label; + char tmp[1024]; + + if ( dw->event_dialog != NULL ) { + return 0; + } + + if ( dw->hdfile == NULL ) { + return 0; + } + + ed = malloc(sizeof(EventDialog)); + if ( ed == NULL ) return 0; + dw->event_dialog = ed; + + ed->window = gtk_dialog_new_with_buttons("Go to event", + GTK_WINDOW(dw->window), + GTK_DIALOG_DESTROY_WITH_PARENT, + GTK_STOCK_CANCEL, GTK_RESPONSE_CLOSE, + GTK_STOCK_OK, GTK_RESPONSE_OK, NULL); + + vbox = gtk_vbox_new(FALSE, 0); + hbox = gtk_hbox_new(TRUE, 0); + gtk_box_pack_start(GTK_BOX(GTK_DIALOG(ed->window)->vbox), + GTK_WIDGET(hbox), FALSE, FALSE, 7); + gtk_box_pack_start(GTK_BOX(hbox), GTK_WIDGET(vbox), FALSE, FALSE, 5); + + table = gtk_table_new(3, 2, FALSE); + gtk_table_set_row_spacings(GTK_TABLE(table), 5); + gtk_table_set_col_spacings(GTK_TABLE(table), 5); + gtk_box_pack_start(GTK_BOX(vbox), GTK_WIDGET(table), FALSE, FALSE, 0); + + label = gtk_label_new("Event ID:"); + gtk_misc_set_alignment(GTK_MISC(label), 1, 0.5); + gtk_table_attach_defaults(GTK_TABLE(table), GTK_WIDGET(label), + 1, 2, 3, 4); + + ed->entry = gtk_entry_new(); + snprintf(tmp, 1023, "%s", + get_event_string(dw->ev_list->events[dw->curr_event])); + gtk_entry_set_text(GTK_ENTRY(ed->entry), tmp); + gtk_table_attach_defaults(GTK_TABLE(table), GTK_WIDGET(ed->entry), + 2, 3, 3, 4); + + g_signal_connect(G_OBJECT(ed->entry), "activate", + G_CALLBACK(displaywindow_set_newevent_response_ac), + dw); + g_signal_connect(G_OBJECT(ed->window), "response", + G_CALLBACK(displaywindow_set_newevent_response), dw); + g_signal_connect(G_OBJECT(ed->window), "destroy", + G_CALLBACK(displaywindow_set_newevent_destroy), dw); + gtk_window_set_resizable(GTK_WINDOW(ed->window), FALSE); + gtk_widget_show_all(ed->window); + gtk_widget_grab_focus(GTK_WIDGET(ed->entry)); + + return 0; +} + + static gint displaywindow_set_ringradius_response(GtkWidget *widget, gint response, DisplayWindow *dw) @@ -1140,6 +1341,23 @@ static void load_features_from_file(struct image *image, const char *filename) image_add_feature(image->features, fs, ss, image, 1.0, "peak"); + } else if ( r == 2 ) { + + p = find_orig_panel(image->det, fs, ss); + + if ( p == NULL ) { + ERROR("Unable to find panel " + "(no geometry file given?)\n"); + } else { + + /* Convert coordinates to match rearranged + * panels in memory */ + fs = fs - p->orig_min_fs + p->min_fs; + ss = ss - p->orig_min_ss + p->min_ss; + } + image_add_feature(image->features, fs, ss, image, 1.0, + "peak"); + } } while ( rval != NULL ); @@ -1191,13 +1409,15 @@ static gint displaywindow_about(GtkWidget *widget, DisplayWindow *dw) gtk_about_dialog_set_name(GTK_ABOUT_DIALOG(window), "hdfsee"); gtk_about_dialog_set_version(GTK_ABOUT_DIALOG(window), PACKAGE_VERSION); gtk_about_dialog_set_copyright(GTK_ABOUT_DIALOG(window), - "(c) 2012-2013 Thomas White <taw@physics.org> and others"); + "© 2012-2015 Deutsches Elektronen-Synchrotron DESY," + " a research centre of the Helmholtz Association."); gtk_about_dialog_set_comments(GTK_ABOUT_DIALOG(window), - "Quick viewer for HDF files"); + "Quick viewer for HDF files"); gtk_about_dialog_set_license(GTK_ABOUT_DIALOG(window), - "(c) 2012-2013 Thomas White <taw@physics.org>\n"); + "© 2012-2015 Deutsches Elektronen-Synchrotron DESY," + " a research centre of the Helmholtz Association."); gtk_about_dialog_set_website(GTK_ABOUT_DIALOG(window), - "http://www.desy.de/~twhite/crystfel"); + "http://www.desy.de/~twhite/crystfel"); gtk_about_dialog_set_authors(GTK_ABOUT_DIALOG(window), authors); g_signal_connect(window, "response", G_CALLBACK(gtk_widget_destroy), @@ -1209,7 +1429,7 @@ static gint displaywindow_about(GtkWidget *widget, DisplayWindow *dw) } -static int save_geometry_file(DisplayWindow *dw) +static int save_geometry_file(GtkWidget *widget, DisplayWindow *dw) { GtkWidget *d; gchar *output_filename; @@ -1227,9 +1447,11 @@ static int save_geometry_file(DisplayWindow *dw) w = write_detector_geometry_2(dw->geom_filename, output_filename, dw->image->det, "Manually optimized with " "hdfsee", 0); - if ( w != 0 ) { - ERROR("Error saving geometry!\n"); + if ( w != 0 && w!=2 ) { + displaywindow_error(dw, + "Unable to save the detector geometry."); } + gtk_widget_destroy(d); g_free(output_filename); return w; @@ -1256,6 +1478,54 @@ static gint displaywindow_peak_overlay(GtkWidget *widget, DisplayWindow *dw) } +static void set_calibration_menu_sensitivity(DisplayWindow *dw, int val) { + + GtkAction * a; + + a = gtk_ui_manager_get_action(dw->ui, + "/ui/displaywindow/calibration"); + gtk_action_set_sensitive(GTK_ACTION(a), val); + a = gtk_ui_manager_get_action(dw->ui, + "/ui/displaywindow/calibration/calibrationprevious"); + gtk_action_set_sensitive(GTK_ACTION(a), val); + a = gtk_ui_manager_get_action(dw->ui, + "/ui/displaywindow/calibration/calibrationnext"); + gtk_action_set_sensitive(GTK_ACTION(a), val); + a = gtk_ui_manager_get_action(dw->ui, + "/ui/displaywindow/calibration/switchcalibmode"); + gtk_action_set_sensitive(GTK_ACTION(a), val); + a = gtk_ui_manager_get_action(dw->ui, + "/ui/displaywindow/calibration/focus"); + gtk_action_set_sensitive(GTK_ACTION(a), val); + a = gtk_ui_manager_get_action(dw->ui, + "/ui/displaywindow/calibration/savegeometry"); + gtk_action_set_sensitive(GTK_ACTION(a), val); +} + + +static void set_events_menu_sensitivity(DisplayWindow *dw, int val) { + + GtkAction * a; + + a = gtk_ui_manager_get_action(dw->ui, + "/ui/displaywindow/events"); + gtk_action_set_sensitive(GTK_ACTION(a), val); + a = gtk_ui_manager_get_action(dw->ui, + "/ui/displaywindow/events/eventprevious"); + gtk_action_set_sensitive(GTK_ACTION(a), val); + a = gtk_ui_manager_get_action(dw->ui, + "/ui/displaywindow/events/eventnext"); + gtk_action_set_sensitive(GTK_ACTION(a), val); + a = gtk_ui_manager_get_action(dw->ui, + "/ui/displaywindow/events/gotoevent"); + gtk_action_set_sensitive(GTK_ACTION(a), val); + a = gtk_ui_manager_get_action(dw->ui, + "/ui/displaywindow/events/randomevent"); + gtk_action_set_sensitive(GTK_ACTION(a), val); + +} + + static gint displaywindow_set_calibmode(GtkWidget *d, DisplayWindow *dw) { GtkWidget *w, *vbox; @@ -1263,7 +1533,7 @@ static gint displaywindow_set_calibmode(GtkWidget *d, DisplayWindow *dw) w = gtk_ui_manager_get_widget(dw->ui, "/ui/displaywindow/tools/calibmode"); - if ( dw->image->det == dw->simple_geom ) { + if ( dw->simple ) { gtk_check_menu_item_set_state(GTK_CHECK_MENU_ITEM(w), 0); return 0; } @@ -1303,6 +1573,8 @@ static gint displaywindow_set_calibmode(GtkWidget *d, DisplayWindow *dw) dw->calib_mode_curr_p = dw->calib_mode_curr_rg->panels[0]; } + set_calibration_menu_sensitivity(dw, TRUE); + dw->calib_mode = CALIBMODE_PANELS; dw->statusbar = gtk_statusbar_new(); @@ -1318,6 +1590,8 @@ static gint displaywindow_set_calibmode(GtkWidget *d, DisplayWindow *dw) } else { + set_calibration_menu_sensitivity(dw, FALSE); + dw->calib_mode = CALIBMODE_NONE; gtk_widget_destroy(dw->statusbar); dw->statusbar = NULL; @@ -1694,6 +1968,207 @@ static gint displaywindow_setscale(GtkWidget *widget, GtkRadioAction *action, return 0; } +static int curr_rg_pointer_index(DisplayWindow *dw) +{ + int r; + + for ( r=0; r<dw->rg_coll->n_rigid_groups; ++r) { + if ( dw->rg_coll->rigid_groups[r] == dw->calib_mode_curr_rg ) { + return r; + } + } + + /* Never reached (we hope) */ + return 999; +} + +static int curr_p_pointer_index(DisplayWindow *dw) +{ + int p; + + for ( p=0; p<dw->image->det->n_panels; ++p) { + if ( &dw->image->det->panels[p] == dw->calib_mode_curr_p ) { + return p; + } + } + + /* Never reached (we hope) */ + return 999; +} + + +static void select_next_group(DisplayWindow *dw, int num_rg) +{ + if ( dw->calib_mode_curr_rg == dw->rg_coll->rigid_groups[num_rg-1] ) { + dw->calib_mode_curr_rg = dw->rg_coll->rigid_groups[0]; + } else { + dw->calib_mode_curr_rg = + dw->rg_coll->rigid_groups[curr_rg_pointer_index(dw)+1]; + } +} + + +static void select_prev_group(DisplayWindow *dw, int num_rg) +{ + if ( dw->calib_mode_curr_rg == dw->rg_coll->rigid_groups[0] ) { + dw->calib_mode_curr_rg = dw->rg_coll->rigid_groups[num_rg-1]; + } else { + dw->calib_mode_curr_rg = + dw->rg_coll->rigid_groups[curr_rg_pointer_index(dw)-1]; + } +} + + +static void select_next_panel(DisplayWindow *dw, int num_p) +{ + if ( dw->calib_mode_curr_p == &dw->image->det->panels[num_p-1] ) { + dw->calib_mode_curr_p = &dw->image->det->panels[0]; + } else { + dw->calib_mode_curr_p = + &dw->image->det->panels[curr_p_pointer_index(dw)+1]; + } +} + + +static void select_prev_panel(DisplayWindow *dw, int num_p) +{ + if ( dw->calib_mode_curr_p == &dw->image->det->panels[0] ) { + dw->calib_mode_curr_p = &dw->image->det->panels[num_p-1]; + } else { + dw->calib_mode_curr_p = + &dw->image->det->panels[curr_p_pointer_index(dw)-1]; + } +} + + +static void toggle_calibmode_groupmode(GtkWidget *widget, DisplayWindow *dw) +{ + struct rigid_group *rg; + struct detector *det = dw->image->det; + + switch ( dw->calib_mode ) { + + case CALIBMODE_NONE: + break; + + case CALIBMODE_PANELS: + if ( det->n_rigid_groups != det->n_panels ) { + /* Only change if there are any rigid groups defined */ + dw->calib_mode = CALIBMODE_GROUPS; + rg = find_corresponding_rigid_group(dw, + dw->calib_mode_curr_p); + if ( rg == NULL) { + dw->calib_mode = CALIBMODE_ALL; + } else { + dw->calib_mode_curr_rg = rg; + } + + } else { + /* ...otherwise skip to ALL mode */ + dw->calib_mode = CALIBMODE_ALL; + } + break; + + case CALIBMODE_GROUPS: + dw->calib_mode = CALIBMODE_ALL; + break; + + case CALIBMODE_ALL: + dw->calib_mode = CALIBMODE_PANELS; + dw->calib_mode_curr_p = dw->calib_mode_curr_rg->panels[0]; + break; + + } + redraw_window(dw); +} + + +static void toggle_calibmode_focus(GtkWidget *widget, DisplayWindow *dw) +{ + dw->calib_mode_show_focus = 1 - dw->calib_mode_show_focus; + redraw_window(dw); +} + + +static void calibmode_next(GtkWidget *widget, DisplayWindow *dw) +{ + int n; + + switch ( dw->calib_mode ) { + + case CALIBMODE_NONE: + break; + + case CALIBMODE_PANELS: + n = dw->image->det->n_panels; + select_next_panel(dw, n); + break; + + case CALIBMODE_GROUPS: + n = dw->image->det->n_rigid_groups; + select_next_group(dw, n); + break; + + case CALIBMODE_ALL: + break; + + } + redraw_window(dw); +} + + +static void calibmode_prev(GtkWidget *widget, DisplayWindow *dw) +{ + int n; + + switch ( dw->calib_mode ) { + + case CALIBMODE_NONE: + break; + + case CALIBMODE_PANELS: + n = dw->image->det->n_panels; + select_prev_panel(dw, n); + break; + + case CALIBMODE_GROUPS: + n = dw->image->det->n_rigid_groups; + select_prev_group(dw, n); + break; + + case CALIBMODE_ALL: + break; + + } + redraw_window(dw); +} + + +static void event_next(GtkWidget *widget, DisplayWindow *dw) +{ + int new_event; + + if ( dw->curr_event == dw->ev_list->num_events-1 ) { + new_event = 0; + } else { + new_event = dw->curr_event+1; + } + displaywindow_newevent(dw, new_event); +} + + +static void event_prev(GtkWidget *widget, DisplayWindow *dw) +{ + int new_event; + + if ( dw->curr_event == 0 ) { + new_event = dw->ev_list->num_events-1; + } else { + new_event = dw->curr_event-1; + } + displaywindow_newevent(dw, new_event); +} + static void displaywindow_addmenubar(DisplayWindow *dw, GtkWidget *vbox, int colscale) @@ -1722,7 +2197,27 @@ static void displaywindow_addmenubar(DisplayWindow *dw, GtkWidget *vbox, { "PeaksAction", NULL, "Load Feature List...", NULL, NULL, G_CALLBACK(displaywindow_peak_overlay) }, - { "EventsAction", NULL, "_Events", NULL, NULL, NULL }, + { "CalibrationAction", NULL, "_Calibration", NULL, NULL, NULL }, + { "CalibPreviousAction", NULL, "Previous Item", "minus", NULL, + G_CALLBACK(calibmode_prev) }, + { "CalibNextAction", NULL, "Next Item", "plus", NULL, + G_CALLBACK(calibmode_next) }, + { "SwitchCalibModeAction", NULL, "Toggle Panel/Group/All", "g", + NULL, G_CALLBACK(toggle_calibmode_groupmode) }, + { "ToggleFocusAction", NULL, "Toggle Focus Rectangle", "i", + NULL, G_CALLBACK(toggle_calibmode_focus) }, + { "SaveGeometryAction", NULL, "Save Geometry", "s", NULL, + G_CALLBACK(save_geometry_file) }, + + { "EventsAction", NULL, "_Event", NULL, NULL, NULL }, + { "EventPreviousAction", NULL, "Previous", "p", NULL, + G_CALLBACK(event_prev) }, + { "EventNextAction", NULL, "Next", "n", NULL, + G_CALLBACK(event_next) }, + { "GotoEventAction", NULL, "Go To Event", "e", NULL, + G_CALLBACK(displaywindow_set_newevent) }, + { "RandomEventAction", NULL, "Go To Random Event", "r", NULL, + G_CALLBACK(displaywindow_randomevent) }, { "HelpAction", NULL, "_Help", NULL, NULL, NULL }, { "AboutAction", GTK_STOCK_ABOUT, "_About hdfsee...", @@ -1779,17 +2274,6 @@ static void displaywindow_addmenubar(DisplayWindow *dw, GtkWidget *vbox, } -static void do_filters(DisplayWindow *dw) -{ - if ( dw->median_filter > 0 ) { - filter_median(dw->image, dw->median_filter); - } - - if ( dw->noisefilter ) { - filter_noise(dw->image); - } -} - struct newhdf { DisplayWindow *dw; GtkWidget *widget; @@ -1806,15 +2290,16 @@ static gint displaywindow_newhdf(GtkMenuItem *item, struct newhdf *nh) a = gtk_check_menu_item_get_active(GTK_CHECK_MENU_ITEM(nh->widget)); if ( !a ) return 0; + /* hdf5_read() will create a new simple geom, so get rid of the old + * one */ + free_detector_geometry(nh->dw->image->det); + nh->dw->image->det = NULL; fail = hdf5_read(nh->dw->hdfile, nh->dw->image, nh->name, 0); if ( fail ) { ERROR("Couldn't load image"); return 1; } - nh->dw->simple_geom = simple_geometry(nh->dw->image); - nh->dw->image->det = nh->dw->simple_geom; - do_filters(nh->dw); displaywindow_update(nh->dw); return 0; @@ -1958,102 +2443,6 @@ static int displaywindow_update_menus(DisplayWindow *dw, const char *selectme) } -static gint displaywindow_newevent(GtkMenuItem *item, struct newev *ne) -{ - gboolean a; - int fail; - int i; - - if ( ne->dw->not_ready_yet ) return 0; - - a = gtk_check_menu_item_get_active(GTK_CHECK_MENU_ITEM(ne->widget)); - if ( !a ) return 0; - - float *old_data = ne->dw->image->data; - uint16_t *old_flags = ne->dw->image->flags; - float **old_dp = ne->dw->image->dp; - int **old_bad = ne->dw->image->bad; - - fail = hdf5_read2(ne->dw->hdfile, ne->dw->image, - ne->dw->ev_list->events[ne->new_ev], 0); - if ( fail ) { - ERROR("Couldn't load image"); - return 1; - } - - ne->dw->curr_event = ne->new_ev; - - do_filters(ne->dw); - displaywindow_update(ne->dw); - for (i = 0; i < ne->dw->image->det->n_panels; i++) { - free(old_dp[i]); - free(old_bad[i]); - } - free(old_data); - free(old_flags); - free(old_dp); - free(old_bad); - return 0; -} - - -static int displaywindow_update_event_menu(DisplayWindow *dw, - struct event_list *ev_list, - int curr_event) -{ - - int ei; - GtkWidget *w; - GtkWidget *ww; - GSList *grp = NULL; - - w = gtk_ui_manager_get_widget(dw->ui, "/ui/displaywindow/events"); - ww = gtk_menu_new(); - - for ( ei=0; ei<ev_list->num_events; ei++ ) { - - GtkWidget *www; - struct newev *ne; - char *ev_string; - - ev_string = get_event_string(ev_list->events[ei]); - www = gtk_radio_menu_item_new_with_label(grp, ev_string); - free(ev_string); - - ne = malloc(sizeof(struct newev)); - if ( ne != NULL ) { - - ne->widget = www; - ne->dw = dw; - ne->new_ev = ei; - - g_signal_connect(G_OBJECT(www), "toggled", - G_CALLBACK(displaywindow_newevent), ne); - - } - - if ( ei == dw->curr_event ) { - gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(www), - TRUE); - } else { - gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(www), - FALSE); - } - - gtk_menu_shell_append(GTK_MENU_SHELL(ww), www); - - grp = gtk_radio_menu_item_get_group(GTK_RADIO_MENU_ITEM(www)); - gtk_widget_show_all(www); - - } - - gtk_menu_item_set_submenu(GTK_MENU_ITEM(w), ww); - - return 0; -} - - - static gint displaywindow_release(GtkWidget *widget, GdkEventButton *event, DisplayWindow *dw) { @@ -2151,174 +2540,6 @@ static gint displaywindow_press(GtkWidget *widget, GdkEventButton *event, return 0; } - -static int curr_rg_pointer_index(DisplayWindow *dw) -{ - int r; - - for ( r=0; r<dw->rg_coll->n_rigid_groups; ++r) { - if ( dw->rg_coll->rigid_groups[r] == dw->calib_mode_curr_rg ) { - return r; - } - } - - /* Never reached (we hope) */ - return 999; -} - - -static int curr_p_pointer_index(DisplayWindow *dw) -{ - int p; - - for ( p=0; p<dw->image->det->n_panels; ++p) { - if ( &dw->image->det->panels[p] == dw->calib_mode_curr_p ) { - return p; - } - } - - /* Never reached (we hope) */ - return 999; -} - - -static void select_next_group(DisplayWindow *dw, int num_rg) -{ - if ( dw->calib_mode_curr_rg == dw->rg_coll->rigid_groups[num_rg-1] ) { - dw->calib_mode_curr_rg = dw->rg_coll->rigid_groups[0]; - } else { - dw->calib_mode_curr_rg = - dw->rg_coll->rigid_groups[curr_rg_pointer_index(dw)+1]; - } -} - - -static void select_prev_group(DisplayWindow *dw, int num_rg) -{ - if ( dw->calib_mode_curr_rg == dw->rg_coll->rigid_groups[0] ) { - dw->calib_mode_curr_rg = dw->rg_coll->rigid_groups[num_rg-1]; - } else { - dw->calib_mode_curr_rg = - dw->rg_coll->rigid_groups[curr_rg_pointer_index(dw)-1]; - } -} - - -static void select_next_panel(DisplayWindow *dw, int num_p) -{ - if ( dw->calib_mode_curr_p == &dw->image->det->panels[num_p-1] ) { - dw->calib_mode_curr_p = &dw->image->det->panels[0]; - } else { - dw->calib_mode_curr_p = - &dw->image->det->panels[curr_p_pointer_index(dw)+1]; - } -} - - -static void select_prev_panel(DisplayWindow *dw, int num_p) -{ - if ( dw->calib_mode_curr_p == &dw->image->det->panels[0] ) { - dw->calib_mode_curr_p = &dw->image->det->panels[num_p-1]; - } else { - dw->calib_mode_curr_p = - &dw->image->det->panels[curr_p_pointer_index(dw)-1]; - } -} - - -static void toggle_calibmode_groupmode(DisplayWindow *dw) -{ - struct rigid_group *rg; - struct detector *det = dw->image->det; - - switch ( dw->calib_mode ) { - - case CALIBMODE_NONE: - break; - - case CALIBMODE_PANELS: - if ( det->n_rigid_groups != det->n_panels ) { - /* Only change if there are any rigid groups defined */ - dw->calib_mode = CALIBMODE_GROUPS; - rg = find_corresponding_rigid_group(dw, - dw->calib_mode_curr_p); - if ( rg == NULL) { - dw->calib_mode = CALIBMODE_ALL; - } else { - dw->calib_mode_curr_rg = rg; - } - - } else { - /* ...otherwise skip to ALL mode */ - dw->calib_mode = CALIBMODE_ALL; - } - break; - - case CALIBMODE_GROUPS: - dw->calib_mode = CALIBMODE_ALL; - break; - - case CALIBMODE_ALL: - dw->calib_mode = CALIBMODE_PANELS; - dw->calib_mode_curr_p = dw->calib_mode_curr_rg->panels[0]; - break; - - } -} - - -static void calibmode_next(DisplayWindow *dw) -{ - int n; - - switch ( dw->calib_mode ) { - - case CALIBMODE_NONE: - break; - - case CALIBMODE_PANELS: - n = dw->image->det->n_panels; - select_next_panel(dw, n); - break; - - case CALIBMODE_GROUPS: - n = dw->image->det->n_rigid_groups; - select_next_group(dw, n); - break; - - case CALIBMODE_ALL: - break; - - } -} - - -static void calibmode_prev(DisplayWindow *dw) -{ - int n; - - switch ( dw->calib_mode ) { - - case CALIBMODE_NONE: - break; - - case CALIBMODE_PANELS: - n = dw->image->det->n_panels; - select_prev_panel(dw, n); - break; - - case CALIBMODE_GROUPS: - n = dw->image->det->n_rigid_groups; - select_prev_group(dw, n); - break; - - case CALIBMODE_ALL: - break; - - } -} - - static void calibmode_up(DisplayWindow *dw) { int pi; @@ -2438,7 +2659,6 @@ static void calibmode_right(DisplayWindow *dw) static gint displaywindow_keypress(GtkWidget *widget, GdkEventKey *event, DisplayWindow *dw) { - int s; if ( !dw->calib_mode ) { return 0; @@ -2470,41 +2690,40 @@ static gint displaywindow_keypress(GtkWidget *widget, GdkEventKey *event, redraw_window(dw); break; - case GDK_plus: case GDK_KP_Add: - calibmode_next(dw); - redraw_window(dw); + calibmode_next(NULL, dw); break; - case GDK_minus: case GDK_KP_Subtract: - calibmode_prev(dw); - redraw_window(dw); + calibmode_prev(NULL, dw); break; + } - case GDK_f: - dw->calib_mode_show_focus = 1 - dw->calib_mode_show_focus; - redraw_window(dw); - break; + return 0; +} - case GDK_g: - toggle_calibmode_groupmode(dw); - redraw_window(dw); - break; - case GDK_s: - s = save_geometry_file(dw); - if ( s != 0 ) { - if ( s != 2 ) { - displaywindow_error(dw, - "Unable to save the detector geometry."); - } - } - break; +static void impose_twod_geometry(DisplayWindow *dw, const char *twod_element) +{ + + int i; + + for ( i=0; i<dw->image->det->n_panels; i++ ) { + + struct panel *p; + + p = &dw->image->det->panels[i]; + if ( p->data != NULL ) free(p->data); + p->data = strdup(twod_element); + + if ( p->dim_structure ) free_dim_structure(p->dim_structure); + p->dim_structure = default_dim_structure(); } - return 0; + dw->image->det->path_dim = 0; + dw->image->det->dim_dim = 0; + } @@ -2521,11 +2740,10 @@ DisplayWindow *displaywindow_open(char *filename, char *geom_filename, int median_filter) { DisplayWindow *dw; - char *title; GtkWidget *vbox; int check; GtkWidget *w; - GtkWidget *ww; + char title[1024]; dw = calloc(1, sizeof(DisplayWindow)); if ( dw == NULL ) return NULL; @@ -2537,7 +2755,7 @@ DisplayWindow *displaywindow_open(char *filename, char *geom_filename, dw->boostint = 1; dw->motion_callback = 0; dw->numbers_window = NULL; - dw->simple_geom = NULL; + dw->simple = 0; dw->image = NULL; dw->show_rings = show_rings; dw->show_peaks = 0; @@ -2559,6 +2777,14 @@ DisplayWindow *displaywindow_open(char *filename, char *geom_filename, dw->statusbar = NULL; dw->multi_event = 0; dw->ev_list = NULL; + dw->rng = *gsl_rng_alloc(gsl_rng_mt19937); + FILE *fh; + unsigned long int seed; + fh = fopen("/dev/urandom", "r"); + fread(&seed, sizeof(seed), 1, fh); + fclose(fh); + gsl_rng_set(&dw->rng, seed); + if ( geom_filename != NULL ) { dw->geom_filename = strdup(geom_filename); } else { @@ -2583,6 +2809,11 @@ DisplayWindow *displaywindow_open(char *filename, char *geom_filename, return NULL; } + if ( dw->image->det != NULL && element != NULL ) { + impose_twod_geometry(dw, element); + dw->multi_event = 0; + } + if ( dw->image->det != NULL && ( dw->image->det->path_dim != 0 || dw->image->det->dim_dim != 0 )) { @@ -2633,6 +2864,7 @@ DisplayWindow *displaywindow_open(char *filename, char *geom_filename, } else { check = hdf5_read(dw->hdfile, dw->image, element, 0); + dw->simple = 1; } if ( check ) { ERROR("Couldn't load file\n"); @@ -2643,11 +2875,6 @@ DisplayWindow *displaywindow_open(char *filename, char *geom_filename, dw->image->filename = strdup(filename); - if ( dw->image->det == NULL ) { - dw->simple_geom = simple_geometry(dw->image); - dw->image->det = dw->simple_geom; - } - /* Filters need geometry */ do_filters(dw); @@ -2659,11 +2886,15 @@ DisplayWindow *displaywindow_open(char *filename, char *geom_filename, dw->window = gtk_window_new(GTK_WINDOW_TOPLEVEL); char *bn = safe_basename(filename); - title = malloc(strlen(bn)+14); - sprintf(title, "%s - hdfsee", bn); + if ( dw->multi_event == 0 ) { + sprintf(title, "%s - hdfsee", bn); + } else { + sprintf(title, "%s - event: %s - hdfsee", bn, + get_event_string(dw->ev_list->events[dw->curr_event])); + } + free(bn); gtk_window_set_title(GTK_WINDOW(dw->window), title); - free(title); g_signal_connect(G_OBJECT(dw->window), "destroy", G_CALLBACK(displaywindow_closed), dw); @@ -2690,17 +2921,16 @@ DisplayWindow *displaywindow_open(char *filename, char *geom_filename, w = gtk_ui_manager_get_widget(dw->ui, "/ui/displaywindow/view/images"); - if ( dw->image->det != dw->simple_geom ) { + if ( !dw->simple ) { gtk_widget_set_sensitive(GTK_WIDGET(w), FALSE); } - ww = gtk_ui_manager_get_widget(dw->ui, - "/ui/displaywindow/events"); - - if ( dw->image->det == dw->simple_geom || dw->multi_event == 0) { - gtk_widget_set_sensitive(GTK_WIDGET(ww), FALSE); + if ( dw->simple || dw->multi_event == 0) { + set_events_menu_sensitivity(dw, FALSE); } + set_calibration_menu_sensitivity(dw, FALSE); + displaywindow_update(dw); gtk_widget_add_events(GTK_WIDGET(dw->drawingarea), @@ -2719,13 +2949,8 @@ DisplayWindow *displaywindow_open(char *filename, char *geom_filename, g_signal_connect(GTK_OBJECT(dw->drawingarea), "key-press-event", G_CALLBACK(displaywindow_keypress), dw); - if ( dw->image->det == dw->simple_geom ) { + if ( dw->simple ) { displaywindow_update_menus(dw, element); - } else { - if ( dw->multi_event ) { - displaywindow_update_event_menu(dw, dw->ev_list, - dw->curr_event); - } } dw->not_ready_yet = 0; diff --git a/src/dw-hdfsee.h b/src/dw-hdfsee.h index 81108213..85c82ac7 100644 --- a/src/dw-hdfsee.h +++ b/src/dw-hdfsee.h @@ -59,6 +59,12 @@ typedef struct { } RingRadiusDialog; +typedef struct { + GtkWidget *window; + GtkWidget *entry; +} EventDialog; + + struct numberswindow { GtkWidget *window; GtkWidget *labels[17*17]; @@ -83,6 +89,9 @@ typedef struct { GtkWidget *scrollarea; GtkUIManager *ui; GtkActionGroup *action_group; + GtkActionGroup *calibration_action_group; + GtkActionGroup *events_action_group; + int n_pixbufs; GdkPixbuf **pixbufs; gulong motion_callback; @@ -90,7 +99,7 @@ typedef struct { int not_ready_yet; - struct detector *simple_geom; + int simple; struct hdfile *hdfile; struct image *image; @@ -102,6 +111,7 @@ typedef struct { BinningDialog *binning_dialog; BoostIntDialog *boostint_dialog; RingRadiusDialog *ringradius_dialog; + EventDialog *event_dialog; struct numberswindow *numbers_window; int width; @@ -136,6 +146,7 @@ typedef struct { struct event_list *ev_list; int curr_event; + gsl_rng rng; } DisplayWindow; diff --git a/src/geoptimiser.c b/src/geoptimiser.c index 002b7af6..ed62767f 100644 --- a/src/geoptimiser.c +++ b/src/geoptimiser.c @@ -1,15 +1,15 @@ /* * geoptimiser.c * - * Refines detector geometry + * Refine detector geometry * - * Copyright © 2014 Deutsches Elektronen-Synchrotron DESY, - * a research centre of the Helmholtz Association. + * Copyright © 2014-2015 Deutsches Elektronen-Synchrotron DESY, + * a research centre of the Helmholtz Association. * * Authors: - * 2014 Thomas White <taw@physics.org> - * Oleksandr Yefanov - * Valerio Mariani + * 2014-2015 Oleksandr Yefanov + * 2014-2015 Valerio Mariani + * 2014-2015 Thomas White <taw@physics.org> * * This file is part of CrystFEL. * @@ -27,7 +27,9 @@ * along with CrystFEL. If not, see <http://www.gnu.org/licenses/>. * */ - +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif #include <stdlib.h> #include <stdio.h> @@ -38,18 +40,31 @@ #include <time.h> #include <float.h> +#ifdef HAVE_CAIRO +#ifdef HAVE_GTK +#define HAVE_SAVE_TO_PNG 1 +#include <cairo.h> +#include <gdk/gdk.h> +#endif /* HAVE_CAIRO */ +#endif /* HAVE_GTK */ + #include <detector.h> #include <stream.h> #include <version.h> #include <crystal.h> #include <image.h> #include <utils.h> +#include <render.h> + +#include "hdfsee-render.h" struct imagefeature; static void show_help(const char *s) { - printf("Syntax: %s [options] input.stream\n\n", s); + printf("Syntax: %s -i input.stream -g input.geom -o refined.geom " + "-c connected_rgcollection -q quadrant_rgcollection [options]\n", + s); printf( "Refines detector geometry.\n" "\n" @@ -64,6 +79,7 @@ static void show_help(const char *s) " -q, --quadrants=<rg_coll> Rigid group collection for quadrants.\n" " -c, --connected=<rg_coll> Rigid group collection for connected\n" " ASICs.\n" +" --no-error-maps Do not generate error map PNGs.\n" " -x, --min-num-peaks-per-pixel=<num> Minimum number of peaks per pixel.\n" " Default: 3. \n" " -p, --min-num-peaks-per-panel=<num> Minimum number of peaks per pixel.\n" @@ -75,8 +91,8 @@ static void show_help(const char *s) " --no-stretch Do not optimize distance offset.\n" " Default: distance offset is optimized\n" " -m --max-peak-dist=<num> Maximum distance between predicted and\n" -" detected peaks\n" -" Default: 4.0.\n" +" detected peaks (in pixels)\n" +" Default: 4.0 pixels.\n" ); } @@ -220,6 +236,28 @@ static double compute_average_clen (struct detector *det, char **clen_from, } +static double extract_f_from_stuff(const char *field_name, + struct stuff_from_stream* stuff) +{ + int i; + + char field_name_plus_equal[256]; + snprintf(field_name_plus_equal, 256, "hdf5%s = ", field_name); + + for ( i=0; i<stuff->n_fields; i++ ) { + + if ( strncmp(stuff->fields[i], field_name_plus_equal, + strlen(field_name_plus_equal)) == 0 ) { + return atoi(stuff->fields[i]+ + strlen(field_name_plus_equal)); + } + } + + ERROR("Failed to recover camera length from stream file\n"); + return -1; +} + + static struct pattern_list *read_patterns_from_steam_file(const char *infile, struct detector *det) { @@ -284,7 +322,8 @@ static struct pattern_list *read_patterns_from_steam_file(const char *infile, struct pattern **patterns_new; patterns_new = realloc(pattern_list->patterns, - (max_patterns+1024)*sizeof(struct pattern *)); + (max_patterns+1024)* + sizeof(struct pattern *)); if ( patterns_new == NULL ) { ERROR("Failed to allocate " "memory for loaded patterns.\n"); @@ -299,7 +338,8 @@ static struct pattern_list *read_patterns_from_steam_file(const char *infile, patn = malloc(sizeof(struct pattern)); if ( patn == NULL ) { - ERROR("Failed to allocate memory for loaded patterns.\n"); + ERROR("Failed to allocate memory for loaded " + "patterns.\n"); free(pattern_list->patterns); free(pattern_list); return NULL; @@ -314,7 +354,7 @@ static struct pattern_list *read_patterns_from_steam_file(const char *infile, avg_clen = compute_average_clen(det, &clen_from, &offset); if ( avg_clen == -1 ) { avg_clen = extract_f_from_stuff(clen_from, - cur.stuff_from_stream)*1e-3; + cur.stuff_from_stream)*1e-3; avg_clen += offset; } @@ -331,12 +371,14 @@ static struct pattern_list *read_patterns_from_steam_file(const char *infile, UnitCell *cell; UnitCell **new_unit_cells; - cell = crystal_get_cell(cur.crystals[0]); + cell = crystal_get_cell(cur.crystals[i]); new_unit_cells = realloc(patn->unit_cells, - (patn->n_unit_cells+1)*sizeof(UnitCell *)); + (patn->n_unit_cells+1)* + sizeof(UnitCell *)); if ( new_unit_cells == NULL ) { - ERROR("Failed to allocate memory for loaded patterns.\n"); + ERROR("Failed to allocate memory for " + "loaded patterns.\n"); free(pattern_list->patterns); free(pattern_list); free(patn); @@ -347,7 +389,8 @@ static struct pattern_list *read_patterns_from_steam_file(const char *infile, patn->n_unit_cells++; patn->unit_cells = new_unit_cells; - crystal_reflist = crystal_get_reflections(cur.crystals[i]); + crystal_reflist = crystal_get_reflections( + cur.crystals[i]); for ( refl = first_refl(crystal_reflist, &iter); refl != NULL; @@ -368,7 +411,8 @@ static struct pattern_list *read_patterns_from_steam_file(const char *infile, pattern_list->n_patterns++; if ( pattern_list->n_patterns%1000 == 0 ) { - STATUS("Loaded %i indexed patterns from %i total patterns\n", + STATUS("Loaded %i indexed patterns from %i " + "total patterns.\n", pattern_list->n_patterns, ++n_chunks); } @@ -378,7 +422,7 @@ static struct pattern_list *read_patterns_from_steam_file(const char *infile, close_stream(st); - STATUS("Found %d indexed patterns in file %s (from a total of %d)\n", + STATUS("Found %d indexed patterns in file %s (from a total of %d).\n", pattern_list->n_patterns, infile, n_chunks ); return pattern_list; @@ -424,12 +468,13 @@ static void compute_avg_cell_parameters(struct pattern_list *pattern_list, for ( cri=0; cri<ptn->n_unit_cells; cri++ ) { - cell_get_parameters(ptn->unit_cells[cri], &cpar[0], // a - &cpar[1], // b - &cpar[2], // c - &cpar[3], // alpha - &cpar[4], // beta - &cpar[5]); // gamma + cell_get_parameters(ptn->unit_cells[cri], + &cpar[0], // a + &cpar[1], // b + &cpar[2], // c + &cpar[3], // alpha + &cpar[4], // beta + &cpar[5]); // gamma cpar[0] *= 1e9; cpar[1] *= 1e9; @@ -454,16 +499,16 @@ static void compute_avg_cell_parameters(struct pattern_list *pattern_list, } STATUS("Average cell coordinates:\n"); - STATUS("Average a, b, c (in A): %6.3f, %6.3f, %6.3f\n", + STATUS("Average a, b, c (in nm): %6.3f, %6.3f, %6.3f\n", avcp[0],avcp[1],avcp[2]); STATUS("Minimum -Maximum a, b, c:\n" "\t%6.3f - %6.3f,\n" "\t%6.3f - %6.3f,\n" "\t%6.3f - %6.3f\n", minc[0], maxc[0], minc[1], maxc[1], minc[2], maxc[2]); - STATUS("Average alpha,beta,gamma: %6.3f, %6.3f, %6.3f\n", + STATUS("Average alpha,beta,gamma in degrees: %6.3f, %6.3f, %6.3f\n", avcp[3], avcp[4], avcp[5]); - STATUS("Minimum - Maximum alpha,beta,gamma:\n" + STATUS("Minimum - Maximum alpha,beta,gamma in degrees:\n" "\t%5.2f - %5.2f,\n" "\t%5.2f - %5.2f,\n" "\t%5.2f - %5.2f\n", @@ -518,7 +563,8 @@ static double compute_clen_to_use(struct pattern_list *pattern_list, int found = 0; for ( i=0; i<num_clens; i++ ) { - if ( fabs(pattern_list->patterns[cp]->clen-clens[i])<0.0001 ) { + if ( fabs(pattern_list->patterns[cp]->clen-clens[i]) + <0.0001 ) { clens_population[i]++; lambdas[i] += pattern_list->patterns[cp]->lambda; found = 1; @@ -535,14 +581,14 @@ static double compute_clen_to_use(struct pattern_list *pattern_list, double *lambdas_new; clens_population_new = realloc(clens_population, - (max_clens+1024)*sizeof(int)); + (max_clens+1024)*sizeof(int)); clens_new = realloc(clens_population, - (max_clens+1024)*sizeof(double)); + (max_clens+1024)*sizeof(double)); lambdas_new = realloc(clens_population, - (max_clens+1024)*sizeof(double)); + (max_clens+1024)*sizeof(double)); - if ( clens_new == NULL || clens_population_new == NULL || - lambdas_new == NULL) { + if ( clens_new == NULL || clens_population_new == NULL + || lambdas_new == NULL) { ERROR("Failed to allocate memory for " "camera length list\n"); free(clens); @@ -570,7 +616,8 @@ static double compute_clen_to_use(struct pattern_list *pattern_list, } if ( num_clens == 1 ) { - STATUS("All patterns have the same camera length: %f\n", clens[0]); + STATUS("All patterns have the same camera length: %f m.\n", + clens[0]); } else { STATUS("%i different camera lengths were found for the input " "patterns:\n", num_clens); @@ -584,17 +631,20 @@ static double compute_clen_to_use(struct pattern_list *pattern_list, irecistep = 1/cqu.u; - min_braggp_dist = fmin(fmin(irecistep/avcp[0],irecistep/avcp[1]), + min_braggp_dist = fmin(fmin(irecistep/avcp[0], + irecistep/avcp[1]), irecistep/avcp[2]); - STATUS("Camera length %0.4f was found %i times.\n" - "Minimum inter-bragg peak distance (based on average cell " - "parameters): %0.1f pixels\n",clens[i], clens_population[i], + STATUS("Camera length %0.4f m was found %i times.\n" + "Minimum inter-bragg peak distance (based on " + "average cell parameters): %0.1f pixels.\n", + clens[i], clens_population[i], min_braggp_dist); if ( min_braggp_dist<1.2*max_peak_distance ) { - STATUS("WARNING: The distance between Bragg peaks is too " - "small: " - "%0.1f < 1.2*%0.1f\n", min_braggp_dist, - max_peak_distance); + STATUS("WARNING: The distance between Bragg " + "peaks is too small: " + "%0.1f < 1.2*%0.1f pixels.\n", + min_braggp_dist, + max_peak_distance); } if ( clens_population[i] > clens_population[best_clen] ) { best_clen = i; @@ -604,7 +654,7 @@ static double compute_clen_to_use(struct pattern_list *pattern_list, } if ( only_best_distance ) { - STATUS("Only %i patterns with CLEN=%0.4f will be used.\n", + STATUS("Only %i patterns with CLEN=%0.4f m will be used.\n", clens_population[best_clen], clen_to_use); } @@ -639,7 +689,8 @@ static double comp_median(double *arr, unsigned int n) return arr[median] ; } - /* Find median of low, middle and high items; swap into position low */ + // Find median of low, middle and high items; swap into position + // low middle = (low + high) / 2; if ( arr[middle]>arr[high] ) { A = arr[middle]; @@ -657,12 +708,14 @@ static double comp_median(double *arr, unsigned int n) arr[low] = A; } - /* Swap low item (now in position middle) into position (low+1) */ + // Swap low item (now in position middle) into position + // (low+1) A = arr[middle]; arr[middle] = arr[low+1]; arr[low+1] = A; - /* Nibble from each end towards middle, swapping items when stuck */ + // Nibble from each end towards middle, swapping items when + // stuck ll = low + 1; hh = high; while (1) { @@ -738,36 +791,150 @@ static void free_all_curr_pix_displ(struct single_pix_displ *all_pix_displ, } +static int fill_pixel_statistics(int *num_pix_displ, + struct single_pix_displ** curr_pix_displ, + struct single_pix_displ* all_pix_displ, + struct connected_data *conn_data, + int ifs, int iss, int di, int aw, int dfv, + double *displ_x, + double *displ_y, double *displ_abs) +{ + double *cPxAfs; + double *cPxAss; + int cnu = 0; + + cPxAfs = calloc(num_pix_displ[ifs+aw*iss], sizeof(double)); + if ( cPxAfs == NULL ) { + ERROR("Failed to allocate memory for pixel statistics.\n"); + return 1; + } + cPxAss = calloc(num_pix_displ[ifs+aw*iss], sizeof(double)); + if ( cPxAss == NULL ) { + ERROR("Failed to allocate memory for pixel statistics.\n"); + free(cPxAfs); + return 1; + } + + curr_pix_displ[ifs+aw*iss] = &all_pix_displ[ifs+aw*iss]; + + while (1) { + if (curr_pix_displ[ifs+aw*iss]->dfs == dfv) break; + cPxAfs[cnu] = curr_pix_displ[ifs+aw*iss]->dfs; + cPxAss[cnu] = curr_pix_displ[ifs+aw*iss]->dss; + cnu++; + if ( curr_pix_displ[ifs+aw*iss]->ne == NULL ) break; + curr_pix_displ[ifs+aw*iss] = curr_pix_displ[ifs+aw*iss]->ne; + } + + if ( cnu < 1 ) return 0; + + displ_x[ifs+aw*iss] = comp_median(cPxAfs, cnu); + displ_y[ifs+aw*iss] = comp_median(cPxAss, cnu); + displ_abs[ifs+aw*iss] = modulus2d(displ_x[ifs+aw*iss], + displ_y[ifs+aw*iss]); + conn_data[di].n_peaks_in_conn++; + + free(cPxAfs); + free(cPxAss); + + return 0; +} + + + +static int compute_panel_statistics(struct rg_collection *connected, + int *num_pix_displ, + struct single_pix_displ** curr_pix_displ, + struct single_pix_displ* all_pix_displ, + struct connected_data *conn_data, + int di, int ip, int np, + double dfv, + int aw, double *displ_x, + double *displ_y, double *displ_abs) +{ + struct panel *p; + int ifs, iss; + + p = connected->rigid_groups[di]->panels[ip]; + + for ( ifs=p->min_fs; ifs<p->max_fs+1; ifs++ ) { + for ( iss=p->min_ss; iss<p->max_ss+1; iss++ ) { + if ( num_pix_displ[ifs+aw*iss]>=np ) { + + int ret; + + ret = fill_pixel_statistics(num_pix_displ, + curr_pix_displ, + all_pix_displ, + conn_data, + ifs, iss, di, aw, + dfv, displ_x, + displ_y, displ_abs); + + if ( ret == -2 ) { + break; + } else if ( ret != 0 ) { + return ret; + } + + } else { + displ_x[ifs+aw*iss] = dfv; + displ_y[ifs+aw*iss] = dfv; + displ_abs[ifs+aw*iss] = dfv; + } + + } + } + return 0; +} + + + +static int allocate_next_element(struct single_pix_displ** curr_pix_displ, + int ipx) +{ + curr_pix_displ[ipx]->ne = malloc(sizeof(struct single_pix_displ)); + if ( curr_pix_displ[ipx]->ne == NULL ) { + ERROR("Failed to allocate memory for pixel statistics.\n"); + return 1; + } + + curr_pix_displ[ipx] = curr_pix_displ[ipx]->ne; + + return 0; +} + + static int compute_pixel_statistics(struct pattern_list *pattern_list, - struct detector *det, - struct rg_collection *connected, - struct rg_collection *quadrants, - int num_pix_in_slab, - int max_peak_distance, int array_width, - double default_fill_value, - double min_num_peaks_per_pixel, - double min_num_peaks_per_panel, - int only_best_distance, - double clen_to_use, - double *slab_to_x, double *slab_to_y, - struct connected_data *conn_data, - double *displ_x, - double *displ_y, double *displ_abs, - struct single_pix_displ* all_pix_displ, - struct single_pix_displ** curr_pix_displ, - int *num_pix_displ) + struct detector *det, + struct rg_collection *connected, + struct rg_collection *quadrants, + int num_pix_in_slab, + int max_peak_distance, int aw, + double dfv, + double min_num_peaks_per_pixel, + double min_num_peaks_per_panel, + int only_best_distance, + double clen_to_use, + double *slab_to_x, double *slab_to_y, + struct connected_data *conn_data, + double *displ_x, + double *displ_y, double *displ_abs, + struct single_pix_displ* all_pix_displ, + struct single_pix_displ** curr_pix_displ, + int *num_pix_displ) { int ipx, cp, ich, di, ip, np; for (di=0; di<connected->n_rigid_groups; di++) { conn_data[di].num_quad = find_quad_for_connected( - connected->rigid_groups[di], - quadrants); + connected->rigid_groups[di], + quadrants); conn_data[di].cang = 0.0; conn_data[di].cstr = 1.0; - conn_data[di].sh_x = default_fill_value; - conn_data[di].sh_y = default_fill_value; + conn_data[di].sh_x = dfv; + conn_data[di].sh_y = dfv; conn_data[di].num_peaks_per_pixel = 1; conn_data[di].name = connected->rigid_groups[di]->name; conn_data[di].n_peaks_in_conn = 0; @@ -775,8 +942,8 @@ static int compute_pixel_statistics(struct pattern_list *pattern_list, for ( ipx=0; ipx<num_pix_in_slab; ipx++ ) { - all_pix_displ[ipx].dfs = default_fill_value; - all_pix_displ[ipx].dss = default_fill_value; + all_pix_displ[ipx].dfs = dfv; + all_pix_displ[ipx].dss = dfv; all_pix_displ[ipx].ne = NULL; curr_pix_displ[ipx] = &all_pix_displ[ipx]; num_pix_displ[ipx] = 0; @@ -787,7 +954,8 @@ static int compute_pixel_statistics(struct pattern_list *pattern_list, ImageFeatureList *flist = pattern_list->patterns[cp]->im_list; if ( only_best_distance ) { - if ( fabs(pattern_list->patterns[cp]->clen-clen_to_use)>0.0001 ) { + if ( fabs(pattern_list->patterns[cp]->clen-clen_to_use) > + 0.0001 ) { continue; } } @@ -807,25 +975,25 @@ static int compute_pixel_statistics(struct pattern_list *pattern_list, struct imagefeature *imfe = image_get_feature(flist, ich); compute_x_y(det, imfe->fs, imfe->ss, &fx, &fy); - refl = find_closest_reflection(rlist, fx, fy, det, &min_dist); + refl = find_closest_reflection(rlist, fx, fy, det, + &min_dist); if ( refl == NULL ) continue; - if ( min_dist<max_peak_distance ) { + if ( min_dist < max_peak_distance ) { - int ipx = ((int)rint(imfe->fs) + array_width* + int ipx = ((int)rint(imfe->fs) + aw* (int)rint(imfe->ss)); if ( num_pix_displ[ipx]>0 ) { - curr_pix_displ[ipx]->ne = malloc(sizeof( - struct single_pix_displ)); - if ( curr_pix_displ[ipx]->ne == NULL ) { - ERROR("Failed to allocate memory for pixel " - "statistics.\n"); - return 1; - } - curr_pix_displ[ipx] = curr_pix_displ[ipx]->ne; + int ret; + + ret = allocate_next_element(curr_pix_displ, + ipx); + + if ( ret != 0) return ret; + } get_detector_pos(refl, &rfs, &rss); @@ -845,78 +1013,29 @@ static int compute_pixel_statistics(struct pattern_list *pattern_list, continue; } - for (ip=0; ip<connected->rigid_groups[di]->n_panels; ip++) { + for (ip=0; ip<connected->rigid_groups[di]->n_panels; + ip++) { - struct panel *p; - int ifs, iss; + int ret; - p = connected->rigid_groups[di]->panels[ip]; + ret = compute_panel_statistics(connected, + num_pix_displ, + curr_pix_displ, + all_pix_displ, + conn_data, di, ip, + np, + dfv, + aw, + displ_x, + displ_y, + displ_abs); - for ( ifs=p->min_fs; ifs<p->max_fs+1; ifs++ ) { - for ( iss=p->min_ss; iss<p->max_ss+1; iss++ ) { - if ( num_pix_displ[ifs+array_width*iss]>=np ) { - - double *cPxAfs; - double *cPxAss; - int cnu = 0; - - cPxAfs = calloc(num_pix_displ[ifs+array_width*iss], - sizeof(double)); - if ( cPxAfs == NULL ) { - ERROR("Failed to allocate memory for " - "pixel statistics.\n"); - return 1; - } - cPxAss = calloc(num_pix_displ[ifs+array_width*iss], - sizeof(double)); - if ( cPxAss == NULL ) { - ERROR("Failed to allocate memory for " - "pixel statistics.\n"); - free(cPxAfs); - return 1; - } - - curr_pix_displ[ifs+array_width*iss] = - &all_pix_displ[ifs+array_width*iss]; - - while (1) { - if (curr_pix_displ[ifs+array_width*iss]->dfs - == default_fill_value) break; - cPxAfs[cnu] = - curr_pix_displ[ifs+array_width*iss]->dfs; - cPxAss[cnu] = - curr_pix_displ[ifs+array_width*iss]->dss; - cnu++; - if ( curr_pix_displ[ifs+array_width*iss]->ne == - NULL ) break; - curr_pix_displ[ifs+array_width*iss] = - curr_pix_displ[ifs+array_width*iss]->ne; - - } - - if ( cnu<1 ) continue; - - displ_x[ifs+array_width*iss] = - comp_median(cPxAfs, cnu); - displ_y[ifs+array_width*iss] = - comp_median(cPxAss, cnu); - displ_abs[ifs+array_width*iss] = modulus2d( - displ_x[ifs+array_width*iss], - displ_y[ifs+array_width*iss]); - conn_data[di].n_peaks_in_conn++; - - free(cPxAfs); - free(cPxAss); - - } else { - displ_x[ifs+array_width*iss] = default_fill_value; - displ_y[ifs+array_width*iss] = default_fill_value; - displ_abs[ifs+array_width*iss] = default_fill_value; - } - } - } + if ( ret !=0 ) return ret; } - if ( conn_data[di].n_peaks_in_conn>=min_num_peaks_per_panel ) { + + + if ( conn_data[di].n_peaks_in_conn >= + min_num_peaks_per_panel ) { conn_data[di].num_peaks_per_pixel = np; } } @@ -927,7 +1046,7 @@ static int compute_pixel_statistics(struct pattern_list *pattern_list, static double compute_error(struct rg_collection *connected, - int array_width, + int aw, struct connected_data *conn_data, int *num_pix_displ, double *displ_abs) @@ -949,13 +1068,13 @@ static double compute_error(struct rg_collection *connected, for (ifs=p->min_fs; ifs<p->max_fs+1; ifs++) { for (iss=p->min_ss; iss<p->max_ss+1; iss++) { - if ( num_pix_displ[ifs+array_width*iss]>= + if ( num_pix_displ[ifs+aw*iss]>= conn_data[di].num_peaks_per_pixel ) { double cer; - cer = displ_abs[ifs+array_width*iss]* - displ_abs[ifs+array_width*iss]; + cer = displ_abs[ifs+aw*iss]* + displ_abs[ifs+aw*iss]; connected_error += cer; num_connected_error++; total_error += cer; @@ -965,12 +1084,14 @@ static double compute_error(struct rg_collection *connected, } } - if ( num_connected_error>0 ) { + if ( num_connected_error > 0 ) { + connected_error /= (double)num_connected_error; connected_error = sqrt(connected_error); - STATUS("Error for connected group %s (%d peaks): " - "<delta^2> = %0.4f\n", conn_data[di].name, + STATUS("Error for connected group %s: %d pixels with " + "more than %d peaks: <delta^2> = %0.4f pixels.\n", + conn_data[di].name, num_connected_error, conn_data[di].num_peaks_per_pixel, connected_error); } @@ -987,7 +1108,7 @@ static double compute_error(struct rg_collection *connected, } -static void fill_coordinate_matrices(struct detector *det, int array_width, +static void fill_coordinate_matrices(struct detector *det, int aw, double *slab_to_x, double *slab_to_y) { int pi; @@ -1006,8 +1127,8 @@ static void fill_coordinate_matrices(struct detector *det, int array_width, compute_x_y(det, ifs, iss, &xs, &ys); - slab_to_x[iss*array_width+ifs] = xs; - slab_to_y[iss*array_width+ifs] = ys; + slab_to_x[iss*aw+ifs] = xs; + slab_to_y[iss*aw+ifs] = ys; } } @@ -1070,14 +1191,19 @@ static int correct_empty_panels(struct rg_collection *quadrants, if ( conn_data[di].n_peaks_in_conn<min_num_peaks_per_panel ) { if (aver_num_ang[conn_data[di].num_quad]>0) { - conn_data[di].cang = aver_ang[conn_data[di].num_quad]; - conn_data[di].cstr = aver_str[conn_data[di].num_quad]; - STATUS("Connected group %s has not enough peaks (%i). Using" - " average angle: %0.4f\n", conn_data[di].name, - conn_data[di].n_peaks_in_conn, conn_data[di].cang); + conn_data[di].cang = + aver_ang[conn_data[di].num_quad]; + conn_data[di].cstr = + aver_str[conn_data[di].num_quad]; + STATUS("Connected group %s has not enough peaks " + "(%i). Using average angle: %0.4f degrees\n", + conn_data[di].name, + conn_data[di].n_peaks_in_conn, + conn_data[di].cang); } else { - STATUS("Connected group %s has not enough peaks (%i). Left " - "unchanged\n", conn_data[di].name, + STATUS("Connected group %s has not enough peaks " + "(%i). Left unchanged\n", + conn_data[di].name, conn_data[di].n_peaks_in_conn); } } @@ -1109,15 +1235,19 @@ static void correct_angle_and_stretch(struct rg_collection *connected, p = connected->rigid_groups[di]->panels[ip]; newx = - p->fsx*cos(conn_data[di].cang)-p->fsy*sin(conn_data[di].cang); + p->fsx*cos(conn_data[di].cang)- + p->fsy*sin(conn_data[di].cang); newy = - p->fsx*sin(conn_data[di].cang)+p->fsy*cos(conn_data[di].cang); + p->fsx*sin(conn_data[di].cang)+ + p->fsy*cos(conn_data[di].cang); p->fsx = newx; p->fsy = newy; newx = - p->ssx*cos(conn_data[di].cang)-p->ssy*sin(conn_data[di].cang); + p->ssx*cos(conn_data[di].cang)- + p->ssy*sin(conn_data[di].cang); newy = - p->ssx*sin(conn_data[di].cang)+p->ssy*cos(conn_data[di].cang); + p->ssx*sin(conn_data[di].cang)+ + p->ssy*cos(conn_data[di].cang); p->ssx = newx; p->ssy = newy; } @@ -1131,8 +1261,8 @@ static void correct_angle_and_stretch(struct rg_collection *connected, for (pi=0; pi<det->n_panels; pi++) { det->panels[pi].coffset -= use_clen*(1.0-stretch_coeff); } - STATUS("Using a single offset distance for the whole detector: %f\n", - det->panels[0].coffset); + STATUS("Using a single offset distance for the whole detector: " + "%f m.\n", det->panels[0].coffset); for ( di=0; di<connected->n_rigid_groups; di++ ) { conn_data[di].cstr = stretch_coeff; @@ -1140,9 +1270,10 @@ static void correct_angle_and_stretch(struct rg_collection *connected, } else { - STATUS("Using individual distances for rigid panels\n"); + STATUS("Using individual distances for rigid panels.\n"); for ( di=0; di<connected->n_rigid_groups; di++ ) { - for ( ip=0; ip<connected->rigid_groups[di]->n_panels; ip++ ) { + for ( ip=0; ip<connected->rigid_groups[di]->n_panels; + ip++ ) { struct panel *p; @@ -1176,17 +1307,55 @@ static void shift_panels(struct rg_collection *connected, } else { struct panel *p0; - double connected_panel_dist; + double delta_x, delta_y; p0 = connected->rigid_groups[di]->panels[0]; - connected_panel_dist = modulus2d( - p->cnx-p0->cnx/conn_data[di].cstr, - p->cny-p0->cny/conn_data[di].cstr - ); + delta_x = (p->cnx-p0->cnx/conn_data[di].cstr); + delta_y = (p->cny-p0->cny/conn_data[di].cstr); - p->cnx = p0->cnx + connected_panel_dist*p0->fsx; - p->cny = p0->cny + connected_panel_dist*p0->fsy; + p->cnx = p0->cnx + delta_x * cos(conn_data[di].cang) + - delta_y * sin(conn_data[di].cang); + p->cny = p0->cny + delta_x * sin(conn_data[di].cang) + + delta_y * cos(conn_data[di].cang); + } + } + } +} + + +static void recompute_panel(struct connected_data *conn_data, int di, int ip, + struct rg_collection *connected, + double *slab_to_x, + double *slab_to_y, + double *recomputed_slab_to_x, + double *recomputed_slab_to_y, + double *displ_x, double *displ_y, + double stretch_coeff, int aw, int *num_pix_displ) +{ + + double c_stretch; + struct panel *p; + int ifs, iss; + + c_stretch = conn_data[di].cstr; + + if ( fabs(c_stretch)<FLT_EPSILON ) c_stretch = + stretch_coeff; + + p = connected->rigid_groups[di]->panels[ip]; + + for ( ifs=p->min_fs; ifs<p->max_fs+1; ifs++ ) { + for ( iss=p->min_ss; iss<p->max_ss+1; iss++ ) { + recomputed_slab_to_x[ifs+aw*iss] /= c_stretch; + recomputed_slab_to_y[ifs+aw*iss] /= c_stretch; + if ( num_pix_displ[ifs+aw*iss] >= + conn_data[di].num_peaks_per_pixel) { + + displ_x[ifs+aw*iss] -= (slab_to_x[ifs+aw*iss]- + recomputed_slab_to_x[ifs+aw*iss]); + displ_y[ifs+aw*iss] -= (slab_to_y[ifs+aw*iss]- + recomputed_slab_to_y[ifs+aw*iss]); } } } @@ -1198,7 +1367,7 @@ static void recompute_differences(struct rg_collection *connected, double *recomputed_slab_to_x, double *recomputed_slab_to_y, struct connected_data *conn_data, - int stretch_coeff, int array_width, + double stretch_coeff, int aw, double *displ_x, double *displ_y, int *num_pix_displ) { @@ -1208,45 +1377,76 @@ static void recompute_differences(struct rg_collection *connected, for ( di=0; di<connected->n_rigid_groups; di++ ) { for (ip=0; ip<connected->rigid_groups[di]->n_panels; ip++) { - double c_stretch; - struct panel *p; - int ifs, iss; - c_stretch = conn_data[di].cstr; + recompute_panel(conn_data, di, ip, connected, + slab_to_x, slab_to_y, + recomputed_slab_to_x, + recomputed_slab_to_y, + displ_x, displ_y, + stretch_coeff, aw, num_pix_displ); + } + } +} - if ( fabs(c_stretch)<FLT_EPSILON ) c_stretch = stretch_coeff; - p = connected->rigid_groups[di]->panels[ip]; +static void fill_av_in_panel(struct rg_collection *connected, int di, int ip, + struct connected_data *conn_data, + int *num_pix_displ, int aw, + double *av_in_panel_fs, + double *av_in_panel_ss, + double *displ_x, double *displ_y) +{ + struct panel *p; + int ifs, iss; - for ( ifs=p->min_fs; ifs<p->max_fs+1; ifs++ ) { - for ( iss=p->min_ss; iss<p->max_ss+1; iss++ ) { - recomputed_slab_to_x[ifs+array_width*iss] /= c_stretch; - recomputed_slab_to_y[ifs+array_width*iss] /= c_stretch; - if ( num_pix_displ[ifs+array_width*iss] >= - conn_data[di].num_peaks_per_pixel) { - - displ_x[ifs+array_width*iss] -= - (slab_to_x[ifs+array_width*iss]- - recomputed_slab_to_x[ifs+array_width*iss]); - displ_y[ifs+array_width*iss] -= - (slab_to_y[ifs+array_width*iss]- - recomputed_slab_to_y[ifs+array_width*iss]); - } - } + p = connected->rigid_groups[di]->panels[ip]; + + for ( ifs=p->min_fs; ifs<p->max_fs+1; ifs++ ) { + for ( iss=p->min_ss; iss<p->max_ss+1; iss++ ) { + + if (num_pix_displ[ifs+aw*iss]>= + conn_data[di].num_peaks_per_pixel) { + av_in_panel_fs[conn_data[di].n_peaks_in_conn] = + displ_x[ifs+aw*iss]; + av_in_panel_ss[conn_data[di].n_peaks_in_conn] = + displ_y[ifs+aw*iss]; + conn_data[di].n_peaks_in_conn++; } } } } +static void fill_con_data_sh(struct connected_data *conn_data, + double *av_in_panel_fs, + double *av_in_panel_ss, int di, + double max_peak_distance) +{ + conn_data[di].sh_x = comp_median(av_in_panel_fs, + conn_data[di].n_peaks_in_conn); + conn_data[di].sh_y = comp_median(av_in_panel_ss, + conn_data[di].n_peaks_in_conn); + STATUS("Panel %s, num pixels: %i, shifts (in pixels) X,Y: %0.8f, %0.8f\n", + conn_data[di].name, conn_data[di].n_peaks_in_conn, + conn_data[di].sh_x, conn_data[di].sh_y); + if ( modulus2d(conn_data[di].sh_x, conn_data[di].sh_y) > + 0.8*max_peak_distance ) { + STATUS(" WARNING: absolute shift is: %0.1f > 0.8*%0.1f pixels." + " Increase the value of the max_peak_distance parameter!\n", + modulus2d(conn_data[di].sh_x, conn_data[di].sh_y), + max_peak_distance); + } +} + + static int compute_shifts(struct rg_collection *connected, struct connected_data *conn_data, - int *num_pix_displ, int array_width, + int *num_pix_displ, int aw, int min_num_peaks_per_panel, - double default_fill_value, double max_peak_distance, - double *displ_x, double *displ_y ) + double dfv, double max_peak_distance, + double *displ_x, double *displ_y) { - STATUS("Median for panels\n"); + STATUS("Median for panels.\n"); int di, ip; @@ -1281,45 +1481,21 @@ static int compute_shifts(struct rg_collection *connected, for (ip=0; ip<connected->rigid_groups[di]->n_panels; ip++) { - struct panel *p; - int ifs, iss; + fill_av_in_panel(connected, di, ip, conn_data, + num_pix_displ, aw, av_in_panel_fs, + av_in_panel_ss, displ_x, displ_y); - p = connected->rigid_groups[di]->panels[ip]; - - for ( ifs=p->min_fs; ifs<p->max_fs+1; ifs++ ) { - for ( iss=p->min_ss; iss<p->max_ss+1; iss++ ) { - - if (num_pix_displ[ifs+array_width*iss]>= - conn_data[di].num_peaks_per_pixel) { - av_in_panel_fs[conn_data[di].n_peaks_in_conn] = - displ_x[ifs+array_width*iss]; - av_in_panel_ss[conn_data[di].n_peaks_in_conn] = - displ_y[ifs+array_width*iss]; - conn_data[di].n_peaks_in_conn++; - } - } - } } if ( conn_data[di].n_peaks_in_conn>=min_num_peaks_per_panel ) { - conn_data[di].sh_x = - comp_median(av_in_panel_fs, conn_data[di].n_peaks_in_conn); - conn_data[di].sh_y = - comp_median(av_in_panel_ss, conn_data[di].n_peaks_in_conn); - STATUS("Panel %s, num pixels: %i, shifts X,Y: %0.8f, %0.8f\n", - conn_data[di].name, conn_data[di].n_peaks_in_conn, - conn_data[di].sh_x, conn_data[di].sh_y); - if ( modulus2d(conn_data[di].sh_x, conn_data[di].sh_y)> - 0.8*max_peak_distance ) { - STATUS(" WARNING: absolute shift is: %0.1f > 0.8*%0.1f. " - "Increase the value of the max_peak_distance parameter!\n", - modulus2d(conn_data[di].sh_x, conn_data[di].sh_y), - max_peak_distance); - } + fill_con_data_sh(conn_data, av_in_panel_fs, + av_in_panel_ss, di, + max_peak_distance); + } else { - conn_data[di].sh_x = default_fill_value; - conn_data[di].sh_y = default_fill_value; + conn_data[di].sh_x = dfv; + conn_data[di].sh_y = dfv; } free(av_in_panel_fs); free(av_in_panel_ss); @@ -1389,13 +1565,16 @@ static int compute_shifts_for_empty_panels(struct rg_collection *quadrants, if ( num_aver[conn_data[di].num_quad]>0 ) { conn_data[di].sh_x = aver_x[conn_data[di].num_quad]; conn_data[di].sh_y = aver_y[conn_data[di].num_quad]; - STATUS("Panel %s has not enough (%i) peaks. Using average " - "shifts X,Y: %0.2f,%0.2f\n", conn_data[di].name, + STATUS("Panel %s has not enough (%i) peaks. " + "Using average shifts (in pixels) X,Y: " + "%0.2f,%0.2f\n", conn_data[di].name, conn_data[di].n_peaks_in_conn, conn_data[di].sh_x, conn_data[di].sh_y); } else { - STATUS("Panel %s has not enough (%i) peaks. Left unchanged\n", - conn_data[di].name, conn_data[di].n_peaks_in_conn); + STATUS("Panel %s has not enough (%i) peaks. " + "Left unchanged.\n", + conn_data[di].name, + conn_data[di].n_peaks_in_conn); } } } @@ -1410,7 +1589,7 @@ static int compute_shifts_for_empty_panels(struct rg_collection *quadrants, static void correct_shifts(struct rg_collection *connected, struct connected_data *conn_data, - double default_fill_value, double clen_to_use) + double dfv, double clen_to_use) { int di; @@ -1423,8 +1602,8 @@ static void correct_shifts(struct rg_collection *connected, p = connected->rigid_groups[di]->panels[ip]; - if ( conn_data[di].sh_x>default_fill_value+1.0 && - conn_data[di].sh_y > default_fill_value+1.0 ) { + if ( conn_data[di].sh_x>dfv+1.0 && + conn_data[di].sh_y > dfv+1.0 ) { p->cnx -= conn_data[di].sh_x; p->cny -= conn_data[di].sh_y; @@ -1438,15 +1617,176 @@ static void correct_shifts(struct rg_collection *connected, } -static int compute_angles_and_stretch - (struct rg_collection *connected, +static void a_s_counting_loop(int *num_pix_displ, int ifs, int iss, + int di, struct connected_data *conn_data, + int aw, double *slab_to_x, + double *slab_to_y, struct panel *p0, + struct panel *p1, double *displ_x, + double *displ_y, double minrad, + int *num_ang) +{ + + double coX, coY, cdX, cdY; + + if ( num_pix_displ[ifs+aw*iss]>= + conn_data[di].num_peaks_per_pixel ) { + + int ifs1, iss1; + int max_fs1_tmp = p0->max_fs; + int max_ss1_tmp = p0->max_ss; + + coX = slab_to_x[ifs+aw*iss]; + coY = slab_to_y[ifs+aw*iss]; + cdX = coX - displ_x[ifs+aw*iss]; + cdY = coY - displ_y[ifs+aw*iss]; + + for (ifs1=ifs+1; ifs1<max_fs1_tmp+1; ifs1++) { + + if ( ifs1 == max_fs1_tmp ) { + max_fs1_tmp = p1->max_fs; + } + + for (iss1=iss+1; iss1<max_ss1_tmp+1; iss1++) { + + if ( iss1 == max_ss1_tmp ) { + max_ss1_tmp = p1->max_ss; + } + + if ( num_pix_displ[ifs1+aw*iss1]>= + conn_data[di].num_peaks_per_pixel ) { + + double dist; + double coX1, coY1, cdX1, cdY1; + double len1, len2; + + dist = modulus2d(ifs-ifs1,iss-iss1); + if ( dist < minrad ) continue; + coX1 = slab_to_x[ifs1+aw*iss1]; + coY1 = slab_to_y[ifs1+aw*iss1]; + cdX1 = + coX1 - displ_x[ifs1+aw*iss1]; + cdY1 = + coY1 - displ_y[ifs1+aw*iss1]; + + len1 = modulus2d(coX1-coX, coY1-coY); + len2 = modulus2d(cdX1-cdX, cdY1-cdY); + if ( len1<FLT_EPSILON || + len2<FLT_EPSILON ) { + continue; + } + + *num_ang = *num_ang+1; + } + } + } + } +} + + +static int a_s_processing_loop(int *num_pix_displ, int ifs, int iss, + int di, struct connected_data *conn_data, + int aw, double *slab_to_x, + double *slab_to_y, struct panel *p0, + struct panel *p1, double *displ_x, + double *displ_y, double minrad, + int max_num_ang, int *num_ang, + double *angles, double *stretches) +{ + double coX, coY, cdX, cdY; + + if ( num_pix_displ[ifs+aw*iss]>= + conn_data[di].num_peaks_per_pixel ) { + + int ifs1, iss1; + int max_fs1_tmp = p0->max_fs; + int max_ss1_tmp = p0->max_ss; + + if ( *num_ang>=max_num_ang ) return -2; + + coX = slab_to_x[ifs+aw*iss]; + coY = slab_to_y[ifs+aw*iss]; + cdX = coX - displ_x[ifs+aw*iss]; + cdY = coY - displ_y[ifs+aw*iss]; + + for (ifs1=ifs+1; ifs1<max_fs1_tmp+1; ifs1++) { + + if ( ifs1 == max_fs1_tmp ) { + max_fs1_tmp = p1->max_fs; + } + + for (iss1=iss+1; iss1<max_ss1_tmp+1; iss1++) { + + if ( iss1 == max_ss1_tmp ) { + max_ss1_tmp = p1->max_ss; + } + + if ( num_pix_displ[ifs1+aw*iss1]>= + conn_data[di].num_peaks_per_pixel ) { + + double dist; + double coX1, coY1, cdX1, cdY1; + double len1, len2; + double scalM; + double multlen; + + if ( *num_ang>=max_num_ang ) return -2; + dist = modulus2d(ifs-ifs1,iss-iss1); + if (dist<minrad) return 0; + coX1 = slab_to_x[ifs1+aw*iss1]; + coY1 = slab_to_y[ifs1+aw*iss1]; + cdX1 = + coX1 - displ_x[ifs1+aw*iss1]; + cdY1 = + coY1 - displ_y[ifs1+aw*iss1]; + + len1 = modulus2d(coX1-coX, coY1-coY); + len2 = modulus2d(cdX1-cdX, cdY1-cdY); + scalM = (coX1-coX)*(cdX1-cdX)+ + (coY1-coY)*(cdY1-cdY)- + FLT_EPSILON; + if ( len1<FLT_EPSILON || + len2<FLT_EPSILON ) { + return 0; + } + + multlen = len1*len2; + if ( fabs(scalM)>=multlen ) { + angles[*num_ang] = 0.0; + } else { + + angles[*num_ang] = 1.0; + + angles[*num_ang] = + acos(scalM/multlen); + + if ((coY1-coY)*(cdX1-cdX)- + (coX1-coX)*(cdY1-cdY) < 0) { + angles[*num_ang] *= -1.; + } + + } + + stretches[*num_ang] = len1/len2; + + *num_ang = *num_ang+1; + } + } + } + } + return 0; +} + + + + +static int compute_angles_and_stretch(struct rg_collection *connected, struct connected_data *conn_data, int *num_pix_displ, double *slab_to_x, double *slab_to_y, double *displ_x, double *displ_y, - int array_width, + int aw, int min_num_peaks_per_panel, double dist_coeff_ang_str, int num_peaks_per_pixel, @@ -1461,18 +1801,21 @@ static int compute_angles_and_stretch csaa = malloc(sizeof(struct connected_stretch_and_angles)); if ( csaa == NULL ) { - ERROR("Failed to allocate memory to compute angles and stretch.\n"); + ERROR("Failed to allocate memory to compute angles and " + "stretch.\n"); return 1; } csaa->stretch_coeff = malloc(connected->n_rigid_groups*sizeof(double)); if ( csaa->stretch_coeff == NULL ) { - ERROR("Failed to allocate memory to compute angles and stretch.\n"); + ERROR("Failed to allocate memory to compute angles and " + "stretch.\n"); free(csaa); return 1; } csaa->num_angles = malloc(connected->n_rigid_groups*sizeof(unsigned int)); if ( csaa->num_angles == NULL ) { - ERROR("Failed to allocate memory to compute angles and stretch.\n"); + ERROR("Failed to allocate memory to compute angles and " + "stretch.\n"); free(csaa->stretch_coeff); free(csaa); return 1; @@ -1481,7 +1824,9 @@ static int compute_angles_and_stretch csaa->num_coeff=0; for ( di=0; di<connected->n_rigid_groups; di++ ) { - if ( conn_data[di].n_peaks_in_conn<min_num_peaks_per_panel ) continue; + if ( conn_data[di].n_peaks_in_conn<min_num_peaks_per_panel ) { + continue; + } unsigned int max_num_ang = 0; @@ -1509,9 +1854,11 @@ static int compute_angles_and_stretch struct panel *p0 = connected->rigid_groups[di]->panels[ip0]; - for ( ip1=0; ip1<connected->rigid_groups[di]->n_panels; ip1++ ) { + for ( ip1=0; ip1<connected->rigid_groups[di]->n_panels; + ip1++ ) { - struct panel *p1 = connected->rigid_groups [di]->panels[ip1]; + struct panel *p1 = + connected->rigid_groups [di]->panels[ip1]; int ifs, iss; int min_fs_tmp = p0->min_fs; @@ -1526,67 +1873,21 @@ static int compute_angles_and_stretch max_fs_tmp = p1->max_fs; } - for (iss=min_ss_tmp; iss<max_ss_tmp+1; iss++) { + for (iss=min_ss_tmp; iss<max_ss_tmp+1; + iss++) { if ( iss == max_ss_tmp ) { min_ss_tmp = p1->min_ss; max_ss_tmp = p1->max_ss; } - double coX, coY, cdX, cdY; - - if ( num_pix_displ[ifs+array_width*iss]>= - conn_data[di].num_peaks_per_pixel ) { - - int ifs1, iss1; - int max_fs1_tmp = p0->max_fs; - int max_ss1_tmp = p0->max_ss; - - coX = slab_to_x[ifs+array_width*iss]; - coY = slab_to_y[ifs+array_width*iss]; - cdX = coX - displ_x[ifs+array_width*iss]; - cdY = coY - displ_y[ifs+array_width*iss]; - - for (ifs1=ifs+1; ifs1<max_fs1_tmp+1; ifs1++) { - - if ( ifs1 == max_fs1_tmp ) { - max_fs1_tmp = p1->max_fs; - } - - for (iss1=iss+1; iss1<max_ss1_tmp+1; iss1++) { + a_s_counting_loop(num_pix_displ, + ifs, iss, di, conn_data, + aw, slab_to_x, slab_to_y, + p0, p1, displ_x, + displ_y, minrad, + &num_ang); - if ( iss1 == max_ss1_tmp ) { - max_ss1_tmp = p1->max_ss; - } - - if ( num_pix_displ[ifs1+array_width*iss1]>= - conn_data[di].num_peaks_per_pixel ) { - - double dist; - double coX1, coY1, cdX1, cdY1; - double len1, len2; - - dist = modulus2d(ifs-ifs1,iss-iss1); - if ( dist < minrad ) continue; - coX1 = slab_to_x[ifs1+array_width*iss1]; - coY1 = slab_to_y[ifs1+array_width*iss1]; - cdX1 = - coX1 - displ_x[ifs1+array_width*iss1]; - cdY1 = - coY1 - displ_y[ifs1+array_width*iss1]; - - len1 = modulus2d(coX1-coX, coY1-coY); - len2 = modulus2d(cdX1-cdX, cdY1-cdY); - if ( len1<FLT_EPSILON || - len2<FLT_EPSILON ) { - continue; - } - - num_ang++; - } - } - } - } } } } @@ -1598,7 +1899,8 @@ static int compute_angles_and_stretch angles = malloc(max_num_ang*sizeof(double)); if ( angles == NULL ) { - ERROR("Error in allocating memory for angle optimization\n"); + ERROR("Error in allocating memory for angle " + "optimization\n"); free(csaa->stretch_coeff); free(csaa->num_angles); free(csaa); @@ -1606,7 +1908,8 @@ static int compute_angles_and_stretch } stretches = malloc(max_num_ang*sizeof(double)); if ( stretches == NULL ) { - ERROR("Error in allocating memory for stretch optimization\n"); + ERROR("Error in allocating memory for stretch " + "optimization\n"); free(angles); free(csaa->stretch_coeff); free(csaa->num_angles); @@ -1620,9 +1923,11 @@ static int compute_angles_and_stretch struct panel *p0 = connected->rigid_groups[di]->panels[ip0]; - for ( ip1=0; ip1<connected->rigid_groups[di]->n_panels; ip1++ ) { + for ( ip1=0; ip1<connected->rigid_groups[di]->n_panels; + ip1++ ) { - struct panel *p1 = connected->rigid_groups [di]->panels[ip1]; + struct panel *p1 = + connected->rigid_groups [di]->panels[ip1]; int ifs, iss; int min_fs_tmp = p0->min_fs; @@ -1637,94 +1942,29 @@ static int compute_angles_and_stretch max_fs_tmp = p1->max_fs; } - for (iss=min_ss_tmp; iss<max_ss_tmp+1; iss++) { + for (iss=min_ss_tmp; iss<max_ss_tmp+1; + iss++) { + + int ret; if ( iss == max_ss_tmp ) { min_ss_tmp = p1->min_ss; max_ss_tmp = p1->max_ss; } - double coX, coY, cdX, cdY; - - if ( num_pix_displ[ifs+array_width*iss]>= - conn_data[di].num_peaks_per_pixel ) { - - int ifs1, iss1; - int max_fs1_tmp = p0->max_fs; - int max_ss1_tmp = p0->max_ss; - - if ( num_ang>=max_num_ang ) break; - - coX = slab_to_x[ifs+array_width*iss]; - coY = slab_to_y[ifs+array_width*iss]; - cdX = coX - displ_x[ifs+array_width*iss]; - cdY = coY - displ_y[ifs+array_width*iss]; - - for (ifs1=ifs+1; ifs1<max_fs1_tmp+1; ifs1++) { - - if ( ifs1 == max_fs1_tmp ) { - max_fs1_tmp = p1->max_fs; - } - - for (iss1=iss+1; iss1<max_ss1_tmp+1; iss1++) { - - if ( iss1 == max_ss1_tmp ) { - max_ss1_tmp = p1->max_ss; - } - - if ( num_pix_displ[ifs1+array_width*iss1]>= - conn_data[di].num_peaks_per_pixel ) { - - double dist; - double coX1, coY1, cdX1, cdY1; - double len1, len2; - double scalM; - double multlen; - - if ( num_ang>=max_num_ang ) break; - dist = modulus2d(ifs-ifs1,iss-iss1); - if (dist<minrad) continue; - coX1 = slab_to_x[ifs1+array_width*iss1]; - coY1 = slab_to_y[ifs1+array_width*iss1]; - cdX1 = - coX1 - displ_x[ifs1+array_width*iss1]; - cdY1 = - coY1 - displ_y[ifs1+array_width*iss1]; - - len1 = modulus2d(coX1-coX, coY1-coY); - len2 = modulus2d(cdX1-cdX, cdY1-cdY); - scalM = (coX1-coX)*(cdX1-cdX)+ - (coY1-coY)*(cdY1-cdY)- - FLT_EPSILON; - if ( len1<FLT_EPSILON || - len2<FLT_EPSILON ) { - continue; - } - - multlen = len1*len2; - if ( fabs(scalM)>=multlen ) { - angles[num_ang] = 0.0; - } else { - - angles[num_ang] = 1.0; - - angles[num_ang] = - acos(scalM/multlen); - - if ((coY1-coY)*(cdX1-cdX)- - (coX1-coX)*(cdY1-cdY) < 0) { - angles[num_ang] *= -1.; - } - - } - - stretches[num_ang] = len1/len2; - - num_ang++; - } - } - } - } + ret = a_s_processing_loop( + num_pix_displ, + ifs, iss, di, + conn_data, + aw, slab_to_x, + slab_to_y, + p0, p1, displ_x, + displ_y, minrad, + max_num_ang, + &num_ang, angles, + stretches); + + if ( ret == -2 ) break; } } } @@ -1734,7 +1974,8 @@ static int compute_angles_and_stretch conn_data[di].cang = -comp_median(angles,num_ang); conn_data[di].cstr = comp_median(stretches,num_ang); - STATUS("Panel %s, num: %i, angle: %0.4f, stretch: %0.4f\n", + STATUS("Panel %s, num: %i, angle: %0.4f deg, stretch coeff: " + "%0.4f\n", conn_data[di].name, num_ang, conn_data[di].cang, conn_data[di].cstr); @@ -1773,8 +2014,9 @@ static int compute_angles_and_stretch stretch_cf = 0; for ( di=0; di<num_coeff; di++ ) { if ( conn_data[di].num_peaks_per_pixel>=ipp ) { - stretch_cf += total_num*csaa->stretch_coeff[di]* - (double)csaa->num_angles[di]; + stretch_cf += + total_num*csaa->stretch_coeff[di]* + (double)csaa->num_angles[di]; } } break; @@ -1789,7 +2031,8 @@ static int compute_angles_and_stretch stretch_cf); if ( man_stretching_coeff>FLT_EPSILON ) { stretch_cf = man_stretching_coeff; - STATUS("Using manually set stretch coefficient: %0.4f\n", stretch_cf); + STATUS("Using manually set stretch coefficient: %0.4f\n", + stretch_cf); for ( di=0; di<connected->n_rigid_groups; di++ ) { conn_data[di].cstr = man_stretching_coeff; @@ -1807,83 +2050,371 @@ static int compute_angles_and_stretch } -static int save_data_to_hdf5(char * filename, struct detector* det, - int max_fs, int max_ss, double default_fill_value, - double *data) +#ifdef HAVE_SAVE_TO_PNG + +static void draw_panel(struct image *image, cairo_t *cr, + cairo_matrix_t *basic_m, GdkPixbuf **pixbufs, int i) { + struct panel p = image->det->panels[i]; + int w = gdk_pixbuf_get_width(pixbufs[i]); + int h = gdk_pixbuf_get_height(pixbufs[i]); + cairo_matrix_t m; + + /* Start with the basic coordinate system */ + cairo_set_matrix(cr, basic_m); + + /* Move to the right location */ + cairo_translate(cr, p.cnx, p.cny); + + /* Twiddle directions according to matrix */ + cairo_matrix_init(&m, p.fsx, p.fsy, p.ssx, p.ssy, 0.0, 0.0); + cairo_transform(cr, &m); + + gdk_cairo_set_source_pixbuf(cr, pixbufs[i], 0.0, 0.0); + cairo_rectangle(cr, 0.0, 0.0, w, h); +} + + +struct rectangle { - struct image *im; - int i; - int ret; + int width, height; + double min_x, min_y, max_x, max_y; +}; - im = malloc(sizeof(struct image)); - if ( im == NULL ) { - ERROR("Failed to allocate memory to save data.\n"); + +static int unpack_slab(struct image *image) +{ + struct detector *det = image->det; + int pi; + + image->dp = malloc(det->n_panels * sizeof(float *)); + image->bad = malloc(det->n_panels * sizeof(int *)); + if ( (image->dp == NULL) || (image->bad == NULL) ) { + ERROR("Failed to allocate panels.\n"); return 1; } - im->data = malloc((max_fs+1)*(max_ss+1)*sizeof(float)); - if ( im->data == NULL ) { + + for ( pi=0; pi<det->n_panels; pi++ ) { + + struct panel *p; + int fs, ss; + + p = &det->panels[pi]; + image->dp[pi] = malloc(p->w*p->h*sizeof(float)); + image->bad[pi] = calloc(p->w*p->h, sizeof(int)); + if ( (image->dp[pi] == NULL) || (image->bad[pi] == NULL) ) { + ERROR("Failed to allocate panel\n"); + return 1; + } + + for ( ss=0; ss<p->h; ss++ ) { + for ( fs=0; fs<p->w; fs++ ) { + + int idx; + int cfs, css; + + cfs = fs+p->min_fs; + css = ss+p->min_ss; + idx = cfs + css*image->width; + + image->dp[pi][fs+p->w*ss] = image->data[idx]; + image->bad[pi][fs+p->w*ss] = 0; + + } + } + } + + return 0; +} + + +static int draw_detector(cairo_surface_t *surf, struct image *image, + struct rectangle rect) +{ + cairo_t *cr; + cairo_matrix_t basic_m; + cairo_matrix_t m; + GdkPixbuf **pixbufs; + int n_pixbufs; + + cr = cairo_create(surf); + + unpack_slab(image); + pixbufs = render_panels(image, 1, SCALE_GEOPTIMISER, 1, &n_pixbufs); + + /* Blank grey background */ + cairo_rectangle(cr, 0.0, 0.0, rect.width, rect.height); + cairo_set_source_rgb(cr, 0.5, 0.5, 0.5); + cairo_fill(cr); + + /* Set up basic coordinate system + * - origin in the centre, y upwards. */ + cairo_identity_matrix(cr); + cairo_matrix_init(&m, 1.0, 0.0, 0.0, -1.0, 0.0, 0.0); + + + cairo_translate(cr, -rect.min_x , rect.max_y); + cairo_transform(cr, &m); + cairo_get_matrix(cr, &basic_m); + + if (pixbufs != NULL) { + + int i; + + for (i = 0; i < image->det->n_panels; i++) { + draw_panel(image, cr, &basic_m, pixbufs, i); + cairo_fill(cr); + } + + } + + /* Free old pixbufs */ + if (pixbufs != NULL) { + int i; + for (i = 0; i < n_pixbufs; i++) { + g_object_unref(pixbufs[i]); + } + free(pixbufs); + } + + return 0; + +} + + +static int save_data_to_png(char *filename, struct detector *det, + int max_fs, int max_ss, double default_fill_value, + double *data) +{ + struct image im; + int i; + struct rectangle rect; + GdkPixbuf *col_scale; + cairo_t *cr; + + cairo_status_t r; + cairo_surface_t *surf; + + im.data = malloc((max_fs+1)*(max_ss+1)*sizeof(float)); + if ( im.data == NULL ) { ERROR("Failed to allocate memory to save data.\n"); - free(im); return 1; } - im->det = det; - im->width = max_fs+1; - im->height = max_ss+1; - im->beam = NULL; - im->spectrum = NULL; + im.det = det; + im.width = max_fs+1; + im.height = max_ss+1; + im.flags = NULL; for ( i=0; i<(max_fs+1)*(max_ss+1); i++) { if ( data[i] == default_fill_value ) { - im->data[i] = 0.0; + im.data[i] = 0.0; + } else if ( data[i] > 1.0) { + im.data[i] = 1.0; } else { - im->data[i] = (float)data[i]; + im.data[i] = (float)data[i]; } + im.data[i] *= 10.0; /* render_panels sets this as max */ } - ret = hdf5_write_image(filename, im, NULL); + get_pixel_extents(im.det, &rect.min_x, &rect.min_y, &rect.max_x, + &rect.max_y); - if ( ret != 0 ) { - free(im->data); - free(im); + if (rect.min_x > 0.0) rect.min_x = 0.0; + if (rect.max_x < 0.0) rect.max_x = 0.0; + if (rect.min_y > 0.0) rect.min_y = 0.0; + if (rect.max_y < 0.0) rect.max_y = 0.0; + + rect.width = rect.max_x - rect.min_x; + rect.height = rect.max_y - rect.min_y; + + /* Add a thin border */ + rect.width += 2.0; + rect.height += 2.0; + surf = cairo_image_surface_create(CAIRO_FORMAT_ARGB32, rect.width + 20, + rect.height); + + draw_detector(surf, &im, rect); + + col_scale = render_get_colour_scale(20, rect.height, SCALE_GEOPTIMISER); + + cr = cairo_create(surf); + cairo_identity_matrix(cr); + cairo_translate(cr, rect.width, 0.0); + cairo_rectangle(cr, 0.0, 0.0, 20.0, rect.height); + gdk_cairo_set_source_pixbuf(cr, col_scale, 0.0, 0.0); + cairo_fill(cr); + cairo_destroy(cr); + + r = cairo_surface_write_to_png(surf, filename); + if (r != CAIRO_STATUS_SUCCESS) { + free(im.data); return 1; } - free(im->data); - free(im); + free(im.data); return 0; } +#endif /* HAVE_SAVE_TO_PNG */ + +static void calculate_panel_correction(int di, int ip, int aw, + int *num_pix_displ, + struct rg_collection *connected, + struct connected_data *conn_data) +{ + struct panel *p; + int ifs, iss; + + p = connected->rigid_groups[di]->panels[ip]; + for (ifs=p->min_fs; ifs<p->max_fs+1; ifs++) { + for (iss=p->min_ss; iss<p->max_ss+1; iss++) { + if ( num_pix_displ[ifs+aw*iss]>= + conn_data[di].num_peaks_per_pixel ) { + conn_data[di].n_peaks_in_conn++; + } + } + } + +} + + +static void compute_abs_displ(struct rg_collection *connected, + struct connected_data *conn_data, + int *num_pix_displ, + double dfv, int di, int ip, int aw, + double *displ_x, + double *displ_y, + double *displ_abs) +{ + struct panel *p; + int ifs, iss; + + if (conn_data[di].sh_x < dfv+1) return; + + p = connected->rigid_groups[di]->panels[ip]; + + for (ifs=p->min_fs; ifs<p->max_fs+1; ifs++) { + for (iss=p->min_ss; iss<p->max_ss+1; iss++) { + if ( num_pix_displ[ifs+aw*iss]>= + conn_data[di].num_peaks_per_pixel ) { + displ_x[ifs+aw*iss] -= conn_data[di].sh_x; + displ_y[ifs+aw*iss] -= conn_data[di].sh_y; + displ_abs[ifs+aw*iss] = modulus2d( + displ_x[ifs+aw*iss], + displ_y[ifs+aw*iss] + ); + } else { + displ_abs[ifs+aw*iss] = dfv; + } + } + } +} + + +int check_and_enforce_cspad_dist(struct detector *det, int enforce) +{ + int np = 0; + int num_errors_found = 0; + + double dist_to_check = 197.0; + double tol = 0.2; + + for ( np=0; np<det->n_panels; np = np+2 ) { + + double dist2; + + struct panel *ep = &det->panels[np]; + struct panel *op = &det->panels[np+1]; + + dist2 = (( ep->cnx - op->cnx )*( ep->cnx - op->cnx ) + + ( ep->cny - op->cny )*( ep->cny - op->cny )); + + if ( dist2 > (dist_to_check+tol)*(dist_to_check+tol) || + dist2 < (dist_to_check-tol)*(dist_to_check-tol) ) { + + num_errors_found += 1; + + STATUS("Warning: distance between panels %s and %s " + "is outside acceptable margins.\n", ep->name, + op->name); + + if ( enforce ) { + + double new_op_cx, new_op_cy; + + new_op_cx = ep->cnx + ep->fsx*dist_to_check; + new_op_cy = ep->cny + ep->fsy*dist_to_check; + + op->cnx = new_op_cx; + op->cny = new_op_cy; + + STATUS("Enforcing distance....\n"); + } + + } + + if ( ep->fsx != op->fsx || ep->ssx != op->ssx || + ep->fsy != op->fsy || ep->ssx != op->ssx ) { + + num_errors_found += 1; + + STATUS("Warning: relative orientation between panels " + "%s and %s is incorrect.\n", ep->name, op->name); + + if ( enforce ) { + + STATUS("Enforcing relative orientation....\n"); + + op->fsx = ep->fsx; + op->ssx = ep->ssx; + op->fsy = ep->fsy; + op->ssy = ep->ssy; + + op->xfs = ep->xfs; + op->xss = ep->xss; + op->yfs = ep->yfs; + op->yss = ep->yss; + } + + } + + } + return num_errors_found; +} + + int optimize_geometry(char *infile, char *outfile, char *geometry_filename, struct detector *det, struct rg_collection* quadrants, struct rg_collection* connected, int min_num_peaks_per_pixel, int min_num_peaks_per_panel, int only_best_distance, int nostretch, - int individual_coffset, double max_peak_dist, - const char *command_line) + int individual_coffset, int error_maps, + int enforce_cspad_layout, int no_cspad, + double max_peak_dist, const char *command_line) { int num_pix_in_slab; int max_fs = 0; int max_ss = 0; - int array_width = 0; + int aw = 0; int pi, di, ip, pti; - int ret1, ret2, ret3; - int ret4, ret5, ret6; + int ret1, ret2; int ret; int write_ret; + int maybe_cspad = 0; + + int *num_pix_displ; double res_sum; double istep; double clen_to_use; double man_stretching_coeff = 0.0; double avc[6] = {0.,0.,0.,0.,0.,0.}; - double default_fill_value = -10000.0; - double dist_coeff_ang_str = 0.2; // for angles and stretch calculation use - // only pixels which are distco*size_panel - // away - - int *num_pix_displ; + double dfv = -10000.0; + // for angles and stretch calculation use + // only pixels which are distco*size_panel + // away + double dist_coeff_ang_str = 0.2; double *displ_x; double *displ_y; double *displ_abs; @@ -1894,6 +2425,7 @@ int optimize_geometry(char *infile, char *outfile, char *geometry_filename, double* recomputed_slab_to_x; double* recomputed_slab_to_y; double stretch_coeff = 1; + struct single_pix_displ *all_pix_displ; struct single_pix_displ **curr_pix_displ; struct connected_data *conn_data = NULL; @@ -1901,13 +2433,62 @@ int optimize_geometry(char *infile, char *outfile, char *geometry_filename, if ( nostretch ) man_stretching_coeff = 1.0; - STATUS("Maximum distance between peaks: %0.1f\n", max_peak_dist); + STATUS("Maximum distance between peaks: %0.1f pixels.\n", max_peak_dist); STATUS("Minimum number of measurements for pixel to be included in the " "refinement: %i\n", min_num_peaks_per_pixel); - STATUS("Minimum number of measurements for panel for accurate estimation of" - " position/orientation: %i\n", min_num_peaks_per_panel); + STATUS("Minimum number of measurements for panel for accurate estimation " + "of position/orientation: %i\n", min_num_peaks_per_panel); + + if ( det->n_panels == 64 ) { + maybe_cspad = 1; + } + + if ( maybe_cspad && !no_cspad ) { + + int num_errors = 0; + + STATUS("It looks like the detector is a CSPAD. " + "Checking relative distance and orientation of " + "connected ASICS.\n"); + STATUS("If the detector is not a CSPAD, please rerun the " + "program with the --no-cspad option.\n"); + if ( enforce_cspad_layout ) { + STATUS("Enforcing CSPAD layout...\n"); + } + + num_errors = check_and_enforce_cspad_dist(det, + enforce_cspad_layout); + + if ( enforce_cspad_layout ) { + + int geom_wr; + + STATUS("Saving geometry with enforced CSPAD layout.\n" + "Please restart geometry optimization using the " + "optimized geometry from this run as input geometry " + "file.\n"); + geom_wr = write_detector_geometry_2(geometry_filename, + outfile, det, + command_line, 1); + if ( geom_wr != 0 ) { + ERROR("Error in writing output geometry file.\n"); + return 1; + } + STATUS("All done!\n"); + return 0; + } + + if ( !enforce_cspad_layout && num_errors > 0 ) { + ERROR("Relative distance and orientation of connected " + "ASICS do not respect the CSPAD layout.\n" + "Geometry optimization cannot continue.\n" + "Please rerun the program with the " + "--enforce-cspad-layout option.\n"); + return 1; + } + } pattern_list = read_patterns_from_steam_file(infile, det); if ( pattern_list->n_patterns < 1 ) { @@ -1931,7 +2512,7 @@ int optimize_geometry(char *infile, char *outfile, char *geometry_filename, istep = res_sum/det->n_panels; - array_width = max_fs+1; + aw = max_fs+1; clen_to_use = compute_clen_to_use(pattern_list, istep, avc, max_peak_dist, @@ -1978,9 +2559,10 @@ int optimize_geometry(char *infile, char *outfile, char *geometry_filename, return 1; } - fill_coordinate_matrices(det, array_width, slab_to_x, slab_to_y); + fill_coordinate_matrices(det, aw, slab_to_x, slab_to_y); - all_pix_displ = calloc(num_pix_in_slab, sizeof(struct single_pix_displ)); + all_pix_displ = calloc(num_pix_in_slab, + sizeof(struct single_pix_displ)); if ( all_pix_displ == NULL ) { ERROR("Error allocating memory for connected structure data.\n"); free(displ_x); @@ -1990,7 +2572,8 @@ int optimize_geometry(char *infile, char *outfile, char *geometry_filename, free(slab_to_y); return 1; } - curr_pix_displ = calloc(num_pix_in_slab, sizeof(struct single_pix_displ*)); + curr_pix_displ = calloc(num_pix_in_slab, + sizeof(struct single_pix_displ*)); if ( curr_pix_displ == NULL ) { ERROR("Error allocating memory for connected structure data.\n"); free(displ_x); @@ -2014,7 +2597,8 @@ int optimize_geometry(char *infile, char *outfile, char *geometry_filename, return 1; } - conn_data = malloc(connected->n_rigid_groups*sizeof(struct connected_data)); + conn_data = malloc(connected->n_rigid_groups* + sizeof(struct connected_data)); if ( conn_data == NULL ) { ERROR("Error allocating memory for connected structure data.\n"); free(displ_x); @@ -2028,14 +2612,17 @@ int optimize_geometry(char *infile, char *outfile, char *geometry_filename, return 1; } - STATUS("Computing pixel statistics\n"); + STATUS("Computing pixel statistics.\n"); ret = compute_pixel_statistics(pattern_list, det, connected, quadrants, num_pix_in_slab, max_peak_dist, - array_width, default_fill_value, + aw, dfv, min_num_peaks_per_pixel, - min_num_peaks_per_panel, only_best_distance, - clen_to_use, slab_to_x, slab_to_y, conn_data, - displ_x, displ_y, displ_abs, all_pix_displ, + min_num_peaks_per_panel, + only_best_distance, + clen_to_use, slab_to_x, + slab_to_y, conn_data, + displ_x, displ_y, displ_abs, + all_pix_displ, curr_pix_displ, num_pix_displ); if ( ret != 0 ) { @@ -2044,7 +2631,8 @@ int optimize_geometry(char *infile, char *outfile, char *geometry_filename, free(displ_abs); free(slab_to_x); free(slab_to_y); - free_all_curr_pix_displ(all_pix_displ, curr_pix_displ, num_pix_in_slab); + free_all_curr_pix_displ(all_pix_displ, curr_pix_displ, + num_pix_in_slab); free(num_pix_displ); free(conn_data); return 1; @@ -2056,7 +2644,8 @@ int optimize_geometry(char *infile, char *outfile, char *geometry_filename, image_feature_list_free(pattern_list->patterns[pti]->im_list); reflist_free(pattern_list->patterns[pti]->ref_list); - for ( nuc=0; nuc<pattern_list->patterns[pti]->n_unit_cells; nuc++) { + for ( nuc=0; nuc<pattern_list->patterns[pti]->n_unit_cells; + nuc++) { cell_free(pattern_list->patterns[pti]->unit_cells[nuc]); } free(pattern_list->patterns[pti]->filename); @@ -2064,31 +2653,41 @@ int optimize_geometry(char *infile, char *outfile, char *geometry_filename, } free(pattern_list); - STATUS("Saving displacements before corrections\n"); - ret1 = save_data_to_hdf5("disp_x_before.h5", det, max_fs, max_ss, - default_fill_value, displ_x); - ret2 = save_data_to_hdf5("disp_y_before.h5", det, max_fs, max_ss, - default_fill_value, displ_y); - ret3 = save_data_to_hdf5("disp_abs_before.h5", det, max_fs, max_ss, - default_fill_value, displ_abs); - if ( ret1!=0 || ret2!=0 || ret3!=0 ) { - ERROR("Error while writing data to file.\n"); - free(conn_data); - free(displ_x); - free(displ_y); - free(displ_abs); - free(num_pix_displ); - free(slab_to_x); - free(slab_to_y); - return 1; + if ( error_maps ) { + STATUS("Saving displacements before corrections.\n"); + +#ifdef HAVE_SAVE_TO_PNG + + ret1 = save_data_to_png("error_map_before.png", det, max_fs, max_ss, + dfv, displ_abs); + if ( ret1!=0 ) { + ERROR("Error while writing data to file.\n"); + free(conn_data); + free(displ_x); + free(displ_y); + free(displ_abs); + free(num_pix_displ); + free(slab_to_x); + free(slab_to_y); + return 1; + } + +#else /* HAVE_SAVE_TO_PNG */ + + STATUS("ERROR: geoptimiser was compiled without GTK and cairo " + "support. Error maps will not be saved.\n"); + +#endif /* HAVE_SAVE_TO_PNG */ + } STATUS("Computing initial error.\n"); - totalError = compute_error(connected, array_width, conn_data, + totalError = compute_error(connected, aw, conn_data, num_pix_displ, displ_abs); - STATUS("The total initial error <delta^2> = %0.4f\n", totalError); - STATUS("Now calculating corrections\n"); + STATUS("The total initial error <delta^2> = %0.4f pixels.\n", + totalError); + STATUS("Now calculating corrections.\n"); for ( di=0;di<connected->n_rigid_groups;di++ ) { @@ -2096,28 +2695,19 @@ int optimize_geometry(char *infile, char *outfile, char *geometry_filename, for (ip=0; ip<connected->rigid_groups[di]->n_panels; ip++) { - struct panel *p; - int ifs, iss; + calculate_panel_correction(di, ip, aw, num_pix_displ, + connected, conn_data); - p = connected->rigid_groups[di]->panels[ip]; - for (ifs=p->min_fs; ifs<p->max_fs+1; ifs++) { - for (iss=p->min_ss; iss<p->max_ss+1; iss++) { - if ( num_pix_displ[ifs+array_width*iss]>= - conn_data[di].num_peaks_per_pixel ) { - conn_data[di].n_peaks_in_conn++; - } - } - } } } - STATUS("Calculating angles and elongations (usually long)\n"); + STATUS("Calculating angles and elongations.\n"); ret = compute_angles_and_stretch(connected, conn_data, num_pix_displ, slab_to_x, slab_to_y, displ_x, displ_y, - array_width, + aw, min_num_peaks_per_panel, dist_coeff_ang_str, min_num_peaks_per_pixel, @@ -2178,16 +2768,17 @@ int optimize_geometry(char *infile, char *outfile, char *geometry_filename, return 1; } - fill_coordinate_matrices(det, array_width, recomputed_slab_to_x, + fill_coordinate_matrices(det, aw, recomputed_slab_to_x, recomputed_slab_to_y); - recompute_differences(connected, slab_to_x, slab_to_y, recomputed_slab_to_x, + recompute_differences(connected, slab_to_x, slab_to_y, + recomputed_slab_to_x, recomputed_slab_to_y, conn_data, - stretch_coeff, array_width, displ_x, displ_y, + stretch_coeff, aw, displ_x, displ_y, num_pix_displ); - ret = compute_shifts(connected, conn_data, num_pix_displ, array_width, - min_num_peaks_per_panel, default_fill_value, + ret = compute_shifts(connected, conn_data, num_pix_displ, aw, + min_num_peaks_per_panel, dfv, max_peak_dist, displ_x, displ_y ); if ( ret != 0 ) return 1; @@ -2198,59 +2789,51 @@ int optimize_geometry(char *infile, char *outfile, char *geometry_filename, for ( di=0;di<connected->n_rigid_groups;di++ ) { for (ip=0; ip<connected->rigid_groups[di]->n_panels; ip++) { - struct panel *p; - int ifs, iss; + compute_abs_displ(connected, conn_data, + num_pix_displ, dfv, di, ip, aw, + displ_x, displ_y, displ_abs); - if (conn_data[di].sh_x < default_fill_value+1) continue; + } + } - p = connected->rigid_groups[di]->panels[ip]; + correct_shifts(connected, conn_data, dfv, clen_to_use); - for (ifs=p->min_fs; ifs<p->max_fs+1; ifs++) { - for (iss=p->min_ss; iss<p->max_ss+1; iss++) { - if ( num_pix_displ[ifs+array_width*iss]>= - conn_data[di].num_peaks_per_pixel ) { - displ_x[ifs+array_width*iss] -= conn_data[di].sh_x; - displ_y[ifs+array_width*iss] -= conn_data[di].sh_y; - displ_abs[ifs+array_width*iss] = modulus2d( - displ_x[ifs+array_width*iss], - displ_y[ifs+array_width*iss] - ); - } else { - displ_abs[ifs+array_width*iss] = default_fill_value; - } - } - } + if ( error_maps ) { + + +#ifdef HAVE_SAVE_TO_PNG + + STATUS("Saving displacements after corrections.\n"); + ret2 = save_data_to_png("error_map_after.png", det, max_fs, max_ss, + dfv, displ_x); + if ( ret2 !=0 ) { + ERROR("Error while writing data to file.\n"); + free(conn_data); + free(displ_x); + free(displ_y); + free(displ_abs); + free(num_pix_displ); + free(slab_to_x); + free(slab_to_y); + free(recomputed_slab_to_x); + free(recomputed_slab_to_y); + return 1; } - } - correct_shifts(connected, conn_data, default_fill_value, clen_to_use); +#else /* HAVE_SAVE_TO_PNG */ + + STATUS("ERROR: geoptimiser was compiled without GTK and cairo support.\n" + "Error maps will not be saved.\n"); + +#endif /* HAVE_SAVE_TO_PNG */ - STATUS("Saving displacements after corrections\n"); - ret4 = save_data_to_hdf5("disp_x_after.h5", det, max_fs, max_ss, - default_fill_value, displ_x); - ret5 = save_data_to_hdf5("disp_y_after.h5", det, max_fs, max_ss, - default_fill_value, displ_y); - ret6 = save_data_to_hdf5("disp_abs_after.h5", det, max_fs, max_ss, - default_fill_value, displ_abs); - if ( ret4!=0 || ret5!=0 || ret6!=0 ) { - ERROR("Error while writing data to file.\n"); - free(conn_data); - free(displ_x); - free(displ_y); - free(displ_abs); - free(num_pix_displ); - free(slab_to_x); - free(slab_to_y); - free(recomputed_slab_to_x); - free(recomputed_slab_to_y); - return 1; } STATUS("Computing final error.\n"); - totalError = compute_error(connected, array_width, conn_data, num_pix_displ, + totalError = compute_error(connected, aw, conn_data, num_pix_displ, displ_abs); - STATUS("The total final error <delta^2> = %0.4f\n",totalError); + STATUS("The total final error <delta^2> = %0.4f pixels.\n",totalError); write_ret = write_detector_geometry_2(geometry_filename, outfile, det, command_line, 1); @@ -2259,6 +2842,15 @@ int optimize_geometry(char *infile, char *outfile, char *geometry_filename, return 1; } STATUS("All done!\n"); + if ( error_maps ) { + +#ifdef HAVE_SAVE_TO_PNG + + STATUS("Be sure to inspect error_map_before.png and " + "error_map_after.png !!\n"); + +#endif /* HAVE_SAVE_TO_PNG */ + } free(conn_data); free(displ_x); @@ -2286,8 +2878,11 @@ int main(int argc, char *argv[]) int min_num_peaks_per_pixel = 3; int min_num_peaks_per_panel = 100; int only_best_distance = 0; + int enforce_cspad_layout = 0; int nostretch = 0; int individual_coffset = 0; + int no_cspad = 0; + int error_maps = 1; double max_peak_dist = 4.0; struct detector *det = NULL; @@ -2298,25 +2893,26 @@ int main(int argc, char *argv[]) const struct option longopts[] = { /* Options with long and short versions */ - {"help", 0, NULL, 'h'}, - {"version", 0, NULL, 10 }, - {"input", 1, NULL, 'i'}, - {"output", 1, NULL, 'o'}, - {"geometry", 1, NULL, 'g'}, - {"quadrants", 1, NULL, 'q'}, - {"connected", 1, NULL, 'c'}, - {"min-num-peaks-per-pixel",1, NULL, 'x'}, - {"min-num-peaks-per-panel",1, NULL, 'p'}, - {"most-few-clen", 0, NULL, 'l'}, - {"max-peak-dist", 1, NULL, 'm'}, - {"individual-dist-offset", 0, NULL, 's'}, - + {"help", 0, NULL, 'h'}, + {"version", 0, NULL, 10 }, + {"input", 1, NULL, 'i'}, + {"output", 1, NULL, 'o'}, + {"geometry", 1, NULL, 'g'}, + {"quadrants", 1, NULL, 'q'}, + {"connected", 1, NULL, 'c'}, + {"min-num-peaks-per-pixel", 1, NULL, 'x'}, + {"min-num-peaks-per-panel", 1, NULL, 'p'}, + {"most-few-clen", 0, NULL, 'l'}, + {"max-peak-dist", 1, NULL, 'm'}, + {"individual-dist-offset", 0, NULL, 's'}, /* Long-only options with no arguments */ - {"no-stretch", 0, &nostretch, 1}, - + {"no-stretch", 0, &nostretch, 1}, + {"no-error-maps", 0, &error_maps, 0}, + {"enforce-cspad-layout", 0, &enforce_cspad_layout, 1}, + {"no-cspad", 0, &no_cspad, 1}, - {0, 0, NULL, 0} + {0, 0, NULL, 0} }; /* Short options */ @@ -2379,6 +2975,7 @@ int main(int argc, char *argv[]) case 's' : individual_coffset = 1; break; + } } @@ -2398,7 +2995,8 @@ int main(int argc, char *argv[]) } if ( quadrant_coll_name == NULL ) { - ERROR("You must provide a rigid group collection for quadrants.\n"); + ERROR("You must provide a rigid group collection for " + "quadrants.\n"); return 1; } @@ -2417,10 +3015,11 @@ int main(int argc, char *argv[]) return 1; } - connected = find_rigid_group_collection_by_name(det, connected_coll_name); + connected = find_rigid_group_collection_by_name(det, + connected_coll_name); if ( connected == NULL ) { - ERROR("Cannot find rigid group collection for connected asics: %s\n", - connected_coll_name); + ERROR("Cannot find rigid group collection for connected " + "asics: %s\n", connected_coll_name); return 1; } @@ -2430,10 +3029,12 @@ int main(int argc, char *argv[]) strcat(command_line, buffer); } + g_type_init(); ret_val = optimize_geometry(infile, outfile, geometry_filename, det, quadrants, connected, min_num_peaks_per_pixel, min_num_peaks_per_panel, only_best_distance, - nostretch, individual_coffset, + nostretch, individual_coffset, error_maps, + enforce_cspad_layout, no_cspad, max_peak_dist, command_line); return ret_val; diff --git a/src/hdfsee-render.c b/src/hdfsee-render.c index abbb2ccd..71e2e1e8 100644 --- a/src/hdfsee-render.c +++ b/src/hdfsee-render.c @@ -46,16 +46,15 @@ #include <image.h> static float *get_binned_panel(struct image *image, int binning, - struct panel *p, double *max, int *pw, int *ph) + int pi, double *max, int *pw, int *ph) { float *data; int x, y; int w, h; int fw; - float *in; + struct panel *p = &image->det->panels[pi]; - fw = image->width; - in = image->data; + fw = p->max_fs - p->min_fs + 1; /* Some pixels might get discarded */ w = (p->max_fs - p->min_fs + 1) / binning; @@ -80,31 +79,13 @@ static float *get_binned_panel(struct image *image, int binning, double v; int fs, ss; - int tbad = 0; - fs = binning*x+xb+p->min_fs; - ss = binning*y+yb+p->min_ss; - v = in[fs+ss*fw]; + fs = binning*x+xb; + ss = binning*y+yb; + v = image->dp[pi][fs+ss*fw]; total += v; - if ( in_bad_region(image->det, fs, ss) ) tbad = 1; - - if ( image->flags != NULL ) { - - uint16_t flags = image->flags[fs+ss*fw]; - - if ( !((flags & image->det->mask_good) - == image->det->mask_good) ) { - tbad = 1; - } - - if ( flags & image->det->mask_bad ) { - tbad = 1; - } - - } - - if ( tbad ) bad = 1; + if ( image->bad[pi][fs+ss*fw] ) bad = 1; } } @@ -205,8 +186,7 @@ GdkPixbuf **render_panels(struct image *image, max = 0.0; for ( i=0; i<np; i++ ) { double this_max = 0.0; - hdrs[i] = get_binned_panel(image, binning, - &image->det->panels[i], &this_max, + hdrs[i] = get_binned_panel(image, binning, i, &this_max, &ws[i], &hs[i]); if ( this_max > max ) max = this_max; } @@ -246,7 +226,7 @@ GdkPixbuf *render_get_colour_scale(size_t w, size_t h, int scale) data = malloc(3*w*h); if ( data == NULL ) return NULL; - max = h; + max = h-(h/6); for ( y=0; y<h; y++ ) { diff --git a/src/hdfsee.c b/src/hdfsee.c index 89b8bf95..baf439bd 100644 --- a/src/hdfsee.c +++ b/src/hdfsee.c @@ -75,9 +75,11 @@ static void show_help(const char *s) " black-blue-pink-red-orange-\n" " -yellow-white.\n" " -e, --image=<element> Start up displaying this image from the\n" -" HDF5 file. Example: /data/data0.\n" -" (Only used when a geometry file is not" -" provided. See option -g)" +" HDF5 file. When this option is used,\n" +" information about the data layout\n" +" from the geometry file is ignored (See\n" +" manual page).\n" +" Example: /data/data0.\n" " --event=<event code> Event to show from multi-event file.\n" " -g, --geometry=<filename> Use geometry from file for display.\n" " (When this option is used, the value of\n" @@ -300,6 +302,11 @@ int main(int argc, char *argv[]) return 1; } + if ( event != NULL && geom_filename == NULL) { + ERROR("The '--event' option requires geometry file\n"); + return 1; + } + if ( cscale == NULL ) cscale = strdup("colour"); if ( strcmp(cscale, "mono") == 0 ) { colscale = SCALE_MONO; diff --git a/src/hrs-scaling.c b/src/hrs-scaling.c deleted file mode 100644 index 7f15f54d..00000000 --- a/src/hrs-scaling.c +++ /dev/null @@ -1,559 +0,0 @@ -/* - * hrs-scaling.c - * - * Intensity scaling using generalised HRS target function - * - * Copyright © 2012-2014 Deutsches Elektronen-Synchrotron DESY, - * a research centre of the Helmholtz Association. - * - * Authors: - * 2010-2014 Thomas White <taw@physics.org> - * - * This file is part of CrystFEL. - * - * CrystFEL is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * CrystFEL is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with CrystFEL. If not, see <http://www.gnu.org/licenses/>. - * - */ - -#ifdef HAVE_CONFIG_H -#include <config.h> -#endif - - -#include <stdlib.h> -#include <assert.h> -#include <gsl/gsl_matrix.h> -#include <gsl/gsl_vector.h> -#include <gsl/gsl_linalg.h> -#include <gsl/gsl_eigen.h> - -#include "image.h" -#include "peaks.h" -#include "symmetry.h" -#include "geometry.h" -#include "cell.h" -#include "utils.h" -#include "reflist.h" - - -/* Minimum partiality of a reflection for it to be used for scaling */ -#define MIN_PART_SCALE (0.05) - -/* Minimum partiality of a reflection for it to be merged */ -#define MIN_PART_MERGE (0.05) - -/* Maximum number of iterations of scaling per macrocycle. */ -#define MAX_CYCLES (10) - - -struct scale_queue_args -{ - RefList *reference; - Crystal **crystals; - int n_started; - PartialityModel pmodel; -}; - - -struct scale_worker_args -{ - Crystal *crystal; - RefList *reference; - PartialityModel pmodel; -}; - - -static void *create_scale_job(void *vqargs) -{ - struct scale_worker_args *wargs; - struct scale_queue_args *qargs = vqargs; - - wargs = malloc(sizeof(struct scale_worker_args)); - wargs->reference = qargs->reference; - wargs->pmodel = qargs->pmodel; - - wargs->crystal = qargs->crystals[qargs->n_started++]; - - return wargs; -} - - -static void run_scale_job(void *vwargs, int cookie) -{ - struct scale_worker_args *wargs = vwargs; - Crystal *cr = wargs->crystal; - RefList *reference = wargs->reference; - Reflection *refl; - RefListIterator *iter; - double num = 0.0; - double den = 0.0; - double g; - - for ( refl = first_refl(crystal_get_reflections(cr), &iter); - refl != NULL; - refl = next_refl(refl, iter) ) - { - signed int h, k, l; - double Ih, Ihl, corr; - Reflection *r; - - if ( (get_partiality(refl) < MIN_PART_SCALE) - || (get_intensity(refl) < 3.0*get_esd_intensity(refl)) ) { - continue; - } - - /* Look up by asymmetric indices */ - get_indices(refl, &h, &k, &l); - r = find_refl(reference, h, k, l); - if ( r == NULL ) continue; - - Ih = get_intensity(r); - - corr = get_partiality(refl) * get_lorentz(refl); - - Ihl = get_intensity(refl) / corr; - - num += Ih * Ihl; - den += Ih * Ih; - - } - - g = num / den; - crystal_set_osf(cr, g); /* If it's NaN, it'll get rejected later */ -} - - -static void finalise_scale_job(void *vqargs, void *vwargs) -{ - struct scale_worker_args *wargs = vwargs; - free(wargs); -} - - -static void iterate_scale(Crystal **crystals, int n, RefList *reference, - int n_threads, PartialityModel pmodel) -{ - struct scale_queue_args qargs; - - assert(reference != NULL); - - qargs.reference = reference; - qargs.n_started = 0; - qargs.crystals = crystals; - qargs.pmodel = pmodel; - - run_threads(n_threads, run_scale_job, create_scale_job, - finalise_scale_job, &qargs, n, 0, 0, 0); -} - - -struct merge_queue_args -{ - RefList *full; - pthread_rwlock_t full_lock; - Crystal **crystals; - int n_started; - PartialityModel pmodel; -}; - - -struct merge_worker_args -{ - Crystal *crystal; - RefList *full; - pthread_rwlock_t *full_lock; - PartialityModel pmodel; -}; - - -static void *create_merge_job(void *vqargs) -{ - struct merge_worker_args *wargs; - struct merge_queue_args *qargs = vqargs; - - wargs = malloc(sizeof(struct merge_worker_args)); - wargs->full = qargs->full; - wargs->full_lock = &qargs->full_lock; - wargs->pmodel = qargs->pmodel; - - wargs->crystal = qargs->crystals[qargs->n_started++]; - - return wargs; -} - - -static void run_merge_job(void *vwargs, int cookie) -{ - struct merge_worker_args *wargs = vwargs; - Crystal *cr = wargs->crystal; - RefList *full = wargs->full; - Reflection *refl; - RefListIterator *iter; - double G; - - /* If this crystal's scaling was dodgy, it doesn't contribute to the - * merged intensities */ - if ( crystal_get_user_flag(cr) != 0 ) return; - - G = crystal_get_osf(cr); - - for ( refl = first_refl(crystal_get_reflections(cr), &iter); - refl != NULL; - refl = next_refl(refl, iter) ) - { - Reflection *f; - signed int h, k, l; - double num, den; - int red; - double Ihl, corr; - - if ( get_partiality(refl) < MIN_PART_MERGE ) continue; - - get_indices(refl, &h, &k, &l); - pthread_rwlock_rdlock(wargs->full_lock); - f = find_refl(full, h, k, l); - if ( f == NULL ) { - - /* Swap read lock for write lock */ - pthread_rwlock_unlock(wargs->full_lock); - pthread_rwlock_wrlock(wargs->full_lock); - - /* In the gap between the unlock and the wrlock, the - * reflection might have been created by another thread. - * So, we must check again */ - f = find_refl(full, h, k, l); - if ( f == NULL ) { - f = add_refl(full, h, k, l); - lock_reflection(f); - pthread_rwlock_unlock(wargs->full_lock); - num = 0.0; - den = 0.0; - red = 0; - - } else { - - /* Someone else created it */ - lock_reflection(f); - pthread_rwlock_unlock(wargs->full_lock); - num = get_temp1(f); - den = get_temp2(f); - red = get_redundancy(f); - - } - - } else { - - lock_reflection(f); - pthread_rwlock_unlock(wargs->full_lock); - num = get_temp1(f); - den = get_temp2(f); - red = get_redundancy(f); - - } - - corr = get_partiality(refl) * get_lorentz(refl); - - Ihl = get_intensity(refl) / corr; - - num += Ihl / G; - den += 1.0; - red++; - - set_temp1(f, num); - set_temp2(f, den); - set_redundancy(f, red); - unlock_reflection(f); - } -} - - -static void finalise_merge_job(void *vqargs, void *vwargs) -{ - free(vwargs); -} - - -static RefList *lsq_intensities(Crystal **crystals, int n, int n_threads, - PartialityModel pmodel) -{ - RefList *full; - struct merge_queue_args qargs; - Reflection *refl; - RefListIterator *iter; - - full = reflist_new(); - - qargs.full = full; - qargs.n_started = 0; - qargs.crystals = crystals; - qargs.pmodel = pmodel; - pthread_rwlock_init(&qargs.full_lock, NULL); - - run_threads(n_threads, run_merge_job, create_merge_job, - finalise_merge_job, &qargs, n, 0, 0, 0); - - pthread_rwlock_destroy(&qargs.full_lock); - - for ( refl = first_refl(full, &iter); - refl != NULL; - refl = next_refl(refl, iter) ) - { - double Ih; - Ih = get_temp1(refl) / get_temp2(refl); - set_intensity(refl, Ih); - } - - return full; -} - - - -struct esd_queue_args -{ - RefList *full; - Crystal **crystals; - int n_started; - PartialityModel pmodel; -}; - - -struct esd_worker_args -{ - Crystal *crystal; - RefList *full; - PartialityModel pmodel; -}; - - -static void *create_esd_job(void *vqargs) -{ - struct esd_worker_args *wargs; - struct esd_queue_args *qargs = vqargs; - - wargs = malloc(sizeof(struct esd_worker_args)); - wargs->full = qargs->full; - wargs->pmodel = qargs->pmodel; - - wargs->crystal = qargs->crystals[qargs->n_started++]; - - return wargs; -} - - -static void run_esd_job(void *vwargs, int cookie) -{ - struct esd_worker_args *wargs = vwargs; - Crystal *cr = wargs->crystal; - RefList *full = wargs->full; - Reflection *refl; - RefListIterator *iter; - double G; - - /* If this crystal's scaling was dodgy, it doesn't contribute to the - * merged intensities */ - if ( crystal_get_user_flag(cr) != 0 ) return; - - G = crystal_get_osf(cr); - - for ( refl = first_refl(crystal_get_reflections(cr), &iter); - refl != NULL; - refl = next_refl(refl, iter) ) - { - Reflection *f; - signed int h, k, l; - double num; - double Ihl, Ih, corr; - - if ( get_partiality(refl) < MIN_PART_MERGE ) continue; - - get_indices(refl, &h, &k, &l); - f = find_refl(full, h, k, l); - assert(f != NULL); - - lock_reflection(f); - - num = get_temp1(f); - - corr = get_partiality(refl) * get_lorentz(refl); - - Ih = get_intensity(f); - Ihl = get_intensity(refl) / (G*corr); - - num += pow(Ihl - Ih, 2.0); - - set_temp1(f, num); - unlock_reflection(f); - } -} - - -static void finalise_esd_job(void *vqargs, void *vwargs) -{ - free(vwargs); -} - - -static void calculate_esds(Crystal **crystals, int n, RefList *full, - int n_threads, int min_red, PartialityModel pmodel) -{ - struct esd_queue_args qargs; - Reflection *refl; - RefListIterator *iter; - - qargs.full = full; - qargs.n_started = 0; - qargs.crystals = crystals; - qargs.pmodel = pmodel; - - for ( refl = first_refl(full, &iter); - refl != NULL; - refl = next_refl(refl, iter) ) - { - set_temp1(refl, 0.0); - set_temp2(refl, 0.0); - } - - run_threads(n_threads, run_esd_job, create_esd_job, - finalise_esd_job, &qargs, n, 0, 0, 0); - - for ( refl = first_refl(full, &iter); - refl != NULL; - refl = next_refl(refl, iter) ) - { - double esd; - int red = get_redundancy(refl); - esd = sqrt(get_temp1(refl)); - esd /= (double)red; - set_esd_intensity(refl, esd); - - if ( red < min_red ) { - set_redundancy(refl, 0); - } - } -} - - -static void reject_outliers(double *old_osfs, int n, Crystal **crystals) -{ - int i; - - for ( i=0; i<n; i++ ) { - double osf = crystal_get_osf(crystals[i]); - if ( isnan(osf) || (osf < 0.0) || (osf > 3.0) ) { - crystal_set_user_flag(crystals[i], 1); - } - } -} - - -static int test_convergence(double *old_osfs, int n, Crystal **crystals) -{ - int i; - double total_change = 0.0; - double mean_change; - int n_change = 0; - - for ( i=0; i<n; i++ ) { - if ( crystal_get_user_flag(crystals[i]) == 0 ) { - double new_osf = crystal_get_osf(crystals[i]); - total_change += fabs(new_osf - old_osfs[i]); - n_change++; - } - } - mean_change = total_change / n_change; - - STATUS("Mean OSF change = %f\n", mean_change); - - return mean_change < 0.01; -} - - -/* Scale the stack of images */ -RefList *scale_intensities(Crystal **crystals, int n, - int n_threads, int noscale, PartialityModel pmodel, - int min_redundancy) -{ - int i; - RefList *full = NULL; - double *old_osfs; - int done; - - for ( i=0; i<n; i++ ) { - crystal_set_user_flag(crystals[i], 0); - crystal_set_osf(crystals[i], 1.0); - } - - if ( noscale ) { - full = lsq_intensities(crystals, n, n_threads, pmodel); - calculate_esds(crystals, n, full, n_threads, min_redundancy, - pmodel); - return full; - } - - /* Create an initial list to refine against */ - full = lsq_intensities(crystals, n, n_threads, pmodel); - - old_osfs = malloc(n*sizeof(double)); - if ( old_osfs == NULL ) return NULL; - - /* Iterate */ - i = 0; - do { - - double total_sf = 0.0; - int n_sf = 0; - double norm_sf; - int j; - - for ( j=0; j<n; j++ ) { - old_osfs[j] = crystal_get_osf(crystals[j]); - crystal_set_user_flag(crystals[j], 0); - } - - iterate_scale(crystals, n, full, n_threads, pmodel); - - /* Normalise the scale factors */ - for ( j=0; j<n; j++ ) { - double osf = crystal_get_osf(crystals[j]); - if ( !isnan(osf) ) { - total_sf += osf; - n_sf++; - } - } - norm_sf = total_sf / n_sf; - for ( j=0; j<n; j++ ) { - crystal_set_osf(crystals[j], - crystal_get_osf(crystals[j])/norm_sf); - } - - reject_outliers(old_osfs, n, crystals); - done = test_convergence(old_osfs, n, crystals); - - /* Generate list for next iteration */ - reflist_free(full); - full = lsq_intensities(crystals, n, n_threads, pmodel); - - i++; - - } while ( !done && (i < MAX_CYCLES) ); - - if ( i == MAX_CYCLES ) { - ERROR("WARNING: Scaling did not converge.\n"); - } - - calculate_esds(crystals, n, full, n_threads, min_redundancy, pmodel); - - free(old_osfs); - return full; -} diff --git a/src/im-sandbox.c b/src/im-sandbox.c index ba709f6f..df50199c 100644 --- a/src/im-sandbox.c +++ b/src/im-sandbox.c @@ -3,13 +3,13 @@ * * Sandbox for indexing * - * Copyright © 2012-2014 Deutsches Elektronen-Synchrotron DESY, + * Copyright © 2012-2015 Deutsches Elektronen-Synchrotron DESY, * a research centre of the Helmholtz Association. * Copyright © 2012 Richard Kirian * Copyright © 2012 Lorenzo Galli * * Authors: - * 2010-2014 Thomas White <taw@physics.org> + * 2010-2015 Thomas White <taw@physics.org> * 2014 Valerio Mariani * 2011 Richard Kirian * 2012 Lorenzo Galli @@ -48,6 +48,7 @@ #include <signal.h> #include <sys/stat.h> #include <assert.h> +#include <sys/mman.h> #ifdef HAVE_CLOCK_GETTIME #include <time.h> @@ -85,6 +86,12 @@ struct sb_reader }; +struct sb_shm +{ + pthread_mutex_t term_lock; +}; + + struct sandbox { pthread_mutex_t lock; @@ -96,7 +103,6 @@ struct sandbox int n_hadcrystals_last_stats; int n_crystals_last_stats; int t_last_stats; - int suspend_stats; struct index_args *iargs; @@ -110,6 +116,8 @@ struct sandbox struct filename_plus_event **last_filename; int serial; + struct sb_shm *shared; + char *tmpdir; struct sb_reader *reader; @@ -359,7 +367,7 @@ static int read_fpe_data(struct buffer_data *bd) static void run_work(const struct index_args *iargs, int filename_pipe, int results_pipe, Stream *st, - int cookie, const char *tmpdir) + int cookie, const char *tmpdir, pthread_mutex_t *term_lock) { FILE *fh; int allDone = 0; @@ -494,7 +502,7 @@ static void run_work(const struct index_args *iargs, pargs.n_crystals = 0; process_image(iargs, &pargs, st, cookie, tmpdir, - results_pipe, ser); + results_pipe, ser, term_lock); /* Request another image */ c = sprintf(buf, "%i\n", pargs.n_crystals); @@ -815,11 +823,12 @@ static void start_worker_process(struct sandbox *sb, int slot) st = open_stream_fd_for_write(stream_pipe[1]); run_work(sb->iargs, filename_pipe[0], result_pipe[1], - st, slot, tmp); + st, slot, tmp, &sb->shared->term_lock); close_stream(st); //close(filename_pipe[0]); close(result_pipe[1]); + munmap(sb->shared, sizeof(struct sb_shm)); free(sb); @@ -894,6 +903,39 @@ static void handle_zombie(struct sandbox *sb) } +static int setup_shm(struct sandbox *sb) +{ + pthread_mutexattr_t attr; + + sb->shared = mmap(NULL, sizeof(struct sb_shm), PROT_READ | PROT_WRITE, + MAP_SHARED | MAP_ANONYMOUS, -1, 0); + + if ( sb->shared == MAP_FAILED ) { + ERROR("SHM setup failed: %s\n", strerror(errno)); + return 1; + } + + if ( pthread_mutexattr_init(&attr) ) { + ERROR("Failed to initialise mutex attr.\n"); + return 1; + } + + if ( pthread_mutexattr_setpshared(&attr, PTHREAD_PROCESS_SHARED) ) { + ERROR("Failed to set process shared attribute.\n"); + return 1; + } + + if ( pthread_mutex_init(&sb->shared->term_lock, &attr) ) { + ERROR("Terminal lock setup failed.\n"); + return 1; + } + + pthread_mutexattr_destroy(&attr); + + return 0; +} + + void create_sandbox(struct index_args *iargs, int n_proc, char *prefix, int config_basename, FILE *fh, Stream *stream, const char *tempdir) @@ -930,7 +972,6 @@ void create_sandbox(struct index_args *iargs, int n_proc, char *prefix, sb->n_hadcrystals_last_stats = 0; sb->n_crystals_last_stats = 0; sb->t_last_stats = get_monotonic_seconds(); - sb->suspend_stats = 0; sb->n_proc = n_proc; sb->iargs = iargs; sb->serial = 1; @@ -939,6 +980,12 @@ void create_sandbox(struct index_args *iargs, int n_proc, char *prefix, sb->reader->fhs = NULL; sb->reader->stream = stream; + if ( setup_shm(sb) ) { + ERROR("Failed to set up SHM.\n"); + free(sb); + return; + } + sb->stream_pipe_write = calloc(n_proc, sizeof(int)); if ( sb->stream_pipe_write == NULL ) { ERROR("Couldn't allocate memory for pipes.\n"); @@ -1106,34 +1153,20 @@ void create_sandbox(struct index_args *iargs, int n_proc, char *prefix, chomp(results); - if ( strcmp(results, "SUSPEND") == 0 ) { - sb->suspend_stats++; - continue; /* Do not send next filename */ - } else if ( strcmp(results, "RELEASE") == 0 ) { - if ( sb->suspend_stats > 0 ) { - sb->suspend_stats--; - } else { - ERROR("RELEASE before SUSPEND.\n"); + strtol(results, &eptr, 10); + if ( eptr == results ) { + if ( strlen(results) > 0 ) { + ERROR("Invalid result '%s'\n", + results); } - continue; /* Do not send next filename */ } else { - strtol(results, &eptr, 10); - if ( eptr == results ) { - if ( strlen(results) > 0 ) { - ERROR("Invalid result '%s'\n", - results); - } - } else { - - int nc = atoi(results); - sb->n_crystals += nc; - if ( nc > 0 ) { - sb->n_hadcrystals++; - } - sb->n_processed++; - + int nc = atoi(results); + sb->n_crystals += nc; + if ( nc > 0 ) { + sb->n_hadcrystals++; } + sb->n_processed++; } @@ -1210,8 +1243,8 @@ void create_sandbox(struct index_args *iargs, int n_proc, char *prefix, /* Update progress */ lock_sandbox(sb); tNow = get_monotonic_seconds(); - if ( !sb->suspend_stats - && (tNow >= sb->t_last_stats+STATS_EVERY_N_SECONDS) ) + r = pthread_mutex_trylock(&sb->shared->term_lock); + if ((r==0) && (tNow >= sb->t_last_stats+STATS_EVERY_N_SECONDS)) { STATUS("%4i indexable out of %4i processed (%4.1f%%), " @@ -1228,7 +1261,9 @@ void create_sandbox(struct index_args *iargs, int n_proc, char *prefix, sb->n_crystals_last_stats = sb->n_crystals; sb->t_last_stats = tNow; + } + if ( r == 0 ) pthread_mutex_unlock(&sb->shared->term_lock); unlock_sandbox(sb); allDone = 1; @@ -1264,6 +1299,7 @@ void create_sandbox(struct index_args *iargs, int n_proc, char *prefix, free(sb->result_fhs); free(sb->pids); free(sb->tmpdir); + munmap(sb->shared, sizeof(struct sb_shm)); pthread_mutex_destroy(&sb->lock); diff --git a/src/indexamajig.c b/src/indexamajig.c index 44f067e2..c7e4a270 100644 --- a/src/indexamajig.c +++ b/src/indexamajig.c @@ -3,13 +3,13 @@ * * Index patterns, output hkl+intensity etc. * - * Copyright © 2012-2014 Deutsches Elektronen-Synchrotron DESY, + * Copyright © 2012-2015 Deutsches Elektronen-Synchrotron DESY, * a research centre of the Helmholtz Association. * Copyright © 2012 Richard Kirian * Copyright © 2012 Lorenzo Galli * * Authors: - * 2010-2014 Thomas White <taw@physics.org> + * 2010-2015 Thomas White <taw@physics.org> * 2011 Richard Kirian * 2012 Lorenzo Galli * 2012 Chunhong Yoon @@ -97,11 +97,9 @@ static void show_help(const char *s) " zaef : Use Zaefferer (2000) gradient detection.\n" " This is the default method.\n" " hdf5 : Get from a table in HDF5 file.\n" +" cxi : Get from CXI format HDF5 file.\n" " --hdf5-peaks=<p> Find peaks table in HDF5 file here.\n" " Default: /processing/hitfinder/peakinfo\n" -" --cxi-hdf5-peaks Peaks in the HDF5 file are in CXI file format.\n" -" Only used in conjunction with the --hdf5-peaks,\n" -" ignored otherwise." " --integration=<meth> Perform final pattern integration using <meth>.\n" "\n\n" "For more control over the process, you might need:\n\n" @@ -127,6 +125,10 @@ static void show_help(const char *s) " --int-radius=<r> Set the integration radii. Default: 4,5,7.\n" " --push-res=<n> Integrate higher than apparent resolution cutoff.\n" " --highres=<n> Absolute resolution cutoff in Angstroms.\n" +" --fix-profile-radius Fix the reciprocal space profile radius for spot\n" +" prediction (default: automatically determine.\n" +" --fix-bandwidth Set the bandwidth for spot prediction.\n" +" --fix-divergence Set the divergence (full angle) for spot prediction.\n" "\n" "\nFor time-resolved stuff, you might want to use:\n\n" " --copy-hdf5-field <f> Copy the value of field <f> into the stream. You\n" @@ -145,6 +147,7 @@ static void show_help(const char *s) " --no-peaks-in-stream Do not record peak search results in the stream.\n" " --no-refls-in-stream Do not record integrated reflections in the stream.\n" " --int-diag=<cond> Show debugging information about reflections.\n" +" --no-refine Skip the prediction refinement step.\n" ); } @@ -214,8 +217,7 @@ int main(int argc, char *argv[]) iargs.det = NULL; iargs.peaks = PEAK_ZAEF; iargs.beam = &beam; - iargs.hdf5_peak_path = strdup("/processing/hitfinder/peakinfo"); - iargs.cxi_hdf5_peaks = 0; + iargs.hdf5_peak_path = NULL; iargs.copyme = NULL; iargs.pk_inn = -1.0; iargs.pk_mid = -1.0; @@ -238,6 +240,10 @@ int main(int argc, char *argv[]) iargs.int_meth = integration_method("rings-nocen", NULL); iargs.push_res = 0.0; iargs.highres = +INFINITY; + iargs.fix_profile_r = -1.0; + iargs.fix_bandwidth = -1.0; + iargs.fix_divergence = -1.0; + iargs.predict_refine = 1; /* Long options */ const struct option longopts[] = { @@ -264,7 +270,7 @@ int main(int argc, char *argv[]) {"no-use-saturated", 0, &iargs.use_saturated, 0}, {"no-revalidate", 0, &iargs.no_revalidate, 1}, {"check-hdf5-snr", 0, &iargs.check_hdf5_snr, 1}, - {"cxi-hdf5-peaks", 0, &iargs.cxi_hdf5_peaks, 1}, + {"no-refine", 0, &iargs.predict_refine, 0}, /* Long-only options which don't actually do anything */ {"no-sat-corr", 0, &iargs.satcorr, 0}, @@ -293,6 +299,9 @@ int main(int argc, char *argv[]) {"res-push", 1, NULL, 19}, /* compat */ {"peak-radius", 1, NULL, 20}, {"highres", 1, NULL, 21}, + {"fix-profile-radius", 1, NULL, 22}, + {"fix-bandwidth", 1, NULL, 23}, + {"fix-divergence", 1, NULL, 24}, {0, 0, NULL, 0} }; @@ -440,6 +449,28 @@ int main(int argc, char *argv[]) iargs.highres = 1.0 / (iargs.highres/1e10); break; + case 22 : + if ( sscanf(optarg, "%f", &iargs.fix_profile_r) != 1 ) { + ERROR("Invalid value for " + "--fix-profile-radius\n"); + return 1; + } + break; + + case 23 : + if ( sscanf(optarg, "%f", &iargs.fix_bandwidth) != 1 ) { + ERROR("Invalid value for --fix-bandwidth\n"); + return 1; + } + break; + + case 24 : + if ( sscanf(optarg, "%f", &iargs.fix_divergence) != 1 ) { + ERROR("Invalid value for --fix-divergence\n"); + return 1; + } + break; + case 0 : break; @@ -481,12 +512,23 @@ int main(int argc, char *argv[]) iargs.peaks = PEAK_ZAEF; } else if ( strcmp(speaks, "hdf5") == 0 ) { iargs.peaks = PEAK_HDF5; + } else if ( strcmp(speaks, "cxi") == 0 ) { + iargs.peaks = PEAK_CXI; } else { ERROR("Unrecognised peak detection method '%s'\n", speaks); return 1; } free(speaks); + /* Set default path for peaks, if appropriate */ + if ( iargs.hdf5_peak_path == NULL ) { + if ( iargs.peaks == PEAK_HDF5 ) { + iargs.hdf5_peak_path = strdup("/processing/hitfinder/peakinfo"); + } else if ( iargs.peaks == PEAK_CXI ) { + iargs.hdf5_peak_path = strdup("/entry_1/result_1"); + } + } + if ( prefix == NULL ) { prefix = strdup(""); } else { @@ -653,11 +695,6 @@ int main(int argc, char *argv[]) free(int_diag); - if ( (n_proc > 1) && (iargs.int_diag != INTDIAG_NONE) ) { - n_proc = 1; - STATUS("Ignored \"-j\" because you used --int-diag.\n"); - } - } st = open_stream_for_write_2(outfile, geom_filename, argc, argv); diff --git a/src/list_events.c b/src/list_events.c new file mode 100644 index 00000000..1ef82701 --- /dev/null +++ b/src/list_events.c @@ -0,0 +1,195 @@ +/* + * list_events.c + * + * Generate event lists + * + * Copyright © 2015 Deutsches Elektronen-Synchrotron DESY, + * a research centre of the Helmholtz Association. + * + * Authors: + * 2015 Thomas White <taw@physics.org> + * + * This file is part of CrystFEL. + * + * CrystFEL is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * CrystFEL is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with CrystFEL. If not, see <http://www.gnu.org/licenses/>. + * + */ + + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#include <stdarg.h> +#include <stdlib.h> +#include <stdio.h> +#include <string.h> +#include <unistd.h> +#include <getopt.h> + +#include "version.h" +#include "utils.h" +#include "detector.h" +#include "hdf5-file.h" + + +static void show_help(const char *s) +{ + printf("Syntax: %s [options] -i files.lst -o events.lst " + "-g geometry.geom\n\n", s); + printf( +"Generate event lists.\n" +"\n" +" -h, --help Display this help message.\n" +" --version Print CrystFEL version number and exit.\n" +"\n" +" -i, --input=<file> Input filename (list of multi-event filenames).\n" +" -g, --geometry=<file> Get data layout from geometry file.\n" +" -o, --output=<file> Output filename (list of events).\n" +); +} + + +int main(int argc, char *argv[]) +{ + int c; + char *input = NULL; + char *output = NULL; + char *geom = NULL; + char *rval; + FILE *ifh; + FILE *ofh; + struct detector *det; + + /* Long options */ + const struct option longopts[] = { + {"help", 0, NULL, 'h'}, + {"version", 0, NULL, 2 }, + {"input", 1, NULL, 'i'}, + {"geometry", 1, NULL, 'g'}, + {"output", 1, NULL, 'o'}, + {0, 0, NULL, 0} + }; + + /* Short options */ + while ((c = getopt_long(argc, argv, "hi:g:o:", + longopts, NULL)) != -1) { + + switch (c) { + + case 'h' : + show_help(argv[0]); + return 0; + + case 2 : + printf("CrystFEL: " CRYSTFEL_VERSIONSTRING "\n"); + printf(CRYSTFEL_BOILERPLATE"\n"); + return 0; + + case 'o' : + output = strdup(optarg); + break; + + case 'i' : + input = strdup(optarg); + break; + + case 'g' : + geom = strdup(optarg); + break; + + case 0 : + break; + + case '?' : + break; + + default : + ERROR("Unhandled option '%c'\n", c); + break; + + } + + } + + if ( (input == NULL) || (output == NULL) || (geom == NULL) ) { + ERROR("You must specify at least the input, output and geometry" + " filenames.\n"); + return 1; + } + + ifh = fopen(input, "r"); + if ( ifh == NULL ) { + ERROR("Couldn't open '%s'\n", input); + return 1; + } + + ofh = fopen(output, "w"); + if ( ofh == NULL ) { + ERROR("Couldn't open '%s'\n", output); + return 1; + } + + det = get_detector_geometry(geom, NULL); + if ( det == NULL ) { + ERROR("Failed to read '%s'\n", geom); + return 1; + } + + if ( (det->path_dim == 0) && (det->dim_dim == 0) ) { + ERROR("This does not look like a multi-event geometry file.\n"); + ERROR("Are you sure you need to use list_events instead of " + "just 'find' or 'ls'?\n"); + return 1; + } + + do { + + char filename[1024]; + int i; + + rval = fgets(filename, 1024, ifh); + if ( rval != NULL ) { + + struct event_list *evlist; + struct hdfile *hdfile; + + chomp(filename); + + hdfile = hdfile_open(filename); + if ( hdfile == NULL ) { + ERROR("Failed to open '%s'\n", filename); + ERROR("Aborting creation of event list.\n"); + return 1; + } + + evlist = fill_event_list(hdfile, det); + + for ( i=0; i<evlist->num_events; i++ ) { + char *str = get_event_string(evlist->events[i]); + fprintf(ofh, "%s %s\n", filename, str); + free(str); + } + + STATUS("%i events found in %s\n", evlist->num_events, + filename); + + free_event_list(evlist); + hdfile_close(hdfile); + } + + } while ( rval != NULL ); + + return 0; +} diff --git a/src/merge.c b/src/merge.c new file mode 100644 index 00000000..1595aea3 --- /dev/null +++ b/src/merge.c @@ -0,0 +1,248 @@ +/* + * merge.c + * + * Parallel weighted merging of intensities + * + * Copyright © 2012-2015 Deutsches Elektronen-Synchrotron DESY, + * a research centre of the Helmholtz Association. + * + * Authors: + * 2010-2015 Thomas White <taw@physics.org> + * + * This file is part of CrystFEL. + * + * CrystFEL is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * CrystFEL is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with CrystFEL. If not, see <http://www.gnu.org/licenses/>. + * + */ + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + + +#include <stdlib.h> +#include <assert.h> +#include <gsl/gsl_matrix.h> +#include <gsl/gsl_vector.h> +#include <gsl/gsl_linalg.h> +#include <gsl/gsl_eigen.h> +#include <gsl/gsl_fit.h> + +#include "image.h" +#include "peaks.h" +#include "symmetry.h" +#include "geometry.h" +#include "cell.h" +#include "utils.h" +#include "reflist.h" +#include "cell-utils.h" + + +/* Minimum partiality of a reflection for it to be merged */ +#define MIN_PART_MERGE (0.05) + + +struct merge_queue_args +{ + RefList *full; + pthread_rwlock_t full_lock; + Crystal **crystals; + int n_started; + PartialityModel pmodel; + double push_res; +}; + + +struct merge_worker_args +{ + struct merge_queue_args *qargs; + Crystal *crystal; + int crystal_number; +}; + + +static void *create_merge_job(void *vqargs) +{ + struct merge_worker_args *wargs; + struct merge_queue_args *qargs = vqargs; + + wargs = malloc(sizeof(struct merge_worker_args)); + wargs->qargs = qargs; + wargs->crystal_number = qargs->n_started; + wargs->crystal = qargs->crystals[qargs->n_started++]; + + return wargs; +} + + +static void run_merge_job(void *vwargs, int cookie) +{ + struct merge_worker_args *wargs = vwargs; + Crystal *cr = wargs->crystal; + RefList *full = wargs->qargs->full; + double push_res = wargs->qargs->push_res; + Reflection *refl; + RefListIterator *iter; + double G, B; + + /* If this crystal's scaling was dodgy, it doesn't contribute to the + * merged intensities */ + if ( crystal_get_user_flag(cr) != 0 ) return; + + G = crystal_get_osf(cr); + B = crystal_get_Bfac(cr); + + for ( refl = first_refl(crystal_get_reflections(cr), &iter); + refl != NULL; + refl = next_refl(refl, iter) ) + { + Reflection *f; + signed int h, k, l; + double mean, sumweight, M2, temp, delta, R; + double corr, res, w, esd; + + if ( get_partiality(refl) < MIN_PART_MERGE ) continue; + + get_indices(refl, &h, &k, &l); + pthread_rwlock_rdlock(&wargs->qargs->full_lock); + f = find_refl(full, h, k, l); + if ( f == NULL ) { + + /* Swap read lock for write lock */ + pthread_rwlock_unlock(&wargs->qargs->full_lock); + pthread_rwlock_wrlock(&wargs->qargs->full_lock); + + /* In the gap between the unlock and the wrlock, the + * reflection might have been created by another thread. + * So, we must check again */ + f = find_refl(full, h, k, l); + if ( f == NULL ) { + f = add_refl(full, h, k, l); + lock_reflection(f); + pthread_rwlock_unlock(&wargs->qargs->full_lock); + set_intensity(f, 0.0); + set_temp1(f, 0.0); + set_temp2(f, 0.0); + + } else { + + /* Someone else created it */ + lock_reflection(f); + pthread_rwlock_unlock(&wargs->qargs->full_lock); + + } + + } else { + + lock_reflection(f); + pthread_rwlock_unlock(&wargs->qargs->full_lock); + + } + + mean = get_intensity(f); + sumweight = get_temp1(f); + M2 = get_temp2(f); + + res = resolution(crystal_get_cell(cr), h, k, l); + + if ( 2.0*res > crystal_get_resolution_limit(cr)+push_res ) { + unlock_reflection(f); + continue; + } + + /* Total (multiplicative) correction factor */ + corr = exp(-G) * exp(B*res*res) * get_lorentz(refl) + / get_partiality(refl); + if ( isnan(corr) ) { + ERROR("NaN corr:\n"); + ERROR("G = %f, B = %e\n", G, B); + ERROR("res = %e\n", res); + ERROR("p = %f\n", get_partiality(refl)); + } + + esd = get_esd_intensity(refl) * corr; + w = 1.0; + + /* Running mean and variance calculation */ + temp = w + sumweight; + delta = get_intensity(refl)*corr - mean; + R = delta * w / temp; + set_intensity(f, mean + R); + set_temp2(f, M2 + sumweight * delta * R); + set_temp1(f, temp); + set_redundancy(f, get_redundancy(f)+1); + unlock_reflection(f); + } +} + + +static void finalise_merge_job(void *vqargs, void *vwargs) +{ + free(vwargs); +} + + +RefList *merge_intensities(Crystal **crystals, int n, int n_threads, + PartialityModel pmodel, int min_meas, + double push_res) +{ + RefList *full; + RefList *full2; + struct merge_queue_args qargs; + Reflection *refl; + RefListIterator *iter; + + full = reflist_new(); + + qargs.full = full; + qargs.n_started = 0; + qargs.crystals = crystals; + qargs.pmodel = pmodel; + qargs.push_res = push_res; + pthread_rwlock_init(&qargs.full_lock, NULL); + + run_threads(n_threads, run_merge_job, create_merge_job, + finalise_merge_job, &qargs, n, 0, 0, 0); + + pthread_rwlock_destroy(&qargs.full_lock); + + /* Calculate ESDs from variances, including only reflections with + * enough measurements */ + full2 = reflist_new(); + if ( full2 == NULL ) return NULL; + for ( refl = first_refl(full, &iter); + refl != NULL; + refl = next_refl(refl, iter) ) + { + double var; + int red; + + red = get_redundancy(refl); + var = get_temp2(refl) / get_temp1(refl); + set_esd_intensity(refl, sqrt(var)/sqrt(red)); + + if ( red >= min_meas ) { + + signed int h, k, l; + Reflection *r2; + + get_indices(refl, &h, &k, &l); + r2 = add_refl(full2, h, k, l); + copy_data(r2, refl); + } + } + + reflist_free(full); + return full2; +} diff --git a/src/hrs-scaling.h b/src/merge.h index 16368b79..b8af63b8 100644 --- a/src/hrs-scaling.h +++ b/src/merge.h @@ -1,13 +1,13 @@ /* - * hrs-scaling.h + * merge.h * - * Intensity scaling using generalised HRS target function + * Parallel weighted merging of intensities * - * Copyright © 2012-2013 Deutsches Elektronen-Synchrotron DESY, + * Copyright © 2012-2015 Deutsches Elektronen-Synchrotron DESY, * a research centre of the Helmholtz Association. * * Authors: - * 2010-2013 Thomas White <taw@physics.org> + * 2010-2015 Thomas White <taw@physics.org> * * This file is part of CrystFEL. * @@ -26,8 +26,8 @@ * */ -#ifndef HRS_SCALING_H -#define HRS_SCALING_H +#ifndef MERGE_H +#define MERGE_H #ifdef HAVE_CONFIG_H @@ -39,9 +39,8 @@ #include "reflist.h" #include "geometry.h" -extern RefList *scale_intensities(Crystal **crystals, int n, int n_threads, - int noscale, PartialityModel pmodel, - int min_redundancy); +extern RefList *merge_intensities(Crystal **crystals, int n, int n_threads, + PartialityModel pmodel, int min_meas, + double push_res); - -#endif /* HRS_SCALING_H */ +#endif /* MERGE */ diff --git a/src/partial_sim.c b/src/partial_sim.c index c0d9aaaa..867183da 100644 --- a/src/partial_sim.c +++ b/src/partial_sim.c @@ -241,6 +241,7 @@ static void show_help(const char *s) " --profile-radius Reciprocal space reflection profile radius in m^-1.\n" " Default 0.001e9 m^-1\n" " --photon-energy Photon energy in eV. Default 9000.\n" +" --really-random Be non-deterministic.\n" "\n" ); } @@ -414,6 +415,27 @@ static void finalise_job(void *vqargs, void *vwargs) } +static void fixup_geom(struct detector *det) +{ + int i; + + for ( i=0; i<det->n_panels; i++ ) { + det->panels[i].clen += det->panels[i].coffset; + } +} + + +static int geom_contains_references(struct detector *det) +{ + int i; + + for ( i=0; i<det->n_panels; i++ ) { + if ( det->panels[i].clen_from != NULL ) return 1; + } + return 0; +} + + int main(int argc, char *argv[]) { int c; @@ -707,6 +729,13 @@ int main(int argc, char *argv[]) ERROR("The value given on the command line " "(with --photon-energy) will be used instead.\n"); } + if ( geom_contains_references(det) ) { + ERROR("Geometry file contains a reference to an HDF5 location" + " for the camera length. Change it to a numerical value " + " and try again.\n"); + return 1; + } + fixup_geom(det); if ( sym_str == NULL ) sym_str = strdup("1"); sym = get_pointgroup(sym_str); @@ -762,6 +791,7 @@ int main(int argc, char *argv[]) free(output_file); image.det = det; + image.beam = &beam; image.width = det->max_fs + 1; image.height = det->max_ss + 1; @@ -776,6 +806,8 @@ int main(int argc, char *argv[]) image.num_peaks = 0; image.num_saturated_peaks = 0; image.spectrum_size = 0; + image.spectrum = NULL; + image.serial = 0; image.event = NULL; STATUS("Simulation parameters:\n"); diff --git a/src/partialator.c b/src/partialator.c index e5f965ed..9156536c 100644 --- a/src/partialator.c +++ b/src/partialator.c @@ -3,11 +3,11 @@ * * Scaling and post refinement for coherent nanocrystallography * - * Copyright © 2012-2014 Deutsches Elektronen-Synchrotron DESY, + * Copyright © 2012-2015 Deutsches Elektronen-Synchrotron DESY, * a research centre of the Helmholtz Association. * * Authors: - * 2010-2013 Thomas White <taw@physics.org> + * 2010-2015 Thomas White <taw@physics.org> * * This file is part of CrystFEL. * @@ -50,11 +50,13 @@ #include <thread-pool.h> #include <reflist.h> #include <reflist-utils.h> +#include <cell.h> +#include <cell-utils.h> #include "version.h" #include "post-refinement.h" -#include "hrs-scaling.h" -#include "scaling-report.h" +#include "merge.h" +#include "rejection.h" static void show_help(const char *s) @@ -75,6 +77,7 @@ static void show_help(const char *s) " --min-measurements=<n> Minimum number of measurements to require.\n" " --no-polarisation Disable polarisation correction.\n" " --max-adu=<n> Saturation value of detector.\n" +" --push-res=<n> Merge higher than apparent resolution cutoff.\n" " -j <n> Run <n> analyses in parallel.\n"); } @@ -84,6 +87,7 @@ struct refine_args RefList *full; Crystal *crystal; PartialityModel pmodel; + int no_scale; struct prdata prdata; }; @@ -94,8 +98,11 @@ struct queue_args int n_done; Crystal **crystals; int n_crystals; - struct srdata *srdata; struct refine_args task_defaults; + double initial_residual; + double initial_free_residual; + double final_residual; + double final_free_residual; }; @@ -104,7 +111,8 @@ static void refine_image(void *task, int id) struct refine_args *pargs = task; Crystal *cr = pargs->crystal; - pargs->prdata = pr_refine(cr, pargs->full, pargs->pmodel); + pargs->prdata = pr_refine(cr, pargs->full, pargs->pmodel, + pargs->no_scale); } @@ -130,10 +138,10 @@ static void done_image(void *vqargs, void *task) struct refine_args *pargs = task; qargs->n_done++; - if ( pargs->prdata.refined ) { - qargs->srdata->n_refined += pargs->prdata.refined; - qargs->srdata->n_filtered += pargs->prdata.n_filtered; - } + qargs->initial_residual += pargs->prdata.initial_residual; + qargs->initial_free_residual += pargs->prdata.initial_free_residual; + qargs->final_residual += pargs->prdata.final_residual; + qargs->final_free_residual += pargs->prdata.final_free_residual; progress_bar(qargs->n_done, qargs->n_crystals, "Refining"); free(task); @@ -142,27 +150,29 @@ static void done_image(void *vqargs, void *task) static void refine_all(Crystal **crystals, int n_crystals, RefList *full, int nthreads, PartialityModel pmodel, - struct srdata *srdata) + int no_scale, + double *initial_residual, double *initial_free_residual, + double *final_residual, double *final_free_residual) { struct refine_args task_defaults; struct queue_args qargs; - /* If the partiality model is "p=1", this refinement is really, really - * easy... */ - if ( pmodel == PMODEL_UNITY ) return; - task_defaults.full = full; task_defaults.crystal = NULL; task_defaults.pmodel = pmodel; task_defaults.prdata.refined = 0; task_defaults.prdata.n_filtered = 0; + task_defaults.no_scale = no_scale; qargs.task_defaults = task_defaults; qargs.n_started = 0; qargs.n_done = 0; qargs.n_crystals = n_crystals; qargs.crystals = crystals; - qargs.srdata = srdata; + qargs.initial_residual = 0.0; + qargs.initial_free_residual = 0.0; + qargs.final_residual = 0.0; + qargs.final_free_residual = 0.0; /* Don't have threads which are doing nothing */ if ( n_crystals < nthreads ) nthreads = n_crystals; @@ -170,9 +180,10 @@ static void refine_all(Crystal **crystals, int n_crystals, run_threads(nthreads, refine_image, get_image, done_image, &qargs, n_crystals, 0, 0, 0); - STATUS("%5.2f eigenvalues filtered on final iteration per successfully " - "refined crystal\n", - (double)srdata->n_filtered/srdata->n_refined); + *initial_residual = qargs.initial_residual; + *initial_free_residual = qargs.initial_free_residual; + *final_residual = qargs.final_residual; + *final_free_residual = qargs.final_free_residual; } @@ -192,11 +203,29 @@ static void display_progress(int n_images, int n_crystals) static const char *str_flags(Crystal *cr) { - if ( crystal_get_user_flag(cr) ) { - return "N"; - } + switch ( crystal_get_user_flag(cr) ) { + + case 0 : + return "OK"; + + case 1 : + return "bad scaling"; + + case 2 : + return "not enough reflections"; + + case 3 : + return "PR solve failed"; + + case 4 : + return "PR lost too many reflections"; + + case 5 : + return "Early rejection"; - return "-"; + default : + return "Unknown flag"; + } } @@ -225,6 +254,186 @@ static RefList *apply_max_adu(RefList *list, double max_adu) } +static void skip_to_end(FILE *fh) +{ + int c; + do { + c = fgetc(fh); + } while ( (c != '\n') && (c != EOF) ); +} + + +static int set_initial_params(Crystal *cr, FILE *fh) +{ + if ( fh != NULL ) { + + int err; + int n; + float osf, B; + + err = fscanf(fh, "%i %f %f", &n, &osf, &B); + if ( err != 3 ) { + ERROR("Failed to read parameters.\n"); + return 1; + } + + crystal_set_osf(cr, osf); + crystal_set_Bfac(cr, B*1e-20); + + skip_to_end(fh); + + } else { + + crystal_set_osf(cr, 0.0); + crystal_set_Bfac(cr, 0.0); + + } + + return 0; +} + + +static void show_duds(Crystal **crystals, int n_crystals) +{ + int j; + int n_dud = 0; + int n_noscale = 0; + int n_noref = 0; + int n_solve = 0; + int n_early = 0; + int n_cc = 0; + + for ( j=0; j<n_crystals; j++ ) { + int flag; + flag = crystal_get_user_flag(crystals[j]); + if ( flag != 0 ) n_dud++; + switch ( flag ) { + + case 0: + break; + + case 1: + n_noscale++; + break; + + case 2: + n_noref++; + break; + + case 3: + n_solve++; + break; + + case 5: + n_early++; + break; + + case 6: + n_cc++; + break; + + default: + STATUS("Unknown flag %i\n", flag); + break; + } + } + + if ( n_dud ) { + STATUS("%i bad crystals:\n", n_dud); + STATUS(" %i scaling failed.\n", n_noscale); + STATUS(" %i not enough reflections.\n", n_noref); + STATUS(" %i solve failed.\n", n_solve); + STATUS(" %i early rejection.\n", n_early); + STATUS(" %i bad CC.\n", n_cc); + } +} + + +/* Flag a random 5% of reflections */ +static void select_free_reflections(RefList *list, gsl_rng *rng) +{ + Reflection *refl; + RefListIterator *iter; + + for ( refl = first_refl(list, &iter); + refl != NULL; + refl = next_refl(refl, iter) ) + { + set_flag(refl, random_flat(rng, 1.0) > 0.95); + } +} + + +static void write_to_pgraph(FILE *fh, RefList *list, RefList *full, Crystal *cr, + int fr) +{ + Reflection *refl; + RefListIterator *iter; + double G = crystal_get_osf(cr); + double B = crystal_get_Bfac(cr); + UnitCell *cell = crystal_get_cell(cr); + + for ( refl = first_refl(list, &iter); + refl != NULL; + refl = next_refl(refl, iter) ) + { + signed int h, k, l; + double pobs, pcalc; + double res, corr, Ipart; + Reflection *match; + + if ( !get_flag(refl) ) continue; /* Not free-flagged */ + + get_indices(refl, &h, &k, &l); + res = resolution(cell, h, k, l); + if ( 2.0*res > crystal_get_resolution_limit(cr) ) continue; + + match = find_refl(full, h, k, l); + if ( match == NULL ) continue; + + /* Calculated partiality */ + pcalc = get_partiality(refl); + + /* Observed partiality */ + corr = exp(-G) * exp(B*res*res) * get_lorentz(refl); + Ipart = get_intensity(refl) * corr; + pobs = Ipart / get_intensity(match); + + fprintf(fh, "%5i %4i %4i %4i %8.4f %8.3f %8.3f\n", + fr, h, k, l, 2*res/1e9, pcalc, pobs); + + } +} + + +static void write_pgraph(RefList *full, Crystal **crystals, int n_crystals, + int iter) +{ + FILE *fh; + char tmp[256]; + int i; + + snprintf(tmp, 256, "pgraph-iter%i.dat", iter); + + fh = fopen(tmp, "w"); + if ( fh == NULL ) { + ERROR("Failed to open '%s'\n", tmp); + return; + } + + fprintf(fh, " fr h k l 1/d(nm) pcalc pobs\n"); + + for ( i=0; i<n_crystals; i++ ) { + if ( crystal_get_user_flag(crystals[i]) != 0 ) continue; + write_to_pgraph(fh, crystal_get_reflections(crystals[i]), full, + crystals[i], i); + } + + fclose(fh); + +} + + int main(int argc, char *argv[]) { int c; @@ -240,17 +449,19 @@ int main(int argc, char *argv[]) int n_images = 0; int n_crystals = 0; char cmdline[1024]; - SRContext *sr; - int noscale = 0; + int no_scale = 0; Stream *st; Crystal **crystals; char *pmodel_str = NULL; PartialityModel pmodel = PMODEL_SCSPHERE; int min_measurements = 2; char *rval; - struct srdata srdata; int polarisation = 1; double max_adu = +INFINITY; + char *sparams_fn = NULL; + FILE *sparams_fh; + double push_res = 0.0; + gsl_rng *rng; /* Long options */ const struct option longopts[] = { @@ -266,8 +477,11 @@ int main(int argc, char *argv[]) {"min-measurements", 1, NULL, 2}, {"max-adu", 1, NULL, 3}, + {"start-params", 1, NULL, 4}, + {"push-res", 1, NULL, 5}, + {"res-push", 1, NULL, 5}, /* compat */ - {"no-scale", 0, &noscale, 1}, + {"no-scale", 0, &no_scale, 1}, {"no-polarisation", 0, &polarisation, 0}, {"no-polarization", 0, &polarisation, 0}, {"polarisation", 0, &polarisation, 1}, /* compat */ @@ -340,6 +554,20 @@ int main(int argc, char *argv[]) } break; + case 4 : + sparams_fn = strdup(optarg); + break; + + case 5 : + errno = 0; + push_res = strtod(optarg, &rval); + if ( *rval != '\0' ) { + ERROR("Invalid value for --push-res.\n"); + return 1; + } + push_res = push_res*1e9; + break; + case 0 : break; @@ -392,12 +620,27 @@ int main(int argc, char *argv[]) } gsl_set_error_handler_off(); + rng = gsl_rng_alloc(gsl_rng_mt19937); /* Fill in what we know about the images so far */ n_images = 0; n_crystals = 0; images = NULL; crystals = NULL; + if ( sparams_fn != NULL ) { + char line[1024]; + sparams_fh = fopen(sparams_fn, "r"); + if ( sparams_fh == NULL ) { + ERROR("Failed to open '%s'\n", sparams_fn); + return 1; + } + fgets(line, 1024, sparams_fh); + STATUS("Reading initial scaling factors (G,B) from '%s'\n", + sparams_fn); + free(sparams_fn); + } else { + sparams_fh = NULL; + } do { @@ -460,10 +703,18 @@ int main(int argc, char *argv[]) cur); } + select_free_reflections(cr_refl, rng); + as = asymmetric_indices(cr_refl, sym); crystal_set_reflections(cr, as); + crystal_set_user_flag(cr, 0); reflist_free(cr_refl); + if ( set_initial_params(cr, sparams_fh) ) { + ERROR("Failed to set initial parameters\n"); + return 1; + } + n_crystals++; } @@ -475,6 +726,7 @@ int main(int argc, char *argv[]) } while ( 1 ); display_progress(n_images, n_crystals); fprintf(stderr, "\n"); + if ( sparams_fh != NULL ) fclose(sparams_fh); close_stream(st); @@ -484,102 +736,101 @@ int main(int argc, char *argv[]) for ( j=0; j<images[i].n_crystals; j++ ) { Crystal *cryst; - int n_gained = 0; - int n_lost = 0; - double mean_p_change = 0.0; cryst = images[i].crystals[j]; crystal_set_image(cryst, &images[i]); /* Now it's safe to do the following */ - update_partialities_2(cryst, pmodel, &n_gained, &n_lost, - &mean_p_change); - assert(n_gained == 0); /* That'd just be silly */ + update_partialities(cryst, pmodel); } } + /* Make a first pass at cutting out crap */ + STATUS("Checking patterns.\n"); + //early_rejection(crystals, n_crystals); + /* Make initial estimates */ - STATUS("Performing initial scaling.\n"); - if ( noscale ) STATUS("Scale factors fixed at 1.\n"); - full = scale_intensities(crystals, n_crystals, - nthreads, noscale, pmodel, min_measurements); + full = merge_intensities(crystals, n_crystals, nthreads, pmodel, + min_measurements, push_res); - srdata.crystals = crystals; - srdata.n = n_crystals; - srdata.full = full; - srdata.n_filtered = 0; - srdata.n_refined = 0; + check_rejection(crystals, n_crystals, full); - sr = sr_titlepage(crystals, n_crystals, "scaling-report.pdf", - infile, cmdline); - sr_iteration(sr, 0, &srdata); + show_duds(crystals, n_crystals); + + write_pgraph(full, crystals, n_crystals, 0); /* Iterate */ for ( i=0; i<n_iter; i++ ) { - int n_noscale = 0; - int n_noref = 0; - int n_solve = 0; - int n_lost = 0; - int n_dud = 0; - int j; - STATUS("Post refinement cycle %i of %i\n", i+1, n_iter); + double init_dev, init_free_dev; + double final_dev, final_free_dev; - srdata.n_filtered = 0; + STATUS("Refinement cycle %i of %i\n", i+1, n_iter); - /* Refine the geometry of all patterns to get the best fit */ + /* Refine all crystals to get the best fit */ refine_all(crystals, n_crystals, full, nthreads, pmodel, - &srdata); - - for ( j=0; j<n_crystals; j++ ) { - int flag; - flag = crystal_get_user_flag(crystals[j]); - if ( flag != 0 ) n_dud++; - if ( flag == 1 ) { - n_noscale++; - } else if ( flag == 2 ) { - n_noref++; - } else if ( flag == 3 ) { - n_solve++; - } else if ( flag == 4 ) { - n_lost++; - } - } + no_scale, &init_dev, &init_free_dev, + &final_dev, &final_free_dev); + + STATUS("Overall residual: initial = %e, final = %e\n", + init_dev, final_dev); + STATUS("Overall free residual: initial = %e, final = %e\n", + init_free_dev, final_free_dev); + + show_duds(crystals, n_crystals); + check_rejection(crystals, n_crystals, full); - if ( n_dud ) { - STATUS("%i crystals could not be refined this cycle.\n", - n_dud); - STATUS("%i scaling failed.\n", n_noscale); - STATUS("%i not enough reflections.\n", n_noref); - STATUS("%i solve failed.\n", n_solve); - STATUS("%i lost too many reflections.\n", n_lost); - } /* Re-estimate all the full intensities */ reflist_free(full); - full = scale_intensities(crystals, n_crystals, nthreads, - noscale, pmodel, min_measurements); - - srdata.full = full; + full = merge_intensities(crystals, n_crystals, nthreads, + pmodel, min_measurements, push_res); - sr_iteration(sr, i+1, &srdata); + write_pgraph(full, crystals, n_crystals, i+1); } - sr_finish(sr); - /* Output results */ write_reflist(outfile, full); + /* Output split results */ + char tmp[1024]; + RefList *split; + Crystal *crystals1[n_crystals]; + Crystal *crystals2[n_crystals]; + int n_crystals1 = 0; + int n_crystals2 = 0; + for ( i=0; i<n_crystals; i++ ) { + if ( i % 2 ) { + crystals1[n_crystals1] = crystals[i]; + n_crystals1++; + } else { + crystals2[n_crystals2] = crystals[i]; + n_crystals2++; + } + } + snprintf(tmp, 1024, "%s1", outfile); + split = merge_intensities(crystals1, n_crystals1, nthreads, + pmodel, min_measurements, push_res); + write_reflist(tmp, split); + reflist_free(split); + snprintf(tmp, 1024, "%s2", outfile); + split = merge_intensities(crystals2, n_crystals2, nthreads, + pmodel, min_measurements, push_res); + write_reflist(tmp, split); + reflist_free(split); + /* Dump parameters */ FILE *fh; fh = fopen("partialator.params", "w"); if ( fh == NULL ) { ERROR("Couldn't open partialator.params!\n"); } else { + fprintf(fh, " cr OSF relB div flag\n"); for ( i=0; i<n_crystals; i++ ) { - fprintf(fh, "%4i %5.2f %8.5e %s\n", i, + fprintf(fh, "%4i %10.5f %10.2f %8.5e %s\n", i, crystal_get_osf(crystals[i]), + crystal_get_Bfac(crystals[i])*1e20, crystal_get_image(crystals[i])->div, str_flags(crystals[i])); } @@ -587,6 +838,7 @@ int main(int argc, char *argv[]) } /* Clean up */ + gsl_rng_free(rng); for ( i=0; i<n_crystals; i++ ) { reflist_free(crystal_get_reflections(crystals[i])); crystal_free(crystals[i]); diff --git a/src/pattern_sim.c b/src/pattern_sim.c index 4ee77c19..e37fd936 100644 --- a/src/pattern_sim.c +++ b/src/pattern_sim.c @@ -97,6 +97,7 @@ static void show_help(const char *s) " --beam-bandwidth Beam bandwidth as a fraction. Default 1%%.\n" " --photon-energy Photon energy in eV. Default 9000.\n" " --nphotons Number of photons per X-ray pulse. Default 1e12.\n" +" --beam-radius Radius of beam in metres (default 1e-6).\n" ); } @@ -661,7 +662,11 @@ int main(int argc, char *argv[]) STATUS("Simulation parameters:\n"); STATUS(" Photon energy: %.2f eV (wavelength %.5f A)\n", photon_energy, image.lambda*1e10); + STATUS(" Number of photons: %.0f (%.2f mJ)\n", nphotons, + eV_to_J(photon_energy)*1e3); STATUS(" Beam divergence: not simulated\n"); + STATUS(" Beam radius: %.2f microns\n", + beam_radius*1e6); STATUS(" Background: %.2f photons\n", background); switch ( spectrum_type ) { diff --git a/src/post-refinement.c b/src/post-refinement.c index 4dae5096..ccf010e2 100644 --- a/src/post-refinement.c +++ b/src/post-refinement.c @@ -3,11 +3,11 @@ * * Post refinement * - * Copyright © 2012-2014 Deutsches Elektronen-Synchrotron DESY, + * Copyright © 2012-2015 Deutsches Elektronen-Synchrotron DESY, * a research centre of the Helmholtz Association. * * Authors: - * 2010-2014 Thomas White <taw@physics.org> + * 2010-2015 Thomas White <taw@physics.org> * * This file is part of CrystFEL. * @@ -54,7 +54,6 @@ /* Maximum number of iterations of NLSq to do for each image per macrocycle. */ #define MAX_CYCLES (10) - /* Returns dp(gauss)/dr at "r" */ static double gaussian_fraction_gradient(double r, double R) { @@ -175,31 +174,34 @@ static double volume_fraction(double rlow, double rhigh, double pr, } -/* Return the gradient of partiality wrt parameter 'k' given the current status - * of 'image'. */ -double p_gradient(Crystal *cr, int k, Reflection *refl, PartialityModel pmodel) +/* Return the gradient of "fx" wrt parameter 'k' given the current + * status of the crystal. */ +double gradient(Crystal *cr, int k, Reflection *refl, PartialityModel pmodel) { - double azi; double glow, ghigh; - double asx, asy, asz; - double bsx, bsy, bsz; - double csx, csy, csz; - double xl, yl, zl; - double ds; - signed int hs, ks, ls; double rlow, rhigh, p; - double philow, phihigh, phi; - double khigh, klow; - double tl, cet, cez; struct image *image = crystal_get_image(cr); double R = crystal_get_profile_radius(cr); - double D, psph; + double gr; + signed int hi, ki, li; + double s; + get_indices(refl, &hi, &ki, &li); + s = resolution(crystal_get_cell(cr), hi, ki, li); get_partial(refl, &rlow, &rhigh, &p); - if ( k == REF_R ) { + if ( k == GPARAM_OSF ) { + return 1.0; + } + + if ( k == GPARAM_BFAC ) { + return -s*s; + } + + if ( k == GPARAM_R ) { double Rglow, Rghigh; + double D, psph; D = rlow - rhigh; psph = volume_fraction(rlow, rhigh, R, pmodel); @@ -207,7 +209,8 @@ double p_gradient(Crystal *cr, int k, Reflection *refl, PartialityModel pmodel) Rglow = volume_fraction_rgradient(rlow, R, pmodel); Rghigh = volume_fraction_rgradient(rhigh, R, pmodel); - return 4.0*psph/(3.0*D) + (4.0*R/(3.0*D))*(Rglow - Rghigh); + gr = 4.0*psph/(3.0*D) + (4.0*R/(3.0*D))*(Rglow - Rghigh); + return gr/p; } @@ -215,78 +218,23 @@ double p_gradient(Crystal *cr, int k, Reflection *refl, PartialityModel pmodel) glow = partiality_gradient(rlow, R, pmodel, rlow, rhigh); ghigh = partiality_gradient(rhigh, R, pmodel, rlow, rhigh); - get_symmetric_indices(refl, &hs, &ks, &ls); - ds = 2.0 * resolution(crystal_get_cell(cr), hs, ks, ls); + if ( k == GPARAM_DIV ) { - cell_get_reciprocal(crystal_get_cell(cr), &asx, &asy, &asz, - &bsx, &bsy, &bsz, - &csx, &csy, &csz); - xl = hs*asx + ks*bsx + ls*csx; - yl = hs*asy + ks*bsy + ls*csy; - zl = hs*asz + ks*bsz + ls*csz; - - /* "low" gives the largest Ewald sphere (wavelength short => k large) - * "high" gives the smallest Ewald sphere (wavelength long => k small) - */ - klow = 1.0/(image->lambda - image->lambda*image->bw/2.0); - khigh = 1.0/(image->lambda + image->lambda*image->bw/2.0); - - tl = sqrt(xl*xl + yl*yl); - - cet = -sin(image->div/2.0) * klow; - cez = -cos(image->div/2.0) * klow; - philow = angle_between_2d(tl-cet, zl-cez, 0.0, 1.0); - - cet = -sin(image->div/2.0) * khigh; - cez = -cos(image->div/2.0) * khigh; - phihigh = angle_between_2d(tl-cet, zl-cez, 0.0, 1.0); - - /* Approximation: philow and phihigh are very similar */ - phi = (philow + phihigh) / 2.0; - - azi = atan2(yl, xl); - - /* For many gradients, just multiply the above number by the gradient - * of excitation error wrt whatever. */ - switch ( k ) { + double D, psph, ds; + signed int hs, ks, ls; - /* Cell parameters and orientation */ - case REF_ASX : - return - hs * sin(phi) * cos(azi) * (glow-ghigh); - - case REF_BSX : - return - ks * sin(phi) * cos(azi) * (glow-ghigh); - - case REF_CSX : - return - ls * sin(phi) * cos(azi) * (glow-ghigh); - - case REF_ASY : - return - hs * sin(phi) * sin(azi) * (glow-ghigh); - - case REF_BSY : - return - ks * sin(phi) * sin(azi) * (glow-ghigh); - - case REF_CSY : - return - ls * sin(phi) * sin(azi) * (glow-ghigh); - - case REF_ASZ : - return - hs * cos(phi) * (glow-ghigh); - - case REF_BSZ : - return - ks * cos(phi) * (glow-ghigh); - - case REF_CSZ : - return - ls * cos(phi) * (glow-ghigh); - - case REF_DIV : D = rlow - rhigh; psph = volume_fraction(rlow, rhigh, R, pmodel); - return (ds/2.0)*(glow+ghigh) - 4.0*R*psph*ds/(3.0*D*D); + get_symmetric_indices(refl, &hs, &ks, &ls); + ds = 2.0 * resolution(crystal_get_cell(cr), hs, ks, ls); + + gr = (ds/2.0)*(glow+ghigh) - 4.0*R*psph*ds/(3.0*D*D); + return gr/p; } - ERROR("No gradient defined for parameter %i\n", k); - abort(); + gr = r_gradient(crystal_get_cell(cr), k, refl, image) * (glow-ghigh); + return gr/p; } @@ -302,15 +250,15 @@ static void apply_cell_shift(UnitCell *cell, int k, double shift) switch ( k ) { - case REF_ASX : asx += shift; break; - case REF_ASY : asy += shift; break; - case REF_ASZ : asz += shift; break; - case REF_BSX : bsx += shift; break; - case REF_BSY : bsy += shift; break; - case REF_BSZ : bsz += shift; break; - case REF_CSX : csx += shift; break; - case REF_CSY : csy += shift; break; - case REF_CSZ : csz += shift; break; + case GPARAM_ASX : asx += shift; break; + case GPARAM_ASY : asy += shift; break; + case GPARAM_ASZ : asz += shift; break; + case GPARAM_BSX : bsx += shift; break; + case GPARAM_BSY : bsy += shift; break; + case GPARAM_BSZ : bsz += shift; break; + case GPARAM_CSX : csx += shift; break; + case GPARAM_CSY : csy += shift; break; + case GPARAM_CSZ : csz += shift; break; } cell_set_reciprocal(cell, asx, asy, asz, @@ -325,32 +273,46 @@ static void apply_shift(Crystal *cr, int k, double shift) double t; struct image *image = crystal_get_image(cr); + if ( isnan(shift) ) { + ERROR("Refusing NaN shift for parameter %i\n", k); + ERROR("Image serial %i\n", image->serial); + return; + } + switch ( k ) { - case REF_DIV : - if ( isnan(shift) ) { - ERROR("NaN divergence shift\n"); - } else { - image->div += shift; - if ( image->div < 0.0 ) image->div = 0.0; - } + case GPARAM_DIV : + image->div += shift; + if ( image->div < 0.0 ) image->div = 0.0; break; - case REF_R : + case GPARAM_R : t = crystal_get_profile_radius(cr); t += shift; crystal_set_profile_radius(cr, t); break; - case REF_ASX : - case REF_ASY : - case REF_ASZ : - case REF_BSX : - case REF_BSY : - case REF_BSZ : - case REF_CSX : - case REF_CSY : - case REF_CSZ : + case GPARAM_BFAC : + t = crystal_get_Bfac(cr); + t += shift; + crystal_set_Bfac(cr, t); + break; + + case GPARAM_OSF : + t = crystal_get_osf(cr); + t += shift; + crystal_set_osf(cr, t); + break; + + case GPARAM_ASX : + case GPARAM_ASY : + case GPARAM_ASZ : + case GPARAM_BSX : + case GPARAM_BSY : + case GPARAM_BSZ : + case GPARAM_CSX : + case GPARAM_CSY : + case GPARAM_CSZ : apply_cell_shift(crystal_get_cell(cr), k, shift); break; @@ -362,143 +324,10 @@ static void apply_shift(Crystal *cr, int k, double shift) } -static int check_eigen(gsl_vector *e_val, int verbose) -{ - int i; - double vmax, vmin; - const int n = e_val->size; - const double max_condition = 1e6; - int n_filt = 0; - - if ( verbose ) STATUS("Eigenvalues:\n"); - vmin = +INFINITY; - vmax = 0.0; - for ( i=0; i<n; i++ ) { - double val = gsl_vector_get(e_val, i); - if ( verbose ) STATUS("%i: %e\n", i, val); - if ( val > vmax ) vmax = val; - if ( val < vmin ) vmin = val; - } - - for ( i=0; i<n; i++ ) { - double val = gsl_vector_get(e_val, i); - if ( val < vmax/max_condition ) { - gsl_vector_set(e_val, i, 0.0); - n_filt++; - } - } - - vmin = +INFINITY; - vmax = 0.0; - for ( i=0; i<n; i++ ) { - double val = gsl_vector_get(e_val, i); - if ( val == 0.0 ) continue; - if ( val > vmax ) vmax = val; - if ( val < vmin ) vmin = val; - } - if ( verbose ) { - STATUS("Condition number: %e / %e = %5.2f\n", - vmax, vmin, vmax/vmin); - STATUS("%i out of %i eigenvalues filtered.\n", n_filt, n); - } - - return n_filt; -} - - -static gsl_vector *solve_svd(gsl_vector *v, gsl_matrix *M, int *n_filt, - int verbose) -{ - gsl_matrix *s_vec; - gsl_vector *s_val; - int err, n; - gsl_vector *shifts; - gsl_vector *SB; - gsl_vector *SinvX; - gsl_matrix *S; /* rescaling matrix due to Bricogne */ - gsl_matrix *AS; - gsl_matrix *SAS; - int i; - gsl_matrix *SAS_copy; - - n = v->size; - if ( v->size != M->size1 ) return NULL; - if ( v->size != M->size2 ) return NULL; - - /* Calculate the rescaling matrix S */ - S = gsl_matrix_calloc(n, n); - for ( i=0; i<n; i++ ) { - double sii = pow(gsl_matrix_get(M, i, i), -0.5); - gsl_matrix_set(S, i, i, sii); - } - - /* Calculate the matrix SAS, which we will be (not) inverting */ - AS = gsl_matrix_calloc(n, n); - SAS = gsl_matrix_calloc(n, n); - gsl_blas_dgemm(CblasNoTrans, CblasNoTrans, 1.0, M, S, 0.0, AS); - gsl_blas_dgemm(CblasNoTrans, CblasNoTrans, 1.0, S, AS, 0.0, SAS); - gsl_matrix_free(AS); - - /* Calculate the vector SB, which is the RHS of the equation */ - SB = gsl_vector_calloc(n); - gsl_blas_dgemv(CblasNoTrans, 1.0, S, v, 0.0, SB); - - if ( verbose ) { - STATUS("The equation after rescaling:\n"); - show_matrix_eqn(SAS, SB); - } - - SAS_copy = gsl_matrix_alloc(SAS->size1, SAS->size2); - gsl_matrix_memcpy(SAS_copy, SAS); - - /* Do the SVD */ - s_val = gsl_vector_calloc(n); - s_vec = gsl_matrix_calloc(n, n); - err = gsl_linalg_SV_decomp_jacobi(SAS, s_vec, s_val); - if ( err ) { - if ( verbose ) ERROR("SVD failed: %s\n", gsl_strerror(err)); - gsl_matrix_free(s_vec); - gsl_vector_free(s_val); - gsl_matrix_free(SAS); - gsl_matrix_free(S); - return NULL; - } - /* "SAS" is now "U" */ - - /* Filter the eigenvalues */ - *n_filt = check_eigen(s_val, verbose); - - gsl_matrix_free(SAS_copy); - - /* Solve the equation SAS.SinvX = SB */ - SinvX = gsl_vector_calloc(n); - err = gsl_linalg_SV_solve(SAS, s_vec, s_val, SB, SinvX); - gsl_vector_free(SB); - gsl_matrix_free(SAS); - gsl_matrix_free(s_vec); - gsl_vector_free(s_val); - - if ( err ) { - ERROR("Matrix solution failed: %s\n", gsl_strerror(err)); - gsl_matrix_free(S); - gsl_vector_free(SinvX); - return NULL; - } - - /* Calculate S.SinvX to get X, the shifts */ - shifts = gsl_vector_calloc(n); - gsl_blas_dgemv(CblasNoTrans, 1.0, S, SinvX, 0.0, shifts); - - gsl_matrix_free(S); - gsl_vector_free(SinvX); - - return shifts; -} - - /* Perform one cycle of post refinement on 'image' against 'full' */ static double pr_iterate(Crystal *cr, const RefList *full, - PartialityModel pmodel, int *n_filtered) + PartialityModel pmodel, int no_scale, int *n_filtered, + int verbose) { gsl_matrix *M; gsl_vector *v; @@ -509,14 +338,42 @@ static double pr_iterate(Crystal *cr, const RefList *full, RefList *reflections; double max_shift; int nref = 0; - const int verbose = 0; + int num_params = 0; + enum gparam rv[32]; + double G, B; *n_filtered = 0; + /* If partiality model is anything other than "unity", refine all the + * geometrical parameters */ + if ( pmodel != PMODEL_UNITY ) { + rv[num_params++] = GPARAM_ASX; + rv[num_params++] = GPARAM_ASY; + rv[num_params++] = GPARAM_ASZ; + rv[num_params++] = GPARAM_BSX; + rv[num_params++] = GPARAM_BSY; + rv[num_params++] = GPARAM_BSZ; + rv[num_params++] = GPARAM_CSX; + rv[num_params++] = GPARAM_CSY; + rv[num_params++] = GPARAM_CSZ; + //rv[num_params++] = GPARAM_R; + } + + /* If we are scaling, refine scale factors (duh) */ + if ( !no_scale ) { + rv[num_params++] = GPARAM_OSF; + rv[num_params++] = GPARAM_BFAC; + } + + if ( num_params == 0 ) return 0.0; + reflections = crystal_get_reflections(cr); - M = gsl_matrix_calloc(NUM_PARAMS, NUM_PARAMS); - v = gsl_vector_calloc(NUM_PARAMS); + M = gsl_matrix_calloc(num_params, num_params); + v = gsl_vector_calloc(num_params); + + G = crystal_get_osf(cr); + B = crystal_get_Bfac(cr); /* Construct the equations, one per reflection in this image */ for ( refl = first_refl(reflections, &iter); @@ -524,54 +381,59 @@ static double pr_iterate(Crystal *cr, const RefList *full, refl = next_refl(refl, iter) ) { signed int ha, ka, la; - double I_full, delta_I; + double I_full, delta_I, esd; double w; double I_partial; int k; - double p, l; + double p, L, s; + double fx; Reflection *match; - double gradients[NUM_PARAMS]; + double gradients[num_params]; + + /* If reflection is free-flagged, don't use it here */ + if ( get_flag(refl) ) continue; /* Find the full version */ get_indices(refl, &ha, &ka, &la); match = find_refl(full, ha, ka, la); if ( match == NULL ) continue; - if ( (get_intensity(refl) < 3.0*get_esd_intensity(refl)) - || (get_partiality(refl) < MIN_PART_REFINE) - || (get_redundancy(match) < 2) ) continue; - + /* Merged intensitty */ I_full = get_intensity(match); - /* Actual measurement of this reflection from this pattern? */ - I_partial = get_intensity(refl) / crystal_get_osf(cr); + /* Actual measurement of this reflection from this pattern */ + I_partial = get_intensity(refl); + esd = get_esd_intensity(refl); + + if ( (get_partiality(refl) < MIN_PART_REFINE) + || (get_redundancy(match) < 2) + || (I_full <= 0) || (I_partial < 3*esd) ) continue; + p = get_partiality(refl); - l = get_lorentz(refl); + L = get_lorentz(refl); + s = resolution(crystal_get_cell(cr), ha, ka, la); /* Calculate the weight for this reflection */ - w = pow(get_esd_intensity(refl), 2.0); - w += l * p * I_full * pow(get_esd_intensity(match), 2.0); - w = pow(w, -1.0); + w = 1.0; /* Calculate all gradients for this reflection */ - for ( k=0; k<NUM_PARAMS; k++ ) { - gradients[k] = p_gradient(cr, k, refl, pmodel) * l; + for ( k=0; k<num_params; k++ ) { + gradients[k] = gradient(cr, rv[k], refl, pmodel); } - for ( k=0; k<NUM_PARAMS; k++ ) { + for ( k=0; k<num_params; k++ ) { int g; double v_c, v_curr; - for ( g=0; g<NUM_PARAMS; g++ ) { + for ( g=0; g<num_params; g++ ) { double M_c, M_curr; /* Matrix is symmetric */ if ( g > k ) continue; - M_c = gradients[g] * gradients[k]; - M_c *= w * pow(I_full, 2.0); + M_c = w * gradients[g] * gradients[k]; M_curr = gsl_matrix_get(M, k, g); gsl_matrix_set(M, k, g, M_curr + M_c); @@ -579,8 +441,9 @@ static double pr_iterate(Crystal *cr, const RefList *full, } - delta_I = I_partial - (l * p * I_full); - v_c = w * delta_I * I_full * gradients[k]; + fx = G + log(p) - log(L) - B*s*s + log(I_full); + delta_I = log(I_partial) - fx; + v_c = w * delta_I * gradients[k]; v_curr = gsl_vector_get(v, k); gsl_vector_set(v, k, v_curr + v_c); @@ -591,10 +454,10 @@ static double pr_iterate(Crystal *cr, const RefList *full, if ( verbose ) { STATUS("The original equation:\n"); show_matrix_eqn(M, v); + STATUS("%i reflections went into the equations.\n", nref); } - //STATUS("%i reflections went into the equations.\n", nref); - if ( nref == 0 ) { + if ( nref < num_params ) { crystal_set_user_flag(cr, 2); gsl_matrix_free(M); gsl_vector_free(v); @@ -605,10 +468,10 @@ static double pr_iterate(Crystal *cr, const RefList *full, shifts = solve_svd(v, M, n_filtered, verbose); if ( shifts != NULL ) { - for ( param=0; param<NUM_PARAMS; param++ ) { + for ( param=0; param<num_params; param++ ) { double shift = gsl_vector_get(shifts, param); - apply_shift(cr, param, shift); - //STATUS("Shift %i: %e\n", param, shift); + if ( verbose ) STATUS("Shift %i: %e\n", param, shift); + apply_shift(cr, rv[param], shift); if ( fabs(shift) > max_shift ) max_shift = fabs(shift); } @@ -624,107 +487,73 @@ static double pr_iterate(Crystal *cr, const RefList *full, } -static double guide_dev(Crystal *cr, const RefList *full) +static double residual(Crystal *cr, const RefList *full, int verbose, int free) { double dev = 0.0; - - /* For each reflection */ + double G, B; Reflection *refl; RefListIterator *iter; + FILE *fh = NULL; + + if ( verbose ) { + fh = fopen("residual.dat", "w"); + } + + G = crystal_get_osf(cr); + B = crystal_get_Bfac(cr); for ( refl = first_refl(crystal_get_reflections(cr), &iter); refl != NULL; - refl = next_refl(refl, iter) ) { - - double G, p; + refl = next_refl(refl, iter) ) + { + double p, L, s, w; signed int h, k, l; - Reflection *full_version; - double I_full, I_partial; + Reflection *match; + double esd, I_full, I_partial; + double fx, dc; - if ( (get_intensity(refl) < 3.0*get_esd_intensity(refl)) - || (get_partiality(refl) < MIN_PART_REFINE) ) continue; + if ( free && !get_flag(refl) ) continue; get_indices(refl, &h, &k, &l); - assert((h!=0) || (k!=0) || (l!=0)); - - full_version = find_refl(full, h, k, l); - if ( full_version == NULL ) continue; - /* Some reflections may have recently become scalable, but - * scale_intensities() might not yet have been called, so the - * full version may not have been calculated yet. */ + match = find_refl(full, h, k, l); + if ( match == NULL ) continue; - G = crystal_get_osf(cr); p = get_partiality(refl); + L = get_lorentz(refl); I_partial = get_intensity(refl); - I_full = get_intensity(full_version); - //STATUS("%3i %3i %3i %5.2f %5.2f %5.2f %5.2f %5.2f\n", - // h, k, l, G, p, I_partial, I_full, - // I_partial - p*G*I_full); - - dev += pow(I_partial - p*G*I_full, 2.0); - - } - - return dev; -} - - -struct param_backup -{ - UnitCell *cell; - double profile_radius; - double div; -}; - - -static struct param_backup backup_crystal(Crystal *cr) -{ - struct param_backup b; - struct image *image = crystal_get_image(cr); - - b.cell = cell_new_from_cell(crystal_get_cell(cr)); - b.profile_radius = crystal_get_profile_radius(cr); - b.div = image->div; - - return b; -} - - -static void revert_crystal(Crystal *cr, struct param_backup b) -{ - double asx, asy, asz; - double bsx, bsy, bsz; - double csx, csy, csz; - struct image *image = crystal_get_image(cr); + I_full = get_intensity(match); + esd = get_esd_intensity(refl); + s = resolution(crystal_get_cell(cr), h, k, l); - cell_get_reciprocal(b.cell, &asx, &asy, &asz, - &bsx, &bsy, &bsz, - &csx, &csy, &csz); + if ( (get_partiality(refl) < MIN_PART_REFINE) + || (get_redundancy(match) < 2) + || (I_full <= 0) || (I_partial < 3*esd) ) continue; - cell_set_reciprocal(crystal_get_cell(cr), asx, asy, asz, - bsx, bsy, bsz, - csx, csy, csz); + fx = G + log(p) - log(L) - B*s*s + log(I_full); + dc = log(I_partial) - fx; + w = 1.0; + dev += w*dc*dc; - crystal_set_profile_radius(cr, b.profile_radius); - image->div = b.div; -} + if ( fh != NULL ) { + fprintf(fh, "%4i %4i %4i %e %.2f %e %f %f\n", + h, k, l, s, G, B, I_partial, I_full); + } + } + if ( fh != NULL ) fclose(fh); -static void free_backup_crystal(struct param_backup b) -{ - cell_free(b.cell); + return dev; } struct prdata pr_refine(Crystal *cr, const RefList *full, - PartialityModel pmodel) + PartialityModel pmodel, int no_scale) { - double dev; int i; - struct param_backup backup; - const int verbose = 0; + int verbose = 0; struct prdata prdata; - double mean_p_change = 0.0; + int done = 0; + double old_dev; prdata.refined = 0; prdata.n_filtered = 0; @@ -732,15 +561,18 @@ struct prdata pr_refine(Crystal *cr, const RefList *full, /* Don't refine crystal if scaling was bad */ if ( crystal_get_user_flag(cr) != 0 ) return prdata; + old_dev = residual(cr, full, 0, 0); + prdata.initial_free_residual = residual(cr, full, 0, 1); + prdata.initial_residual = old_dev; + if ( verbose ) { - dev = guide_dev(cr, full); STATUS("\n"); /* Deal with progress bar */ - STATUS("Before iteration: dev = %10.5e\n", - dev); + STATUS("Initial G=%.2f, B=%e\n", + crystal_get_osf(cr), crystal_get_Bfac(cr)); + STATUS("Initial dev = %10.5e, free dev = %10.5e\n", + old_dev, residual(cr, full, 0, 1)); } - backup = backup_crystal(cr); - i = 0; do { @@ -748,44 +580,37 @@ struct prdata pr_refine(Crystal *cr, const RefList *full, double bsx, bsy, bsz; double csx, csy, csz; double dev; - int n_total; - int n_gained = 0; - int n_lost = 0; - n_total = num_reflections(crystal_get_reflections(cr)); cell_get_reciprocal(crystal_get_cell(cr), &asx, &asy, &asz, &bsx, &bsy, &bsz, &csx, &csy, &csz); - pr_iterate(cr, full, pmodel, &prdata.n_filtered); + pr_iterate(cr, full, pmodel, no_scale, &prdata.n_filtered, 0); - update_partialities_2(cr, pmodel, &n_gained, &n_lost, - &mean_p_change); + update_partialities(cr, pmodel); - if ( verbose ) { - dev = guide_dev(cr, full); - STATUS("PR Iteration %2i: mean p change = %10.2f" - " dev = %10.5e, %i gained, %i lost, %i total\n", - i+1, mean_p_change, dev, - n_gained, n_lost, n_total); - } + dev = residual(cr, full, 0, 0); + if ( fabs(dev - old_dev) < dev*0.01 ) done = 1; - if ( 3*n_lost > n_total ) { - revert_crystal(cr, backup); - update_partialities_2(cr, pmodel, &n_gained, &n_lost, - &mean_p_change); - crystal_set_user_flag(cr, 4); - break; + if ( verbose ) { + STATUS("Iter %2i: dev = %10.5e, free dev = %10.5e\n", + i+1, dev, residual(cr, full, 0, 1)); } i++; + old_dev = dev; - } while ( (mean_p_change > 0.01) && (i < MAX_CYCLES) ); - - free_backup_crystal(backup); + } while ( i < MAX_CYCLES && !done ); if ( crystal_get_user_flag(cr) == 0 ) { prdata.refined = 1; } + prdata.final_free_residual = residual(cr, full, 0, 1); + prdata.final_residual = old_dev; + + if ( verbose ) { + STATUS("Final G=%.2f, B=%e\n", crystal_get_osf(cr), + crystal_get_Bfac(cr)); + } return prdata; } diff --git a/src/post-refinement.h b/src/post-refinement.h index a9c79ed6..fd2d771b 100644 --- a/src/post-refinement.h +++ b/src/post-refinement.h @@ -3,11 +3,11 @@ * * Post refinement * - * Copyright © 2012-2014 Deutsches Elektronen-Synchrotron DESY, + * Copyright © 2012-2015 Deutsches Elektronen-Synchrotron DESY, * a research centre of the Helmholtz Association. * * Authors: - * 2010-2014 Thomas White <taw@physics.org> + * 2010-2015 Thomas White <taw@physics.org> * * This file is part of CrystFEL. * @@ -43,36 +43,22 @@ #include "geometry.h" -/* Refineable parameters. - * Don't forget to add new things to backup_crystal() et al.! */ -enum { - REF_ASX, - REF_ASY, - REF_ASZ, - REF_BSX, - REF_BSY, - REF_BSZ, - REF_CSX, - REF_CSY, - REF_CSZ, - NUM_PARAMS, - REF_R, - REF_DIV, -}; - - struct prdata { int refined; int n_filtered; + double initial_residual; + double initial_free_residual; + double final_residual; + double final_free_residual; }; extern struct prdata pr_refine(Crystal *cr, const RefList *full, - PartialityModel pmodel); + PartialityModel pmodel, int no_scale); /* Exported so it can be poked by tests/pr_p_gradient_check */ -extern double p_gradient(Crystal *cr, int k, Reflection *refl, - PartialityModel pmodel); +extern double gradient(Crystal *cr, int k, Reflection *refl, + PartialityModel pmodel); #endif /* POST_REFINEMENT_H */ diff --git a/src/predict-refine.c b/src/predict-refine.c new file mode 100644 index 00000000..2d845feb --- /dev/null +++ b/src/predict-refine.c @@ -0,0 +1,757 @@ +/* + * predict-refine.c + * + * Prediction refinement + * + * Copyright © 2012-2015 Deutsches Elektronen-Synchrotron DESY, + * a research centre of the Helmholtz Association. + * + * Authors: + * 2010-2015 Thomas White <taw@physics.org> + * + * This file is part of CrystFEL. + * + * CrystFEL is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * CrystFEL is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with CrystFEL. If not, see <http://www.gnu.org/licenses/>. + * + */ + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + + +#include <stdlib.h> +#include <assert.h> +#include <gsl/gsl_matrix.h> +#include <gsl/gsl_vector.h> + +#include "image.h" +#include "geometry.h" +#include "cell-utils.h" + + +/* Maximum number of iterations of NLSq to do for each image per macrocycle. */ +#define MAX_CYCLES (10) + +/* Weighting of excitation error term (m^-1) compared to position term (m) */ +#define EXC_WEIGHT (4e-20) + +/* Parameters to refine */ +static const enum gparam rv[] = +{ + GPARAM_ASX, + GPARAM_ASY, + GPARAM_ASZ, + GPARAM_BSX, + GPARAM_BSY, + GPARAM_BSZ, + GPARAM_CSX, + GPARAM_CSY, + GPARAM_CSZ, + GPARAM_DETX, + GPARAM_DETY, +}; + +static const int num_params = 11; + +struct reflpeak { + Reflection *refl; + struct imagefeature *peak; + double Ih; /* normalised */ + struct panel *panel; /* panel the reflection appears on + * (we assume this never changes) */ +}; + + +static void twod_mapping(double fs, double ss, double *px, double *py, + struct panel *p) +{ + double xs, ys; + + xs = fs*p->fsx + ss*p->ssx; + ys = fs*p->fsy + ss*p->ssy; + + *px = (xs + p->cnx) / p->res; + *py = (ys + p->cny) / p->res; +} + + +static double r_dev(struct reflpeak *rp) +{ + /* Excitation error term */ + double rlow, rhigh, p; + get_partial(rp->refl, &rlow, &rhigh, &p); + return (rlow+rhigh)/2.0; +} + + +static double x_dev(struct reflpeak *rp, struct detector *det) +{ + /* Peak position term */ + double xpk, ypk, xh, yh; + double fsh, ssh; + twod_mapping(rp->peak->fs, rp->peak->ss, &xpk, &ypk, rp->panel); + get_detector_pos(rp->refl, &fsh, &ssh); + twod_mapping(fsh, ssh, &xh, &yh, rp->panel); + return xh-xpk; +} + + +static double y_dev(struct reflpeak *rp, struct detector *det) +{ + /* Peak position term */ + double xpk, ypk, xh, yh; + double fsh, ssh; + twod_mapping(rp->peak->fs, rp->peak->ss, &xpk, &ypk, rp->panel); + get_detector_pos(rp->refl, &fsh, &ssh); + twod_mapping(fsh, ssh, &xh, &yh, rp->panel); + return yh-ypk; +} + + +static void UNUSED write_pairs(const char *filename, struct reflpeak *rps, + int n, struct detector *det) +{ + int i; + FILE *fh; + + fh = fopen(filename, "w"); + if ( fh == NULL ) { + ERROR("Failed to open '%s'\n", filename); + return; + } + + for ( i=0; i<n; i++ ) { + + double write_fs, write_ss; + double fs, ss; + struct panel *p; + + fs = rps[i].peak->fs; + ss = rps[i].peak->ss; + + p = find_panel(det, fs, ss); + write_fs = fs - p->min_fs + p->orig_min_fs; + write_ss = ss - p->min_ss + p->orig_min_ss; + + fprintf(fh, "%7.2f %7.2f dev r,x,y: %9f %9f %9f %9f\n", + write_fs, write_ss, + r_dev(&rps[i])/1e9, fabs(r_dev(&rps[i])/1e9), + x_dev(&rps[i], det), + y_dev(&rps[i], det)); + + } + + fclose(fh); + + STATUS("Wrote %i pairs to %s\n", n, filename); +} + + +static int cmpd2(const void *av, const void *bv) +{ + struct reflpeak *a, *b; + + a = (struct reflpeak *)av; + b = (struct reflpeak *)bv; + + if ( fabs(r_dev(a)) < fabs(r_dev(b)) ) return -1; + return 1; +} + + +static int check_outlier_transition(struct reflpeak *rps, int n, + struct detector *det) +{ + int i; + + if ( n < 3 ) return n; + + qsort(rps, n, sizeof(struct reflpeak), cmpd2); + //write_pairs("pairs-before-outlier.lst", rps, n, det); + + for ( i=1; i<n-1; i++ ) { + + int j; + double grad = fabs(r_dev(&rps[i])) / i; + + for ( j=i+1; j<n; j++ ) { + if ( fabs(r_dev(&rps[j])) < 0.001e9+grad*j ) { + break; + } + } + if ( j == n ) { + //STATUS("Outlier transition found at position %i / %i\n", + // i, n); + return i; + } + } + + //STATUS("No outlier transition found.\n"); + return n; +} + + +/* Associate a Reflection with each peak in "image" which is close to Bragg. + * Reflections will be added to "reflist", which can be NULL if this is not + * needed. "rps" must be an array of sufficient size for all the peaks */ +static int pair_peaks(struct image *image, Crystal *cr, + RefList *reflist, struct reflpeak *rps) +{ + int i; + int n_acc = 0; + int n = 0; + double ax, ay, az; + double bx, by, bz; + double cx, cy, cz; + RefList *all_reflist; + + all_reflist = reflist_new(); + cell_get_cartesian(crystal_get_cell(cr), + &ax, &ay, &az, &bx, &by, &bz, &cx, &cy, &cz); + + /* First, create a RefList containing the most likely indices for each + * peak, with no exclusion criteria */ + for ( i=0; i<image_feature_count(image->features); i++ ) { + + struct imagefeature *f; + double h, k, l, hd, kd, ld; + Reflection *refl; + struct panel *p; + + /* Assume all image "features" are genuine peaks */ + f = image_get_feature(image->features, i); + if ( f == NULL ) continue; + + /* Decimal and fractional Miller indices of nearest reciprocal + * lattice point */ + hd = f->rx * ax + f->ry * ay + f->rz * az; + kd = f->rx * bx + f->ry * by + f->rz * bz; + ld = f->rx * cx + f->ry * cy + f->rz * cz; + h = lrint(hd); + k = lrint(kd); + l = lrint(ld); + + refl = reflection_new(h, k, l); + if ( refl == NULL ) { + ERROR("Failed to create reflection\n"); + return 0; + } + + add_refl_to_list(refl, all_reflist); + set_symmetric_indices(refl, h, k, l); + + /* It doesn't matter if the actual predicted location + * doesn't fall on this panel. We're only interested + * in how far away it is from the peak location. + * The predicted position and excitation errors will be + * filled in by update_partialities(). */ + p = find_panel(image->det, f->fs, f->ss); + set_panel(refl, p); + + rps[n].refl = refl; + rps[n].peak = f; + rps[n].panel = p; + n++; + + } + + /* Get the excitation errors and detector positions for the candidate + * reflections */ + crystal_set_reflections(cr, all_reflist); + update_partialities(cr, PMODEL_SCSPHERE); + + /* Pass over the peaks again, keeping only the ones which look like + * good pairings */ + for ( i=0; i<n; i++ ) { + + double fs, ss, pd; + signed int h, k, l; + Reflection *refl = rps[i].refl; + + get_indices(refl, &h, &k, &l); + + /* Is the supposed reflection anywhere near the peak? */ + get_detector_pos(refl, &fs, &ss); + pd = pow(fs - rps[i].peak->fs, 2.0) + + pow(ss - rps[i].peak->ss, 2.0); + if ( pd > 10.0 * 10.0 ) continue; + + rps[n_acc] = rps[i]; + rps[n_acc].refl = reflection_new(h, k, l); + copy_data(rps[n_acc].refl, refl); + if ( reflist != NULL ) { + add_refl_to_list(rps[n_acc].refl, reflist); + } + n_acc++; + + } + reflist_free(all_reflist); + + /* Sort the pairings by excitation error and look for a transition + * between good pairings and outliers */ + n_acc = check_outlier_transition(rps, n_acc, image->det); + + return n_acc; +} + + +void refine_radius(Crystal *cr, struct image *image) +{ + int n, n_acc; + struct reflpeak *rps; + RefList *reflist; + + /* Maximum possible size */ + rps = malloc(image_feature_count(image->features) + * sizeof(struct reflpeak)); + if ( rps == NULL ) return; + + reflist = reflist_new(); + n_acc = pair_peaks(image, cr, reflist, rps); + if ( n_acc < 3 ) { + ERROR("Too few paired peaks (%i) to determine radius\n", n_acc); + free(rps); + return; + } + crystal_set_reflections(cr, reflist); + update_partialities(cr, PMODEL_SCSPHERE); + crystal_set_reflections(cr, NULL); + + qsort(rps, n_acc, sizeof(struct reflpeak), cmpd2); + n = (n_acc-1) - n_acc/50; + if ( n < 2 ) n = 2; /* n_acc is always >= 2 */ + crystal_set_profile_radius(cr, fabs(r_dev(&rps[n]))); + + reflist_free(reflist); + free(rps); +} + + +/* Returns d(xh-xpk)/dP, where P = any parameter */ +static double x_gradient(int param, struct reflpeak *rp, struct detector *det, + double lambda, UnitCell *cell) +{ + signed int h, k, l; + double xpk, ypk, xh, yh; + double fsh, ssh; + double x, z, wn; + double ax, ay, az, bx, by, bz, cx, cy, cz; + + twod_mapping(rp->peak->fs, rp->peak->ss, &xpk, &ypk, rp->panel); + get_detector_pos(rp->refl, &fsh, &ssh); + twod_mapping(fsh, ssh, &xh, &yh, rp->panel); + get_indices(rp->refl, &h, &k, &l); + + wn = 1.0 / lambda; + + cell_get_reciprocal(cell, &ax, &ay, &az, &bx, &by, &bz, &cx, &cy, &cz); + x = h*ax + k*bx + l*cx; + z = h*az + k*bz + l*cz; + + switch ( param ) { + + case GPARAM_ASX : + return h * rp->panel->clen / (wn+z); + + case GPARAM_BSX : + return k * rp->panel->clen / (wn+z); + + case GPARAM_CSX : + return l * rp->panel->clen / (wn+z); + + case GPARAM_ASY : + return 0.0; + + case GPARAM_BSY : + return 0.0; + + case GPARAM_CSY : + return 0.0; + + case GPARAM_ASZ : + return -h * x * rp->panel->clen / (wn*wn + 2*wn*z + z*z); + + case GPARAM_BSZ : + return -k * x * rp->panel->clen / (wn*wn + 2*wn*z + z*z); + + case GPARAM_CSZ : + return -l * x * rp->panel->clen / (wn*wn + 2*wn*z + z*z); + + case GPARAM_DETX : + return -1; + + case GPARAM_DETY : + return 0; + + case GPARAM_CLEN : + return x / (wn+z); + + } + + ERROR("Positional gradient requested for parameter %i?\n", param); + abort(); +} + + +/* Returns d(yh-ypk)/dP, where P = any parameter */ +static double y_gradient(int param, struct reflpeak *rp, struct detector *det, + double lambda, UnitCell *cell) +{ + signed int h, k, l; + double xpk, ypk, xh, yh; + double fsh, ssh; + double y, z, wn; + double ax, ay, az, bx, by, bz, cx, cy, cz; + + twod_mapping(rp->peak->fs, rp->peak->ss, &xpk, &ypk, rp->panel); + get_detector_pos(rp->refl, &fsh, &ssh); + twod_mapping(fsh, ssh, &xh, &yh, rp->panel); + get_indices(rp->refl, &h, &k, &l); + + wn = 1.0 / lambda; + + cell_get_reciprocal(cell, &ax, &ay, &az, &bx, &by, &bz, &cx, &cy, &cz); + y = h*ay + k*by + l*cy; + z = h*az + k*bz + l*cz; + + switch ( param ) { + + case GPARAM_ASX : + return 0.0; + + case GPARAM_BSX : + return 0.0; + + case GPARAM_CSX : + return 0.0; + + case GPARAM_ASY : + return h * rp->panel->clen / (wn+z); + + case GPARAM_BSY : + return k * rp->panel->clen / (wn+z); + + case GPARAM_CSY : + return l * rp->panel->clen / (wn+z); + + case GPARAM_ASZ : + return -h * y * rp->panel->clen / (wn*wn + 2*wn*z + z*z); + + case GPARAM_BSZ : + return -k * y * rp->panel->clen / (wn*wn + 2*wn*z + z*z); + + case GPARAM_CSZ : + return -l * y * rp->panel->clen / (wn*wn + 2*wn*z + z*z); + + case GPARAM_DETX : + return 0; + + case GPARAM_DETY : + return -1; + + case GPARAM_CLEN : + return y / (wn+z); + + } + + ERROR("Positional gradient requested for parameter %i?\n", param); + abort(); +} + + +static void update_detector(struct detector *det, double xoffs, double yoffs, + double coffs) +{ + int i; + + for ( i=0; i<det->n_panels; i++ ) { + struct panel *p = &det->panels[i]; + p->cnx += xoffs * p->res; + p->cny += yoffs * p->res; + p->clen += coffs; + } +} + + +static int iterate(struct reflpeak *rps, int n, UnitCell *cell, + struct image *image, + double *total_x, double *total_y, double *total_z) +{ + int i; + gsl_matrix *M; + gsl_vector *v; + gsl_vector *shifts; + double asx, asy, asz; + double bsx, bsy, bsz; + double csx, csy, csz; + + /* Number of parameters to refine */ + M = gsl_matrix_calloc(num_params, num_params); + v = gsl_vector_calloc(num_params); + + for ( i=0; i<n; i++ ) { + + int k; + double gradients[num_params]; + double w; + + /* Excitation error terms */ + w = EXC_WEIGHT * rps[i].Ih; + + for ( k=0; k<num_params; k++ ) { + gradients[k] = r_gradient(cell, rv[k], rps[i].refl, + image); + } + + for ( k=0; k<num_params; k++ ) { + + int g; + double v_c, v_curr; + + for ( g=0; g<num_params; g++ ) { + + double M_c, M_curr; + + /* Matrix is symmetric */ + if ( g > k ) continue; + + M_c = w * gradients[g] * gradients[k]; + M_curr = gsl_matrix_get(M, k, g); + gsl_matrix_set(M, k, g, M_curr + M_c); + gsl_matrix_set(M, g, k, M_curr + M_c); + + } + + v_c = w * r_dev(&rps[i]); + v_c *= -gradients[k]; + v_curr = gsl_vector_get(v, k); + gsl_vector_set(v, k, v_curr + v_c); + + } + + /* Positional x terms */ + for ( k=0; k<num_params; k++ ) { + gradients[k] = x_gradient(rv[k], &rps[i], image->det, + image->lambda, cell); + } + + for ( k=0; k<num_params; k++ ) { + + int g; + double v_c, v_curr; + + for ( g=0; g<num_params; g++ ) { + + double M_c, M_curr; + + /* Matrix is symmetric */ + if ( g > k ) continue; + + M_c = gradients[g] * gradients[k]; + M_curr = gsl_matrix_get(M, k, g); + gsl_matrix_set(M, k, g, M_curr + M_c); + gsl_matrix_set(M, g, k, M_curr + M_c); + + } + + v_c = x_dev(&rps[i], image->det); + v_c *= -gradients[k]; + v_curr = gsl_vector_get(v, k); + gsl_vector_set(v, k, v_curr + v_c); + + } + + /* Positional y terms */ + for ( k=0; k<num_params; k++ ) { + gradients[k] = y_gradient(rv[k], &rps[i], image->det, + image->lambda, cell); + } + + for ( k=0; k<num_params; k++ ) { + + int g; + double v_c, v_curr; + + for ( g=0; g<num_params; g++ ) { + + double M_c, M_curr; + + /* Matrix is symmetric */ + if ( g > k ) continue; + + M_c = gradients[g] * gradients[k]; + M_curr = gsl_matrix_get(M, k, g); + gsl_matrix_set(M, k, g, M_curr + M_c); + gsl_matrix_set(M, g, k, M_curr + M_c); + + } + + v_c = y_dev(&rps[i], image->det); + v_c *= -gradients[k]; + v_curr = gsl_vector_get(v, k); + gsl_vector_set(v, k, v_curr + v_c); + + } + + } + + //show_matrix_eqn(M, v); + shifts = solve_svd(v, M, NULL, 0); + if ( shifts == NULL ) { + ERROR("Failed to solve equations.\n"); + gsl_matrix_free(M); + gsl_vector_free(v); + return 1; + } + + for ( i=0; i<num_params; i++ ) { + // STATUS("Shift %i = %e\n", i, gsl_vector_get(shifts, i)); + if ( isnan(gsl_vector_get(shifts, i)) ) { + gsl_vector_set(shifts, i, 0.0); + } + } + + /* Apply shifts */ + cell_get_reciprocal(cell, &asx, &asy, &asz, + &bsx, &bsy, &bsz, + &csx, &csy, &csz); + + /* Ensure the order here matches the order in rv[] */ + asx += gsl_vector_get(shifts, 0); + asy += gsl_vector_get(shifts, 1); + asz += gsl_vector_get(shifts, 2); + bsx += gsl_vector_get(shifts, 3); + bsy += gsl_vector_get(shifts, 4); + bsz += gsl_vector_get(shifts, 5); + csx += gsl_vector_get(shifts, 6); + csy += gsl_vector_get(shifts, 7); + csz += gsl_vector_get(shifts, 8); + update_detector(image->det, gsl_vector_get(shifts, 9), + gsl_vector_get(shifts, 10), + gsl_vector_get(shifts, 11)); + *total_x += gsl_vector_get(shifts, 9); + *total_y += gsl_vector_get(shifts, 10); + *total_z += gsl_vector_get(shifts, 11); + + cell_set_reciprocal(cell, asx, asy, asz, bsx, bsy, bsz, csx, csy, csz); + + gsl_vector_free(shifts); + gsl_matrix_free(M); + gsl_vector_free(v); + + return 0; +} + + +static double UNUSED residual(struct reflpeak *rps, int n, struct detector *det) +{ + int i; + double res = 0.0; + double r; + + r = 0.0; + for ( i=0; i<n; i++ ) { + r += EXC_WEIGHT * rps[i].Ih * pow(r_dev(&rps[i]), 2.0); + } + printf("%e ", r); + res += r; + + r = 0.0; + for ( i=0; i<n; i++ ) { + r += pow(x_dev(&rps[i], det), 2.0); + } + printf("%e ", r); + res += r; + + r = 0.0; + for ( i=0; i<n; i++ ) { + r += pow(y_dev(&rps[i], det), 2.0); + } + printf("%e\n", r); + res += r; + + return res; +} + + +int refine_prediction(struct image *image, Crystal *cr) +{ + int n; + int i; + struct reflpeak *rps; + double max_I; + RefList *reflist; + double total_x = 0.0; + double total_y = 0.0; + double total_z = 0.0; + char tmp[1024]; + + rps = malloc(image_feature_count(image->features) + * sizeof(struct reflpeak)); + if ( rps == NULL ) return 1; + + reflist = reflist_new(); + n = pair_peaks(image, cr, reflist, rps); + if ( n < 10 ) { + ERROR("Too few paired peaks (%i) to refine orientation.\n", n); + free(rps); + reflist_free(reflist); + return 1; + } + crystal_set_reflections(cr, reflist); + + /* Normalise the intensities to max 1 */ + max_I = -INFINITY; + for ( i=0; i<n; i++ ) { + double cur_I = rps[i].peak->intensity; + if ( cur_I > max_I ) max_I = cur_I; + } + if ( max_I <= 0.0 ) { + ERROR("All peaks negative?\n"); + free(rps); + return 1; + } + for ( i=0; i<n; i++ ) { + rps[i].Ih = rps[i].peak->intensity / max_I; + } + + //STATUS("Initial residual = %e\n", residual(rps, n, image->det)); + + /* Refine */ + for ( i=0; i<MAX_CYCLES; i++ ) { + update_partialities(cr, PMODEL_SCSPHERE); + if ( iterate(rps, n, crystal_get_cell(cr), image, + &total_x, &total_y, &total_z) ) return 1; + //STATUS("Residual after %i = %e\n", i, + // residual(rps, n, image->det)); + } + //STATUS("Final residual = %e\n", residual(rps, n, image->det)); + + snprintf(tmp, 1024, "predict_refine/det_shift x = %.3f y = %.3f mm", + total_x*1e3, total_y*1e3); + crystal_add_notes(cr, tmp); + + crystal_set_reflections(cr, NULL); + reflist_free(reflist); + + n = pair_peaks(image, cr, NULL, rps); + free(rps); + if ( n < 10 ) { + ERROR("Too few paired peaks (%i) after refinement.\n", n); + return 1; + } + + return 0; +} diff --git a/src/predict-refine.h b/src/predict-refine.h new file mode 100644 index 00000000..c763d2ca --- /dev/null +++ b/src/predict-refine.h @@ -0,0 +1,45 @@ +/* + * predict-refine.h + * + * Prediction refinement + * + * Copyright © 2012-2015 Deutsches Elektronen-Synchrotron DESY, + * a research centre of the Helmholtz Association. + * + * Authors: + * 2010-2015 Thomas White <taw@physics.org> + * + * This file is part of CrystFEL. + * + * CrystFEL is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * CrystFEL is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with CrystFEL. If not, see <http://www.gnu.org/licenses/>. + * + */ + +#ifndef PREDICT_REFINE_H +#define PREDICT_REFINE_H + + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#include "crystal.h" + +struct image; + +extern int refine_prediction(struct image *image, Crystal *cr); +extern void refine_radius(Crystal *cr, struct image *image); + + +#endif /* PREDICT_REFINE_H */ diff --git a/src/process_hkl.c b/src/process_hkl.c index fbbf4ff0..6bddcafa 100644 --- a/src/process_hkl.c +++ b/src/process_hkl.c @@ -3,12 +3,12 @@ * * Assemble and process FEL Bragg intensities * - * Copyright © 2012-2014 Deutsches Elektronen-Synchrotron DESY, + * Copyright © 2012-2015 Deutsches Elektronen-Synchrotron DESY, * a research centre of the Helmholtz Association. * Copyright © 2012 Lorenzo Galli * * Authors: - * 2009-2014 Thomas White <taw@physics.org> + * 2009-2015 Thomas White <taw@physics.org> * 2011 Andrew Martin <andrew.martin@desy.de> * 2012 Lorenzo Galli <lorenzo.galli@desy.de> * 2014 Chunhong Yoon <chun.hong.yoon@desy.de> @@ -379,7 +379,6 @@ static void display_progress(int n_images, int n_crystals, int n_crystals_used) } - static int merge_all(Stream *st, RefList *model, RefList *reference, const SymOpList *sym, double **hist_vals, signed int hist_h, @@ -561,7 +560,8 @@ int main(int argc, char *argv[]) errno = 0; start_after = strtod(optarg, &rval); if ( *rval != '\0' ) { - ERROR("Invalid value for --start-after.\n"); + ERROR("Invalid value for --start-after (%s)\n", + optarg); return 1; } break; @@ -570,7 +570,8 @@ int main(int argc, char *argv[]) errno = 0; stop_after = strtod(optarg, &rval); if ( *rval != '\0' ) { - ERROR("Invalid value for --stop-after.\n"); + ERROR("Invalid value for --stop-after (%s)\n", + optarg); return 1; } break; diff --git a/src/process_image.c b/src/process_image.c index 6ccd0c9a..783ca464 100644 --- a/src/process_image.c +++ b/src/process_image.c @@ -3,11 +3,11 @@ * * The processing pipeline for one image * - * Copyright © 2012-2014 Deutsches Elektronen-Synchrotron DESY, + * Copyright © 2012-2015 Deutsches Elektronen-Synchrotron DESY, * a research centre of the Helmholtz Association. * * Authors: - * 2010-2014 Thomas White <taw@physics.org> + * 2010-2015 Thomas White <taw@physics.org> * 2014 Valerio Mariani * * This file is part of CrystFEL. @@ -50,79 +50,35 @@ #include "reflist-utils.h" #include "process_image.h" #include "integration.h" +#include "predict-refine.h" -static int cmpd2(const void *av, const void *bv) +static void try_refine_autoR(struct image *image, Crystal *cr) { - double *ap, *bp; - double a, b; + double old_R, new_R; + char notes[1024]; - ap = (double *)av; - bp = (double *)bv; + refine_radius(cr, image); + old_R = crystal_get_profile_radius(cr); - a = ap[1]; - b = bp[1]; - - if ( fabs(a) < fabs(b) ) return -1; - return 1; -} - - -static void refine_radius(Crystal *cr) -{ - Reflection *refl; - RefListIterator *iter; - double vals[num_reflections(crystal_get_reflections(cr))*2]; - int n = 0; - int i; - double ti = 0.0; /* Total intensity */ - - for ( refl = first_refl(crystal_get_reflections(cr), &iter); - refl != NULL; - refl = next_refl(refl, iter) ) - { - double rlow, rhigh, p; - int val = 0; - - if ( get_intensity(refl) > 9.0*get_esd_intensity(refl) ) { - val = 1; - } - - get_partial(refl, &rlow, &rhigh, &p); - - vals[(2*n)+0] = val; - vals[(2*n)+1] = fabs((rhigh+rlow)/2.0); - n++; - - } - - /* Sort in ascending order of absolute "deviation from Bragg" */ - qsort(vals, n, sizeof(double)*2, cmpd2); - - /* Calculate cumulative number of very strong reflections as a function - * of absolute deviation from Bragg */ - for ( i=0; i<n-1; i++ ) { - ti += vals[2*i]; - vals[2*i] = ti; - } - - if ( ti < 10 ) { - ERROR("WARNING: Not enough strong reflections (%.0f) to estimate " - "crystal parameters (trying anyway).\n", ti); + if ( refine_prediction(image, cr) ) { + crystal_set_user_flag(cr, 1); + return; } - /* Find the cutoff where we get 90% of the strong spots */ - for ( i=0; i<n-1; i++ ) { - if ( vals[2*i] > 0.90*ti ) break; - } + /* Estimate radius again with better geometry */ + refine_radius(cr, image); + new_R = crystal_get_profile_radius(cr); - crystal_set_profile_radius(cr, fabs(vals[2*i+1])); + snprintf(notes, 1024, "predict_refine/R old = %.5f new = %.5f nm^-1", + old_R/1e9, new_R/1e9); + crystal_add_notes(cr, notes); } void process_image(const struct index_args *iargs, struct pattern_args *pargs, Stream *st, int cookie, const char *tmpdir, int results_pipe, - int serial) + int serial, pthread_mutex_t *term_lock) { float *data_for_measurement; size_t data_size; @@ -133,6 +89,7 @@ void process_image(const struct index_args *iargs, struct pattern_args *pargs, int r; int ret; char *rn; + int n_crystals_left; image.features = NULL; image.data = NULL; @@ -142,10 +99,11 @@ void process_image(const struct index_args *iargs, struct pattern_args *pargs, image.filename = pargs->filename_p_e->filename; image.event = pargs->filename_p_e->ev; image.beam = iargs->beam; - image.det = iargs->det; + image.det = copy_geom(iargs->det); image.crystals = NULL; image.n_crystals = 0; image.serial = serial; + image.indexed_by = INDEXING_NONE; hdfile = hdfile_open(image.filename); if ( hdfile == NULL ) { @@ -177,10 +135,7 @@ void process_image(const struct index_args *iargs, struct pattern_args *pargs, switch ( iargs->peaks ) { case PEAK_HDF5: - /* Get peaks from HDF5 */ - - if ( get_peaks(&image, hdfile, iargs->hdf5_peak_path, - iargs->cxi_hdf5_peaks, pargs->filename_p_e) ) { + if ( get_peaks(&image, hdfile, iargs->hdf5_peak_path) ) { ERROR("Failed to get peaks from HDF5 file.\n"); } if ( !iargs->no_revalidate ) { @@ -191,6 +146,19 @@ void process_image(const struct index_args *iargs, struct pattern_args *pargs, } break; + case PEAK_CXI: + if ( get_peaks_cxi(&image, hdfile, iargs->hdf5_peak_path, + pargs->filename_p_e) ) { + ERROR("Failed to get peaks from CXI file.\n"); + } + if ( !iargs->no_revalidate ) { + validate_peaks(&image, iargs->min_snr, + iargs->pk_inn, iargs->pk_mid, + iargs->pk_out, iargs->use_saturated, + iargs->check_hdf5_snr); + } + break; + case PEAK_ZAEF: search_peaks(&image, iargs->threshold, iargs->min_gradient, iargs->min_snr, @@ -226,36 +194,73 @@ void process_image(const struct index_args *iargs, struct pattern_args *pargs, } free(rn); - pargs->n_crystals = image.n_crystals; for ( i=0; i<image.n_crystals; i++ ) { crystal_set_image(image.crystals[i], &image); + crystal_set_user_flag(image.crystals[i], 0); } - /* Default parameters */ - image.div = 0.0; - image.bw = 0.00000001; - for ( i=0; i<image.n_crystals; i++ ) { - crystal_set_profile_radius(image.crystals[i], 0.01e9); - crystal_set_mosaicity(image.crystals[i], 0.0); /* radians */ + /* Set beam/crystal parameters */ + if ( iargs->fix_divergence >= 0.0 ) { + image.div = iargs->fix_divergence; + } else { + image.div = 0.0; + } + if ( iargs->fix_bandwidth >= 0.0 ) { + image.bw = iargs->fix_bandwidth; + } else { + image.bw = 0.00000001; + } + if ( iargs->fix_profile_r >= 0.0 ) { + for ( i=0; i<image.n_crystals; i++ ) { + crystal_set_profile_radius(image.crystals[i], + iargs->fix_profile_r); + crystal_set_mosaicity(image.crystals[i], 0.0); + } + } else { + for ( i=0; i<image.n_crystals; i++ ) { + crystal_set_profile_radius(image.crystals[i], 0.02e9); + crystal_set_mosaicity(image.crystals[i], 0.0); + } } - /* Integrate all the crystals at once - need all the crystals so that - * overlaps can be detected. */ - integrate_all_4(&image, iargs->int_meth, PMODEL_SCSPHERE, iargs->push_res, - iargs->ir_inn, iargs->ir_mid, iargs->ir_out, - INTDIAG_NONE, 0, 0, 0, results_pipe); + if ( iargs->fix_profile_r < 0.0 ) { + + for ( i=0; i<image.n_crystals; i++ ) { + if ( iargs->predict_refine ) { + try_refine_autoR(&image, image.crystals[i]); + } else { + refine_radius(image.crystals[i], &image); + } + } + + } else { + for ( i=0; i<image.n_crystals; i++ ) { + if ( iargs->predict_refine ) { + refine_prediction(&image, image.crystals[i]); + } + } + + } + + /* If there are no crystals left, set the indexing flag back to zero */ + n_crystals_left = 0; for ( i=0; i<image.n_crystals; i++ ) { - refine_radius(image.crystals[i]); - reflist_free(crystal_get_reflections(image.crystals[i])); + if ( crystal_get_user_flag(image.crystals[i]) == 0 ) { + n_crystals_left++; + } + } + if ( n_crystals_left == 0 ) { + image.indexed_by = INDEXING_NONE; } + /* Integrate! */ integrate_all_4(&image, iargs->int_meth, PMODEL_SCSPHERE, - iargs->push_res, - iargs->ir_inn, iargs->ir_mid, iargs->ir_out, - iargs->int_diag, iargs->int_diag_h, - iargs->int_diag_k, iargs->int_diag_l, - results_pipe); + iargs->push_res, + iargs->ir_inn, iargs->ir_mid, iargs->ir_out, + iargs->int_diag, iargs->int_diag_h, + iargs->int_diag_k, iargs->int_diag_l, + term_lock); ret = write_chunk(st, &image, hdfile, iargs->stream_peaks, iargs->stream_refls, @@ -269,8 +274,17 @@ void process_image(const struct index_args *iargs, struct pattern_args *pargs, n += crystal_get_num_implausible_reflections(image.crystals[i]); } if ( n > 0 ) { - STATUS("WARNING: %i implausibly negative reflection%s in %s.\n", - n, n>1?"s":"", image.filename); + STATUS("WARNING: %i implausibly negative reflection%s in %s " + "%s\n", n, n>1?"s":"", image.filename, + get_event_string(image.event)); + } + + /* Count crystals which are still good */ + pargs->n_crystals = 0; + for ( i=0; i<image.n_crystals; i++ ) { + if ( crystal_get_user_flag(image.crystals[i]) == 0 ) { + pargs->n_crystals++; + } } for ( i=0; i<image.n_crystals; i++ ) { @@ -290,5 +304,6 @@ void process_image(const struct index_args *iargs, struct pattern_args *pargs, free(image.data); if ( image.flags != NULL ) free(image.flags); image_feature_list_free(image.features); + free_detector_geometry(image.det); hdfile_close(hdfile); } diff --git a/src/process_image.h b/src/process_image.h index a0bd6a83..d982c4f0 100644 --- a/src/process_image.h +++ b/src/process_image.h @@ -3,11 +3,11 @@ * * The processing pipeline for one image * - * Copyright © 2012-2014 Deutsches Elektronen-Synchrotron DESY, + * Copyright © 2012-2015 Deutsches Elektronen-Synchrotron DESY, * a research centre of the Helmholtz Association. * * Authors: - * 2010-2014 Thomas White <taw@physics.org> + * 2010-2015 Thomas White <taw@physics.org> * 2014 Valerio Mariani * * This file is part of CrystFEL. @@ -41,6 +41,7 @@ enum { PEAK_ZAEF, PEAK_HDF5, + PEAK_CXI, }; @@ -63,7 +64,6 @@ struct index_args float tols[4]; struct beam_params *beam; char *hdf5_peak_path; - int cxi_hdf5_peaks; float pk_inn; float pk_mid; float pk_out; @@ -83,6 +83,10 @@ struct index_args signed int int_diag_l; float push_res; float highres; + float fix_profile_r; + float fix_bandwidth; + float fix_divergence; + int predict_refine; }; @@ -100,7 +104,7 @@ struct pattern_args extern void process_image(const struct index_args *iargs, struct pattern_args *pargs, Stream *st, int cookie, const char *tmpdir, int results_pipe, - int serial); + int serial, pthread_mutex_t *term_lock); #endif /* PROCESS_IMAGEs_H */ diff --git a/src/rejection.c b/src/rejection.c new file mode 100644 index 00000000..a565fec3 --- /dev/null +++ b/src/rejection.c @@ -0,0 +1,191 @@ +/* + * rejection.c + * + * Crystal rejection for scaling + * + * Copyright © 2012-2015 Deutsches Elektronen-Synchrotron DESY, + * a research centre of the Helmholtz Association. + * + * Authors: + * 2010-2015 Thomas White <taw@physics.org> + * + * This file is part of CrystFEL. + * + * CrystFEL is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * CrystFEL is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with CrystFEL. If not, see <http://www.gnu.org/licenses/>. + * + */ + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + + +#include <stdlib.h> +#include <assert.h> +#include <gsl/gsl_statistics.h> + +#include "crystal.h" +#include "reflist.h" +#include "rejection.h" +#include "cell-utils.h" + + +static double mean_intensity(RefList *list) +{ + Reflection *refl; + RefListIterator *iter; + double total = 0.0; + int n = 0; + + for ( refl = first_refl(list, &iter); + refl != NULL; + refl = next_refl(refl, iter) ) + { + total += get_intensity(refl); + n++; + } + + return total/n; +} + + +/* Reject really obvious outliers */ +void early_rejection(Crystal **crystals, int n) +{ + int i; + double m = 0.0; + double mean_m; + FILE *fh = fopen("reject.dat", "w"); + int n_flag = 0; + + for ( i=0; i<n; i++ ) { + double u; + RefList *list = crystal_get_reflections(crystals[i]); + u = mean_intensity(list); + m += u; + fprintf(fh, "%i %f\n", i, u); + } + mean_m = m/n; + for ( i=0; i<n; i++ ) { + double u; + RefList *list = crystal_get_reflections(crystals[i]); + u = mean_intensity(list); + if ( u/mean_m < 0.2 ) { + crystal_set_user_flag(crystals[i], 5); + n_flag++; + } + } + fclose(fh); + + STATUS("Mean intensity/peak = %f ADU\n", m/n); + STATUS("%i crystals flagged\n", n_flag); +} + + +static void check_cc(Crystal *cr, RefList *full) +{ + RefList *list = crystal_get_reflections(cr); + Reflection *refl; + RefListIterator *iter; + double G = crystal_get_osf(cr); + double B = crystal_get_Bfac(cr); + UnitCell *cell = crystal_get_cell(cr); + double *vec1, *vec2; + int n, i; + double cc; + + n = num_reflections(list); + vec1 = malloc(n*sizeof(double)); + vec2 = malloc(n*sizeof(double)); + if ( (vec1 == NULL) || (vec2 == NULL) ) { + ERROR("Not enough memory to check CCs\n"); + return; + } + + i = 0; + for ( refl = first_refl(list, &iter); + refl != NULL; + refl = next_refl(refl, iter) ) + { + signed int h, k, l; + double pobs, pcalc; + double res, corr, Ipart; + Reflection *match; + + if ( !get_flag(refl) ) continue; /* Not free-flagged */ + + get_indices(refl, &h, &k, &l); + res = resolution(cell, h, k, l); + if ( 2.0*res > crystal_get_resolution_limit(cr) ) continue; + + match = find_refl(full, h, k, l); + if ( match == NULL ) continue; + + /* Calculated partiality */ + pcalc = get_partiality(refl); + + /* Observed partiality */ + corr = exp(-G) * exp(B*res*res) * get_lorentz(refl); + Ipart = get_intensity(refl) * corr; + pobs = Ipart / get_intensity(match); + + vec1[i] = pobs; + vec2[i] = pcalc; + i++; + } + + cc = gsl_stats_correlation(vec1, 1, vec2, 1, i); + //printf("%f\n", cc); + if ( cc < 0.5 ) crystal_set_user_flag(cr, 6); + + free(vec1); + free(vec2); +} + + +static void check_ccs(Crystal **crystals, int n_crystals, RefList *full) +{ + int i; + + for ( i=0; i<n_crystals; i++ ) { + check_cc(crystals[i], full); + } +} + + +void check_rejection(Crystal **crystals, int n, RefList *full) +{ + int i; + int n_acc = 0; + + /* Check according to CCs FIXME: Disabled */ + //if ( full != NULL ) check_ccs(crystals, n, full); + + for ( i=0; i<n; i++ ) { + + /* Reject if B factor modulus is very large */ + if ( fabs(crystal_get_Bfac(crystals[i])) > 1e-18 ) { + crystal_set_user_flag(crystals[i], 1); + } + + if ( crystal_get_user_flag(crystals[i]) == 0 ) n_acc++; + + } + + if ( n_acc < 2 ) { + ERROR("Not enough crystals left to proceed (%i). Sorry.\n", + n_acc); + exit(1); + } +} diff --git a/src/rejection.h b/src/rejection.h new file mode 100644 index 00000000..ec529941 --- /dev/null +++ b/src/rejection.h @@ -0,0 +1,43 @@ +/* + * rejection.h + * + * Crystal rejection for scaling + * + * Copyright © 2012-2015 Deutsches Elektronen-Synchrotron DESY, + * a research centre of the Helmholtz Association. + * + * Authors: + * 2010-2015 Thomas White <taw@physics.org> + * + * This file is part of CrystFEL. + * + * CrystFEL is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * CrystFEL is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with CrystFEL. If not, see <http://www.gnu.org/licenses/>. + * + */ + +#ifndef REJECTION_H +#define REJECTION_H + + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + + +#include "crystal.h" + +extern void early_rejection(Crystal **crystals, int n); +extern void check_rejection(Crystal **crystals, int n, RefList *full); + +#endif /* REJECTION_H */ diff --git a/src/scaling-report.c b/src/scaling-report.c deleted file mode 100644 index ca4c5cbc..00000000 --- a/src/scaling-report.c +++ /dev/null @@ -1,898 +0,0 @@ -/* - * scaling-report.c - * - * Write a nice PDF of scaling parameters - * - * Copyright © 2012-2013 Deutsches Elektronen-Synchrotron DESY, - * a research centre of the Helmholtz Association. - * - * Authors: - * 2010-2013 Thomas White <taw@physics.org> - * - * This file is part of CrystFEL. - * - * CrystFEL is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * CrystFEL is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with CrystFEL. If not, see <http://www.gnu.org/licenses/>. - * - */ - -#ifdef HAVE_CONFIG_H -#include <config.h> -#endif - -#include <cairo.h> -#include <cairo-pdf.h> -#include <pango/pangocairo.h> -#include <math.h> - -#include "image.h" -#include "scaling-report.h" - - -#define PAGE_WIDTH (842.0) - -enum justification -{ - J_CENTER, - J_LEFT, - J_RIGHT, -}; - - -struct _srcontext -{ - cairo_surface_t *surf; - cairo_t *cr; - double w; - double h; - - /* Most sampled reflections */ - signed int ms_h[9]; - signed int ms_k[9]; - signed int ms_l[9]; - -}; - - -static void show_text(cairo_t *cr, const char *text, double y, - enum justification j, char *font) -{ - PangoLayout *layout; - PangoFontDescription *fontdesc; - int width, height; - PangoAlignment just; - - if ( font == NULL ) font = "Sans 10"; - - layout = pango_cairo_create_layout(cr); - pango_layout_set_ellipsize(layout, PANGO_ELLIPSIZE_NONE); - pango_layout_set_width(layout, PANGO_SCALE*(PAGE_WIDTH-20.0)); - - switch ( j ) - { - case J_CENTER : just = PANGO_ALIGN_CENTER; break; - case J_LEFT : just = PANGO_ALIGN_LEFT; break; - case J_RIGHT : just = PANGO_ALIGN_RIGHT; break; - default: just = PANGO_ALIGN_LEFT; break; - } - - pango_layout_set_alignment(layout, just); - pango_layout_set_wrap(layout, PANGO_WRAP_CHAR); - pango_layout_set_spacing(layout, 4.0*PANGO_SCALE); - - pango_layout_set_text(layout, text, -1); - - fontdesc = pango_font_description_from_string(font); - pango_layout_set_font_description(layout, fontdesc); - - pango_cairo_update_layout(cr, layout); - pango_layout_get_size(layout, &width, &height); - - cairo_move_to(cr, 10.0, y); - cairo_set_source_rgb(cr, 0.0, 0.0, 0.0); - pango_cairo_show_layout(cr, layout); -} - - -static void show_text_simple(cairo_t *cr, const char *text, double x, double y, - char *font, double rot, enum justification j) -{ - PangoLayout *layout; - PangoFontDescription *fontdesc; - int width, height; - - cairo_save(cr); - - if ( font == NULL ) font = "Sans 10"; - - layout = pango_cairo_create_layout(cr); - pango_layout_set_ellipsize(layout, PANGO_ELLIPSIZE_NONE); - pango_layout_set_alignment(layout, PANGO_ALIGN_LEFT); - - pango_layout_set_text(layout, text, -1); - - fontdesc = pango_font_description_from_string(font); - pango_layout_set_font_description(layout, fontdesc); - - pango_cairo_update_layout(cr, layout); - pango_layout_get_size(layout, &width, &height); - - cairo_new_path(cr); - cairo_translate(cr, x, y); - cairo_rotate(cr, rot); - if ( j == J_CENTER ) { - cairo_translate(cr, -(width/2.0)/PANGO_SCALE, - -(height/2.0)/PANGO_SCALE); - } else if ( j == J_RIGHT ) { - cairo_translate(cr, -width/PANGO_SCALE, - -(height/2.0)/PANGO_SCALE); - } else { - cairo_translate(cr, 0.0, -(height/2.0)/PANGO_SCALE); - } - cairo_set_source_rgb(cr, 0.0, 0.0, 0.0); - pango_cairo_show_layout(cr, layout); - - cairo_restore(cr); -} - - -static void plot_point(cairo_t *cr, double g_width, double g_height, - double pcalc, double pobs) -{ - int bad = 0; - - if ( pobs > 1.0 ) { - pobs = 1.01; - bad = 1; - - } - if ( pcalc > 1.0 ) { - pcalc = 1.01; - bad = 1; - } - if ( pobs < 0.0 ) { - pobs = -0.01; - bad = 1; - } - if ( pcalc < 0.0 ) { - pobs = -0.01; - bad = 1; - } - - if ( bad ) { - cairo_set_source_rgb(cr, 1.0, 0.0, 0.0); - } - - cairo_arc(cr, g_width*pcalc, g_height*(1.0-pobs), - 1.0, 0.0, 2.0*M_PI); - cairo_fill(cr); - - if ( bad ) { - cairo_set_source_rgb(cr, 0.0, 0.7, 0.0); - } -} - - -static void partiality_graph(cairo_t *cr, Crystal **crystals, int n, - RefList *full) -{ - const double g_width = 200.0; - const double g_height = 200.0; - int i; - const int nbins = 25; - double t_num[nbins]; - double t_den[nbins]; - double prob; - double pcalcmin[nbins]; - double pcalcmax[nbins]; - int num_nondud; - gsl_rng *rng; - - show_text_simple(cr, "Observed partiality", -20.0, g_height/2.0, - NULL, -M_PI_2, J_CENTER); - show_text_simple(cr, "Calculated partiality", - g_width/2.0,g_height+20.0, NULL, 0.0, J_CENTER); - - show_text_simple(cr, "0.0", -20.0, g_height, NULL, 0.0, J_CENTER); - show_text_simple(cr, "1.0", -20.0, 0.0, NULL, 0.0, J_CENTER); - show_text_simple(cr, "0.0", 0.0, g_height+10.0, NULL, - -M_PI/3.0, J_RIGHT); - show_text_simple(cr, "1.0", g_width, g_height+10.0, NULL, - -M_PI/3.0, J_RIGHT); - - for ( i=0; i<nbins; i++ ) { - t_num[i] = 0.0; - t_den[i] = 0.0; - pcalcmin[i] = (double)i/nbins; - pcalcmax[i] = (double)(i+1)/nbins; - } - pcalcmax[nbins-1] += 0.001; /* Make sure it include pcalc = 1 */ - - num_nondud = 0; - for ( i=0; i<n; i++ ) { - if ( crystal_get_user_flag(crystals[i]) ) continue; - num_nondud++; - } - - /* The reflections chosen for the graph will be the same every time - * (given the same sequence of input reflections, scalabilities etc) */ - rng = gsl_rng_alloc(gsl_rng_mt19937); - - cairo_set_source_rgb(cr, 0.0, 0.7, 0.0); - prob = 1.0 / num_nondud; - for ( i=0; i<n; i++ ) { - - Reflection *refl; - RefListIterator *iter; - Crystal *cryst; - - cryst = crystals[i]; - - if ( crystal_get_user_flag(cryst) ) continue; - - for ( refl = first_refl(crystal_get_reflections(cryst), &iter); - refl != NULL; - refl = next_refl(refl, iter) ) - { - double Ipart, Ifull, pobs, pcalc; - signed int h, k, l; - Reflection *f; - int bin; - - get_indices(refl, &h, &k, &l); - f = find_refl(full, h, k, l); - if ( f == NULL ) continue; - if ( get_redundancy(f) < 2 ) continue; - - Ipart = crystal_get_osf(cryst) * get_intensity(refl); - Ifull = get_intensity(f); - - pobs = Ipart/Ifull; - pcalc = get_lorentz(refl) * get_partiality(refl); - - //STATUS("%4i %4i %4i : %9.6f %9.6f %e %e %e\n", - // h, k, l, pobs, pcalc, - // Ipart, Ifull, images[i].osf); - - for ( bin=0; bin<nbins; bin++ ) { - if ( (pcalc >= pcalcmin[bin]) - && (pcalc < pcalcmax[bin]) ) - { - double esd_pobs, esd_Ip, esd_If; - esd_Ip = get_esd_intensity(refl); - esd_If = get_esd_intensity(f); - esd_If *= crystal_get_osf(cryst); - esd_pobs = pow(esd_Ip/Ipart, 2.0); - esd_pobs += pow(esd_If/Ifull, 2.0); - esd_pobs = sqrt(esd_pobs); - t_num[bin] += pobs / esd_pobs; - t_den[bin] += 1.0 / esd_pobs; - } - } - - bin = nbins * pcalc; - - if ( random_flat(rng, 1.0) < prob ) { - plot_point(cr, g_width, g_height, pcalc, pobs); - } - } - - } - - gsl_rng_free(rng); - - cairo_new_path(cr); - cairo_rectangle(cr, 0.0, 0.0, g_width, g_height); - cairo_clip(cr); - - cairo_new_path(cr); - cairo_move_to(cr, 0.0, g_height); - for ( i=0; i<nbins; i++ ) { - - double pos = pcalcmin[i] + (pcalcmax[i] - pcalcmin[i])/2.0; - - if ( t_den[i] == 0.0 ) continue; - cairo_line_to(cr, g_width*pos, - g_height - g_height*(t_num[i]/t_den[i])); - - } - cairo_set_source_rgb(cr, 0.0, 0.0, 0.0); - cairo_stroke(cr); - - cairo_reset_clip(cr); - - cairo_new_path(cr); - cairo_rectangle(cr, 0.0, 0.0, g_width, g_height); - cairo_set_source_rgb(cr, 0.0, 0.0, 0.0); - cairo_set_line_width(cr, 1.5); - cairo_stroke(cr); -} - - -static void partiality_histogram(cairo_t *cr, Crystal **crystals, int n, - RefList *full, int calc, int backwards) -{ - int f_max; - int i, b; - const int nbins = 100; - int counts[nbins]; - const double g_width = 200.0; - const double g_height = 120.0; - char tmp[32]; - double text_rot, axis_pos; - - if ( backwards ) { - text_rot = M_PI_2; - axis_pos = 215.0; - } else { - text_rot = -M_PI_2; - axis_pos = -15.0; - } - - show_text_simple(cr, "Frequency", axis_pos, g_height/2.0, - NULL, text_rot, J_CENTER); - - for ( b=0; b<nbins; b++ ) { - counts[b] = 0; - } - - for ( i=0; i<n; i++ ) { - - Reflection *refl; - RefListIterator *iter; - Crystal *cryst = crystals[i]; - - if ( crystal_get_user_flag(cryst) ) continue; - - for ( refl = first_refl(crystal_get_reflections(cryst), &iter); - refl != NULL; - refl = next_refl(refl, iter) ) - { - double Ipart, Ifull, pobs, pcalc; - signed int h, k, l; - Reflection *f; - - get_indices(refl, &h, &k, &l); - f = find_refl(full, h, k, l); - if ( f == NULL ) continue; - - Ipart = get_intensity(refl); - Ifull = get_intensity(f); - - pobs = (Ipart * crystal_get_osf(cryst)) - / (Ifull * get_lorentz(refl)); - pcalc = get_partiality(refl); - - if ( calc ) { - b = pcalc*nbins; - } else { - b = pobs*nbins; - } - if ( (b>=0) && (b<nbins) ) counts[b]++; - } - } - - f_max = 0; - for ( b=0; b<nbins; b++ ) { - if ( counts[b] > f_max ) f_max = counts[b]; - } - f_max = (f_max/10)*10 + 10; - - if ( !backwards ) { - show_text_simple(cr, "0", axis_pos, g_height, - NULL, 0.0, J_RIGHT); - } else { - show_text_simple(cr, "0", axis_pos, 0.0, - NULL, M_PI, J_RIGHT); - } - snprintf(tmp, 31, "%i", f_max); - if ( !backwards ) { - show_text_simple(cr, tmp, axis_pos, 0.0, - NULL, 0.0, J_RIGHT); - } else { - show_text_simple(cr, tmp, axis_pos, g_height, - NULL, M_PI, J_RIGHT); - } - - for ( b=0; b<nbins; b++ ) { - - double bar_height; - - bar_height = ((double)counts[b]/f_max)*g_height; - - cairo_new_path(cr); - if ( !backwards ) { - cairo_rectangle(cr, (g_width/nbins)*b, g_height, - g_width/nbins, -bar_height); - } else { - cairo_rectangle(cr, (g_width/nbins)*b, 0.0, - g_width/nbins, bar_height); - } - cairo_set_source_rgb(cr, 0.0, 0.0, 1.0); - cairo_set_line_width(cr, 1.0); - cairo_stroke(cr); - - } - - cairo_new_path(cr); - cairo_rectangle(cr, 0.0, 0.0, g_width, g_height); - cairo_set_source_rgb(cr, 0.0, 0.0, 0.0); - cairo_set_line_width(cr, 1.5); - cairo_stroke(cr); -} - - -static void scale_factor_histogram(cairo_t *cr, Crystal **crystals, int n, - const char *title) -{ - int f_max; - int i, b; - const int nbins = 100; - double osf_max, osf_inc; - double osf_low[nbins]; - double osf_high[nbins]; - int counts[nbins]; - const double g_width = 320.0; - const double g_height = 180.0; - char tmp[32]; - int n_zero, n_half; - - show_text_simple(cr, title, g_width/2.0, -18.0, - "Sans Bold 10", 0.0, J_CENTER); - - show_text_simple(cr, "Frequency", -15.0, g_height/2.0, - NULL, -M_PI_2, J_CENTER); - show_text_simple(cr, "Scale factor", g_width/2.0, g_height+12.0, - NULL, 0.0, J_CENTER); - - osf_max = 0.0; - for ( i=0; i<n; i++ ) { - double osf = crystal_get_osf(crystals[i]); - if ( crystal_get_user_flag(crystals[i]) ) continue; - if ( osf > osf_max ) osf_max = osf; - } - osf_max = ceil(osf_max+osf_max/10000.0); - if ( osf_max > 1000.0 ) { - ERROR("Silly scale factor detected. Using 100.0 instead.\n"); - osf_max = 100.0; - } - - do { - - osf_inc = osf_max / nbins; - - for ( b=0; b<nbins; b++ ) { - osf_low[b] = b*osf_inc; - osf_high[b] = (b+1)*osf_inc; - counts[b] = 0; - } - - for ( i=0; i<n; i++ ) { - - double osf = crystal_get_osf(crystals[i]); - - if ( crystal_get_user_flag(crystals[i]) ) continue; - - for ( b=0; b<nbins; b++ ) { - if ( (osf >= osf_low[b]) - && (osf < osf_high[b]) ) - { - counts[b]++; - break; - } - } - } - - n_zero = 0; - n_half = 0; - if ( osf_max > 10.0 ) { - - /* Count the number of bins with no counts, subtract 1 - * from the maximum value until this isn't the case */ - for ( b=0; b<nbins; b++ ) { - if ( counts[b] == 0 ) n_zero++; - n_half++; - } - n_half /= 2; - - if ( n_zero > n_half ) osf_max -= 1.0; - - } - - } while ( n_zero > n_half ); - - f_max = 0; - for ( b=0; b<nbins; b++ ) { - if ( counts[b] > f_max ) f_max = counts[b]; - } - f_max = (f_max/10)*10 + 10; - - show_text_simple(cr, "0", -10.0, g_height, NULL, 0.0, J_RIGHT); - snprintf(tmp, 31, "%i", f_max); - show_text_simple(cr, tmp, -10.0, 0.0, NULL, 0.0, J_RIGHT); - - show_text_simple(cr, "0.00", 0.0, g_height+10.0, - NULL, -M_PI/3.0, J_RIGHT); - snprintf(tmp, 32, "%5.2f", osf_max); - show_text_simple(cr, tmp, g_width, g_height+10.0, - NULL, -M_PI/3.0, J_RIGHT); - - for ( b=0; b<nbins; b++ ) { - - double bar_height; - - bar_height = ((double)counts[b]/f_max)*g_height; - - cairo_new_path(cr); - cairo_rectangle(cr, (g_width/nbins)*b, g_height, - g_width/nbins, -bar_height); - cairo_set_source_rgb(cr, 0.0, 0.0, 1.0); - cairo_set_line_width(cr, 1.0); - cairo_stroke(cr); - - } - - cairo_new_path(cr); - cairo_rectangle(cr, 0.0, 0.0, g_width, g_height); - cairo_set_source_rgb(cr, 0.0, 0.0, 0.0); - cairo_set_line_width(cr, 1.0); - cairo_stroke(cr); -} - - -static void intensity_histogram(cairo_t *cr, Crystal **crystals, int n, - RefList *full, - signed int h, signed int k, signed int l) -{ - int f_max; - int i, b; - const int nbins = 30; - double int_max, int_inc; - double int_low[nbins]; - double int_high[nbins]; - int counts[nbins]; - const double g_width = 115.0; - const double g_height = 55.0; - char tmp[64]; - Reflection *f; - double Ifull, dsd_Ifull, pos, mI, bit; - int have_full; - - f = find_refl(full, h, k, l); - if ( f != NULL ) { - Ifull = get_intensity(f); - dsd_Ifull = get_esd_intensity(f) * sqrt(get_redundancy(f)); - have_full = 1; - } else { - Ifull = 0.0; - dsd_Ifull = 0.0; - have_full = 0; - } - - snprintf(tmp, 63, "%i %i %i", h, k, l); - show_text_simple(cr, tmp, g_width/2.0, -10.0, - "Sans Bold 10", 0.0, J_CENTER); - - int_max = 0.0; - int nmeas = 0; - for ( i=0; i<n; i++ ) { - - double osf; - Crystal *cryst = crystals[i]; - - if ( crystal_get_user_flag(cryst) ) continue; - - osf = crystal_get_osf(cryst); - - for ( f = find_refl(crystal_get_reflections(cryst), h, k, l); - f != NULL; - f = next_found_refl(f) ) - { - double Iobs, pcalc, Ifull_est; - - pcalc = get_partiality(f); - Iobs = get_intensity(f); - Ifull_est = Iobs / (pcalc * osf); - - if ( Ifull_est > int_max ) int_max = Ifull_est; - nmeas++; - } - - } - int_max *= 1.1; - int_inc = int_max / nbins; - - for ( b=0; b<nbins; b++ ) { - int_low[b] = b*int_inc; - int_high[b] = (b+1)*int_inc; - counts[b] = 0; - } - - for ( i=0; i<n; i++ ) { - - double osf; - Crystal *cryst = crystals[i]; - - if ( crystal_get_user_flag(cryst) ) continue; - - osf = crystal_get_osf(cryst); - - for ( f = find_refl(crystal_get_reflections(cryst), h, k, l); - f != NULL; - f = next_found_refl(f) ) - { - double Iobs, pcalc, Ifull_est; - - pcalc = get_partiality(f); - Iobs = get_intensity(f); - Ifull_est = Iobs / (pcalc * osf); - - for ( b=0; b<nbins; b++ ) { - if ( (Ifull_est >= int_low[b]) - && (Ifull_est < int_high[b]) ) { - counts[b]++; - break; - } - } - } - - } - - f_max = 0; - for ( b=0; b<nbins; b++ ) { - if ( counts[b] > f_max ) f_max = counts[b]; - } - f_max = (f_max/10)*10 + 10; - - snprintf(tmp, 32, "Max I=%.0f", int_max); - show_text_simple(cr, tmp, 10.0, 10.0, "Sans 9", 0.0, J_LEFT); - - for ( b=0; b<nbins; b++ ) { - - double bar_height; - - bar_height = ((double)counts[b]/f_max)*g_height; - - cairo_new_path(cr); - cairo_rectangle(cr, (g_width/nbins)*b, g_height, - g_width/nbins, -bar_height); - cairo_set_source_rgb(cr, 0.0, 0.0, 1.0); - cairo_set_line_width(cr, 1.0); - cairo_stroke(cr); - - } - - cairo_new_path(cr); - cairo_rectangle(cr, 0.0, 0.0, g_width, g_height); - cairo_set_source_rgb(cr, 0.0, 0.0, 0.0); - cairo_set_line_width(cr, 1.5); - cairo_stroke(cr); - - mI = int_high[nbins-1]; - pos = Ifull/mI; - bit = g_height / 15.0; - cairo_arc(cr, g_width*pos, g_height+bit, bit, 0.0, 2.0*M_PI); - if ( have_full ) { - cairo_set_source_rgb(cr, 0.0, 0.67, 0.45); - } else { - cairo_set_source_rgb(cr, 0.86, 0.0, 0.0); - } - cairo_fill(cr); - - if ( have_full ) { - - double eW = g_width*dsd_Ifull/mI; - - cairo_new_path(cr); - cairo_rectangle(cr, 0.0, g_height+bit*2.0, - g_width, g_height+bit*2.0); - //cairo_clip(cr); - - cairo_new_path(cr); - cairo_move_to(cr, g_width*pos - eW, g_height+bit); - cairo_line_to(cr, g_width*pos + eW, g_height+bit); - cairo_set_source_rgb(cr, 0.0, 0.67, 0.45); - cairo_set_line_width(cr, 2.0); - cairo_stroke(cr); - - cairo_reset_clip(cr); - } -} - - -static void watermark(struct _srcontext *sr) -{ - show_text(sr->cr, "Written by partialator from CrystFEL" - " version "PACKAGE_VERSION, sr->h-15.0, J_RIGHT, - "Sans 7"); -} - - -static void new_page(struct _srcontext *sr) -{ - cairo_show_page(sr->cr); - watermark(sr); -} - - -static void find_most_sampled_reflections(RefList *list, int n, signed int *h, - signed int *k, signed int *l) -{ - Reflection *refl; - RefListIterator *iter; - int *samples; - int j; - - for ( j=0; j<n; j++ ) { - h[j] = 0; - k[j] = 0; - l[j] = 0; - } - - samples = calloc(n, sizeof(int)); - - for ( refl = first_refl(list, &iter); - refl != NULL; - refl = next_refl(refl, iter) ) - { - int red; - int i; - - red = get_redundancy(refl); - - for ( i=0; i<n; i++ ) { - - if ( red > samples[i] ) { - - int j; - - /* Shift everything down */ - for ( j=n-2; j>i; j-- ) { - h[j+1] = h[j]; - k[j+1] = k[j]; - l[j+1] = l[j]; - samples[j+1] = samples[j]; - } - - /* Add this in its place */ - get_indices(refl, &h[i], &k[i], &l[i]); - samples[i] = red; - - /* Don't compare against the others */ - break; - - } - - } - - } - - free(samples); -} - - - -SRContext *sr_titlepage(Crystal **crystals, int n, - const char *filename, const char *stream_filename, - const char *cmdline) -{ - char tmp[1024]; - struct _srcontext *sr; - - sr = malloc(sizeof(*sr)); - if ( sr == NULL ) return NULL; - - sr->w = PAGE_WIDTH; - sr->h = 595.0; - - sr->surf = cairo_pdf_surface_create(filename, sr->w, sr->h); - - if ( cairo_surface_status(sr->surf) != CAIRO_STATUS_SUCCESS ) { - fprintf(stderr, "Couldn't create Cairo surface\n"); - cairo_surface_destroy(sr->surf); - free(sr); - return NULL; - } - - sr->cr = cairo_create(sr->surf); - watermark(sr); - - snprintf(tmp, 1023, "%s", stream_filename); - show_text(sr->cr, tmp, 10.0, J_CENTER, "Sans Bold 16"); - snprintf(tmp, 1023, "partialator %s", cmdline); - show_text(sr->cr, tmp, 45.0, J_LEFT, "Mono 7"); - - return sr; -} - - -void sr_iteration(SRContext *sr, int iteration, struct srdata *d) -{ - int i; - char page_title[1024]; - double dash[] = {2.0, 2.0}; - - if ( sr == NULL ) return; - - snprintf(page_title, 1023, "After %i iteration%s", - iteration, iteration==1?"":"s"); - - new_page(sr); - show_text(sr->cr, page_title, 10.0, J_CENTER, "Sans Bold 16"); - - cairo_save(sr->cr); - cairo_translate(sr->cr, 480.0, 350.0); - scale_factor_histogram(sr->cr, d->crystals, d->n, - "Distribution of overall scale factors"); - cairo_restore(sr->cr); - - /* Draw partiality plots (three graphs together) */ - cairo_save(sr->cr); - - cairo_translate(sr->cr, 70.0, 330.0); - partiality_graph(sr->cr, d->crystals, d->n, d->full); - - cairo_save(sr->cr); - cairo_move_to(sr->cr, 0.0, 0.0); - cairo_line_to(sr->cr, 0.0, -30.0); - cairo_move_to(sr->cr, 200.0, 0.0); - cairo_line_to(sr->cr, 200.0, -30.0); - cairo_set_dash(sr->cr, dash, 2, 0.0); - cairo_stroke(sr->cr); - cairo_set_dash(sr->cr, NULL, 0, 0.0); - cairo_translate(sr->cr, 0.0, -150.0); - partiality_histogram(sr->cr, d->crystals, d->n, d->full, 1, 0); - cairo_restore(sr->cr); - - cairo_save(sr->cr); - cairo_move_to(sr->cr, 200.0, 0.0); - cairo_line_to(sr->cr, 230.0, 00.0); - cairo_move_to(sr->cr, 200.0, 200.0); - cairo_line_to(sr->cr, 230.0, 200.0); - cairo_set_dash(sr->cr, dash, 2, 0.0); - cairo_stroke(sr->cr); - cairo_set_dash(sr->cr, NULL, 0, 0.0); - cairo_translate(sr->cr, 230.0, 200.0); - cairo_rotate(sr->cr, -M_PI_2); - partiality_histogram(sr->cr, d->crystals, d->n, d->full, 0, 1); - cairo_restore(sr->cr); - - cairo_restore(sr->cr); - - if ( iteration == 0 ) { - find_most_sampled_reflections(d->full, 9, - sr->ms_h, sr->ms_k, sr->ms_l); - } - - for ( i=0; i<9; i++ ) { - - int x, y; - - x = i % 3; - y = i / 3; - - cairo_save(sr->cr); - cairo_translate(sr->cr, 400.0+140.0*x, 60.0+80.0*y); - intensity_histogram(sr->cr, d->crystals, d->n, d->full, - sr->ms_h[i], sr->ms_k[i], sr->ms_l[i]); - cairo_restore(sr->cr); - - } -} - - -void sr_finish(SRContext *sr) -{ - cairo_surface_finish(sr->surf); - cairo_destroy(sr->cr); -} diff --git a/src/scaling-report.h b/src/scaling-report.h deleted file mode 100644 index 55037038..00000000 --- a/src/scaling-report.h +++ /dev/null @@ -1,82 +0,0 @@ -/* - * scaling-report.h - * - * Write a nice PDF of scaling parameters - * - * Copyright © 2012-2014 Deutsches Elektronen-Synchrotron DESY, - * a research centre of the Helmholtz Association. - * - * Authors: - * 2011-2014 Thomas White <taw@physics.org> - * - * This file is part of CrystFEL. - * - * CrystFEL is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * CrystFEL is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with CrystFEL. If not, see <http://www.gnu.org/licenses/>. - * - */ - -#ifndef SCALING_REPORT_H -#define SCALING_REPORT_H - -#ifdef HAVE_CONFIG_H -#include <config.h> -#endif - - -#include "utils.h" - -typedef struct _srcontext SRContext; /* Opaque */ - -/* Information is logged in this structure */ -struct srdata -{ - Crystal **crystals; - int n; - RefList *full; - - int n_filtered; - int n_refined; -}; - -#if defined(HAVE_CAIRO) && defined(HAVE_PANGO) && defined(HAVE_PANGOCAIRO) - -extern SRContext *sr_titlepage(Crystal **crystals, int n, - const char *filename, - const char *stream_filename, - const char *cmdline); - -extern void sr_iteration(SRContext *sr, int iteration, struct srdata *d); - -extern void sr_finish(SRContext *sr); - -#else /* defined(HAVE_CAIRO) && defined(HAVE_PANGO) && ... */ - -SRContext *sr_titlepage(Crystal **crystals, int n, const char *filename, - const char *stream_filename, const char *cmdline) -{ - return NULL; -} - -void sr_iteration(SRContext *sr, int iteration, struct srdata *d) -{ -} - -void sr_finish(SRContext *sr) -{ -} - -#endif /* defined(HAVE_CAIRO) && defined(HAVE_PANGO) && ... */ - - -#endif /* SCALING_REPORT_H */ diff --git a/tests/.gitignore b/tests/.gitignore index 53b6f706..2afe2ec8 100644 --- a/tests/.gitignore +++ b/tests/.gitignore @@ -3,9 +3,7 @@ list_check gpu_sim_check integration_check -pr_l_gradient_check pr_p_gradient_check -pr_pl_gradient_check symmetry_check centering_check transformation_check @@ -14,3 +12,4 @@ ring_check .dirstamp prof2d_check ambi_check +prediction_gradient_check diff --git a/tests/integration_check.c b/tests/integration_check.c index 2d0a6c7a..ccb613c3 100644 --- a/tests/integration_check.c +++ b/tests/integration_check.c @@ -120,7 +120,7 @@ int main(int argc, char *argv[]) list = reflist_new(); refl = add_refl(list, 0, 0, 0); - set_detector_pos(refl, 0.0, 64, 64); + set_detector_pos(refl, 64, 64); cell = cell_new(); cell_set_lattice_type(cell, L_CUBIC); cell_set_centering(cell, 'P'); diff --git a/tests/pr_p_gradient_check.c b/tests/pr_p_gradient_check.c index cedfc9dc..1ae7b049 100644 --- a/tests/pr_p_gradient_check.c +++ b/tests/pr_p_gradient_check.c @@ -93,15 +93,15 @@ static UnitCell *new_shifted_cell(UnitCell *input, int k, double shift) &csx, &csy, &csz); switch ( k ) { - case REF_ASX : asx += shift; break; - case REF_ASY : asy += shift; break; - case REF_ASZ : asz += shift; break; - case REF_BSX : bsx += shift; break; - case REF_BSY : bsy += shift; break; - case REF_BSZ : bsz += shift; break; - case REF_CSX : csx += shift; break; - case REF_CSY : csy += shift; break; - case REF_CSZ : csz += shift; break; + case GPARAM_ASX : asx += shift; break; + case GPARAM_ASY : asy += shift; break; + case GPARAM_ASZ : asz += shift; break; + case GPARAM_BSX : bsx += shift; break; + case GPARAM_BSY : bsy += shift; break; + case GPARAM_BSZ : bsz += shift; break; + case GPARAM_CSX : csx += shift; break; + case GPARAM_CSY : csy += shift; break; + case GPARAM_CSZ : csz += shift; break; } cell_set_reciprocal(cell, asx, asy, asz, bsx, bsy, bsz, csx, csy, csz); @@ -113,7 +113,7 @@ static void shift_parameter(struct image *image, int k, double shift) { switch ( k ) { - case REF_DIV : image->div += shift; break; + case GPARAM_DIV : image->div += shift; break; } } @@ -135,21 +135,21 @@ static Crystal *new_shifted_crystal(Crystal *cr, int refine, double incr_val) switch ( refine ) { - case REF_ASX : - case REF_ASY : - case REF_ASZ : - case REF_BSX : - case REF_BSY : - case REF_BSZ : - case REF_CSX : - case REF_CSY : - case REF_CSZ : + case GPARAM_ASX : + case GPARAM_ASY : + case GPARAM_ASZ : + case GPARAM_BSX : + case GPARAM_BSY : + case GPARAM_BSZ : + case GPARAM_CSX : + case GPARAM_CSY : + case GPARAM_CSZ : cell = new_shifted_cell(crystal_get_cell(cr), refine, incr_val); crystal_set_cell(cr_new, cell); break; - case REF_R : + case GPARAM_R : cell = cell_new_from_cell(crystal_get_cell(cr)); crystal_set_cell(cr_new, cell); crystal_set_profile_radius(cr_new, r + incr_val); @@ -172,7 +172,7 @@ static void calc_either_side(Crystal *cr, double incr_val, RefList *compare; struct image *image = crystal_get_image(cr); - if ( (refine != REF_DIV) ) { + if ( (refine != GPARAM_DIV) ) { Crystal *cr_new; @@ -303,7 +303,7 @@ static double test_gradients(Crystal *cr, double incr_val, int refine, grad = (grad1 + grad2) / 2.0; i++; - cgrad = p_gradient(cr, refine, refl, pmodel); + cgrad = gradient(cr, refine, refl, pmodel); get_partial(refl, &r1, &r2, &p); @@ -465,51 +465,51 @@ int main(int argc, char *argv[]) &bz, &cx, &cy, &cz); incr_val = incr_frac * image.div; - val = test_gradients(cr, incr_val, REF_DIV, "div", "div", + val = test_gradients(cr, incr_val, GPARAM_DIV, "div", "div", pmodel, quiet, plot); if ( val < 0.99 ) fail = 1; incr_val = incr_frac * crystal_get_profile_radius(cr); - val = test_gradients(cr, incr_val, REF_R, "R", "R", pmodel, + val = test_gradients(cr, incr_val, GPARAM_R, "R", "R", pmodel, quiet, plot); if ( val < 0.99 ) fail = 1; incr_val = incr_frac * ax; - val = test_gradients(cr, incr_val, REF_ASX, "ax*", "x", pmodel, + val = test_gradients(cr, incr_val, GPARAM_ASX, "ax*", "x", pmodel, quiet, plot); if ( val < 0.99 ) fail = 1; incr_val = incr_frac * bx; - val = test_gradients(cr, incr_val, REF_BSX, "bx*", "x", pmodel, + val = test_gradients(cr, incr_val, GPARAM_BSX, "bx*", "x", pmodel, quiet, plot); if ( val < 0.99 ) fail = 1; incr_val = incr_frac * cx; - val = test_gradients(cr, incr_val, REF_CSX, "cx*", "x", pmodel, + val = test_gradients(cr, incr_val, GPARAM_CSX, "cx*", "x", pmodel, quiet, plot); if ( val < 0.99 ) fail = 1; incr_val = incr_frac * ay; - val = test_gradients(cr, incr_val, REF_ASY, "ay*", "y", pmodel, + val = test_gradients(cr, incr_val, GPARAM_ASY, "ay*", "y", pmodel, quiet, plot); if ( val < 0.99 ) fail = 1; incr_val = incr_frac * by; - val = test_gradients(cr, incr_val, REF_BSY, "by*", "y", pmodel, + val = test_gradients(cr, incr_val, GPARAM_BSY, "by*", "y", pmodel, quiet, plot); if ( val < 0.99 ) fail = 1; incr_val = incr_frac * cy; - val = test_gradients(cr, incr_val, REF_CSY, "cy*", "y", pmodel, + val = test_gradients(cr, incr_val, GPARAM_CSY, "cy*", "y", pmodel, quiet, plot); if ( val < 0.99 ) fail = 1; incr_val = incr_frac * az; - val = test_gradients(cr, incr_val, REF_ASZ, "az*", "z", pmodel, + val = test_gradients(cr, incr_val, GPARAM_ASZ, "az*", "z", pmodel, quiet, plot); if ( val < 0.99 ) fail = 1; incr_val = incr_frac * bz; - val = test_gradients(cr, incr_val, REF_BSZ, "bz*", "z", pmodel, + val = test_gradients(cr, incr_val, GPARAM_BSZ, "bz*", "z", pmodel, quiet, plot); if ( val < 0.99 ) fail = 1; incr_val = incr_frac * cz; - val = test_gradients(cr, incr_val, REF_CSZ, "cz*", "z", pmodel, + val = test_gradients(cr, incr_val, GPARAM_CSZ, "cz*", "z", pmodel, quiet, plot); if ( val < 0.99 ) fail = 1; diff --git a/tests/prediction_gradient_check.c b/tests/prediction_gradient_check.c new file mode 100644 index 00000000..18c9f023 --- /dev/null +++ b/tests/prediction_gradient_check.c @@ -0,0 +1,522 @@ +/* + * prediction_gradient_check.c + * + * Check partiality gradients for prediction refinement + * + * Copyright © 2012-2015 Deutsches Elektronen-Synchrotron DESY, + * a research centre of the Helmholtz Association. + * + * Authors: + * 2012-2015 Thomas White <taw@physics.org> + * + * This file is part of CrystFEL. + * + * CrystFEL is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * CrystFEL is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with CrystFEL. If not, see <http://www.gnu.org/licenses/>. + * + */ + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + + +#include <stdlib.h> +#include <stdio.h> +#include <gsl/gsl_statistics.h> +#include <getopt.h> + +#include <image.h> +#include <cell.h> +#include <cell-utils.h> +#include <geometry.h> +#include <reflist.h> +#include "../src/predict-refine.c" + + +int checkrxy; + + +static void scan(RefList *reflections, RefList *compare, + int *valid, long double *vals[3], int idx) +{ + int i; + Reflection *refl; + RefListIterator *iter; + + i = 0; + for ( refl = first_refl(reflections, &iter); + refl != NULL; + refl = next_refl(refl, iter) ) + { + signed int h, k, l; + Reflection *refl2; + double rlow, rhigh, p; + double fs, ss, xh, yh; + struct panel *panel; + + get_indices(refl, &h, &k, &l); + refl2 = find_refl(compare, h, k, l); + if ( refl2 == NULL ) { + valid[i] = 0; + i++; + continue; + } + + get_partial(refl2, &rlow, &rhigh, &p); + get_detector_pos(refl2, &fs, &ss); + panel = get_panel(refl2); + twod_mapping(fs, ss, &xh, &yh, panel); + + switch ( checkrxy ) { + + case 0 : + vals[idx][i] = (rlow + rhigh)/2.0; + break; + + case 1 : + vals[idx][i] = xh; + break; + + case 2 : + vals[idx][i] = yh; + break; + } + + i++; + } +} + + +static UnitCell *new_shifted_cell(UnitCell *input, int k, double shift) +{ + UnitCell *cell; + double asx, asy, asz; + double bsx, bsy, bsz; + double csx, csy, csz; + + cell = cell_new(); + cell_get_reciprocal(input, &asx, &asy, &asz, &bsx, &bsy, &bsz, + &csx, &csy, &csz); + switch ( k ) + { + case GPARAM_ASX : asx += shift; break; + case GPARAM_ASY : asy += shift; break; + case GPARAM_ASZ : asz += shift; break; + case GPARAM_BSX : bsx += shift; break; + case GPARAM_BSY : bsy += shift; break; + case GPARAM_BSZ : bsz += shift; break; + case GPARAM_CSX : csx += shift; break; + case GPARAM_CSY : csy += shift; break; + case GPARAM_CSZ : csz += shift; break; + } + cell_set_reciprocal(cell, asx, asy, asz, bsx, bsy, bsz, csx, csy, csz); + + return cell; +} + + +static Crystal *new_shifted_crystal(Crystal *cr, int refine, double incr_val) +{ + Crystal *cr_new; + UnitCell *cell; + + cr_new = crystal_copy(cr); + if ( cr_new == NULL ) { + ERROR("Failed to allocate crystal.\n"); + return NULL; + } + + crystal_set_image(cr_new, crystal_get_image(cr)); + + switch ( refine ) { + + case GPARAM_ASX : + case GPARAM_ASY : + case GPARAM_ASZ : + case GPARAM_BSX : + case GPARAM_BSY : + case GPARAM_BSZ : + case GPARAM_CSX : + case GPARAM_CSY : + case GPARAM_CSZ : + cell = new_shifted_cell(crystal_get_cell(cr), refine, + incr_val); + crystal_set_cell(cr_new, cell); + break; + + default: + ERROR("Can't shift %i\n", refine); + break; + + } + + return cr_new; +} + + +static void calc_either_side(Crystal *cr, double incr_val, + int *valid, long double *vals[3], int refine) +{ + RefList *compare; + struct image *image = crystal_get_image(cr); + Crystal *cr_new; + + cr_new = new_shifted_crystal(cr, refine, -incr_val); + compare = find_intersections(image, cr_new, PMODEL_SCSPHERE); + scan(crystal_get_reflections(cr), compare, valid, vals, 0); + cell_free(crystal_get_cell(cr_new)); + crystal_free(cr_new); + reflist_free(compare); + + cr_new = new_shifted_crystal(cr, refine, +incr_val); + compare = find_intersections(image, cr_new, PMODEL_SCSPHERE); + scan(crystal_get_reflections(cr), compare, valid, vals, 2); + cell_free(crystal_get_cell(cr_new)); + crystal_free(cr_new); + reflist_free(compare); +} + + +static double test_gradients(Crystal *cr, double incr_val, int refine, + const char *str, const char *file, + int quiet, int plot) +{ + Reflection *refl; + RefListIterator *iter; + long double *vals[3]; + int i; + int *valid; + int nref; + int n_good, n_invalid, n_small, n_nan, n_bad; + RefList *reflections; + FILE *fh = NULL; + int ntot = 0; + double total = 0.0; + char tmp[32]; + double *vec1; + double *vec2; + int n_line; + double cc; + + reflections = find_intersections(crystal_get_image(cr), cr, + PMODEL_SCSPHERE); + crystal_set_reflections(cr, reflections); + + nref = num_reflections(reflections); + if ( nref < 10 ) { + ERROR("Too few reflections found. Failing test by default.\n"); + return 0.0; + } + + vals[0] = malloc(nref*sizeof(long double)); + vals[1] = malloc(nref*sizeof(long double)); + vals[2] = malloc(nref*sizeof(long double)); + if ( (vals[0] == NULL) || (vals[1] == NULL) || (vals[2] == NULL) ) { + ERROR("Couldn't allocate memory.\n"); + return 0.0; + } + + valid = malloc(nref*sizeof(int)); + if ( valid == NULL ) { + ERROR("Couldn't allocate memory.\n"); + return 0.0; + } + for ( i=0; i<nref; i++ ) valid[i] = 1; + + scan(reflections, reflections, valid, vals, 1); + + calc_either_side(cr, incr_val, valid, vals, refine); + + if ( plot ) { + snprintf(tmp, 32, "gradient-test-%s.dat", file); + fh = fopen(tmp, "w"); + } + + vec1 = malloc(nref*sizeof(double)); + vec2 = malloc(nref*sizeof(double)); + if ( (vec1 == NULL) || (vec2 == NULL) ) { + ERROR("Couldn't allocate memory.\n"); + return 0.0; + } + + n_invalid = 0; n_good = 0; + n_nan = 0; n_small = 0; n_bad = 0; n_line = 0; + i = 0; + for ( refl = first_refl(reflections, &iter); + refl != NULL; + refl = next_refl(refl, iter) ) + { + long double grad1, grad2, grad; + double cgrad; + signed int h, k, l; + struct reflpeak rp; + + get_indices(refl, &h, &k, &l); + + if ( !valid[i] ) { + n_invalid++; + i++; + } else { + + double r1, r2, p; + + grad1 = (vals[1][i] - vals[0][i]) / incr_val; + grad2 = (vals[2][i] - vals[1][i]) / incr_val; + grad = (grad1 + grad2) / 2.0; + i++; + + if ( checkrxy == 0 ) { + + cgrad = r_gradient(crystal_get_cell(cr), refine, + refl, crystal_get_image(cr)); + + } else { + + struct imagefeature pk; + struct image *image; + + pk.fs = 0.0; + pk.ss = 0.0; + + image = crystal_get_image(cr); + rp.refl = refl; + rp.peak = &pk; + rp.panel = &image->det->panels[0]; + + if ( checkrxy == 1 ) { + cgrad = x_gradient(refine, &rp, + crystal_get_image(cr)->det, + crystal_get_image(cr)->lambda, + crystal_get_cell(cr)); + } else { + cgrad = y_gradient(refine, &rp, + crystal_get_image(cr)->det, + crystal_get_image(cr)->lambda, + crystal_get_cell(cr)); + } + } + + get_partial(refl, &r1, &r2, &p); + + if ( isnan(cgrad) ) { + n_nan++; + continue; + } + + if ( plot ) { + fprintf(fh, "%e %Le\n", cgrad, grad); + } + + vec1[n_line] = cgrad; + vec2[n_line] = grad; + n_line++; + + if ( (fabs(cgrad) < 5e-12) && (fabs(grad) < 5e-12) ) { + n_small++; + continue; + } + + total += fabs(cgrad - grad); + ntot++; + + if ( !within_tolerance(grad, cgrad, 5.0) + || !within_tolerance(cgrad, grad, 5.0) ) + { + + if ( !quiet ) { + STATUS("!- %s %3i %3i %3i" + " %10.2Le %10.2e ratio = %5.2Lf" + " %10.2e %10.2e\n", + str, h, k, l, grad, cgrad, + cgrad/grad, r1, r2); + } + n_bad++; + + } else { + + //STATUS("OK %s %3i %3i %3i" + // " %10.2Le %10.2e ratio = %5.2Lf" + // " %10.2e %10.2e\n", + // str, h, k, l, grad, cgrad, cgrad/grad, + // r1, r2); + + n_good++; + + } + + } + + } + + STATUS("%3s: %3i within 5%%, %3i outside, %3i nan, %3i invalid, " + "%3i small. ", str, n_good, n_bad, n_nan, n_invalid, n_small); + + if ( plot ) { + fclose(fh); + } + + cc = gsl_stats_correlation(vec1, 1, vec2, 1, n_line); + STATUS("CC = %+f\n", cc); + return cc; +} + + +int main(int argc, char *argv[]) +{ + struct image image; + const double incr_frac = 1.0/100000.0; + double incr_val; + double ax, ay, az; + double bx, by, bz; + double cx, cy, cz; + UnitCell *cell; + Crystal *cr; + struct quaternion orientation; + int fail = 0; + int quiet = 0; + int plot = 0; + int c; + gsl_rng *rng; + UnitCell *rot; + double val; + + const struct option longopts[] = { + {"quiet", 0, &quiet, 1}, + {"plot", 0, &plot, 1}, + {0, 0, NULL, 0} + }; + + while ((c = getopt_long(argc, argv, "", longopts, NULL)) != -1) { + switch (c) { + + case 0 : + break; + + case '?' : + break; + + default : + ERROR("Unhandled option '%c'\n", c); + break; + + } + + } + + image.width = 1024; + image.height = 1024; + image.det = simple_geometry(&image); + image.det->panels[0].res = 13333.3; + image.det->panels[0].clen = 80e-3; + image.det->panels[0].coffset = 0.0; + + image.lambda = ph_en_to_lambda(eV_to_J(8000.0)); + image.div = 1e-3; + image.bw = 0.01; + image.filename = malloc(256); + + cr = crystal_new(); + if ( cr == NULL ) { + ERROR("Failed to allocate crystal.\n"); + return 1; + } + crystal_set_mosaicity(cr, 0.0); + crystal_set_profile_radius(cr, 0.005e9); + crystal_set_image(cr, &image); + + cell = cell_new_from_parameters(10.0e-9, 10.0e-9, 10.0e-9, + deg2rad(90.0), + deg2rad(90.0), + deg2rad(90.0)); + + rng = gsl_rng_alloc(gsl_rng_mt19937); + + for ( checkrxy=0; checkrxy<3; checkrxy++ ) { + + + switch ( checkrxy ) { + case 0 : + STATUS("Excitation error:\n"); + break; + case 1: + STATUS("x coordinate:\n"); + break; + default: + case 2: + STATUS("y coordinate:\n"); + break; + STATUS("WTF??\n"); + break; + } + + orientation = random_quaternion(rng); + rot = cell_rotate(cell, orientation); + crystal_set_cell(cr, rot); + + cell_get_reciprocal(rot, &ax, &ay, &az, + &bx, &by, &bz, &cx, &cy, &cz); + + if ( checkrxy != 2 ) { + + incr_val = incr_frac * ax; + val = test_gradients(cr, incr_val, GPARAM_ASX, + "ax*", "ax", quiet, plot); + if ( val < 0.99 ) fail = 1; + incr_val = incr_frac * bx; + val = test_gradients(cr, incr_val, GPARAM_BSX, + "bx*", "bx", quiet, plot); + if ( val < 0.99 ) fail = 1; + incr_val = incr_frac * cx; + val = test_gradients(cr, incr_val, GPARAM_CSX, + "cx*", "cx", quiet, plot); + if ( val < 0.99 ) fail = 1; + + } + + if ( checkrxy != 1 ) { + + incr_val = incr_frac * ay; + val = test_gradients(cr, incr_val, GPARAM_ASY, + "ay*", "ay", quiet, plot); + if ( val < 0.99 ) fail = 1; + incr_val = incr_frac * by; + val = test_gradients(cr, incr_val, GPARAM_BSY, + "by*", "by", quiet, plot); + if ( val < 0.99 ) fail = 1; + incr_val = incr_frac * cy; + val = test_gradients(cr, incr_val, GPARAM_CSY, + "cy*", "cy", quiet, plot); + if ( val < 0.99 ) fail = 1; + + } + + incr_val = incr_frac * az; + val = test_gradients(cr, incr_val, GPARAM_ASZ, "az*", "az", + quiet, plot); + if ( val < 0.99 ) fail = 1; + incr_val = incr_frac * bz; + val = test_gradients(cr, incr_val, GPARAM_BSZ, "bz*", "bz", + quiet, plot); + if ( val < 0.99 ) fail = 1; + incr_val = incr_frac * cz; + val = test_gradients(cr, incr_val, GPARAM_CSZ, "cz*", "cz", + quiet, plot); + if ( val < 0.99 ) fail = 1; + + } + + gsl_rng_free(rng); + + return fail; +} |