diff --git a/.codecov.yml b/.codecov.yml index 8803dce5..4af5eb24 100644 --- a/.codecov.yml +++ b/.codecov.yml @@ -12,7 +12,3 @@ comment: require_base: false # [true :: must have a base report to post] require_head: false # [true :: must have a head report to post] hide_project_coverage: false # [true :: only show coverage on the git diff aka patch coverage] - -# SCons compiles copies of the sources in build/coverage-/, map them back to src/ -fixes: - - "build/coverage-x86_64/::src/" diff --git a/.github/ISSUE_TEMPLATE/release_checklist.md b/.github/ISSUE_TEMPLATE/release_checklist.md index 9fe00789..a642d778 100644 --- a/.github/ISSUE_TEMPLATE/release_checklist.md +++ b/.github/ISSUE_TEMPLATE/release_checklist.md @@ -14,8 +14,8 @@ assignees: "" - [ ] Locally rendered documentation contains all appropriate pages and other human-written text is up-to-date with any changes in the code. - [ ] Installation instructions in the README, documentation, and the website are updated. - [ ] Grammar and writing quality are checked (no typos). -- [ ] Review `FALLBACK_VERSION` in `site_scons/fallback_version.py` for builds without a resolvable release tag. -- [ ] `scons test` passes locally with the latest conda-forge compilers and dependencies. +- [ ] Review `LIBDIFFPY_FALLBACK_VERSION` in `cmake/FallbackVersion.cmake` for builds without a resolvable release tag. +- [ ] The unit tests pass locally (`cmake --preset debug`, `cmake --build --preset debug`, `ctest --preset debug`) with the latest conda-forge compilers and dependencies. - [ ] Dispatch matrix testing to test the release on all systems. If you do not have permission to run this workflow, tag the maintainer and say `@maintainer, please dispatch matrix testing workflow`. Matrix testing also runs automatically after a GitHub release is published, but it does not block the release, so check that it passes. Please tag the maintainer (e.g., @username) in the comment here when you are ready for the GitHub release. Include any additional comments necessary, such as version information and details about the pre-release here: diff --git a/.github/actions/setup-tests/action.yml b/.github/actions/setup-tests/action.yml index 6f36304c..eff40afa 100644 --- a/.github/actions/setup-tests/action.yml +++ b/.github/actions/setup-tests/action.yml @@ -7,13 +7,6 @@ runs: shell: bash run: git fetch --tags https://github.com/diffpy/libdiffpy.git - - name: Check out CxxTest 4.4 - uses: actions/checkout@v6 - with: - repository: CxxTest/cxxtest - ref: 191adddb3876ab389c0c856e1c03874bf70f8ee4 - path: build/cxxtest - - name: Prepare C++ test configuration shell: python {0} run: | diff --git a/.github/scripts/configure-tests.py b/.github/scripts/configure-tests.py index 9b3e3fc9..aac64f65 100644 --- a/.github/scripts/configure-tests.py +++ b/.github/scripts/configure-tests.py @@ -1,4 +1,4 @@ -"""Configure SCons, CxxTest and the conda environment for CI tests.""" +"""Write the conda environment file for the CI tests.""" import json from pathlib import Path @@ -7,7 +7,7 @@ for filename in ("build.txt", "conda.txt", "tests.txt"): for line in (Path("requirements") / filename).read_text().splitlines(): spec = line.split("#", 1)[0].strip() - if spec and spec != "cxxtest": + if spec: requirements.append(spec) Path("build").mkdir(exist_ok=True) @@ -22,34 +22,3 @@ ) + "\n" ) - -Path("sconscript.local").write_text("""\ -Import('env') -import os -import sys - -prefix = os.environ['CONDA_PREFIX'] -cxxtest = Dir('#/build/cxxtest').abspath -env.Replace(CXXTEST=os.path.join(cxxtest, 'bin', 'cxxtestgen'), - CXXTEST_INSTALL_DIR=cxxtest, CXXTEST_PYTHON=sys.executable) - -if env['PLATFORM'] == 'win32': - from SCons.Platform.win32 import exec_spawn - - # The outer CI shell already activates conda and SCons configures MSVC. - # /D stops each compiler/test-generator child from repeating cmd AutoRun. - def spawn_without_autorun(sh, escape, cmd, args, child_env): - return exec_spawn([sh, '/D', '/C', escape(' '.join(args))], child_env) - - env['SPAWN'] = spawn_without_autorun -else: - env.Replace(CXX=os.environ.get('CXX', os.path.join(prefix, 'bin', 'c++'))) - env.Prepend(CCFLAGS=['-isystem' + os.path.join(prefix, 'include')]) - env.Append(LIBPATH=os.path.join(prefix, 'lib')) - env.Append(LINKFLAGS='-Wl,-rpath,' + os.path.join(prefix, 'lib')) - if env['PLATFORM'] == 'darwin': - sdk = os.environ.get('CONDA_BUILD_SYSROOT') - if sdk: - env.Append(CXXFLAGS=['-isysroot', sdk]) - env.Append(LINKFLAGS=['-isysroot', sdk]) -""") diff --git a/.github/workflows/matrix-and-codecov.yml b/.github/workflows/matrix-and-codecov.yml index 285c9f0f..8fcbcdd5 100644 --- a/.github/workflows/matrix-and-codecov.yml +++ b/.github/workflows/matrix-and-codecov.yml @@ -27,19 +27,21 @@ jobs: include: - os: ubuntu-latest shell: bash -l {0} - build: coverage + build-type: Debug + coverage: "ON" jobs: 4 - os: windows-latest shell: cmd /C CALL {0} - build: fast jobs: 4 - os: macos-latest shell: bash -l {0} - build: fast + build-type: Release + coverage: "OFF" jobs: 2 - os: macos-15-intel shell: bash -l {0} - build: fast + build-type: Release + coverage: "OFF" jobs: 4 defaults: run: @@ -55,13 +57,29 @@ jobs: uses: ./.github/actions/setup-tests - name: Build and run C++ tests - run: scons -j${{ matrix.jobs }} build=${{ matrix.build }} test + if: runner.os != 'Windows' + run: | + cmake -S . -B build/ci -G Ninja $CMAKE_ARGS \ + -DCMAKE_BUILD_TYPE=${{ matrix.build-type }} \ + -DLIBDIFFPY_COVERAGE=${{ matrix.coverage }} \ + -DLIBDIFFPY_BUILD_TESTS=ON + cmake --build build/ci --parallel ${{ matrix.jobs }} + ctest --test-dir build/ci --output-on-failure + + # The conda cxx-compiler activation configures MSVC for Ninja in cmd. + - name: Build and run C++ tests (Windows) + if: runner.os == 'Windows' + run: | + where cl.exe || exit /b 1 + cmake --preset release -DLIBDIFFPY_BUILD_TESTS=ON || exit /b 1 + cmake --build --preset release --parallel ${{ matrix.jobs }} || exit /b 1 + ctest --test-dir build\release --output-on-failure --no-tests=error - name: Generate coverage report if: runner.os == 'Linux' run: >- gcovr --gcov-executable "$CONDA_PREFIX/bin/gcov" - --root . --filter 'build/coverage-.*/diffpy/' + --root . --filter 'src/diffpy/' --xml coverage.xml - name: Save coverage report diff --git a/.github/workflows/tests-on-pr.yml b/.github/workflows/tests-on-pr.yml index 35e3916e..85b4b8f1 100644 --- a/.github/workflows/tests-on-pr.yml +++ b/.github/workflows/tests-on-pr.yml @@ -27,12 +27,15 @@ jobs: - name: Build and run C++ tests with coverage run: | "$CONDA_PREFIX/bin/c++" --version - scons -j4 build=coverage test + cmake --preset coverage $CMAKE_ARGS \ + -DCMAKE_INSTALL_LIBDIR=lib/x86_64-linux-gnu + cmake --build --preset coverage --parallel 4 + ctest --preset coverage - name: Generate coverage report run: >- gcovr --gcov-executable "$CONDA_PREFIX/bin/gcov" - --root . --filter 'build/coverage-.*/diffpy/' + --root . --filter 'src/diffpy/' --xml coverage.xml - name: Upload coverage to Codecov diff --git a/.gitignore b/.gitignore index 489381e6..f9043891 100644 --- a/.gitignore +++ b/.gitignore @@ -19,11 +19,8 @@ __pycache__/ *.la *.a -# SCons build files -.sconf_temp/ -.sconsign.dblite -/sconscript.local -/sconsvars.py +# CMake user settings +/CMakeUserPresets.json .gdb_history tags diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index cffbf824..88573478 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -9,10 +9,9 @@ ci: autoupdate_schedule: monthly skip: [no-commit-to-branch] submodules: false -# Leave the vendored CxxTest SCons tool and the data tables untouched. +# Leave the data tables untouched. exclude: | (?x)^( - site_scons/site_tools/cxxtest\.py| .*\.(cif|dat) )$ repos: diff --git a/.readthedocs.yaml b/.readthedocs.yaml index aaa88895..4b5cef01 100644 --- a/.readthedocs.yaml +++ b/.readthedocs.yaml @@ -4,6 +4,9 @@ build: os: "ubuntu-22.04" tools: python: "latest" + # cmake resolves the documentation version from git tags. + apt_packages: + - cmake python: install: diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 00000000..1a7f3fa8 --- /dev/null +++ b/CMakeLists.txt @@ -0,0 +1,388 @@ +cmake_minimum_required(VERSION 3.22) + +list(PREPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/cmake") +include(LibdiffpyVersion) + +# A source distribution carries a generated version.hpp. Otherwise +# resolve the version from git or from an expanded cmake/gitarchive.cfg. +set(_libdiffpy_sdist_version_header + "${CMAKE_CURRENT_SOURCE_DIR}/src/diffpy/version.hpp") +if(EXISTS "${_libdiffpy_sdist_version_header}") + libdiffpy_read_version_header("${_libdiffpy_sdist_version_header}") + set(_libdiffpy_generate_version OFF) +else() + libdiffpy_resolve_version() + set(_libdiffpy_generate_version ON) +endif() + +project(libdiffpy + VERSION + ${LIBDIFFPY_VERSION_MAJOR}.${LIBDIFFPY_VERSION_MINOR}.${LIBDIFFPY_VERSION_MICRO} + DESCRIPTION + "C++ calculators of PDF, bond valence sum and other pair quantities" + HOMEPAGE_URL "https://github.com/diffpy/libdiffpy" + LANGUAGES CXX) +message(STATUS "libdiffpy version ${LIBDIFFPY_VERSION_STR}") + +# Options --------------------------------------------------------------------- + +if(PROJECT_IS_TOP_LEVEL) + option(BUILD_SHARED_LIBS "Build shared libraries" ON) +elseif(NOT DEFINED BUILD_SHARED_LIBS) + # Keep the default shared without adding a parent-wide cache option. + set(BUILD_SHARED_LIBS ON) +endif() +option(LIBDIFFPY_BUILD_OBJECT_LIB "Build an OBJECT library for embedding" OFF) +option(LIBDIFFPY_INSTALL "Install libdiffpy and its CMake package" ON) +option(LIBDIFFPY_ENABLE_OBJCRYST "Build with the ObjCryst structure adapter" ON) +option(LIBDIFFPY_BUILD_TESTS "Build the Catch2 unit tests" OFF) +option(LIBDIFFPY_TEST_INSTALLED + "Build only the unit tests and link them with the installed libdiffpy" OFF) +set(LIBDIFFPY_TESTS "" CACHE STRING + "Comma or space separated patterns selecting the Test*.cpp unit test files") +option(LIBDIFFPY_COVERAGE "Instrument the build for coverage analysis" OFF) +option(LIBDIFFPY_PROFILE "Build with gprof profiling information" OFF) + +if(DEFINED ENV{CONDA_PREFIX} AND NOT "$ENV{CONDA_PREFIX}" STREQUAL "") + file(TO_CMAKE_PATH "$ENV{CONDA_PREFIX}" _conda_prefix) + if(WIN32) + string(APPEND _conda_prefix "/Library") + endif() + # Search explicit CMake and environment prefixes before the active conda + # environment, and the conda environment before system installations. + if(NOT DEFINED CMAKE_FIND_USE_CMAKE_ENVIRONMENT_PATH OR + CMAKE_FIND_USE_CMAKE_ENVIRONMENT_PATH) + file(TO_CMAKE_PATH "$ENV{CMAKE_PREFIX_PATH}" _env_prefixes) + list(APPEND CMAKE_PREFIX_PATH ${_env_prefixes} "${_conda_prefix}") + endif() + if(PROJECT_IS_TOP_LEVEL AND CMAKE_INSTALL_PREFIX_INITIALIZED_TO_DEFAULT) + set(CMAKE_INSTALL_PREFIX "${_conda_prefix}" CACHE PATH + "Install path prefix, prepended onto install directories." FORCE) + endif() +endif() + +get_property(_libdiffpy_multi_config GLOBAL PROPERTY GENERATOR_IS_MULTI_CONFIG) +if(PROJECT_IS_TOP_LEVEL AND NOT _libdiffpy_multi_config AND NOT CMAKE_BUILD_TYPE) + set(CMAKE_BUILD_TYPE Release CACHE STRING "Build type" FORCE) + set_property(CACHE CMAKE_BUILD_TYPE + PROPERTY STRINGS Debug Release RelWithDebInfo MinSizeRel) +endif() + +include(GNUInstallDirs) + +# Compiler settings ----------------------------------------------------------- + +if(PROJECT_IS_TOP_LEVEL) + if(MSVC) + # /W3 rather than /Wall, which floods the log with warnings from the + # standard library and boost headers. + add_compile_options(/W3) + add_link_options(/OPT:NOREF /OPT:NOICF) + else() + add_compile_options(-Wall $<$:-ffast-math>) + endif() +endif() + +if(MSVC AND (LIBDIFFPY_COVERAGE OR LIBDIFFPY_PROFILE)) + message(FATAL_ERROR + "LIBDIFFPY_COVERAGE and LIBDIFFPY_PROFILE require gcc or clang.") +endif() + +if(LIBDIFFPY_COVERAGE) + add_compile_options(--coverage -O0 -g) + add_link_options(--coverage) +endif() + +if(LIBDIFFPY_PROFILE) + add_compile_options(-pg) + add_link_options(-pg) +endif() + +# Unit tests of an installed library ------------------------------------------- + +# Do not define any other targets, they would conflict with the installed +# files. +if(LIBDIFFPY_TEST_INSTALLED) + find_package(libdiffpy + ${PROJECT_VERSION_MAJOR}.${PROJECT_VERSION_MINOR} CONFIG REQUIRED) + message(STATUS "Testing the installed libdiffpy in ${libdiffpy_DIR}") + set(LIBDIFFPY_HAS_OBJCRYST ${libdiffpy_HAS_OBJCRYST}) + enable_testing() + add_subdirectory(src/tests) + return() +endif() + +# Dependencies ---------------------------------------------------------------- + +# serialization of unordered_map requires boost 1.56.0 +if(NOT TARGET Boost::serialization) + find_package(Boost 1.56 CONFIG REQUIRED COMPONENTS serialization) +endif() +if(NOT TARGET GSL::gsl) + find_package(GSL REQUIRED) +endif() + +set(LIBDIFFPY_HAS_OBJCRYST OFF) +if(LIBDIFFPY_ENABLE_OBJCRYST) + find_package(ObjCryst 2017.2.1) + if(NOT ObjCryst_FOUND) + message(FATAL_ERROR + "ObjCryst library not found. Please install libobjcryst or configure " + "with -DLIBDIFFPY_ENABLE_OBJCRYST=OFF.") + endif() + set(LIBDIFFPY_HAS_OBJCRYST ON) +endif() + +# Runtime data layout --------------------------------------------------------- + +set(_runtime_subdir "diffpy/libdiffpy-${PROJECT_VERSION_MAJOR}.${PROJECT_VERSION_MINOR}") +# Empty cache values mean "compute from the current layout", so the defaults +# follow later changes to the install directories, library type or version. +set(LIBDIFFPY_RUNTIME_INSTALL_DIR "" CACHE STRING + "Install directory for libdiffpy runtime data (empty: automatic)") +if(NOT LIBDIFFPY_RUNTIME_INSTALL_DIR) + set(LIBDIFFPY_RUNTIME_INSTALL_DIR "${CMAKE_INSTALL_DATADIR}/${_runtime_subdir}") +endif() + +# Mirror the install directories under a separate build root per configuration. +# If any destination is outside the prefix, mirror the full directory hierarchy +# so that the relative path from the library to its data stays the same. +set(_build_prefix "${PROJECT_BINARY_DIR}") +if(_libdiffpy_multi_config) + string(APPEND _build_prefix "/$") +endif() +set(_layout_root "${CMAKE_INSTALL_PREFIX}") +foreach(dir IN ITEMS BINDIR LIBDIR DATADIR) + cmake_path(ABSOLUTE_PATH CMAKE_INSTALL_${dir} + BASE_DIRECTORY "${CMAKE_INSTALL_PREFIX}" NORMALIZE OUTPUT_VARIABLE _install_${dir}) + cmake_path(IS_PREFIX CMAKE_INSTALL_PREFIX "${_install_${dir}}" NORMALIZE _in_prefix) + if(NOT _in_prefix) + set(_layout_root "") + endif() +endforeach() +foreach(dir IN ITEMS BINDIR LIBDIR DATADIR) + if(_layout_root) + file(RELATIVE_PATH _relative_dir "${_layout_root}" "${_install_${dir}}") + else() + cmake_path(GET _install_${dir} RELATIVE_PART _relative_dir) + endif() + set(_build_${dir} "${_build_prefix}/${_relative_dir}") +endforeach() +if(PROJECT_IS_TOP_LEVEL) + set(CMAKE_RUNTIME_OUTPUT_DIRECTORY "${_build_BINDIR}") + set(CMAKE_LIBRARY_OUTPUT_DIRECTORY "${_build_LIBDIR}") + set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY "${_build_LIBDIR}") +endif() + +# runtimepath.cpp finds data relative to the module containing its code. +# Static/OBJECT builds default to an executable in bin; an embedding +# extension can override LIBDIFFPY_RUNTIME_RELPATH for its own layout. +if(WIN32 OR NOT BUILD_SHARED_LIBS OR LIBDIFFPY_BUILD_OBJECT_LIB) + set(_module_install_dir "${_install_BINDIR}") + set(_module_build_dir "${_build_BINDIR}") +else() + set(_module_install_dir "${_install_LIBDIR}") + set(_module_build_dir "${_build_LIBDIR}") +endif() +set(LIBDIFFPY_RUNTIME_RELPATH "" CACHE STRING + "Runtime data path relative to the module containing libdiffpy (empty: automatic)") +if(NOT LIBDIFFPY_RUNTIME_RELPATH) + cmake_path(ABSOLUTE_PATH LIBDIFFPY_RUNTIME_INSTALL_DIR + BASE_DIRECTORY "${CMAKE_INSTALL_PREFIX}" NORMALIZE OUTPUT_VARIABLE _runtime_install_dir) + file(RELATIVE_PATH LIBDIFFPY_RUNTIME_RELPATH + "${_module_install_dir}" "${_runtime_install_dir}") +endif() + +set(_runtime_src_dir "${PROJECT_SOURCE_DIR}/src/runtime") +file(GLOB _runtime_data_files CONFIGURE_DEPENDS "${_runtime_src_dir}/*.dat") +list(APPEND _runtime_data_files "${_runtime_src_dir}/bvparm2011sel.cif") + +set(_runtime_build_dir "${_module_build_dir}/${LIBDIFFPY_RUNTIME_RELPATH}") +if(PROJECT_IS_TOP_LEVEL AND NOT IS_ABSOLUTE "${LIBDIFFPY_RUNTIME_RELPATH}") + set(_staged_data_files) + foreach(f IN LISTS _runtime_data_files) + cmake_path(GET f FILENAME fname) + set(staged "${_runtime_build_dir}/${fname}") + add_custom_command( + OUTPUT "${staged}" + COMMAND "${CMAKE_COMMAND}" -E make_directory "${_runtime_build_dir}" + COMMAND "${CMAKE_COMMAND}" -E copy_if_different "${f}" "${staged}" + DEPENDS "${f}" + VERBATIM) + list(APPEND _staged_data_files "${staged}") + endforeach() + add_custom_target(libdiffpy_runtime_data ALL DEPENDS ${_staged_data_files}) +elseif(PROJECT_IS_TOP_LEVEL) + message(WARNING + "The library and its data install on different volumes. " + "Set DIFFPYRUNTIME to run the build-tree library.") +endif() + +# Generated headers ----------------------------------------------------------- + +set(_generated_include_dir "${PROJECT_BINARY_DIR}/include") + +if(_libdiffpy_generate_version) + set(_version_header "${_generated_include_dir}/diffpy/version.hpp") + libdiffpy_write_version_header( + "${PROJECT_SOURCE_DIR}/src/diffpy/version.tpl" "${_version_header}") + # Refresh the git version in every build. The header is rewritten only + # when the version changes. + add_custom_target(libdiffpy_version + COMMAND "${CMAKE_COMMAND}" + "-DLIBDIFFPY_VERSION_HEADER=${_version_header}" + "-DLIBDIFFPY_CONFIGURED_MAJORMINOR=${PROJECT_VERSION_MAJOR}.${PROJECT_VERSION_MINOR}" + -P "${PROJECT_SOURCE_DIR}/cmake/LibdiffpyVersion.cmake" + BYPRODUCTS "${_version_header}" + COMMENT "Checking libdiffpy version" + VERBATIM) +else() + set(_version_header "${_libdiffpy_sdist_version_header}") +endif() + +if(LIBDIFFPY_HAS_OBJCRYST) + set(DIFFPY_HAS_OBJCRYST 1) +else() + set(DIFFPY_HAS_OBJCRYST 0) +endif() +set(_features_header "${_generated_include_dir}/diffpy/features.hpp") +configure_file("${PROJECT_SOURCE_DIR}/src/diffpy/features.tpl" + "${_features_header}") + +# Library --------------------------------------------------------------------- + +file(GLOB _diffpy_sources CONFIGURE_DEPENDS + "${PROJECT_SOURCE_DIR}/src/diffpy/*.cpp" + "${PROJECT_SOURCE_DIR}/src/diffpy/srreal/*.cpp") +file(GLOB _diffpy_headers CONFIGURE_DEPENDS + "${PROJECT_SOURCE_DIR}/src/diffpy/*.hpp" + "${PROJECT_SOURCE_DIR}/src/diffpy/*.ipp") +file(GLOB _srreal_headers CONFIGURE_DEPENDS + "${PROJECT_SOURCE_DIR}/src/diffpy/srreal/*.hpp" + "${PROJECT_SOURCE_DIR}/src/diffpy/srreal/*.ipp") +# The version and features headers are installed separately. +list(FILTER _diffpy_headers EXCLUDE REGEX "/(version|features)\\.hpp$") +if(NOT LIBDIFFPY_HAS_OBJCRYST) + set(_objcryst_regex "/[^/]*[Oo][Bb][Jj][Cc][Rr][Yy][Ss][Tt][^/]*$") + list(FILTER _diffpy_sources EXCLUDE REGEX "${_objcryst_regex}") + list(FILTER _diffpy_headers EXCLUDE REGEX "${_objcryst_regex}") + list(FILTER _srreal_headers EXCLUDE REGEX "${_objcryst_regex}") +endif() + +if(LIBDIFFPY_BUILD_OBJECT_LIB) + add_library(diffpy OBJECT ${_diffpy_sources}) +else() + add_library(diffpy ${_diffpy_sources}) +endif() +add_library(libdiffpy::diffpy ALIAS diffpy) +if(_libdiffpy_generate_version) + add_dependencies(diffpy libdiffpy_version) +endif() + +set_target_properties(diffpy PROPERTIES + EXPORT_NAME diffpy + CXX_EXTENSIONS OFF + # Export the API from the DLL, see diffpy/Export.hpp. Code that uses + # the library imports it instead. + DEFINE_SYMBOL BUILD_DLL + INSTALL_NAME_DIR "${CMAKE_INSTALL_FULL_LIBDIR}") +get_target_property(_libdiffpy_library_type diffpy TYPE) +if(NOT _libdiffpy_library_type STREQUAL "SHARED_LIBRARY") + # Static/OBJECT consumers must not import symbols from a libdiffpy DLL. + target_compile_definitions(diffpy PUBLIC DIFFPY_STATIC) +endif() +if(MSVC) + # Required for the Boost.Serialization code, including embedded builds. + target_compile_options(diffpy PRIVATE /bigobj) +endif() +target_compile_features(diffpy PUBLIC cxx_std_23) +target_include_directories(diffpy PUBLIC + "$" + "$" + "$") +if(WIN32) + # Public headers use M_PI and related constants. + target_compile_definitions(diffpy PUBLIC _USE_MATH_DEFINES) +endif() +set_property(SOURCE "${PROJECT_SOURCE_DIR}/src/diffpy/runtimepath.cpp" + APPEND PROPERTY COMPILE_DEFINITIONS + "DIFFPYRUNTIMERELPATH=${LIBDIFFPY_RUNTIME_RELPATH}") + +if(TARGET libdiffpy_runtime_data) + add_dependencies(diffpy libdiffpy_runtime_data) +endif() + +# The dladdr call in runtimepath.cpp requires the dl library. +target_link_libraries(diffpy + PUBLIC Boost::serialization + PRIVATE GSL::gsl ${CMAKE_DL_LIBS}) +if(LIBDIFFPY_HAS_OBJCRYST) + target_link_libraries(diffpy PUBLIC ObjCryst::ObjCryst) +endif() + +# Installation ---------------------------------------------------------------- + +if(LIBDIFFPY_INSTALL) + set(LIBDIFFPY_CMAKE_INSTALL_DIR "${CMAKE_INSTALL_LIBDIR}/cmake/libdiffpy") + + install(TARGETS diffpy EXPORT libdiffpyTargets + RUNTIME DESTINATION "${CMAKE_INSTALL_BINDIR}" COMPONENT Runtime + LIBRARY DESTINATION "${CMAKE_INSTALL_LIBDIR}" COMPONENT Runtime + OBJECTS DESTINATION "${CMAKE_INSTALL_LIBDIR}" COMPONENT Development + ARCHIVE DESTINATION "${CMAKE_INSTALL_LIBDIR}" COMPONENT Development) + install(FILES ${_diffpy_headers} "${_version_header}" "${_features_header}" + DESTINATION "${CMAKE_INSTALL_INCLUDEDIR}/diffpy" + COMPONENT Development) + install(FILES ${_srreal_headers} + DESTINATION "${CMAKE_INSTALL_INCLUDEDIR}/diffpy/srreal" + COMPONENT Development) + install(FILES ${_runtime_data_files} + DESTINATION "${LIBDIFFPY_RUNTIME_INSTALL_DIR}" + COMPONENT Runtime) + + include(CMakePackageConfigHelpers) + configure_package_config_file( + "${PROJECT_SOURCE_DIR}/cmake/libdiffpyConfig.cmake.in" + "${PROJECT_BINARY_DIR}/libdiffpyConfig.cmake" + INSTALL_DESTINATION "${LIBDIFFPY_CMAKE_INSTALL_DIR}" + PATH_VARS LIBDIFFPY_RUNTIME_INSTALL_DIR) + # The runtime data directory and the ABI change with the minor version. + write_basic_package_version_file( + "${PROJECT_BINARY_DIR}/libdiffpyConfigVersion.cmake" + COMPATIBILITY SameMinorVersion) + install(EXPORT libdiffpyTargets + NAMESPACE libdiffpy:: + DESTINATION "${LIBDIFFPY_CMAKE_INSTALL_DIR}" + COMPONENT Development) + set(_config_files + "${PROJECT_BINARY_DIR}/libdiffpyConfig.cmake" + "${PROJECT_BINARY_DIR}/libdiffpyConfigVersion.cmake") + if(LIBDIFFPY_HAS_OBJCRYST) + list(APPEND _config_files "${PROJECT_SOURCE_DIR}/cmake/FindObjCryst.cmake") + endif() + install(FILES ${_config_files} + DESTINATION "${LIBDIFFPY_CMAKE_INSTALL_DIR}" + COMPONENT Development) +endif() + +# Other targets --------------------------------------------------------------- + +if(LIBDIFFPY_BUILD_TESTS) + enable_testing() + add_subdirectory(src/tests) +endif() + +# sdist -- source distribution bundle libdiffpy-.tar.gz +add_custom_target(sdist + COMMAND "${CMAKE_COMMAND}" "-DLIBDIFFPY_SDIST_DIR=${PROJECT_BINARY_DIR}" + -P "${PROJECT_SOURCE_DIR}/cmake/MakeSdist.cmake" + COMMENT "Creating source distribution" + VERBATIM) + +# zerocounters -- remove cumulative coverage counts +if(LIBDIFFPY_COVERAGE) + add_custom_target(zerocounters + COMMAND "${CMAKE_COMMAND}" "-DLIBDIFFPY_COVERAGE_DIR=${PROJECT_BINARY_DIR}" + -P "${PROJECT_SOURCE_DIR}/cmake/ZeroCounters.cmake" + VERBATIM) +endif() diff --git a/CMakePresets.json b/CMakePresets.json new file mode 100644 index 00000000..3dd54d31 --- /dev/null +++ b/CMakePresets.json @@ -0,0 +1,89 @@ +{ + "version": 3, + "cmakeMinimumRequired": { + "major": 3, + "minor": 22, + "patch": 0 + }, + "configurePresets": [ + { + "name": "ninja", + "hidden": true, + "description": "Ninja uses the active compiler environment, including the MSVC toolchain set up by conda on Windows.", + "generator": "Ninja", + "binaryDir": "${sourceDir}/build/${presetName}", + "cacheVariables": { + "CMAKE_GENERATOR_PLATFORM": "", + "CMAKE_GENERATOR_TOOLSET": "" + } + }, + { + "name": "release", + "inherits": "ninja", + "displayName": "Optimized build", + "cacheVariables": { + "CMAKE_BUILD_TYPE": "Release" + } + }, + { + "name": "debug", + "inherits": "ninja", + "displayName": "Debug build with C assertions and unit tests", + "cacheVariables": { + "CMAKE_BUILD_TYPE": "Debug", + "LIBDIFFPY_BUILD_TESTS": "ON" + } + }, + { + "name": "coverage", + "inherits": "ninja", + "displayName": "Debug build instrumented for coverage analysis", + "cacheVariables": { + "CMAKE_BUILD_TYPE": "Debug", + "LIBDIFFPY_BUILD_TESTS": "ON", + "LIBDIFFPY_COVERAGE": "ON" + } + } + ], + "buildPresets": [ + { + "name": "release", + "configurePreset": "release", + "configuration": "Release" + }, + { + "name": "debug", + "configurePreset": "debug", + "configuration": "Debug" + }, + { + "name": "coverage", + "configurePreset": "coverage", + "configuration": "Debug" + } + ], + "testPresets": [ + { + "name": "debug", + "configurePreset": "debug", + "configuration": "Debug", + "execution": { + "noTestsAction": "error" + }, + "output": { + "outputOnFailure": true + } + }, + { + "name": "coverage", + "configurePreset": "coverage", + "configuration": "Debug", + "execution": { + "noTestsAction": "error" + }, + "output": { + "outputOnFailure": true + } + } + ] +} diff --git a/README.rst b/README.rst index 2237552a..0393bc1e 100644 --- a/README.rst +++ b/README.rst @@ -100,9 +100,16 @@ dependencies listed in the ``requirements`` folder :: Then build and install libdiffpy into the active conda environment :: - scons -j4 install + cmake --preset release + cmake --build --preset release --parallel 4 + cmake --install build/release -See the output from ``scons --help`` for the available build targets and options. +The presets use Ninja and require CMake 3.22 or later. To choose the +installation directory, pass ``-DCMAKE_INSTALL_PREFIX=`` when +configuring. Other CMake projects can use the installed library with :: + + find_package(libdiffpy REQUIRED) + target_link_libraries(myprogram PRIVATE libdiffpy::diffpy) Getting Started --------------- @@ -115,19 +122,18 @@ Support and Contribute If you see a bug or want to request a feature, please `report it as an issue `_ and/or `submit a fix as a PR `_. Feel free to fork the project and contribute. When developing it is -preferable to compile with the ``build=develop`` option, which compiles the +preferable to compile with the ``debug`` preset, which compiles the library without optimizations and with C-assertions checks :: - scons -j4 build=develop + cmake --preset debug + cmake --build --preset debug --parallel 4 The library integrity can be verified by executing the unit tests :: - scons -j4 test + ctest --preset debug -The build script checks for a presence of ``sconsvars.py`` file, which -can be used to permanently set the ``build`` variable. The SCons -construction environment can be further customized in a ``sconscript.local`` -script. +To keep local build settings, add a preset to ``CMakeUserPresets.json`` +in the repository root. See also the `development guide `_ for build variants, running a subset of the unit tests, coverage analysis and testing changes with diffpy.srreal. diff --git a/SConstruct b/SConstruct deleted file mode 100644 index a371be1a..00000000 --- a/SConstruct +++ /dev/null @@ -1,150 +0,0 @@ -MY_SCONS_HELP = """\ -SCons build rules for the libdiffpy C++ library -Usage: scons [target] [var=value] - -Targets: - -lib build the shared library object [default] -install install everything under prefix directory -install-lib install the shared library object -install-include install the C++ header files -install-data install data files used by the library -alltests build the unit test program "alltests" -test execute unit tests (requires the cxxtest framework) -sdist create source distribution tarball from git repository -zerocounters remove cumulative coverage-count data - -Build configuration variables: -%s -Variables can be also assigned in a user-written script sconsvars.py. -SCons construction environment can be customized in sconscript.local script. -""" - -import os -import platform -from os.path import join as pjoin -from SCons.Script import * - -def subdictionary(d, keyset): - return dict(kv for kv in d.items() if kv[0] in keyset) - -# copy system environment variables related to compilation -DefaultEnvironment(ENV=subdictionary(os.environ, ''' - PATH CPATH CPLUS_INCLUDE_PATH LIBRARY_PATH LD_RUN_PATH - LD_LIBRARY_PATH DYLD_LIBRARY_PATH DYLD_FALLBACK_LIBRARY_PATH - MACOSX_DEPLOYMENT_TARGET - HOME USERPROFILE HOMEDRIVE HOMEPATH APPDATA LOCALAPPDATA - '''.split()) -) - -# Create construction environment -env = DefaultEnvironment().Clone() - -# Variables definitions below work only with 0.98.1 or later -env.EnsureSConsVersion(0, 98, 1) - -# Customizable compile variables -vars = Variables('sconsvars.py') - -# Set PATHs -if 'PREFIX' in os.environ: - vars.Add(PathVariable( - 'prefix', - 'installation prefix directory', - os.environ['PREFIX'])) - vars.Update(env) -elif 'CONDA_PREFIX' in os.environ: - vars.Add(PathVariable( - 'prefix', - 'installation prefix directory', - os.environ['CONDA_PREFIX'])) - vars.Update(env) -else: - vars.Add(PathVariable( - 'prefix', - 'installation prefix directory', - '/usr/local')) - vars.Update(env) - -if env['PLATFORM'] == "win32": - include_path = pjoin(env['prefix'], 'Library', 'include') - lib_path = pjoin(env['prefix'], 'Library', 'lib') - shared_path = pjoin(env['prefix'], 'Library', 'share') - - # Windows programs started by SCons, such as python for cxxtestgen, - # fail without these system variables. - for k in ('SYSTEMROOT', 'SYSTEMDRIVE', 'COMSPEC', 'PATHEXT', 'TEMP', 'TMP'): - if k in os.environ: - env['ENV'][k] = os.environ[k] -else: - include_path = pjoin(env['prefix'], 'include') - lib_path = pjoin(env['prefix'], 'lib') - shared_path = pjoin(env['prefix'], 'share') - -vars.Add(PathVariable( - 'libdir', - 'installation directory for compiled programs', - lib_path, - PathVariable.PathAccept)) -vars.Add(PathVariable( - 'includedir', - 'installation directory for C++ header files', - include_path, - PathVariable.PathAccept)) -vars.Add(PathVariable( - 'datadir', - 'installation directory for architecture independent data', - shared_path, - PathVariable.PathAccept)) - -env.AppendUnique(CPPPATH=[include_path]) -env.AppendUnique(LIBPATH=[lib_path]) - -# Customizable build variables -vars.Add(EnumVariable( - 'build', - 'compiler settings', - 'fast', allowed_values=('fast', 'debug', 'develop', 'coverage'))) -vars.Add(EnumVariable( - 'tool', - 'C++ compiler toolkit to be used', - 'default', allowed_values=('default', 'clang', 'gcc', 'intelc'))) -vars.Add(BoolVariable( - 'enable_objcryst', - 'enable objcryst support, when installed', None)) -vars.Add(BoolVariable( - 'profile', - 'build with profiling information', False)) -vars.Add( - 'tests', - 'fixed-string patterns for selecting unit tests', None) -vars.Add(BoolVariable( - 'test_installed', - 'build tests using the installed library.', False)) - -vars.Update(env) - -env.Help(MY_SCONS_HELP % vars.GenerateHelpText(env)) - -env['has_objcryst'] = None - -btags = [env['build'], platform.machine()] -if env['profile']: btags.append('profile') -builddir = env.Dir('build/' + '-'.join(btags)) - -Export('env') - -def GlobSources(pattern): - """Same as Glob but also require that source node is a valid file. - """ - rv = [f for f in Glob(pattern) if f.srcnode().isfile()] - return rv - -Export('GlobSources') - -if os.path.isfile('sconscript.local'): - env.SConscript('sconscript.local') - -env.SConscript('src/SConscript', variant_dir=builddir) - -# vim: ft=python diff --git a/cmake/FallbackVersion.cmake b/cmake/FallbackVersion.cmake new file mode 100644 index 00000000..0831a4be --- /dev/null +++ b/cmake/FallbackVersion.cmake @@ -0,0 +1,8 @@ +# Definition of LIBDIFFPY_FALLBACK_VERSION. +# +# Use it when a checkout or source archive has no release tag from which +# to determine the version. +# +# Review LIBDIFFPY_FALLBACK_VERSION before a release. + +set(LIBDIFFPY_FALLBACK_VERSION "1.5.0.post0") diff --git a/cmake/FindObjCryst.cmake b/cmake/FindObjCryst.cmake new file mode 100644 index 00000000..0c209690 --- /dev/null +++ b/cmake/FindObjCryst.cmake @@ -0,0 +1,62 @@ +# Find the ObjCryst++ library from libobjcryst. +# +# libobjcryst does not install a CMake package configuration, so this +# module looks up its header and library directly. +# +# Imported target: +# +# ObjCryst::ObjCryst +# +# Result variables: +# +# ObjCryst_FOUND +# ObjCryst_VERSION +# ObjCryst_INCLUDE_DIR +# ObjCryst_LIBRARY + +find_path(ObjCryst_INCLUDE_DIR NAMES ObjCryst/version.h) +# Windows builds name the import library libObjCryst.lib. +find_library(ObjCryst_LIBRARY NAMES ObjCryst libObjCryst) +mark_as_advanced(ObjCryst_INCLUDE_DIR ObjCryst_LIBRARY) + +if(ObjCryst_INCLUDE_DIR) + file(STRINGS "${ObjCryst_INCLUDE_DIR}/ObjCryst/version.h" _objcryst_vlines + REGEX "^#define LIBOBJCRYST_VERSION_(MAJOR|MINOR|MICRO) +[0-9]+") + set(ObjCryst_VERSION "") + foreach(_objcryst_field MAJOR MINOR MICRO) + foreach(_objcryst_line IN LISTS _objcryst_vlines) + if(_objcryst_line MATCHES "_${_objcryst_field} +([0-9]+)") + list(APPEND ObjCryst_VERSION "${CMAKE_MATCH_1}") + endif() + endforeach() + endforeach() + list(JOIN ObjCryst_VERSION "." ObjCryst_VERSION) + unset(_objcryst_vlines) + unset(_objcryst_line) + unset(_objcryst_field) +endif() + +include(FindPackageHandleStandardArgs) +find_package_handle_standard_args(ObjCryst + REQUIRED_VARS ObjCryst_LIBRARY ObjCryst_INCLUDE_DIR + VERSION_VAR ObjCryst_VERSION) + +if(ObjCryst_FOUND AND NOT TARGET ObjCryst::ObjCryst) + # libObjCryst.so has no SONAME. An imported SHARED target with + # IMPORTED_NO_SONAME links it as -lObjCryst, so that dependent libraries + # do not record its absolute build-time path. Windows links the import + # library, which needs no special handling. + if(NOT WIN32 AND ObjCryst_LIBRARY MATCHES "${CMAKE_SHARED_LIBRARY_SUFFIX}$") + add_library(ObjCryst::ObjCryst SHARED IMPORTED) + set_target_properties(ObjCryst::ObjCryst PROPERTIES + IMPORTED_NO_SONAME TRUE) + else() + add_library(ObjCryst::ObjCryst UNKNOWN IMPORTED) + endif() + set_target_properties(ObjCryst::ObjCryst PROPERTIES + IMPORTED_LOCATION "${ObjCryst_LIBRARY}" + INTERFACE_INCLUDE_DIRECTORIES "${ObjCryst_INCLUDE_DIR}" + # libobjcryst is built with double precision for ObjCryst's REAL type. + # Match that ABI in all consumers of ObjCryst headers. + INTERFACE_COMPILE_DEFINITIONS "REAL=double") +endif() diff --git a/cmake/LibdiffpyVersion.cmake b/cmake/LibdiffpyVersion.cmake new file mode 100644 index 00000000..5322175a --- /dev/null +++ b/cmake/LibdiffpyVersion.cmake @@ -0,0 +1,222 @@ +# Resolve the libdiffpy version and generate diffpy/version.hpp. +# +# The version comes from, in order of preference, +# +# 1. `git describe` of the libdiffpy repository, +# 2. the release tag in an expanded cmake/gitarchive.cfg, which +# `git archive` fills in for a GitHub source archive, +# 3. LIBDIFFPY_FALLBACK_VERSION from cmake/FallbackVersion.cmake. +# +# Release tags have the form X.Y.Z or X.Y.Z-rc.N. Commits after a tag +# get a ".postN" suffix, where N is the number of commits since the tag. +# +# Use from CMake: +# +# include(LibdiffpyVersion) +# libdiffpy_resolve_version() +# libdiffpy_write_version_header( ) +# +# Use as a script: +# +# cmake -P LibdiffpyVersion.cmake +# Print the version string. +# cmake -DLIBDIFFPY_VERSION_HEADER= -P LibdiffpyVersion.cmake +# Write version.hpp, but only when its content changes. + +include_guard(GLOBAL) + +set(_LIBDIFFPY_VERSION_MODULE_DIR "${CMAKE_CURRENT_LIST_DIR}") +include("${CMAKE_CURRENT_LIST_DIR}/FallbackVersion.cmake") + +set(_LIBDIFFPY_EMSG_GIT_MISSING " +Cannot determine libdiffpy version. Compile from a git repository +or use a source archive from + + https://github.com/diffpy/libdiffpy/releases +") + +# Normalize a release tag X.Y.Z or X.Y.Z-rc.N to X.Y.Z or X.Y.ZrcN. +function(_libdiffpy_release_version tag outvar) + if(NOT tag MATCHES "^([0-9]+\\.[0-9]+\\.[0-9]+)(-rc\\.([0-9]+))?$") + message(FATAL_ERROR + "Invalid release tag '${tag}'; expected X.Y.Z or X.Y.Z-rc.N.") + endif() + set(rv "${CMAKE_MATCH_1}") + if(NOT "${CMAKE_MATCH_3}" STREQUAL "") + string(APPEND rv "rc${CMAKE_MATCH_3}") + endif() + set(${outvar} "${rv}" PARENT_SCOPE) +endfunction() + +# Set _gi_commit, _gi_date and, when a release tag is found, _gi_version +# and _gi_patch in the caller. Leave them empty outside a git repository. +function(_libdiffpy_gitinfo) + foreach(v commit date version patch) + set(_gi_${v} "" PARENT_SCOPE) + endforeach() + find_program(LIBDIFFPY_GIT_EXECUTABLE NAMES git) + mark_as_advanced(LIBDIFFPY_GIT_EXECUTABLE) + if(NOT LIBDIFFPY_GIT_EXECUTABLE) + return() + endif() + execute_process( + COMMAND "${LIBDIFFPY_GIT_EXECUTABLE}" log -1 "--format=%H %ci" + WORKING_DIRECTORY "${_LIBDIFFPY_VERSION_MODULE_DIR}" + RESULT_VARIABLE rc + OUTPUT_VARIABLE out + ERROR_QUIET + OUTPUT_STRIP_TRAILING_WHITESPACE) + if(rc OR NOT out MATCHES "^([^ ]+) (.+)$") + return() + endif() + set(_gi_commit "${CMAKE_MATCH_1}" PARENT_SCOPE) + set(_gi_date "${CMAKE_MATCH_2}" PARENT_SCOPE) + # --long makes the commit count explicit even at a tag. Split from + # the right so the -rc.N suffix remains part of the release tag. + execute_process( + COMMAND "${LIBDIFFPY_GIT_EXECUTABLE}" describe --tags --long + "--match=[0-9]*" + WORKING_DIRECTORY "${_LIBDIFFPY_VERSION_MODULE_DIR}" + RESULT_VARIABLE rc + OUTPUT_VARIABLE out + ERROR_QUIET + OUTPUT_STRIP_TRAILING_WHITESPACE) + if(rc OR NOT out MATCHES "^(.+)-([0-9]+)-[^-]+$") + return() + endif() + set(count "${CMAKE_MATCH_2}") + _libdiffpy_release_version("${CMAKE_MATCH_1}" version) + math(EXPR count "${count}") + if(count) + string(APPEND version ".post${count}") + endif() + set(_gi_version "${version}" PARENT_SCOPE) + set(_gi_patch "${count}" PARENT_SCOPE) +endfunction() + +# Resolve the version and set in the caller +# +# LIBDIFFPY_VERSION_STR full version, e.g. 1.5.0rc1.post3 +# LIBDIFFPY_VERSION_MAJOR +# LIBDIFFPY_VERSION_MINOR +# LIBDIFFPY_VERSION_MICRO +# LIBDIFFPY_VERSION_PATCH number of commits since the release tag +# LIBDIFFPY_VERSION_PRERELEASE rcN for a release candidate, else empty +# LIBDIFFPY_VERSION_DATE commit date +# LIBDIFFPY_GIT_SHA full commit hash +function(libdiffpy_resolve_version) + _libdiffpy_gitinfo() + if(_gi_commit STREQUAL "") + # Not a git repository. Require an expanded source archive. + file(STRINGS "${_LIBDIFFPY_VERSION_MODULE_DIR}/gitarchive.cfg" galines) + foreach(line IN LISTS galines) + if(line MATCHES "^([A-Za-z0-9_]+) *= *(.*[^ \t])[ \t]*$") + set(ga_${CMAKE_MATCH_1} "${CMAKE_MATCH_2}") + endif() + endforeach() + if(NOT DEFINED ga_commit OR ga_commit MATCHES "\\$Format:") + message(FATAL_ERROR "${_LIBDIFFPY_EMSG_GIT_MISSING}") + endif() + set(_gi_commit "${ga_commit}") + set(_gi_date "${ga_date}") + string(REPLACE ", " ";" refs "${ga_refnames}") + foreach(ref IN LISTS refs) + if(ref MATCHES "^tag: ([0-9].*)$") + _libdiffpy_release_version("${CMAKE_MATCH_1}" _gi_version) + set(_gi_patch 0) + break() + endif() + endforeach() + endif() + if(_gi_version STREQUAL "") + if(NOT LIBDIFFPY_FALLBACK_VERSION MATCHES + "^[0-9]+\\.[0-9]+\\.[0-9]+(rc[0-9]+)?\\.post0$") + message(FATAL_ERROR + "LIBDIFFPY_FALLBACK_VERSION must be X.Y.Z.post0 or X.Y.ZrcN.post0.") + endif() + # A development checkout without a release tag, or an untagged + # archive, has no known commit count. Keep the fallback version. + set(_gi_version "${LIBDIFFPY_FALLBACK_VERSION}") + set(_gi_patch 1) + endif() + string(REGEX REPLACE "\\.post.*$" "" vbase "${_gi_version}") + if(NOT vbase MATCHES "^([0-9]+)\\.([0-9]+)\\.([0-9]+)(rc[0-9]+)?$") + message(FATAL_ERROR "Cannot parse libdiffpy version '${_gi_version}'.") + endif() + set(LIBDIFFPY_VERSION_STR "${_gi_version}" PARENT_SCOPE) + set(LIBDIFFPY_VERSION_MAJOR "${CMAKE_MATCH_1}" PARENT_SCOPE) + set(LIBDIFFPY_VERSION_MINOR "${CMAKE_MATCH_2}" PARENT_SCOPE) + set(LIBDIFFPY_VERSION_MICRO "${CMAKE_MATCH_3}" PARENT_SCOPE) + set(LIBDIFFPY_VERSION_PRERELEASE "${CMAKE_MATCH_4}" PARENT_SCOPE) + set(LIBDIFFPY_VERSION_PATCH "${_gi_patch}" PARENT_SCOPE) + set(LIBDIFFPY_VERSION_DATE "${_gi_date}" PARENT_SCOPE) + set(LIBDIFFPY_GIT_SHA "${_gi_commit}" PARENT_SCOPE) +endfunction() + +# Write version.hpp from version.tpl using the LIBDIFFPY_VERSION_* +# variables from libdiffpy_resolve_version. The output file is left +# untouched when its content does not change. +function(libdiffpy_write_version_header template output) + math(EXPR numversion + "((${LIBDIFFPY_VERSION_MAJOR} * 1000 + ${LIBDIFFPY_VERSION_MINOR}) + * 1000 + ${LIBDIFFPY_VERSION_MICRO}) * 1000 + + ${LIBDIFFPY_VERSION_PATCH}") + # Verify that the formulas in version.tpl work as advertised. + if(LIBDIFFPY_VERSION_MINOR GREATER_EQUAL 1000 OR + LIBDIFFPY_VERSION_MICRO GREATER_EQUAL 1000 OR + LIBDIFFPY_VERSION_PATCH GREATER_EQUAL 500) + message(FATAL_ERROR "Inconsistent value of DIFFPY_VERSION = ${numversion}") + endif() + set(DIFFPY_VERSION "${numversion}LL") + if(LIBDIFFPY_VERSION_PRERELEASE) + set(DIFFPY_VERSION "(-500 + ${DIFFPY_VERSION})") + endif() + set(DIFFPY_VERSION_MAJOR "${LIBDIFFPY_VERSION_MAJOR}") + set(DIFFPY_VERSION_MINOR "${LIBDIFFPY_VERSION_MINOR}") + set(DIFFPY_VERSION_MICRO "${LIBDIFFPY_VERSION_MICRO}") + set(DIFFPY_VERSION_PATCH "${LIBDIFFPY_VERSION_PATCH}") + set(DIFFPY_VERSION_STR "${LIBDIFFPY_VERSION_STR}") + set(DIFFPY_VERSION_DATE "${LIBDIFFPY_VERSION_DATE}") + set(DIFFPY_GIT_SHA "${LIBDIFFPY_GIT_SHA}") + configure_file("${template}" "${output}") +endfunction() + +# Set the LIBDIFFPY_VERSION_* variables from an existing version.hpp, +# for example the one bundled in a source distribution. +function(libdiffpy_read_version_header header) + file(READ "${header}" code) + foreach(field MAJOR MINOR MICRO PATCH) + if(NOT code MATCHES "\n#define DIFFPY_VERSION_${field} +([0-9]+)") + message(FATAL_ERROR "Cannot read DIFFPY_VERSION_${field} from ${header}") + endif() + set(LIBDIFFPY_VERSION_${field} "${CMAKE_MATCH_1}" PARENT_SCOPE) + endforeach() + if(NOT code MATCHES "\n#define DIFFPY_VERSION_STR +\"([^\"]*)\"") + message(FATAL_ERROR "Cannot read DIFFPY_VERSION_STR from ${header}") + endif() + set(LIBDIFFPY_VERSION_STR "${CMAKE_MATCH_1}" PARENT_SCOPE) +endfunction() + +# Script mode ----------------------------------------------------------------- + +if(CMAKE_SCRIPT_MODE_FILE STREQUAL CMAKE_CURRENT_LIST_FILE) + libdiffpy_resolve_version() + if(DEFINED LIBDIFFPY_VERSION_HEADER) + if(NOT DEFINED LIBDIFFPY_VERSION_TEMPLATE) + set(LIBDIFFPY_VERSION_TEMPLATE + "${_LIBDIFFPY_VERSION_MODULE_DIR}/../src/diffpy/version.tpl") + endif() + # The build configured the runtime data path for this major.minor. + set(majorminor "${LIBDIFFPY_VERSION_MAJOR}.${LIBDIFFPY_VERSION_MINOR}") + if(DEFINED LIBDIFFPY_CONFIGURED_MAJORMINOR AND + NOT majorminor STREQUAL LIBDIFFPY_CONFIGURED_MAJORMINOR) + message(FATAL_ERROR + "libdiffpy version changed from ${LIBDIFFPY_CONFIGURED_MAJORMINOR} " + "to ${majorminor}. Re-run cmake to update the build.") + endif() + libdiffpy_write_version_header( + "${LIBDIFFPY_VERSION_TEMPLATE}" "${LIBDIFFPY_VERSION_HEADER}") + else() + execute_process(COMMAND "${CMAKE_COMMAND}" -E echo "${LIBDIFFPY_VERSION_STR}") + endif() +endif() diff --git a/cmake/MakeSdist.cmake b/cmake/MakeSdist.cmake new file mode 100644 index 00000000..01384684 --- /dev/null +++ b/cmake/MakeSdist.cmake @@ -0,0 +1,54 @@ +# Create the source distribution libdiffpy-.tar.gz from git HEAD. +# +# cmake [-DLIBDIFFPY_SDIST_DIR=] -P cmake/MakeSdist.cmake +# +# The archive carries the generated src/diffpy/version.hpp in place of +# version.tpl, so that it builds without git. See .gitattributes for the +# files excluded from the archive. Requires git 2.30 or later. + +cmake_minimum_required(VERSION 3.22) + +include("${CMAKE_CURRENT_LIST_DIR}/LibdiffpyVersion.cmake") +get_filename_component(_srcdir "${CMAKE_CURRENT_LIST_DIR}/.." ABSOLUTE) +if(NOT DEFINED LIBDIFFPY_SDIST_DIR) + set(LIBDIFFPY_SDIST_DIR "${_srcdir}") +endif() + +find_program(LIBDIFFPY_GIT_EXECUTABLE NAMES git) +if(LIBDIFFPY_GIT_EXECUTABLE) + execute_process( + COMMAND "${LIBDIFFPY_GIT_EXECUTABLE}" rev-parse --verify --quiet HEAD + WORKING_DIRECTORY "${_srcdir}" + RESULT_VARIABLE _rc + OUTPUT_QUIET ERROR_QUIET) +endif() +if(NOT LIBDIFFPY_GIT_EXECUTABLE OR _rc) + message(FATAL_ERROR "sdist must be built from a git repository.") +endif() + +libdiffpy_resolve_version() +set(_name "libdiffpy-${LIBDIFFPY_VERSION_STR}") +set(_tmpdir "${LIBDIFFPY_SDIST_DIR}/${_name}.sdist-tmp") +set(_output "${LIBDIFFPY_SDIST_DIR}/${_name}.tar.gz") +file(REMOVE_RECURSE "${_tmpdir}") +file(MAKE_DIRECTORY "${_tmpdir}") + +# Generate version.hpp from the committed template. +execute_process( + COMMAND "${LIBDIFFPY_GIT_EXECUTABLE}" show HEAD:src/diffpy/version.tpl + WORKING_DIRECTORY "${_srcdir}" + OUTPUT_FILE "${_tmpdir}/version.tpl" + COMMAND_ERROR_IS_FATAL ANY) +libdiffpy_write_version_header("${_tmpdir}/version.tpl" "${_tmpdir}/version.hpp") + +# --add-file takes the archive path from the last preceding --prefix. +execute_process( + COMMAND "${LIBDIFFPY_GIT_EXECUTABLE}" -c tar.umask=0022 archive + --worktree-attributes --format=tar.gz + "--prefix=${_name}/src/diffpy/" "--add-file=${_tmpdir}/version.hpp" + "--prefix=${_name}/" "--output=${_output}" + HEAD -- . ":(exclude)src/diffpy/version.tpl" + WORKING_DIRECTORY "${_srcdir}" + COMMAND_ERROR_IS_FATAL ANY) +file(REMOVE_RECURSE "${_tmpdir}") +message(STATUS "Created ${_output}") diff --git a/cmake/ZeroCounters.cmake b/cmake/ZeroCounters.cmake new file mode 100644 index 00000000..f5ded2af --- /dev/null +++ b/cmake/ZeroCounters.cmake @@ -0,0 +1,13 @@ +# Remove cumulative coverage counts (*.gcda files) under a build directory. +# +# cmake -DLIBDIFFPY_COVERAGE_DIR= -P ZeroCounters.cmake + +if(NOT DEFINED LIBDIFFPY_COVERAGE_DIR) + message(FATAL_ERROR "Define LIBDIFFPY_COVERAGE_DIR.") +endif() +file(GLOB_RECURSE _gcda_files "${LIBDIFFPY_COVERAGE_DIR}/*.gcda") +if(_gcda_files) + file(REMOVE ${_gcda_files}) +endif() +list(LENGTH _gcda_files _count) +message(STATUS "Removed ${_count} coverage data files.") diff --git a/site_scons/gitarchive.cfg b/cmake/gitarchive.cfg similarity index 100% rename from site_scons/gitarchive.cfg rename to cmake/gitarchive.cfg diff --git a/cmake/libdiffpyConfig.cmake.in b/cmake/libdiffpyConfig.cmake.in new file mode 100644 index 00000000..11700d60 --- /dev/null +++ b/cmake/libdiffpyConfig.cmake.in @@ -0,0 +1,34 @@ +# CMake package configuration for libdiffpy. +# +# Usage: +# +# find_package(libdiffpy REQUIRED) +# target_link_libraries(myprogram PRIVATE libdiffpy::diffpy) +# +# Variables: +# +# libdiffpy_HAS_OBJCRYST true when libdiffpy was built with ObjCryst +# libdiffpy_RUNTIME_DIR directory of the installed data files + +@PACKAGE_INIT@ + +include(CMakeFindDependencyMacro) + +set(libdiffpy_HAS_OBJCRYST @LIBDIFFPY_HAS_OBJCRYST@) +set_and_check(libdiffpy_RUNTIME_DIR "@PACKAGE_LIBDIFFPY_RUNTIME_INSTALL_DIR@") + +find_dependency(Boost CONFIG COMPONENTS serialization) +if(NOT "@_libdiffpy_library_type@" STREQUAL "SHARED_LIBRARY") + find_dependency(GSL) +endif() +if(libdiffpy_HAS_OBJCRYST) + list(PREPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_LIST_DIR}") + find_dependency(ObjCryst) + list(POP_FRONT CMAKE_MODULE_PATH) +endif() + +if(NOT TARGET libdiffpy::diffpy) + include("${CMAKE_CURRENT_LIST_DIR}/libdiffpyTargets.cmake") +endif() + +check_required_components(libdiffpy) diff --git a/docs/source/conf.py b/docs/source/conf.py index 2da4f391..cd20b1c6 100644 --- a/docs/source/conf.py +++ b/docs/source/conf.py @@ -13,6 +13,7 @@ # All configuration values have a default; values that are commented out # serve to show the default. +import subprocess import sys import time from pathlib import Path @@ -23,14 +24,18 @@ # sys.path.insert(0, str(Path(".").resolve())) sys.path.insert(0, str(Path("../..").resolve())) sys.path.insert(0, str(Path("../../src").resolve())) -sys.path.insert(0, str(Path("../../site_scons").resolve())) # libdiffpy is a C++ library, so take the version from git tags through -# the SCons build utilities rather than from installed Python metadata. +# the CMake version script rather than from installed Python metadata. try: - from libdiffpybuildutils import getversion - - fullversion = getversion()["version"] + versionscript = Path(__file__).resolve().parents[2] / "cmake" + versionscript /= "LibdiffpyVersion.cmake" + fullversion = subprocess.run( + ["cmake", "-P", str(versionscript)], + check=True, + capture_output=True, + text=True, + ).stdout.strip() except Exception: fullversion = "No version found. The correct version will appear in the released version." # noqa: E501 diff --git a/docs/source/development.rst b/docs/source/development.rst index 602ff55b..49ecc459 100644 --- a/docs/source/development.rst +++ b/docs/source/development.rst @@ -25,74 +25,106 @@ Build the library Build the shared library with :: - scons -j4 + cmake --preset release + cmake --build --preset release --parallel 4 -The ``build`` option selects the compiler settings, for example -``scons -j4 build=develop``. On Linux and macOS the build variants are +The CMake presets require CMake 3.22 or later and use Ninja. On Windows, +run them in a command prompt with the conda build environment activated. +The build presets are -* ``fast`` - optimized build with C assertion checks disabled (default) -* ``debug`` - build with debug information and C assertion checks -* ``develop`` - unoptimized build with C assertion checks +* ``release`` - optimized build with C assertion checks disabled +* ``debug`` - unoptimized build with debug information and C assertion checks * ``coverage`` - unoptimized build instrumented for coverage analysis -The library is built in the ``build/-`` directory, -for example ``build/fast-x86_64/``. Run ``scons --help`` for the full list -of build targets and options. - -To use the same options for every build, set them in a ``sconsvars.py`` -file in the repository root, for example :: - - build = 'develop' - -The SCons construction environment can be further customized in a -``sconscript.local`` file in the repository root. Both files are ignored -by git. For example, the following ``sconscript.local`` builds with the -conda-forge compiler and records the conda library directory as the runtime -path, so that the unit tests find the conda libraries :: - - Import('env') - import os - prefix = os.environ['CONDA_PREFIX'] - env.Replace(CXX=os.path.join(prefix, 'bin', 'c++')) - env.Prepend(CCFLAGS=['-isystem' + os.path.join(prefix, 'include')]) - env.Append(LIBPATH=os.path.join(prefix, 'lib')) - env.Append(LINKFLAGS='-Wl,-rpath,' + os.path.join(prefix, 'lib')) +The library is built in the ``build/`` directory, for example +``build/release/``. Use ``cmake --install build/release`` to install it. +Pass ``-DCMAKE_INSTALL_PREFIX=`` when configuring to choose the +installation directory. + +To use the same options for every build, set them in a ``CMakeUserPresets.json`` +file in the repository root. To use another generator, configure a separate +directory with ``cmake -S . -B -G ""``. To disable the +ObjCryst adapter, pass ``-DLIBDIFFPY_ENABLE_OBJCRYST=OFF``. For gprof +instrumentation, use ``-DLIBDIFFPY_PROFILE=ON``. + +Embed libdiffpy in another CMake project +---------------------------------------- + +Add libdiffpy with ``add_subdirectory``. It then keeps the parent's build +type, install prefix, output directories and compiler options. A Python +extension would typically build it as an OBJECT library :: + + set(LIBDIFFPY_INSTALL OFF) + set(LIBDIFFPY_BUILD_OBJECT_LIB ON) + set(LIBDIFFPY_ENABLE_OBJCRYST OFF) + set(LIBDIFFPY_RUNTIME_RELPATH data) + set(CMAKE_POSITION_INDEPENDENT_CODE ON) + add_subdirectory(extern/libdiffpy) + target_link_libraries(my_extension PRIVATE libdiffpy::diffpy) + +An OBJECT library keeps all factory and serialization registrations; a static +library (``BUILD_SHARED_LIBS=OFF``) may need whole-archive linking to keep +them. To use your own ``Boost::serialization`` or ``GSL::gsl``, define those +targets before ``add_subdirectory``. + +With ``LIBDIFFPY_INSTALL=OFF`` nothing is installed. Copy ``src/runtime/*.dat`` +and ``src/runtime/bvparm2011sel.cif`` into the ``LIBDIFFPY_RUNTIME_RELPATH`` +directory next to your extension, ``data/`` in this example. Run the unit tests ------------------ -The unit tests use the CxxTest framework. Build and run all of them with :: +The unit tests use Catch2 3.5 or later. Build and run all of them with :: - scons -j4 test + cmake --preset debug + cmake --build --preset debug --parallel 4 + ctest --preset debug To run only some of the tests, pass fixed-string patterns of the test file -names with the ``tests`` option. Patterns are case-insensitive and can be +names with the ``LIBDIFFPY_TESTS`` option. Patterns are case-insensitive and can be separated by commas :: - scons -j4 test tests=PDFCalculator,Lattice + cmake --preset debug -DLIBDIFFPY_TESTS=PDFCalculator,Lattice + cmake --build --preset debug --target check + +Set ``LIBDIFFPY_TESTS`` to an empty string to build all suites again. +CTest runs each suite as a separate test, so ``ctest --preset debug -R Lattice`` +selects suites by name. To run a single case or a group of cases, pass its +name or tag to the Catch2 executable :: + + build/debug/bin/alltests "TestLattice::test_angle" + build/debug/bin/alltests "[Lattice]" To run the tests against the installed library instead of the one in the -``build`` directory, use ``scons test test_installed=True``. +``build`` directory, configure a separate directory with +``-DLIBDIFFPY_TEST_INSTALLED=ON``. If CMake cannot locate the installation, +add its prefix to ``CMAKE_PREFIX_PATH``. Measure test coverage --------------------- -Build with the ``coverage`` variant, run the tests and summarize the results +Build with the ``coverage`` preset, run the tests and summarize the results with ``gcovr`` :: - scons -j4 build=coverage test - gcovr --root . --filter 'build/coverage-.*/diffpy/' + cmake --preset coverage + cmake --build --preset coverage --parallel 4 + ctest --preset coverage + gcovr --root . --filter src/diffpy/ -Use ``scons zerocounters`` to reset the coverage counts between runs. +Use ``cmake --build --preset coverage --target zerocounters`` to reset the +coverage counts between runs. Test changes with diffpy.srreal ------------------------------- `diffpy.srreal `_ compiles the -libdiffpy sources from the commit pinned in its -``cmake/LibdiffpyVersion.cmake`` file. To build diffpy.srreal against a local -libdiffpy clone, point the CMake ``FETCHCONTENT_SOURCE_DIR_LIBDIFFPY`` -variable to it. diffpy.srreal checks that the clone is at the pinned commit, -so update the pin in ``cmake/LibdiffpyVersion.cmake`` when testing new -libdiffpy commits. See the diffpy.srreal documentation for its build -instructions. +libdiffpy sources from the commit pinned in its ``extern/libdiffpy`` submodule. +See the diffpy.srreal documentation for its build instructions. + +Create a source distribution +---------------------------- + +From a Git checkout, run ``cmake -P cmake/MakeSdist.cmake`` to package +``HEAD`` as ``libdiffpy-.tar.gz`` in the repository root. To write +the archive into a configured build directory, build the ``sdist`` target. +The archive includes the version header and can be built without Git. diff --git a/news/cmake-build.rst b/news/cmake-build.rst new file mode 100644 index 00000000..8fe3b69b --- /dev/null +++ b/news/cmake-build.rst @@ -0,0 +1,17 @@ +**Added:** + +* Ninja presets for release, debug and coverage builds. +* An installed CMake package for consumers using ``find_package(libdiffpy)`` + and the ``libdiffpy::diffpy`` target. + +**Changed:** + +* Building from source requires CMake 3.22 or later. Use ``cmake --install`` + to install the shared library, headers and runtime data. +* Building the unit tests requires Catch2 3.5 or later. CTest runs the full + suite in one process. The Catch2 executable accepts case names and tags. + +**Removed:** + +* The SCons build scripts and their ``sconsvars.py`` and ``sconscript.local`` + configuration files. diff --git a/requirements/build.txt b/requirements/build.txt index 802684b1..b3fb0da3 100644 --- a/requirements/build.txt +++ b/requirements/build.txt @@ -1,2 +1,3 @@ cxx-compiler -scons +cmake>=3.22 +ninja diff --git a/requirements/tests.txt b/requirements/tests.txt index a5570111..562b6c86 100644 --- a/requirements/tests.txt +++ b/requirements/tests.txt @@ -1,2 +1,2 @@ -cxxtest +catch2>=3.5 gcovr diff --git a/site_scons/fallback_version.py b/site_scons/fallback_version.py deleted file mode 100644 index b0a32458..00000000 --- a/site_scons/fallback_version.py +++ /dev/null @@ -1,11 +0,0 @@ -#!/usr/bin/env python - -"""Definition of FALLBACK_VERSION. - -Use it when a checkout or source archive has no release tag from which -to determine the version. - -Review FALLBACK_VERSION before a release. -""" - -FALLBACK_VERSION = "1.5.0rc0.post0" diff --git a/site_scons/libdiffpybuildutils.py b/site_scons/libdiffpybuildutils.py deleted file mode 100644 index 396df359..00000000 --- a/site_scons/libdiffpybuildutils.py +++ /dev/null @@ -1,112 +0,0 @@ -#!/usr/bin/env python - -"""Utility functions used by scons and sphinx for version extraction.""" - -import os -import re -import subprocess - -MYDIR = os.path.dirname(os.path.abspath(__file__)) - -EMSG_GIT_MISSING = """ -Cannot determine libdiffpy version. Compile from a git repository -or use a source archive from - - https://github.com/diffpy/libdiffpy/releases -""" - -RELEASE_TAG = re.compile(r"([0-9]+\.[0-9]+\.[0-9]+)(?:-rc\.([0-9]+))?") - - -def release_version(tag): - """Normalize a release tag to the internal version format.""" - match = RELEASE_TAG.fullmatch(tag) - if not match: - raise RuntimeError( - f"Invalid release tag {tag!r}; expected X.Y.Z or X.Y.Z-rc.N." - ) - base, candidate = match.groups() - return base if candidate is None else f"{base}rc{candidate}" - - -def gitinfo(): - "Extract dictionary of version data from git records." - global _cached_gitinfo - if _cached_gitinfo is not None: - return _cached_gitinfo - kw = dict( - stdout=subprocess.PIPE, - stderr=subprocess.DEVNULL, - cwd=MYDIR, - text=True, - ) - proc = subprocess.run(["git", "log", "-1", "--format=%H %ci"], **kw) - if proc.returncode: - _cached_gitinfo = {} - return _cached_gitinfo - rv = {} - rv["commit"], rv["date"] = proc.stdout.strip().split(None, 1) - # --long makes the commit count explicit even at a tag. Split from - # the right so the -rc.N suffix remains part of the release tag. - proc = subprocess.run( - ["git", "describe", "--tags", "--long", "--match=[0-9]*"], **kw - ) - if not proc.returncode: - tag, count, _ = proc.stdout.strip().rsplit("-", 2) - rv["patchnumber"] = int(count) - rv["version"] = release_version(tag) - if rv["patchnumber"]: - rv["version"] += ".post" + count - _cached_gitinfo = rv - return _cached_gitinfo - - -_cached_gitinfo = None - - -def getversion(): - """Extract version from gitinfo and/or gitarchive.cfg file. - - Prefer the version from gitinfo or an expanded git archive tag. Use - FALLBACK_VERSION only when neither provides a version. - """ - gitarchivecfgfile = os.path.join(MYDIR, "gitarchive.cfg") - assert os.path.isfile(gitarchivecfgfile) - with open(gitarchivecfgfile) as fp: - gacontent = fp.read() - gaitems = re.findall(r"^(\w+) *= *(\S.*?)\s*$", gacontent, re.M) - ga = dict(gaitems) - gi = gitinfo() - rv = {} - if not gi: - # Not a git repository. Require an expanded source archive. - if "$Format:" in ga["commit"]: - raise RuntimeError(EMSG_GIT_MISSING) - gi = dict(commit=ga["commit"], date=ga["date"]) - for ref in ga["refnames"].split(", "): - if ref.startswith("tag: ") and re.match(r"[0-9]", ref[5:]): - gi["version"] = release_version(ref[5:]) - gi["patchnumber"] = 0 - break - rv.update(gi) - if "version" not in gi: - from fallback_version import FALLBACK_VERSION - - if not re.fullmatch( - r"[0-9]+\.[0-9]+\.[0-9]+(?:rc[0-9]+)?\.post0", FALLBACK_VERSION - ): - raise RuntimeError( - "FALLBACK_VERSION must be X.Y.Z.post0 or X.Y.ZrcN.post0." - ) - # A development checkout without a new-format tag, or an untagged - # archive, has no known commit count. Keep the fallback version. - rv["version"] = FALLBACK_VERSION - rv["patchnumber"] = 1 - # rv['version'] is resolved, let's parse its subfields. - vbase = rv["version"].split(".post")[0] - mx = re.fullmatch(r"([0-9]+)\.([0-9]+)\.([0-9]+)(rc[0-9]+)?", vbase) - rv["major"] = int(mx.group(1)) - rv["minor"] = int(mx.group(2)) - rv["micro"] = int(mx.group(3)) - rv["prerelease"] = mx.group(4) - return rv diff --git a/site_scons/site_tools/cxxtest.py b/site_scons/site_tools/cxxtest.py deleted file mode 100644 index 8443e553..00000000 --- a/site_scons/site_tools/cxxtest.py +++ /dev/null @@ -1,396 +0,0 @@ -# coding=UTF-8 -#------------------------------------------------------------------------- -# CxxTest: A lightweight C++ unit testing library. -# Copyright (c) 2008 Sandia Corporation. -# This software is distributed under the LGPL License v3 -# For more information, see the COPYING file in the top CxxTest directory. -# Under the terms of Contract DE-AC04-94AL85000 with Sandia Corporation, -# the U.S. Government retains certain rights in this software. -#------------------------------------------------------------------------- -# -# == Preamble == -# Authors of this script are in the Authors file in the same directory as this -# script. -# -# Maintainer: Gašper Ažman -# -# This file is maintained as a part of the CxxTest test suite. -# -# == About == -# -# This builder correctly tracks dependencies and supports just about every -# configuration option for CxxTest that I can think of. It automatically -# defines a target "check" (configurable), so all tests can be run with a -# % scons check -# This will first compile and then run the tests. -# -# The default configuration assumes that cxxtest is located at the base source -# directory (where SConstruct is), that the cxxtestgen is under -# cxxtest/bin/cxxtestgen and headers are in cxxtest/cxxtest/. The -# header include path is automatically added to CPPPATH. It, however, can also -# recognise that cxxtest is installed system-wide (based on redhat's RPM). -# -# For a list of environment variables and their defaults, see the generate() -# function. -# -# This should be in a file called cxxtest.py somewhere in the scons toolpath. -# (default: #/site_scons/site_tools/) -# -# == Usage: == -# -# For configuration options, check the comment of the generate() function. -# -# This builder has a variety of different possible usages, so bear with me. -# -# env.CxxTest('target') -# The simplest of them all, it models the Program call. This sees if target.t.h -# is around and passes it through the cxxtestgen and compiles it. Might only -# work on unix though, because target can't have a suffix right now. -# -# env.CxxTest(['target.t.h']) -# This compiles target.t.h as in the previous example, but now sees that it is a -# source file. It need not have the same suffix as the env['CXXTEST_SUFFIX'] -# variable dictates. The only file provided is taken as the test source file. -# -# env.CxxTest(['test1.t.h','test1_lib.cpp','test1_lib2.cpp','test2.t.h',...]) -# You may also specify multiple source files. In this case, the 1st file that -# ends with CXXTEST_SUFFIX (default: .t.h) will be taken as the default test -# file. All others will be run with the --part switch and linked in. All files -# *not* having the right suffix will be passed to the Program call verbatim. -# -# In the last two cases, you may also specify the desired name of the test as -# the 1st argument to the function. This will result in the end executable -# called that. Normal Program builder rules apply. -# - -from SCons.Script import * -from SCons.Builder import Builder -from SCons.Util import PrependPath, unique, uniquer_hashables -import os - -# A warning class to notify users of problems -class ToolCxxTestWarning(SCons.Warnings.SConsWarning): - pass - -SCons.Warnings.enableWarningClass(ToolCxxTestWarning) - -def accumulateEnvVar(dicts, name, default = []): - """ - Accumulates the values under key 'name' from the list of dictionaries dict. - The default value is appended to the end list if 'name' does not exist in - the dict. - """ - final = [] - for d in dicts: - final += Split(d.get(name, default)) - return final - -def multiget(dictlist, key, default = None): - """ - Takes a list of dictionaries as its 1st argument. Checks if the key exists - in each one and returns the 1st one it finds. If the key is found in no - dictionaries, the default is returned. - """ - for dict in dictlist: - if key in dict: - return dict[key] - else: - return default - -def envget(env, key, default=None): - """Look in the env, then in os.environ. Otherwise same as multiget.""" - return multiget([env, os.environ], key, default) - -def prepend_ld_library_path(env, overrides, **kwargs): - """Prepend LD_LIBRARY_PATH with LIBPATH to run successfully programs that - were linked against local shared libraries.""" - # make it unique but preserve order ... - libpath = uniquer_hashables(Split(kwargs.get('CXXTEST_LIBPATH', [])) + - Split(env.get( 'CXXTEST_LIBPATH', []))) - if len(libpath) > 0: - libpath = env.arg2nodes(libpath, env.fs.Dir) - platform = env.get('PLATFORM','') - if platform == 'win32': - var = 'PATH' - else: - var = 'LD_LIBRARY_PATH' - eenv = overrides.get('ENV', env['ENV'].copy()) - canonicalize = lambda p : p.abspath - eenv[var] = PrependPath(eenv.get(var,''), libpath, os.pathsep, 1, canonicalize) - overrides['ENV'] = eenv - return overrides - -def UnitTest(env, target, source = [], **kwargs): - """ - Prepares the Program call arguments, calls Program and adds the result to - the check target. - """ - # get the c and cxx flags to process. - ccflags = Split( multiget([kwargs, env, os.environ], 'CCFLAGS' )) - cxxflags = Split( multiget([kwargs, env, os.environ], 'CXXFLAGS')) - # get the removal c and cxx flags - cxxremove = set( Split( multiget([kwargs, env, os.environ],'CXXTEST_CXXFLAGS_REMOVE'))) - ccremove = set( Split( multiget([kwargs, env, os.environ],'CXXTEST_CCFLAGS_REMOVE' ))) - # remove the required flags - ccflags = [item for item in ccflags if item not in ccremove] - cxxflags = [item for item in cxxflags if item not in cxxremove] - # fill the flags into kwargs - kwargs["CXXFLAGS"] = cxxflags - kwargs["CCFLAGS"] = ccflags - test = env.Program(target, source = source, **kwargs) - testCommand = multiget([kwargs, env, os.environ], 'CXXTEST_COMMAND') - if testCommand: - testCommand = testCommand.replace('%t', test[0].abspath) - else: - testCommand = test[0].abspath - if multiget([kwargs, env, os.environ], 'CXXTEST_SKIP_ERRORS', False): - runner = env.Action(testCommand, exitstatfunc=lambda x:0) - else: - runner = env.Action(testCommand) - overrides = prepend_ld_library_path(env, {}, **kwargs) - cxxtest_target = multiget([kwargs, env], 'CXXTEST_TARGET') - env.Alias(cxxtest_target, test, runner, **overrides) - env.AlwaysBuild(cxxtest_target) - return test - -def isValidScriptPath(cxxtestgen): - """check keyword arg or environment variable locating cxxtestgen script""" - - if cxxtestgen and os.path.exists(cxxtestgen): - return True - else: - SCons.Warnings.warn(ToolCxxTestWarning, - "Invalid CXXTEST environment variable specified!") - return False - -def defaultCxxTestGenLocation(env): - return os.path.join( - envget(env, 'CXXTEST_CXXTESTGEN_DEFAULT_LOCATION'), - envget(env, 'CXXTEST_CXXTESTGEN_SCRIPT_NAME') - ) - -def findCxxTestGen(env): - """locate the cxxtestgen script by checking environment, path and project""" - - # check the SCons environment... - # Then, check the OS environment... - cxxtest = envget(env, 'CXXTEST', None) - - # check for common passing errors and provide diagnostics. - if isinstance(cxxtest, (list, tuple, dict)): - SCons.Warnings.warn( - ToolCxxTestWarning, - "The CXXTEST variable was specified as a list." - " This is not supported. Please pass a string." - ) - - if cxxtest: - try: - #try getting the absolute path of the file first. - # Required to expand '#' - cxxtest = env.File(cxxtest).abspath - except TypeError: - try: - #maybe only the directory was specified? - cxxtest = env.File( - os.path.join(cxxtest, defaultCxxTestGenLocation(env) - )).abspath - except TypeError: - pass - # If the user specified the location in the environment, - # make sure it was correct - if isValidScriptPath(cxxtest): - return os.path.realpath(cxxtest) - - # No valid environment variable found, so... - # Next, check the path... - # Next, check the project - check_path = os.path.join( - envget(env, 'CXXTEST_INSTALL_DIR'), - envget(env, 'CXXTEST_CXXTESTGEN_DEFAULT_LOCATION')) - - cxxtest = (env.WhereIs(envget(env, 'CXXTEST_CXXTESTGEN_SCRIPT_NAME')) or - env.WhereIs(envget(env, 'CXXTEST_CXXTESTGEN_SCRIPT_NAME'), - path=[Dir(check_path).abspath])) - - if cxxtest: - return cxxtest - else: - # If we weren't able to locate the cxxtestgen script, complain... - SCons.Warnings.warn( - ToolCxxTestWarning, - "Unable to locate cxxtestgen in environment, path or" - " project!\n" - "Please set the CXXTEST variable to the path of the" - " cxxtestgen script" - ) - return None - -def findCxxTestHeaders(env): - searchfile = 'TestSuite.h' - cxxtestgen_pathlen = len(defaultCxxTestGenLocation(env)) - - default_path = Dir(envget(env,'CXXTEST_INSTALL_DIR')).abspath - - os_cxxtestgen = os.path.realpath(File(env['CXXTEST']).abspath) - alt_path = os_cxxtestgen[:-cxxtestgen_pathlen] - - searchpaths = [default_path, alt_path] - foundpaths = [] - for p in searchpaths: - if os.path.exists(os.path.join(p, 'cxxtest', searchfile)): - foundpaths.append(p) - return foundpaths - -def generate(env, **kwargs): - """ - Keyword arguments (all can be set via environment variables as well): - CXXTEST - the path to the cxxtestgen script. - Default: searches SCons environment, OS environment, - path and project in that order. Instead of setting this, - you can also set CXXTEST_INSTALL_DIR - CXXTEST_RUNNER - the runner to use. Default: ErrorPrinter - CXXTEST_OPTS - other options to pass to cxxtest. Default: '' - CXXTEST_SUFFIX - the suffix of the test files. Default: '.t.h' - CXXTEST_TARGET - the target to append the tests to. Default: check - CXXTEST_COMMAND - the command that will be executed to run the test, - %t will be replace with the test executable. - Can be used for example for MPI or valgrind tests. - Default: %t - CXXTEST_CXXFLAGS_REMOVE - the flags that cxxtests can't compile with, - or give lots of warnings. Will be stripped. - Default: -pedantic -Weffc++ - CXXTEST_CCFLAGS_REMOVE - the same thing as CXXTEST_CXXFLAGS_REMOVE, just for - CCFLAGS. Default: same as CXXFLAGS. - CXXTEST_PYTHON - the path to the python binary. - Default: searches path for python - CXXTEST_SKIP_ERRORS - set to True to continue running the next test if one - test fails. Default: False - CXXTEST_CPPPATH - If you do not want to clutter your global CPPPATH with the - CxxTest header files and other stuff you only need for - your tests, this is the variable to set. Behaves as - CPPPATH does. - CXXTEST_LIBPATH - If your test is linked to shared libraries which are - outside of standard directories. This is used as LIBPATH - when compiling the test program and to modify - LD_LIBRARY_PATH (or PATH on win32) when running the - program. - CXXTEST_INSTALL_DIR - this is where you tell the builder where CxxTest is - installed. The install directory has cxxtest, - python, docs and other subdirectories. - ... and all others that Program() accepts, like CPPPATH etc. - """ - # - # Expected behaviour: keyword arguments override environment variables; - # environment variables override default settings. - # - env.SetDefault( CXXTEST_RUNNER = 'ErrorPrinter' ) - env.SetDefault( CXXTEST_OPTS = '' ) - env.SetDefault( CXXTEST_SUFFIX = '.t.h' ) - env.SetDefault( CXXTEST_TARGET = 'check' ) - env.SetDefault( CXXTEST_CPPPATH = ['#'] ) - env.SetDefault( CXXTEST_PYTHON = env.WhereIs('python') ) - env.SetDefault( CXXTEST_SKIP_ERRORS = False ) - env.SetDefault( CXXTEST_CXXFLAGS_REMOVE = - ['-pedantic','-Weffc++','-pedantic-errors'] ) - env.SetDefault( CXXTEST_CCFLAGS_REMOVE = - ['-pedantic','-Weffc++','-pedantic-errors'] ) - env.SetDefault( CXXTEST_INSTALL_DIR = '#/cxxtest/' ) - - # this one's not for public use - it documents where the cxxtestgen script - # is located in the CxxTest tree normally. - env.SetDefault( CXXTEST_CXXTESTGEN_DEFAULT_LOCATION = 'bin' ) - # the cxxtestgen script name. - env.SetDefault( CXXTEST_CXXTESTGEN_SCRIPT_NAME = 'cxxtestgen' ) - - #Here's where keyword arguments are applied - env.Replace(**kwargs) - - #If the user specified the path to CXXTEST, make sure it is correct - #otherwise, search for and set the default toolpath. - if 'CXXTEST' not in kwargs or not isValidScriptPath(kwargs['CXXTEST']): - env["CXXTEST"] = findCxxTestGen(env) - - # find and add the CxxTest headers to the path. - env.AppendUnique( CXXTEST_CPPPATH = findCxxTestHeaders(env) ) - - cxxtest = env['CXXTEST'] - if cxxtest: - # - # Create the Builder (only if we have a valid cxxtestgen!) - # - cxxtest_builder = Builder( - action = - [["$CXXTEST_PYTHON",cxxtest,"--runner=$CXXTEST_RUNNER", - "$CXXTEST_OPTS","$CXXTEST_ROOT_PART","-o","$TARGET","$SOURCE"]], - suffix = ".cpp", - src_suffix = '$CXXTEST_SUFFIX' - ) - else: - cxxtest_builder = (lambda *a: sys.stderr.write("ERROR: CXXTESTGEN NOT FOUND!")) - - def CxxTest(env, target, source = None, **kwargs): - """Usage: - The function is modelled to be called as the Program() call is: - env.CxxTest('target_name') will build the test from the source - target_name + env['CXXTEST_SUFFIX'], - env.CxxTest('target_name', source = 'test_src.t.h') will build the test - from test_src.t.h source, - env.CxxTest('target_name, source = ['test_src.t.h', other_srcs] - builds the test from source[0] and links in other files mentioned in - sources, - You may also add additional arguments to the function. In that case, they - will be passed to the actual Program builder call unmodified. Convenient - for passing different CPPPATHs and the sort. This function also appends - CXXTEST_CPPPATH to CPPPATH. It does not clutter the environment's CPPPATH. - """ - if (source == None): - suffix = multiget([kwargs, env, os.environ], 'CXXTEST_SUFFIX', "") - source = [t + suffix for t in target] - sources = Flatten(Split(source)) - headers = [] - linkins = [] - for l in sources: - # check whether this is a file object or a string path - try: - s = l.abspath - except AttributeError: - s = l - - if s.endswith(multiget([kwargs, env, os.environ], 'CXXTEST_SUFFIX', None)): - headers.append(l) - else: - linkins.append(l) - - deps = [] - if len(headers) == 0: - if len(linkins) != 0: - # the 1st source specified is the test - deps.append(env.CxxTestCpp(linkins.pop(0), **kwargs)) - else: - deps.append(env.CxxTestCpp(headers.pop(0), **kwargs)) - deps.extend( - [env.CxxTestCpp(header, CXXTEST_RUNNER = 'none', - CXXTEST_ROOT_PART = '--part', **kwargs) - for header in headers] - ) - deps.extend(linkins) - kwargs['CPPPATH'] = unique( - Split(kwargs.get('CPPPATH', [])) + - Split(env.get( 'CPPPATH', [])) + - Split(kwargs.get('CXXTEST_CPPPATH', [])) + - Split(env.get( 'CXXTEST_CPPPATH', [])) - ) - kwargs['LIBPATH'] = unique( - Split(kwargs.get('LIBPATH', [])) + - Split(env.get( 'LIBPATH', [])) + - Split(kwargs.get('CXXTEST_LIBPATH', [])) + - Split(env.get( 'CXXTEST_LIBPATH', [])) - ) - - return UnitTest(env, target, source = deps, **kwargs) - - env.Append( BUILDERS = { "CxxTest" : CxxTest, "CxxTestCpp" : cxxtest_builder } ) - -def exists(env): - return os.path.exists(env['CXXTEST']) diff --git a/src/SConscript b/src/SConscript deleted file mode 100644 index 7d69998a..00000000 --- a/src/SConscript +++ /dev/null @@ -1,271 +0,0 @@ -import os -from os.path import join as pjoin - -Import('env') - -# Build environment configuration -------------------------------------------- - -# Insert LIBRARY_PATH explicitly because some compilers -# ignore it in the system environment. -env.PrependUnique(LIBPATH=env['ENV'].get('LIBRARY_PATH', '').split(os.pathsep)) - -# Use C++ compiler specified by the 'tool' option. -# Tools are loaded into env in place, because subsidiary SConscripts -# Import the shared env object. -if env['tool'] == 'intelc': - icpc = env.WhereIs('icpc') - if not icpc: - print("Cannot find the Intel C/C++ compiler 'icpc'.") - Exit(1) - env.Tool('intelc', topdir=icpc[:icpc.rfind('/bin')]) - -if env['tool'] == 'clang': - if not env.WhereIs('clang++'): - print("Cannot find the clang++ compiler.") - Exit(1) - env.Tool('clangxx') - -if env['tool'] == 'gcc': - if not env.WhereIs('g++'): - print("Cannot find the g++ compiler.") - Exit(1) - if env['PLATFORM'] == 'win32': - env.Tool('mingw') - -# libobjcryst is built with double precision for ObjCryst's REAL type. -# Match that ABI in all consumers of ObjCryst headers. -env.PrependUnique(CPPDEFINES={'REAL': 'double'}) - -# Platform specific intricacies. -fast_linkflags = ['-s'] - -# macOS specific options -if env['PLATFORM'] == 'darwin': - env.AppendUnique(CXXFLAGS='-ftemplate-depth-256') - env.AppendUnique(SHLINKFLAGS=['-install_name', '$TARGET.abspath']) - env.AppendUnique(SHLINKFLAGS='-headerpad_max_install_names') - fast_linkflags[:] = [] - -# Compiler specific options -if env['PLATFORM'] == 'win32': - env.AppendUnique(CPPDEFINES=['_USE_MATH_DEFINES']) - if env['tool'] == 'clang': - # clang++ on Windows - env['CC'] = 'clang' - env['CCFLAGS'] = ['-O3', '-ffast-math', '-m64'] - env.AppendUnique(CXXFLAGS='-std=c++23') - env['LINKFLAGS'] = ['-fuse-ld=lld-link', '-m64', '-lstdc++'] - elif env['tool'] == 'gcc': - # MinGW - env.PrependUnique(CCFLAGS=['-O3', '-m64']) - env.AppendUnique(CXXFLAGS='-std=c++23') - env.AppendUnique(LINKFLAGS='-m64') - else: - # Visual C++ - # /W3 rather than /Wall, which floods the log with warnings from the - # standard library and boost headers. /bigobj is needed for the - # boost serialization code. - env.PrependUnique(CCFLAGS=['/Ox', '/EHsc', '/MD', '/W3', '/bigobj']) - env.AppendUnique(CXXFLAGS='/std:c++latest') - env.AppendUnique(CPPDEFINES={'NDEBUG': None}) - env.AppendUnique(LINKFLAGS=['/OPT:NOREF', '/OPT:NOICF']) - -else: - # Specify minimum C++ standard. Allow later standard from sconscript.local. - # In case of multiple `-std` options the last option holds. - env.PrependUnique(CXXFLAGS='-std=c++23', delete_existing=1) - if env['tool'] == 'intelc': - # options for Intel C++ compiler on hpc dev-intel07 - env.PrependUnique(CCFLAGS=['-w1', '-fp-model', 'precise']) - env.PrependUnique(LIBS=['imf']) - fast_optimflags = ['-fast', '-no-ipo'] - else: - # g++ options - env.PrependUnique(CCFLAGS=['-Wall']) - fast_optimflags = ['-ffast-math'] - - # Configure build variants - if env['build'] == 'debug': - env.Append(CCFLAGS='-g') - elif env['build'] == 'coverage': - env.CacheDir(None) - env.Append(CCFLAGS=['-g', '--coverage', '-O0']) - env.Append(LINKFLAGS='--coverage') - elif env['build'] == 'fast': - env.AppendUnique(CCFLAGS=['-O3'] + fast_optimflags) - env.AppendUnique(CPPDEFINES={'NDEBUG' : None}) - env.AppendUnique(LINKFLAGS=fast_linkflags) - - if env['profile']: - env.AppendUnique(CCFLAGS='-pg') - env.AppendUnique(LINKFLAGS='-pg') - - -# configure boost and ObjCryst libraries unless non-relevant. -skip_configure = (GetOption('clean') or GetOption('help') or - (['sdist'] == list(COMMAND_LINE_TARGETS))) - -# Check dependencies -if not skip_configure: - SConscript('SConscript.configure') - -# Libraries used by libdiffpy. Add them to the shared environment, -# because the unit tests also call GSL, boost_serialization and ObjCryst. -# SCons strips the "lib" prefix where the platform adds it, so -# 'libObjCryst' links libObjCryst.so on Linux and libObjCryst.lib on Windows. -env.AppendUnique(LIBS=['gsl', 'gslcblas', 'boost_serialization']) -if env.get('has_objcryst'): - env.AppendUnique(LIBS=['libObjCryst']) - -# when cleaning make sure to also purge ObjCryst files -if GetOption('clean'): - env['has_objcryst'] = True - -# Define lists for storing library source and include files. -env['lib_includes'] = [] -env['lib_sources'] = [] -env['lib_datafiles'] = [] - -# Subsidiary SConscripts ----------------------------------------------------- - -# The targets that concern unit tests. -targets_that_test = set(('test', 'alltests')) - -# Short circuit if we test an already installed library. Do not define -# any further targets as they may conflict with the installed files. -if env['test_installed']: - SConscript('tests/SConscript') - if not targets_that_test.issuperset(COMMAND_LINE_TARGETS): - print('Warning: only test targets are available when ' - '"test_installed=True".') - Return() - -assert not env['test_installed'] - -# Here we do not test the installed library. Any diffpy headers -# should thus be looked up from our source tree. -env.PrependUnique(CPPPATH=Dir('.')) - -# Load the version script first to resolve the majorminor tuple -SConscript('diffpy/SConscript.version') - -# Path where datafiles should be installed -env['runtimepath'] = os.path.join( - env['datadir'], 'diffpy/libdiffpy-%i.%i' % env['majorminor']) -SConscript('runtime/SConscript') - -# Load all other sconscripts that update lib_includes and lib_sources -SConscript('diffpy/SConscript') - -# Define sdist target for creating source distribution bundle -# Do so only if required to avoid extra git executions. -# Note: See .gitattributes for files excluded from sdist. -if 'sdist' in COMMAND_LINE_TARGETS: - SConscript('SConscript.sdist') - -# Top Level Targets ---------------------------------------------------------- - -# lib -- shared library object - -# use new environment with extra libraries needed for libdiffpy. -env_lib = env.Clone() -if env['PLATFORM'] == 'win32': - # Export the API from the DLL, see diffpy/Export.hpp. Code that uses - # the library, including the unit tests, imports it instead. - env_lib.AppendUnique(CPPDEFINES=['BUILD_DLL']) -# The dladdr call in runtimepath.cpp requires the dl library. -# Windows uses GetModuleHandleEx instead. -if env['PLATFORM'] != 'win32': - env_lib.AppendUnique(LIBS=['dl']) - -libdiffpy = env_lib.SharedLibrary('diffpy', env['lib_sources']) - -# Clean up .gcda and .gcno files from coverage analysis. -env_lib.Clean(libdiffpy, Glob('diffpy/*.gc??')) -env_lib.Clean(libdiffpy, Glob('diffpy/srreal/*.gc??')) -env_lib.Depends(libdiffpy, env['lib_includes']) -Export('libdiffpy') - -lib = Alias('lib', [libdiffpy]) -Default(lib) - -# Define targets related to testing. Do so only when testing is requested. -# This enables library build on machines without cxxtest. -if targets_that_test.intersection(COMMAND_LINE_TARGETS): - SConscript('tests/SConscript') - -# Installation targets. - -prefix = env['prefix'] - -# install-lib -if env['PLATFORM'] in ['darwin', 'posix']: - install_lib = env.Install(env['libdir'], libdiffpy) - if env['PLATFORM'] == 'darwin': - # DARWIN_INSTALL_NAME can be pre-set in sconscript.local - env.SetDefault(DARWIN_INSTALL_NAME='$TARGET.abspath') - env.AddPostAction(install_lib, - 'install_name_tool -id $DARWIN_INSTALL_NAME $TARGET') - if env['PLATFORM'] == 'posix' and WhereIs('ldconfig'): - opts = '' if os.getuid() == 0 else '-n' - env.AddPostAction(install_lib, - 'ldconfig %s $TARGET.dir' % opts) - Alias('install-lib', install_lib) - -if env['PLATFORM'] == 'win32': - bindir = pjoin(prefix, 'Library', 'bin') - dll_node = lib_node = None - for node in libdiffpy: - ext = os.path.splitext(str(node))[1].lower() - if ext == '.dll': - dll_node = node - elif ext == '.lib': - lib_node = node - - if dll_node is None: - print("Warning: DLL file not found in build outputs.") - if lib_node is None: - print("Warning: LIB file not found in build outputs.") - - install_dll = env.Install(bindir, dll_node) - install_lib = env.Install(env['libdir'], lib_node) - Alias('install-lib', [install_dll, install_lib]) - -# install-include -ninc = len(Dir('.').path) + 1 -inc_target_path = lambda f: os.path.join(env['includedir'], f.path[ninc:]) -include_targets = [inc_target_path(f) for f in env['lib_includes']] -install_include = InstallAs(include_targets, env['lib_includes']) -Alias('install-include', install_include) - -# install-data -nrt = len(Dir('runtime').path) + 1 -data_target_path = lambda f: os.path.join(env['runtimepath'], f.path[nrt:]) -data_targets = [data_target_path(f) for f in env['lib_datafiles']] -install_data = InstallAs(data_targets, env['lib_datafiles']) -Alias('install-data', install_data) - -# install -Alias('install', ['install-lib', 'install-include', 'install-data']) -# do not install headers and data when shared library cannot be installed. -if 'install' in COMMAND_LINE_TARGETS: - Requires(install_include, install_lib) - Requires(install_data, install_lib) - -# Coverage related targets. - -# zerocounters -cdatadirs = ['diffpy', 'diffpy/srreal', 'tests'] -cdatafiles = [f for d in cdatadirs for f in Glob(d + '/*.gcda')] -zerocounters = Alias('zerocounters', [], [Delete(f) for f in cdatafiles]) -Ignore(cdatadirs, cdatafiles) -AlwaysBuild(zerocounters) - -# when both 'test' and 'zerocounters' are requested, make sure -# to build them in correct order. -require_ordered_targets = ('test' in COMMAND_LINE_TARGETS and - 'zerocounters' in COMMAND_LINE_TARGETS) -if require_ordered_targets: - Depends('test', zerocounters) - -# vim: ft=python diff --git a/src/SConscript.configure b/src/SConscript.configure deleted file mode 100644 index e834089f..00000000 --- a/src/SConscript.configure +++ /dev/null @@ -1,64 +0,0 @@ -Import('env') - -# Define Custom configure checks --------------------------------------------- - -def CheckBoostVersion(context, version): - '''Check if Boost Library is at least of specified version - ''' - # Boost versions are in format major.minor.subminor - v_arr = [int(n) for n in version.split(".")] - version_n = sum([(v * n) for v, n in zip(v_arr, (1e5, 1e2, 1))]) - context.Message('Checking for Boost version >= %s... ' % (version)) - rv = context.TryCompile('\n'.join([ - '#include ', - '#if BOOST_VERSION < %d', - '#error Installed boost is too old!', - '#endif', - 'int main() { return 0; }', - '', ]) % version_n, '.cpp') - context.Result(rv) - return rv - -def CheckObjCrystVersion(context, version): - '''Check if ObjCryst Library is at least of specified version - ''' - # Boost versions are in format major.minor.subminor - v_arr = [int(n) for n in version.split(".")] - version_n = str(int(sum([(v * n) for v, n in zip(v_arr, (1e9, 1e6, 1e3))])))+"LL" - context.Message('Checking for ObjCryst version >= %s... ' % (version)) - rv = context.TryCompile('\n'.join([ - '#include ', - '#if LIBOBJCRYST_VERSION < %s', - '#error Installed ObjCryst is too old!', - '#endif', - 'int main() { return 0; }', - '', ]) % version_n, '.cpp') - context.Result(rv) - return rv - -# Start configuration -------------------------------------------------------- - -conf = Configure(env, custom_tests={ - 'CheckBoostVersion' : CheckBoostVersion, - 'CheckObjCrystVersion' : CheckObjCrystVersion, - }) - -# serialization of unordered_map requires boost 1.56.0 -boost_required = '1.56.0' -if not conf.CheckBoostVersion(boost_required): - print('This software requires Boost %s or later.' % boost_required) - Exit(1) - -# Check for ObjCryst library -if conf.env.get('enable_objcryst', True): - if not conf.CheckObjCrystVersion('2017.2.1'): - print('ObjCryst library not found. Please install it or disable it.') - Exit(1) - else: - conf.env['has_objcryst'] = True -else: - conf.env['has_objcryst'] = False - -env = conf.Finish() - -# vim: ft=python diff --git a/src/SConscript.sdist b/src/SConscript.sdist deleted file mode 100644 index 05959e64..00000000 --- a/src/SConscript.sdist +++ /dev/null @@ -1,71 +0,0 @@ -import os -from libdiffpybuildutils import gitinfo - -Import('env') - - -def add_version_and_compress(target, source, env): - import tarfile - import gzip - tfin = tarfile.open(target[0].path) - fpout = gzip.GzipFile(target[0].path + '.gz', 'w', mtime=0) - tfout = tarfile.open(fileobj=fpout, mode='w') - # copy archive members up to version.cpp - tiiter = (t for t in tfin if not t.name.endswith('/version.tpl')) - for ti in tiiter: - tfout.addfile(ti, tfin.extractfile(ti)) - if ti.name.endswith('diffpy/version.cpp'): - break - assert ti.name.endswith('version.cpp'), \ - "diffpy/version.cpp not found in tar archive." - # add version.hpp to the archive - vp = source[0].path - ti.name = ti.name[:-4] + '.hpp' - ti.size = os.path.getsize(vp) - tfout.addfile(ti, open(vp, 'rb')) - # finally copy the remaining members in the input tar file. - for ti in tiiter: - tfout.addfile(ti, tfin.extractfile(ti)) - tfout.close() - fpout.close() - tfin.close() - return - - -def die_without_git(target, source, env): - print('sdist must be built from a git repository.') - Exit(1) - return - - -def generate_sdist_actions(target, source, env, for_signature): - env.SetDefault(SDIST_ATTRIBUTES='site_scons/.gitattributes') - ginfo = gitinfo() - if not ginfo: - return [die_without_git] - actions = [] - actions.append('echo "gitarchive.cfg -export-subst" > $SDIST_ATTRIBUTES') - prefix = 'libdiffpy-%(version)s/' % ginfo - gitcmd = ('git -c tar.umask=0022 archive ' - '--worktree-attributes ' - '--prefix=%s --output=$TARGET HEAD') % prefix - actions.append(gitcmd) - actions.append(Delete('$SDIST_ATTRIBUTES')) - actions.append(add_version_and_compress) - actions.append(Delete('$TARGET')) - dst = '${TARGET.base}-%(version)s${TARGET.suffix}.gz' % ginfo - actions.append(Move(dst, '${TARGET}.gz')) - return actions - - -env.Append(BUILDERS={'BuildSourceDistribution' : - Builder(generator=generate_sdist_actions, suffix='.tar')}) - -# build node for version.hpp -vhpp = next(f for f in env['lib_includes'] - if str(f).endswith('diffpy/version.hpp')) - -sdist = Alias('sdist', env.BuildSourceDistribution('#/libdiffpy', vhpp)) -AlwaysBuild(sdist) - -# vim: ft=python diff --git a/src/diffpy/Export.hpp b/src/diffpy/Export.hpp index a998ff22..0a38894f 100644 --- a/src/diffpy/Export.hpp +++ b/src/diffpy/Export.hpp @@ -1,6 +1,6 @@ #pragma once -#if defined(_WIN32) +#if defined(_WIN32) && !defined(DIFFPY_STATIC) #if defined(BUILD_DLL) #define DLL_EXPORT __declspec(dllexport) #else diff --git a/src/diffpy/SConscript b/src/diffpy/SConscript deleted file mode 100644 index 01126c05..00000000 --- a/src/diffpy/SConscript +++ /dev/null @@ -1,23 +0,0 @@ -import os - -Import('env', 'GlobSources') - -env['lib_includes'] += GlobSources('*.[hi]pp') -env['lib_sources'] += [f for f in GlobSources('*.cpp') - if str(f) != 'runtimepath.cpp'] - -rtrp = os.path.relpath(env['runtimepath'], env['libdir']) -if env['PLATFORM'] == "win32": - rtrp = rtrp.replace('\\', '/') - -env_rt = env.Clone() -env_rt.AppendUnique(CPPDEFINES=dict(DIFFPYRUNTIMERELPATH=rtrp)) -if env['PLATFORM'] == "win32": - # runtimepath is part of the DLL, see BUILD_DLL in src/SConscript. - env_rt.AppendUnique(CPPDEFINES=['BUILD_DLL']) -rtso = env_rt.SharedObject('runtimepath', 'runtimepath.cpp') -env['lib_sources'] += rtso - -SConscript('srreal/SConscript') - -# vim: ft=python diff --git a/src/diffpy/SConscript.version b/src/diffpy/SConscript.version deleted file mode 100644 index d915fe20..00000000 --- a/src/diffpy/SConscript.version +++ /dev/null @@ -1,96 +0,0 @@ -import os -import re -import string - -Import('env') - - -def get_version_or_die(): - from libdiffpybuildutils import getversion - try: - rv = getversion() - except RuntimeError as e: - print(e) - Exit(1) - return rv - - -def parsemajorminor(hcode): - 'Extract major and minor version from a C++ header file.' - mx = re.search(r'(?m)^#define *DIFFPY_VERSION_MAJOR *(\d+)', hcode) - major = int(mx.group(1)) - mx = re.search(r'(?m)^#define *DIFFPY_VERSION_MINOR *(\d+)', hcode) - minor = int(mx.group(1)) - return (major, minor) - - -def build_VersionCode(target, source, env): - tplcode = source[0].get_text_contents() - numversion = gver['major'] - numversion = 1000 * numversion + gver['minor'] - numversion = 1000 * numversion + gver['micro'] - numversion = 1000 * numversion + gver['patchnumber'] - # verify that formulas in version.tpl work as advertised - emsg = "Inconsistent value of DIFFPY_VERSION = %i" % numversion - assert numversion // 1000000000 == gver['major'], emsg - assert numversion // 1000000 % 1000 == gver['minor'], emsg - assert numversion // 1000 % 1000 == gver['micro'], emsg - assert numversion % 500 == gver['patchnumber'], emsg - libversion = str(numversion) + "LL" - if gver['prerelease']: - libversion = "(-500 + %s)" % libversion - flds = { - 'DIFFPY_VERSION' : libversion, - 'DIFFPY_VERSION_MAJOR' : gver['major'], - 'DIFFPY_VERSION_MINOR' : gver['minor'], - 'DIFFPY_VERSION_MICRO' : gver['micro'], - 'DIFFPY_VERSION_PATCH' : gver['patchnumber'], - 'DIFFPY_VERSION_STR' : gver['version'], - 'DIFFPY_VERSION_DATE' : gver['date'], - 'DIFFPY_GIT_SHA' : gver['commit'], - } - versiontemplate = string.Template(tplcode) - versioncode = versiontemplate.safe_substitute(flds) - with open(target[0].path, 'w') as fp: - fp.write(versioncode) - return None - -env.Append(BUILDERS={'BuildVersionCode' : - Builder(action=build_VersionCode, suffix='.hpp', src_suffix='.tpl')}) - - -def build_FeaturesCode(target, source, env): - tplcode = source[0].get_text_contents() - flds = { - 'DIFFPY_HAS_OBJCRYST' : int(env['has_objcryst']), - } - codetemplate = string.Template(tplcode) - codetext = codetemplate.safe_substitute(flds) - with open(target[0].path, 'w') as fp: - fp.write(codetext) - return None - -env.Append(BUILDERS={'BuildFeaturesCode' : - Builder(action=build_FeaturesCode, suffix='.hpp', src_suffix='.tpl')}) - -# Targets -------------------------------------------------------------------- - -vhpp = File('version.hpp') - -# If version.hpp exists do not use git -if os.path.isfile(str(vhpp.srcnode())): - majorminor = parsemajorminor(vhpp.srcnode().get_text_contents()) -else: - vtpl = File('version.tpl') - gver = get_version_or_die() - vhpp, = env.BuildVersionCode(['version.hpp'], vtpl) - env.Depends(vhpp, env.Value(gver['version'] + gver['commit'])) - majorminor = (gver['major'], gver['minor']) - -fhpp, = env.BuildFeaturesCode(['features.tpl']) -env.Depends(fhpp, env.Value(env['has_objcryst'])) - -env['lib_includes'] += [vhpp, fhpp] -env['majorminor'] = majorminor - -# vim: ft=python diff --git a/src/diffpy/srreal/SConscript b/src/diffpy/srreal/SConscript deleted file mode 100644 index 610b84e4..00000000 --- a/src/diffpy/srreal/SConscript +++ /dev/null @@ -1,11 +0,0 @@ -Import('env', 'GlobSources') - -def srcsupported(f): - rv = env.get('has_objcryst') or 'objcryst' not in str(f).lower() - rv = rv and f.srcnode().isfile() - return rv - -env['lib_includes'] += [f for f in GlobSources('*.[hi]pp') if srcsupported(f)] -env['lib_sources'] += [f for f in GlobSources('*.cpp') if srcsupported(f)] - -# vim: ft=python diff --git a/src/runtime/SConscript b/src/runtime/SConscript deleted file mode 100644 index a217dc1f..00000000 --- a/src/runtime/SConscript +++ /dev/null @@ -1,6 +0,0 @@ -Import('env', 'GlobSources') - -env['lib_datafiles'] += GlobSources('*.dat') -env['lib_datafiles'] += [File('bvparm2011sel.cif')] - -# vim: ft=python diff --git a/src/tests/CMakeLists.txt b/src/tests/CMakeLists.txt new file mode 100644 index 00000000..158fecb7 --- /dev/null +++ b/src/tests/CMakeLists.txt @@ -0,0 +1,104 @@ +# Unit tests with the Catch2 framework. +# +# The Test*.cpp files share the "alltests" program. CTest runs each tagged +# suite separately, preserving filtering without executing test discovery. + +find_package(Catch2 3.5 CONFIG REQUIRED) +message(STATUS "Using Catch2 ${Catch2_VERSION} from ${Catch2_DIR}") + +# Select the test suites ----------------------------------------------------- + +file(GLOB _suites CONFIGURE_DEPENDS "${CMAKE_CURRENT_SOURCE_DIR}/Test*.cpp") +# Windows globbing also matches lowercase helpers such as test_helpers.cpp. +list(FILTER _suites INCLUDE REGEX "/Test[^/]*\\.cpp$") +set(_objcryst_regex "/[^/]*[Oo][Bb][Jj][Cc][Rr][Yy][Ss][Tt][^/]*$") +if(NOT LIBDIFFPY_HAS_OBJCRYST) + list(FILTER _suites EXCLUDE REGEX "${_objcryst_regex}") +endif() + +if(LIBDIFFPY_TESTS) + string(TOLOWER "${LIBDIFFPY_TESTS}" _patterns) + string(REGEX REPLACE "[, \t]+" ";" _patterns "${_patterns}") + list(REMOVE_ITEM _patterns "") + set(_selected) + foreach(suite IN LISTS _suites) + cmake_path(GET suite FILENAME name) + string(TOLOWER "${name}" name) + foreach(pattern IN LISTS _patterns) + string(FIND "${name}" "${pattern}" pos) + if(pos GREATER_EQUAL 0) + list(APPEND _selected "${suite}") + break() + endif() + endforeach() + endforeach() + set(_suites ${_selected}) +endif() +if(NOT _suites) + message(FATAL_ERROR + "Cannot find any test matching LIBDIFFPY_TESTS='${LIBDIFFPY_TESTS}'.") +endif() + +# alltests -- the unit test program ------------------------------------------ + +set(_helpers test_helpers.cpp) +if(LIBDIFFPY_HAS_OBJCRYST) + list(APPEND _helpers objcryst_helpers.cpp) +endif() + +add_executable(alltests ${_suites} ${_helpers}) +set_target_properties(alltests PROPERTIES CXX_EXTENSIONS OFF) +if(MSVC) + target_compile_options(alltests PRIVATE /bigobj) +endif() +target_include_directories(alltests PRIVATE "${CMAKE_CURRENT_SOURCE_DIR}") +target_link_libraries(alltests PRIVATE libdiffpy::diffpy Catch2::Catch2WithMain) +# test_helpers.cpp finds the testdata directory from this macro. +set_property(SOURCE test_helpers.cpp APPEND PROPERTY COMPILE_DEFINITIONS + "DIFFPYTESTSDIRPATH=${CMAKE_CURRENT_SOURCE_DIR}") + +if(NOT PROJECT_IS_TOP_LEVEL AND NOT LIBDIFFPY_TEST_INSTALLED AND + NOT IS_ABSOLUTE "${LIBDIFFPY_RUNTIME_RELPATH}") + # Stage data for the optional embedded tests without changing the parent's + # output layout. Static/OBJECT code lives in alltests; shared code in diffpy. + if(_libdiffpy_library_type STREQUAL "SHARED_LIBRARY") + set(_test_module diffpy) + else() + set(_test_module alltests) + endif() + set(_test_runtime_dir + "$/${LIBDIFFPY_RUNTIME_RELPATH}") + add_custom_command(TARGET alltests POST_BUILD + COMMAND "${CMAKE_COMMAND}" -E make_directory "${_test_runtime_dir}" + COMMAND "${CMAKE_COMMAND}" -E copy_if_different + ${_runtime_data_files} "${_test_runtime_dir}" + VERBATIM) +endif() + +# Some files contain several suites, e.g. the structure and bond generator +# suites in TestPeriodicStructureAdapter.cpp. Register every tag in each +# selected file so none of these cases are lost. +set(_suite_tags) +foreach(suite IN LISTS _suites) + set_property(DIRECTORY APPEND PROPERTY CMAKE_CONFIGURE_DEPENDS "${suite}") + file(STRINGS "${suite}" _tag_lines REGEX "\"\\[[^]]+\\]\"") + string(REGEX MATCHALL "\"\\[[^]]+\\]\"" _tags "${_tag_lines}") + list(APPEND _suite_tags ${_tags}) +endforeach() +list(REMOVE_DUPLICATES _suite_tags) +foreach(tag IN LISTS _suite_tags) + string(REGEX REPLACE "^\"\\[|\\]\"$" "" name "${tag}") + add_test(NAME "${name}" COMMAND alltests "[${name}]") + if(WIN32 AND LIBDIFFPY_TEST_INSTALLED) + set_tests_properties("${name}" PROPERTIES ENVIRONMENT_MODIFICATION + "PATH=path_list_prepend:$") + endif() +endforeach() + +# check -- build and run the unit tests. +add_custom_target(check + COMMAND "${CMAKE_CTEST_COMMAND}" --output-on-failure -C "$" + DEPENDS alltests + WORKING_DIRECTORY "${PROJECT_BINARY_DIR}" + USES_TERMINAL + VERBATIM) diff --git a/src/tests/SConscript b/src/tests/SConscript deleted file mode 100644 index 0dec35f2..00000000 --- a/src/tests/SConscript +++ /dev/null @@ -1,71 +0,0 @@ -Import('env', 'GlobSources') - -# Environment for building unit test driver -env_test = env.Clone(CXXTEST_SUFFIX='.hpp') -env_test.Tool('cxxtest') - -if env['test_installed']: - # build unit tests for the installed library - env_test.PrependUnique(CPPPATH=env['includedir'], delete_existing=1) - lib_dir = env['libdir'] -else: - # link unit tests with our library. - Import('libdiffpy') - lib_dir = libdiffpy[0].dir.abspath -use_our_library = not env['test_installed'] - -env_test.PrependUnique(LIBS='diffpy', LIBPATH=lib_dir, delete_existing=1) -if env['PLATFORM'] == 'win32': - # Windows has no rpath. Let the test program find diffpy.dll on PATH. - env_test.PrependENVPath('PATH', lib_dir) -else: - env_test.PrependUnique(LINKFLAGS="-Wl,-rpath,%r" % lib_dir) - -# Targets -------------------------------------------------------------------- - -def srcsupported(f): - rv = env.get('has_objcryst') or 'objcryst' not in str(f).lower() - return rv - -def srcincluded(f): - fl = str(f).lower() - rv = srcsupported(f) - rv = rv and f.srcnode().isfile() - if env_test.get('tests'): - tpatterns = Split(env_test['tests'].lower().replace(',', ' ')) - rv = rv and any(tp in fl for tp in tpatterns) - return rv - -# alltests -- the unit test driver source files -# Windows globbing also matches lowercase helpers such as test_helpers.hpp. -# Those are support headers, not suites to turn into generated .cpp files. -test_sources = [f for f in GlobSources('Test*.hpp') - if f.name.startswith('Test') and srcincluded(f)] -if not test_sources: - ts = env_test.get('tests', '') - print("Cannot find any test matching 'tests={}'.".format(ts)) - Exit(1) - -# Define the DIFFPYTESTSDIRPATH macro required for test_helpers.cpp -thisdir = Dir('.').srcnode().abspath.replace('\\', '/') -env_th = env_test.Clone() -env_th.AppendUnique(CPPDEFINES=dict(DIFFPYTESTSDIRPATH=thisdir)) -thobj = env_th.Object('test_helpers.cpp') - -test_helpers = thobj + ['objcryst_helpers.cpp'] -test_helpers = [f for f in test_helpers if srcsupported(f)] - -alltests = env_test.CxxTest('alltests', test_sources + test_helpers) -if use_our_library: - env_test.Depends(alltests, libdiffpy) - -# Clean up .gcda and .gcno files from coverage analysis. -env_test.Clean(alltests, Glob('*.gc??')) - -env_test.Alias('alltests', alltests) - -# test -- alias for executing unit tests. -test = env_test.Alias('test', alltests, alltests[0].abspath) -AlwaysBuild(test) - -# vim: ft=python diff --git a/src/tests/TestAtomRadiiTable.cpp b/src/tests/TestAtomRadiiTable.cpp new file mode 100644 index 00000000..7700a3d8 --- /dev/null +++ b/src/tests/TestAtomRadiiTable.cpp @@ -0,0 +1,119 @@ +/***************************************************************************** + * + * libdiffpy by DANSE Diffraction group + * Simon J. L. Billinge + * (c) 2011 The Trustees of Columbia University + * in the City of New York. All rights reserved. + * + * File coded by: Pavol Juhas + * + * See AUTHORS.rst for a list of people who contributed. + * See LICENSE_DANSE.rst for license information. + * + ****************************************************************************** + * + * TestAtomRadiiTable -- unit tests for the AtomRadiiTable class + * + *****************************************************************************/ + +#include +#include + +#include "serialization_helpers.hpp" +#include +#include + +using namespace std; +using namespace diffpy::srreal; + +class TestAtomRadiiTableFixture { + protected: + AtomRadiiTablePtr mrtb; + + public: + TestAtomRadiiTableFixture() { + mrtb = AtomRadiiTable::createByType("constant"); + } +}; // class TestAtomRadiiTableFixture + +TEST_CASE_METHOD(TestAtomRadiiTableFixture, "TestAtomRadiiTable::test_lookup", + "[AtomRadiiTable]") { + CHECK(0.0 == mrtb->lookup("everything")); +} + +TEST_CASE_METHOD(TestAtomRadiiTableFixture, + "TestAtomRadiiTable::test_setCustom", "[AtomRadiiTable]") { + mrtb->setCustom("C", 1.23); + CHECK(1.23 == mrtb->lookup("C")); + mrtb->setCustom("C", 1.5); + CHECK(1.5 == mrtb->lookup("C")); + mrtb->resetAll(); + CHECK(0.0 == mrtb->lookup("C")); +} + +TEST_CASE_METHOD(TestAtomRadiiTableFixture, + "TestAtomRadiiTable::test_fromString", "[AtomRadiiTable]") { + CHECK(0u == mrtb->getAllCustom().size()); + mrtb->fromString("A:1.1, B:1.2, C: 1.3 \t"); + CHECK(3u == mrtb->getAllCustom().size()); + CHECK(1.1 == mrtb->lookup("A")); + CHECK(1.2 == mrtb->lookup("B")); + CHECK(1.3 == mrtb->lookup("C")); + CHECK_THROWS_AS(mrtb->fromString("X12"), invalid_argument); + CHECK(3u == mrtb->getAllCustom().size()); + mrtb->fromString("D:4.3"); + CHECK(4.3 == mrtb->lookup("D")); + CHECK(4u == mrtb->getAllCustom().size()); +} + +TEST_CASE_METHOD(TestAtomRadiiTableFixture, + "TestAtomRadiiTable::test_resetCustom", "[AtomRadiiTable]") { + mrtb->fromString("A:1.1, B:1.2, C: 1.3"); + CHECK(3u == mrtb->getAllCustom().size()); + mrtb->resetCustom("B"); + CHECK(1.1 == mrtb->lookup("A")); + CHECK(1.3 == mrtb->lookup("C")); +} + +TEST_CASE_METHOD(TestAtomRadiiTableFixture, "TestAtomRadiiTable::test_resetAll", + "[AtomRadiiTable]") { + mrtb->fromString("A:1.1, B:1.2, C: 1.3"); + CHECK(3u == mrtb->getAllCustom().size()); + mrtb->resetAll(); + CHECK(mrtb->getAllCustom().empty()); +} + +TEST_CASE_METHOD(TestAtomRadiiTableFixture, "TestAtomRadiiTable::test_toString", + "[AtomRadiiTable]") { + CHECK(mrtb->toString().empty()); + mrtb->fromString("A:1.1, C: 1.3, B:1.2"); + CHECK(string("A:1.1,B:1.2,C:1.3") == mrtb->toString()); + CHECK(string("A:1.1, B:1.2, C:1.3") == mrtb->toString(", ")); + mrtb->resetAll(); + CHECK(mrtb->toString().empty()); +} + +TEST_CASE_METHOD(TestAtomRadiiTableFixture, + "TestAtomRadiiTable::test_setDefault", "[AtomRadiiTable]") { + ConstantRadiiTable* crtb = dynamic_cast(mrtb.get()); + crtb->setDefault(7.1); + CHECK(7.1 == mrtb->lookup("everything")); + crtb->setDefault(3.1); + CHECK(3.1 == mrtb->lookup("Na")); +} + +TEST_CASE_METHOD(TestAtomRadiiTableFixture, + "TestAtomRadiiTable::test_serialization", "[AtomRadiiTable]") { + AtomRadiiTablePtr rtb1; + mrtb->setCustom("H", 1.23); + ConstantRadiiTable* crtb = dynamic_cast(mrtb.get()); + crtb->setDefault(0.5); + rtb1 = dumpandload(mrtb); + CHECK(1.23 == rtb1->lookup("H")); + CHECK(1u == rtb1->getAllCustom().size()); + CHECK(mrtb.get() != rtb1.get()); + ConstantRadiiTable* crtb1 = dynamic_cast(rtb1.get()); + CHECK(0.5 == crtb1->getDefault()); +} + +// End of file diff --git a/src/tests/TestAtomRadiiTable.hpp b/src/tests/TestAtomRadiiTable.hpp deleted file mode 100644 index d68be7b9..00000000 --- a/src/tests/TestAtomRadiiTable.hpp +++ /dev/null @@ -1,107 +0,0 @@ -/***************************************************************************** - * - * libdiffpy by DANSE Diffraction group - * Simon J. L. Billinge - * (c) 2011 The Trustees of Columbia University - * in the City of New York. All rights reserved. - * - * File coded by: Pavol Juhas - * - * See AUTHORS.rst for a list of people who contributed. - * See LICENSE_DANSE.rst for license information. - * - ****************************************************************************** - * - * class TestAtomRadiiTable -- unit tests for the AtomRadiiTable class - * - *****************************************************************************/ - -#include -#include - -#include "serialization_helpers.hpp" -#include -#include - -using namespace std; -using namespace diffpy::srreal; - -class TestAtomRadiiTable : public CxxTest::TestSuite { - private: - AtomRadiiTablePtr mrtb; - - public: - void setUp() { mrtb = AtomRadiiTable::createByType("constant"); } - - void test_lookup() { TS_ASSERT_EQUALS(0.0, mrtb->lookup("everything")); } - - void test_setCustom() { - mrtb->setCustom("C", 1.23); - TS_ASSERT_EQUALS(1.23, mrtb->lookup("C")); - mrtb->setCustom("C", 1.5); - TS_ASSERT_EQUALS(1.5, mrtb->lookup("C")); - mrtb->resetAll(); - TS_ASSERT_EQUALS(0.0, mrtb->lookup("C")); - } - - void test_fromString() { - TS_ASSERT_EQUALS(0u, mrtb->getAllCustom().size()); - mrtb->fromString("A:1.1, B:1.2, C: 1.3 \t"); - TS_ASSERT_EQUALS(3u, mrtb->getAllCustom().size()); - TS_ASSERT_EQUALS(1.1, mrtb->lookup("A")); - TS_ASSERT_EQUALS(1.2, mrtb->lookup("B")); - TS_ASSERT_EQUALS(1.3, mrtb->lookup("C")); - TS_ASSERT_THROWS(mrtb->fromString("X12"), invalid_argument); - TS_ASSERT_EQUALS(3u, mrtb->getAllCustom().size()); - mrtb->fromString("D:4.3"); - TS_ASSERT_EQUALS(4.3, mrtb->lookup("D")); - TS_ASSERT_EQUALS(4u, mrtb->getAllCustom().size()); - } - - void test_resetCustom() { - mrtb->fromString("A:1.1, B:1.2, C: 1.3"); - TS_ASSERT_EQUALS(3u, mrtb->getAllCustom().size()); - mrtb->resetCustom("B"); - TS_ASSERT_EQUALS(1.1, mrtb->lookup("A")); - TS_ASSERT_EQUALS(1.3, mrtb->lookup("C")); - } - - void test_resetAll() { - mrtb->fromString("A:1.1, B:1.2, C: 1.3"); - TS_ASSERT_EQUALS(3u, mrtb->getAllCustom().size()); - mrtb->resetAll(); - TS_ASSERT(mrtb->getAllCustom().empty()); - } - - void test_toString() { - TS_ASSERT(mrtb->toString().empty()); - mrtb->fromString("A:1.1, C: 1.3, B:1.2"); - TS_ASSERT_EQUALS(string("A:1.1,B:1.2,C:1.3"), mrtb->toString()); - TS_ASSERT_EQUALS(string("A:1.1, B:1.2, C:1.3"), mrtb->toString(", ")); - mrtb->resetAll(); - TS_ASSERT(mrtb->toString().empty()); - } - - void test_setDefault() { - ConstantRadiiTable* crtb = dynamic_cast(mrtb.get()); - crtb->setDefault(7.1); - TS_ASSERT_EQUALS(7.1, mrtb->lookup("everything")); - crtb->setDefault(3.1); - TS_ASSERT_EQUALS(3.1, mrtb->lookup("Na")); - } - - void test_serialization() { - AtomRadiiTablePtr rtb1; - mrtb->setCustom("H", 1.23); - ConstantRadiiTable* crtb = dynamic_cast(mrtb.get()); - crtb->setDefault(0.5); - rtb1 = dumpandload(mrtb); - TS_ASSERT_EQUALS(1.23, rtb1->lookup("H")); - TS_ASSERT_EQUALS(1u, rtb1->getAllCustom().size()); - TS_ASSERT_DIFFERS(mrtb.get(), rtb1.get()); - ConstantRadiiTable* crtb1 = dynamic_cast(rtb1.get()); - TS_ASSERT_EQUALS(0.5, crtb1->getDefault()); - } -}; - -// End of file diff --git a/src/tests/TestAtomicStructureAdapter.cpp b/src/tests/TestAtomicStructureAdapter.cpp new file mode 100644 index 00000000..0fa5ccb8 --- /dev/null +++ b/src/tests/TestAtomicStructureAdapter.cpp @@ -0,0 +1,147 @@ +/***************************************************************************** + * + * libdiffpy Complex Modeling Initiative + * (c) 2013 Brookhaven Science Associates, + * Brookhaven National Laboratory. + * All rights reserved. + * + * File coded by: Pavol Juhas + * + * See AUTHORS.rst for a list of people who contributed. + * See LICENSE.rst for license information. + * + ****************************************************************************** + * + * TestAtomicStructureAdapter -- unit tests for an adapter that + * stores data in a series of Atom objects + * + *****************************************************************************/ + +#include + +#include + +#include +#include +#include "serialization_helpers.hpp" + +namespace diffpy { +namespace srreal { + +using namespace std; + +////////////////////////////////////////////////////////////////////////////// +// TestAtomicStructureAdapter +////////////////////////////////////////////////////////////////////////////// + +class TestAtomicStructureAdapterFixture { + protected: + StructureAdapterPtr mstru; + AtomicStructureAdapterPtr mpstru; + + public: + TestAtomicStructureAdapterFixture() { + mstru = StructureAdapterPtr(new AtomicStructureAdapter); + mpstru = std::dynamic_pointer_cast(mstru); + } +}; // class TestAtomicStructureAdapterFixture + +TEST_CASE_METHOD(TestAtomicStructureAdapterFixture, + "TestAtomicStructureAdapter::test_diff", + "[AtomicStructureAdapter]") { + typedef StructureDifference::Method DM; + const DM::Type& NONE = DM::NONE; + const DM::Type& SIDEBYSIDE = DM::SIDEBYSIDE; + const DM::Type& SORTED = DM::SORTED; + StructureDifference sd; + sd = mstru->diff(emptyStructureAdapter()); + CHECK(sd.add1.empty()); + CHECK(!sd.allowsfastupdate()); + sd = mstru->diff(StructureAdapterPtr()); + CHECK(NONE == sd.diffmethod); + CHECK(!sd.allowsfastupdate()); + Atom ai; + ai.atomtype = "C"; + const int SZ = 10; + for (int i = 0; i < SZ; ++i) { + ai.xyz_cartn[0] = i; + mpstru->append(ai); + } + AtomicStructureAdapterPtr cpstru = + std::make_shared(*mpstru); + sd = mstru->diff(mstru); + CHECK(sd.allowsfastupdate()); + sd = mstru->diff(cpstru); + CHECK(SIDEBYSIDE == sd.diffmethod); + CHECK(sd.allowsfastupdate()); + CHECK(sd.pop0.empty()); + CHECK(sd.add1.empty()); + (*cpstru)[0].atomtype = "N"; + sd = mstru->diff(cpstru); + CHECK(SIDEBYSIDE == sd.diffmethod); + CHECK(sd.allowsfastupdate()); + CHECK(1u == sd.pop0.size()); + CHECK(1u == sd.add1.size()); + for (int i = 1; i < (1 - sqrt(0.5)) * SZ; ++i) { + cpstru->erase(0); + sd = mstru->diff(cpstru); + CHECK(SORTED == sd.diffmethod); + CHECK(sd.allowsfastupdate()); + CHECK(i == int(sd.pop0.size())); + CHECK(sd.add1.empty()); + } + cpstru->erase(0); + sd = mstru->diff(cpstru); + CHECK(!sd.allowsfastupdate()); + Atom a2; + a2.atomtype = "N"; + cpstru->append(a2); + sd = mstru->diff(cpstru); + CHECK(!sd.allowsfastupdate()); + CHECK(1u == sd.add1.size()); +} + +TEST_CASE_METHOD(TestAtomicStructureAdapterFixture, + "TestAtomicStructureAdapter::test_serialization", + "[AtomicStructureAdapter]") { + Atom ai; + ai.atomtype = "C"; + ai.xyz_cartn = R3::Vector(1, 2, 3); + ai.anisotropy = true; + mpstru->append(ai); + ai.atomtype = "H"; + ai.xyz_cartn = R3::Vector(4, 5, 6); + ai.anisotropy = false; + mpstru->append(ai); + StructureAdapterPtr stru1; + stru1 = dumpandload(mstru); + AtomicStructureAdapterPtr astru1 = + std::dynamic_pointer_cast(stru1); + CHECK(2 == astru1->countSites()); + CHECK((*mpstru)[0] == (*astru1)[0]); + CHECK((*mpstru)[1] == (*astru1)[1]); +} + +TEST_CASE_METHOD(TestAtomicStructureAdapterFixture, + "TestAtomicStructureAdapter::test_comparison", + "[AtomicStructureAdapter]") { + Atom ai; + ai.atomtype = "C"; + const int SZ = 10; + for (int i = 0; i < SZ; ++i) { + ai.xyz_cartn[0] = i; + mpstru->append(ai); + } + AtomicStructureAdapterPtr cpstru = + std::make_shared(*mpstru); + CHECK(*mpstru == *cpstru); + CHECK(!(*mpstru != *cpstru)); + cpstru->at(0).atomtype = "H"; + CHECK(*mpstru != *cpstru); + CHECK(!(*mpstru == *cpstru)); +} + +} // namespace srreal +} // namespace diffpy + +// End of file diff --git a/src/tests/TestAtomicStructureAdapter.hpp b/src/tests/TestAtomicStructureAdapter.hpp deleted file mode 100644 index 63c9db6b..00000000 --- a/src/tests/TestAtomicStructureAdapter.hpp +++ /dev/null @@ -1,144 +0,0 @@ -/***************************************************************************** - * - * libdiffpy Complex Modeling Initiative - * (c) 2013 Brookhaven Science Associates, - * Brookhaven National Laboratory. - * All rights reserved. - * - * File coded by: Pavol Juhas - * - * See AUTHORS.rst for a list of people who contributed. - * See LICENSE.rst for license information. - * - ****************************************************************************** - * - * class TestAtomicStructureAdapter -- unit tests for an adapter that - * stores data in a series of Atom objects - * - *****************************************************************************/ - -#include - -#include - -#include -#include -#include "serialization_helpers.hpp" - -namespace diffpy { -namespace srreal { - -using namespace std; - -////////////////////////////////////////////////////////////////////////////// -// class TestAtomicStructureAdapter -////////////////////////////////////////////////////////////////////////////// - -class TestAtomicStructureAdapter : public CxxTest::TestSuite { - private: - StructureAdapterPtr mstru; - AtomicStructureAdapterPtr mpstru; - - public: - void setUp() { - mstru = StructureAdapterPtr(new AtomicStructureAdapter); - mpstru = std::dynamic_pointer_cast(mstru); - } - - void test_diff() { - typedef StructureDifference::Method DM; - const DM::Type& NONE = DM::NONE; - const DM::Type& SIDEBYSIDE = DM::SIDEBYSIDE; - const DM::Type& SORTED = DM::SORTED; - StructureDifference sd; - sd = mstru->diff(emptyStructureAdapter()); - TS_ASSERT(sd.add1.empty()); - TS_ASSERT(!sd.allowsfastupdate()); - sd = mstru->diff(StructureAdapterPtr()); - TS_ASSERT_EQUALS(NONE, sd.diffmethod); - TS_ASSERT(!sd.allowsfastupdate()); - Atom ai; - ai.atomtype = "C"; - const int SZ = 10; - for (int i = 0; i < SZ; ++i) { - ai.xyz_cartn[0] = i; - mpstru->append(ai); - } - AtomicStructureAdapterPtr cpstru = - std::make_shared(*mpstru); - sd = mstru->diff(mstru); - TS_ASSERT(sd.allowsfastupdate()) - sd = mstru->diff(cpstru); - TS_ASSERT_EQUALS(SIDEBYSIDE, sd.diffmethod); - TS_ASSERT(sd.allowsfastupdate()) - TS_ASSERT(sd.pop0.empty()); - TS_ASSERT(sd.add1.empty()); - (*cpstru)[0].atomtype = "N"; - sd = mstru->diff(cpstru); - TS_ASSERT_EQUALS(SIDEBYSIDE, sd.diffmethod); - TS_ASSERT(sd.allowsfastupdate()) - TS_ASSERT_EQUALS(1u, sd.pop0.size()); - TS_ASSERT_EQUALS(1u, sd.add1.size()); - for (int i = 1; i < (1 - sqrt(0.5)) * SZ; ++i) { - cpstru->erase(0); - sd = mstru->diff(cpstru); - TS_ASSERT_EQUALS(SORTED, sd.diffmethod); - TS_ASSERT(sd.allowsfastupdate()); - TS_ASSERT_EQUALS(i, int(sd.pop0.size())); - TS_ASSERT(sd.add1.empty()); - } - cpstru->erase(0); - sd = mstru->diff(cpstru); - TS_ASSERT(!sd.allowsfastupdate()); - Atom a2; - a2.atomtype = "N"; - cpstru->append(a2); - sd = mstru->diff(cpstru); - TS_ASSERT(!sd.allowsfastupdate()); - TS_ASSERT_EQUALS(1u, sd.add1.size()); - } - - void test_serialization() { - Atom ai; - ai.atomtype = "C"; - ai.xyz_cartn = R3::Vector(1, 2, 3); - ai.anisotropy = true; - mpstru->append(ai); - ai.atomtype = "H"; - ai.xyz_cartn = R3::Vector(4, 5, 6); - ai.anisotropy = false; - mpstru->append(ai); - StructureAdapterPtr stru1; - stru1 = dumpandload(mstru); - AtomicStructureAdapterPtr astru1 = - std::dynamic_pointer_cast(stru1); - TS_ASSERT_EQUALS(2, astru1->countSites()); - TS_ASSERT_EQUALS((*mpstru)[0], (*astru1)[0]); - TS_ASSERT_EQUALS((*mpstru)[1], (*astru1)[1]); - } - - void test_comparison() { - Atom ai; - ai.atomtype = "C"; - const int SZ = 10; - for (int i = 0; i < SZ; ++i) { - ai.xyz_cartn[0] = i; - mpstru->append(ai); - } - AtomicStructureAdapterPtr cpstru = - std::make_shared(*mpstru); - TS_ASSERT_EQUALS(*mpstru, *cpstru); - TS_ASSERT(!(*mpstru != *cpstru)); - cpstru->at(0).atomtype = "H"; - TS_ASSERT_DIFFERS(*mpstru, *cpstru); - TS_ASSERT(!(*mpstru == *cpstru)); - } - -}; // class TestAtomicStructureAdapter - -} // namespace srreal -} // namespace diffpy - -using diffpy::srreal::TestAtomicStructureAdapter; - -// End of file diff --git a/src/tests/TestAttributes.hpp b/src/tests/TestAttributes.cpp similarity index 55% rename from src/tests/TestAttributes.hpp rename to src/tests/TestAttributes.cpp index 64192d4e..9e264513 100644 --- a/src/tests/TestAttributes.hpp +++ b/src/tests/TestAttributes.cpp @@ -12,11 +12,11 @@ * ****************************************************************************** * - * class TestAttributes -- test the helper Attributes class + * TestAttributes -- test the helper Attributes class * *****************************************************************************/ -#include +#include #include @@ -42,31 +42,32 @@ class Example : public diffpy::Attributes { // --------------------------------------------------------------------------- -class TestAttributes : public CxxTest::TestSuite { - private: +class TestAttributesFixture { + protected: // data std::unique_ptr mobj; public: - void setUp() { mobj.reset(new Example); } - - void test_names() { - TS_ASSERT_EQUALS(2, mobj->namesOfDoubleAttributes().size()); - TS_ASSERT_EQUALS(1, mobj->namesOfWritableDoubleAttributes().size()); - } + TestAttributesFixture() { mobj.reset(new Example); } +}; // class TestAttributesFixture - void test_getsetdoubleattr() { - using diffpy::attributes::DoubleAttributeError; - TS_ASSERT_EQUALS(0.0, mobj->getDoubleAttr("a")); - mobj->setDoubleAttr("a", 1.3); - TS_ASSERT_EQUALS(1.3, mobj->getDoubleAttr("a")); - TS_ASSERT_THROWS(mobj->setDoubleAttr("b", 3), DoubleAttributeError); - TS_ASSERT_EQUALS(1.3, mobj->getDoubleAttr("b")); - const Example ex1; - TS_ASSERT_EQUALS(0.0, ex1.getDoubleAttr("a")); - TS_ASSERT_THROWS(ex1.getDoubleAttr("bad"), DoubleAttributeError); - } +TEST_CASE_METHOD(TestAttributesFixture, "TestAttributes::test_names", + "[Attributes]") { + CHECK(2 == mobj->namesOfDoubleAttributes().size()); + CHECK(1 == mobj->namesOfWritableDoubleAttributes().size()); +} -}; // class TestAttributes +TEST_CASE_METHOD(TestAttributesFixture, "TestAttributes::test_getsetdoubleattr", + "[Attributes]") { + using diffpy::attributes::DoubleAttributeError; + CHECK(0.0 == mobj->getDoubleAttr("a")); + mobj->setDoubleAttr("a", 1.3); + CHECK(1.3 == mobj->getDoubleAttr("a")); + CHECK_THROWS_AS(mobj->setDoubleAttr("b", 3), DoubleAttributeError); + CHECK(1.3 == mobj->getDoubleAttr("b")); + const Example ex1; + CHECK(0.0 == ex1.getDoubleAttr("a")); + CHECK_THROWS_AS(ex1.getDoubleAttr("bad"), DoubleAttributeError); +} // End of file diff --git a/src/tests/TestBVParametersTable.cpp b/src/tests/TestBVParametersTable.cpp new file mode 100644 index 00000000..bd6582a6 --- /dev/null +++ b/src/tests/TestBVParametersTable.cpp @@ -0,0 +1,210 @@ +/***************************************************************************** + * + * libdiffpy by DANSE Diffraction group + * Simon J. L. Billinge + * (c) 2009 The Trustees of Columbia University + * in the City of New York. All rights reserved. + * + * File coded by: Pavol Juhas + * + * See AUTHORS.rst for a list of people who contributed. + * See LICENSE_DANSE.rst for license information. + * + ****************************************************************************** + * + * TestBVParametersTable -- unit tests for BVParametersTable class + * + *****************************************************************************/ + +#include + +#include +#include "serialization_helpers.hpp" + +using namespace std; +using namespace diffpy::srreal; + +class TestBVParametersTableFixture { + protected: + BVParametersTablePtr mbvtb; + + public: + TestBVParametersTableFixture() { mbvtb.reset(new BVParametersTable); } +}; // class TestBVParametersTableFixture + +TEST_CASE_METHOD(TestBVParametersTableFixture, + "TestBVParametersTable::test_copy", "[BVParametersTable]") { + BVParam mynacl("Na", 1, "Cl", -1); + mynacl.mRo = 1.23; + mynacl.mB = 0.377; + mbvtb->setCustom(mynacl); + CHECK(1.23 == mbvtb->lookup("Na", 1, "Cl", -1).mRo); + CHECK(0.377 == mbvtb->lookup("Na", 1, "Cl", -1).mB); + BVParametersTable bvcp(*mbvtb); + CHECK(1.23 == bvcp.lookup("Na", 1, "Cl", -1).mRo); + CHECK(0.377 == bvcp.lookup("Na", 1, "Cl", -1).mB); + bvcp.resetAll(); + CHECK(2.15 == bvcp.lookup("Na", 1, "Cl", -1).mRo); + CHECK(0.37 == bvcp.lookup("Na", 1, "Cl", -1).mB); + CHECK(1.23 == mbvtb->lookup("Na", 1, "Cl", -1).mRo); + CHECK(0.377 == mbvtb->lookup("Na", 1, "Cl", -1).mB); +} + +TEST_CASE_METHOD(TestBVParametersTableFixture, + "TestBVParametersTable::test_none", "[BVParametersTable]") { + const BVParam& bpnone = BVParametersTable::none(); + CHECK(&bpnone == &(mbvtb->none())); + CHECK(0.0 == bpnone.bondvalence(0)); + CHECK(0.0 == bpnone.bondvalence(3)); + CHECK(0.0 == bpnone.bondvalence(10)); + CHECK(0.0 == bpnone.bondvalenceToDistance(0.5)); +} + +TEST_CASE_METHOD(TestBVParametersTableFixture, + "TestBVParametersTable::test_atomvalence", + "[BVParametersTable]") { + CHECK(0 == mbvtb->getAtomValence("O")); + mbvtb->setAtomValence("O", -2); + CHECK(-2 == mbvtb->getAtomValence("O")); + mbvtb->resetAtomValences(); + CHECK(0 == mbvtb->getAtomValence("O")); +} + +TEST_CASE_METHOD(TestBVParametersTableFixture, + "TestBVParametersTable::test_lookup", "[BVParametersTable]") { + BVParam bp = mbvtb->lookup("Xx", 0, "Yy", 3); + CHECK(bp.matom0.empty()); + CHECK(bp.matom1.empty()); + CHECK(0 == bp.mvalence0); + CHECK(0 == bp.mvalence1); + CHECK(0.0 == bp.mRo); + CHECK(0.0 == bp.mB); + CHECK(&mbvtb->lookup("Xx", 0, "Yy", 3) == + &mbvtb->lookup("Na", 33, "Cl", -11)); + BVParam bk("Na", 1, "Cl", -1); + CHECK(&mbvtb->lookup(bk) == &mbvtb->lookup("Cl", -1, "Na", 1)); + CHECK(2.15 == mbvtb->lookup(bk).mRo); + CHECK(0.37 == mbvtb->lookup(bk).mB); + CHECK("b" == mbvtb->lookup(bk).mref_id); + const BVParam& bnacl0 = mbvtb->lookup("Na", 1, "Cl", -1); + CHECK(&bnacl0 == &mbvtb->lookup("Na+", "Cl-")); + CHECK(&bnacl0 == &mbvtb->lookup("Cl1-", "Na1+")); + const BVParam& bpnone = BVParametersTable::none(); + CHECK(&bpnone == &mbvtb->lookup("Na", "Cl")); + mbvtb->setAtomValence("Na", +1); + mbvtb->setAtomValence("Cl", -1); + CHECK(&bnacl0 == &mbvtb->lookup("Na", "Cl")); +} + +TEST_CASE_METHOD(TestBVParametersTableFixture, + "TestBVParametersTable::test_setCustom", + "[BVParametersTable]") { + BVParam mymgo("Mg", 2, "O", -2); + mymgo.mRo = 2.34; + mymgo.mB = 0.345; + mbvtb->setCustom(mymgo); + CHECK(2.34 == mbvtb->lookup("Mg", 2, "O", -2).mRo); + CHECK(0.345 == mbvtb->lookup(mymgo).mB); + mbvtb->resetCustom(mymgo); + CHECK(2.34 != mbvtb->lookup("Mg", 2, "O", -2).mRo); + CHECK(0.345 != mbvtb->lookup(mymgo).mB); + BVParam zrh = mbvtb->lookup("Zr", 4, "H", -1); + CHECK(1.79 == zrh.mRo); + CHECK(0.37 == zrh.mB); + CHECK(string("b") == zrh.mref_id); + mbvtb->setCustom("H", -1, "Zr", 4, 1.791, 0.371, "check"); + zrh = mbvtb->lookup("Zr", 4, "H", -1); + CHECK(1.791 == zrh.mRo); + CHECK(0.371 == zrh.mB); + CHECK(string("check") == zrh.mref_id); + mbvtb->setCustom("H", -1, "Zr", 4, 1.791, 0.444, "mate"); + zrh = mbvtb->lookup("Zr", 4, "H", -1); + CHECK(0.444 == zrh.mB); + CHECK(string("mate") == zrh.mref_id); +} + +TEST_CASE_METHOD(TestBVParametersTableFixture, + "TestBVParametersTable::test_resetAll", + "[BVParametersTable]") { + BVParam mynacl("Cl", -1, "Na", 1, 2.345, 0.44, "pj1"); + BVParam mymgo("O", -2, "Mg", 2, 3.456, 0.55, "pj2"); + mbvtb->setCustom(mynacl); + mbvtb->setCustom(mymgo); + CHECK(string("pj1") == mbvtb->lookup(mynacl).mref_id); + CHECK(string("pj2") == mbvtb->lookup(mymgo).mref_id); + mbvtb->resetAll(); + CHECK(string("pj1") != mbvtb->lookup(mynacl).mref_id); + CHECK(string("pj2") != mbvtb->lookup(mymgo).mref_id); +} + +TEST_CASE_METHOD(TestBVParametersTableFixture, + "TestBVParametersTable::test_getAllCustom", + "[BVParametersTable]") { + CHECK(mbvtb->getAllCustom().empty()); + BVParam mymgo("Mg", 2, "O", -2); + mbvtb->setCustom(mymgo); + CHECK(1u == mbvtb->getAllCustom().size()); + const BVParam& bp = mbvtb->lookup("O2-", "Mg2+"); + BVParametersTable::SetOfBVParam::const_iterator ii; + ii = mbvtb->getAllCustom().find(mymgo); + CHECK(ii != mbvtb->getAllCustom().end()); + CHECK(&mymgo != &(*ii)); + CHECK(&bp == &(*ii)); + CHECK(mymgo == *ii); + // erase the only custom parameter + mbvtb->resetCustom(*ii); + CHECK(mbvtb->getAllCustom().empty()); +} + +TEST_CASE_METHOD(TestBVParametersTableFixture, + "TestBVParametersTable::test_getAll", "[BVParametersTable]") { + BVParametersTable::SetOfBVParam allpars0, allpars1; + allpars0 = mbvtb->getAll(); + CHECK(!allpars0.empty()); + size_t cnt0 = allpars0.size(); + BVParam mymgo("Mg", 2, "O", -2); + mbvtb->setCustom(mymgo); + CHECK(cnt0 == mbvtb->getAll().size()); + mbvtb->setCustom(BVParam("Mg", 7, "O", -3)); + allpars1 = mbvtb->getAll(); + size_t cnt1 = allpars1.size(); + CHECK(cnt0 + 1 == cnt1); + mbvtb->resetAll(); + CHECK(cnt0 == mbvtb->getAll().size()); + mbvtb->setCustom(BVParam("Mg", 2, "O", -2, 77, 88)); + allpars1 = mbvtb->getAll(); + const BVParam& mgo = *allpars1.find(BVParam("Mg", 2, "O", -2)); + CHECK(77.0 == mgo.mRo); + CHECK(88.0 == mgo.mB); +} + +TEST_CASE_METHOD(TestBVParametersTableFixture, + "TestBVParametersTable::test_serialization", + "[BVParametersTable]") { + BVParam mynacl("Cl", -1, "Na", 1, 2.345, 0.44, "pj1"); + BVParam mymgo("O", -2, "Mg", 2, 3.456, 0.55, "pj2"); + mbvtb->setCustom(mynacl); + mbvtb->setCustom(mymgo); + // check serialization of shared pointers + BVParametersTablePtr bvtb1 = dumpandload(mbvtb); + CHECK(mbvtb.get() != bvtb1.get()); + CHECK(2.345 == bvtb1->lookup("Cl", -1, "Na", 1).mRo); + CHECK(0.44 == bvtb1->lookup("Cl", -1, "Na", 1).mB); + CHECK(string("pj1") == bvtb1->lookup("Cl", -1, "Na", 1).mref_id); + CHECK(3.456 == bvtb1->lookup("O", -2, "Mg", 2).mRo); + CHECK(0.55 == bvtb1->lookup("O", -2, "Mg", 2).mB); + CHECK(string("pj2") == bvtb1->lookup("O", -2, "Mg", 2).mref_id); + // check serialization of instances + BVParametersTable tb2 = dumpandload(*mbvtb); + CHECK(2u == tb2.getAllCustom().size()); + CHECK(mynacl == tb2.lookup("Cl-", "Na+")); + // check serialization of customized valences + tb2.setAtomValence("Na", 1); + tb2.setAtomValence("Cl", -1); + BVParametersTable tb3 = dumpandload(tb2); + CHECK(mynacl == tb3.lookup("Cl", "Na")); + const BVParam& bpnone = BVParametersTable::none(); + CHECK(bpnone == mbvtb->lookup("Cl", "Na")); +} + +// End of file diff --git a/src/tests/TestBVParametersTable.hpp b/src/tests/TestBVParametersTable.hpp deleted file mode 100644 index 603f1439..00000000 --- a/src/tests/TestBVParametersTable.hpp +++ /dev/null @@ -1,197 +0,0 @@ -/***************************************************************************** - * - * libdiffpy by DANSE Diffraction group - * Simon J. L. Billinge - * (c) 2009 The Trustees of Columbia University - * in the City of New York. All rights reserved. - * - * File coded by: Pavol Juhas - * - * See AUTHORS.rst for a list of people who contributed. - * See LICENSE_DANSE.rst for license information. - * - ****************************************************************************** - * - * class TestBVParametersTable -- unit tests for BVParametersTable class - * - *****************************************************************************/ - -#include - -#include -#include "serialization_helpers.hpp" - -using namespace std; -using namespace diffpy::srreal; - -class TestBVParametersTable : public CxxTest::TestSuite { - private: - BVParametersTablePtr mbvtb; - - public: - void setUp() { mbvtb.reset(new BVParametersTable); } - - void test_copy() { - BVParam mynacl("Na", 1, "Cl", -1); - mynacl.mRo = 1.23; - mynacl.mB = 0.377; - mbvtb->setCustom(mynacl); - TS_ASSERT_EQUALS(1.23, mbvtb->lookup("Na", 1, "Cl", -1).mRo); - TS_ASSERT_EQUALS(0.377, mbvtb->lookup("Na", 1, "Cl", -1).mB); - BVParametersTable bvcp(*mbvtb); - TS_ASSERT_EQUALS(1.23, bvcp.lookup("Na", 1, "Cl", -1).mRo); - TS_ASSERT_EQUALS(0.377, bvcp.lookup("Na", 1, "Cl", -1).mB); - bvcp.resetAll(); - TS_ASSERT_EQUALS(2.15, bvcp.lookup("Na", 1, "Cl", -1).mRo); - TS_ASSERT_EQUALS(0.37, bvcp.lookup("Na", 1, "Cl", -1).mB); - TS_ASSERT_EQUALS(1.23, mbvtb->lookup("Na", 1, "Cl", -1).mRo); - TS_ASSERT_EQUALS(0.377, mbvtb->lookup("Na", 1, "Cl", -1).mB); - } - - void test_none() { - const BVParam& bpnone = BVParametersTable::none(); - TS_ASSERT_EQUALS(&bpnone, &(mbvtb->none())); - TS_ASSERT_EQUALS(0.0, bpnone.bondvalence(0)); - TS_ASSERT_EQUALS(0.0, bpnone.bondvalence(3)); - TS_ASSERT_EQUALS(0.0, bpnone.bondvalence(10)); - TS_ASSERT_EQUALS(0.0, bpnone.bondvalenceToDistance(0.5)); - } - - void test_atomvalence() { - TS_ASSERT_EQUALS(0, mbvtb->getAtomValence("O")); - mbvtb->setAtomValence("O", -2); - TS_ASSERT_EQUALS(-2, mbvtb->getAtomValence("O")); - mbvtb->resetAtomValences(); - TS_ASSERT_EQUALS(0, mbvtb->getAtomValence("O")); - } - - void test_lookup() { - BVParam bp = mbvtb->lookup("Xx", 0, "Yy", 3); - TS_ASSERT(bp.matom0.empty()); - TS_ASSERT(bp.matom1.empty()); - TS_ASSERT_EQUALS(0, bp.mvalence0); - TS_ASSERT_EQUALS(0, bp.mvalence1); - TS_ASSERT_EQUALS(0.0, bp.mRo); - TS_ASSERT_EQUALS(0.0, bp.mB); - TS_ASSERT_EQUALS(&mbvtb->lookup("Xx", 0, "Yy", 3), - &mbvtb->lookup("Na", 33, "Cl", -11)); - BVParam bk("Na", 1, "Cl", -1); - TS_ASSERT_EQUALS(&mbvtb->lookup(bk), &mbvtb->lookup("Cl", -1, "Na", 1)); - TS_ASSERT_EQUALS(2.15, mbvtb->lookup(bk).mRo); - TS_ASSERT_EQUALS(0.37, mbvtb->lookup(bk).mB); - TS_ASSERT_EQUALS("b", mbvtb->lookup(bk).mref_id); - const BVParam& bnacl0 = mbvtb->lookup("Na", 1, "Cl", -1); - TS_ASSERT_EQUALS(&bnacl0, &mbvtb->lookup("Na+", "Cl-")); - TS_ASSERT_EQUALS(&bnacl0, &mbvtb->lookup("Cl1-", "Na1+")); - const BVParam& bpnone = BVParametersTable::none(); - TS_ASSERT_EQUALS(&bpnone, &mbvtb->lookup("Na", "Cl")); - mbvtb->setAtomValence("Na", +1); - mbvtb->setAtomValence("Cl", -1); - TS_ASSERT_EQUALS(&bnacl0, &mbvtb->lookup("Na", "Cl")); - } - - void test_setCustom() { - BVParam mymgo("Mg", 2, "O", -2); - mymgo.mRo = 2.34; - mymgo.mB = 0.345; - mbvtb->setCustom(mymgo); - TS_ASSERT_EQUALS(2.34, mbvtb->lookup("Mg", 2, "O", -2).mRo); - TS_ASSERT_EQUALS(0.345, mbvtb->lookup(mymgo).mB); - mbvtb->resetCustom(mymgo); - TS_ASSERT_DIFFERS(2.34, mbvtb->lookup("Mg", 2, "O", -2).mRo); - TS_ASSERT_DIFFERS(0.345, mbvtb->lookup(mymgo).mB); - BVParam zrh = mbvtb->lookup("Zr", 4, "H", -1); - TS_ASSERT_EQUALS(1.79, zrh.mRo); - TS_ASSERT_EQUALS(0.37, zrh.mB); - TS_ASSERT_EQUALS(string("b"), zrh.mref_id); - mbvtb->setCustom("H", -1, "Zr", 4, 1.791, 0.371, "check"); - zrh = mbvtb->lookup("Zr", 4, "H", -1); - TS_ASSERT_EQUALS(1.791, zrh.mRo); - TS_ASSERT_EQUALS(0.371, zrh.mB); - TS_ASSERT_EQUALS(string("check"), zrh.mref_id); - mbvtb->setCustom("H", -1, "Zr", 4, 1.791, 0.444, "mate"); - zrh = mbvtb->lookup("Zr", 4, "H", -1); - TS_ASSERT_EQUALS(0.444, zrh.mB); - TS_ASSERT_EQUALS(string("mate"), zrh.mref_id); - } - - void test_resetAll() { - BVParam mynacl("Cl", -1, "Na", 1, 2.345, 0.44, "pj1"); - BVParam mymgo("O", -2, "Mg", 2, 3.456, 0.55, "pj2"); - mbvtb->setCustom(mynacl); - mbvtb->setCustom(mymgo); - TS_ASSERT_EQUALS(string("pj1"), mbvtb->lookup(mynacl).mref_id); - TS_ASSERT_EQUALS(string("pj2"), mbvtb->lookup(mymgo).mref_id); - mbvtb->resetAll(); - TS_ASSERT_DIFFERS(string("pj1"), mbvtb->lookup(mynacl).mref_id); - TS_ASSERT_DIFFERS(string("pj2"), mbvtb->lookup(mymgo).mref_id); - } - - void test_getAllCustom() { - TS_ASSERT(mbvtb->getAllCustom().empty()); - BVParam mymgo("Mg", 2, "O", -2); - mbvtb->setCustom(mymgo); - TS_ASSERT_EQUALS(1u, mbvtb->getAllCustom().size()); - const BVParam& bp = mbvtb->lookup("O2-", "Mg2+"); - BVParametersTable::SetOfBVParam::const_iterator ii; - ii = mbvtb->getAllCustom().find(mymgo); - TS_ASSERT_DIFFERS(ii, mbvtb->getAllCustom().end()); - TS_ASSERT_DIFFERS(&mymgo, &(*ii)); - TS_ASSERT_EQUALS(&bp, &(*ii)); - TS_ASSERT_EQUALS(mymgo, *ii); - // erase the only custom parameter - mbvtb->resetCustom(*ii); - TS_ASSERT(mbvtb->getAllCustom().empty()); - } - - void test_getAll() { - BVParametersTable::SetOfBVParam allpars0, allpars1; - allpars0 = mbvtb->getAll(); - TS_ASSERT(!allpars0.empty()); - size_t cnt0 = allpars0.size(); - BVParam mymgo("Mg", 2, "O", -2); - mbvtb->setCustom(mymgo); - TS_ASSERT_EQUALS(cnt0, mbvtb->getAll().size()); - mbvtb->setCustom(BVParam("Mg", 7, "O", -3)); - allpars1 = mbvtb->getAll(); - size_t cnt1 = allpars1.size(); - TS_ASSERT_EQUALS(cnt0 + 1, cnt1); - mbvtb->resetAll(); - TS_ASSERT_EQUALS(cnt0, mbvtb->getAll().size()); - mbvtb->setCustom(BVParam("Mg", 2, "O", -2, 77, 88)); - allpars1 = mbvtb->getAll(); - const BVParam& mgo = *allpars1.find(BVParam("Mg", 2, "O", -2)); - TS_ASSERT_EQUALS(77.0, mgo.mRo); - TS_ASSERT_EQUALS(88.0, mgo.mB); - } - - void test_serialization() { - BVParam mynacl("Cl", -1, "Na", 1, 2.345, 0.44, "pj1"); - BVParam mymgo("O", -2, "Mg", 2, 3.456, 0.55, "pj2"); - mbvtb->setCustom(mynacl); - mbvtb->setCustom(mymgo); - // check serialization of shared pointers - BVParametersTablePtr bvtb1 = dumpandload(mbvtb); - TS_ASSERT_DIFFERS(mbvtb.get(), bvtb1.get()); - TS_ASSERT_EQUALS(2.345, bvtb1->lookup("Cl", -1, "Na", 1).mRo); - TS_ASSERT_EQUALS(0.44, bvtb1->lookup("Cl", -1, "Na", 1).mB); - TS_ASSERT_EQUALS(string("pj1"), bvtb1->lookup("Cl", -1, "Na", 1).mref_id); - TS_ASSERT_EQUALS(3.456, bvtb1->lookup("O", -2, "Mg", 2).mRo); - TS_ASSERT_EQUALS(0.55, bvtb1->lookup("O", -2, "Mg", 2).mB); - TS_ASSERT_EQUALS(string("pj2"), bvtb1->lookup("O", -2, "Mg", 2).mref_id); - // check serialization of instances - BVParametersTable tb2 = dumpandload(*mbvtb); - TS_ASSERT_EQUALS(2u, tb2.getAllCustom().size()); - TS_ASSERT_EQUALS(mynacl, tb2.lookup("Cl-", "Na+")); - // check serialization of customized valences - tb2.setAtomValence("Na", 1); - tb2.setAtomValence("Cl", -1); - BVParametersTable tb3 = dumpandload(tb2); - TS_ASSERT_EQUALS(mynacl, tb3.lookup("Cl", "Na")); - const BVParam& bpnone = BVParametersTable::none(); - TS_ASSERT_EQUALS(bpnone, mbvtb->lookup("Cl", "Na")); - } - -}; // class TestBVParametersTable - -// End of file diff --git a/src/tests/TestBVSCalculator.cpp b/src/tests/TestBVSCalculator.cpp new file mode 100644 index 00000000..8acae5e6 --- /dev/null +++ b/src/tests/TestBVSCalculator.cpp @@ -0,0 +1,156 @@ +/***************************************************************************** + * + * libdiffpy by DANSE Diffraction group + * Simon J. L. Billinge + * (c) 2010 The Trustees of Columbia University + * in the City of New York. All rights reserved. + * + * File coded by: Pavol Juhas + * + * See AUTHORS.rst for a list of people who contributed. + * See LICENSE_DANSE.rst for license information. + * + ****************************************************************************** + * + * TestBVSCalculator -- unit tests suite + * + *****************************************************************************/ + +#include +#include + +#include +#include +#include +#include "test_helpers.hpp" + +using Catch::Matchers::WithinAbs; + +using namespace std; +using diffpy::srreal::BVParametersTablePtr; +using diffpy::srreal::BVSCalculator; +using diffpy::srreal::PeriodicStructureAdapter; +using diffpy::srreal::PeriodicStructureAdapterPtr; +using diffpy::srreal::StructureAdapterPtr; + +////////////////////////////////////////////////////////////////////////////// +// TestBVSCalculator +////////////////////////////////////////////////////////////////////////////// + +class TestBVSCalculatorFixture { + protected: + StructureAdapterPtr mnacl; + std::shared_ptr mbvc; + + public: + TestBVSCalculatorFixture() { + mnacl = loadTestPeriodicStructure("NaCl.stru"); + mbvc.reset(new BVSCalculator); + } +}; // class TestBVSCalculatorFixture + +TEST_CASE_METHOD(TestBVSCalculatorFixture, "TestBVSCalculator::test_NaCl", + "[BVSCalculator]") { + const double eps = 1e-4; + mbvc->eval(mnacl); + CHECK(8u == mbvc->value().size()); + CHECK_THAT(+1.01352, WithinAbs(mbvc->value()[0], eps)); + CHECK_THAT(+1.01352, WithinAbs(mbvc->value()[1], eps)); + CHECK_THAT(+1.01352, WithinAbs(mbvc->value()[2], eps)); + CHECK_THAT(+1.01352, WithinAbs(mbvc->value()[3], eps)); + CHECK_THAT(-1.01352, WithinAbs(mbvc->value()[4], eps)); + CHECK_THAT(-1.01352, WithinAbs(mbvc->value()[5], eps)); + CHECK_THAT(-1.01352, WithinAbs(mbvc->value()[6], eps)); + CHECK_THAT(-1.01352, WithinAbs(mbvc->value()[7], eps)); +} + +TEST_CASE_METHOD(TestBVSCalculatorFixture, "TestBVSCalculator::test_NaCl_mixed", + "[BVSCalculator]") { + StructureAdapterPtr nacl_mixed = loadTestPeriodicStructure("NaCl_mixed.stru"); + mbvc->eval(mnacl); + BVSCalculator bvc; + bvc.eval(nacl_mixed); + CHECK_THAT(mbvc->bvrmsdiff(), WithinAbs(bvc.bvrmsdiff(), 1e-12)); +} + +TEST_CASE_METHOD(TestBVSCalculatorFixture, + "TestBVSCalculator::test_customAtomValences", + "[BVSCalculator]") { + using diffpy::srreal::Atom; + const double eps = 1e-4; + mbvc->eval(mnacl); + CHECK_THAT(0.01352, WithinAbs(mbvc->bvrmsdiff(), eps)); + BVParametersTablePtr bvtb = mbvc->getBVParamTable(); + bvtb->setAtomValence("Cl1-", 0); + mbvc->eval(mnacl); + CHECK(0.0 == mbvc->value()[0]); + CHECK(0.0 == mbvc->value()[4]); + // create structure with bare atom symbols "Na", "Cl". + PeriodicStructureAdapterPtr naclbare = + std::dynamic_pointer_cast(mnacl->clone()); + for (int i = 0; i < naclbare->countSites(); ++i) { + Atom& a = naclbare->at(i); + a.atomtype = a.atomtype.substr(0, 2); + } + CHECK(string("Na") == naclbare->siteAtomType(0)); + // verify valence sums are zero for a standard setup. + mbvc->eval(naclbare); + CHECK(0.0 == mbvc->value()[0]); + CHECK(0.0 == mbvc->value()[4]); + // verify valence sums with custom atom valences. + bvtb->setAtomValence("Na", +1); + bvtb->setAtomValence("Cl", -1); + mbvc->eval(naclbare); + CHECK_THAT(0.01352, WithinAbs(mbvc->bvrmsdiff(), eps)); +} + +TEST_CASE_METHOD(TestBVSCalculatorFixture, + "TestBVSCalculator::test_setValencePrecision", + "[BVSCalculator]") { + CHECK_THROWS_AS(mbvc->setValencePrecision(0), invalid_argument); + CHECK_THROWS_AS(mbvc->setValencePrecision(-1), invalid_argument); + CHECK_THROWS_AS(mbvc->setDoubleAttr("valenceprecision", 0), invalid_argument); +} + +TEST_CASE_METHOD(TestBVSCalculatorFixture, + "TestBVSCalculator::test_getRmaxUsed", "[BVSCalculator]") { + mbvc->eval(mnacl); + double rmaxused = mbvc->getDoubleAttr("rmaxused"); + CHECK(rmaxused == mbvc->getRmaxUsed()); + CHECK(rmaxused < mbvc->getRmax()); + mbvc->setDoubleAttr("valenceprecision", + mbvc->getDoubleAttr("valenceprecision") / 10.0); + CHECK(rmaxused < mbvc->getDoubleAttr("rmaxused")); + mbvc->setValencePrecision(1e-7); + mbvc->setDoubleAttr("rmax", 5.0); + CHECK(5.0 == mbvc->getDoubleAttr("rmaxused")); + // check if value updates with changes in the table. + BVParametersTablePtr bvtb = mbvc->getBVParamTable(); + bvtb->setCustom("Na", 1, "Cl", -1, 0.0, 0); + CHECK(0.0 == mbvc->getDoubleAttr("rmaxused")); +} + +TEST_CASE_METHOD(TestBVSCalculatorFixture, + "TestBVSCalculator::test_serialization", "[BVSCalculator]") { + mbvc->eval(mnacl); + stringstream storage(ios::in | ios::out | ios::binary); + diffpy::serialization::oarchive oa(storage, ios::binary); + oa << mbvc; + diffpy::serialization::iarchive ia(storage, ios::binary); + std::shared_ptr bvc1; + CHECK(!bvc1.get()); + ia >> bvc1; + CHECK(mbvc.get() != bvc1.get()); + const double eps = 1e-4; + CHECK(8u == bvc1->value().size()); + CHECK_THAT(+1.01352, WithinAbs(bvc1->value()[0], eps)); + CHECK_THAT(+1.01352, WithinAbs(bvc1->value()[1], eps)); + CHECK_THAT(+1.01352, WithinAbs(bvc1->value()[2], eps)); + CHECK_THAT(+1.01352, WithinAbs(bvc1->value()[3], eps)); + CHECK_THAT(-1.01352, WithinAbs(bvc1->value()[4], eps)); + CHECK_THAT(-1.01352, WithinAbs(bvc1->value()[5], eps)); + CHECK_THAT(-1.01352, WithinAbs(bvc1->value()[6], eps)); + CHECK_THAT(-1.01352, WithinAbs(bvc1->value()[7], eps)); +} + +// End of file diff --git a/src/tests/TestBVSCalculator.hpp b/src/tests/TestBVSCalculator.hpp deleted file mode 100644 index 8235e5c1..00000000 --- a/src/tests/TestBVSCalculator.hpp +++ /dev/null @@ -1,150 +0,0 @@ -/***************************************************************************** - * - * libdiffpy by DANSE Diffraction group - * Simon J. L. Billinge - * (c) 2010 The Trustees of Columbia University - * in the City of New York. All rights reserved. - * - * File coded by: Pavol Juhas - * - * See AUTHORS.rst for a list of people who contributed. - * See LICENSE_DANSE.rst for license information. - * - ****************************************************************************** - * - * class TestBVSCalculator -- unit tests suite - * - *****************************************************************************/ - -#include - -#include -#include -#include -#include "test_helpers.hpp" - -using namespace std; -using diffpy::srreal::BVParametersTablePtr; -using diffpy::srreal::BVSCalculator; -using diffpy::srreal::PeriodicStructureAdapter; -using diffpy::srreal::PeriodicStructureAdapterPtr; -using diffpy::srreal::StructureAdapterPtr; - -////////////////////////////////////////////////////////////////////////////// -// class TestBVSCalculator -////////////////////////////////////////////////////////////////////////////// - -class TestBVSCalculator : public CxxTest::TestSuite { - private: - StructureAdapterPtr mnacl; - std::shared_ptr mbvc; - - public: - void setUp() { - CxxTest::setAbortTestOnFail(true); - if (!mnacl) mnacl = loadTestPeriodicStructure("NaCl.stru"); - mbvc.reset(new BVSCalculator); - CxxTest::setAbortTestOnFail(false); - } - - void test_NaCl() { - const double eps = 1e-4; - mbvc->eval(mnacl); - TS_ASSERT_EQUALS(8u, mbvc->value().size()); - TS_ASSERT_DELTA(+1.01352, mbvc->value()[0], eps); - TS_ASSERT_DELTA(+1.01352, mbvc->value()[1], eps); - TS_ASSERT_DELTA(+1.01352, mbvc->value()[2], eps); - TS_ASSERT_DELTA(+1.01352, mbvc->value()[3], eps); - TS_ASSERT_DELTA(-1.01352, mbvc->value()[4], eps); - TS_ASSERT_DELTA(-1.01352, mbvc->value()[5], eps); - TS_ASSERT_DELTA(-1.01352, mbvc->value()[6], eps); - TS_ASSERT_DELTA(-1.01352, mbvc->value()[7], eps); - } - - void test_NaCl_mixed() { - StructureAdapterPtr nacl_mixed = - loadTestPeriodicStructure("NaCl_mixed.stru"); - mbvc->eval(mnacl); - BVSCalculator bvc; - bvc.eval(nacl_mixed); - TS_ASSERT_DELTA(mbvc->bvrmsdiff(), bvc.bvrmsdiff(), 1e-12); - } - - void test_customAtomValences() { - using diffpy::srreal::Atom; - const double eps = 1e-4; - mbvc->eval(mnacl); - TS_ASSERT_DELTA(0.01352, mbvc->bvrmsdiff(), eps); - BVParametersTablePtr bvtb = mbvc->getBVParamTable(); - bvtb->setAtomValence("Cl1-", 0); - mbvc->eval(mnacl); - TS_ASSERT_EQUALS(0.0, mbvc->value()[0]); - TS_ASSERT_EQUALS(0.0, mbvc->value()[4]); - // create structure with bare atom symbols "Na", "Cl". - PeriodicStructureAdapterPtr naclbare = - std::dynamic_pointer_cast(mnacl->clone()); - for (int i = 0; i < naclbare->countSites(); ++i) { - Atom& a = naclbare->at(i); - a.atomtype = a.atomtype.substr(0, 2); - } - TS_ASSERT_EQUALS(string("Na"), naclbare->siteAtomType(0)); - // verify valence sums are zero for a standard setup. - mbvc->eval(naclbare); - TS_ASSERT_EQUALS(0.0, mbvc->value()[0]); - TS_ASSERT_EQUALS(0.0, mbvc->value()[4]); - // verify valence sums with custom atom valences. - bvtb->setAtomValence("Na", +1); - bvtb->setAtomValence("Cl", -1); - mbvc->eval(naclbare); - TS_ASSERT_DELTA(0.01352, mbvc->bvrmsdiff(), eps); - } - - void test_setValencePrecision() { - TS_ASSERT_THROWS(mbvc->setValencePrecision(0), invalid_argument); - TS_ASSERT_THROWS(mbvc->setValencePrecision(-1), invalid_argument); - TS_ASSERT_THROWS(mbvc->setDoubleAttr("valenceprecision", 0), - invalid_argument); - } - - void test_getRmaxUsed() { - mbvc->eval(mnacl); - double rmaxused = mbvc->getDoubleAttr("rmaxused"); - TS_ASSERT_EQUALS(rmaxused, mbvc->getRmaxUsed()); - TS_ASSERT_LESS_THAN(rmaxused, mbvc->getRmax()); - mbvc->setDoubleAttr("valenceprecision", - mbvc->getDoubleAttr("valenceprecision") / 10.0); - TS_ASSERT_LESS_THAN(rmaxused, mbvc->getDoubleAttr("rmaxused")); - mbvc->setValencePrecision(1e-7); - mbvc->setDoubleAttr("rmax", 5.0); - TS_ASSERT_EQUALS(5.0, mbvc->getDoubleAttr("rmaxused")); - // check if value updates with changes in the table. - BVParametersTablePtr bvtb = mbvc->getBVParamTable(); - bvtb->setCustom("Na", 1, "Cl", -1, 0.0, 0); - TS_ASSERT_EQUALS(0.0, mbvc->getDoubleAttr("rmaxused")); - } - - void test_serialization() { - mbvc->eval(mnacl); - stringstream storage(ios::in | ios::out | ios::binary); - diffpy::serialization::oarchive oa(storage, ios::binary); - oa << mbvc; - diffpy::serialization::iarchive ia(storage, ios::binary); - std::shared_ptr bvc1; - TS_ASSERT(!bvc1.get()); - ia >> bvc1; - TS_ASSERT_DIFFERS(mbvc.get(), bvc1.get()); - const double eps = 1e-4; - TS_ASSERT_EQUALS(8u, bvc1->value().size()); - TS_ASSERT_DELTA(+1.01352, bvc1->value()[0], eps); - TS_ASSERT_DELTA(+1.01352, bvc1->value()[1], eps); - TS_ASSERT_DELTA(+1.01352, bvc1->value()[2], eps); - TS_ASSERT_DELTA(+1.01352, bvc1->value()[3], eps); - TS_ASSERT_DELTA(-1.01352, bvc1->value()[4], eps); - TS_ASSERT_DELTA(-1.01352, bvc1->value()[5], eps); - TS_ASSERT_DELTA(-1.01352, bvc1->value()[6], eps); - TS_ASSERT_DELTA(-1.01352, bvc1->value()[7], eps); - } - -}; // class TestBVSCalculator - -// End of file diff --git a/src/tests/TestBVSObjCryst.cpp b/src/tests/TestBVSObjCryst.cpp new file mode 100644 index 00000000..6c2f8ecf --- /dev/null +++ b/src/tests/TestBVSObjCryst.cpp @@ -0,0 +1,62 @@ +/***************************************************************************** + * + * libdiffpy by DANSE Diffraction group + * Simon J. L. Billinge + * (c) 2010 The Trustees of Columbia University + * in the City of New York. All rights reserved. + * + * File coded by: Pavol Juhas + * + * See AUTHORS.rst for a list of people who contributed. + * See LICENSE_DANSE.rst for license information. + * + ****************************************************************************** + * + * TestBVSObjCryst -- unit tests for BVS calculation for an ObjCryst + * crystal structure + * + *****************************************************************************/ + +#include +#include + +#include +#include + +#include "objcryst_helpers.hpp" + +using Catch::Matchers::WithinAbs; + +using namespace std; +using namespace diffpy::srreal; + +////////////////////////////////////////////////////////////////////////////// +// class Test +////////////////////////////////////////////////////////////////////////////// + +class TestBVSObjCrystFixture { + protected: + const ObjCryst::Crystal& mnacl = cachedTestCrystal("NaCl.cif"); + BVSCalculator mbvc; +}; // class TestBVSObjCrystFixture + +TEST_CASE_METHOD(TestBVSObjCrystFixture, "TestBVSObjCryst::test_NaCl", + "[BVSObjCryst]") { + const double eps = 1e-4; + mbvc.eval(mnacl); + CHECK(2u == mbvc.value().size()); + CHECK_THAT(+1.01352, WithinAbs(mbvc.value()[0], eps)); + CHECK_THAT(-1.01352, WithinAbs(mbvc.value()[1], eps)); +} + +TEST_CASE_METHOD(TestBVSObjCrystFixture, "TestBVSObjCryst::test_NaCl_mixed", + "[BVSObjCryst]") { + unique_ptr nacl_mixed; + nacl_mixed.reset(loadTestCrystal("NaCl_mixed.cif")); + mbvc.eval(mnacl); + BVSCalculator bvc; + bvc.eval(*nacl_mixed); + CHECK_THAT(mbvc.bvrmsdiff(), WithinAbs(bvc.bvrmsdiff(), 1e-12)); +} + +// End of file diff --git a/src/tests/TestBVSObjCryst.hpp b/src/tests/TestBVSObjCryst.hpp deleted file mode 100644 index b2eba7c6..00000000 --- a/src/tests/TestBVSObjCryst.hpp +++ /dev/null @@ -1,66 +0,0 @@ -/***************************************************************************** - * - * libdiffpy by DANSE Diffraction group - * Simon J. L. Billinge - * (c) 2010 The Trustees of Columbia University - * in the City of New York. All rights reserved. - * - * File coded by: Pavol Juhas - * - * See AUTHORS.rst for a list of people who contributed. - * See LICENSE_DANSE.rst for license information. - * - ****************************************************************************** - * - * class TestBVSObjCryst -- unit tests for BVS calculation for an ObjCryst - * crystal structure - * - *****************************************************************************/ - -#include - -#include -#include - -#include "objcryst_helpers.hpp" - -using namespace std; -using namespace diffpy::srreal; - -////////////////////////////////////////////////////////////////////////////// -// class Test -////////////////////////////////////////////////////////////////////////////// - -class TestBVSObjCryst : public CxxTest::TestSuite { - private: - unique_ptr mnacl; - unique_ptr mbvc; - - public: - void setUp() { - if (!mnacl.get()) { - mnacl.reset(loadTestCrystal("NaCl.cif")); - } - mbvc.reset(new BVSCalculator); - } - - void test_NaCl() { - const double eps = 1e-4; - mbvc->eval(*mnacl); - TS_ASSERT_EQUALS(2u, mbvc->value().size()); - TS_ASSERT_DELTA(+1.01352, mbvc->value()[0], eps); - TS_ASSERT_DELTA(-1.01352, mbvc->value()[1], eps); - } - - void test_NaCl_mixed() { - unique_ptr nacl_mixed; - nacl_mixed.reset(loadTestCrystal("NaCl_mixed.cif")); - mbvc->eval(*mnacl); - BVSCalculator bvc; - bvc.eval(*nacl_mixed); - TS_ASSERT_DELTA(mbvc->bvrmsdiff(), bvc.bvrmsdiff(), 1e-12); - } - -}; // class TestBVSObjCryst - -// End of file diff --git a/src/tests/TestDebyePDFCalculator.cpp b/src/tests/TestDebyePDFCalculator.cpp new file mode 100644 index 00000000..bda39e72 --- /dev/null +++ b/src/tests/TestDebyePDFCalculator.cpp @@ -0,0 +1,355 @@ +/***************************************************************************** + * + * libdiffpy by DANSE Diffraction group + * Simon J. L. Billinge + * (c) 2011 The Trustees of Columbia University + * in the City of New York. All rights reserved. + * + * File coded by: Pavol Juhas + * + * See AUTHORS.rst for a list of people who contributed. + * See LICENSE_DANSE.rst for license information. + * + ****************************************************************************** + * + * TestDebyePDFCalculator -- unit tests for DebyePDFCalculator class + * + *****************************************************************************/ + +#include +#include + +#include + +#include +#include +#include +#include +#include +#include + +using Catch::Matchers::WithinAbs; + +using namespace std; +using namespace diffpy::srreal; + +class TestDebyePDFCalculatorFixture { + protected: + std::shared_ptr mpdfc; + AtomicStructureAdapterPtr memptystru; + AtomicStructureAdapterPtr mstru10; + AtomicStructureAdapterPtr mstru10d1; + AtomicStructureAdapterPtr mstru10r; + AtomicStructureAdapterPtr mstru9; + diffpy::mathutils::EpsilonEqual allclose; + double meps; + + public: + TestDebyePDFCalculatorFixture() { + const int SZ = 10; + meps = diffpy::mathutils::SQRT_DOUBLE_EPS; + mpdfc.reset(new DebyePDFCalculator); + memptystru = std::make_shared(); + mstru10 = std::make_shared(); + Atom ai; + ai.atomtype = "C"; + ai.uij_cartn = R3::identity(); + ai.uij_cartn(0, 0) = ai.uij_cartn(1, 1) = ai.uij_cartn(2, 2) = 0.004; + for (int i = 0; i < SZ; ++i) { + ai.xyz_cartn[0] = i; + mstru10->append(ai); + } + mstru10d1 = std::make_shared(*mstru10); + (*mstru10d1)[0].atomtype = "Au"; + mstru10r = std::make_shared(); + mstru10r->assign(mstru10->rbegin(), mstru10->rend()); + mstru9 = std::make_shared(*mstru10); + mstru9->erase(9); + } +}; // class TestDebyePDFCalculatorFixture + +TEST_CASE_METHOD(TestDebyePDFCalculatorFixture, + "TestDebyePDFCalculator::test_setQmax", + "[DebyePDFCalculator]") { + const double dq0 = mpdfc->getQstep(); + const double qmax0 = mpdfc->getQmax(); + const double qmax1 = 2 * dq0; + mpdfc->setQmax(qmax1); + CHECK(qmax1 / 4.0 == mpdfc->getQstep()); + mpdfc->setQmax(qmax0); + CHECK(dq0 == mpdfc->getQstep()); +} + +TEST_CASE_METHOD(TestDebyePDFCalculatorFixture, + "TestDebyePDFCalculator::test_setQstep", + "[DebyePDFCalculator]") { + mpdfc->setRstep(2.0); + mpdfc->setQstep(0.02); + CHECK(0.02 == mpdfc->getQstep()); +} + +TEST_CASE_METHOD(TestDebyePDFCalculatorFixture, + "TestDebyePDFCalculator::test_access_Envelopes", + "[DebyePDFCalculator]") { + CHECK(2u == mpdfc->usedEnvelopeTypes().size()); + CHECK(1.0 == mpdfc->getDoubleAttr("scale")); + CHECK(0.0 == mpdfc->getDoubleAttr("qdamp")); + mpdfc->setDoubleAttr("scale", 3.0); + CHECK(3.0 == mpdfc->getDoubleAttr("scale")); + mpdfc->addEnvelopeByType("scale"); + CHECK(1.0 == mpdfc->getDoubleAttr("scale")); + QResolutionEnvelope qdamp4; + qdamp4.setQdamp(4); + mpdfc->addEnvelope(qdamp4.clone()); + CHECK(4.0 == mpdfc->getDoubleAttr("qdamp")); + CHECK_THROWS_AS(mpdfc->addEnvelopeByType("invalid"), logic_error); +} + +TEST_CASE_METHOD(TestDebyePDFCalculatorFixture, + "TestDebyePDFCalculator::test_getPDF", + "[DebyePDFCalculator]") { + QuantityType pdf; + CHECK(1000u == mpdfc->getPDF().size()); + mpdfc->setRmin(2.0); + mpdfc->setRmax(0.0); + mpdfc->eval(memptystru); + pdf = mpdfc->getPDF(); + CHECK(mpdfc->getPDF().empty()); + mpdfc->setRmax(2.0); + mpdfc->eval(memptystru); + pdf = mpdfc->getPDF(); + CHECK(mpdfc->getPDF().empty()); + mpdfc->setRmax(2.01001); + mpdfc->eval(memptystru); + pdf = mpdfc->getPDF(); + CHECK(2u == pdf.size()); +} + +TEST_CASE_METHOD(TestDebyePDFCalculatorFixture, + "TestDebyePDFCalculator::test_getRDF", + "[DebyePDFCalculator]") { + // getRDF is not yet implemented and just returns empty array. + QuantityType rdf = mpdfc->getRDF(); + CHECK(1000u == rdf.size()); + CHECK(0.0 == *std::min_element(rdf.begin(), rdf.end())); + CHECK(0.0 == *std::max_element(rdf.begin(), rdf.end())); +} + +TEST_CASE_METHOD(TestDebyePDFCalculatorFixture, + "TestDebyePDFCalculator::test_getF", "[DebyePDFCalculator]") { + QuantityType fq = mpdfc->getF(); + CHECK(92u == fq.size()); + CHECK(0.0 == *min_element(fq.begin(), fq.end())); + CHECK(0.0 == *max_element(fq.begin(), fq.end())); +} + +TEST_CASE_METHOD(TestDebyePDFCalculatorFixture, + "TestDebyePDFCalculator::test_getQgrid", + "[DebyePDFCalculator]") { + CHECK(92u == mpdfc->getQgrid().size()); +} + +TEST_CASE_METHOD(TestDebyePDFCalculatorFixture, + "TestDebyePDFCalculator::test_getQmin", + "[DebyePDFCalculator]") { + CHECK(0.0 == mpdfc->getQmin()); + mpdfc->setQmin(1.0); + const DebyePDFCalculator& dpc = *mpdfc; + CHECK(1.0 == dpc.getQmin()); + CHECK(0.0 == dpc.BaseDebyeSum::getQmin()); +} + +TEST_CASE_METHOD(TestDebyePDFCalculatorFixture, + "TestDebyePDFCalculator::test_getQmax", + "[DebyePDFCalculator]") { + CHECK_THAT(25.0, WithinAbs(mpdfc->getQmax(), meps)); +} + +TEST_CASE_METHOD(TestDebyePDFCalculatorFixture, + "TestDebyePDFCalculator::test_getQstep", + "[DebyePDFCalculator]") { + const double rcalchi = ceil((10.0 + 12 * M_PI / 25.0) / 0.01) * 0.01; + const double qstep = M_PI / rcalchi; + CHECK_THAT(qstep, WithinAbs(mpdfc->getQstep(), meps)); +} + +TEST_CASE_METHOD(TestDebyePDFCalculatorFixture, + "TestDebyePDFCalculator::test_getRgrid", + "[DebyePDFCalculator]") { + QuantityType rgrid0 = mpdfc->getRgrid(); + CHECK(rgrid0 == mpdfc->getRgrid()); + mpdfc->setRmin(5); + mpdfc->setRmax(4); + CHECK(mpdfc->getRgrid().empty()); +} + +TEST_CASE_METHOD(TestDebyePDFCalculatorFixture, + "TestDebyePDFCalculator::test_serialization", + "[DebyePDFCalculator]") { + // build customized DebyePDFCalculator + mpdfc->setPeakWidthModelByType("constant"); + mpdfc->setDoubleAttr("width", 0.123); + mpdfc->setDoubleAttr("debyeprecision", 0.00011); + mpdfc->setScatteringFactorTableByType("electronnumber"); + mpdfc->getScatteringFactorTable()->setCustomAs("H", "H", 1.1); + // dump it to string + stringstream storage(ios::in | ios::out | ios::binary); + diffpy::serialization::oarchive oa(storage, ios::binary); + oa << mpdfc; + diffpy::serialization::iarchive ia(storage, ios::binary); + std::shared_ptr pdfc1; + ia >> pdfc1; + CHECK(pdfc1.get() != mpdfc.get()); + CHECK(string("constant") == pdfc1->getPeakWidthModel()->type()); + CHECK(0.123 == pdfc1->getDoubleAttr("width")); + CHECK(0.00011 == pdfc1->getDoubleAttr("debyeprecision")); + CHECK(string("electronnumber") == pdfc1->getScatteringFactorTable()->type()); + CHECK(1.1 == pdfc1->getScatteringFactorTable()->lookup("H")); +} + +TEST_CASE_METHOD(TestDebyePDFCalculatorFixture, + "TestDebyePDFCalculator::test_DBPDF_change_atom", + "[DebyePDFCalculator]") { + using std::placeholders::_1; + mpdfc->setQmin(1.0); + DebyePDFCalculator pdfcb = *mpdfc; + DebyePDFCalculator pdfco = *mpdfc; + CHECK(1.0 == pdfcb.getQmin()); + pdfcb.setEvaluatorType(BASIC); + pdfco.setEvaluatorType(OPTIMIZED); + CHECK(NONE == pdfcb.getEvaluatorTypeUsed()); + CHECK(NONE == pdfco.getEvaluatorTypeUsed()); + pdfcb.eval(mstru10); + pdfco.eval(mstru10); + QuantityType gb = pdfcb.getPDF(); + QuantityType go = pdfco.getPDF(); + CHECK(!gb.empty()); + CHECK(gb == go); + int cnonzero = + count_if(gb.begin(), gb.end(), bind(not_equal_to(), _1, 0.0)); + CHECK(cnonzero); + CHECK(BASIC == pdfcb.getEvaluatorType()); + CHECK(OPTIMIZED == pdfco.getEvaluatorType()); + // first call of pdfco should use the BASIC evaluation + CHECK(BASIC == pdfcb.getEvaluatorTypeUsed()); + CHECK(BASIC == pdfco.getEvaluatorTypeUsed()); + // test second call on the same structure + pdfco.eval(mstru10); + go = pdfco.getPDF(); + CHECK(gb == go); + CHECK(OPTIMIZED == pdfco.getEvaluatorTypeUsed()); + // test structure with one different atom + pdfcb.eval(mstru10d1); + pdfco.eval(mstru10d1); + QuantityType gb1 = pdfcb.getPDF(); + QuantityType go1 = pdfco.getPDF(); + CHECK(!allclose(gb, gb1)); + CHECK(OPTIMIZED == pdfco.getEvaluatorTypeUsed()); + CHECK(allclose(gb1, go1)); + // change position of 1 atom + mstru10d1->at(0).xyz_cartn[1] = 0.5; + pdfcb.eval(mstru10d1); + pdfco.eval(mstru10d1); + QuantityType gb2 = pdfcb.getPDF(); + QuantityType go2 = pdfco.getPDF(); + CHECK(!allclose(gb1, gb2)); + CHECK(OPTIMIZED == pdfco.getEvaluatorTypeUsed()); + CHECK(allclose(gb2, go2)); +} + +TEST_CASE_METHOD(TestDebyePDFCalculatorFixture, + "TestDebyePDFCalculator::test_DBPDF_reverse_atoms", + "[DebyePDFCalculator]") { + mpdfc->setQmin(1.1); + DebyePDFCalculator pdfcb = *mpdfc; + DebyePDFCalculator pdfco = *mpdfc; + pdfcb.setEvaluatorType(BASIC); + pdfco.setEvaluatorType(OPTIMIZED); + pdfcb.eval(mstru10); + pdfco.eval(mstru10); + pdfco.eval(mstru10r); + QuantityType gb = pdfcb.getPDF(); + QuantityType go = pdfco.getPDF(); + CHECK(OPTIMIZED == pdfco.getEvaluatorTypeUsed()); + CHECK(allclose(gb, go)); +} + +TEST_CASE_METHOD(TestDebyePDFCalculatorFixture, + "TestDebyePDFCalculator::test_DBPDF_remove_atom", + "[DebyePDFCalculator]") { + mpdfc->setQmin(1.2); + DebyePDFCalculator pdfcb = *mpdfc; + DebyePDFCalculator pdfco = *mpdfc; + pdfcb.setEvaluatorType(BASIC); + pdfco.setEvaluatorType(OPTIMIZED); + pdfcb.eval(mstru9); + pdfco.eval(mstru10); + pdfco.eval(mstru9); + QuantityType gb = pdfcb.getPDF(); + QuantityType go = pdfco.getPDF(); + CHECK(OPTIMIZED == pdfco.getEvaluatorTypeUsed()); + CHECK(allclose(gb, go)); +} + +TEST_CASE_METHOD(TestDebyePDFCalculatorFixture, + "TestDebyePDFCalculator::test_DBPDF_qmin_click", + "[DebyePDFCalculator]") { + mpdfc->setEvaluatorType(OPTIMIZED); + CHECK(NONE == mpdfc->getEvaluatorTypeUsed()); + mpdfc->eval(mstru10); + mpdfc->eval(mstru10); + CHECK(OPTIMIZED == mpdfc->getEvaluatorTypeUsed()); + mpdfc->setQmin(4); + mpdfc->eval(mstru10); + CHECK(OPTIMIZED == mpdfc->getEvaluatorTypeUsed()); + mpdfc->setQmin(0); + CHECK(OPTIMIZED == mpdfc->getEvaluatorTypeUsed()); +} + +TEST_CASE_METHOD(TestDebyePDFCalculatorFixture, + "TestDebyePDFCalculator::test_DBPDF_qmax_click", + "[DebyePDFCalculator]") { + mpdfc->setEvaluatorType(OPTIMIZED); + mpdfc->eval(mstru10); + mpdfc->eval(mstru10); + CHECK(OPTIMIZED == mpdfc->getEvaluatorTypeUsed()); + mpdfc->setQmax(mpdfc->getQmax()); + mpdfc->eval(mstru10); + CHECK(OPTIMIZED == mpdfc->getEvaluatorTypeUsed()); + mpdfc->setQmax(mpdfc->getQmax() - 1.0); + mpdfc->eval(mstru10); + CHECK(BASIC == mpdfc->getEvaluatorTypeUsed()); +} + +TEST_CASE_METHOD(TestDebyePDFCalculatorFixture, + "TestDebyePDFCalculator::test_DBPDF_delta12_click", + "[DebyePDFCalculator]") { + mpdfc->setEvaluatorType(OPTIMIZED); + mpdfc->eval(mstru10); + mpdfc->setDoubleAttr("delta1", mpdfc->getDoubleAttr("delta1")); + mpdfc->setDoubleAttr("delta2", mpdfc->getDoubleAttr("delta2")); + mpdfc->eval(mstru10); + CHECK(OPTIMIZED == mpdfc->getEvaluatorTypeUsed()); + mpdfc->setDoubleAttr("delta1", 0.5 + mpdfc->getDoubleAttr("delta1")); + mpdfc->eval(mstru10); + CHECK(BASIC == mpdfc->getEvaluatorTypeUsed()); + mpdfc->eval(mstru10); + mpdfc->setDoubleAttr("delta2", 0.5 + mpdfc->getDoubleAttr("delta2")); + mpdfc->eval(mstru10); + CHECK(BASIC == mpdfc->getEvaluatorTypeUsed()); +} + +TEST_CASE_METHOD(TestDebyePDFCalculatorFixture, + "TestDebyePDFCalculator::test_DBPDF_SFTB_click", + "[DebyePDFCalculator]") { + mpdfc->setEvaluatorType(OPTIMIZED); + mpdfc->eval(mstru10); + mpdfc->setScatteringFactorTable(mpdfc->getScatteringFactorTable()); + mpdfc->eval(mstru10); + CHECK(OPTIMIZED == mpdfc->getEvaluatorTypeUsed()); + mpdfc->setScatteringFactorTableByType("neutron"); + mpdfc->eval(mstru10); + CHECK(BASIC == mpdfc->getEvaluatorTypeUsed()); +} + +// End of file diff --git a/src/tests/TestDebyePDFCalculator.hpp b/src/tests/TestDebyePDFCalculator.hpp deleted file mode 100644 index ea8a9874..00000000 --- a/src/tests/TestDebyePDFCalculator.hpp +++ /dev/null @@ -1,313 +0,0 @@ -/***************************************************************************** - * - * libdiffpy by DANSE Diffraction group - * Simon J. L. Billinge - * (c) 2011 The Trustees of Columbia University - * in the City of New York. All rights reserved. - * - * File coded by: Pavol Juhas - * - * See AUTHORS.rst for a list of people who contributed. - * See LICENSE_DANSE.rst for license information. - * - ****************************************************************************** - * - * class TestDebyePDFCalculator -- unit tests for DebyePDFCalculator class - * - *****************************************************************************/ - -#include - -#include - -#include -#include -#include -#include -#include -#include - -using namespace std; -using namespace diffpy::srreal; - -class TestDebyePDFCalculator : public CxxTest::TestSuite { - private: - std::shared_ptr mpdfc; - AtomicStructureAdapterPtr memptystru; - AtomicStructureAdapterPtr mstru10; - AtomicStructureAdapterPtr mstru10d1; - AtomicStructureAdapterPtr mstru10r; - AtomicStructureAdapterPtr mstru9; - diffpy::mathutils::EpsilonEqual allclose; - double meps; - - public: - void setUp() { - const int SZ = 10; - meps = diffpy::mathutils::SQRT_DOUBLE_EPS; - mpdfc.reset(new DebyePDFCalculator); - memptystru = std::make_shared(); - mstru10 = std::make_shared(); - Atom ai; - ai.atomtype = "C"; - ai.uij_cartn = R3::identity(); - ai.uij_cartn(0, 0) = ai.uij_cartn(1, 1) = ai.uij_cartn(2, 2) = 0.004; - for (int i = 0; i < SZ; ++i) { - ai.xyz_cartn[0] = i; - mstru10->append(ai); - } - mstru10d1 = std::make_shared(*mstru10); - (*mstru10d1)[0].atomtype = "Au"; - mstru10r = std::make_shared(); - mstru10r->assign(mstru10->rbegin(), mstru10->rend()); - mstru9 = std::make_shared(*mstru10); - mstru9->erase(9); - } - - void test_setQmax() { - const double dq0 = mpdfc->getQstep(); - const double qmax0 = mpdfc->getQmax(); - const double qmax1 = 2 * dq0; - mpdfc->setQmax(qmax1); - TS_ASSERT_EQUALS(qmax1 / 4.0, mpdfc->getQstep()); - mpdfc->setQmax(qmax0); - TS_ASSERT_EQUALS(dq0, mpdfc->getQstep()); - } - - void test_setQstep() { - mpdfc->setRstep(2.0); - mpdfc->setQstep(0.02); - TS_ASSERT_EQUALS(0.02, mpdfc->getQstep()); - } - - void test_access_Envelopes() { - TS_ASSERT_EQUALS(2u, mpdfc->usedEnvelopeTypes().size()); - TS_ASSERT_EQUALS(1.0, mpdfc->getDoubleAttr("scale")); - TS_ASSERT_EQUALS(0.0, mpdfc->getDoubleAttr("qdamp")); - mpdfc->setDoubleAttr("scale", 3.0); - TS_ASSERT_EQUALS(3.0, mpdfc->getDoubleAttr("scale")); - mpdfc->addEnvelopeByType("scale"); - TS_ASSERT_EQUALS(1.0, mpdfc->getDoubleAttr("scale")); - QResolutionEnvelope qdamp4; - qdamp4.setQdamp(4); - mpdfc->addEnvelope(qdamp4.clone()); - TS_ASSERT_EQUALS(4.0, mpdfc->getDoubleAttr("qdamp")); - TS_ASSERT_THROWS(mpdfc->addEnvelopeByType("invalid"), logic_error); - } - - void test_getPDF() { - QuantityType pdf; - TS_ASSERT_EQUALS(1000u, mpdfc->getPDF().size()); - mpdfc->setRmin(2.0); - mpdfc->setRmax(0.0); - mpdfc->eval(memptystru); - pdf = mpdfc->getPDF(); - TS_ASSERT(mpdfc->getPDF().empty()); - mpdfc->setRmax(2.0); - mpdfc->eval(memptystru); - pdf = mpdfc->getPDF(); - TS_ASSERT(mpdfc->getPDF().empty()); - mpdfc->setRmax(2.01001); - mpdfc->eval(memptystru); - pdf = mpdfc->getPDF(); - TS_ASSERT_EQUALS(2u, pdf.size()); - } - - void test_getRDF() { - // getRDF is not yet implemented and just returns empty array. - QuantityType rdf = mpdfc->getRDF(); - TS_ASSERT_EQUALS(1000u, rdf.size()); - TS_ASSERT_EQUALS(0.0, *std::min_element(rdf.begin(), rdf.end())); - TS_ASSERT_EQUALS(0.0, *std::max_element(rdf.begin(), rdf.end())); - } - - void test_getF() { - QuantityType fq = mpdfc->getF(); - TS_ASSERT_EQUALS(92u, fq.size()); - TS_ASSERT_EQUALS(0.0, *min_element(fq.begin(), fq.end())); - TS_ASSERT_EQUALS(0.0, *max_element(fq.begin(), fq.end())); - } - - void test_getQgrid() { TS_ASSERT_EQUALS(92u, mpdfc->getQgrid().size()); } - - void test_getQmin() { - TS_ASSERT_EQUALS(0.0, mpdfc->getQmin()); - mpdfc->setQmin(1.0); - const DebyePDFCalculator& dpc = *mpdfc; - TS_ASSERT_EQUALS(1.0, dpc.getQmin()); - TS_ASSERT_EQUALS(0.0, dpc.BaseDebyeSum::getQmin()); - } - - void test_getQmax() { TS_ASSERT_DELTA(25.0, mpdfc->getQmax(), meps); } - - void test_getQstep() { - const double rcalchi = ceil((10.0 + 12 * M_PI / 25.0) / 0.01) * 0.01; - const double qstep = M_PI / rcalchi; - TS_ASSERT_DELTA(qstep, mpdfc->getQstep(), meps); - } - - void test_getRgrid() { - QuantityType rgrid0 = mpdfc->getRgrid(); - TS_ASSERT_EQUALS(rgrid0, mpdfc->getRgrid()); - mpdfc->setRmin(5); - mpdfc->setRmax(4); - TS_ASSERT(mpdfc->getRgrid().empty()); - } - - void test_serialization() { - // build customized DebyePDFCalculator - mpdfc->setPeakWidthModelByType("constant"); - mpdfc->setDoubleAttr("width", 0.123); - mpdfc->setDoubleAttr("debyeprecision", 0.00011); - mpdfc->setScatteringFactorTableByType("electronnumber"); - mpdfc->getScatteringFactorTable()->setCustomAs("H", "H", 1.1); - // dump it to string - stringstream storage(ios::in | ios::out | ios::binary); - diffpy::serialization::oarchive oa(storage, ios::binary); - oa << mpdfc; - diffpy::serialization::iarchive ia(storage, ios::binary); - std::shared_ptr pdfc1; - ia >> pdfc1; - TS_ASSERT_DIFFERS(pdfc1.get(), mpdfc.get()); - TS_ASSERT_EQUALS(string("constant"), pdfc1->getPeakWidthModel()->type()); - TS_ASSERT_EQUALS(0.123, pdfc1->getDoubleAttr("width")); - TS_ASSERT_EQUALS(0.00011, pdfc1->getDoubleAttr("debyeprecision")); - TS_ASSERT_EQUALS(string("electronnumber"), - pdfc1->getScatteringFactorTable()->type()); - TS_ASSERT_EQUALS(1.1, pdfc1->getScatteringFactorTable()->lookup("H")); - } - - void test_DBPDF_change_atom() { - using std::placeholders::_1; - mpdfc->setQmin(1.0); - DebyePDFCalculator pdfcb = *mpdfc; - DebyePDFCalculator pdfco = *mpdfc; - TS_ASSERT_EQUALS(1.0, pdfcb.getQmin()); - pdfcb.setEvaluatorType(BASIC); - pdfco.setEvaluatorType(OPTIMIZED); - TS_ASSERT_EQUALS(NONE, pdfcb.getEvaluatorTypeUsed()); - TS_ASSERT_EQUALS(NONE, pdfco.getEvaluatorTypeUsed()); - pdfcb.eval(mstru10); - pdfco.eval(mstru10); - QuantityType gb = pdfcb.getPDF(); - QuantityType go = pdfco.getPDF(); - TS_ASSERT(!gb.empty()); - TS_ASSERT_EQUALS(gb, go); - int cnonzero = - count_if(gb.begin(), gb.end(), bind(not_equal_to(), _1, 0.0)); - TS_ASSERT(cnonzero); - TS_ASSERT_EQUALS(BASIC, pdfcb.getEvaluatorType()); - TS_ASSERT_EQUALS(OPTIMIZED, pdfco.getEvaluatorType()); - // first call of pdfco should use the BASIC evaluation - TS_ASSERT_EQUALS(BASIC, pdfcb.getEvaluatorTypeUsed()); - TS_ASSERT_EQUALS(BASIC, pdfco.getEvaluatorTypeUsed()); - // test second call on the same structure - pdfco.eval(mstru10); - go = pdfco.getPDF(); - TS_ASSERT_EQUALS(gb, go); - TS_ASSERT_EQUALS(OPTIMIZED, pdfco.getEvaluatorTypeUsed()); - // test structure with one different atom - pdfcb.eval(mstru10d1); - pdfco.eval(mstru10d1); - QuantityType gb1 = pdfcb.getPDF(); - QuantityType go1 = pdfco.getPDF(); - TS_ASSERT(!allclose(gb, gb1)); - TS_ASSERT_EQUALS(OPTIMIZED, pdfco.getEvaluatorTypeUsed()); - TS_ASSERT(allclose(gb1, go1)); - // change position of 1 atom - mstru10d1->at(0).xyz_cartn[1] = 0.5; - pdfcb.eval(mstru10d1); - pdfco.eval(mstru10d1); - QuantityType gb2 = pdfcb.getPDF(); - QuantityType go2 = pdfco.getPDF(); - TS_ASSERT(!allclose(gb1, gb2)); - TS_ASSERT_EQUALS(OPTIMIZED, pdfco.getEvaluatorTypeUsed()); - TS_ASSERT(allclose(gb2, go2)); - } - - void test_DBPDF_reverse_atoms() { - mpdfc->setQmin(1.1); - DebyePDFCalculator pdfcb = *mpdfc; - DebyePDFCalculator pdfco = *mpdfc; - pdfcb.setEvaluatorType(BASIC); - pdfco.setEvaluatorType(OPTIMIZED); - pdfcb.eval(mstru10); - pdfco.eval(mstru10); - pdfco.eval(mstru10r); - QuantityType gb = pdfcb.getPDF(); - QuantityType go = pdfco.getPDF(); - TS_ASSERT_EQUALS(OPTIMIZED, pdfco.getEvaluatorTypeUsed()); - TS_ASSERT(allclose(gb, go)); - } - - void test_DBPDF_remove_atom() { - mpdfc->setQmin(1.2); - DebyePDFCalculator pdfcb = *mpdfc; - DebyePDFCalculator pdfco = *mpdfc; - pdfcb.setEvaluatorType(BASIC); - pdfco.setEvaluatorType(OPTIMIZED); - pdfcb.eval(mstru9); - pdfco.eval(mstru10); - pdfco.eval(mstru9); - QuantityType gb = pdfcb.getPDF(); - QuantityType go = pdfco.getPDF(); - TS_ASSERT_EQUALS(OPTIMIZED, pdfco.getEvaluatorTypeUsed()); - TS_ASSERT(allclose(gb, go)); - } - - void test_DBPDF_qmin_click() { - mpdfc->setEvaluatorType(OPTIMIZED); - TS_ASSERT_EQUALS(NONE, mpdfc->getEvaluatorTypeUsed()); - mpdfc->eval(mstru10); - mpdfc->eval(mstru10); - TS_ASSERT_EQUALS(OPTIMIZED, mpdfc->getEvaluatorTypeUsed()); - mpdfc->setQmin(4); - mpdfc->eval(mstru10); - TS_ASSERT_EQUALS(OPTIMIZED, mpdfc->getEvaluatorTypeUsed()); - mpdfc->setQmin(0); - TS_ASSERT_EQUALS(OPTIMIZED, mpdfc->getEvaluatorTypeUsed()); - } - - void test_DBPDF_qmax_click() { - mpdfc->setEvaluatorType(OPTIMIZED); - mpdfc->eval(mstru10); - mpdfc->eval(mstru10); - TS_ASSERT_EQUALS(OPTIMIZED, mpdfc->getEvaluatorTypeUsed()); - mpdfc->setQmax(mpdfc->getQmax()); - mpdfc->eval(mstru10); - TS_ASSERT_EQUALS(OPTIMIZED, mpdfc->getEvaluatorTypeUsed()); - mpdfc->setQmax(mpdfc->getQmax() - 1.0); - mpdfc->eval(mstru10); - TS_ASSERT_EQUALS(BASIC, mpdfc->getEvaluatorTypeUsed()); - } - - void test_DBPDF_delta12_click() { - mpdfc->setEvaluatorType(OPTIMIZED); - mpdfc->eval(mstru10); - mpdfc->setDoubleAttr("delta1", mpdfc->getDoubleAttr("delta1")); - mpdfc->setDoubleAttr("delta2", mpdfc->getDoubleAttr("delta2")); - mpdfc->eval(mstru10); - TS_ASSERT_EQUALS(OPTIMIZED, mpdfc->getEvaluatorTypeUsed()); - mpdfc->setDoubleAttr("delta1", 0.5 + mpdfc->getDoubleAttr("delta1")); - mpdfc->eval(mstru10); - TS_ASSERT_EQUALS(BASIC, mpdfc->getEvaluatorTypeUsed()); - mpdfc->eval(mstru10); - mpdfc->setDoubleAttr("delta2", 0.5 + mpdfc->getDoubleAttr("delta2")); - mpdfc->eval(mstru10); - TS_ASSERT_EQUALS(BASIC, mpdfc->getEvaluatorTypeUsed()); - } - - void test_DBPDF_SFTB_click() { - mpdfc->setEvaluatorType(OPTIMIZED); - mpdfc->eval(mstru10); - mpdfc->setScatteringFactorTable(mpdfc->getScatteringFactorTable()); - mpdfc->eval(mstru10); - TS_ASSERT_EQUALS(OPTIMIZED, mpdfc->getEvaluatorTypeUsed()); - mpdfc->setScatteringFactorTableByType("neutron"); - mpdfc->eval(mstru10); - TS_ASSERT_EQUALS(BASIC, mpdfc->getEvaluatorTypeUsed()); - } - -}; // class TestDebyePDFCalculator - -// End of file diff --git a/src/tests/TestEmptyStructureAdapter.cpp b/src/tests/TestEmptyStructureAdapter.cpp new file mode 100644 index 00000000..7300edbe --- /dev/null +++ b/src/tests/TestEmptyStructureAdapter.cpp @@ -0,0 +1,71 @@ +/***************************************************************************** + * + * libdiffpy Complex Modeling Initiative + * (c) 2016 Brookhaven Science Associates, + * Brookhaven National Laboratory. + * All rights reserved. + * + * File coded by: Pavol Juhas + * + * See AUTHORS.rst for a list of people who contributed. + * See LICENSE.rst for license information. + * + ****************************************************************************** + * + * TestEmptyStructureAdapter -- test adapter that is always empty + * + *****************************************************************************/ + +#include + +#include +#include "serialization_helpers.hpp" + +namespace diffpy { +namespace srreal { + +using namespace std; + +////////////////////////////////////////////////////////////////////////////// +// TestEmptyStructureAdapter +////////////////////////////////////////////////////////////////////////////// + +class TestEmptyStructureAdapterFixture { + protected: + StructureAdapterPtr mstru; + + public: + TestEmptyStructureAdapterFixture() { mstru = emptyStructureAdapter(); } +}; // class TestEmptyStructureAdapterFixture + +TEST_CASE_METHOD(TestEmptyStructureAdapterFixture, + "TestEmptyStructureAdapter::test_empty_instance", + "[EmptyStructureAdapter]") { + CHECK(0 == mstru->countSites()); + CHECK(mstru.get() == mstru->clone().get()); + CHECK(mstru == emptyStructureAdapter()); + CHECK_THROWS_AS(mstru->siteAtomType(0), std::out_of_range); + CHECK_THROWS_AS(mstru->siteCartesianPosition(0), std::out_of_range); + CHECK_THROWS_AS(mstru->siteMultiplicity(0), std::out_of_range); + CHECK_THROWS_AS(mstru->siteOccupancy(0), std::out_of_range); + CHECK_THROWS_AS(mstru->siteAnisotropy(0), std::out_of_range); + CHECK_THROWS_AS(mstru->siteCartesianPosition(0), std::out_of_range); +} + +TEST_CASE_METHOD(TestEmptyStructureAdapterFixture, + "TestEmptyStructureAdapter::test_serialization", + "[EmptyStructureAdapter]") { + StructureAdapterPtr stru1; + stru1 = dumpandload(mstru); + AtomicStructureAdapterPtr astru1 = + std::dynamic_pointer_cast(stru1); + CHECK(!astru1); + CHECK(0 == stru1->countSites()); + CHECK(stru1 == stru1->clone()); + CHECK_THROWS_AS(mstru->siteAtomType(0), std::out_of_range); +} + +} // namespace srreal +} // namespace diffpy + +// End of file diff --git a/src/tests/TestEmptyStructureAdapter.hpp b/src/tests/TestEmptyStructureAdapter.hpp deleted file mode 100644 index 00388375..00000000 --- a/src/tests/TestEmptyStructureAdapter.hpp +++ /dev/null @@ -1,70 +0,0 @@ -/***************************************************************************** - * - * libdiffpy Complex Modeling Initiative - * (c) 2016 Brookhaven Science Associates, - * Brookhaven National Laboratory. - * All rights reserved. - * - * File coded by: Pavol Juhas - * - * See AUTHORS.rst for a list of people who contributed. - * See LICENSE.rst for license information. - * - ****************************************************************************** - * - * class TestEmptyStructureAdapter -- test adapter that is always empty - * - *****************************************************************************/ - -#include - -#include -#include "serialization_helpers.hpp" - -namespace diffpy { -namespace srreal { - -using namespace std; - -////////////////////////////////////////////////////////////////////////////// -// class TestEmptyStructureAdapter -////////////////////////////////////////////////////////////////////////////// - -class TestEmptyStructureAdapter : public CxxTest::TestSuite { - private: - StructureAdapterPtr mstru; - - public: - void setUp() { mstru = emptyStructureAdapter(); } - - void test_empty_instance() { - TS_ASSERT_EQUALS(0, mstru->countSites()); - TS_ASSERT_EQUALS(mstru.get(), mstru->clone().get()); - TS_ASSERT_EQUALS(mstru, emptyStructureAdapter()); - TS_ASSERT_THROWS(mstru->siteAtomType(0), std::out_of_range); - TS_ASSERT_THROWS(mstru->siteCartesianPosition(0), std::out_of_range); - TS_ASSERT_THROWS(mstru->siteMultiplicity(0), std::out_of_range); - TS_ASSERT_THROWS(mstru->siteOccupancy(0), std::out_of_range); - TS_ASSERT_THROWS(mstru->siteAnisotropy(0), std::out_of_range); - TS_ASSERT_THROWS(mstru->siteCartesianPosition(0), std::out_of_range); - } - - void test_serialization() { - StructureAdapterPtr stru1; - stru1 = dumpandload(mstru); - AtomicStructureAdapterPtr astru1 = - std::dynamic_pointer_cast(stru1); - TS_ASSERT(!astru1); - TS_ASSERT_EQUALS(0, stru1->countSites()); - TS_ASSERT_EQUALS(stru1, stru1->clone()); - TS_ASSERT_THROWS(mstru->siteAtomType(0), std::out_of_range); - } - -}; // class TestEmptyStructureAdapter - -} // namespace srreal -} // namespace diffpy - -using diffpy::srreal::TestEmptyStructureAdapter; - -// End of file diff --git a/src/tests/TestHasClassRegistry.cpp b/src/tests/TestHasClassRegistry.cpp new file mode 100644 index 00000000..7e9d32cf --- /dev/null +++ b/src/tests/TestHasClassRegistry.cpp @@ -0,0 +1,79 @@ +/***************************************************************************** + * + * libdiffpy Complex Modeling Initiative + * (c) 2016 Brookhaven Science Associates, + * Brookhaven National Laboratory. + * All rights reserved. + * + * File coded by: Pavol Juhas + * + * See AUTHORS.rst for a list of people who contributed. + * See LICENSE.rst for license information. + * + ****************************************************************************** + * + * TestHasClassRegistry -- test registration machinery for named classes. + * + *****************************************************************************/ + +#include + +#include + +using diffpy::srreal::ScatteringFactorTable; +using diffpy::srreal::ScatteringFactorTablePtr; + +////////////////////////////////////////////////////////////////////////////// +// TestHasClassRegistry +////////////////////////////////////////////////////////////////////////////// + +class TestHasClassRegistryFixture { + protected: + typedef diffpy::HasClassRegistry HCRBase; + ScatteringFactorTablePtr msftb; + HCRBase* mpreg; + + public: + TestHasClassRegistryFixture() { + msftb = ScatteringFactorTable::createByType("xray"); + mpreg = msftb.get(); + } + + ~TestHasClassRegistryFixture() { + // restore SFTXray registration if removed in some test. + msftb->deregisterType(msftb->type()); + msftb->registerThisType(); + CHECK(ScatteringFactorTable::isRegisteredType("X")); + CHECK(ScatteringFactorTable::isRegisteredType("xray")); + } +}; // class TestHasClassRegistryFixture + +TEST_CASE_METHOD(TestHasClassRegistryFixture, + "TestHasClassRegistry::test_deregisterType", + "[HasClassRegistry]") { + CHECK(0 == ScatteringFactorTable::deregisterType("invalid")); + CHECK(2 == ScatteringFactorTable::deregisterType("X")); + CHECK(!(mpreg->isRegisteredType("xray"))); + CHECK(!(mpreg->isRegisteredType("X"))); +} + +TEST_CASE_METHOD(TestHasClassRegistryFixture, + "TestHasClassRegistry::test_isRegisteredType", + "[HasClassRegistry]") { + CHECK(msftb->isRegisteredType("xray")); + CHECK(ScatteringFactorTable::isRegisteredType("X")); + CHECK(false == mpreg->isRegisteredType("invalid")); +} + +TEST_CASE_METHOD(TestHasClassRegistryFixture, + "TestHasClassRegistry::test_getAliasedTypes", + "[HasClassRegistry]") { + std::map atps; + atps = ScatteringFactorTable::getAliasedTypes(); + CHECK(4 == atps.size()); + CHECK(atps.count("X")); + CHECK("xray" == atps["X"]); + CHECK("electronnumber" == atps["EN"]); +} + +// End of file diff --git a/src/tests/TestHasClassRegistry.hpp b/src/tests/TestHasClassRegistry.hpp deleted file mode 100644 index 303479b5..00000000 --- a/src/tests/TestHasClassRegistry.hpp +++ /dev/null @@ -1,74 +0,0 @@ -/***************************************************************************** - * - * libdiffpy Complex Modeling Initiative - * (c) 2016 Brookhaven Science Associates, - * Brookhaven National Laboratory. - * All rights reserved. - * - * File coded by: Pavol Juhas - * - * See AUTHORS.rst for a list of people who contributed. - * See LICENSE.rst for license information. - * - ****************************************************************************** - * - * class TestHasClassRegistry -- test registration machinery for named classes. - * - *****************************************************************************/ - -#include - -#include - -using diffpy::srreal::ScatteringFactorTable; -using diffpy::srreal::ScatteringFactorTablePtr; - -////////////////////////////////////////////////////////////////////////////// -// class TestHasClassRegistry -////////////////////////////////////////////////////////////////////////////// - -class TestHasClassRegistry : public CxxTest::TestSuite { - private: - typedef diffpy::HasClassRegistry HCRBase; - ScatteringFactorTablePtr msftb; - HCRBase* mpreg; - - public: - void setUp() { - msftb = ScatteringFactorTable::createByType("xray"); - mpreg = msftb.get(); - } - - void tearDown() { - // restore SFTXray registration if removed in some test. - msftb->deregisterType(msftb->type()); - msftb->registerThisType(); - TS_ASSERT(ScatteringFactorTable::isRegisteredType("X")); - TS_ASSERT(ScatteringFactorTable::isRegisteredType("xray")); - } - - void test_deregisterType() { - TS_ASSERT_EQUALS(0, ScatteringFactorTable::deregisterType("invalid")); - TS_ASSERT_EQUALS(2, ScatteringFactorTable::deregisterType("X")); - TS_ASSERT(!(mpreg->isRegisteredType("xray"))); - TS_ASSERT(!(mpreg->isRegisteredType("X"))); - } - - void test_isRegisteredType() { - TS_ASSERT(msftb->isRegisteredType("xray")); - TS_ASSERT(ScatteringFactorTable::isRegisteredType("X")); - TS_ASSERT_EQUALS(false, mpreg->isRegisteredType("invalid")); - } - - void test_getAliasedTypes() { - std::map atps; - atps = ScatteringFactorTable::getAliasedTypes(); - TS_ASSERT_EQUALS(4, atps.size()); - TS_ASSERT(atps.count("X")); - TS_ASSERT_EQUALS("xray", atps["X"]); - TS_ASSERT_EQUALS("electronnumber", atps["EN"]); - } - -}; // class TestHasClassRegistry - -// End of file diff --git a/src/tests/TestLattice.cpp b/src/tests/TestLattice.cpp new file mode 100644 index 00000000..b3040317 --- /dev/null +++ b/src/tests/TestLattice.cpp @@ -0,0 +1,153 @@ +/***************************************************************************** + * + * libdiffpy by DANSE Diffraction group + * Simon J. L. Billinge + * (c) 2009 The Trustees of Columbia University + * in the City of New York. All rights reserved. + * + * File coded by: Pavol Juhas + * + * See AUTHORS.rst for a list of people who contributed. + * See LICENSE_DANSE.rst for license information. + * + ****************************************************************************** + * + * TestLattice -- unit tests for the Lattice class + * + *****************************************************************************/ + +#include +#include + +#include +#include + +using Catch::Matchers::WithinAbs; + +using namespace std; +using namespace diffpy::srreal; +using diffpy::mathutils::EpsilonEqual; + +class TestLatticeFixture { + protected: + Lattice* lattice; + double precision; + EpsilonEqual allclose; + + public: + TestLatticeFixture() { + lattice = new Lattice(); + precision = 1.0e-8; + allclose = EpsilonEqual(precision); + } + + ~TestLatticeFixture() { delete lattice; } +}; // class TestLatticeFixture + +TEST_CASE_METHOD(TestLatticeFixture, "TestLattice::test_Lattice", "[Lattice]") { + // default lattice should be cartesian + CHECK(allclose(lattice->base(), R3::identity())); + // lattice parameters constructor + Lattice lattice1(1.0, 2.0, 3.0, 90, 90, 120); + R3::Vector va = lattice1.va(); + R3::Vector vb = lattice1.vb(); + R3::Vector vc = lattice1.vc(); + double adotb = R3::dot(va, vb); + double adotc = R3::dot(va, vc); + double bdotc = R3::dot(vb, vc); + CHECK_THAT(1.0, WithinAbs(R3::norm(va), precision)); + CHECK_THAT(2.0, WithinAbs(R3::norm(vb), precision)); + CHECK_THAT(3.0, WithinAbs(R3::norm(vc), precision)); + CHECK_THAT(-0.5 * 1.0 * 2.0, WithinAbs(adotb, precision)); + CHECK_THAT(0.0, WithinAbs(adotc, precision)); + CHECK_THAT(0.0, WithinAbs(bdotc, precision)); + CHECK_THAT(sqrt(4.0 / 3), WithinAbs(lattice1.ar(), precision)); + CHECK_THAT(sqrt(1.0 / 3), WithinAbs(lattice1.br(), precision)); + CHECK_THAT(1.0 / 3, WithinAbs(lattice1.cr(), precision)); + CHECK_THAT(90.0, WithinAbs(lattice1.alphar(), precision)); + CHECK_THAT(90.0, WithinAbs(lattice1.betar(), precision)); + CHECK_THAT(60.0, WithinAbs(lattice1.gammar(), precision)); + // lattice vectors constructor + va = R3::Vector(1.0, 1.0, 0.0); + vb = R3::Vector(0.0, 1.0, 1.0); + vc = R3::Vector(1.0, 0.0, 1.0); + Lattice lattice2(va, vb, vc); + CHECK_THAT(sqrt(2.0), WithinAbs(lattice2.a(), precision)); + CHECK_THAT(60.0, WithinAbs(lattice2.alpha(), precision)); +} + +TEST_CASE_METHOD(TestLatticeFixture, "TestLattice::test_setLatPar", + "[Lattice]") { + lattice->setLatPar(1.0, 2.0, 3.0, 90, 90, 120); + R3::Matrix base_check(sqrt(0.75), -0.5, 0.0, 0.0, +2.0, 0.0, 0.0, +0.0, 3.0); + CHECK(allclose(base_check, lattice->base())); + R3::Matrix recbase_check(sqrt(4.0 / 3), sqrt(1.0 / 12), 0.0, 0.0, 0.5, 0.0, + 0.0, 0.0, 1.0 / 3.0); + CHECK(allclose(recbase_check, lattice->recbase())); +} + +TEST_CASE_METHOD(TestLatticeFixture, "TestLattice::test_setLatBase", + "[Lattice]") { + R3::Vector va(1.0, 1.0, 0.0); + R3::Vector vb(0.0, 1.0, 1.0); + R3::Vector vc(1.0, 0.0, 1.0); + lattice->setLatBase(va, vb, vc); + CHECK_THAT(sqrt(2.0), WithinAbs(lattice->a(), precision)); + CHECK_THAT(sqrt(2.0), WithinAbs(lattice->b(), precision)); + CHECK_THAT(sqrt(2.0), WithinAbs(lattice->c(), precision)); + CHECK_THAT(60.0, WithinAbs(lattice->alpha(), precision)); + CHECK_THAT(60.0, WithinAbs(lattice->beta(), precision)); + CHECK_THAT(60.0, WithinAbs(lattice->gamma(), precision)); + // check determinant of rotation matrix + double detRot0 = R3::determinant(lattice->baserot()); + CHECK_THAT(1.0, WithinAbs(detRot0, precision)); + // check if rotation matrix works + R3::Matrix base_check(va[0], va[1], va[2], vb[0], vb[1], vb[2], vc[0], vc[1], + vc[2]); + CHECK(allclose(base_check, lattice->base())); + R3::Matrix recbase_check(0.5, -0.5, 0.5, 0.5, 0.5, -0.5, -0.5, 0.5, 0.5); + CHECK(allclose(recbase_check, lattice->recbase())); + lattice->setLatPar(lattice->a(), lattice->b(), lattice->c(), 44.0, 66.0, + 88.0); + CHECK(!allclose(base_check, lattice->base())); + CHECK(!allclose(recbase_check, lattice->recbase())); + lattice->setLatPar(lattice->a(), lattice->b(), lattice->c(), 60.0, 60.0, + 60.0); + CHECK(allclose(base_check, lattice->base())); + CHECK(allclose(recbase_check, lattice->recbase())); +} + +TEST_CASE_METHOD(TestLatticeFixture, "TestLattice::test_dist", "[Lattice]") { + R3::Vector va(1.0, 2.0, 2.0); + R3::Vector vb(0.0, 0.0, 0.0); + CHECK_THAT(3.0, WithinAbs(lattice->distance(va, vb), precision)); + lattice->setLatPar(2.0, 2.0, 2.0, 90.0, 90.0, 90.0); + CHECK_THAT(6.0, WithinAbs(lattice->distance(va, vb), precision)); +} + +TEST_CASE_METHOD(TestLatticeFixture, "TestLattice::test_angle", "[Lattice]") { + R3::Vector va(1.0, 0.0, 0.0); + R3::Vector vb(0.0, 1.0, 0.0); + CHECK_THAT(90.0, WithinAbs(lattice->angledeg(va, vb), precision)); + lattice->setLatPar(2.0, 2.0, 2.0, 90.0, 90.0, 120.0); + CHECK_THAT(120.0, WithinAbs(lattice->angledeg(va, vb), precision)); +} + +TEST_CASE_METHOD(TestLatticeFixture, "TestLattice::test_ucvCartesian", + "[Lattice]") { + R3::Vector ucv; + ucv = lattice->ucvCartesian(R3::Vector(0.1, 0.2, 0.3)); + R3::Vector ucv_check(0.1, 0.2, 0.3); + CHECK(allclose(ucv_check, ucv)); + ucv = lattice->ucvCartesian(R3::Vector(1.1, 13.2, -0.7)); + CHECK(allclose(ucv_check, ucv)); + ucv = lattice->ucvCartesian(R3::Vector(0.5, 0.5, 0.5)); + ucv_check = R3::Vector(0.5, 0.5, 0.5); + CHECK(allclose(ucv_check, ucv)); + lattice->setLatPar(13, 17, 19, 37, 41, 47); + ucv = lattice->ucvCartesian(R3::Vector(100.0, 100.0, 100.0)); + ucv_check = R3::Vector(8.09338442077, 9.55056747225, 30.0389043325); + CHECK(allclose(ucv_check, ucv)); +} + +// End of file diff --git a/src/tests/TestLattice.hpp b/src/tests/TestLattice.hpp deleted file mode 100644 index a252f5d2..00000000 --- a/src/tests/TestLattice.hpp +++ /dev/null @@ -1,149 +0,0 @@ -/***************************************************************************** - * - * libdiffpy by DANSE Diffraction group - * Simon J. L. Billinge - * (c) 2009 The Trustees of Columbia University - * in the City of New York. All rights reserved. - * - * File coded by: Pavol Juhas - * - * See AUTHORS.rst for a list of people who contributed. - * See LICENSE_DANSE.rst for license information. - * - ****************************************************************************** - * - * class TestLattice -- unit tests for the Lattice class - * - *****************************************************************************/ - -#include - -#include -#include - -using namespace std; -using namespace diffpy::srreal; -using diffpy::mathutils::EpsilonEqual; - -class TestLattice : public CxxTest::TestSuite { - private: - Lattice* lattice; - double precision; - EpsilonEqual allclose; - - public: - void setUp() { - lattice = new Lattice(); - precision = 1.0e-8; - allclose = EpsilonEqual(precision); - } - - void tearDown() { delete lattice; } - - void test_Lattice() { - // default lattice should be cartesian - TS_ASSERT(allclose(lattice->base(), R3::identity())); - // lattice parameters constructor - Lattice lattice1(1.0, 2.0, 3.0, 90, 90, 120); - R3::Vector va = lattice1.va(); - R3::Vector vb = lattice1.vb(); - R3::Vector vc = lattice1.vc(); - double adotb = R3::dot(va, vb); - double adotc = R3::dot(va, vc); - double bdotc = R3::dot(vb, vc); - TS_ASSERT_DELTA(1.0, R3::norm(va), precision); - TS_ASSERT_DELTA(2.0, R3::norm(vb), precision); - TS_ASSERT_DELTA(3.0, R3::norm(vc), precision); - TS_ASSERT_DELTA(-0.5 * 1.0 * 2.0, adotb, precision); - TS_ASSERT_DELTA(0.0, adotc, precision); - TS_ASSERT_DELTA(0.0, bdotc, precision); - TS_ASSERT_DELTA(sqrt(4.0 / 3), lattice1.ar(), precision); - TS_ASSERT_DELTA(sqrt(1.0 / 3), lattice1.br(), precision); - TS_ASSERT_DELTA(1.0 / 3, lattice1.cr(), precision); - TS_ASSERT_DELTA(90.0, lattice1.alphar(), precision); - TS_ASSERT_DELTA(90.0, lattice1.betar(), precision); - TS_ASSERT_DELTA(60.0, lattice1.gammar(), precision); - // lattice vectors constructor - va = R3::Vector(1.0, 1.0, 0.0); - vb = R3::Vector(0.0, 1.0, 1.0); - vc = R3::Vector(1.0, 0.0, 1.0); - Lattice lattice2(va, vb, vc); - TS_ASSERT_DELTA(sqrt(2.0), lattice2.a(), precision); - TS_ASSERT_DELTA(60.0, lattice2.alpha(), precision); - } - - void test_setLatPar() { - lattice->setLatPar(1.0, 2.0, 3.0, 90, 90, 120); - R3::Matrix base_check(sqrt(0.75), -0.5, 0.0, 0.0, +2.0, 0.0, 0.0, +0.0, - 3.0); - TS_ASSERT(allclose(base_check, lattice->base())); - R3::Matrix recbase_check(sqrt(4.0 / 3), sqrt(1.0 / 12), 0.0, 0.0, 0.5, 0.0, - 0.0, 0.0, 1.0 / 3.0); - TS_ASSERT(allclose(recbase_check, lattice->recbase())); - } - - void test_setLatBase() { - R3::Vector va(1.0, 1.0, 0.0); - R3::Vector vb(0.0, 1.0, 1.0); - R3::Vector vc(1.0, 0.0, 1.0); - lattice->setLatBase(va, vb, vc); - TS_ASSERT_DELTA(sqrt(2.0), lattice->a(), precision); - TS_ASSERT_DELTA(sqrt(2.0), lattice->b(), precision); - TS_ASSERT_DELTA(sqrt(2.0), lattice->c(), precision); - TS_ASSERT_DELTA(60.0, lattice->alpha(), precision); - TS_ASSERT_DELTA(60.0, lattice->beta(), precision); - TS_ASSERT_DELTA(60.0, lattice->gamma(), precision); - // check determinant of rotation matrix - double detRot0 = R3::determinant(lattice->baserot()); - TS_ASSERT_DELTA(1.0, detRot0, precision); - // check if rotation matrix works - R3::Matrix base_check(va[0], va[1], va[2], vb[0], vb[1], vb[2], vc[0], - vc[1], vc[2]); - TS_ASSERT(allclose(base_check, lattice->base())); - R3::Matrix recbase_check(0.5, -0.5, 0.5, 0.5, 0.5, -0.5, -0.5, 0.5, 0.5); - TS_ASSERT(allclose(recbase_check, lattice->recbase())); - lattice->setLatPar(lattice->a(), lattice->b(), lattice->c(), 44.0, 66.0, - 88.0); - TS_ASSERT(!allclose(base_check, lattice->base())); - TS_ASSERT(!allclose(recbase_check, lattice->recbase())); - lattice->setLatPar(lattice->a(), lattice->b(), lattice->c(), 60.0, 60.0, - 60.0); - TS_ASSERT(allclose(base_check, lattice->base())); - TS_ASSERT(allclose(recbase_check, lattice->recbase())); - } - - void test_dist() { - R3::Vector va(1.0, 2.0, 2.0); - R3::Vector vb(0.0, 0.0, 0.0); - TS_ASSERT_DELTA(3.0, lattice->distance(va, vb), precision); - lattice->setLatPar(2.0, 2.0, 2.0, 90.0, 90.0, 90.0); - TS_ASSERT_DELTA(6.0, lattice->distance(va, vb), precision); - } - - void test_angle() { - R3::Vector va(1.0, 0.0, 0.0); - R3::Vector vb(0.0, 1.0, 0.0); - TS_ASSERT_DELTA(90.0, lattice->angledeg(va, vb), precision); - lattice->setLatPar(2.0, 2.0, 2.0, 90.0, 90.0, 120.0); - TS_ASSERT_DELTA(120.0, lattice->angledeg(va, vb), precision); - } - - void test_ucvCartesian() { - R3::Vector ucv; - ucv = lattice->ucvCartesian(R3::Vector(0.1, 0.2, 0.3)); - R3::Vector ucv_check(0.1, 0.2, 0.3); - TS_ASSERT(allclose(ucv_check, ucv)); - ucv = lattice->ucvCartesian(R3::Vector(1.1, 13.2, -0.7)); - TS_ASSERT(allclose(ucv_check, ucv)); - ucv = lattice->ucvCartesian(R3::Vector(0.5, 0.5, 0.5)); - ucv_check = R3::Vector(0.5, 0.5, 0.5); - TS_ASSERT(allclose(ucv_check, ucv)); - lattice->setLatPar(13, 17, 19, 37, 41, 47); - ucv = lattice->ucvCartesian(R3::Vector(100.0, 100.0, 100.0)); - ucv_check = R3::Vector(8.09338442077, 9.55056747225, 30.0389043325); - TS_ASSERT(allclose(ucv_check, ucv)); - } - -}; // class TestLattice - -// End of file diff --git a/src/tests/TestNoMetaStructureAdapter.cpp b/src/tests/TestNoMetaStructureAdapter.cpp new file mode 100644 index 00000000..3b658614 --- /dev/null +++ b/src/tests/TestNoMetaStructureAdapter.cpp @@ -0,0 +1,103 @@ +/***************************************************************************** + * + * libdiffpy by DANSE Diffraction group + * Simon J. L. Billinge + * (c) 2010 The Trustees of Columbia University + * in the City of New York. All rights reserved. + * + * File coded by: Pavol Juhas + * + * See AUTHORS.rst for a list of people who contributed. + * See LICENSE_DANSE.rst for license information. + * + ****************************************************************************** + * + * TestNoMetaStructureAdapter -- unit tests for an adapter that + * avoids metadata forwarding from the diffpy.Structure classes. + * + *****************************************************************************/ + +#include +#include +#include + +#include +#include +#include +#include "test_helpers.hpp" + +using namespace std; +using namespace diffpy::srreal; + +////////////////////////////////////////////////////////////////////////////// +// TestNoMetaStructureAdapter +////////////////////////////////////////////////////////////////////////////// + +class TestNoMetaStructureAdapterFixture { + protected: + StructureAdapterPtr m_pswt; + + public: + TestNoMetaStructureAdapterFixture() { + m_pswt = loadTestPeriodicStructure("PbScW25TiO3.stru"); + } +}; // class TestNoMetaStructureAdapterFixture + +TEST_CASE_METHOD( + TestNoMetaStructureAdapterFixture, + "TestNoMetaStructureAdapter::test_PDFCalcPreservesConfiguration", + "[NoMetaStructureAdapter]") { + PDFCalculator pdfc; + pdfc.setRstep(0.1); + pdfc.setRmax(5.0); + pdfc.setDoubleAttr("scale", 2.5); + pdfc.setStructure(m_pswt); + CHECK(2.5 == pdfc.getDoubleAttr("scale")); + pdfc.eval(m_pswt); + CHECK(2.5 == pdfc.getDoubleAttr("scale")); +} + +TEST_CASE_METHOD(TestNoMetaStructureAdapterFixture, + "TestNoMetaStructureAdapter::test_PDFCalcNoMeta", + "[NoMetaStructureAdapter]") { + PDFCalculator pdfc; + pdfc.setRstep(0.1); + pdfc.setRmax(5.0); + CHECK(1.0 == pdfc.getDoubleAttr("scale")); + pdfc.eval(nometa(m_pswt)); + CHECK(1.0 == pdfc.getDoubleAttr("scale")); +} + +TEST_CASE_METHOD(TestNoMetaStructureAdapterFixture, + "TestNoMetaStructureAdapter::test_NoMetaTwice", + "[NoMetaStructureAdapter]") { + StructureAdapterPtr adpt0 = nometa(m_pswt); + StructureAdapterPtr adpt1 = nometa(adpt0); + CHECK(m_pswt == adpt0); + CHECK(adpt0 == adpt1); + CHECK(emptyStructureAdapter() == nometa(StructureAdapterPtr())); + StructureAdapterPtr proxy(new NoMetaStructureAdapter(m_pswt)); + CHECK(proxy == nometa(proxy)); +} + +TEST_CASE_METHOD(TestNoMetaStructureAdapterFixture, + "TestNoMetaStructureAdapter::test_serialization", + "[NoMetaStructureAdapter]") { + using namespace diffpy::serialization; + stringstream storage(ios::in | ios::out | ios::binary); + oarchive oa(storage, ios::binary); + StructureAdapterPtr pswtbare0(new NoMetaStructureAdapter(m_pswt)); + oa << pswtbare0; + iarchive ia(storage, ios::binary); + StructureAdapterPtr pswtbare1; + CHECK(!pswtbare1.get()); + ia >> pswtbare1; + CHECK(m_pswt.get() != pswtbare1.get()); + CHECK(56 == pswtbare1->countSites()); + CHECK(string("Pb") == pswtbare1->siteAtomType(0)); + CHECK(string("Ti") == pswtbare1->siteAtomType(55)); + StructureAdapter& r_pswtbare1 = *pswtbare1; + CHECK(typeid(NoMetaStructureAdapter) == typeid(r_pswtbare1)); +} + +// End of file diff --git a/src/tests/TestNoMetaStructureAdapter.hpp b/src/tests/TestNoMetaStructureAdapter.hpp deleted file mode 100644 index 41fc447d..00000000 --- a/src/tests/TestNoMetaStructureAdapter.hpp +++ /dev/null @@ -1,99 +0,0 @@ -/***************************************************************************** - * - * libdiffpy by DANSE Diffraction group - * Simon J. L. Billinge - * (c) 2010 The Trustees of Columbia University - * in the City of New York. All rights reserved. - * - * File coded by: Pavol Juhas - * - * See AUTHORS.rst for a list of people who contributed. - * See LICENSE_DANSE.rst for license information. - * - ****************************************************************************** - * - * class TestNoMetaStructureAdapter -- unit tests for an adapter that - * avoids metadata forwarding from the diffpy.Structure classes. - * - *****************************************************************************/ - -#include -#include -#include - -#include -#include -#include -#include "test_helpers.hpp" - -using namespace std; -using namespace diffpy::srreal; - -////////////////////////////////////////////////////////////////////////////// -// class TestNoMetaStructureAdapter -////////////////////////////////////////////////////////////////////////////// - -class TestNoMetaStructureAdapter : public CxxTest::TestSuite { - private: - StructureAdapterPtr m_pswt; - - public: - void setUp() { - CxxTest::setAbortTestOnFail(true); - if (!m_pswt) { - m_pswt = loadTestPeriodicStructure("PbScW25TiO3.stru"); - } - CxxTest::setAbortTestOnFail(false); - } - - void test_PDFCalcPreservesConfiguration() { - PDFCalculator pdfc; - pdfc.setRstep(0.1); - pdfc.setRmax(5.0); - pdfc.setDoubleAttr("scale", 2.5); - pdfc.setStructure(m_pswt); - TS_ASSERT_EQUALS(2.5, pdfc.getDoubleAttr("scale")); - pdfc.eval(m_pswt); - TS_ASSERT_EQUALS(2.5, pdfc.getDoubleAttr("scale")); - } - - void test_PDFCalcNoMeta() { - PDFCalculator pdfc; - pdfc.setRstep(0.1); - pdfc.setRmax(5.0); - TS_ASSERT_EQUALS(1.0, pdfc.getDoubleAttr("scale")); - pdfc.eval(nometa(m_pswt)); - TS_ASSERT_EQUALS(1.0, pdfc.getDoubleAttr("scale")); - } - - void test_NoMetaTwice() { - StructureAdapterPtr adpt0 = nometa(m_pswt); - StructureAdapterPtr adpt1 = nometa(adpt0); - TS_ASSERT_EQUALS(m_pswt, adpt0); - TS_ASSERT_EQUALS(adpt0, adpt1); - TS_ASSERT_EQUALS(emptyStructureAdapter(), nometa(StructureAdapterPtr())); - StructureAdapterPtr proxy(new NoMetaStructureAdapter(m_pswt)); - TS_ASSERT_EQUALS(proxy, nometa(proxy)); - } - - void test_serialization() { - using namespace diffpy::serialization; - stringstream storage(ios::in | ios::out | ios::binary); - oarchive oa(storage, ios::binary); - StructureAdapterPtr pswtbare0(new NoMetaStructureAdapter(m_pswt)); - oa << pswtbare0; - iarchive ia(storage, ios::binary); - StructureAdapterPtr pswtbare1; - TS_ASSERT(!pswtbare1.get()); - ia >> pswtbare1; - TS_ASSERT_DIFFERS(m_pswt.get(), pswtbare1.get()); - TS_ASSERT_EQUALS(56, pswtbare1->countSites()); - TS_ASSERT_EQUALS(string("Pb"), pswtbare1->siteAtomType(0)); - TS_ASSERT_EQUALS(string("Ti"), pswtbare1->siteAtomType(55)); - StructureAdapter& r_pswtbare1 = *pswtbare1; - TS_ASSERT(typeid(NoMetaStructureAdapter) == typeid(r_pswtbare1)); - } - -}; // class TestNoMetaStructureAdapter - -// End of file diff --git a/src/tests/TestObjCrystStructureAdapter.cpp b/src/tests/TestObjCrystStructureAdapter.cpp new file mode 100644 index 00000000..05c4d828 --- /dev/null +++ b/src/tests/TestObjCrystStructureAdapter.cpp @@ -0,0 +1,637 @@ +/***************************************************************************** + * + * libdiffpy by DANSE Diffraction group + * Simon J. L. Billinge + * (c) 2009 The Trustees of Columbia University + * in the City of New York. All rights reserved. + * + * File coded by: Pavol Juhas + * + * See AUTHORS.rst for a list of people who contributed. + * See LICENSE_DANSE.rst for license information. + * + ****************************************************************************** + * + * TestObjCrystStructureAdapter -- unit tests for an adapter + * to the ObjCryst::Crystal class + * + * TestObjCrystStructureBondGenerator -- unit tests for bond generator + * + *****************************************************************************/ + +#include +#include +#include + +#include +#include +#include +#include "serialization_helpers.hpp" +#include "objcryst_helpers.hpp" +#include +#include +#include + +using Catch::Matchers::WithinAbs; + +using namespace std; +using namespace diffpy::srreal; +using ObjCryst::Crystal; +using ObjCryst::Molecule; +using ObjCryst::ScatteringPowerAtom; + +// Local Helpers ------------------------------------------------------------- + +namespace { + +int countBonds(diffpy::srreal::BaseBondGenerator& bnds) { + int rv = 0; + for (bnds.rewind(); !bnds.finished(); bnds.next()) ++rv; + return rv; +} + +template +double testmsd0(const Tstru& stru, const Tbnds& bnds) { + bool anisotropy0 = stru->siteAnisotropy(bnds->site0()); + double rv = + meanSquareDisplacement(bnds->Ucartesian0(), bnds->r01(), anisotropy0); + return rv; +} + +template +double testmsd1(const Tstru& stru, const Tbnds& bnds) { + bool anisotropy1 = stru->siteAnisotropy(bnds->site1()); + double rv = + meanSquareDisplacement(bnds->Ucartesian1(), bnds->r01(), anisotropy1); + return rv; +} + +double diffdegree(StructureAdapterPtr stru0, StructureAdapterPtr stru1) { + StructureDifference sd = stru0->diff(stru1); + double tot = 0.0; + if (stru0) tot += stru0->countSites(); + if (stru1) tot += stru1->countSites(); + double rv = (sd.pop0.size() + sd.add1.size()) / (tot ? tot : 1.0); + return rv; +} + +// Defined below +Molecule* makeC60Molecule(); + +} // namespace + +////////////////////////////////////////////////////////////////////////////// +// TestObjCrystStructureAdapter +////////////////////////////////////////////////////////////////////////////// + +class TestObjCrystStructureAdapterFixture { + protected: + StructureAdapterPtr m_ni = + createStructureAdapter(cachedTestCrystal("Ni.cif")); + StructureAdapterPtr m_kbise = + createStructureAdapter(cachedTestCrystal("alpha_K2Bi8Se13.cif")); + StructureAdapterPtr m_catio3 = + createStructureAdapter(cachedTestCrystal("CaTiO3.cif")); +}; // class TestObjCrystStructureAdapterFixture + +TEST_CASE_METHOD(TestObjCrystStructureAdapterFixture, + "TestObjCrystStructureAdapter::test_typeid", + "[ObjCrystStructureAdapter]") { + StructureAdapter& r_ni = *m_ni; + CHECK(typeid(CrystalStructureAdapter) == typeid(r_ni)); +} + +TEST_CASE_METHOD(TestObjCrystStructureAdapterFixture, + "TestObjCrystStructureAdapter::test_countSites", + "[ObjCrystStructureAdapter]") { + CHECK(1 == m_ni->countSites()); + CHECK(12 == m_kbise->countSites()); + CHECK(4 == m_catio3->countSites()); +} + +TEST_CASE_METHOD(TestObjCrystStructureAdapterFixture, + "TestObjCrystStructureAdapter::test_totalOccupancy", + "[ObjCrystStructureAdapter]") { + CHECK(4.0 == m_ni->totalOccupancy()); + CHECK(23.0 == m_kbise->totalOccupancy()); + CHECK(20.0 == m_catio3->totalOccupancy()); +} + +TEST_CASE_METHOD(TestObjCrystStructureAdapterFixture, + "TestObjCrystStructureAdapter::test_numberDensity", + "[ObjCrystStructureAdapter]") { + const double eps = 1.0e-7; + CHECK_THAT(0.0914114, WithinAbs(m_ni->numberDensity(), eps)); + CHECK_THAT(0.0335565, WithinAbs(m_kbise->numberDensity(), eps)); + CHECK_THAT(0.0894566, WithinAbs(m_catio3->numberDensity(), eps)); +} + +TEST_CASE_METHOD(TestObjCrystStructureAdapterFixture, + "TestObjCrystStructureAdapter::test_siteCartesianPosition", + "[ObjCrystStructureAdapter]") { + const double eps = 1.0e-5; + R3::Vector rCa = m_catio3->siteCartesianPosition(1); + CHECK_THAT(0.03486, WithinAbs(rCa[0], eps)); + CHECK_THAT(0.19366, WithinAbs(rCa[1], eps)); + CHECK_THAT(1.90975, WithinAbs(rCa[2], eps)); +} + +TEST_CASE_METHOD(TestObjCrystStructureAdapterFixture, + "TestObjCrystStructureAdapter::test_siteAnisotropy", + "[ObjCrystStructureAdapter]") { + for (int i = 0; i < m_ni->countSites(); ++i) { + CHECK(false == m_ni->siteAnisotropy(i)); + } + for (int i = 0; i < m_catio3->countSites(); ++i) { + CHECK(true == m_catio3->siteAnisotropy(i)); + } +} + +TEST_CASE_METHOD(TestObjCrystStructureAdapterFixture, + "TestObjCrystStructureAdapter::test_siteCartesianUij", + "[ObjCrystStructureAdapter]") { + // nickel should have all Uij equal zero. + const double* puij = &(m_ni->siteCartesianUij(0).data()[0]); + for (int i = 0; i < 9; ++i) { + CHECK(0.0 == puij[i]); + } + // check CaTiO3 values + const R3::Matrix& UO2 = m_catio3->siteCartesianUij(3); + const double eps = 1e-8; + CHECK_THAT(0.0065, WithinAbs(UO2(0, 0), eps)); + CHECK_THAT(0.0060, WithinAbs(UO2(1, 1), eps)); + CHECK_THAT(0.0095, WithinAbs(UO2(2, 2), eps)); + CHECK_THAT(0.0020, WithinAbs(UO2(0, 1), eps)); + CHECK_THAT(0.0020, WithinAbs(UO2(1, 0), eps)); + CHECK_THAT(-0.0008, WithinAbs(UO2(0, 2), eps)); + CHECK_THAT(-0.0008, WithinAbs(UO2(2, 0), eps)); + CHECK_THAT(-0.0010, WithinAbs(UO2(1, 2), eps)); + CHECK_THAT(-0.0010, WithinAbs(UO2(2, 1), eps)); +} + +TEST_CASE_METHOD(TestObjCrystStructureAdapterFixture, + "TestObjCrystStructureAdapter::test_siteAtomType", + "[ObjCrystStructureAdapter]") { + CHECK(string("Ni") == m_ni->siteAtomType(0)); + CHECK(string("K1+") == m_kbise->siteAtomType(0)); + CHECK(string("Bi3+") == m_kbise->siteAtomType(1)); + CHECK(string("Se") == m_kbise->siteAtomType(5)); + CHECK(string("Se") == m_kbise->siteAtomType(11)); +} + +TEST_CASE_METHOD(TestObjCrystStructureAdapterFixture, + "TestObjCrystStructureAdapter::test_diff", + "[ObjCrystStructureAdapter]") { + StructureAdapterPtr acto = m_catio3; + CrystalStructureAdapterPtr ccto( + std::dynamic_pointer_cast(acto)); + StructureAdapterPtr acto2 = acto->clone(); + CrystalStructureAdapterPtr ccto2( + std::dynamic_pointer_cast(acto2)); + CHECK(0.0 == diffdegree(acto, acto)); + CHECK(1.0 == diffdegree(acto, StructureAdapterPtr())); + CHECK(0.0 == diffdegree(acto, acto2)); + CHECK(0.0 == diffdegree(acto, ccto2)); + const double symprec = ccto2->getSymmetryPrecision(); + ccto2->setSymmetryPrecision(0.010723); + CHECK(1.0 == diffdegree(acto, acto2)); + ccto2->setSymmetryPrecision(symprec); + CHECK(0.0 == diffdegree(acto, acto2)); + ccto2->clearSymOps(); + CHECK(1.0 == diffdegree(acto, acto2)); +} + +TEST_CASE_METHOD(TestObjCrystStructureAdapterFixture, + "TestObjCrystStructureAdapter::test_getLattice", + "[ObjCrystStructureAdapter]") { + CrystalStructureAdapter* pkbise = + dynamic_cast(m_kbise.get()); + CHECK(pkbise); + const Lattice& L = pkbise->getLattice(); + const double eps = 1.0e-12; + CHECK_THAT(13.768, WithinAbs(L.a(), eps)); + CHECK_THAT(12.096, WithinAbs(L.b(), eps)); + CHECK_THAT(4.1656, WithinAbs(L.c(), eps)); + CHECK_THAT(89.98, WithinAbs(L.alpha(), eps)); + CHECK_THAT(98.64, WithinAbs(L.beta(), eps)); + CHECK_THAT(87.96, WithinAbs(L.gamma(), eps)); +} + +TEST_CASE_METHOD(TestObjCrystStructureAdapterFixture, + "TestObjCrystStructureAdapter::test_getEquivalentAtoms", + "[ObjCrystStructureAdapter]") { + diffpy::mathutils::EpsilonEqual allclose; + unique_ptr crst(loadTestCrystal("NH4Br.cif")); + StructureAdapterPtr adpt = createStructureAdapter(*crst); + CrystalStructureAdapterPtr cadpt = + std::dynamic_pointer_cast(adpt); + CHECK(2 == cadpt->countSites()); + CHECK(4 == cadpt->totalOccupancy()); + CrystalStructureAdapter::AtomVector eq0, eq1; + eq0 = cadpt->getEquivalentAtoms(0); + CHECK(2 == eq0.size()); + cadpt->toFractional(eq0[0]); + cadpt->toFractional(eq0[1]); + CHECK(allclose(R3::Vector(0, 0.5, 0.03), eq0[0].xyz_cartn)); + CHECK(allclose(R3::Vector(0.5, 0, 0.97), eq0[1].xyz_cartn)); + eq1 = cadpt->getEquivalentAtoms(1); + CHECK(2 == eq1.size()); + cadpt->toFractional(eq1[0]); + cadpt->toFractional(eq1[1]); + CHECK(allclose(R3::Vector(0, 0, 0.5), eq1[0].xyz_cartn)); + CHECK(allclose(R3::Vector(0.5, 0.5, 0.5), eq1[1].xyz_cartn)); +} + +TEST_CASE_METHOD(TestObjCrystStructureAdapterFixture, + "TestObjCrystStructureAdapter::test_serialization", + "[ObjCrystStructureAdapter]") { + stringstream storage(ios::in | ios::out | ios::binary); + diffpy::serialization::oarchive oa(storage, ios::binary); + oa << m_kbise; + diffpy::serialization::iarchive ia(storage, ios::binary); + StructureAdapterPtr kbise1; + CHECK(!kbise1.get()); + ia >> kbise1; + CHECK(m_kbise.get() != kbise1.get()); + const double eps = 1.0e-7; + CHECK(12 == kbise1->countSites()); + CHECK(23.0 == kbise1->totalOccupancy()); + CHECK_THAT(0.0335565, WithinAbs(kbise1->numberDensity(), eps)); + CHECK(string("K1+") == kbise1->siteAtomType(0)); + CHECK(string("Bi3+") == kbise1->siteAtomType(1)); + CHECK(string("Se") == kbise1->siteAtomType(5)); + CHECK(string("Se") == kbise1->siteAtomType(11)); + CrystalStructureAdapter* pkbise = + dynamic_cast(m_kbise.get()); + CrystalStructureAdapter* pkbise1 = + dynamic_cast(kbise1.get()); + const Lattice& L = pkbise->getLattice(); + const Lattice& L1 = pkbise1->getLattice(); + CHECK(L.a() == L1.a()); + CHECK(L.b() == L1.b()); + CHECK(L.c() == L1.c()); + CHECK(L.alpha() == L1.alpha()); + CHECK(L.beta() == L1.beta()); + CHECK(L.gamma() == L1.gamma()); +} + +////////////////////////////////////////////////////////////////////////////// +// TestObjCrystStructureBondGenerator +////////////////////////////////////////////////////////////////////////////// + +class TestObjCrystStructureBondGeneratorFixture { + protected: + StructureAdapterPtr m_ni = + createStructureAdapter(cachedTestCrystal("Ni.cif")); + BaseBondGeneratorPtr m_nibnds = m_ni->createBondGenerator(); +}; // class TestObjCrystStructureBondGeneratorFixture + +TEST_CASE_METHOD(TestObjCrystStructureBondGeneratorFixture, + "TestObjCrystStructureBondGenerator::test_typeid", + "[ObjCrystStructureBondGenerator]") { + BaseBondGenerator& r_nibnds = *m_nibnds; + CHECK(typeid(CrystalStructureBondGenerator) == typeid(r_nibnds)); +} + +TEST_CASE_METHOD(TestObjCrystStructureBondGeneratorFixture, + "TestObjCrystStructureBondGenerator::test_bondCountNickel", + "[ObjCrystStructureBondGenerator]") { + m_nibnds->selectAnchorSite(0); + m_nibnds->setRmin(0); + m_nibnds->setRmax(1.0); + CHECK(0 == countBonds(*m_nibnds)); + m_nibnds->setRmin(-10); + CHECK(0 == countBonds(*m_nibnds)); + // there are 12 nearest neighbors at 2.49 + m_nibnds->setRmax(3); + CHECK(12 == countBonds(*m_nibnds)); + // there are no bonds between 2.6 and 3.4 + m_nibnds->setRmin(2.6); + m_nibnds->setRmax(3.4); + CHECK(0 == countBonds(*m_nibnds)); + // there are 6 second nearest neighbors at 3.52 + m_nibnds->setRmax(3.6); + CHECK(6 == countBonds(*m_nibnds)); + // which sums to 18 neighbors within radius 3.6 + m_nibnds->setRmin(0); + CHECK(18 == countBonds(*m_nibnds)); +} + +TEST_CASE_METHOD(TestObjCrystStructureBondGeneratorFixture, + "TestObjCrystStructureBondGenerator::test_bondCountWurtzite", + "[ObjCrystStructureBondGenerator]") { + unique_ptr wurtzite(loadTestCrystal("ZnS_wurtzite.cif")); + StructureAdapterPtr stru = createStructureAdapter(*wurtzite); + BaseBondGeneratorPtr bnds = stru->createBondGenerator(); + CHECK(2 == stru->countSites()); + bnds->selectAnchorSite(0); + bnds->selectSiteRange(0, 2); + bnds->setRmin(0); + // there should be no bond below the ZnS distance 2.31 + bnds->setRmax(2.2); + CHECK(0 == countBonds(*bnds)); + // z-neighbor is slightly more distant than 3 in the lower plane + bnds->setRmax(2.35); + CHECK(3 == countBonds(*bnds)); + bnds->setRmax(2.5); + CHECK(4 == countBonds(*bnds)); + // there are 12 second nearest neighbors at 3.81 + bnds->setRmin(3.7); + bnds->setRmax(3.82); + CHECK(12 == countBonds(*bnds)); + // and one more at 3.83 + bnds->setRmax(3.85); + CHECK(13 == countBonds(*bnds)); + // making the total 17 + bnds->setRmin(0); + CHECK(17 == countBonds(*bnds)); + // and the same happens for all other sites + bnds->selectAnchorSite(1); + CHECK(17 == countBonds(*bnds)); +} + +TEST_CASE_METHOD(TestObjCrystStructureBondGeneratorFixture, + "TestObjCrystStructureBondGenerator::test_LiTaO3", + "[ObjCrystStructureBondGenerator]") { + const string lithium = "Li1+"; + const string tantalum = "Ta5+"; + const string oxygen = "O2-"; + const double epsu = 1e-5; + unique_ptr litao3(loadTestCrystal("LiTaO3.cif")); + StructureAdapterPtr stru = createStructureAdapter(*litao3); + CHECK(3 == stru->countSites()); + BaseBondGeneratorPtr bnds = stru->createBondGenerator(); + bnds->selectAnchorSite(0); + bnds->selectSiteRange(0, 3); + // there are 3 oxygen neighbors at 2.065 + bnds->setRmax(2.1); + CHECK(3 == countBonds(*bnds)); + // Li at site 0 is isotropic, oxygens have equal msd-s towards Li + for (bnds->rewind(); !bnds->finished(); bnds->next()) { + CHECK(lithium == stru->siteAtomType(bnds->site0())); + CHECK(oxygen == stru->siteAtomType(bnds->site1())); + CHECK_THAT(0.00265968, WithinAbs(testmsd0(stru, bnds), epsu)); + CHECK_THAT(0.00710945, WithinAbs(testmsd1(stru, bnds), epsu)); + } + // there are 3 oxygen neighbors at 2.26 + bnds->setRmin(2.2); + bnds->setRmax(2.3); + CHECK(3 == countBonds(*bnds)); + for (bnds->rewind(); !bnds->finished(); bnds->next()) { + CHECK(oxygen == stru->siteAtomType(bnds->site1())); + CHECK_THAT(0.00265968, WithinAbs(testmsd0(stru, bnds), epsu)); + CHECK_THAT(0.00824319, WithinAbs(testmsd1(stru, bnds), epsu)); + } + // finally there are 4 Ta neighbors between 2.8 and 3.1 + bnds->setRmin(2.8); + bnds->setRmax(3.1); + CHECK(4 == countBonds(*bnds)); + for (bnds->rewind(); !bnds->finished(); bnds->next()) { + CHECK_THAT(0.00265968, WithinAbs(testmsd0(stru, bnds), epsu)); + CHECK(tantalum == stru->siteAtomType(bnds->site1())); + R3::Vector r01xy = bnds->r01(); + r01xy[2] = 0.0; + // for the tantalum above Li the msd equals U33 + if (R3::norm(r01xy) < 0.1) { + CHECK_THAT(0.00356, WithinAbs(testmsd1(stru, bnds), epsu)); + } + // other 3 tantalums are related by triple axis and + // have the same msd towards the central Li + else { + CHECK_THAT(0.00486942, WithinAbs(testmsd1(stru, bnds), epsu)); + } + } +} + +////////////////////////////////////////////////////////////////////////////// +// TestObjCrystMoleculeAdapter +////////////////////////////////////////////////////////////////////////////// + +class TestObjCrystMoleculeAdapterFixture { + protected: + Molecule* mmol_c60; + StructureAdapterPtr m_c60; + + public: + TestObjCrystMoleculeAdapterFixture() { + mmol_c60 = makeC60Molecule(); + m_c60 = createStructureAdapter(*mmol_c60); + } + + ~TestObjCrystMoleculeAdapterFixture() { + Crystal* crst = &(mmol_c60->GetCrystal()); + delete crst; + } +}; // class TestObjCrystMoleculeAdapterFixture + +TEST_CASE_METHOD(TestObjCrystMoleculeAdapterFixture, + "TestObjCrystMoleculeAdapter::test_typeid", + "[ObjCrystMoleculeAdapter]") { + StructureAdapter& r_c60 = *m_c60; + CHECK(typeid(AtomicStructureAdapter) == typeid(r_c60)); +} + +TEST_CASE_METHOD(TestObjCrystMoleculeAdapterFixture, + "TestObjCrystMoleculeAdapter::test_countSites", + "[ObjCrystMoleculeAdapter]") { + CHECK(60 == m_c60->countSites()); +} + +TEST_CASE_METHOD(TestObjCrystMoleculeAdapterFixture, + "TestObjCrystMoleculeAdapter::test_totalOccupancy", + "[ObjCrystMoleculeAdapter]") { + CHECK(60 == m_c60->totalOccupancy()); +} + +TEST_CASE_METHOD(TestObjCrystMoleculeAdapterFixture, + "TestObjCrystMoleculeAdapter::test_numberDensity", + "[ObjCrystMoleculeAdapter]") { + const double eps = 1.0e-7; + CHECK_THAT(0.0, WithinAbs(m_c60->numberDensity(), eps)); +} + +TEST_CASE_METHOD(TestObjCrystMoleculeAdapterFixture, + "TestObjCrystMoleculeAdapter::test_siteCartesianPosition", + "[ObjCrystMoleculeAdapter]") { + const double eps = 1.0e-5; + R3::Vector rC0 = m_c60->siteCartesianPosition(0); + CHECK_THAT(3.45127, WithinAbs(rC0[0], eps)); + CHECK_THAT(0.68500, WithinAbs(rC0[1], eps)); + CHECK_THAT(0.00000, WithinAbs(rC0[2], eps)); +} + +TEST_CASE_METHOD(TestObjCrystMoleculeAdapterFixture, + "TestObjCrystMoleculeAdapter::test_siteAnisotropy", + "[ObjCrystMoleculeAdapter]") { + for (int i = 0; i < m_c60->countSites(); ++i) { + CHECK(false == m_c60->siteAnisotropy(i)); + } +} + +TEST_CASE_METHOD(TestObjCrystMoleculeAdapterFixture, + "TestObjCrystMoleculeAdapter::test_siteCartesianUij", + "[ObjCrystMoleculeAdapter]") { + double BtoU = 1.0 / (8 * M_PI * M_PI); + const R3::Matrix Uij = m_c60->siteCartesianUij(0); + CHECK(0.0 == Uij(0, 1)); + CHECK(0.0 == Uij(0, 2)); + CHECK(0.0 == Uij(1, 0)); + CHECK(0.0 == Uij(1, 2)); + CHECK(0.0 == Uij(2, 0)); + CHECK(0.0 == Uij(2, 1)); + CHECK(BtoU == Uij(0, 0)); + CHECK(BtoU == Uij(1, 1)); + CHECK(BtoU == Uij(2, 2)); +} + +TEST_CASE_METHOD(TestObjCrystMoleculeAdapterFixture, + "TestObjCrystMoleculeAdapter::test_siteAtomType", + "[ObjCrystMoleculeAdapter]") { + for (int i = 0; i < m_c60->countSites(); ++i) { + CHECK(string("C") == m_c60->siteAtomType(i)); + } +} + +TEST_CASE_METHOD(TestObjCrystMoleculeAdapterFixture, + "TestObjCrystMoleculeAdapter::test_serialization", + "[ObjCrystMoleculeAdapter]") { + StructureAdapterPtr c60a; + c60a = dumpandload(m_c60); + CHECK(m_c60.get() != c60a.get()); + CHECK(60 == c60a->countSites()); + CHECK(60.0 == c60a->totalOccupancy()); + CHECK(0.0 == c60a->numberDensity()); + R3::Vector dxyz(1, 2, 3); + dxyz = m_c60->siteCartesianPosition(0) - c60a->siteCartesianPosition(0); + CHECK(0.0 == R3::norm(dxyz)); + dxyz = m_c60->siteCartesianPosition(37) - c60a->siteCartesianPosition(37); + CHECK(0.0 == R3::norm(dxyz)); + for (int i = 0; i < c60a->countSites(); ++i) { + CHECK(string("C") == c60a->siteAtomType(i)); + } +} + +////////////////////////////////////////////////////////////////////////////// +// TestObjCrystMoleculeBondGenerator +////////////////////////////////////////////////////////////////////////////// + +class TestObjCrystMoleculeBondGeneratorFixture { + protected: + Molecule* mmol_c60; + StructureAdapterPtr m_c60; + BaseBondGeneratorPtr m_c60bnds; + + public: + TestObjCrystMoleculeBondGeneratorFixture() { + mmol_c60 = makeC60Molecule(); + m_c60 = createStructureAdapter(*mmol_c60); + m_c60bnds = m_c60->createBondGenerator(); + } + + ~TestObjCrystMoleculeBondGeneratorFixture() { + Crystal* crst = &(mmol_c60->GetCrystal()); + delete crst; + } +}; // class TestObjCrystMoleculeBondGeneratorFixture + +TEST_CASE_METHOD(TestObjCrystMoleculeBondGeneratorFixture, + "TestObjCrystMoleculeBondGenerator::test_typeid", + "[ObjCrystMoleculeBondGenerator]") { + BaseBondGenerator& r_c60bnds = *m_c60bnds; + CHECK(typeid(BaseBondGenerator) == typeid(r_c60bnds)); +} + +TEST_CASE_METHOD(TestObjCrystMoleculeBondGeneratorFixture, + "TestObjCrystMoleculeBondGenerator::test_bondCountC60", + "[ObjCrystMoleculeBondGenerator]") { + m_c60bnds->selectAnchorSite(0); + m_c60bnds->setRmin(0); + m_c60bnds->setRmax(1.0); + CHECK(0 == countBonds(*m_c60bnds)); + m_c60bnds->setRmin(-10); + CHECK(0 == countBonds(*m_c60bnds)); + // there are 3 nearest neighbors at 1.44 + m_c60bnds->setRmax(1.5); + CHECK(3 == countBonds(*m_c60bnds)); + // There are 59 neighbors total + m_c60bnds->setRmax(8); + CHECK(59 == countBonds(*m_c60bnds)); +} + +namespace { + +Molecule* makeC60Molecule() { + Crystal* cryst = new Crystal(1, 1, 1, "P1"); + Molecule* mol = new Molecule(*cryst, "c60"); + cryst->AddScatterer(mol); + + // Populate the molecule + ScatteringPowerAtom* sp = new ScatteringPowerAtom("C", "C"); + cryst->AddScatteringPower(sp); + mol->AddAtom(3.451266498, 0.685000000, 0.000000000, sp, "C0"); + mol->AddAtom(3.451266498, -0.685000000, 0.000000000, sp, "C1"); + mol->AddAtom(-3.451266498, 0.685000000, 0.000000000, sp, "C2"); + mol->AddAtom(-3.451266498, -0.685000000, 0.000000000, sp, "C3"); + mol->AddAtom(0.685000000, 0.000000000, 3.451266498, sp, "C4"); + mol->AddAtom(-0.685000000, 0.000000000, 3.451266498, sp, "C5"); + mol->AddAtom(0.685000000, 0.000000000, -3.451266498, sp, "C6"); + mol->AddAtom(-0.685000000, 0.000000000, -3.451266498, sp, "C7"); + mol->AddAtom(0.000000000, 3.451266498, 0.685000000, sp, "C8"); + mol->AddAtom(0.000000000, 3.451266498, -0.685000000, sp, "C9"); + mol->AddAtom(0.000000000, -3.451266498, 0.685000000, sp, "C10"); + mol->AddAtom(0.000000000, -3.451266498, -0.685000000, sp, "C11"); + mol->AddAtom(3.003809890, 1.409000000, 1.171456608, sp, "C12"); + mol->AddAtom(3.003809890, 1.409000000, -1.171456608, sp, "C13"); + mol->AddAtom(3.003809890, -1.409000000, 1.171456608, sp, "C14"); + mol->AddAtom(3.003809890, -1.409000000, -1.171456608, sp, "C15"); + mol->AddAtom(-3.003809890, 1.409000000, 1.171456608, sp, "C16"); + mol->AddAtom(-3.003809890, 1.409000000, -1.171456608, sp, "C17"); + mol->AddAtom(-3.003809890, -1.409000000, 1.171456608, sp, "C18"); + mol->AddAtom(-3.003809890, -1.409000000, -1.171456608, sp, "C19"); + mol->AddAtom(1.409000000, 1.171456608, 3.003809890, sp, "C20"); + mol->AddAtom(1.409000000, -1.171456608, 3.003809890, sp, "C21"); + mol->AddAtom(-1.409000000, 1.171456608, 3.003809890, sp, "C22"); + mol->AddAtom(-1.409000000, -1.171456608, 3.003809890, sp, "C23"); + mol->AddAtom(1.409000000, 1.171456608, -3.003809890, sp, "C24"); + mol->AddAtom(1.409000000, -1.171456608, -3.003809890, sp, "C25"); + mol->AddAtom(-1.409000000, 1.171456608, -3.003809890, sp, "C26"); + mol->AddAtom(-1.409000000, -1.171456608, -3.003809890, sp, "C27"); + mol->AddAtom(1.171456608, 3.003809890, 1.409000000, sp, "C28"); + mol->AddAtom(-1.171456608, 3.003809890, 1.409000000, sp, "C29"); + mol->AddAtom(1.171456608, 3.003809890, -1.409000000, sp, "C30"); + mol->AddAtom(-1.171456608, 3.003809890, -1.409000000, sp, "C31"); + mol->AddAtom(1.171456608, -3.003809890, 1.409000000, sp, "C32"); + mol->AddAtom(-1.171456608, -3.003809890, 1.409000000, sp, "C33"); + mol->AddAtom(1.171456608, -3.003809890, -1.409000000, sp, "C34"); + mol->AddAtom(-1.171456608, -3.003809890, -1.409000000, sp, "C35"); + mol->AddAtom(2.580456608, 0.724000000, 2.279809890, sp, "C36"); + mol->AddAtom(2.580456608, 0.724000000, -2.279809890, sp, "C37"); + mol->AddAtom(2.580456608, -0.724000000, 2.279809890, sp, "C38"); + mol->AddAtom(2.580456608, -0.724000000, -2.279809890, sp, "C39"); + mol->AddAtom(-2.580456608, 0.724000000, 2.279809890, sp, "C40"); + mol->AddAtom(-2.580456608, 0.724000000, -2.279809890, sp, "C41"); + mol->AddAtom(-2.580456608, -0.724000000, 2.279809890, sp, "C42"); + mol->AddAtom(-2.580456608, -0.724000000, -2.279809890, sp, "C43"); + mol->AddAtom(0.724000000, 2.279809890, 2.580456608, sp, "C44"); + mol->AddAtom(0.724000000, -2.279809890, 2.580456608, sp, "C45"); + mol->AddAtom(-0.724000000, 2.279809890, 2.580456608, sp, "C46"); + mol->AddAtom(-0.724000000, -2.279809890, 2.580456608, sp, "C47"); + mol->AddAtom(0.724000000, 2.279809890, -2.580456608, sp, "C48"); + mol->AddAtom(0.724000000, -2.279809890, -2.580456608, sp, "C49"); + mol->AddAtom(-0.724000000, 2.279809890, -2.580456608, sp, "C50"); + mol->AddAtom(-0.724000000, -2.279809890, -2.580456608, sp, "C51"); + mol->AddAtom(2.279809890, 2.580456608, 0.724000000, sp, "C52"); + mol->AddAtom(-2.279809890, 2.580456608, 0.724000000, sp, "C53"); + mol->AddAtom(2.279809890, 2.580456608, -0.724000000, sp, "C54"); + mol->AddAtom(-2.279809890, 2.580456608, -0.724000000, sp, "C55"); + mol->AddAtom(2.279809890, -2.580456608, 0.724000000, sp, "C56"); + mol->AddAtom(-2.279809890, -2.580456608, 0.724000000, sp, "C57"); + mol->AddAtom(2.279809890, -2.580456608, -0.724000000, sp, "C58"); + mol->AddAtom(-2.279809890, -2.580456608, -0.724000000, sp, "C59"); + + return mol; +} + +} // namespace + +// End of file diff --git a/src/tests/TestObjCrystStructureAdapter.hpp b/src/tests/TestObjCrystStructureAdapter.hpp deleted file mode 100644 index 28d2841b..00000000 --- a/src/tests/TestObjCrystStructureAdapter.hpp +++ /dev/null @@ -1,605 +0,0 @@ -/***************************************************************************** - * - * libdiffpy by DANSE Diffraction group - * Simon J. L. Billinge - * (c) 2009 The Trustees of Columbia University - * in the City of New York. All rights reserved. - * - * File coded by: Pavol Juhas - * - * See AUTHORS.rst for a list of people who contributed. - * See LICENSE_DANSE.rst for license information. - * - ****************************************************************************** - * - * class TestObjCrystStructureAdapter -- unit tests for an adapter - * to the ObjCryst::Crystal class - * - * class TestObjCrystStructureBondGenerator -- unit tests for bond generator - * - *****************************************************************************/ - -#include -#include - -#include -#include -#include -#include "serialization_helpers.hpp" -#include "objcryst_helpers.hpp" -#include -#include -#include - -using namespace std; -using namespace diffpy::srreal; -using ObjCryst::Crystal; -using ObjCryst::Molecule; -using ObjCryst::ScatteringPowerAtom; - -// Local Helpers ------------------------------------------------------------- - -namespace { - -int countBonds(diffpy::srreal::BaseBondGenerator& bnds) { - int rv = 0; - for (bnds.rewind(); !bnds.finished(); bnds.next()) ++rv; - return rv; -} - -template -double testmsd0(const Tstru& stru, const Tbnds& bnds) { - bool anisotropy0 = stru->siteAnisotropy(bnds->site0()); - double rv = - meanSquareDisplacement(bnds->Ucartesian0(), bnds->r01(), anisotropy0); - return rv; -} - -template -double testmsd1(const Tstru& stru, const Tbnds& bnds) { - bool anisotropy1 = stru->siteAnisotropy(bnds->site1()); - double rv = - meanSquareDisplacement(bnds->Ucartesian1(), bnds->r01(), anisotropy1); - return rv; -} - -double diffdegree(StructureAdapterPtr stru0, StructureAdapterPtr stru1) { - StructureDifference sd = stru0->diff(stru1); - double tot = 0.0; - if (stru0) tot += stru0->countSites(); - if (stru1) tot += stru1->countSites(); - double rv = (sd.pop0.size() + sd.add1.size()) / (tot ? tot : 1.0); - return rv; -} - -// Defined below -Molecule* makeC60Molecule(); - -} // namespace - -////////////////////////////////////////////////////////////////////////////// -// class TestObjCrystStructureAdapter -////////////////////////////////////////////////////////////////////////////// - -class TestObjCrystStructureAdapter : public CxxTest::TestSuite { - private: - unique_ptr mcryst_ni; - StructureAdapterPtr m_ni; - unique_ptr mcryst_kbise; - StructureAdapterPtr m_kbise; - unique_ptr mcryst_catio3; - StructureAdapterPtr m_catio3; - - public: - void setUp() { - if (!m_ni.get()) { - mcryst_ni.reset(loadTestCrystal("Ni.cif")); - m_ni = createStructureAdapter(*mcryst_ni); - } - if (!m_kbise.get()) { - mcryst_kbise.reset(loadTestCrystal("alpha_K2Bi8Se13.cif")); - m_kbise = createStructureAdapter(*mcryst_kbise); - } - if (!m_catio3.get()) { - mcryst_catio3.reset(loadTestCrystal("CaTiO3.cif")); - m_catio3 = createStructureAdapter(*mcryst_catio3); - } - } - - void test_typeid() { - StructureAdapter& r_ni = *m_ni; - TS_ASSERT(typeid(CrystalStructureAdapter) == typeid(r_ni)); - } - - void test_countSites() { - TS_ASSERT_EQUALS(1, m_ni->countSites()); - TS_ASSERT_EQUALS(12, m_kbise->countSites()); - TS_ASSERT_EQUALS(4, m_catio3->countSites()); - } - - void test_totalOccupancy() { - TS_ASSERT_EQUALS(4.0, m_ni->totalOccupancy()); - TS_ASSERT_EQUALS(23.0, m_kbise->totalOccupancy()); - TS_ASSERT_EQUALS(20.0, m_catio3->totalOccupancy()); - } - - void test_numberDensity() { - const double eps = 1.0e-7; - TS_ASSERT_DELTA(0.0914114, m_ni->numberDensity(), eps); - TS_ASSERT_DELTA(0.0335565, m_kbise->numberDensity(), eps); - TS_ASSERT_DELTA(0.0894566, m_catio3->numberDensity(), eps); - } - - void test_siteCartesianPosition() { - const double eps = 1.0e-5; - R3::Vector rCa = m_catio3->siteCartesianPosition(1); - TS_ASSERT_DELTA(0.03486, rCa[0], eps); - TS_ASSERT_DELTA(0.19366, rCa[1], eps); - TS_ASSERT_DELTA(1.90975, rCa[2], eps); - } - - void test_siteAnisotropy() { - for (int i = 0; i < m_ni->countSites(); ++i) { - TS_ASSERT_EQUALS(false, m_ni->siteAnisotropy(i)); - } - for (int i = 0; i < m_catio3->countSites(); ++i) { - TS_ASSERT_EQUALS(true, m_catio3->siteAnisotropy(i)); - } - } - - void test_siteCartesianUij() { - // nickel should have all Uij equal zero. - const double* puij = &(m_ni->siteCartesianUij(0).data()[0]); - for (int i = 0; i < 9; ++i) { - TS_ASSERT_EQUALS(0.0, puij[i]); - } - // check CaTiO3 values - const R3::Matrix& UO2 = m_catio3->siteCartesianUij(3); - const double eps = 1e-8; - TS_ASSERT_DELTA(0.0065, UO2(0, 0), eps); - TS_ASSERT_DELTA(0.0060, UO2(1, 1), eps); - TS_ASSERT_DELTA(0.0095, UO2(2, 2), eps); - TS_ASSERT_DELTA(0.0020, UO2(0, 1), eps); - TS_ASSERT_DELTA(0.0020, UO2(1, 0), eps); - TS_ASSERT_DELTA(-0.0008, UO2(0, 2), eps); - TS_ASSERT_DELTA(-0.0008, UO2(2, 0), eps); - TS_ASSERT_DELTA(-0.0010, UO2(1, 2), eps); - TS_ASSERT_DELTA(-0.0010, UO2(2, 1), eps); - } - - void test_siteAtomType() { - TS_ASSERT_EQUALS(string("Ni"), m_ni->siteAtomType(0)); - TS_ASSERT_EQUALS(string("K1+"), m_kbise->siteAtomType(0)); - TS_ASSERT_EQUALS(string("Bi3+"), m_kbise->siteAtomType(1)); - TS_ASSERT_EQUALS(string("Se"), m_kbise->siteAtomType(5)); - TS_ASSERT_EQUALS(string("Se"), m_kbise->siteAtomType(11)); - } - - void test_diff() { - StructureAdapterPtr acto = m_catio3; - CrystalStructureAdapterPtr ccto( - std::dynamic_pointer_cast(acto)); - StructureAdapterPtr acto2 = acto->clone(); - CrystalStructureAdapterPtr ccto2( - std::dynamic_pointer_cast(acto2)); - TS_ASSERT_EQUALS(0.0, diffdegree(acto, acto)); - TS_ASSERT_EQUALS(1.0, diffdegree(acto, StructureAdapterPtr())); - TS_ASSERT_EQUALS(0.0, diffdegree(acto, acto2)); - TS_ASSERT_EQUALS(0.0, diffdegree(acto, ccto2)); - const double symprec = ccto2->getSymmetryPrecision(); - ccto2->setSymmetryPrecision(0.010723); - TS_ASSERT_EQUALS(1.0, diffdegree(acto, acto2)); - ccto2->setSymmetryPrecision(symprec); - TS_ASSERT_EQUALS(0.0, diffdegree(acto, acto2)); - ccto2->clearSymOps(); - TS_ASSERT_EQUALS(1.0, diffdegree(acto, acto2)); - } - - void test_getLattice() { - CrystalStructureAdapter* pkbise = - dynamic_cast(m_kbise.get()); - TS_ASSERT(pkbise); - const Lattice& L = pkbise->getLattice(); - const double eps = 1.0e-12; - TS_ASSERT_DELTA(13.768, L.a(), eps); - TS_ASSERT_DELTA(12.096, L.b(), eps); - TS_ASSERT_DELTA(4.1656, L.c(), eps); - TS_ASSERT_DELTA(89.98, L.alpha(), eps); - TS_ASSERT_DELTA(98.64, L.beta(), eps); - TS_ASSERT_DELTA(87.96, L.gamma(), eps); - } - - void test_getEquivalentAtoms() { - diffpy::mathutils::EpsilonEqual allclose; - unique_ptr crst(loadTestCrystal("NH4Br.cif")); - StructureAdapterPtr adpt = createStructureAdapter(*crst); - CrystalStructureAdapterPtr cadpt = - std::dynamic_pointer_cast(adpt); - TS_ASSERT_EQUALS(2, cadpt->countSites()); - TS_ASSERT_EQUALS(4, cadpt->totalOccupancy()); - CrystalStructureAdapter::AtomVector eq0, eq1; - eq0 = cadpt->getEquivalentAtoms(0); - TS_ASSERT_EQUALS(2, eq0.size()); - cadpt->toFractional(eq0[0]); - cadpt->toFractional(eq0[1]); - TS_ASSERT(allclose(R3::Vector(0, 0.5, 0.03), eq0[0].xyz_cartn)); - TS_ASSERT(allclose(R3::Vector(0.5, 0, 0.97), eq0[1].xyz_cartn)); - eq1 = cadpt->getEquivalentAtoms(1); - TS_ASSERT_EQUALS(2, eq1.size()); - cadpt->toFractional(eq1[0]); - cadpt->toFractional(eq1[1]); - TS_ASSERT(allclose(R3::Vector(0, 0, 0.5), eq1[0].xyz_cartn)); - TS_ASSERT(allclose(R3::Vector(0.5, 0.5, 0.5), eq1[1].xyz_cartn)); - } - - void test_serialization() { - stringstream storage(ios::in | ios::out | ios::binary); - diffpy::serialization::oarchive oa(storage, ios::binary); - oa << m_kbise; - diffpy::serialization::iarchive ia(storage, ios::binary); - StructureAdapterPtr kbise1; - TS_ASSERT(!kbise1.get()); - ia >> kbise1; - TS_ASSERT_DIFFERS(m_kbise.get(), kbise1.get()); - const double eps = 1.0e-7; - TS_ASSERT_EQUALS(12, kbise1->countSites()); - TS_ASSERT_EQUALS(23.0, kbise1->totalOccupancy()); - TS_ASSERT_DELTA(0.0335565, kbise1->numberDensity(), eps); - TS_ASSERT_EQUALS(string("K1+"), kbise1->siteAtomType(0)); - TS_ASSERT_EQUALS(string("Bi3+"), kbise1->siteAtomType(1)); - TS_ASSERT_EQUALS(string("Se"), kbise1->siteAtomType(5)); - TS_ASSERT_EQUALS(string("Se"), kbise1->siteAtomType(11)); - CrystalStructureAdapter* pkbise = - dynamic_cast(m_kbise.get()); - CrystalStructureAdapter* pkbise1 = - dynamic_cast(kbise1.get()); - const Lattice& L = pkbise->getLattice(); - const Lattice& L1 = pkbise1->getLattice(); - TS_ASSERT_EQUALS(L.a(), L1.a()); - TS_ASSERT_EQUALS(L.b(), L1.b()); - TS_ASSERT_EQUALS(L.c(), L1.c()); - TS_ASSERT_EQUALS(L.alpha(), L1.alpha()); - TS_ASSERT_EQUALS(L.beta(), L1.beta()); - TS_ASSERT_EQUALS(L.gamma(), L1.gamma()); - } - -}; // class TestObjCrystStructureAdapter - -////////////////////////////////////////////////////////////////////////////// -// class TestObjCrystStructureBondGenerator -////////////////////////////////////////////////////////////////////////////// - -class TestObjCrystStructureBondGenerator : public CxxTest::TestSuite { - private: - unique_ptr mcryst_ni; - StructureAdapterPtr m_ni; - BaseBondGeneratorPtr m_nibnds; - - public: - void setUp() { - if (!m_ni.get()) { - mcryst_ni.reset(loadTestCrystal("Ni.cif")); - m_ni = createStructureAdapter(*mcryst_ni); - } - m_nibnds = m_ni->createBondGenerator(); - } - - void test_typeid() { - BaseBondGenerator& r_nibnds = *m_nibnds; - TS_ASSERT(typeid(CrystalStructureBondGenerator) == typeid(r_nibnds)); - } - - void test_bondCountNickel() { - m_nibnds->selectAnchorSite(0); - m_nibnds->setRmin(0); - m_nibnds->setRmax(1.0); - TS_ASSERT_EQUALS(0, countBonds(*m_nibnds)); - m_nibnds->setRmin(-10); - TS_ASSERT_EQUALS(0, countBonds(*m_nibnds)); - // there are 12 nearest neighbors at 2.49 - m_nibnds->setRmax(3); - TS_ASSERT_EQUALS(12, countBonds(*m_nibnds)); - // there are no bonds between 2.6 and 3.4 - m_nibnds->setRmin(2.6); - m_nibnds->setRmax(3.4); - TS_ASSERT_EQUALS(0, countBonds(*m_nibnds)); - // there are 6 second nearest neighbors at 3.52 - m_nibnds->setRmax(3.6); - TS_ASSERT_EQUALS(6, countBonds(*m_nibnds)); - // which sums to 18 neighbors within radius 3.6 - m_nibnds->setRmin(0); - TS_ASSERT_EQUALS(18, countBonds(*m_nibnds)); - } - - void test_bondCountWurtzite() { - unique_ptr wurtzite(loadTestCrystal("ZnS_wurtzite.cif")); - StructureAdapterPtr stru = createStructureAdapter(*wurtzite); - BaseBondGeneratorPtr bnds = stru->createBondGenerator(); - TS_ASSERT_EQUALS(2, stru->countSites()); - bnds->selectAnchorSite(0); - bnds->selectSiteRange(0, 2); - bnds->setRmin(0); - // there should be no bond below the ZnS distance 2.31 - bnds->setRmax(2.2); - TS_ASSERT_EQUALS(0, countBonds(*bnds)); - // z-neighbor is slightly more distant than 3 in the lower plane - bnds->setRmax(2.35); - TS_ASSERT_EQUALS(3, countBonds(*bnds)); - bnds->setRmax(2.5); - TS_ASSERT_EQUALS(4, countBonds(*bnds)); - // there are 12 second nearest neighbors at 3.81 - bnds->setRmin(3.7); - bnds->setRmax(3.82); - TS_ASSERT_EQUALS(12, countBonds(*bnds)); - // and one more at 3.83 - bnds->setRmax(3.85); - TS_ASSERT_EQUALS(13, countBonds(*bnds)); - // making the total 17 - bnds->setRmin(0); - TS_ASSERT_EQUALS(17, countBonds(*bnds)); - // and the same happens for all other sites - bnds->selectAnchorSite(1); - TS_ASSERT_EQUALS(17, countBonds(*bnds)); - } - - void test_LiTaO3() { - const string lithium = "Li1+"; - const string tantalum = "Ta5+"; - const string oxygen = "O2-"; - const double epsu = 1e-5; - unique_ptr litao3(loadTestCrystal("LiTaO3.cif")); - StructureAdapterPtr stru = createStructureAdapter(*litao3); - TS_ASSERT_EQUALS(3, stru->countSites()); - BaseBondGeneratorPtr bnds = stru->createBondGenerator(); - bnds->selectAnchorSite(0); - bnds->selectSiteRange(0, 3); - // there are 3 oxygen neighbors at 2.065 - bnds->setRmax(2.1); - TS_ASSERT_EQUALS(3, countBonds(*bnds)); - // Li at site 0 is isotropic, oxygens have equal msd-s towards Li - for (bnds->rewind(); !bnds->finished(); bnds->next()) { - TS_ASSERT_EQUALS(lithium, stru->siteAtomType(bnds->site0())); - TS_ASSERT_EQUALS(oxygen, stru->siteAtomType(bnds->site1())); - TS_ASSERT_DELTA(0.00265968, testmsd0(stru, bnds), epsu); - TS_ASSERT_DELTA(0.00710945, testmsd1(stru, bnds), epsu); - } - // there are 3 oxygen neighbors at 2.26 - bnds->setRmin(2.2); - bnds->setRmax(2.3); - TS_ASSERT_EQUALS(3, countBonds(*bnds)); - for (bnds->rewind(); !bnds->finished(); bnds->next()) { - TS_ASSERT_EQUALS(oxygen, stru->siteAtomType(bnds->site1())); - TS_ASSERT_DELTA(0.00265968, testmsd0(stru, bnds), epsu); - TS_ASSERT_DELTA(0.00824319, testmsd1(stru, bnds), epsu); - } - // finally there are 4 Ta neighbors between 2.8 and 3.1 - bnds->setRmin(2.8); - bnds->setRmax(3.1); - TS_ASSERT_EQUALS(4, countBonds(*bnds)); - for (bnds->rewind(); !bnds->finished(); bnds->next()) { - TS_ASSERT_DELTA(0.00265968, testmsd0(stru, bnds), epsu); - TS_ASSERT_EQUALS(tantalum, stru->siteAtomType(bnds->site1())); - R3::Vector r01xy = bnds->r01(); - r01xy[2] = 0.0; - // for the tantalum above Li the msd equals U33 - if (R3::norm(r01xy) < 0.1) { - TS_ASSERT_DELTA(0.00356, testmsd1(stru, bnds), epsu); - } - // other 3 tantalums are related by triple axis and - // have the same msd towards the central Li - else { - TS_ASSERT_DELTA(0.00486942, testmsd1(stru, bnds), epsu); - } - } - } - -}; // class TestObjCrystStructureBondGenerator - -////////////////////////////////////////////////////////////////////////////// -// class TestObjCrystMoleculeAdapter -////////////////////////////////////////////////////////////////////////////// - -class TestObjCrystMoleculeAdapter : public CxxTest::TestSuite { - private: - Molecule* mmol_c60; - StructureAdapterPtr m_c60; - - public: - void setUp() { - mmol_c60 = makeC60Molecule(); - m_c60 = createStructureAdapter(*mmol_c60); - } - - void tearDown() { - Crystal* crst = &(mmol_c60->GetCrystal()); - delete crst; - } - - void test_typeid() { - StructureAdapter& r_c60 = *m_c60; - TS_ASSERT(typeid(AtomicStructureAdapter) == typeid(r_c60)); - } - - void test_countSites() { TS_ASSERT_EQUALS(60, m_c60->countSites()); } - - void test_totalOccupancy() { TS_ASSERT_EQUALS(60, m_c60->totalOccupancy()); } - - void test_numberDensity() { - const double eps = 1.0e-7; - TS_ASSERT_DELTA(0.0, m_c60->numberDensity(), eps); - } - - void test_siteCartesianPosition() { - const double eps = 1.0e-5; - R3::Vector rC0 = m_c60->siteCartesianPosition(0); - TS_ASSERT_DELTA(3.45127, rC0[0], eps); - TS_ASSERT_DELTA(0.68500, rC0[1], eps); - TS_ASSERT_DELTA(0.00000, rC0[2], eps); - } - - void test_siteAnisotropy() { - for (int i = 0; i < m_c60->countSites(); ++i) { - TS_ASSERT_EQUALS(false, m_c60->siteAnisotropy(i)); - } - } - - void test_siteCartesianUij() { - double BtoU = 1.0 / (8 * M_PI * M_PI); - const R3::Matrix Uij = m_c60->siteCartesianUij(0); - TS_ASSERT_EQUALS(0.0, Uij(0, 1)); - TS_ASSERT_EQUALS(0.0, Uij(0, 2)); - TS_ASSERT_EQUALS(0.0, Uij(1, 0)); - TS_ASSERT_EQUALS(0.0, Uij(1, 2)); - TS_ASSERT_EQUALS(0.0, Uij(2, 0)); - TS_ASSERT_EQUALS(0.0, Uij(2, 1)); - TS_ASSERT_EQUALS(BtoU, Uij(0, 0)); - TS_ASSERT_EQUALS(BtoU, Uij(1, 1)); - TS_ASSERT_EQUALS(BtoU, Uij(2, 2)); - } - - void test_siteAtomType() { - for (int i = 0; i < m_c60->countSites(); ++i) { - TS_ASSERT_EQUALS(string("C"), m_c60->siteAtomType(i)); - } - } - - void test_serialization() { - StructureAdapterPtr c60a; - c60a = dumpandload(m_c60); - TS_ASSERT_DIFFERS(m_c60.get(), c60a.get()); - TS_ASSERT_EQUALS(60, c60a->countSites()); - TS_ASSERT_EQUALS(60.0, c60a->totalOccupancy()); - TS_ASSERT_EQUALS(0.0, c60a->numberDensity()); - R3::Vector dxyz(1, 2, 3); - dxyz = m_c60->siteCartesianPosition(0) - c60a->siteCartesianPosition(0); - TS_ASSERT_EQUALS(0.0, R3::norm(dxyz)); - dxyz = m_c60->siteCartesianPosition(37) - c60a->siteCartesianPosition(37); - TS_ASSERT_EQUALS(0.0, R3::norm(dxyz)); - for (int i = 0; i < c60a->countSites(); ++i) { - TS_ASSERT_EQUALS(string("C"), c60a->siteAtomType(i)); - } - } - -}; // class TestObjCrystMoleculeAdapter - -////////////////////////////////////////////////////////////////////////////// -// class TestObjCrystMoleculeBondGenerator -////////////////////////////////////////////////////////////////////////////// - -class TestObjCrystMoleculeBondGenerator : public CxxTest::TestSuite { - private: - Molecule* mmol_c60; - StructureAdapterPtr m_c60; - BaseBondGeneratorPtr m_c60bnds; - - public: - void setUp() { - mmol_c60 = makeC60Molecule(); - m_c60 = createStructureAdapter(*mmol_c60); - m_c60bnds = m_c60->createBondGenerator(); - } - - void tearDown() { - Crystal* crst = &(mmol_c60->GetCrystal()); - delete crst; - } - - void test_typeid() { - BaseBondGenerator& r_c60bnds = *m_c60bnds; - TS_ASSERT(typeid(BaseBondGenerator) == typeid(r_c60bnds)); - } - - void test_bondCountC60() { - m_c60bnds->selectAnchorSite(0); - m_c60bnds->setRmin(0); - m_c60bnds->setRmax(1.0); - TS_ASSERT_EQUALS(0, countBonds(*m_c60bnds)); - m_c60bnds->setRmin(-10); - TS_ASSERT_EQUALS(0, countBonds(*m_c60bnds)); - // there are 3 nearest neighbors at 1.44 - m_c60bnds->setRmax(1.5); - TS_ASSERT_EQUALS(3, countBonds(*m_c60bnds)); - // There are 59 neighbors total - m_c60bnds->setRmax(8); - TS_ASSERT_EQUALS(59, countBonds(*m_c60bnds)); - } - -}; // class TestObjCrystMoleculeBondGenerator - -namespace { - -Molecule* makeC60Molecule() { - Crystal* cryst = new Crystal(1, 1, 1, "P1"); - Molecule* mol = new Molecule(*cryst, "c60"); - cryst->AddScatterer(mol); - - // Populate the molecule - ScatteringPowerAtom* sp = new ScatteringPowerAtom("C", "C"); - cryst->AddScatteringPower(sp); - mol->AddAtom(3.451266498, 0.685000000, 0.000000000, sp, "C0"); - mol->AddAtom(3.451266498, -0.685000000, 0.000000000, sp, "C1"); - mol->AddAtom(-3.451266498, 0.685000000, 0.000000000, sp, "C2"); - mol->AddAtom(-3.451266498, -0.685000000, 0.000000000, sp, "C3"); - mol->AddAtom(0.685000000, 0.000000000, 3.451266498, sp, "C4"); - mol->AddAtom(-0.685000000, 0.000000000, 3.451266498, sp, "C5"); - mol->AddAtom(0.685000000, 0.000000000, -3.451266498, sp, "C6"); - mol->AddAtom(-0.685000000, 0.000000000, -3.451266498, sp, "C7"); - mol->AddAtom(0.000000000, 3.451266498, 0.685000000, sp, "C8"); - mol->AddAtom(0.000000000, 3.451266498, -0.685000000, sp, "C9"); - mol->AddAtom(0.000000000, -3.451266498, 0.685000000, sp, "C10"); - mol->AddAtom(0.000000000, -3.451266498, -0.685000000, sp, "C11"); - mol->AddAtom(3.003809890, 1.409000000, 1.171456608, sp, "C12"); - mol->AddAtom(3.003809890, 1.409000000, -1.171456608, sp, "C13"); - mol->AddAtom(3.003809890, -1.409000000, 1.171456608, sp, "C14"); - mol->AddAtom(3.003809890, -1.409000000, -1.171456608, sp, "C15"); - mol->AddAtom(-3.003809890, 1.409000000, 1.171456608, sp, "C16"); - mol->AddAtom(-3.003809890, 1.409000000, -1.171456608, sp, "C17"); - mol->AddAtom(-3.003809890, -1.409000000, 1.171456608, sp, "C18"); - mol->AddAtom(-3.003809890, -1.409000000, -1.171456608, sp, "C19"); - mol->AddAtom(1.409000000, 1.171456608, 3.003809890, sp, "C20"); - mol->AddAtom(1.409000000, -1.171456608, 3.003809890, sp, "C21"); - mol->AddAtom(-1.409000000, 1.171456608, 3.003809890, sp, "C22"); - mol->AddAtom(-1.409000000, -1.171456608, 3.003809890, sp, "C23"); - mol->AddAtom(1.409000000, 1.171456608, -3.003809890, sp, "C24"); - mol->AddAtom(1.409000000, -1.171456608, -3.003809890, sp, "C25"); - mol->AddAtom(-1.409000000, 1.171456608, -3.003809890, sp, "C26"); - mol->AddAtom(-1.409000000, -1.171456608, -3.003809890, sp, "C27"); - mol->AddAtom(1.171456608, 3.003809890, 1.409000000, sp, "C28"); - mol->AddAtom(-1.171456608, 3.003809890, 1.409000000, sp, "C29"); - mol->AddAtom(1.171456608, 3.003809890, -1.409000000, sp, "C30"); - mol->AddAtom(-1.171456608, 3.003809890, -1.409000000, sp, "C31"); - mol->AddAtom(1.171456608, -3.003809890, 1.409000000, sp, "C32"); - mol->AddAtom(-1.171456608, -3.003809890, 1.409000000, sp, "C33"); - mol->AddAtom(1.171456608, -3.003809890, -1.409000000, sp, "C34"); - mol->AddAtom(-1.171456608, -3.003809890, -1.409000000, sp, "C35"); - mol->AddAtom(2.580456608, 0.724000000, 2.279809890, sp, "C36"); - mol->AddAtom(2.580456608, 0.724000000, -2.279809890, sp, "C37"); - mol->AddAtom(2.580456608, -0.724000000, 2.279809890, sp, "C38"); - mol->AddAtom(2.580456608, -0.724000000, -2.279809890, sp, "C39"); - mol->AddAtom(-2.580456608, 0.724000000, 2.279809890, sp, "C40"); - mol->AddAtom(-2.580456608, 0.724000000, -2.279809890, sp, "C41"); - mol->AddAtom(-2.580456608, -0.724000000, 2.279809890, sp, "C42"); - mol->AddAtom(-2.580456608, -0.724000000, -2.279809890, sp, "C43"); - mol->AddAtom(0.724000000, 2.279809890, 2.580456608, sp, "C44"); - mol->AddAtom(0.724000000, -2.279809890, 2.580456608, sp, "C45"); - mol->AddAtom(-0.724000000, 2.279809890, 2.580456608, sp, "C46"); - mol->AddAtom(-0.724000000, -2.279809890, 2.580456608, sp, "C47"); - mol->AddAtom(0.724000000, 2.279809890, -2.580456608, sp, "C48"); - mol->AddAtom(0.724000000, -2.279809890, -2.580456608, sp, "C49"); - mol->AddAtom(-0.724000000, 2.279809890, -2.580456608, sp, "C50"); - mol->AddAtom(-0.724000000, -2.279809890, -2.580456608, sp, "C51"); - mol->AddAtom(2.279809890, 2.580456608, 0.724000000, sp, "C52"); - mol->AddAtom(-2.279809890, 2.580456608, 0.724000000, sp, "C53"); - mol->AddAtom(2.279809890, 2.580456608, -0.724000000, sp, "C54"); - mol->AddAtom(-2.279809890, 2.580456608, -0.724000000, sp, "C55"); - mol->AddAtom(2.279809890, -2.580456608, 0.724000000, sp, "C56"); - mol->AddAtom(-2.279809890, -2.580456608, 0.724000000, sp, "C57"); - mol->AddAtom(2.279809890, -2.580456608, -0.724000000, sp, "C58"); - mol->AddAtom(-2.279809890, -2.580456608, -0.724000000, sp, "C59"); - - return mol; -} - -} // namespace - -// End of file diff --git a/src/tests/TestOverlapCalculator.cpp b/src/tests/TestOverlapCalculator.cpp new file mode 100644 index 00000000..ae3d3364 --- /dev/null +++ b/src/tests/TestOverlapCalculator.cpp @@ -0,0 +1,355 @@ +/***************************************************************************** + * + * libdiffpy by DANSE Diffraction group + * Simon J. L. Billinge + * (c) 2011 The Trustees of Columbia University + * in the City of New York. All rights reserved. + * + * File coded by: Pavol Juhas + * + * See AUTHORS.rst for a list of people who contributed. + * See LICENSE_DANSE.rst for license information. + * + ****************************************************************************** + * + * TestOverlapCalculator -- unit tests for the OverlapCalculator class + * + *****************************************************************************/ + +#include +#include + +#include +#include + +#include +#include +#include +#include +#include "test_helpers.hpp" +#include "serialization_helpers.hpp" + +using Catch::Matchers::WithinAbs; + +using namespace std; +using namespace diffpy::srreal; + +class TestOverlapCalculatorFixture { + protected: + std::shared_ptr molc; + StructureAdapterPtr mnacl; + double meps; + + public: + TestOverlapCalculatorFixture() { + meps = diffpy::mathutils::SQRT_DOUBLE_EPS; + molc.reset(new OverlapCalculator); + molc->getAtomRadiiTable()->setCustom("", 1.0); + molc->getAtomRadiiTable()->setCustom("Na1+", 1.5); + molc->getAtomRadiiTable()->setCustom("Cl1-", 1.8); + mnacl = loadTestPeriodicStructure("NaCl.stru"); + } +}; // class TestOverlapCalculatorFixture + +TEST_CASE_METHOD(TestOverlapCalculatorFixture, + "TestOverlapCalculator::test_rmin", "[OverlapCalculator]") { + molc->eval(mnacl); + CHECK(molc->distances().size()); + double tot = molc->totalSquareOverlap(); + CHECK(0.0 < tot); + molc->setRmin(10); + molc->eval(); + CHECK(molc->distances().empty()); + CHECK(0.0 == molc->totalSquareOverlap()); + molc->setRmin(2); + molc->eval(); + CHECK(tot == molc->totalSquareOverlap()); +} + +TEST_CASE_METHOD(TestOverlapCalculatorFixture, + "TestOverlapCalculator::test_rmax", "[OverlapCalculator]") { + molc->setRmax(2.5); + molc->eval(mnacl); + CHECK(molc->distances().empty()); + CHECK(0.0 == molc->totalSquareOverlap()); +} + +TEST_CASE_METHOD(TestOverlapCalculatorFixture, + "TestOverlapCalculator::test_lineNoTouch", + "[OverlapCalculator]") { + AtomicStructureAdapterPtr stru(new AtomicStructureAdapter); + Atom a0, a1; + a0.xyz_cartn = R3::Vector(0.0, 0.0, 0.0); + a1.xyz_cartn = R3::Vector(0.0, 0.0, 2.0); + stru->append(a0); + stru->append(a1); + molc->eval(stru); + CHECK(0.0 == molc->totalSquareOverlap()); + QuantityType sqolps = molc->siteSquareOverlaps(); + REQUIRE(2u == sqolps.size()); + CHECK(0.0 == sqolps[0]); + CHECK(0.0 == sqolps[1]); + CHECK(0.0 == molc->flipDiffTotal(0, 0)); + CHECK(0.0 == molc->flipDiffTotal(0, 1)); + std::vector g = molc->gradients(); + REQUIRE(2u == g.size()); + CHECK(0.0 == R3::norm(g[0])); + CHECK(0.0 == R3::norm(g[1])); +} + +TEST_CASE_METHOD(TestOverlapCalculatorFixture, + "TestOverlapCalculator::test_lineTouch", + "[OverlapCalculator]") { + AtomicStructureAdapterPtr stru(new AtomicStructureAdapter); + Atom a0, a1; + a0.xyz_cartn = R3::Vector(0.0, 0.0, 0.0); + a1.xyz_cartn = R3::Vector(0.0, 0.0, 1.5); + stru->append(a0); + stru->append(a1); + molc->eval(stru); + CHECK(0.25 == molc->totalSquareOverlap()); + QuantityType sqolps = molc->siteSquareOverlaps(); + REQUIRE(2u == sqolps.size()); + CHECK(0.125 == sqolps[0]); + CHECK(0.125 == sqolps[1]); + CHECK(0.0 == molc->flipDiffTotal(0, 1)); + std::vector g = molc->gradients(); + REQUIRE(2u == g.size()); + R3::Vector g0(0.0, 0.0, +1); + CHECK(0.0 == R3::distance(g0, g[0])); + R3::Vector g1(0.0, 0.0, -1); + CHECK(0.0 == R3::distance(g1, g[1])); +} + +TEST_CASE_METHOD(TestOverlapCalculatorFixture, + "TestOverlapCalculator::test_gradients", + "[OverlapCalculator]") { + AtomicStructureAdapterPtr stru(new AtomicStructureAdapter); + Atom a0, a1; + a0.xyz_cartn = R3::Vector(0.0, 0.0, 0.0); + a1.xyz_cartn = R3::Vector(0.17, 0.31, 0.37); + stru->append(a0); + stru->append(a1); + molc->eval(stru); + std::vector g = molc->gradients(); + REQUIRE(2u == g.size()); + double dx = 1e-6; + double tsqo0 = molc->totalSquareOverlap(); + R3::Vector r1 = stru->siteCartesianPosition(1); + R3::Vector gn1; + for (int k = 0; k != R3::Ndim; ++k) { + R3::Vector& xyz1 = stru->at(1).xyz_cartn; + xyz1 = r1; + xyz1[k] += dx; + molc->eval(stru); + gn1[k] = (molc->totalSquareOverlap() - tsqo0) / dx; + } + CHECK(R3::norm(gn1) > 1.0); + CHECK_THAT(0.0, WithinAbs(R3::norm(gn1 - g[1]), 1e-5)); + CHECK_THAT(0.0, WithinAbs(R3::norm(g[0] + g[1]), 1e-5)); +} + +TEST_CASE_METHOD(TestOverlapCalculatorFixture, + "TestOverlapCalculator::test_bccTouch", + "[OverlapCalculator]") { + PeriodicStructureAdapterPtr bcc(new PeriodicStructureAdapter); + bcc->setLatPar(2.0, 2.0, 2.0, 90, 90, 90); + Atom a; + a.atomtype = "C"; + bcc->append(a); + a.xyz_cartn = R3::Vector(0.5, 0.5, 0.5); + bcc->toCartesian(a); + bcc->append(a); + molc->getAtomRadiiTable()->setCustom("C", 1.0); + molc->eval(bcc); + CHECK(2 == molc->getStructure()->countSites()); + CHECK(8 * pow(2.0 - sqrt(3.0), 2) == molc->totalSquareOverlap()); + // gradient is zero due to bcc symmetry + std::vector g = molc->gradients(); + REQUIRE(2u == g.size()); + CHECK_THAT(0.0, WithinAbs(R3::norm(g[0]), meps)); + CHECK_THAT(0.0, WithinAbs(R3::norm(g[1]), meps)); + // move the second atom so it touches only one ball + bcc->at(1).xyz_cartn = R3::Vector(-0.25, 0.0, 0.0); + bcc->toCartesian(bcc->at(1)); + molc->eval(bcc); + double tsqo = 1.5 * 1.5 + 0.5 * 0.5; + CHECK_THAT(tsqo, WithinAbs(molc->totalSquareOverlap(), meps)); + g = molc->gradients(); + REQUIRE(2u == g.size()); + double gx = 1.5 + 0.5; + CHECK_THAT(-gx, WithinAbs(g[0][0], meps)); + CHECK_THAT(0.0, WithinAbs(g[0][1], meps)); + CHECK_THAT(0.0, WithinAbs(g[0][2], meps)); + CHECK_THAT(+gx, WithinAbs(g[1][0], meps)); + CHECK_THAT(0.0, WithinAbs(g[1][1], meps)); + CHECK_THAT(0.0, WithinAbs(g[1][2], meps)); +} + +TEST_CASE_METHOD(TestOverlapCalculatorFixture, + "TestOverlapCalculator::test_NaCl_overlap", + "[OverlapCalculator]") { + double olp = pow(3.3 - 5.62 / 2, 2) * 6 / 2; + molc->eval(mnacl); + CHECK_THAT(olp, WithinAbs(molc->meanSquareOverlap(), meps)); + QuantityType sqolps = molc->siteSquareOverlaps(); + REQUIRE(8u == sqolps.size()); + for (int i = 0; i < 8; ++i) { + CHECK_THAT(olp, WithinAbs(sqolps[i], meps)); + } +} + +TEST_CASE_METHOD(TestOverlapCalculatorFixture, + "TestOverlapCalculator::test_NaCl_flips", + "[OverlapCalculator]") { + molc->eval(mnacl); + // flipping the same type should not change the cost + CHECK(0.0 == molc->flipDiffTotal(0, 0)); + CHECK(0.0 == molc->flipDiffTotal(0, 1)); + CHECK(0.0 == molc->flipDiffTotal(0, 2)); + CHECK(0.0 == molc->flipDiffTotal(0, 3)); + // flipping with the second Cl neighbor + CHECK_THAT(1.08, WithinAbs(molc->flipDiffTotal(0, 4), meps)); + CHECK_THAT(1.08 / 8, WithinAbs(molc->flipDiffMean(0, 4), meps)); + // flipping with the nearest Cl neighbor + CHECK_THAT(0.72, WithinAbs(molc->flipDiffTotal(0, 5), meps)); + CHECK_THAT(0.72, WithinAbs(molc->flipDiffTotal(0, 6), meps)); + CHECK_THAT(0.72, WithinAbs(molc->flipDiffTotal(0, 7), meps)); +} + +TEST_CASE_METHOD(TestOverlapCalculatorFixture, + "TestOverlapCalculator::test_NaCl_gradient", + "[OverlapCalculator]") { + molc->eval(mnacl); + // default gradients are all zero + std::vector g = molc->gradients(); + REQUIRE(8u == g.size()); + for (int i = 0; i < 8; ++i) { + CHECK_THAT(0.0, WithinAbs(R3::norm(g[i]), meps)); + } + StructureAdapterPtr nacl1 = mnacl->clone(); + PeriodicStructureAdapter& nacl1ref = + static_cast(*nacl1); + R3::Vector& xyzc0 = nacl1ref[0].xyz_cartn; + xyzc0 = R3::Vector(0.02, 0.03, 0.07); + molc->eval(nacl1); + double c0 = molc->totalSquareOverlap(); + REQUIRE(8u == molc->gradients().size()); + R3::Vector g0 = molc->gradients()[0]; + CHECK(R3::norm(g0) > meps); + R3::Vector g0n; + const double dx = 1e-8; + for (int i = 0; i < R3::Ndim; ++i) { + xyzc0[i] += dx; + molc->eval(nacl1); + g0n[i] = (molc->totalSquareOverlap() - c0) / dx; + xyzc0[i] -= dx; + } + CHECK_THAT(0.0, WithinAbs(R3::norm(g0 - g0n), 1e-6)); +} + +TEST_CASE_METHOD(TestOverlapCalculatorFixture, + "TestOverlapCalculator::test_getNeighborSites", + "[OverlapCalculator]") { + molc->eval(mnacl); + auto nbsites = molc->getNeighborSites(0); + REQUIRE(3u == nbsites.size()); + for (int j = 5; j < 8; ++j) { + CHECK(nbsites.count(j)); + } +} + +TEST_CASE_METHOD(TestOverlapCalculatorFixture, + "TestOverlapCalculator::test_coordinations", + "[OverlapCalculator]") { + using std::placeholders::_1; + molc->eval(mnacl); + auto cnums = molc->coordinations(); + REQUIRE(8u == cnums.size()); + auto eq6 = bind(equal_to(), _1, 6.0); + CHECK(all_of(cnums.begin(), cnums.end(), eq6)); +} + +TEST_CASE_METHOD(TestOverlapCalculatorFixture, + "TestOverlapCalculator::test_coordinationByTypes", + "[OverlapCalculator]") { + molc->eval(mnacl); + auto cbtb0 = molc->coordinationByTypes(0); + auto cbtb3 = molc->coordinationByTypes(3); + auto cbtb5 = molc->coordinationByTypes(5); + REQUIRE(1u == cbtb0.size()); + REQUIRE(1u == cbtb5.size()); + CHECK(cbtb0 == cbtb3); + CHECK(6 == cbtb0.at("Cl1-")); + CHECK(6 == cbtb5.at("Na1+")); +} + +TEST_CASE_METHOD(TestOverlapCalculatorFixture, + "TestOverlapCalculator::test_neighborhoods", + "[OverlapCalculator]") { + auto tb = molc->getAtomRadiiTable(); + molc->eval(mnacl); + auto nbhood = molc->neighborhoods(); + REQUIRE(1u == nbhood.size()); + REQUIRE(8u == nbhood[0].size()); + tb->resetAll(); + molc->eval(mnacl); + auto nbsep = molc->neighborhoods(); + REQUIRE(8u == nbsep.size()); + REQUIRE_FALSE(nbsep[5].empty()); + int nb5site = *(nbsep[5].begin()); + CHECK(5 == nb5site); + molc->maskAllPairs(false); + molc->eval(mnacl); + auto nbdark = molc->neighborhoods(); + CHECK(nbdark.empty()); + molc->setTypeMask("Na1+", "Na1+", true); + molc->eval(mnacl); + auto nbnanasep = molc->neighborhoods(); + REQUIRE(4u == nbnanasep.size()); + REQUIRE_FALSE(nbnanasep[3].empty()); + int nb3site = *(nbnanasep[3].begin()); + CHECK(3 == nb3site); + tb->setCustom("Na1+", 5); + tb->setCustom("Cl1-", 5); + molc->eval(mnacl); + auto nbnana = molc->neighborhoods(); + REQUIRE(1u == nbnana.size()); + REQUIRE(4u == nbnana[0].size()); + CHECK(nbnana[0].count(0)); + CHECK(!nbnana[0].count(4)); +} + +TEST_CASE_METHOD(TestOverlapCalculatorFixture, + "TestOverlapCalculator::test_NaCl_mixed_overlap", + "[OverlapCalculator]") { + StructureAdapterPtr nacl_mixed; + nacl_mixed = loadTestPeriodicStructure("NaCl_mixed.stru"); + molc->eval(nacl_mixed); + double olp = pow(3.3 - 5.62 / 2, 2) * 6 / 2; + CHECK_THAT(olp, WithinAbs(molc->meanSquareOverlap(), meps)); +} + +TEST_CASE_METHOD(TestOverlapCalculatorFixture, + "TestOverlapCalculator::test_serialization", + "[OverlapCalculator]") { + // build customized PDFCalculator + AtomicStructureAdapterPtr stru(new AtomicStructureAdapter); + Atom a0, a1; + a0.xyz_cartn = R3::Vector(0.0, 0.0, 0.0); + a1.xyz_cartn = R3::Vector(0.0, 0.0, 1.5); + stru->append(a0); + stru->append(a1); + molc->eval(stru); + std::shared_ptr olc1; + olc1 = dumpandload(molc); + CHECK(molc.get() != olc1.get()); + CHECK(2 == olc1->getStructure()->countSites()); + CHECK(0.25 == olc1->totalSquareOverlap()); + QuantityType sqolps = molc->siteSquareOverlaps(); + REQUIRE(2u == olc1->siteSquareOverlaps().size()); + CHECK(0.125 == olc1->siteSquareOverlaps()[0]); + CHECK(0.125 == olc1->siteSquareOverlaps()[1]); +} + +// End of file diff --git a/src/tests/TestOverlapCalculator.hpp b/src/tests/TestOverlapCalculator.hpp deleted file mode 100644 index b862fd18..00000000 --- a/src/tests/TestOverlapCalculator.hpp +++ /dev/null @@ -1,322 +0,0 @@ -/***************************************************************************** - * - * libdiffpy by DANSE Diffraction group - * Simon J. L. Billinge - * (c) 2011 The Trustees of Columbia University - * in the City of New York. All rights reserved. - * - * File coded by: Pavol Juhas - * - * See AUTHORS.rst for a list of people who contributed. - * See LICENSE_DANSE.rst for license information. - * - ****************************************************************************** - * - * class TestOverlapCalculator -- unit tests for the OverlapCalculator class - * - *****************************************************************************/ - -#include - -#include -#include - -#include -#include -#include -#include -#include "test_helpers.hpp" -#include "serialization_helpers.hpp" - -using namespace std; -using namespace diffpy::srreal; - -class TestOverlapCalculator : public CxxTest::TestSuite { - private: - std::shared_ptr molc; - StructureAdapterPtr mnacl; - double meps; - - public: - void setUp() { - CxxTest::setAbortTestOnFail(true); - meps = diffpy::mathutils::SQRT_DOUBLE_EPS; - molc.reset(new OverlapCalculator); - molc->getAtomRadiiTable()->setCustom("", 1.0); - molc->getAtomRadiiTable()->setCustom("Na1+", 1.5); - molc->getAtomRadiiTable()->setCustom("Cl1-", 1.8); - if (!mnacl) mnacl = loadTestPeriodicStructure("NaCl.stru"); - CxxTest::setAbortTestOnFail(true); - } - - void test_rmin() { - molc->eval(mnacl); - TS_ASSERT(molc->distances().size()); - double tot = molc->totalSquareOverlap(); - TS_ASSERT_LESS_THAN(0.0, tot); - molc->setRmin(10); - molc->eval(); - TS_ASSERT(molc->distances().empty()); - TS_ASSERT_EQUALS(0.0, molc->totalSquareOverlap()); - molc->setRmin(2); - molc->eval(); - TS_ASSERT_EQUALS(tot, molc->totalSquareOverlap()); - } - - void test_rmax() { - molc->setRmax(2.5); - molc->eval(mnacl); - TS_ASSERT(molc->distances().empty()) - TS_ASSERT_EQUALS(0.0, molc->totalSquareOverlap()); - } - - void test_lineNoTouch() { - AtomicStructureAdapterPtr stru(new AtomicStructureAdapter); - Atom a0, a1; - a0.xyz_cartn = R3::Vector(0.0, 0.0, 0.0); - a1.xyz_cartn = R3::Vector(0.0, 0.0, 2.0); - stru->append(a0); - stru->append(a1); - molc->eval(stru); - TS_ASSERT_EQUALS(0.0, molc->totalSquareOverlap()); - QuantityType sqolps = molc->siteSquareOverlaps(); - TS_ASSERT_EQUALS(2u, sqolps.size()); - TS_ASSERT_EQUALS(0.0, sqolps[0]); - TS_ASSERT_EQUALS(0.0, sqolps[1]); - TS_ASSERT_EQUALS(0.0, molc->flipDiffTotal(0, 0)); - TS_ASSERT_EQUALS(0.0, molc->flipDiffTotal(0, 1)); - std::vector g = molc->gradients(); - TS_ASSERT_EQUALS(2u, g.size()); - TS_ASSERT_EQUALS(0.0, R3::norm(g[0])); - TS_ASSERT_EQUALS(0.0, R3::norm(g[1])); - } - - void test_lineTouch() { - AtomicStructureAdapterPtr stru(new AtomicStructureAdapter); - Atom a0, a1; - a0.xyz_cartn = R3::Vector(0.0, 0.0, 0.0); - a1.xyz_cartn = R3::Vector(0.0, 0.0, 1.5); - stru->append(a0); - stru->append(a1); - molc->eval(stru); - TS_ASSERT_EQUALS(0.25, molc->totalSquareOverlap()); - QuantityType sqolps = molc->siteSquareOverlaps(); - TS_ASSERT_EQUALS(2u, sqolps.size()); - TS_ASSERT_EQUALS(0.125, sqolps[0]); - TS_ASSERT_EQUALS(0.125, sqolps[1]); - TS_ASSERT_EQUALS(0.0, molc->flipDiffTotal(0, 1)); - std::vector g = molc->gradients(); - TS_ASSERT_EQUALS(2u, g.size()); - R3::Vector g0(0.0, 0.0, +1); - TS_ASSERT_EQUALS(0.0, R3::distance(g0, g[0])); - R3::Vector g1(0.0, 0.0, -1); - TS_ASSERT_EQUALS(0.0, R3::distance(g1, g[1])); - } - - void test_gradients() { - AtomicStructureAdapterPtr stru(new AtomicStructureAdapter); - Atom a0, a1; - a0.xyz_cartn = R3::Vector(0.0, 0.0, 0.0); - a1.xyz_cartn = R3::Vector(0.17, 0.31, 0.37); - stru->append(a0); - stru->append(a1); - molc->eval(stru); - std::vector g = molc->gradients(); - double dx = 1e-6; - double tsqo0 = molc->totalSquareOverlap(); - R3::Vector r1 = stru->siteCartesianPosition(1); - R3::Vector gn1; - for (int k = 0; k != R3::Ndim; ++k) { - R3::Vector& xyz1 = stru->at(1).xyz_cartn; - xyz1 = r1; - xyz1[k] += dx; - molc->eval(stru); - gn1[k] = (molc->totalSquareOverlap() - tsqo0) / dx; - } - TS_ASSERT(R3::norm(gn1) > 1.0); - TS_ASSERT_DELTA(0.0, R3::norm(gn1 - g[1]), 1e-5); - TS_ASSERT_DELTA(0.0, R3::norm(g[0] + g[1]), 1e-5); - } - - void test_bccTouch() { - PeriodicStructureAdapterPtr bcc(new PeriodicStructureAdapter); - bcc->setLatPar(2.0, 2.0, 2.0, 90, 90, 90); - Atom a; - a.atomtype = "C"; - bcc->append(a); - a.xyz_cartn = R3::Vector(0.5, 0.5, 0.5); - bcc->toCartesian(a); - bcc->append(a); - molc->getAtomRadiiTable()->setCustom("C", 1.0); - molc->eval(bcc); - TS_ASSERT_EQUALS(2, molc->getStructure()->countSites()); - TS_ASSERT_EQUALS(8 * pow(2.0 - sqrt(3.0), 2), molc->totalSquareOverlap()); - // gradient is zero due to bcc symmetry - std::vector g = molc->gradients(); - TS_ASSERT_DELTA(0.0, R3::norm(g[0]), meps); - TS_ASSERT_DELTA(0.0, R3::norm(g[1]), meps); - // move the second atom so it touches only one ball - bcc->at(1).xyz_cartn = R3::Vector(-0.25, 0.0, 0.0); - bcc->toCartesian(bcc->at(1)); - molc->eval(bcc); - double tsqo = 1.5 * 1.5 + 0.5 * 0.5; - TS_ASSERT_DELTA(tsqo, molc->totalSquareOverlap(), meps); - g = molc->gradients(); - TS_ASSERT_EQUALS(2u, g.size()); - double gx = 1.5 + 0.5; - TS_ASSERT_DELTA(-gx, g[0][0], meps); - TS_ASSERT_DELTA(0.0, g[0][1], meps); - TS_ASSERT_DELTA(0.0, g[0][2], meps); - TS_ASSERT_DELTA(+gx, g[1][0], meps); - TS_ASSERT_DELTA(0.0, g[1][1], meps); - TS_ASSERT_DELTA(0.0, g[1][2], meps); - }; - - void test_NaCl_overlap() { - double olp = pow(3.3 - 5.62 / 2, 2) * 6 / 2; - molc->eval(mnacl); - TS_ASSERT_DELTA(olp, molc->meanSquareOverlap(), meps); - QuantityType sqolps = molc->siteSquareOverlaps(); - TS_ASSERT_EQUALS(8u, sqolps.size()); - for (int i = 0; i < 8; ++i) { - TS_ASSERT_DELTA(olp, sqolps[i], meps); - } - } - - void test_NaCl_flips() { - molc->eval(mnacl); - // flipping the same type should not change the cost - TS_ASSERT_EQUALS(0.0, molc->flipDiffTotal(0, 0)); - TS_ASSERT_EQUALS(0.0, molc->flipDiffTotal(0, 1)); - TS_ASSERT_EQUALS(0.0, molc->flipDiffTotal(0, 2)); - TS_ASSERT_EQUALS(0.0, molc->flipDiffTotal(0, 3)); - // flipping with the second Cl neighbor - TS_ASSERT_DELTA(1.08, molc->flipDiffTotal(0, 4), meps); - TS_ASSERT_DELTA(1.08 / 8, molc->flipDiffMean(0, 4), meps); - // flipping with the nearest Cl neighbor - TS_ASSERT_DELTA(0.72, molc->flipDiffTotal(0, 5), meps); - TS_ASSERT_DELTA(0.72, molc->flipDiffTotal(0, 6), meps); - TS_ASSERT_DELTA(0.72, molc->flipDiffTotal(0, 7), meps); - } - - void test_NaCl_gradient() { - molc->eval(mnacl); - // default gradients are all zero - std::vector g = molc->gradients(); - TS_ASSERT_EQUALS(8u, g.size()); - for (int i = 0; i < 8; ++i) { - TS_ASSERT_DELTA(0.0, R3::norm(g[i]), meps); - } - StructureAdapterPtr nacl1 = mnacl->clone(); - PeriodicStructureAdapter& nacl1ref = - static_cast(*nacl1); - R3::Vector& xyzc0 = nacl1ref[0].xyz_cartn; - xyzc0 = R3::Vector(0.02, 0.03, 0.07); - molc->eval(nacl1); - double c0 = molc->totalSquareOverlap(); - R3::Vector g0 = molc->gradients()[0]; - TS_ASSERT(R3::norm(g0) > meps); - R3::Vector g0n; - const double dx = 1e-8; - for (int i = 0; i < R3::Ndim; ++i) { - xyzc0[i] += dx; - molc->eval(nacl1); - g0n[i] = (molc->totalSquareOverlap() - c0) / dx; - xyzc0[i] -= dx; - } - TS_ASSERT_DELTA(0.0, R3::norm(g0 - g0n), 1e-6); - } - - void test_getNeighborSites() { - molc->eval(mnacl); - auto nbsites = molc->getNeighborSites(0); - TS_ASSERT_EQUALS(3u, nbsites.size()); - for (int j = 5; j < 8; ++j) { - TS_ASSERT(nbsites.count(j)); - } - } - - void test_coordinations() { - using std::placeholders::_1; - molc->eval(mnacl); - auto cnums = molc->coordinations(); - TS_ASSERT_EQUALS(8u, cnums.size()); - auto eq6 = bind(equal_to(), _1, 6.0); - TS_ASSERT(all_of(cnums.begin(), cnums.end(), eq6)); - } - - void test_coordinationByTypes() { - molc->eval(mnacl); - auto cbtb0 = molc->coordinationByTypes(0); - auto cbtb3 = molc->coordinationByTypes(3); - auto cbtb5 = molc->coordinationByTypes(5); - TS_ASSERT_EQUALS(1u, cbtb0.size()) - TS_ASSERT_EQUALS(1u, cbtb5.size()) - TS_ASSERT_EQUALS(cbtb0, cbtb3); - TS_ASSERT_EQUALS(6, cbtb0.at("Cl1-")); - TS_ASSERT_EQUALS(6, cbtb5.at("Na1+")); - } - - void test_neighborhoods() { - auto tb = molc->getAtomRadiiTable(); - molc->eval(mnacl); - auto nbhood = molc->neighborhoods(); - TS_ASSERT_EQUALS(1u, nbhood.size()); - TS_ASSERT_EQUALS(8u, nbhood[0].size()); - tb->resetAll(); - molc->eval(mnacl); - auto nbsep = molc->neighborhoods(); - TS_ASSERT_EQUALS(8u, nbsep.size()); - int nb5site = *(nbsep[5].begin()); - TS_ASSERT_EQUALS(5, nb5site); - molc->maskAllPairs(false); - molc->eval(mnacl); - auto nbdark = molc->neighborhoods(); - TS_ASSERT(nbdark.empty()); - molc->setTypeMask("Na1+", "Na1+", true); - molc->eval(mnacl); - auto nbnanasep = molc->neighborhoods(); - TS_ASSERT_EQUALS(4u, nbnanasep.size()); - int nb3site = *(nbnanasep[3].begin()); - TS_ASSERT_EQUALS(3, nb3site); - tb->setCustom("Na1+", 5); - tb->setCustom("Cl1-", 5); - molc->eval(mnacl); - auto nbnana = molc->neighborhoods(); - TS_ASSERT_EQUALS(1u, nbnana.size()); - TS_ASSERT_EQUALS(4u, nbnana[0].size()); - TS_ASSERT(nbnana[0].count(0)); - TS_ASSERT(!nbnana[0].count(4)); - } - - void test_NaCl_mixed_overlap() { - StructureAdapterPtr nacl_mixed; - nacl_mixed = loadTestPeriodicStructure("NaCl_mixed.stru"); - molc->eval(nacl_mixed); - double olp = pow(3.3 - 5.62 / 2, 2) * 6 / 2; - TS_ASSERT_DELTA(olp, molc->meanSquareOverlap(), meps); - } - - void test_serialization() { - // build customized PDFCalculator - AtomicStructureAdapterPtr stru(new AtomicStructureAdapter); - Atom a0, a1; - a0.xyz_cartn = R3::Vector(0.0, 0.0, 0.0); - a1.xyz_cartn = R3::Vector(0.0, 0.0, 1.5); - stru->append(a0); - stru->append(a1); - molc->eval(stru); - std::shared_ptr olc1; - olc1 = dumpandload(molc); - TS_ASSERT_DIFFERS(molc.get(), olc1.get()); - TS_ASSERT_EQUALS(2, olc1->getStructure()->countSites()); - TS_ASSERT_EQUALS(0.25, olc1->totalSquareOverlap()); - QuantityType sqolps = molc->siteSquareOverlaps(); - TS_ASSERT_EQUALS(2u, olc1->siteSquareOverlaps().size()); - TS_ASSERT_EQUALS(0.125, olc1->siteSquareOverlaps()[0]); - TS_ASSERT_EQUALS(0.125, olc1->siteSquareOverlaps()[1]); - } - -}; // class TestOverlapCalculator - -// End of file diff --git a/src/tests/TestPDFBaseline.cpp b/src/tests/TestPDFBaseline.cpp new file mode 100644 index 00000000..31e20bc4 --- /dev/null +++ b/src/tests/TestPDFBaseline.cpp @@ -0,0 +1,89 @@ +/***************************************************************************** + * + * libdiffpy Complex Modeling Initiative + * (c) 2019 Brookhaven Science Associates, + * Brookhaven National Laboratory. + * All rights reserved. + * + * File coded by: Pavol Juhas + * + * See AUTHORS.rst for a list of people who contributed. + * See LICENSE.rst for license information. + * + ****************************************************************************** + * + * TestPDFBaseline -- test PDFBaseline and derived classes + * + *****************************************************************************/ + +#include + +#include +#include +#include +#include "serialization_helpers.hpp" + +namespace diffpy { +namespace srreal { + +////////////////////////////////////////////////////////////////////////////// +// TestPDFBaseline +////////////////////////////////////////////////////////////////////////////// + +class TestPDFBaselineFixture { + protected: + // data + PDFBaselinePtr ml; + LinearBaseline* mcl; + PDFBaselinePtr mz; + ZeroBaseline* mcz; + + public: + TestPDFBaselineFixture() { + ml = PDFBaseline::createByType("linear"); + mcl = static_cast(ml.get()); + mz = PDFBaseline::createByType("zero"); + mcz = static_cast(mz.get()); + } +}; // class TestPDFBaselineFixture + +TEST_CASE_METHOD(TestPDFBaselineFixture, "TestPDFBaseline::test_clone", + "[PDFBaseline]") { + CHECK("linear" == ml->clone()->type()); + CHECK("zero" == mz->clone()->type()); +} + +TEST_CASE_METHOD(TestPDFBaselineFixture, "TestPDFBaseline::test_attributes", + "[PDFBaseline]") { + CHECK(1 == ml->namesOfDoubleAttributes().size()); + CHECK(0 == mz->namesOfDoubleAttributes().size()); + mcl->setSlope(0.1); + CHECK(0.1 == ml->getDoubleAttr("slope")); + ml->setDoubleAttr("slope", 0.2); + CHECK(0.2 == mcl->getSlope()); +} + +TEST_CASE_METHOD(TestPDFBaselineFixture, "TestPDFBaseline::test_calculate", + "[PDFBaseline]") { + CHECK(0.0 == (*ml)(2.0)); + CHECK(0.0 == (*mz)(2.0)); + mcl->setSlope(-2); + CHECK(-4.0 == (*ml)(2.0)); +} + +TEST_CASE_METHOD(TestPDFBaselineFixture, "TestPDFBaseline::test_serialization", + "[PDFBaseline]") { + mcl->setSlope(0.3); + PDFBaselinePtr bl1 = dumpandload(ml); + LinearBaseline* cbl1 = dynamic_cast(bl1.get()); + CHECK(cbl1); + CHECK(0.3 == cbl1->getSlope()); + PDFBaselinePtr bz1 = dumpandload(mz); + ZeroBaseline* cbz1 = dynamic_cast(bz1.get()); + CHECK(cbz1); +} + +} // namespace srreal +} // namespace diffpy + +// End of file diff --git a/src/tests/TestPDFBaseline.hpp b/src/tests/TestPDFBaseline.hpp deleted file mode 100644 index f459663a..00000000 --- a/src/tests/TestPDFBaseline.hpp +++ /dev/null @@ -1,88 +0,0 @@ -/***************************************************************************** - * - * libdiffpy Complex Modeling Initiative - * (c) 2019 Brookhaven Science Associates, - * Brookhaven National Laboratory. - * All rights reserved. - * - * File coded by: Pavol Juhas - * - * See AUTHORS.rst for a list of people who contributed. - * See LICENSE.rst for license information. - * - ****************************************************************************** - * - * class TestPDFBaseline -- test PDFBaseline and derived classes - * - *****************************************************************************/ - -#include - -#include -#include -#include -#include "serialization_helpers.hpp" - -namespace diffpy { -namespace srreal { - -////////////////////////////////////////////////////////////////////////////// -// class TestPDFBaseline -////////////////////////////////////////////////////////////////////////////// - -class TestPDFBaseline : public CxxTest::TestSuite { - private: - // data - PDFBaselinePtr ml; - LinearBaseline* mcl; - PDFBaselinePtr mz; - ZeroBaseline* mcz; - - public: - void setUp() { - ml = PDFBaseline::createByType("linear"); - mcl = static_cast(ml.get()); - mz = PDFBaseline::createByType("zero"); - mcz = static_cast(mz.get()); - } - - void test_clone() { - TS_ASSERT_EQUALS("linear", ml->clone()->type()); - TS_ASSERT_EQUALS("zero", mz->clone()->type()); - } - - void test_attributes() { - TS_ASSERT_EQUALS(1, ml->namesOfDoubleAttributes().size()); - TS_ASSERT_EQUALS(0, mz->namesOfDoubleAttributes().size()); - mcl->setSlope(0.1); - TS_ASSERT_EQUALS(0.1, ml->getDoubleAttr("slope")); - ml->setDoubleAttr("slope", 0.2); - TS_ASSERT_EQUALS(0.2, mcl->getSlope()); - } - - void test_calculate() { - TS_ASSERT_EQUALS(0.0, (*ml)(2.0)); - TS_ASSERT_EQUALS(0.0, (*mz)(2.0)); - mcl->setSlope(-2); - TS_ASSERT_EQUALS(-4.0, (*ml)(2.0)); - } - - void test_serialization() { - mcl->setSlope(0.3); - PDFBaselinePtr bl1 = dumpandload(ml); - LinearBaseline* cbl1 = dynamic_cast(bl1.get()); - TS_ASSERT(cbl1); - TS_ASSERT_EQUALS(0.3, cbl1->getSlope()); - PDFBaselinePtr bz1 = dumpandload(mz); - ZeroBaseline* cbz1 = dynamic_cast(bz1.get()); - TS_ASSERT(cbz1); - } - -}; // class TestPDFBaseline - -} // namespace srreal -} // namespace diffpy - -using diffpy::srreal::TestPDFBaseline; - -// End of file diff --git a/src/tests/TestPDFCalculator.cpp b/src/tests/TestPDFCalculator.cpp new file mode 100644 index 00000000..1de0e5ed --- /dev/null +++ b/src/tests/TestPDFCalculator.cpp @@ -0,0 +1,265 @@ +/***************************************************************************** + * + * libdiffpy by DANSE Diffraction group + * Simon J. L. Billinge + * (c) 2009 The Trustees of Columbia University + * in the City of New York. All rights reserved. + * + * File coded by: Pavol Juhas + * + * See AUTHORS.rst for a list of people who contributed. + * See LICENSE_DANSE.rst for license information. + * + ****************************************************************************** + * + * TestPDFCalculator -- unit tests for PDFCalculator class + * + *****************************************************************************/ + +#include +#include + +#include +#include +#include +#include +#include +#include +#include "test_helpers.hpp" + +using Catch::Matchers::WithinAbs; + +using namespace std; +using namespace diffpy::srreal; + +class TestPDFCalculatorFixture { + protected: + std::shared_ptr mpdfc; + StructureAdapterPtr memptystru; + double meps; + double mepsdb; + + public: + TestPDFCalculatorFixture() { + memptystru = emptyStructureAdapter(); + meps = diffpy::mathutils::SQRT_DOUBLE_EPS; + mepsdb = 10 * diffpy::mathutils::DOUBLE_EPS; + mpdfc.reset(new PDFCalculator); + } +}; // class TestPDFCalculatorFixture + +TEST_CASE_METHOD(TestPDFCalculatorFixture, + "TestPDFCalculator::test_setPeakWidthModel", + "[PDFCalculator]") { + const PeakWidthModelPtr& jpw0 = mpdfc->getPeakWidthModel(); + CHECK(0.0 == jpw0->getDoubleAttr("delta1")); + CHECK(0.0 == jpw0->getDoubleAttr("delta2")); + CHECK(0.0 == jpw0->getDoubleAttr("qbroad")); + CHECK(0.0 == jpw0->getDoubleAttr("qbroad_seperable")); + JeongPeakWidth jpw; + jpw.setDelta1(1.0); + jpw.setDelta2(2.0); + jpw.setQbroad(3.0); + jpw.setQbroad_seperable(4.0); + mpdfc->setPeakWidthModel(jpw.clone()); + const PeakWidthModelPtr& jpw1 = mpdfc->getPeakWidthModel(); + CHECK(1.0 == jpw1->getDoubleAttr("delta1")); + CHECK(2.0 == jpw1->getDoubleAttr("delta2")); + CHECK(3.0 == jpw1->getDoubleAttr("qbroad")); + CHECK(4.0 == jpw1->getDoubleAttr("qbroad_seperable")); +} + +TEST_CASE_METHOD(TestPDFCalculatorFixture, + "TestPDFCalculator::test_getPeakWidthModel", + "[PDFCalculator]") { + string tp = "jeong"; + CHECK(tp == mpdfc->getPeakWidthModel()->type()); + PeakWidthModelPtr pwm(PeakWidthModel::createByType("debye-waller")); + mpdfc->setPeakWidthModel(pwm); + tp = "debye-waller"; + CHECK(tp == mpdfc->getPeakWidthModel()->type()); + mpdfc->setPeakWidthModel(ConstantPeakWidth().clone()); + tp = "constant"; + CHECK(tp == mpdfc->getPeakWidthModel()->type()); +} + +TEST_CASE_METHOD(TestPDFCalculatorFixture, + "TestPDFCalculator::test_access_PeakProfile", + "[PDFCalculator]") { + PeakProfilePtr pkf(mpdfc->getPeakProfile()->clone()); + pkf->setPrecision(1.1); + mpdfc->setPeakProfile(pkf); + CHECK(1.1 == mpdfc->getPeakProfile()->getPrecision()); + CHECK(1.1 == mpdfc->getDoubleAttr("peakprecision")); + mpdfc->setDoubleAttr("peakprecision", 0.2); + CHECK(0.2 == mpdfc->getDoubleAttr("peakprecision")); + CHECK(pkf->type() == mpdfc->getPeakProfile()->type()); + mpdfc->setPeakProfileByType("gaussian"); + CHECK(0.2 == mpdfc->getDoubleAttr("peakprecision")); + CHECK_THROWS_AS(mpdfc->setPeakProfileByType("invalid"), logic_error); +} + +TEST_CASE_METHOD(TestPDFCalculatorFixture, + "TestPDFCalculator::test_access_Envelopes", + "[PDFCalculator]") { + CHECK(2u == mpdfc->usedEnvelopeTypes().size()); + CHECK(1.0 == mpdfc->getDoubleAttr("scale")); + CHECK(0.0 == mpdfc->getDoubleAttr("qdamp")); + mpdfc->setDoubleAttr("scale", 3.0); + CHECK(3.0 == mpdfc->getDoubleAttr("scale")); + mpdfc->addEnvelopeByType("scale"); + CHECK(1.0 == mpdfc->getDoubleAttr("scale")); + QResolutionEnvelope qdamp4; + qdamp4.setQdamp(4); + mpdfc->addEnvelope(qdamp4.clone()); + CHECK(4.0 == mpdfc->getDoubleAttr("qdamp")); + CHECK_THROWS_AS(mpdfc->addEnvelopeByType("invalid"), logic_error); +} + +TEST_CASE_METHOD(TestPDFCalculatorFixture, "TestPDFCalculator::test_getPDF", + "[PDFCalculator]") { + QuantityType pdf; + pdf = mpdfc->getPDF(); + CHECK(1000u == pdf.size()); + mpdfc->setRmin(2.0); + mpdfc->setRmax(0.0); + mpdfc->eval(memptystru); + pdf = mpdfc->getPDF(); + CHECK(pdf.empty()); + mpdfc->setRmax(2.0); + mpdfc->eval(memptystru); + pdf = mpdfc->getPDF(); + CHECK(pdf.empty()); + mpdfc->setRmax(2.01001); + mpdfc->eval(memptystru); + pdf = mpdfc->getPDF(); + CHECK(2u == pdf.size()); + CHECK(0.0 == pdf[0]); + CHECK(0.0 == pdf[1]); +} + +TEST_CASE_METHOD(TestPDFCalculatorFixture, "TestPDFCalculator::test_getRDF", + "[PDFCalculator]") { + QuantityType rdf = mpdfc->getRDF(); + CHECK(1000u == rdf.size()); + CHECK(0.0 == *min_element(rdf.begin(), rdf.end())); + CHECK(0.0 == *max_element(rdf.begin(), rdf.end())); + mpdfc->eval(memptystru); + rdf = mpdfc->getRDF(); + CHECK(1000u == rdf.size()); + CHECK(0.0 == *min_element(rdf.begin(), rdf.end())); + CHECK(0.0 == *max_element(rdf.begin(), rdf.end())); + mpdfc->setRmin(2.0); + mpdfc->setRmax(0.0); + mpdfc->eval(memptystru); + rdf = mpdfc->getRDF(); + CHECK(rdf.empty()); + mpdfc->setRmax(2.01001); + mpdfc->eval(memptystru); + rdf = mpdfc->getRDF(); + CHECK(2u == rdf.size()); + CHECK(0.0 == rdf[0]); + CHECK(0.0 == rdf[1]); +} + +TEST_CASE_METHOD(TestPDFCalculatorFixture, "TestPDFCalculator::test_getF", + "[PDFCalculator]") { + QuantityType fq = mpdfc->getF(); + CHECK(1024u == fq.size()); + CHECK(0.0 == *min_element(fq.begin(), fq.end())); + CHECK(0.0 == *max_element(fq.begin(), fq.end())); +} + +TEST_CASE_METHOD(TestPDFCalculatorFixture, "TestPDFCalculator::test_getQgrid", + "[PDFCalculator]") { + CHECK(1024u == mpdfc->getQgrid().size()); +} + +TEST_CASE_METHOD(TestPDFCalculatorFixture, "TestPDFCalculator::test_getQmin", + "[PDFCalculator]") { + CHECK(0.0 == mpdfc->getQmin()); +} + +TEST_CASE_METHOD(TestPDFCalculatorFixture, "TestPDFCalculator::test_getQmax", + "[PDFCalculator]") { + CHECK_THAT(100 * M_PI, WithinAbs(mpdfc->getQmax(), meps)); +} + +TEST_CASE_METHOD(TestPDFCalculatorFixture, "TestPDFCalculator::test_getQstep", + "[PDFCalculator]") { + const double qstep0 = 100 * M_PI / 1024; + const double qstep1 = 100 * M_PI / 2048; + const double qstep2 = 100 * M_PI / 4096; + const double qstep3 = M_PI / (1024 * 0.03); + CHECK_THAT(qstep0, WithinAbs(mpdfc->getQstep(), meps)); + mpdfc->setRmax(20); + CHECK_THAT(qstep1, WithinAbs(mpdfc->getQstep(), meps)); + mpdfc->setQmax(10); + CHECK_THAT(qstep2, WithinAbs(mpdfc->getQstep(), meps)); + mpdfc->setRstep(0.03); + CHECK_THAT(qstep3, WithinAbs(mpdfc->getQstep(), meps)); + // test qstep after evaluation of some structure. + StructureAdapterPtr catio3; + catio3 = loadTestPeriodicStructure("CaTiO3.stru"); + mpdfc->eval(catio3); + CHECK_THAT(qstep3, WithinAbs(mpdfc->getQstep(), meps)); + // now qstep is not updated because of non-trivial structure. + mpdfc->setRstep(0.01); + CHECK(qstep2 != mpdfc->getQstep()); + // qstep is correct after calculation. + mpdfc->eval(); + CHECK_THAT(qstep2, WithinAbs(mpdfc->getQstep(), meps)); +} + +TEST_CASE_METHOD(TestPDFCalculatorFixture, "TestPDFCalculator::test_getRgrid", + "[PDFCalculator]") { + QuantityType rgrid0 = mpdfc->getRgrid(); + CHECK(rgrid0 == mpdfc->getRgrid()); + mpdfc->setRmin(5); + mpdfc->setRmax(4); + CHECK(mpdfc->getRgrid().empty()); +} + +TEST_CASE_METHOD(TestPDFCalculatorFixture, + "TestPDFCalculator::test_getExtendedRmin", "[PDFCalculator]") { + // empty structure should not extend the grid at all. + CHECK(0.0 == mpdfc->getExtendedRmin()); + mpdfc->setRmin(5); + mpdfc->eval(memptystru); + CHECK_THAT(5.0, WithinAbs(mpdfc->getExtendedRmin(), mepsdb)); +} + +TEST_CASE_METHOD(TestPDFCalculatorFixture, + "TestPDFCalculator::test_getExtendedRmax", "[PDFCalculator]") { + // empty structure should not extend the grid at all. + CHECK_THAT(10.0, WithinAbs(mpdfc->getExtendedRmax(), mepsdb)); + mpdfc->setRmax(7); + mpdfc->eval(memptystru); + CHECK_THAT(7.0, WithinAbs(mpdfc->getExtendedRmax(), mepsdb)); +} + +TEST_CASE_METHOD(TestPDFCalculatorFixture, + "TestPDFCalculator::test_serialization", "[PDFCalculator]") { + // build customized PDFCalculator + mpdfc->setPeakWidthModelByType("constant"); + mpdfc->setDoubleAttr("width", 0.123); + mpdfc->setPeakProfileByType("gaussian"); + mpdfc->setDoubleAttr("peakprecision", 0.011); + mpdfc->setScatteringFactorTableByType("electronnumber"); + mpdfc->getScatteringFactorTable()->setCustomAs("H", "H", 1.1); + // dump it to string + stringstream storage(ios::in | ios::out | ios::binary); + diffpy::serialization::oarchive oa(storage, ios::binary); + oa << mpdfc; + diffpy::serialization::iarchive ia(storage, ios::binary); + std::shared_ptr pdfc1; + ia >> pdfc1; + CHECK(pdfc1.get() != mpdfc.get()); + CHECK(string("constant") == pdfc1->getPeakWidthModel()->type()); + CHECK(0.123 == pdfc1->getDoubleAttr("width")); + CHECK(0.011 == pdfc1->getDoubleAttr("peakprecision")); + CHECK(string("electronnumber") == pdfc1->getScatteringFactorTable()->type()); + CHECK(1.1 == pdfc1->getScatteringFactorTable()->lookup("H")); +} + +// End of file diff --git a/src/tests/TestPDFCalculator.hpp b/src/tests/TestPDFCalculator.hpp deleted file mode 100644 index 818bb024..00000000 --- a/src/tests/TestPDFCalculator.hpp +++ /dev/null @@ -1,239 +0,0 @@ -/***************************************************************************** - * - * libdiffpy by DANSE Diffraction group - * Simon J. L. Billinge - * (c) 2009 The Trustees of Columbia University - * in the City of New York. All rights reserved. - * - * File coded by: Pavol Juhas - * - * See AUTHORS.rst for a list of people who contributed. - * See LICENSE_DANSE.rst for license information. - * - ****************************************************************************** - * - * class TestPDFCalculator -- unit tests for PDFCalculator class - * - *****************************************************************************/ - -#include - -#include -#include -#include -#include -#include -#include -#include "test_helpers.hpp" - -using namespace std; -using namespace diffpy::srreal; - -class TestPDFCalculator : public CxxTest::TestSuite { - private: - std::shared_ptr mpdfc; - StructureAdapterPtr memptystru; - double meps; - double mepsdb; - - public: - void setUp() { - memptystru = emptyStructureAdapter(); - meps = diffpy::mathutils::SQRT_DOUBLE_EPS; - mepsdb = 10 * diffpy::mathutils::DOUBLE_EPS; - mpdfc.reset(new PDFCalculator); - } - - void test_setPeakWidthModel() { - const PeakWidthModelPtr& jpw0 = mpdfc->getPeakWidthModel(); - TS_ASSERT_EQUALS(0.0, jpw0->getDoubleAttr("delta1")); - TS_ASSERT_EQUALS(0.0, jpw0->getDoubleAttr("delta2")); - TS_ASSERT_EQUALS(0.0, jpw0->getDoubleAttr("qbroad")); - TS_ASSERT_EQUALS(0.0, jpw0->getDoubleAttr("qbroad_seperable")); - JeongPeakWidth jpw; - jpw.setDelta1(1.0); - jpw.setDelta2(2.0); - jpw.setQbroad(3.0); - jpw.setQbroad_seperable(4.0); - mpdfc->setPeakWidthModel(jpw.clone()); - const PeakWidthModelPtr& jpw1 = mpdfc->getPeakWidthModel(); - TS_ASSERT_EQUALS(1.0, jpw1->getDoubleAttr("delta1")); - TS_ASSERT_EQUALS(2.0, jpw1->getDoubleAttr("delta2")); - TS_ASSERT_EQUALS(3.0, jpw1->getDoubleAttr("qbroad")); - TS_ASSERT_EQUALS(4.0, jpw1->getDoubleAttr("qbroad_seperable")); - } - - void test_getPeakWidthModel() { - string tp = "jeong"; - TS_ASSERT_EQUALS(tp, mpdfc->getPeakWidthModel()->type()); - PeakWidthModelPtr pwm(PeakWidthModel::createByType("debye-waller")); - mpdfc->setPeakWidthModel(pwm); - tp = "debye-waller"; - TS_ASSERT_EQUALS(tp, mpdfc->getPeakWidthModel()->type()); - mpdfc->setPeakWidthModel(ConstantPeakWidth().clone()); - tp = "constant"; - TS_ASSERT_EQUALS(tp, mpdfc->getPeakWidthModel()->type()); - } - - void test_access_PeakProfile() { - PeakProfilePtr pkf(mpdfc->getPeakProfile()->clone()); - pkf->setPrecision(1.1); - mpdfc->setPeakProfile(pkf); - TS_ASSERT_EQUALS(1.1, mpdfc->getPeakProfile()->getPrecision()); - TS_ASSERT_EQUALS(1.1, mpdfc->getDoubleAttr("peakprecision")); - mpdfc->setDoubleAttr("peakprecision", 0.2); - TS_ASSERT_EQUALS(0.2, mpdfc->getDoubleAttr("peakprecision")); - TS_ASSERT_EQUALS(pkf->type(), mpdfc->getPeakProfile()->type()); - mpdfc->setPeakProfileByType("gaussian"); - TS_ASSERT_EQUALS(0.2, mpdfc->getDoubleAttr("peakprecision")); - TS_ASSERT_THROWS(mpdfc->setPeakProfileByType("invalid"), logic_error); - } - - void test_access_Envelopes() { - TS_ASSERT_EQUALS(2u, mpdfc->usedEnvelopeTypes().size()); - TS_ASSERT_EQUALS(1.0, mpdfc->getDoubleAttr("scale")); - TS_ASSERT_EQUALS(0.0, mpdfc->getDoubleAttr("qdamp")); - mpdfc->setDoubleAttr("scale", 3.0); - TS_ASSERT_EQUALS(3.0, mpdfc->getDoubleAttr("scale")); - mpdfc->addEnvelopeByType("scale"); - TS_ASSERT_EQUALS(1.0, mpdfc->getDoubleAttr("scale")); - QResolutionEnvelope qdamp4; - qdamp4.setQdamp(4); - mpdfc->addEnvelope(qdamp4.clone()); - TS_ASSERT_EQUALS(4.0, mpdfc->getDoubleAttr("qdamp")); - TS_ASSERT_THROWS(mpdfc->addEnvelopeByType("invalid"), logic_error); - } - - void test_getPDF() { - QuantityType pdf; - pdf = mpdfc->getPDF(); - TS_ASSERT_EQUALS(1000u, pdf.size()); - mpdfc->setRmin(2.0); - mpdfc->setRmax(0.0); - mpdfc->eval(memptystru); - pdf = mpdfc->getPDF(); - TS_ASSERT(pdf.empty()); - mpdfc->setRmax(2.0); - mpdfc->eval(memptystru); - pdf = mpdfc->getPDF(); - TS_ASSERT(pdf.empty()); - mpdfc->setRmax(2.01001); - mpdfc->eval(memptystru); - pdf = mpdfc->getPDF(); - TS_ASSERT_EQUALS(2u, pdf.size()); - TS_ASSERT_EQUALS(0.0, pdf[0]); - TS_ASSERT_EQUALS(0.0, pdf[1]); - } - - void test_getRDF() { - QuantityType rdf = mpdfc->getRDF(); - TS_ASSERT_EQUALS(1000u, rdf.size()); - TS_ASSERT_EQUALS(0.0, *min_element(rdf.begin(), rdf.end())); - TS_ASSERT_EQUALS(0.0, *max_element(rdf.begin(), rdf.end())); - mpdfc->eval(memptystru); - rdf = mpdfc->getRDF(); - TS_ASSERT_EQUALS(1000u, rdf.size()); - TS_ASSERT_EQUALS(0.0, *min_element(rdf.begin(), rdf.end())); - TS_ASSERT_EQUALS(0.0, *max_element(rdf.begin(), rdf.end())); - mpdfc->setRmin(2.0); - mpdfc->setRmax(0.0); - mpdfc->eval(memptystru); - rdf = mpdfc->getRDF(); - TS_ASSERT(rdf.empty()); - mpdfc->setRmax(2.01001); - mpdfc->eval(memptystru); - rdf = mpdfc->getRDF(); - TS_ASSERT_EQUALS(2u, rdf.size()); - TS_ASSERT_EQUALS(0.0, rdf[0]); - TS_ASSERT_EQUALS(0.0, rdf[1]); - } - - void test_getF() { - QuantityType fq = mpdfc->getF(); - TS_ASSERT_EQUALS(1024u, fq.size()); - TS_ASSERT_EQUALS(0.0, *min_element(fq.begin(), fq.end())); - TS_ASSERT_EQUALS(0.0, *max_element(fq.begin(), fq.end())); - } - - void test_getQgrid() { TS_ASSERT_EQUALS(1024u, mpdfc->getQgrid().size()); } - - void test_getQmin() { TS_ASSERT_EQUALS(0.0, mpdfc->getQmin()); } - - void test_getQmax() { TS_ASSERT_DELTA(100 * M_PI, mpdfc->getQmax(), meps); } - - void test_getQstep() { - const double qstep0 = 100 * M_PI / 1024; - const double qstep1 = 100 * M_PI / 2048; - const double qstep2 = 100 * M_PI / 4096; - const double qstep3 = M_PI / (1024 * 0.03); - TS_ASSERT_DELTA(qstep0, mpdfc->getQstep(), meps); - mpdfc->setRmax(20); - TS_ASSERT_DELTA(qstep1, mpdfc->getQstep(), meps); - mpdfc->setQmax(10); - TS_ASSERT_DELTA(qstep2, mpdfc->getQstep(), meps); - mpdfc->setRstep(0.03); - TS_ASSERT_DELTA(qstep3, mpdfc->getQstep(), meps); - // test qstep after evaluation of some structure. - StructureAdapterPtr catio3; - catio3 = loadTestPeriodicStructure("CaTiO3.stru"); - mpdfc->eval(catio3); - TS_ASSERT_DELTA(qstep3, mpdfc->getQstep(), meps); - // now qstep is not updated because of non-trivial structure. - mpdfc->setRstep(0.01); - TS_ASSERT_DIFFERS(qstep2, mpdfc->getQstep()); - // qstep is correct after calculation. - mpdfc->eval(); - TS_ASSERT_DELTA(qstep2, mpdfc->getQstep(), meps); - } - - void test_getRgrid() { - QuantityType rgrid0 = mpdfc->getRgrid(); - TS_ASSERT_EQUALS(rgrid0, mpdfc->getRgrid()); - mpdfc->setRmin(5); - mpdfc->setRmax(4); - TS_ASSERT(mpdfc->getRgrid().empty()); - } - - void test_getExtendedRmin() { - // empty structure should not extend the grid at all. - TS_ASSERT_EQUALS(0.0, mpdfc->getExtendedRmin()); - mpdfc->setRmin(5); - mpdfc->eval(memptystru); - TS_ASSERT_DELTA(5.0, mpdfc->getExtendedRmin(), mepsdb); - } - - void test_getExtendedRmax() { - // empty structure should not extend the grid at all. - TS_ASSERT_DELTA(10.0, mpdfc->getExtendedRmax(), mepsdb); - mpdfc->setRmax(7); - mpdfc->eval(memptystru); - TS_ASSERT_DELTA(7.0, mpdfc->getExtendedRmax(), mepsdb); - } - - void test_serialization() { - // build customized PDFCalculator - mpdfc->setPeakWidthModelByType("constant"); - mpdfc->setDoubleAttr("width", 0.123); - mpdfc->setPeakProfileByType("gaussian"); - mpdfc->setDoubleAttr("peakprecision", 0.011); - mpdfc->setScatteringFactorTableByType("electronnumber"); - mpdfc->getScatteringFactorTable()->setCustomAs("H", "H", 1.1); - // dump it to string - stringstream storage(ios::in | ios::out | ios::binary); - diffpy::serialization::oarchive oa(storage, ios::binary); - oa << mpdfc; - diffpy::serialization::iarchive ia(storage, ios::binary); - std::shared_ptr pdfc1; - ia >> pdfc1; - TS_ASSERT_DIFFERS(pdfc1.get(), mpdfc.get()); - TS_ASSERT_EQUALS(string("constant"), pdfc1->getPeakWidthModel()->type()); - TS_ASSERT_EQUALS(0.123, pdfc1->getDoubleAttr("width")); - TS_ASSERT_EQUALS(0.011, pdfc1->getDoubleAttr("peakprecision")); - TS_ASSERT_EQUALS(string("electronnumber"), - pdfc1->getScatteringFactorTable()->type()); - TS_ASSERT_EQUALS(1.1, pdfc1->getScatteringFactorTable()->lookup("H")); - } - -}; // class TestPDFCalculator - -// End of file diff --git a/src/tests/TestPQEvaluator.cpp b/src/tests/TestPQEvaluator.cpp new file mode 100644 index 00000000..c2884f1e --- /dev/null +++ b/src/tests/TestPQEvaluator.cpp @@ -0,0 +1,278 @@ +/***************************************************************************** + * + * libdiffpy Complex Modeling Initiative + * (c) 2013 Brookhaven Science Associates, + * Brookhaven National Laboratory. + * All rights reserved. + * + * File coded by: Pavol Juhas + * + * See AUTHORS.rst for a list of people who contributed. + * See LICENSE.rst for license information. + * + ****************************************************************************** + * + * TestPQEvaluator -- unit tests for the PairQuantity evaluator class + * + *****************************************************************************/ + +#include + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include "test_helpers.hpp" + +namespace diffpy { +namespace srreal { + +using namespace std; +using diffpy::mathutils::EpsilonEqual; + +// calculator with invalid support for OPTIMIZED evaluation + +class BadPairCounter : public PairCounter { + protected: + // intentionally faulty support for PQEvaluatorOptimized + virtual void stashPartialValue() {} + virtual void restorePartialValue() {} +}; + +////////////////////////////////////////////////////////////////////////////// +// TestPQEvaluator +////////////////////////////////////////////////////////////////////////////// + +class TestPQEvaluatorFixture { + protected: + // data + EpsilonEqual allclose; + PDFCalculator mpdfcb; + PDFCalculator mpdfco; + QuantityType mzeros; + AtomicStructureAdapterPtr mstru10; + AtomicStructureAdapterPtr mstru10d1; + AtomicStructureAdapterPtr mstru10r; + AtomicStructureAdapterPtr mstru9; + + // methods + QuantityType pdfcdiff(StructureAdapterPtr stru) { + mpdfcb.eval(stru); + mpdfco.eval(stru); + QuantityType gb = mpdfcb.getPDF(); + QuantityType go = mpdfco.getPDF(); + assert(mpdfcb.getRgrid() == mpdfco.getRgrid()); + QuantityType rv(gb.size()); + transform(gb.begin(), gb.end(), go.begin(), rv.begin(), minus()); + return rv; + } + + public: + TestPQEvaluatorFixture() { + // setup PDFCalculator instances and the zero vector for comparison + mpdfcb.setEvaluatorType(BASIC); + mpdfco.setEvaluatorType(OPTIMIZED); + mzeros.assign(mpdfcb.getRgrid().size(), 0.0); + // setup structure instances + const int SZ = 10; + mstru10 = std::make_shared(); + Atom ai; + ai.atomtype = "C"; + ai.uij_cartn = R3::identity(); + ai.uij_cartn(0, 0) = ai.uij_cartn(1, 1) = ai.uij_cartn(2, 2) = 0.004; + for (int i = 0; i < SZ; ++i) { + ai.xyz_cartn[0] = i; + mstru10->append(ai); + } + mstru10d1 = std::make_shared(*mstru10); + (*mstru10d1)[0].atomtype = "Au"; + mstru10r = std::make_shared(); + mstru10r->assign(mstru10->rbegin(), mstru10->rend()); + mstru9 = std::make_shared(*mstru10); + mstru9->erase(9); + } +}; // class TestPQEvaluatorFixture + +TEST_CASE_METHOD(TestPQEvaluatorFixture, + "TestPQEvaluator::test_initial_evaluator_type_used", + "[PQEvaluator]") { + PDFCalculator pdfc; + CHECK(NONE == pdfc.getEvaluatorTypeUsed()); + pdfc.setEvaluatorType(OPTIMIZED); + CHECK(NONE == pdfc.getEvaluatorTypeUsed()); + pdfc.setEvaluatorType(PQEvaluatorType::CHECK); + CHECK(NONE == pdfc.getEvaluatorTypeUsed()); + pdfc.setEvaluatorType(BASIC); + CHECK(NONE == pdfc.getEvaluatorTypeUsed()); +} + +TEST_CASE_METHOD(TestPQEvaluatorFixture, + "TestPQEvaluator::test_PDF_change_atom", "[PQEvaluator]") { + using std::placeholders::_1; + CHECK(BASIC == mpdfcb.getEvaluatorType()); + CHECK(OPTIMIZED == mpdfco.getEvaluatorType()); + CHECK(mzeros == this->pdfcdiff(mstru10)); + // first call of mpdfco should use the BASIC evaluation + CHECK(BASIC == mpdfcb.getEvaluatorTypeUsed()); + CHECK(BASIC == mpdfco.getEvaluatorTypeUsed()); + // verify there are some non-zero values in the PDF. + QuantityType gb = mpdfcb.getPDF(); + CHECK(!gb.empty()); + int cnonzero = + count_if(gb.begin(), gb.end(), bind(not_equal_to(), _1, 0.0)); + CHECK(cnonzero); + // test second call on the same structure + mpdfco.eval(mstru10); + CHECK(gb == mpdfco.getPDF()); + CHECK(OPTIMIZED == mpdfco.getEvaluatorTypeUsed()); + // test structure with one different atom + CHECK(allclose(mzeros, this->pdfcdiff(mstru10d1))); + CHECK(OPTIMIZED == mpdfco.getEvaluatorTypeUsed()); + QuantityType gb1 = mpdfcb.getPDF(); + CHECK(!allclose(gb, gb1)); + // change position of 1 atom + mstru10d1->at(0).xyz_cartn[1] = 0.5; + CHECK(allclose(mzeros, this->pdfcdiff(mstru10d1))); + CHECK(OPTIMIZED == mpdfco.getEvaluatorTypeUsed()); + QuantityType gb2 = mpdfcb.getPDF(); + CHECK(!allclose(gb1, gb2)); +} + +TEST_CASE_METHOD(TestPQEvaluatorFixture, + "TestPQEvaluator::test_PDF_reverse_atoms", "[PQEvaluator]") { + mpdfco.eval(mstru10); + CHECK(allclose(mzeros, this->pdfcdiff(mstru10r))); + CHECK(OPTIMIZED == mpdfco.getEvaluatorTypeUsed()); +} + +TEST_CASE_METHOD(TestPQEvaluatorFixture, + "TestPQEvaluator::test_PDF_remove_atom", "[PQEvaluator]") { + mpdfco.eval(mstru10); + CHECK(allclose(mzeros, this->pdfcdiff(mstru9))); + CHECK(OPTIMIZED == mpdfco.getEvaluatorTypeUsed()); +} + +TEST_CASE_METHOD(TestPQEvaluatorFixture, "TestPQEvaluator::test_PDF_type_mask", + "[PQEvaluator]") { + mpdfcb.setTypeMask("O2-", "all", false); + mpdfco.setTypeMask("O2-", "all", false); + PeriodicStructureAdapterPtr litao = + std::dynamic_pointer_cast( + loadTestPeriodicStructure("LiTaO3.stru")); + CHECK(mzeros == this->pdfcdiff(litao)); + CHECK(BASIC == mpdfco.getEvaluatorTypeUsed()); + QuantityType gb0 = mpdfcb.getPDF(); + // change masked-away oxygen + Atom& o29 = litao->at(29); + CHECK("O2-" == o29.atomtype); + o29.xyz_cartn = R3::Vector(0.1, 0.2, 0.3); + CHECK(mzeros == this->pdfcdiff(litao)); + CHECK(OPTIMIZED == mpdfco.getEvaluatorTypeUsed()); + QuantityType gb1 = mpdfcb.getPDF(); + CHECK(gb0 == gb1); + // change active lithium + Atom& li0 = litao->at(0); + li0.occupancy = 0.1; + CHECK(allclose(mzeros, this->pdfcdiff(litao))); + CHECK(OPTIMIZED == mpdfco.getEvaluatorTypeUsed()); + QuantityType gb2 = mpdfcb.getPDF(); + CHECK(!allclose(gb0, gb2)); +} + +TEST_CASE_METHOD(TestPQEvaluatorFixture, "TestPQEvaluator::test_PDF_pair_mask", + "[PQEvaluator]") { + mpdfcb.setPairMask(0, 3, false); + mpdfco.setPairMask(0, 3, false); + CHECK(mzeros == this->pdfcdiff(mstru10)); + CHECK(BASIC == mpdfco.getEvaluatorTypeUsed()); + QuantityType gb0 = mpdfcb.getPDF(); + // check structure with one changed atom + CHECK(allclose(mzeros, this->pdfcdiff(mstru10d1))); + CHECK(OPTIMIZED == mpdfco.getEvaluatorTypeUsed()); + QuantityType gb1 = mpdfcb.getPDF(); + CHECK(!allclose(gb0, gb1)); + // check structure with one removed atom + CHECK(allclose(mzeros, this->pdfcdiff(mstru9))); + CHECK(OPTIMIZED == mpdfco.getEvaluatorTypeUsed()); + QuantityType gb2 = mpdfcb.getPDF(); + CHECK(!allclose(gb1, gb2)); + // check full evaluation for structure with reversed atoms + mpdfco.eval(mstru10); + CHECK(mzeros == this->pdfcdiff(mstru10r)); + CHECK(BASIC == mpdfco.getEvaluatorTypeUsed()); + QuantityType gb3 = mpdfcb.getPDF(); + CHECK(allclose(gb0, gb3)); + // check effect of pair mask updates + mpdfco.eval(mstru10r); + CHECK(OPTIMIZED == mpdfco.getEvaluatorTypeUsed()); + mpdfco.setPairMask(0, 3, false); + mpdfco.eval(mstru10r); + CHECK(OPTIMIZED == mpdfco.getEvaluatorTypeUsed()); + mpdfco.setPairMask(0, 3, true); + mpdfco.eval(mstru10r); + CHECK(BASIC == mpdfco.getEvaluatorTypeUsed()); +} + +TEST_CASE_METHOD(TestPQEvaluatorFixture, + "TestPQEvaluator::test_optimized_supported", "[PQEvaluator]") { + mpdfcb.eval(mstru10); + CHECK(BASIC == mpdfcb.getEvaluatorTypeUsed()); + mpdfcb.setEvaluatorType(OPTIMIZED); + CHECK(BASIC == mpdfcb.getEvaluatorTypeUsed()); +} + +TEST_CASE_METHOD(TestPQEvaluatorFixture, + "TestPQEvaluator::test_optimized_unsupported", + "[PQEvaluator]") { + OverlapCalculator olc; + CHECK(BASIC == olc.getEvaluatorType()); + CHECK_THROWS_AS(olc.setEvaluatorType(OPTIMIZED), invalid_argument); + CHECK(BASIC == olc.getEvaluatorType()); +} + +TEST_CASE_METHOD(TestPQEvaluatorFixture, + "TestPQEvaluator::test_checkevaluator_unsupported", + "[PQEvaluator]") { + OverlapCalculator olc; + CHECK(BASIC == olc.getEvaluatorType()); + CHECK_THROWS_AS(olc.setEvaluatorType(PQEvaluatorType::CHECK), + invalid_argument); + CHECK(BASIC == olc.getEvaluatorType()); +} + +TEST_CASE_METHOD(TestPQEvaluatorFixture, + "TestPQEvaluator::test_checkevaluator_passes", + "[PQEvaluator]") { + mpdfco.setEvaluatorType(PQEvaluatorType::CHECK); + CHECK(PQEvaluatorType::CHECK == mpdfco.getEvaluatorType()); + mpdfco.eval(mstru10); + CHECK(BASIC == mpdfco.getEvaluatorTypeUsed()); + mpdfco.eval(mstru10); + CHECK(PQEvaluatorType::CHECK == mpdfco.getEvaluatorTypeUsed()); + mpdfco.eval(mstru9); + CHECK(PQEvaluatorType::CHECK == mpdfco.getEvaluatorTypeUsed()); + mpdfco.eval(emptyStructureAdapter()); + CHECK(BASIC == mpdfco.getEvaluatorTypeUsed()); +} + +TEST_CASE_METHOD(TestPQEvaluatorFixture, + "TestPQEvaluator::test_checkevaluator_fails", + "[PQEvaluator]") { + BadPairCounter badcounter; + badcounter.setEvaluatorType(PQEvaluatorType::CHECK); + CHECK(45 == badcounter(mstru10)); + CHECK(BASIC == badcounter.getEvaluatorTypeUsed()); + CHECK_THROWS_AS(badcounter(mstru10), logic_error); + CHECK(PQEvaluatorType::CHECK == badcounter.getEvaluatorTypeUsed()); +} + +} // namespace srreal +} // namespace diffpy + +// End of file diff --git a/src/tests/TestPQEvaluator.hpp b/src/tests/TestPQEvaluator.hpp deleted file mode 100644 index 1d1cb19d..00000000 --- a/src/tests/TestPQEvaluator.hpp +++ /dev/null @@ -1,264 +0,0 @@ -/***************************************************************************** - * - * libdiffpy Complex Modeling Initiative - * (c) 2013 Brookhaven Science Associates, - * Brookhaven National Laboratory. - * All rights reserved. - * - * File coded by: Pavol Juhas - * - * See AUTHORS.rst for a list of people who contributed. - * See LICENSE.rst for license information. - * - ****************************************************************************** - * - * class TestPQEvaluator -- unit tests for the PairQuantity evaluator class - * - *****************************************************************************/ - -#include - -#include -#include -#include - -#include -#include -#include -#include -#include -#include -#include "test_helpers.hpp" - -namespace diffpy { -namespace srreal { - -using namespace std; -using diffpy::mathutils::EpsilonEqual; - -// calculator with invalid support for OPTIMIZED evaluation - -class BadPairCounter : public PairCounter { - protected: - // intentionally faulty support for PQEvaluatorOptimized - virtual void stashPartialValue() {} - virtual void restorePartialValue() {} -}; - -////////////////////////////////////////////////////////////////////////////// -// class TestPQEvaluator -////////////////////////////////////////////////////////////////////////////// - -class TestPQEvaluator : public CxxTest::TestSuite { - private: - // data - EpsilonEqual allclose; - PDFCalculator mpdfcb; - PDFCalculator mpdfco; - QuantityType mzeros; - AtomicStructureAdapterPtr mstru10; - AtomicStructureAdapterPtr mstru10d1; - AtomicStructureAdapterPtr mstru10r; - AtomicStructureAdapterPtr mstru9; - - // methods - QuantityType pdfcdiff(StructureAdapterPtr stru) { - mpdfcb.eval(stru); - mpdfco.eval(stru); - QuantityType gb = mpdfcb.getPDF(); - QuantityType go = mpdfco.getPDF(); - assert(mpdfcb.getRgrid() == mpdfco.getRgrid()); - QuantityType rv(gb.size()); - transform(gb.begin(), gb.end(), go.begin(), rv.begin(), minus()); - return rv; - } - - public: - void setUp() { - // setup PDFCalculator instances and the zero vector for comparison - mpdfcb.setEvaluatorType(BASIC); - mpdfco.setEvaluatorType(OPTIMIZED); - mzeros.assign(mpdfcb.getRgrid().size(), 0.0); - // setup structure instances - const int SZ = 10; - mstru10 = std::make_shared(); - Atom ai; - ai.atomtype = "C"; - ai.uij_cartn = R3::identity(); - ai.uij_cartn(0, 0) = ai.uij_cartn(1, 1) = ai.uij_cartn(2, 2) = 0.004; - for (int i = 0; i < SZ; ++i) { - ai.xyz_cartn[0] = i; - mstru10->append(ai); - } - mstru10d1 = std::make_shared(*mstru10); - (*mstru10d1)[0].atomtype = "Au"; - mstru10r = std::make_shared(); - mstru10r->assign(mstru10->rbegin(), mstru10->rend()); - mstru9 = std::make_shared(*mstru10); - mstru9->erase(9); - } - - void test_initial_evaluator_type_used() { - PDFCalculator pdfc; - TS_ASSERT_EQUALS(NONE, pdfc.getEvaluatorTypeUsed()); - pdfc.setEvaluatorType(OPTIMIZED); - TS_ASSERT_EQUALS(NONE, pdfc.getEvaluatorTypeUsed()); - pdfc.setEvaluatorType(CHECK); - TS_ASSERT_EQUALS(NONE, pdfc.getEvaluatorTypeUsed()); - pdfc.setEvaluatorType(BASIC); - TS_ASSERT_EQUALS(NONE, pdfc.getEvaluatorTypeUsed()); - } - - void test_PDF_change_atom() { - using std::placeholders::_1; - TS_ASSERT_EQUALS(BASIC, mpdfcb.getEvaluatorType()); - TS_ASSERT_EQUALS(OPTIMIZED, mpdfco.getEvaluatorType()); - TS_ASSERT_EQUALS(mzeros, this->pdfcdiff(mstru10)); - // first call of mpdfco should use the BASIC evaluation - TS_ASSERT_EQUALS(BASIC, mpdfcb.getEvaluatorTypeUsed()); - TS_ASSERT_EQUALS(BASIC, mpdfco.getEvaluatorTypeUsed()); - // verify there are some non-zero values in the PDF. - QuantityType gb = mpdfcb.getPDF(); - TS_ASSERT(!gb.empty()); - int cnonzero = - count_if(gb.begin(), gb.end(), bind(not_equal_to(), _1, 0.0)); - TS_ASSERT(cnonzero); - // test second call on the same structure - mpdfco.eval(mstru10); - TS_ASSERT_EQUALS(gb, mpdfco.getPDF()); - TS_ASSERT_EQUALS(OPTIMIZED, mpdfco.getEvaluatorTypeUsed()); - // test structure with one different atom - TS_ASSERT(allclose(mzeros, this->pdfcdiff(mstru10d1))); - TS_ASSERT_EQUALS(OPTIMIZED, mpdfco.getEvaluatorTypeUsed()); - QuantityType gb1 = mpdfcb.getPDF(); - TS_ASSERT(!allclose(gb, gb1)); - // change position of 1 atom - mstru10d1->at(0).xyz_cartn[1] = 0.5; - TS_ASSERT(allclose(mzeros, this->pdfcdiff(mstru10d1))); - TS_ASSERT_EQUALS(OPTIMIZED, mpdfco.getEvaluatorTypeUsed()); - QuantityType gb2 = mpdfcb.getPDF(); - TS_ASSERT(!allclose(gb1, gb2)); - } - - void test_PDF_reverse_atoms() { - mpdfco.eval(mstru10); - TS_ASSERT(allclose(mzeros, this->pdfcdiff(mstru10r))); - TS_ASSERT_EQUALS(OPTIMIZED, mpdfco.getEvaluatorTypeUsed()); - } - - void test_PDF_remove_atom() { - mpdfco.eval(mstru10); - TS_ASSERT(allclose(mzeros, this->pdfcdiff(mstru9))); - TS_ASSERT_EQUALS(OPTIMIZED, mpdfco.getEvaluatorTypeUsed()); - } - - void test_PDF_type_mask() { - mpdfcb.setTypeMask("O2-", "all", false); - mpdfco.setTypeMask("O2-", "all", false); - PeriodicStructureAdapterPtr litao = - std::dynamic_pointer_cast( - loadTestPeriodicStructure("LiTaO3.stru")); - TS_ASSERT_EQUALS(mzeros, this->pdfcdiff(litao)); - TS_ASSERT_EQUALS(BASIC, mpdfco.getEvaluatorTypeUsed()); - QuantityType gb0 = mpdfcb.getPDF(); - // change masked-away oxygen - Atom& o29 = litao->at(29); - TS_ASSERT_EQUALS("O2-", o29.atomtype); - o29.xyz_cartn = R3::Vector(0.1, 0.2, 0.3); - TS_ASSERT_EQUALS(mzeros, this->pdfcdiff(litao)); - TS_ASSERT_EQUALS(OPTIMIZED, mpdfco.getEvaluatorTypeUsed()); - QuantityType gb1 = mpdfcb.getPDF(); - TS_ASSERT_EQUALS(gb0, gb1); - // change active lithium - Atom& li0 = litao->at(0); - li0.occupancy = 0.1; - TS_ASSERT(allclose(mzeros, this->pdfcdiff(litao))); - TS_ASSERT_EQUALS(OPTIMIZED, mpdfco.getEvaluatorTypeUsed()); - QuantityType gb2 = mpdfcb.getPDF(); - TS_ASSERT(!allclose(gb0, gb2)); - } - - void test_PDF_pair_mask() { - mpdfcb.setPairMask(0, 3, false); - mpdfco.setPairMask(0, 3, false); - TS_ASSERT_EQUALS(mzeros, this->pdfcdiff(mstru10)); - TS_ASSERT_EQUALS(BASIC, mpdfco.getEvaluatorTypeUsed()); - QuantityType gb0 = mpdfcb.getPDF(); - // check structure with one changed atom - TS_ASSERT(allclose(mzeros, this->pdfcdiff(mstru10d1))); - TS_ASSERT_EQUALS(OPTIMIZED, mpdfco.getEvaluatorTypeUsed()); - QuantityType gb1 = mpdfcb.getPDF(); - TS_ASSERT(!allclose(gb0, gb1)); - // check structure with one removed atom - TS_ASSERT(allclose(mzeros, this->pdfcdiff(mstru9))); - TS_ASSERT_EQUALS(OPTIMIZED, mpdfco.getEvaluatorTypeUsed()); - QuantityType gb2 = mpdfcb.getPDF(); - TS_ASSERT(!allclose(gb1, gb2)); - // check full evaluation for structure with reversed atoms - mpdfco.eval(mstru10); - TS_ASSERT_EQUALS(mzeros, this->pdfcdiff(mstru10r)); - TS_ASSERT_EQUALS(BASIC, mpdfco.getEvaluatorTypeUsed()); - QuantityType gb3 = mpdfcb.getPDF(); - TS_ASSERT(allclose(gb0, gb3)); - // check effect of pair mask updates - mpdfco.eval(mstru10r); - TS_ASSERT_EQUALS(OPTIMIZED, mpdfco.getEvaluatorTypeUsed()); - mpdfco.setPairMask(0, 3, false); - mpdfco.eval(mstru10r); - TS_ASSERT_EQUALS(OPTIMIZED, mpdfco.getEvaluatorTypeUsed()); - mpdfco.setPairMask(0, 3, true); - mpdfco.eval(mstru10r); - TS_ASSERT_EQUALS(BASIC, mpdfco.getEvaluatorTypeUsed()); - } - - void test_optimized_supported() { - mpdfcb.eval(mstru10); - TS_ASSERT_EQUALS(BASIC, mpdfcb.getEvaluatorTypeUsed()); - mpdfcb.setEvaluatorType(OPTIMIZED); - TS_ASSERT_EQUALS(BASIC, mpdfcb.getEvaluatorTypeUsed()); - } - - void test_optimized_unsupported() { - OverlapCalculator olc; - TS_ASSERT_EQUALS(BASIC, olc.getEvaluatorType()); - TS_ASSERT_THROWS(olc.setEvaluatorType(OPTIMIZED), invalid_argument); - TS_ASSERT_EQUALS(BASIC, olc.getEvaluatorType()); - } - - void test_checkevaluator_unsupported() { - OverlapCalculator olc; - TS_ASSERT_EQUALS(BASIC, olc.getEvaluatorType()); - TS_ASSERT_THROWS(olc.setEvaluatorType(CHECK), invalid_argument); - TS_ASSERT_EQUALS(BASIC, olc.getEvaluatorType()); - } - - void test_checkevaluator_passes() { - mpdfco.setEvaluatorType(CHECK); - TS_ASSERT_EQUALS(CHECK, mpdfco.getEvaluatorType()); - mpdfco.eval(mstru10); - TS_ASSERT_EQUALS(BASIC, mpdfco.getEvaluatorTypeUsed()); - mpdfco.eval(mstru10); - TS_ASSERT_EQUALS(CHECK, mpdfco.getEvaluatorTypeUsed()); - mpdfco.eval(mstru9); - TS_ASSERT_EQUALS(CHECK, mpdfco.getEvaluatorTypeUsed()); - mpdfco.eval(emptyStructureAdapter()); - TS_ASSERT_EQUALS(BASIC, mpdfco.getEvaluatorTypeUsed()); - } - - void test_checkevaluator_fails() { - BadPairCounter badcounter; - badcounter.setEvaluatorType(CHECK); - TS_ASSERT_EQUALS(45, badcounter(mstru10)); - TS_ASSERT_EQUALS(BASIC, badcounter.getEvaluatorTypeUsed()); - TS_ASSERT_THROWS(badcounter(mstru10), logic_error); - TS_ASSERT_EQUALS(CHECK, badcounter.getEvaluatorTypeUsed()); - } - -}; // class TestPQEvaluator - -} // namespace srreal -} // namespace diffpy - -using diffpy::srreal::TestPQEvaluator; - -// End of file diff --git a/src/tests/TestPairCounter.cpp b/src/tests/TestPairCounter.cpp new file mode 100644 index 00000000..0232f6a6 --- /dev/null +++ b/src/tests/TestPairCounter.cpp @@ -0,0 +1,139 @@ +/***************************************************************************** + * + * libdiffpy by DANSE Diffraction group + * Simon J. L. Billinge + * (c) 2009 The Trustees of Columbia University + * in the City of New York. All rights reserved. + * + * File coded by: Pavol Juhas + * + * See AUTHORS.rst for a list of people who contributed. + * See LICENSE_DANSE.rst for license information. + * + ****************************************************************************** + * + * TestPairCounter -- unit tests for PairCounter class + * + *****************************************************************************/ + +#include + +#include +#include + +using namespace std; +using namespace diffpy::srreal; + +class TestPairCounterFixture { + protected: + AtomicStructureAdapterPtr mstru; + AtomicStructureAdapterPtr mline100; + + public: + TestPairCounterFixture() { + mstru.reset(new AtomicStructureAdapter); + mline100.reset(new AtomicStructureAdapter); + Atom a; + for (int i = 0; i < 100; ++i) { + a.xyz_cartn = R3::Vector(1.0 * i, 0.0, 0.0); + mline100->append(a); + } + } +}; // class TestPairCounterFixture + +TEST_CASE_METHOD(TestPairCounterFixture, "TestPairCounter::test_call", + "[PairCounter]") { + PairCounter pcount; + Atom a; + for (int i = 0; i < 100; ++i) { + int npairs = i * (i - 1) / 2; + CHECK(npairs == pcount(mstru)); + a.xyz_cartn = R3::Vector(1.0 * i, 0.0, 0.0); + mstru->append(a); + } + mstru->clear(); + CHECK(0 == pcount(mstru)); +} + +TEST_CASE_METHOD(TestPairCounterFixture, "TestPairCounter::test_setRmin", + "[PairCounter]") { + PairCounter pcount; + CHECK(100 * 99 / 2 == pcount(mline100)); + pcount.setRmin(100); + CHECK(0 == pcount(mline100)); + pcount.setRmin(99.0); + CHECK(1 == pcount(mline100)); + pcount.setRmin(1.1); + CHECK(100 * 99 / 2 - 99 == pcount(mline100)); +} + +TEST_CASE_METHOD(TestPairCounterFixture, "TestPairCounter::test_setRmax", + "[PairCounter]") { + PairCounter pcount; + pcount.setRmax(0.9); + CHECK(0 == pcount(mline100)); + pcount.setRmax(1.1); + CHECK(99 == pcount(mline100)); + pcount.setRmax(98.5); + CHECK(100 * 99 / 2 - 1 == pcount(mline100)); +} + +TEST_CASE_METHOD(TestPairCounterFixture, "TestPairCounter::test_maskTicker", + "[PairCounter]") { + using diffpy::eventticker::EventTicker; + PairCounter pcount; + EventTicker et0 = pcount.ticker(); + pcount.maskAllPairs(true); + pcount.setPairMask(pcount.ALLATOMSINT, pcount.ALLATOMSINT, true); + pcount.setTypeMask("all", "all", true); + CHECK(et0 == pcount.ticker()); + pcount.setTypeMask("A", "B", true); + EventTicker et1 = pcount.ticker(); + CHECK(et0 < et1); + pcount.setTypeMask("A", "B", true); + CHECK(et1 == pcount.ticker()); + pcount.setTypeMask("A", "B", false); + EventTicker et2 = pcount.ticker(); + CHECK(et1 < et2); + pcount.setTypeMask("A", "all", false); + EventTicker et3 = pcount.ticker(); + CHECK(et2 < et3); + pcount.setTypeMask("A", "all", false); + CHECK(et3 == pcount.ticker()); + pcount.setTypeMask("A", "all", true); + EventTicker et4 = pcount.ticker(); + CHECK(et3 < et4); + pcount.setPairMask(0, 0, false); + EventTicker et5 = pcount.ticker(); + CHECK(et4 < et5); + pcount.setPairMask(0, 0, false); + CHECK(et5 == pcount.ticker()); + pcount.setPairMask(0, 5, true); + CHECK(et5 == pcount.ticker()); + pcount.setPairMask(0, 5, false); + CHECK(et5 < pcount.ticker()); +} + +TEST_CASE_METHOD(TestPairCounterFixture, "TestPairCounter::test_parallel", + "[PairCounter]") { + const int ncpu = 7; + PairCounter pmaster; + PairCounter pslave[ncpu]; + bool allequal = true; + for (int cpuindex = 0; cpuindex < ncpu; ++cpuindex) { + pslave[cpuindex].setupParallelRun(cpuindex, ncpu); + pslave[cpuindex].eval(mline100); + allequal = + allequal && (pslave[cpuindex].value()[0] == pslave[0].value()[0]); + } + CHECK(!allequal); + pmaster.setStructure(mline100); + CHECK(1u == pmaster.value().size()); + CHECK(0.0 == pmaster.value()[0]); + for (PairCounter* p = pslave; p != pslave + ncpu; ++p) { + pmaster.mergeParallelData(p->getParallelData(), ncpu); + } + CHECK(100 * 99 / 2 == pmaster.value()[0]); +} + +// End of file diff --git a/src/tests/TestPairCounter.hpp b/src/tests/TestPairCounter.hpp deleted file mode 100644 index f0db4ef8..00000000 --- a/src/tests/TestPairCounter.hpp +++ /dev/null @@ -1,137 +0,0 @@ -/***************************************************************************** - * - * libdiffpy by DANSE Diffraction group - * Simon J. L. Billinge - * (c) 2009 The Trustees of Columbia University - * in the City of New York. All rights reserved. - * - * File coded by: Pavol Juhas - * - * See AUTHORS.rst for a list of people who contributed. - * See LICENSE_DANSE.rst for license information. - * - ****************************************************************************** - * - * class TestPairCounter -- unit tests for PairCounter class - * - *****************************************************************************/ - -#include - -#include -#include - -using namespace std; -using namespace diffpy::srreal; - -class TestPairCounter : public CxxTest::TestSuite { - private: - AtomicStructureAdapterPtr mstru; - AtomicStructureAdapterPtr mline100; - - public: - void setUp() { - mstru.reset(new AtomicStructureAdapter); - if (!mline100) { - mline100.reset(new AtomicStructureAdapter); - Atom a; - for (int i = 0; i < 100; ++i) { - a.xyz_cartn = R3::Vector(1.0 * i, 0.0, 0.0); - mline100->append(a); - } - } - } - - void test_call() { - PairCounter pcount; - Atom a; - for (int i = 0; i < 100; ++i) { - int npairs = i * (i - 1) / 2; - TS_ASSERT_EQUALS(npairs, pcount(mstru)); - a.xyz_cartn = R3::Vector(1.0 * i, 0.0, 0.0); - mstru->append(a); - } - mstru->clear(); - TS_ASSERT_EQUALS(0, pcount(mstru)); - } - - void test_setRmin() { - PairCounter pcount; - TS_ASSERT_EQUALS(100 * 99 / 2, pcount(mline100)); - pcount.setRmin(100); - TS_ASSERT_EQUALS(0, pcount(mline100)); - pcount.setRmin(99.0); - TS_ASSERT_EQUALS(1, pcount(mline100)); - pcount.setRmin(1.1); - TS_ASSERT_EQUALS(100 * 99 / 2 - 99, pcount(mline100)); - } - - void test_setRmax() { - PairCounter pcount; - pcount.setRmax(0.9); - TS_ASSERT_EQUALS(0, pcount(mline100)); - pcount.setRmax(1.1); - TS_ASSERT_EQUALS(99, pcount(mline100)); - pcount.setRmax(98.5); - TS_ASSERT_EQUALS(100 * 99 / 2 - 1, pcount(mline100)); - } - - void test_maskTicker() { - using diffpy::eventticker::EventTicker; - PairCounter pcount; - EventTicker et0 = pcount.ticker(); - pcount.maskAllPairs(true); - pcount.setPairMask(pcount.ALLATOMSINT, pcount.ALLATOMSINT, true); - pcount.setTypeMask("all", "all", true); - TS_ASSERT_EQUALS(et0, pcount.ticker()); - pcount.setTypeMask("A", "B", true); - EventTicker et1 = pcount.ticker(); - TS_ASSERT_LESS_THAN(et0, et1); - pcount.setTypeMask("A", "B", true); - TS_ASSERT_EQUALS(et1, pcount.ticker()); - pcount.setTypeMask("A", "B", false); - EventTicker et2 = pcount.ticker(); - TS_ASSERT_LESS_THAN(et1, et2); - pcount.setTypeMask("A", "all", false); - EventTicker et3 = pcount.ticker(); - TS_ASSERT_LESS_THAN(et2, et3); - pcount.setTypeMask("A", "all", false); - TS_ASSERT_EQUALS(et3, pcount.ticker()); - pcount.setTypeMask("A", "all", true); - EventTicker et4 = pcount.ticker(); - TS_ASSERT_LESS_THAN(et3, et4); - pcount.setPairMask(0, 0, false); - EventTicker et5 = pcount.ticker(); - TS_ASSERT_LESS_THAN(et4, et5); - pcount.setPairMask(0, 0, false); - TS_ASSERT_EQUALS(et5, pcount.ticker()); - pcount.setPairMask(0, 5, true); - TS_ASSERT_EQUALS(et5, pcount.ticker()); - pcount.setPairMask(0, 5, false); - TS_ASSERT_LESS_THAN(et5, pcount.ticker()); - } - - void test_parallel() { - const int ncpu = 7; - PairCounter pmaster; - PairCounter pslave[ncpu]; - bool allequal = true; - for (int cpuindex = 0; cpuindex < ncpu; ++cpuindex) { - pslave[cpuindex].setupParallelRun(cpuindex, ncpu); - pslave[cpuindex].eval(mline100); - allequal = - allequal && (pslave[cpuindex].value()[0] == pslave[0].value()[0]); - } - TS_ASSERT(!allequal); - pmaster.setStructure(mline100); - TS_ASSERT_EQUALS(1u, pmaster.value().size()); - TS_ASSERT_EQUALS(0.0, pmaster.value()[0]); - for (PairCounter* p = pslave; p != pslave + ncpu; ++p) { - pmaster.mergeParallelData(p->getParallelData(), ncpu); - } - TS_ASSERT_EQUALS(100 * 99 / 2, pmaster.value()[0]); - } - -}; // class TestPairCounter - -// End of file diff --git a/src/tests/TestPeakProfile.cpp b/src/tests/TestPeakProfile.cpp new file mode 100644 index 00000000..1c6109ae --- /dev/null +++ b/src/tests/TestPeakProfile.cpp @@ -0,0 +1,153 @@ +/***************************************************************************** + * + * libdiffpy by DANSE Diffraction group + * Simon J. L. Billinge + * (c) 2009 The Trustees of Columbia University + * in the City of New York. All rights reserved. + * + * File coded by: Pavol Juhas + * + * See AUTHORS.rst for a list of people who contributed. + * See LICENSE_DANSE.rst for license information. + * + ****************************************************************************** + * + * TestPeakProfile -- unit tests for various PeakProfile classes + * + *****************************************************************************/ + +#include +#include +#include +#include + +#include +#include +#include +#include +#include "serialization_helpers.hpp" + +using Catch::Matchers::WithinAbs; + +using namespace std; +using namespace diffpy::srreal; +using diffpy::mathutils::eps_eq; + +class TestPeakProfileFixture { + protected: + PeakProfilePtr mpkgauss; + PeakProfilePtr mpkgcrop; + double meps; + + public: + TestPeakProfileFixture() { + mpkgauss = PeakProfile::createByType("gaussian"); + mpkgcrop = PeakProfile::createByType("croppedgaussian"); + meps = 10 * diffpy::mathutils::DOUBLE_EPS; + } +}; // class TestPeakProfileFixture + +TEST_CASE_METHOD(TestPeakProfileFixture, "TestPeakProfile::test_factory", + "[PeakProfile]") { + CHECK_THROWS_AS(PeakProfile::createByType("invalid"), invalid_argument); + CHECK(string("gaussian") == mpkgauss->type()); +} + +TEST_CASE_METHOD(TestPeakProfileFixture, "TestPeakProfile::test_clone", + "[PeakProfile]") { + CHECK("gaussian" == mpkgauss->clone()->type()); + CHECK("croppedgaussian" == mpkgcrop->clone()->type()); +} + +TEST_CASE_METHOD(TestPeakProfileFixture, "TestPeakProfile::test_attributes", + "[PeakProfile]") { + CHECK(1 == mpkgauss->namesOfDoubleAttributes().size()); + CHECK(1 == mpkgcrop->namesOfDoubleAttributes().size()); + mpkgauss->setDoubleAttr("peakprecision", 0.05); + const double lo0 = mpkgauss->xboundlo(0.2); + GaussianProfile g; + CHECK(!eps_eq(lo0, g.xboundlo(0.2))); + g.setPrecision(0.05); + CHECK(lo0 == g.xboundlo(0.2)); + mpkgcrop->setDoubleAttr("peakprecision", 0.05); + CroppedGaussianProfile fcrop; + CHECK(!eps_eq(fcrop(0.1, 0.2), (*mpkgcrop)(0.1, 0.2))); + fcrop.setPrecision(0.05); + CHECK(fcrop(0.1, 0.2) == (*mpkgcrop)(0.1, 0.2)); +} + +TEST_CASE_METHOD(TestPeakProfileFixture, "TestPeakProfile::test_y", + "[PeakProfile]") { + double Afwhm1 = 2 * sqrt(M_LN2 / M_PI); + const PeakProfile& pkgauss = *mpkgauss; + CHECK_THAT(Afwhm1, WithinAbs(pkgauss(0, 1), meps)); + CHECK_THAT(Afwhm1 / 2, WithinAbs(pkgauss(+0.5, 1), meps)); + CHECK_THAT(Afwhm1 / 2, WithinAbs(pkgauss(-0.5, 1), meps)); + const PeakProfile& pkgcrop = *mpkgcrop; + mpkgauss->setDoubleAttr("peakprecision", 1e-6); + mpkgcrop->setDoubleAttr("peakprecision", 1e-6); + CHECK(pkgcrop(0, 1) > pkgauss(0, 1) + meps); + CHECK(0 == pkgcrop(1, 0.1)); +} + +TEST_CASE_METHOD(TestPeakProfileFixture, "TestPeakProfile::test_xboundlo", + "[PeakProfile]") { + const double epsy = 1e-8; + mpkgauss->setPrecision(epsy); + const double Afwhm1 = 2 * sqrt(M_LN2 / M_PI); + const double xblo1 = mpkgauss->xboundlo(1); + const double Afwhm3 = Afwhm1 / 3; + const double xblo3 = mpkgauss->xboundlo(3); + CHECK_THAT(epsy, WithinAbs((*mpkgauss)(xblo1, 1) / Afwhm1, meps)); + CHECK_THAT(epsy, WithinAbs((*mpkgauss)(xblo3, 3) / Afwhm3, meps)); + CHECK(xblo1 < 0); + CHECK(xblo3 < 0); + mpkgauss->setPrecision(10); + CHECK(0.0 == mpkgauss->xboundlo(1)); + mpkgauss->setPrecision(0.1); + CHECK(0.0 == mpkgauss->xboundlo(0)); + CHECK(0.0 == mpkgauss->xboundlo(-1)); +} + +TEST_CASE_METHOD(TestPeakProfileFixture, "TestPeakProfile::test_xboundhi", + "[PeakProfile]") { + const double epsy = 1e-8; + mpkgauss->setPrecision(epsy); + const double Afwhm1 = 2 * sqrt(M_LN2 / M_PI); + const double xbhi1 = mpkgauss->xboundhi(1); + const double Afwhm3 = Afwhm1 / 3; + const double xbhi3 = mpkgauss->xboundhi(3); + CHECK_THAT(epsy, WithinAbs((*mpkgauss)(xbhi1, 1) / Afwhm1, meps)); + CHECK_THAT(epsy, WithinAbs((*mpkgauss)(xbhi3, 3) / Afwhm3, meps)); + CHECK(xbhi1 > 0); + CHECK(xbhi3 > 0); +} + +TEST_CASE_METHOD(TestPeakProfileFixture, "TestPeakProfile::test_setPrecision", + "[PeakProfile]") { + using diffpy::mathutils::DOUBLE_EPS; + double epsy = 1e-7; + CHECK_THAT(0.0, WithinAbs(mpkgauss->getPrecision(), 10 * DOUBLE_EPS)); + mpkgauss->setPrecision(epsy); + CHECK(epsy == mpkgauss->getPrecision()); + double xbhi1 = mpkgauss->xboundhi(1); + mpkgauss->setPrecision(1e-4); + CHECK(xbhi1 != mpkgauss->xboundhi(1)); +} + +TEST_CASE_METHOD(TestPeakProfileFixture, "TestPeakProfile::test_serialization", + "[PeakProfile]") { + mpkgauss->setPrecision(0.0123); + PeakProfilePtr pk1 = dumpandload(mpkgauss); + CHECK(pk1.get()); + CHECK(mpkgauss.get() != pk1.get()); + CHECK(string("gaussian") == pk1->type()); + CHECK(0.0123 == pk1->getPrecision()); + mpkgcrop->setPrecision(0.004); + PeakProfilePtr pk2 = dumpandload(mpkgcrop); + CHECK("croppedgaussian" == pk2->type()); + CHECK(0.004 == pk2->getPrecision()); + CHECK((*mpkgcrop)(0.1, 0.2) == (*pk2)(0.1, 0.2)); +} + +// End of file diff --git a/src/tests/TestPeakProfile.hpp b/src/tests/TestPeakProfile.hpp deleted file mode 100644 index 38f6d90b..00000000 --- a/src/tests/TestPeakProfile.hpp +++ /dev/null @@ -1,143 +0,0 @@ -/***************************************************************************** - * - * libdiffpy by DANSE Diffraction group - * Simon J. L. Billinge - * (c) 2009 The Trustees of Columbia University - * in the City of New York. All rights reserved. - * - * File coded by: Pavol Juhas - * - * See AUTHORS.rst for a list of people who contributed. - * See LICENSE_DANSE.rst for license information. - * - ****************************************************************************** - * - * class TestPeakProfile -- unit tests for various PeakProfile classes - * - *****************************************************************************/ - -#include -#include -#include - -#include -#include -#include -#include -#include "serialization_helpers.hpp" - -using namespace std; -using namespace diffpy::srreal; -using diffpy::mathutils::eps_eq; - -class TestPeakProfile : public CxxTest::TestSuite { - private: - PeakProfilePtr mpkgauss; - PeakProfilePtr mpkgcrop; - double meps; - - public: - void setUp() { - mpkgauss = PeakProfile::createByType("gaussian"); - mpkgcrop = PeakProfile::createByType("croppedgaussian"); - meps = 10 * diffpy::mathutils::DOUBLE_EPS; - } - - void test_factory() { - TS_ASSERT_THROWS(PeakProfile::createByType("invalid"), invalid_argument); - TS_ASSERT_EQUALS(string("gaussian"), mpkgauss->type()); - } - - void test_clone() { - TS_ASSERT_EQUALS("gaussian", mpkgauss->clone()->type()); - TS_ASSERT_EQUALS("croppedgaussian", mpkgcrop->clone()->type()); - } - - void test_attributes() { - TS_ASSERT_EQUALS(1, mpkgauss->namesOfDoubleAttributes().size()); - TS_ASSERT_EQUALS(1, mpkgcrop->namesOfDoubleAttributes().size()); - mpkgauss->setDoubleAttr("peakprecision", 0.05); - const double lo0 = mpkgauss->xboundlo(0.2); - GaussianProfile g; - TS_ASSERT(!eps_eq(lo0, g.xboundlo(0.2))); - g.setPrecision(0.05); - TS_ASSERT_EQUALS(lo0, g.xboundlo(0.2)); - mpkgcrop->setDoubleAttr("peakprecision", 0.05); - CroppedGaussianProfile fcrop; - TS_ASSERT(!eps_eq(fcrop(0.1, 0.2), (*mpkgcrop)(0.1, 0.2))); - fcrop.setPrecision(0.05); - TS_ASSERT_EQUALS(fcrop(0.1, 0.2), (*mpkgcrop)(0.1, 0.2)); - } - - void test_y() { - double Afwhm1 = 2 * sqrt(M_LN2 / M_PI); - const PeakProfile& pkgauss = *mpkgauss; - TS_ASSERT_DELTA(Afwhm1, pkgauss(0, 1), meps); - TS_ASSERT_DELTA(Afwhm1 / 2, pkgauss(+0.5, 1), meps); - TS_ASSERT_DELTA(Afwhm1 / 2, pkgauss(-0.5, 1), meps); - const PeakProfile& pkgcrop = *mpkgcrop; - mpkgauss->setDoubleAttr("peakprecision", 1e-6); - mpkgcrop->setDoubleAttr("peakprecision", 1e-6); - TS_ASSERT(pkgcrop(0, 1) > pkgauss(0, 1) + meps); - TS_ASSERT_EQUALS(0, pkgcrop(1, 0.1)); - } - - void test_xboundlo() { - const double epsy = 1e-8; - mpkgauss->setPrecision(epsy); - const double Afwhm1 = 2 * sqrt(M_LN2 / M_PI); - const double xblo1 = mpkgauss->xboundlo(1); - const double Afwhm3 = Afwhm1 / 3; - const double xblo3 = mpkgauss->xboundlo(3); - TS_ASSERT_DELTA(epsy, (*mpkgauss)(xblo1, 1) / Afwhm1, meps); - TS_ASSERT_DELTA(epsy, (*mpkgauss)(xblo3, 3) / Afwhm3, meps); - TS_ASSERT(xblo1 < 0); - TS_ASSERT(xblo3 < 0); - mpkgauss->setPrecision(10); - TS_ASSERT_EQUALS(0.0, mpkgauss->xboundlo(1)); - mpkgauss->setPrecision(0.1); - TS_ASSERT_EQUALS(0.0, mpkgauss->xboundlo(0)); - TS_ASSERT_EQUALS(0.0, mpkgauss->xboundlo(-1)); - } - - void test_xboundhi() { - const double epsy = 1e-8; - mpkgauss->setPrecision(epsy); - const double Afwhm1 = 2 * sqrt(M_LN2 / M_PI); - const double xbhi1 = mpkgauss->xboundhi(1); - const double Afwhm3 = Afwhm1 / 3; - const double xbhi3 = mpkgauss->xboundhi(3); - TS_ASSERT_DELTA(epsy, (*mpkgauss)(xbhi1, 1) / Afwhm1, meps); - TS_ASSERT_DELTA(epsy, (*mpkgauss)(xbhi3, 3) / Afwhm3, meps); - TS_ASSERT(xbhi1 > 0); - TS_ASSERT(xbhi3 > 0); - } - - void test_setPrecision() { - using diffpy::mathutils::DOUBLE_EPS; - double epsy = 1e-7; - TS_ASSERT_DELTA(0.0, mpkgauss->getPrecision(), 10 * DOUBLE_EPS); - mpkgauss->setPrecision(epsy); - TS_ASSERT_EQUALS(epsy, mpkgauss->getPrecision()); - double xbhi1 = mpkgauss->xboundhi(1); - mpkgauss->setPrecision(1e-4); - TS_ASSERT(xbhi1 != mpkgauss->xboundhi(1)); - } - - void test_serialization() { - mpkgauss->setPrecision(0.0123); - PeakProfilePtr pk1 = dumpandload(mpkgauss); - TS_ASSERT(pk1.get()); - TS_ASSERT_DIFFERS(mpkgauss.get(), pk1.get()); - TS_ASSERT_EQUALS(string("gaussian"), pk1->type()); - TS_ASSERT_EQUALS(0.0123, pk1->getPrecision()); - mpkgcrop->setPrecision(0.004); - PeakProfilePtr pk2 = dumpandload(mpkgcrop); - TS_ASSERT_EQUALS("croppedgaussian", pk2->type()); - TS_ASSERT_EQUALS(0.004, pk2->getPrecision()); - TS_ASSERT_EQUALS((*mpkgcrop)(0.1, 0.2), (*pk2)(0.1, 0.2)); - } - -}; // class TestPeakProfile - -// End of file diff --git a/src/tests/TestPeakWidthModel.cpp b/src/tests/TestPeakWidthModel.cpp new file mode 100644 index 00000000..3f3ee0e9 --- /dev/null +++ b/src/tests/TestPeakWidthModel.cpp @@ -0,0 +1,193 @@ +/***************************************************************************** + * + * libdiffpy Complex Modeling Initiative + * (c) 2019 Brookhaven Science Associates, + * Brookhaven National Laboratory. + * All rights reserved. + * + * File coded by: Pavol Juhas + * + * See AUTHORS.rst for a list of people who contributed. + * See LICENSE.rst for license information. + * + ****************************************************************************** + * + * TestPeakWidthModel -- test PeakWidthModel and derived classes + * + *****************************************************************************/ + +#include +#include + +#include +#include +#include +#include +#include +#include "serialization_helpers.hpp" + +using Catch::Matchers::WithinAbs; + +namespace diffpy { +namespace srreal { + +const double tuiso = 0.004; +const double UtoB = 8 * M_PI * M_PI; + +////////////////////////////////////////////////////////////////////////////// +// TestPeakWidthModel +////////////////////////////////////////////////////////////////////////////// + +class TestPeakWidthModelFixture { + protected: + // data + PeakWidthModelPtr mcnpw; + ConstantPeakWidth* mccnpw; + PeakWidthModelPtr mdwpw; + PeakWidthModelPtr mjepw; + JeongPeakWidth* mcjepw; + double meps; + + // methods + static AtomicStructureAdapterPtr dimer() { + AtomicStructureAdapterPtr adpt(new AtomicStructureAdapter); + Atom a; + a.atomtype = "C"; + a.uij_cartn = R3::identity() * tuiso; + adpt->append(a); + a.xyz_cartn[2] = 1; + adpt->append(a); + return adpt; + } + + public: + TestPeakWidthModelFixture() { + mcnpw = PeakWidthModel::createByType("constant"); + mccnpw = static_cast(mcnpw.get()); + mdwpw = PeakWidthModel::createByType("debye-waller"); + mjepw = PeakWidthModel::createByType("jeong"); + mcjepw = static_cast(mjepw.get()); + meps = 10 * diffpy::mathutils::DOUBLE_EPS; + } +}; // class TestPeakWidthModelFixture + +TEST_CASE_METHOD(TestPeakWidthModelFixture, "TestPeakWidthModel::test_clone", + "[PeakWidthModel]") { + CHECK("constant" == mcnpw->clone()->type()); + CHECK("debye-waller" == mdwpw->clone()->type()); + CHECK("jeong" == mjepw->clone()->type()); +} + +TEST_CASE_METHOD(TestPeakWidthModelFixture, + "TestPeakWidthModel::test_attributes", "[PeakWidthModel]") { + CHECK(0 == mdwpw->namesOfDoubleAttributes().size()); + CHECK(4 == mjepw->namesOfDoubleAttributes().size()); + mcjepw->setDelta1(0.1); + mcjepw->setDelta2(0.2); + mcjepw->setQbroad(0.3); + mcjepw->setQbroad_seperable(0.4); + CHECK(0.1 == mjepw->getDoubleAttr("delta1")); + CHECK(0.2 == mjepw->getDoubleAttr("delta2")); + CHECK(0.3 == mjepw->getDoubleAttr("qbroad")); + CHECK(0.4 == mjepw->getDoubleAttr("qbroad_seperable")); +} + +TEST_CASE_METHOD(TestPeakWidthModelFixture, + "TestPeakWidthModel::test_calculate", "[PeakWidthModel]") { + using diffpy::mathutils::GAUSS_SIGMA_TO_FWHM; + AtomicStructureAdapterPtr adpt = dimer(); + BaseBondGeneratorPtr bnds = adpt->createBondGenerator(); + bnds->setRmax(2.0); + bnds->rewind(); + const double w = GAUSS_SIGMA_TO_FWHM * sqrt(2 * tuiso); + CHECK_THAT(w, WithinAbs(mdwpw->calculate(*bnds), meps)); + CHECK_THAT(w, WithinAbs(mjepw->calculate(*bnds), meps)); + mcjepw->setDelta2(0.75); + CHECK_THAT(0.5 * w, WithinAbs(mjepw->calculate(*bnds), meps)); + mcjepw->setDelta2(0); + mcjepw->setQbroad(sqrt(3)); + mcjepw->setQbroad_seperable(0); + CHECK_THAT(2 * w, WithinAbs(mjepw->calculate(*bnds), meps)); +} + +TEST_CASE_METHOD(TestPeakWidthModelFixture, + "TestPeakWidthModel::test_serialization", "[PeakWidthModel]") { + PeakWidthModelPtr pwm1 = dumpandload(mdwpw); + DebyeWallerPeakWidth* cpwm1 = dynamic_cast(pwm1.get()); + CHECK(cpwm1); + mcjepw->setDelta1(1.1); + mcjepw->setDoubleAttr("delta2", 2.2); + mcjepw->setDoubleAttr("qbroad", 0.03); + mcjepw->setDoubleAttr("qbroad_seperable", 0.003); + PeakWidthModelPtr pwm2 = dumpandload(mjepw); + JeongPeakWidth* cpwm2 = dynamic_cast(pwm2.get()); + CHECK(cpwm2); + CHECK(1.1 == pwm2->getDoubleAttr("delta1")); + CHECK(2.2 == pwm2->getDoubleAttr("delta2")); + CHECK(0.03 == pwm2->getDoubleAttr("qbroad")); + CHECK(0.003 == pwm2->getDoubleAttr("qbroad_seperable")); +} + +TEST_CASE_METHOD(TestPeakWidthModelFixture, + "TestPeakWidthModel::test_CPWM_attributes", + "[PeakWidthModel]") { + CHECK("constant" == mcnpw->type()); + CHECK(mcnpw->hasDoubleAttr("width")); + CHECK(mcnpw->hasDoubleAttr("bisowidth")); + CHECK(mcnpw->hasDoubleAttr("uisowidth")); + CHECK(3 == mcnpw->namesOfDoubleAttributes().size()); + mccnpw->setWidth(0.1); + CHECK(0.1 == mcnpw->getDoubleAttr("width")); + CHECK(0.1 == mcnpw->maxWidth(dimer(), 0, 4)); + const double bw0 = mcnpw->getDoubleAttr("bisowidth"); + const double uw0 = mcnpw->getDoubleAttr("uisowidth"); + mccnpw->setWidth(2 * mccnpw->getWidth()); + CHECK_THAT(bw0, WithinAbs(UtoB * uw0, meps)); + CHECK_THAT(4 * bw0, WithinAbs(mcnpw->getDoubleAttr("bisowidth"), meps)); + CHECK_THAT(4 * uw0, WithinAbs(mcnpw->getDoubleAttr("uisowidth"), meps)); +} + +TEST_CASE_METHOD(TestPeakWidthModelFixture, + "TestPeakWidthModel::test_CPWM_bisowidth", + "[PeakWidthModel]") { + const double biso = UtoB * tuiso; + mcnpw->setDoubleAttr("bisowidth", biso); + CHECK_THAT(tuiso, WithinAbs(mcnpw->getDoubleAttr("uisowidth"), meps)); + mcnpw->setDoubleAttr("uisowidth", -tuiso); + CHECK_THAT(-biso, WithinAbs(mcnpw->getDoubleAttr("bisowidth"), meps)); +} + +TEST_CASE_METHOD(TestPeakWidthModelFixture, + "TestPeakWidthModel::test_CPWM_uisowidth", + "[PeakWidthModel]") { + StructureAdapterPtr adpt = dimer(); + BaseBondGeneratorPtr bnds = adpt->createBondGenerator(); + PeakWidthModelPtr dwpw(new DebyeWallerPeakWidth); + bnds->setRmax(2.0); + bnds->rewind(); + const double w0 = dwpw->calculate(*bnds); + CHECK(0.0 == mcnpw->calculate(*bnds)); + mcnpw->setDoubleAttr("uisowidth", tuiso); + CHECK_THAT(w0, WithinAbs(mcnpw->calculate(*bnds), meps)); + mcnpw->setDoubleAttr("uisowidth", -tuiso); + CHECK_THAT(-tuiso, WithinAbs(mcnpw->getDoubleAttr("uisowidth"), meps)); + CHECK_THAT(-w0, WithinAbs(mcnpw->getDoubleAttr("width"), meps)); +} + +TEST_CASE_METHOD(TestPeakWidthModelFixture, + "TestPeakWidthModel::test_CPWM_serialization", + "[PeakWidthModel]") { + PeakWidthModelPtr pwm1 = dumpandload(mcnpw); + CHECK("constant" == pwm1->type()); + CHECK(0.0 == pwm1->getDoubleAttr("width")); + mcnpw->setDoubleAttr("width", 0.1); + double uw0 = mcnpw->getDoubleAttr("uisowidth"); + PeakWidthModelPtr pwm2 = dumpandload(mcnpw); + CHECK(0.1 == pwm2->getDoubleAttr("width")); + CHECK_THAT(uw0, WithinAbs(pwm2->getDoubleAttr("uisowidth"), meps)); +} + +} // namespace srreal +} // namespace diffpy + +// End of file diff --git a/src/tests/TestPeakWidthModel.hpp b/src/tests/TestPeakWidthModel.hpp deleted file mode 100644 index 9d19a701..00000000 --- a/src/tests/TestPeakWidthModel.hpp +++ /dev/null @@ -1,182 +0,0 @@ -/***************************************************************************** - * - * libdiffpy Complex Modeling Initiative - * (c) 2019 Brookhaven Science Associates, - * Brookhaven National Laboratory. - * All rights reserved. - * - * File coded by: Pavol Juhas - * - * See AUTHORS.rst for a list of people who contributed. - * See LICENSE.rst for license information. - * - ****************************************************************************** - * - * class TestPeakWidthModel -- test PeakWidthModel and derived classes - * - *****************************************************************************/ - -#include - -#include -#include -#include -#include -#include -#include "serialization_helpers.hpp" - -namespace diffpy { -namespace srreal { - -const double tuiso = 0.004; -const double UtoB = 8 * M_PI * M_PI; - -////////////////////////////////////////////////////////////////////////////// -// class TestPeakWidthModel -////////////////////////////////////////////////////////////////////////////// - -class TestPeakWidthModel : public CxxTest::TestSuite { - private: - // data - PeakWidthModelPtr mcnpw; - ConstantPeakWidth* mccnpw; - PeakWidthModelPtr mdwpw; - PeakWidthModelPtr mjepw; - JeongPeakWidth* mcjepw; - double meps; - - // methods - static AtomicStructureAdapterPtr dimer() { - AtomicStructureAdapterPtr adpt(new AtomicStructureAdapter); - Atom a; - a.atomtype = "C"; - a.uij_cartn = R3::identity() * tuiso; - adpt->append(a); - a.xyz_cartn[2] = 1; - adpt->append(a); - return adpt; - } - - public: - void setUp() { - mcnpw = PeakWidthModel::createByType("constant"); - mccnpw = static_cast(mcnpw.get()); - mdwpw = PeakWidthModel::createByType("debye-waller"); - mjepw = PeakWidthModel::createByType("jeong"); - mcjepw = static_cast(mjepw.get()); - meps = 10 * diffpy::mathutils::DOUBLE_EPS; - } - - void test_clone() { - TS_ASSERT_EQUALS("constant", mcnpw->clone()->type()); - TS_ASSERT_EQUALS("debye-waller", mdwpw->clone()->type()); - TS_ASSERT_EQUALS("jeong", mjepw->clone()->type()); - } - - void test_attributes() { - TS_ASSERT_EQUALS(0, mdwpw->namesOfDoubleAttributes().size()); - TS_ASSERT_EQUALS(4, mjepw->namesOfDoubleAttributes().size()); - mcjepw->setDelta1(0.1); - mcjepw->setDelta2(0.2); - mcjepw->setQbroad(0.3); - mcjepw->setQbroad_seperable(0.4); - TS_ASSERT_EQUALS(0.1, mjepw->getDoubleAttr("delta1")); - TS_ASSERT_EQUALS(0.2, mjepw->getDoubleAttr("delta2")); - TS_ASSERT_EQUALS(0.3, mjepw->getDoubleAttr("qbroad")); - TS_ASSERT_EQUALS(0.4, mjepw->getDoubleAttr("qbroad_seperable")); - } - - void test_calculate() { - using diffpy::mathutils::GAUSS_SIGMA_TO_FWHM; - AtomicStructureAdapterPtr adpt = dimer(); - BaseBondGeneratorPtr bnds = adpt->createBondGenerator(); - bnds->setRmax(2.0); - bnds->rewind(); - const double w = GAUSS_SIGMA_TO_FWHM * sqrt(2 * tuiso); - TS_ASSERT_DELTA(w, mdwpw->calculate(*bnds), meps); - TS_ASSERT_DELTA(w, mjepw->calculate(*bnds), meps); - mcjepw->setDelta2(0.75); - TS_ASSERT_DELTA(0.5 * w, mjepw->calculate(*bnds), meps); - mcjepw->setDelta2(0); - mcjepw->setQbroad(sqrt(3)); - mcjepw->setQbroad_seperable(0); - TS_ASSERT_DELTA(2 * w, mjepw->calculate(*bnds), meps); - } - - void test_serialization() { - PeakWidthModelPtr pwm1 = dumpandload(mdwpw); - DebyeWallerPeakWidth* cpwm1 = - dynamic_cast(pwm1.get()); - TS_ASSERT(cpwm1); - mcjepw->setDelta1(1.1); - mcjepw->setDoubleAttr("delta2", 2.2); - mcjepw->setDoubleAttr("qbroad", 0.03); - mcjepw->setDoubleAttr("qbroad_seperable", 0.003); - PeakWidthModelPtr pwm2 = dumpandload(mjepw); - JeongPeakWidth* cpwm2 = dynamic_cast(pwm2.get()); - TS_ASSERT(cpwm2); - TS_ASSERT_EQUALS(1.1, pwm2->getDoubleAttr("delta1")); - TS_ASSERT_EQUALS(2.2, pwm2->getDoubleAttr("delta2")); - TS_ASSERT_EQUALS(0.03, pwm2->getDoubleAttr("qbroad")); - TS_ASSERT_EQUALS(0.003, pwm2->getDoubleAttr("qbroad_seperable")); - } - - void test_CPWM_attributes() { - TS_ASSERT_EQUALS("constant", mcnpw->type()); - TS_ASSERT(mcnpw->hasDoubleAttr("width")); - TS_ASSERT(mcnpw->hasDoubleAttr("bisowidth")); - TS_ASSERT(mcnpw->hasDoubleAttr("uisowidth")); - TS_ASSERT_EQUALS(3, mcnpw->namesOfDoubleAttributes().size()); - mccnpw->setWidth(0.1); - TS_ASSERT_EQUALS(0.1, mcnpw->getDoubleAttr("width")); - TS_ASSERT_EQUALS(0.1, mcnpw->maxWidth(dimer(), 0, 4)); - const double bw0 = mcnpw->getDoubleAttr("bisowidth"); - const double uw0 = mcnpw->getDoubleAttr("uisowidth"); - mccnpw->setWidth(2 * mccnpw->getWidth()); - TS_ASSERT_DELTA(bw0, UtoB * uw0, meps); - TS_ASSERT_DELTA(4 * bw0, mcnpw->getDoubleAttr("bisowidth"), meps); - TS_ASSERT_DELTA(4 * uw0, mcnpw->getDoubleAttr("uisowidth"), meps); - } - - void test_CPWM_bisowidth() { - const double biso = UtoB * tuiso; - mcnpw->setDoubleAttr("bisowidth", biso); - TS_ASSERT_DELTA(tuiso, mcnpw->getDoubleAttr("uisowidth"), meps); - mcnpw->setDoubleAttr("uisowidth", -tuiso); - TS_ASSERT_DELTA(-biso, mcnpw->getDoubleAttr("bisowidth"), meps); - } - - void test_CPWM_uisowidth() { - StructureAdapterPtr adpt = dimer(); - BaseBondGeneratorPtr bnds = adpt->createBondGenerator(); - PeakWidthModelPtr dwpw(new DebyeWallerPeakWidth); - bnds->setRmax(2.0); - bnds->rewind(); - const double w0 = dwpw->calculate(*bnds); - TS_ASSERT_EQUALS(0.0, mcnpw->calculate(*bnds)); - mcnpw->setDoubleAttr("uisowidth", tuiso); - TS_ASSERT_DELTA(w0, mcnpw->calculate(*bnds), meps); - mcnpw->setDoubleAttr("uisowidth", -tuiso); - TS_ASSERT_DELTA(-tuiso, mcnpw->getDoubleAttr("uisowidth"), meps); - TS_ASSERT_DELTA(-w0, mcnpw->getDoubleAttr("width"), meps); - } - - void test_CPWM_serialization() { - PeakWidthModelPtr pwm1 = dumpandload(mcnpw); - TS_ASSERT_EQUALS("constant", pwm1->type()); - TS_ASSERT_EQUALS(0.0, pwm1->getDoubleAttr("width")); - mcnpw->setDoubleAttr("width", 0.1); - double uw0 = mcnpw->getDoubleAttr("uisowidth"); - PeakWidthModelPtr pwm2 = dumpandload(mcnpw); - TS_ASSERT_EQUALS(0.1, pwm2->getDoubleAttr("width")); - TS_ASSERT_DELTA(uw0, pwm2->getDoubleAttr("uisowidth"), meps); - } - -}; // class TestPeakWidthModel - -} // namespace srreal -} // namespace diffpy - -using diffpy::srreal::TestPeakWidthModel; - -// End of file diff --git a/src/tests/TestPeriodicStructureAdapter.cpp b/src/tests/TestPeriodicStructureAdapter.cpp new file mode 100644 index 00000000..9e6657f6 --- /dev/null +++ b/src/tests/TestPeriodicStructureAdapter.cpp @@ -0,0 +1,385 @@ +/***************************************************************************** + * + * libdiffpy Complex Modeling Initiative + * (c) 2013 Brookhaven Science Associates, + * Brookhaven National Laboratory. + * All rights reserved. + * + * File coded by: Pavol Juhas + * + * See AUTHORS.rst for a list of people who contributed. + * See LICENSE.rst for license information. + * + ****************************************************************************** + * + * TestPeriodicStructureAdapter -- unit tests for an adapter + * for periodic structures + * + * TestPeriodicStructureBondGenerator -- unit tests for bond generator + * + *****************************************************************************/ + +#include +#include +#include +#include + +#include +#include +#include "test_helpers.hpp" +#include "serialization_helpers.hpp" + +using Catch::Matchers::WithinAbs; + +namespace diffpy { +namespace srreal { + +using namespace std; + +// Local Helpers ------------------------------------------------------------- + +namespace { + +int countBonds(BaseBondGenerator& bnds) { + int rv = 0; + for (bnds.rewind(); !bnds.finished(); bnds.next()) ++rv; + return rv; +} + +template +double testmsd0(const Tstru& stru, const Tbnds& bnds) { + bool anisotropy0 = stru->siteAnisotropy(bnds->site0()); + double rv = + meanSquareDisplacement(bnds->Ucartesian0(), bnds->r01(), anisotropy0); + return rv; +} + +template +double testmsd1(const Tstru& stru, const Tbnds& bnds) { + bool anisotropy1 = stru->siteAnisotropy(bnds->site1()); + double rv = + meanSquareDisplacement(bnds->Ucartesian1(), bnds->r01(), anisotropy1); + return rv; +} + +} // namespace + +////////////////////////////////////////////////////////////////////////////// +// TestPeriodicStructureAdapter +////////////////////////////////////////////////////////////////////////////// + +class TestPeriodicStructureAdapterFixture { + protected: + StructureAdapterPtr m_ni; + StructureAdapterPtr m_kbise; + StructureAdapterPtr m_catio3; + StructureAdapterPtr m_pswt; + + public: + TestPeriodicStructureAdapterFixture() { + m_ni = loadTestPeriodicStructure("Ni.stru"); + m_kbise = loadTestPeriodicStructure("alpha_K2Bi8Se13.stru"); + m_catio3 = loadTestPeriodicStructure("CaTiO3.stru"); + m_pswt = loadTestPeriodicStructure("PbScW25TiO3.stru"); + } +}; // class TestPeriodicStructureAdapterFixture + +TEST_CASE_METHOD(TestPeriodicStructureAdapterFixture, + "TestPeriodicStructureAdapter::test_typeid", + "[PeriodicStructureAdapter]") { + StructureAdapter& r_ni = *m_ni; + CHECK(typeid(PeriodicStructureAdapter) == typeid(r_ni)); +} + +TEST_CASE_METHOD(TestPeriodicStructureAdapterFixture, + "TestPeriodicStructureAdapter::test_countSites", + "[PeriodicStructureAdapter]") { + CHECK(4 == m_ni->countSites()); + CHECK(23 == m_kbise->countSites()); + CHECK(20 == m_catio3->countSites()); + CHECK(56 == m_pswt->countSites()); +} + +TEST_CASE_METHOD(TestPeriodicStructureAdapterFixture, + "TestPeriodicStructureAdapter::test_totalOccupancy", + "[PeriodicStructureAdapter]") { + const double eps = 10 * diffpy::mathutils::DOUBLE_EPS; + CHECK(4.0 == m_ni->totalOccupancy()); + CHECK(23.0 == m_kbise->totalOccupancy()); + CHECK(20.0 == m_catio3->totalOccupancy()); + CHECK_THAT(40.0, WithinAbs(m_pswt->totalOccupancy(), eps)); +} + +TEST_CASE_METHOD(TestPeriodicStructureAdapterFixture, + "TestPeriodicStructureAdapter::test_numberDensity", + "[PeriodicStructureAdapter]") { + const double eps = 1.0e-7; + CHECK_THAT(0.0914114, WithinAbs(m_ni->numberDensity(), eps)); + CHECK_THAT(0.0335565, WithinAbs(m_kbise->numberDensity(), eps)); + CHECK_THAT(0.0894566, WithinAbs(m_catio3->numberDensity(), eps)); + CHECK_THAT(0.0760332, WithinAbs(m_pswt->numberDensity(), eps)); +} + +TEST_CASE_METHOD(TestPeriodicStructureAdapterFixture, + "TestPeriodicStructureAdapter::test_siteCartesianPosition", + "[PeriodicStructureAdapter]") { + const double eps = 1.0e-5; + R3::Vector rCa = m_catio3->siteCartesianPosition(4); + CHECK_THAT(0.03486, WithinAbs(rCa[0], eps)); + CHECK_THAT(0.19366, WithinAbs(rCa[1], eps)); + CHECK_THAT(1.90975, WithinAbs(rCa[2], eps)); +} + +TEST_CASE_METHOD(TestPeriodicStructureAdapterFixture, + "TestPeriodicStructureAdapter::test_siteAnisotropy", + "[PeriodicStructureAdapter]") { + for (int i = 0; i < m_ni->countSites(); ++i) { + CHECK(false == m_ni->siteAnisotropy(i)); + } + for (int i = 0; i < m_catio3->countSites(); ++i) { + CHECK(true == m_catio3->siteAnisotropy(i)); + } +} + +TEST_CASE_METHOD(TestPeriodicStructureAdapterFixture, + "TestPeriodicStructureAdapter::test_siteCartesianUij", + "[PeriodicStructureAdapter]") { + // nickel should have all Uij equal zero. + CHECK(R3::zeromatrix() == m_ni->siteCartesianUij(0)); + // check CaTiO3 values + const R3::Matrix& UO2 = m_catio3->siteCartesianUij(12); + const double eps = 1e-8; + CHECK_THAT(0.0065, WithinAbs(UO2(0, 0), eps)); + CHECK_THAT(0.0060, WithinAbs(UO2(1, 1), eps)); + CHECK_THAT(0.0095, WithinAbs(UO2(2, 2), eps)); + CHECK_THAT(0.0020, WithinAbs(UO2(0, 1), eps)); + CHECK_THAT(0.0020, WithinAbs(UO2(1, 0), eps)); + CHECK_THAT(-0.0008, WithinAbs(UO2(0, 2), eps)); + CHECK_THAT(-0.0008, WithinAbs(UO2(2, 0), eps)); + CHECK_THAT(-0.0010, WithinAbs(UO2(1, 2), eps)); + CHECK_THAT(-0.0010, WithinAbs(UO2(2, 1), eps)); +} + +TEST_CASE_METHOD(TestPeriodicStructureAdapterFixture, + "TestPeriodicStructureAdapter::test_siteAtomType", + "[PeriodicStructureAdapter]") { + CHECK(string("Ni") == m_ni->siteAtomType(0)); + CHECK(string("Ni") == m_ni->siteAtomType(3)); + CHECK(string("K1+") == m_kbise->siteAtomType(0)); + CHECK(string("Bi3+") == m_kbise->siteAtomType(2)); + CHECK(string("Se") == m_kbise->siteAtomType(10)); + CHECK(string("Se") == m_kbise->siteAtomType(22)); +} + +TEST_CASE_METHOD(TestPeriodicStructureAdapterFixture, + "TestPeriodicStructureAdapter::test_getLattice", + "[PeriodicStructureAdapter]") { + PeriodicStructureAdapterPtr pkbise = + std::dynamic_pointer_cast(m_kbise); + CHECK(pkbise); + const Lattice& L = pkbise->getLattice(); + const double eps = 1.0e-12; + CHECK_THAT(13.768, WithinAbs(L.a(), eps)); + CHECK_THAT(12.096, WithinAbs(L.b(), eps)); + CHECK_THAT(4.1656, WithinAbs(L.c(), eps)); + CHECK_THAT(89.98, WithinAbs(L.alpha(), eps)); + CHECK_THAT(98.64, WithinAbs(L.beta(), eps)); + CHECK_THAT(87.96, WithinAbs(L.gamma(), eps)); +} + +TEST_CASE_METHOD(TestPeriodicStructureAdapterFixture, + "TestPeriodicStructureAdapter::test_serialization", + "[PeriodicStructureAdapter]") { + StructureAdapterPtr kbise1 = dumpandload(m_kbise); + CHECK(m_kbise.get() != kbise1.get()); + const double eps = 1.0e-7; + CHECK(23 == kbise1->countSites()); + CHECK(23.0 == kbise1->totalOccupancy()); + CHECK_THAT(0.0335565, WithinAbs(kbise1->numberDensity(), eps)); + CHECK(string("K1+") == kbise1->siteAtomType(0)); + CHECK(string("Bi3+") == kbise1->siteAtomType(2)); + CHECK(string("Se") == kbise1->siteAtomType(10)); + CHECK(string("Se") == kbise1->siteAtomType(22)); + PeriodicStructureAdapterPtr pkbise = + std::dynamic_pointer_cast(m_kbise); + PeriodicStructureAdapterPtr pkbise1 = + std::dynamic_pointer_cast(kbise1); + const Lattice& L = pkbise->getLattice(); + const Lattice& L1 = pkbise1->getLattice(); + CHECK(L.a() == L1.a()); + CHECK(L.b() == L1.b()); + CHECK(L.c() == L1.c()); + CHECK(L.alpha() == L1.alpha()); + CHECK(L.beta() == L1.beta()); + CHECK(L.gamma() == L1.gamma()); +} + +TEST_CASE_METHOD(TestPeriodicStructureAdapterFixture, + "TestPeriodicStructureAdapter::test_comparison", + "[PeriodicStructureAdapter]") { + const PeriodicStructureAdapter& kbise0 = + static_cast(*m_kbise); + PeriodicStructureAdapter kbise1(kbise0); + PeriodicStructureAdapter kbise2(kbise0); + CHECK(kbise0 == kbise1); + CHECK(!(kbise0 != kbise1)); + CHECK(kbise1.countSites()); + kbise1.erase(0); + CHECK(kbise0 != kbise1); + CHECK(!(kbise0 == kbise1)); + kbise2.setLatPar(3, 4, 5, 91, 92, 93); + CHECK(kbise0 != kbise2); +} + +////////////////////////////////////////////////////////////////////////////// +// TestPeriodicStructureBondGenerator +////////////////////////////////////////////////////////////////////////////// + +class TestPeriodicStructureBondGeneratorFixture { + protected: + StructureAdapterPtr m_ni; + BaseBondGeneratorPtr m_nibnds; + + public: + TestPeriodicStructureBondGeneratorFixture() { + m_ni = loadTestPeriodicStructure("Ni.stru"); + m_nibnds = m_ni->createBondGenerator(); + } +}; // class TestPeriodicStructureBondGeneratorFixture + +TEST_CASE_METHOD(TestPeriodicStructureBondGeneratorFixture, + "TestPeriodicStructureBondGenerator::test_typeid", + "[PeriodicStructureBondGenerator]") { + BaseBondGenerator& r_nibnds = *m_nibnds; + CHECK(typeid(PeriodicStructureBondGenerator) == typeid(r_nibnds)); +} + +TEST_CASE_METHOD(TestPeriodicStructureBondGeneratorFixture, + "TestPeriodicStructureBondGenerator::test_bondCountNickel", + "[PeriodicStructureBondGenerator]") { + m_nibnds->selectAnchorSite(0); + m_nibnds->setRmin(0); + m_nibnds->setRmax(1.0); + CHECK(0 == countBonds(*m_nibnds)); + m_nibnds->selectAnchorSite(3); + CHECK(0 == countBonds(*m_nibnds)); + m_nibnds->setRmin(-10); + CHECK(0 == countBonds(*m_nibnds)); + // there are 12 nearest neighbors at 2.49 + m_nibnds->setRmax(3); + CHECK(12 == countBonds(*m_nibnds)); + m_nibnds->selectAnchorSite(0); + CHECK(12 == countBonds(*m_nibnds)); + // there are no self neighbors below the cell length of 3.52 + m_nibnds->selectAnchorSite(0); + m_nibnds->selectSiteRange(0, 1); + CHECK(0 == countBonds(*m_nibnds)); + // and any other unit cell atom would give 4 neighbors + m_nibnds->selectAnchorSite(0); + m_nibnds->selectSiteRange(3, 4); + CHECK(4 == countBonds(*m_nibnds)); + // there are no bonds between 2.6 and 3.4 + m_nibnds->setRmin(2.6); + m_nibnds->setRmax(3.4); + m_nibnds->selectSiteRange(0, 4); + CHECK(0 == countBonds(*m_nibnds)); + // there are 6 second nearest neighbors at 3.52 + m_nibnds->setRmax(3.6); + CHECK(6 == countBonds(*m_nibnds)); + // which sums to 18 neighbors within radius 3.6 + m_nibnds->setRmin(0); + CHECK(18 == countBonds(*m_nibnds)); +} + +TEST_CASE_METHOD(TestPeriodicStructureBondGeneratorFixture, + "TestPeriodicStructureBondGenerator::test_bondCountWurtzite", + "[PeriodicStructureBondGenerator]") { + StructureAdapterPtr stru = loadTestPeriodicStructure("ZnS_wurtzite.stru"); + BaseBondGeneratorPtr bnds = stru->createBondGenerator(); + CHECK(4 == stru->countSites()); + bnds->selectAnchorSite(0); + bnds->selectSiteRange(0, 4); + bnds->setRmin(0); + // there should be no bond below the ZnS distance 2.31 + bnds->setRmax(2.2); + CHECK(0 == countBonds(*bnds)); + // z-neighbor is slightly more distant than 3 in the lower plane + bnds->setRmax(2.35); + CHECK(3 == countBonds(*bnds)); + bnds->setRmax(2.5); + CHECK(4 == countBonds(*bnds)); + // there are 12 second nearest neighbors at 3.81 + bnds->setRmin(3.7); + bnds->setRmax(3.82); + CHECK(12 == countBonds(*bnds)); + // and one more at 3.83 + bnds->setRmax(3.85); + CHECK(13 == countBonds(*bnds)); + // making the total 17 + bnds->setRmin(0); + CHECK(17 == countBonds(*bnds)); + // and the same happens for all other sites + bnds->selectAnchorSite(1); + CHECK(17 == countBonds(*bnds)); + bnds->selectAnchorSite(2); + CHECK(17 == countBonds(*bnds)); + bnds->selectAnchorSite(3); + CHECK(17 == countBonds(*bnds)); +} + +TEST_CASE_METHOD(TestPeriodicStructureBondGeneratorFixture, + "TestPeriodicStructureBondGenerator::test_LiTaO3", + "[PeriodicStructureBondGenerator]") { + const string lithium = "Li1+"; + const string tantalum = "Ta5+"; + const string oxygen = "O2-"; + const double epsu = 1e-5; + StructureAdapterPtr stru = loadTestPeriodicStructure("LiTaO3.stru"); + CHECK(30 == stru->countSites()); + BaseBondGeneratorPtr bnds = stru->createBondGenerator(); + bnds->selectAnchorSite(0); + bnds->selectSiteRange(0, 30); + // there are 3 oxygen neighbors at 2.065 + bnds->setRmax(2.1); + CHECK(3 == countBonds(*bnds)); + // Li at site 0 is isotropic, oxygens have equal msd-s towards Li + for (bnds->rewind(); !bnds->finished(); bnds->next()) { + CHECK(lithium == stru->siteAtomType(bnds->site0())); + CHECK(oxygen == stru->siteAtomType(bnds->site1())); + CHECK_THAT(0.00265968, WithinAbs(testmsd0(stru, bnds), epsu)); + CHECK_THAT(0.00710945, WithinAbs(testmsd1(stru, bnds), epsu)); + } + // there are 3 oxygen neighbors at 2.26 + bnds->setRmin(2.2); + bnds->setRmax(2.3); + CHECK(3 == countBonds(*bnds)); + for (bnds->rewind(); !bnds->finished(); bnds->next()) { + CHECK(oxygen == stru->siteAtomType(bnds->site1())); + CHECK_THAT(0.00265968, WithinAbs(testmsd0(stru, bnds), epsu)); + CHECK_THAT(0.00824319, WithinAbs(testmsd1(stru, bnds), epsu)); + } + // finally there are 4 Ta neighbors between 2.8 and 3.1 + bnds->setRmin(2.8); + bnds->setRmax(3.1); + CHECK(4 == countBonds(*bnds)); + for (bnds->rewind(); !bnds->finished(); bnds->next()) { + CHECK_THAT(0.00265968, WithinAbs(testmsd0(stru, bnds), epsu)); + CHECK(tantalum == stru->siteAtomType(bnds->site1())); + R3::Vector r01xy = bnds->r01(); + r01xy[2] = 0.0; + // for the tantalum above Li the msd equals U33 + if (R3::norm(r01xy) < 0.1) { + CHECK_THAT(0.00356, WithinAbs(testmsd1(stru, bnds), epsu)); + } + // other 3 tantalums are related by triple axis and + // have the same msd towards the central Li + else { + CHECK_THAT(0.00486942, WithinAbs(testmsd1(stru, bnds), epsu)); + } + } +} + +} // namespace srreal +} // namespace diffpy + +// End of file diff --git a/src/tests/TestPeriodicStructureAdapter.hpp b/src/tests/TestPeriodicStructureAdapter.hpp deleted file mode 100644 index f4c711a9..00000000 --- a/src/tests/TestPeriodicStructureAdapter.hpp +++ /dev/null @@ -1,371 +0,0 @@ -/***************************************************************************** - * - * libdiffpy Complex Modeling Initiative - * (c) 2013 Brookhaven Science Associates, - * Brookhaven National Laboratory. - * All rights reserved. - * - * File coded by: Pavol Juhas - * - * See AUTHORS.rst for a list of people who contributed. - * See LICENSE.rst for license information. - * - ****************************************************************************** - * - * class TestPeriodicStructureAdapter -- unit tests for an adapter - * for periodic structures - * - * class TestPeriodicStructureBondGenerator -- unit tests for bond generator - * - *****************************************************************************/ - -#include -#include -#include - -#include -#include -#include "test_helpers.hpp" -#include "serialization_helpers.hpp" - -namespace diffpy { -namespace srreal { - -using namespace std; - -// Local Helpers ------------------------------------------------------------- - -namespace { - -int countBonds(BaseBondGenerator& bnds) { - int rv = 0; - for (bnds.rewind(); !bnds.finished(); bnds.next()) ++rv; - return rv; -} - -template -double testmsd0(const Tstru& stru, const Tbnds& bnds) { - bool anisotropy0 = stru->siteAnisotropy(bnds->site0()); - double rv = - meanSquareDisplacement(bnds->Ucartesian0(), bnds->r01(), anisotropy0); - return rv; -} - -template -double testmsd1(const Tstru& stru, const Tbnds& bnds) { - bool anisotropy1 = stru->siteAnisotropy(bnds->site1()); - double rv = - meanSquareDisplacement(bnds->Ucartesian1(), bnds->r01(), anisotropy1); - return rv; -} - -} // namespace - -////////////////////////////////////////////////////////////////////////////// -// class TestPeriodicStructureAdapter -////////////////////////////////////////////////////////////////////////////// - -class TestPeriodicStructureAdapter : public CxxTest::TestSuite { - private: - StructureAdapterPtr m_ni; - StructureAdapterPtr m_kbise; - StructureAdapterPtr m_catio3; - StructureAdapterPtr m_pswt; - - public: - void setUp() { - CxxTest::setAbortTestOnFail(true); - if (!m_ni) { - m_ni = loadTestPeriodicStructure("Ni.stru"); - } - if (!m_kbise) { - m_kbise = loadTestPeriodicStructure("alpha_K2Bi8Se13.stru"); - } - if (!m_catio3) { - m_catio3 = loadTestPeriodicStructure("CaTiO3.stru"); - } - if (!m_pswt) { - m_pswt = loadTestPeriodicStructure("PbScW25TiO3.stru"); - } - CxxTest::setAbortTestOnFail(false); - } - - void test_typeid() { - StructureAdapter& r_ni = *m_ni; - TS_ASSERT(typeid(PeriodicStructureAdapter) == typeid(r_ni)); - } - - void test_countSites() { - TS_ASSERT_EQUALS(4, m_ni->countSites()); - TS_ASSERT_EQUALS(23, m_kbise->countSites()); - TS_ASSERT_EQUALS(20, m_catio3->countSites()); - TS_ASSERT_EQUALS(56, m_pswt->countSites()); - } - - void test_totalOccupancy() { - const double eps = 10 * diffpy::mathutils::DOUBLE_EPS; - TS_ASSERT_EQUALS(4.0, m_ni->totalOccupancy()); - TS_ASSERT_EQUALS(23.0, m_kbise->totalOccupancy()); - TS_ASSERT_EQUALS(20.0, m_catio3->totalOccupancy()); - TS_ASSERT_DELTA(40.0, m_pswt->totalOccupancy(), eps); - } - - void test_numberDensity() { - const double eps = 1.0e-7; - TS_ASSERT_DELTA(0.0914114, m_ni->numberDensity(), eps); - TS_ASSERT_DELTA(0.0335565, m_kbise->numberDensity(), eps); - TS_ASSERT_DELTA(0.0894566, m_catio3->numberDensity(), eps); - TS_ASSERT_DELTA(0.0760332, m_pswt->numberDensity(), eps); - } - - void test_siteCartesianPosition() { - const double eps = 1.0e-5; - R3::Vector rCa = m_catio3->siteCartesianPosition(4); - TS_ASSERT_DELTA(0.03486, rCa[0], eps); - TS_ASSERT_DELTA(0.19366, rCa[1], eps); - TS_ASSERT_DELTA(1.90975, rCa[2], eps); - } - - void test_siteAnisotropy() { - for (int i = 0; i < m_ni->countSites(); ++i) { - TS_ASSERT_EQUALS(false, m_ni->siteAnisotropy(i)); - } - for (int i = 0; i < m_catio3->countSites(); ++i) { - TS_ASSERT_EQUALS(true, m_catio3->siteAnisotropy(i)); - } - } - - void test_siteCartesianUij() { - // nickel should have all Uij equal zero. - TS_ASSERT_EQUALS(R3::zeromatrix(), m_ni->siteCartesianUij(0)); - // check CaTiO3 values - const R3::Matrix& UO2 = m_catio3->siteCartesianUij(12); - const double eps = 1e-8; - TS_ASSERT_DELTA(0.0065, UO2(0, 0), eps); - TS_ASSERT_DELTA(0.0060, UO2(1, 1), eps); - TS_ASSERT_DELTA(0.0095, UO2(2, 2), eps); - TS_ASSERT_DELTA(0.0020, UO2(0, 1), eps); - TS_ASSERT_DELTA(0.0020, UO2(1, 0), eps); - TS_ASSERT_DELTA(-0.0008, UO2(0, 2), eps); - TS_ASSERT_DELTA(-0.0008, UO2(2, 0), eps); - TS_ASSERT_DELTA(-0.0010, UO2(1, 2), eps); - TS_ASSERT_DELTA(-0.0010, UO2(2, 1), eps); - } - - void test_siteAtomType() { - TS_ASSERT_EQUALS(string("Ni"), m_ni->siteAtomType(0)); - TS_ASSERT_EQUALS(string("Ni"), m_ni->siteAtomType(3)); - TS_ASSERT_EQUALS(string("K1+"), m_kbise->siteAtomType(0)); - TS_ASSERT_EQUALS(string("Bi3+"), m_kbise->siteAtomType(2)); - TS_ASSERT_EQUALS(string("Se"), m_kbise->siteAtomType(10)); - TS_ASSERT_EQUALS(string("Se"), m_kbise->siteAtomType(22)); - } - - void test_getLattice() { - PeriodicStructureAdapterPtr pkbise = - std::dynamic_pointer_cast(m_kbise); - TS_ASSERT(pkbise); - const Lattice& L = pkbise->getLattice(); - const double eps = 1.0e-12; - TS_ASSERT_DELTA(13.768, L.a(), eps); - TS_ASSERT_DELTA(12.096, L.b(), eps); - TS_ASSERT_DELTA(4.1656, L.c(), eps); - TS_ASSERT_DELTA(89.98, L.alpha(), eps); - TS_ASSERT_DELTA(98.64, L.beta(), eps); - TS_ASSERT_DELTA(87.96, L.gamma(), eps); - } - - void test_serialization() { - StructureAdapterPtr kbise1 = dumpandload(m_kbise); - TS_ASSERT_DIFFERS(m_kbise.get(), kbise1.get()); - const double eps = 1.0e-7; - TS_ASSERT_EQUALS(23, kbise1->countSites()); - TS_ASSERT_EQUALS(23.0, kbise1->totalOccupancy()); - TS_ASSERT_DELTA(0.0335565, kbise1->numberDensity(), eps); - TS_ASSERT_EQUALS(string("K1+"), kbise1->siteAtomType(0)); - TS_ASSERT_EQUALS(string("Bi3+"), kbise1->siteAtomType(2)); - TS_ASSERT_EQUALS(string("Se"), kbise1->siteAtomType(10)); - TS_ASSERT_EQUALS(string("Se"), kbise1->siteAtomType(22)); - PeriodicStructureAdapterPtr pkbise = - std::dynamic_pointer_cast(m_kbise); - PeriodicStructureAdapterPtr pkbise1 = - std::dynamic_pointer_cast(kbise1); - const Lattice& L = pkbise->getLattice(); - const Lattice& L1 = pkbise1->getLattice(); - TS_ASSERT_EQUALS(L.a(), L1.a()); - TS_ASSERT_EQUALS(L.b(), L1.b()); - TS_ASSERT_EQUALS(L.c(), L1.c()); - TS_ASSERT_EQUALS(L.alpha(), L1.alpha()); - TS_ASSERT_EQUALS(L.beta(), L1.beta()); - TS_ASSERT_EQUALS(L.gamma(), L1.gamma()); - } - - void test_comparison() { - const PeriodicStructureAdapter& kbise0 = - static_cast(*m_kbise); - PeriodicStructureAdapter kbise1(kbise0); - PeriodicStructureAdapter kbise2(kbise0); - TS_ASSERT_EQUALS(kbise0, kbise1); - TS_ASSERT(!(kbise0 != kbise1)); - TS_ASSERT(kbise1.countSites()); - kbise1.erase(0); - TS_ASSERT_DIFFERS(kbise0, kbise1); - TS_ASSERT(!(kbise0 == kbise1)); - kbise2.setLatPar(3, 4, 5, 91, 92, 93); - TS_ASSERT_DIFFERS(kbise0, kbise2); - } - -}; // class TestPeriodicStructureAdapter - -////////////////////////////////////////////////////////////////////////////// -// class TestPeriodicStructureBondGenerator -////////////////////////////////////////////////////////////////////////////// - -class TestPeriodicStructureBondGenerator : public CxxTest::TestSuite { - private: - StructureAdapterPtr m_ni; - BaseBondGeneratorPtr m_nibnds; - - public: - void setUp() { - CxxTest::setAbortTestOnFail(true); - if (!m_ni) { - m_ni = loadTestPeriodicStructure("Ni.stru"); - } - m_nibnds = m_ni->createBondGenerator(); - CxxTest::setAbortTestOnFail(false); - } - - void test_typeid() { - BaseBondGenerator& r_nibnds = *m_nibnds; - TS_ASSERT(typeid(PeriodicStructureBondGenerator) == typeid(r_nibnds)); - } - - void test_bondCountNickel() { - m_nibnds->selectAnchorSite(0); - m_nibnds->setRmin(0); - m_nibnds->setRmax(1.0); - TS_ASSERT_EQUALS(0, countBonds(*m_nibnds)); - m_nibnds->selectAnchorSite(3); - TS_ASSERT_EQUALS(0, countBonds(*m_nibnds)); - m_nibnds->setRmin(-10); - TS_ASSERT_EQUALS(0, countBonds(*m_nibnds)); - // there are 12 nearest neighbors at 2.49 - m_nibnds->setRmax(3); - TS_ASSERT_EQUALS(12, countBonds(*m_nibnds)); - m_nibnds->selectAnchorSite(0); - TS_ASSERT_EQUALS(12, countBonds(*m_nibnds)); - // there are no self neighbors below the cell length of 3.52 - m_nibnds->selectAnchorSite(0); - m_nibnds->selectSiteRange(0, 1); - TS_ASSERT_EQUALS(0, countBonds(*m_nibnds)); - // and any other unit cell atom would give 4 neighbors - m_nibnds->selectAnchorSite(0); - m_nibnds->selectSiteRange(3, 4); - TS_ASSERT_EQUALS(4, countBonds(*m_nibnds)); - // there are no bonds between 2.6 and 3.4 - m_nibnds->setRmin(2.6); - m_nibnds->setRmax(3.4); - m_nibnds->selectSiteRange(0, 4); - TS_ASSERT_EQUALS(0, countBonds(*m_nibnds)); - // there are 6 second nearest neighbors at 3.52 - m_nibnds->setRmax(3.6); - TS_ASSERT_EQUALS(6, countBonds(*m_nibnds)); - // which sums to 18 neighbors within radius 3.6 - m_nibnds->setRmin(0); - TS_ASSERT_EQUALS(18, countBonds(*m_nibnds)); - } - - void test_bondCountWurtzite() { - StructureAdapterPtr stru = loadTestPeriodicStructure("ZnS_wurtzite.stru"); - BaseBondGeneratorPtr bnds = stru->createBondGenerator(); - TS_ASSERT_EQUALS(4, stru->countSites()); - bnds->selectAnchorSite(0); - bnds->selectSiteRange(0, 4); - bnds->setRmin(0); - // there should be no bond below the ZnS distance 2.31 - bnds->setRmax(2.2); - TS_ASSERT_EQUALS(0, countBonds(*bnds)); - // z-neighbor is slightly more distant than 3 in the lower plane - bnds->setRmax(2.35); - TS_ASSERT_EQUALS(3, countBonds(*bnds)); - bnds->setRmax(2.5); - TS_ASSERT_EQUALS(4, countBonds(*bnds)); - // there are 12 second nearest neighbors at 3.81 - bnds->setRmin(3.7); - bnds->setRmax(3.82); - TS_ASSERT_EQUALS(12, countBonds(*bnds)); - // and one more at 3.83 - bnds->setRmax(3.85); - TS_ASSERT_EQUALS(13, countBonds(*bnds)); - // making the total 17 - bnds->setRmin(0); - TS_ASSERT_EQUALS(17, countBonds(*bnds)); - // and the same happens for all other sites - bnds->selectAnchorSite(1); - TS_ASSERT_EQUALS(17, countBonds(*bnds)); - bnds->selectAnchorSite(2); - TS_ASSERT_EQUALS(17, countBonds(*bnds)); - bnds->selectAnchorSite(3); - TS_ASSERT_EQUALS(17, countBonds(*bnds)); - } - - void test_LiTaO3() { - const string lithium = "Li1+"; - const string tantalum = "Ta5+"; - const string oxygen = "O2-"; - const double epsu = 1e-5; - StructureAdapterPtr stru = loadTestPeriodicStructure("LiTaO3.stru"); - TS_ASSERT_EQUALS(30, stru->countSites()); - BaseBondGeneratorPtr bnds = stru->createBondGenerator(); - bnds->selectAnchorSite(0); - bnds->selectSiteRange(0, 30); - // there are 3 oxygen neighbors at 2.065 - bnds->setRmax(2.1); - TS_ASSERT_EQUALS(3, countBonds(*bnds)); - // Li at site 0 is isotropic, oxygens have equal msd-s towards Li - for (bnds->rewind(); !bnds->finished(); bnds->next()) { - TS_ASSERT_EQUALS(lithium, stru->siteAtomType(bnds->site0())); - TS_ASSERT_EQUALS(oxygen, stru->siteAtomType(bnds->site1())); - TS_ASSERT_DELTA(0.00265968, testmsd0(stru, bnds), epsu); - TS_ASSERT_DELTA(0.00710945, testmsd1(stru, bnds), epsu); - } - // there are 3 oxygen neighbors at 2.26 - bnds->setRmin(2.2); - bnds->setRmax(2.3); - TS_ASSERT_EQUALS(3, countBonds(*bnds)); - for (bnds->rewind(); !bnds->finished(); bnds->next()) { - TS_ASSERT_EQUALS(oxygen, stru->siteAtomType(bnds->site1())); - TS_ASSERT_DELTA(0.00265968, testmsd0(stru, bnds), epsu); - TS_ASSERT_DELTA(0.00824319, testmsd1(stru, bnds), epsu); - } - // finally there are 4 Ta neighbors between 2.8 and 3.1 - bnds->setRmin(2.8); - bnds->setRmax(3.1); - TS_ASSERT_EQUALS(4, countBonds(*bnds)); - for (bnds->rewind(); !bnds->finished(); bnds->next()) { - TS_ASSERT_DELTA(0.00265968, testmsd0(stru, bnds), epsu); - TS_ASSERT_EQUALS(tantalum, stru->siteAtomType(bnds->site1())); - R3::Vector r01xy = bnds->r01(); - r01xy[2] = 0.0; - // for the tantalum above Li the msd equals U33 - if (R3::norm(r01xy) < 0.1) { - TS_ASSERT_DELTA(0.00356, testmsd1(stru, bnds), epsu); - } - // other 3 tantalums are related by triple axis and - // have the same msd towards the central Li - else { - TS_ASSERT_DELTA(0.00486942, testmsd1(stru, bnds), epsu); - } - } - } - -}; // class TestPeriodicStructureBondGenerator - -} // namespace srreal -} // namespace diffpy - -using diffpy::srreal::TestPeriodicStructureAdapter; -using diffpy::srreal::TestPeriodicStructureBondGenerator; - -// End of file diff --git a/src/tests/TestPointsInSphere.cpp b/src/tests/TestPointsInSphere.cpp new file mode 100644 index 00000000..901df87a --- /dev/null +++ b/src/tests/TestPointsInSphere.cpp @@ -0,0 +1,190 @@ +/*********************************************************************** + * + * libdiffpy by DANSE Diffraction group + * Simon J. L. Billinge + * (c) 2006 trustees of the Michigan State University + * All rights reserved. + * + * File coded by: Pavol Juhas + * + * See AUTHORS.rst for a list of people who contributed. + * See LICENSE_DANSE.rst for license information. + * + ************************************************************************ + * + * Unit tests for PointsInSphere module + * + * Comments: + * + ***********************************************************************/ + +#include + +#include + +#include + +using namespace std; +using namespace diffpy::srreal; + +namespace { + +const double eps = 1.0e-8; + +struct vidxgroup { + double vijk[4]; + vidxgroup(double v, const int *ijk) { + vijk[0] = v; + for (size_t i = 0; i != 3; ++i) { + vijk[i + 1] = ijk[i]; + } + } + vidxgroup(double v, int i, int j, int k) { + vijk[0] = v; + vijk[1] = i; + vijk[2] = j; + vijk[3] = k; + } +}; + +bool operator<(const vidxgroup &x, const vidxgroup &y) { + if (x.vijk[0] < y.vijk[0] - eps) return true; + if (x.vijk[0] > y.vijk[0] + eps) return false; + return lexicographical_compare(x.vijk, x.vijk + 4, y.vijk, y.vijk + 4); +} + +bool operator==(const vidxgroup &x, const vidxgroup &y) { + bool eq = (fabs(x.vijk[0] - y.vijk[0]) < eps) && + equal(x.vijk + 1, x.vijk + 4, y.vijk + 1); + return eq; +} + +/* avoid warnings about unused function. +ostream& operator<<(ostream &s, const vidxgroup &x) +{ + return s << "<" << x.vijk[0] << ";" << int(x.vijk[1]) + << ',' << int(x.vijk[2]) << ',' << int(x.vijk[3]) << '>'; +} +*/ + +} // namespace + +//////////////////////////////////////////////////////////////////////// +// TestPointsInSphere +//////////////////////////////////////////////////////////////////////// + +using diffpy::srreal::pointsinsphere::LatticeParameters; + +class TestPointsInSphereFixture { + protected: + LatticeParameters *latpar; + + public: + TestPointsInSphereFixture() { + latpar = new LatticeParameters(1, 1, 1, 90, 90, 90); + } + + ~TestPointsInSphereFixture() { delete latpar; } + + protected: + int count(double Rmin, double Rmax) { + int c = 0; + PointsInSphere sph(Rmin, Rmax, *latpar); + for (; !sph.finished(); sph.next()) ++c; + return c; + } + + vector sortedPoints(double Rmin, double Rmax) { + vector ridx; + for (PointsInSphere sph(Rmin, Rmax, *latpar); not sph.finished(); + sph.next()) { + ridx.push_back(vidxgroup(sph.r(), sph.mno())); + } + sort(ridx.begin(), ridx.end()); + return ridx; + } +}; // class TestPointsInSphereFixture + +TEST_CASE_METHOD(TestPointsInSphereFixture, "TestPointsInSphere::test_Cubic", + "[PointsInSphere]") { + latpar->a = latpar->b = latpar->c = 1.0; + latpar->alpha = latpar->beta = latpar->gamma = 90.0; + latpar->update(); + CHECK(0 == count(0.0, 0.0)); + CHECK(0 == count(eps, 0.5)); + CHECK(0 == count(1.0 + eps, 1.1)); + CHECK(1 == count(0.0, eps)); + CHECK(7 == count(0.0, 1 + eps)); + CHECK(19 == count(0.0, sqrt(2.0) + eps)); + CHECK(12 == count(1.0 + eps, sqrt(2.0) + eps)); +} + +TEST_CASE_METHOD(TestPointsInSphereFixture, + "TestPointsInSphere::test_Orthorombic", "[PointsInSphere]") { + latpar->a = 1.0; + latpar->b = 2.0; + latpar->c = 3.0; + latpar->alpha = latpar->beta = latpar->gamma = 90.0; + latpar->update(); + CHECK(3 == count(0.0, 1.1)); + CHECK(4 == count(1.9, 2.1)); + vidxgroup ep[] = { + vidxgroup(0, 0, 0, 0), vidxgroup(1, -1, 0, 0), + vidxgroup(1, 1, 0, 0), vidxgroup(2, -2, 0, 0), + vidxgroup(2, 0, -1, 0), vidxgroup(2, 0, 1, 0), + vidxgroup(2, 2, 0, 0), vidxgroup(sqrt(5.0), -1, -1, 0), + vidxgroup(sqrt(5.0), -1, 1, 0), vidxgroup(sqrt(5.0), 1, -1, 0), + vidxgroup(sqrt(5.0), 1, 1, 0), vidxgroup(sqrt(8.0), -2, -1, 0), + vidxgroup(sqrt(8.0), -2, 1, 0), vidxgroup(sqrt(8.0), 2, -1, 0), + vidxgroup(sqrt(8.0), 2, 1, 0), vidxgroup(3, -3, 0, 0), + vidxgroup(3, 0, 0, -1), vidxgroup(3, 0, 0, 1), + vidxgroup(3, 3, 0, 0), + }; + vector exp_pts(ep, ep + sizeof(ep) / sizeof(vidxgroup)); + vector act_pts = sortedPoints(0.0, 3.0 + eps); + CHECK(exp_pts.size() == act_pts.size()); + for (size_t i = 0; i != exp_pts.size(); ++i) { + CHECK(exp_pts[i] == act_pts[i]); + } +} + +TEST_CASE_METHOD(TestPointsInSphereFixture, + "TestPointsInSphere::test_Hexagonal", "[PointsInSphere]") { + latpar->a = 1.0; + latpar->b = 1.0; + latpar->c = 2.0; + latpar->alpha = latpar->beta = 90.0; + latpar->gamma = 120.0; + latpar->update(); + CHECK(7 == count(0.0, 1 + eps)); + vidxgroup ep[] = { + vidxgroup(0, 0, 0, 0), vidxgroup(1, -1, -1, 0), + vidxgroup(1, -1, 0, 0), vidxgroup(1, 0, -1, 0), + vidxgroup(1, 0, 1, 0), vidxgroup(1, 1, 0, 0), + vidxgroup(1, 1, 1, 0), vidxgroup(sqrt(3.0), -2, -1, 0), + vidxgroup(sqrt(3.0), -1, -2, 0), vidxgroup(sqrt(3.0), -1, 1, 0), + vidxgroup(sqrt(3.0), 1, -1, 0), vidxgroup(sqrt(3.0), 1, 2, 0), + vidxgroup(sqrt(3.0), 2, 1, 0), vidxgroup(2, -2, -2, 0), + vidxgroup(2, -2, 0, 0), vidxgroup(2, 0, -2, 0), + vidxgroup(2, 0, 0, -1), vidxgroup(2, 0, 0, 1), + vidxgroup(2, 0, 2, 0), vidxgroup(2, 2, 0, 0), + vidxgroup(2, 2, 2, 0), + }; + vector exp_pts(ep, ep + sizeof(ep) / sizeof(vidxgroup)); + vector act_pts = sortedPoints(0.0, 2.0 + eps); + CHECK(exp_pts.size() == act_pts.size()); + for (size_t i = 0; i != exp_pts.size(); ++i) { + CHECK(exp_pts[i] == act_pts[i]); + } +} + +TEST_CASE_METHOD(TestPointsInSphereFixture, "TestPointsInSphere::test_FCC", + "[PointsInSphere]") { + latpar->a = latpar->b = latpar->c = sqrt(0.5); + latpar->alpha = latpar->beta = latpar->gamma = 60.0; + latpar->update(); + CHECK(13 == count(0.0, sqrt(0.5) + eps)); + CHECK(19 == count(0.0, 1.0 + eps)); +} + +// End of file diff --git a/src/tests/TestPointsInSphere.hpp b/src/tests/TestPointsInSphere.hpp deleted file mode 100644 index c846892b..00000000 --- a/src/tests/TestPointsInSphere.hpp +++ /dev/null @@ -1,186 +0,0 @@ -/*********************************************************************** - * - * libdiffpy by DANSE Diffraction group - * Simon J. L. Billinge - * (c) 2006 trustees of the Michigan State University - * All rights reserved. - * - * File coded by: Pavol Juhas - * - * See AUTHORS.rst for a list of people who contributed. - * See LICENSE_DANSE.rst for license information. - * - ************************************************************************ - * - * Unit tests for PointsInSphere module - * - * Comments: - * - ***********************************************************************/ - -#include - -#include - -#include - -using namespace std; -using namespace diffpy::srreal; - -namespace { - -const double eps = 1.0e-8; - -struct vidxgroup { - double vijk[4]; - vidxgroup(double v, const int *ijk) { - vijk[0] = v; - for (size_t i = 0; i != 3; ++i) { - vijk[i + 1] = ijk[i]; - } - } - vidxgroup(double v, int i, int j, int k) { - vijk[0] = v; - vijk[1] = i; - vijk[2] = j; - vijk[3] = k; - } -}; - -bool operator<(const vidxgroup &x, const vidxgroup &y) { - if (x.vijk[0] < y.vijk[0] - eps) return true; - if (x.vijk[0] > y.vijk[0] + eps) return false; - return lexicographical_compare(x.vijk, x.vijk + 4, y.vijk, y.vijk + 4); -} - -bool operator==(const vidxgroup &x, const vidxgroup &y) { - bool eq = (fabs(x.vijk[0] - y.vijk[0]) < eps) && - equal(x.vijk + 1, x.vijk + 4, y.vijk + 1); - return eq; -} - -/* avoid warnings about unused function. -ostream& operator<<(ostream &s, const vidxgroup &x) -{ - return s << "<" << x.vijk[0] << ";" << int(x.vijk[1]) - << ',' << int(x.vijk[2]) << ',' << int(x.vijk[3]) << '>'; -} -*/ - -} // namespace - -//////////////////////////////////////////////////////////////////////// -// TestPointsInSphere -//////////////////////////////////////////////////////////////////////// - -using diffpy::srreal::pointsinsphere::LatticeParameters; - -class TestPointsInSphere : public CxxTest::TestSuite { - private: - LatticeParameters *latpar; - - public: - void setUp() { latpar = new LatticeParameters(1, 1, 1, 90, 90, 90); } - - void tearDown() { delete latpar; } - - private: - int count(double Rmin, double Rmax) { - int c = 0; - PointsInSphere sph(Rmin, Rmax, *latpar); - for (; !sph.finished(); sph.next()) ++c; - return c; - } - - vector sortedPoints(double Rmin, double Rmax) { - vector ridx; - for (PointsInSphere sph(Rmin, Rmax, *latpar); not sph.finished(); - sph.next()) { - ridx.push_back(vidxgroup(sph.r(), sph.mno())); - } - sort(ridx.begin(), ridx.end()); - return ridx; - } - - public: - void test_Cubic() { - latpar->a = latpar->b = latpar->c = 1.0; - latpar->alpha = latpar->beta = latpar->gamma = 90.0; - latpar->update(); - TS_ASSERT_EQUALS(0, count(0.0, 0.0)); - TS_ASSERT_EQUALS(0, count(eps, 0.5)); - TS_ASSERT_EQUALS(0, count(1.0 + eps, 1.1)); - TS_ASSERT_EQUALS(1, count(0.0, eps)); - TS_ASSERT_EQUALS(7, count(0.0, 1 + eps)); - TS_ASSERT_EQUALS(19, count(0.0, sqrt(2.0) + eps)); - TS_ASSERT_EQUALS(12, count(1.0 + eps, sqrt(2.0) + eps)); - } - - void test_Orthorombic() { - latpar->a = 1.0; - latpar->b = 2.0; - latpar->c = 3.0; - latpar->alpha = latpar->beta = latpar->gamma = 90.0; - latpar->update(); - TS_ASSERT_EQUALS(3, count(0.0, 1.1)); - TS_ASSERT_EQUALS(4, count(1.9, 2.1)); - vidxgroup ep[] = { - vidxgroup(0, 0, 0, 0), vidxgroup(1, -1, 0, 0), - vidxgroup(1, 1, 0, 0), vidxgroup(2, -2, 0, 0), - vidxgroup(2, 0, -1, 0), vidxgroup(2, 0, 1, 0), - vidxgroup(2, 2, 0, 0), vidxgroup(sqrt(5.0), -1, -1, 0), - vidxgroup(sqrt(5.0), -1, 1, 0), vidxgroup(sqrt(5.0), 1, -1, 0), - vidxgroup(sqrt(5.0), 1, 1, 0), vidxgroup(sqrt(8.0), -2, -1, 0), - vidxgroup(sqrt(8.0), -2, 1, 0), vidxgroup(sqrt(8.0), 2, -1, 0), - vidxgroup(sqrt(8.0), 2, 1, 0), vidxgroup(3, -3, 0, 0), - vidxgroup(3, 0, 0, -1), vidxgroup(3, 0, 0, 1), - vidxgroup(3, 3, 0, 0), - }; - vector exp_pts(ep, ep + sizeof(ep) / sizeof(vidxgroup)); - vector act_pts = sortedPoints(0.0, 3.0 + eps); - TS_ASSERT_EQUALS(exp_pts.size(), act_pts.size()); - for (size_t i = 0; i != exp_pts.size(); ++i) { - TS_ASSERT_EQUALS(exp_pts[i], act_pts[i]); - } - } - - void test_Hexagonal() { - latpar->a = 1.0; - latpar->b = 1.0; - latpar->c = 2.0; - latpar->alpha = latpar->beta = 90.0; - latpar->gamma = 120.0; - latpar->update(); - TS_ASSERT_EQUALS(7, count(0.0, 1 + eps)); - vidxgroup ep[] = { - vidxgroup(0, 0, 0, 0), vidxgroup(1, -1, -1, 0), - vidxgroup(1, -1, 0, 0), vidxgroup(1, 0, -1, 0), - vidxgroup(1, 0, 1, 0), vidxgroup(1, 1, 0, 0), - vidxgroup(1, 1, 1, 0), vidxgroup(sqrt(3.0), -2, -1, 0), - vidxgroup(sqrt(3.0), -1, -2, 0), vidxgroup(sqrt(3.0), -1, 1, 0), - vidxgroup(sqrt(3.0), 1, -1, 0), vidxgroup(sqrt(3.0), 1, 2, 0), - vidxgroup(sqrt(3.0), 2, 1, 0), vidxgroup(2, -2, -2, 0), - vidxgroup(2, -2, 0, 0), vidxgroup(2, 0, -2, 0), - vidxgroup(2, 0, 0, -1), vidxgroup(2, 0, 0, 1), - vidxgroup(2, 0, 2, 0), vidxgroup(2, 2, 0, 0), - vidxgroup(2, 2, 2, 0), - }; - vector exp_pts(ep, ep + sizeof(ep) / sizeof(vidxgroup)); - vector act_pts = sortedPoints(0.0, 2.0 + eps); - TS_ASSERT_EQUALS(exp_pts.size(), act_pts.size()); - for (size_t i = 0; i != exp_pts.size(); ++i) { - TS_ASSERT_EQUALS(exp_pts[i], act_pts[i]); - } - } - - void test_FCC() { - latpar->a = latpar->b = latpar->c = sqrt(0.5); - latpar->alpha = latpar->beta = latpar->gamma = 60.0; - latpar->update(); - TS_ASSERT_EQUALS(13, count(0.0, sqrt(0.5) + eps)); - TS_ASSERT_EQUALS(19, count(0.0, 1.0 + eps)); - } - -}; // class TestPointsInSphere - -// End of file diff --git a/src/tests/TestR3linalg.cpp b/src/tests/TestR3linalg.cpp new file mode 100644 index 00000000..53307e9a --- /dev/null +++ b/src/tests/TestR3linalg.cpp @@ -0,0 +1,105 @@ +/***************************************************************************** + * + * libdiffpy by DANSE Diffraction group + * Simon J. L. Billinge + * (c) 2009 The Trustees of Columbia University + * in the City of New York. All rights reserved. + * + * File coded by: Pavol Juhas + * + * See AUTHORS.rst for a list of people who contributed. + * See LICENSE_DANSE.rst for license information. + * + ****************************************************************************** + * + * TestR3linalg -- unit test suite for the R3linalg module + * + *****************************************************************************/ + +#include +#include + +#include + +using Catch::Matchers::WithinAbs; + +using namespace std; +using namespace diffpy::srreal; +using diffpy::mathutils::EpsilonEqual; + +class TestR3linalgFixture { + protected: + double precision; + EpsilonEqual allclose; + + public: + TestR3linalgFixture() { + precision = 1.0e-12; + allclose = EpsilonEqual(precision); + } +}; // class TestR3linalgFixture + +TEST_CASE_METHOD(TestR3linalgFixture, "TestR3linalg::test_determinant", + "[R3linalg]") { + // default lattice should be cartesian + R3::Matrix A1(9, 1, 9, 6, 7, 4, 0, 2, 9); + R3::Matrix A2(9, 1, 9, 0, 2, 9, 6, 7, 4); + double detA = 549; + CHECK(detA == R3::determinant(A1)); + CHECK(-detA == R3::determinant(A2)); +} + +TEST_CASE_METHOD(TestR3linalgFixture, "TestR3linalg::test_inverse", + "[R3linalg]") { + R3::Matrix A(0.5359, -0.5904, 0.8670, -0.0053, 0.7559, 0.2692, -0.8926, + 0.9424, 0.9692); + R3::Matrix invA(0.49063197005867, 1.42323870111089, -0.83420736316541, + -0.24089965988852, 1.32489393619466, -0.15249839300481, + 0.68609361943181, 0.02249568627913, 0.41178393851247); + CHECK(allclose(invA, R3::inverse(A))); +} + +TEST_CASE_METHOD(TestR3linalgFixture, "TestR3linalg::test_transpose", + "[R3linalg]") { + R3::Matrix A(0.5359, -0.5904, 0.8670, -0.0053, 0.7559, 0.2692, -0.8926, + 0.9424, 0.9692); + R3::Matrix Atrans(0.5359, -0.0053, -0.8926, -0.5904, 0.7559, 0.9424, 0.8670, + 0.2692, 0.9692); + CHECK(Atrans == R3::trans(A)); +} + +TEST_CASE_METHOD(TestR3linalgFixture, "TestR3linalg::test_norm", "[R3linalg]") { + R3::Vector v1(3.0, 4.0, 0.0); + R3::Vector v2(0.67538115798129, 0.72108424545413, 0.15458914063315); + CHECK_THAT(5.0, WithinAbs(R3::norm(v1), precision)); + CHECK_THAT(1.0, WithinAbs(R3::norm(v2), precision)); +} + +TEST_CASE_METHOD(TestR3linalgFixture, "TestR3linalg::test_dot", "[R3linalg]") { + R3::Vector v1(-0.97157650177843, 0.43206192654604, 0.56318686427062); + R3::Vector v2(-0.04787719419083, 0.55895824010234, -0.34472910285751); + double dot_v1v2 = 0.09387402846316; + CHECK_THAT(dot_v1v2, WithinAbs(R3::dot(v1, v2), precision)); +} + +TEST_CASE_METHOD(TestR3linalgFixture, "TestR3linalg::test_cross", + "[R3linalg]") { + R3::Vector v1(-0.55160549932839, -0.58291452407504, 0.12378162306543); + R3::Vector v2(0.60842511285200, -0.97946444006248, -0.02828214306095); + R3::Vector v1xv2(0.13772577008800, 0.05971126233738, 0.89493780662849); + CHECK(allclose(v1xv2, R3::cross(v1, v2))); +} + +TEST_CASE_METHOD(TestR3linalgFixture, "TestR3linalg::test_product", + "[R3linalg]") { + R3::Matrix M(0.459631856585519, 0.726448904209060, 0.085844209317482, + 0.806838095807669, 0.240116998848762, 0.305032463662873, + 0.019487235483683, 0.580605953831255, 0.726077578738676); + R3::Vector v(0.608652521912322, 0.519716469261062, 0.842577887601566); + R3::Vector prod_Mv(0.729633980805669, 0.872890409522479, 0.925388343907868); + R3::Vector prod_vM(0.715502648789536, 1.056153454546559, 0.822556602046019); + CHECK(allclose(prod_Mv, R3::mxvecproduct(M, v))); + CHECK(allclose(prod_vM, R3::mxvecproduct(v, M))); +} + +// End of file diff --git a/src/tests/TestR3linalg.hpp b/src/tests/TestR3linalg.hpp deleted file mode 100644 index 930bc932..00000000 --- a/src/tests/TestR3linalg.hpp +++ /dev/null @@ -1,98 +0,0 @@ -/***************************************************************************** - * - * libdiffpy by DANSE Diffraction group - * Simon J. L. Billinge - * (c) 2009 The Trustees of Columbia University - * in the City of New York. All rights reserved. - * - * File coded by: Pavol Juhas - * - * See AUTHORS.rst for a list of people who contributed. - * See LICENSE_DANSE.rst for license information. - * - ****************************************************************************** - * - * class TestR3linalg -- unit test suite for the R3linalg module - * - *****************************************************************************/ - -#include - -#include - -using namespace std; -using namespace diffpy::srreal; -using diffpy::mathutils::EpsilonEqual; - -class TestR3linalg : public CxxTest::TestSuite { - private: - double precision; - EpsilonEqual allclose; - - public: - void setUp() { - precision = 1.0e-12; - allclose = EpsilonEqual(precision); - } - - void test_determinant() { - // default lattice should be cartesian - R3::Matrix A1(9, 1, 9, 6, 7, 4, 0, 2, 9); - R3::Matrix A2(9, 1, 9, 0, 2, 9, 6, 7, 4); - double detA = 549; - TS_ASSERT_EQUALS(detA, R3::determinant(A1)); - TS_ASSERT_EQUALS(-detA, R3::determinant(A2)); - } - - void test_inverse() { - R3::Matrix A(0.5359, -0.5904, 0.8670, -0.0053, 0.7559, 0.2692, -0.8926, - 0.9424, 0.9692); - R3::Matrix invA(0.49063197005867, 1.42323870111089, -0.83420736316541, - -0.24089965988852, 1.32489393619466, -0.15249839300481, - 0.68609361943181, 0.02249568627913, 0.41178393851247); - TS_ASSERT(allclose(invA, R3::inverse(A))); - } - - void test_transpose() { - R3::Matrix A(0.5359, -0.5904, 0.8670, -0.0053, 0.7559, 0.2692, -0.8926, - 0.9424, 0.9692); - R3::Matrix Atrans(0.5359, -0.0053, -0.8926, -0.5904, 0.7559, 0.9424, 0.8670, - 0.2692, 0.9692); - TS_ASSERT_EQUALS(Atrans, R3::trans(A)); - } - - void test_norm() { - R3::Vector v1(3.0, 4.0, 0.0); - R3::Vector v2(0.67538115798129, 0.72108424545413, 0.15458914063315); - TS_ASSERT_DELTA(5.0, R3::norm(v1), precision); - TS_ASSERT_DELTA(1.0, R3::norm(v2), precision); - } - - void test_dot() { - R3::Vector v1(-0.97157650177843, 0.43206192654604, 0.56318686427062); - R3::Vector v2(-0.04787719419083, 0.55895824010234, -0.34472910285751); - double dot_v1v2 = 0.09387402846316; - TS_ASSERT_DELTA(dot_v1v2, R3::dot(v1, v2), precision); - } - - void test_cross() { - R3::Vector v1(-0.55160549932839, -0.58291452407504, 0.12378162306543); - R3::Vector v2(0.60842511285200, -0.97946444006248, -0.02828214306095); - R3::Vector v1xv2(0.13772577008800, 0.05971126233738, 0.89493780662849); - TS_ASSERT(allclose(v1xv2, R3::cross(v1, v2))); - } - - void test_product() { - R3::Matrix M(0.459631856585519, 0.726448904209060, 0.085844209317482, - 0.806838095807669, 0.240116998848762, 0.305032463662873, - 0.019487235483683, 0.580605953831255, 0.726077578738676); - R3::Vector v(0.608652521912322, 0.519716469261062, 0.842577887601566); - R3::Vector prod_Mv(0.729633980805669, 0.872890409522479, 0.925388343907868); - R3::Vector prod_vM(0.715502648789536, 1.056153454546559, 0.822556602046019); - TS_ASSERT(allclose(prod_Mv, R3::mxvecproduct(M, v))); - TS_ASSERT(allclose(prod_vM, R3::mxvecproduct(v, M))); - } - -}; // class TestR3linalg - -// End of file diff --git a/src/tests/TestRuntimePath.hpp b/src/tests/TestRuntimePath.cpp similarity index 53% rename from src/tests/TestRuntimePath.hpp rename to src/tests/TestRuntimePath.cpp index 99c7c879..68c1bcde 100644 --- a/src/tests/TestRuntimePath.hpp +++ b/src/tests/TestRuntimePath.cpp @@ -12,7 +12,7 @@ * ****************************************************************************** * - * class TestRuntimePath -- unit tests for the resolution of datafile paths + * TestRuntimePath -- unit tests for the resolution of datafile paths * *****************************************************************************/ @@ -20,14 +20,14 @@ #include #include #include -#include +#include #include #include "test_helpers.hpp" using diffpy::runtimepath::datapath; -class TestRuntimePath : public CxxTest::TestSuite { - private: +class TestRuntimePathFixture { + protected: bool mhas_diffpyruntime; std::string mdiffpyruntime; @@ -38,45 +38,47 @@ class TestRuntimePath : public CxxTest::TestSuite { void setRuntime(const char* value) { #ifdef _WIN32 - TS_ASSERT_EQUALS(_putenv_s("DIFFPYRUNTIME", value ? value : ""), 0); + CHECK(_putenv_s("DIFFPYRUNTIME", value ? value : "") == 0); #else if (value) { - TS_ASSERT_EQUALS(setenv("DIFFPYRUNTIME", value, 1), 0); + CHECK(setenv("DIFFPYRUNTIME", value, 1) == 0); } else { - TS_ASSERT_EQUALS(unsetenv("DIFFPYRUNTIME"), 0); + CHECK(unsetenv("DIFFPYRUNTIME") == 0); } #endif } public: - void setUp() { + TestRuntimePathFixture() { char* pe = getenv("DIFFPYRUNTIME"); mhas_diffpyruntime = pe; if (mhas_diffpyruntime) mdiffpyruntime = pe; setRuntime(nullptr); } - void tearDown() { + ~TestRuntimePathFixture() { setRuntime(mhas_diffpyruntime ? mdiffpyruntime.c_str() : nullptr); } +}; // class TestRuntimePathFixture - void test_datapath() { - std::string f = datapath("f0_WaasKirf.dat"); - TS_ASSERT(filereadable(f)); - std::string d = datapath(""); - TS_ASSERT_EQUALS(f, d + '/' + "f0_WaasKirf.dat"); - } +TEST_CASE_METHOD(TestRuntimePathFixture, "TestRuntimePath::test_datapath", + "[RuntimePath]") { + std::string f = datapath("f0_WaasKirf.dat"); + CHECK(filereadable(f)); + std::string d = datapath(""); + CHECK(f == d + '/' + "f0_WaasKirf.dat"); +} - void test_envvar_lookup() { - using namespace std; - setRuntime("does/not/exist"); - TS_ASSERT_THROWS(datapath("f0_WaasKirf.dat"), runtime_error); - TS_ASSERT_THROWS(datapath("NaCl.cif"), runtime_error); - string td = prepend_testdata_dir(""); - setRuntime(td.c_str()); - string fnacl = prepend_testdata_dir("NaCl.cif"); - TS_ASSERT_EQUALS(fnacl, datapath("NaCl.cif")); - } -}; +TEST_CASE_METHOD(TestRuntimePathFixture, "TestRuntimePath::test_envvar_lookup", + "[RuntimePath]") { + using namespace std; + setRuntime("does/not/exist"); + CHECK_THROWS_AS(datapath("f0_WaasKirf.dat"), runtime_error); + CHECK_THROWS_AS(datapath("NaCl.cif"), runtime_error); + string td = prepend_testdata_dir(""); + setRuntime(td.c_str()); + string fnacl = prepend_testdata_dir("NaCl.cif"); + CHECK(fnacl == datapath("NaCl.cif")); +} // End of file diff --git a/src/tests/TestScatteringFactorTable.cpp b/src/tests/TestScatteringFactorTable.cpp new file mode 100644 index 00000000..34952af0 --- /dev/null +++ b/src/tests/TestScatteringFactorTable.cpp @@ -0,0 +1,206 @@ +/***************************************************************************** + * + * libdiffpy by DANSE Diffraction group + * Simon J. L. Billinge + * (c) 2009 The Trustees of Columbia University + * in the City of New York. All rights reserved. + * + * File coded by: Pavol Juhas + * + * See AUTHORS.rst for a list of people who contributed. + * See LICENSE_DANSE.rst for license information. + * + ****************************************************************************** + * + * TestScatteringFactorTable -- unit tests for implementations + * of the ScatteringFactorTable class + * + *****************************************************************************/ + +#include +#include +#include +#include + +#include +#include +#include "serialization_helpers.hpp" + +using Catch::Matchers::WithinAbs; + +using namespace std; +using namespace diffpy::srreal; + +class TestScatteringFactorTableFixture { + protected: + double mtol; + double meps; + ScatteringFactorTablePtr msftb; + + public: + TestScatteringFactorTableFixture() { + mtol = 1.0e-4; + meps = 10 * diffpy::mathutils::DOUBLE_EPS; + } +}; // class TestScatteringFactorTableFixture + +TEST_CASE_METHOD(TestScatteringFactorTableFixture, + "TestScatteringFactorTable::test_factory", + "[ScatteringFactorTable]") { + ScatteringFactorTablePtr sfx0, sfx1, sfn0, sfn1; + CHECK_THROWS_AS(ScatteringFactorTable::createByType("invalid"), + invalid_argument); + sfx0 = ScatteringFactorTable::createByType("xray"); + sfx1 = ScatteringFactorTable::createByType("X"); + CHECK(sfx0.get()); + CHECK(sfx1.get()); + CHECK(sfx0->type() == sfx1->type()); + ScatteringFactorTable& r_sfx0 = *sfx0; + ScatteringFactorTable& r_sfx1 = *sfx1; + CHECK(typeid(r_sfx0) == typeid(r_sfx1)); + sfn0 = ScatteringFactorTable::createByType("neutron"); + sfn1 = ScatteringFactorTable::createByType("N"); + CHECK(sfn0.get()); + CHECK(sfn1.get()); + CHECK(sfn0->type() == sfn1->type()); + ScatteringFactorTable& r_sfn0 = *sfn0; + ScatteringFactorTable& r_sfn1 = *sfn1; + CHECK(typeid(r_sfn0) == typeid(r_sfn1)); +} + +TEST_CASE_METHOD(TestScatteringFactorTableFixture, + "TestScatteringFactorTable::test_setCustomAs", + "[ScatteringFactorTable]") { + msftb = ScatteringFactorTable::createByType("X"); + CHECK_THROWS_AS(msftb->lookup(""), invalid_argument); + CHECK_THAT(6.0, WithinAbs(msftb->lookup("C"), 0.01)); + msftb->setCustomAs("C", "C", 6.3); + CHECK_THAT(6.3, WithinAbs(msftb->lookup("C"), meps)); + msftb->setCustomAs("C", "C", 6.4); + CHECK_THAT(6.4, WithinAbs(msftb->lookup("C"), meps)); + CHECK_THROWS_AS(msftb->lookup("Ccustom"), invalid_argument); + msftb->setCustomAs("Ccustom", "C", 6.5); + CHECK_THAT(6.5, WithinAbs(msftb->lookup("Ccustom"), meps)); + msftb->resetCustom("C"); + CHECK_THAT(6.5, WithinAbs(msftb->lookup("Ccustom"), meps)); + CHECK_THAT(6.0, WithinAbs(msftb->lookup("C"), 0.01)); + msftb->resetAll(); + CHECK_THROWS_AS(msftb->lookup("Ccustom"), invalid_argument); + CHECK_THAT(6.0, WithinAbs(msftb->lookup("C"), 0.01)); + msftb->setCustomAs("Calias", "C"); + CHECK(msftb->lookup("C", 0) == msftb->lookup("Calias", 0)); + CHECK_THAT(msftb->lookup("C", 2.5), + WithinAbs(msftb->lookup("Calias", 2.5), meps)); +} + +TEST_CASE_METHOD(TestScatteringFactorTableFixture, + "TestScatteringFactorTable::test_getCustomSymbols", + "[ScatteringFactorTable]") { + msftb = ScatteringFactorTable::createByType("X"); + CHECK(msftb->getCustomSymbols().empty()); + msftb->setCustomAs("C", "C", 6.1); + CHECK(1u == msftb->getCustomSymbols().size()); + CHECK(1u == msftb->getCustomSymbols().count("C")); + msftb->setCustomAs("C", "C", 6.3); + CHECK(1u == msftb->getCustomSymbols().size()); + CHECK(1u == msftb->getCustomSymbols().count("C")); + CHECK_THAT(6.3, WithinAbs(msftb->lookup("C"), meps)); + ScatteringFactorTablePtr sftb1 = msftb->clone(); + msftb->resetCustom("C"); + CHECK(msftb->getCustomSymbols().empty()); + CHECK(1u == sftb1->getCustomSymbols().size()); + CHECK(1u == sftb1->getCustomSymbols().count("C")); + CHECK_THAT(6.3, WithinAbs(sftb1->lookup("C"), meps)); + sftb1->resetAll(); + CHECK(msftb->getCustomSymbols().empty()); +} + +TEST_CASE_METHOD(TestScatteringFactorTableFixture, + "TestScatteringFactorTable::test_ticker", + "[ScatteringFactorTable]") { + using diffpy::eventticker::EventTicker; + msftb = ScatteringFactorTable::createByType("X"); + EventTicker e0 = msftb->ticker(); + CHECK(e0 == msftb->clone()->ticker()); + CHECK(e0 == dumpandload(msftb)->ticker()); + msftb->setCustomAs("C", "C", 6.1); + CHECK(e0 < msftb->ticker()); +} + +TEST_CASE_METHOD(TestScatteringFactorTableFixture, + "TestScatteringFactorTable::test_SFTXray", + "[ScatteringFactorTable]") { + msftb = ScatteringFactorTable::createByType("X"); + CHECK_THAT(1.0, WithinAbs(msftb->lookup("H"), 0.01)); + CHECK_THAT(8.0, WithinAbs(msftb->lookup("O"), 0.01)); + CHECK_THAT(10.0, WithinAbs(msftb->lookup("O2-"), 0.01)); + CHECK_THAT(11.0, WithinAbs(msftb->lookup("Na"), 0.01)); + CHECK_THAT(10.0, WithinAbs(msftb->lookup("Na+"), 0.01)); + CHECK(msftb->lookup("Na+") == msftb->lookup("Na1+")); + CHECK_THAT(74.0, WithinAbs(msftb->lookup("W"), 0.04)); + CHECK_THAT(88.0, WithinAbs(msftb->lookup("Ra"), 0.04)); + CHECK(msftb->lookup("Si") == msftb->lookup("Si0+")); + CHECK("xray" == msftb->clone()->type()); +} + +TEST_CASE_METHOD(TestScatteringFactorTableFixture, + "TestScatteringFactorTable::test_SFTElectron", + "[ScatteringFactorTable]") { + using diffpy::mathutils::DOUBLE_MAX; + msftb = ScatteringFactorTable::createByType("E"); + CHECK(DOUBLE_MAX == msftb->lookup("H")); + CHECK(DOUBLE_MAX == msftb->lookup("Ra")); + CHECK_THAT(3.42104, WithinAbs(msftb->lookup("Na", 1), 1e-5)); + CHECK_THAT(1.34868, WithinAbs(msftb->lookup("Na", 3), 1e-5)); + CHECK_THAT(0.832158, WithinAbs(msftb->lookup("Na", 5), 1e-5)); + CHECK_THROWS_AS(msftb->lookup("H4+"), invalid_argument); + CHECK_THROWS_AS(msftb->lookup("H4+", 3), invalid_argument); + CHECK("electron" == msftb->clone()->type()); +} + +TEST_CASE_METHOD(TestScatteringFactorTableFixture, + "TestScatteringFactorTable::test_SFTNeutron", + "[ScatteringFactorTable]") { + msftb = ScatteringFactorTable::createByType("N"); + CHECK_THAT(3.63, WithinAbs(msftb->lookup("Na"), mtol)); + CHECK_THAT(-3.37, WithinAbs(msftb->lookup("Ti"), mtol)); + CHECK_THAT(5.805, WithinAbs(msftb->lookup("O"), mtol)); + CHECK_THAT(6.6484, WithinAbs(msftb->lookup("C"), mtol)); + CHECK(msftb->lookup("Na") == msftb->lookup("Na1+")); + CHECK(msftb->lookup("Ge") == msftb->lookup("Ge0+")); + CHECK("neutron" == msftb->clone()->type()); +} + +TEST_CASE_METHOD(TestScatteringFactorTableFixture, + "TestScatteringFactorTable::test_ElectronNumber", + "[ScatteringFactorTable]") { + msftb = ScatteringFactorTable::createByType("electronnumber"); + CHECK(8.0 == msftb->lookup("O")); + CHECK(10.0 == msftb->lookup("O2-")); + CHECK(18.0 == msftb->lookup("K+")); + CHECK(18.0 == msftb->lookup("K1+")); + CHECK(68.0 == msftb->lookup("W6+")); + CHECK_THROWS_AS(msftb->lookup("H4+"), invalid_argument); + CHECK_THROWS_AS(msftb->lookup("O3+"), invalid_argument); + CHECK("electronnumber" == msftb->clone()->type()); +} + +TEST_CASE_METHOD(TestScatteringFactorTableFixture, + "TestScatteringFactorTable::test_serialization", + "[ScatteringFactorTable]") { + ScatteringFactorTablePtr sftb1; + msftb = ScatteringFactorTable::createByType("electronnumber"); + msftb->setCustomAs("H", "H", 1.23); + sftb1 = dumpandload(msftb); + CHECK(string("electronnumber") == sftb1->type()); + CHECK(1.23 == sftb1->lookup("H")); + CHECK(1u == sftb1->getCustomSymbols().size()); + sftb1 = dumpandload(ScatteringFactorTable::createByType("N")); + CHECK(string("neutron") == sftb1->type()); + sftb1 = dumpandload(ScatteringFactorTable::createByType("X")); + CHECK(string("xray") == sftb1->type()); + sftb1 = dumpandload(ScatteringFactorTable::createByType("E")); + CHECK(string("electron") == sftb1->type()); +} + +// End of file diff --git a/src/tests/TestScatteringFactorTable.hpp b/src/tests/TestScatteringFactorTable.hpp deleted file mode 100644 index d5a5381b..00000000 --- a/src/tests/TestScatteringFactorTable.hpp +++ /dev/null @@ -1,185 +0,0 @@ -/***************************************************************************** - * - * libdiffpy by DANSE Diffraction group - * Simon J. L. Billinge - * (c) 2009 The Trustees of Columbia University - * in the City of New York. All rights reserved. - * - * File coded by: Pavol Juhas - * - * See AUTHORS.rst for a list of people who contributed. - * See LICENSE_DANSE.rst for license information. - * - ****************************************************************************** - * - * class TestScatteringFactorTable -- unit tests for implementations - * of the ScatteringFactorTable class - * - *****************************************************************************/ - -#include -#include -#include - -#include -#include -#include "serialization_helpers.hpp" - -using namespace std; -using namespace diffpy::srreal; - -class TestScatteringFactorTable : public CxxTest::TestSuite { - private: - double mtol; - double meps; - ScatteringFactorTablePtr msftb; - - public: - void setUp() { - mtol = 1.0e-4; - meps = 10 * diffpy::mathutils::DOUBLE_EPS; - } - - void test_factory() { - ScatteringFactorTablePtr sfx0, sfx1, sfn0, sfn1; - TS_ASSERT_THROWS(ScatteringFactorTable::createByType("invalid"), - invalid_argument); - sfx0 = ScatteringFactorTable::createByType("xray"); - sfx1 = ScatteringFactorTable::createByType("X"); - TS_ASSERT(sfx0.get()); - TS_ASSERT(sfx1.get()); - TS_ASSERT_EQUALS(sfx0->type(), sfx1->type()); - ScatteringFactorTable& r_sfx0 = *sfx0; - ScatteringFactorTable& r_sfx1 = *sfx1; - TS_ASSERT(typeid(r_sfx0) == typeid(r_sfx1)); - sfn0 = ScatteringFactorTable::createByType("neutron"); - sfn1 = ScatteringFactorTable::createByType("N"); - TS_ASSERT(sfn0.get()); - TS_ASSERT(sfn1.get()); - TS_ASSERT_EQUALS(sfn0->type(), sfn1->type()); - ScatteringFactorTable& r_sfn0 = *sfn0; - ScatteringFactorTable& r_sfn1 = *sfn1; - TS_ASSERT(typeid(r_sfn0) == typeid(r_sfn1)); - } - - void test_setCustomAs() { - msftb = ScatteringFactorTable::createByType("X"); - TS_ASSERT_THROWS(msftb->lookup(""), invalid_argument); - TS_ASSERT_DELTA(6.0, msftb->lookup("C"), 0.01); - msftb->setCustomAs("C", "C", 6.3); - TS_ASSERT_DELTA(6.3, msftb->lookup("C"), meps); - msftb->setCustomAs("C", "C", 6.4); - TS_ASSERT_DELTA(6.4, msftb->lookup("C"), meps); - TS_ASSERT_THROWS(msftb->lookup("Ccustom"), invalid_argument); - msftb->setCustomAs("Ccustom", "C", 6.5); - TS_ASSERT_DELTA(6.5, msftb->lookup("Ccustom"), meps); - msftb->resetCustom("C"); - TS_ASSERT_DELTA(6.5, msftb->lookup("Ccustom"), meps); - TS_ASSERT_DELTA(6.0, msftb->lookup("C"), 0.01); - msftb->resetAll(); - TS_ASSERT_THROWS(msftb->lookup("Ccustom"), invalid_argument); - TS_ASSERT_DELTA(6.0, msftb->lookup("C"), 0.01); - msftb->setCustomAs("Calias", "C"); - TS_ASSERT_EQUALS(msftb->lookup("C", 0), msftb->lookup("Calias", 0)); - TS_ASSERT_DELTA(msftb->lookup("C", 2.5), msftb->lookup("Calias", 2.5), - meps); - } - - void test_getCustomSymbols() { - msftb = ScatteringFactorTable::createByType("X"); - TS_ASSERT(msftb->getCustomSymbols().empty()); - msftb->setCustomAs("C", "C", 6.1); - TS_ASSERT_EQUALS(1u, msftb->getCustomSymbols().size()); - TS_ASSERT_EQUALS(1u, msftb->getCustomSymbols().count("C")); - msftb->setCustomAs("C", "C", 6.3); - TS_ASSERT_EQUALS(1u, msftb->getCustomSymbols().size()); - TS_ASSERT_EQUALS(1u, msftb->getCustomSymbols().count("C")); - TS_ASSERT_DELTA(6.3, msftb->lookup("C"), meps); - ScatteringFactorTablePtr sftb1 = msftb->clone(); - msftb->resetCustom("C"); - TS_ASSERT(msftb->getCustomSymbols().empty()); - TS_ASSERT_EQUALS(1u, sftb1->getCustomSymbols().size()); - TS_ASSERT_EQUALS(1u, sftb1->getCustomSymbols().count("C")); - TS_ASSERT_DELTA(6.3, sftb1->lookup("C"), meps); - sftb1->resetAll(); - TS_ASSERT(msftb->getCustomSymbols().empty()); - } - - void test_ticker() { - using diffpy::eventticker::EventTicker; - msftb = ScatteringFactorTable::createByType("X"); - EventTicker e0 = msftb->ticker(); - TS_ASSERT_EQUALS(e0, msftb->clone()->ticker()); - TS_ASSERT_EQUALS(e0, dumpandload(msftb)->ticker()); - msftb->setCustomAs("C", "C", 6.1); - TS_ASSERT_LESS_THAN(e0, msftb->ticker()); - } - - void test_SFTXray() { - msftb = ScatteringFactorTable::createByType("X"); - TS_ASSERT_DELTA(1.0, msftb->lookup("H"), 0.01); - TS_ASSERT_DELTA(8.0, msftb->lookup("O"), 0.01); - TS_ASSERT_DELTA(10.0, msftb->lookup("O2-"), 0.01); - TS_ASSERT_DELTA(11.0, msftb->lookup("Na"), 0.01); - TS_ASSERT_DELTA(10.0, msftb->lookup("Na+"), 0.01); - TS_ASSERT_EQUALS(msftb->lookup("Na+"), msftb->lookup("Na1+")); - TS_ASSERT_DELTA(74.0, msftb->lookup("W"), 0.04); - TS_ASSERT_DELTA(88.0, msftb->lookup("Ra"), 0.04); - TS_ASSERT_EQUALS(msftb->lookup("Si"), msftb->lookup("Si0+")); - TS_ASSERT_EQUALS("xray", msftb->clone()->type()); - } - - void test_SFTElectron() { - using diffpy::mathutils::DOUBLE_MAX; - msftb = ScatteringFactorTable::createByType("E"); - TS_ASSERT_EQUALS(DOUBLE_MAX, msftb->lookup("H")); - TS_ASSERT_EQUALS(DOUBLE_MAX, msftb->lookup("Ra")); - TS_ASSERT_DELTA(3.42104, msftb->lookup("Na", 1), 1e-5); - TS_ASSERT_DELTA(1.34868, msftb->lookup("Na", 3), 1e-5); - TS_ASSERT_DELTA(0.832158, msftb->lookup("Na", 5), 1e-5); - TS_ASSERT_THROWS(msftb->lookup("H4+"), invalid_argument); - TS_ASSERT_THROWS(msftb->lookup("H4+", 3), invalid_argument); - TS_ASSERT_EQUALS("electron", msftb->clone()->type()); - } - - void test_SFTNeutron() { - msftb = ScatteringFactorTable::createByType("N"); - TS_ASSERT_DELTA(3.63, msftb->lookup("Na"), mtol); - TS_ASSERT_DELTA(-3.37, msftb->lookup("Ti"), mtol); - TS_ASSERT_DELTA(5.805, msftb->lookup("O"), mtol); - TS_ASSERT_DELTA(6.6484, msftb->lookup("C"), mtol); - TS_ASSERT_EQUALS(msftb->lookup("Na"), msftb->lookup("Na1+")); - TS_ASSERT_EQUALS(msftb->lookup("Ge"), msftb->lookup("Ge0+")); - TS_ASSERT_EQUALS("neutron", msftb->clone()->type()); - } - - void test_ElectronNumber() { - msftb = ScatteringFactorTable::createByType("electronnumber"); - TS_ASSERT_EQUALS(8.0, msftb->lookup("O")); - TS_ASSERT_EQUALS(10.0, msftb->lookup("O2-")); - TS_ASSERT_EQUALS(18.0, msftb->lookup("K+")); - TS_ASSERT_EQUALS(18.0, msftb->lookup("K1+")); - TS_ASSERT_EQUALS(68.0, msftb->lookup("W6+")); - TS_ASSERT_THROWS(msftb->lookup("H4+"), invalid_argument); - TS_ASSERT_THROWS(msftb->lookup("O3+"), invalid_argument); - TS_ASSERT_EQUALS("electronnumber", msftb->clone()->type()); - } - - void test_serialization() { - ScatteringFactorTablePtr sftb1; - msftb = ScatteringFactorTable::createByType("electronnumber"); - msftb->setCustomAs("H", "H", 1.23); - sftb1 = dumpandload(msftb); - TS_ASSERT_EQUALS(string("electronnumber"), sftb1->type()); - TS_ASSERT_EQUALS(1.23, sftb1->lookup("H")); - TS_ASSERT_EQUALS(1u, sftb1->getCustomSymbols().size()); - sftb1 = dumpandload(ScatteringFactorTable::createByType("N")); - TS_ASSERT_EQUALS(string("neutron"), sftb1->type()); - sftb1 = dumpandload(ScatteringFactorTable::createByType("X")); - TS_ASSERT_EQUALS(string("xray"), sftb1->type()); - sftb1 = dumpandload(ScatteringFactorTable::createByType("E")); - TS_ASSERT_EQUALS(string("electron"), sftb1->type()); - } -}; - -// End of file diff --git a/src/tests/TestSphericalShapeEnvelope.cpp b/src/tests/TestSphericalShapeEnvelope.cpp new file mode 100644 index 00000000..e4f9e665 --- /dev/null +++ b/src/tests/TestSphericalShapeEnvelope.cpp @@ -0,0 +1,88 @@ +/***************************************************************************** + * + * libdiffpy by DANSE Diffraction group + * Simon J. L. Billinge + * (c) 2009 The Trustees of Columbia University + * in the City of New York. All rights reserved. + * + * File coded by: Pavol Juhas + * + * See AUTHORS.rst for a list of people who contributed. + * See LICENSE_DANSE.rst for license information. + * + ****************************************************************************** + * + * TestSphericalShapeEnvelope -- unit tests for SphericalShapeEnvelope + * + *****************************************************************************/ + +#include +#include + +#include +#include "serialization_helpers.hpp" + +using Catch::Matchers::WithinAbs; + +using namespace std; +using namespace diffpy::srreal; + +class TestSphericalShapeEnvelopeFixture { + protected: + PDFEnvelopePtr menvelope; + + public: + TestSphericalShapeEnvelopeFixture() { + menvelope = PDFEnvelope::createByType("sphericalshape"); + } +}; // class TestSphericalShapeEnvelopeFixture + +TEST_CASE_METHOD(TestSphericalShapeEnvelopeFixture, + "TestSphericalShapeEnvelope::test_create", + "[SphericalShapeEnvelope]") { + CHECK(0.0 == menvelope->getDoubleAttr("spdiameter")); + menvelope->setDoubleAttr("spdiameter", 13.0); + CHECK(13.0 == menvelope->getDoubleAttr("spdiameter")); + PDFEnvelopePtr e1 = menvelope->create(); + CHECK(0.0 == e1->getDoubleAttr("spdiameter")); +} + +TEST_CASE_METHOD(TestSphericalShapeEnvelopeFixture, + "TestSphericalShapeEnvelope::test_copy", + "[SphericalShapeEnvelope]") { + menvelope->setDoubleAttr("spdiameter", 13.0); + CHECK(13.0 == menvelope->getDoubleAttr("spdiameter")); + PDFEnvelopePtr e1 = menvelope->clone(); + CHECK(13.0 == e1->getDoubleAttr("spdiameter")); +} + +TEST_CASE_METHOD(TestSphericalShapeEnvelopeFixture, + "TestSphericalShapeEnvelope::test_type", + "[SphericalShapeEnvelope]") { + CHECK("sphericalshape" == menvelope->type()); +} + +TEST_CASE_METHOD(TestSphericalShapeEnvelopeFixture, + "TestSphericalShapeEnvelope::test_parentheses_operator", + "[SphericalShapeEnvelope]") { + const PDFEnvelope& fne = *menvelope; + CHECK(1.0 == fne(0.0)); + CHECK(1.0 == fne(100.0)); + menvelope->setDoubleAttr("spdiameter", 10.0); + CHECK(1.0 == fne(0.0)); + CHECK(0.0 == fne(10.0)); + CHECK(0.0 == fne(100)); + CHECK(fne(9.99) > 0.0); + CHECK_THAT(0.3125, WithinAbs(fne(5), 1e-8)); +} + +TEST_CASE_METHOD(TestSphericalShapeEnvelopeFixture, + "TestSphericalShapeEnvelope::test_serialization", + "[SphericalShapeEnvelope]") { + menvelope->setDoubleAttr("spdiameter", 13.1); + PDFEnvelopePtr e1 = dumpandload(menvelope); + CHECK(string("sphericalshape") == e1->type()); + CHECK(13.1 == e1->getDoubleAttr("spdiameter")); +} + +// End of file diff --git a/src/tests/TestSphericalShapeEnvelope.hpp b/src/tests/TestSphericalShapeEnvelope.hpp deleted file mode 100644 index 422b7e82..00000000 --- a/src/tests/TestSphericalShapeEnvelope.hpp +++ /dev/null @@ -1,72 +0,0 @@ -/***************************************************************************** - * - * libdiffpy by DANSE Diffraction group - * Simon J. L. Billinge - * (c) 2009 The Trustees of Columbia University - * in the City of New York. All rights reserved. - * - * File coded by: Pavol Juhas - * - * See AUTHORS.rst for a list of people who contributed. - * See LICENSE_DANSE.rst for license information. - * - ****************************************************************************** - * - * class TestSphericalShapeEnvelope -- unit tests for SphericalShapeEnvelope - * - *****************************************************************************/ - -#include - -#include -#include "serialization_helpers.hpp" - -using namespace std; -using namespace diffpy::srreal; - -class TestSphericalShapeEnvelope : public CxxTest::TestSuite { - private: - PDFEnvelopePtr menvelope; - - public: - void setUp() { menvelope = PDFEnvelope::createByType("sphericalshape"); } - - void test_create() { - TS_ASSERT_EQUALS(0.0, menvelope->getDoubleAttr("spdiameter")); - menvelope->setDoubleAttr("spdiameter", 13.0); - TS_ASSERT_EQUALS(13.0, menvelope->getDoubleAttr("spdiameter")); - PDFEnvelopePtr e1 = menvelope->create(); - TS_ASSERT_EQUALS(0.0, e1->getDoubleAttr("spdiameter")); - } - - void test_copy() { - menvelope->setDoubleAttr("spdiameter", 13.0); - TS_ASSERT_EQUALS(13.0, menvelope->getDoubleAttr("spdiameter")); - PDFEnvelopePtr e1 = menvelope->clone(); - TS_ASSERT_EQUALS(13.0, e1->getDoubleAttr("spdiameter")); - } - - void test_type() { TS_ASSERT_EQUALS("sphericalshape", menvelope->type()); } - - void test_parentheses_operator() { - const PDFEnvelope& fne = *menvelope; - TS_ASSERT_EQUALS(1.0, fne(0.0)); - TS_ASSERT_EQUALS(1.0, fne(100.0)); - menvelope->setDoubleAttr("spdiameter", 10.0); - TS_ASSERT_EQUALS(1.0, fne(0.0)); - TS_ASSERT_EQUALS(0.0, fne(10.0)); - TS_ASSERT_EQUALS(0.0, fne(100)); - TS_ASSERT(fne(9.99) > 0.0); - TS_ASSERT_DELTA(0.3125, fne(5), 1e-8); - } - - void test_serialization() { - menvelope->setDoubleAttr("spdiameter", 13.1); - PDFEnvelopePtr e1 = dumpandload(menvelope); - TS_ASSERT_EQUALS(string("sphericalshape"), e1->type()); - TS_ASSERT_EQUALS(13.1, e1->getDoubleAttr("spdiameter")); - } - -}; // class TestSphericalShapeEnvelope - -// End of file diff --git a/src/tests/TestStepCutEnvelope.cpp b/src/tests/TestStepCutEnvelope.cpp new file mode 100644 index 00000000..ad1be40f --- /dev/null +++ b/src/tests/TestStepCutEnvelope.cpp @@ -0,0 +1,83 @@ +/***************************************************************************** + * + * libdiffpy by DANSE Diffraction group + * Simon J. L. Billinge + * (c) 2009 The Trustees of Columbia University + * in the City of New York. All rights reserved. + * + * File coded by: Pavol Juhas + * + * See AUTHORS.rst for a list of people who contributed. + * See LICENSE_DANSE.rst for license information. + * + ****************************************************************************** + * + * TestStepCutEnvelope -- unit tests for StepCutEnvelope class + * + *****************************************************************************/ + +#include + +#include +#include "serialization_helpers.hpp" + +using namespace std; +using namespace diffpy::srreal; + +class TestStepCutEnvelopeFixture { + protected: + PDFEnvelopePtr menvelope; + + public: + TestStepCutEnvelopeFixture() { + menvelope = PDFEnvelope::createByType("stepcut"); + } +}; // class TestStepCutEnvelopeFixture + +TEST_CASE_METHOD(TestStepCutEnvelopeFixture, "TestStepCutEnvelope::test_create", + "[StepCutEnvelope]") { + CHECK(0.0 == menvelope->getDoubleAttr("stepcut")); + menvelope->setDoubleAttr("stepcut", 13.0); + CHECK(13.0 == menvelope->getDoubleAttr("stepcut")); + PDFEnvelopePtr e1(menvelope->create()); + CHECK(0.0 == e1->getDoubleAttr("stepcut")); +} + +TEST_CASE_METHOD(TestStepCutEnvelopeFixture, "TestStepCutEnvelope::test_copy", + "[StepCutEnvelope]") { + menvelope->setDoubleAttr("stepcut", 13.0); + CHECK(13.0 == menvelope->getDoubleAttr("stepcut")); + PDFEnvelopePtr e1(menvelope->clone()); + CHECK(13.0 == e1->getDoubleAttr("stepcut")); +} + +TEST_CASE_METHOD(TestStepCutEnvelopeFixture, "TestStepCutEnvelope::test_type", + "[StepCutEnvelope]") { + CHECK("stepcut" == menvelope->type()); +} + +TEST_CASE_METHOD(TestStepCutEnvelopeFixture, + "TestStepCutEnvelope::test_parentheses_operator", + "[StepCutEnvelope]") { + const PDFEnvelope& fne = *menvelope; + CHECK(1.0 == fne(-1.0)); + CHECK(1.0 == fne(0.0)); + CHECK(1.0 == fne(+1.0)); + menvelope->setDoubleAttr("stepcut", 1.0); + CHECK(1.0 == fne(-1.0)); + CHECK(1.0 == fne(0.0)); + CHECK(1.0 == fne(+1.0)); + CHECK(0.0 == fne(+1.0001)); + CHECK(0.0 == fne(+2.0)); +} + +TEST_CASE_METHOD(TestStepCutEnvelopeFixture, + "TestStepCutEnvelope::test_serialization", + "[StepCutEnvelope]") { + menvelope->setDoubleAttr("stepcut", 13.1); + PDFEnvelopePtr e1 = dumpandload(menvelope); + CHECK(string("stepcut") == e1->type()); + CHECK(13.1 == e1->getDoubleAttr("stepcut")); +} + +// End of file diff --git a/src/tests/TestStepCutEnvelope.hpp b/src/tests/TestStepCutEnvelope.hpp deleted file mode 100644 index 895854d8..00000000 --- a/src/tests/TestStepCutEnvelope.hpp +++ /dev/null @@ -1,73 +0,0 @@ -/***************************************************************************** - * - * libdiffpy by DANSE Diffraction group - * Simon J. L. Billinge - * (c) 2009 The Trustees of Columbia University - * in the City of New York. All rights reserved. - * - * File coded by: Pavol Juhas - * - * See AUTHORS.rst for a list of people who contributed. - * See LICENSE_DANSE.rst for license information. - * - ****************************************************************************** - * - * class TestStepCutEnvelope -- unit tests for StepCutEnvelope class - * - *****************************************************************************/ - -#include - -#include -#include "serialization_helpers.hpp" - -using namespace std; -using namespace diffpy::srreal; - -class TestStepCutEnvelope : public CxxTest::TestSuite { - private: - PDFEnvelopePtr menvelope; - - public: - void setUp() { menvelope = PDFEnvelope::createByType("stepcut"); } - - void test_create() { - TS_ASSERT_EQUALS(0.0, menvelope->getDoubleAttr("stepcut")); - menvelope->setDoubleAttr("stepcut", 13.0); - TS_ASSERT_EQUALS(13.0, menvelope->getDoubleAttr("stepcut")); - PDFEnvelopePtr e1(menvelope->create()); - TS_ASSERT_EQUALS(0.0, e1->getDoubleAttr("stepcut")); - } - - void test_copy() { - menvelope->setDoubleAttr("stepcut", 13.0); - TS_ASSERT_EQUALS(13.0, menvelope->getDoubleAttr("stepcut")); - PDFEnvelopePtr e1(menvelope->clone()); - TS_ASSERT_EQUALS(13.0, e1->getDoubleAttr("stepcut")); - } - - void test_type() { TS_ASSERT_EQUALS("stepcut", menvelope->type()); } - - void test_parentheses_operator() { - const PDFEnvelope& fne = *menvelope; - TS_ASSERT_EQUALS(1.0, fne(-1.0)); - TS_ASSERT_EQUALS(1.0, fne(0.0)); - TS_ASSERT_EQUALS(1.0, fne(+1.0)); - menvelope->setDoubleAttr("stepcut", 1.0); - TS_ASSERT_EQUALS(1.0, fne(-1.0)); - TS_ASSERT_EQUALS(1.0, fne(0.0)); - TS_ASSERT_EQUALS(1.0, fne(+1.0)); - TS_ASSERT_EQUALS(0.0, fne(+1.0001)); - TS_ASSERT_EQUALS(0.0, fne(+2.0)); - } - - void test_serialization() { - menvelope->setDoubleAttr("stepcut", 13.1); - PDFEnvelopePtr e1 = dumpandload(menvelope); - TS_ASSERT_EQUALS(string("stepcut"), e1->type()); - TS_ASSERT_EQUALS(13.1, e1->getDoubleAttr("stepcut")); - } - -}; // class TestStepCutEnvelope - -// End of file diff --git a/src/tests/objcryst_helpers.cpp b/src/tests/objcryst_helpers.cpp index 4b99eb42..05f6c3ae 100644 --- a/src/tests/objcryst_helpers.cpp +++ b/src/tests/objcryst_helpers.cpp @@ -16,9 +16,10 @@ * *****************************************************************************/ -#include #include -#include +#include +#include +#include #include @@ -29,20 +30,33 @@ using namespace std; namespace { -void NoMessages(const string&) {} +struct QuietObjCryst { + decltype(ObjCryst::fpObjCrystInformUser) saved = + ObjCryst::fpObjCrystInformUser; + QuietObjCryst() { + ObjCryst::fpObjCrystInformUser = [](const string&) {}; + } + ~QuietObjCryst() { ObjCryst::fpObjCrystInformUser = saved; } +}; } // namespace ObjCryst::Crystal* loadTestCrystal(const string& tailname) { string fp = prepend_testdata_dir(tailname); ifstream in(fp.c_str()); - // suppress the chatty output from CreateCrystalFromCIF - void (*OCMessages)(const string&) = ObjCryst::fpObjCrystInformUser; - ObjCryst::fpObjCrystInformUser = NoMessages; + if (!in) throw runtime_error("Cannot open " + fp); + QuietObjCryst quiet; ObjCryst::CIF cif(in); ObjCryst::Crystal* crst = ObjCryst::CreateCrystalFromCIF(cif, false); - ObjCryst::fpObjCrystInformUser = OCMessages; + if (!crst) throw runtime_error("No crystal structure in " + fp); return crst; } +const ObjCryst::Crystal& cachedTestCrystal(const string& tailname) { + static map> crystals; + auto& crystal = crystals[tailname]; + if (!crystal) crystal.reset(loadTestCrystal(tailname)); + return *crystal; +} + // End of file diff --git a/src/tests/objcryst_helpers.hpp b/src/tests/objcryst_helpers.hpp index 78c49ba2..360732cf 100644 --- a/src/tests/objcryst_helpers.hpp +++ b/src/tests/objcryst_helpers.hpp @@ -23,5 +23,6 @@ #include ObjCryst::Crystal* loadTestCrystal(const std::string& tailname); +const ObjCryst::Crystal& cachedTestCrystal(const std::string& tailname); #endif // OBJCRYST_HELPERS_HPP_INCLUDED