diff options
Diffstat (limited to 'libcrystfel')
86 files changed, 12817 insertions, 9566 deletions
diff --git a/libcrystfel/CMakeLists.txt b/libcrystfel/CMakeLists.txt index b0f29543..f92e8be2 100644 --- a/libcrystfel/CMakeLists.txt +++ b/libcrystfel/CMakeLists.txt @@ -1,15 +1,17 @@ project(libcrystfel VERSION ${CRYSTFEL_SHORT_VERSION} LANGUAGES C) find_package(Curses) -find_package(XGANDALF) -find_package(PINKINDEXER) -find_package(FDIP) +pkg_check_modules (XGANDALF xgandalf) +pkg_check_modules (PINKINDEXER pinkIndexer) +pkg_check_modules (FDIP fdip) find_package(ZLIB REQUIRED) find_package(FLEX REQUIRED) find_package(BISON REQUIRED) find_package(Doxygen) pkg_search_module(FFTW fftw3) +configure_file(src/libcrystfel-version.c.cmake.in libcrystfel-version.c) + set(HAVE_CURSES ${CURSES_FOUND}) set(HAVE_FFTW ${FFTW_FOUND}) set(HAVE_XGANDALF ${XGANDALF_FOUND}) @@ -21,19 +23,6 @@ set(CMAKE_REQUIRED_LIBRARIES "-lz") check_symbol_exists(gzbuffer "zlib.h" HAVE_GZBUFFER) unset(CMAKE_REQUIRED_LIBRARIES) -# Find out where forkpty() is declared -set(CMAKE_REQUIRED_LIBRARIES "-lutil") -check_symbol_exists(forkpty "pty.h" HAVE_FORKPTY_PTY_H) -check_symbol_exists(forkpty "util.h" HAVE_FORKPTY_UTIL_H) -unset(CMAKE_REQUIRED_LIBRARIES) -if(HAVE_FORKPTY_PTY_H) - message(STATUS "Found forkpty() in pty.h") -elseif(HAVE_FORKPTY_UTIL_H) - message(STATUS "Found forkpty() in util.h") -else() - message(SEND_ERROR "Couldn't find forkpty()") -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) @@ -46,10 +35,8 @@ set(LIBCRYSTFEL_SOURCES src/reflist.c src/utils.c src/cell.c - src/detector.c src/thread-pool.c src/image.c - src/hdf5-file.c src/geometry.c src/peakfinder8.c src/symmetry.c @@ -57,70 +44,68 @@ set(LIBCRYSTFEL_SOURCES src/peaks.c src/reflist-utils.c src/filters.c - src/render.c src/index.c - src/dirax.c - src/mosflm.c src/cell-utils.c src/integer_matrix.c src/crystal.c - src/xds.c src/integration.c src/predict-refine.c - src/events.c - src/felix.c src/peakfinder8.c - src/taketwo.c - src/xgandalf.c - src/pinkindexer.c src/rational.c src/spectrum.c + src/datatemplate.c + src/colscale.c + src/detgeom.c + src/image-cbf.c + src/image-hdf5.c + src/fom.c src/fromfile.c ${BISON_symopp_OUTPUTS} ${FLEX_symopl_OUTPUTS} + src/indexers/dirax.c + src/indexers/mosflm.c + src/indexers/xds.c + src/indexers/felix.c + src/indexers/taketwo.c + src/indexers/asdf.c + src/indexers/xgandalf.c + src/indexers/pinkindexer.c ) -if (HAVE_FFTW) - set(LIBCRYSTFEL_FFTW_SOURCES src/asdf.c) -endif (HAVE_FFTW) - set(LIBCRYSTFEL_HEADERS - src/hdf5-file.h src/reflist.h src/symmetry.h src/cell.h src/reflist-utils.h src/thread-pool.h src/utils.h - src/detector.h src/geometry.h src/peakfinder8.h src/peaks.h src/stream.h - src/render.h src/index.h src/image.h src/filters.h - src/dirax.h - src/mosflm.h src/cell-utils.h src/integer_matrix.h src/crystal.h - src/xds.h src/predict-refine.h src/integration.h - src/events.h - src/asdf.h - src/felix.h src/peakfinder8.h - src/taketwo.h - src/xgandalf.h - src/pinkindexer.h src/rational.h src/spectrum.h + src/datatemplate.h + src/colscale.h + src/detgeom.h + src/image-msgpack.h + src/fom.h src/fromfile.h ) +if (MSGPACK_FOUND) + set(LIBCRYSTFEL_SOURCES ${LIBCRYSTFEL_SOURCES} src/image-msgpack.c) +endif (MSGPACK_FOUND) + if (DOXYGEN_FOUND) configure_file(${PROJECT_SOURCE_DIR}/doc/index.md index.md) set(DOXYGEN_SHOW_INCLUDE_FILES NO) @@ -133,6 +118,7 @@ endif (DOXYGEN_FOUND) add_library(${PROJECT_NAME} SHARED ${LIBCRYSTFEL_SOURCES} + ${CMAKE_CURRENT_BINARY_DIR}/libcrystfel-version.c ${LIBCRYSTFEL_FFTW_SOURCES} ${LIBCRYSTFEL_HEADERS}) @@ -153,18 +139,18 @@ target_link_libraries(${PROJECT_NAME} PRIVATE util ${HDF5_C_LIBRARIES} ${ZLIB_LI Threads::Threads GSL::gsl m) if (XGANDALF_FOUND) - target_include_directories(${PROJECT_NAME} PRIVATE ${XGANDALF_INCLUDES}) - target_link_libraries(${PROJECT_NAME} PRIVATE ${XGANDALF_LIBRARIES}) + target_include_directories(${PROJECT_NAME} PRIVATE ${XGANDALF_INCLUDE_DIRS}) + target_link_libraries(${PROJECT_NAME} PRIVATE ${XGANDALF_LDFLAGS}) endif (XGANDALF_FOUND) if (FDIP_FOUND) - target_include_directories(${PROJECT_NAME} PRIVATE ${FDIP_INCLUDES}) - target_link_libraries(${PROJECT_NAME} PRIVATE ${FDIP_LIBRARIES}) + target_include_directories(${PROJECT_NAME} PRIVATE ${FDIP_INCLUDE_DIRS}) + target_link_libraries(${PROJECT_NAME} PRIVATE ${FDIP_LDFLAGS}) endif (FDIP_FOUND) if (PINKINDEXER_FOUND) - target_include_directories(${PROJECT_NAME} PRIVATE ${PINKINDEXER_INCLUDES}) - target_link_libraries(${PROJECT_NAME} PRIVATE ${PINKINDEXER_LIBRARIES}) + target_include_directories(${PROJECT_NAME} PRIVATE ${PINKINDEXER_INCLUDE_DIRS}) + target_link_libraries(${PROJECT_NAME} PRIVATE ${PINKINDEXER_LDFLAGS}) endif (PINKINDEXER_FOUND) if (FFTW_FOUND) diff --git a/libcrystfel/config.h.cmake.in b/libcrystfel/config.h.cmake.in index 731e6fe0..7f0dcd94 100644 --- a/libcrystfel/config.h.cmake.in +++ b/libcrystfel/config.h.cmake.in @@ -3,11 +3,11 @@ #cmakedefine HAVE_CLOCK_GETTIME #cmakedefine HAVE_FFTW #cmakedefine HAVE_XGANDALF -#cmakedefine HAVE_NBP #cmakedefine HAVE_PINKINDEXER #cmakedefine HAVE_FDIP #cmakedefine HAVE_CURSES #cmakedefine HAVE_GZBUFFER +#cmakedefine HAVE_GDKPIXBUF #cmakedefine HAVE_FORKPTY_PTY_H #cmakedefine HAVE_FORKPTY_UTIL_H diff --git a/libcrystfel/config.h.in b/libcrystfel/config.h.in new file mode 100644 index 00000000..1fdd2e13 --- /dev/null +++ b/libcrystfel/config.h.in @@ -0,0 +1,13 @@ +/* config.h for libcrystfel */ + +#mesondefine HAVE_FFTW +#mesondefine HAVE_XGANDALF +#mesondefine HAVE_PINKINDEXER +#mesondefine HAVE_FDIP +#mesondefine HAVE_CURSES +#mesondefine HAVE_GZBUFFER + +#mesondefine HAVE_FORKPTY_PTY_H +#mesondefine HAVE_FORKPTY_UTIL_H + +#define CRYSTFEL_VERSIONSTRING "${CRYSTFEL_VERSION}" diff --git a/libcrystfel/doc/Doxyfile.in b/libcrystfel/doc/Doxyfile.in new file mode 100644 index 00000000..b3d42b18 --- /dev/null +++ b/libcrystfel/doc/Doxyfile.in @@ -0,0 +1,2536 @@ +# Doxyfile 1.8.15 + +# This file describes the settings to be used by the documentation system +# doxygen (www.doxygen.org) for a project. +# +# All text after a double hash (##) is considered a comment and is placed in +# front of the TAG it is preceding. +# +# All text after a single hash (#) is considered a comment and will be ignored. +# The format is: +# TAG = value [value, ...] +# For lists, items can also be appended using: +# TAG += value [value, ...] +# Values that contain spaces should be placed between quotes (\" \"). + +#--------------------------------------------------------------------------- +# Project related configuration options +#--------------------------------------------------------------------------- + +# This tag specifies the encoding used for all characters in the configuration +# file that follow. The default is UTF-8 which is also the encoding used for all +# text before the first occurrence of this tag. Doxygen uses libiconv (or the +# iconv built into libc) for the transcoding. See +# https://www.gnu.org/software/libiconv/ for the list of possible encodings. +# The default value is: UTF-8. + +DOXYFILE_ENCODING = UTF-8 + +# The PROJECT_NAME tag is a single word (or a sequence of words surrounded by +# double-quotes, unless you are using Doxywizard) that should identify the +# project for which the documentation is generated. This name is used in the +# title of most generated pages and in a few other places. +# The default value is: My Project. + +PROJECT_NAME = "libcrystfel" + +# The PROJECT_NUMBER tag can be used to enter a project or revision number. This +# could be handy for archiving the generated documentation or if some version +# control system is used. + +PROJECT_NUMBER = @VERSION@ + +# Using the PROJECT_BRIEF tag one can provide an optional one line description +# for a project that appears at the top of each page and should give viewer a +# quick idea about the purpose of the project. Keep the description short. + +PROJECT_BRIEF = + +# With the PROJECT_LOGO tag one can specify a logo or an icon that is included +# in the documentation. The maximum height of the logo should not exceed 55 +# pixels and the maximum width should not exceed 200 pixels. Doxygen will copy +# the logo to the output directory. + +PROJECT_LOGO = + +# The OUTPUT_DIRECTORY tag is used to specify the (relative or absolute) path +# into which the generated documentation will be written. If a relative path is +# entered, it will be relative to the location where doxygen was started. If +# left blank the current directory will be used. + +OUTPUT_DIRECTORY = "@OUTPUT_DIR@" + +# If the CREATE_SUBDIRS tag is set to YES then doxygen will create 4096 sub- +# directories (in 2 levels) under the output directory of each output format and +# will distribute the generated files over these directories. Enabling this +# option can be useful when feeding doxygen a huge amount of source files, where +# putting all generated files in the same directory would otherwise causes +# performance problems for the file system. +# The default value is: NO. + +CREATE_SUBDIRS = NO + +# If the ALLOW_UNICODE_NAMES tag is set to YES, doxygen will allow non-ASCII +# characters to appear in the names of generated files. If set to NO, non-ASCII +# characters will be escaped, for example _xE3_x81_x84 will be used for Unicode +# U+3044. +# The default value is: NO. + +ALLOW_UNICODE_NAMES = NO + +# The OUTPUT_LANGUAGE tag is used to specify the language in which all +# documentation generated by doxygen is written. Doxygen will use this +# information to generate all constant output in the proper language. +# Possible values are: Afrikaans, Arabic, Armenian, Brazilian, Catalan, Chinese, +# Chinese-Traditional, Croatian, Czech, Danish, Dutch, English (United States), +# Esperanto, Farsi (Persian), Finnish, French, German, Greek, Hungarian, +# Indonesian, Italian, Japanese, Japanese-en (Japanese with English messages), +# Korean, Korean-en (Korean with English messages), Latvian, Lithuanian, +# Macedonian, Norwegian, Persian (Farsi), Polish, Portuguese, Romanian, Russian, +# Serbian, Serbian-Cyrillic, Slovak, Slovene, Spanish, Swedish, Turkish, +# Ukrainian and Vietnamese. +# The default value is: English. + +OUTPUT_LANGUAGE = English + +# The OUTPUT_TEXT_DIRECTION tag is used to specify the direction in which all +# documentation generated by doxygen is written. Doxygen will use this +# information to generate all generated output in the proper direction. +# Possible values are: None, LTR, RTL and Context. +# The default value is: None. + +OUTPUT_TEXT_DIRECTION = None + +# If the BRIEF_MEMBER_DESC tag is set to YES, doxygen will include brief member +# descriptions after the members that are listed in the file and class +# documentation (similar to Javadoc). Set to NO to disable this. +# The default value is: YES. + +BRIEF_MEMBER_DESC = YES + +# If the REPEAT_BRIEF tag is set to YES, doxygen will prepend the brief +# description of a member or function before the detailed description +# +# Note: If both HIDE_UNDOC_MEMBERS and BRIEF_MEMBER_DESC are set to NO, the +# brief descriptions will be completely suppressed. +# The default value is: YES. + +REPEAT_BRIEF = YES + +# This tag implements a quasi-intelligent brief description abbreviator that is +# used to form the text in various listings. Each string in this list, if found +# as the leading text of the brief description, will be stripped from the text +# and the result, after processing the whole list, is used as the annotated +# text. Otherwise, the brief description is used as-is. If left blank, the +# following values are used ($name is automatically replaced with the name of +# the entity):The $name class, The $name widget, The $name file, is, provides, +# specifies, contains, represents, a, an and the. + +ABBREVIATE_BRIEF = "The $name class" \ + "The $name widget" \ + "The $name file" \ + is \ + provides \ + specifies \ + contains \ + represents \ + a \ + an \ + the + +# If the ALWAYS_DETAILED_SEC and REPEAT_BRIEF tags are both set to YES then +# doxygen will generate a detailed section even if there is only a brief +# description. +# The default value is: NO. + +ALWAYS_DETAILED_SEC = NO + +# If the INLINE_INHERITED_MEMB tag is set to YES, doxygen will show all +# inherited members of a class in the documentation of that class as if those +# members were ordinary class members. Constructors, destructors and assignment +# operators of the base classes will not be shown. +# The default value is: NO. + +INLINE_INHERITED_MEMB = NO + +# If the FULL_PATH_NAMES tag is set to YES, doxygen will prepend the full path +# before files name in the file list and in the header files. If set to NO the +# shortest path that makes the file name unique will be used +# The default value is: YES. + +FULL_PATH_NAMES = YES + +# The STRIP_FROM_PATH tag can be used to strip a user-defined part of the path. +# Stripping is only done if one of the specified strings matches the left-hand +# part of the path. The tag can be used to show relative paths in the file list. +# If left blank the directory from which doxygen is run is used as the path to +# strip. +# +# Note that you can specify absolute paths here, but also relative paths, which +# will be relative from the directory where doxygen is started. +# This tag requires that the tag FULL_PATH_NAMES is set to YES. + +STRIP_FROM_PATH = + +# The STRIP_FROM_INC_PATH tag can be used to strip a user-defined part of the +# path mentioned in the documentation of a class, which tells the reader which +# header file to include in order to use a class. If left blank only the name of +# the header file containing the class definition is used. Otherwise one should +# specify the list of include paths that are normally passed to the compiler +# using the -I flag. + +STRIP_FROM_INC_PATH = + +# If the SHORT_NAMES tag is set to YES, doxygen will generate much shorter (but +# less readable) file names. This can be useful is your file systems doesn't +# support long names like on DOS, Mac, or CD-ROM. +# The default value is: NO. + +SHORT_NAMES = NO + +# If the JAVADOC_AUTOBRIEF tag is set to YES then doxygen will interpret the +# first line (until the first dot) of a Javadoc-style comment as the brief +# description. If set to NO, the Javadoc-style will behave just like regular Qt- +# style comments (thus requiring an explicit @brief command for a brief +# description.) +# The default value is: NO. + +JAVADOC_AUTOBRIEF = NO + +# If the QT_AUTOBRIEF tag is set to YES then doxygen will interpret the first +# line (until the first dot) of a Qt-style comment as the brief description. If +# set to NO, the Qt-style will behave just like regular Qt-style comments (thus +# requiring an explicit \brief command for a brief description.) +# The default value is: NO. + +QT_AUTOBRIEF = NO + +# The MULTILINE_CPP_IS_BRIEF tag can be set to YES to make doxygen treat a +# multi-line C++ special comment block (i.e. a block of //! or /// comments) as +# a brief description. This used to be the default behavior. The new default is +# to treat a multi-line C++ comment block as a detailed description. Set this +# tag to YES if you prefer the old behavior instead. +# +# Note that setting this tag to YES also means that rational rose comments are +# not recognized any more. +# The default value is: NO. + +MULTILINE_CPP_IS_BRIEF = NO + +# If the INHERIT_DOCS tag is set to YES then an undocumented member inherits the +# documentation from any documented member that it re-implements. +# The default value is: YES. + +INHERIT_DOCS = YES + +# If the SEPARATE_MEMBER_PAGES tag is set to YES then doxygen will produce a new +# page for each member. If set to NO, the documentation of a member will be part +# of the file/class/namespace that contains it. +# The default value is: NO. + +SEPARATE_MEMBER_PAGES = NO + +# The TAB_SIZE tag can be used to set the number of spaces in a tab. Doxygen +# uses this value to replace tabs by spaces in code fragments. +# Minimum value: 1, maximum value: 16, default value: 4. + +TAB_SIZE = 4 + +# This tag can be used to specify a number of aliases that act as commands in +# the documentation. An alias has the form: +# name=value +# For example adding +# "sideeffect=@par Side Effects:\n" +# will allow you to put the command \sideeffect (or @sideeffect) in the +# documentation, which will result in a user-defined paragraph with heading +# "Side Effects:". You can put \n's in the value part of an alias to insert +# newlines (in the resulting output). You can put ^^ in the value part of an +# alias to insert a newline as if a physical newline was in the original file. +# When you need a literal { or } or , in the value part of an alias you have to +# escape them by means of a backslash (\), this can lead to conflicts with the +# commands \{ and \} for these it is advised to use the version @{ and @} or use +# a double escape (\\{ and \\}) + +ALIASES = + +# This tag can be used to specify a number of word-keyword mappings (TCL only). +# A mapping has the form "name=value". For example adding "class=itcl::class" +# will allow you to use the command class in the itcl::class meaning. + +TCL_SUBST = + +# Set the OPTIMIZE_OUTPUT_FOR_C tag to YES if your project consists of C sources +# only. Doxygen will then generate output that is more tailored for C. For +# instance, some of the names that are used will be different. The list of all +# members will be omitted, etc. +# The default value is: NO. + +OPTIMIZE_OUTPUT_FOR_C = NO + +# Set the OPTIMIZE_OUTPUT_JAVA tag to YES if your project consists of Java or +# Python sources only. Doxygen will then generate output that is more tailored +# for that language. For instance, namespaces will be presented as packages, +# qualified scopes will look different, etc. +# The default value is: NO. + +OPTIMIZE_OUTPUT_JAVA = NO + +# Set the OPTIMIZE_FOR_FORTRAN tag to YES if your project consists of Fortran +# sources. Doxygen will then generate output that is tailored for Fortran. +# The default value is: NO. + +OPTIMIZE_FOR_FORTRAN = NO + +# Set the OPTIMIZE_OUTPUT_VHDL tag to YES if your project consists of VHDL +# sources. Doxygen will then generate output that is tailored for VHDL. +# The default value is: NO. + +OPTIMIZE_OUTPUT_VHDL = NO + +# Set the OPTIMIZE_OUTPUT_SLICE tag to YES if your project consists of Slice +# sources only. Doxygen will then generate output that is more tailored for that +# language. For instance, namespaces will be presented as modules, types will be +# separated into more groups, etc. +# The default value is: NO. + +OPTIMIZE_OUTPUT_SLICE = NO + +# Doxygen selects the parser to use depending on the extension of the files it +# parses. With this tag you can assign which parser to use for a given +# extension. Doxygen has a built-in mapping, but you can override or extend it +# using this tag. The format is ext=language, where ext is a file extension, and +# language is one of the parsers supported by doxygen: IDL, Java, Javascript, +# Csharp (C#), C, C++, D, PHP, md (Markdown), Objective-C, Python, Slice, +# Fortran (fixed format Fortran: FortranFixed, free formatted Fortran: +# FortranFree, unknown formatted Fortran: Fortran. In the later case the parser +# tries to guess whether the code is fixed or free formatted code, this is the +# default for Fortran type files), VHDL, tcl. For instance to make doxygen treat +# .inc files as Fortran files (default is PHP), and .f files as C (default is +# Fortran), use: inc=Fortran f=C. +# +# Note: For files without extension you can use no_extension as a placeholder. +# +# Note that for custom extensions you also need to set FILE_PATTERNS otherwise +# the files are not read by doxygen. + +EXTENSION_MAPPING = + +# If the MARKDOWN_SUPPORT tag is enabled then doxygen pre-processes all comments +# according to the Markdown format, which allows for more readable +# documentation. See https://daringfireball.net/projects/markdown/ for details. +# The output of markdown processing is further processed by doxygen, so you can +# mix doxygen, HTML, and XML commands with Markdown formatting. Disable only in +# case of backward compatibilities issues. +# The default value is: YES. + +MARKDOWN_SUPPORT = YES + +# When the TOC_INCLUDE_HEADINGS tag is set to a non-zero value, all headings up +# to that level are automatically included in the table of contents, even if +# they do not have an id attribute. +# Note: This feature currently applies only to Markdown headings. +# Minimum value: 0, maximum value: 99, default value: 0. +# This tag requires that the tag MARKDOWN_SUPPORT is set to YES. + +TOC_INCLUDE_HEADINGS = 0 + +# When enabled doxygen tries to link words that correspond to documented +# classes, or namespaces to their corresponding documentation. Such a link can +# be prevented in individual cases by putting a % sign in front of the word or +# globally by setting AUTOLINK_SUPPORT to NO. +# The default value is: YES. + +AUTOLINK_SUPPORT = YES + +# If you use STL classes (i.e. std::string, std::vector, etc.) but do not want +# to include (a tag file for) the STL sources as input, then you should set this +# tag to YES in order to let doxygen match functions declarations and +# definitions whose arguments contain STL classes (e.g. func(std::string); +# versus func(std::string) {}). This also make the inheritance and collaboration +# diagrams that involve STL classes more complete and accurate. +# The default value is: NO. + +BUILTIN_STL_SUPPORT = NO + +# If you use Microsoft's C++/CLI language, you should set this option to YES to +# enable parsing support. +# The default value is: NO. + +CPP_CLI_SUPPORT = NO + +# Set the SIP_SUPPORT tag to YES if your project consists of sip (see: +# https://www.riverbankcomputing.com/software/sip/intro) sources only. Doxygen +# will parse them like normal C++ but will assume all classes use public instead +# of private inheritance when no explicit protection keyword is present. +# The default value is: NO. + +SIP_SUPPORT = NO + +# For Microsoft's IDL there are propget and propput attributes to indicate +# getter and setter methods for a property. Setting this option to YES will make +# doxygen to replace the get and set methods by a property in the documentation. +# This will only work if the methods are indeed getting or setting a simple +# type. If this is not the case, or you want to show the methods anyway, you +# should set this option to NO. +# The default value is: YES. + +IDL_PROPERTY_SUPPORT = YES + +# If member grouping is used in the documentation and the DISTRIBUTE_GROUP_DOC +# tag is set to YES then doxygen will reuse the documentation of the first +# member in the group (if any) for the other members of the group. By default +# all members of a group must be documented explicitly. +# The default value is: NO. + +DISTRIBUTE_GROUP_DOC = NO + +# If one adds a struct or class to a group and this option is enabled, then also +# any nested class or struct is added to the same group. By default this option +# is disabled and one has to add nested compounds explicitly via \ingroup. +# The default value is: NO. + +GROUP_NESTED_COMPOUNDS = NO + +# Set the SUBGROUPING tag to YES to allow class member groups of the same type +# (for instance a group of public functions) to be put as a subgroup of that +# type (e.g. under the Public Functions section). Set it to NO to prevent +# subgrouping. Alternatively, this can be done per class using the +# \nosubgrouping command. +# The default value is: YES. + +SUBGROUPING = YES + +# When the INLINE_GROUPED_CLASSES tag is set to YES, classes, structs and unions +# are shown inside the group in which they are included (e.g. using \ingroup) +# instead of on a separate page (for HTML and Man pages) or section (for LaTeX +# and RTF). +# +# Note that this feature does not work in combination with +# SEPARATE_MEMBER_PAGES. +# The default value is: NO. + +INLINE_GROUPED_CLASSES = NO + +# When the INLINE_SIMPLE_STRUCTS tag is set to YES, structs, classes, and unions +# with only public data fields or simple typedef fields will be shown inline in +# the documentation of the scope in which they are defined (i.e. file, +# namespace, or group documentation), provided this scope is documented. If set +# to NO, structs, classes, and unions are shown on a separate page (for HTML and +# Man pages) or section (for LaTeX and RTF). +# The default value is: NO. + +INLINE_SIMPLE_STRUCTS = NO + +# When TYPEDEF_HIDES_STRUCT tag is enabled, a typedef of a struct, union, or +# enum is documented as struct, union, or enum with the name of the typedef. So +# typedef struct TypeS {} TypeT, will appear in the documentation as a struct +# with name TypeT. When disabled the typedef will appear as a member of a file, +# namespace, or class. And the struct will be named TypeS. This can typically be +# useful for C code in case the coding convention dictates that all compound +# types are typedef'ed and only the typedef is referenced, never the tag name. +# The default value is: NO. + +TYPEDEF_HIDES_STRUCT = NO + +# The size of the symbol lookup cache can be set using LOOKUP_CACHE_SIZE. This +# cache is used to resolve symbols given their name and scope. Since this can be +# an expensive process and often the same symbol appears multiple times in the +# code, doxygen keeps a cache of pre-resolved symbols. If the cache is too small +# doxygen will become slower. If the cache is too large, memory is wasted. The +# cache size is given by this formula: 2^(16+LOOKUP_CACHE_SIZE). The valid range +# is 0..9, the default is 0, corresponding to a cache size of 2^16=65536 +# symbols. At the end of a run doxygen will report the cache usage and suggest +# the optimal cache size from a speed point of view. +# Minimum value: 0, maximum value: 9, default value: 0. + +LOOKUP_CACHE_SIZE = 0 + +#--------------------------------------------------------------------------- +# Build related configuration options +#--------------------------------------------------------------------------- + +# If the EXTRACT_ALL tag is set to YES, doxygen will assume all entities in +# documentation are documented, even if no documentation was available. Private +# class members and static file members will be hidden unless the +# EXTRACT_PRIVATE respectively EXTRACT_STATIC tags are set to YES. +# Note: This will also disable the warnings about undocumented members that are +# normally produced when WARNINGS is set to YES. +# The default value is: NO. + +EXTRACT_ALL = NO + +# If the EXTRACT_PRIVATE tag is set to YES, all private members of a class will +# be included in the documentation. +# The default value is: NO. + +EXTRACT_PRIVATE = NO + +# If the EXTRACT_PACKAGE tag is set to YES, all members with package or internal +# scope will be included in the documentation. +# The default value is: NO. + +EXTRACT_PACKAGE = NO + +# If the EXTRACT_STATIC tag is set to YES, all static members of a file will be +# included in the documentation. +# The default value is: NO. + +EXTRACT_STATIC = NO + +# If the EXTRACT_LOCAL_CLASSES tag is set to YES, classes (and structs) defined +# locally in source files will be included in the documentation. If set to NO, +# only classes defined in header files are included. Does not have any effect +# for Java sources. +# The default value is: YES. + +EXTRACT_LOCAL_CLASSES = YES + +# This flag is only useful for Objective-C code. If set to YES, local methods, +# which are defined in the implementation section but not in the interface are +# included in the documentation. If set to NO, only methods in the interface are +# included. +# The default value is: NO. + +EXTRACT_LOCAL_METHODS = NO + +# If this flag is set to YES, the members of anonymous namespaces will be +# extracted and appear in the documentation as a namespace called +# 'anonymous_namespace{file}', where file will be replaced with the base name of +# the file that contains the anonymous namespace. By default anonymous namespace +# are hidden. +# The default value is: NO. + +EXTRACT_ANON_NSPACES = NO + +# If the HIDE_UNDOC_MEMBERS tag is set to YES, doxygen will hide all +# undocumented members inside documented classes or files. If set to NO these +# members will be included in the various overviews, but no documentation +# section is generated. This option has no effect if EXTRACT_ALL is enabled. +# The default value is: NO. + +HIDE_UNDOC_MEMBERS = NO + +# If the HIDE_UNDOC_CLASSES tag is set to YES, doxygen will hide all +# undocumented classes that are normally visible in the class hierarchy. If set +# to NO, these classes will be included in the various overviews. This option +# has no effect if EXTRACT_ALL is enabled. +# The default value is: NO. + +HIDE_UNDOC_CLASSES = NO + +# If the HIDE_FRIEND_COMPOUNDS tag is set to YES, doxygen will hide all friend +# (class|struct|union) declarations. If set to NO, these declarations will be +# included in the documentation. +# The default value is: NO. + +HIDE_FRIEND_COMPOUNDS = NO + +# If the HIDE_IN_BODY_DOCS tag is set to YES, doxygen will hide any +# documentation blocks found inside the body of a function. If set to NO, these +# blocks will be appended to the function's detailed documentation block. +# The default value is: NO. + +HIDE_IN_BODY_DOCS = NO + +# The INTERNAL_DOCS tag determines if documentation that is typed after a +# \internal command is included. If the tag is set to NO then the documentation +# will be excluded. Set it to YES to include the internal documentation. +# The default value is: NO. + +INTERNAL_DOCS = NO + +# If the CASE_SENSE_NAMES tag is set to NO then doxygen will only generate file +# names in lower-case letters. If set to YES, upper-case letters are also +# allowed. This is useful if you have classes or files whose names only differ +# in case and if your file system supports case sensitive file names. Windows +# and Mac users are advised to set this option to NO. +# The default value is: system dependent. + +CASE_SENSE_NAMES = YES + +# If the HIDE_SCOPE_NAMES tag is set to NO then doxygen will show members with +# their full class and namespace scopes in the documentation. If set to YES, the +# scope will be hidden. +# The default value is: NO. + +HIDE_SCOPE_NAMES = NO + +# If the HIDE_COMPOUND_REFERENCE tag is set to NO (default) then doxygen will +# append additional text to a page's title, such as Class Reference. If set to +# YES the compound reference will be hidden. +# The default value is: NO. + +HIDE_COMPOUND_REFERENCE= NO + +# If the SHOW_INCLUDE_FILES tag is set to YES then doxygen will put a list of +# the files that are included by a file in the documentation of that file. +# The default value is: YES. + +SHOW_INCLUDE_FILES = NO + +# If the SHOW_GROUPED_MEMB_INC tag is set to YES then Doxygen will add for each +# grouped member an include statement to the documentation, telling the reader +# which file to include in order to use the member. +# The default value is: NO. + +SHOW_GROUPED_MEMB_INC = NO + +# If the FORCE_LOCAL_INCLUDES tag is set to YES then doxygen will list include +# files with double quotes in the documentation rather than with sharp brackets. +# The default value is: NO. + +FORCE_LOCAL_INCLUDES = NO + +# If the INLINE_INFO tag is set to YES then a tag [inline] is inserted in the +# documentation for inline members. +# The default value is: YES. + +INLINE_INFO = YES + +# If the SORT_MEMBER_DOCS tag is set to YES then doxygen will sort the +# (detailed) documentation of file and class members alphabetically by member +# name. If set to NO, the members will appear in declaration order. +# The default value is: YES. + +SORT_MEMBER_DOCS = YES + +# If the SORT_BRIEF_DOCS tag is set to YES then doxygen will sort the brief +# descriptions of file, namespace and class members alphabetically by member +# name. If set to NO, the members will appear in declaration order. Note that +# this will also influence the order of the classes in the class list. +# The default value is: NO. + +SORT_BRIEF_DOCS = NO + +# If the SORT_MEMBERS_CTORS_1ST tag is set to YES then doxygen will sort the +# (brief and detailed) documentation of class members so that constructors and +# destructors are listed first. If set to NO the constructors will appear in the +# respective orders defined by SORT_BRIEF_DOCS and SORT_MEMBER_DOCS. +# Note: If SORT_BRIEF_DOCS is set to NO this option is ignored for sorting brief +# member documentation. +# Note: If SORT_MEMBER_DOCS is set to NO this option is ignored for sorting +# detailed member documentation. +# The default value is: NO. + +SORT_MEMBERS_CTORS_1ST = NO + +# If the SORT_GROUP_NAMES tag is set to YES then doxygen will sort the hierarchy +# of group names into alphabetical order. If set to NO the group names will +# appear in their defined order. +# The default value is: NO. + +SORT_GROUP_NAMES = NO + +# If the SORT_BY_SCOPE_NAME tag is set to YES, the class list will be sorted by +# fully-qualified names, including namespaces. If set to NO, the class list will +# be sorted only by class name, not including the namespace part. +# Note: This option is not very useful if HIDE_SCOPE_NAMES is set to YES. +# Note: This option applies only to the class list, not to the alphabetical +# list. +# The default value is: NO. + +SORT_BY_SCOPE_NAME = NO + +# If the STRICT_PROTO_MATCHING option is enabled and doxygen fails to do proper +# type resolution of all parameters of a function it will reject a match between +# the prototype and the implementation of a member function even if there is +# only one candidate or it is obvious which candidate to choose by doing a +# simple string match. By disabling STRICT_PROTO_MATCHING doxygen will still +# accept a match between prototype and implementation in such cases. +# The default value is: NO. + +STRICT_PROTO_MATCHING = NO + +# The GENERATE_TODOLIST tag can be used to enable (YES) or disable (NO) the todo +# list. This list is created by putting \todo commands in the documentation. +# The default value is: YES. + +GENERATE_TODOLIST = YES + +# The GENERATE_TESTLIST tag can be used to enable (YES) or disable (NO) the test +# list. This list is created by putting \test commands in the documentation. +# The default value is: YES. + +GENERATE_TESTLIST = YES + +# The GENERATE_BUGLIST tag can be used to enable (YES) or disable (NO) the bug +# list. This list is created by putting \bug commands in the documentation. +# The default value is: YES. + +GENERATE_BUGLIST = YES + +# The GENERATE_DEPRECATEDLIST tag can be used to enable (YES) or disable (NO) +# the deprecated list. This list is created by putting \deprecated commands in +# the documentation. +# The default value is: YES. + +GENERATE_DEPRECATEDLIST= YES + +# The ENABLED_SECTIONS tag can be used to enable conditional documentation +# sections, marked by \if <section_label> ... \endif and \cond <section_label> +# ... \endcond blocks. + +ENABLED_SECTIONS = + +# The MAX_INITIALIZER_LINES tag determines the maximum number of lines that the +# initial value of a variable or macro / define can have for it to appear in the +# documentation. If the initializer consists of more lines than specified here +# it will be hidden. Use a value of 0 to hide initializers completely. The +# appearance of the value of individual variables and macros / defines can be +# controlled using \showinitializer or \hideinitializer command in the +# documentation regardless of this setting. +# Minimum value: 0, maximum value: 10000, default value: 30. + +MAX_INITIALIZER_LINES = 30 + +# Set the SHOW_USED_FILES tag to NO to disable the list of files generated at +# the bottom of the documentation of classes and structs. If set to YES, the +# list will mention the files that were used to generate the documentation. +# The default value is: YES. + +SHOW_USED_FILES = YES + +# Set the SHOW_FILES tag to NO to disable the generation of the Files page. This +# will remove the Files entry from the Quick Index and from the Folder Tree View +# (if specified). +# The default value is: YES. + +SHOW_FILES = YES + +# Set the SHOW_NAMESPACES tag to NO to disable the generation of the Namespaces +# page. This will remove the Namespaces entry from the Quick Index and from the +# Folder Tree View (if specified). +# The default value is: YES. + +SHOW_NAMESPACES = YES + +# The FILE_VERSION_FILTER tag can be used to specify a program or script that +# doxygen should invoke to get the current version for each file (typically from +# the version control system). Doxygen will invoke the program by executing (via +# popen()) the command command input-file, where command is the value of the +# FILE_VERSION_FILTER tag, and input-file is the name of an input file provided +# by doxygen. Whatever the program writes to standard output is used as the file +# version. For an example see the documentation. + +FILE_VERSION_FILTER = + +# The LAYOUT_FILE tag can be used to specify a layout file which will be parsed +# by doxygen. The layout file controls the global structure of the generated +# output files in an output format independent way. To create the layout file +# that represents doxygen's defaults, run doxygen with the -l option. You can +# optionally specify a file name after the option, if omitted DoxygenLayout.xml +# will be used as the name of the layout file. +# +# Note that if you run doxygen from a directory containing a file called +# DoxygenLayout.xml, doxygen will parse it automatically even if the LAYOUT_FILE +# tag is left empty. + +LAYOUT_FILE = + +# The CITE_BIB_FILES tag can be used to specify one or more bib files containing +# the reference definitions. This must be a list of .bib files. The .bib +# extension is automatically appended if omitted. This requires the bibtex tool +# to be installed. See also https://en.wikipedia.org/wiki/BibTeX for more info. +# For LaTeX the style of the bibliography can be controlled using +# LATEX_BIB_STYLE. To use this feature you need bibtex and perl available in the +# search path. See also \cite for info how to create references. + +CITE_BIB_FILES = + +#--------------------------------------------------------------------------- +# Configuration options related to warning and progress messages +#--------------------------------------------------------------------------- + +# The QUIET tag can be used to turn on/off the messages that are generated to +# standard output by doxygen. If QUIET is set to YES this implies that the +# messages are off. +# The default value is: NO. + +QUIET = NO + +# The WARNINGS tag can be used to turn on/off the warning messages that are +# generated to standard error (stderr) by doxygen. If WARNINGS is set to YES +# this implies that the warnings are on. +# +# Tip: Turn warnings on while writing the documentation. +# The default value is: YES. + +WARNINGS = YES + +# If the WARN_IF_UNDOCUMENTED tag is set to YES then doxygen will generate +# warnings for undocumented members. If EXTRACT_ALL is set to YES then this flag +# will automatically be disabled. +# The default value is: YES. + +WARN_IF_UNDOCUMENTED = NO + +# If the WARN_IF_DOC_ERROR tag is set to YES, doxygen will generate warnings for +# potential errors in the documentation, such as not documenting some parameters +# in a documented function, or documenting parameters that don't exist or using +# markup commands wrongly. +# The default value is: YES. + +WARN_IF_DOC_ERROR = YES + +# This WARN_NO_PARAMDOC option can be enabled to get warnings for functions that +# are documented, but have no documentation for their parameters or return +# value. If set to NO, doxygen will only warn about wrong or incomplete +# parameter documentation, but not about the absence of documentation. If +# EXTRACT_ALL is set to YES then this flag will automatically be disabled. +# The default value is: NO. + +WARN_NO_PARAMDOC = NO + +# If the WARN_AS_ERROR tag is set to YES then doxygen will immediately stop when +# a warning is encountered. +# The default value is: NO. + +WARN_AS_ERROR = NO + +# The WARN_FORMAT tag determines the format of the warning messages that doxygen +# can produce. The string should contain the $file, $line, and $text tags, which +# will be replaced by the file and line number from which the warning originated +# and the warning text. Optionally the format may contain $version, which will +# be replaced by the version of the file (if it could be obtained via +# FILE_VERSION_FILTER) +# The default value is: $file:$line: $text. + +WARN_FORMAT = "$file:$line: $text" + +# The WARN_LOGFILE tag can be used to specify a file to which warning and error +# messages should be written. If left blank the output is written to standard +# error (stderr). + +WARN_LOGFILE = + +#--------------------------------------------------------------------------- +# Configuration options related to the input files +#--------------------------------------------------------------------------- + +# The INPUT tag is used to specify the files and/or directories that contain +# documented source files. You may enter file names like myfile.cpp or +# directories like /usr/src/myproject. Separate the files or directories with +# spaces. See also FILE_PATTERNS and EXTENSION_MAPPING +# Note: If this tag is empty the current directory is searched. + +INPUT = @DOX_TOP@ @INDEX_MD@ @CODING_MD@ + +# This tag can be used to specify the character encoding of the source files +# that doxygen parses. Internally doxygen uses the UTF-8 encoding. Doxygen uses +# libiconv (or the iconv built into libc) for the transcoding. See the libiconv +# documentation (see: https://www.gnu.org/software/libiconv/) for the list of +# possible encodings. +# The default value is: UTF-8. + +INPUT_ENCODING = UTF-8 + +# If the value of the INPUT tag contains directories, you can use the +# FILE_PATTERNS tag to specify one or more wildcard patterns (like *.cpp and +# *.h) to filter out the source-files in the directories. +# +# Note that for custom extensions or not directly supported extensions you also +# need to set EXTENSION_MAPPING for the extension otherwise the files are not +# read by doxygen. +# +# If left blank the following patterns are tested:*.c, *.cc, *.cxx, *.cpp, +# *.c++, *.java, *.ii, *.ixx, *.ipp, *.i++, *.inl, *.idl, *.ddl, *.odl, *.h, +# *.hh, *.hxx, *.hpp, *.h++, *.cs, *.d, *.php, *.php4, *.php5, *.phtml, *.inc, +# *.m, *.markdown, *.md, *.mm, *.dox, *.py, *.pyw, *.f90, *.f95, *.f03, *.f08, +# *.f, *.for, *.tcl, *.vhd, *.vhdl, *.ucf, *.qsf and *.ice. + +FILE_PATTERNS = *.c \ + *.cc \ + *.cxx \ + *.cpp \ + *.c++ \ + *.java \ + *.ii \ + *.ixx \ + *.ipp \ + *.i++ \ + *.inl \ + *.idl \ + *.ddl \ + *.odl \ + *.h \ + *.hh \ + *.hxx \ + *.hpp \ + *.h++ \ + *.cs \ + *.d \ + *.php \ + *.php4 \ + *.php5 \ + *.phtml \ + *.inc \ + *.m \ + *.markdown \ + *.md \ + *.mm \ + *.dox \ + *.py \ + *.pyw \ + *.f90 \ + *.f95 \ + *.f03 \ + *.f08 \ + *.f \ + *.for \ + *.tcl \ + *.vhd \ + *.vhdl \ + *.ucf \ + *.qsf \ + *.ice + +# The RECURSIVE tag can be used to specify whether or not subdirectories should +# be searched for input files as well. +# The default value is: NO. + +RECURSIVE = NO + +# The EXCLUDE tag can be used to specify files and/or directories that should be +# excluded from the INPUT source files. This way you can easily exclude a +# subdirectory from a directory tree whose root is specified with the INPUT tag. +# +# Note that relative paths are relative to the directory from which doxygen is +# run. + +EXCLUDE = + +# The EXCLUDE_SYMLINKS tag can be used to select whether or not files or +# directories that are symbolic links (a Unix file system feature) are excluded +# from the input. +# The default value is: NO. + +EXCLUDE_SYMLINKS = NO + +# If the value of the INPUT tag contains directories, you can use the +# EXCLUDE_PATTERNS tag to specify one or more wildcard patterns to exclude +# certain files from those directories. +# +# Note that the wildcards are matched against the file with absolute path, so to +# exclude all test directories for example use the pattern */test/* + +EXCLUDE_PATTERNS = + +# The EXCLUDE_SYMBOLS tag can be used to specify one or more symbol names +# (namespaces, classes, functions, etc.) that should be excluded from the +# output. The symbol name can be a fully qualified name, a word, or if the +# wildcard * is used, a substring. Examples: ANamespace, AClass, +# AClass::ANamespace, ANamespace::*Test +# +# Note that the wildcards are matched against the file with absolute path, so to +# exclude all test directories use the pattern */test/* + +EXCLUDE_SYMBOLS = + +# The EXAMPLE_PATH tag can be used to specify one or more files or directories +# that contain example code fragments that are included (see the \include +# command). + +EXAMPLE_PATH = + +# If the value of the EXAMPLE_PATH tag contains directories, you can use the +# EXAMPLE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp and +# *.h) to filter out the source-files in the directories. If left blank all +# files are included. + +EXAMPLE_PATTERNS = * + +# If the EXAMPLE_RECURSIVE tag is set to YES then subdirectories will be +# searched for input files to be used with the \include or \dontinclude commands +# irrespective of the value of the RECURSIVE tag. +# The default value is: NO. + +EXAMPLE_RECURSIVE = NO + +# The IMAGE_PATH tag can be used to specify one or more files or directories +# that contain images that are to be included in the documentation (see the +# \image command). + +IMAGE_PATH = + +# The INPUT_FILTER tag can be used to specify a program that doxygen should +# invoke to filter for each input file. Doxygen will invoke the filter program +# by executing (via popen()) the command: +# +# <filter> <input-file> +# +# where <filter> is the value of the INPUT_FILTER tag, and <input-file> is the +# name of an input file. Doxygen will then use the output that the filter +# program writes to standard output. If FILTER_PATTERNS is specified, this tag +# will be ignored. +# +# Note that the filter must not add or remove lines; it is applied before the +# code is scanned, but not when the output code is generated. If lines are added +# or removed, the anchors will not be placed correctly. +# +# Note that for custom extensions or not directly supported extensions you also +# need to set EXTENSION_MAPPING for the extension otherwise the files are not +# properly processed by doxygen. + +INPUT_FILTER = + +# The FILTER_PATTERNS tag can be used to specify filters on a per file pattern +# basis. Doxygen will compare the file name with each pattern and apply the +# filter if there is a match. The filters are a list of the form: pattern=filter +# (like *.cpp=my_cpp_filter). See INPUT_FILTER for further information on how +# filters are used. If the FILTER_PATTERNS tag is empty or if none of the +# patterns match the file name, INPUT_FILTER is applied. +# +# Note that for custom extensions or not directly supported extensions you also +# need to set EXTENSION_MAPPING for the extension otherwise the files are not +# properly processed by doxygen. + +FILTER_PATTERNS = + +# If the FILTER_SOURCE_FILES tag is set to YES, the input filter (if set using +# INPUT_FILTER) will also be used to filter the input files that are used for +# producing the source files to browse (i.e. when SOURCE_BROWSER is set to YES). +# The default value is: NO. + +FILTER_SOURCE_FILES = NO + +# The FILTER_SOURCE_PATTERNS tag can be used to specify source filters per file +# pattern. A pattern will override the setting for FILTER_PATTERN (if any) and +# it is also possible to disable source filtering for a specific pattern using +# *.ext= (so without naming a filter). +# This tag requires that the tag FILTER_SOURCE_FILES is set to YES. + +FILTER_SOURCE_PATTERNS = + +# If the USE_MDFILE_AS_MAINPAGE tag refers to the name of a markdown file that +# is part of the input, its contents will be placed on the main page +# (index.html). This can be useful if you have a project on for instance GitHub +# and want to reuse the introduction page also for the doxygen output. + +USE_MDFILE_AS_MAINPAGE = @INDEX_MD@ + +#--------------------------------------------------------------------------- +# Configuration options related to source browsing +#--------------------------------------------------------------------------- + +# If the SOURCE_BROWSER tag is set to YES then a list of source files will be +# generated. Documented entities will be cross-referenced with these sources. +# +# Note: To get rid of all source code in the generated output, make sure that +# also VERBATIM_HEADERS is set to NO. +# The default value is: NO. + +SOURCE_BROWSER = NO + +# Setting the INLINE_SOURCES tag to YES will include the body of functions, +# classes and enums directly into the documentation. +# The default value is: NO. + +INLINE_SOURCES = NO + +# Setting the STRIP_CODE_COMMENTS tag to YES will instruct doxygen to hide any +# special comment blocks from generated source code fragments. Normal C, C++ and +# Fortran comments will always remain visible. +# The default value is: YES. + +STRIP_CODE_COMMENTS = YES + +# If the REFERENCED_BY_RELATION tag is set to YES then for each documented +# entity all documented functions referencing it will be listed. +# The default value is: NO. + +REFERENCED_BY_RELATION = NO + +# If the REFERENCES_RELATION tag is set to YES then for each documented function +# all documented entities called/used by that function will be listed. +# The default value is: NO. + +REFERENCES_RELATION = NO + +# If the REFERENCES_LINK_SOURCE tag is set to YES and SOURCE_BROWSER tag is set +# to YES then the hyperlinks from functions in REFERENCES_RELATION and +# REFERENCED_BY_RELATION lists will link to the source code. Otherwise they will +# link to the documentation. +# The default value is: YES. + +REFERENCES_LINK_SOURCE = YES + +# If SOURCE_TOOLTIPS is enabled (the default) then hovering a hyperlink in the +# source code will show a tooltip with additional information such as prototype, +# brief description and links to the definition and documentation. Since this +# will make the HTML file larger and loading of large files a bit slower, you +# can opt to disable this feature. +# The default value is: YES. +# This tag requires that the tag SOURCE_BROWSER is set to YES. + +SOURCE_TOOLTIPS = YES + +# If the USE_HTAGS tag is set to YES then the references to source code will +# point to the HTML generated by the htags(1) tool instead of doxygen built-in +# source browser. The htags tool is part of GNU's global source tagging system +# (see https://www.gnu.org/software/global/global.html). You will need version +# 4.8.6 or higher. +# +# To use it do the following: +# - Install the latest version of global +# - Enable SOURCE_BROWSER and USE_HTAGS in the configuration file +# - Make sure the INPUT points to the root of the source tree +# - Run doxygen as normal +# +# Doxygen will invoke htags (and that will in turn invoke gtags), so these +# tools must be available from the command line (i.e. in the search path). +# +# The result: instead of the source browser generated by doxygen, the links to +# source code will now point to the output of htags. +# The default value is: NO. +# This tag requires that the tag SOURCE_BROWSER is set to YES. + +USE_HTAGS = NO + +# If the VERBATIM_HEADERS tag is set the YES then doxygen will generate a +# verbatim copy of the header file for each class for which an include is +# specified. Set to NO to disable this. +# See also: Section \class. +# The default value is: YES. + +VERBATIM_HEADERS = YES + +#--------------------------------------------------------------------------- +# Configuration options related to the alphabetical class index +#--------------------------------------------------------------------------- + +# If the ALPHABETICAL_INDEX tag is set to YES, an alphabetical index of all +# compounds will be generated. Enable this if the project contains a lot of +# classes, structs, unions or interfaces. +# The default value is: YES. + +ALPHABETICAL_INDEX = YES + +# The COLS_IN_ALPHA_INDEX tag can be used to specify the number of columns in +# which the alphabetical index list will be split. +# Minimum value: 1, maximum value: 20, default value: 5. +# This tag requires that the tag ALPHABETICAL_INDEX is set to YES. + +COLS_IN_ALPHA_INDEX = 5 + +# In case all classes in a project start with a common prefix, all classes will +# be put under the same header in the alphabetical index. The IGNORE_PREFIX tag +# can be used to specify a prefix (or a list of prefixes) that should be ignored +# while generating the index headers. +# This tag requires that the tag ALPHABETICAL_INDEX is set to YES. + +IGNORE_PREFIX = + +#--------------------------------------------------------------------------- +# Configuration options related to the HTML output +#--------------------------------------------------------------------------- + +# If the GENERATE_HTML tag is set to YES, doxygen will generate HTML output +# The default value is: YES. + +GENERATE_HTML = YES + +# The HTML_OUTPUT tag is used to specify where the HTML docs will be put. If a +# relative path is entered the value of OUTPUT_DIRECTORY will be put in front of +# it. +# The default directory is: html. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_OUTPUT = html + +# The HTML_FILE_EXTENSION tag can be used to specify the file extension for each +# generated HTML page (for example: .htm, .php, .asp). +# The default value is: .html. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_FILE_EXTENSION = .html + +# The HTML_HEADER tag can be used to specify a user-defined HTML header file for +# each generated HTML page. If the tag is left blank doxygen will generate a +# standard header. +# +# To get valid HTML the header file that includes any scripts and style sheets +# that doxygen needs, which is dependent on the configuration options used (e.g. +# the setting GENERATE_TREEVIEW). It is highly recommended to start with a +# default header using +# doxygen -w html new_header.html new_footer.html new_stylesheet.css +# YourConfigFile +# and then modify the file new_header.html. See also section "Doxygen usage" +# for information on how to generate the default header that doxygen normally +# uses. +# Note: The header is subject to change so you typically have to regenerate the +# default header when upgrading to a newer version of doxygen. For a description +# of the possible markers and block names see the documentation. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_HEADER = + +# The HTML_FOOTER tag can be used to specify a user-defined HTML footer for each +# generated HTML page. If the tag is left blank doxygen will generate a standard +# footer. See HTML_HEADER for more information on how to generate a default +# footer and what special commands can be used inside the footer. See also +# section "Doxygen usage" for information on how to generate the default footer +# that doxygen normally uses. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_FOOTER = + +# The HTML_STYLESHEET tag can be used to specify a user-defined cascading style +# sheet that is used by each HTML page. It can be used to fine-tune the look of +# the HTML output. If left blank doxygen will generate a default style sheet. +# See also section "Doxygen usage" for information on how to generate the style +# sheet that doxygen normally uses. +# Note: It is recommended to use HTML_EXTRA_STYLESHEET instead of this tag, as +# it is more robust and this tag (HTML_STYLESHEET) will in the future become +# obsolete. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_STYLESHEET = + +# The HTML_EXTRA_STYLESHEET tag can be used to specify additional user-defined +# cascading style sheets that are included after the standard style sheets +# created by doxygen. Using this option one can overrule certain style aspects. +# This is preferred over using HTML_STYLESHEET since it does not replace the +# standard style sheet and is therefore more robust against future updates. +# Doxygen will copy the style sheet files to the output directory. +# Note: The order of the extra style sheet files is of importance (e.g. the last +# style sheet in the list overrules the setting of the previous ones in the +# list). For an example see the documentation. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_EXTRA_STYLESHEET = + +# The HTML_EXTRA_FILES tag can be used to specify one or more extra images or +# other source files which should be copied to the HTML output directory. Note +# that these files will be copied to the base HTML output directory. Use the +# $relpath^ marker in the HTML_HEADER and/or HTML_FOOTER files to load these +# files. In the HTML_STYLESHEET file, use the file name only. Also note that the +# files will be copied as-is; there are no commands or markers available. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_EXTRA_FILES = + +# The HTML_COLORSTYLE_HUE tag controls the color of the HTML output. Doxygen +# will adjust the colors in the style sheet and background images according to +# this color. Hue is specified as an angle on a colorwheel, see +# https://en.wikipedia.org/wiki/Hue for more information. For instance the value +# 0 represents red, 60 is yellow, 120 is green, 180 is cyan, 240 is blue, 300 +# purple, and 360 is red again. +# Minimum value: 0, maximum value: 359, default value: 220. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_COLORSTYLE_HUE = 220 + +# The HTML_COLORSTYLE_SAT tag controls the purity (or saturation) of the colors +# in the HTML output. For a value of 0 the output will use grayscales only. A +# value of 255 will produce the most vivid colors. +# Minimum value: 0, maximum value: 255, default value: 100. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_COLORSTYLE_SAT = 100 + +# The HTML_COLORSTYLE_GAMMA tag controls the gamma correction applied to the +# luminance component of the colors in the HTML output. Values below 100 +# gradually make the output lighter, whereas values above 100 make the output +# darker. The value divided by 100 is the actual gamma applied, so 80 represents +# a gamma of 0.8, The value 220 represents a gamma of 2.2, and 100 does not +# change the gamma. +# Minimum value: 40, maximum value: 240, default value: 80. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_COLORSTYLE_GAMMA = 80 + +# If the HTML_TIMESTAMP tag is set to YES then the footer of each generated HTML +# page will contain the date and time when the page was generated. Setting this +# to YES can help to show when doxygen was last run and thus if the +# documentation is up to date. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_TIMESTAMP = NO + +# If the HTML_DYNAMIC_MENUS tag is set to YES then the generated HTML +# documentation will contain a main index with vertical navigation menus that +# are dynamically created via Javascript. If disabled, the navigation index will +# consists of multiple levels of tabs that are statically embedded in every HTML +# page. Disable this option to support browsers that do not have Javascript, +# like the Qt help browser. +# The default value is: YES. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_DYNAMIC_MENUS = YES + +# If the HTML_DYNAMIC_SECTIONS tag is set to YES then the generated HTML +# documentation will contain sections that can be hidden and shown after the +# page has loaded. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_DYNAMIC_SECTIONS = NO + +# With HTML_INDEX_NUM_ENTRIES one can control the preferred number of entries +# shown in the various tree structured indices initially; the user can expand +# and collapse entries dynamically later on. Doxygen will expand the tree to +# such a level that at most the specified number of entries are visible (unless +# a fully collapsed tree already exceeds this amount). So setting the number of +# entries 1 will produce a full collapsed tree by default. 0 is a special value +# representing an infinite number of entries and will result in a full expanded +# tree by default. +# Minimum value: 0, maximum value: 9999, default value: 100. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_INDEX_NUM_ENTRIES = 100 + +# If the GENERATE_DOCSET tag is set to YES, additional index files will be +# generated that can be used as input for Apple's Xcode 3 integrated development +# environment (see: https://developer.apple.com/xcode/), introduced with OSX +# 10.5 (Leopard). To create a documentation set, doxygen will generate a +# Makefile in the HTML output directory. Running make will produce the docset in +# that directory and running make install will install the docset in +# ~/Library/Developer/Shared/Documentation/DocSets so that Xcode will find it at +# startup. See https://developer.apple.com/library/archive/featuredarticles/Doxy +# genXcode/_index.html for more information. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTML is set to YES. + +GENERATE_DOCSET = NO + +# This tag determines the name of the docset feed. A documentation feed provides +# an umbrella under which multiple documentation sets from a single provider +# (such as a company or product suite) can be grouped. +# The default value is: Doxygen generated docs. +# This tag requires that the tag GENERATE_DOCSET is set to YES. + +DOCSET_FEEDNAME = "Doxygen generated docs" + +# This tag specifies a string that should uniquely identify the documentation +# set bundle. This should be a reverse domain-name style string, e.g. +# com.mycompany.MyDocSet. Doxygen will append .docset to the name. +# The default value is: org.doxygen.Project. +# This tag requires that the tag GENERATE_DOCSET is set to YES. + +DOCSET_BUNDLE_ID = org.doxygen.Project + +# The DOCSET_PUBLISHER_ID tag specifies a string that should uniquely identify +# the documentation publisher. This should be a reverse domain-name style +# string, e.g. com.mycompany.MyDocSet.documentation. +# The default value is: org.doxygen.Publisher. +# This tag requires that the tag GENERATE_DOCSET is set to YES. + +DOCSET_PUBLISHER_ID = org.doxygen.Publisher + +# The DOCSET_PUBLISHER_NAME tag identifies the documentation publisher. +# The default value is: Publisher. +# This tag requires that the tag GENERATE_DOCSET is set to YES. + +DOCSET_PUBLISHER_NAME = Publisher + +# If the GENERATE_HTMLHELP tag is set to YES then doxygen generates three +# additional HTML index files: index.hhp, index.hhc, and index.hhk. The +# index.hhp is a project file that can be read by Microsoft's HTML Help Workshop +# (see: https://www.microsoft.com/en-us/download/details.aspx?id=21138) on +# Windows. +# +# The HTML Help Workshop contains a compiler that can convert all HTML output +# generated by doxygen into a single compiled HTML file (.chm). Compiled HTML +# files are now used as the Windows 98 help format, and will replace the old +# Windows help format (.hlp) on all Windows platforms in the future. Compressed +# HTML files also contain an index, a table of contents, and you can search for +# words in the documentation. The HTML workshop also contains a viewer for +# compressed HTML files. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTML is set to YES. + +GENERATE_HTMLHELP = NO + +# The CHM_FILE tag can be used to specify the file name of the resulting .chm +# file. You can add a path in front of the file if the result should not be +# written to the html output directory. +# This tag requires that the tag GENERATE_HTMLHELP is set to YES. + +CHM_FILE = + +# The HHC_LOCATION tag can be used to specify the location (absolute path +# including file name) of the HTML help compiler (hhc.exe). If non-empty, +# doxygen will try to run the HTML help compiler on the generated index.hhp. +# The file has to be specified with full path. +# This tag requires that the tag GENERATE_HTMLHELP is set to YES. + +HHC_LOCATION = + +# The GENERATE_CHI flag controls if a separate .chi index file is generated +# (YES) or that it should be included in the master .chm file (NO). +# The default value is: NO. +# This tag requires that the tag GENERATE_HTMLHELP is set to YES. + +GENERATE_CHI = NO + +# The CHM_INDEX_ENCODING is used to encode HtmlHelp index (hhk), content (hhc) +# and project file content. +# This tag requires that the tag GENERATE_HTMLHELP is set to YES. + +CHM_INDEX_ENCODING = + +# The BINARY_TOC flag controls whether a binary table of contents is generated +# (YES) or a normal table of contents (NO) in the .chm file. Furthermore it +# enables the Previous and Next buttons. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTMLHELP is set to YES. + +BINARY_TOC = NO + +# The TOC_EXPAND flag can be set to YES to add extra items for group members to +# the table of contents of the HTML help documentation and to the tree view. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTMLHELP is set to YES. + +TOC_EXPAND = NO + +# If the GENERATE_QHP tag is set to YES and both QHP_NAMESPACE and +# QHP_VIRTUAL_FOLDER are set, an additional index file will be generated that +# can be used as input for Qt's qhelpgenerator to generate a Qt Compressed Help +# (.qch) of the generated HTML documentation. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTML is set to YES. + +GENERATE_QHP = NO + +# If the QHG_LOCATION tag is specified, the QCH_FILE tag can be used to specify +# the file name of the resulting .qch file. The path specified is relative to +# the HTML output folder. +# This tag requires that the tag GENERATE_QHP is set to YES. + +QCH_FILE = + +# The QHP_NAMESPACE tag specifies the namespace to use when generating Qt Help +# Project output. For more information please see Qt Help Project / Namespace +# (see: http://doc.qt.io/archives/qt-4.8/qthelpproject.html#namespace). +# The default value is: org.doxygen.Project. +# This tag requires that the tag GENERATE_QHP is set to YES. + +QHP_NAMESPACE = org.doxygen.Project + +# The QHP_VIRTUAL_FOLDER tag specifies the namespace to use when generating Qt +# Help Project output. For more information please see Qt Help Project / Virtual +# Folders (see: http://doc.qt.io/archives/qt-4.8/qthelpproject.html#virtual- +# folders). +# The default value is: doc. +# This tag requires that the tag GENERATE_QHP is set to YES. + +QHP_VIRTUAL_FOLDER = doc + +# If the QHP_CUST_FILTER_NAME tag is set, it specifies the name of a custom +# filter to add. For more information please see Qt Help Project / Custom +# Filters (see: http://doc.qt.io/archives/qt-4.8/qthelpproject.html#custom- +# filters). +# This tag requires that the tag GENERATE_QHP is set to YES. + +QHP_CUST_FILTER_NAME = + +# The QHP_CUST_FILTER_ATTRS tag specifies the list of the attributes of the +# custom filter to add. For more information please see Qt Help Project / Custom +# Filters (see: http://doc.qt.io/archives/qt-4.8/qthelpproject.html#custom- +# filters). +# This tag requires that the tag GENERATE_QHP is set to YES. + +QHP_CUST_FILTER_ATTRS = + +# The QHP_SECT_FILTER_ATTRS tag specifies the list of the attributes this +# project's filter section matches. Qt Help Project / Filter Attributes (see: +# http://doc.qt.io/archives/qt-4.8/qthelpproject.html#filter-attributes). +# This tag requires that the tag GENERATE_QHP is set to YES. + +QHP_SECT_FILTER_ATTRS = + +# The QHG_LOCATION tag can be used to specify the location of Qt's +# qhelpgenerator. If non-empty doxygen will try to run qhelpgenerator on the +# generated .qhp file. +# This tag requires that the tag GENERATE_QHP is set to YES. + +QHG_LOCATION = + +# If the GENERATE_ECLIPSEHELP tag is set to YES, additional index files will be +# generated, together with the HTML files, they form an Eclipse help plugin. To +# install this plugin and make it available under the help contents menu in +# Eclipse, the contents of the directory containing the HTML and XML files needs +# to be copied into the plugins directory of eclipse. The name of the directory +# within the plugins directory should be the same as the ECLIPSE_DOC_ID value. +# After copying Eclipse needs to be restarted before the help appears. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTML is set to YES. + +GENERATE_ECLIPSEHELP = NO + +# A unique identifier for the Eclipse help plugin. When installing the plugin +# the directory name containing the HTML and XML files should also have this +# name. Each documentation set should have its own identifier. +# The default value is: org.doxygen.Project. +# This tag requires that the tag GENERATE_ECLIPSEHELP is set to YES. + +ECLIPSE_DOC_ID = org.doxygen.Project + +# If you want full control over the layout of the generated HTML pages it might +# be necessary to disable the index and replace it with your own. The +# DISABLE_INDEX tag can be used to turn on/off the condensed index (tabs) at top +# of each HTML page. A value of NO enables the index and the value YES disables +# it. Since the tabs in the index contain the same information as the navigation +# tree, you can set this option to YES if you also set GENERATE_TREEVIEW to YES. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTML is set to YES. + +DISABLE_INDEX = NO + +# The GENERATE_TREEVIEW tag is used to specify whether a tree-like index +# structure should be generated to display hierarchical information. If the tag +# value is set to YES, a side panel will be generated containing a tree-like +# index structure (just like the one that is generated for HTML Help). For this +# to work a browser that supports JavaScript, DHTML, CSS and frames is required +# (i.e. any modern browser). Windows users are probably better off using the +# HTML help feature. Via custom style sheets (see HTML_EXTRA_STYLESHEET) one can +# further fine-tune the look of the index. As an example, the default style +# sheet generated by doxygen has an example that shows how to put an image at +# the root of the tree instead of the PROJECT_NAME. Since the tree basically has +# the same information as the tab index, you could consider setting +# DISABLE_INDEX to YES when enabling this option. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTML is set to YES. + +GENERATE_TREEVIEW = NO + +# The ENUM_VALUES_PER_LINE tag can be used to set the number of enum values that +# doxygen will group on one line in the generated HTML documentation. +# +# Note that a value of 0 will completely suppress the enum values from appearing +# in the overview section. +# Minimum value: 0, maximum value: 20, default value: 4. +# This tag requires that the tag GENERATE_HTML is set to YES. + +ENUM_VALUES_PER_LINE = 4 + +# If the treeview is enabled (see GENERATE_TREEVIEW) then this tag can be used +# to set the initial width (in pixels) of the frame in which the tree is shown. +# Minimum value: 0, maximum value: 1500, default value: 250. +# This tag requires that the tag GENERATE_HTML is set to YES. + +TREEVIEW_WIDTH = 250 + +# If the EXT_LINKS_IN_WINDOW option is set to YES, doxygen will open links to +# external symbols imported via tag files in a separate window. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTML is set to YES. + +EXT_LINKS_IN_WINDOW = NO + +# Use this tag to change the font size of LaTeX formulas included as images in +# the HTML documentation. When you change the font size after a successful +# doxygen run you need to manually remove any form_*.png images from the HTML +# output directory to force them to be regenerated. +# Minimum value: 8, maximum value: 50, default value: 10. +# This tag requires that the tag GENERATE_HTML is set to YES. + +FORMULA_FONTSIZE = 10 + +# Use the FORMULA_TRANSPARENT tag to determine whether or not the images +# generated for formulas are transparent PNGs. Transparent PNGs are not +# supported properly for IE 6.0, but are supported on all modern browsers. +# +# Note that when changing this option you need to delete any form_*.png files in +# the HTML output directory before the changes have effect. +# The default value is: YES. +# This tag requires that the tag GENERATE_HTML is set to YES. + +FORMULA_TRANSPARENT = YES + +# Enable the USE_MATHJAX option to render LaTeX formulas using MathJax (see +# https://www.mathjax.org) which uses client side Javascript for the rendering +# instead of using pre-rendered bitmaps. Use this if you do not have LaTeX +# installed or if you want to formulas look prettier in the HTML output. When +# enabled you may also need to install MathJax separately and configure the path +# to it using the MATHJAX_RELPATH option. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTML is set to YES. + +USE_MATHJAX = NO + +# When MathJax is enabled you can set the default output format to be used for +# the MathJax output. See the MathJax site (see: +# http://docs.mathjax.org/en/latest/output.html) for more details. +# Possible values are: HTML-CSS (which is slower, but has the best +# compatibility), NativeMML (i.e. MathML) and SVG. +# The default value is: HTML-CSS. +# This tag requires that the tag USE_MATHJAX is set to YES. + +MATHJAX_FORMAT = HTML-CSS + +# When MathJax is enabled you need to specify the location relative to the HTML +# output directory using the MATHJAX_RELPATH option. The destination directory +# should contain the MathJax.js script. For instance, if the mathjax directory +# is located at the same level as the HTML output directory, then +# MATHJAX_RELPATH should be ../mathjax. The default value points to the MathJax +# Content Delivery Network so you can quickly see the result without installing +# MathJax. However, it is strongly recommended to install a local copy of +# MathJax from https://www.mathjax.org before deployment. +# The default value is: https://cdnjs.cloudflare.com/ajax/libs/mathjax/2.7.5/. +# This tag requires that the tag USE_MATHJAX is set to YES. + +MATHJAX_RELPATH = https://cdnjs.cloudflare.com/ajax/libs/mathjax/2.7.5/ + +# The MATHJAX_EXTENSIONS tag can be used to specify one or more MathJax +# extension names that should be enabled during MathJax rendering. For example +# MATHJAX_EXTENSIONS = TeX/AMSmath TeX/AMSsymbols +# This tag requires that the tag USE_MATHJAX is set to YES. + +MATHJAX_EXTENSIONS = + +# The MATHJAX_CODEFILE tag can be used to specify a file with javascript pieces +# of code that will be used on startup of the MathJax code. See the MathJax site +# (see: http://docs.mathjax.org/en/latest/output.html) for more details. For an +# example see the documentation. +# This tag requires that the tag USE_MATHJAX is set to YES. + +MATHJAX_CODEFILE = + +# When the SEARCHENGINE tag is enabled doxygen will generate a search box for +# the HTML output. The underlying search engine uses javascript and DHTML and +# should work on any modern browser. Note that when using HTML help +# (GENERATE_HTMLHELP), Qt help (GENERATE_QHP), or docsets (GENERATE_DOCSET) +# there is already a search function so this one should typically be disabled. +# For large projects the javascript based search engine can be slow, then +# enabling SERVER_BASED_SEARCH may provide a better solution. It is possible to +# search using the keyboard; to jump to the search box use <access key> + S +# (what the <access key> is depends on the OS and browser, but it is typically +# <CTRL>, <ALT>/<option>, or both). Inside the search box use the <cursor down +# key> to jump into the search results window, the results can be navigated +# using the <cursor keys>. Press <Enter> to select an item or <escape> to cancel +# the search. The filter options can be selected when the cursor is inside the +# search box by pressing <Shift>+<cursor down>. Also here use the <cursor keys> +# to select a filter and <Enter> or <escape> to activate or cancel the filter +# option. +# The default value is: YES. +# This tag requires that the tag GENERATE_HTML is set to YES. + +SEARCHENGINE = YES + +# When the SERVER_BASED_SEARCH tag is enabled the search engine will be +# implemented using a web server instead of a web client using Javascript. There +# are two flavors of web server based searching depending on the EXTERNAL_SEARCH +# setting. When disabled, doxygen will generate a PHP script for searching and +# an index file used by the script. When EXTERNAL_SEARCH is enabled the indexing +# and searching needs to be provided by external tools. See the section +# "External Indexing and Searching" for details. +# The default value is: NO. +# This tag requires that the tag SEARCHENGINE is set to YES. + +SERVER_BASED_SEARCH = NO + +# When EXTERNAL_SEARCH tag is enabled doxygen will no longer generate the PHP +# script for searching. Instead the search results are written to an XML file +# which needs to be processed by an external indexer. Doxygen will invoke an +# external search engine pointed to by the SEARCHENGINE_URL option to obtain the +# search results. +# +# Doxygen ships with an example indexer (doxyindexer) and search engine +# (doxysearch.cgi) which are based on the open source search engine library +# Xapian (see: https://xapian.org/). +# +# See the section "External Indexing and Searching" for details. +# The default value is: NO. +# This tag requires that the tag SEARCHENGINE is set to YES. + +EXTERNAL_SEARCH = NO + +# The SEARCHENGINE_URL should point to a search engine hosted by a web server +# which will return the search results when EXTERNAL_SEARCH is enabled. +# +# Doxygen ships with an example indexer (doxyindexer) and search engine +# (doxysearch.cgi) which are based on the open source search engine library +# Xapian (see: https://xapian.org/). See the section "External Indexing and +# Searching" for details. +# This tag requires that the tag SEARCHENGINE is set to YES. + +SEARCHENGINE_URL = + +# When SERVER_BASED_SEARCH and EXTERNAL_SEARCH are both enabled the unindexed +# search data is written to a file for indexing by an external tool. With the +# SEARCHDATA_FILE tag the name of this file can be specified. +# The default file is: searchdata.xml. +# This tag requires that the tag SEARCHENGINE is set to YES. + +SEARCHDATA_FILE = searchdata.xml + +# When SERVER_BASED_SEARCH and EXTERNAL_SEARCH are both enabled the +# EXTERNAL_SEARCH_ID tag can be used as an identifier for the project. This is +# useful in combination with EXTRA_SEARCH_MAPPINGS to search through multiple +# projects and redirect the results back to the right project. +# This tag requires that the tag SEARCHENGINE is set to YES. + +EXTERNAL_SEARCH_ID = + +# The EXTRA_SEARCH_MAPPINGS tag can be used to enable searching through doxygen +# projects other than the one defined by this configuration file, but that are +# all added to the same external search index. Each project needs to have a +# unique id set via EXTERNAL_SEARCH_ID. The search mapping then maps the id of +# to a relative location where the documentation can be found. The format is: +# EXTRA_SEARCH_MAPPINGS = tagname1=loc1 tagname2=loc2 ... +# This tag requires that the tag SEARCHENGINE is set to YES. + +EXTRA_SEARCH_MAPPINGS = + +#--------------------------------------------------------------------------- +# Configuration options related to the LaTeX output +#--------------------------------------------------------------------------- + +# If the GENERATE_LATEX tag is set to YES, doxygen will generate LaTeX output. +# The default value is: YES. + +GENERATE_LATEX = YES + +# The LATEX_OUTPUT tag is used to specify where the LaTeX docs will be put. If a +# relative path is entered the value of OUTPUT_DIRECTORY will be put in front of +# it. +# The default directory is: latex. +# This tag requires that the tag GENERATE_LATEX is set to YES. + +LATEX_OUTPUT = latex + +# The LATEX_CMD_NAME tag can be used to specify the LaTeX command name to be +# invoked. +# +# Note that when not enabling USE_PDFLATEX the default is latex when enabling +# USE_PDFLATEX the default is pdflatex and when in the later case latex is +# chosen this is overwritten by pdflatex. For specific output languages the +# default can have been set differently, this depends on the implementation of +# the output language. +# This tag requires that the tag GENERATE_LATEX is set to YES. + +LATEX_CMD_NAME = + +# The MAKEINDEX_CMD_NAME tag can be used to specify the command name to generate +# index for LaTeX. +# Note: This tag is used in the Makefile / make.bat. +# See also: LATEX_MAKEINDEX_CMD for the part in the generated output file +# (.tex). +# The default file is: makeindex. +# This tag requires that the tag GENERATE_LATEX is set to YES. + +MAKEINDEX_CMD_NAME = makeindex + +# The LATEX_MAKEINDEX_CMD tag can be used to specify the command name to +# generate index for LaTeX. +# Note: This tag is used in the generated output file (.tex). +# See also: MAKEINDEX_CMD_NAME for the part in the Makefile / make.bat. +# The default value is: \makeindex. +# This tag requires that the tag GENERATE_LATEX is set to YES. + +LATEX_MAKEINDEX_CMD = \makeindex + +# If the COMPACT_LATEX tag is set to YES, doxygen generates more compact LaTeX +# documents. This may be useful for small projects and may help to save some +# trees in general. +# The default value is: NO. +# This tag requires that the tag GENERATE_LATEX is set to YES. + +COMPACT_LATEX = NO + +# The PAPER_TYPE tag can be used to set the paper type that is used by the +# printer. +# Possible values are: a4 (210 x 297 mm), letter (8.5 x 11 inches), legal (8.5 x +# 14 inches) and executive (7.25 x 10.5 inches). +# The default value is: a4. +# This tag requires that the tag GENERATE_LATEX is set to YES. + +PAPER_TYPE = a4 + +# The EXTRA_PACKAGES tag can be used to specify one or more LaTeX package names +# that should be included in the LaTeX output. The package can be specified just +# by its name or with the correct syntax as to be used with the LaTeX +# \usepackage command. To get the times font for instance you can specify : +# EXTRA_PACKAGES=times or EXTRA_PACKAGES={times} +# To use the option intlimits with the amsmath package you can specify: +# EXTRA_PACKAGES=[intlimits]{amsmath} +# If left blank no extra packages will be included. +# This tag requires that the tag GENERATE_LATEX is set to YES. + +EXTRA_PACKAGES = + +# The LATEX_HEADER tag can be used to specify a personal LaTeX header for the +# generated LaTeX document. The header should contain everything until the first +# chapter. If it is left blank doxygen will generate a standard header. See +# section "Doxygen usage" for information on how to let doxygen write the +# default header to a separate file. +# +# Note: Only use a user-defined header if you know what you are doing! The +# following commands have a special meaning inside the header: $title, +# $datetime, $date, $doxygenversion, $projectname, $projectnumber, +# $projectbrief, $projectlogo. Doxygen will replace $title with the empty +# string, for the replacement values of the other commands the user is referred +# to HTML_HEADER. +# This tag requires that the tag GENERATE_LATEX is set to YES. + +LATEX_HEADER = + +# The LATEX_FOOTER tag can be used to specify a personal LaTeX footer for the +# generated LaTeX document. The footer should contain everything after the last +# chapter. If it is left blank doxygen will generate a standard footer. See +# LATEX_HEADER for more information on how to generate a default footer and what +# special commands can be used inside the footer. +# +# Note: Only use a user-defined footer if you know what you are doing! +# This tag requires that the tag GENERATE_LATEX is set to YES. + +LATEX_FOOTER = + +# The LATEX_EXTRA_STYLESHEET tag can be used to specify additional user-defined +# LaTeX style sheets that are included after the standard style sheets created +# by doxygen. Using this option one can overrule certain style aspects. Doxygen +# will copy the style sheet files to the output directory. +# Note: The order of the extra style sheet files is of importance (e.g. the last +# style sheet in the list overrules the setting of the previous ones in the +# list). +# This tag requires that the tag GENERATE_LATEX is set to YES. + +LATEX_EXTRA_STYLESHEET = + +# The LATEX_EXTRA_FILES tag can be used to specify one or more extra images or +# other source files which should be copied to the LATEX_OUTPUT output +# directory. Note that the files will be copied as-is; there are no commands or +# markers available. +# This tag requires that the tag GENERATE_LATEX is set to YES. + +LATEX_EXTRA_FILES = + +# If the PDF_HYPERLINKS tag is set to YES, the LaTeX that is generated is +# prepared for conversion to PDF (using ps2pdf or pdflatex). The PDF file will +# contain links (just like the HTML output) instead of page references. This +# makes the output suitable for online browsing using a PDF viewer. +# The default value is: YES. +# This tag requires that the tag GENERATE_LATEX is set to YES. + +PDF_HYPERLINKS = YES + +# If the USE_PDFLATEX tag is set to YES, doxygen will use pdflatex to generate +# the PDF file directly from the LaTeX files. Set this option to YES, to get a +# higher quality PDF documentation. +# The default value is: YES. +# This tag requires that the tag GENERATE_LATEX is set to YES. + +USE_PDFLATEX = YES + +# If the LATEX_BATCHMODE tag is set to YES, doxygen will add the \batchmode +# command to the generated LaTeX files. This will instruct LaTeX to keep running +# if errors occur, instead of asking the user for help. This option is also used +# when generating formulas in HTML. +# The default value is: NO. +# This tag requires that the tag GENERATE_LATEX is set to YES. + +LATEX_BATCHMODE = NO + +# If the LATEX_HIDE_INDICES tag is set to YES then doxygen will not include the +# index chapters (such as File Index, Compound Index, etc.) in the output. +# The default value is: NO. +# This tag requires that the tag GENERATE_LATEX is set to YES. + +LATEX_HIDE_INDICES = NO + +# If the LATEX_SOURCE_CODE tag is set to YES then doxygen will include source +# code with syntax highlighting in the LaTeX output. +# +# Note that which sources are shown also depends on other settings such as +# SOURCE_BROWSER. +# The default value is: NO. +# This tag requires that the tag GENERATE_LATEX is set to YES. + +LATEX_SOURCE_CODE = NO + +# The LATEX_BIB_STYLE tag can be used to specify the style to use for the +# bibliography, e.g. plainnat, or ieeetr. See +# https://en.wikipedia.org/wiki/BibTeX and \cite for more info. +# The default value is: plain. +# This tag requires that the tag GENERATE_LATEX is set to YES. + +LATEX_BIB_STYLE = plain + +# If the LATEX_TIMESTAMP tag is set to YES then the footer of each generated +# page will contain the date and time when the page was generated. Setting this +# to NO can help when comparing the output of multiple runs. +# The default value is: NO. +# This tag requires that the tag GENERATE_LATEX is set to YES. + +LATEX_TIMESTAMP = NO + +# The LATEX_EMOJI_DIRECTORY tag is used to specify the (relative or absolute) +# path from which the emoji images will be read. If a relative path is entered, +# it will be relative to the LATEX_OUTPUT directory. If left blank the +# LATEX_OUTPUT directory will be used. +# This tag requires that the tag GENERATE_LATEX is set to YES. + +LATEX_EMOJI_DIRECTORY = + +#--------------------------------------------------------------------------- +# Configuration options related to the RTF output +#--------------------------------------------------------------------------- + +# If the GENERATE_RTF tag is set to YES, doxygen will generate RTF output. The +# RTF output is optimized for Word 97 and may not look too pretty with other RTF +# readers/editors. +# The default value is: NO. + +GENERATE_RTF = NO + +# The RTF_OUTPUT tag is used to specify where the RTF docs will be put. If a +# relative path is entered the value of OUTPUT_DIRECTORY will be put in front of +# it. +# The default directory is: rtf. +# This tag requires that the tag GENERATE_RTF is set to YES. + +RTF_OUTPUT = rtf + +# If the COMPACT_RTF tag is set to YES, doxygen generates more compact RTF +# documents. This may be useful for small projects and may help to save some +# trees in general. +# The default value is: NO. +# This tag requires that the tag GENERATE_RTF is set to YES. + +COMPACT_RTF = NO + +# If the RTF_HYPERLINKS tag is set to YES, the RTF that is generated will +# contain hyperlink fields. The RTF file will contain links (just like the HTML +# output) instead of page references. This makes the output suitable for online +# browsing using Word or some other Word compatible readers that support those +# fields. +# +# Note: WordPad (write) and others do not support links. +# The default value is: NO. +# This tag requires that the tag GENERATE_RTF is set to YES. + +RTF_HYPERLINKS = NO + +# Load stylesheet definitions from file. Syntax is similar to doxygen's +# configuration file, i.e. a series of assignments. You only have to provide +# replacements, missing definitions are set to their default value. +# +# See also section "Doxygen usage" for information on how to generate the +# default style sheet that doxygen normally uses. +# This tag requires that the tag GENERATE_RTF is set to YES. + +RTF_STYLESHEET_FILE = + +# Set optional variables used in the generation of an RTF document. Syntax is +# similar to doxygen's configuration file. A template extensions file can be +# generated using doxygen -e rtf extensionFile. +# This tag requires that the tag GENERATE_RTF is set to YES. + +RTF_EXTENSIONS_FILE = + +# If the RTF_SOURCE_CODE tag is set to YES then doxygen will include source code +# with syntax highlighting in the RTF output. +# +# Note that which sources are shown also depends on other settings such as +# SOURCE_BROWSER. +# The default value is: NO. +# This tag requires that the tag GENERATE_RTF is set to YES. + +RTF_SOURCE_CODE = NO + +#--------------------------------------------------------------------------- +# Configuration options related to the man page output +#--------------------------------------------------------------------------- + +# If the GENERATE_MAN tag is set to YES, doxygen will generate man pages for +# classes and files. +# The default value is: NO. + +GENERATE_MAN = NO + +# The MAN_OUTPUT tag is used to specify where the man pages will be put. If a +# relative path is entered the value of OUTPUT_DIRECTORY will be put in front of +# it. A directory man3 will be created inside the directory specified by +# MAN_OUTPUT. +# The default directory is: man. +# This tag requires that the tag GENERATE_MAN is set to YES. + +MAN_OUTPUT = man + +# The MAN_EXTENSION tag determines the extension that is added to the generated +# man pages. In case the manual section does not start with a number, the number +# 3 is prepended. The dot (.) at the beginning of the MAN_EXTENSION tag is +# optional. +# The default value is: .3. +# This tag requires that the tag GENERATE_MAN is set to YES. + +MAN_EXTENSION = .3 + +# The MAN_SUBDIR tag determines the name of the directory created within +# MAN_OUTPUT in which the man pages are placed. If defaults to man followed by +# MAN_EXTENSION with the initial . removed. +# This tag requires that the tag GENERATE_MAN is set to YES. + +MAN_SUBDIR = + +# If the MAN_LINKS tag is set to YES and doxygen generates man output, then it +# will generate one additional man file for each entity documented in the real +# man page(s). These additional files only source the real man page, but without +# them the man command would be unable to find the correct page. +# The default value is: NO. +# This tag requires that the tag GENERATE_MAN is set to YES. + +MAN_LINKS = NO + +#--------------------------------------------------------------------------- +# Configuration options related to the XML output +#--------------------------------------------------------------------------- + +# If the GENERATE_XML tag is set to YES, doxygen will generate an XML file that +# captures the structure of the code including all documentation. +# The default value is: NO. + +GENERATE_XML = NO + +# The XML_OUTPUT tag is used to specify where the XML pages will be put. If a +# relative path is entered the value of OUTPUT_DIRECTORY will be put in front of +# it. +# The default directory is: xml. +# This tag requires that the tag GENERATE_XML is set to YES. + +XML_OUTPUT = xml + +# If the XML_PROGRAMLISTING tag is set to YES, doxygen will dump the program +# listings (including syntax highlighting and cross-referencing information) to +# the XML output. Note that enabling this will significantly increase the size +# of the XML output. +# The default value is: YES. +# This tag requires that the tag GENERATE_XML is set to YES. + +XML_PROGRAMLISTING = YES + +# If the XML_NS_MEMB_FILE_SCOPE tag is set to YES, doxygen will include +# namespace members in file scope as well, matching the HTML output. +# The default value is: NO. +# This tag requires that the tag GENERATE_XML is set to YES. + +XML_NS_MEMB_FILE_SCOPE = NO + +#--------------------------------------------------------------------------- +# Configuration options related to the DOCBOOK output +#--------------------------------------------------------------------------- + +# If the GENERATE_DOCBOOK tag is set to YES, doxygen will generate Docbook files +# that can be used to generate PDF. +# The default value is: NO. + +GENERATE_DOCBOOK = NO + +# The DOCBOOK_OUTPUT tag is used to specify where the Docbook pages will be put. +# If a relative path is entered the value of OUTPUT_DIRECTORY will be put in +# front of it. +# The default directory is: docbook. +# This tag requires that the tag GENERATE_DOCBOOK is set to YES. + +DOCBOOK_OUTPUT = docbook + +# If the DOCBOOK_PROGRAMLISTING tag is set to YES, doxygen will include the +# program listings (including syntax highlighting and cross-referencing +# information) to the DOCBOOK output. Note that enabling this will significantly +# increase the size of the DOCBOOK output. +# The default value is: NO. +# This tag requires that the tag GENERATE_DOCBOOK is set to YES. + +DOCBOOK_PROGRAMLISTING = NO + +#--------------------------------------------------------------------------- +# Configuration options for the AutoGen Definitions output +#--------------------------------------------------------------------------- + +# If the GENERATE_AUTOGEN_DEF tag is set to YES, doxygen will generate an +# AutoGen Definitions (see http://autogen.sourceforge.net/) file that captures +# the structure of the code including all documentation. Note that this feature +# is still experimental and incomplete at the moment. +# The default value is: NO. + +GENERATE_AUTOGEN_DEF = NO + +#--------------------------------------------------------------------------- +# Configuration options related to the Perl module output +#--------------------------------------------------------------------------- + +# If the GENERATE_PERLMOD tag is set to YES, doxygen will generate a Perl module +# file that captures the structure of the code including all documentation. +# +# Note that this feature is still experimental and incomplete at the moment. +# The default value is: NO. + +GENERATE_PERLMOD = NO + +# If the PERLMOD_LATEX tag is set to YES, doxygen will generate the necessary +# Makefile rules, Perl scripts and LaTeX code to be able to generate PDF and DVI +# output from the Perl module output. +# The default value is: NO. +# This tag requires that the tag GENERATE_PERLMOD is set to YES. + +PERLMOD_LATEX = NO + +# If the PERLMOD_PRETTY tag is set to YES, the Perl module output will be nicely +# formatted so it can be parsed by a human reader. This is useful if you want to +# understand what is going on. On the other hand, if this tag is set to NO, the +# size of the Perl module output will be much smaller and Perl will parse it +# just the same. +# The default value is: YES. +# This tag requires that the tag GENERATE_PERLMOD is set to YES. + +PERLMOD_PRETTY = YES + +# The names of the make variables in the generated doxyrules.make file are +# prefixed with the string contained in PERLMOD_MAKEVAR_PREFIX. This is useful +# so different doxyrules.make files included by the same Makefile don't +# overwrite each other's variables. +# This tag requires that the tag GENERATE_PERLMOD is set to YES. + +PERLMOD_MAKEVAR_PREFIX = + +#--------------------------------------------------------------------------- +# Configuration options related to the preprocessor +#--------------------------------------------------------------------------- + +# If the ENABLE_PREPROCESSING tag is set to YES, doxygen will evaluate all +# C-preprocessor directives found in the sources and include files. +# The default value is: YES. + +ENABLE_PREPROCESSING = YES + +# If the MACRO_EXPANSION tag is set to YES, doxygen will expand all macro names +# in the source code. If set to NO, only conditional compilation will be +# performed. Macro expansion can be done in a controlled way by setting +# EXPAND_ONLY_PREDEF to YES. +# The default value is: NO. +# This tag requires that the tag ENABLE_PREPROCESSING is set to YES. + +MACRO_EXPANSION = NO + +# If the EXPAND_ONLY_PREDEF and MACRO_EXPANSION tags are both set to YES then +# the macro expansion is limited to the macros specified with the PREDEFINED and +# EXPAND_AS_DEFINED tags. +# The default value is: NO. +# This tag requires that the tag ENABLE_PREPROCESSING is set to YES. + +EXPAND_ONLY_PREDEF = NO + +# If the SEARCH_INCLUDES tag is set to YES, the include files in the +# INCLUDE_PATH will be searched if a #include is found. +# The default value is: YES. +# This tag requires that the tag ENABLE_PREPROCESSING is set to YES. + +SEARCH_INCLUDES = YES + +# The INCLUDE_PATH tag can be used to specify one or more directories that +# contain include files that are not input files but should be processed by the +# preprocessor. +# This tag requires that the tag SEARCH_INCLUDES is set to YES. + +INCLUDE_PATH = + +# You can use the INCLUDE_FILE_PATTERNS tag to specify one or more wildcard +# patterns (like *.h and *.hpp) to filter out the header-files in the +# directories. If left blank, the patterns specified with FILE_PATTERNS will be +# used. +# This tag requires that the tag ENABLE_PREPROCESSING is set to YES. + +INCLUDE_FILE_PATTERNS = + +# The PREDEFINED tag can be used to specify one or more macro names that are +# defined before the preprocessor is started (similar to the -D option of e.g. +# gcc). The argument of the tag is a list of macros of the form: name or +# name=definition (no spaces). If the definition and the "=" are omitted, "=1" +# is assumed. To prevent a macro definition from being undefined via #undef or +# recursively expanded use the := operator instead of the = operator. +# This tag requires that the tag ENABLE_PREPROCESSING is set to YES. + +PREDEFINED = HAVE_FFTW + +# If the MACRO_EXPANSION and EXPAND_ONLY_PREDEF tags are set to YES then this +# tag can be used to specify a list of macro names that should be expanded. The +# macro definition that is found in the sources will be used. Use the PREDEFINED +# tag if you want to use a different macro definition that overrules the +# definition found in the source code. +# This tag requires that the tag ENABLE_PREPROCESSING is set to YES. + +EXPAND_AS_DEFINED = + +# If the SKIP_FUNCTION_MACROS tag is set to YES then doxygen's preprocessor will +# remove all references to function-like macros that are alone on a line, have +# an all uppercase name, and do not end with a semicolon. Such function macros +# are typically used for boiler-plate code, and will confuse the parser if not +# removed. +# The default value is: YES. +# This tag requires that the tag ENABLE_PREPROCESSING is set to YES. + +SKIP_FUNCTION_MACROS = YES + +#--------------------------------------------------------------------------- +# Configuration options related to external references +#--------------------------------------------------------------------------- + +# The TAGFILES tag can be used to specify one or more tag files. For each tag +# file the location of the external documentation should be added. The format of +# a tag file without this location is as follows: +# TAGFILES = file1 file2 ... +# Adding location for the tag files is done as follows: +# TAGFILES = file1=loc1 "file2 = loc2" ... +# where loc1 and loc2 can be relative or absolute paths or URLs. See the +# section "Linking to external documentation" for more information about the use +# of tag files. +# Note: Each tag file must have a unique name (where the name does NOT include +# the path). If a tag file is not located in the directory in which doxygen is +# run, you must also specify the path to the tagfile here. + +TAGFILES = + +# When a file name is specified after GENERATE_TAGFILE, doxygen will create a +# tag file that is based on the input files it reads. See section "Linking to +# external documentation" for more information about the usage of tag files. + +GENERATE_TAGFILE = + +# If the ALLEXTERNALS tag is set to YES, all external class will be listed in +# the class index. If set to NO, only the inherited external classes will be +# listed. +# The default value is: NO. + +ALLEXTERNALS = NO + +# If the EXTERNAL_GROUPS tag is set to YES, all external groups will be listed +# in the modules index. If set to NO, only the current project's groups will be +# listed. +# The default value is: YES. + +EXTERNAL_GROUPS = YES + +# If the EXTERNAL_PAGES tag is set to YES, all external pages will be listed in +# the related pages index. If set to NO, only the current project's pages will +# be listed. +# The default value is: YES. + +EXTERNAL_PAGES = YES + +# The PERL_PATH should be the absolute path and name of the perl script +# interpreter (i.e. the result of 'which perl'). +# The default file (with absolute path) is: /usr/bin/perl. + +PERL_PATH = /usr/bin/perl + +#--------------------------------------------------------------------------- +# Configuration options related to the dot tool +#--------------------------------------------------------------------------- + +# If the CLASS_DIAGRAMS tag is set to YES, doxygen will generate a class diagram +# (in HTML and LaTeX) for classes with base or super classes. Setting the tag to +# NO turns the diagrams off. Note that this option also works with HAVE_DOT +# disabled, but it is recommended to install and use dot, since it yields more +# powerful graphs. +# The default value is: YES. + +CLASS_DIAGRAMS = YES + +# You can define message sequence charts within doxygen comments using the \msc +# command. Doxygen will then run the mscgen tool (see: +# http://www.mcternan.me.uk/mscgen/)) to produce the chart and insert it in the +# documentation. The MSCGEN_PATH tag allows you to specify the directory where +# the mscgen tool resides. If left empty the tool is assumed to be found in the +# default search path. + +MSCGEN_PATH = + +# You can include diagrams made with dia in doxygen documentation. Doxygen will +# then run dia to produce the diagram and insert it in the documentation. The +# DIA_PATH tag allows you to specify the directory where the dia binary resides. +# If left empty dia is assumed to be found in the default search path. + +DIA_PATH = + +# If set to YES the inheritance and collaboration graphs will hide inheritance +# and usage relations if the target is undocumented or is not a class. +# The default value is: YES. + +HIDE_UNDOC_RELATIONS = YES + +# If you set the HAVE_DOT tag to YES then doxygen will assume the dot tool is +# available from the path. This tool is part of Graphviz (see: +# http://www.graphviz.org/), a graph visualization toolkit from AT&T and Lucent +# Bell Labs. The other options in this section have no effect if this option is +# set to NO +# The default value is: NO. + +HAVE_DOT = NO + +# The DOT_NUM_THREADS specifies the number of dot invocations doxygen is allowed +# to run in parallel. When set to 0 doxygen will base this on the number of +# processors available in the system. You can set it explicitly to a value +# larger than 0 to get control over the balance between CPU load and processing +# speed. +# Minimum value: 0, maximum value: 32, default value: 0. +# This tag requires that the tag HAVE_DOT is set to YES. + +DOT_NUM_THREADS = 0 + +# When you want a differently looking font in the dot files that doxygen +# generates you can specify the font name using DOT_FONTNAME. You need to make +# sure dot is able to find the font, which can be done by putting it in a +# standard location or by setting the DOTFONTPATH environment variable or by +# setting DOT_FONTPATH to the directory containing the font. +# The default value is: Helvetica. +# This tag requires that the tag HAVE_DOT is set to YES. + +DOT_FONTNAME = Helvetica + +# The DOT_FONTSIZE tag can be used to set the size (in points) of the font of +# dot graphs. +# Minimum value: 4, maximum value: 24, default value: 10. +# This tag requires that the tag HAVE_DOT is set to YES. + +DOT_FONTSIZE = 10 + +# By default doxygen will tell dot to use the default font as specified with +# DOT_FONTNAME. If you specify a different font using DOT_FONTNAME you can set +# the path where dot can find it using this tag. +# This tag requires that the tag HAVE_DOT is set to YES. + +DOT_FONTPATH = + +# If the CLASS_GRAPH tag is set to YES then doxygen will generate a graph for +# each documented class showing the direct and indirect inheritance relations. +# Setting this tag to YES will force the CLASS_DIAGRAMS tag to NO. +# The default value is: YES. +# This tag requires that the tag HAVE_DOT is set to YES. + +CLASS_GRAPH = YES + +# If the COLLABORATION_GRAPH tag is set to YES then doxygen will generate a +# graph for each documented class showing the direct and indirect implementation +# dependencies (inheritance, containment, and class references variables) of the +# class with other documented classes. +# The default value is: YES. +# This tag requires that the tag HAVE_DOT is set to YES. + +COLLABORATION_GRAPH = YES + +# If the GROUP_GRAPHS tag is set to YES then doxygen will generate a graph for +# groups, showing the direct groups dependencies. +# The default value is: YES. +# This tag requires that the tag HAVE_DOT is set to YES. + +GROUP_GRAPHS = YES + +# If the UML_LOOK tag is set to YES, doxygen will generate inheritance and +# collaboration diagrams in a style similar to the OMG's Unified Modeling +# Language. +# The default value is: NO. +# This tag requires that the tag HAVE_DOT is set to YES. + +UML_LOOK = NO + +# If the UML_LOOK tag is enabled, the fields and methods are shown inside the +# class node. If there are many fields or methods and many nodes the graph may +# become too big to be useful. The UML_LIMIT_NUM_FIELDS threshold limits the +# number of items for each type to make the size more manageable. Set this to 0 +# for no limit. Note that the threshold may be exceeded by 50% before the limit +# is enforced. So when you set the threshold to 10, up to 15 fields may appear, +# but if the number exceeds 15, the total amount of fields shown is limited to +# 10. +# Minimum value: 0, maximum value: 100, default value: 10. +# This tag requires that the tag HAVE_DOT is set to YES. + +UML_LIMIT_NUM_FIELDS = 10 + +# If the TEMPLATE_RELATIONS tag is set to YES then the inheritance and +# collaboration graphs will show the relations between templates and their +# instances. +# The default value is: NO. +# This tag requires that the tag HAVE_DOT is set to YES. + +TEMPLATE_RELATIONS = NO + +# If the INCLUDE_GRAPH, ENABLE_PREPROCESSING and SEARCH_INCLUDES tags are set to +# YES then doxygen will generate a graph for each documented file showing the +# direct and indirect include dependencies of the file with other documented +# files. +# The default value is: YES. +# This tag requires that the tag HAVE_DOT is set to YES. + +INCLUDE_GRAPH = YES + +# If the INCLUDED_BY_GRAPH, ENABLE_PREPROCESSING and SEARCH_INCLUDES tags are +# set to YES then doxygen will generate a graph for each documented file showing +# the direct and indirect include dependencies of the file with other documented +# files. +# The default value is: YES. +# This tag requires that the tag HAVE_DOT is set to YES. + +INCLUDED_BY_GRAPH = YES + +# If the CALL_GRAPH tag is set to YES then doxygen will generate a call +# dependency graph for every global function or class method. +# +# Note that enabling this option will significantly increase the time of a run. +# So in most cases it will be better to enable call graphs for selected +# functions only using the \callgraph command. Disabling a call graph can be +# accomplished by means of the command \hidecallgraph. +# The default value is: NO. +# This tag requires that the tag HAVE_DOT is set to YES. + +CALL_GRAPH = NO + +# If the CALLER_GRAPH tag is set to YES then doxygen will generate a caller +# dependency graph for every global function or class method. +# +# Note that enabling this option will significantly increase the time of a run. +# So in most cases it will be better to enable caller graphs for selected +# functions only using the \callergraph command. Disabling a caller graph can be +# accomplished by means of the command \hidecallergraph. +# The default value is: NO. +# This tag requires that the tag HAVE_DOT is set to YES. + +CALLER_GRAPH = NO + +# If the GRAPHICAL_HIERARCHY tag is set to YES then doxygen will graphical +# hierarchy of all classes instead of a textual one. +# The default value is: YES. +# This tag requires that the tag HAVE_DOT is set to YES. + +GRAPHICAL_HIERARCHY = YES + +# If the DIRECTORY_GRAPH tag is set to YES then doxygen will show the +# dependencies a directory has on other directories in a graphical way. The +# dependency relations are determined by the #include relations between the +# files in the directories. +# The default value is: YES. +# This tag requires that the tag HAVE_DOT is set to YES. + +DIRECTORY_GRAPH = YES + +# The DOT_IMAGE_FORMAT tag can be used to set the image format of the images +# generated by dot. For an explanation of the image formats see the section +# output formats in the documentation of the dot tool (Graphviz (see: +# http://www.graphviz.org/)). +# Note: If you choose svg you need to set HTML_FILE_EXTENSION to xhtml in order +# to make the SVG files visible in IE 9+ (other browsers do not have this +# requirement). +# Possible values are: png, jpg, gif, svg, png:gd, png:gd:gd, png:cairo, +# png:cairo:gd, png:cairo:cairo, png:cairo:gdiplus, png:gdiplus and +# png:gdiplus:gdiplus. +# The default value is: png. +# This tag requires that the tag HAVE_DOT is set to YES. + +DOT_IMAGE_FORMAT = png + +# If DOT_IMAGE_FORMAT is set to svg, then this option can be set to YES to +# enable generation of interactive SVG images that allow zooming and panning. +# +# Note that this requires a modern browser other than Internet Explorer. Tested +# and working are Firefox, Chrome, Safari, and Opera. +# Note: For IE 9+ you need to set HTML_FILE_EXTENSION to xhtml in order to make +# the SVG files visible. Older versions of IE do not have SVG support. +# The default value is: NO. +# This tag requires that the tag HAVE_DOT is set to YES. + +INTERACTIVE_SVG = NO + +# The DOT_PATH tag can be used to specify the path where the dot tool can be +# found. If left blank, it is assumed the dot tool can be found in the path. +# This tag requires that the tag HAVE_DOT is set to YES. + +DOT_PATH = + +# The DOTFILE_DIRS tag can be used to specify one or more directories that +# contain dot files that are included in the documentation (see the \dotfile +# command). +# This tag requires that the tag HAVE_DOT is set to YES. + +DOTFILE_DIRS = + +# The MSCFILE_DIRS tag can be used to specify one or more directories that +# contain msc files that are included in the documentation (see the \mscfile +# command). + +MSCFILE_DIRS = + +# The DIAFILE_DIRS tag can be used to specify one or more directories that +# contain dia files that are included in the documentation (see the \diafile +# command). + +DIAFILE_DIRS = + +# When using plantuml, the PLANTUML_JAR_PATH tag should be used to specify the +# path where java can find the plantuml.jar file. If left blank, it is assumed +# PlantUML is not used or called during a preprocessing step. Doxygen will +# generate a warning when it encounters a \startuml command in this case and +# will not generate output for the diagram. + +PLANTUML_JAR_PATH = + +# When using plantuml, the PLANTUML_CFG_FILE tag can be used to specify a +# configuration file for plantuml. + +PLANTUML_CFG_FILE = + +# When using plantuml, the specified paths are searched for files specified by +# the !include statement in a plantuml block. + +PLANTUML_INCLUDE_PATH = + +# The DOT_GRAPH_MAX_NODES tag can be used to set the maximum number of nodes +# that will be shown in the graph. If the number of nodes in a graph becomes +# larger than this value, doxygen will truncate the graph, which is visualized +# by representing a node as a red box. Note that doxygen if the number of direct +# children of the root node in a graph is already larger than +# DOT_GRAPH_MAX_NODES then the graph will not be shown at all. Also note that +# the size of a graph can be further restricted by MAX_DOT_GRAPH_DEPTH. +# Minimum value: 0, maximum value: 10000, default value: 50. +# This tag requires that the tag HAVE_DOT is set to YES. + +DOT_GRAPH_MAX_NODES = 50 + +# The MAX_DOT_GRAPH_DEPTH tag can be used to set the maximum depth of the graphs +# generated by dot. A depth value of 3 means that only nodes reachable from the +# root by following a path via at most 3 edges will be shown. Nodes that lay +# further from the root node will be omitted. Note that setting this option to 1 +# or 2 may greatly reduce the computation time needed for large code bases. Also +# note that the size of a graph can be further restricted by +# DOT_GRAPH_MAX_NODES. Using a depth of 0 means no depth restriction. +# Minimum value: 0, maximum value: 1000, default value: 0. +# This tag requires that the tag HAVE_DOT is set to YES. + +MAX_DOT_GRAPH_DEPTH = 0 + +# Set the DOT_TRANSPARENT tag to YES to generate images with a transparent +# background. This is disabled by default, because dot on Windows does not seem +# to support this out of the box. +# +# Warning: Depending on the platform used, enabling this option may lead to +# badly anti-aliased labels on the edges of a graph (i.e. they become hard to +# read). +# The default value is: NO. +# This tag requires that the tag HAVE_DOT is set to YES. + +DOT_TRANSPARENT = NO + +# Set the DOT_MULTI_TARGETS tag to YES to allow dot to generate multiple output +# files in one run (i.e. multiple -o and -T options on the command line). This +# makes dot run faster, but since only newer versions of dot (>1.8.10) support +# this, this feature is disabled by default. +# The default value is: NO. +# This tag requires that the tag HAVE_DOT is set to YES. + +DOT_MULTI_TARGETS = NO + +# If the GENERATE_LEGEND tag is set to YES doxygen will generate a legend page +# explaining the meaning of the various boxes and arrows in the dot generated +# graphs. +# The default value is: YES. +# This tag requires that the tag HAVE_DOT is set to YES. + +GENERATE_LEGEND = YES + +# If the DOT_CLEANUP tag is set to YES, doxygen will remove the intermediate dot +# files that are used to generate the various graphs. +# The default value is: YES. +# This tag requires that the tag HAVE_DOT is set to YES. + +DOT_CLEANUP = YES diff --git a/libcrystfel/doc/index.md b/libcrystfel/doc/index.md index a14e62db..f1c72ed4 100644 --- a/libcrystfel/doc/index.md +++ b/libcrystfel/doc/index.md @@ -49,6 +49,7 @@ API documentation * \ref felix.h "Felix indexer interface" * \ref predict-refine.h "Prediction refinement" * \ref integration.h "Integration of reflections" +* \ref datatemplate.h "Contents of geometry files" * \ref detector.h "Detector geometry descriptions" * \ref spectrum.h "Radiation spectrum object" * \ref hdf5-file.h "HDF5 file interface" diff --git a/libcrystfel/meson.build b/libcrystfel/meson.build new file mode 100644 index 00000000..8b8eb77d --- /dev/null +++ b/libcrystfel/meson.build @@ -0,0 +1,185 @@ +# libcrystfel + +zlibdep = dependency('zlib', required : true) +if cc.has_function('gzbuffer', + prefix: '#include <zlib.h>', + dependencies: zlibdep) + conf_data.set10('HAVE_GZBUFFER', 1) +endif + +fftwdep = dependency('fftw3', required : false) +if fftwdep.found() + conf_data.set10('HAVE_FFTW', 1) +endif + +ncursesdep = dependency('ncurses', required: false) +if ncursesdep.found() + conf_data.set10('HAVE_CURSES', 1) +endif + +xgandalfdep = dependency('xgandalf', required: false) +if xgandalfdep.found() + conf_data.set10('HAVE_XGANDALF', 1) +endif + +pinkindexerdep = dependency('pinkIndexer', required: false) +if pinkindexerdep.found() + conf_data.set10('HAVE_PINKINDEXER', 1) +endif + +fdipdep = dependency('fdip', required: false) +if fdipdep.found() + conf_data.set10('HAVE_FDIP', 1) +endif + +libcrystfel_versionc = vcs_tag(input: 'src/libcrystfel-version.c.in', + output: 'libcrystfel-version.c') + + +libcrystfel_includes = include_directories('src') + +# Find forkpty() +utildep = cc.find_library('util', required : true) +if cc.has_function('forkpty', dependencies : utildep, prefix : '#include <pty.h>') + conf_data.set10('HAVE_FORKPTY_PTY_H', 1) +elif cc.has_function('forkpty', dependencies : utildep, prefix : '#include <util.h>') + conf_data.set10('HAVE_FORKPTY_UTIL_H', 1) +else + error('Couldn\'t find forkpty()') +endif + + +# Symmetry operation parser Flex/Bison stuff +flex = find_program('flex') +bison = find_program('bison') + +flex_gen = generator(flex, + output : ['@BASENAME@-lex.c', '@BASENAME@-lex.h'], + arguments : ['--outfile=@OUTPUT0@', + '--header-file=@OUTPUT1@', + '@INPUT@']) + +bison_gen = generator(bison, + output : ['@BASENAME@-parse.c', '@BASENAME@-parse.h'], + arguments : ['--output=@OUTPUT0@', + '--defines=@OUTPUT1@', + '--report=all', + '@INPUT@']) + +symop_parse_ch = bison_gen.process('src/symop.y') +symop_lex_ch = flex_gen.process('src/symop.l') + + +libcrystfel_sources = ['src/image.c', + 'src/cell.c', + 'src/index.c', + 'src/spectrum.c', + 'src/cell-utils.c', + 'src/integer_matrix.c', + 'src/stream.c', + 'src/crystal.c', + 'src/integration.c', + 'src/symmetry.c', + 'src/peakfinder8.c', + 'src/thread-pool.c', + 'src/peaks.c', + 'src/utils.c', + 'src/predict-refine.c', + 'src/filters.c', + 'src/rational.c', + 'src/geometry.c', + 'src/reflist.c', + 'src/reflist-utils.c', + 'src/datatemplate.c', + 'src/colscale.c', + 'src/detgeom.c', + 'src/fom.c', + 'src/image-cbf.c', + 'src/image-hdf5.c', + 'src/indexers/dirax.c', + 'src/indexers/felix.c', + 'src/indexers/mosflm.c', + 'src/indexers/taketwo.c', + 'src/indexers/xds.c', + 'src/indexers/asdf.c', + 'src/indexers/xgandalf.c', + 'src/indexers/pinkindexer.c', + symop_lex_ch, + symop_parse_ch, + ] + +if msgpackdep.found() + libcrystfel_sources += 'src/image-msgpack.c' +endif + +configure_file(input : 'config.h.in', + output : 'config.h', + configuration : conf_data) + +libcrystfel_conf_inc = include_directories('.') + +libcrystfel = library('crystfel', [libcrystfel_sources, libcrystfel_versionc], + include_directories : [libcrystfel_includes, libcrystfel_conf_inc], + dependencies : [mdep, utildep, fftwdep, gsldep, zlibdep, + hdf5dep, pthreaddep, ncursesdep, + xgandalfdep, pinkindexerdep, fdipdep], + install : true) + +libcrystfeldep = declare_dependency(include_directories : libcrystfel_includes, + link_with : libcrystfel, + dependencies: gsldep) + + +install_headers(['src/reflist.h', + 'src/symmetry.h', + 'src/cell.h', + 'src/reflist-utils.h', + 'src/thread-pool.h', + 'src/utils.h', + 'src/geometry.h', + 'src/peakfinder8.h', + 'src/peaks.h', + 'src/stream.h', + 'src/index.h', + 'src/image.h', + 'src/filters.h', + 'src/cell-utils.h', + 'src/integer_matrix.h', + 'src/crystal.h', + 'src/predict-refine.h', + 'src/integration.h', + 'src/peakfinder8.h', + 'src/rational.h', + 'src/spectrum.h', + 'src/datatemplate.h', + 'src/colscale.h', + 'src/detgeom.h', + 'src/image-msgpack.h', + 'src/fom.h'], + subdir: 'crystfel') + +# API documentation (Doxygen) +doxygen = find_program('doxygen') + +index_md = files('doc/index.md') +coding_md = files('doc/coding.md') + +doc_data = configuration_data() +doc_data.set('DOX_TOP', join_paths(meson.current_source_dir(), 'src')) +doc_data.set('INDEX_MD', join_paths(meson.current_source_dir(), 'doc/index.md')) +doc_data.set('CODING_MD', join_paths(meson.current_source_dir(), 'doc/coding.md')) +doc_data.set('OUTPUT_DIR', join_paths(meson.current_build_dir(), 'docs')) +doc_data.set('VERSION', meson.project_version()) + +doxyfile = configure_file(input: 'doc/Doxyfile.in', + output: 'Doxyfile', + configuration: doc_data, + install: false) + +api_docs = run_target('api-docs', command : [doxygen, doxyfile]) + + +# pkg-config file +pkg = import('pkgconfig') +pkg.generate(libcrystfel, + description: 'Data processing for serial crystallography (shared library)') diff --git a/libcrystfel/src/cell-utils.c b/libcrystfel/src/cell-utils.c index d269e703..bb654bc5 100644 --- a/libcrystfel/src/cell-utils.c +++ b/libcrystfel/src/cell-utils.c @@ -3,12 +3,12 @@ * * Unit Cell utility functions * - * Copyright © 2012-2020 Deutsches Elektronen-Synchrotron DESY, + * Copyright © 2012-2021 Deutsches Elektronen-Synchrotron DESY, * a research centre of the Helmholtz Association. * Copyright © 2012 Lorenzo Galli * * Authors: - * 2009-2019 Thomas White <taw@physics.org> + * 2009-2021 Thomas White <taw@physics.org> * 2012 Lorenzo Galli * * This file is part of CrystFEL. @@ -256,6 +256,48 @@ void cell_print(UnitCell *cell) } +void cell_print_oneline(UnitCell *cell) +{ + LatticeType lt; + char cen; + + if ( cell == NULL ) { + STATUS("(NULL cell)\n"); + return; + } + + lt = cell_get_lattice_type(cell); + cen = cell_get_centering(cell); + + STATUS("%s %c", str_lattice(lt), cen); + + if ( (lt==L_MONOCLINIC) || (lt==L_TETRAGONAL) || ( lt==L_HEXAGONAL) + || ( (lt==L_ORTHORHOMBIC) && (cen=='A') ) + || ( (lt==L_ORTHORHOMBIC) && (cen=='B') ) + || ( (lt==L_ORTHORHOMBIC) && (cen=='C') ) ) + { + STATUS(", unique axis %c", cell_get_unique_axis(cell)); + } + + if ( cell_has_parameters(cell) ) { + + double a, b, c, alpha, beta, gamma; + + if ( !right_handed(cell) ) { + STATUS(" (left handed)"); + } + + cell_get_parameters(cell, &a, &b, &c, &alpha, &beta, &gamma); + + STATUS(" %.2f %.2f %.2f A, %.2f %.2f %.2f deg\n", + a*1e10, b*1e10, c*1e10, + rad2deg(alpha), rad2deg(beta), rad2deg(gamma)); + } else { + STATUS(", no cell parameters.\n"); + } +} + + void cell_print_full(UnitCell *cell) { cell_print(cell); @@ -291,8 +333,6 @@ void cell_print_full(UnitCell *cell) rad2deg(angle_between(asx, asy, asz, csx, csy, csz)), rad2deg(angle_between(asx, asy, asz, bsx, bsy, bsz))); - STATUS("Cell representation is %s.\n", cell_rep(cell)); - } } @@ -577,28 +617,18 @@ UnitCell *uncenter_cell(UnitCell *in, IntegerMatrix **pC, RationalMatrix **pCi) /* Return sin(theta)/lambda = 1/2d. Multiply by two if you want 1/d */ double resolution(UnitCell *cell, signed int h, signed int k, signed int l) { - double a, b, c, alpha, beta, gamma; - - cell_get_parameters(cell, &a, &b, &c, &alpha, &beta, &gamma); - - const double Vsq = a*a*b*b*c*c*(1 - cos(alpha)*cos(alpha) - - cos(beta)*cos(beta) - - cos(gamma)*cos(gamma) - + 2*cos(alpha)*cos(beta)*cos(gamma) ); - - const double S11 = b*b*c*c*sin(alpha)*sin(alpha); - const double S22 = a*a*c*c*sin(beta)*sin(beta); - const double S33 = a*a*b*b*sin(gamma)*sin(gamma); - const double S12 = a*b*c*c*(cos(alpha)*cos(beta) - cos(gamma)); - const double S23 = a*a*b*c*(cos(beta)*cos(gamma) - cos(alpha)); - const double S13 = a*b*b*c*(cos(gamma)*cos(alpha) - cos(beta)); + double asx, asy, asz; + double bsx, bsy, bsz; + double csx, csy, csz; - const double brackets = S11*h*h + S22*k*k + S33*l*l - + 2*S12*h*k + 2*S23*k*l + 2*S13*h*l; - const double oneoverdsq = brackets / Vsq; - const double oneoverd = sqrt(oneoverdsq); + cell_get_reciprocal(cell, + &asx, &asy, &asz, + &bsx, &bsy, &bsz, + &csx, &csy, &csz); - return oneoverd / 2; + return modulus(h*asx + k*bsx + l*csx, + h*asy + k*bsy + l*csy, + h*asz + k*bsz + l*csz) / 2.0; } @@ -1271,6 +1301,49 @@ double cell_get_volume(UnitCell *cell) /** + * \param cell: A %UnitCell + * + * \returns the value of 1/d for the lowest order reflection + * that is not systematically absent according to the centering. + * + */ +double lowest_reflection(UnitCell *cell) +{ + signed int h, k, l; + double lowres = INFINITY; + + /* FIXME: Inelegant and nasty. Anyone want to work out + * all the possible cases? */ + for ( h=0; h<4; h++ ) { + for ( k=0; k<4; k++ ) { + for ( l=0; l<4; l++ ) { + if ( (h==0) && (k==0) && (l==0) ) continue; + if ( !forbidden_reflection(cell, h, k, l) ) { + double r = resolution(cell, h, k, l); + if ( r < lowres ) { + lowres = r; + } + } + } + } + } + return lowres; +} + + +/* Return true if the two centering symbols are identical, + * or if they are a pair of R/P, which should be considered the + * same for the purposes of cell comparison */ +static int centering_equivalent(char cen1, char cen2) +{ + if ( cen1 == cen2 ) return 1; + if ( (cen1=='P') && (cen2=='R') ) return 1; + if ( (cen1=='R') && (cen2=='P') ) return 1; + return 0; +} + + +/** * \param cell: A UnitCell * \param reference: Another UnitCell * \param tols: Pointer to tolerances for a,b,c (fractional), al,be,ga (radians) @@ -1294,11 +1367,13 @@ int compare_cell_parameters(UnitCell *cell, UnitCell *reference, /* Centering must match: we don't arbitrate primitive vs centered, * different cell choices etc */ - if ( cell_get_centering(cell) != cell_get_centering(reference) ) return 0; + if ( !centering_equivalent(cell_get_centering(cell), + cell_get_centering(reference)) ) return 0; cell_get_parameters(cell, &a1, &b1, &c1, &al1, &be1, &ga1); cell_get_parameters(reference, &a2, &b2, &c2, &al2, &be2, &ga2); + /* within_tolerance() takes a percentage */ if ( !within_tolerance(a1, a2, tols[0]*100.0) ) return 0; if ( !within_tolerance(b1, b2, tols[1]*100.0) ) return 0; if ( !within_tolerance(c1, c2, tols[2]*100.0) ) return 0; @@ -1344,6 +1419,7 @@ static double moduli_check(double ax, double ay, double az, * \returns non-zero if the cells match. * */ +/* 'tols' is in frac (not %) and radians */ int compare_cell_parameters_and_orientation(UnitCell *cell, UnitCell *reference, const double *tols) { @@ -1399,6 +1475,7 @@ int compare_cell_parameters_and_orientation(UnitCell *cell, UnitCell *reference, * \returns non-zero if the cells match. * */ +/* 'tols' is in frac (not %) and radians */ int compare_permuted_cell_parameters_and_orientation(UnitCell *cell, UnitCell *reference, const double *tols, diff --git a/libcrystfel/src/cell-utils.h b/libcrystfel/src/cell-utils.h index 1c356588..8eefe800 100644 --- a/libcrystfel/src/cell-utils.h +++ b/libcrystfel/src/cell-utils.h @@ -3,12 +3,12 @@ * * Unit Cell utility functions * - * Copyright © 2012-2020 Deutsches Elektronen-Synchrotron DESY, + * Copyright © 2012-2021 Deutsches Elektronen-Synchrotron DESY, * a research centre of the Helmholtz Association. * Copyright © 2012 Lorenzo Galli * * Authors: - * 2009-2018 Thomas White <taw@physics.org> + * 2009-2020 Thomas White <taw@physics.org> * 2012 Lorenzo Galli * * This file is part of CrystFEL. @@ -31,10 +31,6 @@ #ifndef CELL_UTILS_H #define CELL_UTILS_H -#ifdef HAVE_CONFIG_H -#include <config.h> -#endif - #include <gsl/gsl_matrix.h> #include "cell.h" @@ -57,6 +53,7 @@ extern UnitCell *rotate_cell(UnitCell *in, double omega, double phi, double rot); extern void cell_print(UnitCell *cell); +extern void cell_print_oneline(UnitCell *cell); extern void cell_print_full(UnitCell *cell); extern UnitCell *load_cell_from_pdb(const char *filename); @@ -82,6 +79,8 @@ extern int forbidden_reflection(UnitCell *cell, extern double cell_get_volume(UnitCell *cell); +extern double lowest_reflection(UnitCell *cell); + extern int compare_cell_parameters(UnitCell *cell, UnitCell *reference, const double *tols); diff --git a/libcrystfel/src/cell.c b/libcrystfel/src/cell.c index f6ed412d..2e944ddb 100644 --- a/libcrystfel/src/cell.c +++ b/libcrystfel/src/cell.c @@ -3,15 +3,15 @@ * * A class representing a unit cell * - * Copyright © 2012-2020 Deutsches Elektronen-Synchrotron DESY, + * Copyright © 2012-2021 Deutsches Elektronen-Synchrotron DESY, * a research centre of the Helmholtz Association. * Copyright © 2012 Richard Kirian * Copyright © 2012 Lorenzo Galli * * Authors: - * 2009-2012,2014,2017 Thomas White <taw@physics.org> - * 2010 Richard Kirian - * 2012 Lorenzo Galli + * 2009-2021 Thomas White <taw@physics.org> + * 2010 Richard Kirian + * 2012 Lorenzo Galli * * This file is part of CrystFEL. * @@ -35,6 +35,7 @@ #endif #include <math.h> +#include <assert.h> #include <stdlib.h> #include <stdio.h> #include <string.h> @@ -54,19 +55,14 @@ */ -typedef enum { - CELL_REP_CRYST, - CELL_REP_CART, - CELL_REP_RECIP -} CellRepresentation; - struct _unitcell { - CellRepresentation rep; - - int have_parameters; + LatticeType lattice_type; + char centering; + char unique_axis; /* Crystallographic representation */ + int have_cryst; double a; /* m */ double b; /* m */ double c; /* m */ @@ -75,18 +71,16 @@ struct _unitcell { double gamma; /* Radians */ /* Cartesian representation */ + int have_cart; double ax; double bx; double cx; double ay; double by; double cy; double az; double bz; double cz; /* Cartesian representation of reciprocal axes */ + int have_recip; double axs; double bxs; double cxs; double ays; double bys; double cys; double azs; double bzs; double czs; - - LatticeType lattice_type; - char centering; - char unique_axis; }; typedef enum { @@ -127,12 +121,13 @@ UnitCell *cell_new() cell->beta = 0.0; cell->gamma = 0.0; - cell->rep = CELL_REP_CRYST; + cell->have_cryst = 0; + cell->have_cart = 0; + cell->have_recip = 0; cell->lattice_type = L_TRICLINIC; cell->centering = 'P'; cell->unique_axis = '?'; - cell->have_parameters = 0; return cell; } @@ -157,10 +152,12 @@ void cell_free(UnitCell *cell) * \returns True if cell has its parameters specified. * */ -int cell_has_parameters(UnitCell *cell) +int cell_has_parameters(const UnitCell *cell) { if ( cell == NULL ) return 0; - return cell->have_parameters; + return (cell->have_cryst > 0) + || (cell->have_cart > 0 ) + || (cell->have_recip > 0); } @@ -176,8 +173,9 @@ void cell_set_parameters(UnitCell *cell, double a, double b, double c, cell->beta = beta; cell->gamma = gamma; - cell->rep = CELL_REP_CRYST; - cell->have_parameters = 1; + cell->have_cryst = 1; + cell->have_cart = 0; + cell->have_recip = 0; } @@ -192,8 +190,9 @@ void cell_set_cartesian(UnitCell *cell, cell->bx = bx; cell->by = by; cell->bz = bz; cell->cx = cx; cell->cy = cy; cell->cz = cz; - cell->rep = CELL_REP_CART; - cell->have_parameters = 1; + cell->have_cryst = 0; + cell->have_cart = 1; + cell->have_recip = 0; } @@ -223,8 +222,9 @@ UnitCell *cell_new_from_reciprocal_axes(struct rvec as, struct rvec bs, cell->bxs = bs.u; cell->bys = bs.v; cell->bzs = bs.w; cell->cxs = cs.u; cell->cys = cs.v; cell->czs = cs.w; - cell->rep = CELL_REP_RECIP; - cell->have_parameters = 1; + cell->have_cryst = 0; + cell->have_cart = 0; + cell->have_recip = 1; return cell; } @@ -241,8 +241,9 @@ UnitCell *cell_new_from_direct_axes(struct rvec a, struct rvec b, struct rvec c) cell->bx = b.u; cell->by = b.v; cell->bz = b.w; cell->cx = c.u; cell->cy = c.v; cell->cz = c.w; - cell->rep = CELL_REP_CART; - cell->have_parameters = 1; + cell->have_cryst = 0; + cell->have_cart = 1; + cell->have_recip = 0; return cell; } @@ -268,8 +269,9 @@ void cell_set_reciprocal(UnitCell *cell, cell->bxs = bsx; cell->bys = bsy; cell->bzs = bsz; cell->cxs = csx; cell->cys = csy; cell->czs = csz; - cell->rep = CELL_REP_RECIP; - cell->have_parameters = 1; + cell->have_cryst = 0; + cell->have_cart = 0; + cell->have_recip = 1; } @@ -291,31 +293,25 @@ void cell_set_unique_axis(UnitCell *cell, char unique_axis) } -/************************* Getter helper functions ****************************/ +/************************* Conversion functions ****************************/ -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) +static void crystallographic_to_cartesian(UnitCell *cell) { double tmp, V, cosalphastar, cstar; - if ( !cell->have_parameters ) { - ERROR("Unit cell has unspecified parameters.\n"); - return 1; - } + assert(cell->have_cryst == 1); /* Firstly: Get a in terms of x, y and z * +a (cryst) is defined to lie along +x (cart) */ - *ax = cell->a; - *ay = 0.0; - *az = 0.0; + cell->ax = cell->a; + cell->ay = 0.0; + cell->az = 0.0; /* b in terms of x, y and z * b (cryst) is defined to lie in the xy (cart) plane */ - *bx = cell->b*cos(cell->gamma); - *by = cell->b*sin(cell->gamma); - *bz = 0.0; + cell->bx = cell->b*cos(cell->gamma); + cell->by = cell->b*sin(cell->gamma); + cell->bz = 0.0; tmp = cos(cell->alpha)*cos(cell->alpha) + cos(cell->beta)*cos(cell->beta) @@ -329,21 +325,41 @@ static int cell_crystallographic_to_cartesian(const UnitCell *cell, cstar = (cell->a * cell->b * sin(cell->gamma))/V; /* c in terms of x, y and z */ - *cx = cell->c*cos(cell->beta); - *cy = -cell->c*sin(cell->beta)*cosalphastar; - *cz = 1.0/cstar; + cell->cx = cell->c*cos(cell->beta); + cell->cy = -cell->c*sin(cell->beta)*cosalphastar; + cell->cz = 1.0/cstar; - return 0; + cell->have_cart = 1; +} + + +static void cartesian_to_crystallographic(UnitCell *cell) +{ + assert(cell->have_cart); + + /* Convert cartesian -> crystallographic */ + cell->a = modulus(cell->ax, cell->ay, cell->az); + cell->b = modulus(cell->bx, cell->by, cell->bz); + cell->c = modulus(cell->cx, cell->cy, cell->cz); + + cell->alpha = angle_between(cell->bx, cell->by, cell->bz, + cell->cx, cell->cy, cell->cz); + cell->beta = angle_between(cell->ax, cell->ay, cell->az, + cell->cx, cell->cy, cell->cz); + cell->gamma = angle_between(cell->ax, cell->ay, cell->az, + cell->bx, cell->by, cell->bz); + + cell->have_cryst = 1; } /* Why yes, I do enjoy long argument lists...! */ -static int cell_invert(double ax, double ay, double az, - double bx, double by, double bz, - double cx, double cy, double cz, - double *asx, double *asy, double *asz, - double *bsx, double *bsy, double *bsz, - double *csx, double *csy, double *csz) +static int invert(double ax, double ay, double az, + double bx, double by, double bz, + double cx, double cy, double cz, + double *asx, double *asy, double *asz, + double *bsx, double *bsy, double *bsz, + double *csx, double *csy, double *csz) { int s; gsl_matrix *m; @@ -413,167 +429,153 @@ static int cell_invert(double ax, double ay, double az, } +static int reciprocal_to_cartesian(UnitCell *cell) +{ + assert(cell->have_recip); + + if ( invert(cell->axs, cell->ays, cell->azs, + cell->bxs, cell->bys, cell->bzs, + cell->cxs, cell->cys, cell->czs, + &cell->ax, &cell->ay, &cell->az, + &cell->bx, &cell->by, &cell->bz, + &cell->cx, &cell->cy, &cell->cz) ) return 1; + + cell->have_cart = 1; + return 0; +} + + +static int cartesian_to_reciprocal(UnitCell *cell) +{ + assert(cell->have_cart); + + if ( invert(cell->ax, cell->ay, cell->az, + cell->bx, cell->by, cell->bz, + cell->cx, cell->cy, cell->cz, + &cell->axs, &cell->ays, &cell->azs, + &cell->bxs, &cell->bys, &cell->bzs, + &cell->cxs, &cell->cys, &cell->czs) ) return 1; + + cell->have_recip = 1; + return 0; +} + + /********************************** Getters ***********************************/ -int cell_get_parameters(const UnitCell *cell, double *a, double *b, double *c, +int cell_get_parameters(UnitCell *cell, + double *a, double *b, double *c, double *alpha, double *beta, double *gamma) { - double ax, ay, az, bx, by, bz, cx, cy, cz; - if ( cell == NULL ) return 1; - if ( !cell->have_parameters ) { + if ( cell->have_cryst ) { + + /* Nothing to do */ + + } else if ( cell->have_cart ) { + + cartesian_to_crystallographic(cell); + + } else if ( cell->have_recip ) { + + if ( reciprocal_to_cartesian(cell) ) return 1; + cartesian_to_crystallographic(cell); + + } else { + ERROR("Unit cell has unspecified parameters.\n"); return 1; - } - switch ( cell->rep ) { - - case CELL_REP_CRYST: - /* Direct response */ - *a = cell->a; - *b = cell->b; - *c = cell->c; - *alpha = cell->alpha; - *beta = cell->beta; - *gamma = cell->gamma; - return 0; - - case CELL_REP_CART: - /* Convert cartesian -> crystallographic */ - *a = modulus(cell->ax, cell->ay, cell->az); - *b = modulus(cell->bx, cell->by, cell->bz); - *c = modulus(cell->cx, cell->cy, cell->cz); - - *alpha = angle_between(cell->bx, cell->by, cell->bz, - cell->cx, cell->cy, cell->cz); - *beta = angle_between(cell->ax, cell->ay, cell->az, - cell->cx, cell->cy, cell->cz); - *gamma = angle_between(cell->ax, cell->ay, cell->az, - cell->bx, cell->by, cell->bz); - return 0; - - case CELL_REP_RECIP: - /* Convert reciprocal -> crystallographic. - * Start by converting reciprocal -> cartesian */ - if ( cell_invert(cell->axs, cell->ays, cell->azs, - cell->bxs, cell->bys, cell->bzs, - cell->cxs, cell->cys, cell->czs, - &ax, &ay, &az, - &bx, &by, &bz, - &cx, &cy, &cz) ) return 1; - - /* Now convert cartesian -> crystallographic */ - *a = modulus(ax, ay, az); - *b = modulus(bx, by, bz); - *c = modulus(cx, cy, cz); - - *alpha = angle_between(bx, by, bz, cx, cy, cz); - *beta = angle_between(ax, ay, az, cx, cy, cz); - *gamma = angle_between(ax, ay, az, bx, by, bz); - return 0; } - return 1; + *a = cell->a; + *b = cell->b; + *c = cell->c; + *alpha = cell->alpha; + *beta = cell->beta; + *gamma = cell->gamma; + return 0; } -int cell_get_cartesian(const UnitCell *cell, +int cell_get_cartesian(UnitCell *cell, double *ax, double *ay, double *az, double *bx, double *by, double *bz, double *cx, double *cy, double *cz) { if ( cell == NULL ) return 1; - if ( !cell->have_parameters ) { + if ( cell->have_cart ) { + + /* Nothing to do */ + + } else if ( cell->have_recip ) { + + /* NB recip->cart has priority over + * cryst->cart, to preserve orientation */ + reciprocal_to_cartesian(cell); + + } else if ( cell->have_cryst ) { + + crystallographic_to_cartesian(cell); + + } else { + ERROR("Unit cell has unspecified parameters.\n"); return 1; - } - - switch ( cell->rep ) { - - case CELL_REP_CRYST: - /* Convert crystallographic -> cartesian. */ - return cell_crystallographic_to_cartesian(cell, - ax, ay, az, - bx, by, bz, - cx, cy, cz); - - case CELL_REP_CART: - /* Direct response */ - *ax = cell->ax; *ay = cell->ay; *az = cell->az; - *bx = cell->bx; *by = cell->by; *bz = cell->bz; - *cx = cell->cx; *cy = cell->cy; *cz = cell->cz; - return 0; - - case CELL_REP_RECIP: - /* Convert reciprocal -> cartesian */ - return cell_invert(cell->axs, cell->ays, cell->azs, - cell->bxs, cell->bys, cell->bzs, - cell->cxs, cell->cys, cell->czs, - ax, ay, az, bx, by, bz, cx, cy, cz); } - return 1; + *ax = cell->ax; *ay = cell->ay; *az = cell->az; + *bx = cell->bx; *by = cell->by; *bz = cell->bz; + *cx = cell->cx; *cy = cell->cy; *cz = cell->cz; + return 0; } -int cell_get_reciprocal(const UnitCell *cell, +int cell_get_reciprocal(UnitCell *cell, double *asx, double *asy, double *asz, double *bsx, double *bsy, double *bsz, double *csx, double *csy, double *csz) { - int r; - double ax, ay, az, bx, by, bz, cx, cy, cz; - if ( cell == NULL ) return 1; - if ( !cell->have_parameters ) { + if ( cell->have_recip ) { + + /* Nothing to do */ + + } else if ( cell->have_cart ) { + + /* NB cart->recip has priority over cryst->recip */ + cartesian_to_reciprocal(cell); + + } else if ( cell->have_cryst ) { + + crystallographic_to_cartesian(cell); + cartesian_to_reciprocal(cell); + + } else { + ERROR("Unit cell has unspecified parameters.\n"); return 1; - } - - switch ( cell->rep ) { - - case CELL_REP_CRYST: - /* Convert crystallographic -> reciprocal */ - r = cell_crystallographic_to_cartesian(cell, - &ax, &ay, &az, - &bx, &by, &bz, - &cx, &cy, &cz); - if ( r ) return r; - return cell_invert(ax, ay, az,bx, by, bz, cx, cy, cz, - asx, asy, asz, bsx, bsy, bsz, csx, csy, csz); - - case CELL_REP_CART: - /* Convert cartesian -> reciprocal */ - cell_invert(cell->ax, cell->ay, cell->az, - cell->bx, cell->by, cell->bz, - cell->cx, cell->cy, cell->cz, - asx, asy, asz, bsx, bsy, bsz, csx, csy, csz); - return 0; - - case CELL_REP_RECIP: - /* Direct response */ - *asx = cell->axs; *asy = cell->ays; *asz = cell->azs; - *bsx = cell->bxs; *bsy = cell->bys; *bsz = cell->bzs; - *csx = cell->cxs; *csy = cell->cys; *csz = cell->czs; - return 0; } - return 1; + *asx = cell->axs; *asy = cell->ays; *asz = cell->azs; + *bsx = cell->bxs; *bsy = cell->bys; *bsz = cell->bzs; + *csx = cell->cxs; *csy = cell->cys; *csz = cell->czs; + return 0; } -char cell_get_centering(UnitCell *cell) +char cell_get_centering(const UnitCell *cell) { return cell->centering; } -LatticeType cell_get_lattice_type(UnitCell *cell) +LatticeType cell_get_lattice_type(const UnitCell *cell) { return cell->lattice_type; } @@ -594,31 +596,12 @@ struct g6 cell_get_G6(UnitCell *cell) } -char cell_get_unique_axis(UnitCell *cell) +char cell_get_unique_axis(const UnitCell *cell) { return cell->unique_axis; } -const char *cell_rep(UnitCell *cell) -{ - switch ( cell->rep ) { - - case CELL_REP_CRYST: - return "crystallographic, direct space"; - - case CELL_REP_CART: - return "cartesian, direct space"; - - case CELL_REP_RECIP: - return "cartesian, reciprocal space"; - - } - - return "unknown"; -} - - UnitCell *cell_transform_gsl_direct(UnitCell *in, gsl_matrix *m) { gsl_matrix *c; diff --git a/libcrystfel/src/cell.h b/libcrystfel/src/cell.h index 3deb92ff..ed82fff7 100644 --- a/libcrystfel/src/cell.h +++ b/libcrystfel/src/cell.h @@ -3,15 +3,15 @@ * * A class representing a unit cell * - * Copyright © 2012-2020 Deutsches Elektronen-Synchrotron DESY, + * Copyright © 2012-2021 Deutsches Elektronen-Synchrotron DESY, * a research centre of the Helmholtz Association. * Copyright © 2012 Richard Kirian * Copyright © 2012 Lorenzo Galli * * Authors: - * 2009-2012,2014,2017 Thomas White <taw@physics.org> - * 2010,2012 Richard Kirian - * 2012 Lorenzo Galli + * 2009-2021 Thomas White <taw@physics.org> + * 2010-2012 Richard Kirian + * 2012 Lorenzo Galli * * This file is part of CrystFEL. * @@ -33,10 +33,6 @@ #ifndef CELL_H #define CELL_H -#ifdef HAVE_CONFIG_H -#include <config.h> -#endif - #include "utils.h" #include "integer_matrix.h" @@ -101,7 +97,7 @@ extern UnitCell *cell_new_from_reciprocal_axes(struct rvec as, struct rvec bs, extern UnitCell *cell_new_from_direct_axes(struct rvec as, struct rvec bs, struct rvec cs); -extern int cell_has_parameters(UnitCell *cell); +extern int cell_has_parameters(const UnitCell *cell); extern void cell_set_cartesian(UnitCell *cell, double ax, double ay, double az, @@ -111,15 +107,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(const UnitCell *cell, double *a, double *b, double *c, +extern int cell_get_parameters(UnitCell *cell, double *a, double *b, double *c, double *alpha, double *beta, double *gamma); -extern int cell_get_cartesian(const UnitCell *cell, +extern int cell_get_cartesian(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(const UnitCell *cell, +extern int cell_get_reciprocal(UnitCell *cell, double *asx, double *asy, double *asz, double *bsx, double *bsy, double *bsz, double *csx, double *csy, double *csz); @@ -129,7 +125,7 @@ extern void cell_set_reciprocal(UnitCell *cell, double bsx, double bsy, double bsz, double csx, double csy, double csz); -extern LatticeType cell_get_lattice_type(UnitCell *cell); +extern LatticeType cell_get_lattice_type(const UnitCell *cell); extern void cell_set_lattice_type(UnitCell *cell, LatticeType lattice_type); struct g6 @@ -144,14 +140,12 @@ struct g6 extern struct g6 cell_get_G6(UnitCell *cell); -extern char cell_get_centering(UnitCell *cell); +extern char cell_get_centering(const UnitCell *cell); extern void cell_set_centering(UnitCell *cell, char centering); -extern char cell_get_unique_axis(UnitCell *cell); +extern char cell_get_unique_axis(const UnitCell *cell); extern void cell_set_unique_axis(UnitCell *cell, char unique_axis); -extern const char *cell_rep(UnitCell *cell); - extern UnitCell *cell_transform_gsl_direct(UnitCell *in, gsl_matrix *m); extern UnitCell *cell_transform_rational(UnitCell *cell, RationalMatrix *m); diff --git a/libcrystfel/src/render.c b/libcrystfel/src/colscale.c index 284f4c46..4a39de8d 100644 --- a/libcrystfel/src/render.c +++ b/libcrystfel/src/colscale.c @@ -1,13 +1,13 @@ /* - * render.c + * colscale.c * - * Render a high dynamic range buffer in some sensible way + * Colour scales * - * Copyright © 2012-2020 Deutsches Elektronen-Synchrotron DESY, + * Copyright © 2012-2021 Deutsches Elektronen-Synchrotron DESY, * a research centre of the Helmholtz Association. * * Authors: - * 2009-2012,2014 Thomas White <taw@physics.org> + * 2009-2020 Thomas White <taw@physics.org> * * This file is part of CrystFEL. * @@ -35,14 +35,9 @@ #include <math.h> #include <stdint.h> +#include "colscale.h" -#include "hdf5-file.h" -#include "render.h" -#include "peaks.h" -#include "filters.h" -#include "utils.h" - -/** \file render.h */ +/** \file colscale.h */ static void render_rgb(double val, double max, double *rp, double *gp, double *bp) @@ -223,8 +218,8 @@ static void render_invmono(double val, double max, } -void render_scale(double val, double max, int scale, - double *rp, double *gp, double *bp) +void colscale_lookup(double val, double max, int scale, + double *rp, double *gp, double *bp) { switch ( scale ) { diff --git a/libcrystfel/src/render.h b/libcrystfel/src/colscale.h index a3292515..972b659b 100644 --- a/libcrystfel/src/render.h +++ b/libcrystfel/src/colscale.h @@ -1,13 +1,13 @@ /* - * render.h + * colscale.h * - * Render a high dynamic range buffer in some sensible way + * Colour scales * - * Copyright © 2012-2020 Deutsches Elektronen-Synchrotron DESY, + * Copyright © 2012-2021 Deutsches Elektronen-Synchrotron DESY, * a research centre of the Helmholtz Association. * * Authors: - * 2009-2012 Thomas White <taw@physics.org> + * 2009-2020 Thomas White <taw@physics.org> * * This file is part of CrystFEL. * @@ -26,16 +26,12 @@ * */ -#ifdef HAVE_CONFIG_H -#include <config.h> -#endif - -#ifndef RENDER_H -#define RENDER_H +#ifndef COLSCALE_H +#define COLSCALE_H /** - * \file render.h - * Colour scale for rendering + * \file colscale.h + * Colour scales for rendering */ enum { @@ -51,12 +47,12 @@ extern "C" { #endif /* Colour scale lookup */ -extern void render_scale(double val, double max, int scale, - double *rp, double *gp, double *bp); +extern void colscale_lookup(double val, double max, int scale, + double *rp, double *gp, double *bp); #ifdef __cplusplus } #endif -#endif /* RENDER_H */ +#endif /* COLSCALE_H */ diff --git a/libcrystfel/src/crystal.c b/libcrystfel/src/crystal.c index 9202c668..c9f59bb7 100644 --- a/libcrystfel/src/crystal.c +++ b/libcrystfel/src/crystal.c @@ -3,11 +3,11 @@ * * A class representing a single crystal * - * Copyright © 2013-2020 Deutsches Elektronen-Synchrotron DESY, + * Copyright © 2013-2021 Deutsches Elektronen-Synchrotron DESY, * a research centre of the Helmholtz Association. * * Authors: - * 2013-2016 Thomas White <taw@physics.org> + * 2013-2020 Thomas White <taw@physics.org> * 2016 Valerio Mariani * * This file is part of CrystFEL. @@ -33,6 +33,7 @@ #include "crystal.h" #include "utils.h" +#include "reflist-utils.h" /** @@ -66,7 +67,7 @@ struct _crystal /* Text notes, which go in the stream */ char *notes; - /* Detector shift */ + /* Detector shift in metres */ double det_shift_x; double det_shift_y; }; @@ -127,6 +128,44 @@ Crystal *crystal_copy(const Crystal *cryst) /** + * \param cryst: A \ref Crystal to copy. + * + * Creates a new \ref Crystal which is a copy of \p cryst. The copy is a "deep + * copy", which means that copies ARE made of the data structures which + * \p cryst contains references to, for example its \ref RefList. + * + * \returns A (deep) copy of \p cryst, or NULL on failure. + * + */ +Crystal *crystal_copy_deep(const Crystal *cryst) +{ + Crystal *c; + + c = crystal_new(); + if ( c == NULL ) return NULL; + + memcpy(c, cryst, sizeof(Crystal)); + if ( c->notes != NULL ) c->notes = strdup(c->notes); + + if ( cryst->cell != NULL ) { + UnitCell *cell; + cell = cell_new_from_cell(cryst->cell); + if ( cell == NULL ) return NULL; + c->cell = cell; + } + + if ( cryst->reflections != NULL ) { + RefList *refls; + refls = copy_reflist(cryst->reflections); + if ( refls == NULL ) return NULL; + c->reflections = refls; + } + + return c; +} + + +/** * \param cryst: A \ref Crystal to free. * * Frees a \ref Crystal, and all internal resources concerning that crystal. diff --git a/libcrystfel/src/crystal.h b/libcrystfel/src/crystal.h index 669c173f..5a4ca3f6 100644 --- a/libcrystfel/src/crystal.h +++ b/libcrystfel/src/crystal.h @@ -3,11 +3,11 @@ * * A class representing a single crystal * - * Copyright © 2013-2020 Deutsches Elektronen-Synchrotron DESY, + * Copyright © 2013-2021 Deutsches Elektronen-Synchrotron DESY, * a research centre of the Helmholtz Association. * * Authors: - * 2013-2016 Thomas White <taw@physics.org> + * 2013-2020 Thomas White <taw@physics.org> * 2016 Valerio Mariani * * This file is part of CrystFEL. @@ -30,11 +30,6 @@ #ifndef CRYSTAL_H #define CRYSTAL_H -#ifdef HAVE_CONFIG_H -#include <config.h> -#endif - - #include "cell.h" /** @@ -56,6 +51,7 @@ extern "C" { extern Crystal *crystal_new(void); extern Crystal *crystal_copy(const Crystal *cryst); +extern Crystal *crystal_copy_deep(const Crystal *cryst); extern void crystal_free(Crystal *cryst); extern UnitCell *crystal_get_cell(Crystal *cryst); diff --git a/libcrystfel/src/datatemplate.c b/libcrystfel/src/datatemplate.c new file mode 100644 index 00000000..290b9227 --- /dev/null +++ b/libcrystfel/src/datatemplate.c @@ -0,0 +1,1683 @@ +/* + * datatemplate.c + * + * Data template structure + * + * Copyright © 2019-2021 Deutsches Elektronen-Synchrotron DESY, + * a research centre of the Helmholtz Association. + * + * Authors: + * 2019-2021 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 <math.h> +#include <string.h> +#include <assert.h> +#include <ctype.h> + +#include "utils.h" +#include "datatemplate.h" + +#include "datatemplate_priv.h" + + +/** + * \file datatemplate.h + */ + +struct rg_definition { + char *name; + char *pns; +}; + + +struct rgc_definition { + char *name; + char *rgs; +}; + + +static struct panel_template *new_panel(DataTemplate *det, + const char *name, + struct panel_template *defaults) +{ + struct panel_template *new; + int i; + + det->n_panels++; + det->panels = realloc(det->panels, + det->n_panels*sizeof(struct panel_template)); + + new = &det->panels[det->n_panels-1]; + memcpy(new, defaults, sizeof(struct panel_template)); + + /* Set name */ + new->name = strdup(name); + + /* Copy strings */ + new->cnz_from = safe_strdup(defaults->cnz_from); + new->data = safe_strdup(defaults->data); + new->satmap = safe_strdup(defaults->satmap); + new->satmap_file = safe_strdup(defaults->satmap_file); + for ( i=0; i<MAX_MASKS; i++ ) { + new->masks[i].data_location = safe_strdup(defaults->masks[i].data_location); + new->masks[i].filename = safe_strdup(defaults->masks[i].filename); + } + + return new; +} + + +static struct dt_badregion *new_bad_region(DataTemplate *det, const char *name) +{ + struct dt_badregion *new; + + det->n_bad++; + det->bad = realloc(det->bad, det->n_bad*sizeof(struct dt_badregion)); + + new = &det->bad[det->n_bad-1]; + new->min_x = NAN; + new->max_x = NAN; + new->min_y = NAN; + new->max_y = NAN; + new->min_fs = 0; + new->max_fs = 0; + new->min_ss = 0; + new->max_ss = 0; + new->is_fsss = 99; /* Slightly nasty: means "unassigned" */ + new->panel_name = NULL; + new->panel_number = 0; /* Needs to be set after loading */ + strcpy(new->name, name); + + return new; +} + + +static struct panel_template *find_panel_by_name(DataTemplate *det, + const char *name) +{ + int i; + + for ( i=0; i<det->n_panels; i++ ) { + if ( strcmp(det->panels[i].name, name) == 0 ) { + return &det->panels[i]; + } + } + + return NULL; +} + + +static struct dt_badregion *find_bad_region_by_name(DataTemplate *det, + const char *name) +{ + int i; + + for ( i=0; i<det->n_bad; i++ ) { + if ( strcmp(det->bad[i].name, name) == 0 ) { + return &det->bad[i]; + } + } + + return NULL; +} + + +static struct rigid_group *find_or_add_rg(DataTemplate *det, + const char *name) +{ + int i; + struct rigid_group **new; + struct rigid_group *rg; + + for ( i=0; i<det->n_rigid_groups; i++ ) { + + if ( strcmp(det->rigid_groups[i]->name, name) == 0 ) { + return det->rigid_groups[i]; + } + + } + + new = realloc(det->rigid_groups, + (1+det->n_rigid_groups)*sizeof(struct rigid_group *)); + if ( new == NULL ) return NULL; + + det->rigid_groups = new; + + rg = malloc(sizeof(struct rigid_group)); + if ( rg == NULL ) return NULL; + + det->rigid_groups[det->n_rigid_groups++] = rg; + + rg->name = strdup(name); + rg->panel_numbers = NULL; + rg->n_panels = 0; + + return rg; +} + + +static struct rg_collection *find_or_add_rg_coll(DataTemplate *det, + const char *name) +{ + int i; + struct rg_collection **new; + struct rg_collection *rgc; + + for ( i=0; i<det->n_rg_collections; i++ ) { + if ( strcmp(det->rigid_group_collections[i]->name, name) == 0 ) + { + return det->rigid_group_collections[i]; + } + } + + new = realloc(det->rigid_group_collections, + (1+det->n_rg_collections)*sizeof(struct rg_collection *)); + if ( new == NULL ) return NULL; + + det->rigid_group_collections = new; + + rgc = malloc(sizeof(struct rg_collection)); + if ( rgc == NULL ) return NULL; + + det->rigid_group_collections[det->n_rg_collections++] = rgc; + + rgc->name = strdup(name); + rgc->rigid_groups = NULL; + rgc->n_rigid_groups = 0; + + return rgc; +} + + +static void add_to_rigid_group(struct rigid_group *rg, int panel_number) +{ + int *pn; + + pn = realloc(rg->panel_numbers, (1+rg->n_panels)*sizeof(int)); + if ( pn == NULL ) { + ERROR("Couldn't add panel to rigid group.\n"); + return; + } + + rg->panel_numbers = pn; + rg->panel_numbers[rg->n_panels++] = panel_number; +} + + +static void add_to_rigid_group_coll(struct rg_collection *rgc, + struct rigid_group *rg) +{ + struct rigid_group **r; + + r = realloc(rgc->rigid_groups, (1+rgc->n_rigid_groups)* + sizeof(struct rigid_group *)); + if ( r == NULL ) { + ERROR("Couldn't add rigid group to collection.\n"); + return; + } + + rgc->rigid_groups = r; + rgc->rigid_groups[rgc->n_rigid_groups++] = rg; +} + + +/* Free all rigid groups in detector */ +static void free_all_rigid_groups(DataTemplate *det) +{ + int i; + + if ( det->rigid_groups == NULL ) return; + for ( i=0; i<det->n_rigid_groups; i++ ) { + free(det->rigid_groups[i]->name); + free(det->rigid_groups[i]->panel_numbers); + free(det->rigid_groups[i]); + } + free(det->rigid_groups); +} + + +/* Free all rigid groups in detector */ +static void free_all_rigid_group_collections(DataTemplate *det) +{ + int i; + + if ( det->rigid_group_collections == NULL ) return; + for ( i=0; i<det->n_rg_collections; i++ ) { + free(det->rigid_group_collections[i]->name); + free(det->rigid_group_collections[i]->rigid_groups); + free(det->rigid_group_collections[i]); + } + free(det->rigid_group_collections); +} + + +static struct rigid_group *find_rigid_group_by_name(DataTemplate *det, + char *name) +{ + int i; + + for ( i=0; i<det->n_rigid_groups; i++ ) { + if ( strcmp(det->rigid_groups[i]->name, name) == 0 ) { + return det->rigid_groups[i]; + } + } + + return NULL; +} + + +static int atob(const char *a) +{ + if ( strcasecmp(a, "true") == 0 ) return 1; + if ( strcasecmp(a, "false") == 0 ) return 0; + return atoi(a); +} + + +static int assplode_algebraic(const char *a_orig, char ***pbits) +{ + int len, i; + int nexp; + char **bits; + char *a; + int idx, istr; + + len = strlen(a_orig); + + /* Add plus at start if no sign there already */ + if ( (a_orig[0] != '+') && (a_orig[0] != '-') ) { + len += 1; + a = malloc(len+1); + snprintf(a, len+1, "+%s", a_orig); + a[len] = '\0'; + + } else { + a = strdup(a_orig); + } + + /* Count the expressions */ + nexp = 0; + for ( i=0; i<len; i++ ) { + if ( (a[i] == '+') || (a[i] == '-') ) nexp++; + } + + bits = calloc(nexp, sizeof(char *)); + + /* Break the string up */ + idx = -1; + istr = 0; + assert((a[0] == '+') || (a[0] == '-')); + for ( i=0; i<len; i++ ) { + + char ch; + + ch = a[i]; + + if ( ch == ' ' ) continue; + + if ( (ch == '+') || (ch == '-') ) { + if ( idx >= 0 ) bits[idx][istr] = '\0'; + idx++; + bits[idx] = malloc(len+1); + istr = 0; + } + + if ( !isdigit(ch) && (ch != '.') && (ch != '+') && (ch != '-') + && (ch != 'x') && (ch != 'y') && (ch != 'z') ) + { + ERROR("Invalid character '%c' found.\n", ch); + return 0; + } + + assert(idx >= 0); + bits[idx][istr++] = ch; + + } + if ( idx >= 0 ) bits[idx][istr] = '\0'; + + *pbits = bits; + free(a); + + return nexp; +} + + +/* Parses the scan directions (accounting for possible rotation) + * Assumes all white spaces have been already removed */ +static int dir_conv(const char *a, double *sx, double *sy, double *sz) +{ + int n; + char **bits; + int i; + + *sx = 0.0; *sy = 0.0; *sz = 0.0; + + n = assplode_algebraic(a, &bits); + + if ( n == 0 ) { + ERROR("Invalid direction '%s'\n", a); + return 1; + } + + for ( i=0; i<n; i++ ) { + + int len; + double val; + char axis; + int j; + + len = strlen(bits[i]); + assert(len != 0); + axis = bits[i][len-1]; + if ( (axis != 'x') && (axis != 'y') && (axis != 'z') ) { + ERROR("Invalid symbol '%c' - must be x, y or z.\n", + axis); + return 1; + } + + /* Chop off the symbol now it's dealt with */ + bits[i][len-1] = '\0'; + + /* Check for anything that isn't part of a number */ + for ( j=0; j<strlen(bits[i]); j++ ) { + if ( isdigit(bits[i][j]) ) continue; + if ( bits[i][j] == '+' ) continue; + if ( bits[i][j] == '-' ) continue; + if ( bits[i][j] == '.' ) continue; + ERROR("Invalid coefficient '%s'\n", bits[i]); + } + + if ( strlen(bits[i]) == 0 ) { + val = 1.0; + } else { + val = atof(bits[i]); + } + if ( strlen(bits[i]) == 1 ) { + if ( bits[i][0] == '+' ) val = 1.0; + if ( bits[i][0] == '-' ) val = -1.0; + } + switch ( axis ) { + + case 'x' : + *sx += val; + break; + + case 'y' : + *sy += val; + break; + + case 'z' : + *sz += val; + break; + } + + free(bits[i]); + + } + free(bits); + + return 0; +} + + +int set_dim(struct panel_template *panel, int dimension, + const char *val) +{ + if ( dimension >= MAX_DIMS ) { + ERROR("Too many dimensions!\n"); + return 1; + } + + if ( strcmp(val, "fs") == 0 ) { + panel->dims[dimension] = DIM_FS; + } else if ( strcmp(val, "ss") == 0 ) { + panel->dims[dimension] = DIM_SS; + } else if ( strcmp(val, "%") == 0 ) { + panel->dims[dimension] = DIM_PLACEHOLDER; + } else { + char *endptr; + unsigned long int fix_val = strtoul(val, &endptr, 10); + if ( endptr[0] != '\0' ) { + ERROR("Invalid dimension value '%s'\n", val); + return 1; + } else { + panel->dims[dimension] = fix_val; + } + } + return 0; +} + + +static int add_flag_value(struct panel_template *p, + float val, + enum flag_value_type type) +{ + int i; + + for ( i=0; i<MAX_FLAG_VALUES; i++ ) { + if ( p->flag_types[i] == FLAG_NOTHING ) { + p->flag_types[i] = type; + p->flag_values[i] = val; + return 0; + } + } + + ERROR("Too many flag values.\n"); + return 1; +} + + +static int parse_mask(struct panel_template *panel, + const char *key_orig, + const char *val) +{ + int n; + char *key; + + if ( sscanf(key_orig, "mask%d_", &n) != 1 ) { + ERROR("Invalid mask directive '%s'\n", key_orig); + return 1; + } + + key = strdup(key_orig); + if ( key == NULL ) return 1; + + key[4] = '_'; + + if ( strcmp(key, "mask__file") == 0 ) { + + panel->masks[n].filename = strdup(val); + + } else if ( strcmp(key, "mask__data") == 0 ) { + + if ( strncmp(val, "/", 1) != 0 ) { + ERROR("Invalid mask location '%s'\n", val); + free(key); + return 1; + } + panel->masks[n].data_location = strdup(val); + + } else if ( strcmp(key, "mask__goodbits") == 0 ) { + + char *end; + double v = strtod(val, &end); + + if ( end != val ) { + panel->masks[n].good_bits = v; + } else { + free(key); + return 1; + } + + } else if ( strcmp(key, "mask__badbits") == 0 ) { + + char *end; + double v = strtod(val, &end); + + if ( end != val ) { + panel->masks[n].bad_bits = v; + } else { + free(key); + return 1; + } + + } + + free(key); + return 0; +} + + +static int parse_field_for_panel(struct panel_template *panel, const char *key, + const char *val, DataTemplate *det) +{ + int reject = 0; + + if ( strcmp(key, "min_fs") == 0 ) { + panel->orig_min_fs = atof(val); + } else if ( strcmp(key, "max_fs") == 0 ) { + panel->orig_max_fs = atof(val); + } else if ( strcmp(key, "min_ss") == 0 ) { + panel->orig_min_ss = atof(val); + } else if ( strcmp(key, "max_ss") == 0 ) { + panel->orig_max_ss = atof(val); + } else if ( strcmp(key, "corner_x") == 0 ) { + panel->cnx = atof(val); + } else if ( strcmp(key, "corner_y") == 0 ) { + panel->cny = atof(val); + } else if ( strcmp(key, "rail_direction") == 0 ) { + if ( dir_conv(val, &panel->rail_x, + &panel->rail_y, + &panel->rail_z) ) + { + ERROR("Invalid rail direction '%s'\n", val); + reject = 1; + } + } else if ( strcmp(key, "clen_for_centering") == 0 ) { + panel->clen_for_centering = atof(val); + } else if ( strcmp(key, "adu_per_eV") == 0 ) { + panel->adu_scale = atof(val); + panel->adu_scale_unit = ADU_PER_EV; + } else if ( strcmp(key, "adu_per_photon") == 0 ) { + panel->adu_scale = atof(val); + panel->adu_scale_unit = ADU_PER_PHOTON; + } else if ( strcmp(key, "clen") == 0 ) { + /* Gets expanded when image is loaded */ + panel->cnz_from = strdup(val); + + } else if ( strcmp(key, "data") == 0 ) { + if ( strncmp(val,"/",1) != 0 ) { + ERROR("Invalid data location '%s'\n", val); + reject = -1; + } + free(panel->data); + panel->data = strdup(val); + + } else if ( strcmp(key, "mask_bad") == 0 ) { + parse_field_for_panel(panel, "mask0_badbits", val, det); + } else if ( strcmp(key, "mask_good") == 0 ) { + parse_field_for_panel(panel, "mask0_goodbits", val, det); + } else if ( strcmp(key, "mask") == 0 ) { + parse_field_for_panel(panel, "mask0_data", val, det); + } else if ( strcmp(key, "mask_file") == 0 ) { + parse_field_for_panel(panel, "mask0_file", val, det); + + } else if ( strncmp(key, "mask", 4) == 0 ) { + reject = parse_mask(panel, key, val); + + } else if ( strcmp(key, "saturation_map") == 0 ) { + panel->satmap = strdup(val); + } else if ( strcmp(key, "saturation_map_file") == 0 ) { + panel->satmap_file = strdup(val); + + } else if ( strcmp(key, "coffset") == 0) { + panel->cnz_offset = atof(val); + } else if ( strcmp(key, "res") == 0 ) { + panel->pixel_pitch = 1.0/atof(val); + } else if ( strcmp(key, "max_adu") == 0 ) { + panel->max_adu = atof(val); + ERROR("WARNING: It's usually better not to set max_adu " + "in the geometry file. Use --max-adu during " + "merging instead.\n"); + + } else if ( strcmp(key, "flag_equal") == 0 ) { + if ( add_flag_value(panel, atof(val), FLAG_EQUAL) ) { + reject = -1; + } + } else if ( strcmp(key, "flag_lessthan") == 0 ) { + if ( add_flag_value(panel, atof(val), FLAG_LESSTHAN) ) { + reject = -1; + } + } else if ( strcmp(key, "flag_morethan") == 0 ) { + if ( add_flag_value(panel, atof(val), FLAG_MORETHAN) ) { + reject = -1; + } + + } else if ( strcmp(key, "badrow_direction") == 0 ) { + ERROR("WARNING 'badrow_direction' is ignored in this version.\n"); + } else if ( strcmp(key, "no_index") == 0 ) { + panel->bad = atob(val); + } else if ( strcmp(key, "fs") == 0 ) { + if ( dir_conv(val, &panel->fsx, &panel->fsy, &panel->fsz) != 0 ) + { + ERROR("Invalid fast scan direction '%s'\n", val); + reject = 1; + } + } else if ( strcmp(key, "ss") == 0 ) { + if ( dir_conv(val, &panel->ssx, &panel->ssy, &panel->ssz) != 0 ) + { + ERROR("Invalid slow scan direction '%s'\n", val); + reject = 1; + } + } else if ( strncmp(key, "dim", 3) == 0) { + int dim_entry; + char *endptr; + if ( key[3] != '\0' ) { + dim_entry = strtoul(key+3, &endptr, 10); + if ( endptr[0] != '\0' ) { + ERROR("Invalid dimension number %s\n", + key+3); + } else { + if ( set_dim(panel, dim_entry, val) ) { + ERROR("Failed to set dim structure entry\n"); + } + } + } else { + ERROR("'dim' must be followed by a number, e.g. 'dim0'\n"); + } + } else { + ERROR("Unrecognised field '%s'\n", key); + } + + return reject; +} + + +static int check_badr_fsss(struct dt_badregion *badr, int is_fsss) +{ + /* First assignment? */ + if ( badr->is_fsss == 99 ) { + badr->is_fsss = is_fsss; + return 0; + } + + if ( is_fsss != badr->is_fsss ) { + ERROR("You can't mix x/y and fs/ss in a bad region.\n"); + return 1; + } + + return 0; +} + + +static int parse_field_bad(struct dt_badregion *badr, const char *key, + const char *val) +{ + int reject = 0; + + if ( strcmp(key, "min_x") == 0 ) { + badr->min_x = atof(val); + reject = check_badr_fsss(badr, 0); + } else if ( strcmp(key, "max_x") == 0 ) { + badr->max_x = atof(val); + reject = check_badr_fsss(badr, 0); + } else if ( strcmp(key, "min_y") == 0 ) { + badr->min_y = atof(val); + reject = check_badr_fsss(badr, 0); + } else if ( strcmp(key, "max_y") == 0 ) { + badr->max_y = atof(val); + reject = check_badr_fsss(badr, 0); + } else if ( strcmp(key, "min_fs") == 0 ) { + badr->min_fs = atof(val); + reject = check_badr_fsss(badr, 1); + } else if ( strcmp(key, "max_fs") == 0 ) { + badr->max_fs = atof(val); + reject = check_badr_fsss(badr, 1); + } else if ( strcmp(key, "min_ss") == 0 ) { + badr->min_ss = atof(val); + reject = check_badr_fsss(badr, 1); + } else if ( strcmp(key, "max_ss") == 0 ) { + badr->max_ss = atof(val); + reject = check_badr_fsss(badr, 1); + } else if ( strcmp(key, "panel") == 0 ) { + badr->panel_name = strdup(val); + } else { + ERROR("Unrecognised field '%s'\n", key); + } + + return reject; +} + + +static int parse_electron_voltage(const char *val, + char **p_from, + enum wavelength_unit *punit) +{ + char *valcpy; + char *sp; + + valcpy = strdup(val); + if ( valcpy == NULL ) return 1; + + /* "electron_voltage" directive must have explicit units */ + sp = strchr(valcpy, ' '); + if ( sp == NULL ) { + free(valcpy); + return 1; + } + + if ( strcmp(sp+1, "V") == 0 ) { + *punit = WAVELENGTH_ELECTRON_V; + } else if ( strcmp(sp+1, "kV") == 0 ) { + *punit = WAVELENGTH_ELECTRON_KV; + } else { + free(valcpy); + return 1; + } + + sp[0] = '\0'; + *p_from = valcpy; + return 0; +} + + +static int parse_wavelength(const char *val, + char **p_from, + enum wavelength_unit *punit) +{ + char *valcpy; + char *sp; + + valcpy = strdup(val); + if ( valcpy == NULL ) return 1; + + /* "wavelength" directive must have explicit units */ + sp = strchr(valcpy, ' '); + if ( sp == NULL ) { + free(valcpy); + return 1; + } + + if ( strcmp(sp+1, "m") == 0 ) { + *punit = WAVELENGTH_M; + } else if ( strcmp(sp+1, "A") == 0 ) { + *punit = WAVELENGTH_A; + } else { + free(valcpy); + return 1; + } + + sp[0] = '\0'; + *p_from = valcpy; + return 0; +} + + +static int parse_photon_energy(const char *val, + char **p_from, + enum wavelength_unit *punit) +{ + char *valcpy; + char *sp; + + valcpy = strdup(val); + if ( valcpy == NULL ) return 1; + + /* "photon_energy" is the only one of the wavelength + * directives which is allowed to not have units */ + sp = strchr(valcpy, ' '); + if ( sp == NULL ) { + *punit = WAVELENGTH_PHOTON_EV; + } else if ( strcmp(sp+1, "eV") == 0 ) { + *punit = WAVELENGTH_PHOTON_EV; + sp[0] = '\0'; + } else if ( strcmp(sp+1, "keV") == 0 ) { + *punit = WAVELENGTH_PHOTON_KEV; + sp[0] = '\0'; + } else { + /* Unit specified, but unrecognised */ + free(valcpy); + return 1; + } + + *p_from = valcpy; + return 0; +} + + +static int parse_peak_layout(const char *val, + enum peak_layout *layout) +{ + if ( strcmp(val, "auto") == 0 ) { + *layout = PEAK_LIST_AUTO; + return 0; + } + + if ( strcmp(val, "cxi") == 0 ) { + *layout = PEAK_LIST_CXI; + return 0; + } + + if ( (strcmp(val, "list3") == 0) ) { + *layout = PEAK_LIST_LIST3; + return 0; + } + + return 1; +} + + +static int parse_toplevel(DataTemplate *dt, + const char *key, + const char *val, + struct rg_definition ***rg_defl, + struct rgc_definition ***rgc_defl, + int *n_rg_defs, + int *n_rgc_defs, + struct panel_template *defaults) +{ + if ( strcmp(key, "detector_shift_x") == 0 ) { + dt->shift_x_from = strdup(val); + + } else if ( strcmp(key, "detector_shift_y") == 0 ) { + dt->shift_y_from = strdup(val); + + } else if ( strcmp(key, "photon_energy") == 0 ) { + return parse_photon_energy(val, + &dt->wavelength_from, + &dt->wavelength_unit); + + } else if ( strcmp(key, "electron_voltage") == 0 ) { + return parse_electron_voltage(val, + &dt->wavelength_from, + &dt->wavelength_unit); + + } else if ( strcmp(key, "wavelength") == 0 ) { + return parse_wavelength(val, + &dt->wavelength_from, + &dt->wavelength_unit); + + } else if ( strcmp(key, "peak_list") == 0 ) { + dt->peak_list = strdup(val); + + } else if ( strcmp(key, "peak_list_type") == 0 ) { + return parse_peak_layout(val, &dt->peak_list_type); + + } else if ( strcmp(key, "bandwidth") == 0 ) { + double v; + char *end; + v = strtod(val, &end); + if ( (val[0] != '\0') && (end[0] == '\0') ) { + dt->bandwidth = v; + } else { + ERROR("Invalid value for bandwidth\n"); + } + + } else if (strncmp(key, "rigid_group", 11) == 0 + && strncmp(key, "rigid_group_collection", 22) != 0 ) { + + struct rg_definition **new; + + new = realloc(*rg_defl, + ((*n_rg_defs)+1)*sizeof(struct rg_definition*)); + *rg_defl = new; + + (*rg_defl)[*n_rg_defs] = malloc(sizeof(struct rg_definition)); + (*rg_defl)[*n_rg_defs]->name = strdup(key+12); + (*rg_defl)[*n_rg_defs]->pns = strdup(val); + *n_rg_defs = *n_rg_defs+1; + + } else if ( strncmp(key, "rigid_group_collection", 22) == 0 ) { + + struct rgc_definition **new; + + new = realloc(*rgc_defl, ((*n_rgc_defs)+1)* + sizeof(struct rgc_definition*)); + *rgc_defl = new; + + (*rgc_defl)[*n_rgc_defs] = + malloc(sizeof(struct rgc_definition)); + (*rgc_defl)[*n_rgc_defs]->name = strdup(key+23); + (*rgc_defl)[*n_rgc_defs]->rgs = strdup(val); + *n_rgc_defs = *n_rgc_defs+1; + + } else if ( parse_field_for_panel(defaults, key, val, dt) ) { + return 1; + } + + return 0; +} + + +static int dt_num_path_placeholders(const char *str) +{ + size_t i, len; + int n_pl = 0; + + if ( str == NULL ) return 0; + + len = strlen(str); + for ( i=0; i<len; i++ ) { + if ( str[i] == '%' ) n_pl++; + } + + return n_pl; +} + + +signed int find_dim(signed int *dims, int which) +{ + int i; + + for ( i=0; i<MAX_DIMS; i++ ) { + if ( dims[i] == DIM_UNDEFINED ) break; + if ( dims[i] == which ) return i; + } + + return -1; +} + + +static int lookup_panel(const char *panel_name, + const DataTemplate *dt, + int *res) +{ + int i; + + /* If there is exactly one panel, you can get away without + * specifying the panel name */ + if ( (panel_name == NULL) && (dt->n_panels == 1) ) { + *res = 0; + return 0; + } + + for ( i=0; i<dt->n_panels; i++ ) { + if ( strcmp(dt->panels[i].name, panel_name) == 0 ) { + *res = i; + return 0; + } + } + + return 1; +} + + +static int check_mask_and_satmap_placeholders(const DataTemplate *dt) +{ + int i; + + for ( i=0; i<dt->n_panels; i++ ) { + + int num_data_pl; + int num_satmap_pl; + int j; + + num_data_pl = dt_num_path_placeholders(dt->panels[i].data); + num_satmap_pl = dt_num_path_placeholders(dt->panels[i].satmap); + + if ( num_satmap_pl > num_data_pl ) return 1; + + for ( j=0; j<MAX_MASKS; j++ ) { + + int num_mask_pl; + + /* Unused slot? */ + if ( dt->panels[i].masks[j].data_location == NULL ) continue; + + num_mask_pl = dt_num_path_placeholders(dt->panels[i].masks[j].data_location); + if ( num_mask_pl > num_data_pl ) return 1; + } + } + + return 0; +} + + +DataTemplate *data_template_new_from_string(const char *string_in) +{ + DataTemplate *dt; + char **bits; + int done = 0; + int i; + int rgi, rgci; + int reject = 0; + struct rg_definition **rg_defl = NULL; + struct rgc_definition **rgc_defl = NULL; + int n_rg_definitions = 0; + int n_rgc_definitions = 0; + char *string; + char *string_orig; + size_t len; + struct panel_template defaults; + + dt = calloc(1, sizeof(DataTemplate)); + if ( dt == NULL ) return NULL; + + dt->n_panels = 0; + dt->panels = NULL; + dt->n_bad = 0; + dt->bad = NULL; + dt->n_rigid_groups = 0; + dt->rigid_groups = NULL; + dt->n_rg_collections = 0; + dt->rigid_group_collections = NULL; + dt->bandwidth = 0.00000001; + dt->peak_list = NULL; + dt->shift_x_from = NULL; + dt->shift_y_from = NULL; + + /* The default defaults... */ + defaults.orig_min_fs = -1; + defaults.orig_min_ss = -1; + defaults.orig_max_fs = -1; + defaults.orig_max_ss = -1; + defaults.cnx = NAN; + defaults.cny = NAN; + defaults.cnz_from = NULL; + defaults.cnz_offset = 0.0; + defaults.pixel_pitch = -1.0; + defaults.bad = 0; + defaults.fsx = 1.0; + defaults.fsy = 0.0; + defaults.fsz = 0.0; + defaults.ssx = 0.0; + defaults.ssy = 1.0; + defaults.ssz = 0.0; + defaults.rail_x = NAN; /* The actual default rail direction */ + defaults.rail_y = NAN; /* is below */ + defaults.rail_z = NAN; + defaults.clen_for_centering = NAN; + defaults.adu_scale = NAN; + defaults.adu_scale_unit = ADU_PER_PHOTON; + for ( i=0; i<MAX_FLAG_VALUES; i++ ) defaults.flag_values[i] = 0; + for ( i=0; i<MAX_FLAG_VALUES; i++ ) defaults.flag_types[i] = FLAG_NOTHING; + for ( i=0; i<MAX_MASKS; i++ ) { + defaults.masks[i].data_location = NULL; + defaults.masks[i].filename = NULL; + defaults.masks[i].good_bits = 0; + defaults.masks[i].bad_bits = 0; + } + defaults.max_adu = +INFINITY; + defaults.satmap = NULL; + defaults.satmap_file = NULL; + defaults.data = strdup("/data/data"); + defaults.name = NULL; + defaults.dims[0] = DIM_SS; + defaults.dims[1] = DIM_FS; + for ( i=2; i<MAX_DIMS; i++ ) defaults.dims[i] = DIM_UNDEFINED; + + string = strdup(string_in); + if ( string == NULL ) return NULL; + len = strlen(string); + for ( i=0; i<len; i++ ) { + if ( string_in[i] == '\r' ) string[i] = '\n'; + } + + /* Becaue 'string' will get modified */ + string_orig = string; + + do { + + char *line; + struct dt_badregion *badregion = NULL; + struct panel_template *panel = NULL; + + /* Copy the next line from the big string */ + 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; + } + + /* Trim leading spaces */ + i = 0; + char *line_orig = line; + while ( (line_orig[i] == ' ') + || (line_orig[i] == '\t') ) i++; + line = strdup(line+i); + free(line_orig); + + /* Stop at comment symbol */ + char *comm = strchr(line, ';'); + if ( comm != NULL ) comm[0] = '\0'; + + /* Nothing left? Entire line was commented out, + * and can be silently ignored */ + if ( line[0] == '\0' ) { + free(line); + continue; + } + + /* Find the equals sign */ + char *eq = strchr(line, '='); + if ( eq == NULL ) { + ERROR("Bad line in geometry file: '%s'\n", line); + free(line); + reject = 1; + continue; + } + + /* Split into two strings */ + eq[0] = '\0'; + char *val = eq+1; + + /* Trim leading and trailing spaces in value */ + while ( (val[0] == ' ') || (val[0] == '\t') ) val++; + notrail(val); + + /* Trim trailing spaces in key + * (leading spaces already done above) */ + notrail(line); + + /* Find slash after panel name */ + char *slash = strchr(line, '/'); + if ( slash == NULL ) { + + /* Top-level option */ + if ( parse_toplevel(dt, line, val, + &rg_defl, + &rgc_defl, + &n_rg_definitions, + &n_rgc_definitions, + &defaults) ) + { + ERROR("Invalid top-level line '%s'\n", + line); + reject = 1; + } + free(line); + continue; + } + + slash[0] = '\0'; + char *key = slash+1; + /* No space trimming this time - must be "panel/key" */ + + /* Find either panel or bad region */ + if ( strncmp(line, "bad", 3) == 0 ) { + badregion = find_bad_region_by_name(dt, line); + if ( badregion == NULL ) { + badregion = new_bad_region(dt, line); + } + } else { + panel = find_panel_by_name(dt, line); + if ( panel == NULL ) { + panel = new_panel(dt, line, &defaults); + } + } + + if ( panel != NULL ) { + if ( parse_field_for_panel(panel, key, val, + dt) ) reject = 1; + } else { + if ( parse_field_bad(badregion, key, + val) ) reject = 1; + } + + free(line); + + } while ( !done ); + + if ( dt->n_panels == 0 ) { + ERROR("No panel descriptions in geometry file.\n"); + free(dt); + return NULL; + } + + if ( check_mask_and_satmap_placeholders(dt) ) { + ERROR("Mask and saturation map paths must have fewer " + "placeholders than image data path.\n"); + reject = 1; + } + + for ( i=0; i<dt->n_panels; i++ ) { + + int j; + struct panel_template *p = &dt->panels[i]; + signed int dim_fs = find_dim(p->dims, DIM_FS); + signed int dim_ss = find_dim(p->dims, DIM_SS); + + if ( (dim_fs<0) || (dim_ss<0) ) { + ERROR("Panel %s does not have dimensions " + "assigned to both fs and ss.\n", + p->name); + reject = 1; + } + + if ( dim_ss > dim_fs ) { + ERROR("Fast scan dimension must be lower than " + "slow scan (panel %s)\n", p->name); + reject = 1; + } + + if ( p->orig_min_fs < 0 ) { + ERROR("Please specify the minimum FS coordinate for" + " panel %s\n", dt->panels[i].name); + reject = 1; + } + if ( p->orig_max_fs < 0 ) { + ERROR("Please specify the maximum FS coordinate for" + " panel %s\n", dt->panels[i].name); + reject = 1; + } + if ( p->orig_min_ss < 0 ) { + ERROR("Please specify the minimum SS coordinate for" + " panel %s\n", dt->panels[i].name); + reject = 1; + } + if ( p->orig_max_ss < 0 ) { + ERROR("Please specify the maximum SS coordinate for" + " panel %s\n", dt->panels[i].name); + reject = 1; + } + if ( isnan(p->cnx) ) { + ERROR("Please specify the corner X coordinate for" + " panel %s\n", dt->panels[i].name); + reject = 1; + } + if ( isnan(p->cny) ) { + ERROR("Please specify the corner Y coordinate for" + " panel %s\n", dt->panels[i].name); + reject = 1; + } + if ( p->cnz_from == NULL ) { + ERROR("Please specify the camera length for panel %s\n", + dt->panels[i].name); + reject = 1; + } + if ( p->pixel_pitch < 0 ) { + ERROR("Please specify the pixel size for" + " panel %s\n", dt->panels[i].name); + reject = 1; + } + if ( p->data == NULL ) { + ERROR("Please specify the data location for panel %s\n", + p->name); + reject = 1; + } + if ( isnan(p->adu_scale) ) { + ERROR("Please specify either adu_per_eV or " + "adu_per_photon for panel %s\n", + dt->panels[i].name); + reject = 1; + } + + if ( isnan(p->clen_for_centering) && !isnan(p->rail_x) ) + { + ERROR("You must specify clen_for_centering if you " + "specify the rail direction (panel %s)\n", + p->name); + reject = 1; + } + + for ( j=0; j<MAX_MASKS; j++ ) { + if ( (p->masks[j].filename != NULL) + && (p->masks[j].data_location == NULL) ) + { + ERROR("You have specified filename but not data" + " location for mask %i of panel %s\n", + j, p->name); + reject = 1; + } + } + + /* The default rail direction */ + if ( isnan(p->rail_x) ) { + p->rail_x = 0.0; + p->rail_y = 0.0; + p->rail_z = 1.0; + } + if ( isnan(p->clen_for_centering) ) p->clen_for_centering = 0.0; + + } + + for ( i=0; i<dt->n_bad; i++ ) { + + if ( dt->bad[i].is_fsss == 99 ) { + ERROR("Please specify the coordinate ranges for" + " bad region %s\n", dt->bad[i].name); + reject = 1; + } + + if ( dt->bad[i].is_fsss ) { + if ( dt->bad[i].panel_name == NULL ) { + + ERROR("Panel not specified for bad region '%s'\n", + dt->bad[i].name); + reject = 1; + + } else if ( lookup_panel(dt->bad[i].panel_name, dt, + &dt->bad[i].panel_number) ) + { + ERROR("No such panel '%s' for bad region %s\n", + dt->bad[i].panel_name, dt->bad[i].name); + reject = 1; + + } else { + struct panel_template *p; + struct dt_badregion *bad; + int r = 0; + p = &dt->panels[dt->bad[i].panel_number]; + bad = &dt->bad[i]; + if ( bad->min_fs < p->orig_min_fs ) r = 1; + if ( bad->min_ss < p->orig_min_ss ) r = 1; + if ( bad->max_fs > p->orig_max_fs ) r = 1; + if ( bad->max_ss > p->orig_max_ss ) r = 1; + if ( r ) { + ERROR("Bad region '%s' is outside the " + "panel bounds (%s) as presented " + "in data (%i %i, %i %i inclusive): " + "Bad region %i,%i to %i, %i " + "inclusive\n", + bad->name, p->name, + p->orig_min_fs, p->orig_min_ss, + p->orig_max_fs, p->orig_max_ss, + bad->min_fs, bad->min_ss, + bad->max_fs, bad->max_ss); + reject = 1; + } + bad->min_fs -= p->orig_min_fs; + bad->max_fs -= p->orig_min_fs; + bad->min_ss -= p->orig_min_ss; + bad->max_ss -= p->orig_min_ss; + } + } + } + + free(defaults.cnz_from); + free(defaults.data); + for ( i=0; i<MAX_MASKS; i++ ) { + free(defaults.masks[i].data_location); + free(defaults.masks[i].filename); + } + + for ( rgi=0; rgi<n_rg_definitions; rgi++) { + + int pi, n1; + struct rigid_group *rigidgroup = NULL; + + rigidgroup = find_or_add_rg(dt, rg_defl[rgi]->name); + + n1 = assplode(rg_defl[rgi]->pns, ",", &bits, ASSPLODE_NONE); + + for ( pi=0; pi<n1; pi++ ) { + + int panel_number; + if ( data_template_panel_name_to_number(dt, + bits[pi], + &panel_number) ) + { + ERROR("Cannot add panel to rigid group\n"); + ERROR("Panel not found: %s\n", bits[pi]); + return NULL; + } + add_to_rigid_group(rigidgroup, panel_number); + free(bits[pi]); + + } + free(bits); + free(rg_defl[rgi]->name); + free(rg_defl[rgi]->pns); + free(rg_defl[rgi]); + } + free(rg_defl); + + for ( rgci=0; rgci<n_rgc_definitions; rgci++ ) { + + int rgi, n2; + struct rg_collection *rgcollection = NULL; + + rgcollection = find_or_add_rg_coll(dt, rgc_defl[rgci]->name); + + n2 = assplode(rgc_defl[rgci]->rgs, ",", &bits, ASSPLODE_NONE); + + for ( rgi=0; rgi<n2; rgi++ ) { + + struct rigid_group *r; + + r = find_rigid_group_by_name(dt, bits[rgi]); + if ( r == NULL ) { + ERROR("Cannot add rigid group to collection\n"); + ERROR("Rigid group not found: %s\n", bits[rgi]); + return NULL; + } + add_to_rigid_group_coll(rgcollection, r); + free(bits[rgi]); + } + free(bits); + free(rgc_defl[rgci]->name); + free(rgc_defl[rgci]->rgs); + free(rgc_defl[rgci]); + + } + free(rgc_defl); + + free(string_orig); + + if ( reject ) return NULL; + + return dt; +} + + +DataTemplate *data_template_new_from_file(const char *filename) +{ + char *contents; + DataTemplate *dt; + + contents = load_entire_file(filename); + if ( contents == NULL ) { + ERROR("Failed to load geometry file '%s'\n", filename); + return NULL; + } + + dt = data_template_new_from_string(contents); + free(contents); + return dt; +} + + +void data_template_free(DataTemplate *dt) +{ + int i; + + if ( dt == NULL ) return; + + free_all_rigid_groups(dt); + free_all_rigid_group_collections(dt); + + for ( i=0; i<dt->n_panels; i++ ) { + + int j; + + free(dt->panels[i].name); + free(dt->panels[i].data); + free(dt->panels[i].satmap); + free(dt->panels[i].satmap_file); + free(dt->panels[i].cnz_from); + + for ( j=0; j<MAX_MASKS; j++ ) { + free(dt->panels[i].masks[j].filename); + free(dt->panels[i].masks[j].data_location); + } + } + + free(dt->wavelength_from); + free(dt->peak_list); + + free(dt->panels); + free(dt->bad); + free(dt); +} + + +static int data_template_find_panel(const DataTemplate *dt, + int fs, int ss, int *ppn) +{ + int p; + + for ( p=0; p<dt->n_panels; p++ ) { + if ( (fs >= dt->panels[p].orig_min_fs) + && (fs < dt->panels[p].orig_max_fs+1) + && (ss >= dt->panels[p].orig_min_ss) + && (ss < dt->panels[p].orig_max_ss+1) ) { + *ppn = p; + return 0; + } + } + + return 1; +} + + +int data_template_file_to_panel_coords(const DataTemplate *dt, + float *pfs, float *pss, + int *ppn) +{ + int pn; + + if ( data_template_find_panel(dt, *pfs, *pss, &pn) ) { + return 1; + } + + *ppn = pn; + *pfs = *pfs - dt->panels[pn].orig_min_fs; + *pss = *pss - dt->panels[pn].orig_min_ss; + return 0; +} + + +int data_template_panel_to_file_coords(const DataTemplate *dt, + int pn, float *pfs, float *pss) +{ + if ( pn >= dt->n_panels ) return 1; + *pfs = *pfs + dt->panels[pn].orig_min_fs; + *pss = *pss + dt->panels[pn].orig_min_ss; + return 0; +} + + +const char *data_template_panel_number_to_name(const DataTemplate *dt, + int pn) +{ + if ( pn >= dt->n_panels ) return NULL; + return dt->panels[pn].name; +} + + +int data_template_panel_name_to_number(const DataTemplate *dt, + const char *panel_name, + int *pn) +{ + int i; + + if ( panel_name == NULL ) return 1; + + for ( i=0; i<dt->n_panels; i++ ) { + if ( strcmp(panel_name, dt->panels[i].name) == 0 ) { + *pn = i; + return 0; + } + } + + return 1; +} + + +void data_template_add_copy_header(DataTemplate *dt, + const char *header) +{ + /* FIXME: Add "header" to list of things to copy */ + STATUS("Adding %s\n", header); +} + + +static int dt_num_placeholders(const struct panel_template *p) +{ + int i; + int n_pl = 0; + for ( i=0; i<MAX_DIMS; i++ ) { + if ( p->dims[i] == DIM_PLACEHOLDER ) n_pl++; + } + return n_pl; +} + + +int data_template_get_slab_extents(const DataTemplate *dt, + int *pw, int *ph) +{ + int w, h; + char *data_from; + int i; + + data_from = dt->panels[0].data; + + w = 0; h = 0; + for ( i=0; i<dt->n_panels; i++ ) { + + struct panel_template *p = &dt->panels[i]; + + if ( strcmp(data_from, p->data) != 0 ) { + /* Not slabby */ + return 1; + } + + if ( dt_num_placeholders(p) > 0 ) { + /* Not slabby */ + return 1; + } + + if ( p->orig_max_fs > w ) { + w = p->orig_max_fs; + } + if ( p->orig_max_ss > h ) { + h = p->orig_max_ss; + } + + } + + /* Inclusive -> exclusive */ + *pw = w + 1; + *ph = h + 1; + return 0; +} + + +double convert_to_m(double val, int units) +{ + switch ( units ) { + + case WAVELENGTH_M : + return val; + + case WAVELENGTH_A : + return val * 1e-10; + + case WAVELENGTH_PHOTON_EV : + return ph_eV_to_lambda(val); + + case WAVELENGTH_PHOTON_KEV : + return ph_eV_to_lambda(val*1e3); + + case WAVELENGTH_ELECTRON_V : + return el_V_to_lambda(val); + + case WAVELENGTH_ELECTRON_KV : + return el_V_to_lambda(val*1e3); + + } + + return NAN; +} + + +/** + * Get the wavelength from a DataTemplate, if possible. + * + * WARNING: This is probably not the routine you are looking for! + * See the disclaimer for image_create_for_simulation(), which applies + * equally to this routine. + * + * \returns the wavelength, in metres, or NAN if impossible. + */ +double data_template_get_wavelength_if_possible(const DataTemplate *dt) +{ + float val; + char *rval; + + if ( dt->wavelength_from == NULL ) return NAN; + + val = strtod(dt->wavelength_from, &rval); + if ( (*rval == '\0') && (rval != dt->wavelength_from) ) { + return convert_to_m(val, dt->wavelength_unit); + } else { + return NAN; + } +} diff --git a/libcrystfel/src/datatemplate.h b/libcrystfel/src/datatemplate.h new file mode 100644 index 00000000..3d457f80 --- /dev/null +++ b/libcrystfel/src/datatemplate.h @@ -0,0 +1,99 @@ +/* + * datatemplate.h + * + * Template for loading data + * + * Copyright © 2019-2021 Deutsches Elektronen-Synchrotron DESY, + * a research centre of the Helmholtz Association. + * + * Authors: + * 2019-2020 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 DATATEMPLATE_H +#define DATATEMPLATE_H + +#include "detgeom.h" + +/** + * \file datatemplate.h + * Template for loading data. + */ + +/** + * This data structure is opaque. You must use the available accessor functions + * to read and write its contents. + **/ +typedef struct _datatemplate DataTemplate; + + +#ifdef __cplusplus +extern "C" { +#endif + +struct rigid_group +{ + char *name; + int *panel_numbers; + int n_panels; +}; + + +struct rg_collection +{ + char *name; + struct rigid_group **rigid_groups; + int n_rigid_groups; +}; + + +extern DataTemplate *data_template_new_from_file(const char *filename); +extern DataTemplate *data_template_new_from_string(const char *string_in); +extern void data_template_free(DataTemplate *dt); + +extern const char *data_template_panel_number_to_name(const DataTemplate *dt, + int pn); + +extern int data_template_panel_name_to_number(const DataTemplate *dt, + const char *panel_name, + int *pn); + +extern int data_template_file_to_panel_coords(const DataTemplate *dt, + float *pfs, float *pss, + int *pn); + +extern int data_template_panel_to_file_coords(const DataTemplate *dt, + int pn, + float *pfs, float *pss); + +extern void data_template_add_copy_header(DataTemplate *dt, + const char *header); + +extern int data_template_get_slab_extents(const DataTemplate *dt, int *pw, int *ph); + +extern struct rg_collection *data_template_get_rigid_groups(const DataTemplate *dtempl, + const char *collection_name); + +extern double data_template_get_wavelength_if_possible(const DataTemplate *dt); + +#ifdef __cplusplus +} +#endif + +#endif /* DATATEMPLATE_H */ diff --git a/libcrystfel/src/datatemplate_priv.h b/libcrystfel/src/datatemplate_priv.h new file mode 100644 index 00000000..71569273 --- /dev/null +++ b/libcrystfel/src/datatemplate_priv.h @@ -0,0 +1,239 @@ +/* + * datatemplate_priv.h + * + * Data template structure (private parts) + * + * Copyright © 2019-2021 Deutsches Elektronen-Synchrotron DESY, + * a research centre of the Helmholtz Association. + * + * Authors: + * 2019-2021 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/>. + * + */ + +/* NB This file is NOT part of the public API, and should NOT + * be installed, but rather stays in the libcrystfel source folder. */ + +#ifndef DATATEMPLATE_PRIV_H +#define DATATEMPLATE_PRIV_H + +/* Maximum number of dimensions expected in data files */ +#define MAX_DIMS (16) + +/* Maximum number of placeholders expected in path structure */ +#define MAX_PATH_PARTS (16) + +enum adu_per_unit +{ + ADU_PER_PHOTON, + ADU_PER_EV +}; + + +enum wavelength_unit +{ + WAVELENGTH_M, + WAVELENGTH_A, + WAVELENGTH_ELECTRON_KV, + WAVELENGTH_ELECTRON_V, + WAVELENGTH_PHOTON_KEV, + WAVELENGTH_PHOTON_EV +}; + +#define MAX_FLAG_VALUES (16) + +enum flag_value_type +{ + FLAG_NOTHING, + FLAG_EQUAL, + FLAG_MORETHAN, + FLAG_LESSTHAN +}; + +enum peak_layout +{ + PEAK_LIST_AUTO, + PEAK_LIST_CXI, + PEAK_LIST_LIST3 +}; + +/* Special values for dimension IDs */ +#define DIM_FS (-1) +#define DIM_SS (-2) +#define DIM_UNDEFINED (-3) +#define DIM_PLACEHOLDER (-4) + + +/* Maximum number of masks per panel */ +#define MAX_MASKS (8) + +struct mask_template +{ + /** Location of mask data */ + char *data_location; + + /** Filename for mask data */ + char *filename; + + /** Bit mask for bad pixels + * (pixel is bad if any of these are set) */ + unsigned int bad_bits; + + /** Bit mask for good pixels + * (pixel cannot be good unless all of these are set) */ + unsigned int good_bits; +}; + + +/** + * Represents one panel of a detector + */ +struct panel_template +{ + /** Text name for panel */ + char *name; + + /** \name Location of corner in units of the pixel size of this panel */ + /**@{*/ + double cnx; + double cny; + /**@}*/ + + /** Location to get \ref cnz from, e.g. from HDF5 file */ + char *cnz_from; + + /** The offset to be applied from \ref clen */ + double cnz_offset; + + /** Mask definitions */ + struct mask_template masks[MAX_MASKS]; + + /** Location of per-pixel saturation map */ + char *satmap; + + /** Filename for saturation map */ + char *satmap_file; + + /** Mark entire panel as bad if set */ + int bad; + + /** Resolution in pixels per metre */ + double pixel_pitch; + + /** Number of detector intensity units per photon, or eV */ + double adu_scale; + enum adu_per_unit adu_scale_unit; + + /** Treat pixel as unreliable if higher than this */ + double max_adu; + + /** Pixels with exactly this value will be marked as bad */ + enum flag_value_type flag_types[MAX_FLAG_VALUES]; + signed int flag_values[MAX_FLAG_VALUES]; + + /** Location of data in file (possibly with placeholders) */ + char *data; + + /** Dimensions (see definitions for DIM_FS etc above) */ + signed int dims[MAX_DIMS]; + + /** \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 Position of the panel in the data block in the file. */ + /*@{*/ + int orig_min_fs; + int orig_max_fs; + int orig_min_ss; + int orig_max_ss; + /*@}*/ +}; + + +#define PANEL_WIDTH(p) ((p)->orig_max_fs - (p)->orig_min_fs + 1) +#define PANEL_HEIGHT(p) ((p)->orig_max_ss - (p)->orig_min_ss + 1) + + +struct dt_badregion +{ + char name[1024]; + int is_fsss; + + double min_x; + double max_x; + double min_y; + double max_y; + + /* Coordinates are specified INCLUSIVELY */ + int panel_number; + char *panel_name; + int min_fs; + int max_fs; + int min_ss; + int max_ss; + +}; + + +struct _datatemplate +{ + struct panel_template *panels; + int n_panels; + + struct dt_badregion *bad; + int n_bad; + + char *wavelength_from; + enum wavelength_unit wavelength_unit; + + double bandwidth; + + struct rigid_group **rigid_groups; + int n_rigid_groups; + + struct rg_collection **rigid_group_collections; + int n_rg_collections; + + char *peak_list; + enum peak_layout peak_list_type; + + /* Shift of whole detector, in m */ + char *shift_x_from; + char *shift_y_from; +}; + +extern double convert_to_m(double val, int units); + +#endif /* DATATEMPLATE_PRIV_H */ diff --git a/libcrystfel/src/detector.c b/libcrystfel/src/detector.c deleted file mode 100644 index 629e13f0..00000000 --- a/libcrystfel/src/detector.c +++ /dev/null @@ -1,2401 +0,0 @@ -/* - * detector.c - * - * Detector properties - * - * Copyright © 2012-2020 Deutsches Elektronen-Synchrotron DESY, - * a research centre of the Helmholtz Association. - * Copyright © 2012 Richard Kirian - * - * Authors: - * 2009-2019 Thomas White <taw@physics.org> - * 2014 Valerio Mariani - * 2014 Kenneth Beyerlein <kenneth.beyerlein@desy.de> - * 2011 Andrew Aquila - * 2011 Richard Kirian <rkirian@asu.edu> - * - * 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 <stdlib.h> -#include <math.h> -#include <stdio.h> -#include <string.h> -#include <assert.h> -#include <ctype.h> -#include <sys/stat.h> - -#include "image.h" -#include "utils.h" -#include "detector.h" -#include "hdf5-file.h" - - -/** - * \file detector.h - */ - - -struct rg_definition { - char *name; - char *pns; -}; - - -struct rgc_definition { - char *name; - char *rgs; -}; - - -static int atob(const char *a) -{ - if ( strcasecmp(a, "true") == 0 ) return 1; - if ( strcasecmp(a, "false") == 0 ) return 0; - return atoi(a); -} - - -static int assplode_algebraic(const char *a_orig, char ***pbits) -{ - int len, i; - int nexp; - char **bits; - char *a; - int idx, istr; - - len = strlen(a_orig); - - /* Add plus at start if no sign there already */ - if ( (a_orig[0] != '+') && (a_orig[0] != '-') ) { - len += 1; - a = malloc(len+1); - snprintf(a, len+1, "+%s", a_orig); - a[len] = '\0'; - - } else { - a = strdup(a_orig); - } - - /* Count the expressions */ - nexp = 0; - for ( i=0; i<len; i++ ) { - if ( (a[i] == '+') || (a[i] == '-') ) nexp++; - } - - bits = calloc(nexp, sizeof(char *)); - - /* Break the string up */ - idx = -1; - istr = 0; - assert((a[0] == '+') || (a[0] == '-')); - for ( i=0; i<len; i++ ) { - - char ch; - - ch = a[i]; - - if ( (ch == '+') || (ch == '-') ) { - if ( idx >= 0 ) bits[idx][istr] = '\0'; - idx++; - bits[idx] = malloc(len+1); - istr = 0; - } - - if ( !isdigit(ch) && (ch != '.') && (ch != '+') && (ch != '-') - && (ch != 'x') && (ch != 'y') && (ch != 'z') ) - { - ERROR("Invalid character '%c' found.\n", ch); - return 0; - } - - assert(idx >= 0); - bits[idx][istr++] = ch; - - } - if ( idx >= 0 ) bits[idx][istr] = '\0'; - - *pbits = bits; - free(a); - - return nexp; -} - - -/* Parses the scan directions (accounting for possible rotation) - * Assumes all white spaces have been already removed */ -static int dir_conv(const char *a, double *sx, double *sy, double *sz) -{ - int n; - char **bits; - int i; - - *sx = 0.0; *sy = 0.0; *sz = 0.0; - - n = assplode_algebraic(a, &bits); - - if ( n == 0 ) { - ERROR("Invalid direction '%s'\n", a); - return 1; - } - - for ( i=0; i<n; i++ ) { - - int len; - double val; - char axis; - int j; - - len = strlen(bits[i]); - assert(len != 0); - axis = bits[i][len-1]; - if ( (axis != 'x') && (axis != 'y') && (axis != 'z') ) { - ERROR("Invalid symbol '%c' - must be x, y or z.\n", - axis); - return 1; - } - - /* Chop off the symbol now it's dealt with */ - bits[i][len-1] = '\0'; - - /* Check for anything that isn't part of a number */ - for ( j=0; j<strlen(bits[i]); j++ ) { - if ( isdigit(bits[i][j]) ) continue; - if ( bits[i][j] == '+' ) continue; - if ( bits[i][j] == '-' ) continue; - if ( bits[i][j] == '.' ) continue; - ERROR("Invalid coefficient '%s'\n", bits[i]); - } - - if ( strlen(bits[i]) == 0 ) { - val = 1.0; - } else { - val = atof(bits[i]); - } - if ( strlen(bits[i]) == 1 ) { - if ( bits[i][0] == '+' ) val = 1.0; - if ( bits[i][0] == '-' ) val = -1.0; - } - switch ( axis ) { - - case 'x' : - *sx += val; - break; - - case 'y' : - *sy += val; - break; - - case 'z' : - *sz += val; - break; - } - - free(bits[i]); - - } - free(bits); - - return 0; -} - - -static int count_trailing_spaces(const char *string) { - - int i; - - for ( i=0; i<strlen(string); i++ ) { - if ( !isspace(string[i]) ) { - return i; - } - } - - return -1; -} - - -static void build_output_line(const char *line, char *new_line, - const char *string_to_write) -{ - int nsp, i, w, ww; - int trailsp; - int n_bits; - char **bits; - - n_bits = assplode(line, "=", &bits, ASSPLODE_NONE); - trailsp = count_trailing_spaces(bits[1]); - - strcat(new_line, bits[0]); - strcat(new_line, "="); - - for ( nsp=0; nsp < trailsp; nsp++ ) { - strcat(new_line, " "); - } - strcat(new_line, string_to_write); - - for ( w=0; w<strlen(line); w++ ) { - if ( strncmp(&line[w],";",1) == 0 ) { - for ( ww=w-1; ww>=0; ww-- ) { - if ( !isspace(line[ww])) { - strcat(new_line, &line[ww]); - break; - } - } - break; - } - } - for ( i=0; i<n_bits; i++) free(bits[i]); - free(bits); -} - - -struct rvec get_q_for_panel(struct panel *p, double fs, double ss, - double *ttp, double k) -{ - struct rvec q; - double ctt, twotheta; - double xs, ys, zs; - double az; - - /* Calculate 3D position of given position, in m */ - xs = (p->cnx + fs*p->fsx + ss*p->ssx) / p->res; - ys = (p->cny + fs*p->fsy + ss*p->ssy) / p->res; - zs = p->clen + (fs*p->fsz + ss*p->ssz) / p->res; - - ctt = zs/sqrt(xs*xs + ys*ys + zs*zs); - twotheta = acos(ctt); - az = atan2(ys, xs); - if ( ttp != NULL ) *ttp = twotheta; - - q.u = k * sin(twotheta)*cos(az); - q.v = k * sin(twotheta)*sin(az); - q.w = k * (ctt - 1.0); - - return q; -} - - -int in_bad_region(struct detector *det, struct panel *p, double fs, double ss) -{ - double rx, ry; - double xs, ys; - int i; - - /* No panel found -> definitely bad! */ - if ( p == NULL ) return 1; - - /* Convert xs and ys, which are in fast scan/slow scan coordinates, - * to x and y */ - xs = fs*p->fsx + ss*p->ssx; - ys = fs*p->fsy + ss*p->ssy; - - rx = xs + p->cnx; - ry = ys + p->cny; - - for ( i=0; i<det->n_bad; i++ ) { - - struct badregion *b = &det->bad[i]; - - if ( (b->panel != NULL) - && (strcmp(b->panel, p->name) != 0) ) continue; - - if ( b->is_fsss ) { - - int nfs, nss; - - /* fs/ss bad regions are specified according to the - * original coordinates */ - nfs = fs + p->orig_min_fs; - nss = ss + p->orig_min_ss; - - if ( nfs < b->min_fs ) continue; - if ( nfs > b->max_fs ) continue; - if ( nss < b->min_ss ) continue; - if ( nss > b->max_ss ) continue; - - } else { - - if ( rx < b->min_x ) continue; - if ( rx > b->max_x ) continue; - if ( ry < b->min_y ) continue; - if ( ry > b->max_y ) continue; - - } - - return 1; - } - - return 0; -} - - -int detector_has_clen_references(struct detector *det) -{ - int i; - - for ( i=0; i<det->n_panels; i++ ) { - if ( det->panels[i].clen_from != NULL ) return 1; - } - - return 0; -} - - -static void record_panel(struct panel *p, float *dp, int do_poisson, - gsl_rng *rng, double ph_per_e, double background, - double lambda, - int *n_neg1, int *n_inf1, int *n_nan1, - int *n_neg2, int *n_inf2, int *n_nan2, - double *max_tt) -{ - int fs, ss; - - for ( ss=0; ss<p->h; ss++ ) { - for ( fs=0; fs<p->w; fs++ ) { - - double counts; - double cf; - double intensity, sa; - double pix_area, Lsq; - double xs, ys, rx, ry; - double dsq, proj_area; - float dval; - double twotheta; - - intensity = (double)dp[fs + p->w*ss]; - if ( isinf(intensity) ) (*n_inf1)++; - if ( intensity < 0.0 ) (*n_neg1)++; - if ( isnan(intensity) ) (*n_nan1)++; - - /* Area of one pixel */ - pix_area = pow(1.0/p->res, 2.0); - Lsq = pow(p->clen, 2.0); - - /* Calculate distance from crystal to pixel */ - xs = fs*p->fsx + ss*p->ssx; - ys = ss*p->fsy + ss*p->ssy; - rx = (xs + p->cnx) / p->res; - ry = (ys + p->cny) / p->res; - dsq = pow(rx, 2.0) + pow(ry, 2.0); - twotheta = atan2(sqrt(dsq), p->clen); - - /* Area of pixel as seen from crystal (approximate) */ - proj_area = pix_area * cos(twotheta); - - /* Projected area of pixel divided by distance squared */ - sa = proj_area / (dsq + Lsq); - - if ( do_poisson ) { - counts = poisson_noise(rng, intensity * ph_per_e * sa); - } else { - cf = intensity * ph_per_e * sa; - counts = cf; - } - - /* Number of photons in pixel */ - dval = counts + poisson_noise(rng, background); - - /* Convert to ADU */ - dval *= p->adu_per_photon; - - /* Saturation */ - if ( dval > p->max_adu ) dval = p->max_adu; - - dp[fs + p->w*ss] = dval; - - /* Sanity checks */ - if ( isinf(dp[fs + p->w*ss]) ) n_inf2++; - if ( isnan(dp[fs + p->w*ss]) ) n_nan2++; - if ( dp[fs + p->w*ss] < 0.0 ) n_neg2++; - - if ( twotheta > *max_tt ) *max_tt = twotheta; - - } - } -} - - -void record_image(struct image *image, int do_poisson, double background, - gsl_rng *rng, double beam_radius, double nphotons) -{ - double total_energy, energy_density; - double ph_per_e; - double area; - double max_tt = 0.0; - int n_inf1 = 0; - int n_neg1 = 0; - int n_nan1 = 0; - int n_inf2 = 0; - int n_neg2 = 0; - int n_nan2 = 0; - int pn; - - /* How many photons are scattered per electron? */ - area = M_PI*pow(beam_radius, 2.0); - total_energy = nphotons * ph_lambda_to_en(image->lambda); - energy_density = total_energy / area; - ph_per_e = (nphotons /area) * pow(THOMSON_LENGTH, 2.0); - STATUS("Fluence = %8.2e photons, " - "Energy density = %5.3f kJ/cm^2, " - "Total energy = %5.3f microJ\n", - nphotons, energy_density/1e7, total_energy*1e6); - - fill_in_adu(image); - - for ( pn=0; pn<image->det->n_panels; pn++ ) { - - record_panel(&image->det->panels[pn], image->dp[pn], - do_poisson, rng, ph_per_e, background, - image->lambda, - &n_neg1, &n_inf1, &n_nan1, - &n_neg2, &n_inf2, &n_nan2, &max_tt); - } - - STATUS("Max 2theta = %.2f deg, min d = %.2f nm\n", - rad2deg(max_tt), (image->lambda/(2.0*sin(max_tt/2.0)))/1e-9); - - STATUS("Halve the d values to get the voxel size for a synthesis.\n"); - - if ( n_neg1 + n_inf1 + n_nan1 + n_neg2 + n_inf2 + n_nan2 ) { - ERROR("WARNING: The raw calculation produced %i negative" - " values, %i infinities and %i NaNs.\n", - n_neg1, n_inf1, n_nan1); - ERROR("WARNING: After processing, there were %i negative" - " values, %i infinities and %i NaNs.\n", - n_neg2, n_inf2, n_nan2); - } -} - - -signed int find_orig_panel_number(struct detector *det, double fs, double ss) -{ - int p; - - for ( p=0; p<det->n_panels; p++ ) { - if ( (fs >= det->panels[p].orig_min_fs) - && (fs < det->panels[p].orig_max_fs+1) - && (ss >= det->panels[p].orig_min_ss) - && (ss < det->panels[p].orig_max_ss+1) ) return p; - } - - return -1; -} - - -/* Like find_panel(), but uses the original panel bounds, i.e. referring to - * what's in the HDF5 file */ -struct panel *find_orig_panel(struct detector *det, double fs, double ss) -{ - signed int pn = find_orig_panel_number(det, fs, ss); - if ( pn == -1 ) return NULL; - return &det->panels[pn]; -} - - -int panel_number(const struct detector *det, const struct panel *p) -{ - int pn; - - for ( pn=0; pn<det->n_panels; pn++ ) { - if ( &det->panels[pn] == p ) return pn; - } - - return det->n_panels; -} - - -void adjust_centering_for_rail(struct panel *p) -{ - double offs; - - /* Offset in +z direction from calibrated clen to actual */ - offs = p->clen - p->clen_for_centering; - p->cnx += p->rail_x * offs; - p->cny += p->rail_y * offs; - p->clen = p->clen_for_centering + p->coffset + p->rail_z * offs; -} - - -void fill_in_adu(struct image *image) -{ - int i; - - if ( image->det == NULL ) return; - - for ( i=0; i<image->det->n_panels; i++ ) { - - struct panel *p = &image->det->panels[i]; - - /* Already have ADU per photon? */ - if ( !isnan(p->adu_per_photon) ) continue; - - if ( isnan(p->adu_per_eV) ) { - ERROR("Neither adu_per_eV nor adu_per_photon set for " - "panel %s\n", p->name); - continue; - } - - /* Convert ADU per eV to ADU per photon */ - p->adu_per_photon = ph_lambda_to_eV(image->lambda) - * p->adu_per_eV; - } -} - - -int panel_is_in_rigid_group(const struct rigid_group *rg, struct panel *p) -{ - int i; - - for ( i=0; i<rg->n_panels; i++ ) { - if ( rg->panels[i] == p ) { - return 1; - } - } - - return 0; -} - - -int rigid_group_is_in_collection(struct rg_collection *c, - struct rigid_group *rg) -{ - int i; - - for ( i=0; i<c->n_rigid_groups; i++ ) { - if ( c->rigid_groups[i] == rg ) { - return 1; - } - } - - return 0; -} - - -struct rg_collection *find_rigid_group_collection_by_name(struct detector *det, - const char *name) -{ - int i; - - for ( i=0; i<det->n_rg_collections; i++ ) { - if ( strcmp(det->rigid_group_collections[i]->name, - name) == 0 ) { - return det->rigid_group_collections[i]; - } - } - - return NULL; -} - - -static struct panel *new_panel(struct detector *det, const char *name) -{ - struct panel *new; - - det->n_panels++; - det->panels = realloc(det->panels, det->n_panels*sizeof(struct panel)); - - new = &det->panels[det->n_panels-1]; - memcpy(new, &det->defaults, sizeof(struct panel)); - - strcpy(new->name, name); - - /* Create a new copy of the camera length location if needed */ - if ( new->clen_from != NULL ) { - new->clen_from = strdup(new->clen_from); - } - - /* Create a new copy of the data location if needed */ - if ( new->data != NULL ) { - new->data = strdup(new->data); - } - - /* Create a new copy of the dim_structure if needed */ - if ( new->dim_structure != NULL ) { - - struct dim_structure *dim_copy; - int di; - - dim_copy = initialize_dim_structure(); - dim_copy->num_dims = new->dim_structure->num_dims; - dim_copy->dims = malloc(dim_copy->num_dims*sizeof(int)); - for ( di=0; di<dim_copy->num_dims; di++ ) { - dim_copy->dims[di] = new->dim_structure->dims[di]; - } - - new->dim_structure = dim_copy; - } - - /* Create a new copy of the bad pixel mask location */ - if ( new->mask != NULL ) { - new->mask = strdup(new->mask); - } - - return new; -} - - -static struct badregion *new_bad_region(struct detector *det, const char *name) -{ - struct badregion *new; - - det->n_bad++; - det->bad = realloc(det->bad, det->n_bad*sizeof(struct badregion)); - - new = &det->bad[det->n_bad-1]; - new->min_x = NAN; - new->max_x = NAN; - new->min_y = NAN; - new->max_y = NAN; - new->min_fs = 0; - new->max_fs = 0; - new->min_ss = 0; - new->max_ss = 0; - new->is_fsss = 99; /* Slightly nasty: means "unassigned" */ - new->panel = NULL; - strcpy(new->name, name); - - return new; -} - - -struct panel *find_panel_by_name(struct detector *det, const char *name) -{ - int i; - - for ( i=0; i<det->n_panels; i++ ) { - if ( strcmp(det->panels[i].name, name) == 0 ) { - return &det->panels[i]; - } - } - - return NULL; -} - - -static struct badregion *find_bad_region_by_name(struct detector *det, - const char *name) -{ - int i; - - for ( i=0; i<det->n_bad; i++ ) { - if ( strcmp(det->bad[i].name, name) == 0 ) { - return &det->bad[i]; - } - } - - return NULL; -} - - -static struct rigid_group *find_or_add_rg(struct detector *det, - const char *name) -{ - int i; - struct rigid_group **new; - struct rigid_group *rg; - - for ( i=0; i<det->n_rigid_groups; i++ ) { - - if ( strcmp(det->rigid_groups[i]->name, name) == 0 ) { - return det->rigid_groups[i]; - } - - } - - new = realloc(det->rigid_groups, - (1+det->n_rigid_groups)*sizeof(struct rigid_group *)); - if ( new == NULL ) return NULL; - - det->rigid_groups = new; - - rg = malloc(sizeof(struct rigid_group)); - if ( rg == NULL ) return NULL; - - det->rigid_groups[det->n_rigid_groups++] = rg; - - rg->name = strdup(name); - rg->panels = NULL; - rg->n_panels = 0; - - return rg; -} - - -static struct rg_collection *find_or_add_rg_coll(struct detector *det, - const char *name) -{ - int i; - struct rg_collection **new; - struct rg_collection *rgc; - - for ( i=0; i<det->n_rg_collections; i++ ) { - if ( strcmp(det->rigid_group_collections[i]->name, name) == 0 ) - { - return det->rigid_group_collections[i]; - } - } - - new = realloc(det->rigid_group_collections, - (1+det->n_rg_collections)*sizeof(struct rg_collection *)); - if ( new == NULL ) return NULL; - - det->rigid_group_collections = new; - - rgc = malloc(sizeof(struct rg_collection)); - if ( rgc == NULL ) return NULL; - - det->rigid_group_collections[det->n_rg_collections++] = rgc; - - rgc->name = strdup(name); - rgc->rigid_groups = NULL; - rgc->n_rigid_groups = 0; - - return rgc; -} - - -static void add_to_rigid_group(struct rigid_group *rg, struct panel *p) -{ - struct panel **pn; - - pn = realloc(rg->panels, (1+rg->n_panels)*sizeof(struct panel *)); - if ( pn == NULL ) { - ERROR("Couldn't add panel to rigid group.\n"); - return; - } - - rg->panels = pn; - rg->panels[rg->n_panels++] = p; -} - - -static void add_to_rigid_group_coll(struct rg_collection *rgc, - struct rigid_group *rg) -{ - struct rigid_group **r; - - r = realloc(rgc->rigid_groups, (1+rgc->n_rigid_groups)* - sizeof(struct rigid_group *)); - if ( r == NULL ) { - ERROR("Couldn't add rigid group to collection.\n"); - return; - } - - rgc->rigid_groups = r; - rgc->rigid_groups[rgc->n_rigid_groups++] = rg; -} - - -/* Free all rigid groups in detector */ -static void free_all_rigid_groups(struct detector *det) -{ - int i; - - if ( det->rigid_groups == NULL ) return; - for ( i=0; i<det->n_rigid_groups; i++ ) { - free(det->rigid_groups[i]->name); - free(det->rigid_groups[i]->panels); - free(det->rigid_groups[i]); - } - free(det->rigid_groups); -} - - -/* Free all rigid groups in detector */ -static void free_all_rigid_group_collections(struct detector *det) -{ - int i; - - if ( det->rigid_group_collections == NULL ) return; - for ( i=0; i<det->n_rg_collections; i++ ) { - free(det->rigid_group_collections[i]->name); - free(det->rigid_group_collections[i]->rigid_groups); - free(det->rigid_group_collections[i]); - } - free(det->rigid_group_collections); -} - - -static struct rigid_group *find_rigid_group_by_name(struct detector *det, - char *name) -{ - int i; - - for ( i=0; i<det->n_rigid_groups; i++ ) { - if ( strcmp(det->rigid_groups[i]->name, name) == 0 ) { - return det->rigid_groups[i]; - } - } - - return NULL; -} - - -static int parse_field_for_panel(struct panel *panel, const char *key, - const char *val, struct detector *det) -{ - int reject = 0; - - if ( strcmp(key, "min_fs") == 0 ) { - panel->orig_min_fs = atof(val); - } else if ( strcmp(key, "max_fs") == 0 ) { - panel->orig_max_fs = atof(val); - } else if ( strcmp(key, "min_ss") == 0 ) { - panel->orig_min_ss = atof(val); - } else if ( strcmp(key, "max_ss") == 0 ) { - panel->orig_max_ss = atof(val); - } else if ( strcmp(key, "corner_x") == 0 ) { - panel->cnx = atof(val); - } else if ( strcmp(key, "corner_y") == 0 ) { - panel->cny = atof(val); - } else if ( strcmp(key, "rail_direction") == 0 ) { - if ( dir_conv(val, &panel->rail_x, - &panel->rail_y, - &panel->rail_z) ) - { - ERROR("Invalid rail direction '%s'\n", val); - reject = 1; - } - } else if ( strcmp(key, "clen_for_centering") == 0 ) { - panel->clen_for_centering = atof(val); - } else if ( strcmp(key, "adu_per_eV") == 0 ) { - panel->adu_per_eV = atof(val); - } else if ( strcmp(key, "adu_per_photon") == 0 ) { - panel->adu_per_photon = atof(val); - } else if ( strcmp(key, "rigid_group") == 0 ) { - add_to_rigid_group(find_or_add_rg(det, val), panel); - } else if ( strcmp(key, "clen") == 0 ) { - - char *end; - double v = strtod(val, &end); - if ( end == val ) { - /* This means "fill in later" */ - panel->clen = -1.0; - panel->clen_from = strdup(val); - } else { - panel->clen = v; - panel->clen_from = NULL; - } - - } else if ( strcmp(key, "data") == 0 ) { - if ( strncmp(val,"/",1) != 0 ) { - ERROR("Invalid data location '%s'\n", val); - reject = -1; - } - panel->data = strdup(val); - - } else if ( strcmp(key, "mask") == 0 ) { - if ( strncmp(val,"/",1) != 0 ) { - ERROR("Invalid mask location '%s'\n", val); - reject = -1; - } - panel->mask = strdup(val); - - } else if ( strcmp(key, "mask_file") == 0 ) { - panel->mask_file = strdup(val); - - } else if ( strcmp(key, "saturation_map") == 0 ) { - panel->satmap = strdup(val); - } else if ( strcmp(key, "saturation_map_file") == 0 ) { - panel->satmap_file = strdup(val); - - } else if ( strcmp(key, "coffset") == 0) { - panel->coffset = atof(val); - } else if ( strcmp(key, "res") == 0 ) { - panel->res = atof(val); - } else if ( strcmp(key, "max_adu") == 0 ) { - panel->max_adu = atof(val); - } else if ( strcmp(key, "badrow_direction") == 0 ) { - panel->badrow = val[0]; /* First character only */ - if ( (panel->badrow != 'x') && (panel->badrow != 'y') - && (panel->badrow != 'f') && (panel->badrow != 's') - && (panel->badrow != '-') ) { - ERROR("badrow_direction must be x, y, f, s or '-'\n"); - ERROR("Assuming '-'\n."); - panel->badrow = '-'; - } - if ( panel->badrow == 'x' ) panel->badrow = 'f'; - if ( panel->badrow == 'y' ) panel->badrow = 's'; - } else if ( strcmp(key, "no_index") == 0 ) { - panel->no_index = atob(val); - } else if ( strcmp(key, "fs") == 0 ) { - if ( dir_conv(val, &panel->fsx, &panel->fsy, &panel->fsz) != 0 ) - { - ERROR("Invalid fast scan direction '%s'\n", val); - reject = 1; - } - } else if ( strcmp(key, "ss") == 0 ) { - if ( dir_conv(val, &panel->ssx, &panel->ssy, &panel->ssz) != 0 ) - { - ERROR("Invalid slow scan direction '%s'\n", val); - reject = 1; - } - } else if ( strncmp(key, "dim", 3) == 0) { - int dim_entry; - char *endptr; - if ( key[3] != '\0' ) { - if ( panel->dim_structure == NULL ) { - panel->dim_structure = initialize_dim_structure(); - } - dim_entry = strtoul(key+3, &endptr, 10); - if ( endptr[0] != '\0' ) { - ERROR("Invalid dimension number %s\n", key+3); - } else { - if ( set_dim_structure_entry(panel->dim_structure, - dim_entry, val) ) - { - ERROR("Failed to set dim structure entry\n"); - } - } - } else { - ERROR("'dim' must be followed by a number, e.g. 'dim0'\n"); - } - } else { - ERROR("Unrecognised field '%s'\n", key); - } - - return reject; -} - - -static int check_badr_fsss(struct badregion *badr, int is_fsss) -{ - /* First assignment? */ - if ( badr->is_fsss == 99 ) { - badr->is_fsss = is_fsss; - return 0; - } - - if ( is_fsss != badr->is_fsss ) { - ERROR("You can't mix x/y and fs/ss in a bad region.\n"); - return 1; - } - - return 0; -} - - -static int parse_field_bad(struct badregion *badr, const char *key, - const char *val) -{ - int reject = 0; - - if ( strcmp(key, "min_x") == 0 ) { - badr->min_x = atof(val); - reject = check_badr_fsss(badr, 0); - } else if ( strcmp(key, "max_x") == 0 ) { - badr->max_x = atof(val); - reject = check_badr_fsss(badr, 0); - } else if ( strcmp(key, "min_y") == 0 ) { - badr->min_y = atof(val); - reject = check_badr_fsss(badr, 0); - } else if ( strcmp(key, "max_y") == 0 ) { - badr->max_y = atof(val); - reject = check_badr_fsss(badr, 0); - } else if ( strcmp(key, "min_fs") == 0 ) { - badr->min_fs = atof(val); - reject = check_badr_fsss(badr, 1); - } else if ( strcmp(key, "max_fs") == 0 ) { - badr->max_fs = atof(val); - reject = check_badr_fsss(badr, 1); - } else if ( strcmp(key, "min_ss") == 0 ) { - badr->min_ss = atof(val); - reject = check_badr_fsss(badr, 1); - } else if ( strcmp(key, "max_ss") == 0 ) { - badr->max_ss = atof(val); - reject = check_badr_fsss(badr, 1); - } else if ( strcmp(key, "panel") == 0 ) { - badr->panel = strdup(val); - } else { - ERROR("Unrecognised field '%s'\n", key); - } - - return reject; -} - - -static void parse_toplevel(struct detector *det, struct beam_params *beam, - const char *key, const char *val, - struct rg_definition ***rg_defl, - struct rgc_definition ***rgc_defl, int *n_rg_defs, - int *n_rgc_defs, char **hdf5_peak_path) -{ - - if ( strcmp(key, "mask_bad") == 0 ) { - - char *end; - double v = strtod(val, &end); - - if ( end != val ) { - det->mask_bad = v; - } - - } else if ( strcmp(key, "mask_good") == 0 ) { - - char *end; - double v = strtod(val, &end); - - if ( end != val ) { - det->mask_good = v; - } - - } else if ( strcmp(key, "coffset") == 0 ) { - det->defaults.coffset = atof(val); - - } else if ( strcmp(key, "photon_energy") == 0 ) { - if ( beam != NULL ) { - double v; - char *end; - v = strtod(val, &end); - if ( (val[0] != '\0') && (end[0] == '\0') ) { - beam->photon_energy = v; - beam->photon_energy_from = NULL; - } else { - beam->photon_energy = 0.0; - beam->photon_energy_from = strdup(val); - } - } - - } else if ( strcmp(key, "photon_energy_bandwidth") == 0 ) { - if ( beam != NULL ) { - double v; - char *end; - v = strtod(val, &end); - if ( (val[0] != '\0') && (end[0] == '\0') ) { - beam->bandwidth = v; - } else { - ERROR("Invalid value for " - "photon_energy_bandwidth\n"); - } - } - - } else if ( strcmp(key, "photon_energy_scale") == 0 ) { - if ( beam != NULL ) { - beam->photon_energy_scale = atof(val); - } - - } else if ( strcmp(key, "peak_info_location") == 0 ) { - if ( hdf5_peak_path != NULL ) { - *hdf5_peak_path = strdup(val); - } - - } else if (strncmp(key, "rigid_group", 11) == 0 - && strncmp(key, "rigid_group_collection", 22) != 0 ) { - - struct rg_definition **new; - - new = realloc(*rg_defl, - ((*n_rg_defs)+1)*sizeof(struct rg_definition*)); - *rg_defl = new; - - (*rg_defl)[*n_rg_defs] = malloc(sizeof(struct rg_definition)); - (*rg_defl)[*n_rg_defs]->name = strdup(key+12); - (*rg_defl)[*n_rg_defs]->pns = strdup(val); - *n_rg_defs = *n_rg_defs+1; - - } else if ( strncmp(key, "rigid_group_collection", 22) == 0 ) { - - struct rgc_definition **new; - - new = realloc(*rgc_defl, ((*n_rgc_defs)+1)* - sizeof(struct rgc_definition*)); - *rgc_defl = new; - - (*rgc_defl)[*n_rgc_defs] = - malloc(sizeof(struct rgc_definition)); - (*rgc_defl)[*n_rgc_defs]->name = strdup(key+23); - (*rgc_defl)[*n_rgc_defs]->rgs = strdup(val); - *n_rgc_defs = *n_rgc_defs+1; - - } else if ( parse_field_for_panel(&det->defaults, key, val, det) ) { - ERROR("Unrecognised top level field '%s'\n", key); - } -} - - -/* Test if fs,ss in panel "p" is further {out,in} than {*p_max_d,*p_min_d}, and - * if so update det->furthest_{out,in}_{panel,fs,ss}. */ -static void check_point(struct panel *p, double fs, double ss, - double *p_min_d, double *p_max_d, struct detector *det) -{ - double xs, ys, rx, ry, d; - - xs = fs*p->fsx + ss*p->ssx; - ys = fs*p->fsy + ss*p->ssy; - - rx = (xs + p->cnx) / p->res; - ry = (ys + p->cny) / p->res; - - d = sqrt(pow(rx, 2.0) + pow(ry, 2.0)); - - if ( d > *p_max_d ) { - - det->furthest_out_panel = p; - det->furthest_out_fs = fs; - det->furthest_out_ss = ss; - *p_max_d = d; - - } else if ( d < *p_min_d ) { - - det->furthest_in_panel = p; - det->furthest_in_fs = fs; - det->furthest_in_ss = ss; - *p_min_d = d; - - } -} - - -static void find_min_max_d(struct detector *det) -{ - double max_d, min_d; - int i; - - min_d = +INFINITY; - max_d = 0.0; - for ( i=0; i<det->n_panels; i++ ) { - - struct panel *p; - - p = &det->panels[i]; - - check_point(p, 0, 0, &min_d, &max_d, det); - check_point(p, p->w, 0, &min_d, &max_d, det); - check_point(p, 0, p->h, &min_d, &max_d, det); - check_point(p, p->w, p->h, &min_d, &max_d, det); - - } -} - - -struct detector *get_detector_geometry(const char *filename, - struct beam_params *beam) -{ - return get_detector_geometry_2(filename, beam, NULL); -} - - -struct detector *get_detector_geometry_from_string(const char *string_in, - struct beam_params *beam, - char **hdf5_peak_path) -{ - struct detector *det; - char **bits; - int done = 0; - int i; - int rgi, rgci; - int reject = 0; - int path_dim, mask_path_dim; - int dim_dim; - int dim_reject = 0; - int dim_dim_reject = 0; - struct rg_definition **rg_defl = NULL; - struct rgc_definition **rgc_defl = NULL; - int n_rg_definitions = 0; - int n_rgc_definitions = 0; - char *string; - char *string_orig; - size_t len; - - det = calloc(1, sizeof(struct detector)); - if ( det == NULL ) return NULL; - - if ( beam != NULL ) { - beam->photon_energy = 0.0; - beam->photon_energy_from = NULL; - beam->photon_energy_scale = 1.0; - beam->bandwidth = 0.00000001; - } - - det->n_panels = 0; - det->panels = NULL; - det->n_bad = 0; - det->bad = NULL; - det->mask_good = 0; - det->mask_bad = 0; - det->n_rigid_groups = 0; - det->rigid_groups = NULL; - det->path_dim = 0; - det->dim_dim = 0; - det->n_rg_collections = 0; - det->rigid_group_collections = NULL; - - /* The default defaults... */ - det->defaults.orig_min_fs = -1; - det->defaults.orig_min_ss = -1; - det->defaults.orig_max_fs = -1; - det->defaults.orig_max_ss = -1; - det->defaults.cnx = NAN; - det->defaults.cny = NAN; - det->defaults.clen = NAN; - det->defaults.coffset = 0.0; - det->defaults.res = -1.0; - det->defaults.badrow = '-'; - det->defaults.no_index = 0; - det->defaults.fsx = 1.0; - det->defaults.fsy = 0.0; - det->defaults.fsz = 0.0; - det->defaults.ssx = 0.0; - det->defaults.ssy = 1.0; - det->defaults.ssz = 0.0; - det->defaults.rail_x = NAN; /* The actual default rail direction */ - det->defaults.rail_y = NAN; /* is below */ - det->defaults.rail_z = NAN; - det->defaults.clen_for_centering = NAN; - det->defaults.adu_per_eV = NAN; - det->defaults.adu_per_photon = NAN; - det->defaults.max_adu = +INFINITY; - det->defaults.mask = NULL; - det->defaults.mask_file = NULL; - det->defaults.satmap = NULL; - det->defaults.satmap_file = NULL; - det->defaults.data = NULL; - det->defaults.dim_structure = NULL; - strncpy(det->defaults.name, "", 1023); - - string = strdup(string_in); - if ( string == NULL ) return NULL; - len = strlen(string); - for ( i=0; i<len; i++ ) { - if ( string_in[i] == '\r' ) string[i] = '\n'; - } - - /* Because 'string' will get modified */ - string_orig = string; - - do { - - int n1, n2; - char **path; - char *line; - struct badregion *badregion = NULL; - struct panel *panel = NULL; - char wholeval[1024]; - - 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] == ';' ) { - free(line); - continue; - } - - n1 = assplode(line, " \t", &bits, ASSPLODE_NONE); - if ( n1 < 3 ) { - for ( i=0; i<n1; i++ ) free(bits[i]); - free(bits); - free(line); - continue; - } - - /* Stitch the pieces of the "value" back together */ - wholeval[0] = '\0'; /* Empty string */ - for ( i=2; i<n1; i++ ) { - if ( bits[i][0] == ';' ) break; /* Stop on comment */ - strncat(wholeval, bits[i], 1023); - } - - if ( bits[1][0] != '=' ) { - for ( i=0; i<n1; i++ ) free(bits[i]); - free(bits); - free(line); - continue; - } - - n2 = assplode(bits[0], "/\\.", &path, ASSPLODE_NONE); - if ( n2 < 2 ) { - - /* This was a top-level option, not handled above. */ - parse_toplevel(det, beam, bits[0], wholeval, &rg_defl, - &rgc_defl, &n_rg_definitions, - &n_rgc_definitions, hdf5_peak_path); - for ( i=0; i<n1; i++ ) free(bits[i]); - free(bits); - for ( i=0; i<n2; i++ ) free(path[i]); - free(path); - free(line); - continue; - } - - if ( strncmp(path[0], "bad", 3) == 0 ) { - badregion = find_bad_region_by_name(det, path[0]); - if ( badregion == NULL ) { - badregion = new_bad_region(det, path[0]); - } - } else { - panel = find_panel_by_name(det, path[0]); - if ( panel == NULL ) { - panel = new_panel(det, path[0]); - } - } - - if ( panel != NULL ) { - if ( parse_field_for_panel(panel, path[1], - wholeval, det) ) - { - reject = 1; - } - } else { - if ( parse_field_bad(badregion, path[1], wholeval) ) { - reject = 1; - } - } - - for ( i=0; i<n1; i++ ) free(bits[i]); - for ( i=0; i<n2; i++ ) free(path[i]); - free(bits); - free(path); - free(line); - - } while ( !done ); - - if ( det->n_panels == -1 ) { - ERROR("No panel descriptions in geometry file.\n"); - free(det); - return NULL; - } - - path_dim = -1; - dim_reject = 0; - - for ( i=0; i<det->n_panels; i++ ) { - - int panel_dim = 0; - char *next_instance; - - next_instance = det->panels[i].data; - - while ( next_instance ) { - next_instance = strstr(next_instance, "%"); - if ( next_instance != NULL ) { - next_instance += 1*sizeof(char); - panel_dim += 1; - } - } - - if ( path_dim == -1 ) { - path_dim = panel_dim; - } else { - if ( panel_dim != path_dim ) { - dim_reject = 1; - } - } - - } - - mask_path_dim = -1; - for ( i=0; i<det->n_panels; i++ ) { - - int panel_mask_dim = 0; - char *next_instance; - - if ( det->panels[i].mask != NULL ) { - - next_instance = det->panels[i].mask; - - while ( next_instance ) { - next_instance = strstr(next_instance, "%"); - if ( next_instance != NULL ) { - next_instance += 1*sizeof(char); - panel_mask_dim += 1; - } - } - - if ( mask_path_dim == -1 ) { - mask_path_dim = panel_mask_dim; - } else { - if ( panel_mask_dim != mask_path_dim ) { - dim_reject = 1; - } - } - - } - } - - if ( dim_reject == 1 ) { - ERROR("All panels' data and mask entries must have the same " - "number of placeholders\n"); - reject = 1; - } - - if ( mask_path_dim > path_dim ) { - ERROR("Number of placeholders in mask cannot be larger than " - "for data\n"); - reject = 1; - } - - det->path_dim = path_dim; - - dim_dim_reject = 0; - dim_dim = -1; - - for ( i=0; i<det->n_panels; i++ ) { - - int di; - int found_ss = 0; - int found_fs = 0; - int panel_dim_dim = 0; - - if ( det->panels[i].dim_structure == NULL ) { - det->panels[i].dim_structure = default_dim_structure(); - } - - for ( di=0; di<det->panels[i].dim_structure->num_dims; di++ ) { - - if ( det->panels[i].dim_structure->dims[di] == - HYSL_UNDEFINED ) { - dim_dim_reject = 1; - ERROR("Dimension %i for panel %s is undefined.\n", - di, det->panels[i].name); - } - if ( det->panels[i].dim_structure->dims[di] == - HYSL_PLACEHOLDER ) { - panel_dim_dim += 1; - } - if ( det->panels[i].dim_structure->dims[di] == - HYSL_SS ) { - found_ss += 1; - } - if ( det->panels[i].dim_structure->dims[di] == - HYSL_FS ) { - found_fs += 1; - } - - } - - if ( found_ss != 1 ) { - ERROR("Exactly one slow scan dim coordinate is needed " - "(found %i for panel %s)\n", found_ss, - det->panels[i].name); - dim_dim_reject = 1; - } - - if ( found_fs != 1 ) { - ERROR("Exactly one fast scan dim coordinate is needed " - "(found %i for panel %s)\n", found_fs, - det->panels[i].name); - dim_dim_reject = 1; - } - - if ( panel_dim_dim > 1 ) { - ERROR("Maximum one placeholder dim coordinate is " - "allowed (found %i for panel %s)\n", - panel_dim_dim, det->panels[i].name); - dim_dim_reject = 1; - } - - if ( dim_dim == -1 ) { - dim_dim = panel_dim_dim; - } else { - if ( panel_dim_dim != dim_dim ) { - dim_dim_reject = 1; - } - } - - } - - if ( dim_dim_reject == 1) { - reject = 1; - } - - det->dim_dim = dim_dim; - - for ( i=0; i<det->n_panels; i++ ) { - - struct panel *p = &det->panels[i]; - - if ( p->orig_min_fs < 0 ) { - ERROR("Please specify the minimum FS coordinate for" - " panel %s\n", det->panels[i].name); - reject = 1; - } - if ( p->orig_max_fs < 0 ) { - ERROR("Please specify the maximum FS coordinate for" - " panel %s\n", det->panels[i].name); - reject = 1; - } - if ( p->orig_min_ss < 0 ) { - ERROR("Please specify the minimum SS coordinate for" - " panel %s\n", det->panels[i].name); - reject = 1; - } - if ( p->orig_max_ss < 0 ) { - ERROR("Please specify the maximum SS coordinate for" - " panel %s\n", det->panels[i].name); - reject = 1; - } - if ( isnan(p->cnx) ) { - ERROR("Please specify the corner X coordinate for" - " panel %s\n", det->panels[i].name); - reject = 1; - } - if ( isnan(p->cny) ) { - ERROR("Please specify the corner Y coordinate for" - " panel %s\n", det->panels[i].name); - reject = 1; - } - if ( isnan(p->clen) && (p->clen_from == NULL) ) { - ERROR("Please specify the camera length for" - " panel %s\n", det->panels[i].name); - reject = 1; - } - if ( p->res < 0 ) { - ERROR("Please specify the resolution for" - " panel %s\n", det->panels[i].name); - reject = 1; - } - if ( isnan(p->adu_per_eV) && isnan(p->adu_per_photon) ) { - ERROR("Please specify either adu_per_eV or " - "adu_per_photon for panel %s\n", - det->panels[i].name); - reject = 1; - } - - if ( isnan(p->clen_for_centering) && !isnan(p->rail_x) ) - { - ERROR("You must specify clen_for_centering if you " - "specify the rail direction (panel %s)\n", - p->name); - reject = 1; - } - - if ( (p->mask_file != NULL) && (p->mask == NULL) ) { - ERROR("You have specified 'mask_file' but not 'mask'. " - "'mask_file' will therefore have no effect. " - "(panel %s)\n", p->name); - reject = 1; - } - - /* The default rail direction */ - if ( isnan(p->rail_x) ) { - p->rail_x = 0.0; - p->rail_y = 0.0; - p->rail_z = 1.0; - } - if ( isnan(p->clen_for_centering) ) p->clen_for_centering = 0.0; - - /* It's OK if the badrow direction is '0' */ - /* It's not a problem if "no_index" is still zero */ - /* The default transformation matrix is at least valid */ - - det->panels[i].w = det->panels[i].orig_max_fs - - det->panels[i].orig_min_fs+1; - det->panels[i].h = det->panels[i].orig_max_ss - - det->panels[i].orig_min_ss+1; - - } - - for ( i=0; i<det->n_bad; i++ ) { - if ( det->bad[i].is_fsss == 99 ) { - ERROR("Please specify the coordinate ranges for" - " bad region %s\n", det->bad[i].name); - reject = 1; - } - } - - free(det->defaults.clen_from); - free(det->defaults.data); - free(det->defaults.mask); - - for ( rgi=0; rgi<n_rg_definitions; rgi++) { - - int pi, n1; - struct rigid_group *rigidgroup = NULL; - - rigidgroup = find_or_add_rg(det, rg_defl[rgi]->name); - - n1 = assplode(rg_defl[rgi]->pns, ",", &bits, ASSPLODE_NONE); - - for ( pi=0; pi<n1; pi++ ) { - - struct panel *p; - - p = find_panel_by_name(det, bits[pi]); - if ( p == NULL ) { - ERROR("Cannot add panel to rigid group\n"); - ERROR("Panel not found: %s\n", bits[pi]); - return NULL; - } - add_to_rigid_group(rigidgroup, p); - free(bits[pi]); - } - free(bits); - free(rg_defl[rgi]->name); - free(rg_defl[rgi]->pns); - free(rg_defl[rgi]); - } - free(rg_defl); - - for ( rgci=0; rgci<n_rgc_definitions; rgci++ ) { - - int rgi, n2; - struct rg_collection *rgcollection = NULL; - - rgcollection = find_or_add_rg_coll(det, rgc_defl[rgci]->name); - - n2 = assplode(rgc_defl[rgci]->rgs, ",", &bits, ASSPLODE_NONE); - - for ( rgi=0; rgi<n2; rgi++ ) { - - struct rigid_group *r; - - r = find_rigid_group_by_name(det, bits[rgi]); - if ( r == NULL ) { - ERROR("Cannot add rigid group to collection\n"); - ERROR("Rigid group not found: %s\n", bits[rgi]); - return NULL; - } - add_to_rigid_group_coll(rgcollection, r); - free(bits[rgi]); - } - free(bits); - free(rgc_defl[rgci]->name); - free(rgc_defl[rgci]->rgs); - free(rgc_defl[rgci]); - - } - free(rgc_defl); - - if ( n_rg_definitions == 0 ) { - - int pi; - - for ( pi=0; pi<det->n_panels; pi++ ) { - - struct rigid_group *rigidgroup = NULL; - - rigidgroup = find_or_add_rg(det, det->panels[pi].name); - add_to_rigid_group(rigidgroup, &det->panels[pi]); - - } - } - - if ( n_rgc_definitions == 0 ) { - - int rgi; - struct rg_collection *rgcollection = NULL; - - rgcollection = find_or_add_rg_coll(det, "default"); - - for ( rgi=0; rgi<det->n_rigid_groups; rgi++ ) { - - add_to_rigid_group_coll(rgcollection, - det->rigid_groups[rgi]); - - } - } - - /* Calculate matrix inverses and other stuff */ - for ( i=0; i<det->n_panels; i++ ) { - - struct panel *p; - double d; - - p = &det->panels[i]; - - if ( p->fsx*p->ssy == p->ssx*p->fsy ) { - ERROR("Panel %i transformation singular.\n", i); - } - - d = (double)p->fsx*p->ssy - p->ssx*p->fsy; - p->xfs = p->ssy / d; - p->yfs = -p->ssx / d; - p->xss = -p->fsy / d; - p->yss = p->fsx / d; - - p->w = p->orig_max_fs - p->orig_min_fs + 1; - p->h = p->orig_max_ss - p->orig_min_ss + 1; - - } - - find_min_max_d(det); - free(string_orig); - - 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; -} - - -void free_detector_geometry(struct detector *det) -{ - int i; - - free_all_rigid_groups(det); - free_all_rigid_group_collections(det); - - for ( i=0; i<det->n_panels; i++ ) { - free(det->panels[i].clen_from); - free_dim_structure(det->panels[i].dim_structure); - } - - free(det->panels); - free(det->bad); - free(det); -} - - -static int rg_number(const struct detector *det, const struct rigid_group *rg) -{ - int i; - for ( i=0; i<det->n_rigid_groups; i++ ) { - if ( det->rigid_groups[i] == rg ) return i; - } - return det->n_rigid_groups; -} - - -struct detector *copy_geom(const struct detector *in) -{ - struct detector *out; - int i; - - if ( in == NULL ) return NULL; - - out = malloc(sizeof(struct detector)); - if ( out == NULL ) return NULL; - - /* Copy everything */ - memcpy(out, in, sizeof(struct detector)); - - out->panels = malloc(out->n_panels * sizeof(struct panel)); - memcpy(out->panels, in->panels, out->n_panels * sizeof(struct panel)); - - out->bad = malloc(out->n_bad * sizeof(struct badregion)); - memcpy(out->bad, in->bad, out->n_bad * sizeof(struct badregion)); - - /* Copy the panels */ - for ( i=0; i<out->n_panels; i++ ) { - - struct panel *p; - - /* Copy all fields */ - p = &out->panels[i]; - - /* Now fix up everything involving pointers... */ - - if ( p->clen_from != NULL ) { - /* Make a copy of the clen_from fields unique to this - * copy of the structure. */ - p->clen_from = strdup(p->clen_from); - } - - if ( p->data != NULL ) { - /* Make a copy of the data fields unique to this - * copy of the structure. */ - p->data = strdup(p->data); - } - - if ( p->dim_structure != NULL ) { - /* Make a copy of the dim_structure fields unique to this - * copy of the structure. */ - - struct dim_structure *dim_new; - int di; - - dim_new = initialize_dim_structure(); - dim_new->num_dims = p->dim_structure->num_dims; - dim_new->dims = malloc(dim_new->num_dims*sizeof(int)); - for ( di=0; di<dim_new->num_dims; di++ ) { - dim_new->dims[di] = p->dim_structure->dims[di]; - } - - p->dim_structure = dim_new; - - } - - if ( &in->panels[i] == in->furthest_out_panel ) { - out->furthest_out_panel = &out->panels[i]; - } - if ( &in->panels[i] == in->furthest_in_panel ) { - out->furthest_in_panel = &out->panels[i]; - } - - } - - /* Copy all the rigid groups */ - out->rigid_groups = malloc(out->n_rigid_groups*sizeof(struct rigid_group)); - if ( out->rigid_groups == NULL ) return NULL; - for ( i=0; i<out->n_rigid_groups; i++ ) { - - struct rigid_group *inrg; - struct rigid_group *rg; - int j; - - rg = malloc(sizeof(struct rigid_group)); - if ( rg == NULL ) return NULL; - - out->rigid_groups[i] = rg; - - inrg = in->rigid_groups[i]; - - rg->name = strdup(inrg->name); - if ( rg->name == NULL ) return NULL; - - rg->n_panels = inrg->n_panels; - rg->panels = malloc(inrg->n_panels*sizeof(struct panel *)); - if ( rg->panels == NULL ) return NULL; - - for ( j=0; j<rg->n_panels; j++ ) { - int k = panel_number(in, inrg->panels[j]); - rg->panels[j] = &out->panels[k]; - } - - } - - /* Copy all the rigid group collections */ - out->rigid_group_collections = malloc(out->n_rg_collections*sizeof(struct rg_collection)); - if ( out->rigid_group_collections == NULL ) return NULL; - for ( i=0; i<out->n_rg_collections; i++ ) { - - struct rg_collection *inrgc; - struct rg_collection *rgc; - int j; - - rgc = malloc(sizeof(struct rg_collection)); - if ( rgc == NULL ) return NULL; - - out->rigid_group_collections[i] = rgc; - - inrgc = in->rigid_group_collections[i]; - - rgc->name = strdup(inrgc->name); - if ( rgc->name == NULL ) return NULL; - - rgc->n_rigid_groups = inrgc->n_rigid_groups; - rgc->rigid_groups = malloc(rgc->n_rigid_groups*sizeof(struct rg_collection)); - if ( rgc->rigid_groups == NULL ) return NULL; - - for ( j=0; j<rgc->n_rigid_groups; j++ ) { - int k = rg_number(in, inrgc->rigid_groups[j]); - if ( k == in->n_rigid_groups ) return NULL; - rgc->rigid_groups[j] = out->rigid_groups[k]; - } - - } - - return out; -} - - -struct detector *simple_geometry(const struct image *image, int w, int h) -{ - struct detector *geom; - - geom = calloc(1, sizeof(struct detector)); - - geom->n_panels = 1; - geom->panels = calloc(1, sizeof(struct panel)); - - geom->panels[0].orig_min_fs = 0; - geom->panels[0].orig_max_fs = w-1; - geom->panels[0].orig_min_ss = 0; - geom->panels[0].orig_max_ss = h-1; - geom->panels[0].cnx = -w / 2.0; - geom->panels[0].cny = -h / 2.0; - geom->panels[0].max_adu = INFINITY; - geom->panels[0].orig_min_fs = -1; - geom->panels[0].orig_max_fs = -1; - geom->panels[0].orig_min_ss = -1; - geom->panels[0].orig_max_ss = -1; - - geom->panels[0].fsx = 1; - geom->panels[0].fsy = 0; - geom->panels[0].fsz = 0; - geom->panels[0].ssx = 0; - geom->panels[0].ssy = 1; - geom->panels[0].ssz = 0; - - geom->panels[0].xfs = 1; - geom->panels[0].xss = 0; - geom->panels[0].yfs = 0; - geom->panels[0].yss = 1; - - geom->panels[0].w = w; - geom->panels[0].h = h; - - geom->panels[0].mask = NULL; - geom->panels[0].data = NULL; - - find_min_max_d(geom); - - return geom; -} - - -int reverse_2d_mapping(double x, double y, struct detector *det, - struct panel **pp, double *pfs, double *pss) -{ - int i; - - for ( i=0; i<det->n_panels; i++ ) { - - struct panel *p = &det->panels[i]; - double cx, cy, fs, ss; - - /* Get position relative to corner */ - cx = x - p->cnx; - cy = y - p->cny; - - /* Reverse the transformation matrix */ - fs = cx*p->xfs + cy*p->yfs; - ss = cx*p->xss + cy*p->yss; - - /* In range? */ - if ( fs < 0 ) continue; - if ( ss < 0 ) continue; - if ( fs > p->w ) continue; - if ( ss > p->h ) continue; - - *pfs = fs; - *pss = ss; - *pp = p; - return 0; - - } - - return 1; -} - - -static void check_extents(struct panel p, double *min_x, double *min_y, - double *max_x, double *max_y, double fs, double ss) -{ - double xs, ys, rx, ry; - - xs = fs*p.fsx + ss*p.ssx; - ys = fs*p.fsy + ss*p.ssy; - - rx = xs + p.cnx; - ry = ys + p.cny; - - if ( rx > *max_x ) *max_x = rx; - if ( ry > *max_y ) *max_y = ry; - if ( rx < *min_x ) *min_x = rx; - if ( ry < *min_y ) *min_y = ry; -} - - -static void rewrite_panel_fields(const struct panel *p, char *line, - FILE *fh, char **bits, - int write_panel_coffset) -{ - char new_line[1024]; - char string_to_write[512]; - - strcpy(new_line,"\0"); - strcpy(string_to_write,"\0"); - - if(strstr(bits[1], "fs") != NULL && - strstr(bits[1], "min_fs") == NULL && - strstr(bits[1], "max_fs") == NULL && - strstr(bits[1], "offset") == NULL ) { - - sprintf(string_to_write, "%+fx %+fy", - p->fsx, p->fsy); - build_output_line(line, new_line, - string_to_write); - fputs(new_line, fh); - fputs("\n", fh); - return; - - } else if ( strstr(bits[1], "ss") != NULL && - strstr(bits[1], "min_ss") == NULL && - strstr(bits[1], "max_ss") == NULL) { - - sprintf(string_to_write, "%+fx %+fy", - p->ssx, p->ssy); - 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) { - - sprintf(string_to_write, "%g", - p->cnx); - 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) { - - sprintf(string_to_write, "%g", - p->cny); - build_output_line(line, new_line, - string_to_write); - fputs(new_line, fh); - fputs("\n", fh); - return; - - } else if ( strstr(bits[1], "coffset") != NULL) { - - if ( write_panel_coffset ) { - return; - } else { - fputs(line, fh); - fputs("\n", fh); - return; - } - - } else { - fputs(line, fh); - fputs("\n", fh); - } -} - - -double largest_q(struct image *image) -{ - struct rvec q; - double tt; - - if ( image->det == NULL ) { - ERROR("No detector geometry. assuming detector is infinite!\n"); - return INFINITY; - } - - q = get_q_for_panel(image->det->furthest_out_panel, - image->det->furthest_out_fs, - image->det->furthest_out_ss, - &tt, 1.0/image->lambda); - - return modulus(q.u, q.v, q.w); -} - - -double smallest_q(struct image *image) -{ - struct rvec q; - double tt; - - q = get_q_for_panel(image->det->furthest_in_panel, - image->det->furthest_in_fs, - image->det->furthest_in_ss, - &tt, 1.0/image->lambda); - - return modulus(q.u, q.v, q.w); -} - - -void get_pixel_extents(struct detector *det, - double *min_x, double *min_y, - double *max_x, double *max_y) -{ - int i; - - *min_x = 0.0; - *max_x = 0.0; - *min_y = 0.0; - *max_y = 0.0; - - /* To determine the maximum extents of the detector, put all four - * corners of each panel through the transformations and watch for the - * biggest */ - - for ( i=0; i<det->n_panels; i++ ) { - - check_extents(det->panels[i], min_x, min_y, max_x, max_y, - 0.0, 0.0); - - check_extents(det->panels[i], min_x, min_y, max_x, max_y, - 0.0, det->panels[i].h+1); - - check_extents(det->panels[i], min_x, min_y, max_x, max_y, - det->panels[i].w+1, 0.0); - - check_extents(det->panels[i], min_x, min_y, max_x, max_y, - det->panels[i].w+1, det->panels[i].h+1); - - - } -} - - -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; - int done = 0; - - if ( geometry_data == NULL ) return 2; - if ( output_filename == NULL ) return 2; - if ( det->n_panels < 1 ) return 3; - - fh = fopen(output_filename, "w"); - if ( fh == NULL ) return 1; - - if ( additional_comment != NULL ) { - fputs("; ", fh); - fputs(additional_comment, fh); - fputs("\n", fh); - } - - if ( write_panel_coffset ) { - fputs("; Optimized panel offsets can be found at the " - "end of the file\n", fh); - } - - 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(line, "/=", &bits, ASSPLODE_NONE); - - if ( n_bits != 3 ) { - 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 ) { - rewrite_panel_fields(p, line, fh, bits, - write_panel_coffset); - } else { - fputs(line, fh); - fputs("\n", fh); - } - } - - for ( i=0; i<n_bits; i++ ) free(bits[i]); - - } while ( !done ); - - if ( write_panel_coffset ) { - - int pi; - - fputs("\n", fh); - - for ( pi=0; pi<det->n_panels; pi++ ) { - fprintf(fh, "%s/coffset = %f\n", - det->panels[pi].name, det->panels[pi].coffset); - } - } - - 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) -{ - return write_detector_geometry_2(geometry_filename, output_filename, - det, NULL, 0); -} - - -/** - * \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 \p image, every pixel whose resolution is - * between \p min and \p max. - * - */ - -void mark_resolution_range_as_bad(struct image *image, - double min, double max) -{ - int i; - - if ( isinf(min) && isinf(max) ) return; /* nothing to do */ - - for ( i=0; i<image->det->n_panels; i++ ) { - - int fs, ss; - struct panel *p = &image->det->panels[i]; - - for ( ss=0; ss<p->h; ss++ ) { - for ( fs=0; fs<p->w; fs++ ) { - struct rvec q; - double r; - q = get_q_for_panel(p, fs, ss, NULL, 1.0/image->lambda); - r = modulus(q.u, q.v, q.w); - if ( (r >= min) && (r <= max) ) { - image->bad[i][fs+p->w*ss] = 1; - } - } - } - - } -} - - -static int safe_strcmp(const char *a, const char *b) -{ - /* If both are NULL, they count as equal */ - if ( (a == NULL) && (b == NULL) ) return 0; - - /* Otherwise, if either is NULL then they're different */ - if ( a == NULL ) return 1; - if ( b == NULL ) return 1; - - /* Otherwise, normal string comparison */ - return strcmp(a, b); -} - - -/** - * \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 \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) -{ - int pi; - const char *first_datafrom = NULL; - const char *curr_datafrom = NULL; - - if ( det->panels[0].data == NULL ) { - first_datafrom = element; /* Might be NULL */ - } else { - first_datafrom = det->panels[0].data; - } - - for ( pi=1; pi<det->n_panels; pi++ ) { - - if ( det->panels[pi].data == NULL ) { - curr_datafrom = element; /* Might be NULL */ - } else { - curr_datafrom = det->panels[pi].data; - } - - if ( safe_strcmp(curr_datafrom, first_datafrom) != 0 ) { - return 0; - } - - } - - return 1; -} - - -int multi_event_geometry(struct detector *det) -{ - return (det->path_dim != 0) || (det->dim_dim != 0); -} diff --git a/libcrystfel/src/detector.h b/libcrystfel/src/detector.h deleted file mode 100644 index 2176861f..00000000 --- a/libcrystfel/src/detector.h +++ /dev/null @@ -1,333 +0,0 @@ -/* - * detector.h - * - * Detector properties - * - * Copyright © 2012-2020 Deutsches Elektronen-Synchrotron DESY, - * a research centre of the Helmholtz Association. - * Copyright © 2012 Richard Kirian - * - * Authors: - * 2009-2019 Thomas White <taw@physics.org> - * 2011-2012 Richard Kirian <rkirian@asu.edu> - * 2014 Valerio Mariani - * 2011 Andrew Aquila - * - * 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 DETECTOR_H -#define DETECTOR_H - -struct rigid_group; -struct rg_collection; -struct detector; -struct panel; -struct badregion; -struct beam_params; -struct hdfile; -struct event; - -#include "hdf5-file.h" -#include "image.h" - -#ifdef __cplusplus -extern "C" { -#endif - -/** - * \file detector.h - * Detector geometry structure and related functions. - */ - - -struct rigid_group -{ - char *name; - struct panel **panels; - int n_panels; -}; - - -struct rg_collection -{ - char *name; - struct rigid_group **rigid_groups; - int n_rigid_groups; -}; - - -/** - * Represents one panel of a detector - */ -struct panel -{ - /** Text name for panel (fixed length array) */ - char name[1024]; - - /** \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; - - /** 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; - - /** 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; - - /** 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; - /*@}*/ - - /** \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; - - /*** Height, calculated as max_ss-min_ss+1 */ - int h; -}; - - -struct badregion -{ - char name[1024]; - int is_fsss; - char *panel; - - double min_x; - double max_x; - double min_y; - double max_y; - - /* Specified INCLUSIVELY */ - int min_fs; - int max_fs; - int min_ss; - int max_ss; - -}; - - -struct detector -{ - struct panel *panels; - int n_panels; - - struct badregion *bad; - int n_bad; - - unsigned int mask_bad; - unsigned int mask_good; - - struct rigid_group **rigid_groups; - int n_rigid_groups; - - struct rg_collection **rigid_group_collections; - int n_rg_collections; - - /* Location of the pixel furthest away from the beam position, which - * will have the largest value of 2theta regardless of camera length - * and wavelength */ - struct panel *furthest_out_panel; - double furthest_out_fs; - double furthest_out_ss; - - /* As above, but for the smallest 2theta */ - struct panel *furthest_in_panel; - double furthest_in_fs; - double furthest_in_ss; - - int path_dim; - int dim_dim; - - struct panel defaults; -}; - - -extern struct rvec get_q_for_panel(struct panel *p, double fs, double ss, - double *ttp, double k); - -extern double get_tt(struct image *image, double xs, double ys, int *err); - -extern int in_bad_region(struct detector *det, struct panel *p, - double fs, double ss); - -extern void record_image(struct image *image, int do_poisson, double background, - gsl_rng *rng, double beam_radius, double nphotons); - -extern struct panel *find_orig_panel(struct detector *det, - double fs, double ss); - -extern signed int find_orig_panel_number(struct detector *det, - double fs, double ss); - -extern int panel_number(const struct detector *det, const struct panel *p); - -extern struct detector *get_detector_geometry(const char *filename, - struct beam_params *beam); - -extern struct detector *get_detector_geometry_2(const char *filename, - struct beam_params *beam, - 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); - -extern struct detector *simple_geometry(const struct image *image, int w, int h); - -extern void get_pixel_extents(struct detector *det, - double *min_x, double *min_y, - double *max_x, double *max_y); - -extern void fill_in_adu(struct image *image); -extern void adjust_centering_for_rail(struct panel *p); - -extern int panel_is_in_rigid_group(const struct rigid_group *rg, - struct panel *p); - -extern int rigid_group_is_in_collection(struct rg_collection *c, - struct rigid_group *rg); - -extern struct detector *copy_geom(const struct detector *in); - -extern int reverse_2d_mapping(double x, double y, struct detector *det, - struct panel **pp, double *pfs, double *pss); - -extern double largest_q(struct image *image); - -extern double smallest_q(struct image *image); - -extern struct panel *find_panel_by_name(struct detector *det, const char *name); - -extern int write_detector_geometry_2(const char *geometry_filename, - const char *output_filename, - struct detector *det, - 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); - -extern void mark_resolution_range_as_bad(struct image *image, - double min, double max); - - -extern int single_panel_data_source(struct detector *det, const char *element); - -struct rg_collection *find_rigid_group_collection_by_name(struct detector *det, - const char *name); - -extern int detector_has_clen_references(struct detector *det); - -extern int multi_event_geometry(struct detector *det); - -#ifdef __cplusplus -} -#endif - -#endif /* DETECTOR_H */ diff --git a/libcrystfel/src/detgeom.c b/libcrystfel/src/detgeom.c new file mode 100644 index 00000000..5612a225 --- /dev/null +++ b/libcrystfel/src/detgeom.c @@ -0,0 +1,141 @@ +/* + * detgeom.c + * + * Utility functions for detgeom structure + * + * Copyright © 2019-2021 Deutsches Elektronen-Synchrotron DESY, + * a research centre of the Helmholtz Association. + * + * Authors: + * 2020-2021 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 "detgeom.h" +#include "utils.h" + + +/** + * \file detgeom.h + */ + + +void detgeom_transform_coords(struct detgeom_panel *p, + double fs, double ss, + double wavelength, + double dx, double dy, + double *r) +{ + double xs, ys, zs; + double fac; + + /* Calculate 3D position of given position, in pixels */ + xs = p->cnx + fs*p->fsx + ss*p->ssx + dx*p->pixel_pitch; + ys = p->cny + fs*p->fsy + ss*p->ssy + dy*p->pixel_pitch; + zs = p->cnz + fs*p->fsz + ss*p->ssz; + + fac = wavelength * sqrt(xs*xs + ys*ys + zs*zs); + + r[0] = xs / fac; + r[1] = ys / fac; + r[2] = zs / fac - 1.0/wavelength; +} + + +void detgeom_free(struct detgeom *detgeom) +{ + int i; + + for ( i=0; i<detgeom->n_panels; i++ ) { + free(detgeom->panels[i].name); + } + + free(detgeom->panels); + free(detgeom); +} + + +static double panel_max_res(struct detgeom_panel *p, + double wavelength) +{ + double r[3]; + double max_res = 0.0; + + detgeom_transform_coords(p, 0, 0, wavelength, 0.0, 0.0, r); + max_res = biggest(max_res, modulus(r[0], r[1], r[2])); + + detgeom_transform_coords(p, 0, p->h, wavelength, 0.0, 0.0, r); + max_res = biggest(max_res, modulus(r[0], r[1], r[2])); + + detgeom_transform_coords(p, p->w, 0, wavelength, 0.0, 0.0, r); + max_res = biggest(max_res, modulus(r[0], r[1], r[2])); + + detgeom_transform_coords(p, p->w, p->h, wavelength, 0.0, 0.0, r); + max_res = biggest(max_res, modulus(r[0], r[1], r[2])); + + return max_res; +} + + +double detgeom_max_resolution(struct detgeom *detgeom, + double wavelength) +{ + int i; + double max_res = 0.0; + + for ( i=0; i<detgeom->n_panels; i++ ) { + + double panel_maxres; + + panel_maxres = panel_max_res(&detgeom->panels[i], + wavelength); + if ( panel_maxres > max_res ) { + max_res = panel_maxres; + } + } + + return max_res; +} + + +void show_panel(struct detgeom_panel *p) +{ + STATUS("Panel '%s':\n", p->name); + STATUS(" Size %i x %i px\n", p->w, p->h); + STATUS(" Transformation [cnx] + [%6.2f %6.2f] [fs] = [x]\n", + p->fsx, p->ssx); + STATUS(" [cny] + [%6.2f %6.2f] [ss] = [y]\n", + p->fsy, p->ssy); + STATUS(" [cnz] + [%6.2f %6.2f] = [z]\n", + p->fsz, p->ssz); + STATUS(" corner x,y,z = %f, %f, %f px\n", + p->cnx, p->cny, p->cnz); + STATUS(" = %f, %f, %f mm\n", + p->cnx*p->pixel_pitch*1e3, + p->cny*p->pixel_pitch*1e3, + p->cnz*p->pixel_pitch*1e3); + STATUS(" %f adu/photon, max %f adu\n", + p->adu_per_photon, p->max_adu); +} diff --git a/libcrystfel/src/detgeom.h b/libcrystfel/src/detgeom.h new file mode 100644 index 00000000..5e3815ac --- /dev/null +++ b/libcrystfel/src/detgeom.h @@ -0,0 +1,112 @@ +/* + * detgeom.h + * + * Detector geometry structure + * + * Copyright © 2012-2021 Deutsches Elektronen-Synchrotron DESY, + * a research centre of the Helmholtz Association. + * Copyright © 2012 Richard Kirian + * + * Authors: + * 2009-2020 Thomas White <taw@physics.org> + * 2011-2012 Richard Kirian <rkirian@asu.edu> + * 2014 Valerio Mariani + * 2011 Andrew Aquila + * + * 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 DETGEOM_H +#define DETGEOM_H + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * \file detgeom.h + * Detector geometry structure and related functions. + */ + + +/** + * Represents one panel of a detector + */ +struct detgeom_panel +{ + /** Text name for panel */ + char *name; + + /** \name Location of corner in units of the pixel size of this panel, \ + * measured from the interaction point. */ + /**@{*/ + double cnx; + double cny; + double cnz; + /**@}*/ + + /** Pixel size in metres */ + double pixel_pitch; + + /** Number of detector intensity units per photon (or electron, etc) */ + double adu_per_photon; + + /** Treat pixel as unreliable if higher than this */ + double max_adu; + + /** \name Transformation matrix from pixel coordinates to lab frame */ + /*@{*/ + double fsx; + double fsy; + double fsz; + double ssx; + double ssy; + double ssz; + /*@}*/ + + /** \name Width and height of panel */ + /*@{*/ + int w; + int h; + /*@}*/ +}; + + +struct detgeom +{ + struct detgeom_panel *panels; + int n_panels; +}; + +extern void detgeom_transform_coords(struct detgeom_panel *p, + double fs, double ss, + double wavelength, + double dx, double dy, + double *r); + +extern void detgeom_free(struct detgeom *detgeom); + +extern double detgeom_max_resolution(struct detgeom *detgeom, + double wavelength); + +extern void show_panel(struct detgeom_panel *p); + +#ifdef __cplusplus +} +#endif + +#endif /* DETGEOM_H */ diff --git a/libcrystfel/src/events.c b/libcrystfel/src/events.c deleted file mode 100644 index 491f7811..00000000 --- a/libcrystfel/src/events.c +++ /dev/null @@ -1,636 +0,0 @@ -/* - * events.c - * - * Event properties - * - * Copyright © 2012-2020 Deutsches Elektronen-Synchrotron DESY, - * a research centre of the Helmholtz Association. - * - * Authors: - * 2017 Thomas White - * 2014 Valerio Mariani - * - * 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 "events.h" -#include "utils.h" - -#include <hdf5.h> -#include <string.h> -#include <stdlib.h> -#include <assert.h> - -/** \file events.h */ - -struct event *initialize_event() -{ - - struct event *ev; - - ev = malloc(sizeof(struct event)); - ev->path_entries = NULL; - ev->path_length = 0; - - ev->dim_entries = NULL; - ev->dim_length = 0; - - return ev; - -} - - -struct event_list *initialize_event_list() -{ - - struct event_list *ev_list; - - ev_list = malloc(sizeof(struct event_list)); - - ev_list->events = NULL; - ev_list->num_events = 0; - - return ev_list; - -} - -struct filename_plus_event *initialize_filename_plus_event() -{ - - struct filename_plus_event *fpe; - - fpe = malloc(sizeof(struct filename_plus_event)); - - fpe->filename = NULL; - fpe->ev = NULL; - - return fpe; -} - - -int event_cmp(struct event *ev1, struct event *ev2) -{ - int pi; - int di; - - if ( ev1->path_length != ev2->path_length || - ev1->dim_length != ev2->dim_length ) { - return 1; - } - - for ( pi=0; pi<ev1->path_length; pi++ ) { - if ( strcmp(ev1->path_entries[pi], ev2->path_entries[pi]) != 0 ) { - return 1; - } - } - - for ( di=0; di<ev1->dim_length; di++ ) { - if ( ev1->path_entries[di] != ev2->path_entries[di] ) { - return 1; - } - } - - return 0; - -} - - -int add_non_existing_event_to_event_list(struct event_list *ev_list, - struct event *ev) -{ - int evi; - int found = 0; - - for ( evi=0; evi<ev_list->num_events; evi++ ) { - if (event_cmp(ev_list->events[evi], ev) == 0 ) { - found = 1; - break; - } - } - - if ( found == 0) { - return append_event_to_event_list(ev_list, ev); - } - - return 0; -} - - -int append_event_to_event_list(struct event_list *ev_list, struct event *ev) -{ - struct event **new_el; - - new_el = realloc(ev_list->events, - (1+ev_list->num_events)*sizeof(struct event*)); - if ( new_el == NULL ) return 1; - - ev_list->events = new_el; - ev_list->events[ev_list->num_events] = copy_event(ev); - ev_list->num_events +=1; - - return 0; -} - - -struct event *copy_event(struct event *ev) -{ - struct event *new_ev; - int pi, di; - - if ( ev == NULL ) return NULL; - - if ( ev->dim_length == 0 && ev->path_length == 0) { - - new_ev = initialize_event(); - - } else { - - new_ev=malloc(sizeof(struct event)); - - new_ev->path_entries = malloc(ev->path_length*sizeof(char *)); - new_ev->path_length = ev->path_length; - - new_ev->dim_entries = malloc(ev->dim_length*sizeof(int *)); - new_ev->dim_length = ev->dim_length; - - for ( pi=0; pi<new_ev->path_length; pi++ ) { - new_ev->path_entries[pi] = strdup(ev->path_entries[pi]); - } - - for ( di=0; di<new_ev->dim_length; di++ ) { - new_ev->dim_entries[di] = ev->dim_entries[di]; - } - - } - return new_ev; -} - - -struct event_list *copy_event_list(struct event_list *el) -{ - int ei; - struct event_list *el_copy; - struct event **events_copy; - - el_copy = malloc(1); - if ( el_copy == NULL ) { - return NULL; - } - - events_copy = malloc(el->num_events); - if ( events_copy == NULL ) { - free (el_copy); - return NULL; - } - el_copy->events = events_copy; - - for ( ei=0; ei<el->num_events; ei++ ) { - el_copy->events[ei]=copy_event(el->events[ei]); - } - - el_copy->num_events = el->num_events; - - return el_copy; -} - - -static int events_equal(struct event *ev1, struct event *ev2) -{ - int i; - - if ( ev1->path_length != ev2->path_length ) return 0; - if ( ev1->dim_length != ev2->dim_length ) return 0; - - for ( i=0; i<ev1->path_length; i++ ) { - if ( strcmp(ev1->path_entries[i], ev2->path_entries[i]) != 0 ) { - return 0; - } - } - - for ( i=0; i<ev1->dim_length; i++ ) { - if ( ev1->dim_entries[i] != ev2->dim_entries[i] ) return 0; - } - - return 1; -} - - -/** - * \param ev: An event structure - * \param el: An event list - * - * \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) -{ - int i; - - if ( ev == NULL ) return el->num_events; - - for ( i=0; i<el->num_events; i++ ) { - if ( events_equal(ev, el->events[i]) ) return i; - } - - return i; -} - - -void free_event(struct event *ev) -{ - int pi; - - if ( ev == NULL ) return; - - if ( ev->path_length != 0 ) { - for ( pi=0; pi<ev->path_length; pi++ ) { - free(ev->path_entries[pi]); - } - } - free(ev->dim_entries); - free(ev); -} - - -void free_event_list(struct event_list *el) -{ - int ei; - - for ( ei=0; ei<el->num_events; ei++ ) { - free_event(el->events[ei]); - } - free(el); -} - - -void free_filename_plus_event(struct filename_plus_event *fpe) -{ - free(fpe->filename); - - if ( fpe->ev != NULL ) { - free_event(fpe->ev); - } - - free(fpe); -} - - -char *get_event_string(struct event *ev) -{ - char *evstr; - int i; - size_t ev_len; - - if ( ev == NULL ) return strdup("(none)"); - - ev_len = 1; /* Zero terminator */ - for ( i=0; i<ev->path_length; i++ ) { - ev_len += strlen(ev->path_entries[i]); - ev_len += 1; /* Slash afterwards */ - } - ev_len += 16*ev->dim_length; /* Max length of number plus slash */ - ev_len += 2; /* Double slash in middle */ - - evstr = malloc(ev_len); - if ( evstr == NULL ) return NULL; - evstr[0] = '\0'; - - for ( i=0; i<ev->path_length; i++ ) { - if ( i > 0 ) strcat(evstr, "/"); - strcat(evstr, ev->path_entries[i]); - } - - strcat(evstr, "//"); - - for ( i=0; i<ev->dim_length; i++ ) { - char num_buf[16]; - snprintf(num_buf, 16, "%i", ev->dim_entries[i]); - if ( i > 0 ) strcat(evstr, "/"); - strcat(evstr, num_buf); - } - - return evstr; -} - - -struct event *get_event_from_event_string(const char *ev_string) -{ - struct event *ev; - char *ev_sep; - char buf_path[1024]; - char buf_dim[1024]; - char *sep; - char *start; - - ev_sep = strstr(ev_string, "//"); - if ( ev_sep == NULL ) return NULL; - - strncpy(buf_path, ev_string, ev_sep-ev_string); - buf_path[ev_sep-ev_string] = '\0'; - - strncpy(buf_dim, ev_sep+2, strlen(ev_sep)-2); - buf_dim[strlen(ev_sep)-2] = '\0'; - - ev = initialize_event(); - if ( ev == NULL ) return NULL; - - if ( strlen(buf_path) !=0 ) { - start = buf_path; - do { - char buf[2014]; - - sep = strstr(start, "/"); - - if ( sep != NULL ) { - strncpy(buf, start, sep-start); - buf[sep-start]='\0'; - push_path_entry_to_event(ev, buf); - start = sep + 1; - - } else { - - sprintf(buf,"%s",start); - push_path_entry_to_event(ev, buf); - - } - - } while (sep); - - } - - if ( strlen(buf_dim) !=0 ) { - - start = buf_dim; - - do { - - char buf[2014]; - int buf_int; - - sep = strstr(start, "/"); - if ( sep != NULL ) { - strncpy(buf, start, sep-start); - buf[sep-start]='\0'; - buf_int = atoi(buf); - push_dim_entry_to_event(ev, buf_int); - start = sep + 1; - - } else { - - sprintf(buf,"%s",start); - buf_int = atoi(buf); - push_dim_entry_to_event(ev, buf_int); - - } - - } while (sep); - - } - - - return ev; -} - - -int push_path_entry_to_event(struct event *ev, const char *entry) -{ - char **new_path_entries; - - new_path_entries = realloc(ev->path_entries, - (1+ev->path_length)*sizeof(char *)); - if ( new_path_entries == NULL ) return 1; - - ev->path_entries = new_path_entries; - ev->path_entries[ev->path_length] = strdup(entry); - ev->path_length += 1; - - return 0; -} - - -int push_dim_entry_to_event(struct event *ev, int entry) -{ - int *new_dim_entries; - - new_dim_entries = realloc(ev->dim_entries, - (1+ev->dim_length)*sizeof(int)); - if ( new_dim_entries == NULL ) return 1; - - ev->dim_entries = new_dim_entries; - ev->dim_entries[ev->dim_length] = entry; - ev->dim_length += 1; - - return 0; -} - - -int pop_path_entry_from_event(struct event *ev) -{ - char **new_path_entries; - - if ( ev->path_length == 0 ) return 1; - - free(ev->path_entries[ev->path_length-1]); - - if ( ev->path_length == 1 ) { - ev->path_entries = NULL; - ev->path_length = 0; - return 0; - } - - new_path_entries = realloc(ev->path_entries, - (ev->path_length-1)*sizeof(char *)); - - if ( new_path_entries == NULL ) return 1; - - ev->path_entries = new_path_entries; - ev->path_length = ev->path_length-1; - - return 0; -} - - -int pop_dim_entry_from_event(struct event *ev) -{ - int *new_dim_entries; - - if ( ev->dim_length == 0 ) { - return 1; - } - - if ( ev->dim_length == 1 ) { - ev->dim_entries = NULL; - ev->dim_length = 0; - return 0; - } - - new_dim_entries = realloc(ev->dim_entries, - (ev->dim_length-1)*sizeof(int)); - - if ( new_dim_entries == NULL) { - return 1; - } - - ev->dim_entries = new_dim_entries; - ev->dim_length = ev->dim_length-1; - - return 0; -} - - -char *event_path_placeholder_subst(const char *entry, const char *data) -{ - - char *ph_loc; - char *full_path; - ptrdiff_t len_head; - size_t len_entry, len_data; - - len_entry = strlen(entry); - len_data = strlen(data); - full_path = malloc(len_data + len_entry + 1); - if ( full_path == NULL ) return NULL; - - ph_loc = strchr(data, '%'); - len_head = ph_loc - data; - assert(len_head >= 0); - - strncpy(full_path, data, len_head); - full_path[len_head] = '\0'; - strcat(full_path, entry); - strcat(full_path, ph_loc+1); - - return full_path; -} - - -char *retrieve_full_path(struct event *ev, const char *data) -{ - int ei ; - char *return_value; - char *pholder; - - return_value = strdup(data); - pholder = strstr(return_value,"%"); - ei = 0; - - while ( pholder != NULL ) { - - char *tmp; - - /* Check we have enough things to put in the placeholders */ - if ( ei >= ev->path_length ) { - ERROR("Too many placeholders ('%%') in location.\n"); - return NULL; - } - - /* Substitute one placeholder */ - tmp = event_path_placeholder_subst(ev->path_entries[ei++], - return_value); - - if ( tmp == NULL ) { - ERROR("Couldn't substitute placeholder\n"); - return NULL; - } - - /* Next time, we will substitute the next part of the path into - * the partially substituted string */ - free(return_value); - return_value = tmp; - - pholder = strstr(return_value, "%"); - - } - - return return_value; -} - - -struct dim_structure *initialize_dim_structure() -{ - struct dim_structure *hs; - - hs = malloc(sizeof(struct dim_structure)); - if ( hs == NULL ) return NULL; - - hs->dims = NULL; - hs->num_dims = 0; - - return hs; -} - - -struct dim_structure *default_dim_structure() -{ - struct dim_structure *hsd; - - hsd = initialize_dim_structure(); - - set_dim_structure_entry(hsd, 0, "ss"); - set_dim_structure_entry(hsd, 1, "fs"); - - return hsd; -} - - -void free_dim_structure(struct dim_structure *hsd) -{ - if ( hsd == NULL ) return; - free(hsd->dims); - free(hsd); -} - - -static int parse_dim_structure_val(const char *val) -{ - if ( strcmp(val,"%") == 0 ) { - return HYSL_PLACEHOLDER; - } else if ( strcmp(val,"ss") == 0 ) { - return HYSL_SS; - } else if ( strcmp(val,"fs") == 0 ) { - return HYSL_FS; - } - return atoi(val); - -} - - -int set_dim_structure_entry(struct dim_structure *hsd, int dim_entry, - const char *val_string) -{ - /* "dims" array needs element with zero index */ - if ( dim_entry >= hsd->num_dims ) { - - int di; - - int *new_dims = realloc(hsd->dims, (dim_entry+1)*sizeof(int)); - if ( new_dims == NULL ) return 1; - - /* Initialise the elements just allocated */ - for ( di=hsd->num_dims; di<=dim_entry; di++ ) { - new_dims[di] = HYSL_UNDEFINED; - } - - hsd->dims = new_dims; - hsd->num_dims = dim_entry+1; - - } - - hsd->dims[dim_entry] = parse_dim_structure_val(val_string); - - return 0; -} diff --git a/libcrystfel/src/events.h b/libcrystfel/src/events.h deleted file mode 100644 index b743f827..00000000 --- a/libcrystfel/src/events.h +++ /dev/null @@ -1,110 +0,0 @@ -/* - * events.h - * - * Event properties - * - * Copyright © 2012-2020 Deutsches Elektronen-Synchrotron DESY, - * a research centre of the Helmholtz Association. - * - * Authors: - * 2014 Valerio Mariani - * - * 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 EVENTS_H -#define EVENTS_H - -/** - * \file events.h - * Event description structures - */ - -struct event -{ - char **path_entries; - int path_length; - int *dim_entries; - int dim_length; -}; - -struct event_list -{ - struct event **events; - int num_events; -}; - -struct filename_plus_event -{ - char *filename; - struct event *ev; -}; - -enum dimension_id -{ - HYSL_UNDEFINED = -99, - HYSL_PLACEHOLDER = -98, - HYSL_FS = -1, - HYSL_SS = -2 -}; - -struct dim_structure -{ - int *dims; - int num_dims; -}; - -extern struct event *initialize_event(void); -extern int push_path_entry_to_event(struct event *ev, const char *entry); -extern int pop_path_entry_from_event(struct event *ev); -extern int push_dim_entry_to_event(struct event *ev, int entry); -extern int pop_dim_entry_from_event(struct event *ev); -extern struct event *copy_event(struct event *ev); -extern void free_event(struct event *ev); -extern char *get_event_string(struct event *ev); -extern struct event *get_event_from_event_string(const char *ev_string); -extern char *event_path_placeholder_subst(const char *ev_name, - const char *data); -extern char *retrieve_full_path(struct event *ev, const char *data); - - -extern struct filename_plus_event *initialize_filename_plus_event(void); -extern void free_filename_plus_event(struct filename_plus_event *fpe); - - -extern struct event_list *initialize_event_list(void); -extern int append_event_to_event_list(struct event_list *ev_list, - struct event *ev); -extern int add_non_existing_event_to_event_list(struct event_list *ev_list, - struct event *ev); -extern struct event_list *copy_event_list(struct event_list *el); -extern int find_event(struct event *ev, struct event_list *el); -extern void free_event_list(struct event_list *el); - - -extern struct dim_structure *initialize_dim_structure(void); -extern struct dim_structure *default_dim_structure(void); -extern int set_dim_structure_entry(struct dim_structure *hsd, - int dim_entry, const char *val_string); -extern void free_dim_structure_entry(struct dim_structure *hsd); -extern void free_dim_structure(struct dim_structure *hsd); - -#endif /* EVENTS_H */ diff --git a/libcrystfel/src/filters.c b/libcrystfel/src/filters.c index 352046aa..9d01bac3 100644 --- a/libcrystfel/src/filters.c +++ b/libcrystfel/src/filters.c @@ -3,11 +3,11 @@ * * Image filtering * - * Copyright © 2012-2020 Deutsches Elektronen-Synchrotron DESY, + * Copyright © 2012-2021 Deutsches Elektronen-Synchrotron DESY, * a research centre of the Helmholtz Association. * * Authors: - * 2010-2016 Thomas White <taw@physics.org> + * 2010-2020 Thomas White <taw@physics.org> * 2013 Anton Barty <anton.barty@desy.de> * * This file is part of CrystFEL. @@ -84,8 +84,8 @@ void filter_noise(struct image *image) { int i; - for ( i=0; i<image->det->n_panels; i++ ) { - struct panel *p = &image->det->panels[i]; + for ( i=0; i<image->detgeom->n_panels; i++ ) { + struct detgeom_panel *p = &image->detgeom->panels[i]; filter_noise_in_panel(image->dp[i], p->w, p->h); } } @@ -149,14 +149,14 @@ void filter_median(struct image *image, int size) /* Determine local background * (median over window width either side of current pixel) */ - for ( pn=0; pn<image->det->n_panels; pn++ ) { + for ( pn=0; pn<image->detgeom->n_panels; pn++ ) { int fs, ss; int i; - struct panel *p; + struct detgeom_panel *p; float *localBg; - p = &image->det->panels[pn]; + p = &image->detgeom->panels[pn]; localBg = calloc(p->w*p->h, sizeof(float)); if ( localBg == NULL ) { diff --git a/libcrystfel/src/filters.h b/libcrystfel/src/filters.h index 17c235b1..83b76b9b 100644 --- a/libcrystfel/src/filters.h +++ b/libcrystfel/src/filters.h @@ -1,13 +1,13 @@ /* - * peaks.h + * filters.h * * Image filtering * - * Copyright © 2012-2020 Deutsches Elektronen-Synchrotron DESY, + * Copyright © 2012-2021 Deutsches Elektronen-Synchrotron DESY, * a research centre of the Helmholtz Association. * * Authors: - * 2010,2012-2013 Thomas White <taw@physics.org> + * 2010-2019 Thomas White <taw@physics.org> * * This file is part of CrystFEL. * @@ -29,10 +29,6 @@ #ifndef FILTERS_H #define FILTERS_H -#ifdef HAVE_CONFIG_H -#include <config.h> -#endif - #ifdef __cplusplus extern "C" { #endif diff --git a/libcrystfel/src/fom.c b/libcrystfel/src/fom.c new file mode 100644 index 00000000..af1ba8fe --- /dev/null +++ b/libcrystfel/src/fom.c @@ -0,0 +1,1465 @@ +/* + * fom.c + * + * Figure of merit calculation + * + * Copyright © 2012-2021 Deutsches Elektronen-Synchrotron DESY, + * a research centre of the Helmholtz Association. + * + * Authors: + * 2010-2021 Thomas White <taw@physics.org> + * 2013 Lorenzo Galli <lorenzo.galli@desy.de> + * + * 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 <gsl/gsl_errno.h> +#include <gsl/gsl_statistics.h> +#include <gsl/gsl_fit.h> +#include <assert.h> + +#include "utils.h" +#include "fom.h" +#include "cell.h" +#include "cell-utils.h" +#include "reflist.h" +#include "reflist-utils.h" + +/** + * \file fom.h + */ + +struct fom_context +{ + enum fom_type fom; + int nshells; + int *cts; + + /* For R-factors */ + double *num; + double *den; + + /* For "double" R-factors */ + double *num2; + double *den2; + + /* For CCs */ + double **vec1; + double **vec2; + int *n; + int nmax; + + /* For "counting" things e.g. d1sig or d2sig */ + int *n_within; + + long int *n_meas; + long int *possible; +}; + + +static struct fom_context *init_fom(enum fom_type fom, int nmax, int nshells) +{ + struct fom_context *fctx; + int i; + + fctx = malloc(sizeof(struct fom_context)); + if ( fctx == NULL ) return NULL; + + fctx->fom = fom; + fctx->nshells = nshells; + fctx->cts = malloc(nshells*sizeof(int)); + for ( i=0; i<nshells; i++ ) { + fctx->cts[i] = 0; + } + + fctx->num = NULL; + fctx->den = NULL; + fctx->num2 = NULL; + fctx->den2 = NULL; + fctx->possible = NULL; + + switch ( fctx->fom ) { + + case FOM_RANORSPLIT : + fctx->num2 = malloc(nshells*sizeof(double)); + fctx->den2 = malloc(nshells*sizeof(double)); + if ( (fctx->num2 == NULL) || (fctx->den2 == NULL) ) return NULL; + for ( i=0; i<nshells; i++ ) { + fctx->num2[i] = 0.0; + fctx->den2[i] = 0.0; + } + /* Intentional fall-through (no break) */ + + case FOM_R1I : + case FOM_R1F : + case FOM_R2 : + case FOM_RSPLIT : + case FOM_RANO : + case FOM_MEAN_INTENSITY : + case FOM_SNR : + case FOM_REDUNDANCY : + fctx->num = malloc(nshells*sizeof(double)); + fctx->den = malloc(nshells*sizeof(double)); + if ( (fctx->num == NULL) || (fctx->den == NULL) ) return NULL; + for ( i=0; i<nshells; i++ ) { + fctx->num[i] = 0.0; + fctx->den[i] = 0.0; + } + break; + + case FOM_COMPLETENESS : + /* Uses 'cts' and 'possible' only */ + break; + + case FOM_NUM_MEASUREMENTS : + fctx->n_meas = calloc(nshells, sizeof(long int)); + if ( fctx->n_meas == NULL ) return NULL; + break; + + case FOM_CC : + case FOM_CCSTAR : + case FOM_CCANO : + case FOM_CRDANO : + fctx->vec1 = malloc(nshells*sizeof(double *)); + fctx->vec2 = malloc(nshells*sizeof(double *)); + if ( (fctx->vec1 == NULL) || (fctx->vec2 == NULL) ) return NULL; + for ( i=0; i<nshells; i++ ) { + fctx->vec1[i] = malloc(nmax*sizeof(double)); + if ( fctx->vec1[i] == NULL ) return NULL; + fctx->vec2[i] = malloc(nmax*sizeof(double)); + if ( fctx->vec2[i] == NULL ) return NULL; + fctx->n = malloc(nshells*sizeof(int)); + if ( fctx->n == NULL ) return NULL; + } + for ( i=0; i<nshells; i++ ) { + fctx->n[i] = 0; + } + fctx->nmax = nmax; + break; + + case FOM_D1SIG : + case FOM_D2SIG : + fctx->n_within = malloc(nshells*sizeof(int)); + if ( fctx->n_within == NULL ) return NULL; + for ( i=0; i<nshells; i++ ) { + fctx->n_within[i] = 0; + } + break; + + } + + return fctx; +} + + +static int add_to_fom(struct fom_context *fctx, + Reflection *refl1, + Reflection *refl2, + Reflection *refl1bij, + Reflection *refl2bij, + int bin) +{ + double i1, i2, i1bij, i2bij, sig1, sig2; + double im, imbij; + int bad = 0; + + fctx->cts[bin]++; + + switch ( fctx->fom ) { + + case FOM_R1I : + i1 = get_intensity(refl1); + i2 = get_intensity(refl2); + fctx->num[bin] += fabs(i1 - i2); + fctx->den[bin] += i1; + break; + + case FOM_R1F : + i1 = get_intensity(refl1); + i2 = get_intensity(refl2); + fctx->num[bin] += fabs(sqrt(i1) - sqrt(i2)); + fctx->den[bin] += sqrt(i1); + break; + + case FOM_R2 : + i1 = get_intensity(refl1); + i2 = get_intensity(refl2); + fctx->num[bin] += pow(i1 - i2, 2.0); + fctx->den[bin] += pow(i1, 2.0); + break; + + case FOM_RSPLIT : + i1 = get_intensity(refl1); + i2 = get_intensity(refl2); + fctx->num[bin] += fabs(i1 - i2); + fctx->den[bin] += i1 + i2; + break; + + case FOM_CC : + case FOM_CCSTAR : + assert(fctx->n[bin] < fctx->nmax); + i1 = get_intensity(refl1); + i2 = get_intensity(refl2); + fctx->vec1[bin][fctx->n[bin]] = i1; + fctx->vec2[bin][fctx->n[bin]] = i2; + fctx->n[bin]++; + break; + + case FOM_CCANO : + case FOM_CRDANO : + assert(fctx->n[bin] < fctx->nmax); + i1 = get_intensity(refl1); + i2 = get_intensity(refl2); + i1bij = get_intensity(refl1bij); + i2bij = get_intensity(refl2bij); + fctx->vec1[bin][fctx->n[bin]] = i1 - i1bij; + fctx->vec2[bin][fctx->n[bin]] = i2 - i2bij; + fctx->n[bin]++; + break; + + case FOM_RANORSPLIT : + i1 = get_intensity(refl1); + i2 = get_intensity(refl2); + fctx->num2[bin] += fabs(i1 - i2); + fctx->den2[bin] += i1 + i2; + /* Intentional fall-through (no break) */ + + case FOM_RANO : + i1 = get_intensity(refl1); + i2 = get_intensity(refl2); + i1bij = get_intensity(refl1bij); + i2bij = get_intensity(refl2bij); + im = (i1 + i2)/2.0; + imbij = (i1bij + i2bij)/2.0; + fctx->num[bin] += fabs(im - imbij); + fctx->den[bin] += im + imbij; + break; + + case FOM_D1SIG : + i1 = get_intensity(refl1); + i2 = get_intensity(refl2); + sig1 = get_esd_intensity(refl1); + sig2 = get_esd_intensity(refl2); + if ( fabs(i1-i2) < sqrt(sig1*sig1 + sig2*sig2) ) { + fctx->n_within[bin]++; + } + break; + + case FOM_D2SIG : + i1 = get_intensity(refl1); + i2 = get_intensity(refl2); + sig1 = get_esd_intensity(refl1); + sig2 = get_esd_intensity(refl2); + if ( fabs(i1-i2) < 2.0*sqrt(sig1*sig1 + sig2*sig2) ) { + fctx->n_within[bin]++; + } + break; + + case FOM_NUM_MEASUREMENTS : + fctx->n_meas[bin] += get_redundancy(refl1); + break; + + case FOM_REDUNDANCY : + fctx->num[bin] += get_redundancy(refl1); + fctx->den[bin] += 1.0; + break; + + case FOM_SNR : + i1 = get_intensity(refl1); + sig1 = get_esd_intensity(refl1); + if ( isfinite(i1/sig1) ) { + fctx->num[bin] += i1/sig1; + fctx->den[bin] += 1.0; + } else { + bad = 1; + } + break; + + case FOM_MEAN_INTENSITY : + i1 = get_intensity(refl1); + fctx->num[bin] += i1; + fctx->den[bin] += 1.0; + break; + + case FOM_COMPLETENESS : + /* fctx->cts already incremented, as needed. + * Will calculate possible reflections later */ + break; + + } + + return bad; +} + + +/** + * Calculates the overall value for the %fom_context + * + * You must have previously called fom_calculate() + */ +double fom_overall_value(struct fom_context *fctx) +{ + double overall_num = INFINITY; + double overall_den = 0.0; + double overall_num2 = INFINITY; + double overall_den2 = 0.0; + int i; + double *overall_vec1; + double *overall_vec2; + int overall_n; + double *overall_along_diagonal; + double *overall_perpend_diagonal; + double variance_signal; + double variance_error; + double cc = INFINITY; + long int total_meas = 0; + long int overall_cts = 0; + long int overall_possible = 0; + + switch ( fctx->fom ) { + + case FOM_R1I : + case FOM_R1F : + case FOM_R2 : + case FOM_RSPLIT : + case FOM_RANO : + case FOM_REDUNDANCY : + case FOM_SNR : + case FOM_MEAN_INTENSITY : + overall_num = 0.0; + overall_den = 0.0; + for ( i=0; i<fctx->nshells; i++ ) { + overall_num += fctx->num[i]; + overall_den += fctx->den[i]; + } + break; + + case FOM_RANORSPLIT : + overall_num = 0.0; + overall_den = 0.0; + for ( i=0; i<fctx->nshells; i++ ) { + overall_num += fctx->num[i]; + overall_den += fctx->den[i]; + } + overall_num2 = 0.0; + overall_den2 = 0.0; + for ( i=0; i<fctx->nshells; i++ ) { + overall_num2 += fctx->num2[i]; + overall_den2 += fctx->den2[i]; + } + break; + + case FOM_CC : + case FOM_CCSTAR : + case FOM_CCANO : + overall_vec1 = malloc(fctx->nmax*sizeof(double)); + overall_vec2 = malloc(fctx->nmax*sizeof(double)); + overall_n = 0; + for ( i=0; i<fctx->nshells; i++ ) { + int j; + for ( j=0; j<fctx->n[i]; j++ ) { + overall_vec1[overall_n] = fctx->vec1[i][j]; + overall_vec2[overall_n] = fctx->vec2[i][j]; + overall_n++; + } + } + cc = gsl_stats_correlation(overall_vec1, 1, overall_vec2, 1, + overall_n); + free(overall_vec1); + free(overall_vec2); + break; + + case FOM_CRDANO : + overall_along_diagonal = malloc(fctx->nmax*sizeof(double)); + overall_perpend_diagonal = malloc(fctx->nmax*sizeof(double)); + overall_n = 0; + for ( i=0; i<fctx->nshells; i++ ) { + int j; + for ( j=0; j<fctx->n[i]; j++ ) { + overall_along_diagonal[overall_n] = + ( fctx->vec1[i][j] + fctx->vec2[i][j] ) + / sqrt(2.0); + overall_perpend_diagonal[overall_n] = + ( fctx->vec1[i][j] - fctx->vec2[i][j] ) + / sqrt(2.0); + overall_n++; + } + } + variance_signal = gsl_stats_variance_m(overall_along_diagonal, + 1, overall_n, 0.0); + variance_error = gsl_stats_variance_m(overall_perpend_diagonal, + 1, overall_n, 0.0); + cc = sqrt(variance_signal / variance_error ); + + free(overall_along_diagonal); + free(overall_perpend_diagonal); + break; + + case FOM_D1SIG : + case FOM_D2SIG : + overall_num = 0.0; + overall_den = 0.0; + for ( i=0; i<fctx->nshells; i++ ) { + overall_num += fctx->n_within[i]; + overall_den += fctx->cts[i]; + } + break; + + case FOM_NUM_MEASUREMENTS : + total_meas = 0; + for ( i=0; i<fctx->nshells; i++ ) { + total_meas += fctx->n_meas[i]; + } + break; + + case FOM_COMPLETENESS : + for ( i=0; i<fctx->nshells; i++ ) { + overall_cts += fctx->cts[i]; + overall_possible += fctx->possible[i]; + } + break; + + } + + switch ( fctx->fom ) { + + case FOM_R1I : + case FOM_R1F : + case FOM_REDUNDANCY : + case FOM_SNR : + case FOM_MEAN_INTENSITY : + return overall_num/overall_den; + + case FOM_COMPLETENESS : + return (double)overall_cts / overall_possible; + + case FOM_NUM_MEASUREMENTS : + return total_meas; + + case FOM_R2 : + return sqrt(overall_num/overall_den); + + case FOM_RSPLIT : + return 2.0*(overall_num/overall_den) / sqrt(2.0); + + case FOM_CC : + case FOM_CCANO : + case FOM_CRDANO : + return cc; + + case FOM_CCSTAR : + return sqrt((2.0*cc)/(1.0+cc)); + + case FOM_RANO : + return 2.0*(overall_num/overall_den); + + case FOM_RANORSPLIT : + return (2.0*(overall_num/overall_den)) / + (2.0*(overall_num2/overall_den2) / sqrt(2.0)); + + case FOM_D1SIG : + case FOM_D2SIG : + return overall_num/overall_den; + + } + + ERROR("This point is never reached.\n"); + abort(); +} + + +/** + * Calculates the figure of merit for the specified shell number. + * You must have previously called fom_calculate() + */ +double fom_shell_value(struct fom_context *fctx, int i) +{ + double cc; + int j; + double variance_signal; + double variance_error; + double *along_diagonal; + double *perpend_diagonal; + + switch ( fctx->fom ) { + + case FOM_R1I : + case FOM_R1F : + case FOM_REDUNDANCY : + case FOM_SNR : + case FOM_MEAN_INTENSITY : + return fctx->num[i]/fctx->den[i]; + + case FOM_R2 : + return sqrt(fctx->num[i]/fctx->den[i]); + + case FOM_RSPLIT : + return 2.0*(fctx->num[i]/fctx->den[i]) / sqrt(2.0); + + case FOM_CC : + case FOM_CCANO : + return gsl_stats_correlation(fctx->vec1[i], 1, fctx->vec2[i], 1, + fctx->n[i]); + + case FOM_CCSTAR : + cc = gsl_stats_correlation(fctx->vec1[i], 1, fctx->vec2[i], 1, + fctx->n[i]); + return sqrt((2.0*cc)/(1.0+cc)); + + case FOM_RANO : + return 2.0 * fctx->num[i]/fctx->den[i]; + + case FOM_RANORSPLIT : + return (2.0*fctx->num[i]/fctx->den[i]) / + (2.0*(fctx->num2[i]/fctx->den2[i]) / sqrt(2.0)); + + case FOM_CRDANO : + along_diagonal = malloc(fctx->n[i] * sizeof(double)); + perpend_diagonal = malloc(fctx->n[i] * sizeof(double)); + for ( j=0; j<fctx->n[i]; j++ ) { + along_diagonal[j] = ( fctx->vec1[i][j] + + fctx->vec2[i][j] ) / sqrt(2.0); + perpend_diagonal[j] = ( fctx->vec1[i][j] - + fctx->vec2[i][j] ) / sqrt(2.0); + } + variance_signal = gsl_stats_variance_m(along_diagonal, 1, + fctx->n[i], 0.0); + variance_error = gsl_stats_variance_m(perpend_diagonal, 1, + fctx->n[i], 0.0); + free(along_diagonal); + free(perpend_diagonal); + return sqrt(variance_signal / variance_error); + + case FOM_D1SIG : + case FOM_D2SIG : + return (double)fctx->n_within[i] / fctx->cts[i]; + + case FOM_NUM_MEASUREMENTS : + return fctx->n_meas[i]; + + case FOM_COMPLETENESS : + return (double)fctx->cts[i] / fctx->possible[i]; + + } + + ERROR("This point is never reached.\n"); + abort(); +} + + +/** + * \param rmin: The minimum value of 1/d, in m^-1 + * \param rmax: The maximum value of 1/d, in m^-1 + * \param nshells: The number of shells to use + * + * Create a %fom_shells structure for the specified minimum and maximum + * resolution limits + * + * Returns the %fom_shells structure, or NULL on error. + */ +struct fom_shells *fom_make_resolution_shells(double rmin, double rmax, + int nshells) +{ + struct fom_shells *s; + double total_vol, vol_per_shell; + int i; + + s = malloc(sizeof(struct fom_shells)); + if ( s == NULL ) return NULL; + + s->rmins = malloc(nshells*sizeof(double)); + s->rmaxs = malloc(nshells*sizeof(double)); + + if ( (s->rmins==NULL) || (s->rmaxs==NULL) ) { + ERROR("Couldn't allocate memory for resolution shells.\n"); + free(s); + return NULL; + } + + s->nshells = nshells; + + total_vol = pow(rmax, 3.0) - pow(rmin, 3.0); + vol_per_shell = total_vol / nshells; + s->rmins[0] = rmin; + for ( i=1; i<nshells; i++ ) { + + double r; + + r = vol_per_shell + pow(s->rmins[i-1], 3.0); + r = pow(r, 1.0/3.0); + + /* Shells of constant volume */ + s->rmaxs[i-1] = r; + s->rmins[i] = r; + + } + s->rmaxs[nshells-1] = rmax; + + return s; +} + + +/** + * \param s: A %fom_shells structure + * \param i: The shell number + * + * Returns the value of 1/d at the middle of the shell, + * i.e. the mean of the minimum and maximum 1/d values for the shell + */ +double fom_shell_centre(struct fom_shells *s, int i) +{ + return s->rmins[i] + (s->rmaxs[i] - s->rmins[i])/2.0; +} + + +static int get_bin(struct fom_shells *s, Reflection *refl, UnitCell *cell) +{ + double d; + int bin, j; + signed int h, k, l; + + get_indices(refl, &h, &k, &l); + d = 2.0 * resolution(cell, h, k, l); + + bin = -1; + for ( j=0; j<s->nshells; j++ ) { + if ( (d>s->rmins[j]) && (d<=s->rmaxs[j]) ) { + bin = j; + break; + } + } + + /* Allow for slight rounding errors */ + if ( (bin == -1) && (d <= s->rmins[0]) ) bin = 0; + if ( (bin == -1) && (d >= s->rmaxs[s->nshells-1]) ) bin = 0; + assert(bin != -1); + + return bin; +} + + +static int wilson_scale(RefList *list1, RefList *list2, UnitCell *cell) +{ + Reflection *refl1; + Reflection *refl2; + RefListIterator *iter; + int max_n = 256; + int n = 0; + double *x; + double *y; + int r; + double G, B; + double c0, c1, cov00, cov01, cov11, chisq; + + x = malloc(max_n*sizeof(double)); + y = malloc(max_n*sizeof(double)); + if ( (x==NULL) || (y==NULL) ) { + ERROR("Failed to allocate memory for scaling.\n"); + return 1; + } + + for ( refl1 = first_refl(list1, &iter); + refl1 != NULL; + refl1 = next_refl(refl1, iter) ) + { + signed int h, k, l; + double Ih1, Ih2; + double res; + + get_indices(refl1, &h, &k, &l); + res = resolution(cell, h, k, l); + + refl2 = find_refl(list2, h, k, l); + assert(refl2 != NULL); + + Ih1 = get_intensity(refl1); + Ih2 = get_intensity(refl2); + + if ( (Ih1 <= 0.0) || (Ih2 <= 0.0) ) continue; + if ( isnan(Ih1) || isinf(Ih1) ) continue; + if ( isnan(Ih2) || isinf(Ih2) ) continue; + + if ( n == max_n ) { + max_n *= 2; + x = realloc(x, max_n*sizeof(double)); + y = realloc(y, max_n*sizeof(double)); + if ( (x==NULL) || (y==NULL) ) { + ERROR("Failed to allocate memory for scaling.\n"); + return 1; + } + } + + x[n] = res*res; + y[n] = log(Ih1/Ih2); + n++; + + } + + if ( n < 2 ) { + ERROR("Not enough reflections for scaling\n"); + return 1; + } + + r = gsl_fit_linear(x, 1, y, 1, n, &c0, &c1, + &cov00, &cov01, &cov11, &chisq); + + if ( r ) { + ERROR("Scaling failed.\n"); + return 1; + } + + G = exp(c0); + B = c1/2.0; + + STATUS("Relative scale factor = %f, relative B factor = %f A^2\n", + G, B*1e20); + STATUS("A scale factor greater than 1 means that the second reflection " + "list is weaker than the first.\n"); + STATUS("A positive relative B factor means that the second reflection " + "list falls off with resolution more quickly than the first.\n"); + + free(x); + free(y); + + /* Apply the scaling factor */ + for ( refl2 = first_refl(list2, &iter); + refl2 != NULL; + refl2 = next_refl(refl2, iter) ) + { + signed int h, k, l; + double res; + double corr; + + get_indices(refl2, &h, &k, &l); + res = resolution(cell, h, k, l); + + corr = G * exp(2.0*B*res*res); + set_intensity(refl2, get_intensity(refl2)*corr); + set_esd_intensity(refl2, get_esd_intensity(refl2)*corr); + + } + return 0; +} + + +static int calculate_possible(struct fom_context *fctx, + struct fom_shells *shells, + UnitCell *cell, + const SymOpList *sym) +{ + RefList *counted; + int hmax, kmax, lmax; + double ax, ay, az; + double bx, by, bz; + double cx, cy, cz; + signed int h, k, l; + + fctx->possible = calloc(fctx->nshells, sizeof(long int)); + if ( fctx->possible == NULL ) return 1; + + counted = reflist_new(); + if ( counted == NULL ) { + free(fctx->possible); + return 1; + } + + cell_get_cartesian(cell, &ax, &ay, &az, + &bx, &by, &bz, + &cx, &cy, &cz); + hmax = shells->rmaxs[fctx->nshells-1] * modulus(ax, ay, az); + kmax = shells->rmaxs[fctx->nshells-1] * modulus(bx, by, bz); + lmax = shells->rmaxs[fctx->nshells-1] * modulus(cx, cy, cz); + for ( h=-hmax; h<=hmax; h++ ) { + for ( k=-kmax; k<=kmax; k++ ) { + for ( l=-lmax; l<=lmax; l++ ) { + + double d; + signed int hs, ks, ls; + int bin; + int i; + + get_asymm(sym, h, k, l, &hs, &ks, &ls); + d = 2.0 * resolution(cell, hs, ks, ls); + + if ( forbidden_reflection(cell, h, k, l) ) continue; + + bin = -1; + for ( i=0; i<fctx->nshells; i++ ) { + if ( (d>shells->rmins[i]) && (d<=shells->rmaxs[i]) ) { + bin = i; + break; + } + } + if ( bin == -1 ) continue; + + if ( find_refl(counted, hs, ks, ls) != NULL ) continue; + add_refl(counted, hs, ks, ls); + + fctx->possible[bin]++; + + } + } + } + reflist_free(counted); + + return 0; +} + + +int fom_is_anomalous(enum fom_type fom) +{ + switch ( fom ) { + + case FOM_CCANO: + case FOM_RANO: + case FOM_CRDANO: + case FOM_RANORSPLIT: + return 1; + + case FOM_R1I: + case FOM_R1F: + case FOM_R2: + case FOM_RSPLIT: + case FOM_CC: + case FOM_CCSTAR: + case FOM_D1SIG: + case FOM_D2SIG: + case FOM_NUM_MEASUREMENTS: + case FOM_REDUNDANCY: + case FOM_SNR: + case FOM_MEAN_INTENSITY: + case FOM_COMPLETENESS: + return 0; + } + + ERROR("This point never reached\n"); + abort(); +} + + +int fom_is_comparison(enum fom_type fom) +{ + switch ( fom ) { + + case FOM_CCANO: + case FOM_RANO: + case FOM_CRDANO: + case FOM_RANORSPLIT: + case FOM_R1I: + case FOM_R1F: + case FOM_R2: + case FOM_RSPLIT: + case FOM_CC: + case FOM_CCSTAR: + case FOM_D1SIG: + case FOM_D2SIG: + return 1; + + case FOM_NUM_MEASUREMENTS: + case FOM_REDUNDANCY: + case FOM_SNR: + case FOM_MEAN_INTENSITY: + case FOM_COMPLETENESS: + return 0; + } + + ERROR("This point never reached\n"); + abort(); +} + + +static int is_single_list(enum fom_type fom) +{ + switch ( fom ) { + + case FOM_CCANO: + case FOM_RANO: + case FOM_CRDANO: + case FOM_RANORSPLIT: + case FOM_R1I: + case FOM_R1F: + case FOM_R2: + case FOM_RSPLIT: + case FOM_CC: + case FOM_CCSTAR: + case FOM_D1SIG: + case FOM_D2SIG: + return 0; + + case FOM_NUM_MEASUREMENTS: + case FOM_REDUNDANCY: + case FOM_SNR: + case FOM_MEAN_INTENSITY: + case FOM_COMPLETENESS: + return 1; + } + + ERROR("This point never reached\n"); + abort(); +} + + +/** + * \param list1: A %RefList + * \param list2: A %RefList + * \param cell: A %UnitCell + * \param shells: A %fom_shells structure + * \param fom: The figure of merit to calculate + * \param noscale: Non-zero to disable scaline of reflection lists + * \param sym: The symmetry of \p list1 and \p list2. + * + * Calculates the specified figure of merit, comparing the two reflection lists. + * + * The \p cell and \p sym must match both reflection lists. You should also have + * called fom_select_reflection_pairs() to pre-process the lists. + * + * If the figure of merit does not involve comparison (e.g. %FOM_SNR), + * then \p list1 will be used. In this case, \p list2 and \p noscale will be + * ignored. Use fom_select_reflections() instead of fom_select_reflection_pairs() + * in this case. + * + * \returns a %fom_context structure. Use fom_shell_value() et al., to + * extract the actual figure of merit values. + */ +struct fom_context *fom_calculate(RefList *list1, RefList *list2, UnitCell *cell, + struct fom_shells *shells, enum fom_type fom, + int noscale, const SymOpList *sym) +{ + Reflection *refl1; + RefListIterator *iter; + struct fom_context *fctx; + long int n_out = 0; + long int n_rej = 0; + + fctx = init_fom(fom, num_reflections(list1), shells->nshells); + + if ( fctx == NULL ) { + ERROR("Couldn't allocate memory for resolution shells.\n"); + return NULL; + } + + if ( !is_single_list(fom) ) { + if ( !noscale && wilson_scale(list1, list2, cell) ) { + ERROR("Error with scaling.\n"); + return NULL; + } + + for ( refl1 = first_refl(list1, &iter); + refl1 != NULL; + refl1 = next_refl(refl1, iter) ) + { + Reflection *refl2; + signed int h, k, l; + set_flag(refl1, 0); + get_indices(refl1, &h, &k, &l); + refl2 = find_refl(list2, h, k, l); + assert(refl2 != NULL); + set_flag(refl2, 0); + } + } + + for ( refl1 = first_refl(list1, &iter); + refl1 != NULL; + refl1 = next_refl(refl1, iter) ) + { + signed int h, k, l; + int bin; + Reflection *refl2; + Reflection *refl1_bij = NULL; + Reflection *refl2_bij = NULL; + + get_indices(refl1, &h, &k, &l); + + if ( is_single_list(fom) ) { + refl2 = NULL; + } else { + refl2 = find_refl(list2, h, k, l); + if ( refl2 == NULL ) continue; + } + + bin = get_bin(shells, refl1, cell); + if ( bin == -1 ) { + n_out++; + continue; + } + + if ( fom_is_anomalous(fom) ) { + + signed int hb, kb, lb; + + if ( find_equiv_in_list(list1, -h, -k, -l, sym, + &hb, &kb, &lb) ) + { + refl1_bij = find_refl(list1, hb, kb, lb); + } + + if ( find_equiv_in_list(list2, -h, -k, -l, sym, + &hb, &kb, &lb) ) + { + refl2_bij = find_refl(list2, hb, kb, lb); + } + + /* Each reflection must only be counted once, whether + * we are visiting it now as "normal" or "bij" */ + if ( get_flag(refl1) ) continue; + assert(!get_flag(refl2)); + set_flag(refl1, 1); + set_flag(refl1_bij, 1); + set_flag(refl2, 1); + set_flag(refl2_bij, 1); + + assert(refl1_bij != NULL); + assert(refl2_bij != NULL); + + } + + n_rej += add_to_fom(fctx, refl1, refl2, refl1_bij, refl2_bij, bin); + + } + if ( n_out ) { + ERROR("WARNING: %i reflection pairs outside range.\n", n_out); + } + if ( n_rej ) { + if ( fom == FOM_SNR ) { + ERROR("WARNING: %li reflections had infinite or " + "invalid values of I/sigma(I).\n", n_rej); + } else { + ERROR("WARNING: %li reflections rejected by add_to_fom\n", + n_rej); + } + } + + if ( fom == FOM_COMPLETENESS ) { + calculate_possible(fctx, shells, cell, sym); + } + + return fctx; +} + + +/** + * \param list1: The first input %RefList + * \param list2: The second input %RefList + * \param plist1_acc: Pointer to location for accepted list + * \param plist2_acc: Pointer to location for accepted list + * \param cell: A %UnitCell + * \param sym: The symmetry of \p raw_list + * \param anom: Non-zero if you will calculate a FoM for anomalous signal + * \param rmin_fix: If positive, minimum resolution to use + * \param rmax_fix: If positive, maximum resolution to use + * \param sigma_cutoff: Minimum I/sigI value + * \param ignore_negs: Non-zero to filter out negative intensities + * \param zero_negs: Non-zero to set negative intensities to zero + * \param mul_cutoff: Minimum number of measurements per reflection + * + * Selects reflections suitable for use with fom_calculate(). + * + * Use -INFINITY for \p sigma_cutoff to disable the check. + * Set \p mul_cutoff to zero to disable the check. + * + * \returns a %fom_rejections structure with the counts of reflections. + */ +struct fom_rejections fom_select_reflection_pairs(RefList *list1, RefList *list2, + RefList **plist1_acc, + RefList **plist2_acc, + UnitCell *cell, SymOpList *sym, + int anom, double rmin_fix, double rmax_fix, + double sigma_cutoff, int ignore_negs, + int zero_negs, int mul_cutoff) +{ + Reflection *refl1; + RefListIterator *iter; + struct fom_rejections rej; + RefList *list1_acc; + RefList *list2_acc; + + rej.common = 0; + rej.low_snr = 0; + rej.negative_deleted = 0; + rej.negative_zeroed = 0; + rej.few_measurements = 0; + rej.outside_resolution_range = 0; + rej.no_bijvoet = 0; + rej.centric = 0; + rej.nan_inf_value = 0; + + list1_acc = reflist_new(); + list2_acc = reflist_new(); + + for ( refl1 = first_refl(list1, &iter); + refl1 != NULL; + refl1 = next_refl(refl1, iter) ) + { + signed int h, k, l; + double val1, val2; + double esd1, esd2; + int mul1, mul2; + Reflection *refl2; + Reflection *refl1_acc; + Reflection *refl2_acc; + + get_indices(refl1, &h, &k, &l); + + refl2 = find_refl(list2, h, k, l); + if ( refl2 == NULL ) continue; + + val1 = get_intensity(refl1); + val2 = get_intensity(refl2); + + esd1 = get_esd_intensity(refl1); + esd2 = get_esd_intensity(refl2); + + mul1 = get_redundancy(refl1); + mul2 = get_redundancy(refl2); + + if ( !isfinite(val1) || !isfinite(val2) + || !isfinite(esd1) || !isfinite(esd2) ) + { + rej.nan_inf_value++; + continue; + } + + if ( (val1 < sigma_cutoff * esd1) + || (val2 < sigma_cutoff * esd2) ) + { + rej.low_snr++; + continue; + } + + if ( ignore_negs && ((val1 < 0.0) || (val2 < 0.0)) ) { + rej.negative_deleted++; + continue; + } + + if ( (mul1 < mul_cutoff) || (mul2 < mul_cutoff) ) { + rej.few_measurements++; + continue; + } + + if ( zero_negs ) { + int d = 0; + if ( val1 < 0.0 ) { + val1 = 0.0; + d = 1; + } + if ( val2 < 0.0 ) { + val2 = 0.0; + d = 1; + } + if ( d ) rej.negative_zeroed++; + continue; + } + + if ( rmin_fix > 0.0 ) { + double res = 2.0*resolution(cell, h, k, l); + if ( res < rmin_fix ) { + rej.outside_resolution_range++; + continue; + } + } + + if ( rmax_fix > 0.0 ) { + double res = 2.0*resolution(cell, h, k, l); + if ( res > rmax_fix ) { + rej.outside_resolution_range++; + continue; + } + } + + refl1_acc = add_refl(list1_acc, h, k, l); + copy_data(refl1_acc, refl1); + set_intensity(refl1_acc, val1); + + refl2_acc = add_refl(list2_acc, h, k, l); + copy_data(refl2_acc, refl2); + set_intensity(refl2_acc, val2); + + rej.common++; + + } + + /* For anomalous figures of merit, we additionally require that we have + * all the Bijvoet pairs after the above rejection tests */ + if ( anom ) { + + list1 = list1_acc; + list2 = list2_acc; + list1_acc = reflist_new(); + list2_acc = reflist_new(); + + rej.common = 0; + + for ( refl1 = first_refl(list1, &iter); + refl1 != NULL; + refl1 = next_refl(refl1, iter) ) + { + Reflection *refl1_bij = NULL; + Reflection *refl2_bij = NULL; + signed int h, k, l; + signed int hb, kb, lb; + Reflection *refl1_acc; + Reflection *refl2_acc; + Reflection *refl2; + double val1, val2; + + get_indices(refl1, &h, &k, &l); + + refl2 = find_refl(list2, h, k, l); + assert(refl2 != NULL); + + val1 = get_intensity(refl1); + val2 = get_intensity(refl2); + + if ( is_centric(h, k, l, sym) ) { + rej.centric++; + continue; + } + + if ( find_equiv_in_list(list1, -h, -k, -l, sym, + &hb, &kb, &lb) ) + { + refl1_bij = find_refl(list1, hb, kb, lb); + } + + if ( find_equiv_in_list(list2, -h, -k, -l, sym, + &hb, &kb, &lb) ) + { + refl2_bij = find_refl(list2, hb, kb, lb); + } + + if ( (refl1_bij == NULL) || (refl2_bij == NULL) ) { + rej.no_bijvoet++; + continue; + } + + refl1_acc = add_refl(list1_acc, h, k, l); + copy_data(refl1_acc, refl1); + set_intensity(refl1_acc, val1); + + refl2_acc = add_refl(list2_acc, h, k, l); + copy_data(refl2_acc, refl2); + set_intensity(refl2_acc, val2); + + rej.common++; + } + } + + *plist1_acc = list1_acc; + *plist2_acc = list2_acc; + return rej; +} + + +/** + * \param raw_list: The input %RefList + * \param plist_acc: Pointer to location for accepted list + * \param cell: A %UnitCell + * \param sym: The symmetry of \p raw_list + * \param rmin_fix: If positive, minimum resolution to use + * \param rmax_fix: If positive, maximum resolution to use + * \param sigma_cutoff: Minimum I/sigI value + * \param ignore_negs: Non-zero to filter out negative intensities + * \param zero_negs: Non-zero to set negative intensities to zero + * \param mul_cutoff: Minimum number of measurements per reflection + * + * Use -INFINITY for \p sigma_cutoff to disable the check. + * Set \p mul_cutoff to zero to disable the check. + * + * \returns a %fom_rejections structure with the counts of reflections. + */ +struct fom_rejections fom_select_reflections(RefList *raw_list, + RefList **plist_acc, + UnitCell *cell, SymOpList *sym, + double rmin_fix, double rmax_fix, + double sigma_cutoff, int ignore_negs, + int zero_negs, int mul_cutoff) +{ + RefList *list; + Reflection *refl; + RefListIterator *iter; + struct fom_rejections rej; + + *plist_acc = NULL; + + rej.common = 0; + rej.low_snr = 0; + rej.negative_deleted = 0; + rej.negative_zeroed = 0; + rej.few_measurements = 0; + rej.outside_resolution_range = 0; + rej.no_bijvoet = 0; + rej.centric = 0; + rej.nan_inf_value = 0; + + list = reflist_new(); + if ( list == NULL ) return rej; + + for ( refl = first_refl(raw_list, &iter); + refl != NULL; + refl = next_refl(refl, iter) ) { + + signed int h, k, l; + double val, sig; + int ig = 0; + Reflection *new; + + get_indices(refl, &h, &k, &l); + + val = get_intensity(refl); + sig = get_esd_intensity(refl); + + if ( !isfinite(val) || !isfinite(sig) ) { + rej.nan_inf_value++; + continue; + } + + if ( val < sigma_cutoff * sig ) { + rej.low_snr++; + ig = 1; + } + + if ( ignore_negs && (val < 0.0) ) { + rej.negative_deleted++; + ig = 1; + } + + if ( zero_negs && (val < 0.0) ) { + set_intensity(refl, 0.0); + rej.negative_zeroed++; + } + + if ( rmin_fix > 0.0 ) { + double res = 2.0*resolution(cell, h, k, l); + if ( res < rmin_fix ) { + rej.outside_resolution_range++; + continue; + } + } + + if ( rmax_fix > 0.0 ) { + double res = 2.0*resolution(cell, h, k, l); + if ( res > rmax_fix ) { + rej.outside_resolution_range++; + continue; + } + } + + if ( ig ) continue; + + new = add_refl(list, h, k, l); + copy_data(new, refl); + } + + *plist_acc = list; + return rej; +} + + +/** + * \param fctx: A %fom_context structure + * + * \returns the total number of unique reflections + */ +int fom_overall_num_reflections(struct fom_context *fctx) +{ + int i; + long int n = 0; + + for ( i=0; i<fctx->nshells; i++ ) { + n += fctx->cts[i]; + } + return n; +} + + +/** + * \param fctx: A %fom_context structure + * \param i: Shell number + * + * \returns the number of unique reflections in the shell + */ +int fom_shell_num_reflections(struct fom_context *fctx, int i) +{ + return fctx->cts[i]; +} + + +/** + * \param fctx: A %fom_context structure + * + * This must only be called on a %fom_context for %FOM_COMPLETENESS. + * + * \returns the total number of reflections possible in all shells, taking into + * account symmetry and lattice absences, but not screw axis/glide place absences. + */ +int fom_overall_num_possible(struct fom_context *fctx) +{ + int i; + long int n = 0; + + assert(fctx->fom == FOM_COMPLETENESS); + + for ( i=0; i<fctx->nshells; i++ ) { + n += fctx->possible[i]; + } + return n; +} + + +/** + * \param fctx: A %fom_context structure + * \param i: Shell number + * + * This must only be called on a %fom_context for %FOM_COMPLETENESS. + * + * \returns the number of reflections possible in the shell, taking into account + * symmetry and lattice absences, but not screw axis/glide place absences. + */ +int fom_shell_num_possible(struct fom_context *fctx, int i) +{ + assert(fctx->fom == FOM_COMPLETENESS); + return fctx->possible[i]; +} + + +const char *fom_name(enum fom_type f) +{ + switch ( f ) { + case FOM_R1I : return "R1(I)"; + case FOM_R1F : return "R1(F)"; + case FOM_R2 : return "R2"; + case FOM_RSPLIT : return "Rsplit"; + case FOM_CC : return "CC"; + case FOM_CCSTAR : return "CC*"; + case FOM_CCANO : return "CCano"; + case FOM_CRDANO : return "CRDano"; + case FOM_RANO : return "Rano"; + case FOM_RANORSPLIT : return "Rano/Rsplit"; + case FOM_D1SIG : return "D<1sigma"; + case FOM_D2SIG : return "D<2sigma"; + case FOM_NUM_MEASUREMENTS : return "nMeas"; + case FOM_REDUNDANCY : return "Redundancy"; + case FOM_SNR : return "I/sigI"; + case FOM_MEAN_INTENSITY : return "mean I"; + case FOM_COMPLETENESS : return "Completeness"; + default : return "unknown FoM"; + } +} diff --git a/libcrystfel/src/fom.h b/libcrystfel/src/fom.h new file mode 100644 index 00000000..08bc655b --- /dev/null +++ b/libcrystfel/src/fom.h @@ -0,0 +1,141 @@ +/* + * fom.h + * + * Figure of merit calculation + * + * Copyright © 2012-2021 Deutsches Elektronen-Synchrotron DESY, + * a research centre of the Helmholtz Association. + * + * Authors: + * 2010-2021 Thomas White <taw@physics.org> + * 2013 Lorenzo Galli <lorenzo.galli@desy.de> + * + * 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 FOM_H +#define FOM_H + +/** + * \file fom.h + * Figure of merit calculation + */ + +#include <reflist.h> +#include <symmetry.h> + +/** + * Contains counts of rejected reflections + */ +struct fom_rejections +{ + int common; /**< Number of common reflection pairs accepted */ + int low_snr; /**< Reflections with I/sigI too low */ + int negative_deleted; /**< Negative intensities which were deleted */ + int negative_zeroed; /**< Negative intensities which were set to zero */ + int few_measurements; /**< Reflections with too few measurements */ + int outside_resolution_range; /**< Reflections outside resolution range */ + int no_bijvoet; /**< Reflections with no Bijvoet partner */ + int centric; /**< Reflections which are centric */ + int nan_inf_value; /**< Reflections with NaN or infinite intensity */ +}; + +/** + * An enumeration of possible figures of merit to calculate + */ +enum fom_type +{ + FOM_R1I, + FOM_R1F, + FOM_R2, + FOM_RSPLIT, + FOM_CC, + FOM_CCSTAR, + FOM_CCANO, + FOM_CRDANO, + FOM_RANO, + FOM_RANORSPLIT, + FOM_D1SIG, + FOM_D2SIG, + FOM_NUM_MEASUREMENTS, + FOM_REDUNDANCY, + FOM_SNR, + FOM_MEAN_INTENSITY, + FOM_COMPLETENESS, +}; + +struct fom_shells +{ + int nshells; + double *rmins; + double *rmaxs; +}; + +struct fom_context; + +extern struct fom_rejections fom_select_reflection_pairs(RefList *list1, + RefList *list2, + RefList **plist1_acc, + RefList **plist2_acc, + UnitCell *cell, + SymOpList *sym, + int anom, + double rmin_fix, + double rmax_fix, + double sigma_cutoff, + int ignore_negs, + int zero_negs, + int mul_cutoff); + +extern struct fom_rejections fom_select_reflections(RefList *list, + RefList **plist_acc, + UnitCell *cell, + SymOpList *sym, + double rmin_fix, + double rmax_fix, + double sigma_cutoff, + int ignore_negs, + int zero_negs, + int mul_cutoff); + + +extern struct fom_context *fom_calculate(RefList *list1, RefList *list2, + UnitCell *cell, + struct fom_shells *shells, + enum fom_type fom, int noscale, + const SymOpList *sym); + +extern struct fom_shells *fom_make_resolution_shells(double rmin, double rmax, + int nshells); + +extern double fom_shell_centre(struct fom_shells *s, int i); + +extern double fom_overall_value(struct fom_context *fctx); +extern double fom_shell_value(struct fom_context *fctx, int i); + +extern int fom_overall_num_reflections(struct fom_context *fctx); +extern int fom_shell_num_reflections(struct fom_context *fctx, int i); + +extern int fom_overall_num_possible(struct fom_context *fctx); +extern int fom_shell_num_possible(struct fom_context *fctx, int i); + +extern int fom_is_anomalous(enum fom_type f); +extern int fom_is_comparison(enum fom_type f); + +extern const char *fom_name(enum fom_type f); + +#endif /* FOM */ diff --git a/libcrystfel/src/geometry.c b/libcrystfel/src/geometry.c index 8f8a9672..adb95e69 100644 --- a/libcrystfel/src/geometry.c +++ b/libcrystfel/src/geometry.c @@ -3,11 +3,11 @@ * * Geometry of diffraction * - * Copyright © 2012-2020 Deutsches Elektronen-Synchrotron DESY, + * Copyright © 2012-2021 Deutsches Elektronen-Synchrotron DESY, * a research centre of the Helmholtz Association. * * Authors: - * 2010-2016 Thomas White <taw@physics.org> + * 2010-2021 Thomas White <taw@physics.org> * * This file is part of CrystFEL. * @@ -52,7 +52,7 @@ /** \file geometry.h */ static int locate_peak_on_panel(double x, double y, double z, double k, - struct panel *p, + struct detgeom_panel *p, double *pfs, double *pss) { double ctt, tta, phi; @@ -85,7 +85,7 @@ static int locate_peak_on_panel(double x, double y, double z, double k, gsl_matrix_set(M, 1, 0, p->cny); gsl_matrix_set(M, 1, 1, p->fsy); gsl_matrix_set(M, 1, 2, p->ssy); - gsl_matrix_set(M, 2, 0, p->clen*p->res); + gsl_matrix_set(M, 2, 0, p->cnz); gsl_matrix_set(M, 2, 1, p->fsz); gsl_matrix_set(M, 2, 2, p->ssz); @@ -103,6 +103,10 @@ static int locate_peak_on_panel(double x, double y, double z, double k, *pfs = fs; *pss = ss; + /* If "mu" is negative, then the reflection is in the + * wrong direction */ + if ( one_over_mu < 0.0 ) return 0; + /* Now, is this on this panel? */ if ( fs < 0.0 ) return 0; if ( fs >= p->w ) return 0; @@ -113,7 +117,8 @@ static int locate_peak_on_panel(double x, double y, double z, double k, } static signed int locate_peak(double x, double y, double z, double k, - struct detector *det, double *pfs, double *pss) + struct detgeom *det, + double *pfs, double *pss) { int i; @@ -121,7 +126,7 @@ static signed int locate_peak(double x, double y, double z, double k, for ( i=0; i<det->n_panels; i++ ) { - struct panel *p; + struct detgeom_panel *p; p = &det->panels[i]; @@ -379,29 +384,31 @@ static Reflection *check_reflection(struct image *image, Crystal *cryst, /* If we are updating a previous reflection, assume it stays * on the same panel and calculate the new position even if it's * fallen off the edge of the panel. */ - if ( (image->det != NULL) && (updateme != NULL) ) { + if ( (image->detgeom != NULL) && (updateme != NULL) ) { double fs, ss; + assert(get_panel_number(updateme) <= image->detgeom->n_panels); locate_peak_on_panel(xl, yl, zl, mean_kpred, - get_panel(updateme), &fs, &ss); + &image->detgeom->panels[get_panel_number(updateme)], + &fs, &ss); set_detector_pos(refl, fs, ss); } - /* Otherwise, calculate position if we have a detector structure, and + /* otherwise, calculate position if we have a detector structure, and * if we don't then just make do with partiality calculation */ - if ( (image->det != NULL) && (updateme == NULL) ) { + if ( (image->detgeom != NULL) && (updateme == NULL) ) { - double fs, ss; /* Position on detector */ - signed int p; /* Panel number */ + double fs, ss; /* position on detector */ + signed int p; /* panel number */ p = locate_peak(xl, yl, zl, mean_kpred, - image->det, &fs, &ss); + image->detgeom, &fs, &ss); if ( p == -1 ) { reflection_free(refl); return NULL; } set_detector_pos(refl, fs, ss); - set_panel(refl, &image->det->panels[p]); + set_panel_number(refl, p); } @@ -503,6 +510,7 @@ RefList *predict_to_res(Crystal *cryst, double max_res) double mres; signed int h, k, l; UnitCell *cell; + struct image *image; cell = crystal_get_cell(cryst); if ( cell == NULL ) return NULL; @@ -512,14 +520,15 @@ RefList *predict_to_res(Crystal *cryst, double max_res) /* Cell angle check from Foadi and Evans (2011) */ if ( !cell_is_sensible(cell) ) { ERROR("Invalid unit cell parameters given to" - " find_intersections()\n"); + " predict_to_res()\n"); cell_print(cell); return NULL; } cell_get_cartesian(cell, &ax, &ay, &az, &bx, &by, &bz, &cx, &cy, &cz); - mres = largest_q(crystal_get_image(cryst)); + image = crystal_get_image(cryst); + mres = detgeom_max_resolution(image->detgeom, image->lambda); if ( mres > max_res ) mres = max_res; hmax = mres * modulus(ax, ay, az); @@ -1076,7 +1085,8 @@ void polarisation_correction(RefList *list, UnitCell *cell, /* Returns dx_h/dP, where P = any parameter */ -double x_gradient(int param, Reflection *refl, UnitCell *cell, struct panel *p) +double x_gradient(int param, Reflection *refl, UnitCell *cell, + struct detgeom_panel *p) { signed int h, k, l; double xl, zl, kpred; @@ -1093,13 +1103,13 @@ double x_gradient(int param, Reflection *refl, UnitCell *cell, struct panel *p) switch ( param ) { case GPARAM_ASX : - return h * p->clen / (kpred + zl); + return h * p->cnz * p->pixel_pitch / (kpred + zl); case GPARAM_BSX : - return k * p->clen / (kpred + zl); + return k * p->cnz * p->pixel_pitch / (kpred + zl); case GPARAM_CSX : - return l * p->clen / (kpred + zl); + return l * p->cnz * p->pixel_pitch / (kpred + zl); case GPARAM_ASY : return 0.0; @@ -1111,13 +1121,13 @@ double x_gradient(int param, Reflection *refl, UnitCell *cell, struct panel *p) return 0.0; case GPARAM_ASZ : - return -h * xl * p->clen / (kpred*kpred + 2.0*kpred*zl + zl*zl); + return -h * xl * p->cnz * p->pixel_pitch / (kpred*kpred + 2.0*kpred*zl + zl*zl); case GPARAM_BSZ : - return -k * xl * p->clen / (kpred*kpred + 2.0*kpred*zl + zl*zl); + return -k * xl * p->cnz * p->pixel_pitch / (kpred*kpred + 2.0*kpred*zl + zl*zl); case GPARAM_CSZ : - return -l * xl * p->clen / (kpred*kpred + 2.0*kpred*zl + zl*zl); + return -l * xl * p->cnz * p->pixel_pitch / (kpred*kpred + 2.0*kpred*zl + zl*zl); case GPARAM_DETX : return -1; @@ -1136,7 +1146,8 @@ double x_gradient(int param, Reflection *refl, UnitCell *cell, struct panel *p) /* Returns dy_h/dP, where P = any parameter */ -double y_gradient(int param, Reflection *refl, UnitCell *cell, struct panel *p) +double y_gradient(int param, Reflection *refl, UnitCell *cell, + struct detgeom_panel *p) { signed int h, k, l; double yl, zl, kpred; @@ -1162,22 +1173,22 @@ double y_gradient(int param, Reflection *refl, UnitCell *cell, struct panel *p) return 0.0; case GPARAM_ASY : - return h * p->clen / (kpred + zl); + return h * p->cnz * p->pixel_pitch / (kpred + zl); case GPARAM_BSY : - return k * p->clen / (kpred + zl); + return k * p->cnz * p->pixel_pitch / (kpred + zl); case GPARAM_CSY : - return l * p->clen / (kpred + zl); + return l * p->cnz * p->pixel_pitch / (kpred + zl); case GPARAM_ASZ : - return -h * yl * p->clen / (kpred*kpred + 2.0*kpred*zl + zl*zl); + return -h * yl * p->cnz * p->pixel_pitch / (kpred*kpred + 2.0*kpred*zl + zl*zl); case GPARAM_BSZ : - return -k * yl * p->clen / (kpred*kpred + 2.0*kpred*zl + zl*zl); + return -k * yl * p->cnz * p->pixel_pitch / (kpred*kpred + 2.0*kpred*zl + zl*zl); case GPARAM_CSZ : - return -l * yl * p->clen / (kpred*kpred + 2.0*kpred*zl + zl*zl); + return -l * yl * p->cnz * p->pixel_pitch / (kpred*kpred + 2.0*kpred*zl + zl*zl); case GPARAM_DETX : return 0; diff --git a/libcrystfel/src/geometry.h b/libcrystfel/src/geometry.h index afaa9d6c..19c6a23a 100644 --- a/libcrystfel/src/geometry.h +++ b/libcrystfel/src/geometry.h @@ -3,12 +3,12 @@ * * Geometry of diffraction * - * Copyright © 2013-2020 Deutsches Elektronen-Synchrotron DESY, + * Copyright © 2013-2021 Deutsches Elektronen-Synchrotron DESY, * a research centre of the Helmholtz Association. * Copyright © 2012 Richard Kirian * * Authors: - * 2010-2016 Thomas White <taw@physics.org> + * 2010-2020 Thomas White <taw@physics.org> * 2012 Richard Kirian * * This file is part of CrystFEL. @@ -31,14 +31,10 @@ #ifndef GEOMETRY_H #define GEOMETRY_H - -#ifdef HAVE_CONFIG_H -#include <config.h> -#endif - #include "reflist.h" #include "cell.h" #include "crystal.h" +#include "detgeom.h" #ifdef __cplusplus extern "C" { @@ -122,9 +118,9 @@ extern double sphere_fraction(double rlow, double rhigh, double pr); extern double gaussian_fraction(double rlow, double rhigh, double pr); extern double x_gradient(int param, Reflection *refl, UnitCell *cell, - struct panel *p); + struct detgeom_panel *p); extern double y_gradient(int param, Reflection *refl, UnitCell *cell, - struct panel *p); + struct detgeom_panel *p); #ifdef __cplusplus } diff --git a/libcrystfel/src/hdf5-file.c b/libcrystfel/src/hdf5-file.c deleted file mode 100644 index e2738a8b..00000000 --- a/libcrystfel/src/hdf5-file.c +++ /dev/null @@ -1,2785 +0,0 @@ -/* - * hdf5-file.c - * - * Read/write HDF5 data files - * - * Copyright © 2012-2020 Deutsches Elektronen-Synchrotron DESY, - * a research centre of the Helmholtz Association. - * - * Authors: - * 2009-2016 Thomas White <taw@physics.org> - * 2014 Valerio Mariani - * - * 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 <stdint.h> -#include <hdf5.h> -#include <assert.h> -#include <unistd.h> - -#include "events.h" -#include "image.h" -#include "hdf5-file.h" -#include "utils.h" - -/** \file hdf5-file.h */ - -struct hdf5_write_location { - - const char *location; - int n_panels; - int *panel_idxs; - - int max_ss; - int max_fs; - -}; - - -int split_group_and_object(const char *path, char **group, char **object) -{ - const char *sep; - const char *store; - - sep = path; - store = sep; - sep = strpbrk(sep + 1, "/"); - if ( sep != NULL ) { - while ( 1 ) { - store = sep; - sep = strpbrk(sep + 1, "/"); - if ( sep == NULL ) { - break; - } - } - } - if ( store == path ) { - *group = NULL; - *object = strdup(path); - } else { - *group = strndup(path, store - path); - *object = strdup(store+1); - } - return 0; -}; - - -struct hdfile { - - const char *path; /* Current data path */ - - hid_t fh; /* HDF file handle */ - hid_t dh; /* Dataset handle */ - - int data_open; /* True if dh is initialised */ -}; - - -struct hdfile *hdfile_open(const char *filename) -{ - struct hdfile *f; - - f = malloc(sizeof(struct hdfile)); - if ( f == NULL ) return NULL; - - if ( access( filename, R_OK ) == -1 ) { - ERROR("File does not exist or cannot be read: %s\n", - filename); - free(f); - return NULL; - } - - f->fh = H5Fopen(filename, H5F_ACC_RDONLY, H5P_DEFAULT); - if ( f->fh < 0 ) { - ERROR("Couldn't open file: %s\n", filename); - free(f); - return NULL; - } - - f->data_open = 0; - return f; -} - - -int hdfile_set_image(struct hdfile *f, const char *path) -{ - f->dh = H5Dopen2(f->fh, path, H5P_DEFAULT); - if ( f->dh < 0 ) { - ERROR("Couldn't open dataset\n"); - return -1; - } - f->data_open = 1; - return 0; -} - - -static int read_peak_count(struct hdfile *f, char *path, int line, - int *num_peaks) -{ - - hid_t dh, sh, mh; - hsize_t size[1]; - hsize_t max_size[1]; - hsize_t offset[1], count[1]; - hsize_t m_offset[1], m_count[1], dimmh[1]; - - - int tw, r; - - dh = H5Dopen2(f->fh, path, H5P_DEFAULT); - if ( dh < 0 ) { - ERROR("Data block %s not found.\n", path); - return 1; - } - - sh = H5Dget_space(dh); - if ( sh < 0 ) { - H5Dclose(dh); - ERROR("Couldn't get dataspace for data.\n"); - return 1; - } - - if ( H5Sget_simple_extent_ndims(sh) != 1 ) { - ERROR("Data block %s has the wrong dimensionality (%i).\n", - path, H5Sget_simple_extent_ndims(sh)); - H5Sclose(sh); - H5Dclose(dh); - return 1; - } - - H5Sget_simple_extent_dims(sh, size, max_size); - - tw = size[0]; - - if ( line > tw-1 ) { - H5Sclose(sh); - H5Dclose(dh); - ERROR("Data block %s does not contain data for required event.\n", - path); - return 1; - } - - offset[0] = line; - count[0] = 1; - - r = H5Sselect_hyperslab(sh, H5S_SELECT_SET, - offset, NULL, count, NULL); - if ( r < 0 ) { - ERROR("Error selecting file dataspace " - "for data block %s\n", path); - H5Dclose(dh); - H5Sclose(sh); - return 1; - } - - m_offset[0] = 0; - m_count[0] = 1; - dimmh[0] = 1; - mh = H5Screate_simple(1, dimmh, NULL); - r = H5Sselect_hyperslab(mh, H5S_SELECT_SET, - m_offset, NULL, m_count, NULL); - if ( r < 0 ) { - ERROR("Error selecting memory dataspace " - "for data block %s\n", path); - H5Dclose(dh); - H5Sclose(sh); - H5Sclose(mh); - return 1; - } - - r = H5Dread(dh, H5T_NATIVE_INT, mh, - sh, H5P_DEFAULT, num_peaks); - if ( r < 0 ) { - ERROR("Couldn't read data for block %s, line %i\n", path, line); - H5Dclose(dh); - H5Sclose(sh); - H5Sclose(mh); - return 1; - } - - H5Dclose(dh); - H5Sclose(sh); - H5Sclose(mh); - return 0; -} - - - -static float *read_hdf5_data(struct hdfile *f, char *path, int line) -{ - - hid_t dh, sh, mh; - hsize_t size[2]; - hsize_t max_size[2]; - hsize_t offset[2], count[2]; - hsize_t m_offset[2], m_count[2], dimmh[2]; - float *buf; - int tw, r; - - dh = H5Dopen2(f->fh, path, H5P_DEFAULT); - if ( dh < 0 ) { - ERROR("Data block (%s) not found.\n", path); - return NULL; - } - - sh = H5Dget_space(dh); - if ( sh < 0 ) { - H5Dclose(dh); - ERROR("Couldn't get dataspace for data.\n"); - return NULL; - } - - if ( H5Sget_simple_extent_ndims(sh) != 2 ) { - ERROR("Data block %s has the wrong dimensionality (%i).\n", - path, H5Sget_simple_extent_ndims(sh)); - H5Sclose(sh); - H5Dclose(dh); - return NULL; - } - - H5Sget_simple_extent_dims(sh, size, max_size); - - tw = size[0]; - if ( line> tw-1 ) { - H5Sclose(sh); - H5Dclose(dh); - ERROR("Data block %s does not contain data for required event.\n", - path); - return NULL; - } - - offset[0] = line; - offset[1] = 0; - count[0] = 1; - count[1] = size[1]; - - r = H5Sselect_hyperslab(sh, H5S_SELECT_SET, offset, NULL, count, NULL); - if ( r < 0 ) { - ERROR("Error selecting file dataspace " - "for data block %s\n", path); - H5Dclose(dh); - H5Sclose(sh); - return NULL; - } - - m_offset[0] = 0; - m_offset[1] = 0; - m_count[0] = 1; - m_count[1] = size[1]; - dimmh[0] = 1; - dimmh[1] = size[1]; - - mh = H5Screate_simple(2, dimmh, NULL); - r = H5Sselect_hyperslab(mh, H5S_SELECT_SET, - m_offset, NULL, m_count, NULL); - if ( r < 0 ) { - ERROR("Error selecting memory dataspace " - "for data block %s\n", path); - H5Dclose(dh); - H5Sclose(sh); - H5Sclose(mh); - return NULL; - } - - buf = malloc(size[1]*sizeof(float)); - if ( buf == NULL ) return NULL; - r = H5Dread(dh, H5T_NATIVE_FLOAT, mh, sh, H5P_DEFAULT, buf); - if ( r < 0 ) { - ERROR("Couldn't read data for block %s, line %i\n", path, line); - H5Dclose(dh); - H5Sclose(sh); - H5Sclose(mh); - return NULL; - } - - H5Dclose(dh); - H5Sclose(sh); - H5Sclose(mh); - return buf; -} - - -/** - * \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 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 - * \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 - * detector panel, in pixel units, consistent with its description of detector - * 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 \p half_pixel_shift is non-zero. - * - * \returns Non-zero on error, zero otherwise. - * - */ -int get_peaks_cxi_2(struct image *image, struct hdfile *f, const char *p, - struct filename_plus_event *fpe, int half_pixel_shift) -{ - char path_n[1024]; - char path_x[1024]; - char path_y[1024]; - char path_i[1024]; - int r; - int pk; - - int line = 0; - int num_peaks; - - float *buf_x; - float *buf_y; - float *buf_i; - - double peak_offset = half_pixel_shift ? 0.5 : 0.0; - - if ( (fpe != NULL) && (fpe->ev != NULL) - && (fpe->ev->dim_entries != NULL) ) - { - line = fpe->ev->dim_entries[0]; - } else { - ERROR("CXI format peak list format selected," - "but file has no event structure"); - return 1; - } - - snprintf(path_n, 1024, "%s/nPeaks", p); - snprintf(path_x, 1024, "%s/peakXPosRaw", p); - snprintf(path_y, 1024, "%s/peakYPosRaw", p); - snprintf(path_i, 1024, "%s/peakTotalIntensity", p); - - r = read_peak_count(f, path_n, line, &num_peaks); - if ( r != 0 ) return 1; - - buf_x = read_hdf5_data(f, path_x, line); - if ( r != 0 ) return 1; - - buf_y = read_hdf5_data(f, path_y, line); - if ( r != 0 ) return 1; - - buf_i = read_hdf5_data(f, path_i, line); - if ( r != 0 ) return 1; - - if ( image->features != NULL ) { - image_feature_list_free(image->features); - } - image->features = image_feature_list_new(); - - for ( pk=0; pk<num_peaks; pk++ ) { - - float fs, ss, val; - struct panel *p; - - fs = buf_x[pk] + peak_offset; - ss = buf_y[pk] + peak_offset; - val = buf_i[pk]; - - p = find_orig_panel(image->det, fs, ss); - if ( p == NULL ) continue; - if ( p->no_index ) continue; - - /* Convert coordinates to panel-relative */ - fs = fs - p->orig_min_fs; - ss = ss - p->orig_min_ss; - - image_add_feature(image->features, fs, ss, p, image, val, NULL); - - } - - return 0; -} - - -/** - * \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 \ref get_peaks_cxi_2 instead. - * - * 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. - * - */ -int get_peaks_cxi(struct image *image, struct hdfile *f, const char *p, - struct filename_plus_event *fpe) -{ - return get_peaks_cxi_2(image, f, p, fpe, 1); -} - - -/** - * \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 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 - * intensities. - * - * CrystFEL considers all peak locations to be distances from the corner of the - * detector panel, in pixel units, consistent with its description of detector - * 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 \p half_pixel_shift is non-zero. - * - * \returns Non-zero on error, zero otherwise. - * - */ -int get_peaks_2(struct image *image, struct hdfile *f, const char *p, - int half_pixel_shift) -{ - hid_t dh, sh; - hsize_t size[2]; - hsize_t max_size[2]; - int i; - float *buf; - herr_t r; - int tw; - char *np; - double peak_offset = half_pixel_shift ? 0.5 : 0.0; - - if ( image->event != NULL ) { - np = retrieve_full_path(image->event, p); - } else { - np = strdup(p); - } - - dh = H5Dopen2(f->fh, np, H5P_DEFAULT); - if ( dh < 0 ) { - ERROR("Peak list (%s) not found.\n", np); - return 1; - } - - sh = H5Dget_space(dh); - if ( sh < 0 ) { - H5Dclose(dh); - ERROR("Couldn't get dataspace for peak list.\n"); - free(np); - return 1; - } - - if ( H5Sget_simple_extent_ndims(sh) != 2 ) { - ERROR("Peak list has the wrong dimensionality (%i).\n", - H5Sget_simple_extent_ndims(sh)); - H5Sclose(sh); - H5Dclose(dh); - free(np); - return 1; - } - - H5Sget_simple_extent_dims(sh, size, max_size); - - tw = size[1]; - if ( (tw != 3) && (tw != 4) ) { - H5Sclose(sh); - H5Dclose(dh); - ERROR("Peak list has the wrong dimensions.\n"); - free(np); - return 1; - } - - buf = malloc(sizeof(float)*size[0]*size[1]); - if ( buf == NULL ) { - H5Sclose(sh); - H5Dclose(dh); - ERROR("Couldn't reserve memory for the peak list.\n"); - free(np); - return 1; - } - r = H5Dread(dh, H5T_NATIVE_FLOAT, H5S_ALL, H5S_ALL, - H5P_DEFAULT, buf); - if ( r < 0 ) { - ERROR("Couldn't read peak list.\n"); - free(buf); - free(np); - return 1; - } - - if ( image->features != NULL ) { - image_feature_list_free(image->features); - } - image->features = image_feature_list_new(); - - for ( i=0; i<size[0]; i++ ) { - - float fs, ss, val; - struct panel *p; - - fs = buf[tw*i+0] + peak_offset; - ss = buf[tw*i+1] + peak_offset; - val = buf[tw*i+2]; - - p = find_orig_panel(image->det, fs, ss); - if ( p == NULL ) continue; - if ( p->no_index ) continue; - - /* Convert coordinates to panel-relative */ - fs = fs - p->orig_min_fs; - ss = ss - p->orig_min_ss; - - image_add_feature(image->features, fs, ss, p, image, val, - NULL); - - } - - free(buf); - free(np); - H5Sclose(sh); - H5Dclose(dh); - - return 0; -} - - -/** - * \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 \ref get_peaks_2 instead. - * - * This function is equivalent to \ref get_peaks_2(\p image, \p f, \p p, 1). - * - * \returns Non-zero on error, zero otherwise. - * - */ -int get_peaks(struct image *image, struct hdfile *f, const char *p) -{ - return get_peaks_2(image, f, p, 1); -} - - -static void cleanup(hid_t fh) -{ - int n_ids, i; - hid_t ids[2048]; - - n_ids = H5Fget_obj_ids(fh, H5F_OBJ_ALL, 2048, ids); - - for ( i=0; i<n_ids; i++ ) { - - hid_t id; - H5I_type_t type; - - id = ids[i]; - - type = H5Iget_type(id); - - if ( type == H5I_GROUP ) H5Gclose(id); - if ( type == H5I_DATASET ) H5Dclose(id); - if ( type == H5I_DATATYPE ) H5Tclose(id); - if ( type == H5I_DATASPACE ) H5Sclose(id); - if ( type == H5I_ATTR ) H5Aclose(id); - - } - -} - - -void hdfile_close(struct hdfile *f) -{ - - if ( f->data_open ) { - H5Dclose(f->dh); - } - - cleanup(f->fh); - - H5Fclose(f->fh); - - free(f); -} - - -/* Deprecated */ -int hdf5_write(const char *filename, const void *data, - int width, int height, int type) -{ - hid_t fh, gh, sh, dh; /* File, group, dataspace and data handles */ - herr_t r; - hsize_t size[2]; - - fh = H5Fcreate(filename, H5F_ACC_TRUNC, H5P_DEFAULT, H5P_DEFAULT); - if ( fh < 0 ) { - ERROR("Couldn't create file: %s\n", filename); - return 1; - } - - gh = H5Gcreate2(fh, "data", H5P_DEFAULT, H5P_DEFAULT, H5P_DEFAULT); - if ( gh < 0 ) { - ERROR("Couldn't create group\n"); - H5Fclose(fh); - return 1; - } - - /* Note the "swap" here, according to section 3.2.5, - * "C versus Fortran Dataspaces", of the HDF5 user's guide. */ - size[0] = height; - size[1] = width; - sh = H5Screate_simple(2, size, NULL); - - dh = H5Dcreate2(gh, "data", type, sh, - H5P_DEFAULT, H5P_DEFAULT, H5P_DEFAULT); - if ( dh < 0 ) { - ERROR("Couldn't create dataset\n"); - H5Fclose(fh); - return 1; - } - - /* Muppet check */ - H5Sget_simple_extent_dims(sh, size, NULL); - - r = H5Dwrite(dh, type, H5S_ALL, - H5S_ALL, H5P_DEFAULT, data); - if ( r < 0 ) { - ERROR("Couldn't write data\n"); - H5Dclose(dh); - H5Fclose(fh); - return 1; - } - H5Dclose(dh); - H5Gclose(gh); - H5Fclose(fh); - - return 0; -} - - -static void add_panel_to_location(struct hdf5_write_location *loc, - struct panel *p, int pi) -{ - int *new_panel_idxs; - - new_panel_idxs = realloc(loc->panel_idxs, - (loc->n_panels+1)*sizeof(int)); - if ( new_panel_idxs == NULL ) { - ERROR("Error while managing write location list.\n"); - return; - } - loc->panel_idxs = new_panel_idxs; - loc->panel_idxs[loc->n_panels] = pi; - loc->n_panels += 1; - if ( p->orig_max_fs > loc->max_fs ) { - loc->max_fs = p->orig_max_fs; - } - if ( p->orig_max_ss > loc->max_ss ) { - loc->max_ss = p->orig_max_ss; - } -} - - -static void add_panel_location(struct panel *p, const char *p_location, int pi, - struct hdf5_write_location **plocations, - int *pnum_locations) -{ - int li; - int num_locations = *pnum_locations; - struct hdf5_write_location *locations = *plocations; - int done = 0; - - /* Does this HDF5 path already exist in the location list? - * If so, add the new panel to it (with a unique index, we hope) */ - for ( li=0; li<num_locations; li++ ) { - if ( strcmp(p_location, locations[li].location) == 0 ) { - add_panel_to_location(&locations[li], p, pi); - done = 1; - } - } - - /* If not, add a new location to ths list */ - if ( !done ) { - - struct hdf5_write_location *new_locations; - size_t nsz; - - nsz = (num_locations+1)*sizeof(struct hdf5_write_location); - new_locations = realloc(locations, nsz); - if ( new_locations == NULL ) { - ERROR("Failed to grow location list.\n"); - return; - } - locations = new_locations; - - locations[num_locations].max_ss = p->orig_max_ss; - locations[num_locations].max_fs = p->orig_max_fs; - locations[num_locations].location = p_location; - locations[num_locations].panel_idxs = malloc(sizeof(int)); - if ( locations[num_locations].panel_idxs == NULL ) { - ERROR("Failed to allocate single idx (!)\n"); - return; - } - locations[num_locations].panel_idxs[0] = pi; - locations[num_locations].n_panels = 1; - - num_locations += 1; - - } - - *plocations = locations; - *pnum_locations = num_locations; -} - - -static struct hdf5_write_location *make_location_list(struct detector *det, - const char *def_location, - int *pnum_locations) -{ - int pi; - struct hdf5_write_location *locations = NULL; - int num_locations = 0; - - for ( pi=0; pi<det->n_panels; pi++ ) { - - struct panel *p; - const char *p_location; - - p = &det->panels[pi]; - - if ( p->data == NULL ) { - p_location = def_location; - } else { - p_location = p->data; - } - - add_panel_location(p, p_location, pi, - &locations, &num_locations); - - } - - *pnum_locations = num_locations; - return locations; -} - - -static void write_location(hid_t fh, struct detector *det, float **dp, - struct hdf5_write_location *loc) -{ - hid_t sh, dh, ph; - hid_t dh_dataspace; - hsize_t size[2]; - int pi; - - /* Note the "swap" here, according to section 3.2.5, - * "C versus Fortran Dataspaces", of the HDF5 user's guide. */ - size[0] = loc->max_ss+1; - size[1] = loc->max_fs+1; - sh = H5Screate_simple(2, size, NULL); - - ph = H5Pcreate(H5P_LINK_CREATE); - H5Pset_create_intermediate_group(ph, 1); - - dh = H5Dcreate2(fh, loc->location, H5T_NATIVE_FLOAT, sh, - ph, H5P_DEFAULT, H5P_DEFAULT); - if ( dh < 0 ) { - ERROR("Couldn't create dataset\n"); - H5Fclose(fh); - return; - } - - H5Sget_simple_extent_dims(sh, size, NULL); - - for ( pi=0; pi<loc->n_panels; pi++ ) { - - hsize_t f_offset[2], f_count[2], dims[2]; - hid_t memspace; - struct panel p; - int r; - - p = det->panels[loc->panel_idxs[pi]]; - - f_offset[0] = p.orig_min_ss; - f_offset[1] = p.orig_min_fs; - f_count[0] = p.orig_max_ss - p.orig_min_ss +1; - f_count[1] = p.orig_max_fs - p.orig_min_fs +1; - - dh_dataspace = H5Dget_space(dh); - r = H5Sselect_hyperslab(dh_dataspace, H5S_SELECT_SET, - f_offset, NULL, f_count, NULL); - if ( r < 0 ) { - ERROR("Error selecting file dataspace " - "for panel %s\n", p.name); - H5Pclose(ph); - H5Dclose(dh); - H5Sclose(dh_dataspace); - H5Sclose(sh); - H5Fclose(fh); - return; - } - - dims[0] = p.h; - dims[1] = p.w; - memspace = H5Screate_simple(2, dims, NULL); - - r = H5Dwrite(dh, H5T_NATIVE_FLOAT, memspace, dh_dataspace, - H5P_DEFAULT, dp[loc->panel_idxs[pi]]); - if ( r < 0 ) { - ERROR("Couldn't write data\n"); - H5Pclose(ph); - H5Dclose(dh); - H5Sclose(dh_dataspace); - H5Sclose(memspace); - H5Sclose(sh); - H5Fclose(fh); - return; - } - - H5Sclose(dh_dataspace); - H5Sclose(memspace); - } - H5Pclose(ph); - H5Sclose(sh); - H5Dclose(dh); -} - - -static void write_photon_energy(hid_t fh, double eV, const char *ph_en_loc) -{ - hid_t ph, sh, dh; - hsize_t size1d[1]; - int r; - - ph = H5Pcreate(H5P_LINK_CREATE); - H5Pset_create_intermediate_group(ph, 1); - - size1d[0] = 1; - sh = H5Screate_simple(1, size1d, NULL); - - dh = H5Dcreate2(fh, ph_en_loc, H5T_NATIVE_DOUBLE, sh, - ph, H5S_ALL, H5P_DEFAULT); - if ( dh < 0 ) { - ERROR("Couldn't create dataset for photon energy.\n"); - return; - } - r = H5Dwrite(dh, H5T_NATIVE_DOUBLE, H5S_ALL, H5S_ALL, H5P_DEFAULT, &eV); - if ( r < 0 ) { - ERROR("Couldn't write photon energy.\n"); - /* carry on */ - } - - H5Pclose(ph); - H5Dclose(dh); -} - - -static void write_spectrum(hid_t fh, Spectrum *s) -{ - herr_t r; - double *arr; - int i; - 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(n*sizeof(double)); - if ( arr == NULL ) { - ERROR("Failed to allocate memory for spectrum.\n"); - return; - } - - /* 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); - } - - sh = H5Screate_simple(1, &n, NULL); - - dh = H5Dcreate2(fh, "/spectrum/wavelengths_A", H5T_NATIVE_DOUBLE, - sh, ph, H5S_ALL, H5P_DEFAULT); - if ( dh < 0 ) { - ERROR("Failed to create dataset for spectrum wavelengths.\n"); - return; - } - r = H5Dwrite(dh, H5T_NATIVE_DOUBLE, H5S_ALL, - H5S_ALL, H5P_DEFAULT, arr); - if ( r < 0 ) { - ERROR("Failed to write spectrum wavelengths.\n"); - return; - } - H5Dclose(dh); - - /* 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/pdf", H5T_NATIVE_DOUBLE, sh, - H5P_DEFAULT, H5S_ALL, H5P_DEFAULT); - if ( dh < 0 ) { - 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 p.d.f.\n"); - return; - } - - H5Dclose(dh); - H5Pclose(ph); - free(arr); -} - - -int hdf5_write_image(const char *filename, const struct image *image, - char *element) -{ - hid_t fh; - int li; - char *default_location; - struct hdf5_write_location *locations; - int num_locations; - const char *ph_en_loc; - - if ( image->det == NULL ) { - ERROR("Geometry not available\n"); - return 1; - } - - fh = H5Fcreate(filename, H5F_ACC_TRUNC, H5P_DEFAULT, H5P_DEFAULT); - if ( fh < 0 ) { - ERROR("Couldn't create file: %s\n", filename); - return 1; - } - - if ( element != NULL ) { - default_location = strdup(element); - } else { - default_location = strdup("/data/data"); - } - - locations = make_location_list(image->det, default_location, - &num_locations); - - for ( li=0; li<num_locations; li++ ) { - write_location(fh, image->det, image->dp, &locations[li]); - } - - if ( image->beam == NULL - || (image->beam != NULL && image->beam->photon_energy_from == NULL) ) { - ph_en_loc = "photon_energy_eV"; - } else { - ph_en_loc = image->beam->photon_energy_from; - } - - write_photon_energy(fh, ph_lambda_to_eV(image->lambda), ph_en_loc); - - if ( image->spectrum != NULL ) { - write_spectrum(fh, image->spectrum); - } - - H5Fclose(fh); - free(default_location); - for ( li=0; li<num_locations; li ++ ) { - free(locations[li].panel_idxs); - } - free(locations); - return 0; -} - - -static void debodge_saturation(struct hdfile *f, struct image *image) -{ - hid_t dh, sh; - hsize_t size[2]; - hsize_t max_size[2]; - int i; - float *buf; - herr_t r; - - dh = H5Dopen2(f->fh, "/processing/hitfinder/peakinfo_saturated", - H5P_DEFAULT); - - if ( dh < 0 ) { - /* This isn't an error */ - return; - } - - sh = H5Dget_space(dh); - if ( sh < 0 ) { - H5Dclose(dh); - ERROR("Couldn't get dataspace for saturation table.\n"); - return; - } - - if ( H5Sget_simple_extent_ndims(sh) != 2 ) { - H5Sclose(sh); - H5Dclose(dh); - return; - } - - H5Sget_simple_extent_dims(sh, size, max_size); - - if ( size[1] != 3 ) { - H5Sclose(sh); - H5Dclose(dh); - ERROR("Saturation table has the wrong dimensions.\n"); - return; - } - - buf = malloc(sizeof(float)*size[0]*size[1]); - if ( buf == NULL ) { - H5Sclose(sh); - H5Dclose(dh); - ERROR("Couldn't reserve memory for saturation table.\n"); - return; - } - r = H5Dread(dh, H5T_NATIVE_FLOAT, H5S_ALL, H5S_ALL, H5P_DEFAULT, buf); - if ( r < 0 ) { - ERROR("Couldn't read saturation table.\n"); - free(buf); - return; - } - - for ( i=0; i<size[0]; i++ ) { - - unsigned int fs, ss; - float val; - struct panel *p; - signed int pn; - - fs = buf[3*i+0]; - ss = buf[3*i+1]; - val = buf[3*i+2]; - - /* Turn "original" position into "panel" position */ - pn = find_orig_panel_number(image->det, fs, ss); - if ( pn == -1 ) { - ERROR("Failed to find panel!\n"); - continue; - } - p = &image->det->panels[pn]; - - image->dp[pn][fs+p->w*ss] = val/5.0; - image->dp[pn][fs+1+p->w*ss] = val/5.0; - image->dp[pn][fs-1+p->w*ss] = val/5.0; - image->dp[pn][fs+p->w*(ss-1)] = val/5.0; - image->dp[pn][fs+p->w*(ss+1)] = val/5.0; - - } - - free(buf); - H5Sclose(sh); - H5Dclose(dh); -} - - -static int *make_badmask(int *flags, struct detector *det, float *data, - struct panel *p) -{ - int *badmap; - int fs, ss; - - badmap = malloc(p->w*p->h*sizeof(int)); - if ( badmap == NULL ) { - ERROR("Failed to allocate bad mask for panel %s\n", - p->name); - return NULL; - } - - /* Defaults, then bad pixels arising from bad regions or panels */ - for ( ss=0; ss<p->h; ss++ ) { - for ( fs=0; fs<p->w; fs++ ) { - - int bad = 0; - - if ( p->no_index ) bad = 1; - - if ( in_bad_region(det, p, fs, ss) ) { - bad = 1; - } - - badmap[fs+p->w*ss] = bad; - } - } - - /* Bad pixels from mask */ - if ( flags != NULL ) { - for ( ss=0; ss<p->h; ss++ ) { - for ( fs=0; fs<p->w; fs++ ) { - - 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; - - /* 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; - - } - } - } - - return badmap; -} - - -int hdfile_get_value(struct hdfile *f, const char *name, struct event *ev, - void *val, hid_t memtype) -{ - hid_t dh; - hid_t type; - hid_t class; - hid_t sh; - hid_t ms; - hsize_t *f_offset = NULL; - hsize_t *f_count = NULL; - hsize_t m_offset[1]; - hsize_t m_count[1]; - hsize_t msdims[1]; - hsize_t size[64]; - herr_t r; - herr_t check; - int check_pe; - int dim_flag; - int ndims; - int i; - char *subst_name = NULL; - - if ( (ev != NULL) && (ev->path_length != 0) ) { - subst_name = retrieve_full_path(ev, name); - } else { - subst_name = strdup(name); - } - - check_pe = check_path_existence(f->fh, subst_name); - if ( check_pe == 0 ) { - ERROR("No such event-based float field '%s'\n", subst_name); - return 1; - } - - dh = H5Dopen2(f->fh, subst_name, H5P_DEFAULT); - type = H5Dget_type(dh); - class = H5Tget_class(type); - - if ( (class != H5T_FLOAT) && (class != H5T_INTEGER) ) { - ERROR("Not a floating point or integer value.\n"); - H5Tclose(type); - H5Dclose(dh); - return 1; - } - - /* Get the dimensionality. We have to cope with scalars expressed as - * arrays with all dimensions 1, as well as zero-d arrays. */ - sh = H5Dget_space(dh); - ndims = H5Sget_simple_extent_ndims(sh); - if ( ndims > 64 ) { - ERROR("Too many dimensions for hdfile_get_value\n"); - H5Tclose(type); - H5Dclose(dh); - return 1; - } - H5Sget_simple_extent_dims(sh, size, NULL); - - m_offset[0] = 0; - m_count[0] = 1; - msdims[0] = 1; - ms = H5Screate_simple(1,msdims,NULL); - - /* Check that the size in all dimensions is 1 - * or that one of the dimensions has the same - * size as the hyperplane events */ - - dim_flag = 0; - - for ( i=0; i<ndims; i++ ) { - if ( size[i] == 1 ) continue; - if ( ( i==0 ) && (ev != NULL) && (ev->dim_length > 0) - && (size[i] > ev->dim_entries[0]) ) - { - dim_flag = 1; - } else { - H5Tclose(type); - H5Dclose(dh); - return 1; - } - } - - if ( dim_flag == 0 ) { - - if ( H5Dread(dh, memtype, H5S_ALL, H5S_ALL, H5P_DEFAULT, val) < 0 ) { - ERROR("Couldn't read value.\n"); - H5Tclose(type); - H5Dclose(dh); - return 1; - } - - } else { - - f_offset = malloc(ndims*sizeof(hsize_t)); - f_count = malloc(ndims*sizeof(hsize_t)); - - for ( i=0; i<ndims; i++ ) { - - if ( i == 0 ) { - f_offset[i] = ev->dim_entries[0]; - f_count[i] = 1; - } else { - f_offset[i] = 0; - f_count[i] = 0; - } - - } - - check = H5Sselect_hyperslab(sh, H5S_SELECT_SET, - f_offset, NULL, f_count, NULL); - if ( check <0 ) { - ERROR("Error selecting dataspace for float value"); - free(f_offset); - free(f_count); - return 1; - } - - ms = H5Screate_simple(1,msdims,NULL); - check = H5Sselect_hyperslab(ms, H5S_SELECT_SET, - m_offset, NULL, m_count, NULL); - if ( check < 0 ) { - ERROR("Error selecting memory dataspace for float value"); - free(f_offset); - free(f_count); - return 1; - } - - r = H5Dread(dh, memtype, ms, sh, H5P_DEFAULT, val); - if ( r < 0 ) { - ERROR("Couldn't read value.\n"); - H5Tclose(type); - H5Dclose(dh); - return 1; - } - - } - - free(subst_name); - - return 0; -} - - -static void hdfile_fill_in_beam_parameters(struct beam_params *beam, - struct hdfile *f, - struct event *ev, - struct image *image) -{ - double eV; - - if ( beam->photon_energy_from == NULL ) { - - /* Explicit value given */ - eV = beam->photon_energy; - - } else { - - int r; - - r = hdfile_get_value(f, beam->photon_energy_from, - ev, &eV, H5T_NATIVE_DOUBLE); - if ( r ) { - ERROR("Failed to read '%s'\n", - beam->photon_energy_from); - } - - } - - image->lambda = ph_en_to_lambda(eV_to_J(eV))*beam->photon_energy_scale; -} - - -static void hdfile_fill_in_clen(struct detector *det, struct hdfile *f, - struct event *ev) -{ - int i; - - for ( i=0; i<det->n_panels; i++ ) { - - struct panel *p = &det->panels[i]; - - if ( p->clen_from != NULL ) { - - double val; - int r; - - r = hdfile_get_value(f, p->clen_from, ev, &val, - H5T_NATIVE_DOUBLE); - if ( r ) { - ERROR("Failed to read '%s'\n", p->clen_from); - } else { - p->clen = val * 1.0e-3; - } - - } - - adjust_centering_for_rail(p); - - } -} - - -int hdf5_read(struct hdfile *f, struct image *image, const char *element, - int satcorr) -{ - herr_t r; - float *buf; - int fail; - hsize_t *size; - hsize_t *max_size; - hid_t sh; - int sh_dim; - int w, h; - - if ( element == NULL ) { - fail = hdfile_set_first_image(f, "/"); - } else { - fail = hdfile_set_image(f, element); - } - - if ( fail ) { - ERROR("Couldn't select path\n"); - return 1; - } - - sh = H5Dget_space(f->dh); - sh_dim = H5Sget_simple_extent_ndims(sh); - - if ( sh_dim != 2 ) { - ERROR("Dataset is not two-dimensional\n"); - return -1; - } - - size = malloc(sh_dim*sizeof(hsize_t)); - max_size = malloc(sh_dim*sizeof(hsize_t)); - H5Sget_simple_extent_dims(sh, size, max_size); - H5Sclose(sh); - w = size[1]; - h = size[0]; - free(size); - free(max_size); - - buf = malloc(sizeof(float)*w*h); - r = H5Dread(f->dh, H5T_NATIVE_FLOAT, H5S_ALL, H5S_ALL, - H5P_DEFAULT, buf); - if ( r < 0 ) { - ERROR("Couldn't read data\n"); - free(buf); - return 1; - } - - if ( image->det != NULL ) { - ERROR("WARNING: hdf5_read() called with geometry structure.\n"); - } - image->det = simple_geometry(image, w, h); - image->dp = malloc(sizeof(double *)); - if ( image->dp == NULL ) { - ERROR("Failed to allocate memory for image data!\n"); - return 1; - } - image->dp[0] = buf; - - if ( satcorr ) debodge_saturation(f, image); - - if ( image->beam != NULL ) { - - hdfile_fill_in_beam_parameters(image->beam, f, NULL, image); - - if ( image->lambda > 1000 ) { - /* Error message covers a silly value in the beam file - * or in the HDF5 file. */ - ERROR("WARNING: Missing or nonsensical wavelength " - "(%e m) for %s.\n", - image->lambda, image->filename); - } - - } - - fill_in_adu(image); - - return 0; -} - - -static hsize_t *first_two_dims(hsize_t *in, struct dim_structure *ds) -{ - int i, j; - hsize_t *out = malloc(2*sizeof(hsize_t)); - - if ( out == NULL ) return NULL; - - j = 0; - for ( i=0; i<ds->num_dims; i++ ) { - if ( (ds->dims[i] == HYSL_FS) || (ds->dims[i] == HYSL_SS) ) { - out[j++] = in[i]; - } - } - return out; -} - - -static int load_satmap(struct hdfile *f, struct event *ev, struct panel *p, - hsize_t *in_f_offset, hsize_t *in_f_count, - struct dim_structure *dim_struct, - float *satmap) -{ - char *loc; /* Sat map location after possible substitution */ - hid_t satmap_dataspace, satmap_dh; - int exists; - int check, r; - hid_t memspace; - hsize_t dimsm[2]; - hid_t fh; - hsize_t *f_offset, *f_count; - - if ( p->satmap_file != NULL ) { - - fh = H5Fopen(p->satmap_file, H5F_ACC_RDONLY, H5P_DEFAULT); - if ( fh < 0 ) { - ERROR("Couldn't open satmap file '%s'\n", p->satmap_file); - return 1; - } - - /* If we have an external map file, we assume it to be a simple - * 2D job */ - f_offset = first_two_dims(in_f_offset, dim_struct); - f_count = first_two_dims(in_f_count, dim_struct); - - } else { - - /* Otherwise, we assume it has the same dimensions as the - * image data itself */ - fh = f->fh; - f_offset = in_f_offset; - f_count = in_f_count; - } - - if ( ev != NULL ) { - loc = retrieve_full_path(ev, p->satmap); - } else { - loc = strdup(p->satmap); - } - - exists = check_path_existence(fh, loc); - if ( !exists ) { - ERROR("Cannot find satmap for panel %s\n", p->name); - goto err; - } - - satmap_dh = H5Dopen2(fh, loc, H5P_DEFAULT); - if ( satmap_dh <= 0 ) { - ERROR("Couldn't open satmap for panel %s\n", p->name); - goto err; - } - - satmap_dataspace = H5Dget_space(satmap_dh); - check = H5Sselect_hyperslab(satmap_dataspace, H5S_SELECT_SET, - f_offset, NULL, f_count, NULL); - if ( check < 0 ) { - ERROR("Error selecting satmap dataspace for panel %s\n", - p->name); - goto err; - } - - dimsm[0] = p->h; - dimsm[1] = p->w; - memspace = H5Screate_simple(2, dimsm, NULL); - if ( check < 0 ) { - ERROR("Error selecting satmap memory dataspace for panel %s\n", - p->name); - goto err; - } - - r = H5Dread(satmap_dh, H5T_NATIVE_FLOAT, memspace, - satmap_dataspace, H5P_DEFAULT, satmap); - if ( r < 0 ) { - ERROR("Couldn't read satmap for panel %s\n", p->name); - goto err; - } - - H5Sclose(satmap_dataspace); - H5Dclose(satmap_dh); - free(loc); - - return 0; - -err: - if ( p->satmap_file != NULL ) H5Fclose(fh); - free(loc); - return 1; -} - - - -static int load_mask(struct hdfile *f, struct event *ev, struct panel *p, - int *flags, - hsize_t *in_f_offset, hsize_t *in_f_count, - struct dim_structure *dim_struct) -{ - char *mask; /* Mask location after possible substitution */ - hid_t mask_dataspace, mask_dh; - int exists; - int check, r; - hid_t memspace; - hsize_t dimsm[2]; - hid_t fh; - hsize_t *f_offset, *f_count; - - if ( p->mask_file != NULL ) { - - fh = H5Fopen(p->mask_file, H5F_ACC_RDONLY, H5P_DEFAULT); - if ( fh < 0 ) { - ERROR("Couldn't open mask file '%s'\n", p->mask_file); - return 1; - } - - /* If we have an external map file, we assume it to be a simple - * 2D job */ - f_offset = first_two_dims(in_f_offset, dim_struct); - f_count = first_two_dims(in_f_count, dim_struct); - - } else { - fh = f->fh; - f_offset = in_f_offset; - f_count = in_f_count; - } - - if ( ev != NULL ) { - mask = retrieve_full_path(ev, p->mask); - } else { - mask = strdup(p->mask); - } - - exists = check_path_existence(fh, mask); - if ( !exists ) { - ERROR("Cannot find flags for panel %s\n", p->name); - goto err; - } - - mask_dh = H5Dopen2(fh, mask, H5P_DEFAULT); - if ( mask_dh <= 0 ) { - ERROR("Couldn't open flags for panel %s\n", p->name); - goto err; - } - - mask_dataspace = H5Dget_space(mask_dh); - check = H5Sselect_hyperslab(mask_dataspace, H5S_SELECT_SET, - f_offset, NULL, f_count, NULL); - if ( check < 0 ) { - ERROR("Error selecting mask dataspace for panel %s\n", p->name); - goto err; - } - - dimsm[0] = p->h; - dimsm[1] = p->w; - memspace = H5Screate_simple(2, dimsm, NULL); - if ( check < 0 ) { - ERROR("Error selecting memory dataspace for panel %s\n", p->name); - goto err; - } - - r = H5Dread(mask_dh, H5T_NATIVE_INT, memspace, - mask_dataspace, H5P_DEFAULT, flags); - if ( r < 0 ) { - ERROR("Couldn't read flags for panel %s\n", p->name); - goto err; - } - - H5Sclose(mask_dataspace); - H5Dclose(mask_dh); - free(mask); - - return 0; - -err: - if ( p->mask_file != NULL ) H5Fclose(fh); - free(mask); - return 1; -} - - -int hdf5_read2(struct hdfile *f, struct image *image, struct event *ev, - int satcorr) -{ - herr_t r; - int pi; - int i; - - if ( image->det == NULL ) { - ERROR("Geometry not available\n"); - return 1; - } - - image->dp = malloc(image->det->n_panels*sizeof(float *)); - image->bad = malloc(image->det->n_panels*sizeof(int *)); - image->sat = malloc(image->det->n_panels*sizeof(float *)); - if ( (image->dp==NULL) || (image->bad==NULL) || (image->sat==NULL) ) { - ERROR("Failed to allocate data arrays.\n"); - return 1; - } - - for ( pi=0; pi<image->det->n_panels; pi++ ) { - - hsize_t *f_offset, *f_count; - int hsi; - struct dim_structure *hsd; - herr_t check; - hid_t dataspace, memspace; - int fail; - struct panel *p; - hsize_t dims[2]; - - p = &image->det->panels[pi]; - - if ( ev != NULL ) { - - int exists; - char *panel_full_path; - - panel_full_path = retrieve_full_path(ev, p->data); - - exists = check_path_existence(f->fh, panel_full_path); - if ( !exists ) { - ERROR("Cannot find data for panel %s\n", - p->name); - free(image->dp); - free(image->bad); - free(image->sat); - return 1; - } - - fail = hdfile_set_image(f, panel_full_path); - - free(panel_full_path); - - } else { - - if ( p->data == NULL ) { - - fail = hdfile_set_first_image(f, "/"); - - } else { - - int exists; - exists = check_path_existence(f->fh, p->data); - if ( !exists ) { - ERROR("Cannot find data for panel %s\n", - p->name); - free(image->dp); - free(image->bad); - free(image->sat); - return 1; - } - fail = hdfile_set_image(f, p->data); - - } - - } - if ( fail ) { - ERROR("Couldn't select path for panel %s\n", - p->name); - free(image->dp); - free(image->bad); - free(image->sat); - return 1; - } - - /* Determine where to read the data from in the file */ - hsd = image->det->panels[pi].dim_structure; - f_offset = malloc(hsd->num_dims*sizeof(hsize_t)); - f_count = malloc(hsd->num_dims*sizeof(hsize_t)); - if ( (f_offset == NULL) || (f_count == NULL ) ) { - ERROR("Failed to allocate offset or count.\n"); - free(image->dp); - free(image->bad); - free(image->sat); - return 1; - } - for ( hsi=0; hsi<hsd->num_dims; hsi++ ) { - - if ( hsd->dims[hsi] == HYSL_FS ) { - f_offset[hsi] = p->orig_min_fs; - f_count[hsi] = p->orig_max_fs - p->orig_min_fs+1; - } else if ( hsd->dims[hsi] == HYSL_SS ) { - f_offset[hsi] = p->orig_min_ss; - f_count[hsi] = p->orig_max_ss - p->orig_min_ss+1; - } else if (hsd->dims[hsi] == HYSL_PLACEHOLDER ) { - f_offset[hsi] = ev->dim_entries[0]; - f_count[hsi] = 1; - } else { - f_offset[hsi] = hsd->dims[hsi]; - f_count[hsi] = 1; - } - - } - - /* Set up dataspace for file */ - dataspace = H5Dget_space(f->dh); - check = H5Sselect_hyperslab(dataspace, H5S_SELECT_SET, - f_offset, NULL, f_count, NULL); - if ( check < 0 ) { - ERROR("Error selecting file dataspace for panel %s\n", - p->name); - free(image->dp); - free(image->bad); - free(image->sat); - return 1; - } - - dims[0] = p->h; - dims[1] = p->w; - memspace = H5Screate_simple(2, dims, NULL); - - image->dp[pi] = malloc(p->w*p->h*sizeof(float)); - image->sat[pi] = malloc(p->w*p->h*sizeof(float)); - if ( (image->dp[pi] == NULL) || (image->sat[pi] == NULL) ) { - ERROR("Failed to allocate panel %s\n", p->name); - free(f_offset); - free(f_count); - free(image->dp); - free(image->bad); - free(image->sat); - return 1; - } - for ( i=0; i<p->w*p->h; i++ ) image->sat[pi][i] = INFINITY; - - r = H5Dread(f->dh, H5T_NATIVE_FLOAT, memspace, dataspace, - H5P_DEFAULT, image->dp[pi]); - if ( r < 0 ) { - ERROR("Couldn't read data for panel %s\n", - p->name); - free(f_offset); - free(f_count); - for ( i=0; i<=pi; i++ ) { - free(image->dp[i]); - free(image->sat[i]); - } - free(image->dp); - free(image->bad); - free(image->sat); - return 1; - } - - 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, image->det, - image->dp[pi], p); - } else { - image->bad[pi] = make_badmask(NULL, image->det, - image->dp[pi], p); - } - free(flags); - } else { - image->bad[pi] = make_badmask(NULL, image->det, - image->dp[pi], p); - } - - if ( p->satmap != NULL ) { - if ( load_satmap(f, ev, p, f_offset, f_count, hsd, - image->sat[pi]) ) - { - ERROR("Failed to load sat map for panel %s\n", - p->name); - } - } - - H5Sclose(dataspace); - free(f_offset); - free(f_count); - - } - - H5Dclose(f->dh); - f->data_open = 0; - hdfile_fill_in_clen(image->det, f, ev); - - if ( satcorr ) debodge_saturation(f, image); - - if ( image->beam != NULL ) { - - hdfile_fill_in_beam_parameters(image->beam, f, ev, image); - - if ( (image->lambda > 1.0) || (image->lambda < 1e-20) ) { - - ERROR("WARNING: Nonsensical wavelength (%e m) value " - "for file: %s, event: %s.\n", - image->lambda, image->filename, - get_event_string(image->event)); - } - - } - - fill_in_adu(image); - - return 0; -} - - -static int looks_like_image(hid_t h) -{ - hid_t sh; - hsize_t size[2]; - hsize_t max_size[2]; - - sh = H5Dget_space(h); - if ( sh < 0 ) return 0; - - if ( H5Sget_simple_extent_ndims(sh) != 2 ) { - H5Sclose(sh); - return 0; - } - - H5Sget_simple_extent_dims(sh, size, max_size); - H5Sclose(sh); - - if ( ( size[0] > 64 ) && ( size[1] > 64 ) ) return 1; - - return 0; -} - - -int hdfile_is_scalar(struct hdfile *f, const char *name, int verbose) -{ - hid_t dh; - hid_t sh; - hsize_t size[3]; - hid_t type; - int ndims; - int i; - int check; - - check = check_path_existence(f->fh, name); - if ( check == 0 ) { - ERROR("No such scalar field '%s'\n", name); - return 0; - } - - dh = H5Dopen2(f->fh, name, H5P_DEFAULT); - type = H5Dget_type(dh); - - /* Get the dimensionality. We have to cope with scalars expressed as - * arrays with all dimensions 1, as well as zero-d arrays. */ - sh = H5Dget_space(dh); - ndims = H5Sget_simple_extent_ndims(sh); - if ( ndims > 3 ) { - if ( verbose ) { - ERROR("Too many dimensions (%i).\n", ndims); - } - H5Sclose(sh); - H5Tclose(type); - H5Dclose(dh); - return 0; - } - - /* Check that the size in all dimensions is 1 */ - H5Sget_simple_extent_dims(sh, size, NULL); - H5Sclose(sh); - H5Tclose(type); - H5Dclose(dh); - for ( i=0; i<ndims; i++ ) { - if ( size[i] != 1 ) { - if ( verbose ) { - ERROR("%s not a scalar value (ndims=%i," - "size[%i]=%i)\n", - name, ndims, i, (int)size[i]); - } - return 0; - } - } - - return 1; -} - - -struct copy_hdf5_field -{ - char **fields; - int n_fields; - int max_fields; -}; - - -struct copy_hdf5_field *new_copy_hdf5_field_list() -{ - struct copy_hdf5_field *n; - - n = calloc(1, sizeof(struct copy_hdf5_field)); - if ( n == NULL ) return NULL; - - n->max_fields = 32; - n->fields = malloc(n->max_fields*sizeof(char *)); - if ( n->fields == NULL ) { - free(n); - return NULL; - } - - return n; -} - - -void free_copy_hdf5_field_list(struct copy_hdf5_field *n) -{ - int i; - for ( i=0; i<n->n_fields; i++ ) { - free(n->fields[i]); - } - free(n->fields); - free(n); -} - - -void add_copy_hdf5_field(struct copy_hdf5_field *copyme, - const char *name) -{ - int i; - - /* Already on the list? Don't re-add if so. */ - for ( i=0; i<copyme->n_fields; i++ ) { - if ( strcmp(copyme->fields[i], name) == 0 ) return; - } - - /* Need more space? */ - if ( copyme->n_fields == copyme->max_fields ) { - - char **nfields; - int nmax = copyme->max_fields + 32; - - nfields = realloc(copyme->fields, nmax*sizeof(char *)); - if ( nfields == NULL ) { - ERROR("Failed to allocate space for new HDF5 field.\n"); - return; - } - - copyme->max_fields = nmax; - copyme->fields = nfields; - - } - - copyme->fields[copyme->n_fields] = strdup(name); - if ( copyme->fields[copyme->n_fields] == NULL ) { - ERROR("Failed to add field for copying '%s'\n", name); - return; - } - - copyme->n_fields++; -} - - -void copy_hdf5_fields(struct hdfile *f, const struct copy_hdf5_field *copyme, - FILE *fh, struct event *ev) -{ - int i; - - if ( copyme == NULL ) return; - - for ( i=0; i<copyme->n_fields; i++ ) { - - char *val; - char *field; - - field = copyme->fields[i]; - val = hdfile_get_string_value(f, field, ev); - - if ( field[0] == '/' ) { - fprintf(fh, "hdf5%s = %s\n", field, val); - } else { - fprintf(fh, "hdf5/%s = %s\n", field, val); - } - - free(val); - - } -} - - -static int make_dataspaces(hid_t dh, struct event *ev, hid_t *memspace, - hid_t *filespace) -{ - hsize_t *f_offset, *f_count; - hsize_t *m_offset, *m_count; - hid_t sh, mh; - int ndims; - int i; - - if ( ev == NULL ) { - ERROR("Can't make dataspaces: no event ID\n"); - return 1; - } - - /* Check that there are at least as many dim entries as dimensions */ - sh = H5Dget_space(dh); - ndims = H5Sget_simple_extent_ndims(sh); - if ( ndims > ev->dim_length ) { - return 1; - } - - /* Now set up arrays of offsets and counts in files and memory */ - f_offset = malloc(sizeof(hsize_t)*ndims); - f_count = malloc(sizeof(hsize_t)*ndims); - m_offset = malloc(sizeof(hsize_t)*ndims); - m_count = malloc(sizeof(hsize_t)*ndims); - if ( (f_offset == NULL) || (f_count == NULL) - || (m_offset == NULL) || (m_count == NULL) ) return 1; - - for ( i=0; i<ev->dim_length; i++ ) { - f_offset[i] = ev->dim_entries[i]; - f_count[i] = 1; - m_offset[i] = 0; - m_count[i] = 1; - } - - if ( H5Sselect_hyperslab(sh, H5S_SELECT_SET, - f_offset, NULL, f_count, NULL) ) return 1; - - free(f_offset); - free(f_count); - - mh = H5Screate_simple(ndims, m_count, NULL); - if ( H5Sselect_hyperslab(mh, H5S_SELECT_SET, - m_offset, NULL, m_count, NULL) ) return 1; - free(m_offset); - free(m_count); - - *memspace = mh; - *filespace = sh; - - return 0; -} - - -static char *read_vlen_string(hid_t dh, struct event *ev) -{ - hid_t memspace, filespace; - herr_t r; - char *tmp; - hid_t type; - - if ( make_dataspaces(dh, ev, &memspace, &filespace) ) { - return strdup("[couldn't make dataspaces - variable len]"); - } - - type = H5Dget_type(dh); - r = H5Dread(dh, type, memspace, filespace, H5P_DEFAULT, &tmp); - H5Tclose(type); - if ( r < 0 ) { - return strdup("[couldn't read vlen string]"); - } - - H5Sclose(memspace); - H5Sclose(filespace); - - /* Variable strings are 0-terminated */ - chomp(tmp); - return tmp; -} - - -static char *read_fixed_string(hid_t dh, struct event *ev) -{ - hid_t memspace, filespace; - herr_t r; - hid_t sh, type; - size_t size; - char *tmp; - - type = H5Dget_type(dh); - size = H5Tget_size(type); - tmp = malloc(size+1); - if ( tmp == NULL ) { - H5Tclose(type); - return strdup("[couldn't allocate string]"); - } - - if ( ev == NULL ) { - - /* Try a simple fixed-length string */ - sh = H5Dget_space(dh); - if ( H5Sget_simple_extent_ndims(sh) ) { - H5Tclose(type); - return strdup("[non-scalar string]"); - } - - sh = H5Screate(H5S_SCALAR); - r = H5Dread(dh, type, sh, H5S_ALL, H5P_DEFAULT, tmp); - H5Sclose(sh); - if ( r < 0 ) { - free(tmp); - H5Tclose(type); - return strdup("[couldn't read scalar string]"); - } else { - H5Tclose(type); - tmp[size] = '\0'; - chomp(tmp); - return tmp; - } - } - - if ( make_dataspaces(dh, ev, &memspace, &filespace) ) { - H5Tclose(type); - return strdup("[couldn't make dataspaces - fixed len]"); - } - - r = H5Dread(dh, type, memspace, filespace, H5P_DEFAULT, tmp); - if ( r < 0 ) { - H5Tclose(type); - return strdup("[couldn't read string]"); - } - - H5Tclose(type); - H5Sclose(memspace); - H5Sclose(filespace); - - tmp[size] = '\0'; - chomp(tmp); - return tmp; -} - - -static char *read_general_string(hid_t dh, struct event *ev) -{ - htri_t v; - hid_t type; - - type = H5Dget_type(dh); - v = H5Tis_variable_str(type); - H5Tclose(type); - - if ( v < 0 ) { - return strdup("[unrecognised string type]"); - - } else if ( v > 0 ) { - /* Variable length string */ - return read_vlen_string(dh, ev); - - } else { - /* Fixed-length string */ - return read_fixed_string(dh, ev); - - } -} - - -char *hdfile_get_string_value(struct hdfile *f, const char *name, - struct event *ev) -{ - hid_t dh; - hid_t type; - hid_t class; - int buf_i; - double buf_f; - char *tmp = NULL; - char *subst_name = NULL; - - if ( (ev != NULL) && (ev->path_length != 0) ) { - subst_name = retrieve_full_path(ev, name); - } else { - subst_name = strdup(name); - } - - dh = H5Dopen2(f->fh, subst_name, H5P_DEFAULT); - if ( dh < 0 ) { - free(subst_name); - return strdup("[couldn't read string]"); - } - - type = H5Dget_type(dh); - class = H5Tget_class(type); - H5Tclose(type); - - if ( class == H5T_STRING ) { - - free(subst_name); - tmp = read_general_string(dh, ev); - H5Dclose(dh); - return tmp; - - } else { - - int r; - - H5Dclose(dh); - - switch ( class ) { - - case H5T_FLOAT : - r = hdfile_get_value(f, subst_name, ev, &buf_f, - H5T_NATIVE_DOUBLE); - free(subst_name); - if ( r == 0 ) { - tmp = malloc(256); - if ( tmp == NULL ) { - ERROR("Failed to allocate float\n"); - return NULL; - } - snprintf(tmp, 255, "%f", buf_f); - return tmp; - } else { - return NULL; - } - break; - - case H5T_INTEGER : - r = hdfile_get_value(f, subst_name, ev, &buf_i, - H5T_NATIVE_INT); - free(subst_name); - if ( r == 0 ) { - tmp = malloc(256); - if ( tmp == NULL ) { - ERROR("Failed to allocate int buf!\n"); - return NULL; - } - snprintf(tmp, 255, "%d", buf_i); - return tmp; - - } else { - return NULL; - } - break; - - default : - ERROR("Unrecognised type: %s\n", subst_name); - free(subst_name); - return NULL; - } - - } -} - - -char **hdfile_read_group(struct hdfile *f, int *n, const char *parent, - int **p_is_group, int **p_is_image) -{ - hid_t gh; - hsize_t num; - char **res; - int i; - int *is_group; - int *is_image; - H5G_info_t ginfo; - - gh = H5Gopen2(f->fh, parent, H5P_DEFAULT); - if ( gh < 0 ) { - *n = 0; - return NULL; - } - - if ( H5Gget_info(gh, &ginfo) < 0 ) { - /* Whoopsie */ - *n = 0; - return NULL; - } - num = ginfo.nlinks; - *n = num; - if ( num == 0 ) return NULL; - - res = malloc(num*sizeof(char *)); - is_image = malloc(num*sizeof(int)); - is_group = malloc(num*sizeof(int)); - *p_is_image = is_image; - *p_is_group = is_group; - - for ( i=0; i<num; i++ ) { - - char buf[256]; - hid_t dh; - H5I_type_t type; - - H5Lget_name_by_idx(gh, ".", H5_INDEX_NAME, H5_ITER_NATIVE, - i, buf, 255, H5P_DEFAULT); - res[i] = malloc(512); - if ( strlen(parent) > 1 ) { - snprintf(res[i], 511, "%s/%s", parent, buf); - } else { - snprintf(res[i], 511, "%s%s", parent, buf); - } /* ick */ - - is_image[i] = 0; - is_group[i] = 0; - dh = H5Oopen(gh, buf, H5P_DEFAULT); - if ( dh < 0 ) continue; - type = H5Iget_type(dh); - - if ( type == H5I_GROUP ) { - is_group[i] = 1; - } else if ( type == H5I_DATASET ) { - is_image[i] = looks_like_image(dh); - } - H5Oclose(dh); - - } - - H5Gclose(gh); - - return res; -} - - -int hdfile_set_first_image(struct hdfile *f, const char *group) -{ - char **names; - int *is_group; - int *is_image; - int n, i, j; - - names = hdfile_read_group(f, &n, group, &is_group, &is_image); - if ( n == 0 ) return 1; - - for ( i=0; i<n; i++ ) { - - if ( is_image[i] ) { - hdfile_set_image(f, names[i]); - for ( j=0; j<n; j++ ) free(names[j]); - free(is_image); - free(is_group); - free(names); - return 0; - } else if ( is_group[i] ) { - if ( !hdfile_set_first_image(f, names[i]) ) { - for ( j=0; j<n; j++ ) free(names[j]); - free(is_image); - free(is_group); - free(names); - return 0; - } - } - - } - - for ( j=0; j<n; j++ ) free(names[j]); - free(is_image); - free(is_group); - free(names); - - return 1; -} - - -struct parse_params { - struct hdfile *hdfile; - int path_dim; - const char *path; - struct event *curr_event; - struct event_list *ev_list; - int top_level; -}; - - -int check_path_existence(hid_t fh, const char *path) -{ - - char buffer[256]; - char buffer_full_path[2048]; - herr_t herrt; - struct H5O_info_t ob_info; - char *path_copy = strdup(path); - char *start = path_copy; - char *sep = NULL; - - buffer[0] = '\0'; - buffer_full_path[0] = '\0'; - - if ( strcmp(path_copy, "/" ) == 0 ) { - return 1; - } - - do { - - int check; - - sep = strstr(start, "/"); - if ( sep != NULL && strlen(sep) == 1 ) { - ERROR("Error: Data path ends with a / symbol\n"); - free(path_copy); - return 1; - } - - if ( sep != NULL ) { - - if ( sep == start ) { - start = sep+1; - strcat(buffer_full_path, "/"); - continue; - } - - strncpy(buffer, start, sep-start); - buffer[sep-start] = '\0'; - strcat(buffer_full_path, buffer); - - check = H5Lexists(fh, buffer_full_path, H5P_DEFAULT); - if ( check == 0 ) { - return 0; - } else { - herrt = H5Oget_info_by_name(fh, buffer_full_path, - &ob_info, - H5P_DEFAULT); - if ( herrt < 0 ) { - return -1; - } - if ( ob_info.type != H5O_TYPE_GROUP ) { - return 0; - } - - start = sep+1; - strcat(buffer_full_path, "/"); - - } - - } else { - - strcpy(buffer, start); - strcat(buffer_full_path, buffer); - - check = H5Lexists(fh, buffer_full_path, H5P_DEFAULT); - if ( check == 0 ) { - return 0; - } - - } - } while (sep); - - free(path_copy); - return 1; - -} - - -static herr_t parse_file_event_structure(hid_t loc_id, char *name, - const H5L_info_t *info, - struct parse_params *pp) - -{ - char *substituted_path; - char *ph_loc; - char *truncated_path; - htri_t check; - herr_t herrt_iterate, herrt_info; - struct H5O_info_t object_info; - - if ( !pp->top_level ) { - - int fail_push; - - fail_push = push_path_entry_to_event(pp->curr_event, name); - if ( fail_push ) { - return -1; - } - - substituted_path = event_path_placeholder_subst(name, pp->path); - - } else { - substituted_path = strdup(pp->path); - } - - if ( pp->top_level == 1 ) { - pp->top_level = 0; - } - - truncated_path = strdup(substituted_path); - ph_loc = strstr(substituted_path,"%"); - if ( ph_loc != NULL) { - truncated_path[ph_loc-substituted_path] = '\0'; - } - - herrt_iterate = 0; - herrt_info = 0; - - check = check_path_existence(pp->hdfile->fh, truncated_path); - if ( check == 0 ) { - pop_path_entry_from_event(pp->curr_event); - return 0; - } else { - - herrt_info = H5Oget_info_by_name(pp->hdfile->fh, truncated_path, - &object_info, H5P_DEFAULT); - if ( herrt_info < 0 ) { - free(truncated_path); - free(substituted_path); - return -1; - } - - if ( pp->curr_event->path_length == pp->path_dim - && object_info.type == H5O_TYPE_DATASET ) - { - - int fail_append; - - fail_append = append_event_to_event_list(pp->ev_list, - pp->curr_event); - if ( fail_append ) { - free(truncated_path); - free(substituted_path); - return -1; - } - - pop_path_entry_from_event(pp->curr_event); - return 0; - - } else { - - pp->path = substituted_path; - - if ( object_info.type == H5O_TYPE_GROUP ) { - - herrt_iterate = H5Literate_by_name(pp->hdfile->fh, - truncated_path, H5_INDEX_NAME, - H5_ITER_NATIVE, NULL, - (H5L_iterate_t)parse_file_event_structure, - (void *)pp, H5P_DEFAULT); - } - } - } - - pop_path_entry_from_event(pp->curr_event); - - free(truncated_path); - free(substituted_path); - - return herrt_iterate; -} - - -static int fill_paths(struct hdfile *hdfile, struct detector *det, int pi, - struct event_list *master_el) -{ - struct parse_params pparams; - struct event *empty_event; - struct event_list *panel_ev_list; - int ei; - int check; - - empty_event = initialize_event(); - panel_ev_list = initialize_event_list(); - if ( (empty_event == NULL) || (panel_ev_list == NULL) ) - { - ERROR("Failed to allocate memory for event list.\n"); - return 1; - } - - pparams.path = det->panels[pi].data; - pparams.hdfile = hdfile; - pparams.path_dim = det->path_dim; - pparams.curr_event = empty_event; - pparams.top_level = 1; - pparams.ev_list = panel_ev_list; - - check = parse_file_event_structure(hdfile->fh, NULL, NULL, &pparams); - if ( check < 0 ) { - free_event(empty_event); - free_event_list(panel_ev_list); - return 1; - } - - for ( ei=0; ei<panel_ev_list->num_events; ei++ ) { - - int fail_add; - - fail_add = add_non_existing_event_to_event_list(master_el, - panel_ev_list->events[ei]); - if ( fail_add ) { - free_event(empty_event); - free_event_list(panel_ev_list); - return 1; - } - - } - - free_event(empty_event); - free_event_list(panel_ev_list); - - return 0; -} - - -static int check_dims(struct hdfile *hdfile, struct panel *p, struct event *ev, - struct event_list *events, int *global_path_dim) -{ - char *full_panel_path; - hid_t dh; - hid_t sh; - int dims; - hsize_t *size; - hsize_t *max_size; - int hsdi; - int panel_path_dim = 0; - struct dim_structure *panel_dim_structure; - - /* Get the full path for this panel in this event */ - full_panel_path = retrieve_full_path(ev, p->data); - - dh = H5Dopen2(hdfile->fh, full_panel_path, H5P_DEFAULT); - if ( dh < 0 ) { - ERROR("Error opening '%s'\n", full_panel_path); - ERROR("Failed to enumerate events. " - "Check your geometry file.\n"); - return 1; - } - - sh = H5Dget_space(dh); - dims = H5Sget_simple_extent_ndims(sh); - size = malloc(dims*sizeof(hsize_t)); - max_size = malloc(dims*sizeof(hsize_t)); - if ( (size==NULL) || (max_size==NULL) ) { - ERROR("Failed to allocate memory for dimensions\n"); - return 1; - } - - dims = H5Sget_simple_extent_dims(sh, size, max_size); - - panel_dim_structure = p->dim_structure; - for ( hsdi=0; hsdi<panel_dim_structure->num_dims; hsdi++ ) { - if ( panel_dim_structure->dims[hsdi] == HYSL_PLACEHOLDER ) { - panel_path_dim = size[hsdi]; - break; - } - } - - if ( *global_path_dim == -1 ) { - - *global_path_dim = panel_path_dim; - - } else if ( panel_path_dim != *global_path_dim ) { - - ERROR("All panels must have the same number of frames\n"); - ERROR("Panel %s has %i frames in one dimension, but the first " - "panel has %i.\n", - p->name, panel_path_dim, *global_path_dim); - free(size); - free(max_size); - return 1; - } - - H5Sclose(sh); - H5Dclose(dh); - - return 0; -} - - -struct event_list *fill_event_list(struct hdfile *hdfile, struct detector *det) -{ - struct event_list *master_el; - - master_el = initialize_event_list(); - if ( master_el == NULL ) { - ERROR("Failed to allocate event list.\n"); - return NULL; - } - - /* First expand any placeholders in the HDF5 paths */ - if ( det->path_dim != 0 ) { - int pi; - for ( pi=0; pi<det->n_panels; pi++ ) { - if ( fill_paths(hdfile, det, pi, master_el) ) { - ERROR("Failed to enumerate paths.\n"); - return NULL; - } - } - } - - /* Now enumerate the placeholder dimensions */ - if ( det->dim_dim > 0 ) { - - struct event_list *master_el_with_dims; - int evi; - - /* If there were no HDF5 path placeholders, add a dummy event */ - if ( master_el->num_events == 0 ) { - struct event *empty_ev; - empty_ev = initialize_event(); - append_event_to_event_list(master_el, empty_ev); - free(empty_ev); - } - - master_el_with_dims = initialize_event_list(); - - /* For each event so far, expand the dimensions */ - for ( evi=0; evi<master_el->num_events; evi++ ) { - - int pi; - int global_path_dim = -1; - int mlwd; - - /* Check the dimensionality of each panel */ - for ( pi=0; pi<det->n_panels; pi++ ) { - if ( check_dims(hdfile, &det->panels[pi], - master_el->events[evi], - master_el_with_dims, - &global_path_dim) ) - { - ERROR("Failed to enumerate dims.\n"); - return NULL; - } - } - - /* Add this dimensionality to all events */ - for ( mlwd=0; mlwd<global_path_dim; mlwd++ ) { - - struct event *mlwd_ev; - - mlwd_ev = copy_event(master_el->events[evi]); - push_dim_entry_to_event(mlwd_ev, mlwd); - append_event_to_event_list(master_el_with_dims, - mlwd_ev); - free_event(mlwd_ev); - } - - } - - free_event_list(master_el); - return master_el_with_dims; - - } else { - - return master_el; - - } -} diff --git a/libcrystfel/src/hdf5-file.h b/libcrystfel/src/hdf5-file.h deleted file mode 100644 index 99f231e1..00000000 --- a/libcrystfel/src/hdf5-file.h +++ /dev/null @@ -1,117 +0,0 @@ -/* - * hdf5-file.h - * - * Read/write HDF5 data files - * - * Copyright © 2012-2020 Deutsches Elektronen-Synchrotron DESY, - * a research centre of the Helmholtz Association. - * - * Authors: - * 2009-2017 Thomas White <taw@physics.org> - * 2014 Valerio Mariani - - * - * 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 HDF5_H -#define HDF5_H - -struct event_list; - -#include <stdint.h> -#include <hdf5.h> -#include "image.h" -#include "events.h" - -struct hdfile; -struct copy_hdf5_field; - -#include "image.h" -#include "events.h" - -#ifdef __cplusplus -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); - -extern int hdf5_write_image(const char *filename, const struct image *image, - char *element); - -extern int hdf5_read(struct hdfile *f, struct image *image, - const char* element, int satcorr); - -extern int hdf5_read2(struct hdfile *f, struct image *image, - struct event *ev, int satcorr); - -extern int check_path_existence(hid_t fh, const char *path); - -extern struct hdfile *hdfile_open(const char *filename); -int hdfile_set_image(struct hdfile *f, const char *path); - -extern int16_t *hdfile_get_image_binned(struct hdfile *hdfile, - int binning, int16_t *maxp); -extern char **hdfile_read_group(struct hdfile *f, int *n, const char *parent, - int **p_is_group, int **p_is_image); -extern int hdfile_set_first_image(struct hdfile *f, const char *group); -extern void hdfile_close(struct hdfile *f); - -extern int get_peaks(struct image *image, struct hdfile *f, const char *p); - -extern int get_peaks_2(struct image *image, struct hdfile *f, const char *p, - int half_pixel_shift); - -extern int get_peaks_cxi(struct image *image, struct hdfile *f, const char *p, - struct filename_plus_event *fpe); - -extern int get_peaks_cxi_2(struct image *image, struct hdfile *f, const char *p, - struct filename_plus_event *fpe, - int half_pixel_shift); - -extern struct copy_hdf5_field *new_copy_hdf5_field_list(void); -extern void free_copy_hdf5_field_list(struct copy_hdf5_field *f); - -extern void copy_hdf5_fields(struct hdfile *f, - const struct copy_hdf5_field *copyme, - FILE *fh, struct event *ev); -extern void add_copy_hdf5_field(struct copy_hdf5_field *copyme, - const char *name); -extern struct event_list *fill_event_list(struct hdfile* hdfile, - struct detector* det); - -extern int hdfile_get_value(struct hdfile *f, const char *name, - struct event *ev, void *val, hid_t memtype); -extern int hdfile_is_scalar(struct hdfile *f, const char *name, int verbose); -extern char *hdfile_get_string_value(struct hdfile *f, const char *name, - struct event *ev); - -#ifdef __cplusplus -} -#endif - -#endif /* HDF5_H */ diff --git a/libcrystfel/src/image-cbf.c b/libcrystfel/src/image-cbf.c new file mode 100644 index 00000000..c9de17df --- /dev/null +++ b/libcrystfel/src/image-cbf.c @@ -0,0 +1,634 @@ +/* + * image-cbf.c + * + * Image loading, CBF parts + * + * Copyright © 2012-2021 Deutsches Elektronen-Synchrotron DESY, + * a research centre of the Helmholtz Association. + * + * Authors: + * 2020 Thomas White <taw@physics.org> + * + * This file is part of CrystFEL. + * + * CrystFEL is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * CrystFEL is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with CrystFEL. If not, see <http://www.gnu.org/licenses/>. + * + */ + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#include <stdlib.h> +#include <assert.h> +#include <math.h> +#include <stdio.h> +#include <zlib.h> +#include <unistd.h> + +#include "image.h" +#include "utils.h" +#include "detgeom.h" + +#include "datatemplate.h" +#include "datatemplate_priv.h" + +static void add_out(float val, float *data_out, int nmemb_out, + int *outpos, int *nrej) +{ + if ( *outpos < nmemb_out ) { + data_out[(*outpos)++] = val; + } else { + (*nrej)++; + } +} + + +/* 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); + } +} + + +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; +} + + +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) +{ + 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; + + case CBF_ELEMENT_S32: + data_out[o++] = ((int32_t *)data_in)[i]; + break; + + 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 0; +} + + +static float *read_cbf_data(const char *filename, int gz, int *w, int *h) +{ + FILE *fh; + 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 ( !gz ) { + + fh = fopen(filename, "rb"); + if ( fh == NULL ) { + ERROR("Failed to open '%s'\n", filename); + return NULL; + } + + } else { + + gzFile gzfh; + size_t len, len_read; + const size_t bufinc = 8*1024*1024; /* Allocate buffer in 8Mb chunks */ + size_t bufsz = bufinc; + + gzfh = gzopen(filename, "rb"); + if ( gzfh == NULL ) return NULL; + + /* Set larger buffer size for hopefully faster uncompression */ + gzbuffer(gzfh, 128*1024); + + buf = malloc(bufsz); + if ( buf == NULL ) return NULL; + + len = 0; + do { + + len_read = gzread(gzfh, buf+len, bufinc); + if ( len_read == -1 ) return NULL; + len += len_read; + + if ( len_read == bufinc ) { + bufsz += bufinc; + buf = realloc(buf, bufsz); + if ( buf == NULL ) return NULL; + } + + } while ( len_read == bufinc ); + + fh = fmemopen(buf, len, "rb"); + if ( fh == NULL ) return NULL; + + gzclose(gzfh); + + } + + /* 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 { + + char line[1024]; + long line_start; + + line_start = ftell(fh); + rval = fgets(line, 1023, fh); + if ( rval == NULL ) break; + chomp(line); + + if ( strcmp(line, "--CIF-BINARY-FORMAT-SECTION--") == 0 ) { + in_binary_section = 1; + } + + if ( strcmp(line, "--CIF-BINARY-FORMAT-SECTION----") == 0 ) { + in_binary_section = 0; + } + + if ( in_binary_section ) { + + if ( strncmp(line, "X-Binary-Size: ", 15) == 0 ) { + data_compressed_len = atoi(line+15); + } + + 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; + } + } + + /* 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; + } + + /* 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; + } + } + + if ( strncmp(line, "X-Binary-Size-Fastest-Dimension: ", 33) == 0 ) { + *w = atoi(line+33); + } + + if ( strncmp(line, "X-Binary-Size-Second-Dimension: ", 32) == 0 ) { + *h = atoi(line+32); + } + + } + + 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); + + if ( r ) { + free(buf); + free(data_out); + fclose(fh); + return NULL; + } + + free(buf); + fclose(fh); + return data_out; + + } + + } while ( rval != NULL ); + + ERROR("Reached end of CBF file before finding data.\n"); + free(buf); /* might be NULL */ + return NULL; +} + + +signed int is_cbf_file(const char *filename) +{ + FILE *fh; + char line[1024]; + + fh = fopen(filename, "r"); + if ( fh == NULL ) return -1; + + if ( fgets(line, 1024, fh) == NULL ) return 0; + fclose(fh); + + if ( strstr(line, "CBF") == NULL ) { + return 0; + } + + return 1; +} + + +signed int is_cbfgz_file(const char *filename) +{ + gzFile gzfh; + char line[1024]; + + gzfh = gzopen(filename, "rb"); + if ( gzfh == NULL ) return -1; + if ( gzgets(gzfh, line, 1024) == NULL ) return 0; + gzclose(gzfh); + + if ( strstr(line, "CBF") == NULL ) { + return 0; + } + + return 1; +} + + +int image_cbf_read_mask(struct panel_template *p, + const char *filename, const char *event, + int gz, int *bad, int mask_good, int mask_bad) +{ + ERROR("Mask loading from CBF not yet supported\n"); + return 1; +} + + +static int unpack_panels(struct image *image, + const DataTemplate *dtempl, + float *data, int data_width, int data_height) +{ + int pi; + + image->dp = malloc(dtempl->n_panels * sizeof(float *)); + if ( image->dp == NULL ) { + ERROR("Failed to allocate panels.\n"); + return 1; + } + + for ( pi=0; pi<dtempl->n_panels; pi++ ) { + + struct panel_template *p; + int fs, ss; + int p_w, p_h; + + p = &dtempl->panels[pi]; + p_w = p->orig_max_fs - p->orig_min_fs + 1; + p_h = p->orig_max_ss - p->orig_min_ss + 1; + image->dp[pi] = malloc(p_w*p_h*sizeof(float)); + if ( image->dp[pi] == NULL ) { + ERROR("Failed to allocate panel\n"); + return 1; + } + + if ( (p->orig_min_fs + p_w > data_width) + || (p->orig_min_ss + p_h > data_height) ) + { + ERROR("Panel %s is outside range of data in CBF file\n", + p->name); + return 1; + } + + for ( ss=0; ss<p_h; ss++ ) { + for ( fs=0; fs<p_w; fs++ ) { + + int idx; + int cfs, css; + + cfs = fs+p->orig_min_fs; + css = ss+p->orig_min_ss; + idx = cfs + css*data_width; + + image->dp[pi][fs+p_w*ss] = data[idx]; + + } + } + + } + + return 0; +} + + +int image_cbf_read(struct image *image, + const DataTemplate *dtempl, + const char *filename, + const char *event, + int gz) +{ + float *data; + int w, h; + + if ( access(filename, R_OK) == -1 ) { + ERROR("File does not exist or cannot be read: %s\n", filename); + return 1; + } + + data = read_cbf_data(filename, gz, &w, &h); + if ( data == NULL ) { + ERROR("Failed to read CBF data\n"); + return 1; + } + + unpack_panels(image, dtempl, data, w, h); + free(data); + + //cbf_fill_in_beam_parameters(image->beam, f, image); + //cbf_fill_in_clen(image->det, f); + //fill_in_adu(image); + + return 0; +} diff --git a/libcrystfel/src/image-cbf.h b/libcrystfel/src/image-cbf.h new file mode 100644 index 00000000..ca79a1a3 --- /dev/null +++ b/libcrystfel/src/image-cbf.h @@ -0,0 +1,56 @@ +/* + * image-cbf.h + * + * Image loading, CBF parts + * + * Copyright © 2012-2021 Deutsches Elektronen-Synchrotron DESY, + * a research centre of the Helmholtz Association. + * + * Authors: + * 2020 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/>. + * + */ + +/* NB This file is NOT part of the public API, and should NOT + * be installed, but rather stays in the libcrystfel source folder. */ + +#ifndef IMAGE_CBF_H +#define IMAGE_CBF_H + +#include "datatemplate_priv.h" + +extern signed int is_cbf_file(const char *filename); + +extern signed int is_cbfgz_file(const char *filename); + +extern int load_mask_cbf(struct panel_template *p, + const char *filename, const char *event, + int gz, int *bad, int mask_good, int mask_bad); + +extern int image_cbf_read(struct image *image, + const DataTemplate *dtempl, + const char *filename, + const char *event, + int gz); + +extern int image_cbf_read_mask(struct panel_template *p, + const char *filename, const char *event, + int gz, int *bad, + int mask_good, int mask_bad); + +#endif /* IMAGE_CBF_H */ diff --git a/libcrystfel/src/image-hdf5.c b/libcrystfel/src/image-hdf5.c new file mode 100644 index 00000000..ac987970 --- /dev/null +++ b/libcrystfel/src/image-hdf5.c @@ -0,0 +1,2007 @@ +/* + * image-hdf5.c + * + * Image loading, HDF5 parts + * + * Copyright © 2012-2021 Deutsches Elektronen-Synchrotron DESY, + * a research centre of the Helmholtz Association. + * + * Authors: + * 2020 Thomas White <taw@physics.org> + * 2014-2018 Valerio Mariani + * + * This file is part of CrystFEL. + * + * CrystFEL is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * CrystFEL is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with CrystFEL. If not, see <http://www.gnu.org/licenses/>. + * + */ + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#include <stdlib.h> +#include <assert.h> +#include <math.h> +#include <stdio.h> +#include <hdf5.h> +#include <unistd.h> + +#include "image.h" +#include "utils.h" +#include "detgeom.h" + +#include "datatemplate.h" +#include "datatemplate_priv.h" + + +static void close_hdf5(hid_t fh) +{ + int n_ids, i; + hid_t ids[2048]; + + n_ids = H5Fget_obj_ids(fh, H5F_OBJ_ALL, 2048, ids); + + for ( i=0; i<n_ids; i++ ) { + + hid_t id; + H5I_type_t type; + + id = ids[i]; + + type = H5Iget_type(id); + + if ( type == H5I_GROUP ) H5Gclose(id); + if ( type == H5I_DATASET ) H5Dclose(id); + if ( type == H5I_DATATYPE ) H5Tclose(id); + if ( type == H5I_DATASPACE ) H5Sclose(id); + if ( type == H5I_ATTR ) H5Aclose(id); + + } + + H5Fclose(fh); +} + + +/* Get the path parts of the event ID + * e.g. ev_orig = abc/def/ghi//5/2/7 + * -> [abc, def, ghi], with *pn_plvals=3. + * + * Not part of public API. Not "static" for testing. */ +char **read_path_parts(const char *ev_orig, int *pn_plvals) +{ + char **plvals; + char *ev; + int n_plvals = 0; + char *start; + + plvals = malloc(MAX_PATH_PARTS*sizeof(char *)); + if ( plvals == NULL ) return NULL; + + if ( ev_orig == NULL ) { + /* No ev -> no path parts */ + *pn_plvals = 0; + return plvals; + } + + ev = strdup(ev_orig); + if ( ev == NULL ) return NULL; + + start = ev; + do { + + char *sep; + + sep = strchr(start, '/'); + + if ( sep == NULL ) { + /* This would be very strange, because it + * must at least have // */ + ERROR("Couldn't read path parts ('%s')\n", + start); + free(ev); + free(plvals); + return NULL; + } + + /* Remaining string starts with '/' is end condition */ + if ( sep == start ) break; + + if ( n_plvals == MAX_PATH_PARTS ) { + ERROR("Too many path parts: %s\n", ev_orig); + free(ev); + free(plvals); + return NULL; + } + + sep[0] = '\0'; + plvals[n_plvals++] = strdup(start); + + start = sep+1; + + } while ( 1 ); + + free(ev); + *pn_plvals = n_plvals; + return plvals; +} + + +/* Get the dimension parts of the event ID + * e.g. ev_orig = abc/def/ghi//5/2/7 + * -> [5, 2, 7], with *pn_dvals=3 + * + * Not part of public API. Not "static" for testing. */ +int *read_dim_parts(const char *ev_orig, int *pn_dvals) + +{ + char *ev; + int n_dvals = 0; + int *dvals; + char *start; + int done; + + if ( ev_orig == NULL ) ev_orig = "//"; + + /* Valid event ID? (Just the part after //, please) */ + ev = strstr(ev_orig, "//"); + if ( ev == NULL ) return NULL; + + dvals = malloc(MAX_DIMS*sizeof(int)); + if ( dvals == NULL ) return NULL; + + if ( ev[2] == '\0' ) { + /* No dimension parts - early bailout */ + *pn_dvals = 0; + return dvals; /* NB Not NULL */ + } + + ev = strdup(ev+2); + if ( ev == NULL ) return NULL; + + start = ev; + done = 0; + do { + + char *sep = strchr(start, '/'); + + if ( sep != NULL ) { + sep[0] = '\0'; + } else { + done = 1; + } + + if ( n_dvals == MAX_PATH_PARTS ) { + ERROR("Too many path parts: %s\n", ev_orig); + free(ev); + free(dvals); + return NULL; + } + + if ( start[0] == '\0' ) { + ERROR("Missing dimension: %s\n", ev_orig); + free(ev); + free(dvals); + return NULL; + } + + dvals[n_dvals++] = atoi(start); + + start = sep+1; + + } while ( !done ); + + free(ev); + *pn_dvals = n_dvals; + return dvals; +} + + +static int imh_num_path_placeholders(const char *pattern) +{ + size_t l, i; + int n_pl_exp = 0; + + l = strlen(pattern); + for ( i=0; i<l; i++ ) { + if ( pattern[i] == '%' ) n_pl_exp++; + } + return n_pl_exp; +} + + +/* ev = abc/def/ghi//5/2/7 + * pattern = /data/%/somewhere/%/%/data + * output = /data/abc/somewhere/def/ghi/data + * + * Not part of public API. Not "static" for testing. + */ +char *substitute_path(const char *ev, const char *pattern) +{ + char **plvals; + int n_plvals; + int n_pl_exp; + size_t total_len; + int i; + char *subs; + const char *start; + const char *pl_pos; + + if ( pattern == NULL ) { + ERROR("Pattern cannot be NULL\n"); + return NULL; + } + + plvals = read_path_parts(ev, &n_plvals); + if ( plvals == NULL ) return NULL; + + n_pl_exp = imh_num_path_placeholders(pattern); + + if ( n_plvals != n_pl_exp ) { + ERROR("Wrong number of path placeholders: " + "'%s' (%i) into '%s' (%i)\n", + ev, n_plvals, pattern, n_pl_exp); + return NULL; + } + + if ( n_pl_exp == 0 ) { + /* No placeholders in path */ + for ( i=0; i<n_plvals; i++ ) { + free(plvals[i]); + } + free(plvals); + return strdup(pattern); + } + + total_len = strlen(pattern) - n_pl_exp; + for ( i=0; i<n_plvals; i++ ) { + total_len += strlen(plvals[i]); + } + subs = malloc(total_len+1); + if ( subs == NULL ) { + free(plvals); + return NULL; + } + + pl_pos = strchr(pattern, '%'); + if ( pl_pos == NULL ) { + ERROR("Expected a placeholder char (%): '%s'\n", + pattern); + return NULL; + } + strncpy(subs, pattern, pl_pos-pattern); + subs[pl_pos-pattern] = '\0'; + + start = pl_pos+1; + for ( i=0; i<n_plvals; i++ ) { + + /* Add the placeholder's value */ + strcat(subs, plvals[i]); + free(plvals[i]); + + /* Add the chars up to the next placeholder... */ + pl_pos = strchr(start, '%'); + if ( pl_pos == NULL ) { + /* ... or the end */ + pl_pos = start+strlen(start); + } + strncat(subs, start, pl_pos-start); + start = pl_pos+1; + } + + free(plvals); + + return subs; +} + + +static void make_placeholder_skip(signed int *dt_dims, + signed int *panel_dims) +{ + int i; + int n_dt = 0; + for ( i=0; i<MAX_DIMS; i++ ) { + if ( panel_dims[i] != DIM_PLACEHOLDER ) { + dt_dims[n_dt++] = panel_dims[i]; + } + } +} + + +static int imh_num_placeholders(const struct panel_template *p) +{ + int i; + int n_pl = 0; + for ( i=0; i<MAX_DIMS; i++ ) { + if ( p->dims[i] == DIM_PLACEHOLDER ) n_pl++; + } + return n_pl; +} + + +static int total_dimensions(const struct panel_template *p) +{ + int i; + int n_dim = 0; + for ( i=0; i<MAX_DIMS; i++ ) { + if ( p->dims[i] != DIM_UNDEFINED ) n_dim++; + } + return n_dim; +} + + +static int load_hdf5_hyperslab(struct panel_template *p, + const char *filename, + const char *event, + void **pdata, + hid_t el_type, size_t el_size, + int skip_placeholders_ok, + const char *path_spec) +{ + hid_t fh; + int total_dt_dims; + int plh_dt_dims; + int dt_dims[MAX_DIMS]; + int n_dt_dims; + herr_t r; + hsize_t *f_offset, *f_count; + hid_t dh; + herr_t check; + hid_t dataspace, memspace; + hsize_t dims[2]; + char *panel_full_path; + void *data; + int ndims; + int dim; + int *dim_vals; + int n_dim_vals; + int pl_pos; + + if ( access(filename, R_OK) == -1 ) { + ERROR("File does not exist or cannot be read: %s\n", + filename); + return 1; + } + + fh = H5Fopen(filename, H5F_ACC_RDONLY, H5P_DEFAULT); + if ( fh < 0 ) { + ERROR("Couldn't open file: %s\n", filename); + return 1; + } + + panel_full_path = substitute_path(event, path_spec); + if ( panel_full_path == NULL ) { + ERROR("Invalid path substitution: '%s' '%s'\n", + event, path_spec); + close_hdf5(fh); + return 1; + } + + dh = H5Dopen2(fh, panel_full_path, H5P_DEFAULT); + if ( dh < 0 ) { + ERROR("Cannot open data for panel %s (%s) in file %s\n", + p->name, panel_full_path, filename); + free(panel_full_path); + close_hdf5(fh); + return 1; + } + + free(panel_full_path); + + /* Set up dataspace for file + * (determine where to read the data from) */ + dataspace = H5Dget_space(dh); + ndims = H5Sget_simple_extent_ndims(dataspace); + if ( ndims < 0 ) { + ERROR("Failed to get number of dimensions for panel %s\n", + p->name); + close_hdf5(fh); + return 1; + } + + /* Does the array have the expected number of dimensions? */ + total_dt_dims = total_dimensions(p); + plh_dt_dims = imh_num_placeholders(p); + if ( ndims != total_dt_dims ) { + /* If the dimensions match after excluding + * placeholders, it's OK - probably a static mask + * in a multi-event file. */ + if ( skip_placeholders_ok + && (ndims == total_dt_dims - plh_dt_dims) ) + { + make_placeholder_skip(dt_dims, p->dims); + n_dt_dims = total_dt_dims - plh_dt_dims; + } else { + ERROR("Unexpected number of dimensions for " + "panel %s (%i, but expected %i or %i)\n", + p->name, ndims, total_dt_dims, + total_dt_dims - plh_dt_dims); + close_hdf5(fh); + return 1; + } + } else { + int i; + for ( i=0; i<MAX_DIMS; i++ ) { + dt_dims[i] = p->dims[i]; + } + n_dt_dims = total_dt_dims; + } + + f_offset = malloc(ndims*sizeof(hsize_t)); + f_count = malloc(ndims*sizeof(hsize_t)); + if ( (f_offset == NULL) || (f_count == NULL ) ) { + ERROR("Failed to allocate offset or count.\n"); + close_hdf5(fh); + return 1; + } + + /* Get those placeholder values from the event ID */ + dim_vals = read_dim_parts(event, &n_dim_vals); + + pl_pos = 0; + for ( dim=0; dim<n_dt_dims; dim++ ) { + + switch ( dt_dims[dim] ) { + + case DIM_FS: + f_offset[dim] = p->orig_min_fs; + f_count[dim] = p->orig_max_fs - p->orig_min_fs+1; + break; + + case DIM_SS: + f_offset[dim] = p->orig_min_ss; + f_count[dim] = p->orig_max_ss - p->orig_min_ss+1; + break; + + case DIM_PLACEHOLDER: + f_offset[dim] = dim_vals[pl_pos++]; + f_count[dim] = 1; + break; + + case DIM_UNDEFINED: + ERROR("Undefined dimension found!\n"); + break; + + default: + /* Fixed value */ + f_offset[dim] = dt_dims[dim]; + f_count[dim] = 1; + break; + + } + } + + free(dim_vals); + + check = H5Sselect_hyperslab(dataspace, H5S_SELECT_SET, + f_offset, NULL, f_count, NULL); + if ( check < 0 ) { + ERROR("Error selecting file dataspace for panel %s\n", + p->name); + free(f_offset); + free(f_count); + close_hdf5(fh); + return 1; + } + + dims[0] = p->orig_max_ss - p->orig_min_ss + 1; + dims[1] = p->orig_max_fs - p->orig_min_fs + 1; + memspace = H5Screate_simple(2, dims, NULL); + + data = malloc(dims[0]*dims[1]*el_size); + if ( data == NULL ) { + ERROR("Failed to allocate panel %s\n", p->name); + free(f_offset); + free(f_count); + close_hdf5(fh); + return 1; + } + + r = H5Dread(dh, el_type, memspace, dataspace, + H5P_DEFAULT, data); + if ( r < 0 ) { + ERROR("Couldn't read data for panel %s\n", + p->name); + free(f_offset); + free(f_count); + free(data); + close_hdf5(fh); + return 1; + } + + free(f_offset); + free(f_count); + close_hdf5(fh); + + *pdata = data; + return 0; +} + + +int image_hdf5_read(struct image *image, + const DataTemplate *dtempl, + const char *filename, + const char *event) +{ + int i; + + image->dp = malloc(dtempl->n_panels*sizeof(float *)); + if ( image->dp == NULL ) { + ERROR("Failed to allocate data array.\n"); + return 1; + } + + if ( event == NULL ) { + event = "//"; + } + + /* Set all pointers to NULL for easier clean-up */ + for ( i=0; i<dtempl->n_panels; i++ ) image->dp[i] = NULL; + + for ( i=0; i<dtempl->n_panels; i++ ) { + if ( load_hdf5_hyperslab(&dtempl->panels[i], filename, + event, (void *)&image->dp[i], + H5T_NATIVE_FLOAT, + sizeof(float), 0, + dtempl->panels[i].data) ) + { + ERROR("Failed to load panel data\n"); + return 1; + } + } + + image->filename = strdup(filename); + image->ev = safe_strdup(event); + + return 0; +} + + +int image_hdf5_read_mask(struct panel_template *p, + const char *filename, const char *event, + int *bad, const char *mask_location, + int mask_good, int mask_bad) +{ + int p_w, p_h; + int *mask = NULL; + long unsigned int j; + + p_w = p->orig_max_fs - p->orig_min_fs + 1; + p_h = p->orig_max_ss - p->orig_min_ss + 1; + + if ( load_hdf5_hyperslab(p, filename, event, + (void *)&mask, H5T_NATIVE_INT, + sizeof(int), 1, mask_location) ) + { + ERROR("Failed to load mask data\n"); + free(mask); + return 1; + } + + for ( j=0; j<p_w*p_h; j++ ) { + + /* Bad if it's missing any of the "good" bits */ + if ( (mask[j] & mask_good) != mask_good ) bad[j] = 1; + + /* Bad if it has any of the "bad" bits. */ + if ( mask[j] & mask_bad ) bad[j] = 1; + + } + + free(mask); + return 0; +} + + +double image_hdf5_get_value(const char *name, const char *filename, + const char *event) +{ + hid_t dh; + hid_t type; + hid_t class; + hid_t sh; + hid_t ms; + hsize_t *f_offset = NULL; + hsize_t *f_count = NULL; + hsize_t m_offset[1]; + hsize_t m_count[1]; + hsize_t msdims[1]; + hsize_t size[64]; + herr_t r; + herr_t check; + int ndims; + int i; + char *subst_name = NULL; + hid_t fh; + double val; + int *dim_vals; + int n_dim_vals; + int dim_val_pos; + + if ( access(filename, R_OK) == -1 ) { + ERROR("File does not exist or cannot be read: %s\n", filename); + return NAN; + } + + fh = H5Fopen(filename, H5F_ACC_RDONLY, H5P_DEFAULT); + if ( fh < 0 ) { + ERROR("Couldn't open file: %s\n", filename); + return NAN; + } + + subst_name = substitute_path(event, name); + if ( subst_name == NULL ) { + ERROR("Invalid event ID '%s'\n", event); + close_hdf5(fh); + return NAN; + } + + dh = H5Dopen2(fh, subst_name, H5P_DEFAULT); + if ( dh < 0 ) { + ERROR("No such numeric field '%s'\n", subst_name); + close_hdf5(fh); + return NAN; + } + + type = H5Dget_type(dh); + class = H5Tget_class(type); + + if ( (class != H5T_FLOAT) && (class != H5T_INTEGER) ) { + ERROR("Not a floating point or integer value.\n"); + close_hdf5(fh); + return NAN; + } + + /* Get the dimensionality. We have to cope with scalars expressed as + * arrays with all dimensions 1, as well as zero-d arrays. */ + sh = H5Dget_space(dh); + ndims = H5Sget_simple_extent_ndims(sh); + if ( ndims > 64 ) { + ERROR("Too many dimensions for numeric value\n"); + close_hdf5(fh); + return NAN; + } + H5Sget_simple_extent_dims(sh, size, NULL); + + /* We want to read the value as a scalar */ + m_offset[0] = 0; + m_count[0] = 1; + msdims[0] = 1; + ms = H5Screate_simple(1, msdims, NULL); + + if ( ndims == 0 ) { + /* Easy case, because value is a scalar */ + r = H5Dread(dh, H5T_NATIVE_DOUBLE, ms, sh, H5P_DEFAULT, &val); + if ( r < 0 ) { + ERROR("Couldn't read scalar value from %s.\n", + subst_name); + free(subst_name); + close_hdf5(fh); + return NAN; + } + return val; + } + + dim_vals = read_dim_parts(event, &n_dim_vals); + if ( dim_vals == NULL ) { + ERROR("Couldn't parse event '%s'\n"); + close_hdf5(fh); + return NAN; + } + + f_offset = malloc(ndims*sizeof(hsize_t)); + f_count = malloc(ndims*sizeof(hsize_t)); + if ( (f_offset == NULL) || (f_count == NULL) ) { + ERROR("Couldn't allocate dimension arrays\n"); + close_hdf5(fh); + return NAN; + } + + /* Every dimension of the dataset must either be size 1 or + * large enough to contain the next value from the event ID */ + dim_val_pos = 0; + for ( i=0; i<ndims; i++ ) { + + if ( size[i] != 1 ) { + + if ( size[i] <= dim_vals[dim_val_pos] ) { + ERROR("Array of scalar values is too " + "small (%s, dim %i, ev value %i," + " size %i)\n", + subst_name, i, + dim_vals[dim_val_pos], size[i]); + close_hdf5(fh); + return NAN; + } + + f_offset[i] = dim_vals[dim_val_pos]; + f_count[i] = 1; + dim_val_pos++; + + } else { + + f_offset[i] = 0; + f_count[i] = 1; + + } + + } + + check = H5Sselect_hyperslab(sh, H5S_SELECT_SET, + f_offset, NULL, f_count, NULL); + if ( check <0 ) { + ERROR("Error selecting dataspace for float value\n"); + free(f_offset); + free(f_count); + close_hdf5(fh); + return NAN; + } + + ms = H5Screate_simple(1,msdims,NULL); + check = H5Sselect_hyperslab(ms, H5S_SELECT_SET, + m_offset, NULL, m_count, NULL); + if ( check < 0 ) { + ERROR("Error selecting memory dataspace for float value\n"); + free(f_offset); + free(f_count); + close_hdf5(fh); + return NAN; + } + + r = H5Dread(dh, H5T_NATIVE_DOUBLE, ms, sh, H5P_DEFAULT, &val); + if ( r < 0 ) { + ERROR("Couldn't read value.\n"); + close_hdf5(fh); + return NAN; + } + + free(f_offset); + free(f_count); + free(subst_name); + close_hdf5(fh); + + return val; +} + + +static int read_peak_count(hid_t fh, char *path, int line, + int *num_peaks) +{ + + hid_t dh, sh, mh; + hsize_t size[1]; + hsize_t max_size[1]; + hsize_t offset[1], count[1]; + hsize_t m_offset[1], m_count[1], dimmh[1]; + int tw, r; + + dh = H5Dopen2(fh, path, H5P_DEFAULT); + if ( dh < 0 ) { + ERROR("Data block %s not found.\n", path); + return 1; + } + + sh = H5Dget_space(dh); + if ( sh < 0 ) { + H5Dclose(dh); + ERROR("Couldn't get dataspace for data.\n"); + return 1; + } + + if ( H5Sget_simple_extent_ndims(sh) != 1 ) { + ERROR("Data block %s has the wrong dimensionality (%i).\n", + path, H5Sget_simple_extent_ndims(sh)); + H5Sclose(sh); + H5Dclose(dh); + return 1; + } + + H5Sget_simple_extent_dims(sh, size, max_size); + + tw = size[0]; + + if ( line > tw-1 ) { + H5Sclose(sh); + H5Dclose(dh); + ERROR("Data block %s does not contain data for required event.\n", + path); + return 1; + } + + offset[0] = line; + count[0] = 1; + + r = H5Sselect_hyperslab(sh, H5S_SELECT_SET, + offset, NULL, count, NULL); + if ( r < 0 ) { + ERROR("Error selecting file dataspace " + "for data block %s\n", path); + H5Dclose(dh); + H5Sclose(sh); + return 1; + } + + m_offset[0] = 0; + m_count[0] = 1; + dimmh[0] = 1; + mh = H5Screate_simple(1, dimmh, NULL); + r = H5Sselect_hyperslab(mh, H5S_SELECT_SET, + m_offset, NULL, m_count, NULL); + if ( r < 0 ) { + ERROR("Error selecting memory dataspace " + "for data block %s\n", path); + H5Dclose(dh); + H5Sclose(sh); + H5Sclose(mh); + return 1; + } + + r = H5Dread(dh, H5T_NATIVE_INT, mh, + sh, H5P_DEFAULT, num_peaks); + if ( r < 0 ) { + ERROR("Couldn't read data for block %s, line %i\n", path, line); + H5Dclose(dh); + H5Sclose(sh); + H5Sclose(mh); + return 1; + } + + H5Dclose(dh); + H5Sclose(sh); + H5Sclose(mh); + return 0; +} + + +static float *read_peak_line(hid_t fh, char *path, int line) +{ + + hid_t dh, sh, mh; + hsize_t size[2]; + hsize_t max_size[2]; + hsize_t offset[2], count[2]; + hsize_t m_offset[2], m_count[2], dimmh[2]; + float *buf; + int tw, r; + + dh = H5Dopen2(fh, path, H5P_DEFAULT); + if ( dh < 0 ) { + ERROR("Data block (%s) not found.\n", path); + return NULL; + } + + sh = H5Dget_space(dh); + if ( sh < 0 ) { + H5Dclose(dh); + ERROR("Couldn't get dataspace for data.\n"); + return NULL; + } + + if ( H5Sget_simple_extent_ndims(sh) != 2 ) { + ERROR("Data block %s has the wrong dimensionality (%i).\n", + path, H5Sget_simple_extent_ndims(sh)); + H5Sclose(sh); + H5Dclose(dh); + return NULL; + } + + H5Sget_simple_extent_dims(sh, size, max_size); + + tw = size[0]; + if ( line> tw-1 ) { + H5Sclose(sh); + H5Dclose(dh); + ERROR("Data block %s does not contain data for required event.\n", + path); + return NULL; + } + + offset[0] = line; + offset[1] = 0; + count[0] = 1; + count[1] = size[1]; + + r = H5Sselect_hyperslab(sh, H5S_SELECT_SET, offset, NULL, count, NULL); + if ( r < 0 ) { + ERROR("Error selecting file dataspace " + "for data block %s\n", path); + H5Dclose(dh); + H5Sclose(sh); + return NULL; + } + + m_offset[0] = 0; + m_offset[1] = 0; + m_count[0] = 1; + m_count[1] = size[1]; + dimmh[0] = 1; + dimmh[1] = size[1]; + + mh = H5Screate_simple(2, dimmh, NULL); + r = H5Sselect_hyperslab(mh, H5S_SELECT_SET, + m_offset, NULL, m_count, NULL); + if ( r < 0 ) { + ERROR("Error selecting memory dataspace " + "for data block %s\n", path); + H5Dclose(dh); + H5Sclose(sh); + H5Sclose(mh); + return NULL; + } + + buf = malloc(size[1]*sizeof(float)); + if ( buf == NULL ) return NULL; + r = H5Dread(dh, H5T_NATIVE_FLOAT, mh, sh, H5P_DEFAULT, buf); + if ( r < 0 ) { + ERROR("Couldn't read data for block %s, line %i\n", path, line); + H5Dclose(dh); + H5Sclose(sh); + H5Sclose(mh); + return NULL; + } + + H5Dclose(dh); + H5Sclose(sh); + H5Sclose(mh); + return buf; +} + + +ImageFeatureList *image_hdf5_read_peaks_cxi(const DataTemplate *dtempl, + const char *filename, + const char *event, + int half_pixel_shift) +{ + ImageFeatureList *features; + hid_t fh; + char path_n[1024]; + char path_x[1024]; + char path_y[1024]; + char path_i[1024]; + int r; + int pk; + char *subst_name; + int line; + int num_peaks; + float *buf_x; + float *buf_y; + float *buf_i; + int *dim_vals; + int n_dim_vals; + + double peak_offset = half_pixel_shift ? 0.5 : 0.0; + + if ( access(filename, R_OK) == -1 ) { + ERROR("File does not exist or cannot be read: %s\n", + filename); + return NULL; + } + + subst_name = substitute_path(event, dtempl->peak_list); + if ( subst_name == NULL ) { + ERROR("Invalid peak path %s\n", subst_name); + return NULL; + } + + dim_vals = read_dim_parts(event, &n_dim_vals); + if ( dim_vals == NULL ) { + ERROR("Couldn't parse event '%s'\n"); + return NULL; + } + + if ( n_dim_vals < 1 ) { + ERROR("Not enough dimensions in event ID to use CXI " + "peak lists (%i)\n", n_dim_vals); + return NULL; + } + + line = dim_vals[0]; + free(dim_vals); + + snprintf(path_n, 1024, "%s/nPeaks", subst_name); + snprintf(path_x, 1024, "%s/peakXPosRaw", subst_name); + snprintf(path_y, 1024, "%s/peakYPosRaw", subst_name); + snprintf(path_i, 1024, "%s/peakTotalIntensity", subst_name); + + fh = H5Fopen(filename, H5F_ACC_RDONLY, H5P_DEFAULT); + if ( fh < 0 ) { + ERROR("Couldn't open file: %s\n", filename); + return NULL; + } + + r = read_peak_count(fh, path_n, line, &num_peaks); + if ( r != 0 ) { + close_hdf5(fh); + return NULL; + } + + buf_x = read_peak_line(fh, path_x, line); + if ( r != 0 ) { + close_hdf5(fh); + return NULL; + } + + buf_y = read_peak_line(fh, path_y, line); + if ( r != 0 ) { + close_hdf5(fh); + return NULL; + } + + buf_i = read_peak_line(fh, path_i, line); + if ( r != 0 ) { + close_hdf5(fh); + return NULL; + } + + features = image_feature_list_new(); + + for ( pk=0; pk<num_peaks; pk++ ) { + + float fs, ss, val; + int pn; + + fs = buf_x[pk] + peak_offset; + ss = buf_y[pk] + peak_offset; + val = buf_i[pk]; + + if ( data_template_file_to_panel_coords(dtempl, + &fs, &ss, + &pn) ) + { + ERROR("Failed to convert %i,%i to " + "panel-relative coordinates\n", fs, ss); + } else { + image_add_feature(features, fs, ss, pn, + NULL, val, NULL); + } + + } + + close_hdf5(fh); + + return features; +} + + +ImageFeatureList *image_hdf5_read_peaks_hdf5(const DataTemplate *dtempl, + const char *filename, + const char *event, + int half_pixel_shift) +{ + hid_t fh, dh, sh; + hsize_t size[2]; + hsize_t max_size[2]; + int i; + float *buf; + herr_t r; + int tw; + char *subst_name; + ImageFeatureList *features; + double peak_offset = half_pixel_shift ? 0.5 : 0.0; + + if ( dtempl->peak_list == NULL ) { + ERROR("Peak location is not given in geometry file.\n"); + return NULL; + } + + if ( access(filename, R_OK) == -1 ) { + ERROR("File does not exist or cannot be read: %s\n", + filename); + return NULL; + } + + fh = H5Fopen(filename, H5F_ACC_RDONLY, H5P_DEFAULT); + if ( fh < 0 ) { + ERROR("Couldn't open file: %s\n", filename); + return NULL; + } + + subst_name = substitute_path(event, dtempl->peak_list); + if ( subst_name == NULL ) { + ERROR("Invalid peak path: '%s' '%s'\n", + event, dtempl->peak_list); + close_hdf5(fh); + return NULL; + } + + dh = H5Dopen2(fh, subst_name, H5P_DEFAULT); + if ( dh < 0 ) { + ERROR("Peak list (%s) not found.\n", subst_name); + free(subst_name); + close_hdf5(fh); + return NULL; + } + free(subst_name); + + sh = H5Dget_space(dh); + if ( sh < 0 ) { + ERROR("Couldn't get dataspace for peak list.\n"); + close_hdf5(fh); + return NULL; + } + + if ( H5Sget_simple_extent_ndims(sh) != 2 ) { + ERROR("Peak list has the wrong dimensionality (%i).\n", + H5Sget_simple_extent_ndims(sh)); + close_hdf5(fh); + return NULL; + } + + H5Sget_simple_extent_dims(sh, size, max_size); + H5Sclose(sh); + + tw = size[1]; + if ( (tw != 3) && (tw != 4) ) { + ERROR("Peak list has the wrong dimensions.\n"); + close_hdf5(fh); + return NULL; + } + + buf = malloc(sizeof(float)*size[0]*size[1]); + if ( buf == NULL ) { + ERROR("Couldn't reserve memory for the peak list.\n"); + close_hdf5(fh); + return NULL; + } + r = H5Dread(dh, H5T_NATIVE_FLOAT, H5S_ALL, H5S_ALL, + H5P_DEFAULT, buf); + if ( r < 0 ) { + ERROR("Couldn't read peak list.\n"); + close_hdf5(fh); + return NULL; + } + + features = image_feature_list_new(); + if ( features == NULL ) { + ERROR("Failed to allocate peak list\n"); + close_hdf5(fh); + return NULL; + } + + for ( i=0; i<size[0]; i++ ) { + + float fs, ss, val; + int pn; + + fs = buf[tw*i+0] + peak_offset; + ss = buf[tw*i+1] + peak_offset; + val = buf[tw*i+2]; + + if ( data_template_file_to_panel_coords(dtempl, + &fs, &ss, + &pn) ) + { + ERROR("Failed to convert %i,%i to " + "panel-relative coordinates\n", fs, ss); + } else { + image_add_feature(features, fs, ss, pn, + NULL, val, NULL); + } + + } + + free(buf); + close_hdf5(fh); + + return features; +} + + +/* This could be extended, later, to include patterns other than just + * a literal string (no placeholders) and just %. However, pattern + * matching is in general not that easy. */ +static char *matches_pattern(const char *name, const char *pattern, + const char *ev_str_old) +{ + if ( strcmp(pattern, "%") == 0 ) { + char *nstr = malloc(strlen(ev_str_old)+strlen(name)+2); + if ( nstr == NULL ) { + ERROR("Couldn't allocate memory\n"); + return NULL; + } + strcpy(nstr, ev_str_old); + strcat(nstr, "/"); + strcat(nstr, name); + return nstr; + } else { + if ( strcmp(name, pattern) == 0 ) { + return strdup(ev_str_old); + } else { + return NULL; + } + } +} + + +/* Private structure, just to avoid passing char *** around */ +struct ev_list +{ + char **events; + int n_events; + int max_events; +}; + + +static int add_ev_to_list(struct ev_list *list, char *ev_str) +{ + if ( list->n_events == list->max_events ) { + char **new_events = realloc(list->events, + (list->max_events+128)*sizeof(char *)); + if ( new_events == NULL ) return 1; + list->max_events += 128; + list->events = new_events; + } + + list->events[list->n_events++] = strdup(ev_str); + + return 0; +} + + +static char *demunge_event(const char *orig) +{ + size_t len = strlen(orig); + char *slash; + + if ( len == 0 ) return strdup("//"); + + slash = malloc(len+3); + if ( slash == NULL ) return NULL; + strcpy(slash, orig+1); + strcat(slash, "//"); + return slash; +} + + +static int rec_expand_paths(hid_t gh, struct ev_list *list, + const char *ev_str, + char **pattern_bits, int n_pattern_bits) +{ + int i; + H5G_info_t group_info; + + if ( H5Gget_info(gh, &group_info) < 0 ) { + ERROR("Couldn't get group info\n"); + return 1; + } + + for ( i=0; i<group_info.nlinks; i++ ) { + + ssize_t size; + char *name; + H5O_info_t obj_info; + char *ev_str_new; + + size = H5Lget_name_by_idx(gh, ".", H5_INDEX_NAME, + H5_ITER_INC, i, NULL, 0, + H5P_DEFAULT); + if ( (size < 0) || (size > 20000) ) { + ERROR("Couldn't get link name\n"); + return 1; + } + + name = malloc(size+1); + if ( name == NULL ) { + ERROR("Couldn't allocate memory\n"); + return 1; + } + + if ( H5Lget_name_by_idx(gh, ".", H5_INDEX_NAME, + H5_ITER_INC, i, name, size+1, + H5P_DEFAULT) < 0 ) + { + ERROR("Couldn't get name\n"); + return 1; + } + + ev_str_new = matches_pattern(name, pattern_bits[0], + ev_str); + if ( ev_str_new == NULL ) { + free(name); + continue; + } + + if ( H5Oget_info_by_idx(gh, ".", H5_INDEX_NAME, + H5_ITER_INC, i, &obj_info, 0) < 0 ) + { + ERROR("Couldn't get info\n"); + free(name); + free(ev_str_new); + return 1; + } + + if ( obj_info.type == H5O_TYPE_GROUP ) { + + hid_t child_gh; + + if ( n_pattern_bits == 1 ) { + ERROR("Pattern doesn't match file" + " (too short)\n"); + free(name); + free(ev_str_new); + return 1; + } + + child_gh = H5Gopen1(gh, name); + if ( child_gh < 0 ) { + ERROR("Couldn't open '%s'\n", name); + free(name); + free(ev_str_new); + return 1; + } + + if ( rec_expand_paths(child_gh, list, + ev_str_new, + &pattern_bits[1], + n_pattern_bits - 1) ) + { + free(name); + free(ev_str_new); + return 1; + } + + free(ev_str_new); + H5Gclose(child_gh); + + } else if ( obj_info.type == H5O_TYPE_DATASET ) { + + char *addme; + + if ( n_pattern_bits != 1 ) { + ERROR("Pattern doesn't match file" + " (too long by %i)\n", + n_pattern_bits); + free(name); + free(ev_str_new); + return 1; + } + + addme = demunge_event(ev_str_new); + if ( addme != NULL ) { + add_ev_to_list(list, addme); + free(addme); + } + free(ev_str_new); + + } + + free(name); + + } + + return 0; +} + + +/* Not "static" so that ev_enumX can test it. + * Not part of public API! */ +char **expand_paths(hid_t fh, char *pattern, int *n_evs) +{ + int n_sep; + size_t len; + char **pattern_bits; + struct ev_list list; + int i; + char *start; + + if ( pattern == NULL ) return NULL; + if ( pattern[0] != '/' ) return NULL; + + /* Chop up the pattern into path bits */ + len = strlen(pattern); + n_sep = 0; + for ( i=0; i<len; i++ ) { + if ( pattern[i] == '/' ) n_sep++; + } + + pattern_bits = malloc(n_sep*sizeof(char *)); + if ( pattern_bits == NULL ) return NULL; + + start = pattern+1; + for ( i=0; i<n_sep; i++ ) { + char *sep = strchr(start, '/'); + if ( sep == NULL ) { + sep = start+strlen(start); + } + pattern_bits[i] = strndup(start, sep-start); + if ( pattern_bits[i] == NULL ) return NULL; + start = sep+1; + } + + list.n_events = 0; + list.max_events = 0; + list.events = NULL; + + rec_expand_paths(fh, &list, "", pattern_bits, n_sep); + + for ( i=0; i<n_sep; i++ ) { + free(pattern_bits[i]); + } + free(pattern_bits); + + *n_evs = list.n_events; + return list.events; +} + + +static int rec_expand_dims(struct ev_list *list, + int *placeholder_sizes, + int n_placeholder_dims, + char *path_ev) +{ + int i; + char *dim_ev; + size_t len; + + len = strlen(path_ev); + dim_ev = malloc(len+16); + if ( dim_ev == NULL ) return 1; + + if ( n_placeholder_dims == 1 ) { + for ( i=0; i<placeholder_sizes[0]; i++ ) { + snprintf(dim_ev, 16, "%s/%i", path_ev, i); + if ( add_ev_to_list(list, dim_ev) ) return 1; + } + } else { + + for ( i=0; i<placeholder_sizes[0]; i++ ) { + snprintf(dim_ev, 16, "%s/%i", path_ev, i); + if ( rec_expand_dims(list, + &placeholder_sizes[1], + n_placeholder_dims - 1, + dim_ev) ) return 1; + } + + } + + free(dim_ev); + return 0; +} + + +static char **expand_dims(int *placeholder_sizes, + int n_placeholder_dims, + char *path_ev, + int *n_evs) +{ + struct ev_list list; + + list.n_events = 0; + list.max_events = 0; + list.events = NULL; + + if ( rec_expand_dims(&list, placeholder_sizes, + n_placeholder_dims, path_ev) ) + { + *n_evs = 0; + return NULL; + } + + *n_evs = list.n_events; + return list.events; +} + + +static int n_dims_expected(struct panel_template *p) +{ + int i; + int n_dims = 0; + for ( i=0; i<MAX_DIMS; i++ ) { + if ( p->dims[i] != DIM_UNDEFINED ) n_dims++; + } + return n_dims; +} + + +char **image_hdf5_expand_frames(const DataTemplate *dtempl, + const char *filename, + int *pn_frames) +{ + char **path_evs; + int n_path_evs; + hid_t fh; + int i; + int dims_expected; + struct ev_list full_evs; + + if ( dtempl->n_panels == 0 ) return NULL; + + full_evs.events = NULL; + full_evs.n_events = 0; + full_evs.max_events = 0; + + /* If the DataTemplate already says that one frame will be + * found per file, short-circuit this whole affair */ + if ( (imh_num_placeholders(&dtempl->panels[0]) == 0) + && (imh_num_path_placeholders(dtempl->panels[0].data) == 0) ) + { + add_ev_to_list(&full_evs, "//"); + *pn_frames = full_evs.n_events; + return full_evs.events; + } + + + fh = H5Fopen(filename, H5F_ACC_RDONLY, H5P_DEFAULT); + if ( fh < 0 ) { + ERROR("Couldn't open file '%s'\n", filename); + return NULL; + } + + /* First, expand placeholders in the HDF5 paths. + * + * Since we require the number of placeholders to be the same + * for all panels, and the placeholders will be substituted + * with the same values for each panel (since they come from + * the same event ID), this only needs to be done for the + * first panel. */ + path_evs = expand_paths(fh, dtempl->panels[0].data, + &n_path_evs); + if ( path_evs == NULL ) { + ERROR("Failed to enumerate paths.\n"); + close_hdf5(fh); + return NULL; + } + + dims_expected = n_dims_expected(&dtempl->panels[0]); + + /* For each expanded path, enumerate the placeholder + * dimensions. Once again, since the number of placeholders + * must be the same for each panel, and the substituted values + * will be the same, this only needs to be done for one panel. + */ + for ( i=0; i<n_path_evs; i++ ) { + + hid_t dh, sh; + char *path; + hsize_t *size; + int dims; + int *placeholder_sizes; + int n_placeholder_dims; + int j; + struct panel_template *p = &dtempl->panels[0]; + + path = substitute_path(path_evs[i], p->data); + if ( path == NULL ) { + ERROR("Path substitution failed during " + "expansion of '%s' with partial event " + "ID '%s'\n", + p->data, path_evs[i]); + return NULL; + } + + dh = H5Dopen2(fh, path, H5P_DEFAULT); + if ( dh < 0 ) { + ERROR("Error opening '%s'\n", path); + ERROR("Failed to enumerate events. " + "Check your geometry file.\n"); + close_hdf5(fh); + return NULL; + } + + sh = H5Dget_space(dh); + dims = H5Sget_simple_extent_ndims(sh); + if ( dims != dims_expected ) { + ERROR("Unexpected number of dimensions" + "(%s has %i, expected %i)\n", + path, dims, dims_expected); + close_hdf5(fh); + return NULL; + } + + size = malloc(dims*sizeof(hsize_t)); + placeholder_sizes = malloc(dims*sizeof(int)); + if ( (size == NULL) || (placeholder_sizes == NULL) ) { + ERROR("Failed to allocate dimensions\n"); + close_hdf5(fh); + return NULL; + } + + if ( H5Sget_simple_extent_dims(sh, size, NULL) < 0 ) { + ERROR("Failed to get size\n"); + close_hdf5(fh); + return NULL; + } + + n_placeholder_dims = 0; + for ( j=0; j<dims; j++ ) { + if ( p->dims[j] == DIM_PLACEHOLDER ) { + placeholder_sizes[n_placeholder_dims++] = size[j]; + } + } + free(size); + + /* Path event ID ends with //, but expand_dims will + * add a slash. So, remove one slash */ + if ( n_placeholder_dims > 0 ) { + + char **evs_this_path; + int n_evs_this_path; + + path_evs[i][strlen(path_evs[i])-1] = '\0'; + evs_this_path = expand_dims(placeholder_sizes, + n_placeholder_dims, + path_evs[i], + &n_evs_this_path); + + for ( j=0; j<n_evs_this_path; j++ ) { + add_ev_to_list(&full_evs, evs_this_path[j]); + free(evs_this_path[j]); + } + + free(evs_this_path); + + } else { + + /* Easy case with no dims to expand */ + add_ev_to_list(&full_evs, path_evs[i]); + + } + + free(placeholder_sizes); + free(path); + free(path_evs[i]); + + } + + close_hdf5(fh); + free(path_evs); + *pn_frames = full_evs.n_events; + return full_evs.events; +} + + +int is_hdf5_file(const char *filename) +{ + const char *ext = NULL; + + ext = filename_extension(filename, NULL); + if ( ext == NULL ) return 0; + + return ( (strcmp(ext, ".h5") == 0) + || (strcmp(ext, ".cxi") == 0) ); +} + + +/***************************** Writing *****************************/ + +struct hdf5_write_location { + + const char *location; + int n_panels; + int *panel_idxs; + + int max_ss; + int max_fs; + +}; + + +static void add_panel_to_location(struct hdf5_write_location *loc, + struct panel_template *p, int pi) +{ + int *new_panel_idxs; + + new_panel_idxs = realloc(loc->panel_idxs, + (loc->n_panels+1)*sizeof(int)); + if ( new_panel_idxs == NULL ) { + ERROR("Error while managing write location list.\n"); + return; + } + loc->panel_idxs = new_panel_idxs; + loc->panel_idxs[loc->n_panels] = pi; + loc->n_panels += 1; + if ( p->orig_max_fs > loc->max_fs ) { + loc->max_fs = p->orig_max_fs; + } + if ( p->orig_max_ss > loc->max_ss ) { + loc->max_ss = p->orig_max_ss; + } +} + + +static void add_panel_location(struct panel_template *p, + const char *p_location, int pi, + struct hdf5_write_location **plocations, + int *pnum_locations) +{ + int li; + int num_locations = *pnum_locations; + struct hdf5_write_location *locations = *plocations; + int done = 0; + + /* Does this HDF5 path already exist in the location list? + * If so, add the new panel to it (with a unique index, we hope) */ + for ( li=0; li<num_locations; li++ ) { + if ( strcmp(p_location, locations[li].location) == 0 ) { + add_panel_to_location(&locations[li], p, pi); + done = 1; + } + } + + /* If not, add a new location to ths list */ + if ( !done ) { + + struct hdf5_write_location *new_locations; + size_t nsz; + + nsz = (num_locations+1)*sizeof(struct hdf5_write_location); + new_locations = realloc(locations, nsz); + if ( new_locations == NULL ) { + ERROR("Failed to grow location list.\n"); + return; + } + locations = new_locations; + + locations[num_locations].max_ss = p->orig_max_ss; + locations[num_locations].max_fs = p->orig_max_fs; + locations[num_locations].location = p_location; + locations[num_locations].panel_idxs = malloc(sizeof(int)); + if ( locations[num_locations].panel_idxs == NULL ) { + ERROR("Failed to allocate single idx (!)\n"); + return; + } + locations[num_locations].panel_idxs[0] = pi; + locations[num_locations].n_panels = 1; + + num_locations += 1; + + } + + *plocations = locations; + *pnum_locations = num_locations; +} + + +static struct hdf5_write_location *make_location_list(const DataTemplate *dtempl, + int *pnum_locations) +{ + int pi; + struct hdf5_write_location *locations = NULL; + int num_locations = 0; + + for ( pi=0; pi<dtempl->n_panels; pi++ ) { + + struct panel_template *p; + const char *p_location; + + p = &dtempl->panels[pi]; + + assert(p->data != NULL); + p_location = p->data; + + add_panel_location(p, p_location, pi, + &locations, &num_locations); + + } + + *pnum_locations = num_locations; + return locations; +} + + +static void write_location(hid_t fh, const DataTemplate *dtempl, + float **dp, + struct hdf5_write_location *loc) +{ + hid_t sh, dh, ph; + hid_t dh_dataspace; + hsize_t size[2]; + int pi; + + /* Note the "swap" here, according to section 3.2.5, + * "C versus Fortran Dataspaces", of the HDF5 user's guide. */ + size[0] = loc->max_ss+1; + size[1] = loc->max_fs+1; + sh = H5Screate_simple(2, size, NULL); + + ph = H5Pcreate(H5P_LINK_CREATE); + H5Pset_create_intermediate_group(ph, 1); + + dh = H5Dcreate2(fh, loc->location, H5T_NATIVE_FLOAT, sh, + ph, H5P_DEFAULT, H5P_DEFAULT); + if ( dh < 0 ) { + ERROR("Couldn't create dataset\n"); + H5Fclose(fh); + return; + } + + H5Sget_simple_extent_dims(sh, size, NULL); + + for ( pi=0; pi<loc->n_panels; pi++ ) { + + hsize_t f_offset[2], f_count[2], dims[2]; + hid_t memspace; + struct panel_template *p; + int r; + + p = &dtempl->panels[loc->panel_idxs[pi]]; + + f_offset[0] = p->orig_min_ss; + f_offset[1] = p->orig_min_fs; + f_count[0] = p->orig_max_ss - p->orig_min_ss +1; + f_count[1] = p->orig_max_fs - p->orig_min_fs +1; + + dh_dataspace = H5Dget_space(dh); + r = H5Sselect_hyperslab(dh_dataspace, H5S_SELECT_SET, + f_offset, NULL, f_count, NULL); + if ( r < 0 ) { + ERROR("Error selecting file dataspace " + "for panel %s\n", p->name); + H5Pclose(ph); + H5Dclose(dh); + H5Sclose(dh_dataspace); + H5Sclose(sh); + H5Fclose(fh); + return; + } + + dims[0] = PANEL_HEIGHT(p); + dims[1] = PANEL_WIDTH(p); + memspace = H5Screate_simple(2, dims, NULL); + + r = H5Dwrite(dh, H5T_NATIVE_FLOAT, memspace, dh_dataspace, + H5P_DEFAULT, dp[loc->panel_idxs[pi]]); + if ( r < 0 ) { + ERROR("Couldn't write data\n"); + H5Pclose(ph); + H5Dclose(dh); + H5Sclose(dh_dataspace); + H5Sclose(memspace); + H5Sclose(sh); + H5Fclose(fh); + return; + } + + H5Sclose(dh_dataspace); + H5Sclose(memspace); + } + H5Pclose(ph); + H5Sclose(sh); + H5Dclose(dh); +} + + +static void write_wavelength(hid_t fh, double wl, + const DataTemplate *dtempl) +{ + hid_t ph, sh, dh; + hsize_t size1d[1]; + int r; + + ph = H5Pcreate(H5P_LINK_CREATE); + H5Pset_create_intermediate_group(ph, 1); + + size1d[0] = 1; + sh = H5Screate_simple(1, size1d, NULL); + + dh = H5Dcreate2(fh, "/wavelength", H5T_NATIVE_DOUBLE, sh, + ph, H5S_ALL, H5P_DEFAULT); + if ( dh < 0 ) { + ERROR("Couldn't create dataset for photon energy.\n"); + return; + } + r = H5Dwrite(dh, H5T_NATIVE_DOUBLE, H5S_ALL, H5S_ALL, + H5P_DEFAULT, &wl); + if ( r < 0 ) { + ERROR("Couldn't write photon energy.\n"); + /* carry on */ + } + + H5Pclose(ph); + H5Dclose(dh); +} + + +static void write_spectrum(hid_t fh, Spectrum *s, + const DataTemplate *dtempl) +{ + herr_t r; + double *arr; + int i; + 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(n*sizeof(double)); + if ( arr == NULL ) { + ERROR("Failed to allocate memory for spectrum.\n"); + return; + } + + /* 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); + } + + sh = H5Screate_simple(1, &n, NULL); + + dh = H5Dcreate2(fh, "/spectrum/wavelengths_A", H5T_NATIVE_DOUBLE, + sh, ph, H5S_ALL, H5P_DEFAULT); + if ( dh < 0 ) { + ERROR("Failed to create dataset for spectrum wavelengths.\n"); + return; + } + r = H5Dwrite(dh, H5T_NATIVE_DOUBLE, H5S_ALL, + H5S_ALL, H5P_DEFAULT, arr); + if ( r < 0 ) { + ERROR("Failed to write spectrum wavelengths.\n"); + return; + } + H5Dclose(dh); + + /* 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/pdf", H5T_NATIVE_DOUBLE, sh, + H5P_DEFAULT, H5S_ALL, H5P_DEFAULT); + if ( dh < 0 ) { + 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 p.d.f.\n"); + return; + } + + H5Dclose(dh); + H5Pclose(ph); + free(arr); +} + + +int image_hdf5_write(const struct image *image, + const DataTemplate *dtempl, + const char *filename) +{ + hid_t fh; + int li; + struct hdf5_write_location *locations; + int num_locations; + + if ( dtempl == NULL ) return 1; + + fh = H5Fcreate(filename, H5F_ACC_TRUNC, H5P_DEFAULT, H5P_DEFAULT); + if ( fh < 0 ) { + ERROR("Couldn't create file: %s\n", filename); + return 1; + } + + locations = make_location_list(dtempl, &num_locations); + + for ( li=0; li<num_locations; li++ ) { + write_location(fh, dtempl, image->dp, &locations[li]); + } + + write_wavelength(fh, image->lambda, dtempl); + + if ( image->spectrum != NULL ) { + write_spectrum(fh, image->spectrum, dtempl); + } + + H5Fclose(fh); + for ( li=0; li<num_locations; li ++ ) { + free(locations[li].panel_idxs); + } + free(locations); + return 0; +} diff --git a/libcrystfel/src/image-hdf5.h b/libcrystfel/src/image-hdf5.h new file mode 100644 index 00000000..efb8a3b7 --- /dev/null +++ b/libcrystfel/src/image-hdf5.h @@ -0,0 +1,72 @@ +/* + * image-hdf5.h + * + * Image loading, HDF5 parts + * + * Copyright © 2012-2021 Deutsches Elektronen-Synchrotron DESY, + * a research centre of the Helmholtz Association. + * + * Authors: + * 2020 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/>. + * + */ + +/* NB This file is NOT part of the public API, and should NOT + * be installed, but rather stays in the libcrystfel source folder. */ + +#ifndef IMAGE_HDF5_H +#define IMAGE_HDF5_H + +#include "datatemplate_priv.h" + +extern double image_hdf5_get_value(const char *from, + const char *filename, + const char *ev); + +extern int image_hdf5_read(struct image *image, + const DataTemplate *dtempl, + const char *filename, + const char *event); + +extern int image_hdf5_read_mask(struct panel_template *p, + const char *filename, + const char *event, int *bad, + const char *mask_location, + int mask_good, int mask_bad); + +extern ImageFeatureList *image_hdf5_read_peaks_cxi(const DataTemplate *dtempl, + const char *filename, + const char *event, + int half_pixel_shift); + +extern ImageFeatureList *image_hdf5_read_peaks_hdf5(const DataTemplate *dtempl, + const char *filename, + const char *event, + int half_pixel_shift); + +extern char **image_hdf5_expand_frames(const DataTemplate *dtempl, + const char *filename, + int *n_frames); + +extern int is_hdf5_file(const char *filename); + +extern int image_hdf5_write(const struct image *image, + const DataTemplate *dtempl, + const char *filename); + +#endif /* IMAGE_HDF5_H */ diff --git a/libcrystfel/src/image-msgpack.c b/libcrystfel/src/image-msgpack.c new file mode 100644 index 00000000..420ecfb4 --- /dev/null +++ b/libcrystfel/src/image-msgpack.c @@ -0,0 +1,315 @@ +/* + * image-msgpack.c + * + * Image loading, MessagePack parts + * + * Copyright © 2017-2021 Deutsches Elektronen-Synchrotron DESY, + * a research centre of the Helmholtz Association. + * + * Authors: + * 2018-2020 Thomas White <taw@physics.org> + * 2014 Valerio Mariani + * 2017 Stijn de Graaf + * + * 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 <stdint.h> +#include <assert.h> +#include <unistd.h> +#include <zmq.h> +#include <msgpack.h> + +#include <image.h> +#include <utils.h> +#include <msgpack.h> + +#include "datatemplate_priv.h" + + +static msgpack_object *find_msgpack_kv(msgpack_object *obj, const char *key) +{ + int i; + + if ( obj == NULL ) return NULL; + if ( obj->type != MSGPACK_OBJECT_MAP ) return NULL; + + for ( i=0; i<obj->via.map.size; i++ ) { + const char *kstr; + size_t klen; + assert(obj->via.map.ptr[i].key.type == MSGPACK_OBJECT_STR); + kstr = obj->via.map.ptr[i].key.via.str.ptr; + klen = obj->via.map.ptr[i].key.via.str.size; + if ( strncmp(kstr, key, klen) == 0 ) { + return &obj->via.map.ptr[i].val; + } + } + return NULL; +} + + +/** + * image_msgpack_read_peaks + * @dtempl: A %DataTemplate + * @obj: A %msgpack_object containing data + * @half_pixel_shift: Non-zero if 0.5 should be added to all peak coordinates + * + * Get peaks from msgpack_object. The data should be in a map, with the value + * given by "peak_list" as an array of arrays. The first of these should contain + * the list of fs positions of the peaks, the second the ss positions, and the + * third the intensities of the peaks. + * + * http://c.msgpack.org/c/ provides documentation on msgpack objects + * + * CrystFEL considers all peak locations to be distances from the corner of the + * detector panel, in pixel units, consistent with its description of detector + * 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. + * + * Returns: a newly-allocated %ImageFeatureList. + * + */ +ImageFeatureList *image_msgpack_read_peaks(const DataTemplate *dtempl, + msgpack_object *obj, + int half_pixel_shift) +{ + ImageFeatureList *features; + int num_peaks; + int pk; + msgpack_object *peak_list; + msgpack_object *peak_x; + msgpack_object *peak_y; + msgpack_object *peak_i; + double peak_offset = half_pixel_shift ? 0.5 : 0.0; + + if ( obj == NULL ) { + ERROR("No MessagePack object to get peaks from.\n"); + return NULL; + } + + /* Object has structure: + * { + * "peak_list": [[peak_x], [peak_y], [peak_i]] + * "key2":val2, + * ... + * } + */ + peak_list = find_msgpack_kv(obj, "peak_list"); + peak_x = &peak_list->via.array.ptr[0]; + peak_y = &peak_list->via.array.ptr[1]; + peak_i = &peak_list->via.array.ptr[2]; + + /* Length of peak_x array gives number of peaks */ + num_peaks = peak_x->via.array.size; + + features = image_feature_list_new(); + + for ( pk=0; pk<num_peaks; pk++ ) { + + float fs, ss, val; + int pn; + + /* Retrieve data from peak_list and apply half_pixel_shift, + * if appropriate */ + fs = peak_x->via.array.ptr[pk].via.f64 + peak_offset; + ss = peak_y->via.array.ptr[pk].via.f64 + peak_offset; + val = peak_i->via.array.ptr[pk].via.f64; + + /* Convert coordinates to panel-relative */ + if ( data_template_file_to_panel_coords(dtempl, &fs, &ss, &pn) ) { + ERROR("Peak not in panel!\n"); + } else { + image_add_feature(features, fs, ss, pn, + NULL, val, NULL); + } + } + + return features; +} + + +static int unpack_slab(struct image *image, + const DataTemplate *dtempl, + double *data, + int data_width, int data_height) +{ + int pi; + + image->dp = malloc(dtempl->n_panels*sizeof(float *)); + if ( image->dp == NULL ) { + ERROR("Failed to allocate data arrays.\n"); + return 1; + } + + for ( pi=0; pi<dtempl->n_panels; pi++ ) { + + struct panel_template *p; + int fs, ss; + int p_w, p_h; + + p = &dtempl->panels[pi]; + p_w = p->orig_max_fs - p->orig_min_fs + 1; + p_h = p->orig_max_ss - p->orig_min_ss + 1; + + image->dp[pi] = malloc(p_w*p_h*sizeof(float)); + if ( image->dp[pi] == NULL ) { + ERROR("Failed to allocate panel\n"); + return 1; + } + + if ( (p->orig_min_fs + p_w > data_width) + || (p->orig_min_ss + p_h > data_height) ) + { + ERROR("Panel %s is outside range of data provided\n", + p->name); + return 1; + } + + for ( ss=0; ss<p_h; ss++) { + for ( fs=0; fs<p_w; fs++) { + + int idx; + int cfs, css; + + cfs = fs+p->orig_min_fs; + css = ss+p->orig_min_ss; + idx = cfs + css*data_width; + + image->dp[pi][fs+p_w*ss] = data[idx]; + + } + } + + } + + return 0; +} + + +static double *find_msgpack_data(msgpack_object *obj, int *width, int *height) +{ + msgpack_object *corr_data_obj; + msgpack_object *data_obj; + msgpack_object *shape_obj; + double *data; + + corr_data_obj = find_msgpack_kv(obj, "corr_data"); + if ( corr_data_obj == NULL ) { + ERROR("No corr_data MessagePack object found.\n"); + return NULL; + } + + data_obj = find_msgpack_kv(corr_data_obj, "data"); + if ( data_obj == NULL ) { + ERROR("No data MessagePack object found inside corr_data.\n"); + return NULL; + } + if ( data_obj->type != MSGPACK_OBJECT_STR ) { + ERROR("corr_data.data isn't a binary object.\n"); + return NULL; + } + data = (double *)data_obj->via.str.ptr; + + shape_obj = find_msgpack_kv(corr_data_obj, "shape"); + if ( shape_obj == NULL ) { + ERROR("No shape MessagePack object found inside corr_data.\n"); + return NULL; + } + if ( shape_obj->type != MSGPACK_OBJECT_ARRAY ) { + ERROR("corr_data.shape isn't an array object.\n"); + return NULL; + } + if ( shape_obj->via.array.size != 2 ) { + ERROR("corr_data.shape is wrong size (%i, should be 2)\n", + shape_obj->via.array.size); + return NULL; + } + if ( shape_obj->via.array.ptr[0].type != MSGPACK_OBJECT_POSITIVE_INTEGER ) { + ERROR("corr_data.shape contains wrong type of element.\n"); + return NULL; + } + *height = shape_obj->via.array.ptr[0].via.i64; + *width = shape_obj->via.array.ptr[1].via.i64; + return data; +} + + +/* Unpacks the raw panel data from a msgpack_object, applies panel geometry, + * and stores the resulting data in an image struct. Object has structure + * { + * "corr_data": + * { + * "data": binary_data, + * "shape": [data_height, data_width], + * ... + * ... + * }, + * "key2": val2, + * ... + * ... + * } + */ +struct image *image_msgpack_read(DataTemplate *dtempl, + msgpack_object *obj, + int no_image_data, + int no_mask_data) +{ + struct image *image; + int data_width, data_height; + double *data; + + if ( obj == NULL ) { + ERROR("No MessagePack object!\n"); + return NULL; + } + + if ( dtempl == NULL ) { + ERROR("NULL data template!\n"); + return NULL; + } + + image = image_new(); + if ( image == NULL ) { + ERROR("Couldn't allocate image structure.\n"); + return NULL; + } + + if ( !no_image_data ) { + data = find_msgpack_data(obj, + &data_width, &data_height); + if ( data == NULL ) { + ERROR("No image data in MessagePack object.\n"); + return NULL; + } + unpack_slab(image, dtempl, data, + data_width, data_height); + } else { + image_set_zero_data(image, dtempl); + } + + image_set_zero_mask(image, dtempl); + + return image; +} diff --git a/libcrystfel/src/image-msgpack.h b/libcrystfel/src/image-msgpack.h new file mode 100644 index 00000000..a8e5af34 --- /dev/null +++ b/libcrystfel/src/image-msgpack.h @@ -0,0 +1,64 @@ +/* + * image-msgpack.h + * + * Image loading, MessagePack parts + * + * Copyright © 2012-2020 Deutsches Elektronen-Synchrotron DESY, + * a research centre of the Helmholtz Association. + * + * Authors: + * 2020 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 IMAGE_MSGPACK_H +#define IMAGE_MSGPACK_H + +#include "datatemplate.h" + +#if defined(HAVE_MSGPACK) + +#include <msgpack.h> + +extern struct image *image_msgpack_read(DataTemplate *dtempl, + msgpack_object *obj, + int no_image_data); + +extern ImageFeatureList *image_msgpack_read_peaks(const DataTemplate *dtempl, + msgpack_object *obj, + int half_pixel_shift); + +#else /* defined(HAVE_MSGPACK) */ + +static UNUSED struct image *image_msgpack_read(DataTemplate *dtempl, + void *obj, + int no_image_data) +{ + return NULL; +} + +static UNUSED ImageFeatureList *image_msgpack_read_peaks(const DataTemplate *dtempl, + void *obj, + int half_pixel_shift) +{ + return NULL; +} + +#endif /* defined(HAVE_MSGPACK) */ + +#endif /* IMAGE_MSGPACK_H */ diff --git a/libcrystfel/src/image.c b/libcrystfel/src/image.c index 6358a9d0..db47f75d 100644 --- a/libcrystfel/src/image.c +++ b/libcrystfel/src/image.c @@ -3,12 +3,12 @@ * * Handle images and image features * - * Copyright © 2012-2020 Deutsches Elektronen-Synchrotron DESY, + * Copyright © 2012-2021 Deutsches Elektronen-Synchrotron DESY, * a research centre of the Helmholtz Association. * * Authors: * 2014 Kenneth Beyerlein <kenneth.beyerlein@desy.de> - * 2011-2017 Thomas White <taw@physics.org> + * 2011-2021 Thomas White <taw@physics.org> * * This file is part of CrystFEL. * @@ -27,30 +27,27 @@ * */ +#ifdef HAVE_CONFIG_H #include <config.h> +#endif #include <stdlib.h> #include <assert.h> #include <math.h> #include <stdio.h> -#include <hdf5.h> -#include <zlib.h> +#include <sys/stat.h> +#include <fenv.h> #include "image.h" #include "utils.h" -#include "events.h" -#include "hdf5-file.h" -#include "detector.h" +#include "detgeom.h" +#include "image-hdf5.h" +#include "image-cbf.h" -/** \file image.h */ - -struct imagefile -{ - enum imagefile_type type; - char *filename; - struct hdfile *hdfile; -}; +#include "datatemplate.h" +#include "datatemplate_priv.h" +/** \file image.h */ struct _imagefeaturelist { @@ -61,7 +58,7 @@ struct _imagefeaturelist void image_add_feature(ImageFeatureList *flist, double fs, double ss, - struct panel *p, + int pn, struct image *parent, double intensity, const char *name) { if ( flist->n_features == flist->max_features ) { @@ -75,9 +72,8 @@ void image_add_feature(ImageFeatureList *flist, double fs, double ss, flist->features[flist->n_features].fs = fs; flist->features[flist->n_features].ss = ss; - flist->features[flist->n_features].p = p; + flist->features[flist->n_features].pn = pn; flist->features[flist->n_features].intensity = intensity; - flist->features[flist->n_features].parent = parent; flist->features[flist->n_features].name = name; flist->n_features++; @@ -107,10 +103,7 @@ static int comp(const void *a, const void *b) } -/** - * Strongest first. - */ -ImageFeatureList *sort_peaks(ImageFeatureList *flist) +ImageFeatureList *image_feature_list_copy(const ImageFeatureList *flist) { ImageFeatureList *n; int nf, i; @@ -128,30 +121,40 @@ ImageFeatureList *sort_peaks(ImageFeatureList *flist) nf = 0; for ( i=0; i<flist->n_features; i++ ) { - struct imagefeature *f; - f = image_get_feature(flist, i); + const struct imagefeature *f; + f = image_get_feature_const(flist, i); if ( f == NULL ) continue; n->features[nf++] = flist->features[i]; } n->n_features = nf; - qsort(n->features, nf, sizeof(struct imagefeature), comp); + return n; +} + +/** + * Strongest first. + */ +ImageFeatureList *sort_peaks(ImageFeatureList *flist) +{ + ImageFeatureList *n = image_feature_list_copy(flist); + qsort(n->features, image_feature_count(n), + sizeof(struct imagefeature), comp); return n; } void image_feature_list_free(ImageFeatureList *flist) { - if ( !flist ) return; - if ( flist->features ) free(flist->features); + if ( flist == NULL ) return; + free(flist->features); free(flist); } struct imagefeature *image_feature_closest(ImageFeatureList *flist, double fs, double ss, - struct panel *p, double *d, int *idx) + int pn, double *d, int *idx) { int i; double dmin = +HUGE_VAL; @@ -161,7 +164,7 @@ struct imagefeature *image_feature_closest(ImageFeatureList *flist, double ds; - if ( p != flist->features[i].p ) continue; + if ( pn != flist->features[i].pn ) continue; ds = distance(flist->features[i].fs, flist->features[i].ss, fs, ss); @@ -184,53 +187,21 @@ struct imagefeature *image_feature_closest(ImageFeatureList *flist, } -Reflection *image_reflection_closest(RefList *rlist, - double fs, double ss, struct panel *p, - struct detector *det, - double *d) +int image_feature_count(ImageFeatureList *flist) { - - double dmin = HUGE_VAL; - Reflection *closest = NULL; - Reflection *refl; - RefListIterator *iter; - - for ( refl = first_refl(rlist, &iter); - refl != NULL; - refl = next_refl(refl, iter) ) - { - double ds; - struct panel *p2; - double rfs, rss; - - get_detector_pos(refl, &rfs, &rss); - p2 = get_panel(refl); - - if ( p != p2 ) continue; - - ds = distance(rfs, rss, fs, ss); - - if ( ds < dmin ) { - dmin = ds; - closest = refl; - } - - } - - if ( dmin < +HUGE_VAL ) { - *d = dmin; - return closest; - } - - *d = +INFINITY; - return NULL; + if ( flist == NULL ) return 0; + return flist->n_features; } -int image_feature_count(ImageFeatureList *flist) +const struct imagefeature *image_get_feature_const(const ImageFeatureList *flist, + int idx) { - if ( flist == NULL ) return 0; - return flist->n_features; + /* Sanity check */ + if ( flist == NULL ) return NULL; + if ( idx >= flist->n_features ) return NULL; + + return &flist->features[idx]; } @@ -310,929 +281,909 @@ void free_all_crystals(struct image *image) } -/**************************** Image field lists *******************************/ - -struct imagefile_field_list +static double get_value(struct image *image, const char *from, + int *is_literal_number) { - char **fields; - int n_fields; - int max_fields; -}; - + double val; + char *rval; -struct imagefile_field_list *new_imagefile_field_list() -{ - struct imagefile_field_list *n; + if ( from == NULL ) return NAN; - n = calloc(1, sizeof(struct imagefile_field_list)); - if ( n == NULL ) return NULL; + val = strtod(from, &rval); + if ( (*rval == '\0') && (rval != from) ) { + if ( is_literal_number != NULL ) { + *is_literal_number = 1; + } + return val; + } - n->max_fields = 32; - n->fields = malloc(n->max_fields*sizeof(char *)); - if ( n->fields == NULL ) { - free(n); - return NULL; + if ( image == NULL ) { + ERROR("Attempt to retrieve a header value without an image\n"); + return NAN; } - return n; -} + if ( is_hdf5_file(image->filename) ) { + return image_hdf5_get_value(from, + image->filename, + image->ev); + } else if ( is_cbf_file(image->filename) ) { + /* FIXME: From headers */ + return NAN; -void free_imagefile_field_list(struct imagefile_field_list *n) -{ - int i; - for ( i=0; i<n->n_fields; i++ ) { - free(n->fields[i]); + } else if ( is_cbfgz_file(image->filename) ) { + /* FIXME: From headers */ + return NAN; + + } else { + ERROR("Unrecognised file type: %s\n", image->filename); + return NAN; } - free(n->fields); - free(n); } -void add_imagefile_field(struct imagefile_field_list *copyme, const char *name) +static char *get_value_and_units(struct image *image, const char *from, + double *pvalue, + int *is_literal_number) { - int i; + char *sp; + char *fromcpy; + char *unitscpy; - /* Already on the list? Don't re-add if so. */ - for ( i=0; i<copyme->n_fields; i++ ) { - if ( strcmp(copyme->fields[i], name) == 0 ) return; + if ( from == NULL ) { + *pvalue = NAN; + return NULL; } - /* Need more space? */ - if ( copyme->n_fields == copyme->max_fields ) { + fromcpy = strdup(from); + if ( fromcpy == NULL ) { + *pvalue = NAN; + return NULL; + } - char **nfields; - int nmax = copyme->max_fields + 32; + sp = strchr(fromcpy, ' '); + if ( sp == NULL ) { + unitscpy = NULL; + } else { + unitscpy = strdup(sp+1); + sp[0] = '\0'; + } - nfields = realloc(copyme->fields, nmax*sizeof(char *)); - if ( nfields == NULL ) { - ERROR("Failed to allocate space for new HDF5 field.\n"); - return; - } + *pvalue = get_value(image, fromcpy, is_literal_number); + free(fromcpy); - copyme->max_fields = nmax; - copyme->fields = nfields; + return unitscpy; +} - } - copyme->fields[copyme->n_fields] = strdup(name); - if ( copyme->fields[copyme->n_fields] == NULL ) { - ERROR("Failed to add field for copying '%s'\n", name); - return; +/* default_scale is a value to be used if both of the following + * conditions are met: + * + * 1. The value is a reference to image headers/metadata, + * rather than a literal number. + * 2. No units are specified in the number. + * + * This is totally horrible. Sorry. Blame history. + */ +static double im_get_length(struct image *image, const char *from, + double default_scale) +{ + char *units; + double value; + double scale; + int is_literal_number = 0; + + if ( from == NULL ) return NAN; + + units = get_value_and_units(image, from, + &value, &is_literal_number); + if ( units == NULL ) { + if ( is_literal_number ) { + scale = 1.0; + } else { + scale = default_scale; + } + } else { + if ( strcmp(units, "mm") == 0 ) { + scale = 1e-3; + } else if ( strcmp(units, "m") == 0 ) { + scale = 1.0; + } else { + ERROR("Invalid length unit '%s'\n", units); + free(units); + return NAN; + } } - copyme->n_fields++; + free(units); + return value * scale; } -/******************************* CBF files ************************************/ - -static int unpack_panels(struct image *image, float *data, int data_width, - int data_height) +int create_detgeom(struct image *image, const DataTemplate *dtempl) { - int pi; + struct detgeom *detgeom; + int i; - /* FIXME: Load these masks from an HDF5 file, if filenames are - * given in the geometry file */ - uint16_t *flags = NULL; - float *sat = NULL; - - image->dp = malloc(image->det->n_panels * sizeof(float *)); - image->bad = malloc(image->det->n_panels * sizeof(int *)); - image->sat = malloc(image->det->n_panels * sizeof(float *)); - if ( (image->dp == NULL) || (image->bad == NULL) - || (image->sat == NULL) ) - { - ERROR("Failed to allocate panels.\n"); + if ( dtempl == NULL ) { + ERROR("NULL data template!\n"); return 1; } - for ( pi=0; pi<image->det->n_panels; pi++ ) { - - struct panel *p; - int fs, ss; + detgeom = malloc(sizeof(struct detgeom)); + if ( detgeom == NULL ) return 1; - p = &image->det->panels[pi]; - image->dp[pi] = malloc(p->w*p->h*sizeof(float)); - image->bad[pi] = calloc(p->w*p->h, sizeof(int)); - image->sat[pi] = malloc(p->w*p->h*sizeof(float)); - if ( (image->dp[pi] == NULL) || (image->bad[pi] == NULL) - || (image->sat[pi] == NULL) ) - { - ERROR("Failed to allocate panel\n"); - return 1; - } + detgeom->panels = malloc(dtempl->n_panels*sizeof(struct detgeom_panel)); + if ( detgeom->panels == NULL ) return 1; - if ( p->mask != NULL ) { - ERROR("WARNING: Bad pixel masks do not currently work " - "with CBF files\n"); - ERROR(" (bad pixel regions specified in the geometry " - "file will be used, however)\n"); - } + detgeom->n_panels = dtempl->n_panels; - if ( p->satmap != NULL ) { - ERROR("WARNING: Saturation maps do not currently work " - "with CBF files\n"); - } + for ( i=0; i<dtempl->n_panels; i++ ) { - if ( (p->orig_min_fs + p->w > data_width) - || (p->orig_min_ss + p->h > data_height) ) - { - ERROR("Panel %s is outside range of data in CBF file\n", - p->name); - return 1; - } - - for ( ss=0; ss<p->h; ss++ ) { - for ( fs=0; fs<p->w; fs++ ) { + double shift_x, shift_y; - int idx; - int cfs, css; - int bad = 0; + detgeom->panels[i].name = safe_strdup(dtempl->panels[i].name); - cfs = fs+p->orig_min_fs; - css = ss+p->orig_min_ss; - idx = cfs + css*data_width; + detgeom->panels[i].pixel_pitch = dtempl->panels[i].pixel_pitch; - image->dp[pi][fs+p->w*ss] = data[idx]; + /* NB cnx,cny are in pixels, cnz is in m */ + detgeom->panels[i].cnx = dtempl->panels[i].cnx; + detgeom->panels[i].cny = dtempl->panels[i].cny; + detgeom->panels[i].cnz = im_get_length(image, + dtempl->panels[i].cnz_from, + 1e-3); - if ( sat != NULL ) { - image->sat[pi][fs+p->w*ss] = sat[idx]; - } else { - image->sat[pi][fs+p->w*ss] = INFINITY; - } + /* Apply offset (in m) and then convert cnz from + * m to pixels */ + detgeom->panels[i].cnz += dtempl->panels[i].cnz_offset; + detgeom->panels[i].cnz /= detgeom->panels[i].pixel_pitch; - if ( p->no_index ) bad = 1; + /* Apply overall shift (already in m) */ + shift_x = im_get_length(image, dtempl->shift_x_from, 1.0); + shift_y = im_get_length(image, dtempl->shift_y_from, 1.0); - if ( in_bad_region(image->det, p, cfs, css) ) { - bad = 1; - } + if ( !isnan(shift_x) ) { + detgeom->panels[i].cnx += shift_x; + } + if ( !isnan(shift_y) ) { + detgeom->panels[i].cny += shift_y; + } - if ( isnan(data[idx]) || isinf(data[idx]) ) bad = 1; + detgeom->panels[i].max_adu = dtempl->panels[i].max_adu; - if ( flags != NULL ) { + switch ( dtempl->panels[i].adu_scale_unit ) { - int f; + case ADU_PER_PHOTON: + detgeom->panels[i].adu_per_photon = dtempl->panels[i].adu_scale; + break; - f = flags[idx]; + case ADU_PER_EV: + detgeom->panels[i].adu_per_photon = dtempl->panels[i].adu_scale + * ph_lambda_to_eV(image->lambda); + break; - /* Bad if it's missing any of the "good" bits */ - if ( (f & image->det->mask_good) - != image->det->mask_good ) bad = 1; + default: + detgeom->panels[i].adu_per_photon = 1.0; + ERROR("Invalid ADU/ph scale unit (%i)\n", + dtempl->panels[i].adu_scale_unit); + break; - /* Bad if it has any of the "bad" bits. */ - if ( f & image->det->mask_bad ) bad = 1; + } - } - image->bad[pi][fs+p->w*ss] = bad; + detgeom->panels[i].w = dtempl->panels[i].orig_max_fs + - dtempl->panels[i].orig_min_fs + 1; + detgeom->panels[i].h = dtempl->panels[i].orig_max_ss + - dtempl->panels[i].orig_min_ss + 1; - } - } + detgeom->panels[i].fsx = dtempl->panels[i].fsx; + detgeom->panels[i].fsy = dtempl->panels[i].fsy; + detgeom->panels[i].fsz = dtempl->panels[i].fsz; + detgeom->panels[i].ssx = dtempl->panels[i].ssx; + detgeom->panels[i].ssy = dtempl->panels[i].ssy; + detgeom->panels[i].ssz = dtempl->panels[i].ssz; } + image->detgeom = detgeom; + return 0; } -static void cbf_fill_in_beam_parameters(struct beam_params *beam, - struct imagefile *f, - struct image *image) +int image_set_zero_data(struct image *image, + const DataTemplate *dtempl) { - double eV; + int pi; - if ( beam->photon_energy_from == NULL ) { + image->dp = malloc(dtempl->n_panels*sizeof(float *)); + if ( image->dp == NULL ) return 1; - /* Explicit value given */ - eV = beam->photon_energy; + for ( pi=0; pi<dtempl->n_panels; pi++ ) { - } else { + struct panel_template *p; + int p_w, p_h; + + p = &dtempl->panels[pi]; + p_w = p->orig_max_fs - p->orig_min_fs + 1; + p_h = p->orig_max_ss - p->orig_min_ss + 1; - ERROR("Can't get photon energy from CBF yet.\n"); - eV = 0.0; + image->dp[pi] = calloc(p_w*p_h, sizeof(float)); + if ( image->dp[pi] == NULL ) return 1; } - image->lambda = ph_en_to_lambda(eV_to_J(eV))*beam->photon_energy_scale; + return 0; } -static void cbf_fill_in_clen(struct detector *det, struct imagefile *f) +int image_set_zero_mask(struct image *image, + const DataTemplate *dtempl) { - int i; - - for ( i=0; i<det->n_panels; i++ ) { + int pi; - struct panel *p = &det->panels[i]; + image->bad = malloc(dtempl->n_panels*sizeof(int *)); + image->sat = malloc(dtempl->n_panels*sizeof(float *)); + if ( (image->bad == NULL) || (image->sat == NULL) ) return 1; - if ( p->clen_from != NULL ) { + for ( pi=0; pi<dtempl->n_panels; pi++ ) { - ERROR("Can't get clen from CBF yet.\n"); + struct panel_template *p; + int p_w, p_h; + long int i; - } + p = &dtempl->panels[pi]; + p_w = p->orig_max_fs - p->orig_min_fs + 1; + p_h = p->orig_max_ss - p->orig_min_ss + 1; - adjust_centering_for_rail(p); + image->bad[pi] = calloc(p_w*p_h, sizeof(int)); + image->sat[pi] = calloc(p_w*p_h, sizeof(float)); + if ( image->bad[pi] == NULL ) return 1; + if ( image->sat[pi] == NULL ) return 1; + for ( i=0; i<p_w*p_h; i++ ) { + image->sat[pi][i] = INFINITY; + } } -} - -static void add_out(float val, float *data_out, int nmemb_out, - int *outpos, int *nrej) -{ - if ( *outpos < nmemb_out ) { - data_out[(*outpos)++] = val; - } else { - (*nrej)++; - } + return 0; } -/* 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) +static int file_exists(const char *filename) { - int inpos = 0; - int outpos = 0; - int nrej = 0; - float val = 0.0; - - while ( inpos < n ) { + struct stat statbuf; + int r; - int64_t delta = data_in[inpos++]; + r = stat(filename, &statbuf); + if ( r != 0 ) { + return 0; + } - if ( (delta >= -127) && (delta <= 127) ) { - val += delta; - add_out(val, data_out, nmemb_out, &outpos, &nrej); - continue; - } + return 1; +} - 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; - } +static int image_read_image_data(struct image *image, + const DataTemplate *dtempl, + const char *filename, + const char *event) +{ + if ( !file_exists(filename) ) { + ERROR("File not found: %s\n", filename); + return image_set_zero_data(image, dtempl); + } - delta = *(int32_t *)(data_in+inpos); - inpos += 4; + if ( is_hdf5_file(filename) ) { + return image_hdf5_read(image, dtempl, filename, event); - if ( (delta >= -2147483647) && (delta <= 2147483647) ) { - val += delta; - add_out(val, data_out, nmemb_out, &outpos, &nrej); - continue; - } + } else if ( is_cbf_file(filename) ) { + return image_cbf_read(image, dtempl, filename, event, 0); - delta = *(int64_t *)(data_in+inpos); - inpos += 8; - val += delta; - add_out(val, data_out, nmemb_out, &outpos, &nrej); + } else if ( is_cbfgz_file(filename) ) { + return image_cbf_read(image, dtempl, filename, event, 1); } - if ( nrej > 0 ) { - STATUS("%i elements rejected\n", nrej); - } + ERROR("Unrecognised file type: %s\n", filename); + return 1; } -static int binary_start(char *data) +static void set_image_parameters(struct image *image, + const DataTemplate *dtempl) { - char *datac = data; - if ( (datac[0] == (char)0x0c) && (datac[1] == (char)0x1a) - && (datac[2] == (char)0x04) && (datac[3] == (char)0xd5) ) return 1; - return 0; -} + /* Wavelength might be needed to create detgeom (adu_per_eV) */ + image->lambda = convert_to_m(get_value(image, + dtempl->wavelength_from, + NULL), + dtempl->wavelength_unit); + image->bw = dtempl->bandwidth; -enum cbf_data_conversion -{ - CBF_NO_CONVERSION, - CBF_BYTE_OFFSET, - CBF_PACKED, - CBF_CANONICAL -}; + /* FIXME: Possibly load spectrum from file */ + image->spectrum = spectrum_generate_gaussian(image->lambda, + image->bw); -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) +static void mark_flagged_pixels_lessthan(float *dp, int *bad, + long int n, float val) { - if ( strstr(t, "signed 8-bit integer") != NULL ) - { - return CBF_ELEMENT_S8; + long int i; + for ( i=0; i<n; i++ ) { + if ( dp[i] < val ) bad[i] = 1; } +} - if ( strstr(t, "unsigned 8-bit integer") != NULL ) - { - return CBF_ELEMENT_U8; - } - if ( strstr(t, "signed 16-bit integer") != NULL ) - { - return CBF_ELEMENT_S16; +static void mark_flagged_pixels_morethan(float *dp, int *bad, + long int n, float val) +{ + long int i; + for ( i=0; i<n; i++ ) { + if ( dp[i] > val ) bad[i] = 1; } +} - 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; - } +static void mark_flagged_pixels_equal(float *dp, int *bad, + long int n, float val) +{ + long int i; + fenv_t envp; - if ( strstr(t, "signed 32-bit real IEEE") != NULL ) - { - return CBF_ELEMENT_F32; - } + fegetenv(&envp); + fesetround(1); /* Round to nearest (for flag_value) */ - if ( strstr(t, "signed 64-bit real IEEE") != NULL ) - { - return CBF_ELEMENT_F64; + for ( i=0; i<n; i++ ) { + if ( rint(dp[i]) == val ) bad[i] = 1; } - /* complex type is unsupported */ - - return CBF_NO_TYPE; + fesetenv(&envp); } -static size_t element_size(enum cbf_data_type t) +static void mark_flagged_pixels_naninf(float *dp, int *bad, + long int n) { - 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; + long int i; + for ( i=0; i<n; i++ ) { + float val = dp[i]; + if ( isnan(val) || isinf(val) ) bad[i] = 1; } } - -static int convert_type(float *data_out, long nmemb_exp, - enum cbf_data_type eltype, - void *data_in, size_t data_in_len) +static void mark_flagged_pixels(struct panel_template *p, + float *dp, int *bad) { - 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; - } + int p_w, p_h; + long int n; + int i; - for ( i=0; i<nmemb_exp; i++ ) { - switch ( eltype ) { + p_w = p->orig_max_fs - p->orig_min_fs + 1; + p_h = p->orig_max_ss - p->orig_min_ss + 1; + n = p_w * p_h; - case CBF_ELEMENT_S8: - data_out[o++] = ((int8_t *)data_in)[i]; - break; + mark_flagged_pixels_naninf(dp, bad, n); - case CBF_ELEMENT_U8: - data_out[o++] = ((uint8_t *)data_in)[i]; - break; + for ( i=0; i<MAX_FLAG_VALUES; i++ ) { - case CBF_ELEMENT_S16: - data_out[o++] = ((int16_t *)data_in)[i]; - break; + float fv = p->flag_values[i]; - case CBF_ELEMENT_U16: - data_out[o++] = ((uint16_t *)data_in)[i]; - break; + switch ( p->flag_types[i] ) { - case CBF_ELEMENT_S32: - data_out[o++] = ((int32_t *)data_in)[i]; + case FLAG_NOTHING: break; - case CBF_ELEMENT_U32: - data_out[o++] = ((uint32_t *)data_in)[i]; + case FLAG_LESSTHAN: + mark_flagged_pixels_lessthan(dp, bad, n, fv); break; - case CBF_ELEMENT_F32: - data_out[o++] = ((float *)data_in)[i]; + case FLAG_MORETHAN: + mark_flagged_pixels_morethan(dp, bad, n, fv); break; - case CBF_ELEMENT_F64: - data_out[o++] = ((double *)data_in)[i]; + case FLAG_EQUAL: + mark_flagged_pixels_equal(dp, bad, n, fv); break; - case CBF_NO_TYPE: - break; } } - - return 0; } -static float *read_cbf_data(struct imagefile *f, int *w, int *h) +static int region_within_panel(struct dt_badregion *region, + struct detgeom_panel *panel) { - FILE *fh; - 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; + assert(region->is_fsss); - *w = 0; - *h = 0; + if ( region->min_fs < 0 ) return 0; + if ( region->min_ss < 0 ) return 0; + if ( region->max_fs >= panel->w ) return 0; + if ( region->max_ss >= panel->h ) return 0; + return 1; +} - if ( f->type == IMAGEFILE_CBF ) { - fh = fopen(f->filename, "rb"); - if ( fh == NULL ) { - ERROR("Failed to open '%s'\n", f->filename); - return NULL; - } +static void draw_bad_region_fsss(struct dt_badregion *region, + int **bad, + struct detgeom *detgeom) +{ + struct detgeom_panel *panel; + int fs, ss; - } else if ( f->type == IMAGEFILE_CBFGZ ) { + panel = &detgeom->panels[region->panel_number]; - gzFile gzfh; - size_t len, len_read; - const size_t bufinc = 8*1024*1024; /* Allocate buffer in 8Mb chunks */ - size_t bufsz = bufinc; + if ( !region_within_panel(region, panel) ) { + ERROR("Bad pixel region %s is (partially) outside panel - ignoring it\n", + region->name); + return; + } - gzfh = gzopen(f->filename, "rb"); - if ( gzfh == NULL ) return NULL; + for ( ss=region->min_ss; ss<=region->max_ss; ss++ ) { + for ( fs=region->min_fs; fs<=region->max_fs; fs++ ) { + bad[region->panel_number][fs+ss*panel->w] = 1; + } + } +} - #ifdef HAVE_GZBUFFER - /* Set larger buffer size for hopefully faster uncompression */ - gzbuffer(gzfh, 128*1024); - #endif - buf = malloc(bufsz); - if ( buf == NULL ) return NULL; +static void draw_bad_region_xy(struct dt_badregion *region, + int **bad, + struct detgeom *detgeom) +{ + int i; - len = 0; - do { + for ( i=0; i<detgeom->n_panels; i++ ) { - len_read = gzread(gzfh, buf+len, bufinc); - if ( len_read == -1 ) return NULL; - len += len_read; + int fs, ss; - if ( len_read == bufinc ) { - bufsz += bufinc; - buf = realloc(buf, bufsz); - if ( buf == NULL ) return NULL; - } + struct detgeom_panel *p = &detgeom->panels[i]; + for ( ss=0; ss<p->h; ss++ ) { + for ( fs=0; fs<p->w; fs++ ) { - } while ( len_read == bufinc ); + double x, y; - fh = fmemopen(buf, len, "rb"); - if ( fh == NULL ) return NULL; + x = fs*p->fsx + ss*p->ssx + p->cnx; + y = fs*p->fsy + ss*p->ssy + p->cny; - gzclose(gzfh); + if ( (x > region->min_x ) + && (x < region->max_x) + && (y > region->min_y) + && (y < region->max_y) ) + { + bad[i][fs+ss*p->w] = 1; + } - } else { - /* Don't know how we ended up here */ - 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 { - - char line[1024]; - long line_start; - line_start = ftell(fh); - rval = fgets(line, 1023, fh); - if ( rval == NULL ) break; - chomp(line); +static void mark_bad_regions(struct image *image, + const DataTemplate *dtempl) +{ + int i; - if ( strcmp(line, "--CIF-BINARY-FORMAT-SECTION--") == 0 ) { - in_binary_section = 1; + for ( i=0; i<dtempl->n_bad; i++ ) { + if ( dtempl->bad[i].is_fsss ) { + draw_bad_region_fsss(&dtempl->bad[i], + image->bad, + image->detgeom); + } else { + draw_bad_region_xy(&dtempl->bad[i], + image->bad, + image->detgeom); } + } +} - if ( strcmp(line, "--CIF-BINARY-FORMAT-SECTION----") == 0 ) { - in_binary_section = 0; - } - if ( in_binary_section ) { +static int load_mask(struct panel_template *p, + const char *mask_fn, + const char *ev, + int *bad, + const char *mask_location, + unsigned int mask_good, + unsigned int mask_bad) +{ + if ( is_hdf5_file(mask_fn) ) { + image_hdf5_read_mask(p, mask_fn, ev, bad, mask_location, + mask_good, mask_bad); - if ( strncmp(line, "X-Binary-Size: ", 15) == 0 ) { - data_compressed_len = atoi(line+15); - } + } else if ( is_cbf_file(mask_fn) ) { + image_cbf_read_mask(p, mask_fn, ev, 0, bad, + mask_good, mask_bad); - 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; - } - } + } else if ( is_cbfgz_file(mask_fn) ) { + image_cbf_read_mask(p, mask_fn, ev, 1, bad, + mask_good, mask_bad); - /* 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; - } + } else { + ERROR("Unrecognised mask file type (%s)\n", mask_fn); + return 1; + } - /* 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; - } - } + return 0; +} - if ( strncmp(line, "X-Binary-Size-Fastest-Dimension: ", 33) == 0 ) { - *w = atoi(line+33); - } - if ( strncmp(line, "X-Binary-Size-Second-Dimension: ", 32) == 0 ) { - *h = atoi(line+32); - } +static int create_badmap(struct image *image, + const DataTemplate *dtempl, + int no_mask_data) +{ + int i; - } + image->bad = malloc(dtempl->n_panels * sizeof(int *)); + if ( image->bad == NULL ) { + ERROR("Failed to allocate bad pixel mask\n"); + return 1; + } - if ( in_binary_section && binary_start(line) ) { + for ( i=0; i<dtempl->n_panels; i++ ) { - size_t len_read; - int nmemb_exp; - void *data_compressed; - int r = 0; + int p_w, p_h; + struct panel_template *p = &dtempl->panels[i]; - if ( data_compressed_len == 0 ) { - ERROR("Found CBF data before X-Binary-Size!\n"); - free(buf); - fclose(fh); - return NULL; - } + p_w = p->orig_max_fs - p->orig_min_fs + 1; + p_h = p->orig_max_ss - p->orig_min_ss + 1; - if ( (*w == 0) || (*h == 0) ) { - ERROR("Found CBF data before dimensions!\n"); - free(buf); - fclose(fh); - return NULL; - } + image->bad[i] = calloc(p_w*p_h, sizeof(int)); + if ( image->bad[i] == NULL ) { + ERROR("Failed to allocate bad pixel mask\n"); + return 1; + } - if ( data_compressed_len > 100*1024*1024 ) { - ERROR("Stated CBF data size too big\n"); - free(buf); - fclose(fh); - return NULL; - } + /* Panel marked as bad? */ + if ( p->bad ) { + /* NB this sets every element to 0x1111, + * but that's OK - value is still 'true'. */ + memset(image->bad[i], 1, p_w*p_h); + } - 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; - } + /* Add bad regions (skip if panel is bad anyway) */ + if ( !p->bad ) { + mark_flagged_pixels(p, image->dp[i], + image->bad[i]); + } - 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; - } + /* Load masks (skip if panel is bad anyway) */ + if ( (!no_mask_data) && (!p->bad) ) { - 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; - } + int j; - 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; + for ( j=0; j<MAX_MASKS; j++ ) { - } + const char *mask_fn; - free(data_compressed); + if ( p->masks[j].data_location == NULL ) { + continue; + } - if ( r ) { - free(buf); - free(data_out); - fclose(fh); - return NULL; - } + if ( p->masks[j].filename == NULL ) { + mask_fn = image->filename; + } else { + mask_fn = p->masks[j].filename; + } - free(buf); - fclose(fh); - return data_out; + load_mask(p, mask_fn, image->ev, image->bad[i], + p->masks[j].data_location, + p->masks[j].good_bits, + p->masks[j].bad_bits); + } } + } - } while ( rval != NULL ); + mark_bad_regions(image, dtempl); - ERROR("Reached end of CBF file before finding data.\n"); - free(buf); /* might be NULL */ - return NULL; + return 0; } -static int read_cbf(struct imagefile *f, struct image *image) +static int create_satmap(struct image *image, + const DataTemplate *dtempl) { - float *data; - int w, h; - - data = read_cbf_data(f, &w, &h); - if ( data == NULL ) { - ERROR("Failed to read CBF data\n"); - return 1; - } - - unpack_panels(image, data, w, h); - free(data); - - if ( image->beam != NULL ) { - cbf_fill_in_beam_parameters(image->beam, f, image); - if ( image->lambda > 1000 ) { - ERROR("WARNING: Missing or nonsensical wavelength " - "(%e m) for %s.\n", - image->lambda, image->filename); - } - } - cbf_fill_in_clen(image->det, f); - fill_in_adu(image); - + /* FIXME: Implementation */ return 0; } -static int read_cbf_simple(struct imagefile *f, struct image *image) +/** + * Create an image structure, suitable for simulation. + * + * WARNING: This is probably not the routine you are looking for! + * If you use this routine anywhere other than a simulation program, then + * you are abusing the API and can expect breakage. In particular, your + * program will only work when the experiment is completely described by + * the DataTemplate, with no values whatsoever coming from image headers. + * + * \returns the new image structure. + * + */ +struct image *image_create_for_simulation(const DataTemplate *dtempl) { - float *data; - int w, h; + struct image *image; - data = read_cbf_data(f, &w, &h); - if ( data == NULL ) { - ERROR("Failed to read CBF data\n"); - return 1; + if ( dtempl == NULL ) { + ERROR("NULL data template!\n"); + return NULL; } - image->det = simple_geometry(image, w, h); - image->dp = malloc(sizeof(float *)); - if ( image->dp == NULL ) { - ERROR("Failed to allocate dp array\n"); - return 1; + image = image_new(); + if ( image == NULL ) { + ERROR("Couldn't allocate image structure.\n"); + return NULL; } - image->dp[0] = data; + if ( image_set_zero_data(image, dtempl) ) { + image_free(image); + return NULL; + } - if ( image->beam != NULL ) { - cbf_fill_in_beam_parameters(image->beam, f, image); - if ( image->lambda > 1000 ) { - ERROR("WARNING: Missing or nonsensical wavelength " - "(%e m) for %s.\n", - image->lambda, image->filename); - } + set_image_parameters(image, dtempl); + + if ( create_detgeom(image, dtempl) ) { + image_free(image); + return NULL; } - cbf_fill_in_clen(image->det, f); - fill_in_adu(image); - return 0; -} + if ( create_badmap(image, dtempl, 1) ) { + image_free(image); + return NULL; + } + if ( create_satmap(image, dtempl) ) { + image_free(image); + return NULL; + } -/****************************** Image files ***********************************/ + return image; +} -signed int is_cbf_file(const char *filename) +struct image *image_read(const DataTemplate *dtempl, + const char *filename, + const char *event, + int no_image_data, + int no_mask_data) { - FILE *fh; - char line[1024]; + struct image *image; + int r; - fh = fopen(filename, "r"); - if ( fh == NULL ) return -1; + if ( dtempl == NULL ) { + ERROR("NULL data template!\n"); + return NULL; + } - if ( fgets(line, 1024, fh) == NULL ) return -1; - fclose(fh); + image = image_new(); + if ( image == NULL ) { + ERROR("Couldn't allocate image structure.\n"); + return NULL; + } - if ( strstr(line, "CBF") == NULL ) { - return 0; + image->filename = strdup(filename); + if ( event != NULL ) { + image->ev = strdup(event); + } else { + image->ev = strdup("//"); /* Null event */ } - return 1; -} + /* Load the image data */ + if ( !no_image_data ) { + r = image_read_image_data(image, dtempl, + filename, event); + } else { + r = image_set_zero_data(image, dtempl); + } + if ( r ) { + image_free(image); + return NULL; + } + set_image_parameters(image, dtempl); -signed int is_cbfgz_file(const char *filename) -{ - gzFile gzfh; - char line[1024]; + if ( create_detgeom(image, dtempl) ) { + image_free(image); + return NULL; + } - gzfh = gzopen(filename, "rb"); - if ( gzfh == NULL ) return -1; - if ( gzgets(gzfh, line, 1024) == NULL ) return -1; - gzclose(gzfh); + if ( create_badmap(image, dtempl, no_mask_data) ) { + image_free(image); + return NULL; + } - if ( strstr(line, "CBF") == NULL ) { - return 0; + if ( create_satmap(image, dtempl) ) { + image_free(image); + return NULL; } - return 1; + return image; } -struct imagefile *imagefile_open(const char *filename) +void image_free(struct image *image) { - struct imagefile *f; + int i, np; + + if ( image == NULL ) return; + image_feature_list_free(image->features); + free_all_crystals(image); + spectrum_free(image->spectrum); + free(image->filename); + free(image->ev); + + if ( image->detgeom != NULL ) { + np = image->detgeom->n_panels; + detgeom_free(image->detgeom); + } else { + np = 0; + } - f = malloc(sizeof(struct imagefile)); - if ( f == NULL ) return NULL; + for ( i=0; i<np; i++ ) { + if ( image->dp != NULL ) free(image->dp[i]); + if ( image->sat != NULL ) free(image->sat[i]); + if ( image->bad != NULL ) free(image->bad[i]); + } - if ( H5Fis_hdf5(filename) > 0 ) { + free(image->dp); + free(image->sat); + free(image->bad); - /* This is an HDF5, pass through to HDF5 layer */ - f->type = IMAGEFILE_HDF5; - f->hdfile = hdfile_open(filename); + free(image); +} - if ( f->hdfile == NULL ) { - free(f); - return NULL; - } - } else if ( is_cbf_file(filename) > 0 ) { +struct image *image_new() +{ + struct image *image; + + image = malloc(sizeof(struct image)); + if ( image == NULL ) return NULL; + + image->dp = NULL; + image->bad = NULL; + image->sat = NULL; + image->hit = 0; + image->crystals = NULL; + image->n_crystals = 0; + image->indexed_by = INDEXING_NONE; + image->detgeom = NULL; + image->filename = NULL; + image->ev = NULL; + image->copied_headers = NULL; + image->id = 0; + image->serial = 0; + image->spectrum = NULL; + image->lambda = -1.0; + image->div = 0.0; + image->bw = -1.0; + image->peak_resolution = -1.0; + image->features = NULL; + + return image; +} - f->type = IMAGEFILE_CBF; - } else if ( is_cbfgz_file(filename) ) { +ImageFeatureList *image_read_peaks(const DataTemplate *dtempl, + const char *filename, + const char *event, + int half_pixel_shift) +{ + if ( is_hdf5_file(filename) ) { - f->type = IMAGEFILE_CBFGZ; + enum peak_layout layout; - } else { + if ( dtempl->peak_list_type == PEAK_LIST_AUTO ) { - ERROR("Unrecognised file type: %s\n", filename); - return NULL; + const char *ext; + ext = filename_extension(filename, NULL); - } + if ( strcmp(ext, ".cxi") == 0 ) { + layout = PEAK_LIST_CXI; + } else if ( strcmp(ext, ".h5") == 0 ) { + layout = PEAK_LIST_LIST3; + } else { + ERROR("Couldn't determine peak list layout.\n"); + ERROR("Specify peak_layout = cxi or list3n in geometry file.\n"); + return NULL; + } - f->filename = strdup(filename); - return f; -} + } else { + layout = dtempl->peak_list_type; + } + switch ( layout ) { -int imagefile_read(struct imagefile *f, struct image *image, - struct event *event) -{ - if ( f->type == IMAGEFILE_HDF5 ) { - return hdf5_read2(f->hdfile, image, event, 0); - } else if ( f->type == IMAGEFILE_CBF ) { - return read_cbf(f, image); - } else if ( f->type == IMAGEFILE_CBFGZ ) { - return read_cbf(f, image); - } else { - ERROR("Unknown file type %i\n", f->type); - return 1; - } -} + case PEAK_LIST_CXI : + return image_hdf5_read_peaks_cxi(dtempl, + filename, + event, + half_pixel_shift); + case PEAK_LIST_LIST3 : + return image_hdf5_read_peaks_hdf5(dtempl, + filename, + event, + half_pixel_shift); -/* Read a simple file, no multi-event, no prior geometry etc, and - * generate a geometry for it */ -int imagefile_read_simple(struct imagefile *f, struct image *image) -{ - if ( f->type == IMAGEFILE_HDF5 ) { - return hdf5_read(f->hdfile, image, NULL, 0); - } else if ( f->type == IMAGEFILE_CBF ) { - return read_cbf_simple(f, image); - } else if ( f->type == IMAGEFILE_CBFGZ ) { - return read_cbf_simple(f, image); - } else { - ERROR("Unknown file type %i\n", f->type); - return 1; - } -} + default : + ERROR("Invalid peak list type %i\n", layout); + return NULL; + } -enum imagefile_type imagefile_get_type(struct imagefile *f) -{ - assert(f != NULL); - return f->type; + } else { + ERROR("Peak lists can only be read from HDF5 files\n"); + return NULL; + } } -struct hdfile *imagefile_get_hdfile(struct imagefile *f) +char **image_expand_frames(const DataTemplate *dtempl, + const char *filename, int *n_frames) { - if ( f == NULL ) return NULL; - - if ( f->type != IMAGEFILE_HDF5 ) { - ERROR("Not an HDF5 file!\n"); + if ( !file_exists(filename) ) { + ERROR("File not found: %s\n", filename); return NULL; } - return f->hdfile; + if ( is_hdf5_file(filename) ) { + return image_hdf5_expand_frames(dtempl, filename, + n_frames); + } else { + char **list; + list = malloc(sizeof(char *)); + if ( list == NULL ) return NULL; + list[0] = strdup("//"); + if ( list[0] == NULL ) return NULL; + *n_frames = 1; + return list; + } } -void imagefile_copy_fields(struct imagefile *f, - const struct imagefile_field_list *copyme, - FILE *fh, struct event *ev) +void mark_resolution_range_as_bad(struct image *image, + double min, double max) { int i; - if ( copyme == NULL ) return; + if ( isinf(min) && isinf(max) ) return; /* nothing to do */ - for ( i=0; i<copyme->n_fields; i++ ) { + for ( i=0; i<image->detgeom->n_panels; i++ ) { - char *val; - char *field; - - field = copyme->fields[i]; + int fs, ss; + struct detgeom_panel *p = &image->detgeom->panels[i]; - if ( f->type == IMAGEFILE_HDF5 ) { - val = hdfile_get_string_value(f->hdfile, field, ev); - if ( field[0] == '/' ) { - fprintf(fh, "hdf5%s = %s\n", field, val); - } else { - fprintf(fh, "hdf5/%s = %s\n", field, val); + for ( ss=0; ss<p->h; ss++ ) { + for ( fs=0; fs<p->w; fs++ ) { + double q[3]; + double r; + detgeom_transform_coords(p, fs, ss, + image->lambda, + 0.0, 0.0, q); + r = modulus(q[0], q[1], q[2]); + if ( (r >= min) && (r <= max) ) { + image->bad[i][fs+p->w*ss] = 1; + } } - free(val); - - } else { - STATUS("Mock CBF variable\n"); - fprintf(fh, "cbf/%s = %s\n", field, "(FIXME)"); } - } } -void imagefile_close(struct imagefile *f) +int image_write(const struct image *image, + const DataTemplate *dtempl, + const char *filename) { - if ( f->type == IMAGEFILE_HDF5 ) { - hdfile_close(f->hdfile); + if ( is_hdf5_file(filename) ) { + return image_hdf5_write(image, dtempl, filename); } - free(f->filename); - free(f); + + ERROR("Can only write to HDF5 files.\n"); + return 1; } diff --git a/libcrystfel/src/image.h b/libcrystfel/src/image.h index d3bb56e5..c3b55ba0 100644 --- a/libcrystfel/src/image.h +++ b/libcrystfel/src/image.h @@ -3,11 +3,11 @@ * * Handle images and image features * - * Copyright © 2012-2020 Deutsches Elektronen-Synchrotron DESY, + * Copyright © 2012-2021 Deutsches Elektronen-Synchrotron DESY, * a research centre of the Helmholtz Association. * * Authors: - * 2009-2019 Thomas White <taw@physics.org> + * 2009-2020 Thomas White <taw@physics.org> * 2014 Valerio Mariani * * @@ -28,33 +28,23 @@ * */ -#ifdef HAVE_CONFIG_H -#include <config.h> -#endif - #ifndef IMAGE_H #define IMAGE_H -struct detector; - #include <stdint.h> #include <complex.h> #include <sys/types.h> struct imagefeature; -struct sample; struct image; -struct imagefile; -struct imagefile_field_list; #include "utils.h" #include "cell.h" -#include "detector.h" #include "reflist.h" #include "crystal.h" #include "index.h" -#include "events.h" #include "spectrum.h" +#include "datatemplate.h" /** * \file image.h @@ -65,8 +55,6 @@ struct imagefile_field_list; /** Represents a peak in an image. */ struct imagefeature { - 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. @@ -77,44 +65,17 @@ struct imagefeature { double ss; /**@}*/ - struct panel *p; /**< Pointer to panel */ + int pn; /**< Panel number */ double intensity; /**< Intensity */ - /** \name Reciprocal space coordinates (m^-1) of this feature */ - /** @{ */ - double rx; - double ry; - double rz; - /** @} */ - const char *name; /**< Text name, e.g. "5,3,-1" */ }; -/** An enum representing the image file formats we can handle */ -enum imagefile_type -{ - 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 */ typedef struct _imagefeaturelist ImageFeatureList; -struct beam_params -{ - 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 */ - double bandwidth; /**< FWHM bandwidth as a fraction of - * wavelength */ -}; - - struct image { /** The image data, by panel */ @@ -142,25 +103,16 @@ struct image int n_indexing_tries; /** The detector structure */ - struct detector *det; - - /** The nominal beam parameters (or where to get them) */ - struct beam_params *beam; + struct detgeom *detgeom; /** \name The filename and event ID for the image * @{ */ char *filename; - struct event *event; + char *ev; /** @} */ - /** 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; - - /** Mean of the camera length values for all panels */ - double avg_clen; + char *copied_headers; /** ID number of the worker processing handling this image */ int id; @@ -198,7 +150,7 @@ extern ImageFeatureList *image_feature_list_new(void); extern void image_feature_list_free(ImageFeatureList *flist); extern void image_add_feature(ImageFeatureList *flist, double x, double y, - struct panel *p, + int pn, struct image *parent, double intensity, const char *name); @@ -206,42 +158,53 @@ extern void image_remove_feature(ImageFeatureList *flist, int idx); extern struct imagefeature *image_feature_closest(ImageFeatureList *flist, double fs, double ss, - struct panel *p, + int pn, double *d, int *idx); -extern Reflection *image_reflection_closest(RefList *rlist, - double fs, double ss, - struct panel *p, - struct detector *det, - double *d); - extern int image_feature_count(ImageFeatureList *flist); extern struct imagefeature *image_get_feature(ImageFeatureList *flist, int idx); +extern const struct imagefeature *image_get_feature_const(const ImageFeatureList *flist, + int idx); extern ImageFeatureList *sort_peaks(ImageFeatureList *flist); +extern ImageFeatureList *image_feature_list_copy(const ImageFeatureList *flist); extern void image_add_crystal(struct image *image, Crystal *cryst); extern int remove_flagged_crystals(struct image *image); extern void free_all_crystals(struct image *image); -/* Image files */ -extern struct imagefile *imagefile_open(const char *filename); -extern int imagefile_read(struct imagefile *f, struct image *image, - struct event *event); -extern int imagefile_read_simple(struct imagefile *f, struct image *image); -extern struct hdfile *imagefile_get_hdfile(struct imagefile *f); -extern enum imagefile_type imagefile_get_type(struct imagefile *f); -extern void imagefile_copy_fields(struct imagefile *f, - const struct imagefile_field_list *copyme, - FILE *fh, struct event *ev); -extern void imagefile_close(struct imagefile *f); -extern signed int is_cbf_file(const char *filename); - -/* Field lists */ -extern struct imagefile_field_list *new_imagefile_field_list(void); -extern void free_imagefile_field_list(struct imagefile_field_list *f); - -extern void add_imagefile_field(struct imagefile_field_list *copyme, - const char *name); +extern void mark_resolution_range_as_bad(struct image *image, + double min, double max); + +extern struct image *image_new(void); +extern struct image *image_read(const DataTemplate *dtempl, + const char *filename, + const char *event, + int no_image_data, + int no_mask_data); +extern struct image *image_create_for_simulation(const DataTemplate *dtempl); +extern void image_free(struct image *image); + +extern ImageFeatureList *image_read_peaks(const DataTemplate *dtempl, + const char *filename, + const char *event, + int half_pixel_shift); + +extern char **image_expand_frames(const DataTemplate *dtempl, + const char *filename, int *nframes); + +extern int image_set_zero_data(struct image *image, + const DataTemplate *dtempl); + +extern int image_set_zero_mask(struct image *image, + const DataTemplate *dtempl); + +extern int image_write(const struct image *image, + const DataTemplate *dtempl, + const char *filename); + +/* Use within libcrystfel only */ +extern int create_detgeom(struct image *image, + const DataTemplate *dtempl); #ifdef __cplusplus } diff --git a/libcrystfel/src/index.c b/libcrystfel/src/index.c index 7b03ba3e..58e90437 100644 --- a/libcrystfel/src/index.c +++ b/libcrystfel/src/index.c @@ -3,12 +3,12 @@ * * Perform indexing (somehow) * - * Copyright © 2012-2020 Deutsches Elektronen-Synchrotron DESY, + * Copyright © 2012-2021 Deutsches Elektronen-Synchrotron DESY, * a research centre of the Helmholtz Association. * Copyright © 2012 Lorenzo Galli * * Authors: - * 2010-2017 Thomas White <taw@physics.org> + * 2010-2021 Thomas White <taw@physics.org> * 2010-2011 Richard Kirian <rkirian@asu.edu> * 2012 Lorenzo Galli * 2013 Cornelius Gati <cornelius.gati@cfel.de> @@ -47,19 +47,18 @@ #include "image.h" #include "utils.h" #include "peaks.h" -#include "dirax.h" -#include "asdf.h" -#include "mosflm.h" -#include "xds.h" -#include "detector.h" #include "index.h" #include "geometry.h" #include "cell-utils.h" -#include "felix.h" #include "predict-refine.h" -#include "taketwo.h" -#include "xgandalf.h" -#include "pinkindexer.h" +#include "indexers/dirax.h" +#include "indexers/asdf.h" +#include "indexers/mosflm.h" +#include "indexers/xds.h" +#include "indexers/felix.h" +#include "indexers/taketwo.h" +#include "indexers/xgandalf.h" +#include "indexers/pinkindexer.h" #include "fromfile.h" #include "uthash.h" @@ -72,6 +71,8 @@ struct _indexingprivate IndexingFlags flags; UnitCell *target_cell; double tolerance[6]; + double wavelength_estimate; + int n_threads; int n_methods; IndexingMethod *methods; @@ -113,7 +114,8 @@ static void show_indexing_flags(IndexingFlags flags) onoff(flags & INDEXING_RETRY)); } -static char *base_indexer_str(IndexingMethod indm) + +char *base_indexer_str(IndexingMethod indm) { char *str; @@ -199,7 +201,7 @@ static char *friendly_indexer_name(IndexingMethod m) static void *prepare_method(IndexingMethod *m, UnitCell *cell, - struct detector *det, struct beam_params *beam, + double wavelength_estimate, struct xgandalf_options *xgandalf_opts, struct pinkIndexer_options* pinkIndexer_opts, struct felix_options *felix_opts, @@ -250,7 +252,7 @@ static void *prepare_method(IndexingMethod *m, UnitCell *cell, case INDEXING_PINKINDEXER : priv = pinkIndexer_prepare(m, cell, pinkIndexer_opts, - det, beam); + wavelength_estimate); break; default : @@ -280,23 +282,16 @@ static void *prepare_method(IndexingMethod *m, UnitCell *cell, } -IndexingPrivate *setup_indexing(const char *method_list, UnitCell *cell, - struct detector *det, struct beam_params *beam, - float *tols, IndexingFlags flags, - struct taketwo_options *ttopts, - struct xgandalf_options *xgandalf_opts, - struct pinkIndexer_options *pinkIndexer_opts, - struct felix_options *felix_opts, - char *filename) +IndexingMethod *parse_indexing_methods(const char *method_list, + int *pn) { int i, n; char **method_strings; - IndexingPrivate *ipriv; + IndexingMethod *methods; - /* Parse indexing methods */ n = assplode(method_list, ",", &method_strings, ASSPLODE_NONE); - IndexingMethod *methods = malloc(n * sizeof(IndexingMethod)); + methods = malloc(n * sizeof(IndexingMethod)); if ( methods == NULL ) { ERROR("Failed to allocate indexing method list\n"); return NULL; @@ -323,6 +318,30 @@ IndexingPrivate *setup_indexing(const char *method_list, UnitCell *cell, } free(method_strings); + *pn = n; + return methods; +} + + +/* 'tols' is in frac (not %) and radians */ +IndexingPrivate *setup_indexing(const char *method_list, + UnitCell *cell, + float *tols, + IndexingFlags flags, + double wavelength_estimate, + int n_threads, + struct taketwo_options *ttopts, + struct xgandalf_options *xgandalf_opts, + struct pinkIndexer_options *pinkIndexer_opts, + struct felix_options *felix_opts, + char *filename) +{ + IndexingPrivate *ipriv; + IndexingMethod *methods; + int n, i; + + methods = parse_indexing_methods(method_list, &n); + /* No cell parameters -> no cell checking, no prior cell */ if ( !cell_has_parameters(cell) ) { @@ -380,7 +399,7 @@ IndexingPrivate *setup_indexing(const char *method_list, UnitCell *cell, int j; ipriv->engine_private[i] = prepare_method(&methods[i], cell, - det, beam, + wavelength_estimate, xgandalf_opts, pinkIndexer_opts, felix_opts, @@ -424,6 +443,8 @@ IndexingPrivate *setup_indexing(const char *method_list, UnitCell *cell, ipriv->methods = methods; ipriv->n_methods = n; ipriv->flags = flags; + ipriv->wavelength_estimate = wavelength_estimate; + ipriv->n_threads = n_threads; if ( cell != NULL ) { ipriv->target_cell = cell_new_from_cell(cell); @@ -518,29 +539,6 @@ void cleanup_indexing(IndexingPrivate *ipriv) } -void map_all_peaks(struct image *image) -{ - int i, n; - - n = image_feature_count(image->features); - - /* Map positions to 3D */ - for ( i=0; i<n; i++ ) { - - struct imagefeature *f; - struct rvec r; - - f = image_get_feature(image->features, i); - if ( f == NULL ) continue; - - r = get_q_for_panel(f->p, f->fs, f->ss, - NULL, 1.0/image->lambda); - f->rx = r.u; f->ry = r.v; f->rz = r.w; - - } -} - - /* Return 0 for cell OK, 1 for cell incorrect */ static int check_cell(IndexingFlags flags, Crystal *cr, UnitCell *target, double *tolerance) @@ -551,10 +549,14 @@ static int check_cell(IndexingFlags flags, Crystal *cr, UnitCell *target, /* Check at all? */ if ( !(flags & INDEXING_CHECK_CELL) ) return 0; - if ( !right_handed(target) ) STATUS("WARNING: reference cell is left handed\n"); - if ( !right_handed(crystal_get_cell(cr)) ) STATUS("WARNING: unmatched cell is left handed\n"); + if ( !right_handed(target) ) { + STATUS("WARNING: reference cell is left handed\n"); + } + if ( !right_handed(crystal_get_cell(cr)) ) { + STATUS("WARNING: unmatched cell is left handed\n"); + } out = compare_reindexed_cell_parameters(crystal_get_cell(cr), target, - tolerance, &rm); + tolerance, &rm); if ( out != NULL ) { @@ -573,6 +575,12 @@ static int check_cell(IndexingFlags flags, Crystal *cr, UnitCell *target, cell_set_lattice_type(out, cell_get_lattice_type(target)); cell_set_unique_axis(out, cell_get_unique_axis(target)); + /* Correct P to R centering, for the same reason */ + if ( (cell_get_centering(target) == 'R') + && (cell_get_centering(out) == 'P') ) { + cell_set_centering(out, 'R'); + } + return 0; } @@ -580,6 +588,16 @@ static int check_cell(IndexingFlags flags, Crystal *cr, UnitCell *target, } +#ifdef MEASURE_INDEX_TIME +static float real_time() +{ + struct timespec tp; + clock_gettime(CLOCK_MONOTONIC_RAW, &tp); + return tp.tv_sec + tp.tv_nsec*1e-9; +} +#endif + + /* Return non-zero for "success" */ static int try_indexer(struct image *image, IndexingMethod indm, IndexingPrivate *ipriv, void *mpriv, char *last_task) @@ -588,6 +606,12 @@ static int try_indexer(struct image *image, IndexingMethod indm, int n_bad = 0; int n_before = image->n_crystals; + #ifdef MEASURE_INDEX_TIME + float time_start; + float time_end; + time_start = real_time(); + #endif + switch ( indm & INDEXING_METHOD_MASK ) { case INDEXING_NONE : @@ -631,7 +655,7 @@ static int try_indexer(struct image *image, IndexingMethod indm, case INDEXING_PINKINDEXER : set_last_task(last_task, "indexing:pinkindexer"); - r = run_pinkIndexer(image, mpriv); + r = run_pinkIndexer(image, mpriv, ipriv->n_threads); break; case INDEXING_XGANDALF : @@ -647,6 +671,10 @@ static int try_indexer(struct image *image, IndexingMethod indm, set_last_task(last_task, "indexing:finalisation"); + #ifdef MEASURE_INDEX_TIME + time_end = real_time(); + #endif + /* Stop a really difficult to debug situation in its tracks */ if ( image->n_crystals - n_before != r ) { ERROR("Whoops, indexer didn't return the right number " @@ -710,6 +738,7 @@ static int try_indexer(struct image *image, IndexingMethod indm, for ( j=0; j<this_crystal; j++ ) { Crystal *that_cr = image->crystals[j]; + /* 'tols' is in frac (not %) and radians */ const double tols[] = {0.1, 0.1, 0.1, deg2rad(5.0), deg2rad(5.0), @@ -731,6 +760,16 @@ static int try_indexer(struct image *image, IndexingMethod indm, n_bad = remove_flagged_crystals(image); assert(r >= n_bad); + #ifdef MEASURE_INDEX_TIME + printf("%s took %f s, %i crystals found of which %i accepted. %s %s\n", + indexer_str(indm & INDEXING_METHOD_MASK), + time_end - time_start, + r, r - n_bad, + image->filename, + image->ev); + fflush(stdout); + #endif + return r - n_bad; } @@ -765,6 +804,7 @@ static int delete_explained_peaks(struct image *image, Crystal *cr) double ax, ay, az; double bx, by, bz; double cx, cy, cz; + double dx, dy; const double min_dist = 0.25; int i, nspots = 0, nindexed = 0; @@ -775,11 +815,13 @@ static int delete_explained_peaks(struct image *image, Crystal *cr) cell_get_cartesian(crystal_get_cell(cr), &ax, &ay, &az, &bx, &by, &bz, &cx, &cy, &cz); + crystal_get_det_shift(cr, &dx, &dy); + /* Loop over peaks, checking proximity to nearest reflection */ for ( i=0; i<image_feature_count(image->features); i++ ) { struct imagefeature *f; - struct rvec q; + double q[3]; double h, k, l, hd, kd, ld; double dsq; @@ -788,14 +830,15 @@ static int delete_explained_peaks(struct image *image, Crystal *cr) nspots++; /* Reciprocal space position of found peak */ - q = get_q_for_panel(f->p, f->fs, f->ss, - NULL, 1.0/image->lambda); + detgeom_transform_coords(&image->detgeom->panels[f->pn], + f->fs, f->ss, image->lambda, dx, dy, + q); /* Decimal and fractional Miller indices of nearest * reciprocal lattice point */ - hd = q.u * ax + q.v * ay + q.w * az; - kd = q.u * bx + q.v * by + q.w * bz; - ld = q.u * cx + q.v * cy + q.w * cz; + hd = q[0] * ax + q[1] * ay + q[2] * az; + kd = q[0] * bx + q[1] * by + q[2] * bz; + ld = q[0] * cx + q[1] * cy + q[2] * cz; h = lrint(hd); k = lrint(kd); l = lrint(ld); @@ -875,14 +918,23 @@ void index_pattern_3(struct image *image, IndexingPrivate *ipriv, int *ping, if ( ipriv == NULL ) return; - if ( ipriv->methods[0] != INDEXING_FILE){ - map_all_peaks(image); - orig = image->features; - } - image->crystals = NULL; image->n_crystals = 0; + if ( !isnan(ipriv->wavelength_estimate) ) { + if ( !within_tolerance(image->lambda, + ipriv->wavelength_estimate, + 10.0) ) + { + ERROR("WARNING: Wavelength for %s %s (%e) differs by " + "more than 10%% from estimated value (%e)\n", + image->filename, image->ev, + image->lambda, ipriv->wavelength_estimate); + } + } + + orig = image->features; + for ( n=0; n<ipriv->n_methods; n++ ) { int done = 0; @@ -921,13 +973,6 @@ void index_pattern_3(struct image *image, IndexingPrivate *ipriv, int *ping, } - if ( ipriv->methods[0] == INDEXING_FILE){ - map_all_peaks(image); - } - else{ - image->features = orig; - } - if ( n < ipriv->n_methods ) { image->indexed_by = ipriv->methods[n]; } else { @@ -1144,13 +1189,13 @@ char *detect_indexing_methods(UnitCell *cell) if ( methods == NULL ) return NULL; methods[0] = '\0'; + do_probe(taketwo_probe, cell, methods); + do_probe(xgandalf_probe, cell, methods); do_probe(mosflm_probe, cell, methods); - do_probe(dirax_probe, cell, methods); do_probe(asdf_probe, cell, methods); + do_probe(dirax_probe, cell, methods); do_probe(xds_probe, cell, methods); - do_probe(xgandalf_probe, cell, methods); - /* Don't automatically use TakeTwo, Felix or PinkIndexer (yet) */ - //do_probe(taketwo_probe, cell, methods); + //do_probe(felix_probe, cell, methods); //do_probe(pinkIndexer_probe, cell, methods); @@ -1161,3 +1206,15 @@ char *detect_indexing_methods(UnitCell *cell) return methods; } + + +void default_method_options(TakeTwoOptions **ttopts, + XGandalfOptions **xgandalf_opts, + PinkIndexerOptions **pinkIndexer_opts, + FelixOptions **felix_opts) +{ + taketwo_default_options(ttopts); + xgandalf_default_options(xgandalf_opts); + pinkIndexer_default_options(pinkIndexer_opts); + felix_default_options(felix_opts); +} diff --git a/libcrystfel/src/index.h b/libcrystfel/src/index.h index bd5be68e..92406604 100644 --- a/libcrystfel/src/index.h +++ b/libcrystfel/src/index.h @@ -3,13 +3,13 @@ * * Perform indexing (somehow) * - * Copyright © 2012-2020 Deutsches Elektronen-Synchrotron DESY, + * Copyright © 2012-2021 Deutsches Elektronen-Synchrotron DESY, * a research centre of the Helmholtz Association. * Copyright © 2012 Richard Kirian * Copyright © 2012 Lorenzo Galli * * Authors: - * 2010-2017 Thomas White <taw@physics.org> + * 2010-2021 Thomas White <taw@physics.org> * 2010 Richard Kirian * 2012 Lorenzo Galli * 2015 Kenneth Beyerlein <kenneth.beyerlein@desy.de> @@ -34,10 +34,6 @@ #ifndef INDEX_H #define INDEX_H -#ifdef HAVE_CONFIG_H -#include <config.h> -#endif - /** * \file index.h * The indexing subsystem @@ -144,18 +140,35 @@ extern char *indexer_str(IndexingMethod indm); extern IndexingMethod get_indm_from_string(const char *method); extern IndexingMethod get_indm_from_string_2(const char *method, int *err); -#include "detector.h" +extern IndexingMethod *parse_indexing_methods(const char *method_list, + int *pn); +extern char *base_indexer_str(IndexingMethod indm); + #include "cell.h" #include "image.h" -#include "taketwo.h" -#include "xgandalf.h" -#include "pinkindexer.h" -#include "felix.h" - -extern IndexingPrivate *setup_indexing(const char *methods, UnitCell *cell, - struct detector *det, - struct beam_params *beam, float *ltl, +#include "datatemplate.h" + +typedef struct felix_options FelixOptions; +typedef struct taketwo_options TakeTwoOptions; +typedef struct xgandalf_options XGandalfOptions; +typedef struct pinkIndexer_options PinkIndexerOptions; + +extern struct argp felix_argp; +extern struct argp pinkIndexer_argp; +extern struct argp taketwo_argp; +extern struct argp xgandalf_argp; + +extern void default_method_options(TakeTwoOptions **ttopts, + XGandalfOptions **xgandalf_opts, + PinkIndexerOptions **pinkIndexer_opts, + FelixOptions **felix_opts); + +extern IndexingPrivate *setup_indexing(const char *methods, + UnitCell *cell, + float *ltl, IndexingFlags flags, + double wavelength_estimate, + int n_threads, struct taketwo_options *ttopts, struct xgandalf_options *xgandalf_opts, struct pinkIndexer_options *pinkIndexer_opts, diff --git a/libcrystfel/src/asdf.c b/libcrystfel/src/indexers/asdf.c index 7185172d..0c57f9f0 100644 --- a/libcrystfel/src/asdf.c +++ b/libcrystfel/src/indexers/asdf.c @@ -4,12 +4,12 @@ * Alexandra's Superior Direction Finder, or * Algorithm Similar to DirAx, FFT-based * - * Copyright © 2014-2020 Deutsches Elektronen-Synchrotron DESY, + * Copyright © 2014-2021 Deutsches Elektronen-Synchrotron DESY, * a research centre of the Helmholtz Association. * * Authors: * 2014-2015 Alexandra Tolstikova <alexandra.tolstikova@desy.de> - * 2015,2017 Thomas White <taw@physics.org> + * 2015-2017 Thomas White <taw@physics.org> * * This file is part of CrystFEL. * @@ -42,19 +42,27 @@ #include <gsl/gsl_linalg.h> #include <gsl/gsl_multifit.h> #include <gsl/gsl_fit.h> -#include <fftw3.h> #include "image.h" -#include "dirax.h" #include "utils.h" #include "peaks.h" #include "cell-utils.h" #include "asdf.h" +#include "cell.h" /** * \file asdf.h */ +#ifdef HAVE_FFTW + +#define FFTW_NO_Complex /* Please use "double[2]", not C99 "complex", + * despite complex.h possibly already being + * included. For more information, refer to: + * http://www.fftw.org/doc/Complex-numbers.html */ + +#include <fftw3.h> + struct fftw_vars { int N; fftw_plan p; @@ -794,15 +802,18 @@ static int find_cell(struct tvector *tvectors, int N_tvectors, double IndexFit, int i_min = sl < 2 * N_tvectors ? 0 : sl - 2 * N_tvectors; int i_max = sl < N_tvectors ? sl : N_tvectors; - for ( i = i_min; i < i_max; i++) if (tvectors[i].n > acl ) { + for ( i = i_min; i < i_max; i++) { + + if (tvectors[i].n <= acl ) continue; int j_min = sl - N_tvectors - 2 * i - 1 < 0 ? i + 1 : sl - N_tvectors - i; int j_max = sl - N_tvectors - i < 0 ? sl - i : N_tvectors; - for ( j = j_min; j < j_max; j++ ) - if ( tvectors[j].n > acl ) { + for ( j = j_min; j < j_max; j++ ) { + + if ( tvectors[j].n <= acl ) continue; k = sl - i - j - 1; @@ -1083,7 +1094,7 @@ int run_asdf(struct image *image, void *ipriv) d_max = max(a, b, c) * 3 * 1e10; - double volume = cell_get_volume(dp->template) / 1e30; + double volume = cell_get_volume(dp->template) * 1e30; /* Divide volume constraints by number of lattice points per * unit cell since asdf always finds primitive cell */ @@ -1105,14 +1116,19 @@ int run_asdf(struct image *image, void *ipriv) for ( i=0; i<n; i++ ) { struct imagefeature *f; + double r[3]; f = image_get_feature(image->features, i); if ( f == NULL ) continue; + detgeom_transform_coords(&image->detgeom->panels[f->pn], + f->fs, f->ss, image->lambda, + 0.0, 0.0, r); + reflections[N_reflections] = gsl_vector_alloc(3); - gsl_vector_set(reflections[N_reflections], 0, f->rx/1e10); - gsl_vector_set(reflections[N_reflections], 1, f->ry/1e10); - gsl_vector_set(reflections[N_reflections], 2, f->rz/1e10); + gsl_vector_set(reflections[N_reflections], 0, r[0]/1e10); + gsl_vector_set(reflections[N_reflections], 1, r[1]/1e10); + gsl_vector_set(reflections[N_reflections], 2, r[2]/1e10); N_reflections++; } @@ -1200,3 +1216,32 @@ const char *asdf_probe(UnitCell *cell) { return "asdf"; } + +#else /* HAVE_FFTW */ + +int run_asdf(struct image *image, void *ipriv) +{ + ERROR("This copy of CrystFEL was compiled without FFTW support.\n"); + return 0; +} + + +void *asdf_prepare(IndexingMethod *indm, UnitCell *cell) +{ + ERROR("This copy of CrystFEL was compiled without FFTW support.\n"); + ERROR("To use asdf indexing, recompile with FFTW.\n"); + return NULL; +} + + +const char *asdf_probe(UnitCell *cell) +{ + return NULL; +} + + +void asdf_cleanup(void *pp) +{ +} + +#endif /* HAVE_FFTW */ diff --git a/libcrystfel/src/asdf.h b/libcrystfel/src/indexers/asdf.h index d900c192..98095e3c 100644 --- a/libcrystfel/src/asdf.h +++ b/libcrystfel/src/indexers/asdf.h @@ -4,12 +4,12 @@ * Alexandra's Superior Direction Finder, or * Algorithm Similar to DirAx, FFT-based * - * Copyright © 2014-2020 Deutsches Elektronen-Synchrotron DESY, + * Copyright © 2014-2021 Deutsches Elektronen-Synchrotron DESY, * a research centre of the Helmholtz Association. * * Authors: * 2014-2015 Alexandra Tolstikova <alexandra.tolstikova@desy.de> - * 2015,2017 Thomas White <taw@physics.org> + * 2015-2021 Thomas White <taw@physics.org> * * This file is part of CrystFEL. * @@ -31,10 +31,6 @@ #ifndef ASDF_H #define ASDF_H -#ifdef HAVE_CONFIG_H -#include <config.h> -#endif - #include "index.h" #ifdef __cplusplus @@ -46,8 +42,6 @@ extern "C" { * The ASDF indexing algorithm. */ -#ifdef HAVE_FFTW - extern int run_asdf(struct image *image, void *ipriv); extern void *asdf_prepare(IndexingMethod *indm, UnitCell *cell); @@ -55,37 +49,6 @@ extern const char *asdf_probe(UnitCell *cell); extern void asdf_cleanup(void *pp); -#else /* HAVE_FFTW */ - -int run_asdf(struct image *image, void *ipriv) -{ - ERROR("This copy of CrystFEL was compiled without FFTW support.\n"); - return 0; -} - - -void *asdf_prepare(IndexingMethod *indm, UnitCell *cell) -{ - ERROR("This copy of CrystFEL was compiled without FFTW support.\n"); - ERROR("To use asdf indexing, recompile with FFTW.\n"); - return NULL; -} - - -const char *asdf_probe(UnitCell *cell) -{ - return NULL; -} - - -void asdf_cleanup(void *pp) -{ -} - - -#endif /* HAVE_FFTW */ - - #ifdef __cplusplus } #endif diff --git a/libcrystfel/src/dirax.c b/libcrystfel/src/indexers/dirax.c index 24be87ba..4b59f6cb 100644 --- a/libcrystfel/src/dirax.c +++ b/libcrystfel/src/indexers/dirax.c @@ -3,11 +3,11 @@ * * Invoke the DirAx auto-indexing program * - * Copyright © 2012-2020 Deutsches Elektronen-Synchrotron DESY, + * Copyright © 2012-2021 Deutsches Elektronen-Synchrotron DESY, * a research centre of the Helmholtz Association. * * Authors: - * 2010-2014,2017 Thomas White <taw@physics.org> + * 2010-2021 Thomas White <taw@physics.org> * * This file is part of CrystFEL. * @@ -490,12 +490,17 @@ static void write_drx(struct image *image) for ( i=0; i<image_feature_count(image->features); i++ ) { struct imagefeature *f; + double r[3]; f = image_get_feature(image->features, i); if ( f == NULL ) continue; + detgeom_transform_coords(&image->detgeom->panels[f->pn], + f->fs, f->ss, image->lambda, + 0.0, 0.0, r); + fprintf(fh, "%10f %10f %10f %8f\n", - f->rx/1e10, f->ry/1e10, f->rz/1e10, 1.0); + r[0]/1e10, r[1]/1e10, r[2]/1e10, 1.0); } fclose(fh); diff --git a/libcrystfel/src/dirax.h b/libcrystfel/src/indexers/dirax.h index 33dc1189..2bb560bb 100644 --- a/libcrystfel/src/dirax.h +++ b/libcrystfel/src/indexers/dirax.h @@ -3,11 +3,11 @@ * * Invoke the DirAx auto-indexing program * - * Copyright © 2012-2020 Deutsches Elektronen-Synchrotron DESY, + * Copyright © 2012-2021 Deutsches Elektronen-Synchrotron DESY, * a research centre of the Helmholtz Association. * * Authors: - * 2010,2012-2014,2017 Thomas White <taw@physics.org> + * 2010-2017 Thomas White <taw@physics.org> * * This file is part of CrystFEL. * @@ -29,10 +29,6 @@ #ifndef DIRAX_H #define DIRAX_H -#ifdef HAVE_CONFIG_H -#include <config.h> -#endif - #include "index.h" #ifdef __cplusplus diff --git a/libcrystfel/src/felix.c b/libcrystfel/src/indexers/felix.c index 2e4898e6..89de5f70 100644 --- a/libcrystfel/src/felix.c +++ b/libcrystfel/src/indexers/felix.c @@ -3,11 +3,11 @@ * * Invoke Felix for multi-crystal autoindexing. * - * Copyright © 2015-2020 Deutsches Elektronen-Synchrotron DESY, + * Copyright © 2015-2021 Deutsches Elektronen-Synchrotron DESY, * a research centre of the Helmholtz Association. * * Authors: - * 2015-2018 Thomas White <taw@physics.org> + * 2015-2021 Thomas White <taw@physics.org> * 2015 Kenneth Beyerlein <kenneth.beyerlein@desy.de> * * This file is part of CrystFEL. @@ -364,14 +364,19 @@ static void write_gve(struct image *image, struct felix_private *gp) for ( i=0; i<image_feature_count(image->features); i++ ) { struct imagefeature *f; + double r[3]; f = image_get_feature(image->features, i); if ( f == NULL ) continue; + detgeom_transform_coords(&image->detgeom->panels[f->pn], + f->fs, f->ss, image->lambda, + 0.0, 0.0, r); + fprintf(fh, "%.6f %.6f %.6f 0 0 %.6f %.6f %.6f 0\n", - f->rz/1e10, f->rx/1e10, f->ry/1e10, - modulus(f->rx, f->ry, f->rz)/1e10, /* dstar */ - rad2deg(atan2(f->ry, f->rx)), 0.0); /* eta, omega */ + r[2]/1e10, r[0]/1e10, r[1]/1e10, + modulus(r[0], r[1], r[2])/1e10, /* dstar */ + rad2deg(atan2(r[1], r[0])), 0.0); /* eta, omega */ } fclose(fh); @@ -384,7 +389,6 @@ static char *write_ini(struct image *image, struct felix_private *gp) char *filename; char gveFilename[1024]; char logFilename[1024]; - double tt; filename = malloc(1024); if ( filename == NULL ) return NULL; @@ -400,11 +404,6 @@ static char *write_ini(struct image *image, struct felix_private *gp) return NULL; } - get_q_for_panel(image->det->furthest_out_panel, - image->det->furthest_out_fs, - image->det->furthest_out_ss, - &tt, 1.0/image->lambda); - fprintf(fh, "spacegroup %i\n", gp->spacegroup); fprintf(fh, "tthrange %f %f\n", rad2deg(gp->tthrange_min), rad2deg(gp->tthrange_max)); @@ -806,7 +805,7 @@ const char *felix_probe(UnitCell *cell) } -static void show_help() +static void felix_show_help() { printf("Parameters for the Felix indexing algorithm:\n" " --felix-domega Degree range of omega (moscaicity) to consider.\n" @@ -838,30 +837,45 @@ static void show_help() } -static error_t parse_arg(int key, char *arg, struct argp_state *state) +int felix_default_options(FelixOptions **opts_ptr) +{ + FelixOptions *opts; + + opts = malloc(sizeof(struct felix_options)); + if ( opts == NULL ) return ENOMEM; + + opts->ttmin = -1.0; + opts->ttmax = -1.0; + opts->min_visits = 0; + opts->min_completeness = -1.0; + opts->max_uniqueness = -1.0; + opts->n_voxels = 0; + opts->fraction_max_visits = -1.0; + opts->sigma = -1.0; + opts->domega = -1.0; + opts->max_internal_angle = -1.0; + + *opts_ptr = opts; + return 0; +} + + +static error_t felix_parse_arg(int key, char *arg, + struct argp_state *state) { struct felix_options **opts_ptr = state->input; float tmp; + int r; switch ( key ) { case ARGP_KEY_INIT : - *opts_ptr = malloc(sizeof(struct felix_options)); - if ( *opts_ptr == NULL ) return ENOMEM; - (*opts_ptr)->ttmin = -1.0; - (*opts_ptr)->ttmax = -1.0; - (*opts_ptr)->min_visits = 0; - (*opts_ptr)->min_completeness = -1.0; - (*opts_ptr)->max_uniqueness = -1.0; - (*opts_ptr)->n_voxels = 0; - (*opts_ptr)->fraction_max_visits = -1.0; - (*opts_ptr)->sigma = -1.0; - (*opts_ptr)->domega = -1.0; - (*opts_ptr)->max_internal_angle = -1.0; + r = felix_default_options(opts_ptr); + if ( r ) return r; break; case 1 : - show_help(); + felix_show_help(); return EINVAL; case 2 : @@ -945,7 +959,7 @@ static error_t parse_arg(int key, char *arg, struct argp_state *state) } -static struct argp_option options[] = { +static struct argp_option felix_options[] = { {"help-felix", 1, NULL, OPTION_NO_USAGE, "Show options for Felix indexing algorithm", 99}, @@ -964,4 +978,5 @@ static struct argp_option options[] = { }; -struct argp felix_argp = { options, parse_arg, NULL, NULL, NULL, NULL, NULL }; +struct argp felix_argp = { felix_options, felix_parse_arg, + NULL, NULL, NULL, NULL, NULL }; diff --git a/libcrystfel/src/felix.h b/libcrystfel/src/indexers/felix.h index 4a992548..83179d6f 100644 --- a/libcrystfel/src/felix.h +++ b/libcrystfel/src/indexers/felix.h @@ -3,12 +3,12 @@ * * Invoke Felix for multi-crystal autoindexing * - * Copyright © 2013-2020 Deutsches Elektronen-Synchrotron DESY, + * Copyright © 2013-2021 Deutsches Elektronen-Synchrotron DESY, * a research centre of the Helmholtz Association. * * Authors: - * 2010-2013,2017 Thomas White <taw@physics.org> - * 2013-2014 Kenneth Beyerlein <kenneth.beyerlein@desy.de> + * 2010-2017 Thomas White <taw@physics.org> + * 2013-2014 Kenneth Beyerlein <kenneth.beyerlein@desy.de> * * This file is part of CrystFEL. * @@ -30,10 +30,6 @@ #ifndef FELIX_H #define FELIX_H -#ifdef HAVE_CONFIG_H -#include <config.h> -#endif - #include <argp.h> #include "cell.h" @@ -43,8 +39,7 @@ * Felix indexer interface */ -typedef struct felix_options FelixOptions; -extern struct argp felix_argp; +extern int felix_default_options(FelixOptions **opts_ptr); extern void *felix_prepare(IndexingMethod *indm, UnitCell *cell, struct felix_options *opts); diff --git a/libcrystfel/src/mosflm.c b/libcrystfel/src/indexers/mosflm.c index ef282cbe..b0e0b121 100644 --- a/libcrystfel/src/mosflm.c +++ b/libcrystfel/src/indexers/mosflm.c @@ -3,7 +3,7 @@ * * Invoke the DPS auto-indexing algorithm through MOSFLM * - * Copyright © 2012-2020 Deutsches Elektronen-Synchrotron DESY, + * Copyright © 2012-2021 Deutsches Elektronen-Synchrotron DESY, * a research centre of the Helmholtz Association. * Copyright © 2012 Richard Kirian * @@ -201,10 +201,11 @@ static void mosflm_parseline(const char *line, struct image *image, } -/* This is the opposite of spacegroup_for_lattice() below. +/* This is the opposite of mosflm_spacegroup_for_lattice() below. * Note that this is not general, just a set of rules for interpreting MOSFLM's * output. */ -static LatticeType spacegroup_to_lattice(const char *sg, char *ua, char *cen) +static LatticeType mosflm_spacegroup_to_lattice(const char *sg, + char *ua, char *cen) { LatticeType latt; @@ -298,7 +299,7 @@ static int read_newmat(struct mosflm_data *mosflm, const char *filename, return 1; } //STATUS("MOSFLM says '%s'\n", symm); - latt = spacegroup_to_lattice(symm+5, &ua, &cen); + latt = mosflm_spacegroup_to_lattice(symm+5, &ua, &cen); /* MOSFLM "A" matrix is multiplied by lambda, so fix this */ c = 1.0/image->lambda; @@ -356,16 +357,21 @@ static void write_spt(struct image *image, const char *filename) struct imagefeature *f; double ttx, tty, x, y; + double r[3]; f = image_get_feature(image->features, i); if ( f == NULL ) continue; + detgeom_transform_coords(&image->detgeom->panels[f->pn], + f->fs, f->ss, image->lambda, + 0.0, 0.0, r); + ttx = angle_between_2d(0.0, 1.0, - f->rx, 1.0/image->lambda + f->rz); + r[0], 1.0/image->lambda + r[2]); tty = angle_between_2d(0.0, 1.0, - f->ry, 1.0/image->lambda + f->rz); - if ( f->rx < 0.0 ) ttx *= -1.0; - if ( f->ry < 0.0 ) tty *= -1.0; + r[1], 1.0/image->lambda + r[2]); + if ( r[0] < 0.0 ) ttx *= -1.0; + if ( r[1] < 0.0 ) tty *= -1.0; x = -tan(ttx)*FAKE_CLEN; y = tan(tty)*FAKE_CLEN; @@ -437,7 +443,7 @@ static void mosflm_sendline(const char *line, struct mosflm_data *mosflm) /* Turn what we know about the unit cell into something which we can give to * MOSFLM to make it give us only indexing results compatible with the cell. */ -static char *spacegroup_for_lattice(UnitCell *cell) +static char *mosflm_spacegroup_for_lattice(UnitCell *cell) { LatticeType latt; char centering; @@ -530,7 +536,7 @@ static void mosflm_send_next(struct image *image, struct mosflm_data *mosflm) mosflm_sendline("CRYSTAL R\n", mosflm); } - symm = spacegroup_for_lattice(mosflm->mp->template); + symm = mosflm_spacegroup_for_lattice(mosflm->mp->template); snprintf(tmp, 255, "SYMM %s\n", symm); //STATUS("Asking MOSFLM for '%s'\n", symm); free(symm); @@ -754,8 +760,8 @@ int run_mosflm(struct image *image, void *ipriv) t.c_lflag &= ~(ECHO | ECHOE | ECHOK | ECHONL); tcsetattr(STDIN_FILENO, TCSANOW, &t); - execlp("mosflm", "mosflm", (char *)NULL); - execlp("ipmosflm", "ipmosflm", (char *)NULL); + execlp("mosflm", "mosflm", "-n", (char *)NULL); + execlp("ipmosflm", "ipmosflm", "-n", (char *)NULL); ERROR("Invocation: Failed to invoke MOSFLM: %s\n", strerror(errno)); _exit(0); diff --git a/libcrystfel/src/mosflm.h b/libcrystfel/src/indexers/mosflm.h index b6d708f5..0c64090b 100644 --- a/libcrystfel/src/mosflm.h +++ b/libcrystfel/src/indexers/mosflm.h @@ -3,13 +3,13 @@ * * Invoke the DPS auto-indexing algorithm through MOSFLM * - * Copyright © 2012-2020 Deutsches Elektronen-Synchrotron DESY, + * Copyright © 2012-2021 Deutsches Elektronen-Synchrotron DESY, * a research centre of the Helmholtz Association. * Copyright © 2012 Richard Kirian * * Authors: - * 2010 Richard Kirian <rkirian@asu.edu> - * 2012-2014,2017 Thomas White <taw@physics.org> + * 2010 Richard Kirian <rkirian@asu.edu> + * 2012-2017 Thomas White <taw@physics.org> * * This file is part of CrystFEL. * @@ -31,10 +31,6 @@ #ifndef MOSFLM_H #define MOSFLM_H -#ifdef HAVE_CONFIG_H -#include <config.h> -#endif - #include "index.h" #ifdef __cplusplus diff --git a/libcrystfel/src/pinkindexer.c b/libcrystfel/src/indexers/pinkindexer.c index 22208885..b0b5a9fa 100644 --- a/libcrystfel/src/pinkindexer.c +++ b/libcrystfel/src/indexers/pinkindexer.c @@ -3,11 +3,12 @@ * * Interface to PinkIndexer * - * Copyright © 2017-2020 Deutsches Elektronen-Synchrotron DESY, + * Copyright © 2017-2021 Deutsches Elektronen-Synchrotron DESY, * a research centre of the Helmholtz Association. * * Authors: * 2017-2019 Yaroslav Gevorkov <yaroslav.gevorkov@desy.de> + * 2021 Thomas White <thomas.white@desy.de> * * This file is part of CrystFEL. * @@ -26,34 +27,46 @@ * */ +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + #include "pinkindexer.h" -#ifdef HAVE_PINKINDEXER #include <stdlib.h> +#include <sys/errno.h> +#include <argp.h> #include "utils.h" #include "cell-utils.h" #include "peaks.h" -#include "pinkIndexer/adaptions/crystfel/Lattice.h" -#include "pinkIndexer/adaptions/crystfel/ExperimentSettings.h" -#include "pinkIndexer/adaptions/crystfel/PinkIndexer.h" -#define MAX_MULTI_LATTICE_COUNT 8 +#define FAKE_CLEN (0.25) + +struct pinkIndexer_options { + unsigned int considered_peaks_count; + unsigned int angle_resolution; + unsigned int refinement_type; + float maxResolutionForIndexing_1_per_A; + float tolerance; + float reflectionRadius; /* In m^-1 */ + float customBandwidth; + float maxRefinementDisbalance; +}; + +#ifdef HAVE_PINKINDEXER + +#include <pinkIndexer/adaptions/crystfel/Lattice.h> +#include <pinkIndexer/adaptions/crystfel/ExperimentSettings.h> +#include <pinkIndexer/adaptions/crystfel/PinkIndexer.h> struct pinkIndexer_private_data { PinkIndexer *pinkIndexer; - reciprocalPeaks_1_per_A_t reciprocalPeaks_1_per_A; - float *intensities; IndexingMethod indm; UnitCell *cellTemplate; - int threadCount; - int multi; - int min_peaks; - - int no_check_indexed; float maxRefinementDisbalance; @@ -66,132 +79,182 @@ struct pinkIndexer_private_data { static void reduceReciprocalCell(UnitCell* cell, LatticeTransform_t* appliedReductionTransform); static void restoreReciprocalCell(UnitCell *cell, LatticeTransform_t* appliedReductionTransform); static void makeRightHanded(UnitCell* cell); -static void update_detector(struct detector *det, double xoffs, double yoffs); -int run_pinkIndexer(struct image *image, void *ipriv) + +static double mean_clen(struct detgeom *dg) { - struct pinkIndexer_private_data* pinkIndexer_private_data = (struct pinkIndexer_private_data*) ipriv; - reciprocalPeaks_1_per_A_t* reciprocalPeaks_1_per_A = &(pinkIndexer_private_data->reciprocalPeaks_1_per_A); - float *intensities = pinkIndexer_private_data->intensities; - - int peakCountMax = image_feature_count(image->features); - if (peakCountMax < 5) { - int goodLatticesCount = 0; - return goodLatticesCount; + int i; + double total = 0.0; + for ( i=0; i<dg->n_panels; i++ ) { + total += dg->panels[i].cnz; } - reciprocalPeaks_1_per_A->peakCount = 0; - for (int i = 0; i < peakCountMax && i < MAX_PEAK_COUNT_FOR_INDEXER; i++) { - struct imagefeature *f; - f = image_get_feature(image->features, i); - if (f == NULL) { - continue; + return total / dg->n_panels; +} + + +static void scale_detector_shift(double fake_clen, + struct detgeom *dg, + double inx, double iny, + double *pdx, double *pdy) +{ + int i; + double mean = mean_clen(dg); + for ( i=0; i<dg->n_panels; i++ ) { + if ( !within_tolerance(dg->panels[i].cnz, mean, 2.0) ) { + ERROR("WARNING: Detector is not flat enough to apply " + "detector position offset\n"); + *pdx = 0.0; + *pdy = 0.0; + return; } + } + *pdx = (mean/fake_clen)*inx; + *pdy = (mean/fake_clen)*iny; +} + - reciprocalPeaks_1_per_A->coordinates_x[reciprocalPeaks_1_per_A->peakCount] = f->rz * 1e-10; - reciprocalPeaks_1_per_A->coordinates_y[reciprocalPeaks_1_per_A->peakCount] = f->rx * 1e-10; - reciprocalPeaks_1_per_A->coordinates_z[reciprocalPeaks_1_per_A->peakCount] = f->ry * 1e-10; - intensities[reciprocalPeaks_1_per_A->peakCount] = (float) (f->intensity); - reciprocalPeaks_1_per_A->peakCount++; +int run_pinkIndexer(struct image *image, void *ipriv, int n_threads) +{ + struct pinkIndexer_private_data *pinkIndexer_private_data = ipriv; + reciprocalPeaks_1_per_A_t reciprocalPeaks_1_per_A; + float *intensities; + int npk; + int i; + + /* FIXME: Check if wavelength is too far from estimate */ + + npk = image_feature_count(image->features); + if ( npk < 5 ) return 0; + + if ( npk > MAX_PEAK_COUNT_FOR_INDEXER ) { + npk = MAX_PEAK_COUNT_FOR_INDEXER; + } + + reciprocalPeaks_1_per_A.peakCount = 0; + intensities = malloc(npk*sizeof(float)); + allocReciprocalPeaks(&reciprocalPeaks_1_per_A); + if ( intensities == NULL ) return 0; + + for ( i=0; i<npk; i++ ) { + + struct imagefeature *f; + double r[3]; + + f = image_get_feature(image->features, i); + if ( f == NULL ) continue; + + detgeom_transform_coords(&image->detgeom->panels[f->pn], + f->fs, f->ss, image->lambda, + 0.0, 0.0, r); + reciprocalPeaks_1_per_A.coordinates_x[reciprocalPeaks_1_per_A.peakCount] = r[2] * 1e-10; + reciprocalPeaks_1_per_A.coordinates_y[reciprocalPeaks_1_per_A.peakCount] = r[0] * 1e-10; + reciprocalPeaks_1_per_A.coordinates_z[reciprocalPeaks_1_per_A.peakCount] = r[1] * 1e-10; + intensities[reciprocalPeaks_1_per_A.peakCount] = f->intensity; + reciprocalPeaks_1_per_A.peakCount++; } int indexed = 0; - Lattice_t indexedLattice[MAX_MULTI_LATTICE_COUNT]; - float center_shift[MAX_MULTI_LATTICE_COUNT][2]; + float center_shift[2]; + Lattice_t indexedLattice; + int matchedPeaksCount = PinkIndexer_indexPattern(pinkIndexer_private_data->pinkIndexer, + &indexedLattice, + center_shift, + &reciprocalPeaks_1_per_A, + intensities, + pinkIndexer_private_data->maxRefinementDisbalance, + n_threads); + free(intensities); + freeReciprocalPeaks(reciprocalPeaks_1_per_A); - do { - int peakCount = reciprocalPeaks_1_per_A->peakCount; - int matchedPeaksCount = PinkIndexer_indexPattern(pinkIndexer_private_data->pinkIndexer, - &(indexedLattice[indexed]), center_shift[indexed], reciprocalPeaks_1_per_A, intensities, - pinkIndexer_private_data->maxRefinementDisbalance, - pinkIndexer_private_data->threadCount); + if ( matchedPeaksCount == -1 ) { - if(matchedPeaksCount == -1){ - STATUS("WARNING: Indexing solution was rejected due to too large disbalance of the refinement." - "If you see this message often, check the documentation for the parameter " - "--pinkIndexer-max-refinement-disbalance\n"); + STATUS("WARNING: Indexing solution was rejected due to too " + "large imbalance of the refinement.\n" + "If you see this message often, check the documentation " + "for parameter --pinkIndexer-max-refinement-disbalance\n"); - matchedPeaksCount = 0; - } + } else { - printf("matchedPeaksCount %d from %d\n",matchedPeaksCount,peakCount); - if ((matchedPeaksCount >= 25 && matchedPeaksCount >= peakCount * 0.30) - || matchedPeaksCount >= peakCount * 0.4 - || matchedPeaksCount >= 70 - || pinkIndexer_private_data->no_check_indexed == 1) - { - UnitCell *uc; - uc = cell_new(); + UnitCell *uc; + UnitCell *new_cell_trans; - Lattice_t *l = &(indexedLattice[indexed]); + uc = cell_new(); - cell_set_reciprocal(uc, l->ay * 1e10, l->az * 1e10, l->ax * 1e10, - l->by * 1e10, l->bz * 1e10, l->bx * 1e10, - l->cy * 1e10, l->cz * 1e10, l->cx * 1e10); + cell_set_reciprocal(uc, indexedLattice.ay * 1e10, + indexedLattice.az * 1e10, + indexedLattice.ax * 1e10, + indexedLattice.by * 1e10, + indexedLattice.bz * 1e10, + indexedLattice.bx * 1e10, + indexedLattice.cy * 1e10, + indexedLattice.cz * 1e10, + indexedLattice.cx * 1e10); - restoreReciprocalCell(uc, &pinkIndexer_private_data->latticeReductionTransform); + restoreReciprocalCell(uc, &pinkIndexer_private_data->latticeReductionTransform); - UnitCell *new_cell_trans = cell_transform_intmat(uc, pinkIndexer_private_data->centeringTransformation); - cell_free(uc); - uc = new_cell_trans; + new_cell_trans = cell_transform_intmat(uc, pinkIndexer_private_data->centeringTransformation); + cell_free(uc); - cell_set_lattice_type(new_cell_trans, cell_get_lattice_type(pinkIndexer_private_data->cellTemplate)); - cell_set_centering(new_cell_trans, cell_get_centering(pinkIndexer_private_data->cellTemplate)); - cell_set_unique_axis(new_cell_trans, cell_get_unique_axis(pinkIndexer_private_data->cellTemplate)); + cell_set_lattice_type(new_cell_trans, + cell_get_lattice_type(pinkIndexer_private_data->cellTemplate)); + cell_set_centering(new_cell_trans, + cell_get_centering(pinkIndexer_private_data->cellTemplate)); + cell_set_unique_axis(new_cell_trans, + cell_get_unique_axis(pinkIndexer_private_data->cellTemplate)); - if (validate_cell(uc)) { - ERROR("pinkIndexer: problem with returned cell!\n"); - } + if ( validate_cell(new_cell_trans) ) { + ERROR("pinkIndexer: problem with returned cell!\n"); + } else { - Crystal * cr = crystal_new(); - if (cr == NULL) { + double dx, dy; + Crystal *cr = crystal_new(); + if ( cr == NULL ) { ERROR("Failed to allocate crystal.\n"); return 0; } - crystal_set_cell(cr, uc); - crystal_set_det_shift(cr, center_shift[indexed][0], center_shift[indexed][1]); - update_detector(image->det, center_shift[indexed][0], center_shift[indexed][1]); + crystal_set_cell(cr, new_cell_trans); + scale_detector_shift(FAKE_CLEN, + image->detgeom, + center_shift[0], + center_shift[1], + &dx, &dy); + crystal_set_det_shift(cr, dx, dy); image_add_crystal(image, cr); indexed++; - } else { - break; } - } while (pinkIndexer_private_data->multi - && indexed <= MAX_MULTI_LATTICE_COUNT - && reciprocalPeaks_1_per_A->peakCount >= pinkIndexer_private_data->min_peaks); + + } return indexed; } -void *pinkIndexer_prepare(IndexingMethod *indm, UnitCell *cell, + +void *pinkIndexer_prepare(IndexingMethod *indm, + UnitCell *cell, struct pinkIndexer_options *pinkIndexer_opts, - struct detector *det, struct beam_params *beam) + double wavelength_estimate) { - if ( beam->photon_energy_from != NULL && pinkIndexer_opts->customPhotonEnergy <= 0) { - ERROR("For pinkIndexer, the photon_energy must be defined as a " - "constant in the geometry file or as a parameter (see --pinkIndexer-override-photon-energy)\n"); - return NULL; - } - if ( (det->panels[0].clen_from != NULL) && pinkIndexer_opts->refinement_type == - REFINEMENT_TYPE_firstFixedThenVariableLatticeParametersCenterAdjustmentMultiSeed) { - ERROR("Using center refinement makes it necessary to have the detector distance fixed in the geometry file!"); + float beamEenergy_eV; + + if ( isnan(wavelength_estimate) ) { + ERROR("PinkIndexer requires a wavelength estimate. " + "Try again with --wavelength-estimate=xx\n"); return NULL; + } else { + beamEenergy_eV = J_to_eV(ph_lambda_to_en(wavelength_estimate)); } - if(cell == NULL){ - ERROR("Pink indexer needs a unit cell file to be specified!") + + if ( cell == NULL ) { + ERROR("Unit cell information is required for " + "PinkIndexer.\n"); return NULL; } struct pinkIndexer_private_data* pinkIndexer_private_data = malloc(sizeof(struct pinkIndexer_private_data)); - allocReciprocalPeaks(&(pinkIndexer_private_data->reciprocalPeaks_1_per_A)); - pinkIndexer_private_data->intensities = malloc(MAX_PEAK_COUNT_FOR_INDEXER * sizeof(float)); pinkIndexer_private_data->indm = *indm; pinkIndexer_private_data->cellTemplate = cell; - pinkIndexer_private_data->threadCount = pinkIndexer_opts->thread_count; - pinkIndexer_private_data->multi = pinkIndexer_opts->multi; - pinkIndexer_private_data->min_peaks = pinkIndexer_opts->min_peaks; - pinkIndexer_private_data->no_check_indexed = pinkIndexer_opts->no_check_indexed; pinkIndexer_private_data->maxRefinementDisbalance = pinkIndexer_opts->maxRefinementDisbalance; UnitCell* primitiveCell = uncenter_cell(cell, &pinkIndexer_private_data->centeringTransformation, NULL); @@ -209,21 +272,7 @@ void *pinkIndexer_prepare(IndexingMethod *indm, UnitCell *cell, .bx = bsz * 1e-10, .by = bsx * 1e-10, .bz = bsy * 1e-10, .cx = csz * 1e-10, .cy = csx * 1e-10, .cz = csy * 1e-10 }; - float detectorDistance_m; - if ( det->panels[0].clen_from != NULL ) { - detectorDistance_m = 0.25; /* fake value */ - } else { - detectorDistance_m = det->panels[0].clen + det->panels[0].coffset; - } - - float beamEenergy_eV = beam->photon_energy; - float nonMonochromaticity = beam->bandwidth*5; - if(pinkIndexer_opts->customPhotonEnergy > 0){ - beamEenergy_eV = pinkIndexer_opts->customPhotonEnergy; - } - if(pinkIndexer_opts->customBandwidth >= 0){ - nonMonochromaticity = pinkIndexer_opts->customBandwidth; - } + float nonMonochromaticity = 0.01; float reflectionRadius_1_per_A; if (pinkIndexer_opts->reflectionRadius < 0) { @@ -235,7 +284,9 @@ void *pinkIndexer_prepare(IndexingMethod *indm, UnitCell *cell, } if(beamEenergy_eV > 75000 && nonMonochromaticity < 0.02 && reflectionRadius_1_per_A < 0.0005){ - STATUS("Trying to index electron diffraction? It might be helpful to set a higher reflection radius (see documentation for --pinkIndexer-reflection-radius)") + STATUS("Trying to index electron diffraction? It might be " + " helpful to set a higher reflection radius " + "(see documentation for --pinkIndexer-reflection-radius)"); } float divergenceAngle_deg = 0.01; //fake @@ -243,9 +294,14 @@ void *pinkIndexer_prepare(IndexingMethod *indm, UnitCell *cell, float tolerance = pinkIndexer_opts->tolerance; Lattice_t sampleReciprocalLattice_1_per_A = lattice; float detectorRadius_m = 0.03; //fake, only for prediction - ExperimentSettings* experimentSettings = ExperimentSettings_new(beamEenergy_eV, detectorDistance_m, - detectorRadius_m, divergenceAngle_deg, nonMonochromaticity, sampleReciprocalLattice_1_per_A, tolerance, - reflectionRadius_1_per_A); + ExperimentSettings *experimentSettings = ExperimentSettings_new(beamEenergy_eV, + FAKE_CLEN, + detectorRadius_m, + divergenceAngle_deg, + nonMonochromaticity, + sampleReciprocalLattice_1_per_A, + tolerance, + reflectionRadius_1_per_A); consideredPeaksCount_t consideredPeaksCount = pinkIndexer_opts->considered_peaks_count; angleResolution_t angleResolution = pinkIndexer_opts->angle_resolution; @@ -341,24 +397,10 @@ static void makeRightHanded(UnitCell *cell) } } -//hack for electron crystallography while crystal_set_det_shift is not working approprietly -static void update_detector(struct detector *det, double xoffs, double yoffs) -{ - int i; - - for (i = 0; i < det->n_panels; i++) { - struct panel *p = &det->panels[i]; - p->cnx += xoffs * p->res; - p->cny += yoffs * p->res; - } -} - void pinkIndexer_cleanup(void *pp) { struct pinkIndexer_private_data* pinkIndexer_private_data = (struct pinkIndexer_private_data*) pp; - freeReciprocalPeaks(pinkIndexer_private_data->reciprocalPeaks_1_per_A); - free(pinkIndexer_private_data->intensities); intmat_free(pinkIndexer_private_data->centeringTransformation); PinkIndexer_delete(pinkIndexer_private_data->pinkIndexer); } @@ -370,15 +412,16 @@ const char *pinkIndexer_probe(UnitCell *cell) #else /* HAVE_PINKINDEXER */ -int run_pinkIndexer(struct image *image, void *ipriv) +int run_pinkIndexer(struct image *image, void *ipriv, int n_threads) { ERROR("This copy of CrystFEL was compiled without PINKINDEXER support.\n"); return 0; } -extern void *pinkIndexer_prepare(IndexingMethod *indm, UnitCell *cell, - struct pinkIndexer_options *pinkIndexer_opts, - struct detector *det, struct beam_params *beam) +extern void *pinkIndexer_prepare(IndexingMethod *indm, + UnitCell *cell, + struct pinkIndexer_options *pinkIndexer_opts, + double wavelength_estimate) { ERROR("This copy of CrystFEL was compiled without PINKINDEXER support.\n"); ERROR("To use PINKINDEXER indexing, recompile with PINKINDEXER.\n"); @@ -396,7 +439,7 @@ const char *pinkIndexer_probe(UnitCell *cell) #endif /* HAVE_PINKINDEXER */ -static void show_help() +static void pinkIndexer_show_help() { printf( "Parameters for the PinkIndexer indexing algorithm:\n" @@ -417,56 +460,49 @@ static void show_help() " Specified in 1/A. Default is 2%% of a*.\n" " --pinkIndexer-max-resolution-for-indexing=n\n" " Measured in 1/A\n" -" --pinkIndexer-multi Use pinkIndexers own multi indexing.\n" -" --pinkIndexer-thread-count=n\n" -" Thread count for internal parallelization \n" -" Default: 1\n" -" --pinkIndexer-no-check-indexed\n" -" Disable internal check for correct indexing\n" -" solutions\n" " --pinkIndexer-max-refinement-disbalance=n\n" -" Maximum disbalance after refinement:\n" -" 0 (no disbalance) to 2 (extreme disbalance), default 0.4\n" -" --pinkIndexer-override-photon-energy=ev\n" -" Mean energy in eV to use for indexing.\n" -" --pinkIndexer-override-bandwidth=n\n" -" Bandwidth in (delta energy)/(mean energy) to use for indexing.\n" -" --pinkIndexer-override-visible-energy-range=min-max\n" -" Overrides photon energy and bandwidth according to a range of \n" -" energies that have high enough intensity to produce \"visible\" \n" -" Bragg spots on the detector.\n" -" Min and max range borders are separated by a minus sign (no whitespace).\n" +" Maximum imbalance after refinement:\n" +" 0 (no imbalance) to 2 (extreme imbalance), default 0.4\n" ); } -static error_t parse_arg(int key, char *arg, struct argp_state *state) +int pinkIndexer_default_options(PinkIndexerOptions **opts_ptr) +{ + PinkIndexerOptions *opts; + + opts = malloc(sizeof(struct pinkIndexer_options)); + if ( opts == NULL ) return ENOMEM; + + opts->considered_peaks_count = 4; + opts->angle_resolution = 2; + opts->refinement_type = 1; + opts->tolerance = 0.06; + opts->maxResolutionForIndexing_1_per_A = +INFINITY; + opts->reflectionRadius = -1; + opts->maxRefinementDisbalance = 0.4; + + *opts_ptr = opts; + return 0; +} + + +static error_t pinkindexer_parse_arg(int key, char *arg, + struct argp_state *state) { - float tmp, tmp2; + float tmp; + int r; struct pinkIndexer_options **opts_ptr = state->input; switch ( key ) { case ARGP_KEY_INIT : - *opts_ptr = malloc(sizeof(struct pinkIndexer_options)); - if ( *opts_ptr == NULL ) return ENOMEM; - (*opts_ptr)->considered_peaks_count = 4; - (*opts_ptr)->angle_resolution = 2; - (*opts_ptr)->refinement_type = 1; - (*opts_ptr)->tolerance = 0.06; - (*opts_ptr)->maxResolutionForIndexing_1_per_A = +INFINITY; - (*opts_ptr)->thread_count = 1; - (*opts_ptr)->multi = 0; - (*opts_ptr)->no_check_indexed = 0; - (*opts_ptr)->min_peaks = 2; - (*opts_ptr)->reflectionRadius = -1; - (*opts_ptr)->customPhotonEnergy = -1; - (*opts_ptr)->customBandwidth = -1; - (*opts_ptr)->maxRefinementDisbalance = 0.4; + r = pinkIndexer_default_options(opts_ptr); + if ( r ) return r; break; case 1 : - show_help(); + pinkIndexer_show_help(); return EINVAL; case 2 : @@ -497,12 +533,9 @@ static error_t parse_arg(int key, char *arg, struct argp_state *state) break; case 5 : - if (sscanf(arg, "%d", &(*opts_ptr)->thread_count) != 1) - { - ERROR("Invalid value for --pinkIndexer-thread-count\n"); - return EINVAL; - } - break; + ERROR("Please use --max-indexer-threads instead of " + "--pinkIndexer-thread-count.\n"); + return EINVAL; case 6 : if (sscanf(arg, "%f", &(*opts_ptr)->maxResolutionForIndexing_1_per_A) != 1) @@ -522,11 +555,11 @@ static error_t parse_arg(int key, char *arg, struct argp_state *state) break; case 8 : - (*opts_ptr)->multi = 1; + ERROR("WARNING: --pinkIndexer-multi is ignored.\n"); break; case 9 : - (*opts_ptr)->no_check_indexed = 1; + ERROR("WARNING: --pinkIndexer-no-check-indexed is ignored.\n"); break; case 10 : @@ -538,32 +571,20 @@ static error_t parse_arg(int key, char *arg, struct argp_state *state) break; case 11 : - if (sscanf(arg, "%f", &(*opts_ptr)->customPhotonEnergy) != 1) - { - ERROR("Invalid value for --pinkIndexer-override-photon-energy\n"); - return EINVAL; - } - break; + ERROR("Please use --wavelength-estimate instead of " + "--pinkIndexer-override-photon-energy.\n"); + return EINVAL; case 12 : - if (sscanf(arg, "%f", &(*opts_ptr)->customBandwidth) != 1) - { - ERROR("Invalid value for --pinkIndexer-override-bandwidth\n"); - return EINVAL; - } - break; + ERROR("This CrystFEL version does not handle wide bandwidth "); + ERROR("(invalid option --pinkIndexer-override-bandwidth)\n"); + return EINVAL; + case 13 : - if (sscanf(arg, "%f-%f", &tmp, &tmp2) != 2) - { - ERROR("Invalid value for --pinkIndexer-override-visible-energy-range\n"); - return EINVAL; - } - (*opts_ptr)->customPhotonEnergy = (tmp + tmp2)/2; - (*opts_ptr)->customBandwidth = (tmp2 - tmp)/(*opts_ptr)->customPhotonEnergy; - if((*opts_ptr)->customBandwidth < 0){ - (*opts_ptr)->customBandwidth *= -1; - } - break; + ERROR("This CrystFEL version does not handle wide bandwidth "); + ERROR("(invalid option --pinkIndexer-override-visible-energy-range)\n"); + return EINVAL; + case 14 : if (sscanf(arg, "%f", &(*opts_ptr)->maxRefinementDisbalance) != 1) { @@ -576,7 +597,7 @@ static error_t parse_arg(int key, char *arg, struct argp_state *state) } -static struct argp_option options[] = { +static struct argp_option pinkindexer_options[] = { {"help-pinkindexer", 1, NULL, OPTION_NO_USAGE, "Show options for PinkIndexer indexing algorithm", 99}, @@ -617,4 +638,6 @@ static struct argp_option options[] = { }; -struct argp pinkIndexer_argp = { options, parse_arg, NULL, NULL, NULL, NULL, NULL }; +struct argp pinkIndexer_argp = { pinkindexer_options, + pinkindexer_parse_arg, + NULL, NULL, NULL, NULL, NULL }; diff --git a/libcrystfel/src/pinkindexer.h b/libcrystfel/src/indexers/pinkindexer.h index f79f4331..358a8221 100644 --- a/libcrystfel/src/pinkindexer.h +++ b/libcrystfel/src/indexers/pinkindexer.h @@ -3,11 +3,12 @@ * * Interface to PinkIndexer * - * Copyright © 2017-2020 Deutsches Elektronen-Synchrotron DESY, + * Copyright © 2017-2021 Deutsches Elektronen-Synchrotron DESY, * a research centre of the Helmholtz Association. * * Authors: * 2017-2019 Yaroslav Gevorkov <yaroslav.gevorkov@desy.de> + * 2021 Thomas White <thomas.white@desy.de> * * This file is part of CrystFEL. * @@ -29,37 +30,19 @@ #ifndef LIBCRYSTFEL_SRC_PINKINDEXER_H_ #define LIBCRYSTFEL_SRC_PINKINDEXER_H_ -#ifdef HAVE_CONFIG_H -#include <config.h> -#endif - -typedef struct pinkIndexer_options PinkIndexerOptions; -extern struct argp pinkIndexer_argp; - -struct pinkIndexer_options { - unsigned int considered_peaks_count; - unsigned int angle_resolution; - unsigned int refinement_type; - float maxResolutionForIndexing_1_per_A; - float tolerance; - int multi; - int thread_count; - int min_peaks; - int no_check_indexed; - float reflectionRadius; /* In m^-1 */ - float customPhotonEnergy; - float customBandwidth; - float maxRefinementDisbalance; -}; - #include <stddef.h> + #include "index.h" +#include "datatemplate.h" + +extern int pinkIndexer_default_options(PinkIndexerOptions **opts_ptr); -extern int run_pinkIndexer(struct image *image, void *ipriv); +extern int run_pinkIndexer(struct image *image, void *ipriv, int n_threads); -extern void *pinkIndexer_prepare(IndexingMethod *indm, UnitCell *cell, +extern void *pinkIndexer_prepare(IndexingMethod *indm, + UnitCell *cell, struct pinkIndexer_options *pinkIndexer_opts, - struct detector *det, struct beam_params *beam); + double wavelength_estimate); extern void pinkIndexer_cleanup(void *pp); diff --git a/libcrystfel/src/taketwo.c b/libcrystfel/src/indexers/taketwo.c index a67d372c..1bccf1e0 100644 --- a/libcrystfel/src/taketwo.c +++ b/libcrystfel/src/indexers/taketwo.c @@ -4,12 +4,12 @@ * Rewrite of TakeTwo algorithm (Acta D72 (8) 956-965) for CrystFEL * * Copyright © 2016-2017 Helen Ginn - * Copyright © 2016-2020 Deutsches Elektronen-Synchrotron DESY, + * Copyright © 2016-2021 Deutsches Elektronen-Synchrotron DESY, * a research centre of the Helmholtz Association. * * Authors: * 2016-2017 Helen Ginn <helen@strubi.ox.ac.uk> - * 2016-2017 Thomas White <taw@physics.org> + * 2016-2021 Thomas White <taw@physics.org> * * This file is part of CrystFEL. * @@ -86,6 +86,10 @@ * * Clean up the mess (cleanup_taketwo_obs_vecs()) */ +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + #include <gsl/gsl_matrix.h> #include <gsl/gsl_blas.h> #include <float.h> @@ -229,7 +233,10 @@ struct TakeTwoCell /* Maximum observed vectors before TakeTwo gives up and deals with * what is already there. */ -#define MAX_OBS_VECTORS 100000 +#define MAX_OBS_VECTORS 300 + +/* Maximum number of seeds to start from in start_seeds() */ +#define MAX_SEEDS 10 /* Tolerance for two angles to be considered the same */ #define ANGLE_TOLERANCE (deg2rad(0.6)) @@ -527,7 +534,7 @@ static double matrix_trace(gsl_matrix *a) return tr; } -static char *add_ua(const char *inp, char ua) +static char *add_unique_axis(const char *inp, char ua) { char *pg = malloc(64); if ( pg == NULL ) return NULL; @@ -540,12 +547,13 @@ static char *get_chiral_holohedry(UnitCell *cell) { LatticeType lattice = cell_get_lattice_type(cell); char *pg; - char *pgout = 0; + int add_ua = 1; switch (lattice) { case L_TRICLINIC: pg = "1"; + add_ua = 0; break; case L_MONOCLINIC: @@ -554,6 +562,7 @@ static char *get_chiral_holohedry(UnitCell *cell) case L_ORTHORHOMBIC: pg = "222"; + add_ua = 0; break; case L_TETRAGONAL: @@ -562,11 +571,13 @@ static char *get_chiral_holohedry(UnitCell *cell) case L_RHOMBOHEDRAL: pg = "3_R"; + add_ua = 0; break; case L_HEXAGONAL: if ( cell_get_centering(cell) == 'H' ) { pg = "3_H"; + add_ua = 0; } else { pg = "622"; } @@ -574,6 +585,7 @@ static char *get_chiral_holohedry(UnitCell *cell) case L_CUBIC: pg = "432"; + add_ua = 0; break; default: @@ -581,26 +593,11 @@ static char *get_chiral_holohedry(UnitCell *cell) break; } - switch (lattice) - { - case L_TRICLINIC: - case L_ORTHORHOMBIC: - case L_RHOMBOHEDRAL: - case L_CUBIC: - pgout = strdup(pg); - break; - - case L_MONOCLINIC: - case L_TETRAGONAL: - case L_HEXAGONAL: - pgout = add_ua(pg, cell_get_unique_axis(cell)); - break; - - default: - break; + if ( add_ua ) { + return add_unique_axis(pg, cell_get_unique_axis(cell)); + } else { + return strdup(pg); } - - return pgout; } @@ -1553,7 +1550,8 @@ static int find_seeds(struct TakeTwoCell *cell, struct taketwo_private *tp) cell->seeds = tmp; - for (int i = 0; i < seed_num; i++) + int i; + for ( i = 0; i < seed_num; i++) { if (seeds[i].idx1 < 0 || seeds[i].idx2 < 0) { @@ -1582,10 +1580,12 @@ static unsigned int start_seeds(gsl_matrix **rotation, struct TakeTwoCell *cell) int member_num = 0; int max_members = 0; gsl_matrix *rot = NULL; + int k; + + if ( seed_num > MAX_SEEDS ) seed_num = MAX_SEEDS; /* We have seeds! Pass each of them through the seed-starter */ /* If a seed has the highest achieved membership, make note...*/ - int k; for ( k=0; k<seed_num; k++ ) { int seed_idx1 = seeds[k].idx1; int seed_idx2 = seeds[k].idx2; @@ -2102,7 +2102,8 @@ static void partial_taketwo_cleanup(struct taketwo_private *tp) { if (tp->prevSols != NULL) { - for (int i = 0; i < tp->numPrevs; i++) + int i; + for (i = 0; i < tp->numPrevs; i++) { gsl_matrix_free(tp->prevSols[i]); } @@ -2154,18 +2155,20 @@ int taketwo_index(struct image *image, void *priv) tp->xtal_num = image->n_crystals; } - /* - STATUS("Indexing %i with %i attempts, %i crystals\n", this_serial, tp->attempts, - image->n_crystals); - */ - rlps = malloc((image_feature_count(image->features)+1)*sizeof(struct rvec)); for ( i=0; i<image_feature_count(image->features); i++ ) { + + double r[3]; struct imagefeature *pk = image_get_feature(image->features, i); if ( pk == NULL ) continue; - rlps[n_rlps].u = pk->rx; - rlps[n_rlps].v = pk->ry; - rlps[n_rlps].w = pk->rz; + + detgeom_transform_coords(&image->detgeom->panels[pk->pn], + pk->fs, pk->ss, image->lambda, + 0.0, 0.0, r); + + rlps[n_rlps].u = r[0]; + rlps[n_rlps].v = r[1]; + rlps[n_rlps].w = r[2]; n_rlps++; } rlps[n_rlps].u = 0.0; @@ -2235,6 +2238,7 @@ void *taketwo_prepare(IndexingMethod *indm, struct taketwo_options *opts, if ( tp == NULL ) return NULL; tp->cell = cell; + tp->opts = opts; tp->indm = *indm; tp->serial_num = -1; tp->xtal_num = 0; @@ -2269,7 +2273,7 @@ const char *taketwo_probe(UnitCell *cell) } -static void show_help() +static void taketwo_show_help() { printf("Parameters for the TakeTwo indexing algorithm:\n" " --taketwo-member-threshold\n" @@ -2284,24 +2288,38 @@ static void show_help() } -static error_t parse_arg(int key, char *arg, struct argp_state *state) +int taketwo_default_options(TakeTwoOptions **opts_ptr) +{ + TakeTwoOptions *opts; + + opts = malloc(sizeof(struct taketwo_options)); + if ( opts == NULL ) return ENOMEM; + opts->member_thresh = -1.0; + opts->len_tol = -1.0; + opts->angle_tol = -1.0; + opts->trace_tol = -1.0; + + *opts_ptr = opts; + return 0; +} + + +static error_t taketwo_parse_arg(int key, char *arg, + struct argp_state *state) { struct taketwo_options **opts_ptr = state->input; float tmp; + int r; switch ( key ) { case ARGP_KEY_INIT : - *opts_ptr = malloc(sizeof(struct taketwo_options)); - if ( *opts_ptr == NULL ) return ENOMEM; - (*opts_ptr)->member_thresh = -1.0; - (*opts_ptr)->len_tol = -1.0; - (*opts_ptr)->angle_tol = -1.0; - (*opts_ptr)->trace_tol = -1.0; + r = taketwo_default_options(opts_ptr); + if ( r ) return r; break; case 1 : - show_help(); + taketwo_show_help(); return EINVAL; case 2 : @@ -2348,7 +2366,7 @@ static error_t parse_arg(int key, char *arg, struct argp_state *state) } -static struct argp_option options[] = { +static struct argp_option taketwo_options[] = { {"help-taketwo", 1, NULL, OPTION_NO_USAGE, "Show options for TakeTwo indexing algorithm", 99}, @@ -2361,4 +2379,5 @@ static struct argp_option options[] = { }; -struct argp taketwo_argp = { options, parse_arg, NULL, NULL, NULL, NULL, NULL }; +struct argp taketwo_argp = { taketwo_options, taketwo_parse_arg, + NULL, NULL, NULL, NULL, NULL }; diff --git a/libcrystfel/src/taketwo.h b/libcrystfel/src/indexers/taketwo.h index 2239a77a..f3157d72 100644 --- a/libcrystfel/src/taketwo.h +++ b/libcrystfel/src/indexers/taketwo.h @@ -4,7 +4,7 @@ * Rewrite of TakeTwo algorithm (Acta D72 (8) 956-965) for CrystFEL * * Copyright © 2016-2017 Helen Ginn - * Copyright © 2016-2020 Deutsches Elektronen-Synchrotron DESY, + * Copyright © 2016-2021 Deutsches Elektronen-Synchrotron DESY, * a research centre of the Helmholtz Association. * * Authors: @@ -38,9 +38,7 @@ /** \file taketwo.h */ -typedef struct taketwo_options TakeTwoOptions; -extern struct argp taketwo_argp; - +extern int taketwo_default_options(TakeTwoOptions **opts_ptr); extern void *taketwo_prepare(IndexingMethod *indm, struct taketwo_options *opts, UnitCell *cell); extern const char *taketwo_probe(UnitCell *cell); diff --git a/libcrystfel/src/xds.c b/libcrystfel/src/indexers/xds.c index ab6335db..3b2f2b87 100644 --- a/libcrystfel/src/xds.c +++ b/libcrystfel/src/indexers/xds.c @@ -3,13 +3,13 @@ * * Invoke xds for crystal autoindexing * - * Copyright © 2013-2020 Deutsches Elektronen-Synchrotron DESY, + * Copyright © 2013-2021 Deutsches Elektronen-Synchrotron DESY, * a research centre of the Helmholtz Association. * Copyright © 2013 Cornelius Gati * * Authors: * 2010-2018 Thomas White <taw@physics.org> - * 2013 Cornelius Gati <cornelius.gati@cfel.de> + * 2013 Cornelius Gati <cornelius.gati@cfel.de> * * This file is part of CrystFEL. * @@ -56,7 +56,6 @@ #include "image.h" #include "utils.h" #include "peaks.h" -#include "detector.h" #include "cell-utils.h" /** \file xds.h */ @@ -74,7 +73,7 @@ struct xds_private }; -/* Essentially the reverse of spacegroup_for_lattice(), below */ +/* Essentially the reverse of xds_spacegroup_for_lattice(), below */ static int convert_spacegroup_number(int spg, LatticeType *lt, char *cen, char *ua) { @@ -214,17 +213,22 @@ static void write_spot(struct image *image) { struct imagefeature *f; double ttx, tty, x, y; + double r[3]; f = image_get_feature(image->features, i); if ( f == NULL ) continue; if ( f->intensity <= 0 ) continue; + detgeom_transform_coords(&image->detgeom->panels[f->pn], + f->fs, f->ss, image->lambda, + 0.0, 0.0, r); + ttx = angle_between_2d(0.0, 1.0, - f->rx, 1.0/image->lambda + f->rz); + r[0], 1.0/image->lambda + r[2]); tty = angle_between_2d(0.0, 1.0, - f->ry, 1.0/image->lambda + f->rz); - if ( f->rx < 0.0 ) ttx *= -1.0; - if ( f->ry < 0.0 ) tty *= -1.0; + r[1], 1.0/image->lambda + r[2]); + if ( r[0] < 0.0 ) ttx *= -1.0; + if ( r[1] < 0.0 ) tty *= -1.0; x = tan(ttx)*FAKE_CLEN; y = tan(tty)*FAKE_CLEN; @@ -241,7 +245,7 @@ static void write_spot(struct image *image) /* Turn what we know about the unit cell into something which we can give to * XDS to make it give us only indexing results compatible with the cell. */ -static const char *spacegroup_for_lattice(UnitCell *cell) +static const char *xds_spacegroup_for_lattice(UnitCell *cell) { LatticeType latt; char centering; @@ -336,7 +340,7 @@ static int write_inp(struct image *image, struct xds_private *xp) if ( xp->indm & INDEXING_USE_LATTICE_TYPE ) { fprintf(fh, "SPACE_GROUP_NUMBER= %s\n", - spacegroup_for_lattice(xp->cell)); + xds_spacegroup_for_lattice(xp->cell)); } else { fprintf(fh, "SPACE_GROUP_NUMBER= 0\n"); } @@ -462,7 +466,7 @@ void *xds_prepare(IndexingMethod *indm, UnitCell *cell) return NULL; } - if ( (cell != NULL) && (spacegroup_for_lattice(cell) == NULL) ) { + if ( (cell != NULL) && (xds_spacegroup_for_lattice(cell) == NULL) ) { ERROR("Don't know how to ask XDS for your cell.\n"); return NULL; } diff --git a/libcrystfel/src/xds.h b/libcrystfel/src/indexers/xds.h index bb96feca..49a83ff5 100644 --- a/libcrystfel/src/xds.h +++ b/libcrystfel/src/indexers/xds.h @@ -3,13 +3,13 @@ * * Invoke xds for crystal autoindexing * - * Copyright © 2013-2020 Deutsches Elektronen-Synchrotron DESY, + * Copyright © 2013-2021 Deutsches Elektronen-Synchrotron DESY, * a research centre of the Helmholtz Association. * Copyright © 2013 Cornelius Gati * * Authors: - * 2010-2013,2017 Thomas White <taw@physics.org> - * 2013 Cornelius Gati <cornelius.gati@cfel.de> + * 2010-2017 Thomas White <taw@physics.org> + * 2013 Cornelius Gati <cornelius.gati@cfel.de> * * This file is part of CrystFEL. * @@ -31,10 +31,6 @@ #ifndef XDS_H #define XDS_H -#ifdef HAVE_CONFIG_H -#include <config.h> -#endif - #include "cell.h" #include "index.h" diff --git a/libcrystfel/src/xgandalf.c b/libcrystfel/src/indexers/xgandalf.c index 9135c2f3..6b09f65c 100644 --- a/libcrystfel/src/xgandalf.c +++ b/libcrystfel/src/indexers/xgandalf.c @@ -3,7 +3,7 @@ * * Interface to XGANDALF indexer * - * Copyright © 2017-2020 Deutsches Elektronen-Synchrotron DESY, + * Copyright © 2017-2021 Deutsches Elektronen-Synchrotron DESY, * a research centre of the Helmholtz Association. * * Authors: @@ -26,6 +26,10 @@ * */ +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + #include "xgandalf.h" #include <stdlib.h> @@ -80,21 +84,28 @@ static void makeRightHanded(UnitCell* cell); int run_xgandalf(struct image *image, void *ipriv) { + int i; struct xgandalf_private_data *xgandalf_private_data = (struct xgandalf_private_data*) ipriv; reciprocalPeaks_1_per_A_t *reciprocalPeaks_1_per_A = &(xgandalf_private_data->reciprocalPeaks_1_per_A); int peakCountMax = image_feature_count(image->features); reciprocalPeaks_1_per_A->peakCount = 0; - for (int i = 0; i < peakCountMax && i < MAX_PEAK_COUNT_FOR_INDEXER; i++) { + for ( i = 0; i < peakCountMax && i < MAX_PEAK_COUNT_FOR_INDEXER; i++) { struct imagefeature *f; + double r[3]; + f = image_get_feature(image->features, i); if (f == NULL) { continue; } - reciprocalPeaks_1_per_A->coordinates_x[reciprocalPeaks_1_per_A->peakCount] = f->rx * 1e-10; - reciprocalPeaks_1_per_A->coordinates_y[reciprocalPeaks_1_per_A->peakCount] = f->ry * 1e-10; - reciprocalPeaks_1_per_A->coordinates_z[reciprocalPeaks_1_per_A->peakCount] = f->rz * 1e-10; + detgeom_transform_coords(&image->detgeom->panels[f->pn], + f->fs, f->ss, image->lambda, + 0.0, 0.0, r); + + reciprocalPeaks_1_per_A->coordinates_x[reciprocalPeaks_1_per_A->peakCount] = r[0] * 1e-10; + reciprocalPeaks_1_per_A->coordinates_y[reciprocalPeaks_1_per_A->peakCount] = r[1] * 1e-10; + reciprocalPeaks_1_per_A->coordinates_z[reciprocalPeaks_1_per_A->peakCount] = r[2] * 1e-10; reciprocalPeaks_1_per_A->peakCount++; } @@ -112,7 +123,7 @@ int run_xgandalf(struct image *image, void *ipriv) } int goodLatticesCount = assembledLatticesCount; - for (int i = 0; i < assembledLatticesCount && i < 1; i++) { + for ( i = 0; i < assembledLatticesCount && i < 1; i++) { reorderLattice(&(xgandalf_private_data->sampleRealLattice_A), &assembledLattices[i]); @@ -346,7 +357,7 @@ const char *xgandalf_probe(UnitCell *cell) #endif // HAVE_XGANDALF -static void show_help() +static void xgandalf_show_help() { printf("Parameters for the TakeTwo indexing algorithm:\n" " --xgandalf-sampling-pitch\n" @@ -378,26 +389,41 @@ static void show_help() } -static error_t parse_arg(int key, char *arg, struct argp_state *state) +int xgandalf_default_options(XGandalfOptions **opts_ptr) +{ + XGandalfOptions *opts; + + opts = malloc(sizeof(struct xgandalf_options)); + if ( opts == NULL ) return ENOMEM; + + opts->sampling_pitch = 6; + opts->grad_desc_iterations = 4; + opts->tolerance = 0.02; + opts->no_deviation_from_provided_cell = 0; + opts->minLatticeVectorLength_A = 30; + opts->maxLatticeVectorLength_A = 250; + opts->maxPeaksForIndexing = 250; + + *opts_ptr = opts; + return 0; +} + + +static error_t xgandalf_parse_arg(int key, char *arg, + struct argp_state *state) { struct xgandalf_options **opts_ptr = state->input; + int r; switch ( key ) { case ARGP_KEY_INIT : - *opts_ptr = malloc(sizeof(struct xgandalf_options)); - if ( *opts_ptr == NULL ) return ENOMEM; - (*opts_ptr)->sampling_pitch = 6; - (*opts_ptr)->grad_desc_iterations = 4; - (*opts_ptr)->tolerance = 0.02; - (*opts_ptr)->no_deviation_from_provided_cell = 0; - (*opts_ptr)->minLatticeVectorLength_A = 30; - (*opts_ptr)->maxLatticeVectorLength_A = 250; - (*opts_ptr)->maxPeaksForIndexing = 250; + r = xgandalf_default_options(opts_ptr); + if ( r ) return r; break; case 1 : - show_help(); + xgandalf_show_help(); return EINVAL; case 2 : @@ -457,7 +483,7 @@ static error_t parse_arg(int key, char *arg, struct argp_state *state) } -static struct argp_option options[] = { +static struct argp_option xgandalf_options[] = { {"help-xgandalf", 1, NULL, OPTION_NO_USAGE, "Show options for XGANDALF indexing algorithm", 99}, @@ -488,4 +514,5 @@ static struct argp_option options[] = { }; -struct argp xgandalf_argp = { options, parse_arg, NULL, NULL, NULL, NULL, NULL }; +struct argp xgandalf_argp = { xgandalf_options, xgandalf_parse_arg, + NULL, NULL, NULL, NULL, NULL }; diff --git a/libcrystfel/src/xgandalf.h b/libcrystfel/src/indexers/xgandalf.h index 07fcba01..79078410 100644 --- a/libcrystfel/src/xgandalf.h +++ b/libcrystfel/src/indexers/xgandalf.h @@ -3,7 +3,7 @@ * * Interface to XGANDALF indexer * - * Copyright © 2017-2020 Deutsches Elektronen-Synchrotron DESY, + * Copyright © 2017-2021 Deutsches Elektronen-Synchrotron DESY, * a research centre of the Helmholtz Association. * * Authors: @@ -29,10 +29,6 @@ #ifndef LIBCRYSTFEL_SRC_XGANDALF_H #define LIBCRYSTFEL_SRC_XGANDALF_H -#ifdef HAVE_CONFIG_H -#include <config.h> -#endif - #include <stddef.h> #include <argp.h> @@ -41,11 +37,10 @@ * XGANDALF indexer interface */ -typedef struct xgandalf_options XGandalfOptions; -extern struct argp xgandalf_argp; - #include "index.h" +extern int xgandalf_default_options(XGandalfOptions **opts_ptr); + extern int run_xgandalf(struct image *image, void *ipriv); extern void *xgandalf_prepare(IndexingMethod *indm, UnitCell *cell, diff --git a/libcrystfel/src/integer_matrix.c b/libcrystfel/src/integer_matrix.c index 43f41eeb..7fcd07c4 100644 --- a/libcrystfel/src/integer_matrix.c +++ b/libcrystfel/src/integer_matrix.c @@ -3,11 +3,11 @@ * * A small integer matrix library * - * Copyright © 2012-2020 Deutsches Elektronen-Synchrotron DESY, + * Copyright © 2012-2021 Deutsches Elektronen-Synchrotron DESY, * a research centre of the Helmholtz Association. * * Authors: - * 2012 Thomas White <taw@physics.org> + * 2012-2020 Thomas White <taw@physics.org> * * This file is part of CrystFEL. * @@ -253,8 +253,9 @@ void intmat_zero(IntegerMatrix *m) } -static IntegerMatrix *delete_row_and_column(const IntegerMatrix *m, - unsigned int di, unsigned int dj) +static IntegerMatrix *intmat_delete_row_and_column(const IntegerMatrix *m, + unsigned int di, + unsigned int dj) { IntegerMatrix *n; unsigned int i, j; @@ -280,13 +281,13 @@ static IntegerMatrix *delete_row_and_column(const IntegerMatrix *m, } -static signed int cofactor(const IntegerMatrix *m, - unsigned int i, unsigned int j) +static signed int intmat_cofactor(const IntegerMatrix *m, + unsigned int i, unsigned int j) { IntegerMatrix *n; signed int t, C; - n = delete_row_and_column(m, i, j); + n = intmat_delete_row_and_column(m, i, j); if ( n == NULL ) { fprintf(stderr, "Failed to allocate matrix.\n"); return 0; @@ -324,7 +325,7 @@ signed int intmat_det(const IntegerMatrix *m) i = 0; /* Fixed */ for ( j=0; j<m->cols; j++ ) { - det += intmat_get(m, i, j) * cofactor(m, i, j); + det += intmat_get(m, i, j) * intmat_cofactor(m, i, j); } @@ -343,7 +344,7 @@ static IntegerMatrix *intmat_cofactors(const IntegerMatrix *m) for ( i=0; i<n->rows; i++ ) { for ( j=0; j<n->cols; j++ ) { - intmat_set(n, i, j, cofactor(m, i, j)); + intmat_set(n, i, j, intmat_cofactor(m, i, j)); } } diff --git a/libcrystfel/src/integer_matrix.h b/libcrystfel/src/integer_matrix.h index 024480c3..2ebef300 100644 --- a/libcrystfel/src/integer_matrix.h +++ b/libcrystfel/src/integer_matrix.h @@ -3,11 +3,11 @@ * * A small integer matrix library * - * Copyright © 2012-2020 Deutsches Elektronen-Synchrotron DESY, + * Copyright © 2012-2021 Deutsches Elektronen-Synchrotron DESY, * a research centre of the Helmholtz Association. * * Authors: - * 2012 Thomas White <taw@physics.org> + * 2012-2019 Thomas White <taw@physics.org> * * This file is part of CrystFEL. * @@ -29,10 +29,6 @@ #ifndef INTEGER_MATRIX_H #define INTEGER_MATRIX_H -#ifdef HAVE_CONFIG_H -#include <config.h> -#endif - /** * \file integer_matrix.h * Matrix type containing only integers diff --git a/libcrystfel/src/integration.c b/libcrystfel/src/integration.c index 3d8258c1..4c813a11 100644 --- a/libcrystfel/src/integration.c +++ b/libcrystfel/src/integration.c @@ -3,11 +3,11 @@ * * Integration of intensities * - * Copyright © 2012-2020 Deutsches Elektronen-Synchrotron DESY, + * Copyright © 2012-2021 Deutsches Elektronen-Synchrotron DESY, * a research centre of the Helmholtz Association. * * Authors: - * 2010-2016 Thomas White <taw@physics.org> + * 2010-2021 Thomas White <taw@physics.org> * * This file is part of CrystFEL. * @@ -52,6 +52,7 @@ #include "image.h" #include "peaks.h" #include "integration.h" +#include "detgeom.h" /** \file integration.h */ @@ -110,7 +111,7 @@ struct peak_box enum boxmask_val *bm; /* Box mask */ int pn; /* Panel number */ - struct panel *p; /* The panel itself */ + struct detgeom_panel *p; /* The panel itself */ /* Fitted background parameters */ double a; @@ -273,9 +274,6 @@ static void show_peak_box(struct intcontext *ic, struct peak_box *bx, get_indices(bx->refl, &h, &k, &l); get_detector_pos(bx->refl, &fs, &ss); - /* Convert coordinates to match arrangement of panels in HDF5 file */ - fs = fs + bx->p->orig_min_fs; - ss = ss + bx->p->orig_min_ss; printw("Indices %i %i %i\nPanel %s\nPosition fs = %.1f, ss = %.1f\n\n", h, k, l, bx->p->name, fs, ss); @@ -432,33 +430,106 @@ static int alloc_boxes(struct intcontext *ic, int new_max_boxes) } -static int init_intcontext(struct intcontext *ic) +static void setup_ring_masks(struct intcontext *ic, + double ir_inn, + double ir_mid, + double ir_out) { - int i; + double lim_sq, out_lim_sq, mid_lim_sq; + int p, q; + + lim_sq = pow(ir_inn, 2.0); + mid_lim_sq = pow(ir_mid, 2.0); + out_lim_sq = pow(ir_out, 2.0); + + for ( p=0; p<ic->w; p++ ) { + for ( q=0; q<ic->w; q++ ) { + + int rsq; + + rsq = (p-ic->halfw)*(p-ic->halfw) + (q-ic->halfw)*(q-ic->halfw); + + if ( rsq > out_lim_sq ) { + /* Outside outer radius */ + ic->bm[p + ic->w*q] = BM_IG; + } else { + + if ( rsq >= mid_lim_sq ) { + /* Inside outer radius, outside middle radius */ + ic->bm[p + ic->w*q] = BM_BG; + } else if ( rsq <= lim_sq ) { + /* Inside inner radius */ + ic->bm[p + ic->w*q] = BM_PK; + } else { + /* Outside inner radius, inside middle radius */ + ic->bm[p + ic->w*q] = BM_IG; + } + } + + } + } + +} + + +void intcontext_set_diag(struct intcontext *ic, + IntDiag int_diag, + signed int idh, + signed int idk, + signed int idl) +{ + ic->int_diag = int_diag; + ic->int_diag_h = idh; + ic->int_diag_k = idk; + ic->int_diag_l = idl; +} + + +struct intcontext *intcontext_new(struct image *image, + UnitCell *cell, + IntegrationMethod meth, + int ir_inn, int ir_mid, int ir_out, + int **masks) +{ + int i; + struct intcontext *ic; + + ic = malloc(sizeof(struct intcontext)); + if ( ic == NULL ) return NULL; + + ic->halfw = ir_out; + ic->image = image; + ic->k = 1.0/image->lambda; + ic->meth = meth; + ic->n_saturated = 0; + ic->n_implausible = 0; + ic->cell = cell; + ic->masks = masks; + ic->int_diag = INTDIAG_NONE; ic->w = 2*ic->halfw + 1; ic->bm = malloc(ic->w * ic->w * sizeof(enum boxmask_val)); if ( ic->bm == NULL ) { ERROR("Failed to allocate box mask.\n"); - return 1; + return NULL; } /* How many reference profiles? */ ic->n_reference_profiles = 1; ic->reference_profiles = calloc(ic->n_reference_profiles, sizeof(double *)); - if ( ic->reference_profiles == NULL ) return 1; + if ( ic->reference_profiles == NULL ) return NULL; ic->reference_den = calloc(ic->n_reference_profiles, sizeof(double *)); - if ( ic->reference_den == NULL ) return 1; + if ( ic->reference_den == NULL ) return NULL; ic->n_profiles_in_reference = calloc(ic->n_reference_profiles, sizeof(int)); - if ( ic->n_profiles_in_reference == NULL ) return 1; + if ( ic->n_profiles_in_reference == NULL ) return NULL; for ( i=0; i<ic->n_reference_profiles; i++ ) { ic->reference_profiles[i] = malloc(ic->w*ic->w*sizeof(double)); - if ( ic->reference_profiles[i] == NULL ) return 1; + if ( ic->reference_profiles[i] == NULL ) return NULL; ic->reference_den[i] = malloc(ic->w*ic->w*sizeof(double)); - if ( ic->reference_den[i] == NULL ) return 1; + if ( ic->reference_den[i] == NULL ) return NULL; } zero_profiles(ic); @@ -466,14 +537,16 @@ static int init_intcontext(struct intcontext *ic) ic->n_boxes = 0; ic->max_boxes = 0; if ( alloc_boxes(ic, 32) ) { - return 1; + return NULL; } - return 0; + setup_ring_masks(ic, ir_inn, ir_mid, ir_out); + + return ic; } -static void free_intcontext(struct intcontext *ic) +void intcontext_free(struct intcontext *ic) { int i; @@ -494,47 +567,6 @@ static void free_intcontext(struct intcontext *ic) } -static void setup_ring_masks(struct intcontext *ic, - double ir_inn, double ir_mid, double ir_out) -{ - double lim_sq, out_lim_sq, mid_lim_sq; - int p, q; - - lim_sq = pow(ir_inn, 2.0); - mid_lim_sq = pow(ir_mid, 2.0); - out_lim_sq = pow(ir_out, 2.0); - - for ( p=0; p<ic->w; p++ ) { - for ( q=0; q<ic->w; q++ ) { - - int rsq; - - rsq = (p-ic->halfw)*(p-ic->halfw) + (q-ic->halfw)*(q-ic->halfw); - - if ( rsq > out_lim_sq ) { - /* Outside outer radius */ - ic->bm[p + ic->w*q] = BM_IG; - } else { - - if ( rsq >= mid_lim_sq ) { - /* Inside outer radius, outside middle radius */ - ic->bm[p + ic->w*q] = BM_BG; - } else if ( rsq <= lim_sq ) { - /* Inside inner radius */ - ic->bm[p + ic->w*q] = BM_PK; - } else { - /* Outside inner radius, inside middle radius */ - ic->bm[p + ic->w*q] = BM_IG; - } - - } - - } - } - -} - - static struct peak_box *add_box(struct intcontext *ic) { int idx; @@ -804,9 +836,6 @@ static int check_box(struct intcontext *ic, struct peak_box *bx, int *sat) for ( q=0; q<ic->w; q++ ) { int fs, ss; - double hd, kd, ld; - signed int h, k, l; - struct rvec dv; float lsat; fs = bx->cfs + p; @@ -862,22 +891,11 @@ static int check_box(struct intcontext *ic, struct peak_box *bx, int *sat) if ( sat != NULL ) *sat = 1; } - /* Ignore if this pixel is closer to the next reciprocal lattice - * point */ - dv = get_q_for_panel(bx->p, fs, ss, NULL, ic->k); - hd = dv.u * adx + dv.v * ady + dv.w * adz; - kd = dv.u * bdx + dv.v * bdy + dv.w * bdz; - ld = dv.u * cdx + dv.v * cdy + dv.w * cdz; - h = lrint(hd); - k = lrint(kd); - l = lrint(ld); - if ( (h != hr) || (k != kr) || (l != lr) ) { - bx->bm[p+ic->w*q] = BM_BH; - } - + /* Find brightest pixel */ if ( (bx->bm[p+ic->w*q] != BM_IG) && (bx->bm[p+ic->w*q] != BM_BH) - && (boxi(ic, bx, p, q) > bx->peak) ) { + && (boxi(ic, bx, p, q) > bx->peak) ) + { bx->peak = boxi(ic, bx, p, q); } @@ -1273,7 +1291,6 @@ static void setup_profile_boxes(struct intcontext *ic, RefList *list) double pfs, pss; struct peak_box *bx; int pn; - struct panel *p; int fid_fs, fid_ss; /* Center coordinates, rounded, * in overall data block */ int cfs, css; /* Corner coordinates */ @@ -1283,12 +1300,7 @@ static void setup_profile_boxes(struct intcontext *ic, RefList *list) set_redundancy(refl, 0); get_detector_pos(refl, &pfs, &pss); - p = get_panel(refl); - pn = panel_number(ic->image->det, p); - if ( pn == ic->image->det->n_panels ) { - ERROR("Couldn't find panel %p\n", p); - continue; - } + pn = get_panel_number(refl); /* Explicit truncation of digits after the decimal point. * This is actually the correct thing to do here, not @@ -1307,7 +1319,7 @@ static void setup_profile_boxes(struct intcontext *ic, RefList *list) bx->refl = refl; bx->cfs = cfs; bx->css = css; - bx->p = p; + bx->p = &ic->image->detgeom->panels[pn]; bx->pn = pn; /* Which reference profile? */ @@ -1345,68 +1357,58 @@ static void setup_profile_boxes(struct intcontext *ic, RefList *list) } -static void integrate_prof2d(IntegrationMethod meth, - Crystal *cr, struct image *image, IntDiag int_diag, - signed int idh, signed int idk, signed int idl, - double ir_inn, double ir_mid, double ir_out, - pthread_mutex_t *term_lock, int **masks) +void integrate_prof2d(IntegrationMethod meth, + Crystal *cr, struct image *image, IntDiag int_diag, + signed int idh, signed int idk, signed int idl, + double ir_inn, double ir_mid, double ir_out, + pthread_mutex_t *term_lock, int **masks) { RefList *list; UnitCell *cell; - struct intcontext ic; + struct intcontext *ic; int i; list = crystal_get_reflections(cr); cell = crystal_get_cell(cr); - ic.halfw = ir_out; - ic.image = image; - ic.k = 1.0/image->lambda; - ic.meth = meth; - ic.n_saturated = 0; - ic.n_implausible = 0; - ic.cell = cell; - ic.int_diag = int_diag; - ic.int_diag_h = idh; - ic.int_diag_k = idk; - ic.int_diag_l = idl; - ic.masks = masks; - if ( init_intcontext(&ic) ) { + ic = intcontext_new(image, cell, meth, + ir_inn, ir_mid, ir_out, + masks); + if ( ic == NULL ) { ERROR("Failed to initialise integration.\n"); return; } - setup_ring_masks(&ic, ir_inn, ir_mid, ir_out); - setup_profile_boxes(&ic, list); - calculate_reference_profiles(&ic); + intcontext_set_diag(ic, int_diag, idh, idk, idl); + setup_profile_boxes(ic, list); + calculate_reference_profiles(ic); - for ( i=0; i<ic.n_reference_profiles; i++ ) { - if ( ic.n_profiles_in_reference[i] == 0 ) { + for ( i=0; i<ic->n_reference_profiles; i++ ) { + if ( ic->n_profiles_in_reference[i] == 0 ) { ERROR("Reference profile %i has no contributions.\n", i); - free_intcontext(&ic); + intcontext_free(ic); return; } } - for ( i=0; i<ic.n_boxes; i++ ) { + for ( i=0; i<ic->n_boxes; i++ ) { struct peak_box *bx; - bx = &ic.boxes[i]; - integrate_prof2d_once(&ic, bx, term_lock); + bx = &ic->boxes[i]; + integrate_prof2d_once(ic, bx, term_lock); } - free_intcontext(&ic); + intcontext_free(ic); } -static int integrate_rings_once(Reflection *refl, struct image *image, - struct intcontext *ic, UnitCell *cell, - pthread_mutex_t *term_lock) +int integrate_rings_once(Reflection *refl, + struct intcontext *ic, + pthread_mutex_t *term_lock) { double pfs, pss; struct peak_box *bx; int pn; - struct panel *p; int fid_fs, fid_ss; /* Center coordinates, rounded, * in overall data block */ int cfs, css; /* Corner coordinates */ @@ -1419,12 +1421,7 @@ static int integrate_rings_once(Reflection *refl, struct image *image, set_redundancy(refl, 0); get_detector_pos(refl, &pfs, &pss); - p = get_panel(refl); - pn = panel_number(image->det, p); - if ( pn == image->det->n_panels ) { - ERROR("Couldn't find panel %p\n", p); - return 1; - } + pn = get_panel_number(refl); /* Explicit truncation of digits after the decimal point. * This is actually the correct thing to do here, not @@ -1442,7 +1439,7 @@ static int integrate_rings_once(Reflection *refl, struct image *image, bx->refl = refl; bx->cfs = cfs; bx->css = css; - bx->p = p; + bx->p = &ic->image->detgeom->panels[pn]; bx->pn = pn; if ( ic->meth & INTEGRATION_CENTER ) { @@ -1521,17 +1518,7 @@ static int integrate_rings_once(Reflection *refl, struct image *image, } -static int compare_double(const void *av, const void *bv) -{ - double a = *(double *)av; - double b = *(double *)bv; - if ( a > b ) return 1; - if ( a < b ) return -1; - return 0; -} - - -static double estimate_resolution(UnitCell *cell, ImageFeatureList *flist) +static double estimate_resolution(Crystal *cr, struct image *image) { int i; const double min_dist = 0.25; @@ -1540,6 +1527,9 @@ static double estimate_resolution(UnitCell *cell, ImageFeatureList *flist) int n_acc = 0; int max_acc = 1024; int n; + double dx, dy; + UnitCell *cell; + acc = malloc(max_acc*sizeof(double)); if ( acc == NULL ) { @@ -1547,27 +1537,36 @@ static double estimate_resolution(UnitCell *cell, ImageFeatureList *flist) return INFINITY; } - for ( i=0; i<image_feature_count(flist); i++ ) { + cell = crystal_get_cell(cr); + crystal_get_det_shift(cr, &dx, &dy); + + for ( i=0; i<image_feature_count(image->features); i++ ) { struct imagefeature *f; double h, k, l, hd, kd, ld; - - /* Assume all image "features" are genuine peaks */ - f = image_get_feature(flist, i); - if ( f == NULL ) continue; - double ax, ay, az; double bx, by, bz; double cx, cy, cz; + double r[3]; + + /* Assume all image "features" are genuine peaks */ + f = image_get_feature(image->features, i); + if ( f == NULL ) continue; cell_get_cartesian(cell, - &ax, &ay, &az, &bx, &by, &bz, &cx, &cy, &cz); + &ax, &ay, &az, + &bx, &by, &bz, + &cx, &cy, &cz); + + detgeom_transform_coords(&image->detgeom->panels[f->pn], + f->fs, f->ss, image->lambda, + dx, dy, r); /* Decimal and fractional Miller indices of nearest * reciprocal lattice point */ - hd = f->rx * ax + f->ry * ay + f->rz * az; - kd = f->rx * bx + f->ry * by + f->rz * bz; - ld = f->rx * cx + f->ry * cy + f->rz * cz; + hd = r[0] * ax + r[1] * ay + r[2] * az; + kd = r[0] * bx + r[1] * by + r[2] * bz; + ld = r[0] * cx + r[1] * cy + r[2] * cz; h = lrint(hd); k = lrint(kd); l = lrint(ld); @@ -1619,49 +1618,39 @@ static void integrate_rings(IntegrationMethod meth, Reflection *refl; RefListIterator *iter; UnitCell *cell; - struct intcontext ic; + struct intcontext *ic; int n_rej = 0; list = crystal_get_reflections(cr); cell = crystal_get_cell(cr); - ic.halfw = ir_out; - ic.image = image; - ic.k = 1.0/image->lambda; - ic.n_saturated = 0; - ic.n_implausible = 0; - ic.cell = cell; - ic.ir_inn = ir_inn; - ic.ir_mid = ir_mid; - ic.ir_out = ir_out; - ic.int_diag = int_diag; - ic.int_diag_h = idh; - ic.int_diag_k = idk; - ic.int_diag_l = idl; - ic.meth = meth; - ic.masks = masks; - if ( init_intcontext(&ic) ) { + ic = intcontext_new(image, cell, meth, + ir_inn, ir_mid, ir_out, + masks); + if ( ic == NULL ) { ERROR("Failed to initialise integration.\n"); return; } - setup_ring_masks(&ic, ir_inn, ir_mid, ir_out); + + intcontext_set_diag(ic, int_diag, idh, idk, idl); for ( refl = first_refl(list, &iter); refl != NULL; refl = next_refl(refl, iter) ) { - n_rej += integrate_rings_once(refl, image, &ic, cell, term_lock); + n_rej += integrate_rings_once(refl, ic, + term_lock); } - free_intcontext(&ic); + intcontext_free(ic); if ( n_rej > 0 ) { ERROR("WARNING: %i reflections could not be integrated\n", n_rej); } - crystal_set_num_saturated_reflections(cr, ic.n_saturated); - crystal_set_num_implausible_reflections(cr, ic.n_implausible); + crystal_set_num_saturated_reflections(cr, ic->n_saturated); + crystal_set_num_implausible_reflections(cr, ic->n_implausible); } @@ -1673,7 +1662,7 @@ void integrate_all_5(struct image *image, IntegrationMethod meth, pthread_mutex_t *term_lock, int overpredict) { int i; - int *masks[image->det->n_panels]; + int *masks[image->detgeom->n_panels]; /* Predict all reflections */ for ( i=0; i<image->n_crystals; i++ ) { @@ -1687,8 +1676,7 @@ void integrate_all_5(struct image *image, IntegrationMethod meth, saved_R * 5); } - res = estimate_resolution(crystal_get_cell(image->crystals[i]), - image->features); + res = estimate_resolution(image->crystals[i], image); crystal_set_resolution_limit(image->crystals[i], res); list = predict_to_res(image->crystals[i], res+push_res); @@ -1700,8 +1688,9 @@ void integrate_all_5(struct image *image, IntegrationMethod meth, } - for ( i=0; i<image->det->n_panels; i++ ) { - masks[i] = make_BgMask(image, &image->det->panels[i], ir_inn); + for ( i=0; i<image->detgeom->n_panels; i++ ) { + masks[i] = make_BgMask(image, &image->detgeom->panels[i], + i, ir_inn); } for ( i=0; i<image->n_crystals; i++ ) { @@ -1737,7 +1726,7 @@ void integrate_all_5(struct image *image, IntegrationMethod meth, } - for ( i=0; i<image->det->n_panels; i++ ) { + for ( i=0; i<image->detgeom->n_panels; i++ ) { free(masks[i]); } } @@ -1852,4 +1841,3 @@ IntegrationMethod integration_method(const char *str, int *err) return meth; } - diff --git a/libcrystfel/src/integration.h b/libcrystfel/src/integration.h index 11e67dbe..f230201d 100644 --- a/libcrystfel/src/integration.h +++ b/libcrystfel/src/integration.h @@ -3,11 +3,11 @@ * * Integration of intensities * - * Copyright © 2012-2020 Deutsches Elektronen-Synchrotron DESY, + * Copyright © 2012-2021 Deutsches Elektronen-Synchrotron DESY, * a research centre of the Helmholtz Association. * * Authors: - * 2010-2015 Thomas White <taw@physics.org> + * 2010-2020 Thomas White <taw@physics.org> * * This file is part of CrystFEL. * @@ -29,10 +29,6 @@ #ifndef INTEGRATION_H #define INTEGRATION_H -#ifdef HAVE_CONFIG_H -#include <config.h> -#endif - #include "geometry.h" /** @@ -113,8 +109,22 @@ typedef enum { extern "C" { #endif +struct intcontext; + extern IntegrationMethod integration_method(const char *t, int *err); +extern struct intcontext *intcontext_new(struct image *image, + UnitCell *cell, + IntegrationMethod meth, + int ir_inn, int ir_mid, int ir_out, + int **masks); + +extern int integrate_rings_once(Reflection *refl, + struct intcontext *ic, + pthread_mutex_t *term_lock); + +extern void intcontext_free(struct intcontext *ic); + extern void integrate_all(struct image *image, IntegrationMethod meth, double ir_inn, double ir_mid, double ir_out, IntDiag int_diag, diff --git a/libcrystfel/src/libcrystfel-version.c.cmake.in b/libcrystfel/src/libcrystfel-version.c.cmake.in new file mode 100644 index 00000000..513d7871 --- /dev/null +++ b/libcrystfel/src/libcrystfel-version.c.cmake.in @@ -0,0 +1,4 @@ +const char *libcrystfel_version_string() +{ + return "${CRYSTFEL_VERSION}"; +} diff --git a/libcrystfel/src/libcrystfel-version.c.in b/libcrystfel/src/libcrystfel-version.c.in new file mode 100644 index 00000000..1cde75f7 --- /dev/null +++ b/libcrystfel/src/libcrystfel-version.c.in @@ -0,0 +1,6 @@ +#define LIBCRYSTFEL_VERSION_H + +const char *libcrystfel_version_string() +{ + return "@VCS_TAG@"; +} diff --git a/libcrystfel/src/libcrystfel-version.h b/libcrystfel/src/libcrystfel-version.h new file mode 100644 index 00000000..837b8299 --- /dev/null +++ b/libcrystfel/src/libcrystfel-version.h @@ -0,0 +1,5 @@ +#ifndef LIBCRYSTFEL_VERSION_H +#define LIBCRYSTFEL_VERSION_H +extern const char *libcrystfel_version_string(void); +extern const char *libcrystfel_licence_string(void); +#endif diff --git a/libcrystfel/src/peakfinder8.c b/libcrystfel/src/peakfinder8.c index c3f7c391..641a154b 100644 --- a/libcrystfel/src/peakfinder8.c +++ b/libcrystfel/src/peakfinder8.c @@ -3,13 +3,14 @@ * * The peakfinder8 algorithm * - * Copyright © 2012-2020 Deutsches Elektronen-Synchrotron DESY, + * Copyright © 2012-2021 Deutsches Elektronen-Synchrotron DESY, * a research centre of the Helmholtz Association. * * Authors: - * 2017 Valerio Mariani <valerio.mariani@desy.de> - * 2017 Anton Barty <anton.barty@desy.de> - * 2017 Oleksandr Yefanov <oleksandr.yefanov@desy.de> + * 2017-2021 Thomas White <taw@physics.org> + * 2017 Valerio Mariani <valerio.mariani@desy.de> + * 2017 Anton Barty <anton.barty@desy.de> + * 2017 Oleksandr Yefanov <oleksandr.yefanov@desy.de> * * This file is part of CrystFEL. * @@ -37,6 +38,8 @@ #include <stdlib.h> #include "peakfinder8.h" +#include "detgeom.h" +#include "image.h" /** \file peakfinder8.h */ @@ -101,10 +104,10 @@ struct peakfinder_peak_data // CrystFEL-only block 2 -static struct radius_maps *compute_radius_maps(struct detector *det) +static struct radius_maps *compute_radius_maps(struct detgeom *det) { int i, u, iss, ifs; - struct panel p; + struct detgeom_panel p; struct radius_maps *rm = NULL; rm = (struct radius_maps *)malloc(sizeof(struct radius_maps)); @@ -173,14 +176,14 @@ static struct peakfinder_mask *create_peakfinder_mask(struct image *img, struct peakfinder_mask *msk; msk = (struct peakfinder_mask *)malloc(sizeof(struct peakfinder_mask)); - msk->masks =(char **) malloc(img->det->n_panels*sizeof(char*)); - msk->n_masks = img->det->n_panels; - for ( i=0; i<img->det->n_panels; i++) { + msk->masks =(char **) malloc(img->detgeom->n_panels*sizeof(char*)); + msk->n_masks = img->detgeom->n_panels; + for ( i=0; i<img->detgeom->n_panels; i++) { - struct panel p; + struct detgeom_panel p; int iss, ifs; - p = img->det->panels[i]; + p = img->detgeom->panels[i]; msk->masks[i] = (char *)calloc(p.w*p.h,sizeof(char)); @@ -1040,14 +1043,10 @@ int peakfinder8(struct image *img, int max_n_peaks, iterations = 5; - if ( img-> det == NULL) { - return 1; - } + if ( img->detgeom == NULL) return 1; - rmaps = compute_radius_maps(img->det); - if ( rmaps == NULL ) { - return 1; - } + rmaps = compute_radius_maps(img->detgeom); + if ( rmaps == NULL ) return 1; pfmask = create_peakfinder_mask(img, rmaps, min_res, max_res); if ( pfmask == NULL ) { @@ -1055,18 +1054,18 @@ int peakfinder8(struct image *img, int max_n_peaks, return 1; } - pfdata = allocate_panel_data(img->det->n_panels); + pfdata = allocate_panel_data(img->detgeom->n_panels); if ( pfdata == NULL) { free_radius_maps(rmaps); free_peakfinder_mask(pfmask); return 1; } - for ( pi=0 ; pi<img->det->n_panels ; pi++ ) { - pfdata->panel_h[pi] = img->det->panels[pi].h; - pfdata->panel_w[pi] = img->det->panels[pi].w; + for ( pi=0 ; pi<img->detgeom->n_panels ; pi++ ) { + pfdata->panel_h[pi] = img->detgeom->panels[pi].h; + pfdata->panel_w[pi] = img->detgeom->panels[pi].w; pfdata->panel_data[pi] = img->dp[pi]; - pfdata->num_panels = img->det->n_panels; + pfdata->num_panels = img->detgeom->n_panels; } max_r = -1e9; @@ -1139,7 +1138,7 @@ int peakfinder8(struct image *img, int max_n_peaks, remaining_max_num_peaks = max_n_peaks; - for ( pi=0 ; pi<img->det->n_panels ; pi++) { + for ( pi=0 ; pi<img->detgeom->n_panels ; pi++) { int peaks_to_add; int pki; @@ -1147,10 +1146,6 @@ int peakfinder8(struct image *img, int max_n_peaks, num_found_peaks = 0; - if ( img->det->panels[pi].no_index ) { - continue; - } - ret = peakfinder8_base(rstats->roffset, rstats->rthreshold, pfdata->panel_data[pi], @@ -1192,9 +1187,9 @@ int peakfinder8(struct image *img, int max_n_peaks, for ( pki=0 ; pki<peaks_to_add ; pki++ ) { - struct panel *p; + struct detgeom_panel *p; - p = &img->det->panels[pi]; + p = &img->detgeom->panels[pi]; if ( pkdata->max_i[pki] > p->max_adu ) { if ( !use_saturated ) { @@ -1205,9 +1200,7 @@ int peakfinder8(struct image *img, int max_n_peaks, image_add_feature(img->features, pkdata->com_fs[pki]+0.5, pkdata->com_ss[pki]+0.5, - p, - img, - pkdata->tot_i[pki], + pi, img, pkdata->tot_i[pki], NULL); } } diff --git a/libcrystfel/src/peakfinder8.h b/libcrystfel/src/peakfinder8.h index b4d54fd1..28212e49 100644 --- a/libcrystfel/src/peakfinder8.h +++ b/libcrystfel/src/peakfinder8.h @@ -3,13 +3,14 @@ * * The peakfinder8 algorithm * - * Copyright © 2012-2020 Deutsches Elektronen-Synchrotron DESY, + * Copyright © 2012-2021 Deutsches Elektronen-Synchrotron DESY, * a research centre of the Helmholtz Association. * * Authors: - * 2017 Valerio Mariani <valerio.mariani@desy.de> - * 2017 Anton Barty <anton.barty@desy.de> - * 2017 Oleksandr Yefanov <oleksandr.yefanov@desy.de> + * 2019 Thomas White <taw@physics.org> + * 2017 Valerio Mariani <valerio.mariani@desy.de> + * 2017 Anton Barty <anton.barty@desy.de> + * 2017 Oleksandr Yefanov <oleksandr.yefanov@desy.de> * * This file is part of CrystFEL. * diff --git a/libcrystfel/src/peaks.c b/libcrystfel/src/peaks.c index 7f2526bc..d55ddd03 100644 --- a/libcrystfel/src/peaks.c +++ b/libcrystfel/src/peaks.c @@ -3,12 +3,12 @@ * * Peak search and other image analysis * - * Copyright © 2012-2020 Deutsches Elektronen-Synchrotron DESY, + * Copyright © 2012-2021 Deutsches Elektronen-Synchrotron DESY, * a research centre of the Helmholtz Association. * Copyright © 2012 Richard Kirian * * Authors: - * 2010-2016 Thomas White <taw@physics.org> + * 2010-2020 Thomas White <taw@physics.org> * 2012 Kenneth Beyerlein <kenneth.beyerlein@desy.de> * 2011 Andrew Martin <andrew.martin@desy.de> * 2011 Richard Kirian @@ -55,7 +55,7 @@ #include "image.h" #include "utils.h" #include "peaks.h" -#include "detector.h" +#include "detgeom.h" #include "filters.h" #include "reflist-utils.h" #include "cell-utils.h" @@ -64,94 +64,8 @@ /** \file peaks.h */ -static int cull_peaks_in_panel(struct image *image, struct panel *p) -{ - int i, n; - int nelim = 0; - - n = image_feature_count(image->features); - - for ( i=0; i<n; i++ ) { - - struct imagefeature *f; - int j, ncol; - - f = image_get_feature(image->features, i); - if ( f == NULL ) continue; - - if ( f->p != p ) continue; - - /* How many peaks are in the same column? */ - ncol = 0; - for ( j=0; j<n; j++ ) { - - struct imagefeature *g; - - if ( i==j ) continue; - - g = image_get_feature(image->features, j); - if ( g == NULL ) continue; - - if ( p->badrow == 'f' ) { - if ( fabs(f->ss - g->ss) < 2.0 ) ncol++; - } else if ( p->badrow == 's' ) { - if ( fabs(f->fs - g->fs) < 2.0 ) ncol++; - } /* else do nothing */ - - } - - /* More than three? */ - if ( ncol <= 3 ) continue; - - /* Yes? Delete them all... */ - for ( j=0; j<n; j++ ) { - struct imagefeature *g; - g = image_get_feature(image->features, j); - if ( g == NULL ) continue; - if ( p->badrow == 'f' ) { - if ( fabs(f->ss - g->ss) < 2.0 ) { - image_remove_feature(image->features, - j); - nelim++; - } - } else if ( p->badrow == 's' ) { - if ( fabs(f->fs - g->ss) < 2.0 ) { - image_remove_feature(image->features, - j); - nelim++; - } - } else { - ERROR("Invalid badrow direction.\n"); - abort(); - } - - } - - } - - return nelim; -} - - -/* Post-processing of the peak list to remove noise */ -static int cull_peaks(struct image *image) -{ - int nelim = 0; - struct panel *p; - int i; - - for ( i=0; i<image->det->n_panels; i++ ) { - p = &image->det->panels[i]; - if ( p->badrow != '-' ) { - nelim += cull_peaks_in_panel(image, p); - } - } - - return nelim; -} - - -static void add_crystal_to_mask(struct image *image, struct panel *p, +static void add_crystal_to_mask(struct image *image, + struct detgeom_panel *p, int pn, double ir_inn, int *mask, Crystal *cr) { Reflection *refl; @@ -168,7 +82,7 @@ static void add_crystal_to_mask(struct image *image, struct panel *p, get_detector_pos(refl, &pk2_fs, &pk2_ss); /* Determine if reflection is in the same panel */ - if ( get_panel(refl) != p ) continue; + if ( get_panel_number(refl) != pn ) continue; for ( dfs=-ir_inn; dfs<=ir_inn; dfs++ ) { for ( dss=-ir_inn; dss<=ir_inn; dss++ ) { @@ -197,7 +111,8 @@ static void add_crystal_to_mask(struct image *image, struct panel *p, /* cfs, css relative to panel origin */ -int *make_BgMask(struct image *image, struct panel *p, double ir_inn) +int *make_BgMask(struct image *image, struct detgeom_panel *p, + int pn, double ir_inn) { int *mask; int i; @@ -208,7 +123,7 @@ int *make_BgMask(struct image *image, struct panel *p, double ir_inn) if ( image->crystals == NULL ) return mask; for ( i=0; i<image->n_crystals; i++ ) { - add_crystal_to_mask(image, p, ir_inn, + add_crystal_to_mask(image, p, pn, ir_inn, mask, image->crystals[i]); } @@ -218,12 +133,12 @@ int *make_BgMask(struct image *image, struct panel *p, double ir_inn) /* Returns non-zero if peak has been vetoed. * i.e. don't use result if return value is not zero. */ -static int integrate_peak(struct image *image, - int p_cfs, int p_css, struct panel *p, - double *pfs, double *pss, - double *intensity, double *sigma, - double ir_inn, double ir_mid, double ir_out, - int *saturated) +int integrate_peak(struct image *image, + int p_cfs, int p_css, int pn, + double *pfs, double *pss, + double *intensity, double *sigma, + double ir_inn, double ir_mid, double ir_out, + int *saturated) { signed int dfs, dss; double lim_sq, out_lim_sq, mid_lim_sq; @@ -236,22 +151,18 @@ static int integrate_peak(struct image *image, double bg_tot_sq = 0.0; double var; double aduph; - int pn; + struct detgeom_panel *p; if ( saturated != NULL ) *saturated = 0; + p = &image->detgeom->panels[pn]; + aduph = p->adu_per_photon; lim_sq = pow(ir_inn, 2.0); mid_lim_sq = pow(ir_mid, 2.0); out_lim_sq = pow(ir_out, 2.0); - pn = panel_number(image->det, p); - if ( pn == image->det->n_panels ) { - ERROR("Couldn't find panel %p\n", p); - return 20; - } - /* Estimate the background */ for ( dss=-ir_out; dss<=+ir_out; dss++ ) { for ( dfs=-ir_out; dfs<=+ir_out; dfs++ ) { @@ -352,7 +263,7 @@ static void search_peaks_in_panel(struct image *image, float threshold, { int fs, ss, stride; float *data; - struct panel *p; + struct detgeom_panel *p; double d; int idx; double f_fs = 0.0; @@ -366,9 +277,8 @@ static void search_peaks_in_panel(struct image *image, float threshold, int nrej_snr = 0; int nrej_sat = 0; int nacc = 0; - int ncull; - p = &image->det->panels[pn]; + p = &image->detgeom->panels[pn]; data = image->dp[pn]; stride = p->w; @@ -456,7 +366,7 @@ static void search_peaks_in_panel(struct image *image, float threshold, assert(mask_ss >= 0); /* Centroid peak and get better coordinates. */ - r = integrate_peak(image, mask_fs, mask_ss, p, + r = integrate_peak(image, mask_fs, mask_ss, pn, &f_fs, &f_ss, &intensity, &sigma, ir_inn, ir_mid, ir_out, &saturated); @@ -479,7 +389,8 @@ static void search_peaks_in_panel(struct image *image, float threshold, } /* Check for a nearby feature */ - image_feature_closest(image->features, f_fs, f_ss, p, &d, &idx); + image_feature_closest(image->features, f_fs, f_ss, pn, + &d, &idx); if ( d < 2.0*ir_inn ) { nrej_pro++; continue; @@ -491,33 +402,19 @@ static void search_peaks_in_panel(struct image *image, float threshold, } /* Add using "better" coordinates */ - image_add_feature(image->features, f_fs, f_ss, p, + image_add_feature(image->features, f_fs, f_ss, pn, image, intensity, NULL); nacc++; } } - if ( image->det != NULL ) { - ncull = cull_peaks(image); - nacc -= ncull; - } else { - STATUS("Not culling peaks because I don't have a " - "detector geometry file.\n"); - ncull = 0; - } - //STATUS("%i accepted, %i box, %i proximity, %i outside panel, " // "%i failed integration, %i with SNR < %g, %i badrow culled, " // "%i saturated.\n", // nacc, nrej_dis, nrej_pro, nrej_fra, nrej_fail, - // nrej_snr, min_snr, ncull, nrej_sat); + // nrej_snr, min_snr, nrej_sat); - if ( ncull != 0 ) { - STATUS("WARNING: %i peaks were badrow culled. This feature" - " should not usually be used.\nConsider setting" - " badrow=- in the geometry file.\n", ncull); - } } @@ -532,9 +429,7 @@ void search_peaks(struct image *image, float threshold, float min_sq_gradient, } image->features = image_feature_list_new(); - for ( i=0; i<image->det->n_panels; i++ ) { - - if ( image->det->panels[i].no_index ) continue; + for ( i=0; i<image->detgeom->n_panels; i++ ) { search_peaks_in_panel(image, threshold, min_sq_gradient, min_snr, i, ir_inn, ir_mid, ir_out, @@ -606,16 +501,14 @@ int search_peaks_peakfinder9(struct image *image, float min_snr_biggest_pix, if ( allocatePeakList(&peakList, NpeaksMax) ) return 1; - for ( panel_number=0; panel_number<image->det->n_panels; panel_number++ ) { + for ( panel_number=0; panel_number<image->detgeom->n_panels; panel_number++ ) { int w, h; int peak_number; detectorRawFormat_t det_size_one_panel; - if ( image->det->panels[panel_number].no_index ) continue; - - w = image->det->panels[panel_number].w; - h = image->det->panels[panel_number].h; + w = image->detgeom->panels[panel_number].w; + h = image->detgeom->panels[panel_number].h; det_size_one_panel.asic_nx = w; det_size_one_panel.asic_ny = h; @@ -648,8 +541,7 @@ int search_peaks_peakfinder9(struct image *image, float min_snr_biggest_pix, image_add_feature(image->features, peakList.centerOfMass_rawX[peak_number], peakList.centerOfMass_rawY[peak_number], - &image->det->panels[panel_number], - image, + panel_number, image, peakList.totalIntensity[peak_number], NULL); } @@ -698,7 +590,7 @@ int indexing_peak_check(struct image *image, Crystal **crystals, int n_cryst, for ( i=0; i<image_feature_count(image->features); i++ ) { struct imagefeature *f; - struct rvec q; + double q[3]; double h,k,l,hd,kd,ld; int j; int ok = 0; @@ -708,15 +600,22 @@ int indexing_peak_check(struct image *image, Crystal **crystals, int n_cryst, if ( f == NULL ) continue; n_feat++; - /* Reciprocal space position of found peak */ - q = get_q_for_panel(f->p, f->fs, f->ss, - NULL, 1.0/image->lambda); - for ( j=0; j<n_cryst; j++ ) { double ax, ay, az; double bx, by, bz; double cx, cy, cz; + double dx, dy; + + crystal_get_det_shift(crystals[j], &dx, &dy); + + /* Reciprocal space position of found peak, + * based on a calculation including any updates to the + * detector position from the refinement of the + * current crystal. */ + detgeom_transform_coords(&image->detgeom->panels[f->pn], + f->fs, f->ss, image->lambda, + dx, dy, q); cell_get_cartesian(crystal_get_cell(crystals[j]), &ax, &ay, &az, @@ -725,9 +624,9 @@ int indexing_peak_check(struct image *image, Crystal **crystals, int n_cryst, /* Decimal and fractional Miller indices of nearest * reciprocal lattice point */ - hd = q.u * ax + q.v * ay + q.w * az; - kd = q.u * bx + q.v * by + q.w * bz; - ld = q.u * cx + q.v * cy + q.w * cz; + hd = q[0] * ax + q[1] * ay + q[2] * az; + kd = q[0] * bx + q[1] * by + q[2] * bz; + ld = q[0] * cx + q[1] * cy + q[2] * cz; h = lrint(hd); k = lrint(kd); l = lrint(ld); @@ -797,7 +696,7 @@ void validate_peaks(struct image *image, double min_snr, continue; } - r = integrate_peak(image, f->fs, f->ss, f->p, + r = integrate_peak(image, f->fs, f->ss, f->pn, &f_fs, &f_ss, &intensity, &sigma, ir_inn, ir_mid, ir_out, &saturated); if ( r ) { @@ -818,8 +717,8 @@ void validate_peaks(struct image *image, double min_snr, } /* Add using "better" coordinates */ - image_add_feature(flist, f->fs, f->ss, f->p, image, intensity, - NULL); + image_add_feature(flist, f->fs, f->ss, f->pn, image, + intensity, NULL); } @@ -831,17 +730,8 @@ void validate_peaks(struct image *image, double min_snr, } -static int compare_double(const void *av, const void *bv) -{ - double a = *(double *)av; - double b = *(double *)bv; - if ( a > b ) return 1; - if ( a < b ) return -1; - return 0; -} - - -double estimate_peak_resolution(ImageFeatureList *peaks, double lambda) +double estimate_peak_resolution(ImageFeatureList *peaks, double lambda, + struct detgeom *det) { int i, npk, ncut; double *rns; @@ -859,12 +749,14 @@ double estimate_peak_resolution(ImageFeatureList *peaks, double lambda) for ( i=0; i<npk; i++ ) { struct imagefeature *f; - struct rvec r; + double r[3]; f = image_get_feature(peaks, i); - r = get_q_for_panel(f->p, f->fs, f->ss, NULL, 1.0/lambda); - rns[i] = modulus(r.u, r.v, r.w); + detgeom_transform_coords(&det->panels[f->pn], + f->fs, f->ss, + lambda, 0.0, 0.0, r); + rns[i] = modulus(r[0], r[1], r[2]); } @@ -877,3 +769,38 @@ double estimate_peak_resolution(ImageFeatureList *peaks, double lambda) free(rns); return max_res; } + +const char *str_peaksearch(enum peak_search_method meth) +{ + switch ( meth ) { + case PEAK_PEAKFINDER9: return "peakfinder9"; + case PEAK_PEAKFINDER8: return "peakfinder8"; + case PEAK_ZAEF: return "zaef"; + case PEAK_HDF5: return "hdf5"; + case PEAK_CXI: return "cxi"; + case PEAK_MSGPACK: return "msgpack"; + case PEAK_NONE: return "none"; + default: return "???"; + } +} + +enum peak_search_method parse_peaksearch(const char *arg) +{ + if ( strcmp(arg, "zaef") == 0 ) { + return PEAK_ZAEF; + } else if ( strcmp(arg, "peakfinder8") == 0 ) { + return PEAK_PEAKFINDER8; + } else if ( strcmp(arg, "hdf5") == 0 ) { + return PEAK_HDF5; + } else if ( strcmp(arg, "cxi") == 0 ) { + return PEAK_CXI; + } else if ( strcmp(arg, "peakfinder9") == 0 ) { + return PEAK_PEAKFINDER9; + } else if ( strcmp(arg, "msgpack") == 0 ) { + return PEAK_MSGPACK; + } else if ( strcmp(arg, "none") == 0 ) { + return PEAK_NONE; + } + + return PEAK_ERROR; +} diff --git a/libcrystfel/src/peaks.h b/libcrystfel/src/peaks.h index 6e61758c..2f100db2 100644 --- a/libcrystfel/src/peaks.h +++ b/libcrystfel/src/peaks.h @@ -3,11 +3,11 @@ * * Peak search and other image analysis * - * Copyright © 2012-2020 Deutsches Elektronen-Synchrotron DESY, + * Copyright © 2012-2021 Deutsches Elektronen-Synchrotron DESY, * a research centre of the Helmholtz Association. * * Authors: - * 2010-2018 Thomas White <taw@physics.org> + * 2010-2020 Thomas White <taw@physics.org> * 2017 Valerio Mariani <valerio.mariani@desy.de> * 2017-2018 Yaroslav Gevorkov <yaroslav.gevorkov@desy.de> * @@ -31,14 +31,12 @@ #ifndef PEAKS_H #define PEAKS_H -#ifdef HAVE_CONFIG_H -#include <config.h> -#endif - #include <pthread.h> #include "reflist.h" #include "crystal.h" +#include "image.h" +#include "detgeom.h" #ifdef __cplusplus extern "C" { @@ -49,7 +47,23 @@ extern "C" { * Peak search functions */ -extern int *make_BgMask(struct image *image, struct panel *p, double ir_inn); +enum peak_search_method { + PEAK_PEAKFINDER9, + PEAK_PEAKFINDER8, + PEAK_ZAEF, + PEAK_HDF5, + PEAK_CXI, + PEAK_MSGPACK, + PEAK_NONE, + PEAK_ERROR +}; + +extern const char *str_peaksearch(enum peak_search_method meth); + +extern enum peak_search_method parse_peaksearch(const char *arg); + +extern int *make_BgMask(struct image *image, struct detgeom_panel *p, + int pn, double ir_inn); extern void search_peaks(struct image *image, float threshold, float min_gradient, float min_snr, double ir_inn, @@ -78,7 +92,9 @@ extern void validate_peaks(struct image *image, double min_snr, int ir_inn, int ir_mid, int ir_out, int use_saturated, int check_snr); -extern double estimate_peak_resolution(ImageFeatureList *peaks, double lambda); +extern double estimate_peak_resolution(ImageFeatureList *peaks, + double lambda, + struct detgeom *det); #ifdef __cplusplus } diff --git a/libcrystfel/src/predict-refine.c b/libcrystfel/src/predict-refine.c index aff5fc22..2432055f 100644 --- a/libcrystfel/src/predict-refine.c +++ b/libcrystfel/src/predict-refine.c @@ -3,11 +3,11 @@ * * Prediction refinement * - * Copyright © 2012-2020 Deutsches Elektronen-Synchrotron DESY, + * Copyright © 2012-2021 Deutsches Elektronen-Synchrotron DESY, * a research centre of the Helmholtz Association. * * Authors: - * 2010-2016 Thomas White <taw@physics.org> + * 2010-2020 Thomas White <taw@physics.org> * 2016 Valerio Mariani * * This file is part of CrystFEL. @@ -73,21 +73,21 @@ struct reflpeak { Reflection *refl; struct imagefeature *peak; double Ih; /* normalised */ - struct panel *panel; /* panel the reflection appears on - * (we assume this never changes) */ + struct detgeom_panel *panel; /* panel the reflection appears on + * (we assume this never changes) */ }; static void twod_mapping(double fs, double ss, double *px, double *py, - struct panel *p) + struct detgeom_panel *p) { double xs, ys; - xs = fs*p->fsx + ss*p->ssx; - ys = fs*p->fsy + ss*p->ssy; + xs = fs*p->fsx + ss*p->ssx; /* pixels */ + ys = fs*p->fsy + ss*p->ssy; /* pixels */ - *px = (xs + p->cnx) / p->res; - *py = (ys + p->cny) / p->res; + *px = (xs + p->cnx) * p->pixel_pitch; /* metres */ + *py = (ys + p->cny) * p->pixel_pitch; /* metres */ } @@ -98,7 +98,7 @@ static double r_dev(struct reflpeak *rp) } -static double x_dev(struct reflpeak *rp, struct detector *det) +static double x_dev(struct reflpeak *rp, struct detgeom *det) { /* Peak position term */ double xpk, ypk, xh, yh; @@ -110,7 +110,7 @@ static double x_dev(struct reflpeak *rp, struct detector *det) } -static double y_dev(struct reflpeak *rp, struct detector *det) +static double y_dev(struct reflpeak *rp, struct detgeom *det) { /* Peak position term */ double xpk, ypk, xh, yh; @@ -122,50 +122,6 @@ static double y_dev(struct reflpeak *rp, struct detector *det) } -static void UNUSED write_pairs(const char *filename, struct reflpeak *rps, - int n, struct detector *det) -{ - int i; - FILE *fh; - - fh = fopen(filename, "w"); - if ( fh == NULL ) { - ERROR("Failed to open '%s'\n", filename); - return; - } - - for ( i=0; i<n; i++ ) { - - double write_fs, write_ss; - double fs, ss; - struct panel *p; - signed int h, k, l; - - fs = rps[i].peak->fs; - ss = rps[i].peak->ss; - p = rps[i].panel; - get_indices(rps[i].refl, &h, &k, &l); - - write_fs = fs + p->orig_min_fs; - write_ss = ss + p->orig_min_ss; - - fprintf(fh, "%7.2f %7.2f dev r,x,y: %9f %9f %9f %9f\n", - write_fs, write_ss, - r_dev(&rps[i])/1e9, fabs(r_dev(&rps[i])/1e9), - x_dev(&rps[i], det), - y_dev(&rps[i], det)); - - //fprintf(fh, "%4i %4i %4i 0.0 - 0.0 1 %7.2f %7.2f %s\n", - // h, k, l, write_fs, write_ss, p->name); - - } - - fclose(fh); - - STATUS("Wrote %i pairs to %s\n", n, filename); -} - - static int cmpd2(const void *av, const void *bv) { struct reflpeak *a, *b; @@ -179,14 +135,13 @@ static int cmpd2(const void *av, const void *bv) static int check_outlier_transition(struct reflpeak *rps, int n, - struct detector *det) + struct detgeom *det) { int i; if ( n < 3 ) return n; qsort(rps, n, sizeof(struct reflpeak), cmpd2); - //write_pairs("pairs-before-outlier.lst", rps, n, det); for ( i=1; i<n-1; i++ ) { @@ -223,12 +178,15 @@ static int pair_peaks(struct image *image, Crystal *cr, double ax, ay, az; double bx, by, bz; double cx, cy, cz; + double dx, dy; RefList *all_reflist; all_reflist = reflist_new(); cell_get_cartesian(crystal_get_cell(cr), &ax, &ay, &az, &bx, &by, &bz, &cx, &cy, &cz); + crystal_get_det_shift(cr, &dx, &dy); + /* First, create a RefList containing the most likely indices for each * peak, with no exclusion criteria */ for ( i=0; i<image_feature_count(image->features); i++ ) { @@ -236,16 +194,21 @@ static int pair_peaks(struct image *image, Crystal *cr, struct imagefeature *f; double h, k, l, hd, kd, ld; Reflection *refl; + double r[3]; /* Assume all image "features" are genuine peaks */ f = image_get_feature(image->features, i); if ( f == NULL ) continue; + detgeom_transform_coords(&image->detgeom->panels[f->pn], + f->fs, f->ss, image->lambda, + dx, dy, r); + /* Decimal and fractional Miller indices of nearest reciprocal * lattice point */ - hd = f->rx * ax + f->ry * ay + f->rz * az; - kd = f->rx * bx + f->ry * by + f->rz * bz; - ld = f->rx * cx + f->ry * cy + f->rz * cz; + hd = r[0] * ax + r[1] * ay + r[2] * az; + kd = r[0] * bx + r[1] * by + r[2] * bz; + ld = r[0] * cx + r[1] * cy + r[2] * cz; h = lrint(hd); k = lrint(kd); l = lrint(ld); @@ -267,11 +230,11 @@ static int pair_peaks(struct image *image, Crystal *cr, * in how far away it is from the peak location. * The predicted position and excitation errors will be * filled in by update_predictions(). */ - set_panel(refl, f->p); + set_panel_number(refl, f->pn); rps[n].refl = refl; rps[n].peak = f; - rps[n].panel = f->p; + rps[n].panel = &image->detgeom->panels[f->pn]; n++; } @@ -307,7 +270,7 @@ static int pair_peaks(struct image *image, Crystal *cr, /* Sort the pairings by excitation error and look for a transition * between good pairings and outliers */ - n_final = check_outlier_transition(rps, n_acc, image->det); + n_final = check_outlier_transition(rps, n_acc, image->detgeom); /* Add the final accepted reflections to the caller's list */ if ( reflist != NULL ) { @@ -359,16 +322,18 @@ int refine_radius(Crystal *cr, struct image *image) } -static void update_detector(struct detector *det, double xoffs, double yoffs, - double coffs) +static void update_detector(struct detgeom *det, + double xoffs, double yoffs, double coffs) { int i; for ( i=0; i<det->n_panels; i++ ) { - struct panel *p = &det->panels[i]; - p->cnx += xoffs * p->res; - p->cny += yoffs * p->res; - p->clen += coffs; + struct detgeom_panel *p = &det->panels[i]; + + /* Convert to pixels */ + p->cnx += xoffs / p->pixel_pitch; + p->cny += yoffs / p->pixel_pitch; + p->cnz += coffs / p->pixel_pitch; } } @@ -454,7 +419,7 @@ static int iterate(struct reflpeak *rps, int n, UnitCell *cell, } - v_c = x_dev(&rps[i], image->det); + v_c = x_dev(&rps[i], image->detgeom); v_c *= -gradients[k]; v_curr = gsl_vector_get(v, k); gsl_vector_set(v, k, v_curr + v_c); @@ -486,7 +451,7 @@ static int iterate(struct reflpeak *rps, int n, UnitCell *cell, } - v_c = y_dev(&rps[i], image->det); + v_c = y_dev(&rps[i], image->detgeom); v_c *= -gradients[k]; v_curr = gsl_vector_get(v, k); gsl_vector_set(v, k, v_curr + v_c); @@ -538,12 +503,13 @@ static int iterate(struct reflpeak *rps, int n, UnitCell *cell, csx += gsl_vector_get(shifts, 6); csy += gsl_vector_get(shifts, 7); csz += gsl_vector_get(shifts, 8); - update_detector(image->det, gsl_vector_get(shifts, 9), - gsl_vector_get(shifts, 10), - gsl_vector_get(shifts, 11)); + update_detector(image->detgeom, + gsl_vector_get(shifts, 9), + gsl_vector_get(shifts, 10), + 0.0); *total_x += gsl_vector_get(shifts, 9); *total_y += gsl_vector_get(shifts, 10); - *total_z += gsl_vector_get(shifts, 11); + *total_z += 0.0; cell_set_reciprocal(cell, asx, asy, asz, bsx, bsy, bsz, csx, csy, csz); @@ -555,7 +521,7 @@ static int iterate(struct reflpeak *rps, int n, UnitCell *cell, } -static double pred_residual(struct reflpeak *rps, int n, struct detector *det) +static double pred_residual(struct reflpeak *rps, int n, struct detgeom *det) { int i; double res = 0.0; @@ -621,6 +587,8 @@ int refine_prediction(struct image *image, Crystal *cr) } crystal_set_reflections(cr, reflist); + crystal_get_det_shift(cr, &total_x, &total_y); + /* Normalise the intensities to max 1 */ max_I = -INFINITY; for ( i=0; i<n; i++ ) { @@ -636,7 +604,7 @@ int refine_prediction(struct image *image, Crystal *cr) rps[i].Ih = rps[i].peak->intensity / max_I; } - //STATUS("Initial residual = %e\n", pred_residual(rps, n, image->det)); + //STATUS("Initial residual = %e\n", pred_residual(rps, n, image->detgeom)); /* Refine */ for ( i=0; i<MAX_CYCLES; i++ ) { @@ -644,12 +612,12 @@ int refine_prediction(struct image *image, Crystal *cr) if ( iterate(rps, n, crystal_get_cell(cr), image, &total_x, &total_y, &total_z) ) return 1; //STATUS("Residual after %i = %e\n", i, - // pred_residual(rps, n, image->det)); + // pred_residual(rps, n, image->detgeom)); } - //STATUS("Final residual = %e\n", pred_residual(rps, n, image->det)); + //STATUS("Final residual = %e\n", pred_residual(rps, n, image->detgeom)); snprintf(tmp, 255, "predict_refine/final_residual = %e", - pred_residual(rps, n, image->det)); + pred_residual(rps, n, image->detgeom)); crystal_add_notes(cr, tmp); crystal_set_det_shift(cr, total_x, total_y); diff --git a/libcrystfel/src/predict-refine.h b/libcrystfel/src/predict-refine.h index 6396df8a..5607e356 100644 --- a/libcrystfel/src/predict-refine.h +++ b/libcrystfel/src/predict-refine.h @@ -3,11 +3,11 @@ * * Prediction refinement * - * Copyright © 2012-2020 Deutsches Elektronen-Synchrotron DESY, + * Copyright © 2012-2021 Deutsches Elektronen-Synchrotron DESY, * a research centre of the Helmholtz Association. * * Authors: - * 2010-2015 Thomas White <taw@physics.org> + * 2010-2019 Thomas White <taw@physics.org> * * This file is part of CrystFEL. * @@ -29,11 +29,6 @@ #ifndef PREDICT_REFINE_H #define PREDICT_REFINE_H - -#ifdef HAVE_CONFIG_H -#include <config.h> -#endif - #include "crystal.h" struct image; diff --git a/libcrystfel/src/rational.c b/libcrystfel/src/rational.c index 05bca429..afffcf47 100644 --- a/libcrystfel/src/rational.c +++ b/libcrystfel/src/rational.c @@ -3,11 +3,11 @@ * * A small rational number library * - * Copyright © 2019-2020 Deutsches Elektronen-Synchrotron DESY, + * Copyright © 2019-2021 Deutsches Elektronen-Synchrotron DESY, * a research centre of the Helmholtz Association. * * Authors: - * 2019 Thomas White <taw@physics.org> + * 2019-2020 Thomas White <taw@physics.org> * * This file is part of CrystFEL. * @@ -640,8 +640,9 @@ void transform_fractional_coords_rtnl_inverse(const RationalMatrix *P, } -static RationalMatrix *delete_row_and_column(const RationalMatrix *m, - unsigned int di, unsigned int dj) +static RationalMatrix *rational_delete_row_and_column(const RationalMatrix *m, + unsigned int di, + unsigned int dj) { RationalMatrix *n; unsigned int i, j; @@ -667,13 +668,13 @@ static RationalMatrix *delete_row_and_column(const RationalMatrix *m, } -static Rational cofactor(const RationalMatrix *m, - unsigned int i, unsigned int j) +static Rational rational_cofactor(const RationalMatrix *m, + unsigned int i, unsigned int j) { RationalMatrix *n; Rational t, C; - n = delete_row_and_column(m, i, j); + n = rational_delete_row_and_column(m, i, j); if ( n == NULL ) { fprintf(stderr, "Failed to allocate matrix.\n"); return rtnl_zero(); @@ -708,7 +709,8 @@ Rational rtnl_mtx_det(const RationalMatrix *m) 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)); + a = rtnl_mul(rtnl_mtx_get(m, i, j), + rational_cofactor(m, i, j)); det = rtnl_add(det, a); } diff --git a/libcrystfel/src/rational.h b/libcrystfel/src/rational.h index f8aaa537..a083f861 100644 --- a/libcrystfel/src/rational.h +++ b/libcrystfel/src/rational.h @@ -3,7 +3,7 @@ * * A small rational number library * - * Copyright © 2019-2020 Deutsches Elektronen-Synchrotron DESY, + * Copyright © 2019-2021 Deutsches Elektronen-Synchrotron DESY, * a research centre of the Helmholtz Association. * * Authors: @@ -29,10 +29,6 @@ #ifndef RATIONAL_H #define RATIONAL_H -#ifdef HAVE_CONFIG_H -#include <config.h> -#endif - /** * \file rational.h * %Rational numbers (including rational matrices) diff --git a/libcrystfel/src/reflist-utils.c b/libcrystfel/src/reflist-utils.c index 7a41f335..42c3f06d 100644 --- a/libcrystfel/src/reflist-utils.c +++ b/libcrystfel/src/reflist-utils.c @@ -3,11 +3,11 @@ * * Utilities to complement the core reflist.c * - * Copyright © 2012-2020 Deutsches Elektronen-Synchrotron DESY, + * Copyright © 2012-2021 Deutsches Elektronen-Synchrotron DESY, * a research centre of the Helmholtz Association. * * Authors: - * 2011-2018 Thomas White <taw@physics.org> + * 2011-2020 Thomas White <taw@physics.org> * 2014 Valerio Mariani * * This file is part of CrystFEL. @@ -41,6 +41,7 @@ #include "utils.h" #include "reflist-utils.h" #include "symmetry.h" +#include "libcrystfel-version.h" /** \file reflist-utils.h @@ -672,7 +673,7 @@ void free_contribs(RefList *list) static char *full_command_line(int argc, char *argv[]) { int i; - size_t len = 0; + size_t len = 1; char *cl; if ( argc == 0 ) return strdup(""); @@ -698,9 +699,8 @@ void reflist_add_command_and_version(RefList *list, int argc, char *argv[]) char *tmp; char vers[128]; - vers[0] = '\0'; - strcat(vers, "Generated by CrystFEL "); - strncat(vers, CRYSTFEL_VERSIONSTRING, 100); + snprintf(vers, 128, "Generated by CrystFEL %s", + libcrystfel_version_string()); reflist_add_notes(list, vers); tmp = full_command_line(argc, argv); diff --git a/libcrystfel/src/reflist-utils.h b/libcrystfel/src/reflist-utils.h index 9eb21ecb..f5177cee 100644 --- a/libcrystfel/src/reflist-utils.h +++ b/libcrystfel/src/reflist-utils.h @@ -3,11 +3,11 @@ * * Utilities to complement the core reflist.c * - * Copyright © 2012-2020 Deutsches Elektronen-Synchrotron DESY, + * Copyright © 2012-2021 Deutsches Elektronen-Synchrotron DESY, * a research centre of the Helmholtz Association. * * Authors: - * 2011-2018 Thomas White <taw@physics.org> + * 2011-2019 Thomas White <taw@physics.org> * 2014 Valerio Mariani * * This file is part of CrystFEL. @@ -27,10 +27,6 @@ * */ -#ifdef HAVE_CONFIG_H -#include <config.h> -#endif - /** @cond */ #ifndef REFLIST_UTILS_H #define REFLIST_UTILS_H diff --git a/libcrystfel/src/reflist.c b/libcrystfel/src/reflist.c index 9cedfc86..90c50e1f 100644 --- a/libcrystfel/src/reflist.c +++ b/libcrystfel/src/reflist.c @@ -3,11 +3,11 @@ * * Fast reflection/peak list * - * Copyright © 2012-2020 Deutsches Elektronen-Synchrotron DESY, + * Copyright © 2012-2021 Deutsches Elektronen-Synchrotron DESY, * a research centre of the Helmholtz Association. * * Authors: - * 2011-2018 Thomas White <taw@physics.org> + * 2011-2021 Thomas White <taw@physics.org> * * This file is part of CrystFEL. * @@ -26,6 +26,10 @@ * */ +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + #include <stdlib.h> #include <assert.h> #include <stdio.h> @@ -53,7 +57,7 @@ struct _refldata { /* Location in image */ double fs; double ss; - struct panel *panel; + int panel_number; /* Non-zero if this reflection can be used for scaling */ int scalable; @@ -111,7 +115,6 @@ struct _reflection { struct _reflist { struct _reflection *head; - struct _reflection *tail; char *notes; }; @@ -124,6 +127,7 @@ static Reflection *new_node(unsigned int serial) Reflection *new; new = calloc(1, sizeof(struct _reflection)); + if ( new == NULL ) return NULL; new->serial = serial; new->next = NULL; new->prev = NULL; @@ -308,12 +312,13 @@ void get_detector_pos(const Reflection *refl, double *fs, double *ss) /** * \param refl: Reflection * - * \returns the panel which the reflection appears on + * \returns panel number (index in detgeom/DataTemplate structure) + * which the reflection appears on * **/ -struct panel *get_panel(const Reflection *refl) +int get_panel_number(const Reflection *refl) { - return refl->data.panel; + return refl->data.panel_number; } @@ -589,14 +594,13 @@ void set_detector_pos(Reflection *refl, double fs, double ss) /** * \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. + * \param pn: Panel number (index in detgeom/DataTemplate structure) of + * the panel on which the reflection appears. * **/ -void set_panel(Reflection *refl, struct panel *p) +void set_panel_number(Reflection *refl, int pn) { - refl->data.panel = p; + refl->data.panel_number = pn; } @@ -882,8 +886,11 @@ static Reflection *insert_node(Reflection *refl, Reflection *new) } -static void add_to_list(RefList *list, Reflection *new, - signed int h, signed int k, signed int l) +static void add_refl_to_list_real(RefList *list, + Reflection *new, + signed int h, + signed int k, + signed int l) { Reflection *f; @@ -929,7 +936,7 @@ Reflection *add_refl(RefList *list, signed int h, signed int k, signed int l) new = new_node(SERIAL(h, k, l)); if ( new == NULL ) return NULL; - add_to_list(list, new, h, k, l); + add_refl_to_list_real(list, new, h, k, l); return new; } @@ -948,7 +955,7 @@ void add_refl_to_list(Reflection *refl, RefList *list) get_indices(refl, &h, &k, &l); - add_to_list(list, refl, h, k, l); + add_refl_to_list_real(list, refl, h, k, l); } diff --git a/libcrystfel/src/reflist.h b/libcrystfel/src/reflist.h index 3085a2aa..864e871d 100644 --- a/libcrystfel/src/reflist.h +++ b/libcrystfel/src/reflist.h @@ -3,11 +3,11 @@ * * Fast reflection/peak list * - * Copyright © 2012-2020 Deutsches Elektronen-Synchrotron DESY, + * Copyright © 2012-2021 Deutsches Elektronen-Synchrotron DESY, * a research centre of the Helmholtz Association. * * Authors: - * 2011-2018 Thomas White <taw@physics.org> + * 2011-2020 Thomas White <taw@physics.org> * * This file is part of CrystFEL. * @@ -29,10 +29,6 @@ #ifndef REFLIST_H #define REFLIST_H -#ifdef HAVE_CONFIG_H -#include <config.h> -#endif - #define SERIAL(h, k, l) ((((h)+512)<<20) + (((k)+512)<<10) + ((l)+512)) #define GET_H(serial) ((((serial) & 0x3ff00000)>>20)-512) #define GET_K(serial) ((((serial) & 0x000ffc00)>>10)-512) @@ -109,7 +105,7 @@ extern Reflection *next_found_refl(Reflection *refl); /* Get */ extern void get_detector_pos(const Reflection *refl, double *fs, double *ss); -extern struct panel *get_panel(const Reflection *refl); +extern int get_panel_number(const Reflection *refl); extern double get_partiality(const Reflection *refl); extern double get_khalf(const Reflection *refl); extern double get_kpred(const Reflection *refl); @@ -134,7 +130,7 @@ extern struct reflection_contributions *get_contributions(const Reflection *refl /* Set */ extern void copy_data(Reflection *to, const Reflection *from); extern void set_detector_pos(Reflection *refl, double fs, double ss); -extern void set_panel(Reflection *refl, struct panel *p); +extern void set_panel_number(Reflection *refl, int pn); extern void set_kpred(Reflection *refl, double kpred); extern void set_khalf(Reflection *refl, double khalf); extern void set_exerr(Reflection *refl, double exerr); diff --git a/libcrystfel/src/spectrum.c b/libcrystfel/src/spectrum.c index e00b697f..bca293e9 100644 --- a/libcrystfel/src/spectrum.c +++ b/libcrystfel/src/spectrum.c @@ -3,7 +3,7 @@ * * A class representing a radiation spectrum * - * Copyright © 2019-2020 Deutsches Elektronen-Synchrotron DESY, + * Copyright © 2019-2021 Deutsches Elektronen-Synchrotron DESY, * a research centre of the Helmholtz Association. * * Authors: diff --git a/libcrystfel/src/spectrum.h b/libcrystfel/src/spectrum.h index 55578974..d0fcc9c1 100644 --- a/libcrystfel/src/spectrum.h +++ b/libcrystfel/src/spectrum.h @@ -3,7 +3,7 @@ * * A class representing a radiation spectrum * - * Copyright © 2019-2020 Deutsches Elektronen-Synchrotron DESY, + * Copyright © 2019-2021 Deutsches Elektronen-Synchrotron DESY, * a research centre of the Helmholtz Association. * * Authors: @@ -29,10 +29,6 @@ #ifndef SPECTRUM_H #define SPECTRUM_H -#ifdef HAVE_CONFIG_H -#include <config.h> -#endif - #include <gsl/gsl_rng.h> /** diff --git a/libcrystfel/src/stream.c b/libcrystfel/src/stream.c index bfe4a50a..83756c7a 100644 --- a/libcrystfel/src/stream.c +++ b/libcrystfel/src/stream.c @@ -3,12 +3,12 @@ * * Stream tools * - * Copyright © 2013-2020 Deutsches Elektronen-Synchrotron DESY, + * Copyright © 2013-2021 Deutsches Elektronen-Synchrotron DESY, * a research centre of the Helmholtz Association. * Copyright © 2012 Richard Kirian * * Authors: - * 2010-2019 Thomas White <taw@physics.org> + * 2010-2021 Thomas White <taw@physics.org> * 2014-2016 Valerio Mariani * 2011 Richard Kirian * 2011 Andrew Aquila @@ -51,6 +51,10 @@ #include "stream.h" #include "reflist.h" #include "reflist-utils.h" +#include "datatemplate.h" +#include "detgeom.h" +#include "libcrystfel-version.h" + /** \file stream.h */ @@ -69,15 +73,21 @@ struct _stream char *audit_info; char *geometry_file; + /* The DataTemplate provided to us for writing things. + * We don't own this. */ + const DataTemplate *dtempl_write; + + /* The DataTemplate we got from the stream itself, used for + * reading things. We own this. */ + DataTemplate *dtempl_read; + long long int ln; int old_indexers; /* True if the stream reader encountered a deprecated * indexing method */ - int in_chunk; /* True if a chunk start marker has been "accidentally" - * encountered, so read_chunk() should assume a chunk is - * already in progress instead of looking for another - * marker */ + long *chunk_offsets; + int n_chunks; }; @@ -87,211 +97,136 @@ int stream_has_old_indexers(Stream *st) } -static int read_peaks(Stream *st, struct image *image) +static ImageFeatureList *read_peaks(Stream *st, + struct image *image) { char *rval = NULL; int first = 1; + ImageFeatureList *features; - image->features = image_feature_list_new(); + features = image_feature_list_new(); do { char line[1024]; float x, y, d, intensity; - int r; - struct panel *p = NULL; - float add_x, add_y; + int r, exp_n; + char panel_name[1024]; rval = fgets(line, 1023, st->fh); st->ln++; - if ( rval == NULL ) continue; + if ( rval == NULL ) { + image_feature_list_free(features); + return NULL; + } chomp(line); - if ( strcmp(line, PEAK_LIST_END_MARKER) == 0 ) return 0; - - r = sscanf(line, "%f %f %f %f", &x, &y, &d, &intensity); - if ( (r != 4) && (!first) ) { - ERROR("Failed to parse peak list line.\n"); - ERROR("The failed line was: '%s'\n", line); - return 1; + if ( strcmp(line, STREAM_PEAK_LIST_END_MARKER) == 0 ) { + return features; } - first = 0; - if ( r == 4 ) { - - if ( image->det != NULL ) { - - p = find_orig_panel(image->det, x, y); - if ( p == NULL ) { - ERROR("Panel not found\n"); - return 1; - } - - add_x = x-p->orig_min_fs; - add_y = y-p->orig_min_ss; - - image_add_feature(image->features, add_x, add_y, - p, image, intensity, NULL); - - } else { - - image_add_feature(image->features, x, y, - p, image, intensity, NULL); - } + if ( first ) { + first = 0; + continue; } - } while ( rval != NULL ); - - /* Got read error of some kind before finding PEAK_LIST_END_MARKER */ - return 1; -} - - -static int read_peaks_2_3(Stream *st, struct image *image) -{ - char *rval = NULL; - int first = 1; - - image->features = image_feature_list_new(); - - do { - - char line[1024]; - char pn[32]; - float x, y, d, intensity; - int r; - struct panel *p = NULL; - float add_x, add_y; - - rval = fgets(line, 1023, st->fh); - st->ln++; - if ( rval == NULL ) continue; - chomp(line); - - if ( strcmp(line, PEAK_LIST_END_MARKER) == 0 ) return 0; + if ( AT_LEAST_VERSION(st, 2, 3) ) { + r = sscanf(line, "%f %f %f %f %s", + &x, &y, &d, &intensity, panel_name); + exp_n = 5; + } else { + r = sscanf(line, "%f %f %f %f", + &x, &y, &d, &intensity); + exp_n = 4; + } - r = sscanf(line, "%f %f %f %f %s", &x, &y, &d, &intensity, pn); - if ( (r != 5) && (!first) ) { + if ( r != exp_n ) { ERROR("Failed to parse peak list line.\n"); ERROR("The failed line was: '%s'\n", line); - return 1; - } - - first = 0; - - if ( r == 5 ) { - - p = find_panel_by_name(image->det, pn); - if ( p == NULL ) { - ERROR("Panel not found: %s\n", pn); - return 1; - } - - add_x = x-p->orig_min_fs; - add_y = y-p->orig_min_ss; - - image_add_feature(image->features, add_x, add_y, p, - image, intensity, NULL); - + image_feature_list_free(features); + return NULL; } - } while ( rval != NULL ); - - /* Got read error of some kind before finding PEAK_LIST_END_MARKER */ - return 1; -} - - -static int write_peaks(struct image *image, FILE *ofh) -{ - int i; - - fprintf(ofh, PEAK_LIST_START_MARKER"\n"); - fprintf(ofh, " fs/px ss/px (1/d)/nm^-1 Intensity\n"); - - for ( i=0; i<image_feature_count(image->features); i++ ) { - - struct imagefeature *f; - struct rvec r; - double q; + if ( (panel_name[0] != '\0') && (st->dtempl_read != NULL) ) { - f = image_get_feature(image->features, i); - if ( f == NULL ) continue; + int pn; - r = get_q_for_panel(f->p, f->fs, f->ss, - NULL, 1.0/image->lambda); - q = modulus(r.u, r.v, r.w); + if ( data_template_panel_name_to_number(st->dtempl_read, + panel_name, + &pn) ) + { + ERROR("No such panel '%s'\n", + panel_name); + } else { - if ( image->det != NULL ) { + data_template_file_to_panel_coords(st->dtempl_read, + &x, &y, &pn); - struct panel *p; - double write_fs, write_ss; + image_add_feature(features, x, y, + pn, image, intensity, + NULL); - p = find_orig_panel(image->det, f->fs, f->ss); - if ( p == NULL ) { - ERROR("Panel not found\n"); - return 1; } - /* Convert coordinates to match arrangement of panels in - * HDF5 file */ - write_fs = f->fs + p->orig_min_fs; - write_ss = f->ss + p->orig_min_ss; - - fprintf(ofh, "%7.2f %7.2f %10.2f %10.2f\n", - write_fs, write_ss, q/1.0e9, f->intensity); - } else { - fprintf(ofh, "%7.2f %7.2f %10.2f %10.2f\n", - f->fs, f->ss, q/1.0e9, f->intensity); + /* Either it's an old format stream (in which + * case the data is probably "slabby", so no + * coordinate conversion is needed), or + * the caller isn't interested in panel + * locations */ + image_add_feature(features, x, y, 0, + image, intensity, NULL); } - } + } while ( rval != NULL ); - fprintf(ofh, PEAK_LIST_END_MARKER"\n"); - return 0; + return features; } -static int write_peaks_2_3(struct image *image, FILE *ofh) +static int write_peaks(const struct image *image, + const DataTemplate *dtempl, FILE *ofh) { int i; - fprintf(ofh, PEAK_LIST_START_MARKER"\n"); + fprintf(ofh, STREAM_PEAK_LIST_START_MARKER"\n"); fprintf(ofh, " fs/px ss/px (1/d)/nm^-1 Intensity Panel\n"); for ( i=0; i<image_feature_count(image->features); i++ ) { struct imagefeature *f; - struct rvec r; + double r[3]; double q; - double write_fs, write_ss; + float write_fs, write_ss; + struct detgeom_panel *p; f = image_get_feature(image->features, i); if ( f == NULL ) continue; - r = get_q_for_panel(f->p, f->fs, f->ss, - NULL, 1.0/image->lambda); - q = modulus(r.u, r.v, r.w); + p = &image->detgeom->panels[f->pn]; + detgeom_transform_coords(p, f->fs, f->ss, + image->lambda, 0.0, 0.0, r); + q = modulus(r[0], r[1], r[2]); - /* Convert coordinates to match arrangement of panels in HDF5 - * file */ - write_fs = f->fs + f->p->orig_min_fs; - write_ss = f->ss + f->p->orig_min_ss; + write_fs = f->fs; + write_ss = f->ss; + data_template_panel_to_file_coords(dtempl, f->pn, + &write_fs, &write_ss); fprintf(ofh, "%7.2f %7.2f %10.2f %10.2f %s\n", - write_fs, write_ss, q/1.0e9, f->intensity, f->p->name); + write_fs, write_ss, q/1.0e9, f->intensity, + data_template_panel_number_to_name(dtempl, f->pn)); } - fprintf(ofh, PEAK_LIST_END_MARKER"\n"); + fprintf(ofh, STREAM_PEAK_LIST_END_MARKER"\n"); return 0; } -static RefList *read_stream_reflections_2_3(Stream *st, struct detector *det) +static RefList *read_stream_reflections_2_3(Stream *st) { char *rval = NULL; int first = 1; @@ -317,7 +252,7 @@ static RefList *read_stream_reflections_2_3(Stream *st, struct detector *det) if ( rval == NULL ) continue; chomp(line); - if ( strcmp(line, REFLECTION_END_MARKER) == 0 ) return out; + if ( strcmp(line, STREAM_REFLECTION_END_MARKER) == 0 ) return out; r = sscanf(line, "%i %i %i %f %f %f %f %f %f %s", &h, &k, &l, &intensity, &sigma, &pk, &bg, @@ -332,24 +267,19 @@ static RefList *read_stream_reflections_2_3(Stream *st, struct detector *det) if ( r == 10 ) { - struct panel *p; - refl = add_refl(out, h, k, l); if ( refl == NULL ) { ERROR("Failed to add reflection\n"); return NULL; } set_intensity(refl, intensity); - if ( det != NULL ) { - double write_fs, write_ss; - p = find_panel_by_name(det,pn); - if ( p == NULL ) { - ERROR("Couldn't find panel '%s'\n", pn); + if ( st->dtempl_read != NULL ) { + int pn; + if ( data_template_file_to_panel_coords(st->dtempl_read, &fs, &ss, &pn) ) { + ERROR("Failed to convert\n"); } else { - write_fs = fs - p->orig_min_fs; - write_ss = ss - p->orig_min_ss; - set_detector_pos(refl, write_fs, write_ss); - set_panel(refl, p); + set_detector_pos(refl, fs, ss); + set_panel_number(refl, pn); } } set_esd_intensity(refl, sigma); @@ -361,12 +291,12 @@ static RefList *read_stream_reflections_2_3(Stream *st, struct detector *det) } while ( rval != NULL ); - /* Got read error of some kind before finding PEAK_LIST_END_MARKER */ + /* Got read error of some kind before finding STREAM_PEAK_LIST_END_MARKER */ return NULL; } -static RefList *read_stream_reflections_2_1(Stream *st, struct detector *det) +static RefList *read_stream_reflections_2_1(Stream *st) { char *rval = NULL; int first = 1; @@ -393,7 +323,7 @@ static RefList *read_stream_reflections_2_1(Stream *st, struct detector *det) if ( rval == NULL ) continue; chomp(line); - if ( strcmp(line, REFLECTION_END_MARKER) == 0 ) return out; + if ( strcmp(line, STREAM_REFLECTION_END_MARKER) == 0 ) return out; r = sscanf(line, "%i %i %i %f %s %f %i %f %f", &h, &k, &l, &intensity, phs, &sigma, &cts, @@ -416,19 +346,14 @@ static RefList *read_stream_reflections_2_1(Stream *st, struct detector *det) } set_intensity(refl, intensity); - if ( det != NULL ) { - - double write_fs, write_ss; - struct panel *p; + if ( st->dtempl_read != NULL ) { - p = find_orig_panel(det, fs, ss); - if ( p == NULL ) { - ERROR("No panel at %.2f,%.2f\n", - fs, ss); + int pn; + if ( data_template_file_to_panel_coords(st->dtempl_read, &fs, &ss, &pn) ) { + ERROR("Failed to convert\n"); } else { - write_fs = fs - p->orig_min_fs; - write_ss = ss - p->orig_min_ss; - set_detector_pos(refl, write_fs, write_ss); + set_detector_pos(refl, fs, ss); + set_panel_number(refl, pn); } } else { @@ -447,12 +372,12 @@ static RefList *read_stream_reflections_2_1(Stream *st, struct detector *det) } while ( rval != NULL ); - /* Got read error of some kind before finding PEAK_LIST_END_MARKER */ + /* Got read error of some kind before finding STREAM_PEAK_LIST_END_MARKER */ return NULL; } -static RefList *read_stream_reflections_2_2(Stream *st, struct detector *det) +static RefList *read_stream_reflections_2_2(Stream *st) { char *rval = NULL; int first = 1; @@ -473,7 +398,7 @@ static RefList *read_stream_reflections_2_2(Stream *st, struct detector *det) if ( rval == NULL ) continue; chomp(line); - if ( strcmp(line, REFLECTION_END_MARKER) == 0 ) return out; + if ( strcmp(line, STREAM_REFLECTION_END_MARKER) == 0 ) return out; r = sscanf(line, "%i %i %i %f %f %f %f %f %f", &h, &k, &l, &intensity, &sigma, &pk, &bg, &fs, &ss); @@ -492,19 +417,17 @@ static RefList *read_stream_reflections_2_2(Stream *st, struct detector *det) } set_intensity(refl, intensity); - if ( det != NULL ) { + if ( st->dtempl_read != NULL ) { - double write_fs, write_ss; - struct panel *p; + int pn; - p = find_orig_panel(det, fs, ss); - if ( p == NULL ) { - ERROR("No panel at %.2f,%.2f\n", - fs, ss); + if ( data_template_file_to_panel_coords(st->dtempl_read, &fs, &ss, &pn) ) { + ERROR("Failed to convert to " + "panel-relative coordinates: " + "%i,%i\n", fs, ss); } else { - write_fs = fs - p->orig_min_fs; - write_ss = ss - p->orig_min_ss; - set_detector_pos(refl, write_fs, write_ss); + set_detector_pos(refl, fs, ss); + set_panel_number(refl, pn); } } else { @@ -523,144 +446,13 @@ static RefList *read_stream_reflections_2_2(Stream *st, struct detector *det) } while ( rval != NULL ); - /* Got read error of some kind before finding REFLECTION_END_MARKER */ + /* Got read error of some kind before finding STREAM_REFLECTION_END_MARKER */ return NULL; } -static int write_stream_reflections_2_1(FILE *fh, RefList *list, - struct image *image) -{ - Reflection *refl; - RefListIterator *iter; - - fprintf(fh, " h k l I phase sigma(I) " - " counts fs/px ss/px\n"); - - for ( refl = first_refl(list, &iter); - refl != NULL; - refl = next_refl(refl, iter) ) - { - - signed int h, k, l; - double intensity, esd_i, ph; - int red; - double fs, ss; - char phs[16]; - int have_phase; - - get_indices(refl, &h, &k, &l); - get_detector_pos(refl, &fs, &ss); - intensity = get_intensity(refl); - esd_i = get_esd_intensity(refl); - red = get_redundancy(refl); - ph = get_phase(refl, &have_phase); - - /* Reflections with redundancy = 0 are not written */ - if ( red == 0 ) continue; - - if ( have_phase ) { - snprintf(phs, 16, "%8.2f", rad2deg(ph)); - } else { - strncpy(phs, " -", 15); - } - - if ( image->det != NULL ) { - - struct panel *p; - double write_fs, write_ss; - - p = find_orig_panel(image->det, fs, ss); - if ( p == NULL ) { - ERROR("Panel not found\n"); - return 1; - } - - /* Convert coordinates to match arrangement of panels - * in HDF5 file */ - write_fs = fs + p->orig_min_fs; - write_ss = ss + p->orig_min_ss; - - fprintf(fh, "%3i %3i %3i %10.2f %s %10.2f %7i " - "%6.1f %6.1f\n", - h, k, l, intensity, phs, esd_i, red, - write_fs, write_ss); - - } else { - - fprintf(fh, "%3i %3i %3i %10.2f %s %10.2f %7i " - "%6.1f %6.1f\n", - h, k, l, intensity, phs, esd_i, red, - fs, ss); - - } - } - return 0; -} - - -static int write_stream_reflections_2_2(FILE *fh, RefList *list, - struct image *image) -{ - Reflection *refl; - RefListIterator *iter; - - fprintf(fh, " h k l I sigma(I) " - "peak background fs/px ss/px\n"); - - for ( refl = first_refl(list, &iter); - refl != NULL; - refl = next_refl(refl, iter) ) - { - - signed int h, k, l; - double intensity, esd_i, bg, pk; - double fs, ss; - - get_indices(refl, &h, &k, &l); - get_detector_pos(refl, &fs, &ss); - intensity = get_intensity(refl); - esd_i = get_esd_intensity(refl); - pk = get_peak(refl); - bg = get_mean_bg(refl); - - /* Reflections with redundancy = 0 are not written */ - if ( get_redundancy(refl) == 0 ) continue; - - if ( image->det != NULL ) { - - struct panel *p; - double write_fs, write_ss; - - p = find_orig_panel(image->det, fs, ss); - if ( p == NULL ) { - ERROR("Panel not found\n"); - return 1; - } - - /* Convert coordinates to match arrangement of panels in HDF5 - * file */ - write_fs = fs + p->orig_min_fs; - write_ss = ss + p->orig_min_ss; - - fprintf(fh, "%4i %4i %4i %10.2f %10.2f %10.2f %10.2f" - " %6.1f %6.1f\n", - h, k, l, intensity, esd_i, pk, bg, write_fs, - write_ss); - - } else { - - fprintf(fh, "%4i %4i %4i %10.2f %10.2f %10.2f %10.2f" - " %6.1f %6.1f\n", - h, k, l, intensity, esd_i, pk, bg, fs, ss); - } - } - return 0; -} - - -static int write_stream_reflections_2_3(FILE *fh, RefList *list, - struct image *image) +static int write_stream_reflections(FILE *fh, RefList *list, + const DataTemplate *dtempl) { Reflection *refl; RefListIterator *iter; @@ -675,13 +467,14 @@ static int write_stream_reflections_2_3(FILE *fh, RefList *list, signed int h, k, l; double intensity, esd_i, pk, bg; - double fs, ss; - double write_fs, write_ss; - struct panel *p; + double dfs, dss; + float fs, ss; + int pn; get_indices(refl, &h, &k, &l); - get_detector_pos(refl, &fs, &ss); - p = get_panel(refl); + get_detector_pos(refl, &dfs, &dss); + fs = dfs; ss = dss; + pn = get_panel_number(refl); intensity = get_intensity(refl); esd_i = get_esd_intensity(refl); pk = get_peak(refl); @@ -690,14 +483,13 @@ static int write_stream_reflections_2_3(FILE *fh, RefList *list, /* Reflections with redundancy = 0 are not written */ if ( get_redundancy(refl) == 0 ) continue; - write_fs = fs+p->orig_min_fs; - write_ss = ss+p->orig_min_ss; + data_template_panel_to_file_coords(dtempl, pn, + &fs, &ss); - fprintf(fh, - "%4i %4i %4i %10.2f %10.2f %10.2f %10.2f " - "%6.1f %6.1f %s\n", - h, k, l, intensity, esd_i, pk, bg, - write_fs, write_ss, p->name); + fprintf(fh, "%4i %4i %4i %10.2f %10.2f %10.2f %10.2f " + "%6.1f %6.1f %s\n", + h, k, l, intensity, esd_i, pk, bg, + fs, ss, data_template_panel_number_to_name(dtempl, pn)); } return 0; @@ -721,7 +513,8 @@ static int num_integrated_reflections(RefList *list) } -static int write_crystal(Stream *st, Crystal *cr, int include_reflections) +static int write_crystal(Stream *st, Crystal *cr, + int include_reflections) { UnitCell *cell; RefList *reflist; @@ -733,7 +526,7 @@ static int write_crystal(Stream *st, Crystal *cr, int include_reflections) double det_shift_x, det_shift_y; int ret = 0; - fprintf(st->fh, CRYSTAL_START_MARKER"\n"); + fprintf(st->fh, STREAM_CRYSTAL_START_MARKER"\n"); cell = crystal_get_cell(cr); assert(cell != NULL); @@ -792,27 +585,10 @@ static int write_crystal(Stream *st, Crystal *cr, int include_reflections) if ( reflist != NULL ) { - struct image *image; - - image = crystal_get_image(cr); - - fprintf(st->fh, REFLECTION_START_MARKER"\n"); - if ( AT_LEAST_VERSION(st, 2, 3) ) { - ret = write_stream_reflections_2_3(st->fh, - reflist, - image); - } else if ( AT_LEAST_VERSION(st, 2, 2) ) { - ret = write_stream_reflections_2_2(st->fh, - reflist, - image); - } else { - /* This function writes like a normal reflection - * list was written in stream 2.1 */ - ret = write_stream_reflections_2_1(st->fh, - reflist, - image); - } - fprintf(st->fh, REFLECTION_END_MARKER"\n"); + fprintf(st->fh, STREAM_REFLECTION_START_MARKER"\n"); + ret = write_stream_reflections(st->fh, reflist, + st->dtempl_write); + fprintf(st->fh, STREAM_REFLECTION_END_MARKER"\n"); } else { @@ -821,7 +597,7 @@ static int write_crystal(Stream *st, Crystal *cr, int include_reflections) } } - fprintf(st->fh, CRYSTAL_END_MARKER"\n"); + fprintf(st->fh, STREAM_CRYSTAL_END_MARKER"\n"); return ret; } @@ -830,29 +606,23 @@ 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 + * \param srf A \ref StreamFlags enum saying what to write * * 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) +int stream_write_chunk(Stream *st, const struct image *i, + StreamFlags srf) { int j; char *indexer; int ret = 0; - fprintf(st->fh, CHUNK_START_MARKER"\n"); + fprintf(st->fh, STREAM_CHUNK_START_MARKER"\n"); fprintf(st->fh, "Image filename: %s\n", i->filename); - if ( i->event != NULL ) { - fprintf(st->fh, "Event: %s\n", get_event_string(i->event)); - } - + fprintf(st->fh, "Event: %s\n", i->ev); fprintf(st->fh, "Image serial number: %i\n", i->serial); fprintf(st->fh, "hit = %i\n", i->hit); @@ -869,40 +639,39 @@ int write_chunk(Stream *st, struct image *i, struct imagefile *imfile, fprintf(st->fh, "beam_divergence = %.2e rad\n", i->div); fprintf(st->fh, "beam_bandwidth = %.2e (fraction)\n", i->bw); - imagefile_copy_fields(imfile, i->copyme, st->fh, ev); + /* FIXME: Better way of doing this */ + //imagefile_copy_fields(imfile, i->copyme, st->fh, ev); - if ( i->det != NULL ) { + if ( i->detgeom != NULL ) { int j; double tclen = 0.0; - for ( j=0; j<i->det->n_panels; j++ ) { - tclen += i->det->panels[j].clen; + for ( j=0; j<i->detgeom->n_panels; j++ ) { + tclen += i->detgeom->panels[j].cnz + * i->detgeom->panels[j].pixel_pitch; } fprintf(st->fh, "average_camera_length = %f m\n", - tclen / i->det->n_panels); + tclen / i->detgeom->n_panels); } 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 ) { - if ( AT_LEAST_VERSION(st, 2, 3) ) { - ret = write_peaks_2_3(i, st->fh); - } else { - ret = write_peaks(i, st->fh); - } + if ( srf & STREAM_PEAKS ) { + ret = write_peaks(i, st->dtempl_write, st->fh); } for ( j=0; j<i->n_crystals; j++ ) { - if ( crystal_get_user_flag(i->crystals[j]) == 0 ) { - ret = write_crystal(st, i->crystals[j], - include_reflections); + if ( crystal_get_user_flag(i->crystals[j]) ) { + continue; } + ret = write_crystal(st, i->crystals[j], + srf & STREAM_REFLECTIONS); } - fprintf(st->fh, CHUNK_END_MARKER"\n"); + fprintf(st->fh, STREAM_CHUNK_END_MARKER"\n"); fflush(st->fh); @@ -915,14 +684,6 @@ static int find_start_of_chunk(Stream *st) char *rval = NULL; char line[1024]; - /* Perhaps read_geometry() encountered a chunk start marker instead of a - * geometry file. In that case, we're already in a chunk, so this is - * easy. */ - if ( st->in_chunk ) { - st->in_chunk = 0; - return 0; - } - do { rval = fgets(line, 1023, st->fh); @@ -933,13 +694,14 @@ static int find_start_of_chunk(Stream *st) chomp(line); - } while ( strcmp(line, CHUNK_START_MARKER) != 0 ); + } while ( strcmp(line, STREAM_CHUNK_START_MARKER) != 0 ); return 0; } -static void read_crystal(Stream *st, struct image *image, StreamReadFlags srf) +static void read_crystal(Stream *st, struct image *image, + StreamFlags srf) { char line[1024]; char *rval = NULL; @@ -980,29 +742,25 @@ static void read_crystal(Stream *st, struct image *image, StreamReadFlags srf) if ( rval == NULL ) break; chomp(line); - if ( (srf & STREAM_READ_UNITCELL) - && (sscanf(line, "astar = %f %f %f", &u, &v, &w) == 3) ) + if ( sscanf(line, "astar = %f %f %f", &u, &v, &w) == 3 ) { as.u = u*1e9; as.v = v*1e9; as.w = w*1e9; have_as = 1; } - if ( (srf & STREAM_READ_UNITCELL) - && (sscanf(line, "bstar = %f %f %f", &u, &v, &w) == 3) ) + if ( sscanf(line, "bstar = %f %f %f", &u, &v, &w) == 3 ) { bs.u = u*1e9; bs.v = v*1e9; bs.w = w*1e9; have_bs = 1; } - if ( (srf & STREAM_READ_UNITCELL) - && (sscanf(line, "cstar = %f %f %f", &u, &v, &w) == 3) ) + if ( sscanf(line, "cstar = %f %f %f", &u, &v, &w) == 3 ) { cs.u = u*1e9; cs.v = v*1e9; cs.w = w*1e9; have_cs = 1; } - if ( (srf & STREAM_READ_UNITCELL) - && (sscanf(line, "centering = %c", &c) == 1) ) + if ( sscanf(line, "centering = %c", &c) == 1 ) { if ( !have_cen ) { centering = c; @@ -1013,8 +771,7 @@ static void read_crystal(Stream *st, struct image *image, StreamReadFlags srf) } } - if ( (srf & STREAM_READ_UNITCELL) - && (sscanf(line, "unique_axis = %c", &c) == 1) ) + if ( sscanf(line, "unique_axis = %c", &c) == 1 ) { if ( !have_ua ) { unique_axis = c; @@ -1025,8 +782,7 @@ static void read_crystal(Stream *st, struct image *image, StreamReadFlags srf) } } - if ( (srf & STREAM_READ_UNITCELL) - && (strncmp(line, "lattice_type = ", 15) == 0) ) + if ( strncmp(line, "lattice_type = ", 15) == 0 ) { if ( !have_latt ) { lattice_type = lattice_from_str(line+15); @@ -1057,8 +813,8 @@ static void read_crystal(Stream *st, struct image *image, StreamReadFlags srf) } - if ( (strcmp(line, REFLECTION_START_MARKER) == 0) - && (srf & STREAM_READ_REFLECTIONS) ) + if ( (strcmp(line, STREAM_REFLECTION_START_MARKER) == 0) + && (srf & STREAM_REFLECTIONS) ) { RefList *reflist; @@ -1066,20 +822,16 @@ static void read_crystal(Stream *st, struct image *image, StreamReadFlags srf) /* The reflection list format in the stream diverges * after 2.2 */ if ( AT_LEAST_VERSION(st, 2, 3) ) { - reflist = read_stream_reflections_2_3(st, - image->det); + reflist = read_stream_reflections_2_3(st); } else if ( AT_LEAST_VERSION(st, 2, 2) ) { - reflist = read_stream_reflections_2_2(st, - image->det); + reflist = read_stream_reflections_2_2(st); } else { - reflist = read_stream_reflections_2_1(st, - image->det); + reflist = read_stream_reflections_2_1(st); } if ( reflist == NULL ) { ERROR("Failed while reading reflections\n"); ERROR("Filename = %s\n", image->filename); - ERROR("Event = %s\n", - get_event_string(image->event)); + ERROR("Event = %s\n", image->ev); break; } @@ -1087,7 +839,7 @@ static void read_crystal(Stream *st, struct image *image, StreamReadFlags srf) } - if ( strcmp(line, CRYSTAL_END_MARKER) == 0 ) break; + if ( strcmp(line, STREAM_CRYSTAL_END_MARKER) == 0 ) break; } while ( 1 ); @@ -1139,77 +891,21 @@ static void read_crystal(Stream *st, struct image *image, StreamReadFlags srf) } -void free_stuff_from_stream(struct stuff_from_stream *sfs) -{ - int i; - if ( sfs == NULL ) return; - for ( i=0; i<sfs->n_fields; i++ ) { - free(sfs->fields[i]); - } - free(sfs->fields); - free(sfs); -} - - -static int read_and_store_field(struct image *image, const char *line) -{ - char **new_fields; - char *nf; - - if ( image->stuff_from_stream == NULL ) { - image->stuff_from_stream = - malloc(sizeof(struct stuff_from_stream)); - if ( image->stuff_from_stream == NULL) { - ERROR("Failed reading entries from stream\n"); - return 1; - } - image->stuff_from_stream->fields = NULL; - image->stuff_from_stream->n_fields = 0; - } - - new_fields = realloc(image->stuff_from_stream->fields, - (1+image->stuff_from_stream->n_fields)* - sizeof(char *)); - if ( new_fields == NULL ) { - ERROR("Failed reading entries from stream\n"); - return 1; - } - image->stuff_from_stream->fields = new_fields; - - nf = strdup(line); - if ( nf == NULL ) { - ERROR("Failed to allocate field from stream\n"); - return 1; - } - image->stuff_from_stream->fields[image->stuff_from_stream->n_fields] = nf; - image->stuff_from_stream->n_fields++; - - return 0; -} - - /** - * Read the next chunk from a stream and fill in 'image' + * Read the next chunk from a stream and return an image structure */ -int read_chunk_2(Stream *st, struct image *image, StreamReadFlags srf) +struct image *stream_read_chunk(Stream *st, StreamFlags srf) { char line[1024]; char *rval = NULL; int have_filename = 0; int have_ev = 0; + struct image *image; - if ( find_start_of_chunk(st) ) return 1; + if ( find_start_of_chunk(st) ) return NULL; - image->lambda = -1.0; - image->features = NULL; - image->crystals = NULL; - image->n_crystals = 0; - image->event = NULL; - image->stuff_from_stream = NULL; - - if ( (srf & STREAM_READ_REFLECTIONS) || (srf & STREAM_READ_UNITCELL) ) { - srf |= STREAM_READ_CRYSTALS; - } + image = image_new(); + if ( image == NULL ) return NULL; do { int ser; @@ -1229,7 +925,7 @@ int read_chunk_2(Stream *st, struct image *image, StreamReadFlags srf) } if ( strncmp(line, "Event: ", 7) == 0 ) { - image->event = get_event_from_event_string(line+7); + image->ev = strdup(line+7); } if ( strncmp(line, "indexed_by = ", 13) == 0 ) { @@ -1260,70 +956,46 @@ int read_chunk_2(Stream *st, struct image *image, StreamReadFlags srf) image->serial = ser; } - if ( strncmp(line, "camera_length_", 14) == 0 ) { - if ( image->det != NULL ) { - - int k; - char name[1024]; - struct panel *p; - - for ( k=0; k<strlen(line)-14; k++ ) { - char ch = line[k+14]; - name[k] = ch; - if ( (ch == ' ') || (ch == '=') ) { - name[k] = '\0'; - break; - } - } - - p = find_panel_by_name(image->det, name); - if ( p == NULL ) { - ERROR("No panel '%s'\n", name); - } else { - p->clen = atof(line+14+k+3); - } - - } - } - if ( strstr(line, " = ") != NULL ) { + if ( (srf & STREAM_PEAKS) + && strcmp(line, STREAM_PEAK_LIST_START_MARKER) == 0 ) { - int fail; + ImageFeatureList *peaks; + peaks = read_peaks(st, image); - fail = read_and_store_field(image, line); - if ( fail ) { - ERROR("Failed to read fields from stream.\n"); - return 1; + if ( peaks == NULL ) { + ERROR("Failed while reading peaks\n"); + image_free(image); + return NULL; } - } - if ( (srf & STREAM_READ_PEAKS) - && strcmp(line, PEAK_LIST_START_MARKER) == 0 ) { + image->features = peaks; - int fail; - if ( AT_LEAST_VERSION(st, 2, 3) ) { - fail = read_peaks_2_3(st, image); - } else { - fail = read_peaks(st, image); - } - if ( fail ) { - ERROR("Failed while reading peaks\n"); - return 1; - } } - if ( (srf & STREAM_READ_CRYSTALS) - && (strcmp(line, CRYSTAL_START_MARKER) == 0) ) { + if ( strcmp(line, STREAM_CRYSTAL_START_MARKER) == 0 ) { read_crystal(st, image, srf); } /* A chunk must have at least a filename and a wavelength, * otherwise it's incomplete */ - if ( strcmp(line, CHUNK_END_MARKER) == 0 ) { - if ( have_filename && have_ev ) return 0; + if ( strcmp(line, STREAM_CHUNK_END_MARKER) == 0 ) { + if ( have_filename && have_ev ) { + /* Success */ + if ( srf & STREAM_DATA_DETGEOM ) { + create_detgeom(image, st->dtempl_read); + image_set_zero_data(image, st->dtempl_read); + image_set_zero_mask(image, st->dtempl_read); + } + /* FIXME: Maybe arbitrary spectrum from file (?) */ + image->spectrum = spectrum_generate_gaussian(image->lambda, + image->bw); + return image; + } ERROR("Incomplete chunk found in input file.\n"); - return 1; + image_free(image); + return NULL; } } while ( 1 ); @@ -1332,38 +1004,9 @@ int read_chunk_2(Stream *st, struct image *image, StreamReadFlags srf) ERROR("Error reading stream.\n"); } - return 1; /* Either error or EOF, don't care because we will complain - * on the terminal if it was an error. */ -} - - - -/** - * \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 - | STREAM_READ_REFLECTIONS - | STREAM_READ_PEAKS); -} - - -void write_stream_header(FILE *ofh, int argc, char *argv[]) -{ - int i; - - fprintf(ofh, "Command line:"); - for ( i=0; i<argc; i++ ) { - fprintf(ofh, " %s", argv[i]); - } - fprintf(ofh, "\n"); - fflush(ofh); + image_free(image); + return NULL; /* Either error or EOF, don't care because we will complain + * on the terminal if it was an error. */ } @@ -1380,120 +1023,101 @@ char *stream_geometry_file(Stream *st) } -static void read_audit_lines(Stream *st) +static void read_geometry_file(Stream *st) { int done = 0; size_t len = 0; - int first = 1; + const size_t max_geom_len = 64*1024; + char *geom; - st->audit_info = malloc(4096); - if ( st->audit_info == NULL ) { - ERROR("Failed to allocate memory for audit information\n"); + geom = malloc(max_geom_len); + if ( geom == NULL ) { + ERROR("Failed to allocate memory for geometry file\n"); return; } - st->audit_info[0] = '\0'; + geom[0] = '\0'; - /* Read lines from stream until one of them starts with "-----", - * then rewind to the start of that line */ do { char line[1024]; char *rval; - long pos; - - pos = ftell(st->fh); rval = fgets(line, 1023, st->fh); + st->ln++; if ( rval == NULL ) { - ERROR("Failed to read stream audit info.\n"); - close_stream(st); + ERROR("Failed to read stream geometry file.\n"); + stream_close(st); + free(geom); return; } - if ( strncmp(line, "-----", 5) == 0 ) { - fseek(st->fh, pos, SEEK_SET); + if ( strcmp(line, STREAM_GEOM_END_MARKER"\n") == 0 ) { done = 1; + 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(geom); + return; } else { - chomp(line); - len += strlen(line); - if ( len > 4090 ) { - ERROR("Too much audit information.\n"); - return; - } else { - if ( !first ) { - strcat(st->audit_info, "\n"); - } - first = 0; - strcat(st->audit_info, line); - } + strcat(geom, line); } } while ( !done ); + + st->geometry_file = geom; + st->dtempl_read = data_template_new_from_string(geom); } -static void read_geometry_file(Stream *st) +static void read_headers(Stream *st) { int done = 0; size_t len = 0; - int started = 0; - const size_t max_geom_len = 64*1024; - st->geometry_file = malloc(max_geom_len); - if ( st->geometry_file == NULL ) { + st->audit_info = malloc(4096); + if ( st->audit_info == NULL ) { ERROR("Failed to allocate memory for audit information\n"); return; } - st->geometry_file[0] = '\0'; + st->audit_info[0] = '\0'; + /* Read lines from stream until one of them starts with "-----", + * then rewind to the start of that line */ do { char line[1024]; char *rval; rval = fgets(line, 1023, st->fh); + st->ln++; if ( rval == NULL ) { - ERROR("Failed to read stream geometry file.\n"); - close_stream(st); - free(st->geometry_file); - st->geometry_file = NULL; + ERROR("Failed to read stream audit info.\n"); + stream_close(st); return; } - if ( strcmp(line, GEOM_START_MARKER"\n") == 0 ) { - started = 1; - continue; - } - - if ( strcmp(line, GEOM_END_MARKER"\n") == 0 ) { - done = 1; - continue; - } - - if ( strcmp(line, CHUNK_START_MARKER"\n") == 0 ) { + if ( strcmp(line, STREAM_GEOM_START_MARKER"\n") == 0 ) { + read_geometry_file(st); done = 1; - st->in_chunk = 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); + len += strlen(line); + if ( len > 4090 ) { + ERROR("Too much audit information.\n"); + return; + } else { + strcat(st->audit_info, line); + } } } while ( !done ); } -Stream *open_stream_for_read(const char *filename) +Stream *stream_open_for_read(const char *filename) { Stream *st; @@ -1502,7 +1126,10 @@ Stream *open_stream_for_read(const char *filename) st->old_indexers = 0; st->audit_info = NULL; st->geometry_file = NULL; - st->in_chunk = 0; + st->n_chunks = 0; + st->chunk_offsets = NULL; + st->dtempl_read = NULL; + st->dtempl_write = NULL; if ( strcmp(filename, "-") == 0 ) { st->fh = stdin; @@ -1521,7 +1148,7 @@ Stream *open_stream_for_read(const char *filename) rval = fgets(line, 1023, st->fh); if ( rval == NULL ) { ERROR("Failed to read stream version.\n"); - close_stream(st); + stream_close(st); return NULL; } @@ -1539,14 +1166,13 @@ Stream *open_stream_for_read(const char *filename) st->minor_version = 3; } else { ERROR("Invalid stream, or stream format is too new.\n"); - close_stream(st); + stream_close(st); return NULL; } st->ln = 1; - read_audit_lines(st); - read_geometry_file(st); + read_headers(st); return st; } @@ -1556,16 +1182,16 @@ Stream *open_stream_for_read(const char *filename) * \param fd File descriptor (e.g. from open()) to use for stream data. * * Creates a new \ref Stream from \p fd, so that stream data can be written to \p fd - * using \ref write_chunk. + * using \ref stream_write_chunk. * - * In contrast to \ref open_stream_for_write, this function does not write any of + * In contrast to \ref stream_open_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 \ref open_stream_for_write, and the substreams using this function. + * opened using \ref stream_open_for_write, and the substreams using this function. * * \returns A \ref Stream, or NULL on failure. */ -Stream *open_stream_fd_for_write(int fd) +Stream *stream_open_fd_for_write(int fd, const DataTemplate *dtempl) { Stream *st; @@ -1574,7 +1200,10 @@ Stream *open_stream_fd_for_write(int fd) st->old_indexers = 0; st->audit_info = NULL; st->geometry_file = NULL; - st->in_chunk = 0; + st->n_chunks = 0; + st->chunk_offsets = NULL; + st->dtempl_read = NULL; + st->dtempl_write = NULL; st->fh = fdopen(fd, "w"); if ( st->fh == NULL ) { @@ -1582,6 +1211,7 @@ Stream *open_stream_fd_for_write(int fd) return NULL; } + st->dtempl_write = dtempl; st->major_version = LATEST_MAJOR_VERSION; st->minor_version = LATEST_MINOR_VERSION; @@ -1589,102 +1219,32 @@ Stream *open_stream_fd_for_write(int fd) } -static void write_cell_to_stream(Stream *st, UnitCell *cell) +void stream_write_target_cell(Stream *st, UnitCell *cell) { - fprintf(st->fh, CELL_START_MARKER"\n"); + if ( cell == NULL ) return; + fprintf(st->fh, STREAM_CELL_START_MARKER"\n"); write_cell(cell, st->fh); fprintf(st->fh, "; Please note: this is the target unit cell.\n"); fprintf(st->fh, "; The actual unit cells produced by indexing " "depend on many other factors.\n"); - fprintf(st->fh, CELL_END_MARKER"\n"); + fprintf(st->fh, STREAM_CELL_END_MARKER"\n"); fflush(st->fh); } /** * \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 + * \param dtempl A DataTemplate * - * 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 \ref Stream, or NULL on failure. - */ -Stream *open_stream_for_write_4(const char *filename, - const char *geom_filename, UnitCell *cell, - int argc, char *argv[], const char *indm_str) - -{ - Stream *st; - - st = malloc(sizeof(struct _stream)); - if ( st == NULL ) return NULL; - st->old_indexers = 0; - st->audit_info = NULL; - st->geometry_file = NULL; - st->in_chunk = 0; - - st->fh = fopen(filename, "w"); - if ( st->fh == NULL ) { - ERROR("Failed to open stream.\n"); - free(st); - return NULL; - } - - st->major_version = LATEST_MAJOR_VERSION; - st->minor_version = LATEST_MINOR_VERSION; - - fprintf(st->fh, "CrystFEL stream format %i.%i\n", - st->major_version, st->minor_version); - fprintf(st->fh, "Generated by CrystFEL "CRYSTFEL_VERSIONSTRING"\n"); - fflush(st->fh); - - if ( (argc > 0) && (argv != NULL) ) { - write_command(st, argc, argv); - } - - if ( indm_str != NULL ) { - fprintf(st->fh, "Indexing methods selected: %s\n", indm_str); - } - if ( geom_filename != NULL ) { - write_geometry_file(st, geom_filename); - } - if ( cell != NULL ) { - write_cell_to_stream(st, cell); - } - - return st; -} - - -Stream *open_stream_for_write_3(const char *filename, - const char *geom_filename, UnitCell *cell, - int argc, char *argv[]) -{ - return open_stream_for_write_4(filename, geom_filename, cell, - argc, argv, NULL); -} - - -/** - * \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 \p filename. If \p filename already + * exists, it will be overwritten. * - * Creates a new stream with name \p filename, and adds the stream format - * and version header, plus a verbatim copy of the geometry file + * Audit information (e.g. CrystFEL version number) will be written. * * \returns A \ref Stream, or NULL on failure. */ -Stream *open_stream_for_write_2(const char *filename, - const char *geom_filename, int argc, - char *argv[]) +Stream *stream_open_for_write(const char *filename, + const DataTemplate *dtempl) { Stream *st; @@ -1694,7 +1254,10 @@ Stream *open_stream_for_write_2(const char *filename, st->old_indexers = 0; st->audit_info = NULL; st->geometry_file = NULL; - st->in_chunk = 0; + st->n_chunks = 0; + st->chunk_offsets = NULL; + st->dtempl_write = dtempl; + st->dtempl_read = NULL; st->fh = fopen(filename, "w"); if ( st->fh == NULL ) { @@ -1708,50 +1271,15 @@ Stream *open_stream_for_write_2(const char *filename, fprintf(st->fh, "CrystFEL stream format %i.%i\n", st->major_version, st->minor_version); - fprintf(st->fh, "Generated by CrystFEL "CRYSTFEL_VERSIONSTRING"\n"); + fprintf(st->fh, "Generated by CrystFEL %s\n", + libcrystfel_version_string()); fflush(st->fh); - if ( (argc > 0) && (argv != NULL) ) { - write_command(st, argc, argv); - } - if ( geom_filename != NULL ) { - write_geometry_file(st, geom_filename); - } - return st; } -/** - * \param filename Filename of new stream - * - * 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 \ref write_command to record the - * command line. - * - * \returns A \ref Stream, or NULL on failure. - */ -Stream *open_stream_for_write(const char *filename) -{ - return open_stream_for_write_2(filename, NULL, 0, NULL); -} - - -/** - * \param st A \ref Stream - * - * This function gets the integer file descriptor for \p st, a bit like fileno(). - * - * 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 \p st are \ref open_stream_for_write and \ref close_stream. - * - * \returns An integer file descriptor - */ -int get_stream_fd(Stream *st) +int stream_get_fd(Stream *st) { return fileno(st->fh); } @@ -1762,46 +1290,27 @@ int get_stream_fd(Stream *st) * * Closes the stream */ -void close_stream(Stream *st) +void stream_close(Stream *st) { + if ( st == NULL ) return; free(st->audit_info); free(st->geometry_file); + data_template_free(st->dtempl_read); fclose(st->fh); free(st); } -int is_stream(const char *filename) -{ - FILE *fh; - char line[1024]; - char *rval; - - fh = fopen(filename, "r"); - if ( fh == NULL ) return 0; - - rval = fgets(line, 1023, fh); - fclose(fh); - if ( rval == NULL ) return 0; - - if ( strncmp(line, "CrystFEL stream format 2.0", 26) == 0 ) return 1; - if ( strncmp(line, "CrystFEL stream format 2.1", 26) == 0 ) return 1; - if ( strncmp(line, "CrystFEL stream format 2.2", 26) == 0 ) return 1; - - return 0; -} - - /** * \param st A \ref Stream * \param argc number of arguments * \param argv command-line arguments * - * 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 - * ref open_stream_for_write. + * 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 \ref stream_open_for_write. */ -void write_command(Stream *st, int argc, char *argv[]) +void stream_write_commandline_args(Stream *st, int argc, char *argv[]) { int i; @@ -1816,6 +1325,13 @@ void write_command(Stream *st, int argc, char *argv[]) } +void stream_write_indexing_methods(Stream *st, const char *indm_str) +{ + fprintf(st->fh, "Indexing methods selected: %s\n", indm_str); + fflush(st->fh); +} + + /** * \param st A \ref Stream * \param geom_filename geomtry file name @@ -1823,8 +1339,8 @@ void write_command(Stream *st, int argc, char *argv[]) * 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) { - +void stream_write_geometry_file(Stream *st, const char *geom_filename) +{ char line[2014]; FILE *geom_fh; char *rval; @@ -1838,7 +1354,7 @@ void write_geometry_file(Stream *st, const char *geom_filename) { "'%s'\n", geom_filename); return; } - fprintf(st->fh, GEOM_START_MARKER"\n"); + fprintf(st->fh, STREAM_GEOM_START_MARKER"\n"); do { rval = fgets(line, 1023, geom_fh); @@ -1853,7 +1369,7 @@ void write_geometry_file(Stream *st, const char *geom_filename) { fclose(geom_fh); - fprintf(st->fh, GEOM_END_MARKER"\n"); + fprintf(st->fh, STREAM_GEOM_END_MARKER"\n"); fflush(st->fh); } @@ -1862,16 +1378,186 @@ void write_geometry_file(Stream *st, const char *geom_filename) { * \param st A \ref Stream * * 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 + * later calls to \ref stream_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. */ -int rewind_stream(Stream *st) +int stream_rewind(Stream *st) { st->ln = 0; return fseek(st->fh, 0, SEEK_SET); } + +struct _streamindex +{ + char **keys; + long int *ptrs; + int n_keys; + int max_keys; +}; + + +void stream_index_free(StreamIndex *index) +{ + if ( index == NULL ) return; + free(index->keys); + free(index->ptrs); + free(index); +} + + +static char *make_key(const char *filename, + const char *ev) +{ + char *key; + + if ( ev == NULL ) ev = "//"; + + key = malloc(strlen(filename)+strlen(ev)+2); + if ( key == NULL ) return NULL; + + strcpy(key, filename); + strcat(key, " "); + strcat(key, ev); + + return key; +} + + +int stream_select_chunk(Stream *st, + StreamIndex *index, + const char *filename, + const char *ev) +{ + int i; + char *key; + + if ( index == NULL ) return 1; + + key = make_key(filename, ev); + for ( i=0; i<index->n_keys; i++ ) { + if ( strcmp(index->keys[i], key) == 0 ) { + if ( st != NULL ) { + fseek(st->fh, index->ptrs[i], SEEK_SET); + } + return 0; + } + } + return 1; +} + + +static void add_index_record(StreamIndex *index, + long int ptr, + const char *filename, + const char *ev) +{ + char *key; + + if ( index->n_keys == index->max_keys ) { + + int new_max_keys = index->max_keys + 256; + char **new_keys; + long int *new_ptrs; + + new_keys = realloc(index->keys, + new_max_keys*sizeof(char *)); + if ( new_keys == NULL ) return; + + new_ptrs = realloc(index->ptrs, + new_max_keys*sizeof(long int)); + if ( new_ptrs == NULL ) return; + + index->keys = new_keys; + index->ptrs = new_ptrs; + index->max_keys = new_max_keys; + + } + + key = make_key(filename, ev); + if ( key == NULL ) return; + + index->keys[index->n_keys] = key; + index->ptrs[index->n_keys] = ptr; + index->n_keys++; +} + + +StreamIndex *stream_make_index(const char *filename) +{ + FILE *fh; + StreamIndex *index; + long int last_start_pos = 0; + char *last_filename = NULL; + char *last_ev = NULL; + int done = 0; + + fh = fopen(filename, "r"); + if ( fh == NULL ) return NULL; + + index = malloc(sizeof(StreamIndex)); + if ( index == NULL ) { + fclose(fh); + return NULL; + } + + index->keys = NULL; + index->ptrs = NULL; + index->n_keys = 0; + index->max_keys = 0; + + STATUS("Scanning %s\n", filename); + + do { + + char *rval; + char line[1024]; + long int pos; + + pos = ftell(fh); + rval = fgets(line, 1024, fh); + if ( rval == NULL ) { + done = 1; + break; + } + chomp(line); + + if ( strcmp(line, STREAM_CHUNK_START_MARKER) == 0 ) { + last_start_pos = pos; + last_filename = NULL; + last_ev = NULL; + } + + if ( strncmp(line, "Image filename: ", 16) == 0 ) { + last_filename = strdup(line+16); + } + + if ( strncmp(line, "Event: ", 7) == 0 ) { + last_ev = strdup(line+7); + } + + if ( strcmp(line, STREAM_CHUNK_END_MARKER) == 0 ) { + if ( (last_start_pos != 0) + && (last_filename != NULL) ) + { + add_index_record(index, + last_start_pos, + last_filename, + last_ev); + } + free(last_filename); + free(last_ev); + last_start_pos = 0; + last_filename = NULL; + last_ev = NULL; + } + + } while ( !done ); + + fclose(fh); + return index; +} diff --git a/libcrystfel/src/stream.h b/libcrystfel/src/stream.h index f3e4d7e8..115d9918 100644 --- a/libcrystfel/src/stream.h +++ b/libcrystfel/src/stream.h @@ -3,11 +3,11 @@ * * Stream tools * - * Copyright © 2013-2020 Deutsches Elektronen-Synchrotron DESY, + * Copyright © 2013-2021 Deutsches Elektronen-Synchrotron DESY, * a research centre of the Helmholtz Association. * * Authors: - * 2010-2019 Thomas White <taw@physics.org> + * 2010-2021 Thomas White <taw@physics.org> * 2014 Valerio Mariani * 2011 Andrew Aquila * @@ -31,34 +31,28 @@ #ifndef STREAM_H #define STREAM_H -#ifdef HAVE_CONFIG_H -#include <config.h> -#endif - /** * \file stream.h * Stream functions (for indexing results) */ struct image; -struct hdfile; -struct event; -struct imagefile; + +#include "datatemplate.h" #include "cell.h" -#define GEOM_START_MARKER "----- Begin geometry file -----" -#define GEOM_END_MARKER "----- End geometry file -----" -#define CELL_START_MARKER "----- Begin unit cell -----" -#define CELL_END_MARKER "----- End unit cell -----" -#define CHUNK_START_MARKER "----- Begin chunk -----" -#define CHUNK_END_MARKER "----- End chunk -----" -#define PEAK_LIST_START_MARKER "Peaks from peak search" -#define PEAK_LIST_END_MARKER "End of peak list" -#define CRYSTAL_START_MARKER "--- Begin crystal" -#define CRYSTAL_END_MARKER "--- End crystal" -#define REFLECTION_START_MARKER "Reflections measured after indexing" -/* REFLECTION_END_MARKER is over in reflist-utils.h because it is also - * used to terminate a standalone list of reflections */ +#define STREAM_GEOM_START_MARKER "----- Begin geometry file -----" +#define STREAM_GEOM_END_MARKER "----- End geometry file -----" +#define STREAM_CELL_START_MARKER "----- Begin unit cell -----" +#define STREAM_CELL_END_MARKER "----- End unit cell -----" +#define STREAM_CHUNK_START_MARKER "----- Begin chunk -----" +#define STREAM_CHUNK_END_MARKER "----- End chunk -----" +#define STREAM_PEAK_LIST_START_MARKER "Peaks from peak search" +#define STREAM_PEAK_LIST_END_MARKER "End of peak list" +#define STREAM_CRYSTAL_START_MARKER "--- Begin crystal" +#define STREAM_CRYSTAL_END_MARKER "--- End crystal" +#define STREAM_REFLECTION_START_MARKER "Reflections measured after indexing" +#define STREAM_REFLECTION_END_MARKER "End of reflections" /** * An opaque structure representing a stream being read or written @@ -66,77 +60,72 @@ struct imagefile; typedef struct _stream Stream; /** - * A bitfield of things that can be read from a stream. Use this (and - * \ref read_chunk_2) to read the stream faster if you don't need the entire - * contents of the stream. + * A bitfield of things that can be read from or written to a stream. + * Use this together with stream_{read,write}_chunk to read/write the + * stream faster if you don't need all the information. * - * Using either or both of \p STREAM_READ_REFLECTIONS and \p STREAM_READ_UNITCELL - * implies \p STREAM_READ_CRYSTALS. + * General information about crystals (including unit cell parameters) + * is always read and written. **/ typedef enum { - /** Read the unit cell */ - STREAM_READ_UNITCELL = 1, - /** Read the integrated reflections */ - STREAM_READ_REFLECTIONS = 2, + STREAM_REFLECTIONS = 2, /** Read the peak search results */ - STREAM_READ_PEAKS = 4, - - /** Read the general information about crystals */ - STREAM_READ_CRYSTALS = 8, + STREAM_PEAKS = 4, -} StreamReadFlags; + /** Reconstruct the detgeom structure, + * and create (blank) data/mask arrays. + * (NB this is (currently) a slow operation) */ + STREAM_DATA_DETGEOM = 8, -struct stuff_from_stream -{ - char **fields; - int n_fields; -}; +} StreamFlags; #ifdef __cplusplus extern "C" { #endif -extern Stream *open_stream_for_read(const char *filename); -extern Stream *open_stream_for_write(const char *filename); -extern Stream *open_stream_for_write_2(const char *filename, - const char* geom_filename, int argc, - char *argv[]); -extern Stream *open_stream_for_write_3(const char *filename, - const char* geom_filename, UnitCell *cell, - int argc, char *argv[]); -extern Stream *open_stream_for_write_4(const char *filename, - const char *geom_filename, UnitCell *cell, - int argc, char *argv[], const char *indm_str); -extern Stream *open_stream_fd_for_write(int fd); -extern int get_stream_fd(Stream *st); -extern void close_stream(Stream *st); - -extern void free_stuff_from_stream(struct stuff_from_stream *sfs); - -extern int read_chunk(Stream *st, struct image *image); -extern int read_chunk_2(Stream *st, struct image *image, - StreamReadFlags srf); +/* Opening/closing streams */ +extern Stream *stream_open_for_read(const char *filename); +extern Stream *stream_open_for_write(const char *filename, + const DataTemplate *dtempl); +extern Stream *stream_open_fd_for_write(int fd, + const DataTemplate *dtempl); +extern void stream_close(Stream *st); + +/* Writing things to stream header */ +extern void stream_write_geometry_file(Stream *st, + const char *geom_filename); +extern void stream_write_target_cell(Stream *st, + UnitCell *cell); +extern void stream_write_commandline_args(Stream *st, + int argc, char *argv[]); +extern void stream_write_indexing_methods(Stream *st, + const char *indm_str); + +/* Metadata */ extern int stream_has_old_indexers(Stream *st); - -extern int write_chunk(Stream *st, struct image *image, struct imagefile *imfile, - int include_peaks, int include_reflections, - struct event *ev); - -extern int write_chunk_2(Stream *st, struct image *image, - struct imagefile *imfile, - int include_peaks, int include_reflections, - struct event *ev); - -extern void write_command(Stream *st, int argc, char *argv[]); -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); +/* Low-level stuff used for indexamajig sandbox */ +extern int stream_get_fd(Stream *st); +extern int stream_rewind(Stream *st); + +/* Random access */ +typedef struct _streamindex StreamIndex; +extern StreamIndex *stream_make_index(const char *filename); +extern int stream_select_chunk(Stream *st, StreamIndex *index, + const char *filename, + const char *ev); +extern void stream_index_free(StreamIndex *index); + +/* Read/write chunks */ +extern struct image *stream_read_chunk(Stream *st, StreamFlags srf); +extern int stream_write_chunk(Stream *st, const struct image *image, + StreamFlags srf); + #ifdef __cplusplus } #endif diff --git a/libcrystfel/src/symmetry.c b/libcrystfel/src/symmetry.c index 32c5f6c3..bd6d2e8f 100644 --- a/libcrystfel/src/symmetry.c +++ b/libcrystfel/src/symmetry.c @@ -3,11 +3,11 @@ * * Symmetry * - * Copyright © 2012-2020 Deutsches Elektronen-Synchrotron DESY, + * Copyright © 2012-2021 Deutsches Elektronen-Synchrotron DESY, * a research centre of the Helmholtz Association. * * Authors: - * 2010-2019 Thomas White <taw@physics.org> + * 2010-2020 Thomas White <taw@physics.org> * 2014 Kenneth Beyerlein <kenneth.beyerlein@desy.de> * * This file is part of CrystFEL. @@ -1684,21 +1684,24 @@ SymOpList *parse_symmetry_operations(const char *s) } -static void add_chars(char *t, const char *s, int max_len) +static void add_chars(char *t, const char *s, size_t max_len) { - char *tmp; + size_t len; - tmp = strdup(t); + len = strlen(t) + strlen(s); + if ( len > max_len ) { + ERROR("get_matrix_name: String too long!\n"); + return; + } - snprintf(t, max_len, "%s%s", tmp, s); - free(tmp); + strcat(t, s); } char *get_matrix_name(const IntegerMatrix *m, int col) { char *text; - const int max_len = 9; + const size_t max_len = 31; int i; int printed = 0; diff --git a/libcrystfel/src/symmetry.h b/libcrystfel/src/symmetry.h index f71cadb9..b8780735 100644 --- a/libcrystfel/src/symmetry.h +++ b/libcrystfel/src/symmetry.h @@ -3,7 +3,7 @@ * * Symmetry * - * Copyright © 2012-2020 Deutsches Elektronen-Synchrotron DESY, + * Copyright © 2012-2021 Deutsches Elektronen-Synchrotron DESY, * a research centre of the Helmholtz Association. * * Authors: @@ -30,11 +30,6 @@ #ifndef SYMMETRY_H #define SYMMETRY_H -#ifdef HAVE_CONFIG_H -#include <config.h> -#endif - - #include "integer_matrix.h" #include "rational.h" diff --git a/libcrystfel/src/thread-pool.c b/libcrystfel/src/thread-pool.c index 67645b1e..61849270 100644 --- a/libcrystfel/src/thread-pool.c +++ b/libcrystfel/src/thread-pool.c @@ -3,11 +3,11 @@ * * A thread pool implementation * - * Copyright © 2012-2020 Deutsches Elektronen-Synchrotron DESY, + * Copyright © 2012-2021 Deutsches Elektronen-Synchrotron DESY, * a research centre of the Helmholtz Association. * * Authors: - * 2010-2012 Thomas White <taw@physics.org> + * 2010-2019 Thomas White <taw@physics.org> * * This file is part of CrystFEL. * @@ -47,7 +47,6 @@ static int use_status_labels = 0; static pthread_key_t status_label_key; -pthread_mutex_t stderr_lock = PTHREAD_MUTEX_INITIALIZER; struct worker_args { diff --git a/libcrystfel/src/thread-pool.h b/libcrystfel/src/thread-pool.h index 16fb38bf..6bd7139c 100644 --- a/libcrystfel/src/thread-pool.h +++ b/libcrystfel/src/thread-pool.h @@ -3,11 +3,11 @@ * * A thread pool implementation * - * Copyright © 2012-2020 Deutsches Elektronen-Synchrotron DESY, + * Copyright © 2012-2021 Deutsches Elektronen-Synchrotron DESY, * a research centre of the Helmholtz Association. * * Authors: - * 2010-2012 Thomas White <taw@physics.org> + * 2010-2019 Thomas White <taw@physics.org> * * This file is part of CrystFEL. * @@ -29,18 +29,12 @@ #ifndef THREAD_POOL_H #define THREAD_POOL_H -#ifdef HAVE_CONFIG_H -#include <config.h> -#endif - - #include <pthread.h> #ifdef __cplusplus extern "C" { #endif -extern pthread_mutex_t stderr_lock; extern signed int get_status_label(void); /** diff --git a/libcrystfel/src/utils.c b/libcrystfel/src/utils.c index 4873b050..053e952b 100644 --- a/libcrystfel/src/utils.c +++ b/libcrystfel/src/utils.c @@ -3,11 +3,11 @@ * * Utility stuff * - * Copyright © 2012-2020 Deutsches Elektronen-Synchrotron DESY, + * Copyright © 2012-2021 Deutsches Elektronen-Synchrotron DESY, * a research centre of the Helmholtz Association. * * Authors: - * 2009-2014 Thomas White <taw@physics.org> + * 2009-2020 Thomas White <taw@physics.org> * * This file is part of CrystFEL. * @@ -34,6 +34,7 @@ #include <math.h> #include <string.h> #include <stdio.h> +#include <stdarg.h> #include <unistd.h> #include <sys/types.h> #include <sys/stat.h> @@ -251,6 +252,76 @@ gsl_vector *solve_svd(gsl_vector *v, gsl_matrix *M, int *pn_filt, int verbose) } +/* ------------------------------ Message logging ---------------------------- */ + +/* Lock to keep lines serialised on the terminal */ +pthread_mutex_t stderr_lock = PTHREAD_MUTEX_INITIALIZER; + + +static void log_to_stderr(enum log_msg_type type, const char *msg, + void *vp) +{ + int error_print_val = get_status_label(); + pthread_mutex_lock(&stderr_lock); + if ( error_print_val >= 0 ) { + fprintf(stderr, "%3i: ", error_print_val); + } + fprintf(stderr, "%s", msg); + pthread_mutex_unlock(&stderr_lock); +} + + +/* Function to call with ERROR/STATUS messages */ +LogMsgFunc log_msg_func = log_to_stderr; +void *log_msg_vp = NULL; + + +void set_log_message_func(LogMsgFunc new_log_msg_func, void *vp) +{ + log_msg_func = new_log_msg_func; + log_msg_vp = vp; +} + + +void STATUS(const char *format, ...) +{ + va_list args; + char tmp[1024]; + va_start(args, format); + vsnprintf(tmp, 1024, format, args); + va_end(args); + log_msg_func(LOG_MSG_STATUS, tmp, log_msg_vp); +} + + +void ERROR(const char *format, ...) +{ + va_list args; + char tmp[1024]; + va_start(args, format); + vsnprintf(tmp, 1024, format, args); + va_end(args); + log_msg_func(LOG_MSG_ERROR, tmp, log_msg_vp); +} + + +/* ------------------------------ Useful functions ---------------------------- */ + +int convert_int(const char *str, int *pval) +{ + int val; + char *rval; + + val = strtod(str, &rval); + if ( *rval != '\0' ) { + return 1; + } else { + *pval = val; + return 0; + } +} + + size_t notrail(char *s) { ssize_t i; @@ -272,10 +343,12 @@ size_t notrail(char *s) void chomp(char *s) { size_t i; + size_t len; if ( s == NULL ) return; + len = strlen(s); - for ( i=0; i<strlen(s); i++ ) { + for ( i=0; i<len; i++ ) { if ( (s[i] == '\n') || (s[i] == '\r') ) { s[i] = '\0'; return; @@ -503,6 +576,13 @@ char *check_prefix(char *prefix) } +char *safe_strdup(const char *in) +{ + if ( in == NULL ) return NULL; + return strdup(in); +} + + char *safe_basename(const char *in) { int i; @@ -550,6 +630,29 @@ void strip_extension(char *bfn) } +const char *filename_extension(const char *fn, const char **pext2) +{ + const char *ext = NULL; + const char *ext2 = NULL; + size_t r = strlen(fn)-1; + + while ( r > 0 ) { + if ( fn[r] == '.' ) { + if ( ext != NULL ) { + ext2 = fn+r; + break; + } else { + ext = fn+r; + } + } + r--; + } + + if ( pext2 != NULL ) *pext2 = ext2; + return ext; +} + + /* Force the linker to bring in CBLAS to make GSL happy */ void utils_fudge_gslcblas() { @@ -674,3 +777,64 @@ struct rvec quat_rot(struct rvec q, struct quaternion z) return res; } + + +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; +} + + +int compare_double(const void *av, const void *bv) +{ + double a = *(double *)av; + double b = *(double *)bv; + if ( a > b ) return 1; + if ( a < b ) return -1; + return 0; +} + + +/* -------------------------- libcrystfel features ------------------------ */ + +int crystfel_has_peakfinder9() +{ +#ifdef HAVE_FDIP + return 1; +#else + return 0; +#endif +} diff --git a/libcrystfel/src/utils.h b/libcrystfel/src/utils.h index beb05ee0..8bcb51ff 100644 --- a/libcrystfel/src/utils.h +++ b/libcrystfel/src/utils.h @@ -3,11 +3,11 @@ * * Utility stuff * - * Copyright © 2012-2020 Deutsches Elektronen-Synchrotron DESY, + * Copyright © 2012-2021 Deutsches Elektronen-Synchrotron DESY, * a research centre of the Helmholtz Association. * * Authors: - * 2009-2014 Thomas White <taw@physics.org> + * 2009-2020 Thomas White <taw@physics.org> * * This file is part of CrystFEL. * @@ -29,10 +29,6 @@ #ifndef UTILS_H #define UTILS_H -#ifdef HAVE_CONFIG_H -#include <config.h> -#endif - #include <math.h> #include <complex.h> #include <float.h> @@ -54,9 +50,12 @@ /* -------------------------- Fundamental constants ------------------------ */ -/* Electron charge in C */ +/* Electron charge (Coulombs) */ #define ELECTRON_CHARGE (1.6021773e-19) +/* Electron rest mass (kg) */ +#define ELECTRON_MASS (9.1093837015e-31) + /* Planck's constant (Js) */ #define PLANCK (6.62606896e-34) @@ -77,9 +76,13 @@ extern void show_matrix_eqn(gsl_matrix *M, gsl_vector *v); extern void show_matrix(gsl_matrix *M); extern gsl_vector *solve_svd(gsl_vector *v, gsl_matrix *M, int *n_filt, int verbose); + extern size_t notrail(char *s); +extern int convert_int(const char *str, int *pval); extern void chomp(char *s); +#define CLEAR_BIT(val, bit) (((val) | (bit)) ^ (bit)) + /** * Controls the behaviour of \ref assplode. **/ @@ -192,37 +195,45 @@ static inline int within_tolerance(double a, double b, double percent) /* Photon energy (eV) to k (1/m) */ #define ph_eV_to_k(a) ((a)*ELECTRON_CHARGE/PLANCK/C_VACUO) +/* Electron accelerating voltage (V) to wavelength (m) */ +static inline double el_V_to_lambda(double E) +{ + double Estar; + + /* Relativistically corrected accelerating voltage */ + Estar = E * (1.0 + E * ELECTRON_CHARGE/(2.0*ELECTRON_MASS*C_VACUO*C_VACUO)); + + return PLANCK / sqrt(2.0*ELECTRON_MASS*ELECTRON_CHARGE*Estar); +} + -/* ------------------------------ Message macros ---------------------------- */ +/* ------------------------------ Message logging ---------------------------- */ extern pthread_mutex_t stderr_lock; -#define ERROR(...) { \ - int error_print_val = get_status_label(); \ - pthread_mutex_lock(&stderr_lock); \ - if ( error_print_val >= 0 ) { \ - fprintf(stderr, "%3i: ", error_print_val); \ - } \ - fprintf(stderr, __VA_ARGS__); \ - pthread_mutex_unlock(&stderr_lock); \ - } - -#define STATUS(...) { \ - int status_print_val = get_status_label(); \ - pthread_mutex_lock(&stderr_lock); \ - if ( status_print_val >= 0 ) { \ - fprintf(stderr, "%3i: ", status_print_val); \ - } \ - fprintf(stderr, __VA_ARGS__); \ - pthread_mutex_unlock(&stderr_lock); \ - } +enum log_msg_type { + LOG_MSG_STATUS, + LOG_MSG_ERROR +}; + + +extern void STATUS(const char *format, ...); +extern void ERROR(const char *format, ...); + +typedef void (*LogMsgFunc)(enum log_msg_type type, const char *msg, void *vp); + +extern void set_log_message_func(LogMsgFunc new_log_msg_func, + void *vp); /* ------------------------------ File handling ----------------------------- */ extern char *check_prefix(char *prefix); extern char *safe_basename(const char *in); +extern char *safe_strdup(const char *in); extern void strip_extension(char *bfn); +extern char *load_entire_file(const char *filename); +extern const char *filename_extension(const char *fn, const char **ext2); /* ------------------------------ Useful stuff ------------------------------ */ @@ -258,6 +269,8 @@ extern struct quaternion random_quaternion(gsl_rng *rng); extern int quaternion_valid(struct quaternion q); extern struct rvec quat_rot(struct rvec q, struct quaternion z); +extern int compare_double(const void *av, const void *bv); + /* Keep these ones inline, to avoid function call overhead */ static inline struct quaternion invalid_quaternion(void) { @@ -293,6 +306,12 @@ static inline void mean_variance(const double x, const double w, *sumw = temp; } + +/* -------------------------- libcrystfel features ------------------------ */ + +extern int crystfel_has_peakfinder9(void); + + #ifdef __cplusplus } #endif |