aboutsummaryrefslogtreecommitdiff
path: root/libcrystfel
diff options
context:
space:
mode:
Diffstat (limited to 'libcrystfel')
-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/histogram.c278
-rw-r--r--libcrystfel/src/histogram.h59
-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
72 files changed, 4906 insertions, 3415 deletions
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/histogram.c b/libcrystfel/src/histogram.c
deleted file mode 100644
index 39f034d6..00000000
--- a/libcrystfel/src/histogram.c
+++ /dev/null
@@ -1,278 +0,0 @@
-/*
- * histogram.c
- *
- * Quick histogram functions
- *
- * Copyright © 2013-2014 Deutsches Elektronen-Synchrotron DESY,
- * a research centre of the Helmholtz Association.
- *
- * Authors:
- * 2013-2014 Thomas White <taw@physics.org>
- *
- * This file is part of CrystFEL.
- *
- * CrystFEL is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * CrystFEL is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with CrystFEL. If not, see <http://www.gnu.org/licenses/>.
- *
- */
-
-#ifdef HAVE_CONFIG_H
-#include <config.h>
-#endif
-
-#include <stdlib.h>
-#include <stdio.h>
-#include <math.h>
-#include <string.h>
-#include <assert.h>
-
-#include "histogram.h"
-
-struct _histogram
-{
- int n_vals;
- int max_vals;
- double *vals;
-
- double min;
- double max;
-
- int full;
-
- int n_bins;
- double bin_width;
-
- int *bins;
-
- int plot_height;
- int peak;
- double mean;
- double stddev;
-};
-
-
-Histogram *histogram_init()
-{
- Histogram *hi;
-
- hi = malloc(sizeof(struct _histogram));
- if ( hi == NULL ) return NULL;
-
- hi->n_vals = 0;
- hi->max_vals = 0;
- hi->vals = NULL;
- hi->full = 0;
-
- hi->max = -INFINITY;
- hi->min = INFINITY;
-
- hi->bins = NULL;
- hi->n_bins = 50;
-
- hi->plot_height = 10;
-
- return hi;
-}
-
-
-void histogram_free(Histogram *hi)
-{
- free(hi);
-}
-
-
-void histogram_add_value(Histogram *hi, double val)
-{
- if ( hi->n_vals == hi->max_vals ) {
- double *new_vals;
- new_vals = realloc(hi->vals, (hi->max_vals+512)*sizeof(double));
- if ( new_vals == NULL ) {
- hi->full = 1;
- fprintf(stderr, "Histogram %p full.\n", hi);
- return;
- }
- hi->max_vals += 512;
- hi->vals = new_vals;
- }
-
- hi->vals[hi->n_vals++] = val;
-
- if ( val > hi->max ) hi->max = val;
- if ( val < hi->min ) hi->min = val;
-}
-
-
-static void calc_bins(Histogram *hi)
-{
- int i;
-
- if ( hi->bins != NULL ) {
- free(hi->bins);
- }
-
- hi->bins = calloc(hi->n_bins, sizeof(int));
- if ( hi->bins == NULL ) {
- fprintf(stderr, "Failed to allocate bins.\n");
- return;
- }
-
- hi->bin_width = (hi->max - hi->min)/hi->n_bins;
-
- for ( i=0; i<hi->n_vals; i++ ) {
-
- int j;
-
- j = (hi->vals[i] - hi->min) / hi->bin_width;
-
- /* Tidy up rounding errors */
- if ( j < 0 ) j = 0;
- if ( j >= hi->n_bins ) j = hi->n_bins - 1;
-
- hi->bins[j]++;
-
- }
-
- hi->peak = 0;
- for ( i=0; i<hi->n_bins; i++ ) {
- if ( hi->bins[i] > hi->peak ) hi->peak = hi->bins[i];
- }
-}
-
-
-static void calc_stddev(Histogram *hi)
-{
- double sum = 0.0;
- int i;
-
- for ( i=0; i<hi->n_vals; i++ ) {
- sum += hi->vals[i];
- }
- hi->mean = sum / hi->n_vals;
-
- sum = 0.0;
- for ( i=0; i<hi->n_vals; i++ ) {
- sum += pow(hi->vals[i] - hi->mean, 2.0);
- }
- hi->stddev = sqrt(sum / hi->n_vals);
-}
-
-
-int *histogram_get_data(Histogram *hi, int *n)
-{
- calc_bins(hi);
- *n = hi->n_bins;
- return hi->bins;
-}
-
-
-void histogram_show(Histogram *hi)
-{
- int i;
- int *bins;
- double lines_per_count;
- char tmp[32];
- size_t n_left, n_mid, n_right;
-
- calc_bins(hi);
- calc_stddev(hi);
-
- bins = malloc(hi->n_bins*sizeof(int));
- if ( bins == NULL ) {
- fprintf(stderr, "Couldn't scale bins\n");
- return;
- }
-
- lines_per_count = (double)hi->plot_height / hi->peak;
-
- for ( i=0; i<hi->n_bins; i++ ) {
- bins[i] = hi->bins[i] * lines_per_count;
- }
-
- for ( i=hi->plot_height-1; i>=0; i-- ) {
-
- int j;
-
- for ( j=0; j<hi->n_bins; j++ ) {
- if ( bins[j] > i ) {
- printf("*");
- } else {
- printf(" ");
- }
- }
- printf("\n");
-
- }
-
- printf("|");
- for ( i=1; i<hi->n_bins-1; i++ ) {
- double bin_low, bin_high;
- bin_low = hi->min + i*hi->bin_width;
- bin_high = hi->min + (i+1)*hi->bin_width;
- if ( (bin_low < 0.0) && (bin_high > 0.0) ) {
- printf("+");
- } else {
- printf("-");
- }
- }
- printf("|\n");
-
- snprintf(tmp, 31, "%.2f", hi->min);
- n_left = strlen(tmp);
- snprintf(tmp, 31, "%.2f", hi->max);
- n_right = strlen(tmp);
- n_mid = hi->n_bins - (n_left + n_right);
-
- printf("%.2f", hi->min);
- for ( i=0; i<n_mid; i++ ) printf(" ");
- printf("%.2f\n", hi->max);
-
- printf("Mean = %.2f, Std dev = %.2f\n", hi->mean, hi->stddev);
-
- free(bins);
-}
-
-
-double histogram_get_min(Histogram *hi)
-{
- return hi->min;
-}
-
-
-double histogram_get_max(Histogram *hi)
-{
- return hi->max;
-}
-
-
-int histogram_get_num_bins(Histogram *hi)
-{
- return hi->n_bins;
-}
-
-
-void histogram_set_min(Histogram *hi, double min)
-{
- hi->min = min;
-}
-
-
-void histogram_set_max(Histogram *hi, double max)
-{
- hi->max = max;
-}
-
-
-void histogram_set_num_bins(Histogram *hi, int n)
-{
- hi->n_bins = n;
-}
diff --git a/libcrystfel/src/histogram.h b/libcrystfel/src/histogram.h
deleted file mode 100644
index c3ff27f7..00000000
--- a/libcrystfel/src/histogram.h
+++ /dev/null
@@ -1,59 +0,0 @@
-/*
- * histogram.h
- *
- * Quick histogram functions
- *
- * Copyright © 2013-2014 Deutsches Elektronen-Synchrotron DESY,
- * a research centre of the Helmholtz Association.
- *
- * Authors:
- * 2013-2014 Thomas White <taw@physics.org>
- *
- * This file is part of CrystFEL.
- *
- * CrystFEL is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * CrystFEL is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with CrystFEL. If not, see <http://www.gnu.org/licenses/>.
- *
- */
-
-#ifndef HISTOGRAM_H
-#define HISTOGRAM_H
-
-#ifdef HAVE_CONFIG_H
-#include <config.h>
-#endif
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-typedef struct _histogram Histogram;
-
-extern Histogram *histogram_init();
-extern void histogram_free(Histogram *hi);
-extern void histogram_add_value(Histogram *hi, double val);
-extern void histogram_show(Histogram *hi);
-
-extern int *histogram_get_data(Histogram *hi, int *n);
-extern double histogram_get_min(Histogram *hi);
-extern double histogram_get_max(Histogram *hi);
-extern int histogram_get_num_bins(Histogram *hi);
-extern void histogram_set_min(Histogram *hi, double min);
-extern void histogram_set_max(Histogram *hi, double max);
-extern void histogram_set_num_bins(Histogram *hi, int n);
-
-#ifdef __cplusplus
-}
-#endif
-
-#endif /* HISTOGRAM_H */
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"