aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--CMake/FindCBF.cmake31
-rw-r--r--CMakeLists.txt10
-rw-r--r--README8
-rw-r--r--doc/man/cell_tool.1147
-rw-r--r--doc/man/crystfel_geometry.58
-rw-r--r--doc/man/geoptimiser.110
-rw-r--r--doc/man/indexamajig.16
-rw-r--r--doc/man/partial_sim.16
-rw-r--r--doc/man/partialator.13
-rw-r--r--doc/matrix-notation.lyx868
-rw-r--r--doc/matrix-notation.pdfbin0 -> 156692 bytes
-rw-r--r--doc/reference/libcrystfel/CrystFEL-docs.sgml136
-rw-r--r--doc/reference/libcrystfel/CrystFEL-sections.txt619
-rwxr-xr-xdoc/reference/libcrystfel/build-docs14
-rw-r--r--doc/reference/libcrystfel/xml/coding-standards.xml157
-rw-r--r--doc/reference/libcrystfel/xml/gtkdocentities.ent.cmake.in8
-rw-r--r--libcrystfel/CMakeLists.txt36
-rw-r--r--libcrystfel/config.h.cmake.in8
-rw-r--r--libcrystfel/doc/coding.md221
-rw-r--r--libcrystfel/doc/index.md58
-rw-r--r--libcrystfel/src/asdf.c6
-rw-r--r--libcrystfel/src/asdf.h5
-rw-r--r--libcrystfel/src/cell-utils.c869
-rw-r--r--libcrystfel/src/cell-utils.h37
-rw-r--r--libcrystfel/src/cell.c634
-rw-r--r--libcrystfel/src/cell.h61
-rw-r--r--libcrystfel/src/crystal.c36
-rw-r--r--libcrystfel/src/crystal.h7
-rw-r--r--libcrystfel/src/detector.c250
-rw-r--r--libcrystfel/src/detector.h140
-rw-r--r--libcrystfel/src/dirax.c2
-rw-r--r--libcrystfel/src/dirax.h3
-rw-r--r--libcrystfel/src/events.c8
-rw-r--r--libcrystfel/src/events.h7
-rw-r--r--libcrystfel/src/felix.c2
-rw-r--r--libcrystfel/src/felix.h5
-rw-r--r--libcrystfel/src/filters.c1
-rw-r--r--libcrystfel/src/filters.h4
-rw-r--r--libcrystfel/src/geometry.c122
-rw-r--r--libcrystfel/src/geometry.h21
-rw-r--r--libcrystfel/src/hdf5-file.c264
-rw-r--r--libcrystfel/src/hdf5-file.h5
-rw-r--r--libcrystfel/src/image.c606
-rw-r--r--libcrystfel/src/image.h223
-rw-r--r--libcrystfel/src/index.c9
-rw-r--r--libcrystfel/src/index.h85
-rw-r--r--libcrystfel/src/integer_matrix.c202
-rw-r--r--libcrystfel/src/integer_matrix.h25
-rw-r--r--libcrystfel/src/integration.c6
-rw-r--r--libcrystfel/src/integration.h70
-rw-r--r--libcrystfel/src/mosflm.c1
-rw-r--r--libcrystfel/src/mosflm.h5
-rw-r--r--libcrystfel/src/peakfinder8.c18
-rw-r--r--libcrystfel/src/peakfinder8.h5
-rw-r--r--libcrystfel/src/peaks.c34
-rw-r--r--libcrystfel/src/peaks.h5
-rw-r--r--libcrystfel/src/predict-refine.c3
-rw-r--r--libcrystfel/src/predict-refine.h5
-rw-r--r--libcrystfel/src/rational.c647
-rw-r--r--libcrystfel/src/rational.h108
-rw-r--r--libcrystfel/src/reflist-utils.c93
-rw-r--r--libcrystfel/src/reflist-utils.h13
-rw-r--r--libcrystfel/src/reflist.c345
-rw-r--r--libcrystfel/src/reflist.h34
-rw-r--r--libcrystfel/src/render.c1
-rw-r--r--libcrystfel/src/render.h4
-rw-r--r--libcrystfel/src/spectrum.c591
-rw-r--r--libcrystfel/src/spectrum.h94
-rw-r--r--libcrystfel/src/statistics.c693
-rw-r--r--libcrystfel/src/statistics.h68
-rw-r--r--libcrystfel/src/stream.c209
-rw-r--r--libcrystfel/src/stream.h31
-rw-r--r--libcrystfel/src/symmetry.c355
-rw-r--r--libcrystfel/src/symmetry.h22
-rw-r--r--libcrystfel/src/symop.l52
-rw-r--r--libcrystfel/src/symop.y140
-rw-r--r--libcrystfel/src/taketwo.c153
-rw-r--r--libcrystfel/src/taketwo.h2
-rw-r--r--libcrystfel/src/thread-pool.c47
-rw-r--r--libcrystfel/src/thread-pool.h28
-rw-r--r--libcrystfel/src/utils.c76
-rw-r--r--libcrystfel/src/utils.h20
-rw-r--r--libcrystfel/src/xds.c1
-rw-r--r--libcrystfel/src/xds.h5
-rw-r--r--libcrystfel/src/xgandalf.c32
-rw-r--r--libcrystfel/src/xgandalf.h6
-rwxr-xr-xscripts/extract-geom2
-rwxr-xr-xscripts/move-entire-detector41
-rwxr-xr-xscripts/plot-pr15
-rwxr-xr-xscripts/plot-pr-contourmap (renamed from scripts/plot-contourmap)11
-rw-r--r--src/cell_explorer.c480
-rw-r--r--src/cell_tool.c581
-rw-r--r--src/check_hkl.c45
-rw-r--r--src/cl-utils.c38
-rw-r--r--src/cl-utils.h15
-rw-r--r--src/compare_hkl.c1
-rw-r--r--src/diffraction-gpu.c32
-rw-r--r--src/diffraction-gpu.h4
-rw-r--r--src/diffraction.c336
-rw-r--r--src/diffraction.h2
-rw-r--r--src/dw-hdfsee.c178
-rw-r--r--src/dw-hdfsee.h6
-rw-r--r--src/geoptimiser.c138
-rw-r--r--src/get_hkl.c4
-rw-r--r--src/indexamajig.c120
-rw-r--r--src/merge.c61
-rw-r--r--src/merge.h8
-rw-r--r--src/partial_sim.c97
-rw-r--r--src/partialator.c92
-rw-r--r--src/pattern_sim.c82
-rw-r--r--src/post-refinement.c729
-rw-r--r--src/post-refinement.h5
-rw-r--r--src/process_hkl.c1
-rw-r--r--src/process_image.h2
-rw-r--r--src/rejection.c143
-rw-r--r--src/rejection.h6
-rw-r--r--src/whirligig.c20
-rw-r--r--tests/CMakeLists.txt13
-rw-r--r--tests/centering_check.c22
-rw-r--r--tests/gpu_sim_check.c26
-rw-r--r--tests/histogram.c (renamed from libcrystfel/src/histogram.c)2
-rw-r--r--tests/histogram.h (renamed from libcrystfel/src/histogram.h)5
-rw-r--r--tests/integration_check.c3
-rw-r--r--tests/prof2d_check.c3
-rw-r--r--tests/rational_check.c194
-rw-r--r--tests/spectrum_check.c114
-rw-r--r--tests/transformation_check.c246
127 files changed, 8530 insertions, 5408 deletions
diff --git a/CMake/FindCBF.cmake b/CMake/FindCBF.cmake
deleted file mode 100644
index e51fd01a..00000000
--- a/CMake/FindCBF.cmake
+++ /dev/null
@@ -1,31 +0,0 @@
-# - Find CBF
-# Find the native CBF includes and library
-#
-# CBF_INCLUDES - where to find cbf.h
-# CBF_LIBRARIES - List of libraries when using CBF.
-# CBF_FOUND - True if CBF found.
-
-if (CBF_INCLUDES)
- # Already in cache, be silent
- set (CBF_FIND_QUIETLY TRUE)
-endif (CBF_INCLUDES)
-
-find_path (CBF_CBF_INCLUDES cbf/cbf.h)
-find_path (CBFLIB_CBF_INCLUDES cbflib/cbf.h)
-
-if (NOT CBF_CBF_INCLUDES MATCHES "NOTFOUND")
- message(STATUS "Found cbf/cbf.h in ${CBF_CBF_INCLUDES}")
- set(CBF_INCLUDES ${CBF_CBF_INCLUDES})
- set(HAVE_CBF_CBF_H ON)
-elseif (NOT CBFLIB_CBF_INCLUDES MATCHES "NOTFOUND")
- message(STATUS "Found cbflib/cbf.h in ${CBFLIB_CBF_INCLUDES}")
- set(CBF_INCLUDES ${CBFLIB_CBF_INCLUDES})
- set(HAVE_CBFLIB_CBF_H ON)
-endif (NOT CBF_CBF_INCLUDES MATCHES "NOTFOUND")
-
-find_library (CBF_LIBRARIES NAMES cbf)
-
-# handle the QUIETLY and REQUIRED arguments and set CBF_FOUND to TRUE if
-# all listed variables are TRUE
-include (FindPackageHandleStandardArgs)
-find_package_handle_standard_args (CBF DEFAULT_MSG CBF_LIBRARIES CBF_INCLUDES)
diff --git a/CMakeLists.txt b/CMakeLists.txt
index ec732277..43a61e4f 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -386,6 +386,15 @@ target_link_libraries(make_pixelmap ${COMMON_LIBRARIES} ${HDF5_C_LIBRARIES})
list(APPEND CRYSTFEL_EXECUTABLES make_pixelmap)
# ----------------------------------------------------------------------
+# cell_tool
+
+set(CELL_TOOL_SOURCES src/cell_tool.c)
+add_executable(cell_tool ${CELL_TOOL_SOURCES})
+target_include_directories(cell_tool PRIVATE ${COMMON_INCLUDES})
+target_link_libraries(cell_tool ${COMMON_LIBRARIES})
+list(APPEND CRYSTFEL_EXECUTABLES cell_tool)
+
+# ----------------------------------------------------------------------
# Install targets
set_target_properties(${CRYSTFEL_EXECUTABLES}
@@ -417,6 +426,7 @@ install(FILES
doc/man/render_hkl.1
doc/man/whirligig.1
doc/man/make_pixelmap.1
+ doc/man/cell_tool.1
DESTINATION ${CMAKE_INSTALL_MANDIR}/man1
)
diff --git a/README b/README
index aede37fc..d1afbf47 100644
--- a/README
+++ b/README
@@ -129,7 +129,7 @@ $ sudo make install
At a minimum, you will need the HDF5 library (version 1.8.0 or later) and the
GNU Scientific Library (GSL). For a full installation, you will also need
-libTIFF, libPNG, Cairo, FFTW3, CBFLib and GTK2. All of these should be
+libTIFF, libPNG, Cairo, FFTW3 and GTK2. All of these should be
installed from your distribution's package manager. Be sure to install the
development files for each package, which will have a name like "gtk+-2.0-dev"
or "gtk+-2.0-devel", depending on which distribution you use.
@@ -142,12 +142,6 @@ cmake -DXGANDALF_INCLUDES=/path/to/xgandalf/include \
The path to HDF5 can be set similarly:
cmake -DHDF5_ROOT=/path/to/hdf5 (... /include, /lib etc)
-Or for CBF, use one of:
--DCBF_CBF_INCLUDES=/path/to/cbflib/include (... /cbf/cbf.h)
--DCBFLIB_CBF_INCLUDES=/path/to/cbflib/include (... /cbflib/cbf.h)
-and:
--DCBF_LIBRARIES=/path/to/libcbf.so
-
To install CrystFEL in a custom location, use:
-DCMAKE_INSTALL_PREFIX=/path/for/crystfel/installation
diff --git a/doc/man/cell_tool.1 b/doc/man/cell_tool.1
new file mode 100644
index 00000000..576943d0
--- /dev/null
+++ b/doc/man/cell_tool.1
@@ -0,0 +1,147 @@
+.\"
+.\" cell_tool man page
+.\"
+.\" Copyright © 2015-2019 Deutsches Elektronen-Synchrotron DESY,
+.\" a research centre of the Helmholtz Association.
+.\"
+.\" Part of CrystFEL - crystallography with a FEL
+.\"
+
+.TH CELL_TOOL 1
+.SH NAME
+cell_tool \- manipulate unit cells
+.SH SYNOPSIS
+.PP
+\fBcell_tool --find-ambi \fImy_structure.cell \fR[\fB-y \fImypointgroup\fR] [\fB--tolerance=\fItols\fR]
+.PP
+\fBcell_tool --uncenter \fImy_structure.cell \fR[\fB-o \fIoutput.cell\fR]
+.PP
+\fBcell_tool --rings \fImy_structure.cell \fR[\fB--highres=\fIangstroms\fR]
+.PP
+\fBcell_tool --compare-cell \fIreference.cell \fImy_structure.cell \fR[\fB--tolerance=\fItols\fR]
+.PP
+\fBcell_tool --transform=\fIop\fR \fImy_structure.cell
+.PP
+\fBcell_tool --help\fI
+
+.SH DESCRIPTION
+\fBcell_tool\fR performs various manipulations on unit cells, including generating power ring positions, comparing one unit cell to another, calculating a primitive unit cell from a centered one and searching for indexing ambiguities.
+.PP
+The unit cell can be given as a CrystFEL unit cell file, or alternatively as a PDB file.
+
+.SH CALCULATING POWDER RING POSITIONS
+.PP
+\fBcell_tool --rings \fImy_structure.cell \fR[--highres=\fIangstroms\fR] [-y \fIpg\fR]
+.PP
+This will generate a list of d-spacings and hkl values for the powder rings given by the unit cell file. Note that screw axis and glide plane absences will not be taken into account, so some rings may be absent depending on the space group.
+.PP
+If you additionally specify the point group using \fB-y\fR (see 'man crystfel' for how to specify point groups), symmetrically equivalent rings will be combined and multiplicities calculated.
+
+.SH GENERATING A PRIMITIVE UNIT CELL
+.PP
+\fBcell_tool --uncenter \fImy_structure.cell \fR[-o \fIoutput.cell\fR]
+.PP
+This will generate a primitive unit cell representing the same lattice as the input. Add the \fB-o\fR option to write the result to a new unit cell file.
+.PP
+There are an infinite number of primitive unit cell for any lattice. This program generates only one of them.
+
+.SH COMPARING UNIT CELLS
+.PP
+\fBcell_tool --compare-cell \fIreference.cell my_structure.cell \fR[\fB--tolerance=\fItols\fR]
+.PP
+The program will compare the two cells, and report if \fImy_structure.cell\fR can be made to look similar to \fIreference.cell\fR applying any transformation matrix.
+.PP
+The tolerance \fItols\fR is given as lengthtol,angtol, in percent and degrees respectively, which will be applied to the real-space unit cell axis lengths and angles.
+
+.SH TRANSFORMING A UNIT CELL
+.PP
+\fBcell_tool --transform=\fIop\fR \fImy_structure.cell
+.PP
+The program will transform the unit cell according to \fIop\fR. Example: \fB--transform=b,c,a\fR means to permute the axes such that the new \fIa\fR axis matches the old \fIb\fR axis, and so on.
+
+.SH FINDING INDEXING AMBIGUITIES
+.PP
+\fBcell_tool --find-ambi \fImy_structure.cell \fR[-y \fIpg\fR] [\fB--tolerance=\fItols\fR]
+.PP
+The program will report all transformation matrices which produce a similar unit cell, to within the specified tolerance. The tolerance \fItols\fR is given as lengthtol,angtol, in percent and degrees respectively, which will be applied to the real-space unit cell axis lengths and angles.
+.PP
+If you additionally give the true symmetry using \fB-y\fR, the program will calculate the ambiguity operators, i.e. the operations which are not symmetry operators of the structure, but which nevertheless leave the lattice looking the same.
+.PP
+\fBExample 1: Merohedral indexing ambiguity in photosystem I\fR
+
+The space group of photosystem I crystals as described by PDB code 1JB0 is P63,
+so the point group is '6':
+
+$ cell_tool --find-ambi 1JB0.pdb -y 6
+.nf
+[...]
+Observed symmetry operations:
+ 1 : hkl -h-k,k,-l -h-k,h,l -h,-k,l -h,h+k,-l
+ -k,-h,-l -k,h+k,l k,-h-k,l k,h,-l h,-h-k,-l
+ h+k,-h,l h+k,-k,-l
+Ambiguity operations:
+ 1 -> 6 : hkl -h-k,k,-l
+.fi
+
+There are 12 reflections which cannot be distinguished between by the lattice alone, but only 6 of those are true symmetry equivalents according to the structure. The transformation describing the indexing ambiguity as follows: "A reflection hkl will be confused with one with indices -h-k,k,-l". Had the point group of the crystals been '622', there would have been no indexing ambiguity (try it!).
+
+.PP
+\fBExample 2: No indexing ambiguity in lysozyme\fR
+
+The space group of lysozyme crystals as described by PDB code 1VDS is P 43 21 2, so the point group is '422':
+
+.nf
+$ cell_tool --find-ambi 1VDS.pdb -y 422
+[...]
+Observed symmetry operations:
+ 1 : hkl -h,-k,l -h,k,-l -k,-h,-l -k,h,l
+ k,-h,l k,h,-l h,-k,-l
+Ambiguity operations:
+ 1 -> 422 : hkl
+.fi
+
+All of the apparently equivalent reflections are true symmetry equivalents according to point group 422, so there is no indexing ambiguity. The only operation produced by left coset decomposition is the identity operation.
+
+.PP
+\fBExample 3: "Accidental" ambiguity in myoglobin\fR
+
+The space group of myoglobin crystals as described by PDB code 3VAU is P2, so the point group is '2'. Note that the "unique axis b" convention has been used for 3VAU (see "man crystfel" for information about specifying point groups):
+
+.nf
+$ cell_tool --find-ambi 3VAU.pdb -y 2_uab
+[...]
+ a b c alpha beta gamma
+ 3.51 2.84 6.29 nm 90.00 105.50 90.00 deg
+[...]
+ a b c alpha beta gamma
+ 3.51 2.84 6.33 nm 90.00 106.84 90.00 deg
+[...]
+Observed symmetry operations:
+ 1 : hkl -h,-k,h+l -h,k,-l h,-k,-h-l
+Ambiguity operations:
+ 1 -> 2 : hkl -h,-k,h+l
+.fi
+
+The transformations '-h,-k,h+l' and 'h,-k,-h-l', which correspond to indexing "diagonally", produce cells which look very similar to the original cell - a difference of only 0.4A and 1.34 degrees. These two transformations are themselves related by a twofold rotation, which is a true symmetry of this crystal structure. There is therefore only one ambiguity transformation. The transformation is strange because it isn't one of the symmetries displayed by a monoclinic lattice in general. This ambiguity has arisen because of of the particular unit cell parameters for this structure.
+
+.SH AUTHOR
+This page was written by Thomas White.
+
+.SH REPORTING BUGS
+Report bugs to <taw@physics.org>, or visit <http://www.desy.de/~twhite/crystfel>.
+
+.SH COPYRIGHT AND DISCLAIMER
+Copyright © 2015-2019 Deutsches Elektronen-Synchrotron DESY, a research centre of the Helmholtz Association.
+.P
+cell-tool, and this manual, are part of CrystFEL.
+.P
+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.
+.P
+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.
+.P
+You should have received a copy of the GNU General Public License along with CrystFEL. If not, see <http://www.gnu.org/licenses/>.
+
+.SH SEE ALSO
+.BR crystfel (7),
+.BR indexamajig (1),
+.BR get_hkl (1)
diff --git a/doc/man/crystfel_geometry.5 b/doc/man/crystfel_geometry.5
index db444214..381d05aa 100644
--- a/doc/man/crystfel_geometry.5
+++ b/doc/man/crystfel_geometry.5
@@ -99,6 +99,9 @@ the axis encoding the slow scan index
.IP fs
.PD
the axis encodes the fast scan index
+.IP \fInumber\fR
+.PD
+the index in this dimension should be fixed at \fInumber\fR.
.RE
.IP
CrystFEL assumes that the data block defined by the 'data' property has a dimensionality equal to the axis with the highest value of \fIn\fR defined by the 'dim' property, and requires the user to provide information about each of the axes in the data block. When no 'dim' property is defined in the geometry file, CrystFEL assumes the data block to be 2-dimensional, with the two axes encoding slow scan and fast scan information respectively.
@@ -107,15 +110,18 @@ Note that this does indeed mean that you can assign the fast scan coordinates to
Example:
.RS
+.PD 0
.IP
dim0 = %
.IP
dim1 = ss
.IP
dim2 = fs
+.IP
+dim3 = 4
.RE
.IP
-The above snippet specifies that the data block is 3-dimensional. The first axis represents the event number, the second the slow scan panel coordinate, and the third the fast scan panel coordinate.
+The above snippet specifies that the data block is 3-dimensional. The first axis represents the event number, the second the slow scan panel coordinate, the third the fast scan panel coordinate, and the index in the final axis is always 4.
.PD 0
.IP \fBmin_fs\fR
diff --git a/doc/man/geoptimiser.1 b/doc/man/geoptimiser.1
index 369a36f1..3caba9c2 100644
--- a/doc/man/geoptimiser.1
+++ b/doc/man/geoptimiser.1
@@ -27,8 +27,10 @@ For a complete description of the optimization algorithm, see the following pape
O. Yefanov, V. Mariani, C. Gati, T. A. White, H. N. Chapman, and A. Barty. "Accurate determination of segmented X-ray detector geometry". Optics Express 23 (2015) 28459. doi:10.1364/OE.23.028459.
.PP
-For minimal basic use, you need to provide a stream file with diffraction patterns, a geometry file to optimize, a filename for the output optimized geometry, and
-the name of two rigid group collections defined in the geometry file: one describing which panels in the detector are physically connected (and hence whose geometry should be optimized as if they were a single panel), and one to describe which panels are attached to the same underlying support (whose position and orientation are likely to be correlated).
+For minimal basic use, you need to provide a stream file with diffraction patterns, a geometry file to optimize, a filename for the output optimized geometry, and the name of two rigid group collections defined in the geometry file: one describing which panels in the detector are physically connected (and hence whose geometry should be optimized as if they were a single panel), and one to describe which panels are attached to the same underlying support (whose position and orientation are likely to be correlated).
+
+.PP
+If you leave out the \fB-g\fR option, the geometry file from the stream's audit information will be used. This is usually what you want.
.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.
@@ -49,7 +51,7 @@ Give the filename of the stream from which to read the indexed patterns.
.IP "\fB-g\fR \fIfilename\fR"
.IP \fB--geometry=\fR\fIfilename\fR
.PD
-Read the detector geometry to optimize from the \fIfilename\fR file.
+Read the detector geometry to optimize from \fIfilename\fR. If this option is omitted, the geometry file from the stream's header will be used (see \fB--input\fR), if present.
.PD 0
.IP "\fB-o\fR \fIfilename\fR"
@@ -139,7 +141,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 © 2014-2018 Deutsches Elektronen-Synchrotron DESY, a research centre of the Helmholtz Association.
+Copyright © 2014-2019 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/indexamajig.1 b/doc/man/indexamajig.1
index 95db227a..1866231a 100644
--- a/doc/man/indexamajig.1
+++ b/doc/man/indexamajig.1
@@ -423,6 +423,8 @@ These set low-level parameters for the Felix indexing algorithm.
.IP \fB--xgandalf-no-deviation-from-provided-cell\fR
.IP \fB--xgandalf-max-lattice-vector-length=\fIn\fR
.IP \fB--xgandalf-min-lattice-vector-length=\fIn\fR
+.IP \fB--xgandalf-max-peaks=\fIn\fR
+.IP \fB--xgandalf-fast-execution\fR
.PD
These set low-level parameters for the XGANDALF indexing algorithm.
.IP
@@ -435,6 +437,10 @@ These set low-level parameters for the XGANDALF indexing algorithm.
\fB--xgandalf-no-deviation-from-provided-cell\fR if a prior unit cell was provided, and this flag is set, the found unit cell will have exactly the same size as the provided one.
.IP
\fB--xgandalf-min-lattice-vector-length\fR and \fB--xgandalf-min-lattice-vector-length\fR minimum and maximum possible lattice vector lengths (unit is A). Used for fitting without prior lattice as starting point for gradient descent, so the final minimum lattice vector length can be smaller/highier as min/max. Note: This is valid for the uncentered cell, i.e. the P-cell! Default is 30A and 250A respectively.
+.IP
+\fB--xgandalf-max-peaks\fR maximum number of peaks used for indexing. For refinement all peaks are used. Peaks are selected by increasing radius. Limits the maximum execution time for patterns with a huge amount of peaks - either real ones or false positives. Default is 250.
+.IP
+\fB--xgandalf-fast-execution\fR Shortcut to set --xgandalf-sampling-pitch=2 --xgandalf-grad-desc-iterations=3
.SH INTEGRATION OPTIONS
.PD 0
diff --git a/doc/man/partial_sim.1 b/doc/man/partial_sim.1
index 72655758..d7b91040 100644
--- a/doc/man/partial_sim.1
+++ b/doc/man/partial_sim.1
@@ -148,6 +148,12 @@ Set the central photon energy, in eV, for the incident beam. The default is \fB
.IP \fB--really-random\fR
.PD
Seed the random number generator using the kernel random number generator (/dev/urandom). This means that truly random (although not "cryptographically random") numbers will be used for the orientation and crystal size, instead of the same sequence being used for each new run.
+
+.IP "\fB--template-stream=\fImy.stream\fR"
+.PD
+Get the crystal cell parameters, orientations and the reflections to calculate from \fImy.stream\fR. This allows you to re-calculate partial intensities using new beam parameters. There must only be one crystal per chunk in the template. If more than one thread is used (see \fB-j\fR), note that there is no guarantee that the order of crystals in the output stream will match that of \fImy.stream\fR.
+
+
.SH AUTHOR
This page was written by Thomas White.
diff --git a/doc/man/partialator.1 b/doc/man/partialator.1
index 1a6d30d3..f0491616 100644
--- a/doc/man/partialator.1
+++ b/doc/man/partialator.1
@@ -169,8 +169,9 @@ If you prefer, you can specify the ambiguity operator by specifying the apparent
.PD 0
.IP \fB--force-bandwidth=\fIbw\fR
.IP \fB--force-radius=\fIR\fR
+.IP \fB--force-lambda=\fIR\fR
.PD
-Set the X-ray bandwidth or initial profile radius for all crystals before proceeding, overriding the values from the stream. Bandwidth is given as a fraction, i.e. \fB--force-bandwidth=0.0013\fR means 0.13 percent (approximate FWHM). Radius is given in nm^-1.
+Set the X-ray bandwidth, initial profile radius or wavelength for all crystals before proceeding, overriding the values from the stream. Bandwidth is given as a fraction, i.e. \fB--force-bandwidth=0.0013\fR means 0.13 percent (approximate FWHM). Radius is given in nm^-1. Wavelength is given in Angstroms.
.SH PARTIALITY MODELS
diff --git a/doc/matrix-notation.lyx b/doc/matrix-notation.lyx
new file mode 100644
index 00000000..7279be8f
--- /dev/null
+++ b/doc/matrix-notation.lyx
@@ -0,0 +1,868 @@
+#LyX 2.3 created this file. For more info see http://www.lyx.org/
+\lyxformat 544
+\begin_document
+\begin_header
+\save_transient_properties true
+\origin unavailable
+\textclass article
+\use_default_options true
+\maintain_unincluded_children false
+\language english
+\language_package default
+\inputencoding auto
+\fontencoding global
+\font_roman "default" "default"
+\font_sans "default" "default"
+\font_typewriter "default" "default"
+\font_math "auto" "auto"
+\font_default_family default
+\use_non_tex_fonts false
+\font_sc false
+\font_osf false
+\font_sf_scale 100 100
+\font_tt_scale 100 100
+\use_microtype false
+\use_dash_ligatures true
+\graphics default
+\default_output_format default
+\output_sync 0
+\bibtex_command default
+\index_command default
+\paperfontsize default
+\spacing single
+\use_hyperref false
+\papersize a4paper
+\use_geometry true
+\use_package amsmath 1
+\use_package amssymb 1
+\use_package cancel 1
+\use_package esint 1
+\use_package mathdots 1
+\use_package mathtools 1
+\use_package mhchem 1
+\use_package stackrel 1
+\use_package stmaryrd 1
+\use_package undertilde 1
+\cite_engine basic
+\cite_engine_type default
+\biblio_style plain
+\use_bibtopic false
+\use_indices false
+\paperorientation portrait
+\suppress_date true
+\justification true
+\use_refstyle 1
+\use_minted 0
+\index Index
+\shortcut idx
+\color #008000
+\end_index
+\leftmargin 1cm
+\topmargin 1cm
+\rightmargin 1cm
+\bottommargin 1cm
+\headsep 0cm
+\secnumdepth 3
+\tocdepth 3
+\paragraph_separation skip
+\defskip smallskip
+\is_math_indent 0
+\math_numbering_side default
+\quotes_style english
+\dynamic_quotes 0
+\papercolumns 1
+\papersides 1
+\paperpagestyle empty
+\tracking_changes false
+\output_changes false
+\html_math_output 0
+\html_css_as_file 0
+\html_be_strict false
+\end_header
+
+\begin_body
+
+\begin_layout Title
+\begin_inset ERT
+status open
+
+\begin_layout Plain Layout
+
+
+\backslash
+vskip -3em
+\end_layout
+
+\end_inset
+
+CrystFEL matrix notation conventions
+\end_layout
+
+\begin_layout Standard
+\begin_inset ERT
+status open
+
+\begin_layout Plain Layout
+
+
+\backslash
+vskip -4em
+\end_layout
+
+\begin_layout Plain Layout
+
+
+\backslash
+thispagestyle{empty}
+\end_layout
+
+\end_inset
+
+
+\end_layout
+
+\begin_layout Standard
+CrystFEL's UnitCell structure doesn't expose its contents as a matrix, only
+ as named components (
+\begin_inset Formula $a_{x}^{*}$
+\end_inset
+
+,
+\begin_inset Formula $c_{y}$
+\end_inset
+
+ an so on).
+ However, the arrangement of the transformation matrices given to functions
+ such as cell_transform_rational() are important.
+ This document aims to make the conventions explicit.
+ The notation used in CrystFEL follows that used in the International Tables
+ for Crystallography, volume A, chapter 5.1 (
+\begin_inset Quotes eld
+\end_inset
+
+Transformations of the coordinate system (unit-cell transformations)
+\begin_inset Quotes erd
+\end_inset
+
+) and 11.1 (
+\begin_inset Quotes eld
+\end_inset
+
+Point coordinates, symmetry operations and their symbols
+\begin_inset Quotes erd
+\end_inset
+
+).
+\end_layout
+
+\begin_layout Itemize
+
+\emph on
+cell_transform_gsl_direct
+\emph default
+,
+\emph on
+cell_transform_rational
+\emph default
+ and
+\emph on
+cell_transform_intmat
+\emph default
+ all do what is written in equation
+\begin_inset CommandInset ref
+LatexCommand ref
+reference "eq:cell_transform"
+plural "false"
+caps "false"
+noprefix "false"
+
+\end_inset
+
+.
+ These functions all take a
+\begin_inset Formula $\boldsymbol{\hat{P}}$
+\end_inset
+
+ matrix.
+ The
+\begin_inset Quotes eld
+\end_inset
+
+intmat
+\begin_inset Quotes erd
+\end_inset
+
+ and
+\begin_inset Quotes eld
+\end_inset
+
+rational
+\begin_inset Quotes erd
+\end_inset
+
+ versions will additionally determine the centering of the resulting unit
+ cell (and hopefully eventually the lattice type and unique axis).
+\end_layout
+
+\begin_layout Itemize
+
+\emph on
+transform_indices
+\emph default
+ does what is written in equation
+\begin_inset CommandInset ref
+LatexCommand ref
+reference "eq:indices"
+plural "false"
+caps "false"
+noprefix "false"
+
+\end_inset
+
+.
+ It takes a vector of (reciprocal space) Miller indices and a
+\begin_inset Formula $\boldsymbol{\hat{P}}$
+\end_inset
+
+ matrix.
+\end_layout
+
+\begin_layout Itemize
+A SymOpList is essentially a list of
+\begin_inset Formula $\boldsymbol{\hat{W}}^{-1}$
+\end_inset
+
+ matrices, which, as shown below, behave like
+\begin_inset Formula $\boldsymbol{\hat{P}}$
+\end_inset
+
+ matrices.
+\end_layout
+
+\begin_layout Itemize
+transform_fractional_coords_rtnl takes a
+\begin_inset Formula $\boldsymbol{\hat{P}}$
+\end_inset
+
+ matrix and a column vector of fractional coordinates, calculates the
+\begin_inset Formula $\boldsymbol{\hat{Q}}$
+\end_inset
+
+ matrix and evaluates equation
+\begin_inset CommandInset ref
+LatexCommand ref
+reference "eq:fractional-coord-transform-basis-change"
+plural "false"
+caps "false"
+noprefix "false"
+
+\end_inset
+
+ (NB
+\series bold
+not
+\series default
+ equation
+\begin_inset CommandInset ref
+LatexCommand ref
+reference "eq:fractional-coord-transform-symmetry"
+plural "false"
+caps "false"
+noprefix "false"
+
+\end_inset
+
+).
+
+\emph on
+Implementation detail:
+\emph default
+it actually solves a matrix-vector equation rather than going all the way
+ to the inverted matrix.
+\end_layout
+
+\begin_layout Itemize
+transform_fractional_coords_rtnl_inverse takes a
+\begin_inset Formula $\boldsymbol{\hat{P}}$
+\end_inset
+
+ matrix and a column vector of fractional coordinates
+\begin_inset Formula $\boldsymbol{v}$
+\end_inset
+
+, and does the reverse of transform_fractional_coords_rtnl.
+ That means it just has to evaluate
+\begin_inset Formula $\boldsymbol{\hat{P}}\boldsymbol{v}$
+\end_inset
+
+.
+ This looks like equation
+\begin_inset CommandInset ref
+LatexCommand ref
+reference "eq:fractional-coord-transform-symmetry"
+plural "false"
+caps "false"
+noprefix "false"
+
+\end_inset
+
+.
+\end_layout
+
+\begin_layout Subsection*
+Matrices of unit cell components
+\end_layout
+
+\begin_layout Standard
+When matrix calculations are performed in CrystFEL, which is usually only
+ inside certain libcrystfel functions, basis vectors
+\begin_inset Formula $(\boldsymbol{a},\boldsymbol{b},\boldsymbol{c})$
+\end_inset
+
+ are written as the columns of the matrix.
+\end_layout
+
+\begin_layout Standard
+\begin_inset Formula
+\[
+\boldsymbol{\hat{M}}=\left(\begin{array}{ccc}
+a_{x} & b_{x} & c_{x}\\
+a_{y} & b_{y} & c_{y}\\
+a_{z} & b_{z} & c_{z}
+\end{array}\right)
+\]
+
+\end_inset
+
+
+\end_layout
+
+\begin_layout Standard
+Reciprocal basis vectors are written as the rows of the matrix.
+\end_layout
+
+\begin_layout Standard
+\begin_inset Formula
+\[
+\boldsymbol{\hat{R}}=\left(\begin{array}{ccc}
+a_{x}^{*} & a_{y}^{*} & a_{z}^{*}\\
+b_{x}^{*} & b_{y}^{*} & b_{z}^{*}\\
+c_{x}^{*} & c_{y}^{*} & c_{z}^{*}
+\end{array}\right)
+\]
+
+\end_inset
+
+
+\end_layout
+
+\begin_layout Standard
+Real and reciprocal spaces are sometimes said to be
+\begin_inset Quotes eld
+\end_inset
+
+inverse transposes of each other
+\begin_inset Quotes erd
+\end_inset
+
+.
+ The components of the two matrices are already transposed with respect
+ to one another (one has vectors in columns, the other in rows).
+ Therefore
+\begin_inset Formula $\boldsymbol{\hat{M}}=\boldsymbol{\hat{R}}^{-1}$
+\end_inset
+
+ and
+\begin_inset Formula $\boldsymbol{\hat{R}}=\boldsymbol{\hat{M}}^{-1}$
+\end_inset
+
+.
+\end_layout
+
+\begin_layout Subsection*
+Transformation of unit cell basis
+\end_layout
+
+\begin_layout Standard
+Given a transformation matrix
+\begin_inset Formula $\boldsymbol{\hat{P}}$
+\end_inset
+
+ which means
+\begin_inset Formula $\boldsymbol{a}^{\prime}=\boldsymbol{a}-\boldsymbol{b}$
+\end_inset
+
+,
+\begin_inset Formula $\boldsymbol{b}^{\prime}=\boldsymbol{b}$
+\end_inset
+
+,
+\begin_inset Formula $\boldsymbol{c}^{\prime}=\boldsymbol{c}$
+\end_inset
+
+:
+\end_layout
+
+\begin_layout Standard
+\begin_inset Formula
+\begin{equation}
+\boldsymbol{\hat{M}}^{\prime}=\boldsymbol{\hat{M}}\boldsymbol{\hat{P}}=\left(\begin{array}{ccc}
+a_{x} & b_{x} & c_{x}\\
+a_{y} & b_{y} & c_{y}\\
+a_{z} & b_{z} & c_{z}
+\end{array}\right)\left(\begin{array}{ccc}
+1 & 0 & 0\\
+\bar{1} & 1 & 0\\
+0 & 0 & 1
+\end{array}\right)\label{eq:cell_transform}
+\end{equation}
+
+\end_inset
+
+
+\end_layout
+
+\begin_layout Standard
+Note that the
+\begin_inset Quotes eld
+\end_inset
+
+amount of old a,b,c to make new a
+\begin_inset Quotes erd
+\end_inset
+
+ is found in the first column of the
+\begin_inset Formula $\boldsymbol{\hat{P}}$
+\end_inset
+
+ matrix, and so on.
+\end_layout
+
+\begin_layout Subsection*
+Transformation of reciprocal basis
+\end_layout
+
+\begin_layout Standard
+With the same transformation as before (
+\begin_inset Formula $\boldsymbol{a}^{\prime}=\boldsymbol{a}-\boldsymbol{b}$
+\end_inset
+
+,
+\begin_inset Formula $\boldsymbol{b}^{\prime}=\boldsymbol{b}$
+\end_inset
+
+,
+\begin_inset Formula $\boldsymbol{c}^{\prime}=\boldsymbol{c}$
+\end_inset
+
+), the reciprocal basis transforms as follows:
+\end_layout
+
+\begin_layout Standard
+\begin_inset Formula
+\[
+\boldsymbol{\hat{R}}^{\prime}=\left(\begin{array}{ccc}
+a_{x}^{*} & a_{y}^{*} & a_{z}^{*}\\
+b_{x}^{*} & b_{y}^{*} & b_{z}^{*}\\
+c_{x}^{*} & c_{y}^{*} & c_{z}^{*}
+\end{array}\right)^{\prime}=\boldsymbol{\hat{P}}^{-1}\boldsymbol{\hat{R}}=\boldsymbol{\hat{Q}}\boldsymbol{\hat{R}}=\left(\begin{array}{ccc}
+1 & 0 & 0\\
+\bar{1} & 1 & 0\\
+0 & 0 & 1
+\end{array}\right)^{-1}\left(\begin{array}{ccc}
+a_{x}^{*} & a_{y}^{*} & a_{z}^{*}\\
+b_{x}^{*} & b_{y}^{*} & b_{z}^{*}\\
+c_{x}^{*} & c_{y}^{*} & c_{z}^{*}
+\end{array}\right)=\left(\begin{array}{ccc}
+1 & 0 & 0\\
+1 & 1 & 0\\
+0 & 0 & 1
+\end{array}\right)\left(\begin{array}{ccc}
+a_{x}^{*} & a_{y}^{*} & a_{z}^{*}\\
+b_{x}^{*} & b_{y}^{*} & b_{z}^{*}\\
+c_{x}^{*} & c_{y}^{*} & c_{z}^{*}
+\end{array}\right)
+\]
+
+\end_inset
+
+
+\end_layout
+
+\begin_layout Subsection*
+Miller indices (reciprocal space) under transformation of unit cell basis
+ (real-space)
+\end_layout
+
+\begin_layout Standard
+From ITA chapter 5.1, this is how the reflections would be re-labelled if
+ the axes were changed.
+ See later for how to apply symmetry operations.
+\end_layout
+
+\begin_layout Standard
+\begin_inset Formula
+\begin{equation}
+\left(h^{\prime},k^{\prime},l^{\prime}\right)=\left(h^{\prime},k^{\prime},l^{\prime}\right)\boldsymbol{\hat{P}}=\left(h,k,l\right)\left(\begin{array}{ccc}
+1 & 0 & 0\\
+\bar{1} & 1 & 0\\
+0 & 0 & 1
+\end{array}\right)=(h-k,k,l)\label{eq:indices}
+\end{equation}
+
+\end_inset
+
+
+\end_layout
+
+\begin_layout Subsection*
+Fractional coordinates under transformation of unit cell basis
+\end_layout
+
+\begin_layout Standard
+Where u, v and w are fractional coordinates, i.e.
+ the cartesian coordinates are:
+\end_layout
+
+\begin_layout Standard
+\begin_inset Formula
+\[
+\boldsymbol{\hat{M}}\left(\begin{array}{c}
+u\\
+v\\
+w
+\end{array}\right)=\left(\begin{array}{ccc}
+a_{x} & b_{x} & c_{x}\\
+a_{y} & b_{y} & c_{y}\\
+a_{z} & b_{z} & c_{z}
+\end{array}\right)\left(\begin{array}{c}
+u\\
+v\\
+w
+\end{array}\right)
+\]
+
+\end_inset
+
+From ITA chapter 5.1, the new coordinates are:
+\end_layout
+
+\begin_layout Standard
+\begin_inset Formula
+\begin{equation}
+\left(\begin{array}{c}
+u^{\prime}\\
+v^{\prime}\\
+w^{\prime}
+\end{array}\right)=\boldsymbol{\hat{P}}^{-1}\left(\begin{array}{c}
+u\\
+v\\
+w
+\end{array}\right)=\boldsymbol{\hat{Q}}\left(\begin{array}{c}
+u\\
+v\\
+w
+\end{array}\right)\label{eq:fractional-coord-transform-basis-change}
+\end{equation}
+
+\end_inset
+
+
+\end_layout
+
+\begin_layout Subsection*
+Symmetry operations acting on fractional coordinates
+\end_layout
+
+\begin_layout Standard
+Following ITA chapter 11.1, the following matrix represents the symmetry
+ operation written as
+\begin_inset Formula $-y,x,z$
+\end_inset
+
+, where for this time only, x,y and z are fractional coordinates:
+\begin_inset Formula
+\begin{equation}
+\boldsymbol{\hat{W}}=\left(\begin{array}{ccc}
+0 & \bar{1} & 0\\
+1 & 0 & 0\\
+0 & 0 & 1
+\end{array}\right)\label{eq:wmatrix}
+\end{equation}
+
+\end_inset
+
+
+\end_layout
+
+\begin_layout Standard
+It transforms the fractional coordinates as follows:
+\end_layout
+
+\begin_layout Standard
+\begin_inset Formula
+\begin{equation}
+\left(\begin{array}{c}
+u^{\prime}\\
+v^{\prime}\\
+w^{\prime}
+\end{array}\right)=\boldsymbol{\hat{W}}\left(\begin{array}{c}
+u\\
+v\\
+w
+\end{array}\right)=\left(\begin{array}{ccc}
+0 & \bar{1} & 0\\
+1 & 0 & 0\\
+0 & 0 & 1
+\end{array}\right)\left(\begin{array}{c}
+u\\
+v\\
+w
+\end{array}\right)\label{eq:fractional-coord-transform-symmetry}
+\end{equation}
+
+\end_inset
+
+
+\end_layout
+
+\begin_layout Standard
+Note also that we're ignoring the translational part of the symmetry operation
+ (there isn't one in this case).
+\end_layout
+
+\begin_layout Standard
+Note that
+\begin_inset Formula $\boldsymbol{\hat{W}}$
+\end_inset
+
+ behaves like
+\begin_inset Formula $\boldsymbol{\hat{P}}^{-1}$
+\end_inset
+
+ above.
+ CrystFEL's
+\begin_inset Quotes eld
+\end_inset
+
+SymOpList
+\begin_inset Quotes erd
+\end_inset
+
+ structure contains IntegerMatrix objects representing
+\begin_inset Formula $\boldsymbol{\hat{W}}^{-1}$
+\end_inset
+
+ matrices, also known as
+\begin_inset Formula $\boldsymbol{\hat{P}}$
+\end_inset
+
+ matrices.
+\end_layout
+
+\begin_layout Subsection*
+Symmetry operations acting on Miller indices (reciprocal space)
+\end_layout
+
+\begin_layout Standard
+Since
+\begin_inset Formula $\boldsymbol{\hat{W}}^{-1}$
+\end_inset
+
+ matrices are just
+\begin_inset Formula $\boldsymbol{\hat{P}}$
+\end_inset
+
+ matrices, simply change the
+\begin_inset Formula $\boldsymbol{\hat{P}}$
+\end_inset
+
+ into
+\begin_inset Formula $\boldsymbol{\hat{W}}^{-1}$
+\end_inset
+
+ in equation
+\begin_inset CommandInset ref
+LatexCommand ref
+reference "eq:indices"
+plural "false"
+caps "false"
+noprefix "false"
+
+\end_inset
+
+:
+\end_layout
+
+\begin_layout Standard
+\begin_inset Formula
+\begin{equation}
+\left(h^{\prime},k^{\prime},l^{\prime}\right)=\left(h,k,l\right)\boldsymbol{\hat{W}}^{-1}=\left(h^{\prime},k^{\prime},l^{\prime}\right)\left(\begin{array}{ccc}
+0 & 1 & 0\\
+\bar{1} & 0 & 0\\
+0 & 0 & 1
+\end{array}\right)=(\bar{k},h,l)\label{eq:symmetry}
+\end{equation}
+
+\end_inset
+
+
+\end_layout
+
+\begin_layout Standard
+In this case, the symmetry operation is a rotation, so the matrix inverse
+ is just the same as a matrix transpose.
+ However, don't get confused: the matrix appearing here is the inverse of
+ the one appearing in equation
+\begin_inset CommandInset ref
+LatexCommand ref
+reference "eq:wmatrix"
+plural "false"
+caps "false"
+noprefix "false"
+
+\end_inset
+
+.
+\end_layout
+
+\begin_layout Standard
+Note that the
+\begin_inset Quotes eld
+\end_inset
+
+amount of old h, k and l to make new h
+\begin_inset Quotes erd
+\end_inset
+
+ is found in the first column of the
+\begin_inset Formula $\boldsymbol{\hat{W}}^{-1}$
+\end_inset
+
+ matrix, and so on.
+\end_layout
+
+\begin_layout Subsection*
+Transforming symmetry operations
+\end_layout
+
+\begin_layout Standard
+Given a
+\begin_inset Formula $\boldsymbol{\hat{P}}$
+\end_inset
+
+ matrix which transforms the unit cell as written in equation
+\begin_inset CommandInset ref
+LatexCommand ref
+reference "eq:cell_transform"
+plural "false"
+caps "false"
+noprefix "false"
+
+\end_inset
+
+, and a
+\begin_inset Formula $\boldsymbol{\hat{W}}^{-1}$
+\end_inset
+
+ matrix representing a symmetry operation, we seek to apply the symmetry
+ operation to the cell as it would appear after applying the transformation.
+ For example, let
+\begin_inset Formula $\boldsymbol{\hat{P}}$
+\end_inset
+
+ permute the cell axes, turning a
+\begin_inset Quotes eld
+\end_inset
+
+unique axis b
+\begin_inset Quotes erd
+\end_inset
+
+ cell into a
+\begin_inset Quotes eld
+\end_inset
+
+unique axis c
+\begin_inset Quotes erd
+\end_inset
+
+ one, and let
+\begin_inset Formula $\boldsymbol{\hat{W}}^{-1}$
+\end_inset
+
+ represent a twofold rotation around the c axis:
+\end_layout
+
+\begin_layout Standard
+To transform a set of Miller indices
+\begin_inset Formula $(h,k,l)$
+\end_inset
+
+, first use
+\begin_inset Formula $\boldsymbol{\hat{P}}$
+\end_inset
+
+ to get the indices referred to the
+\begin_inset Quotes eld
+\end_inset
+
+unique axis c
+\begin_inset Quotes erd
+\end_inset
+
+ setting using equation
+\begin_inset CommandInset ref
+LatexCommand ref
+reference "eq:indices"
+plural "false"
+caps "false"
+noprefix "false"
+
+\end_inset
+
+, then apply the symmetry operation using equation
+\begin_inset CommandInset ref
+LatexCommand ref
+reference "eq:symmetry"
+plural "false"
+caps "false"
+noprefix "false"
+
+\end_inset
+
+, and finally transform back to the original cell using equation
+\begin_inset CommandInset ref
+LatexCommand ref
+reference "eq:indices"
+plural "false"
+caps "false"
+noprefix "false"
+
+\end_inset
+
+ and the inverse matrix:
+\end_layout
+
+\begin_layout Standard
+\begin_inset Formula
+\[
+(h^{\prime},k^{\prime},l^{\prime})=(h,k,l)\boldsymbol{\hat{P}}\boldsymbol{\hat{W}}^{-1}\boldsymbol{\hat{P}}^{-1}
+\]
+
+\end_inset
+
+
+\end_layout
+
+\begin_layout Standard
+The combined matrix
+\begin_inset Formula $\boldsymbol{\hat{P}}\boldsymbol{\hat{W}}^{-1}\boldsymbol{\hat{P}}^{-1}$
+\end_inset
+
+ represents a twofold rotation symmetry operation along the b axis instead
+ of c.
+\end_layout
+
+\end_body
+\end_document
diff --git a/doc/matrix-notation.pdf b/doc/matrix-notation.pdf
new file mode 100644
index 00000000..b8cb6501
--- /dev/null
+++ b/doc/matrix-notation.pdf
Binary files differ
diff --git a/doc/reference/libcrystfel/CrystFEL-docs.sgml b/doc/reference/libcrystfel/CrystFEL-docs.sgml
deleted file mode 100644
index 150fadf9..00000000
--- a/doc/reference/libcrystfel/CrystFEL-docs.sgml
+++ /dev/null
@@ -1,136 +0,0 @@
-<?xml version="1.0"?>
-<!DOCTYPE book PUBLIC "-//OASIS//DTD DocBook XML V4.3//EN"
- "http://www.oasis-open.org/docbook/xml/4.3/docbookx.dtd"
-[
- <!ENTITY % local.common.attrib "xmlns:xi CDATA #FIXED 'http://www.w3.org/2003/XInclude'">
-]>
-<book id="index">
- <bookinfo>
- <title>CrystFEL Reference Manual</title>
- <releaseinfo>
- For libcrystfel from CrystFEL 0.8.0.
- </releaseinfo>
- <abstract>
- This is the internal documentation for CrystFEL. Unless you are looking at
- the code, writing new programs or fixing bugs, you should not need to read
- this. You might use the information here when reading the code or to better
- understand how the software works, or refer to it when creating a new
- program within the suite.
- </abstract>
- </bookinfo>
-
- <chapter>
- <title>Images</title>
- <xi:include href="xml/image.xml"/>
- </chapter>
-
- <chapter>
- <title>Handling reflection data</title>
- <abstract>There are three main reflection list thingies.</abstract>
- <xi:include href="xml/reflist.xml"/>
- <xi:include href="xml/reflist-utils.xml"/>
- </chapter>
-
- <chapter>
- <title>Unit cells</title>
- <xi:include href="xml/cell.xml"/>
- <xi:include href="xml/cell-utils.xml"/>
- </chapter>
-
- <chapter>
- <title>Crystals</title>
- <xi:include href="xml/crystal.xml"/>
- </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>
-
- <chapter>
- <title>Peaks</title>
- <xi:include href="xml/peaks.xml"/>
- </chapter>
-
- <chapter>
- <title>Image filters</title>
- <xi:include href="xml/filters.xml"/>
- </chapter>
-
- <chapter>
- <title>Statistics and R-factors</title>
- <xi:include href="xml/statistics.xml"><xi:fallback /></xi:include>
- </chapter>
-
- <chapter>
- <title>Symmetry</title>
- <xi:include href="xml/symmetry.xml"><xi:fallback /></xi:include>
- </chapter>
-
- <chapter>
- <title>Integer Matrices</title>
- <xi:include href="xml/integer_matrix.xml"><xi:fallback /></xi:include>
- </chapter>
-
- <chapter>
- <title>Indexing</title>
- <xi:include href="xml/index.xml"><xi:fallback /></xi:include>
- </chapter>
-
- <chapter>
- <title>Prediction refinement</title>
- <xi:include href="xml/predict-refine.xml"><xi:fallback /></xi:include>
- </chapter>
-
- <chapter>
- <title>Integration</title>
- <xi:include href="xml/integration.xml"><xi:fallback /></xi:include>
- </chapter>
-
- <chapter>
- <title>Detector</title>
- <xi:include href="xml/detector.xml"><xi:fallback /></xi:include>
- </chapter>
-
- <chapter>
- <title>HDF5</title>
- <xi:include href="xml/hdf5-file.xml"><xi:fallback /></xi:include>
- </chapter>
-
- <chapter>
- <title>Stream</title>
- <xi:include href="xml/stream.xml"><xi:fallback /></xi:include>
- </chapter>
-
- <chapter>
- <title>Render</title>
- <xi:include href="xml/render.xml"><xi:fallback /></xi:include>
- </chapter>
-
- <chapter>
- <title>Parallel programming</title>
- <xi:include href="xml/thread-pool.xml"><xi:fallback /></xi:include>
- </chapter>
-
- <chapter>
- <title>Miscellaneous</title>
- <xi:include href="xml/histogram.xml"/>
- <xi:include href="xml/utils.xml"/>
- </chapter>
-
- <chapter>
- <title>Information for developers</title>
- <xi:include href="xml/coding-standards.xml"><xi:fallback /></xi:include>
- </chapter>
-
- <index id="api-index-full">
- <title>API Index</title>
- <xi:include href="xml/api-index-full.xml"><xi:fallback /></xi:include>
- </index>
-
-</book>
diff --git a/doc/reference/libcrystfel/CrystFEL-sections.txt b/doc/reference/libcrystfel/CrystFEL-sections.txt
deleted file mode 100644
index 4a436e34..00000000
--- a/doc/reference/libcrystfel/CrystFEL-sections.txt
+++ /dev/null
@@ -1,619 +0,0 @@
-<SECTION>
-<FILE>reflist</FILE>
-RefList
-Reflection
-RefListIterator
-<SUBSECTION>
-reflist_new
-reflist_free
-reflection_new
-reflection_free
-reflist_add_notes
-reflist_get_notes
-<SUBSECTION>
-add_refl
-add_refl_to_list
-<SUBSECTION>
-first_refl
-first_refl_const
-next_refl
-next_refl_const
-<SUBSECTION>
-find_refl
-next_found_refl
-<SUBSECTION>
-get_detector_pos
-get_panel
-get_partiality
-get_lorentz
-get_indices
-get_symmetric_indices
-get_intensity
-get_redundancy
-get_esd_intensity
-get_phase
-get_mean_bg
-get_peak
-get_temp1
-get_temp2
-get_flag
-get_exerr
-get_khalf
-get_kpred
-<SUBSECTION>
-set_detector_pos
-set_panel
-set_partiality
-set_lorentz
-set_intensity
-set_redundancy
-set_esd_intensity
-set_phase
-set_mean_bg
-set_peak
-set_symmetric_indices
-set_temp1
-set_temp2
-set_flag
-set_exerr
-set_khalf
-set_kpred
-<SUBSECTION>
-copy_data
-num_reflections
-tree_depth
-lock_reflection
-unlock_reflection
-GET_H
-GET_K
-GET_L
-SERIAL
-</SECTION>
-
-<SECTION>
-<FILE>reflist-utils</FILE>
-write_reflist
-write_reflist_2
-read_reflections
-read_reflections_2
-asymmetric_indices
-res_cutoff
-check_list_symmetry
-copy_reflist
-find_equiv_in_list
-resolution_limits
-max_intensity
-reflist_add_command_and_version
-</SECTION>
-
-<SECTION>
-<FILE>unitcell</FILE>
-UnitCell
-LatticeType
-UnitCellTransformation
-rvec
-<SUBSECTION>
-cell_new
-cell_new_from_cell
-cell_new_from_direct_axes
-cell_new_from_parameters
-cell_new_from_reciprocal_axes
-cell_free
-<SUBSECTION>
-cell_get_cartesian
-cell_get_parameters
-cell_get_reciprocal
-cell_get_centering
-cell_get_lattice_type
-cell_get_unique_axis
-cell_has_parameters
-<SUBSECTION>
-cell_set_cartesian
-cell_set_parameters
-cell_set_reciprocal
-cell_set_centering
-cell_set_lattice_type
-cell_set_unique_axis
-<SUBSECTION>
-cell_transform
-cell_transform_inverse
-tfn_combine
-tfn_identity
-tfn_from_intmat
-tfn_inverse
-tfn_print
-tfn_vector
-tfn_free
-<SUBSECTION>
-
-cell_rep
-</SECTION>
-
-<SECTION>
-<FILE>cell-utils</FILE>
-cell_rotate
-rotate_cell
-cell_print
-resolution
-match_cell
-compare_cells
-match_cell_ab
-cell_is_sensible
-validate_cell
-uncenter_cell
-bravais_lattice
-right_handed
-str_lattice
-forbidden_reflection
-load_cell_from_pdb
-load_cell_from_file
-write_cell
-lattice_from_str
-cell_get_volume
-transform_cell_gsl
-</SECTION>
-
-<SECTION>
-<FILE>utils</FILE>
-show_matrix_eqn
-solve_svd
-AssplodeFlag
-C_VACUO
-ELECTRON_CHARGE
-PLANCK
-THOMSON_LENGTH
-assplode
-chomp
-gaussian_noise
-progress_bar
-poisson_noise
-notrail
-random_flat
-flat_noise
-show_matrix
-STATUS
-ERROR
-J_to_eV
-eV_to_J
-biggest
-smallest
-check_prefix
-safe_basename
-deg2rad
-rad2deg
-is_odd
-ph_en_to_lambda
-ph_lambda_to_en
-ph_eV_to_lambda
-ph_lambda_to_eV
-likely
-unlikely
-UNUSED
-</SECTION>
-
-<SECTION>
-<FILE>quaternion</FILE>
-quaternion
-<SUBSECTION>
-quaternion_modulus
-normalise_quaternion
-random_quaternion
-quaternion_valid
-quat_rot
-</SECTION>
-
-<SECTION>
-<FILE>histogram</FILE>
-Histogram
-histogram_init
-histogram_free
-histogram_add_value
-histogram_show
-histogram_get_data
-histogram_get_max
-histogram_get_min
-histogram_get_num_bins
-histogram_set_max
-histogram_set_min
-histogram_set_num_bins
-</SECTION>
-
-<SECTION>
-<FILE>image</FILE>
-image
-beam_params
-imagefeature
-ImageFeatureList
-SpectrumType
-sample
-<SUBSECTION>
-imagefile
-imagefile_close
-imagefile_copy_fields
-imagefile_field_list
-imagefile_get_hdfile
-imagefile_get_type
-imagefile_open
-imagefile_read
-imagefile_read_simple
-is_cbf_file
-<SUBSECTION>
-new_imagefile_field_list
-add_imagefile_field
-free_imagefile_field_list
-<SUBSECTION>
-image_add_feature
-image_feature_closest
-image_reflection_closest
-image_feature_count
-image_feature_list_free
-image_feature_list_new
-image_get_feature
-image_add_crystal
-image_remove_feature
-free_all_crystals
-remove_flagged_crystals
-</SECTION>
-
-<SECTION>
-<FILE>thread-pool</FILE>
-TPGetTaskFunc
-TPWorkFunc
-TPFinalFunc
-<SUBSECTION>
-run_threads
-stderr_lock
-get_status_label
-</SECTION>
-
-<SECTION>
-<FILE>statistics</FILE>
-stat_pearson_f_ignore
-stat_pearson_f_zero
-stat_pearson_i
-stat_r1_i
-stat_r1_ignore
-stat_r1_zero
-stat_r2
-stat_rdiff_ignore
-stat_rdiff_intensity
-stat_rdiff_zero
-stat_scale_intensity
-</SECTION>
-
-<SECTION>
-<FILE>indexing</FILE>
-IndexingMethod
-IndexingPrivate
-IndexingFlags
-INDEXING_DEFAULTS_DIRAX
-INDEXING_DEFAULTS_MOSFLM
-INDEXING_DEFAULTS_XDS
-INDEXING_DEFAULTS_ASDF
-INDEXING_DEFAULTS_FELIX
-INDEXING_DEFAULTS_TAKETWO
-INDEXING_METHOD_MASK
-detect_indexing_methods
-setup_indexing
-cleanup_indexing
-get_indm_from_string
-get_indm_from_string_2
-index_pattern
-index_pattern_2
-indexer_str
-dirax_probe
-dirax_prepare
-run_dirax
-dirax_cleanup
-mosflm_probe
-mosflm_prepare
-run_mosflm
-mosflm_cleanup
-xds_probe
-xds_prepare
-run_xds
-xds_cleanup
-asdf_probe
-asdf_prepare
-run_asdf
-asdf_cleanup
-felix_options
-felix_probe
-felix_prepare
-felix_index
-felix_cleanup
-taketwo_options
-taketwo_probe
-taketwo_prepare
-taketwo_index
-taketwo_cleanup
-</SECTION>
-
-<SECTION>
-<FILE>symmetry</FILE>
-SymOpList
-SymOpMask
-<SUBSECTION>
-free_symoplist
-num_equivs
-get_pointgroup
-parse_symmetry_operations
-add_symop
-get_equiv
-get_symop
-special_position
-get_asymm
-get_ambiguities
-is_subgroup
-<SUBSECTION>
-new_symopmask
-free_symopmask
-<SUBSECTION>
-describe_symmetry
-symmetry_name
-set_symmetry_name
-get_matrix_name
-pointgroup_warning
-is_centrosymmetric
-is_centric
-</SECTION>
-
-<SECTION>
-<FILE>integer_matrix</FILE>
-IntegerMatrix
-<SUBSECTION>
-intmat_new
-intmat_copy
-intmat_identity
-intmat_free
-<SUBSECTION>
-intmat_get
-intmat_set
-<SUBSECTION>
-intmat_intmat_mult
-intmat_intvec_mult
-intmat_det
-intmat_inverse
-<SUBSECTION>
-intmat_equals
-intmat_is_identity
-intmat_is_inversion
-<SUBSECTION>
-intmat_print
-</SECTION>
-
-<SECTION>
-<FILE>detector</FILE>
-detector
-panel
-badregion
-rigid_group
-rg_collection
-<SUBSECTION>
-copy_geom
-fill_in_adu
-free_detector_geometry
-get_detector_geometry
-get_detector_geometry_2
-write_detector_geometry
-write_detector_geometry_2
-panel_number
-find_panel_by_name
-simple_geometry
-record_image
-get_pixel_extents
-get_q_for_panel
-get_tt
-smallest_q
-reverse_2d_mapping
-largest_q
-in_bad_region
-mark_resolution_range_as_bad
-find_orig_panel
-find_orig_panel_number
-panel_is_in_rigid_group
-rigid_group_is_in_collection
-single_panel_data_source
-find_rigid_group_collection_by_name
-detector_has_clen_references
-adjust_centering_for_rail
-</SECTION>
-
-<SECTION>
-<FILE>events</FILE>
-event
-event_list
-dim_structure
-filename_plus_event
-<SUBSECTION>
-initialize_event
-push_path_entry_to_event
-pop_path_entry_from_event
-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
-retrieve_full_path
-initialize_filename_plus_event
-free_filename_plus_event
-initialize_event_list
-append_event_to_event_list
-add_non_existing_event_to_event_list
-copy_event_list
-free_event_list
-initialize_dim_structure
-default_dim_structure
-set_dim_structure_entry
-free_dim_structure_entry
-free_dim_structure
-</SECTION>
-
-<SECTION>
-<FILE>crystal</FILE>
-</SECTION>
-
-<SECTION>
-<FILE>hdf5-file</FILE>
-hdf5_read
-hdf5_read2
-hdf5_write
-hdf5_write_image
-hdfile
-hdfile_close
-hdfile_get_image_binned
-hdfile_get_string_value
-hdfile_open
-hdfile_read_group
-hdfile_set_first_image
-hdfile_set_image
-hdfile_get_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_2
-get_peaks_cxi
-get_peaks_cxi_2
-hdfile_is_scalar
-check_path_existence
-fill_event_list
-</SECTION>
-
-<SECTION>
-<FILE>crystal</FILE>
-Crystal
-crystal_new
-crystal_copy
-crystal_free
-<SUBSECTION>
-crystal_get_cell
-crystal_get_cell_const
-crystal_get_image
-crystal_get_mosaicity
-crystal_get_num_saturated_reflections
-crystal_get_osf
-crystal_get_Bfac
-crystal_get_profile_radius
-crystal_get_reflections
-crystal_get_resolution_limit
-crystal_get_user_flag
-crystal_get_num_implausible_reflections
-crystal_get_notes
-crystal_get_det_shift
-crystal_set_cell
-crystal_set_image
-crystal_set_mosaicity
-crystal_set_num_saturated_reflections
-crystal_set_osf
-crystal_set_Bfac
-crystal_set_profile_radius
-crystal_set_reflections
-crystal_set_resolution_limit
-crystal_set_user_flag
-crystal_set_num_implausible_reflections
-crystal_set_notes
-crystal_add_notes
-crystal_set_det_shift
-</SECTION>
-
-<SECTION>
-<FILE>geometry</FILE>
-PartialityModel
-gparam
-predict_to_res
-update_predictions
-calculate_partialities
-polarisation_correction
-sphere_fraction
-gaussian_fraction
-r_gradient
-x_gradient
-y_gradient
-</SECTION>
-
-<SECTION>
-<FILE>peaks</FILE>
-peak_sanity_check
-search_peaks
-search_peaks_peakfinder8
-make_BgMask
-peakfinder8
-validate_peaks
-sort_peaks
-</SECTION>
-
-<SECTION>
-<FILE>render</FILE>
-render_scale
-</SECTION>
-
-<SECTION>
-<FILE>filter</FILE>
-filter_cm
-filter_noise
-filter_median
-</SECTION>
-
-<SECTION>
-<FILE>integration</FILE>
-INTEGRATION_DEFAULTS_PROF2D
-INTEGRATION_DEFAULTS_RINGS
-INTEGRATION_METHOD_MASK
-IntegrationMethod
-IntDiag
-integrate_all
-integrate_all_2
-integrate_all_3
-integrate_all_4
-integrate_all_5
-integration_method
-</SECTION>
-
-<SECTION>
-<FILE>stream</FILE>
-Stream
-StreamReadFlags
-CHUNK_START_MARKER
-CHUNK_END_MARKER
-CRYSTAL_START_MARKER
-CRYSTAL_END_MARKER
-PEAK_LIST_START_MARKER
-PEAK_LIST_END_MARKER
-REFLECTION_START_MARKER
-REFLECTION_END_MARKER
-GEOM_START_MARKER
-GEOM_END_MARKER
-CELL_START_MARKER
-CELL_END_MARKER
-open_stream_fd_for_write
-open_stream_for_read
-open_stream_for_write
-open_stream_for_write_2
-open_stream_for_write_3
-open_stream_for_write_4
-get_stream_fd
-close_stream
-read_chunk
-read_chunk_2
-write_chunk
-write_chunk_2
-rewind_stream
-is_stream
-write_command
-write_geometry_file
-stuff_from_stream
-stream_audit_info
-stream_has_old_indexers
-</SECTION>
-
-<SECTION>
-<FILE>predict-refine</FILE>
-refine_prediction
-refine_radius
-</SECTION>
diff --git a/doc/reference/libcrystfel/build-docs b/doc/reference/libcrystfel/build-docs
deleted file mode 100755
index adb2137a..00000000
--- a/doc/reference/libcrystfel/build-docs
+++ /dev/null
@@ -1,14 +0,0 @@
-#!/bin/sh
-
-set -x
-DOC_MODULE=CrystFEL
-
-mkdir docs
-cd docs
-gtkdoc-scan --module=${DOC_MODULE} ../../libcrystfel/src/*.h
-gtkdoc-mkdb --module=${DOC_MODULE} --output-format=xml --source-dir=../../libcrystfel/src
-mkdir html
-cd html
-gtkdoc-mkhtml ${DOC_MODULE} ../CrystFEL-docs.sgml
-cd ..
-gtkdoc-fixxref --module=${DOC_MODULE} --module-dir=html
diff --git a/doc/reference/libcrystfel/xml/coding-standards.xml b/doc/reference/libcrystfel/xml/coding-standards.xml
deleted file mode 100644
index d51ba293..00000000
--- a/doc/reference/libcrystfel/xml/coding-standards.xml
+++ /dev/null
@@ -1,157 +0,0 @@
-<?xml version="1.0"?>
-<!DOCTYPE refentry PUBLIC "-//OASIS//DTD DocBook XML V4.3//EN"
- "http://www.oasis-open.org/docbook/xml/4.3/docbookx.dtd"
-[
- <!ENTITY % local.common.attrib "xmlns:xi CDATA #FIXED 'http://www.w3.org/2003/XInclude'">
-]>
-<refentry id="CrystFEL-coding-standards">
-<refmeta>
-<refentrytitle role="top_of_page" id="CrystFEL-coding-standards.top_of_page">CrystFEL coding standards</refentrytitle>
-<manvolnum>3</manvolnum>
-<refmiscinfo>
- Coding standards
-</refmiscinfo>
-</refmeta>
-<refnamediv></refnamediv>
-
-
-<refsect1 id="CrystFEL-coding-standards.description" role="desc">
-<title role="desc.title">Summary</title>
-<para>
-This page documents the coding conventions used within the CrystFEL source code. Read these to help when reading the code or before making modifications destined to be sent upstream.
-</para>
-</refsect1>
-
-
-<refsect1 id="CrystFEL-coding-standards.license">
-<title>Licensing and copyright</title>
-<para>
-CrystFEL is distributed under the terms of the GNU General Public License version 3 or higher. Contributions are very welcome provided they also use this license.
-</para>
-<para>
-Whenever you edit a source file, don't forget to update the copyright dates at the top. Add your name and email address if they're not there already. Be sure to add your name to the 'AUTHORS' file in the top level folder, as well.
-</para>
-</refsect1>
-
-
-<refsect1 id="CrystFEL-coding-standards.formatting">
-<title>Formatting</title>
-<para>
-<emphasis>Indentation</emphasis> is done with <emphasis>tabs</emphasis> and
-<emphasis>alignment</emphasis> is done with spaces. This way, the code looks
-neat whatever width you configure your editor to display tabs as. This means,
-for example:
-</para>
-<para>
-<programlisting>
-struct something
-{
- int thing; /* &lt;--- spaces used to align comments */
- int thing_with_longer_name; /* &lt;--- spaces used to align comments */
-}
-
-void somefunction(int something)
-{
- /* &lt;--- Tab character used at the start of this line */
-}
-</programlisting>
-</para>
-<para>
-However, code must be <emphasis>strictly</emphasis> wrapped at 80 columns, or
-what would be 80 columns if the tabs were displayed as 8 spaces.
-If you think you need more width, you're at too many levels of indentation and
-need to break things down a bit. There are no exceptions whatsoever.
-</para>
-<para>
-When performing a two or three dimensional iteration, for example over image
-coordinates or Miller indices, it is acceptable to indent as follows:
-</para>
-<para>
-<programlisting>
-for ( h=-10; h&lt;+10; h++ ) {
-for ( k=-10; k&lt;+10; k++ ) {
-for ( l=-10; l&lt;+10; l++ ) {
-
- /* Do stuff */
-
-}
-}
-}
-</programlisting>
-</para>
-</refsect1>
-
-
-<refsect1 id="CrystFEL-coding-standards.brackets">
-<title>Brackets and so on</title>
-<para>
-Brackets and so on should go like this:
-</para>
-<para>
-<programlisting>
-/* Multiple line comments have stars
- * down one side */
-void somefunction(int someparam)
-{
- /* Single line comments use this style (not //) */
- if ( a &lt; b ) {
- /* 'if' statements usually have the opening brace on the same
- * line as the condition. */
- } else {
- /* 'else's are 'cuddled' */
- }
-
- if ( some &amp;&amp; very &amp;&amp; long &amp;&amp; condition &amp;&amp; that &amp;&amp; spans
- &amp;&amp; two &amp;&amp; lines )
- {
- /* Opening brace is on a line by itself in the case of a very
- * long condition */
- }
-}
-
-/* Comments use proper capitalisation to make things look neat */
-</programlisting>
-</para>
-<para>'struct' blocks can have the braces like functions or 'if' statements. Usually the former looks nicer if the struct is large.</para>
-<para>Parentheses should have spaces after them in 'if' statements, but not in function calls. Function arguments should have spaces after the comma. There should be no space between the function name and the opening bracket. That means:</para>
-<para><programlisting>
-if ( something ) {
- do_something(a, b, c);
-}
-</programlisting></para>
-<para>instead of:</para>
-<para><programlisting>
-if (something) {
- do_something (a,b,c);
-}
-</programlisting></para>
-</refsect1>
-
-
-<refsect1 id="CrystFEL-coding-standards.cleverness">
-<title>Cleverness</title>
-<para>
-Yes, we all know you can insert a new node into an RB-tree while simultaneously
-calculating Pi to 150 decimal places in one line of code. You don't need to
-prove it here. As a general rule, if you think you're about to do something clever, <emphasis>don't do it at all</emphasis>.
-</para>
-</refsect1>
-
-
-<refsect1 id="CrystFEL-coding-standards.commitmessages">
-<title>VCS commit messages</title>
-<para>The first line of your commit message should include a one line summary of the changes, in the form "Do XYZ". That is, not "Did XYZ".</para>
-<para>Make the minimum possible changes in each commit. Try to really distill your changes down to the bare bones, and keep 'cleaning up' in separate commits. Remember that Git thinks about 'changes' rather than 'versions'.</para>
-</refsect1>
-
-
-<refsect1 id="CrystFEL-coding-standards.evilness">
-<title>Evil dictator</title>
-<para>
-Despite your following all of the above, I will probably still touch up your
-code in some places while (or shortly after) integrating it into mainline
-CrystFEL. Please try not to take it personally.
-</para>
-</refsect1>
-
-</refentry>
diff --git a/doc/reference/libcrystfel/xml/gtkdocentities.ent.cmake.in b/doc/reference/libcrystfel/xml/gtkdocentities.ent.cmake.in
deleted file mode 100644
index 3fd9b080..00000000
--- a/doc/reference/libcrystfel/xml/gtkdocentities.ent.cmake.in
+++ /dev/null
@@ -1,8 +0,0 @@
-<!ENTITY package "libcrystfel">
-<!ENTITY package_bugreport "taw@physics.org">
-<!ENTITY package_name "libcrystfel">
-<!ENTITY package_string "crystfel ${PROJECT_VERSION}+${GIT_SHA1}">
-<!ENTITY package_tarname "libcrystfel">
-<!ENTITY package_url "https://www.desy.de/~twhite/crystfel">
-<!ENTITY package_version "${PROJECT_VERSION}+${GIT_SHA1}">
-
diff --git a/libcrystfel/CMakeLists.txt b/libcrystfel/CMakeLists.txt
index 247b925e..67a20ee5 100644
--- a/libcrystfel/CMakeLists.txt
+++ b/libcrystfel/CMakeLists.txt
@@ -1,19 +1,20 @@
project(libcrystfel VERSION ${CRYSTFEL_SHORT_VERSION} LANGUAGES C)
find_package(Curses)
-find_package(CBF)
find_package(XGANDALF)
find_package(PINKINDEXER)
find_package(NBP)
find_package(FDIP)
find_package(ZLIB REQUIRED)
+find_package(FLEX REQUIRED)
+find_package(BISON REQUIRED)
+find_package(Doxygen)
pkg_search_module(FFTW fftw3)
set(HAVE_CURSES ${CURSES_FOUND})
set(HAVE_FFTW ${FFTW_FOUND})
set(HAVE_XGANDALF ${XGANDALF_FOUND})
set(HAVE_FDIP ${FDIP_FOUND})
-set(HAVE_CBFLIB ${CBF_FOUND})
# Find out where forkpty() is declared
set(CMAKE_REQUIRED_LIBRARIES "-lutil")
@@ -30,6 +31,12 @@ endif()
configure_file(config.h.cmake.in config.h)
+bison_target(symopp src/symop.y ${CMAKE_CURRENT_BINARY_DIR}/symop-parse.c COMPILE_FLAGS --report=all)
+flex_target(symopl src/symop.l ${CMAKE_CURRENT_BINARY_DIR}/symop-lex.c
+ DEFINES_FILE ${CMAKE_CURRENT_BINARY_DIR}/symop-lex.h)
+add_flex_bison_dependency(symopl symopp)
+include_directories(${PROJECT_SOURCE_DIR}/src)
+
set(LIBCRYSTFEL_SOURCES
src/reflist.c
src/utils.c
@@ -40,7 +47,6 @@ set(LIBCRYSTFEL_SOURCES
src/hdf5-file.c
src/geometry.c
src/peakfinder8.c
- src/statistics.c
src/symmetry.c
src/stream.c
src/peaks.c
@@ -56,12 +62,15 @@ set(LIBCRYSTFEL_SOURCES
src/xds.c
src/integration.c
src/predict-refine.c
- src/histogram.c
src/events.c
src/felix.c
src/peakfinder8.c
src/taketwo.c
src/xgandalf.c
+ src/rational.c
+ src/spectrum.c
+ ${BISON_symopp_OUTPUTS}
+ ${FLEX_symopl_OUTPUTS}
)
if (HAVE_FFTW)
@@ -75,7 +84,6 @@ set(LIBCRYSTFEL_HEADERS
src/cell.h
src/reflist-utils.h
src/thread-pool.h
- src/statistics.h
src/utils.h
src/detector.h
src/geometry.h
@@ -94,15 +102,26 @@ set(LIBCRYSTFEL_HEADERS
src/xds.h
src/predict-refine.h
src/integration.h
- src/histogram.h
src/events.h
src/asdf.h
src/felix.h
src/peakfinder8.h
src/taketwo.h
src/xgandalf.h
+ src/rational.h
+ src/spectrum.h
)
+if (DOXYGEN_FOUND)
+ configure_file(${PROJECT_SOURCE_DIR}/doc/index.md index.md)
+ set(DOXYGEN_SHOW_INCLUDE_FILES NO)
+ set(DOXYGEN_WARN_IF_UNDOCUMENTED NO)
+ set(DOXYGEN_PREDEFINED HAVE_FFTW)
+ doxygen_add_docs(api-docs ${PROJECT_SOURCE_DIR}/src
+ ${CMAKE_CURRENT_BINARY_DIR}/index.md
+ ${PROJECT_SOURCE_DIR}/doc/coding.md)
+endif (DOXYGEN_FOUND)
+
add_library(${PROJECT_NAME} SHARED
${LIBCRYSTFEL_SOURCES}
${LIBCRYSTFEL_FFTW_SOURCES}
@@ -149,11 +168,6 @@ if (FFTW_FOUND)
target_link_libraries(${PROJECT_NAME} PRIVATE ${FFTW_LDFLAGS})
endif (FFTW_FOUND)
-if (CBF_FOUND)
- target_include_directories(${PROJECT_NAME} PRIVATE ${CBF_INCLUDES})
- target_link_libraries(${PROJECT_NAME} PRIVATE ${CBF_LIBRARIES})
-endif (CBF_FOUND)
-
if (CURSES_FOUND)
target_include_directories(${PROJECT_NAME} PRIVATE ${CURSES_INCLUDE_DIRS})
target_link_libraries(${PROJECT_NAME} PRIVATE ${CURSES_LIBRARIES})
diff --git a/libcrystfel/config.h.cmake.in b/libcrystfel/config.h.cmake.in
index 61f458f8..ea26c1a2 100644
--- a/libcrystfel/config.h.cmake.in
+++ b/libcrystfel/config.h.cmake.in
@@ -7,14 +7,6 @@
#cmakedefine HAVE_FDIP
#cmakedefine HAVE_CURSES
-/* We avoid adding the full path to cbf.h, because CBFlib unhelpfully installs
- * some conflicting HDF5 headers which we want to keep out of the include path.
- * Unfortunately, sometimes CBFlib installs cbf/cbf.h, other times cbflib/cbf.h.
- * These defines tell whether we have CBFlib at all, and if so, what to #include */
-#cmakedefine HAVE_CBFLIB
-#cmakedefine HAVE_CBF_CBF_H
-#cmakedefine HAVE_CBFLIB_CBF_H
-
#cmakedefine HAVE_FORKPTY_PTY_H
#cmakedefine HAVE_FORKPTY_UTIL_H
diff --git a/libcrystfel/doc/coding.md b/libcrystfel/doc/coding.md
new file mode 100644
index 00000000..6e84e4fe
--- /dev/null
+++ b/libcrystfel/doc/coding.md
@@ -0,0 +1,221 @@
+\page coding CrystFEL coding standards
+
+### Licensing
+
+CrystFEL is distributed under the terms of the GNU General Public License
+version 3 or higher. Contributions are very welcome provided they also use this
+license. If your code is not already licensed compatibly, or if the license if
+not clear, we will ask you to re-license it.
+
+Whenever you edit a source file, don't forget to update the copyright dates at
+the top. Add your name and email address if they're not there already. Be sure
+to add your name to the 'AUTHORS' file in the top level folder, as well.
+
+### Scope
+
+The remainder of these rules apply to C code in libcrystfel and the core
+CrystFEL programs. There are currently no specific guidelines for Python,
+Perl, shell script, CMake files or other parts of the codebase.
+
+### Indentation
+
+*Indentation* is done with *tabs* and *alignment* is done with spaces.
+For example:
+
+ int function(int a, int b)
+ {
+ <-tab-->int p; /* <--- Tab character used to indent code inside function */
+ char *str;
+
+ <-tab-->do_something(a, "A long string which takes up a lot of space",
+ <-tab-->.............str, &p); /* <--- spaces used to align with bracket */
+ }
+
+**Rationale:** Using tab characters makes it easy to align code correctly,
+because you can't slip out of alignment by one character. It also makes the
+code look neat whatever width you configure your editor to display tabs as.
+
+### Wrap width
+
+Code should fit into 80 columns (counting tab characters as 8 columns) wherever
+possible, with exceptions only in cases where not doing so would cause line
+breaks at ugly positions, such as straight after an *opening* bracket. The
+absolute maximum allowable line length is 120 columns, with no exceptions
+whatsoever.
+
+For example, this is preferred because it fits into 80 columns (just!):
+
+ very_long_variable_name = very_long_function_name(even_longer_long_variablename,
+ var2, var3);
+
+However, if the variable names are even longer, then the following is preferred,
+even though it goes over 80 columns:
+
+ very_very_long_variable_name = very_long_function_name(even_longer_long_variablename,
+ var2, var3);
+
+This is preferred over the following type of thing, which is just plain ugly:
+
+ very_very_long_variable_name = very_long_function_name(
+ even_longer_long_variablename,
+ var2, var3);
+
+However, see the point below regarding variable names.
+
+**Rationale:** Aside from ensuring it's always possible to display two parts of
+the code side-by-side with a reasonable font size, this is not really about how
+the code looks. Rather, it is to discourage excessive levels of nesting and
+encourage smaller, more easily understood functions. I don't think I've yet
+seen any examples of code where the intelligibility could not be improved while
+simultaneously reducing the number of levels of indentation.
+
+### Variable, function and type names
+
+Shorter variable and function names, with explanatory comments, are preferred
+over long variable names:
+
+ double wavelength_in_angstrom_units; /* <--- not preferred */
+
+Preferred:
+
+ /* The wavelength in Angstrom units */
+ double wl;
+
+***Rationale:*** Shorter variable names make it easier to see the overall
+logical or mathematical structure.
+
+"Snake case" is preferred over "camel case":
+
+ int model_option; /* <--- Preferred */
+ int modelOption; /* <--- Discouraged */
+
+Capitalisation is used for complex type names:
+
+ UnitCell *cell;
+
+### Nested loops
+
+When performing a two or three dimensional iteration which could be considered
+as one larger iteration, for example over image coordinates or Miller indices,
+it is acceptable to indent as follows:
+
+ for ( h=-10; h<+10; h++ ) {
+ for ( k=-10; k<+10; k++ ) {
+ for ( l=-10; l<+10; l++ ) {
+
+ /* Do stuff */
+
+ }
+ }
+ }
+
+In this case, there must be no lines at all, not even blank ones, between each
+of the "for" statements and also between each of the final closing braces.
+
+***Rationale:*** Large multi-dimensional loops are common in scientific code,
+with more than three levels not at all uncommon. Any reasonable limit on code
+width would be overshot by indenting each level separately.
+
+### Comments
+
+C-style comments are preferred over C++-style, even for one-line comments:
+
+ /* Preferred type of comment */
+
+ // Discouraged type of comment
+
+***Rationale:*** CrystFEL is written in C, not C++.
+
+Multi-line comments should have stars down one side:
+
+ /* This is a multiple-line comment.
+ * Here is the second line of the comment */
+
+It's also acceptable to put the closing slash on its own line:
+
+ /* Here is another multiple-line comment.
+ * Here is the second line of the comment
+ */
+
+### Bracket positions
+
+The opening brace of a function should be on its own line:
+
+ void somefunction(int someparam)
+ {
+ /* Here is the body of the function */
+ }
+
+The opening brace of an if statement should be on the same line as the 'if',
+unless the condition is very long, especially if it spans multiple lines:
+
+ if ( a < b ) {
+ do_something(a);
+ } else {
+ do_other_something(a);
+ }
+
+ if ( a_very_long_condition == structure->wibble.value
+ && other_very_long_condition )
+ {
+ do_something_completely_different(someparam);
+ }
+
+***Rationale:*** This keeps the appearance of 'if' statements compact instead of
+spread out, while allowing some visual separation between long conditions and
+the conditional code.
+
+### Space around parantheses
+
+Parentheses should have spaces after them in 'if' statements, but not in
+function calls. Function arguments should have spaces after the comma. There
+should be no space between the function name and the opening bracket. That
+means:
+
+ if ( something < 3 ) {
+ do_something(a, b, c);
+ }
+
+or:
+
+ if ( h>3 && k<3 && l==-1 ) {
+ do_something(h, k, l);
+ }
+
+instead of:
+
+ if (something<3) {
+ do_something (a,b,c);
+ }
+
+***Rationale:*** I find this guideline helps to encourage good grouping of
+logical statements. Some flexibility is allowed here, though.
+
+### Whitespace
+
+No trailing whitespace at all, even in empty lines.
+
+***Rationale:*** Invisible characters do nothing other than generate VCS
+conflicts.
+
+### Cleverness
+
+Transparent, easily understood solutions are preferred over faster ones, except
+where you can demonstrate that the code is performance-critical and the benefit
+is significant. Even in that case, copious comments should be provided.
+
+Use of undefined behaviour, even if "it always works", is absolutely forbidden.
+
+### Git/VCS usage
+
+This style of commit message is preferred:
+
+> Strip out libfrosticle references and add new function model
+
+This style of commit message is discouraged:
+
+> Stripped out libfrosticle references, and added new function model
+
+**Rationale:** this encourages you to think in terms of small, self-contained
+changes to the code, rather than successive versions with many different changes
+combined together. It also matches the conventions used by most other projects.
diff --git a/libcrystfel/doc/index.md b/libcrystfel/doc/index.md
new file mode 100644
index 00000000..a14e62db
--- /dev/null
+++ b/libcrystfel/doc/index.md
@@ -0,0 +1,58 @@
+\mainpage libcrystfel index page
+
+Version
+=======
+This documentation is for libcrystfel from CrystFEL ${CRYSTFEL_VERSION}, API
+revision ${CRYSTFEL_API_VERSION}.
+
+Abstract
+========
+This is the internal documentation for CrystFEL. Unless you are looking at
+the code, writing new programs or fixing bugs, you should not need to read
+this. You might use the information here when reading the code or to better
+understand how the software works, or refer to it when creating a new
+program within the suite.
+
+Coding standards
+================
+Please see the \ref coding "section on coding standards" for CrystFEL's coding
+style rules (including libcrystfel and the core CrystFEL programs).
+
+API documentation
+=================
+
+* \ref image.h "The image structure and image data file handling"
+* Handling reflection data:
+ * \ref reflist.h "Reflection list structure"
+ * \ref reflist-utils.h "Reflection list utility functions"
+* Unit cells:
+ * \ref cell.h "Unit cell structure"
+ * \ref cell-utils.h "Unit cell utility functions"
+* \ref crystal.h "Crystal structure"
+* \ref events.h "Frame descriptors for multi-event files"
+* \ref geometry.h "Geometry of diffraction (prediction/partiality calculations)"
+* Peak search
+ * \ref peaks.h "Main peak search functions"
+ * \ref peakfinder8.h "The peakfinder8 algorithm"
+* \ref filters.h "Image (noise) filters"
+* \ref symmetry.h "Point group symmetry"
+* Mathematical constructions:
+ * \ref integer_matrix.h "Integer matrices"
+ * \ref rational.h "Rational numbers (including rational matrices)"
+* \ref index.h "Top-level indexing system"
+ * \ref xgandalf.h "XGANDALF indexer interface"
+ * \ref xds.h "XDS indexer inderface"
+ * \ref mosflm.h "MOSFLM indexer interface"
+ * \ref dirax.h "DirAx indexer interface"
+ * \ref taketwo.h "TakeTwo indexing algorithm"
+ * \ref asdf.h "ASDF indexing algorithm"
+ * \ref felix.h "Felix indexer interface"
+* \ref predict-refine.h "Prediction refinement"
+* \ref integration.h "Integration of reflections"
+* \ref detector.h "Detector geometry descriptions"
+* \ref spectrum.h "Radiation spectrum object"
+* \ref hdf5-file.h "HDF5 file interface"
+* \ref stream.h "Stream format for indexing/integration results"
+* \ref render.h "Miscellaneous rendering functions (colour scale)"
+* \ref thread-pool.h "Thread pool"
+* \ref utils.h "Miscellaneous utility functions"
diff --git a/libcrystfel/src/asdf.c b/libcrystfel/src/asdf.c
index 9dc5f9d5..72b110d7 100644
--- a/libcrystfel/src/asdf.c
+++ b/libcrystfel/src/asdf.c
@@ -51,6 +51,9 @@
#include "cell-utils.h"
#include "asdf.h"
+/**
+ * \file asdf.h
+ */
struct fftw_vars {
int N;
@@ -1162,6 +1165,9 @@ int run_asdf(struct image *image, void *ipriv)
}
+/**
+ * Prepare the ASDF indexing algorithm
+ */
void *asdf_prepare(IndexingMethod *indm, UnitCell *cell)
{
struct asdf_private *dp;
diff --git a/libcrystfel/src/asdf.h b/libcrystfel/src/asdf.h
index 896f65a8..59dc6a86 100644
--- a/libcrystfel/src/asdf.h
+++ b/libcrystfel/src/asdf.h
@@ -41,6 +41,11 @@
extern "C" {
#endif
+/**
+ * \file asdf.h
+ * The ASDF indexing algorithm.
+ */
+
#ifdef HAVE_FFTW
extern int run_asdf(struct image *image, void *ipriv);
diff --git a/libcrystfel/src/cell-utils.c b/libcrystfel/src/cell-utils.c
index 7b1984bb..1609d816 100644
--- a/libcrystfel/src/cell-utils.c
+++ b/libcrystfel/src/cell-utils.c
@@ -3,13 +3,13 @@
*
* Unit Cell utility functions
*
- * Copyright © 2012-2017 Deutsches Elektronen-Synchrotron DESY,
+ * Copyright © 2012-2019 Deutsches Elektronen-Synchrotron DESY,
* a research centre of the Helmholtz Association.
* Copyright © 2012 Lorenzo Galli
*
* Authors:
- * 2009-2012,2014-2017 Thomas White <taw@physics.org>
- * 2012 Lorenzo Galli
+ * 2009-2019 Thomas White <taw@physics.org>
+ * 2012 Lorenzo Galli
*
* This file is part of CrystFEL.
*
@@ -47,15 +47,7 @@
/**
- * SECTION:cell-utils
- * @short_description: Unit cell utilities
- * @title: Unit cell utilities
- * @section_id:
- * @see_also:
- * @include: "cell-utils.h"
- * @Image:
- *
- * There are some utility functions associated with the core %UnitCell.
+ * \file cell-utils.h
**/
@@ -64,13 +56,12 @@
/**
- * cell_rotate:
- * @in: A %UnitCell to rotate
- * @quat: A %quaternion
+ * \param in: A UnitCell to rotate
+ * \param quat: A quaternion
*
- * Rotate a %UnitCell using a %quaternion.
+ * Rotate a UnitCell using a quaternion.
*
- * Returns: a newly allocated rotated copy of @in.
+ * \returns a newly allocated rotated copy of \p in.
*
*/
UnitCell *cell_rotate(UnitCell *in, struct quaternion quat)
@@ -219,11 +210,6 @@ int right_handed(UnitCell *cell)
void cell_print(UnitCell *cell)
{
- double asx, asy, asz;
- double bsx, bsy, bsz;
- double csx, csy, csz;
- double a, b, c, alpha, beta, gamma;
- double ax, ay, az, bx, by, bz, cx, cy, cz;
LatticeType lt;
char cen;
@@ -251,12 +237,31 @@ void cell_print(UnitCell *cell)
}
if ( cell_has_parameters(cell) ) {
+
+ double a, b, c, alpha, beta, gamma;
cell_get_parameters(cell, &a, &b, &c, &alpha, &beta, &gamma);
STATUS("a b c alpha beta gamma\n");
STATUS("%6.2f %6.2f %6.2f A %6.2f %6.2f %6.2f deg\n",
a*1e10, b*1e10, c*1e10,
rad2deg(alpha), rad2deg(beta), rad2deg(gamma));
+ } else {
+ STATUS("Unit cell parameters are not specified.\n");
+ }
+}
+
+
+void cell_print_full(UnitCell *cell)
+{
+
+ cell_print(cell);
+
+ if ( cell_has_parameters(cell) ) {
+
+ double asx, asy, asz;
+ double bsx, bsy, bsz;
+ double csx, csy, csz;
+ double ax, ay, az, bx, by, bz, cx, cy, cz;
cell_get_cartesian(cell, &ax, &ay, &az, &bx, &by, &bz, &cx, &cy, &cz);
@@ -283,8 +288,6 @@ void cell_print(UnitCell *cell)
STATUS("Cell representation is %s.\n", cell_rep(cell));
- } else {
- STATUS("Unit cell parameters are not specified.\n");
}
}
@@ -349,203 +352,217 @@ int bravais_lattice(UnitCell *cell)
}
-static UnitCellTransformation *uncentering_transformation(UnitCell *in,
- char *new_centering,
- LatticeType *new_latt)
+static RationalMatrix *create_rtnl_mtx(signed int a1, signed int a2,
+ signed int b1, signed int b2,
+ signed int c1, signed int c2,
+ signed int d1, signed int d2,
+ signed int e1, signed int e2,
+ signed int f1, signed int f2,
+ signed int g1, signed int g2,
+ signed int h1, signed int h2,
+ signed int i1, signed int i2)
+{
+ RationalMatrix *m = rtnl_mtx_new(3, 3);
+ if ( m == NULL ) return NULL;
+ rtnl_mtx_set(m, 0, 0, rtnl(a1, a2));
+ rtnl_mtx_set(m, 0, 1, rtnl(b1, b2));
+ rtnl_mtx_set(m, 0, 2, rtnl(c1, c2));
+ rtnl_mtx_set(m, 1, 0, rtnl(d1, d2));
+ rtnl_mtx_set(m, 1, 1, rtnl(e1, e2));
+ rtnl_mtx_set(m, 1, 2, rtnl(f1, f2));
+ rtnl_mtx_set(m, 2, 0, rtnl(g1, g2));
+ rtnl_mtx_set(m, 2, 1, rtnl(h1, h2));
+ rtnl_mtx_set(m, 2, 2, rtnl(i1, i2));
+ return m;
+}
+
+
+/* Given a centered cell "in", return the integer transformation matrix which
+ * turns a primitive cell into "in". Set new_centering and new_latt to the
+ * centering and lattice type of the primitive cell (usually aP, sometimes rR,
+ * rarely mP). Store the inverse matrix at pCi */
+static IntegerMatrix *centering_transformation(UnitCell *in,
+ char *new_centering,
+ LatticeType *new_latt,
+ char *new_ua,
+ RationalMatrix **pCi)
{
- UnitCellTransformation *t;
- const double OT = 1.0/3.0;
- const double TT = 2.0/3.0;
- const double H = 0.5;
LatticeType lt;
char ua, cen;
+ IntegerMatrix *C = NULL;
+ RationalMatrix *Ci = NULL;
lt = cell_get_lattice_type(in);
ua = cell_get_unique_axis(in);
cen = cell_get_centering(in);
- t = tfn_identity();
- if ( t == NULL ) return NULL;
-
- if ( ua == 'a' ) {
- tfn_combine(t, tfn_vector(0,1,0),
- tfn_vector(0,0,1),
- tfn_vector(1,0,0));
- if ( lt == L_MONOCLINIC ) {
- assert(cen != 'A');
- switch ( cen ) {
- case 'B' : cen = 'A'; break;
- case 'C' : cen = 'B'; break;
- case 'I' : cen = 'I'; break;
- }
- }
- }
+ /* Write the matrices exactly as they appear in ITA Table 5.1.3.1.
+ * C is "P", and Ci is "Q=P^-1". Vice-versa if the transformation
+ * should go the opposite way to what's written in the first column. */
- if ( ua == 'b' ) {
- tfn_combine(t, tfn_vector(0,0,1),
- tfn_vector(1,0,0),
- tfn_vector(0,1,0));
- if ( lt == L_MONOCLINIC ) {
- assert(cen != 'B');
- switch ( cen ) {
- case 'C' : cen = 'A'; break;
- case 'A' : cen = 'B'; break;
- case 'I' : cen = 'I'; break;
- }
- }
- }
-
- switch ( cen ) {
-
- case 'P' :
+ if ( (cen=='P') || (cen=='R') ) {
+ *new_centering = cen;
*new_latt = lt;
- *new_centering = 'P';
- break;
-
- case 'R' :
- *new_latt = L_RHOMBOHEDRAL;
- *new_centering = 'R';
- break;
+ *new_ua = ua;
+ C = intmat_identity(3);
+ Ci = rtnl_mtx_identity(3);
+ }
- case 'I' :
- tfn_combine(t, tfn_vector(-H,H,H),
- tfn_vector(H,-H,H),
- tfn_vector(H,H,-H));
+ if ( cen == 'I' ) {
+ C = intmat_create_3x3(0, 1, 1,
+ 1, 0, 1,
+ 1, 1, 0);
+ Ci = create_rtnl_mtx(-1,2, 1,2, 1,2,
+ 1,2, -1,2, 1,2,
+ 1,2, 1,2, -1,2);
if ( lt == L_CUBIC ) {
*new_latt = L_RHOMBOHEDRAL;
*new_centering = 'R';
+ *new_ua = '*';
} else {
- /* Tetragonal or orthorhombic */
*new_latt = L_TRICLINIC;
*new_centering = 'P';
+ *new_ua = '*';
}
- break;
+ }
- case 'F' :
- tfn_combine(t, tfn_vector(0,H,H),
- tfn_vector(H,0,H),
- tfn_vector(H,H,0));
+ if ( cen == 'F' ) {
+ C = intmat_create_3x3(-1, 1, 1,
+ 1, -1, 1,
+ 1, 1, -1);
+ Ci = create_rtnl_mtx( 0,1, 1,2, 1,2,
+ 1,2, 0,1, 1,2,
+ 1,2, 1,2, 0,1);
if ( lt == L_CUBIC ) {
*new_latt = L_RHOMBOHEDRAL;
*new_centering = 'R';
+ *new_ua = '*';
} else {
- assert(lt == L_ORTHORHOMBIC);
*new_latt = L_TRICLINIC;
*new_centering = 'P';
+ *new_ua = '*';
}
- break;
+ }
- case 'A' :
- tfn_combine(t, tfn_vector( 1, 0, 0),
- tfn_vector( 0, H, H),
- tfn_vector( 0,-H, H));
+ if ( (lt == L_HEXAGONAL) && (cen == 'H') && (ua == 'c') ) {
+ /* Obverse setting */
+ C = intmat_create_3x3( 1, 0, 1,
+ -1, 1, 1,
+ 0, -1, 1);
+ Ci = create_rtnl_mtx( 2,3, -1,3, -1,3,
+ 1,3, 1,3, -2,3,
+ 1,3, 1,3, 1,3);
+ assert(lt == L_HEXAGONAL);
+ assert(ua == 'c');
+ *new_latt = L_RHOMBOHEDRAL;
+ *new_centering = 'R';
+ *new_ua = '*';
+ }
+
+ if ( cen == 'A' ) {
+ C = intmat_create_3x3( 1, 0, 0,
+ 0, 1, 1,
+ 0, -1, 1);
+ Ci = create_rtnl_mtx( 1,1, 0,1, 0,1,
+ 0,1, 1,2, -1,2,
+ 0,1, 1,2, 1,2);
if ( lt == L_ORTHORHOMBIC ) {
*new_latt = L_MONOCLINIC;
+ *new_centering = 'P';
+ *new_ua = 'a';
} else {
*new_latt = L_TRICLINIC;
+ *new_centering = 'P';
+ *new_ua = '*';
}
- *new_centering = 'P';
- break;
+ }
- case 'B' :
- tfn_combine(t, tfn_vector( H, 0, H),
- tfn_vector( 0, 1, 0),
- tfn_vector(-H, 0, H));
+ if ( cen == 'B' ) {
+ C = intmat_create_3x3( 1, 0, 1,
+ 0, 1, 0,
+ -1, 0, 1);
+ Ci = create_rtnl_mtx( 1,2, 0,1, -1,2,
+ 0,1, 1,1, 0,1,
+ 1,2, 0,1, 1,2);
if ( lt == L_ORTHORHOMBIC ) {
*new_latt = L_MONOCLINIC;
+ *new_centering = 'P';
+ *new_ua = 'b';
} else {
*new_latt = L_TRICLINIC;
+ *new_centering = 'P';
+ *new_ua = '*';
}
- *new_centering = 'P';
- break;
+ }
- case 'C' :
- tfn_combine(t, tfn_vector( H, H, 0),
- tfn_vector(-H, H, 0),
- tfn_vector( 0, 0, 1));
+ if ( cen == 'C' ) {
+ C = intmat_create_3x3( 1, 1, 0,
+ -1, 1, 0,
+ 0, 0, 1);
+ Ci = create_rtnl_mtx( 1,2, -1,2, 0,1,
+ 1,2, 1,2, 0,1,
+ 0,1, 0,1, 1,1);
if ( lt == L_ORTHORHOMBIC ) {
*new_latt = L_MONOCLINIC;
+ *new_centering = 'P';
+ *new_ua = 'c';
} else {
*new_latt = L_TRICLINIC;
- }
- *new_centering = 'P';
- break;
-
- case 'H' :
- /* Obverse setting */
- tfn_combine(t, tfn_vector(TT,OT,OT),
- tfn_vector(-OT,OT,OT),
- tfn_vector(-OT,-TT,OT));
- assert(lt == L_HEXAGONAL);
- *new_latt = L_RHOMBOHEDRAL;
- *new_centering = 'R';
- break;
-
- default :
- ERROR("Invalid centering '%c'\n", cell_get_centering(in));
- return NULL;
-
- }
-
- /* Reverse the axis permutation, but only if this was not an H->R
- * transformation */
- if ( !((cen=='H') && (*new_latt == L_RHOMBOHEDRAL)) ) {
- if ( ua == 'a' ) {
- tfn_combine(t, tfn_vector(0,0,1),
- tfn_vector(1,0,0),
- tfn_vector(0,1,0));
- }
-
- if ( ua == 'b' ) {
- tfn_combine(t, tfn_vector(0,1,0),
- tfn_vector(0,0,1),
- tfn_vector(1,0,0));
+ *new_centering = 'P';
+ *new_ua = '*';
}
}
- return t;
+ *pCi = Ci;
+ return C;
}
/**
- * uncenter_cell:
- * @in: A %UnitCell
- * @t: Location at which to store the transformation which was used.
+ * \param in: A %UnitCell
+ * \param pC: Location at which to store the centering transformation
+ * \param pCi: Location at which to store the inverse centering transformation
*
- * Turns any cell into a primitive one, e.g. for comparison purposes. The
- * transformation which was used is stored at @t, which can be NULL if the
- * transformation is not required.
+ * Turns any cell into a primitive one, e.g. for comparison purposes.
*
- * Returns: a primitive version of @in in a conventional (unique axis c)
- * setting.
+ * The transformation which was used is stored at \p Ci. The centering
+ * transformation, which is the transformation you should apply if you want to
+ * get back the original cell, will be stored at \p C. Either or both of these
+ * can be NULL if you don't need that information.
+ *
+ * \returns a primitive version of \p in.
*
*/
-UnitCell *uncenter_cell(UnitCell *in, UnitCellTransformation **t)
+UnitCell *uncenter_cell(UnitCell *in, IntegerMatrix **pC, RationalMatrix **pCi)
{
- UnitCellTransformation *tt;
+ IntegerMatrix *C;
+ RationalMatrix *Ci;
char new_centering;
LatticeType new_latt;
+ char new_ua;
UnitCell *out;
- if ( !bravais_lattice(in) ) {
- ERROR("Cannot uncenter: not a Bravais lattice.\n");
- cell_print(in);
- return NULL;
- }
-
- tt = uncentering_transformation(in, &new_centering, &new_latt);
- if ( tt == NULL ) return NULL;
+ C = centering_transformation(in, &new_centering, &new_latt,
+ &new_ua, &Ci);
+ if ( C == NULL ) return NULL;
- out = cell_transform(in, tt);
+ out = cell_transform_rational(in, Ci);
if ( out == NULL ) return NULL;
cell_set_lattice_type(out, new_latt);
cell_set_centering(out, new_centering);
+ cell_set_unique_axis(out, new_ua);
- if ( t != NULL ) {
- *t = tt;
+ if ( pC != NULL ) {
+ *pC = C;
} else {
- tfn_free(tt);
+ intmat_free(C);
+ }
+
+ if ( pCi != NULL ) {
+ *pCi = Ci;
+ } else {
+ rtnl_mtx_free(Ci);
}
return out;
@@ -609,16 +626,16 @@ UnitCell *match_cell(UnitCell *cell_in, UnitCell *template_in, int verbose,
float angtol = deg2rad(tols[3]);
UnitCell *cell;
UnitCell *template;
- UnitCellTransformation *uncentering;
+ IntegerMatrix *centering;
UnitCell *new_cell_trans;
/* "Un-center" the template unit cell to make the comparison easier */
- template = uncenter_cell(template_in, &uncentering);
+ template = uncenter_cell(template_in, &centering, NULL);
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);
+ cell = uncenter_cell(cell_in, NULL, NULL);
if ( cell == NULL ) return NULL;
if ( cell_get_reciprocal(template, &asx, &asy, &asz,
@@ -627,7 +644,7 @@ UnitCell *match_cell(UnitCell *cell_in, UnitCell *template_in, int verbose,
ERROR("Couldn't get reciprocal cell for template.\n");
cell_free(template);
cell_free(cell);
- tfn_free(uncentering);
+ intmat_free(centering);
return NULL;
}
@@ -649,7 +666,7 @@ UnitCell *match_cell(UnitCell *cell_in, UnitCell *template_in, int verbose,
ERROR("Couldn't get reciprocal cell.\n");
cell_free(template);
cell_free(cell);
- tfn_free(uncentering);
+ intmat_free(centering);
return NULL;
}
@@ -811,7 +828,7 @@ UnitCell *match_cell(UnitCell *cell_in, UnitCell *template_in, int verbose,
/* Reverse the de-centering transformation */
if ( new_cell != NULL ) {
- new_cell_trans = cell_transform_inverse(new_cell, uncentering);
+ new_cell_trans = cell_transform_intmat(new_cell, centering);
cell_free(new_cell);
cell_set_lattice_type(new_cell_trans,
cell_get_lattice_type(template_in));
@@ -821,13 +838,13 @@ UnitCell *match_cell(UnitCell *cell_in, UnitCell *template_in, int verbose,
cell_get_unique_axis(template_in));
cell_free(template);
- tfn_free(uncentering);
+ intmat_free(centering);
return new_cell_trans;
} else {
cell_free(template);
- tfn_free(uncentering);
+ intmat_free(centering);
return NULL;
}
}
@@ -850,16 +867,16 @@ UnitCell *match_cell_ab(UnitCell *cell_in, UnitCell *template_in)
int have_real_c;
UnitCell *cell;
UnitCell *template;
- UnitCellTransformation *to_given_cell;
+ IntegerMatrix *to_given_cell;
UnitCell *new_cell;
UnitCell *new_cell_trans;
/* "Un-center" the template unit cell to make the comparison easier */
- template = uncenter_cell(template_in, &to_given_cell);
+ template = uncenter_cell(template_in, &to_given_cell, 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);
+ cell = uncenter_cell(cell_in, NULL, NULL);
/* Get the lengths to match */
if ( cell_get_cartesian(template, &ax, &ay, &az,
@@ -940,7 +957,7 @@ UnitCell *match_cell_ab(UnitCell *cell_in, UnitCell *template_in)
new_cell = cell_new_from_direct_axes(real_a, real_b, real_c);
/* Reverse the de-centering transformation */
- new_cell_trans = cell_transform_inverse(new_cell, to_given_cell);
+ new_cell_trans = cell_transform_intmat_inverse(new_cell, to_given_cell);
cell_free(new_cell);
cell_set_lattice_type(new_cell, cell_get_lattice_type(template_in));
cell_set_centering(new_cell, cell_get_centering(template_in));
@@ -1099,12 +1116,11 @@ static void determine_lattice(UnitCell *cell,
/**
- * load_cell_from_pdb:
- * @filename: The filename from which to load the cell
+ * \param filename: The filename from which to load the cell
*
* Loads a unit cell from the CRYST1 line of a PDB file.
*
- * Returns: a newly allocated %UnitCell.
+ * \returns a newly allocated %UnitCell.
*
*/
UnitCell *load_cell_from_pdb(const char *filename)
@@ -1239,11 +1255,10 @@ static int get_angle_rad(char **bits, int nbits, double *pl)
}
/**
- * write_cell:
- * @cell: a %UnitCell
- * @fh: a file handle
+ * \param cell: a %UnitCell
+ * \param fh: a file handle
*
- * Writes @cell to @fh, in CrystFEL unit cell file format
+ * Writes \p cell to \p fh, in CrystFEL unit cell file format
*
*/
void write_cell(UnitCell *cell, FILE *fh)
@@ -1275,12 +1290,11 @@ void write_cell(UnitCell *cell, FILE *fh)
/**
- * load_cell_from_file:
- * @filename: The filename from which to load the cell
+ * \param filename: The filename from which to load the cell
*
* Loads a unit cell from a file of any type (PDB or CrystFEL format)
*
- * Returns: a newly allocated %UnitCell.
+ * \returns a newly allocated %UnitCell.
*
*/
UnitCell *load_cell_from_file(const char *filename)
@@ -1443,59 +1457,15 @@ void cell_fudge_gslcblas()
}
-UnitCell *transform_cell_gsl(UnitCell *in, gsl_matrix *m)
-{
- gsl_matrix *c;
- double asx, asy, asz;
- double bsx, bsy, bsz;
- double csx, csy, csz;
- gsl_matrix *res;
- UnitCell *out;
-
- cell_get_reciprocal(in, &asx, &asy, &asz, &bsx, &bsy,
- &bsz, &csx, &csy, &csz);
-
- c = gsl_matrix_alloc(3, 3);
- gsl_matrix_set(c, 0, 0, asx);
- gsl_matrix_set(c, 1, 0, asy);
- gsl_matrix_set(c, 2, 0, asz);
- gsl_matrix_set(c, 0, 1, bsx);
- gsl_matrix_set(c, 1, 1, bsy);
- gsl_matrix_set(c, 2, 1, bsz);
- gsl_matrix_set(c, 0, 2, csx);
- gsl_matrix_set(c, 1, 2, csy);
- gsl_matrix_set(c, 2, 2, csz);
-
- res = gsl_matrix_calloc(3, 3);
- gsl_blas_dgemm(CblasNoTrans, CblasNoTrans, 1.0, m, c, 0.0, res);
-
- out = cell_new_from_cell(in);
- cell_set_reciprocal(out, gsl_matrix_get(res, 0, 0),
- gsl_matrix_get(res, 1, 0),
- gsl_matrix_get(res, 2, 0),
- gsl_matrix_get(res, 0, 1),
- gsl_matrix_get(res, 1, 1),
- gsl_matrix_get(res, 2, 1),
- gsl_matrix_get(res, 0, 2),
- gsl_matrix_get(res, 1, 2),
- gsl_matrix_get(res, 2, 2));
-
- gsl_matrix_free(res);
- gsl_matrix_free(c);
- return out;
-}
-
-
/**
- * rotate_cell:
- * @in: A %UnitCell to rotate
- * @omega: Euler angle about +z
- * @phi: Euler angle about +x
- * @rot: Euler angle about new +z
+ * \param in: A %UnitCell to rotate
+ * \param omega: Euler angle about +z
+ * \param phi: Euler angle about +x
+ * \param rot: Euler angle about new +z
*
* Rotate a %UnitCell using Euler angles
*
- * Returns: a newly allocated rotated copy of @in.
+ * \returns a newly allocated rotated copy of \p in.
*
*/
UnitCell *rotate_cell(UnitCell *in, double omega, double phi, double rot)
@@ -1579,14 +1549,15 @@ int cell_is_sensible(UnitCell *cell)
/**
- * validate_cell:
- * @cell: A %UnitCell to validate
+ * \param cell: A %UnitCell to validate
*
- * Perform some checks for crystallographic validity @cell, such as that the
+ * Perform some checks for crystallographic validity \p cell, such as that the
* lattice is a conventional Bravais lattice.
* Warnings are printied if any of the checks are failed.
*
- * Returns: true if cell is invalid.
+ * \returns zero if the cell is fine, 1 if it is unconventional but otherwise
+ * OK (e.g. left-handed or not a Bravais lattice), and 2 if there is a serious
+ * problem such as the parameters being physically impossible.
*
*/
int validate_cell(UnitCell *cell)
@@ -1596,7 +1567,7 @@ int validate_cell(UnitCell *cell)
if ( cell_has_parameters(cell) && !cell_is_sensible(cell) ) {
ERROR("WARNING: Unit cell parameters are not sensible.\n");
- err = 1;
+ err = 2;
}
if ( !bravais_lattice(cell) ) {
@@ -1620,7 +1591,7 @@ int validate_cell(UnitCell *cell)
|| ((cen == 'C') && (ua == 'c')) ) {
ERROR("WARNING: A, B or C centering matches unique"
" axis.\n");
- err = 1;
+ err = 2;
}
}
@@ -1629,13 +1600,12 @@ int validate_cell(UnitCell *cell)
/**
- * forbidden_reflection:
- * @cell: A %UnitCell
- * @h: h index to check
- * @k: k index to check
- * @l: l index to check
+ * \param cell: A %UnitCell
+ * \param h: h index to check
+ * \param k: k index to check
+ * \param l: l index to check
*
- * Returns: true if this reflection is forbidden.
+ * \returns true if this reflection is forbidden.
*
*/
int forbidden_reflection(UnitCell *cell,
@@ -1646,7 +1616,7 @@ int forbidden_reflection(UnitCell *cell,
cen = cell_get_centering(cell);
/* Reflection conditions here must match the transformation matrices
- * in uncentering_transformation(). tests/centering_check verifies
+ * in centering_transformation(). tests/centering_check verifies
* this (amongst other things). */
if ( cen == 'P' ) return 0;
@@ -1666,7 +1636,9 @@ int forbidden_reflection(UnitCell *cell,
}
-/* Returns cell volume in A^3 */
+/**
+ * \returns cell volume in A^3
+ */
double cell_get_volume(UnitCell *cell)
{
double asx, asy, asz;
@@ -1694,6 +1666,47 @@ double cell_get_volume(UnitCell *cell)
}
+/**
+ * \param cell1: A UnitCell
+ * \param cell2: Another UnitCell
+ * \param ltl: Maximum allowable fractional difference in axis lengths
+ * \param atl: Maximum allowable difference in reciprocal angles (in radians)
+ *
+ * Compare the two unit cells. If the real space parameters match to within
+ * fractional difference \p ltl, and the inter-axial angles match within \p atl,
+ * and the centering matches, this function returns 1. Otherwise 0.
+ *
+ * This function considers the cell parameters and centering, but ignores the
+ * orientation of the cell. If you want to compare the orientation as well,
+ * use compare_cell_parameters_and_orientation() instead.
+ *
+ * \returns non-zero if the cells match.
+ *
+ */
+int compare_cell_parameters(UnitCell *cell1, UnitCell *cell2,
+ float ltl, float atl)
+{
+ double a1, b1, c1, al1, be1, ga1;
+ double a2, b2, c2, al2, be2, ga2;
+
+ /* Centering must match: we don't arbitrarte primitive vs centered,
+ * different cell choices etc */
+ if ( cell_get_centering(cell1) != cell_get_centering(cell2) ) return 0;
+
+ cell_get_parameters(cell1, &a1, &b1, &c1, &al1, &be1, &ga1);
+ cell_get_parameters(cell2, &a2, &b2, &c2, &al2, &be2, &ga2);
+
+ if ( !within_tolerance(a1, a2, ltl*100.0) ) return 0;
+ if ( !within_tolerance(b1, b2, ltl*100.0) ) return 0;
+ if ( !within_tolerance(c1, c2, ltl*100.0) ) return 0;
+ if ( fabs(al1-al2) > atl ) return 0;
+ if ( fabs(be1-be2) > atl ) return 0;
+ if ( fabs(ga1-ga2) > atl ) return 0;
+
+ return 1;
+}
+
+
static double moduli_check(double ax, double ay, double az,
double bx, double by, double bz)
{
@@ -1703,28 +1716,41 @@ static double moduli_check(double ax, double ay, double az,
}
-static int cells_are_similar(UnitCell *cell1, UnitCell *cell2,
- const double ltl, const double atl)
+/**
+ * \param cell1: A UnitCell
+ * \param cell2: Another UnitCell
+ * \param ltl: Maximum allowable fractional difference in reciprocal axis lengths
+ * \param atl: Maximum allowable difference in reciprocal angles (in radians)
+ *
+ * Compare the two unit cells. If the axes match in length (to within
+ * fractional difference \p ltl) and the axes are aligned to within \p atl radians,
+ * this function returns non-zero.
+ *
+ * This function compares the orientation of the cell as well as the parameters.
+ * If you just want to see if the parameters are the same, use
+ * compare_cell_parameters() instead.
+ *
+ * The cells \p a and \p b must have the same centering. Otherwise, this function
+ * always returns zero.
+ *
+ * \returns non-zero if the cells match.
+ *
+ */
+int compare_cell_parameters_and_orientation(UnitCell *cell1, UnitCell *cell2,
+ const double ltl, const double atl)
{
double asx1, asy1, asz1, bsx1, bsy1, bsz1, csx1, csy1, csz1;
double asx2, asy2, asz2, bsx2, bsy2, bsz2, csx2, csy2, csz2;
- UnitCell *pcell1, *pcell2;
- /* Compare primitive cells, not centered */
- pcell1 = uncenter_cell(cell1, NULL);
- pcell2 = uncenter_cell(cell2, NULL);
+ if ( cell_get_centering(cell1) != cell_get_centering(cell2) ) return 0;
- cell_get_reciprocal(pcell1, &asx1, &asy1, &asz1,
- &bsx1, &bsy1, &bsz1,
- &csx1, &csy1, &csz1);
+ cell_get_cartesian(cell1, &asx1, &asy1, &asz1,
+ &bsx1, &bsy1, &bsz1,
+ &csx1, &csy1, &csz1);
- cell_get_reciprocal(pcell2, &asx2, &asy2, &asz2,
- &bsx2, &bsy2, &bsz2,
- &csx2, &csy2, &csz2);
-
-
- cell_free(pcell1);
- cell_free(pcell2);
+ cell_get_cartesian(cell2, &asx2, &asy2, &asz2,
+ &bsx2, &bsy2, &bsz2,
+ &csx2, &csy2, &csz2);
if ( angle_between(asx1, asy1, asz1, asx2, asy2, asz2) > atl ) return 0;
if ( angle_between(bsx1, bsy1, bsz1, bsx2, bsy2, bsz2) > atl ) return 0;
@@ -1738,28 +1764,37 @@ static int cells_are_similar(UnitCell *cell1, UnitCell *cell2,
}
-
/**
- * compare_cells:
- * @a: A %UnitCell
- * @b: Another %UnitCell
- * @ltl: Maximum allowable fractional difference in reciprocal axis lengths
- * @atl: Maximum allowable difference in reciprocal angles (in radians)
- * @pmb: Place to store pointer to matrix
+ * \param a: A UnitCell
+ * \param b: Another UnitCell
+ * \param ltl: Maximum allowable fractional difference in reciprocal axis lengths
+ * \param atl: Maximum allowable difference in reciprocal angles (in radians)
+ * \param pmb: Place to store pointer to matrix
*
- * Compare the two units cells. If they agree to within @ltl and @atl, using
- * any change of axes, returns non-zero and stores the transformation to map @b
- * onto @a.
+ * Compare the two unit cells. If, using any permutation of the axes, the
+ * axes can be made to match in length (to within fractional difference \p ltl)
+ * and the axes aligned to within \p atl radians, this function returns non-zero
+ * and stores the transformation to map \p b onto \p a.
*
- * Returns: non-zero if the cells match.
+ * Note that the orientations of the cells must match, not just the parameters.
+ * The comparison is done after reindexing using
+ * compare_cell_parameters_and_orientation().
+ *
+ * The cells \p a and \p b must have the same centering. Otherwise, this function
+ * always returns zero.
+ *
+ * \returns non-zero if the cells match.
*
*/
-int compare_cells(UnitCell *a, UnitCell *b, double ltl, double atl,
- IntegerMatrix **pmb)
+int compare_reindexed_cell_parameters_and_orientation(UnitCell *a, UnitCell *b,
+ double ltl, double atl,
+ IntegerMatrix **pmb)
{
IntegerMatrix *m;
int i[9];
+ if ( cell_get_centering(a) != cell_get_centering(b) ) return 0;
+
m = intmat_new(3, 3);
for ( i[0]=-1; i[0]<=+1; i[0]++ ) {
@@ -1772,7 +1807,6 @@ int compare_cells(UnitCell *a, UnitCell *b, double ltl, double atl,
for ( i[7]=-1; i[7]<=+1; i[7]++ ) {
for ( i[8]=-1; i[8]<=+1; i[8]++ ) {
- UnitCellTransformation *tfn;
UnitCell *nc;
int j, k;
int l = 0;
@@ -1783,17 +1817,14 @@ int compare_cells(UnitCell *a, UnitCell *b, double ltl, double atl,
if ( intmat_det(m) != +1 ) continue;
- tfn = tfn_from_intmat(m);
- nc = cell_transform(b, tfn);
+ nc = cell_transform_intmat(b, m);
- if ( cells_are_similar(a, nc, ltl, atl) ) {
+ if ( compare_cell_parameters_and_orientation(a, nc, ltl, atl) ) {
if ( pmb != NULL ) *pmb = m;
- tfn_free(tfn);
cell_free(nc);
return 1;
}
- tfn_free(tfn);
cell_free(nc);
}
@@ -1809,3 +1840,291 @@ int compare_cells(UnitCell *a, UnitCell *b, double ltl, double atl,
intmat_free(m);
return 0;
}
+
+
+struct cand
+{
+ Rational abc[3];
+ double fom;
+};
+
+
+static int cmpcand(const void *av, const void *bv)
+{
+ const struct cand *a = av;
+ const struct cand *b = bv;
+ return a->fom > b->fom;
+}
+
+
+static Rational *find_candidates(double len, double *a, double *b, double *c,
+ double ltl, int csl, int *pncand)
+{
+ Rational *r;
+ struct cand *cands;
+ const int max_cand = 1024;
+ int ncand = 0;
+ Rational *rat;
+ int nrat;
+ int nrej = 0;
+ int ia, ib, ic;
+ int i;
+
+ cands = malloc(max_cand * sizeof(struct cand));
+ if ( cands == NULL ) return NULL;
+
+ rat = rtnl_list(-5, 5, 1, csl ? 4 : 1, &nrat);
+ if ( rat == NULL ) return NULL;
+
+ for ( ia=0; ia<nrat; ia++ ) {
+ for ( ib=0; ib<nrat; ib++ ) {
+ for ( ic=0; ic<nrat; ic++ ) {
+ double vec[3];
+ double abc[3];
+ double veclen;
+ abc[0] = rtnl_as_double(rat[ia]);
+ abc[1] = rtnl_as_double(rat[ib]);
+ abc[2] = rtnl_as_double(rat[ic]);
+ vec[0] = a[0]*abc[0] + b[0]*abc[1] + c[0]*abc[2];
+ vec[1] = a[1]*abc[0] + b[1]*abc[1] + c[1]*abc[2];
+ vec[2] = a[2]*abc[0] + b[2]*abc[1] + c[2]*abc[2];
+ veclen = modulus(vec[0], vec[1], vec[2]);
+ if ( within_tolerance(len, veclen, ltl*100.0) ) {
+ if ( ncand == max_cand ) {
+ nrej++;
+ } else {
+ cands[ncand].abc[0] = rat[ia];
+ cands[ncand].abc[1] = rat[ib];
+ cands[ncand].abc[2] = rat[ic];
+ cands[ncand].fom = fabs(veclen - len);
+ ncand++;
+ }
+ }
+ }
+ }
+ }
+
+ if ( nrej ) {
+ ERROR("WARNING: Too many vector candidates (%i rejected)\n", nrej);
+ }
+
+ /* Sort by difference from reference vector length */
+ qsort(cands, ncand, sizeof(struct cand), cmpcand);
+
+ r = malloc(ncand * 3 * sizeof(Rational));
+ if ( r == 0 ) return NULL;
+
+ for ( i=0; i<ncand; i++ ) {
+ r[3*i+0] = cands[i].abc[0];
+ r[3*i+1] = cands[i].abc[1];
+ r[3*i+2] = cands[i].abc[2];
+ }
+ free(cands);
+
+ *pncand = ncand;
+ return r;
+}
+
+
+static void g6_components(double *g6, double a, double b, double c,
+ double al, double be, double ga)
+{
+ g6[0] = a*a;
+ g6[1] = b*b;
+ g6[2] = c*c;
+ g6[3] = 2.0*b*c*cos(al);
+ g6[4] = 2.0*a*c*cos(be);
+ g6[5] = 2.0*a*b*cos(ga);
+}
+
+
+static double g6_distance(double a1, double b1, double c1,
+ double al1, double be1, double ga1,
+ double a2, double b2, double c2,
+ double al2, double be2, double ga2)
+{
+ double g1[6], g2[6];
+ int i;
+ double total = 0.0;
+ g6_components(g1, a1, b1, c1, al1, be1, ga1);
+ g6_components(g2, a2, b2, c2, al2, be2, ga2);
+ for ( i=0; i<6; i++ ) {
+ total += (g1[i]-g2[i])*(g1[i]-g2[i]);
+ }
+ return sqrt(total);
+}
+
+
+/**
+ * \param cell_in: A UnitCell
+ * \param reference_in: Another UnitCell
+ * \param ltl: Maximum allowable fractional difference in direct-space axis lengths
+ * \param atl: Maximum allowable difference in direct-space angles (in radians)
+ * \param csl: Non-zero to look for coincidence site lattice relationships
+ * \param pmb: Place to store pointer to matrix
+ *
+ * Compare the \p cell_in with \p reference_in. If \p cell is a derivative lattice
+ * of \p reference, within fractional axis length difference \p ltl and absolute angle
+ * difference \p atl (in radians), this function returns non-zero and stores the
+ * transformation which needs to be applied to \p cell_in at \p pmb.
+ *
+ * Note that the tolerances will be applied to the primitive unit cell. If
+ * the reference cell is centered, a primitive unit cell will first be calculated.
+ *
+ * Subject to the tolerances, this function will find the transformation which
+ * gives the best match to the reference cell, using the Euclidian norm in
+ * G6 [see e.g. Andrews and Bernstein, Acta Cryst. A44 (1988) p1009].
+ *
+ * Only the cell parameters will be compared. The relative orientations are
+ * irrelevant.
+ *
+ * If \p csl is zero, the lattices must be derivatives of one another. If
+ * non-zero, a coincidence site lattice relationship will be searched for,
+ * meaning that the lattice points of the transformed version of \p cell_in
+ * might not coincide with lattice points of \p reference_in.
+ *
+ * \returns non-zero if the cells match, zero for no match or error.
+ *
+ */
+int compare_reindexed_cell_parameters(UnitCell *cell_in, UnitCell *reference_in,
+ double ltl, double atl, int csl,
+ RationalMatrix **pmb)
+{
+ UnitCell *cell;
+ UnitCell *reference;
+ IntegerMatrix *CBint;
+ RationalMatrix *CiA;
+ RationalMatrix *CB;
+ RationalMatrix *M;
+ double a, b, c, al, be, ga;
+ double av[3], bv[3], cv[3];
+ Rational *cand_a;
+ Rational *cand_b;
+ Rational *cand_c;
+ int ncand_a, ncand_b, ncand_c;
+ int ia, ib;
+ RationalMatrix *MCB;
+ RationalMatrix *CiAMCB;
+ double min_dist = +INFINITY;
+
+ /* Actually compare against primitive version of reference */
+ reference = uncenter_cell(reference_in, &CBint, NULL);
+ if ( reference == NULL ) return 0;
+ CB = rtnl_mtx_from_intmat(CBint);
+ intmat_free(CBint);
+
+ /* Actually compare primitive version of cell */
+ cell = uncenter_cell(cell_in, NULL, &CiA);
+ if ( cell == NULL ) return 0;
+
+ /* Get target parameters */
+ cell_get_parameters(reference, &a, &b, &c, &al, &be, &ga);
+ cell_get_cartesian(cell, &av[0], &av[1], &av[2],
+ &bv[0], &bv[1], &bv[2],
+ &cv[0], &cv[1], &cv[2]);
+
+ /* Find vectors in 'cell' with lengths close to a, b and c */
+ cand_a = find_candidates(a, av, bv, cv, ltl, csl, &ncand_a);
+ cand_b = find_candidates(b, av, bv, cv, ltl, csl, &ncand_b);
+ cand_c = find_candidates(c, av, bv, cv, ltl, csl, &ncand_c);
+
+ if ( (ncand_a==0) || (ncand_b==0) || (ncand_c==0) ) {
+ *pmb = NULL;
+ cell_free(cell);
+ cell_free(reference);
+ rtnl_mtx_free(CB);
+ rtnl_mtx_free(CiA);
+ return 0;
+ }
+
+ M = rtnl_mtx_new(3, 3);
+ MCB = rtnl_mtx_new(3, 3);
+ CiAMCB = rtnl_mtx_new(3, 3);
+ for ( ia=0; ia<ncand_a; ia++ ) {
+ for ( ib=0; ib<ncand_b; ib++ ) {
+
+ UnitCell *test;
+ double at, bt, ct, alt, bet, gat;
+ double dist;
+ int ic = 0;
+
+ /* Form the matrix using the first candidate for c */
+ rtnl_mtx_set(M, 0, 0, cand_a[3*ia+0]);
+ rtnl_mtx_set(M, 1, 0, cand_a[3*ia+1]);
+ rtnl_mtx_set(M, 2, 0, cand_a[3*ia+2]);
+ rtnl_mtx_set(M, 0, 1, cand_b[3*ib+0]);
+ rtnl_mtx_set(M, 1, 1, cand_b[3*ib+1]);
+ rtnl_mtx_set(M, 2, 1, cand_b[3*ib+2]);
+ rtnl_mtx_set(M, 0, 2, cand_c[3*ic+0]);
+ rtnl_mtx_set(M, 1, 2, cand_c[3*ic+1]);
+ rtnl_mtx_set(M, 2, 2, cand_c[3*ic+2]);
+
+ /* Check angle between a and b */
+ test = cell_transform_rational(cell, M);
+ cell_get_parameters(test, &at, &bt, &ct, &alt, &bet, &gat);
+ cell_free(test);
+ if ( fabs(gat - ga) > atl ) continue;
+
+ /* Gamma OK, now look for place for c axis */
+ for ( ic=0; ic<ncand_c; ic++ ) {
+
+ rtnl_mtx_set(M, 0, 0, cand_a[3*ia+0]);
+ rtnl_mtx_set(M, 1, 0, cand_a[3*ia+1]);
+ rtnl_mtx_set(M, 2, 0, cand_a[3*ia+2]);
+ rtnl_mtx_set(M, 0, 1, cand_b[3*ib+0]);
+ rtnl_mtx_set(M, 1, 1, cand_b[3*ib+1]);
+ rtnl_mtx_set(M, 2, 1, cand_b[3*ib+2]);
+ rtnl_mtx_set(M, 0, 2, cand_c[3*ic+0]);
+ rtnl_mtx_set(M, 1, 2, cand_c[3*ic+1]);
+ rtnl_mtx_set(M, 2, 2, cand_c[3*ic+2]);
+
+ if ( rtnl_cmp(rtnl_mtx_det(M),rtnl_zero()) == 0 ) continue;
+
+ test = cell_transform_rational(cell, M);
+
+ if ( !csl && (cell_get_centering(test) != 'P') ) continue;
+
+ cell_get_parameters(test, &at, &bt, &ct, &alt, &bet, &gat);
+ if ( !right_handed(test) ) {
+ cell_free(test);
+ continue;
+ }
+ if ( fabs(alt - al) > atl ) {
+ cell_free(test);
+ continue;
+ }
+ if ( fabs(bet - be) > atl ) {
+ cell_free(test);
+ continue;
+ }
+
+ dist = g6_distance(at, bt, ct, alt, bet, gat,
+ a, b, c, al, be, ga);
+ if ( dist < min_dist ) {
+ min_dist = dist;
+ rtnl_mtx_mtxmult(M, CB, MCB);
+ rtnl_mtx_mtxmult(CiA, MCB, CiAMCB);
+ }
+
+ cell_free(test);
+
+ }
+ }
+ }
+
+ rtnl_mtx_free(M);
+ rtnl_mtx_free(MCB);
+ free(cand_a);
+ free(cand_b);
+ free(cand_c);
+
+ if ( isinf(min_dist) ) {
+ rtnl_mtx_free(CiAMCB);
+ *pmb = NULL;
+ return 0;
+ }
+
+ /* Solution found */
+ *pmb = CiAMCB;
+ return 1;
+}
diff --git a/libcrystfel/src/cell-utils.h b/libcrystfel/src/cell-utils.h
index 5e2b2825..be878c10 100644
--- a/libcrystfel/src/cell-utils.h
+++ b/libcrystfel/src/cell-utils.h
@@ -3,13 +3,13 @@
*
* Unit Cell utility functions
*
- * Copyright © 2012-2017 Deutsches Elektronen-Synchrotron DESY,
+ * Copyright © 2012-2019 Deutsches Elektronen-Synchrotron DESY,
* a research centre of the Helmholtz Association.
* Copyright © 2012 Lorenzo Galli
*
* Authors:
- * 2009-2013,2014,2017 Thomas White <taw@physics.org>
- * 2012 Lorenzo Galli
+ * 2009-2018 Thomas White <taw@physics.org>
+ * 2012 Lorenzo Galli
*
* This file is part of CrystFEL.
*
@@ -43,15 +43,21 @@
extern "C" {
#endif
+/**
+ * \file cell-utils.h
+ * Unit cell utility functions.
+ **/
+
+
extern double resolution(UnitCell *cell,
signed int h, signed int k, signed int l);
extern UnitCell *cell_rotate(UnitCell *in, struct quaternion quat);
extern UnitCell *rotate_cell(UnitCell *in, double omega, double phi,
double rot);
-extern UnitCell *transform_cell_gsl(UnitCell *in, gsl_matrix *m);
extern void cell_print(UnitCell *cell);
+extern void cell_print_full(UnitCell *cell);
extern UnitCell *match_cell(UnitCell *cell, UnitCell *tempcell, int verbose,
const float *ltl, int reduce);
@@ -66,7 +72,8 @@ extern int cell_is_sensible(UnitCell *cell);
extern int validate_cell(UnitCell *cell);
-extern UnitCell *uncenter_cell(UnitCell *in, UnitCellTransformation **t);
+extern UnitCell *uncenter_cell(UnitCell *in, IntegerMatrix **pC,
+ RationalMatrix **pCi);
extern int bravais_lattice(UnitCell *cell);
@@ -80,8 +87,24 @@ extern int forbidden_reflection(UnitCell *cell,
extern double cell_get_volume(UnitCell *cell);
-extern int compare_cells(UnitCell *a, UnitCell *b, double ltl, double atl,
- IntegerMatrix **pmb);
+extern int compare_cell_parameters(UnitCell *cell1, UnitCell *cell2,
+ float ltl, float atl);
+
+
+extern int compare_cell_parameters_and_orientation(UnitCell *cell1,
+ UnitCell *cell2,
+ const double ltl,
+ const double atl);
+
+extern int compare_reindexed_cell_parameters_and_orientation(UnitCell *a,
+ UnitCell *b,
+ double ltl,
+ double atl,
+ IntegerMatrix **pmb);
+
+extern int compare_reindexed_cell_parameters(UnitCell *cell, UnitCell *reference,
+ double ltl, double atl, int csl,
+ RationalMatrix **pmb);
#ifdef __cplusplus
}
diff --git a/libcrystfel/src/cell.c b/libcrystfel/src/cell.c
index cc18b49d..6b1d58c7 100644
--- a/libcrystfel/src/cell.c
+++ b/libcrystfel/src/cell.c
@@ -45,18 +45,12 @@
#include "cell.h"
#include "utils.h"
#include "image.h"
+#include "integer_matrix.h"
+#include "rational.h"
/**
- * SECTION:unitcell
- * @short_description: Unit cell
- * @title: UnitCell
- * @section_id:
- * @see_also:
- * @include: "cell.h"
- * @Image:
- *
- * This structure represents a unit cell.
+ * \file cell.h
*/
@@ -95,16 +89,28 @@ struct _unitcell {
char unique_axis;
};
+typedef enum {
+ CMASK_P = 1<<0,
+ CMASK_A = 1<<1,
+ CMASK_B = 1<<2,
+ CMASK_C = 1<<3,
+ CMASK_I = 1<<4,
+ CMASK_F = 1<<5,
+ CMASK_H = 1<<6,
+ CMASK_R = 1<<7
+} CenteringMask;
+
+#define CMASK_ALL (CMASK_P | CMASK_A | CMASK_B | CMASK_C | CMASK_I \
+ | CMASK_F | CMASK_H | CMASK_R)
+
/************************** Setters and Constructors **************************/
/**
- * cell_new:
- *
- * Create a new %UnitCell.
+ * Create a new UnitCell.
*
- * Returns: the new unit cell, or NULL on failure.
+ * \returns the new unit cell, or NULL on failure.
*
*/
UnitCell *cell_new()
@@ -133,8 +139,7 @@ UnitCell *cell_new()
/**
- * cell_free:
- * @cell: A %UnitCell to free.
+ * \param cell: A %UnitCell to free.
*
* Frees a %UnitCell, and all internal resources concerning that cell.
*
@@ -147,10 +152,9 @@ void cell_free(UnitCell *cell)
/**
- * cell_has_parameters:
- * @cell: A %UnitCell
+ * \param cell: A %UnitCell
*
- * Returns: True if @cell has its parameters specified.
+ * \returns True if cell has its parameters specified.
*
*/
int cell_has_parameters(UnitCell *cell)
@@ -289,10 +293,10 @@ void cell_set_unique_axis(UnitCell *cell, char unique_axis)
/************************* Getter helper functions ****************************/
-static int cell_crystallographic_to_cartesian(UnitCell *cell,
- double *ax, double *ay, double *az,
- double *bx, double *by, double *bz,
- double *cx, double *cy, double *cz)
+static int cell_crystallographic_to_cartesian(const UnitCell *cell,
+ double *ax, double *ay, double *az,
+ double *bx, double *by, double *bz,
+ double *cx, double *cy, double *cz)
{
double tmp, V, cosalphastar, cstar;
@@ -352,13 +356,13 @@ static int cell_invert(double ax, double ay, double az,
return 1;
}
gsl_matrix_set(m, 0, 0, ax);
- gsl_matrix_set(m, 0, 1, bx);
- gsl_matrix_set(m, 0, 2, cx);
gsl_matrix_set(m, 1, 0, ay);
- gsl_matrix_set(m, 1, 1, by);
- gsl_matrix_set(m, 1, 2, cy);
gsl_matrix_set(m, 2, 0, az);
+ gsl_matrix_set(m, 0, 1, bx);
+ gsl_matrix_set(m, 1, 1, by);
gsl_matrix_set(m, 2, 1, bz);
+ gsl_matrix_set(m, 0, 2, cx);
+ gsl_matrix_set(m, 1, 2, cy);
gsl_matrix_set(m, 2, 2, cz);
/* Invert */
@@ -394,13 +398,13 @@ static int cell_invert(double ax, double ay, double az,
gsl_matrix_transpose(inv);
*asx = gsl_matrix_get(inv, 0, 0);
- *bsx = gsl_matrix_get(inv, 0, 1);
- *csx = gsl_matrix_get(inv, 0, 2);
*asy = gsl_matrix_get(inv, 1, 0);
- *bsy = gsl_matrix_get(inv, 1, 1);
- *csy = gsl_matrix_get(inv, 1, 2);
*asz = gsl_matrix_get(inv, 2, 0);
+ *bsx = gsl_matrix_get(inv, 0, 1);
+ *bsy = gsl_matrix_get(inv, 1, 1);
*bsz = gsl_matrix_get(inv, 2, 1);
+ *csx = gsl_matrix_get(inv, 0, 2);
+ *csy = gsl_matrix_get(inv, 1, 2);
*csz = gsl_matrix_get(inv, 2, 2);
gsl_matrix_free(inv);
@@ -411,7 +415,7 @@ static int cell_invert(double ax, double ay, double az,
/********************************** Getters ***********************************/
-int cell_get_parameters(UnitCell *cell, double *a, double *b, double *c,
+int cell_get_parameters(const UnitCell *cell, double *a, double *b, double *c,
double *alpha, double *beta, double *gamma)
{
double ax, ay, az, bx, by, bz, cx, cy, cz;
@@ -474,7 +478,7 @@ int cell_get_parameters(UnitCell *cell, double *a, double *b, double *c,
}
-int cell_get_cartesian(UnitCell *cell,
+int cell_get_cartesian(const UnitCell *cell,
double *ax, double *ay, double *az,
double *bx, double *by, double *bz,
double *cx, double *cy, double *cz)
@@ -515,7 +519,7 @@ int cell_get_cartesian(UnitCell *cell,
}
-int cell_get_reciprocal(UnitCell *cell,
+int cell_get_reciprocal(const UnitCell *cell,
double *asx, double *asy, double *asz,
double *bsx, double *bsy, double *bsz,
double *csx, double *csy, double *csz)
@@ -600,333 +604,401 @@ const char *cell_rep(UnitCell *cell)
}
-struct _unitcelltransformation
-{
- gsl_matrix *m;
-};
-
-
-/**
- * tfn_inverse:
- * @t: A %UnitCellTransformation.
- *
- * Calculates the inverse of @t. That is, if you apply cell_transform() to a
- * %UnitCell using @t, and then apply cell_transform() to the result using
- * tfn_inverse(@t) instead of @t, you will recover the same lattice vectors
- * (but note that the lattice type, centering and unique axis information will
- * be lost).
- *
- * Returns: The inverse of @t.
- *
- */
-UnitCellTransformation *tfn_inverse(UnitCellTransformation *t)
+UnitCell *cell_transform_gsl_direct(UnitCell *in, gsl_matrix *m)
{
- int s;
- gsl_matrix *m;
- gsl_matrix *inv;
- gsl_permutation *perm;
- UnitCellTransformation *out;
-
- m = gsl_matrix_alloc(3, 3);
- if ( m == NULL ) return NULL;
-
- out = tfn_identity();
- if ( out == NULL ) {
- gsl_matrix_free(m);
- return NULL;
- }
-
- gsl_matrix_memcpy(m, t->m);
-
- perm = gsl_permutation_alloc(m->size1);
- if ( perm == NULL ) {
- ERROR("Couldn't allocate permutation\n");
- return NULL;
- }
- inv = gsl_matrix_alloc(m->size1, m->size2);
- if ( inv == NULL ) {
- ERROR("Couldn't allocate inverse\n");
- gsl_permutation_free(perm);
- return NULL;
- }
- if ( gsl_linalg_LU_decomp(m, perm, &s) ) {
- ERROR("Couldn't decompose matrix\n");
- gsl_permutation_free(perm);
- return NULL;
- }
- if ( gsl_linalg_LU_invert(m, perm, inv) ) {
- ERROR("Couldn't invert transformation matrix\n");
- gsl_permutation_free(perm);
- return NULL;
- }
- gsl_permutation_free(perm);
+ gsl_matrix *c;
+ double asx, asy, asz;
+ double bsx, bsy, bsz;
+ double csx, csy, csz;
+ gsl_matrix *res;
+ UnitCell *out;
- gsl_matrix_free(out->m);
- gsl_matrix_free(m);
- out->m = inv;
+ cell_get_cartesian(in, &asx, &asy, &asz, &bsx, &bsy,
+ &bsz, &csx, &csy, &csz);
+
+ c = gsl_matrix_alloc(3, 3);
+ gsl_matrix_set(c, 0, 0, asx);
+ gsl_matrix_set(c, 1, 0, asy);
+ gsl_matrix_set(c, 2, 0, asz);
+ gsl_matrix_set(c, 0, 1, bsx);
+ gsl_matrix_set(c, 1, 1, bsy);
+ gsl_matrix_set(c, 2, 1, bsz);
+ gsl_matrix_set(c, 0, 2, csx);
+ gsl_matrix_set(c, 1, 2, csy);
+ gsl_matrix_set(c, 2, 2, csz);
+
+ res = gsl_matrix_calloc(3, 3);
+ gsl_blas_dgemm(CblasNoTrans, CblasNoTrans, 1.0, c, m, 0.0, res);
+
+ out = cell_new_from_cell(in);
+ cell_set_cartesian(out, gsl_matrix_get(res, 0, 0),
+ gsl_matrix_get(res, 1, 0),
+ gsl_matrix_get(res, 2, 0),
+ gsl_matrix_get(res, 0, 1),
+ gsl_matrix_get(res, 1, 1),
+ gsl_matrix_get(res, 2, 1),
+ gsl_matrix_get(res, 0, 2),
+ gsl_matrix_get(res, 1, 2),
+ gsl_matrix_get(res, 2, 2));
+
+ gsl_matrix_free(res);
+ gsl_matrix_free(c);
return out;
}
-/**
- * cell_transform:
- * @cell: A %UnitCell.
- * @t: A %UnitCellTransformation.
- *
- * Applies @t to @cell. Note that the lattice type, centering and unique axis
- * information will not be preserved.
- *
- * Returns: Transformed copy of @cell.
- *
- */
-UnitCell *cell_transform(UnitCell *cell, UnitCellTransformation *t)
-{
- UnitCell *out;
- double ax, ay, az;
- double bx, by, bz;
- double cx, cy, cz;
- gsl_matrix *m;
- gsl_matrix *a;
+static int centering_has_point(char cen, Rational *p)
+{
+ /* First, put the point into the range 0..1 */
+ while ( rtnl_cmp(p[0], rtnl_zero()) < 0 ) p[0] = rtnl_add(p[0], rtnl(1, 1));
+ while ( rtnl_cmp(p[1], rtnl_zero()) < 0 ) p[1] = rtnl_add(p[1], rtnl(1, 1));
+ while ( rtnl_cmp(p[2], rtnl_zero()) < 0 ) p[2] = rtnl_add(p[2], rtnl(1, 1));
+ while ( rtnl_cmp(p[0], rtnl(1, 1)) >= 0 ) p[0] = rtnl_sub(p[0], rtnl(1, 1));
+ while ( rtnl_cmp(p[1], rtnl(1, 1)) >= 0 ) p[1] = rtnl_sub(p[1], rtnl(1, 1));
+ while ( rtnl_cmp(p[2], rtnl(1, 1)) >= 0 ) p[2] = rtnl_sub(p[2], rtnl(1, 1));
+
+ /* 0,0,0 is present in all centerings */
+ if ( (rtnl_cmp(p[0], rtnl_zero()) == 0)
+ && (rtnl_cmp(p[1], rtnl_zero()) == 0)
+ && (rtnl_cmp(p[2], rtnl_zero()) == 0) ) return 1;
+
+ /* Only I has 1/2 , 1/2, 1/2 */
+ if ( (rtnl_cmp(p[0], rtnl(1,2)) == 0)
+ && (rtnl_cmp(p[1], rtnl(1,2)) == 0)
+ && (rtnl_cmp(p[2], rtnl(1,2)) == 0)
+ && (cen == 'I') ) return 1;
+
+ /* A or F has 0 , 1/2, 1/2 */
+ if ( (rtnl_cmp(p[0], rtnl_zero()) == 0)
+ && (rtnl_cmp(p[1], rtnl(1,2)) == 0)
+ && (rtnl_cmp(p[2], rtnl(1,2)) == 0)
+ && ((cen == 'A') || (cen == 'F')) ) return 1;
+
+ /* B or F has 1/2 , 0 , 1/2 */
+ if ( (rtnl_cmp(p[0], rtnl(1,2)) == 0)
+ && (rtnl_cmp(p[1], rtnl_zero()) == 0)
+ && (rtnl_cmp(p[2], rtnl(1,2)) == 0)
+ && ((cen == 'B') || (cen == 'F')) ) return 1;
+
+ /* C or F has 1/2 , 1/2 , 0 */
+ if ( (rtnl_cmp(p[0], rtnl(1,2)) == 0)
+ && (rtnl_cmp(p[1], rtnl(1,2)) == 0)
+ && (rtnl_cmp(p[2], rtnl_zero()) == 0)
+ && ((cen == 'C') || (cen == 'F')) ) return 1;
+
+ /* H has 2/3 , 1/3 , 1/3 */
+ if ( (rtnl_cmp(p[0], rtnl(2,3)) == 0)
+ && (rtnl_cmp(p[1], rtnl(1,3)) == 0)
+ && (rtnl_cmp(p[2], rtnl(1,3)) == 0)
+ && (cen == 'H') ) return 1;
+
+ /* H has 1/3 , 2/3 , 2/3 */
+ if ( (rtnl_cmp(p[0], rtnl(1,3)) == 0)
+ && (rtnl_cmp(p[1], rtnl(2,3)) == 0)
+ && (rtnl_cmp(p[2], rtnl(2,3)) == 0)
+ && (cen == 'H') ) return 1;
- if ( t == NULL ) return NULL;
+ return 0;
+}
- out = cell_new_from_cell(cell);
- if ( out == NULL ) return NULL;
- if ( cell_get_cartesian(out, &ax, &ay, &az,
- &bx, &by, &bz,
- &cx, &cy, &cz) ) return NULL;
+static void maybe_eliminate(CenteringMask c, CenteringMask *cmask, Rational *nc,
+ char cen)
+{
+ /* Skip test if this centering isn't even a candidate */
+ if ( !(*cmask & c) ) return;
- m = gsl_matrix_alloc(3,3);
- a = gsl_matrix_calloc(3,3);
- if ( (m == NULL) || (a == NULL) ) {
- cell_free(out);
- return NULL;
+ if ( !centering_has_point(cen, nc) ) {
+ *cmask |= c;
+ *cmask ^= c;
}
+}
- gsl_matrix_set(m, 0, 0, ax);
- gsl_matrix_set(m, 0, 1, ay);
- gsl_matrix_set(m, 0, 2, az);
- gsl_matrix_set(m, 1, 0, bx);
- gsl_matrix_set(m, 1, 1, by);
- gsl_matrix_set(m, 1, 2, bz);
- gsl_matrix_set(m, 2, 0, cx);
- gsl_matrix_set(m, 2, 1, cy);
- gsl_matrix_set(m, 2, 2, cz);
-
- gsl_blas_dgemm(CblasNoTrans, CblasNoTrans, 1.0, t->m, m, 0.0, a);
- cell_set_cartesian(out, gsl_matrix_get(a, 0, 0),
- gsl_matrix_get(a, 0, 1),
- gsl_matrix_get(a, 0, 2),
- gsl_matrix_get(a, 1, 0),
- gsl_matrix_get(a, 1, 1),
- gsl_matrix_get(a, 1, 2),
- gsl_matrix_get(a, 2, 0),
- gsl_matrix_get(a, 2, 1),
- gsl_matrix_get(a, 2, 2));
+/* Check if the point x,y,z in the original cell matches any lattice point
+ * in the transformed cell */
+static void check_point_fwd(RationalMatrix *P, CenteringMask *cmask,
+ Rational x, Rational y, Rational z)
+{
+ Rational c[3] = {x, y, z};
+ Rational nc[3];
- gsl_matrix_free(a);
- gsl_matrix_free(m);
+ /* Transform the lattice point */
+ transform_fractional_coords_rtnl(P, c, nc);
- return out;
+ /* Eliminate any centerings which don't include the transformed point */
+ maybe_eliminate(CMASK_P, cmask, nc, 'P');
+ maybe_eliminate(CMASK_R, cmask, nc, 'R');
+ maybe_eliminate(CMASK_A, cmask, nc, 'A');
+ maybe_eliminate(CMASK_B, cmask, nc, 'B');
+ maybe_eliminate(CMASK_C, cmask, nc, 'C');
+ maybe_eliminate(CMASK_I, cmask, nc, 'I');
+ maybe_eliminate(CMASK_F, cmask, nc, 'F');
+ maybe_eliminate(CMASK_H, cmask, nc, 'H');
}
-/**
- * cell_transform_inverse:
- * @cell: A %UnitCell.
- * @t: A %UnitCellTransformation.
- *
- * Applies the inverse of @t to @cell.
- *
- * Returns: Transformed copy of @cell.
- *
- */
-UnitCell *cell_transform_inverse(UnitCell *cell, UnitCellTransformation *t)
+/* Check if the point x,y,z in the transformed cell matches any lattice point
+ * in the original cell. If not, eliminate "exclude" from "*mask". */
+static void check_point_bwd(RationalMatrix *P, CenteringMask *mask,
+ char cen, CenteringMask exclude,
+ Rational x, Rational y, Rational z)
{
- UnitCellTransformation *inv;
- UnitCell *out;
+ Rational nc[3];
+ Rational c[3] = {x, y, z};
- inv = tfn_inverse(t);
- out = cell_transform(cell, inv);
- tfn_free(inv);
- return out;
+ transform_fractional_coords_rtnl_inverse(P, c, nc);
+
+ if ( !centering_has_point(cen, nc) ) {
+ *mask |= exclude;
+ *mask ^= exclude; /* Unset bits */
+ }
}
-/**
- * tfn_identity:
- *
- * Returns: A %UnitCellTransformation corresponding to an identity operation.
- *
- */
-UnitCellTransformation *tfn_identity()
+static char cmask_decode(CenteringMask mask)
{
- UnitCellTransformation *tfn;
+ char res[32];
- tfn = calloc(1, sizeof(UnitCellTransformation));
- if ( tfn == NULL ) return NULL;
+ res[0] = '\0';
- tfn->m = gsl_matrix_alloc(3, 3);
- if ( tfn->m == NULL ) {
- free(tfn);
- return NULL;
- }
+ if ( mask & CMASK_H ) strcat(res, "H");
+ if ( mask & CMASK_F ) strcat(res, "F");
+ if ( mask & CMASK_I ) strcat(res, "I");
+ if ( mask & CMASK_A ) strcat(res, "A");
+ if ( mask & CMASK_B ) strcat(res, "B");
+ if ( mask & CMASK_C ) strcat(res, "C");
+ if ( mask & CMASK_P ) strcat(res, "P");
+ if ( mask & CMASK_R ) strcat(res, "R");
- gsl_matrix_set_identity(tfn->m);
-
- return tfn;
+ if ( strlen(res) == 0 ) return '?';
+ return res[0];
}
+static char determine_centering(RationalMatrix *P, char cen)
+{
+ CenteringMask cmask = CMASK_ALL;
+
+ /* Check whether the current centering can provide all the lattice
+ * points for the transformed cell. Eliminate any centerings for which
+ * it can't. */
+ check_point_bwd(P, &cmask, cen, CMASK_A | CMASK_F, rtnl_zero(), rtnl(1,2), rtnl(1,2));
+ check_point_bwd(P, &cmask, cen, CMASK_B | CMASK_F, rtnl(1,2), rtnl_zero(), rtnl(1,2));
+ check_point_bwd(P, &cmask, cen, CMASK_C | CMASK_F, rtnl(1,2), rtnl(1,2), rtnl_zero());
+ check_point_bwd(P, &cmask, cen, CMASK_I, rtnl(1,2), rtnl(1,2), rtnl(1,2));
+ check_point_bwd(P, &cmask, cen, CMASK_H, rtnl(2,3), rtnl(1,3), rtnl(1,3));
+ check_point_bwd(P, &cmask, cen, CMASK_H, rtnl(1,3), rtnl(2,3), rtnl(2,3));
+ check_point_bwd(P, &cmask, cen, CMASK_ALL, rtnl(1,1), rtnl(1,1), rtnl(1,1));
+
+ /* Check whether the current centering's lattice points will all
+ * coincide with lattice points in the new centering. Eliminate any
+ * centerings for which they don't (they give "excess lattice points"). */
+ switch ( cen ) {
+
+ case 'P' :
+ case 'R' :
+ check_point_fwd(P, &cmask, rtnl(1,1), rtnl(1,1), rtnl(1,1));
+ break;
+
+ case 'A' :
+ check_point_fwd(P, &cmask, rtnl(1,1), rtnl(1,1), rtnl(1,1));
+ check_point_fwd(P, &cmask, rtnl_zero(), rtnl(1,2), rtnl(1,2));
+ break;
+
+ case 'B' :
+ check_point_fwd(P, &cmask, rtnl(1,1), rtnl(1,1), rtnl(1,1));
+ check_point_fwd(P, &cmask, rtnl(1,2), rtnl_zero(), rtnl(1,2));
+ break;
+
+ case 'C' :
+ check_point_fwd(P, &cmask, rtnl(1,1), rtnl(1,1), rtnl(1,1));
+ check_point_fwd(P, &cmask, rtnl(1,2), rtnl(1,2), rtnl_zero());
+ break;
+
+ case 'I' :
+ check_point_fwd(P, &cmask, rtnl(1,1), rtnl(1,1), rtnl(1,1));
+ check_point_fwd(P, &cmask, rtnl(1,2), rtnl(1,2), rtnl(1,2));
+ break;
+
+ case 'F' :
+ check_point_fwd(P, &cmask, rtnl(1,1), rtnl(1,1), rtnl(1,1));
+ check_point_fwd(P, &cmask, rtnl_zero(), rtnl(1,2), rtnl(1,2));
+ check_point_fwd(P, &cmask, rtnl(1,2), rtnl_zero(), rtnl(1,2));
+ check_point_fwd(P, &cmask, rtnl(1,2), rtnl(1,2), rtnl_zero());
+ break;
+
+ case 'H' :
+ check_point_fwd(P, &cmask, rtnl(1,1), rtnl(1,1), rtnl(1,1));
+ check_point_fwd(P, &cmask, rtnl(2,3), rtnl(1,3), rtnl(1,3));
+ check_point_fwd(P, &cmask, rtnl(1,3), rtnl(2,3), rtnl(2,3));
+ break;
-/**
- * tfn_from_intmat:
- * @m: An %IntegerMatrix
- *
- * Returns: A %UnitCellTransformation corresponding to @m.
- *
- */
-UnitCellTransformation *tfn_from_intmat(IntegerMatrix *m)
-{
- UnitCellTransformation *tfn;
- int i, j;
-
- tfn = tfn_identity();
- if ( tfn == NULL ) return NULL;
-
- for ( i=0; i<3; i++ ) {
- for ( j=0; j<3; j++ ) {
- gsl_matrix_set(tfn->m, i, j, intmat_get(m, i, j));
- }
}
- return tfn;
+ return cmask_decode(cmask);
}
/**
- * tfn_combine:
- * @t: A %UnitCellTransformation
- * @na: Pointer to three doubles representing naa, nab, nac
- * @nb: Pointer to three doubles representing nba, nbb, nbc
- * @nc: Pointer to three doubles representing nca, ncb, ncc
+ * \param cell: A %UnitCell.
+ * \param m: A %RationalMatrix.
+ *
+ * Applies \p m to \p cell.
*
- * Updates @t such that it represents its previous transformation followed by
- * a new transformation, corresponding to letting a = naa*a + nab*b + nac*c.
- * Likewise, a = nba*a + nbb*b + nbc*c and c = nca*a + ncb*b + ncc*c.
+ * This function will determine the centering of the resulting unit cell,
+ * producing '?' if any lattice points cannot be accounted for. Note that if
+ * there are 'excess' lattice points in the transformed cell, the centering
+ * will still be '?' even if the lattice points for another centering are
+ * all present.
+ *
+ * The lattice type will be set to triclinic, and the unique axis to '?'.
+ *
+ * \returns Transformed copy of \p cell.
*
*/
-void tfn_combine(UnitCellTransformation *t, double *na, double *nb, double *nc)
+UnitCell *cell_transform_rational(UnitCell *cell, RationalMatrix *m)
{
- gsl_matrix *a;
- gsl_matrix *n;
+ UnitCell *out;
+ gsl_matrix *tm;
+ char ncen;
+ int i, j;
+ Rational det;
- n = gsl_matrix_alloc(3, 3);
- a = gsl_matrix_calloc(3, 3);
- if ( (n == NULL) || (a == NULL) ) {
- return;
+ if ( m == NULL ) return NULL;
+
+ det = rtnl_mtx_det(m);
+ if ( rtnl_cmp(det, rtnl_zero()) == 0 ) return NULL;
+
+ tm = gsl_matrix_alloc(3,3);
+ if ( tm == NULL ) {
+ return NULL;
+ }
+
+ for ( i=0; i<3; i++ ) {
+ for ( j=0; j<3; j++ ) {
+ gsl_matrix_set(tm, i, j,
+ rtnl_as_double(rtnl_mtx_get(m, i, j)));
+ }
}
- gsl_matrix_set(n, 0, 0, na[0]);
- gsl_matrix_set(n, 0, 1, na[1]);
- gsl_matrix_set(n, 0, 2, na[2]);
- gsl_matrix_set(n, 1, 0, nb[0]);
- gsl_matrix_set(n, 1, 1, nb[1]);
- gsl_matrix_set(n, 1, 2, nb[2]);
- gsl_matrix_set(n, 2, 0, nc[0]);
- gsl_matrix_set(n, 2, 1, nc[1]);
- gsl_matrix_set(n, 2, 2, nc[2]);
- free(na);
- free(nb);
- free(nc);
+ out = cell_transform_gsl_direct(cell, tm);
+ gsl_matrix_free(tm);
+
+ ncen = determine_centering(m, cell_get_centering(cell));
+ cell_set_centering(out, ncen);
- gsl_blas_dgemm(CblasNoTrans, CblasNoTrans, 1.0, n, t->m, 0.0, a);
+ /* FIXME: Update unique axis, lattice type */
+ cell_set_lattice_type(out, L_TRICLINIC);
+ cell_set_unique_axis(out, '?');
- gsl_matrix_free(t->m);
- t->m = a;
- gsl_matrix_free(n);
+ return out;
}
/**
- * tfn_vector:
- * @a: Amount of "a" to include in new vector
- * @b: Amount of "b" to include in new vector
- * @c: Amount of "c" to include in new vector
+ * \param cell: A %UnitCell.
+ * \param m: An %IntegerMatrix.
+ *
+ * Applies \p m to \p cell.
+ *
+ * This is just a convenience function which turns \p m into a %RationalMatrix
+ * and then calls cell_transform_rational(). See the documentation for that
+ * function for some important information.
*
- * This is a convenience function to use when sending vectors to tfn_combine():
- * tfn_combine(tfn, tfn_vector(1,0,0),
- * tfn_vector(0,2,0),
- * tfn_vector(0,0,1));
+ * \returns Transformed copy of \p cell.
*
*/
-double *tfn_vector(double a, double b, double c)
+UnitCell *cell_transform_intmat(UnitCell *cell, IntegerMatrix *m)
{
- double *vec = malloc(3*sizeof(double));
- if ( vec == NULL ) return NULL;
- vec[0] = a; vec[1] = b; vec[2] = c;
- return vec;
+ UnitCell *ans;
+ RationalMatrix *mtx = rtnl_mtx_from_intmat(m);
+ ans = cell_transform_rational(cell, mtx);
+ rtnl_mtx_free(mtx);
+ return ans;
}
/**
- * tfn_print:
- * @t: A %UnitCellTransformation
+ * \param cell: A %UnitCell.
+ * \param m: A %RationalMatrix
*
- * Prints information about @t to stderr.
+ * Applies the inverse of \p m to \p cell.
+ *
+ * \returns Transformed copy of \p cell.
*
*/
-void tfn_print(UnitCellTransformation *t)
+UnitCell *cell_transform_rational_inverse(UnitCell *cell, RationalMatrix *m)
{
+ UnitCell *out;
+ gsl_matrix *tm;
+ gsl_matrix *inv;
gsl_permutation *perm;
- gsl_matrix *lu;
int s;
+ int i, j;
+
+ if ( m == NULL ) return NULL;
- STATUS("New a = %+.2fa %+.2fb %+.2fc\n", gsl_matrix_get(t->m, 0, 0),
- gsl_matrix_get(t->m, 0, 1),
- gsl_matrix_get(t->m, 0, 2));
- STATUS("New b = %+.2fa %+.2fb %+.2fc\n", gsl_matrix_get(t->m, 1, 0),
- gsl_matrix_get(t->m, 1, 1),
- gsl_matrix_get(t->m, 1, 2));
- STATUS("New c = %+.2fa %+.2fb %+.2fc\n", gsl_matrix_get(t->m, 2, 0),
- gsl_matrix_get(t->m, 2, 1),
- gsl_matrix_get(t->m, 2, 2));
- lu = gsl_matrix_alloc(3, 3);
- if ( lu == NULL ) {
- ERROR("Couldn't allocate LU decomposition.\n");
- return;
+ tm = gsl_matrix_alloc(3,3);
+ if ( tm == NULL ) {
+ return NULL;
}
- gsl_matrix_memcpy(lu, t->m);
+ for ( i=0; i<3; i++ ) {
+ for ( j=0; j<3; j++ ) {
+ gsl_matrix_set(tm, i, j,
+ rtnl_as_double(rtnl_mtx_get(m, i, j)));
+ }
+ }
- perm = gsl_permutation_alloc(t->m->size1);
+ perm = gsl_permutation_alloc(3);
if ( perm == NULL ) {
- ERROR("Couldn't allocate permutation.\n");
- gsl_matrix_free(lu);
- return;
+ ERROR("Couldn't allocate permutation\n");
+ return NULL;
+ }
+ inv = gsl_matrix_alloc(3, 3);
+ if ( inv == NULL ) {
+ ERROR("Couldn't allocate inverse\n");
+ gsl_permutation_free(perm);
+ return NULL;
}
- if ( gsl_linalg_LU_decomp(lu, perm, &s) ) {
- ERROR("LU decomposition failed.\n");
+ if ( gsl_linalg_LU_decomp(tm, perm, &s) ) {
+ ERROR("Couldn't decompose matrix\n");
gsl_permutation_free(perm);
- gsl_matrix_free(lu);
- return;
+ return NULL;
}
+ if ( gsl_linalg_LU_invert(tm, perm, inv) ) {
+ ERROR("Couldn't invert transformation matrix\n");
+ gsl_permutation_free(perm);
+ return NULL;
+ }
+ gsl_permutation_free(perm);
+
+ out = cell_transform_gsl_direct(cell, inv);
+
+ /* FIXME: Update centering, unique axis, lattice type */
- STATUS("Transformation determinant = %.2f\n", gsl_linalg_LU_det(lu, s));
+ gsl_matrix_free(tm);
+ gsl_matrix_free(inv);
+
+ return out;
}
/**
- * tfn_free:
- * @t: A %UnitCellTransformation
+ * \param cell: A %UnitCell.
+ * \param m: An %IntegerMatrix
+ *
+ * Applies the inverse of \p m to \p cell.
*
- * Frees all resources associated with @t.
+ * \returns Transformed copy of \p cell.
*
*/
-void tfn_free(UnitCellTransformation *t)
+UnitCell *cell_transform_intmat_inverse(UnitCell *cell, IntegerMatrix *m)
{
- gsl_matrix_free(t->m);
- free(t);
+ UnitCell *ans;
+ RationalMatrix *mtx = rtnl_mtx_from_intmat(m);
+ ans = cell_transform_rational_inverse(cell, mtx);
+ rtnl_mtx_free(mtx);
+ return ans;
}
diff --git a/libcrystfel/src/cell.h b/libcrystfel/src/cell.h
index 39e6a1ed..4bbc1be2 100644
--- a/libcrystfel/src/cell.h
+++ b/libcrystfel/src/cell.h
@@ -41,32 +41,24 @@
#include "integer_matrix.h"
/**
- * rvec:
- * @u: x component (in reciprocal space)
- * @v: y component (in reciprocal space)
- * @w: z component (in reciprocal space)
- *
+ * \file cell.h
+ * Unit cell structure
+ */
+
+/**
* Structure representing a 3D vector in reciprocal space.
+ *
* Note: Heavily abused to serve as a real space vector as well.
**/
struct rvec
{
- double u;
- double v;
- double w;
+ double u; /**< x component (in reciprocal space) */
+ double v; /**< y component (in reciprocal space) */
+ double w; /**< z component (in reciprocal space) */
};
/**
- * LatticeType:
- * @L_TRICLINIC: Triclinic lattice
- * @L_MONOCLINIC: Monoclinic lattice
- * @L_ORTHORHOMBIC: Orthorhombic lattice
- * @L_TETRAGONAL: Tetragonal lattice
- * @L_RHOMBOHEDRAL: Rhombohedral lattice
- * @L_HEXAGONAL: Hexagonal lattice
- * @L_CUBIC: Cubic lattice
- *
* An enumeration of the possible lattice types: triclinic, monoclinic,
* orthorhombic, tetragonal, rhombohedral, hexagonal and cubic.
**/
@@ -83,7 +75,7 @@ typedef enum
/**
- * UnitCell:
+ * Opaque data structure representing a unit cell.
*
* This data structure is opaque. You must use the available accessor functions
* to read and write its contents.
@@ -91,14 +83,6 @@ typedef enum
typedef struct _unitcell UnitCell;
-/**
- * UnitCellTransformation:
- *
- * This opaque data structure represents a tranformation of a unit cell, such
- * as a rotation or a centering operation.
- **/
-typedef struct _unitcelltransformation UnitCellTransformation;
-
#ifdef __cplusplus
extern "C" {
#endif
@@ -127,15 +111,15 @@ extern void cell_set_cartesian(UnitCell *cell,
extern void cell_set_parameters(UnitCell *cell, double a, double b, double c,
double alpha, double beta, double gamma);
-extern int cell_get_parameters(UnitCell *cell, double *a, double *b, double *c,
+extern int cell_get_parameters(const UnitCell *cell, double *a, double *b, double *c,
double *alpha, double *beta, double *gamma);
-extern int cell_get_cartesian(UnitCell *cell,
+extern int cell_get_cartesian(const UnitCell *cell,
double *ax, double *ay, double *az,
double *bx, double *by, double *bz,
double *cx, double *cy, double *cz);
-extern int cell_get_reciprocal(UnitCell *cell,
+extern int cell_get_reciprocal(const UnitCell *cell,
double *asx, double *asy, double *asz,
double *bsx, double *bsy, double *bsz,
double *csx, double *csy, double *csz);
@@ -156,18 +140,13 @@ extern void cell_set_unique_axis(UnitCell *cell, char unique_axis);
extern const char *cell_rep(UnitCell *cell);
-extern UnitCell *cell_transform(UnitCell *cell, UnitCellTransformation *t);
-extern UnitCell *cell_transform_inverse(UnitCell *cell,
- UnitCellTransformation *t);
-
-extern UnitCellTransformation *tfn_identity(void);
-extern UnitCellTransformation *tfn_from_intmat(IntegerMatrix *m);
-extern void tfn_combine(UnitCellTransformation *t,
- double *na, double *nb, double *nc);
-extern void tfn_print(UnitCellTransformation *t);
-extern UnitCellTransformation *tfn_inverse(UnitCellTransformation *t);
-extern double *tfn_vector(double a, double b, double c);
-extern void tfn_free(UnitCellTransformation *t);
+extern UnitCell *cell_transform_gsl_direct(UnitCell *in, gsl_matrix *m);
+
+extern UnitCell *cell_transform_rational(UnitCell *cell, RationalMatrix *m);
+extern UnitCell *cell_transform_rational_inverse(UnitCell *cell, RationalMatrix *m);
+
+extern UnitCell *cell_transform_intmat(UnitCell *cell, IntegerMatrix *m);
+extern UnitCell *cell_transform_intmat_inverse(UnitCell *cell, IntegerMatrix *m);
#ifdef __cplusplus
}
diff --git a/libcrystfel/src/crystal.c b/libcrystfel/src/crystal.c
index 66a8e19c..f6e881de 100644
--- a/libcrystfel/src/crystal.c
+++ b/libcrystfel/src/crystal.c
@@ -36,15 +36,7 @@
/**
- * SECTION:crystal
- * @short_description: Crystal
- * @title: Crystal
- * @section_id:
- * @see_also:
- * @include: "crystal.h"
- * @Image:
- *
- * This structure represents a single crystal.
+ * \file crystal.h
*/
@@ -84,11 +76,9 @@ struct _crystal
/**
- * crystal_new:
- *
- * Create a new %Crystal.
+ * Create a new \ref Crystal.
*
- * Returns: the new unit cell, or NULL on failure.
+ * \returns The new unit cell, or NULL on failure.
*
*/
Crystal *crystal_new()
@@ -113,14 +103,13 @@ Crystal *crystal_new()
/**
- * crystal_copy:
- * @cryst: A %Crystal to copy.
+ * \param cryst: A \ref Crystal to copy.
*
- * Creates a new %Crystal which is a copy of @cryst. The copy is a "shallow
+ * Creates a new \ref Crystal which is a copy of \p cryst. The copy is a "shallow
* copy", which means that copies are NOT made of the data structures which
- * @cryst contains references to, for example its %RefList.
+ * \p cryst contains references to, for example its \ref RefList.
*
- * Returns: a (shallow) copy of @cryst, or NULL on failure.
+ * \returns A (shallow) copy of \p cryst, or NULL on failure.
*
*/
Crystal *crystal_copy(const Crystal *cryst)
@@ -138,10 +127,9 @@ Crystal *crystal_copy(const Crystal *cryst)
/**
- * crystal_free:
- * @cryst: A %Crystal to free.
+ * \param cryst: A \ref Crystal to free.
*
- * Frees a %Crystal, and all internal resources concerning that crystal.
+ * Frees a \ref Crystal, and all internal resources concerning that crystal.
*
*/
void crystal_free(Crystal *cryst)
@@ -203,6 +191,12 @@ struct image *crystal_get_image(Crystal *cryst)
}
+const struct image *crystal_get_image_const(const Crystal *cryst)
+{
+ return cryst->image;
+}
+
+
double crystal_get_osf(Crystal *cryst)
{
return cryst->osf;
diff --git a/libcrystfel/src/crystal.h b/libcrystfel/src/crystal.h
index cc5754ca..f246ab35 100644
--- a/libcrystfel/src/crystal.h
+++ b/libcrystfel/src/crystal.h
@@ -37,10 +37,12 @@
#include "cell.h"
+/**
+ * \file crystal.h
+ * Data structure representing a crystal
+ */
/**
- * Crystal:
- *
* This data structure is opaque. You must use the available accessor functions
* to read and write its contents.
**/
@@ -67,6 +69,7 @@ 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 const struct image *crystal_get_image_const(const Crystal *cryst);
extern double crystal_get_mosaicity(Crystal *cryst);
extern const char *crystal_get_notes(Crystal *cryst);
extern void crystal_get_det_shift(Crystal *cryst,
diff --git a/libcrystfel/src/detector.c b/libcrystfel/src/detector.c
index 75d5bdb9..fd04beaf 100644
--- a/libcrystfel/src/detector.c
+++ b/libcrystfel/src/detector.c
@@ -3,12 +3,12 @@
*
* Detector properties
*
- * Copyright © 2012-2016 Deutsches Elektronen-Synchrotron DESY,
+ * Copyright © 2012-2019 Deutsches Elektronen-Synchrotron DESY,
* a research centre of the Helmholtz Association.
* Copyright © 2012 Richard Kirian
*
* Authors:
- * 2009-2016 Thomas White <taw@physics.org>
+ * 2009-2019 Thomas White <taw@physics.org>
* 2014 Valerio Mariani
* 2014 Kenneth Beyerlein <kenneth.beyerlein@desy.de>
* 2011 Andrew Aquila
@@ -37,6 +37,7 @@
#include <string.h>
#include <assert.h>
#include <ctype.h>
+#include <sys/stat.h>
#include "image.h"
#include "utils.h"
@@ -45,19 +46,10 @@
/**
- * SECTION:detector
- * @short_description: Detector geometry
- * @title: Detector
- * @section_id:
- * @see_also:
- * @include: "detector.h"
- * @Image:
- *
- * This structure represents the detector geometry
+ * \file detector.h
*/
-
struct rg_definition {
char *name;
char *pns;
@@ -266,8 +258,6 @@ static void build_output_line(const char *line, char *new_line,
break;
}
}
- if ( w == strlen(line) ) strcat(new_line, "\n");
-
for ( i=0; i<n_bits; i++) free(bits[i]);
free(bits);
}
@@ -1179,6 +1169,7 @@ static void find_min_max_d(struct detector *det)
}
}
+
struct detector *get_detector_geometry(const char *filename,
struct beam_params *beam)
{
@@ -1186,14 +1177,13 @@ struct detector *get_detector_geometry(const char *filename,
}
-struct detector *get_detector_geometry_2(const char *filename,
- struct beam_params *beam,
- char **hdf5_peak_path)
+struct detector *get_detector_geometry_from_string(const char *string,
+ struct beam_params *beam,
+ char **hdf5_peak_path)
{
- FILE *fh;
struct detector *det;
- char *rval;
char **bits;
+ int done = 0;
int i;
int rgi, rgci;
int reject = 0;
@@ -1206,14 +1196,8 @@ struct detector *get_detector_geometry_2(const char *filename,
int n_rg_definitions = 0;
int n_rgc_definitions = 0;
- fh = fopen(filename, "r");
- if ( fh == NULL ) return NULL;
-
det = calloc(1, sizeof(struct detector));
- if ( det == NULL ) {
- fclose(fh);
- return NULL;
- }
+ if ( det == NULL ) return NULL;
if ( beam != NULL ) {
beam->photon_energy = 0.0;
@@ -1271,14 +1255,21 @@ struct detector *get_detector_geometry_2(const char *filename,
int n1, n2;
char **path;
- char line[1024];
+ char *line;
struct badregion *badregion = NULL;
struct panel *panel = NULL;
char wholeval[1024];
- rval = fgets(line, 1023, fh);
- if ( rval == NULL ) break;
- chomp(line);
+ const char *nl = strchr(string, '\n');
+ if ( nl != NULL ) {
+ size_t len = nl - string;
+ line = strndup(string, nl-string);
+ line[len] = '\0';
+ string += len+1;
+ } else {
+ line = strdup(string);
+ done = 1;
+ }
if ( line[0] == ';' ) continue;
@@ -1345,11 +1336,10 @@ struct detector *get_detector_geometry_2(const char *filename,
free(bits);
free(path);
- } while ( rval != NULL );
+ } while ( !done );
if ( det->n_panels == -1 ) {
ERROR("No panel descriptions in geometry file.\n");
- fclose(fh);
free(det);
return NULL;
}
@@ -1711,8 +1701,64 @@ struct detector *get_detector_geometry_2(const char *filename,
if ( reject ) return NULL;
+ return det;
+}
+
+
+char *load_entire_file(const char *filename)
+{
+ struct stat statbuf;
+ int r;
+ char *contents;
+ FILE *fh;
+
+ r = stat(filename, &statbuf);
+ if ( r != 0 ) {
+ ERROR("File '%s' not found\n", filename);
+ return NULL;
+ }
+
+ contents = malloc(statbuf.st_size+1);
+ if ( contents == NULL ) {
+ ERROR("Failed to allocate memory for file\n");
+ return NULL;
+ }
+
+ fh = fopen(filename, "r");
+ if ( fh == NULL ) {
+ ERROR("Failed to open file '%s'\n", filename);
+ free(contents);
+ return NULL;
+ }
+
+ if ( fread(contents, 1, statbuf.st_size, fh) != statbuf.st_size ) {
+ ERROR("Failed to read file '%s'\n", filename);
+ free(contents);
+ return NULL;
+ }
+ contents[statbuf.st_size] = '\0';
+
fclose(fh);
+ return contents;
+}
+
+
+struct detector *get_detector_geometry_2(const char *filename,
+ struct beam_params *beam,
+ char **hdf5_peak_path)
+{
+ char *contents;
+ struct detector *det;
+
+ contents = load_entire_file(filename);
+ if ( contents == NULL ) {
+ ERROR("Failed to load geometry file '%s'\n", filename);
+ return NULL;
+ }
+
+ det = get_detector_geometry_from_string(contents, beam, hdf5_peak_path);
+ free(contents);
return det;
}
@@ -1740,6 +1786,8 @@ struct detector *copy_geom(const struct detector *in)
struct detector *out;
int i;
+ if ( in == NULL ) return NULL;
+
out = malloc(sizeof(struct detector));
memcpy(out, in, sizeof(struct detector));
@@ -1940,7 +1988,7 @@ static void check_extents(struct panel p, double *min_x, double *min_y,
}
-static void process_panel_fields(const struct panel *p, char *line,
+static void rewrite_panel_fields(const struct panel *p, char *line,
FILE *fh, char **bits,
int write_panel_coffset)
{
@@ -1960,6 +2008,7 @@ static void process_panel_fields(const struct panel *p, char *line,
build_output_line(line, new_line,
string_to_write);
fputs(new_line, fh);
+ fputs("\n", fh);
return;
} else if ( strstr(bits[1], "ss") != NULL &&
@@ -1971,6 +2020,7 @@ static void process_panel_fields(const struct panel *p, char *line,
build_output_line(line, new_line,
string_to_write);
fputs(new_line, fh);
+ fputs("\n", fh);
return;
} else if ( strstr(bits[1], "corner_x") != NULL) {
@@ -1980,6 +2030,7 @@ static void process_panel_fields(const struct panel *p, char *line,
build_output_line(line, new_line,
string_to_write);
fputs(new_line, fh);
+ fputs("\n", fh);
return;
} else if ( strstr(bits[1], "corner_y") != NULL) {
@@ -1989,6 +2040,7 @@ static void process_panel_fields(const struct panel *p, char *line,
build_output_line(line, new_line,
string_to_write);
fputs(new_line, fh);
+ fputs("\n", fh);
return;
} else if ( strstr(bits[1], "coffset") != NULL) {
@@ -1997,11 +2049,13 @@ static void process_panel_fields(const struct panel *p, char *line,
return;
} else {
fputs(line, fh);
+ fputs("\n", fh);
return;
}
} else {
fputs(line, fh);
+ fputs("\n", fh);
}
}
@@ -2073,65 +2127,18 @@ void get_pixel_extents(struct detector *det,
}
-static char **file_to_lines(const char *fn)
-{
- char **lines;
- FILE *fh;
- int i = 0;
- int max_lines = 64;
-
- fh = fopen(fn, "r");
- if ( fh == NULL ) return NULL;
-
- lines = malloc(max_lines*sizeof(char *));
- if ( lines == NULL ) return NULL;
-
- do {
- char line[2048];
- char *rval;
- rval = fgets(line, 2048, fh);
- if ( rval == NULL ) break;
- lines[i++] = strdup(line);
-
- /* Allow one space so the terminator always fits */
- if ( i == max_lines-1 ) {
- max_lines += 64;
- lines = realloc(lines, max_lines*sizeof(char *));
- if ( lines == NULL ) return NULL;
- }
- } while ( 1 );
-
- lines[i++] = NULL;
- return lines;
-}
-
-
-static void free_lines(char **lines)
-{
- int i = 0;
- while ( lines[i] != NULL ) {
- free(lines[i++]);
- };
- free(lines);
-}
-
-
-int write_detector_geometry_2(const char *geometry_filename,
+int write_detector_geometry_3(const char *geometry_data,
const char *output_filename, struct detector *det,
const char *additional_comment,
int write_panel_coffset)
{
FILE *fh;
- char **lines;
- int lno = 0;
+ int done = 0;
- if ( geometry_filename == NULL ) return 2;
+ if ( geometry_data == NULL ) return 2;
if ( output_filename == NULL ) return 2;
if ( det->n_panels < 1 ) return 3;
- lines = file_to_lines(geometry_filename);
- if ( lines == NULL ) return 1;
-
fh = fopen(output_filename, "w");
if ( fh == NULL ) return 1;
@@ -2146,42 +2153,51 @@ int write_detector_geometry_2(const char *geometry_filename,
"end of the file\n", fh);
}
- lno = 0;
- while ( lines[lno] != NULL) {
+ do {
int n_bits;
char **bits;
int i;
struct panel *p;
+ char *line;
+ const char *nl;
+
+ /* Get the next line */
+ nl = strchr(geometry_data, '\n');
+ if ( nl != NULL ) {
+ size_t len = nl - geometry_data;
+ line = strndup(geometry_data, nl-geometry_data);
+ line[len] = '\0';
+ geometry_data += len+1;
+ } else {
+ /* Last line might now have newline at end */
+ line = strdup(geometry_data);
+ done = 1;
+ }
- n_bits = assplode(lines[lno], "/=", &bits, ASSPLODE_NONE);
+ n_bits = assplode(line, "/=", &bits, ASSPLODE_NONE);
if ( n_bits != 3 ) {
- if ( strstr(bits[0], "coffset" ) != NULL &&
- write_panel_coffset ) {
- lno++;
- continue;
- } else {
- fputs(lines[lno], fh);
- }
+ if ( write_panel_coffset && (bits != NULL)
+ && (strstr(bits[0], "coffset" ) != NULL) ) continue;
+ fputs(line, fh);
+ fputs("\n", fh);
} else {
p = find_panel_by_name(det, bits[0]);
if ( p != NULL ) {
- process_panel_fields(p, lines[lno], fh, bits,
+ rewrite_panel_fields(p, line, fh, bits,
write_panel_coffset);
-
} else {
- fputs(lines[lno], fh);
+ fputs(line, fh);
+ fputs("\n", fh);
}
}
for ( i=0; i<n_bits; i++ ) free(bits[i]);
- lno++;
-
- };
+ } while ( !done );
if ( write_panel_coffset ) {
@@ -2195,13 +2211,24 @@ int write_detector_geometry_2(const char *geometry_filename,
}
}
- fclose(fh);
- free_lines(lines);
-
return 0;
}
+int write_detector_geometry_2(const char *geometry_filename,
+ const char *output_filename, struct detector *det,
+ const char *additional_comment,
+ int write_panel_coffset)
+{
+ int r;
+ char *geometry_data = load_entire_file(geometry_filename);
+ r = write_detector_geometry_3(geometry_data, output_filename, det,
+ additional_comment, write_panel_coffset);
+ free(geometry_data);
+ return r;
+}
+
+
int write_detector_geometry(const char *geometry_filename,
const char *output_filename, struct detector *det)
{
@@ -2211,13 +2238,12 @@ int write_detector_geometry(const char *geometry_filename,
/**
- * mark_resolution_range_as_bad:
- * @image: An image structure
- * @min: Minimum value of 1/d to be marked as bad
- * @max: Maximum value of 1/d to be marked as bad
+ * \param image An image structure
+ * \param min Minimum value of 1/d to be marked as bad
+ * \param max Maximum value of 1/d to be marked as bad
*
- * Flags, in the bad pixel mask for @image, every pixel whose resolution is
- * between @min and @max.
+ * Flags, in the bad pixel mask for \p image, every pixel whose resolution is
+ * between \p min and \p max.
*
*/
@@ -2264,14 +2290,12 @@ static int safe_strcmp(const char *a, const char *b)
/**
- * single_panel_data_source:
- * @det: A detector structure
- * @element: If manually selected by the user, the HDF5 element being used.
+ * \param det A detector structure
+ * \param element If manually selected by the user, the HDF5 element being used.
* Otherwise NULL.
*
- * Returns: non-zero if the combination of @det and @element mean that all the
+ * \returns Non-zero if the combination of \p det and \p element mean that all the
* data comes from a single block.
- *
*/
int single_panel_data_source(struct detector *det, const char *element)
{
diff --git a/libcrystfel/src/detector.h b/libcrystfel/src/detector.h
index a7a4a01f..bbdeeaed 100644
--- a/libcrystfel/src/detector.h
+++ b/libcrystfel/src/detector.h
@@ -3,12 +3,12 @@
*
* Detector properties
*
- * Copyright © 2012-2017 Deutsches Elektronen-Synchrotron DESY,
+ * Copyright © 2012-2019 Deutsches Elektronen-Synchrotron DESY,
* a research centre of the Helmholtz Association.
* Copyright © 2012 Richard Kirian
*
* Authors:
- * 2009-2017 Thomas White <taw@physics.org>
+ * 2009-2019 Thomas White <taw@physics.org>
* 2011-2012 Richard Kirian <rkirian@asu.edu>
* 2014 Valerio Mariani
* 2011 Andrew Aquila
@@ -53,6 +53,11 @@ struct event;
extern "C" {
#endif
+/**
+ * \file detector.h
+ * Detector geometry structure and related functions.
+ */
+
struct rigid_group
{
@@ -80,97 +85,110 @@ struct rg_collection
/**
- * panel:
- * @name: Text name for the panel (fixed length array)
- * @cnx: Location of corner, in pixels, x coordinate
- * @cny: Location of corner, in pixels, y coordinate
- * @coffset: The offset to be applied from @clen (which may come from elsewhere)
- * @clen: The distance from the interaction point to the corner of the first pixel
- * @clen_from: Location to get @clen from, e.g. from HDF5 file
- * @mask: Location of mask data
- * @mask_file: Filename for mask data
- * @satmap: Location of per-pixel saturation map
- * @satmap_file: Filename for saturation map
- * @res: Resolution of panel in pixels per metre
- * @badrow: Readout direction (for filtering out clusters of peaks)
- * @no_index: Non-zero if panel is entirely "bad"
- * @adu_per_photon: Number of detector intensity units per photon
- * @adu_per_eV: Number of detector intensity units per eV of photon energy
- * @max_adu: Saturation value
- * @dim_structure: Dimension structure
- * @fsx: Real-space x-direction of data fast-scan direction
- * @fsy: Real-space y-direction of data fast-scan direction
- * @fsz: Real-space z-direction of data fast-scan direction
- * @ssx: Real-space x-direction of data slow-scan direction
- * @ssy: Real-space y-direction of data slow-scan direction
- * @ssz: Real-space z-direction of data slow-scan direction
- * @rail_x: x direction of camera length "rail"
- * @rail_y: y direction of camera length "rail"
- * @rail_z: z direction of camera length "rail"
- * @clen_for_centering: Value of clen (without coffset) at which beam is centered
- * @xfs: Data fast-scan direction of real-space x-direction
- * @yfs: Data fast-scan direction of real-space y-direction
- * @xss: Data slow-scan direction of real-space x-direction
- * @yss: Data slow-scan direction of real-space y-direction
- * @orig_min_fs: Minimum fs coordinate of data in file
- * @orig_max_fs: Maximum fs coordinate of data in file
- * @orig_min_ss: Minimum ss coordinate of data in file (inclusive)
- * @orig_max_ss: Maximum ss coordinate of data in file (inclusive)
- * @data: Location of data in file
- * @w: Width of panel
- * @h: Height of panel
+ * Represents one panel of a detector
*/
struct panel
{
- char name[1024]; /* Name for this panel */
+ /** Text name for panel (fixed length array) */
+ char name[1024];
- double cnx; /* Location of corner (min_fs,min_ss) in pixels */
+ /** \name Location of corner in units of the pixel size of this panel */
+ /**@{*/
+ double cnx;
double cny;
+ /**@}*/
+
+ /** The offset to be applied from \ref clen */
double coffset;
- double clen; /* Camera length in metres */
+
+ /** The distance from the interaction point to the corner of the
+ * first pixel */
+ double clen;
+
+ /** Location to get \ref clen from, e.g. from HDF5 file */
char *clen_from;
+
+ /** Location of mask data */
char *mask;
+
+ /** Filename for mask data */
char *mask_file;
+
+ /** Location of per-pixel saturation map */
char *satmap;
+
+ /** Filename for saturation map */
char *satmap_file;
- double res; /* Resolution in pixels per metre */
- char badrow; /* 'x' or 'y' */
- int no_index; /* Don't index peaks in this panel if non-zero */
- double adu_per_photon; /* Number of ADU per photon */
- double max_adu; /* Treat pixel as unreliable if higher than this */
+
+ /** Resolution in pixels per metre */
+ double res;
+
+ /** Readout direction (for filtering out clusters of peaks)
+ * ('x' or 'y') */
+ char badrow;
+
+ /** Non-zero if panel should be considered entirely bad */
+ int no_index;
+
+ /** Number of detector intensity units per photon */
+ double adu_per_photon;
+
+ /** Treat pixel as unreliable if higher than this */
+ double max_adu;
+
+ /** Location of data in file */
char *data;
- double adu_per_eV; /* Number of ADU per eV */
+ /** Number of detector intensity units per eV of photon energy */
+ double adu_per_eV;
+ /** Dimension structure */
struct dim_structure *dim_structure;
+ /** \name Transformation matrix from pixel coordinates to lab frame */
+ /*@{*/
double fsx;
double fsy;
double fsz;
double ssx;
double ssy;
double ssz;
+ /*@}*/
+ /** \name Rail direction */
+ /*@{*/
double rail_x;
double rail_y;
double rail_z;
+ /*@}*/
+
+ /* Value of clen (without coffset) at which beam is centered */
double clen_for_centering;
+ /** \name Inverse of 2D part of transformation matrix */
+ /*@{*/
double xfs;
double yfs;
double xss;
double yss;
+ /*@}*/
- /* Position of the panel in the data block in the file. The panels may
- * get moved around when the file is loaded (see hdf5_read2()),
- * especially if the panels come from different HDF5 elements. */
+ /** \name Position of the panel in the data block in the file.
+ * The panels may get moved around when the file is loaded (see
+ * hdf5_read2()), especially if the panels come from different HDF5
+ * elements. */
+ /*@{*/
int orig_min_fs;
int orig_max_fs;
int orig_min_ss;
int orig_max_ss;
+ /*@}*/
+
+ /** Width, calculated as max_fs-min_fs+1 */
+ int w;
- int w; /* Width, calculated as max_fs-min_fs+1 */
- int h; /* Height, calculated as max_ss-min_ss+1 */
+ /*** Height, calculated as max_ss-min_ss+1 */
+ int h;
};
@@ -254,7 +272,11 @@ extern struct detector *get_detector_geometry(const char *filename,
extern struct detector *get_detector_geometry_2(const char *filename,
struct beam_params *beam,
- char **hdf5_peak_path);
+ char **hdf5_peak_path);
+
+extern struct detector *get_detector_geometry_from_string(const char *string,
+ struct beam_params *beam,
+ char **hdf5_peak_path);
extern void free_detector_geometry(struct detector *det);
@@ -290,6 +312,12 @@ extern int write_detector_geometry_2(const char *geometry_filename,
const char *additional_comment,
int write_panel_coffset);
+extern int write_detector_geometry_3(const char *geometry_data,
+ const char *output_filename,
+ struct detector *det,
+ const char *additional_comment,
+ int write_panel_coffset);
+
extern int write_detector_geometry(const char *geometry_filename,
const char *output_filename,
struct detector *det);
diff --git a/libcrystfel/src/dirax.c b/libcrystfel/src/dirax.c
index 53090c7c..3e02d5b5 100644
--- a/libcrystfel/src/dirax.c
+++ b/libcrystfel/src/dirax.c
@@ -56,6 +56,8 @@
#include "cell-utils.h"
+/** \file dirax.h */
+
#define DIRAX_VERBOSE 0
#define MAX_DIRAX_CELL_CANDIDATES (5)
diff --git a/libcrystfel/src/dirax.h b/libcrystfel/src/dirax.h
index dce27c57..e3b03565 100644
--- a/libcrystfel/src/dirax.h
+++ b/libcrystfel/src/dirax.h
@@ -39,6 +39,9 @@
extern "C" {
#endif
+/** \file dirax.h
+ * DirAx indexer interface
+ */
extern int run_dirax(struct image *image, void *ipriv);
extern void *dirax_prepare(IndexingMethod *indm, UnitCell *cell);
diff --git a/libcrystfel/src/events.c b/libcrystfel/src/events.c
index 7cd05784..b1cd5d11 100644
--- a/libcrystfel/src/events.c
+++ b/libcrystfel/src/events.c
@@ -35,6 +35,7 @@
#include <stdlib.h>
#include <assert.h>
+/** \file events.h */
struct event *initialize_event()
{
@@ -229,11 +230,10 @@ static int events_equal(struct event *ev1, struct event *ev2)
/**
- * find_event:
- * @ev: An event structure
- * @el: An event list
+ * \param ev: An event structure
+ * \param el: An event list
*
- * Returns: the indexing into @el of the event matching @ev, of el->num_events
+ * \returns The indexing into \p el of the event matching \p ev, of el->num_events
* if no such event is found.
**/
int find_event(struct event *ev, struct event_list *el)
diff --git a/libcrystfel/src/events.h b/libcrystfel/src/events.h
index d7fae24a..6e9204b3 100644
--- a/libcrystfel/src/events.h
+++ b/libcrystfel/src/events.h
@@ -33,6 +33,11 @@
#ifndef EVENTS_H
#define EVENTS_H
+/**
+ * \file events.h
+ * Event description structures
+ */
+
struct event
{
char **path_entries;
@@ -53,7 +58,7 @@ struct filename_plus_event
struct event *ev;
};
-enum
+enum dimension_id
{
HYSL_UNDEFINED = -99,
HYSL_PLACEHOLDER = -98,
diff --git a/libcrystfel/src/felix.c b/libcrystfel/src/felix.c
index b0cb605d..ea27093a 100644
--- a/libcrystfel/src/felix.c
+++ b/libcrystfel/src/felix.c
@@ -32,6 +32,8 @@
#endif
+/** \file felix.h */
+
#include <stdlib.h>
#include <stdio.h>
#include <math.h>
diff --git a/libcrystfel/src/felix.h b/libcrystfel/src/felix.h
index b1776293..645adaeb 100644
--- a/libcrystfel/src/felix.h
+++ b/libcrystfel/src/felix.h
@@ -36,6 +36,11 @@
#include "cell.h"
+/**
+ * \file felix.h
+ * Felix indexer interface
+ */
+
struct felix_options
{
double ttmin; /* radians */
diff --git a/libcrystfel/src/filters.c b/libcrystfel/src/filters.c
index cbaf6001..2f802dd3 100644
--- a/libcrystfel/src/filters.c
+++ b/libcrystfel/src/filters.c
@@ -41,6 +41,7 @@
#include "image.h"
+/** \file filters.h */
static void filter_noise_in_panel(float *data, int width, int height)
{
diff --git a/libcrystfel/src/filters.h b/libcrystfel/src/filters.h
index 1d4f7bf0..5738726e 100644
--- a/libcrystfel/src/filters.h
+++ b/libcrystfel/src/filters.h
@@ -37,6 +37,10 @@
extern "C" {
#endif
+/**
+ * \file filters.h
+ * Image noise filtering functions
+ */
extern void filter_cm(struct image *image);
extern void filter_noise(struct image *image);
extern void filter_median(struct image *image, int size);
diff --git a/libcrystfel/src/geometry.c b/libcrystfel/src/geometry.c
index 9ae6d96f..e5f33d64 100644
--- a/libcrystfel/src/geometry.c
+++ b/libcrystfel/src/geometry.c
@@ -47,6 +47,7 @@
#include "symmetry.h"
#include "geometry.h"
+/** \file geometry.h */
static int locate_peak_on_panel(double x, double y, double z, double k,
struct panel *p,
@@ -380,6 +381,15 @@ double r_gradient(UnitCell *cell, int k, Reflection *refl, struct image *image)
}
+/**
+ * \param cryst: A \ref Crystal
+ * \param max_res: Maximum resolution to predict to (m^-1)
+ *
+ * Calculates reflection positions for \p crys, up to maximum 1/d value
+ * \p max_res
+ *
+ * \returns A list of predicted reflections
+ */
RefList *predict_to_res(Crystal *cryst, double max_res)
{
double ax, ay, az;
@@ -512,7 +522,7 @@ static void set_random_partialities(Crystal *cryst)
static double do_integral(double q2, double zl, double R,
- double lambda, double sig, char *verbose)
+ Spectrum *spectrum, char *verbose)
{
int i;
double kmin, kmax, kstart, kfinis;
@@ -520,7 +530,6 @@ static double do_integral(double q2, double zl, double R,
double total = 0.0;
double k0, k1;
const int SAMPLES = 50; /* Number of samples for integration */
- const double N = 1.5; /* Pointiness of spectrum */
FILE *fh = NULL;
assert(R*R < q2);
@@ -535,8 +544,7 @@ static double do_integral(double q2, double zl, double R,
if ( k1 < 0.0 ) k1 = +INFINITY;
/* Range over which E is significantly different from zero */
- kmin = 1.0 / (lambda + 3.0*sig);
- kmax = 1.0 / (lambda - 3.0*sig);
+ spectrum_get_range(spectrum, &kmin, &kmax);
/* Calculate range over which E*P is different from zero */
if ( k0 < k1 ) {
@@ -582,9 +590,6 @@ static double do_integral(double q2, double zl, double R,
snprintf(fn, 63, "partial%s.graph", verbose);
fh = fopen(fn, "w");
fprintf(fh, " n p wavelength E P\n");
- STATUS("Nominal k = %e m^-1\n", 1.0/lambda);
- STATUS(" (wavelength %e m)\n", lambda);
- STATUS("Bandwidth %e m\n", sig);
STATUS("k1/2 = %e m^-1\n", -q2/(2.0*zl));
STATUS(" (wavelength %e m)\n", 1.0/(-q2/(2.0*zl)));
STATUS("Reflection k goes from %e to %e m^-1\n", k1, k0);
@@ -597,7 +602,7 @@ static double do_integral(double q2, double zl, double R,
for ( i=0; i<SAMPLES; i++ ) {
- double p, kp, lrel;
+ double p, kp;
double E, P;
kp = kstart + i*inc;
@@ -605,9 +610,7 @@ static double do_integral(double q2, double zl, double R,
p = pref + 0.5 - kp/(2.0*R);
/* Spectral energy term */
- lrel = fabs(1.0/kp - lambda);
- E = exp(-0.5 * pow(lrel / sig, N));
- E /= sqrt(2.0 * M_PI * sig);
+ E = spectrum_get_density_at_k(spectrum, kp);
/* RLP profile term */
P = 4.0*p * (1.0 - p);
@@ -623,9 +626,6 @@ static double do_integral(double q2, double zl, double R,
if ( isnan(total) ) {
ERROR("NaN total!\n");
- STATUS("Nominal k = %e m^-1\n", 1.0/lambda);
- STATUS(" (wavelength %e m)\n", lambda);
- STATUS("Bandwidth %e m\n", sig);
STATUS("k1/2 = %e m^-1\n", -q2/(2.0*zl));
STATUS(" (wavelength %e m)\n", 1.0/(-q2/(2.0*zl)));
STATUS("Reflection k goes from %e to %e m^-1\n", k1, k0);
@@ -645,7 +645,7 @@ static void ginn_spectrum_partialities(Crystal *cryst)
RefList *list;
Reflection *refl;
RefListIterator *iter;
- double r0, m, lambda, sig;
+ double r0, m;
struct image *image;
UnitCell *cell;
double asx, asy, asz, bsx, bsy, bsz, csx, csy, csz;
@@ -673,8 +673,6 @@ static void ginn_spectrum_partialities(Crystal *cryst)
r0 = fabs(crystal_get_profile_radius(cryst));
m = crystal_get_mosaicity(cryst);
- lambda = image->lambda;
- sig = image->bw * lambda / 2.0;
for ( refl = first_refl(crystal_get_reflections(cryst), &iter);
refl != NULL;
@@ -700,8 +698,9 @@ static void ginn_spectrum_partialities(Crystal *cryst)
//snprintf(tmp, 255, "-%i,%i,%i", h, k, l);
char *tmp = NULL;
- total = do_integral(q2, zl, R, lambda, sig, tmp);
- norm = do_integral(q2, -0.5*q2*lambda, R, lambda, sig, NULL);
+ total = do_integral(q2, zl, R, image->spectrum, tmp);
+ norm = do_integral(q2, -0.5*q2*image->lambda, R,
+ image->spectrum, NULL);
set_partiality(refl, total/norm);
set_lorentz(refl, 1.0);
@@ -712,17 +711,77 @@ static void ginn_spectrum_partialities(Crystal *cryst)
}
+static void ewald_offset_partialities(Crystal *cryst)
+{
+ RefList *list;
+ Reflection *refl;
+ RefListIterator *iter;
+ double r0, m;
+ struct image *image;
+ UnitCell *cell;
+ double asx, asy, asz, bsx, bsy, bsz, csx, csy, csz;
+
+ list = crystal_get_reflections(cryst);
+ if ( list == NULL ) {
+ ERROR("No reflections for partiality calculation!\n");
+ return;
+ }
+
+ image = crystal_get_image(cryst);
+ if ( image == NULL ) {
+ ERROR("No image for partiality calculation!\n");
+ return;
+ }
+
+ cell = crystal_get_cell(cryst);
+ if ( cell == NULL ) {
+ ERROR("No unit cell for partiality calculation!\n");
+ return;
+ }
+ cell_get_reciprocal(cell, &asx, &asy, &asz,
+ &bsx, &bsy, &bsz,
+ &csx, &csy, &csz);
+
+ r0 = fabs(crystal_get_profile_radius(cryst));
+ m = crystal_get_mosaicity(cryst);
+
+ for ( refl = first_refl(crystal_get_reflections(cryst), &iter);
+ refl != NULL;
+ refl = next_refl(refl, iter) )
+ {
+ signed int h, k, l;
+ double xl, yl, zl;
+ double q2, R, t;
+
+ get_symmetric_indices(refl, &h, &k, &l);
+ xl = h*asx + k*bsx + l*csx;
+ yl = h*asy + k*bsy + l*csy;
+ zl = h*asz + k*bsz + l*csz;
+
+ /* Radius of rlp profile */
+ q2 = xl*xl + yl*yl + zl*zl;
+ R = r0 + m * sqrt(q2);
+
+ /* Excitation error */
+ t = get_exerr(refl);
+
+ set_partiality(refl, exp(-(t*t)/(R*R)));
+ set_lorentz(refl, 1.0);
+
+ }
+}
+
+
/**
- * calculate_partialities:
- * @cryst: A %Crystal
- * @pmodel: A %PartialityModel
+ * \param cryst A \ref Crystal
+ * \param pmodel A \ref PartialityModel
*
- * Calculates the partialities for the reflections in @cryst, given the current
+ * Calculates the partialities for the reflections in \p cryst, given the current
* crystal and image parameters. The crystal's image and reflection lists
- * must be set. The specified %PartialityModel will be used.
+ * must be set. The specified \ref PartialityModel will be used.
*
* You must not have changed the crystal or image parameters since you last
- * called predict_to_res() or update_predictions(), because this function
+ * called \ref predict_to_res or \ref update_predictions, because this function
* relies on the limiting wavelength values calculated by those functions.
*/
void calculate_partialities(Crystal *cryst, PartialityModel pmodel)
@@ -737,6 +796,10 @@ void calculate_partialities(Crystal *cryst, PartialityModel pmodel)
ginn_spectrum_partialities(cryst);
break;
+ case PMODEL_OFFSET :
+ ewald_offset_partialities(cryst);
+ break;
+
case PMODEL_RANDOM :
set_random_partialities(cryst);
break;
@@ -750,15 +813,14 @@ void calculate_partialities(Crystal *cryst, PartialityModel pmodel)
/**
- * update_predictions:
- * @cryst: A %Crystal
+ * \param cryst A \ref Crystal
*
* Updates the predicted reflections (positions and excitation errors, but not
- * the actual partialities) of @cryst's reflections according to
+ * the actual partialities) of \p cryst's reflections according to
* the current state of the crystal (e.g. its unit cell parameters).
*
- * If you need to update the partialities as well, call calculate_partialities()
- * afterwards.
+ * If you need to update the partialities as well, call
+ * \ref calculate_partialities afterwards.
*/
void update_predictions(Crystal *cryst)
{
diff --git a/libcrystfel/src/geometry.h b/libcrystfel/src/geometry.h
index 529f112e..e6e862a5 100644
--- a/libcrystfel/src/geometry.h
+++ b/libcrystfel/src/geometry.h
@@ -45,24 +45,27 @@ extern "C" {
#endif
/**
- * PartialityModel:
- * @PMODEL_UNITY : Set all all partialities and Lorentz factors to 1.
- * @PMODEL_XSPHERE : Flat sphere model with super-Gaussian spectrum
- * @PMODEL_RANDOM : Randomly assigned partialities
+ * \file geometry.h
+ * Geometry of diffraction
*
- * A %PartialityModel describes a geometrical model which can be used to
+ * This contains the prediction and partiality calculation functions.
+ */
+
+/**
+ * A PartialityModel describes a geometrical model which can be used to
* calculate spot partialities and Lorentz correction factors.
**/
typedef enum {
- PMODEL_UNITY,
- PMODEL_XSPHERE,
- PMODEL_RANDOM,
+ PMODEL_UNITY, /**< Set all partialities and Lorentz factors to 1. */
+ PMODEL_XSPHERE, /**< Flat sphere model with super-Gaussian spectrum */
+ PMODEL_OFFSET, /**< Ewald offset model for monochromatic beam */
+ PMODEL_RANDOM, /**< Randomly assigned partialities */
} PartialityModel;
-/* Enumeration of parameters which may want to be refined */
+/** Enumeration of parameters which may want to be refined */
enum gparam {
GPARAM_ASX,
GPARAM_ASY,
diff --git a/libcrystfel/src/hdf5-file.c b/libcrystfel/src/hdf5-file.c
index 9c8038ea..8451c68a 100644
--- a/libcrystfel/src/hdf5-file.c
+++ b/libcrystfel/src/hdf5-file.c
@@ -43,19 +43,7 @@
#include "hdf5-file.h"
#include "utils.h"
-
-/**
- * SECTION:hdf5-file
- * @short_description: HDF5 file handling
- * @title: HDF5 file handling
- * @section_id:
- * @see_also:
- * @include: "hdf5-file.h"
- * @Image:
- *
- * Routines for accessing HDF5 files.
- **/
-
+/** \file hdf5-file.h */
struct hdf5_write_location {
@@ -333,19 +321,18 @@ static float *read_hdf5_data(struct hdfile *f, char *path, int line)
/**
- * get_peaks_cxi_2:
- * @image: An %image structure
- * @f: An %hdfile structure
- * @p: The HDF5 path to the peak data
- * @fpe: A %filename_plus_event structure specifying the event
- * @half_pixel_shift: Non-zero if 0.5 should be added to all peak coordinates
+ * \param image: An \ref image structure
+ * \param f: An \ref hdfile structure
+ * \param p: The HDF5 path to the peak data
+ * \param fpe: A \ref filename_plus_event structure specifying the event
+ * \param half_pixel_shift: Non-zero if 0.5 should be added to all peak coordinates
*
* Get peaks from HDF5, in "CXI format" (as in "CXIDB"). The data should be in
- * a set of arrays under @p. The number of peaks should be in a 1D array at
- * @p/nPeaks. The fast-scan and slow-scan coordinates should be in 2D arrays at
- * @p/peakXPosRaw and @p/peakYPosRaw respectively (sorry about the naming). The
+ * a set of arrays under \p p. The number of peaks should be in a 1D array at
+ * \p p/nPeaks. The fast-scan and slow-scan coordinates should be in 2D arrays at
+ * \p p/peakXPosRaw and \p p/peakYPosRaw respectively (sorry about the naming). The
* first dimension of these arrays should be the event number (as given by
- * @fpe). The intensities are expected to be at @p/peakTotalIntensity in a
+ * \p fpe). The intensities are expected to be at \p p/peakTotalIntensity in a
* similar 2D array.
*
* CrystFEL considers all peak locations to be distances from the corner of the
@@ -353,9 +340,9 @@ static float *read_hdf5_data(struct hdfile *f, char *path, int line)
* geometry (see 'man crystfel_geometry'). The software which generates the
* CXI files, including Cheetah, may instead consider the peak locations to be
* pixel indices in the data array. In this case, the peak coordinates should
- * have 0.5 added to them. This will be done if @half_pixel_shift is non-zero.
+ * have 0.5 added to them. This will be done if \p half_pixel_shift is non-zero.
*
- * Returns: non-zero on error, zero otherwise.
+ * \returns Non-zero on error, zero otherwise.
*
*/
int get_peaks_cxi_2(struct image *image, struct hdfile *f, const char *p,
@@ -409,8 +396,6 @@ int get_peaks_cxi_2(struct image *image, struct hdfile *f, const char *p,
}
image->features = image_feature_list_new();
- image->num_peaks = 0;
- image->num_saturated_peaks = 0;
for ( pk=0; pk<num_peaks; pk++ ) {
float fs, ss, val;
@@ -429,7 +414,6 @@ int get_peaks_cxi_2(struct image *image, struct hdfile *f, const char *p,
ss = ss - p->orig_min_ss;
image_add_feature(image->features, fs, ss, p, image, val, NULL);
- image->num_peaks++;
}
@@ -438,18 +422,17 @@ int get_peaks_cxi_2(struct image *image, struct hdfile *f, const char *p,
/**
- * get_peaks_cxi:
- * @image: An %image structure
- * @f: An %hdfile structure
- * @p: The HDF5 path to the peak data
- * @fpe: A %filename_plus_event structure specifying the event
+ * \param image: An \ref image structure
+ * \param f: An \ref hdfile structure
+ * \param p: The HDF5 path to the peak data
+ * \param fpe: A \ref filename_plus_event structure specifying the event
*
* This is a wrapper function to preserve API compatibility with older CrystFEL
- * versions. Use get_peaks_cxi_2() instead.
+ * versions. Use \ref get_peaks_cxi_2 instead.
*
- * This function is equivalent to get_peaks_cxi_2(@image, @f, @p, @fpe, 1).
+ * This function is equivalent to get_peaks_cxi_2(\p image, \p f, \p p, \p fpe, 1).
*
- * Returns: non-zero on error, zero otherwise.
+ * \returns Non-zero on error, zero otherwise.
*
*/
int get_peaks_cxi(struct image *image, struct hdfile *f, const char *p,
@@ -460,13 +443,12 @@ int get_peaks_cxi(struct image *image, struct hdfile *f, const char *p,
/**
- * get_peaks_2:
- * @image: An %image structure
- * @f: An %hdfile structure
- * @p: The HDF5 path to the peak data
- * @half_pixel_shift: Non-zero if 0.5 should be added to all peak coordinates
+ * \param image: An \ref image structure
+ * \param f: An \ref hdfile structure
+ * \param p: The HDF5 path to the peak data
+ * \param half_pixel_shift: Non-zero if 0.5 should be added to all peak coordinates
*
- * Get peaks from HDF5. The peak list should be located at @p in the HDF5 file,
+ * Get peaks from HDF5. The peak list should be located at \p p in the HDF5 file,
* a 2D array where the first dimension equals the number of peaks and second
* dimension is three. The first two columns contain the fast scan and slow
* scan coordinates, respectively, of the peaks. The third column contains the
@@ -477,9 +459,9 @@ int get_peaks_cxi(struct image *image, struct hdfile *f, const char *p,
* geometry (see 'man crystfel_geometry'). The software which generates the
* CXI files, including Cheetah, may instead consider the peak locations to be
* pixel indices in the data array. In this case, the peak coordinates should
- * have 0.5 added to them. This will be done if @half_pixel_shift is non-zero.
+ * have 0.5 added to them. This will be done if \p half_pixel_shift is non-zero.
*
- * Returns: non-zero on error, zero otherwise.
+ * \returns Non-zero on error, zero otherwise.
*
*/
int get_peaks_2(struct image *image, struct hdfile *f, const char *p,
@@ -557,8 +539,6 @@ int get_peaks_2(struct image *image, struct hdfile *f, const char *p,
}
image->features = image_feature_list_new();
- image->num_peaks = 0;
- image->num_saturated_peaks = 0;
for ( i=0; i<size[0]; i++ ) {
float fs, ss, val;
@@ -578,7 +558,6 @@ int get_peaks_2(struct image *image, struct hdfile *f, const char *p,
image_add_feature(image->features, fs, ss, p, image, val,
NULL);
- image->num_peaks++;
}
@@ -592,17 +571,16 @@ int get_peaks_2(struct image *image, struct hdfile *f, const char *p,
/**
- * get_peaks:
- * @image: An %image structure
- * @f: An %hdfile structure
- * @p: The HDF5 path to the peak data
+ * \param image: An \ref image structure
+ * \param f: An \ref hdfile structure
+ * \param p: The HDF5 path to the peak data
*
* This is a wrapper function to preserve API compatibility with older CrystFEL
- * versions. Use get_peaks_2() instead.
+ * versions. Use \ref get_peaks_2 instead.
*
- * This function is equivalent to get_peaks_2(@image, @f, @p, 1).
+ * This function is equivalent to \ref get_peaks_2(\p image, \p f, \p p, 1).
*
- * Returns: non-zero on error, zero otherwise.
+ * \returns Non-zero on error, zero otherwise.
*
*/
int get_peaks(struct image *image, struct hdfile *f, const char *p)
@@ -923,29 +901,32 @@ static void write_photon_energy(hid_t fh, double eV, const char *ph_en_loc)
}
-static void write_spectrum(hid_t fh, struct sample *spectrum, int spectrum_size,
- int nsamples)
+static void write_spectrum(hid_t fh, Spectrum *s)
{
herr_t r;
double *arr;
int i;
- hsize_t size1d[1];
hid_t sh, dh, ph;
+ double kmin, kmax, step;
+ const hsize_t n = 1024;
ph = H5Pcreate(H5P_LINK_CREATE);
H5Pset_create_intermediate_group(ph, 1);
- arr = malloc(spectrum_size*sizeof(double));
+ arr = malloc(n*sizeof(double));
if ( arr == NULL ) {
ERROR("Failed to allocate memory for spectrum.\n");
return;
}
- for ( i=0; i<spectrum_size; i++ ) {
- arr[i] = 1.0e10/spectrum[i].k;
+
+ /* Save the wavelength values */
+ spectrum_get_range(s, &kmin, &kmax);
+ step = (kmax-kmin)/n;
+ for ( i=0; i<n; i++ ) {
+ arr[i] = 1.0e10/(kmin+i*step);
}
- size1d[0] = spectrum_size;
- sh = H5Screate_simple(1, size1d, NULL);
+ sh = H5Screate_simple(1, &n, NULL);
dh = H5Dcreate2(fh, "/spectrum/wavelengths_A", H5T_NATIVE_DOUBLE,
sh, ph, H5S_ALL, H5P_DEFAULT);
@@ -961,46 +942,27 @@ static void write_spectrum(hid_t fh, struct sample *spectrum, int spectrum_size,
}
H5Dclose(dh);
- for ( i=0; i<spectrum_size; i++ ) {
- arr[i] = spectrum[i].weight;
+ /* Save the probability density values */
+ for ( i=0; i<n; i++ ) {
+ arr[i] = spectrum_get_density_at_k(s, kmin+i*step);
}
- dh = H5Dcreate2(fh, "/spectrum/weights", H5T_NATIVE_DOUBLE, sh,
+ dh = H5Dcreate2(fh, "/spectrum/pdf", H5T_NATIVE_DOUBLE, sh,
H5P_DEFAULT, H5S_ALL, H5P_DEFAULT);
if ( dh < 0 ) {
- ERROR("Failed to create dataset for spectrum weights.\n");
+ ERROR("Failed to create dataset for spectrum p.d.f.\n");
return;
}
r = H5Dwrite(dh, H5T_NATIVE_DOUBLE, H5S_ALL,
H5S_ALL, H5P_DEFAULT, arr);
if ( r < 0 ) {
- ERROR("Failed to write spectrum weights.\n");
- return;
- }
-
- H5Dclose(dh);
- free(arr);
-
- size1d[0] = 1;
- sh = H5Screate_simple(1, size1d, NULL);
-
- dh = H5Dcreate2(fh, "/spectrum/number_of_samples", H5T_NATIVE_INT, sh,
- ph, H5S_ALL, H5P_DEFAULT);
- if ( dh < 0 ) {
- ERROR("Failed to create dataset for number of spectrum "
- "samples.\n");
- return;
- }
-
- r = H5Dwrite(dh, H5T_NATIVE_INT, H5S_ALL,
- H5S_ALL, H5P_DEFAULT, &nsamples);
- if ( r < 0 ) {
- ERROR("Failed to write number of spectrum samples.\n");
+ ERROR("Failed to write spectrum p.d.f.\n");
return;
}
H5Dclose(dh);
H5Pclose(ph);
+ free(arr);
}
@@ -1047,10 +1009,8 @@ int hdf5_write_image(const char *filename, const struct image *image,
write_photon_energy(fh, ph_lambda_to_eV(image->lambda), ph_en_loc);
- if ( image->spectrum0 != NULL && image->spectrum_size > 0 ) {
-
- write_spectrum(fh, image->spectrum0, image->spectrum_size,
- image->nsamples);
+ if ( image->spectrum != NULL ) {
+ write_spectrum(fh, image->spectrum);
}
H5Fclose(fh);
@@ -1149,7 +1109,8 @@ static void debodge_saturation(struct hdfile *f, struct image *image)
}
-static int *make_badmask(int *flags, struct panel *p, struct detector *det)
+static int *make_badmask(int *flags, struct detector *det, float *data,
+ struct panel *p)
{
int *badmap;
int fs, ss;
@@ -1184,6 +1145,7 @@ static int *make_badmask(int *flags, struct panel *p, struct detector *det)
int f = flags[fs+p->w*ss];
int bad = badmap[fs+p->w*ss];
+ float val = data[fs+p->w*ss];
/* Bad if it's missing any of the "good" bits */
if ( (f & det->mask_good) != det->mask_good ) bad = 1;
@@ -1191,6 +1153,9 @@ static int *make_badmask(int *flags, struct panel *p, struct detector *det)
/* Bad if it has any of the "bad" bits. */
if ( f & det->mask_bad ) bad = 1;
+ /* Bad if pixel value is NaN or inf */
+ if ( isnan(val) || isinf(val) ) bad = 1;
+
badmap[fs+p->w*ss] = bad;
}
@@ -1201,53 +1166,8 @@ static int *make_badmask(int *flags, struct panel *p, 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) && (class != H5T_INTEGER) ) {
- ERROR("Not a floating point or integer 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;
- }
-
- H5Tclose(type);
- H5Dclose(dh);
-
- return 0;
-}
-
-
-static int get_ev_based_value(struct hdfile *f, const char *name,
- struct event *ev, void *val, hid_t memtype)
+int hdfile_get_value(struct hdfile *f, const char *name, struct event *ev,
+ void *val, hid_t memtype)
{
hid_t dh;
hid_t type;
@@ -1259,7 +1179,7 @@ static int get_ev_based_value(struct hdfile *f, const char *name,
hsize_t m_offset[1];
hsize_t m_count[1];
hsize_t msdims[1];
- hsize_t size[3];
+ hsize_t size[64];
herr_t r;
herr_t check;
int check_pe;
@@ -1268,7 +1188,7 @@ static int get_ev_based_value(struct hdfile *f, const char *name,
int i;
char *subst_name = NULL;
- if ( ev->path_length != 0 ) {
+ if ( (ev != NULL) && (ev->path_length != 0) ) {
subst_name = retrieve_full_path(ev, name);
} else {
subst_name = strdup(name);
@@ -1295,7 +1215,8 @@ static int get_ev_based_value(struct hdfile *f, const char *name,
* arrays with all dimensions 1, as well as zero-d arrays. */
sh = H5Dget_space(dh);
ndims = H5Sget_simple_extent_ndims(sh);
- if ( ndims > 3 ) {
+ if ( ndims > 64 ) {
+ ERROR("Too many dimensions for hdfile_get_value\n");
H5Tclose(type);
H5Dclose(dh);
return 1;
@@ -1307,29 +1228,26 @@ static int get_ev_based_value(struct hdfile *f, const char *name,
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 */
+ /* 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 ( size[i] == 1 ) continue;
+ if ( ( i==0 ) && (ev != NULL) && (size[i] > ev->dim_entries[0]) ) {
+ dim_flag = 1;
+ } else {
+ H5Tclose(type);
+ H5Dclose(dh);
+ return 1;
}
}
- if ( dim_flag == 0 ) {
+ if ( dim_flag == 0 ) {
- r = H5Dread(dh, memtype, H5S_ALL, H5S_ALL, H5P_DEFAULT, val);
-
- if ( r < 0 ) {
+ if ( H5Dread(dh, memtype, H5S_ALL, H5S_ALL, H5P_DEFAULT, val) < 0 ) {
ERROR("Couldn't read value.\n");
H5Tclose(type);
H5Dclose(dh);
@@ -1388,17 +1306,6 @@ static int get_ev_based_value(struct hdfile *f, const char *name,
}
-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);
- }
-}
-
-
static void hdfile_fill_in_beam_parameters(struct beam_params *beam,
struct hdfile *f,
struct event *ev,
@@ -1902,13 +1809,16 @@ int hdf5_read2(struct hdfile *f, struct image *image, struct event *ev,
if ( p->mask != NULL ) {
int *flags = malloc(p->w*p->h*sizeof(int));
if ( !load_mask(f, ev, p, flags, f_offset, f_count, hsd) ) {
- image->bad[pi] = make_badmask(flags, p, image->det);
+ image->bad[pi] = make_badmask(flags, image->det,
+ image->dp[pi], p);
} else {
- image->bad[pi] = make_badmask(NULL, p, image->det);
+ image->bad[pi] = make_badmask(NULL, image->det,
+ image->dp[pi], p);
}
free(flags);
} else {
- image->bad[pi] = make_badmask(NULL, p, image->det);
+ image->bad[pi] = make_badmask(NULL, image->det,
+ image->dp[pi], p);
}
if ( p->satmap != NULL ) {
@@ -2184,8 +2094,14 @@ char *hdfile_get_string_value(struct hdfile *f, const char *name,
size = H5Tget_size(type);
tmp = malloc(size+1);
- sh = H5Screate(H5S_SCALAR);
+ sh = H5Dget_space(dh);
+ if ( H5Sget_simple_extent_ndims(sh) ) {
+ H5Tclose(type);
+ free(subst_name);
+ return strdup("[non-scalar string]");
+ }
+ sh = H5Screate(H5S_SCALAR);
r = H5Dread(dh, type, sh, H5S_ALL, H5P_DEFAULT, tmp);
H5Sclose(sh);
if ( r < 0 ) {
@@ -2229,7 +2145,6 @@ char *hdfile_get_string_value(struct hdfile *f, const char *name,
snprintf(tmp, 255, "%f", buf_f);
return tmp;
} else {
- ERROR("Failed to read value\n");
return NULL;
}
break;
@@ -2248,7 +2163,6 @@ char *hdfile_get_string_value(struct hdfile *f, const char *name,
return tmp;
} else {
- ERROR("Failed to read value\n");
return NULL;
}
break;
diff --git a/libcrystfel/src/hdf5-file.h b/libcrystfel/src/hdf5-file.h
index ab53dd2e..793d83cd 100644
--- a/libcrystfel/src/hdf5-file.h
+++ b/libcrystfel/src/hdf5-file.h
@@ -52,6 +52,11 @@ struct copy_hdf5_field;
extern "C" {
#endif
+/**
+ * \file hdf5-file.h
+ * HDF5 abstraction layer
+ */
+
extern int hdf5_write(const char *filename, const void *data,
int width, int height, int type);
diff --git a/libcrystfel/src/image.c b/libcrystfel/src/image.c
index 7dfb9c97..44ef2774 100644
--- a/libcrystfel/src/image.c
+++ b/libcrystfel/src/image.c
@@ -36,35 +36,13 @@
#include <hdf5.h>
#include <zlib.h>
-#ifdef HAVE_CBFLIB
-#ifdef HAVE_CBFLIB_CBF_H
-#include <cbflib/cbf.h>
-#endif
-#ifdef HAVE_CBF_CBF_H
-#include <cbf/cbf.h>
-#endif
-#endif
-
#include "image.h"
#include "utils.h"
#include "events.h"
#include "hdf5-file.h"
#include "detector.h"
-/**
- * SECTION:image
- * @short_description: Data structure representing an image
- * @title: Image
- * @section_id:
- * @see_also:
- * @include: "image.h"
- * @Image:
- *
- * The <structname>image</structname> structure represents an image, usually one
- * frame from a large series of diffraction patterns, which might be from the
- * same or different crystals.
- */
-
+/** \file image.h */
struct imagefile
{
@@ -76,8 +54,9 @@ struct imagefile
struct _imagefeaturelist
{
- struct imagefeature *features;
- int n_features;
+ struct imagefeature *features;
+ int max_features;
+ int n_features;
};
@@ -85,13 +64,13 @@ void image_add_feature(ImageFeatureList *flist, double fs, double ss,
struct panel *p,
struct image *parent, double intensity, const char *name)
{
- if ( flist->features ) {
- flist->features = realloc(flist->features,
- (flist->n_features+1)
- *sizeof(struct imagefeature));
- } else {
- assert(flist->n_features == 0);
- flist->features = malloc(sizeof(struct imagefeature));
+ if ( flist->n_features == flist->max_features ) {
+ struct imagefeature *nf;
+ int nmf = flist->max_features + 128;
+ nf = realloc(flist->features, nmf*sizeof(struct imagefeature));
+ if ( nf == NULL ) return;
+ flist->features = nf;
+ flist->max_features = nmf;
}
flist->features[flist->n_features].fs = fs;
@@ -100,10 +79,8 @@ void image_add_feature(ImageFeatureList *flist, double fs, double ss,
flist->features[flist->n_features].intensity = intensity;
flist->features[flist->n_features].parent = parent;
flist->features[flist->n_features].name = name;
- flist->features[flist->n_features].valid = 1;
flist->n_features++;
-
}
@@ -114,6 +91,7 @@ ImageFeatureList *image_feature_list_new()
flist = malloc(sizeof(ImageFeatureList));
flist->n_features = 0;
+ flist->max_features = 0;
flist->features = NULL;
return flist;
@@ -129,8 +107,9 @@ static int comp(const void *a, const void *b)
}
-/* Strongest first. Returned list is guaranteed not to have any holes
- * (feature->valid = 0) */
+/**
+ * Strongest first.
+ */
ImageFeatureList *sort_peaks(ImageFeatureList *flist)
{
ImageFeatureList *n = image_feature_list_new();
@@ -162,7 +141,6 @@ ImageFeatureList *sort_peaks(ImageFeatureList *flist)
void image_feature_list_free(ImageFeatureList *flist)
{
if ( !flist ) return;
-
if ( flist->features ) free(flist->features);
free(flist);
}
@@ -259,15 +237,15 @@ struct imagefeature *image_get_feature(ImageFeatureList *flist, int idx)
if ( flist == NULL ) return NULL;
if ( idx >= flist->n_features ) return NULL;
- if ( flist->features[idx].valid == 0 ) return NULL;
-
return &flist->features[idx];
}
void image_remove_feature(ImageFeatureList *flist, int idx)
{
- flist->features[idx].valid = 0;
+ memmove(&flist->features[idx], &flist->features[idx+1],
+ (flist->n_features-idx-1)*sizeof(struct imagefeature));
+ flist->n_features--;
}
@@ -406,40 +384,6 @@ void add_imagefile_field(struct imagefile_field_list *copyme, const char *name)
/******************************* CBF files ************************************/
-#ifdef HAVE_CBFLIB
-
-static char *cbf_strerr(int e)
-{
- char *err;
-
- err = malloc(1024);
- if ( err == NULL ) return NULL;
-
- err[0] = '\0';
-
- /* NB Sum of lengths of all strings must be less than 1024 */
- if ( e & CBF_FORMAT ) strcat(err, "Invalid format");
- if ( e & CBF_ALLOC ) strcat(err, "Memory allocation failed");
- if ( e & CBF_ARGUMENT ) strcat(err, "Invalid argument");
- if ( e & CBF_ASCII ) strcat(err, "Value is ASCII");
- if ( e & CBF_BINARY ) strcat(err, "Value is binary");
- if ( e & CBF_BITCOUNT ) strcat(err, "Wrong number of bits");
- if ( e & CBF_ENDOFDATA ) strcat(err, "End of data");
- if ( e & CBF_FILECLOSE ) strcat(err, "File close error");
- if ( e & CBF_FILEOPEN ) strcat(err, "File open error");
- if ( e & CBF_FILEREAD ) strcat(err, "File read error");
- if ( e & CBF_FILETELL ) strcat(err, "File tell error");
- if ( e & CBF_FILEWRITE ) strcat(err, "File write error");
- if ( e & CBF_IDENTICAL ) strcat(err, "Name already exists");
- if ( e & CBF_NOTFOUND ) strcat(err, "Not found");
- if ( e & CBF_OVERFLOW ) strcat(err, "Overflow");
- if ( e & CBF_UNDEFINED ) strcat(err, "Number undefined");
- if ( e & CBF_NOTIMPLEMENTED ) strcat(err, "Not implemented");
-
- return err;
-}
-
-
static int unpack_panels(struct image *image, float *data, int data_width,
int data_height)
{
@@ -521,6 +465,8 @@ static int unpack_panels(struct image *image, float *data, int data_width,
bad = 1;
}
+ if ( isnan(data[idx]) || isinf(data[idx]) ) bad = 1;
+
if ( flags != NULL ) {
int f;
@@ -588,50 +534,237 @@ static void cbf_fill_in_clen(struct detector *det, struct imagefile *f)
}
-static float *convert_sint32_float(int32_t *data, int w, int h)
+static void add_out(float val, float *data_out, int nmemb_out,
+ int *outpos, int *nrej)
{
- float *df;
- long int i;
+ if ( *outpos < nmemb_out ) {
+ data_out[(*outpos)++] = val;
+ } else {
+ (*nrej)++;
+ }
+}
- df = malloc(sizeof(float)*w*h);
- if ( df == NULL ) return NULL;
- for ( i=0; i<w*h; i++ ) {
- df[i] = data[i];
+/* Reverses byte offset compression and converts to single precision float.
+ * Note that this compression scheme specifies the data format of the input
+ * data, therefore the X-Binary-Element-Type is completely ignored. */
+static void decode_cbf_byte_offset(float *data_out, int nmemb_out,
+ const int8_t *data_in, const size_t n)
+{
+ int inpos = 0;
+ int outpos = 0;
+ int nrej = 0;
+ float val = 0.0;
+
+ while ( inpos < n ) {
+
+ int64_t delta = data_in[inpos++];
+
+ if ( (delta >= -127) && (delta <= 127) ) {
+ val += delta;
+ add_out(val, data_out, nmemb_out, &outpos, &nrej);
+ continue;
+ }
+
+ delta = *(int16_t *)(data_in+inpos);
+ inpos += 2;
+
+ if ( (delta >= -32767) && (delta <= 32767) ) {
+ val += delta;
+ add_out(val, data_out, nmemb_out, &outpos, &nrej);
+ continue;
+ }
+
+ delta = *(int32_t *)(data_in+inpos);
+ inpos += 4;
+
+ if ( (delta >= -2147483647) && (delta <= 2147483647) ) {
+ val += delta;
+ add_out(val, data_out, nmemb_out, &outpos, &nrej);
+ continue;
+ }
+
+ delta = *(int64_t *)(data_in+inpos);
+ inpos += 8;
+ val += delta;
+ add_out(val, data_out, nmemb_out, &outpos, &nrej);
+
+ }
+
+ if ( nrej > 0 ) {
+ STATUS("%i elements rejected\n", nrej);
}
+}
+
- return df;
+static int binary_start(char *data)
+{
+ char *datac = data;
+ if ( (datac[0] == (char)0x0c) && (datac[1] == (char)0x1a)
+ && (datac[2] == (char)0x04) && (datac[3] == (char)0xd5) ) return 1;
+ return 0;
}
-static float *convert_sint16_float(int16_t *data, int w, int h)
+enum cbf_data_conversion
+{
+ CBF_NO_CONVERSION,
+ CBF_BYTE_OFFSET,
+ CBF_PACKED,
+ CBF_CANONICAL
+};
+
+enum cbf_data_type
+{
+ CBF_NO_TYPE,
+ CBF_ELEMENT_U8,
+ CBF_ELEMENT_S8,
+ CBF_ELEMENT_U16,
+ CBF_ELEMENT_S16,
+ CBF_ELEMENT_U32,
+ CBF_ELEMENT_S32,
+ CBF_ELEMENT_F32,
+ CBF_ELEMENT_F64,
+};
+
+
+static enum cbf_data_type parse_element_type(const char *t)
+{
+ if ( strstr(t, "signed 8-bit integer") != NULL )
+ {
+ return CBF_ELEMENT_S8;
+ }
+
+ if ( strstr(t, "unsigned 8-bit integer") != NULL )
+ {
+ return CBF_ELEMENT_U8;
+ }
+
+ if ( strstr(t, "signed 16-bit integer") != NULL )
+ {
+ return CBF_ELEMENT_S16;
+ }
+
+ if ( strstr(t, "unsigned 16-bit integer") != NULL )
+ {
+ return CBF_ELEMENT_U16;
+ }
+
+ if ( strstr(t, "signed 32-bit integer") != NULL )
+ {
+ return CBF_ELEMENT_S32;
+ }
+
+ if ( strstr(t, "unsigned 32-bit integer") != NULL )
+ {
+ return CBF_ELEMENT_U32;
+ }
+
+ if ( strstr(t, "signed 32-bit real IEEE") != NULL )
+ {
+ return CBF_ELEMENT_F32;
+ }
+
+ if ( strstr(t, "signed 64-bit real IEEE") != NULL )
+ {
+ return CBF_ELEMENT_F64;
+ }
+
+ /* complex type is unsupported */
+
+ return CBF_NO_TYPE;
+}
+
+
+static size_t element_size(enum cbf_data_type t)
+{
+ switch ( t ) {
+ case CBF_ELEMENT_S8 : return 1;
+ case CBF_ELEMENT_U8 : return 1;
+ case CBF_ELEMENT_S16 : return 2;
+ case CBF_ELEMENT_U16 : return 2;
+ case CBF_ELEMENT_S32 : return 4;
+ case CBF_ELEMENT_U32 : return 4;
+ case CBF_ELEMENT_F32 : return 4;
+ case CBF_ELEMENT_F64 : return 8;
+ default : return 0;
+ }
+}
+
+
+
+static int convert_type(float *data_out, long nmemb_exp,
+ enum cbf_data_type eltype,
+ void *data_in, size_t data_in_len)
{
- float *df;
long int i;
+ long int o = 0;
+ size_t elsize = element_size(eltype);
+
+ if ( elsize == 0 ) return 1;
+
+ if ( nmemb_exp * elsize > data_in_len ) {
+ ERROR("Not enough CBF data for image size/type!\n");
+ return 1;
+ }
+
+ for ( i=0; i<nmemb_exp; i++ ) {
+ switch ( eltype ) {
+
+ case CBF_ELEMENT_S8:
+ data_out[o++] = ((int8_t *)data_in)[i];
+ break;
+
+ case CBF_ELEMENT_U8:
+ data_out[o++] = ((uint8_t *)data_in)[i];
+ break;
+
+ case CBF_ELEMENT_S16:
+ data_out[o++] = ((int16_t *)data_in)[i];
+ break;
+
+ case CBF_ELEMENT_U16:
+ data_out[o++] = ((uint16_t *)data_in)[i];
+ break;
- df = malloc(sizeof(float)*w*h);
- if ( df == NULL ) return NULL;
+ case CBF_ELEMENT_S32:
+ data_out[o++] = ((int32_t *)data_in)[i];
+ break;
- for ( i=0; i<w*h; i++ ) {
- df[i] = data[i];
+ case CBF_ELEMENT_U32:
+ data_out[o++] = ((uint32_t *)data_in)[i];
+ break;
+
+ case CBF_ELEMENT_F32:
+ data_out[o++] = ((float *)data_in)[i];
+ break;
+
+ case CBF_ELEMENT_F64:
+ data_out[o++] = ((double *)data_in)[i];
+ break;
+
+ case CBF_NO_TYPE:
+ break;
+ }
}
- return df;
+ return 0;
}
-static float *read_cbf_data(struct imagefile *f, int *w, int *h, cbf_handle *pcbfh)
+static float *read_cbf_data(struct imagefile *f, int *w, int *h)
{
- cbf_handle cbfh;
FILE *fh;
- int r;
- unsigned int compression;
- int binary_id, minelement, maxelement, elsigned, elunsigned;
- size_t elsize, elements, elread, dimfast, dimmid, dimslow, padding;
- const char *byteorder;
- void *data;
- float *dataf;
void *buf = NULL;
+ char *rval;
+ size_t data_compressed_len = 0;
+ float *data_out = NULL;
+ enum cbf_data_conversion data_conversion = CBF_NO_CONVERSION;
+ enum cbf_data_type data_type = CBF_ELEMENT_U32; /* ITG (2006) 2.3.3.3 */
+ int in_binary_section = 0;
+
+ *w = 0;
+ *h = 0;
if ( f->type == IMAGEFILE_CBF ) {
@@ -682,108 +815,182 @@ static float *read_cbf_data(struct imagefile *f, int *w, int *h, cbf_handle *pcb
return NULL;
}
- if ( cbf_make_handle(&cbfh) ) {
- ERROR("Failed to allocate CBF handle\n");
- return NULL;
- }
+ /* This is really horrible, but there are at least three different types
+ * of header mingled together (CIF, MIME, DECTRIS), so a real parser
+ * would be very complicated and much more likely to have weird bugs. */
+ do {
- /* CBFlib will call fclose(fh) when it's ready */
+ char line[1024];
+ long line_start;
- if ( cbf_read_widefile(cbfh, fh, 0) ) {
- ERROR("Failed to read CBF file '%s'\n", f->filename);
- cbf_free_handle(cbfh);
- return NULL;
- }
+ line_start = ftell(fh);
+ rval = fgets(line, 1023, fh);
+ if ( rval == NULL ) break;
+ chomp(line);
- /* Select row 0 in data column inside array_data */
- cbf_find_category(cbfh, "array_data");
- cbf_find_column(cbfh, "data");
- cbf_select_row(cbfh, 0);
-
- /* Get parameters for array read */
- r = cbf_get_integerarrayparameters_wdims(cbfh, &compression, &binary_id,
- &elsize, &elsigned, &elunsigned,
- &elements,
- &minelement, &maxelement,
- &byteorder,
- &dimfast, &dimmid, &dimslow,
- &padding);
- if ( r ) {
- char *err = cbf_strerr(r);
- ERROR("Failed to read CBF array parameters: %s\n", err);
- free(err);
- cbf_free_handle(cbfh);
- return NULL;
- }
+ if ( strcmp(line, "--CIF-BINARY-FORMAT-SECTION--") == 0 ) {
+ in_binary_section = 1;
+ }
- if ( dimslow != 0 ) {
- ERROR("CBF data array is 3D - don't know what to do with it\n");
- cbf_free_handle(cbfh);
- return NULL;
- }
+ if ( strcmp(line, "--CIF-BINARY-FORMAT-SECTION----") == 0 ) {
+ in_binary_section = 0;
+ }
- if ( dimfast*dimmid*elsize > 10e9 ) {
- ERROR("CBF data is far too big (%i x %i x %i bytes).\n",
- (int)dimfast, (int)dimmid, (int)elsize);
- cbf_free_handle(cbfh);
- return NULL;
- }
+ if ( in_binary_section ) {
- if ( (elsize != 4) && (elsize != 2) ) {
- STATUS("Don't know what to do with element size %i\n",
- (int)elsize);
- cbf_free_handle(cbfh);
- return NULL;
- }
+ if ( strncmp(line, "X-Binary-Size: ", 15) == 0 ) {
+ data_compressed_len = atoi(line+15);
+ }
- if ( !elsigned ) {
- STATUS("Don't know what to do with unsigned data (yet)\n");
- cbf_free_handle(cbfh);
- return NULL;
- }
+ if ( strncmp(line, "X-Binary-Element-Byte-Order: ", 29) == 0 ) {
+ const char *elbo = line+29;
+ if ( strcmp(elbo, "LITTLE_ENDIAN") != 0 ) {
+ ERROR("Unsupported endianness: %s\n", elbo);
+ free(buf);
+ fclose(fh);
+ return NULL;
+ }
+ }
- if ( strcmp(byteorder, "little_endian") != 0 ) {
- STATUS("Don't know what to do with non-little-endian datan\n");
- cbf_free_handle(cbfh);
- return NULL;
- }
+ /* Try to spot compression algorithm */
+ if ( strstr(line, "conversions=\"x-CBF_BYTE_OFFSET\"") != NULL ) {
+ data_conversion = CBF_BYTE_OFFSET;
+ } else if ( strstr(line, "conversions=\"x-CBF_CANONICAL\"") != NULL ) {
+ data_conversion = CBF_CANONICAL;
+ } else if ( strstr(line, "conversions=\"x-CBF_PACKED\"") != NULL ) {
+ data_conversion = CBF_PACKED;
+ } else if ( strstr(line, "conversions=") != NULL ) {
+ ERROR("Unrecognised CBF content conversion: %s\n", line);
+ free(buf);
+ fclose(fh);
+ return NULL;
+ }
- data = malloc(elsize*dimfast*dimmid);
- if ( data == NULL ) {
- ERROR("Failed to allocate memory for CBF data\n");
- cbf_free_handle(cbfh);
- return NULL;
- }
+ /* Likewise, element type */
+ if ( strncmp(line, "X-Binary-Element-Type: ", 23) == 0 )
+ {
+ const char *eltype = (line+23);
+ data_type = parse_element_type(eltype);
+ if ( data_type == CBF_NO_TYPE ) {
+ ERROR("Unrecognised element type: %s\n",
+ eltype);
+ free(buf);
+ fclose(fh);
+ return NULL;
+ }
+ }
- r = cbf_get_integerarray(cbfh, &binary_id, data, elsize, 1,
- elsize*dimfast*dimmid, &elread);
- if ( r ) {
- char *err = cbf_strerr(r);
- ERROR("Failed to read CBF array: %s\n", err);
- free(err);
- cbf_free_handle(cbfh);
- return NULL;
- }
+ if ( strncmp(line, "X-Binary-Size-Fastest-Dimension: ", 33) == 0 ) {
+ *w = atoi(line+33);
+ }
- if ( elsize == 4 ) {
- dataf = convert_sint32_float(data, dimfast, dimmid);
- } else if ( elsize == 2 ) {
- dataf = convert_sint16_float(data, dimfast, dimmid);
- } else {
- ERROR("Don't know how to convert element size %i\n",
- (int)elsize);
- cbf_free_handle(cbfh);
- return NULL;
- }
+ if ( strncmp(line, "X-Binary-Size-Second-Dimension: ", 32) == 0 ) {
+ *h = atoi(line+32);
+ }
- free(data);
+ }
+
+ if ( in_binary_section && binary_start(line) ) {
+
+ size_t len_read;
+ int nmemb_exp;
+ void *data_compressed;
+ int r = 0;
+
+ if ( data_compressed_len == 0 ) {
+ ERROR("Found CBF data before X-Binary-Size!\n");
+ free(buf);
+ fclose(fh);
+ return NULL;
+ }
+
+ if ( (*w == 0) || (*h == 0) ) {
+ ERROR("Found CBF data before dimensions!\n");
+ free(buf);
+ fclose(fh);
+ return NULL;
+ }
+
+ if ( data_compressed_len > 100*1024*1024 ) {
+ ERROR("Stated CBF data size too big\n");
+ free(buf);
+ fclose(fh);
+ return NULL;
+ }
+
+ data_compressed = malloc(data_compressed_len);
+ if ( data_compressed == NULL ) {
+ ERROR("Failed to allocate memory for CBF data\n");
+ free(buf);
+ fclose(fh);
+ return NULL;
+ }
+
+ fseek(fh, line_start+4, SEEK_SET);
+ len_read = fread(data_compressed, 1, data_compressed_len, fh);
+ if ( len_read < data_compressed_len ) {
+ ERROR("Couldn't read entire CBF data\n");
+ free(buf);
+ free(data_compressed);
+ fclose(fh);
+ return NULL;
+ }
+
+ nmemb_exp = (*w) * (*h);
+ data_out = malloc(nmemb_exp*sizeof(float));
+ if ( data_out == NULL ) {
+ ERROR("Failed to allocate memory for CBF data\n");
+ free(buf);
+ free(data_compressed);
+ fclose(fh);
+ return NULL;
+ }
+
+ switch ( data_conversion ) {
+
+ case CBF_NO_CONVERSION:
+ r = convert_type(data_out, nmemb_exp, data_type,
+ data_compressed,
+ data_compressed_len);
+ break;
+
+ case CBF_BYTE_OFFSET:
+ decode_cbf_byte_offset(data_out, nmemb_exp,
+ data_compressed,
+ data_compressed_len);
+ break;
+
+ case CBF_PACKED:
+ case CBF_CANONICAL:
+ ERROR("Don't yet know how to decompress "
+ "CBF_PACKED or CBF_CANONICAL\n");
+ free(buf);
+ free(data_compressed);
+ fclose(fh);
+ return NULL;
+
+ }
+
+ free(data_compressed);
- free(buf); /* Might be NULL */
+ if ( r ) {
+ free(buf);
+ free(data_out);
+ fclose(fh);
+ return NULL;
+ }
+
+ free(buf);
+ fclose(fh);
+ return data_out;
+
+ }
+
+ } while ( rval != NULL );
- *w = dimfast;
- *h = dimmid;
- *pcbfh = cbfh;
- return dataf;
+ ERROR("Reached end of CBF file before finding data.\n");
+ free(buf); /* might be NULL */
+ return NULL;
}
@@ -791,9 +998,8 @@ static int read_cbf(struct imagefile *f, struct image *image)
{
float *data;
int w, h;
- cbf_handle cbfh;
- data = read_cbf_data(f, &w, &h, &cbfh);
+ data = read_cbf_data(f, &w, &h);
if ( data == NULL ) {
ERROR("Failed to read CBF data\n");
return 1;
@@ -813,7 +1019,6 @@ static int read_cbf(struct imagefile *f, struct image *image)
cbf_fill_in_clen(image->det, f);
fill_in_adu(image);
- cbf_free_handle(cbfh);
return 0;
}
@@ -822,9 +1027,8 @@ static int read_cbf_simple(struct imagefile *f, struct image *image)
{
float *data;
int w, h;
- cbf_handle cbfh;
- data = read_cbf_data(f, &w, &h, &cbfh);
+ data = read_cbf_data(f, &w, &h);
if ( data == NULL ) {
ERROR("Failed to read CBF data\n");
return 1;
@@ -850,30 +1054,10 @@ static int read_cbf_simple(struct imagefile *f, struct image *image)
cbf_fill_in_clen(image->det, f);
fill_in_adu(image);
- cbf_free_handle(cbfh);
return 0;
}
-#else /* HAVE_CBFLIB */
-
-static int read_cbf_simple(struct imagefile *f, struct image *image)
-{
- ERROR("This version of CrystFEL was compiled without CBF support.\n");
- return 1;
-}
-
-
-static int read_cbf(struct imagefile *f, struct image *image)
-{
- ERROR("This version of CrystFEL was compiled without CBF support.\n");
- return 1;
-}
-
-
-#endif /* HAVE_CBFLIB */
-
-
/****************************** Image files ***********************************/
diff --git a/libcrystfel/src/image.h b/libcrystfel/src/image.h
index cc1bcc5d..706b06b1 100644
--- a/libcrystfel/src/image.h
+++ b/libcrystfel/src/image.h
@@ -3,11 +3,11 @@
*
* Handle images and image features
*
- * Copyright © 2012-2018 Deutsches Elektronen-Synchrotron DESY,
+ * Copyright © 2012-2019 Deutsches Elektronen-Synchrotron DESY,
* a research centre of the Helmholtz Association.
*
* Authors:
- * 2009-2018 Thomas White <taw@physics.org>
+ * 2009-2019 Thomas White <taw@physics.org>
* 2014 Valerio Mariani
*
*
@@ -54,195 +54,134 @@ struct imagefile_field_list;
#include "crystal.h"
#include "index.h"
#include "events.h"
+#include "spectrum.h"
/**
- * SpectrumType:
- * @SPECTRUM_TOPHAT: A top hat distribution of wavelengths
- * @SPECTRUM_SASE: A simulated SASE spectrum
- * @SPECTRUM_TWOCOLOUR: A spectrum containing two peaks
- * @SPECTRUM_FROMFILE: An arbitrary spectrum read from input file
+ * \file image.h
*
- * A %SpectrumType represents a type of X-ray energy spectrum to use for
- * generating simulated data.
- **/
-typedef enum {
- SPECTRUM_TOPHAT,
- SPECTRUM_SASE,
- SPECTRUM_TWOCOLOUR,
- SPECTRUM_FROMFILE
-} SpectrumType;
-
-
-/**
- * imagefeature:
- * @parent: Image this feature belongs to
- * @fs: Fast scan coordinate
- * @ss: Slow scan coordinate
- * @p: Pointer to panel
- * @intensity: Intensity of peak
- * @rx: Reciprocal x coordinate in m^-1
- * @ry: Reciprocal y coordinate in m^-1
- * @rz: Reciprocal z coordinate in m^-1
- * @name: Text name for feature
- *
- * Represents a peak in an image.
- *
- * Note carefully that the @fs and @ss coordinates are the distances, measured
- * in pixels, from the corner of the panel. They are NOT pixel indices.
- * If the peak is in the middle of the first pixel, its coordinates would be
- * 0.5,0.5.
+ * Information about images
*/
+
+/** Represents a peak in an image. */
struct imagefeature {
- struct image *parent;
+ struct image *parent; /**< Pointer to image */
+
+ /** \name Coordinates on panel (fast scan, slow scan)
+ * Note carefully that these are the distances, measured in pixels,
+ * from the corner of the panel. They are NOT pixel indices.
+ * If the peak is in the middle of the first pixel, its coordinates would be
+ * 0.5,0.5. */
+ /**@{*/
double fs;
double ss;
- struct panel *p;
- double intensity;
+ /**@}*/
- /* Reciprocal space coordinates (m^-1 of course) of this feature */
+ struct panel *p; /**< Pointer to panel */
+ double intensity; /**< Intensity */
+
+ /** \name Reciprocal space coordinates (m^-1) of this feature */
+ /** @{ */
double rx;
double ry;
double rz;
+ /** @} */
- const char *name;
-
- /*< private >*/
- int valid;
+ const char *name; /**< Text name, e.g. "5,3,-1" */
};
-/* An enum representing the image file formats we can handle */
+/** An enum representing the image file formats we can handle */
enum imagefile_type
{
- IMAGEFILE_HDF5,
- IMAGEFILE_CBF,
- IMAGEFILE_CBFGZ
+ IMAGEFILE_HDF5, /**< HDF5 file (single or multiple frames per file) */
+ IMAGEFILE_CBF, /**< CBF file */
+ IMAGEFILE_CBFGZ /**< gzipped CBF file, i.e. "file.cbf.gz" */
};
-/* An opaque type representing a list of image features */
+/** An opaque type representing a list of image features */
typedef struct _imagefeaturelist ImageFeatureList;
-struct spectrum
-{
- int n;
- double *ks; /* 1/m */
- double *weights;
-};
-
-
-/* Structure describing a wavelength sample from a spectrum */
-struct sample
-{
- double k; /* 1/m */
- double weight;
-};
-
-
-/**
- * beam_params:
- * @photon_energy: eV per photon
- * @photon_energy_from: HDF5 dataset name
- * @photon_energy_scale: Scale factor for photon energy, if it comes from HDF5
- */
struct beam_params
{
- double photon_energy;
- char *photon_energy_from;
- double photon_energy_scale;
+ double photon_energy; /**< eV per photon */
+ char *photon_energy_from; /**< HDF5 dataset name */
+ double photon_energy_scale; /**< Scale factor for photon energy, if it
+ * comes from an image header */
};
-/**
- * image:
- * @hit: Non-zero if the frame was determined to be a "hit"
- * @crystals: Array of crystals in the image
- * @n_crystals: The number of crystals in the image
- * @indexed_by: Indexing method which indexed this pattern
- * @n_indexing_tries: Number of times the indexer was tried before indexing
- * @det: Detector structure
- * @beam: Beam parameters structure
- * @filename: Filename for the image file
- * @copyme: Fields to copy from the image file to the stream
- * @id: ID number of the thread handling this image
- * @serial: Serial number for this image
- * @lambda: Wavelength
- * @div: Divergence
- * @bw: Bandwidth
- * @num_peaks: The number of peaks
- * @num_saturated_peaks: The number of saturated peaks
- * @features: The peaks found in the image
- * @dp: The image data, by panel
- * @bad: The bad pixel mask, array by panel
- * @sat: The per-pixel saturation mask, array by panel
- * @event: Event ID for the image
- * @stuff_from_stream: Items read back from the stream
- * @avg_clen: Mean of camera length values for all panels
- * @spectrum: Spectrum information
- * @nsamples: Number of spectrum samples
- * @spectrum_size: Size of spectrum array
- *
- * The field <structfield>data</structfield> contains the raw image data, if it
- * is currently available. The data might be available throughout the
- * processing of an experimental pattern, but it might not be available when
- * simulating, scaling or merging patterns.
- *
- * <structfield>crystals</structfield> is an array of %Crystal directly
- * returned by the low-level indexing system. <structfield>n_crystals</structfield>
- * is the number of crystals which were found in the image.
- *
- * <structfield>copyme</structfield> represents a list of fields in the image
- * file (e.g. HDF5 fields or CBF headers) to copy to the output stream.
- **/
-struct image;
+struct image
+{
+ /** The image data, by panel */
+ float **dp;
-struct image {
+ /** The bad pixel mask, by panel */
+ int **bad;
- float **dp; /* Data in panel */
- int **bad; /* Bad pixels by panel */
- float **sat; /* Per-pixel saturation values */
+ /** The per-pixel saturation values, by panel */
+ float **sat;
+ /** Non-zero if the frame was determined to be a "hit" */
int hit;
+
+ /**Array of crystals in the image */
Crystal **crystals;
+
+ /** The number of crystals in the image (size of \p crystals) */
int n_crystals;
+
+ /** Indexing method which indexed this pattern */
IndexingMethod indexed_by;
+
+ /** Number of times the indexer was tried before succeeding */
int n_indexing_tries;
+ /** The detector structure */
struct detector *det;
- struct beam_params *beam; /* The nominal beam parameters */
+
+ /** The nominal beam parameters (or where to get them) */
+ struct beam_params *beam;
+
+ /** \name The filename and event ID for the image
+ * @{ */
char *filename;
struct event *event;
+ /** @} */
+
+ /** A list of image file headers to copy to the stream */
const struct imagefile_field_list *copyme;
+
+ /** A list of metadata read from the stream */
struct stuff_from_stream *stuff_from_stream;
- double avg_clen; /* Average camera length extracted
- * from stuff_from_stream */
+ /** Mean of the camera length values for all panels */
+ double avg_clen;
+
+ /** ID number of the worker processing handling this image */
+ int id;
+
+ /** Monotonically increasing serial number for this image */
+ int serial;
+
+ /** Spectrum information */
+ Spectrum *spectrum;
- int id; /* ID number of the thread
- * handling this image */
- int serial; /* Monotonically ascending serial
- * number for this image */
+ /** Wavelength of the incident radiation, in metres */
+ double lambda;
- struct spectrum *spectrum; /* Beam spectrum for pink beam data */
+ /** Convergence angle of the incident ration, in radians (full angle) */
+ double div;
- /* FIXME: These are only used in pattern_sim, which should be changed to
- * use the "struct spectrum" from above later */
- struct sample *spectrum0;
- int nsamples; /* Number of wavelengths */
- int spectrum_size; /* Size of "spectrum" */
+ /** Full-width half-maximum bandwidth as a fraction, applied to wavelength */
+ double bw;
- /* Per-shot radiation values */
- double lambda; /* Wavelength in m */
- double div; /* Divergence in radians */
- double bw; /* FWHM bandwidth as a fraction */
+ /** Resolution estimate based on peaks */
+ double peak_resolution;
- /* Detected peaks */
- long long num_peaks;
- long long num_saturated_peaks;
- double peak_resolution; /* Estimate of resolution
- * based on peaks only */
+ /** List of peaks found in the image */
ImageFeatureList *features;
};
diff --git a/libcrystfel/src/index.c b/libcrystfel/src/index.c
index f72334c4..6f0d1b8c 100644
--- a/libcrystfel/src/index.c
+++ b/libcrystfel/src/index.c
@@ -60,6 +60,9 @@
#include "xgandalf.h"
+/** \file index.h */
+
+
struct _indexingprivate
{
IndexingFlags flags;
@@ -698,9 +701,9 @@ static int try_indexer(struct image *image, IndexingMethod indm,
/* Don't do similarity check against bad crystals */
if ( crystal_get_user_flag(that_cr) ) continue;
- if ( compare_cells(crystal_get_cell(cr),
- crystal_get_cell(that_cr),
- 0.1, deg2rad(0.5), NULL) )
+ if ( compare_reindexed_cell_parameters_and_orientation(crystal_get_cell(cr),
+ crystal_get_cell(that_cr),
+ 0.1, deg2rad(0.5), NULL) )
{
crystal_set_user_flag(cr, 1);
}
diff --git a/libcrystfel/src/index.h b/libcrystfel/src/index.h
index eaf6f912..15b21e25 100644
--- a/libcrystfel/src/index.h
+++ b/libcrystfel/src/index.h
@@ -38,6 +38,11 @@
#include <config.h>
#endif
+/**
+ * \file index.h
+ * The indexing subsystem
+ */
+
#define INDEXING_DEFAULTS_DIRAX (INDEXING_DIRAX)
@@ -59,62 +64,64 @@
#define INDEXING_DEFAULTS_XGANDALF (INDEXING_XGANDALF | INDEXING_USE_CELL_PARAMETERS)
/**
- * IndexingMethod:
- * @INDEXING_NONE: No indexing to be performed
- * @INDEXING_DIRAX: Invoke DirAx
- * @INDEXING_MOSFLM: Invoke MOSFLM
- * @INDEXING_FELIX: Invoke Felix
- * @INDEXING_XDS: Invoke XDS
- * @INDEXING_SIMULATION: Dummy value
- * @INDEXING_DEBUG: Results injector for debugging
- * @INDEXING_ASDF: Use in-built "asdf" indexer
- * @INDEXING_TAKETWO: Use in-built "taketwo" indexer
- * @INDEXING_XGANDALF: Invoke XGANDALF
- * @INDEXING_ERROR: Special value for unrecognised indexing engine name
- * @INDEXING_USE_LATTICE_TYPE: Use lattice type and centering information to
- * guide the indexing process.
- * @INDEXING_USE_CELL_PARAMETERS: Use the unit cell parameters to guide the
- * indexing process.
- *
- * An enumeration of all the available indexing methods. The dummy value
- * @INDEXING_SIMULATION is used by partial_sim to indicate that no indexing was
- * performed, and that the indexing results are just from simulation.
+ * An enumeration of all the available indexing methods.
**/
typedef enum {
- INDEXING_NONE = 0,
-
- /* The core indexing methods themselves */
- INDEXING_DIRAX = 1,
- INDEXING_MOSFLM = 2,
- INDEXING_FELIX = 4,
- INDEXING_XDS = 5,
- INDEXING_SIMULATION = 6,
- INDEXING_DEBUG = 7,
- INDEXING_ASDF = 8,
- INDEXING_TAKETWO = 9,
- INDEXING_XGANDALF = 10,
-
- INDEXING_ERROR = 255, /* Unrecognised indexing engine */
-
- /* Bits at the top of the IndexingMethod are flags which modify the
- * behaviour of the indexer. */
+ INDEXING_NONE = 0, /**< No indexing to be performed */
+
+ INDEXING_DIRAX = 1, /**< Invoke DirAx program */
+ INDEXING_MOSFLM = 2, /**< Invoke MOSFLM program */
+ INDEXING_FELIX = 4, /**< Invoke Felix program */
+ INDEXING_XDS = 5, /**< Invoke XDS program (NB not nXDS) */
+ INDEXING_SIMULATION = 6, /**< Dummy value for simulated data */
+ INDEXING_DEBUG = 7, /**< Results injector for debugging */
+ INDEXING_ASDF = 8, /**< Use built-in ASDF algorithm */
+ INDEXING_TAKETWO = 9, /**< Use built-in TakeTwo algorithm */
+ INDEXING_XGANDALF = 10, /**< Use XGANDALF (via optional library) */
+
+ INDEXING_ERROR = 255, /**< Special value for unrecognised indexing
+ * engine */
+
+ /** \name Bits which can be set to modify the behaviour of the above
+ * indexing methods */
+ /**@{*/
+ /** Use lattice type and centering information */
INDEXING_USE_LATTICE_TYPE = 2048,
+
+ /** Use the cell parameters themselves */
INDEXING_USE_CELL_PARAMETERS = 4096,
+ /**@}*/
+
} IndexingMethod;
-/* This defines the bits in "IndexingMethod" which are used to represent the
+/** This defines the bits in "IndexingMethod" which are used to represent the
* core of the indexing method */
#define INDEXING_METHOD_MASK (0xff)
+/**
+ * Flags affecting how the indexing system processes the results from the
+ * indexing engine
+ */
typedef enum {
+ /** Retry indexing if it doesn't work */
INDEXING_RETRY = 1,
+
+ /** Attempt to index remaining peaks to find more lattices */
INDEXING_MULTI = 2,
+
+ /** Refine the indexing solution */
INDEXING_REFINE = 4,
+
+ /** Check the unit cell, including derivative lattices */
INDEXING_CHECK_CELL_COMBINATIONS = 8,
+
+ /** Check the unit cell, only permuting axes if necessary */
INDEXING_CHECK_CELL_AXES = 16,
+
+ /** Check that the peaks agree with the indexing solution */
INDEXING_CHECK_PEAKS = 32,
} IndexingFlags;
@@ -124,8 +131,6 @@ extern "C" {
#endif
/**
- * IndexingPrivate:
- *
* This is an opaque data structure containing information needed by the
* indexing system.
**/
diff --git a/libcrystfel/src/integer_matrix.c b/libcrystfel/src/integer_matrix.c
index c31072b4..1d1c3a47 100644
--- a/libcrystfel/src/integer_matrix.c
+++ b/libcrystfel/src/integer_matrix.c
@@ -35,20 +35,12 @@
#include <string.h>
#include <assert.h>
+#include "rational.h"
#include "integer_matrix.h"
+#include "utils.h"
-/**
- * SECTION:integer_matrix
- * @short_description: Integer matrices
- * @title: Integer matrices
- * @section_id:
- * @see_also:
- * @include: "integer_matrix.h"
- * @Image:
- *
- * An integer matrix library
- */
+/** \file integer_matrix.h */
struct _integermatrix
@@ -61,13 +53,12 @@ struct _integermatrix
/**
- * intmat_new:
- * @rows: Number of rows that the new matrix is to have
- * @cols: Number of columns that the new matrix is to have
+ * \param rows Number of rows that the new matrix is to have
+ * \param cols Number of columns that the new matrix is to have
*
- * Allocates a new %IntegerMatrix with all elements set to zero.
+ * Allocates a new \ref IntegerMatrix with all elements set to zero.
*
- * Returns: a new %IntegerMatrix, or NULL on error.
+ * \returns A new \ref IntegerMatrix, or NULL on error.
**/
IntegerMatrix *intmat_new(unsigned int rows, unsigned int cols)
{
@@ -90,12 +81,11 @@ IntegerMatrix *intmat_new(unsigned int rows, unsigned int cols)
/**
- * intmat_copy:
- * @m: An %IntegerMatrix
+ * \param m An \ref IntegerMatrix
*
- * Returns: a newly allocated copy of @m, or NULL on error/
+ * \returns A newly allocated copy of \p m, or NULL on error
**/
-IntegerMatrix *intmat_copy(IntegerMatrix *m)
+IntegerMatrix *intmat_copy(const IntegerMatrix *m)
{
IntegerMatrix *p;
int i, j;
@@ -114,10 +104,9 @@ IntegerMatrix *intmat_copy(IntegerMatrix *m)
/**
- * intmat_free:
- * @m: An %IntegerMatrix
+ * \param m An \ref IntegerMatrix
*
- * Frees @m, unless @m is NULL in which case nothing is done.
+ * Frees \p m, unless \p m is NULL in which case nothing is done.
**/
void intmat_free(IntegerMatrix *m)
{
@@ -128,13 +117,32 @@ void intmat_free(IntegerMatrix *m)
/**
- * intmat_set:
- * @m: An %IntegerMatrix
- * @i: row number to set
- * @j: column number to set
- * @v: value to set to
+ * \param m An \ref IntegerMatrix
+ * \param rows Location to store number of rows
+ * \param cols Location to store number of columns
+ *
+ * Sets \p rows and \p cols to the size of \p m.
+ */
+void intmat_size(const IntegerMatrix *m, unsigned int *rows, unsigned int *cols)
+{
+ if ( m == NULL ) {
+ *rows = 0;
+ *cols = 0;
+ return;
+ }
+
+ *rows = m->rows;
+ *cols = m->cols;
+}
+
+
+/**
+ * \param m An \ref IntegerMatrix
+ * \param i row number to set
+ * \param j column number to set
+ * \param v value to set to
*
- * Sets the @i,@j element of @m to @v.
+ * Sets the \p i,\p j element of \p m to \p v.
**/
void intmat_set(IntegerMatrix *m, unsigned int i, unsigned int j, signed int v)
{
@@ -145,14 +153,13 @@ void intmat_set(IntegerMatrix *m, unsigned int i, unsigned int j, signed int v)
/**
- * intmat_get:
- * @m: An %IntegerMatrix
- * @i: column number to set
- * @j: row number to set
+ * \param m An \ref IntegerMatrix
+ * \param i column number to set
+ * \param j row number to set
*
- * Gets the @i,@j element of @m.
+ * Gets the \p i,\p j element of \p m.
*
- * Returns: the @i,@j element of @m.
+ * \returns The \p i,\p j element of \p m.
**/
signed int intmat_get(const IntegerMatrix *m, unsigned int i, unsigned int j)
{
@@ -163,32 +170,37 @@ signed int intmat_get(const IntegerMatrix *m, unsigned int i, unsigned int j)
/**
- * intmat_intvec_mult:
- * @m: An %IntegerMatrix
- * @vec: An array of signed integers
+ * \param P An \ref IntegerMatrix
+ * \param hkl An array of signed integers
*
- * Multiplies the matrix @m by the vector @vec. The size of @vec must equal the
- * number of columns in @m, and the size of the result equals the number of rows
- * in @m.
+ * Apply transformation matrix P to a set of reciprocal space Miller indices.
*
- * Returns: a newly allocated array of signed integers containing the answer,
+ * In other words:
+ * Multiplies the matrix \p P by the row vector \p hkl. The size of \p vec must equal
+ * the number of columns in \p P, and the size of the result equals the number of
+ * rows in \p P.
+ *
+ * The multiplication looks like this:
+ * (a1, a2, a3) = (hkl1, hkl2, hkl3) P
+ * Therefore matching the notation in ITA chapter 5.1.
+ *
+ * \returns A newly allocated array of signed integers containing the answer,
* or NULL on error.
**/
-signed int *intmat_intvec_mult(const IntegerMatrix *m, const signed int *vec)
+signed int *transform_indices(const IntegerMatrix *P, const signed int *hkl)
{
signed int *ans;
- unsigned int i;
+ unsigned int j;
- ans = malloc(m->rows * sizeof(signed int));
+ ans = malloc(P->rows * sizeof(signed int));
if ( ans == NULL ) return NULL;
- for ( i=0; i<m->rows; i++ ) {
-
- unsigned int j;
+ for ( j=0; j<P->cols; j++ ) {
- ans[i] = 0;
- for ( j=0; j<m->cols; j++ ) {
- ans[i] += intmat_get(m, i, j) * vec[j];
+ unsigned int i;
+ ans[j] = 0;
+ for ( i=0; i<P->rows; i++ ) {
+ ans[j] += intmat_get(P, i, j) * hkl[i];
}
}
@@ -198,13 +210,12 @@ signed int *intmat_intvec_mult(const IntegerMatrix *m, const signed int *vec)
/**
- * intmat_intmat_mult:
- * @a: An %IntegerMatrix
- * @b: An %IntegerMatrix
+ * \param a An \ref IntegerMatrix
+ * \param b An \ref IntegerMatrix
*
- * Multiplies the matrix @a by the matrix @b.
+ * Multiplies the matrix \p a by the matrix \p b.
*
- * Returns: a newly allocated %IntegerMatrix containing the answer, or NULL on
+ * \returns A newly allocated \ref IntegerMatrix containing the answer, or NULL on
* error.
**/
IntegerMatrix *intmat_intmat_mult(const IntegerMatrix *a,
@@ -286,12 +297,11 @@ static signed int cofactor(const IntegerMatrix *m,
/**
- * intmat_det:
- * @m: An %IntegerMatrix
+ * \param m An \ref IntegerMatrix
*
- * Calculates the determinant of @m. Inefficiently.
+ * Calculates the determinant of \p m. Inefficiently.
*
- * Returns: the determinant of @m.
+ * \returns The determinant of \p m.
**/
signed int intmat_det(const IntegerMatrix *m)
{
@@ -337,12 +347,14 @@ static IntegerMatrix *intmat_cofactors(const IntegerMatrix *m)
/**
- * intmat_inverse:
- * @m: An %IntegerMatrix
+ * \param m An \ref IntegerMatrix
*
- * Calculates the inverse of @m. Inefficiently.
+ * Calculates the inverse of \p m. Inefficiently.
*
- * Returns: the inverse of @m, or NULL on error.
+ * Works only if the inverse of the matrix is also an integer matrix,
+ * i.e. if the determinant of \p m is +/- 1.
+ *
+ * \returns The inverse of \p m, or NULL on error.
**/
IntegerMatrix *intmat_inverse(const IntegerMatrix *m)
{
@@ -385,10 +397,9 @@ IntegerMatrix *intmat_inverse(const IntegerMatrix *m)
/**
- * intmat_print
- * @m: An %IntegerMatrix
+ * \param m An \ref IntegerMatrix
*
- * Prints @m to stderr.
+ * Prints \param m to stderr.
*
*/
void intmat_print(const IntegerMatrix *m)
@@ -407,10 +418,9 @@ void intmat_print(const IntegerMatrix *m)
/**
- * intmat_is_identity
- * @m: An %IntegerMatrix
+ * \param m An \ref IntegerMatrix
*
- * Returns: true if @m is an identity matrix.
+ * \returns True if \p m is an identity matrix.
*
*/
int intmat_is_identity(const IntegerMatrix *m)
@@ -440,10 +450,9 @@ int intmat_is_identity(const IntegerMatrix *m)
/**
- * intmat_is_inversion
- * @m: An %IntegerMatrix
+ * \param m An \ref IntegerMatrix
*
- * Returns: true if @m = -I, where I is an identity matrix.
+ * \returns True if \p m = -I, where I is an identity matrix.
*
*/
int intmat_is_inversion(const IntegerMatrix *m)
@@ -473,11 +482,10 @@ int intmat_is_inversion(const IntegerMatrix *m)
/**
- * intmat_equals
- * @a: An %IntegerMatrix
- * @b: An %IntegerMatrix
+ * \param a An \ref IntegerMatrix
+ * \param b An \ref IntegerMatrix
*
- * Returns: true if @a = @b.
+ * \returns True if \p a = \p b.
*
*/
int intmat_equals(const IntegerMatrix *a, const IntegerMatrix *b)
@@ -504,10 +512,9 @@ int intmat_equals(const IntegerMatrix *a, const IntegerMatrix *b)
/**
- * intmat_identity
- * @size: The size of the (square) matrix
+ * \param size The size of the (square) matrix
*
- * Returns: an identity %IntegerMatrix with side length @size, or NULL on error.
+ * \returns An identity \ref IntegerMatrix with side length \p size, or NULL on error.
*
*/
IntegerMatrix *intmat_identity(int size)
@@ -532,3 +539,38 @@ IntegerMatrix *intmat_identity(int size)
return m;
}
+
+
+/**
+ * \param m11 Matrix element
+ * \param m12 Matrix element
+ * \param m13 Matrix element
+ * \param m21 Matrix element
+ * \param m22 Matrix element
+ * \param m23 Matrix element
+ * \param m31 Matrix element
+ * \param m32 Matrix element
+ * \param m33 Matrix element
+ *
+ * \returns A newly allocated 3x3 \ref IntegerMatrix with the given values.
+ */
+IntegerMatrix *intmat_create_3x3(signed int m11, signed int m12, signed int m13,
+ signed int m21, signed int m22, signed int m23,
+ signed int m31, signed int m32, signed int m33)
+{
+ IntegerMatrix *m = intmat_new(3, 3);
+ if ( m == NULL ) return NULL;
+
+ intmat_set(m, 0, 0, m11);
+ intmat_set(m, 0, 1, m12);
+ intmat_set(m, 0, 2, m13);
+
+ intmat_set(m, 1, 0, m21);
+ intmat_set(m, 1, 1, m22);
+ intmat_set(m, 1, 2, m23);
+
+ intmat_set(m, 2, 0, m31);
+ intmat_set(m, 2, 1, m32);
+ intmat_set(m, 2, 2, m33);
+ return m;
+}
diff --git a/libcrystfel/src/integer_matrix.h b/libcrystfel/src/integer_matrix.h
index 6616b5e8..dcbe63bc 100644
--- a/libcrystfel/src/integer_matrix.h
+++ b/libcrystfel/src/integer_matrix.h
@@ -34,31 +34,42 @@
#endif
/**
- * IntegerMatrix
- *
- * The IntegerMatrix is an opaque data structure representing an integer matrix.
+ * \file integer_matrix.h
+ * Matrix type containing only integers
+ */
+
+/**
+ * The \p IntegerMatrix is an opaque data structure representing an integer matrix.
**/
typedef struct _integermatrix IntegerMatrix;
+#include "rational.h"
+
#ifdef __cplusplus
extern "C" {
#endif
/* Alloc/dealloc */
extern IntegerMatrix *intmat_new(unsigned int rows, unsigned int cols);
-extern IntegerMatrix *intmat_copy(IntegerMatrix *m);
+extern IntegerMatrix *intmat_copy(const IntegerMatrix *m);
extern IntegerMatrix *intmat_identity(int size);
extern void intmat_free(IntegerMatrix *m);
/* Get/set */
+extern void intmat_size(const IntegerMatrix *m, unsigned int *rows,
+ unsigned int *cols);
+
extern void intmat_set(IntegerMatrix *m, unsigned int i, unsigned int j,
signed int v);
extern signed int intmat_get(const IntegerMatrix *m,
unsigned int i, unsigned int j);
-/* Matrix-(int)vector multiplication */
-extern signed int *intmat_intvec_mult(const IntegerMatrix *m,
- const signed int *vec);
+extern IntegerMatrix *intmat_create_3x3(signed int m11, signed int m12, signed int m13,
+ signed int m21, signed int m22, signed int m23,
+ signed int m31, signed int m32, signed int m33);
+
+/* Matrix-vector multiplication */
+extern signed int *transform_indices(const IntegerMatrix *P, const signed int *hkl);
/* Matrix-matrix multiplication */
extern IntegerMatrix *intmat_intmat_mult(const IntegerMatrix *a,
diff --git a/libcrystfel/src/integration.c b/libcrystfel/src/integration.c
index 880ae87e..5270816b 100644
--- a/libcrystfel/src/integration.c
+++ b/libcrystfel/src/integration.c
@@ -54,6 +54,9 @@
#include "integration.h"
+/** \file integration.h */
+
+
enum boxmask_val
{
BM_IG, /* "Soft" ignore */
@@ -1352,7 +1355,6 @@ static void integrate_prof2d(IntegrationMethod meth,
UnitCell *cell;
struct intcontext ic;
int i;
- int n_saturated = 0;
list = crystal_get_reflections(cr);
cell = crystal_get_cell(cr);
@@ -1394,8 +1396,6 @@ static void integrate_prof2d(IntegrationMethod meth,
}
free_intcontext(&ic);
-
- image->num_saturated_peaks = n_saturated;
}
diff --git a/libcrystfel/src/integration.h b/libcrystfel/src/integration.h
index 3bf11ee8..7a230daf 100644
--- a/libcrystfel/src/integration.h
+++ b/libcrystfel/src/integration.h
@@ -36,30 +36,39 @@
#include "geometry.h"
/**
- * IntDiag:
- * @INTDIAG_NONE: Never show diagnostics
- * @INTDIAG_RANDOM: Show diagnostics for a randomly selected 1% of reflections
- * @INTDIAG_ALL: Show diagnostics for all reflections
- * @INTDIAG_INDICES: Show diagnostics when the Miller indices of the reflection
- * are the ones specified
- * @INTDIAG_NEGATIVE: Show diagnostics when the measured intensity is less than
- * minus three times its estimated error.
- * @INTDIAG_IMPLAUSIBLE: Show diagnostics when the measured intensity is les
- * than minus five times its estimated error.
- * @INTDIAG_STRONG: Show diagnostics when the measured intensity is more than
- * three times its estimated error.
- *
- * An %IntDiag describes the condition under which the integration subsystem
+ * \file integration.h
+ * Integration of reflections
+ */
+
+/**
+ * An IntDiag describes the condition under which the integration subsystem
* should display diagnostic information to the user.
**/
typedef enum {
+ /** Never show diagnostics */
INTDIAG_NONE,
+
+ /** Show diagnostics for a randomly selected 1% of reflections */
INTDIAG_RANDOM,
+
+ /** Show diagnostics for all reflections */
INTDIAG_ALL,
+
+ /** Show diagnostics when the Miller indices of the reflection are the
+ * ones specified */
INTDIAG_INDICES,
+
+ /** Show diagnostics when the measured intensity is less than minus
+ * three times its estimated error. */
INTDIAG_NEGATIVE,
+
+ /** Show diagnostics when the measured intensity is less than minus five
+ * times its estimated error. */
INTDIAG_IMPLAUSIBLE,
+
+ /** Show diagnostics when the measured intensity is more than three
+ * times its estimated error. */
INTDIAG_STRONG
} IntDiag;
@@ -68,36 +77,37 @@ typedef enum {
#define INTEGRATION_DEFAULTS_PROF2D (INTEGRATION_PROF2D | INTEGRATION_CENTER)
/**
- * IntegrationMethod:
- * @INTEGRATION_NONE: No integration at all
- * @INTEGRATION_RINGS: Summation of pixel values inside ring, minus background
- * @INTEGRATION_PROF2D: Two dimensional profile fitting
- * @INTEGRATION_SATURATED: Integrate saturated reflections
- * @INTEGRATION_CENTER: Center the peak in the box prior to integration
- * @INTEGRATION_RESCUT: Stop integrating at the diffraction limit of the crystal
- * @INTEGRATION_GRADIENTBG: Fit a gradient to the background
- *
- * An enumeration of all the available integration methods.
- **/
+ * An enumeration of all the available integration methods. The first items
+ * are the actual integration methods. The later ones are flags which can be
+ * ORed with the method to affect its behaviour.
+ */
typedef enum {
+ /** No integration at all */
INTEGRATION_NONE = 0,
- /* The core integration methods themselves */
+ /** Summation of pixel values inside ring, minus background */
INTEGRATION_RINGS = 1,
+
+ /** Two dimensional profile fitting */
INTEGRATION_PROF2D = 2,
- /* Bits at the top of the IntegrationMethod are flags which modify the
- * behaviour of the integration. */
+ /** Integrate saturated reflections */
INTEGRATION_SATURATED = 256,
+
+ /** Center the peak in the box prior to integration */
INTEGRATION_CENTER = 512,
+
+ /** Stop integrating at the diffraction limit of the crystal */
INTEGRATION_RESCUT = 1024,
+
+ /** Fit a gradient to the background */
INTEGRATION_GRADIENTBG = 2048,
} IntegrationMethod;
-/* This defines the bits in "IntegrationMethod" which are used to represent the
- * core of the integration method */
+/** This defines the bits in \ref IntegrationMethod which are used to represent the
+ * core of the integration method. */
#define INTEGRATION_METHOD_MASK (0xff)
#ifdef __cplusplus
diff --git a/libcrystfel/src/mosflm.c b/libcrystfel/src/mosflm.c
index d48dcef2..10ca49ec 100644
--- a/libcrystfel/src/mosflm.c
+++ b/libcrystfel/src/mosflm.c
@@ -90,6 +90,7 @@
#include "peaks.h"
#include "cell-utils.h"
+/** \file mosflm.h */
#define MOSFLM_VERBOSE 0
#define FAKE_CLEN (0.1)
diff --git a/libcrystfel/src/mosflm.h b/libcrystfel/src/mosflm.h
index 9339b856..d39f0854 100644
--- a/libcrystfel/src/mosflm.h
+++ b/libcrystfel/src/mosflm.h
@@ -41,6 +41,11 @@
extern "C" {
#endif
+/**
+ * \file mosflm.h
+ * MOSFLM indexer interface
+ */
+
extern int run_mosflm(struct image *image, void *ipriv);
extern void *mosflm_prepare(IndexingMethod *indm, UnitCell *cell);
diff --git a/libcrystfel/src/peakfinder8.c b/libcrystfel/src/peakfinder8.c
index 9090d48f..30082580 100644
--- a/libcrystfel/src/peakfinder8.c
+++ b/libcrystfel/src/peakfinder8.c
@@ -39,6 +39,8 @@
#include "peakfinder8.h"
+/** \file peakfinder8.h */
+
// CrystFEL-only block 1
struct radius_maps
{
@@ -1003,6 +1005,20 @@ static int peakfinder8_base(float *roffset, float *rthreshold,
}
+/**
+ * \param img An \ref image structure
+ * \param max_n_peaks The maximum number of peaks to be searched for
+ * \param threshold The image threshold value, in detector units
+ * \param min_snr The minimum signal to noise ratio for a peak
+ * \param min_pix_count The minimum number of pixels in a peak
+ * \param max_pix_count The maximum number of pixels in a peak
+ * \param local_bg_radius The averaging radius for background calculation
+ * \param min_res The minimum number of pixels out from the center
+ * \param max_res The maximum number of pixels out from the center
+ * \param use_saturated Whether saturated peaks should be considered
+ *
+ * Runs the peakfinder8 peak search algorithm
+ */
int peakfinder8(struct image *img, int max_n_peaks,
float threshold, float min_snr,
int min_pix_count, int max_pix_count,
@@ -1180,9 +1196,7 @@ int peakfinder8(struct image *img, int max_n_peaks,
p = &img->det->panels[pi];
- img->num_peaks += 1;
if ( pkdata->max_i[pki] > p->max_adu ) {
- img->num_saturated_peaks++;
if ( !use_saturated ) {
continue;
}
diff --git a/libcrystfel/src/peakfinder8.h b/libcrystfel/src/peakfinder8.h
index 483eebdf..e25cf0ec 100644
--- a/libcrystfel/src/peakfinder8.h
+++ b/libcrystfel/src/peakfinder8.h
@@ -37,6 +37,11 @@
extern "C" {
#endif
+/**
+ * \file peakfinder8.h
+ * The "peakfinder8" peak search algorithm, originally implemented in Cheetah
+ */
+
extern int peakfinder8(struct image *img, int max_n_peaks,
float threshold, float min_snr,
int mix_pix_count, int max_pix_count,
diff --git a/libcrystfel/src/peaks.c b/libcrystfel/src/peaks.c
index 49e09868..1af901e0 100644
--- a/libcrystfel/src/peaks.c
+++ b/libcrystfel/src/peaks.c
@@ -62,6 +62,7 @@
#include "geometry.h"
#include "peakfinder8.h"
+/** \file peaks.h */
static int cull_peaks_in_panel(struct image *image, struct panel *p)
{
@@ -484,12 +485,9 @@ static void search_peaks_in_panel(struct image *image, float threshold,
continue;
}
- if ( saturated ) {
- image->num_saturated_peaks++;
- if ( !use_saturated ) {
- nrej_sat++;
- continue;
- }
+ if ( saturated && !use_saturated ) {
+ nrej_sat++;
+ continue;
}
/* Add using "better" coordinates */
@@ -509,8 +507,6 @@ static void search_peaks_in_panel(struct image *image, float threshold,
ncull = 0;
}
- image->num_peaks += nacc;
-
//STATUS("%i accepted, %i box, %i proximity, %i outside panel, "
// "%i failed integration, %i with SNR < %g, %i badrow culled, "
// "%i saturated.\n",
@@ -535,8 +531,6 @@ void search_peaks(struct image *image, float threshold, float min_sq_gradient,
image_feature_list_free(image->features);
}
image->features = image_feature_list_new();
- image->num_peaks = 0;
- image->num_saturated_peaks = 0;
for ( i=0; i<image->det->n_panels; i++ ) {
@@ -550,6 +544,22 @@ void search_peaks(struct image *image, float threshold, float min_sq_gradient,
}
+/**
+ * \param image An \ref image structure
+ * \param max_n_peaks The maximum number of peaks to be searched for
+ * \param threshold The image threshold value, in detector units
+ * \param min_snr The minimum signal to noise ratio for a peak
+ * \param min_pix_count The minimum number of pixels in a peak
+ * \param max_pix_count The maximum number of pixels in a peak
+ * \param local_bg_radius The averaging radius for background calculation
+ * \param min_res The minimum number of pixels out from the center
+ * \param max_res The maximum number of pixels out from the center
+ * \param use_saturated Whether saturated peaks should be considered
+ *
+ * Runs the peakfinder8 peak search algorithm. This is a thin wrapper which
+ * creates an empty \ref ImageFeatureList for \p image, then calls
+ * the actual \ref peakfinder8 function, found in \ref peakfinder8.h.
+ */
int search_peaks_peakfinder8(struct image *image, int max_n_peaks,
float threshold, float min_snr,
int min_pix_count, int max_pix_count,
@@ -560,8 +570,6 @@ int search_peaks_peakfinder8(struct image *image, int max_n_peaks,
image_feature_list_free(image->features);
}
image->features = image_feature_list_new();
- image->num_peaks = 0;
- image->num_saturated_peaks = 0;
return peakfinder8(image, max_n_peaks, threshold, min_snr,
min_pix_count, max_pix_count,
@@ -793,8 +801,6 @@ void validate_peaks(struct image *image, double min_snr,
// n, image_feature_count(flist), n_wtf, n_int, n_snr, n_sat);
image_feature_list_free(image->features);
image->features = flist;
- image->num_saturated_peaks = n_sat;
- image->num_peaks = image_feature_count(flist);
}
diff --git a/libcrystfel/src/peaks.h b/libcrystfel/src/peaks.h
index a82d8d25..b08defcd 100644
--- a/libcrystfel/src/peaks.h
+++ b/libcrystfel/src/peaks.h
@@ -44,6 +44,11 @@
extern "C" {
#endif
+/**
+ * \file peaks.h
+ * Peak search functions
+ */
+
extern int *make_BgMask(struct image *image, struct panel *p, double ir_inn);
extern void search_peaks(struct image *image, float threshold,
diff --git a/libcrystfel/src/predict-refine.c b/libcrystfel/src/predict-refine.c
index 6b151d48..11f4b630 100644
--- a/libcrystfel/src/predict-refine.c
+++ b/libcrystfel/src/predict-refine.c
@@ -42,6 +42,9 @@
#include "cell-utils.h"
+/** \file predict-refine.h */
+
+
/* Maximum number of iterations of NLSq to do for each image per macrocycle. */
#define MAX_CYCLES (10)
diff --git a/libcrystfel/src/predict-refine.h b/libcrystfel/src/predict-refine.h
index fe700f47..5b3c05e1 100644
--- a/libcrystfel/src/predict-refine.h
+++ b/libcrystfel/src/predict-refine.h
@@ -38,6 +38,11 @@
struct image;
+/**
+ * \file predict-refine.h
+ * Prediction refinement: refinement of indexing solutions before integration.
+ */
+
extern int refine_prediction(struct image *image, Crystal *cr);
extern int refine_radius(Crystal *cr, struct image *image);
diff --git a/libcrystfel/src/rational.c b/libcrystfel/src/rational.c
new file mode 100644
index 00000000..5138c3e9
--- /dev/null
+++ b/libcrystfel/src/rational.c
@@ -0,0 +1,647 @@
+/*
+ * rational.c
+ *
+ * A small rational number library
+ *
+ * Copyright © 2019 Deutsches Elektronen-Synchrotron DESY,
+ * a research centre of the Helmholtz Association.
+ *
+ * Authors:
+ * 2019 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 <string.h>
+#include <assert.h>
+#include <locale.h>
+
+#include "rational.h"
+#include "integer_matrix.h"
+#include "utils.h"
+
+/** \file rational.h */
+
+/* Eucliden algorithm for finding greatest common divisor */
+static signed int gcd(signed int a, signed int b)
+{
+ while ( b != 0 ) {
+ signed int t = b;
+ b = a % b;
+ a = t;
+ }
+ return a;
+}
+
+
+static void squish(Rational *rt)
+{
+ signed int g;
+
+ if ( rt->num == 0 ) {
+ rt->den = 1;
+ return;
+ }
+
+ g = gcd(rt->num, rt->den);
+ assert(g != 0);
+ rt->num /= g;
+ rt->den /= g;
+
+ if ( rt->den < 0 ) {
+ rt->num = -rt->num;
+ rt->den = -rt->den;
+ }
+}
+
+
+Rational rtnl_zero()
+{
+ Rational r;
+ r.num = 0;
+ r.den = 1;
+ return r;
+}
+
+
+Rational rtnl(signed long long int num, signed long long int den)
+{
+ Rational r;
+ r.num = num;
+ r.den = den;
+ squish(&r);
+ return r;
+}
+
+
+double rtnl_as_double(Rational r)
+{
+ return (double)r.num/r.den;
+}
+
+
+static void overflow(long long int c, long long int a, long long int b)
+{
+ setlocale(LC_ALL, "");
+ ERROR("Overflow detected in rational number library.\n");
+ ERROR("%'lli < %'lli * %'lli\n", c, a, b);
+ abort();
+}
+
+
+static void check_overflow(long long int c, long long int a, long long int b)
+{
+ if ( (a==0) || (b==0) ) {
+ if ( c != 0 ) overflow(c,a,b);
+ } else if ( (llabs(c) < llabs(a)) || (llabs(c) < llabs(b)) ) {
+ overflow(c,a,b);
+ }
+}
+
+
+Rational rtnl_mul(Rational a, Rational b)
+{
+ Rational r;
+ r.num = a.num * b.num;
+ r.den = a.den * b.den;
+ check_overflow(r.num, a.num, b.num);
+ check_overflow(r.den, a.den, b.den);
+ squish(&r);
+ return r;
+}
+
+
+Rational rtnl_div(Rational a, Rational b)
+{
+ signed int t = b.num;
+ b.num = b.den;
+ b.den = t;
+ return rtnl_mul(a, b);
+}
+
+
+Rational rtnl_add(Rational a, Rational b)
+{
+ Rational r, trt1, trt2;
+
+ trt1.num = a.num * b.den;
+ trt2.num = b.num * a.den;
+ check_overflow(trt1.num, a.num, b.den);
+ check_overflow(trt2.num, b.num, a.den);
+
+ trt1.den = a.den * b.den;
+ trt2.den = trt1.den;
+ check_overflow(trt1.den, a.den, b.den);
+
+ r.num = trt1.num + trt2.num;
+ r.den = trt1.den;
+ squish(&r);
+ return r;
+}
+
+
+Rational rtnl_sub(Rational a, Rational b)
+{
+ b.num = -b.num;
+ return rtnl_add(a, b);
+}
+
+
+/* -1, 0 +1 respectively for a<b, a==b, a>b */
+signed int rtnl_cmp(Rational a, Rational b)
+{
+ Rational trt1, trt2;
+
+ trt1.num = a.num * b.den;
+ trt2.num = b.num * a.den;
+
+ trt1.den = a.den * b.den;
+ trt2.den = a.den * b.den;
+
+ if ( trt1.num > trt2.num ) return +1;
+ if ( trt1.num < trt2.num ) return -1;
+ return 0;
+}
+
+
+Rational rtnl_abs(Rational a)
+{
+ Rational r = a;
+ squish(&r);
+ if ( r.num < 0 ) r.num = -r.num;
+ return r;
+}
+
+
+/**
+ * \param rt A \ref Rational
+ *
+ * Formats \p rt as a string
+ *
+ * \returns A string which should be freed by the caller
+ */
+char *rtnl_format(Rational rt)
+{
+ char *v = malloc(32);
+ if ( v == NULL ) return NULL;
+ if ( rt.den == 1 ) {
+ snprintf(v, 31, "%lli", rt.num);
+ } else {
+ snprintf(v, 31, "%lli/%lli", rt.num, rt.den);
+ }
+ return v;
+}
+
+
+Rational *rtnl_list(signed int num_min, signed int num_max,
+ signed int den_min, signed int den_max,
+ int *pn)
+{
+ signed int num, den;
+ Rational *list;
+ int n = 0;
+
+ list = malloc((1+num_max-num_min)*(1+den_max-den_min)*sizeof(Rational));
+ if ( list == NULL ) return NULL;
+
+ for ( num=num_min; num<=num_max; num++ ) {
+ for ( den=den_min; den<=den_max; den++ ) {
+
+ Rational r = rtnl(num, den);
+
+ /* Denominator zero? */
+ if ( den == 0 ) continue;
+
+ /* Same as last entry? */
+ if ( (n>0) && (rtnl_cmp(list[n-1], r)==0) ) continue;
+
+ /* Can be reduced? */
+ if ( gcd(num, den) != 1 ) continue;
+
+ list[n++] = r;
+ }
+ }
+ *pn = n;
+ return list;
+}
+
+
+struct _rationalmatrix
+{
+ unsigned int rows;
+ unsigned int cols;
+ Rational *v;
+};
+
+
+/**
+ * \param rows Number of rows that the new matrix is to have
+ * \param cols Number of columns that the new matrix is to have
+ *
+ * Allocates a new \ref RationalMatrix with all elements set to zero.
+ *
+ * \returns A new \ref RationalMatrix, or NULL on error.
+ **/
+RationalMatrix *rtnl_mtx_new(unsigned int rows, unsigned int cols)
+{
+ RationalMatrix *m;
+ int i;
+
+ m = malloc(sizeof(RationalMatrix));
+ if ( m == NULL ) return NULL;
+
+ m->v = calloc(rows*cols, sizeof(Rational));
+ if ( m->v == NULL ) {
+ free(m);
+ return NULL;
+ }
+
+ m->rows = rows;
+ m->cols = cols;
+
+ for ( i=0; i<m->rows*m->cols; i++ ) {
+ m->v[i] = rtnl_zero();
+ }
+
+ return m;
+}
+
+
+RationalMatrix *rtnl_mtx_identity(int rows)
+{
+ int i;
+ RationalMatrix *m = rtnl_mtx_new(rows, rows);
+ for ( i=0; i<rows; i++ ) {
+ rtnl_mtx_set(m, i, i, rtnl(1,1));
+ }
+ return m;
+}
+
+
+RationalMatrix *rtnl_mtx_copy(const RationalMatrix *m)
+{
+ RationalMatrix *n;
+ int i;
+
+ n = rtnl_mtx_new(m->rows, m->cols);
+ if ( n == NULL ) return NULL;
+
+ for ( i=0; i<m->rows*m->cols; i++ ) {
+ n->v[i] = m->v[i];
+ }
+
+ return n;
+}
+
+
+Rational rtnl_mtx_get(const RationalMatrix *m, int i, int j)
+{
+ assert(m != NULL);
+ return m->v[j+m->cols*i];
+}
+
+
+void rtnl_mtx_set(const RationalMatrix *m, int i, int j, Rational v)
+{
+ assert(m != NULL);
+ m->v[j+m->cols*i] = v;
+}
+
+
+RationalMatrix *rtnl_mtx_from_intmat(const IntegerMatrix *m)
+{
+ RationalMatrix *n;
+ unsigned int rows, cols;
+ int i, j;
+
+ intmat_size(m, &rows, &cols);
+ n = rtnl_mtx_new(rows, cols);
+ if ( n == NULL ) return NULL;
+
+ for ( i=0; i<rows; i++ ) {
+ for ( j=0; j<cols; j++ ) {
+ rtnl_mtx_set(n, i, j, rtnl(intmat_get(m, i, j), 1));
+ }
+ }
+
+ return n;
+}
+
+
+IntegerMatrix *intmat_from_rtnl_mtx(const RationalMatrix *m)
+{
+ IntegerMatrix *n;
+ int i, j;
+
+ n = intmat_new(m->rows, m->cols);
+ if ( n == NULL ) return NULL;
+
+ for ( i=0; i<m->rows; i++ ) {
+ for ( j=0; j<m->cols; j++ ) {
+ Rational v = rtnl_mtx_get(m, i, j);
+ squish(&v);
+ if ( v.den != 1 ) {
+ ERROR("Rational matrix can't be converted to integers\n");
+ intmat_free(n);
+ return NULL;
+ }
+ intmat_set(n, i, j, v.num);
+ }
+ }
+
+ return n;
+}
+
+
+void rtnl_mtx_free(RationalMatrix *mtx)
+{
+ if ( mtx == NULL ) return;
+ free(mtx->v);
+ free(mtx);
+}
+
+
+/* rtnl_mtx_solve:
+ * @P: A %RationalMatrix
+ * @vec: An array of %Rational
+ * @ans: An array of %Rational in which to store the result
+ *
+ * Solves the matrix equation m*ans = vec, where @ans and @vec are
+ * vectors of %Rational.
+ *
+ * In this version, @m must be square.
+ *
+ * The number of columns in @m must equal the length of @ans, and the number
+ * of rows in @m must equal the length of @vec, but note that there is no way
+ * for this function to check that this is the case.
+ *
+ * Given that P is transformation of the unit cell axes (see ITA chapter 5.1),
+ * this function calculates the fractional coordinates of a point in the
+ * transformed axes, given its fractional coordinates in the original axes.
+ *
+ * Returns: non-zero on error
+ **/
+int transform_fractional_coords_rtnl(const RationalMatrix *P,
+ const Rational *ivec, Rational *ans)
+{
+ RationalMatrix *cm;
+ Rational *vec;
+ int h, k;
+ int i;
+
+ if ( P->rows != P->cols ) return 1;
+
+ /* Copy the matrix and vector because the calculation will
+ * be done in-place */
+ cm = rtnl_mtx_copy(P);
+ if ( cm == NULL ) return 1;
+
+ vec = malloc(cm->rows*sizeof(Rational));
+ if ( vec == NULL ) return 1;
+ for ( h=0; h<cm->rows; h++ ) vec[h] = ivec[h];
+
+ /* Gaussian elimination with partial pivoting */
+ h = 0;
+ k = 0;
+ while ( h<=cm->rows && k<=cm->cols ) {
+
+ int prow = 0;
+ Rational pval = rtnl_zero();
+ Rational t;
+
+ /* Find the row with the largest value in column k */
+ for ( i=h; i<cm->rows; i++ ) {
+ if ( rtnl_cmp(rtnl_abs(rtnl_mtx_get(cm, i, k)), pval) > 0 ) {
+ pval = rtnl_abs(rtnl_mtx_get(cm, i, k));
+ prow = i;
+ }
+ }
+
+ if ( rtnl_cmp(pval, rtnl_zero()) == 0 ) {
+ k++;
+ continue;
+ }
+
+ /* Swap 'prow' with row h */
+ for ( i=0; i<cm->cols; i++ ) {
+ t = rtnl_mtx_get(cm, h, i);
+ rtnl_mtx_set(cm, h, i, rtnl_mtx_get(cm, prow, i));
+ rtnl_mtx_set(cm, prow, i, t);
+ }
+ t = vec[prow];
+ vec[prow] = vec[h];
+ vec[h] = t;
+
+ /* Divide and subtract rows below */
+ for ( i=h+1; i<cm->rows; i++ ) {
+
+ int j;
+ Rational dval;
+
+ dval = rtnl_div(rtnl_mtx_get(cm, i, k),
+ rtnl_mtx_get(cm, h, k));
+
+ for ( j=0; j<cm->cols; j++ ) {
+ Rational t = rtnl_mtx_get(cm, i, j);
+ Rational p = rtnl_mul(dval, rtnl_mtx_get(cm, h, j));
+ t = rtnl_sub(t, p);
+ rtnl_mtx_set(cm, i, j, t);
+ }
+
+ /* Divide the right hand side as well */
+ Rational t = vec[i];
+ Rational p = rtnl_mul(dval, vec[h]);
+ vec[i] = rtnl_sub(t, p);
+ }
+
+ h++;
+ k++;
+
+
+ }
+
+ /* Back-substitution */
+ for ( i=cm->rows-1; i>=0; i-- ) {
+ int j;
+ Rational sum = rtnl_zero();
+ for ( j=i+1; j<cm->cols; j++ ) {
+ Rational av;
+ av = rtnl_mul(rtnl_mtx_get(cm, i, j), ans[j]);
+ sum = rtnl_add(sum, av);
+ }
+ sum = rtnl_sub(vec[i], sum);
+ ans[i] = rtnl_div(sum, rtnl_mtx_get(cm, i, i));
+ }
+
+ free(vec);
+ rtnl_mtx_free(cm);
+
+ return 0;
+}
+
+
+/**
+ * \param m A \ref RationalMatrix
+ *
+ * Prints \p m to stderr.
+ *
+ */
+void rtnl_mtx_print(const RationalMatrix *m)
+{
+ unsigned int i, j;
+
+ for ( i=0; i<m->rows; i++ ) {
+
+ fprintf(stderr, "[ ");
+ for ( j=0; j<m->cols; j++ ) {
+ char *v = rtnl_format(rtnl_mtx_get(m, i, j));
+ fprintf(stderr, "%4s ", v);
+ free(v);
+ }
+ fprintf(stderr, "]\n");
+ }
+}
+
+
+void rtnl_mtx_mtxmult(const RationalMatrix *A, const RationalMatrix *B,
+ RationalMatrix *ans)
+{
+ int i, j;
+
+ assert(ans->cols == B->cols);
+ assert(ans->rows == A->rows);
+ assert(A->cols == B->rows);
+
+ for ( i=0; i<ans->rows; i++ ) {
+ for ( j=0; j<ans->cols; j++ ) {
+ int k;
+ Rational sum = rtnl_zero();
+ for ( k=0; k<A->rows; k++ ) {
+ Rational add;
+ add = rtnl_mul(rtnl_mtx_get(A, i, k),
+ rtnl_mtx_get(B, k, j));
+ sum = rtnl_add(sum, add);
+ }
+ rtnl_mtx_set(ans, i, j, sum);
+ }
+ }
+}
+
+
+/* Given a "P-matrix" (see ITA chapter 5.1), calculate the fractional
+ * coordinates of point "vec" in the original axes, given its fractional
+ * coordinates in the transformed axes.
+ * To transform in the opposite direction, we would multiply by Q = P^-1.
+ * Therefore, this direction is the "easy way" and needs just a matrix
+ * multiplication. */
+void transform_fractional_coords_rtnl_inverse(const RationalMatrix *P,
+ const Rational *vec,
+ Rational *ans)
+{
+ int i, j;
+
+ for ( i=0; i<P->rows; i++ ) {
+ ans[i] = rtnl_zero();
+ for ( j=0; j<P->cols; j++ ) {
+ Rational add;
+ add = rtnl_mul(rtnl_mtx_get(P, i, j), vec[j]);
+ ans[i] = rtnl_add(ans[i], add);
+ }
+ }
+}
+
+
+static RationalMatrix *delete_row_and_column(const RationalMatrix *m,
+ unsigned int di, unsigned int dj)
+{
+ RationalMatrix *n;
+ unsigned int i, j;
+
+ n = rtnl_mtx_new(m->rows-1, m->cols-1);
+ if ( n == NULL ) return NULL;
+
+ for ( i=0; i<n->rows; i++ ) {
+ for ( j=0; j<n->cols; j++ ) {
+
+ Rational val;
+ unsigned int gi, gj;
+
+ gi = (i>=di) ? i+1 : i;
+ gj = (j>=dj) ? j+1 : j;
+ val = rtnl_mtx_get(m, gi, gj);
+ rtnl_mtx_set(n, i, j, val);
+
+ }
+ }
+
+ return n;
+}
+
+
+static Rational cofactor(const RationalMatrix *m,
+ unsigned int i, unsigned int j)
+{
+ RationalMatrix *n;
+ Rational t, C;
+
+ n = delete_row_and_column(m, i, j);
+ if ( n == NULL ) {
+ fprintf(stderr, "Failed to allocate matrix.\n");
+ return rtnl_zero();
+ }
+
+ /* -1 if odd, +1 if even */
+ t = (i+j) & 0x1 ? rtnl(-1, 1) : rtnl(1, 1);
+
+ C = rtnl_mul(t, rtnl_mtx_det(n));
+ rtnl_mtx_free(n);
+
+ return C;
+}
+
+
+
+Rational rtnl_mtx_det(const RationalMatrix *m)
+{
+ unsigned int i, j;
+ Rational det;
+
+ assert(m->rows == m->cols); /* Otherwise determinant doesn't exist */
+
+ if ( m->rows == 2 ) {
+ Rational a, b;
+ a = rtnl_mul(rtnl_mtx_get(m, 0, 0), rtnl_mtx_get(m, 1, 1));
+ b = rtnl_mul(rtnl_mtx_get(m, 0, 1), rtnl_mtx_get(m, 1, 0));
+ return rtnl_sub(a, b);
+ }
+
+ i = 0; /* Fixed */
+ det = rtnl_zero();
+ for ( j=0; j<m->cols; j++ ) {
+ Rational a;
+ a = rtnl_mul(rtnl_mtx_get(m, i, j), cofactor(m, i, j));
+ det = rtnl_add(det, a);
+ }
+
+ return det;
+}
diff --git a/libcrystfel/src/rational.h b/libcrystfel/src/rational.h
new file mode 100644
index 00000000..e67c603a
--- /dev/null
+++ b/libcrystfel/src/rational.h
@@ -0,0 +1,108 @@
+/*
+ * rational.h
+ *
+ * A small rational number library
+ *
+ * Copyright © 2019 Deutsches Elektronen-Synchrotron DESY,
+ * a research centre of the Helmholtz Association.
+ *
+ * Authors:
+ * 2019 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 RATIONAL_H
+#define RATIONAL_H
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+/**
+ * \file rational.h
+ * %Rational numbers (including rational matrices)
+ */
+
+/**
+ * The Rational is an opaque-ish data structure representing a rational number.
+ *
+ * "Opaque-ish" means that the structure isn't technically opaque, allowing you
+ * to assign and allocate them easily. But you shouldn't look at or set its
+ * contents, except by using the accessor functions.
+ **/
+typedef struct {
+ /* Private, don't modify */
+ signed long long int num;
+ signed long long int den;
+} Rational;
+
+
+/**
+ * The RationalMatrix is an opaque data structure representing a matrix of
+ * rational numbers.
+ **/
+typedef struct _rationalmatrix RationalMatrix;
+
+#include "integer_matrix.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+extern Rational rtnl_zero(void);
+extern Rational rtnl(signed long long int num, signed long long int den);
+extern double rtnl_as_double(Rational r);
+
+extern Rational rtnl_mul(Rational a, Rational b);
+extern Rational rtnl_div(Rational a, Rational b);
+extern Rational rtnl_add(Rational a, Rational b);
+extern Rational rtnl_sub(Rational a, Rational b);
+
+extern signed int rtnl_cmp(Rational a, Rational b);
+extern Rational rtnl_abs(Rational a);
+
+extern char *rtnl_format(Rational rt);
+
+extern Rational *rtnl_list(signed int num_min, signed int num_max,
+ signed int den_min, signed int den_max,
+ int *pn);
+
+extern RationalMatrix *rtnl_mtx_new(unsigned int rows, unsigned int cols);
+extern RationalMatrix *rtnl_mtx_copy(const RationalMatrix *m);
+extern Rational rtnl_mtx_get(const RationalMatrix *m, int i, int j);
+extern void rtnl_mtx_set(const RationalMatrix *m, int i, int j, Rational v);
+extern RationalMatrix *rtnl_mtx_from_intmat(const IntegerMatrix *m);
+extern RationalMatrix *rtnl_mtx_identity(int rows);
+extern IntegerMatrix *intmat_from_rtnl_mtx(const RationalMatrix *m);
+extern void rtnl_mtx_free(RationalMatrix *mtx);
+extern void rtnl_mtx_mtxmult(const RationalMatrix *A, const RationalMatrix *B,
+ RationalMatrix *ans);
+extern int transform_fractional_coords_rtnl(const RationalMatrix *P,
+ const Rational *ivec,
+ Rational *ans);
+extern void transform_fractional_coords_rtnl_inverse(const RationalMatrix *P,
+ const Rational *vec,
+ Rational *ans);
+extern void rtnl_mtx_print(const RationalMatrix *m);
+extern Rational rtnl_mtx_det(const RationalMatrix *m);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* RATIONAL_H */
diff --git a/libcrystfel/src/reflist-utils.c b/libcrystfel/src/reflist-utils.c
index 70548994..d514990a 100644
--- a/libcrystfel/src/reflist-utils.c
+++ b/libcrystfel/src/reflist-utils.c
@@ -43,19 +43,18 @@
#include "symmetry.h"
-/**
- * SECTION:reflist-utils
- * @short_description: Reflection list utilities
- * @title: RefList utilities
- * @section_id:
- * @see_also:
- * @include: "reflist-utils.h"
- * @Image:
- *
- * There are some utility functions associated with the core %RefList.
- **/
+/** \file reflist-utils.h
+ */
+/**
+ * Checks that the symmetry of \p list is indeed \p sym.
+ *
+ * \param list A list of reflections
+ * \param sym Symmetry of the reflection list
+ *
+ * \returns 0 if the symmetry is correct, otherwise 1
+ */
int check_list_symmetry(RefList *list, const SymOpList *sym)
{
Reflection *refl;
@@ -155,7 +154,7 @@ int find_equiv_in_list(RefList *list, signed int h, signed int k,
}
-/**
+/*
* Write the actual reflections to the file, no headers etc.
* Reflections which have a redundancy of zero will not be written.
* The resulting list can be read back with read_reflections_from_file().
@@ -202,19 +201,18 @@ static void write_reflections_to_file(FILE *fh, RefList *list)
/**
- * write_reflist_2:
- * @filename: Filename
- * @list: The reflection list to write
- * @sym: A %SymOpList describing the symmetry of the list
- *
- * This function writes the contents of @list to @file,
+ * This function writes the contents of list to file,
*
* Reflections which have a redundancy of zero will not be written.
*
* The resulting list can be read back with read_reflections_from_file() or
* read_reflections().
*
- * Returns: zero on success, non-zero on failure.
+ * \param filename Filename
+ * \param list The reflection list to write
+ * \param sym A %SymOpList describing the symmetry of the list
+ *
+ * \returns zero on success, non-zero on failure.
**/
int write_reflist_2(const char *filename, RefList *list, SymOpList *sym)
{
@@ -255,23 +253,22 @@ int write_reflist_2(const char *filename, RefList *list, SymOpList *sym)
/**
- * write_reflist:
- * @filename: Filename
- * @list: The reflection list to write
- *
- * This function writes the contents of @list to @file,
+ * This function writes the contents of list to file,
*
* Reflections which have a redundancy of zero will not be written.
*
* The resulting list can be read back with read_reflections_from_file() or
* read_reflections().
*
- * This is a convenience function which simply opens @filename and then calls
+ * This is a convenience function which simply opens filename and then calls
* write_reflections_to_file.
*
- * Deprecated: use write_reflist_2() instead.
+ * \deprecated Use write_reflist_2() instead.
+ *
+ * \param filename Filename
+ * \param list The reflection list to write
*
- * Returns: zero on success, non-zero on failure.
+ * \returns Zero on success, non-zero on failure.
**/
int write_reflist(const char *filename, RefList *list)
{
@@ -422,8 +419,8 @@ static RefList *read_reflections_from_file(FILE *fh, char **sym)
/**
* read_reflections_2:
- * @filename: Filename to read from
- * @sym: Pointer to a "char *" at which to store the symmetry
+ * \param filename: Filename to read from
+ * \param sym: Pointer to a "char *" at which to store the symmetry
*
* This function reads a reflection list from a file, including the
* symmetry from the header (e.g. "Symmetry: 4/mmm").
@@ -456,7 +453,7 @@ RefList *read_reflections_2(const char *filename, char **sym)
/**
* read_reflections:
- * @filename: Filename to read from
+ * \param filename: Filename to read from
*
* This function reads a reflection list from a file.
*
@@ -470,11 +467,11 @@ RefList *read_reflections(const char *filename)
/**
* asymmetric_indices:
- * @in: A %RefList
- * @sym: A %SymOpList
+ * \param in: A %RefList
+ * \param sym: A %SymOpList
*
- * This function creates a newly allocated copy of @in, but indexed using the
- * asymmetric indices according to @sym instead of the original indices. The
+ * This function creates a newly allocated copy of in, but indexed using the
+ * asymmetric indices according to sym instead of the original indices. The
* original indices are stored and can be retrieved using
* get_symmetric_indices() if required.
*
@@ -515,10 +512,10 @@ RefList *asymmetric_indices(RefList *in, const SymOpList *sym)
/**
* resolution_limits:
- * @list: A %RefList
- * @cell: A %UnitCell
- * @rmin: Place to store the minimum 1/d value
- * @rmax: Place to store the maximum 1/d value
+ * \param list: A %RefList
+ * \param cell: A %UnitCell
+ * \param rmin: Place to store the minimum 1/d value
+ * \param rmax: Place to store the maximum 1/d value
*
* This function calculates the minimum and maximum values of 1/d, where
* 2dsin(theta) = wavelength. The answers are in m^-1.
@@ -550,9 +547,9 @@ void resolution_limits(RefList *list, UnitCell *cell,
/**
* max_intensity:
- * @list: A %RefList
+ * \param list: A %RefList
*
- * Returns: The maximum intensity in @list.
+ * Returns: The maximum intensity in \p list.
**/
double max_intensity(RefList *list)
{
@@ -576,12 +573,12 @@ double max_intensity(RefList *list)
/**
* res_cutoff:
- * @list: A %RefList
- * @cell: A %UnitCell with which to calculate 1/d values for @list
- * @min: Minimum acceptable value of 1/d
- * @max: Maximum acceptable value of 1/d
+ * \param list: A %RefList
+ * \param cell: A %UnitCell with which to calculate 1/d values for \p list
+ * \param min: Minimum acceptable value of 1/d
+ * \param max: Maximum acceptable value of 1/d
*
- * Applies a resolution cutoff to @list, returning the new version and freeing
+ * Applies a resolution cutoff to \p list, returning the new version and freeing
* the old version.
*
* Returns: A new %RefList with resolution cutoff applied
@@ -619,7 +616,7 @@ RefList *res_cutoff(RefList *list, UnitCell *cell, double min, double max)
/**
* copy_reflist:
- * @list: A %RefList
+ * \param list: A %RefList
*
* Returns: A copy of %RefList.
**/
@@ -650,9 +647,9 @@ RefList *copy_reflist(RefList *list)
/**
* free_contribs:
- * @list: A %RefList
+ * \param list: A %RefList
*
- * Goes through @list and frees all the reflection contribution structures.
+ * Goes through \p list and frees all the reflection contribution structures.
**/
void free_contribs(RefList *list)
{
diff --git a/libcrystfel/src/reflist-utils.h b/libcrystfel/src/reflist-utils.h
index c955491a..68695ae2 100644
--- a/libcrystfel/src/reflist-utils.h
+++ b/libcrystfel/src/reflist-utils.h
@@ -31,8 +31,10 @@
#include <config.h>
#endif
+/** @cond */
#ifndef REFLIST_UTILS_H
#define REFLIST_UTILS_H
+/** @endcond */
#include "image.h"
#include "reflist.h"
@@ -43,8 +45,15 @@
extern "C" {
#endif
-#define REFLECTION_END_MARKER "End of reflections"
+/** \file reflist-utils.h
+ *
+ * Reflection list utility functions.
+ */
+/** @cond
+ * Used in stream.c as well, but not part of the API */
+#define REFLECTION_END_MARKER "End of reflections"
+/** @endcond */
extern int write_reflist(const char *filename, RefList *list);
extern int write_reflist_2(const char *filename, RefList *list, SymOpList *sym);
@@ -78,4 +87,6 @@ extern void reflist_add_command_and_version(RefList *list,
}
#endif
+/** @cond */
#endif /* REFLIST_UTILS_H */
+/** @endcond */
diff --git a/libcrystfel/src/reflist.c b/libcrystfel/src/reflist.c
index 9c8a6a14..ee601018 100644
--- a/libcrystfel/src/reflist.c
+++ b/libcrystfel/src/reflist.c
@@ -34,30 +34,7 @@
#include "reflist.h"
#include "utils.h"
-/**
- * SECTION:reflist
- * @short_description: The fast reflection list
- * @title: RefList
- * @section_id:
- * @see_also:
- * @include: "reflist.h"
- * @Image:
- *
- * The fast reflection list stores reflections in an RB-tree indexed
- * by the Miller indices h, k and l. Any reflection can be found in a
- * length of time which scales logarithmically with the number of reflections in
- * the list.
- *
- * A RefList can contain any number of reflections, and can store more than
- * one reflection with a given set of indices, for example when two distinct
- * reflections are to be stored according to their asymmetric indices.
- *
- * There are getters and setters which can be used to get and set values for an
- * individual reflection. The reflection list does not calculate any values,
- * only stores what it was given earlier. As such, you will need to carefully
- * examine which fields your prior processing steps have filled in.
- */
-
+/** \file reflist.h */
struct _refldata {
@@ -160,11 +137,9 @@ static Reflection *new_node(unsigned int serial)
/**
- * reflist_new:
- *
* Creates a new reflection list.
*
- * Returns: the new reflection list, or NULL on error.
+ * \returns the new reflection list, or NULL on error.
*/
RefList *reflist_new()
{
@@ -181,10 +156,9 @@ RefList *reflist_new()
/**
- * reflection_new:
- * @h: The h index of the new reflection
- * @k: The k index of the new reflection
- * @l: The l index of the new reflection
+ * \param h The h index of the new reflection
+ * \param k The k index of the new reflection
+ * \param l The l index of the new reflection
*
* Creates a new individual reflection. You'll probably want to use
* add_refl_to_list() at some later point.
@@ -196,8 +170,7 @@ Reflection *reflection_new(signed int h, signed int k, signed int l)
/**
- * reflection_free:
- * @refl: The reflection to free.
+ * \param refl: The reflection to free.
*
* Destroys an individual reflection.
*/
@@ -222,8 +195,7 @@ static void recursive_free(Reflection *refl)
/**
- * reflist_free:
- * @list: The reflection list to free.
+ * \param list: The reflection list to free.
*
* Destroys a reflection list.
*/
@@ -241,18 +213,17 @@ void reflist_free(RefList *list)
/********************************** Search ************************************/
/**
- * find_refl:
- * @list: The reflection list to search in
- * @h: The 'h' index to search for
- * @k: The 'k' index to search for
- * @l: The 'l' index to search for
+ * \param list: The reflection list to search in
+ * \param h: The 'h' index to search for
+ * \param k: The 'k' index to search for
+ * \param l: The 'l' index to search for
*
* This function finds the first reflection in 'list' with the given indices.
*
* Since a %RefList can contain multiple reflections with the same indices, you
* may need to use next_found_refl() to get the other reflections.
*
- * Returns: The found reflection, or NULL if no reflection with the given
+ * \returns The found reflection, or NULL if no reflection with the given
* indices could be found.
**/
Reflection *find_refl(const RefList *list,
@@ -302,13 +273,12 @@ Reflection *find_refl(const RefList *list,
/**
- * next_found_refl:
- * @refl: A reflection returned by find_refl() or next_found_refl()
+ * \param refl: A reflection returned by find_refl() or next_found_refl()
*
- * This function returns the next reflection in @refl's list with the same
+ * This function returns the next reflection in \p refl's list with the same
* indices.
*
- * Returns: The found reflection, or NULL if there are no more reflections with
+ * \returns The found reflection, or NULL if there are no more reflections with
* the same indices.
**/
Reflection *next_found_refl(Reflection *refl)
@@ -323,10 +293,9 @@ Reflection *next_found_refl(Reflection *refl)
/**
- * 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
+ * \param refl: Reflection
+ * \param fs: Location at which to store the fast scan offset of the reflection
+ * \param ss: Location at which to store the slow scan offset of the reflection
*
**/
void get_detector_pos(const Reflection *refl, double *fs, double *ss)
@@ -337,10 +306,9 @@ void get_detector_pos(const Reflection *refl, double *fs, double *ss)
/**
- * get_panel:
- * @refl: A %Reflection
+ * \param refl: Reflection
*
- * Returns: the panel which the reflection appears on
+ * \returns the panel which the reflection appears on
*
**/
struct panel *get_panel(const Reflection *refl)
@@ -350,11 +318,10 @@ struct panel *get_panel(const Reflection *refl)
/**
- * get_indices:
- * @refl: A %Reflection
- * @h: Location at which to store the 'h' index of the reflection
- * @k: Location at which to store the 'k' index of the reflection
- * @l: Location at which to store the 'l' index of the reflection
+ * \param refl: Reflection
+ * \param h: Location at which to store the 'h' index of the reflection
+ * \param k: Location at which to store the 'k' index of the reflection
+ * \param l: Location at which to store the 'l' index of the reflection
*
**/
void get_indices(const Reflection *refl,
@@ -367,11 +334,10 @@ void get_indices(const Reflection *refl,
/**
- * get_symmetric_indices:
- * @refl: A %Reflection
- * @hs: Location at which to store the 'h' index of the reflection
- * @ks: Location at which to store the 'k' index of the reflection
- * @ls: Location at which to store the 'l' index of the reflection
+ * \param refl: Reflection
+ * \param hs: Location at which to store the 'h' index of the reflection
+ * \param ks: Location at which to store the 'k' index of the reflection
+ * \param ls: Location at which to store the 'l' index of the reflection
*
* This function gives the symmetric indices, that is, the "real" indices before
* squashing down to the asymmetric reciprocal unit. This may be useful if the
@@ -390,10 +356,9 @@ void get_symmetric_indices(const Reflection *refl,
/**
- * get_partiality:
- * @refl: A %Reflection
+ * \param refl: Reflection
*
- * Returns: The partiality of the reflection. See get_lorentz().
+ * \returns The partiality of the reflection. See get_lorentz().
**/
double get_partiality(const Reflection *refl)
{
@@ -402,10 +367,9 @@ double get_partiality(const Reflection *refl)
/**
- * get_lorentz:
- * @refl: A %Reflection
+ * \param refl: Reflection
*
- * Returns: The Lorentz factor for the reflection. To "scale up" a partial
+ * \returns The Lorentz factor for the reflection. To "scale up" a partial
* reflection, divide by this multiplied by the partiality.
**/
double get_lorentz(const Reflection *refl)
@@ -415,10 +379,9 @@ double get_lorentz(const Reflection *refl)
/**
- * get_intensity:
- * @refl: A %Reflection
+ * \param refl: Reflection
*
- * Returns: The intensity of the reflection.
+ * \returns The intensity of the reflection.
**/
double get_intensity(const Reflection *refl)
{
@@ -428,9 +391,9 @@ double get_intensity(const Reflection *refl)
/**
* get_khalf
- * @refl: A %Reflection
+ * \param refl: Reflection
*
- * Returns: the wavenumber at the centre of the reflection
+ * \returns the wavenumber at the centre of the reflection
*
**/
double get_khalf(const Reflection *refl)
@@ -442,10 +405,9 @@ double get_khalf(const Reflection *refl)
/**
- * get_kpred:
- * @refl: A %Reflection
+ * \param refl: Reflection
*
- * Returns: the wavenumber which should be used for prediction of this reflection
+ * \returns the wavenumber which should be used for prediction of this reflection
*
**/
double get_kpred(const Reflection *refl)
@@ -455,10 +417,9 @@ double get_kpred(const Reflection *refl)
/**
- * get_exerr:
- * @refl: A %Reflection
+ * \param refl: Reflection
*
- * Returns: the excitation error (in m^-1) for this reflection
+ * \returns the excitation error (in m^-1) for this reflection
*
**/
double get_exerr(const Reflection *refl)
@@ -468,8 +429,7 @@ double get_exerr(const Reflection *refl)
/**
- * get_redundancy:
- * @refl: A %Reflection
+ * \param refl: Reflection
*
* The redundancy of the reflection is the number of measurements that have been
* made of it. Note that a redundancy of zero may have a special meaning, such
@@ -478,7 +438,7 @@ double get_exerr(const Reflection *refl)
* copies of the reflection in the list. The total number of reflection
* measurements should always be the sum of the redundancies in the entire list.
*
- * Returns: the number of measurements of this reflection.
+ * \returns the number of measurements of this reflection.
*
**/
int get_redundancy(const Reflection *refl)
@@ -488,10 +448,9 @@ int get_redundancy(const Reflection *refl)
/**
- * get_esd_intensity:
- * @refl: A %Reflection
+ * \param refl: Reflection
*
- * Returns: the standard error in the intensity measurement (as returned by
+ * \returns the standard error in the intensity measurement (as returned by
* get_intensity()) for this reflection.
*
**/
@@ -502,11 +461,10 @@ double get_esd_intensity(const Reflection *refl)
/**
- * get_phase:
- * @refl: A %Reflection
- * @have_phase: Place to store a non-zero value if the phase is set, or NULL.
+ * \param refl: Reflection
+ * \param have_phase: Place to store a non-zero value if the phase is set, or NULL.
*
- * Returns: the phase for this reflection.
+ * \returns the phase for this reflection.
*
**/
double get_phase(const Reflection *refl, int *have_phase)
@@ -517,10 +475,9 @@ double get_phase(const Reflection *refl, int *have_phase)
/**
- * get_peak:
- * @refl: A %Reflection
+ * \param refl: Reflection
*
- * Returns: the peak height (value of the highest pixel, before background
+ * \returns the peak height (value of the highest pixel, before background
* subtraction) for this reflection.
*
**/
@@ -531,10 +488,9 @@ double get_peak(const Reflection *refl)
/**
- * get_mean_bg:
- * @refl: A %Reflection
+ * \param refl: Reflection
*
- * Returns: the mean background level for this reflection.
+ * \returns the mean background level for this reflection.
*
**/
double get_mean_bg(const Reflection *refl)
@@ -544,13 +500,12 @@ double get_mean_bg(const Reflection *refl)
/**
- * get_temp1:
- * @refl: A %Reflection
+ * \param refl: Reflection
*
* The temporary values can be used according to the needs of the calling
* program.
*
- * Returns: the first temporary value for this reflection.
+ * \returns the first temporary value for this reflection.
*
**/
double get_temp1(const Reflection *refl)
@@ -560,13 +515,12 @@ double get_temp1(const Reflection *refl)
/**
- * get_temp2:
- * @refl: A %Reflection
+ * \param refl: Reflection
*
* The temporary values can be used according to the needs of the calling
* program.
*
- * Returns: the second temporary value for this reflection.
+ * \returns the second temporary value for this reflection.
*
**/
double get_temp2(const Reflection *refl)
@@ -576,13 +530,12 @@ double get_temp2(const Reflection *refl)
/**
- * get_flag:
- * @refl: A %Reflection
+ * \param refl: Reflection
*
* The integer flag value can be used according to the needs of the calling
* program.
*
- * Returns: the flag for this reflection.
+ * \returns the flag for this reflection.
*
**/
int get_flag(const Reflection *refl)
@@ -592,10 +545,9 @@ int get_flag(const Reflection *refl)
/**
- * get_contributions:
- * @refl: A %Reflection
+ * \param refl: Reflection
*
- * Returns: the reflection's contribution list
+ * \returns the reflection's contribution list
*
**/
struct reflection_contributions *get_contributions(const Reflection *refl)
@@ -606,9 +558,8 @@ struct reflection_contributions *get_contributions(const Reflection *refl)
/********************************** Setters ***********************************/
/**
- * copy_data:
- * @to: %Reflection to copy data into
- * @from: %Reflection to copy data from
+ * \param to: %Reflection to copy data into
+ * \param from: %Reflection to copy data from
*
* This function is used to copy the data (which is everything listed above in
* the list of getters and setters, apart from the indices themselves) from one
@@ -624,10 +575,9 @@ void copy_data(Reflection *to, const Reflection *from)
/**
- * set_detector_pos:
- * @refl: A %Reflection
- * @fs: The fast scan offset of the reflection
- * @ss: The slow scan offset of the reflection
+ * \param refl: Reflection
+ * \param fs: The fast scan offset of the reflection
+ * \param ss: The slow scan offset of the reflection
*
**/
void set_detector_pos(Reflection *refl, double fs, double ss)
@@ -638,9 +588,8 @@ void set_detector_pos(Reflection *refl, double fs, double ss)
/**
- * set_panel:
- * @refl: A %Reflection
- * @p: Pointer to the panel structure on which the reflection appears
+ * \param refl: Reflection
+ * \param p: Pointer to the panel structure on which the reflection appears
*
* Note that the pointer will be stored, not the contents of the structure.
*
@@ -652,9 +601,8 @@ void set_panel(Reflection *refl, struct panel *p)
/**
- * set_khalf:
- * @refl: A %Reflection
- * @khalf: The wavenumber at which the reflection should be predicted
+ * \param refl: Reflection
+ * \param khalf: The wavenumber at which the reflection should be predicted
*
* Sets the wavenumber at the centre of the reflection.
**/
@@ -665,9 +613,8 @@ void set_khalf(Reflection *refl, double khalf)
/**
- * set_kpred:
- * @refl: A %Reflection
- * @kpred: The wavenumber at which the reflection should be predicted
+ * \param refl: Reflection
+ * \param kpred: The wavenumber at which the reflection should be predicted
*
* Sets the wavenumber at which the reflection should be predicted.
* Used by predict_to_res() and update_predictions()
@@ -679,9 +626,8 @@ void set_kpred(Reflection *refl, double kpred)
/**
- * set_exerr:
- * @refl: A %Reflection
- * @exerr: The excitation error for the reflection
+ * \param refl: Reflection
+ * \param exerr: The excitation error for the reflection
*
**/
void set_exerr(Reflection *refl, double exerr)
@@ -691,9 +637,8 @@ void set_exerr(Reflection *refl, double exerr)
/**
- * set_intensity:
- * @refl: A %Reflection
- * @p: The partiality for the reflection.
+ * \param refl: Reflection
+ * \param p: The partiality for the reflection.
*
* Set the partiality for the reflection. See set_lorentz().
**/
@@ -703,9 +648,8 @@ void set_partiality(Reflection *refl, double p)
}
/**
- * set_lorentz:
- * @refl: A %Reflection
- * @L: The Lorentz factor for the reflection.
+ * \param refl: Reflection
+ * \param L: The Lorentz factor for the reflection.
*
* Set the Lorentz factor for the reflection. To "scale up" a partial
* reflection, divide by this multiplied by the partiality.
@@ -717,9 +661,8 @@ void set_lorentz(Reflection *refl, double L)
/**
- * set_intensity:
- * @refl: A %Reflection
- * @intensity: The intensity for the reflection.
+ * \param refl: Reflection
+ * \param intensity: The intensity for the reflection.
*
* Set the intensity for the reflection.
**/
@@ -730,9 +673,8 @@ void set_intensity(Reflection *refl, double intensity)
/**
- * set_redundancy:
- * @refl: A %Reflection
- * @red: New redundancy for the reflection
+ * \param refl: Reflection
+ * \param red: New redundancy for the reflection
*
* The redundancy of the reflection is the number of measurements that have been
* made of it. Note that a redundancy of zero may have a special meaning, such
@@ -749,9 +691,8 @@ void set_redundancy(Reflection *refl, int red)
/**
- * set_esd_intensity:
- * @refl: A %Reflection
- * @esd: New standard error for this reflection's intensity measurement
+ * \param refl: Reflection
+ * \param esd: New standard error for this reflection's intensity measurement
*
**/
void set_esd_intensity(Reflection *refl, double esd)
@@ -761,9 +702,8 @@ void set_esd_intensity(Reflection *refl, double esd)
/**
- * set_phase:
- * @refl: A %Reflection
- * @phase: New phase for the reflection
+ * \param refl: Reflection
+ * \param phase: New phase for the reflection
*
**/
void set_phase(Reflection *refl, double phase)
@@ -774,9 +714,8 @@ void set_phase(Reflection *refl, double phase)
/**
- * set_peak:
- * @refl: A %Reflection
- * @peak: New peak height for the reflection
+ * \param refl: Reflection
+ * \param peak: New peak height for the reflection
*
**/
void set_peak(Reflection *refl, double peak)
@@ -786,9 +725,8 @@ void set_peak(Reflection *refl, double peak)
/**
- * set_mean_bg:
- * @refl: A %Reflection
- * @mean_bg: New peak height for the reflection
+ * \param refl: Reflection
+ * \param mean_bg: New peak height for the reflection
*
**/
void set_mean_bg(Reflection *refl, double mean_bg)
@@ -798,11 +736,10 @@ void set_mean_bg(Reflection *refl, double mean_bg)
/**
- * set_symmetric_indices:
- * @refl: A %Reflection
- * @hs: The 'h' index of the reflection
- * @ks: The 'k' index of the reflection
- * @ls: The 'l' index of the reflection
+ * \param refl: Reflection
+ * \param hs: The 'h' index of the reflection
+ * \param ks: The 'k' index of the reflection
+ * \param ls: The 'l' index of the reflection
*
* This function gives the symmetric indices, that is, the "real" indices before
* squashing down to the asymmetric reciprocal unit. This may be useful if the
@@ -820,9 +757,8 @@ void set_symmetric_indices(Reflection *refl,
/**
- * set_temp1
- * @refl: A %Reflection
- * @temp: New temporary value for the reflection
+ * \param refl: A \ref Reflection
+ * \param temp: New temporary value for the reflection
*
* The temporary values can be used according to the needs of the calling
* program.
@@ -835,9 +771,8 @@ void set_temp1(Reflection *refl, double temp)
/**
- * set_temp2
- * @refl: A %Reflection
- * @temp: New temporary value for the reflection
+ * \param refl: A \ref Reflection
+ * \param temp: New temporary value for the reflection
*
* The temporary values can be used according to the needs of the calling
* program.
@@ -850,11 +785,10 @@ void set_temp2(Reflection *refl, double temp)
/**
- * set_flag
- * @refl: A %Reflection
- * @flag: New flag value
+ * \param refl: A \ref Reflection
+ * \param flag: New flag value
*
- * @flag is an integer value which can be used according to the needs of the
+ * \param flag is an integer value which can be used according to the needs of the
* calling program.
*
**/
@@ -865,9 +799,8 @@ void set_flag(Reflection *refl, int flag)
/**
- * set_contributions:
- * @refl: A %Reflection
- * @contribs: Pointer to the contribution list
+ * \param refl: Reflection
+ * \param contribs: Pointer to the contribution list
*
* Note that the pointer will be stored, not the contents of the structure.
*
@@ -973,17 +906,16 @@ static void add_to_list(RefList *list, Reflection *new,
/**
- * add_refl
- * @list: A %RefList
- * @h: The 'h' index of the reflection
- * @k: The 'k' index of the reflection
- * @l: The 'l' index of the reflection
+ * \param list: A %RefList
+ * \param h: The 'h' index of the reflection
+ * \param k: The 'k' index of the reflection
+ * \param l: The 'l' index of the reflection
*
- * Adds a new reflection to @list. Note that the implementation allows there to
+ * Adds a new reflection to \p list. Note that the implementation allows there to
* be multiple reflections with the same indices in the list, so this function
* should succeed even if the given indices already feature in the list.
*
- * Returns: The newly created reflection, or NULL on failure.
+ * \returns The newly created reflection, or NULL on failure.
*
**/
Reflection *add_refl(RefList *list, signed int h, signed int k, signed int l)
@@ -1004,11 +936,10 @@ Reflection *add_refl(RefList *list, signed int h, signed int k, signed int l)
/**
- * add_refl_to_list
- * @refl: A %Reflection
- * @list: A %RefList
+ * \param refl: Reflection
+ * \param list: A %RefList
*
- * Adds a @refl to @list.
+ * Adds \p refl to \p list.
*
**/
void add_refl_to_list(Reflection *refl, RefList *list)
@@ -1034,15 +965,14 @@ struct _reflistiterator {
/**
- * first_refl:
- * @list: A %RefList to iterate over
- * @piter: Address at which to store a %RefListIterator
+ * \param list: A %RefList to iterate over
+ * \param piter: Address at which to store a %RefListIterator
*
* This function sets up the state required for iteration over the entire list,
* and then returns the first reflection in the list. An iterator object will
* be created and its address stored at the location given in piter.
*
- * Returns: the first reflection in the list.
+ * \returns the first reflection in the list.
*
**/
Reflection *first_refl(RefList *list, RefListIterator **piter)
@@ -1089,14 +1019,13 @@ Reflection *first_refl(RefList *list, RefListIterator **piter)
/**
- * first_refl_const:
- * @list: A %RefList to iterate over
- * @piter: Address at which to store a %RefListIterator
+ * \param list: A %RefList to iterate over
+ * \param piter: Address at which to store a %RefListIterator
*
* As first_refl(), except returns a const %Reflection.
* Use this when you don't need to modify any of the reflections.
*
- * Returns: the first reflection in the list.
+ * \returns the first reflection in the list.
*
**/
const Reflection *first_refl_const(const RefList *list, RefListIterator **piter)
@@ -1143,14 +1072,13 @@ const Reflection *first_refl_const(const RefList *list, RefListIterator **piter)
/**
- * next_refl:
- * @refl: A reflection
- * @iter: A %RefListIterator
+ * \param refl: A reflection
+ * \param iter: A %RefListIterator
*
* This function looks up the next reflection in the list that was given earlier
* to first_refl().
*
- * Returns: the next reflection in the list, or NULL if no more.
+ * \returns the next reflection in the list, or NULL if no more.
*
**/
Reflection *next_refl(Reflection *refl, RefListIterator *iter)
@@ -1197,14 +1125,13 @@ Reflection *next_refl(Reflection *refl, RefListIterator *iter)
/**
- * next_refl_const:
- * @refl: A reflection
- * @iter: A %RefListIterator
+ * \param refl: A reflection
+ * \param iter: A %RefListIterator
*
* As next_refl(), except returns a const %Reflection.
* Use this when you don't need to modify any of the reflections.
*
- * Returns: the next reflection in the list, or NULL if no more.
+ * \returns the next reflection in the list, or NULL if no more.
*
**/
const Reflection *next_refl_const(const Reflection *refl, RefListIterator *iter)
@@ -1286,10 +1213,9 @@ static int recursive_count(Reflection *refl)
/**
- * num_reflections:
- * @list: A %RefList
+ * \param list: A %RefList
*
- * Returns: the number of reflections in @list.
+ * \returns the number of reflections in \p list.
*
**/
int num_reflections(RefList *list)
@@ -1299,13 +1225,12 @@ int num_reflections(RefList *list)
/**
- * tree_depth:
- * @list: A %RefList
+ * \param list: A %RefList
*
* If the depth of the tree is more than about 20, access to the list will be
* slow. This should never happen.
*
- * Returns: the depth of the RB-tree used internally to represent @list.
+ * \returns the depth of the RB-tree used internally to represent \p list.
*
**/
int tree_depth(RefList *list)
@@ -1315,8 +1240,7 @@ int tree_depth(RefList *list)
/**
- * lock_reflection:
- * @refl: A %Reflection
+ * \param refl: Reflection
*
* Acquires a lock on the reflection.
*/
@@ -1327,8 +1251,7 @@ void lock_reflection(Reflection *refl)
/**
- * unlock_reflection:
- * @refl: A %Reflection
+ * \param refl: Reflection
*
* Releases a lock on the reflection.
*/
@@ -1346,10 +1269,9 @@ static void reflist_set_notes(RefList *reflist, const char *notes)
/**
- * reflist_get_notes:
- * @reflist: A %RefList
+ * \param reflist: Reflection list
*
- * Returns the notes field for @reflist, or NULL if there are no notes.
+ * \returns the notes field for \p reflist, or NULL if there are no notes.
* See reflist_add_notes() for more details.
*/
const char *reflist_get_notes(RefList *reflist)
@@ -1359,11 +1281,10 @@ const char *reflist_get_notes(RefList *reflist)
/**
- * reflist_add_notes:
- * @reflist: A %RefList
- * @notes_add: Notes to add
+ * \param reflist: Reflection list
+ * \param notes_add: Notes to add
*
- * Appends the string @notes_add to the notes field for @reflist. The notes
+ * Appends the string \p notes_add to the notes field for \p reflist. The notes
* will be stored in the reflection list file by, e.g.,
* write_reflist(), and are meant to be for humans to read.
* Possible uses include making a record of the command line arguments used to
diff --git a/libcrystfel/src/reflist.h b/libcrystfel/src/reflist.h
index 30bbaa28..110280d6 100644
--- a/libcrystfel/src/reflist.h
+++ b/libcrystfel/src/reflist.h
@@ -38,11 +38,26 @@
#define GET_K(serial) ((((serial) & 0x000ffc00)>>10)-512)
#define GET_L(serial) (((serial) & 0x000003ff)-512)
+/**
+ * \file reflist.h
+ * The fast reflection list stores reflections in an RB-tree indexed
+ * by the Miller indices h, k and l. Any reflection can be found in a
+ * length of time which scales logarithmically with the number of reflections in
+ * the list.
+ *
+ * A RefList can contain any number of reflections, and can store more than
+ * one reflection with a given set of indices, for example when two distinct
+ * reflections are to be stored according to their asymmetric indices.
+ *
+ * There are getters and setters which can be used to get and set values for an
+ * individual reflection. The reflection list does not calculate any values,
+ * only stores what it was given earlier. As such, you will need to carefully
+ * examine which fields your prior processing steps have filled in.
+ */
+
/**
- * RefList:
- *
- * A %RefList represents a list of Bragg reflections.
+ * A RefList represents a list of Bragg reflections.
*
* This data structure is opaque. You must use the available accessor functions
* to read and write its contents.
@@ -51,9 +66,7 @@
typedef struct _reflist RefList;
/**
- * Reflection:
- *
- * A %Reflection represents a single Bragg reflection.
+ * A Reflection represents a single Bragg reflection.
*
* This data structure is opaque. You must use the available accessor functions
* to read and write its contents.
@@ -62,10 +75,8 @@ typedef struct _reflist RefList;
typedef struct _reflection Reflection;
/**
- * RefListIterator:
- *
- * A %RefListIterator is an opaque data type used when iterating over a
- * %RefList.
+ * A RefListIterator is an opaque data type used when iterating over a
+ * RefList.
*
**/
typedef struct _reflistiterator RefListIterator;
@@ -85,8 +96,9 @@ struct reflection_contributions
Crystal **contrib_crystals;
};
-/* Creation/deletion */
extern RefList *reflist_new(void);
+
+
extern void reflist_free(RefList *list);
extern Reflection *reflection_new(signed int h, signed int k, signed int l);
extern void reflection_free(Reflection *refl);
diff --git a/libcrystfel/src/render.c b/libcrystfel/src/render.c
index 2dcb7b93..f7dd86fc 100644
--- a/libcrystfel/src/render.c
+++ b/libcrystfel/src/render.c
@@ -42,6 +42,7 @@
#include "filters.h"
#include "utils.h"
+/** \file render.h */
static void render_rgb(double val, double max,
double *rp, double *gp, double *bp)
diff --git a/libcrystfel/src/render.h b/libcrystfel/src/render.h
index c2e5965a..0c2360b1 100644
--- a/libcrystfel/src/render.h
+++ b/libcrystfel/src/render.h
@@ -33,6 +33,10 @@
#ifndef RENDER_H
#define RENDER_H
+/**
+ * \file render.h
+ * Colour scale for rendering
+ */
enum {
SCALE_COLOUR,
diff --git a/libcrystfel/src/spectrum.c b/libcrystfel/src/spectrum.c
new file mode 100644
index 00000000..be454a59
--- /dev/null
+++ b/libcrystfel/src/spectrum.c
@@ -0,0 +1,591 @@
+/*
+ * spectrum.c
+ *
+ * A class representing a radiation spectrum
+ *
+ * Copyright © 2019 Deutsches Elektronen-Synchrotron DESY,
+ * a research centre of the Helmholtz Association.
+ *
+ * Authors:
+ * 2019 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 <assert.h>
+#include <gsl/gsl_sort.h>
+
+#include "spectrum.h"
+#include "utils.h"
+
+
+/**
+ * \file spectrum.h
+ */
+
+
+enum spectrumrep
+{
+ SPEC_HISTOGRAM,
+ SPEC_GAUSSIANS
+};
+
+struct _spectrum
+{
+ enum spectrumrep rep;
+
+ /* Gaussian representation */
+ struct gaussian *gaussians;
+ int n_gaussians;
+
+ /* Histogram representation */
+ double *k;
+ double *pdf;
+ int n_samples;
+};
+
+
+/**
+ * Create a new \ref Spectrum.
+ *
+ * \returns The new spectrum, or NULL on failure.
+ *
+ */
+Spectrum *spectrum_new()
+{
+ Spectrum *s;
+
+ s = malloc(sizeof(Spectrum));
+ if ( s == NULL ) return NULL;
+
+ s->rep = SPEC_GAUSSIANS;
+
+ s->gaussians = NULL;
+ s->n_gaussians = 0;
+
+ s->k = NULL;
+ s->pdf = NULL;
+ s->n_samples = 0;
+
+ return s;
+}
+
+
+/**
+ * \param s A \ref Spectrum
+ *
+ * Frees a \ref Spectrum.
+ */
+void spectrum_free(Spectrum *s)
+{
+ if ( s == NULL ) return;
+ free(s->gaussians);
+ free(s->k);
+ free(s->pdf);
+ free(s);
+}
+
+
+/**
+ * \param s A \ref Spectrum
+ *
+ * \returns The number of Gaussians in the spectrum, or zero if \p s is not
+ * currently represented as Gaussians.
+ */
+int spectrum_get_num_gaussians(Spectrum *s)
+{
+ if ( s->rep == SPEC_GAUSSIANS ) return s->n_gaussians;
+ return 0;
+}
+
+
+/**
+ * \param s A \ref Spectrum
+ * \param n The index number of the required Gaussian
+ *
+ * Returns The \p n-th Gaussian in the spectrum. The Gaussians are
+ * returned in descending order of integrated intensity, indexed from zero.
+ *
+ * If \p n is greater than or equal to the number of Gaussians in the spectrum,
+ * or if the spectrum is not represented as Gaussians, the returned Gaussian
+ * will have zero height.
+ *
+ * \returns The \p n-th Gaussian.
+ */
+struct gaussian spectrum_get_gaussian(Spectrum *s, int n)
+{
+ struct gaussian g;
+ if ( (s->rep != SPEC_GAUSSIANS) || (n >= s->n_gaussians) ) {
+ g.kcen = 0.0;
+ g.sigma = 0.0;
+ g.height = 0.0;
+ } else {
+ g = s->gaussians[n];
+ }
+ return g;
+}
+
+
+/**
+ * \param s A \ref Spectrum
+ * \param k A wavenumber (in 1/metres)
+ *
+ * Retrieves the spectral density at wavenumber \p k.
+ * This is a sample from a probability density function, so to calculate the
+ * "amount of intensity" from this, you'll need to multiply the value by a
+ * small width of k.
+ *
+ * \returns The density at \p k.
+ */
+double spectrum_get_density_at_k(Spectrum *s, double k)
+{
+ if ( s->rep == SPEC_HISTOGRAM ) {
+ int i = 0;
+ double frac;
+ if ( k <= s->k[0] ) return 0.0;
+ if ( k >= s->k[s->n_samples-1] ) return 0.0;
+ /* k is definitely after the first sample, and definitely
+ * before the last one */
+ while ( (s->k[i] < k) && (i<s->n_samples) ) i++;
+ assert(i < s->n_samples);
+ frac = (k - s->k[i-1]) / (s->k[i] - s->k[i-1]);
+ return s->pdf[i-1] + frac * (s->pdf[i] - s->pdf[i-1]);
+ }
+
+ if ( s->rep == SPEC_GAUSSIANS ) {
+ double total = 0.0;
+ int i;
+ for ( i=0; i<s->n_gaussians; i++ ) {
+ double a = s->gaussians[i].height;
+ double b = s->gaussians[i].kcen;
+ double c = s->gaussians[i].sigma;
+ total += a*exp(-(k-b)*(k-b)/(2.0*c*c));
+ }
+ return total;
+ }
+
+ return 0.0;
+}
+
+
+static double smallest_in_list(double *vals, int n_vals)
+{
+ int i;
+ double v = +INFINITY;
+ for ( i=0; i<n_vals; i++ ) {
+ if ( vals[i] < v ) v = vals[i];
+ }
+ return v;
+}
+
+
+static double largest_in_list(double *vals, int n_vals)
+{
+ int i;
+ double v = -INFINITY;
+ for ( i=0; i<n_vals; i++ ) {
+ if ( vals[i] > v ) v = vals[i];
+ }
+ return v;
+}
+
+
+static double gauss_low(struct gaussian *gauss, int n_gauss)
+{
+ int i;
+ double v = +INFINITY;
+ for ( i=0; i<n_gauss; i++ ) {
+ double gv = gauss[i].kcen - 5.0*gauss[i].sigma;
+ if ( gv < v ) v = gv;
+ }
+ return v;
+}
+
+
+static double gauss_high(struct gaussian *gauss, int n_gauss)
+{
+ int i;
+ double v = -INFINITY;
+ for ( i=0; i<n_gauss; i++ ) {
+ double gv = gauss[i].kcen + 5.0*gauss[i].sigma;
+ if ( gv > v ) v = gv;
+ }
+ return v;
+}
+
+
+/**
+ * \param s A \ref Spectrum
+ * \param kmin Location to store minimum k value
+ * \param kmax Location to store maximum k value
+ *
+ * Sets \p kmin and \p kmax to the range of k values in the spectrum. If the
+ * spectrum is represented as Gaussians, the range returned will be enough to
+ * contain at least 5 sigmas of all the Gaussians.
+ *
+ * The values will be returned in units of 1/m.
+ */
+void spectrum_get_range(Spectrum *s, double *kmin, double *kmax)
+{
+ if ( s->rep == SPEC_HISTOGRAM ) {
+ *kmin = smallest_in_list(s->k, s->n_samples);
+ *kmax = largest_in_list(s->k, s->n_samples);
+ } else {
+ assert(s->rep == SPEC_GAUSSIANS);
+ *kmin = gauss_low(s->gaussians, s->n_gaussians);
+ *kmax = gauss_high(s->gaussians, s->n_gaussians);
+ }
+}
+
+
+static signed int cmp_gauss(const void *va, const void *vb)
+{
+ const struct gaussian *a = va;
+ const struct gaussian *b = vb;
+ /* Integral of Gaussian = height * std deviation */
+ if ( a->height * a->sigma > b->height * b->sigma ) return +1;
+ return -1;
+}
+
+
+static void normalise_gaussians(struct gaussian *gauss, int n_gauss)
+{
+ int i;
+ double total_area = 0.0;
+ for ( i=0; i<n_gauss; i++ ) {
+ total_area += gauss[i].height * gauss[i].sigma;
+ }
+ total_area *= sqrt(2.0 * M_PI);
+ for ( i=0; i<n_gauss; i++ ) {
+ gauss[i].height /= total_area;
+ }
+}
+
+
+/**
+ * \param s A \ref Spectrum
+ * \param gs Pointer to array of \ref gaussian structures
+ * \param n_gauss Number of Gaussians in \p gs
+ *
+ * Sets the spectrum in terms of a sum of Gaussians.
+ *
+ * The spectral density function will be normalised, so only the relative areas
+ * under the curves are relevant.
+ *
+ * The input array will be copied, so you can safely free it after calling this
+ * function.
+ */
+void spectrum_set_gaussians(Spectrum *s, struct gaussian *gs, int n_gauss)
+{
+ /* Free old contents (if any - may be NULL) */
+ free(s->gaussians);
+ free(s->k);
+ free(s->pdf);
+
+ s->gaussians = malloc(n_gauss * sizeof(struct gaussian));
+ if ( s->gaussians == NULL ) return;
+
+ memcpy(s->gaussians, gs, n_gauss*sizeof(struct gaussian));
+ s->n_gaussians = n_gauss;
+ s->rep = SPEC_GAUSSIANS;
+
+ qsort(s->gaussians, s->n_gaussians, sizeof(struct gaussian), cmp_gauss);
+ normalise_gaussians(s->gaussians, s->n_gaussians);
+}
+
+
+/* Samples must already have been sorted */
+static void normalise_pdf(double *k, double *pdf, int n)
+{
+ int i;
+ double total_area = 0.0;
+ double old_k = k[0];
+ double old_pdf = pdf[0];
+ for ( i=1; i<n; i++ ) {
+ total_area += (pdf[i]+old_pdf)*(k[i]-old_k)/2.0;
+ old_k = k[i];
+ old_pdf = pdf[i];
+ }
+
+ for ( i=0; i<n; i++ ) {
+ pdf[i] /= total_area;
+ }
+}
+
+
+/**
+ * \param s A \ref Spectrum
+ * \param kvals Pointer to array of k values (in 1/m);
+ * \param heights Pointer to array of spectral density samples
+ * \param n Number of samples
+ *
+ * Sets the spectrum in terms of samples of the probability density function.
+ * The spectral density function will be normalised. The spectrum can have a
+ * non-zero value only for k values in the range [kmin,kmax] (exclusive
+ * interval), where kmin and kmax are the smallest and largest values in
+ * \p kvals.
+ *
+ * The input arrays will be copied, so you can safely free them after calling
+ * this function.
+ */
+void spectrum_set_pdf(Spectrum *s, double *kvals, double *heights, int n)
+{
+ size_t *perm;
+ int i;
+
+ /* Free old contents (if any - may be NULL) */
+ free(s->gaussians);
+ free(s->k);
+ free(s->pdf);
+
+ s->k = malloc(n * sizeof(double));
+ if ( s->k == NULL ) return;
+
+ s->pdf = malloc(n * sizeof(double));
+ if ( s->pdf == NULL ) return;
+
+ perm = malloc(n * sizeof(size_t));
+ if ( perm == NULL ) return;
+
+ gsl_sort_index(perm, kvals, 1, n);
+
+ for ( i=0; i<n; i++ ) {
+ s->k[i] = kvals[perm[i]];
+ s->pdf[i] = heights[perm[i]];
+ }
+ free(perm);
+
+ s->n_samples = n;
+ s->rep = SPEC_HISTOGRAM;
+
+ normalise_pdf(s->k, s->pdf, s->n_samples);
+}
+
+
+static int read_esrf_spectrum(FILE *fh, Spectrum *s)
+{
+ double *k = NULL;
+ double *samp = NULL;
+ int n_bins = 0;
+ int max_bins = 0;
+
+ while ( !feof(fh) ) {
+
+ float energy, weight;
+ if ( fscanf(fh, "%e %e\n", &energy, &weight) != 2 ) return 1;
+
+ if ( n_bins == max_bins ) {
+ max_bins += 64;
+ k = realloc(k, max_bins*sizeof(double));
+ samp = realloc(samp, max_bins*sizeof(double));
+ if ( (k==NULL) || (samp==NULL) ) return 1;
+ }
+
+ k[n_bins] = ph_eV_to_k(energy*1000.0);
+ samp[n_bins] = weight;
+ n_bins++;
+
+ }
+
+ spectrum_set_pdf(s, k, samp, n_bins);
+ free(k);
+ free(samp);
+
+ return 0;
+}
+
+
+/**
+ * \param filename Filename for the input file
+ *
+ * Loads the spectrum from \s filename. Currently, only the ESRF spectrum file
+ * format is supported.
+ *
+ * \returns A newly allocated \ref Spectrum, or NULL on error.
+ */
+Spectrum *spectrum_load(const char *filename)
+{
+ FILE *fh;
+ Spectrum *s;
+ char line[1024];
+
+ fh = fopen(filename, "r");
+ if ( fh == NULL ) return NULL;
+
+ s = spectrum_new();
+ if ( s == NULL ) {
+ fclose(fh);
+ return NULL;
+ }
+
+ if ( fgets(line, 1024, fh) != line ) {
+ ERROR("Failed to read '%s'\n", filename);
+ spectrum_free(s);
+ return NULL;
+ }
+
+ chomp(line);
+ if ( strcmp(line, "# energy/keV current/A") == 0 ) {
+ if ( read_esrf_spectrum(fh, s) ) {
+ ERROR("Failed to read ESRF spectrum from %s\n",
+ filename);
+ spectrum_free(s);
+ return NULL;
+ }
+ } else {
+ ERROR("Spectrum format not recognised: %s\n", filename);
+ fclose(fh);
+ spectrum_free(s);
+ return NULL;
+ }
+
+ fclose(fh);
+ return s;
+}
+
+
+/**
+ * \param wavelength Wavelength in metres
+ * \param bandwidth Bandwidth as a fraction
+ *
+ * Generates a top-hat spectrum centered on 'wavelength', where the width of the
+ * flat top is bandwidth/wavelength
+ *
+ * \returns A newly-allocated \ref Spectrum, or NULL on error.
+ */
+Spectrum *spectrum_generate_tophat(double wavelength, double bandwidth)
+{
+ Spectrum *s;
+ double kvals[2];
+ double samp[2];
+ double kcen;
+
+ s = spectrum_new();
+ if ( s == NULL ) return NULL;
+
+ kcen = 1.0/wavelength;
+ kvals[0] = kcen - kcen*bandwidth/2.0;
+ kvals[1] = kcen + kcen*bandwidth/2.0;
+ samp[0] = 1.0;
+ samp[1] = 1.0;
+ spectrum_set_pdf(s, kvals, samp, 2);
+ return s;
+}
+
+
+/**
+ * \param wavelength Wavelength in metres
+ * \param bandwidth Bandwidth as a fraction
+ *
+ * Generates a Gaussian spectrum centered on 'wavelength', where the standard
+ * deviation of the Gaussian is bandwidth divided by wavelength (bandwidth
+ * fraction times k value).
+ *
+ * \returns A newly-allocated \ref Spectrum, or NULL on error.
+ */
+Spectrum *spectrum_generate_gaussian(double wavelength, double bandwidth)
+{
+ Spectrum *s;
+ struct gaussian g;
+
+ s = spectrum_new();
+ if ( s == NULL ) return NULL;
+
+ g.kcen = 1.0/wavelength;
+ g.sigma = bandwidth/wavelength;
+ g.height = 1;
+ spectrum_set_gaussians(s, &g, 1);
+
+ return s;
+}
+
+
+/**
+ * \param wavelength Wavelength in metres
+ * \param bandwidth Bandwidth as a fraction of wavelength
+ * \param spike_width The width of the SASE spikes, as a fraction of wavelength
+ * \param rng A GSL random number generator
+ *
+ * Generates a SASE spectrum centered on 'wavelength', with 15 spikes of width
+ * spike_width divided by wavelength (i.e. spike_width times k value). The
+ * spikes will be distributed within a width of k values of bandwidth divided
+ * by wavelength (bandwidth times k-value).
+ *
+ * Note that CrystFEL is not an undulator simulation program. This function
+ * just simulates a rough idea of the kind of spiky spectrum resulting from the
+ * SASE process.
+ *
+ * \returns A newly-allocated \ref Spectrum, or NULL on error.
+ */
+Spectrum *spectrum_generate_sase(double wavelength, double bandwidth,
+ double spike_width, gsl_rng *rng)
+{
+ int i;
+ Spectrum *s;
+ struct gaussian g[15];
+
+ s = spectrum_new();
+ if ( s == NULL ) return NULL;
+
+ for ( i=0; i<15; i++ ) {
+ g[i].kcen = 1.0/wavelength + (gsl_rng_uniform_pos(rng)-0.5) * bandwidth/wavelength;
+ g[i].sigma = spike_width/wavelength;
+ g[i].height = gsl_rng_uniform(rng);
+ }
+
+ spectrum_set_gaussians(s, g, 15);
+
+ return s;
+}
+
+
+/**
+ * \param wavelength Wavelength in metres
+ * \param bandwidth Bandwidth as a fraction of wavelength
+ * \param separation Separation between peak centres, in m^-1
+ *
+ * Generates a two-colour spectrum with Gaussian peaks centered at wavenumbers
+ * 1/wavelength ± separation/2. Each peak will have a standard deviation of
+ * bandwidth divided by wavelength (bandwidth fraction times k value).
+ *
+ * \returns A newly-allocated \ref Spectrum, or NULL on error.
+ */
+Spectrum *spectrum_generate_twocolour(double wavelength, double bandwidth,
+ double separation)
+{
+ Spectrum *s;
+ struct gaussian g[2];
+
+ s = spectrum_new();
+ if ( s == NULL ) return NULL;
+
+ g[0].kcen = 1.0/wavelength - separation/2.0;
+ g[0].sigma = bandwidth/wavelength;
+ g[0].height = 1;
+
+ g[1].kcen = 1.0/wavelength + separation/2.0;
+ g[1].sigma = bandwidth/wavelength;
+ g[1].height = 1;
+
+ spectrum_set_gaussians(s, g, 2);
+
+ return s;
+}
diff --git a/libcrystfel/src/spectrum.h b/libcrystfel/src/spectrum.h
new file mode 100644
index 00000000..8298a4cd
--- /dev/null
+++ b/libcrystfel/src/spectrum.h
@@ -0,0 +1,94 @@
+/*
+ * spectrum.h
+ *
+ * A class representing a radiation spectrum
+ *
+ * Copyright © 2019 Deutsches Elektronen-Synchrotron DESY,
+ * a research centre of the Helmholtz Association.
+ *
+ * Authors:
+ * 2019 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 SPECTRUM_H
+#define SPECTRUM_H
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <gsl/gsl_rng.h>
+
+/**
+ * \file spectrum.h
+ * Data structure representing a radiation spectrum
+ */
+
+/**
+ * This data structure is opaque. You must use the available accessor functions
+ * to read and write its contents.
+ **/
+typedef struct _spectrum Spectrum;
+
+
+/**
+ * Structure representing a Gaussian distribution of spectral density.
+ */
+struct gaussian
+{
+ double kcen; /**< k value at centre of Gaussian (in 1/m) */
+ double sigma; /**< Standard deviation of Gaussian (in 1/m) */
+ double height; /**< Height of Gaussian (arbitrary units) */
+};
+
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* Alloc/free */
+extern Spectrum *spectrum_new(void);
+extern void spectrum_free(Spectrum *s);
+extern Spectrum *spectrum_load(const char *filename);
+
+/* Representation as Gaussians */
+extern void spectrum_set_gaussians(Spectrum *s, struct gaussian *gs,
+ int n_gauss);
+extern int spectrum_get_num_gaussians(Spectrum *s);
+extern struct gaussian spectrum_get_gaussian(Spectrum *s, int n);
+
+/* Representation as PDF */
+extern void spectrum_set_pdf(Spectrum *s, double *kcens, double *heights,
+ int nbins);
+extern void spectrum_get_range(Spectrum *s, double *kmin, double *kmax);
+extern double spectrum_get_density_at_k(Spectrum *s, double k);
+
+/* Generation of spectra */
+extern Spectrum *spectrum_generate_tophat(double wavelength, double bandwidth);
+extern Spectrum *spectrum_generate_gaussian(double wavelength, double bandwidth);
+extern Spectrum *spectrum_generate_sase(double wavelength, double bandwidth,
+ double spike_width, gsl_rng *rng);
+extern Spectrum *spectrum_generate_twocolour(double wavelength, double bandwidth,
+ double separation);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* SPECTRUM_H */
diff --git a/libcrystfel/src/statistics.c b/libcrystfel/src/statistics.c
deleted file mode 100644
index ccf35194..00000000
--- a/libcrystfel/src/statistics.c
+++ /dev/null
@@ -1,693 +0,0 @@
-/*
- * statistics.c
- *
- * Structure-factor statistics
- *
- * Copyright © 2012 Deutsches Elektronen-Synchrotron DESY,
- * a research centre of the Helmholtz Association.
- *
- * Authors:
- * 2009-2012 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 <math.h>
-#include <stdlib.h>
-#include <gsl/gsl_errno.h>
-#include <gsl/gsl_min.h>
-#include <gsl/gsl_statistics.h>
-
-#include "statistics.h"
-#include "utils.h"
-
-/**
- * SECTION:statistics
- * @short_description: Intensity statistics and R-factors
- * @title: Statistics
- * @section_id:
- * @see_also:
- * @include: "statistics.h"
- * @Image:
- *
- * These functions are for calculating various figures of merit.
- */
-
-
-struct r_params {
- RefList *list1;
- RefList *list2;
- int fom; /* Which FoM to use (see the enum just below) */
-};
-
-enum {
- R_1_ZERO,
- R_1_IGNORE,
- R_2,
- R_1_I,
- R_DIFF_ZERO,
- R_DIFF_IGNORE,
- R_DIFF_INTENSITY,
-};
-
-
-/* Return the least squares optimal scaling factor when comparing intensities.
- * list1,list2 are the two intensity lists to compare.
- */
-double stat_scale_intensity(RefList *list1, RefList *list2)
-{
- double top = 0.0;
- double bot = 0.0;
- Reflection *refl1;
- RefListIterator *iter;
-
- for ( refl1 = first_refl(list1, &iter);
- refl1 != NULL;
- refl1 = next_refl(refl1, iter) )
- {
- double i1, i2;
- signed int h, k, l;
- Reflection *refl2;
-
- get_indices(refl1, &h, &k, &l);
- refl2 = find_refl(list2, h, k, l);
- if ( refl2 == NULL ) continue; /* No common reflection */
-
- i1 = get_intensity(refl1);
- i2 = get_intensity(refl2);
-
- top += i1 * i2;
- bot += i2 * i2;
-
- }
-
- return top/bot;
-}
-
-
-/* Return the least squares optimal scaling factor when comparing the square
- * roots of the intensities (i.e. one approximation to the structure factor
- * moduli).
- * list1,list2 are the two intensity lists to compare (they contain intensities,
- * not square rooted intensities).
- */
-static double stat_scale_sqrti(RefList *list1, RefList *list2)
-{
- double top = 0.0;
- double bot = 0.0;
- Reflection *refl1;
- RefListIterator *iter;
-
- for ( refl1 = first_refl(list1, &iter);
- refl1 != NULL;
- refl1 = next_refl(refl1, iter) )
- {
- double i1, i2;
- double f1, f2;
- signed int h, k, l;
- Reflection *refl2;
-
- get_indices(refl1, &h, &k, &l);
- refl2 = find_refl(list2, h, k, l);
- if ( refl2 == NULL ) continue; /* No common reflection */
-
- i1 = get_intensity(refl1);
- i2 = get_intensity(refl2);
-
- if ( i1 < 0.0 ) continue;
- f1 = sqrt(i1);
-
- if ( i2 < 0.0 ) continue;
- f2 = sqrt(i2);
-
- top += f1 * f2;
- bot += f2 * f2;
-
- }
-
- return top/bot;
-}
-
-
-static double internal_r1_ignorenegs(RefList *list1, RefList *list2,
- double scale)
-{
- double top = 0.0;
- double bot = 0.0;
- Reflection *refl1;
- RefListIterator *iter;
-
- for ( refl1 = first_refl(list1, &iter);
- refl1 != NULL;
- refl1 = next_refl(refl1, iter) )
- {
- double i1, i2;
- double f1, f2;
- signed int h, k, l;
- Reflection *refl2;
-
- get_indices(refl1, &h, &k, &l);
- refl2 = find_refl(list2, h, k, l);
- if ( refl2 == NULL ) continue; /* No common reflection */
-
- i1 = get_intensity(refl1);
- i2 = get_intensity(refl2);
-
- if ( i1 < 0.0 ) continue;
- f1 = sqrt(i1);
-
- if ( i2 < 0.0 ) continue;
- f2 = sqrt(i2);
- f2 *= scale;
-
- top += fabs(f1 - f2);
- bot += f1;
-
- }
-
- return top/bot;
-}
-
-
-static double internal_r1_negstozero(RefList *list1, RefList *list2,
- double scale)
-{
- double top = 0.0;
- double bot = 0.0;
- Reflection *refl1;
- RefListIterator *iter;
-
- for ( refl1 = first_refl(list1, &iter);
- refl1 != NULL;
- refl1 = next_refl(refl1, iter) )
- {
- double i1, i2;
- double f1, f2;
- signed int h, k, l;
- Reflection *refl2;
-
- get_indices(refl1, &h, &k, &l);
- refl2 = find_refl(list2, h, k, l);
- if ( refl2 == NULL ) continue; /* No common reflection */
-
- i1 = get_intensity(refl1);
- i2 = get_intensity(refl2);
-
- f1 = i1 > 0.0 ? sqrt(i1) : 0.0;
-
- f2 = i2 > 0.0 ? sqrt(i2) : 0.0;
- f2 *= scale;
-
- top += fabs(f1 - f2);
- bot += f1;
-
- }
-
- return top/bot;
-}
-
-
-static double internal_r2(RefList *list1, RefList *list2, double scale)
-{
- double top = 0.0;
- double bot = 0.0;
- Reflection *refl1;
- RefListIterator *iter;
-
- for ( refl1 = first_refl(list1, &iter);
- refl1 != NULL;
- refl1 = next_refl(refl1, iter) )
- {
- double i1, i2;
- signed int h, k, l;
- Reflection *refl2;
-
- get_indices(refl1, &h, &k, &l);
- refl2 = find_refl(list2, h, k, l);
- if ( refl2 == NULL ) continue; /* No common reflection */
-
- i1 = get_intensity(refl1);
- i2 = get_intensity(refl2);
-
- i2 *= scale;
-
- top += pow(i1 - i2, 2.0);
- bot += pow(i1, 2.0);
-
- }
-
- return sqrt(top/bot);
-}
-
-
-static double internal_r_i(RefList *list1, RefList *list2, double scale)
-{
- double top = 0.0;
- double bot = 0.0;
- Reflection *refl1;
- RefListIterator *iter;
-
- for ( refl1 = first_refl(list1, &iter);
- refl1 != NULL;
- refl1 = next_refl(refl1, iter) )
- {
- double i1, i2;
- signed int h, k, l;
- Reflection *refl2;
-
- get_indices(refl1, &h, &k, &l);
- refl2 = find_refl(list2, h, k, l);
- if ( refl2 == NULL ) continue; /* No common reflection */
-
- i1 = get_intensity(refl1);
- i2 = get_intensity(refl2);
- i2 *= scale;
-
- top += fabs(i1-i2);
- bot += fabs(i1);
-
- }
-
- return top/bot;
-}
-
-
-static double internal_rdiff_intensity(RefList *list1, RefList *list2,
- double scale)
-{
- double top = 0.0;
- double bot = 0.0;
- Reflection *refl1;
- RefListIterator *iter;
-
- for ( refl1 = first_refl(list1, &iter);
- refl1 != NULL;
- refl1 = next_refl(refl1, iter) )
- {
- double i1, i2;
- signed int h, k, l;
- Reflection *refl2;
-
- get_indices(refl1, &h, &k, &l);
- refl2 = find_refl(list2, h, k, l);
- if ( refl2 == NULL ) continue; /* No common reflection */
-
- i1 = get_intensity(refl1);
- i2 = get_intensity(refl2);
- i2 *= scale;
-
- top += fabs(i1 - i2);
- bot += i1 + i2;
-
- }
-
- return 2.0*top/bot;
-}
-
-
-static double internal_rdiff_negstozero(RefList *list1, RefList *list2,
- double scale)
-{
- double top = 0.0;
- double bot = 0.0;
- Reflection *refl1;
- RefListIterator *iter;
-
- for ( refl1 = first_refl(list1, &iter);
- refl1 != NULL;
- refl1 = next_refl(refl1, iter) )
- {
- double i1, i2;
- double f1, f2;
- signed int h, k, l;
- Reflection *refl2;
-
- get_indices(refl1, &h, &k, &l);
- refl2 = find_refl(list2, h, k, l);
- if ( refl2 == NULL ) continue; /* No common reflection */
-
- i1 = get_intensity(refl1);
- i2 = get_intensity(refl2);
-
- f1 = i1 > 0.0 ? sqrt(i1) : 0.0;
-
- f2 = i2 > 0.0 ? sqrt(i2) : 0.0;
- f2 *= scale;
-
- top += fabs(f1 - f2);
- bot += f1 + f2;
-
- }
-
- return 2.0*top/bot;
-}
-
-
-static double internal_rdiff_ignorenegs(RefList *list1, RefList *list2,
- double scale)
-{
- double top = 0.0;
- double bot = 0.0;
- Reflection *refl1;
- RefListIterator *iter;
-
- for ( refl1 = first_refl(list1, &iter);
- refl1 != NULL;
- refl1 = next_refl(refl1, iter) )
- {
- double i1, i2;
- double f1, f2;
- signed int h, k, l;
- Reflection *refl2;
-
- get_indices(refl1, &h, &k, &l);
- refl2 = find_refl(list2, h, k, l);
- if ( refl2 == NULL ) continue; /* No common reflection */
-
- i1 = get_intensity(refl1);
- i2 = get_intensity(refl2);
-
- if ( i1 < 0.0 ) continue;
- f1 = sqrt(i1);
-
- if ( i2 < 0.0 ) continue;
- f2 = sqrt(i2);
- f2 *= scale;
-
- top += fabs(f1 - f2);
- bot += f1 + f2;
-
- }
-
- return 2.0*top/bot;
-}
-
-
-static double calc_r(double scale, void *params)
-{
- struct r_params *rp = params;
-
- switch ( rp->fom ) {
-
- case R_1_ZERO :
- return internal_r1_negstozero(rp->list1, rp->list2, scale);
-
- case R_1_IGNORE :
- return internal_r1_ignorenegs(rp->list1, rp->list2, scale);
-
- case R_2 :
- return internal_r2(rp->list1, rp->list2, scale);
-
- case R_1_I :
- return internal_r_i(rp->list1, rp->list2, scale);
-
- case R_DIFF_ZERO :
- return internal_rdiff_negstozero(rp->list1, rp->list2,scale);
-
- case R_DIFF_IGNORE :
- return internal_rdiff_ignorenegs(rp->list1, rp->list2, scale);
-
- case R_DIFF_INTENSITY :
- return internal_rdiff_intensity(rp->list1, rp->list2, scale);
-
- }
-
- ERROR("No such FoM!\n");
- abort();
-}
-
-
-static double r_minimised(RefList *list1, RefList *list2, double *scalep, int fom,
- int u)
-{
- gsl_function F;
- gsl_min_fminimizer *s;
- int status;
- double scale = 1.0;
- struct r_params rp;
- int iter = 0;
-
- rp.list1 = list1;
- rp.list2 = list2;
- rp.fom = fom;
-
- if ( u ) {
-
- scale = 1.0;
-
- } else {
-
- F.function = &calc_r;
- F.params = &rp;
-
- s = gsl_min_fminimizer_alloc(gsl_min_fminimizer_brent);
-
- /* Initial guess */
- switch ( fom ) {
-
- case R_1_ZERO :
- case R_1_IGNORE :
- case R_DIFF_ZERO :
- case R_DIFF_IGNORE :
- scale = stat_scale_sqrti(list1, list2);
- break;
-
- case R_2 :
- case R_1_I :
- case R_DIFF_INTENSITY :
- scale = stat_scale_intensity(list1, list2);
- break;
-
- }
- //STATUS("Initial scale factor estimate: %5.2e\n", scale);
-
- /* Probably within an order of magnitude either side */
- gsl_min_fminimizer_set(s, &F, scale, scale/10.0, scale*10.0);
-
- do {
-
- double lo, up;
-
- /* Iterate */
- if ( gsl_min_fminimizer_iterate(s) ) {
- ERROR("Failed to find scale factor.\n");
- return NAN;
- }
-
- /* Get the current estimate */
- scale = gsl_min_fminimizer_x_minimum(s);
- lo = gsl_min_fminimizer_x_lower(s);
- up = gsl_min_fminimizer_x_upper(s);
-
- /* Check for convergence */
- status = gsl_min_test_interval(lo, up, 0.001, 0.0);
-
- iter++;
-
- } while ( status == GSL_CONTINUE );
-
- if ( status != GSL_SUCCESS ) {
- ERROR("Scale factor minimisation failed.\n");
- }
-
- gsl_min_fminimizer_free(s);
-
- }
-
- //STATUS("Final scale factor: %5.2e\n", scale);
- *scalep = scale;
- return calc_r(scale, &rp);
-}
-
-
-double stat_r1_ignore(RefList *list1, RefList *list2, double *scalep, int u)
-{
- return r_minimised(list1, list2, scalep, R_1_IGNORE, u);
-}
-
-
-double stat_r1_zero(RefList *list1, RefList *list2, double *scalep, int u)
-{
- return r_minimised(list1, list2, scalep, R_1_ZERO, u);
-}
-
-
-double stat_r2(RefList *list1, RefList *list2, double *scalep, int u)
-{
- return r_minimised(list1, list2, scalep, R_2, u);
-}
-
-
-double stat_r1_i(RefList *list1, RefList *list2, double *scalep, int u)
-{
- return r_minimised(list1, list2, scalep, R_1_I, u);
-}
-
-
-double stat_rdiff_zero(RefList *list1, RefList *list2, double *scalep, int u)
-{
- return r_minimised(list1, list2, scalep, R_DIFF_ZERO, u);
-}
-
-
-double stat_rdiff_ignore(RefList *list1, RefList *list2, double *scalep, int u)
-{
- return r_minimised(list1, list2, scalep, R_DIFF_IGNORE, u);
-}
-
-
-double stat_rdiff_intensity(RefList *list1, RefList *list2, double *scalep, int u)
-{
- return r_minimised(list1, list2, scalep, R_DIFF_INTENSITY, u);
-}
-
-
-double stat_pearson_i(RefList *list1, RefList *list2)
-{
- double *vec1, *vec2;
- int ni = num_reflections(list1);
- double val;
- int nacc = 0;
- Reflection *refl1;
- RefListIterator *iter;
-
- vec1 = malloc(ni*sizeof(double));
- vec2 = malloc(ni*sizeof(double));
-
- for ( refl1 = first_refl(list1, &iter);
- refl1 != NULL;
- refl1 = next_refl(refl1, iter) )
- {
- double i1, i2;
- signed int h, k, l;
- Reflection *refl2;
-
- get_indices(refl1, &h, &k, &l);
- refl2 = find_refl(list2, h, k, l);
- if ( refl2 == NULL ) continue; /* No common reflection */
-
- i1 = get_intensity(refl1);
- i2 = get_intensity(refl2);
-
- vec1[nacc] = i1;
- vec2[nacc] = i2;
- nacc++;
- }
-
- val = gsl_stats_correlation(vec1, 1, vec2, 1, nacc);
- free(vec1);
- free(vec2);
-
- return val;
-}
-
-
-double stat_pearson_f_ignore(RefList *list1, RefList *list2)
-{
- double *vec1, *vec2;
- int ni = num_reflections(list1);
- double val;
- int nacc = 0;
- Reflection *refl1;
- RefListIterator *iter;
-
- vec1 = malloc(ni*sizeof(double));
- vec2 = malloc(ni*sizeof(double));
-
- for ( refl1 = first_refl(list1, &iter);
- refl1 != NULL;
- refl1 = next_refl(refl1, iter) )
- {
- double i1, i2;
- double f1, f2;
- signed int h, k, l;
- Reflection *refl2;
-
- get_indices(refl1, &h, &k, &l);
- refl2 = find_refl(list2, h, k, l);
- if ( refl2 == NULL ) continue; /* No common reflection */
-
- i1 = get_intensity(refl1);
- i2 = get_intensity(refl2);
-
- if ( i1 < 0.0 ) continue;
- if ( i2 < 0.0 ) continue;
-
- f1 = sqrt(i1);
- f2 = sqrt(i2);
-
- vec1[nacc] = f1;
- vec2[nacc] = f2;
- nacc++;
-
- }
-
- val = gsl_stats_correlation(vec1, 1, vec2, 1, nacc);
- free(vec1);
- free(vec2);
-
- return val;
-}
-
-
-double stat_pearson_f_zero(RefList *list1, RefList *list2)
-{
- double *vec1, *vec2;
- int ni = num_reflections(list1);
- double val;
- int nacc = 0;
- Reflection *refl1;
- RefListIterator *iter;
-
- vec1 = malloc(ni*sizeof(double));
- vec2 = malloc(ni*sizeof(double));
-
- for ( refl1 = first_refl(list1, &iter);
- refl1 != NULL;
- refl1 = next_refl(refl1, iter) )
- {
- double i1, i2;
- double f1, f2;
- signed int h, k, l;
- Reflection *refl2;
-
- get_indices(refl1, &h, &k, &l);
- refl2 = find_refl(list2, h, k, l);
- if ( refl2 == NULL ) continue; /* No common reflection */
-
- i1 = get_intensity(refl1);
- i2 = get_intensity(refl2);
-
- f1 = i1 > 0.0 ? sqrt(i1) : 0.0;
- f2 = i2 > 0.0 ? sqrt(i2) : 0.0;
-
- vec1[nacc] = f1;
- vec2[nacc] = f2;
- nacc++;
-
- }
-
- val = gsl_stats_correlation(vec1, 1, vec2, 1, nacc);
- free(vec1);
- free(vec2);
-
- return val;
-}
diff --git a/libcrystfel/src/statistics.h b/libcrystfel/src/statistics.h
deleted file mode 100644
index 17338dad..00000000
--- a/libcrystfel/src/statistics.h
+++ /dev/null
@@ -1,68 +0,0 @@
-/*
- * statistics.h
- *
- * Structure-factor statistics
- *
- * Copyright © 2012 Deutsches Elektronen-Synchrotron DESY,
- * a research centre of the Helmholtz Association.
- *
- * Authors:
- * 2009-2012 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
-
-#ifndef STATISTICS_H
-#define STATISTICS_H
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-#include "reflist.h"
-
-extern double stat_scale_intensity(RefList *list1, RefList *list2);
-
-extern double stat_r1_zero(RefList *list1, RefList *list2,
- double *scalep, int u);
-extern double stat_r1_ignore(RefList *list1, RefList *list2,
- double *scalep, int u);
-
-extern double stat_r2(RefList *list1, RefList *list2, double *scalep, int u);
-
-extern double stat_r1_i(RefList *list1, RefList *list2, double *scalep, int u);
-
-extern double stat_rdiff_zero(RefList *list1, RefList *list2,
- double *scalep, int u);
-extern double stat_rdiff_ignore(RefList *list1, RefList *list2,
- double *scalep, int u);
-extern double stat_rdiff_intensity(RefList *list1, RefList *list2,
- double *scalep, int u);
-
-extern double stat_pearson_i(RefList *list1, RefList *list2);
-extern double stat_pearson_f_zero(RefList *list1, RefList *list2);
-extern double stat_pearson_f_ignore(RefList *list1, RefList *list2);
-
-#ifdef __cplusplus
-}
-#endif
-
-#endif /* STATISTICS_H */
diff --git a/libcrystfel/src/stream.c b/libcrystfel/src/stream.c
index 66b3b657..ed05d986 100644
--- a/libcrystfel/src/stream.c
+++ b/libcrystfel/src/stream.c
@@ -3,12 +3,12 @@
*
* Stream tools
*
- * Copyright © 2013-2016 Deutsches Elektronen-Synchrotron DESY,
+ * Copyright © 2013-2019 Deutsches Elektronen-Synchrotron DESY,
* a research centre of the Helmholtz Association.
* Copyright © 2012 Richard Kirian
*
* Authors:
- * 2010-2016 Thomas White <taw@physics.org>
+ * 2010-2019 Thomas White <taw@physics.org>
* 2014-2016 Valerio Mariani
* 2011 Richard Kirian
* 2011 Andrew Aquila
@@ -52,6 +52,8 @@
#include "reflist.h"
#include "reflist-utils.h"
+/** \file stream.h */
+
#define LATEST_MAJOR_VERSION (2)
#define LATEST_MINOR_VERSION (3)
@@ -65,6 +67,7 @@ struct _stream
int major_version;
int minor_version;
char *audit_info;
+ char *geometry_file;
long long int ln;
@@ -348,6 +351,7 @@ static RefList *read_stream_reflections_2_3(Stream *st, struct detector *det)
set_peak(refl, pk);
set_mean_bg(refl, bg);
set_redundancy(refl, 1);
+ set_symmetric_indices(refl, h, k, l);
}
} while ( rval != NULL );
@@ -429,6 +433,7 @@ static RefList *read_stream_reflections_2_1(Stream *st, struct detector *det)
}
set_esd_intensity(refl, sigma);
set_redundancy(refl, cts);
+ set_symmetric_indices(refl, h, k, l);
ph = strtod(phs, &v);
if ( v != phs ) set_phase(refl, deg2rad(ph));
@@ -507,6 +512,7 @@ static RefList *read_stream_reflections_2_2(Stream *st, struct detector *det)
set_redundancy(refl, 1);
set_peak(refl, pk);
set_mean_bg(refl, bg);
+ set_symmetric_indices(refl, h, k, l);
}
@@ -816,6 +822,18 @@ static int write_crystal(Stream *st, Crystal *cr, int include_reflections)
}
+/**
+ * \param st A \ref Stream
+ * \param i An \ref image structure
+ * \param imfile A \ref imagefile structure
+ * \param include_peaks Whether to include peak search results in stream
+ * \param include_reflections Whether to include integration results in stream
+ * \param ev A \ref event strucutre
+ *
+ * Writes a new chunk to \p st.
+ *
+ * \returns non-zero on error.
+ */
int write_chunk(Stream *st, struct image *i, struct imagefile *imfile,
int include_peaks, int include_reflections, struct event *ev)
{
@@ -883,8 +901,7 @@ int write_chunk(Stream *st, struct image *i, struct imagefile *imfile,
}
- fprintf(st->fh, "num_peaks = %lli\n", i->num_peaks);
- fprintf(st->fh, "num_saturated_peaks = %lli\n", i->num_saturated_peaks);
+ fprintf(st->fh, "num_peaks = %i\n", image_feature_count(i->features));
fprintf(st->fh, "peak_resolution = %f nm^-1 or %f A\n",
i->peak_resolution/1e9, 1e10/i->peak_resolution);
if ( include_peaks ) {
@@ -1180,7 +1197,9 @@ static int read_and_store_field(struct image *image, const char *line)
}
-/* Read the next chunk from a stream and fill in 'image' */
+/**
+ * Read the next chunk from a stream and fill in 'image'
+ */
int read_chunk_2(Stream *st, struct image *image, StreamReadFlags srf)
{
char line[1024];
@@ -1202,7 +1221,6 @@ int read_chunk_2(Stream *st, struct image *image, StreamReadFlags srf)
}
do {
- long long num_peaks;
int ser;
float div, bw;
@@ -1247,10 +1265,6 @@ int read_chunk_2(Stream *st, struct image *image, StreamReadFlags srf)
image->bw = bw;
}
- if ( sscanf(line, "num_peaks = %lld", &num_peaks) == 1 ) {
- image->num_peaks = num_peaks;
- }
-
if ( sscanf(line, "Image serial number: %i", &ser) == 1 ) {
image->serial = ser;
}
@@ -1332,6 +1346,15 @@ int read_chunk_2(Stream *st, struct image *image, StreamReadFlags srf)
}
+
+/**
+ * \param st A \ref Stream
+ * \param image An \ref image structure to be filled
+ *
+ * Reads a chunk from \p st, placing the information in \p image.
+ *
+ * \returns non-zero on error.
+ */
int read_chunk(Stream *st, struct image *image)
{
return read_chunk_2(st, image, STREAM_READ_UNITCELL
@@ -1360,6 +1383,12 @@ char *stream_audit_info(Stream *st)
}
+char *stream_geometry_file(Stream *st)
+{
+ return st->geometry_file;
+}
+
+
static void read_audit_lines(Stream *st)
{
int done = 0;
@@ -1412,6 +1441,63 @@ static void read_audit_lines(Stream *st)
}
+static void read_geometry_file(Stream *st)
+{
+ int done = 0;
+ size_t len = 0;
+ int started = 0;
+ const size_t max_geom_len = 16*1024;
+
+ st->geometry_file = malloc(max_geom_len);
+ if ( st->geometry_file == NULL ) {
+ ERROR("Failed to allocate memory for audit information\n");
+ return;
+ }
+ st->geometry_file[0] = '\0';
+
+ do {
+
+ char line[1024];
+ char *rval;
+
+ rval = fgets(line, 1023, st->fh);
+ if ( rval == NULL ) {
+ ERROR("Failed to read stream geometry file.\n");
+ close_stream(st);
+ free(st->geometry_file);
+ st->geometry_file = NULL;
+ return;
+ }
+
+ if ( strcmp(line, GEOM_START_MARKER"\n") == 0 ) {
+ started = 1;
+ continue;
+ }
+
+ if ( (strcmp(line, CHUNK_START_MARKER"\n") == 0)
+ || (strcmp(line, GEOM_END_MARKER"\n") == 0) )
+ {
+ done = 1;
+ continue;
+ }
+
+ if ( !started ) continue;
+
+ len += strlen(line);
+ if ( len > max_geom_len-1 ) {
+ ERROR("Stream's geometry file is too long (%li > %i).\n",
+ (long)len, (int)max_geom_len);
+ free(st->geometry_file);
+ st->geometry_file = NULL;
+ return;
+ } else {
+ strcat(st->geometry_file, line);
+ }
+
+ } while ( !done );
+}
+
+
Stream *open_stream_for_read(const char *filename)
{
Stream *st;
@@ -1420,6 +1506,7 @@ Stream *open_stream_for_read(const char *filename)
if ( st == NULL ) return NULL;
st->old_indexers = 0;
st->audit_info = NULL;
+ st->geometry_file = NULL;
if ( strcmp(filename, "-") == 0 ) {
st->fh = stdin;
@@ -1463,24 +1550,24 @@ Stream *open_stream_for_read(const char *filename)
st->ln = 1;
read_audit_lines(st);
+ read_geometry_file(st);
return st;
}
/**
- * open_stream_fd_for_write
- * @fd: File descriptor (e.g. from open()) to use for stream data.
+ * \param fd File descriptor (e.g. from open()) to use for stream data.
*
- * Creates a new %Stream from @fd, so that stream data can be written to @fd
- * using write_chunk().
+ * Creates a new \ref Stream from \p fd, so that stream data can be written to \p fd
+ * using \ref write_chunk.
*
- * In contrast to open_stream_for_write(), this function does not write any of
+ * In contrast to \ref open_stream_for_write, this function does not write any of
* the usual headers. This function is mostly for use when multiple substreams
* need to be multiplexed into a single master stream. The master would be
- * opened using open_stream_for_write(), and the substreams using this function.
+ * opened using \ref open_stream_for_write, and the substreams using this function.
*
- * Returns: a %Stream, or NULL on failure.
+ * \returns A \ref Stream, or NULL on failure.
*/
Stream *open_stream_fd_for_write(int fd)
{
@@ -1490,6 +1577,7 @@ Stream *open_stream_fd_for_write(int fd)
if ( st == NULL ) return NULL;
st->old_indexers = 0;
st->audit_info = NULL;
+ st->geometry_file = NULL;
st->fh = fdopen(fd, "w");
if ( st->fh == NULL ) {
@@ -1517,19 +1605,18 @@ static void write_cell_to_stream(Stream *st, UnitCell *cell)
/**
- * open_stream_for_write_4
- * @filename: Filename of new stream
- * @geom_filename: The geometry filename to copy
- * @cell: A %UnitCell to write into the stream
- * @argc: The number of arguments to the program
- * @argv: The arguments to the program
- * @indm_str: The list of indexing methods
+ * \param filename Filename of new stream
+ * \param geom_filename The geometry filename to copy
+ * \param cell A \ref UnitCell to write into the stream
+ * \param argc The number of arguments to the program
+ * \param argv The arguments to the program
+ * \param indm_str The list of indexing methods
*
- * Creates a new stream with name @filename, and adds the stream format
+ * Creates a new stream with name \p filename, and adds the stream format
* and version header, plus a verbatim copy of the geometry file and the unit
* cell in CrystFEL format.
*
- * Returns: a %Stream, or NULL on failure.
+ * \returns A \ref Stream, or NULL on failure.
*/
Stream *open_stream_for_write_4(const char *filename,
const char *geom_filename, UnitCell *cell,
@@ -1542,6 +1629,7 @@ Stream *open_stream_for_write_4(const char *filename,
if ( st == NULL ) return NULL;
st->old_indexers = 0;
st->audit_info = NULL;
+ st->geometry_file = NULL;
st->fh = fopen(filename, "w");
if ( st->fh == NULL ) {
@@ -1586,16 +1674,15 @@ Stream *open_stream_for_write_3(const char *filename,
/**
- * open_stream_for_write_2
- * @filename: Filename of new stream
- * @geom_filename: The geometry filename to copy
- * @argc: The number of arguments to the program
- * @argv: The arguments to the program
+ * \param filename Filename of new stream
+ * \param geom_filename The geometry filename to copy
+ * \param argc The number of arguments to the program
+ * \param argv The arguments to the program
*
- * Creates a new stream with name @filename, and adds the stream format
+ * Creates a new stream with name \p filename, and adds the stream format
* and version header, plus a verbatim copy of the geometry file
*
- * Returns: a %Stream, or NULL on failure.
+ * \returns A \ref Stream, or NULL on failure.
*/
Stream *open_stream_for_write_2(const char *filename,
const char *geom_filename, int argc,
@@ -1608,6 +1695,7 @@ Stream *open_stream_for_write_2(const char *filename,
if ( st == NULL ) return NULL;
st->old_indexers = 0;
st->audit_info = NULL;
+ st->geometry_file = NULL;
st->fh = fopen(filename, "w");
if ( st->fh == NULL ) {
@@ -1636,16 +1724,15 @@ Stream *open_stream_for_write_2(const char *filename,
/**
- * open_stream_for_write
- * @filename: Filename of new stream
+ * \param filename Filename of new stream
*
- * Creates a new stream with name @filename, and adds the stream format
+ * Creates a new stream with name \p filename, and adds the stream format
* and version headers.
*
- * You may want to follow this with a call to write_command() to record the
+ * You may want to follow this with a call to \ref write_command to record the
* command line.
*
- * Returns: a %Stream, or NULL on failure.
+ * \returns A \ref Stream, or NULL on failure.
*/
Stream *open_stream_for_write(const char *filename)
{
@@ -1654,17 +1741,16 @@ Stream *open_stream_for_write(const char *filename)
/**
- * get_stream_fd
- * @st: A %Stream
+ * \param st A \ref Stream
*
- * This function gets the integer file descriptor for @st, a bit like fileno().
+ * This function gets the integer file descriptor for \p st, a bit like fileno().
*
- * This is useful in conjunction with open_stream_fd_for_write(), to get the
+ * This is useful in conjunction with \ref open_stream_fd_for_write, to get the
* underlying file descriptor to which the multiplexed stream data should be
* written. In this case, the only other operations you should ever do (or have
- * done) on @st are open_stream_for_write() and close_stream().
+ * done) on \p st are \ref open_stream_for_write and \ref close_stream.
*
- * Returns: an integer file descriptor
+ * \returns An integer file descriptor
*/
int get_stream_fd(Stream *st)
{
@@ -1672,9 +1758,15 @@ int get_stream_fd(Stream *st)
}
+/**
+ * \param st A \ref Stream
+ *
+ * Closes the stream
+ */
void close_stream(Stream *st)
{
free(st->audit_info);
+ free(st->geometry_file);
fclose(st->fh);
free(st);
}
@@ -1702,14 +1794,13 @@ int is_stream(const char *filename)
/**
- * write_command
- * @st: A %Stream
- * @argc: number of arguments
- * @argv: command-line arguments
+ * \param st A \ref Stream
+ * \param argc number of arguments
+ * \param argv command-line arguments
*
- * Writes the command line to @st. @argc and @argv should be exactly as were
+ * Writes the command line to \p st. \p argc and \p argv should be exactly as were
* given to main(). This should usually be called immediately after
- * open_stream_for_write().
+ * ref open_stream_for_write.
*/
void write_command(Stream *st, int argc, char *argv[])
{
@@ -1727,12 +1818,11 @@ void write_command(Stream *st, int argc, char *argv[])
/**
- * write_geometry_file
- * @st: A %Stream
- * @geom_filename: geomtry file name
+ * \param st A \ref Stream
+ * \param geom_filename geomtry file name
*
- * Writes the content of the geometry file to @st. This should usually be
- * called immediately after write_command().
+ * Writes the content of the geometry file to \p st. This should usually be
+ * called immediately after \ref write_command.
*/
void write_geometry_file(Stream *st, const char *geom_filename) {
@@ -1770,16 +1860,15 @@ void write_geometry_file(Stream *st, const char *geom_filename) {
/**
- * rewind_stream:
- * @st: A %Stream
+ * \param st A \ref Stream
*
- * Attempts to set the file pointer for @st to the start of the stream, so that
- * later calls to read_chunk() will repeat the sequence of chunks from the
+ * Attempts to set the file pointer for \p st to the start of the stream, so that
+ * later calls to \ref read_chunk will repeat the sequence of chunks from the
* start.
*
* Programs must not assume that this operation always succeeds!
*
- * Returns: non-zero if the stream could not be rewound.
+ * \returns Non-zero if the stream could not be rewound.
*/
int rewind_stream(Stream *st)
{
diff --git a/libcrystfel/src/stream.h b/libcrystfel/src/stream.h
index bd7aa690..da2d40f8 100644
--- a/libcrystfel/src/stream.h
+++ b/libcrystfel/src/stream.h
@@ -3,11 +3,11 @@
*
* Stream tools
*
- * Copyright © 2013-2018 Deutsches Elektronen-Synchrotron DESY,
+ * Copyright © 2013-2019 Deutsches Elektronen-Synchrotron DESY,
* a research centre of the Helmholtz Association.
*
* Authors:
- * 2010-2018 Thomas White <taw@physics.org>
+ * 2010-2019 Thomas White <taw@physics.org>
* 2014 Valerio Mariani
* 2011 Andrew Aquila
*
@@ -35,6 +35,10 @@
#include <config.h>
#endif
+/**
+ * \file stream.h
+ * Stream functions (for indexing results)
+ */
struct image;
struct hdfile;
@@ -56,27 +60,31 @@ struct imagefile;
/* REFLECTION_END_MARKER is over in reflist-utils.h because it is also
* used to terminate a standalone list of reflections */
+/**
+ * An opaque structure representing a stream being read or written
+ */
typedef struct _stream Stream;
/**
- * StreamReadFlags:
- * @STREAM_READ_UNITCELL: Read the unit cell
- * @STREAM_READ_REFLECTIONS: Read the integrated reflections
- * @STREAM_READ_PEAKS: Read the peak search results
- * @STREAM_READ_CRYSTALS: Read the general information about crystals
- *
* A bitfield of things that can be read from a stream. Use this (and
- * read_chunk_2()) to read the stream faster if you don't need the entire
+ * \ref read_chunk_2) to read the stream faster if you don't need the entire
* contents of the stream.
*
- * Using either or both of @STREAM_READ_REFLECTIONS and @STREAM_READ_UNITCELL
- * implies @STREAM_READ_CRYSTALS.
+ * Using either or both of \p STREAM_READ_REFLECTIONS and \p STREAM_READ_UNITCELL
+ * implies \p STREAM_READ_CRYSTALS.
**/
typedef enum {
+ /** Read the unit cell */
STREAM_READ_UNITCELL = 1,
+
+ /** Read the integrated reflections */
STREAM_READ_REFLECTIONS = 2,
+
+ /** Read the peak search results */
STREAM_READ_PEAKS = 4,
+
+ /** Read the general information about crystals */
STREAM_READ_CRYSTALS = 8,
} StreamReadFlags;
@@ -127,6 +135,7 @@ 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 char *stream_audit_info(Stream *st);
+extern char *stream_geometry_file(Stream *st);
#ifdef __cplusplus
}
diff --git a/libcrystfel/src/symmetry.c b/libcrystfel/src/symmetry.c
index 06cc368c..6739daaf 100644
--- a/libcrystfel/src/symmetry.c
+++ b/libcrystfel/src/symmetry.c
@@ -3,12 +3,12 @@
*
* Symmetry
*
- * Copyright © 2012-2016 Deutsches Elektronen-Synchrotron DESY,
+ * Copyright © 2012-2019 Deutsches Elektronen-Synchrotron DESY,
* a research centre of the Helmholtz Association.
*
* Authors:
- * 2010-2014,2016 Thomas White <taw@physics.org>
- * 2014 Kenneth Beyerlein <kenneth.beyerlein@desy.de>
+ * 2010-2019 Thomas White <taw@physics.org>
+ * 2014 Kenneth Beyerlein <kenneth.beyerlein@desy.de>
*
* This file is part of CrystFEL.
*
@@ -41,20 +41,11 @@
#include "symmetry.h"
#include "utils.h"
#include "integer_matrix.h"
+#include "symop-parse.h"
+#include "symop-lex.h"
-/**
- * SECTION:symmetry
- * @short_description: Point symmetry handling
- * @title: Symmetry
- * @section_id:
- * @see_also:
- * @include: "symmetry.h"
- * @Image:
- *
- * Routines to handle point symmetry.
- */
-
+/** \file symmetry.h */
struct _symoplist
{
@@ -82,9 +73,9 @@ static void alloc_ops(SymOpList *ops)
/**
* new_symopmask:
- * @list: A %SymOpList
+ * \param list A \ref SymOpList
*
- * Returns: a new %SymOpMask, which you can use when filtering out special
+ * \returns A new \ref SymOpMask, which you can use when filtering out special
* reflections.
**/
SymOpMask *new_symopmask(const SymOpList *list)
@@ -127,10 +118,9 @@ static SymOpList *new_symoplist()
/**
- * free_symoplist:
- * @ops: A %SymOpList to free
+ * \param ops A \ref SymOpList to free
*
- * Frees a %SymOpList and all associated resources.
+ * Frees a \ref SymOpList and all associated resources.
**/
void free_symoplist(SymOpList *ops)
{
@@ -146,10 +136,9 @@ void free_symoplist(SymOpList *ops)
}
/**
- * free_symopmask:
- * @m: A %SymOpMask to free
+ * \param m A \ref SymOpMask to free
*
- * Frees a %SymOpMask and all associated resources.
+ * Frees a \ref SymOpMask and all associated resources.
**/
void free_symopmask(SymOpMask *m)
{
@@ -168,11 +157,10 @@ static int num_ops(const SymOpList *ops)
/**
- * add_symop:
- * @ops: A %SymOpList
- * @m: An %IntegerMatrix
+ * \param ops A \ref SymOpList
+ * \param m An \ref IntegerMatrix
*
- * Adds @m to @ops.
+ * Adds \p m to \p ops.
**/
void add_symop(SymOpList *ops, IntegerMatrix *m)
{
@@ -195,9 +183,9 @@ static void add_symop_v(SymOpList *ops,
m = intmat_new(3, 3);
assert(m != NULL);
- for ( i=0; i<3; i++ ) intmat_set(m, 0, i, h[i]);
- for ( i=0; i<3; i++ ) intmat_set(m, 1, i, k[i]);
- for ( i=0; i<3; i++ ) intmat_set(m, 2, i, l[i]);
+ for ( i=0; i<3; i++ ) intmat_set(m, i, 0, h[i]);
+ for ( i=0; i<3; i++ ) intmat_set(m, i, 1, k[i]);
+ for ( i=0; i<3; i++ ) intmat_set(m, i, 2, l[i]);
free(h);
free(k);
@@ -208,10 +196,9 @@ static void add_symop_v(SymOpList *ops,
/**
- * get_symop:
- * @ops: A %SymOpList
- * @m: A %SymOpMask
- * @idx: Index of the operation to get
+ * \param ops A \ref SymOpList
+ * \param m A \ref SymOpMask
+ * \param idx Index of the operation to get
*
* This function returns a pointer to an integer matrix specifying a symmetry
* operation contained in the symmetry operator list, and identified by the
@@ -268,13 +255,12 @@ static signed int *v(signed int h, signed int k, signed int i, signed int l)
/**
- * num_equivs:
- * @ops: A %SymOpList
- * @m: A %SymOpMask, which has been shown to special_position()
+ * \param ops A \ref SymOpList
+ * \param m A \ref SymOpMask, which has been shown to \ref special_position
*
- * Returns: the number of equivalent reflections for a general reflection
+ * \returns The number of equivalent reflections for a general reflection
* in point group "ops", which were not flagged by your call to
- * special_position().
+ * \ref special_position.
**/
int num_equivs(const SymOpList *ops, const SymOpMask *m)
{
@@ -369,13 +355,13 @@ static void expand_ops(SymOpList *s)
/* Transform all the operations in a SymOpList by a given matrix.
* The matrix must have a determinant of +/- 1 (otherwise its inverse would
* not also be an integer matrix). */
-static void transform_ops(SymOpList *s, IntegerMatrix *t)
+static void transform_ops(SymOpList *s, IntegerMatrix *P)
{
int n, i;
- IntegerMatrix *inv;
+ IntegerMatrix *Pi;
signed int det;
- det = intmat_det(t);
+ det = intmat_det(P);
if ( det == -1 ) {
ERROR("WARNING: mirrored SymOpList.\n");
} else if ( det != 1 ) {
@@ -383,8 +369,8 @@ static void transform_ops(SymOpList *s, IntegerMatrix *t)
return;
}
- inv = intmat_inverse(t);
- if ( inv == NULL ) {
+ Pi = intmat_inverse(P);
+ if ( Pi == NULL ) {
ERROR("Failed to invert matrix.\n");
return;
}
@@ -394,13 +380,13 @@ static void transform_ops(SymOpList *s, IntegerMatrix *t)
IntegerMatrix *r, *f;
- r = intmat_intmat_mult(s->ops[i], t);
+ r = intmat_intmat_mult(P, s->ops[i]);
if ( r == NULL ) {
ERROR("Matrix multiplication failed.\n");
return;
}
- f = intmat_intmat_mult(inv, r);
+ f = intmat_intmat_mult(r, Pi);
if ( f == NULL ) {
ERROR("Matrix multiplication failed.\n");
return;
@@ -413,7 +399,7 @@ static void transform_ops(SymOpList *s, IntegerMatrix *t)
}
- intmat_free(inv);
+ intmat_free(Pi);
}
@@ -1012,14 +998,14 @@ static SymOpList *getpg_arbitrary_ua(const char *sym, size_t s)
case 'a' :
intmat_set(t, 0, 2, 1);
- intmat_set(t, 1, 1, 1);
- intmat_set(t, 2, 0, -1);
+ intmat_set(t, 1, 0, 1);
+ intmat_set(t, 2, 1, 1);
break;
case 'b' :
- intmat_set(t, 0, 0, 1);
+ intmat_set(t, 0, 1, 1);
intmat_set(t, 1, 2, 1);
- intmat_set(t, 2, 1, -1);
+ intmat_set(t, 2, 0, 1);
break;
@@ -1054,10 +1040,9 @@ static SymOpList *getpg_arbitrary_ua(const char *sym, size_t s)
/**
- * get_pointgroup:
- * @sym: A string representation of a point group
+ * \param sym A string representation of a point group
*
- * This function parses @sym and returns the corresponding %SymOpList.
+ * This function parses \p sym and returns the corresponding \ref SymOpList.
* In the string representation of the point group, use a preceding minus sign
* for any character which would have a "bar". Trigonal groups must be suffixed
* with either "_H" or "_R" for a hexagonal or rhombohedral lattice
@@ -1124,7 +1109,7 @@ static void do_op(const IntegerMatrix *op,
v[0] = h; v[1] = k; v[2] = l;
- ans = intmat_intvec_mult(op, v);
+ ans = transform_indices(op, v);
assert(ans != NULL);
*he = ans[0]; *ke = ans[1]; *le = ans[2];
@@ -1133,26 +1118,25 @@ static void do_op(const IntegerMatrix *op,
/**
- * get_equiv:
- * @ops: A %SymOpList
- * @m: A %SymOpMask, which has been shown to special_position()
- * @idx: Index of the operation to use
- * @h: index of reflection
- * @k: index of reflection
- * @l: index of reflection
- * @he: location to store h index of equivalent reflection
- * @ke: location to store k index of equivalent reflection
- * @le: location to store l index of equivalent reflection
+ * \param ops A \ref SymOpList
+ * \param m A \ref SymOpMask, which has been shown to \ref special_position
+ * \param idx Index of the operation to use
+ * \param h index of reflection
+ * \param k index of reflection
+ * \param l index of reflection
+ * \param he location to store h index of equivalent reflection
+ * \param ke location to store k index of equivalent reflection
+ * \param le location to store l index of equivalent reflection
*
- * This function applies the @idx-th symmetry operation from @ops to the
- * reflection @h, @k, @l, and stores the result at @he, @ke and @le.
+ * This function applies the \p idx-th symmetry operation from \p ops to the
+ * reflection \p h, \p k, \p l, and stores the result at \p he, \p ke and \p le.
*
* Call this function multiple times with idx=0 .. num_equivs(ops, m) to get all
* of the equivalent reflections in turn.
*
* If you don't mind that the same equivalent might appear twice, simply let
- * @m = NULL. Otherwise, call new_symopmask() and then special_position() to
- * set up a %SymOpMask appropriately.
+ * \p m = NULL. Otherwise, call \ref new_symopmask and then
+ * \ref special_position to set up a \ref SymOpMask appropriately.
**/
void get_equiv(const SymOpList *ops, const SymOpMask *m, int idx,
signed int h, signed int k, signed int l,
@@ -1166,16 +1150,14 @@ void get_equiv(const SymOpList *ops, const SymOpMask *m, int idx,
/**
- * special_position:
- * @ops: A %SymOpList, usually corresponding to a point group
- * @m: A %SymOpMask created with new_symopmask()
- * @h: index of a reflection
- * @k: index of a reflection
- * @l: index of a reflection
- *
- * This function sets up @m to contain information about which operations in
- * @ops map the reflection @h, @k, @l onto itself.
+ * \param ops A \ref SymOpList, usually corresponding to a point group
+ * \param m A \ref SymOpMask created with \ref new_symopmask
+ * \param h index of a reflection
+ * \param k index of a reflection
+ * \param l index of a reflection
*
+ * This function sets up \p m to contain information about which operations in
+ * \p ops map the reflection \p h, \p k, \p l onto itself.
**/
void special_position(const SymOpList *ops, SymOpMask *m,
signed int h, signed int k, signed int l)
@@ -1231,15 +1213,14 @@ static int any_negative(signed int h, signed int k, signed int l)
/**
- * is_centric:
- * @h: h index
- * @k: k index
- * @l: l index
- * @ops: A %SymOpList
+ * \param h h index
+ * \param k k index
+ * \param l l index
+ * \param ops A \ref SymOpList
*
* A reflection is centric if it is related by symmetry to its Friedel partner.
*
- * Returns: true if @h @k @l is centric in @ops.
+ * \returns True if \p h \p k \p l is centric in \p ops.
*
**/
int is_centric(signed int h, signed int k, signed int l, const SymOpList *ops)
@@ -1259,24 +1240,23 @@ int is_centric(signed int h, signed int k, signed int l, const SymOpList *ops)
/**
- * get_asymm:
- * @ops: A %SymOpList, usually corresponding to a point group
- * @h: index of a reflection
- * @k: index of a reflection
- * @l: index of a reflection
- * @hp: location for asymmetric index of reflection
- * @kp: location for asymmetric index of reflection
- * @lp: location for asymmetric index of reflection
+ * \param ops A \ref SymOpList, usually corresponding to a point group
+ * \param h index of a reflection
+ * \param k index of a reflection
+ * \param l index of a reflection
+ * \param hp location for asymmetric index of reflection
+ * \param kp location for asymmetric index of reflection
+ * \param lp location for asymmetric index of reflection
*
- * This function determines the asymmetric version of the reflection @h, @k, @l
- * in symmetry group @ops, and puts the result in @hp, @kp, @lp.
+ * This function determines the asymmetric version of the reflection \p h, \p k, \p l
+ * in symmetry group \p ops, and puts the result in \p hp, \p kp, \p lp.
*
* This is a relatively expensive operation because of its generality.
* Therefore, if you know you'll need to make repeated use of the asymmetric
- * indices, consider creating a new %RefList indexed according to the asymmetric
- * indices themselves with asymmetric_indices(). If you do that, you'll still
+ * indices, consider creating a new \ref RefList indexed according to the asymmetric
+ * indices themselves with \ref asymmetric_indices. If you do that, you'll still
* be able to get the original versions of the indices with
- * get_symmetric_indices().
+ * \ref get_symmetric_indices.
*
**/
void get_asymm(const SymOpList *ops,
@@ -1336,10 +1316,9 @@ void get_asymm(const SymOpList *ops,
/**
- * is_centrosymmetric:
- * @s: A %SymOpList
+ * \param s A \ref SymOpList
*
- * Returns: non-zero if @s contains an inversion operation
+ * \returns Non-zero if \p s contains an inversion operation
*/
int is_centrosymmetric(const SymOpList *s)
{
@@ -1372,11 +1351,10 @@ static int check_mult(const IntegerMatrix *ans,
/**
- * is_subgroup:
- * @source: A %SymOpList
- * @target: Another %SymOpList, which might be a subgroup of @source.
+ * \param source A \ref SymOpList
+ * \param target Another \ref SymOpList, which might be a subgroup of \p source.
*
- * Returns: non-zero if every operation in @target is also in @source.
+ * \returns Non-zero if every operation in \p target is also in \p source.
**/
int is_subgroup(const SymOpList *source, const SymOpList *target)
{
@@ -1527,19 +1505,18 @@ static SymOpList *flack_reorder(const SymOpList *source)
/**
- * get_ambiguities:
- * @source: The "source" symmetry, a %SymOpList
- * @target: The "target" symmetry, a %SymOpList
+ * \param source The "source" symmetry, a \ref SymOpList
+ * \param target The "target" symmetry, a \ref SymOpList
- * Calculates twinning laws. Returns a %SymOpList containing the twinning
- * operators, which are the symmetry operations which can be added to @target
- * to generate @source. Only rotations are allowable - no mirrors nor
+ * Calculates twinning laws. Returns a \ref SymOpList containing the twinning
+ * operators, which are the symmetry operations which can be added to \p target
+ * to generate \p source. Only rotations are allowable - no mirrors nor
* inversions.
- * To count the number of possibilities, use num_equivs() on the result.
+ * To count the number of possibilities, use \ref num_equivs on the result.
*
* The algorithm used is "Algorithm A" from Flack (1987), Acta Cryst A43 p564.
*
- * Returns: A %SymOpList containing the twinning operators, or NULL if the
+ * \returns a \ref SymOpList containing the twinning operators, or NULL if the
* source symmetry cannot be generated from that target symmetry without using
* mirror or inversion operations.
*/
@@ -1636,105 +1613,74 @@ SymOpList *get_ambiguities(const SymOpList *source, const SymOpList *target)
}
-static IntegerMatrix *parse_symmetry_operation(const char *s)
+/* Parse a single symmetry operation, e.g. 'h,-2k,(h+l)/3' */
+RationalMatrix *parse_symmetry_operation(const char *s)
{
- IntegerMatrix *m;
- char **els;
- int n, i;
+ YY_BUFFER_STATE b;
+ RationalMatrix *m;
+ int r;
+ m = rtnl_mtx_new(3, 3);
+ b = symop_scan_string(s);
+ r = symopparse(m, NULL);
+ symop_delete_buffer(b);
- n = assplode(s, ",", &els, ASSPLODE_NONE);
- if ( n != 3 ) {
- for ( i=0; i<n; i++ ) free(els[i]);
- free(els);
+ if ( r ) {
+ ERROR("Failed to parse '%s'\n", s);
+ rtnl_mtx_free(m);
return NULL;
}
- m = intmat_new(3, 3);
- if ( m == NULL ) return NULL;
-
- for ( i=0; i<n; i++ ) {
-
- int c;
- size_t cl;
- signed int nh = 0;
- signed int nk = 0;
- signed int nl = 0;
- signed int mult = 1;
- int ndigit = 0;
- signed int sign = +1;
-
- /* We have one expression something like "-2h+k" */
- cl = strlen(els[i]);
- for ( c=0; c<cl; c++ ) {
-
- if ( els[i][c] == '-' ) sign *= -1;
- if ( els[i][c] == 'h' ) {
- nh = mult*sign;
- mult = 1;
- ndigit = 0;
- sign = +1;
- }
- if ( els[i][c] == 'k' ) {
- nk = mult*sign;
- mult = 1;
- ndigit = 0;
- sign = +1;
- }
- if ( els[i][c] == 'l' ) {
- nl = mult*sign;
- mult = 1;
- ndigit = 0;
- sign = +1;
- }
- if ( isdigit(els[i][c]) ) {
- if ( ndigit > 0 ) {
- mult *= 10;
- mult += els[i][c] - '0';
- } else {
- mult *= els[i][c] - '0';
- }
- ndigit++;
- }
- }
-
- intmat_set(m, i, 0, nh);
- intmat_set(m, i, 1, nk);
- intmat_set(m, i, 2, nl);
-
- free(els[i]);
+ return m;
+}
- }
- free(els);
- return m;
+/**
+ * \param s Textual representation of cell transformation
+ *
+ * Parses \p s, for example 'a,(b+a)/2,c', and returns the corresponding
+ * \ref RationalMatrix.
+ *
+ * \returns a \ref RationalMatrix describing the transformation, or NULL on error.
+ *
+ */
+RationalMatrix *parse_cell_transformation(const char *s)
+{
+ return parse_symmetry_operation(s);
}
+/**
+ * \param s Textual representation of a list of symmetry operations
+ *
+ * Parses \p s, for example 'h,k,l;k,h,-l', and returns the corresponding
+ * \ref SymOpList
+ *
+ * \returns a \ref SymOpList, or NULL on error.
+ *
+ */
SymOpList *parse_symmetry_operations(const char *s)
{
- SymOpList *sol;
- char **ops;
- int n, i;
+ YY_BUFFER_STATE b;
+ RationalMatrix *m;
+ SymOpList *list;
+ int r;
- sol = new_symoplist();
- if ( sol == NULL ) return NULL;
+ m = rtnl_mtx_new(3, 3); /* Scratch space for parser */
+ list = new_symoplist(); /* The result we want */
- n = assplode(s, ";:", &ops, ASSPLODE_NONE);
- for ( i=0; i<n; i++ ) {
- IntegerMatrix *m;
- m = parse_symmetry_operation(ops[i]);
- if ( m != NULL ) {
- add_symop(sol, m);
- } else {
- ERROR("Invalid symmetry operation '%s'\n", ops[i]);
- /* Try the next one */
- }
- free(ops[i]);
+ b = symop_scan_string(s);
+ r = symopparse(m, list);
+ symop_delete_buffer(b);
+ rtnl_mtx_free(m);
+
+ if ( r ) {
+ ERROR("Failed to parse '%s'\n", s);
+ free_symoplist(list);
+ return NULL;
}
- free(ops);
- return sol;
+ return list;
}
@@ -1749,7 +1695,7 @@ static void add_chars(char *t, const char *s, int max_len)
}
-char *get_matrix_name(const IntegerMatrix *m, int row)
+char *get_matrix_name(const IntegerMatrix *m, int col)
{
char *text;
const int max_len = 9;
@@ -1763,7 +1709,7 @@ char *get_matrix_name(const IntegerMatrix *m, int row)
signed int v;
- v = intmat_get(m, row, i);
+ v = intmat_get(m, i, col);
if ( v == 0 ) continue;
@@ -1816,8 +1762,7 @@ static char *name_equiv(const IntegerMatrix *op)
/**
- * describe_symmetry:
- * @s: A %SymOpList
+ * \param s A \ref SymOpList
*
* Writes the name and a list of operations to stderr.
*/
@@ -1860,10 +1805,9 @@ void describe_symmetry(const SymOpList *s)
/**
- * symmetry_name:
- * @ops: A %SymOpList
+ * \param ops A \ref SymOpList
*
- * Returns: a text description of @ops.
+ * \returns A text description of \p ops.
*/
const char *symmetry_name(const SymOpList *ops)
{
@@ -1872,12 +1816,11 @@ const char *symmetry_name(const SymOpList *ops)
/**
- * set_symmetry_name:
- * @ops: A %SymOpList
- * @name: New name for the %SymOpList
+ * \param ops A \ref SymOpList
+ * \param name New name for the \ref SymOpList
*
- * Sets the text description of @ops to @name. See symmetry_name().
- * @name will be copied, so you can safely free it after calling this function,
+ * Sets the text description of \p ops to \p name. See \ref symmetry_name.
+ * \p name will be copied, so you can safely free it after calling this function,
* if that's otherwise appropriate.
*/
void set_symmetry_name(SymOpList *ops, const char *name)
diff --git a/libcrystfel/src/symmetry.h b/libcrystfel/src/symmetry.h
index a61ca96f..42a2b9fb 100644
--- a/libcrystfel/src/symmetry.h
+++ b/libcrystfel/src/symmetry.h
@@ -3,12 +3,12 @@
*
* Symmetry
*
- * Copyright © 2012-2016 Deutsches Elektronen-Synchrotron DESY,
+ * Copyright © 2012-2019 Deutsches Elektronen-Synchrotron DESY,
* a research centre of the Helmholtz Association.
*
* Authors:
- * 2010-2014,2016 Thomas White <taw@physics.org>
- * 2014 Kenneth Beyerlein <kenneth.beyerlein@desy.de>
+ * 2010-2019 Thomas White <taw@physics.org>
+ * 2014 Kenneth Beyerlein <kenneth.beyerlein@desy.de>
*
* This file is part of CrystFEL.
*
@@ -36,10 +36,14 @@
#include "integer_matrix.h"
+#include "rational.h"
+
+/**
+ * \file symmetry.h
+ * Point group symmetry
+ */
/**
- * SymOpList
- *
* The SymOpList is an opaque data structure containing a list of point symmetry
* operations. It could represent an point group or a list of indexing
* ambiguities (twin laws), or similar.
@@ -47,11 +51,9 @@
typedef struct _symoplist SymOpList;
/**
- * SymOpMask
- *
* The SymOpMask is an opaque data structure containing a list of flags
- * associated with point symmetry operations in a specific %SymOpList. It is
- * used to filter the operations in the %SymOpList to avoid duplicating
+ * associated with point symmetry operations in a specific \ref SymOpList. It is
+ * used to filter the operations in the \ref SymOpList to avoid duplicating
* equivalent reflections when the reflection is somehow special (e.g. 'hk0').
**/
typedef struct _symopmask SymOpMask;
@@ -93,7 +95,9 @@ extern int is_centric(signed int h, signed int k, signed int l,
extern void pointgroup_warning(const char *sym);
extern void add_symop(SymOpList *ops, IntegerMatrix *m);
+extern RationalMatrix *parse_symmetry_operation(const char *s);
extern SymOpList *parse_symmetry_operations(const char *s);
+extern RationalMatrix *parse_cell_transformation(const char *s);
extern char *get_matrix_name(const IntegerMatrix *m, int row);
#ifdef __cplusplus
diff --git a/libcrystfel/src/symop.l b/libcrystfel/src/symop.l
new file mode 100644
index 00000000..ed38a65e
--- /dev/null
+++ b/libcrystfel/src/symop.l
@@ -0,0 +1,52 @@
+/*
+ * symop.l
+ *
+ * Lexical scanner for symmetry operations
+ *
+ * Copyright © 2019 Deutsches Elektronen-Synchrotron DESY,
+ * a research centre of the Helmholtz Association.
+ *
+ * Authors:
+ * 2019 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/>.
+ *
+ */
+
+%{
+ #define YYDEBUG 1
+ #include "rational.h"
+ #include "symop-parse.h"
+%}
+
+%option prefix="symop"
+%option noyywrap nounput noinput
+
+%%
+
+[,] { return COMMA; }
+[0-9]+ { symoplval.n = atoi(yytext); return NUMBER; }
+[/] { return DIVIDE; }
+[+] { return PLUS; }
+[-] { return MINUS; }
+[ahx] { return H; }
+[bky] { return K; }
+[clz] { return L; }
+[(] { return OPENB; }
+[)] { return CLOSEB; }
+[;] { return SEMICOLON; }
+
+%%
diff --git a/libcrystfel/src/symop.y b/libcrystfel/src/symop.y
new file mode 100644
index 00000000..be31c21d
--- /dev/null
+++ b/libcrystfel/src/symop.y
@@ -0,0 +1,140 @@
+/*
+ * symop.y
+ *
+ * Parser for symmetry operations
+ *
+ * Copyright © 2019 Deutsches Elektronen-Synchrotron DESY,
+ * a research centre of the Helmholtz Association.
+ *
+ * Authors:
+ * 2019 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/>.
+ *
+ */
+
+%{
+ #include <stdio.h>
+
+ #include "rational.h"
+ #include "symmetry.h"
+
+ extern int symoplex();
+ extern int symopparse(RationalMatrix *m, SymOpList *list);
+ void symoperror(RationalMatrix *m, SymOpList *list, const char *s);
+%}
+
+%define api.prefix {symop}
+
+%parse-param {RationalMatrix *m} {SymOpList *list}
+
+%code requires {
+ #include "symmetry.h"
+}
+
+%union {
+ RationalMatrix *m; /* Full rational matrix */
+ Rational rv[3]; /* Rational vector, e.g. '1/2h+3k' */
+ Rational r; /* Rational number */
+ int n; /* Just a number */
+}
+
+%token SEMICOLON
+%token COMMA
+%token NUMBER
+%token OPENB CLOSEB
+%token H K L
+
+%left PLUS MINUS
+%left DIVIDE
+%precedence MUL
+%precedence NEG
+
+%type <m> symop
+%type <rv> axexpr
+%type <rv> part
+%type <n> NUMBER
+%type <r> fraction
+
+%{
+static int try_add_symop(SymOpList *list, RationalMatrix *m, int complain)
+{
+ if ( list == NULL ) {
+ /* Only complain if this isn't the only operation provided */
+ if ( complain ) {
+ yyerror(m, list, "Must be a single symmetry operation");
+ }
+ return 1;
+ } else {
+ IntegerMatrix *im;
+ im = intmat_from_rtnl_mtx(m);
+ if ( im == NULL ) {
+ yyerror(m, list, "Symmetry operations must all be integer");
+ return 1;
+ } else {
+ add_symop(list, im);
+ }
+ }
+ return 0;
+}
+%}
+
+%%
+
+symoplist:
+ symop { try_add_symop(list, m, 0); }
+| symoplist SEMICOLON symop { if ( try_add_symop(list, m, 1) ) YYERROR; }
+;
+
+symop:
+ axexpr COMMA axexpr COMMA axexpr { rtnl_mtx_set(m, 0, 0, $1[0]);
+ rtnl_mtx_set(m, 1, 0, $1[1]);
+ rtnl_mtx_set(m, 2, 0, $1[2]);
+ rtnl_mtx_set(m, 0, 1, $3[0]);
+ rtnl_mtx_set(m, 1, 1, $3[1]);
+ rtnl_mtx_set(m, 2, 1, $3[2]);
+ rtnl_mtx_set(m, 0, 2, $5[0]);
+ rtnl_mtx_set(m, 1, 2, $5[1]);
+ rtnl_mtx_set(m, 2, 2, $5[2]);
+ }
+;
+
+axexpr:
+ part { int i; for ( i=0; i<3; i++ ) $$[i] = $1[i]; }
+| axexpr PLUS axexpr { int i; for ( i=0; i<3; i++ ) $$[i] = rtnl_add($1[i], $3[i]); }
+| axexpr MINUS axexpr { int i; for ( i=0; i<3; i++ ) $$[i] = rtnl_sub($1[i], $3[i]); }
+| MINUS axexpr %prec NEG { int i; for ( i=0; i<3; i++ ) $$[i] = rtnl_sub(rtnl_zero(), $2[i]); }
+| OPENB axexpr CLOSEB { int i; for ( i=0; i<3; i++ ) $$[i] = $2[i]; }
+| axexpr DIVIDE NUMBER { int i; for ( i=0; i<3; i++ ) $$[i] = rtnl_div($1[i], rtnl($3, 1)); }
+| NUMBER axexpr %prec MUL { int i; for ( i=0; i<3; i++ ) $$[i] = rtnl_mul($2[i], rtnl($1, 1)); }
+| fraction axexpr %prec MUL { int i; for ( i=0; i<3; i++ ) $$[i] = rtnl_mul($2[i], $1); }
+;
+
+part:
+ H { $$[0] = rtnl(1, 1); $$[1] = rtnl_zero(); $$[2] = rtnl_zero(); }
+| K { $$[1] = rtnl(1, 1); $$[0] = rtnl_zero(); $$[2] = rtnl_zero(); }
+| L { $$[2] = rtnl(1, 1); $$[0] = rtnl_zero(); $$[1] = rtnl_zero(); }
+;
+
+fraction:
+ NUMBER DIVIDE NUMBER { $$ = rtnl($1, $3); }
+;
+
+%%
+
+void symoperror(RationalMatrix *m, SymOpList *list, const char *s) {
+ printf("Error: %s\n", s);
+}
diff --git a/libcrystfel/src/taketwo.c b/libcrystfel/src/taketwo.c
index 203e5a05..ddbfe173 100644
--- a/libcrystfel/src/taketwo.c
+++ b/libcrystfel/src/taketwo.c
@@ -29,19 +29,22 @@
*/
/**
- * \class TakeTwo
- * Code outline.
- * --- Get ready for calculation ---
- * Pre-calculate symmetry operations (generate_rotation_symops())
- * Pre-calculate theoretical vectors from unit cell dimensions
+ * \file taketwo.h
+
+ * ## Code outline
+
+ * ### Get ready for calculation
+ * * Pre-calculate symmetry operations (generate_rotation_symops())
+ * * Pre-calculate theoretical vectors from unit cell dimensions
* (gen_theoretical_vecs())
- * Generate observed vectors from data (gen_observed_vecs())
- * Match observed vectors to theoretical vectors (match_obs_to_cell_vecs())
+ * * Generate observed vectors from data (gen_observed_vecs())
+ * * Match observed vectors to theoretical vectors (match_obs_to_cell_vecs())
+ *
+ * ### Business bit
*
- * --- Business bit ---
* ... n.b. rearranging to find all seeds in advance.
*
- * Find starting seeds (find_seeds()):
+ * * Find starting seeds (find_seeds()):
* - Loop through pairs of observed vectors
* - If they share a spot, find matching pairs of theoretical vectors
* - Remove all duplicate matches due to symmetry operations
@@ -50,7 +53,7 @@
* - If it returns a membership greater than the highest member threshold,
* return the matrix to CrystFEL.
*
- * Extending a seed (start_seed()):
+ * * Extending a seed (start_seed()):
* - Generate a rotation matrix which matches the chosen start seed.
* - Loop through all observed vectors starting from 0.
* - Find another vector to add to the network, if available
@@ -64,7 +67,7 @@
* - If the membership does not, then resume the loop and search for the
* next vector.
*
- * Finding the next member (find_next_index()):
+ * * Finding the next member (find_next_index()):
* - Go through the observed vectors, starting from the last index + 1 to
* explore only the "new" vectors.
* - If the vector does not share a spot with the current array of vectors,
@@ -75,20 +78,12 @@
* for a single matching theoretical vector.
* - If it does match, we can return a success.
*
- * Tidying the solution (finish_solution()):
+ * * Tidying the solution (finish_solution()):
* - This chooses the most common rotation matrix of the bunch to choose to
* send to CrystFEL. But this should probably take the average instead,
* which is very possible.
*
- * Clean up the mess (cleanup_taketwo_obs_vecs())
- */
-
-/**
- * Helen's to-do list
- * -
- *
- *
- * - Improve the final solution
+ * * Clean up the mess (cleanup_taketwo_obs_vecs())
*/
#include <gsl/gsl_matrix.h>
@@ -98,6 +93,7 @@
#include <assert.h>
#include <time.h>
+#include "cell.h"
#include "cell-utils.h"
#include "index.h"
#include "taketwo.h"
@@ -105,13 +101,12 @@
#include "symmetry.h"
/**
- * spotvec
- * @obsvec: an observed vector between two spots
- * @matches: array of matching theoretical vectors from unit cell
- * @match_num: number of matches
- * @distance: length of obsvec (do I need this?)
- * @her_rlp: pointer to first rlp position for difference vec
- * @his_rlp: pointer to second rlp position for difference vec
+ * \param obsvec an observed vector between two spots
+ * \param matches array of matching theoretical vectors from unit cell
+ * \param match_num number of matches
+ * \param distance length of obsvec (do I need this?)
+ * \param her_rlp pointer to first rlp position for difference vec
+ * \param his_rlp pointer to second rlp position for difference vec
*
* Structure representing 3D vector between two potential Bragg peaks
* in reciprocal space, and an array of potential matching theoretical
@@ -355,6 +350,54 @@ static void show_rvec(struct rvec r2)
* functions called under the core functions, still specialised (Level 3)
* ------------------------------------------------------------------------*/
+/* cell_transform_gsl_direct() doesn't do quite what we want here.
+ * The matrix m should be post-multiplied by a matrix of real or reciprocal
+ * basis vectors (it doesn't matter which because it's just a rotation).
+ * M contains the basis vectors written in columns: M' = mM */
+static UnitCell *cell_post_smiley_face(UnitCell *in, gsl_matrix *m)
+{
+ gsl_matrix *c;
+ double asx, asy, asz;
+ double bsx, bsy, bsz;
+ double csx, csy, csz;
+ gsl_matrix *res;
+ UnitCell *out;
+
+ cell_get_cartesian(in, &asx, &asy, &asz,
+ &bsx, &bsy, &bsz,
+ &csx, &csy, &csz);
+
+ c = gsl_matrix_alloc(3, 3);
+ gsl_matrix_set(c, 0, 0, asx);
+ gsl_matrix_set(c, 1, 0, asy);
+ gsl_matrix_set(c, 2, 0, asz);
+ gsl_matrix_set(c, 0, 1, bsx);
+ gsl_matrix_set(c, 1, 1, bsy);
+ gsl_matrix_set(c, 2, 1, bsz);
+ gsl_matrix_set(c, 0, 2, csx);
+ gsl_matrix_set(c, 1, 2, csy);
+ gsl_matrix_set(c, 2, 2, csz);
+
+ res = gsl_matrix_calloc(3, 3);
+ gsl_blas_dgemm(CblasNoTrans, CblasNoTrans, 1.0, m, c, 0.0, res);
+
+ out = cell_new_from_cell(in);
+ cell_set_cartesian(out, gsl_matrix_get(res, 0, 0),
+ gsl_matrix_get(res, 1, 0),
+ gsl_matrix_get(res, 2, 0),
+ gsl_matrix_get(res, 0, 1),
+ gsl_matrix_get(res, 1, 1),
+ gsl_matrix_get(res, 2, 1),
+ gsl_matrix_get(res, 0, 2),
+ gsl_matrix_get(res, 1, 2),
+ gsl_matrix_get(res, 2, 2));
+
+ gsl_matrix_free(res);
+ gsl_matrix_free(c);
+ return out;
+}
+
+
static void rotation_around_axis(struct rvec c, double th,
gsl_matrix *res)
{
@@ -463,35 +506,6 @@ static void closest_rot_mat(struct rvec vec1, struct rvec vec2,
rotation_around_axis(axis, bestAngle, twizzle);
}
-/*
-static double matrix_angle(gsl_matrix *m)
-{
- double a = gsl_matrix_get(m, 0, 0);
- double b = gsl_matrix_get(m, 1, 1);
- double c = gsl_matrix_get(m, 2, 2);
-
- double cos_t = (a + b + c - 1) / 2;
- double theta = acos(cos_t);
-
- return theta;
-}
-
-static struct rvec matrix_axis(gsl_matrix *a)
-{
- double ang = matrix_angle(a);
- double cos_t = cos(ang);
- double p = gsl_matrix_get(a, 0, 0);
- double q = gsl_matrix_get(a, 1, 1);
- double r = gsl_matrix_get(a, 2, 2);
- double x = sqrt((p - cos_t) / (1 - cos_t));
- double y = sqrt((q - cos_t) / (1 - cos_t));
- double z = sqrt((r - cos_t) / (1 - cos_t));
-
- struct rvec v = new_rvec(x, y, z);
- return v;
-}
-*/
-
static double matrix_trace(gsl_matrix *a)
{
int i;
@@ -1601,7 +1615,8 @@ static unsigned int start_seeds(gsl_matrix **rotation, struct TakeTwoCell *cell)
}
-static void set_gsl_matrix(gsl_matrix *mat, double asx, double asy, double asz,
+static void set_gsl_matrix(gsl_matrix *mat,
+ double asx, double asy, double asz,
double bsx, double bsy, double bsz,
double csx, double csy, double csz)
{
@@ -1630,15 +1645,23 @@ static int generate_rotation_sym_ops(struct TakeTwoCell *ttCell)
gsl_matrix *recip = gsl_matrix_alloc(3, 3);
gsl_matrix *cart = gsl_matrix_alloc(3, 3);
- cell_get_reciprocal(ttCell->cell, &asx, &asy, &asz, &bsx, &bsy,
- &bsz, &csx, &csy, &csz);
- set_gsl_matrix(recip, asx, asy, asz, bsx, bsy, bsz, csx, csy, csz);
+ cell_get_reciprocal(ttCell->cell, &asx, &asy, &asz,
+ &bsx, &bsy, &bsz,
+ &csx, &csy, &csz);
+
+ set_gsl_matrix(recip, asx, asy, asz,
+ asx, bsy, bsz,
+ csx, csy, csz);
+
+ cell_get_cartesian(ttCell->cell, &asx, &asy, &asz,
+ &bsx, &bsy, &bsz,
+ &csx, &csy, &csz);
- cell_get_cartesian(ttCell->cell, &asx, &asy, &asz, &bsx, &bsy,
- &bsz, &csx, &csy, &csz);
+ set_gsl_matrix(cart, asx, bsx, csx,
+ asy, bsy, csy,
+ asz, bsz, csz);
- set_gsl_matrix(cart, asx, asy, asz, bsx, bsy, bsz, csx, csy, csz);
int i, j, k;
int numOps = num_equivs(rawList, NULL);
@@ -2058,7 +2081,7 @@ static UnitCell *run_taketwo(UnitCell *cell, const struct taketwo_options *opts,
tp->numPrevs++;
/* Prepare the solution for CrystFEL friendliness */
- result = transform_cell_gsl(cell, solution);
+ result = cell_post_smiley_face(cell, solution);
cleanup_taketwo_cell(&ttCell);
return result;
diff --git a/libcrystfel/src/taketwo.h b/libcrystfel/src/taketwo.h
index fcd7aebc..6960446d 100644
--- a/libcrystfel/src/taketwo.h
+++ b/libcrystfel/src/taketwo.h
@@ -34,6 +34,8 @@
#include "cell.h"
#include "index.h"
+/** \file taketwo.h */
+
struct taketwo_options
{
int member_thresh;
diff --git a/libcrystfel/src/thread-pool.c b/libcrystfel/src/thread-pool.c
index 709b1521..936baf92 100644
--- a/libcrystfel/src/thread-pool.c
+++ b/libcrystfel/src/thread-pool.c
@@ -41,19 +41,7 @@
#include "utils.h"
-/**
- * SECTION:thread-pool
- * @short_description: The thread pool
- * @title: The thread pool
- * @section_id:
- * @see_also:
- * @include: "thread-pool.h"
- * @Image:
- *
- * The thread pool helps when running many tasks in parallel. It takes care of
- * starting and stopping threads, and presents a relatively simple interface to
- * the individual programs.
- */
+/** \file thread-pool.h */
/* --------------------------- Status label stuff --------------------------- */
@@ -151,32 +139,31 @@ static void *task_worker(void *pargsv)
/**
- * run_threads:
- * @n_threads: The number of threads to run in parallel
- * @work: The function to be called to do the work
- * @get_task: The function which will determine the next unassigned task
- * @final: The function which will be called to clean up after a task
- * @queue_args: A pointer to any data required to determine the next task
- * @max: Stop calling get_task after starting this number of jobs
- * @cpu_num: Ignored
- * @cpu_groupsize: Ignored
- * @cpu_offset: Ignored
+ * \param n_threads The number of threads to run in parallel
+ * \param work The function to be called to do the work
+ * \param get_task The function which will determine the next unassigned task
+ * \param final The function which will be called to clean up after a task
+ * \param queue_args A pointer to any data required to determine the next task
+ * \param max Stop calling get_task after starting this number of jobs
+ * \param cpu_num Ignored
+ * \param cpu_groupsize Ignored
+ * \param cpu_offset Ignored
*
- * 'get_task' will be called every time a worker is idle. It returns either
+ * \p get_task will be called every time a worker is idle. It returns either
* NULL, indicating that no further work is available, or a pointer which will
- * be passed to 'work'.
+ * be passed to \p work.
*
- * 'final' will be called once per image, and will be given both queue_args
+ * \p final will be called once per image, and will be given both queue_args
* and the last task pointer.
*
- * 'get_task' and 'final' will be called only under lock, and so do NOT need to
+ * \p get_task and \p final will be called only under lock, and so do NOT need to
* be re-entrant or otherwise thread safe. 'work', of course, needs to be
* thread safe.
*
- * Work will stop after 'max' tasks have been processed whether get_task
- * returned NULL or not. If "max" is zero, all tasks will be processed.
+ * Work will stop after \p max tasks have been processed whether get_task
+ * returned NULL or not. If \p max is zero, all tasks will be processed.
*
- * Returns: The number of tasks completed.
+ * \returns The number of tasks completed.
**/
int run_threads(int n_threads, TPWorkFunc work,
TPGetTaskFunc get_task, TPFinalFunc final,
diff --git a/libcrystfel/src/thread-pool.h b/libcrystfel/src/thread-pool.h
index 5a305d5d..d1def8b9 100644
--- a/libcrystfel/src/thread-pool.h
+++ b/libcrystfel/src/thread-pool.h
@@ -43,41 +43,39 @@ extern "C" {
extern pthread_mutex_t stderr_lock;
extern signed int get_status_label(void);
+/**
+ * \file thread-pool.h
+ * Thread pool.
+ */
/**
- * TPGetTaskFunc:
- * @qargs: The queue_args pointer which was given to run_threads().
- * Returns: A pointer which will be passed to the worker function.
+ * \p qargs: The queue_args pointer which was given to run_threads().
+ * \returns a pointer which will be passed to the worker function.
*
* This function is called, non-reentrantly, to get a new work item to give to
* your work function. The stuff you need to generate the new work item should
- * have been stored in @qargs which was passed to run_threads().
- *
+ * have been stored in \p qargs which was passed to \ref run_threads.
**/
typedef void *(*TPGetTaskFunc)(void *qargs);
/**
- * TPWorkFunc:
- * @work: The queue_args pointer which was given to run_threads().
- * @cookie: A small integral number which is guaranteed to be unique among all
+ * \param work The queue_args pointer which was given to \ref run_threads.
+ * \param cookie A small integral number which is guaranteed to be unique among all
* currently running threads.
*
* This function is called, reentrantly, for each work item.
- *
**/
typedef void (*TPWorkFunc)(void *work, int cookie);
/**
- * TPFinalFunc:
- * @qargs: The queue_args pointer which was given to run_threads().
- * @work: The pointer which was returned by your get_task function.
+ * \param qargs The queue_args pointer which was given to \ref run_threads.
+ * \param work The pointer which was returned by your get_task function.
*
* This function is called, non-reentrantly, after each work item has been
- * completed. A typical use might be to update some counters inside @qargs
- * according to fields withing @work which were filled by your 'work' function.
- *
+ * completed. A typical use might be to update some counters inside \p qargs
+ * according to fields withing \p work which were filled by your 'work' function.
**/
typedef void (*TPFinalFunc)(void *qargs, void *work);
diff --git a/libcrystfel/src/utils.c b/libcrystfel/src/utils.c
index 19dfe2a8..a2ba2c60 100644
--- a/libcrystfel/src/utils.c
+++ b/libcrystfel/src/utils.c
@@ -46,25 +46,13 @@
#include "utils.h"
#include "image.h"
+/** \file utils.h */
/**
- * SECTION:utils
- * @short_description: Miscellaneous utilities
- * @title: Utilities
- * @section_id:
- * @see_also:
- * @include: "utils.h"
- * @Image:
+ * \param M A matrix
+ * \param v A vector
*
- * Wibble
- */
-
-/**
- * show_matrix_eqn:
- * @M: A matrix
- * @v: A vector
- *
- * Displays a matrix equation of the form @M.a = @v.
+ * Displays a matrix equation of the form \p M.a = \p v.
**/
void show_matrix_eqn(gsl_matrix *M, gsl_vector *v)
{
@@ -91,8 +79,7 @@ void show_matrix_eqn(gsl_matrix *M, gsl_vector *v)
/**
- * show_matrix:
- * @M: A matrix
+ * \param M A matrix
*
* Displays a matrix.
**/
@@ -155,11 +142,10 @@ static int check_eigen(gsl_vector *e_val, int verbose)
/**
- * 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
+ * \param v a gsl_vector
+ * \param M a gsl_matrix
+ * \param pn_filt pointer to store the number of filtered eigenvalues
+ * \param verbose flag for verbosity on the terminal
*
* Solves the matrix equation M.x = v, returning x.
* Performs rescaling and eigenvalue filtering.
@@ -412,7 +398,8 @@ static int assplode_extract(char ***pbits, int n, size_t n_captured,
}
-/* Split the string 'a' using 'delims' as a zero-terminated list of
+/**
+ * Split the string 'a' using 'delims' as a zero-terminated list of
* deliminators.
* Store each segment in bits[0...n] where n is the number of segments and is
* the return value. pbits = &bits
@@ -569,26 +556,11 @@ void utils_fudge_gslcblas()
/**
- * SECTION:quaternion
- * @short_description: Simple quaternion handling
- * @title: Quaternion
- * @section_id:
- * @see_also:
- * @include: "utils.h"
- * @Image:
- *
- * There is a simple quaternion structure in CrystFEL. At the moment, it is
- * only used when simulating patterns, as an argument to cell_rotate() to
- * orient the unit cell.
- */
-
-/**
- * quaternion_modulus:
- * @q: A %quaternion
+ * \param q A \ref quaternion
*
* If a quaternion represents a pure rotation, its modulus should be unity.
*
- * Returns: the modulus of the given quaternion.
+ * \returns The modulus of the given quaternion.
**/
double quaternion_modulus(struct quaternion q)
{
@@ -597,12 +569,11 @@ double quaternion_modulus(struct quaternion q)
/**
- * normalise_quaternion:
- * @q: A %quaternion
+ * \param q A \ref quaternion
*
* Rescales the quaternion such that its modulus is unity.
*
- * Returns: the normalised version of @q
+ * \returns The normalised version of \p q
**/
struct quaternion normalise_quaternion(struct quaternion q)
{
@@ -621,10 +592,9 @@ struct quaternion normalise_quaternion(struct quaternion q)
/**
- * random_quaternion:
- * @rng: A GSL random number generator to use
+ * \param rng A GSL random number generator to use
*
- * Returns: a randomly generated, normalised, quaternion.
+ * \returns A randomly generated, normalised, quaternion.
**/
struct quaternion random_quaternion(gsl_rng *rng)
{
@@ -641,8 +611,7 @@ struct quaternion random_quaternion(gsl_rng *rng)
/**
- * quaternion_valid:
- * @q: A %quaternion
+ * \param q A \ref quaternion
*
* Checks if the given quaternion is normalised.
*
@@ -650,7 +619,7 @@ struct quaternion random_quaternion(gsl_rng *rng)
* <code>(modulus > 0.999) && (modulus < 1.001)</code>, and so should not be
* relied upon to spot anything other than the most obvious input error.
*
- * Returns: 1 if the quaternion is normalised, 0 if not.
+ * \returns 1 if the quaternion is normalised, 0 if not.
**/
int quaternion_valid(struct quaternion q)
{
@@ -667,13 +636,12 @@ int quaternion_valid(struct quaternion q)
/**
- * quat_rot
- * @q: A vector (in the form of a "struct rvec")
- * @z: A %quaternion
+ * \param q A vector (in the form of an \ref rvec struct)
+ * \param z A \ref quaternion
*
* Rotates a vector according to a quaternion.
*
- * Returns: A rotated version of @p.
+ * \returns a rotated version of \p p.
**/
struct rvec quat_rot(struct rvec q, struct quaternion z)
{
diff --git a/libcrystfel/src/utils.h b/libcrystfel/src/utils.h
index 65ab9b00..cd21bd93 100644
--- a/libcrystfel/src/utils.h
+++ b/libcrystfel/src/utils.h
@@ -46,6 +46,11 @@
#include "thread-pool.h"
+/**
+ * \file utils.h
+ * Miscellaneous utility functions
+ */
+
/* -------------------------- Fundamental constants ------------------------ */
/* Electron charge in C */
@@ -75,13 +80,11 @@ extern size_t notrail(char *s);
extern void chomp(char *s);
/**
- * AssplodeFlag:
- * @ASSPLODE_NONE: Nothing
- * @ASSPLODE_DUPS: Don't merge deliminators
+ * Controls the behaviour of \ref assplode.
**/
typedef enum {
- ASSPLODE_NONE = 0,
- ASSPLODE_DUPS = 1<<0
+ ASSPLODE_NONE = 0, /**< Nothing */
+ ASSPLODE_DUPS = 1<<0 /**< Don't merge deliminators */
} AssplodeFlag;
extern int assplode(const char *a, const char *delims, char ***pbits,
AssplodeFlag flags);
@@ -237,14 +240,7 @@ extern void strip_extension(char *bfn);
/* ------------------------------ Quaternions ------------------------------- */
/**
- * quaternion:
- * @w: component
- * @x: component
- * @y: component
- * @z: component
- *
* A structure representing a quaternion.
- *
**/
struct quaternion;
diff --git a/libcrystfel/src/xds.c b/libcrystfel/src/xds.c
index 3c5864a5..c0fccc7b 100644
--- a/libcrystfel/src/xds.c
+++ b/libcrystfel/src/xds.c
@@ -59,6 +59,7 @@
#include "detector.h"
#include "cell-utils.h"
+/** \file xds.h */
/* Fake pixel size and camera length, both in metres */
#define FAKE_PIXEL_SIZE (70e-6)
diff --git a/libcrystfel/src/xds.h b/libcrystfel/src/xds.h
index c89b615c..811c0dc9 100644
--- a/libcrystfel/src/xds.h
+++ b/libcrystfel/src/xds.h
@@ -42,6 +42,11 @@
extern "C" {
#endif
+/**
+ * \file xds.h
+ * XDS indexer interface
+ */
+
extern int run_xds(struct image *image, void *ipriv);
extern void *xds_prepare(IndexingMethod *indm, UnitCell *cell);
diff --git a/libcrystfel/src/xgandalf.c b/libcrystfel/src/xgandalf.c
index 5f31df33..6a8ba4d5 100644
--- a/libcrystfel/src/xgandalf.c
+++ b/libcrystfel/src/xgandalf.c
@@ -39,6 +39,8 @@
#include "xgandalf/adaptions/crystfel/ExperimentSettings.h"
#include "xgandalf/adaptions/crystfel/IndexerPlain.h"
+/** \file xgandalf.h */
+
struct xgandalf_private_data {
IndexerPlain *indexer;
reciprocalPeaks_1_per_A_t reciprocalPeaks_1_per_A;
@@ -46,7 +48,7 @@ struct xgandalf_private_data {
IndexingMethod indm;
UnitCell *cellTemplate;
Lattice_t sampleRealLattice_A; //same as cellTemplate
- UnitCellTransformation *uncenteringTransformation;
+ IntegerMatrix *centeringTransformation;
LatticeTransform_t latticeReductionTransform;
};
@@ -114,7 +116,7 @@ int run_xgandalf(struct image *image, void *ipriv)
if(xgandalf_private_data->cellTemplate != NULL){
restoreCell(uc, &xgandalf_private_data->latticeReductionTransform);
- UnitCell *new_cell_trans = cell_transform_inverse(uc, xgandalf_private_data->uncenteringTransformation);
+ UnitCell *new_cell_trans = cell_transform_intmat(uc, xgandalf_private_data->centeringTransformation);
cell_free(uc);
uc = new_cell_trans;
@@ -147,7 +149,7 @@ void *xgandalf_prepare(IndexingMethod *indm, UnitCell *cell,
allocReciprocalPeaks(&(xgandalf_private_data->reciprocalPeaks_1_per_A));
xgandalf_private_data->indm = *indm;
xgandalf_private_data->cellTemplate = NULL;
- xgandalf_private_data->uncenteringTransformation = NULL;
+ xgandalf_private_data->centeringTransformation = NULL;
float tolerance = xgandalf_opts->tolerance;
samplingPitch_t samplingPitch = xgandalf_opts->sampling_pitch;
@@ -157,13 +159,10 @@ void *xgandalf_prepare(IndexingMethod *indm, UnitCell *cell,
xgandalf_private_data->cellTemplate = cell;
- UnitCell* primitiveCell = uncenter_cell(cell, &xgandalf_private_data->uncenteringTransformation);
+ UnitCell* primitiveCell = uncenter_cell(cell, &xgandalf_private_data->centeringTransformation, NULL);
- UnitCell *uc = cell_new_from_cell(primitiveCell);
reduceCell(primitiveCell, &xgandalf_private_data->latticeReductionTransform);
- cell_free(uc);
-
double asx, asy, asz, bsx, bsy, bsz, csx, csy, csz;
int ret = cell_get_reciprocal(primitiveCell, &asx, &asy, &asz,
&bsx, &bsy, &bsz,
@@ -201,10 +200,6 @@ void *xgandalf_prepare(IndexingMethod *indm, UnitCell *cell,
FAKE_REFLECTION_RADIUS);
xgandalf_private_data->indexer = IndexerPlain_new(experimentSettings);
- IndexerPlain_setSamplingPitch(xgandalf_private_data->indexer,
- samplingPitch);
- IndexerPlain_setGradientDescentIterationsCount(xgandalf_private_data->indexer,
- gradientDescentIterationsCount);
if (xgandalf_opts->no_deviation_from_provided_cell) {
IndexerPlain_setRefineWithExactLattice(xgandalf_private_data->indexer, 1);
@@ -229,14 +224,17 @@ void *xgandalf_prepare(IndexingMethod *indm, UnitCell *cell,
FAKE_REFLECTION_RADIUS);
xgandalf_private_data->indexer = IndexerPlain_new(experimentSettings);
- IndexerPlain_setSamplingPitch(xgandalf_private_data->indexer,
- samplingPitch);
- IndexerPlain_setGradientDescentIterationsCount(xgandalf_private_data->indexer,
- gradientDescentIterationsCount);
ExperimentSettings_delete(experimentSettings);
}
+ IndexerPlain_setSamplingPitch(xgandalf_private_data->indexer,
+ samplingPitch);
+ IndexerPlain_setGradientDescentIterationsCount(xgandalf_private_data->indexer,
+ gradientDescentIterationsCount);
+ IndexerPlain_setMaxPeaksToUseForIndexing(xgandalf_private_data->indexer,
+ xgandalf_opts->maxPeaksForIndexing);
+
/* Flags that XGANDALF knows about */
*indm &= INDEXING_METHOD_MASK | INDEXING_USE_CELL_PARAMETERS;
@@ -250,8 +248,8 @@ void xgandalf_cleanup(void *pp)
freeReciprocalPeaks(xgandalf_private_data->reciprocalPeaks_1_per_A);
IndexerPlain_delete(xgandalf_private_data->indexer);
- if(xgandalf_private_data->uncenteringTransformation != NULL){
- tfn_free(xgandalf_private_data->uncenteringTransformation);
+ if(xgandalf_private_data->centeringTransformation != NULL){
+ intmat_free(xgandalf_private_data->centeringTransformation);
}
free(xgandalf_private_data);
}
diff --git a/libcrystfel/src/xgandalf.h b/libcrystfel/src/xgandalf.h
index 1aced417..23c5c1b0 100644
--- a/libcrystfel/src/xgandalf.h
+++ b/libcrystfel/src/xgandalf.h
@@ -35,6 +35,11 @@
#include <stddef.h>
+/**
+ * \file xgandalf.h
+ * XGANDALF indexer interface
+ */
+
struct xgandalf_options {
unsigned int sampling_pitch;
unsigned int grad_desc_iterations;
@@ -42,6 +47,7 @@ struct xgandalf_options {
unsigned int no_deviation_from_provided_cell;
float minLatticeVectorLength_A;
float maxLatticeVectorLength_A;
+ int maxPeaksForIndexing;
};
#include "index.h"
diff --git a/scripts/extract-geom b/scripts/extract-geom
index 13f7ac2e..a5d42057 100755
--- a/scripts/extract-geom
+++ b/scripts/extract-geom
@@ -8,6 +8,6 @@ with open(sys.argv[1]) as f:
s = f.readline()
s = f.readline()
while "End geometry file" not in s:
- print(s),
+ print(s.rstrip("\r\n")),
s = f.readline()
diff --git a/scripts/move-entire-detector b/scripts/move-entire-detector
index ffd6120f..9e31a281 100755
--- a/scripts/move-entire-detector
+++ b/scripts/move-entire-detector
@@ -21,13 +21,14 @@ op.add_argument('--output', dest='ofn', metavar='output.geom',
help="Output geometry file")
op.add_argument('--pixels', action='store_true', dest='px',
- help="Indicates that the shifts are in pixel units")
+ help="Indicates that the x and y shifts are in pixel units. "
+ "The z shift is always in millimeters")
op.add_argument('--mm', action='store_false', dest='px',
help="Indicates that the shifts are in millimeters (this is the default)")
op.add_argument('--beam', action='store_true', dest='beam',
- help="Shift the X-ray beam rather than the detector")
+ help="Shift the X-ray beam rather than the detector (reverses x and y shifts)")
op.add_argument('--detector', action='store_false', dest='beam',
help="Shift the detector rather than the X-ray beam. This is the default.")
@@ -41,6 +42,9 @@ op.add_argument('xshift', type=float, metavar='XXX',
op.add_argument('yshift', type=float, metavar='YYY',
help="The shift in the y direction")
+op.add_argument('zshift', type=float, metavar='ZZZ',
+ help="The shift in the z direction")
+
args = op.parse_args()
if not args.ofn:
@@ -59,7 +63,7 @@ else:
bm = 'detector'
print('Input filename {}\nOutput filename {}'.format(args.ifn,out))
-print('Shifting {} by {},{} {}'.format(bm, args.xshift, args.yshift, units))
+print('Shifting {} by {},{},{} {}'.format(bm, args.xshift, args.yshift, args.zshift, units))
if args.beam:
args.xshift = -args.xshift
@@ -68,12 +72,17 @@ if args.beam:
f = open(args.ifn, 'r')
h = open(out, 'w')
panel_resolutions = {}
+panels = []
+panels_have_coffset = []
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")
+prog5 = re.compile("^\s*(.*)\/coffset\s+=\s+([0-9\.\-]+)\s")
+prog6 = re.compile("^\s*coffset\s+=\s+([0-9\.]+)\s")
default_res = 0
+default_coffset = None
while True:
fline = f.readline()
@@ -86,6 +95,12 @@ while True:
h.write(fline)
continue
+ match = prog6.match(fline)
+ if match:
+ default_coffset = float(match.group(1))
+ h.write("coffset = %f\n" % (default_coffset+args.zshift))
+ continue
+
match = prog2.match(fline)
if match:
panel = match.group(1)
@@ -98,6 +113,14 @@ while True:
match = prog3.match(fline)
if match:
panel = match.group(1)
+ if panel not in panels:
+ panels.append(panel)
+
+ # If we have a default coffset, this panel has a coffset
+ # (which has already been adjusted)
+ if default_coffset is not None:
+ panels_have_coffset.append(panel)
+
panel_cnx = float(match.group(2))
if panel in panel_resolutions:
res = panel_resolutions[panel]
@@ -127,8 +150,20 @@ while True:
h.write('%s/corner_y = %f\n' % (panel,panel_cny+(args.yshift*res*1e-3)))
continue
+ match = prog5.match(fline)
+ if match:
+ panel = match.group(1)
+ panel_coffset = float(match.group(2))
+ h.write('%s/coffset = %f\n' % (panel,panel_coffset+args.zshift*1e-3))
+ panels_have_coffset.append(panel)
+ continue
+
h.write(fline)
+h.write("\n")
+for p in [n for n in panels if n not in panels_have_coffset]:
+ h.write("%s/coffset = %f\n" % (p, args.zshift*1e-3))
+
f.close()
h.close()
diff --git a/scripts/plot-pr b/scripts/plot-pr
index 25427cae..ccd925a1 100755
--- a/scripts/plot-pr
+++ b/scripts/plot-pr
@@ -95,14 +95,14 @@ def read_spectrum(filename):
def next_click(w):
global specgraph, crystal
crystal += 20
- specgraph = read_spectrum("pr-logs/specgraph-crystal%i.dat" % crystal)
+ specgraph = read_spectrum("%s/specgraph-crystal%i.dat" % (folder,crystal))
update_graph()
def prev_click(w):
global specgraph, crystal
crystal -= 20
- specgraph = read_spectrum("pr-logs/specgraph-crystal%i.dat" % crystal)
+ specgraph = read_spectrum("%s/specgraph-crystal%i.dat" % (folder,crystal))
update_graph()
@@ -116,8 +116,15 @@ def set_sensible_axes(w):
# Read the pgraph file
pgraph = []
+if len(sys.argv) == 2:
+ folder = sys.argv[1]
+else:
+ folder = "pr-logs"
+
+print("Using folder '%s'" % folder)
+
try:
- fh = open("pr-logs/pgraph.dat", 'r')
+ fh = open("%s/pgraph.dat" % folder, 'r')
except IOError:
print("Failed to read "+filename)
raise IOError
@@ -137,7 +144,7 @@ while True:
# Read the spectrum file
-specgraph = read_spectrum("pr-logs/specgraph-crystal%i.dat" % crystal)
+specgraph = read_spectrum("%s/specgraph-crystal%i.dat" % (folder,crystal))
fig = plt.figure(figsize=(15,6))
fig.subplots_adjust(left=0.05, right=0.65)
diff --git a/scripts/plot-contourmap b/scripts/plot-pr-contourmap
index 8b8e60c3..39fedc97 100755
--- a/scripts/plot-contourmap
+++ b/scripts/plot-pr-contourmap
@@ -44,7 +44,7 @@ def update_graph():
global im, cnt, centre_marker
- filename="pr-logs/grid-crystal%i-cycle%s-%s.dat" % (crystal,cycle,varpair)
+ filename="%s/grid-crystal%i-cycle%s-%s.dat" % (folder,crystal,cycle,varpair)
print filename
with open(filename, "r") as f:
@@ -99,11 +99,18 @@ def update_graph():
fig = plt.figure(figsize=(10,5))
fig.subplots_adjust(left=0.05, bottom=0.05, right=0.70, top=0.95)
+if len(sys.argv) == 2:
+ folder = sys.argv[1]
+else:
+ folder = "pr-logs"
+
+print("Using folder '%s'" % folder)
+
# Find out what there is to plot
crystals = []
cycles = []
varpairs = []
-for file in os.listdir("pr-logs"):
+for file in os.listdir(folder):
if not fnmatch.fnmatch(file, "grid-*.dat"):
continue
sp = file.rstrip(".dat").split("-")
diff --git a/src/cell_explorer.c b/src/cell_explorer.c
index 69dd7178..446aec5d 100644
--- a/src/cell_explorer.c
+++ b/src/cell_explorer.c
@@ -39,6 +39,7 @@
#include <math.h>
#include <gdk/gdkkeysyms-compat.h>
#include <gsl/gsl_multifit_nlin.h>
+#include <assert.h>
#include "stream.h"
#include "image.h"
@@ -519,36 +520,6 @@ static gboolean keyconf_sig(GtkWidget *key, GdkEventConfigure *event,
}
-static gint keyclick_sig(GtkWidget *widget, GdkEventButton *event,
- CellWindow *w)
-{
- int width, cat;
- GtkAllocation alloc;
-
- /* Ignore extra events for double click */
- if ( event->type != GDK_BUTTON_PRESS ) return FALSE;
-
- gtk_widget_get_allocation(widget, &alloc);
- width = alloc.width;
-
- cat = 8*event->x / width;
-
- if ( cat == 0 ) {
- /* Special handling for P so that it doesn't go
- * black->black->grey */
- w->cols_on[cat] = (w->cols_on[cat]+1) % 2;
- } else {
- w->cols_on[cat] = (w->cols_on[cat]+1) % 3;
- }
-
- gtk_widget_queue_draw(widget);
- redraw_all(w);
-
- return TRUE;
-}
-
-
-
static gboolean keydraw_sig(GtkWidget *da, cairo_t *cr, CellWindow *w)
{
int width, height;
@@ -694,15 +665,19 @@ static void scan_cells(CellWindow *w)
al = rad2deg(al); be = rad2deg(be); ga = rad2deg(ga);
switch ( cell_get_centering(cells[i]) ) {
- case 'P' : cat = 1<<CAT_P; break;
- case 'A' : cat = 1<<CAT_A; break;
- case 'B' : cat = 1<<CAT_B; break;
- case 'C' : cat = 1<<CAT_C; break;
- case 'I' : cat = 1<<CAT_I; break;
- case 'F' : cat = 1<<CAT_F; break;
- case 'H' : cat = 1<<CAT_H; break;
- case 'R' : cat = 1<<CAT_R; break;
- default : abort();
+ case 'P' : cat = CAT_P; break;
+ case 'A' : cat = CAT_A; break;
+ case 'B' : cat = CAT_B; break;
+ case 'C' : cat = CAT_C; break;
+ case 'I' : cat = CAT_I; break;
+ case 'F' : cat = CAT_F; break;
+ case 'H' : cat = CAT_H; break;
+ case 'R' : cat = CAT_R; break;
+
+ default :
+ ERROR("Unknown centering '%c'\n",
+ cell_get_centering(cells[i]));
+ continue;
}
if ( check_exclude(w->hist_a, a) ||
@@ -712,16 +687,19 @@ static void scan_cells(CellWindow *w)
check_exclude(w->hist_be, be) ||
check_exclude(w->hist_ga, ga) )
{
- cat = (unsigned)1<<CAT_EXCLUDE;
+ cat = CAT_EXCLUDE;
+ n_excl++;
+ } else if ( w->cols_on[cat] == 0 ) {
+ cat = CAT_EXCLUDE;
n_excl++;
}
- multihistogram_add_value(w->hist_a->h, a, cat);
- multihistogram_add_value(w->hist_b->h, b, cat);
- multihistogram_add_value(w->hist_c->h, c, cat);
- multihistogram_add_value(w->hist_al->h, al, cat);
- multihistogram_add_value(w->hist_be->h, be, cat);
- multihistogram_add_value(w->hist_ga->h, ga, cat);
+ multihistogram_add_value(w->hist_a->h, a, 1<<cat);
+ multihistogram_add_value(w->hist_b->h, b, 1<<cat);
+ multihistogram_add_value(w->hist_c->h, c, 1<<cat);
+ multihistogram_add_value(w->hist_al->h, al, 1<<cat);
+ multihistogram_add_value(w->hist_be->h, be, 1<<cat);
+ multihistogram_add_value(w->hist_ga->h, ga, 1<<cat);
}
@@ -729,6 +707,37 @@ static void scan_cells(CellWindow *w)
}
+static gint keyclick_sig(GtkWidget *widget, GdkEventButton *event,
+ CellWindow *w)
+{
+ int width, cat;
+ GtkAllocation alloc;
+
+ /* Ignore extra events for double click */
+ if ( event->type != GDK_BUTTON_PRESS ) return FALSE;
+
+ gtk_widget_get_allocation(widget, &alloc);
+ width = alloc.width;
+
+ cat = 8*event->x / width;
+
+ if ( cat == 0 ) {
+ /* Special handling for P so that it doesn't go
+ * black->black->grey */
+ w->cols_on[cat] = (w->cols_on[cat]+1) % 2;
+ } else {
+ w->cols_on[cat] = (w->cols_on[cat]+1) % 3;
+ }
+
+ scan_cells(w);
+ redraw_all(w);
+
+ gtk_widget_queue_draw(widget);
+
+ return TRUE;
+}
+
+
static void check_minmax(HistoBox *h, double val)
{
if ( val > h->max ) h->max = val;
@@ -976,6 +985,92 @@ static gint fit_sig(GtkWidget *widget, CellWindow *w)
}
+static void write_vals(FILE *fh, MultiHistogram *h, int nbin, char *cen, int cat)
+{
+ int *data;
+ int i;
+ fprintf(fh, "%s: ", cen);
+ data = multihistogram_get_data(h, cat);
+ for ( i=0; i<nbin; i++ ) {
+ fprintf(fh, "%i%s", data[i], (i==nbin-1) ? "\n" : ",");
+ }
+}
+
+
+static void write_multihistogram(FILE *fh, char *s, char *unit, HistoBox *h)
+{
+ fprintf(fh, "# %s. Range %f to %f %s. %i bins\n",
+ s, h->min, h->max, unit, h->n);
+ if ( h->have_fit ) {
+ fprintf(fh, "# Fitted curve: y = A.exp(-(x-B)^2/C^2, where "
+ "A = %f, B = %f, C = %f\n",
+ h->fit_a, h->fit_b, h->fit_c);
+
+ }
+ write_vals(fh, h->h, h->n, "P", CAT_P);
+ write_vals(fh, h->h, h->n, "A", CAT_A);
+ write_vals(fh, h->h, h->n, "B", CAT_B);
+ write_vals(fh, h->h, h->n, "C", CAT_C);
+ write_vals(fh, h->h, h->n, "I", CAT_I);
+ write_vals(fh, h->h, h->n, "F", CAT_F);
+ write_vals(fh, h->h, h->n, "R", CAT_R);
+ write_vals(fh, h->h, h->n, "H", CAT_H);
+ write_vals(fh, h->h, h->n, "Excluded", CAT_EXCLUDE);
+}
+
+
+static int write_histogram_data(CellWindow *w, const char *filename)
+{
+ FILE *fh;
+
+ fh = fopen(filename, "w");
+ if ( fh == NULL ) return 1;
+
+ fprintf(fh, "# Unit cell histogram data\n");
+ fprintf(fh, "# NB All data is included, not just what was visible in the "
+ "cell_explorer window.\n");
+
+ write_multihistogram(fh, "a axis length", "Angstrom", w->hist_a);
+ write_multihistogram(fh, "b axis length", "Angstrom", w->hist_b);
+ write_multihistogram(fh, "c axis length", "Angstrom", w->hist_c);
+ write_multihistogram(fh, "alpha angle", "degrees", w->hist_al);
+ write_multihistogram(fh, "beta angle", "degrees", w->hist_be);
+ write_multihistogram(fh, "gamma angle", "degrees", w->hist_ga);
+ fclose(fh);
+ return 0;
+}
+
+
+static gint savedata_sig(GtkWidget *widget, CellWindow *w)
+{
+ GtkWidget *d;
+ gint r;
+
+ d = gtk_file_chooser_dialog_new("Save Histogram Data",
+ GTK_WINDOW(w->window),
+ GTK_FILE_CHOOSER_ACTION_SAVE,
+ GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
+ GTK_STOCK_SAVE, GTK_RESPONSE_ACCEPT,
+ NULL);
+ gtk_file_chooser_set_do_overwrite_confirmation(GTK_FILE_CHOOSER(d),
+ TRUE);
+ r = gtk_dialog_run(GTK_DIALOG(d));
+ if ( r == GTK_RESPONSE_ACCEPT ) {
+
+ gchar *output_filename;
+
+ output_filename = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(d));
+ if ( write_histogram_data(w, output_filename) ) {
+ error_box(w, "Failed to save histogram data");
+ }
+ g_free(output_filename);
+
+ }
+ gtk_widget_destroy(d);
+ return FALSE;
+}
+
+
static int ninety(double a)
{
if ( fabs(rad2deg(a) - 90.0) < 0.3 ) return 1;
@@ -1184,14 +1279,275 @@ static int write_cell_to_file(UnitCell *cell, const char *filename)
}
+static char *cell_string(UnitCell *cell)
+{
+ LatticeType lt;
+ char cen;
+ double a, b, c, alpha, beta, gamma;
+ char *str;
+ size_t len;
+
+ lt = cell_get_lattice_type(cell);
+ cen = cell_get_centering(cell);
+
+ str = malloc(256);
+ if ( str == NULL ) return NULL;
+
+ len = snprintf(str, 32, "Unit cell: %s %c, ", str_lattice(lt), cen);
+
+ if ( (lt==L_MONOCLINIC) || (lt==L_TETRAGONAL) || ( lt==L_HEXAGONAL) ) {
+ len += snprintf(str+len, 32, "unique axis %c, ",
+ cell_get_unique_axis(cell));
+ }
+
+ cell_get_parameters(cell, &a, &b, &c, &alpha, &beta, &gamma);
+
+ snprintf(str+len, 128,
+ "a=%.2f Å, b=%.2f Å, c=%.2f Å, α=%.2f° β=%.2f° γ=%.2f°",
+ a*1e10, b*1e10, c*1e10,
+ rad2deg(alpha), rad2deg(beta), rad2deg(gamma));
+
+ return str;
+}
+
+
+struct save_cell_data
+{
+ GtkWidget *label;
+ UnitCell *orig_cell;
+ UnitCell *enforced_cell;
+ GtkWidget *combobox;
+ char *combo_ids[32]; /* Workaround for GTK < 3 */
+ int n_ids;
+};
+
+
+static UnitCell *enforce_cell(UnitCell *orig, const char *t)
+{
+ UnitCell *cell = cell_new_from_cell(orig);
+ double a, b, c, alpha, beta, gamma;
+
+ cell_get_parameters(cell, &a, &b, &c, &alpha, &beta, &gamma);
+
+ if ( (t[0] == 'o') || (t[0] == 't') || (t[0] == 'c') ) {
+ alpha = deg2rad(90.0);
+ beta = deg2rad(90.0);
+ gamma = deg2rad(90.0);
+ }
+
+ if ( (t[0] == 'm') || (t[0] == 'h') ) {
+ if ( t[1] == 'a' ) {
+ beta = deg2rad(90.0);
+ gamma = deg2rad(90.0);
+ }
+ if ( t[1] == 'b' ) {
+ alpha = deg2rad(90.0);
+ gamma = deg2rad(90.0);
+ }
+ if ( t[1] == 'c' ) {
+ alpha = deg2rad(90.0);
+ beta = deg2rad(90.0);
+ }
+ }
+
+ if ( t[0] == 'h' ) {
+ if ( t[1] == 'a' ) {
+ double av = (b+c)/2.0;
+ alpha = deg2rad(120.0);
+ b = av; c = av;
+ }
+ if ( t[1] == 'b' ) {
+ double av = (a+c)/2.0;
+ beta = deg2rad(120.0);
+ a = av; c = av;
+ }
+ if ( t[1] == 'c' ) {
+ double av = (a+b)/2.0;
+ gamma = deg2rad(120.0);
+ a = av; b = av;
+ }
+ }
+
+ if ( t[0] == 't' ) {
+ if ( t[1] == 'a' ) {
+ double av = (b+c)/2.0;
+ b = av; c = av;
+ }
+ if ( t[1] == 'b' ) {
+ double av = (a+c)/2.0;
+ a = av; c = av;
+ }
+ if ( t[1] == 'c' ) {
+ double av = (a+b)/2.0;
+ a = av; b = av;
+ }
+ }
+
+ if ( t[0] == 'r' ) {
+ double av = (alpha+beta+gamma)/3.0;
+ alpha = av; beta = av; gamma = av;
+ }
+
+ if ( (t[0] == 'c') && (t[0] == 'r') ) {
+ double av = (a+b+c)/3.0;
+ a = av; b = av; c = av;
+ }
+
+ cell_set_parameters(cell, a, b, c, alpha, beta, gamma);
+
+ switch ( t[0] ) {
+
+ case 'a':
+ cell_set_lattice_type(cell, L_TRICLINIC);
+ break;
+
+ case 'h':
+ cell_set_lattice_type(cell, L_HEXAGONAL);
+ break;
+
+ case 'o':
+ cell_set_lattice_type(cell, L_ORTHORHOMBIC);
+ break;
+
+ case 't':
+ cell_set_lattice_type(cell, L_TETRAGONAL);
+ break;
+
+ case 'c':
+ cell_set_lattice_type(cell, L_CUBIC);
+ break;
+
+ case 'r':
+ cell_set_lattice_type(cell, L_RHOMBOHEDRAL);
+ break;
+
+ case 'm':
+ cell_set_lattice_type(cell, L_MONOCLINIC);
+ break;
+
+ }
+ cell_set_unique_axis(cell, t[1]);
+
+ return cell;
+}
+
+
+static void savecell_changed(GtkComboBox *widget, gpointer data)
+{
+ struct save_cell_data *scd = data;
+ char *cell_str;
+ int active_n;
+
+ active_n = gtk_combo_box_get_active(widget);
+ if ( (active_n >= scd->n_ids) || (active_n < 0) ) {
+ ERROR("Combobox active index invalid\n");
+ return;
+ }
+
+ cell_free(scd->enforced_cell);
+ scd->enforced_cell = enforce_cell(scd->orig_cell,
+ scd->combo_ids[active_n]);
+
+ cell_str = cell_string(scd->enforced_cell);
+ if ( cell_str != NULL ) {
+ gtk_label_set_text(GTK_LABEL(scd->label), cell_str);
+ } else {
+ gtk_label_set_text(GTK_LABEL(scd->label), "error");
+ }
+ free(cell_str);
+}
+
+
+static void add_lto(struct save_cell_data *scd, const char *label,
+ const char *text)
+{
+ assert(scd->n_ids < 32);
+ scd->combo_ids[scd->n_ids++] = strdup(label);
+ gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(scd->combobox), text);
+}
+
+
+static void add_lattice_type_options(struct save_cell_data *scd, UnitCell *cell)
+{
+ LatticeType lt;
+ char ua;
+ char tmp1[256];
+ char tmp2[256];
+
+ lt = cell_get_lattice_type(cell);
+ ua = cell_get_unique_axis(cell);
+
+ switch ( lt ) {
+
+ case L_HEXAGONAL :
+ tmp1[0] = 'h'; tmp1[1] = ua; tmp1[2] = '\0';
+ snprintf(tmp2, 255, "Hexagonal, unique axis %c", ua);
+ add_lto(scd, tmp1, tmp2);
+ tmp1[0] = 'm'; tmp1[1] = ua; tmp1[2] = '\0';
+ snprintf(tmp2, 255, "Monoclinic, unique axis %c", ua);
+ add_lto(scd, tmp1, tmp2);
+ add_lto(scd, "a*", "Triclinic");
+ break;
+
+ case L_RHOMBOHEDRAL :
+ add_lto(scd, "r*", "Rhombohedral");
+ add_lto(scd, "a*", "Triclinic");
+ break;
+
+ /* Fall through */
+ case L_CUBIC :
+ add_lto(scd, "c*", "Cubic");
+ add_lto(scd, "r*", "Rhombohedral");
+ /* Fall through */
+
+ case L_TETRAGONAL :
+ if ( ua != '*' ) {
+ tmp1[0] = 't'; tmp1[1] = ua; tmp1[2] = '\0';
+ snprintf(tmp2, 255, "Tetragonal, unique axis %c", ua);
+ add_lto(scd, tmp1, tmp2);
+ } else {
+ add_lto(scd, "ta", "Tetragonal, unique axis a");
+ add_lto(scd, "tb", "Tetragonal, unique axis b");
+ add_lto(scd, "tc", "Tetragonal, unique axis c");
+ }
+ /* Fall through */
+
+ case L_ORTHORHOMBIC :
+ add_lto(scd, "o*", "Orthorhombic");
+ add_lto(scd, "ma", "Monoclinic, unique axis a");
+ add_lto(scd, "mb", "Monoclinic, unique axis b");
+ add_lto(scd, "mc", "Monoclinic, unique axis c");
+ add_lto(scd, "a*", "Triclinic");
+ break;
+
+ case L_MONOCLINIC :
+ tmp1[0] = 'm'; tmp1[1] = ua; tmp1[2] = '\0';
+ snprintf(tmp2, 255, "Monoclinic, unique axis %c", ua);
+ add_lto(scd, tmp1, tmp2);
+ /* Fall through */
+
+ case L_TRICLINIC :
+ add_lto(scd, "a*", "Triclinic");
+ break;
+
+ }
+}
+
+
static gint savecell_sig(GtkWidget *widget, CellWindow *w)
{
GtkWidget *d;
- UnitCell *cell;
gint r;
+ GtkWidget *vbox;
+ GtkWidget *hbox;
+ GtkWidget *label;
+ GtkWidget *cb;
+ struct save_cell_data scd;
+ int i;
- cell = get_cell(w);
- if ( cell == NULL ) return FALSE;
+ scd.orig_cell = get_cell(w);
+ scd.enforced_cell = NULL;
+ if ( scd.orig_cell == NULL ) return FALSE;
d = gtk_file_chooser_dialog_new("Save Unit Cell File",
GTK_WINDOW(w->window),
@@ -1201,19 +1557,40 @@ static gint savecell_sig(GtkWidget *widget, CellWindow *w)
NULL);
gtk_file_chooser_set_do_overwrite_confirmation(GTK_FILE_CHOOSER(d),
TRUE);
+
+ vbox = gtk_vbox_new(FALSE, 8);
+ hbox = gtk_hbox_new(FALSE, 8);
+ gtk_box_pack_start(GTK_BOX(vbox), GTK_WIDGET(hbox), FALSE, FALSE, 0);
+ label = gtk_label_new("Enforce lattice type:");
+ gtk_box_pack_start(GTK_BOX(hbox), GTK_WIDGET(label), FALSE, FALSE, 0);
+ cb = gtk_combo_box_text_new();
+ gtk_box_pack_start(GTK_BOX(hbox), GTK_WIDGET(cb), FALSE, FALSE, 0);
+ scd.label = gtk_label_new("");
+ gtk_box_pack_start(GTK_BOX(vbox), GTK_WIDGET(scd.label), TRUE, FALSE, 0);
+ gtk_file_chooser_set_extra_widget(GTK_FILE_CHOOSER(d), GTK_WIDGET(vbox));
+ gtk_widget_show_all(vbox);
+
+ scd.combobox = cb;
+ scd.n_ids = 0;
+ add_lattice_type_options(&scd, scd.orig_cell);
+
+ g_signal_connect(G_OBJECT(cb), "changed", G_CALLBACK(savecell_changed), &scd);
+ gtk_combo_box_set_active(GTK_COMBO_BOX(cb), 0);
+
r = gtk_dialog_run(GTK_DIALOG(d));
if ( r == GTK_RESPONSE_ACCEPT ) {
gchar *output_filename;
output_filename = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(d));
- if ( write_cell_to_file(cell, output_filename) ) {
+ if ( write_cell_to_file(scd.enforced_cell, output_filename) ) {
error_box(w, "Failed to save unit cell");
}
g_free(output_filename);
}
gtk_widget_destroy(d);
+ for ( i=0; i<scd.n_ids; i++ ) free(scd.combo_ids[i]);
return FALSE;
}
@@ -1258,6 +1635,7 @@ static void add_menu_bar(CellWindow *w, GtkWidget *vbox)
const char *ui = "<ui> <menubar name=\"cellwindow\">"
"<menu name=\"file\" action=\"FileAction\">"
" <menuitem name=\"savecell\" action=\"SaveCellAction\" />"
+ " <menuitem name=\"savedata\" action=\"SaveDataAction\" />"
" <menuitem name=\"quit\" action=\"QuitAction\" />"
"</menu>"
"<menu name=\"tools\" action=\"ToolsAction\" >"
@@ -1273,6 +1651,8 @@ static void add_menu_bar(CellWindow *w, GtkWidget *vbox)
{ "FileAction", NULL, "_File", NULL, NULL, NULL },
{ "SaveCellAction", GTK_STOCK_SAVE, "_Create unit cell file",
NULL, NULL, G_CALLBACK(savecell_sig) },
+ { "SaveDataAction", NULL, "_Save histogram data",
+ NULL, NULL, G_CALLBACK(savedata_sig) },
{ "QuitAction", GTK_STOCK_QUIT, "_Quit", NULL, NULL,
G_CALLBACK(quit_sig) },
diff --git a/src/cell_tool.c b/src/cell_tool.c
new file mode 100644
index 00000000..cd457f25
--- /dev/null
+++ b/src/cell_tool.c
@@ -0,0 +1,581 @@
+/*
+ * cell_tool.c
+ *
+ * Unit cell tool
+ *
+ * Copyright © 2018 Deutsches Elektronen-Synchrotron DESY,
+ * a research centre of the Helmholtz Association.
+ *
+ * Authors:
+ * 2012-2018 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 <assert.h>
+
+#include "cell.h"
+#include "cell-utils.h"
+#include "reflist-utils.h"
+#include "reflist.h"
+
+
+static void show_help(const char *s)
+{
+ printf("Syntax: %s [options]\n\n", s);
+ printf(
+"Unit cell manipulation tool.\n"
+"\n"
+" -h, --help Display this help message.\n"
+" -p, --pdb=<file> Get unit cell from <file> (PDB or CrystFEL format).\n"
+" -o <file> Output unit cell file.\n"
+"\n"
+" Actions:\n"
+" --find-ambi Find indexing ambiguities for the cell.\n"
+" --uncenter Calculate a primitive cell.\n"
+" --rings Calculate powder ring positions.\n"
+" --compare-cell <file> Compare unit cell with cell from <file>.\n"
+" --cell-choices Calculate all three cell choices for monoclinic C cell.\n"
+" --transform=<op> Transform unit cell.\n"
+"\n"
+" -y <pointgroup> Real point group of the structure.\n"
+" --tolerance=<tol> Set the tolerances for cell comparison.\n"
+" Default: 5,1.5 (axis percentage, angle deg).\n"
+" --highres=n Resolution limit (Angstroms) for --rings\n"
+" --csl Allow --compare to find coincidence site lattice relationships.\n"
+);
+}
+
+
+static int comparecells(UnitCell *cell, const char *comparecell,
+ double ltl, double atl, int csl)
+{
+ UnitCell *cell2;
+ RationalMatrix *m;
+
+ cell2 = load_cell_from_file(comparecell);
+ if ( cell2 == NULL ) {
+ ERROR("Failed to load unit cell from '%s'\n", comparecell);
+ return 1;
+ }
+ if ( validate_cell(cell2) > 1 ) {
+ ERROR("Comparison cell is invalid.\n");
+ return 1;
+ }
+ STATUS("------------------> The reference unit cell:\n");
+ cell_print(cell2);
+
+ STATUS("------------------> The comparison results:\n");
+ if ( !compare_reindexed_cell_parameters(cell, cell2, ltl, atl, csl, &m) ) {
+ STATUS("No relationship found between lattices.\n");
+ return 0;
+ } else {
+ UnitCell *trans;
+ STATUS("Relationship found. To become similar to the reference"
+ " cell, the input cell should be transformed by:\n");
+ rtnl_mtx_print(m);
+ STATUS("Transformed version of input unit cell:\n");
+ trans = cell_transform_rational(cell, m);
+ cell_print(trans);
+ cell_free(trans);
+ STATUS("NB transformed cell might not really be triclinic, "
+ "it's just that I don't (yet) know how to work out what "
+ "it is.\n");
+
+ }
+
+ rtnl_mtx_free(m);
+
+ return 0;
+}
+
+
+struct sortmerefl {
+ signed int h;
+ signed int k;
+ signed int l;
+ double resolution;
+ int multi;
+};
+
+
+static int cmpres(const void *av, const void *bv)
+{
+ const struct sortmerefl *a = av;
+ const struct sortmerefl *b = bv;
+ return a->resolution > b->resolution;
+}
+
+
+static int all_rings(UnitCell *cell, SymOpList *sym, double mres)
+{
+ double ax, ay, az;
+ double bx, by, bz;
+ double cx, cy, cz;
+ int hmax, kmax, lmax;
+ signed int h, k, l;
+ RefList *list;
+ int i, n;
+ RefListIterator *iter;
+ Reflection *refl;
+ struct sortmerefl *sortus;
+
+ cell_get_cartesian(cell, &ax, &ay, &az, &bx, &by, &bz, &cx, &cy, &cz);
+ hmax = mres * modulus(ax, ay, az);
+ kmax = mres * modulus(bx, by, bz);
+ lmax = mres * modulus(cx, cy, cz);
+ list = reflist_new();
+ for ( h=-hmax; h<=hmax; h++ ) {
+ for ( k=-kmax; k<=kmax; k++ ) {
+ for ( l=-lmax; l<=lmax; l++ ) {
+
+ signed int ha, ka, la;
+
+ if ( forbidden_reflection(cell, h, k, l) ) continue;
+ if ( 2.0*resolution(cell, h, k, l) > mres ) continue;
+
+ if ( sym != NULL ) {
+
+ Reflection *refl;
+
+ get_asymm(sym, h, k, l, &ha, &ka, &la);
+ refl = find_refl(list, ha, ka, la);
+ if ( refl == NULL ) {
+ refl = add_refl(list, ha, ka, la);
+ set_redundancy(refl, 1);
+ } else {
+ set_redundancy(refl, get_redundancy(refl)+1);
+ }
+
+ } else {
+ Reflection *refl;
+ refl = add_refl(list, h, k, l);
+ set_redundancy(refl, 1);
+ }
+
+ }
+ }
+ }
+
+ n = num_reflections(list);
+ sortus = malloc(n*sizeof(struct sortmerefl));
+
+ i = 0;
+ for ( refl = first_refl(list, &iter);
+ refl != NULL;
+ refl = next_refl(refl, iter) )
+ {
+ signed int h, k, l;
+
+ get_indices(refl, &h, &k, &l);
+
+ sortus[i].h = h;
+ sortus[i].k = k;
+ sortus[i].l = l;
+ sortus[i].resolution = 2.0*resolution(cell, h, k, l); /* one over d */
+ sortus[i].multi = get_redundancy(refl);
+ i++;
+
+ }
+
+ qsort(sortus, n, sizeof(struct sortmerefl), cmpres);
+
+ STATUS("\nAll powder rings up to %f Ångstrøms.\n", 1e+10/mres);
+ STATUS("Note that screw axis or glide plane absences are not "
+ "omitted from this list.\n");
+ STATUS("\n d (Å) 1/d (m^-1) h k l multiplicity\n");
+ STATUS("------------------------------------------------------\n");
+ for ( i=0; i<n; i++ ) {
+ printf("%10.3f %10.3e %4i %4i %4i m = %i\n",
+ 1e10/sortus[i].resolution, sortus[i].resolution,
+ sortus[i].h, sortus[i].k, sortus[i].l,
+ sortus[i].multi);
+ }
+
+ return 0;
+}
+
+
+static int find_ambi(UnitCell *cell, SymOpList *sym, double ltl, double atl)
+{
+ SymOpList *amb;
+ SymOpList *ops;
+ signed int i[9];
+ const int maxorder = 3;
+
+ ops = get_pointgroup("1");
+ if ( ops == NULL ) return 1;
+ set_symmetry_name(ops, "Observed");
+
+ if ( sym == NULL ) {
+ sym = get_pointgroup("1");
+ }
+
+ STATUS("Looking for ambiguities up to %ix each lattice length.\n", maxorder);
+ STATUS("This will take about 30 seconds. Please wait...\n");
+
+ for ( i[0]=-maxorder; i[0]<=+maxorder; i[0]++ ) {
+ for ( i[1]=-maxorder; i[1]<=+maxorder; i[1]++ ) {
+ for ( i[2]=-maxorder; i[2]<=+maxorder; i[2]++ ) {
+ for ( i[3]=-maxorder; i[3]<=+maxorder; i[3]++ ) {
+ for ( i[4]=-maxorder; i[4]<=+maxorder; i[4]++ ) {
+ for ( i[5]=-maxorder; i[5]<=+maxorder; i[5]++ ) {
+ for ( i[6]=-maxorder; i[6]<=+maxorder; i[6]++ ) {
+ for ( i[7]=-maxorder; i[7]<=+maxorder; i[7]++ ) {
+ for ( i[8]=-maxorder; i[8]<=+maxorder; i[8]++ ) {
+
+ UnitCell *nc;
+ IntegerMatrix *m;
+ int j, k;
+ int l = 0;
+
+ m = intmat_new(3, 3);
+ for ( j=0; j<3; j++ ) {
+ for ( k=0; k<3; k++ ) {
+ intmat_set(m, j, k, i[l++]);
+ }
+ }
+
+ if ( intmat_det(m) != +1 ) continue;
+
+ nc = cell_transform_intmat(cell, m);
+
+ if ( compare_cell_parameters(cell, nc, ltl, atl) ) {
+ if ( !intmat_is_identity(m) ) add_symop(ops, m);
+ STATUS("-----------------------------------------------"
+ "-------------------------------------------\n");
+ cell_print(nc);
+ intmat_print(m);
+ } else {
+ intmat_free(m);
+ }
+
+ cell_free(nc);
+
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+
+ STATUS("Observed symmetry operations:\n");
+ describe_symmetry(ops);
+
+ amb = get_ambiguities(ops, sym);
+ if ( amb == NULL ) {
+ STATUS("No ambiguities (or error calculating them)\n");
+ } else {
+ STATUS("Ambiguity operations:\n");
+ describe_symmetry(amb);
+ free_symoplist(amb);
+ }
+
+ free_symoplist(ops);
+
+ return 0;
+}
+
+
+static int uncenter(UnitCell *cell, const char *out_file)
+{
+ UnitCell *cnew;
+ IntegerMatrix *C;
+ RationalMatrix *Ci;
+
+ cnew = uncenter_cell(cell, &C, &Ci);
+
+ STATUS("------------------> The primitive unit cell:\n");
+ cell_print(cnew);
+
+ STATUS("------------------> The centering transformation:\n");
+ intmat_print(C);
+
+ STATUS("------------------> The un-centering transformation:\n");
+ rtnl_mtx_print(Ci);
+
+ if ( out_file != NULL ) {
+ FILE *fh = fopen(out_file, "w");
+ if ( fh == NULL ) {
+ ERROR("Failed to open '%s'\n", out_file);
+ return 1;
+ }
+ write_cell(cnew, fh);
+ fclose(fh);
+ }
+
+ return 0;
+}
+
+
+static int transform(UnitCell *cell, const char *trans_str,
+ const char *out_file)
+{
+ RationalMatrix *trans;
+ Rational det;
+ UnitCell *nc;
+
+ trans = parse_cell_transformation(trans_str);
+ if ( trans == NULL ) {
+ ERROR("Invalid cell transformation '%s'\n", trans_str);
+ return 1;
+ }
+
+ nc = cell_transform_rational(cell, trans);
+
+ STATUS("------------------> The transformation matrix:\n");
+ rtnl_mtx_print(trans);
+ det = rtnl_mtx_det(trans);
+ STATUS("Determinant = %s\n", rtnl_format(det));
+ if ( rtnl_cmp(det, rtnl_zero()) == 0 ) {
+ ERROR("Singular transformation matrix - cannot transform.\n");
+ return 1;
+ }
+
+ STATUS("------------------> The transformed unit cell:\n");
+ cell_print(nc);
+ STATUS("NB transformed cell might not really be triclinic, "
+ "it's just that I don't (yet) know how to work out what "
+ "it is.\n");
+
+ if ( out_file != NULL ) {
+ FILE *fh = fopen(out_file, "w");
+ if ( fh == NULL ) {
+ ERROR("Failed to open '%s'\n", out_file);
+ return 1;
+ }
+ write_cell(nc, fh);
+ fclose(fh);
+ }
+
+ return 0;
+}
+
+
+static int cell_choices(UnitCell *cell)
+{
+ if ( cell_get_lattice_type(cell) != L_MONOCLINIC ) {
+ ERROR("Cell must be monoclinic to use --cell-choices\n");
+ return 1;
+ }
+
+ if ( cell_get_unique_axis(cell) == 'b' ) {
+ transform(cell, "-a-c,b,a", NULL);
+ transform(cell, "c,b,-a-c", NULL);
+ } else {
+ ERROR("Sorry, --cell-choices only supports unique axis b.\n");
+ return 1;
+ }
+
+ return 0;
+}
+
+
+enum {
+ CT_NOTHING,
+ CT_FINDAMBI,
+ CT_UNCENTER,
+ CT_RINGS,
+ CT_COMPARE,
+ CT_CHOICES,
+ CT_TRANSFORM,
+};
+
+
+int main(int argc, char *argv[])
+{
+ int c;
+ char *cell_file = NULL;
+ UnitCell *cell;
+ char *toler = NULL;
+ float ltl = 0.05; /* fraction */
+ float atl = deg2rad(1.5); /* radians */
+ char *sym_str = NULL;
+ SymOpList *sym = NULL;
+ int mode = CT_NOTHING;
+ char *comparecell = NULL;
+ char *out_file = NULL;
+ float highres;
+ double rmax = 1/(2.0e-10);
+ char *trans_str = NULL;
+ int csl = 0;
+
+ /* Long options */
+ const struct option longopts[] = {
+ {"help", 0, NULL, 'h'},
+ {"pdb", 1, NULL, 'p'},
+ {"tolerance", 1, NULL, 2},
+ {"output", 1, NULL, 'o'},
+
+ /* Modes of operation */
+ {"find-ambi", 0, &mode, CT_FINDAMBI},
+ {"uncenter", 0, &mode, CT_UNCENTER},
+ {"uncentre", 0, &mode, CT_UNCENTER},
+ {"rings", 0, &mode, CT_RINGS},
+ {"compare-cell", 1, NULL, 3},
+ {"cell-choices", 0, &mode, CT_CHOICES},
+
+ {"transform", 1, NULL, 4},
+ {"highres", 1, NULL, 5},
+ {"csl", 0, &csl, 1},
+
+ {0, 0, NULL, 0}
+ };
+
+ /* Short options */
+ while ((c = getopt_long(argc, argv, "hp:y:o:",
+ longopts, NULL)) != -1) {
+
+ switch (c) {
+
+ case 'h' :
+ show_help(argv[0]);
+ return 0;
+
+ case 'p' :
+ cell_file = strdup(optarg);
+ break;
+
+ case 'o' :
+ out_file = strdup(optarg);
+ break;
+
+ case 'y' :
+ sym_str = strdup(optarg);
+ break;
+
+ case 2 :
+ toler = strdup(optarg);
+ break;
+
+ case 3 :
+ comparecell = strdup(optarg);
+ mode = CT_COMPARE;
+ break;
+
+ case 4 :
+ trans_str = strdup(optarg);
+ mode = CT_TRANSFORM;
+ break;
+
+ case 5 :
+ if ( sscanf(optarg, "%e", &highres) != 1 ) {
+ ERROR("Invalid value for --highres\n");
+ return 1;
+ }
+ rmax = 1.0 / (highres/1e10);
+ break;
+
+ case 0 :
+ break;
+
+ default :
+ return 1;
+
+ }
+
+ }
+
+ /* If there's a parameter left over, we assume it's the unit cell */
+ if ( (argc > optind) && (cell_file == NULL) ) {
+ cell_file = strdup(argv[optind++]);
+ }
+
+ /* If there's STILL a parameter left over, complain*/
+ if ( argc > optind ) {
+ ERROR("Excess command-line arguments:\n");
+ do {
+ ERROR("'%s'\n", argv[optind++]);
+ } while ( argc > optind );
+ return 1;
+ }
+
+ if ( cell_file == NULL ) {
+ ERROR("You must give a filename for the unit cell PDB file.\n");
+ return 1;
+ }
+ STATUS("Input unit cell: %s\n", cell_file);
+ cell = load_cell_from_file(cell_file);
+ if ( cell == NULL ) {
+ ERROR("Failed to load cell from '%s'\n", cell_file);
+ return 1;
+ }
+ free(cell_file);
+
+ if ( toler != NULL ) {
+ int i;
+ int ncomma = 0;
+ size_t l = strlen(toler);
+ for ( i=0; i<l; i++ ) if ( toler[i] == ',' ) ncomma++;
+ if ( ncomma != 1 ) {
+ ERROR("Invalid parameters for --tolerance. "
+ "Should be: --tolerance=lengthtol,angtol "
+ "(percent,degrees)\n");
+ return 1;
+ }
+ if ( sscanf(toler, "%f,%f", &ltl, &atl) != 2 ) {
+ ERROR("Invalid parameters for --tolerance\n");
+ return 1;
+ }
+ ltl /= 100.0; /* percent to fraction */
+ atl = deg2rad(atl);
+ free(toler);
+ }
+
+ STATUS("------------------> The input unit cell:\n");
+ cell_print(cell);
+
+ if ( validate_cell(cell) > 1 ) {
+ ERROR("Cell is invalid.\n");
+ return 1;
+ }
+
+ if ( sym_str != NULL ) {
+ sym = get_pointgroup(sym_str);
+ if ( sym == NULL ) return 1;
+ free(sym_str);
+ }
+
+ if ( mode == CT_NOTHING ) {
+ ERROR("Please specify mode of operation (see --help)\n");
+ return 1;
+ }
+
+ if ( mode == CT_FINDAMBI ) return find_ambi(cell, sym, ltl, atl);
+ if ( mode == CT_UNCENTER ) return uncenter(cell, out_file);
+ if ( mode == CT_RINGS ) return all_rings(cell, sym, rmax);
+ if ( mode == CT_COMPARE ) return comparecells(cell, comparecell,
+ ltl, atl, csl);
+ if ( mode == CT_TRANSFORM ) return transform(cell, trans_str, out_file);
+ if ( mode == CT_CHOICES ) return cell_choices(cell);
+
+ return 1;
+}
diff --git a/src/check_hkl.c b/src/check_hkl.c
index ba577d45..ca096ae8 100644
--- a/src/check_hkl.c
+++ b/src/check_hkl.c
@@ -41,7 +41,6 @@
#include <assert.h>
#include "utils.h"
-#include "statistics.h"
#include "symmetry.h"
#include "reflist.h"
#include "reflist-utils.h"
@@ -389,10 +388,10 @@ static void plot_shells(RefList *list, UnitCell *cell, const SymOpList *sym,
double rmin_fix, double rmax_fix, int nshells,
const char *shell_file)
{
- int *possible;
- unsigned int *measurements;
- unsigned int *measured;
- unsigned int *snr_measured;
+ unsigned long *possible;
+ unsigned long *measurements;
+ unsigned long *measured;
+ unsigned long *snr_measured;
double total_vol, vol_per_shell;
double *rmins;
double *rmaxs;
@@ -404,10 +403,11 @@ static void plot_shells(RefList *list, UnitCell *cell, const SymOpList *sym,
int i;
FILE *fh;
double snr_total = 0;
- int nrefl = 0;
- int nmeastot = 0;
- int nout = 0;
- int nsilly = 0;
+ unsigned long nrefl = 0;
+ unsigned long nmeastot = 0;
+ unsigned long nout = 0;
+ unsigned long possible_tot = 0;
+ unsigned long nsilly = 0;
Reflection *refl;
RefListIterator *iter;
RefList *counted;
@@ -416,10 +416,10 @@ static void plot_shells(RefList *list, UnitCell *cell, const SymOpList *sym,
double bx, by, bz;
double cx, cy, cz;
- possible = malloc(nshells*sizeof(int));
- measurements = malloc(nshells*sizeof(unsigned int));
- measured = malloc(nshells*sizeof(unsigned int));
- snr_measured = malloc(nshells*sizeof(unsigned int));
+ possible = malloc(nshells*sizeof(unsigned long));
+ measurements = malloc(nshells*sizeof(unsigned long));
+ measured = malloc(nshells*sizeof(unsigned long));
+ snr_measured = malloc(nshells*sizeof(unsigned long));
if ( (possible == NULL) || (measurements == NULL)
|| (measured == NULL) || (snr_measured == NULL) ) {
ERROR("Couldn't allocate memory.\n");
@@ -527,6 +527,7 @@ static void plot_shells(RefList *list, UnitCell *cell, const SymOpList *sym,
add_refl(counted, hs, ks, ls);
possible[bin]++;
+ possible_tot++;
}
}
@@ -618,17 +619,23 @@ static void plot_shells(RefList *list, UnitCell *cell, const SymOpList *sym,
var[bin] += pow(val-mean[bin], 2.0);
}
- STATUS("overall <snr> = %f\n", snr_total/(double)nrefl);
- STATUS("%i measurements in total.\n", nmeastot);
- STATUS("%i reflections in total.\n", nrefl);
+
+ STATUS("Overall values within specified resolution range:\n");
+ STATUS("%li measurements in total.\n", nmeastot);
+ STATUS("%li reflections in total.\n", nrefl);
+ STATUS("%li reflections possible.\n", possible_tot);
+ STATUS("Overall <snr> = %f\n", snr_total/(double)nrefl);
+ STATUS("Overall redundancy = %f measurements/unique reflection\n",
+ nmeastot/(double)nrefl);
+ STATUS("Overall completeness = %f %%\n", 100.0*nrefl/(double)possible_tot);
if ( nout ) {
- STATUS("WARNING; %i reflections outside resolution range.\n",
+ STATUS("WARNING; %li reflections outside resolution range.\n",
nout);
}
if ( nsilly ) {
- STATUS("WARNING; %i reflections had infinite or invalid values"
+ STATUS("WARNING; %li reflections had infinite or invalid values"
" of I/sigma(I).\n", nsilly);
}
@@ -639,7 +646,7 @@ static void plot_shells(RefList *list, UnitCell *cell, const SymOpList *sym,
double cen;
cen = rmins[i] + (rmaxs[i] - rmins[i])/2.0;
- fprintf(fh, "%10.3f %8i %8i %6.2f %10i %5.1f"
+ fprintf(fh, "%10.3f %8li %8li %6.2f %10li %5.1f"
" %5.2f %10.2f %10.2f %8.2f %10.3f %10.3f\n",
cen*1.0e-9,
measured[i],
diff --git a/src/cl-utils.c b/src/cl-utils.c
index fee2b53e..7c069ed1 100644
--- a/src/cl-utils.c
+++ b/src/cl-utils.c
@@ -3,11 +3,11 @@
*
* OpenCL utility functions
*
- * Copyright © 2012-2018 Deutsches Elektronen-Synchrotron DESY,
+ * Copyright © 2012-2019 Deutsches Elektronen-Synchrotron DESY,
* a research centre of the Helmholtz Association.
*
* Authors:
- * 2010-2018 Thomas White <taw@physics.org>
+ * 2010-2019 Thomas White <taw@physics.org>
*
* This file is part of CrystFEL.
*
@@ -43,6 +43,40 @@
#include "utils.h"
+/* Return 1 if a GPU device is present, 0 if not, 2 on error. */
+int have_gpu_device()
+{
+ cl_uint nplat;
+ cl_platform_id platforms[8];
+ cl_context_properties prop[3];
+ cl_int err;
+ int i;
+
+ err = clGetPlatformIDs(8, platforms, &nplat);
+ if ( err != CL_SUCCESS ) return 2;
+ if ( nplat == 0 ) return 0;
+
+ /* Find a GPU platform in the list */
+ for ( i=0; i<nplat; i++ ) {
+
+ prop[0] = CL_CONTEXT_PLATFORM;
+ prop[1] = (cl_context_properties)platforms[i];
+ prop[2] = 0;
+
+ clCreateContextFromType(prop, CL_DEVICE_TYPE_GPU,
+ NULL, NULL, &err);
+
+ if ( err != CL_SUCCESS ) {
+ if ( err != CL_DEVICE_NOT_FOUND ) return 2;
+ } else {
+ return 1;
+ }
+ }
+
+ return 0;
+}
+
+
const char *clError(cl_int err)
{
switch ( err ) {
diff --git a/src/cl-utils.h b/src/cl-utils.h
index fcaf1380..43f3f651 100644
--- a/src/cl-utils.h
+++ b/src/cl-utils.h
@@ -3,11 +3,11 @@
*
* OpenCL utility functions
*
- * Copyright © 2012-2018 Deutsches Elektronen-Synchrotron DESY,
+ * Copyright © 2012-2019 Deutsches Elektronen-Synchrotron DESY,
* a research centre of the Helmholtz Association.
*
* Authors:
- * 2010-2018 Thomas White <taw@physics.org>
+ * 2010-2019 Thomas White <taw@physics.org>
*
* This file is part of CrystFEL.
*
@@ -34,6 +34,13 @@
#endif
+#ifdef HAVE_CL_CL_H
+#include <CL/cl.h>
+#else
+#include <cl.h>
+#endif
+
+extern int have_gpu_device(void);
extern const char *clError(cl_int err);
extern cl_device_id get_cl_dev(cl_context ctx, int n);
extern cl_program load_program_from_string(const char *source_in, size_t len,
@@ -41,8 +48,8 @@ extern cl_program load_program_from_string(const char *source_in, size_t len,
cl_int *err, const char *extra_cflags,
const char *insert_stuff);
extern cl_program load_program(const char *filename, cl_context ctx,
- cl_device_id dev, cl_int *err,
- const char *extra_cflags, const char *insert_stuff);
+ cl_device_id dev, cl_int *err,
+ const char *extra_cflags, const char *insert_stuff);
#endif /* CLUTILS_H */
diff --git a/src/compare_hkl.c b/src/compare_hkl.c
index 5812cad2..479d7332 100644
--- a/src/compare_hkl.c
+++ b/src/compare_hkl.c
@@ -44,7 +44,6 @@
#include <gsl/gsl_fit.h>
#include "utils.h"
-#include "statistics.h"
#include "symmetry.h"
#include "reflist-utils.h"
#include "cell-utils.h"
diff --git a/src/diffraction-gpu.c b/src/diffraction-gpu.c
index 9cbfdf33..3feba50b 100644
--- a/src/diffraction-gpu.c
+++ b/src/diffraction-gpu.c
@@ -283,7 +283,7 @@ static int do_panels(struct gpu_context *gctx, struct image *image,
int get_diffraction_gpu(struct gpu_context *gctx, struct image *image,
int na, int nb, int nc, UnitCell *ucell,
- int no_fringes, int flat)
+ int no_fringes, int flat, int n_samples)
{
double ax, ay, az;
double bx, by, bz;
@@ -294,6 +294,7 @@ int get_diffraction_gpu(struct gpu_context *gctx, struct image *image,
int n_neg = 0;
int n_nan = 0;
int i;
+ double kmin, kmax, step, norm;
if ( gctx == NULL ) {
ERROR("GPU setup failed.\n");
@@ -338,23 +339,30 @@ int get_diffraction_gpu(struct gpu_context *gctx, struct image *image,
}
}
- double tot = 0.0;
- for ( i=0; i<image->nsamples; i++ ) {
+ spectrum_get_range(image->spectrum, &kmin, &kmax);
+ step = (kmax-kmin)/(n_samples+1);
- printf("%.3f eV, weight = %.5f\n",
- ph_lambda_to_eV(1.0/image->spectrum0[i].k),
- image->spectrum0[i].weight);
+ /* Determine normalisation factor such that weights add up to 1 after
+ * sampling (bins must have constant width) */
+ norm = 0.0;
+ for ( i=1; i<=n_samples; i++ ) {
+ double k = kmin + i*step;
+ norm += spectrum_get_density_at_k(image->spectrum, k);
+ }
+ for ( i=1; i<=n_samples; i++ ) {
- err = do_panels(gctx, image, image->spectrum0[i].k,
- image->spectrum0[i].weight,
- &n_inf, &n_neg, &n_nan);
+ double k = kmin + i*step;
+ double prob;
- if ( err ) return 1;
+ /* Probability = p.d.f. times step width */
+ prob = spectrum_get_density_at_k(image->spectrum, k)/norm;
- tot += image->spectrum0[i].weight;
+ STATUS("Wavelength: %e m, weight = %.5f\n", 1.0/k, prob);
+
+ err = do_panels(gctx, image, k, prob, &n_inf, &n_neg, &n_nan);
+ if ( err ) return 1;
}
- printf("total weight = %f\n", tot);
if ( n_neg + n_inf + n_nan ) {
ERROR("WARNING: The GPU calculation produced %i negative"
diff --git a/src/diffraction-gpu.h b/src/diffraction-gpu.h
index bef12a8e..a4d16583 100644
--- a/src/diffraction-gpu.h
+++ b/src/diffraction-gpu.h
@@ -42,7 +42,7 @@ struct gpu_context;
extern int get_diffraction_gpu(struct gpu_context *gctx, struct image *image,
int na, int nb, int nc, UnitCell *ucell,
- int no_fringes, int flat);
+ int no_fringes, int flat, int n_samples);
extern struct gpu_context *setup_gpu(int no_sfac,
const double *intensities,
const unsigned char *flags,
@@ -53,7 +53,7 @@ extern void cleanup_gpu(struct gpu_context *gctx);
static int get_diffraction_gpu(struct gpu_context *gctx, struct image *image,
int na, int nb, int nc, UnitCell *ucell,
- int no_fringes, int flat)
+ int no_fringes, int flat, int n_samples)
{
/* Do nothing */
ERROR("This copy of CrystFEL was not compiled with OpenCL support.\n");
diff --git a/src/diffraction.c b/src/diffraction.c
index 4dc21c59..6f52df11 100644
--- a/src/diffraction.c
+++ b/src/diffraction.c
@@ -443,317 +443,11 @@ 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;
- struct sample *sample2 = (struct sample *)b;
- if ( sample1->weight < sample2->weight ) {
- return 1;
- }
- return -1;
-}
-
-
-static struct sample *get_gaussian_spectrum(double eV_cen, double eV_step,
- double sigma, int spec_size,
- double eV_start)
-{
- struct sample *spectrum;
- int i;
- double eV;
-
- spectrum = malloc(spec_size * sizeof(struct sample));
- if ( spectrum == NULL ) return NULL;
-
- if (eV_start == 0) { /* eV starts at 3 sigma below the mean*/
- eV = eV_cen - (spec_size/2)*eV_step;
- } else {
- eV = eV_start;
- }
-
- for ( i=0; i<spec_size; i++ ) {
-
- spectrum[i].k = 1.0/ph_eV_to_lambda(eV);
- spectrum[i].weight = exp(-(pow(eV - eV_cen, 2.0)
- / (2.0*sigma*sigma)));
- eV += eV_step;
- }
-
- return spectrum;
-}
-
-
-static int add_sase_noise(struct sample *spectrum, int nsteps, gsl_rng *rng)
-{
- struct sample *noise;
- int i, j;
- double *gaussianNoise;
- int shiftLim = 5;
- double noise_mean = 0.0;
- double noise_sigma = 1.0;
-
- if ( shiftLim > nsteps ) shiftLim = nsteps;
-
- noise = malloc(nsteps * sizeof(struct sample));
- if ( noise == NULL ) return 1;
-
- gaussianNoise = malloc(3 * nsteps * sizeof(double));
- if ( gaussianNoise == NULL ) {
- free(noise);
- return 1;
- }
-
- /* Generate Gaussian noise of length of spectrum
- * (replicate on both ends for circshift below) */
- for ( i=0; i<nsteps; i++) {
-
- noise[i].weight = 0.0;
-
- /* Gaussian noise with mean = 0, std = 1 */
- gaussianNoise[i] = gaussian_noise(rng, noise_mean, noise_sigma);
- gaussianNoise[i+nsteps] = gaussianNoise[i];
- gaussianNoise[i+2*nsteps] = gaussianNoise[i];
- }
-
- /* Sum Gaussian noise by circshifting by +/- shiftLim */
- for ( i=nsteps; i<2*nsteps; i++ ) {
- for ( j=-shiftLim; j<=shiftLim; j++ ) {
- noise[i-nsteps].weight += gaussianNoise[i+j];
- }
- }
-
- /* Normalize the number of circshift sum */
- for ( i=0; i<nsteps; i++) {
- noise[i].weight = noise[i].weight/(2*shiftLim+1);
- }
-
- /* Noise amplitude should have a 2 x Gaussian distribution */
- for ( i=0; i<nsteps; i++ ) {
- noise[i].weight = 2.0 * spectrum[i].weight * noise[i].weight;
- }
-
- /* Add noise to spectrum */
- for ( i=0; i<nsteps; i++ ) {
-
- spectrum[i].weight += noise[i].weight;
-
- /* The final spectrum can not be negative */
- if ( spectrum[i].weight < 0.0 ) spectrum[i].weight = 0.0;
-
- }
-
- return 0;
-}
-
-
-struct sample *generate_tophat(struct image *image)
-{
- struct sample *spectrum;
- int i;
- double k, k_step;
-
- double halfwidth = image->bw * image->lambda / 2.0; /* m */
- double mink = 1.0/(image->lambda + halfwidth);
- double maxk = 1.0/(image->lambda - halfwidth);
-
- spectrum = malloc(image->nsamples * sizeof(struct sample));
- if ( spectrum == NULL ) return NULL;
-
- k = mink;
- k_step = (maxk-mink)/(image->nsamples-1);
- for ( i=0; i<image->nsamples; i++ ) {
- spectrum[i].k = k;
- spectrum[i].weight = 1.0/(double)image->nsamples;
- k += k_step;
- }
-
- image->spectrum_size = image->nsamples;
- /* No need to call normalise_sampled_spectrum() in this case */
-
- return spectrum;
-}
-
-
-struct sample *generate_spectrum_fromfile(struct image *image,
- char *spectrum_fn)
-{
- struct sample *spectrum;
- int i;
- double k, w;
- double w_sum = 0;
-
- spectrum = malloc(image->nsamples * sizeof(struct sample));
- if ( spectrum == NULL ) return NULL;
-
- FILE *f;
- f = fopen(spectrum_fn, "r");
-
- int nsamples = 0;
- for ( i=0; i<image->nsamples; i++ ) {
- if (fscanf(f, "%lf %lf", &k, &w) != EOF) {
- spectrum[i].k = ph_eV_to_k(k);
- spectrum[i].weight = w;
- w_sum += w;
- nsamples += 1;
- } else break;
- }
-
- for ( i=0; i<nsamples; i++ ) {
- spectrum[i].weight /= w_sum;
- }
-
- image->spectrum_size = nsamples;
-
- return spectrum;
-}
-
-
-struct sample *generate_SASE(struct image *image, gsl_rng *rng)
-{
- struct sample *spectrum;
- const int spec_size = 1024;
- double eV_cen; /* Central photon energy for this spectrum */
- const double jitter_sigma_eV = 8.0;
-
- /* Central wavelength jitters with Gaussian distribution */
- eV_cen = gaussian_noise(rng, ph_lambda_to_eV(image->lambda),
- jitter_sigma_eV);
-
- /* Convert FWHM to standard deviation. Note that bandwidth is taken
- * here to be "delta E over E" (E = photon energy), not the bandwidth in
- * terms of wavelength (as it is everywhere else), but the difference
- * should be very small */
- double sigma = (image->bw*eV_cen) / (2.0*sqrt(2.0*log(2.0)));
-
- /* The spectrum will be calculated to a resolution which spreads six
- * sigmas of the original (no SASE noise) Gaussian pulse over spec_size
- * points */
- double eV_step = 6.0*sigma/(spec_size-1);
-
- spectrum = get_gaussian_spectrum(eV_cen, eV_step, sigma, spec_size,0);
-
- /* Add SASE-type noise to Gaussian spectrum */
- add_sase_noise(spectrum, spec_size, rng);
-
- /* 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);
-
- normalise_sampled_spectrum(spectrum, spec_size, image->nsamples);
-
- image->spectrum_size = spec_size;
- return spectrum;
-}
-
-
-struct sample *generate_twocolour(struct image *image)
-{
- struct sample *spectrum;
- struct sample *spectrum1;
- struct sample *spectrum2;
- int i;
- double eV_cen1; /* Central photon energy for first colour */
- double eV_cen2; /* Central photon energy for second colour */
- double eV_cen; /* Central photon energy for this spectrum */
- const int spec_size = 1024;
-
- eV_cen = ph_lambda_to_eV(image->lambda);
-
- /* Convert FWHM to standard deviation. Note that bandwidth is taken
- * here to be "delta E over E" (E = photon energy), not the bandwidth in
- * terms of wavelength (as it is everywhere else), but the difference
- * should be very small */
- double halfwidth = eV_cen*image->bw/2.0; /* eV */
-
- eV_cen1 = eV_cen - halfwidth;
- eV_cen2 = eV_cen + halfwidth;
-
- /* Hard-code sigma to be 1/5 of bandwidth */
- double sigma = eV_cen*image->bw/5.0; /* eV */
-
- /* The spectrum will be calculated to a resolution which spreads six
- * sigmas of the original (no SASE noise) Gaussian pulse over spec_size
- * points */
- double eV_start = eV_cen1 - 3*sigma;
- double eV_end = eV_cen2 + 3*sigma;
- double eV_step = (eV_end - eV_start)/(spec_size-1);
-
- spectrum1 = get_gaussian_spectrum(eV_cen1, eV_step, sigma, spec_size,
- eV_start);
- spectrum2 = get_gaussian_spectrum(eV_cen2, eV_step, sigma, spec_size,
- eV_start);
-
- spectrum = malloc(spec_size * sizeof(struct sample));
- if ( spectrum == NULL ) return NULL;
-
- for ( i=0; i<spec_size; i++ ) {
- spectrum[i].weight = spectrum1[i].weight + spectrum2[i].weight;
- spectrum[i].k = spectrum1[i].k;
- if ( spectrum2[i].k != spectrum1[i].k ) {
- printf("%e %e\n", spectrum1[i].k, spectrum2[i].k);
- }
- }
-
- /* 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);
-
- normalise_sampled_spectrum(spectrum, spec_size, image->nsamples);
-
- image->spectrum_size = spec_size;
- return spectrum;
-}
-
-
void get_diffraction(struct image *image, int na, int nb, int nc,
const double *intensities, const double *phases,
const unsigned char *flags, UnitCell *cell,
GradientMethod m, const SymOpList *sym,
- int no_fringes, int flat)
+ int no_fringes, int flat, int n_samples)
{
double ax, ay, az;
double bx, by, bz;
@@ -762,6 +456,8 @@ void get_diffraction(struct image *image, int na, int nb, int nc,
double *lut_b;
double *lut_c;
int i;
+ double kmin, kmax, step;
+ double norm = 0.0;
cell_get_cartesian(cell, &ax, &ay, &az, &bx, &by, &bz, &cx, &cy, &cz);
@@ -769,17 +465,29 @@ void get_diffraction(struct image *image, int na, int nb, int nc,
lut_b = get_sinc_lut(nb, no_fringes, flat);
lut_c = get_sinc_lut(nc, no_fringes, flat);
- for ( i=0; i<image->nsamples; i++ ) {
+ spectrum_get_range(image->spectrum, &kmin, &kmax);
+ step = (kmax-kmin)/(n_samples+1);
+
+ /* Determine normalisation factor such that weights add up to 1 after
+ * sampling (bins must have constant width) */
+ for ( i=1; i<=n_samples; i++ ) {
+ double k = kmin + i*step;
+ norm += spectrum_get_density_at_k(image->spectrum, k);
+ }
+ for ( i=1; i<=n_samples; i++ ) {
+
+ double k = kmin + i*step;
+ double prob;
+
+ /* Probability = p.d.f. times step width */
+ prob = spectrum_get_density_at_k(image->spectrum, k)/norm;
- printf("%.1f eV, weight = %.5f\n",
- ph_lambda_to_eV(1.0/image->spectrum0[i].k),
- image->spectrum0[i].weight);
+ STATUS("Wavelength: %e m, weight = %.5f\n", 1.0/k, prob);
diffraction_at_k(image, intensities, phases,
- flags, cell, m, sym, image->spectrum0[i].k,
+ flags, cell, m, sym, k,
ax, ay, az, bx, by, bz, cx, cy, cz,
- lut_a, lut_b, lut_c, image->spectrum0[i].weight);
-
+ lut_a, lut_b, lut_c, prob);
}
diff --git a/src/diffraction.h b/src/diffraction.h
index 0f95f83c..16b0386f 100644
--- a/src/diffraction.h
+++ b/src/diffraction.h
@@ -52,7 +52,7 @@ extern void get_diffraction(struct image *image, int na, int nb, int nc,
const double *intensities, const double *phases,
const unsigned char *flags, UnitCell *cell,
GradientMethod m, const SymOpList *sym,
- int no_fringes, int flat);
+ int no_fringes, int flat, int n_samples);
extern struct sample *generate_tophat(struct image *image);
diff --git a/src/dw-hdfsee.c b/src/dw-hdfsee.c
index c4149cd4..31878594 100644
--- a/src/dw-hdfsee.c
+++ b/src/dw-hdfsee.c
@@ -53,6 +53,7 @@
#include "filters.h"
#include "events.h"
+static int displaywindow_update_menus(DisplayWindow *dw, const char *selectme);
static void displaywindow_error(DisplayWindow *dw, const char *message)
{
@@ -317,6 +318,7 @@ static void show_simple_ring(cairo_t *cr, DisplayWindow *dw,
cairo_stroke(cr);
}
+
static struct rigid_group *find_corresponding_rigid_group(DisplayWindow *dw,
struct panel *p) {
@@ -922,18 +924,35 @@ static void do_filters(DisplayWindow *dw)
}
+static void update_titlebar(DisplayWindow *dw)
+{
+ char title[1024];
+ char *bn = safe_basename(dw->image->filename);
+ if ( dw->ev != NULL ) {
+ snprintf(title, 1024, "%s - event: %s - hdfsee", bn, get_event_string(dw->ev));
+ } else {
+ snprintf(title, 1024, "%s - hdfsee", bn);
+ }
+ gtk_window_set_title(GTK_WINDOW(dw->window), title);
+ free(bn);
+}
+
+
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_dp = dw->image->dp;
int **old_bad = dw->image->bad;
+ if ( dw->image->det != NULL ) {
+ free_detector_geometry(dw->image->det);
+ }
+
+ dw->image->det = copy_geom(dw->original_geom);
fail = imagefile_read(dw->imagefile, dw->image,
dw->ev_list->events[new_event]);
if ( fail ) {
@@ -943,16 +962,13 @@ static gint displaywindow_newevent(DisplayWindow *dw, int new_event)
return 1;
}
- dw->curr_event = new_event;
+ dw->ev = dw->ev_list->events[new_event];
+ update_titlebar(dw);
do_filters(dw);
+ displaywindow_update_menus(dw, NULL);
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]);
@@ -1085,8 +1101,7 @@ static gint displaywindow_set_newevent(GtkWidget *widget, DisplayWindow *dw)
1, 2, 3, 4);
ed->entry = gtk_entry_new();
- snprintf(tmp, 1023, "%s",
- get_event_string(dw->ev_list->events[dw->curr_event]));
+ snprintf(tmp, 1023, "%s", get_event_string(dw->ev));
gtk_entry_set_text(GTK_ENTRY(ed->entry), tmp);
gtk_table_attach_defaults(GTK_TABLE(table), GTK_WIDGET(ed->entry),
2, 3, 3, 4);
@@ -1547,7 +1562,7 @@ static gint displaywindow_set_calibmode(GtkWidget *d, DisplayWindow *dw)
w = gtk_ui_manager_get_widget(dw->ui,
"/ui/displaywindow/tools/calibmode");
- if ( dw->simple ) {
+ if ( dw->geom_filename == NULL ) {
gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(w), 0);
return 0;
}
@@ -1751,9 +1766,8 @@ static gint displaywindow_save(GtkWidget *widget, DisplayWindow *dw)
const char *pk;
const char *rings;
- if ( dw->multi_event ) {
- struct event *ev = dw->ev_list->events[dw->curr_event];
- evs = get_event_string(ev);
+ if ( dw->ev != NULL ) {
+ evs = get_event_string(dw->ev);
substitute_slashes(evs);
} else {
evs = strdup("");
@@ -2260,12 +2274,14 @@ static void calibmode_prev(GtkWidget *widget, DisplayWindow *dw)
static void event_next(GtkWidget *widget, DisplayWindow *dw)
{
- int new_event;
+ int new_event, curr_event;
- if ( dw->curr_event == dw->ev_list->num_events-1 ) {
+ curr_event = find_event(dw->ev, dw->ev_list);
+
+ if ( curr_event >= dw->ev_list->num_events-1 ) {
new_event = 0;
} else {
- new_event = dw->curr_event+1;
+ new_event = curr_event+1;
}
displaywindow_newevent(dw, new_event);
}
@@ -2273,12 +2289,14 @@ static void event_next(GtkWidget *widget, DisplayWindow *dw)
static void event_prev(GtkWidget *widget, DisplayWindow *dw)
{
- int new_event;
+ int new_event, curr_event;
+
+ curr_event = find_event(dw->ev, dw->ev_list);
- if ( dw->curr_event == 0 ) {
+ if ( curr_event == 0 ) {
new_event = dw->ev_list->num_events-1;
} else {
- new_event = dw->curr_event-1;
+ new_event = curr_event-1;
}
displaywindow_newevent(dw, new_event);
}
@@ -2500,8 +2518,12 @@ static GtkWidget *displaywindow_addhdfgroup(struct hdfile *hdfile,
dw, rgp, selectme);
gtk_menu_item_set_submenu(GTK_MENU_ITEM(item), sub);
- } else if ( is_image[i] ) {
-
+ /* No changing image if we're using multiple events,
+ * or if we have a geometry file (already saying where to
+ * find the data. */
+ } else if ( is_image[i] && (dw->ev == NULL)
+ && (dw->original_geom == NULL) )
+ {
struct newhdf *nh;
item = gtk_radio_menu_item_new_with_label(*rgp,
@@ -2532,13 +2554,7 @@ static GtkWidget *displaywindow_addhdfgroup(struct hdfile *hdfile,
char *tmp;
item = gtk_menu_item_new_with_label(names[i]);
-
- if ( hdfile_is_scalar(hdfile, names[i], 0) ) {
- tmp = hdfile_get_string_value(hdfile, names[i],
- NULL);
- } else {
- tmp = NULL;
- }
+ tmp = hdfile_get_string_value(hdfile, names[i], dw->ev);
if ( tmp != NULL ) {
GtkWidget *ss;
@@ -2869,7 +2885,8 @@ static gint displaywindow_keypress(GtkWidget *widget, GdkEventKey *event,
case GDK_Right:
case GDK_KP_Right:
calibmode_right(dw);
- redraw_window(dw); break;
+ redraw_window(dw);
+ break;
case GDK_KP_Add:
calibmode_next(NULL, dw);
@@ -2939,11 +2956,12 @@ DisplayWindow *displaywindow_open(char *filename, char *geom_filename,
DisplayWindow *dw;
GtkWidget *vbox;
int check;
- GtkWidget *w;
- char title[1024];
+ FILE *fh;
+ unsigned long int seed;
dw = calloc(1, sizeof(DisplayWindow));
if ( dw == NULL ) return NULL;
+
dw->pixbufs = NULL;
dw->peaktype = 0;
dw->binning_dialog = NULL;
@@ -2953,7 +2971,6 @@ DisplayWindow *displaywindow_open(char *filename, char *geom_filename,
dw->boostint = 1;
dw->motion_callback = 0;
dw->numbers_window = NULL;
- dw->simple = 0;
dw->image = NULL;
dw->show_rings = show_rings;
dw->show_peaks = 0;
@@ -2973,11 +2990,10 @@ DisplayWindow *displaywindow_open(char *filename, char *geom_filename,
dw->calib_mode_curr_p = NULL;
dw->calib_mode_show_focus = 1;
dw->statusbar = NULL;
- dw->multi_event = 0;
dw->ev_list = NULL;
+ dw->ev = 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);
@@ -2994,7 +3010,8 @@ DisplayWindow *displaywindow_open(char *filename, char *geom_filename,
dw->rg_coll_name = NULL;
}
- dw->image->det = det_geom;
+ dw->original_geom = det_geom;
+ dw->image->det = copy_geom(det_geom);
dw->image->beam = beam;
dw->image->lambda = 0.0;
dw->image->filename = filename;
@@ -3009,10 +3026,18 @@ DisplayWindow *displaywindow_open(char *filename, char *geom_filename,
if ( dw->image->det != NULL && element != NULL ) {
impose_twod_geometry(dw, element);
- dw->multi_event = 0;
}
- if ( multi_event_geometry(dw->image->det) )
+ if ( element != NULL ) {
+ if ( imagefile_get_type(dw->imagefile) != IMAGEFILE_HDF5 ) {
+ ERROR("Can only use -e/--image with HDF5 files\n");
+ return NULL;
+ }
+ hdfile_set_image(imagefile_get_hdfile(dw->imagefile), element);
+ }
+
+ if ( (dw->image->det != NULL)
+ && (multi_event_geometry(dw->image->det)) )
{
struct hdfile *hdfile;
@@ -3022,8 +3047,6 @@ DisplayWindow *displaywindow_open(char *filename, char *geom_filename,
}
hdfile = imagefile_get_hdfile(dw->imagefile);
- dw->multi_event = 1;
-
dw->ev_list = fill_event_list(hdfile, dw->image->det);
if ( dw->ev_list == NULL ) {
@@ -3039,45 +3062,34 @@ DisplayWindow *displaywindow_open(char *filename, char *geom_filename,
free(dw->geom_filename);
free(dw);
return NULL;
- } else {
- dw->curr_event = 0;
}
- }
-
- if ( dw->image->det != NULL ) {
-
- if ( dw->multi_event ) {
- struct event *ev;
- if ( event != NULL ) {
- ev = get_event_from_event_string(event);
- dw->curr_event = find_event(ev, dw->ev_list);
- if ( dw->curr_event == dw->ev_list->num_events)
- {
- ERROR("Invalid event\n");
- return NULL;
- }
- } else {
- dw->curr_event = 0;
- ev = dw->ev_list->events[dw->curr_event];
+ if ( event != NULL ) {
+ int curr_event;
+ dw->ev = get_event_from_event_string(event);
+ curr_event = find_event(dw->ev, dw->ev_list);
+ if ( curr_event == dw->ev_list->num_events ) {
+ ERROR("Invalid event\n");
+ return NULL;
}
- check = imagefile_read(dw->imagefile, dw->image, ev);
} else {
- check = imagefile_read(dw->imagefile, dw->image, NULL);
+ dw->ev = dw->ev_list->events[0];
}
+ check = imagefile_read(dw->imagefile, dw->image, dw->ev);
+
} else {
- if ( element != NULL ) {
- if ( imagefile_get_type(dw->imagefile) != IMAGEFILE_HDF5 ) {
- ERROR("-e/--image requiest an HDF5 file\n");
- return NULL;
- }
- hdfile_set_image(imagefile_get_hdfile(dw->imagefile),
- element);
+
+ dw->ev = NULL;
+
+ if ( dw->image->det == NULL ) {
+ check = imagefile_read_simple(dw->imagefile, dw->image);
+ } else {
+ check = imagefile_read(dw->imagefile, dw->image, dw->ev);
}
- check = imagefile_read_simple(dw->imagefile, dw->image);
- dw->simple = 1;
+
}
+
if ( check ) {
ERROR("Couldn't load file\n");
imagefile_close(dw->imagefile);
@@ -3097,16 +3109,7 @@ DisplayWindow *displaywindow_open(char *filename, char *geom_filename,
}
dw->window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
- char *bn = safe_basename(filename);
- 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);
+ update_titlebar(dw);
g_signal_connect(G_OBJECT(dw->window), "destroy",
G_CALLBACK(displaywindow_closed), dw);
@@ -3135,14 +3138,7 @@ DisplayWindow *displaywindow_open(char *filename, char *geom_filename,
gtk_window_set_resizable(GTK_WINDOW(dw->window), TRUE);
gtk_widget_show_all(dw->window);
- w = gtk_ui_manager_get_widget(dw->ui,
- "/ui/displaywindow/view/images");
-
- if ( !dw->simple ) {
- gtk_widget_set_sensitive(GTK_WIDGET(w), FALSE);
- }
-
- if ( dw->simple || dw->multi_event == 0) {
+ if ( dw->ev_list == NULL ) {
set_events_menu_sensitivity(dw, FALSE);
}
@@ -3166,9 +3162,7 @@ DisplayWindow *displaywindow_open(char *filename, char *geom_filename,
g_signal_connect(G_OBJECT(dw->drawingarea), "key-press-event",
G_CALLBACK(displaywindow_keypress), dw);
- if ( dw->simple ) {
- displaywindow_update_menus(dw, element);
- }
+ displaywindow_update_menus(dw, element);
dw->not_ready_yet = 0;
diff --git a/src/dw-hdfsee.h b/src/dw-hdfsee.h
index 85fa288f..7e1ba88e 100644
--- a/src/dw-hdfsee.h
+++ b/src/dw-hdfsee.h
@@ -96,11 +96,10 @@ typedef struct {
GdkPixbuf **pixbufs;
gulong motion_callback;
cairo_surface_t *surf;
+ struct detector *original_geom;
int not_ready_yet;
- int simple;
-
struct imagefile *imagefile;
struct image *image;
int peaktype;
@@ -143,9 +142,8 @@ typedef struct {
int scale;
GdkPixbuf *col_scale;
- int multi_event;
struct event_list *ev_list;
- int curr_event;
+ struct event *ev;
gsl_rng rng;
diff --git a/src/geoptimiser.c b/src/geoptimiser.c
index f66ac709..83298b92 100644
--- a/src/geoptimiser.c
+++ b/src/geoptimiser.c
@@ -3,13 +3,13 @@
*
* Refine detector geometry
*
- * Copyright © 2014-2016 Deutsches Elektronen-Synchrotron DESY,
+ * Copyright © 2014-2019 Deutsches Elektronen-Synchrotron DESY,
* a research centre of the Helmholtz Association.
*
* Authors:
* 2014-2015 Oleksandr Yefanov
* 2014-2015 Valerio Mariani
- * 2014-2016 Thomas White <taw@physics.org>
+ * 2014-2019 Thomas White <taw@physics.org>
*
* This file is part of CrystFEL.
*
@@ -75,15 +75,12 @@ static void show_help(const char *s)
"\n"
" -h, --help Display this help message.\n"
"\n"
-" --version Print CrystFEL version number and\n"
-" exit.\n"
-" -i, --input=<filename> Specify stream file to be used for \n"
-" geometry optimization.\n"
-" -g. --geometry=<file> Get detector geometry from file.\n"
-" -o, --output=<filename> Output stream.\n"
+" --version Print CrystFEL version number and exit.\n"
+" -i, --input=<filename> Input stream\n"
+" -g. --geometry=<file> Input geometry file (if omitted: from stream).\n"
+" -o, --output=<filename> Output geometry file.\n"
" -q, --quadrants=<rg_coll> Rigid group collection for quadrants.\n"
-" -c, --connected=<rg_coll> Rigid group collection for connected\n"
-" ASICs.\n"
+" -c, --connected=<rg_coll> Rigid group collection for connected ASICs.\n"
" --no-error-maps Do not generate error map PNGs.\n"
" --stretch-map Generate stretch map PNG (panels distance).\n"
" -x, --min-num-peaks-per-pixel=<num> Minimum number of peaks per pixel.\n"
@@ -91,10 +88,8 @@ static void show_help(const char *s)
" -z, --max-num-peaks-per-pixel=<num> Maximum number of peaks per pixel.\n"
" Default is num_patterns / 10. \n"
" -p, --min-num-pixels-per-conn-group=<num> Minimum number of useful pixels per\n"
-" connected group.\n"
-" Default: 100.\n"
-" -l, --most-freq-clen Use only the most frequent camera\n"
-" length.\n"
+" connected group. Default: 100.\n"
+" -l, --most-freq-clen Use only the most frequent camera length\n"
" -s, --individual-dist-offset Use a distance offset for each panel.\n"
" Default: whole-detector offset.\n"
" --no-stretch Do not optimize distance offset.\n"
@@ -109,9 +104,7 @@ static void show_help(const char *s)
struct geoptimiser_params
{
- char *infile;
char *outfile;
- char *geometry_filename;
int min_num_peaks_per_pix;
int max_num_peaks_per_pix;
int min_num_pix_per_conn_group;
@@ -248,10 +241,9 @@ static double get_average_clen(struct image *image)
}
-static struct image *read_patterns_from_stream(const char *infile,
+static struct image *read_patterns_from_stream(Stream *st,
struct detector *det, int *n)
{
- Stream *st;
struct image *images;
int n_chunks = 0;
int max_images = 1024;
@@ -263,13 +255,6 @@ static struct image *read_patterns_from_stream(const char *infile,
return NULL;
}
- st = open_stream_for_read(infile);
- if ( st == NULL ) {
- ERROR("Failed to open input stream '%s'\n", infile);
- free(images);
- return NULL;
- }
-
do {
images[n_images].det = det;
@@ -313,11 +298,10 @@ static struct image *read_patterns_from_stream(const char *infile,
} while ( 1 );
- close_stream(st);
*n = n_images;
- STATUS("Found %i indexed patterns in file %s (from a total of %i).\n",
- n_images, infile, n_chunks);
+ STATUS("Found %i indexed patterns in stream (from a total of %i).\n",
+ n_images, n_chunks);
return images;
}
@@ -2288,7 +2272,7 @@ void recompute_avg_displ(struct rg_collection *connected,
}
-int optimize_geometry(struct geoptimiser_params *gparams,
+int optimize_geometry(struct geoptimiser_params *gparams, Stream *st,
struct detector *det,
struct rg_collection *quadrants,
struct rg_collection *connected)
@@ -2314,6 +2298,7 @@ int optimize_geometry(struct geoptimiser_params *gparams,
int n_images = 0;
UnitCell *avg_cell;
struct gpanel *gpanels;
+ const char *geometry_data = stream_geometry_file(st);
STATUS("Maximum distance between peaks: %0.1f pixels.\n",
gparams->max_peak_dist);
@@ -2346,10 +2331,9 @@ int optimize_geometry(struct geoptimiser_params *gparams,
"Please restart geometry optimization using the "
"optimized geometry from this run as input "
"geometry file.\n");
- geom_wr = write_detector_geometry_2(
- gparams->geometry_filename,
- gparams->outfile, det,
- gparams->command_line, 1);
+ geom_wr = write_detector_geometry_3(geometry_data,
+ gparams->outfile, det,
+ gparams->command_line, 1);
if ( geom_wr != 0 ) {
ERROR("Error in writing output geometry file.\n");
return 1;
@@ -2391,10 +2375,9 @@ int optimize_geometry(struct geoptimiser_params *gparams,
"Please restart geometry optimization using the "
"optimized geometry from this run as input "
"geometry file.\n");
- geom_wr = write_detector_geometry_2(
- gparams->geometry_filename,
- gparams->outfile, det,
- gparams->command_line, 1);
+ geom_wr = write_detector_geometry_3(geometry_data,
+ gparams->outfile, det,
+ gparams->command_line, 1);
if ( geom_wr != 0 ) {
ERROR("Error in writing output geometry file.\n");
return 1;
@@ -2413,7 +2396,7 @@ int optimize_geometry(struct geoptimiser_params *gparams,
}
}
- images = read_patterns_from_stream(gparams->infile, det, &n_images);
+ images = read_patterns_from_stream(st, det, &n_images);
if ( (n_images < 1) || (images == NULL) ) {
ERROR("Error reading stream file\n");
return 1;
@@ -2603,7 +2586,7 @@ int optimize_geometry(struct geoptimiser_params *gparams,
STATUS("Detector-wide error after correction: RMSD = %0.4f pixels.\n",
total_error);
- write_ret = write_detector_geometry_2(gparams->geometry_filename,
+ write_ret = write_detector_geometry_3(geometry_data,
gparams->outfile, det,
gparams->command_line, 1);
if ( write_ret != 0 ) {
@@ -2632,21 +2615,20 @@ int main(int argc, char *argv[])
int ret_val;
char buffer[256];
char command_line[1024];
-
+ char *infile = NULL;
char *quadrant_coll_name = NULL;
char *connected_coll_name = NULL;
-
+ Stream *st;
struct geoptimiser_params *gparams;
struct detector *det = NULL;
struct rg_collection *quadrants;
struct rg_collection *connected;
struct beam_params beam;
+ char *geometry_filename = NULL;
gparams = malloc(sizeof(struct geoptimiser_params));
gparams->outfile = NULL;
- gparams->infile = NULL;
- gparams->geometry_filename = NULL;
gparams->min_num_peaks_per_pix = 3;
gparams->max_num_peaks_per_pix = 0;
gparams->min_num_pix_per_conn_group = 100;
@@ -2714,18 +2696,11 @@ int main(int argc, char *argv[])
break;
case 'i' :
- gparams->infile = strdup(optarg);
+ infile = strdup(optarg);
break;
case 'g' :
- gparams->geometry_filename = strdup(optarg);
- det = get_detector_geometry(gparams->geometry_filename,
- &beam);
- if ( det == NULL ) {
- ERROR("Failed to read detector geometry from "
- "'%s'\n", optarg);
- return 1;
- }
+ geometry_filename = strdup(optarg);
break;
case 'q' :
@@ -2772,12 +2747,7 @@ int main(int argc, char *argv[])
}
}
- if ( gparams->geometry_filename == NULL ) {
- ERROR("You must provide a geometry to optimize.\n");
- return 1;
- }
-
- if ( gparams->infile == NULL ) {
+ if ( infile == NULL ) {
ERROR("You must provide an input stream file.\n");
return 1;
}
@@ -2799,7 +2769,43 @@ int main(int argc, char *argv[])
return 1;
}
- strcpy(command_line, "\0");
+ command_line[0] = '\0';
+ for ( i=0; i<argc; i++ ) {
+ if ( i > 0 ) strcat(command_line, " ");
+ strcpy(buffer, argv[i]);
+ strcat(command_line, buffer);
+ }
+
+#ifdef CAN_SAVE_TO_PNG
+#if !GLIB_CHECK_VERSION(2,36,0)
+ g_type_init();
+#endif
+#endif
+
+ st = open_stream_for_read(infile);
+ if ( st == NULL ) {
+ ERROR("Failed to open input stream '%s'\n", infile);
+ return 1;
+ }
+
+ if ( geometry_filename == NULL ) {
+ const char *stgeom = stream_geometry_file(st);
+ if ( stgeom != NULL ) {
+ det = get_detector_geometry_from_string(stgeom, &beam, NULL);
+ } else {
+ ERROR("No input geometry file given, and no geometry "
+ "found in stream.\n");
+ return 1;
+ }
+ } else {
+ det = get_detector_geometry(geometry_filename, &beam);
+ free(geometry_filename);
+ }
+
+ if ( det == NULL ) {
+ ERROR("Failed to read initial detector geometry.\n");
+ return 1;
+ }
quadrants = find_rigid_group_collection_by_name(det, quadrant_coll_name);
if ( quadrants == NULL ) {
@@ -2816,19 +2822,9 @@ int main(int argc, char *argv[])
return 1;
}
- for ( i=0; i<argc; i++ ) {
- if ( i > 0 ) strcat(command_line, " ");
- strcpy(buffer, argv[i]);
- strcat(command_line, buffer);
- }
+ ret_val = optimize_geometry(gparams, st, det, quadrants, connected);
-#ifdef CAN_SAVE_TO_PNG
-#if !GLIB_CHECK_VERSION(2,36,0)
- g_type_init();
-#endif
-#endif
-
- ret_val = optimize_geometry(gparams, det, quadrants, connected);
+ close_stream(st);
return ret_val;
}
diff --git a/src/get_hkl.c b/src/get_hkl.c
index efd06c94..2a17aab5 100644
--- a/src/get_hkl.c
+++ b/src/get_hkl.c
@@ -580,6 +580,10 @@ int main(int argc, char *argv[])
if ( reindex_str != NULL ) {
reindex = parse_symmetry_operations(reindex_str);
if ( reindex == NULL ) return 1;
+ if ( num_equivs(reindex, NULL) != 1 ) {
+ ERROR("Please provide only ONE reindexing operation\n");
+ return 1;
+ }
set_symmetry_name(reindex, "Reindex");
}
diff --git a/src/indexamajig.c b/src/indexamajig.c
index dbd77ad8..739b4969 100644
--- a/src/indexamajig.c
+++ b/src/indexamajig.c
@@ -187,6 +187,9 @@ static void show_help(const char *s)
" --xgandalf-grad-desc-iterations\n"
" Gradient descent iterations: 0 (few) to 5 (many)\n"
" Default: 4\n"
+" --xgandalf-fast-execution Shortcut to set\n"
+" --xgandalf-sampling-pitch=2\n"
+" --xgandalf-grad-desc-iterations=3\n"
" --xgandalf-tolerance Relative tolerance of the lattice vectors.\n"
" Default is 0.02\n"
" --xgandalf-no-deviation-from-provided-cell\n"
@@ -198,6 +201,10 @@ static void show_help(const char *s)
" --xgandalf-max-lattice-vector-length\n"
" Maximum possible lattice vector length in A.\n"
" Default: 250 A\n"
+" --xgandalf-max-peaks\n"
+" Maximum number of peaks used for indexing.\n"
+" All peaks are used for refinement.\n"
+" Default: 250\n"
"\n"
"\nIntegration options:\n\n"
" --integration=<meth> Integration method (rings,prof2d)-(cen,nocen)\n"
@@ -245,66 +252,6 @@ static void add_geom_beam_stuff_to_field_list(struct imagefile_field_list *copym
}
-static struct spectrum *read_spectrum_fromfile(char *fn)
-{
- FILE *f;
- struct spectrum *s;
- int i;
- double k, w;
- double w_sum = 0;
-
- f = fopen(fn, "r");
- if ( f == NULL ) {
- ERROR("Couldn't open '%s'\n", fn);
- return NULL;
- }
-
- s = malloc(sizeof(struct spectrum));
- if ( s == NULL ) return NULL;
-
- if ( fscanf(f, "%d", &s->n) == EOF ) {
- return NULL;
- }
-
- if ( s->n <= 0 ) {
- return NULL;
- }
-
- s->ks = malloc(s->n * sizeof(double));
- if ( s->ks == NULL ) {
- ERROR("Failed to allocate spectrum!\n");
- return NULL;
- }
-
- s->weights = malloc(s->n * sizeof(double));
- if ( s->weights == NULL ) {
- ERROR("Failed to allocate spectrum!\n");
- return NULL;
- }
-
- for ( i=0; i<s->n; i++ ) {
- if ( fscanf(f, "%lf %lf", &k, &w) != EOF ) {
- s->ks[i] = ph_eV_to_k(k);
- s->weights[i] = w;
- w_sum += w;
- } else {
- break;
- }
- }
-
- if ( i < s->n - 1 ) {
- ERROR("Failed to read %d lines from %s\n", s->n, fn);
- return NULL;
- }
-
- for ( i=0; i<s->n; i++ ) {
- s->weights[i] /= w_sum;
- }
-
- return s;
-}
-
-
int main(int argc, char *argv[])
{
int c;
@@ -412,6 +359,7 @@ int main(int argc, char *argv[])
iargs.xgandalf_opts.no_deviation_from_provided_cell = 0;
iargs.xgandalf_opts.minLatticeVectorLength_A = 30;
iargs.xgandalf_opts.maxLatticeVectorLength_A = 250;
+ iargs.xgandalf_opts.maxPeaksForIndexing = 250;
iargs.felix_opts.ttmin = -1.0;
iargs.felix_opts.ttmax = -1.0;
iargs.felix_opts.min_visits = 0;
@@ -541,6 +489,8 @@ int main(int argc, char *argv[])
{"wait-for-file", 1, NULL, 358},
{"min-squared-gradient",1,NULL, 359},
{"min-sq-gradient", 1, NULL, 359}, /* compat */
+ {"xgandalf-fast-execution", 0, NULL, 360},
+ {"xgandalf-max-peaks", 1, NULL, 361},
{0, 0, NULL, 0}
};
@@ -798,7 +748,8 @@ int main(int argc, char *argv[])
break;
case 338:
- if ( sscanf(optarg, "%i", &iargs.felix_opts.min_visits) != 1 )
+ if ( sscanf(optarg, "%i",
+ &iargs.felix_opts.min_visits) != 1 )
{
ERROR("Invalid value for --felix-min-visits\n");
return 1;
@@ -806,7 +757,8 @@ int main(int argc, char *argv[])
break;
case 339:
- if ( sscanf(optarg, "%lf", &iargs.felix_opts.min_completeness) != 1 )
+ if ( sscanf(optarg, "%lf",
+ &iargs.felix_opts.min_completeness) != 1 )
{
ERROR("Invalid value for --felix-min-completeness\n");
return 1;
@@ -814,7 +766,8 @@ int main(int argc, char *argv[])
break;
case 340:
- if ( sscanf(optarg, "%lf", &iargs.felix_opts.max_uniqueness) != 1 )
+ if ( sscanf(optarg, "%lf",
+ &iargs.felix_opts.max_uniqueness) != 1 )
{
ERROR("Invalid value for --felix-max-uniqueness\n");
return 1;
@@ -822,7 +775,8 @@ int main(int argc, char *argv[])
break;
case 341:
- if ( sscanf(optarg, "%i", &iargs.felix_opts.n_voxels) != 1 )
+ if ( sscanf(optarg, "%i",
+ &iargs.felix_opts.n_voxels) != 1 )
{
ERROR("Invalid value for --felix-num-voxels\n");
return 1;
@@ -830,7 +784,8 @@ int main(int argc, char *argv[])
break;
case 342:
- if ( sscanf(optarg, "%lf", &iargs.felix_opts.fraction_max_visits) != 1 )
+ if ( sscanf(optarg, "%lf",
+ &iargs.felix_opts.fraction_max_visits) != 1 )
{
ERROR("Invalid value for --felix-fraction-max-visits\n");
return 1;
@@ -838,7 +793,8 @@ int main(int argc, char *argv[])
break;
case 343:
- if ( sscanf(optarg, "%lf", &iargs.felix_opts.sigma) != 1 )
+ if ( sscanf(optarg, "%lf",
+ &iargs.felix_opts.sigma) != 1 )
{
ERROR("Invalid value for --felix-sigma\n");
return 1;
@@ -886,7 +842,8 @@ int main(int argc, char *argv[])
break;
case 351:
- if (sscanf(optarg, "%u", &iargs.xgandalf_opts.sampling_pitch) != 1)
+ if (sscanf(optarg, "%u",
+ &iargs.xgandalf_opts.sampling_pitch) != 1)
{
ERROR("Invalid value for --xgandalf-sampling-pitch\n");
return 1;
@@ -894,7 +851,8 @@ int main(int argc, char *argv[])
break;
case 352:
- if (sscanf(optarg, "%u", &iargs.xgandalf_opts.grad_desc_iterations) != 1)
+ if (sscanf(optarg, "%u",
+ &iargs.xgandalf_opts.grad_desc_iterations) != 1)
{
ERROR("Invalid value for --xgandalf-grad-desc-iterations\n");
return 1;
@@ -902,7 +860,8 @@ int main(int argc, char *argv[])
break;
case 353:
- if (sscanf(optarg, "%f", &iargs.xgandalf_opts.tolerance) != 1)
+ if (sscanf(optarg, "%f",
+ &iargs.xgandalf_opts.tolerance) != 1)
{
ERROR("Invalid value for --xgandalf-tolerance\n");
return 1;
@@ -910,12 +869,12 @@ int main(int argc, char *argv[])
break;
case 354:
- iargs.xgandalf_opts.no_deviation_from_provided_cell = 1;
+ iargs.xgandalf_opts.no_deviation_from_provided_cell = 1;
break;
case 355:
if (sscanf(optarg, "%f",
- &iargs.xgandalf_opts.minLatticeVectorLength_A) != 1)
+ &iargs.xgandalf_opts.minLatticeVectorLength_A) != 1)
{
ERROR("Invalid value for "
"--xgandalf-min-lattice-vector-length\n");
@@ -925,7 +884,7 @@ int main(int argc, char *argv[])
case 356:
if (sscanf(optarg, "%f",
- &iargs.xgandalf_opts.maxLatticeVectorLength_A) != 1)
+ &iargs.xgandalf_opts.maxLatticeVectorLength_A) != 1)
{
ERROR("Invalid value for "
"--xgandalf-max-lattice-vector-length\n");
@@ -949,6 +908,20 @@ int main(int argc, char *argv[])
iargs.min_sq_gradient = strtof(optarg, NULL);
break;
+ case 360:
+ iargs.xgandalf_opts.sampling_pitch = 2;
+ iargs.xgandalf_opts.grad_desc_iterations = 3;
+ break;
+
+ case 361:
+ if (sscanf(optarg, "%i",
+ &iargs.xgandalf_opts.maxPeaksForIndexing) != 1)
+ {
+ ERROR("Invalid value for --xgandalf-max-peaks\n");
+ return 1;
+ }
+ break;
+
case 0 :
break;
@@ -1140,13 +1113,12 @@ int main(int argc, char *argv[])
/* Load spectrum from file if given */
if ( spectrum_fn != NULL ) {
- iargs.spectrum = read_spectrum_fromfile(spectrum_fn);
+ iargs.spectrum = spectrum_load(spectrum_fn);
if ( iargs.spectrum == NULL ) {
ERROR("Couldn't read spectrum (from %s)\n", spectrum_fn);
return 1;
}
free(spectrum_fn);
- STATUS("Read %d lines from %s\n", iargs.spectrum->n, spectrum_fn);
} else {
iargs.spectrum = NULL;
}
diff --git a/src/merge.c b/src/merge.c
index 8017182b..93fe5a21 100644
--- a/src/merge.c
+++ b/src/merge.c
@@ -101,7 +101,7 @@ static int alloc_contribs(struct reflection_contributions *c)
/* Find reflection hkl in 'list', creating it if it's not there, under
* protection of 'lock' and returning a locked reflection */
static Reflection *get_locked_reflection(RefList *list, pthread_rwlock_t *lock,
- signed int h, signed int k, signed int l)
+ signed int h, signed int k, signed int l)
{
Reflection *f;
@@ -218,14 +218,14 @@ static void run_merge_job(void *vwargs, int cookie)
}
/* Reflections count less the more they have to be scaled up */
- w = get_partiality(refl);
+ w = get_partiality(refl) / correct_reflection_nopart(1.0, refl, G, B, res);
/* Running mean and variance calculation */
temp = w + sumweight;
if ( ln_merge ) {
- delta = log(correct_reflection(refl, G, B, res)) - mean;
+ delta = log(correct_reflection(get_intensity(refl), refl, G, B, res)) - mean;
} else {
- delta = correct_reflection(refl, G, B, res) - mean;
+ delta = correct_reflection(get_intensity(refl), refl, G, B, res) - mean;
}
R = delta * w / temp;
set_intensity(f, mean + R);
@@ -297,9 +297,12 @@ RefList *merge_intensities(Crystal **crystals, int n, int n_threads,
refl != NULL;
refl = next_refl(refl, iter) )
{
+ double var;
+ int red;
/* Correct for averaging log of intensities*/
- if (ln_merge){
+ if ( ln_merge ) {
+
double ln_I, ln_temp2;
ln_temp2 = get_temp2(refl);
@@ -307,13 +310,10 @@ RefList *merge_intensities(Crystal **crystals, int n, int n_threads,
ln_I = get_intensity(refl);
set_intensity(refl, exp(ln_I));
- }
- double var;
- int red;
+ }
red = get_redundancy(refl);
- //TODO is this still correct for log averaging?
var = get_temp2(refl) / get_temp1(refl);
set_esd_intensity(refl, sqrt(var)/sqrt(red));
@@ -343,21 +343,22 @@ RefList *merge_intensities(Crystal **crystals, int n, int n_threads,
}
-/* Correct intensity in pattern for scaling and Lorentz factors,
- * but not partiality nor polarisation */
-double correct_reflection_nopart(Reflection *refl, double osf, double Bfac,
- double res)
+/* Correct 'val' (probably an intensity from one pattern, maybe an e.s.d.)
+ * for scaling and Lorentz factors but not partiality nor polarisation */
+double correct_reflection_nopart(double val, Reflection *refl, double osf,
+ double Bfac, double res)
{
double corr = osf * exp(-Bfac*res*res);
- return (get_intensity(refl) / corr) / get_lorentz(refl);
+ return (val / corr) / get_lorentz(refl);
}
-/* Correct intensity in pattern for scaling, partiality and Lorentz factors,
- * but not polarisation */
-double correct_reflection(Reflection *refl, double osf, double Bfac, double res)
+/* Correct 'val' (probably an intensity from one pattern, maybe an e.s.d.)
+ * for scaling, partiality and Lorentz factors but not polarisation */
+double correct_reflection(double val, Reflection *refl, double osf, double Bfac,
+ double res)
{
- double Ipart = correct_reflection_nopart(refl, osf, Bfac, res);
+ double Ipart = correct_reflection_nopart(val, refl, osf, Bfac, res);
return Ipart / get_partiality(refl);
}
@@ -382,9 +383,9 @@ double residual(Crystal *cr, const RefList *full, int free,
signed int h, k, l;
Reflection *match;
double I_full;
- double int1, int2;
+ double int1, pobs, pcalc;
- if ( free && !get_flag(refl) ) continue;
+ if ( free != get_flag(refl) ) continue;
get_indices(refl, &h, &k, &l);
res = resolution(cell, h, k, l);
@@ -394,19 +395,27 @@ double residual(Crystal *cr, const RefList *full, int free,
if ( get_redundancy(match) < 2 ) continue;
- int1 = correct_reflection_nopart(refl, G, B, res);
- int2 = get_partiality(refl)*I_full;
- w = 1.0;
+ int1 = correct_reflection_nopart(get_intensity(refl), refl, G, B, res);
+ pobs = int1 / I_full;
+ if ( pobs > 1.0 ) pobs = 1.0;
+ if ( pobs < 0.0 ) pobs = 0.0;
+
+ pcalc = get_partiality(refl);
+
+ w = 1.0 / correct_reflection_nopart(1.0, refl, G, B, res);
+ if ( isnan(w) ) {
+ w = 0.0;
+ }
- num += fabs(int1 - int2) * w;
- den += fabs(int1 + int2) * w/2.0;
+ num += w*fabs(pobs-pcalc);
+ den += w;
n_used++;
}
if ( pn_used != NULL ) *pn_used = n_used;
- return num/(den*sqrt(2));
+ return num/den;
}
diff --git a/src/merge.h b/src/merge.h
index 2c2cfeda..4edef109 100644
--- a/src/merge.h
+++ b/src/merge.h
@@ -47,11 +47,11 @@ extern RefList *merge_intensities(Crystal **crystals, int n, int n_threads,
int min_meas, double push_res, int use_weak,
int ln_merge);
-extern double correct_reflection_nopart(Reflection *refl, double osf,
- double Bfac, double res);
+extern double correct_reflection_nopart(double val, Reflection *refl,
+ double osf, double Bfac, double res);
-extern double correct_reflection(Reflection *refl, double osf, double Bfac,
- double res);
+extern double correct_reflection(double val, Reflection *refl, double osf,
+ double Bfac, double res);
extern double residual(Crystal *cr, const RefList *full, int free,
int *pn_used, const char *filename);
diff --git a/src/partial_sim.c b/src/partial_sim.c
index 012b0a59..d0c2bd5a 100644
--- a/src/partial_sim.c
+++ b/src/partial_sim.c
@@ -90,7 +90,8 @@ static void calculate_partials(Crystal *cr,
pthread_rwlock_t *full_lock,
unsigned long int *n_ref, double *p_hist,
double *p_max, double max_q, double full_stddev,
- double noise_stddev, gsl_rng *rng)
+ double noise_stddev, gsl_rng *rng,
+ UnitCell *template_cell, RefList *template_reflist)
{
Reflection *refl;
RefListIterator *iter;
@@ -303,6 +304,7 @@ struct queue_args
Stream *stream;
gsl_rng **rngs;
+ Stream *template_stream;
};
@@ -312,6 +314,9 @@ struct worker_args
Crystal *crystal;
struct image image;
+ UnitCell *template_cell;
+ RefList *template_reflist;
+
/* Histogram for this image */
double p_hist[NBINS];
unsigned long int n_ref[NBINS];
@@ -334,6 +339,36 @@ static void *create_job(void *vqargs)
wargs->qargs = qargs;
wargs->image = *qargs->template_image;
+ if ( qargs->template_stream != NULL ) {
+
+ struct image im;
+ int r;
+
+ im.det = wargs->image.det;
+ r = read_chunk_2(qargs->template_stream, &im,
+ STREAM_READ_UNITCELL | STREAM_READ_REFLECTIONS);
+ if ( r ) {
+ ERROR("Failed to read template chunk!\n");
+ return NULL;
+ }
+ if ( im.n_crystals != 1 ) {
+ ERROR("Template stream must have exactly one crystal "
+ "per frame.\n");
+ return NULL;
+ }
+
+ wargs->template_cell = crystal_get_cell(im.crystals[0]);
+ wargs->template_reflist = crystal_get_reflections(im.crystals[0]);
+
+ crystal_free(im.crystals[0]);
+ free(im.filename);
+ free_event(im.event);
+
+ } else {
+ wargs->template_cell = NULL;
+ wargs->template_reflist = NULL;
+ }
+
qargs->n_started++;
wargs->n = qargs->n_started;
@@ -343,12 +378,10 @@ static void *create_job(void *vqargs)
static void run_job(void *vwargs, int cookie)
{
- struct quaternion orientation;
struct worker_args *wargs = vwargs;
struct queue_args *qargs = wargs->qargs;
int i;
Crystal *cr;
- RefList *reflections;
double osf;
cr = crystal_new();
@@ -367,9 +400,14 @@ static void run_job(void *vwargs, int cookie)
crystal_set_mosaicity(cr, 0.0);
crystal_set_profile_radius(cr, qargs->profile_radius);
- /* Set up a random orientation */
- orientation = random_quaternion(qargs->rngs[cookie]);
- crystal_set_cell(cr, cell_rotate(qargs->cell, orientation));
+ if ( wargs->template_cell == NULL ) {
+ /* Set up a random orientation */
+ struct quaternion orientation;
+ orientation = random_quaternion(qargs->rngs[cookie]);
+ crystal_set_cell(cr, cell_rotate(qargs->cell, orientation));
+ } else {
+ crystal_set_cell(cr, wargs->template_cell);
+ }
wargs->image.filename = malloc(256);
if ( wargs->image.filename == NULL ) {
@@ -383,9 +421,16 @@ static void run_job(void *vwargs, int cookie)
snprintf(wargs->image.filename, 255, "dummy.h5");
}
- reflections = predict_to_res(cr, largest_q(&wargs->image));
- crystal_set_reflections(cr, reflections);
- calculate_partialities(cr, PMODEL_XSPHERE);
+ if ( wargs->template_reflist == NULL ) {
+ RefList *reflections;
+ reflections = predict_to_res(cr, largest_q(&wargs->image));
+ crystal_set_reflections(cr, reflections);
+ calculate_partialities(cr, PMODEL_XSPHERE);
+ } else {
+ crystal_set_reflections(cr, wargs->template_reflist);
+ update_predictions(cr);
+ calculate_partialities(cr, PMODEL_XSPHERE);
+ }
for ( i=0; i<NBINS; i++ ) {
wargs->n_ref[i] = 0;
@@ -398,10 +443,11 @@ static void run_job(void *vwargs, int cookie)
&qargs->full_lock,
wargs->n_ref, wargs->p_hist, wargs->p_max,
qargs->max_q, qargs->full_stddev,
- qargs->noise_stddev, qargs->rngs[cookie]);
+ qargs->noise_stddev, qargs->rngs[cookie],
+ wargs->template_cell, wargs->template_reflist);
if ( qargs->image_prefix != NULL ) {
- draw_and_write_image(&wargs->image, reflections,
+ draw_and_write_image(&wargs->image, crystal_get_reflections(cr),
qargs->rngs[cookie], qargs->background);
}
@@ -489,6 +535,8 @@ int main(int argc, char *argv[])
gsl_rng *rng_for_seeds;
int config_random = 0;
char *image_prefix = NULL;
+ Stream *template_stream = NULL;
+ char *template = NULL;
/* Default simulation parameters */
double divergence = 0.001;
@@ -524,6 +572,7 @@ int main(int argc, char *argv[])
{"beam-bandwidth", 1, NULL, 9},
{"profile-radius", 1, NULL, 10},
{"photon-energy", 1, NULL, 11},
+ {"template-stream", 1, NULL, 12},
{"really-random", 0, &config_random, 1},
@@ -698,6 +747,10 @@ int main(int argc, char *argv[])
}
break;
+ case 12 :
+ template = strdup(optarg);
+ break;
+
case 0 :
break;
@@ -830,23 +883,32 @@ int main(int argc, char *argv[])
}
free(output_file);
+ if ( template != NULL ) {
+ template_stream = open_stream_for_read(template);
+ if ( template_stream == NULL ) {
+ ERROR("Couldn't open template stream '%s'\n", template);
+ return 1;
+ }
+ }
+
image.det = det;
image.beam = &beam;
image.lambda = ph_en_to_lambda(eV_to_J(photon_energy));
image.div = divergence;
image.bw = bandwidth;
+ image.spectrum = spectrum_generate_gaussian(image.lambda, image.bw);
image.filename = "dummy.h5";
image.copyme = NULL;
image.crystals = NULL;
image.n_crystals = 0;
image.indexed_by = INDEXING_SIMULATION;
- image.num_peaks = 0;
- image.num_saturated_peaks = 0;
- image.spectrum_size = 0;
- image.spectrum0 = NULL;
image.serial = 0;
image.event = NULL;
+ image.hit = 0;
+ image.n_indexing_tries = 1;
+ image.features = NULL;
+ image.peak_resolution = 0.0;
STATUS("Simulation parameters:\n");
STATUS(" Photon energy: %.2f eV (wavelength %.5f A)\n",
@@ -874,6 +936,10 @@ int main(int argc, char *argv[])
}
STATUS(" Max error in cell components: %.2f %%\n", cnoise);
STATUS("Scale factor standard deviation: %.2f\n", osf_stddev);
+ if ( template_stream != NULL ) {
+ STATUS("Crystal orientations and reflections to use from %s\n",
+ template);
+ }
if ( random_intensities ) {
full = reflist_new();
@@ -897,6 +963,7 @@ int main(int argc, char *argv[])
qargs.max_q = largest_q(&image);
qargs.image_prefix = image_prefix;
qargs.profile_radius = profile_radius;
+ qargs.template_stream = template_stream;
qargs.rngs = malloc(n_threads * sizeof(gsl_rng *));
if ( qargs.rngs == NULL ) {
diff --git a/src/partialator.c b/src/partialator.c
index 9de5276f..68c886c9 100644
--- a/src/partialator.c
+++ b/src/partialator.c
@@ -336,7 +336,8 @@ static void show_help(const char *s)
" -w <pg> Apparent point group for resolving ambiguities.\n"
" --operator=<op> Indexing ambiguity operator for resolving.\n"
" --force-bandwidth=<n> Set all bandwidths to <n> (fraction).\n"
-" --force-radius=<n> Set all profile radii to <n> nm^-1.\n");
+" --force-radius=<n> Set all profile radii to <n> nm^-1.\n"
+" --force-lambda=<n> Set all wavelengths to <n> A.\n");
}
@@ -569,8 +570,11 @@ static void skip_to_end(FILE *fh)
}
-static int set_initial_params(Crystal *cr, FILE *fh)
+static int set_initial_params(Crystal *cr, FILE *fh, double force_bandwidth,
+ double force_radius, double force_lambda)
{
+ struct image *image = crystal_get_image(cr);
+
if ( fh != NULL ) {
int err;
@@ -595,6 +599,17 @@ static int set_initial_params(Crystal *cr, FILE *fh)
}
+ if ( force_bandwidth > 0.0 ) {
+ image->bw = force_bandwidth;
+ }
+ if ( force_radius > 0.0 ) {
+ crystal_set_profile_radius(cr, force_radius);
+ }
+ if ( force_lambda > 0.0 ) {
+ image->lambda = force_lambda;
+ }
+ image->spectrum = spectrum_generate_gaussian(image->lambda, image->bw);
+
return 0;
}
@@ -648,7 +663,6 @@ static void write_to_pgraph(FILE *fh, RefList *list, RefList *full, Crystal *cr,
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;
@@ -660,7 +674,7 @@ static void write_to_pgraph(FILE *fh, RefList *list, RefList *full, Crystal *cr,
pcalc = get_partiality(refl);
/* Observed partiality */
- Ipart = correct_reflection_nopart(refl, G, B, res);
+ Ipart = correct_reflection_nopart(get_intensity(refl), refl, G, B, res);
pobs = Ipart / get_intensity(match);
fprintf(fh, "%5i %4i %4i %4i %e %e %8.3f %8.3f %s\n",
@@ -705,6 +719,7 @@ static void write_pgraph(RefList *full, Crystal **crystals, int n_crystals,
static void all_residuals(Crystal **crystals, int n_crystals, RefList *full,
+ int no_free,
double *presidual, double *pfree_residual,
double *plog_residual, double *pfree_log_residual,
int *pn_used)
@@ -758,8 +773,9 @@ static void all_residuals(Crystal **crystals, int n_crystals, RefList *full,
n_nan_log_free++;
}
- if ( isnan(r) || isnan(free_r)
- || isnan(log_r) || isnan(free_log_r) ) continue;
+ if ( isnan(r) || isnan(log_r) ) continue;
+
+ if ( !no_free && (isnan(free_r) || isnan(free_log_r)) ) continue;
*presidual += r;
*pfree_residual += free_r;
@@ -808,12 +824,12 @@ static void all_residuals(Crystal **crystals, int n_crystals, RefList *full,
static void show_all_residuals(Crystal **crystals, int n_crystals,
- RefList *full)
+ RefList *full, int no_free)
{
double dev, free_dev, log_dev, free_log_dev;
int n;
- all_residuals(crystals, n_crystals, full,
+ all_residuals(crystals, n_crystals, full, no_free,
&dev, &free_dev, &log_dev, &free_log_dev, &n);
STATUS("Residuals:"
" linear linear free log log free\n");
@@ -831,6 +847,8 @@ struct log_qargs
int n_crystals;
RefList *full;
int scaleflags;
+ PartialityModel pmodel;
+ int n_done;
};
@@ -839,6 +857,7 @@ struct log_args
Crystal *cr;
RefList *full;
int scaleflags;
+ PartialityModel pmodel;
int iter;
int cnum;
};
@@ -859,6 +878,7 @@ static void *get_log_task(void *vp)
task->iter = qargs->iter;
task->cnum = qargs->next;
task->scaleflags = qargs->scaleflags;
+ task->pmodel = qargs->pmodel;
qargs->next += 20;
return task;
@@ -870,21 +890,24 @@ static void write_logs(void *vp, int cookie)
struct log_args *args = vp;
write_specgraph(args->cr, args->full, args->iter, args->cnum);
write_gridscan(args->cr, args->full, args->iter, args->cnum,
- args->scaleflags);
+ args->scaleflags, args->pmodel);
write_test_logs(args->cr, args->full, args->iter, args->cnum);
}
-static void done_log(void *qargs, void *vp)
+static void done_log(void *vqargs, void *vp)
{
struct log_args *task = vp;
+ struct log_qargs *qargs = vqargs;
+ qargs->n_done++;
+ progress_bar(qargs->n_done, qargs->n_crystals/20, "Writing logs/grid scans");
free(task);
}
static void write_logs_parallel(Crystal **crystals, int n_crystals,
RefList *full, int iter, int n_threads,
- int scaleflags)
+ int scaleflags, PartialityModel pmodel)
{
struct log_qargs qargs;
@@ -892,13 +915,13 @@ static void write_logs_parallel(Crystal **crystals, int n_crystals,
qargs.next = 0;
qargs.full = full;
qargs.crystals = crystals;
+ qargs.n_done = 0;
qargs.n_crystals = n_crystals;
qargs.scaleflags = scaleflags;
+ qargs.pmodel = pmodel;
- STATUS("Writing logs...\n");
run_threads(n_threads, write_logs, get_log_task, done_log, &qargs,
n_crystals/20, 0, 0, 0);
- STATUS("Done.\n");
}
@@ -948,6 +971,7 @@ int main(int argc, char *argv[])
char *operator = NULL;
double force_bandwidth = -1.0;
double force_radius = -1.0;
+ double force_lambda = -1.0;
char *audit_info;
int scaleflags = 0;
double min_res = 0.0;
@@ -980,6 +1004,7 @@ int main(int argc, char *argv[])
{"force-bandwidth", 1, NULL, 10},
{"force-radius", 1, NULL, 11},
{"min-res", 1, NULL, 12},
+ {"force-lambda", 1, NULL, 13},
{"no-scale", 0, &no_scale, 1},
{"no-Bscale", 0, &no_Bscale, 1},
@@ -1149,6 +1174,17 @@ int main(int argc, char *argv[])
min_res = 1e10/min_res;
break;
+ case 13 :
+ errno = 0;
+ force_lambda = strtod(optarg, &rval);
+ if ( *rval != '\0' ) {
+ ERROR("Invalid value for --force-lambda.\n");
+ return 1;
+ }
+ force_lambda *= 1e-10;
+ break;
+
+
case 0 :
break;
@@ -1231,6 +1267,8 @@ int main(int argc, char *argv[])
pmodel = PMODEL_UNITY;
} else if ( strcmp(pmodel_str, "xsphere") == 0 ) {
pmodel = PMODEL_XSPHERE;
+ } else if ( strcmp(pmodel_str, "offset") == 0 ) {
+ pmodel = PMODEL_OFFSET;
} else if ( strcmp(pmodel_str, "random") == 0 ) {
pmodel = PMODEL_RANDOM;
} else {
@@ -1402,7 +1440,9 @@ int main(int argc, char *argv[])
crystal_set_user_flag(cr, PRFLAG_OK);
reflist_free(cr_refl);
- if ( set_initial_params(cr, sparams_fh) ) {
+ if ( set_initial_params(cr, sparams_fh, force_bandwidth,
+ force_radius, force_lambda) )
+ {
ERROR("Failed to set initial parameters\n");
return 1;
}
@@ -1433,12 +1473,6 @@ int main(int argc, char *argv[])
STATUS("Initial partiality calculation...\n");
for ( i=0; i<n_crystals; i++ ) {
Crystal *cr = crystals[i];
- if ( force_bandwidth > 0.0 ) {
- crystal_get_image(cr)->bw = force_bandwidth;
- }
- if ( force_radius > 0.0 ) {
- crystal_set_profile_radius(cr, force_radius);
- }
update_predictions(cr);
calculate_partialities(cr, pmodel);
}
@@ -1462,13 +1496,14 @@ int main(int argc, char *argv[])
}
/* Check rejection and write figures of merit */
- check_rejection(crystals, n_crystals, full, max_B, no_deltacchalf);
- show_all_residuals(crystals, n_crystals, full);
+ check_rejection(crystals, n_crystals, full, max_B, no_deltacchalf,
+ nthreads);
+ show_all_residuals(crystals, n_crystals, full, no_free);
if ( do_write_logs ) {
write_pgraph(full, crystals, n_crystals, 0, "");
write_logs_parallel(crystals, n_crystals, full, 0, nthreads,
- scaleflags);
+ scaleflags, pmodel);
}
/* Iterate */
@@ -1478,7 +1513,7 @@ int main(int argc, char *argv[])
if ( !no_pr ) {
refine_all(crystals, n_crystals, full, nthreads, pmodel,
- 0, i+1, no_logs, sym, amb, scaleflags);
+ i+1, no_logs, sym, amb, scaleflags);
}
/* Create new reference if needed */
@@ -1494,8 +1529,9 @@ int main(int argc, char *argv[])
push_res, 1, 0);
} /* else full still equals reference */
- check_rejection(crystals, n_crystals, full, max_B, no_deltacchalf);
- show_all_residuals(crystals, n_crystals, full);
+ check_rejection(crystals, n_crystals, full, max_B,
+ no_deltacchalf, nthreads);
+ show_all_residuals(crystals, n_crystals, full, no_free);
if ( do_write_logs ) {
write_pgraph(full, crystals, n_crystals, i+1, "");
@@ -1546,11 +1582,11 @@ int main(int argc, char *argv[])
}
/* Write final figures of merit (no rejection any more) */
- show_all_residuals(crystals, n_crystals, full);
+ show_all_residuals(crystals, n_crystals, full, no_free);
if ( do_write_logs ) {
write_pgraph(full, crystals, n_crystals, -1, "");
write_logs_parallel(crystals, n_crystals, full, -1, nthreads,
- scaleflags);
+ scaleflags, pmodel);
}
/* Output results */
diff --git a/src/pattern_sim.c b/src/pattern_sim.c
index 5b25100e..d13baff8 100644
--- a/src/pattern_sim.c
+++ b/src/pattern_sim.c
@@ -58,6 +58,14 @@
#include "stream.h"
+enum spectrum_type {
+ SPECTRUM_TOPHAT, /**< A top hat distribution */
+ SPECTRUM_GAUSSIAN, /**< A Gaussian distribution */
+ SPECTRUM_SASE, /**< A simulated SASE spectrum */
+ SPECTRUM_TWOCOLOUR, /**< A spectrum containing two peaks */
+ SPECTRUM_FROMFILE /**< An arbitrary spectrum read from a file */
+};
+
static void show_help(const char *s)
{
printf("Syntax: %s [options]\n\n", s);
@@ -95,7 +103,12 @@ static void show_help(const char *s)
" --template=<file> Take orientations from stream <file>.\n"
" --no-fringes Exclude the side maxima of Bragg peaks.\n"
" --flat Make Bragg peaks flat.\n"
-" --beam-bandwidth Beam bandwidth as a fraction. Default 1%%.\n"
+" --beam-bandwidth Beam bandwidth (standard deviation of wavelength as\n"
+" a fraction of wavelenth). Default 0.001 (1%%)\n"
+" --sase-spike-width SASE spike width (standard deviation in m^-1)\n"
+" Default 2e6 m^-1\n"
+" --twocol-separation Separation between peaks in two-colour mode in m^-1\n"
+" Default 8e6 m^-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"
@@ -370,7 +383,7 @@ int main(int argc, char *argv[])
char *spectrum_str = NULL;
char *spectrum_fn = NULL;
GradientMethod grad;
- SpectrumType spectrum_type;
+ enum spectrum_type spectrum_type;
int ndone = 0; /* Number of simulations done (images or not) */
int number = 1; /* Number used for filename of image */
int n_images = 1; /* Generate one image by default */
@@ -394,6 +407,8 @@ int main(int argc, char *argv[])
double beam_radius = 1e-6; /* metres */
double bandwidth = 0.01;
double photon_energy = 9000.0;
+ double sase_spike_width = 2e6; /* m^-1 */
+ double twocol_sep = 8e6; /* m^-1 */
struct beam_params beam;
int i;
@@ -431,6 +446,8 @@ int main(int argc, char *argv[])
{"nphotons", 1, NULL, 10},
{"beam-radius", 1, NULL, 11},
{"spectrum-file", 1, NULL, 12},
+ {"sase-spike-width", 1, NULL, 13},
+ {"twocol-separation", 1, NULL, 14},
{0, 0, NULL, 0}
};
@@ -596,6 +613,30 @@ int main(int argc, char *argv[])
spectrum_fn = strdup(optarg);
break;
+ case 13 :
+ sase_spike_width = strtod(optarg, &rval);
+ if ( *rval != '\0' ) {
+ ERROR("Invalid SASE spike width.\n");
+ return 1;
+ }
+ if ( beam_radius < 0.0 ) {
+ ERROR("SASE spike width must be positive.\n");
+ return 1;
+ }
+ break;
+
+ case 14 :
+ twocol_sep = strtod(optarg, &rval);
+ if ( *rval != '\0' ) {
+ ERROR("Invalid two colour separation.\n");
+ return 1;
+ }
+ if ( beam_radius < 0.0 ) {
+ ERROR("Two-colour separation must be positive.\n");
+ return 1;
+ }
+ break;
+
case 0 :
break;
@@ -693,6 +734,8 @@ int main(int argc, char *argv[])
spectrum_type = SPECTRUM_TOPHAT;
} else if ( strcasecmp(spectrum_str, "tophat") == 0) {
spectrum_type = SPECTRUM_TOPHAT;
+ } else if ( strcasecmp(spectrum_str, "gaussian") == 0) {
+ spectrum_type = SPECTRUM_GAUSSIAN;
} else if ( strcasecmp(spectrum_str, "sase") == 0) {
spectrum_type = SPECTRUM_SASE;
} else if ( strcasecmp(spectrum_str, "twocolour") == 0 ||
@@ -782,7 +825,6 @@ int main(int argc, char *argv[])
image.lambda = ph_en_to_lambda(eV_to_J(photon_energy));
image.bw = bandwidth;
- image.nsamples = nsamples;
/* Initialise stuff */
image.filename = NULL;
@@ -819,7 +861,7 @@ int main(int argc, char *argv[])
powder.det = image.det;
powder.beam = NULL;
powder.lambda = 0.0;
- powder.spectrum0 = NULL;
+ powder.spectrum = NULL;
powder.dp = malloc(image.det->n_panels*sizeof(float *));
if ( powder.dp == NULL ) {
ERROR("Failed to allocate powder data\n");
@@ -853,6 +895,11 @@ int main(int argc, char *argv[])
"width %.5f %%\n", image.bw*100.0);
break;
+ case SPECTRUM_GAUSSIAN:
+ STATUS(" X-ray spectrum: Gaussian, "
+ "bandwidth %.5f %%\n", image.bw*100.0);
+ break;
+
case SPECTRUM_SASE:
STATUS(" X-ray spectrum: SASE, "
"bandwidth %.5f %%\n", image.bw*100.0);
@@ -978,20 +1025,31 @@ int main(int argc, char *argv[])
switch ( spectrum_type ) {
case SPECTRUM_TOPHAT :
- image.spectrum0 = generate_tophat(&image);
+ image.spectrum = spectrum_generate_tophat(image.lambda,
+ image.bw);
break;
+ case SPECTRUM_GAUSSIAN :
+ image.spectrum = spectrum_generate_gaussian(image.lambda,
+ image.bw);
+ break;
+
+
case SPECTRUM_SASE :
- image.spectrum0 = generate_SASE(&image, rng);
+ image.spectrum = spectrum_generate_sase(image.lambda,
+ image.bw,
+ sase_spike_width,
+ rng);
break;
case SPECTRUM_TWOCOLOUR :
- image.spectrum0 = generate_twocolour(&image);
+ image.spectrum = spectrum_generate_twocolour(image.lambda,
+ image.bw,
+ twocol_sep);
break;
case SPECTRUM_FROMFILE :
- image.spectrum0 = generate_spectrum_fromfile(&image,
- spectrum_fn);
+ image.spectrum = spectrum_load(spectrum_fn);
break;
}
@@ -1009,11 +1067,13 @@ int main(int argc, char *argv[])
gpu_dev);
}
err = get_diffraction_gpu(gctx, &image, na, nb, nc,
- cell, no_fringes, flat);
+ cell, no_fringes, flat,
+ nsamples);
} else {
get_diffraction(&image, na, nb, nc, intensities, phases,
- flags, cell, grad, sym, no_fringes, flat);
+ flags, cell, grad, sym, no_fringes, flat,
+ nsamples);
}
if ( err ) {
ERROR("Diffraction calculation failed.\n");
diff --git a/src/post-refinement.c b/src/post-refinement.c
index 4ab25b54..dde81716 100644
--- a/src/post-refinement.c
+++ b/src/post-refinement.c
@@ -33,8 +33,6 @@
#include <stdlib.h>
#include <assert.h>
-#include <gsl/gsl_multimin.h>
-#include <gsl/gsl_fit.h>
#include "image.h"
#include "post-refinement.h"
@@ -47,12 +45,28 @@
#include "scaling.h"
#include "merge.h"
+struct rf_alteration
+{
+ double rot_x;
+ double rot_y;
+ double delta_R;
+ double delta_wave;
+};
+
-struct prdata
+struct rf_priv
{
- int refined;
+ const Crystal *cr; /**< Original crystal (before any refinement) */
+ const RefList *full;
+ int serial;
+ int scaleflags;
+ PartialityModel pmodel;
+
+ Crystal *cr_tgt; /**< Crystal to use for testing modifications */
+ struct image image_tgt; /**< Image structure to go with cr_tgt */
};
+
const char *str_prflag(enum prflag flag)
{
switch ( flag ) {
@@ -84,16 +98,17 @@ const char *str_prflag(enum prflag flag)
}
-static void rotate_cell_xy(UnitCell *cell, double ang1, double ang2)
+static void rotate_cell_xy(const UnitCell *source, UnitCell *tgt,
+ double ang1, double ang2)
{
double asx, asy, asz;
double bsx, bsy, bsz;
double csx, csy, csz;
double xnew, ynew, znew;
- cell_get_reciprocal(cell, &asx, &asy, &asz,
- &bsx, &bsy, &bsz,
- &csx, &csy, &csz);
+ cell_get_reciprocal(source, &asx, &asy, &asz,
+ &bsx, &bsy, &bsz,
+ &csx, &csy, &csz);
/* "a" around x */
xnew = asx;
@@ -131,219 +146,59 @@ static void rotate_cell_xy(UnitCell *cell, double ang1, double ang2)
znew = -csx*sin(ang2) + csz*cos(ang2);
csx = xnew; csy = ynew; csz = znew;
- cell_set_reciprocal(cell, asx, asy, asz, bsx, bsy, bsz, csx, csy, csz);
-}
-
-
-static const char *get_label(enum gparam p)
-{
- switch ( p ) {
- case GPARAM_ANG1 : return "First angle (radians)";
- case GPARAM_ANG2 : return "Second angle (radians)";
- case GPARAM_R : return "Profile radius (m^-1)";
- case GPARAM_WAVELENGTH : return "Wavelength (Angstroms)";
- default : return "unknown";
- }
-}
-
-
-/* We set all the step sizes to 1, then scale them.
- * This way, the size of the simplex stays meaningful and we possibly also
- * avoid some roundoff errors */
-static double get_scale(enum gparam p)
-{
- switch ( p ) {
-
- case GPARAM_ANG1 : return deg2rad(0.05);
- case GPARAM_ANG2 : return deg2rad(0.05);
- case GPARAM_R : return 0.0005e9;
- case GPARAM_WAVELENGTH : return 0.001e-10;
-
- default : return 0.0;
-
- }
+ cell_set_reciprocal(tgt, asx, asy, asz, bsx, bsy, bsz, csx, csy, csz);
}
-struct rf_priv {
- const Crystal *cr;
- const RefList *full;
- enum gparam rv[32];
- int verbose;
- int serial;
- gsl_vector *initial;
- int scaleflags;
-
- /* For freeing later */
- gsl_vector *vals;
- gsl_vector *step;
-
- /* So that it stays around until the end of minimisation */
- gsl_multimin_function f;
-};
-
-
-static double get_actual_val(const gsl_vector *v, const gsl_vector *initial,
- enum gparam *rv, int i)
+static void apply_parameters(const Crystal *cr_orig, Crystal *cr_tgt,
+ struct rf_alteration alter)
{
- return gsl_vector_get(v, i) * get_scale(rv[i])
- + gsl_vector_get(initial, i);
+ double R, lambda;
+ struct gaussian g;
+ struct image *image;
+
+ image = crystal_get_image(cr_tgt);
+ R = crystal_get_profile_radius(cr_orig);
+ lambda = crystal_get_image_const(cr_orig)->lambda;
+
+ rotate_cell_xy(crystal_get_cell_const(cr_orig), crystal_get_cell(cr_tgt),
+ alter.rot_x, alter.rot_y);
+ crystal_set_profile_radius(cr_tgt, R+alter.delta_R);
+ image->lambda = lambda+alter.delta_wave;
+
+ g.kcen = 1.0/image->lambda;
+ g.sigma = image->bw/image->lambda;
+ g.height = 1;
+ spectrum_set_gaussians(image->spectrum, &g, 1);
}
-static void apply_parameters(const gsl_vector *v, const gsl_vector *initial,
- enum gparam *rv, Crystal *cr)
+static double calc_residual(struct rf_priv *pv, struct rf_alteration alter,
+ int free)
{
- int i;
- double ang1, ang2, R, lambda;
-
- /* Default parameters if not used in refinement */
- ang1 = 0.0;
- ang2 = 0.0;
- R = crystal_get_profile_radius(cr);
- lambda = crystal_get_image(cr)->lambda;
-
- for ( i=0; i<v->size; i++ ) {
+ apply_parameters(pv->cr, pv->cr_tgt, alter);
- double val;
-
- val = get_actual_val(v, initial, rv, i);
-
- switch ( rv[i] ) {
-
- case GPARAM_ANG1 :
- ang1 = val;
- break;
-
- case GPARAM_ANG2 :
- ang2 = val;
- break;
-
- case GPARAM_R :
- R = val;
- break;
-
- case GPARAM_WAVELENGTH :
- lambda = val;
- break;
-
- default :
- ERROR("Don't understand parameter %i\n", rv[i]);
- break;
-
- }
- }
-
- rotate_cell_xy(crystal_get_cell(cr), ang1, ang2);
- crystal_set_profile_radius(cr, R);
- crystal_get_image(cr)->lambda = lambda;
-}
-
-
-static double residual_f(const gsl_vector *v, void *pp)
-{
- struct rf_priv *pv = pp;
- RefList *list;
- struct image im;
- Crystal *cr;
- double res;
- int i;
-
- for ( i=0; i<v->size; i++ ) {
- if ( gsl_vector_get(v, i) > 100.0 ) return GSL_NAN;
- }
-
- cr = crystal_copy(pv->cr);
- im = *crystal_get_image(cr);
- crystal_set_image(cr, &im);
- crystal_set_cell(cr, cell_new_from_cell(crystal_get_cell(cr)));
-
- apply_parameters(v, pv->initial, pv->rv, cr);
-
- if ( fabs(crystal_get_profile_radius(cr)) > 5e9 ) {
- cell_free(crystal_get_cell(cr));
- crystal_free(cr);
- if ( pv->verbose ) STATUS("radius > 5e9\n");
- return GSL_NAN;
+ if ( fabs(crystal_get_profile_radius(pv->cr_tgt)) > 5e9 ) {
+ STATUS("radius > 5e9\n");
+ return NAN;
}
/* Can happen with grid scans and certain --force-radius values */
- if ( fabs(crystal_get_profile_radius(cr)) < 0.0000001e9 ) {
- cell_free(crystal_get_cell(cr));
- crystal_free(cr);
- if ( pv->verbose ) STATUS("radius very small\n");
- return GSL_NAN;
+ if ( fabs(crystal_get_profile_radius(pv->cr_tgt)) < 0.0000001e9 ) {
+ //STATUS("radius very small\n");
+ return NAN;
}
- if ( im.lambda <= 0.0 ) {
- cell_free(crystal_get_cell(cr));
- crystal_free(cr);
- if ( pv->verbose ) STATUS("lambda < 0\n");
- return GSL_NAN;
+ if ( crystal_get_image(pv->cr_tgt)->lambda <= 0.0 ) {
+ STATUS("lambda < 0\n");
+ return NAN;
}
- list = copy_reflist(crystal_get_reflections(cr));
- crystal_set_reflections(cr, list);
-
- update_predictions(cr);
- calculate_partialities(cr, PMODEL_XSPHERE);
-
- if ( scale_one_crystal(cr, pv->full, pv->scaleflags) ) {
- cell_free(crystal_get_cell(cr));
- reflist_free(crystal_get_reflections(cr));
- crystal_free(cr);
- if ( pv->verbose ) STATUS("Bad scaling\n");
- return GSL_NAN;
- }
- res = residual(cr, pv->full, 0, NULL, NULL);
+ update_predictions(pv->cr_tgt);
+ calculate_partialities(pv->cr_tgt, pv->pmodel);
- cell_free(crystal_get_cell(cr));
- reflist_free(crystal_get_reflections(cr));
- crystal_free(cr);
- return res;
-}
-
-
-static double get_initial_param(Crystal *cr, enum gparam p)
-{
- switch ( p ) {
-
- case GPARAM_ANG1 : return 0.0;
- case GPARAM_ANG2 : return 0.0;
- case GPARAM_R : return crystal_get_profile_radius(cr);
- case GPARAM_WAVELENGTH : return crystal_get_image(cr)->lambda;
-
- default: return 0.0;
-
- }
-}
-
-
-static int check_angle_shifts(gsl_vector *cur, const gsl_vector *initial,
- enum gparam *rv, struct rf_priv *residual_f_priv)
-{
- int i = 0;
- double ang = 0.0;
-
- while ( rv[i] != GPARAM_EOL ) {
- if ( (rv[i] == GPARAM_ANG1) || (rv[i] == GPARAM_ANG2) ) {
- ang += fabs(get_actual_val(cur, initial, rv, i));
- }
- rv++;
- }
-
- if ( rad2deg(ang) > 5.0 ) {
- ERROR("More than 5 degrees total rotation!\n");
- residual_f_priv->verbose = 1;
- double res = residual_f(cur, residual_f_priv);
- STATUS("residual after rotation = %e\n", res);
- residual_f_priv->verbose = 2;
- res = residual_f(initial, residual_f_priv);
- STATUS("residual before rotation = %e\n", res);
- return 1;
- }
- return 0;
+ return residual(pv->cr_tgt, pv->full, free, NULL, NULL);
}
@@ -410,7 +265,8 @@ static void reindex_cell(UnitCell *cell, SymOpList *amb, int idx)
static void try_reindex(Crystal *crin, const RefList *full,
- SymOpList *sym, SymOpList *amb, int scaleflags)
+ SymOpList *sym, SymOpList *amb, int scaleflags,
+ PartialityModel pmodel)
{
RefList *list;
Crystal *cr;
@@ -439,7 +295,7 @@ static void try_reindex(Crystal *crin, const RefList *full,
crystal_set_reflections(cr, list);
update_predictions(cr);
- calculate_partialities(cr, PMODEL_XSPHERE);
+ calculate_partialities(cr, pmodel);
if ( scale_one_crystal(cr, full, scaleflags) ) return;
residual_flipped = residual(cr, full, 0, NULL, NULL);
@@ -561,6 +417,9 @@ void write_specgraph(Crystal *crystal, const RefList *full,
signed int h, k, l;
Reflection *match;
+ /* Strong reflections only */
+ if ( get_intensity(refl) < 3.0*get_esd_intensity(refl) ) continue;
+
get_indices(refl, &h, &k, &l);
res = resolution(cell, h, k, l);
@@ -570,7 +429,7 @@ void write_specgraph(Crystal *crystal, const RefList *full,
/* Don't calculate pobs if reference reflection is weak */
if ( fabs(get_intensity(match)) / get_esd_intensity(match) < 3.0 ) continue;
- Ipart = correct_reflection_nopart(refl, G, B, res);
+ Ipart = correct_reflection_nopart(get_intensity(refl), refl, G, B, res);
Ifull = get_intensity(match);
pobs = Ipart / Ifull;
pcalc = get_partiality(refl);
@@ -584,90 +443,98 @@ void write_specgraph(Crystal *crystal, const RefList *full,
}
-static gsl_multimin_fminimizer *setup_minimiser(Crystal *cr, const RefList *full,
- int verbose, int serial,
- int scaleflags,
- struct rf_priv *priv)
+static void write_angle_grid(Crystal *cr, const RefList *full,
+ signed int cycle, int serial, int scaleflags,
+ PartialityModel pmodel)
{
- gsl_multimin_fminimizer *min;
- int n_params = 0;
- int i, r;
-
- /* The parameters to be refined */
- priv->rv[n_params++] = GPARAM_ANG1;
- priv->rv[n_params++] = GPARAM_ANG2;
- priv->rv[n_params++] = GPARAM_R;
- priv->rv[n_params++] = GPARAM_WAVELENGTH;
- priv->rv[n_params] = GPARAM_EOL; /* End of list */
-
- priv->cr = cr;
- priv->full = full;
- priv->verbose = verbose;
- priv->serial = serial;
- priv->scaleflags = scaleflags;
-
- priv->f.f = residual_f;
- priv->f.n = n_params;
- priv->f.params = priv;
-
- priv->initial = gsl_vector_alloc(n_params);
- priv->vals = gsl_vector_alloc(n_params);
- priv->step = gsl_vector_alloc(n_params);
-
- for ( i=0; i<n_params; i++ ) {
- gsl_vector_set(priv->initial, i, get_initial_param(cr, priv->rv[i]));
- gsl_vector_set(priv->vals, i, 0.0);
- gsl_vector_set(priv->step, i, 1.0);
- }
+ FILE *fh;
+ char fn[64];
+ char ins[16];
+ struct rf_priv priv;
+ RefList *list;
+ UnitCell *cell;
+ Spectrum *spectrum;
+
+ priv.cr = cr;
+ priv.full = full;
+ priv.serial = serial;
+ priv.scaleflags = scaleflags;
+ priv.pmodel = pmodel;
+ priv.cr_tgt = crystal_copy(cr);
+ priv.image_tgt = *crystal_get_image(cr);
+ spectrum = spectrum_new();
+ priv.image_tgt.spectrum = spectrum;
+ crystal_set_image(priv.cr_tgt, &priv.image_tgt);
+ list = copy_reflist(crystal_get_reflections(cr));
+ crystal_set_reflections(priv.cr_tgt, list);
+ cell = cell_new_from_cell(crystal_get_cell(cr));
+ crystal_set_cell(priv.cr_tgt, cell);
- min = gsl_multimin_fminimizer_alloc(gsl_multimin_fminimizer_nmsimplex2,
- n_params);
- if ( min == NULL ) {
- ERROR("Failed to allocate minimiser\n");
- gsl_vector_free(priv->vals);
- gsl_vector_free(priv->step);
- gsl_vector_free(priv->initial);
- return NULL;
+ if ( cycle >= 0 ) {
+ snprintf(ins, 16, "%i", cycle);
+ } else {
+ ins[0] = 'F';
+ ins[1] = '\0';
}
- r = gsl_multimin_fminimizer_set(min, &priv->f, priv->vals, priv->step);
- if ( r != 0 ) {
- gsl_multimin_fminimizer_free(min);
- gsl_vector_free(priv->vals);
- gsl_vector_free(priv->step);
- gsl_vector_free(priv->initial);
- ERROR("Failed to set up minimiser: %s\n", gsl_strerror(r));
- return NULL;
+ snprintf(fn, 64, "pr-logs/grid-crystal%i-cycle%s-ang1-ang2.dat",
+ serial, ins);
+ fh = fopen(fn, "w");
+ if ( fh != NULL ) {
+ double v1, v2;
+ fprintf(fh, "%e %e %e %s\n", -5.0e-3, 5.0e-3, 0.0, "rot_x/rad");
+ fprintf(fh, "%e %e %e %s\n", -5.0e-3, 5.0e-3, 0.0, "rot_y/rad");
+ for ( v2=-5.0e-3; v2<=5.1e-3; v2+=0.25e-3 ) {
+ int first=1;
+ for ( v1=-5.0e-3; v1<=5.1e-3; v1+=0.25e-3 ) {
+ double res;
+ struct rf_alteration alter;
+ alter.rot_x = v1;
+ alter.rot_y = v2;
+ alter.delta_R = 0.0;
+ alter.delta_wave = 0.0;
+ res = calc_residual(&priv, alter, 0);
+ if ( !first ) fprintf(fh, " ");
+ first = 0;
+ fprintf(fh, "%e", res);
+ }
+ fprintf(fh, "\n");
+ }
}
+ fclose(fh);
- return min;
+ reflist_free(crystal_get_reflections(priv.cr_tgt));
+ crystal_free(priv.cr_tgt);
+ spectrum_free(spectrum);
}
-static void write_grid(Crystal *cr, const RefList *full,
- signed int cycle, int serial, int scaleflags,
- const enum gparam par1, const enum gparam par2,
- const char *name)
+static void write_radius_grid(Crystal *cr, const RefList *full,
+ signed int cycle, int serial, int scaleflags,
+ PartialityModel pmodel)
{
FILE *fh;
char fn[64];
char ins[16];
- gsl_multimin_fminimizer *min;
struct rf_priv priv;
- int idx1, idx2;
- int i;
-
- min = setup_minimiser(cr, full, 0, serial, scaleflags, &priv);
- if ( min == NULL ) return;
-
- idx1 = 99;
- idx2 = 99;
- for ( i=0; i<priv.f.n; i++ ) {
- if ( priv.rv[i] == par1 ) idx1 = i;
- if ( priv.rv[i] == par2 ) idx2 = i;
- }
- assert(idx1 != 99);
- assert(idx2 != 99);
+ RefList *list;
+ UnitCell *cell;
+ Spectrum *spectrum;
+
+ priv.cr = cr;
+ priv.full = full;
+ priv.serial = serial;
+ priv.scaleflags = scaleflags;
+ priv.pmodel = pmodel;
+ priv.cr_tgt = crystal_copy(cr);
+ priv.image_tgt = *crystal_get_image(cr);
+ spectrum = spectrum_new();
+ priv.image_tgt.spectrum = spectrum;
+ crystal_set_image(priv.cr_tgt, &priv.image_tgt);
+ list = copy_reflist(crystal_get_reflections(cr));
+ crystal_set_reflections(priv.cr_tgt, list);
+ cell = cell_new_from_cell(crystal_get_cell(cr));
+ crystal_set_cell(priv.cr_tgt, cell);
if ( cycle >= 0 ) {
snprintf(ins, 16, "%i", cycle);
@@ -676,28 +543,23 @@ static void write_grid(Crystal *cr, const RefList *full,
ins[1] = '\0';
}
- snprintf(fn, 64, "pr-logs/grid-crystal%i-cycle%s-%s.dat",
- serial, ins, name);
+ snprintf(fn, 64, "pr-logs/grid-crystal%i-cycle%s-R-wave.dat",
+ serial, ins);
fh = fopen(fn, "w");
if ( fh != NULL ) {
double v1, v2;
- fprintf(fh, "%e %e %e %s\n",
- -5.0*get_scale(par1)+get_initial_param(cr, par1),
- 5.0*get_scale(par1)+get_initial_param(cr, par1),
- get_initial_param(cr, par1),
- get_label(par1));
- fprintf(fh, "%e %e %e %s\n",
- -5.0*get_scale(par2)+get_initial_param(cr, par2),
- 5.0*get_scale(par2)+get_initial_param(cr, par2),
- get_initial_param(cr, par2),
- get_label(par2));
- for ( v2=-5.0; v2<=5.0; v2+=0.25 ) {
+ fprintf(fh, "%e %e %e %s\n", -4e-13, 4e-13, 0.0, "wavelength change/m");
+ fprintf(fh, "%e %e %e %s\n", -2e6, 2e6, 0.0, "radius change/m^-1");
+ for ( v2=-2e6; v2<=2.001e6; v2+=100000 ) {
int first=1;
- for ( v1=-5.0; v1<=5.0; v1+=0.25 ) {
+ for ( v1=-4e-13; v1<=4.001e-13; v1+=2e-14 ) {
double res;
- gsl_vector_set(min->x, idx1, v1);
- gsl_vector_set(min->x, idx2, v2);
- res = residual_f(min->x, &priv);
+ struct rf_alteration alter;
+ alter.rot_x = 0.0;
+ alter.rot_y = 0.0;
+ alter.delta_R = v2;
+ alter.delta_wave = v1;
+ res = calc_residual(&priv, alter, 0);
if ( !first ) fprintf(fh, " ");
first = 0;
fprintf(fh, "%e", res);
@@ -707,205 +569,177 @@ static void write_grid(Crystal *cr, const RefList *full,
}
fclose(fh);
- gsl_multimin_fminimizer_free(min);
- gsl_vector_free(priv.initial);
- gsl_vector_free(priv.vals);
- gsl_vector_free(priv.step);
+ reflist_free(crystal_get_reflections(priv.cr_tgt));
+ crystal_free(priv.cr_tgt);
+ spectrum_free(spectrum);
}
void write_gridscan(Crystal *cr, const RefList *full,
- signed int cycle, int serial, int scaleflags)
+ signed int cycle, int serial, int scaleflags,
+ PartialityModel pmodel)
{
- write_grid(cr, full, cycle, serial, scaleflags,
- GPARAM_ANG1, GPARAM_ANG2, "ang1-ang2");
- write_grid(cr, full, cycle, serial, scaleflags,
- GPARAM_ANG1, GPARAM_WAVELENGTH, "ang1-wave");
- write_grid(cr, full, cycle, serial, scaleflags,
- GPARAM_R, GPARAM_WAVELENGTH, "R-wave");
+ write_angle_grid(cr, full, cycle, serial, scaleflags, pmodel);
+ write_radius_grid(cr, full, cycle, serial, scaleflags, pmodel);
}
-static void do_pr_refine(Crystal *cr, const RefList *full,
- PartialityModel pmodel, int verbose, int serial,
- int cycle, int write_logs,
- SymOpList *sym, SymOpList *amb, int scaleflags)
+static int refine_loop(struct rf_alteration *cur, struct rf_alteration *dirns,
+ int n_dirns, struct rf_priv *priv, int *total_iter,
+ FILE *fh)
{
- gsl_multimin_fminimizer *min;
- struct rf_priv priv;
int n_iter = 0;
- int status;
- double residual_start, residual_free_start;
- FILE *fh = NULL;
-
- try_reindex(cr, full, sym, amb, scaleflags);
+ int best;
- if ( scale_one_crystal(cr, full, scaleflags | SCALE_VERBOSE_ERRORS) ) {
- ERROR("Bad scaling at start of refinement.\n");
- return;
- }
- residual_start = residual(cr, full, 0, NULL, NULL);
- residual_free_start = residual(cr, full, 1, NULL, NULL);
-
- if ( verbose ) {
- STATUS("\nPR initial: dev = %10.5e, free dev = %10.5e\n",
- residual_start, residual_free_start);
- }
-
- min = setup_minimiser(cr, full, verbose, serial, scaleflags, &priv);
- if ( min == NULL ) return;
-
- if ( verbose ) {
- double res = residual_f(min->x, &priv);
- double size = gsl_multimin_fminimizer_size(min);
- STATUS("At start: %f %f %f %f ----> %f %f %e %f residual = %e size %f\n",
- gsl_vector_get(min->x, 0),
- gsl_vector_get(min->x, 1),
- gsl_vector_get(min->x, 2),
- gsl_vector_get(min->x, 3),
- rad2deg(get_actual_val(min->x, priv.initial, priv.rv, 0)),
- rad2deg(get_actual_val(min->x, priv.initial, priv.rv, 1)),
- get_actual_val(min->x, priv.initial, priv.rv, 2),
- get_actual_val(min->x, priv.initial, priv.rv, 3)*1e10,
- res, size);
- }
+ do {
- if ( write_logs ) {
+ struct rf_alteration trials[n_dirns+1];
+ double lowest_fom = INFINITY;
+ double freefom;
+ int i;
- char fn[64];
+ best = 0;
- snprintf(fn, 63, "pr-logs/crystal%i-cycle%i.log", serial, cycle);
- fh = fopen(fn, "w");
- if ( fh != NULL ) {
- fprintf(fh, "iteration RtoReference CCtoReference nref "
- "ang1 ang2 radius wavelength");
- double res = residual_f(min->x, &priv);
- fprintf(fh, "%5i %10.8f %10.8f %5i %10.8f %10.8f %e %e\n",
- n_iter, res, 0.0, 0,
- rad2deg(get_actual_val(min->x, priv.initial, priv.rv, 0)),
- rad2deg(get_actual_val(min->x, priv.initial, priv.rv, 1)),
- get_actual_val(min->x, priv.initial, priv.rv, 2),
- get_actual_val(min->x, priv.initial, priv.rv, 3)*1e10);
+ trials[0] = *cur;
+ for ( i=0; i<n_dirns; i++ ) {
+ trials[i+1] = *cur;
+ trials[i+1].rot_x += dirns[i].rot_x;
+ trials[i+1].rot_y += dirns[i].rot_y;
+ trials[i+1].delta_R += dirns[i].delta_R;
+ trials[i+1].delta_wave += dirns[i].delta_wave;
}
- }
+ for ( i=0; i<n_dirns+1; i++ ) {
+ double nfom = calc_residual(priv, trials[i], 0);
+ if ( nfom < lowest_fom ) {
+ best = i;
+ lowest_fom = nfom;
+ }
+ }
- do {
- double res;
+ *cur = trials[best];
n_iter++;
+ (*total_iter)++;
- status = gsl_multimin_fminimizer_iterate(min);
- if ( status ) break;
-
- res = residual_f(min->x, &priv);
- if ( isnan(res) ) {
- status = GSL_ENOPROG;
- break;
- }
-
- if ( verbose ) {
- double res = residual_f(min->x, &priv);
- double size = gsl_multimin_fminimizer_size(min);
- STATUS("%f %f %f %f ----> %f %f %e %f residual = %e size %f\n",
- gsl_vector_get(min->x, 0),
- gsl_vector_get(min->x, 1),
- gsl_vector_get(min->x, 2),
- gsl_vector_get(min->x, 3),
- rad2deg(get_actual_val(min->x, priv.initial, priv.rv, 0)),
- rad2deg(get_actual_val(min->x, priv.initial, priv.rv, 1)),
- get_actual_val(min->x, priv.initial, priv.rv, 2),
- get_actual_val(min->x, priv.initial, priv.rv, 3)*1e10,
- res, size);
- }
-
+ freefom = calc_residual(priv, *cur, 1);
if ( fh != NULL ) {
- fprintf(fh, "%5i %10.8f %10.8f %5i %10.8f %10.8f %e %e\n",
- n_iter, res, 0.0, 0,
- rad2deg(get_actual_val(min->x, priv.initial, priv.rv, 0)),
- rad2deg(get_actual_val(min->x, priv.initial, priv.rv, 1)),
- get_actual_val(min->x, priv.initial, priv.rv, 2),
- get_actual_val(min->x, priv.initial, priv.rv, 3)*1e10);
+ fprintf(fh, "%5i %10.8f %10.8f %10.8f %10.8f %e %e\n",
+ *total_iter, lowest_fom, freefom,
+ cur->rot_x, cur->rot_y,
+ crystal_get_profile_radius(priv->cr)+cur->delta_R,
+ crystal_get_image_const(priv->cr)->lambda+cur->delta_wave);
}
- status = gsl_multimin_test_size(min->size, 0.005);
+ } while ( (best != 0) && (n_iter < 10) );
- } while ( status == GSL_CONTINUE && n_iter < 1000 );
+ return 0;
+}
- if ( verbose ) {
- STATUS("Done with refinement after %i iter\n", n_iter);
- STATUS("status = %i (%s)\n", status, gsl_strerror(status));
- }
- if ( status == GSL_SUCCESS ) {
+static void zero_alter(struct rf_alteration *alter)
+{
+ alter->rot_x = 0.0;
+ alter->rot_y = 0.0;
+ alter->delta_R = 0.0;
+ alter->delta_wave = 0.0;
+}
- if ( check_angle_shifts(min->x, priv.initial, priv.rv, &priv) ) return;
- if ( verbose ) {
+static void do_pr_refine(Crystal *cr, const RefList *full,
+ PartialityModel pmodel, int serial,
+ int cycle, int write_logs,
+ SymOpList *sym, SymOpList *amb, int scaleflags)
+{
+ struct rf_priv priv;
+ struct rf_alteration alter;
+ int n_iter = 0;
+ double fom, freefom;
+ RefList *list;
+ FILE *fh = NULL;
+ UnitCell *cell;
+ Spectrum *spectrum;
+
+ try_reindex(cr, full, sym, amb, scaleflags, pmodel);
+
+ zero_alter(&alter);
+
+ priv.cr = cr;
+ priv.full = full;
+ priv.serial = serial;
+ priv.scaleflags = scaleflags;
+ priv.pmodel = pmodel;
+ priv.cr_tgt = crystal_copy(cr);
+ priv.image_tgt = *crystal_get_image(cr);
+ spectrum = spectrum_new();
+ priv.image_tgt.spectrum = spectrum;
+ crystal_set_image(priv.cr_tgt, &priv.image_tgt);
+ list = copy_reflist(crystal_get_reflections(cr));
+ crystal_set_reflections(priv.cr_tgt, list);
+ cell = cell_new_from_cell(crystal_get_cell(cr));
+ crystal_set_cell(priv.cr_tgt, cell);
- double res = residual_f(min->x, &priv);
- double size = gsl_multimin_fminimizer_size(min);
- STATUS("At end: %f %f %f %f ----> %f %f %e %f residual = %e size %f\n",
- gsl_vector_get(min->x, 0),
- gsl_vector_get(min->x, 1),
- gsl_vector_get(min->x, 2),
- gsl_vector_get(min->x, 3),
- rad2deg(get_actual_val(min->x, priv.initial, priv.rv, 0)),
- rad2deg(get_actual_val(min->x, priv.initial, priv.rv, 1)),
- get_actual_val(min->x, priv.initial, priv.rv, 2),
- get_actual_val(min->x, priv.initial, priv.rv, 3)*1e10,
- res, size);
+ fom = calc_residual(&priv, alter, 0);
+ freefom = calc_residual(&priv, alter, 1);
- }
+ if ( write_logs ) {
+
+ char fn[64];
+ snprintf(fn, 63, "pr-logs/crystal%i-cycle%i.log", serial, cycle);
+ fh = fopen(fn, "w");
if ( fh != NULL ) {
- double res = residual_f(min->x, &priv);
- fprintf(fh, "%5i %10.8f %10.8f %5i %10.8f %10.8f %e %e\n",
- n_iter, res, 0.0, 0,
- rad2deg(get_actual_val(min->x, priv.initial, priv.rv, 0)),
- rad2deg(get_actual_val(min->x, priv.initial, priv.rv, 1)),
- get_actual_val(min->x, priv.initial, priv.rv, 2),
- get_actual_val(min->x, priv.initial, priv.rv, 3)*1e10);
+ fprintf(fh, "iter FoM FreeFoM rotx/rad "
+ "roty/rad radius/m wavelength/m\n");
+ fprintf(fh, "%5i %10.8f %10.8f %10.8f %10.8f %e %e\n",
+ n_iter, fom, freefom,
+ alter.rot_x, alter.rot_y,
+ crystal_get_profile_radius(cr)+alter.delta_R,
+ crystal_get_image(cr)->lambda+alter.delta_wave);
}
- /* Apply the final shifts */
- apply_parameters(min->x, priv.initial, priv.rv, cr);
- update_predictions(cr);
- calculate_partialities(cr, PMODEL_XSPHERE);
- scale_one_crystal(cr, full, scaleflags);
+ }
- if ( verbose ) {
+ /* Refine orientation */
+ struct rf_alteration dirns[4];
+ zero_alter(&dirns[0]); dirns[0].rot_x += 0.1e-3;
+ zero_alter(&dirns[1]); dirns[1].rot_x -= 0.1e-3;
+ zero_alter(&dirns[2]); dirns[2].rot_y += 0.1e-3;
+ zero_alter(&dirns[3]); dirns[3].rot_y -= 0.1e-3;
+ refine_loop(&alter, dirns, 4, &priv, &n_iter, fh);
- STATUS("After applying final shifts:\n");
- STATUS("PR final: dev = %10.5e, free dev = %10.5e\n",
- residual(cr, full, 0, NULL, NULL),
- residual(cr, full, 1, NULL, NULL));
- STATUS("Final R = %e m^-1\n", crystal_get_profile_radius(cr));
+ /* Refine profile radius */
+ zero_alter(&dirns[0]); dirns[0].delta_R += 1e5;
+ zero_alter(&dirns[1]); dirns[1].delta_R -= 1e5;
+ refine_loop(&alter, dirns, 2, &priv, &n_iter, fh);
- }
+ /* Refine wavelength */
+ zero_alter(&dirns[0]); dirns[0].delta_wave += 2.0e-14;
+ zero_alter(&dirns[1]); dirns[1].delta_wave -= 2.0e-14;
+ refine_loop(&alter, dirns, 2, &priv, &n_iter, fh);
- } else {
- ERROR("Bad refinement: crystal %i\n", serial);
- }
+ /* Apply the final shifts */
+ apply_parameters(cr, cr, alter);
+ update_predictions(cr);
+ calculate_partialities(cr, pmodel);
if ( write_logs ) {
- write_gridscan(cr, full, cycle, serial, scaleflags);
+ write_gridscan(cr, full, cycle, serial, scaleflags, pmodel);
write_specgraph(cr, full, cycle, serial);
write_test_logs(cr, full, cycle, serial);
}
if ( crystal_get_profile_radius(cr) > 5e9 ) {
- ERROR("Very large radius: crystal %i\n", serial);
+ ERROR("WARNING: Very large radius: crystal %i\n", serial);
}
if ( fh != NULL ) {
fclose(fh);
}
- gsl_multimin_fminimizer_free(min);
- gsl_vector_free(priv.initial);
- gsl_vector_free(priv.vals);
- gsl_vector_free(priv.step);
+ reflist_free(crystal_get_reflections(priv.cr_tgt));
+ crystal_free(priv.cr_tgt);
+ spectrum_free(spectrum);
}
@@ -915,8 +749,6 @@ struct refine_args
Crystal *crystal;
PartialityModel pmodel;
int serial;
- struct prdata prdata;
- int verbose;
int cycle;
int no_logs;
SymOpList *sym;
@@ -942,15 +774,10 @@ static void refine_image(void *task, int id)
int write_logs = 0;
write_logs = !pargs->no_logs && (pargs->serial % 20 == 0);
- pargs->prdata.refined = 0;
- do_pr_refine(cr, pargs->full, pargs->pmodel, pargs->verbose,
+ do_pr_refine(cr, pargs->full, pargs->pmodel,
pargs->serial, pargs->cycle, write_logs,
pargs->sym, pargs->amb, pargs->scaleflags);
-
- if ( crystal_get_user_flag(cr) == 0 ) {
- pargs->prdata.refined = 1;
- }
}
@@ -984,7 +811,7 @@ static void done_image(void *vqargs, void *task)
void refine_all(Crystal **crystals, int n_crystals,
RefList *full, int nthreads, PartialityModel pmodel,
- int verbose, int cycle, int no_logs,
+ int cycle, int no_logs,
SymOpList *sym, SymOpList *amb, int scaleflags)
{
struct refine_args task_defaults;
@@ -993,8 +820,6 @@ void refine_all(Crystal **crystals, int n_crystals,
task_defaults.full = full;
task_defaults.crystal = NULL;
task_defaults.pmodel = pmodel;
- task_defaults.prdata.refined = 0;
- task_defaults.verbose = verbose;
task_defaults.cycle = cycle;
task_defaults.no_logs = no_logs;
task_defaults.sym = sym;
diff --git a/src/post-refinement.h b/src/post-refinement.h
index e2873b76..7a86b813 100644
--- a/src/post-refinement.h
+++ b/src/post-refinement.h
@@ -60,11 +60,12 @@ extern const char *str_prflag(enum prflag flag);
extern void refine_all(Crystal **crystals, int n_crystals,
RefList *full, int nthreads, PartialityModel pmodel,
- int verbose, int cycle, int no_logs,
+ int cycle, int no_logs,
SymOpList *sym, SymOpList *amb, int scaleflags);
extern void write_gridscan(Crystal *cr, const RefList *full,
- int cycle, int serial, int scaleflags);
+ int cycle, int serial, int scaleflags,
+ PartialityModel model);
extern void write_specgraph(Crystal *crystal, const RefList *full,
signed int cycle, int serial);
diff --git a/src/process_hkl.c b/src/process_hkl.c
index ab5b9af1..393dc3e8 100644
--- a/src/process_hkl.c
+++ b/src/process_hkl.c
@@ -44,7 +44,6 @@
#include <getopt.h>
#include "utils.h"
-#include "statistics.h"
#include "reflist-utils.h"
#include "symmetry.h"
#include "stream.h"
diff --git a/src/process_image.h b/src/process_image.h
index 48160bb6..8d6682a6 100644
--- a/src/process_image.h
+++ b/src/process_image.h
@@ -117,7 +117,7 @@ struct index_args
struct taketwo_options taketwo_opts;
struct xgandalf_options xgandalf_opts;
struct felix_options felix_opts;
- struct spectrum *spectrum;
+ Spectrum *spectrum;
signed int wait_for_file; /* -1 means wait forever */
int no_image_data;
};
diff --git a/src/rejection.c b/src/rejection.c
index 5d1041fa..8d6c30bb 100644
--- a/src/rejection.c
+++ b/src/rejection.c
@@ -3,11 +3,11 @@
*
* Crystal rejection for scaling
*
- * Copyright © 2012-2018 Deutsches Elektronen-Synchrotron DESY,
+ * Copyright © 2012-2019 Deutsches Elektronen-Synchrotron DESY,
* a research centre of the Helmholtz Association.
*
* Authors:
- * 2010-2018 Thomas White <taw@physics.org>
+ * 2010-2019 Thomas White <taw@physics.org>
*
* This file is part of CrystFEL.
*
@@ -143,7 +143,7 @@ static int calculate_refl_mean_var(RefList *full)
G = crystal_get_osf(c->contrib_crystals[j]);
B = crystal_get_Bfac(c->contrib_crystals[j]);
- Ii = correct_reflection(c->contribs[j], G, B, res);
+ Ii = correct_reflection(get_intensity(c->contribs[j]), c->contribs[j], G, B, res);
Ex += Ii - K;
Ex2 += (Ii - K) * (Ii - K);
@@ -234,7 +234,7 @@ static double calculate_cchalf(RefList *template, RefList *full,
if ( get_partiality(exrefl) > MIN_PART_MERGE ) {
- double Ii = correct_reflection(exrefl, G, B, res);
+ double Ii = correct_reflection(get_intensity(exrefl), exrefl, G, B, res);
/* Remove contribution of this reflection */
Ex -= Ii - K;
@@ -274,15 +274,98 @@ static double calculate_cchalf(RefList *template, RefList *full,
}
-static void check_deltacchalf(Crystal **crystals, int n, RefList *full)
+struct deltacchalf_queue_args
+{
+ RefList *full;
+ Crystal **crystals;
+ int n_crystals;
+ int n_done;
+ int n_started;
+ int n_non;
+ int n_nan;
+ double *vals;
+};
+
+
+struct deltacchalf_worker_args
+{
+ RefList *full;
+ Crystal *crystal;
+ int crystal_number;
+ int non;
+ int nan;
+ double deltaCChalf;
+};
+
+
+static void *create_deltacchalf_job(void *vqargs)
+{
+ struct deltacchalf_worker_args *wargs;
+ struct deltacchalf_queue_args *qargs = vqargs;
+
+ wargs = malloc(sizeof(struct deltacchalf_worker_args));
+
+ wargs->full = qargs->full;
+ wargs->crystal = qargs->crystals[qargs->n_started];
+ wargs->crystal_number = qargs->n_started;
+ wargs->non = 0;
+ wargs->nan = 0;
+
+ qargs->n_started++;
+
+ return wargs;
+}
+
+
+static void run_deltacchalf_job(void *vwargs, int cookie)
+{
+ double cchalf, cchalfi;
+ struct deltacchalf_worker_args *wargs = vwargs;
+ int nref = 0;
+ RefList *template = crystal_get_reflections(wargs->crystal);
+ cchalf = calculate_cchalf(template, wargs->full, NULL, &nref);
+ cchalfi = calculate_cchalf(template, wargs->full, wargs->crystal, &nref);
+ //STATUS("Frame %i:", i);
+ //STATUS(" With = %f ", cchalf*100.0);
+ //STATUS("Without = %f", cchalfi*100.0);
+ //STATUS(" Delta = %f ", (cchalf - cchalfi)*100.0);
+ //STATUS("(nref = %i)\n", nref);
+ if ( nref == 0 ) {
+ wargs->deltaCChalf = 0.0;
+ wargs->non = 1;
+ } else {
+ wargs->deltaCChalf = cchalf - cchalfi;
+ if ( isnan(wargs->deltaCChalf) || isinf(wargs->deltaCChalf) ) {
+ wargs->deltaCChalf = 0.0;
+ wargs->nan = 1;
+ }
+ }
+}
+
+
+static void finalise_deltacchalf_job(void *vqargs, void *vwargs)
+{
+ struct deltacchalf_queue_args *qargs = vqargs;
+ struct deltacchalf_worker_args *wargs = vwargs;
+ qargs->n_done++;
+ if ( wargs->nan ) qargs->n_nan++;
+ if ( wargs->non ) qargs->n_non++;
+ qargs->vals[wargs->crystal_number] = wargs->deltaCChalf;
+ progress_bar(qargs->n_done, qargs->n_crystals,
+ "Calculating deltaCChalf");
+ free(vwargs);
+}
+
+
+static void check_deltacchalf(Crystal **crystals, int n, RefList *full,
+ int n_threads)
{
double cchalf;
int i;
double *vals;
double mean, sd;
int nref = 0;
- int nnan = 0;
- int nnon = 0;
+ struct deltacchalf_queue_args qargs;
if ( calculate_refl_mean_var(full) ) {
STATUS("No reflection contributions for deltaCChalf "
@@ -299,35 +382,25 @@ static void check_deltacchalf(Crystal **crystals, int n, RefList *full)
return;
}
- for ( i=0; i<n; i++ ) {
- double cchalf, cchalfi;
- RefList *template = crystal_get_reflections(crystals[i]);
- cchalf = calculate_cchalf(template, full, NULL, &nref);
- cchalfi = calculate_cchalf(template, full, crystals[i], &nref);
- //STATUS("Frame %i:", i);
- //STATUS(" With = %f ", cchalf*100.0);
- //STATUS("Without = %f", cchalfi*100.0);
- //STATUS(" Delta = %f ", (cchalf - cchalfi)*100.0);
- //STATUS("(nref = %i)\n", nref);
- if ( nref == 0 ) {
- vals[i] = 0.0;
- nnon++;
- } else {
- vals[i] = cchalf - cchalfi;
- if ( isnan(vals[i]) || isinf(vals[i]) ) {
- vals[i] = 0.0;
- nnan++;
- }
- }
- progress_bar(i, n-1, "Calculating deltaCChalf");
- }
- if ( nnon > 0 ) {
+ qargs.full = full;
+ qargs.crystals = crystals;
+ qargs.n_started = 0;
+ qargs.n_crystals = n;
+ qargs.n_done = 0;
+ qargs.n_nan = 0;
+ qargs.n_non = 0;
+ qargs.vals = vals;
+ run_threads(n_threads, run_deltacchalf_job, create_deltacchalf_job,
+ finalise_deltacchalf_job, &qargs, n, 0, 0, 0);
+
+ if ( qargs.n_non > 0 ) {
STATUS("WARNING: %i patterns had no reflections in deltaCChalf "
- "calculation (I set deltaCChalf=zero for them)\n", nnon);
+ "calculation (I set deltaCChalf=zero for them)\n",
+ qargs.n_non);
}
- if ( nnan > 0 ) {
+ if ( qargs.n_nan > 0 ) {
STATUS("WARNING: %i NaN or inf deltaCChalf values were "
- "replaced with zero\n", nnan);
+ "replaced with zero\n", qargs.n_nan);
}
mean = gsl_stats_mean(vals, 1, n);
@@ -374,14 +447,14 @@ static void show_duds(Crystal **crystals, int n_crystals)
void check_rejection(Crystal **crystals, int n, RefList *full, double max_B,
- int no_deltacchalf)
+ int no_deltacchalf, int n_threads)
{
int i;
int n_acc = 0;
/* Check according to delta CC½ */
if ( !no_deltacchalf && (full != NULL) ) {
- check_deltacchalf(crystals, n, full);
+ check_deltacchalf(crystals, n, full, n_threads);
}
for ( i=0; i<n; i++ ) {
diff --git a/src/rejection.h b/src/rejection.h
index 5a0c3e9f..31859928 100644
--- a/src/rejection.h
+++ b/src/rejection.h
@@ -3,11 +3,11 @@
*
* Crystal rejection for scaling
*
- * Copyright © 2012-2018 Deutsches Elektronen-Synchrotron DESY,
+ * Copyright © 2012-2019 Deutsches Elektronen-Synchrotron DESY,
* a research centre of the Helmholtz Association.
*
* Authors:
- * 2010-2018 Thomas White <taw@physics.org>
+ * 2010-2019 Thomas White <taw@physics.org>
*
* This file is part of CrystFEL.
*
@@ -39,6 +39,6 @@
extern void early_rejection(Crystal **crystals, int n);
extern void check_rejection(Crystal **crystals, int n, RefList *full,
- double max_B, int no_deltacchalf);
+ double max_B, int no_deltacchalf, int n_threads);
#endif /* REJECTION_H */
diff --git a/src/whirligig.c b/src/whirligig.c
index f5a435c2..cc2b531e 100644
--- a/src/whirligig.c
+++ b/src/whirligig.c
@@ -84,7 +84,7 @@ static void do_op(const IntegerMatrix *op,
v[0] = h; v[1] = k; v[2] = l;
- ans = intmat_intvec_mult(op, v);
+ ans = transform_indices(op, v);
assert(ans != NULL);
*he = ans[0]; *ke = ans[1]; *le = ans[2];
@@ -309,9 +309,9 @@ static IntegerMatrix *try_all(struct window *win, int n1, int n2,
for ( i=0; i<i1->n_crystals; i++ ) {
for ( j=0; j<i2->n_crystals; j++ ) {
- if ( compare_cells(crystal_get_cell(i1->crystals[i]),
- crystal_get_cell(i2->crystals[j]),
- 0.1, deg2rad(5.0), &m) )
+ if ( compare_reindexed_cell_parameters_and_orientation(crystal_get_cell(i1->crystals[i]),
+ crystal_get_cell(i2->crystals[j]),
+ 0.1, deg2rad(5.0), &m) )
{
if ( !crystal_used(win, n1, i)
&& !crystal_used(win, n2, j) )
@@ -364,22 +364,20 @@ static int try_join(struct window *win, int sn)
int j;
Crystal *cr;
UnitCell *ref;
- UnitCellTransformation *tfn;
const int sp = win->join_ptr - 1;
/* Get the appropriately transformed cell from the last crystal in this
* series */
- tfn = tfn_from_intmat(win->mat[sn][sp]);
cr = win->img[sp].crystals[win->ser[sn][sp]];
- ref = cell_transform(crystal_get_cell(cr), tfn);
- tfn_free(tfn);
+ ref = cell_transform_intmat(crystal_get_cell(cr), win->mat[sn][sp]);
for ( j=0; j<win->img[win->join_ptr].n_crystals; j++ ) {
Crystal *cr2;
cr2 = win->img[win->join_ptr].crystals[j];
- if ( compare_cells(ref, crystal_get_cell(cr2),
- 0.1, deg2rad(5.0),
- &win->mat[sn][win->join_ptr]) ) {
+ if ( compare_reindexed_cell_parameters_and_orientation(ref, crystal_get_cell(cr2),
+ 0.1, deg2rad(5.0),
+ &win->mat[sn][win->join_ptr]) )
+ {
win->ser[sn][win->join_ptr] = j;
cell_free(ref);
return 1;
diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt
index 02f3ca02..192913ce 100644
--- a/tests/CMakeLists.txt
+++ b/tests/CMakeLists.txt
@@ -28,7 +28,7 @@ target_include_directories(centering_check PRIVATE ${COMMON_INCLUDES})
target_link_libraries(centering_check ${COMMON_LIBRARIES})
add_test(centering_check centering_check)
-add_executable(integration_check integration_check.c)
+add_executable(integration_check integration_check.c histogram.c)
target_include_directories(integration_check PRIVATE ${COMMON_INCLUDES})
target_link_libraries(integration_check PRIVATE ${COMMON_LIBRARIES})
if (CURSES_FOUND)
@@ -47,7 +47,7 @@ target_include_directories(prediction_gradient_check PRIVATE ${COMMON_INCLUDES})
target_link_libraries(prediction_gradient_check ${COMMON_LIBRARIES})
add_test(prediction_gradient_check prediction_gradient_check)
-add_executable(prof2d_check prof2d_check.c)
+add_executable(prof2d_check prof2d_check.c histogram.c)
target_include_directories(prof2d_check PRIVATE ${COMMON_INCLUDES})
target_link_libraries(prof2d_check PRIVATE ${COMMON_LIBRARIES})
if (CURSES_FOUND)
@@ -83,3 +83,12 @@ if (HAVE_OPENCL)
add_test(gpu_sim_check gpu_sim_check)
endif (HAVE_OPENCL)
+add_executable(rational_check rational_check.c)
+target_include_directories(rational_check PRIVATE ${COMMON_INCLUDES})
+target_link_libraries(rational_check ${COMMON_LIBRARIES})
+add_test(rational_check rational_check)
+
+add_executable(spectrum_check spectrum_check.c)
+target_include_directories(spectrum_check PRIVATE ${COMMON_INCLUDES})
+target_link_libraries(spectrum_check ${COMMON_LIBRARIES})
+add_test(spectrum_check spectrum_check)
diff --git a/tests/centering_check.c b/tests/centering_check.c
index 887311dd..cc553dd0 100644
--- a/tests/centering_check.c
+++ b/tests/centering_check.c
@@ -61,7 +61,8 @@ static int check_centering(double a, double b, double c,
{
UnitCell *cell, *cref;
UnitCell *n;
- UnitCellTransformation *t;
+ IntegerMatrix *C;
+ RationalMatrix *Ci;
int fail = 0;
int i;
double asx, asy, asz;
@@ -86,14 +87,19 @@ static int check_centering(double a, double b, double c,
cell_free(cref);
check_cell(cell, "Input");
- n = uncenter_cell(cell, &t);
+ n = uncenter_cell(cell, &C, &Ci);
if ( n != NULL ) {
- STATUS("Transformation was:\n");
- tfn_print(t);
+ STATUS("The back transformation is:\n");
+ intmat_print(C);
if ( check_cell(n, "Output") ) fail = 1;
- if ( !fail ) cell_print(n);
+ if ( intmat_is_identity(C) && ((cen=='P') || (cen=='R')) ) {
+ STATUS("Identity, as expected.\n");
+ } else {
+ cell_print(n);
+ }
} else {
- fail = 1;
+ STATUS("************* Could not uncenter.\n");
+ return 1;
}
cell_get_reciprocal(cell, &asx, &asy, &asz,
@@ -299,10 +305,6 @@ int main(int argc, char *argv[])
fail += check_centering(20e-10, 20e-10, 40e-10, 90.0, 90.0, 120.0,
L_HEXAGONAL, 'H', 'c', rng);
- /* Cubic P */
- fail += check_centering(30e-10, 30e-10, 30e-10, 90.0, 90.0, 90.0,
- L_CUBIC, 'P', '*', rng);
-
/* Cubic I */
fail += check_centering(30e-10, 30e-10, 30e-10, 90.0, 90.0, 90.0,
L_CUBIC, 'I', '*', rng);
diff --git a/tests/gpu_sim_check.c b/tests/gpu_sim_check.c
index aee7a068..c864e9c6 100644
--- a/tests/gpu_sim_check.c
+++ b/tests/gpu_sim_check.c
@@ -3,11 +3,11 @@
*
* Check that GPU simulation agrees with CPU version
*
- * Copyright © 2012-2015 Deutsches Elektronen-Synchrotron DESY,
+ * Copyright © 2012-2019 Deutsches Elektronen-Synchrotron DESY,
* a research centre of the Helmholtz Association.
*
* Authors:
- * 2012-2015 Thomas White <taw@physics.org>
+ * 2012-2019 Thomas White <taw@physics.org>
*
* This file is part of CrystFEL.
*
@@ -42,6 +42,7 @@
#include "../src/diffraction.h"
#include "../src/diffraction-gpu.h"
+#include "../src/cl-utils.h"
#include <detector.h>
#include <utils.h>
#include <symmetry.h>
@@ -90,6 +91,11 @@ int main(int argc, char *argv[])
SymOpList *sym;
gsl_rng *rng;
+ if ( have_gpu_device() == 0 ) {
+ ERROR("No GPU device found - skipping test.\n");
+ return 0;
+ }
+
rng = gsl_rng_alloc(gsl_rng_mt19937);
gctx = setup_gpu(1, NULL, NULL, NULL, 0);
@@ -157,21 +163,19 @@ int main(int argc, char *argv[])
gpu_image.det = det;
cpu_image.beam = NULL;
gpu_image.beam = NULL;
- cpu_image.spectrum0 = NULL;
- gpu_image.spectrum0 = NULL;
cpu_image.lambda = ph_en_to_lambda(eV_to_J(6000));
gpu_image.lambda = ph_en_to_lambda(eV_to_J(6000));
cpu_image.bw = 1.0 / 100.0;
gpu_image.bw = 1.0 / 100.0;
- cpu_image.nsamples = 10;
- gpu_image.nsamples = 10;
- cpu_image.spectrum0 = generate_tophat(&cpu_image);
- gpu_image.spectrum0 = generate_tophat(&gpu_image);
+ cpu_image.spectrum = spectrum_generate_tophat(cpu_image.lambda,
+ cpu_image.bw);
+ gpu_image.spectrum = spectrum_generate_tophat(gpu_image.lambda,
+ gpu_image.bw);
start = get_hires_seconds();
- if ( get_diffraction_gpu(gctx, &gpu_image, 8, 8, 8, cell, 0, 0) ) {
+ if ( get_diffraction_gpu(gctx, &gpu_image, 8, 8, 8, cell, 0, 0, 10) ) {
return 1;
}
end = get_hires_seconds();
@@ -195,7 +199,7 @@ int main(int argc, char *argv[])
start = get_hires_seconds();
get_diffraction(&cpu_image, 8, 8, 8, NULL, NULL, NULL, cell,
- GRADIENT_MOSAIC, sym, 0, 0);
+ GRADIENT_MOSAIC, sym, 0, 0, 10);
end = get_hires_seconds();
cpu_time = end - start;
@@ -233,7 +237,7 @@ int main(int argc, char *argv[])
STATUS("CPU: min=%8e, max=%8e, total=%8e\n", cpu_min, cpu_max, cpu_tot);
STATUS("dev = %8e (%5.2f%% of CPU total)\n", dev, perc);
- if ( perc > 1.0 ) {
+ if ( perc > 1.2 ) {
STATUS("Test failed! I'm writing cpu-sim.h5 and gpu-sim.h5"
" for you to inspect.\n");
diff --git a/libcrystfel/src/histogram.c b/tests/histogram.c
index 39f034d6..69acd987 100644
--- a/libcrystfel/src/histogram.c
+++ b/tests/histogram.c
@@ -38,6 +38,8 @@
#include "histogram.h"
+/** \file histogram.h */
+
struct _histogram
{
int n_vals;
diff --git a/libcrystfel/src/histogram.h b/tests/histogram.h
index c3ff27f7..2b9d4928 100644
--- a/libcrystfel/src/histogram.h
+++ b/tests/histogram.h
@@ -37,6 +37,11 @@
extern "C" {
#endif
+/**
+ * \file histogram.h
+ * Quick histogram functions
+ */
+
typedef struct _histogram Histogram;
extern Histogram *histogram_init();
diff --git a/tests/integration_check.c b/tests/integration_check.c
index 65241612..a6ceb2c4 100644
--- a/tests/integration_check.c
+++ b/tests/integration_check.c
@@ -36,7 +36,8 @@
#include <image.h>
#include <utils.h>
-#include <histogram.h>
+
+#include "histogram.h"
#include "../libcrystfel/src/integration.c"
diff --git a/tests/prof2d_check.c b/tests/prof2d_check.c
index 8b9de871..803e60c4 100644
--- a/tests/prof2d_check.c
+++ b/tests/prof2d_check.c
@@ -36,7 +36,8 @@
#include <image.h>
#include <utils.h>
-#include <histogram.h>
+
+#include "histogram.h"
#include "../libcrystfel/src/integration.c"
diff --git a/tests/rational_check.c b/tests/rational_check.c
new file mode 100644
index 00000000..484264d9
--- /dev/null
+++ b/tests/rational_check.c
@@ -0,0 +1,194 @@
+/*
+ * rational_check.c
+ *
+ * Check that rational numbers work
+ *
+ * Copyright © 2012-2019 Deutsches Elektronen-Synchrotron DESY,
+ * a research centre of the Helmholtz Association.
+ *
+ * Authors:
+ * 2012-2019 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 <stdarg.h>
+#include <gsl/gsl_rng.h>
+
+#include <rational.h>
+#include <utils.h>
+
+
+static Rational gen_rtnl(gsl_rng *rng, double *dptr, int sz)
+{
+ Rational r;
+ signed int num, den;
+ do {
+ num = gsl_rng_uniform_int(rng, 2*sz) - sz;
+ den = gsl_rng_uniform_int(rng, 2*sz) - sz;
+ } while ( den == 0 );
+ r = rtnl(num, den);
+ *dptr = (double)num/den;
+ return r;
+}
+
+
+static int test_rational(gsl_rng *rng)
+{
+ Rational r1, r2, r3;
+ double rd1, rd2, rd3;
+ char op;
+
+ r1 = gen_rtnl(rng, &rd1, 100);
+ r2 = gen_rtnl(rng, &rd2, 100);
+
+ switch ( gsl_rng_uniform_int(rng, 3) ) {
+
+ case 0:
+ r3 = rtnl_mul(r1, r2);
+ rd3 = rd1*rd2;
+ op = '*';
+ break;
+
+ case 1:
+ r3 = rtnl_div(r1, r2);
+ rd3 = rd1/rd2;
+ op = '/';
+ break;
+
+ case 2:
+ r3 = rtnl_add(r1, r2);
+ rd3 = rd1+rd2;
+ op = '+';
+ break;
+
+ case 3:
+ r3 = rtnl_sub(r1, r2);
+ rd3 = rd1-rd2;
+ op = '-';
+ break;
+
+ default:
+ abort();
+
+ }
+
+ if ( isinf(rd3) && isinf(rtnl_as_double(r3)) ) return 0;
+
+ if ( fabs(rtnl_as_double(r3) - rd3) > 0.001 ) {
+ ERROR("%10s %c %10s = %10s", rtnl_format(r1), op,
+ rtnl_format(r2), rtnl_format(r3));
+ ERROR(" ---> %10f %c %10f = %10f\n", rd1, op, rd2, rd3);
+ return 1;
+ }
+
+ return 0;
+}
+
+
+static int test_rational_matrix(gsl_rng *rng)
+{
+ const int size = 3;
+ RationalMatrix *rm;
+ Rational *rvec;
+ Rational *rans;
+ gsl_matrix *gm;
+ gsl_vector *gvec;
+ gsl_vector *gans;
+ int i, j;
+ int filt;
+ int fail = 0;
+
+ rm = rtnl_mtx_new(size, size);
+ gm = gsl_matrix_alloc(size, size);
+ rvec = malloc(size*sizeof(Rational));
+ gvec = gsl_vector_alloc(size);
+ rans = malloc(size*sizeof(Rational));
+ if ( (rm==NULL) || (gm==NULL) || (rvec==NULL)
+ || (gvec==NULL) || (rans==NULL) ) return 1;
+
+ /* Find a matrix equation which actually works */
+ do {
+ for ( i=0; i<size; i++ ) {
+ double d;
+ Rational r;
+ for ( j=0; j<size; j++ ) {
+ r = gen_rtnl(rng, &d, 5);
+ rtnl_mtx_set(rm, i, j, r);
+ gsl_matrix_set(gm ,i, j, d);
+ }
+ r = gen_rtnl(rng, &d, 5);
+ rvec[i] = r;
+ gsl_vector_set(gvec ,i, d);
+ }
+
+ gans = solve_svd(gvec, gm, &filt, 0);
+ } while ( (gans==NULL) || (filt!=0) || (isnan(gsl_vector_get(gans,0))) );
+
+ transform_fractional_coords_rtnl(rm, rvec, rans);
+
+ for ( i=0; i<size; i++ ) {
+ if ( fabs(rtnl_as_double(rans[i]) - gsl_vector_get(gans, i) > 0.001) )
+ {
+ STATUS("%25s %10f %10f\n", rtnl_format(rans[i]),
+ rtnl_as_double(rans[i]),
+ gsl_vector_get(gans, i));
+ fail = 1;
+ }
+ }
+
+ gsl_matrix_free(gm);
+ rtnl_mtx_free(rm);
+ free(rans);
+ free(rvec);
+ gsl_vector_free(gvec);
+ gsl_vector_free(gans);
+ return fail;
+}
+
+
+int main(int argc, char *argv[])
+{
+ int fail = 0;
+ gsl_rng *rng;
+ int i;
+
+ rng = gsl_rng_alloc(gsl_rng_mt19937);
+ gsl_set_error_handler_off();
+
+ STATUS("Arithmetic test...\n");
+ for ( i=0; i<1000; i++ ) {
+ fail += test_rational(rng);
+ }
+
+ STATUS("Matrix test...\n");
+ for ( i=0; i<1000; i++ ) {
+ fail += test_rational_matrix(rng);
+ }
+
+ gsl_rng_free(rng);
+
+ if ( fail != 0 ) return 1;
+ return 0;
+}
diff --git a/tests/spectrum_check.c b/tests/spectrum_check.c
new file mode 100644
index 00000000..5ee85dcc
--- /dev/null
+++ b/tests/spectrum_check.c
@@ -0,0 +1,114 @@
+/*
+ * spectrum_check.c
+ *
+ * Check that Spectrum object works
+ *
+ * Copyright © 2019 Deutsches Elektronen-Synchrotron DESY,
+ * a research centre of the Helmholtz Association.
+ *
+ * Authors:
+ * 2019 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 <stdarg.h>
+
+#include <spectrum.h>
+#include <utils.h>
+
+static int check_integral(Spectrum *s, int nsamp)
+{
+ double min, max, step;
+ int i;
+ double area = 0.0;
+
+ spectrum_get_range(s, &min, &max);
+ fprintf(stderr, "bounds: %e %e\n", min, max);
+ step = (max-min)/nsamp;
+ for ( i=0; i<=nsamp; i++ ) {
+ double x = min+i*step;
+ double y = spectrum_get_density_at_k(s, x);
+ area += y * step;
+ }
+ fprintf(stderr, "Total area + %f\n", area);
+ if ( area > 1.1 ) return 1;
+ if ( area < 0.9 ) return 1;
+ return 0;
+}
+
+
+static void plot_spectrum(Spectrum *s)
+{
+ double min, max, step;
+ int i;
+ const int nsamp = 100;
+
+ spectrum_get_range(s, &min, &max);
+ step = (max-min)/nsamp;
+ for ( i=0; i<=nsamp; i++ ) {
+ double x = min+i*step;
+ double y = spectrum_get_density_at_k(s, x);
+ printf("%e %e\n", x, y);
+ }
+}
+
+int main(int argc, char *argv[])
+{
+ Spectrum *s;
+ struct gaussian gauss;
+ gsl_rng *rng;
+ int r = 0;
+
+ rng = gsl_rng_alloc(gsl_rng_mt19937);
+
+ s = spectrum_new();
+ gauss.kcen = ph_eV_to_k(9000);
+ gauss.sigma = ph_eV_to_k(100);
+ gauss.height = 1.0;
+ spectrum_set_gaussians(s, &gauss, 1);
+ r += check_integral(s, 100);
+ spectrum_free(s);
+
+ s = spectrum_generate_sase(ph_eV_to_lambda(9000), 0.01, 0.0001, rng);
+ r += check_integral(s, 100);
+ plot_spectrum(s);
+ spectrum_free(s);
+
+ s = spectrum_generate_gaussian(ph_eV_to_lambda(9000), 0.01);
+ r += check_integral(s, 100);
+ spectrum_free(s);
+
+ s = spectrum_generate_tophat(ph_eV_to_lambda(9000), 0.01);
+ r += check_integral(s, 100);
+ spectrum_free(s);
+
+ s = spectrum_generate_twocolour(ph_eV_to_lambda(9000), 0.001, ph_eV_to_k(100));
+ r += check_integral(s, 100);
+ spectrum_free(s);
+
+ gsl_rng_free(rng);
+
+ return r;
+}
diff --git a/tests/transformation_check.c b/tests/transformation_check.c
index 2e7e7378..33f44340 100644
--- a/tests/transformation_check.c
+++ b/tests/transformation_check.c
@@ -37,10 +37,30 @@
#include <cell.h>
#include <cell-utils.h>
+#include <symmetry.h>
#define MAX_REFLS (10*1024)
+static void intmat_set_all_3x3(IntegerMatrix *m,
+ signed int m11, signed int m12, signed int m13,
+ signed int m21, signed int m22, signed int m23,
+ signed int m31, signed int m32, signed int m33)
+{
+ intmat_set(m, 0, 0, m11);
+ intmat_set(m, 0, 1, m12);
+ intmat_set(m, 0, 2, m13);
+
+ intmat_set(m, 1, 0, m21);
+ intmat_set(m, 1, 1, m22);
+ intmat_set(m, 1, 2, m23);
+
+ intmat_set(m, 2, 0, m31);
+ intmat_set(m, 2, 1, m32);
+ intmat_set(m, 2, 2, m33);
+}
+
+
static struct rvec *all_refls(UnitCell *cell, double max_r, int *n)
{
double asx, asy, asz;
@@ -134,52 +154,61 @@ static int compare_rvecs(struct rvec *a, int na, struct rvec *b, int nb)
}
-static int check_transformation(UnitCell *cell, UnitCellTransformation *tfn,
- int pred_test, UnitCell *ct)
+static int check_same_reflections(UnitCell *cell, UnitCell *cnew)
{
- UnitCell *cnew, *cback;
- UnitCellTransformation *inv;
- double a[9], b[9];
- int i;
- int fail = 0;
struct rvec *vecs;
struct rvec *tvecs;
int na, nb;
- STATUS("-----------------------\n");
- if ( ct == NULL ) {
- cnew = cell_transform(cell, tfn);
+ /* Check that the two cells predict the same reflections */
+ vecs = all_refls(cell, 1e9, &na);
+ tvecs = all_refls(cnew, 1e9, &nb);
+ if ( compare_rvecs(vecs, na, tvecs, nb)
+ || compare_rvecs(tvecs, nb, vecs, na) )
+ {
+ ERROR("********************************************** ");
+ ERROR("Transformed cell didn't predict the same reflections\n");
+ //printf("---\n");
+ //for ( i=0; i<na; i++ ) {
+ // printf("%e %e %e\n", vecs[i].u, vecs[i].v, vecs[i].w);
+ //}
+ //printf("---\n");
+ //for ( i=0; i<nb; i++ ) {
+ // printf("%e %e %e\n", tvecs[i].u, tvecs[i].v, tvecs[i].w);
+ //}
+ return 1;
} else {
- cnew = ct;
+ STATUS("The cells predict the same reflections.\n");
}
- cback = cell_transform_inverse(cnew, tfn);
+ free(vecs);
+ free(tvecs);
+ return 0;
+}
+
+static int check_transformation(UnitCell *cell, IntegerMatrix *tfn,
+ int pred_test)
+{
+ UnitCell *cnew, *cback;
+ double a[9], b[9];
+ int i;
+ int fail = 0;
+
+ STATUS("-----------------------\n");
+ cnew = cell_transform_intmat(cell, tfn);
+ cback = cell_transform_intmat_inverse(cnew, tfn);
+
+ STATUS("----> Before transformation:\n");
cell_print(cell);
- tfn_print(tfn);
+ STATUS("----> The transformation matrix:\n");
+ intmat_print(tfn);
+ STATUS("----> After transformation:\n");
cell_print(cnew);
+ STATUS("----> After back transformation:\n");
+ cell_print(cback);
if ( pred_test ) {
- /* Check that the two cells predict the same reflections */
- vecs = all_refls(cell, 1e9, &na);
- tvecs = all_refls(cnew, 1e9, &nb);
- if ( compare_rvecs(vecs, na, tvecs, nb)
- || compare_rvecs(tvecs, nb, vecs, na) )
- {
- ERROR("Transformed cell didn't predict the same reflections\n");
- //printf("---\n");
- //for ( i=0; i<na; i++ ) {
- // printf("%e %e %e\n", vecs[i].u, vecs[i].v, vecs[i].w);
- //}
- //printf("---\n");
- //for ( i=0; i<nb; i++ ) {
- // printf("%e %e %e\n", tvecs[i].u, tvecs[i].v, tvecs[i].w);
- //}
- return 1;
- } else {
- STATUS("The cells predict the same reflections.\n");
- }
- free(vecs);
- free(tvecs);
+ check_same_reflections(cell, cnew);
} else {
STATUS("Cells not expected to predict the same reflections.\n");
}
@@ -193,18 +222,18 @@ static int check_transformation(UnitCell *cell, UnitCellTransformation *tfn,
&b[6], &b[7], &b[8]);
for ( i=0; i<9; i++ ) {
if ( !tolerance(a[i], b[i]) ) {
+ //STATUS("%e %e\n", a[i], b[i]);
fail = 1;
- STATUS("%e %e\n", a[i], b[i]);
}
}
if ( fail ) {
+ ERROR("********************************************** ");
ERROR("Original cell not recovered after transformation:\n");
- cell_print(cell);
- tfn_print(tfn);
- inv = tfn_inverse(tfn);
- tfn_print(inv);
+ STATUS("----> After transformation and transformation back:\n");
cell_print(cback);
+ } else {
+ STATUS("The original cell was recovered after inverse transform.\n");
}
return fail;
@@ -214,21 +243,73 @@ static int check_transformation(UnitCell *cell, UnitCellTransformation *tfn,
static int check_uncentering(UnitCell *cell)
{
UnitCell *ct;
- UnitCellTransformation *tr;
+ IntegerMatrix *C;
+ RationalMatrix *Ci;
+ UnitCell *cback;
+ double a[9], b[9];
+ int i;
+ int fail = 0;
+
+ STATUS("-----------------------\n");
- ct = uncenter_cell(cell, &tr);
- return check_transformation(cell, tr, 1, ct);
+ STATUS("----> Before transformation:\n");
+ cell_print_full(cell);
+
+ ct = uncenter_cell(cell, &C, &Ci);
+ if ( ct == NULL ) return 1;
+
+ STATUS("----> The primitive unit cell:\n");
+ cell_print(ct);
+
+ STATUS("----> The matrix to put the centering back:\n");
+ intmat_print(C);
+
+ STATUS("----> The recovered centered cell:\n");
+ cback = cell_transform_intmat(ct, C);
+ cell_print(cback);
+
+ cell_get_cartesian(cell, &a[0], &a[1], &a[2],
+ &a[3], &a[4], &a[5],
+ &a[6], &a[7], &a[8]);
+ cell_get_cartesian(cback, &b[0], &b[1], &b[2],
+ &b[3], &b[4], &b[5],
+ &b[6], &b[7], &b[8]);
+ for ( i=0; i<9; i++ ) {
+ if ( fabs(a[i] - b[i]) > 1e-12 ) {
+ fail = 1;
+ }
+ }
+
+ if ( fail ) {
+ ERROR("********************************************** ");
+ ERROR("Original cell not recovered after back transformation\n");
+ }
+
+ fail += check_same_reflections(cell, ct);
+ cell_free(ct);
+ cell_free(cback);
+
+ return fail;
}
-static int check_identity(UnitCell *cell, UnitCellTransformation *tfn)
+static int check_identity(UnitCell *cell, IntegerMatrix *tfn)
{
UnitCell *cnew;
double a[9], b[9];
int i;
int fail = 0;
- cnew = cell_transform(cell, tfn);
+ STATUS("-----------------------\n");
+
+ cnew = cell_transform_intmat(cell, tfn);
+
+ STATUS("----> Before identity transformation:\n");
+ cell_print(cell);
+ STATUS("----> The identity transformation matrix:\n");
+ intmat_print(tfn);
+ STATUS("----> After identity transformation:\n");
+ cell_print(cnew);
cell_get_cartesian(cell, &a[0], &a[1], &a[2],
&a[3], &a[4], &a[5],
@@ -239,14 +320,15 @@ static int check_identity(UnitCell *cell, UnitCellTransformation *tfn)
for ( i=0; i<9; i++ ) {
if ( !within_tolerance(a[i], b[i], 0.1) ) {
fail = 1;
- STATUS("%e %e\n", a[i], b[i]);
+ //STATUS("%e %e\n", a[i], b[i]);
}
}
if ( fail ) {
- ERROR("Original cell not recovered after transformation:\n");
+ ERROR("********************************************** ");
+ ERROR("Original cell not recovered after identity transformation:\n");
cell_print(cell);
- tfn_print(tfn);
+ intmat_print(tfn);
cell_print(cnew);
}
@@ -258,7 +340,8 @@ int main(int argc, char *argv[])
{
int fail = 0;
UnitCell *cell, *cref;
- UnitCellTransformation *tfn;
+ IntegerMatrix *tfn;
+ IntegerMatrix *part1, *part2;
gsl_rng *rng;
rng = gsl_rng_alloc(gsl_rng_mt19937);
@@ -273,53 +356,52 @@ int main(int argc, char *argv[])
if ( cell == NULL ) return 1;
cell_free(cref);
+ tfn = intmat_identity(3);
+
/* Permutation of axes */
- tfn = tfn_identity();
if ( tfn == NULL ) return 1;
- tfn_combine(tfn, tfn_vector(0,1,0),
- tfn_vector(0,0,1),
- tfn_vector(1,0,0));
- fail += check_transformation(cell, tfn, 1, NULL);
- tfn_free(tfn);
+ intmat_set_all_3x3(tfn, 0,0,1,
+ 1,0,0,
+ 0,1,0);
+ fail += check_transformation(cell, tfn, 1);
/* Doubling of cell in one direction */
- tfn = tfn_identity();
if ( tfn == NULL ) return 1;
- tfn_combine(tfn, tfn_vector(2,0,0),
- tfn_vector(0,1,0),
- tfn_vector(0,0,1));
- fail += check_transformation(cell, tfn, 0, NULL);
- tfn_free(tfn);
-
- /* Diagonal supercell */
- tfn = tfn_identity();
+ intmat_set_all_3x3(tfn, 2,0,0,
+ 0,1,0,
+ 0,0,1);
+ fail += check_transformation(cell, tfn, 0);
+
+ /* Shearing */
if ( tfn == NULL ) return 1;
- tfn_combine(tfn, tfn_vector(1,1,0),
- tfn_vector(0,1,0),
- tfn_vector(0,0,1));
- fail += check_transformation(cell, tfn, 1, NULL);
- tfn_free(tfn);
+ intmat_set_all_3x3(tfn, 1,0,0,
+ 1,1,0,
+ 0,0,1);
+ fail += check_transformation(cell, tfn, 1);
/* Crazy */
- tfn = tfn_identity();
if ( tfn == NULL ) return 1;
- tfn_combine(tfn, tfn_vector(1,1,0),
- tfn_vector(0,1,0),
- tfn_vector(0,1,1));
- fail += check_transformation(cell, tfn, 0, NULL);
- tfn_free(tfn);
+ intmat_set_all_3x3(tfn, 1,0,0,
+ 1,1,1,
+ 0,0,1);
+ fail += check_transformation(cell, tfn, 0);
/* Identity in two parts */
- tfn = tfn_identity();
+ part1 = intmat_identity(3);
+ part2 = intmat_identity(3);
if ( tfn == NULL ) return 1;
- tfn_combine(tfn, tfn_vector(0,0,1),
- tfn_vector(0,1,0),
- tfn_vector(-1,0,0));
- tfn_combine(tfn, tfn_vector(0,0,-1),
- tfn_vector(0,1,0),
- tfn_vector(1,0,0));
+ intmat_set_all_3x3(part1, 0,0,-1,
+ 0,1,0,
+ 1,0,0);
+ intmat_set_all_3x3(part2, 0,0,1,
+ 0,1,0,
+ -1,0,0);
+ tfn = intmat_intmat_mult(part1, part2);
fail += check_identity(cell, tfn);
- tfn_free(tfn);
+ intmat_free(part1);
+ intmat_free(part2);
+
+ intmat_free(tfn);
/* Check some uncentering transformations */
cref = cell_new_from_parameters(50e-10, 50e-10, 50e-10,