diff options
Diffstat (limited to 'libcrystfel')
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, ¢ering, 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" |