From 738d4622f8fe3909621cfaf2ae5ee54f705f40ee Mon Sep 17 00:00:00 2001 From: Tieqiong Zhang Date: Sat, 1 Feb 2025 18:09:37 -0500 Subject: [PATCH 01/56] gitignore --- .gitignore | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/.gitignore b/.gitignore index 496ca8a9..449bdbd4 100644 --- a/.gitignore +++ b/.gitignore @@ -30,3 +30,10 @@ tags # source distribution tarball libdiffpy-*.tar.gz + + +.vs/* +.exp +.lib +.dll +.vscode/* From 59d568851c39e93580fcdb74bb89c85982cea9a2 Mon Sep 17 00:00:00 2001 From: Tieqiong Zhang Date: Sat, 1 Feb 2025 18:10:57 -0500 Subject: [PATCH 02/56] fix win include and export --- src/diffpy/Attributes.hpp | 12 +++-- src/diffpy/EventTicker.hpp | 4 +- src/diffpy/Export.hpp | 11 ++++ src/diffpy/mathutils.ipp | 6 --- src/diffpy/runtimepath.cpp | 51 ++++++++++++++++++- src/diffpy/serialization.hpp | 8 ++- src/diffpy/serialization.ipp | 35 +++++++------ src/diffpy/srreal/AtomRadiiTable.cpp | 2 +- src/diffpy/srreal/AtomRadiiTable.hpp | 4 +- src/diffpy/srreal/AtomicStructureAdapter.hpp | 14 ++--- src/diffpy/srreal/BVParam.hpp | 8 +-- src/diffpy/srreal/BVParametersTable.hpp | 4 +- src/diffpy/srreal/BVSCalculator.hpp | 4 +- src/diffpy/srreal/BaseBondGenerator.hpp | 4 +- src/diffpy/srreal/BaseDebyeSum.hpp | 4 +- src/diffpy/srreal/BondCalculator.hpp | 4 +- src/diffpy/srreal/ConstantPeakWidth.hpp | 4 +- src/diffpy/srreal/ConstantRadiiTable.hpp | 4 +- src/diffpy/srreal/CroppedGaussianProfile.hpp | 4 +- src/diffpy/srreal/CrystalStructureAdapter.hpp | 12 +++-- src/diffpy/srreal/DebyePDFCalculator.hpp | 4 +- src/diffpy/srreal/DebyeWallerPeakWidth.hpp | 4 +- src/diffpy/srreal/GaussianProfile.hpp | 4 +- src/diffpy/srreal/JeongPeakWidth.hpp | 4 +- src/diffpy/srreal/LinearBaseline.hpp | 4 +- src/diffpy/srreal/NoMetaStructureAdapter.hpp | 6 ++- .../srreal/NoSymmetryStructureAdapter.hpp | 6 ++- .../srreal/ObjCrystStructureAdapter.hpp | 7 ++- src/diffpy/srreal/OverlapCalculator.hpp | 4 +- src/diffpy/srreal/PDFBaseline.cpp | 2 +- src/diffpy/srreal/PDFBaseline.hpp | 4 +- src/diffpy/srreal/PDFCalculator.hpp | 4 +- src/diffpy/srreal/PDFEnvelope.cpp | 2 +- src/diffpy/srreal/PDFEnvelope.hpp | 6 ++- src/diffpy/srreal/PDFUtils.hpp | 6 ++- src/diffpy/srreal/PQEvaluator.hpp | 12 +++-- src/diffpy/srreal/PairQuantity.hpp | 8 +-- src/diffpy/srreal/PeakProfile.cpp | 2 +- src/diffpy/srreal/PeakProfile.hpp | 4 +- src/diffpy/srreal/PeakWidthModel.cpp | 2 +- src/diffpy/srreal/PeakWidthModel.hpp | 6 ++- .../srreal/PeriodicStructureAdapter.hpp | 9 ++-- src/diffpy/srreal/QResolutionEnvelope.hpp | 4 +- src/diffpy/srreal/QuantityType.hpp | 4 +- src/diffpy/srreal/R3linalg.cpp | 4 ++ src/diffpy/srreal/R3linalg.hpp | 8 +-- src/diffpy/srreal/SFTElectron.hpp | 4 +- src/diffpy/srreal/SFTElectronNumber.hpp | 4 +- src/diffpy/srreal/SFTNeutron.hpp | 4 +- src/diffpy/srreal/SFTXray.hpp | 4 +- src/diffpy/srreal/ScaleEnvelope.hpp | 4 +- src/diffpy/srreal/ScatteringFactorTable.cpp | 2 +- src/diffpy/srreal/ScatteringFactorTable.hpp | 6 ++- src/diffpy/srreal/SphericalShapeEnvelope.hpp | 4 +- src/diffpy/srreal/StepCutEnvelope.hpp | 4 +- src/diffpy/srreal/StructureAdapter.hpp | 16 ++++-- src/diffpy/srreal/StructureDifference.hpp | 4 +- src/diffpy/srreal/ZeroBaseline.hpp | 4 +- src/diffpy/version.tpl | 4 +- 59 files changed, 280 insertions(+), 113 deletions(-) create mode 100644 src/diffpy/Export.hpp diff --git a/src/diffpy/Attributes.hpp b/src/diffpy/Attributes.hpp index 8e6a9ae6..3c84ea04 100644 --- a/src/diffpy/Attributes.hpp +++ b/src/diffpy/Attributes.hpp @@ -26,6 +26,8 @@ #include #include +#include + namespace diffpy { namespace attributes { @@ -46,7 +48,7 @@ class DoubleAttributeError : public std::runtime_error /// @class BaseDoubleAttribute /// @brief abstract base class for accessing a particular double attribute -class BaseDoubleAttribute +class DLL_EXPORT BaseDoubleAttribute { public: @@ -75,7 +77,7 @@ class BaseAttributesVisitor /// should derive from Attributes and register their setter and /// getter methods in their constructors. -class Attributes +class DLL_EXPORT Attributes { public: @@ -98,7 +100,7 @@ class Attributes protected: - friend void registerBaseDoubleAttribute(Attributes*, + friend DLL_EXPORT void registerBaseDoubleAttribute(Attributes*, const std::string&, attributes::BaseDoubleAttribute* pa); template void registerDoubleAttribute(const std::string& name, T* obj, Getter); @@ -186,10 +188,10 @@ class Attributes // non-member helpers -void registerBaseDoubleAttribute(Attributes* obj, +DLL_EXPORT void registerBaseDoubleAttribute(Attributes* obj, const std::string& name, BaseDoubleAttribute* pa); -void throwDoubleAttributeReadOnly(); +DLL_EXPORT void throwDoubleAttributeReadOnly(); } // namespace attributes } // namespace diffpy diff --git a/src/diffpy/EventTicker.hpp b/src/diffpy/EventTicker.hpp index 54d11ef7..c0024a9d 100644 --- a/src/diffpy/EventTicker.hpp +++ b/src/diffpy/EventTicker.hpp @@ -26,10 +26,12 @@ #include #include +#include + namespace diffpy { namespace eventticker { -class EventTicker +class DLL_EXPORT EventTicker { public: diff --git a/src/diffpy/Export.hpp b/src/diffpy/Export.hpp new file mode 100644 index 00000000..142b61fb --- /dev/null +++ b/src/diffpy/Export.hpp @@ -0,0 +1,11 @@ +#pragma once + +#if defined(_WIN32) + #if defined(BUILD_DLL) + #define DLL_EXPORT __declspec(dllexport) + #else + #define DLL_EXPORT __declspec(dllimport) + #endif +#else + #define DLL_EXPORT +#endif diff --git a/src/diffpy/mathutils.ipp b/src/diffpy/mathutils.ipp index acf4d24c..2c60d83a 100644 --- a/src/diffpy/mathutils.ipp +++ b/src/diffpy/mathutils.ipp @@ -34,12 +34,6 @@ inline double remainder(double x, double y) fmod(x, y) : (fmod(x, y) + y); } - -inline double log2(double x) -{ - return log(x) / log(2.0); -} - #endif // _MSC_VER namespace diffpy { diff --git a/src/diffpy/runtimepath.cpp b/src/diffpy/runtimepath.cpp index 0957e74e..0f7299ee 100644 --- a/src/diffpy/runtimepath.cpp +++ b/src/diffpy/runtimepath.cpp @@ -23,8 +23,57 @@ #include #include #include -#include +#include // not available on Windows, conda install dlfcn-win32 + +// libgen.h is not available on Windows +#ifdef _WIN32 +#include +#include + +// A simple replacement for dirname() on Windows. +inline std::string win_dirname(const std::string& path) +{ + // Find the last occurrence of either '\' or '/' + size_t pos = path.find_last_of("\\/"); + if (pos == std::string::npos) + return "."; // No directory found, return current directory + return path.substr(0, pos); +} + +// Overload to match the expected C-style interface if needed: +inline char* dirname(char* path) +{ + static std::string dir; + dir = win_dirname(std::string(path)); + return const_cast(dir.c_str()); +} +#else #include +#endif + +// S_ISDIR, PATH_MAX, realpath, etc. +#ifdef _WIN32 +#include +#include // for _fullpath +#include // for _stat +#include // for _getcwd, etc. + +// Define S_ISDIR using the Windows _S_IFDIR flag from sys/stat.h. +#ifndef S_ISDIR +#define S_ISDIR(mode) (((mode) & _S_IFDIR) == _S_IFDIR) +#endif + +// Use MAX_PATH from as PATH_MAX if not defined. +#ifndef PATH_MAX +#define PATH_MAX MAX_PATH +#endif + +// Define realpath in terms of _fullpath. +// _fullpath returns a pointer to the resolved path in the buffer you provide. +// The usage should be nearly equivalent. +#define realpath(N, R) _fullpath((R), (N), PATH_MAX) +#endif + #include #include diff --git a/src/diffpy/serialization.hpp b/src/diffpy/serialization.hpp index 9d80d14d..3c8f9015 100644 --- a/src/diffpy/serialization.hpp +++ b/src/diffpy/serialization.hpp @@ -32,9 +32,10 @@ namespace diffpy { template - std::string serialization_tostring(const T& tobj); +std::string serialization_tostring(const T& tobj); + template - void serialization_fromstring(T& tobj, const std::string& s); +void serialization_fromstring(T& tobj, const std::string& s); namespace serialization { @@ -44,4 +45,7 @@ typedef ::boost::archive::binary_oarchive oarchive; } // namespace serialization } // namespace diffpy +// Pull in the inline definitions and macro logic +#include "serialization.ipp" + #endif // SERIALIZATION_HPP_INCLUDED diff --git a/src/diffpy/serialization.ipp b/src/diffpy/serialization.ipp index fba56fff..6663b680 100644 --- a/src/diffpy/serialization.ipp +++ b/src/diffpy/serialization.ipp @@ -54,25 +54,30 @@ void serialization_fromstring(T& tobj, const std::string& s) ia >> tobj; } -} // namespace diffpy - // Macros -------------------------------------------------------------------- +// Macros for explicit instantiation: +// - On Windows (_WIN32), make them no-ops to avoid dllimport/dllexport issues. +// - On other platforms, keep them as they were. +#ifdef _WIN32 + + #define DIFFPY_INSTANTIATE_SERIALIZATION(C) /* no-op */ + #define DIFFPY_INSTANTIATE_PTR_SERIALIZATION(C) /* no-op */ -/// Insert explicit instantiations for serialization_tostring and -/// serialization_fromstring for class C. +#else -#define DIFFPY_INSTANTIATE_SERIALIZATION(C) \ - template \ - std::string \ - diffpy::serialization_tostring(const C&); \ - template \ - void \ - diffpy::serialization_fromstring(C&, const std::string&); \ + /// Insert explicit instantiations for serialization_tostring and + /// serialization_fromstring for class C. + #define DIFFPY_INSTANTIATE_SERIALIZATION(C) \ + template std::string diffpy::serialization_tostring(const C&); \ + template void diffpy::serialization_fromstring(C&, const std::string&); -/// Insert explicit instantiations for serialization_tostring and -/// serialization_fromstring for type boost::shared_ptr. + /// Insert explicit instantiations for serialization_tostring and + /// serialization_fromstring for type boost::shared_ptr. + #define DIFFPY_INSTANTIATE_PTR_SERIALIZATION(C) \ + DIFFPY_INSTANTIATE_SERIALIZATION(boost::shared_ptr) -#define DIFFPY_INSTANTIATE_PTR_SERIALIZATION(C) \ - DIFFPY_INSTANTIATE_SERIALIZATION(boost::shared_ptr) +#endif + +} // namespace diffpy #endif // SERIALIZATION_IPP_INCLUDED diff --git a/src/diffpy/srreal/AtomRadiiTable.cpp b/src/diffpy/srreal/AtomRadiiTable.cpp index 20995f69..ba338350 100644 --- a/src/diffpy/srreal/AtomRadiiTable.cpp +++ b/src/diffpy/srreal/AtomRadiiTable.cpp @@ -29,7 +29,7 @@ namespace diffpy { // Unique instantiation of the template registry base class. -template class HasClassRegistry; +template class DLL_EXPORT HasClassRegistry; namespace srreal { diff --git a/src/diffpy/srreal/AtomRadiiTable.hpp b/src/diffpy/srreal/AtomRadiiTable.hpp index 830adc40..7eb17034 100644 --- a/src/diffpy/srreal/AtomRadiiTable.hpp +++ b/src/diffpy/srreal/AtomRadiiTable.hpp @@ -32,10 +32,12 @@ #include +#include + namespace diffpy { namespace srreal { -class AtomRadiiTable : +class DLL_EXPORT AtomRadiiTable : public diffpy::HasClassRegistry { public: diff --git a/src/diffpy/srreal/AtomicStructureAdapter.hpp b/src/diffpy/srreal/AtomicStructureAdapter.hpp index 6eb3cbc2..640de9b4 100644 --- a/src/diffpy/srreal/AtomicStructureAdapter.hpp +++ b/src/diffpy/srreal/AtomicStructureAdapter.hpp @@ -24,10 +24,12 @@ #include +#include + namespace diffpy { namespace srreal { -class Atom +class DLL_EXPORT Atom { public: @@ -64,17 +66,17 @@ class Atom // Functions related to class Atom -bool operator<(const Atom&, const Atom&); +DLL_EXPORT bool operator<(const Atom&, const Atom&); inline bool operator>(const Atom& a0, const Atom& a1) { return a1 < a0; } inline bool operator<=(const Atom& a0, const Atom& a1) { return !(a1 < a0); } inline bool operator>=(const Atom& a0, const Atom& a1) { return !(a0 < a1); } -bool operator==(const Atom&, const Atom&); -bool operator!=(const Atom&, const Atom&); -size_t hash_value(const Atom&); +DLL_EXPORT bool operator==(const Atom&, const Atom&); +DLL_EXPORT bool operator!=(const Atom&, const Atom&); +DLL_EXPORT size_t hash_value(const Atom&); -class AtomicStructureAdapter : public StructureAdapter +class DLL_EXPORT AtomicStructureAdapter : public StructureAdapter { public: diff --git a/src/diffpy/srreal/BVParam.hpp b/src/diffpy/srreal/BVParam.hpp index 061fb98c..e36e692e 100644 --- a/src/diffpy/srreal/BVParam.hpp +++ b/src/diffpy/srreal/BVParam.hpp @@ -22,10 +22,12 @@ #include #include +#include + namespace diffpy { namespace srreal { -class BVParam +class DLL_EXPORT BVParam { public: @@ -56,14 +58,14 @@ class BVParam std::string mref_id; // hash function for cation-anion bond type only - class BondHash + class DLL_EXPORT BondHash { public: size_t operator()(const BVParam&) const; }; // equality test for cation-anion bond type - class BondEqual + class DLL_EXPORT BondEqual { public: bool operator()(const BVParam& bp0, const BVParam& bp1) const; diff --git a/src/diffpy/srreal/BVParametersTable.hpp b/src/diffpy/srreal/BVParametersTable.hpp index 690ecb04..55fc715b 100644 --- a/src/diffpy/srreal/BVParametersTable.hpp +++ b/src/diffpy/srreal/BVParametersTable.hpp @@ -27,12 +27,14 @@ #include #include +#include + namespace diffpy { namespace srreal { typedef boost::shared_ptr BVParametersTablePtr; -class BVParametersTable +class DLL_EXPORT BVParametersTable { public: diff --git a/src/diffpy/srreal/BVSCalculator.hpp b/src/diffpy/srreal/BVSCalculator.hpp index c864b31b..d7cb6514 100644 --- a/src/diffpy/srreal/BVSCalculator.hpp +++ b/src/diffpy/srreal/BVSCalculator.hpp @@ -22,10 +22,12 @@ #include #include +#include + namespace diffpy { namespace srreal { -class BVSCalculator : public PairQuantity +class DLL_EXPORT BVSCalculator : public PairQuantity { public: diff --git a/src/diffpy/srreal/BaseBondGenerator.hpp b/src/diffpy/srreal/BaseBondGenerator.hpp index 691c4666..849cdd25 100644 --- a/src/diffpy/srreal/BaseBondGenerator.hpp +++ b/src/diffpy/srreal/BaseBondGenerator.hpp @@ -23,13 +23,15 @@ #include #include +#include + namespace diffpy { namespace srreal { /// Use zero default for rmax so any misconfiguration of r-limits is obvious. const double DEFAULT_BONDGENERATOR_RMAX = 0.0; -class BaseBondGenerator +class DLL_EXPORT BaseBondGenerator { public: diff --git a/src/diffpy/srreal/BaseDebyeSum.hpp b/src/diffpy/srreal/BaseDebyeSum.hpp index 5eaea6f5..ac608fcc 100644 --- a/src/diffpy/srreal/BaseDebyeSum.hpp +++ b/src/diffpy/srreal/BaseDebyeSum.hpp @@ -23,10 +23,12 @@ #include #include +#include + namespace diffpy { namespace srreal { -class BaseDebyeSum : +class DLL_EXPORT BaseDebyeSum : public PairQuantity, public PeakWidthModelOwner { diff --git a/src/diffpy/srreal/BondCalculator.hpp b/src/diffpy/srreal/BondCalculator.hpp index 49c6876f..aceb700a 100644 --- a/src/diffpy/srreal/BondCalculator.hpp +++ b/src/diffpy/srreal/BondCalculator.hpp @@ -21,10 +21,12 @@ #include +#include + namespace diffpy { namespace srreal { -class BondCalculator : public PairQuantity +class DLL_EXPORT BondCalculator : public PairQuantity { public: diff --git a/src/diffpy/srreal/ConstantPeakWidth.hpp b/src/diffpy/srreal/ConstantPeakWidth.hpp index 8c0f552b..4ccddc4f 100644 --- a/src/diffpy/srreal/ConstantPeakWidth.hpp +++ b/src/diffpy/srreal/ConstantPeakWidth.hpp @@ -21,11 +21,13 @@ #include +#include + namespace diffpy { namespace srreal { -class ConstantPeakWidth : public PeakWidthModel +class DLL_EXPORT ConstantPeakWidth : public PeakWidthModel { public: diff --git a/src/diffpy/srreal/ConstantRadiiTable.hpp b/src/diffpy/srreal/ConstantRadiiTable.hpp index 5a047c75..8be15bba 100644 --- a/src/diffpy/srreal/ConstantRadiiTable.hpp +++ b/src/diffpy/srreal/ConstantRadiiTable.hpp @@ -21,10 +21,12 @@ #include +#include + namespace diffpy { namespace srreal { -class ConstantRadiiTable : public AtomRadiiTable +class DLL_EXPORT ConstantRadiiTable : public AtomRadiiTable { public: diff --git a/src/diffpy/srreal/CroppedGaussianProfile.hpp b/src/diffpy/srreal/CroppedGaussianProfile.hpp index ed1573ea..ac560ab1 100644 --- a/src/diffpy/srreal/CroppedGaussianProfile.hpp +++ b/src/diffpy/srreal/CroppedGaussianProfile.hpp @@ -23,10 +23,12 @@ #include +#include + namespace diffpy { namespace srreal { -class CroppedGaussianProfile : public GaussianProfile +class DLL_EXPORT CroppedGaussianProfile : public GaussianProfile { public: diff --git a/src/diffpy/srreal/CrystalStructureAdapter.hpp b/src/diffpy/srreal/CrystalStructureAdapter.hpp index 56645810..ef74e33d 100644 --- a/src/diffpy/srreal/CrystalStructureAdapter.hpp +++ b/src/diffpy/srreal/CrystalStructureAdapter.hpp @@ -25,6 +25,8 @@ #include +#include + namespace diffpy { namespace srreal { @@ -63,7 +65,7 @@ bool operator!=(const SymOpRotTrans& op0, const SymOpRotTrans& op1) } -class CrystalStructureAdapter : public PeriodicStructureAdapter +class DLL_EXPORT CrystalStructureAdapter : public PeriodicStructureAdapter { friend class CrystalStructureBondGenerator; @@ -111,7 +113,7 @@ class CrystalStructureAdapter : public PeriodicStructureAdapter bool isSymmetryCached() const; // comparison - friend bool operator==( + friend DLL_EXPORT bool operator==( const CrystalStructureAdapter&, const CrystalStructureAdapter&); // serialization @@ -133,11 +135,11 @@ typedef boost::shared_ptr CrystalStructureAdapterPtr; // Comparison functions -bool operator==(const CrystalStructureAdapter&, const CrystalStructureAdapter&); -bool operator!=(const CrystalStructureAdapter&, const CrystalStructureAdapter&); +DLL_EXPORT bool operator==(const CrystalStructureAdapter&, const CrystalStructureAdapter&); +DLL_EXPORT bool operator!=(const CrystalStructureAdapter&, const CrystalStructureAdapter&); -class CrystalStructureBondGenerator : public PeriodicStructureBondGenerator +class DLL_EXPORT CrystalStructureBondGenerator : public PeriodicStructureBondGenerator { public: diff --git a/src/diffpy/srreal/DebyePDFCalculator.hpp b/src/diffpy/srreal/DebyePDFCalculator.hpp index 512e2884..5fb21bf8 100644 --- a/src/diffpy/srreal/DebyePDFCalculator.hpp +++ b/src/diffpy/srreal/DebyePDFCalculator.hpp @@ -52,10 +52,12 @@ #include #include +#include + namespace diffpy { namespace srreal { -class DebyePDFCalculator : +class DLL_EXPORT DebyePDFCalculator : public BaseDebyeSum, public ScatteringFactorTableOwner, public PDFEnvelopeOwner diff --git a/src/diffpy/srreal/DebyeWallerPeakWidth.hpp b/src/diffpy/srreal/DebyeWallerPeakWidth.hpp index c900f7f1..25cb9f4b 100644 --- a/src/diffpy/srreal/DebyeWallerPeakWidth.hpp +++ b/src/diffpy/srreal/DebyeWallerPeakWidth.hpp @@ -22,11 +22,13 @@ #include +#include + namespace diffpy { namespace srreal { -class DebyeWallerPeakWidth : public PeakWidthModel +class DLL_EXPORT DebyeWallerPeakWidth : public PeakWidthModel { public: diff --git a/src/diffpy/srreal/GaussianProfile.hpp b/src/diffpy/srreal/GaussianProfile.hpp index 21b65b65..d193897d 100644 --- a/src/diffpy/srreal/GaussianProfile.hpp +++ b/src/diffpy/srreal/GaussianProfile.hpp @@ -22,10 +22,12 @@ #include +#include + namespace diffpy { namespace srreal { -class GaussianProfile : public PeakProfile +class DLL_EXPORT GaussianProfile : public PeakProfile { public: diff --git a/src/diffpy/srreal/JeongPeakWidth.hpp b/src/diffpy/srreal/JeongPeakWidth.hpp index 1acd9a19..6518e5c6 100644 --- a/src/diffpy/srreal/JeongPeakWidth.hpp +++ b/src/diffpy/srreal/JeongPeakWidth.hpp @@ -23,11 +23,13 @@ #include +#include + namespace diffpy { namespace srreal { -class JeongPeakWidth : public DebyeWallerPeakWidth +class DLL_EXPORT JeongPeakWidth : public DebyeWallerPeakWidth { public: diff --git a/src/diffpy/srreal/LinearBaseline.hpp b/src/diffpy/srreal/LinearBaseline.hpp index ab86aa7a..d1616f6c 100644 --- a/src/diffpy/srreal/LinearBaseline.hpp +++ b/src/diffpy/srreal/LinearBaseline.hpp @@ -21,13 +21,15 @@ #include +#include + namespace diffpy { namespace srreal { /// @class LinearBaseline /// @brief linear PDF baseline -class LinearBaseline : public PDFBaseline +class DLL_EXPORT LinearBaseline : public PDFBaseline { public: diff --git a/src/diffpy/srreal/NoMetaStructureAdapter.hpp b/src/diffpy/srreal/NoMetaStructureAdapter.hpp index da454fcc..2c8bd5f1 100644 --- a/src/diffpy/srreal/NoMetaStructureAdapter.hpp +++ b/src/diffpy/srreal/NoMetaStructureAdapter.hpp @@ -27,10 +27,12 @@ #include +#include + namespace diffpy { namespace srreal { -class NoMetaStructureAdapter : public StructureAdapter +class DLL_EXPORT NoMetaStructureAdapter : public StructureAdapter { public: @@ -75,7 +77,7 @@ class NoMetaStructureAdapter : public StructureAdapter // Routines ------------------------------------------------------------------ /// create NoMetaStructureAdapter from an existing StructureAdapter -StructureAdapterPtr nometa(StructureAdapterPtr stru); +DLL_EXPORT StructureAdapterPtr nometa(StructureAdapterPtr stru); /// create NoMetaStructureAdapter from an adaptable structure object diff --git a/src/diffpy/srreal/NoSymmetryStructureAdapter.hpp b/src/diffpy/srreal/NoSymmetryStructureAdapter.hpp index 5d8d87c4..cff5279b 100644 --- a/src/diffpy/srreal/NoSymmetryStructureAdapter.hpp +++ b/src/diffpy/srreal/NoSymmetryStructureAdapter.hpp @@ -29,10 +29,12 @@ #include +#include + namespace diffpy { namespace srreal { -class NoSymmetryStructureAdapter : public StructureAdapter +class DLL_EXPORT NoSymmetryStructureAdapter : public StructureAdapter { public: @@ -77,7 +79,7 @@ class NoSymmetryStructureAdapter : public StructureAdapter // Routines ------------------------------------------------------------------ /// create NoSymmetryStructureAdapter from an existing StructureAdapter -StructureAdapterPtr nosymmetry(StructureAdapterPtr stru); +DLL_EXPORT StructureAdapterPtr nosymmetry(StructureAdapterPtr stru); /// create NoSymmetryStructureAdapter from an adaptable structure object diff --git a/src/diffpy/srreal/ObjCrystStructureAdapter.hpp b/src/diffpy/srreal/ObjCrystStructureAdapter.hpp index 0cb21cc7..264578cc 100644 --- a/src/diffpy/srreal/ObjCrystStructureAdapter.hpp +++ b/src/diffpy/srreal/ObjCrystStructureAdapter.hpp @@ -24,6 +24,9 @@ #include #include #include + +#include + #include namespace diffpy { @@ -31,7 +34,7 @@ namespace srreal { // ObjCryst::Crystal is now adapted with CrystalStructureAdapter -StructureAdapterPtr +DLL_EXPORT StructureAdapterPtr createStructureAdapter(const ObjCryst::Crystal& cryst); // ObjCryst::Molecule can be adapted with AtomicStructureAdapter @@ -41,7 +44,7 @@ createStructureAdapter(const ObjCryst::Crystal& cryst); // molecule as a scattering component within an ObjCryst::Crystal. -StructureAdapterPtr +DLL_EXPORT StructureAdapterPtr createStructureAdapter(const ObjCryst::Molecule& molecule); } // namespace srreal diff --git a/src/diffpy/srreal/OverlapCalculator.hpp b/src/diffpy/srreal/OverlapCalculator.hpp index 0a6b5763..1f861a48 100644 --- a/src/diffpy/srreal/OverlapCalculator.hpp +++ b/src/diffpy/srreal/OverlapCalculator.hpp @@ -24,10 +24,12 @@ #include #include +#include + namespace diffpy { namespace srreal { -class OverlapCalculator : public PairQuantity +class DLL_EXPORT OverlapCalculator : public PairQuantity { public: diff --git a/src/diffpy/srreal/PDFBaseline.cpp b/src/diffpy/srreal/PDFBaseline.cpp index 96525875..aa20ccba 100644 --- a/src/diffpy/srreal/PDFBaseline.cpp +++ b/src/diffpy/srreal/PDFBaseline.cpp @@ -23,7 +23,7 @@ namespace diffpy { // Unique instantiation of the template registry base class. -template class HasClassRegistry; +template class DLL_EXPORT HasClassRegistry; } // namespace diffpy diff --git a/src/diffpy/srreal/PDFBaseline.hpp b/src/diffpy/srreal/PDFBaseline.hpp index 5329828f..8d2f1385 100644 --- a/src/diffpy/srreal/PDFBaseline.hpp +++ b/src/diffpy/srreal/PDFBaseline.hpp @@ -30,13 +30,15 @@ #include #include +#include + namespace diffpy { namespace srreal { /// @class PDFBaseline /// @brief abstract base class for PDF baseline function -class PDFBaseline : +class DLL_EXPORT PDFBaseline : public Attributes, public HasClassRegistry { diff --git a/src/diffpy/srreal/PDFCalculator.hpp b/src/diffpy/srreal/PDFCalculator.hpp index 79220e0a..f93795e3 100644 --- a/src/diffpy/srreal/PDFCalculator.hpp +++ b/src/diffpy/srreal/PDFCalculator.hpp @@ -26,10 +26,12 @@ #include #include +#include + namespace diffpy { namespace srreal { -class PDFCalculator : +class DLL_EXPORT PDFCalculator : public PairQuantity, public PeakWidthModelOwner, public ScatteringFactorTableOwner, diff --git a/src/diffpy/srreal/PDFEnvelope.cpp b/src/diffpy/srreal/PDFEnvelope.cpp index 04023176..3d6811d3 100644 --- a/src/diffpy/srreal/PDFEnvelope.cpp +++ b/src/diffpy/srreal/PDFEnvelope.cpp @@ -31,7 +31,7 @@ using diffpy::validators::ensureNonNull; namespace diffpy { // Unique instantiation of the template registry base class. -template class HasClassRegistry; +template class DLL_EXPORT HasClassRegistry; namespace srreal { diff --git a/src/diffpy/srreal/PDFEnvelope.hpp b/src/diffpy/srreal/PDFEnvelope.hpp index f7035737..7cb169ee 100644 --- a/src/diffpy/srreal/PDFEnvelope.hpp +++ b/src/diffpy/srreal/PDFEnvelope.hpp @@ -35,6 +35,8 @@ #include #include +#include + namespace diffpy { namespace srreal { @@ -42,7 +44,7 @@ namespace srreal { /// @class PDFEnvelope /// @brief abstract base class for PDF envelope scaling function -class PDFEnvelope : +class DLL_EXPORT PDFEnvelope : public diffpy::Attributes, public diffpy::HasClassRegistry { @@ -69,7 +71,7 @@ typedef PDFEnvelope::SharedPtr PDFEnvelopePtr; /// @brief storage of one or more PDFEnvelope functions and their /// application to unscaled x, y arrays -class PDFEnvelopeOwner +class DLL_EXPORT PDFEnvelopeOwner { public: diff --git a/src/diffpy/srreal/PDFUtils.hpp b/src/diffpy/srreal/PDFUtils.hpp index 97e2764a..b28686ba 100644 --- a/src/diffpy/srreal/PDFUtils.hpp +++ b/src/diffpy/srreal/PDFUtils.hpp @@ -28,6 +28,8 @@ #include #include +#include + namespace diffpy { namespace srreal { @@ -43,10 +45,10 @@ const double DEFAULT_QGRID_QMAX = 10.0; const double DEFAULT_QGRID_QSTEP = 0.05; /// fast Fourier transformation converting G(r) to F(Q) -QuantityType fftgtof(const QuantityType& g, double rstep, double rmin=0.0); +DLL_EXPORT QuantityType fftgtof(const QuantityType& g, double rstep, double rmin=0.0); /// fast Fourier transformation converting F(Q) to G(r) -QuantityType fftftog(const QuantityType& f, double qstep, double qmin=0.0); +DLL_EXPORT QuantityType fftftog(const QuantityType& f, double qstep, double qmin=0.0); /// shared methods for PDFCalculator and DebyePDFCalculator template QuantityType pdfutils_getQgrid(const T* pdfc); diff --git a/src/diffpy/srreal/PQEvaluator.hpp b/src/diffpy/srreal/PQEvaluator.hpp index 8fa5f228..2efd5aad 100644 --- a/src/diffpy/srreal/PQEvaluator.hpp +++ b/src/diffpy/srreal/PQEvaluator.hpp @@ -33,6 +33,8 @@ #include #include +#include + namespace diffpy { namespace srreal { @@ -51,11 +53,11 @@ enum PQEvaluatorFlag { FIXEDSITEINDEX = 2, }; -class PQEvaluatorBasic +class DLL_EXPORT PQEvaluatorBasic { public: - friend + friend DLL_EXPORT PQEvaluatorPtr createPQEvaluator(PQEvaluatorType, PQEvaluatorPtr); // constructor PQEvaluatorBasic(); @@ -98,7 +100,7 @@ class PQEvaluatorBasic }; -class PQEvaluatorOptimized : public PQEvaluatorBasic +class DLL_EXPORT PQEvaluatorOptimized : public PQEvaluatorBasic { public: @@ -127,7 +129,7 @@ class PQEvaluatorOptimized : public PQEvaluatorBasic }; -class PQEvaluatorCheck : public PQEvaluatorOptimized +class DLL_EXPORT PQEvaluatorCheck : public PQEvaluatorOptimized { public: @@ -149,7 +151,7 @@ class PQEvaluatorCheck : public PQEvaluatorOptimized // Factory function for PairQuantity evaluators ------------------------------ -PQEvaluatorPtr createPQEvaluator( +DLL_EXPORT PQEvaluatorPtr createPQEvaluator( PQEvaluatorType pqtp, PQEvaluatorPtr pqevsrc=PQEvaluatorPtr()); } // namespace srreal diff --git a/src/diffpy/srreal/PairQuantity.hpp b/src/diffpy/srreal/PairQuantity.hpp index b80086ec..04d8c841 100644 --- a/src/diffpy/srreal/PairQuantity.hpp +++ b/src/diffpy/srreal/PairQuantity.hpp @@ -32,12 +32,14 @@ #include #include +#include + namespace diffpy { namespace srreal { class BaseBondGenerator; -class PairQuantity : public diffpy::Attributes +class DLL_EXPORT PairQuantity : public diffpy::Attributes { public: @@ -84,7 +86,7 @@ class PairQuantity : public diffpy::Attributes friend class PQEvaluatorBasic; friend class PQEvaluatorOptimized; - friend StructureAdapterPtr + friend DLL_EXPORT StructureAdapterPtr replacePairQuantityStructure(PairQuantity&, StructureAdapterPtr); // methods @@ -171,7 +173,7 @@ void PairQuantity::setStructure(const T& stru) /// The purpose of this function is to support Python pickling of /// PairQuantity objects that hold Python-derived StructureAdapter classes. /// Use it only if you absolutely have to and you know what you do. -StructureAdapterPtr +DLL_EXPORT StructureAdapterPtr replacePairQuantityStructure(PairQuantity& pq, StructureAdapterPtr stru); } // namespace srreal diff --git a/src/diffpy/srreal/PeakProfile.cpp b/src/diffpy/srreal/PeakProfile.cpp index ced61f2b..e3211127 100644 --- a/src/diffpy/srreal/PeakProfile.cpp +++ b/src/diffpy/srreal/PeakProfile.cpp @@ -23,7 +23,7 @@ namespace diffpy { // Unique instantiation of the template registry base class. -template class HasClassRegistry; +template class DLL_EXPORT HasClassRegistry; namespace srreal { diff --git a/src/diffpy/srreal/PeakProfile.hpp b/src/diffpy/srreal/PeakProfile.hpp index 8f6d4e14..c5342012 100644 --- a/src/diffpy/srreal/PeakProfile.hpp +++ b/src/diffpy/srreal/PeakProfile.hpp @@ -33,10 +33,12 @@ #include #include +#include + namespace diffpy { namespace srreal { -class PeakProfile : +class DLL_EXPORT PeakProfile : public diffpy::Attributes, public diffpy::HasClassRegistry { diff --git a/src/diffpy/srreal/PeakWidthModel.cpp b/src/diffpy/srreal/PeakWidthModel.cpp index 0418d9e1..a9bb766c 100644 --- a/src/diffpy/srreal/PeakWidthModel.cpp +++ b/src/diffpy/srreal/PeakWidthModel.cpp @@ -27,7 +27,7 @@ using diffpy::validators::ensureNonNull; namespace diffpy { // Unique instantiation of the template registry base class. -template class HasClassRegistry; +template class DLL_EXPORT HasClassRegistry; namespace srreal { diff --git a/src/diffpy/srreal/PeakWidthModel.hpp b/src/diffpy/srreal/PeakWidthModel.hpp index 25a589bf..3e917d12 100644 --- a/src/diffpy/srreal/PeakWidthModel.hpp +++ b/src/diffpy/srreal/PeakWidthModel.hpp @@ -35,10 +35,12 @@ #include #include +#include + namespace diffpy { namespace srreal { -class PeakWidthModel : +class DLL_EXPORT PeakWidthModel : public diffpy::Attributes, public diffpy::HasClassRegistry { @@ -71,7 +73,7 @@ class PeakWidthModel : typedef PeakWidthModel::SharedPtr PeakWidthModelPtr; -class PeakWidthModelOwner +class DLL_EXPORT PeakWidthModelOwner { public: diff --git a/src/diffpy/srreal/PeriodicStructureAdapter.hpp b/src/diffpy/srreal/PeriodicStructureAdapter.hpp index d3082e0e..365f632b 100644 --- a/src/diffpy/srreal/PeriodicStructureAdapter.hpp +++ b/src/diffpy/srreal/PeriodicStructureAdapter.hpp @@ -23,14 +23,17 @@ #define PERIODICSTRUCTUREADAPTER_HPP_INCLUDED #include +#include #include +#include + namespace diffpy { namespace srreal { class PointsInSphere; -class PeriodicStructureAdapter : public AtomicStructureAdapter +class DLL_EXPORT PeriodicStructureAdapter : public AtomicStructureAdapter { public: @@ -68,8 +71,8 @@ typedef boost::shared_ptr PeriodicStructureAdapterPtr; // Comparison functions -bool operator==(const PeriodicStructureAdapter&, const PeriodicStructureAdapter&); -bool operator!=(const PeriodicStructureAdapter&, const PeriodicStructureAdapter&); +DLL_EXPORT bool operator==(const PeriodicStructureAdapter&, const PeriodicStructureAdapter&); +DLL_EXPORT bool operator!=(const PeriodicStructureAdapter&, const PeriodicStructureAdapter&); class PeriodicStructureBondGenerator : public BaseBondGenerator diff --git a/src/diffpy/srreal/QResolutionEnvelope.hpp b/src/diffpy/srreal/QResolutionEnvelope.hpp index dccb3b37..f7d33ae6 100644 --- a/src/diffpy/srreal/QResolutionEnvelope.hpp +++ b/src/diffpy/srreal/QResolutionEnvelope.hpp @@ -21,13 +21,15 @@ #include +#include + namespace diffpy { namespace srreal { /// @class QResolutionEnvelope /// @brief wide Gaussian PDF scaling envelope caused by finite Q resolution -class QResolutionEnvelope : public PDFEnvelope +class DLL_EXPORT QResolutionEnvelope : public PDFEnvelope { public: diff --git a/src/diffpy/srreal/QuantityType.hpp b/src/diffpy/srreal/QuantityType.hpp index 11a1f678..c625ec5e 100644 --- a/src/diffpy/srreal/QuantityType.hpp +++ b/src/diffpy/srreal/QuantityType.hpp @@ -26,10 +26,12 @@ #include #include +#include + namespace diffpy { namespace srreal { -class QuantityType : public std::vector +class DLL_EXPORT QuantityType : public std::vector { private: diff --git a/src/diffpy/srreal/R3linalg.cpp b/src/diffpy/srreal/R3linalg.cpp index 19a70cc7..15a32814 100644 --- a/src/diffpy/srreal/R3linalg.cpp +++ b/src/diffpy/srreal/R3linalg.cpp @@ -25,6 +25,10 @@ namespace diffpy { namespace srreal { namespace R3 { +// Constants +DLL_EXPORT const int Ndim = 3; + +// Functions const Matrix& identity() { static Matrix mx = ublas::identity_matrix(Ndim); diff --git a/src/diffpy/srreal/R3linalg.hpp b/src/diffpy/srreal/R3linalg.hpp index bffd5c7c..69916aad 100644 --- a/src/diffpy/srreal/R3linalg.hpp +++ b/src/diffpy/srreal/R3linalg.hpp @@ -26,6 +26,8 @@ #include #include +#include + namespace diffpy { namespace srreal { namespace R3 { @@ -40,13 +42,13 @@ using ublas::trans; // Constants -const int Ndim = 3; +extern DLL_EXPORT const int Ndim; using ::diffpy::mathutils::SQRT_DOUBLE_EPS; const ublas::zero_vector zerovector(Ndim); // Classes -class Vector : public ublas::vector > +class DLL_EXPORT Vector : public ublas::vector > { typedef ublas::vector > BaseVector; @@ -94,7 +96,7 @@ class Vector : public ublas::vector > }; -class Matrix : public ublas::matrix > { typedef ublas::matrix #include +#include + namespace diffpy { namespace srreal { -class SFTElectron : public ScatteringFactorTable +class DLL_EXPORT SFTElectron : public ScatteringFactorTable { public: diff --git a/src/diffpy/srreal/SFTElectronNumber.hpp b/src/diffpy/srreal/SFTElectronNumber.hpp index 3736861b..d3da0981 100644 --- a/src/diffpy/srreal/SFTElectronNumber.hpp +++ b/src/diffpy/srreal/SFTElectronNumber.hpp @@ -24,10 +24,12 @@ #include +#include + namespace diffpy { namespace srreal { -class SFTElectronNumber : public ScatteringFactorTable +class DLL_EXPORT SFTElectronNumber : public ScatteringFactorTable { public: diff --git a/src/diffpy/srreal/SFTNeutron.hpp b/src/diffpy/srreal/SFTNeutron.hpp index d2264dc6..a1a17427 100644 --- a/src/diffpy/srreal/SFTNeutron.hpp +++ b/src/diffpy/srreal/SFTNeutron.hpp @@ -24,10 +24,12 @@ #include +#include + namespace diffpy { namespace srreal { -class SFTNeutron : public ScatteringFactorTable +class DLL_EXPORT SFTNeutron : public ScatteringFactorTable { public: diff --git a/src/diffpy/srreal/SFTXray.hpp b/src/diffpy/srreal/SFTXray.hpp index 833ff611..8a9fc362 100644 --- a/src/diffpy/srreal/SFTXray.hpp +++ b/src/diffpy/srreal/SFTXray.hpp @@ -23,10 +23,12 @@ #include +#include + namespace diffpy { namespace srreal { -class SFTXray : public ScatteringFactorTable +class DLL_EXPORT SFTXray : public ScatteringFactorTable { public: diff --git a/src/diffpy/srreal/ScaleEnvelope.hpp b/src/diffpy/srreal/ScaleEnvelope.hpp index a01bb462..97b927e0 100644 --- a/src/diffpy/srreal/ScaleEnvelope.hpp +++ b/src/diffpy/srreal/ScaleEnvelope.hpp @@ -21,13 +21,15 @@ #include +#include + namespace diffpy { namespace srreal { /// @class ScaleEnvelope /// @brief constant PDF scaling factor -class ScaleEnvelope : public PDFEnvelope +class DLL_EXPORT ScaleEnvelope : public PDFEnvelope { public: diff --git a/src/diffpy/srreal/ScatteringFactorTable.cpp b/src/diffpy/srreal/ScatteringFactorTable.cpp index b5c4872a..07727d62 100644 --- a/src/diffpy/srreal/ScatteringFactorTable.cpp +++ b/src/diffpy/srreal/ScatteringFactorTable.cpp @@ -27,7 +27,7 @@ using diffpy::validators::ensureNonNull; namespace diffpy { // Unique instantiation of the template registry base class. -template class HasClassRegistry; +template class DLL_EXPORT HasClassRegistry; namespace srreal { diff --git a/src/diffpy/srreal/ScatteringFactorTable.hpp b/src/diffpy/srreal/ScatteringFactorTable.hpp index e876748d..1f798438 100644 --- a/src/diffpy/srreal/ScatteringFactorTable.hpp +++ b/src/diffpy/srreal/ScatteringFactorTable.hpp @@ -33,10 +33,12 @@ #include #include +#include + namespace diffpy { namespace srreal { -class ScatteringFactorTable : +class DLL_EXPORT ScatteringFactorTable : public diffpy::HasClassRegistry { public: @@ -79,7 +81,7 @@ class ScatteringFactorTable : typedef ScatteringFactorTable::SharedPtr ScatteringFactorTablePtr; -class ScatteringFactorTableOwner +class DLL_EXPORT ScatteringFactorTableOwner { public: diff --git a/src/diffpy/srreal/SphericalShapeEnvelope.hpp b/src/diffpy/srreal/SphericalShapeEnvelope.hpp index 67ba9740..e6e95bb5 100644 --- a/src/diffpy/srreal/SphericalShapeEnvelope.hpp +++ b/src/diffpy/srreal/SphericalShapeEnvelope.hpp @@ -21,6 +21,8 @@ #include +#include + namespace diffpy { namespace srreal { @@ -29,7 +31,7 @@ namespace srreal { /// Spherical shape envelope is not applied for spherical particle /// diameter, spdiameter, smaller or equal to zero. -class SphericalShapeEnvelope : public PDFEnvelope +class DLL_EXPORT SphericalShapeEnvelope : public PDFEnvelope { public: diff --git a/src/diffpy/srreal/StepCutEnvelope.hpp b/src/diffpy/srreal/StepCutEnvelope.hpp index 597b5a64..4460d874 100644 --- a/src/diffpy/srreal/StepCutEnvelope.hpp +++ b/src/diffpy/srreal/StepCutEnvelope.hpp @@ -21,6 +21,8 @@ #include +#include + namespace diffpy { namespace srreal { @@ -28,7 +30,7 @@ namespace srreal { /// @brief empirical step-function PDF envelope. The envelope is not /// applied when the cutoff radius stepcut is smaller or equal zero. -class StepCutEnvelope : public PDFEnvelope +class DLL_EXPORT StepCutEnvelope : public PDFEnvelope { public: diff --git a/src/diffpy/srreal/StructureAdapter.hpp b/src/diffpy/srreal/StructureAdapter.hpp index 1ff4218d..2b707129 100644 --- a/src/diffpy/srreal/StructureAdapter.hpp +++ b/src/diffpy/srreal/StructureAdapter.hpp @@ -32,6 +32,14 @@ #include #include +#include + +#ifdef _MSC_VER + #define DEPRECATED __declspec(deprecated) +#else + #define DEPRECATED __attribute__((deprecated)) +#endif + namespace diffpy { namespace srreal { @@ -43,7 +51,7 @@ class StructureDifference; /// @brief abstract adaptor to structure data needed by /// PairQuantity calculator -class StructureAdapter : +class DLL_EXPORT StructureAdapter : public boost::enable_shared_from_this { public: @@ -89,9 +97,7 @@ class StructureAdapter : /// this method allows custom special configuration for a concrete /// pair of StructureAdapter and PairQuantity objects. - virtual void customPQConfig(PairQuantity* pq) const - __attribute__ ((deprecated)) - { } + virtual DEPRECATED void customPQConfig(PairQuantity* pq) const { } /// Return difference from the other StructureAdapter virtual StructureDifference diff(StructureAdapterConstPtr) const; @@ -108,7 +114,7 @@ class StructureAdapter : // Routines ------------------------------------------------------------------ /// Return a singleton instance of an empty StructureAdapter -StructureAdapterPtr emptyStructureAdapter(); +DLL_EXPORT StructureAdapterPtr emptyStructureAdapter(); /// Calculate MSD along specified direction in Cartesian space. double meanSquareDisplacement(const R3::Matrix& Uijcartn, const R3::Vector& s, diff --git a/src/diffpy/srreal/StructureDifference.hpp b/src/diffpy/srreal/StructureDifference.hpp index 4be27e58..47bddd5b 100644 --- a/src/diffpy/srreal/StructureDifference.hpp +++ b/src/diffpy/srreal/StructureDifference.hpp @@ -24,10 +24,12 @@ #include +#include + namespace diffpy { namespace srreal { -class StructureDifference { +class DLL_EXPORT StructureDifference { public: diff --git a/src/diffpy/srreal/ZeroBaseline.hpp b/src/diffpy/srreal/ZeroBaseline.hpp index 06ba3978..a863b3e0 100644 --- a/src/diffpy/srreal/ZeroBaseline.hpp +++ b/src/diffpy/srreal/ZeroBaseline.hpp @@ -21,13 +21,15 @@ #include +#include + namespace diffpy { namespace srreal { /// @class ZeroBaseline /// @brief trivial zero baseline -class ZeroBaseline : public PDFBaseline +class DLL_EXPORT ZeroBaseline : public PDFBaseline { public: diff --git a/src/diffpy/version.tpl b/src/diffpy/version.tpl index 548fb3ab..6edf3327 100644 --- a/src/diffpy/version.tpl +++ b/src/diffpy/version.tpl @@ -64,7 +64,9 @@ // libdiffpy_version_info will hold runtime version data, which may be // different from client compile-time values. -struct libdiffpy_version_info { +#include + +struct DLL_EXPORT libdiffpy_version_info { static const long long version; static const char* version_str; From 601959fc179ce357215db5b5442c7194d0b379ac Mon Sep 17 00:00:00 2001 From: Tieqiong Zhang Date: Sat, 1 Feb 2025 18:22:10 -0500 Subject: [PATCH 03/56] fix build scripts for Windows --- SConstruct | 65 ++++++++++++---- src/SConscript | 161 +++++++++++++++++++++++++++------------ src/SConscript.configure | 62 ++++++--------- src/diffpy/SConscript | 5 +- 4 files changed, 188 insertions(+), 105 deletions(-) diff --git a/SConstruct b/SConstruct index 5179bba5..1bca2b36 100644 --- a/SConstruct +++ b/SConstruct @@ -21,7 +21,8 @@ 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) @@ -37,32 +38,63 @@ DefaultEnvironment(ENV=subdictionary(os.environ, ''' # Create construction environment env = DefaultEnvironment().Clone() -# Variables definitions below work only with 0.98.1 or later. +# Variables definitions below work only with 0.98.1 or later env.EnsureSConsVersion(0, 98, 1) # Customizable compile variables vars = Variables('sconsvars.py') -vars.Add(PathVariable( - 'prefix', - 'installation prefix directory', - '/usr/local')) -vars.Update(env) +# 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') + + env['ENV']['TMP'] = os.environ['TMP'] +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 library [prefix/lib]', - env['prefix'] + '/lib', + 'installation directory for compiled programs', + lib_path, PathVariable.PathAccept)) vars.Add(PathVariable( 'includedir', - 'installation directory for C++ header files [prefix/include]', - env['prefix'] + '/include', + 'installation directory for C++ header files', + include_path, PathVariable.PathAccept)) vars.Add(PathVariable( 'datadir', - 'installation directory for architecture independent data [prefix/share]', - env['prefix'] + '/share', + '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', @@ -70,7 +102,7 @@ vars.Add(EnumVariable( vars.Add(EnumVariable( 'tool', 'C++ compiler toolkit to be used', - 'default', allowed_values=('default', 'intelc'))) + 'default', allowed_values=('default', 'clang', 'gcc', 'intelc'))) vars.Add(BoolVariable( 'enable_objcryst', 'enable objcryst support, when installed', None)) @@ -83,11 +115,14 @@ vars.Add( 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()] + +btags = [env['build'], env['PLATFORM']] if env['profile']: btags.append('profile') builddir = env.Dir('build/' + '-'.join(btags)) diff --git a/src/SConscript b/src/SConscript index fce570ec..6bc5cc38 100644 --- a/src/SConscript +++ b/src/SConscript @@ -1,66 +1,104 @@ import os +from SCons.Script import * +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(':')) - -# Use Intel C++ compiler if requested by the user. -icpc = None +# Use C++ compiler specified by the 'tool' option. +# Intel C++ compiler 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')]) + env=env.Clone() -fast_linkflags = ['-s'] +# Clang++ compiler +if env['tool'] == 'clang': + llvm = env.WhereIs('clang++') + if not llvm: + print("Cannot find the clang++ compiler.") + Exit(1) + env=env.Clone(tools=['clangxx']) -# 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++11', delete_existing=1) +# g++ compiler +if env['tool'] == 'gcc': + gcc = env.WhereIs('g++') + if not gcc: + print("Cannot find the g++ compiler.") + Exit(1) + env=env.Clone(tools=['mingw']) + +# Default use scons auto found compiler # Platform specific intricacies. +fast_linkflags = ['-s'] + +# macOS specific options if env['PLATFORM'] == 'darwin': env.AppendUnique(CXXFLAGS='-ftemplate-depth-256') - env.Append(SHLINKFLAGS=['-install_name', '$TARGET.abspath']) + env.AppendUnique(SHLINKFLAGS=['-install_name', '$TARGET.abspath']) env.AppendUnique(SHLINKFLAGS='-headerpad_max_install_names') fast_linkflags[:] = [] # Compiler specific options -if icpc: - # 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') +if env['PLATFORM'] == "win32": + if env['tool'] == 'clang': + # clang++ on Windows + env['CC'] = 'clang' + env['CCFLAGS'] = ['-O3', '-ffast-math', '-std=c++14', '-m64', '-DBUILD_DLL'] + env.PrependUnique(CPPDEFINES='_USE_MATH_DEFINES') + env['LINKFLAGS']= ['-fuse-ld=lld-link', '-m64', '-lstdc++'] + + elif env['tool'] == 'gcc': + # MinGW + env.PrependUnique(CCFLAGS=['-O3', '-std=c++14', '-m64', '-DBUILD_DLL']) + env.PrependUnique(CPPDEFINES='_USE_MATH_DEFINES') + env.AppendUnique(LINKFLAGS='-m64') + + else: + # Visual c++ + env.PrependUnique(CCFLAGS=['/Ox', '/EHsc', '/MD', '/std:c++14', '/Wall', '/DBUILD_DLL']) + env.AppendUnique(CPPDEFINES=[{'NDEBUG': None}, '_USE_MATH_DEFINES']) + env.AppendUnique(LINKFLAGS=['/OPT:NOREF', '/OPT:NOICF']) + +else: + env.PrependUnique(CXXFLAGS='-std=c++11') + if icpc: + # 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') @@ -117,11 +155,15 @@ if 'sdist' in COMMAND_LINE_TARGETS: # use new environment with extra libraries needed for libdiffpy. env_lib = env.Clone() # Setup GSL, the GNU Scientific library. -env_lib.ParseConfig("gsl-config --cflags --libs") -# The dladdr call in runtimepath.cpp requires the dl library. -env_lib.AppendUnique(LIBS=['dl']) +# The dladdr call in runtimepath.cpp requires the dl library for Windows. +if env['has_objcryst'] == True: + env_lib.AppendUnique(LIBS=['dl', 'gsl', 'gslcblas','boost_serialization', 'libobjcryst']) +else: + env_lib.AppendUnique(LIBS=['dl', 'gsl', 'gslcblas','boost_serialization']) + 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??')) @@ -141,17 +183,36 @@ if targets_that_test.intersection(COMMAND_LINE_TARGETS): prefix = env['prefix'] # install-lib -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'] 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') + 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 diff --git a/src/SConscript.configure b/src/SConscript.configure index 3bc0568c..e834089f 100644 --- a/src/SConscript.configure +++ b/src/SConscript.configure @@ -19,61 +19,45 @@ def CheckBoostVersion(context, version): context.Result(rv) return rv -# Helper functions ----------------------------------------------------------- - -boostlibtags = ['', '-mt'] -def configure_boost_library(libname): - '''Add a boost library to the configured environment allowing for any - of the boostlibtags name extensions. - - libname -- boost library name without any extension - - Note: CheckLib function automatically adds library to the environment. +def CheckObjCrystVersion(context, version): + '''Check if ObjCryst Library is at least of specified version ''' - # using global conf defined below - for t in boostlibtags: - libnamefull = libname + t - if conf.CheckLib(libnamefull, language='C++'): - boostlibtags[:] = [t] - return - # library not found here - print('This program requires %r library' % libname) - Exit(1) + # 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, }) -# Add Conda include and library paths -import os -conda_prefix = Environment(ENV=os.environ)['ENV'].get('CONDA_PREFIX') -if conda_prefix: - env.Append(CPPPATH=[os.path.join(conda_prefix, 'include')]) - env.Append(LIBPATH=[os.path.join(conda_prefix, 'lib')]) - # 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) -# boost_serialization -configure_boost_library('boost_serialization') - -# ObjCryst - assume a no-objcryst fallback configuration. -conf.env['has_objcryst'] = False -# Detect ObjCryst and exit with error if requested and not found. -# By default conf.env does not contain the 'enable_objcryst' key. +# Check for ObjCryst library if conf.env.get('enable_objcryst', True): - conf.env['has_objcryst'] = conf.CheckLibWithHeader( - 'ObjCryst', 'ObjCryst/ObjCryst/Crystal.h', - language='C++', autoadd=True) - objcryst_requested = conf.env.get('enable_objcryst', False) - if objcryst_requested and not conf.env['has_objcryst']: - print("Adjust compiler paths or build with 'enable_objcryst=False'.") + 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() diff --git a/src/diffpy/SConscript b/src/diffpy/SConscript index 8e3c07d8..f8b8ad6f 100644 --- a/src/diffpy/SConscript +++ b/src/diffpy/SConscript @@ -4,9 +4,12 @@ Import('env', 'GlobSources') env['lib_includes'] += GlobSources('*.[hi]pp') env['lib_sources'] += [f for f in GlobSources('*.cpp') - if str(f) != 'runtimepath.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)) rtso = env_rt.SharedObject('runtimepath', 'runtimepath.cpp') From 28c0622d7ded9e531c55592221563a4d0cf79021 Mon Sep 17 00:00:00 2001 From: Tieqiong Zhang Date: Sat, 1 Feb 2025 19:26:41 -0500 Subject: [PATCH 04/56] small fix --- src/SConscript | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/SConscript b/src/SConscript index 6bc5cc38..af19b4f3 100644 --- a/src/SConscript +++ b/src/SConscript @@ -67,7 +67,7 @@ if env['PLATFORM'] == "win32": else: env.PrependUnique(CXXFLAGS='-std=c++11') - if icpc: + if env['tool'] == 'intelc': # options for Intel C++ compiler on hpc dev-intel07 env.PrependUnique(CCFLAGS=['-w1', '-fp-model', 'precise']) env.PrependUnique(LIBS=['imf']) From 86ba6f78d51c6c4718f4a585ac95db798ad14c36 Mon Sep 17 00:00:00 2001 From: Tieqiong Zhang Date: Thu, 19 Jun 2025 15:21:23 -0500 Subject: [PATCH 05/56] add flags for objcryst --- src/SConscript | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/SConscript b/src/SConscript index af19b4f3..2d38bc6c 100644 --- a/src/SConscript +++ b/src/SConscript @@ -49,19 +49,19 @@ if env['PLATFORM'] == "win32": if env['tool'] == 'clang': # clang++ on Windows env['CC'] = 'clang' - env['CCFLAGS'] = ['-O3', '-ffast-math', '-std=c++14', '-m64', '-DBUILD_DLL'] + env['CCFLAGS'] = ['-O3', '-ffast-math', '-std=c++14', '-m64', '-DBUILD_DLL', '-DREAL=double'] env.PrependUnique(CPPDEFINES='_USE_MATH_DEFINES') env['LINKFLAGS']= ['-fuse-ld=lld-link', '-m64', '-lstdc++'] elif env['tool'] == 'gcc': # MinGW - env.PrependUnique(CCFLAGS=['-O3', '-std=c++14', '-m64', '-DBUILD_DLL']) + env.PrependUnique(CCFLAGS=['-O3', '-std=c++14', '-m64', '-DBUILD_DLL', '-DREAL=double']) env.PrependUnique(CPPDEFINES='_USE_MATH_DEFINES') env.AppendUnique(LINKFLAGS='-m64') else: # Visual c++ - env.PrependUnique(CCFLAGS=['/Ox', '/EHsc', '/MD', '/std:c++14', '/Wall', '/DBUILD_DLL']) + env.PrependUnique(CCFLAGS=['/Ox', '/EHsc', '/MD', '/std:c++14', '/Wall', '/DBUILD_DLL', '/DREAL=double']) env.AppendUnique(CPPDEFINES=[{'NDEBUG': None}, '_USE_MATH_DEFINES']) env.AppendUnique(LINKFLAGS=['/OPT:NOREF', '/OPT:NOICF']) @@ -74,7 +74,7 @@ else: fast_optimflags = ['-fast', '-no-ipo'] else: # g++ options - env.PrependUnique(CCFLAGS=['-Wall']) + env.PrependUnique(CCFLAGS=['-Wall', '-DREAL=double']) fast_optimflags = ['-ffast-math'] # Configure build variants From 251f9aa0b0e7aa290d027e28af8f713e5598e412 Mon Sep 17 00:00:00 2001 From: Tieqiong Zhang Date: Fri, 20 Jun 2025 12:47:25 -0500 Subject: [PATCH 06/56] correct prefix --- SConstruct | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/SConstruct b/SConstruct index 1bca2b36..7360504c 100644 --- a/SConstruct +++ b/SConstruct @@ -49,7 +49,7 @@ if 'PREFIX' in os.environ: vars.Add(PathVariable( 'prefix', 'installation prefix directory', - os.environ['prefix'])) + os.environ['PREFIX'])) vars.Update(env) elif 'CONDA_PREFIX' in os.environ: vars.Add(PathVariable( From b472d419f87a739639e5dd6242959abc94e699bb Mon Sep 17 00:00:00 2001 From: Tieqiong Zhang Date: Mon, 23 Jun 2025 18:00:16 -0500 Subject: [PATCH 07/56] auto release with sha after tag push --- .github/workflows/github-release.yml | 67 ++++++++++++++++++++++++++++ 1 file changed, 67 insertions(+) create mode 100644 .github/workflows/github-release.yml diff --git a/.github/workflows/github-release.yml b/.github/workflows/github-release.yml new file mode 100644 index 00000000..ca55540d --- /dev/null +++ b/.github/workflows/github-release.yml @@ -0,0 +1,67 @@ +name: "Tag-based Release with SHA256" + +on: + push: + tags: + - '*' + +jobs: + release: + runs-on: ubuntu-latest + steps: + + - name: Checkout libdiffpy + uses: actions/checkout@v3 + with: + fetch-depth: 0 + + - name: Check permission + id: perm + uses: actions/github-script@v6 + with: + result-encoding: string + script: | + const { data } = await github.rest.repos.getCollaboratorPermissionLevel({ + owner: context.repo.owner, + repo: context.repo.repo, + username: context.actor + }); + return data.permission + + - name: Fail if not maintainer/admin + if: ${{ steps.perm.outputs.result != 'maintain' && steps.perm.outputs.result != 'admin' }} + run: | + echo "${{ github.actor }} is not a maintainer or admin" + exit 1 + + - name: Download tarball + run: | + curl -L "https://codeload.github.com/${{ github.repository }}/tar.gz/${{ github.ref_name }}" \ + -o source.tar.gz + + - name: Compute SHA256 + id: sha + run: | + shasum=$(openssl sha256 source.tar.gz | awk '{print $2}') + echo "sha=$shasum" >> $GITHUB_OUTPUT + + - name: Get tag message + id: tagmsg + run: | + TAG=${GITHUB_REF##*/} + message=$(git tag -l --format='%(contents)' "$TAG") + echo "tag_message<> $GITHUB_OUTPUT + echo "$message" >> $GITHUB_OUTPUT + echo "EOF" >> $GITHUB_OUTPUT + + - name: Create GitHub Release + uses: softprops/action-gh-release@v1 + with: + tag_name: ${{ github.ref_name }} + name: Release ${{ github.ref_name }} + body: | + ${{ steps.tagmsg.outputs.tag_message }} + + SHA256 (tar.gz): ${{ steps.sha.outputs.sha }} + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} From ba376c74884c9e57eb6fb86f17f2e482389ac547 Mon Sep 17 00:00:00 2001 From: Tieqiong Zhang Date: Wed, 25 Jun 2025 17:17:33 -0500 Subject: [PATCH 08/56] add precommit hooks and configuration files remove travis ci and local conda recipe --- .clang-format | 10 + .codespell/ignore_words.txt | 2 + .flake8 | 14 + .gitignore | 113 +- .isort.cfg | 5 + .pre-commit-config.yaml | 84 ++ .travis-sconscript.local | 34 - .travis.yml | 153 --- conda-recipe/build.sh | 12 - conda-recipe/conda_build_config.yaml | 11 - conda-recipe/meta.yaml | 51 - conda-recipe/run_test.sh | 16 - conda-recipe/sconscript.local | 18 - conda-recipe/setup.py | 22 - doc/{manual => }/Makefile | 0 doc/{manual => }/source/conf.py | 161 +-- doc/{manual => }/source/index.rst | 0 doc/{manual => }/source/license.rst | 0 doc/{manual => }/source/release.rst | 0 examples/testlib.cpp | 16 +- site_scons/fallback_version.py | 6 +- site_scons/libdiffpybuildutils.py | 68 +- site_scons/site_tools/cxxtest.py | 254 ++-- src/SConscript | 4 +- src/diffpy/Attributes.cpp | 256 ++-- src/diffpy/Attributes.hpp | 281 ++-- src/diffpy/EventTicker.cpp | 112 +- src/diffpy/EventTicker.hpp | 151 +-- src/diffpy/Export.hpp | 12 +- src/diffpy/HasClassRegistry.hpp | 130 +- src/diffpy/mathutils.hpp | 126 +- src/diffpy/runtimepath.cpp | 275 ++-- src/diffpy/runtimepath.hpp | 73 +- src/diffpy/serialization.hpp | 48 +- src/diffpy/srreal/AtomRadiiTable.cpp | 164 +-- src/diffpy/srreal/AtomRadiiTable.hpp | 117 +- src/diffpy/srreal/AtomUtils.cpp | 106 +- src/diffpy/srreal/AtomUtils.hpp | 36 +- src/diffpy/srreal/AtomicStructureAdapter.cpp | 411 +++--- src/diffpy/srreal/AtomicStructureAdapter.hpp | 268 ++-- src/diffpy/srreal/BVParam.cpp | 168 +-- src/diffpy/srreal/BVParam.hpp | 138 +- src/diffpy/srreal/BVParametersTable.cpp | 251 ++-- src/diffpy/srreal/BVParametersTable.hpp | 139 +- src/diffpy/srreal/BVSCalculator.cpp | 317 ++--- src/diffpy/srreal/BVSCalculator.hpp | 165 ++- src/diffpy/srreal/BaseBondGenerator.cpp | 320 ++--- src/diffpy/srreal/BaseBondGenerator.hpp | 171 ++- src/diffpy/srreal/BaseDebyeSum.cpp | 383 +++--- src/diffpy/srreal/BaseDebyeSum.hpp | 198 ++- src/diffpy/srreal/BondCalculator.cpp | 417 +++--- src/diffpy/srreal/BondCalculator.hpp | 212 ++- src/diffpy/srreal/ConstantPeakWidth.cpp | 135 +- src/diffpy/srreal/ConstantPeakWidth.hpp | 101 +- src/diffpy/srreal/ConstantRadiiTable.cpp | 77 +- src/diffpy/srreal/ConstantRadiiTable.hpp | 89 +- src/diffpy/srreal/CroppedGaussianProfile.cpp | 104 +- src/diffpy/srreal/CroppedGaussianProfile.hpp | 95 +- src/diffpy/srreal/CrystalStructureAdapter.cpp | 475 +++---- src/diffpy/srreal/CrystalStructureAdapter.hpp | 265 ++-- src/diffpy/srreal/DebyePDFCalculator.cpp | 510 ++++--- src/diffpy/srreal/DebyePDFCalculator.hpp | 220 ++- src/diffpy/srreal/DebyeWallerPeakWidth.cpp | 86 +- src/diffpy/srreal/DebyeWallerPeakWidth.hpp | 85 +- src/diffpy/srreal/EmptyStructureAdapter.cpp | 144 +- src/diffpy/srreal/GaussianProfile.cpp | 110 +- src/diffpy/srreal/GaussianProfile.hpp | 100 +- src/diffpy/srreal/JeongPeakWidth.cpp | 194 ++- src/diffpy/srreal/JeongPeakWidth.hpp | 136 +- src/diffpy/srreal/Lattice.cpp | 471 +++---- src/diffpy/srreal/Lattice.hpp | 727 ++++------ src/diffpy/srreal/LinearBaseline.cpp | 88 +- src/diffpy/srreal/LinearBaseline.hpp | 92 +- src/diffpy/srreal/NoMetaStructureAdapter.cpp | 186 ++- src/diffpy/srreal/NoMetaStructureAdapter.hpp | 131 +- .../srreal/NoSymmetryStructureAdapter.cpp | 186 ++- .../srreal/NoSymmetryStructureAdapter.hpp | 135 +- .../srreal/ObjCrystStructureAdapter.cpp | 303 ++--- .../srreal/ObjCrystStructureAdapter.hpp | 41 +- src/diffpy/srreal/OverlapCalculator.cpp | 873 ++++++------ src/diffpy/srreal/OverlapCalculator.hpp | 241 ++-- src/diffpy/srreal/PDFBaseline.cpp | 34 +- src/diffpy/srreal/PDFBaseline.hpp | 69 +- src/diffpy/srreal/PDFCalculator.cpp | 1067 +++++++-------- src/diffpy/srreal/PDFCalculator.hpp | 377 +++--- src/diffpy/srreal/PDFEnvelope.cpp | 164 +-- src/diffpy/srreal/PDFEnvelope.hpp | 139 +- src/diffpy/srreal/PDFUtils.cpp | 127 +- src/diffpy/srreal/PDFUtils.hpp | 66 +- src/diffpy/srreal/PQEvaluator.cpp | 682 +++++----- src/diffpy/srreal/PQEvaluator.hpp | 221 ++-- src/diffpy/srreal/PairCounter.cpp | 46 +- src/diffpy/srreal/PairCounter.hpp | 69 +- src/diffpy/srreal/PairQuantity.cpp | 704 +++++----- src/diffpy/srreal/PairQuantity.hpp | 265 ++-- src/diffpy/srreal/PeakProfile.cpp | 57 +- src/diffpy/srreal/PeakProfile.hpp | 107 +- src/diffpy/srreal/PeakWidthModel.cpp | 75 +- src/diffpy/srreal/PeakWidthModel.hpp | 145 +- .../srreal/PeriodicStructureAdapter.cpp | 289 ++-- .../srreal/PeriodicStructureAdapter.hpp | 171 ++- src/diffpy/srreal/PointsInSphere.cpp | 502 +++---- src/diffpy/srreal/PointsInSphere.hpp | 424 +++--- src/diffpy/srreal/QResolutionEnvelope.cpp | 90 +- src/diffpy/srreal/QResolutionEnvelope.hpp | 92 +- src/diffpy/srreal/QuantityType.hpp | 90 +- src/diffpy/srreal/R3linalg.cpp | 166 +-- src/diffpy/srreal/R3linalg.hpp | 364 +++-- src/diffpy/srreal/SFTElectron.cpp | 80 +- src/diffpy/srreal/SFTElectron.hpp | 85 +- src/diffpy/srreal/SFTElectronNumber.cpp | 77 +- src/diffpy/srreal/SFTElectronNumber.hpp | 87 +- src/diffpy/srreal/SFTNeutron.cpp | 80 +- src/diffpy/srreal/SFTNeutron.hpp | 87 +- src/diffpy/srreal/SFTXray.cpp | 78 +- src/diffpy/srreal/SFTXray.hpp | 85 +- src/diffpy/srreal/ScaleEnvelope.cpp | 87 +- src/diffpy/srreal/ScaleEnvelope.hpp | 92 +- src/diffpy/srreal/ScatteringFactorTable.cpp | 191 ++- src/diffpy/srreal/ScatteringFactorTable.hpp | 160 ++- src/diffpy/srreal/SphericalShapeEnvelope.cpp | 96 +- src/diffpy/srreal/SphericalShapeEnvelope.hpp | 92 +- src/diffpy/srreal/StepCutEnvelope.cpp | 88 +- src/diffpy/srreal/StepCutEnvelope.hpp | 84 +- src/diffpy/srreal/StructureAdapter.cpp | 158 +-- src/diffpy/srreal/StructureAdapter.hpp | 190 ++- src/diffpy/srreal/StructureDifference.cpp | 93 +- src/diffpy/srreal/StructureDifference.hpp | 95 +- src/diffpy/srreal/ZeroBaseline.cpp | 64 +- src/diffpy/srreal/ZeroBaseline.hpp | 72 +- src/diffpy/srreal/forwardtypes.hpp | 39 +- src/diffpy/srreal/scatteringfactordata.cpp | 615 ++++----- src/diffpy/srreal/scatteringfactordata.hpp | 4 +- src/diffpy/validators.hpp | 94 +- src/diffpy/version.cpp | 32 +- src/tests/TestAtomRadiiTable.hpp | 214 ++- src/tests/TestAtomicStructureAdapter.hpp | 250 ++-- src/tests/TestAttributes.hpp | 119 +- src/tests/TestBVParametersTable.hpp | 399 +++--- src/tests/TestBVSCalculator.hpp | 274 ++-- src/tests/TestBVSObjCryst.hpp | 95 +- src/tests/TestDebyePDFCalculator.hpp | 637 ++++----- src/tests/TestEmptyStructureAdapter.hpp | 106 +- src/tests/TestHasClassRegistry.hpp | 127 +- src/tests/TestLattice.hpp | 295 ++--- src/tests/TestNoMetaStructureAdapter.hpp | 165 ++- src/tests/TestObjCrystStructureAdapter.hpp | 1175 ++++++++--------- src/tests/TestOverlapCalculator.hpp | 645 +++++---- src/tests/TestPDFBaseline.hpp | 142 +- src/tests/TestPDFCalculator.hpp | 484 +++---- src/tests/TestPQEvaluator.hpp | 488 ++++--- src/tests/TestPairCounter.hpp | 258 ++-- src/tests/TestPeakProfile.hpp | 269 ++-- src/tests/TestPeakWidthModel.hpp | 331 +++-- src/tests/TestPeriodicStructureAdapter.hpp | 703 +++++----- src/tests/TestPointsInSphere.hpp | 311 ++--- src/tests/TestR3linalg.hpp | 209 ++- src/tests/TestRuntimePath.hpp | 131 +- src/tests/TestScatteringFactorTable.hpp | 364 +++-- src/tests/TestSphericalShapeEnvelope.hpp | 134 +- src/tests/TestStepCutEnvelope.hpp | 136 +- src/tests/objcryst_helpers.cpp | 58 +- src/tests/objcryst_helpers.hpp | 32 +- src/tests/serialization_helpers.hpp | 47 +- src/tests/test_custompqconfig.hpp | 115 +- src/tests/test_helpers.cpp | 195 ++- src/tests/test_helpers.hpp | 36 +- 167 files changed, 14141 insertions(+), 17309 deletions(-) create mode 100644 .clang-format create mode 100644 .codespell/ignore_words.txt create mode 100644 .flake8 create mode 100644 .isort.cfg create mode 100644 .pre-commit-config.yaml delete mode 100644 .travis-sconscript.local delete mode 100644 .travis.yml delete mode 100644 conda-recipe/build.sh delete mode 100644 conda-recipe/conda_build_config.yaml delete mode 100644 conda-recipe/meta.yaml delete mode 100644 conda-recipe/run_test.sh delete mode 100644 conda-recipe/sconscript.local delete mode 100644 conda-recipe/setup.py rename doc/{manual => }/Makefile (100%) rename doc/{manual => }/source/conf.py (73%) rename doc/{manual => }/source/index.rst (100%) rename doc/{manual => }/source/license.rst (100%) rename doc/{manual => }/source/release.rst (100%) diff --git a/.clang-format b/.clang-format new file mode 100644 index 00000000..0e711179 --- /dev/null +++ b/.clang-format @@ -0,0 +1,10 @@ +BasedOnStyle: Google + +IndentWidth: 2 +ContinuationIndentWidth: 2 +TabWidth: 2 +UseTab: Never + +ColumnLimit: 80 + +SortIncludes: false diff --git a/.codespell/ignore_words.txt b/.codespell/ignore_words.txt new file mode 100644 index 00000000..7d03c5ba --- /dev/null +++ b/.codespell/ignore_words.txt @@ -0,0 +1,2 @@ +multipl +Ccustom diff --git a/.flake8 b/.flake8 new file mode 100644 index 00000000..52d7edb1 --- /dev/null +++ b/.flake8 @@ -0,0 +1,14 @@ +# As of now, flake8 does not natively support configuration via pyproject.toml +# https://github.com/microsoft/vscode-flake8/issues/135 +[flake8] +exclude = + .git, + __pycache__, + build, + dist, + doc/source/conf.py + site_scons/* # bring it back after updating scons tools +max-line-length = 115 +# Ignore some style 'errors' produced while formatting by 'black' +# https://black.readthedocs.io/en/stable/guides/using_black_with_other_tools.html#labels-why-pycodestyle-warnings +extend-ignore = E203 diff --git a/.gitignore b/.gitignore index 449bdbd4..59993767 100644 --- a/.gitignore +++ b/.gitignore @@ -1,19 +1,26 @@ -# Compiled Object files -*.slo -*.lo -*.o +# Byte-compiled / optimized / DLL files +__pycache__/ +*.py[cod] +*$py.class +.exp +.lib +.dll # Compiled Dynamic libraries *.so *.dylib +# Compiled Object files +*.slo +*.lo +*.o + # Compiled Static libraries *.lai *.la *.a.sconf_temp # SCons build files -*.pyc .gdb_history .sconf_temp/ .sconsign.dblite @@ -24,16 +31,98 @@ errors.err /sconsvars.py tags -# eclipse +# Distribution / packaging +.Python +env/ +build/ +_build/ +develop-eggs/ +dist/ +downloads/ +eggs/ +.eggs/ +lib/ +lib64/ +parts/ +sdist/ +var/ +venv/ +*.egg-info/ +.installed.cfg +*.egg +bin/ +temp/ +tags/ +errors.err + +# PyInstaller +# Usually these files are written by a python script from a template +# before PyInstaller builds the exe, so as to inject date/other infos into it. +*.manifest +*.spec + +# Installer logs +pip-log.txt +pip-delete-this-directory.txt +MANIFEST + +# Unit test / coverage reports +htmlcov/ +.tox/ +.coverage +.coverage.* +.cache +nosetests.xml +coverage.xml +*,cover +.hypothesis/ + +# Translations +*.mo +*.pot + +# Mr Developer +.mr.developer.cfg .project .pydevproject -# source distribution tarball -libdiffpy-*.tar.gz +# Django stuff: +*.log + +# Sphinx documentation +docs/build/ +docs/source/generated/ + +# pytest +.pytest_cache/ + +# PyBuilder +target/ +# Editor files +# mac +.DS_Store +*~ +# vim +*.swp +*.swo + +# pycharm +.idea/ + +# VSCode +.vscode/ + +# Visual Studio .vs/* -.exp -.lib -.dll -.vscode/* + +# eclipse +.project +.pydevproject + +# Ipython Notebook +.ipynb_checkpoints + +# source distribution tarball +libdiffpy-*.tar.gz diff --git a/.isort.cfg b/.isort.cfg new file mode 100644 index 00000000..7ce0fb1f --- /dev/null +++ b/.isort.cfg @@ -0,0 +1,5 @@ +[settings] +# Keep import statement below line_length character limit +line_length = 115 +multi_line_output = 3 +include_trailing_comma = True diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml new file mode 100644 index 00000000..1f9b90b9 --- /dev/null +++ b/.pre-commit-config.yaml @@ -0,0 +1,84 @@ +default_language_version: + python: python3 + +exclude: (?x)^.*\.(cif|dat)$ + +ci: + autofix_commit_msg: | + [pre-commit.ci] auto fixes from pre-commit hooks + autofix_prs: true + autoupdate_branch: "pre-commit-autoupdate" + autoupdate_commit_msg: "[pre-commit.ci] pre-commit autoupdate" + autoupdate_schedule: monthly + skip: [no-commit-to-branch] + submodules: false + +repos: + - repo: https://github.com/pre-commit/pre-commit-hooks + rev: v4.6.0 + hooks: + - id: check-yaml + - id: end-of-file-fixer + - id: trailing-whitespace + - id: check-case-conflict + - id: check-merge-conflict + - id: check-toml + - id: check-added-large-files + + - repo: https://github.com/psf/black + rev: 24.4.2 + hooks: + - id: black + files: \.py$ + + - repo: https://github.com/pycqa/isort + rev: 5.13.2 + hooks: + - id: isort + args: ["--profile", "black"] + files: \.py$ + + - repo: https://github.com/pycqa/flake8 + rev: 7.0.0 + hooks: + - id: flake8 + files: \.py$ + + - repo: https://github.com/pre-commit/mirrors-clang-format + rev: v20.1.6 + hooks: + - id: clang-format + name: clang-format (C/C++) + files: \.(c|cpp|cc|cxx|h|hpp|hh)$ + + # Add this back after maintainance + # - repo: https://github.com/cpplint/cpplint + # rev: 2.0.2 + # hooks: + # - id: cpplint + # name: cpplint (C/C++) + # files: \.(c|cc|cpp|cxx|h|hh|hpp|hxx)$ + # args: + # - --filter= + # -whitespace/line_length + # ,-legal/copyright + # ,-build/header_guard + # ,-build/include_order + # ,-build/include_what_you_use + + - repo: https://github.com/codespell-project/codespell + rev: v2.3.0 + hooks: + - id: codespell + additional_dependencies: + - tomli + args: + - "--ignore-words=./.codespell/ignore_words.txt" + + - repo: https://github.com/pre-commit/pre-commit-hooks + rev: v4.4.0 + hooks: + - id: no-commit-to-branch + name: Prevent commit to main branch + args: ["--branch", "main"] + stages: [pre-commit] diff --git a/.travis-sconscript.local b/.travis-sconscript.local deleted file mode 100644 index c7fb22be..00000000 --- a/.travis-sconscript.local +++ /dev/null @@ -1,34 +0,0 @@ -# Customize scons build environment. - -Import('env') - -import os - -# Workaround: DEBUG_CPPFLAGS is defined on Linux, but not on Mac OS X. -# Make sure it is always sensibly defined. -os.environ.setdefault('DEBUG_CPPFLAGS', os.environ['CPPFLAGS']) - -# Apply environment settings for Anaconda compilers -env.Replace(CXX=os.environ['CXX']) -if env['build'] == 'fast': - env.MergeFlags(os.environ['CFLAGS']) - env.MergeFlags(os.environ['CPPFLAGS']) - env.MergeFlags(os.environ['CXXFLAGS']) -else: - env.MergeFlags(os.environ['DEBUG_CFLAGS']) - env.MergeFlags(os.environ['DEBUG_CPPFLAGS']) - env.MergeFlags(os.environ['DEBUG_CXXFLAGS']) -env.MergeFlags(os.environ['LDFLAGS']) - -# Silence copious warnings from the boost headers. -P = os.environ['MYPREFIX'] -env.Prepend(CCFLAGS=['-isystem{}/include'.format(P)]) - -# Define path to the shared libraries from Anaconda environment. -L = P + '/lib' -env.Append(LIBPATH=L) -env.Append(LINKFLAGS='-Wl,-rpath,{!r}'.format(L)) -if env['PLATFORM'] == 'posix': - env.Append(LINKFLAGS='-Wl,-rpath-link,{!r}'.format(L)) - -# vim: ft=python diff --git a/.travis.yml b/.travis.yml deleted file mode 100644 index c7af470c..00000000 --- a/.travis.yml +++ /dev/null @@ -1,153 +0,0 @@ -dist: xenial -language: cpp - -os: - - linux - - osx - -env: - - MYUSEMC=true MYPYTHON_VERSION=2.7 - - MYUSEMC=true MYPYTHON_VERSION=3.7 - - MYUSEMC=false - -matrix: - exclude: - - os: osx - env: MYUSEMC=true MYPYTHON_VERSION=2.7 - -git: - depth: 999999 - -branches: - except: - - /^v[0-9]/ - - -before_install: - - MYNAME=libdiffpy - - MYCOMMIT="$(git rev-parse HEAD)" - # travis virtual hosts have 2 cores as per https://goo.gl/DQy4oW - - MYNCPU=2 - - umask 022 - - git fetch origin --tags - - NOSYS=true; NOAPT=true; NOBREW=true; NOMC=true - - if ${MYUSEMC}; then - NOMC=false; - elif [[ ${TRAVIS_OS_NAME} == linux ]]; then - NOAPT=false; NOSYS=false; - MYPIPFLAGS="--user"; - elif [[ ${TRAVIS_OS_NAME} == osx ]]; then - NOBREW=false; NOSYS=false; - MYPIPFLAGS="--user"; - fi - - MYMCREPO=https://repo.anaconda.com/miniconda - - case ${TRAVIS_OS_NAME} in - linux) - MYMCBUNDLE=Miniconda3-latest-Linux-x86_64.sh; - MYCXXCOMPILER=gxx_linux-64 - ;; - osx) - MYMCBUNDLE=Miniconda3-latest-MacOSX-x86_64.sh; - MYCXXCOMPILER=clangxx_osx-64 - ;; - *) - echo "Unsupported operating system." >&2; - exit 2 ;; - esac - - - mkdir -p ~/pkgs - - - $NOMC || pushd ~/pkgs - - $NOMC || wget --timestamping ${MYMCREPO}/${MYMCBUNDLE} - - $NOMC || test -x ~/mc/bin/conda || bash ${MYMCBUNDLE} -b -f -p ~/mc - - $NOMC || popd - - $NOMC || source ~/mc/bin/activate base - - $NOMC || conda update --yes conda - - $NOMC || conda install --yes conda-build conda-verify jinja2 - - $NOMC || conda create --name=testenv --yes $MYCXXCOMPILER - - $NOMC || conda config --add channels diffpy - - - $NOAPT || test "${TRAVIS_OS_NAME}" = "linux" || exit $? - - $NOAPT || sudo apt-get update -qq - - $NOAPT || sudo apt-get install -y - libgsl0-dev libboost-all-dev python-pip - build-essential scons cxxtest - - - $NOBREW || test "${TRAVIS_OS_NAME}" = "osx" || exit $? - - - $NOBREW || brew update - - $NOBREW || brew install gsl - - $NOBREW || brew install scons - - $NOBREW || brew install cxxtest - - $NOBREW || PATH="$(brew --prefix python)/libexec/bin:${PATH}" - - $NOBREW || export CXXTEST="$(brew --prefix cxxtest)"/libexec/bin/cxxtestgen - - - $NOSYS || scons sdist - - $NOSYS || MYTARBUNDLE="$(ls -t "${PWD}"/*.tar.gz | head -1)" - - $NOSYS || pushd ~/pkgs - - $NOSYS || tar xzf "${MYTARBUNDLE}" - - $NOSYS || git clone https://github.com/diffpy/libobjcryst.git - - $NOSYS || popd - - -install: - - $NOMC || conda build --python=${MYPYTHON_VERSION} conda-recipe - - $NOMC || conda render --python=${MYPYTHON_VERSION} --output conda-recipe | - sed 's,.*/,,; s/[.]tar[.]bz2$//; s/-/=/g' > /tmp/mypackage.txt - - $NOMC || source activate base - - $NOMC || conda build purge - - $NOMC || source activate testenv - - $NOMC || conda install --yes --use-local --file=/tmp/mypackage.txt - - $NOMC || conda install --yes cxxtest scons - - $NOMC || MYPREFIX="${HOME}/mc/envs/testenv" - - - MYSUDO= - - $NOAPT || MYSUDO=sudo - - $NOSYS || $MYSUDO scons -j $MYNCPU -C ~/pkgs/libobjcryst install - - $NOSYS || cd ~/pkgs/"$(basename "${MYTARBUNDLE}" .tar.gz)" - - $NOSYS || scons -j $MYNCPU build=fast lib - - $NOSYS || $MYSUDO scons build=fast install - - $NOSYS || scons -Q build=fast --clean lib - - $NOSYS || MYPREFIX=/usr/local - - # Verify version of the installed library. - - MYINCLUDE="${MYPREFIX}/include" - - MYVERSION="${MYINCLUDE}/diffpy/version.hpp" - - MYGIT_REV=$(grep '^#define DIFFPY_GIT_SHA' "$MYVERSION" | cut -d '"' -f 2) - - if [[ "${MYCOMMIT}" != "${MYGIT_REV}" ]]; then - echo "Version mismatch ${MYCOMMIT} vs ${MYGIT_REV}."; - exit 1; - fi - - - $NOMC || export MYPREFIX - # drop linker flags that spuriously remove linkage with libgslcblas - - $NOMC || LDFLAGS="${LDFLAGS/-Wl,-dead_strip_dylibs/}" - - $NOMC || cp .travis-sconscript.local sconscript.local - - scons -j $MYNCPU build=fast prefix="${MYPREFIX}" test_installed=true alltests - - MYALLTESTSFAST=$(ls -t ${PWD}/build/fast*/tests/alltests | head -1) - - scons -j $MYNCPU build=debug lib alltests - # APT and BREW jobs test builds from expanded source archive, but codecov - # requires compilation from a git repository. Tell scons to do so. - - scons -j $MYNCPU -C $TRAVIS_BUILD_DIR build=coverage lib alltests - - -before_script: - - $NOBREW || USER_BASE="$(python -c 'import site; print(site.USER_BASE)')" - - $NOBREW || PATH="${USER_BASE}/bin:${PATH}" - - -script: - - scons -Q build=debug test - - scons -Q build=debug enable_objcryst=false test - - ${MYALLTESTSFAST} - - scons -j $MYNCPU -C $TRAVIS_BUILD_DIR build=coverage test - - -after_success: - - cd $TRAVIS_BUILD_DIR - - pip install $MYPIPFLAGS codecov - - codecov - - -deploy: - - provider: releases - - draft: true diff --git a/conda-recipe/build.sh b/conda-recipe/build.sh deleted file mode 100644 index 591d6c57..00000000 --- a/conda-recipe/build.sh +++ /dev/null @@ -1,12 +0,0 @@ -#!/bin/bash - -MYNCPU=$(( (CPU_COUNT > 8) ? 8 : CPU_COUNT )) - -# drop linker flags that spuriously remove linkage with libgslcblas -LDFLAGS="${LDFLAGS/-Wl,-dead_strip_dylibs/}" - -# Apply sconscript.local customizations. -cp ${RECIPE_DIR}/sconscript.local ./ - -# Build and install the library. -scons -j $MYNCPU lib install prefix=$PREFIX diff --git a/conda-recipe/conda_build_config.yaml b/conda-recipe/conda_build_config.yaml deleted file mode 100644 index 3fb0e02f..00000000 --- a/conda-recipe/conda_build_config.yaml +++ /dev/null @@ -1,11 +0,0 @@ -libobjcryst: - - 2017.2.* - -gsl: - - 2.4 - -boost: - - 1.67 - -pin_run_as_build: - libboost: x.x diff --git a/conda-recipe/meta.yaml b/conda-recipe/meta.yaml deleted file mode 100644 index e5fd1eb5..00000000 --- a/conda-recipe/meta.yaml +++ /dev/null @@ -1,51 +0,0 @@ -{% set setupdata = load_setup_py_data(from_recipe_dir=True) %} - -package: - name: libdiffpy - version: {{ setupdata['version'] }} - -source: - # git_url: https://github.com/diffpy/libdiffpy.git - git_url: .. - -build: - number: 0 - run_exports: - - {{ pin_subpackage('libdiffpy', max_pin='x.x') }} - -requirements: - build: - - {{ compiler('cxx') }} - - python - - scons - host: - - libboost {{ boost }} - - gsl {{ gsl }} - - libobjcryst {{ libobjcryst }} - - run: - # NOTE libobjcryst is implicitly added by libobjcryst run_exports - - libboost - - {{ pin_compatible('gsl', min_pin='x.x', max_pin='x') }} - -test: - requires: - - {{ compiler('cxx') }} - - cxxtest - - scons - - source_files: - - SConstruct - - site_scons/ - - src/SConscript - - src/SConscript.configure - - src/tests/ - - # commands: - # Execute the run_test.sh script. - -about: - home: https://github.com/diffpy/libdiffpy - summary: C++ calculators of PDF, bond valence sum and other pair quantities. - license: Modified BSD License - license_file: LICENSE.txt diff --git a/conda-recipe/run_test.sh b/conda-recipe/run_test.sh deleted file mode 100644 index ae0d976d..00000000 --- a/conda-recipe/run_test.sh +++ /dev/null @@ -1,16 +0,0 @@ -#!/bin/bash - -export CPATH="${PREFIX}/include:${CPATH}" -export LIBRARY_PATH="${PREFIX}/lib:${LIBRARY_PATH}" - -MYNCPU=$(( (CPU_COUNT > 8) ? 8 : CPU_COUNT )) - -# Apply sconscript.local customizations. -cp ${RECIPE_DIR}/sconscript.local ./ - -# Build the unit tests program using the installed library. -scons -j $MYNCPU alltests prefix=$PREFIX test_installed=true - -# Execute the unit tests. -MYALLTESTSFAST=$(ls -t ${PWD}/build/fast*/tests/alltests | head -1) -${MYALLTESTSFAST} diff --git a/conda-recipe/sconscript.local b/conda-recipe/sconscript.local deleted file mode 100644 index f8740395..00000000 --- a/conda-recipe/sconscript.local +++ /dev/null @@ -1,18 +0,0 @@ -# Customize scons build environment. - -Import('env') - -import os - -# Apply environment settings for Anaconda compilers -env.Replace(CXX=os.environ['CXX']) -env.MergeFlags(os.environ['CFLAGS']) -env.MergeFlags(os.environ['CPPFLAGS']) -env.MergeFlags(os.environ['CXXFLAGS']) -env.MergeFlags(os.environ['LDFLAGS']) - -# Silence copious warnings from the boost headers. -P = os.environ['PREFIX'] -env.Prepend(CCFLAGS=['-isystem{}/include'.format(P)]) - -# vim: ft=python diff --git a/conda-recipe/setup.py b/conda-recipe/setup.py deleted file mode 100644 index e03b9486..00000000 --- a/conda-recipe/setup.py +++ /dev/null @@ -1,22 +0,0 @@ -#!/usr/bin/env python - -"""\ -Dummy distutils setup script that provides version data for meta.yaml. -""" - -import os -import sys -from distutils.core import setup - -MYDIR = os.path.dirname(os.path.abspath(__file__)) -SITESCONSDIR = os.path.abspath(os.path.join(MYDIR, '../site_scons')) -sys.path.insert(0, SITESCONSDIR) - -from libdiffpybuildutils import gitinfo - -ginfo = gitinfo() - -setup( - name='libdiffpy', - version=ginfo['version'], -) diff --git a/doc/manual/Makefile b/doc/Makefile similarity index 100% rename from doc/manual/Makefile rename to doc/Makefile diff --git a/doc/manual/source/conf.py b/doc/source/conf.py similarity index 73% rename from doc/manual/source/conf.py rename to doc/source/conf.py index 12452388..74ef0cd1 100644 --- a/doc/manual/source/conf.py +++ b/doc/source/conf.py @@ -13,75 +13,76 @@ # All configuration values have a default; values that are commented out # serve to show the default. -import sys import os +import sys import time # If extensions (or modules to document with autodoc) are in another directory, # add these directories to sys.path here. If the directory is relative to the # documentation root, use os.path.abspath to make it absolute, like shown here. -#sys.path.insert(0, os.path.abspath('.')) -sys.path.insert(0, os.path.abspath('../../../site_scons')) +# sys.path.insert(0, os.path.abspath('.')) +sys.path.insert(0, os.path.abspath("../../../site_scons")) # abbreviations -ab_authors = u'Pavol Juhás, Christopher L. Farrow, Simon J.L. Billinge group' +ab_authors = "Pavol Juhás, Christopher L. Farrow, Simon J.L. Billinge group" # -- General configuration ------------------------------------------------ # If your documentation needs a minimal Sphinx version, state it here. -#needs_sphinx = '1.0' +# needs_sphinx = '1.0' # Add any Sphinx extension module names here, as strings. They can be # extensions coming with Sphinx (named 'sphinx.ext.*') or your custom # ones. extensions = [ - 'sphinx.ext.intersphinx', - 'sphinx.ext.ifconfig', - 'm2r', + "sphinx.ext.intersphinx", + "sphinx.ext.ifconfig", + "m2r", ] # Add any paths that contain templates here, relative to this directory. -templates_path = ['_templates'] +templates_path = ["_templates"] # The suffix of source filenames. -source_suffix = ['.rst', '.md'] +source_suffix = [".rst", ".md"] # The encoding of source files. -#source_encoding = 'utf-8-sig' +# source_encoding = 'utf-8-sig' # The master toctree document. -master_doc = 'index' +master_doc = "index" # General information about the project. -project = u'libdiffpy' -copyright = u'%Y, Brookhaven National Laboratory' +project = "libdiffpy" +copyright = "%Y, Brookhaven National Laboratory" # The version info for the project you're documenting, acts as replacement for # |version| and |release|, also used in various other places throughout the # built documents. from libdiffpybuildutils import getversion + gver = getversion() # The short X.Y version. -vfmt = '%(major)i.%(minor)i' + ('.%(micro)i' if gver['micro'] else '') +vfmt = "%(major)i.%(minor)i" + (".%(micro)i" if gver["micro"] else "") version = vfmt % gver # The full version, including alpha/beta/rc tags. -release = gver['version'] +release = gver["version"] # The language for content autogenerated by Sphinx. Refer to documentation # for a list of supported languages. -#language = None +# language = None # There are two options for replacing |today|: either, you set today to some # non-false value, then it is used: -#today = '' -fulldate = gver['date'] -today_seconds = time.strptime(fulldate.split()[0], '%Y-%m-%d') -today = time.strftime('%B %d, %Y', today_seconds) +# today = '' +fulldate = gver["date"] +today_seconds = time.strptime(fulldate.split()[0], "%Y-%m-%d") +today = time.strftime("%B %d, %Y", today_seconds) year = today.split()[-1] # Else, today_fmt is used as the format for a strftime call. -#today_fmt = '%B %d, %Y' +# today_fmt = '%B %d, %Y' # substitute YEAR in the copyright string -copyright = copyright.replace('%Y', year) +copyright = copyright.replace("%Y", year) # List of patterns, relative to source directory, that match files and # directories to ignore when looking for source files. @@ -89,27 +90,27 @@ # The reST default role (used for this markup: `text`) to use for all # documents. -#default_role = None +# default_role = None # If true, '()' will be appended to :func: etc. cross-reference text. -#add_function_parentheses = True +# add_function_parentheses = True # If true, the current module name will be prepended to all description # unit titles (such as .. function::). -#add_module_names = True +# add_module_names = True # If true, sectionauthor and moduleauthor directives will be shown in the # output. They are ignored by default. -#show_authors = False +# show_authors = False # The name of the Pygments (syntax highlighting) style to use. -pygments_style = 'sphinx' +pygments_style = "sphinx" # A list of ignored prefixes for module index sorting. -#modindex_common_prefix = [] +# modindex_common_prefix = [] # If true, keep warnings as "system message" paragraphs in the built documents. -#keep_warnings = False +# keep_warnings = False # Display all warnings for missing links. nitpicky = True @@ -119,35 +120,35 @@ # The theme to use for HTML and HTML Help pages. See the documentation for # a list of builtin themes. # -html_theme = 'sphinx_py3doc_enhanced_theme' +html_theme = "sphinx_py3doc_enhanced_theme" # Theme options are theme-specific and customize the look and feel of a theme # further. For a list of options available for each theme, see the # documentation. # html_theme_options = { - 'collapsiblesidebar' : 'true', - 'navigation_with_keys' : 'true', + "collapsiblesidebar": "true", + "navigation_with_keys": "true", } # Add any paths that contain custom themes here, relative to this directory. -#html_theme_path = [] +# html_theme_path = [] # The name for this set of Sphinx documents. If None, it defaults to # " v documentation". -#html_title = None +# html_title = None # A shorter title for the navigation bar. Default is the same as html_title. -#html_short_title = None +# html_short_title = None # The name of an image file (relative to this directory) to place at the top # of the sidebar. -#html_logo = None +# html_logo = None # The name of an image file (within the static path) to use as favicon of the # docs. This file should be a Windows icon file (.ico) being 16x16 or 32x32 # pixels large. -#html_favicon = None +# html_favicon = None # Add any paths that contain custom static files (such as style sheets) here, # relative to this directory. They are copied after the builtin static files, @@ -157,106 +158,100 @@ # Add any extra paths that contain custom files (such as robots.txt or # .htaccess) here, relative to this directory. These files are copied # directly to the root of the documentation. -#html_extra_path = [] +# html_extra_path = [] # If not '', a 'Last updated on:' timestamp is inserted at every page bottom, # using the given strftime format. -#html_last_updated_fmt = '%b %d, %Y' +# html_last_updated_fmt = '%b %d, %Y' # If true, SmartyPants will be used to convert quotes and dashes to # typographically correct entities. -#html_use_smartypants = True +# html_use_smartypants = True # Custom sidebar templates, maps document names to template names. -#html_sidebars = {} +# html_sidebars = {} # Additional templates that should be rendered to pages, maps page names to # template names. -#html_additional_pages = {} +# html_additional_pages = {} # If false, no module index is generated. -#html_domain_indices = True +# html_domain_indices = True # If false, no index is generated. -#html_use_index = True +# html_use_index = True # If true, the index is split into individual pages for each letter. -#html_split_index = False +# html_split_index = False # If true, links to the reST sources are added to the pages. -#html_show_sourcelink = True +# html_show_sourcelink = True # If true, "Created using Sphinx" is shown in the HTML footer. Default is True. -#html_show_sphinx = True +# html_show_sphinx = True # If true, "(C) Copyright ..." is shown in the HTML footer. Default is True. -#html_show_copyright = True +# html_show_copyright = True # If true, an OpenSearch description file will be output, and all pages will # contain a tag referring to it. The value of this option must be the # base URL from which the finished HTML is served. -#html_use_opensearch = '' +# html_use_opensearch = '' # This is the file name suffix for HTML files (e.g. ".xhtml"). -#html_file_suffix = None +# html_file_suffix = None # Output file base name for HTML help builder. -htmlhelp_basename = 'libdiffpydoc' +htmlhelp_basename = "libdiffpydoc" # -- Options for LaTeX output --------------------------------------------- latex_elements = { -# The paper size ('letterpaper' or 'a4paper'). -#'papersize': 'letterpaper', - -# The font size ('10pt', '11pt' or '12pt'). -#'pointsize': '10pt', - -# Additional stuff for the LaTeX preamble. -#'preamble': '', + # The paper size ('letterpaper' or 'a4paper'). + #'papersize': 'letterpaper', + # The font size ('10pt', '11pt' or '12pt'). + #'pointsize': '10pt', + # Additional stuff for the LaTeX preamble. + #'preamble': '', } # Grouping the document tree into LaTeX files. List of tuples # (source start file, target name, title, # author, documentclass [howto, manual, or own class]). latex_documents = [ - ('index', 'libdiffpy.tex', u'libdiffpy Documentation', - ab_authors, 'manual'), + ("index", "libdiffpy.tex", "libdiffpy Documentation", ab_authors, "manual"), ] # The name of an image file (relative to this directory) to place at the top of # the title page. -#latex_logo = None +# latex_logo = None # For "manual" documents, if this is true, then toplevel headings are parts, # not chapters. -#latex_use_parts = False +# latex_use_parts = False # If true, show page references after internal links. -#latex_show_pagerefs = False +# latex_show_pagerefs = False # If true, show URL addresses after external links. -#latex_show_urls = False +# latex_show_urls = False # Documents to append as an appendix to all manuals. -#latex_appendices = [] +# latex_appendices = [] # If false, no module index is generated. -#latex_domain_indices = True +# latex_domain_indices = True # -- Options for manual page output --------------------------------------- # One entry per manual page. List of tuples # (source start file, name, description, authors, manual section). -man_pages = [ - ('index', 'libdiffpy', u'libdiffpy Documentation', - ab_authors, 1) -] +man_pages = [("index", "libdiffpy", "libdiffpy Documentation", ab_authors, 1)] # If true, show URL addresses after external links. -#man_show_urls = False +# man_show_urls = False # -- Options for Texinfo output ------------------------------------------- @@ -265,22 +260,28 @@ # (source start file, target name, title, author, # dir menu entry, description, category) texinfo_documents = [ - ('index', 'libdiffpy', u'libdiffpy Documentation', - ab_authors, 'libdiffpy', 'One line description of project.', - 'Miscellaneous'), + ( + "index", + "libdiffpy", + "libdiffpy Documentation", + ab_authors, + "libdiffpy", + "One line description of project.", + "Miscellaneous", + ), ] # Documents to append as an appendix to all manuals. -#texinfo_appendices = [] +# texinfo_appendices = [] # If false, no module index is generated. -#texinfo_domain_indices = True +# texinfo_domain_indices = True # How to display URL addresses: 'footnote', 'no', or 'inline'. -#texinfo_show_urls = 'footnote' +# texinfo_show_urls = 'footnote' # If true, do not generate a @detailmenu in the "Top" node's menu. -#texinfo_no_detailmenu = False +# texinfo_no_detailmenu = False # Example configuration for intersphinx: refer to the Python standard library. diff --git a/doc/manual/source/index.rst b/doc/source/index.rst similarity index 100% rename from doc/manual/source/index.rst rename to doc/source/index.rst diff --git a/doc/manual/source/license.rst b/doc/source/license.rst similarity index 100% rename from doc/manual/source/license.rst rename to doc/source/license.rst diff --git a/doc/manual/source/release.rst b/doc/source/release.rst similarity index 100% rename from doc/manual/source/release.rst rename to doc/source/release.rst diff --git a/examples/testlib.cpp b/examples/testlib.cpp index 18abadfd..79ad5101 100644 --- a/examples/testlib.cpp +++ b/examples/testlib.cpp @@ -4,16 +4,14 @@ // c++ testlib.cpp -ldiffpy // ./a.out - #include #include -int main(int argc, char* argv[]) -{ - using namespace diffpy::srreal; - ScatteringFactorTablePtr ntbl; - ntbl = ScatteringFactorTable::createByType("neutron"); - ntbl->lookup("Pb"); - std::cout << "Installation of libdiffpy shared library works!\n"; - return 0; +int main(int argc, char* argv[]) { + using namespace diffpy::srreal; + ScatteringFactorTablePtr ntbl; + ntbl = ScatteringFactorTable::createByType("neutron"); + ntbl->lookup("Pb"); + std::cout << "Installation of libdiffpy shared library works!\n"; + return 0; } diff --git a/site_scons/fallback_version.py b/site_scons/fallback_version.py index 01b1119b..c147df59 100644 --- a/site_scons/fallback_version.py +++ b/site_scons/fallback_version.py @@ -1,10 +1,10 @@ #!/usr/bin/env python -''' +""" Definition of FALLBACK_VERSION. Use it when git repository is not available for example, when building from git zip archive. Update FALLBACK_VERSION when tagging a new release. -''' +""" -FALLBACK_VERSION = '1.4.0.post0' +FALLBACK_VERSION = "1.4.0.post0" diff --git a/site_scons/libdiffpybuildutils.py b/site_scons/libdiffpybuildutils.py index a8d8afbd..5312d27b 100644 --- a/site_scons/libdiffpybuildutils.py +++ b/site_scons/libdiffpybuildutils.py @@ -1,7 +1,7 @@ #!/usr/bin/env python -'''Utility functions used by scons and sphinx for version extraction. -''' +"""Utility functions used by scons and sphinx for version extraction. +""" import os import re @@ -21,33 +21,34 @@ def gitinfo(): - 'Extract dictionary of version data from git records.' - from subprocess import Popen, PIPE + "Extract dictionary of version data from git records." + from subprocess import PIPE, Popen + global _cached_gitinfo if _cached_gitinfo is not None: return _cached_gitinfo - nullfile = open(os.devnull, 'w') - kw = dict(stdout=PIPE, stderr=nullfile, cwd=MYDIR, - universal_newlines=True) - proc = Popen(['git', 'describe', '--match=v[[:digit:]]*'], **kw) + nullfile = open(os.devnull, "w") + kw = dict(stdout=PIPE, stderr=nullfile, cwd=MYDIR, universal_newlines=True) + proc = Popen(["git", "describe", "--match=v[[:digit:]]*"], **kw) desc = proc.stdout.read() if proc.wait(): _cached_gitinfo = {} return gitinfo() - proc = Popen(['git', 'log', '-1', '--format=%H %ci'], **kw) + proc = Popen(["git", "log", "-1", "--format=%H %ci"], **kw) glog = proc.stdout.read() - words = desc.strip().split('-') - vtag = words[0].lstrip('v') - vnum = int((words[1:2] or ['0'])[0]) + words = desc.strip().split("-") + vtag = words[0].lstrip("v") + vnum = int((words[1:2] or ["0"])[0]) rv = {} - rv['commit'], rv['date'] = glog.strip().split(None, 1) - rv['patchnumber'] = vnum - rv['version'] = vtag + rv["commit"], rv["date"] = glog.strip().split(None, 1) + rv["patchnumber"] = vnum + rv["version"] = vtag if vnum: - rv['version'] += '.post' + str(vnum) + rv["version"] += ".post" + str(vnum) _cached_gitinfo = rv return gitinfo() + _cached_gitinfo = None @@ -59,41 +60,42 @@ def getversion(): these sources are from git archive bundle. """ from fallback_version import FALLBACK_VERSION - gitarchivecfgfile = os.path.join(MYDIR, 'gitarchive.cfg') + + 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) + gaitems = re.findall(r"^(\w+) *= *(\S.*?)\s*$", gacontent, re.M) ga = dict(gaitems) gi = gitinfo() rv = {} if gi: afb = FALLBACK_VERSION - gfb = gi['version'].split('.post')[0] + '.post0' + gfb = gi["version"].split(".post")[0] + ".post0" if gfb != afb: raise RuntimeError(EMSG_BAD_FALLBACK_VERSION.format(afb, gfb)) rv.update(gi) else: # Not a git repository. Require that gitarchive.cfg is expanded. - if '$Format:' in ga['commit']: + if "$Format:" in ga["commit"]: raise RuntimeError(EMSG_GIT_MISSING) - rv['commit'] = ga['commit'] - rv['date'] = ga['date'] + rv["commit"] = ga["commit"] + rv["date"] = ga["date"] # First assume we have an undetermined post-release. Keep version # suffix as ".post0", but set patchnumber to ensure we are post # FALLBACK_VERSION. - rv['version'] = FALLBACK_VERSION - rv['patchnumber'] = 1 + rv["version"] = FALLBACK_VERSION + rv["patchnumber"] = 1 # If we are at version tag turn this to regular release. - mx = re.search(r'\btag: v(\d[^,]*)', ga['refnames']) + mx = re.search(r"\btag: v(\d[^,]*)", ga["refnames"]) if mx: - rv['version'] = mx.group(1) - rv['patchnumber'] = 0 + rv["version"] = mx.group(1) + rv["patchnumber"] = 0 # rv['version'] is resolved, let's parse its subfields. - vbase = rv['version'].split('.post')[0] - mx = re.match(r'^(\d+)\.(\d+)(?:\.(\d+))?((?:[ab]|rc)\d*)?', vbase) - rv['major'] = int(mx.group(1)) - rv['minor'] = int(mx.group(2)) - rv['micro'] = int(mx.group(3) or 0) - rv['prerelease'] = mx.group(4) + vbase = rv["version"].split(".post")[0] + mx = re.match(r"^(\d+)\.(\d+)(?:\.(\d+))?((?:[ab]|rc)\d*)?", vbase) + rv["major"] = int(mx.group(1)) + rv["minor"] = int(mx.group(2)) + rv["micro"] = int(mx.group(3) or 0) + rv["prerelease"] = mx.group(4) return rv diff --git a/site_scons/site_tools/cxxtest.py b/site_scons/site_tools/cxxtest.py index dc798269..565a27c0 100644 --- a/site_scons/site_tools/cxxtest.py +++ b/site_scons/site_tools/cxxtest.py @@ -1,12 +1,12 @@ # 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 @@ -63,18 +63,22 @@ # called that. Normal Program builder rules apply. # -from SCons.Script import * +import os + from SCons.Builder import Builder +from SCons.Script import * from SCons.Util import PrependPath, unique, uniquer -import os + # A warning class to notify users of problems class ToolCxxTestWarning(SCons.Warnings.Warning): pass + SCons.Warnings.enableWarningClass(ToolCxxTestWarning) -def accumulateEnvVar(dicts, name, default = []): + +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 @@ -85,7 +89,8 @@ def accumulateEnvVar(dicts, name, default = []): final += Split(d.get(name, default)) return final -def multiget(dictlist, key, default = None): + +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 @@ -97,151 +102,164 @@ def multiget(dictlist, key, default = None): 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(Split(kwargs.get('CXXTEST_LIBPATH', [])) + - Split(env.get( 'CXXTEST_LIBPATH', []))) + libpath = uniquer( + 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' + 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 + 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): + +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')) + 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' ))) + 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] + 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') + 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) + 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) + 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') + 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!") + 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') - ) + 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) + 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." - ) + 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. + # 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? + # maybe only the directory was specified? cxxtest = env.File( - os.path.join(cxxtest, defaultCxxTestGenLocation(env) - )).abspath + 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) + 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')) + 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])) + 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" - ) + 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' + searchfile = "TestSuite.h" cxxtestgen_pathlen = len(defaultCxxTestGenLocation(env)) - default_path = Dir(envget(env,'CXXTEST_INSTALL_DIR')).abspath + default_path = Dir(envget(env, "CXXTEST_INSTALL_DIR")).abspath - os_cxxtestgen = os.path.realpath(File(env['CXXTEST']).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)): + 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): @@ -284,52 +302,61 @@ def generate(env, **kwargs): # 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/' ) + 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' ) + env.SetDefault(CXXTEST_CXXTESTGEN_DEFAULT_LOCATION="bin") # the cxxtestgen script name. - env.SetDefault( CXXTEST_CXXTESTGEN_SCRIPT_NAME = 'cxxtestgen' ) + env.SetDefault(CXXTEST_CXXTESTGEN_SCRIPT_NAME="cxxtestgen") - #Here's where keyword arguments are applied + # 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']): + # 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) ) + env.AppendUnique(CXXTEST_CPPPATH=findCxxTestHeaders(env)) - cxxtest = env['CXXTEST'] + 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' - ) + 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!")) + cxxtest_builder = lambda *a: sys.stderr.write("ERROR: CXXTESTGEN NOT FOUND!") - def CxxTest(env, target, source = None, **kwargs): + 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 @@ -344,8 +371,8 @@ def CxxTest(env, target, source = None, **kwargs): 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', "") + if source == None: + suffix = multiget([kwargs, env, os.environ], "CXXTEST_SUFFIX", "") source = [t + suffix for t in target] sources = Flatten(Split(source)) headers = [] @@ -357,7 +384,7 @@ def CxxTest(env, target, source = None, **kwargs): except AttributeError: s = l - if s.endswith(multiget([kwargs, env, os.environ], 'CXXTEST_SUFFIX', None)): + if s.endswith(multiget([kwargs, env, os.environ], "CXXTEST_SUFFIX", None)): headers.append(l) else: linkins.append(l) @@ -370,27 +397,34 @@ def CxxTest(env, target, source = None, **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', [])) + [ + 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) - return UnitTest(env, target, source = deps, **kwargs) + env.Append(BUILDERS={"CxxTest": CxxTest, "CxxTestCpp": cxxtest_builder}) - env.Append( BUILDERS = { "CxxTest" : CxxTest, "CxxTestCpp" : cxxtest_builder } ) def exists(env): - return os.path.exists(env['CXXTEST']) + return os.path.exists(env["CXXTEST"]) diff --git a/src/SConscript b/src/SConscript index 2d38bc6c..f53292a4 100644 --- a/src/SConscript +++ b/src/SConscript @@ -58,14 +58,14 @@ if env['PLATFORM'] == "win32": env.PrependUnique(CCFLAGS=['-O3', '-std=c++14', '-m64', '-DBUILD_DLL', '-DREAL=double']) env.PrependUnique(CPPDEFINES='_USE_MATH_DEFINES') env.AppendUnique(LINKFLAGS='-m64') - + else: # Visual c++ env.PrependUnique(CCFLAGS=['/Ox', '/EHsc', '/MD', '/std:c++14', '/Wall', '/DBUILD_DLL', '/DREAL=double']) env.AppendUnique(CPPDEFINES=[{'NDEBUG': None}, '_USE_MATH_DEFINES']) env.AppendUnique(LINKFLAGS=['/OPT:NOREF', '/OPT:NOICF']) -else: +else: env.PrependUnique(CXXFLAGS='-std=c++11') if env['tool'] == 'intelc': # options for Intel C++ compiler on hpc dev-intel07 diff --git a/src/diffpy/Attributes.cpp b/src/diffpy/Attributes.cpp index ab2db890..2307182d 100644 --- a/src/diffpy/Attributes.cpp +++ b/src/diffpy/Attributes.cpp @@ -1,21 +1,21 @@ /***************************************************************************** -* -* 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.txt for a list of people who contributed. -* See LICENSE_DANSE.txt for license information. -* -****************************************************************************** -* -* class Attributes - interface for calling setter and getter methods using -* their string names. -* -*****************************************************************************/ + * + * 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.txt for a list of people who contributed. + * See LICENSE_DANSE.txt for license information. + * + ****************************************************************************** + * + * class Attributes - interface for calling setter and getter methods using + * their string names. + * + *****************************************************************************/ #include @@ -32,192 +32,136 @@ namespace attributes { // Public Methods ------------------------------------------------------------ -double Attributes::getDoubleAttr(const string& name) const -{ - this->checkAttributeName(name); - GetDoubleAttrVisitor vg(name); - this->accept(vg); - return vg.getValue(); +double Attributes::getDoubleAttr(const string& name) const { + this->checkAttributeName(name); + GetDoubleAttrVisitor vg(name); + this->accept(vg); + return vg.getValue(); } - -void Attributes::setDoubleAttr(const string& name, double value) -{ - this->checkAttributeName(name); - SetDoubleAttrVisitor vs(name, value); - this->accept(vs); +void Attributes::setDoubleAttr(const string& name, double value) { + this->checkAttributeName(name); + SetDoubleAttrVisitor vs(name, value); + this->accept(vs); } - -bool Attributes::hasDoubleAttr(const string& name) const -{ - CountDoubleAttrVisitor vc(name); - this->accept(vc); - return vc.count(); +bool Attributes::hasDoubleAttr(const string& name) const { + CountDoubleAttrVisitor vc(name); + this->accept(vc); + return vc.count(); } - -set Attributes::namesOfDoubleAttributes() const -{ - const bool excludereadonly = false; - NamesOfDoubleAttributesVisitor vn(excludereadonly); - this->accept(vn); - set rv = vn.names(); - return rv; +set Attributes::namesOfDoubleAttributes() const { + const bool excludereadonly = false; + NamesOfDoubleAttributesVisitor vn(excludereadonly); + this->accept(vn); + set rv = vn.names(); + return rv; } - -set Attributes::namesOfWritableDoubleAttributes() const -{ - const bool excludereadonly = true; - NamesOfDoubleAttributesVisitor vn(excludereadonly); - this->accept(vn); - set rv = vn.names(); - return rv; +set Attributes::namesOfWritableDoubleAttributes() const { + const bool excludereadonly = true; + NamesOfDoubleAttributesVisitor vn(excludereadonly); + this->accept(vn); + set rv = vn.names(); + return rv; } // Private Methods ----------------------------------------------------------- -void Attributes::checkAttributeName(const string& name) const -{ - CountDoubleAttrVisitor v(name); - this->accept(v); - if (v.count() == 0) - { - ostringstream emsg; - emsg << "Invalid attribute name '" << name << "'."; - throw DoubleAttributeError(emsg.str()); - } - else if (v.count() > 1) - { - ostringstream emsg; - emsg << "Duplicate attribute '" << name << "'."; - throw logic_error(emsg.str()); - } +void Attributes::checkAttributeName(const string& name) const { + CountDoubleAttrVisitor v(name); + this->accept(v); + if (v.count() == 0) { + ostringstream emsg; + emsg << "Invalid attribute name '" << name << "'."; + throw DoubleAttributeError(emsg.str()); + } else if (v.count() > 1) { + ostringstream emsg; + emsg << "Duplicate attribute '" << name << "'."; + throw logic_error(emsg.str()); + } } // Visitor Classes ----------------------------------------------------------- // CountDoubleAttrVisitor -Attributes::CountDoubleAttrVisitor:: -CountDoubleAttrVisitor(const string& name) : - mname(name), - mcount(0) -{ } +Attributes::CountDoubleAttrVisitor::CountDoubleAttrVisitor(const string& name) + : mname(name), mcount(0) {} - -void Attributes::CountDoubleAttrVisitor:: -visit(const Attributes& a) -{ - mcount += a.mdoubleattrs.count(mname); +void Attributes::CountDoubleAttrVisitor::visit(const Attributes& a) { + mcount += a.mdoubleattrs.count(mname); } - -int Attributes::CountDoubleAttrVisitor:: -count() const -{ - return mcount; -} +int Attributes::CountDoubleAttrVisitor::count() const { return mcount; } // GetDoubleAttrVisitor -Attributes::GetDoubleAttrVisitor:: -GetDoubleAttrVisitor(const string& name) : - mname(name) -{ } - - -void Attributes::GetDoubleAttrVisitor:: -visit(const Attributes& a) -{ - DoubleAttributeStorage::const_iterator ai; - ai = a.mdoubleattrs.find(mname); - if (ai != a.mdoubleattrs.end()) - { - mvalue = ai->second->getValue(&a); - } -} +Attributes::GetDoubleAttrVisitor::GetDoubleAttrVisitor(const string& name) + : mname(name) {} - -double Attributes::GetDoubleAttrVisitor:: -getValue() const -{ - return mvalue; +void Attributes::GetDoubleAttrVisitor::visit(const Attributes& a) { + DoubleAttributeStorage::const_iterator ai; + ai = a.mdoubleattrs.find(mname); + if (ai != a.mdoubleattrs.end()) { + mvalue = ai->second->getValue(&a); + } } -// SetDoubleAttrVisitor +double Attributes::GetDoubleAttrVisitor::getValue() const { return mvalue; } -Attributes::SetDoubleAttrVisitor:: -SetDoubleAttrVisitor(const string& name, double value) : - mname(name), - mvalue(value) -{ } +// SetDoubleAttrVisitor +Attributes::SetDoubleAttrVisitor::SetDoubleAttrVisitor(const string& name, + double value) + : mname(name), mvalue(value) {} -void Attributes::SetDoubleAttrVisitor:: -visit(const Attributes& a) -{ - const char* emsg = "Cannot set attribute of a const instance."; - throw logic_error(emsg); +void Attributes::SetDoubleAttrVisitor::visit(const Attributes& a) { + const char* emsg = "Cannot set attribute of a const instance."; + throw logic_error(emsg); } - -void Attributes::SetDoubleAttrVisitor:: -visit(Attributes& a) -{ - DoubleAttributeStorage::iterator ai; - ai = a.mdoubleattrs.find(mname); - if (ai != a.mdoubleattrs.end()) - { - ai->second->setValue(&a, mvalue); - } +void Attributes::SetDoubleAttrVisitor::visit(Attributes& a) { + DoubleAttributeStorage::iterator ai; + ai = a.mdoubleattrs.find(mname); + if (ai != a.mdoubleattrs.end()) { + ai->second->setValue(&a, mvalue); + } } // NamesOfDoubleAttributesVisitor -Attributes::NamesOfDoubleAttributesVisitor:: -NamesOfDoubleAttributesVisitor(bool excludereadonly) -{ - mexcludereadonly = excludereadonly; +Attributes::NamesOfDoubleAttributesVisitor::NamesOfDoubleAttributesVisitor( + bool excludereadonly) { + mexcludereadonly = excludereadonly; } - -void Attributes::NamesOfDoubleAttributesVisitor:: -visit(const Attributes& a) -{ - DoubleAttributeStorage::const_iterator ati; - for (ati = a.mdoubleattrs.begin(); ati != a.mdoubleattrs.end(); ++ati) - { - if (mexcludereadonly && ati->second->isreadonly()) continue; - mnames.insert(ati->first); - } +void Attributes::NamesOfDoubleAttributesVisitor::visit(const Attributes& a) { + DoubleAttributeStorage::const_iterator ati; + for (ati = a.mdoubleattrs.begin(); ati != a.mdoubleattrs.end(); ++ati) { + if (mexcludereadonly && ati->second->isreadonly()) continue; + mnames.insert(ati->first); + } } - -const set& Attributes::NamesOfDoubleAttributesVisitor:: -names() const -{ - return mnames; +const set& Attributes::NamesOfDoubleAttributesVisitor::names() const { + return mnames; } // Non-member Helpers -------------------------------------------------------- -void registerBaseDoubleAttribute(Attributes* obj, - const string& name, BaseDoubleAttribute* pa) -{ - obj->mdoubleattrs[name].reset(pa); +void registerBaseDoubleAttribute(Attributes* obj, const string& name, + BaseDoubleAttribute* pa) { + obj->mdoubleattrs[name].reset(pa); } - -void throwDoubleAttributeReadOnly() -{ - const char* emsg = - "Cannot change value of read-only DoubleAttribute."; - throw DoubleAttributeError(emsg); +void throwDoubleAttributeReadOnly() { + const char* emsg = "Cannot change value of read-only DoubleAttribute."; + throw DoubleAttributeError(emsg); } -} // namespace attributes -} // namespace diffpy +} // namespace attributes +} // namespace diffpy // End of file diff --git a/src/diffpy/Attributes.hpp b/src/diffpy/Attributes.hpp index 3c84ea04..1f70b8d7 100644 --- a/src/diffpy/Attributes.hpp +++ b/src/diffpy/Attributes.hpp @@ -1,21 +1,21 @@ /***************************************************************************** -* -* 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.txt for a list of people who contributed. -* See LICENSE_DANSE.txt for license information. -* -****************************************************************************** -* -* class Attributes - interface for calling setter and getter methods using -* their string names. -* -*****************************************************************************/ + * + * 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.txt for a list of people who contributed. + * See LICENSE_DANSE.txt for license information. + * + ****************************************************************************** + * + * class Attributes - interface for calling setter and getter methods using + * their string names. + * + *****************************************************************************/ #ifndef ATTRIBUTES_HPP_INCLUDED #define ATTRIBUTES_HPP_INCLUDED @@ -37,39 +37,30 @@ class Attributes; /// @brief custom exception for Attributes-related errors. This is /// thrown for invalid names or for attempts to set a read-only attribute. -class DoubleAttributeError : public std::runtime_error -{ - public: - DoubleAttributeError(const std::string msg="") : - std::runtime_error(msg) - { } +class DoubleAttributeError : public std::runtime_error { + public: + DoubleAttributeError(const std::string msg = "") : std::runtime_error(msg) {} }; /// @class BaseDoubleAttribute /// @brief abstract base class for accessing a particular double attribute -class DLL_EXPORT BaseDoubleAttribute -{ - public: - - virtual ~BaseDoubleAttribute() { } - virtual double getValue(const Attributes* obj) const = 0; - virtual void setValue(Attributes* obj, double value) = 0; - virtual bool isreadonly() const = 0; +class DLL_EXPORT BaseDoubleAttribute { + public: + virtual ~BaseDoubleAttribute() {} + virtual double getValue(const Attributes* obj) const = 0; + virtual void setValue(Attributes* obj, double value) = 0; + virtual bool isreadonly() const = 0; }; +class BaseAttributesVisitor { + public: + virtual ~BaseAttributesVisitor() {} -class BaseAttributesVisitor -{ - public: - - virtual ~BaseAttributesVisitor() { } - - virtual void visit(const Attributes& a) = 0; - virtual void visit(Attributes& a) - { - visit(static_cast(a)); - } + virtual void visit(const Attributes& a) = 0; + virtual void visit(Attributes& a) { + visit(static_cast(a)); + } }; /// @class Attributes @@ -77,124 +68,106 @@ class BaseAttributesVisitor /// should derive from Attributes and register their setter and /// getter methods in their constructors. -class DLL_EXPORT Attributes -{ - public: - - // class is virtual - virtual ~Attributes() { } - - // assignments in derived classes should not change mdoubleattrs - // mdoubleattrs should be only changed via registerDoubleAttribute - Attributes& operator=(const Attributes& other) { return *this; } - - // methods - double getDoubleAttr(const std::string& name) const; - void setDoubleAttr(const std::string& name, double value); - bool hasDoubleAttr(const std::string& name) const; - std::set namesOfDoubleAttributes() const; - std::set namesOfWritableDoubleAttributes() const; - // visitors - virtual void accept(BaseAttributesVisitor& v) { v.visit(*this); } - virtual void accept(BaseAttributesVisitor& v) const { v.visit(*this); } - - protected: - - friend DLL_EXPORT void registerBaseDoubleAttribute(Attributes*, - const std::string&, attributes::BaseDoubleAttribute* pa); - template - void registerDoubleAttribute(const std::string& name, T* obj, Getter); - template - void registerDoubleAttribute(const std::string& name, T* obj, Getter, Setter); - - private: - - // types - typedef std::map > - DoubleAttributeStorage; - // data - DoubleAttributeStorage mdoubleattrs; - - // methods - void checkAttributeName(const std::string& name) const; - - // visitor classes - - class CountDoubleAttrVisitor : public BaseAttributesVisitor - { - public: - - CountDoubleAttrVisitor(const std::string& name); - virtual void visit(const Attributes& a); - int count() const; - - private: - - // data - const std::string& mname; - int mcount; - }; - - - class GetDoubleAttrVisitor : public BaseAttributesVisitor - { - public: - - GetDoubleAttrVisitor(const std::string& name); - virtual void visit(const Attributes& a); - double getValue() const; - - private: - - // data - const std::string& mname; - double mvalue; - }; - - - class SetDoubleAttrVisitor : public BaseAttributesVisitor - { - public: - - SetDoubleAttrVisitor(const std::string& name, double value); - virtual void visit(const Attributes& a); - virtual void visit(Attributes& a); - - private: - - // data - const std::string& mname; - double mvalue; - }; - - - class NamesOfDoubleAttributesVisitor : public BaseAttributesVisitor - { - public: - - NamesOfDoubleAttributesVisitor(bool excludereadonly); - virtual void visit(const Attributes& a); - const std::set& names() const; - - private: - - // data - bool mexcludereadonly; - std::set mnames; - }; +class DLL_EXPORT Attributes { + public: + // class is virtual + virtual ~Attributes() {} + + // assignments in derived classes should not change mdoubleattrs + // mdoubleattrs should be only changed via registerDoubleAttribute + Attributes& operator=(const Attributes& other) { return *this; } + + // methods + double getDoubleAttr(const std::string& name) const; + void setDoubleAttr(const std::string& name, double value); + bool hasDoubleAttr(const std::string& name) const; + std::set namesOfDoubleAttributes() const; + std::set namesOfWritableDoubleAttributes() const; + // visitors + virtual void accept(BaseAttributesVisitor& v) { v.visit(*this); } + virtual void accept(BaseAttributesVisitor& v) const { v.visit(*this); } + + protected: + friend DLL_EXPORT void registerBaseDoubleAttribute( + Attributes*, const std::string&, attributes::BaseDoubleAttribute* pa); + template + void registerDoubleAttribute(const std::string& name, T* obj, Getter); + template + void registerDoubleAttribute(const std::string& name, T* obj, Getter, Setter); + + private: + // types + typedef std::map > + DoubleAttributeStorage; + // data + DoubleAttributeStorage mdoubleattrs; + + // methods + void checkAttributeName(const std::string& name) const; + + // visitor classes + + class CountDoubleAttrVisitor : public BaseAttributesVisitor { + public: + CountDoubleAttrVisitor(const std::string& name); + virtual void visit(const Attributes& a); + int count() const; + + private: + // data + const std::string& mname; + int mcount; + }; + + class GetDoubleAttrVisitor : public BaseAttributesVisitor { + public: + GetDoubleAttrVisitor(const std::string& name); + virtual void visit(const Attributes& a); + double getValue() const; + + private: + // data + const std::string& mname; + double mvalue; + }; + + class SetDoubleAttrVisitor : public BaseAttributesVisitor { + public: + SetDoubleAttrVisitor(const std::string& name, double value); + virtual void visit(const Attributes& a); + virtual void visit(Attributes& a); + + private: + // data + const std::string& mname; + double mvalue; + }; + + class NamesOfDoubleAttributesVisitor : public BaseAttributesVisitor { + public: + NamesOfDoubleAttributesVisitor(bool excludereadonly); + virtual void visit(const Attributes& a); + const std::set& names() const; + + private: + // data + bool mexcludereadonly; + std::set mnames; + }; }; // class Attributes // non-member helpers DLL_EXPORT void registerBaseDoubleAttribute(Attributes* obj, - const std::string& name, BaseDoubleAttribute* pa); + const std::string& name, + BaseDoubleAttribute* pa); DLL_EXPORT void throwDoubleAttributeReadOnly(); -} // namespace attributes -} // namespace diffpy +} // namespace attributes +} // namespace diffpy // Implementation ------------------------------------------------------------ @@ -202,8 +175,8 @@ DLL_EXPORT void throwDoubleAttributeReadOnly(); // make selected classes visible in diffpy namespace namespace diffpy { - using attributes::Attributes; - using attributes::BaseAttributesVisitor; -} +using attributes::Attributes; +using attributes::BaseAttributesVisitor; +} // namespace diffpy #endif // ATTRIBUTES_HPP_INCLUDED diff --git a/src/diffpy/EventTicker.cpp b/src/diffpy/EventTicker.cpp index 5caf20fb..7da4f04a 100644 --- a/src/diffpy/EventTicker.cpp +++ b/src/diffpy/EventTicker.cpp @@ -1,24 +1,24 @@ /***************************************************************************** -* -* libdiffpy Complex Modeling Initiative -* (c) 2013 Brookhaven Science Associates, -* Brookhaven National Laboratory. -* All rights reserved. -* -* File coded by: Pavol Juhas -* -* See AUTHORS.txt for a list of people who contributed. -* See LICENSE.txt for license information. -* -****************************************************************************** -* -* class EventTicker for managing modification times of dependent objects. -* -* This is used to determine if cached values need update, due to a change -* of their dependencies. The EventTicker class is inspired by the -* ObjCryst::RefinableObjClock class. -* -*****************************************************************************/ + * + * libdiffpy Complex Modeling Initiative + * (c) 2013 Brookhaven Science Associates, + * Brookhaven National Laboratory. + * All rights reserved. + * + * File coded by: Pavol Juhas + * + * See AUTHORS.txt for a list of people who contributed. + * See LICENSE.txt for license information. + * + ****************************************************************************** + * + * class EventTicker for managing modification times of dependent objects. + * + * This is used to determine if cached values need update, due to a change + * of their dependencies. The EventTicker class is inspired by the + * ObjCryst::RefinableObjClock class. + * + *****************************************************************************/ #include #include @@ -32,77 +32,55 @@ namespace eventticker { // Constructor --------------------------------------------------------------- -EventTicker::EventTicker() : mtick(0, 0) -{ } +EventTicker::EventTicker() : mtick(0, 0) {} // Public Methods ------------------------------------------------------------ -void EventTicker::click() -{ - ++gtick.second; - if (0 >= gtick.second) - { - ++gtick.first; - gtick.second = 0; - } - mtick = gtick; +void EventTicker::click() { + ++gtick.second; + if (0 >= gtick.second) { + ++gtick.first; + gtick.second = 0; + } + mtick = gtick; } - -void EventTicker::updateFrom(const EventTicker& other) -{ - if (other > *this) mtick = other.mtick; +void EventTicker::updateFrom(const EventTicker& other) { + if (other > *this) mtick = other.mtick; } +EventTicker::value_type EventTicker::value() const { return mtick; } -EventTicker::value_type EventTicker::value() const -{ - return mtick; +bool EventTicker::operator<(const EventTicker& other) const { + return mtick < other.mtick; } - -bool EventTicker::operator<(const EventTicker& other) const -{ - return mtick < other.mtick; -} - - -bool EventTicker::operator<=(const EventTicker& other) const -{ - return mtick <= other.mtick; +bool EventTicker::operator<=(const EventTicker& other) const { + return mtick <= other.mtick; } - -bool EventTicker::operator>(const EventTicker& other) const -{ - return mtick > other.mtick; +bool EventTicker::operator>(const EventTicker& other) const { + return mtick > other.mtick; } - -bool EventTicker::operator>=(const EventTicker& other) const -{ - return mtick >= other.mtick; +bool EventTicker::operator>=(const EventTicker& other) const { + return mtick >= other.mtick; } - -bool EventTicker::operator==(const EventTicker& other) const -{ - return mtick == other.mtick; +bool EventTicker::operator==(const EventTicker& other) const { + return mtick == other.mtick; } - -bool EventTicker::operator!=(const EventTicker& other) const -{ - return mtick != other.mtick; +bool EventTicker::operator!=(const EventTicker& other) const { + return mtick != other.mtick; } - // Static Global Data -------------------------------------------------------- EventTicker::value_type EventTicker::gtick(0, 0); -} // namespace eventticker -} // namespace diffpy +} // namespace eventticker +} // namespace diffpy // Serialization ------------------------------------------------------------- diff --git a/src/diffpy/EventTicker.hpp b/src/diffpy/EventTicker.hpp index c0024a9d..ec7cef36 100644 --- a/src/diffpy/EventTicker.hpp +++ b/src/diffpy/EventTicker.hpp @@ -1,24 +1,24 @@ /***************************************************************************** -* -* libdiffpy Complex Modeling Initiative -* (c) 2013 Brookhaven Science Associates, -* Brookhaven National Laboratory. -* All rights reserved. -* -* File coded by: Pavol Juhas -* -* See AUTHORS.txt for a list of people who contributed. -* See LICENSE.txt for license information. -* -****************************************************************************** -* -* class EventTicker for managing modification times of dependent objects. -* -* This is used to determine if cached values need update, due to a change -* of their dependencies. The EventTicker class is inspired by the -* ObjCryst::RefinableObjClock class. -* -*****************************************************************************/ + * + * libdiffpy Complex Modeling Initiative + * (c) 2013 Brookhaven Science Associates, + * Brookhaven National Laboratory. + * All rights reserved. + * + * File coded by: Pavol Juhas + * + * See AUTHORS.txt for a list of people who contributed. + * See LICENSE.txt for license information. + * + ****************************************************************************** + * + * class EventTicker for managing modification times of dependent objects. + * + * This is used to determine if cached values need update, due to a change + * of their dependencies. The EventTicker class is inspired by the + * ObjCryst::RefinableObjClock class. + * + *****************************************************************************/ #ifndef EVENTTICKER_HPP_INCLUDED #define EVENTTICKER_HPP_INCLUDED @@ -31,65 +31,60 @@ namespace diffpy { namespace eventticker { -class DLL_EXPORT EventTicker -{ - public: - - // constructor - EventTicker(); - - // types - typedef std::pair value_type; - - // methods - /// record an event and advance the global ticker. - void click(); - /// advance internal ticker if the other ticker is newer. - void updateFrom(const EventTicker& other); - /// return the internal ticker value - value_type value() const; - - // comparison - bool operator<(const EventTicker&) const; - bool operator<=(const EventTicker&) const; - bool operator>(const EventTicker&) const; - bool operator>=(const EventTicker&) const; - bool operator==(const EventTicker&) const; - bool operator!=(const EventTicker&) const; - - private: - - // global counter - static value_type gtick; - - // data - value_type mtick; - - // serialization - friend class boost::serialization::access; - template - void save(Archive& ar, const unsigned int version) const - { - ar << mtick << gtick; - } - - template - void load(Archive& ar, const unsigned int version) - { - value_type ga; - ar >> mtick >> ga; - if (ga > gtick) - { - if (ga.first != gtick.first) mtick.first = mtick.second = 0; - else mtick.second += gtick.second - ga.second; - } - } - - BOOST_SERIALIZATION_SPLIT_MEMBER() - +class DLL_EXPORT EventTicker { + public: + // constructor + EventTicker(); + + // types + typedef std::pair value_type; + + // methods + /// record an event and advance the global ticker. + void click(); + /// advance internal ticker if the other ticker is newer. + void updateFrom(const EventTicker& other); + /// return the internal ticker value + value_type value() const; + + // comparison + bool operator<(const EventTicker&) const; + bool operator<=(const EventTicker&) const; + bool operator>(const EventTicker&) const; + bool operator>=(const EventTicker&) const; + bool operator==(const EventTicker&) const; + bool operator!=(const EventTicker&) const; + + private: + // global counter + static value_type gtick; + + // data + value_type mtick; + + // serialization + friend class boost::serialization::access; + template + void save(Archive& ar, const unsigned int version) const { + ar << mtick << gtick; + } + + template + void load(Archive& ar, const unsigned int version) { + value_type ga; + ar >> mtick >> ga; + if (ga > gtick) { + if (ga.first != gtick.first) + mtick.first = mtick.second = 0; + else + mtick.second += gtick.second - ga.second; + } + } + + BOOST_SERIALIZATION_SPLIT_MEMBER() }; -} // namespace eventticker -} // namespace diffpy +} // namespace eventticker +} // namespace diffpy #endif // EVENTTICKER_HPP_INCLUDED diff --git a/src/diffpy/Export.hpp b/src/diffpy/Export.hpp index 142b61fb..a998ff22 100644 --- a/src/diffpy/Export.hpp +++ b/src/diffpy/Export.hpp @@ -1,11 +1,11 @@ #pragma once #if defined(_WIN32) - #if defined(BUILD_DLL) - #define DLL_EXPORT __declspec(dllexport) - #else - #define DLL_EXPORT __declspec(dllimport) - #endif +#if defined(BUILD_DLL) +#define DLL_EXPORT __declspec(dllexport) #else - #define DLL_EXPORT +#define DLL_EXPORT __declspec(dllimport) +#endif +#else +#define DLL_EXPORT #endif diff --git a/src/diffpy/HasClassRegistry.hpp b/src/diffpy/HasClassRegistry.hpp index b3f2a55a..2b94e753 100644 --- a/src/diffpy/HasClassRegistry.hpp +++ b/src/diffpy/HasClassRegistry.hpp @@ -1,28 +1,28 @@ /***************************************************************************** -* -* 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.txt for a list of people who contributed. -* See LICENSE_DANSE.txt for license information. -* -****************************************************************************** -* -* class HasClassRegistry -- template class providing registry for factory -* functions that produce derived classes. To be used as a base for a -* specific class branch. For a proper function, the HasClassRegistry -* template must be instantiated in just one cpp file that includes the -* implementatios in HasClassRegistry.ipp. -* -* A Base class with registry should be declared as -* class Base : public HasClassRegistry -* The derived classes need to implement create, clone and type methods. -* -*****************************************************************************/ + * + * 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.txt for a list of people who contributed. + * See LICENSE_DANSE.txt for license information. + * + ****************************************************************************** + * + * class HasClassRegistry -- template class providing registry for factory + * functions that produce derived classes. To be used as a base for a + * specific class branch. For a proper function, the HasClassRegistry + * template must be instantiated in just one cpp file that includes the + * implementations in HasClassRegistry.ipp. + * + * A Base class with registry should be declared as + * class Base : public HasClassRegistry + * The derived classes need to implement create, clone and type methods. + * + *****************************************************************************/ #ifndef HASCLASSREGISTRY_HPP_INCLUDED #define HASCLASSREGISTRY_HPP_INCLUDED @@ -37,63 +37,59 @@ namespace diffpy { template -class HasClassRegistry -{ - public: +class HasClassRegistry { + public: + // types + typedef boost::shared_ptr SharedPtr; - // types - typedef boost::shared_ptr SharedPtr; + // Contains virtual methods + virtual ~HasClassRegistry() {} - // Contains virtual methods - virtual ~HasClassRegistry() { } + // methods that have to be implemented in the TBase-derived classes + virtual SharedPtr create() const = 0; + virtual SharedPtr clone() const = 0; + virtual const std::string& type() const = 0; - // methods that have to be implemented in the TBase-derived classes - virtual SharedPtr create() const = 0; - virtual SharedPtr clone() const = 0; - virtual const std::string& type() const = 0; + // methods provided by HasClassRegistry - // methods provided by HasClassRegistry + /// Add a prototype of this instance to the registry. + virtual bool registerThisType() const; - /// Add a prototype of this instance to the registry. - virtual bool registerThisType() const; + /// Make registered type tp available under a different alias. + static bool aliasType(const std::string& tp, const std::string& al); - /// Make registered type tp available under a different alias. - static bool aliasType(const std::string& tp, const std::string& al); + /// Clear registration of the specified type under its standard name + /// or any alias. Return the number of unset names and aliases or + /// 0 if the specified string type was not registered. + static int deregisterType(const std::string& tp); - /// Clear registration of the specified type under its standard name - /// or any alias. Return the number of unset names and aliases or - /// 0 if the specifed string type was not registered. - static int deregisterType(const std::string& tp); + /// Create new instance of a specified string type. + static SharedPtr createByType(const std::string& tp); - /// Create new instance of a specified string type. - static SharedPtr createByType(const std::string& tp); + /// Return true if string is a registered string type or its alias. + static bool isRegisteredType(const std::string& tp); - /// Return true if string is a registered string type or its alias. - static bool isRegisteredType(const std::string& tp); + /// Return a map of string aliases to standard type strings. + static std::map getAliasedTypes(); - /// Return a map of string aliases to standard type strings. - static std::map getAliasedTypes(); + /// Return a set of all registered string types + static std::set getRegisteredTypes(); - /// Return a set of all registered string types - static std::set getRegisteredTypes(); + protected: + /// Optional setup for the registered object. + /// + /// This is used for increasing reference count on Python-extended + /// classes, so they are not destroyed before destroying the C++ + /// object registry. + virtual void setupRegisteredObject(SharedPtr p) const {} - protected: + private: + typedef std::map RegistryStorage; - /// Optional setup for the registered object. - /// - /// This is used for increasing reference count on Python-extended - /// classes, so they are not destroyed before destroying the C++ - /// object registry. - virtual void setupRegisteredObject(SharedPtr p) const { } - - private: - - typedef std::map RegistryStorage; - - /// Return a singleton instance of internal registry - static RegistryStorage& getRegistry(); + /// Return a singleton instance of internal registry + static RegistryStorage& getRegistry(); }; -} // namespace diffpy +} // namespace diffpy #endif // HASCLASSREGISTRY_HPP_INCLUDED diff --git a/src/diffpy/mathutils.hpp b/src/diffpy/mathutils.hpp index f8e8ffec..3713bb98 100644 --- a/src/diffpy/mathutils.hpp +++ b/src/diffpy/mathutils.hpp @@ -1,20 +1,20 @@ /***************************************************************************** -* -* 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.txt for a list of people who contributed. -* See LICENSE_DANSE.txt for license information. -* -****************************************************************************** -* -* Various common mathematical constants and functions. -* -*****************************************************************************/ + * + * 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.txt for a list of people who contributed. + * See LICENSE_DANSE.txt for license information. + * + ****************************************************************************** + * + * Various common mathematical constants and functions. + * + *****************************************************************************/ #ifndef MATHUTILS_HPP_INCLUDED #define MATHUTILS_HPP_INCLUDED @@ -43,68 +43,56 @@ double asind(double x); // round-off aware comparison operations -bool eps_eq(const double& x, const double& y, double eps=SQRT_DOUBLE_EPS); -bool eps_gt(const double& x, const double& y, double eps=SQRT_DOUBLE_EPS); -bool eps_lt(const double& x, const double& y, double eps=SQRT_DOUBLE_EPS); +bool eps_eq(const double& x, const double& y, double eps = SQRT_DOUBLE_EPS); +bool eps_gt(const double& x, const double& y, double eps = SQRT_DOUBLE_EPS); +bool eps_lt(const double& x, const double& y, double eps = SQRT_DOUBLE_EPS); // binary functor for round-off aware less-than comparison -class EpsilonLess : public std::binary_function -{ - public: - - // constructor - EpsilonLess(double eps=SQRT_DOUBLE_EPS) : meps(eps) { } - - // comparison method - bool operator()(const double& x, const double& y) const - { - return eps_lt(x, y, meps); - } - template - bool operator()(const Seq0& v0, const Seq1& v1) const - { - bool rv = std::lexicographical_compare( - v0.begin(), v0.end(), - v1.begin(), v1.end(), *this); - return rv; - } - - private: - - double meps; +class EpsilonLess : public std::binary_function { + public: + // constructor + EpsilonLess(double eps = SQRT_DOUBLE_EPS) : meps(eps) {} + + // comparison method + bool operator()(const double& x, const double& y) const { + return eps_lt(x, y, meps); + } + template + bool operator()(const Seq0& v0, const Seq1& v1) const { + bool rv = std::lexicographical_compare(v0.begin(), v0.end(), v1.begin(), + v1.end(), *this); + return rv; + } + + private: + double meps; }; // binary functor for round-off aware equality comparison -class EpsilonEqual : public std::binary_function -{ - public: - - // constructor - EpsilonEqual(double eps=SQRT_DOUBLE_EPS) : meps(eps) { } - - // comparison method - bool operator()(const double& x, const double& y) const - { - return eps_eq(x, y, meps); - } - template - bool operator()(const Seq0& v0, const Seq1& v1) const - { - bool rv = (v0.size() == v1.size()) && - std::equal(v0.begin(), v0.end(), v1.begin(), *this); - return rv; - } - - private: - - double meps; +class EpsilonEqual : public std::binary_function { + public: + // constructor + EpsilonEqual(double eps = SQRT_DOUBLE_EPS) : meps(eps) {} + + // comparison method + bool operator()(const double& x, const double& y) const { + return eps_eq(x, y, meps); + } + template + bool operator()(const Seq0& v0, const Seq1& v1) const { + bool rv = (v0.size() == v1.size()) && + std::equal(v0.begin(), v0.end(), v1.begin(), *this); + return rv; + } + + private: + double meps; }; - -} // namespace mathutils -} // namespace diffpy +} // namespace mathutils +} // namespace diffpy // Implementation ------------------------------------------------------------ diff --git a/src/diffpy/runtimepath.cpp b/src/diffpy/runtimepath.cpp index 0f7299ee..93337c0c 100644 --- a/src/diffpy/runtimepath.cpp +++ b/src/diffpy/runtimepath.cpp @@ -1,20 +1,20 @@ /***************************************************************************** -* -* libdiffpy Complex Modeling Initiative -* (c) 2013 Brookhaven Science Associates, -* Brookhaven National Laboratory. -* All rights reserved. -* -* File coded by: Pavol Juhas -* -* See AUTHORS.txt for a list of people who contributed. -* See LICENSE.txt for license information. -* -****************************************************************************** -* -* Functions for resolving paths to static data files at runtime. -* -*****************************************************************************/ + * + * libdiffpy Complex Modeling Initiative + * (c) 2013 Brookhaven Science Associates, + * Brookhaven National Laboratory. + * All rights reserved. + * + * File coded by: Pavol Juhas + * + * See AUTHORS.txt for a list of people who contributed. + * See LICENSE.txt for license information. + * + ****************************************************************************** + * + * Functions for resolving paths to static data files at runtime. + * + *****************************************************************************/ #include #include @@ -23,7 +23,7 @@ #include #include #include -#include // not available on Windows, conda install dlfcn-win32 +#include // not available on Windows, conda install dlfcn-win32 // libgen.h is not available on Windows #ifdef _WIN32 @@ -31,21 +31,19 @@ #include // A simple replacement for dirname() on Windows. -inline std::string win_dirname(const std::string& path) -{ - // Find the last occurrence of either '\' or '/' - size_t pos = path.find_last_of("\\/"); - if (pos == std::string::npos) - return "."; // No directory found, return current directory - return path.substr(0, pos); +inline std::string win_dirname(const std::string& path) { + // Find the last occurrence of either '\' or '/' + size_t pos = path.find_last_of("\\/"); + if (pos == std::string::npos) + return "."; // No directory found, return current directory + return path.substr(0, pos); } // Overload to match the expected C-style interface if needed: -inline char* dirname(char* path) -{ - static std::string dir; - dir = win_dirname(std::string(path)); - return const_cast(dir.c_str()); +inline char* dirname(char* path) { + static std::string dir; + dir = win_dirname(std::string(path)); + return const_cast(dir.c_str()); } #else #include @@ -74,7 +72,6 @@ inline char* dirname(char* path) #define realpath(N, R) _fullpath((R), (N), PATH_MAX) #endif - #include #include #define STRINGIFY(m) STRINGIFY_BRAIN_DAMAGE(m) @@ -88,166 +85,138 @@ namespace { const char* runtimerelpath = #ifdef DIFFPYRUNTIMERELPATH - STRINGIFY(DIFFPYRUNTIMERELPATH) + STRINGIFY(DIFFPYRUNTIMERELPATH) #else - "../share/diffpy/libdiffpy-" - STRINGIFY(DIFFPY_VERSION_MAJOR) - "." - STRINGIFY(DIFFPY_VERSION_MINOR) + "../share/diffpy/libdiffpy-" STRINGIFY(DIFFPY_VERSION_MAJOR) "." STRINGIFY( + DIFFPY_VERSION_MINOR) #endif -; + ; -bool isdir(const string& d) -{ - struct stat sd; - return (stat(d.c_str(), &sd) == 0 && S_ISDIR(sd.st_mode)); +bool isdir(const string& d) { + struct stat sd; + return (stat(d.c_str(), &sd) == 0 && S_ISDIR(sd.st_mode)); } - /// throw runtime error if directory does not exist. -void ensureIsDir(const std::string& d) -{ - if (isdir(d)) return; - string emsg("Directory '@D' does not exist."); - emsg = emsg.replace(emsg.find_first_of('@'), 2, d); - throw runtime_error(emsg); +void ensureIsDir(const std::string& d) { + if (isdir(d)) return; + string emsg("Directory '@D' does not exist."); + emsg = emsg.replace(emsg.find_first_of('@'), 2, d); + throw runtime_error(emsg); } - -const string& diffpyruntime() -{ - char fpb[PATH_MAX]; - static string librt; - static bool did_librt = false; - static string envrt; - static bool did_envrt = false; - // check the DIFFPYRUNTIME environment variable. - char* pe = getenv("DIFFPYRUNTIME"); - if (pe && *pe != '\0') - { - static string dprt; - did_envrt = did_envrt && (dprt == pe); - if (!did_envrt) - { - dprt = pe; - envrt = pe; - size_t pt = envrt.find_last_not_of('/'); - if (pt == string::npos) envrt = envrt.substr(0, 1); - else envrt.erase(pt + 1); - ensureIsDir(envrt); - envrt = realpath(envrt.c_str(), fpb); - did_envrt = true; - } - return envrt; - } - if (did_librt) return librt; - // here we need to resolve path of the libdiffpy shared library - Dl_info i; - dladdr(reinterpret_cast(diffpyruntime), &i); - // first candidate is resolved in relative data path - strncpy(fpb, i.dli_fname, PATH_MAX); - string d1 = string(dirname(fpb)) + "/" + runtimerelpath; - if (isdir(d1)) - { - librt = realpath(d1.c_str(), fpb); - did_librt = true; - return librt; +const string& diffpyruntime() { + char fpb[PATH_MAX]; + static string librt; + static bool did_librt = false; + static string envrt; + static bool did_envrt = false; + // check the DIFFPYRUNTIME environment variable. + char* pe = getenv("DIFFPYRUNTIME"); + if (pe && *pe != '\0') { + static string dprt; + did_envrt = did_envrt && (dprt == pe); + if (!did_envrt) { + dprt = pe; + envrt = pe; + size_t pt = envrt.find_last_not_of('/'); + if (pt == string::npos) + envrt = envrt.substr(0, 1); + else + envrt.erase(pt + 1); + ensureIsDir(envrt); + envrt = realpath(envrt.c_str(), fpb); + did_envrt = true; } - // second candidate is resolved with respect to physical library location - // according to the source tree layout - string d2 = dirname(realpath(i.dli_fname, fpb)); - d2 += "/../../src/runtime"; - if (isdir(d2)) - { - librt = realpath(d2.c_str(), fpb); - did_librt = true; - return librt; - } - // nothing worked - throw exception about the first candidate path. - ensureIsDir(d1); + return envrt; + } + if (did_librt) return librt; + // here we need to resolve path of the libdiffpy shared library + Dl_info i; + dladdr(reinterpret_cast(diffpyruntime), &i); + // first candidate is resolved in relative data path + strncpy(fpb, i.dli_fname, PATH_MAX); + string d1 = string(dirname(fpb)) + "/" + runtimerelpath; + if (isdir(d1)) { + librt = realpath(d1.c_str(), fpb); + did_librt = true; + return librt; + } + // second candidate is resolved with respect to physical library location + // according to the source tree layout + string d2 = dirname(realpath(i.dli_fname, fpb)); + d2 += "/../../src/runtime"; + if (isdir(d2)) { + librt = realpath(d2.c_str(), fpb); + did_librt = true; return librt; + } + // nothing worked - throw exception about the first candidate path. + ensureIsDir(d1); + return librt; } -} // namespace +} // namespace // Implementation ------------------------------------------------------------ namespace diffpy { namespace runtimepath { -string datapath(const std::string& f) -{ - string rv = diffpyruntime(); - rv += (f.empty() ? "" : "/") + f; - return rv; +string datapath(const std::string& f) { + string rv = diffpyruntime(); + rv += (f.empty() ? "" : "/") + f; + return rv; } // class LineReader ---------------------------------------------------------- // Constructor -LineReader::LineReader() : - lineno(0), - commentmark("#") -{ } +LineReader::LineReader() : lineno(0), commentmark("#") {} // Methods -bool LineReader::isignored() const -{ - return words.empty() || this->iscomment(); +bool LineReader::isignored() const { + return words.empty() || this->iscomment(); } - -bool LineReader::iscomment() const -{ - bool rv = !commentmark.empty() && !words.empty() && +bool LineReader::iscomment() const { + bool rv = !commentmark.empty() && !words.empty() && (0 == words[0].compare(0, commentmark.size(), commentmark)); - return rv; + return rv; } +bool LineReader::isblank() const { return words.empty(); } -bool LineReader::isblank() const -{ - return words.empty(); -} - +size_t LineReader::wcount() const { return words.size(); } -size_t LineReader::wcount() const -{ - return words.size(); -} - - -runtime_error LineReader::format_error( - const string& filename, const string& edetail) -{ - ostringstream emsg; - emsg << "Invalid data format in " << - filename << " line " << this->lineno << '.'; - if (!edetail.empty()) emsg << '\n' << edetail; - return runtime_error(emsg.str()); +runtime_error LineReader::format_error(const string& filename, + const string& edetail) { + ostringstream emsg; + emsg << "Invalid data format in " << filename << " line " << this->lineno + << '.'; + if (!edetail.empty()) emsg << '\n' << edetail; + return runtime_error(emsg.str()); } // Non-member operators -istream& operator>> (istream& fp, LineReader& line) -{ - getline(fp, line.line); - if (fp) line.lineno++; - istringstream fpline(line.line); - line.words.clear(); - string w; - if (line.separator.empty()) { - while (fpline >> w) line.words.push_back(w); - } - else { - assert(line.separator.size() == 1); - const char& sep = line.separator[0]; - while (getline(fpline, w, sep)) line.words.push_back(w); - } - return fp; +istream& operator>>(istream& fp, LineReader& line) { + getline(fp, line.line); + if (fp) line.lineno++; + istringstream fpline(line.line); + line.words.clear(); + string w; + if (line.separator.empty()) { + while (fpline >> w) line.words.push_back(w); + } else { + assert(line.separator.size() == 1); + const char& sep = line.separator[0]; + while (getline(fpline, w, sep)) line.words.push_back(w); + } + return fp; } -} // namespace runtimepath -} // namespace diffpy +} // namespace runtimepath +} // namespace diffpy diff --git a/src/diffpy/runtimepath.hpp b/src/diffpy/runtimepath.hpp index fc188f1f..f458f15d 100644 --- a/src/diffpy/runtimepath.hpp +++ b/src/diffpy/runtimepath.hpp @@ -1,20 +1,20 @@ /***************************************************************************** -* -* libdiffpy Complex Modeling Initiative -* (c) 2013 Brookhaven Science Associates, -* Brookhaven National Laboratory. -* All rights reserved. -* -* File coded by: Pavol Juhas -* -* See AUTHORS.txt for a list of people who contributed. -* See LICENSE.txt for license information. -* -****************************************************************************** -* -* Functions for resolving paths to static data files at runtime. -* -*****************************************************************************/ + * + * libdiffpy Complex Modeling Initiative + * (c) 2013 Brookhaven Science Associates, + * Brookhaven National Laboratory. + * All rights reserved. + * + * File coded by: Pavol Juhas + * + * See AUTHORS.txt for a list of people who contributed. + * See LICENSE.txt for license information. + * + ****************************************************************************** + * + * Functions for resolving paths to static data files at runtime. + * + *****************************************************************************/ #ifndef RUNTIMEPATH_HPP_INCLUDED #define RUNTIMEPATH_HPP_INCLUDED @@ -40,32 +40,31 @@ namespace runtimepath { std::string datapath(const std::string& f); /// Helper class for loading text data -class LineReader -{ - public: - // constructor - LineReader(); +class LineReader { + public: + // constructor + LineReader(); - // methods - bool isignored() const; - bool iscomment() const; - bool isblank() const; - size_t wcount() const; - std::runtime_error format_error( - const std::string& filename, const std::string& edetail); + // methods + bool isignored() const; + bool iscomment() const; + bool isblank() const; + size_t wcount() const; + std::runtime_error format_error(const std::string& filename, + const std::string& edetail); - // data - int lineno; - std::string commentmark; - std::string separator; - std::string line; - std::vector words; + // data + int lineno; + std::string commentmark; + std::string separator; + std::string line; + std::vector words; }; // non-member functions for the LineReader class -std::istream& operator>> (std::istream&, LineReader&); +std::istream& operator>>(std::istream&, LineReader&); -} // namespace runtimepath -} // namespace diffpy +} // namespace runtimepath +} // namespace diffpy #endif // RUNTIMEPATH_HPP_INCLUDED diff --git a/src/diffpy/serialization.hpp b/src/diffpy/serialization.hpp index 3c8f9015..09dbba59 100644 --- a/src/diffpy/serialization.hpp +++ b/src/diffpy/serialization.hpp @@ -1,26 +1,26 @@ /***************************************************************************** -* -* 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.txt for a list of people who contributed. -* See LICENSE_DANSE.txt for license information. -* -****************************************************************************** -* -* Declaration of template functions: -* diffpy::serialization_tostring -* diffpy::serialization_fromstring -* -* Shared definitions of serialization archive types: -* diffpy::serialization::iarchive -* diffpy::serialization::oarchive -* -*****************************************************************************/ + * + * 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.txt for a list of people who contributed. + * See LICENSE_DANSE.txt for license information. + * + ****************************************************************************** + * + * Declaration of template functions: + * diffpy::serialization_tostring + * diffpy::serialization_fromstring + * + * Shared definitions of serialization archive types: + * diffpy::serialization::iarchive + * diffpy::serialization::oarchive + * + *****************************************************************************/ #ifndef SERIALIZATION_HPP_INCLUDED #define SERIALIZATION_HPP_INCLUDED @@ -42,8 +42,8 @@ namespace serialization { typedef ::boost::archive::binary_iarchive iarchive; typedef ::boost::archive::binary_oarchive oarchive; -} // namespace serialization -} // namespace diffpy +} // namespace serialization +} // namespace diffpy // Pull in the inline definitions and macro logic #include "serialization.ipp" diff --git a/src/diffpy/srreal/AtomRadiiTable.cpp b/src/diffpy/srreal/AtomRadiiTable.cpp index ba338350..8cea5315 100644 --- a/src/diffpy/srreal/AtomRadiiTable.cpp +++ b/src/diffpy/srreal/AtomRadiiTable.cpp @@ -1,20 +1,20 @@ /***************************************************************************** -* -* 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.txt for a list of people who contributed. -* See LICENSE_DANSE.txt for license information. -* -****************************************************************************** -* -* class AtomRadiiTable -- storage of empirical atomic radii -* -*****************************************************************************/ + * + * 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.txt for a list of people who contributed. + * See LICENSE_DANSE.txt for license information. + * + ****************************************************************************** + * + * class AtomRadiiTable -- storage of empirical atomic radii + * + *****************************************************************************/ #include #include @@ -37,97 +37,75 @@ using namespace std; // Public Methods ------------------------------------------------------------ -double AtomRadiiTable::lookup(const string& smbl) const -{ - CustomRadiiStorage::const_iterator tb; - tb = mcustomradius.find(smbl); - if (tb != mcustomradius.end()) return tb->second; - return this->standardLookup(smbl); +double AtomRadiiTable::lookup(const string& smbl) const { + CustomRadiiStorage::const_iterator tb; + tb = mcustomradius.find(smbl); + if (tb != mcustomradius.end()) return tb->second; + return this->standardLookup(smbl); } - -void AtomRadiiTable::setCustom(const string& smbl, double radius) -{ - mcustomradius[smbl] = radius; +void AtomRadiiTable::setCustom(const string& smbl, double radius) { + mcustomradius[smbl] = radius; } - -void AtomRadiiTable::fromString(const string& s) -{ - // create deblanked string - string s1; - remove_copy_if(s.begin(), s.end(), back_inserter(s1), ::isspace); - // replace commas with space so we can use the split function - replace(s1.begin(), s1.end(), ',', ' '); - istringstream ss1(s1); - CustomRadiiStorage rds; - for (string w; ss1 >> w;) - { - string::size_type p = w.find(':'); - if (p == string::npos) - { - ostringstream emsg; - emsg << "Invalid radius specification, missing ':' in '" << - w << "'."; - throw invalid_argument(emsg.str()); - } - string smbl = w.substr(0, p); - double value; - istringstream ssv(w.substr(p + 1)); - if (!(ssv >> value)) - { - ostringstream emsg; - emsg << "Invalid floating point number in '" << w << "'."; - throw invalid_argument(emsg.str()); - } - rds[smbl] = value; +void AtomRadiiTable::fromString(const string& s) { + // create deblanked string + string s1; + remove_copy_if(s.begin(), s.end(), back_inserter(s1), ::isspace); + // replace commas with space so we can use the split function + replace(s1.begin(), s1.end(), ',', ' '); + istringstream ss1(s1); + CustomRadiiStorage rds; + for (string w; ss1 >> w;) { + string::size_type p = w.find(':'); + if (p == string::npos) { + ostringstream emsg; + emsg << "Invalid radius specification, missing ':' in '" << w << "'."; + throw invalid_argument(emsg.str()); } - // everything worked up to here, we can do the assignment - CustomRadiiStorage::const_iterator kv = rds.begin(); - for (; kv != rds.end(); ++kv) mcustomradius[kv->first] = kv->second; -} - - -void AtomRadiiTable::resetCustom(const string& smbl) -{ - mcustomradius.erase(smbl); + string smbl = w.substr(0, p); + double value; + istringstream ssv(w.substr(p + 1)); + if (!(ssv >> value)) { + ostringstream emsg; + emsg << "Invalid floating point number in '" << w << "'."; + throw invalid_argument(emsg.str()); + } + rds[smbl] = value; + } + // everything worked up to here, we can do the assignment + CustomRadiiStorage::const_iterator kv = rds.begin(); + for (; kv != rds.end(); ++kv) mcustomradius[kv->first] = kv->second; } - -void AtomRadiiTable::resetAll() -{ - mcustomradius.clear(); +void AtomRadiiTable::resetCustom(const string& smbl) { + mcustomradius.erase(smbl); } +void AtomRadiiTable::resetAll() { mcustomradius.clear(); } -const AtomRadiiTable::CustomRadiiStorage& -AtomRadiiTable::getAllCustom() const -{ - return mcustomradius; +const AtomRadiiTable::CustomRadiiStorage& AtomRadiiTable::getAllCustom() const { + return mcustomradius; } - -string AtomRadiiTable::toString(string separator) const -{ - CustomRadiiStorage::const_iterator tb; - vector symbols; - for (tb = mcustomradius.begin(); tb != mcustomradius.end(); ++tb) - { - symbols.push_back(tb->first); - } - sort(symbols.begin(), symbols.end()); - ostringstream rv; - vector::const_iterator smbi; - for (smbi = symbols.begin(); smbi != symbols.end(); ++smbi) - { - if (smbi != symbols.begin()) rv << separator; - rv << *smbi << ':' << mcustomradius.at(*smbi); - } - return rv.str(); +string AtomRadiiTable::toString(string separator) const { + CustomRadiiStorage::const_iterator tb; + vector symbols; + for (tb = mcustomradius.begin(); tb != mcustomradius.end(); ++tb) { + symbols.push_back(tb->first); + } + sort(symbols.begin(), symbols.end()); + ostringstream rv; + vector::const_iterator smbi; + for (smbi = symbols.begin(); smbi != symbols.end(); ++smbi) { + if (smbi != symbols.begin()) rv << separator; + rv << *smbi << ':' << mcustomradius.at(*smbi); + } + return rv.str(); } -} // srreal -} // diffpy +} // namespace srreal +} // namespace diffpy // Serialization ------------------------------------------------------------- diff --git a/src/diffpy/srreal/AtomRadiiTable.hpp b/src/diffpy/srreal/AtomRadiiTable.hpp index 7eb17034..c1f09f34 100644 --- a/src/diffpy/srreal/AtomRadiiTable.hpp +++ b/src/diffpy/srreal/AtomRadiiTable.hpp @@ -1,24 +1,24 @@ /***************************************************************************** -* -* 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.txt for a list of people who contributed. -* See LICENSE_DANSE.txt for license information. -* -****************************************************************************** -* -* class AtomRadiiTable -- storage of empirical atomic radii -* This is an abstract base class that provides a createByType factory -* function for creating a registered concrete instances. -* The derived classes has to overload the standardLookup method and also -* create, clone and type methods from the HasClassRegistry base. -* -*****************************************************************************/ + * + * 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.txt for a list of people who contributed. + * See LICENSE_DANSE.txt for license information. + * + ****************************************************************************** + * + * class AtomRadiiTable -- storage of empirical atomic radii + * This is an abstract base class that provides a createByType factory + * function for creating a registered concrete instances. + * The derived classes has to overload the standardLookup method and also + * create, clone and type methods from the HasClassRegistry base. + * + *****************************************************************************/ #ifndef ATOMRADIITABLE_HPP_INCLUDED #define ATOMRADIITABLE_HPP_INCLUDED @@ -37,51 +37,46 @@ namespace diffpy { namespace srreal { -class DLL_EXPORT AtomRadiiTable : - public diffpy::HasClassRegistry -{ - public: - - // types - typedef std::unordered_map CustomRadiiStorage; - - // methods - /// fast value lookup, which does not change the table. - double lookup(const std::string& smbl) const; - /// overloadable lookup function that retrieved standard values - virtual double standardLookup(const std::string& smbl) const = 0; - /// set custom radius for a specified atom symbol - void setCustom(const std::string& smbl, double radius); - /// set custom radii from a string in (A1:r1, A2:r2, ...) format - void fromString(const std::string& s); - /// reset custom value for the specified atom type - void resetCustom(const std::string& smbl); - /// reset all custom values - void resetAll(); - /// return all custom radii defined in this table - const CustomRadiiStorage& getAllCustom() const; - /// convert all custom radii to a string in (A1:r1,A2:r2,...) format - std::string toString(std::string separator=",") const; - - private: - - // data - CustomRadiiStorage mcustomradius; - - // serialization - friend class boost::serialization::access; - template - void serialize(Archive& ar, const unsigned int version) - { - ar & mcustomradius; - } - +class DLL_EXPORT AtomRadiiTable + : public diffpy::HasClassRegistry { + public: + // types + typedef std::unordered_map CustomRadiiStorage; + + // methods + /// fast value lookup, which does not change the table. + double lookup(const std::string& smbl) const; + /// overloadable lookup function that retrieved standard values + virtual double standardLookup(const std::string& smbl) const = 0; + /// set custom radius for a specified atom symbol + void setCustom(const std::string& smbl, double radius); + /// set custom radii from a string in (A1:r1, A2:r2, ...) format + void fromString(const std::string& s); + /// reset custom value for the specified atom type + void resetCustom(const std::string& smbl); + /// reset all custom values + void resetAll(); + /// return all custom radii defined in this table + const CustomRadiiStorage& getAllCustom() const; + /// convert all custom radii to a string in (A1:r1,A2:r2,...) format + std::string toString(std::string separator = ",") const; + + private: + // data + CustomRadiiStorage mcustomradius; + + // serialization + friend class boost::serialization::access; + template + void serialize(Archive& ar, const unsigned int version) { + ar & mcustomradius; + } }; typedef AtomRadiiTable::SharedPtr AtomRadiiTablePtr; -} // namespace srreal -} // namespace diffpy +} // namespace srreal +} // namespace diffpy // Serialization ------------------------------------------------------------- diff --git a/src/diffpy/srreal/AtomUtils.cpp b/src/diffpy/srreal/AtomUtils.cpp index 37cd41ac..b76d5bf8 100644 --- a/src/diffpy/srreal/AtomUtils.cpp +++ b/src/diffpy/srreal/AtomUtils.cpp @@ -1,72 +1,64 @@ /***************************************************************************** -* -* libdiffpy Complex Modeling Initiative -* (c) 2013 Brookhaven Science Associates, -* Brookhaven National Laboratory. -* All rights reserved. -* -* File coded by: Pavol Juhas -* -* See AUTHORS.txt for a list of people who contributed. -* See LICENSE.txt for license information. -* -****************************************************************************** -* -* Small routines related to atom properties. -* -*****************************************************************************/ + * + * libdiffpy Complex Modeling Initiative + * (c) 2013 Brookhaven Science Associates, + * Brookhaven National Laboratory. + * All rights reserved. + * + * File coded by: Pavol Juhas + * + * See AUTHORS.txt for a list of people who contributed. + * See LICENSE.txt for license information. + * + ****************************************************************************** + * + * Small routines related to atom properties. + * + *****************************************************************************/ #include namespace diffpy { namespace srreal { -std::string atomBareSymbol(const std::string& atomtype) -{ - std::string::size_type pb, pe; - pb = atomtype.find_first_not_of("0123456789- \t"); - pe = atomtype.find_last_not_of("+-012345678 \t"); - std::string::size_type npos = std::string::npos; - std::string rv; - if (pb != npos && pe != npos) rv = atomtype.substr(pb, pe - pb + 1); - return rv; +std::string atomBareSymbol(const std::string& atomtype) { + std::string::size_type pb, pe; + pb = atomtype.find_first_not_of("0123456789- \t"); + pe = atomtype.find_last_not_of("+-012345678 \t"); + std::string::size_type npos = std::string::npos; + std::string rv; + if (pb != npos && pe != npos) rv = atomtype.substr(pb, pe - pb + 1); + return rv; } - /// Return valence of possibly ionic symbol such as "S2-" or "Cl-". -int atomValence(const std::string& atomtype) -{ - std::string::const_reverse_iterator ci; - int rv = 0; - for (ci = atomtype.rbegin(); ci != atomtype.rend(); ++ci) - { - if (isspace(*ci)) continue; - // read trailing sign - if (rv == 0) - { - if (*ci == '+') - { - rv = 1; - continue; - } - if (*ci == '-') - { - rv = -1; - continue; - } - break; - } - // allow one [1-8] digit before the +/- sign - if (rv && '0' <= *ci && *ci <= '8') - { - rv *= (*ci - '0'); - } - break; +int atomValence(const std::string& atomtype) { + std::string::const_reverse_iterator ci; + int rv = 0; + for (ci = atomtype.rbegin(); ci != atomtype.rend(); ++ci) { + if (isspace(*ci)) continue; + // read trailing sign + if (rv == 0) { + if (*ci == '+') { + rv = 1; + continue; + } + if (*ci == '-') { + rv = -1; + continue; + } + break; + } + // allow one [1-8] digit before the +/- sign + if (rv && '0' <= *ci && *ci <= '8') { + rv *= (*ci - '0'); } - return rv; + break; + } + return rv; } -} // namespace srreal -} // namespace diffpy +} // namespace srreal +} // namespace diffpy // End of file diff --git a/src/diffpy/srreal/AtomUtils.hpp b/src/diffpy/srreal/AtomUtils.hpp index 30071147..636ab9c3 100644 --- a/src/diffpy/srreal/AtomUtils.hpp +++ b/src/diffpy/srreal/AtomUtils.hpp @@ -1,20 +1,20 @@ /***************************************************************************** -* -* libdiffpy Complex Modeling Initiative -* (c) 2013 Brookhaven Science Associates, -* Brookhaven National Laboratory. -* All rights reserved. -* -* File coded by: Pavol Juhas -* -* See AUTHORS.txt for a list of people who contributed. -* See LICENSE.txt for license information. -* -****************************************************************************** -* -* Small routines related to atom properties. -* -*****************************************************************************/ + * + * libdiffpy Complex Modeling Initiative + * (c) 2013 Brookhaven Science Associates, + * Brookhaven National Laboratory. + * All rights reserved. + * + * File coded by: Pavol Juhas + * + * See AUTHORS.txt for a list of people who contributed. + * See LICENSE.txt for license information. + * + ****************************************************************************** + * + * Small routines related to atom properties. + * + *****************************************************************************/ #ifndef ATOMUTILS_HPP_INCLUDED #define ATOMUTILS_HPP_INCLUDED @@ -31,7 +31,7 @@ std::string atomBareSymbol(const std::string& atomtype); /// Return valence of possibly ionic symbol such as "S2-" or "Cl-". int atomValence(const std::string& atomtype); -} // namespace srreal -} // namespace diffpy +} // namespace srreal +} // namespace diffpy #endif // ATOMUTILS_HPP_INCLUDED diff --git a/src/diffpy/srreal/AtomicStructureAdapter.cpp b/src/diffpy/srreal/AtomicStructureAdapter.cpp index ac5a0192..f8b0d168 100644 --- a/src/diffpy/srreal/AtomicStructureAdapter.cpp +++ b/src/diffpy/srreal/AtomicStructureAdapter.cpp @@ -1,21 +1,21 @@ /***************************************************************************** -* -* libdiffpy by DANSE Diffraction group -* Simon J. L. Billinge -* (c) 2012 The Trustees of Columbia University -* in the City of New York. All rights reserved. -* -* File coded by: Pavol Juhas -* -* See AUTHORS.txt for a list of people who contributed. -* See LICENSE_DANSE.txt for license information. -* -****************************************************************************** -* -* class AtomicStructureAdapter -- universal structure adapter for -* a non-periodic set of atoms. -* -*****************************************************************************/ + * + * libdiffpy by DANSE Diffraction group + * Simon J. L. Billinge + * (c) 2012 The Trustees of Columbia University + * in the City of New York. All rights reserved. + * + * File coded by: Pavol Juhas + * + * See AUTHORS.txt for a list of people who contributed. + * See LICENSE_DANSE.txt for license information. + * + ****************************************************************************** + * + * class AtomicStructureAdapter -- universal structure adapter for + * a non-periodic set of atoms. + * + *****************************************************************************/ #include #include @@ -34,62 +34,48 @@ namespace srreal { // class Atom ////////////////////////////////////////////////////////////////////////////// -bool operator< (const Atom& a0, const Atom& a1) -{ - if (a0.atomtype < a1.atomtype) return true; - if (a0.atomtype > a1.atomtype) return false; - R3::Vector::const_iterator p0 = a0.xyz_cartn.begin(); - R3::Vector::const_iterator p1 = a1.xyz_cartn.begin(); - for (; p0 != a0.xyz_cartn.end(); ++p0, ++p1) - { - if (*p0 < *p1) return true; - if (*p0 > *p1) return false; - } - if (a0.occupancy < a1.occupancy) return true; - if (a0.occupancy > a1.occupancy) return false; - if (a0.anisotropy < a1.anisotropy) return true; - if (a0.anisotropy > a1.anisotropy) return false; - typedef R3::Matrix::array_type::const_iterator CIter; - CIter u0 = a0.uij_cartn.data().begin(); - CIter u0last = a0.uij_cartn.data().end(); - CIter u1 = a1.uij_cartn.data().begin(); - for (; u0 != u0last; ++u0, ++u1) - { - if (*u0 < *u1) return true; - if (*u0 > *u1) return false; - } - return false; -} - - -bool operator==(const Atom& a0, const Atom& a1) -{ - bool rv = (&a0 == &a1) || ( - a0.atomtype == a1.atomtype && - (a0.xyz_cartn == a1.xyz_cartn) && - a0.occupancy == a1.occupancy && - a0.anisotropy == a1.anisotropy && - a0.uij_cartn == a1.uij_cartn - ); - return rv; -} - - -bool operator!=(const Atom& a0, const Atom& a1) -{ - return !(a0 == a1); -} - - -size_t hash_value(const Atom& a) -{ - size_t seed = 0; - boost::hash_combine(seed, a.atomtype); - boost::hash_combine(seed, a.xyz_cartn); - boost::hash_combine(seed, a.occupancy); - boost::hash_combine(seed, a.anisotropy); - boost::hash_combine(seed, a.uij_cartn); - return seed; +bool operator<(const Atom& a0, const Atom& a1) { + if (a0.atomtype < a1.atomtype) return true; + if (a0.atomtype > a1.atomtype) return false; + R3::Vector::const_iterator p0 = a0.xyz_cartn.begin(); + R3::Vector::const_iterator p1 = a1.xyz_cartn.begin(); + for (; p0 != a0.xyz_cartn.end(); ++p0, ++p1) { + if (*p0 < *p1) return true; + if (*p0 > *p1) return false; + } + if (a0.occupancy < a1.occupancy) return true; + if (a0.occupancy > a1.occupancy) return false; + if (a0.anisotropy < a1.anisotropy) return true; + if (a0.anisotropy > a1.anisotropy) return false; + typedef R3::Matrix::array_type::const_iterator CIter; + CIter u0 = a0.uij_cartn.data().begin(); + CIter u0last = a0.uij_cartn.data().end(); + CIter u1 = a1.uij_cartn.data().begin(); + for (; u0 != u0last; ++u0, ++u1) { + if (*u0 < *u1) return true; + if (*u0 > *u1) return false; + } + return false; +} + +bool operator==(const Atom& a0, const Atom& a1) { + bool rv = (&a0 == &a1) || + (a0.atomtype == a1.atomtype && (a0.xyz_cartn == a1.xyz_cartn) && + a0.occupancy == a1.occupancy && a0.anisotropy == a1.anisotropy && + a0.uij_cartn == a1.uij_cartn); + return rv; +} + +bool operator!=(const Atom& a0, const Atom& a1) { return !(a0 == a1); } + +size_t hash_value(const Atom& a) { + size_t seed = 0; + boost::hash_combine(seed, a.atomtype); + boost::hash_combine(seed, a.xyz_cartn); + boost::hash_combine(seed, a.occupancy); + boost::hash_combine(seed, a.anisotropy); + boost::hash_combine(seed, a.uij_cartn); + return seed; } ////////////////////////////////////////////////////////////////////////////// @@ -98,58 +84,41 @@ size_t hash_value(const Atom& a) // Public Methods ------------------------------------------------------------ -StructureAdapterPtr AtomicStructureAdapter::clone() const -{ - StructureAdapterPtr rv(new AtomicStructureAdapter(*this)); - return rv; +StructureAdapterPtr AtomicStructureAdapter::clone() const { + StructureAdapterPtr rv(new AtomicStructureAdapter(*this)); + return rv; } - -BaseBondGeneratorPtr AtomicStructureAdapter::createBondGenerator() const -{ - BaseBondGeneratorPtr bnds(new BaseBondGenerator(shared_from_this())); - return bnds; +BaseBondGeneratorPtr AtomicStructureAdapter::createBondGenerator() const { + BaseBondGeneratorPtr bnds(new BaseBondGenerator(shared_from_this())); + return bnds; } +int AtomicStructureAdapter::countSites() const { return matoms.size(); } -int AtomicStructureAdapter::countSites() const -{ - return matoms.size(); +const string& AtomicStructureAdapter::siteAtomType(int idx) const { + assert(0 <= idx && idx < this->countSites()); + return matoms[idx].atomtype; } - -const string& AtomicStructureAdapter::siteAtomType(int idx) const -{ - assert(0 <= idx && idx < this->countSites()); - return matoms[idx].atomtype; +const R3::Vector& AtomicStructureAdapter::siteCartesianPosition(int idx) const { + assert(0 <= idx && idx < this->countSites()); + return matoms[idx].xyz_cartn; } - -const R3::Vector& AtomicStructureAdapter::siteCartesianPosition(int idx) const -{ - assert(0 <= idx && idx < this->countSites()); - return matoms[idx].xyz_cartn; +double AtomicStructureAdapter::siteOccupancy(int idx) const { + assert(0 <= idx && idx < this->countSites()); + return matoms[idx].occupancy; } - -double AtomicStructureAdapter::siteOccupancy(int idx) const -{ - assert(0 <= idx && idx < this->countSites()); - return matoms[idx].occupancy; +bool AtomicStructureAdapter::siteAnisotropy(int idx) const { + assert(0 <= idx && idx < this->countSites()); + return matoms[idx].anisotropy; } - -bool AtomicStructureAdapter::siteAnisotropy(int idx) const -{ - assert(0 <= idx && idx < this->countSites()); - return matoms[idx].anisotropy; -} - - -const R3::Matrix& AtomicStructureAdapter::siteCartesianUij(int idx) const -{ - assert(0 <= idx && idx < this->countSites()); - return matoms[idx].uij_cartn; +const R3::Matrix& AtomicStructureAdapter::siteCartesianUij(int idx) const { + assert(0 <= idx && idx < this->countSites()); + return matoms[idx].uij_cartn; } // helper for diff @@ -157,150 +126,124 @@ namespace { typedef std::pair atomindex; -bool cmpatomindex(const atomindex& ai0, const atomindex& ai1) -{ - return (*(ai0.first) < *(ai1.first)); -} - -} // namespace - -StructureDifference -AtomicStructureAdapter::diff(StructureAdapterConstPtr other) const -{ - using std::min; - StructureDifference sd = this->StructureAdapter::diff(other); - if (sd.stru0 == sd.stru1) return sd; - typedef boost::shared_ptr APtr; - APtr pother = boost::dynamic_pointer_cast(other); - if (!pother) return sd; - // try fast side-by-side comparison - sd.diffmethod = StructureDifference::Method::SIDEBYSIDE; - const AtomicStructureAdapter& astru0 = *this; - const AtomicStructureAdapter& astru1 = *pother; - sd.pop0.clear(); - sd.add1.clear(); - const_iterator ai0 = astru0.matoms.begin(); - const_iterator ai1 = astru1.matoms.begin(); - int nboth = min(astru0.countSites(), astru1.countSites()); - for (int i = 0; i < nboth; ++i, ++ai0, ++ai1) - { - if (*ai0 != *ai1) - { - sd.pop0.push_back(i); - sd.add1.push_back(i); - } - } - for (int i = nboth; ai0 != astru0.matoms.end(); ++i, ++ai0) - { - sd.pop0.push_back(i); - } - for (int i = nboth; ai1 != astru1.matoms.end(); ++i, ++ai1) - { - sd.add1.push_back(i); +bool cmpatomindex(const atomindex& ai0, const atomindex& ai1) { + return (*(ai0.first) < *(ai1.first)); +} + +} // namespace + +StructureDifference AtomicStructureAdapter::diff( + StructureAdapterConstPtr other) const { + using std::min; + StructureDifference sd = this->StructureAdapter::diff(other); + if (sd.stru0 == sd.stru1) return sd; + typedef boost::shared_ptr APtr; + APtr pother = boost::dynamic_pointer_cast(other); + if (!pother) return sd; + // try fast side-by-side comparison + sd.diffmethod = StructureDifference::Method::SIDEBYSIDE; + const AtomicStructureAdapter& astru0 = *this; + const AtomicStructureAdapter& astru1 = *pother; + sd.pop0.clear(); + sd.add1.clear(); + const_iterator ai0 = astru0.matoms.begin(); + const_iterator ai1 = astru1.matoms.begin(); + int nboth = min(astru0.countSites(), astru1.countSites()); + for (int i = 0; i < nboth; ++i, ++ai0, ++ai1) { + if (*ai0 != *ai1) { + sd.pop0.push_back(i); + sd.add1.push_back(i); } - if (sd.allowsfastupdate()) return sd; - // here the structures differ too much when compared side by side. - // Let's compare assuming no relation in atom site order. - sd.pop0.clear(); - sd.add1.clear(); - // let's build sorted vectors of atoms in stru0 and stru1 - sd.diffmethod = StructureDifference::Method::SORTED; - std::vector satoms0, satoms1; - satoms0.reserve(astru0.countSites()); - const_iterator ai = astru0.matoms.begin(); - for (int i = 0; ai != astru0.matoms.end(); ++ai, ++i) - { - satoms0.push_back(atomindex(&(*ai), i)); - } - // use negative index for stru1 atoms so we can tell them apart - // in the output of set_symmetric_difference - satoms1.reserve(astru1.countSites()); - ai = astru1.matoms.begin(); - for (int i = -1; ai != astru1.matoms.end(); ++ai, --i) - { - satoms1.push_back(atomindex(&(*ai), i)); - } - sort(satoms0.begin(), satoms0.end(), cmpatomindex); - sort(satoms1.begin(), satoms1.end(), cmpatomindex); - std::vector symdiffatoms(satoms0.size() + satoms1.size()); - std::vector::iterator ii; - ii = std::set_symmetric_difference(satoms0.begin(), satoms0.end(), - satoms1.begin(), satoms1.end(), - symdiffatoms.begin(), cmpatomindex); - symdiffatoms.erase(ii, symdiffatoms.end()); - for (ii = symdiffatoms.begin(); ii != symdiffatoms.end(); ++ii) - { - if (ii->second >= 0) sd.pop0.push_back(ii->second); - else sd.add1.push_back(-1 * ii->second - 1); - } - assert(sd.pop0.size() <= min(satoms0.size(), symdiffatoms.size())); - assert(sd.add1.size() <= min(satoms1.size(), symdiffatoms.size())); - sort(sd.pop0.begin(), sd.pop0.end()); - sort(sd.add1.begin(), sd.add1.end()); - return sd; + } + for (int i = nboth; ai0 != astru0.matoms.end(); ++i, ++ai0) { + sd.pop0.push_back(i); + } + for (int i = nboth; ai1 != astru1.matoms.end(); ++i, ++ai1) { + sd.add1.push_back(i); + } + if (sd.allowsfastupdate()) return sd; + // here the structures differ too much when compared side by side. + // Let's compare assuming no relation in atom site order. + sd.pop0.clear(); + sd.add1.clear(); + // let's build sorted vectors of atoms in stru0 and stru1 + sd.diffmethod = StructureDifference::Method::SORTED; + std::vector satoms0, satoms1; + satoms0.reserve(astru0.countSites()); + const_iterator ai = astru0.matoms.begin(); + for (int i = 0; ai != astru0.matoms.end(); ++ai, ++i) { + satoms0.push_back(atomindex(&(*ai), i)); + } + // use negative index for stru1 atoms so we can tell them apart + // in the output of set_symmetric_difference + satoms1.reserve(astru1.countSites()); + ai = astru1.matoms.begin(); + for (int i = -1; ai != astru1.matoms.end(); ++ai, --i) { + satoms1.push_back(atomindex(&(*ai), i)); + } + sort(satoms0.begin(), satoms0.end(), cmpatomindex); + sort(satoms1.begin(), satoms1.end(), cmpatomindex); + std::vector symdiffatoms(satoms0.size() + satoms1.size()); + std::vector::iterator ii; + ii = std::set_symmetric_difference(satoms0.begin(), satoms0.end(), + satoms1.begin(), satoms1.end(), + symdiffatoms.begin(), cmpatomindex); + symdiffatoms.erase(ii, symdiffatoms.end()); + for (ii = symdiffatoms.begin(); ii != symdiffatoms.end(); ++ii) { + if (ii->second >= 0) + sd.pop0.push_back(ii->second); + else + sd.add1.push_back(-1 * ii->second - 1); + } + assert(sd.pop0.size() <= min(satoms0.size(), symdiffatoms.size())); + assert(sd.add1.size() <= min(satoms1.size(), symdiffatoms.size())); + sort(sd.pop0.begin(), sd.pop0.end()); + sort(sd.add1.begin(), sd.add1.end()); + return sd; } typedef AtomicStructureAdapter::iterator iterator; -iterator AtomicStructureAdapter::insert(int idx, const Atom& atom) -{ - assert(0 <= idx && idx <= this->countSites()); - return this->insert(matoms.begin() + idx, atom); +iterator AtomicStructureAdapter::insert(int idx, const Atom& atom) { + assert(0 <= idx && idx <= this->countSites()); + return this->insert(matoms.begin() + idx, atom); } - -iterator AtomicStructureAdapter::insert(iterator ii, const Atom& atom) -{ - return matoms.insert(ii, atom); +iterator AtomicStructureAdapter::insert(iterator ii, const Atom& atom) { + return matoms.insert(ii, atom); } - -void AtomicStructureAdapter::append(const Atom& atom) -{ - matoms.push_back(atom); -} - - -void AtomicStructureAdapter::clear() -{ - matoms.clear(); +void AtomicStructureAdapter::append(const Atom& atom) { + matoms.push_back(atom); } +void AtomicStructureAdapter::clear() { matoms.clear(); } -iterator AtomicStructureAdapter::erase(int idx) -{ - assert(0 <= idx && idx < this->countSites()); - return matoms.erase(matoms.begin() + idx); +iterator AtomicStructureAdapter::erase(int idx) { + assert(0 <= idx && idx < this->countSites()); + return matoms.erase(matoms.begin() + idx); } - -iterator AtomicStructureAdapter::erase(iterator pos) -{ - return matoms.erase(pos); +iterator AtomicStructureAdapter::erase(iterator pos) { + return matoms.erase(pos); } - -iterator AtomicStructureAdapter::erase(iterator first, iterator last) -{ - return matoms.erase(first, last); +iterator AtomicStructureAdapter::erase(iterator first, iterator last) { + return matoms.erase(first, last); } - -Atom& AtomicStructureAdapter::operator[](int idx) -{ - assert(0 <= idx && idx < this->countSites()); - return matoms[idx]; +Atom& AtomicStructureAdapter::operator[](int idx) { + assert(0 <= idx && idx < this->countSites()); + return matoms[idx]; } - -const Atom& AtomicStructureAdapter::operator[](int idx) const -{ - assert(0 <= idx && idx < this->countSites()); - return matoms[idx]; +const Atom& AtomicStructureAdapter::operator[](int idx) const { + assert(0 <= idx && idx < this->countSites()); + return matoms[idx]; } -} // namespace srreal -} // namespace diffpy +} // namespace srreal +} // namespace diffpy // Serialization ------------------------------------------------------------- diff --git a/src/diffpy/srreal/AtomicStructureAdapter.hpp b/src/diffpy/srreal/AtomicStructureAdapter.hpp index 640de9b4..de1787f8 100644 --- a/src/diffpy/srreal/AtomicStructureAdapter.hpp +++ b/src/diffpy/srreal/AtomicStructureAdapter.hpp @@ -1,21 +1,21 @@ /***************************************************************************** -* -* libdiffpy by DANSE Diffraction group -* Simon J. L. Billinge -* (c) 2012 The Trustees of Columbia University -* in the City of New York. All rights reserved. -* -* File coded by: Pavol Juhas -* -* See AUTHORS.txt for a list of people who contributed. -* See LICENSE_DANSE.txt for license information. -* -****************************************************************************** -* -* class AtomicStructureAdapter -- universal structure adapter for -* a non-periodic set of atoms. -* -*****************************************************************************/ + * + * libdiffpy by DANSE Diffraction group + * Simon J. L. Billinge + * (c) 2012 The Trustees of Columbia University + * in the City of New York. All rights reserved. + * + * File coded by: Pavol Juhas + * + * See AUTHORS.txt for a list of people who contributed. + * See LICENSE_DANSE.txt for license information. + * + ****************************************************************************** + * + * class AtomicStructureAdapter -- universal structure adapter for + * a non-periodic set of atoms. + * + *****************************************************************************/ #ifndef ATOMICSTRUCTUREADAPTER_HPP_INCLUDED #define ATOMICSTRUCTUREADAPTER_HPP_INCLUDED @@ -29,149 +29,133 @@ namespace diffpy { namespace srreal { -class DLL_EXPORT Atom -{ - public: - - // constructor - Atom() : - xyz_cartn(0.0, 0.0, 0.0), - occupancy(1.0), - anisotropy(false), - uij_cartn(R3::zeromatrix()) - { }; - - // data - std::string atomtype; - R3::Vector xyz_cartn; - double occupancy; - bool anisotropy; - R3::Matrix uij_cartn; - - private: - - // serialization - friend class boost::serialization::access; - template - void serialize(Archive& ar, const unsigned int version) - { - ar & atomtype; - ar & xyz_cartn; - ar & occupancy; - ar & anisotropy; - ar & uij_cartn; - } - +class DLL_EXPORT Atom { + public: + // constructor + Atom() + : xyz_cartn(0.0, 0.0, 0.0), + occupancy(1.0), + anisotropy(false), + uij_cartn(R3::zeromatrix()) {}; + + // data + std::string atomtype; + R3::Vector xyz_cartn; + double occupancy; + bool anisotropy; + R3::Matrix uij_cartn; + + private: + // serialization + friend class boost::serialization::access; + template + void serialize(Archive& ar, const unsigned int version) { + ar & atomtype; + ar & xyz_cartn; + ar & occupancy; + ar & anisotropy; + ar & uij_cartn; + } }; // Functions related to class Atom DLL_EXPORT bool operator<(const Atom&, const Atom&); -inline bool operator>(const Atom& a0, const Atom& a1) { return a1 < a0; } -inline bool operator<=(const Atom& a0, const Atom& a1) { return !(a1 < a0); } -inline bool operator>=(const Atom& a0, const Atom& a1) { return !(a0 < a1); } +inline bool operator>(const Atom& a0, const Atom& a1) { return a1 < a0; } +inline bool operator<=(const Atom& a0, const Atom& a1) { return !(a1 < a0); } +inline bool operator>=(const Atom& a0, const Atom& a1) { return !(a0 < a1); } DLL_EXPORT bool operator==(const Atom&, const Atom&); DLL_EXPORT bool operator!=(const Atom&, const Atom&); DLL_EXPORT size_t hash_value(const Atom&); - -class DLL_EXPORT AtomicStructureAdapter : public StructureAdapter -{ - public: - - typedef std::vector AtomVector; - typedef AtomVector::value_type value_type; - typedef AtomVector::iterator iterator; - typedef AtomVector::const_iterator const_iterator; - typedef AtomVector::reverse_iterator reverse_iterator; - typedef AtomVector::const_reverse_iterator const_reverse_iterator; - typedef AtomVector::difference_type difference_type; - typedef AtomVector::size_type size_type; - - // methods - overloaded - virtual StructureAdapterPtr clone() const; - virtual BaseBondGeneratorPtr createBondGenerator() const; - virtual int countSites() const; - // reusing StructureAdapter::numberDensity() - virtual const std::string& siteAtomType(int idx) const; - virtual const R3::Vector& siteCartesianPosition(int idx) const; - // reusing StructureAdapter::siteMultiplicity() - virtual double siteOccupancy(int idx) const; - virtual bool siteAnisotropy(int idx) const; - virtual const R3::Matrix& siteCartesianUij(int idx) const; - virtual StructureDifference diff(StructureAdapterConstPtr other) const; - - // methods - own - iterator insert(int, const Atom&); - iterator insert(iterator position, const Atom&); - template - void insert(iterator position, Iter first, Iter last) - { - matoms.insert(position, first, last); - } - void append(const Atom&); - void clear(); - iterator erase(int idx); - iterator erase(iterator pos); - iterator erase(iterator first, iterator last); - void reserve(size_t sz) { matoms.reserve(sz); } - size_type size() const { return matoms.size(); } - Atom& operator[](int); - const Atom& operator[](int) const; - Atom& at(int idx) { return (*this)[idx]; } - const Atom& at(int idx) const { return (*this)[idx]; } - template - void assign (Iter first, Iter last) { matoms.assign(first, last); } - void assign (size_t n, const Atom& a) { matoms.assign(n, a); } - // iterator forwarding - iterator begin() { return matoms.begin(); } - iterator end() { return matoms.end(); } - const_iterator begin() const { return matoms.begin(); } - const_iterator end() const { return matoms.end(); } - reverse_iterator rbegin() { return matoms.rbegin(); } - reverse_iterator rend() { return matoms.rend(); } - const_reverse_iterator rbegin() const { return matoms.rbegin(); } - const_reverse_iterator rend() const { return matoms.rend(); } - - private: - - // data - AtomVector matoms; - - // comparison - friend bool operator==( - const AtomicStructureAdapter& stru0, - const AtomicStructureAdapter& stru1) - { - return (stru0.matoms == stru1.matoms); - } - - // serialization - friend class boost::serialization::access; - template - void serialize(Archive& ar, const unsigned int version) - { - ar & boost::serialization::base_object(*this); - ar & matoms; - } - +class DLL_EXPORT AtomicStructureAdapter : public StructureAdapter { + public: + typedef std::vector AtomVector; + typedef AtomVector::value_type value_type; + typedef AtomVector::iterator iterator; + typedef AtomVector::const_iterator const_iterator; + typedef AtomVector::reverse_iterator reverse_iterator; + typedef AtomVector::const_reverse_iterator const_reverse_iterator; + typedef AtomVector::difference_type difference_type; + typedef AtomVector::size_type size_type; + + // methods - overloaded + virtual StructureAdapterPtr clone() const; + virtual BaseBondGeneratorPtr createBondGenerator() const; + virtual int countSites() const; + // reusing StructureAdapter::numberDensity() + virtual const std::string& siteAtomType(int idx) const; + virtual const R3::Vector& siteCartesianPosition(int idx) const; + // reusing StructureAdapter::siteMultiplicity() + virtual double siteOccupancy(int idx) const; + virtual bool siteAnisotropy(int idx) const; + virtual const R3::Matrix& siteCartesianUij(int idx) const; + virtual StructureDifference diff(StructureAdapterConstPtr other) const; + + // methods - own + iterator insert(int, const Atom&); + iterator insert(iterator position, const Atom&); + template + void insert(iterator position, Iter first, Iter last) { + matoms.insert(position, first, last); + } + void append(const Atom&); + void clear(); + iterator erase(int idx); + iterator erase(iterator pos); + iterator erase(iterator first, iterator last); + void reserve(size_t sz) { matoms.reserve(sz); } + size_type size() const { return matoms.size(); } + Atom& operator[](int); + const Atom& operator[](int) const; + Atom& at(int idx) { return (*this)[idx]; } + const Atom& at(int idx) const { return (*this)[idx]; } + template + void assign(Iter first, Iter last) { + matoms.assign(first, last); + } + void assign(size_t n, const Atom& a) { matoms.assign(n, a); } + // iterator forwarding + iterator begin() { return matoms.begin(); } + iterator end() { return matoms.end(); } + const_iterator begin() const { return matoms.begin(); } + const_iterator end() const { return matoms.end(); } + reverse_iterator rbegin() { return matoms.rbegin(); } + reverse_iterator rend() { return matoms.rend(); } + const_reverse_iterator rbegin() const { return matoms.rbegin(); } + const_reverse_iterator rend() const { return matoms.rend(); } + + private: + // data + AtomVector matoms; + + // comparison + friend bool operator==(const AtomicStructureAdapter& stru0, + const AtomicStructureAdapter& stru1) { + return (stru0.matoms == stru1.matoms); + } + + // serialization + friend class boost::serialization::access; + template + void serialize(Archive& ar, const unsigned int version) { + ar& boost::serialization::base_object(*this); + ar & matoms; + } }; typedef boost::shared_ptr AtomicStructureAdapterPtr; // Complementary comparison function -inline -bool operator!=( - const AtomicStructureAdapter& stru0, - const AtomicStructureAdapter& stru1) -{ - return !(stru0 == stru1); +inline bool operator!=(const AtomicStructureAdapter& stru0, + const AtomicStructureAdapter& stru1) { + return !(stru0 == stru1); } -} // namespace srreal -} // namespace diffpy +} // namespace srreal +} // namespace diffpy // Serialization ------------------------------------------------------------- diff --git a/src/diffpy/srreal/BVParam.cpp b/src/diffpy/srreal/BVParam.cpp index 74601708..abb6f906 100644 --- a/src/diffpy/srreal/BVParam.cpp +++ b/src/diffpy/srreal/BVParam.cpp @@ -1,20 +1,20 @@ /***************************************************************************** -* -* 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.txt for a list of people who contributed. -* See LICENSE_DANSE.txt for license information. -* -****************************************************************************** -* -* class BVParam -- bond valence parameters for a cation-anion pair -* -*****************************************************************************/ + * + * 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.txt for a list of people who contributed. + * See LICENSE_DANSE.txt for license information. + * + ****************************************************************************** + * + * class BVParam -- bond valence parameters for a cation-anion pair + * + *****************************************************************************/ #include #include @@ -30,109 +30,87 @@ namespace srreal { // Constructor --------------------------------------------------------------- -BVParam::BVParam() -{ - mvalence0 = 0; - mvalence1 = 0; - mRo = 0.0; - mB = 0.0; +BVParam::BVParam() { + mvalence0 = 0; + mvalence1 = 0; + mRo = 0.0; + mB = 0.0; } -BVParam::BVParam(const string& atom0, int valence0, - const string& atom1, int valence1, - double Ro, double B, string ref_id) -{ - matom0 = atom0; - mvalence0 = valence0; - matom1 = atom1; - mvalence1 = valence1; - if (mvalence0 < mvalence1) - { - swap(matom0, matom1); - swap(mvalence0, mvalence1); - } - mRo = Ro; - mB = B; - mref_id = ref_id; +BVParam::BVParam(const string& atom0, int valence0, const string& atom1, + int valence1, double Ro, double B, string ref_id) { + matom0 = atom0; + mvalence0 = valence0; + matom1 = atom1; + mvalence1 = valence1; + if (mvalence0 < mvalence1) { + swap(matom0, matom1); + swap(mvalence0, mvalence1); + } + mRo = Ro; + mB = B; + mref_id = ref_id; } // Public Methods ------------------------------------------------------------ -bool BVParam::operator==(const BVParam& other) const -{ - return (this == &other) || ( - mvalence0 == other.mvalence0 && - mvalence1 == other.mvalence1 && - mRo == other.mRo && - mB == other.mB && - matom0 == other.matom0 && - matom1 == other.matom1 && - mref_id == other.mref_id); +bool BVParam::operator==(const BVParam& other) const { + return (this == &other) || + (mvalence0 == other.mvalence0 && mvalence1 == other.mvalence1 && + mRo == other.mRo && mB == other.mB && matom0 == other.matom0 && + matom1 == other.matom1 && mref_id == other.mref_id); } - -bool BVParam::operator!=(const BVParam& other) const -{ - return !(*this == other); +bool BVParam::operator!=(const BVParam& other) const { + return !(*this == other); } - -double BVParam::bondvalence(double distance) const -{ - double rv = (mB > 0.0) ? exp((mRo - distance) / mB) : 0.0; - return rv; +double BVParam::bondvalence(double distance) const { + double rv = (mB > 0.0) ? exp((mRo - distance) / mB) : 0.0; + return rv; } - -double BVParam::bondvalenceToDistance(double bvalence) const -{ - double rv = mRo - mB * log(bvalence); - return rv; +double BVParam::bondvalenceToDistance(double bvalence) const { + double rv = mRo - mB * log(bvalence); + return rv; } - -void BVParam::setFromCifLine(const std::string& cifline) -{ - string a0, a1, ref; - int v0, v1; - double Ro, B; - istringstream linefp(cifline); - linefp >> a0 >> v0 >> a1 >> v1 >> Ro >> B >> ref; - if (!linefp) - { - const char* emsg = "Cannot parse cif line."; - throw invalid_argument(emsg); - } - BVParam bp1(a0, v0, a1, v1, Ro, B, ref); - *this = bp1; +void BVParam::setFromCifLine(const std::string& cifline) { + string a0, a1, ref; + int v0, v1; + double Ro, B; + istringstream linefp(cifline); + linefp >> a0 >> v0 >> a1 >> v1 >> Ro >> B >> ref; + if (!linefp) { + const char* emsg = "Cannot parse cif line."; + throw invalid_argument(emsg); + } + BVParam bp1(a0, v0, a1, v1, Ro, B, ref); + *this = bp1; } // class BVParam::BondHash --------------------------------------------------- -size_t BVParam::BondHash::operator()(const BVParam& bp) const -{ - size_t seed = 0; - boost::hash_combine(seed, bp.matom0); - boost::hash_combine(seed, bp.mvalence0); - boost::hash_combine(seed, bp.matom1); - boost::hash_combine(seed, bp.mvalence1); - return seed; +size_t BVParam::BondHash::operator()(const BVParam& bp) const { + size_t seed = 0; + boost::hash_combine(seed, bp.matom0); + boost::hash_combine(seed, bp.mvalence0); + boost::hash_combine(seed, bp.matom1); + boost::hash_combine(seed, bp.mvalence1); + return seed; } // class BVParam::BondEqual -------------------------------------------------- -bool BVParam::BondEqual::operator()( - const BVParam& bp0, const BVParam& bp1) const -{ - return (&bp0 == &bp1) || ( - bp0.mvalence0 == bp1.mvalence0 && - bp0.mvalence1 == bp1.mvalence1 && - bp0.matom0 == bp1.matom0 && - bp0.matom1 == bp1.matom1); +bool BVParam::BondEqual::operator()(const BVParam& bp0, + const BVParam& bp1) const { + return (&bp0 == &bp1) || + (bp0.mvalence0 == bp1.mvalence0 && bp0.mvalence1 == bp1.mvalence1 && + bp0.matom0 == bp1.matom0 && bp0.matom1 == bp1.matom1); } -} // namespace srreal -} // namespace diffpy +} // namespace srreal +} // namespace diffpy DIFFPY_INSTANTIATE_SERIALIZATION(diffpy::srreal::BVParam) diff --git a/src/diffpy/srreal/BVParam.hpp b/src/diffpy/srreal/BVParam.hpp index e36e692e..4a02dd59 100644 --- a/src/diffpy/srreal/BVParam.hpp +++ b/src/diffpy/srreal/BVParam.hpp @@ -1,20 +1,20 @@ /***************************************************************************** -* -* 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.txt for a list of people who contributed. -* See LICENSE_DANSE.txt for license information. -* -****************************************************************************** -* -* class BVParam -- bond valence parameters for a cation-anion pair -* -*****************************************************************************/ + * + * 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.txt for a list of people who contributed. + * See LICENSE_DANSE.txt for license information. + * + ****************************************************************************** + * + * class BVParam -- bond valence parameters for a cation-anion pair + * + *****************************************************************************/ #ifndef BVPARAM_HPP_INCLUDED #define BVPARAM_HPP_INCLUDED @@ -27,69 +27,63 @@ namespace diffpy { namespace srreal { -class DLL_EXPORT BVParam -{ - public: +class DLL_EXPORT BVParam { + public: + // constructors + BVParam(); + BVParam(const std::string& atom0, int valence0, const std::string& atom1, + int valence1, double Ro = 0.0, double B = 0.0, + std::string ref_id = ""); - // constructors - BVParam(); - BVParam(const std::string& atom0, int valence0, - const std::string& atom1, int valence1, - double Ro=0.0, double B=0.0, std::string ref_id=""); + // methods + /// comparison binary_function + bool operator==(const BVParam& other) const; + bool operator!=(const BVParam& other) const; + /// Return bond valence at a specified distance + double bondvalence(double distance) const; + /// Return distance corresponding to a specified bond valence + double bondvalenceToDistance(double bvalence) const; + /// obtain data from a cif record in bvparm.cif + void setFromCifLine(const std::string&); - // methods - /// comparison binary_function - bool operator==(const BVParam& other) const; - bool operator!=(const BVParam& other) const; - /// Return bond valence at a specified distance - double bondvalence(double distance) const; - /// Return distance corresponding to a specified bond valence - double bondvalenceToDistance(double bvalence) const; - /// obtain data from a cif record in bvparm.cif - void setFromCifLine(const std::string&); + // data + std::string matom0; + int mvalence0; + std::string matom1; + int mvalence1; + double mRo; + double mB; + std::string mref_id; - // data - std::string matom0; - int mvalence0; - std::string matom1; - int mvalence1; - double mRo; - double mB; - std::string mref_id; + // hash function for cation-anion bond type only + class DLL_EXPORT BondHash { + public: + size_t operator()(const BVParam&) const; + }; - // hash function for cation-anion bond type only - class DLL_EXPORT BondHash - { - public: - size_t operator()(const BVParam&) const; - }; + // equality test for cation-anion bond type + class DLL_EXPORT BondEqual { + public: + bool operator()(const BVParam& bp0, const BVParam& bp1) const; + }; - // equality test for cation-anion bond type - class DLL_EXPORT BondEqual - { - public: - bool operator()(const BVParam& bp0, const BVParam& bp1) const; - }; - - private: - - // serialization - friend class boost::serialization::access; - template - void serialize(Archive& ar, const unsigned int version) - { - ar & matom0; - ar & mvalence0; - ar & matom1; - ar & mvalence1; - ar & mRo; - ar & mB; - ar & mref_id; - } + private: + // serialization + friend class boost::serialization::access; + template + void serialize(Archive& ar, const unsigned int version) { + ar & matom0; + ar & mvalence0; + ar & matom1; + ar & mvalence1; + ar & mRo; + ar & mB; + ar & mref_id; + } }; // class BVParam -} // namespace srreal -} // namespace diffpy +} // namespace srreal +} // namespace diffpy #endif // BVPARAM_HPP_INCLUDED diff --git a/src/diffpy/srreal/BVParametersTable.cpp b/src/diffpy/srreal/BVParametersTable.cpp index 896cd463..5e8547ba 100644 --- a/src/diffpy/srreal/BVParametersTable.cpp +++ b/src/diffpy/srreal/BVParametersTable.cpp @@ -1,20 +1,20 @@ /***************************************************************************** -* -* 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.txt for a list of people who contributed. -* See LICENSE_DANSE.txt for license information. -* -****************************************************************************** -* -* class BVParametersTable -- table of bond valence sum parameters -* -*****************************************************************************/ + * + * 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.txt for a list of people who contributed. + * See LICENSE_DANSE.txt for license information. + * + ****************************************************************************** + * + * class BVParametersTable -- table of bond valence sum parameters + * + *****************************************************************************/ #include #include @@ -32,169 +32,134 @@ namespace srreal { // Static Methods ------------------------------------------------------------ -const BVParam& BVParametersTable::none() -{ - static const BVParam bpnone; - return bpnone; +const BVParam& BVParametersTable::none() { + static const BVParam bpnone; + return bpnone; } // Public Methods ------------------------------------------------------------ -int BVParametersTable::getAtomValence(const string& smbl) const -{ - AtomTypeValence::const_iterator ii = matomvalence.find(smbl); - int rv = (ii != matomvalence.end()) ? ii->second : atomValence(smbl); - return rv; +int BVParametersTable::getAtomValence(const string& smbl) const { + AtomTypeValence::const_iterator ii = matomvalence.find(smbl); + int rv = (ii != matomvalence.end()) ? ii->second : atomValence(smbl); + return rv; } - -void BVParametersTable::setAtomValence(const string& smbl, int value) -{ - matomvalence[smbl] = value; +void BVParametersTable::setAtomValence(const string& smbl, int value) { + matomvalence[smbl] = value; } - -void BVParametersTable::resetAtomValences() -{ - matomvalence.clear(); +void BVParametersTable::resetAtomValences() { matomvalence.clear(); } + +const BVParam& BVParametersTable::lookup(const BVParam& bpk) const { + SetOfBVParam::const_iterator bpit; + bpit = mcustomtable.find(bpk); + if (bpit != mcustomtable.end()) return *bpit; + const SetOfBVParam& stdtable = this->getStandardSetOfBVParam(); + bpit = stdtable.find(bpk); + if (bpit != stdtable.end()) return *bpit; + // unspecified cations are marked with valence 9 + // perform this search only if the first atom is indeed a cation + if (!(bpk.mvalence0 > 0)) return this->none(); + BVParam bpk9 = bpk; + bpk9.mvalence0 = 9; + bpit = mcustomtable.find(bpk9); + if (bpit != mcustomtable.end()) return *bpit; + bpit = stdtable.find(bpk9); + if (bpit != stdtable.end()) return *bpit; + // not found - return blank BVParam + return this->none(); } - -const BVParam& BVParametersTable::lookup(const BVParam& bpk) const -{ - SetOfBVParam::const_iterator bpit; - bpit = mcustomtable.find(bpk); - if (bpit != mcustomtable.end()) return *bpit; - const SetOfBVParam& stdtable = this->getStandardSetOfBVParam(); - bpit = stdtable.find(bpk); - if (bpit != stdtable.end()) return *bpit; - // unspecified cations are marked with valence 9 - // perform this search only if the first atom is indeed a cation - if (!(bpk.mvalence0 > 0)) return this->none(); - BVParam bpk9 = bpk; - bpk9.mvalence0 = 9; - bpit = mcustomtable.find(bpk9); - if (bpit != mcustomtable.end()) return *bpit; - bpit = stdtable.find(bpk9); - if (bpit != stdtable.end()) return *bpit; - // not found - return blank BVParam - return this->none(); +const BVParam& BVParametersTable::lookup(const string& smbl0, + const string& smbl1) const { + const int v0 = this->getAtomValence(smbl0); + const int v1 = this->getAtomValence(smbl1); + const BVParam& rv = + this->lookup(atomBareSymbol(smbl0), v0, atomBareSymbol(smbl1), v1); + return rv; } - -const BVParam& -BVParametersTable::lookup(const string& smbl0, const string& smbl1) const -{ - const int v0 = this->getAtomValence(smbl0); - const int v1 = this->getAtomValence(smbl1); - const BVParam& rv = this->lookup( - atomBareSymbol(smbl0), v0, - atomBareSymbol(smbl1), v1); - return rv; -} - - const BVParam& BVParametersTable::lookup(const string& atom0, int valence0, - const string& atom1, int valence1) const -{ - BVParam bpk(atom0, valence0, atom1, valence1); - const BVParam& rv = this->lookup(bpk); - return rv; + const string& atom1, + int valence1) const { + BVParam bpk(atom0, valence0, atom1, valence1); + const BVParam& rv = this->lookup(bpk); + return rv; } - -void BVParametersTable::setCustom(const BVParam& bp) -{ - this->resetCustom(bp); - mcustomtable.insert(bp); +void BVParametersTable::setCustom(const BVParam& bp) { + this->resetCustom(bp); + mcustomtable.insert(bp); } - void BVParametersTable::setCustom(const string& atom0, int valence0, - const string& atom1, int valence1, - double Ro, double b, std::string ref_id) -{ - BVParam bp(atom0, valence0, atom1, valence1, Ro, b, ref_id); - this->setCustom(bp); + const string& atom1, int valence1, double Ro, + double b, std::string ref_id) { + BVParam bp(atom0, valence0, atom1, valence1, Ro, b, ref_id); + this->setCustom(bp); } - -void BVParametersTable::resetCustom(const BVParam& bp) -{ - mcustomtable.erase(bp); +void BVParametersTable::resetCustom(const BVParam& bp) { + mcustomtable.erase(bp); } - void BVParametersTable::resetCustom(const string& atom0, int valence0, - const string& atom1, int valence1) -{ - BVParam bp(atom0, valence0, atom1, valence1); - this->resetCustom(bp); + const string& atom1, int valence1) { + BVParam bp(atom0, valence0, atom1, valence1); + this->resetCustom(bp); } +void BVParametersTable::resetAll() { mcustomtable.clear(); } -void BVParametersTable::resetAll() -{ - mcustomtable.clear(); +const BVParametersTable::SetOfBVParam& BVParametersTable::getAllCustom() const { + return mcustomtable; } - -const BVParametersTable::SetOfBVParam& -BVParametersTable::getAllCustom() const -{ - return mcustomtable; -} - - -BVParametersTable::SetOfBVParam -BVParametersTable::getAll() const -{ - // insert inserts only those items that are not yet in the set, therefore - // we start with the custom table and then add the standard entries. - SetOfBVParam rv = mcustomtable; - const SetOfBVParam& stdtable = this->getStandardSetOfBVParam(); - rv.insert(stdtable.begin(), stdtable.end()); - return rv; +BVParametersTable::SetOfBVParam BVParametersTable::getAll() const { + // insert inserts only those items that are not yet in the set, therefore + // we start with the custom table and then add the standard entries. + SetOfBVParam rv = mcustomtable; + const SetOfBVParam& stdtable = this->getStandardSetOfBVParam(); + rv.insert(stdtable.begin(), stdtable.end()); + return rv; } // Private Methods ----------------------------------------------------------- const BVParametersTable::SetOfBVParam& -BVParametersTable::getStandardSetOfBVParam() const -{ - using namespace diffpy::runtimepath; - using diffpy::validators::ensureFileOK; - static unique_ptr the_set; - if (!the_set) - { - the_set.reset(new SetOfBVParam); - string bvparmfile = datapath("bvparm2011sel.cif"); - ifstream fp(bvparmfile.c_str()); - ensureFileOK(bvparmfile, fp); - // read the header up to _valence_param_B and then up to an empty line. - LineReader lnrd; - lnrd.commentmark = '#'; - while (fp >> lnrd) - { - if (lnrd.wcount() && lnrd.words[0] == "_valence_param_B") break; - } - // skip to an empty line - while (fp >> lnrd && !lnrd.isblank()) { } - // load data lines skipping the empty or commented entries - while (fp >> lnrd) - { - if (lnrd.isignored()) continue; - BVParam bp; - bp.setFromCifLine(lnrd.line); - assert(!the_set->count(bp)); - the_set->insert(bp); - } +BVParametersTable::getStandardSetOfBVParam() const { + using namespace diffpy::runtimepath; + using diffpy::validators::ensureFileOK; + static unique_ptr the_set; + if (!the_set) { + the_set.reset(new SetOfBVParam); + string bvparmfile = datapath("bvparm2011sel.cif"); + ifstream fp(bvparmfile.c_str()); + ensureFileOK(bvparmfile, fp); + // read the header up to _valence_param_B and then up to an empty line. + LineReader lnrd; + lnrd.commentmark = '#'; + while (fp >> lnrd) { + if (lnrd.wcount() && lnrd.words[0] == "_valence_param_B") break; + } + // skip to an empty line + while (fp >> lnrd && !lnrd.isblank()) { + } + // load data lines skipping the empty or commented entries + while (fp >> lnrd) { + if (lnrd.isignored()) continue; + BVParam bp; + bp.setFromCifLine(lnrd.line); + assert(!the_set->count(bp)); + the_set->insert(bp); } - return *the_set; + } + return *the_set; } -} // namespace srreal -} // namespace diffpy +} // namespace srreal +} // namespace diffpy // Serialization ------------------------------------------------------------- diff --git a/src/diffpy/srreal/BVParametersTable.hpp b/src/diffpy/srreal/BVParametersTable.hpp index 55fc715b..c8ca1b74 100644 --- a/src/diffpy/srreal/BVParametersTable.hpp +++ b/src/diffpy/srreal/BVParametersTable.hpp @@ -1,20 +1,20 @@ /***************************************************************************** -* -* 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.txt for a list of people who contributed. -* See LICENSE_DANSE.txt for license information. -* -****************************************************************************** -* -* class BVParametersTable -- table of bond valence sum parameters -* -*****************************************************************************/ + * + * 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.txt for a list of people who contributed. + * See LICENSE_DANSE.txt for license information. + * + ****************************************************************************** + * + * class BVParametersTable -- table of bond valence sum parameters + * + *****************************************************************************/ #ifndef BVPARAMETERSTABLE_HPP_INCLUDED #define BVPARAMETERSTABLE_HPP_INCLUDED @@ -34,65 +34,60 @@ namespace srreal { typedef boost::shared_ptr BVParametersTablePtr; -class DLL_EXPORT BVParametersTable -{ - - public: - - // types - typedef std::unordered_set SetOfBVParam; - - // static methods - static const BVParam& none(); - - // methods - int getAtomValence(const std::string&) const; - void setAtomValence(const std::string&, int value); - void resetAtomValences(); - const BVParam& lookup(const BVParam&) const; - const BVParam& lookup( - const std::string& smbl0, const std::string& smbl1) const; - const BVParam& lookup(const std::string& atom0, int valence0, - const std::string& atom1, int valence1) const; - void setCustom(const BVParam&); - void setCustom(const std::string& atom0, int valence0, - const std::string& atom1, int valence1, - double Ro, double b, std::string ref_id=""); - void resetCustom(const BVParam&); - void resetCustom(const std::string& atom0, int valence0, - const std::string& atom1, int valence1); - void resetAll(); - const SetOfBVParam& getAllCustom() const; - SetOfBVParam getAll() const; - - private: - - // types - typedef std::unordered_map AtomTypeValence; - - // data - SetOfBVParam mcustomtable; - AtomTypeValence matomvalence; - - // methods - const SetOfBVParam& getStandardSetOfBVParam() const; - - // serialization - friend class boost::serialization::access; - template - void serialize(Archive& ar, const unsigned int version) - { - ar & mcustomtable; - if (version >= 1) { - ar & matomvalence; - } - } +class DLL_EXPORT BVParametersTable { + public: + // types + typedef std::unordered_set + SetOfBVParam; + + // static methods + static const BVParam& none(); + + // methods + int getAtomValence(const std::string&) const; + void setAtomValence(const std::string&, int value); + void resetAtomValences(); + const BVParam& lookup(const BVParam&) const; + const BVParam& lookup(const std::string& smbl0, + const std::string& smbl1) const; + const BVParam& lookup(const std::string& atom0, int valence0, + const std::string& atom1, int valence1) const; + void setCustom(const BVParam&); + void setCustom(const std::string& atom0, int valence0, + const std::string& atom1, int valence1, double Ro, double b, + std::string ref_id = ""); + void resetCustom(const BVParam&); + void resetCustom(const std::string& atom0, int valence0, + const std::string& atom1, int valence1); + void resetAll(); + const SetOfBVParam& getAllCustom() const; + SetOfBVParam getAll() const; + + private: + // types + typedef std::unordered_map AtomTypeValence; + + // data + SetOfBVParam mcustomtable; + AtomTypeValence matomvalence; + + // methods + const SetOfBVParam& getStandardSetOfBVParam() const; + + // serialization + friend class boost::serialization::access; + template + void serialize(Archive& ar, const unsigned int version) { + ar & mcustomtable; + if (version >= 1) { + ar & matomvalence; + } + } }; // class BVParametersTable -} // namespace srreal -} // namespace diffpy +} // namespace srreal +} // namespace diffpy // Serialization ------------------------------------------------------------- diff --git a/src/diffpy/srreal/BVSCalculator.cpp b/src/diffpy/srreal/BVSCalculator.cpp index 44e5de46..b5a66a66 100644 --- a/src/diffpy/srreal/BVSCalculator.cpp +++ b/src/diffpy/srreal/BVSCalculator.cpp @@ -1,20 +1,20 @@ /***************************************************************************** -* -* 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.txt for a list of people who contributed. -* See LICENSE_DANSE.txt for license information. -* -****************************************************************************** -* -* class BVSCalculator -- concrete counter of pairs in a structure. -* -*****************************************************************************/ + * + * 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.txt for a list of people who contributed. + * See LICENSE_DANSE.txt for license information. + * + ****************************************************************************** + * + * class BVSCalculator -- concrete counter of pairs in a structure. + * + *****************************************************************************/ #include #include @@ -32,209 +32,168 @@ namespace srreal { // Constructor --------------------------------------------------------------- -BVSCalculator::BVSCalculator() -{ - // default configuration - const double valence_precision = 1e-5; - // use very large rmax, it will be cropped by rmaxFromPrecision - this->setRmax(100); - BVParametersTablePtr bvtb(new BVParametersTable); - this->setBVParamTable(bvtb); - this->setValencePrecision(valence_precision); - this->setStructure(mstructure); - // attributes - this->registerDoubleAttribute("valenceprecision", this, - &BVSCalculator::getValencePrecision, - &BVSCalculator::setValencePrecision); - this->registerDoubleAttribute("rmaxused", this, - &BVSCalculator::getRmaxUsed); +BVSCalculator::BVSCalculator() { + // default configuration + const double valence_precision = 1e-5; + // use very large rmax, it will be cropped by rmaxFromPrecision + this->setRmax(100); + BVParametersTablePtr bvtb(new BVParametersTable); + this->setBVParamTable(bvtb); + this->setValencePrecision(valence_precision); + this->setStructure(mstructure); + // attributes + this->registerDoubleAttribute("valenceprecision", this, + &BVSCalculator::getValencePrecision, + &BVSCalculator::setValencePrecision); + this->registerDoubleAttribute("rmaxused", this, &BVSCalculator::getRmaxUsed); } // Public Methods ------------------------------------------------------------ // results -QuantityType BVSCalculator::valences() const -{ - QuantityType rv(mstructure_cache.valences.begin(), - mstructure_cache.valences.end()); - return rv; +QuantityType BVSCalculator::valences() const { + QuantityType rv(mstructure_cache.valences.begin(), + mstructure_cache.valences.end()); + return rv; } - -QuantityType BVSCalculator::bvdiff() const -{ - QuantityType vobs = this->valences(); - assert(vobs.size() == this->value().size()); - int cntsites = this->countSites(); - QuantityType rv(cntsites); - const QuantityType& vsim = this->value(); - for (int i = 0; i < cntsites; ++i) - { - rv[i] = fabs(vobs[i]) - fabs(vsim[i]); - } - return rv; +QuantityType BVSCalculator::bvdiff() const { + QuantityType vobs = this->valences(); + assert(vobs.size() == this->value().size()); + int cntsites = this->countSites(); + QuantityType rv(cntsites); + const QuantityType& vsim = this->value(); + for (int i = 0; i < cntsites; ++i) { + rv[i] = fabs(vobs[i]) - fabs(vsim[i]); + } + return rv; } - -double BVSCalculator::bvmsdiff() const -{ - QuantityType bd = this->bvdiff(); - int cntsites = this->countSites(); - assert(int(bd.size()) == cntsites); - double sumofsquares = 0.0; - for (int i = 0; i < cntsites; ++i) - { - sumofsquares += mstructure->siteMultiplicity(i) * - mstructure->siteOccupancy(i) * bd[i] * bd[i]; - } - double totocc = mstructure->totalOccupancy(); - double rv = (totocc > 0.0) ? (sumofsquares / totocc) : 0.0; - return rv; +double BVSCalculator::bvmsdiff() const { + QuantityType bd = this->bvdiff(); + int cntsites = this->countSites(); + assert(int(bd.size()) == cntsites); + double sumofsquares = 0.0; + for (int i = 0; i < cntsites; ++i) { + sumofsquares += mstructure->siteMultiplicity(i) * + mstructure->siteOccupancy(i) * bd[i] * bd[i]; + } + double totocc = mstructure->totalOccupancy(); + double rv = (totocc > 0.0) ? (sumofsquares / totocc) : 0.0; + return rv; } - -double BVSCalculator::bvrmsdiff() const -{ - double rvsq = this->bvmsdiff(); - return sqrt(rvsq); +double BVSCalculator::bvrmsdiff() const { + double rvsq = this->bvmsdiff(); + return sqrt(rvsq); } - -void BVSCalculator::setBVParamTable(BVParametersTablePtr bvtb) -{ - ensureNonNull("BVParametersTable", bvtb); - mbvptable = bvtb; +void BVSCalculator::setBVParamTable(BVParametersTablePtr bvtb) { + ensureNonNull("BVParametersTable", bvtb); + mbvptable = bvtb; } +BVParametersTablePtr& BVSCalculator::getBVParamTable() { return mbvptable; } -BVParametersTablePtr& BVSCalculator::getBVParamTable() -{ - return mbvptable; +const BVParametersTablePtr& BVSCalculator::getBVParamTable() const { + return mbvptable; } - -const BVParametersTablePtr& BVSCalculator::getBVParamTable() const -{ - return mbvptable; +void BVSCalculator::setValencePrecision(double eps) { + ensureEpsilonPositive("valenceprecision", eps); + mvalenceprecision = eps; } +double BVSCalculator::getValencePrecision() const { return mvalenceprecision; } -void BVSCalculator::setValencePrecision(double eps) -{ - ensureEpsilonPositive("valenceprecision", eps); - mvalenceprecision = eps; -} - - -double BVSCalculator::getValencePrecision() const -{ - return mvalenceprecision; -} - - -double BVSCalculator::getRmaxUsed() const -{ - double rv = min(this->getRmax(), - this->rmaxFromPrecision(this->getValencePrecision())); - return rv; +double BVSCalculator::getRmaxUsed() const { + double rv = + min(this->getRmax(), this->rmaxFromPrecision(this->getValencePrecision())); + return rv; } // Protected Methods --------------------------------------------------------- // PairQuantity overloads -void BVSCalculator::resetValue() -{ - // structure data need to be cached for rmaxFromPrecision - this->cacheStructureData(); - this->resizeValue(this->countSites()); - this->PairQuantity::resetValue(); +void BVSCalculator::resetValue() { + // structure data need to be cached for rmaxFromPrecision + this->cacheStructureData(); + this->resizeValue(this->countSites()); + this->PairQuantity::resetValue(); } - -void BVSCalculator::configureBondGenerator(BaseBondGenerator& bnds) const -{ - bnds.setRmax(this->getRmaxUsed()); +void BVSCalculator::configureBondGenerator(BaseBondGenerator& bnds) const { + bnds.setRmax(this->getRmaxUsed()); } - void BVSCalculator::addPairContribution(const BaseBondGenerator& bnds, - int summationscale) -{ - const string& a0 = mstructure_cache.baresymbols[bnds.site0()]; - const string& a1 = mstructure_cache.baresymbols[bnds.site1()]; - int v0 = mstructure_cache.valences[bnds.site0()]; - int v1 = mstructure_cache.valences[bnds.site1()]; - const BVParametersTable& bvtb = *(this->getBVParamTable()); - const BVParam& bp = bvtb.lookup(a0, v0, a1, v1); - // do nothing if there are no bond parameters for this pair - if (&bp == &bvtb.none()) return; - double valencehalf = bp.bondvalence(bnds.distance()) / 2.0; - int pm0 = (v0 >= 0) ? 1 : -1; - int pm1 = (v1 >= 0) ? 1 : -1; - const double& o0 = mstructure->siteOccupancy(bnds.site0()); - const double& o1 = mstructure->siteOccupancy(bnds.site1()); - mvalue[bnds.site0()] += summationscale * pm0 * valencehalf * o1; - mvalue[bnds.site1()] += summationscale * pm1 * valencehalf * o0; + int summationscale) { + const string& a0 = mstructure_cache.baresymbols[bnds.site0()]; + const string& a1 = mstructure_cache.baresymbols[bnds.site1()]; + int v0 = mstructure_cache.valences[bnds.site0()]; + int v1 = mstructure_cache.valences[bnds.site1()]; + const BVParametersTable& bvtb = *(this->getBVParamTable()); + const BVParam& bp = bvtb.lookup(a0, v0, a1, v1); + // do nothing if there are no bond parameters for this pair + if (&bp == &bvtb.none()) return; + double valencehalf = bp.bondvalence(bnds.distance()) / 2.0; + int pm0 = (v0 >= 0) ? 1 : -1; + int pm1 = (v1 >= 0) ? 1 : -1; + const double& o0 = mstructure->siteOccupancy(bnds.site0()); + const double& o1 = mstructure->siteOccupancy(bnds.site1()); + mvalue[bnds.site0()] += summationscale * pm0 * valencehalf * o1; + mvalue[bnds.site1()] += summationscale * pm1 * valencehalf * o0; } // Private Methods ----------------------------------------------------------- -void BVSCalculator::cacheStructureData() -{ - int cntsites = this->countSites(); - mstructure_cache.baresymbols.resize(cntsites); - mstructure_cache.valences.resize(cntsites); - const BVParametersTable& bvtb = *(this->getBVParamTable()); - for (int i = 0; i < cntsites; ++i) - { - const string& smbl = mstructure->siteAtomType(i); - mstructure_cache.baresymbols[i] = atomBareSymbol(smbl); - mstructure_cache.valences[i] = bvtb.getAtomValence(smbl); - } -} - - -double BVSCalculator::rmaxFromPrecision(double eps) const -{ - const BVParametersTable& bvtb = *(this->getBVParamTable()); - // build a set of unique (symbol, valence) pairs - typedef std::unordered_set< - pair, - boost::hash< pair > - > SymbolValenceSet; - SymbolValenceSet allsymvals; - assert(int(mstructure_cache.baresymbols.size()) == this->countSites()); - assert(int(mstructure_cache.valences.size()) == this->countSites()); - for (int i = 0; i < this->countSites(); ++i) - { - allsymvals.insert(make_pair(mstructure_cache.baresymbols[i], - mstructure_cache.valences[i])); - } - // find all used bond parameters - BVParametersTable::SetOfBVParam bpused; - SymbolValenceSet::const_iterator sv0, sv1; - for (sv0 = allsymvals.begin(); sv0 != allsymvals.end(); ++sv0) - { - for (sv1 = sv0; sv1 != allsymvals.end(); ++sv1) - { - const BVParam& bp = bvtb.lookup( - sv0->first, sv0->second, sv1->first, sv1->second); - bpused.insert(bp); - } - } - double rv = 0.0; - BVParametersTable::SetOfBVParam::iterator bpit; - for (bpit = bpused.begin(); bpit != bpused.end(); ++bpit) - { - rv = max(rv, bpit->bondvalenceToDistance(eps)); +void BVSCalculator::cacheStructureData() { + int cntsites = this->countSites(); + mstructure_cache.baresymbols.resize(cntsites); + mstructure_cache.valences.resize(cntsites); + const BVParametersTable& bvtb = *(this->getBVParamTable()); + for (int i = 0; i < cntsites; ++i) { + const string& smbl = mstructure->siteAtomType(i); + mstructure_cache.baresymbols[i] = atomBareSymbol(smbl); + mstructure_cache.valences[i] = bvtb.getAtomValence(smbl); + } +} + +double BVSCalculator::rmaxFromPrecision(double eps) const { + const BVParametersTable& bvtb = *(this->getBVParamTable()); + // build a set of unique (symbol, valence) pairs + typedef std::unordered_set, + boost::hash > > + SymbolValenceSet; + SymbolValenceSet allsymvals; + assert(int(mstructure_cache.baresymbols.size()) == this->countSites()); + assert(int(mstructure_cache.valences.size()) == this->countSites()); + for (int i = 0; i < this->countSites(); ++i) { + allsymvals.insert( + make_pair(mstructure_cache.baresymbols[i], mstructure_cache.valences[i])); + } + // find all used bond parameters + BVParametersTable::SetOfBVParam bpused; + SymbolValenceSet::const_iterator sv0, sv1; + for (sv0 = allsymvals.begin(); sv0 != allsymvals.end(); ++sv0) { + for (sv1 = sv0; sv1 != allsymvals.end(); ++sv1) { + const BVParam& bp = + bvtb.lookup(sv0->first, sv0->second, sv1->first, sv1->second); + bpused.insert(bp); } - return rv; + } + double rv = 0.0; + BVParametersTable::SetOfBVParam::iterator bpit; + for (bpit = bpused.begin(); bpit != bpused.end(); ++bpit) { + rv = max(rv, bpit->bondvalenceToDistance(eps)); + } + return rv; } -} // namespace srreal -} // namespace diffpy +} // namespace srreal +} // namespace diffpy // Serialization ------------------------------------------------------------- diff --git a/src/diffpy/srreal/BVSCalculator.hpp b/src/diffpy/srreal/BVSCalculator.hpp index d7cb6514..3bcd9ce6 100644 --- a/src/diffpy/srreal/BVSCalculator.hpp +++ b/src/diffpy/srreal/BVSCalculator.hpp @@ -1,20 +1,20 @@ /***************************************************************************** -* -* 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.txt for a list of people who contributed. -* See LICENSE_DANSE.txt for license information. -* -****************************************************************************** -* -* class BVSCalculator -- bond valence sums calculator -* -*****************************************************************************/ + * + * 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.txt for a list of people who contributed. + * See LICENSE_DANSE.txt for license information. + * + ****************************************************************************** + * + * class BVSCalculator -- bond valence sums calculator + * + *****************************************************************************/ #ifndef BVSCALCULATOR_HPP_INCLUDED #define BVSCALCULATOR_HPP_INCLUDED @@ -27,78 +27,73 @@ namespace diffpy { namespace srreal { -class DLL_EXPORT BVSCalculator : public PairQuantity -{ - public: - - // constructor - BVSCalculator(); - - // results - /// expected valence per each site - QuantityType valences() const; - /// difference between expected and calculated absolute valence at - /// each site. Positive for underbonding, negative for overbonding. - QuantityType bvdiff() const; - /// mean square difference of BVS from the expected values - double bvmsdiff() const; - /// root mean square difference of BVS from the expected values - double bvrmsdiff() const; - - // access and configuration of BVS parameters - void setBVParamTable(BVParametersTablePtr); - BVParametersTablePtr& getBVParamTable(); - const BVParametersTablePtr& getBVParamTable() const; - - // R-range configuration using the valence precision - /// set cutoff value for bond valence contributions - void setValencePrecision(double); - /// return cutoff value for bond valence contributions - double getValencePrecision() const; - /// effective rmax value, where valence contributions become smaller - /// than the valence precision cutoff. Always less or equal to rmax. - double getRmaxUsed() const; - - protected: - - // PairQuantity overloads - virtual void resetValue(); - virtual void configureBondGenerator(BaseBondGenerator&) const; - virtual void addPairContribution(const BaseBondGenerator&, int); - - private: - - // methods - void cacheStructureData(); - /// rmax necessary for achieving the specified valence precision - double rmaxFromPrecision(double) const; - - // data - // configuration - BVParametersTablePtr mbvptable; - double mvalenceprecision; - // cache - struct { - std::vector baresymbols; - std::vector valences; - } mstructure_cache; - - // serialization - friend class boost::serialization::access; - template - void serialize(Archive& ar, const unsigned int version) - { - ar & boost::serialization::base_object(*this); - ar & mbvptable; - ar & mvalenceprecision; - ar & mstructure_cache.baresymbols; - ar & mstructure_cache.valences; - } +class DLL_EXPORT BVSCalculator : public PairQuantity { + public: + // constructor + BVSCalculator(); + + // results + /// expected valence per each site + QuantityType valences() const; + /// difference between expected and calculated absolute valence at + /// each site. Positive for underbonding, negative for overbonding. + QuantityType bvdiff() const; + /// mean square difference of BVS from the expected values + double bvmsdiff() const; + /// root mean square difference of BVS from the expected values + double bvrmsdiff() const; + + // access and configuration of BVS parameters + void setBVParamTable(BVParametersTablePtr); + BVParametersTablePtr& getBVParamTable(); + const BVParametersTablePtr& getBVParamTable() const; + + // R-range configuration using the valence precision + /// set cutoff value for bond valence contributions + void setValencePrecision(double); + /// return cutoff value for bond valence contributions + double getValencePrecision() const; + /// effective rmax value, where valence contributions become smaller + /// than the valence precision cutoff. Always less or equal to rmax. + double getRmaxUsed() const; + + protected: + // PairQuantity overloads + virtual void resetValue(); + virtual void configureBondGenerator(BaseBondGenerator&) const; + virtual void addPairContribution(const BaseBondGenerator&, int); + + private: + // methods + void cacheStructureData(); + /// rmax necessary for achieving the specified valence precision + double rmaxFromPrecision(double) const; + + // data + // configuration + BVParametersTablePtr mbvptable; + double mvalenceprecision; + // cache + struct { + std::vector baresymbols; + std::vector valences; + } mstructure_cache; + + // serialization + friend class boost::serialization::access; + template + void serialize(Archive& ar, const unsigned int version) { + ar& boost::serialization::base_object(*this); + ar & mbvptable; + ar & mvalenceprecision; + ar & mstructure_cache.baresymbols; + ar & mstructure_cache.valences; + } }; // class BVSCalculator -} // namespace srreal -} // namespace diffpy +} // namespace srreal +} // namespace diffpy // Serialization ------------------------------------------------------------- diff --git a/src/diffpy/srreal/BaseBondGenerator.cpp b/src/diffpy/srreal/BaseBondGenerator.cpp index 6fa3b002..e05a9f60 100644 --- a/src/diffpy/srreal/BaseBondGenerator.cpp +++ b/src/diffpy/srreal/BaseBondGenerator.cpp @@ -1,21 +1,21 @@ /***************************************************************************** -* -* 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.txt for a list of people who contributed. -* See LICENSE_DANSE.txt for license information. -* -****************************************************************************** -* -* class BaseBondGenerator -- semi-abstract class for generation -* of all atom pairs containing specified anchor atom. -* -*****************************************************************************/ + * + * 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.txt for a list of people who contributed. + * See LICENSE_DANSE.txt for license information. + * + ****************************************************************************** + * + * class BaseBondGenerator -- semi-abstract class for generation + * of all atom pairs containing specified anchor atom. + * + *****************************************************************************/ #include #include @@ -26,253 +26,175 @@ using diffpy::mathutils::eps_eq; namespace diffpy { namespace srreal { -//using namespace std; +// using namespace std; // Constructor --------------------------------------------------------------- -BaseBondGenerator::BaseBondGenerator(StructureAdapterConstPtr stru) : - msite_anchor(0), - mrmin(0.0), - mrmax(DEFAULT_BONDGENERATOR_RMAX), - mstructure(stru), - mr0(R3::zerovector), - mr1(R3::zerovector), - mr01(R3::zerovector), - mdistance(0.0) -{ - int cnt = stru->countSites(); - msite_all.resize(cnt); - for (int i = 0; i != cnt; ++i) msite_all[i] = i; - msite_first = msite_all.begin(); - msite_last = msite_all.end(); - this->setFinishedFlag(); +BaseBondGenerator::BaseBondGenerator(StructureAdapterConstPtr stru) + : msite_anchor(0), + mrmin(0.0), + mrmax(DEFAULT_BONDGENERATOR_RMAX), + mstructure(stru), + mr0(R3::zerovector), + mr1(R3::zerovector), + mr01(R3::zerovector), + mdistance(0.0) { + int cnt = stru->countSites(); + msite_all.resize(cnt); + for (int i = 0; i != cnt; ++i) msite_all[i] = i; + msite_first = msite_all.begin(); + msite_last = msite_all.end(); + this->setFinishedFlag(); } // Public Methods ------------------------------------------------------------ // loop control -void BaseBondGenerator::rewind() -{ - msite_current = msite_first; - // avoid calling rewindSymmetry at an invalid site - if (this->finished()) return; - this->rewindSymmetry(); - this->advanceWhileInvalid(); +void BaseBondGenerator::rewind() { + msite_current = msite_first; + // avoid calling rewindSymmetry at an invalid site + if (this->finished()) return; + this->rewindSymmetry(); + this->advanceWhileInvalid(); } - -void BaseBondGenerator::next() -{ - this->getNextBond(); - this->advanceWhileInvalid(); +void BaseBondGenerator::next() { + this->getNextBond(); + this->advanceWhileInvalid(); } - -bool BaseBondGenerator::finished() const -{ - return msite_current >= msite_last; -} +bool BaseBondGenerator::finished() const { return msite_current >= msite_last; } // configuration -void BaseBondGenerator::selectAnchorSite(int anchor) -{ - assert(0 <= anchor && anchor < mstructure->countSites()); - msite_anchor = anchor; - mr0 = mstructure->siteCartesianPosition(msite_anchor); - this->setFinishedFlag(); +void BaseBondGenerator::selectAnchorSite(int anchor) { + assert(0 <= anchor && anchor < mstructure->countSites()); + msite_anchor = anchor; + mr0 = mstructure->siteCartesianPosition(msite_anchor); + this->setFinishedFlag(); } - -void BaseBondGenerator::selectSiteRange(int first, int last) -{ - assert(0 <= first); - assert(last <= mstructure->countSites()); - msite_first = msite_all.begin() + first; - msite_last = msite_all.begin() + last; - this->setFinishedFlag(); +void BaseBondGenerator::selectSiteRange(int first, int last) { + assert(0 <= first); + assert(last <= mstructure->countSites()); + msite_first = msite_all.begin() + first; + msite_last = msite_all.begin() + last; + this->setFinishedFlag(); } - -void BaseBondGenerator::selectSites(const SiteIndices& selection) -{ - msite_selection = selection; - msite_first = msite_selection.begin(); - msite_last = msite_selection.end(); - this->setFinishedFlag(); +void BaseBondGenerator::selectSites(const SiteIndices& selection) { + msite_selection = selection; + msite_first = msite_selection.begin(); + msite_last = msite_selection.end(); + this->setFinishedFlag(); } - -void BaseBondGenerator::selectSites( - SiteIndices::const_iterator first, - SiteIndices::const_iterator last) -{ - msite_first = first; - msite_last = last; - this->setFinishedFlag(); +void BaseBondGenerator::selectSites(SiteIndices::const_iterator first, + SiteIndices::const_iterator last) { + msite_first = first; + msite_last = last; + this->setFinishedFlag(); } - -void BaseBondGenerator::setRmin(double rmin) -{ - if (rmin != mrmin) this->setFinishedFlag(); - mrmin = rmin; +void BaseBondGenerator::setRmin(double rmin) { + if (rmin != mrmin) this->setFinishedFlag(); + mrmin = rmin; } - -void BaseBondGenerator::setRmax(double rmax) -{ - if (rmax != mrmax) this->setFinishedFlag(); - mrmax = rmax; +void BaseBondGenerator::setRmax(double rmax) { + if (rmax != mrmax) this->setFinishedFlag(); + mrmax = rmax; } // data query -const double& BaseBondGenerator::getRmin() const -{ - return mrmin; -} - - -const double& BaseBondGenerator::getRmax() const -{ - return mrmax; -} - - -int BaseBondGenerator::site0() const -{ - return msite_anchor; -} - - -int BaseBondGenerator::site1() const -{ - return *msite_current; -} +const double& BaseBondGenerator::getRmin() const { return mrmin; } +const double& BaseBondGenerator::getRmax() const { return mrmax; } -int BaseBondGenerator::multiplicity() const -{ - return mstructure->siteMultiplicity(this->site0()); -} +int BaseBondGenerator::site0() const { return msite_anchor; } +int BaseBondGenerator::site1() const { return *msite_current; } -const R3::Vector& BaseBondGenerator::r0() const -{ - return mr0; +int BaseBondGenerator::multiplicity() const { + return mstructure->siteMultiplicity(this->site0()); } +const R3::Vector& BaseBondGenerator::r0() const { return mr0; } -const R3::Vector& BaseBondGenerator::r1() const -{ - return mr1; -} +const R3::Vector& BaseBondGenerator::r1() const { return mr1; } +const double& BaseBondGenerator::distance() const { return mdistance; } -const double& BaseBondGenerator::distance() const -{ - return mdistance; -} - +const R3::Vector& BaseBondGenerator::r01() const { return mr01; } -const R3::Vector& BaseBondGenerator::r01() const -{ - return mr01; +const R3::Matrix& BaseBondGenerator::Ucartesian0() const { + return mstructure->siteCartesianUij(this->site0()); } - -const R3::Matrix& BaseBondGenerator::Ucartesian0() const -{ - return mstructure->siteCartesianUij(this->site0()); -} - - -const R3::Matrix& BaseBondGenerator::Ucartesian1() const -{ - return mstructure->siteCartesianUij(this->site1()); +const R3::Matrix& BaseBondGenerator::Ucartesian1() const { + return mstructure->siteCartesianUij(this->site1()); } - -double BaseBondGenerator::msd() const -{ - static R3::Vector s; - s = this->r01(); - double msd0 = meanSquareDisplacement(this->Ucartesian0(), s, - mstructure->siteAnisotropy(this->site0())); - double msd1 = meanSquareDisplacement(this->Ucartesian1(), s, - mstructure->siteAnisotropy(this->site1())); - double rv = msd0 + msd1; - return rv; +double BaseBondGenerator::msd() const { + static R3::Vector s; + s = this->r01(); + double msd0 = meanSquareDisplacement( + this->Ucartesian0(), s, mstructure->siteAnisotropy(this->site0())); + double msd1 = meanSquareDisplacement( + this->Ucartesian1(), s, mstructure->siteAnisotropy(this->site1())); + double rv = msd0 + msd1; + return rv; } // Protected Methods --------------------------------------------------------- -bool BaseBondGenerator::iterateSymmetry() -{ - return false; -} - +bool BaseBondGenerator::iterateSymmetry() { return false; } -void BaseBondGenerator::rewindSymmetry() -{ - mr1 = mstructure->siteCartesianPosition(this->site1()); - this->updateDistance(); +void BaseBondGenerator::rewindSymmetry() { + mr1 = mstructure->siteCartesianPosition(this->site1()); + this->updateDistance(); } - -void BaseBondGenerator::getNextBond() -{ - if (this->iterateSymmetry()) return; - ++msite_current; - // avoid calling rewindSymmetry at an invalid site - if (!(this->finished())) this->rewindSymmetry(); +void BaseBondGenerator::getNextBond() { + if (this->iterateSymmetry()) return; + ++msite_current; + // avoid calling rewindSymmetry at an invalid site + if (!(this->finished())) this->rewindSymmetry(); } - -void BaseBondGenerator::updateDistance() -{ - if ((mdistance = fabs(mr01[0] = mr1[0] - mr0[0])) > mrmax) return; - if ((mdistance = fabs(mr01[1] = mr1[1] - mr0[1])) > mrmax) return; - if ((mdistance = fabs(mr01[2] = mr1[2] - mr0[2])) > mrmax) return; - mdistance = R3::norm(mr01); +void BaseBondGenerator::updateDistance() { + if ((mdistance = fabs(mr01[0] = mr1[0] - mr0[0])) > mrmax) return; + if ((mdistance = fabs(mr01[1] = mr1[1] - mr0[1])) > mrmax) return; + if ((mdistance = fabs(mr01[2] = mr1[2] - mr0[2])) > mrmax) return; + mdistance = R3::norm(mr01); } // Private Methods ----------------------------------------------------------- -void BaseBondGenerator::advanceWhileInvalid() -{ - while (!this->finished() && - (this->bondOutOfRange() || this->atSelfPair())) - { - this->getNextBond(); - } +void BaseBondGenerator::advanceWhileInvalid() { + while (!this->finished() && (this->bondOutOfRange() || this->atSelfPair())) { + this->getNextBond(); + } } // Private Methods ----------------------------------------------------------- -bool BaseBondGenerator::bondOutOfRange() const -{ - const double& d = this->distance(); - bool rv = (d < this->getRmin()) || (d > this->getRmax()); - return rv; +bool BaseBondGenerator::bondOutOfRange() const { + const double& d = this->distance(); + bool rv = (d < this->getRmin()) || (d > this->getRmax()); + return rv; } - -bool BaseBondGenerator::atSelfPair() const -{ - bool rv = eps_eq(this->distance(), 0.0); - return rv; +bool BaseBondGenerator::atSelfPair() const { + bool rv = eps_eq(this->distance(), 0.0); + return rv; } +void BaseBondGenerator::setFinishedFlag() { msite_current = msite_last; } -void BaseBondGenerator::setFinishedFlag() -{ - msite_current = msite_last; -} - -} // namespace srreal -} // namespace diffpy +} // namespace srreal +} // namespace diffpy // End of file diff --git a/src/diffpy/srreal/BaseBondGenerator.hpp b/src/diffpy/srreal/BaseBondGenerator.hpp index 849cdd25..435e1510 100644 --- a/src/diffpy/srreal/BaseBondGenerator.hpp +++ b/src/diffpy/srreal/BaseBondGenerator.hpp @@ -1,21 +1,21 @@ /***************************************************************************** -* -* 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.txt for a list of people who contributed. -* See LICENSE_DANSE.txt for license information. -* -****************************************************************************** -* -* class BaseBondGenerator -- semi-abstract class for a generation -* of all atom pairs containing specified anchor atom. -* -*****************************************************************************/ + * + * 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.txt for a list of people who contributed. + * See LICENSE_DANSE.txt for license information. + * + ****************************************************************************** + * + * class BaseBondGenerator -- semi-abstract class for a generation + * of all atom pairs containing specified anchor atom. + * + *****************************************************************************/ #ifndef BASEBONDGENERATOR_HPP_INCLUDED #define BASEBONDGENERATOR_HPP_INCLUDED @@ -31,79 +31,72 @@ namespace srreal { /// Use zero default for rmax so any misconfiguration of r-limits is obvious. const double DEFAULT_BONDGENERATOR_RMAX = 0.0; -class DLL_EXPORT BaseBondGenerator -{ - public: - - // constructor - BaseBondGenerator(StructureAdapterConstPtr); - virtual ~BaseBondGenerator() { } - - // methods - // loop control - virtual void rewind(); - bool finished() const; - void next(); - - // configuration - virtual void selectAnchorSite(int); - void selectSiteRange(int first, int last); - void selectSites(const SiteIndices&); - void selectSites( - SiteIndices::const_iterator first, - SiteIndices::const_iterator last); - virtual void setRmin(double); - virtual void setRmax(double); - - // get data - const double& getRmin() const; - const double& getRmax() const; - int site0() const; - int site1() const; - int multiplicity() const; - const R3::Vector& r0() const; - const R3::Vector& r1() const; - const double& distance() const; - const R3::Vector& r01() const; - virtual const R3::Matrix& Ucartesian0() const; - virtual const R3::Matrix& Ucartesian1() const; - double msd() const; - - protected: - - // data - int msite_anchor; - SiteIndices::const_iterator msite_first; - SiteIndices::const_iterator msite_last; - SiteIndices::const_iterator msite_current; - double mrmin; - double mrmax; - StructureAdapterConstPtr mstructure; - R3::Vector mr0; - R3::Vector mr1; - R3::Vector mr01; - double mdistance; - SiteIndices msite_all; - SiteIndices msite_selection; - - // methods - virtual bool iterateSymmetry(); - virtual void rewindSymmetry(); - virtual void getNextBond(); - void updateDistance(); - - private: - - // methods - void advanceWhileInvalid(); - bool bondOutOfRange() const; - bool atSelfPair() const; - void setFinishedFlag(); - +class DLL_EXPORT BaseBondGenerator { + public: + // constructor + BaseBondGenerator(StructureAdapterConstPtr); + virtual ~BaseBondGenerator() {} + + // methods + // loop control + virtual void rewind(); + bool finished() const; + void next(); + + // configuration + virtual void selectAnchorSite(int); + void selectSiteRange(int first, int last); + void selectSites(const SiteIndices&); + void selectSites(SiteIndices::const_iterator first, + SiteIndices::const_iterator last); + virtual void setRmin(double); + virtual void setRmax(double); + + // get data + const double& getRmin() const; + const double& getRmax() const; + int site0() const; + int site1() const; + int multiplicity() const; + const R3::Vector& r0() const; + const R3::Vector& r1() const; + const double& distance() const; + const R3::Vector& r01() const; + virtual const R3::Matrix& Ucartesian0() const; + virtual const R3::Matrix& Ucartesian1() const; + double msd() const; + + protected: + // data + int msite_anchor; + SiteIndices::const_iterator msite_first; + SiteIndices::const_iterator msite_last; + SiteIndices::const_iterator msite_current; + double mrmin; + double mrmax; + StructureAdapterConstPtr mstructure; + R3::Vector mr0; + R3::Vector mr1; + R3::Vector mr01; + double mdistance; + SiteIndices msite_all; + SiteIndices msite_selection; + + // methods + virtual bool iterateSymmetry(); + virtual void rewindSymmetry(); + virtual void getNextBond(); + void updateDistance(); + + private: + // methods + void advanceWhileInvalid(); + bool bondOutOfRange() const; + bool atSelfPair() const; + void setFinishedFlag(); }; - -} // namespace srreal -} // namespace diffpy +} // namespace srreal +} // namespace diffpy #endif // BASEBONDGENERATOR_HPP_INCLUDED diff --git a/src/diffpy/srreal/BaseDebyeSum.cpp b/src/diffpy/srreal/BaseDebyeSum.cpp index e4165694..1fdfc7e8 100644 --- a/src/diffpy/srreal/BaseDebyeSum.cpp +++ b/src/diffpy/srreal/BaseDebyeSum.cpp @@ -1,20 +1,20 @@ /***************************************************************************** -* -* 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.txt for a list of people who contributed. -* See LICENSE_DANSE.txt for license information. -* -****************************************************************************** -* -* class BaseDebyeSum -- base class for concrete Debye sum calculators -* -*****************************************************************************/ + * + * 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.txt for a list of people who contributed. + * See LICENSE_DANSE.txt for license information. + * + ****************************************************************************** + * + * class BaseDebyeSum -- base class for concrete Debye sum calculators + * + *****************************************************************************/ #include #include @@ -29,8 +29,8 @@ using namespace std; using namespace diffpy::validators; -using diffpy::mathutils::eps_gt; using diffpy::mathutils::eps_eq; +using diffpy::mathutils::eps_gt; namespace diffpy { namespace srreal { @@ -42,256 +42,205 @@ namespace { /// Default cutoff for the Q-decreasing scale of the sine contributions. const double DEFAULT_DEBYE_PRECISION = 1e-6; -} // namespace +} // namespace // Constructor --------------------------------------------------------------- -BaseDebyeSum::BaseDebyeSum() : - mqmin(0.0), - mqmax(DEFAULT_QGRID_QMAX), - mqstep(DEFAULT_QGRID_QSTEP), - mdebyeprecision(DEFAULT_DEBYE_PRECISION) -{ - mstructure_cache.totaloccupancy = 0.0; - // default configuration - this->setPeakWidthModelByType("jeong"); - this->setEvaluatorType(OPTIMIZED); - // attributes - this->registerDoubleAttribute("debyeprecision", this, - &BaseDebyeSum::getDebyePrecision, - &BaseDebyeSum::setDebyePrecision); +BaseDebyeSum::BaseDebyeSum() + : mqmin(0.0), + mqmax(DEFAULT_QGRID_QMAX), + mqstep(DEFAULT_QGRID_QSTEP), + mdebyeprecision(DEFAULT_DEBYE_PRECISION) { + mstructure_cache.totaloccupancy = 0.0; + // default configuration + this->setPeakWidthModelByType("jeong"); + this->setEvaluatorType(OPTIMIZED); + // attributes + this->registerDoubleAttribute("debyeprecision", this, + &BaseDebyeSum::getDebyePrecision, + &BaseDebyeSum::setDebyePrecision); } // Public Methods ------------------------------------------------------------ // PairQuantity overloads -eventticker::EventTicker& BaseDebyeSum::ticker() const -{ - mticker.updateFrom(this->PeakWidthModelOwner::ticker()); - return mticker; +eventticker::EventTicker& BaseDebyeSum::ticker() const { + mticker.updateFrom(this->PeakWidthModelOwner::ticker()); + return mticker; } // results -QuantityType BaseDebyeSum::getF() const -{ - QuantityType rv = this->value(); - const double& totocc = mstructure_cache.totaloccupancy; - const int npts = pdfutils_qmaxSteps(this); - for (int kq = pdfutils_qminSteps(this); kq < npts; ++kq) - { - double sfavg = this->sfAverageAtkQ(kq); - double fscale = (sfavg * totocc) == 0 ? 0.0 : - 1.0 / (sfavg * sfavg * totocc); - rv[kq] *= fscale; - } - return rv; +QuantityType BaseDebyeSum::getF() const { + QuantityType rv = this->value(); + const double& totocc = mstructure_cache.totaloccupancy; + const int npts = pdfutils_qmaxSteps(this); + for (int kq = pdfutils_qminSteps(this); kq < npts; ++kq) { + double sfavg = this->sfAverageAtkQ(kq); + double fscale = + (sfavg * totocc) == 0 ? 0.0 : 1.0 / (sfavg * sfavg * totocc); + rv[kq] *= fscale; + } + return rv; } // Q-range methods -QuantityType BaseDebyeSum::getQgrid() const -{ - return pdfutils_getQgrid(this); -} +QuantityType BaseDebyeSum::getQgrid() const { return pdfutils_getQgrid(this); } // Q-range configuration -void BaseDebyeSum::setQmin(double qmin) -{ - ensureNonNegative("Qmin", qmin); - if (mqmin != qmin) mticker.click(); - mqmin = qmin; -} - - -const double& BaseDebyeSum::getQmin() const -{ - return mqmin; -} - - -void BaseDebyeSum::setQmax(double qmax) -{ - ensureNonNegative("Qmax", qmax); - if (mqmax != qmax) mticker.click(); - mqmax = qmax; -} - - -const double& BaseDebyeSum::getQmax() const -{ - return mqmax; +void BaseDebyeSum::setQmin(double qmin) { + ensureNonNegative("Qmin", qmin); + if (mqmin != qmin) mticker.click(); + mqmin = qmin; } +const double& BaseDebyeSum::getQmin() const { return mqmin; } -void BaseDebyeSum::setQstep(double qstep) -{ - ensureEpsilonPositive("Qstep", qstep); - if (mqstep != qstep) mticker.click(); - mqstep = qstep; +void BaseDebyeSum::setQmax(double qmax) { + ensureNonNegative("Qmax", qmax); + if (mqmax != qmax) mticker.click(); + mqmax = qmax; } +const double& BaseDebyeSum::getQmax() const { return mqmax; } -const double& BaseDebyeSum::getQstep() const -{ - return mqstep; +void BaseDebyeSum::setQstep(double qstep) { + ensureEpsilonPositive("Qstep", qstep); + if (mqstep != qstep) mticker.click(); + mqstep = qstep; } +const double& BaseDebyeSum::getQstep() const { return mqstep; } -void BaseDebyeSum::setDebyePrecision(double precision) -{ - if (mdebyeprecision != precision) mticker.click(); - mdebyeprecision = precision; +void BaseDebyeSum::setDebyePrecision(double precision) { + if (mdebyeprecision != precision) mticker.click(); + mdebyeprecision = precision; } - -const double& BaseDebyeSum::getDebyePrecision() const -{ - return mdebyeprecision; +const double& BaseDebyeSum::getDebyePrecision() const { + return mdebyeprecision; } // Protected Methods --------------------------------------------------------- // PairQuantity overloads -void BaseDebyeSum::resetValue() -{ - this->cacheStructureData(); - this->resizeValue(pdfutils_qmaxSteps(this)); - this->PairQuantity::resetValue(); +void BaseDebyeSum::resetValue() { + this->cacheStructureData(); + this->resizeValue(pdfutils_qmaxSteps(this)); + this->PairQuantity::resetValue(); } - void BaseDebyeSum::addPairContribution(const BaseBondGenerator& bnds, - int summationscale) -{ - const double dist = bnds.distance(); - if (eps_eq(0.0, dist)) return; - // calculate sigma parameter for the Debye-Waller dampign Gaussian - const double fwhm = this->getPeakWidthModel()->calculate(bnds); - const double fwhmtosigma = 1.0 / (2 * sqrt(2 * M_LN2)); - const double dwsigma = fwhmtosigma * fwhm; - const int nqpts = pdfutils_qmaxSteps(this); - const int smscale = summationscale * bnds.multiplicity(); - const double& sineprec = this->getDebyePrecision(); - for (int kq = pdfutils_qminSteps(this); kq < nqpts; ++kq) - { - const double q = kq * this->getQstep(); - const double dwscale = exp(-0.5 * pow(dwsigma * q, 2)); - const double sinescale = smscale * dwscale * - this->sfSiteAtkQ(bnds.site0(), kq) * - this->sfSiteAtkQ(bnds.site1(), kq) / dist; - if (eps_eq(0.0, sinescale, sineprec)) break; - mvalue[kq] += sinescale * sin(q * dist); - } -} - - -void BaseDebyeSum::stashPartialValue() -{ - mdbsumstash = this->value(); -} - - -void BaseDebyeSum::restorePartialValue() -{ - assert(mdbsumstash.size() == mvalue.size()); - mvalue.swap(mdbsumstash); - mdbsumstash.clear(); -} - - -double BaseDebyeSum::sfSiteAtQ(int siteidx, const double& q) const -{ - return 1.0; + int summationscale) { + const double dist = bnds.distance(); + if (eps_eq(0.0, dist)) return; + // calculate sigma parameter for the Debye-Waller dampign Gaussian + const double fwhm = this->getPeakWidthModel()->calculate(bnds); + const double fwhmtosigma = 1.0 / (2 * sqrt(2 * M_LN2)); + const double dwsigma = fwhmtosigma * fwhm; + const int nqpts = pdfutils_qmaxSteps(this); + const int smscale = summationscale * bnds.multiplicity(); + const double& sineprec = this->getDebyePrecision(); + for (int kq = pdfutils_qminSteps(this); kq < nqpts; ++kq) { + const double q = kq * this->getQstep(); + const double dwscale = exp(-0.5 * pow(dwsigma * q, 2)); + const double sinescale = smscale * dwscale * + this->sfSiteAtkQ(bnds.site0(), kq) * + this->sfSiteAtkQ(bnds.site1(), kq) / dist; + if (eps_eq(0.0, sinescale, sineprec)) break; + mvalue[kq] += sinescale * sin(q * dist); + } +} + +void BaseDebyeSum::stashPartialValue() { mdbsumstash = this->value(); } + +void BaseDebyeSum::restorePartialValue() { + assert(mdbsumstash.size() == mvalue.size()); + mvalue.swap(mdbsumstash); + mdbsumstash.clear(); +} + +double BaseDebyeSum::sfSiteAtQ(int siteidx, const double& q) const { + return 1.0; } // Private Methods ----------------------------------------------------------- -double BaseDebyeSum::sfSiteAtkQ(int siteidx, int kq) const -{ - assert(0 <= siteidx && siteidx < int(mstructure_cache.typeofsite.size())); - int typeidx = mstructure_cache.typeofsite[siteidx]; - assert(typeidx < int(mstructure_cache.sftypeatkq.size())); - const QuantityType& sfarray = mstructure_cache.sftypeatkq[typeidx]; - assert(0 <= kq && kq < int(sfarray.size())); - return sfarray[kq]; -} - - -double BaseDebyeSum::sfAverageAtkQ(int kq) const -{ - assert(kq < int(mstructure_cache.sfaverageatkq.size())); - return mstructure_cache.sfaverageatkq[kq]; -} - - -void BaseDebyeSum::cacheStructureData() -{ - using std::placeholders::_1; - int cntsites = this->countSites(); - const int nqpts = pdfutils_qmaxSteps(this); - QuantityType zeros(nqpts, 0.0); - unordered_map atomtypeidx; - // sftypeatkq - mstructure_cache.typeofsite.clear(); - mstructure_cache.typeofsite.reserve(cntsites); - mstructure_cache.sftypeatkq.clear(); - for (int siteidx = 0; siteidx < cntsites; ++siteidx) - { - const string& smbl = mstructure->siteAtomType(siteidx); - if (!atomtypeidx.count(smbl)) - { - atomtypeidx.insert(make_pair(smbl, int(atomtypeidx.size()))); - } - int tpidx = atomtypeidx[smbl]; - mstructure_cache.typeofsite.push_back(tpidx); - // do nothing if the type has been already cached - if (tpidx < int(mstructure_cache.sftypeatkq.size())) continue; - assert(tpidx == int(mstructure_cache.sftypeatkq.size())); - // here we need to build a new array - mstructure_cache.sftypeatkq.push_back(zeros); - QuantityType& sfarray = mstructure_cache.sftypeatkq.back(); - for (int kq = pdfutils_qminSteps(this); kq < nqpts; ++kq) - { - double q = this->getQstep() * kq; - sfarray[kq] = this->sfSiteAtQ(siteidx, q); - } +double BaseDebyeSum::sfSiteAtkQ(int siteidx, int kq) const { + assert(0 <= siteidx && siteidx < int(mstructure_cache.typeofsite.size())); + int typeidx = mstructure_cache.typeofsite[siteidx]; + assert(typeidx < int(mstructure_cache.sftypeatkq.size())); + const QuantityType& sfarray = mstructure_cache.sftypeatkq[typeidx]; + assert(0 <= kq && kq < int(sfarray.size())); + return sfarray[kq]; +} + +double BaseDebyeSum::sfAverageAtkQ(int kq) const { + assert(kq < int(mstructure_cache.sfaverageatkq.size())); + return mstructure_cache.sfaverageatkq[kq]; +} + +void BaseDebyeSum::cacheStructureData() { + using std::placeholders::_1; + int cntsites = this->countSites(); + const int nqpts = pdfutils_qmaxSteps(this); + QuantityType zeros(nqpts, 0.0); + unordered_map atomtypeidx; + // sftypeatkq + mstructure_cache.typeofsite.clear(); + mstructure_cache.typeofsite.reserve(cntsites); + mstructure_cache.sftypeatkq.clear(); + for (int siteidx = 0; siteidx < cntsites; ++siteidx) { + const string& smbl = mstructure->siteAtomType(siteidx); + if (!atomtypeidx.count(smbl)) { + atomtypeidx.insert(make_pair(smbl, int(atomtypeidx.size()))); } - assert(cntsites == int(mstructure_cache.typeofsite.size())); - assert(atomtypeidx.size() == mstructure_cache.sftypeatkq.size()); - // totaloccupancy - mstructure_cache.totaloccupancy = mstructure->totalOccupancy(); - // sfaverageatkq - QuantityType& sfak = mstructure_cache.sfaverageatkq; - sfak = zeros; - int ntps = mstructure_cache.sftypeatkq.size(); - vector tpmultipl(ntps, 0); - for (int siteidx = 0; siteidx < cntsites; ++siteidx) - { - int tpidx = mstructure_cache.typeofsite[siteidx]; - assert(tpidx < ntps); - tpmultipl[tpidx] += mstructure->siteMultiplicity(siteidx); + int tpidx = atomtypeidx[smbl]; + mstructure_cache.typeofsite.push_back(tpidx); + // do nothing if the type has been already cached + if (tpidx < int(mstructure_cache.sftypeatkq.size())) continue; + assert(tpidx == int(mstructure_cache.sftypeatkq.size())); + // here we need to build a new array + mstructure_cache.sftypeatkq.push_back(zeros); + QuantityType& sfarray = mstructure_cache.sftypeatkq.back(); + for (int kq = pdfutils_qminSteps(this); kq < nqpts; ++kq) { + double q = this->getQstep() * kq; + sfarray[kq] = this->sfSiteAtQ(siteidx, q); } - for (int tpidx = 0; tpidx < ntps; ++tpidx) - { - QuantityType& sfarray = mstructure_cache.sftypeatkq[tpidx]; - const int multipl = tpmultipl[tpidx]; - for (int kq = pdfutils_qminSteps(this); kq < nqpts; ++kq) - { - sfak[kq] += sfarray[kq] * multipl; - } + } + assert(cntsites == int(mstructure_cache.typeofsite.size())); + assert(atomtypeidx.size() == mstructure_cache.sftypeatkq.size()); + // totaloccupancy + mstructure_cache.totaloccupancy = mstructure->totalOccupancy(); + // sfaverageatkq + QuantityType& sfak = mstructure_cache.sfaverageatkq; + sfak = zeros; + int ntps = mstructure_cache.sftypeatkq.size(); + vector tpmultipl(ntps, 0); + for (int siteidx = 0; siteidx < cntsites; ++siteidx) { + int tpidx = mstructure_cache.typeofsite[siteidx]; + assert(tpidx < ntps); + tpmultipl[tpidx] += mstructure->siteMultiplicity(siteidx); + } + for (int tpidx = 0; tpidx < ntps; ++tpidx) { + QuantityType& sfarray = mstructure_cache.sftypeatkq[tpidx]; + const int multipl = tpmultipl[tpidx]; + for (int kq = pdfutils_qminSteps(this); kq < nqpts; ++kq) { + sfak[kq] += sfarray[kq] * multipl; } - const double& totocc = mstructure_cache.totaloccupancy; - const double tosc = eps_gt(totocc, 0.0) ? (1.0 / totocc) : 1.0; - transform(sfak.begin(), sfak.end(), sfak.begin(), + } + const double& totocc = mstructure_cache.totaloccupancy; + const double tosc = eps_gt(totocc, 0.0) ? (1.0 / totocc) : 1.0; + transform(sfak.begin(), sfak.end(), sfak.begin(), bind(multiplies(), tosc, _1)); } -} // namespace srreal -} // namespace diffpy +} // namespace srreal +} // namespace diffpy // Serialization ------------------------------------------------------------- diff --git a/src/diffpy/srreal/BaseDebyeSum.hpp b/src/diffpy/srreal/BaseDebyeSum.hpp index ac608fcc..e1ed7248 100644 --- a/src/diffpy/srreal/BaseDebyeSum.hpp +++ b/src/diffpy/srreal/BaseDebyeSum.hpp @@ -1,20 +1,20 @@ /***************************************************************************** -* -* 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.txt for a list of people who contributed. -* See LICENSE_DANSE.txt for license information. -* -****************************************************************************** -* -* class BaseDebyeSum -- base class for concrete Debye sum calculators -* -*****************************************************************************/ + * + * 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.txt for a list of people who contributed. + * See LICENSE_DANSE.txt for license information. + * + ****************************************************************************** + * + * class BaseDebyeSum -- base class for concrete Debye sum calculators + * + *****************************************************************************/ #ifndef BASEDEBYESUM_HPP_INCLUDED #define BASEDEBYESUM_HPP_INCLUDED @@ -28,95 +28,89 @@ namespace diffpy { namespace srreal { -class DLL_EXPORT BaseDebyeSum : - public PairQuantity, - public PeakWidthModelOwner -{ - public: - - // constructor - BaseDebyeSum(); - - // PairQuantity overloads - virtual eventticker::EventTicker& ticker() const; - - // results - /// F values on a full Q-grid starting at 0 - QuantityType getF() const; - - // Q-range methods - /// Full Q-grid starting at 0 - QuantityType getQgrid() const; - // Q-range configuration - void setQmin(double); - const double& getQmin() const; - void setQmax(double); - const double& getQmax() const; - void setQstep(double); - const double& getQstep() const; - - // Summation cutoff due to Q-dependent pair scaling or large distance - /// set relative cutoff value for Debye sum contribution - void setDebyePrecision(double); - /// return relative cutoff value for Debye sum contribution - const double& getDebyePrecision() const; - - protected: - - // PairQuantity overloads - virtual void resetValue(); - virtual void addPairContribution(const BaseBondGenerator&, int); - // support for PQEvaluatorOptimized - virtual void stashPartialValue(); - virtual void restorePartialValue(); - - // own methods - virtual double sfSiteAtQ(int, const double& Q) const; - - private: - - // methods - /// cache structure factors data for a quick access during summation - double sfSiteAtkQ(int siteidx, int kq) const; - double sfAverageAtkQ(int kq) const; - void cacheStructureData(); - - // data - // configuration - double mqmin; - double mqmax; - double mqstep; - double mdebyeprecision; - struct { - std::vector typeofsite; - std::vector sftypeatkq; - QuantityType sfaverageatkq; - double totaloccupancy; - } mstructure_cache; - QuantityType mdbsumstash; - - // serialization - friend class boost::serialization::access; - template - void serialize(Archive& ar, const unsigned int version) - { - using boost::serialization::base_object; - ar & base_object(*this); - ar & base_object(*this); - ar & mqmin; - ar & mqmax; - ar & mqstep; - ar & mdebyeprecision; - ar & mstructure_cache.typeofsite; - ar & mstructure_cache.sftypeatkq; - ar & mstructure_cache.sfaverageatkq; - ar & mstructure_cache.totaloccupancy; - } +class DLL_EXPORT BaseDebyeSum : public PairQuantity, + public PeakWidthModelOwner { + public: + // constructor + BaseDebyeSum(); + + // PairQuantity overloads + virtual eventticker::EventTicker& ticker() const; + + // results + /// F values on a full Q-grid starting at 0 + QuantityType getF() const; + + // Q-range methods + /// Full Q-grid starting at 0 + QuantityType getQgrid() const; + // Q-range configuration + void setQmin(double); + const double& getQmin() const; + void setQmax(double); + const double& getQmax() const; + void setQstep(double); + const double& getQstep() const; + + // Summation cutoff due to Q-dependent pair scaling or large distance + /// set relative cutoff value for Debye sum contribution + void setDebyePrecision(double); + /// return relative cutoff value for Debye sum contribution + const double& getDebyePrecision() const; + + protected: + // PairQuantity overloads + virtual void resetValue(); + virtual void addPairContribution(const BaseBondGenerator&, int); + // support for PQEvaluatorOptimized + virtual void stashPartialValue(); + virtual void restorePartialValue(); + + // own methods + virtual double sfSiteAtQ(int, const double& Q) const; + + private: + // methods + /// cache structure factors data for a quick access during summation + double sfSiteAtkQ(int siteidx, int kq) const; + double sfAverageAtkQ(int kq) const; + void cacheStructureData(); + + // data + // configuration + double mqmin; + double mqmax; + double mqstep; + double mdebyeprecision; + struct { + std::vector typeofsite; + std::vector sftypeatkq; + QuantityType sfaverageatkq; + double totaloccupancy; + } mstructure_cache; + QuantityType mdbsumstash; + + // serialization + friend class boost::serialization::access; + template + void serialize(Archive& ar, const unsigned int version) { + using boost::serialization::base_object; + ar& base_object(*this); + ar& base_object(*this); + ar & mqmin; + ar & mqmax; + ar & mqstep; + ar & mdebyeprecision; + ar & mstructure_cache.typeofsite; + ar & mstructure_cache.sftypeatkq; + ar & mstructure_cache.sfaverageatkq; + ar & mstructure_cache.totaloccupancy; + } }; // class BaseDebyeSum -} // namespace srreal -} // namespace diffpy +} // namespace srreal +} // namespace diffpy // Serialization ------------------------------------------------------------- diff --git a/src/diffpy/srreal/BondCalculator.cpp b/src/diffpy/srreal/BondCalculator.cpp index 21e8435a..94b04740 100644 --- a/src/diffpy/srreal/BondCalculator.cpp +++ b/src/diffpy/srreal/BondCalculator.cpp @@ -1,20 +1,20 @@ /***************************************************************************** -* -* 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.txt for a list of people who contributed. -* See LICENSE_DANSE.txt for license information. -* -****************************************************************************** -* -* class BondCalculator -- bond distance calculator -* -*****************************************************************************/ + * + * 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.txt for a list of people who contributed. + * See LICENSE_DANSE.txt for license information. + * + ****************************************************************************** + * + * class BondCalculator -- bond distance calculator + * + *****************************************************************************/ #include #include @@ -36,263 +36,214 @@ namespace { const double DEFAULT_BONDCALCULATOR_RMAX = 5.0; -} // namespace +} // namespace class BondOp { - - public: - - static bool compare( - const BondCalculator::BondEntry& be0, - const BondCalculator::BondEntry& be1) - { - if (be0.distance < be1.distance) return true; - if (be0.distance > be1.distance) return false; - if (be0.site0 < be1.site0) return true; - if (be0.site0 > be1.site0) return false; - if (be0.site1 < be1.site1) return true; - if (be0.site1 > be1.site1) return false; - if (be0.direction0 < be1.direction0) return true; - if (be0.direction0 > be1.direction0) return false; - if (be0.direction1 < be1.direction1) return true; - if (be0.direction1 > be1.direction1) return false; - if (be0.direction2 < be1.direction2) return true; - if (be0.direction2 > be1.direction2) return false; - return false; - } - - - static bool reverse_compare( - const BondCalculator::BondEntry& be0, - const BondCalculator::BondEntry& be1) - { - return compare(be1, be0); - } - - - static void bmerge( - BondCalculator::BondDataStorage& dstbonds, - const BondCalculator::BondDataStorage& srcbonds) - { - dstbonds.resize(dstbonds.size() + srcbonds.size()); - merge(dstbonds.rbegin() + srcbonds.size(), dstbonds.rend(), - srcbonds.rbegin(), srcbonds.rend(), - dstbonds.rbegin(), reverse_compare); - } - - - static BondCalculator::BondEntry entryFrom( - const BaseBondGenerator& bnds) - { - BondCalculator::BondEntry rv; - rv.distance = bnds.distance(); - rv.site0 = bnds.site0(); - rv.site1 = bnds.site1(); - rv.direction0 = bnds.r01()[0]; - rv.direction1 = bnds.r01()[1]; - rv.direction2 = bnds.r01()[2]; - return rv; - } - + public: + static bool compare(const BondCalculator::BondEntry& be0, + const BondCalculator::BondEntry& be1) { + if (be0.distance < be1.distance) return true; + if (be0.distance > be1.distance) return false; + if (be0.site0 < be1.site0) return true; + if (be0.site0 > be1.site0) return false; + if (be0.site1 < be1.site1) return true; + if (be0.site1 > be1.site1) return false; + if (be0.direction0 < be1.direction0) return true; + if (be0.direction0 > be1.direction0) return false; + if (be0.direction1 < be1.direction1) return true; + if (be0.direction1 > be1.direction1) return false; + if (be0.direction2 < be1.direction2) return true; + if (be0.direction2 > be1.direction2) return false; + return false; + } + + static bool reverse_compare(const BondCalculator::BondEntry& be0, + const BondCalculator::BondEntry& be1) { + return compare(be1, be0); + } + + static void bmerge(BondCalculator::BondDataStorage& dstbonds, + const BondCalculator::BondDataStorage& srcbonds) { + dstbonds.resize(dstbonds.size() + srcbonds.size()); + merge(dstbonds.rbegin() + srcbonds.size(), dstbonds.rend(), + srcbonds.rbegin(), srcbonds.rend(), dstbonds.rbegin(), + reverse_compare); + } + + static BondCalculator::BondEntry entryFrom(const BaseBondGenerator& bnds) { + BondCalculator::BondEntry rv; + rv.distance = bnds.distance(); + rv.site0 = bnds.site0(); + rv.site1 = bnds.site1(); + rv.direction0 = bnds.r01()[0]; + rv.direction1 = bnds.r01()[1]; + rv.direction2 = bnds.r01()[2]; + return rv; + } }; // class BondOp // Constructor --------------------------------------------------------------- -BondCalculator::BondCalculator() -{ - this->setRmax(DEFAULT_BONDCALCULATOR_RMAX); - this->setEvaluatorType(OPTIMIZED); - mevaluator->setFlag(USEFULLSUM, true); - mevaluator->setFlag(FIXEDSITEINDEX, true); +BondCalculator::BondCalculator() { + this->setRmax(DEFAULT_BONDCALCULATOR_RMAX); + this->setEvaluatorType(OPTIMIZED); + mevaluator->setFlag(USEFULLSUM, true); + mevaluator->setFlag(FIXEDSITEINDEX, true); } // Public Methods ------------------------------------------------------------ -QuantityType BondCalculator::distances() const -{ - QuantityType rv; - rv.reserve(this->count()); - BondDataStorage::const_iterator bi = mbonds.begin(); - for (; bi != mbonds.end(); bi++) rv.push_back(bi->distance); - return rv; +QuantityType BondCalculator::distances() const { + QuantityType rv; + rv.reserve(this->count()); + BondDataStorage::const_iterator bi = mbonds.begin(); + for (; bi != mbonds.end(); bi++) rv.push_back(bi->distance); + return rv; } - -vector BondCalculator::directions() const -{ - vector rv; - rv.reserve(this->count()); - BondDataStorage::const_iterator bi = mbonds.begin(); - for (; bi != mbonds.end(); ++bi) - { - R3::Vector dir(bi->direction0, bi->direction1, bi->direction2); - rv.push_back(dir); - } - return rv; +vector BondCalculator::directions() const { + vector rv; + rv.reserve(this->count()); + BondDataStorage::const_iterator bi = mbonds.begin(); + for (; bi != mbonds.end(); ++bi) { + R3::Vector dir(bi->direction0, bi->direction1, bi->direction2); + rv.push_back(dir); + } + return rv; } - -SiteIndices BondCalculator::sites0() const -{ - SiteIndices rv; - rv.reserve(this->count()); - BondDataStorage::const_iterator bi = mbonds.begin(); - for (; bi != mbonds.end(); ++bi) rv.push_back(bi->site0); - return rv; +SiteIndices BondCalculator::sites0() const { + SiteIndices rv; + rv.reserve(this->count()); + BondDataStorage::const_iterator bi = mbonds.begin(); + for (; bi != mbonds.end(); ++bi) rv.push_back(bi->site0); + return rv; } - -SiteIndices BondCalculator::sites1() const -{ - SiteIndices rv; - rv.reserve(this->count()); - BondDataStorage::const_iterator bi = mbonds.begin(); - for (; bi != mbonds.end(); ++bi) rv.push_back(bi->site1); - return rv; +SiteIndices BondCalculator::sites1() const { + SiteIndices rv; + rv.reserve(this->count()); + BondDataStorage::const_iterator bi = mbonds.begin(); + for (; bi != mbonds.end(); ++bi) rv.push_back(bi->site1); + return rv; } - -vector BondCalculator::types0() const -{ - return siteIndicesToTypes(mstructure, this->sites0()); +vector BondCalculator::types0() const { + return siteIndicesToTypes(mstructure, this->sites0()); } - -vector BondCalculator::types1() const -{ - return siteIndicesToTypes(mstructure, this->sites1()); +vector BondCalculator::types1() const { + return siteIndicesToTypes(mstructure, this->sites1()); } - -void BondCalculator::filterCone(R3::Vector coneaxis, double degrees) -{ - using namespace diffpy::validators; - double nmconeaxis = R3::norm(coneaxis); - ensureEpsilonPositive("magnitude of cone vector", nmconeaxis); - coneaxis /= nmconeaxis; - mfilter_directions.push_back(coneaxis); - mfilter_degrees.push_back(degrees); - mticker.click(); +void BondCalculator::filterCone(R3::Vector coneaxis, double degrees) { + using namespace diffpy::validators; + double nmconeaxis = R3::norm(coneaxis); + ensureEpsilonPositive("magnitude of cone vector", nmconeaxis); + coneaxis /= nmconeaxis; + mfilter_directions.push_back(coneaxis); + mfilter_degrees.push_back(degrees); + mticker.click(); } - -void BondCalculator::filterOff() -{ - if (!mfilter_directions.empty()) mticker.click(); - mfilter_directions.clear(); - mfilter_degrees.clear(); +void BondCalculator::filterOff() { + if (!mfilter_directions.empty()) mticker.click(); + mfilter_directions.clear(); + mfilter_degrees.clear(); } // PairQuantity overloads -string BondCalculator::getParallelData() const -{ - ostringstream storage(ios::binary); - diffpy::serialization::oarchive oa(storage, ios::binary); - oa << mpopbonds << maddbonds; - return storage.str(); +string BondCalculator::getParallelData() const { + ostringstream storage(ios::binary); + diffpy::serialization::oarchive oa(storage, ios::binary); + oa << mpopbonds << maddbonds; + return storage.str(); } // Protected Methods --------------------------------------------------------- -void BondCalculator::resetValue() -{ - mvalue.clear(); - mbonds.clear(); - maddbonds.clear(); - mpopbonds.clear(); - this->PairQuantity::resetValue(); -} - - -void BondCalculator::addPairContribution( - const BaseBondGenerator& bnds, - int summationscale) -{ - assert(summationscale == +1 || summationscale == -1); - static R3::Vector ru01; - const R3::Vector& r01 = bnds.r01(); - ru01 = r01 / bnds.distance(); - if (!(this->checkConeFilters(ru01))) return; - BondDataStorage& bes = (summationscale > 0) ? maddbonds : mpopbonds; - bes.push_back(BondOp::entryFrom(bnds)); -} - - -void BondCalculator::executeParallelMerge(const std::string& pdata) -{ - istringstream storage(pdata, ios::binary); - diffpy::serialization::iarchive ia(storage, ios::binary); - BondDataStorage bpop, badd; - ia >> bpop >> badd; - BondOp::bmerge(mpopbonds, bpop); - BondOp::bmerge(maddbonds, badd); -} - - -void BondCalculator::finishValue() -{ - // filter-out entries marked for removal - assert(mpopbonds.size() <= mbonds.size()); - sort(mpopbonds.begin(), mpopbonds.end(), BondOp::compare); - sort(maddbonds.begin(), maddbonds.end(), BondOp::compare); - if (mevaluator->isParallel()) return; - BondDataStorage::iterator last; - last = set_difference(mbonds.begin(), mbonds.end(), - mpopbonds.begin(), mpopbonds.end(), - mbonds.begin(), BondOp::compare); - mbonds.erase(last, mbonds.end()); - if (mbonds.empty()) mbonds.swap(maddbonds); - else BondOp::bmerge(mbonds, maddbonds); - mvalue = this->distances(); - mpopbonds.clear(); - maddbonds.clear(); -} - - -void BondCalculator::stashPartialValue() -{ - mstashedvalue.bonds.swap(mbonds); - mstashedvalue.popbonds.swap(mpopbonds); - // No need to stash maddbonds as they are evaluated after partial value. -} - - -void BondCalculator::restorePartialValue() -{ - mbonds.swap(mstashedvalue.bonds); - mpopbonds.swap(mstashedvalue.popbonds); - mstashedvalue.bonds.clear(); - mstashedvalue.popbonds.clear(); +void BondCalculator::resetValue() { + mvalue.clear(); + mbonds.clear(); + maddbonds.clear(); + mpopbonds.clear(); + this->PairQuantity::resetValue(); +} + +void BondCalculator::addPairContribution(const BaseBondGenerator& bnds, + int summationscale) { + assert(summationscale == +1 || summationscale == -1); + static R3::Vector ru01; + const R3::Vector& r01 = bnds.r01(); + ru01 = r01 / bnds.distance(); + if (!(this->checkConeFilters(ru01))) return; + BondDataStorage& bes = (summationscale > 0) ? maddbonds : mpopbonds; + bes.push_back(BondOp::entryFrom(bnds)); +} + +void BondCalculator::executeParallelMerge(const std::string& pdata) { + istringstream storage(pdata, ios::binary); + diffpy::serialization::iarchive ia(storage, ios::binary); + BondDataStorage bpop, badd; + ia >> bpop >> badd; + BondOp::bmerge(mpopbonds, bpop); + BondOp::bmerge(maddbonds, badd); +} + +void BondCalculator::finishValue() { + // filter-out entries marked for removal + assert(mpopbonds.size() <= mbonds.size()); + sort(mpopbonds.begin(), mpopbonds.end(), BondOp::compare); + sort(maddbonds.begin(), maddbonds.end(), BondOp::compare); + if (mevaluator->isParallel()) return; + BondDataStorage::iterator last; + last = set_difference(mbonds.begin(), mbonds.end(), mpopbonds.begin(), + mpopbonds.end(), mbonds.begin(), BondOp::compare); + mbonds.erase(last, mbonds.end()); + if (mbonds.empty()) + mbonds.swap(maddbonds); + else + BondOp::bmerge(mbonds, maddbonds); + mvalue = this->distances(); + mpopbonds.clear(); + maddbonds.clear(); +} + +void BondCalculator::stashPartialValue() { + mstashedvalue.bonds.swap(mbonds); + mstashedvalue.popbonds.swap(mpopbonds); + // No need to stash maddbonds as they are evaluated after partial value. +} + +void BondCalculator::restorePartialValue() { + mbonds.swap(mstashedvalue.bonds); + mpopbonds.swap(mstashedvalue.popbonds); + mstashedvalue.bonds.clear(); + mstashedvalue.popbonds.clear(); } // Private Methods ----------------------------------------------------------- -int BondCalculator::count() const -{ - return mbonds.size(); -} - +int BondCalculator::count() const { return mbonds.size(); } -bool BondCalculator::checkConeFilters(const R3::Vector& ru01) const -{ - using diffpy::mathutils::eps_eq; - if (mfilter_directions.empty()) return true; - assert(eps_eq(1.0, R3::norm(ru01))); - vector::const_iterator coneax = mfilter_directions.begin(); - vector::const_iterator deg = mfilter_degrees.begin(); - for (; coneax != mfilter_directions.end(); ++coneax, ++deg) - { - if (180.0 <= *deg) return true; - double angledegrees = 180.0 / M_PI * acos(R3::dot(ru01, *coneax)); - if (angledegrees <= *deg) return true; - } - return false; +bool BondCalculator::checkConeFilters(const R3::Vector& ru01) const { + using diffpy::mathutils::eps_eq; + if (mfilter_directions.empty()) return true; + assert(eps_eq(1.0, R3::norm(ru01))); + vector::const_iterator coneax = mfilter_directions.begin(); + vector::const_iterator deg = mfilter_degrees.begin(); + for (; coneax != mfilter_directions.end(); ++coneax, ++deg) { + if (180.0 <= *deg) return true; + double angledegrees = 180.0 / M_PI * acos(R3::dot(ru01, *coneax)); + if (angledegrees <= *deg) return true; + } + return false; } -} // namespace srreal -} // namespace diffpy +} // namespace srreal +} // namespace diffpy // Serialization ------------------------------------------------------------- diff --git a/src/diffpy/srreal/BondCalculator.hpp b/src/diffpy/srreal/BondCalculator.hpp index aceb700a..f79b1ab4 100644 --- a/src/diffpy/srreal/BondCalculator.hpp +++ b/src/diffpy/srreal/BondCalculator.hpp @@ -1,20 +1,20 @@ /***************************************************************************** -* -* 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.txt for a list of people who contributed. -* See LICENSE_DANSE.txt for license information. -* -****************************************************************************** -* -* class BondCalculator -- bond distance calculator -* -*****************************************************************************/ + * + * 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.txt for a list of people who contributed. + * See LICENSE_DANSE.txt for license information. + * + ****************************************************************************** + * + * class BondCalculator -- bond distance calculator + * + *****************************************************************************/ #ifndef BONDCALCULATOR_HPP_INCLUDED #define BONDCALCULATOR_HPP_INCLUDED @@ -26,109 +26,97 @@ namespace diffpy { namespace srreal { -class DLL_EXPORT BondCalculator : public PairQuantity -{ - public: - - // constructor - BondCalculator(); - - // methods - template QuantityType operator()(const T&); - QuantityType distances() const; - std::vector directions() const; - SiteIndices sites0() const; - SiteIndices sites1() const; - std::vector types0() const; - std::vector types1() const; - void filterCone(R3::Vector coneaxis, double degrees); - void filterOff(); - - // PairQuantity overloads - virtual std::string getParallelData() const; - - protected: - - // PairQuantity overloads - virtual void resetValue(); - virtual void addPairContribution(const BaseBondGenerator&, int); - virtual void executeParallelMerge(const std::string& pdata); - virtual void finishValue(); - - // support for PQEvaluatorOptimized - virtual void stashPartialValue(); - virtual void restorePartialValue(); - - friend class BondOp; - class BondEntry { - - public: - - double distance; - int site0; - int site1; - double direction0; - double direction1; - double direction2; - - private: - - friend class boost::serialization::access; - template - void serialize(Archive& ar, const unsigned int version) - { - ar & distance & site0 & site1; - ar & direction0 & direction1 & direction2; - } - - }; - - typedef std::vector BondDataStorage; - - private: - - // serialization - friend class boost::serialization::access; - template - void serialize(Archive& ar, const unsigned int version) - { - using boost::serialization::base_object; - ar & base_object(*this); - ar & mbonds; - ar & mfilter_directions; - ar & mfilter_degrees; - } - - // methods - int count() const; - bool checkConeFilters(const R3::Vector& ru01) const; - - // data - std::vector mfilter_directions; - std::vector mfilter_degrees; - BondDataStorage mbonds; - BondDataStorage mpopbonds; - BondDataStorage maddbonds; - // support for PQEvaluatorOptimized - struct { - BondDataStorage bonds; - BondDataStorage popbonds; - } mstashedvalue; - +class DLL_EXPORT BondCalculator : public PairQuantity { + public: + // constructor + BondCalculator(); + + // methods + template + QuantityType operator()(const T&); + QuantityType distances() const; + std::vector directions() const; + SiteIndices sites0() const; + SiteIndices sites1() const; + std::vector types0() const; + std::vector types1() const; + void filterCone(R3::Vector coneaxis, double degrees); + void filterOff(); + + // PairQuantity overloads + virtual std::string getParallelData() const; + + protected: + // PairQuantity overloads + virtual void resetValue(); + virtual void addPairContribution(const BaseBondGenerator&, int); + virtual void executeParallelMerge(const std::string& pdata); + virtual void finishValue(); + + // support for PQEvaluatorOptimized + virtual void stashPartialValue(); + virtual void restorePartialValue(); + + friend class BondOp; + class BondEntry { + public: + double distance; + int site0; + int site1; + double direction0; + double direction1; + double direction2; + + private: + friend class boost::serialization::access; + template + void serialize(Archive& ar, const unsigned int version) { + ar & distance & site0 & site1; + ar & direction0 & direction1 & direction2; + } + }; + + typedef std::vector BondDataStorage; + + private: + // serialization + friend class boost::serialization::access; + template + void serialize(Archive& ar, const unsigned int version) { + using boost::serialization::base_object; + ar& base_object(*this); + ar & mbonds; + ar & mfilter_directions; + ar & mfilter_degrees; + } + + // methods + int count() const; + bool checkConeFilters(const R3::Vector& ru01) const; + + // data + std::vector mfilter_directions; + std::vector mfilter_degrees; + BondDataStorage mbonds; + BondDataStorage mpopbonds; + BondDataStorage maddbonds; + // support for PQEvaluatorOptimized + struct { + BondDataStorage bonds; + BondDataStorage popbonds; + } mstashedvalue; }; // Public Template Methods --------------------------------------------------- template -QuantityType BondCalculator::operator()(const T& stru) -{ - this->eval(stru); - return this->distances(); +QuantityType BondCalculator::operator()(const T& stru) { + this->eval(stru); + return this->distances(); } - -} // namespace srreal -} // namespace diffpy +} // namespace srreal +} // namespace diffpy // Serialization ------------------------------------------------------------- diff --git a/src/diffpy/srreal/ConstantPeakWidth.cpp b/src/diffpy/srreal/ConstantPeakWidth.cpp index 32b8bc78..4bf50b3f 100644 --- a/src/diffpy/srreal/ConstantPeakWidth.cpp +++ b/src/diffpy/srreal/ConstantPeakWidth.cpp @@ -1,20 +1,20 @@ /***************************************************************************** -* -* 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.txt for a list of people who contributed. -* See LICENSE_DANSE.txt for license information. -* -****************************************************************************** -* -* class ConstantPeakWidth -- constant peak width model for testing -* -*****************************************************************************/ + * + * 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.txt for a list of people who contributed. + * See LICENSE_DANSE.txt for license information. + * + ****************************************************************************** + * + * class ConstantPeakWidth -- constant peak width model for testing + * + *****************************************************************************/ #include #include @@ -31,102 +31,81 @@ namespace { using diffpy::mathutils::GAUSS_SIGMA_TO_FWHM; const double UtoB = 8 * M_PI * M_PI; - -double getuisowidth(const ConstantPeakWidth* ppwm) -{ - const double rmsd = ppwm->getWidth() / GAUSS_SIGMA_TO_FWHM; - const int pm = (rmsd >= 0) ? +1 : -1; - return pm * 0.5 * rmsd * rmsd; +double getuisowidth(const ConstantPeakWidth* ppwm) { + const double rmsd = ppwm->getWidth() / GAUSS_SIGMA_TO_FWHM; + const int pm = (rmsd >= 0) ? +1 : -1; + return pm * 0.5 * rmsd * rmsd; } -void setuisowidth(ConstantPeakWidth* ppwm, const double& uiso) -{ - const int pm = (uiso >= 0) ? +1 : -1; - const double uisoplus = pm * uiso; - const double fwhm = pm * GAUSS_SIGMA_TO_FWHM * sqrt(2.0 * uisoplus); - ppwm->setWidth(fwhm); +void setuisowidth(ConstantPeakWidth* ppwm, const double& uiso) { + const int pm = (uiso >= 0) ? +1 : -1; + const double uisoplus = pm * uiso; + const double fwhm = pm * GAUSS_SIGMA_TO_FWHM * sqrt(2.0 * uisoplus); + ppwm->setWidth(fwhm); } - -double getbisowidth(const ConstantPeakWidth* ppwm) -{ - return UtoB * getuisowidth(ppwm); +double getbisowidth(const ConstantPeakWidth* ppwm) { + return UtoB * getuisowidth(ppwm); } -void setbisowidth(ConstantPeakWidth* ppwm, const double& biso) -{ - setuisowidth(ppwm, biso / UtoB); +void setbisowidth(ConstantPeakWidth* ppwm, const double& biso) { + setuisowidth(ppwm, biso / UtoB); } -} // namespace +} // namespace // Constructors -------------------------------------------------------------- -ConstantPeakWidth::ConstantPeakWidth() : mwidth(0.0) -{ - this->registerDoubleAttribute("width", this, - &ConstantPeakWidth::getWidth, &ConstantPeakWidth::setWidth); - this->registerDoubleAttribute("bisowidth", this, - &getbisowidth, &setbisowidth); - this->registerDoubleAttribute("uisowidth", this, - &getuisowidth, &setuisowidth); +ConstantPeakWidth::ConstantPeakWidth() : mwidth(0.0) { + this->registerDoubleAttribute("width", this, &ConstantPeakWidth::getWidth, + &ConstantPeakWidth::setWidth); + this->registerDoubleAttribute("bisowidth", this, &getbisowidth, + &setbisowidth); + this->registerDoubleAttribute("uisowidth", this, &getuisowidth, + &setuisowidth); } - -PeakWidthModelPtr ConstantPeakWidth::create() const -{ - PeakWidthModelPtr rv(new ConstantPeakWidth()); - return rv; +PeakWidthModelPtr ConstantPeakWidth::create() const { + PeakWidthModelPtr rv(new ConstantPeakWidth()); + return rv; } - -PeakWidthModelPtr ConstantPeakWidth::clone() const -{ - PeakWidthModelPtr rv(new ConstantPeakWidth(*this)); - return rv; +PeakWidthModelPtr ConstantPeakWidth::clone() const { + PeakWidthModelPtr rv(new ConstantPeakWidth(*this)); + return rv; } // Public Methods ------------------------------------------------------------ -const string& ConstantPeakWidth::type() const -{ - static const string rv = "constant"; - return rv; +const string& ConstantPeakWidth::type() const { + static const string rv = "constant"; + return rv; } - -double ConstantPeakWidth::calculate(const BaseBondGenerator& bnds) const -{ - return this->getWidth(); +double ConstantPeakWidth::calculate(const BaseBondGenerator& bnds) const { + return this->getWidth(); } - -double ConstantPeakWidth::maxWidth( - StructureAdapterPtr stru, double rmin, double rmax) const -{ - return this->getWidth(); +double ConstantPeakWidth::maxWidth(StructureAdapterPtr stru, double rmin, + double rmax) const { + return this->getWidth(); } // data access -const double& ConstantPeakWidth::getWidth() const -{ - return mwidth; -} - +const double& ConstantPeakWidth::getWidth() const { return mwidth; } -void ConstantPeakWidth::setWidth(double width) -{ - if (mwidth != width) mticker.click(); - mwidth = width; +void ConstantPeakWidth::setWidth(double width) { + if (mwidth != width) mticker.click(); + mwidth = width; } // Registration -------------------------------------------------------------- bool reg_ConstantPeakWidth = ConstantPeakWidth().registerThisType(); -} // namespace srreal -} // namespace diffpy +} // namespace srreal +} // namespace diffpy // Serialization ------------------------------------------------------------- diff --git a/src/diffpy/srreal/ConstantPeakWidth.hpp b/src/diffpy/srreal/ConstantPeakWidth.hpp index 4ccddc4f..7ba271b3 100644 --- a/src/diffpy/srreal/ConstantPeakWidth.hpp +++ b/src/diffpy/srreal/ConstantPeakWidth.hpp @@ -1,20 +1,20 @@ /***************************************************************************** -* -* 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.txt for a list of people who contributed. -* See LICENSE_DANSE.txt for license information. -* -****************************************************************************** -* -* class ConstantPeakWidth -- constant peak width -* -*****************************************************************************/ + * + * 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.txt for a list of people who contributed. + * See LICENSE_DANSE.txt for license information. + * + ****************************************************************************** + * + * class ConstantPeakWidth -- constant peak width + * + *****************************************************************************/ #ifndef CONSTANTPEAKWIDTH_HPP_INCLUDED #define CONSTANTPEAKWIDTH_HPP_INCLUDED @@ -26,46 +26,39 @@ namespace diffpy { namespace srreal { - -class DLL_EXPORT ConstantPeakWidth : public PeakWidthModel -{ - public: - - // constructors - ConstantPeakWidth(); - virtual PeakWidthModelPtr create() const; - virtual PeakWidthModelPtr clone() const; - - // methods - virtual const std::string& type() const; - virtual double calculate(const BaseBondGenerator&) const; - virtual double maxWidth(StructureAdapterPtr, - double rmin, double rmax) const; - - // data access - const double& getWidth() const; - void setWidth(double); - - private: - - // data - double mwidth; - - // serialization - friend class boost::serialization::access; - - template - void serialize(Archive& ar, const unsigned int version) - { - using boost::serialization::base_object; - ar & base_object(*this); - ar & mwidth; - } - +class DLL_EXPORT ConstantPeakWidth : public PeakWidthModel { + public: + // constructors + ConstantPeakWidth(); + virtual PeakWidthModelPtr create() const; + virtual PeakWidthModelPtr clone() const; + + // methods + virtual const std::string& type() const; + virtual double calculate(const BaseBondGenerator&) const; + virtual double maxWidth(StructureAdapterPtr, double rmin, double rmax) const; + + // data access + const double& getWidth() const; + void setWidth(double); + + private: + // data + double mwidth; + + // serialization + friend class boost::serialization::access; + + template + void serialize(Archive& ar, const unsigned int version) { + using boost::serialization::base_object; + ar& base_object(*this); + ar & mwidth; + } }; -} // namespace srreal -} // namespace diffpy +} // namespace srreal +} // namespace diffpy // Serialization ------------------------------------------------------------- diff --git a/src/diffpy/srreal/ConstantRadiiTable.cpp b/src/diffpy/srreal/ConstantRadiiTable.cpp index 81014d8b..4f1a8df6 100644 --- a/src/diffpy/srreal/ConstantRadiiTable.cpp +++ b/src/diffpy/srreal/ConstantRadiiTable.cpp @@ -1,21 +1,21 @@ /***************************************************************************** -* -* 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.txt for a list of people who contributed. -* See LICENSE_DANSE.txt for license information. -* -****************************************************************************** -* -* class ConstantRadiiTable -- this AtomRadiiTable returns same value -* for all elements unless redefined by the setCustom method. -* -*****************************************************************************/ + * + * 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.txt for a list of people who contributed. + * See LICENSE_DANSE.txt for license information. + * + ****************************************************************************** + * + * class ConstantRadiiTable -- this AtomRadiiTable returns same value + * for all elements unless redefined by the setCustom method. + * + *****************************************************************************/ #include #include @@ -34,56 +34,41 @@ using namespace std; // Constructors -------------------------------------------------------------- -ConstantRadiiTable::ConstantRadiiTable() : mdefaultradius(0.0) -{ } +ConstantRadiiTable::ConstantRadiiTable() : mdefaultradius(0.0) {} // Public Methods ------------------------------------------------------------ // HasClassRegistry methods -AtomRadiiTablePtr ConstantRadiiTable::create() const -{ - return boost::make_shared(); +AtomRadiiTablePtr ConstantRadiiTable::create() const { + return boost::make_shared(); } - -AtomRadiiTablePtr ConstantRadiiTable::clone() const -{ - return boost::make_shared(*this); +AtomRadiiTablePtr ConstantRadiiTable::clone() const { + return boost::make_shared(*this); } - -const string& ConstantRadiiTable::type() const -{ - static string rv = "constant"; - return rv; +const string& ConstantRadiiTable::type() const { + static string rv = "constant"; + return rv; } // own methods -double ConstantRadiiTable::standardLookup(const string& smbl) const -{ - return mdefaultradius; -} - - -void ConstantRadiiTable::setDefault(double value) -{ - mdefaultradius = value; +double ConstantRadiiTable::standardLookup(const string& smbl) const { + return mdefaultradius; } +void ConstantRadiiTable::setDefault(double value) { mdefaultradius = value; } -double ConstantRadiiTable::getDefault() const -{ - return mdefaultradius; -} +double ConstantRadiiTable::getDefault() const { return mdefaultradius; } // Registration -------------------------------------------------------------- bool reg_ConstantRadiiTable = ConstantRadiiTable().registerThisType(); -} // srreal -} // diffpy +} // namespace srreal +} // namespace diffpy // Serialization ------------------------------------------------------------- diff --git a/src/diffpy/srreal/ConstantRadiiTable.hpp b/src/diffpy/srreal/ConstantRadiiTable.hpp index 8be15bba..11dd3ca5 100644 --- a/src/diffpy/srreal/ConstantRadiiTable.hpp +++ b/src/diffpy/srreal/ConstantRadiiTable.hpp @@ -1,20 +1,20 @@ /***************************************************************************** -* -* 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.txt for a list of people who contributed. -* See LICENSE_DANSE.txt for license information. -* -****************************************************************************** -* -* class ConstantRadiiTable -- concrete AtomRadiiTable for a uniform radius -* -*****************************************************************************/ + * + * 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.txt for a list of people who contributed. + * See LICENSE_DANSE.txt for license information. + * + ****************************************************************************** + * + * class ConstantRadiiTable -- concrete AtomRadiiTable for a uniform radius + * + *****************************************************************************/ #ifndef CONSTANTRADIITABLE_HPP_INCLUDED #define CONSTANTRADIITABLE_HPP_INCLUDED @@ -26,42 +26,37 @@ namespace diffpy { namespace srreal { -class DLL_EXPORT ConstantRadiiTable : public AtomRadiiTable -{ - public: +class DLL_EXPORT ConstantRadiiTable : public AtomRadiiTable { + public: + // constructor + ConstantRadiiTable(); - // constructor - ConstantRadiiTable(); + // methods - HasClassRegistry + SharedPtr create() const; + SharedPtr clone() const; + const std::string& type() const; + // method overloads + double standardLookup(const std::string& smbl) const; + // methods specific for this class + void setDefault(double); + double getDefault() const; - // methods - HasClassRegistry - SharedPtr create() const; - SharedPtr clone() const; - const std::string& type() const; - // method overloads - double standardLookup(const std::string& smbl) const; - // methods specific for this class - void setDefault(double); - double getDefault() const; - - private: - - // data - double mdefaultradius; - - // serialization - friend class boost::serialization::access; - template - void serialize(Archive& ar, const unsigned int version) - { - using boost::serialization::base_object; - ar & base_object(*this); - ar & mdefaultradius; - } + private: + // data + double mdefaultradius; + // serialization + friend class boost::serialization::access; + template + void serialize(Archive& ar, const unsigned int version) { + using boost::serialization::base_object; + ar& base_object(*this); + ar & mdefaultradius; + } }; -} // namespace srreal -} // namespace diffpy +} // namespace srreal +} // namespace diffpy // Serialization ------------------------------------------------------------- diff --git a/src/diffpy/srreal/CroppedGaussianProfile.cpp b/src/diffpy/srreal/CroppedGaussianProfile.cpp index aabdba0e..2650980e 100644 --- a/src/diffpy/srreal/CroppedGaussianProfile.cpp +++ b/src/diffpy/srreal/CroppedGaussianProfile.cpp @@ -1,22 +1,22 @@ /***************************************************************************** -* -* 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.txt for a list of people who contributed. -* See LICENSE_DANSE.txt for license information. -* -****************************************************************************** -* -* class CroppedGaussianProfile -- Gaussian profile cropped to zero beyond -* xboundhi and scaled so that its integrated area equals 1. -* Registered as "croppedgaussian". -* -*****************************************************************************/ + * + * 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.txt for a list of people who contributed. + * See LICENSE_DANSE.txt for license information. + * + ****************************************************************************** + * + * class CroppedGaussianProfile -- Gaussian profile cropped to zero beyond + * xboundhi and scaled so that its integrated area equals 1. + * Registered as "croppedgaussian". + * + *****************************************************************************/ #include @@ -32,63 +32,53 @@ namespace srreal { // Constructors -------------------------------------------------------------- -CroppedGaussianProfile::CroppedGaussianProfile() : mscale(0.0) -{ - // refresh mscale - this->setPrecision(this->getPrecision()); +CroppedGaussianProfile::CroppedGaussianProfile() : mscale(0.0) { + // refresh mscale + this->setPrecision(this->getPrecision()); } - -PeakProfilePtr CroppedGaussianProfile::create() const -{ - PeakProfilePtr rv(new CroppedGaussianProfile()); - return rv; +PeakProfilePtr CroppedGaussianProfile::create() const { + PeakProfilePtr rv(new CroppedGaussianProfile()); + return rv; } - -PeakProfilePtr CroppedGaussianProfile::clone() const -{ - PeakProfilePtr rv(new CroppedGaussianProfile(*this)); - return rv; +PeakProfilePtr CroppedGaussianProfile::clone() const { + PeakProfilePtr rv(new CroppedGaussianProfile(*this)); + return rv; } // Public Methods ------------------------------------------------------------ -const string& CroppedGaussianProfile::type() const -{ - static string rv = "croppedgaussian"; - return rv; +const string& CroppedGaussianProfile::type() const { + static string rv = "croppedgaussian"; + return rv; } - -double CroppedGaussianProfile::operator()(double x, double fwhm) const -{ - double xrel = x / fwhm; - double rv = (fabs(xrel) >= mhalfboundrel) ? 0.0 : - 2 * sqrt(M_LN2 / M_PI) / fwhm * - mscale * exp(-4 * M_LN2 * xrel * xrel); - return rv; +double CroppedGaussianProfile::operator()(double x, double fwhm) const { + double xrel = x / fwhm; + double rv = + (fabs(xrel) >= mhalfboundrel) + ? 0.0 + : 2 * sqrt(M_LN2 / M_PI) / fwhm * mscale * exp(-4 * M_LN2 * xrel * xrel); + return rv; } - -void CroppedGaussianProfile::setPrecision(double eps) -{ - this->GaussianProfile::setPrecision(eps); - double eps1 = this->getPrecision(); - mscale = 1.0; - if (eps1 < 1) - { - double area = gsl_sf_erf(sqrt(-log(eps1))); - mscale = 1.0 / area; - } +void CroppedGaussianProfile::setPrecision(double eps) { + this->GaussianProfile::setPrecision(eps); + double eps1 = this->getPrecision(); + mscale = 1.0; + if (eps1 < 1) { + double area = gsl_sf_erf(sqrt(-log(eps1))); + mscale = 1.0 / area; + } } // Registration -------------------------------------------------------------- bool reg_CroppedGaussianProfile = CroppedGaussianProfile().registerThisType(); -} // namespace srreal -} // namespace diffpy +} // namespace srreal +} // namespace diffpy // Serialization ------------------------------------------------------------- diff --git a/src/diffpy/srreal/CroppedGaussianProfile.hpp b/src/diffpy/srreal/CroppedGaussianProfile.hpp index ac560ab1..f056e52a 100644 --- a/src/diffpy/srreal/CroppedGaussianProfile.hpp +++ b/src/diffpy/srreal/CroppedGaussianProfile.hpp @@ -1,22 +1,22 @@ /***************************************************************************** -* -* 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.txt for a list of people who contributed. -* See LICENSE_DANSE.txt for license information. -* -****************************************************************************** -* -* class CroppedGaussianProfile -- Gaussian profile cropped to zero beyond -* xboundhi and scaled so that its integrated area equals 1. -* Registered as "croppedgaussian". -* -*****************************************************************************/ + * + * 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.txt for a list of people who contributed. + * See LICENSE_DANSE.txt for license information. + * + ****************************************************************************** + * + * class CroppedGaussianProfile -- Gaussian profile cropped to zero beyond + * xboundhi and scaled so that its integrated area equals 1. + * Registered as "croppedgaussian". + * + *****************************************************************************/ #ifndef CROPPEDGAUSSIANPROFILE_HPP_INCLUDED #define CROPPEDGAUSSIANPROFILE_HPP_INCLUDED @@ -28,40 +28,35 @@ namespace diffpy { namespace srreal { -class DLL_EXPORT CroppedGaussianProfile : public GaussianProfile -{ - public: - - // constructors - CroppedGaussianProfile(); - PeakProfilePtr create() const; - PeakProfilePtr clone() const; - - // methods - const std::string& type() const; - double operator()(double x, double fwhm) const; - void setPrecision(double eps); - - private: - - // data - double mscale; - - // serialization - friend class boost::serialization::access; - - template - void serialize(Archive& ar, const unsigned int version) - { - using boost::serialization::base_object; - ar & base_object(*this); - ar & mscale; - } - +class DLL_EXPORT CroppedGaussianProfile : public GaussianProfile { + public: + // constructors + CroppedGaussianProfile(); + PeakProfilePtr create() const; + PeakProfilePtr clone() const; + + // methods + const std::string& type() const; + double operator()(double x, double fwhm) const; + void setPrecision(double eps); + + private: + // data + double mscale; + + // serialization + friend class boost::serialization::access; + + template + void serialize(Archive& ar, const unsigned int version) { + using boost::serialization::base_object; + ar& base_object(*this); + ar & mscale; + } }; -} // namespace srreal -} // namespace diffpy +} // namespace srreal +} // namespace diffpy // Serialization ------------------------------------------------------------- diff --git a/src/diffpy/srreal/CrystalStructureAdapter.cpp b/src/diffpy/srreal/CrystalStructureAdapter.cpp index 9db5f0d7..8f9d8a3c 100644 --- a/src/diffpy/srreal/CrystalStructureAdapter.cpp +++ b/src/diffpy/srreal/CrystalStructureAdapter.cpp @@ -1,24 +1,24 @@ /***************************************************************************** -* -* libdiffpy Complex Modeling Initiative -* (c) 2013 Brookhaven Science Associates, -* Brookhaven National Laboratory. -* All rights reserved. -* -* File coded by: Pavol Juhas -* -* See AUTHORS.txt for a list of people who contributed. -* See LICENSE.txt for license information. -* -****************************************************************************** -* -* class CrystalStructureAdapter -- universal adapter for crystal structure -* composed of asymmetric unit cell and list of symmetry operations in -* the space group -* -* class CrystalStructureBondGenerator -- bond generator -* -*****************************************************************************/ + * + * libdiffpy Complex Modeling Initiative + * (c) 2013 Brookhaven Science Associates, + * Brookhaven National Laboratory. + * All rights reserved. + * + * File coded by: Pavol Juhas + * + * See AUTHORS.txt for a list of people who contributed. + * See LICENSE.txt for license information. + * + ****************************************************************************** + * + * class CrystalStructureAdapter -- universal adapter for crystal structure + * composed of asymmetric unit cell and list of symmetry operations in + * the space group + * + * class CrystalStructureBondGenerator -- bond generator + * + *****************************************************************************/ #include @@ -43,238 +43,195 @@ const double DEFAULT_SYMMETRY_PRECISION = 5e-5; // Constructor --------------------------------------------------------------- -CrystalStructureAdapter::CrystalStructureAdapter() : - PeriodicStructureAdapter(), - msymmetry_precision(DEFAULT_SYMMETRY_PRECISION), - msymmetry_cached(false) -{ } +CrystalStructureAdapter::CrystalStructureAdapter() + : PeriodicStructureAdapter(), + msymmetry_precision(DEFAULT_SYMMETRY_PRECISION), + msymmetry_cached(false) {} // Public Methods ------------------------------------------------------------ -StructureAdapterPtr CrystalStructureAdapter::clone() const -{ - StructureAdapterPtr rv(new CrystalStructureAdapter(*this)); - return rv; +StructureAdapterPtr CrystalStructureAdapter::clone() const { + StructureAdapterPtr rv(new CrystalStructureAdapter(*this)); + return rv; } - -BaseBondGeneratorPtr CrystalStructureAdapter::createBondGenerator() const -{ - this->updateSymmetryPositions(); - BaseBondGeneratorPtr bnds( - new CrystalStructureBondGenerator(shared_from_this())); - return bnds; -} - - -int CrystalStructureAdapter::siteMultiplicity(int idx) const -{ - if (!this->isSymmetryCached()) this->updateSymmetryPositions(); - int rv = msymatoms[idx].size(); - return rv; +BaseBondGeneratorPtr CrystalStructureAdapter::createBondGenerator() const { + this->updateSymmetryPositions(); + BaseBondGeneratorPtr bnds( + new CrystalStructureBondGenerator(shared_from_this())); + return bnds; } - -StructureDifference -CrystalStructureAdapter::diff(StructureAdapterConstPtr other) const -{ - StructureDifference sd = this->StructureAdapter::diff(other); - if (sd.stru0 == sd.stru1) return sd; - typedef boost::shared_ptr CPtr; - CPtr cother = boost::dynamic_pointer_cast(other); - if (!cother) return sd; - // compare symmetry operations in both adapters - assert(cother == sd.stru1); - const bool sameprecision = ( - this->getSymmetryPrecision() == cother->getSymmetryPrecision()); - if (!sameprecision) return sd; - if (msymops != cother->msymops) return sd; - sd = this->PeriodicStructureAdapter::diff(other); - return sd; +int CrystalStructureAdapter::siteMultiplicity(int idx) const { + if (!this->isSymmetryCached()) this->updateSymmetryPositions(); + int rv = msymatoms[idx].size(); + return rv; } - -void CrystalStructureAdapter::setSymmetryPrecision(double eps) -{ - using namespace diffpy::validators; - ensureEpsilonPositive("symmetryprecision", eps); - if (eps != msymmetry_precision) msymmetry_cached = false; - msymmetry_precision = eps; +StructureDifference CrystalStructureAdapter::diff( + StructureAdapterConstPtr other) const { + StructureDifference sd = this->StructureAdapter::diff(other); + if (sd.stru0 == sd.stru1) return sd; + typedef boost::shared_ptr CPtr; + CPtr cother = boost::dynamic_pointer_cast(other); + if (!cother) return sd; + // compare symmetry operations in both adapters + assert(cother == sd.stru1); + const bool sameprecision = + (this->getSymmetryPrecision() == cother->getSymmetryPrecision()); + if (!sameprecision) return sd; + if (msymops != cother->msymops) return sd; + sd = this->PeriodicStructureAdapter::diff(other); + return sd; } - -const double& CrystalStructureAdapter::getSymmetryPrecision() const -{ - return msymmetry_precision; +void CrystalStructureAdapter::setSymmetryPrecision(double eps) { + using namespace diffpy::validators; + ensureEpsilonPositive("symmetryprecision", eps); + if (eps != msymmetry_precision) msymmetry_cached = false; + msymmetry_precision = eps; } - -int CrystalStructureAdapter::countSymOps() const -{ - return msymops.size(); +const double& CrystalStructureAdapter::getSymmetryPrecision() const { + return msymmetry_precision; } +int CrystalStructureAdapter::countSymOps() const { return msymops.size(); } -void CrystalStructureAdapter::clearSymOps() -{ - msymops.clear(); - msymmetry_cached = false; +void CrystalStructureAdapter::clearSymOps() { + msymops.clear(); + msymmetry_cached = false; } - -void CrystalStructureAdapter::addSymOp(const SymOpRotTrans& op) -{ - msymops.push_back(op); - msymmetry_cached = false; +void CrystalStructureAdapter::addSymOp(const SymOpRotTrans& op) { + msymops.push_back(op); + msymmetry_cached = false; } - -void CrystalStructureAdapter::addSymOp(const R3::Matrix& R, const R3::Vector& t) -{ - SymOpRotTrans op = {R, t}; - this->addSymOp(op); - assert(!msymmetry_cached); +void CrystalStructureAdapter::addSymOp(const R3::Matrix& R, + const R3::Vector& t) { + SymOpRotTrans op = {R, t}; + this->addSymOp(op); + assert(!msymmetry_cached); } - -const SymOpRotTrans& CrystalStructureAdapter::getSymOp(int i) const -{ - return msymops[i]; +const SymOpRotTrans& CrystalStructureAdapter::getSymOp(int i) const { + return msymops[i]; } - const CrystalStructureAdapter::AtomVector& -CrystalStructureAdapter::getEquivalentAtoms(int idx) const -{ - assert(0 <= idx && idx < this->countSites()); - if (!this->isSymmetryCached()) this->updateSymmetryPositions(); - return msymatoms[idx]; +CrystalStructureAdapter::getEquivalentAtoms(int idx) const { + assert(0 <= idx && idx < this->countSites()); + if (!this->isSymmetryCached()) this->updateSymmetryPositions(); + return msymatoms[idx]; } - -CrystalStructureAdapter::AtomVector -CrystalStructureAdapter::expandLatticeAtom(const Atom& a0) const -{ - using mathutils::eps_eq; - AtomVector eqsites; - vector eqsumpos; - vector eqduplicity; - eqsumpos.reserve(this->countSymOps()); - eqduplicity.reserve(this->countSymOps()); - const Lattice& L = this->getLattice(); - SymOpVector::const_iterator op = msymops.begin(); - Atom a1 = a0; - for (; op != msymops.end(); ++op) - { - // positions and Uij-s are actually fractional here - a1.xyz_cartn = R3::mxvecproduct(op->R, a0.xyz_cartn); - a1.xyz_cartn += op->t; - // check if a1 is a duplicate of an existing symmetry site - int ieq = this->findEqualPosition(eqsites, a1); - if (ieq < 0) - { - // a1 is a new symmetry site - R3::Matrix utmp = R3::prod(a0.uij_cartn, R3::trans(op->R)); - a1.uij_cartn = R3::prod(op->R, utmp); - eqsites.push_back(a1); - eqsumpos.push_back(R3::zerovector); - eqduplicity.push_back(0); - ieq = eqsites.size() - 1; - } - eqsumpos[ieq] += L.ucvFractional(a1.xyz_cartn); - eqduplicity[ieq] += 1; - } - // assume P1 if symmetry operations were not defined - if (msymops.empty()) - { - assert(eqsites.empty()); - assert(eqsumpos.empty()); - assert(eqduplicity.empty()); - eqsites.push_back(a0); - eqsumpos.push_back(a0.xyz_cartn); - eqduplicity.push_back(1); - } - // calculate mean values from equivalent sites and adjust any roundoffs - assert(eqsites.size() == eqduplicity.size()); - assert(eqsites.size() == eqsumpos.size()); - iterator ai = eqsites.begin(); - vector::const_iterator sii = eqsumpos.begin(); - vector::const_iterator dpi = eqduplicity.begin(); - for (; ai != eqsites.end(); ++ai, ++sii, ++dpi) - { - ai->xyz_cartn = (*sii) / (*dpi); +CrystalStructureAdapter::AtomVector CrystalStructureAdapter::expandLatticeAtom( + const Atom& a0) const { + using mathutils::eps_eq; + AtomVector eqsites; + vector eqsumpos; + vector eqduplicity; + eqsumpos.reserve(this->countSymOps()); + eqduplicity.reserve(this->countSymOps()); + const Lattice& L = this->getLattice(); + SymOpVector::const_iterator op = msymops.begin(); + Atom a1 = a0; + for (; op != msymops.end(); ++op) { + // positions and Uij-s are actually fractional here + a1.xyz_cartn = R3::mxvecproduct(op->R, a0.xyz_cartn); + a1.xyz_cartn += op->t; + // check if a1 is a duplicate of an existing symmetry site + int ieq = this->findEqualPosition(eqsites, a1); + if (ieq < 0) { + // a1 is a new symmetry site + R3::Matrix utmp = R3::prod(a0.uij_cartn, R3::trans(op->R)); + a1.uij_cartn = R3::prod(op->R, utmp); + eqsites.push_back(a1); + eqsumpos.push_back(R3::zerovector); + eqduplicity.push_back(0); + ieq = eqsites.size() - 1; } - return eqsites; + eqsumpos[ieq] += L.ucvFractional(a1.xyz_cartn); + eqduplicity[ieq] += 1; + } + // assume P1 if symmetry operations were not defined + if (msymops.empty()) { + assert(eqsites.empty()); + assert(eqsumpos.empty()); + assert(eqduplicity.empty()); + eqsites.push_back(a0); + eqsumpos.push_back(a0.xyz_cartn); + eqduplicity.push_back(1); + } + // calculate mean values from equivalent sites and adjust any roundoffs + assert(eqsites.size() == eqduplicity.size()); + assert(eqsites.size() == eqsumpos.size()); + iterator ai = eqsites.begin(); + vector::const_iterator sii = eqsumpos.begin(); + vector::const_iterator dpi = eqduplicity.begin(); + for (; ai != eqsites.end(); ++ai, ++sii, ++dpi) { + ai->xyz_cartn = (*sii) / (*dpi); + } + return eqsites; } - -void CrystalStructureAdapter::updateSymmetryPositions() const -{ - // build asymmetric unit in lattice coordinates - AtomVector lcatoms(this->begin(), this->end()); - AtomVector::iterator lcai = lcatoms.begin(); - for (; lcai != lcatoms.end(); ++lcai) this->toFractional(*lcai); - // build symmetry positions for all atoms in the asymmetric unit - msymatoms.resize(this->countSites()); - assert(lcatoms.size() == msymatoms.size()); - lcai = lcatoms.begin(); - std::vector::iterator saii = msymatoms.begin(); - for (; lcai != lcatoms.end(); ++lcai, ++saii) - { - *saii = this->expandLatticeAtom(*lcai); - iterator ai = saii->begin(); - for (; ai != saii->end(); ++ai) this->toCartesian(*ai); - } - msymmetry_cached = true; +void CrystalStructureAdapter::updateSymmetryPositions() const { + // build asymmetric unit in lattice coordinates + AtomVector lcatoms(this->begin(), this->end()); + AtomVector::iterator lcai = lcatoms.begin(); + for (; lcai != lcatoms.end(); ++lcai) this->toFractional(*lcai); + // build symmetry positions for all atoms in the asymmetric unit + msymatoms.resize(this->countSites()); + assert(lcatoms.size() == msymatoms.size()); + lcai = lcatoms.begin(); + std::vector::iterator saii = msymatoms.begin(); + for (; lcai != lcatoms.end(); ++lcai, ++saii) { + *saii = this->expandLatticeAtom(*lcai); + iterator ai = saii->begin(); + for (; ai != saii->end(); ++ai) this->toCartesian(*ai); + } + msymmetry_cached = true; } // Private Methods ----------------------------------------------------------- -int CrystalStructureAdapter::findEqualPosition( - const AtomVector& eqsites, const Atom& a0) const -{ - const double symeps = this->getSymmetryPrecision(); - const Lattice& L = this->getLattice(); - R3::Vector dxyz; - const_iterator ai = eqsites.begin(); - for (; ai != eqsites.end(); ++ai) - { - dxyz = ai->xyz_cartn - a0.xyz_cartn; - dxyz[0] -= round(dxyz[0]); - dxyz[1] -= round(dxyz[1]); - dxyz[2] -= round(dxyz[2]); - if (L.norm(dxyz) <= symeps) return (ai - eqsites.begin()); - } - return -1; +int CrystalStructureAdapter::findEqualPosition(const AtomVector& eqsites, + const Atom& a0) const { + const double symeps = this->getSymmetryPrecision(); + const Lattice& L = this->getLattice(); + R3::Vector dxyz; + const_iterator ai = eqsites.begin(); + for (; ai != eqsites.end(); ++ai) { + dxyz = ai->xyz_cartn - a0.xyz_cartn; + dxyz[0] -= round(dxyz[0]); + dxyz[1] -= round(dxyz[1]); + dxyz[2] -= round(dxyz[2]); + if (L.norm(dxyz) <= symeps) return (ai - eqsites.begin()); + } + return -1; } -bool CrystalStructureAdapter::isSymmetryCached() const -{ - msymmetry_cached = msymmetry_cached && - (int(msymatoms.size()) == this->countSites()); - return msymmetry_cached; +bool CrystalStructureAdapter::isSymmetryCached() const { + msymmetry_cached = + msymmetry_cached && (int(msymatoms.size()) == this->countSites()); + return msymmetry_cached; } // Comparison functions ------------------------------------------------------ -bool operator==( - const CrystalStructureAdapter& stru0, - const CrystalStructureAdapter& stru1) -{ - const PeriodicStructureAdapter& pstru0 = stru0; - const PeriodicStructureAdapter& pstru1 = stru1; - bool rv = (&stru0 == &stru1) || ( - (pstru0 == pstru1) && - (stru0.msymops == stru1.msymops)); - return rv; +bool operator==(const CrystalStructureAdapter& stru0, + const CrystalStructureAdapter& stru1) { + const PeriodicStructureAdapter& pstru0 = stru0; + const PeriodicStructureAdapter& pstru1 = stru1; + bool rv = (&stru0 == &stru1) || + ((pstru0 == pstru1) && (stru0.msymops == stru1.msymops)); + return rv; } - -bool operator!=( - const CrystalStructureAdapter& stru0, - const CrystalStructureAdapter& stru1) -{ - return !(stru0 == stru1); +bool operator!=(const CrystalStructureAdapter& stru0, + const CrystalStructureAdapter& stru1) { + return !(stru0 == stru1); } ////////////////////////////////////////////////////////////////////////////// @@ -284,85 +241,73 @@ bool operator!=( // Constructor --------------------------------------------------------------- CrystalStructureBondGenerator::CrystalStructureBondGenerator( - StructureAdapterConstPtr adpt) : PeriodicStructureBondGenerator(adpt) -{ - mcstructure = dynamic_cast(adpt.get()); - assert(mcstructure); - msymidx = 0; - mpuc1 = &(R3::zeromatrix()); + StructureAdapterConstPtr adpt) + : PeriodicStructureBondGenerator(adpt) { + mcstructure = dynamic_cast(adpt.get()); + assert(mcstructure); + msymidx = 0; + mpuc1 = &(R3::zeromatrix()); } // Public Methods ------------------------------------------------------------ -void CrystalStructureBondGenerator::selectAnchorSite(int anchor) -{ - this->BaseBondGenerator::selectAnchorSite(anchor); - const Atom& a0 = this->symatoms(anchor)[0]; - mr0 = a0.xyz_cartn; +void CrystalStructureBondGenerator::selectAnchorSite(int anchor) { + this->BaseBondGenerator::selectAnchorSite(anchor); + const Atom& a0 = this->symatoms(anchor)[0]; + mr0 = a0.xyz_cartn; } - -const R3::Matrix& CrystalStructureBondGenerator::Ucartesian1() const -{ - return *mpuc1; +const R3::Matrix& CrystalStructureBondGenerator::Ucartesian1() const { + return *mpuc1; } // Protected Methods --------------------------------------------------------- -bool CrystalStructureBondGenerator::iterateSymmetry() -{ - // Iterate the sphere at a fixed symmetry position. - if (this->PeriodicStructureBondGenerator::iterateSymmetry()) - { - this->updater1(); - return true; - } - // Advance to the next symmetry position. We are done if they - // were all already used. - const AtomVector& sa = this->symatoms(this->site1()); - ++msymidx; - if (msymidx >= sa.size()) return false; - // rewind the sphere for the new symmetry position. - // this calls CrystalStructureBondGenerator::updater1() - this->PeriodicStructureBondGenerator::rewindSymmetry(); +bool CrystalStructureBondGenerator::iterateSymmetry() { + // Iterate the sphere at a fixed symmetry position. + if (this->PeriodicStructureBondGenerator::iterateSymmetry()) { + this->updater1(); return true; + } + // Advance to the next symmetry position. We are done if they + // were all already used. + const AtomVector& sa = this->symatoms(this->site1()); + ++msymidx; + if (msymidx >= sa.size()) return false; + // rewind the sphere for the new symmetry position. + // this calls CrystalStructureBondGenerator::updater1() + this->PeriodicStructureBondGenerator::rewindSymmetry(); + return true; } - -void CrystalStructureBondGenerator::rewindSymmetry() -{ - msymidx = 0; - // this calls CrystalStructureBondGenerator::updater1() - this->PeriodicStructureBondGenerator::rewindSymmetry(); +void CrystalStructureBondGenerator::rewindSymmetry() { + msymidx = 0; + // this calls CrystalStructureBondGenerator::updater1() + this->PeriodicStructureBondGenerator::rewindSymmetry(); } - -void CrystalStructureBondGenerator::getNextBond() -{ - this->BaseBondGenerator::getNextBond(); +void CrystalStructureBondGenerator::getNextBond() { + this->BaseBondGenerator::getNextBond(); } - -void CrystalStructureBondGenerator::updater1() -{ - const AtomVector& sa = this->symatoms(this->site1()); - assert(msymidx < sa.size()); - mr1 = mrcsphere + sa[msymidx].xyz_cartn; - mpuc1 = &(sa[msymidx].uij_cartn); - this->updateDistance(); +void CrystalStructureBondGenerator::updater1() { + const AtomVector& sa = this->symatoms(this->site1()); + assert(msymidx < sa.size()); + mr1 = mrcsphere + sa[msymidx].xyz_cartn; + mpuc1 = &(sa[msymidx].uij_cartn); + this->updateDistance(); } // Private Methods ----------------------------------------------------------- const CrystalStructureAdapter::AtomVector& -CrystalStructureBondGenerator::symatoms(int idx) -{ - assert(0 <= idx && idx < int(mcstructure->msymatoms.size())); - return mcstructure->msymatoms[idx]; +CrystalStructureBondGenerator::symatoms(int idx) { + assert(0 <= idx && idx < int(mcstructure->msymatoms.size())); + return mcstructure->msymatoms[idx]; } -} // namespace srreal -} // namespace diffpy +} // namespace srreal +} // namespace diffpy // Serialization ------------------------------------------------------------- diff --git a/src/diffpy/srreal/CrystalStructureAdapter.hpp b/src/diffpy/srreal/CrystalStructureAdapter.hpp index ef74e33d..e0d0c225 100644 --- a/src/diffpy/srreal/CrystalStructureAdapter.hpp +++ b/src/diffpy/srreal/CrystalStructureAdapter.hpp @@ -1,24 +1,24 @@ /***************************************************************************** -* -* libdiffpy Complex Modeling Initiative -* (c) 2013 Brookhaven Science Associates, -* Brookhaven National Laboratory. -* All rights reserved. -* -* File coded by: Pavol Juhas -* -* See AUTHORS.txt for a list of people who contributed. -* See LICENSE.txt for license information. -* -****************************************************************************** -* -* class CrystalStructureAdapter -- universal adapter for crystal structure -* composed of asymmetric unit cell and list of symmetry operations in -* the space group -* -* class CrystalStructureBondGenerator -- bond generator -* -*****************************************************************************/ + * + * libdiffpy Complex Modeling Initiative + * (c) 2013 Brookhaven Science Associates, + * Brookhaven National Laboratory. + * All rights reserved. + * + * File coded by: Pavol Juhas + * + * See AUTHORS.txt for a list of people who contributed. + * See LICENSE.txt for license information. + * + ****************************************************************************** + * + * class CrystalStructureAdapter -- universal adapter for crystal structure + * composed of asymmetric unit cell and list of symmetry operations in + * the space group + * + * class CrystalStructureBondGenerator -- bond generator + * + *****************************************************************************/ #ifndef CRYSTALSTRUCTUREADAPTER_HPP_INCLUDED #define CRYSTALSTRUCTUREADAPTER_HPP_INCLUDED @@ -31,151 +31,132 @@ namespace diffpy { namespace srreal { /// store rotation matrix and translation vector for a symmetry operation -class SymOpRotTrans -{ - public: - - R3::Matrix R; - R3::Vector t; - - private: - - // serialization - friend class boost::serialization::access; - template - void serialize(Archive& ar, const unsigned int version) - { - ar & R & t; - } - +class SymOpRotTrans { + public: + R3::Matrix R; + R3::Vector t; + + private: + // serialization + friend class boost::serialization::access; + template + void serialize(Archive& ar, const unsigned int version) { + ar & R & t; + } }; // Comparison functions -inline -bool operator==(const SymOpRotTrans& op0, const SymOpRotTrans& op1) -{ - return (op0.R == op1.R) && (op0.t == op1.t); +inline bool operator==(const SymOpRotTrans& op0, const SymOpRotTrans& op1) { + return (op0.R == op1.R) && (op0.t == op1.t); } -inline -bool operator!=(const SymOpRotTrans& op0, const SymOpRotTrans& op1) -{ - return !(op0 == op1); +inline bool operator!=(const SymOpRotTrans& op0, const SymOpRotTrans& op1) { + return !(op0 == op1); } - -class DLL_EXPORT CrystalStructureAdapter : public PeriodicStructureAdapter -{ - friend class CrystalStructureBondGenerator; - - public: - - typedef std::vector SymOpVector; - // constructor - CrystalStructureAdapter(); - - // methods - overloaded - virtual StructureAdapterPtr clone() const; - virtual BaseBondGeneratorPtr createBondGenerator() const; - virtual int siteMultiplicity(int idx) const; - virtual StructureDifference diff(StructureAdapterConstPtr other) const; - - // methods - own - void setSymmetryPrecision(double eps); - const double& getSymmetryPrecision() const; - int countSymOps() const; - void clearSymOps(); - void addSymOp(const SymOpRotTrans&); - void addSymOp(const R3::Matrix& R, const R3::Vector& t); - const SymOpRotTrans& getSymOp(int i) const; - /// return all symmetry equivalent atoms in the unit cell for site i - const AtomVector& getEquivalentAtoms(int idx) const; - /// return all symmetry related atoms in fractional coordinates - AtomVector expandLatticeAtom(const Atom&) const; - void updateSymmetryPositions() const; - - private: - - // data - /// array of symmetry operations - SymOpVector msymops; - double msymmetry_precision; - mutable std::vector msymatoms; - mutable bool msymmetry_cached; - - // symmetry helpers - /// return index of AtomVector atom at an equal position or -1 - int findEqualPosition(const AtomVector&, const Atom&) const; - /// fuzzy check if symmetry positions are up to date - /// this only detects addition or removal of atom in the asymmetric - /// unit, but does not check for changes in atom positions. - bool isSymmetryCached() const; - - // comparison - friend DLL_EXPORT bool operator==( - const CrystalStructureAdapter&, const CrystalStructureAdapter&); - - // serialization - friend class boost::serialization::access; - template - void serialize(Archive& ar, const unsigned int version) - { - ar & boost::serialization::base_object(*this); - ar & msymops; - ar & msymmetry_precision; - ar & msymatoms; - ar & msymmetry_cached; - } - +class DLL_EXPORT CrystalStructureAdapter : public PeriodicStructureAdapter { + friend class CrystalStructureBondGenerator; + + public: + typedef std::vector SymOpVector; + // constructor + CrystalStructureAdapter(); + + // methods - overloaded + virtual StructureAdapterPtr clone() const; + virtual BaseBondGeneratorPtr createBondGenerator() const; + virtual int siteMultiplicity(int idx) const; + virtual StructureDifference diff(StructureAdapterConstPtr other) const; + + // methods - own + void setSymmetryPrecision(double eps); + const double& getSymmetryPrecision() const; + int countSymOps() const; + void clearSymOps(); + void addSymOp(const SymOpRotTrans&); + void addSymOp(const R3::Matrix& R, const R3::Vector& t); + const SymOpRotTrans& getSymOp(int i) const; + /// return all symmetry equivalent atoms in the unit cell for site i + const AtomVector& getEquivalentAtoms(int idx) const; + /// return all symmetry related atoms in fractional coordinates + AtomVector expandLatticeAtom(const Atom&) const; + void updateSymmetryPositions() const; + + private: + // data + /// array of symmetry operations + SymOpVector msymops; + double msymmetry_precision; + mutable std::vector msymatoms; + mutable bool msymmetry_cached; + + // symmetry helpers + /// return index of AtomVector atom at an equal position or -1 + int findEqualPosition(const AtomVector&, const Atom&) const; + /// fuzzy check if symmetry positions are up to date + /// this only detects addition or removal of atom in the asymmetric + /// unit, but does not check for changes in atom positions. + bool isSymmetryCached() const; + + // comparison + friend DLL_EXPORT bool operator==(const CrystalStructureAdapter&, + const CrystalStructureAdapter&); + + // serialization + friend class boost::serialization::access; + template + void serialize(Archive& ar, const unsigned int version) { + ar& boost::serialization::base_object(*this); + ar & msymops; + ar & msymmetry_precision; + ar & msymatoms; + ar & msymmetry_cached; + } }; - typedef boost::shared_ptr CrystalStructureAdapterPtr; // Comparison functions -DLL_EXPORT bool operator==(const CrystalStructureAdapter&, const CrystalStructureAdapter&); -DLL_EXPORT bool operator!=(const CrystalStructureAdapter&, const CrystalStructureAdapter&); - - -class DLL_EXPORT CrystalStructureBondGenerator : public PeriodicStructureBondGenerator -{ - public: - - // constructors - CrystalStructureBondGenerator(StructureAdapterConstPtr); - - // configuration - virtual void selectAnchorSite(int); - - // data access - virtual const R3::Matrix& Ucartesian1() const; +DLL_EXPORT bool operator==(const CrystalStructureAdapter&, + const CrystalStructureAdapter&); +DLL_EXPORT bool operator!=(const CrystalStructureAdapter&, + const CrystalStructureAdapter&); - protected: +class DLL_EXPORT CrystalStructureBondGenerator + : public PeriodicStructureBondGenerator { + public: + // constructors + CrystalStructureBondGenerator(StructureAdapterConstPtr); - // methods - virtual bool iterateSymmetry(); - virtual void rewindSymmetry(); - virtual void getNextBond(); - virtual void updater1(); + // configuration + virtual void selectAnchorSite(int); - // data - const CrystalStructureAdapter* mcstructure; - size_t msymidx; - const R3::Matrix* mpuc1; + // data access + virtual const R3::Matrix& Ucartesian1() const; - private: + protected: + // methods + virtual bool iterateSymmetry(); + virtual void rewindSymmetry(); + virtual void getNextBond(); + virtual void updater1(); - typedef CrystalStructureAdapter::AtomVector AtomVector; + // data + const CrystalStructureAdapter* mcstructure; + size_t msymidx; + const R3::Matrix* mpuc1; - // methods - const AtomVector& symatoms(int idx); + private: + typedef CrystalStructureAdapter::AtomVector AtomVector; + // methods + const AtomVector& symatoms(int idx); }; -} // namespace srreal -} // namespace diffpy +} // namespace srreal +} // namespace diffpy BOOST_CLASS_EXPORT_KEY(diffpy::srreal::CrystalStructureAdapter) diff --git a/src/diffpy/srreal/DebyePDFCalculator.cpp b/src/diffpy/srreal/DebyePDFCalculator.cpp index d77198ec..4491c840 100644 --- a/src/diffpy/srreal/DebyePDFCalculator.cpp +++ b/src/diffpy/srreal/DebyePDFCalculator.cpp @@ -1,20 +1,20 @@ /***************************************************************************** -* -* 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, Chris Farrow -* -* See AUTHORS.txt for a list of people who contributed. -* See LICENSE_DANSE.txt for license information. -* -****************************************************************************** -* -* class DebyePDFCalculator -- calculate PDF from the Debye equation. -* -*****************************************************************************/ + * + * 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, Chris Farrow + * + * See AUTHORS.txt for a list of people who contributed. + * See LICENSE_DANSE.txt for license information. + * + ****************************************************************************** + * + * class DebyePDFCalculator -- calculate PDF from the Debye equation. + * + *****************************************************************************/ #include #include @@ -38,191 +38,154 @@ const double DEFAULT_DEBYEPDFCALCULATOR_QMAX = 25.0; // Constructor --------------------------------------------------------------- -DebyePDFCalculator::DebyePDFCalculator() : - mqminpdf(0.0), - moptimumqstep(false), - mrstep(DEFAULT_PDFCALCULATOR_RSTEP), - mmaxextension(DEFAULT_PDFCALCULATOR_MAXEXTENSION), - mrcalclosteps(0), - mrcalchisteps(0), - mrlimits_are_cached(false) -{ - // default configuration - this->setScatteringFactorTableByType("xray"); - this->setRmax(DEFAULT_PDFCALCULATOR_RMAX); - this->setQmax(DEFAULT_DEBYEPDFCALCULATOR_QMAX); - this->setOptimumQstep(); - // envelopes - this->addEnvelopeByType("scale"); - this->addEnvelopeByType("qresolution"); - // cache all internal data according to an empty structure. - // this rebuilds mstructure_cache and mrlimits_cache. - this->setStructure(mstructure); - // attributes - this->registerDoubleAttribute("qmin", this, - &DebyePDFCalculator::getQmin, &DebyePDFCalculator::setQmin); - this->registerDoubleAttribute("qmax", this, - &DebyePDFCalculator::getQmax, &DebyePDFCalculator::setQmax); - this->registerDoubleAttribute("qstep", this, - &DebyePDFCalculator::getQstep, &DebyePDFCalculator::setQstep); - this->registerDoubleAttribute("rmin", this, - &DebyePDFCalculator::getRmin, &DebyePDFCalculator::setRmin); - this->registerDoubleAttribute("rmax", this, - &DebyePDFCalculator::getRmax, &DebyePDFCalculator::setRmax); - this->registerDoubleAttribute("rstep", this, - &DebyePDFCalculator::getRstep, &DebyePDFCalculator::setRstep); - this->registerDoubleAttribute("maxextension", this, - &DebyePDFCalculator::getMaxExtension, - &DebyePDFCalculator::setMaxExtension); - this->registerDoubleAttribute("extendedrmin", this, - &DebyePDFCalculator::rcalclo); - this->registerDoubleAttribute("extendedrmax", this, - &DebyePDFCalculator::rcalchi); +DebyePDFCalculator::DebyePDFCalculator() + : mqminpdf(0.0), + moptimumqstep(false), + mrstep(DEFAULT_PDFCALCULATOR_RSTEP), + mmaxextension(DEFAULT_PDFCALCULATOR_MAXEXTENSION), + mrcalclosteps(0), + mrcalchisteps(0), + mrlimits_are_cached(false) { + // default configuration + this->setScatteringFactorTableByType("xray"); + this->setRmax(DEFAULT_PDFCALCULATOR_RMAX); + this->setQmax(DEFAULT_DEBYEPDFCALCULATOR_QMAX); + this->setOptimumQstep(); + // envelopes + this->addEnvelopeByType("scale"); + this->addEnvelopeByType("qresolution"); + // cache all internal data according to an empty structure. + // this rebuilds mstructure_cache and mrlimits_cache. + this->setStructure(mstructure); + // attributes + this->registerDoubleAttribute("qmin", this, &DebyePDFCalculator::getQmin, + &DebyePDFCalculator::setQmin); + this->registerDoubleAttribute("qmax", this, &DebyePDFCalculator::getQmax, + &DebyePDFCalculator::setQmax); + this->registerDoubleAttribute("qstep", this, &DebyePDFCalculator::getQstep, + &DebyePDFCalculator::setQstep); + this->registerDoubleAttribute("rmin", this, &DebyePDFCalculator::getRmin, + &DebyePDFCalculator::setRmin); + this->registerDoubleAttribute("rmax", this, &DebyePDFCalculator::getRmax, + &DebyePDFCalculator::setRmax); + this->registerDoubleAttribute("rstep", this, &DebyePDFCalculator::getRstep, + &DebyePDFCalculator::setRstep); + this->registerDoubleAttribute("maxextension", this, + &DebyePDFCalculator::getMaxExtension, + &DebyePDFCalculator::setMaxExtension); + this->registerDoubleAttribute("extendedrmin", this, + &DebyePDFCalculator::rcalclo); + this->registerDoubleAttribute("extendedrmax", this, + &DebyePDFCalculator::rcalchi); } // Public Methods ------------------------------------------------------------ // PairQuantity overloads -eventticker::EventTicker& DebyePDFCalculator::ticker() const -{ - // collect composite tickers for the BaseDebyeSum parent class - eventticker::EventTicker& tic = this->BaseDebyeSum::ticker(); - assert(&mticker == &tic); - tic.updateFrom(this->ScatteringFactorTableOwner::ticker()); - return tic; +eventticker::EventTicker& DebyePDFCalculator::ticker() const { + // collect composite tickers for the BaseDebyeSum parent class + eventticker::EventTicker& tic = this->BaseDebyeSum::ticker(); + assert(&mticker == &tic); + tic.updateFrom(this->ScatteringFactorTableOwner::ticker()); + return tic; } // results -QuantityType DebyePDFCalculator::getPDF() const -{ - QuantityType rgrid = this->getRgrid(); - QuantityType pdf0 = this->getPDFAtQmin(this->getQmin()); - QuantityType pdf1 = this->applyEnvelopes(rgrid, pdf0); - return pdf1; +QuantityType DebyePDFCalculator::getPDF() const { + QuantityType rgrid = this->getRgrid(); + QuantityType pdf0 = this->getPDFAtQmin(this->getQmin()); + QuantityType pdf1 = this->applyEnvelopes(rgrid, pdf0); + return pdf1; } - -QuantityType DebyePDFCalculator::getRDF() const -{ - QuantityType rgrid = this->getRgrid(); - QuantityType rv = this->getRDFperR(); - assert(rv.size() == rgrid.size()); - transform(rgrid.begin(), rgrid.end(), rv.begin(), rv.begin(), +QuantityType DebyePDFCalculator::getRDF() const { + QuantityType rgrid = this->getRgrid(); + QuantityType rv = this->getRDFperR(); + assert(rv.size() == rgrid.size()); + transform(rgrid.begin(), rgrid.end(), rv.begin(), rv.begin(), multiplies()); - return rv; + return rv; } - -QuantityType DebyePDFCalculator::getRDFperR() const -{ - return this->getPDFAtQmin(0.0); +QuantityType DebyePDFCalculator::getRDFperR() const { + return this->getPDFAtQmin(0.0); } // Q-range configuration -void DebyePDFCalculator::setQmin(double qmin) -{ - // NOTE: do not update ticker here, because the actual - // Debye summation is always conducted from Qmin=0. - ensureNonNegative("Qmin", qmin); - this->BaseDebyeSum::setQmin(0.0); - mqminpdf = qmin; +void DebyePDFCalculator::setQmin(double qmin) { + // NOTE: do not update ticker here, because the actual + // Debye summation is always conducted from Qmin=0. + ensureNonNegative("Qmin", qmin); + this->BaseDebyeSum::setQmin(0.0); + mqminpdf = qmin; } +const double& DebyePDFCalculator::getQmin() const { return mqminpdf; } -const double& DebyePDFCalculator::getQmin() const -{ - return mqminpdf; +void DebyePDFCalculator::setQmax(double qmax) { + this->BaseDebyeSum::setQmax(qmax); + this->updateQstep(); } - -void DebyePDFCalculator::setQmax(double qmax) -{ - this->BaseDebyeSum::setQmax(qmax); - this->updateQstep(); +void DebyePDFCalculator::setQstep(double qstep) { + this->BaseDebyeSum::setQstep(qstep); + moptimumqstep = false; + this->updateQstep(); } - -void DebyePDFCalculator::setQstep(double qstep) -{ - this->BaseDebyeSum::setQstep(qstep); - moptimumqstep = false; - this->updateQstep(); -} - - -void DebyePDFCalculator::setOptimumQstep() -{ - moptimumqstep = true; - this->updateQstep(); +void DebyePDFCalculator::setOptimumQstep() { + moptimumqstep = true; + this->updateQstep(); } - -bool DebyePDFCalculator::isOptimumQstep() const -{ - return moptimumqstep; -} +bool DebyePDFCalculator::isOptimumQstep() const { return moptimumqstep; } // R-range methods -QuantityType DebyePDFCalculator::getRgrid() const -{ - return pdfutils_getRgrid(this); +QuantityType DebyePDFCalculator::getRgrid() const { + return pdfutils_getRgrid(this); } // R-range configuration -void DebyePDFCalculator::setRmin(double rmin) -{ - ensureNonNegative("Rmin", rmin); - if (mrmin != rmin) mrlimits_are_cached = false; - this->PairQuantity::setRmin(rmin); +void DebyePDFCalculator::setRmin(double rmin) { + ensureNonNegative("Rmin", rmin); + if (mrmin != rmin) mrlimits_are_cached = false; + this->PairQuantity::setRmin(rmin); } - -void DebyePDFCalculator::setRmax(double rmax) -{ - ensureNonNegative("Rmax", rmax); - if (mrmax != rmax) mrlimits_are_cached = false; - this->PairQuantity::setRmax(rmax); - this->updateQstep(); +void DebyePDFCalculator::setRmax(double rmax) { + ensureNonNegative("Rmax", rmax); + if (mrmax != rmax) mrlimits_are_cached = false; + this->PairQuantity::setRmax(rmax); + this->updateQstep(); } - -void DebyePDFCalculator::setRstep(double rstep) -{ - // NOTE: do not update ticker here, rstep is only used in the FFT - // and not in the Debye summation. - ensureEpsilonPositive("Rstep", rstep); - if (mrstep != rstep) mrlimits_are_cached = false; - mrstep = rstep; +void DebyePDFCalculator::setRstep(double rstep) { + // NOTE: do not update ticker here, rstep is only used in the FFT + // and not in the Debye summation. + ensureEpsilonPositive("Rstep", rstep); + if (mrstep != rstep) mrlimits_are_cached = false; + mrstep = rstep; } +const double& DebyePDFCalculator::getRstep() const { return mrstep; } -const double& DebyePDFCalculator::getRstep() const -{ - return mrstep; +void DebyePDFCalculator::setMaxExtension(double maxextension) { + ensureNonNegative("maxextension", maxextension); + if (mmaxextension != maxextension) { + mticker.click(); + mrlimits_are_cached = false; + } + mmaxextension = maxextension; + this->updateQstep(); } - -void DebyePDFCalculator::setMaxExtension(double maxextension) -{ - ensureNonNegative("maxextension", maxextension); - if (mmaxextension != maxextension) - { - mticker.click(); - mrlimits_are_cached = false; - } - mmaxextension = maxextension; - this->updateQstep(); -} - - -const double& DebyePDFCalculator::getMaxExtension() const -{ - return mmaxextension; +const double& DebyePDFCalculator::getMaxExtension() const { + return mmaxextension; } // Protected Methods --------------------------------------------------------- @@ -233,167 +196,142 @@ namespace { // single implementation for constant and non-constant accept template -void debyepdfcalc_accept(T* obj, diffpy::BaseAttributesVisitor& v) -{ - obj->getPeakWidthModel()->accept(v); - // PDF envelopes - set evnames = obj->usedEnvelopeTypes(); - set::const_iterator nm = evnames.begin(); - for (; nm != evnames.end(); ++nm) - { - obj->getEnvelopeByType(*nm)->accept(v); - } - // finally call standard accept - obj->diffpy::Attributes::accept(v); +void debyepdfcalc_accept(T* obj, diffpy::BaseAttributesVisitor& v) { + obj->getPeakWidthModel()->accept(v); + // PDF envelopes + set evnames = obj->usedEnvelopeTypes(); + set::const_iterator nm = evnames.begin(); + for (; nm != evnames.end(); ++nm) { + obj->getEnvelopeByType(*nm)->accept(v); + } + // finally call standard accept + obj->diffpy::Attributes::accept(v); } -} // namespace - +} // namespace -void DebyePDFCalculator::accept(diffpy::BaseAttributesVisitor& v) -{ - debyepdfcalc_accept(this, v); +void DebyePDFCalculator::accept(diffpy::BaseAttributesVisitor& v) { + debyepdfcalc_accept(this, v); } - -void DebyePDFCalculator::accept(diffpy::BaseAttributesVisitor& v) const -{ - debyepdfcalc_accept(this, v); +void DebyePDFCalculator::accept(diffpy::BaseAttributesVisitor& v) const { + debyepdfcalc_accept(this, v); } // BaseDebyeSum overloads -void DebyePDFCalculator::resetValue() -{ - this->cacheRlimitsData(); - this->updateQstep(); - this->BaseDebyeSum::resetValue(); +void DebyePDFCalculator::resetValue() { + this->cacheRlimitsData(); + this->updateQstep(); + this->BaseDebyeSum::resetValue(); } - -void DebyePDFCalculator::configureBondGenerator(BaseBondGenerator& bnds) const -{ - bnds.setRmin(this->rcalclo()); - bnds.setRmax(this->rcalchi()); +void DebyePDFCalculator::configureBondGenerator(BaseBondGenerator& bnds) const { + bnds.setRmin(this->rcalclo()); + bnds.setRmax(this->rcalchi()); } - -double DebyePDFCalculator::sfSiteAtQ(int siteidx, const double& Q) const -{ - const ScatteringFactorTablePtr& sftable = this->getScatteringFactorTable(); - const string& smbl = mstructure->siteAtomType(siteidx); - const double occupancy = mstructure->siteOccupancy(siteidx); - double rv = sftable->lookup(smbl, Q) * occupancy; - return rv; +double DebyePDFCalculator::sfSiteAtQ(int siteidx, const double& Q) const { + const ScatteringFactorTablePtr& sftable = this->getScatteringFactorTable(); + const string& smbl = mstructure->siteAtomType(siteidx); + const double occupancy = mstructure->siteOccupancy(siteidx); + double rv = sftable->lookup(smbl, Q) * occupancy; + return rv; } // Private Methods ----------------------------------------------------------- -QuantityType DebyePDFCalculator::getPDFAtQmin(double qmin) const -{ - // build a zero padded F vector that gives dr <= rstep - QuantityType fpad = this->getF(); - // zero all F values below qmin - int nqmin = pdfutils_qminSteps(qmin, this->getQstep()); - if (nqmin > int(fpad.size())) nqmin = fpad.size(); - fill(fpad.begin(), fpad.begin() + nqmin, 0.0); - int nfromdr = int(ceil(M_PI / this->getRstep() / this->getQstep())); - if (nfromdr > int(fpad.size())) fpad.resize(nfromdr, 0.0); - QuantityType gpad = fftftog(fpad, this->getQstep()); - const double drpad = M_PI / (gpad.size() * this->getQstep()); - QuantityType rgrid = this->getRgrid(); - QuantityType pdf0(rgrid.size()); - QuantityType::const_iterator ri = rgrid.begin(); - QuantityType::iterator pdfi = pdf0.begin(); - for (; ri != rgrid.end(); ++ri, ++pdfi) - { - double xdrp = *ri / drpad; - int iplo = int(xdrp); - int iphi = iplo + 1; - double wphi = xdrp - iplo; - double wplo = 1.0 - wphi; - assert(iphi < int(gpad.size())); - *pdfi = wplo * gpad[iplo] + wphi * gpad[iphi]; - } - return pdf0; +QuantityType DebyePDFCalculator::getPDFAtQmin(double qmin) const { + // build a zero padded F vector that gives dr <= rstep + QuantityType fpad = this->getF(); + // zero all F values below qmin + int nqmin = pdfutils_qminSteps(qmin, this->getQstep()); + if (nqmin > int(fpad.size())) nqmin = fpad.size(); + fill(fpad.begin(), fpad.begin() + nqmin, 0.0); + int nfromdr = int(ceil(M_PI / this->getRstep() / this->getQstep())); + if (nfromdr > int(fpad.size())) fpad.resize(nfromdr, 0.0); + QuantityType gpad = fftftog(fpad, this->getQstep()); + const double drpad = M_PI / (gpad.size() * this->getQstep()); + QuantityType rgrid = this->getRgrid(); + QuantityType pdf0(rgrid.size()); + QuantityType::const_iterator ri = rgrid.begin(); + QuantityType::iterator pdfi = pdf0.begin(); + for (; ri != rgrid.end(); ++ri, ++pdfi) { + double xdrp = *ri / drpad; + int iplo = int(xdrp); + int iphi = iplo + 1; + double wphi = xdrp - iplo; + double wplo = 1.0 - wphi; + assert(iphi < int(gpad.size())); + *pdfi = wplo * gpad[iplo] + wphi * gpad[iphi]; + } + return pdf0; } - -void DebyePDFCalculator::updateQstep() -{ - double rmaxext = this->rcalchi(); - // Use at least 4 steps to Qmax even for tiny rmaxext. - // Avoid division by zero. - double oqstep = (this->getQmax() * rmaxext / M_PI > 4) ? - (M_PI / rmaxext) : (this->getQmax() / 4); - // if custom qstep is higher than the optimum one, - // force adjustment to the optimum value. - if (this->getQstep() >= oqstep) moptimumqstep = true; - if (moptimumqstep) this->BaseDebyeSum::setQstep(oqstep); +void DebyePDFCalculator::updateQstep() { + double rmaxext = this->rcalchi(); + // Use at least 4 steps to Qmax even for tiny rmaxext. + // Avoid division by zero. + double oqstep = (this->getQmax() * rmaxext / M_PI > 4) + ? (M_PI / rmaxext) + : (this->getQmax() / 4); + // if custom qstep is higher than the optimum one, + // force adjustment to the optimum value. + if (this->getQstep() >= oqstep) moptimumqstep = true; + if (moptimumqstep) this->BaseDebyeSum::setQstep(oqstep); } - -double DebyePDFCalculator::rcalclo() const -{ - if (!mrlimits_are_cached) this->cacheRlimitsData(); - double rv = mrcalclosteps * this->getRstep(); - return rv; +double DebyePDFCalculator::rcalclo() const { + if (!mrlimits_are_cached) this->cacheRlimitsData(); + double rv = mrcalclosteps * this->getRstep(); + return rv; } - -double DebyePDFCalculator::rcalchi() const -{ - if (!mrlimits_are_cached) this->cacheRlimitsData(); - double rv = mrcalchisteps * this->getRstep(); - return rv; +double DebyePDFCalculator::rcalchi() const { + if (!mrlimits_are_cached) this->cacheRlimitsData(); + double rv = mrcalchisteps * this->getRstep(); + return rv; } - -double DebyePDFCalculator::extFromTerminationRipples() const -{ - // number of termination ripples for extending the r-range - const int nripples = 6; - // extension due to termination ripples - double rv = (this->getQmax() > 0.0) ? - (nripples*2*M_PI / this->getQmax()) : 0.0; - return rv; +double DebyePDFCalculator::extFromTerminationRipples() const { + // number of termination ripples for extending the r-range + const int nripples = 6; + // extension due to termination ripples + double rv = + (this->getQmax() > 0.0) ? (nripples * 2 * M_PI / this->getQmax()) : 0.0; + return rv; } - -double DebyePDFCalculator::extFromPeakTails() const -{ - const PeakWidthModel& pwm = *(this->getPeakWidthModel()); - double maxfwhm = pwm.maxWidth( - mstructure, this->getRmin(), this->getRmax()); - // assume Gaussian peak profile - GaussianProfile pkf; - pkf.setPrecision(DEFAULT_PEAKPRECISION); - // Gaussian function is symmetric, no need to use xboundlo - double rv = pkf.xboundhi(maxfwhm); - return rv; +double DebyePDFCalculator::extFromPeakTails() const { + const PeakWidthModel& pwm = *(this->getPeakWidthModel()); + double maxfwhm = pwm.maxWidth(mstructure, this->getRmin(), this->getRmax()); + // assume Gaussian peak profile + GaussianProfile pkf; + pkf.setPrecision(DEFAULT_PEAKPRECISION); + // Gaussian function is symmetric, no need to use xboundlo + double rv = pkf.xboundhi(maxfwhm); + return rv; } - -void DebyePDFCalculator::cacheRlimitsData() const -{ - const int minreductionsteps = 50; - double ext_total = min(this->getMaxExtension(), +void DebyePDFCalculator::cacheRlimitsData() const { + const int minreductionsteps = 50; + double ext_total = + min(this->getMaxExtension(), this->extFromTerminationRipples() + this->extFromPeakTails()); - // abbreviations - const double& rmin = this->getRmin(); - const double& rmax = this->getRmax(); - const double& dr = this->getRstep(); - mrcalclosteps = max(0, pdfutils_rminSteps(rmin - ext_total, dr)); - const int nhi = pdfutils_rmaxSteps(rmax + ext_total, dr); - if (nhi <= (mrcalchisteps - minreductionsteps) || nhi > mrcalchisteps) - { - mrcalchisteps = nhi; - } - mrlimits_are_cached = true; + // abbreviations + const double& rmin = this->getRmin(); + const double& rmax = this->getRmax(); + const double& dr = this->getRstep(); + mrcalclosteps = max(0, pdfutils_rminSteps(rmin - ext_total, dr)); + const int nhi = pdfutils_rmaxSteps(rmax + ext_total, dr); + if (nhi <= (mrcalchisteps - minreductionsteps) || nhi > mrcalchisteps) { + mrcalchisteps = nhi; + } + mrlimits_are_cached = true; } -} // namespace srreal -} // namespace diffpy +} // namespace srreal +} // namespace diffpy // Serialization ------------------------------------------------------------- diff --git a/src/diffpy/srreal/DebyePDFCalculator.hpp b/src/diffpy/srreal/DebyePDFCalculator.hpp index 5fb21bf8..8a5114c0 100644 --- a/src/diffpy/srreal/DebyePDFCalculator.hpp +++ b/src/diffpy/srreal/DebyePDFCalculator.hpp @@ -1,20 +1,20 @@ /***************************************************************************** -* -* 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, Chris Farrow -* -* See AUTHORS.txt for a list of people who contributed. -* See LICENSE_DANSE.txt for license information. -* -****************************************************************************** -* -* class DebyePDFCalculator -- calculate PDF from the Debye equation. -* -*****************************************************************************/ + * + * 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, Chris Farrow + * + * See AUTHORS.txt for a list of people who contributed. + * See LICENSE_DANSE.txt for license information. + * + ****************************************************************************** + * + * class DebyePDFCalculator -- calculate PDF from the Debye equation. + * + *****************************************************************************/ // Comments from Chris Farrow's sources for PDFNanoProfile: // @@ -57,106 +57,100 @@ namespace diffpy { namespace srreal { -class DLL_EXPORT DebyePDFCalculator : - public BaseDebyeSum, - public ScatteringFactorTableOwner, - public PDFEnvelopeOwner -{ - public: - - // constructor - DebyePDFCalculator(); - - // PairQuantity overloads - virtual eventticker::EventTicker& ticker() const; - - // results - /// PDF on the specified r-grid - QuantityType getPDF() const; - QuantityType getRDF() const; - QuantityType getRDFperR() const; - - // Q-range configuration - void setQmin(double); - const double& getQmin() const; - void setQmax(double); - void setQstep(double); - void setOptimumQstep(); - bool isOptimumQstep() const; - - // R-range methods - QuantityType getRgrid() const; - // R-range configuration - virtual void setRmin(double); - virtual void setRmax(double); - void setRstep(double); - const double& getRstep() const; - /// maximum total extension of the r-range accounting for both - /// termination ripples and peak tails - void setMaxExtension(double); - /// maximum total extension of the r-range accounting for both - /// termination ripples and peak tails - const double& getMaxExtension() const; - - protected: - - // Attributes overload to direct visitors around data structures - virtual void accept(diffpy::BaseAttributesVisitor& v); - virtual void accept(diffpy::BaseAttributesVisitor& v) const; - - // BaseDebyeSum overloads - virtual void resetValue(); - virtual void configureBondGenerator(BaseBondGenerator&) const; - virtual double sfSiteAtQ(int, const double& Q) const; - - private: - - // methods - QuantityType getPDFAtQmin(double qmin) const; - void updateQstep(); - /// complete lower bound extension of the calculated grid - double rcalclo() const; - /// complete upper bound extension of the calculated grid - double rcalchi() const; - /// r-range extension to allow propagation of termination ripples - double extFromTerminationRipples() const; - /// r-range extension to account for tails from out-of-range peaks - double extFromPeakTails() const; - void cacheRlimitsData() const; - - // data - double mqminpdf; - bool moptimumqstep; - double mrstep; - double mmaxextension; - mutable int mrcalclosteps; - mutable int mrcalchisteps; - mutable bool mrlimits_are_cached; - - // serialization - friend class boost::serialization::access; - template - void serialize(Archive& ar, const unsigned int version) - { - using boost::serialization::base_object; - ar & base_object(*this); - ar & base_object(*this); - ar & base_object(*this); - ar & mqminpdf; - ar & moptimumqstep; - ar & mrstep; - ar & mmaxextension; - ar & mrcalclosteps; - ar & mrcalchisteps; - if (version >= 1) { - ar & mrlimits_are_cached; - } - } +class DLL_EXPORT DebyePDFCalculator : public BaseDebyeSum, + public ScatteringFactorTableOwner, + public PDFEnvelopeOwner { + public: + // constructor + DebyePDFCalculator(); + + // PairQuantity overloads + virtual eventticker::EventTicker& ticker() const; + + // results + /// PDF on the specified r-grid + QuantityType getPDF() const; + QuantityType getRDF() const; + QuantityType getRDFperR() const; + + // Q-range configuration + void setQmin(double); + const double& getQmin() const; + void setQmax(double); + void setQstep(double); + void setOptimumQstep(); + bool isOptimumQstep() const; + + // R-range methods + QuantityType getRgrid() const; + // R-range configuration + virtual void setRmin(double); + virtual void setRmax(double); + void setRstep(double); + const double& getRstep() const; + /// maximum total extension of the r-range accounting for both + /// termination ripples and peak tails + void setMaxExtension(double); + /// maximum total extension of the r-range accounting for both + /// termination ripples and peak tails + const double& getMaxExtension() const; + + protected: + // Attributes overload to direct visitors around data structures + virtual void accept(diffpy::BaseAttributesVisitor& v); + virtual void accept(diffpy::BaseAttributesVisitor& v) const; + + // BaseDebyeSum overloads + virtual void resetValue(); + virtual void configureBondGenerator(BaseBondGenerator&) const; + virtual double sfSiteAtQ(int, const double& Q) const; + + private: + // methods + QuantityType getPDFAtQmin(double qmin) const; + void updateQstep(); + /// complete lower bound extension of the calculated grid + double rcalclo() const; + /// complete upper bound extension of the calculated grid + double rcalchi() const; + /// r-range extension to allow propagation of termination ripples + double extFromTerminationRipples() const; + /// r-range extension to account for tails from out-of-range peaks + double extFromPeakTails() const; + void cacheRlimitsData() const; + + // data + double mqminpdf; + bool moptimumqstep; + double mrstep; + double mmaxextension; + mutable int mrcalclosteps; + mutable int mrcalchisteps; + mutable bool mrlimits_are_cached; + + // serialization + friend class boost::serialization::access; + template + void serialize(Archive& ar, const unsigned int version) { + using boost::serialization::base_object; + ar& base_object(*this); + ar& base_object(*this); + ar& base_object(*this); + ar & mqminpdf; + ar & moptimumqstep; + ar & mrstep; + ar & mmaxextension; + ar & mrcalclosteps; + ar & mrcalchisteps; + if (version >= 1) { + ar & mrlimits_are_cached; + } + } }; // class DebyePDFCalculator -} // namespace srreal -} // namespace diffpy +} // namespace srreal +} // namespace diffpy // Serialization ------------------------------------------------------------- diff --git a/src/diffpy/srreal/DebyeWallerPeakWidth.cpp b/src/diffpy/srreal/DebyeWallerPeakWidth.cpp index 10c6bfe6..e5f52aec 100644 --- a/src/diffpy/srreal/DebyeWallerPeakWidth.cpp +++ b/src/diffpy/srreal/DebyeWallerPeakWidth.cpp @@ -1,21 +1,21 @@ /***************************************************************************** -* -* 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: Christopher Farrow, Pavol Juhas -* -* See AUTHORS.txt for a list of people who contributed. -* See LICENSE_DANSE.txt for license information. -* -****************************************************************************** -* -* class DebyeWallerPeakWidth -- peak width calculated assuming independent -* thermal vibrations of atoms forming a pair. -* -*****************************************************************************/ + * + * 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: Christopher Farrow, Pavol Juhas + * + * See AUTHORS.txt for a list of people who contributed. + * See LICENSE_DANSE.txt for license information. + * + ****************************************************************************** + * + * class DebyeWallerPeakWidth -- peak width calculated assuming independent + * thermal vibrations of atoms forming a pair. + * + *****************************************************************************/ #include #include @@ -28,52 +28,44 @@ using namespace std; // Constructors -------------------------------------------------------------- -PeakWidthModelPtr DebyeWallerPeakWidth::create() const -{ - PeakWidthModelPtr rv(new DebyeWallerPeakWidth()); - return rv; +PeakWidthModelPtr DebyeWallerPeakWidth::create() const { + PeakWidthModelPtr rv(new DebyeWallerPeakWidth()); + return rv; } - -PeakWidthModelPtr DebyeWallerPeakWidth::clone() const -{ - PeakWidthModelPtr rv(new DebyeWallerPeakWidth(*this)); - return rv; +PeakWidthModelPtr DebyeWallerPeakWidth::clone() const { + PeakWidthModelPtr rv(new DebyeWallerPeakWidth(*this)); + return rv; } // Public Methods ------------------------------------------------------------ -const string& DebyeWallerPeakWidth::type() const -{ - static const string rv = "debye-waller"; - return rv; +const string& DebyeWallerPeakWidth::type() const { + static const string rv = "debye-waller"; + return rv; } - -double DebyeWallerPeakWidth::calculate(const BaseBondGenerator& bnds) const -{ - using diffpy::mathutils::GAUSS_SIGMA_TO_FWHM; - double msdval = bnds.msd(); - double rv = (msdval < 0.0) ? 0.0 : GAUSS_SIGMA_TO_FWHM * sqrt(msdval); - return rv; +double DebyeWallerPeakWidth::calculate(const BaseBondGenerator& bnds) const { + using diffpy::mathutils::GAUSS_SIGMA_TO_FWHM; + double msdval = bnds.msd(); + double rv = (msdval < 0.0) ? 0.0 : GAUSS_SIGMA_TO_FWHM * sqrt(msdval); + return rv; } - -double DebyeWallerPeakWidth::maxWidth(StructureAdapterPtr stru, - double rmin, double rmax) const -{ - using diffpy::mathutils::GAUSS_SIGMA_TO_FWHM; - double maxmsd = 2 * maxUii(stru); - double rv = (maxmsd <= 0.0) ? 0.0 : GAUSS_SIGMA_TO_FWHM * sqrt(maxmsd); - return rv; +double DebyeWallerPeakWidth::maxWidth(StructureAdapterPtr stru, double rmin, + double rmax) const { + using diffpy::mathutils::GAUSS_SIGMA_TO_FWHM; + double maxmsd = 2 * maxUii(stru); + double rv = (maxmsd <= 0.0) ? 0.0 : GAUSS_SIGMA_TO_FWHM * sqrt(maxmsd); + return rv; } // Registration -------------------------------------------------------------- bool reg_DebyeWallerPeakWidth = DebyeWallerPeakWidth().registerThisType(); -} // namespace srreal -} // namespace diffpy +} // namespace srreal +} // namespace diffpy // Serialization ------------------------------------------------------------- diff --git a/src/diffpy/srreal/DebyeWallerPeakWidth.hpp b/src/diffpy/srreal/DebyeWallerPeakWidth.hpp index 25cb9f4b..a6e127c9 100644 --- a/src/diffpy/srreal/DebyeWallerPeakWidth.hpp +++ b/src/diffpy/srreal/DebyeWallerPeakWidth.hpp @@ -1,21 +1,21 @@ /***************************************************************************** -* -* 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: Christopher Farrow, Pavol Juhas -* -* See AUTHORS.txt for a list of people who contributed. -* See LICENSE_DANSE.txt for license information. -* -****************************************************************************** -* -* class DebyeWallerPeakWidth -- peak width calculated assuming independent -* thermal vibrations of atoms forming a pair. -* -*****************************************************************************/ + * + * 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: Christopher Farrow, Pavol Juhas + * + * See AUTHORS.txt for a list of people who contributed. + * See LICENSE_DANSE.txt for license information. + * + ****************************************************************************** + * + * class DebyeWallerPeakWidth -- peak width calculated assuming independent + * thermal vibrations of atoms forming a pair. + * + *****************************************************************************/ #ifndef DEBYEWALLERPEAKWIDTH_HPP_INCLUDED #define DEBYEWALLERPEAKWIDTH_HPP_INCLUDED @@ -27,37 +27,30 @@ namespace diffpy { namespace srreal { - -class DLL_EXPORT DebyeWallerPeakWidth : public PeakWidthModel -{ - public: - - // constructors - virtual PeakWidthModelPtr create() const; - virtual PeakWidthModelPtr clone() const; - - // methods - virtual const std::string& type() const; - virtual double calculate(const BaseBondGenerator&) const; - virtual double maxWidth(StructureAdapterPtr, - double rmin, double rmax) const; - - private: - - // serialization - friend class boost::serialization::access; - - template - void serialize(Archive& ar, const unsigned int version) - { - using boost::serialization::base_object; - ar & base_object(*this); - } - +class DLL_EXPORT DebyeWallerPeakWidth : public PeakWidthModel { + public: + // constructors + virtual PeakWidthModelPtr create() const; + virtual PeakWidthModelPtr clone() const; + + // methods + virtual const std::string& type() const; + virtual double calculate(const BaseBondGenerator&) const; + virtual double maxWidth(StructureAdapterPtr, double rmin, double rmax) const; + + private: + // serialization + friend class boost::serialization::access; + + template + void serialize(Archive& ar, const unsigned int version) { + using boost::serialization::base_object; + ar& base_object(*this); + } }; -} // namespace srreal -} // namespace diffpy +} // namespace srreal +} // namespace diffpy // Serialization ------------------------------------------------------------- diff --git a/src/diffpy/srreal/EmptyStructureAdapter.cpp b/src/diffpy/srreal/EmptyStructureAdapter.cpp index 84c0658d..46273ac0 100644 --- a/src/diffpy/srreal/EmptyStructureAdapter.cpp +++ b/src/diffpy/srreal/EmptyStructureAdapter.cpp @@ -1,20 +1,20 @@ /***************************************************************************** -* -* libdiffpy Complex Modeling Initiative -* (c) 2016 Brookhaven Science Associates, -* Brookhaven National Laboratory. -* All rights reserved. -* -* File coded by: Pavol Juhas -* -* See AUTHORS.txt for a list of people who contributed. -* See LICENSE.txt for license information. -* -****************************************************************************** -* -* class EmptyStructureAdapter -- immutable class that is always empty. -* -*****************************************************************************/ + * + * libdiffpy Complex Modeling Initiative + * (c) 2016 Brookhaven Science Associates, + * Brookhaven National Laboratory. + * All rights reserved. + * + * File coded by: Pavol Juhas + * + * See AUTHORS.txt for a list of people who contributed. + * See LICENSE.txt for license information. + * + ****************************************************************************** + * + * class EmptyStructureAdapter -- immutable class that is always empty. + * + *****************************************************************************/ #include @@ -30,101 +30,69 @@ namespace { std::out_of_range outofrange("Index out of range."); -} // namespace +} // namespace ////////////////////////////////////////////////////////////////////////////// // class EmptyStructureAdapter ////////////////////////////////////////////////////////////////////////////// -class EmptyStructureAdapter : public StructureAdapter -{ - public: +class EmptyStructureAdapter : public StructureAdapter { + public: + // virtual methods from the base class - // virtual methods from the base class + virtual StructureAdapterPtr clone() const { + StructureAdapterPtr rv = + boost::const_pointer_cast(shared_from_this()); + return rv; + } - virtual StructureAdapterPtr clone() const - { - StructureAdapterPtr rv = - boost::const_pointer_cast(shared_from_this()); - return rv; - } + virtual BaseBondGeneratorPtr createBondGenerator() const { + return boost::make_shared(shared_from_this()); + } + virtual int countSites() const { return 0; } - virtual BaseBondGeneratorPtr createBondGenerator() const - { - return boost::make_shared(shared_from_this()); - } + // reusing StructureAdapter::totalOccupancy() + // reusing StructureAdapter::numberDensity() + virtual const std::string& siteAtomType(int idx) const { throw outofrange; } - virtual int countSites() const - { - return 0; - } + virtual const R3::Vector& siteCartesianPosition(int idx) const { + throw outofrange; + } - // reusing StructureAdapter::totalOccupancy() - // reusing StructureAdapter::numberDensity() + virtual int siteMultiplicity(int idx) const { throw outofrange; } - virtual const std::string& siteAtomType(int idx) const - { - throw outofrange; - } + virtual double siteOccupancy(int idx) const { throw outofrange; } + virtual bool siteAnisotropy(int idx) const { throw outofrange; } - virtual const R3::Vector& siteCartesianPosition(int idx) const - { - throw outofrange; - } + virtual const R3::Matrix& siteCartesianUij(int idx) const { + throw outofrange; + } + // reusing StructureAdapter::customPQConfig() + // reusing StructureAdapter::diff() - virtual int siteMultiplicity(int idx) const - { - throw outofrange; - } - - - virtual double siteOccupancy(int idx) const - { - throw outofrange; - } - - - virtual bool siteAnisotropy(int idx) const - { - throw outofrange; - } - - - virtual const R3::Matrix& siteCartesianUij(int idx) const - { - throw outofrange; - } - - // reusing StructureAdapter::customPQConfig() - // reusing StructureAdapter::diff() - - private: - - // serialization - friend class boost::serialization::access; - template - void serialize(Archive& ar, const unsigned int version) - { - ar & boost::serialization::base_object(*this); - } - + private: + // serialization + friend class boost::serialization::access; + template + void serialize(Archive& ar, const unsigned int version) { + ar& boost::serialization::base_object(*this); + } }; // Routines ------------------------------------------------------------------ -StructureAdapterPtr emptyStructureAdapter() -{ - static StructureAdapterPtr stru(new EmptyStructureAdapter); - assert(stru && stru->countSites() == 0); - return stru; +StructureAdapterPtr emptyStructureAdapter() { + static StructureAdapterPtr stru(new EmptyStructureAdapter); + assert(stru && stru->countSites() == 0); + return stru; } -} // namespace srreal -} // namespace diffpy +} // namespace srreal +} // namespace diffpy BOOST_CLASS_EXPORT(diffpy::srreal::EmptyStructureAdapter) DIFFPY_INSTANTIATE_PTR_SERIALIZATION(diffpy::srreal::EmptyStructureAdapter) diff --git a/src/diffpy/srreal/GaussianProfile.cpp b/src/diffpy/srreal/GaussianProfile.cpp index 97622474..e2cc69ef 100644 --- a/src/diffpy/srreal/GaussianProfile.cpp +++ b/src/diffpy/srreal/GaussianProfile.cpp @@ -1,21 +1,21 @@ /***************************************************************************** -* -* 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.txt for a list of people who contributed. -* See LICENSE_DANSE.txt for license information. -* -****************************************************************************** -* -* class GaussianProfile -- concrete implementation of the PeakProfile class. -* GaussianProfile is registered as "gaussian". -* -*****************************************************************************/ + * + * 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.txt for a list of people who contributed. + * See LICENSE_DANSE.txt for license information. + * + ****************************************************************************** + * + * class GaussianProfile -- concrete implementation of the PeakProfile class. + * GaussianProfile is registered as "gaussian". + * + *****************************************************************************/ #include @@ -32,72 +32,60 @@ using diffpy::mathutils::DOUBLE_EPS; // Constructors -------------------------------------------------------------- -GaussianProfile::GaussianProfile() -{ - // call setPrecision to update mhalfboundrel - this->setPrecision(DOUBLE_EPS); +GaussianProfile::GaussianProfile() { + // call setPrecision to update mhalfboundrel + this->setPrecision(DOUBLE_EPS); } - -PeakProfilePtr GaussianProfile::create() const -{ - PeakProfilePtr rv(new GaussianProfile()); - return rv; +PeakProfilePtr GaussianProfile::create() const { + PeakProfilePtr rv(new GaussianProfile()); + return rv; } - -PeakProfilePtr GaussianProfile::clone() const -{ - PeakProfilePtr rv(new GaussianProfile(*this)); - return rv; +PeakProfilePtr GaussianProfile::clone() const { + PeakProfilePtr rv(new GaussianProfile(*this)); + return rv; } // Public Methods ------------------------------------------------------------ -const string& GaussianProfile::type() const -{ - static string rv = "gaussian"; - return rv; +const string& GaussianProfile::type() const { + static string rv = "gaussian"; + return rv; } - -double GaussianProfile::operator()(double x, double fwhm) const -{ - if ( fwhm <= 0 ) return 0.0; - double xrel = x / fwhm; - double rv = 2 * sqrt(M_LN2 / M_PI) / fwhm * exp(-4 * M_LN2 * xrel * xrel); - return rv; +double GaussianProfile::operator()(double x, double fwhm) const { + if (fwhm <= 0) return 0.0; + double xrel = x / fwhm; + double rv = 2 * sqrt(M_LN2 / M_PI) / fwhm * exp(-4 * M_LN2 * xrel * xrel); + return rv; } - -double GaussianProfile::xboundlo(double fwhm) const -{ - return -1 * this->GaussianProfile::xboundhi(fwhm); +double GaussianProfile::xboundlo(double fwhm) const { + return -1 * this->GaussianProfile::xboundhi(fwhm); } - -double GaussianProfile::xboundhi(double fwhm) const -{ - double rv = (fwhm <= 0.0) ? 0.0 : (mhalfboundrel * fwhm); - return rv; +double GaussianProfile::xboundhi(double fwhm) const { + double rv = (fwhm <= 0.0) ? 0.0 : (mhalfboundrel * fwhm); + return rv; } - -void GaussianProfile::setPrecision(double eps) -{ - // correct any settings below DOUBLE_EPS - double eps1 = max(eps, DOUBLE_EPS); - this->PeakProfile::setPrecision(eps1); - if (eps1 < 1.0) mhalfboundrel = sqrt(-log(eps1) / (4 * M_LN2)); - else mhalfboundrel = 0.0; +void GaussianProfile::setPrecision(double eps) { + // correct any settings below DOUBLE_EPS + double eps1 = max(eps, DOUBLE_EPS); + this->PeakProfile::setPrecision(eps1); + if (eps1 < 1.0) + mhalfboundrel = sqrt(-log(eps1) / (4 * M_LN2)); + else + mhalfboundrel = 0.0; } // Registration -------------------------------------------------------------- bool reg_GaussianProfile = GaussianProfile().registerThisType(); -} // namespace srreal -} // namespace diffpy +} // namespace srreal +} // namespace diffpy // Serialization ------------------------------------------------------------- diff --git a/src/diffpy/srreal/GaussianProfile.hpp b/src/diffpy/srreal/GaussianProfile.hpp index d193897d..f5a777e1 100644 --- a/src/diffpy/srreal/GaussianProfile.hpp +++ b/src/diffpy/srreal/GaussianProfile.hpp @@ -1,21 +1,21 @@ /***************************************************************************** -* -* 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.txt for a list of people who contributed. -* See LICENSE_DANSE.txt for license information. -* -****************************************************************************** -* -* class GaussianProfile -- concrete implementation of the PeakProfile class. -* GaussianProfile is registered as "gaussian". -* -*****************************************************************************/ + * + * 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.txt for a list of people who contributed. + * See LICENSE_DANSE.txt for license information. + * + ****************************************************************************** + * + * class GaussianProfile -- concrete implementation of the PeakProfile class. + * GaussianProfile is registered as "gaussian". + * + *****************************************************************************/ #ifndef GAUSSIANPROFILE_HPP_INCLUDED #define GAUSSIANPROFILE_HPP_INCLUDED @@ -27,44 +27,38 @@ namespace diffpy { namespace srreal { -class DLL_EXPORT GaussianProfile : public PeakProfile -{ - public: - - // constructors - GaussianProfile(); - PeakProfilePtr create() const; - PeakProfilePtr clone() const; - - // methods - const std::string& type() const; - double operator()(double x, double fwhm) const; - double xboundlo(double fwhm) const; - double xboundhi(double fwhm) const; - void setPrecision(double eps); - - protected: - - // data - double mhalfboundrel; - - private: - - // serialization - friend class boost::serialization::access; - - template - void serialize(Archive& ar, const unsigned int version) - { - using boost::serialization::base_object; - ar & base_object(*this); - ar & mhalfboundrel; - } - +class DLL_EXPORT GaussianProfile : public PeakProfile { + public: + // constructors + GaussianProfile(); + PeakProfilePtr create() const; + PeakProfilePtr clone() const; + + // methods + const std::string& type() const; + double operator()(double x, double fwhm) const; + double xboundlo(double fwhm) const; + double xboundhi(double fwhm) const; + void setPrecision(double eps); + + protected: + // data + double mhalfboundrel; + + private: + // serialization + friend class boost::serialization::access; + + template + void serialize(Archive& ar, const unsigned int version) { + using boost::serialization::base_object; + ar& base_object(*this); + ar & mhalfboundrel; + } }; -} // namespace srreal -} // namespace diffpy +} // namespace srreal +} // namespace diffpy // Serialization ------------------------------------------------------------- diff --git a/src/diffpy/srreal/JeongPeakWidth.cpp b/src/diffpy/srreal/JeongPeakWidth.cpp index 7cd9679e..cf1874fc 100644 --- a/src/diffpy/srreal/JeongPeakWidth.cpp +++ b/src/diffpy/srreal/JeongPeakWidth.cpp @@ -1,22 +1,22 @@ /***************************************************************************** -* -* 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: Christopher Farrow, Pavol Juhas -* -* See AUTHORS.txt for a list of people who contributed. -* See LICENSE_DANSE.txt for license information. -* -****************************************************************************** -* -* class DebyeWallerPeakWidth -- peak width model based on -* I.-K. Jeong, et al., Phys. Rev. B 67, 104301 (2003) -* http://link.aps.org/doi/10.1103/PhysRevB.67.104301 -* -*****************************************************************************/ + * + * 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: Christopher Farrow, Pavol Juhas + * + * See AUTHORS.txt for a list of people who contributed. + * See LICENSE_DANSE.txt for license information. + * + ****************************************************************************** + * + * class DebyeWallerPeakWidth -- peak width model based on + * I.-K. Jeong, et al., Phys. Rev. B 67, 104301 (2003) + * http://link.aps.org/doi/10.1103/PhysRevB.67.104301 + * + *****************************************************************************/ #include #include @@ -29,135 +29,105 @@ using namespace std; // Constructors -------------------------------------------------------------- -JeongPeakWidth::JeongPeakWidth() : - mdelta1(0.0), mdelta2(0.0), mqbroad(0.0), mqbroad_seperable(0.0) -{ - this->registerDoubleAttribute("delta1", - this, &JeongPeakWidth::getDelta1, &JeongPeakWidth::setDelta1); - this->registerDoubleAttribute("delta2", - this, &JeongPeakWidth::getDelta2, &JeongPeakWidth::setDelta2); - this->registerDoubleAttribute("qbroad", - this, &JeongPeakWidth::getQbroad, &JeongPeakWidth::setQbroad); - this->registerDoubleAttribute("qbroad_seperable", - this, &JeongPeakWidth::getQbroad_seperable, &JeongPeakWidth::setQbroad_seperable); +JeongPeakWidth::JeongPeakWidth() + : mdelta1(0.0), mdelta2(0.0), mqbroad(0.0), mqbroad_seperable(0.0) { + this->registerDoubleAttribute("delta1", this, &JeongPeakWidth::getDelta1, + &JeongPeakWidth::setDelta1); + this->registerDoubleAttribute("delta2", this, &JeongPeakWidth::getDelta2, + &JeongPeakWidth::setDelta2); + this->registerDoubleAttribute("qbroad", this, &JeongPeakWidth::getQbroad, + &JeongPeakWidth::setQbroad); + this->registerDoubleAttribute("qbroad_seperable", this, + &JeongPeakWidth::getQbroad_seperable, + &JeongPeakWidth::setQbroad_seperable); } - -PeakWidthModelPtr JeongPeakWidth::create() const -{ - PeakWidthModelPtr rv(new JeongPeakWidth()); - return rv; +PeakWidthModelPtr JeongPeakWidth::create() const { + PeakWidthModelPtr rv(new JeongPeakWidth()); + return rv; } - -PeakWidthModelPtr JeongPeakWidth::clone() const -{ - PeakWidthModelPtr rv(new JeongPeakWidth(*this)); - return rv; +PeakWidthModelPtr JeongPeakWidth::clone() const { + PeakWidthModelPtr rv(new JeongPeakWidth(*this)); + return rv; } // Public Methods ------------------------------------------------------------ -const string& JeongPeakWidth::type() const -{ - static const string rv = "jeong"; - return rv; -} - - -double JeongPeakWidth::calculate(const BaseBondGenerator& bnds) const -{ - double r = bnds.distance(); - double corr = this->msdSharpeningRatio(r); - // avoid calculating square root of negative value - double fwhm = (corr <= 0) ? 0.0 : - (sqrt(corr) * this->DebyeWallerPeakWidth::calculate(bnds) + - pow(this->getQbroad_seperable()*r, 2)); - return fwhm; -} - - -double JeongPeakWidth::maxWidth(StructureAdapterPtr stru, - double rmin, double rmax) const -{ - double maxwidth0 = this->DebyeWallerPeakWidth::maxWidth(stru, rmin, rmax); - double maxmsdsharp = max( - this->msdSharpeningRatio(rmin), - this->msdSharpeningRatio(rmax)); - // if the sharpening factor is larger than 1 adjust the maximum width - double rv = maxwidth0 * sqrt(max(1.0, maxmsdsharp))+ pow(this->getQbroad_seperable()*rmax, 2); - return rv; +const string& JeongPeakWidth::type() const { + static const string rv = "jeong"; + return rv; } -const double& JeongPeakWidth::getDelta1() const -{ - return mdelta1; +double JeongPeakWidth::calculate(const BaseBondGenerator& bnds) const { + double r = bnds.distance(); + double corr = this->msdSharpeningRatio(r); + // avoid calculating square root of negative value + double fwhm = (corr <= 0) + ? 0.0 + : (sqrt(corr) * this->DebyeWallerPeakWidth::calculate(bnds) + + pow(this->getQbroad_seperable() * r, 2)); + return fwhm; } - -void JeongPeakWidth::setDelta1(double delta1) -{ - if (mdelta1 != delta1) mticker.click(); - mdelta1 = delta1; +double JeongPeakWidth::maxWidth(StructureAdapterPtr stru, double rmin, + double rmax) const { + double maxwidth0 = this->DebyeWallerPeakWidth::maxWidth(stru, rmin, rmax); + double maxmsdsharp = + max(this->msdSharpeningRatio(rmin), this->msdSharpeningRatio(rmax)); + // if the sharpening factor is larger than 1 adjust the maximum width + double rv = maxwidth0 * sqrt(max(1.0, maxmsdsharp)) + + pow(this->getQbroad_seperable() * rmax, 2); + return rv; } +const double& JeongPeakWidth::getDelta1() const { return mdelta1; } -const double& JeongPeakWidth::getDelta2() const -{ - return mdelta2; +void JeongPeakWidth::setDelta1(double delta1) { + if (mdelta1 != delta1) mticker.click(); + mdelta1 = delta1; } +const double& JeongPeakWidth::getDelta2() const { return mdelta2; } -void JeongPeakWidth::setDelta2(double delta2) -{ - if (mdelta2 != delta2) mticker.click(); - mdelta2 = delta2; +void JeongPeakWidth::setDelta2(double delta2) { + if (mdelta2 != delta2) mticker.click(); + mdelta2 = delta2; } +const double& JeongPeakWidth::getQbroad() const { return mqbroad; } -const double& JeongPeakWidth::getQbroad() const -{ - return mqbroad; +const double& JeongPeakWidth::getQbroad_seperable() const { + return mqbroad_seperable; } - -const double& JeongPeakWidth::getQbroad_seperable() const -{ - return mqbroad_seperable; +void JeongPeakWidth::setQbroad_seperable(double qbroad_seperable) { + if (mqbroad_seperable != qbroad_seperable) mticker.click(); + mqbroad_seperable = qbroad_seperable; } - -void JeongPeakWidth::setQbroad_seperable(double qbroad_seperable) -{ - if (mqbroad_seperable != qbroad_seperable) mticker.click(); - mqbroad_seperable = qbroad_seperable; -} - - -void JeongPeakWidth::setQbroad(double qbroad) -{ - if (mqbroad != qbroad) mticker.click(); - mqbroad = qbroad; +void JeongPeakWidth::setQbroad(double qbroad) { + if (mqbroad != qbroad) mticker.click(); + mqbroad = qbroad; } // Private Methods ----------------------------------------------------------- -double JeongPeakWidth::msdSharpeningRatio(const double& r) const -{ - using diffpy::mathutils::DOUBLE_EPS; - // avoid division by zero - if (r < DOUBLE_EPS) return 0.0; - double rv = 1.0 - this->getDelta1()/r - this->getDelta2()/pow(r, 2) + - pow(this->getQbroad()*r, 2); - return rv; +double JeongPeakWidth::msdSharpeningRatio(const double& r) const { + using diffpy::mathutils::DOUBLE_EPS; + // avoid division by zero + if (r < DOUBLE_EPS) return 0.0; + double rv = 1.0 - this->getDelta1() / r - this->getDelta2() / pow(r, 2) + + pow(this->getQbroad() * r, 2); + return rv; } // Registration -------------------------------------------------------------- bool reg_JeongPeakWidth = JeongPeakWidth().registerThisType(); -} // namespace srreal -} // namespace diffpy +} // namespace srreal +} // namespace diffpy // Serialization ------------------------------------------------------------- diff --git a/src/diffpy/srreal/JeongPeakWidth.hpp b/src/diffpy/srreal/JeongPeakWidth.hpp index 6518e5c6..ff4c8a9d 100644 --- a/src/diffpy/srreal/JeongPeakWidth.hpp +++ b/src/diffpy/srreal/JeongPeakWidth.hpp @@ -1,22 +1,22 @@ /***************************************************************************** -* -* 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: Christopher Farrow, Pavol Juhas -* -* See AUTHORS.txt for a list of people who contributed. -* See LICENSE_DANSE.txt for license information. -* -****************************************************************************** -* -* class DebyeWallerPeakWidth -- peak width model based on -* I.-K. Jeong, et al., Phys. Rev. B 67, 104301 (2003) -* http://link.aps.org/doi/10.1103/PhysRevB.67.104301 -* -*****************************************************************************/ + * + * 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: Christopher Farrow, Pavol Juhas + * + * See AUTHORS.txt for a list of people who contributed. + * See LICENSE_DANSE.txt for license information. + * + ****************************************************************************** + * + * class DebyeWallerPeakWidth -- peak width model based on + * I.-K. Jeong, et al., Phys. Rev. B 67, 104301 (2003) + * http://link.aps.org/doi/10.1103/PhysRevB.67.104301 + * + *****************************************************************************/ #ifndef JEONGPEAKWIDTH_HPP_INCLUDED #define JEONGPEAKWIDTH_HPP_INCLUDED @@ -28,62 +28,54 @@ namespace diffpy { namespace srreal { - -class DLL_EXPORT JeongPeakWidth : public DebyeWallerPeakWidth -{ - public: - - // constructors - JeongPeakWidth(); - virtual PeakWidthModelPtr create() const; - virtual PeakWidthModelPtr clone() const; - - // methods - virtual const std::string& type() const; - virtual double calculate(const BaseBondGenerator&) const; - virtual double maxWidth(StructureAdapterPtr, - double rmin, double rmax) const; - - // data access - const double& getDelta1() const; - void setDelta1(double); - const double& getDelta2() const; - void setDelta2(double); - const double& getQbroad() const; - void setQbroad(double); - const double& getQbroad_seperable() const; - void setQbroad_seperable(double); - - private: - - // data - double mdelta1; - double mdelta2; - double mqbroad; - double mqbroad_seperable; - - // methods - double msdSharpeningRatio(const double& r) const; - - // serialization - friend class boost::serialization::access; - - template - void serialize(Archive& ar, const unsigned int version) - { - using boost::serialization::base_object; - ar & base_object(*this); - ar & mdelta1; - ar & mdelta2; - ar & mqbroad; - ar & mqbroad_seperable; - } - +class DLL_EXPORT JeongPeakWidth : public DebyeWallerPeakWidth { + public: + // constructors + JeongPeakWidth(); + virtual PeakWidthModelPtr create() const; + virtual PeakWidthModelPtr clone() const; + + // methods + virtual const std::string& type() const; + virtual double calculate(const BaseBondGenerator&) const; + virtual double maxWidth(StructureAdapterPtr, double rmin, double rmax) const; + + // data access + const double& getDelta1() const; + void setDelta1(double); + const double& getDelta2() const; + void setDelta2(double); + const double& getQbroad() const; + void setQbroad(double); + const double& getQbroad_seperable() const; + void setQbroad_seperable(double); + + private: + // data + double mdelta1; + double mdelta2; + double mqbroad; + double mqbroad_seperable; + + // methods + double msdSharpeningRatio(const double& r) const; + + // serialization + friend class boost::serialization::access; + + template + void serialize(Archive& ar, const unsigned int version) { + using boost::serialization::base_object; + ar& base_object(*this); + ar & mdelta1; + ar & mdelta2; + ar & mqbroad; + ar & mqbroad_seperable; + } }; - -} // namespace srreal -} // namespace diffpy +} // namespace srreal +} // namespace diffpy // Serialization ------------------------------------------------------------- diff --git a/src/diffpy/srreal/Lattice.cpp b/src/diffpy/srreal/Lattice.cpp index 6fd171d4..e8dd6769 100644 --- a/src/diffpy/srreal/Lattice.cpp +++ b/src/diffpy/srreal/Lattice.cpp @@ -1,21 +1,21 @@ /***************************************************************************** -* -* 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.txt for a list of people who contributed. -* See LICENSE_DANSE.txt for license information. -* -****************************************************************************** -* -* class Lattice -- vector and matrix conversions between general and -* Cartesian coordinate systems -* -*****************************************************************************/ + * + * 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.txt for a list of people who contributed. + * See LICENSE_DANSE.txt for license information. + * + ****************************************************************************** + * + * class Lattice -- vector and matrix conversions between general and + * Cartesian coordinate systems + * + *****************************************************************************/ #include #include @@ -31,282 +31,247 @@ using namespace diffpy::srreal; // Constructors -------------------------------------------------------------- -Lattice::Lattice() -{ - mbaserot = R3::identity(); - this->setLatPar(1.0, 1.0, 1.0, 90.0, 90.0, 90.0); +Lattice::Lattice() { + mbaserot = R3::identity(); + this->setLatPar(1.0, 1.0, 1.0, 90.0, 90.0, 90.0); } - -Lattice::Lattice(double a0, double b0, double c0, - double alpha0, double beta0, double gamma0) -{ - mbaserot = R3::identity(); - this->setLatPar(a0, b0, c0, alpha0, beta0, gamma0); +Lattice::Lattice(double a0, double b0, double c0, double alpha0, double beta0, + double gamma0) { + mbaserot = R3::identity(); + this->setLatPar(a0, b0, c0, alpha0, beta0, gamma0); } // Public Methods ------------------------------------------------------------ -void Lattice::setLatPar(double a0, double b0, double c0, - double alpha0, double beta0, double gamma0) -{ - using diffpy::mathutils::cosd; - using diffpy::mathutils::sind; - using diffpy::mathutils::acosd; - ma = a0; - mb = b0; - mc = c0; - malpha = alpha0; - mbeta = beta0; - mgamma = gamma0; - mcosa = cosd(malpha); - msina = sind(malpha); - mcosb = cosd(mbeta); - msinb = sind(mbeta); - mcosg = cosd(mgamma); - msing = sind(mgamma); - // Vnorm is a volume of unit cell with a=b=c=1 - double Vnorm = this->volumeNormal(); - // reciprocal lattice - mar = msina / (ma * Vnorm); - mbr = msinb / (mb * Vnorm); - mcr = msing / (mc * Vnorm); - mcosar = (mcosb*mcosg - mcosa) / (msinb * msing); - mcosbr = (mcosa*mcosg - mcosb) / (msina * msing); - mcosgr = (mcosa*mcosb - mcosg) / (msina * msinb); - msinar = sqrt(1.0 - mcosar*mcosar); - msinbr = sqrt(1.0 - mcosbr*mcosbr); - msingr = sqrt(1.0 - mcosgr*mcosgr); - malphar = acosd(mcosar); - mbetar = acosd(mcosbr); - mgammar = acosd(mcosgr); - // metric tensor - this->updateMetrics(); - // standard cartesian coordinates of lattice vectors - this->updateStandardBase(); - // calculate unit cell rotation matrix baserot, base = stdbase * baserot - mbase = R3::prod(mstdbase, mbaserot); - mva = R3::row(mbase, 0); - mvb = R3::row(mbase, 1); - mvc = R3::row(mbase, 2); - mrecbase = R3::inverse(mbase); - mvar = R3::column(mrecbase, 0); - mvbr = R3::column(mrecbase, 1); - mvcr = R3::column(mrecbase, 2); - R3::row(mnormbase, 0) = R3::row(mbase, 0) * mar; - R3::row(mnormbase, 1) = R3::row(mbase, 1) * mbr; - R3::row(mnormbase, 2) = R3::row(mbase, 2) * mcr; - R3::column(mrecnormbase, 0) = R3::column(mrecbase, 0) / mar; - R3::column(mrecnormbase, 1) = R3::column(mrecbase, 1) / mbr; - R3::column(mrecnormbase, 2) = R3::column(mrecbase, 2) / mcr; +void Lattice::setLatPar(double a0, double b0, double c0, double alpha0, + double beta0, double gamma0) { + using diffpy::mathutils::acosd; + using diffpy::mathutils::cosd; + using diffpy::mathutils::sind; + ma = a0; + mb = b0; + mc = c0; + malpha = alpha0; + mbeta = beta0; + mgamma = gamma0; + mcosa = cosd(malpha); + msina = sind(malpha); + mcosb = cosd(mbeta); + msinb = sind(mbeta); + mcosg = cosd(mgamma); + msing = sind(mgamma); + // Vnorm is a volume of unit cell with a=b=c=1 + double Vnorm = this->volumeNormal(); + // reciprocal lattice + mar = msina / (ma * Vnorm); + mbr = msinb / (mb * Vnorm); + mcr = msing / (mc * Vnorm); + mcosar = (mcosb * mcosg - mcosa) / (msinb * msing); + mcosbr = (mcosa * mcosg - mcosb) / (msina * msing); + mcosgr = (mcosa * mcosb - mcosg) / (msina * msinb); + msinar = sqrt(1.0 - mcosar * mcosar); + msinbr = sqrt(1.0 - mcosbr * mcosbr); + msingr = sqrt(1.0 - mcosgr * mcosgr); + malphar = acosd(mcosar); + mbetar = acosd(mcosbr); + mgammar = acosd(mcosgr); + // metric tensor + this->updateMetrics(); + // standard cartesian coordinates of lattice vectors + this->updateStandardBase(); + // calculate unit cell rotation matrix baserot, base = stdbase * baserot + mbase = R3::prod(mstdbase, mbaserot); + mva = R3::row(mbase, 0); + mvb = R3::row(mbase, 1); + mvc = R3::row(mbase, 2); + mrecbase = R3::inverse(mbase); + mvar = R3::column(mrecbase, 0); + mvbr = R3::column(mrecbase, 1); + mvcr = R3::column(mrecbase, 2); + R3::row(mnormbase, 0) = R3::row(mbase, 0) * mar; + R3::row(mnormbase, 1) = R3::row(mbase, 1) * mbr; + R3::row(mnormbase, 2) = R3::row(mbase, 2) * mcr; + R3::column(mrecnormbase, 0) = R3::column(mrecbase, 0) / mar; + R3::column(mrecnormbase, 1) = R3::column(mrecbase, 1) / mbr; + R3::column(mrecnormbase, 2) = R3::column(mrecbase, 2) / mcr; } -void Lattice::setLatBase(const R3::Vector& va0, - const R3::Vector& vb0, - const R3::Vector& vc0) -{ - using diffpy::mathutils::acosd; - R3::row(mbase, 0) = va0; - R3::row(mbase, 1) = vb0; - R3::row(mbase, 2) = vc0; - double detbase = R3::determinant(mbase); - if (fabs(detbase) < 1.0e-8) - { - throw invalid_argument("base vectors are degenerate"); - } - else if (detbase < 0.0) - { - throw invalid_argument("base is not right-handed"); - } - mva = va0; - mvb = vb0; - mvc = vc0; - ma = R3::norm(mva); - mb = R3::norm(mvb); - mc = R3::norm(mvc); - mcosa = R3::dot(mvb, mvc) / (mb*mc); - mcosb = R3::dot(mva, mvc) / (ma*mc); - mcosg = R3::dot(mva, mvb) / (ma*mb); - msina = sqrt(1.0 - mcosa*mcosa); - msinb = sqrt(1.0 - mcosb*mcosb); - msing = sqrt(1.0 - mcosg*mcosg); - malpha = acosd(mcosa); - mbeta = acosd(mcosb); - mgamma = acosd(mcosg); - // Vnorm is a volume of unit cell with a=b=c=1 - double Vnorm = this->volumeNormal(); - // reciprocal lattice - mar = msina/(ma*Vnorm); - mbr = msinb/(mb*Vnorm); - mcr = msing/(mc*Vnorm); - mcosar = (mcosb*mcosg - mcosa)/(msinb*msing); - mcosbr = (mcosa*mcosg - mcosb)/(msina*msing); - mcosgr = (mcosa*mcosb - mcosg)/(msina*msinb); - msinar = sqrt(1.0 - mcosar*mcosar); - msinbr = sqrt(1.0 - mcosbr*mcosbr); - msingr = sqrt(1.0 - mcosgr*mcosgr); - malphar = acosd(mcosar); - mbetar = acosd(mcosbr); - mgammar = acosd(mcosgr); - // standard orientation of lattice vectors - this->updateStandardBase(); - // calculate unit cell rotation matrix, base = stdbase*baserot - mbaserot = R3::prod(R3::inverse(mstdbase), mbase); - mrecbase = R3::inverse(mbase); - mvar = R3::column(mrecbase, 0); - mvbr = R3::column(mrecbase, 1); - mvcr = R3::column(mrecbase, 2); - // bases normalized to unit reciprocal vectors - R3::row(mnormbase, 0) = R3::row(mbase, 0) * mar; - R3::row(mnormbase, 1) = R3::row(mbase, 1) * mbr; - R3::row(mnormbase, 2) = R3::row(mbase, 2) * mcr; - R3::column(mrecnormbase, 0) = R3::column(mrecbase, 0) / mar; - R3::column(mrecnormbase, 1) = R3::column(mrecbase, 1) / mbr; - R3::column(mrecnormbase, 2) = R3::column(mrecbase, 2) / mcr; - this->updateMetrics(); +void Lattice::setLatBase(const R3::Vector& va0, const R3::Vector& vb0, + const R3::Vector& vc0) { + using diffpy::mathutils::acosd; + R3::row(mbase, 0) = va0; + R3::row(mbase, 1) = vb0; + R3::row(mbase, 2) = vc0; + double detbase = R3::determinant(mbase); + if (fabs(detbase) < 1.0e-8) { + throw invalid_argument("base vectors are degenerate"); + } else if (detbase < 0.0) { + throw invalid_argument("base is not right-handed"); + } + mva = va0; + mvb = vb0; + mvc = vc0; + ma = R3::norm(mva); + mb = R3::norm(mvb); + mc = R3::norm(mvc); + mcosa = R3::dot(mvb, mvc) / (mb * mc); + mcosb = R3::dot(mva, mvc) / (ma * mc); + mcosg = R3::dot(mva, mvb) / (ma * mb); + msina = sqrt(1.0 - mcosa * mcosa); + msinb = sqrt(1.0 - mcosb * mcosb); + msing = sqrt(1.0 - mcosg * mcosg); + malpha = acosd(mcosa); + mbeta = acosd(mcosb); + mgamma = acosd(mcosg); + // Vnorm is a volume of unit cell with a=b=c=1 + double Vnorm = this->volumeNormal(); + // reciprocal lattice + mar = msina / (ma * Vnorm); + mbr = msinb / (mb * Vnorm); + mcr = msing / (mc * Vnorm); + mcosar = (mcosb * mcosg - mcosa) / (msinb * msing); + mcosbr = (mcosa * mcosg - mcosb) / (msina * msing); + mcosgr = (mcosa * mcosb - mcosg) / (msina * msinb); + msinar = sqrt(1.0 - mcosar * mcosar); + msinbr = sqrt(1.0 - mcosbr * mcosbr); + msingr = sqrt(1.0 - mcosgr * mcosgr); + malphar = acosd(mcosar); + mbetar = acosd(mcosbr); + mgammar = acosd(mcosgr); + // standard orientation of lattice vectors + this->updateStandardBase(); + // calculate unit cell rotation matrix, base = stdbase*baserot + mbaserot = R3::prod(R3::inverse(mstdbase), mbase); + mrecbase = R3::inverse(mbase); + mvar = R3::column(mrecbase, 0); + mvbr = R3::column(mrecbase, 1); + mvcr = R3::column(mrecbase, 2); + // bases normalized to unit reciprocal vectors + R3::row(mnormbase, 0) = R3::row(mbase, 0) * mar; + R3::row(mnormbase, 1) = R3::row(mbase, 1) * mbr; + R3::row(mnormbase, 2) = R3::row(mbase, 2) * mcr; + R3::column(mrecnormbase, 0) = R3::column(mrecbase, 0) / mar; + R3::column(mrecnormbase, 1) = R3::column(mrecbase, 1) / mbr; + R3::column(mrecnormbase, 2) = R3::column(mrecbase, 2) / mcr; + this->updateMetrics(); } - // volumeNormal is a volume of unit cell with a=b=c=1 -double Lattice::volumeNormal() const -{ - double rv = sqrt(1.0 + - 2.0 * mcosa * mcosb * mcosg - - mcosa * mcosa - mcosb * mcosb - mcosg * mcosg); - return rv; +double Lattice::volumeNormal() const { + double rv = sqrt(1.0 + 2.0 * mcosa * mcosb * mcosg - mcosa * mcosa - + mcosb * mcosb - mcosg * mcosg); + return rv; } - -double Lattice::volume() const -{ - double rv = this->volumeNormal() * this->a() * this->b() * this->c(); - return rv; +double Lattice::volume() const { + double rv = this->volumeNormal() * this->a() * this->b() * this->c(); + return rv; } - -const R3::Vector& Lattice::cartesian(const R3::Vector& lv) const -{ - static R3::Vector res; - res = R3::mxvecproduct(lv, mbase); - return res; +const R3::Vector& Lattice::cartesian(const R3::Vector& lv) const { + static R3::Vector res; + res = R3::mxvecproduct(lv, mbase); + return res; } -const R3::Vector& Lattice::fractional(const R3::Vector& cv) const -{ - static R3::Vector res; - res = R3::mxvecproduct(cv, mrecbase); - return res; +const R3::Vector& Lattice::fractional(const R3::Vector& cv) const { + static R3::Vector res; + res = R3::mxvecproduct(cv, mrecbase); + return res; } -const R3::Vector& Lattice::ucvCartesian(const R3::Vector& cv) const -{ - static R3::Vector res; - res = cartesian(ucvFractional(fractional(cv))); - return res; +const R3::Vector& Lattice::ucvCartesian(const R3::Vector& cv) const { + static R3::Vector res; + res = cartesian(ucvFractional(fractional(cv))); + return res; } -const R3::Vector& Lattice::ucvFractional(const R3::Vector& lv) const -{ - using mathutils::eps_eq; - static R3::Vector res; - res = lv - floor(lv); - if (eps_eq(res[0], 1.0)) res[0] = 0.0; - if (eps_eq(res[1], 1.0)) res[1] = 0.0; - if (eps_eq(res[2], 1.0)) res[2] = 0.0; - return res; +const R3::Vector& Lattice::ucvFractional(const R3::Vector& lv) const { + using mathutils::eps_eq; + static R3::Vector res; + res = lv - floor(lv); + if (eps_eq(res[0], 1.0)) res[0] = 0.0; + if (eps_eq(res[1], 1.0)) res[1] = 0.0; + if (eps_eq(res[2], 1.0)) res[2] = 0.0; + return res; } -const R3::Matrix& Lattice::cartesianMatrix(const R3::Matrix& Ml) const -{ - static R3::Matrix res0, res1; - res0 = prod(Ml, mnormbase); - res1 = prod(R3::trans(mnormbase), res0); - return res1; +const R3::Matrix& Lattice::cartesianMatrix(const R3::Matrix& Ml) const { + static R3::Matrix res0, res1; + res0 = prod(Ml, mnormbase); + res1 = prod(R3::trans(mnormbase), res0); + return res1; } -const R3::Matrix& Lattice::fractionalMatrix(const R3::Matrix& Mc) const -{ - static R3::Matrix res0, res1; - res0 = prod(Mc, mrecnormbase); - res1 = prod(R3::trans(mrecnormbase), res0); - return res1; +const R3::Matrix& Lattice::fractionalMatrix(const R3::Matrix& Mc) const { + static R3::Matrix res0, res1; + res0 = prod(Mc, mrecnormbase); + res1 = prod(R3::trans(mrecnormbase), res0); + return res1; } - -const R3::Vector& Lattice::ucMaxDiagonal() const -{ - static list ucdiagonals; - if (ucdiagonals.empty()) - { - ucdiagonals.push_back(R3::Vector(+1, +1, +1)); - ucdiagonals.push_back(R3::Vector(-1, +1, +1)); - ucdiagonals.push_back(R3::Vector(+1, -1, +1)); - ucdiagonals.push_back(R3::Vector(+1, +1, -1)); - } - double maxnorm = -1; - list::iterator ucd; - list::iterator maxucd = ucdiagonals.end(); - for (ucd = ucdiagonals.begin(); ucd != ucdiagonals.end(); ++ucd) - { - double normucd = this->norm(*ucd); - if (normucd > maxnorm) - { - maxnorm = normucd; - maxucd = ucd; - } +const R3::Vector& Lattice::ucMaxDiagonal() const { + static list ucdiagonals; + if (ucdiagonals.empty()) { + ucdiagonals.push_back(R3::Vector(+1, +1, +1)); + ucdiagonals.push_back(R3::Vector(-1, +1, +1)); + ucdiagonals.push_back(R3::Vector(+1, -1, +1)); + ucdiagonals.push_back(R3::Vector(+1, +1, -1)); + } + double maxnorm = -1; + list::iterator ucd; + list::iterator maxucd = ucdiagonals.end(); + for (ucd = ucdiagonals.begin(); ucd != ucdiagonals.end(); ++ucd) { + double normucd = this->norm(*ucd); + if (normucd > maxnorm) { + maxnorm = normucd; + maxucd = ucd; } - assert(maxucd != ucdiagonals.end()); - return *maxucd; + } + assert(maxucd != ucdiagonals.end()); + return *maxucd; } - -double Lattice::ucMaxDiagonalLength() const -{ - const R3::Vector& ucmd = this->ucMaxDiagonal(); - double res = this->norm(ucmd); - return res; +double Lattice::ucMaxDiagonalLength() const { + const R3::Vector& ucmd = this->ucMaxDiagonal(); + double res = this->norm(ucmd); + return res; } - -bool Lattice::operator==(const Lattice& other) const -{ - bool rv = (this->base() == other.base()); - return rv; +bool Lattice::operator==(const Lattice& other) const { + bool rv = (this->base() == other.base()); + return rv; } - -bool Lattice::operator!=(const Lattice& other) const -{ - return !(*this == other); +bool Lattice::operator!=(const Lattice& other) const { + return !(*this == other); } // Private Methods ----------------------------------------------------------- -void Lattice::updateMetrics() -{ - mmetrics(0,0) = ma * ma; - mmetrics(1,1) = mb * mb; - mmetrics(2,2) = mc * mc; - mmetrics(0,1) = mmetrics(1,0) = ma * mb * mcosg; - mmetrics(0,2) = mmetrics(2,0) = ma * mc * mcosb; - mmetrics(1,2) = mmetrics(2,1) = mb * mc * mcosa; +void Lattice::updateMetrics() { + mmetrics(0, 0) = ma * ma; + mmetrics(1, 1) = mb * mb; + mmetrics(2, 2) = mc * mc; + mmetrics(0, 1) = mmetrics(1, 0) = ma * mb * mcosg; + mmetrics(0, 2) = mmetrics(2, 0) = ma * mc * mcosb; + mmetrics(1, 2) = mmetrics(2, 1) = mb * mc * mcosa; } -void Lattice::updateStandardBase() -{ - // a - mstdbase(0,0) = 1.0 / mar; - mstdbase(1,0) = 0.0; - mstdbase(2,0) = 0.0; - // b - mstdbase(0,1) = -mcosgr / msingr / mar; - mstdbase(1,1) = mb * msina; - mstdbase(2,1) = 0.0; - // c - mstdbase(0,2) = mcosb * ma; - mstdbase(1,2) = mb * mcosa; - mstdbase(2,2) = mc; +void Lattice::updateStandardBase() { + // a + mstdbase(0, 0) = 1.0 / mar; + mstdbase(1, 0) = 0.0; + mstdbase(2, 0) = 0.0; + // b + mstdbase(0, 1) = -mcosgr / msingr / mar; + mstdbase(1, 1) = mb * msina; + mstdbase(2, 1) = 0.0; + // c + mstdbase(0, 2) = mcosb * ma; + mstdbase(1, 2) = mb * mcosa; + mstdbase(2, 2) = mc; } - // End of file diff --git a/src/diffpy/srreal/Lattice.hpp b/src/diffpy/srreal/Lattice.hpp index 4e9146c5..1c8c15f3 100644 --- a/src/diffpy/srreal/Lattice.hpp +++ b/src/diffpy/srreal/Lattice.hpp @@ -1,21 +1,21 @@ /***************************************************************************** -* -* 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.txt for a list of people who contributed. -* See LICENSE_DANSE.txt for license information. -* -****************************************************************************** -* -* class Lattice -- vector and matrix conversions between general and -* Cartesian coordinate systems -* -*****************************************************************************/ + * + * 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.txt for a list of people who contributed. + * See LICENSE_DANSE.txt for license information. + * + ****************************************************************************** + * + * class Lattice -- vector and matrix conversions between general and + * Cartesian coordinate systems + * + *****************************************************************************/ #ifndef LATTICE_HPP_INCLUDED #define LATTICE_HPP_INCLUDED @@ -27,171 +27,162 @@ namespace diffpy { namespace srreal { -class Lattice -{ - public: - - // constructors - - Lattice(); - Lattice(double a, double b, double c, - double alpha, double beta, double gamma); - // create from base vectors - template - Lattice(const V& va, const V& vb, const V& vc); - - // methods - - // set lattice parameters - void setLatPar( - double a, double b, double c, - double alpha, double beta, double gamma ); - // set lattice base vectors - void setLatBase( - const R3::Vector& va, - const R3::Vector& vb, - const R3::Vector& vc); - template - void setLatBase(const V& va, const V& vb, const V& vc); - // direct lattice - double a() const; - double b() const; - double c() const; - double alpha() const; - double beta() const; - double gamma() const; - double cosalpha() const; - double cosbeta() const; - double cosgamma() const; - double sinalpha() const; - double sinbeta() const; - double singamma() const; - const R3::Vector& va() const; - const R3::Vector& vb() const; - const R3::Vector& vc() const; - double volumeNormal() const; - double volume() const; - // reciprocal lattice - double ar() const; - double br() const; - double cr() const; - double alphar() const; - double betar() const; - double gammar() const; - double cosalphar() const; - double cosbetar() const; - double cosgammar() const; - double sinalphar() const; - double sinbetar() const; - double singammar() const; - const R3::Vector& var() const; - const R3::Vector& vbr() const; - const R3::Vector& vcr() const; - // lattice related tensors - // metrics tensor - const R3::Matrix& metrics() const; - // matrix of base vectors, base() = stdbase() * baserot() - const R3::Matrix& base() const; - // standard base vectors - const R3::Matrix& stdbase() const; - // base rotation matrix - const R3::Matrix& baserot() const; - // inverse of base matrix - const R3::Matrix& recbase() const; - // vector operations using lattice coordinates - template - double dot(const V& u, const V& v) const; - template - double norm(const V& u) const; - template - double distance(const V& u, const V& v) const; - // angle in degrees - template - double angledeg(const V& u, const V& v) const; - // angle in radians - template - double anglerad(const V& u, const V& v) const; - // conversion of coordinates and tensors - const R3::Vector& cartesian(const R3::Vector& lv) const; - template - const R3::Vector& cartesian(const V& lv) const; - const R3::Vector& fractional(const R3::Vector& cv) const; - template - const R3::Vector& fractional(const V& cv) const; - const R3::Vector& ucvCartesian(const R3::Vector& cv) const; - template - const R3::Vector& ucvCartesian(const V& cv) const; - const R3::Vector& ucvFractional(const R3::Vector& lv) const; - template - const R3::Vector& ucvFractional(const V& lv) const; - const R3::Matrix& cartesianMatrix(const R3::Matrix& Ml) const; - const R3::Matrix& fractionalMatrix(const R3::Matrix& Mc) const; - // largest cell diagonal in fractional coordinates - const R3::Vector& ucMaxDiagonal() const; - double ucMaxDiagonalLength() const; - - // comparison - bool operator==(const Lattice&) const; - bool operator!=(const Lattice&) const; - - private: - - // methods - void updateMetrics(); - void updateStandardBase(); - - // data - direct lattice parameters - double ma, mb, mc; - double malpha, mbeta, mgamma; - double mcosa, mcosb, mcosg; - double msina, msinb, msing; - R3::Vector mva, mvb, mvc; - - // data - reciprocal lattice parameters - double mar, mbr, mcr; - double malphar, mbetar, mgammar; - double mcosar, mcosbr, mcosgr; - double msinar, msinbr, msingr; - R3::Vector mvar, mvbr, mvcr; - - // data - tensors - // mbase = mstdbase * mbaserot - R3::Matrix mmetrics; // metrics tensor - R3::Matrix mbase; // lattice base - R3::Matrix mstdbase; // standard unit cell base - R3::Matrix mbaserot; // base rotation matrix - R3::Matrix mrecbase; // inverse of base matrix - // base multiplied by magnitudes of reciprocal vectors - R3::Matrix mnormbase; - R3::Matrix mrecnormbase; // inverse of mnormbase - - // serialization - friend class boost::serialization::access; - template - void serialize(Archive& ar, const unsigned int version) - { - ar & ma & mb & mc; - ar & malpha & mbeta & mgamma; - ar & mcosa & mcosb & mcosg; - ar & msina & msinb & msing; - ar & mva & mvb & mvc; - ar & mar & mbr & mcr; - ar & malphar & mbetar & mgammar; - ar & mcosar & mcosbr & mcosgr; - ar & msinar & msinbr & msingr; - ar & mvar & mvbr & mvcr; - ar & mmetrics; - ar & mbase; - ar & mstdbase; - ar & mbaserot; - ar & mrecbase; - ar & mnormbase; - ar & mrecnormbase; - } - +class Lattice { + public: + // constructors + + Lattice(); + Lattice(double a, double b, double c, double alpha, double beta, + double gamma); + // create from base vectors + template + Lattice(const V& va, const V& vb, const V& vc); + + // methods + + // set lattice parameters + void setLatPar(double a, double b, double c, double alpha, double beta, + double gamma); + // set lattice base vectors + void setLatBase(const R3::Vector& va, const R3::Vector& vb, + const R3::Vector& vc); + template + void setLatBase(const V& va, const V& vb, const V& vc); + // direct lattice + double a() const; + double b() const; + double c() const; + double alpha() const; + double beta() const; + double gamma() const; + double cosalpha() const; + double cosbeta() const; + double cosgamma() const; + double sinalpha() const; + double sinbeta() const; + double singamma() const; + const R3::Vector& va() const; + const R3::Vector& vb() const; + const R3::Vector& vc() const; + double volumeNormal() const; + double volume() const; + // reciprocal lattice + double ar() const; + double br() const; + double cr() const; + double alphar() const; + double betar() const; + double gammar() const; + double cosalphar() const; + double cosbetar() const; + double cosgammar() const; + double sinalphar() const; + double sinbetar() const; + double singammar() const; + const R3::Vector& var() const; + const R3::Vector& vbr() const; + const R3::Vector& vcr() const; + // lattice related tensors + // metrics tensor + const R3::Matrix& metrics() const; + // matrix of base vectors, base() = stdbase() * baserot() + const R3::Matrix& base() const; + // standard base vectors + const R3::Matrix& stdbase() const; + // base rotation matrix + const R3::Matrix& baserot() const; + // inverse of base matrix + const R3::Matrix& recbase() const; + // vector operations using lattice coordinates + template + double dot(const V& u, const V& v) const; + template + double norm(const V& u) const; + template + double distance(const V& u, const V& v) const; + // angle in degrees + template + double angledeg(const V& u, const V& v) const; + // angle in radians + template + double anglerad(const V& u, const V& v) const; + // conversion of coordinates and tensors + const R3::Vector& cartesian(const R3::Vector& lv) const; + template + const R3::Vector& cartesian(const V& lv) const; + const R3::Vector& fractional(const R3::Vector& cv) const; + template + const R3::Vector& fractional(const V& cv) const; + const R3::Vector& ucvCartesian(const R3::Vector& cv) const; + template + const R3::Vector& ucvCartesian(const V& cv) const; + const R3::Vector& ucvFractional(const R3::Vector& lv) const; + template + const R3::Vector& ucvFractional(const V& lv) const; + const R3::Matrix& cartesianMatrix(const R3::Matrix& Ml) const; + const R3::Matrix& fractionalMatrix(const R3::Matrix& Mc) const; + // largest cell diagonal in fractional coordinates + const R3::Vector& ucMaxDiagonal() const; + double ucMaxDiagonalLength() const; + + // comparison + bool operator==(const Lattice&) const; + bool operator!=(const Lattice&) const; + + private: + // methods + void updateMetrics(); + void updateStandardBase(); + + // data - direct lattice parameters + double ma, mb, mc; + double malpha, mbeta, mgamma; + double mcosa, mcosb, mcosg; + double msina, msinb, msing; + R3::Vector mva, mvb, mvc; + + // data - reciprocal lattice parameters + double mar, mbr, mcr; + double malphar, mbetar, mgammar; + double mcosar, mcosbr, mcosgr; + double msinar, msinbr, msingr; + R3::Vector mvar, mvbr, mvcr; + + // data - tensors + // mbase = mstdbase * mbaserot + R3::Matrix mmetrics; // metrics tensor + R3::Matrix mbase; // lattice base + R3::Matrix mstdbase; // standard unit cell base + R3::Matrix mbaserot; // base rotation matrix + R3::Matrix mrecbase; // inverse of base matrix + // base multiplied by magnitudes of reciprocal vectors + R3::Matrix mnormbase; + R3::Matrix mrecnormbase; // inverse of mnormbase + + // serialization + friend class boost::serialization::access; + template + void serialize(Archive& ar, const unsigned int version) { + ar & ma & mb & mc; + ar & malpha & mbeta & mgamma; + ar & mcosa & mcosb & mcosg; + ar & msina & msinb & msing; + ar & mva & mvb & mvc; + ar & mar & mbr & mcr; + ar & malphar & mbetar & mgammar; + ar & mcosar & mcosbr & mcosgr; + ar & msinar & msinbr & msingr; + ar & mvar & mvbr & mvcr; + ar & mmetrics; + ar & mbase; + ar & mstdbase; + ar & mbaserot; + ar & mrecbase; + ar & mnormbase; + ar & mrecnormbase; + } }; - ////////////////////////////////////////////////////////////////////////////// // Definitions ////////////////////////////////////////////////////////////////////////////// @@ -199,366 +190,170 @@ class Lattice // Template Constructor ------------------------------------------------------ template -Lattice::Lattice(const V& va0, const V& vb0, const V& vc0) -{ - this->setLatBase(va0, vb0, vc0); +Lattice::Lattice(const V& va0, const V& vb0, const V& vc0) { + this->setLatBase(va0, vb0, vc0); } // Template and Inline Methods ----------------------------------------------- template -void Lattice::setLatBase(const V& va0, const V& vb0, const V& vc0) -{ - R3::Vector va1(va0[0], va0[1], va0[2]); - R3::Vector vb1(vb0[0], vb0[1], vb0[2]); - R3::Vector vc1(vc0[0], vc0[1], vc0[2]); - this->setLatBase(va1, vb1, vc1); +void Lattice::setLatBase(const V& va0, const V& vb0, const V& vc0) { + R3::Vector va1(va0[0], va0[1], va0[2]); + R3::Vector vb1(vb0[0], vb0[1], vb0[2]); + R3::Vector vc1(vc0[0], vc0[1], vc0[2]); + this->setLatBase(va1, vb1, vc1); } // direct lattice -inline -double Lattice::a() const -{ - return ma; -} - - -inline -double Lattice::b() const -{ - return mb; -} - - -inline -double Lattice::c() const -{ - return mc; -} - - -inline -double Lattice::alpha() const -{ - return malpha; -} - - -inline -double Lattice::beta() const -{ - return mbeta; -} - - -inline -double Lattice::gamma() const -{ - return mgamma; -} - - -inline -double Lattice::cosalpha() const -{ - return mcosa; -} - +inline double Lattice::a() const { return ma; } -inline -double Lattice::cosbeta() const -{ - return mcosb; -} +inline double Lattice::b() const { return mb; } +inline double Lattice::c() const { return mc; } -inline -double Lattice::cosgamma() const -{ - return mcosg; -} - +inline double Lattice::alpha() const { return malpha; } -inline -double Lattice::sinalpha() const -{ - return msina; -} +inline double Lattice::beta() const { return mbeta; } +inline double Lattice::gamma() const { return mgamma; } -inline -double Lattice::sinbeta() const -{ - return msinb; -} +inline double Lattice::cosalpha() const { return mcosa; } +inline double Lattice::cosbeta() const { return mcosb; } -inline -double Lattice::singamma() const -{ - return msing; -} +inline double Lattice::cosgamma() const { return mcosg; } +inline double Lattice::sinalpha() const { return msina; } -inline -const R3::Vector& Lattice::va() const -{ - return mva; -} +inline double Lattice::sinbeta() const { return msinb; } +inline double Lattice::singamma() const { return msing; } -inline -const R3::Vector& Lattice::vb() const -{ - return mvb; -} +inline const R3::Vector& Lattice::va() const { return mva; } +inline const R3::Vector& Lattice::vb() const { return mvb; } -inline -const R3::Vector& Lattice::vc() const -{ - return mvc; -} +inline const R3::Vector& Lattice::vc() const { return mvc; } // reciprocal lattice -inline -double Lattice::ar() const -{ - return mar; -} +inline double Lattice::ar() const { return mar; } +inline double Lattice::br() const { return mbr; } -inline -double Lattice::br() const -{ - return mbr; -} +inline double Lattice::cr() const { return mcr; } +inline double Lattice::alphar() const { return malphar; } -inline -double Lattice::cr() const -{ - return mcr; -} - +inline double Lattice::betar() const { return mbetar; } -inline -double Lattice::alphar() const -{ - return malphar; -} +inline double Lattice::gammar() const { return mgammar; } +inline double Lattice::cosalphar() const { return mcosar; } -inline -double Lattice::betar() const -{ - return mbetar; -} +inline double Lattice::cosbetar() const { return mcosbr; } +inline double Lattice::cosgammar() const { return mcosgr; } -inline -double Lattice::gammar() const -{ - return mgammar; -} +inline double Lattice::sinalphar() const { return msinar; } +inline double Lattice::sinbetar() const { return msinbr; } -inline -double Lattice::cosalphar() const -{ - return mcosar; -} +inline double Lattice::singammar() const { return msingr; } +inline const R3::Vector& Lattice::var() const { return mvar; } -inline -double Lattice::cosbetar() const -{ - return mcosbr; -} +inline const R3::Vector& Lattice::vbr() const { return mvbr; } - -inline -double Lattice::cosgammar() const -{ - return mcosgr; -} - - -inline -double Lattice::sinalphar() const -{ - return msinar; -} - - -inline -double Lattice::sinbetar() const -{ - return msinbr; -} - - -inline -double Lattice::singammar() const -{ - return msingr; -} - - -inline -const R3::Vector& Lattice::var() const -{ - return mvar; -} - - -inline -const R3::Vector& Lattice::vbr() const -{ - return mvbr; -} - - -inline -const R3::Vector& Lattice::vcr() const -{ - return mvcr; -} +inline const R3::Vector& Lattice::vcr() const { return mvcr; } // lattice related tensors -inline -const R3::Matrix& Lattice::metrics() const -{ - return mmetrics; -} - - -inline -const R3::Matrix& Lattice::base() const -{ - return mbase; -} - - -inline -const R3::Matrix& Lattice::stdbase() const -{ - return mstdbase; -} +inline const R3::Matrix& Lattice::metrics() const { return mmetrics; } +inline const R3::Matrix& Lattice::base() const { return mbase; } -inline -const R3::Matrix& Lattice::baserot() const -{ - return mbaserot; -} +inline const R3::Matrix& Lattice::stdbase() const { return mstdbase; } +inline const R3::Matrix& Lattice::baserot() const { return mbaserot; } -inline -const R3::Matrix& Lattice::recbase() const -{ - return mrecbase; -} - +inline const R3::Matrix& Lattice::recbase() const { return mrecbase; } template -double Lattice::dot(const V& u, const V& v) const -{ - const R3::Matrix& M = this->metrics(); - double dp = - u[0] * v[0] * M(0,0) + - u[1] * v[1] * M(1,1) + - u[2] * v[2] * M(2,2) + - (u[0] * v[1] + u[1] * v[0]) * M(0,1) + - (u[0] * v[2] + u[2] * v[0]) * M(0,2) + - (u[2] * v[1] + u[1] * v[2]) * M(1,2); - return dp; +double Lattice::dot(const V& u, const V& v) const { + const R3::Matrix& M = this->metrics(); + double dp = u[0] * v[0] * M(0, 0) + u[1] * v[1] * M(1, 1) + + u[2] * v[2] * M(2, 2) + (u[0] * v[1] + u[1] * v[0]) * M(0, 1) + + (u[0] * v[2] + u[2] * v[0]) * M(0, 2) + + (u[2] * v[1] + u[1] * v[2]) * M(1, 2); + return dp; } - template -double Lattice::norm(const V& u) const -{ - return sqrt(this->dot(u, u)); +double Lattice::norm(const V& u) const { + return sqrt(this->dot(u, u)); } - template -double Lattice::distance(const V& u, const V& v) const -{ - static R3::Vector duv; - duv[0] = u[0] - v[0]; - duv[1] = u[1] - v[1]; - duv[2] = u[2] - v[2]; - return this->norm(duv); +double Lattice::distance(const V& u, const V& v) const { + static R3::Vector duv; + duv[0] = u[0] - v[0]; + duv[1] = u[1] - v[1]; + duv[2] = u[2] - v[2]; + return this->norm(duv); } - template -double Lattice::angledeg(const V& u, const V& v) const -{ - using diffpy::mathutils::acosd; - double ca = dot(u, v)/(norm(u) * norm(v)); - return acosd(ca); +double Lattice::angledeg(const V& u, const V& v) const { + using diffpy::mathutils::acosd; + double ca = dot(u, v) / (norm(u) * norm(v)); + return acosd(ca); } - template -double Lattice::anglerad(const V& u, const V& v) const -{ - double ca = dot(u, v)/(norm(u) * norm(v)); - return acos(ca); +double Lattice::anglerad(const V& u, const V& v) const { + double ca = dot(u, v) / (norm(u) * norm(v)); + return acos(ca); } - template -const R3::Vector& Lattice::cartesian(const V& lv) const -{ - static R3::Vector lvcopy; - lvcopy[0] = lv[0]; - lvcopy[1] = lv[1]; - lvcopy[2] = lv[2]; - return this->cartesian(lvcopy); +const R3::Vector& Lattice::cartesian(const V& lv) const { + static R3::Vector lvcopy; + lvcopy[0] = lv[0]; + lvcopy[1] = lv[1]; + lvcopy[2] = lv[2]; + return this->cartesian(lvcopy); } - template -const R3::Vector& Lattice::fractional(const V& cv) const -{ - static R3::Vector cvcopy; - cvcopy[0] = cv[0]; - cvcopy[1] = cv[1]; - cvcopy[2] = cv[2]; - return this->fractional(cvcopy); +const R3::Vector& Lattice::fractional(const V& cv) const { + static R3::Vector cvcopy; + cvcopy[0] = cv[0]; + cvcopy[1] = cv[1]; + cvcopy[2] = cv[2]; + return this->fractional(cvcopy); } - template -const R3::Vector& Lattice::ucvCartesian(const V& cv) const -{ - static R3::Vector cvcopy; - cvcopy[0] = cv[0]; - cvcopy[1] = cv[1]; - cvcopy[2] = cv[2]; - return this->ucvCartesian(cvcopy); +const R3::Vector& Lattice::ucvCartesian(const V& cv) const { + static R3::Vector cvcopy; + cvcopy[0] = cv[0]; + cvcopy[1] = cv[1]; + cvcopy[2] = cv[2]; + return this->ucvCartesian(cvcopy); } - template -const R3::Vector& Lattice::ucvFractional(const V& cv) const -{ - static R3::Vector cvcopy; - cvcopy[0] = cv[0]; - cvcopy[1] = cv[1]; - cvcopy[2] = cv[2]; - return ucvFractional(cvcopy); +const R3::Vector& Lattice::ucvFractional(const V& cv) const { + static R3::Vector cvcopy; + cvcopy[0] = cv[0]; + cvcopy[1] = cv[1]; + cvcopy[2] = cv[2]; + return ucvFractional(cvcopy); } - -} // namespace srreal -} // namespace diffpy +} // namespace srreal +} // namespace diffpy #endif // LATTICE_HPP_INCLUDED diff --git a/src/diffpy/srreal/LinearBaseline.cpp b/src/diffpy/srreal/LinearBaseline.cpp index 861d82a3..8033c164 100644 --- a/src/diffpy/srreal/LinearBaseline.cpp +++ b/src/diffpy/srreal/LinearBaseline.cpp @@ -1,20 +1,20 @@ /***************************************************************************** -* -* 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.txt for a list of people who contributed. -* See LICENSE_DANSE.txt for license information. -* -****************************************************************************** -* -* class LinearBaseline -- linear PDF baseline -* -*****************************************************************************/ + * + * 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.txt for a list of people who contributed. + * See LICENSE_DANSE.txt for license information. + * + ****************************************************************************** + * + * class LinearBaseline -- linear PDF baseline + * + *****************************************************************************/ #include #include @@ -26,61 +26,43 @@ namespace srreal { // Constructors -------------------------------------------------------------- -LinearBaseline::LinearBaseline() -{ - this->setSlope(0.0); - this->registerDoubleAttribute("slope", this, - &LinearBaseline::getSlope, - &LinearBaseline::setSlope); +LinearBaseline::LinearBaseline() { + this->setSlope(0.0); + this->registerDoubleAttribute("slope", this, &LinearBaseline::getSlope, + &LinearBaseline::setSlope); } - -PDFBaselinePtr LinearBaseline::create() const -{ - PDFBaselinePtr rv(new LinearBaseline()); - return rv; +PDFBaselinePtr LinearBaseline::create() const { + PDFBaselinePtr rv(new LinearBaseline()); + return rv; } - -PDFBaselinePtr LinearBaseline::clone() const -{ - PDFBaselinePtr rv(new LinearBaseline(*this)); - return rv; +PDFBaselinePtr LinearBaseline::clone() const { + PDFBaselinePtr rv(new LinearBaseline(*this)); + return rv; } // Public Methods ------------------------------------------------------------ -const string& LinearBaseline::type() const -{ - static string rv = "linear"; - return rv; -} - - -double LinearBaseline::operator()(const double& r) const -{ - return (this->getSlope() * r); +const string& LinearBaseline::type() const { + static string rv = "linear"; + return rv; } - -void LinearBaseline::setSlope(double sc) -{ - mslope = sc; +double LinearBaseline::operator()(const double& r) const { + return (this->getSlope() * r); } +void LinearBaseline::setSlope(double sc) { mslope = sc; } -const double& LinearBaseline::getSlope() const -{ - return mslope; -} - +const double& LinearBaseline::getSlope() const { return mslope; } // Registration -------------------------------------------------------------- bool reg_LinearBaseline = LinearBaseline().registerThisType(); -} // namespace srreal -} // namespace diffpy +} // namespace srreal +} // namespace diffpy // Serialization ------------------------------------------------------------- diff --git a/src/diffpy/srreal/LinearBaseline.hpp b/src/diffpy/srreal/LinearBaseline.hpp index d1616f6c..cd19a097 100644 --- a/src/diffpy/srreal/LinearBaseline.hpp +++ b/src/diffpy/srreal/LinearBaseline.hpp @@ -1,20 +1,20 @@ /***************************************************************************** -* -* 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.txt for a list of people who contributed. -* See LICENSE_DANSE.txt for license information. -* -****************************************************************************** -* -* class LinearBaseline -- linear PDF baseline -* -*****************************************************************************/ + * + * 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.txt for a list of people who contributed. + * See LICENSE_DANSE.txt for license information. + * + ****************************************************************************** + * + * class LinearBaseline -- linear PDF baseline + * + *****************************************************************************/ #ifndef LINEARBASELINE_HPP_INCLUDED #define LINEARBASELINE_HPP_INCLUDED @@ -29,41 +29,37 @@ namespace srreal { /// @class LinearBaseline /// @brief linear PDF baseline -class DLL_EXPORT LinearBaseline : public PDFBaseline -{ - public: - - // constructors - LinearBaseline(); - PDFBaselinePtr create() const; - PDFBaselinePtr clone() const; - - // methods - const std::string& type() const; - double operator()(const double& r) const; - void setSlope(double sc); - const double& getSlope() const; - - private: - - // data - double mslope; - - // serialization - friend class boost::serialization::access; - - template - void serialize(Archive& ar, const unsigned int version) - { - using boost::serialization::base_object; - ar & base_object(*this); - ar & mslope; - } +class DLL_EXPORT LinearBaseline : public PDFBaseline { + public: + // constructors + LinearBaseline(); + PDFBaselinePtr create() const; + PDFBaselinePtr clone() const; + + // methods + const std::string& type() const; + double operator()(const double& r) const; + void setSlope(double sc); + const double& getSlope() const; + + private: + // data + double mslope; + + // serialization + friend class boost::serialization::access; + + template + void serialize(Archive& ar, const unsigned int version) { + using boost::serialization::base_object; + ar& base_object(*this); + ar & mslope; + } }; // class LinearBaseline -} // namespace srreal -} // namespace diffpy +} // namespace srreal +} // namespace diffpy // Serialization ------------------------------------------------------------- diff --git a/src/diffpy/srreal/NoMetaStructureAdapter.cpp b/src/diffpy/srreal/NoMetaStructureAdapter.cpp index 59f9b540..dcb803da 100644 --- a/src/diffpy/srreal/NoMetaStructureAdapter.cpp +++ b/src/diffpy/srreal/NoMetaStructureAdapter.cpp @@ -1,26 +1,26 @@ /***************************************************************************** -* -* 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.txt for a list of people who contributed. -* See LICENSE_DANSE.txt for license information. -* -****************************************************************************** -* -* class NoMetaStructureAdapter -- StructureAdapter proxy class that disables -* customPQConfig of another StructureAdapter instance. This may be used -* for preventing the adapter for diffpy.Structure class from applying -* scale, spdiameter and such metadata to the PDFCalculator. -* -* nometa -- factory function that returns a NoMetaStructureAdapter -* instance wrapped in StructureAdapterPtr -* -*****************************************************************************/ + * + * 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.txt for a list of people who contributed. + * See LICENSE_DANSE.txt for license information. + * + ****************************************************************************** + * + * class NoMetaStructureAdapter -- StructureAdapter proxy class that disables + * customPQConfig of another StructureAdapter instance. This may be used + * for preventing the adapter for diffpy.Structure class from applying + * scale, spdiameter and such metadata to the PDFCalculator. + * + * nometa -- factory function that returns a NoMetaStructureAdapter + * instance wrapped in StructureAdapterPtr + * + *****************************************************************************/ #include #include @@ -36,129 +36,97 @@ namespace srreal { // Constructor --------------------------------------------------------------- NoMetaStructureAdapter::NoMetaStructureAdapter( - StructureAdapterPtr srcstructure) -{ - boost::shared_ptr nmptr = - boost::dynamic_pointer_cast(srcstructure); - if (nmptr) srcstructure = nmptr->getSourceStructure(); - msrcstructure = srcstructure.get() ? - srcstructure : emptyStructureAdapter(); + StructureAdapterPtr srcstructure) { + boost::shared_ptr nmptr = + boost::dynamic_pointer_cast(srcstructure); + if (nmptr) srcstructure = nmptr->getSourceStructure(); + msrcstructure = srcstructure.get() ? srcstructure : emptyStructureAdapter(); } // Public Methods ------------------------------------------------------------ -StructureAdapterPtr NoMetaStructureAdapter::clone() const -{ - boost::shared_ptr rv(new NoMetaStructureAdapter); - if (msrcstructure) rv->msrcstructure = msrcstructure->clone(); - return rv; +StructureAdapterPtr NoMetaStructureAdapter::clone() const { + boost::shared_ptr rv(new NoMetaStructureAdapter); + if (msrcstructure) rv->msrcstructure = msrcstructure->clone(); + return rv; } - -BaseBondGeneratorPtr NoMetaStructureAdapter::createBondGenerator() const -{ - BaseBondGeneratorPtr bnds = msrcstructure->createBondGenerator(); - return bnds; +BaseBondGeneratorPtr NoMetaStructureAdapter::createBondGenerator() const { + BaseBondGeneratorPtr bnds = msrcstructure->createBondGenerator(); + return bnds; } - -int NoMetaStructureAdapter::countSites() const -{ - return msrcstructure->countSites(); +int NoMetaStructureAdapter::countSites() const { + return msrcstructure->countSites(); } - -double NoMetaStructureAdapter::numberDensity() const -{ - return msrcstructure->numberDensity(); +double NoMetaStructureAdapter::numberDensity() const { + return msrcstructure->numberDensity(); } - -const std::string& NoMetaStructureAdapter::siteAtomType(int idx) const -{ - return msrcstructure->siteAtomType(idx); +const std::string& NoMetaStructureAdapter::siteAtomType(int idx) const { + return msrcstructure->siteAtomType(idx); } - -const R3::Vector& NoMetaStructureAdapter::siteCartesianPosition( - int idx) const -{ - return msrcstructure->siteCartesianPosition(idx); +const R3::Vector& NoMetaStructureAdapter::siteCartesianPosition(int idx) const { + return msrcstructure->siteCartesianPosition(idx); } - -int NoMetaStructureAdapter::siteMultiplicity(int idx) const -{ - return msrcstructure->siteMultiplicity(idx); +int NoMetaStructureAdapter::siteMultiplicity(int idx) const { + return msrcstructure->siteMultiplicity(idx); } - -double NoMetaStructureAdapter::siteOccupancy(int idx) const -{ - return msrcstructure->siteOccupancy(idx); +double NoMetaStructureAdapter::siteOccupancy(int idx) const { + return msrcstructure->siteOccupancy(idx); } - -bool NoMetaStructureAdapter::siteAnisotropy(int idx) const -{ - return msrcstructure->siteAnisotropy(idx); +bool NoMetaStructureAdapter::siteAnisotropy(int idx) const { + return msrcstructure->siteAnisotropy(idx); } - -const R3::Matrix& NoMetaStructureAdapter::siteCartesianUij(int idx) const -{ - return msrcstructure->siteCartesianUij(idx); +const R3::Matrix& NoMetaStructureAdapter::siteCartesianUij(int idx) const { + return msrcstructure->siteCartesianUij(idx); } - -void NoMetaStructureAdapter::customPQConfig(PairQuantity* pq) const -{ - // disable customPQConfig of the wrapped PairQuantity +void NoMetaStructureAdapter::customPQConfig(PairQuantity* pq) const { + // disable customPQConfig of the wrapped PairQuantity } - -StructureDifference -NoMetaStructureAdapter::diff(StructureAdapterConstPtr other) const -{ - StructureDifference sd = this->StructureAdapter::diff(other); - if (sd.stru0 == sd.stru1) return sd; - typedef boost::shared_ptr PPtr; - PPtr pother = boost::dynamic_pointer_cast(other); - if (!pother) return sd; - sd = this->getSourceStructure()->diff(pother->getSourceStructure()); - assert(sd.stru0 == this->getSourceStructure()); - assert(sd.stru1 == pother->getSourceStructure()); - sd.stru0 = this->shared_from_this(); - sd.stru1 = other; - return sd; +StructureDifference NoMetaStructureAdapter::diff( + StructureAdapterConstPtr other) const { + StructureDifference sd = this->StructureAdapter::diff(other); + if (sd.stru0 == sd.stru1) return sd; + typedef boost::shared_ptr PPtr; + PPtr pother = boost::dynamic_pointer_cast(other); + if (!pother) return sd; + sd = this->getSourceStructure()->diff(pother->getSourceStructure()); + assert(sd.stru0 == this->getSourceStructure()); + assert(sd.stru1 == pother->getSourceStructure()); + sd.stru0 = this->shared_from_this(); + sd.stru1 = other; + return sd; } - -StructureAdapterPtr -NoMetaStructureAdapter::getSourceStructure() -{ - return msrcstructure; +StructureAdapterPtr NoMetaStructureAdapter::getSourceStructure() { + return msrcstructure; } - -StructureAdapterConstPtr -NoMetaStructureAdapter::getSourceStructure() const -{ - return msrcstructure; +StructureAdapterConstPtr NoMetaStructureAdapter::getSourceStructure() const { + return msrcstructure; } // Routines ------------------------------------------------------------------ -StructureAdapterPtr nometa(StructureAdapterPtr stru) -{ - StructureAdapterPtr rv = - boost::dynamic_pointer_cast(stru) ? stru : - StructureAdapterPtr(new NoMetaStructureAdapter(stru)); - return rv; +StructureAdapterPtr nometa(StructureAdapterPtr stru) { + StructureAdapterPtr rv = + boost::dynamic_pointer_cast(stru) + ? stru + : StructureAdapterPtr(new NoMetaStructureAdapter(stru)); + return rv; } -} // namespace srreal -} // namespace diffpy +} // namespace srreal +} // namespace diffpy // Serialization ------------------------------------------------------------- diff --git a/src/diffpy/srreal/NoMetaStructureAdapter.hpp b/src/diffpy/srreal/NoMetaStructureAdapter.hpp index 2c8bd5f1..8aa08336 100644 --- a/src/diffpy/srreal/NoMetaStructureAdapter.hpp +++ b/src/diffpy/srreal/NoMetaStructureAdapter.hpp @@ -1,26 +1,26 @@ /***************************************************************************** -* -* 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.txt for a list of people who contributed. -* See LICENSE_DANSE.txt for license information. -* -****************************************************************************** -* -* class NoMetaStructureAdapter -- StructureAdapter proxy class that disables -* customPQConfig of another StructureAdapter instance. This may be used -* for preventing the adapter for diffpy.Structure class from applying -* scale, spdiameter and such metadata to the PDFCalculator. -* -* nometa -- factory function that returns a NoMetaStructureAdapter -* instance wrapped in StructureAdapterPtr -* -*****************************************************************************/ + * + * 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.txt for a list of people who contributed. + * See LICENSE_DANSE.txt for license information. + * + ****************************************************************************** + * + * class NoMetaStructureAdapter -- StructureAdapter proxy class that disables + * customPQConfig of another StructureAdapter instance. This may be used + * for preventing the adapter for diffpy.Structure class from applying + * scale, spdiameter and such metadata to the PDFCalculator. + * + * nometa -- factory function that returns a NoMetaStructureAdapter + * instance wrapped in StructureAdapterPtr + * + *****************************************************************************/ #ifndef NOMETASTRUCTUREADAPTER_HPP_INCLUDED #define NOMETASTRUCTUREADAPTER_HPP_INCLUDED @@ -32,46 +32,41 @@ namespace diffpy { namespace srreal { -class DLL_EXPORT NoMetaStructureAdapter : public StructureAdapter -{ - public: - - // constructors - NoMetaStructureAdapter() { } - NoMetaStructureAdapter(StructureAdapterPtr); - - // methods - overloaded - virtual StructureAdapterPtr clone() const; - virtual BaseBondGeneratorPtr createBondGenerator() const; - virtual int countSites() const; - virtual double numberDensity() const; - virtual const std::string& siteAtomType(int idx) const; - virtual const R3::Vector& siteCartesianPosition(int idx) const; - virtual int siteMultiplicity(int idx) const; - virtual double siteOccupancy(int idx) const; - virtual bool siteAnisotropy(int idx) const; - virtual const R3::Matrix& siteCartesianUij(int idx) const; - virtual void customPQConfig(PairQuantity* pq) const; - virtual StructureDifference diff(StructureAdapterConstPtr) const; - - // methods - own - StructureAdapterPtr getSourceStructure(); - StructureAdapterConstPtr getSourceStructure() const; - - private: - - // data - StructureAdapterPtr msrcstructure; - - // serialization - friend class boost::serialization::access; - template - void serialize(Archive& ar, const unsigned int version) - { - ar & boost::serialization::base_object(*this); - ar & msrcstructure; - } - +class DLL_EXPORT NoMetaStructureAdapter : public StructureAdapter { + public: + // constructors + NoMetaStructureAdapter() {} + NoMetaStructureAdapter(StructureAdapterPtr); + + // methods - overloaded + virtual StructureAdapterPtr clone() const; + virtual BaseBondGeneratorPtr createBondGenerator() const; + virtual int countSites() const; + virtual double numberDensity() const; + virtual const std::string& siteAtomType(int idx) const; + virtual const R3::Vector& siteCartesianPosition(int idx) const; + virtual int siteMultiplicity(int idx) const; + virtual double siteOccupancy(int idx) const; + virtual bool siteAnisotropy(int idx) const; + virtual const R3::Matrix& siteCartesianUij(int idx) const; + virtual void customPQConfig(PairQuantity* pq) const; + virtual StructureDifference diff(StructureAdapterConstPtr) const; + + // methods - own + StructureAdapterPtr getSourceStructure(); + StructureAdapterConstPtr getSourceStructure() const; + + private: + // data + StructureAdapterPtr msrcstructure; + + // serialization + friend class boost::serialization::access; + template + void serialize(Archive& ar, const unsigned int version) { + ar& boost::serialization::base_object(*this); + ar & msrcstructure; + } }; // Routines ------------------------------------------------------------------ @@ -79,17 +74,15 @@ class DLL_EXPORT NoMetaStructureAdapter : public StructureAdapter /// create NoMetaStructureAdapter from an existing StructureAdapter DLL_EXPORT StructureAdapterPtr nometa(StructureAdapterPtr stru); - /// create NoMetaStructureAdapter from an adaptable structure object template -StructureAdapterPtr nometa(const T& stru) -{ - StructureAdapterPtr bstru = convertToStructureAdapter(stru); - return nometa(bstru); +StructureAdapterPtr nometa(const T& stru) { + StructureAdapterPtr bstru = convertToStructureAdapter(stru); + return nometa(bstru); } -} // namespace srreal -} // namespace diffpy +} // namespace srreal +} // namespace diffpy // Serialization ------------------------------------------------------------- diff --git a/src/diffpy/srreal/NoSymmetryStructureAdapter.cpp b/src/diffpy/srreal/NoSymmetryStructureAdapter.cpp index 7c080d7b..6df3e9cb 100644 --- a/src/diffpy/srreal/NoSymmetryStructureAdapter.cpp +++ b/src/diffpy/srreal/NoSymmetryStructureAdapter.cpp @@ -1,28 +1,28 @@ /***************************************************************************** -* -* 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.txt for a list of people who contributed. -* See LICENSE_DANSE.txt for license information. -* -****************************************************************************** -* -* class NoSymmetryStructureAdapter -- StructureAdapter class that removes -* any symmetry expansions (rotations or periodic translations) from -* another StructureAdapter instance. This can be used to use only -* the asymmetric unit from any adapter to crystal structure. -* -* class NoSymmetryBondGenerator -- bond generator -* -* nosymmetry -- factory function that creates a NoSymmetryStructureAdapter -* instance inside StructureAdapterPtr -* -*****************************************************************************/ + * + * 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.txt for a list of people who contributed. + * See LICENSE_DANSE.txt for license information. + * + ****************************************************************************** + * + * class NoSymmetryStructureAdapter -- StructureAdapter class that removes + * any symmetry expansions (rotations or periodic translations) from + * another StructureAdapter instance. This can be used to use only + * the asymmetric unit from any adapter to crystal structure. + * + * class NoSymmetryBondGenerator -- bond generator + * + * nosymmetry -- factory function that creates a NoSymmetryStructureAdapter + * instance inside StructureAdapterPtr + * + *****************************************************************************/ #include #include @@ -38,124 +38,94 @@ namespace srreal { // Constructor --------------------------------------------------------------- NoSymmetryStructureAdapter::NoSymmetryStructureAdapter( - StructureAdapterPtr srcstructure) -{ - boost::shared_ptr nmptr = - boost::dynamic_pointer_cast(srcstructure); - if (nmptr) srcstructure = nmptr->getSourceStructure(); - msrcstructure = srcstructure.get() ? - srcstructure : emptyStructureAdapter(); + StructureAdapterPtr srcstructure) { + boost::shared_ptr nmptr = + boost::dynamic_pointer_cast(srcstructure); + if (nmptr) srcstructure = nmptr->getSourceStructure(); + msrcstructure = srcstructure.get() ? srcstructure : emptyStructureAdapter(); } // Public Methods ------------------------------------------------------------ -StructureAdapterPtr NoSymmetryStructureAdapter::clone() const -{ - boost::shared_ptr - rv(new NoSymmetryStructureAdapter); - if (msrcstructure) rv->msrcstructure = msrcstructure->clone(); - return rv; +StructureAdapterPtr NoSymmetryStructureAdapter::clone() const { + boost::shared_ptr rv( + new NoSymmetryStructureAdapter); + if (msrcstructure) rv->msrcstructure = msrcstructure->clone(); + return rv; } - -BaseBondGeneratorPtr NoSymmetryStructureAdapter::createBondGenerator() const -{ - BaseBondGeneratorPtr bnds(new BaseBondGenerator(shared_from_this())); - return bnds; +BaseBondGeneratorPtr NoSymmetryStructureAdapter::createBondGenerator() const { + BaseBondGeneratorPtr bnds(new BaseBondGenerator(shared_from_this())); + return bnds; } - -int NoSymmetryStructureAdapter::countSites() const -{ - return msrcstructure->countSites(); +int NoSymmetryStructureAdapter::countSites() const { + return msrcstructure->countSites(); } +double NoSymmetryStructureAdapter::numberDensity() const { return 0.0; } -double NoSymmetryStructureAdapter::numberDensity() const -{ - return 0.0; +const std::string& NoSymmetryStructureAdapter::siteAtomType(int idx) const { + return msrcstructure->siteAtomType(idx); } - -const std::string& NoSymmetryStructureAdapter::siteAtomType(int idx) const -{ - return msrcstructure->siteAtomType(idx); -} - - const R3::Vector& NoSymmetryStructureAdapter::siteCartesianPosition( - int idx) const -{ - return msrcstructure->siteCartesianPosition(idx); + int idx) const { + return msrcstructure->siteCartesianPosition(idx); } - -double NoSymmetryStructureAdapter::siteOccupancy(int idx) const -{ - return msrcstructure->siteOccupancy(idx); +double NoSymmetryStructureAdapter::siteOccupancy(int idx) const { + return msrcstructure->siteOccupancy(idx); } - -bool NoSymmetryStructureAdapter::siteAnisotropy(int idx) const -{ - return msrcstructure->siteAnisotropy(idx); +bool NoSymmetryStructureAdapter::siteAnisotropy(int idx) const { + return msrcstructure->siteAnisotropy(idx); } - -const R3::Matrix& NoSymmetryStructureAdapter::siteCartesianUij(int idx) const -{ - return msrcstructure->siteCartesianUij(idx); +const R3::Matrix& NoSymmetryStructureAdapter::siteCartesianUij(int idx) const { + return msrcstructure->siteCartesianUij(idx); } - -void NoSymmetryStructureAdapter::customPQConfig(PairQuantity* pq) const -{ - msrcstructure->customPQConfig(pq); +void NoSymmetryStructureAdapter::customPQConfig(PairQuantity* pq) const { + msrcstructure->customPQConfig(pq); } - -StructureDifference -NoSymmetryStructureAdapter::diff(StructureAdapterConstPtr other) const -{ - StructureDifference sd = this->StructureAdapter::diff(other); - if (sd.stru0 == sd.stru1) return sd; - typedef boost::shared_ptr PPtr; - PPtr pother = boost::dynamic_pointer_cast(other); - if (!pother) return sd; - sd = this->getSourceStructure()->diff(pother->getSourceStructure()); - assert(sd.stru0 == this->getSourceStructure()); - assert(sd.stru1 == pother->getSourceStructure()); - sd.stru0 = this->shared_from_this(); - sd.stru1 = other; - return sd; +StructureDifference NoSymmetryStructureAdapter::diff( + StructureAdapterConstPtr other) const { + StructureDifference sd = this->StructureAdapter::diff(other); + if (sd.stru0 == sd.stru1) return sd; + typedef boost::shared_ptr PPtr; + PPtr pother = boost::dynamic_pointer_cast(other); + if (!pother) return sd; + sd = this->getSourceStructure()->diff(pother->getSourceStructure()); + assert(sd.stru0 == this->getSourceStructure()); + assert(sd.stru1 == pother->getSourceStructure()); + sd.stru0 = this->shared_from_this(); + sd.stru1 = other; + return sd; } - -StructureAdapterPtr -NoSymmetryStructureAdapter::getSourceStructure() -{ - return msrcstructure; +StructureAdapterPtr NoSymmetryStructureAdapter::getSourceStructure() { + return msrcstructure; } - -StructureAdapterConstPtr -NoSymmetryStructureAdapter::getSourceStructure() const -{ - return msrcstructure; +StructureAdapterConstPtr NoSymmetryStructureAdapter::getSourceStructure() + const { + return msrcstructure; } // Routines ------------------------------------------------------------------ -StructureAdapterPtr nosymmetry(StructureAdapterPtr stru) -{ - StructureAdapterPtr rv = - boost::dynamic_pointer_cast(stru) ? stru : - StructureAdapterPtr(new NoSymmetryStructureAdapter(stru)); - return rv; +StructureAdapterPtr nosymmetry(StructureAdapterPtr stru) { + StructureAdapterPtr rv = + boost::dynamic_pointer_cast(stru) + ? stru + : StructureAdapterPtr(new NoSymmetryStructureAdapter(stru)); + return rv; } -} // namespace srreal -} // namespace diffpy +} // namespace srreal +} // namespace diffpy // Serialization ------------------------------------------------------------- diff --git a/src/diffpy/srreal/NoSymmetryStructureAdapter.hpp b/src/diffpy/srreal/NoSymmetryStructureAdapter.hpp index cff5279b..bd9526b0 100644 --- a/src/diffpy/srreal/NoSymmetryStructureAdapter.hpp +++ b/src/diffpy/srreal/NoSymmetryStructureAdapter.hpp @@ -1,28 +1,28 @@ /***************************************************************************** -* -* 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.txt for a list of people who contributed. -* See LICENSE_DANSE.txt for license information. -* -****************************************************************************** -* -* class NoSymmetryStructureAdapter -- StructureAdapter class that removes -* any symmetry expansions (rotations or periodic translations) from -* another StructureAdapter instance. This can be used to use only -* the asymmetric unit from any adapter to crystal structure. -* -* class NoSymmetryBondGenerator -- bond generator -* -* nosymmetry -- factory function that creates a NoSymmetryStructureAdapter -* instance inside StructureAdapterPtr -* -*****************************************************************************/ + * + * 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.txt for a list of people who contributed. + * See LICENSE_DANSE.txt for license information. + * + ****************************************************************************** + * + * class NoSymmetryStructureAdapter -- StructureAdapter class that removes + * any symmetry expansions (rotations or periodic translations) from + * another StructureAdapter instance. This can be used to use only + * the asymmetric unit from any adapter to crystal structure. + * + * class NoSymmetryBondGenerator -- bond generator + * + * nosymmetry -- factory function that creates a NoSymmetryStructureAdapter + * instance inside StructureAdapterPtr + * + *****************************************************************************/ #ifndef NOSYMMETRYSTRUCTUREADAPTER_HPP_INCLUDED #define NOSYMMETRYSTRUCTUREADAPTER_HPP_INCLUDED @@ -34,46 +34,41 @@ namespace diffpy { namespace srreal { -class DLL_EXPORT NoSymmetryStructureAdapter : public StructureAdapter -{ - public: - - // constructors - NoSymmetryStructureAdapter() { } - NoSymmetryStructureAdapter(StructureAdapterPtr); - - // methods - overloaded - virtual StructureAdapterPtr clone() const; - virtual BaseBondGeneratorPtr createBondGenerator() const; - virtual int countSites() const; - virtual double numberDensity() const; - virtual const std::string& siteAtomType(int idx) const; - virtual const R3::Vector& siteCartesianPosition(int idx) const; - // reusing base-class StructureAdapter::siteMultiplicity() - virtual double siteOccupancy(int idx) const; - virtual bool siteAnisotropy(int idx) const; - virtual const R3::Matrix& siteCartesianUij(int idx) const; - virtual void customPQConfig(PairQuantity* pq) const; - virtual StructureDifference diff(StructureAdapterConstPtr) const; - - // methods - own - StructureAdapterPtr getSourceStructure(); - StructureAdapterConstPtr getSourceStructure() const; - - private: - - // data - StructureAdapterPtr msrcstructure; - - // serialization - friend class boost::serialization::access; - template - void serialize(Archive& ar, const unsigned int version) - { - ar & boost::serialization::base_object(*this); - ar & msrcstructure; - } - +class DLL_EXPORT NoSymmetryStructureAdapter : public StructureAdapter { + public: + // constructors + NoSymmetryStructureAdapter() {} + NoSymmetryStructureAdapter(StructureAdapterPtr); + + // methods - overloaded + virtual StructureAdapterPtr clone() const; + virtual BaseBondGeneratorPtr createBondGenerator() const; + virtual int countSites() const; + virtual double numberDensity() const; + virtual const std::string& siteAtomType(int idx) const; + virtual const R3::Vector& siteCartesianPosition(int idx) const; + // reusing base-class StructureAdapter::siteMultiplicity() + virtual double siteOccupancy(int idx) const; + virtual bool siteAnisotropy(int idx) const; + virtual const R3::Matrix& siteCartesianUij(int idx) const; + virtual void customPQConfig(PairQuantity* pq) const; + virtual StructureDifference diff(StructureAdapterConstPtr) const; + + // methods - own + StructureAdapterPtr getSourceStructure(); + StructureAdapterConstPtr getSourceStructure() const; + + private: + // data + StructureAdapterPtr msrcstructure; + + // serialization + friend class boost::serialization::access; + template + void serialize(Archive& ar, const unsigned int version) { + ar& boost::serialization::base_object(*this); + ar & msrcstructure; + } }; // Routines ------------------------------------------------------------------ @@ -81,17 +76,15 @@ class DLL_EXPORT NoSymmetryStructureAdapter : public StructureAdapter /// create NoSymmetryStructureAdapter from an existing StructureAdapter DLL_EXPORT StructureAdapterPtr nosymmetry(StructureAdapterPtr stru); - /// create NoSymmetryStructureAdapter from an adaptable structure object template -StructureAdapterPtr nosymmetry(const T& stru) -{ - StructureAdapterPtr bstru = convertToStructureAdapter(stru); - return nosymmetry(bstru); +StructureAdapterPtr nosymmetry(const T& stru) { + StructureAdapterPtr bstru = convertToStructureAdapter(stru); + return nosymmetry(bstru); } -} // namespace srreal -} // namespace diffpy +} // namespace srreal +} // namespace diffpy // Serialization ------------------------------------------------------------- diff --git a/src/diffpy/srreal/ObjCrystStructureAdapter.cpp b/src/diffpy/srreal/ObjCrystStructureAdapter.cpp index f74395ee..51a1f8b9 100644 --- a/src/diffpy/srreal/ObjCrystStructureAdapter.cpp +++ b/src/diffpy/srreal/ObjCrystStructureAdapter.cpp @@ -1,22 +1,22 @@ /***************************************************************************** -* -* 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: Chris Farrow -* -* See AUTHORS.txt for a list of people who contributed. -* See LICENSE_DANSE.txt for license information. -* -****************************************************************************** -* -* StructureAdapterPtr createStructureAdapter(const ObjCryst::Crystal&) -* StructureAdapterPtr createStructureAdapter(const ObjCryst::Molecule&) -* -- structure adapter factories for ObjCryst objects. -* -*****************************************************************************/ + * + * 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: Chris Farrow + * + * See AUTHORS.txt for a list of people who contributed. + * See LICENSE_DANSE.txt for license information. + * + ****************************************************************************** + * + * StructureAdapterPtr createStructureAdapter(const ObjCryst::Crystal&) + * StructureAdapterPtr createStructureAdapter(const ObjCryst::Molecule&) + * -- structure adapter factories for ObjCryst objects. + * + *****************************************************************************/ #include #include @@ -39,168 +39,147 @@ namespace { // Utility functions --------------------------------------------------------- -R3::Matrix -getUij(const ObjCryst::ScatteringPower* sp) -{ - const double BtoU = 1.0 / (8 * M_PI * M_PI); - R3::Matrix Uij = R3::zeromatrix(); - if (sp->IsIsotropic()) - { - Uij(0,0) = Uij(1,1) = Uij(2,2) = sp->GetBiso() * BtoU; - } - else - { - Uij(0,0) = sp->GetBij(1,1) * BtoU; - Uij(1,1) = sp->GetBij(2,2) * BtoU; - Uij(2,2) = sp->GetBij(3,3) * BtoU; - Uij(0,1) = Uij(1,0) = sp->GetBij(1,2) * BtoU; - Uij(0,2) = Uij(2,0) = sp->GetBij(1,3) * BtoU; - Uij(1,2) = Uij(2,1) = sp->GetBij(2,3) * BtoU; - } - return Uij; +R3::Matrix getUij(const ObjCryst::ScatteringPower* sp) { + const double BtoU = 1.0 / (8 * M_PI * M_PI); + R3::Matrix Uij = R3::zeromatrix(); + if (sp->IsIsotropic()) { + Uij(0, 0) = Uij(1, 1) = Uij(2, 2) = sp->GetBiso() * BtoU; + } else { + Uij(0, 0) = sp->GetBij(1, 1) * BtoU; + Uij(1, 1) = sp->GetBij(2, 2) * BtoU; + Uij(2, 2) = sp->GetBij(3, 3) * BtoU; + Uij(0, 1) = Uij(1, 0) = sp->GetBij(1, 2) * BtoU; + Uij(0, 2) = Uij(2, 0) = sp->GetBij(1, 3) * BtoU; + Uij(1, 2) = Uij(2, 1) = sp->GetBij(2, 3) * BtoU; + } + return Uij; } - -CrystalStructureAdapter::SymOpVector -fetchSymmetryOperations(const ObjCryst::SpaceGroup& spacegroup) -{ - CrystalStructureAdapter::SymOpVector rv; - const int nbsym = spacegroup.GetNbSymmetrics(); - rv.resize(nbsym); - const vector& sgsymops = - spacegroup.GetSymmetryOperations(); - // copy convert the ObjCryst symmetry operations - int last = sgsymops.size(); - assert(last <= nbsym); - for (int i = 0; i != last; ++i) - { - CrystalStructureAdapter::SymOpVector::value_type& op = rv[i]; - copy(sgsymops[i].mx, sgsymops[i].mx + 9, op.R.data().begin()); - copy(sgsymops[i].tr, sgsymops[i].tr + 3, op.t.data().begin()); +CrystalStructureAdapter::SymOpVector fetchSymmetryOperations( + const ObjCryst::SpaceGroup& spacegroup) { + CrystalStructureAdapter::SymOpVector rv; + const int nbsym = spacegroup.GetNbSymmetrics(); + rv.resize(nbsym); + const vector& sgsymops = + spacegroup.GetSymmetryOperations(); + // copy convert the ObjCryst symmetry operations + int last = sgsymops.size(); + assert(last <= nbsym); + for (int i = 0; i != last; ++i) { + CrystalStructureAdapter::SymOpVector::value_type& op = rv[i]; + copy(sgsymops[i].mx, sgsymops[i].mx + 9, op.R.data().begin()); + copy(sgsymops[i].tr, sgsymops[i].tr + 3, op.t.data().begin()); + } + // apply all origin translations + const vector& sgtrans = + spacegroup.GetTranslationVectors(); + const int nbtran = spacegroup.GetNbTranslationVectors(); + assert(int(sgtrans.size()) == nbtran); + assert(nbtran * last <= nbsym); + for (int nt = 0; nt < nbtran; ++nt) { + const double* pt = sgtrans[nt].tr; + R3::Vector sgt(pt[0], pt[1], pt[2]); + for (int i = 0; i < last; ++i) { + int k = i + nt * last; + assert(k < nbsym); + rv[k] = rv[i]; + rv[k].t += sgt; } - // apply all origin translations - const vector& sgtrans = - spacegroup.GetTranslationVectors(); - const int nbtran = spacegroup.GetNbTranslationVectors(); - assert(int(sgtrans.size()) == nbtran); - assert(nbtran * last <= nbsym); - for (int nt = 0; nt < nbtran; ++nt) - { - const double* pt = sgtrans[nt].tr; - R3::Vector sgt(pt[0], pt[1], pt[2]); - for (int i = 0; i < last; ++i) - { - int k = i + nt * last; - assert(k < nbsym); - rv[k] = rv[i]; - rv[k].t += sgt; - } + } + last = sgsymops.size() * nbtran; + // apply the inversion center if necessary + if (last < nbsym) { + assert(spacegroup.IsCentrosymmetric()); + assert(nbsym == 2 * last); + CrystVector_REAL xyzinv = spacegroup.GetInversionCenter(); + // tinv is the overall translation associated with inversion center. + // it is double the inversion center position. + R3::Vector tinv(xyzinv(0), xyzinv(1), xyzinv(2)); + tinv *= 2.0; + // now apply the inversion center here + for (int i = 0, j = last; i < last; ++i, ++j) { + rv[j].R = -1 * rv[i].R; + rv[j].t = tinv - rv[i].t; } - last = sgsymops.size() * nbtran; - // apply the inversion center if necessary - if (last < nbsym) - { - assert(spacegroup.IsCentrosymmetric()); - assert(nbsym == 2 * last); - CrystVector_REAL xyzinv = spacegroup.GetInversionCenter(); - // tinv is the overall translation associated with inversion center. - // it is double the inversion center position. - R3::Vector tinv(xyzinv(0), xyzinv(1), xyzinv(2)); - tinv *= 2.0; - // now apply the inversion center here - for (int i = 0, j = last; i < last; ++i, ++j) - { - rv[j].R = -1 * rv[i].R; - rv[j].t = tinv - rv[i].t; - } - last *= 2; - } - assert(nbsym == last); - return rv; + last *= 2; + } + assert(nbsym == last); + return rv; } -} // namespace +} // namespace ////////////////////////////////////////////////////////////////////////////// // StructureAdapter factory for ObjCryst::Crystal ////////////////////////////////////////////////////////////////////////////// -StructureAdapterPtr -createStructureAdapter(const ObjCryst::Crystal& cryst) -{ - const double radtodeg = 180 / M_PI; - CrystalStructureAdapterPtr adpt(new CrystalStructureAdapter); - adpt->setLatPar( - cryst.GetLatticePar(0), - cryst.GetLatticePar(1), - cryst.GetLatticePar(2), - radtodeg * cryst.GetLatticePar(3), - radtodeg * cryst.GetLatticePar(4), - radtodeg * cryst.GetLatticePar(5)); - // find out number of scatterers in the asymmetric unit - const ObjCryst::ScatteringComponentList& scl = - cryst.GetScatteringComponentList(); - size_t nbComponent = scl.GetNbComponent(); - adpt->reserve(nbComponent); - Atom ai; - const Lattice& L = adpt->getLattice(); - for (size_t i = 0; i < nbComponent; ++i) - { - const ObjCryst::ScatteringComponent& sc = scl(i); - const ObjCryst::ScatteringPower* sp = sc.mpScattPow; - // Skip over this if it is a dummy atom. A dummy atom has no - // mpScattPow, and therefore no type. It's just in a structure as a - // reference position. - if (sp == NULL) continue; - ai.occupancy = sc.mOccupancy; - ai.anisotropy = !(sp->IsIsotropic()); - ai.atomtype = sp->GetSymbol(); - R3::Vector xyz(sc.mX, sc.mY, sc.mZ); - ai.xyz_cartn = L.cartesian(xyz); - // Store Uij - R3::Matrix uijl = getUij(sp); - ai.uij_cartn = ai.anisotropy ? L.cartesianMatrix(uijl) : uijl; - adpt->append(ai); - } - const ObjCryst::SpaceGroup& spacegroup = cryst.GetSpaceGroup(); - CrystalStructureAdapter::SymOpVector symops = - fetchSymmetryOperations(spacegroup); - CrystalStructureAdapter::SymOpVector::const_iterator op; - for (op = symops.begin(); op != symops.end(); ++op) adpt->addSymOp(*op); - adpt->updateSymmetryPositions(); - return adpt; +StructureAdapterPtr createStructureAdapter(const ObjCryst::Crystal& cryst) { + const double radtodeg = 180 / M_PI; + CrystalStructureAdapterPtr adpt(new CrystalStructureAdapter); + adpt->setLatPar(cryst.GetLatticePar(0), cryst.GetLatticePar(1), + cryst.GetLatticePar(2), radtodeg * cryst.GetLatticePar(3), + radtodeg * cryst.GetLatticePar(4), + radtodeg * cryst.GetLatticePar(5)); + // find out number of scatterers in the asymmetric unit + const ObjCryst::ScatteringComponentList& scl = + cryst.GetScatteringComponentList(); + size_t nbComponent = scl.GetNbComponent(); + adpt->reserve(nbComponent); + Atom ai; + const Lattice& L = adpt->getLattice(); + for (size_t i = 0; i < nbComponent; ++i) { + const ObjCryst::ScatteringComponent& sc = scl(i); + const ObjCryst::ScatteringPower* sp = sc.mpScattPow; + // Skip over this if it is a dummy atom. A dummy atom has no + // mpScattPow, and therefore no type. It's just in a structure as a + // reference position. + if (sp == NULL) continue; + ai.occupancy = sc.mOccupancy; + ai.anisotropy = !(sp->IsIsotropic()); + ai.atomtype = sp->GetSymbol(); + R3::Vector xyz(sc.mX, sc.mY, sc.mZ); + ai.xyz_cartn = L.cartesian(xyz); + // Store Uij + R3::Matrix uijl = getUij(sp); + ai.uij_cartn = ai.anisotropy ? L.cartesianMatrix(uijl) : uijl; + adpt->append(ai); + } + const ObjCryst::SpaceGroup& spacegroup = cryst.GetSpaceGroup(); + CrystalStructureAdapter::SymOpVector symops = + fetchSymmetryOperations(spacegroup); + CrystalStructureAdapter::SymOpVector::const_iterator op; + for (op = symops.begin(); op != symops.end(); ++op) adpt->addSymOp(*op); + adpt->updateSymmetryPositions(); + return adpt; } ////////////////////////////////////////////////////////////////////////////// // StructureAdapter factory for ObjCryst::Molecule ////////////////////////////////////////////////////////////////////////////// -StructureAdapterPtr -createStructureAdapter(const ObjCryst::Molecule& molecule) -{ - AtomicStructureAdapterPtr adpt(new AtomicStructureAdapter); - size_t nbComponent = molecule.GetNbComponent(); - adpt->reserve(nbComponent); - Atom ai; - for (size_t i = 0; i < nbComponent; ++i) - { - const ObjCryst::MolAtom& atom = molecule.GetAtom(i); - if (atom.IsDummy()) continue; - ai.occupancy = atom.GetOccupancy(); - const ObjCryst::ScatteringPower* sp = &(atom.GetScatteringPower()); - ai.anisotropy = !(sp->IsIsotropic()); - ai.atomtype = sp->GetSymbol(); - ai.xyz_cartn[0] = atom.X(); - ai.xyz_cartn[1] = atom.Y(); - ai.xyz_cartn[2] = atom.Z(); - // Store Uij - ai.uij_cartn = getUij(sp); - adpt->append(ai); - } - return adpt; +StructureAdapterPtr createStructureAdapter(const ObjCryst::Molecule& molecule) { + AtomicStructureAdapterPtr adpt(new AtomicStructureAdapter); + size_t nbComponent = molecule.GetNbComponent(); + adpt->reserve(nbComponent); + Atom ai; + for (size_t i = 0; i < nbComponent; ++i) { + const ObjCryst::MolAtom& atom = molecule.GetAtom(i); + if (atom.IsDummy()) continue; + ai.occupancy = atom.GetOccupancy(); + const ObjCryst::ScatteringPower* sp = &(atom.GetScatteringPower()); + ai.anisotropy = !(sp->IsIsotropic()); + ai.atomtype = sp->GetSymbol(); + ai.xyz_cartn[0] = atom.X(); + ai.xyz_cartn[1] = atom.Y(); + ai.xyz_cartn[2] = atom.Z(); + // Store Uij + ai.uij_cartn = getUij(sp); + adpt->append(ai); + } + return adpt; } -} // namespace srreal -} // namespace diffpy +} // namespace srreal +} // namespace diffpy // End of file diff --git a/src/diffpy/srreal/ObjCrystStructureAdapter.hpp b/src/diffpy/srreal/ObjCrystStructureAdapter.hpp index 264578cc..35ca5ee9 100644 --- a/src/diffpy/srreal/ObjCrystStructureAdapter.hpp +++ b/src/diffpy/srreal/ObjCrystStructureAdapter.hpp @@ -1,22 +1,22 @@ /***************************************************************************** -* -* 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: Chris Farrow -* -* See AUTHORS.txt for a list of people who contributed. -* See LICENSE_DANSE.txt for license information. -* -****************************************************************************** -* -* StructureAdapterPtr createStructureAdapter(const ObjCryst::Crystal&) -* StructureAdapterPtr createStructureAdapter(const ObjCryst::Molecule&) -* -- structure adapter factories for ObjCryst objects. -* -*****************************************************************************/ + * + * 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: Chris Farrow + * + * See AUTHORS.txt for a list of people who contributed. + * See LICENSE_DANSE.txt for license information. + * + ****************************************************************************** + * + * StructureAdapterPtr createStructureAdapter(const ObjCryst::Crystal&) + * StructureAdapterPtr createStructureAdapter(const ObjCryst::Molecule&) + * -- structure adapter factories for ObjCryst objects. + * + *****************************************************************************/ #ifndef OBJCRYSTSTRUCTUREADAPTER_HPP_INCLUDED #define OBJCRYSTSTRUCTUREADAPTER_HPP_INCLUDED @@ -43,12 +43,11 @@ createStructureAdapter(const ObjCryst::Crystal& cryst); // as if in a cartesian cell. If this is not what is intended, pass the // molecule as a scattering component within an ObjCryst::Crystal. - DLL_EXPORT StructureAdapterPtr createStructureAdapter(const ObjCryst::Molecule& molecule); -} // namespace srreal -} // namespace diffpy +} // namespace srreal +} // namespace diffpy // Required for argument-lookup visibility when instantiating the // convertToStructureAdapter template. diff --git a/src/diffpy/srreal/OverlapCalculator.cpp b/src/diffpy/srreal/OverlapCalculator.cpp index 008addcb..976ad8fb 100644 --- a/src/diffpy/srreal/OverlapCalculator.cpp +++ b/src/diffpy/srreal/OverlapCalculator.cpp @@ -1,20 +1,20 @@ /***************************************************************************** -* -* 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.txt for a list of people who contributed. -* See LICENSE_DANSE.txt for license information. -* -****************************************************************************** -* -* class OverlapCalculator -- calculator of atom radii overlaps -* -*****************************************************************************/ + * + * 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.txt for a list of people who contributed. + * See LICENSE_DANSE.txt for license information. + * + ****************************************************************************** + * + * class OverlapCalculator -- calculator of atom radii overlaps + * + *****************************************************************************/ #include @@ -35,533 +35,440 @@ namespace srreal { namespace { enum { - DISTANCE_OFFSET, - DIRECTION0_OFFSET, - DIRECTION1_OFFSET, - DIRECTION2_OFFSET, - SITE0_OFFSET, - SITE1_OFFSET, - CHUNK_SIZE, + DISTANCE_OFFSET, + DIRECTION0_OFFSET, + DIRECTION1_OFFSET, + DIRECTION2_OFFSET, + SITE0_OFFSET, + SITE1_OFFSET, + CHUNK_SIZE, }; - template -vector vectorconvert(const QuantityType& v) -{ - vector rv; - rv.reserve(v.size()); - QuantityType::const_iterator x = v.begin(); - for (; x != v.end(); ++x) rv.push_back(T(*x)); - return rv; +vector vectorconvert(const QuantityType& v) { + vector rv; + rv.reserve(v.size()); + QuantityType::const_iterator x = v.begin(); + for (; x != v.end(); ++x) rv.push_back(T(*x)); + return rv; } - -} // namespace +} // namespace // Constructor --------------------------------------------------------------- -OverlapCalculator::OverlapCalculator() -{ - mevaluator->setFlag(USEFULLSUM, true); - // default configuration - AtomRadiiTablePtr table(new ConstantRadiiTable); - this->setAtomRadiiTable(table); - this->cacheStructureData(); - // use very large rmax, it will be cropped by rmaxused - this->setRmax(100); - mneighborids_cached = false; - // attributes - this->registerDoubleAttribute("rmaxused", this, - &OverlapCalculator::getRmaxUsed); +OverlapCalculator::OverlapCalculator() { + mevaluator->setFlag(USEFULLSUM, true); + // default configuration + AtomRadiiTablePtr table(new ConstantRadiiTable); + this->setAtomRadiiTable(table); + this->cacheStructureData(); + // use very large rmax, it will be cropped by rmaxused + this->setRmax(100); + mneighborids_cached = false; + // attributes + this->registerDoubleAttribute("rmaxused", this, + &OverlapCalculator::getRmaxUsed); }; // Public Methods ------------------------------------------------------------ -QuantityType OverlapCalculator::overlaps() const -{ - int n = this->count(); - QuantityType rv; - rv.reserve(n); - for (int idx = 0; idx < n; ++idx) - { - double olp = this->suboverlap(idx); - if (olp <= 0.0) continue; - rv.push_back(olp); - } - return rv; -} - - -QuantityType OverlapCalculator::distances() const -{ - return this->subvector(DISTANCE_OFFSET, OVERLAPPING); -} - - -vector OverlapCalculator::directions() const -{ - int n = this->count(); - vector rv; - rv.reserve(n); - for (int index = 0; index < n; ++index) - { - if (this->suboverlap(index) <= 0.0) continue; - rv.push_back(this->subdirection(index)); - } - return rv; -} - - -SiteIndices OverlapCalculator::sites0() const -{ - QuantityType rv0 = this->subvector(SITE0_OFFSET, OVERLAPPING); - return vectorconvert(rv0); -} - - -SiteIndices OverlapCalculator::sites1() const -{ - QuantityType rv0 = this->subvector(SITE1_OFFSET, OVERLAPPING); - return vectorconvert(rv0); +QuantityType OverlapCalculator::overlaps() const { + int n = this->count(); + QuantityType rv; + rv.reserve(n); + for (int idx = 0; idx < n; ++idx) { + double olp = this->suboverlap(idx); + if (olp <= 0.0) continue; + rv.push_back(olp); + } + return rv; } - -vector OverlapCalculator::types0() const -{ - return siteIndicesToTypes(mstructure, this->sites0()); +QuantityType OverlapCalculator::distances() const { + return this->subvector(DISTANCE_OFFSET, OVERLAPPING); } - -vector OverlapCalculator::types1() const -{ - return siteIndicesToTypes(mstructure, this->sites1()); +vector OverlapCalculator::directions() const { + int n = this->count(); + vector rv; + rv.reserve(n); + for (int index = 0; index < n; ++index) { + if (this->suboverlap(index) <= 0.0) continue; + rv.push_back(this->subdirection(index)); + } + return rv; } - -QuantityType OverlapCalculator::siteSquareOverlaps() const -{ - int n = this->count(); - int cntsites = this->countSites(); - QuantityType rv(cntsites, 0.0); - for (int index = 0; index < n; ++index) - { - double olp = this->suboverlap(index); - if (olp <= 0.0) continue; - double sqoverlap = olp * olp; - int i = int(this->subvalue(SITE0_OFFSET, index)); - int j = int(this->subvalue(SITE1_OFFSET, index)); - rv[i] += sqoverlap * mstructure->siteOccupancy(j); - } - // overlaps are shared by 2 atoms - QuantityType::iterator xi; - for (xi = rv.begin(); xi != rv.end(); ++xi) *xi /= 2; - return rv; -} - - -double OverlapCalculator::totalSquareOverlap() const -{ - int cntsites = this->countSites(); - QuantityType sqoverlap = this->siteSquareOverlaps(); - double rv = 0.0; - for (int i = 0; i < cntsites; ++i) - { - rv += sqoverlap[i] * - mstructure->siteMultiplicity(i) * mstructure->siteOccupancy(i); - } - return rv; -} - - -double OverlapCalculator::meanSquareOverlap() const -{ - double totocc = mstructure->totalOccupancy(); - double rv = (totocc > 0) ? (this->totalSquareOverlap() / totocc) : 0.0; - return rv; +SiteIndices OverlapCalculator::sites0() const { + QuantityType rv0 = this->subvector(SITE0_OFFSET, OVERLAPPING); + return vectorconvert(rv0); } - -double OverlapCalculator::flipDiffTotal(int i, int j) const -{ - int cntsites = this->countSites(); - if (i < 0 || i >= cntsites || j < 0 || j >= cntsites) - { - const char* emsg = "Index out of range."; - throw invalid_argument(emsg); - } - bool sameradii = (i == j) || - (mstructure_cache.siteradii[i] == mstructure_cache.siteradii[j]); - if (sameradii) return 0.0; - // here we have to remove the overlap contributions for i and j - double rv = 0.0; - const list& ineighbors = this->getNeighborIds(i); - const list& jneighbors = this->getNeighborIds(j); - unordered_set allids; - allids.insert(ineighbors.begin(), ineighbors.end()); - allids.insert(jneighbors.begin(), jneighbors.end()); - unordered_set::const_iterator idx; - for (idx = allids.begin(); idx != allids.end(); ++idx) - { - int i1 = int(this->subvalue(SITE0_OFFSET, *idx)); - int j1 = int(this->subvalue(SITE1_OFFSET, *idx)); - double sqscale = - ((i1 == j1) ? 1 : 2) * - mstructure->siteOccupancy(i1) * mstructure->siteOccupancy(j1) * - mstructure->siteMultiplicity(i1) / 2; - double olp0 = this->suboverlap(*idx); - double olp1 = this->suboverlap(*idx, i, j); - rv -= sqscale * olp0 * olp0; - rv += sqscale * olp1 * olp1; - } - return rv; +SiteIndices OverlapCalculator::sites1() const { + QuantityType rv0 = this->subvector(SITE1_OFFSET, OVERLAPPING); + return vectorconvert(rv0); } - -double OverlapCalculator::flipDiffMean(int i, int j) const -{ - double totocc = mstructure->totalOccupancy(); - double rv = (totocc > 0) ? (this->flipDiffTotal(i, j) / totocc) : 0.0; - return rv; +vector OverlapCalculator::types0() const { + return siteIndicesToTypes(mstructure, this->sites0()); } - -vector OverlapCalculator::gradients() const -{ - using diffpy::mathutils::eps_gt; - int n = this->count(); - int cntsites = this->countSites(); - vector rv(cntsites, R3::Vector(0.0, 0.0, 0.0)); - R3::Vector gij; - for (int index = 0; index < n; ++index) - { - double olp = this->suboverlap(index); - if (olp <= 0.0) continue; - const double& dst = this->subvalue(DISTANCE_OFFSET, index); - assert(eps_gt(dst, 0.0)); - int j = int(this->subvalue(SITE1_OFFSET, index)); - gij = -2.0 * olp / dst * this->subdirection(index); - rv[j] += gij; - } - return rv; -} - - -unordered_set OverlapCalculator::getNeighborSites(int i) const -{ - unordered_set rv; - const list& ineighbors = this->getNeighborIds(i); - list::const_iterator idx; - for (idx = ineighbors.begin(); idx != ineighbors.end(); ++idx) - { - double olp = this->suboverlap(*idx); - if (olp <= 0.0) continue; - assert(i == int(this->subvalue(SITE0_OFFSET, *idx))); - int j1 = int(this->subvalue(SITE1_OFFSET, *idx)); - rv.insert(j1); - } - return rv; +vector OverlapCalculator::types1() const { + return siteIndicesToTypes(mstructure, this->sites1()); } - -QuantityType OverlapCalculator::coordinations() const -{ - int cntsites = this->countSites(); - QuantityType rv(cntsites, 0.0); - int n = this->count(); - for (int index = 0; index < n; ++index) - { - double olp = this->suboverlap(index); - if (olp <= 0.0) continue; - int j0 = int(this->subvalue(SITE0_OFFSET, index)); - int j1 = int(this->subvalue(SITE1_OFFSET, index)); - rv[j0] += mstructure->siteOccupancy(j1); - } - return rv; -} - - -unordered_map -OverlapCalculator::coordinationByTypes(int i) const -{ - unordered_map rv; - const list& ineighbors = this->getNeighborIds(i); - list::const_iterator idx; - for (idx = ineighbors.begin(); idx != ineighbors.end(); ++idx) - { - double olp = this->suboverlap(*idx); - if (olp <= 0.0) continue; - int j0 = int(this->subvalue(SITE0_OFFSET, *idx)); - int j1 = int(this->subvalue(SITE1_OFFSET, *idx)); - if (j0 == i) - { - const string& tp = mstructure->siteAtomType(j1); - rv[tp] += mstructure->siteOccupancy(j1); - } - else - { - assert(j1 == i); - const string& tp = mstructure->siteAtomType(j0); - rv[tp] += mstructure->siteOccupancy(j0) * +QuantityType OverlapCalculator::siteSquareOverlaps() const { + int n = this->count(); + int cntsites = this->countSites(); + QuantityType rv(cntsites, 0.0); + for (int index = 0; index < n; ++index) { + double olp = this->suboverlap(index); + if (olp <= 0.0) continue; + double sqoverlap = olp * olp; + int i = int(this->subvalue(SITE0_OFFSET, index)); + int j = int(this->subvalue(SITE1_OFFSET, index)); + rv[i] += sqoverlap * mstructure->siteOccupancy(j); + } + // overlaps are shared by 2 atoms + QuantityType::iterator xi; + for (xi = rv.begin(); xi != rv.end(); ++xi) *xi /= 2; + return rv; +} + +double OverlapCalculator::totalSquareOverlap() const { + int cntsites = this->countSites(); + QuantityType sqoverlap = this->siteSquareOverlaps(); + double rv = 0.0; + for (int i = 0; i < cntsites; ++i) { + rv += sqoverlap[i] * mstructure->siteMultiplicity(i) * + mstructure->siteOccupancy(i); + } + return rv; +} + +double OverlapCalculator::meanSquareOverlap() const { + double totocc = mstructure->totalOccupancy(); + double rv = (totocc > 0) ? (this->totalSquareOverlap() / totocc) : 0.0; + return rv; +} + +double OverlapCalculator::flipDiffTotal(int i, int j) const { + int cntsites = this->countSites(); + if (i < 0 || i >= cntsites || j < 0 || j >= cntsites) { + const char* emsg = "Index out of range."; + throw invalid_argument(emsg); + } + bool sameradii = (i == j) || (mstructure_cache.siteradii[i] == + mstructure_cache.siteradii[j]); + if (sameradii) return 0.0; + // here we have to remove the overlap contributions for i and j + double rv = 0.0; + const list& ineighbors = this->getNeighborIds(i); + const list& jneighbors = this->getNeighborIds(j); + unordered_set allids; + allids.insert(ineighbors.begin(), ineighbors.end()); + allids.insert(jneighbors.begin(), jneighbors.end()); + unordered_set::const_iterator idx; + for (idx = allids.begin(); idx != allids.end(); ++idx) { + int i1 = int(this->subvalue(SITE0_OFFSET, *idx)); + int j1 = int(this->subvalue(SITE1_OFFSET, *idx)); + double sqscale = ((i1 == j1) ? 1 : 2) * mstructure->siteOccupancy(i1) * + mstructure->siteOccupancy(j1) * + mstructure->siteMultiplicity(i1) / 2; + double olp0 = this->suboverlap(*idx); + double olp1 = this->suboverlap(*idx, i, j); + rv -= sqscale * olp0 * olp0; + rv += sqscale * olp1 * olp1; + } + return rv; +} + +double OverlapCalculator::flipDiffMean(int i, int j) const { + double totocc = mstructure->totalOccupancy(); + double rv = (totocc > 0) ? (this->flipDiffTotal(i, j) / totocc) : 0.0; + return rv; +} + +vector OverlapCalculator::gradients() const { + using diffpy::mathutils::eps_gt; + int n = this->count(); + int cntsites = this->countSites(); + vector rv(cntsites, R3::Vector(0.0, 0.0, 0.0)); + R3::Vector gij; + for (int index = 0; index < n; ++index) { + double olp = this->suboverlap(index); + if (olp <= 0.0) continue; + const double& dst = this->subvalue(DISTANCE_OFFSET, index); + assert(eps_gt(dst, 0.0)); + int j = int(this->subvalue(SITE1_OFFSET, index)); + gij = -2.0 * olp / dst * this->subdirection(index); + rv[j] += gij; + } + return rv; +} + +unordered_set OverlapCalculator::getNeighborSites(int i) const { + unordered_set rv; + const list& ineighbors = this->getNeighborIds(i); + list::const_iterator idx; + for (idx = ineighbors.begin(); idx != ineighbors.end(); ++idx) { + double olp = this->suboverlap(*idx); + if (olp <= 0.0) continue; + assert(i == int(this->subvalue(SITE0_OFFSET, *idx))); + int j1 = int(this->subvalue(SITE1_OFFSET, *idx)); + rv.insert(j1); + } + return rv; +} + +QuantityType OverlapCalculator::coordinations() const { + int cntsites = this->countSites(); + QuantityType rv(cntsites, 0.0); + int n = this->count(); + for (int index = 0; index < n; ++index) { + double olp = this->suboverlap(index); + if (olp <= 0.0) continue; + int j0 = int(this->subvalue(SITE0_OFFSET, index)); + int j1 = int(this->subvalue(SITE1_OFFSET, index)); + rv[j0] += mstructure->siteOccupancy(j1); + } + return rv; +} + +unordered_map OverlapCalculator::coordinationByTypes( + int i) const { + unordered_map rv; + const list& ineighbors = this->getNeighborIds(i); + list::const_iterator idx; + for (idx = ineighbors.begin(); idx != ineighbors.end(); ++idx) { + double olp = this->suboverlap(*idx); + if (olp <= 0.0) continue; + int j0 = int(this->subvalue(SITE0_OFFSET, *idx)); + int j1 = int(this->subvalue(SITE1_OFFSET, *idx)); + if (j0 == i) { + const string& tp = mstructure->siteAtomType(j1); + rv[tp] += mstructure->siteOccupancy(j1); + } else { + assert(j1 == i); + const string& tp = mstructure->siteAtomType(j0); + rv[tp] += mstructure->siteOccupancy(j0) * mstructure->siteMultiplicity(j0) / mstructure->siteMultiplicity(j1); - } } - return rv; -} - - -vector< unordered_set > OverlapCalculator::neighborhoods() const -{ - int cntsites = this->countSites(); - typedef unordered_set SiteSet; - typedef std::vector< boost::shared_ptr > SiteSetPointers; - SiteSetPointers rvptr(cntsites); - int n = this->count(); - for (int index = 0; index < n; ++index) - { - double olp = this->suboverlap(index); - if (olp <= 0.0) continue; - int j0 = int(this->subvalue(SITE0_OFFSET, index)); - int j1 = int(this->subvalue(SITE1_OFFSET, index)); - if (!rvptr[j0].get()) - { - rvptr[j0].reset(new SiteSet); - rvptr[j0]->insert(j0); - } - if (!rvptr[j1].get()) - { - rvptr[j0]->insert(j1); - rvptr[j1] = rvptr[j0]; - } - assert(rvptr[j0].get() && rvptr[j1].get()); - if (rvptr[j0].get() == rvptr[j1].get()) continue; - // here we need to unify the j0 and j1 sets - SiteSetPointers::value_type ss1 = rvptr[j1]; - SiteSet::const_iterator idx1 = ss1->begin(); - for (; idx1 != ss1->end(); ++idx1) - { - rvptr[j0]->insert(*idx1); - rvptr[*idx1] = rvptr[j0]; - } + } + return rv; +} + +vector > OverlapCalculator::neighborhoods() const { + int cntsites = this->countSites(); + typedef unordered_set SiteSet; + typedef std::vector > SiteSetPointers; + SiteSetPointers rvptr(cntsites); + int n = this->count(); + for (int index = 0; index < n; ++index) { + double olp = this->suboverlap(index); + if (olp <= 0.0) continue; + int j0 = int(this->subvalue(SITE0_OFFSET, index)); + int j1 = int(this->subvalue(SITE1_OFFSET, index)); + if (!rvptr[j0].get()) { + rvptr[j0].reset(new SiteSet); + rvptr[j0]->insert(j0); } - // helper function that initializes neighbor set at site i - auto initsiteset = [&](int i, int& counter) { - if (!rvptr[i]) { - rvptr[i].reset(new SiteSet(&i, &i + 1)); - ++counter; - } - }; - // count initialized items in rvptr - int cnt = count_if(rvptr.begin(), rvptr.end(), - [](SiteSetPointers::value_type& p) { return bool(p); }); - // create self-neighborhoods unless prohibited by mask - for (int j0 = 0; j0 < cntsites && cnt < cntsites; ++j0) - { - for (int j1 = j0; j1 < cntsites; ++j1) - { - if (rvptr[j0] && rvptr[j1]) continue; - if (!this->getPairMask(j0, j1)) continue; - initsiteset(j0, cnt); - initsiteset(j1, cnt); - } + if (!rvptr[j1].get()) { + rvptr[j0]->insert(j1); + rvptr[j1] = rvptr[j0]; } - unordered_set duplicate; - vector< unordered_set > rv; - SiteSetPointers::const_iterator ssp; - for (ssp = rvptr.begin(); ssp != rvptr.end(); ++ssp) - { - if (!ssp->get() || duplicate.count(ssp->get())) continue; - duplicate.insert(ssp->get()); - rv.push_back(**ssp); + assert(rvptr[j0].get() && rvptr[j1].get()); + if (rvptr[j0].get() == rvptr[j1].get()) continue; + // here we need to unify the j0 and j1 sets + SiteSetPointers::value_type ss1 = rvptr[j1]; + SiteSet::const_iterator idx1 = ss1->begin(); + for (; idx1 != ss1->end(); ++idx1) { + rvptr[j0]->insert(*idx1); + rvptr[*idx1] = rvptr[j0]; } - return rv; + } + // helper function that initializes neighbor set at site i + auto initsiteset = [&](int i, int& counter) { + if (!rvptr[i]) { + rvptr[i].reset(new SiteSet(&i, &i + 1)); + ++counter; + } + }; + // count initialized items in rvptr + int cnt = count_if(rvptr.begin(), rvptr.end(), + [](SiteSetPointers::value_type& p) { return bool(p); }); + // create self-neighborhoods unless prohibited by mask + for (int j0 = 0; j0 < cntsites && cnt < cntsites; ++j0) { + for (int j1 = j0; j1 < cntsites; ++j1) { + if (rvptr[j0] && rvptr[j1]) continue; + if (!this->getPairMask(j0, j1)) continue; + initsiteset(j0, cnt); + initsiteset(j1, cnt); + } + } + unordered_set duplicate; + vector > rv; + SiteSetPointers::const_iterator ssp; + for (ssp = rvptr.begin(); ssp != rvptr.end(); ++ssp) { + if (!ssp->get() || duplicate.count(ssp->get())) continue; + duplicate.insert(ssp->get()); + rv.push_back(**ssp); + } + return rv; } - -void OverlapCalculator::setAtomRadiiTable(AtomRadiiTablePtr table) -{ - ensureNonNull("AtomRadiiTable", table); - matomradiitable = table; +void OverlapCalculator::setAtomRadiiTable(AtomRadiiTablePtr table) { + ensureNonNull("AtomRadiiTable", table); + matomradiitable = table; } - -void OverlapCalculator::setAtomRadiiTableByType(const std::string& tp) -{ - matomradiitable = AtomRadiiTable::createByType(tp); +void OverlapCalculator::setAtomRadiiTableByType(const std::string& tp) { + matomradiitable = AtomRadiiTable::createByType(tp); } - -AtomRadiiTablePtr& OverlapCalculator::getAtomRadiiTable() -{ - return matomradiitable; +AtomRadiiTablePtr& OverlapCalculator::getAtomRadiiTable() { + return matomradiitable; } - -const AtomRadiiTablePtr& OverlapCalculator::getAtomRadiiTable() const -{ - return matomradiitable; +const AtomRadiiTablePtr& OverlapCalculator::getAtomRadiiTable() const { + return matomradiitable; } - -double OverlapCalculator::getRmaxUsed() const -{ - assert(this->countSites() == int(mstructure_cache.siteradii.size())); - double rv = min(this->getRmax(), mstructure_cache.maxseparation); - return rv; +double OverlapCalculator::getRmaxUsed() const { + assert(this->countSites() == int(mstructure_cache.siteradii.size())); + double rv = min(this->getRmax(), mstructure_cache.maxseparation); + return rv; } // Protected Methods --------------------------------------------------------- -void OverlapCalculator::resetValue() -{ - mvalue.clear(); - mneighborids.clear(); - mneighborids_cached = false; - this->cacheStructureData(); - this->PairQuantity::resetValue(); +void OverlapCalculator::resetValue() { + mvalue.clear(); + mneighborids.clear(); + mneighborids_cached = false; + this->cacheStructureData(); + this->PairQuantity::resetValue(); } - -void OverlapCalculator::configureBondGenerator(BaseBondGenerator& bnds) const -{ - bnds.setRmin(this->getRmin()); - bnds.setRmax(this->getRmaxUsed()); +void OverlapCalculator::configureBondGenerator(BaseBondGenerator& bnds) const { + bnds.setRmin(this->getRmin()); + bnds.setRmax(this->getRmaxUsed()); } - -void OverlapCalculator::addPairContribution( - const BaseBondGenerator& bnds, int summationscale) -{ - assert(summationscale == 1); - assert(bnds.distance() <= mstructure_cache.maxseparation); - const R3::Vector& r01 = bnds.r01(); - int baseidx = mvalue.size(); - mvalue.insert(mvalue.end(), CHUNK_SIZE, 0.0); - mvalue[baseidx + DISTANCE_OFFSET] = bnds.distance(); - mvalue[baseidx + DIRECTION0_OFFSET] = r01[0]; - mvalue[baseidx + DIRECTION1_OFFSET] = r01[1]; - mvalue[baseidx + DIRECTION2_OFFSET] = r01[2]; - mvalue[baseidx + SITE0_OFFSET] = bnds.site0(); - mvalue[baseidx + SITE1_OFFSET] = bnds.site1(); +void OverlapCalculator::addPairContribution(const BaseBondGenerator& bnds, + int summationscale) { + assert(summationscale == 1); + assert(bnds.distance() <= mstructure_cache.maxseparation); + const R3::Vector& r01 = bnds.r01(); + int baseidx = mvalue.size(); + mvalue.insert(mvalue.end(), CHUNK_SIZE, 0.0); + mvalue[baseidx + DISTANCE_OFFSET] = bnds.distance(); + mvalue[baseidx + DIRECTION0_OFFSET] = r01[0]; + mvalue[baseidx + DIRECTION1_OFFSET] = r01[1]; + mvalue[baseidx + DIRECTION2_OFFSET] = r01[2]; + mvalue[baseidx + SITE0_OFFSET] = bnds.site0(); + mvalue[baseidx + SITE1_OFFSET] = bnds.site1(); } - -void OverlapCalculator::executeParallelMerge(const std::string& pdata) -{ - istringstream storage(pdata, ios::binary); - diffpy::serialization::iarchive ia(storage, ios::binary); - QuantityType pvalue; - ia >> pvalue; - mvalue.insert(mvalue.end(), pvalue.begin(), pvalue.end()); +void OverlapCalculator::executeParallelMerge(const std::string& pdata) { + istringstream storage(pdata, ios::binary); + diffpy::serialization::iarchive ia(storage, ios::binary); + QuantityType pvalue; + ia >> pvalue; + mvalue.insert(mvalue.end(), pvalue.begin(), pvalue.end()); } // Private Methods ----------------------------------------------------------- -int OverlapCalculator::count() const -{ - int rv = int(mvalue.size()) / CHUNK_SIZE; - return rv; -} - - -QuantityType OverlapCalculator::subvector(int offset, - OverlapCalculator::OverlapFlag flag) const -{ - assert(0 <= offset && offset < CHUNK_SIZE); - assert(flag == ALLVALUES || flag == OVERLAPPING); +int OverlapCalculator::count() const { + int rv = int(mvalue.size()) / CHUNK_SIZE; + return rv; +} + +QuantityType OverlapCalculator::subvector( + int offset, OverlapCalculator::OverlapFlag flag) const { + assert(0 <= offset && offset < CHUNK_SIZE); + assert(flag == ALLVALUES || flag == OVERLAPPING); + int n = this->count(); + QuantityType rv; + rv.reserve(n); + for (int index = 0; index < n; ++index) { + if (OVERLAPPING == flag && this->suboverlap(index) <= 0.0) continue; + rv.push_back(this->subvalue(offset, index)); + } + return rv; +} + +const double& OverlapCalculator::subvalue(int offset, int index) const { + assert(0 <= offset && offset < CHUNK_SIZE); + assert(0 <= index && index < this->count()); + return mvalue[offset + CHUNK_SIZE * index]; +} + +const R3::Vector& OverlapCalculator::subdirection(int index) const { + static R3::Vector rv; + rv[0] = this->subvalue(DIRECTION0_OFFSET, index); + rv[1] = this->subvalue(DIRECTION1_OFFSET, index); + rv[2] = this->subvalue(DIRECTION2_OFFSET, index); + return rv; +} + +double OverlapCalculator::suboverlap(int index, int flipi, int flipj) const { + assert(0 <= flipi && flipi < this->countSites()); + assert(0 <= flipj && flipj < this->countSites()); + int i = int(this->subvalue(SITE0_OFFSET, index)); + int j = int(this->subvalue(SITE1_OFFSET, index)); + const double& radiusi = (flipi == flipj) ? mstructure_cache.siteradii[i] + : (i == flipi) ? mstructure_cache.siteradii[flipj] + : (i == flipj) ? mstructure_cache.siteradii[flipi] + : mstructure_cache.siteradii[i]; + const double& radiusj = (flipi == flipj) ? mstructure_cache.siteradii[j] + : (j == flipi) ? mstructure_cache.siteradii[flipj] + : (j == flipj) ? mstructure_cache.siteradii[flipi] + : mstructure_cache.siteradii[j]; + const double& dij = this->subvalue(DISTANCE_OFFSET, index); + double sepij = radiusi + radiusj; + double rv = (dij < sepij) ? (sepij - dij) : 0.0; + return rv; +} + +void OverlapCalculator::cacheStructureData() { + int cntsites = this->countSites(); + mstructure_cache.siteradii.resize(cntsites); + const AtomRadiiTablePtr& table = this->getAtomRadiiTable(); + for (int i = 0; i < cntsites; ++i) { + const string& smbl = mstructure->siteAtomType(i); + mstructure_cache.siteradii[i] = table->lookup(smbl); + } + double maxradius = mstructure_cache.siteradii.empty() + ? 0.0 + : *max_element(mstructure_cache.siteradii.begin(), + mstructure_cache.siteradii.end()); + mstructure_cache.maxseparation = 2 * maxradius; +} + +const std::list& OverlapCalculator::getNeighborIds(int k) const { + typedef std::list NbList; + assert(0 <= k && k < this->countSites()); + if (!mneighborids_cached) { + assert(mneighborids.empty()); int n = this->count(); - QuantityType rv; - rv.reserve(n); - for (int index = 0; index < n; ++index) - { - if (OVERLAPPING == flag && this->suboverlap(index) <= 0.0) continue; - rv.push_back(this->subvalue(offset, index)); - } - return rv; -} - - -const double& OverlapCalculator::subvalue(int offset, int index) const -{ - assert(0 <= offset && offset < CHUNK_SIZE); - assert(0 <= index && index < this->count()); - return mvalue[offset + CHUNK_SIZE * index]; -} - - -const R3::Vector& OverlapCalculator::subdirection(int index) const -{ - static R3::Vector rv; - rv[0] = this->subvalue(DIRECTION0_OFFSET, index); - rv[1] = this->subvalue(DIRECTION1_OFFSET, index); - rv[2] = this->subvalue(DIRECTION2_OFFSET, index); - return rv; -} - - -double OverlapCalculator::suboverlap(int index, int flipi, int flipj) const -{ - assert(0 <= flipi && flipi < this->countSites()); - assert(0 <= flipj && flipj < this->countSites()); - int i = int(this->subvalue(SITE0_OFFSET, index)); - int j = int(this->subvalue(SITE1_OFFSET, index)); - const double& radiusi = (flipi == flipj) ? mstructure_cache.siteradii[i] : - (i == flipi) ? mstructure_cache.siteradii[flipj] : - (i == flipj) ? mstructure_cache.siteradii[flipi] : - mstructure_cache.siteradii[i]; - const double& radiusj = (flipi == flipj) ? mstructure_cache.siteradii[j] : - (j == flipi) ? mstructure_cache.siteradii[flipj] : - (j == flipj) ? mstructure_cache.siteradii[flipi] : - mstructure_cache.siteradii[j]; - const double& dij = this->subvalue(DISTANCE_OFFSET, index); - double sepij = radiusi + radiusj; - double rv = (dij < sepij) ? (sepij - dij) : 0.0; - return rv; -} - - -void OverlapCalculator::cacheStructureData() -{ - int cntsites = this->countSites(); - mstructure_cache.siteradii.resize(cntsites); - const AtomRadiiTablePtr& table = this->getAtomRadiiTable(); - for (int i = 0; i < cntsites; ++i) - { - const string& smbl = mstructure->siteAtomType(i); - mstructure_cache.siteradii[i] = table->lookup(smbl); - } - double maxradius = mstructure_cache.siteradii.empty() ? - 0.0 : *max_element(mstructure_cache.siteradii.begin(), - mstructure_cache.siteradii.end()); - mstructure_cache.maxseparation = 2 * maxradius; -} - - -const std::list& -OverlapCalculator::getNeighborIds(int k) const -{ - typedef std::list NbList; - assert(0 <= k && k < this->countSites()); - if (!mneighborids_cached) - { - assert(mneighborids.empty()); - int n = this->count(); - for (int idx = 0; idx < n; ++idx) - { - int i = int(this->subvalue(SITE0_OFFSET, idx)); - mneighborids[i].push_back(idx); - } - mneighborids_cached = true; + for (int idx = 0; idx < n; ++idx) { + int i = int(this->subvalue(SITE0_OFFSET, idx)); + mneighborids[i].push_back(idx); } - static NbList noneighbors; - NeighborIdsStorage::const_iterator nbit = mneighborids.find(k); - const NbList& rv = - (nbit == mneighborids.end()) ? noneighbors : nbit->second; - return rv; + mneighborids_cached = true; + } + static NbList noneighbors; + NeighborIdsStorage::const_iterator nbit = mneighborids.find(k); + const NbList& rv = (nbit == mneighborids.end()) ? noneighbors : nbit->second; + return rv; } -} // namespace srreal -} // namespace diffpy +} // namespace srreal +} // namespace diffpy // Serialization ------------------------------------------------------------- diff --git a/src/diffpy/srreal/OverlapCalculator.hpp b/src/diffpy/srreal/OverlapCalculator.hpp index 1f861a48..c37cdb64 100644 --- a/src/diffpy/srreal/OverlapCalculator.hpp +++ b/src/diffpy/srreal/OverlapCalculator.hpp @@ -1,20 +1,20 @@ /***************************************************************************** -* -* 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.txt for a list of people who contributed. -* See LICENSE_DANSE.txt for license information. -* -****************************************************************************** -* -* class OverlapCalculator -- calculator of atom radii overlaps -* -*****************************************************************************/ + * + * 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.txt for a list of people who contributed. + * See LICENSE_DANSE.txt for license information. + * + ****************************************************************************** + * + * class OverlapCalculator -- calculator of atom radii overlaps + * + *****************************************************************************/ #ifndef OVERLAPCALCULATOR_HPP_INCLUDED #define OVERLAPCALCULATOR_HPP_INCLUDED @@ -29,122 +29,113 @@ namespace diffpy { namespace srreal { -class DLL_EXPORT OverlapCalculator : public PairQuantity -{ - public: - - // constructor - OverlapCalculator(); - - // methods - /// return siteSquareOverlaps for the specified structure - template QuantityType operator()(const T&); - /// overlap values for all overlapping sites in the structure - QuantityType overlaps() const; - /// distances all overlapping sites in the structure - QuantityType distances() const; - /// directions between the centers of overlapping sites - std::vector directions() const; - /// indices of the first site for all overlapping pairs - SiteIndices sites0() const; - /// indices of the second site for all overlapping pairs - SiteIndices sites1() const; - /// atom symbols for the first site of all overlapping pairs - std::vector types0() const; - /// atom symbols for the second site of all overlapping pairs - std::vector types1() const; - /// sum of squared overlaps at each structure site - QuantityType siteSquareOverlaps() const; - /// sum of squared overlaps for all atoms in the structure - double totalSquareOverlap() const; - /// mean square per one atom in the structure - double meanSquareOverlap() const; - /// difference in the totalSquareOverlap for a flip of two sites - double flipDiffTotal(int i, int j) const; - /// difference in the meanSquareOverlap for a flip of two sites - double flipDiffMean(int i, int j) const; - /// gradients of totalSquareOverlap at each site in the structure - std::vector gradients() const; - /// indices of the neighboring sites - std::unordered_set getNeighborSites(int i) const; - /// coordination number at each site of the structure - QuantityType coordinations() const; - /// coordination number split per each type of neighboring atoms - std::unordered_map - coordinationByTypes(int i) const; - /// sets of site inidices per each sites neighborhood in the structure - std::vector< std::unordered_set > neighborhoods() const; - - // access and configuration of the atom radii - void setAtomRadiiTable(AtomRadiiTablePtr); - void setAtomRadiiTableByType(const std::string& tp); - AtomRadiiTablePtr& getAtomRadiiTable(); - const AtomRadiiTablePtr& getAtomRadiiTable() const; - - /// effective rmax value, usually a double of the maximum atom radius. - double getRmaxUsed() const; - - - protected: - - // PairQuantity overloads - virtual void resetValue(); - virtual void configureBondGenerator(BaseBondGenerator&) const; - virtual void addPairContribution(const BaseBondGenerator&, int); - virtual void executeParallelMerge(const std::string&); - - private: - - // types - enum OverlapFlag {ALLVALUES, OVERLAPPING}; - typedef std::unordered_map > NeighborIdsStorage; - - // methods - int count() const; - QuantityType subvector(int offset, OverlapFlag flag) const; - const double& subvalue(int offset, int index) const; - const R3::Vector& subdirection(int index) const; - double suboverlap(int index, int iflip=0, int jflip=0) const; - void cacheStructureData(); - const std::list& getNeighborIds(int i) const; - - // data - AtomRadiiTablePtr matomradiitable; - mutable NeighborIdsStorage mneighborids; - mutable bool mneighborids_cached; - // cache - struct { - QuantityType siteradii; - double maxseparation; - } mstructure_cache; - - // serialization - friend class boost::serialization::access; - template - void serialize(Archive& ar, const unsigned int version) - { - using boost::serialization::base_object; - ar & base_object(*this); - ar & matomradiitable; - ar & mneighborids; - ar & mstructure_cache.siteradii; - ar & mstructure_cache.maxseparation; - } - +class DLL_EXPORT OverlapCalculator : public PairQuantity { + public: + // constructor + OverlapCalculator(); + + // methods + /// return siteSquareOverlaps for the specified structure + template + QuantityType operator()(const T&); + /// overlap values for all overlapping sites in the structure + QuantityType overlaps() const; + /// distances all overlapping sites in the structure + QuantityType distances() const; + /// directions between the centers of overlapping sites + std::vector directions() const; + /// indices of the first site for all overlapping pairs + SiteIndices sites0() const; + /// indices of the second site for all overlapping pairs + SiteIndices sites1() const; + /// atom symbols for the first site of all overlapping pairs + std::vector types0() const; + /// atom symbols for the second site of all overlapping pairs + std::vector types1() const; + /// sum of squared overlaps at each structure site + QuantityType siteSquareOverlaps() const; + /// sum of squared overlaps for all atoms in the structure + double totalSquareOverlap() const; + /// mean square per one atom in the structure + double meanSquareOverlap() const; + /// difference in the totalSquareOverlap for a flip of two sites + double flipDiffTotal(int i, int j) const; + /// difference in the meanSquareOverlap for a flip of two sites + double flipDiffMean(int i, int j) const; + /// gradients of totalSquareOverlap at each site in the structure + std::vector gradients() const; + /// indices of the neighboring sites + std::unordered_set getNeighborSites(int i) const; + /// coordination number at each site of the structure + QuantityType coordinations() const; + /// coordination number split per each type of neighboring atoms + std::unordered_map coordinationByTypes(int i) const; + /// sets of site inidices per each sites neighborhood in the structure + std::vector > neighborhoods() const; + + // access and configuration of the atom radii + void setAtomRadiiTable(AtomRadiiTablePtr); + void setAtomRadiiTableByType(const std::string& tp); + AtomRadiiTablePtr& getAtomRadiiTable(); + const AtomRadiiTablePtr& getAtomRadiiTable() const; + + /// effective rmax value, usually a double of the maximum atom radius. + double getRmaxUsed() const; + + protected: + // PairQuantity overloads + virtual void resetValue(); + virtual void configureBondGenerator(BaseBondGenerator&) const; + virtual void addPairContribution(const BaseBondGenerator&, int); + virtual void executeParallelMerge(const std::string&); + + private: + // types + enum OverlapFlag { ALLVALUES, OVERLAPPING }; + typedef std::unordered_map > NeighborIdsStorage; + + // methods + int count() const; + QuantityType subvector(int offset, OverlapFlag flag) const; + const double& subvalue(int offset, int index) const; + const R3::Vector& subdirection(int index) const; + double suboverlap(int index, int iflip = 0, int jflip = 0) const; + void cacheStructureData(); + const std::list& getNeighborIds(int i) const; + + // data + AtomRadiiTablePtr matomradiitable; + mutable NeighborIdsStorage mneighborids; + mutable bool mneighborids_cached; + // cache + struct { + QuantityType siteradii; + double maxseparation; + } mstructure_cache; + + // serialization + friend class boost::serialization::access; + template + void serialize(Archive& ar, const unsigned int version) { + using boost::serialization::base_object; + ar& base_object(*this); + ar & matomradiitable; + ar & mneighborids; + ar & mstructure_cache.siteradii; + ar & mstructure_cache.maxseparation; + } }; // Public Template Methods --------------------------------------------------- template -QuantityType OverlapCalculator::operator()(const T& stru) -{ - this->eval(stru); - return this->siteSquareOverlaps(); +QuantityType OverlapCalculator::operator()(const T& stru) { + this->eval(stru); + return this->siteSquareOverlaps(); } - -} // namespace srreal -} // namespace diffpy +} // namespace srreal +} // namespace diffpy // Serialization ------------------------------------------------------------- diff --git a/src/diffpy/srreal/PDFBaseline.cpp b/src/diffpy/srreal/PDFBaseline.cpp index aa20ccba..89e13f1a 100644 --- a/src/diffpy/srreal/PDFBaseline.cpp +++ b/src/diffpy/srreal/PDFBaseline.cpp @@ -1,20 +1,20 @@ /***************************************************************************** -* -* 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.txt for a list of people who contributed. -* See LICENSE_DANSE.txt for license information. -* -****************************************************************************** -* -* class PDFBaseline -- abstract base class for PDF baseline functions -* -*****************************************************************************/ + * + * 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.txt for a list of people who contributed. + * See LICENSE_DANSE.txt for license information. + * + ****************************************************************************** + * + * class PDFBaseline -- abstract base class for PDF baseline functions + * + *****************************************************************************/ #include #include @@ -25,7 +25,7 @@ namespace diffpy { // Unique instantiation of the template registry base class. template class DLL_EXPORT HasClassRegistry; -} // namespace diffpy +} // namespace diffpy // Serialization ------------------------------------------------------------- diff --git a/src/diffpy/srreal/PDFBaseline.hpp b/src/diffpy/srreal/PDFBaseline.hpp index 8d2f1385..64e57770 100644 --- a/src/diffpy/srreal/PDFBaseline.hpp +++ b/src/diffpy/srreal/PDFBaseline.hpp @@ -1,23 +1,23 @@ /***************************************************************************** -* -* 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.txt for a list of people who contributed. -* See LICENSE_DANSE.txt for license information. -* -****************************************************************************** -* -* class PDFBaseline -- abstract base class for PDF baseline functions -* A concrete instance of PDFBaseline is a functor, that calculates -* baseline value at a given pair distance r. The baseline is added to -* (R(r) * r) before multiplication by any envelope functions. -* -*****************************************************************************/ + * + * 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.txt for a list of people who contributed. + * See LICENSE_DANSE.txt for license information. + * + ****************************************************************************** + * + * class PDFBaseline -- abstract base class for PDF baseline functions + * A concrete instance of PDFBaseline is a functor, that calculates + * baseline value at a given pair distance r. The baseline is added to + * (R(r) * r) before multiplication by any envelope functions. + * + *****************************************************************************/ #ifndef PDFBASELINE_HPP_INCLUDED #define PDFBASELINE_HPP_INCLUDED @@ -38,28 +38,23 @@ namespace srreal { /// @class PDFBaseline /// @brief abstract base class for PDF baseline function -class DLL_EXPORT PDFBaseline : - public Attributes, - public HasClassRegistry -{ - public: - - // methods - virtual double operator()(const double& r) const = 0; - - private: - - // serialization - friend class boost::serialization::access; - template - void serialize(Archive& ar, const unsigned int version) { } - +class DLL_EXPORT PDFBaseline : public Attributes, + public HasClassRegistry { + public: + // methods + virtual double operator()(const double& r) const = 0; + + private: + // serialization + friend class boost::serialization::access; + template + void serialize(Archive& ar, const unsigned int version) {} }; typedef PDFBaseline::SharedPtr PDFBaselinePtr; -} // namespace srreal -} // namespace diffpy +} // namespace srreal +} // namespace diffpy // Serialization ------------------------------------------------------------- diff --git a/src/diffpy/srreal/PDFCalculator.cpp b/src/diffpy/srreal/PDFCalculator.cpp index 47c7189a..32970787 100644 --- a/src/diffpy/srreal/PDFCalculator.cpp +++ b/src/diffpy/srreal/PDFCalculator.cpp @@ -1,20 +1,20 @@ /***************************************************************************** -* -* 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.txt for a list of people who contributed. -* See LICENSE_DANSE.txt for license information. -* -****************************************************************************** -* -* class PDFCalculator -- real space PDF calculator -* -*****************************************************************************/ + * + * 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.txt for a list of people who contributed. + * See LICENSE_DANSE.txt for license information. + * + ****************************************************************************** + * + * class PDFCalculator -- real space PDF calculator + * + *****************************************************************************/ #include #include @@ -42,406 +42,326 @@ namespace { /// Return true if qstep value can be cheaply recomputed in initial setup. template -bool _initialQstepUpdate(const PDFC* pc) -{ - // Check if we use the initial empty structure and constructor - // has completed, i.e., the last attribute is available. - const bool rv = - (pc->getStructure() == emptyStructureAdapter()) && - pc->hasDoubleAttr("extendedrmax"); - return rv; +bool _initialQstepUpdate(const PDFC* pc) { + // Check if we use the initial empty structure and constructor + // has completed, i.e., the last attribute is available. + const bool rv = (pc->getStructure() == emptyStructureAdapter()) && + pc->hasDoubleAttr("extendedrmax"); + return rv; } -} // namespace +} // namespace // Constructor --------------------------------------------------------------- -PDFCalculator::PDFCalculator() : - mqmin(0.0), - mqmax(DOUBLE_MAX), - mrstep(DEFAULT_PDFCALCULATOR_RSTEP), - mmaxextension(DEFAULT_PDFCALCULATOR_MAXEXTENSION) -{ - // default configuration - mrmax = DEFAULT_PDFCALCULATOR_RMAX; - this->setPeakWidthModelByType("jeong"); - this->setPeakProfileByType("gaussian"); - this->getPeakProfile()->setPrecision(DEFAULT_PEAKPRECISION); - this->setBaselineByType("linear"); - this->setScatteringFactorTableByType("xray"); - // envelopes - this->addEnvelopeByType("scale"); - this->addEnvelopeByType("qresolution"); - // cache all internal data according to an empty structure. - // this rebuilds mstructure_cache and mrlimits_cache. - this->setStructure(mstructure); - // setup the OPTIMIZED evaluator last so that its validation passes. - this->setEvaluatorType(OPTIMIZED); - // attributes - this->registerDoubleAttribute("qmin", this, - &PDFCalculator::getQmin, &PDFCalculator::setQmin); - this->registerDoubleAttribute("qmax", this, - &PDFCalculator::getQmax, &PDFCalculator::setQmax); - this->registerDoubleAttribute("qstep", this, - &PDFCalculator::getQstep); - this->registerDoubleAttribute("rmin", this, - &PDFCalculator::getRmin, &PDFCalculator::setRmin); - this->registerDoubleAttribute("rmax", this, - &PDFCalculator::getRmax, &PDFCalculator::setRmax); - this->registerDoubleAttribute("rstep", this, - &PDFCalculator::getRstep, &PDFCalculator::setRstep); - this->registerDoubleAttribute("maxextension", this, - &PDFCalculator::getMaxExtension, &PDFCalculator::setMaxExtension); - this->registerDoubleAttribute("extendedrmin", this, - &PDFCalculator::getExtendedRmin); - this->registerDoubleAttribute("extendedrmax", this, - &PDFCalculator::getExtendedRmax); +PDFCalculator::PDFCalculator() + : mqmin(0.0), + mqmax(DOUBLE_MAX), + mrstep(DEFAULT_PDFCALCULATOR_RSTEP), + mmaxextension(DEFAULT_PDFCALCULATOR_MAXEXTENSION) { + // default configuration + mrmax = DEFAULT_PDFCALCULATOR_RMAX; + this->setPeakWidthModelByType("jeong"); + this->setPeakProfileByType("gaussian"); + this->getPeakProfile()->setPrecision(DEFAULT_PEAKPRECISION); + this->setBaselineByType("linear"); + this->setScatteringFactorTableByType("xray"); + // envelopes + this->addEnvelopeByType("scale"); + this->addEnvelopeByType("qresolution"); + // cache all internal data according to an empty structure. + // this rebuilds mstructure_cache and mrlimits_cache. + this->setStructure(mstructure); + // setup the OPTIMIZED evaluator last so that its validation passes. + this->setEvaluatorType(OPTIMIZED); + // attributes + this->registerDoubleAttribute("qmin", this, &PDFCalculator::getQmin, + &PDFCalculator::setQmin); + this->registerDoubleAttribute("qmax", this, &PDFCalculator::getQmax, + &PDFCalculator::setQmax); + this->registerDoubleAttribute("qstep", this, &PDFCalculator::getQstep); + this->registerDoubleAttribute("rmin", this, &PDFCalculator::getRmin, + &PDFCalculator::setRmin); + this->registerDoubleAttribute("rmax", this, &PDFCalculator::getRmax, + &PDFCalculator::setRmax); + this->registerDoubleAttribute("rstep", this, &PDFCalculator::getRstep, + &PDFCalculator::setRstep); + this->registerDoubleAttribute("maxextension", this, + &PDFCalculator::getMaxExtension, + &PDFCalculator::setMaxExtension); + this->registerDoubleAttribute("extendedrmin", this, + &PDFCalculator::getExtendedRmin); + this->registerDoubleAttribute("extendedrmax", this, + &PDFCalculator::getExtendedRmax); } // Public Methods ------------------------------------------------------------ // PairQuantity overloads -eventticker::EventTicker& PDFCalculator::ticker() const -{ - eventticker::EventTicker& tic = this->PairQuantity::ticker(); - assert(&mticker == &tic); - tic.updateFrom(this->PeakWidthModelOwner::ticker()); - tic.updateFrom(this->ScatteringFactorTableOwner::ticker()); - const PeakProfilePtr& pkpf = this->getPeakProfile(); - if (pkpf) tic.updateFrom(pkpf->ticker()); - return tic; +eventticker::EventTicker& PDFCalculator::ticker() const { + eventticker::EventTicker& tic = this->PairQuantity::ticker(); + assert(&mticker == &tic); + tic.updateFrom(this->PeakWidthModelOwner::ticker()); + tic.updateFrom(this->ScatteringFactorTableOwner::ticker()); + const PeakProfilePtr& pkpf = this->getPeakProfile(); + if (pkpf) tic.updateFrom(pkpf->ticker()); + return tic; } // results -QuantityType PDFCalculator::getPDF() const -{ - QuantityType pdf = this->getExtendedPDF(); - this->cutRipplePoints(pdf); - return pdf; -} - - -QuantityType PDFCalculator::getRDF() const -{ - QuantityType rdf = this->getExtendedRDF(); - this->cutRipplePoints(rdf); - return rdf; -} - - -QuantityType PDFCalculator::getRDFperR() const -{ - QuantityType rdfperr = this->getExtendedRDFperR(); - this->cutRipplePoints(rdfperr); - return rdfperr; -} - - -QuantityType PDFCalculator::getF() const -{ - QuantityType f_ext = this->getExtendedF(); - assert(pdfutils_qmaxSteps(this) <= int(f_ext.size())); - QuantityType rv(f_ext.begin(), f_ext.begin() + pdfutils_qmaxSteps(this)); - return rv; +QuantityType PDFCalculator::getPDF() const { + QuantityType pdf = this->getExtendedPDF(); + this->cutRipplePoints(pdf); + return pdf; } - -QuantityType PDFCalculator::getExtendedPDF() const -{ - QuantityType rgrid_ext = this->getExtendedRgrid(); - // Skip FFT when qmax is not specified and qmin does not exclude the - // the F(Q=Qstep) point (excluding F(0) == 0 makes no difference to G). - const bool skipfft = - !eps_lt(this->getQmax(), M_PI / this->getRstep()) && - !(1 < pdfutils_qminSteps(this)); - if (skipfft) - { - QuantityType rdfpr = this->getExtendedRDFperR(); - QuantityType rdfprb = this->applyBaseline(rgrid_ext, rdfpr); - QuantityType pdf = this->applyEnvelopes(rgrid_ext, rdfprb); - return pdf; - } - // FFT required here - // we need a full range PDF to apply termination ripples correctly - QuantityType f_ext = this->getExtendedF(); - // zero all F points at Q < Qmin - QuantityType::iterator ii_qmin = - f_ext.begin() + min(pdfutils_qminSteps(this), int(f_ext.size())); - fill(f_ext.begin(), ii_qmin, 0.0); - // zero all F points at Q >= Qmax - assert(pdfutils_qmaxSteps(this) <= int(f_ext.size())); - QuantityType::iterator ii_qmax = f_ext.begin() + pdfutils_qmaxSteps(this); - fill(ii_qmax, f_ext.end(), 0.0); - QuantityType pdf1 = fftftog(f_ext, this->getQstep()); - // cut away the FFT padded points - assert(this->extendedRmaxSteps() <= int(pdf1.size())); - pdf1.erase(pdf1.begin() + this->extendedRmaxSteps(), pdf1.end()); - pdf1.erase(pdf1.begin(), pdf1.begin() + this->extendedRminSteps()); - QuantityType pdf2 = this->applyEnvelopes(rgrid_ext, pdf1); - return pdf2; -} - - -QuantityType PDFCalculator::getExtendedRDF() const -{ - QuantityType rdf(this->countExtendedPoints()); - const double& totocc = mstructure_cache.totaloccupancy; - double sfavg = this->sfAverage(); - double rdf_scale = (totocc * sfavg == 0.0) ? 0.0 : - 1.0 / (totocc * sfavg * sfavg); - QuantityType::iterator iirdf = rdf.begin(); - QuantityType::const_iterator iival, iival_last; - iival = this->value().begin() + - this->extendedRminSteps() - this->rcalcloSteps(); - iival_last = this->value().begin() + - this->extendedRmaxSteps() - this->rcalcloSteps(); - assert(iival >= this->value().begin()); - assert(iival_last <= this->value().end()); - assert(rdf.size() == size_t(iival_last - iival)); - for (; iirdf != rdf.end(); ++iival, ++iirdf) - { - *iirdf = *iival * rdf_scale; - } - return rdf; +QuantityType PDFCalculator::getRDF() const { + QuantityType rdf = this->getExtendedRDF(); + this->cutRipplePoints(rdf); + return rdf; } - -QuantityType PDFCalculator::getExtendedRDFperR() const -{ - QuantityType rdf_ext = this->getExtendedRDF(); - QuantityType rgrid_ext = this->getExtendedRgrid(); - assert(rdf_ext.size() == rgrid_ext.size()); - QuantityType::const_iterator ri = rgrid_ext.begin(); - QuantityType::iterator rdfi = rdf_ext.begin(); - for (; ri != rgrid_ext.end(); ++ri, ++rdfi) - { - *rdfi = eps_gt(*ri, 0) ? (*rdfi / *ri) : 0.0; - } - return rdf_ext; +QuantityType PDFCalculator::getRDFperR() const { + QuantityType rdfperr = this->getExtendedRDFperR(); + this->cutRipplePoints(rdfperr); + return rdfperr; } - -QuantityType PDFCalculator::getExtendedF() const -{ - QuantityType rdfperr_ext = this->getExtendedRDFperR(); - QuantityType rgrid_ext = this->getExtendedRgrid(); - QuantityType rdfperr_ext1 = this->applyBaseline(rgrid_ext, rdfperr_ext); - const double rmin_ext = this->getExtendedRmin(); - QuantityType rv = fftgtof(rdfperr_ext1, this->getRstep(), rmin_ext); - assert(rv.empty() || eps_eq(M_PI, - this->getQstep() * rv.size() * this->getRstep())); - return rv; +QuantityType PDFCalculator::getF() const { + QuantityType f_ext = this->getExtendedF(); + assert(pdfutils_qmaxSteps(this) <= int(f_ext.size())); + QuantityType rv(f_ext.begin(), f_ext.begin() + pdfutils_qmaxSteps(this)); + return rv; } - -QuantityType PDFCalculator::getExtendedRgrid() const -{ - QuantityType rv; - rv.reserve(this->countExtendedPoints()); - // make sure exact value of rmin will be in the extended grid - for (int i = this->extendedRminSteps(); i < this->extendedRmaxSteps(); ++i) - { - rv.push_back(i * this->getRstep()); - } - assert(rv.empty() || !eps_lt(rv.front(), this->getExtendedRmin())); - assert(rv.empty() || !eps_gt(rv.back(), this->getExtendedRmax())); - return rv; +QuantityType PDFCalculator::getExtendedPDF() const { + QuantityType rgrid_ext = this->getExtendedRgrid(); + // Skip FFT when qmax is not specified and qmin does not exclude the + // the F(Q=Qstep) point (excluding F(0) == 0 makes no difference to G). + const bool skipfft = !eps_lt(this->getQmax(), M_PI / this->getRstep()) && + !(1 < pdfutils_qminSteps(this)); + if (skipfft) { + QuantityType rdfpr = this->getExtendedRDFperR(); + QuantityType rdfprb = this->applyBaseline(rgrid_ext, rdfpr); + QuantityType pdf = this->applyEnvelopes(rgrid_ext, rdfprb); + return pdf; + } + // FFT required here + // we need a full range PDF to apply termination ripples correctly + QuantityType f_ext = this->getExtendedF(); + // zero all F points at Q < Qmin + QuantityType::iterator ii_qmin = + f_ext.begin() + min(pdfutils_qminSteps(this), int(f_ext.size())); + fill(f_ext.begin(), ii_qmin, 0.0); + // zero all F points at Q >= Qmax + assert(pdfutils_qmaxSteps(this) <= int(f_ext.size())); + QuantityType::iterator ii_qmax = f_ext.begin() + pdfutils_qmaxSteps(this); + fill(ii_qmax, f_ext.end(), 0.0); + QuantityType pdf1 = fftftog(f_ext, this->getQstep()); + // cut away the FFT padded points + assert(this->extendedRmaxSteps() <= int(pdf1.size())); + pdf1.erase(pdf1.begin() + this->extendedRmaxSteps(), pdf1.end()); + pdf1.erase(pdf1.begin(), pdf1.begin() + this->extendedRminSteps()); + QuantityType pdf2 = this->applyEnvelopes(rgrid_ext, pdf1); + return pdf2; +} + +QuantityType PDFCalculator::getExtendedRDF() const { + QuantityType rdf(this->countExtendedPoints()); + const double& totocc = mstructure_cache.totaloccupancy; + double sfavg = this->sfAverage(); + double rdf_scale = + (totocc * sfavg == 0.0) ? 0.0 : 1.0 / (totocc * sfavg * sfavg); + QuantityType::iterator iirdf = rdf.begin(); + QuantityType::const_iterator iival, iival_last; + iival = + this->value().begin() + this->extendedRminSteps() - this->rcalcloSteps(); + iival_last = + this->value().begin() + this->extendedRmaxSteps() - this->rcalcloSteps(); + assert(iival >= this->value().begin()); + assert(iival_last <= this->value().end()); + assert(rdf.size() == size_t(iival_last - iival)); + for (; iirdf != rdf.end(); ++iival, ++iirdf) { + *iirdf = *iival * rdf_scale; + } + return rdf; +} + +QuantityType PDFCalculator::getExtendedRDFperR() const { + QuantityType rdf_ext = this->getExtendedRDF(); + QuantityType rgrid_ext = this->getExtendedRgrid(); + assert(rdf_ext.size() == rgrid_ext.size()); + QuantityType::const_iterator ri = rgrid_ext.begin(); + QuantityType::iterator rdfi = rdf_ext.begin(); + for (; ri != rgrid_ext.end(); ++ri, ++rdfi) { + *rdfi = eps_gt(*ri, 0) ? (*rdfi / *ri) : 0.0; + } + return rdf_ext; +} + +QuantityType PDFCalculator::getExtendedF() const { + QuantityType rdfperr_ext = this->getExtendedRDFperR(); + QuantityType rgrid_ext = this->getExtendedRgrid(); + QuantityType rdfperr_ext1 = this->applyBaseline(rgrid_ext, rdfperr_ext); + const double rmin_ext = this->getExtendedRmin(); + QuantityType rv = fftgtof(rdfperr_ext1, this->getRstep(), rmin_ext); + assert(rv.empty() || + eps_eq(M_PI, this->getQstep() * rv.size() * this->getRstep())); + return rv; +} + +QuantityType PDFCalculator::getExtendedRgrid() const { + QuantityType rv; + rv.reserve(this->countExtendedPoints()); + // make sure exact value of rmin will be in the extended grid + for (int i = this->extendedRminSteps(); i < this->extendedRmaxSteps(); ++i) { + rv.push_back(i * this->getRstep()); + } + assert(rv.empty() || !eps_lt(rv.front(), this->getExtendedRmin())); + assert(rv.empty() || !eps_gt(rv.back(), this->getExtendedRmax())); + return rv; } // Q-range methods -QuantityType PDFCalculator::getQgrid() const -{ - return pdfutils_getQgrid(this); -} +QuantityType PDFCalculator::getQgrid() const { return pdfutils_getQgrid(this); } // Q-range configuration -void PDFCalculator::setQmin(double qmin) -{ - ensureNonNegative("Qmin", qmin); - mqmin = qmin; +void PDFCalculator::setQmin(double qmin) { + ensureNonNegative("Qmin", qmin); + mqmin = qmin; } +const double& PDFCalculator::getQmin() const { return mqmin; } -const double& PDFCalculator::getQmin() const -{ - return mqmin; +void PDFCalculator::setQmax(double qmax) { + ensureNonNegative("Qmax", qmax); + double qmax1 = (qmax > 0.0) ? qmax : DOUBLE_MAX; + if (qmax1 < mqmax) mticker.click(); + mqmax = qmax1; + if (_initialQstepUpdate(this)) this->resetValue(); } - -void PDFCalculator::setQmax(double qmax) -{ - ensureNonNegative("Qmax", qmax); - double qmax1 = (qmax > 0.0) ? qmax : DOUBLE_MAX; - if (qmax1 < mqmax) mticker.click(); - mqmax = qmax1; - if (_initialQstepUpdate(this)) this->resetValue(); +const double& PDFCalculator::getQmax() const { + static double rv; + rv = min(mqmax, M_PI / this->getRstep()); + return rv; } - -const double& PDFCalculator::getQmax() const -{ - static double rv; - rv = min(mqmax, M_PI / this->getRstep()); - return rv; -} - - -const double& PDFCalculator::getQstep() const -{ - static double rv; - // replicate the zero padding as done in fftgtof - int Npad1 = this->extendedRmaxSteps(); - int Npad2 = (Npad1 > 0) ? (1 << int(ceil(log2(Npad1)))) : 0; - rv = (Npad2 > 0) ? M_PI / (Npad2 * this->getRstep()) : 0.0; - return rv; +const double& PDFCalculator::getQstep() const { + static double rv; + // replicate the zero padding as done in fftgtof + int Npad1 = this->extendedRmaxSteps(); + int Npad2 = (Npad1 > 0) ? (1 << int(ceil(log2(Npad1)))) : 0; + rv = (Npad2 > 0) ? M_PI / (Npad2 * this->getRstep()) : 0.0; + return rv; } // R-range methods -QuantityType PDFCalculator::getRgrid() const -{ - return pdfutils_getRgrid(this); -} +QuantityType PDFCalculator::getRgrid() const { return pdfutils_getRgrid(this); } // R-range configuration -void PDFCalculator::setRmin(double rmin) -{ - ensureNonNegative("Rmin", rmin); - this->PairQuantity::setRmin(rmin); -} - - -void PDFCalculator::setRmax(double rmax) -{ - ensureNonNegative("Rmax", rmax); - this->PairQuantity::setRmax(rmax); - if (_initialQstepUpdate(this)) this->resetValue(); -} - - -void PDFCalculator::setRstep(double rstep) -{ - ensureEpsilonPositive("Rstep", rstep); - if (mrstep != rstep) mticker.click(); - mrstep = rstep; - if (_initialQstepUpdate(this)) this->resetValue(); +void PDFCalculator::setRmin(double rmin) { + ensureNonNegative("Rmin", rmin); + this->PairQuantity::setRmin(rmin); } - -const double& PDFCalculator::getRstep() const -{ - return mrstep; +void PDFCalculator::setRmax(double rmax) { + ensureNonNegative("Rmax", rmax); + this->PairQuantity::setRmax(rmax); + if (_initialQstepUpdate(this)) this->resetValue(); } - -void PDFCalculator::setMaxExtension(double maxextension) -{ - ensureNonNegative("maxextension", maxextension); - if (mmaxextension != maxextension) mticker.click(); - mmaxextension = maxextension; +void PDFCalculator::setRstep(double rstep) { + ensureEpsilonPositive("Rstep", rstep); + if (mrstep != rstep) mticker.click(); + mrstep = rstep; + if (_initialQstepUpdate(this)) this->resetValue(); } +const double& PDFCalculator::getRstep() const { return mrstep; } -const double& PDFCalculator::getMaxExtension() const -{ - return mmaxextension; +void PDFCalculator::setMaxExtension(double maxextension) { + ensureNonNegative("maxextension", maxextension); + if (mmaxextension != maxextension) mticker.click(); + mmaxextension = maxextension; } +const double& PDFCalculator::getMaxExtension() const { return mmaxextension; } -double PDFCalculator::getExtendedRmin() const -{ - double rv = this->extendedRminSteps() * this->getRstep(); - return rv; +double PDFCalculator::getExtendedRmin() const { + double rv = this->extendedRminSteps() * this->getRstep(); + return rv; } - -double PDFCalculator::getExtendedRmax() const -{ - double rv = this->extendedRmaxSteps() * this->getRstep(); - return rv; +double PDFCalculator::getExtendedRmax() const { + double rv = this->extendedRmaxSteps() * this->getRstep(); + return rv; } // PDF peak profile configuration -void PDFCalculator::setPeakProfile(PeakProfilePtr pkf) -{ - ensureNonNull("PeakProfile", pkf); - if (mpeakprofile != pkf) mticker.click(); - mpeakprofile = pkf; +void PDFCalculator::setPeakProfile(PeakProfilePtr pkf) { + ensureNonNull("PeakProfile", pkf); + if (mpeakprofile != pkf) mticker.click(); + mpeakprofile = pkf; } - -void PDFCalculator::setPeakProfileByType(const string& tp) -{ - PeakProfilePtr pkf = PeakProfile::createByType(tp); - if (mpeakprofile.get()) - { - pkf->setPrecision(mpeakprofile->getPrecision()); - } - this->setPeakProfile(pkf); +void PDFCalculator::setPeakProfileByType(const string& tp) { + PeakProfilePtr pkf = PeakProfile::createByType(tp); + if (mpeakprofile.get()) { + pkf->setPrecision(mpeakprofile->getPrecision()); + } + this->setPeakProfile(pkf); } - -PeakProfilePtr& PDFCalculator::getPeakProfile() -{ - assert(mpeakprofile.get()); - return mpeakprofile; +PeakProfilePtr& PDFCalculator::getPeakProfile() { + assert(mpeakprofile.get()); + return mpeakprofile; } - -const PeakProfilePtr& PDFCalculator::getPeakProfile() const -{ - assert(mpeakprofile.get()); - return mpeakprofile; +const PeakProfilePtr& PDFCalculator::getPeakProfile() const { + assert(mpeakprofile.get()); + return mpeakprofile; } // PDF baseline methods -QuantityType PDFCalculator::applyBaseline( - const QuantityType& x, const QuantityType& y) const -{ - assert(x.size() == y.size()); - QuantityType z = y; - const PDFBaseline& baseline = *(this->getBaseline()); - QuantityType::const_iterator xi = x.begin(); - QuantityType::iterator zi = z.begin(); - for (; xi != x.end(); ++xi, ++zi) - { - *zi += baseline(*xi); - } - return z; +QuantityType PDFCalculator::applyBaseline(const QuantityType& x, + const QuantityType& y) const { + assert(x.size() == y.size()); + QuantityType z = y; + const PDFBaseline& baseline = *(this->getBaseline()); + QuantityType::const_iterator xi = x.begin(); + QuantityType::iterator zi = z.begin(); + for (; xi != x.end(); ++xi, ++zi) { + *zi += baseline(*xi); + } + return z; } - -void PDFCalculator::setBaseline(PDFBaselinePtr baseline) -{ - ensureNonNull("PDFBaseline", baseline); - mbaseline = baseline; +void PDFCalculator::setBaseline(PDFBaselinePtr baseline) { + ensureNonNull("PDFBaseline", baseline); + mbaseline = baseline; } - -void PDFCalculator::setBaselineByType(const std::string& tp) -{ - mbaseline = PDFBaseline::createByType(tp); +void PDFCalculator::setBaselineByType(const std::string& tp) { + mbaseline = PDFBaseline::createByType(tp); } - -PDFBaselinePtr& PDFCalculator::getBaseline() -{ - assert(mbaseline.get()); - return mbaseline; +PDFBaselinePtr& PDFCalculator::getBaseline() { + assert(mbaseline.get()); + return mbaseline; } - -const PDFBaselinePtr& PDFCalculator::getBaseline() const -{ - assert(mbaseline.get()); - return mbaseline; +const PDFBaselinePtr& PDFCalculator::getBaseline() const { + assert(mbaseline.get()); + return mbaseline; } // Protected Methods --------------------------------------------------------- @@ -451,310 +371,257 @@ const PDFBaselinePtr& PDFCalculator::getBaseline() const namespace { template -void pdfcalc_accept(T* obj, diffpy::BaseAttributesVisitor& v) -{ - obj->getPeakWidthModel()->accept(v); - obj->getPeakProfile()->accept(v); - obj->getBaseline()->accept(v); - // PDF envelopes - set evnames = obj->usedEnvelopeTypes(); - set::const_iterator nm = evnames.begin(); - for (; nm != evnames.end(); ++nm) - { - obj->getEnvelopeByType(*nm)->accept(v); - } - // finally call standard accept - obj->diffpy::Attributes::accept(v); +void pdfcalc_accept(T* obj, diffpy::BaseAttributesVisitor& v) { + obj->getPeakWidthModel()->accept(v); + obj->getPeakProfile()->accept(v); + obj->getBaseline()->accept(v); + // PDF envelopes + set evnames = obj->usedEnvelopeTypes(); + set::const_iterator nm = evnames.begin(); + for (; nm != evnames.end(); ++nm) { + obj->getEnvelopeByType(*nm)->accept(v); + } + // finally call standard accept + obj->diffpy::Attributes::accept(v); } -} // namespace +} // namespace - -void PDFCalculator::accept(diffpy::BaseAttributesVisitor& v) -{ - pdfcalc_accept(this, v); +void PDFCalculator::accept(diffpy::BaseAttributesVisitor& v) { + pdfcalc_accept(this, v); } - -void PDFCalculator::accept(diffpy::BaseAttributesVisitor& v) const -{ - pdfcalc_accept(this, v); +void PDFCalculator::accept(diffpy::BaseAttributesVisitor& v) const { + pdfcalc_accept(this, v); } // PairQuantity overloads -void PDFCalculator::resetValue() -{ - // calcPoints requires that structure and rlimits data are cached. - this->cacheStructureData(); - this->cacheRlimitsData(); - // when applicable, configure linear baseline - if (this->getBaseline()->type() == "linear") - { - double partialpdfscale = - (0.0 == mstructure_cache.totaloccupancy) ? 0.0 : - mstructure_cache.activeoccupancy / mstructure_cache.totaloccupancy; - double pnumdensity = partialpdfscale * mstructure->numberDensity(); - PDFBaseline& bl = *(this->getBaseline()); - bl.setDoubleAttr("slope", -4 * M_PI * pnumdensity); - } - this->resizeValue(this->countCalcPoints()); - this->PairQuantity::resetValue(); -} - - -void PDFCalculator::configureBondGenerator(BaseBondGenerator& bnds) const -{ - bnds.setRmin(this->rcalclo()); - bnds.setRmax(this->rcalchi()); +void PDFCalculator::resetValue() { + // calcPoints requires that structure and rlimits data are cached. + this->cacheStructureData(); + this->cacheRlimitsData(); + // when applicable, configure linear baseline + if (this->getBaseline()->type() == "linear") { + double partialpdfscale = + (0.0 == mstructure_cache.totaloccupancy) + ? 0.0 + : mstructure_cache.activeoccupancy / mstructure_cache.totaloccupancy; + double pnumdensity = partialpdfscale * mstructure->numberDensity(); + PDFBaseline& bl = *(this->getBaseline()); + bl.setDoubleAttr("slope", -4 * M_PI * pnumdensity); + } + this->resizeValue(this->countCalcPoints()); + this->PairQuantity::resetValue(); +} + +void PDFCalculator::configureBondGenerator(BaseBondGenerator& bnds) const { + bnds.setRmin(this->rcalclo()); + bnds.setRmax(this->rcalchi()); } - void PDFCalculator::addPairContribution(const BaseBondGenerator& bnds, - int summationscale) -{ - double sfprod = this->sfSite(bnds.site0()) * this->sfSite(bnds.site1()); - double peakscale = sfprod * bnds.multiplicity() * summationscale; - double fwhm = this->getPeakWidthModel()->calculate(bnds); - const PeakProfile& pkf = *(this->getPeakProfile()); - double dist = bnds.distance(); - double xlo = dist + pkf.xboundlo(fwhm); - double xhi = dist + pkf.xboundhi(fwhm); - int i = max(0, this->calcIndex(xlo)); - int ilast = min(this->countCalcPoints(), this->calcIndex(xhi) + 1); - assert(ilast <= int(mvalue.size())); - assert(eps_gt(dist, 0.0)); - for (; i < ilast; ++i) - { - double x = (this->rcalcloSteps() + i) * this->getRstep() - dist; - double y = pkf(x, fwhm); - // Contributions in G(r) need to be normalized by pair distance, - // not by r as done in PDFfit or PDFfit2. Here we rescale RDF - // in such way that division by r will give a correct result. - double yrdf = y * (x / dist + 1); - mvalue[i] += peakscale * yrdf; - } -} - - -void PDFCalculator::stashPartialValue() -{ - mstashedvalue.value = this->value(); - mstashedvalue.rclosteps = this->rcalcloSteps(); -} - - -void PDFCalculator::restorePartialValue() -{ - assert(!mstashedvalue.value.empty()); - assert(!mvalue.empty()); - QuantityType::const_iterator si = mstashedvalue.value.begin(); - QuantityType::const_iterator slast = mstashedvalue.value.end(); - QuantityType::iterator ti = mvalue.begin(); - QuantityType::iterator tlast = mvalue.end(); - int leftshift = this->rcalcloSteps() - mstashedvalue.rclosteps; - int sz = mstashedvalue.value.size(); - if (leftshift >= 0) si += min(leftshift, sz); - else ti += min(-leftshift, int(mvalue.size())); - for (; si != slast && ti != tlast; ++si, ++ti) *ti = *si; - mstashedvalue.value.clear(); + int summationscale) { + double sfprod = this->sfSite(bnds.site0()) * this->sfSite(bnds.site1()); + double peakscale = sfprod * bnds.multiplicity() * summationscale; + double fwhm = this->getPeakWidthModel()->calculate(bnds); + const PeakProfile& pkf = *(this->getPeakProfile()); + double dist = bnds.distance(); + double xlo = dist + pkf.xboundlo(fwhm); + double xhi = dist + pkf.xboundhi(fwhm); + int i = max(0, this->calcIndex(xlo)); + int ilast = min(this->countCalcPoints(), this->calcIndex(xhi) + 1); + assert(ilast <= int(mvalue.size())); + assert(eps_gt(dist, 0.0)); + for (; i < ilast; ++i) { + double x = (this->rcalcloSteps() + i) * this->getRstep() - dist; + double y = pkf(x, fwhm); + // Contributions in G(r) need to be normalized by pair distance, + // not by r as done in PDFfit or PDFfit2. Here we rescale RDF + // in such way that division by r will give a correct result. + double yrdf = y * (x / dist + 1); + mvalue[i] += peakscale * yrdf; + } +} + +void PDFCalculator::stashPartialValue() { + mstashedvalue.value = this->value(); + mstashedvalue.rclosteps = this->rcalcloSteps(); +} + +void PDFCalculator::restorePartialValue() { + assert(!mstashedvalue.value.empty()); + assert(!mvalue.empty()); + QuantityType::const_iterator si = mstashedvalue.value.begin(); + QuantityType::const_iterator slast = mstashedvalue.value.end(); + QuantityType::iterator ti = mvalue.begin(); + QuantityType::iterator tlast = mvalue.end(); + int leftshift = this->rcalcloSteps() - mstashedvalue.rclosteps; + int sz = mstashedvalue.value.size(); + if (leftshift >= 0) + si += min(leftshift, sz); + else + ti += min(-leftshift, int(mvalue.size())); + for (; si != slast && ti != tlast; ++si, ++ti) *ti = *si; + mstashedvalue.value.clear(); } // calculation specific -double PDFCalculator::rcalclo() const -{ - double rv = this->rcalcloSteps() * this->getRstep(); - return rv; +double PDFCalculator::rcalclo() const { + double rv = this->rcalcloSteps() * this->getRstep(); + return rv; } - -double PDFCalculator::rcalchi() const -{ - double rv = this->rcalchiSteps() * this->getRstep(); - return rv; +double PDFCalculator::rcalchi() const { + double rv = this->rcalchiSteps() * this->getRstep(); + return rv; } - -double PDFCalculator::extFromTerminationRipples() const -{ - // number of termination ripples for extending the r-range - const int nripples = 6; - // extension due to termination ripples. - // apply only when qmax is below the Nyquist frequency for rstep. - const double& qmax = this->getQmax(); - const double& dr = this->getRstep(); - double rv = (eps_gt(qmax, 0.0) && eps_lt(qmax, M_PI / dr)) ? - (nripples * 2 * M_PI / qmax) : 0.0; - return rv; +double PDFCalculator::extFromTerminationRipples() const { + // number of termination ripples for extending the r-range + const int nripples = 6; + // extension due to termination ripples. + // apply only when qmax is below the Nyquist frequency for rstep. + const double& qmax = this->getQmax(); + const double& dr = this->getRstep(); + double rv = (eps_gt(qmax, 0.0) && eps_lt(qmax, M_PI / dr)) + ? (nripples * 2 * M_PI / qmax) + : 0.0; + return rv; } - -double PDFCalculator::extFromPeakTails() const -{ - // assume uncorrelated neighbors with maxUii - const PeakWidthModel& pwm = *(this->getPeakWidthModel()); - double maxfwhm = pwm.maxWidth( - mstructure, this->getRmin(), this->getRmax()); - const PeakProfile& pkf = *(this->getPeakProfile()); - double xleft = fabs(pkf.xboundlo(maxfwhm)); - double xright = fabs(pkf.xboundhi(maxfwhm)); - double rv = max(xleft, xright); - return rv; +double PDFCalculator::extFromPeakTails() const { + // assume uncorrelated neighbors with maxUii + const PeakWidthModel& pwm = *(this->getPeakWidthModel()); + double maxfwhm = pwm.maxWidth(mstructure, this->getRmin(), this->getRmax()); + const PeakProfile& pkf = *(this->getPeakProfile()); + double xleft = fabs(pkf.xboundlo(maxfwhm)); + double xright = fabs(pkf.xboundhi(maxfwhm)); + double rv = max(xleft, xright); + return rv; } +int PDFCalculator::rcalcloSteps() const { return mrlimits_cache.rcalclosteps; } -int PDFCalculator::rcalcloSteps() const -{ - return mrlimits_cache.rcalclosteps; -} +int PDFCalculator::rcalchiSteps() const { return mrlimits_cache.rcalchisteps; } - -int PDFCalculator::rcalchiSteps() const -{ - return mrlimits_cache.rcalchisteps; +int PDFCalculator::extendedRminSteps() const { + return mrlimits_cache.extendedrminsteps; } - -int PDFCalculator::extendedRminSteps() const -{ - return mrlimits_cache.extendedrminsteps; +int PDFCalculator::extendedRmaxSteps() const { + return mrlimits_cache.extendedrmaxsteps; } - -int PDFCalculator::extendedRmaxSteps() const -{ - return mrlimits_cache.extendedrmaxsteps; +int PDFCalculator::countExtendedPoints() const { + int rv = this->extendedRmaxSteps() - this->extendedRminSteps(); + assert(rv >= 0); + return rv; } - -int PDFCalculator::countExtendedPoints() const -{ - int rv = this->extendedRmaxSteps() - this->extendedRminSteps(); - assert(rv >= 0); - return rv; +int PDFCalculator::countCalcPoints() const { + int rv = this->rcalchiSteps() - this->rcalcloSteps(); + assert(rv >= 0); + return rv; } - -int PDFCalculator::countCalcPoints() const -{ - int rv = this->rcalchiSteps() - this->rcalcloSteps(); - assert(rv >= 0); - return rv; +int PDFCalculator::calcIndex(double r) const { + int rv = int(floor(r / this->getRstep())) - this->rcalcloSteps(); + return rv; } - -int PDFCalculator::calcIndex(double r) const -{ - int rv = int(floor(r / this->getRstep())) - this->rcalcloSteps(); - return rv; +void PDFCalculator::cutRipplePoints(QuantityType& y) const { + if (y.empty()) return; + assert(int(y.size()) == + this->extendedRmaxSteps() - this->extendedRminSteps()); + int ncutlo = pdfutils_rminSteps(this) - this->extendedRminSteps(); + int ncuthi = this->extendedRmaxSteps() - pdfutils_rmaxSteps(this); + assert(ncutlo + ncuthi <= int(y.size())); + y.erase(y.end() - ncuthi, y.end()); + y.erase(y.begin(), y.begin() + ncutlo); } - -void PDFCalculator::cutRipplePoints(QuantityType& y) const -{ - if (y.empty()) return; - assert(int(y.size()) == - this->extendedRmaxSteps() - this->extendedRminSteps()); - int ncutlo = pdfutils_rminSteps(this) - this->extendedRminSteps(); - int ncuthi = this->extendedRmaxSteps() - pdfutils_rmaxSteps(this); - assert(ncutlo + ncuthi <= int(y.size())); - y.erase(y.end() - ncuthi, y.end()); - y.erase(y.begin(), y.begin() + ncutlo); +const double& PDFCalculator::sfSite(int siteidx) const { + assert(0 <= siteidx && siteidx < int(mstructure_cache.sfsite.size())); + return mstructure_cache.sfsite[siteidx]; } +double PDFCalculator::sfAverage() const { return mstructure_cache.sfaverage; } -const double& PDFCalculator::sfSite(int siteidx) const -{ - assert(0 <= siteidx && siteidx < int(mstructure_cache.sfsite.size())); - return mstructure_cache.sfsite[siteidx]; -} - - -double PDFCalculator::sfAverage() const -{ - return mstructure_cache.sfaverage; -} - - -void PDFCalculator::cacheStructureData() -{ - int cntsites = this->countSites(); - // sfsite - unordered_map fcache; - mstructure_cache.sfsite.resize(cntsites); - const ScatteringFactorTablePtr sftable = this->getScatteringFactorTable(); - for (int i = 0; i < cntsites; ++i) - { - const string& smbl = mstructure->siteAtomType(i); - unordered_map::iterator ff = fcache.find(smbl); - if (ff == fcache.end()) - { - const double value = sftable->lookup(smbl); - ff = fcache.insert(make_pair(smbl, value)).first; - } - mstructure_cache.sfsite[i] = ff->second * mstructure->siteOccupancy(i); - } - // sfaverage - double totocc = mstructure->totalOccupancy(); - double totsf = 0.0; - for (int i = 0; i < cntsites; ++i) - { - totsf += this->sfSite(i) * mstructure->siteMultiplicity(i); - } - mstructure_cache.sfaverage = (totocc == 0.0) ? 0.0 : (totsf / totocc); - // totaloccupancy - mstructure_cache.totaloccupancy = totocc; - // active occupancy - double invmasktotal = 0.0; - for (auto&& ij : minvertpairmask) - { - const int& i = ij.first; - const int& j = ij.second; - bool outofbounds = (i < 0 || i >= cntsites || j < 0 || j >= cntsites); - if (outofbounds) continue; - int sumscale = (i == j) ? 1 : 2; - double occij = sumscale * - mstructure->siteOccupancy(i) * mstructure->siteMultiplicity(i) * - mstructure->siteOccupancy(j) * mstructure->siteMultiplicity(j); - invmasktotal += occij; - } - if (totocc > 0.0) invmasktotal /= totocc; - mstructure_cache.activeoccupancy = (mdefaultpairmask) ? - (totocc - invmasktotal) : invmasktotal; -} - - -void PDFCalculator::cacheRlimitsData() -{ - mrlimits_cache.extendedrminsteps = 0; - mrlimits_cache.extendedrmaxsteps = 0; - mrlimits_cache.rcalclosteps = 0; - mrlimits_cache.rcalchisteps = 0; - if (pdfutils_rminSteps(this) >= pdfutils_rmaxSteps(this)) return; - // obtain extension magnitudes and rescale to fit maximum extension - double ext_ripples = this->extFromTerminationRipples(); - double ext_pktails = this->extFromPeakTails(); - double ext_total = ext_ripples + ext_pktails; - if (ext_total > this->getMaxExtension()) - { - double sc = this->getMaxExtension() / ext_total; - ext_ripples *= sc; - ext_pktails *= sc; - ext_total = this->getMaxExtension(); +void PDFCalculator::cacheStructureData() { + int cntsites = this->countSites(); + // sfsite + unordered_map fcache; + mstructure_cache.sfsite.resize(cntsites); + const ScatteringFactorTablePtr sftable = this->getScatteringFactorTable(); + for (int i = 0; i < cntsites; ++i) { + const string& smbl = mstructure->siteAtomType(i); + unordered_map::iterator ff = fcache.find(smbl); + if (ff == fcache.end()) { + const double value = sftable->lookup(smbl); + ff = fcache.insert(make_pair(smbl, value)).first; } - // abbreviations - const double& rmin = this->getRmin(); - const double& rmax = this->getRmax(); - const double& dr = this->getRstep(); - mrlimits_cache.extendedrminsteps = max(0, pdfutils_rminSteps(rmin - ext_ripples, dr)); - mrlimits_cache.extendedrmaxsteps = pdfutils_rmaxSteps(rmax + ext_ripples, dr); - mrlimits_cache.rcalclosteps = max(0, pdfutils_rminSteps(rmin - ext_total, dr)); - mrlimits_cache.rcalchisteps = pdfutils_rmaxSteps(rmax + ext_total, dr); -} - -} // namespace diffpy -} // namespace srreal + mstructure_cache.sfsite[i] = ff->second * mstructure->siteOccupancy(i); + } + // sfaverage + double totocc = mstructure->totalOccupancy(); + double totsf = 0.0; + for (int i = 0; i < cntsites; ++i) { + totsf += this->sfSite(i) * mstructure->siteMultiplicity(i); + } + mstructure_cache.sfaverage = (totocc == 0.0) ? 0.0 : (totsf / totocc); + // totaloccupancy + mstructure_cache.totaloccupancy = totocc; + // active occupancy + double invmasktotal = 0.0; + for (auto&& ij : minvertpairmask) { + const int& i = ij.first; + const int& j = ij.second; + bool outofbounds = (i < 0 || i >= cntsites || j < 0 || j >= cntsites); + if (outofbounds) continue; + int sumscale = (i == j) ? 1 : 2; + double occij = sumscale * mstructure->siteOccupancy(i) * + mstructure->siteMultiplicity(i) * + mstructure->siteOccupancy(j) * + mstructure->siteMultiplicity(j); + invmasktotal += occij; + } + if (totocc > 0.0) invmasktotal /= totocc; + mstructure_cache.activeoccupancy = + (mdefaultpairmask) ? (totocc - invmasktotal) : invmasktotal; +} + +void PDFCalculator::cacheRlimitsData() { + mrlimits_cache.extendedrminsteps = 0; + mrlimits_cache.extendedrmaxsteps = 0; + mrlimits_cache.rcalclosteps = 0; + mrlimits_cache.rcalchisteps = 0; + if (pdfutils_rminSteps(this) >= pdfutils_rmaxSteps(this)) return; + // obtain extension magnitudes and rescale to fit maximum extension + double ext_ripples = this->extFromTerminationRipples(); + double ext_pktails = this->extFromPeakTails(); + double ext_total = ext_ripples + ext_pktails; + if (ext_total > this->getMaxExtension()) { + double sc = this->getMaxExtension() / ext_total; + ext_ripples *= sc; + ext_pktails *= sc; + ext_total = this->getMaxExtension(); + } + // abbreviations + const double& rmin = this->getRmin(); + const double& rmax = this->getRmax(); + const double& dr = this->getRstep(); + mrlimits_cache.extendedrminsteps = + max(0, pdfutils_rminSteps(rmin - ext_ripples, dr)); + mrlimits_cache.extendedrmaxsteps = pdfutils_rmaxSteps(rmax + ext_ripples, dr); + mrlimits_cache.rcalclosteps = + max(0, pdfutils_rminSteps(rmin - ext_total, dr)); + mrlimits_cache.rcalchisteps = pdfutils_rmaxSteps(rmax + ext_total, dr); +} + +} // namespace srreal +} // namespace diffpy // Serialization ------------------------------------------------------------- diff --git a/src/diffpy/srreal/PDFCalculator.hpp b/src/diffpy/srreal/PDFCalculator.hpp index f93795e3..2f5c127a 100644 --- a/src/diffpy/srreal/PDFCalculator.hpp +++ b/src/diffpy/srreal/PDFCalculator.hpp @@ -1,20 +1,20 @@ /***************************************************************************** -* -* 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.txt for a list of people who contributed. -* See LICENSE_DANSE.txt for license information. -* -****************************************************************************** -* -* class PDFCalculator -- real space PDF calculator -* -*****************************************************************************/ + * + * 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.txt for a list of people who contributed. + * See LICENSE_DANSE.txt for license information. + * + ****************************************************************************** + * + * class PDFCalculator -- real space PDF calculator + * + *****************************************************************************/ #ifndef PDFCALCULATOR_HPP_INCLUDED #define PDFCALCULATOR_HPP_INCLUDED @@ -31,184 +31,179 @@ namespace diffpy { namespace srreal { -class DLL_EXPORT PDFCalculator : - public PairQuantity, - public PeakWidthModelOwner, - public ScatteringFactorTableOwner, - public PDFEnvelopeOwner -{ - public: - - // constructor - PDFCalculator(); - - // PairQuantity overloads - virtual eventticker::EventTicker& ticker() const; - - // results - QuantityType getPDF() const; - QuantityType getRDF() const; - QuantityType getRDFperR() const; - QuantityType getF() const; - - /// PDF on an r-range extended for termination ripples - QuantityType getExtendedPDF() const; - /// RDF on an r-range extended for termination ripples - QuantityType getExtendedRDF() const; - /// RDF divided by r on an r-range extended for termination ripples - QuantityType getExtendedRDFperR() const; - /// F(Q) on a zero-padded grid that reaches r-sampling Qmax = PI/dr - QuantityType getExtendedF() const; - /// r-grid extended for termination ripples - QuantityType getExtendedRgrid() const; - - // Q-range methods - QuantityType getQgrid() const; - // Q-range configuration - void setQmin(double); - const double& getQmin() const; - void setQmax(double); - const double& getQmax() const; - const double& getQstep() const; - - // R-range methods - QuantityType getRgrid() const; - // R-range configuration - virtual void setRmin(double); - virtual void setRmax(double); - void setRstep(double); - const double& getRstep() const; - /// maximum total extension of the r-range accounting for both - /// termination ripples and peak tails - void setMaxExtension(double); - /// maximum total extension of the r-range accounting for both - /// termination ripples and peak tails - const double& getMaxExtension() const; - /// lower bound for the r-range extended for termination ripples - double getExtendedRmin() const; - /// upper bound of the r-range extended for termination ripples - double getExtendedRmax() const; - - // PDF profile configuration - void setPeakProfile(PeakProfilePtr); - void setPeakProfileByType(const std::string& tp); - PeakProfilePtr& getPeakProfile(); - const PeakProfilePtr& getPeakProfile() const; - - // PDF baseline configuration - // application on an array - QuantityType applyBaseline(const QuantityType& x, const QuantityType& y) const; - void setBaseline(PDFBaselinePtr); - void setBaselineByType(const std::string& tp); - PDFBaselinePtr& getBaseline(); - const PDFBaselinePtr& getBaseline() const; - - protected: - - // Attributes overload to direct visitors around data structures - virtual void accept(diffpy::BaseAttributesVisitor& v); - virtual void accept(diffpy::BaseAttributesVisitor& v) const; - - // PairQuantity overloads - virtual void resetValue(); - virtual void configureBondGenerator(BaseBondGenerator&) const; - virtual void addPairContribution(const BaseBondGenerator&, int); - // support for PQEvaluatorOptimized - virtual void stashPartialValue(); - virtual void restorePartialValue(); - - private: - - // methods - calculation specific - /// complete lower bound extension of the calculated grid - double rcalclo() const; - /// complete upper bound extension of the calculated grid - double rcalchi() const; - /// r-range extension to allow propagation of termination ripples - double extFromTerminationRipples() const; - /// r-range extension to account for tails from out-of-range peaks - double extFromPeakTails() const; - /// number of dr steps at rcalclo from 0 - int rcalcloSteps() const; - /// number of dr steps at rhalchi from 0 - int rcalchiSteps() const; - /// number of dr steps at lower extension due to termination ripples - int extendedRminSteps() const; - /// number of dr steps at upper extension due to termination ripples - int extendedRmaxSteps() const; - /// number of points in the r-grid extended with termination ripples - int countExtendedPoints() const; - /// number of points in the complete calculated r-grid - int countCalcPoints() const; - /// index of a nearby point in the complete calculated r-grid - int calcIndex(double r) const; - /// reduce extended grid to user-requested results grid - /// by cutting away the points for termination ripples - void cutRipplePoints(QuantityType& y) const; - - // structure factors - fast lookup by site index - /// effective scattering factor at a given site scaled by occupancy - const double& sfSite(int) const; - /// average scattering factor - double sfAverage() const; - void cacheStructureData(); - void cacheRlimitsData(); - - // data - // configuration - double mqmin; - double mqmax; - double mrstep; - double mmaxextension; - PeakProfilePtr mpeakprofile; - PDFBaselinePtr mbaseline; - struct { - std::vector sfsite; - double sfaverage; - double totaloccupancy; - double activeoccupancy; - } mstructure_cache; - struct { - int extendedrminsteps; - int extendedrmaxsteps; - int rcalclosteps; - int rcalchisteps; - } mrlimits_cache; - // support for PQEvaluatorOptimized - struct { - QuantityType value; - int rclosteps; - } mstashedvalue; - // serialization - friend class boost::serialization::access; - template - void serialize(Archive& ar, const unsigned int version) - { - using boost::serialization::base_object; - ar & base_object(*this); - ar & base_object(*this); - ar & base_object(*this); - ar & base_object(*this); - ar & mqmin; - ar & mqmax; - ar & mrstep; - ar & mmaxextension; - ar & mpeakprofile; - ar & mbaseline; - ar & mstructure_cache.sfsite; - ar & mstructure_cache.sfaverage; - ar & mstructure_cache.totaloccupancy; - ar & mstructure_cache.activeoccupancy; - ar & mrlimits_cache.extendedrminsteps; - ar & mrlimits_cache.extendedrmaxsteps; - ar & mrlimits_cache.rcalclosteps; - ar & mrlimits_cache.rcalchisteps; - } +class DLL_EXPORT PDFCalculator : public PairQuantity, + public PeakWidthModelOwner, + public ScatteringFactorTableOwner, + public PDFEnvelopeOwner { + public: + // constructor + PDFCalculator(); + + // PairQuantity overloads + virtual eventticker::EventTicker& ticker() const; + + // results + QuantityType getPDF() const; + QuantityType getRDF() const; + QuantityType getRDFperR() const; + QuantityType getF() const; + + /// PDF on an r-range extended for termination ripples + QuantityType getExtendedPDF() const; + /// RDF on an r-range extended for termination ripples + QuantityType getExtendedRDF() const; + /// RDF divided by r on an r-range extended for termination ripples + QuantityType getExtendedRDFperR() const; + /// F(Q) on a zero-padded grid that reaches r-sampling Qmax = PI/dr + QuantityType getExtendedF() const; + /// r-grid extended for termination ripples + QuantityType getExtendedRgrid() const; + + // Q-range methods + QuantityType getQgrid() const; + // Q-range configuration + void setQmin(double); + const double& getQmin() const; + void setQmax(double); + const double& getQmax() const; + const double& getQstep() const; + + // R-range methods + QuantityType getRgrid() const; + // R-range configuration + virtual void setRmin(double); + virtual void setRmax(double); + void setRstep(double); + const double& getRstep() const; + /// maximum total extension of the r-range accounting for both + /// termination ripples and peak tails + void setMaxExtension(double); + /// maximum total extension of the r-range accounting for both + /// termination ripples and peak tails + const double& getMaxExtension() const; + /// lower bound for the r-range extended for termination ripples + double getExtendedRmin() const; + /// upper bound of the r-range extended for termination ripples + double getExtendedRmax() const; + + // PDF profile configuration + void setPeakProfile(PeakProfilePtr); + void setPeakProfileByType(const std::string& tp); + PeakProfilePtr& getPeakProfile(); + const PeakProfilePtr& getPeakProfile() const; + + // PDF baseline configuration + // application on an array + QuantityType applyBaseline(const QuantityType& x, + const QuantityType& y) const; + void setBaseline(PDFBaselinePtr); + void setBaselineByType(const std::string& tp); + PDFBaselinePtr& getBaseline(); + const PDFBaselinePtr& getBaseline() const; + + protected: + // Attributes overload to direct visitors around data structures + virtual void accept(diffpy::BaseAttributesVisitor& v); + virtual void accept(diffpy::BaseAttributesVisitor& v) const; + + // PairQuantity overloads + virtual void resetValue(); + virtual void configureBondGenerator(BaseBondGenerator&) const; + virtual void addPairContribution(const BaseBondGenerator&, int); + // support for PQEvaluatorOptimized + virtual void stashPartialValue(); + virtual void restorePartialValue(); + + private: + // methods - calculation specific + /// complete lower bound extension of the calculated grid + double rcalclo() const; + /// complete upper bound extension of the calculated grid + double rcalchi() const; + /// r-range extension to allow propagation of termination ripples + double extFromTerminationRipples() const; + /// r-range extension to account for tails from out-of-range peaks + double extFromPeakTails() const; + /// number of dr steps at rcalclo from 0 + int rcalcloSteps() const; + /// number of dr steps at rhalchi from 0 + int rcalchiSteps() const; + /// number of dr steps at lower extension due to termination ripples + int extendedRminSteps() const; + /// number of dr steps at upper extension due to termination ripples + int extendedRmaxSteps() const; + /// number of points in the r-grid extended with termination ripples + int countExtendedPoints() const; + /// number of points in the complete calculated r-grid + int countCalcPoints() const; + /// index of a nearby point in the complete calculated r-grid + int calcIndex(double r) const; + /// reduce extended grid to user-requested results grid + /// by cutting away the points for termination ripples + void cutRipplePoints(QuantityType& y) const; + + // structure factors - fast lookup by site index + /// effective scattering factor at a given site scaled by occupancy + const double& sfSite(int) const; + /// average scattering factor + double sfAverage() const; + void cacheStructureData(); + void cacheRlimitsData(); + + // data + // configuration + double mqmin; + double mqmax; + double mrstep; + double mmaxextension; + PeakProfilePtr mpeakprofile; + PDFBaselinePtr mbaseline; + struct { + std::vector sfsite; + double sfaverage; + double totaloccupancy; + double activeoccupancy; + } mstructure_cache; + struct { + int extendedrminsteps; + int extendedrmaxsteps; + int rcalclosteps; + int rcalchisteps; + } mrlimits_cache; + // support for PQEvaluatorOptimized + struct { + QuantityType value; + int rclosteps; + } mstashedvalue; + // serialization + friend class boost::serialization::access; + template + void serialize(Archive& ar, const unsigned int version) { + using boost::serialization::base_object; + ar& base_object(*this); + ar& base_object(*this); + ar& base_object(*this); + ar& base_object(*this); + ar & mqmin; + ar & mqmax; + ar & mrstep; + ar & mmaxextension; + ar & mpeakprofile; + ar & mbaseline; + ar & mstructure_cache.sfsite; + ar & mstructure_cache.sfaverage; + ar & mstructure_cache.totaloccupancy; + ar & mstructure_cache.activeoccupancy; + ar & mrlimits_cache.extendedrminsteps; + ar & mrlimits_cache.extendedrmaxsteps; + ar & mrlimits_cache.rcalclosteps; + ar & mrlimits_cache.rcalchisteps; + } }; // class PDFCalculator -} // namespace srreal -} // namespace diffpy +} // namespace srreal +} // namespace diffpy // Serialization ------------------------------------------------------------- diff --git a/src/diffpy/srreal/PDFEnvelope.cpp b/src/diffpy/srreal/PDFEnvelope.cpp index 3d6811d3..b61e2ae2 100644 --- a/src/diffpy/srreal/PDFEnvelope.cpp +++ b/src/diffpy/srreal/PDFEnvelope.cpp @@ -1,20 +1,20 @@ /***************************************************************************** -* -* 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.txt for a list of people who contributed. -* See LICENSE_DANSE.txt for license information. -* -****************************************************************************** -* -* class PDFEnvelope -- abstract base class for PDF envelope functions -* -*****************************************************************************/ + * + * 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.txt for a list of people who contributed. + * See LICENSE_DANSE.txt for license information. + * + ****************************************************************************** + * + * class PDFEnvelope -- abstract base class for PDF envelope functions + * + *****************************************************************************/ #include #include @@ -39,103 +39,79 @@ namespace srreal { // public methods -QuantityType PDFEnvelopeOwner::applyEnvelopes( - const QuantityType& x, const QuantityType& y) const -{ - assert(x.size() == y.size()); - QuantityType z = y; - EnvelopeStorage::const_iterator evit; - for (evit = menvelope.begin(); evit != menvelope.end(); ++evit) - { - PDFEnvelope& fenvelope = *(evit->second); - QuantityType::const_iterator xi = x.begin(); - QuantityType::iterator zi = z.begin(); - for (; xi != x.end(); ++xi, ++zi) - { - *zi *= fenvelope(*xi); - } +QuantityType PDFEnvelopeOwner::applyEnvelopes(const QuantityType& x, + const QuantityType& y) const { + assert(x.size() == y.size()); + QuantityType z = y; + EnvelopeStorage::const_iterator evit; + for (evit = menvelope.begin(); evit != menvelope.end(); ++evit) { + PDFEnvelope& fenvelope = *(evit->second); + QuantityType::const_iterator xi = x.begin(); + QuantityType::iterator zi = z.begin(); + for (; xi != x.end(); ++xi, ++zi) { + *zi *= fenvelope(*xi); } - return z; + } + return z; } - -void PDFEnvelopeOwner::addEnvelope(PDFEnvelopePtr envlp) -{ - ensureNonNull("PDFEnvelope", envlp); - menvelope[envlp->type()] = envlp; +void PDFEnvelopeOwner::addEnvelope(PDFEnvelopePtr envlp) { + ensureNonNull("PDFEnvelope", envlp); + menvelope[envlp->type()] = envlp; } - -void PDFEnvelopeOwner::addEnvelopeByType(const string& tp) -{ - // this throws invalid_argument for invalid type - PDFEnvelopePtr envlp = PDFEnvelope::createByType(tp); - // we get here only when createByType was successful - menvelope[envlp->type()] = envlp; +void PDFEnvelopeOwner::addEnvelopeByType(const string& tp) { + // this throws invalid_argument for invalid type + PDFEnvelopePtr envlp = PDFEnvelope::createByType(tp); + // we get here only when createByType was successful + menvelope[envlp->type()] = envlp; } - -void PDFEnvelopeOwner::popEnvelope(PDFEnvelopePtr envlp) -{ - EnvelopeStorage::iterator evit = menvelope.begin(); - for (; evit != menvelope.end(); ++evit) - { - if (evit->second == envlp) - { - menvelope.erase(evit); - break; - } +void PDFEnvelopeOwner::popEnvelope(PDFEnvelopePtr envlp) { + EnvelopeStorage::iterator evit = menvelope.begin(); + for (; evit != menvelope.end(); ++evit) { + if (evit->second == envlp) { + menvelope.erase(evit); + break; } + } } - -void PDFEnvelopeOwner::popEnvelopeByType(const string& tp) -{ - menvelope.erase(tp); +void PDFEnvelopeOwner::popEnvelopeByType(const string& tp) { + menvelope.erase(tp); } - -const PDFEnvelopePtr& PDFEnvelopeOwner::getEnvelopeByType(const string& tp) const -{ - // call non-constant method - const PDFEnvelopePtr& rv = - const_cast(this)->getEnvelopeByType(tp); - return rv; +const PDFEnvelopePtr& PDFEnvelopeOwner::getEnvelopeByType( + const string& tp) const { + // call non-constant method + const PDFEnvelopePtr& rv = + const_cast(this)->getEnvelopeByType(tp); + return rv; } - -PDFEnvelopePtr& PDFEnvelopeOwner::getEnvelopeByType(const string& tp) -{ - if (!menvelope.count(tp)) - { - ostringstream emsg; - emsg << "Invalid or missing PDFEnvelope type '" << tp << "'."; - throw invalid_argument(emsg.str()); - } - PDFEnvelopePtr& rv = menvelope[tp]; - return rv; +PDFEnvelopePtr& PDFEnvelopeOwner::getEnvelopeByType(const string& tp) { + if (!menvelope.count(tp)) { + ostringstream emsg; + emsg << "Invalid or missing PDFEnvelope type '" << tp << "'."; + throw invalid_argument(emsg.str()); + } + PDFEnvelopePtr& rv = menvelope[tp]; + return rv; } - -set PDFEnvelopeOwner::usedEnvelopeTypes() const -{ - set rv; - EnvelopeStorage::const_iterator evit; - for (evit = menvelope.begin(); evit != menvelope.end(); ++evit) - { - rv.insert(rv.end(), evit->first); - } - return rv; +set PDFEnvelopeOwner::usedEnvelopeTypes() const { + set rv; + EnvelopeStorage::const_iterator evit; + for (evit = menvelope.begin(); evit != menvelope.end(); ++evit) { + rv.insert(rv.end(), evit->first); + } + return rv; } +void PDFEnvelopeOwner::clearEnvelopes() { menvelope.clear(); } -void PDFEnvelopeOwner::clearEnvelopes() -{ - menvelope.clear(); -} - -} // namespace srreal -} // namespace diffpy +} // namespace srreal +} // namespace diffpy // Serialization ------------------------------------------------------------- diff --git a/src/diffpy/srreal/PDFEnvelope.hpp b/src/diffpy/srreal/PDFEnvelope.hpp index 7cb169ee..6383be0e 100644 --- a/src/diffpy/srreal/PDFEnvelope.hpp +++ b/src/diffpy/srreal/PDFEnvelope.hpp @@ -1,23 +1,23 @@ /***************************************************************************** -* -* 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.txt for a list of people who contributed. -* See LICENSE_DANSE.txt for license information. -* -****************************************************************************** -* -* class PDFEnvelope -- abstract base class for PDF envelope functions -* A concrete instance of PDFEnvelope is a functor, that calculates -* PDF scaling coefficients at a given pair distance r. Several functors -* can be defined and applied in PDFCalculator. -* -*****************************************************************************/ + * + * 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.txt for a list of people who contributed. + * See LICENSE_DANSE.txt for license information. + * + ****************************************************************************** + * + * class PDFEnvelope -- abstract base class for PDF envelope functions + * A concrete instance of PDFEnvelope is a functor, that calculates + * PDF scaling coefficients at a given pair distance r. Several functors + * can be defined and applied in PDFCalculator. + * + *****************************************************************************/ #ifndef PDFENVELOPE_HPP_INCLUDED #define PDFENVELOPE_HPP_INCLUDED @@ -40,76 +40,63 @@ namespace diffpy { namespace srreal { - /// @class PDFEnvelope /// @brief abstract base class for PDF envelope scaling function -class DLL_EXPORT PDFEnvelope : - public diffpy::Attributes, - public diffpy::HasClassRegistry -{ - public: - - // methods - virtual double operator()(const double& r) const = 0; - - private: - - // serialization - friend class boost::serialization::access; - template - void serialize(Archive& ar, const unsigned int version) - { } - +class DLL_EXPORT PDFEnvelope : public diffpy::Attributes, + public diffpy::HasClassRegistry { + public: + // methods + virtual double operator()(const double& r) const = 0; + + private: + // serialization + friend class boost::serialization::access; + template + void serialize(Archive& ar, const unsigned int version) {} }; - typedef PDFEnvelope::SharedPtr PDFEnvelopePtr; - /// @class PDFEnvelopeOwner /// @brief storage of one or more PDFEnvelope functions and their /// application to unscaled x, y arrays -class DLL_EXPORT PDFEnvelopeOwner -{ - public: - - // application on (x, y) data - QuantityType applyEnvelopes(const QuantityType& x, const QuantityType& y) const; - - // access and configuration of PDF envelope functions - // configuration of envelopes - void addEnvelope(PDFEnvelopePtr); - void addEnvelopeByType(const std::string& tp); - void popEnvelope(PDFEnvelopePtr); - void popEnvelopeByType(const std::string& tp); - const PDFEnvelopePtr& getEnvelopeByType(const std::string& tp) const; - PDFEnvelopePtr& getEnvelopeByType(const std::string& tp); - std::set usedEnvelopeTypes() const; - void clearEnvelopes(); - - private: - - // types - typedef std::map EnvelopeStorage; - - // data - EnvelopeStorage menvelope; - - // serialization - friend class boost::serialization::access; - - template - void serialize(Archive& ar, const unsigned int version) - { - ar & menvelope; - } - +class DLL_EXPORT PDFEnvelopeOwner { + public: + // application on (x, y) data + QuantityType applyEnvelopes(const QuantityType& x, + const QuantityType& y) const; + + // access and configuration of PDF envelope functions + // configuration of envelopes + void addEnvelope(PDFEnvelopePtr); + void addEnvelopeByType(const std::string& tp); + void popEnvelope(PDFEnvelopePtr); + void popEnvelopeByType(const std::string& tp); + const PDFEnvelopePtr& getEnvelopeByType(const std::string& tp) const; + PDFEnvelopePtr& getEnvelopeByType(const std::string& tp); + std::set usedEnvelopeTypes() const; + void clearEnvelopes(); + + private: + // types + typedef std::map EnvelopeStorage; + + // data + EnvelopeStorage menvelope; + + // serialization + friend class boost::serialization::access; + + template + void serialize(Archive& ar, const unsigned int version) { + ar & menvelope; + } }; -} // namespace srreal -} // namespace diffpy +} // namespace srreal +} // namespace diffpy // Serialization ------------------------------------------------------------- diff --git a/src/diffpy/srreal/PDFUtils.cpp b/src/diffpy/srreal/PDFUtils.cpp index 37a50b67..4994c171 100644 --- a/src/diffpy/srreal/PDFUtils.cpp +++ b/src/diffpy/srreal/PDFUtils.cpp @@ -1,20 +1,20 @@ /***************************************************************************** -* -* 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.txt for a list of people who contributed. -* See LICENSE_DANSE.txt for license information. -* -****************************************************************************** -* -* Various common routines useful for PDF calculation. -* -*****************************************************************************/ + * + * 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.txt for a list of people who contributed. + * See LICENSE_DANSE.txt for license information. + * + ****************************************************************************** + * + * Various common routines useful for PDF calculation. + * + *****************************************************************************/ #include @@ -38,63 +38,56 @@ namespace { const char* EMSGFFT = "Fourier Transformation failed."; -} // namespace +} // namespace // PDFUtils functions -------------------------------------------------------- -QuantityType fftgtof(const QuantityType& g, double rstep, double rmin) -{ - if (g.empty()) return g; - int padrmin = int(round(rmin / rstep)); - int Npad1 = padrmin + g.size(); - // pad to the next power of 2 for fast Fourier transformation - int Npad2 = (1 << int(ceil(log2(Npad1)))); - // sine transformations needs an odd extension - // gpadc array has to be doubled for complex coefficients - int Npad4 = 4 * Npad2; - valarray gpadc(0.0, Npad4); - QuantityType::const_iterator gi; - // copy the original g signal - int ilo = padrmin; - for (gi = g.begin(); gi != g.end(); ++gi, ++ilo) - { - gpadc[2 * ilo] = *gi; - } - // copy the odd part of g skipping the first point, - // because it is periodic image of gpadc[0] - int ihi = 2 * Npad2 - 1; - for (ilo = 1; ilo < Npad2; ++ilo, --ihi) - { - gpadc[2 * ihi] = -1 * gpadc[2 * ilo]; - } - int status; - status = gsl_fft_complex_radix2_inverse(&(gpadc[0]), 1, 2 * Npad2); - if (status != GSL_SUCCESS) throw invalid_argument(EMSGFFT); - QuantityType f(Npad2); - for (int i = 0; i < Npad2; ++i) - { - f[i] = gpadc[2 * i + 1] * Npad2 * rstep; - } - // real components should be all close to zero - assert(fabs(valarray(gpadc[slice(0, 2 * Npad2, 2)]).min()) <= - SQRT_DOUBLE_EPS * gpadc.max()); - assert(fabs(valarray(gpadc[slice(0, 2 * Npad2, 2)]).max()) <= - SQRT_DOUBLE_EPS * gpadc.max()); - return f; +QuantityType fftgtof(const QuantityType& g, double rstep, double rmin) { + if (g.empty()) return g; + int padrmin = int(round(rmin / rstep)); + int Npad1 = padrmin + g.size(); + // pad to the next power of 2 for fast Fourier transformation + int Npad2 = (1 << int(ceil(log2(Npad1)))); + // sine transformations needs an odd extension + // gpadc array has to be doubled for complex coefficients + int Npad4 = 4 * Npad2; + valarray gpadc(0.0, Npad4); + QuantityType::const_iterator gi; + // copy the original g signal + int ilo = padrmin; + for (gi = g.begin(); gi != g.end(); ++gi, ++ilo) { + gpadc[2 * ilo] = *gi; + } + // copy the odd part of g skipping the first point, + // because it is periodic image of gpadc[0] + int ihi = 2 * Npad2 - 1; + for (ilo = 1; ilo < Npad2; ++ilo, --ihi) { + gpadc[2 * ihi] = -1 * gpadc[2 * ilo]; + } + int status; + status = gsl_fft_complex_radix2_inverse(&(gpadc[0]), 1, 2 * Npad2); + if (status != GSL_SUCCESS) throw invalid_argument(EMSGFFT); + QuantityType f(Npad2); + for (int i = 0; i < Npad2; ++i) { + f[i] = gpadc[2 * i + 1] * Npad2 * rstep; + } + // real components should be all close to zero + assert(fabs(valarray(gpadc[slice(0, 2 * Npad2, 2)]).min()) <= + SQRT_DOUBLE_EPS * gpadc.max()); + assert(fabs(valarray(gpadc[slice(0, 2 * Npad2, 2)]).max()) <= + SQRT_DOUBLE_EPS * gpadc.max()); + return f; } - -QuantityType fftftog(const QuantityType& f, double qstep, double qmin) -{ - // ftog is the same as gtof, just normalized by 2 / PI - QuantityType g = fftgtof(f, qstep, qmin); - QuantityType::iterator gi; - for (gi = g.begin(); gi != g.end(); ++gi) *gi *= 2.0 / M_PI; - return g; +QuantityType fftftog(const QuantityType& f, double qstep, double qmin) { + // ftog is the same as gtof, just normalized by 2 / PI + QuantityType g = fftgtof(f, qstep, qmin); + QuantityType::iterator gi; + for (gi = g.begin(); gi != g.end(); ++gi) *gi *= 2.0 / M_PI; + return g; } - -} // namespace srreal -} // namespace diffpy +} // namespace srreal +} // namespace diffpy // End of file diff --git a/src/diffpy/srreal/PDFUtils.hpp b/src/diffpy/srreal/PDFUtils.hpp index b28686ba..ed40079c 100644 --- a/src/diffpy/srreal/PDFUtils.hpp +++ b/src/diffpy/srreal/PDFUtils.hpp @@ -1,23 +1,23 @@ /***************************************************************************** -* -* 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.txt for a list of people who contributed. -* See LICENSE_DANSE.txt for license information. -* -****************************************************************************** -* -* Various common routines useful for PDF calculation: -* meanSquareDisplacement -* maxUii -* fftftog and fftgtof -* -*****************************************************************************/ + * + * 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.txt for a list of people who contributed. + * See LICENSE_DANSE.txt for license information. + * + ****************************************************************************** + * + * Various common routines useful for PDF calculation: + * meanSquareDisplacement + * maxUii + * fftftog and fftgtof + * + *****************************************************************************/ #ifndef PDFUTILS_HPP_INCLUDED #define PDFUTILS_HPP_INCLUDED @@ -45,23 +45,31 @@ const double DEFAULT_QGRID_QMAX = 10.0; const double DEFAULT_QGRID_QSTEP = 0.05; /// fast Fourier transformation converting G(r) to F(Q) -DLL_EXPORT QuantityType fftgtof(const QuantityType& g, double rstep, double rmin=0.0); +DLL_EXPORT QuantityType fftgtof(const QuantityType& g, double rstep, + double rmin = 0.0); /// fast Fourier transformation converting F(Q) to G(r) -DLL_EXPORT QuantityType fftftog(const QuantityType& f, double qstep, double qmin=0.0); +DLL_EXPORT QuantityType fftftog(const QuantityType& f, double qstep, + double qmin = 0.0); /// shared methods for PDFCalculator and DebyePDFCalculator -template QuantityType pdfutils_getQgrid(const T* pdfc); -template int pdfutils_qminSteps(const T* pdfc); -template int pdfutils_qmaxSteps(const T* pdfc); -template QuantityType pdfutils_getRgrid(const T* pdfc); +template +QuantityType pdfutils_getQgrid(const T* pdfc); +template +int pdfutils_qminSteps(const T* pdfc); +template +int pdfutils_qmaxSteps(const T* pdfc); +template +QuantityType pdfutils_getRgrid(const T* pdfc); int pdfutils_rminSteps(const double& rmin, const double& rstep); -template int pdfutils_rminSteps(const T* pdfc); +template +int pdfutils_rminSteps(const T* pdfc); int pdfutils_rmaxSteps(const double& rmax, const double& rstep); -template int pdfutils_rmaxSteps(const T* pdfc); +template +int pdfutils_rmaxSteps(const T* pdfc); -} // namespace srreal -} // namespace diffpy +} // namespace srreal +} // namespace diffpy // Implementation ------------------------------------------------------------ diff --git a/src/diffpy/srreal/PQEvaluator.cpp b/src/diffpy/srreal/PQEvaluator.cpp index 71874053..ff51794d 100644 --- a/src/diffpy/srreal/PQEvaluator.cpp +++ b/src/diffpy/srreal/PQEvaluator.cpp @@ -1,25 +1,24 @@ /***************************************************************************** -* -* 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.txt for a list of people who contributed. -* See LICENSE_DANSE.txt for license information. -* -****************************************************************************** -* -* class PQEvaluatorBasic -- robust PairQuantity evaluator, the result -* is always calculated from scratch. -* -* class PQEvaluatorOptimized -- optimized PairQuantity evaluator with fast -* quantity updates -* -*****************************************************************************/ - + * + * 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.txt for a list of people who contributed. + * See LICENSE_DANSE.txt for license information. + * + ****************************************************************************** + * + * class PQEvaluatorBasic -- robust PairQuantity evaluator, the result + * is always calculated from scratch. + * + * class PQEvaluatorOptimized -- optimized PairQuantity evaluator with fast + * quantity updates + * + *****************************************************************************/ #include #include @@ -42,429 +41,340 @@ namespace { // tolerated load variance for splitting outer loop for parallel evaluation const double CPU_LOAD_VARIANCE = 0.1; -SiteIndices -complementary_indices(const int sz, const SiteIndices& indices0) -{ - SiteIndices rv; - rv.reserve(sz); - SiteIndices::const_iterator ii0 = indices0.begin(); - for (int k = 0; k < sz; ++k) - { - if (ii0 == indices0.end() || k < *ii0) - { - rv.push_back(k); - } - else - { - assert(k == *ii0); - ++ii0; - } +SiteIndices complementary_indices(const int sz, const SiteIndices& indices0) { + SiteIndices rv; + rv.reserve(sz); + SiteIndices::const_iterator ii0 = indices0.begin(); + for (int k = 0; k < sz; ++k) { + if (ii0 == indices0.end() || k < *ii0) { + rv.push_back(k); + } else { + assert(k == *ii0); + ++ii0; } - return rv; + } + return rv; } -} // namespace +} // namespace ////////////////////////////////////////////////////////////////////////////// // class PQEvaluatorBasic ////////////////////////////////////////////////////////////////////////////// -PQEvaluatorBasic::PQEvaluatorBasic() : - mconfigflags(0), - mcpuindex(0), mncpu(1), mtypeused(NONE) -{ } - - -PQEvaluatorType PQEvaluatorBasic::typeint() const -{ - return BASIC; -} - - -PQEvaluatorType PQEvaluatorBasic::typeintused() const -{ - return mtypeused; -} - - -void PQEvaluatorBasic::validate(PairQuantity& pq) const -{ - return; -} - - -void PQEvaluatorBasic::updateValue( - PairQuantity& pq, StructureAdapterPtr stru) -{ - mtypeused = BASIC; - pq.setStructure(stru); - BaseBondGeneratorPtr bnds = pq.mstructure->createBondGenerator(); - pq.configureBondGenerator(*bnds); - int cntsites = pq.mstructure->countSites(); - // loop counter - long n = mcpuindex; - // split outer loop for many atoms. The CPUs should have similar load. - bool chop_outer = (mncpu <= ((cntsites - 1) * CPU_LOAD_VARIANCE + 1)); - bool chop_inner = !chop_outer; - const bool hasmask = pq.hasMask(); - if (!this->isParallel()) chop_outer = chop_inner = false; - const bool usefullsum = this->getFlag(USEFULLSUM); - for (int i0 = 0; i0 < cntsites; ++i0) - { - if (chop_outer && (n++ % mncpu)) continue; - bnds->selectAnchorSite(i0); - int i1hi = usefullsum ? cntsites : (i0 + 1); - bnds->selectSiteRange(0, i1hi); - for (bnds->rewind(); !bnds->finished(); bnds->next()) - { - if (chop_inner && (n++ % mncpu)) continue; - int i1 = bnds->site1(); - if (hasmask && !pq.getPairMask(i0, i1)) continue; - int summationscale = (usefullsum || i0 == i1) ? 1 : 2; - pq.addPairContribution(*bnds, summationscale); - } +PQEvaluatorBasic::PQEvaluatorBasic() + : mconfigflags(0), mcpuindex(0), mncpu(1), mtypeused(NONE) {} + +PQEvaluatorType PQEvaluatorBasic::typeint() const { return BASIC; } + +PQEvaluatorType PQEvaluatorBasic::typeintused() const { return mtypeused; } + +void PQEvaluatorBasic::validate(PairQuantity& pq) const { return; } + +void PQEvaluatorBasic::updateValue(PairQuantity& pq, StructureAdapterPtr stru) { + mtypeused = BASIC; + pq.setStructure(stru); + BaseBondGeneratorPtr bnds = pq.mstructure->createBondGenerator(); + pq.configureBondGenerator(*bnds); + int cntsites = pq.mstructure->countSites(); + // loop counter + long n = mcpuindex; + // split outer loop for many atoms. The CPUs should have similar load. + bool chop_outer = (mncpu <= ((cntsites - 1) * CPU_LOAD_VARIANCE + 1)); + bool chop_inner = !chop_outer; + const bool hasmask = pq.hasMask(); + if (!this->isParallel()) chop_outer = chop_inner = false; + const bool usefullsum = this->getFlag(USEFULLSUM); + for (int i0 = 0; i0 < cntsites; ++i0) { + if (chop_outer && (n++ % mncpu)) continue; + bnds->selectAnchorSite(i0); + int i1hi = usefullsum ? cntsites : (i0 + 1); + bnds->selectSiteRange(0, i1hi); + for (bnds->rewind(); !bnds->finished(); bnds->next()) { + if (chop_inner && (n++ % mncpu)) continue; + int i1 = bnds->site1(); + if (hasmask && !pq.getPairMask(i0, i1)) continue; + int summationscale = (usefullsum || i0 == i1) ? 1 : 2; + pq.addPairContribution(*bnds, summationscale); } - mvalue_ticker.click(); + } + mvalue_ticker.click(); } - -void PQEvaluatorBasic::setFlag(PQEvaluatorFlag flag, bool value) -{ - if (value) mconfigflags |= int(flag); - else mconfigflags &= ~int(flag); +void PQEvaluatorBasic::setFlag(PQEvaluatorFlag flag, bool value) { + if (value) + mconfigflags |= int(flag); + else + mconfigflags &= ~int(flag); } - -bool PQEvaluatorBasic::getFlag(PQEvaluatorFlag flag) const -{ - return mconfigflags & int(flag); +bool PQEvaluatorBasic::getFlag(PQEvaluatorFlag flag) const { + return mconfigflags & int(flag); } - -void PQEvaluatorBasic::setupParallelRun(int cpuindex, int ncpu) -{ - // make sure ncpu is at least one - if (ncpu < 1) - { - const char* emsg = "Number of CPU ncpu must be at least 1."; - throw invalid_argument(emsg); - } - mcpuindex = cpuindex; - mncpu = ncpu; +void PQEvaluatorBasic::setupParallelRun(int cpuindex, int ncpu) { + // make sure ncpu is at least one + if (ncpu < 1) { + const char* emsg = "Number of CPU ncpu must be at least 1."; + throw invalid_argument(emsg); + } + mcpuindex = cpuindex; + mncpu = ncpu; } - -bool PQEvaluatorBasic::isParallel() const -{ - return mncpu > 1; -} +bool PQEvaluatorBasic::isParallel() const { return mncpu > 1; } ////////////////////////////////////////////////////////////////////////////// // class PQEvaluatorOptimized ////////////////////////////////////////////////////////////////////////////// -PQEvaluatorType PQEvaluatorOptimized::typeint() const -{ - return OPTIMIZED; -} +PQEvaluatorType PQEvaluatorOptimized::typeint() const { return OPTIMIZED; } - -void PQEvaluatorOptimized::validate(PairQuantity& pq) const -{ - // Check if PairQuantity provides stashPartialValue. - pq.stashPartialValue(); - pq.restorePartialValue(); +void PQEvaluatorOptimized::validate(PairQuantity& pq) const { + // Check if PairQuantity provides stashPartialValue. + pq.stashPartialValue(); + pq.restorePartialValue(); } - -void PQEvaluatorOptimized::updateValue( - PairQuantity& pq, StructureAdapterPtr stru) -{ - mtypeused = OPTIMIZED; - // revert to normal calculation if there is no structure or - // if PairQuantity uses mask - if (pq.ticker() >= mvalue_ticker || !mlast_structure) - { - return this->updateValueCompletely(pq, stru); +void PQEvaluatorOptimized::updateValue(PairQuantity& pq, + StructureAdapterPtr stru) { + mtypeused = OPTIMIZED; + // revert to normal calculation if there is no structure or + // if PairQuantity uses mask + if (pq.ticker() >= mvalue_ticker || !mlast_structure) { + return this->updateValueCompletely(pq, stru); + } + // do not do fast updates if they take more work + StructureDifference sd = mlast_structure->diff(stru); + if (!sd.allowsfastupdate()) { + return this->updateValueCompletely(pq, stru); + } + if ((this->getFlag(FIXEDSITEINDEX) || pq.hasPairMask()) && + sd.diffmethod != StructureDifference::Method::SIDEBYSIDE) { + return this->updateValueCompletely(pq, stru); + } + // Remove contributions from the extra sites in the old structure + assert(sd.stru0 == mlast_structure); + int cntsites0 = sd.stru0->countSites(); + BaseBondGeneratorPtr bnds0 = sd.stru0->createBondGenerator(); + pq.configureBondGenerator(*bnds0); + // loop counter + long n = mcpuindex; + bool usefullsum = this->getFlag(USEFULLSUM); + // the loop is adjusted according to usefullsum and split within + // the outer loop in case of parallel evaluation. + SiteIndices anchors = sd.pop0; + SiteIndices unchanged; + if (!sd.pop0.empty()) { + unchanged = complementary_indices(cntsites0, sd.pop0); + anchors.insert(anchors.end(), unchanged.begin(), unchanged.end()); + } + bnds0->selectSites(anchors.begin(), anchors.end()); + SiteIndices::const_iterator last_anchor = + usefullsum ? anchors.end() : (anchors.begin() + sd.pop0.size()); + SiteIndices::const_iterator ii0; + bool needsreselection = usefullsum; + const bool hasmask = pq.hasMask(); + for (ii0 = anchors.begin(); ii0 != last_anchor; ++ii0) { + if (n++ % mncpu) continue; + const int& i0 = *ii0; + bnds0->selectAnchorSite(i0); + // when using half sum, deselect visited popped sites + if (!usefullsum) bnds0->selectSites(ii0, anchors.end()); + // when using full sum, select only the popped sites when + // anchored at an unchanged atom + else if (needsreselection && ii0 >= (anchors.begin() + sd.pop0.size())) { + bnds0->selectSites(sd.pop0.begin(), sd.pop0.end()); + needsreselection = false; } - // do not do fast updates if they take more work - StructureDifference sd = mlast_structure->diff(stru); - if (!sd.allowsfastupdate()) - { - return this->updateValueCompletely(pq, stru); + for (bnds0->rewind(); !bnds0->finished(); bnds0->next()) { + int i1 = bnds0->site1(); + if (hasmask && !pq.getPairMask(i0, i1)) continue; + const int summationscale = (usefullsum || i0 == i1) ? -1 : -2; + pq.addPairContribution(*bnds0, summationscale); } - if ((this->getFlag(FIXEDSITEINDEX) || pq.hasPairMask()) && - sd.diffmethod != StructureDifference::Method::SIDEBYSIDE) - { - return this->updateValueCompletely(pq, stru); + } + // Add contributions from the new atoms in the updated structure + // save current value to override the resetValue call from setStructure + assert(sd.stru1); + pq.stashPartialValue(); + // setStructure(stru1) calls stru1->customPQConfig(pq), which may totally + // change pq configuration. If so, revert to full calculation. + assert(pq.ticker() < mvalue_ticker); + pq.setStructure(sd.stru1); + if (pq.ticker() >= mvalue_ticker) { + return this->updateValueCompletely(pq, stru); + } + pq.restorePartialValue(); + int cntsites1 = sd.stru1->countSites(); + BaseBondGeneratorPtr bnds1 = sd.stru1->createBondGenerator(); + pq.configureBondGenerator(*bnds1); + anchors = sd.add1; + unchanged.clear(); + if (!sd.add1.empty()) { + unchanged = complementary_indices(cntsites1, sd.add1); + anchors.insert(anchors.begin(), unchanged.begin(), unchanged.end()); + } + bnds1->selectSites(sd.add1.begin(), sd.add1.end()); + SiteIndices::const_iterator first_anchor = + usefullsum ? anchors.begin() : (anchors.end() - sd.add1.size()); + SiteIndices::const_iterator ii1; + needsreselection = usefullsum; + for (ii1 = first_anchor; ii1 != anchors.end(); ++ii1) { + if (n++ % mncpu) continue; + const int& i0 = *ii1; + bnds1->selectAnchorSite(i0); + // when using half sum, activate the added site + if (!usefullsum) bnds1->selectSites(anchors.begin(), ii1 + 1); + // when using full sum select unchanged atoms once anchored + // at an added atom. + else if (needsreselection && ii1 >= (anchors.end() - sd.add1.size())) { + bnds1->selectSites(anchors.begin(), anchors.end()); + needsreselection = false; } - // Remove contributions from the extra sites in the old structure - assert(sd.stru0 == mlast_structure); - int cntsites0 = sd.stru0->countSites(); - BaseBondGeneratorPtr bnds0 = sd.stru0->createBondGenerator(); - pq.configureBondGenerator(*bnds0); - // loop counter - long n = mcpuindex; - bool usefullsum = this->getFlag(USEFULLSUM); - // the loop is adjusted according to usefullsum and split within - // the outer loop in case of parallel evaluation. - SiteIndices anchors = sd.pop0; - SiteIndices unchanged; - if (!sd.pop0.empty()) - { - unchanged = complementary_indices(cntsites0, sd.pop0); - anchors.insert(anchors.end(), unchanged.begin(), unchanged.end()); + for (bnds1->rewind(); !bnds1->finished(); bnds1->next()) { + int i1 = bnds1->site1(); + if (hasmask && !pq.getPairMask(i0, i1)) continue; + const int summationscale = (usefullsum || i0 == i1) ? +1 : +2; + pq.addPairContribution(*bnds1, summationscale); } - bnds0->selectSites(anchors.begin(), anchors.end()); - SiteIndices::const_iterator last_anchor = usefullsum ? - anchors.end() : (anchors.begin() + sd.pop0.size()); - SiteIndices::const_iterator ii0; - bool needsreselection = usefullsum; - const bool hasmask = pq.hasMask(); - for (ii0 = anchors.begin(); ii0 != last_anchor; ++ii0) - { - if (n++ % mncpu) continue; - const int& i0 = *ii0; - bnds0->selectAnchorSite(i0); - // when using half sum, deselect visited popped sites - if (!usefullsum) bnds0->selectSites(ii0, anchors.end()); - // when using full sum, select only the popped sites when - // anchored at an unchanged atom - else if (needsreselection && ii0 >= (anchors.begin() + sd.pop0.size())) - { - bnds0->selectSites(sd.pop0.begin(), sd.pop0.end()); - needsreselection = false; - } - for (bnds0->rewind(); !bnds0->finished(); bnds0->next()) - { - int i1 = bnds0->site1(); - if (hasmask && !pq.getPairMask(i0, i1)) continue; - const int summationscale = (usefullsum || i0 == i1) ? -1 : -2; - pq.addPairContribution(*bnds0, summationscale); - } - } - // Add contributions from the new atoms in the updated structure - // save current value to override the resetValue call from setStructure - assert(sd.stru1); - pq.stashPartialValue(); - // setStructure(stru1) calls stru1->customPQConfig(pq), which may totally - // change pq configuration. If so, revert to full calculation. - assert(pq.ticker() < mvalue_ticker); - pq.setStructure(sd.stru1); - if (pq.ticker() >= mvalue_ticker) - { - return this->updateValueCompletely(pq, stru); - } - pq.restorePartialValue(); - int cntsites1 = sd.stru1->countSites(); - BaseBondGeneratorPtr bnds1 = sd.stru1->createBondGenerator(); - pq.configureBondGenerator(*bnds1); - anchors = sd.add1; - unchanged.clear(); - if (!sd.add1.empty()) - { - unchanged = complementary_indices(cntsites1, sd.add1); - anchors.insert(anchors.begin(), unchanged.begin(), unchanged.end()); - } - bnds1->selectSites(sd.add1.begin(), sd.add1.end()); - SiteIndices::const_iterator first_anchor = usefullsum ? - anchors.begin() : (anchors.end() - sd.add1.size()); - SiteIndices::const_iterator ii1; - needsreselection = usefullsum; - for (ii1 = first_anchor; ii1 != anchors.end(); ++ii1) - { - if (n++ % mncpu) continue; - const int& i0 = *ii1; - bnds1->selectAnchorSite(i0); - // when using half sum, activate the added site - if (!usefullsum) bnds1->selectSites(anchors.begin(), ii1 + 1); - // when using full sum select unchanged atoms once anchored - // at an added atom. - else if (needsreselection && ii1 >= (anchors.end() - sd.add1.size())) - { - bnds1->selectSites(anchors.begin(), anchors.end()); - needsreselection = false; - } - for (bnds1->rewind(); !bnds1->finished(); bnds1->next()) - { - int i1 = bnds1->site1(); - if (hasmask && !pq.getPairMask(i0, i1)) continue; - const int summationscale = (usefullsum || i0 == i1) ? +1 : +2; - pq.addPairContribution(*bnds1, summationscale); - } - } - mlast_structure = pq.getStructure()->clone(); - mvalue_ticker.click(); + } + mlast_structure = pq.getStructure()->clone(); + mvalue_ticker.click(); } - -void PQEvaluatorOptimized::updateValueCompletely( - PairQuantity& pq, StructureAdapterPtr stru) -{ - this->PQEvaluatorBasic::updateValue(pq, stru); - mlast_structure = pq.getStructure()->clone(); +void PQEvaluatorOptimized::updateValueCompletely(PairQuantity& pq, + StructureAdapterPtr stru) { + this->PQEvaluatorBasic::updateValue(pq, stru); + mlast_structure = pq.getStructure()->clone(); } // Helper classes and functions for PQEvaluatorCheck ------------------------- namespace { -bool cmpfabs(const double& x, const double& y) -{ - return fabs(x) < fabs(y); -} - - -class pqresults -{ - public: - - pqresults(const PairQuantity& pq) : - msaved_value(pq.value()) - { } - - - virtual ~pqresults() { } +bool cmpfabs(const double& x, const double& y) { return fabs(x) < fabs(y); } +class pqresults { + public: + pqresults(const PairQuantity& pq) : msaved_value(pq.value()) {} - virtual bool compare(const PairQuantity& pq) const - { - QuantityType::const_iterator smx = max_element( - msaved_value.begin(), msaved_value.end(), cmpfabs); - const double eps = R3::SQRT_DOUBLE_EPS * - (msaved_value.empty() ? 1.0 : max(*smx, 1.0)); - mathutils::EpsilonEqual allclose(eps); - const QuantityType& cur_value = pq.value(); - return allclose(msaved_value, cur_value); - } + virtual ~pqresults() {} - private: + virtual bool compare(const PairQuantity& pq) const { + QuantityType::const_iterator smx = + max_element(msaved_value.begin(), msaved_value.end(), cmpfabs); + const double eps = + R3::SQRT_DOUBLE_EPS * (msaved_value.empty() ? 1.0 : max(*smx, 1.0)); + mathutils::EpsilonEqual allclose(eps); + const QuantityType& cur_value = pq.value(); + return allclose(msaved_value, cur_value); + } - // data - QuantityType msaved_value; + private: + // data + QuantityType msaved_value; }; - -class pqresults_bondcalculator : public pqresults -{ - public: - - pqresults_bondcalculator(const BondCalculator& bc) : - pqresults(bc), - msaved_directions(bc.directions()), - msaved_sites0(bc.sites0()), - msaved_sites1(bc.sites1()), - msaved_types0(bc.types0()), - msaved_types1(bc.types1()) - { } - - - virtual bool compare(const PairQuantity& pq) const - { - if (!(this->pqresults::compare(pq))) return false; - const BondCalculator& bc = dynamic_cast(pq); - if (msaved_sites0 != bc.sites0()) return false; - if (msaved_sites1 != bc.sites1()) return false; - if (msaved_types0 != bc.types0()) return false; - if (msaved_types1 != bc.types1()) return false; - const vector cur_directions = bc.directions(); - const bool szneq = - msaved_directions.size() != cur_directions.size(); - if (szneq) return false; - vector::const_iterator sv, cv; - sv = msaved_directions.begin(); - cv = cur_directions.begin(); - mathutils::EpsilonEqual allclose; - for (; sv != msaved_directions.end(); ++sv, ++cv) - { - if (!allclose(*sv, *cv)) return false; - } - return true; - } - - private: - - // data - vector msaved_directions; - SiteIndices msaved_sites0; - SiteIndices msaved_sites1; - vector msaved_types0; - vector msaved_types1; +class pqresults_bondcalculator : public pqresults { + public: + pqresults_bondcalculator(const BondCalculator& bc) + : pqresults(bc), + msaved_directions(bc.directions()), + msaved_sites0(bc.sites0()), + msaved_sites1(bc.sites1()), + msaved_types0(bc.types0()), + msaved_types1(bc.types1()) {} + + virtual bool compare(const PairQuantity& pq) const { + if (!(this->pqresults::compare(pq))) return false; + const BondCalculator& bc = dynamic_cast(pq); + if (msaved_sites0 != bc.sites0()) return false; + if (msaved_sites1 != bc.sites1()) return false; + if (msaved_types0 != bc.types0()) return false; + if (msaved_types1 != bc.types1()) return false; + const vector cur_directions = bc.directions(); + const bool szneq = msaved_directions.size() != cur_directions.size(); + if (szneq) return false; + vector::const_iterator sv, cv; + sv = msaved_directions.begin(); + cv = cur_directions.begin(); + mathutils::EpsilonEqual allclose; + for (; sv != msaved_directions.end(); ++sv, ++cv) { + if (!allclose(*sv, *cv)) return false; + } + return true; + } + + private: + // data + vector msaved_directions; + SiteIndices msaved_sites0; + SiteIndices msaved_sites1; + vector msaved_types0; + vector msaved_types1; }; - -pqresults* create_pqresults(const PairQuantity& pq) -{ - const BondCalculator* pbc = dynamic_cast(&pq); - if (pbc) return new pqresults_bondcalculator(*pbc); - return new pqresults(pq); +pqresults* create_pqresults(const PairQuantity& pq) { + const BondCalculator* pbc = dynamic_cast(&pq); + if (pbc) return new pqresults_bondcalculator(*pbc); + return new pqresults(pq); } -} // namespace +} // namespace ////////////////////////////////////////////////////////////////////////////// // class PQEvaluatorCheck ////////////////////////////////////////////////////////////////////////////// -PQEvaluatorType PQEvaluatorCheck::typeint() const -{ - return CHECK; -} - - -void PQEvaluatorCheck::updateValue( - PairQuantity& pq, StructureAdapterPtr stru) -{ - this->PQEvaluatorOptimized::updateValue(pq, stru); - if (mtypeused == BASIC) return; - unique_ptr results(create_pqresults(pq)); - this->PQEvaluatorBasic::updateValue(pq, stru); - mtypeused = CHECK; - if (!results->compare(pq)) - { - const char* emsg = "Inconsistent results from OPTIMIZED evaluation."; - throw logic_error(emsg); - } +PQEvaluatorType PQEvaluatorCheck::typeint() const { return CHECK; } + +void PQEvaluatorCheck::updateValue(PairQuantity& pq, StructureAdapterPtr stru) { + this->PQEvaluatorOptimized::updateValue(pq, stru); + if (mtypeused == BASIC) return; + unique_ptr results(create_pqresults(pq)); + this->PQEvaluatorBasic::updateValue(pq, stru); + mtypeused = CHECK; + if (!results->compare(pq)) { + const char* emsg = "Inconsistent results from OPTIMIZED evaluation."; + throw logic_error(emsg); + } } // Factory for PairQuantity evaluators --------------------------------------- -PQEvaluatorPtr createPQEvaluator(PQEvaluatorType pqtp, PQEvaluatorPtr pqevsrc) -{ - PQEvaluatorPtr rv; - switch (pqtp) - { - case BASIC: - rv.reset(new PQEvaluatorBasic()); - break; - - case OPTIMIZED: - rv.reset(new PQEvaluatorOptimized()); - break; - - case CHECK: - rv.reset(new PQEvaluatorCheck()); - break; - - default: - ostringstream emsg; - emsg << "Invalid PQEvaluatorType value " << pqtp; - throw invalid_argument(emsg.str()); - } - if (pqevsrc) - { - rv->mconfigflags = pqevsrc->mconfigflags; - rv->mcpuindex = pqevsrc->mcpuindex; - rv->mncpu = pqevsrc->mncpu; - rv->mvalue_ticker = pqevsrc->mvalue_ticker; - rv->mtypeused = pqevsrc->mtypeused; - } - return rv; +PQEvaluatorPtr createPQEvaluator(PQEvaluatorType pqtp, PQEvaluatorPtr pqevsrc) { + PQEvaluatorPtr rv; + switch (pqtp) { + case BASIC: + rv.reset(new PQEvaluatorBasic()); + break; + + case OPTIMIZED: + rv.reset(new PQEvaluatorOptimized()); + break; + + case CHECK: + rv.reset(new PQEvaluatorCheck()); + break; + + default: + ostringstream emsg; + emsg << "Invalid PQEvaluatorType value " << pqtp; + throw invalid_argument(emsg.str()); + } + if (pqevsrc) { + rv->mconfigflags = pqevsrc->mconfigflags; + rv->mcpuindex = pqevsrc->mcpuindex; + rv->mncpu = pqevsrc->mncpu; + rv->mvalue_ticker = pqevsrc->mvalue_ticker; + rv->mtypeused = pqevsrc->mtypeused; + } + return rv; } - -} // namespace srreal -} // namespace diffpy +} // namespace srreal +} // namespace diffpy // Serialization ------------------------------------------------------------- diff --git a/src/diffpy/srreal/PQEvaluator.hpp b/src/diffpy/srreal/PQEvaluator.hpp index 2efd5aad..049ae786 100644 --- a/src/diffpy/srreal/PQEvaluator.hpp +++ b/src/diffpy/srreal/PQEvaluator.hpp @@ -1,25 +1,24 @@ /***************************************************************************** -* -* 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.txt for a list of people who contributed. -* See LICENSE_DANSE.txt for license information. -* -****************************************************************************** -* -* class PQEvaluatorBasic -- robust PairQuantity evaluator, the result -* is always calculated from scratch. -* -* class PQEvaluatorOptimized -- optimized PairQuantity evaluator with fast -* quantity updates -* -*****************************************************************************/ - + * + * 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.txt for a list of people who contributed. + * See LICENSE_DANSE.txt for license information. + * + ****************************************************************************** + * + * class PQEvaluatorBasic -- robust PairQuantity evaluator, the result + * is always calculated from scratch. + * + * class PQEvaluatorOptimized -- optimized PairQuantity evaluator with fast + * quantity updates + * + *****************************************************************************/ #ifndef PQEVALUATOR_HPP_INCLUDED #define PQEVALUATOR_HPP_INCLUDED @@ -44,118 +43,102 @@ class PairQuantity; typedef boost::shared_ptr PQEvaluatorPtr; -enum PQEvaluatorType {NONE, BASIC, OPTIMIZED, CHECK}; +enum PQEvaluatorType { NONE, BASIC, OPTIMIZED, CHECK }; enum PQEvaluatorFlag { - // sum over full matrix of atom pairs, use pair symmetry otherwise. - USEFULLSUM = 1, - // allow fast updates only if unchanged atoms keep their indices. - FIXEDSITEINDEX = 2, + // sum over full matrix of atom pairs, use pair symmetry otherwise. + USEFULLSUM = 1, + // allow fast updates only if unchanged atoms keep their indices. + FIXEDSITEINDEX = 2, }; -class DLL_EXPORT PQEvaluatorBasic -{ - public: - - friend DLL_EXPORT - PQEvaluatorPtr createPQEvaluator(PQEvaluatorType, PQEvaluatorPtr); - // constructor - PQEvaluatorBasic(); - virtual ~PQEvaluatorBasic() { } - - // methods - virtual PQEvaluatorType typeint() const; - PQEvaluatorType typeintused() const; - virtual void updateValue(PairQuantity&, StructureAdapterPtr); - virtual void validate(PairQuantity&) const; - void setFlag(PQEvaluatorFlag flag, bool value); - bool getFlag(PQEvaluatorFlag flag) const; - void setupParallelRun(int cpuindex, int ncpu); - bool isParallel() const; - - protected: - - - // data - /// per-bit storage of boolean configuration flags - int mconfigflags; - /// zero-based index of this CPU - int mcpuindex; - /// total number of the CPU units - int mncpu; - /// ticker for recording when was the value updated - eventticker::EventTicker mvalue_ticker; - /// type of PQEvaluator that was actually used - PQEvaluatorType mtypeused; - - private: - - // serialization - friend class boost::serialization::access; - template - void serialize(Archive& ar, const unsigned int version) - { - ar & mconfigflags & mcpuindex & mncpu & mvalue_ticker; - } +class DLL_EXPORT PQEvaluatorBasic { + public: + friend DLL_EXPORT PQEvaluatorPtr createPQEvaluator(PQEvaluatorType, + PQEvaluatorPtr); + // constructor + PQEvaluatorBasic(); + virtual ~PQEvaluatorBasic() {} + + // methods + virtual PQEvaluatorType typeint() const; + PQEvaluatorType typeintused() const; + virtual void updateValue(PairQuantity&, StructureAdapterPtr); + virtual void validate(PairQuantity&) const; + void setFlag(PQEvaluatorFlag flag, bool value); + bool getFlag(PQEvaluatorFlag flag) const; + void setupParallelRun(int cpuindex, int ncpu); + bool isParallel() const; + + protected: + // data + /// per-bit storage of boolean configuration flags + int mconfigflags; + /// zero-based index of this CPU + int mcpuindex; + /// total number of the CPU units + int mncpu; + /// ticker for recording when was the value updated + eventticker::EventTicker mvalue_ticker; + /// type of PQEvaluator that was actually used + PQEvaluatorType mtypeused; + + private: + // serialization + friend class boost::serialization::access; + template + void serialize(Archive& ar, const unsigned int version) { + ar & mconfigflags & mcpuindex & mncpu & mvalue_ticker; + } }; - -class DLL_EXPORT PQEvaluatorOptimized : public PQEvaluatorBasic -{ - public: - - // methods - virtual PQEvaluatorType typeint() const; - virtual void validate(PairQuantity&) const; - virtual void updateValue(PairQuantity&, StructureAdapterPtr); - - private: - - // data - StructureAdapterPtr mlast_structure; - - // helper method - void updateValueCompletely(PairQuantity&, StructureAdapterPtr); - - // serialization - friend class boost::serialization::access; - template - void serialize(Archive& ar, const unsigned int version) - { - using boost::serialization::base_object; - ar & base_object(*this); - ar & mlast_structure; - } +class DLL_EXPORT PQEvaluatorOptimized : public PQEvaluatorBasic { + public: + // methods + virtual PQEvaluatorType typeint() const; + virtual void validate(PairQuantity&) const; + virtual void updateValue(PairQuantity&, StructureAdapterPtr); + + private: + // data + StructureAdapterPtr mlast_structure; + + // helper method + void updateValueCompletely(PairQuantity&, StructureAdapterPtr); + + // serialization + friend class boost::serialization::access; + template + void serialize(Archive& ar, const unsigned int version) { + using boost::serialization::base_object; + ar& base_object(*this); + ar & mlast_structure; + } }; - -class DLL_EXPORT PQEvaluatorCheck : public PQEvaluatorOptimized -{ - public: - - // methods - virtual PQEvaluatorType typeint() const; - virtual void updateValue(PairQuantity&, StructureAdapterPtr); - - private: - - // serialization - friend class boost::serialization::access; - template - void serialize(Archive& ar, const unsigned int version) - { - using boost::serialization::base_object; - ar & base_object(*this); - } +class DLL_EXPORT PQEvaluatorCheck : public PQEvaluatorOptimized { + public: + // methods + virtual PQEvaluatorType typeint() const; + virtual void updateValue(PairQuantity&, StructureAdapterPtr); + + private: + // serialization + friend class boost::serialization::access; + template + void serialize(Archive& ar, const unsigned int version) { + using boost::serialization::base_object; + ar& base_object(*this); + } }; // Factory function for PairQuantity evaluators ------------------------------ DLL_EXPORT PQEvaluatorPtr createPQEvaluator( - PQEvaluatorType pqtp, PQEvaluatorPtr pqevsrc=PQEvaluatorPtr()); + PQEvaluatorType pqtp, PQEvaluatorPtr pqevsrc = PQEvaluatorPtr()); -} // namespace srreal -} // namespace diffpy +} // namespace srreal +} // namespace diffpy // Serialization ------------------------------------------------------------- diff --git a/src/diffpy/srreal/PairCounter.cpp b/src/diffpy/srreal/PairCounter.cpp index e11c9f91..71fbe0c9 100644 --- a/src/diffpy/srreal/PairCounter.cpp +++ b/src/diffpy/srreal/PairCounter.cpp @@ -1,20 +1,20 @@ /***************************************************************************** -* -* 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.txt for a list of people who contributed. -* See LICENSE_DANSE.txt for license information. -* -****************************************************************************** -* -* class PairCounter -- concrete counter of pairs in a structure. -* -*****************************************************************************/ + * + * 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.txt for a list of people who contributed. + * See LICENSE_DANSE.txt for license information. + * + ****************************************************************************** + * + * class PairCounter -- concrete counter of pairs in a structure. + * + *****************************************************************************/ #include @@ -22,19 +22,17 @@ using namespace diffpy::srreal; // Constructor --------------------------------------------------------------- -PairCounter::PairCounter() -{ - // use very large default rmax. - this->setRmax(1000); - this->resizeValue(1); +PairCounter::PairCounter() { + // use very large default rmax. + this->setRmax(1000); + this->resizeValue(1); } // Protected Methods --------------------------------------------------------- void PairCounter::addPairContribution(const BaseBondGenerator& bnds, - int summationscale) -{ - mvalue.front() += summationscale / 2.0; + int summationscale) { + mvalue.front() += summationscale / 2.0; } // End of file diff --git a/src/diffpy/srreal/PairCounter.hpp b/src/diffpy/srreal/PairCounter.hpp index eef4b92a..d11855ae 100644 --- a/src/diffpy/srreal/PairCounter.hpp +++ b/src/diffpy/srreal/PairCounter.hpp @@ -1,20 +1,20 @@ /***************************************************************************** -* -* 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.txt for a list of people who contributed. -* See LICENSE_DANSE.txt for license information. -* -****************************************************************************** -* -* class PairCounter -- concrete counter of pairs in a structure. -* -*****************************************************************************/ + * + * 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.txt for a list of people who contributed. + * See LICENSE_DANSE.txt for license information. + * + ****************************************************************************** + * + * class PairCounter -- concrete counter of pairs in a structure. + * + *****************************************************************************/ #ifndef PAIRCOUNTER_HPP_INCLUDED #define PAIRCOUNTER_HPP_INCLUDED @@ -24,35 +24,30 @@ namespace diffpy { namespace srreal { -class PairCounter : public PairQuantity -{ - public: +class PairCounter : public PairQuantity { + public: + // constructor + PairCounter(); - // constructor - PairCounter(); - - // methods - template int operator()(const T&); - - protected: - - // methods - virtual void addPairContribution(const BaseBondGenerator&, int); + // methods + template + int operator()(const T&); + protected: + // methods + virtual void addPairContribution(const BaseBondGenerator&, int); }; // Public Template Methods --------------------------------------------------- template -int PairCounter::operator()(const T& stru) -{ - this->eval(stru); - int cnt = int(this->value().front()); - return cnt; +int PairCounter::operator()(const T& stru) { + this->eval(stru); + int cnt = int(this->value().front()); + return cnt; } - -} // namespace srreal -} // namespace diffpy +} // namespace srreal +} // namespace diffpy #endif // PAIRCOUNTER_HPP_INCLUDED diff --git a/src/diffpy/srreal/PairQuantity.cpp b/src/diffpy/srreal/PairQuantity.cpp index b525b546..060b0938 100644 --- a/src/diffpy/srreal/PairQuantity.cpp +++ b/src/diffpy/srreal/PairQuantity.cpp @@ -1,20 +1,20 @@ /***************************************************************************** -* -* libdiffpy by DANSE Diffraction group -* Simon J. L. Billinge -* (c) 2008 The Trustees of Columbia University -* in the City of New York. All rights reserved. -* -* File coded by: Pavol Juhas -* -* See AUTHORS.txt for a list of people who contributed. -* See LICENSE_DANSE.txt for license information. -* -****************************************************************************** -* -* class PairQuantity -- general implementation of pair quantity calculator -* -*****************************************************************************/ + * + * libdiffpy by DANSE Diffraction group + * Simon J. L. Billinge + * (c) 2008 The Trustees of Columbia University + * in the City of New York. All rights reserved. + * + * File coded by: Pavol Juhas + * + * See AUTHORS.txt for a list of people who contributed. + * See LICENSE_DANSE.txt for license information. + * + ****************************************************************************** + * + * class PairQuantity -- general implementation of pair quantity calculator + * + *****************************************************************************/ #include #include @@ -36,456 +36,349 @@ const string PairQuantity::ALLATOMSSTR = "all"; // Constructor --------------------------------------------------------------- -PairQuantity::PairQuantity() : - mstructure(emptyStructureAdapter()), - mrmin(0.0), - mrmax(DEFAULT_BONDGENERATOR_RMAX), - mdefaultpairmask(true) -{ - this->setEvaluatorType(BASIC); - // attributes - this->registerDoubleAttribute("rmin", this, - &PairQuantity::getRmin, &PairQuantity::setRmin); - this->registerDoubleAttribute("rmax", this, - &PairQuantity::getRmax, &PairQuantity::setRmax); +PairQuantity::PairQuantity() + : mstructure(emptyStructureAdapter()), + mrmin(0.0), + mrmax(DEFAULT_BONDGENERATOR_RMAX), + mdefaultpairmask(true) { + this->setEvaluatorType(BASIC); + // attributes + this->registerDoubleAttribute("rmin", this, &PairQuantity::getRmin, + &PairQuantity::setRmin); + this->registerDoubleAttribute("rmax", this, &PairQuantity::getRmax, + &PairQuantity::setRmax); } // Public Methods ------------------------------------------------------------ -const QuantityType& PairQuantity::eval() -{ - return this->eval(mstructure); -} - +const QuantityType& PairQuantity::eval() { return this->eval(mstructure); } -const QuantityType& PairQuantity::eval(StructureAdapterPtr stru) -{ - mevaluator->updateValue(*this, stru); - this->finishValue(); - return this->value(); +const QuantityType& PairQuantity::eval(StructureAdapterPtr stru) { + mevaluator->updateValue(*this, stru); + this->finishValue(); + return this->value(); } - -void PairQuantity::setStructure(StructureAdapterPtr stru) -{ - mstructure = stru.get() ? stru : emptyStructureAdapter(); - mstructure->customPQConfig(this); - this->updateMaskData(); - this->resetValue(); +void PairQuantity::setStructure(StructureAdapterPtr stru) { + mstructure = stru.get() ? stru : emptyStructureAdapter(); + mstructure->customPQConfig(this); + this->updateMaskData(); + this->resetValue(); } +StructureAdapterPtr& PairQuantity::getStructure() { return mstructure; } -StructureAdapterPtr& PairQuantity::getStructure() -{ - return mstructure; +const StructureAdapterPtr& PairQuantity::getStructure() const { + return mstructure; } +const QuantityType& PairQuantity::value() const { return mvalue; } -const StructureAdapterPtr& PairQuantity::getStructure() const -{ - return mstructure; +void PairQuantity::mergeParallelData(const string& pdata, int ncpu) { + if (mmergedvaluescount >= ncpu) { + const char* emsg = "Number of merged values exceeds NCPU."; + throw runtime_error(emsg); + } + this->executeParallelMerge(pdata); + ++mmergedvaluescount; + if (mmergedvaluescount == ncpu) this->finishValue(); } - -const QuantityType& PairQuantity::value() const -{ - return mvalue; +string PairQuantity::getParallelData() const { + ostringstream storage(ios::binary); + diffpy::serialization::oarchive oa(storage, ios::binary); + oa << this->value(); + return storage.str(); } - -void PairQuantity::mergeParallelData(const string& pdata, int ncpu) -{ - if (mmergedvaluescount >= ncpu) - { - const char* emsg = "Number of merged values exceeds NCPU."; - throw runtime_error(emsg); - } - this->executeParallelMerge(pdata); - ++mmergedvaluescount; - if (mmergedvaluescount == ncpu) this->finishValue(); -} - - -string PairQuantity::getParallelData() const -{ - ostringstream storage(ios::binary); - diffpy::serialization::oarchive oa(storage, ios::binary); - oa << this->value(); - return storage.str(); +void PairQuantity::setRmin(double rmin) { + if (mrmin != rmin) mticker.click(); + mrmin = rmin; } +const double& PairQuantity::getRmin() const { return mrmin; } -void PairQuantity::setRmin(double rmin) -{ - if (mrmin != rmin) mticker.click(); - mrmin = rmin; +void PairQuantity::setRmax(double rmax) { + if (mrmax != rmax) mticker.click(); + mrmax = rmax; } +const double& PairQuantity::getRmax() const { return mrmax; } -const double& PairQuantity::getRmin() const -{ - return mrmin; +void PairQuantity::setEvaluatorType(PQEvaluatorType evtp) { + if (mevaluator.get() && mevaluator->typeint() == evtp) return; + PQEvaluatorPtr pqev = createPQEvaluator(evtp, mevaluator); + // validate the new evaluator object + try { + pqev->validate(*this); + } catch (logic_error e) { + string emsg("EvaluatorType not supported. "); + emsg += e.what(); + throw invalid_argument(emsg); + } + // validator is good here, use it now. + mevaluator = pqev; + this->resetValue(); } - -void PairQuantity::setRmax(double rmax) -{ - if (mrmax != rmax) mticker.click(); - mrmax = rmax; +PQEvaluatorType PairQuantity::getEvaluatorType() const { + return mevaluator->typeint(); } - -const double& PairQuantity::getRmax() const -{ - return mrmax; +PQEvaluatorType PairQuantity::getEvaluatorTypeUsed() const { + return mevaluator->typeintused(); } - -void PairQuantity::setEvaluatorType(PQEvaluatorType evtp) -{ - if (mevaluator.get() && mevaluator->typeint() == evtp) return; - PQEvaluatorPtr pqev = createPQEvaluator(evtp, mevaluator); - // validate the new evaluator object - try - { - pqev->validate(*this); - } - catch (logic_error e) - { - string emsg("EvaluatorType not supported. "); - emsg += e.what(); - throw invalid_argument(emsg); - } - // validator is good here, use it now. - mevaluator = pqev; - this->resetValue(); +void PairQuantity::setupParallelRun(int cpuindex, int ncpu) { + mevaluator->setupParallelRun(cpuindex, ncpu); } - -PQEvaluatorType PairQuantity::getEvaluatorType() const -{ - return mevaluator->typeint(); +void PairQuantity::maskAllPairs(bool mask) { + bool nochange = minvertpairmask.empty() && msiteallmask.empty() && + mtypemask.empty() && (mdefaultpairmask == mask); + if (!nochange) mticker.click(); + minvertpairmask.clear(); + msiteallmask.clear(); + mtypemask.clear(); + mdefaultpairmask = mask; } - -PQEvaluatorType PairQuantity::getEvaluatorTypeUsed() const -{ - return mevaluator->typeintused(); +void PairQuantity::invertMask() { + mticker.click(); + mdefaultpairmask = !mdefaultpairmask; + unordered_map::iterator mm; + for (mm = msiteallmask.begin(); mm != msiteallmask.end(); ++mm) { + mm->second = !(mm->second); + } + TypeMaskStorage::iterator tpmsk; + for (tpmsk = mtypemask.begin(); tpmsk != mtypemask.end(); ++tpmsk) { + tpmsk->second = !(tpmsk->second); + } } - -void PairQuantity::setupParallelRun(int cpuindex, int ncpu) -{ - mevaluator->setupParallelRun(cpuindex, ncpu); -} - - -void PairQuantity::maskAllPairs(bool mask) -{ - bool nochange = minvertpairmask.empty() && msiteallmask.empty() && - mtypemask.empty() && (mdefaultpairmask == mask); - if (!nochange) mticker.click(); - minvertpairmask.clear(); - msiteallmask.clear(); +void PairQuantity::setPairMask(int i, int j, bool mask) { + if (i < 0) i = ALLATOMSINT; + if (j < 0) j = ALLATOMSINT; + // short circuit for all-all + if (ALLATOMSINT == i && ALLATOMSINT == j) { + this->maskAllPairs(mask); + return; + } + bool modified = false; + // update ticker if we are switching from type-mask mode + if (!mtypemask.empty()) { mtypemask.clear(); - mdefaultpairmask = mask; -} - - -void PairQuantity::invertMask() -{ - mticker.click(); - mdefaultpairmask = !mdefaultpairmask; - unordered_map::iterator mm; - for (mm = msiteallmask.begin(); mm != msiteallmask.end(); ++mm) - { - mm->second = !(mm->second); + modified = true; + } + // handle one ALLATOMSINT argument + if (ALLATOMSINT == i || ALLATOMSINT == j) { + int k = (ALLATOMSINT != i) ? i : j; + pair::iterator, bool> pmm; + pmm = msiteallmask.emplace(k, mask); + if (pmm.second || pmm.first->second != mask) modified = true; + pmm.first->second = mask; + int cntsites = this->countSites(); + for (int l = 0; l < cntsites; ++l) { + this->setPairMaskValue(k, l, mask); } + if (modified) mticker.click(); + return; + } + // here neither i nor j is ALLATOMSINT + unordered_map::iterator ii, jj; + ii = msiteallmask.find(i); + if (ii != msiteallmask.end() && mask != ii->second) { + msiteallmask.erase(ii); + modified = true; + } + jj = msiteallmask.find(j); + if (jj != msiteallmask.end() && mask != jj->second) { + msiteallmask.erase(jj); + modified = true; + } + modified = this->setPairMaskValue(i, j, mask) ? true : modified; + if (modified) mticker.click(); +} + +bool PairQuantity::getPairMask(int i, int j) const { + pair ij = (i > j) ? make_pair(j, i) : make_pair(i, j); + bool rv = minvertpairmask.count(ij) ? !mdefaultpairmask : mdefaultpairmask; + return rv; +} + +void PairQuantity::setTypeMask(string smbli, string smblj, bool mask) { + static string upcaseall; + if (upcaseall.empty()) { + upcaseall = ALLATOMSSTR; + transform(upcaseall.begin(), upcaseall.end(), upcaseall.begin(), ::toupper); + } + // accept "ALL" (upper ALLATOMSSTR) for smbli and smblj + if (upcaseall == smbli) smbli = ALLATOMSSTR; + if (upcaseall == smblj) smblj = ALLATOMSSTR; + pair allall(ALLATOMSSTR, ALLATOMSSTR); + pair smblij = + (smbli > smblj) ? make_pair(smblj, smbli) : make_pair(smbli, smblj); + // short circuit for all-all + if (allall == smblij) { + this->maskAllPairs(mask); + return; + } + // msiteallmask is only relevant for index-based masking. + // clean it up to avoid unexpected effects when switching to index masks. + msiteallmask.clear(); + bool modified = false; + // when all is used, remove all typemask elements with the other type + if (ALLATOMSSTR == smbli || ALLATOMSSTR == smblj) { + const string& sk = (ALLATOMSSTR != smbli) ? smbli : smblj; TypeMaskStorage::iterator tpmsk; - for (tpmsk = mtypemask.begin(); tpmsk != mtypemask.end(); ++tpmsk) - { - tpmsk->second = !(tpmsk->second); - } -} - - -void PairQuantity::setPairMask(int i, int j, bool mask) -{ - if (i < 0) i = ALLATOMSINT; - if (j < 0) j = ALLATOMSINT; - // short circuit for all-all - if (ALLATOMSINT == i && ALLATOMSINT == j) - { - this->maskAllPairs(mask); - return; - } - bool modified = false; - // update ticker if we are switching from type-mask mode - if (!mtypemask.empty()) - { - mtypemask.clear(); - modified = true; - } - // handle one ALLATOMSINT argument - if (ALLATOMSINT == i || ALLATOMSINT == j) - { - int k = (ALLATOMSINT != i) ? i : j; - pair::iterator, bool> pmm; - pmm = msiteallmask.emplace(k, mask); - if (pmm.second || pmm.first->second != mask) modified = true; - pmm.first->second = mask; - int cntsites = this->countSites(); - for (int l = 0; l < cntsites; ++l) - { - this->setPairMaskValue(k, l, mask); - } - if (modified) mticker.click(); - return; - } - // here neither i nor j is ALLATOMSINT - unordered_map::iterator ii, jj; - ii = msiteallmask.find(i); - if (ii != msiteallmask.end() && mask != ii->second) - { - msiteallmask.erase(ii); - modified = true; - } - jj = msiteallmask.find(j); - if (jj != msiteallmask.end() && mask != jj->second) - { - msiteallmask.erase(jj); - modified = true; + TypeMaskStorage::iterator skip = mtypemask.find(smblij); + for (tpmsk = mtypemask.begin(); tpmsk != mtypemask.end();) { + const bool eraseitem = (tpmsk != skip) && (sk == tpmsk->first.first || + sk == tpmsk->first.second); + if (eraseitem) { + if (mask != tpmsk->second) modified = true; + tpmsk = mtypemask.erase(tpmsk); + } else + ++tpmsk; } - modified = this->setPairMaskValue(i, j, mask) ? true : modified; - if (modified) mticker.click(); -} - - -bool PairQuantity::getPairMask(int i, int j) const -{ - pair ij = (i > j) ? make_pair(j, i) : make_pair(i, j); - bool rv = minvertpairmask.count(ij) ? - !mdefaultpairmask : mdefaultpairmask; - return rv; -} - - -void PairQuantity:: -setTypeMask(string smbli, string smblj, bool mask) -{ - static string upcaseall; - if (upcaseall.empty()) - { - upcaseall = ALLATOMSSTR; - transform(upcaseall.begin(), upcaseall.end(), - upcaseall.begin(), ::toupper); - } - // accept "ALL" (upper ALLATOMSSTR) for smbli and smblj - if (upcaseall == smbli) smbli = ALLATOMSSTR; - if (upcaseall == smblj) smblj = ALLATOMSSTR; - pair allall(ALLATOMSSTR, ALLATOMSSTR); - pair smblij = (smbli > smblj) ? - make_pair(smblj, smbli) : make_pair(smbli, smblj); - // short circuit for all-all - if (allall == smblij) - { - this->maskAllPairs(mask); - return; - } - // msiteallmask is only relevant for index-based masking. - // clean it up to avoid unexpected effects when switching to index masks. - msiteallmask.clear(); - bool modified = false; - // when all is used, remove all typemask elements with the other type - if (ALLATOMSSTR == smbli || ALLATOMSSTR == smblj) - { - const string& sk = (ALLATOMSSTR != smbli) ? smbli : smblj; - TypeMaskStorage::iterator tpmsk; - TypeMaskStorage::iterator skip = mtypemask.find(smblij); - for (tpmsk = mtypemask.begin(); tpmsk != mtypemask.end();) - { - const bool eraseitem = (tpmsk != skip) && - (sk == tpmsk->first.first || sk == tpmsk->first.second); - if (eraseitem) - { - if (mask != tpmsk->second) modified = true; - tpmsk = mtypemask.erase(tpmsk); - } - else ++tpmsk; - } - } - pair pmm; - pmm = mtypemask.emplace(smblij, mask); - if (pmm.second || pmm.first->second != mask) modified = true; - pmm.first->second = mask; - if (modified) mticker.click(); -} - - -bool PairQuantity::getTypeMask(const string& smbli, const string& smblj) const -{ - pair smblij = (smbli > smblj) ? - make_pair(smblj, smbli) : make_pair(smbli, smblj); - pair alli = (smbli > ALLATOMSSTR) ? - make_pair(ALLATOMSSTR, smbli) : make_pair(smbli, ALLATOMSSTR); - pair allj = (smblj > ALLATOMSSTR) ? - make_pair(ALLATOMSSTR, smblj) : make_pair(smblj, ALLATOMSSTR); - bool rv = mtypemask.count(smblij) ? mtypemask.at(smblij) : - mtypemask.count(alli) ? mtypemask.at(alli) : - mtypemask.count(allj) ? mtypemask.at(allj) : - mdefaultpairmask; - return rv; + } + pair pmm; + pmm = mtypemask.emplace(smblij, mask); + if (pmm.second || pmm.first->second != mask) modified = true; + pmm.first->second = mask; + if (modified) mticker.click(); +} + +bool PairQuantity::getTypeMask(const string& smbli, const string& smblj) const { + pair smblij = + (smbli > smblj) ? make_pair(smblj, smbli) : make_pair(smbli, smblj); + pair alli = (smbli > ALLATOMSSTR) + ? make_pair(ALLATOMSSTR, smbli) + : make_pair(smbli, ALLATOMSSTR); + pair allj = (smblj > ALLATOMSSTR) + ? make_pair(ALLATOMSSTR, smblj) + : make_pair(smblj, ALLATOMSSTR); + bool rv = mtypemask.count(smblij) ? mtypemask.at(smblij) + : mtypemask.count(alli) ? mtypemask.at(alli) + : mtypemask.count(allj) ? mtypemask.at(allj) + : mdefaultpairmask; + return rv; } // Protected Methods --------------------------------------------------------- -void PairQuantity::resizeValue(size_t sz) -{ - mvalue.resize(sz); -} - - -void PairQuantity::resetValue() -{ - mmergedvaluescount = 0; - fill(mvalue.begin(), mvalue.end(), 0.0); -} - +void PairQuantity::resizeValue(size_t sz) { mvalue.resize(sz); } -void PairQuantity::configureBondGenerator(BaseBondGenerator& bnds) const -{ - bnds.setRmin(this->getRmin()); - bnds.setRmax(this->getRmax()); +void PairQuantity::resetValue() { + mmergedvaluescount = 0; + fill(mvalue.begin(), mvalue.end(), 0.0); } - -void PairQuantity::executeParallelMerge(const string& pdata) -{ - istringstream storage(pdata, ios::binary); - diffpy::serialization::iarchive ia(storage, ios::binary); - QuantityType pvalue; - ia >> pvalue; - if (pvalue.size() != mvalue.size()) - { - throw invalid_argument("Merged data array must have the same size."); - } - transform(mvalue.begin(), mvalue.end(), pvalue.begin(), - mvalue.begin(), plus()); +void PairQuantity::configureBondGenerator(BaseBondGenerator& bnds) const { + bnds.setRmin(this->getRmin()); + bnds.setRmax(this->getRmax()); } - -int PairQuantity::countSites() const -{ - int rv = mstructure.get() ? mstructure->countSites() : 0; - return rv; +void PairQuantity::executeParallelMerge(const string& pdata) { + istringstream storage(pdata, ios::binary); + diffpy::serialization::iarchive ia(storage, ios::binary); + QuantityType pvalue; + ia >> pvalue; + if (pvalue.size() != mvalue.size()) { + throw invalid_argument("Merged data array must have the same size."); + } + transform(mvalue.begin(), mvalue.end(), pvalue.begin(), mvalue.begin(), + plus()); } - -bool PairQuantity::hasMask() const -{ - bool rv = !(mdefaultpairmask && minvertpairmask.empty() && - msiteallmask.empty() && mtypemask.empty()); - return rv; +int PairQuantity::countSites() const { + int rv = mstructure.get() ? mstructure->countSites() : 0; + return rv; } - -bool PairQuantity::hasPairMask() const -{ - bool rv = this->hasMask() && !this->hasTypeMask(); - return rv; +bool PairQuantity::hasMask() const { + bool rv = !(mdefaultpairmask && minvertpairmask.empty() && + msiteallmask.empty() && mtypemask.empty()); + return rv; } - -bool PairQuantity::hasTypeMask() const -{ - return !mtypemask.empty(); +bool PairQuantity::hasPairMask() const { + bool rv = this->hasMask() && !this->hasTypeMask(); + return rv; } +bool PairQuantity::hasTypeMask() const { return !mtypemask.empty(); } -void PairQuantity::stashPartialValue() -{ - const char* emsg = - "stashPartialValue() is not defined in the calculator class."; - throw logic_error(emsg); +void PairQuantity::stashPartialValue() { + const char* emsg = + "stashPartialValue() is not defined in the calculator class."; + throw logic_error(emsg); } - -void PairQuantity::restorePartialValue() -{ - const char* emsg = - "restorePartialValue() is not defined in the calculator class."; - throw logic_error(emsg); +void PairQuantity::restorePartialValue() { + const char* emsg = + "restorePartialValue() is not defined in the calculator class."; + throw logic_error(emsg); } // Private Methods ----------------------------------------------------------- -void PairQuantity::updateMaskData() -{ - int cntsites = this->countSites(); - // Propagate masks with ALLATOMSINT to all valid indices. - if (mtypemask.empty()) - { - unordered_map::const_iterator ia; - for (ia = msiteallmask.begin(); ia != msiteallmask.end(); ++ia) - { - for (int j = 0; j < cntsites; ++j) - { - this->setPairMaskValue(ia->first, j, ia->second); - } - } +void PairQuantity::updateMaskData() { + int cntsites = this->countSites(); + // Propagate masks with ALLATOMSINT to all valid indices. + if (mtypemask.empty()) { + unordered_map::const_iterator ia; + for (ia = msiteallmask.begin(); ia != msiteallmask.end(); ++ia) { + for (int j = 0; j < cntsites; ++j) { + this->setPairMaskValue(ia->first, j, ia->second); + } } - // For type masking propagate atom types to corresponding atom indices. - else - { - // build a list of indices per each unique atom type - unordered_map< string, list > siteindices; - for (int i = 0; i < cntsites; ++i) - { - const string& smbl = mstructure->siteAtomType(i); - siteindices[smbl].push_back(i); - siteindices[ALLATOMSSTR].push_back(i); - } - // rebuild minvertpairmask according to mtypemask - minvertpairmask.clear(); - TypeMaskStorage::const_iterator tpmsk; - // build a list of type masks with all-masks at the begining - list< pair > orderedpairs; - for (tpmsk = mtypemask.begin(); tpmsk != mtypemask.end(); ++tpmsk) - { - bool hasall = (ALLATOMSSTR == tpmsk->first.first || - ALLATOMSSTR == tpmsk->first.second); - if (hasall) orderedpairs.push_front(tpmsk->first); - else orderedpairs.push_back(tpmsk->first); - } - list< pair >::const_iterator tpp; - for (tpp = orderedpairs.begin(); tpp != orderedpairs.end(); ++tpp) - { - const list& isites = siteindices[tpp->first]; - const list& jsites = siteindices[tpp->second]; - bool msk = mtypemask.at(*tpp); - list::const_iterator ii, jj; - for (ii = isites.begin(); ii != isites.end(); ++ii) - { - jj = (&isites == &jsites) ? ii : jsites.begin(); - for (; jj != jsites.end(); ++jj) - { - this->setPairMaskValue(*ii, *jj, msk); - } - } + } + // For type masking propagate atom types to corresponding atom indices. + else { + // build a list of indices per each unique atom type + unordered_map > siteindices; + for (int i = 0; i < cntsites; ++i) { + const string& smbl = mstructure->siteAtomType(i); + siteindices[smbl].push_back(i); + siteindices[ALLATOMSSTR].push_back(i); + } + // rebuild minvertpairmask according to mtypemask + minvertpairmask.clear(); + TypeMaskStorage::const_iterator tpmsk; + // build a list of type masks with all-masks at the beginning + list > orderedpairs; + for (tpmsk = mtypemask.begin(); tpmsk != mtypemask.end(); ++tpmsk) { + bool hasall = (ALLATOMSSTR == tpmsk->first.first || + ALLATOMSSTR == tpmsk->first.second); + if (hasall) + orderedpairs.push_front(tpmsk->first); + else + orderedpairs.push_back(tpmsk->first); + } + list >::const_iterator tpp; + for (tpp = orderedpairs.begin(); tpp != orderedpairs.end(); ++tpp) { + const list& isites = siteindices[tpp->first]; + const list& jsites = siteindices[tpp->second]; + bool msk = mtypemask.at(*tpp); + list::const_iterator ii, jj; + for (ii = isites.begin(); ii != isites.end(); ++ii) { + jj = (&isites == &jsites) ? ii : jsites.begin(); + for (; jj != jsites.end(); ++jj) { + this->setPairMaskValue(*ii, *jj, msk); } + } } + } } - -bool PairQuantity::setPairMaskValue(int i, int j, bool mask) -{ - assert(i >= 0 && j >= 0); - pair ij = (i > j) ? make_pair(j, i) : make_pair(i, j); - bool rv; - rv = (mask == mdefaultpairmask) ? - minvertpairmask.erase(ij) : - minvertpairmask.insert(ij).second; - return rv; +bool PairQuantity::setPairMaskValue(int i, int j, bool mask) { + assert(i >= 0 && j >= 0); + pair ij = (i > j) ? make_pair(j, i) : make_pair(i, j); + bool rv; + rv = (mask == mdefaultpairmask) ? minvertpairmask.erase(ij) + : minvertpairmask.insert(ij).second; + return rv; } // Other functions ----------------------------------------------------------- @@ -493,16 +386,15 @@ bool PairQuantity::setPairMaskValue(int i, int j, bool mask) /// The purpose of this function is to support Python pickling of /// PairQuantity objects that hold Python-derived StructureAdapter classes. /// Use it only if you absolutely have to and you know what you do. -StructureAdapterPtr -replacePairQuantityStructure(PairQuantity& pq, StructureAdapterPtr stru) -{ - StructureAdapterPtr rv = pq.mstructure; - pq.mstructure = stru; - return rv; +StructureAdapterPtr replacePairQuantityStructure(PairQuantity& pq, + StructureAdapterPtr stru) { + StructureAdapterPtr rv = pq.mstructure; + pq.mstructure = stru; + return rv; } -} // namespace srreal -} // namespace diffpy +} // namespace srreal +} // namespace diffpy // Serialization ------------------------------------------------------------- diff --git a/src/diffpy/srreal/PairQuantity.hpp b/src/diffpy/srreal/PairQuantity.hpp index 04d8c841..41974811 100644 --- a/src/diffpy/srreal/PairQuantity.hpp +++ b/src/diffpy/srreal/PairQuantity.hpp @@ -1,20 +1,20 @@ /***************************************************************************** -* -* 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.txt for a list of people who contributed. -* See LICENSE_DANSE.txt for license information. -* -****************************************************************************** -* -* class PairQuantity -- general implementation of pair quantity calculator -* -*****************************************************************************/ + * + * 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.txt for a list of people who contributed. + * See LICENSE_DANSE.txt for license information. + * + ****************************************************************************** + * + * class PairQuantity -- general implementation of pair quantity calculator + * + *****************************************************************************/ #ifndef PAIRQUANTITY_HPP_INCLUDED #define PAIRQUANTITY_HPP_INCLUDED @@ -39,133 +39,124 @@ namespace srreal { class BaseBondGenerator; -class DLL_EXPORT PairQuantity : public diffpy::Attributes -{ - public: - - // class constants - static const int ALLATOMSINT; - static const std::string ALLATOMSSTR; - - // constructor - PairQuantity(); - virtual ~PairQuantity() { } - - // methods - const QuantityType& eval(); - template const QuantityType& eval(const T&); - const QuantityType& eval(StructureAdapterPtr); - const QuantityType& value() const; - void mergeParallelData(const std::string& pdata, int ncpu); - virtual std::string getParallelData() const; - - // configuration - template void setStructure(const T&); - void setStructure(StructureAdapterPtr); - StructureAdapterPtr& getStructure(); - const StructureAdapterPtr& getStructure() const; - virtual void setRmin(double); - const double& getRmin() const; - virtual void setRmax(double); - const double& getRmax() const; - void setEvaluatorType(PQEvaluatorType evtp); - PQEvaluatorType getEvaluatorType() const; - PQEvaluatorType getEvaluatorTypeUsed() const; - void setupParallelRun(int cpuindex, int ncpu); - void maskAllPairs(bool mask); - void invertMask(); - void setPairMask(int i, int j, bool mask); - bool getPairMask(int i, int j) const; - void setTypeMask(std::string, std::string, bool mask); - bool getTypeMask(const std::string&, const std::string&) const; - - // ticker for any updates in configuration - virtual eventticker::EventTicker& ticker() const { return mticker; } - - protected: - - friend class PQEvaluatorBasic; - friend class PQEvaluatorOptimized; - friend DLL_EXPORT StructureAdapterPtr - replacePairQuantityStructure(PairQuantity&, StructureAdapterPtr); - - // methods - virtual void resizeValue(size_t); - virtual void resetValue(); - virtual void configureBondGenerator(BaseBondGenerator&) const; - virtual void addPairContribution(const BaseBondGenerator&, int) { } - virtual void executeParallelMerge(const std::string& pdata); - virtual void finishValue() { } - int countSites() const; - // support methods for PQEvaluatorOptimized - bool hasMask() const; - bool hasPairMask() const; - bool hasTypeMask() const; - virtual void stashPartialValue(); - virtual void restorePartialValue(); - - // data - typedef std::unordered_set< - std::pair, - boost::hash< std::pair > - > PairMaskStorage; - typedef std::unordered_map< - std::pair, bool, - boost::hash< std::pair > - > TypeMaskStorage; - QuantityType mvalue; - StructureAdapterPtr mstructure; - double mrmin; - double mrmax; - PQEvaluatorPtr mevaluator; - bool mdefaultpairmask; - PairMaskStorage minvertpairmask; - std::unordered_map msiteallmask; - TypeMaskStorage mtypemask; - int mmergedvaluescount; - mutable eventticker::EventTicker mticker; - - private: - - // methods - void updateMaskData(); - bool setPairMaskValue(int i, int j, bool mask); - - // serialization - friend class boost::serialization::access; - template - void serialize(Archive& ar, const unsigned int version) - { - ar & mvalue; - ar & mstructure; - ar & mrmin; - ar & mrmax; - ar & mevaluator; - ar & mdefaultpairmask; - ar & minvertpairmask; - ar & msiteallmask; - ar & mtypemask; - ar & mmergedvaluescount; - ar & mticker; - } - +class DLL_EXPORT PairQuantity : public diffpy::Attributes { + public: + // class constants + static const int ALLATOMSINT; + static const std::string ALLATOMSSTR; + + // constructor + PairQuantity(); + virtual ~PairQuantity() {} + + // methods + const QuantityType& eval(); + template + const QuantityType& eval(const T&); + const QuantityType& eval(StructureAdapterPtr); + const QuantityType& value() const; + void mergeParallelData(const std::string& pdata, int ncpu); + virtual std::string getParallelData() const; + + // configuration + template + void setStructure(const T&); + void setStructure(StructureAdapterPtr); + StructureAdapterPtr& getStructure(); + const StructureAdapterPtr& getStructure() const; + virtual void setRmin(double); + const double& getRmin() const; + virtual void setRmax(double); + const double& getRmax() const; + void setEvaluatorType(PQEvaluatorType evtp); + PQEvaluatorType getEvaluatorType() const; + PQEvaluatorType getEvaluatorTypeUsed() const; + void setupParallelRun(int cpuindex, int ncpu); + void maskAllPairs(bool mask); + void invertMask(); + void setPairMask(int i, int j, bool mask); + bool getPairMask(int i, int j) const; + void setTypeMask(std::string, std::string, bool mask); + bool getTypeMask(const std::string&, const std::string&) const; + + // ticker for any updates in configuration + virtual eventticker::EventTicker& ticker() const { return mticker; } + + protected: + friend class PQEvaluatorBasic; + friend class PQEvaluatorOptimized; + friend DLL_EXPORT StructureAdapterPtr + replacePairQuantityStructure(PairQuantity&, StructureAdapterPtr); + + // methods + virtual void resizeValue(size_t); + virtual void resetValue(); + virtual void configureBondGenerator(BaseBondGenerator&) const; + virtual void addPairContribution(const BaseBondGenerator&, int) {} + virtual void executeParallelMerge(const std::string& pdata); + virtual void finishValue() {} + int countSites() const; + // support methods for PQEvaluatorOptimized + bool hasMask() const; + bool hasPairMask() const; + bool hasTypeMask() const; + virtual void stashPartialValue(); + virtual void restorePartialValue(); + + // data + typedef std::unordered_set, + boost::hash > > + PairMaskStorage; + typedef std::unordered_map, bool, + boost::hash > > + TypeMaskStorage; + QuantityType mvalue; + StructureAdapterPtr mstructure; + double mrmin; + double mrmax; + PQEvaluatorPtr mevaluator; + bool mdefaultpairmask; + PairMaskStorage minvertpairmask; + std::unordered_map msiteallmask; + TypeMaskStorage mtypemask; + int mmergedvaluescount; + mutable eventticker::EventTicker mticker; + + private: + // methods + void updateMaskData(); + bool setPairMaskValue(int i, int j, bool mask); + + // serialization + friend class boost::serialization::access; + template + void serialize(Archive& ar, const unsigned int version) { + ar & mvalue; + ar & mstructure; + ar & mrmin; + ar & mrmax; + ar & mevaluator; + ar & mdefaultpairmask; + ar & minvertpairmask; + ar & msiteallmask; + ar & mtypemask; + ar & mmergedvaluescount; + ar & mticker; + } }; // Template Public Methods --------------------------------------------------- template -const QuantityType& PairQuantity::eval(const T& stru) -{ - StructureAdapterPtr pstru = convertToStructureAdapter(stru); - return this->eval(pstru); +const QuantityType& PairQuantity::eval(const T& stru) { + StructureAdapterPtr pstru = convertToStructureAdapter(stru); + return this->eval(pstru); } - template -void PairQuantity::setStructure(const T& stru) -{ - StructureAdapterPtr pstru = convertToStructureAdapter(stru); - this->setStructure(pstru); +void PairQuantity::setStructure(const T& stru) { + StructureAdapterPtr pstru = convertToStructureAdapter(stru); + this->setStructure(pstru); } // Other functions ----------------------------------------------------------- @@ -176,8 +167,8 @@ void PairQuantity::setStructure(const T& stru) DLL_EXPORT StructureAdapterPtr replacePairQuantityStructure(PairQuantity& pq, StructureAdapterPtr stru); -} // namespace srreal -} // namespace diffpy +} // namespace srreal +} // namespace diffpy // Serialization ------------------------------------------------------------- diff --git a/src/diffpy/srreal/PeakProfile.cpp b/src/diffpy/srreal/PeakProfile.cpp index e3211127..555301a3 100644 --- a/src/diffpy/srreal/PeakProfile.cpp +++ b/src/diffpy/srreal/PeakProfile.cpp @@ -1,20 +1,20 @@ /***************************************************************************** -* -* 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.txt for a list of people who contributed. -* See LICENSE_DANSE.txt for license information. -* -****************************************************************************** -* -* class PeakProfile -- base class for calculation of peak profiles. -* -*****************************************************************************/ + * + * 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.txt for a list of people who contributed. + * See LICENSE_DANSE.txt for license information. + * + ****************************************************************************** + * + * class PeakProfile -- base class for calculation of peak profiles. + * + *****************************************************************************/ #include #include @@ -33,28 +33,23 @@ namespace srreal { // Constructors -------------------------------------------------------------- -PeakProfile::PeakProfile() : mprecision(0.0) -{ - this->registerDoubleAttribute("peakprecision", - this, &PeakProfile::getPrecision, &PeakProfile::setPrecision); +PeakProfile::PeakProfile() : mprecision(0.0) { + this->registerDoubleAttribute("peakprecision", this, + &PeakProfile::getPrecision, + &PeakProfile::setPrecision); } // Public Methods ------------------------------------------------------------ -void PeakProfile::setPrecision(double eps) -{ - if (mprecision != eps) mticker.click(); - mprecision = eps; +void PeakProfile::setPrecision(double eps) { + if (mprecision != eps) mticker.click(); + mprecision = eps; } +const double& PeakProfile::getPrecision() const { return mprecision; } -const double& PeakProfile::getPrecision() const -{ - return mprecision; -} - -} // namespace srreal -} // namespace diffpy +} // namespace srreal +} // namespace diffpy // Serialization ------------------------------------------------------------- diff --git a/src/diffpy/srreal/PeakProfile.hpp b/src/diffpy/srreal/PeakProfile.hpp index c5342012..ce337efd 100644 --- a/src/diffpy/srreal/PeakProfile.hpp +++ b/src/diffpy/srreal/PeakProfile.hpp @@ -1,25 +1,25 @@ /***************************************************************************** -* -* 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.txt for a list of people who contributed. -* See LICENSE_DANSE.txt for license information. -* -****************************************************************************** -* -* class PeakProfile -- base class for calculation of peak profiles. -* When possible total integrated area of the profile should eqaul 1. -* The operator()(x, fwhm) returns amplitude of a zero-centered profile. -* Methods xboundlo(fwhm), xboundhi(fwhm) return low and high x-boundaries, -* where amplitude relative to the maximum becomes smaller than precision -* set by setPrecision(). -* -*****************************************************************************/ + * + * 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.txt for a list of people who contributed. + * See LICENSE_DANSE.txt for license information. + * + ****************************************************************************** + * + * class PeakProfile -- base class for calculation of peak profiles. + * When possible total integrated area of the profile should equal 1. + * The operator()(x, fwhm) returns amplitude of a zero-centered profile. + * Methods xboundlo(fwhm), xboundhi(fwhm) return low and high x-boundaries, + * where amplitude relative to the maximum becomes smaller than precision + * set by setPrecision(). + * + *****************************************************************************/ #ifndef PEAKPROFILE_HPP_INCLUDED #define PEAKPROFILE_HPP_INCLUDED @@ -38,46 +38,39 @@ namespace diffpy { namespace srreal { -class DLL_EXPORT PeakProfile : - public diffpy::Attributes, - public diffpy::HasClassRegistry -{ - public: - - // constructors - PeakProfile(); - // methods - virtual double operator()(double x, double fwhm) const = 0; - virtual double xboundlo(double fwhm) const = 0; - virtual double xboundhi(double fwhm) const = 0; - virtual void setPrecision(double eps); - const double& getPrecision() const; - virtual eventticker::EventTicker& ticker() const { return mticker; } - - protected: - - // data - mutable eventticker::EventTicker mticker; - - private: - - // data - double mprecision; - - // serialization - friend class boost::serialization::access; - template - void serialize(Archive& ar, const unsigned int version) - { - ar & mticker & mprecision; - } - +class DLL_EXPORT PeakProfile : public diffpy::Attributes, + public diffpy::HasClassRegistry { + public: + // constructors + PeakProfile(); + // methods + virtual double operator()(double x, double fwhm) const = 0; + virtual double xboundlo(double fwhm) const = 0; + virtual double xboundhi(double fwhm) const = 0; + virtual void setPrecision(double eps); + const double& getPrecision() const; + virtual eventticker::EventTicker& ticker() const { return mticker; } + + protected: + // data + mutable eventticker::EventTicker mticker; + + private: + // data + double mprecision; + + // serialization + friend class boost::serialization::access; + template + void serialize(Archive& ar, const unsigned int version) { + ar & mticker & mprecision; + } }; typedef PeakProfile::SharedPtr PeakProfilePtr; -} // namespace srreal -} // namespace diffpy +} // namespace srreal +} // namespace diffpy // Serialization ------------------------------------------------------------- diff --git a/src/diffpy/srreal/PeakWidthModel.cpp b/src/diffpy/srreal/PeakWidthModel.cpp index a9bb766c..8cbe3b15 100644 --- a/src/diffpy/srreal/PeakWidthModel.cpp +++ b/src/diffpy/srreal/PeakWidthModel.cpp @@ -1,28 +1,28 @@ /***************************************************************************** -* -* 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.txt for a list of people who contributed. -* See LICENSE_DANSE.txt for license information. -* -****************************************************************************** -* -* class PeakWidthModel -- base class for calculation of peak widths. -* -*****************************************************************************/ + * + * 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.txt for a list of people who contributed. + * See LICENSE_DANSE.txt for license information. + * + ****************************************************************************** + * + * class PeakWidthModel -- base class for calculation of peak widths. + * + *****************************************************************************/ #include #include #include #include -using std::string; using diffpy::validators::ensureNonNull; +using std::string; namespace diffpy { @@ -33,41 +33,30 @@ namespace srreal { // class PeakWidthModelOwner ------------------------------------------------- -void PeakWidthModelOwner::setPeakWidthModel(PeakWidthModelPtr pwm) -{ - ensureNonNull("PeakWidthModel", pwm); - if (mpwmodel != pwm) mprivateticker.click(); - mpwmodel = pwm; +void PeakWidthModelOwner::setPeakWidthModel(PeakWidthModelPtr pwm) { + ensureNonNull("PeakWidthModel", pwm); + if (mpwmodel != pwm) mprivateticker.click(); + mpwmodel = pwm; } - -void PeakWidthModelOwner::setPeakWidthModelByType(const string& tp) -{ - mpwmodel = PeakWidthModel::createByType(tp); - mprivateticker.click(); +void PeakWidthModelOwner::setPeakWidthModelByType(const string& tp) { + mpwmodel = PeakWidthModel::createByType(tp); + mprivateticker.click(); } +PeakWidthModelPtr& PeakWidthModelOwner::getPeakWidthModel() { return mpwmodel; } -PeakWidthModelPtr& PeakWidthModelOwner::getPeakWidthModel() -{ - return mpwmodel; +const PeakWidthModelPtr& PeakWidthModelOwner::getPeakWidthModel() const { + return mpwmodel; } - -const PeakWidthModelPtr& PeakWidthModelOwner::getPeakWidthModel() const -{ - return mpwmodel; -} - - -eventticker::EventTicker& PeakWidthModelOwner::ticker() const -{ - if (mpwmodel) mprivateticker.updateFrom(mpwmodel->ticker()); - return mprivateticker; +eventticker::EventTicker& PeakWidthModelOwner::ticker() const { + if (mpwmodel) mprivateticker.updateFrom(mpwmodel->ticker()); + return mprivateticker; } -} // namespace srreal -} // namespace diffpy +} // namespace srreal +} // namespace diffpy // Serialization ------------------------------------------------------------- diff --git a/src/diffpy/srreal/PeakWidthModel.hpp b/src/diffpy/srreal/PeakWidthModel.hpp index 3e917d12..e615da22 100644 --- a/src/diffpy/srreal/PeakWidthModel.hpp +++ b/src/diffpy/srreal/PeakWidthModel.hpp @@ -1,26 +1,26 @@ /***************************************************************************** -* -* 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.txt for a list of people who contributed. -* See LICENSE_DANSE.txt for license information. -* -****************************************************************************** -* -* class PeakWidthModel -- base class for calculation of peak widths. -* The calculate function takes a BondGenerator instance and -* returns full width at half maximum, based on peak model parameters -* and anisotropic displacement parameters of atoms in the pair. -* -* class PeakWidthModelOwner -- to be used as a base class for classes -* that own PeakWidthModel -* -*****************************************************************************/ + * + * 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.txt for a list of people who contributed. + * See LICENSE_DANSE.txt for license information. + * + ****************************************************************************** + * + * class PeakWidthModel -- base class for calculation of peak widths. + * The calculate function takes a BondGenerator instance and + * returns full width at half maximum, based on peak model parameters + * and anisotropic displacement parameters of atoms in the pair. + * + * class PeakWidthModelOwner -- to be used as a base class for classes + * that own PeakWidthModel + * + *****************************************************************************/ #ifndef PEAKWIDTHMODEL_HPP_INCLUDED #define PEAKWIDTHMODEL_HPP_INCLUDED @@ -40,69 +40,56 @@ namespace diffpy { namespace srreal { -class DLL_EXPORT PeakWidthModel : - public diffpy::Attributes, - public diffpy::HasClassRegistry -{ - public: - - // methods - virtual double calculate(const BaseBondGenerator&) const = 0; - virtual double maxWidth(StructureAdapterPtr, - double rmin, double rmax) const = 0; - virtual eventticker::EventTicker& ticker() const { return mticker; } - - protected: - - // data - mutable eventticker::EventTicker mticker; - - private: - - // serialization - friend class boost::serialization::access; - template - void serialize(Archive& ar, const unsigned int version) - { - ar & mticker; - } - +class DLL_EXPORT PeakWidthModel + : public diffpy::Attributes, + public diffpy::HasClassRegistry { + public: + // methods + virtual double calculate(const BaseBondGenerator&) const = 0; + virtual double maxWidth(StructureAdapterPtr, double rmin, + double rmax) const = 0; + virtual eventticker::EventTicker& ticker() const { return mticker; } + + protected: + // data + mutable eventticker::EventTicker mticker; + + private: + // serialization + friend class boost::serialization::access; + template + void serialize(Archive& ar, const unsigned int version) { + ar & mticker; + } }; - typedef PeakWidthModel::SharedPtr PeakWidthModelPtr; - -class DLL_EXPORT PeakWidthModelOwner -{ - public: - - // PDF peak width configuration - void setPeakWidthModel(PeakWidthModelPtr); - void setPeakWidthModelByType(const std::string& tp); - PeakWidthModelPtr& getPeakWidthModel(); - const PeakWidthModelPtr& getPeakWidthModel() const; - eventticker::EventTicker& ticker() const; - - private: - - // data - PeakWidthModelPtr mpwmodel; - mutable eventticker::EventTicker mprivateticker; - - // serialization - friend class boost::serialization::access; - - template - void serialize(Archive& ar, const unsigned int version) - { - ar & mpwmodel & mprivateticker; - } - +class DLL_EXPORT PeakWidthModelOwner { + public: + // PDF peak width configuration + void setPeakWidthModel(PeakWidthModelPtr); + void setPeakWidthModelByType(const std::string& tp); + PeakWidthModelPtr& getPeakWidthModel(); + const PeakWidthModelPtr& getPeakWidthModel() const; + eventticker::EventTicker& ticker() const; + + private: + // data + PeakWidthModelPtr mpwmodel; + mutable eventticker::EventTicker mprivateticker; + + // serialization + friend class boost::serialization::access; + + template + void serialize(Archive& ar, const unsigned int version) { + ar & mpwmodel & mprivateticker; + } }; -} // namespace srreal -} // namespace diffpy +} // namespace srreal +} // namespace diffpy // Serialization ------------------------------------------------------------- diff --git a/src/diffpy/srreal/PeriodicStructureAdapter.cpp b/src/diffpy/srreal/PeriodicStructureAdapter.cpp index 80fdd5f5..01f5cba6 100644 --- a/src/diffpy/srreal/PeriodicStructureAdapter.cpp +++ b/src/diffpy/srreal/PeriodicStructureAdapter.cpp @@ -1,23 +1,23 @@ /***************************************************************************** -* -* libdiffpy Complex Modeling Initiative -* (c) 2013 Brookhaven Science Associates, -* Brookhaven National Laboratory. -* All rights reserved. -* -* File coded by: Pavol Juhas -* -* See AUTHORS.txt for a list of people who contributed. -* See LICENSE.txt for license information. -* -****************************************************************************** -* -* class PeriodicStructureAdapter -- universal adapter for structure with -* periodic boundary conditions that has no space group symmetry -* -* class PeriodicStructureBondGenerator -- bond generator -* -*****************************************************************************/ + * + * libdiffpy Complex Modeling Initiative + * (c) 2013 Brookhaven Science Associates, + * Brookhaven National Laboratory. + * All rights reserved. + * + * File coded by: Pavol Juhas + * + * See AUTHORS.txt for a list of people who contributed. + * See LICENSE.txt for license information. + * + ****************************************************************************** + * + * class PeriodicStructureAdapter -- universal adapter for structure with + * periodic boundary conditions that has no space group symmetry + * + * class PeriodicStructureBondGenerator -- bond generator + * + *****************************************************************************/ #include @@ -37,93 +37,69 @@ namespace srreal { // Public Methods ------------------------------------------------------------ -StructureAdapterPtr PeriodicStructureAdapter::clone() const -{ - StructureAdapterPtr rv(new PeriodicStructureAdapter(*this)); - return rv; +StructureAdapterPtr PeriodicStructureAdapter::clone() const { + StructureAdapterPtr rv(new PeriodicStructureAdapter(*this)); + return rv; } - -BaseBondGeneratorPtr PeriodicStructureAdapter::createBondGenerator() const -{ - BaseBondGeneratorPtr bnds( - new PeriodicStructureBondGenerator(shared_from_this())); - return bnds; -} - - -double PeriodicStructureAdapter::numberDensity() const -{ - const Lattice& L = this->getLattice(); - double rv = this->totalOccupancy() / L.volume(); - return rv; +BaseBondGeneratorPtr PeriodicStructureAdapter::createBondGenerator() const { + BaseBondGeneratorPtr bnds( + new PeriodicStructureBondGenerator(shared_from_this())); + return bnds; } - -StructureDifference -PeriodicStructureAdapter::diff(StructureAdapterConstPtr other) const -{ - StructureDifference sd = this->StructureAdapter::diff(other); - if (sd.stru0 == sd.stru1) return sd; - typedef boost::shared_ptr PPtr; - PPtr pother = boost::dynamic_pointer_cast(other); - if (!pother) return sd; - assert(pother == sd.stru1); - if (this->getLattice() != pother->getLattice()) return sd; - sd = this->AtomicStructureAdapter::diff(other); - return sd; +double PeriodicStructureAdapter::numberDensity() const { + const Lattice& L = this->getLattice(); + double rv = this->totalOccupancy() / L.volume(); + return rv; } - -void PeriodicStructureAdapter::setLatPar( - double a, double b, double c, - double alphadeg, double betadeg, double gammadeg) -{ - mlattice.setLatPar(a, b, c, alphadeg, betadeg, gammadeg); +StructureDifference PeriodicStructureAdapter::diff( + StructureAdapterConstPtr other) const { + StructureDifference sd = this->StructureAdapter::diff(other); + if (sd.stru0 == sd.stru1) return sd; + typedef boost::shared_ptr PPtr; + PPtr pother = boost::dynamic_pointer_cast(other); + if (!pother) return sd; + assert(pother == sd.stru1); + if (this->getLattice() != pother->getLattice()) return sd; + sd = this->AtomicStructureAdapter::diff(other); + return sd; } - -const Lattice& PeriodicStructureAdapter::getLattice() const -{ - return mlattice; +void PeriodicStructureAdapter::setLatPar(double a, double b, double c, + double alphadeg, double betadeg, + double gammadeg) { + mlattice.setLatPar(a, b, c, alphadeg, betadeg, gammadeg); } +const Lattice& PeriodicStructureAdapter::getLattice() const { return mlattice; } -void PeriodicStructureAdapter::toCartesian(Atom& a) const -{ - const Lattice& L = this->getLattice(); - a.xyz_cartn = L.cartesian(a.xyz_cartn); - a.uij_cartn = L.cartesianMatrix(a.uij_cartn); +void PeriodicStructureAdapter::toCartesian(Atom& a) const { + const Lattice& L = this->getLattice(); + a.xyz_cartn = L.cartesian(a.xyz_cartn); + a.uij_cartn = L.cartesianMatrix(a.uij_cartn); } - -void PeriodicStructureAdapter::toFractional(Atom& a) const -{ - const Lattice& L = this->getLattice(); - a.xyz_cartn = L.fractional(a.xyz_cartn); - a.uij_cartn = L.fractionalMatrix(a.uij_cartn); +void PeriodicStructureAdapter::toFractional(Atom& a) const { + const Lattice& L = this->getLattice(); + a.xyz_cartn = L.fractional(a.xyz_cartn); + a.uij_cartn = L.fractionalMatrix(a.uij_cartn); } // Comparison functions ------------------------------------------------------ -bool operator==( - const PeriodicStructureAdapter& stru0, - const PeriodicStructureAdapter& stru1) -{ - const AtomicStructureAdapter& astru0 = stru0; - const AtomicStructureAdapter& astru1 = stru1; - bool rv = - (astru0 == astru1) && - (stru0.getLattice() == stru1.getLattice()); - return rv; +bool operator==(const PeriodicStructureAdapter& stru0, + const PeriodicStructureAdapter& stru1) { + const AtomicStructureAdapter& astru0 = stru0; + const AtomicStructureAdapter& astru1 = stru1; + bool rv = (astru0 == astru1) && (stru0.getLattice() == stru1.getLattice()); + return rv; } - -bool operator!=( - const PeriodicStructureAdapter& stru0, - const PeriodicStructureAdapter& stru1) -{ - return !(stru0 == stru1); +bool operator!=(const PeriodicStructureAdapter& stru0, + const PeriodicStructureAdapter& stru1) { + return !(stru0 == stru1); } ////////////////////////////////////////////////////////////////////////////// @@ -133,107 +109,92 @@ bool operator!=( // Constructor --------------------------------------------------------------- PeriodicStructureBondGenerator::PeriodicStructureBondGenerator( - StructureAdapterConstPtr adpt) : BaseBondGenerator(adpt) -{ - mpstructure = dynamic_cast(adpt.get()); - assert(mpstructure); - int cntsites = mpstructure->countSites(); - mcartesian_positions_uc.reserve(cntsites); - const Lattice& L = mpstructure->getLattice(); - PeriodicStructureAdapter::const_iterator ai = mpstructure->begin(); - R3::Vector xyzc; - for (; ai != mpstructure->end(); ++ai) - { - xyzc = L.ucvCartesian(ai->xyz_cartn); - mcartesian_positions_uc.push_back(xyzc); - } + StructureAdapterConstPtr adpt) + : BaseBondGenerator(adpt) { + mpstructure = dynamic_cast(adpt.get()); + assert(mpstructure); + int cntsites = mpstructure->countSites(); + mcartesian_positions_uc.reserve(cntsites); + const Lattice& L = mpstructure->getLattice(); + PeriodicStructureAdapter::const_iterator ai = mpstructure->begin(); + R3::Vector xyzc; + for (; ai != mpstructure->end(); ++ai) { + xyzc = L.ucvCartesian(ai->xyz_cartn); + mcartesian_positions_uc.push_back(xyzc); + } } // Public Methods ------------------------------------------------------------ -void PeriodicStructureBondGenerator::rewind() -{ - // Delay msphere instantiation to here instead of in constructor, - // so it is possible to use setRmin, setRmax. - if (!msphere.get()) - { - const Lattice& L = mpstructure->getLattice(); - double buffzone = L.ucMaxDiagonalLength(); - double rsphmin = this->getRmin() - buffzone; - double rsphmax = this->getRmax() + buffzone; - msphere.reset(new PointsInSphere(rsphmin, rsphmax, L)); - } - // BaseBondGenerator::rewind calls this->rewindSymmetry, - // which takes care of msphere configuration - this->BaseBondGenerator::rewind(); +void PeriodicStructureBondGenerator::rewind() { + // Delay msphere instantiation to here instead of in constructor, + // so it is possible to use setRmin, setRmax. + if (!msphere.get()) { + const Lattice& L = mpstructure->getLattice(); + double buffzone = L.ucMaxDiagonalLength(); + double rsphmin = this->getRmin() - buffzone; + double rsphmax = this->getRmax() + buffzone; + msphere.reset(new PointsInSphere(rsphmin, rsphmax, L)); + } + // BaseBondGenerator::rewind calls this->rewindSymmetry, + // which takes care of msphere configuration + this->BaseBondGenerator::rewind(); } - -void PeriodicStructureBondGenerator::selectAnchorSite(int anchor) -{ - this->BaseBondGenerator::selectAnchorSite(anchor); - mr0 = mcartesian_positions_uc[anchor]; +void PeriodicStructureBondGenerator::selectAnchorSite(int anchor) { + this->BaseBondGenerator::selectAnchorSite(anchor); + mr0 = mcartesian_positions_uc[anchor]; } - -void PeriodicStructureBondGenerator::setRmin(double rmin) -{ - // destroy msphere so it will be created on rewind with new rmin - if (this->getRmin() != rmin) msphere.reset(); - this->BaseBondGenerator::setRmin(rmin); +void PeriodicStructureBondGenerator::setRmin(double rmin) { + // destroy msphere so it will be created on rewind with new rmin + if (this->getRmin() != rmin) msphere.reset(); + this->BaseBondGenerator::setRmin(rmin); } - -void PeriodicStructureBondGenerator::setRmax(double rmax) -{ - // destroy msphere so it will be created on rewind with new rmax - if (this->getRmax() != rmax) msphere.reset(); - this->BaseBondGenerator::setRmax(rmax); +void PeriodicStructureBondGenerator::setRmax(double rmax) { + // destroy msphere so it will be created on rewind with new rmax + if (this->getRmax() != rmax) msphere.reset(); + this->BaseBondGenerator::setRmax(rmax); } // Protected Methods --------------------------------------------------------- -bool PeriodicStructureBondGenerator::iterateSymmetry() -{ - msphere->next(); - bool done = msphere->finished(); - mrcsphere = done ? R3::zerovector : - mpstructure->getLattice().cartesian(msphere->mno()); - return !done; +bool PeriodicStructureBondGenerator::iterateSymmetry() { + msphere->next(); + bool done = msphere->finished(); + mrcsphere = + done ? R3::zerovector : mpstructure->getLattice().cartesian(msphere->mno()); + return !done; } - -void PeriodicStructureBondGenerator::rewindSymmetry() -{ - msphere->rewind(); - mrcsphere = msphere->finished() ? R3::zerovector : - mpstructure->getLattice().cartesian(msphere->mno()); - this->updater1(); +void PeriodicStructureBondGenerator::rewindSymmetry() { + msphere->rewind(); + mrcsphere = msphere->finished() + ? R3::zerovector + : mpstructure->getLattice().cartesian(msphere->mno()); + this->updater1(); } - -void PeriodicStructureBondGenerator::getNextBond() -{ - ++msite_current; - // go back to the first site if there is next symmetry element - if (msite_current >= msite_last && this->iterateSymmetry()) - { - msite_current = msite_first; - } - // update values only if not finished - if (!this->finished()) this->updater1(); +void PeriodicStructureBondGenerator::getNextBond() { + ++msite_current; + // go back to the first site if there is next symmetry element + if (msite_current >= msite_last && this->iterateSymmetry()) { + msite_current = msite_first; + } + // update values only if not finished + if (!this->finished()) this->updater1(); } // Private Methods ----------------------------------------------------------- -void PeriodicStructureBondGenerator::updater1() -{ - mr1 = mrcsphere + mcartesian_positions_uc[this->site1()]; - this->updateDistance(); +void PeriodicStructureBondGenerator::updater1() { + mr1 = mrcsphere + mcartesian_positions_uc[this->site1()]; + this->updateDistance(); } -} // namespace srreal -} // namespace diffpy +} // namespace srreal +} // namespace diffpy // Serialization ------------------------------------------------------------- diff --git a/src/diffpy/srreal/PeriodicStructureAdapter.hpp b/src/diffpy/srreal/PeriodicStructureAdapter.hpp index 365f632b..b32f246e 100644 --- a/src/diffpy/srreal/PeriodicStructureAdapter.hpp +++ b/src/diffpy/srreal/PeriodicStructureAdapter.hpp @@ -1,23 +1,23 @@ /***************************************************************************** -* -* libdiffpy Complex Modeling Initiative -* (c) 2013 Brookhaven Science Associates, -* Brookhaven National Laboratory. -* All rights reserved. -* -* File coded by: Pavol Juhas -* -* See AUTHORS.txt for a list of people who contributed. -* See LICENSE.txt for license information. -* -****************************************************************************** -* -* class PeriodicStructureAdapter -- universal adapter for structure with -* periodic boundary conditions that has no space group symmetry -* -* class PeriodicStructureBondGenerator -- bond generator -* -*****************************************************************************/ + * + * libdiffpy Complex Modeling Initiative + * (c) 2013 Brookhaven Science Associates, + * Brookhaven National Laboratory. + * All rights reserved. + * + * File coded by: Pavol Juhas + * + * See AUTHORS.txt for a list of people who contributed. + * See LICENSE.txt for license information. + * + ****************************************************************************** + * + * class PeriodicStructureAdapter -- universal adapter for structure with + * periodic boundary conditions that has no space group symmetry + * + * class PeriodicStructureBondGenerator -- bond generator + * + *****************************************************************************/ #ifndef PERIODICSTRUCTUREADAPTER_HPP_INCLUDED #define PERIODICSTRUCTUREADAPTER_HPP_INCLUDED @@ -33,85 +33,76 @@ namespace srreal { class PointsInSphere; -class DLL_EXPORT PeriodicStructureAdapter : public AtomicStructureAdapter -{ - public: - - // methods - overloaded - virtual StructureAdapterPtr clone() const; - virtual BaseBondGeneratorPtr createBondGenerator() const; - virtual double numberDensity() const; - virtual StructureDifference diff(StructureAdapterConstPtr other) const; - - // methods - own - void setLatPar( - double a, double b, double c, - double alphadeg, double betadeg, double gammadeg); - const Lattice& getLattice() const; - void toCartesian(Atom&) const; - void toFractional(Atom&) const; - - private: - - // data - Lattice mlattice; - - // serialization - friend class boost::serialization::access; - template - void serialize(Archive& ar, const unsigned int version) - { - ar & boost::serialization::base_object(*this); - ar & mlattice; - } - +class DLL_EXPORT PeriodicStructureAdapter : public AtomicStructureAdapter { + public: + // methods - overloaded + virtual StructureAdapterPtr clone() const; + virtual BaseBondGeneratorPtr createBondGenerator() const; + virtual double numberDensity() const; + virtual StructureDifference diff(StructureAdapterConstPtr other) const; + + // methods - own + void setLatPar(double a, double b, double c, double alphadeg, double betadeg, + double gammadeg); + const Lattice& getLattice() const; + void toCartesian(Atom&) const; + void toFractional(Atom&) const; + + private: + // data + Lattice mlattice; + + // serialization + friend class boost::serialization::access; + template + void serialize(Archive& ar, const unsigned int version) { + ar& boost::serialization::base_object(*this); + ar & mlattice; + } }; typedef boost::shared_ptr PeriodicStructureAdapterPtr; // Comparison functions -DLL_EXPORT bool operator==(const PeriodicStructureAdapter&, const PeriodicStructureAdapter&); -DLL_EXPORT bool operator!=(const PeriodicStructureAdapter&, const PeriodicStructureAdapter&); - - -class PeriodicStructureBondGenerator : public BaseBondGenerator -{ - public: - - // constructors - PeriodicStructureBondGenerator(StructureAdapterConstPtr); - - // methods - // loop control - virtual void rewind(); - - // configuration - virtual void selectAnchorSite(int); - virtual void setRmin(double); - virtual void setRmax(double); - - protected: - - // data - const PeriodicStructureAdapter* mpstructure; - std::unique_ptr msphere; - R3::Vector mrcsphere; - - // methods - virtual bool iterateSymmetry(); - virtual void rewindSymmetry(); - virtual void getNextBond(); - virtual void updater1(); - - private: - - // data - std::vector mcartesian_positions_uc; +DLL_EXPORT bool operator==(const PeriodicStructureAdapter&, + const PeriodicStructureAdapter&); +DLL_EXPORT bool operator!=(const PeriodicStructureAdapter&, + const PeriodicStructureAdapter&); + +class PeriodicStructureBondGenerator : public BaseBondGenerator { + public: + // constructors + PeriodicStructureBondGenerator(StructureAdapterConstPtr); + + // methods + // loop control + virtual void rewind(); + + // configuration + virtual void selectAnchorSite(int); + virtual void setRmin(double); + virtual void setRmax(double); + + protected: + // data + const PeriodicStructureAdapter* mpstructure; + std::unique_ptr msphere; + R3::Vector mrcsphere; + + // methods + virtual bool iterateSymmetry(); + virtual void rewindSymmetry(); + virtual void getNextBond(); + virtual void updater1(); + + private: + // data + std::vector mcartesian_positions_uc; }; -} // namespace srreal -} // namespace diffpy +} // namespace srreal +} // namespace diffpy // Serialization ------------------------------------------------------------- diff --git a/src/diffpy/srreal/PointsInSphere.cpp b/src/diffpy/srreal/PointsInSphere.cpp index 88c24ef5..d6713646 100644 --- a/src/diffpy/srreal/PointsInSphere.cpp +++ b/src/diffpy/srreal/PointsInSphere.cpp @@ -1,22 +1,22 @@ /***************************************************************************** -* -* 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.txt for a list of people who contributed. -* See LICENSE_DANSE.txt for license information. -* -****************************************************************************** -* -* classes PointsInSphere, ReflectionsInQminQmax, ReflectionsInDmaxDmin -* -* Comments: sequencers for lattice points insided 3D sphere -* -*****************************************************************************/ + * + * 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.txt for a list of people who contributed. + * See LICENSE_DANSE.txt for license information. + * + ****************************************************************************** + * + * classes PointsInSphere, ReflectionsInQminQmax, ReflectionsInDmaxDmin + * + * Comments: sequencers for lattice points inside 3D sphere + * + *****************************************************************************/ #include #include @@ -31,53 +31,56 @@ using pointsinsphere::LatticeParameters; // Constructor --------------------------------------------------------------- -LatticeParameters::LatticeParameters( double _a, double _b, double _c, - double _alpha, double _beta, double _gamma ) : - a(_a), b(_b), c(_c), - alpha(_alpha), beta(_beta), gamma(_gamma) -{ - update(); +LatticeParameters::LatticeParameters(double _a, double _b, double _c, + double _alpha, double _beta, double _gamma) + : a(_a), b(_b), c(_c), alpha(_alpha), beta(_beta), gamma(_gamma) { + update(); } // Public Methods ------------------------------------------------------------ -void LatticeParameters::update() -{ - using diffpy::mathutils::cosd; - using diffpy::mathutils::sind; - ca = cosd(alpha); cb = cosd(beta); cg = cosd(gamma); - sa = sind(alpha); sb = sind(beta); sg = sind(gamma); - // Vunit is a volume of unit cell with a=b=c=1 - const double Vunit = sqrt(1.0 + 2.0*ca*cb*cg - ca*ca - cb*cb - cg*cg); - ar = sa/(a*Vunit); - br = sb/(b*Vunit); - cr = sg/(c*Vunit); - car = (cb*cg - ca)/(sb*sg); sar = sqrt(1.0 - car*car); - cbr = (ca*cg - cb)/(sa*sg); sbr = sqrt(1.0 - cbr*cbr); - cgr = (ca*cb - cg)/(sa*sb); sgr = sqrt(1.0 - cgr*cgr); - alphar = 180.0/M_PI*acos(car); - betar = 180.0/M_PI*acos(cbr); - gammar = 180.0/M_PI*acos(cgr); -} - - -LatticeParameters LatticeParameters::reciprocal() const -{ - using namespace std; - LatticeParameters rec(*this); - swap(rec.a, rec.ar); - swap(rec.b, rec.br); - swap(rec.c, rec.cr); - swap(rec.alpha, rec.alphar); - swap(rec.beta, rec.betar); - swap(rec.gamma, rec.gammar); - swap(rec.ca, rec.car); - swap(rec.cb, rec.cbr); - swap(rec.cg, rec.cgr); - swap(rec.sa, rec.sar); - swap(rec.sb, rec.sbr); - swap(rec.sg, rec.sgr); - return rec; +void LatticeParameters::update() { + using diffpy::mathutils::cosd; + using diffpy::mathutils::sind; + ca = cosd(alpha); + cb = cosd(beta); + cg = cosd(gamma); + sa = sind(alpha); + sb = sind(beta); + sg = sind(gamma); + // Vunit is a volume of unit cell with a=b=c=1 + const double Vunit = + sqrt(1.0 + 2.0 * ca * cb * cg - ca * ca - cb * cb - cg * cg); + ar = sa / (a * Vunit); + br = sb / (b * Vunit); + cr = sg / (c * Vunit); + car = (cb * cg - ca) / (sb * sg); + sar = sqrt(1.0 - car * car); + cbr = (ca * cg - cb) / (sa * sg); + sbr = sqrt(1.0 - cbr * cbr); + cgr = (ca * cb - cg) / (sa * sb); + sgr = sqrt(1.0 - cgr * cgr); + alphar = 180.0 / M_PI * acos(car); + betar = 180.0 / M_PI * acos(cbr); + gammar = 180.0 / M_PI * acos(cgr); +} + +LatticeParameters LatticeParameters::reciprocal() const { + using namespace std; + LatticeParameters rec(*this); + swap(rec.a, rec.ar); + swap(rec.b, rec.br); + swap(rec.c, rec.cr); + swap(rec.alpha, rec.alphar); + swap(rec.beta, rec.betar); + swap(rec.gamma, rec.gammar); + swap(rec.ca, rec.car); + swap(rec.cb, rec.cbr); + swap(rec.cg, rec.cgr); + swap(rec.sa, rec.sar); + swap(rec.sb, rec.sbr); + swap(rec.sg, rec.sgr); + return rec; } ////////////////////////////////////////////////////////////////////////////// @@ -87,186 +90,140 @@ LatticeParameters LatticeParameters::reciprocal() const // Constructors -------------------------------------------------------------- PointsInSphere::PointsInSphere(double rmin, double rmax, - const LatticeParameters& _latpar ) : - _Rmin(rmin), _Rmax(rmax), - latpar(_latpar), - _m(_mno[0]), _n(_mno[1]), _o(_mno[2]) -{ - init(); - rewind(); -} - - -PointsInSphere::PointsInSphere(double rmin, double rmax, - double _a, double _b, double _c, - double _alpha, double _beta, double _gamma) : - _Rmin(rmin), _Rmax(rmax), - latpar(_a, _b, _c, _alpha, _beta, _gamma), - _m(_mno[0]), _n(_mno[1]), _o(_mno[2]) -{ - init(); - rewind(); + const LatticeParameters& _latpar) + : _Rmin(rmin), + _Rmax(rmax), + latpar(_latpar), + _m(_mno[0]), + _n(_mno[1]), + _o(_mno[2]) { + init(); + rewind(); +} + +PointsInSphere::PointsInSphere(double rmin, double rmax, double _a, double _b, + double _c, double _alpha, double _beta, + double _gamma) + : _Rmin(rmin), + _Rmax(rmax), + latpar(_a, _b, _c, _alpha, _beta, _gamma), + _m(_mno[0]), + _n(_mno[1]), + _o(_mno[2]) { + init(); + rewind(); } // Public Methods ------------------------------------------------------------ // loop control -void PointsInSphere::rewind() -{ - mHalfSpan = Rmax()*latpar.ar; - hi_m = int(ceil(mHalfSpan)); - this->_m = -hi_m; - // make indices n, o invalid, reset the neares point - this->_n = hi_n = 0; - this->_o = hi_o = outside_o = 0; - n0plane = o0plane = o0line = 0.0; - // unset excluded zone - oExclHalfSpan = 0.0; - // get the first inside point - next_o(); +void PointsInSphere::rewind() { + mHalfSpan = Rmax() * latpar.ar; + hi_m = int(ceil(mHalfSpan)); + this->_m = -hi_m; + // make indices n, o invalid, reset the nearest point + this->_n = hi_n = 0; + this->_o = hi_o = outside_o = 0; + n0plane = o0plane = o0line = 0.0; + // unset excluded zone + oExclHalfSpan = 0.0; + // get the first inside point + next_o(); } +void PointsInSphere::next() { next_o(); } -void PointsInSphere::next() -{ - next_o(); -} - - -bool PointsInSphere::finished() const -{ - return !(m() < this->hi_m); -} +bool PointsInSphere::finished() const { return !(m() < this->hi_m); } // data access -const double& PointsInSphere::Rmin() const -{ - return this->_Rmin; -} +const double& PointsInSphere::Rmin() const { return this->_Rmin; } +const double& PointsInSphere::Rmax() const { return this->_Rmax; } -const double& PointsInSphere::Rmax() const -{ - return this->_Rmax; -} +const int* PointsInSphere::mno() const { return this->_mno; } +int PointsInSphere::m() const { return this->_mno[0]; } -const int* PointsInSphere::mno() const -{ - return this->_mno; -} +int PointsInSphere::n() const { return this->_mno[1]; } +int PointsInSphere::o() const { return this->_mno[2]; } -int PointsInSphere::m() const -{ - return this->_mno[0]; -} - - -int PointsInSphere::n() const -{ - return this->_mno[1]; -} - - -int PointsInSphere::o() const -{ - return this->_mno[2]; -} - - -double PointsInSphere::r() const -{ - const double &a = latpar.a, &b = latpar.b, &c = latpar.c; - const double &ca = latpar.ca, &cb = latpar.cb, &cg = latpar.cg; - return sqrt( m()*m()*a*a + n()*n()*b*b + o()*o()*c*c - + 2*m()*n()*a*b*cg + 2*m()*o()*a*c*cb + 2*n()*o()*b*c*ca ); +double PointsInSphere::r() const { + const double &a = latpar.a, &b = latpar.b, &c = latpar.c; + const double &ca = latpar.ca, &cb = latpar.cb, &cg = latpar.cg; + return sqrt(m() * m() * a * a + n() * n() * b * b + o() * o() * c * c + + 2 * m() * n() * a * b * cg + 2 * m() * o() * a * c * cb + + 2 * n() * o() * b * c * ca); } // Private Methods ----------------------------------------------------------- -void PointsInSphere::next_m() -{ - this->_m += 1; - if (finished()) - { - return; +void PointsInSphere::next_m() { + this->_m += 1; + if (finished()) { + return; + } + // not finished here + n0plane = m() * dn0dm; + o0plane = m() * do0dm; + RplaneSquare = RmaxSquare - pow(m() / latpar.ar, 2); + nHalfSpan = RplaneSquare > 0.0 ? sqrt(RplaneSquare) * b2r : 0.0; + this->_n = int(floor(n0plane - nHalfSpan)); + hi_n = int(ceil(n0plane + nHalfSpan)); +} + +void PointsInSphere::next_n() { + do { + this->_n += 1; + if (n() < hi_n) { + o0line = o0plane + (n() - n0plane) * do0dn; + double RlineSquare = RplaneSquare - pow((n() - n0plane) / b2r, 2); + oHalfSpan = RlineSquare > 0.0 ? sqrt(RlineSquare) * c1r : 0.0; + // parentheses improve round-off errors around [0,0,0] + double RExclSquare = (RlineSquare - RmaxSquare) + RminSquare; + oExclHalfSpan = RExclSquare > 0.0 ? sqrt(RExclSquare) * c1r : 0.0; + this->_o = int(floor(o0line - oHalfSpan)); + outside_o = int(ceil(o0line + oHalfSpan)); + hi_o = outside_o; + if (oExclHalfSpan) { + int hole_o = int(ceil(o0line - oExclHalfSpan)); + if (fabs(hole_o - o0line) < oExclHalfSpan) hi_o = hole_o; + } + return; } - // not finished here - n0plane = m()*dn0dm; - o0plane = m()*do0dm; - RplaneSquare = RmaxSquare - pow(m()/latpar.ar,2); - nHalfSpan = RplaneSquare > 0.0 ? sqrt(RplaneSquare)*b2r : 0.0; - this->_n = int(floor(n0plane - nHalfSpan)); - hi_n = int(ceil(n0plane + nHalfSpan)); + next_m(); + } while (!finished()); } - -void PointsInSphere::next_n() -{ - do - { - this->_n += 1; - if (n() < hi_n) - { - o0line = o0plane + (n()-n0plane)*do0dn; - double RlineSquare = RplaneSquare - pow((n()-n0plane)/b2r,2); - oHalfSpan = RlineSquare > 0.0 ? sqrt(RlineSquare)*c1r : 0.0; - // parentheses improve round-off errors around [0,0,0] - double RExclSquare = (RlineSquare - RmaxSquare) + RminSquare; - oExclHalfSpan = RExclSquare > 0.0 ? sqrt(RExclSquare)*c1r : 0.0; - this->_o = int(floor(o0line - oHalfSpan)); - outside_o = int(ceil(o0line + oHalfSpan)); - hi_o = outside_o; - if (oExclHalfSpan) - { - int hole_o = int(ceil(o0line - oExclHalfSpan)); - if (fabs(hole_o-o0line) < oExclHalfSpan) hi_o = hole_o; - } - return; - } - next_m(); +void PointsInSphere::next_o() { + do { + this->_o += 1; + if (o() < hi_o) { + return; } - while (!finished()); -} - - -void PointsInSphere::next_o() -{ - do - { - this->_o += 1; - if (o() < hi_o) - { - return; - } - if (hi_o != outside_o) - { - hi_o = outside_o; - this->_o = int( ceil(o0line+oExclHalfSpan) ) - 1; - continue; - } - next_n(); + if (hi_o != outside_o) { + hi_o = outside_o; + this->_o = int(ceil(o0line + oExclHalfSpan)) - 1; + continue; } - while (!finished()); + next_n(); + } while (!finished()); } - -void PointsInSphere::init() -{ - RminSquare = (_Rmin < 0.0) ? -(_Rmin*_Rmin) : _Rmin*_Rmin; - RmaxSquare = (_Rmax < 0.0) ? -(_Rmax*_Rmax) : _Rmax*_Rmax; - dn0dm = latpar.cgr*latpar.br/latpar.ar; - do0dm = latpar.cbr*latpar.cr/latpar.ar; - // 2D reciprocal parameters in bc plane - b2r = 1.0/(latpar.b*latpar.sa); - c2r = 1.0/(latpar.c*latpar.sa); - ca2r = -latpar.ca; - do0dn = ca2r*c2r/b2r; - // 1D reciprocal along c axis - c1r = 1.0/latpar.c; +void PointsInSphere::init() { + RminSquare = (_Rmin < 0.0) ? -(_Rmin * _Rmin) : _Rmin * _Rmin; + RmaxSquare = (_Rmax < 0.0) ? -(_Rmax * _Rmax) : _Rmax * _Rmax; + dn0dm = latpar.cgr * latpar.br / latpar.ar; + do0dm = latpar.cbr * latpar.cr / latpar.ar; + // 2D reciprocal parameters in bc plane + b2r = 1.0 / (latpar.b * latpar.sa); + c2r = 1.0 / (latpar.c * latpar.sa); + ca2r = -latpar.ca; + do0dn = ca2r * c2r / b2r; + // 1D reciprocal along c axis + c1r = 1.0 / latpar.c; } ////////////////////////////////////////////////////////////////////////////// @@ -276,90 +233,48 @@ void PointsInSphere::init() // Constructors -------------------------------------------------------------- ReflectionsInQminQmax::ReflectionsInQminQmax(double qmin, double qmax, - const LatticeParameters& _latpar) : - _Qmin(qmin), _Qmax(qmax), - latpar(_latpar), - sph(qmin*M_1_PI/2.0, qmax*M_1_PI/2.0, latpar.reciprocal()) -{ } - + const LatticeParameters& _latpar) + : _Qmin(qmin), + _Qmax(qmax), + latpar(_latpar), + sph(qmin * M_1_PI / 2.0, qmax * M_1_PI / 2.0, latpar.reciprocal()) {} ReflectionsInQminQmax::ReflectionsInQminQmax(double qmin, double qmax, - double _a, double _b, double _c, - double _alpha, double _beta, double _gamma ) : - _Qmin(qmin), _Qmax(qmax), - latpar(_a, _b, _c, _alpha, _beta, _gamma), - sph(qmin*M_1_PI/2.0, qmax*M_1_PI/2.0, latpar.reciprocal()) -{ } + double _a, double _b, double _c, + double _alpha, double _beta, + double _gamma) + : _Qmin(qmin), + _Qmax(qmax), + latpar(_a, _b, _c, _alpha, _beta, _gamma), + sph(qmin * M_1_PI / 2.0, qmax * M_1_PI / 2.0, latpar.reciprocal()) {} // Public Methods ------------------------------------------------------------ // loop control -void ReflectionsInQminQmax::rewind() -{ - this->sph.rewind(); -} +void ReflectionsInQminQmax::rewind() { this->sph.rewind(); } +void ReflectionsInQminQmax::next() { this->sph.next(); } -void ReflectionsInQminQmax::next() -{ - this->sph.next(); -} - - -bool ReflectionsInQminQmax::finished() const -{ - return this->sph.finished(); -} +bool ReflectionsInQminQmax::finished() const { return this->sph.finished(); } // data access -const double& ReflectionsInQminQmax::Qmin() const -{ - return this->_Qmin; -} - - -const double& ReflectionsInQminQmax::Qmax() const -{ - return this->_Qmax; -} - - -const int* ReflectionsInQminQmax::hkl() const -{ - return this->sph.mno(); -} +const double& ReflectionsInQminQmax::Qmin() const { return this->_Qmin; } +const double& ReflectionsInQminQmax::Qmax() const { return this->_Qmax; } -int ReflectionsInQminQmax::h() const -{ - return this->sph.m(); -} +const int* ReflectionsInQminQmax::hkl() const { return this->sph.mno(); } +int ReflectionsInQminQmax::h() const { return this->sph.m(); } -int ReflectionsInQminQmax::k() const -{ - return this->sph.n(); -} +int ReflectionsInQminQmax::k() const { return this->sph.n(); } +int ReflectionsInQminQmax::l() const { return this->sph.o(); } -int ReflectionsInQminQmax::l() const -{ - return this->sph.o(); -} - - -double ReflectionsInQminQmax::Q() const -{ - return 2.0*M_PI*sph.r(); -} +double ReflectionsInQminQmax::Q() const { return 2.0 * M_PI * sph.r(); } - -double ReflectionsInQminQmax::d() const -{ - return 1.0/sph.r(); -} +double ReflectionsInQminQmax::d() const { return 1.0 / sph.r(); } ////////////////////////////////////////////////////////////////////////////// // class ReflectionsInDmaxDmin @@ -368,33 +283,26 @@ double ReflectionsInQminQmax::d() const // Constructors -------------------------------------------------------------- ReflectionsInDmaxDmin::ReflectionsInDmaxDmin(double dmax, double dmin, - const LatticeParameters& _latpar) : - ReflectionsInQminQmax(2.0*M_PI/dmax, 2.0*M_PI/dmin, _latpar), - _Dmin(dmin), _Dmax(dmax) -{ } - + const LatticeParameters& _latpar) + : ReflectionsInQminQmax(2.0 * M_PI / dmax, 2.0 * M_PI / dmin, _latpar), + _Dmin(dmin), + _Dmax(dmax) {} ReflectionsInDmaxDmin::ReflectionsInDmaxDmin(double dmax, double dmin, - double _a, double _b, double _c, - double _alpha, double _beta, double _gamma) : - ReflectionsInQminQmax(2.0*M_PI/dmax, 2.0*M_PI/dmin, - _a, _b, _c, _alpha, _beta, _gamma), - _Dmin(dmin), _Dmax(dmax) -{ } + double _a, double _b, double _c, + double _alpha, double _beta, + double _gamma) + : ReflectionsInQminQmax(2.0 * M_PI / dmax, 2.0 * M_PI / dmin, _a, _b, _c, + _alpha, _beta, _gamma), + _Dmin(dmin), + _Dmax(dmax) {} // Public Methods ------------------------------------------------------------ // data access -const double& ReflectionsInDmaxDmin::Dmin() const -{ - return this->_Dmin; -} - +const double& ReflectionsInDmaxDmin::Dmin() const { return this->_Dmin; } -const double& ReflectionsInDmaxDmin::Dmax() const -{ - return this->_Dmax; -} +const double& ReflectionsInDmaxDmin::Dmax() const { return this->_Dmax; } // End of file diff --git a/src/diffpy/srreal/PointsInSphere.hpp b/src/diffpy/srreal/PointsInSphere.hpp index 40451aad..c78cbc76 100644 --- a/src/diffpy/srreal/PointsInSphere.hpp +++ b/src/diffpy/srreal/PointsInSphere.hpp @@ -1,52 +1,52 @@ /***************************************************************************** -* -* 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.txt for a list of people who contributed. -* See LICENSE_DANSE.txt for license information. -* -****************************************************************************** -* -* classes PointsInSphere, ReflectionsInQminQmax, ReflectionsInDmaxDmin -* -* Constructors: -* -* PointsInSphere(Rmin, Rmax, a, b, c, alpha, beta, gamma) -* ReflectionsInQminQmax(Qmin, Qmax, a, b, c, alpha, beta, gamma) -* ReflectionsInDmaxDmin(Dmax, Dmin, a, b, c, alpha, beta, gamma) -* -* template PointsInSphere(Rmin, Rmax, const Lattice&) -* -* where class Lattice must provide methods a(), b(), c(), -* alpha(), beta(), gamma() -* -* Examples: -* -* PointsInSphere sph(Rmin, Rmax, a, b, c, alpha, beta, gamma) -* for (sph.rewind(); !sph.finished(); sph.next()) -* { -* // lattice indices are in sph.m(), sph.n(), sph.o() or sph.mno() -* // sph.r() is distance from origin, -* // where sph.Rmin() < sph.r() < sph.Rmax() -* } -* -* ReflectionsInQminQmax ref(Qmin, Qmax, a, b, c, alpha, beta, gamma) -* for (ReflectionsInQminQmax ref(Qmin, Qmax, a, b, c, alpha, beta, gamma); -* !ref.finished(); ref.next() ) -* { -* // Miller indices are in ref.h(), ref.k(), ref.l() or ref.hkl() -* // ref.Q() is magnitude of Q vector -* // ref.d() is lattice plane spacing -* } -* -* Tip: add epsilon to Rmax to avoid roundoff issues -* -*****************************************************************************/ + * + * 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.txt for a list of people who contributed. + * See LICENSE_DANSE.txt for license information. + * + ****************************************************************************** + * + * classes PointsInSphere, ReflectionsInQminQmax, ReflectionsInDmaxDmin + * + * Constructors: + * + * PointsInSphere(Rmin, Rmax, a, b, c, alpha, beta, gamma) + * ReflectionsInQminQmax(Qmin, Qmax, a, b, c, alpha, beta, gamma) + * ReflectionsInDmaxDmin(Dmax, Dmin, a, b, c, alpha, beta, gamma) + * + * template PointsInSphere(Rmin, Rmax, const Lattice&) + * + * where class Lattice must provide methods a(), b(), c(), + * alpha(), beta(), gamma() + * + * Examples: + * + * PointsInSphere sph(Rmin, Rmax, a, b, c, alpha, beta, gamma) + * for (sph.rewind(); !sph.finished(); sph.next()) + * { + * // lattice indices are in sph.m(), sph.n(), sph.o() or sph.mno() + * // sph.r() is distance from origin, + * // where sph.Rmin() < sph.r() < sph.Rmax() + * } + * + * ReflectionsInQminQmax ref(Qmin, Qmax, a, b, c, alpha, beta, gamma) + * for (ReflectionsInQminQmax ref(Qmin, Qmax, a, b, c, alpha, beta, gamma); + * !ref.finished(); ref.next() ) + * { + * // Miller indices are in ref.h(), ref.k(), ref.l() or ref.hkl() + * // ref.Q() is magnitude of Q vector + * // ref.d() is lattice plane spacing + * } + * + * Tip: add epsilon to Rmax to avoid roundoff issues + * + *****************************************************************************/ #ifndef POINTSINSPHERE_HPP_INCLUDED #define POINTSINSPHERE_HPP_INCLUDED @@ -58,198 +58,180 @@ namespace srreal { namespace pointsinsphere { -class LatticeParameters -{ - public: - - // data - // input arguments - double a, b, c, alpha, beta, gamma; - // cosines and sines of direct lattice angles - double ca, cb, cg, sa, sb, sg; - // reciprocal lattice and its cosines and sines - double ar, br, cr, alphar, betar, gammar; - double car, cbr, cgr, sar, sbr, sgr; - - // constructor - LatticeParameters(double _a, double _b, double _c, - double _alpha, double _beta, double _gamma); - - // methods - // calculate all properties from current lattice parameters - void update(); - // return a reciprocal of this lattice - LatticeParameters reciprocal() const; - +class LatticeParameters { + public: + // data + // input arguments + double a, b, c, alpha, beta, gamma; + // cosines and sines of direct lattice angles + double ca, cb, cg, sa, sb, sg; + // reciprocal lattice and its cosines and sines + double ar, br, cr, alphar, betar, gammar; + double car, cbr, cgr, sar, sbr, sgr; + + // constructor + LatticeParameters(double _a, double _b, double _c, double _alpha, + double _beta, double _gamma); + + // methods + // calculate all properties from current lattice parameters + void update(); + // return a reciprocal of this lattice + LatticeParameters reciprocal() const; }; -} // namespace pointsinsphere - - -class PointsInSphere -{ - public: - - // constructors - PointsInSphere(double rmin, double rmax, - const pointsinsphere::LatticeParameters& _latpar); - PointsInSphere(double rmin, double rmax, - double _a, double _b, double _c, - double _alpha, double _beta, double _gamma); - template - PointsInSphere(double rmin, double rmax, const L&); - - // methods - // loop control - void rewind(); - void next(); - bool finished() const; - // data access - const double& Rmin() const; - const double& Rmax() const; - const int* mno() const; - int m() const; - int n() const; - int o() const; - double r() const; - - private: - - // data - // inputs - const double _Rmin; - const double _Rmax; - const pointsinsphere::LatticeParameters latpar; - // output - int _mno[3]; - int& _m; - int& _n; - int& _o; - // calculated constants set by init() - double RminSquare, RmaxSquare; - // 2D reciprocal parameters and cosine in bc plane - double b2r, c2r, ca2r; - // reciprocal c - double c1r; - // offset of the nearest point to [0,0,0] - double dn0dm, do0dm, do0dn; - // loop variables - double n0plane, o0plane, o0line; - double mHalfSpan, nHalfSpan, oHalfSpan; - // o indices excluded due to Rmin - double oExclHalfSpan; - int hi_m, hi_n, hi_o, outside_o; - double RplaneSquare; - - // methods - // loop advance - void next_m(); - void next_n(); - void next_o(); - void init(); +} // namespace pointsinsphere + +class PointsInSphere { + public: + // constructors + PointsInSphere(double rmin, double rmax, + const pointsinsphere::LatticeParameters& _latpar); + PointsInSphere(double rmin, double rmax, double _a, double _b, double _c, + double _alpha, double _beta, double _gamma); + template + PointsInSphere(double rmin, double rmax, const L&); + + // methods + // loop control + void rewind(); + void next(); + bool finished() const; + // data access + const double& Rmin() const; + const double& Rmax() const; + const int* mno() const; + int m() const; + int n() const; + int o() const; + double r() const; + + private: + // data + // inputs + const double _Rmin; + const double _Rmax; + const pointsinsphere::LatticeParameters latpar; + // output + int _mno[3]; + int& _m; + int& _n; + int& _o; + // calculated constants set by init() + double RminSquare, RmaxSquare; + // 2D reciprocal parameters and cosine in bc plane + double b2r, c2r, ca2r; + // reciprocal c + double c1r; + // offset of the nearest point to [0,0,0] + double dn0dm, do0dm, do0dn; + // loop variables + double n0plane, o0plane, o0line; + double mHalfSpan, nHalfSpan, oHalfSpan; + // o indices excluded due to Rmin + double oExclHalfSpan; + int hi_m, hi_n, hi_o, outside_o; + double RplaneSquare; + + // methods + // loop advance + void next_m(); + void next_n(); + void next_o(); + void init(); }; - -class ReflectionsInQminQmax -{ - public: - - // constructors - ReflectionsInQminQmax(double _Qmin, double _Qmax, - const pointsinsphere::LatticeParameters& _latpar); - ReflectionsInQminQmax(double _Qmin, double _Qmax, - double _a, double _b, double _c, - double _alpha, double _beta, double _gamma); - template - ReflectionsInQminQmax(double _Qmin, double _Qmax, const L&); - - // methods - // loop control - void rewind(); - void next(); - bool finished() const; - // data access - const double& Qmin() const; - const double& Qmax() const; - const int* hkl() const; - int h() const; - int k() const; - int l() const; - double Q() const; - double d() const; - - private: - - // data - // inputs - const double _Qmin; - const double _Qmax; - const pointsinsphere::LatticeParameters latpar; - // composite - PointsInSphere sph; +class ReflectionsInQminQmax { + public: + // constructors + ReflectionsInQminQmax(double _Qmin, double _Qmax, + const pointsinsphere::LatticeParameters& _latpar); + ReflectionsInQminQmax(double _Qmin, double _Qmax, double _a, double _b, + double _c, double _alpha, double _beta, double _gamma); + template + ReflectionsInQminQmax(double _Qmin, double _Qmax, const L&); + + // methods + // loop control + void rewind(); + void next(); + bool finished() const; + // data access + const double& Qmin() const; + const double& Qmax() const; + const int* hkl() const; + int h() const; + int k() const; + int l() const; + double Q() const; + double d() const; + + private: + // data + // inputs + const double _Qmin; + const double _Qmax; + const pointsinsphere::LatticeParameters latpar; + // composite + PointsInSphere sph; }; - -class ReflectionsInDmaxDmin : public ReflectionsInQminQmax -{ - public: - - // constructors - ReflectionsInDmaxDmin(double dmax, double dmin, - const pointsinsphere::LatticeParameters& _latpar); - ReflectionsInDmaxDmin(double dmax, double dmin, - double _a, double _b, double _c, - double _alpha, double _beta, double _gamma); - template - ReflectionsInDmaxDmin(double dmax, double dmin, const L&); - - // methods - // data access - const double& Dmin() const; - const double& Dmax() const; - - private: - - // data - // inputs - const double _Dmin; - const double _Dmax; +class ReflectionsInDmaxDmin : public ReflectionsInQminQmax { + public: + // constructors + ReflectionsInDmaxDmin(double dmax, double dmin, + const pointsinsphere::LatticeParameters& _latpar); + ReflectionsInDmaxDmin(double dmax, double dmin, double _a, double _b, + double _c, double _alpha, double _beta, double _gamma); + template + ReflectionsInDmaxDmin(double dmax, double dmin, const L&); + + // methods + // data access + const double& Dmin() const; + const double& Dmax() const; + + private: + // data + // inputs + const double _Dmin; + const double _Dmax; }; // Template Constructor for PointsInSphere ----------------------------------- template -PointsInSphere::PointsInSphere(double rmin, double rmax, const L& lat) : - _Rmin(rmin), _Rmax(rmax), - latpar(lat.a(), lat.b(), lat.c(), lat.alpha(), lat.beta(), lat.gamma()), - _m(_mno[0]), _n(_mno[1]), _o(_mno[2]) -{ - init(); - rewind(); +PointsInSphere::PointsInSphere(double rmin, double rmax, const L& lat) + : _Rmin(rmin), + _Rmax(rmax), + latpar(lat.a(), lat.b(), lat.c(), lat.alpha(), lat.beta(), lat.gamma()), + _m(_mno[0]), + _n(_mno[1]), + _o(_mno[2]) { + init(); + rewind(); } // Template Constructor for ReflectionsInQminQmax ---------------------------- template -ReflectionsInQminQmax::ReflectionsInQminQmax( - double _qmin, double _qmax, const L& lat) : - _Qmin(_qmin), _Qmax(_qmax), - latpar(lat.a(), lat.b(), lat.c(), - lat.alpha(), lat.beta(), lat.gamma()), - sph(_qmin*M_1_PI/2.0, _qmax*M_1_PI/2.0, latpar.reciprocal()) -{ } +ReflectionsInQminQmax::ReflectionsInQminQmax(double _qmin, double _qmax, + const L& lat) + : _Qmin(_qmin), + _Qmax(_qmax), + latpar(lat.a(), lat.b(), lat.c(), lat.alpha(), lat.beta(), lat.gamma()), + sph(_qmin * M_1_PI / 2.0, _qmax * M_1_PI / 2.0, latpar.reciprocal()) {} // Template Constructor for ReflectionsInDmaxDmin ---------------------------- template -ReflectionsInDmaxDmin::ReflectionsInDmaxDmin( - double dmax, double dmin, const L& lat) : - ReflectionsInQminQmax(2.0*M_PI/dmax, 2.0*M_PI/dmin, lat), - _Dmin(dmin), _Dmax(dmax) -{ } - - -} // namespace srreal -} // namespace diffpy +ReflectionsInDmaxDmin::ReflectionsInDmaxDmin(double dmax, double dmin, + const L& lat) + : ReflectionsInQminQmax(2.0 * M_PI / dmax, 2.0 * M_PI / dmin, lat), + _Dmin(dmin), + _Dmax(dmax) {} + +} // namespace srreal +} // namespace diffpy #endif // POINTSINSPHERE_HPP_INCLUDED diff --git a/src/diffpy/srreal/QResolutionEnvelope.cpp b/src/diffpy/srreal/QResolutionEnvelope.cpp index 53049a23..ca096812 100644 --- a/src/diffpy/srreal/QResolutionEnvelope.cpp +++ b/src/diffpy/srreal/QResolutionEnvelope.cpp @@ -1,20 +1,20 @@ /***************************************************************************** -* -* 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.txt for a list of people who contributed. -* See LICENSE_DANSE.txt for license information. -* -****************************************************************************** -* -* class QResolutionEnvelope -- Gaussian envelope due to limited Q resolution -* -*****************************************************************************/ + * + * 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.txt for a list of people who contributed. + * See LICENSE_DANSE.txt for license information. + * + ****************************************************************************** + * + * class QResolutionEnvelope -- Gaussian envelope due to limited Q resolution + * + *****************************************************************************/ #include @@ -28,62 +28,44 @@ namespace srreal { // Constructor --------------------------------------------------------------- -QResolutionEnvelope::QResolutionEnvelope() -{ - this->setQdamp(0.0); - this->registerDoubleAttribute("qdamp", this, - &QResolutionEnvelope::getQdamp, &QResolutionEnvelope::setQdamp); +QResolutionEnvelope::QResolutionEnvelope() { + this->setQdamp(0.0); + this->registerDoubleAttribute("qdamp", this, &QResolutionEnvelope::getQdamp, + &QResolutionEnvelope::setQdamp); } - -PDFEnvelopePtr QResolutionEnvelope::create() const -{ - PDFEnvelopePtr rv(new QResolutionEnvelope()); - return rv; +PDFEnvelopePtr QResolutionEnvelope::create() const { + PDFEnvelopePtr rv(new QResolutionEnvelope()); + return rv; } - -PDFEnvelopePtr QResolutionEnvelope::clone() const -{ - PDFEnvelopePtr rv(new QResolutionEnvelope(*this)); - return rv; +PDFEnvelopePtr QResolutionEnvelope::clone() const { + PDFEnvelopePtr rv(new QResolutionEnvelope(*this)); + return rv; } // Public Methods ------------------------------------------------------------ -const string& QResolutionEnvelope::type() const -{ - static string rv = "qresolution"; - return rv; +const string& QResolutionEnvelope::type() const { + static string rv = "qresolution"; + return rv; } - -double QResolutionEnvelope::operator()(const double& r) const -{ - double rv = (mqdamp > 0.0) ? - exp(-pow(r * mqdamp, 2) / 2) : - 1.0; - return rv; +double QResolutionEnvelope::operator()(const double& r) const { + double rv = (mqdamp > 0.0) ? exp(-pow(r * mqdamp, 2) / 2) : 1.0; + return rv; } +void QResolutionEnvelope::setQdamp(double sc) { mqdamp = sc; } -void QResolutionEnvelope::setQdamp(double sc) -{ - mqdamp = sc; -} - - -const double& QResolutionEnvelope::getQdamp() const -{ - return mqdamp; -} +const double& QResolutionEnvelope::getQdamp() const { return mqdamp; } // Registration -------------------------------------------------------------- bool reg_QResolutionEnvelope = QResolutionEnvelope().registerThisType(); -} // namespace srreal -} // namespace diffpy +} // namespace srreal +} // namespace diffpy // Serialization ------------------------------------------------------------- diff --git a/src/diffpy/srreal/QResolutionEnvelope.hpp b/src/diffpy/srreal/QResolutionEnvelope.hpp index f7d33ae6..e1aef643 100644 --- a/src/diffpy/srreal/QResolutionEnvelope.hpp +++ b/src/diffpy/srreal/QResolutionEnvelope.hpp @@ -1,20 +1,20 @@ /***************************************************************************** -* -* 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.txt for a list of people who contributed. -* See LICENSE_DANSE.txt for license information. -* -****************************************************************************** -* -* class QResolutionEnvelope -- Gaussian envelope due to limited Q resolution -* -*****************************************************************************/ + * + * 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.txt for a list of people who contributed. + * See LICENSE_DANSE.txt for license information. + * + ****************************************************************************** + * + * class QResolutionEnvelope -- Gaussian envelope due to limited Q resolution + * + *****************************************************************************/ #ifndef QRESOLUTIONENVELOPE_HPP_INCLUDED #define QRESOLUTIONENVELOPE_HPP_INCLUDED @@ -29,41 +29,37 @@ namespace srreal { /// @class QResolutionEnvelope /// @brief wide Gaussian PDF scaling envelope caused by finite Q resolution -class DLL_EXPORT QResolutionEnvelope : public PDFEnvelope -{ - public: - - // constructors - QResolutionEnvelope(); - virtual PDFEnvelopePtr create() const; - virtual PDFEnvelopePtr clone() const; - - // methods - virtual const std::string& type() const; - virtual double operator()(const double& r) const; - void setQdamp(double sc); - const double& getQdamp() const; - - private: - - // data - double mqdamp; - - // serialization - friend class boost::serialization::access; - - template - void serialize(Archive& ar, const unsigned int version) - { - using boost::serialization::base_object; - ar & base_object(*this); - ar & mqdamp; - } +class DLL_EXPORT QResolutionEnvelope : public PDFEnvelope { + public: + // constructors + QResolutionEnvelope(); + virtual PDFEnvelopePtr create() const; + virtual PDFEnvelopePtr clone() const; + + // methods + virtual const std::string& type() const; + virtual double operator()(const double& r) const; + void setQdamp(double sc); + const double& getQdamp() const; + + private: + // data + double mqdamp; + + // serialization + friend class boost::serialization::access; + + template + void serialize(Archive& ar, const unsigned int version) { + using boost::serialization::base_object; + ar& base_object(*this); + ar & mqdamp; + } }; // class QResolutionEnvelope -} // namespace srreal -} // namespace diffpy +} // namespace srreal +} // namespace diffpy // Serialization ------------------------------------------------------------- diff --git a/src/diffpy/srreal/QuantityType.hpp b/src/diffpy/srreal/QuantityType.hpp index c625ec5e..86befa24 100644 --- a/src/diffpy/srreal/QuantityType.hpp +++ b/src/diffpy/srreal/QuantityType.hpp @@ -1,24 +1,24 @@ /***************************************************************************** -* -* 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.txt for a list of people who contributed. -* See LICENSE_DANSE.txt for license information. -* -****************************************************************************** -* -* Shared types and small functions used by PairQuantity calculators. -* -* QuantityType -- type that stores PairQuantity results, an array of doubles -* It is a unique derived class from vector to avoid conflicts with -* boost_python convertors in cctbx. -* -*****************************************************************************/ + * + * 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.txt for a list of people who contributed. + * See LICENSE_DANSE.txt for license information. + * + ****************************************************************************** + * + * Shared types and small functions used by PairQuantity calculators. + * + * QuantityType -- type that stores PairQuantity results, an array of doubles + * It is a unique derived class from vector to avoid conflicts with + * boost_python converters in cctbx. + * + *****************************************************************************/ #ifndef QUANTITYTYPE_HPP_INCLUDED #define QUANTITYTYPE_HPP_INCLUDED @@ -31,35 +31,29 @@ namespace diffpy { namespace srreal { -class DLL_EXPORT QuantityType : public std::vector -{ - private: - - typedef std::vector Base; - - public: - - // Constructors from std::vector - QuantityType() : Base() { } - QuantityType(const Base& src) : Base(src) { } - explicit QuantityType(Base::size_type n) : Base(n) { } - explicit QuantityType(Base::size_type n, double x) : Base(n, x) { } - template - QuantityType(Iter first, Iter last) : Base(first, last) { } - - private: - - // serialization - friend class boost::serialization::access; - template - void serialize(Archive& ar, const unsigned int version) - { - ar & boost::serialization::base_object(*this); - } - +class DLL_EXPORT QuantityType : public std::vector { + private: + typedef std::vector Base; + + public: + // Constructors from std::vector + QuantityType() : Base() {} + QuantityType(const Base& src) : Base(src) {} + explicit QuantityType(Base::size_type n) : Base(n) {} + explicit QuantityType(Base::size_type n, double x) : Base(n, x) {} + template + QuantityType(Iter first, Iter last) : Base(first, last) {} + + private: + // serialization + friend class boost::serialization::access; + template + void serialize(Archive& ar, const unsigned int version) { + ar& boost::serialization::base_object(*this); + } }; -} // namespace srreal -} // namespace diffpy +} // namespace srreal +} // namespace diffpy #endif // QUANTITYTYPE_HPP_INCLUDED diff --git a/src/diffpy/srreal/R3linalg.cpp b/src/diffpy/srreal/R3linalg.cpp index 15a32814..20cca1cb 100644 --- a/src/diffpy/srreal/R3linalg.cpp +++ b/src/diffpy/srreal/R3linalg.cpp @@ -1,20 +1,20 @@ /***************************************************************************** -* -* 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.txt for a list of people who contributed. -* See LICENSE_DANSE.txt for license information. -* -****************************************************************************** -* -* R3linalg -- vector and matrix types and linar algebra operations in R3 space -* -*****************************************************************************/ + * + * 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.txt for a list of people who contributed. + * See LICENSE_DANSE.txt for license information. + * + ****************************************************************************** + * + * R3linalg -- vector and matrix types and linear algebra operations in R3 space + * + *****************************************************************************/ #include #include @@ -29,112 +29,92 @@ namespace R3 { DLL_EXPORT const int Ndim = 3; // Functions -const Matrix& identity() -{ - static Matrix mx = ublas::identity_matrix(Ndim); - return mx; +const Matrix& identity() { + static Matrix mx = ublas::identity_matrix(Ndim); + return mx; } - -const Matrix& zeromatrix() -{ - static Matrix mx = ublas::zero_matrix(Ndim, Ndim); - return mx; +const Matrix& zeromatrix() { + static Matrix mx = ublas::zero_matrix(Ndim, Ndim); + return mx; } - -double determinant(const Matrix& A) -{ - gsl_matrix* gA = gsl_matrix_alloc(Ndim, Ndim); - for (int i = 0; i != Ndim; ++i) - { - for (int j = 0; j != Ndim; ++j) - { - gsl_matrix_set(gA, i, j, A(i,j)); - } +double determinant(const Matrix& A) { + gsl_matrix* gA = gsl_matrix_alloc(Ndim, Ndim); + for (int i = 0; i != Ndim; ++i) { + for (int j = 0; j != Ndim; ++j) { + gsl_matrix_set(gA, i, j, A(i, j)); } - gsl_permutation* gP = gsl_permutation_alloc(Ndim); - int signum; - gsl_linalg_LU_decomp(gA, gP, &signum); - double det = gsl_linalg_LU_det(gA, signum); - gsl_permutation_free(gP); - gsl_matrix_free(gA); - return det; + } + gsl_permutation* gP = gsl_permutation_alloc(Ndim); + int signum; + gsl_linalg_LU_decomp(gA, gP, &signum); + double det = gsl_linalg_LU_det(gA, signum); + gsl_permutation_free(gP); + gsl_matrix_free(gA); + return det; } - -const Matrix& inverse(const Matrix& A) -{ - static Matrix B; - gsl_matrix* gA = gsl_matrix_alloc(Ndim, Ndim); - for (int i = 0; i != Ndim; ++i) - { - for (int j = 0; j != Ndim; ++j) - { - gsl_matrix_set(gA, i, j, A(i,j)); - } +const Matrix& inverse(const Matrix& A) { + static Matrix B; + gsl_matrix* gA = gsl_matrix_alloc(Ndim, Ndim); + for (int i = 0; i != Ndim; ++i) { + for (int j = 0; j != Ndim; ++j) { + gsl_matrix_set(gA, i, j, A(i, j)); } - gsl_permutation* gP = gsl_permutation_alloc(Ndim); - int signum; - gsl_linalg_LU_decomp(gA, gP, &signum); - double* bdata = &(B.data()[0]); - gsl_matrix_view gB = gsl_matrix_view_array(bdata, Ndim, Ndim); - gsl_linalg_LU_invert(gA, gP, &gB.matrix); - gsl_permutation_free(gP); - gsl_matrix_free(gA); - return B; + } + gsl_permutation* gP = gsl_permutation_alloc(Ndim); + int signum; + gsl_linalg_LU_decomp(gA, gP, &signum); + double* bdata = &(B.data()[0]); + gsl_matrix_view gB = gsl_matrix_view_array(bdata, Ndim, Ndim); + gsl_linalg_LU_invert(gA, gP, &gB.matrix); + gsl_permutation_free(gP); + gsl_matrix_free(gA); + return B; } - -size_t hash_value(const Vector& v) -{ - return boost::hash_range(v.begin(), v.end()); +size_t hash_value(const Vector& v) { + return boost::hash_range(v.begin(), v.end()); } - -size_t hash_value(const Matrix& A) -{ - return boost::hash_range(A.data().begin(), A.data().end()); +size_t hash_value(const Matrix& A) { + return boost::hash_range(A.data().begin(), A.data().end()); } -} // namespace R3 -} // namespace srreal +} // namespace R3 +} // namespace srreal namespace mathutils { // EpsilonLess specialization ------------------------------------------------ -template<> +template <> bool EpsilonLess::operator()( - const srreal::R3::Matrix& A, const srreal::R3::Matrix& B) const -{ - bool rv = std::lexicographical_compare( - A.data().begin(), A.data().end(), - B.data().begin(), B.data().end(), *this); - return rv; + const srreal::R3::Matrix& A, const srreal::R3::Matrix& B) const { + bool rv = std::lexicographical_compare( + A.data().begin(), A.data().end(), B.data().begin(), B.data().end(), *this); + return rv; } // EpsilonEqual specializations ---------------------------------------------- -template<> +template <> bool EpsilonEqual::operator()( - const srreal::R3::Vector& u, const srreal::R3::Vector& v) const -{ - bool rv = std::equal(u.begin(), u.end(), v.begin(), *this); - return rv; + const srreal::R3::Vector& u, const srreal::R3::Vector& v) const { + bool rv = std::equal(u.begin(), u.end(), v.begin(), *this); + return rv; } - -template<> +template <> bool EpsilonEqual::operator()( - const srreal::R3::Matrix& A, const srreal::R3::Matrix& B) const -{ - bool rv = std::equal(A.data().begin(), A.data().end(), - B.data().begin(), *this); - return rv; + const srreal::R3::Matrix& A, const srreal::R3::Matrix& B) const { + bool rv = + std::equal(A.data().begin(), A.data().end(), B.data().begin(), *this); + return rv; } -} // namespace mathutils -} // namespace diffpy +} // namespace mathutils +} // namespace diffpy // End of file diff --git a/src/diffpy/srreal/R3linalg.hpp b/src/diffpy/srreal/R3linalg.hpp index 69916aad..c7e73a12 100644 --- a/src/diffpy/srreal/R3linalg.hpp +++ b/src/diffpy/srreal/R3linalg.hpp @@ -1,20 +1,20 @@ /***************************************************************************** -* -* 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.txt for a list of people who contributed. -* See LICENSE_DANSE.txt for license information. -* -****************************************************************************** -* -* R3linalg -- vector and matrix types and linar algebra operations in R3 space -* -*****************************************************************************/ + * + * 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.txt for a list of people who contributed. + * See LICENSE_DANSE.txt for license information. + * + ****************************************************************************** + * + * R3linalg -- vector and matrix types and linear algebra operations in R3 space + * + *****************************************************************************/ #ifndef R3LINALG_HPP_INCLUDED #define R3LINALG_HPP_INCLUDED @@ -35,9 +35,9 @@ namespace R3 { // Declarations -------------------------------------------------------------- namespace ublas = boost::numeric::ublas; +using ublas::column; using ublas::prod; using ublas::row; -using ublas::column; using ublas::trans; // Constants @@ -48,102 +48,92 @@ const ublas::zero_vector zerovector(Ndim); // Classes -class DLL_EXPORT Vector : public ublas::vector > -{ - typedef ublas::vector > - BaseVector; - - public: - - // constructors - Vector() : BaseVector(3) { } - - Vector(const double& x, const double& y, const double& z) - : BaseVector(3) - { - Vector::iterator xi = this->begin(); - *(xi++) = x; - *(xi++) = y; - *(xi++) = z; - } - - template - Vector(const ublas::vector_expression& r) : BaseVector(r) - { } - - template - void operator=(const ublas::vector_expression& r) - { - BaseVector::operator=(r); - } - - template - void operator=(const BaseVector& r) - { - BaseVector::operator=(r); - } - - private: - - // serialization - friend class boost::serialization::access; - template - void serialize(Archive& ar, const unsigned int version) - { - using boost::serialization::base_object; - ar & base_object(*this); - } +class DLL_EXPORT Vector + : public ublas::vector > { + typedef ublas::vector > BaseVector; + + public: + // constructors + Vector() : BaseVector(3) {} + + Vector(const double& x, const double& y, const double& z) : BaseVector(3) { + Vector::iterator xi = this->begin(); + *(xi++) = x; + *(xi++) = y; + *(xi++) = z; + } + + template + Vector(const ublas::vector_expression& r) : BaseVector(r) {} + + template + void operator=(const ublas::vector_expression& r) { + BaseVector::operator=(r); + } + + template + void operator=(const BaseVector& r) { + BaseVector::operator=(r); + } + + private: + // serialization + friend class boost::serialization::access; + template + void serialize(Archive& ar, const unsigned int version) { + using boost::serialization::base_object; + ar& base_object(*this); + } }; - -class DLL_EXPORT Matrix : public ublas::matrix > -{ - typedef ublas::matrix > BaseMatrix; - - public: - - // constructors - Matrix() : BaseMatrix(3, 3) { } - - Matrix(const double& x0, const double& x1, const double& x2, - const double& x3, const double& x4, const double& x5, - const double& x6, const double& x7, const double& x8) : - BaseMatrix(3, 3) - { - Matrix::array_type::iterator xi = this->data().begin(); - *(xi++) = x0; *(xi++) = x1; *(xi++) = x2; - *(xi++) = x3; *(xi++) = x4; *(xi++) = x5; - *(xi++) = x6; *(xi++) = x7; *(xi++) = x8; - } - - template - Matrix(const ublas::matrix_expression& r) : BaseMatrix(r) - { } - - template - void operator=(const ublas::matrix_expression& r) - { - BaseMatrix::operator=(r); - } - - template - void operator=(const BaseMatrix& r) - { - BaseMatrix::operator=(r); - } - - private: - - // serialization - friend class boost::serialization::access; - template - void serialize(Archive& ar, const unsigned int version) - { - using boost::serialization::base_object; - ar & base_object(*this); - } +class DLL_EXPORT Matrix + : public ublas::matrix > { + typedef ublas::matrix > + BaseMatrix; + + public: + // constructors + Matrix() : BaseMatrix(3, 3) {} + + Matrix(const double& x0, const double& x1, const double& x2, const double& x3, + const double& x4, const double& x5, const double& x6, const double& x7, + const double& x8) + : BaseMatrix(3, 3) { + Matrix::array_type::iterator xi = this->data().begin(); + *(xi++) = x0; + *(xi++) = x1; + *(xi++) = x2; + *(xi++) = x3; + *(xi++) = x4; + *(xi++) = x5; + *(xi++) = x6; + *(xi++) = x7; + *(xi++) = x8; + } + + template + Matrix(const ublas::matrix_expression& r) : BaseMatrix(r) {} + + template + void operator=(const ublas::matrix_expression& r) { + BaseMatrix::operator=(r); + } + + template + void operator=(const BaseMatrix& r) { + BaseMatrix::operator=(r); + } + + private: + // serialization + friend class boost::serialization::access; + template + void serialize(Archive& ar, const unsigned int version) { + using boost::serialization::base_object; + ar& base_object(*this); + } }; // Functions @@ -154,44 +144,35 @@ double determinant(const Matrix& A); const Matrix& inverse(const Matrix& A); const Vector& floor(const Vector&); -template double norm(const V&); -template double distance(const V& u, const V& v); -template double dot(const V& u, const V& v); -template Vector cross(const V& u, const V& v); -template const Vector& mxvecproduct(const Matrix&, const V&); -template const Vector& mxvecproduct(const V&, const Matrix&); +template +double norm(const V&); +template +double distance(const V& u, const V& v); +template +double dot(const V& u, const V& v); +template +Vector cross(const V& u, const V& v); +template +const Vector& mxvecproduct(const Matrix&, const V&); +template +const Vector& mxvecproduct(const V&, const Matrix&); // Equality ------------------------------------------------------------------ -inline -bool operator==(const Vector& u, const Vector& v) -{ - bool rv = std::equal(u.begin(), u.end(), v.begin()); - return rv; +inline bool operator==(const Vector& u, const Vector& v) { + bool rv = std::equal(u.begin(), u.end(), v.begin()); + return rv; } +inline bool operator!=(const Vector& u, const Vector& v) { return !(u == v); } -inline -bool operator!=(const Vector& u, const Vector& v) -{ - return !(u == v); +inline bool operator==(const Matrix& A, const Matrix& B) { + bool rv = (&A == &B) || + std::equal(A.data().begin(), A.data().end(), B.data().begin()); + return rv; } - -inline -bool operator==(const Matrix& A, const Matrix& B) -{ - bool rv = (&A == &B) || - std::equal(A.data().begin(), A.data().end(), B.data().begin()); - return rv; -} - - -inline -bool operator!=(const Matrix& A, const Matrix& B) -{ - return !(A == B); -} +inline bool operator!=(const Matrix& A, const Matrix& B) { return !(A == B); } // Hashing ------------------------------------------------------------------- @@ -200,95 +181,82 @@ size_t hash_value(const Matrix& A); // Inlined functions --------------------------------------------------------- -inline -const Vector& floor(const Vector& v) -{ - static Vector res; - Vector::const_iterator xi = v.begin(); - Vector::iterator xo = res.begin(); - for (; xi != v.end(); ++xi, ++xo) *xo = std::floor(*xi); - return res; +inline const Vector& floor(const Vector& v) { + static Vector res; + Vector::const_iterator xi = v.begin(); + Vector::iterator xo = res.begin(); + for (; xi != v.end(); ++xi, ++xo) *xo = std::floor(*xi); + return res; } // Template functions -------------------------------------------------------- template -double norm(const V& u) -{ - return sqrt(R3::dot(u, u)); +double norm(const V& u) { + return sqrt(R3::dot(u, u)); } - template -double distance(const V& u, const V& v) -{ - static R3::Vector duv; - duv[0] = u[0] - v[0]; - duv[1] = u[1] - v[1]; - duv[2] = u[2] - v[2]; - return R3::norm(duv); +double distance(const V& u, const V& v) { + static R3::Vector duv; + duv[0] = u[0] - v[0]; + duv[1] = u[1] - v[1]; + duv[2] = u[2] - v[2]; + return R3::norm(duv); } - template -double dot(const V& u, const V& v) -{ - return (u[0]*v[0] + u[1]*v[1] + u[2]*v[2]); +double dot(const V& u, const V& v) { + return (u[0] * v[0] + u[1] * v[1] + u[2] * v[2]); } - template -Vector cross(const V& u, const V& v) -{ - Vector res; - res[0] = u[1]*v[2] - u[2]*v[1]; - res[1] = u[2]*v[0] - u[0]*v[2]; - res[2] = u[0]*v[1] - u[1]*v[0]; - return res; +Vector cross(const V& u, const V& v) { + Vector res; + res[0] = u[1] * v[2] - u[2] * v[1]; + res[1] = u[2] * v[0] - u[0] * v[2]; + res[2] = u[0] * v[1] - u[1] * v[0]; + return res; } - template -const Vector& mxvecproduct(const Matrix& M, const V& u) -{ - static Vector res; - res[0] = M(0,0)*u[0] + M(0,1)*u[1] + M(0,2)*u[2]; - res[1] = M(1,0)*u[0] + M(1,1)*u[1] + M(1,2)*u[2]; - res[2] = M(2,0)*u[0] + M(2,1)*u[1] + M(2,2)*u[2]; - return res; +const Vector& mxvecproduct(const Matrix& M, const V& u) { + static Vector res; + res[0] = M(0, 0) * u[0] + M(0, 1) * u[1] + M(0, 2) * u[2]; + res[1] = M(1, 0) * u[0] + M(1, 1) * u[1] + M(1, 2) * u[2]; + res[2] = M(2, 0) * u[0] + M(2, 1) * u[1] + M(2, 2) * u[2]; + return res; } - template -const Vector& mxvecproduct(const V& u, const Matrix& M) -{ - static Vector res; - res[0] = u[0]*M(0,0) + u[1]*M(1,0) + u[2]*M(2,0); - res[1] = u[0]*M(0,1) + u[1]*M(1,1) + u[2]*M(2,1); - res[2] = u[0]*M(0,2) + u[1]*M(1,2) + u[2]*M(2,2); - return res; +const Vector& mxvecproduct(const V& u, const Matrix& M) { + static Vector res; + res[0] = u[0] * M(0, 0) + u[1] * M(1, 0) + u[2] * M(2, 0); + res[1] = u[0] * M(0, 1) + u[1] * M(1, 1) + u[2] * M(2, 1); + res[2] = u[0] * M(0, 2) + u[1] * M(1, 2) + u[2] * M(2, 2); + return res; } -} // namespace R3 -} // namespace srreal +} // namespace R3 +} // namespace srreal namespace mathutils { // Template specializations -------------------------------------------------- -template<> +template <> bool EpsilonLess::operator()( - const srreal::R3::Matrix& A, const srreal::R3::Matrix& B) const; + const srreal::R3::Matrix& A, const srreal::R3::Matrix& B) const; -template<> +template <> bool EpsilonEqual::operator()( - const srreal::R3::Vector& u, const srreal::R3::Vector& v) const; + const srreal::R3::Vector& u, const srreal::R3::Vector& v) const; -template<> +template <> bool EpsilonEqual::operator()( - const srreal::R3::Matrix& A, const srreal::R3::Matrix& B) const; + const srreal::R3::Matrix& A, const srreal::R3::Matrix& B) const; -} // namespace mathutils -} // namespace diffpy +} // namespace mathutils +} // namespace diffpy #endif // R3LINALG_HPP_INCLUDED diff --git a/src/diffpy/srreal/SFTElectron.cpp b/src/diffpy/srreal/SFTElectron.cpp index 556aedea..f27df75b 100644 --- a/src/diffpy/srreal/SFTElectron.cpp +++ b/src/diffpy/srreal/SFTElectron.cpp @@ -1,25 +1,25 @@ /***************************************************************************** -* -* 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.txt for a list of people who contributed. -* See LICENSE_DANSE.txt for license information. -* -****************************************************************************** -* -* class SFTElectron -* -* Implementation of electron ScatteringFactorTable using the formula in -* International Tables Volume C, page 224. -* The instance can be also created by calling the -* createByType("electron") factory. -* -*****************************************************************************/ + * + * 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.txt for a list of people who contributed. + * See LICENSE_DANSE.txt for license information. + * + ****************************************************************************** + * + * class SFTElectron + * + * Implementation of electron ScatteringFactorTable using the formula in + * International Tables Volume C, page 224. + * The instance can be also created by calling the + * createByType("electron") factory. + * + *****************************************************************************/ #include #include @@ -34,44 +34,38 @@ using namespace std; // HasClassRegistry methods -ScatteringFactorTablePtr SFTElectron::create() const -{ - ScatteringFactorTablePtr rv(new SFTElectron()); - return rv; +ScatteringFactorTablePtr SFTElectron::create() const { + ScatteringFactorTablePtr rv(new SFTElectron()); + return rv; } -ScatteringFactorTablePtr SFTElectron::clone() const -{ - ScatteringFactorTablePtr rv(new SFTElectron(*this)); - return rv; +ScatteringFactorTablePtr SFTElectron::clone() const { + ScatteringFactorTablePtr rv(new SFTElectron(*this)); + return rv; } -const string& SFTElectron::type() const -{ - static string rv = "electron"; - return rv; +const string& SFTElectron::type() const { + static string rv = "electron"; + return rv; } // own methods - overloads -const string& SFTElectron::radiationType() const -{ - static string rv = "E"; - return rv; +const string& SFTElectron::radiationType() const { + static string rv = "E"; + return rv; } - -double SFTElectron::standardLookup(const string& smbl, double q) const -{ - return felectronatq(smbl, q); +double SFTElectron::standardLookup(const string& smbl, double q) const { + return felectronatq(smbl, q); } // Registration -------------------------------------------------------------- bool reg_SFTElectron = SFTElectron().registerThisType(); -} // namespace srreal -} // namespace diffpy +} // namespace srreal +} // namespace diffpy // Serialization ------------------------------------------------------------- diff --git a/src/diffpy/srreal/SFTElectron.hpp b/src/diffpy/srreal/SFTElectron.hpp index e20897a3..b4c91bf5 100644 --- a/src/diffpy/srreal/SFTElectron.hpp +++ b/src/diffpy/srreal/SFTElectron.hpp @@ -1,24 +1,24 @@ /***************************************************************************** -* -* 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.txt for a list of people who contributed. -* See LICENSE_DANSE.txt for license information. -* -****************************************************************************** -* -* class SFTElectron -* -* SFTElectron gives Q-dependent electron scattering factor according to -* the formula in International Tables Volume C, page 224. -* The formula diverges at Q = 0, where SFTElectron returns DOUBLE_MAX. -* -*****************************************************************************/ + * + * 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.txt for a list of people who contributed. + * See LICENSE_DANSE.txt for license information. + * + ****************************************************************************** + * + * class SFTElectron + * + * SFTElectron gives Q-dependent electron scattering factor according to + * the formula in International Tables Volume C, page 224. + * The formula diverges at Q = 0, where SFTElectron returns DOUBLE_MAX. + * + *****************************************************************************/ #ifndef SFTELECTRON_HPP_INCLUDED #define SFTELECTRON_HPP_INCLUDED @@ -31,36 +31,31 @@ namespace diffpy { namespace srreal { -class DLL_EXPORT SFTElectron : public ScatteringFactorTable -{ - public: +class DLL_EXPORT SFTElectron : public ScatteringFactorTable { + public: + // HasClassRegistry methods + ScatteringFactorTablePtr create() const; + ScatteringFactorTablePtr clone() const; + const std::string& type() const; + // own methods + const std::string& radiationType() const; + // method overloads + double standardLookup(const std::string& smbl, double q) const; - // HasClassRegistry methods - ScatteringFactorTablePtr create() const; - ScatteringFactorTablePtr clone() const; - const std::string& type() const; - // own methods - const std::string& radiationType() const; - // method overloads - double standardLookup(const std::string& smbl, double q) const; + private: + // serialization + friend class boost::serialization::access; - private: - - // serialization - friend class boost::serialization::access; - - template - void serialize(Archive& ar, const unsigned int version) - { - using boost::serialization::base_object; - ar & base_object(*this); - } + template + void serialize(Archive& ar, const unsigned int version) { + using boost::serialization::base_object; + ar& base_object(*this); + } }; // class SFTElectron - -} // namespace srreal -} // namespace diffpy +} // namespace srreal +} // namespace diffpy // Serialization ------------------------------------------------------------- diff --git a/src/diffpy/srreal/SFTElectronNumber.cpp b/src/diffpy/srreal/SFTElectronNumber.cpp index 47f65d8e..db37e48e 100644 --- a/src/diffpy/srreal/SFTElectronNumber.cpp +++ b/src/diffpy/srreal/SFTElectronNumber.cpp @@ -1,23 +1,23 @@ /***************************************************************************** -* -* 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.txt for a list of people who contributed. -* See LICENSE_DANSE.txt for license information. -* -****************************************************************************** -* -* class SFTElectronNumber -* -* ScatteringFactorTable implementation where scattering power equals number -* of valence electrons and is constant with Q. -* -*****************************************************************************/ + * + * 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.txt for a list of people who contributed. + * See LICENSE_DANSE.txt for license information. + * + ****************************************************************************** + * + * class SFTElectronNumber + * + * ScatteringFactorTable implementation where scattering power equals number + * of valence electrons and is constant with Q. + * + *****************************************************************************/ #include #include @@ -32,45 +32,38 @@ namespace srreal { // HasClassRegistry methods -ScatteringFactorTablePtr SFTElectronNumber::create() const -{ - ScatteringFactorTablePtr rv(new SFTElectronNumber()); - return rv; +ScatteringFactorTablePtr SFTElectronNumber::create() const { + ScatteringFactorTablePtr rv(new SFTElectronNumber()); + return rv; } - -ScatteringFactorTablePtr SFTElectronNumber::clone() const -{ - ScatteringFactorTablePtr rv(new SFTElectronNumber(*this)); - return rv; +ScatteringFactorTablePtr SFTElectronNumber::clone() const { + ScatteringFactorTablePtr rv(new SFTElectronNumber(*this)); + return rv; } -const string& SFTElectronNumber::type() const -{ - static string rv = "electronnumber"; - return rv; +const string& SFTElectronNumber::type() const { + static string rv = "electronnumber"; + return rv; } // own methods - overloads -const string& SFTElectronNumber::radiationType() const -{ - static string rv = "EN"; - return rv; +const string& SFTElectronNumber::radiationType() const { + static string rv = "EN"; + return rv; } - -double SFTElectronNumber::standardLookup(const string& smbl, double q) const -{ - return electronnumber(smbl); +double SFTElectronNumber::standardLookup(const string& smbl, double q) const { + return electronnumber(smbl); } // Registration -------------------------------------------------------------- bool reg_SFTElectronNumber = SFTElectronNumber().registerThisType(); -} // namespace srreal -} // namespace diffpy +} // namespace srreal +} // namespace diffpy // Serialization ------------------------------------------------------------- diff --git a/src/diffpy/srreal/SFTElectronNumber.hpp b/src/diffpy/srreal/SFTElectronNumber.hpp index d3da0981..70a0d1b0 100644 --- a/src/diffpy/srreal/SFTElectronNumber.hpp +++ b/src/diffpy/srreal/SFTElectronNumber.hpp @@ -1,23 +1,23 @@ /***************************************************************************** -* -* 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.txt for a list of people who contributed. -* See LICENSE_DANSE.txt for license information. -* -****************************************************************************** -* -* class SFTElectronNumber -* -* ScatteringFactorTable with scattering power equal number -* of valence electrons without any Q-dependence -* -*****************************************************************************/ + * + * 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.txt for a list of people who contributed. + * See LICENSE_DANSE.txt for license information. + * + ****************************************************************************** + * + * class SFTElectronNumber + * + * ScatteringFactorTable with scattering power equal number + * of valence electrons without any Q-dependence + * + *****************************************************************************/ #ifndef SFTELECTRONNUMBER_HPP_INCLUDED #define SFTELECTRONNUMBER_HPP_INCLUDED @@ -29,36 +29,31 @@ namespace diffpy { namespace srreal { -class DLL_EXPORT SFTElectronNumber : public ScatteringFactorTable -{ - public: - - // HasClassRegistry methods - ScatteringFactorTablePtr create() const; - ScatteringFactorTablePtr clone() const; - const std::string& type() const; - // own methods - const std::string& radiationType() const; - // method overloads - double standardLookup(const std::string& smbl, double q) const; - - private: - - // serialization - friend class boost::serialization::access; - - template - void serialize(Archive& ar, const unsigned int version) - { - using boost::serialization::base_object; - ar & base_object(*this); - } +class DLL_EXPORT SFTElectronNumber : public ScatteringFactorTable { + public: + // HasClassRegistry methods + ScatteringFactorTablePtr create() const; + ScatteringFactorTablePtr clone() const; + const std::string& type() const; + // own methods + const std::string& radiationType() const; + // method overloads + double standardLookup(const std::string& smbl, double q) const; + + private: + // serialization + friend class boost::serialization::access; + + template + void serialize(Archive& ar, const unsigned int version) { + using boost::serialization::base_object; + ar& base_object(*this); + } }; // class SFTElectronNumber - -} // namespace srreal -} // namespace diffpy +} // namespace srreal +} // namespace diffpy // Serialization ------------------------------------------------------------- diff --git a/src/diffpy/srreal/SFTNeutron.cpp b/src/diffpy/srreal/SFTNeutron.cpp index 96d9af20..0b92b46b 100644 --- a/src/diffpy/srreal/SFTNeutron.cpp +++ b/src/diffpy/srreal/SFTNeutron.cpp @@ -1,25 +1,25 @@ /***************************************************************************** -* -* 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.txt for a list of people who contributed. -* See LICENSE_DANSE.txt for license information. -* -****************************************************************************** -* -* class SFTNeutron -* -* Implementation of neutron ScatteringFactorTable using coherent cross -* sections data from Paul Kienzle periodictable library for Python. -* approximation. The instance can be also created by calling the -* createByType("neutron") factory. -* -*****************************************************************************/ + * + * 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.txt for a list of people who contributed. + * See LICENSE_DANSE.txt for license information. + * + ****************************************************************************** + * + * class SFTNeutron + * + * Implementation of neutron ScatteringFactorTable using coherent cross + * sections data from Paul Kienzle periodictable library for Python. + * approximation. The instance can be also created by calling the + * createByType("neutron") factory. + * + *****************************************************************************/ #include #include @@ -34,44 +34,38 @@ using namespace std; // HasClassRegistry methods -ScatteringFactorTablePtr SFTNeutron::create() const -{ - ScatteringFactorTablePtr rv(new SFTNeutron()); - return rv; +ScatteringFactorTablePtr SFTNeutron::create() const { + ScatteringFactorTablePtr rv(new SFTNeutron()); + return rv; } -ScatteringFactorTablePtr SFTNeutron::clone() const -{ - ScatteringFactorTablePtr rv(new SFTNeutron(*this)); - return rv; +ScatteringFactorTablePtr SFTNeutron::clone() const { + ScatteringFactorTablePtr rv(new SFTNeutron(*this)); + return rv; } -const string& SFTNeutron::type() const -{ - static string rv = "neutron"; - return rv; +const string& SFTNeutron::type() const { + static string rv = "neutron"; + return rv; } // own methods - overloads -const string& SFTNeutron::radiationType() const -{ - static string rv = "N"; - return rv; +const string& SFTNeutron::radiationType() const { + static string rv = "N"; + return rv; } - -double SFTNeutron::standardLookup(const string& smbl, double q) const -{ - return bcneutron(smbl); +double SFTNeutron::standardLookup(const string& smbl, double q) const { + return bcneutron(smbl); } // Registration -------------------------------------------------------------- bool reg_SFTNeutron = SFTNeutron().registerThisType(); -} // namespace srreal -} // namespace diffpy +} // namespace srreal +} // namespace diffpy // Serialization ------------------------------------------------------------- diff --git a/src/diffpy/srreal/SFTNeutron.hpp b/src/diffpy/srreal/SFTNeutron.hpp index a1a17427..58af7446 100644 --- a/src/diffpy/srreal/SFTNeutron.hpp +++ b/src/diffpy/srreal/SFTNeutron.hpp @@ -1,23 +1,23 @@ /***************************************************************************** -* -* 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.txt for a list of people who contributed. -* See LICENSE_DANSE.txt for license information. -* -****************************************************************************** -* -* class SFTNeutron -* -* Neutron ScatteringFactorTable using coherent cross sections extracted -* from Paul Kienzle periodictable library for Python. -* -*****************************************************************************/ + * + * 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.txt for a list of people who contributed. + * See LICENSE_DANSE.txt for license information. + * + ****************************************************************************** + * + * class SFTNeutron + * + * Neutron ScatteringFactorTable using coherent cross sections extracted + * from Paul Kienzle periodictable library for Python. + * + *****************************************************************************/ #ifndef SFTNEUTRON_HPP_INCLUDED #define SFTNEUTRON_HPP_INCLUDED @@ -29,36 +29,31 @@ namespace diffpy { namespace srreal { -class DLL_EXPORT SFTNeutron : public ScatteringFactorTable -{ - public: - - // HasClassRegistry methods - ScatteringFactorTablePtr create() const; - ScatteringFactorTablePtr clone() const; - const std::string& type() const; - // own methods - const std::string& radiationType() const; - // method overloads - double standardLookup(const std::string& smbl, double q) const; - - private: - - // serialization - friend class boost::serialization::access; - - template - void serialize(Archive& ar, const unsigned int version) - { - using boost::serialization::base_object; - ar & base_object(*this); - } +class DLL_EXPORT SFTNeutron : public ScatteringFactorTable { + public: + // HasClassRegistry methods + ScatteringFactorTablePtr create() const; + ScatteringFactorTablePtr clone() const; + const std::string& type() const; + // own methods + const std::string& radiationType() const; + // method overloads + double standardLookup(const std::string& smbl, double q) const; + + private: + // serialization + friend class boost::serialization::access; + + template + void serialize(Archive& ar, const unsigned int version) { + using boost::serialization::base_object; + ar& base_object(*this); + } }; // class SFTNeutron - -} // namespace srreal -} // namespace diffpy +} // namespace srreal +} // namespace diffpy // Serialization ------------------------------------------------------------- diff --git a/src/diffpy/srreal/SFTXray.cpp b/src/diffpy/srreal/SFTXray.cpp index 9d24562a..8289e027 100644 --- a/src/diffpy/srreal/SFTXray.cpp +++ b/src/diffpy/srreal/SFTXray.cpp @@ -1,24 +1,24 @@ /***************************************************************************** -* -* 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.txt for a list of people who contributed. -* See LICENSE_DANSE.txt for license information. -* -****************************************************************************** -* -* class SFTXray -* -* Implementation of x-ray ScatteringFactorTable using Waasmaier and Kirfel -* approximation. The instance can be also created by calling the -* createByType("xray") factory. -* -*****************************************************************************/ + * + * 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.txt for a list of people who contributed. + * See LICENSE_DANSE.txt for license information. + * + ****************************************************************************** + * + * class SFTXray + * + * Implementation of x-ray ScatteringFactorTable using Waasmaier and Kirfel + * approximation. The instance can be also created by calling the + * createByType("xray") factory. + * + *****************************************************************************/ #include #include @@ -33,44 +33,38 @@ using namespace std; // HasClassRegistry methods -ScatteringFactorTablePtr SFTXray::create() const -{ - ScatteringFactorTablePtr rv(new SFTXray()); - return rv; +ScatteringFactorTablePtr SFTXray::create() const { + ScatteringFactorTablePtr rv(new SFTXray()); + return rv; } -ScatteringFactorTablePtr SFTXray::clone() const -{ - ScatteringFactorTablePtr rv(new SFTXray(*this)); - return rv; +ScatteringFactorTablePtr SFTXray::clone() const { + ScatteringFactorTablePtr rv(new SFTXray(*this)); + return rv; } -const string& SFTXray::type() const -{ - static string rv = "xray"; - return rv; +const string& SFTXray::type() const { + static string rv = "xray"; + return rv; } // own methods - overloads -const string& SFTXray::radiationType() const -{ - static string rv = "X"; - return rv; +const string& SFTXray::radiationType() const { + static string rv = "X"; + return rv; } - -double SFTXray::standardLookup(const string& smbl, double q) const -{ - return fxrayatq(smbl, q); +double SFTXray::standardLookup(const string& smbl, double q) const { + return fxrayatq(smbl, q); } // Registration -------------------------------------------------------------- bool reg_SFTXray = SFTXray().registerThisType(); -} // namespace srreal -} // namespace diffpy +} // namespace srreal +} // namespace diffpy // Serialization ------------------------------------------------------------- diff --git a/src/diffpy/srreal/SFTXray.hpp b/src/diffpy/srreal/SFTXray.hpp index 8a9fc362..687aff5e 100644 --- a/src/diffpy/srreal/SFTXray.hpp +++ b/src/diffpy/srreal/SFTXray.hpp @@ -1,22 +1,22 @@ /***************************************************************************** -* -* 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.txt for a list of people who contributed. -* See LICENSE_DANSE.txt for license information. -* -****************************************************************************** -* -* class SFTXray -* -* X-ray ScatteringFactorTable using Waasmaier and Kirfel approximation. -* -*****************************************************************************/ + * + * 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.txt for a list of people who contributed. + * See LICENSE_DANSE.txt for license information. + * + ****************************************************************************** + * + * class SFTXray + * + * X-ray ScatteringFactorTable using Waasmaier and Kirfel approximation. + * + *****************************************************************************/ #ifndef SFTXRAY_HPP_INCLUDED #define SFTXRAY_HPP_INCLUDED @@ -28,36 +28,31 @@ namespace diffpy { namespace srreal { -class DLL_EXPORT SFTXray : public ScatteringFactorTable -{ - public: - - // HasClassRegistry methods - ScatteringFactorTablePtr create() const; - ScatteringFactorTablePtr clone() const; - const std::string& type() const; - // own methods - const std::string& radiationType() const; - // method overloads - double standardLookup(const std::string& smbl, double q) const; - - private: - - // serialization - friend class boost::serialization::access; - - template - void serialize(Archive& ar, const unsigned int version) - { - using boost::serialization::base_object; - ar & base_object(*this); - } +class DLL_EXPORT SFTXray : public ScatteringFactorTable { + public: + // HasClassRegistry methods + ScatteringFactorTablePtr create() const; + ScatteringFactorTablePtr clone() const; + const std::string& type() const; + // own methods + const std::string& radiationType() const; + // method overloads + double standardLookup(const std::string& smbl, double q) const; + + private: + // serialization + friend class boost::serialization::access; + + template + void serialize(Archive& ar, const unsigned int version) { + using boost::serialization::base_object; + ar& base_object(*this); + } }; // class SFTXray - -} // namespace srreal -} // namespace diffpy +} // namespace srreal +} // namespace diffpy // Serialization ------------------------------------------------------------- diff --git a/src/diffpy/srreal/ScaleEnvelope.cpp b/src/diffpy/srreal/ScaleEnvelope.cpp index 65dab37a..b671a9a8 100644 --- a/src/diffpy/srreal/ScaleEnvelope.cpp +++ b/src/diffpy/srreal/ScaleEnvelope.cpp @@ -1,20 +1,20 @@ /***************************************************************************** -* -* 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.txt for a list of people who contributed. -* See LICENSE_DANSE.txt for license information. -* -****************************************************************************** -* -* class ScaleEnvelope -- constant scaling factor -* -*****************************************************************************/ + * + * 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.txt for a list of people who contributed. + * See LICENSE_DANSE.txt for license information. + * + ****************************************************************************** + * + * class ScaleEnvelope -- constant scaling factor + * + *****************************************************************************/ #include #include @@ -26,60 +26,43 @@ namespace srreal { // Constructors -------------------------------------------------------------- -ScaleEnvelope::ScaleEnvelope() -{ - this->setScale(1.0); - this->registerDoubleAttribute("scale", - this, &ScaleEnvelope::getScale, &ScaleEnvelope::setScale); +ScaleEnvelope::ScaleEnvelope() { + this->setScale(1.0); + this->registerDoubleAttribute("scale", this, &ScaleEnvelope::getScale, + &ScaleEnvelope::setScale); } - -PDFEnvelopePtr ScaleEnvelope::create() const -{ - PDFEnvelopePtr rv(new ScaleEnvelope()); - return rv; +PDFEnvelopePtr ScaleEnvelope::create() const { + PDFEnvelopePtr rv(new ScaleEnvelope()); + return rv; } - -PDFEnvelopePtr ScaleEnvelope::clone() const -{ - PDFEnvelopePtr rv(new ScaleEnvelope(*this)); - return rv; +PDFEnvelopePtr ScaleEnvelope::clone() const { + PDFEnvelopePtr rv(new ScaleEnvelope(*this)); + return rv; } // Public Methods ------------------------------------------------------------ -const string& ScaleEnvelope::type() const -{ - static string rv = "scale"; - return rv; -} - - -double ScaleEnvelope::operator()(const double& r) const -{ - return this->getScale(); +const string& ScaleEnvelope::type() const { + static string rv = "scale"; + return rv; } - -void ScaleEnvelope::setScale(double sc) -{ - mscale = sc; +double ScaleEnvelope::operator()(const double& r) const { + return this->getScale(); } +void ScaleEnvelope::setScale(double sc) { mscale = sc; } -const double& ScaleEnvelope::getScale() const -{ - return mscale; -} - +const double& ScaleEnvelope::getScale() const { return mscale; } // Registration -------------------------------------------------------------- bool reg_ScaleEnvelope = ScaleEnvelope().registerThisType(); -} // namespace srreal -} // namespace diffpy +} // namespace srreal +} // namespace diffpy // Serialization ------------------------------------------------------------- diff --git a/src/diffpy/srreal/ScaleEnvelope.hpp b/src/diffpy/srreal/ScaleEnvelope.hpp index 97b927e0..8c5c0e7b 100644 --- a/src/diffpy/srreal/ScaleEnvelope.hpp +++ b/src/diffpy/srreal/ScaleEnvelope.hpp @@ -1,20 +1,20 @@ /***************************************************************************** -* -* 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.txt for a list of people who contributed. -* See LICENSE_DANSE.txt for license information. -* -****************************************************************************** -* -* class ScaleEnvelope -- constant scaling factor -* -*****************************************************************************/ + * + * 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.txt for a list of people who contributed. + * See LICENSE_DANSE.txt for license information. + * + ****************************************************************************** + * + * class ScaleEnvelope -- constant scaling factor + * + *****************************************************************************/ #ifndef SCALEENVELOPE_HPP_INCLUDED #define SCALEENVELOPE_HPP_INCLUDED @@ -29,41 +29,37 @@ namespace srreal { /// @class ScaleEnvelope /// @brief constant PDF scaling factor -class DLL_EXPORT ScaleEnvelope : public PDFEnvelope -{ - public: - - // constructors - ScaleEnvelope(); - PDFEnvelopePtr create() const; - PDFEnvelopePtr clone() const; - - // methods - const std::string& type() const; - double operator()(const double& r) const; - void setScale(double sc); - const double& getScale() const; - - private: - - // data - double mscale; - - // serialization - friend class boost::serialization::access; - - template - void serialize(Archive& ar, const unsigned int version) - { - using boost::serialization::base_object; - ar & base_object(*this); - ar & mscale; - } +class DLL_EXPORT ScaleEnvelope : public PDFEnvelope { + public: + // constructors + ScaleEnvelope(); + PDFEnvelopePtr create() const; + PDFEnvelopePtr clone() const; + + // methods + const std::string& type() const; + double operator()(const double& r) const; + void setScale(double sc); + const double& getScale() const; + + private: + // data + double mscale; + + // serialization + friend class boost::serialization::access; + + template + void serialize(Archive& ar, const unsigned int version) { + using boost::serialization::base_object; + ar& base_object(*this); + ar & mscale; + } }; // class ScaleEnvelope -} // namespace srreal -} // namespace diffpy +} // namespace srreal +} // namespace diffpy // Serialization ------------------------------------------------------------- diff --git a/src/diffpy/srreal/ScatteringFactorTable.cpp b/src/diffpy/srreal/ScatteringFactorTable.cpp index 07727d62..28ea556b 100644 --- a/src/diffpy/srreal/ScatteringFactorTable.cpp +++ b/src/diffpy/srreal/ScatteringFactorTable.cpp @@ -1,20 +1,20 @@ /***************************************************************************** -* -* 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.txt for a list of people who contributed. -* See LICENSE_DANSE.txt for license information. -* -****************************************************************************** -* -* class ScatteringFactorTable -- base class for looking up scattering factors -* -*****************************************************************************/ + * + * 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.txt for a list of people who contributed. + * See LICENSE_DANSE.txt for license information. + * + ****************************************************************************** + * + * class ScatteringFactorTable -- base class for looking up scattering factors + * + *****************************************************************************/ #include #include @@ -35,131 +35,104 @@ namespace srreal { // public methods -bool ScatteringFactorTable::registerThisType() const -{ - typedef diffpy::HasClassRegistry HCRBase; - bool rv = this->HCRBase::registerThisType(); - rv = rv && ScatteringFactorTable::aliasType( - this->type(), this->radiationType()); - return rv; +bool ScatteringFactorTable::registerThisType() const { + typedef diffpy::HasClassRegistry HCRBase; + bool rv = this->HCRBase::registerThisType(); + rv = + rv && ScatteringFactorTable::aliasType(this->type(), this->radiationType()); + return rv; } - -double ScatteringFactorTable::lookup(const string& smbl, double q) const -{ - double rv; - CustomDataStorage::const_iterator csft = mcustom.find(smbl); - if (csft != mcustom.end()) - { - const string& srcsmbl = csft->second.first; - const double& scale = csft->second.second; - rv = this->standardLookup(srcsmbl, q) * scale; - } - else { - rv = this->standardLookup(smbl, q); - } - return rv; +double ScatteringFactorTable::lookup(const string& smbl, double q) const { + double rv; + CustomDataStorage::const_iterator csft = mcustom.find(smbl); + if (csft != mcustom.end()) { + const string& srcsmbl = csft->second.first; + const double& scale = csft->second.second; + rv = this->standardLookup(srcsmbl, q) * scale; + } else { + rv = this->standardLookup(smbl, q); + } + return rv; } - -void ScatteringFactorTable::setCustomAs( - const string& smbl, const string& srcsmbl) -{ - const double scale = 1.0; - CustomDataStorage::mapped_type entry(srcsmbl, scale); - if (mcustom.count(smbl) && mcustom.at(smbl) == entry) return; - mcustom[smbl] = entry; - mticker.click(); +void ScatteringFactorTable::setCustomAs(const string& smbl, + const string& srcsmbl) { + const double scale = 1.0; + CustomDataStorage::mapped_type entry(srcsmbl, scale); + if (mcustom.count(smbl) && mcustom.at(smbl) == entry) return; + mcustom[smbl] = entry; + mticker.click(); } - -void ScatteringFactorTable::setCustomAs( - const string& smbl, const string& srcsmbl, - double value, double q) -{ - double fsrc = this->standardLookup(srcsmbl, q); - double scale = value / fsrc; - CustomDataStorage::mapped_type entry(srcsmbl, scale); - if (mcustom.count(smbl) && mcustom.at(smbl) == entry) return; - mcustom[smbl] = entry; - mticker.click(); +void ScatteringFactorTable::setCustomAs(const string& smbl, + const string& srcsmbl, double value, + double q) { + double fsrc = this->standardLookup(srcsmbl, q); + double scale = value / fsrc; + CustomDataStorage::mapped_type entry(srcsmbl, scale); + if (mcustom.count(smbl) && mcustom.at(smbl) == entry) return; + mcustom[smbl] = entry; + mticker.click(); } - -void ScatteringFactorTable::resetCustom(const string& smbl) -{ - if (mcustom.count(smbl)) mticker.click(); - mcustom.erase(smbl); +void ScatteringFactorTable::resetCustom(const string& smbl) { + if (mcustom.count(smbl)) mticker.click(); + mcustom.erase(smbl); } - -void ScatteringFactorTable::resetAll() -{ - if (!mcustom.empty()) mticker.click(); - mcustom.clear(); +void ScatteringFactorTable::resetAll() { + if (!mcustom.empty()) mticker.click(); + mcustom.clear(); } - -unordered_set ScatteringFactorTable::getCustomSymbols() const -{ - unordered_set rv; - CustomDataStorage::const_iterator csft; - for (csft = mcustom.begin(); csft != mcustom.end(); ++csft) - { - rv.insert(csft->first); - } - return rv; +unordered_set ScatteringFactorTable::getCustomSymbols() const { + unordered_set rv; + CustomDataStorage::const_iterator csft; + for (csft = mcustom.begin(); csft != mcustom.end(); ++csft) { + rv.insert(csft->first); + } + return rv; } // class ScatteringFactorTableOwner ------------------------------------------ void ScatteringFactorTableOwner::setScatteringFactorTable( - ScatteringFactorTablePtr sft) -{ - ensureNonNull("ScatteringFactorTable", sft); - if (msftable != sft) mprivateticker.click(); - msftable = sft; + ScatteringFactorTablePtr sft) { + ensureNonNull("ScatteringFactorTable", sft); + if (msftable != sft) mprivateticker.click(); + msftable = sft; } - void ScatteringFactorTableOwner::setScatteringFactorTableByType( - const string& tp) -{ - msftable = ScatteringFactorTable::createByType(tp); - mprivateticker.click(); + const string& tp) { + msftable = ScatteringFactorTable::createByType(tp); + mprivateticker.click(); } - ScatteringFactorTablePtr& -ScatteringFactorTableOwner::getScatteringFactorTable() -{ - return msftable; +ScatteringFactorTableOwner::getScatteringFactorTable() { + return msftable; } - const ScatteringFactorTablePtr& -ScatteringFactorTableOwner::getScatteringFactorTable() const -{ - return msftable; +ScatteringFactorTableOwner::getScatteringFactorTable() const { + return msftable; } - -const string& ScatteringFactorTableOwner::getRadiationType() const -{ - static string empty; - const string& tp = msftable.get() ? msftable->radiationType() : empty; - return tp; +const string& ScatteringFactorTableOwner::getRadiationType() const { + static string empty; + const string& tp = msftable.get() ? msftable->radiationType() : empty; + return tp; } - -eventticker::EventTicker& ScatteringFactorTableOwner::ticker() const -{ - if (msftable) mprivateticker.updateFrom(msftable->ticker()); - return mprivateticker; +eventticker::EventTicker& ScatteringFactorTableOwner::ticker() const { + if (msftable) mprivateticker.updateFrom(msftable->ticker()); + return mprivateticker; } -} // namespace srreal -} // namespace diffpy +} // namespace srreal +} // namespace diffpy // Serialization ------------------------------------------------------------- diff --git a/src/diffpy/srreal/ScatteringFactorTable.hpp b/src/diffpy/srreal/ScatteringFactorTable.hpp index 1f798438..6a17adb4 100644 --- a/src/diffpy/srreal/ScatteringFactorTable.hpp +++ b/src/diffpy/srreal/ScatteringFactorTable.hpp @@ -1,23 +1,23 @@ /***************************************************************************** -* -* 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.txt for a list of people who contributed. -* See LICENSE_DANSE.txt for license information. -* -****************************************************************************** -* -* class ScatteringFactorTable -- base class for looking up scattering factors -* -* class ScatteringFactorTableOwner -- to be used as a base class for classes -* that own ScatteringFactorTable -* -*****************************************************************************/ + * + * 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.txt for a list of people who contributed. + * See LICENSE_DANSE.txt for license information. + * + ****************************************************************************** + * + * class ScatteringFactorTable -- base class for looking up scattering factors + * + * class ScatteringFactorTableOwner -- to be used as a base class for classes + * that own ScatteringFactorTable + * + *****************************************************************************/ #ifndef SCATTERINGFACTORTABLE_HPP_INCLUDED #define SCATTERINGFACTORTABLE_HPP_INCLUDED @@ -38,78 +38,68 @@ namespace diffpy { namespace srreal { -class DLL_EXPORT ScatteringFactorTable : - public diffpy::HasClassRegistry -{ - public: - - // HasClassRegistry override - bool registerThisType() const; - - // own methods - virtual const std::string& radiationType() const = 0; - double lookup(const std::string& smbl, double q=0.0) const; - virtual double standardLookup(const std::string&, double) const = 0; - void setCustomAs(const std::string& smbl, const std::string& srcsmbl); - void setCustomAs(const std::string& smbl, const std::string& srcsmbl, - double value, double q=0.0); - void resetCustom(const std::string& smbl); - void resetAll(); - std::unordered_set getCustomSymbols() const; - - typedef std::unordered_map > CustomDataStorage; - virtual eventticker::EventTicker& ticker() const { return mticker; } - - protected: - - // data - CustomDataStorage mcustom; - mutable eventticker::EventTicker mticker; - - private: - - // serialization - friend class boost::serialization::access; - template - void serialize(Archive& ar, const unsigned int version) - { - ar & mcustom & mticker; - } - +class DLL_EXPORT ScatteringFactorTable + : public diffpy::HasClassRegistry { + public: + // HasClassRegistry override + bool registerThisType() const; + + // own methods + virtual const std::string& radiationType() const = 0; + double lookup(const std::string& smbl, double q = 0.0) const; + virtual double standardLookup(const std::string&, double) const = 0; + void setCustomAs(const std::string& smbl, const std::string& srcsmbl); + void setCustomAs(const std::string& smbl, const std::string& srcsmbl, + double value, double q = 0.0); + void resetCustom(const std::string& smbl); + void resetAll(); + std::unordered_set getCustomSymbols() const; + + typedef std::unordered_map > + CustomDataStorage; + virtual eventticker::EventTicker& ticker() const { return mticker; } + + protected: + // data + CustomDataStorage mcustom; + mutable eventticker::EventTicker mticker; + + private: + // serialization + friend class boost::serialization::access; + template + void serialize(Archive& ar, const unsigned int version) { + ar & mcustom & mticker; + } }; typedef ScatteringFactorTable::SharedPtr ScatteringFactorTablePtr; -class DLL_EXPORT ScatteringFactorTableOwner -{ - public: - - // access and configuration of scattering factors - void setScatteringFactorTable(ScatteringFactorTablePtr); - void setScatteringFactorTableByType(const std::string& tp); - ScatteringFactorTablePtr& getScatteringFactorTable(); - const ScatteringFactorTablePtr& getScatteringFactorTable() const; - const std::string& getRadiationType() const; - eventticker::EventTicker& ticker() const; - - private: - - // data - ScatteringFactorTablePtr msftable; - mutable eventticker::EventTicker mprivateticker; - - // serialization - friend class boost::serialization::access; - template - void serialize(Archive& ar, const unsigned int version) - { - ar & msftable & mprivateticker; - } +class DLL_EXPORT ScatteringFactorTableOwner { + public: + // access and configuration of scattering factors + void setScatteringFactorTable(ScatteringFactorTablePtr); + void setScatteringFactorTableByType(const std::string& tp); + ScatteringFactorTablePtr& getScatteringFactorTable(); + const ScatteringFactorTablePtr& getScatteringFactorTable() const; + const std::string& getRadiationType() const; + eventticker::EventTicker& ticker() const; + + private: + // data + ScatteringFactorTablePtr msftable; + mutable eventticker::EventTicker mprivateticker; + + // serialization + friend class boost::serialization::access; + template + void serialize(Archive& ar, const unsigned int version) { + ar & msftable & mprivateticker; + } }; -} // namespace srreal -} // namespace diffpy +} // namespace srreal +} // namespace diffpy // Serialization ------------------------------------------------------------- diff --git a/src/diffpy/srreal/SphericalShapeEnvelope.cpp b/src/diffpy/srreal/SphericalShapeEnvelope.cpp index c7f7388c..7e66437d 100644 --- a/src/diffpy/srreal/SphericalShapeEnvelope.cpp +++ b/src/diffpy/srreal/SphericalShapeEnvelope.cpp @@ -1,20 +1,20 @@ /***************************************************************************** -* -* 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.txt for a list of people who contributed. -* See LICENSE_DANSE.txt for license information. -* -****************************************************************************** -* -* class SphericalShapeEnvelope -- PDF envelope due to spherical shape factor -* -*****************************************************************************/ + * + * 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.txt for a list of people who contributed. + * See LICENSE_DANSE.txt for license information. + * + ****************************************************************************** + * + * class SphericalShapeEnvelope -- PDF envelope due to spherical shape factor + * + *****************************************************************************/ #include @@ -28,64 +28,50 @@ namespace srreal { // Constructor --------------------------------------------------------------- -SphericalShapeEnvelope::SphericalShapeEnvelope() -{ - this->setSPDiameter(0.0); - this->registerDoubleAttribute("spdiameter", this, - &SphericalShapeEnvelope::getSPDiameter, - &SphericalShapeEnvelope::setSPDiameter); +SphericalShapeEnvelope::SphericalShapeEnvelope() { + this->setSPDiameter(0.0); + this->registerDoubleAttribute("spdiameter", this, + &SphericalShapeEnvelope::getSPDiameter, + &SphericalShapeEnvelope::setSPDiameter); } - -PDFEnvelopePtr SphericalShapeEnvelope::create() const -{ - PDFEnvelopePtr rv(new SphericalShapeEnvelope()); - return rv; +PDFEnvelopePtr SphericalShapeEnvelope::create() const { + PDFEnvelopePtr rv(new SphericalShapeEnvelope()); + return rv; } - -PDFEnvelopePtr SphericalShapeEnvelope::clone() const -{ - PDFEnvelopePtr rv(new SphericalShapeEnvelope(*this)); - return rv; +PDFEnvelopePtr SphericalShapeEnvelope::clone() const { + PDFEnvelopePtr rv(new SphericalShapeEnvelope(*this)); + return rv; } // Public Methods ------------------------------------------------------------ -const string& SphericalShapeEnvelope::type() const -{ - static string rv = "sphericalshape"; - return rv; -} - - -double SphericalShapeEnvelope::operator()(const double& r) const -{ - if (mspdiameter <= 0.0) return 1.0; - if (r > mspdiameter) return 0.0; - double rdratio = r / mspdiameter; - double rv = 1.0 - 1.5 * rdratio + 0.5 * pow(rdratio, 3); - return rv; +const string& SphericalShapeEnvelope::type() const { + static string rv = "sphericalshape"; + return rv; } - -void SphericalShapeEnvelope::setSPDiameter(double spd) -{ - mspdiameter = spd; +double SphericalShapeEnvelope::operator()(const double& r) const { + if (mspdiameter <= 0.0) return 1.0; + if (r > mspdiameter) return 0.0; + double rdratio = r / mspdiameter; + double rv = 1.0 - 1.5 * rdratio + 0.5 * pow(rdratio, 3); + return rv; } +void SphericalShapeEnvelope::setSPDiameter(double spd) { mspdiameter = spd; } -const double& SphericalShapeEnvelope::getSPDiameter() const -{ - return mspdiameter; +const double& SphericalShapeEnvelope::getSPDiameter() const { + return mspdiameter; } // Registration -------------------------------------------------------------- bool reg_SphericalShapeEnvelope = SphericalShapeEnvelope().registerThisType(); -} // namespace srreal -} // namespace diffpy +} // namespace srreal +} // namespace diffpy // Serialization ------------------------------------------------------------- diff --git a/src/diffpy/srreal/SphericalShapeEnvelope.hpp b/src/diffpy/srreal/SphericalShapeEnvelope.hpp index e6e95bb5..e7a2c534 100644 --- a/src/diffpy/srreal/SphericalShapeEnvelope.hpp +++ b/src/diffpy/srreal/SphericalShapeEnvelope.hpp @@ -1,20 +1,20 @@ /***************************************************************************** -* -* 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.txt for a list of people who contributed. -* See LICENSE_DANSE.txt for license information. -* -****************************************************************************** -* -* class SphericalShapeEnvelope -- PDF envelope due to spherical shape factor -* -*****************************************************************************/ + * + * 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.txt for a list of people who contributed. + * See LICENSE_DANSE.txt for license information. + * + ****************************************************************************** + * + * class SphericalShapeEnvelope -- PDF envelope due to spherical shape factor + * + *****************************************************************************/ #ifndef SPHERICALSHAPEENVELOPE_HPP_INCLUDED #define SPHERICALSHAPEENVELOPE_HPP_INCLUDED @@ -31,41 +31,37 @@ namespace srreal { /// Spherical shape envelope is not applied for spherical particle /// diameter, spdiameter, smaller or equal to zero. -class DLL_EXPORT SphericalShapeEnvelope : public PDFEnvelope -{ - public: - - // constructors - SphericalShapeEnvelope(); - virtual PDFEnvelopePtr create() const; - virtual PDFEnvelopePtr clone() const; - - // methods - virtual const std::string& type() const; - virtual double operator()(const double& r) const; - void setSPDiameter(double spd); - const double& getSPDiameter() const; - - private: - - // data - double mspdiameter; - - // serialization - friend class boost::serialization::access; - - template - void serialize(Archive& ar, const unsigned int version) - { - using boost::serialization::base_object; - ar & base_object(*this); - ar & mspdiameter; - } +class DLL_EXPORT SphericalShapeEnvelope : public PDFEnvelope { + public: + // constructors + SphericalShapeEnvelope(); + virtual PDFEnvelopePtr create() const; + virtual PDFEnvelopePtr clone() const; + + // methods + virtual const std::string& type() const; + virtual double operator()(const double& r) const; + void setSPDiameter(double spd); + const double& getSPDiameter() const; + + private: + // data + double mspdiameter; + + // serialization + friend class boost::serialization::access; + + template + void serialize(Archive& ar, const unsigned int version) { + using boost::serialization::base_object; + ar& base_object(*this); + ar & mspdiameter; + } }; // class SphericalShapeEnvelope -} // namespace srreal -} // namespace diffpy +} // namespace srreal +} // namespace diffpy // Serialization ------------------------------------------------------------- diff --git a/src/diffpy/srreal/StepCutEnvelope.cpp b/src/diffpy/srreal/StepCutEnvelope.cpp index 0c186693..e2c1ea7e 100644 --- a/src/diffpy/srreal/StepCutEnvelope.cpp +++ b/src/diffpy/srreal/StepCutEnvelope.cpp @@ -1,20 +1,20 @@ /***************************************************************************** -* -* 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.txt for a list of people who contributed. -* See LICENSE_DANSE.txt for license information. -* -****************************************************************************** -* -* class StepCutEnvelope -- empirical step-function PDF envelope. -* -*****************************************************************************/ + * + * 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.txt for a list of people who contributed. + * See LICENSE_DANSE.txt for license information. + * + ****************************************************************************** + * + * class StepCutEnvelope -- empirical step-function PDF envelope. + * + *****************************************************************************/ #include #include @@ -26,60 +26,44 @@ namespace srreal { // Constructor --------------------------------------------------------------- -StepCutEnvelope::StepCutEnvelope() -{ - this->setStepCut(0.0); - this->registerDoubleAttribute("stepcut", this, - &StepCutEnvelope::getStepCut, &StepCutEnvelope::setStepCut); +StepCutEnvelope::StepCutEnvelope() { + this->setStepCut(0.0); + this->registerDoubleAttribute("stepcut", this, &StepCutEnvelope::getStepCut, + &StepCutEnvelope::setStepCut); } - -PDFEnvelopePtr StepCutEnvelope::create() const -{ - PDFEnvelopePtr rv(new StepCutEnvelope()); - return rv; +PDFEnvelopePtr StepCutEnvelope::create() const { + PDFEnvelopePtr rv(new StepCutEnvelope()); + return rv; } - -PDFEnvelopePtr StepCutEnvelope::clone() const -{ - PDFEnvelopePtr rv(new StepCutEnvelope(*this)); - return rv; +PDFEnvelopePtr StepCutEnvelope::clone() const { + PDFEnvelopePtr rv(new StepCutEnvelope(*this)); + return rv; } // Public Methods ------------------------------------------------------------ -const string& StepCutEnvelope::type() const -{ - static string rv = "stepcut"; - return rv; +const string& StepCutEnvelope::type() const { + static string rv = "stepcut"; + return rv; } - -double StepCutEnvelope::operator()(const double& r) const -{ - double rv = (mstepcut > 0.0 && r > mstepcut) ? 0.0 : 1.0; - return rv; +double StepCutEnvelope::operator()(const double& r) const { + double rv = (mstepcut > 0.0 && r > mstepcut) ? 0.0 : 1.0; + return rv; } +void StepCutEnvelope::setStepCut(double sc) { mstepcut = sc; } -void StepCutEnvelope::setStepCut(double sc) -{ - mstepcut = sc; -} - - -const double& StepCutEnvelope::getStepCut() const -{ - return mstepcut; -} +const double& StepCutEnvelope::getStepCut() const { return mstepcut; } // Registration -------------------------------------------------------------- bool reg_StepCutEnvelope = StepCutEnvelope().registerThisType(); -} // namespace srreal -} // namespace diffpy +} // namespace srreal +} // namespace diffpy // Serialization ------------------------------------------------------------- diff --git a/src/diffpy/srreal/StepCutEnvelope.hpp b/src/diffpy/srreal/StepCutEnvelope.hpp index 4460d874..f47e5721 100644 --- a/src/diffpy/srreal/StepCutEnvelope.hpp +++ b/src/diffpy/srreal/StepCutEnvelope.hpp @@ -1,20 +1,20 @@ /***************************************************************************** -* -* 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.txt for a list of people who contributed. -* See LICENSE_DANSE.txt for license information. -* -****************************************************************************** -* -* class StepCutEnvelope -- empirical step-function PDF envelope. -* -*****************************************************************************/ + * + * 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.txt for a list of people who contributed. + * See LICENSE_DANSE.txt for license information. + * + ****************************************************************************** + * + * class StepCutEnvelope -- empirical step-function PDF envelope. + * + *****************************************************************************/ #ifndef STEPCUTENVELOPE_HPP_INCLUDED #define STEPCUTENVELOPE_HPP_INCLUDED @@ -30,42 +30,38 @@ namespace srreal { /// @brief empirical step-function PDF envelope. The envelope is not /// applied when the cutoff radius stepcut is smaller or equal zero. -class DLL_EXPORT StepCutEnvelope : public PDFEnvelope -{ - public: +class DLL_EXPORT StepCutEnvelope : public PDFEnvelope { + public: + // constructors + StepCutEnvelope(); + virtual PDFEnvelopePtr create() const; + virtual PDFEnvelopePtr clone() const; - // constructors - StepCutEnvelope(); - virtual PDFEnvelopePtr create() const; - virtual PDFEnvelopePtr clone() const; + // methods - // methods + virtual const std::string& type() const; + virtual double operator()(const double& r) const; + void setStepCut(double sc); + const double& getStepCut() const; - virtual const std::string& type() const; - virtual double operator()(const double& r) const; - void setStepCut(double sc); - const double& getStepCut() const; + private: + // data + double mstepcut; - private: + // serialization + friend class boost::serialization::access; - // data - double mstepcut; - - // serialization - friend class boost::serialization::access; - - template - void serialize(Archive& ar, const unsigned int version) - { - using boost::serialization::base_object; - ar & base_object(*this); - ar & mstepcut; - } + template + void serialize(Archive& ar, const unsigned int version) { + using boost::serialization::base_object; + ar& base_object(*this); + ar & mstepcut; + } }; // class StepCutEnvelope -} // namespace srreal -} // namespace diffpy +} // namespace srreal +} // namespace diffpy // Serialization ------------------------------------------------------------- diff --git a/src/diffpy/srreal/StructureAdapter.cpp b/src/diffpy/srreal/StructureAdapter.cpp index d3468ea4..b5ac0814 100644 --- a/src/diffpy/srreal/StructureAdapter.cpp +++ b/src/diffpy/srreal/StructureAdapter.cpp @@ -1,21 +1,21 @@ /***************************************************************************** -* -* 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.txt for a list of people who contributed. -* See LICENSE_DANSE.txt for license information. -* -****************************************************************************** -* -* class StructureAdapter -- abstract base class for interfacing general -* structure objects with srreal classes such as PairQuantity -* -*****************************************************************************/ + * + * 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.txt for a list of people who contributed. + * See LICENSE_DANSE.txt for license information. + * + ****************************************************************************** + * + * class StructureAdapter -- abstract base class for interfacing general + * structure objects with srreal classes such as PairQuantity + * + *****************************************************************************/ #include #include @@ -33,98 +33,70 @@ namespace srreal { // Public Methods ------------------------------------------------------------ -double StructureAdapter::totalOccupancy() const -{ - double total_occupancy = 0.0; - int cntsites = this->countSites(); - for (int i = 0; i < cntsites; ++i) - { - total_occupancy += this->siteOccupancy(i) * this->siteMultiplicity(i); - } - return total_occupancy; -} - - -double StructureAdapter::numberDensity() const -{ - return 0.0; -} - - -const string& StructureAdapter::siteAtomType(int idx) const -{ - static string rv = ""; - return rv; +double StructureAdapter::totalOccupancy() const { + double total_occupancy = 0.0; + int cntsites = this->countSites(); + for (int i = 0; i < cntsites; ++i) { + total_occupancy += this->siteOccupancy(i) * this->siteMultiplicity(i); + } + return total_occupancy; } +double StructureAdapter::numberDensity() const { return 0.0; } -int StructureAdapter::siteMultiplicity(int idx) const -{ - return 1; +const string& StructureAdapter::siteAtomType(int idx) const { + static string rv = ""; + return rv; } +int StructureAdapter::siteMultiplicity(int idx) const { return 1; } -double StructureAdapter::siteOccupancy(int idx) const -{ - return 1.0; -} - +double StructureAdapter::siteOccupancy(int idx) const { return 1.0; } -StructureDifference -StructureAdapter::diff(StructureAdapterConstPtr other) const -{ - StructureDifference sd(this->shared_from_this(), other); - return sd; +StructureDifference StructureAdapter::diff( + StructureAdapterConstPtr other) const { + StructureDifference sd(this->shared_from_this(), other); + return sd; } // Routines ------------------------------------------------------------------ -double meanSquareDisplacement(const R3::Matrix& Uijcartn, - const R3::Vector& s, bool anisotropy) -{ - double rv; - if (anisotropy) - { - assert(R3::norm(s) > 0); - assert(eps_eq(Uijcartn(0,1), Uijcartn(1,0))); - assert(eps_eq(Uijcartn(0,2), Uijcartn(2,0))); - assert(eps_eq(Uijcartn(1,2), Uijcartn(2,1))); - static R3::Vector sn; - sn = s / R3::norm(s); - rv = Uijcartn(0,0) * sn(0) * sn(0) + - Uijcartn(1,1) * sn(1) * sn(1) + - Uijcartn(2,2) * sn(2) * sn(2) + - 2 * Uijcartn(0,1) * sn(0) * sn(1) + - 2 * Uijcartn(0,2) * sn(0) * sn(2) + - 2 * Uijcartn(1,2) * sn(1) * sn(2); - } - else - { - assert(eps_eq(Uijcartn(0,0), Uijcartn(1,1))); - assert(eps_eq(Uijcartn(0,0), Uijcartn(2,2))); - rv = Uijcartn(0,0); - } - return rv; +double meanSquareDisplacement(const R3::Matrix& Uijcartn, const R3::Vector& s, + bool anisotropy) { + double rv; + if (anisotropy) { + assert(R3::norm(s) > 0); + assert(eps_eq(Uijcartn(0, 1), Uijcartn(1, 0))); + assert(eps_eq(Uijcartn(0, 2), Uijcartn(2, 0))); + assert(eps_eq(Uijcartn(1, 2), Uijcartn(2, 1))); + static R3::Vector sn; + sn = s / R3::norm(s); + rv = Uijcartn(0, 0) * sn(0) * sn(0) + Uijcartn(1, 1) * sn(1) * sn(1) + + Uijcartn(2, 2) * sn(2) * sn(2) + 2 * Uijcartn(0, 1) * sn(0) * sn(1) + + 2 * Uijcartn(0, 2) * sn(0) * sn(2) + + 2 * Uijcartn(1, 2) * sn(1) * sn(2); + } else { + assert(eps_eq(Uijcartn(0, 0), Uijcartn(1, 1))); + assert(eps_eq(Uijcartn(0, 0), Uijcartn(2, 2))); + rv = Uijcartn(0, 0); + } + return rv; } - -double maxUii(StructureAdapterPtr stru) -{ - if (!stru) return 0.0; - double rv = 0.0; - for (int i = 0; i < stru->countSites(); ++i) - { - const R3::Matrix& U = stru->siteCartesianUij(i); - for (int k = 0; k < R3::Ndim; k++) - { - if (U(k,k) > rv) rv = U(k,k); - } +double maxUii(StructureAdapterPtr stru) { + if (!stru) return 0.0; + double rv = 0.0; + for (int i = 0; i < stru->countSites(); ++i) { + const R3::Matrix& U = stru->siteCartesianUij(i); + for (int k = 0; k < R3::Ndim; k++) { + if (U(k, k) > rv) rv = U(k, k); } - return rv; + } + return rv; } -} // namespace srreal -} // namespace diffpy +} // namespace srreal +} // namespace diffpy DIFFPY_INSTANTIATE_PTR_SERIALIZATION(diffpy::srreal::StructureAdapter) diff --git a/src/diffpy/srreal/StructureAdapter.hpp b/src/diffpy/srreal/StructureAdapter.hpp index 2b707129..40adc91d 100644 --- a/src/diffpy/srreal/StructureAdapter.hpp +++ b/src/diffpy/srreal/StructureAdapter.hpp @@ -1,21 +1,21 @@ /***************************************************************************** -* -* 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.txt for a list of people who contributed. -* See LICENSE_DANSE.txt for license information. -* -****************************************************************************** -* -* class StructureAdapter -- abstract base class for interfacing general -* structure objects with srreal classes such as PairQuantity -* -*****************************************************************************/ + * + * 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.txt for a list of people who contributed. + * See LICENSE_DANSE.txt for license information. + * + ****************************************************************************** + * + * class StructureAdapter -- abstract base class for interfacing general + * structure objects with srreal classes such as PairQuantity + * + *****************************************************************************/ #ifndef STRUCTUREADAPTER_HPP_INCLUDED #define STRUCTUREADAPTER_HPP_INCLUDED @@ -35,9 +35,9 @@ #include #ifdef _MSC_VER - #define DEPRECATED __declspec(deprecated) +#define DEPRECATED __declspec(deprecated) #else - #define DEPRECATED __attribute__((deprecated)) +#define DEPRECATED __attribute__((deprecated)) #endif namespace diffpy { @@ -46,69 +46,64 @@ namespace srreal { class PairQuantity; class StructureDifference; - /// @class StructureAdapter /// @brief abstract adaptor to structure data needed by /// PairQuantity calculator -class DLL_EXPORT StructureAdapter : - public boost::enable_shared_from_this -{ - public: - - virtual ~StructureAdapter() { } - - // methods - virtual StructureAdapterPtr clone() const = 0; +class DLL_EXPORT StructureAdapter + : public boost::enable_shared_from_this { + public: + virtual ~StructureAdapter() {} - /// factory for creating compatible BondGenerator instance. - virtual BaseBondGeneratorPtr createBondGenerator() const = 0; + // methods + virtual StructureAdapterPtr clone() const = 0; - /// number of independent sites in the structure, before - /// any symmetry expansion. - virtual int countSites() const = 0; + /// factory for creating compatible BondGenerator instance. + virtual BaseBondGeneratorPtr createBondGenerator() const = 0; - /// total number of atoms in the structure unit accounting - /// for possibly fractional occupancies. - double totalOccupancy() const; + /// number of independent sites in the structure, before + /// any symmetry expansion. + virtual int countSites() const = 0; - /// number density in the structure model or 0 when not defined. - virtual double numberDensity() const; + /// total number of atoms in the structure unit accounting + /// for possibly fractional occupancies. + double totalOccupancy() const; - /// symbol for element or ion at the independent site @param idx - virtual const std::string& siteAtomType(int idx) const; + /// number density in the structure model or 0 when not defined. + virtual double numberDensity() const; - /// Cartesian coordinates of the independent site @param idx - virtual const R3::Vector& siteCartesianPosition(int idx) const = 0; + /// symbol for element or ion at the independent site @param idx + virtual const std::string& siteAtomType(int idx) const; - /// multiplicity of the independent site @param idx in the structure - virtual int siteMultiplicity(int idx) const; + /// Cartesian coordinates of the independent site @param idx + virtual const R3::Vector& siteCartesianPosition(int idx) const = 0; - /// site occupancy at the independent site @param idx - virtual double siteOccupancy(int idx) const; + /// multiplicity of the independent site @param idx in the structure + virtual int siteMultiplicity(int idx) const; - /// flag for anisotropic atom displacements at independent site - /// @param idx - virtual bool siteAnisotropy(int idx) const = 0; + /// site occupancy at the independent site @param idx + virtual double siteOccupancy(int idx) const; - /// tensor of atom displacement parameters converted in - /// Cartesian coordinate system at independent site @param idx - virtual const R3::Matrix& siteCartesianUij(int idx) const = 0; + /// flag for anisotropic atom displacements at independent site + /// @param idx + virtual bool siteAnisotropy(int idx) const = 0; - /// this method allows custom special configuration for a concrete - /// pair of StructureAdapter and PairQuantity objects. - virtual DEPRECATED void customPQConfig(PairQuantity* pq) const { } + /// tensor of atom displacement parameters converted in + /// Cartesian coordinate system at independent site @param idx + virtual const R3::Matrix& siteCartesianUij(int idx) const = 0; - /// Return difference from the other StructureAdapter - virtual StructureDifference diff(StructureAdapterConstPtr) const; + /// this method allows custom special configuration for a concrete + /// pair of StructureAdapter and PairQuantity objects. + virtual DEPRECATED void customPQConfig(PairQuantity* pq) const {} - private: - - // serialization - friend class boost::serialization::access; - template - void serialize(Archive& ar, const unsigned int version) { } + /// Return difference from the other StructureAdapter + virtual StructureDifference diff(StructureAdapterConstPtr) const; + private: + // serialization + friend class boost::serialization::access; + template + void serialize(Archive& ar, const unsigned int version) {} }; // Routines ------------------------------------------------------------------ @@ -118,25 +113,23 @@ DLL_EXPORT StructureAdapterPtr emptyStructureAdapter(); /// Calculate MSD along specified direction in Cartesian space. double meanSquareDisplacement(const R3::Matrix& Uijcartn, const R3::Vector& s, - bool anisotropy=true); + bool anisotropy = true); /// Maximum diagonal Uii element from all atoms in the structure. double maxUii(StructureAdapterPtr stru); /// Translate an index container to a vector of string symbols template -std::vector -siteIndicesToTypes(const StructureAdapterPtr& stru, const T& indices) -{ - std::vector rv; - rv.reserve(stru->countSites()); - typename T::const_iterator i; - for (i = indices.begin(); i != indices.end(); ++i) - { - const std::string& smbl = stru->siteAtomType(*i); - rv.push_back(smbl); - } - return rv; +std::vector siteIndicesToTypes(const StructureAdapterPtr& stru, + const T& indices) { + std::vector rv; + rv.reserve(stru->countSites()); + typename T::const_iterator i; + for (i = indices.begin(); i != indices.end(); ++i) { + const std::string& smbl = stru->siteAtomType(*i); + rv.push_back(smbl); + } + return rv; } // Conversion helpers -------------------------------------------------------- @@ -148,43 +141,38 @@ siteIndicesToTypes(const StructureAdapterPtr& stru, const T& indices) /// This converter provides by-value support for StructureAdapter instances. -inline -StructureAdapterPtr createStructureAdapter(const StructureAdapter& stru) -{ - return stru.clone(); +inline StructureAdapterPtr createStructureAdapter( + const StructureAdapter& stru) { + return stru.clone(); } /// convertToStructureAdapter is an interface function that should be used /// by the clients that need to convert to StructureAdapterPtr. template -StructureAdapterPtr convertToStructureAdapter(const T& stru) -{ - StructureAdapterPtr rv = createStructureAdapter(stru); - return rv; +StructureAdapterPtr convertToStructureAdapter(const T& stru) { + StructureAdapterPtr rv = createStructureAdapter(stru); + return rv; } - template -StructureAdapterPtr convertToStructureAdapter(const boost::shared_ptr& stru) -{ - StructureAdapterPtr rv = - boost::dynamic_pointer_cast(stru); - assert(rv); - return rv; +StructureAdapterPtr convertToStructureAdapter( + const boost::shared_ptr& stru) { + StructureAdapterPtr rv = + boost::dynamic_pointer_cast(stru); + assert(rv); + return rv; } - -inline -StructureAdapterPtr convertToStructureAdapter(StructureAdapterConstPtr stru) -{ - StructureAdapterPtr rv = - boost::const_pointer_cast(stru); - return rv; +inline StructureAdapterPtr convertToStructureAdapter( + StructureAdapterConstPtr stru) { + StructureAdapterPtr rv = + boost::const_pointer_cast(stru); + return rv; } -} // namespace srreal -} // namespace diffpy +} // namespace srreal +} // namespace diffpy // Serialization ------------------------------------------------------------- diff --git a/src/diffpy/srreal/StructureDifference.cpp b/src/diffpy/srreal/StructureDifference.cpp index 90a57cb6..4f146569 100644 --- a/src/diffpy/srreal/StructureDifference.cpp +++ b/src/diffpy/srreal/StructureDifference.cpp @@ -1,23 +1,23 @@ /***************************************************************************** -* -* libdiffpy Complex Modeling Initiative -* (c) 2013 Brookhaven Science Associates, -* Brookhaven National Laboratory. -* All rights reserved. -* -* File coded by: Pavol Juhas -* -* See AUTHORS.txt for a list of people who contributed. -* See LICENSE.txt for license information. -* -****************************************************************************** -* -* class StructureDifference for storing differences between two -* instances of the StructureAdapter class. -* -* StructureDifference is returned by the StructureAdapter::diff method. -* -*****************************************************************************/ + * + * libdiffpy Complex Modeling Initiative + * (c) 2013 Brookhaven Science Associates, + * Brookhaven National Laboratory. + * All rights reserved. + * + * File coded by: Pavol Juhas + * + * See AUTHORS.txt for a list of people who contributed. + * See LICENSE.txt for license information. + * + ****************************************************************************** + * + * class StructureDifference for storing differences between two + * instances of the StructureAdapter class. + * + * StructureDifference is returned by the StructureAdapter::diff method. + * + *****************************************************************************/ #include #include @@ -31,40 +31,33 @@ namespace srreal { // Constructors -------------------------------------------------------------- -StructureDifference::StructureDifference() : diffmethod(Method::NONE) -{ } - - -StructureDifference::StructureDifference( - StructureAdapterConstPtr oldstru, - StructureAdapterConstPtr newstru) - : - stru0(oldstru), stru1(newstru), diffmethod(Method::NONE) -{ - if (stru0 && stru0 == stru1) - { - diffmethod = Method::SIDEBYSIDE; - return; - } - // assume completely different structures - int v; - SiteIndices::iterator ii; - int n0 = stru0 ? stru0->countSites() : 0; - pop0.resize(n0); - for (ii = pop0.begin(), v = 0; ii != pop0.end();) *(ii++) = v++; - int n1 = stru1 ? stru1->countSites() : 0; - add1.resize(n1); - for (ii = add1.begin(), v = 0; ii != add1.end();) *(ii++) = v++; +StructureDifference::StructureDifference() : diffmethod(Method::NONE) {} + +StructureDifference::StructureDifference(StructureAdapterConstPtr oldstru, + StructureAdapterConstPtr newstru) + : stru0(oldstru), stru1(newstru), diffmethod(Method::NONE) { + if (stru0 && stru0 == stru1) { + diffmethod = Method::SIDEBYSIDE; + return; + } + // assume completely different structures + int v; + SiteIndices::iterator ii; + int n0 = stru0 ? stru0->countSites() : 0; + pop0.resize(n0); + for (ii = pop0.begin(), v = 0; ii != pop0.end();) *(ii++) = v++; + int n1 = stru1 ? stru1->countSites() : 0; + add1.resize(n1); + for (ii = add1.begin(), v = 0; ii != add1.end();) *(ii++) = v++; } // Public Methods ------------------------------------------------------------ -bool StructureDifference::allowsfastupdate() const -{ - int N0 = stru0 ? stru0->countSites() : 0; - double popbound = (1 - sqrt(0.5)) * N0; - return int(pop0.size()) < popbound; +bool StructureDifference::allowsfastupdate() const { + int N0 = stru0 ? stru0->countSites() : 0; + double popbound = (1 - sqrt(0.5)) * N0; + return int(pop0.size()) < popbound; } -} // namespace srreal -} // namespace diffpy +} // namespace srreal +} // namespace diffpy diff --git a/src/diffpy/srreal/StructureDifference.hpp b/src/diffpy/srreal/StructureDifference.hpp index 47bddd5b..980de159 100644 --- a/src/diffpy/srreal/StructureDifference.hpp +++ b/src/diffpy/srreal/StructureDifference.hpp @@ -1,23 +1,23 @@ /***************************************************************************** -* -* libdiffpy Complex Modeling Initiative -* (c) 2013 Brookhaven Science Associates, -* Brookhaven National Laboratory. -* All rights reserved. -* -* File coded by: Pavol Juhas -* -* See AUTHORS.txt for a list of people who contributed. -* See LICENSE.txt for license information. -* -****************************************************************************** -* -* class StructureDifference for storing differences between two -* instances of the StructureAdapter class. -* -* StructureDifference is returned by the StructureAdapter::diff method. -* -*****************************************************************************/ + * + * libdiffpy Complex Modeling Initiative + * (c) 2013 Brookhaven Science Associates, + * Brookhaven National Laboratory. + * All rights reserved. + * + * File coded by: Pavol Juhas + * + * See AUTHORS.txt for a list of people who contributed. + * See LICENSE.txt for license information. + * + ****************************************************************************** + * + * class StructureDifference for storing differences between two + * instances of the StructureAdapter class. + * + * StructureDifference is returned by the StructureAdapter::diff method. + * + *****************************************************************************/ #ifndef STRUCTUREDIFFERENCE_HPP_INCLUDED #define STRUCTUREDIFFERENCE_HPP_INCLUDED @@ -30,42 +30,39 @@ namespace diffpy { namespace srreal { class DLL_EXPORT StructureDifference { + public: + // constructors + StructureDifference(); + StructureDifference(StructureAdapterConstPtr, StructureAdapterConstPtr); - public: + // enumeration type for difference methods + struct Method { + enum Type { NONE, SIDEBYSIDE, SORTED }; + }; - // constructors - StructureDifference(); - StructureDifference(StructureAdapterConstPtr, StructureAdapterConstPtr); + // data - // enumeration type for difference methods - struct Method { - enum Type {NONE, SIDEBYSIDE, SORTED}; - }; + /// pointer to the original StructureAdapter + StructureAdapterConstPtr stru0; + /// pointer to the new StructureAdapter + StructureAdapterConstPtr stru1; + /// indices of atoms in stru0 that are not in stru1. + /// These atoms need to be removed for a fast update of PairQuantity. + SiteIndices pop0; + /// indices of atoms in stru1 that are not in stru0 + /// These atoms need to be added in a fast update of PairQuantity. + SiteIndices add1; + /// type of comparison used in obtaining this difference + Method::Type diffmethod; - // data - - /// pointer to the original StructureAdapter - StructureAdapterConstPtr stru0; - /// pointer to the new StructureAdapter - StructureAdapterConstPtr stru1; - /// indices of atoms in stru0 that are not in stru1. - /// These atoms need to be removed for a fast update of PairQuantity. - SiteIndices pop0; - /// indices of atoms in stru1 that are not in stru0 - /// These atoms need to be added in a fast update of PairQuantity. - SiteIndices add1; - /// type of comparison used in obtaining this difference - Method::Type diffmethod; - - // methods - - /// Return true if PairQuantity can be fast-updated after changing - /// structure from stru0 to stru1. - bool allowsfastupdate() const; + // methods + /// Return true if PairQuantity can be fast-updated after changing + /// structure from stru0 to stru1. + bool allowsfastupdate() const; }; -} // namespace srreal -} // namespace diffpy +} // namespace srreal +} // namespace diffpy #endif // STRUCTUREDIFFERENCE_HPP_INCLUDED diff --git a/src/diffpy/srreal/ZeroBaseline.cpp b/src/diffpy/srreal/ZeroBaseline.cpp index eede7bd5..f130ac1a 100644 --- a/src/diffpy/srreal/ZeroBaseline.cpp +++ b/src/diffpy/srreal/ZeroBaseline.cpp @@ -1,20 +1,20 @@ /***************************************************************************** -* -* 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.txt for a list of people who contributed. -* See LICENSE_DANSE.txt for license information. -* -****************************************************************************** -* -* class ZeroBaseline -- linear PDF baseline -* -*****************************************************************************/ + * + * 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.txt for a list of people who contributed. + * See LICENSE_DANSE.txt for license information. + * + ****************************************************************************** + * + * class ZeroBaseline -- linear PDF baseline + * + *****************************************************************************/ #include #include @@ -26,39 +26,31 @@ namespace srreal { // Constructors -------------------------------------------------------------- -PDFBaselinePtr ZeroBaseline::create() const -{ - PDFBaselinePtr rv(new ZeroBaseline()); - return rv; +PDFBaselinePtr ZeroBaseline::create() const { + PDFBaselinePtr rv(new ZeroBaseline()); + return rv; } - -PDFBaselinePtr ZeroBaseline::clone() const -{ - PDFBaselinePtr rv(new ZeroBaseline(*this)); - return rv; +PDFBaselinePtr ZeroBaseline::clone() const { + PDFBaselinePtr rv(new ZeroBaseline(*this)); + return rv; } // Public Methods ------------------------------------------------------------ -const string& ZeroBaseline::type() const -{ - static string rv = "zero"; - return rv; +const string& ZeroBaseline::type() const { + static string rv = "zero"; + return rv; } - -double ZeroBaseline::operator()(const double& r) const -{ - return 0.0; -} +double ZeroBaseline::operator()(const double& r) const { return 0.0; } // Registration -------------------------------------------------------------- bool reg_ZeroBaseline = ZeroBaseline().registerThisType(); -} // namespace srreal -} // namespace diffpy +} // namespace srreal +} // namespace diffpy // Serialization ------------------------------------------------------------- diff --git a/src/diffpy/srreal/ZeroBaseline.hpp b/src/diffpy/srreal/ZeroBaseline.hpp index a863b3e0..6ec0619d 100644 --- a/src/diffpy/srreal/ZeroBaseline.hpp +++ b/src/diffpy/srreal/ZeroBaseline.hpp @@ -1,20 +1,20 @@ /***************************************************************************** -* -* 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.txt for a list of people who contributed. -* See LICENSE_DANSE.txt for license information. -* -****************************************************************************** -* -* class ZeroBaseline -- linear PDF baseline -* -*****************************************************************************/ + * + * 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.txt for a list of people who contributed. + * See LICENSE_DANSE.txt for license information. + * + ****************************************************************************** + * + * class ZeroBaseline -- linear PDF baseline + * + *****************************************************************************/ #ifndef ZEROBASELINE_HPP_INCLUDED #define ZEROBASELINE_HPP_INCLUDED @@ -29,34 +29,30 @@ namespace srreal { /// @class ZeroBaseline /// @brief trivial zero baseline -class DLL_EXPORT ZeroBaseline : public PDFBaseline -{ - public: +class DLL_EXPORT ZeroBaseline : public PDFBaseline { + public: + // constructors + PDFBaselinePtr create() const; + PDFBaselinePtr clone() const; - // constructors - PDFBaselinePtr create() const; - PDFBaselinePtr clone() const; + // methods + const std::string& type() const; + double operator()(const double& r) const; - // methods - const std::string& type() const; - double operator()(const double& r) const; + private: + // serialization + friend class boost::serialization::access; - private: - - // serialization - friend class boost::serialization::access; - - template - void serialize(Archive& ar, const unsigned int version) - { - using boost::serialization::base_object; - ar & base_object(*this); - } + template + void serialize(Archive& ar, const unsigned int version) { + using boost::serialization::base_object; + ar& base_object(*this); + } }; // class ZeroBaseline -} // namespace srreal -} // namespace diffpy +} // namespace srreal +} // namespace diffpy // Serialization ------------------------------------------------------------- diff --git a/src/diffpy/srreal/forwardtypes.hpp b/src/diffpy/srreal/forwardtypes.hpp index fe047063..c6629c42 100644 --- a/src/diffpy/srreal/forwardtypes.hpp +++ b/src/diffpy/srreal/forwardtypes.hpp @@ -1,20 +1,20 @@ /***************************************************************************** -* -* libdiffpy Complex Modeling Initiative -* (c) 2013 Brookhaven Science Associates, -* Brookhaven National Laboratory. -* All rights reserved. -* -* File coded by: Pavol Juhas -* -* See AUTHORS.txt for a list of people who contributed. -* See LICENSE.txt for license information. -* -****************************************************************************** -* -* Forward declaration of commonly used pointer types. -* -*****************************************************************************/ + * + * libdiffpy Complex Modeling Initiative + * (c) 2013 Brookhaven Science Associates, + * Brookhaven National Laboratory. + * All rights reserved. + * + * File coded by: Pavol Juhas + * + * See AUTHORS.txt for a list of people who contributed. + * See LICENSE.txt for license information. + * + ****************************************************************************** + * + * Forward declaration of commonly used pointer types. + * + *****************************************************************************/ #ifndef FORWARDTYPES_HPP_INCLUDED #define FORWARDTYPES_HPP_INCLUDED @@ -28,12 +28,13 @@ namespace srreal { /// shared pointers to structure adapter and bond generator related objects typedef boost::shared_ptr BaseBondGeneratorPtr; typedef boost::shared_ptr StructureAdapterPtr; -typedef boost::shared_ptr StructureAdapterConstPtr; +typedef boost::shared_ptr + StructureAdapterConstPtr; /// type for holding indices for atom sites typedef std::vector SiteIndices; -} // namespace srreal -} // namespace diffpy +} // namespace srreal +} // namespace diffpy #endif // FORWARDTYPES_HPP_INCLUDED diff --git a/src/diffpy/srreal/scatteringfactordata.cpp b/src/diffpy/srreal/scatteringfactordata.cpp index 6439269d..0d0a20a8 100644 --- a/src/diffpy/srreal/scatteringfactordata.cpp +++ b/src/diffpy/srreal/scatteringfactordata.cpp @@ -44,309 +44,261 @@ namespace { const int WKTerms = 5; -class WaasKirfFormula -{ - public: - - // Constructor - WaasKirfFormula() - { - fill(a, a + WKTerms, 0.0); - fill(b, b + WKTerms, 0.0); - c = 0.0; - } - - WaasKirfFormula(const WaasKirfFormula& wk0) - { - symbol = wk0.symbol; - copy(wk0.a, wk0.a + WKTerms, a); - copy(wk0.b, wk0.b + WKTerms, b); - c = wk0.c; - } - - // methods - double xrayatstol(double stol) const - { - double rv = c; - for (int i = 0; i < WKTerms; ++i) - { - rv += a[i] * exp(-b[i] * stol * stol); - } - return rv; - } - - double Z() const - { - double rv = c; - for (int i = 0; i < WKTerms; ++i) rv += a[i]; - return round(rv); - } - - double electronatstol(double stol) const - { - using diffpy::mathutils::eps_eq; - using diffpy::mathutils::DOUBLE_MAX; - if (eps_eq(stol, 0.0)) return DOUBLE_MAX; - double rv = 0.023934 * - (this->Z() - this->xrayatstol(stol)) / (stol * stol); - return rv; - } - - // data - string symbol; - double a[WKTerms]; - double b[WKTerms]; - double c; +class WaasKirfFormula { + public: + // Constructor + WaasKirfFormula() { + fill(a, a + WKTerms, 0.0); + fill(b, b + WKTerms, 0.0); + c = 0.0; + } + + WaasKirfFormula(const WaasKirfFormula& wk0) { + symbol = wk0.symbol; + copy(wk0.a, wk0.a + WKTerms, a); + copy(wk0.b, wk0.b + WKTerms, b); + c = wk0.c; + } + + // methods + double xrayatstol(double stol) const { + double rv = c; + for (int i = 0; i < WKTerms; ++i) { + rv += a[i] * exp(-b[i] * stol * stol); + } + return rv; + } + + double Z() const { + double rv = c; + for (int i = 0; i < WKTerms; ++i) rv += a[i]; + return round(rv); + } + + double electronatstol(double stol) const { + using diffpy::mathutils::DOUBLE_MAX; + using diffpy::mathutils::eps_eq; + if (eps_eq(stol, 0.0)) return DOUBLE_MAX; + double rv = 0.023934 * (this->Z() - this->xrayatstol(stol)) / (stol * stol); + return rv; + } + + // data + string symbol; + double a[WKTerms]; + double b[WKTerms]; + double c; }; - -class wksmbl_equal : - public binary_function -{ - public: - bool operator() ( - const WaasKirfFormula& wk0, const WaasKirfFormula& wk1) const - { - return wk0.symbol == wk1.symbol; - } +class wksmbl_equal + : public binary_function { + public: + bool operator()(const WaasKirfFormula& wk0, + const WaasKirfFormula& wk1) const { + return wk0.symbol == wk1.symbol; + } }; - -class wksmbl_hash -{ - public: - size_t operator()(const WaasKirfFormula& wk) const - { - hash hasher; - return hasher(wk.symbol); - } +class wksmbl_hash { + public: + size_t operator()(const WaasKirfFormula& wk) const { + hash hasher; + return hasher(wk.symbol); + } }; - -typedef unordered_set SetOfWKFormulas; - - -const SetOfWKFormulas& getWKFormulasSet() -{ - using namespace diffpy::runtimepath; - using diffpy::validators::ensureFileOK; - static unique_ptr the_set; - if (the_set) return *the_set; - the_set.reset(new SetOfWKFormulas); - string wkfile = datapath("f0_WaasKirf.dat"); - ifstream fp(wkfile.c_str()); - ensureFileOK(wkfile, fp); - LineReader line; - line.commentmark = '#'; - for (WaasKirfFormula wk; fp >> line;) - { - if (line.iscomment() && (0 == line.words[0].compare(0, 2, "#S"))) - { - if (line.wcount() < 3) - { - throw line.format_error(wkfile, - "Expected at least 3 columns of data."); - } - wk.symbol = line.words[2]; - continue; - } - if (line.iscomment() && (0 == line.words[0].compare(0, 2, "#L"))) - { - if (wk.symbol.empty()) - { - throw line.format_error(wkfile, - "Missing \"#S\" line with atom symbol definition."); - } - fp >> line; - if (line.wcount() != 11) - { - throw line.format_error(wkfile, "Expected 11 values."); - } - istringstream fpline(line.line); - fpline >> - wk.a[0] >> wk.a[1] >> wk.a[2] >> wk.a[3] >> wk.a[4] >> - wk.c >> wk.b[0] >> wk.b[1] >> wk.b[2] >> wk.b[3] >> wk.b[4]; - if (!fpline) - { - throw line.format_error(wkfile, - "Line should contain 11 floating point values."); - } - if (the_set->count(wk)) - { - string emsg = "Duplicate atom symbol \""; - emsg += wk.symbol + "\"."; - throw line.format_error(wkfile, emsg); - } - the_set->insert(wk); - wk.symbol.clear(); - } - } - return getWKFormulasSet(); -} - - -SetOfWKFormulas::const_iterator findWKFormula(const string& smbl) -{ - using diffpy::srreal::atomBareSymbol; - using diffpy::srreal::atomValence; - WaasKirfFormula wk; - wk.symbol = smbl; - const SetOfWKFormulas& swk = getWKFormulasSet(); - SetOfWKFormulas::const_iterator wkit = swk.find(wk); - if (wkit != swk.end()) return wkit; - // try again with an adjusted charge suffix - wk.symbol = atomBareSymbol(smbl); - int v = atomValence(smbl); - if (v == 0) wkit = swk.find(wk); - else if (abs(v) == 1) - { - wk.symbol += ((v > 0) ? "1+" : "1-"); - wkit = swk.find(wk); +typedef unordered_set + SetOfWKFormulas; + +const SetOfWKFormulas& getWKFormulasSet() { + using namespace diffpy::runtimepath; + using diffpy::validators::ensureFileOK; + static unique_ptr the_set; + if (the_set) return *the_set; + the_set.reset(new SetOfWKFormulas); + string wkfile = datapath("f0_WaasKirf.dat"); + ifstream fp(wkfile.c_str()); + ensureFileOK(wkfile, fp); + LineReader line; + line.commentmark = '#'; + for (WaasKirfFormula wk; fp >> line;) { + if (line.iscomment() && (0 == line.words[0].compare(0, 2, "#S"))) { + if (line.wcount() < 3) { + throw line.format_error(wkfile, "Expected at least 3 columns of data."); + } + wk.symbol = line.words[2]; + continue; } - // raise exception if no match - if (wkit == swk.end()) - { - string emsg("Unknown atom or ion symbol '"); - emsg += smbl + "'."; - throw invalid_argument(emsg); + if (line.iscomment() && (0 == line.words[0].compare(0, 2, "#L"))) { + if (wk.symbol.empty()) { + throw line.format_error( + wkfile, "Missing \"#S\" line with atom symbol definition."); + } + fp >> line; + if (line.wcount() != 11) { + throw line.format_error(wkfile, "Expected 11 values."); + } + istringstream fpline(line.line); + fpline >> wk.a[0] >> wk.a[1] >> wk.a[2] >> wk.a[3] >> wk.a[4] >> wk.c >> + wk.b[0] >> wk.b[1] >> wk.b[2] >> wk.b[3] >> wk.b[4]; + if (!fpline) { + throw line.format_error( + wkfile, "Line should contain 11 floating point values."); + } + if (the_set->count(wk)) { + string emsg = "Duplicate atom symbol \""; + emsg += wk.symbol + "\"."; + throw line.format_error(wkfile, emsg); + } + the_set->insert(wk); + wk.symbol.clear(); } - return wkit; + } + return getWKFormulasSet(); } +SetOfWKFormulas::const_iterator findWKFormula(const string& smbl) { + using diffpy::srreal::atomBareSymbol; + using diffpy::srreal::atomValence; + WaasKirfFormula wk; + wk.symbol = smbl; + const SetOfWKFormulas& swk = getWKFormulasSet(); + SetOfWKFormulas::const_iterator wkit = swk.find(wk); + if (wkit != swk.end()) return wkit; + // try again with an adjusted charge suffix + wk.symbol = atomBareSymbol(smbl); + int v = atomValence(smbl); + if (v == 0) + wkit = swk.find(wk); + else if (abs(v) == 1) { + wk.symbol += ((v > 0) ? "1+" : "1-"); + wkit = swk.find(wk); + } + // raise exception if no match + if (wkit == swk.end()) { + string emsg("Unknown atom or ion symbol '"); + emsg += smbl + "'."; + throw invalid_argument(emsg); + } + return wkit; +} // Electron numbers for elements and ions -typedef unordered_map ElectronNumberStorage; - -ElectronNumberStorage& getElectronNumberTable() -{ - using namespace diffpy::runtimepath; - using diffpy::validators::ensureFileOK; - static unique_ptr entable; - typedef ElectronNumberStorage::value_type ENPair; - if (!entable) - { - entable.reset(new ElectronNumberStorage); - string ionfile = datapath("ionlist.dat"); - ifstream fp0(ionfile.c_str()); - ensureFileOK(ionfile, fp0); - LineReader line; - while (fp0 >> line) - { - if (line.isignored()) continue; - istringstream fpline(line.line); - string element; - int z = 0; - fpline >> element >> z; - if (!fpline) - { - throw line.format_error(ionfile, - "Expected at least 2 columns for (symbol, Z)."); - } - entable->insert(ENPair(element, z)); - for (int v; fpline >> v;) - { - ostringstream smbl; - smbl << element << abs(v) << ((v > 0) ? '+' : '-'); - entable->insert(ENPair(smbl.str(), z - v)); - } - } - const size_t mintablesize = 447; - if (entable->size() < mintablesize) - { - ostringstream emsg; - emsg << "Incomplete file. Expected " << mintablesize << - " items loaded " << entable->size() << "."; - throw line.format_error(ionfile, emsg.str()); - } +typedef unordered_map ElectronNumberStorage; + +ElectronNumberStorage& getElectronNumberTable() { + using namespace diffpy::runtimepath; + using diffpy::validators::ensureFileOK; + static unique_ptr entable; + typedef ElectronNumberStorage::value_type ENPair; + if (!entable) { + entable.reset(new ElectronNumberStorage); + string ionfile = datapath("ionlist.dat"); + ifstream fp0(ionfile.c_str()); + ensureFileOK(ionfile, fp0); + LineReader line; + while (fp0 >> line) { + if (line.isignored()) continue; + istringstream fpline(line.line); + string element; + int z = 0; + fpline >> element >> z; + if (!fpline) { + throw line.format_error(ionfile, + "Expected at least 2 columns for (symbol, Z)."); + } + entable->insert(ENPair(element, z)); + for (int v; fpline >> v;) { + ostringstream smbl; + smbl << element << abs(v) << ((v > 0) ? '+' : '-'); + entable->insert(ENPair(smbl.str(), z - v)); + } + } + const size_t mintablesize = 447; + if (entable->size() < mintablesize) { + ostringstream emsg; + emsg << "Incomplete file. Expected " << mintablesize << " items loaded " + << entable->size() << "."; + throw line.format_error(ionfile, emsg.str()); } - return *entable; + } + return *entable; } // Neutron scattering lengths -typedef unordered_map NeutronBCStorage; - -const NeutronBCStorage& getNeutronBCTable() -{ - using namespace diffpy::runtimepath; - using diffpy::validators::ensureFileOK; - typedef NeutronBCStorage::value_type BCPair; - static unique_ptr bctable; - if (bctable) return *bctable; - bctable.reset(new NeutronBCStorage); - string nsffile = datapath("nsftable.dat"); - ifstream fp(nsffile.c_str()); - ensureFileOK(nsffile, fp); - LineReader line; - line.commentmark = '#'; - line.separator = ','; - while (fp >> line) - { - if (line.isignored()) continue; - if (line.wcount() != 11) - { - throw line.format_error(nsffile, - "Expected 11 comma-separated items."); - } - if (line.words[3].empty()) continue; - string smbl = line.words[0]; - size_t p0 = smbl.find_first_not_of("0123456789-"); - if (p0 == string::npos) - { - throw line.format_error(nsffile, "Missing or invalid atom symbol."); - } - smbl.erase(0, p0); - size_t p1 = smbl.find_last_of('-'); - if (p1 != string::npos) - { - smbl = smbl.substr(p1 + 1) + "-" + smbl.substr(0, p1); - } - istringstream fpbc(line.words[3]); - double bc; - fpbc >> bc; - if (!fpbc) - { - throw line.format_error(nsffile, "Invalid b_c value."); - } - if (bctable->count(smbl)) - { - string emsg = "Duplicate atom symbol \""; - emsg += smbl + "\"."; - throw line.format_error(nsffile, emsg); - } - bctable->insert(BCPair(smbl, bc)); - // elements are not explicitly included if there is just one isotope - // or if all isotopes are unstable - size_t p2 = smbl.find_first_of('-'); - if (p2 != string::npos) - { - string el = smbl.substr(p2 + 1); - // there is just one isotope - const string& chlf = line.words[1]; - bool addel = (chlf == "100") || - (!bctable->count(el) && *chlf.rbegin() == 'Y'); - if (addel) - { - if (bctable->count(el)) - { - string emsg = "Duplicate element entry for \""; - emsg += el + "\"."; - throw line.format_error(nsffile, emsg);; - } - bctable->insert(BCPair(el, bc)); - } +typedef unordered_map NeutronBCStorage; + +const NeutronBCStorage& getNeutronBCTable() { + using namespace diffpy::runtimepath; + using diffpy::validators::ensureFileOK; + typedef NeutronBCStorage::value_type BCPair; + static unique_ptr bctable; + if (bctable) return *bctable; + bctable.reset(new NeutronBCStorage); + string nsffile = datapath("nsftable.dat"); + ifstream fp(nsffile.c_str()); + ensureFileOK(nsffile, fp); + LineReader line; + line.commentmark = '#'; + line.separator = ','; + while (fp >> line) { + if (line.isignored()) continue; + if (line.wcount() != 11) { + throw line.format_error(nsffile, "Expected 11 comma-separated items."); + } + if (line.words[3].empty()) continue; + string smbl = line.words[0]; + size_t p0 = smbl.find_first_not_of("0123456789-"); + if (p0 == string::npos) { + throw line.format_error(nsffile, "Missing or invalid atom symbol."); + } + smbl.erase(0, p0); + size_t p1 = smbl.find_last_of('-'); + if (p1 != string::npos) { + smbl = smbl.substr(p1 + 1) + "-" + smbl.substr(0, p1); + } + istringstream fpbc(line.words[3]); + double bc; + fpbc >> bc; + if (!fpbc) { + throw line.format_error(nsffile, "Invalid b_c value."); + } + if (bctable->count(smbl)) { + string emsg = "Duplicate atom symbol \""; + emsg += smbl + "\"."; + throw line.format_error(nsffile, emsg); + } + bctable->insert(BCPair(smbl, bc)); + // elements are not explicitly included if there is just one isotope + // or if all isotopes are unstable + size_t p2 = smbl.find_first_of('-'); + if (p2 != string::npos) { + string el = smbl.substr(p2 + 1); + // there is just one isotope + const string& chlf = line.words[1]; + bool addel = + (chlf == "100") || (!bctable->count(el) && *chlf.rbegin() == 'Y'); + if (addel) { + if (bctable->count(el)) { + string emsg = "Duplicate element entry for \""; + emsg += el + "\"."; + throw line.format_error(nsffile, emsg); + ; } + bctable->insert(BCPair(el, bc)); + } } - // define aliases for neutron, deuterium and tritium - bctable->insert(BCPair("n", bctable->at("1-n"))); - bctable->insert(BCPair("D", bctable->at("2-H"))); - bctable->insert(BCPair("T", bctable->at("3-H"))); - return getNeutronBCTable(); + } + // define aliases for neutron, deuterium and tritium + bctable->insert(BCPair("n", bctable->at("1-n"))); + bctable->insert(BCPair("D", bctable->at("2-H"))); + bctable->insert(BCPair("T", bctable->at("3-H"))); + return getNeutronBCTable(); } -} // namespace +} // namespace // Implementation ------------------------------------------------------------ @@ -354,74 +306,61 @@ namespace diffpy { namespace srreal { /// X-ray scattering factor of an element or ion a given Q -double fxrayatq(const string& smbl, double q) -{ - const double stol = q / (4 * M_PI); - return fxrayatstol(smbl, stol); +double fxrayatq(const string& smbl, double q) { + const double stol = q / (4 * M_PI); + return fxrayatstol(smbl, stol); } - /// X-ray scattering factor of an element or ion a given sin(theta)/lambda -double fxrayatstol(const string& smbl, double stol) -{ - SetOfWKFormulas::const_iterator wkit = findWKFormula(smbl); - return wkit->xrayatstol(stol); +double fxrayatstol(const string& smbl, double stol) { + SetOfWKFormulas::const_iterator wkit = findWKFormula(smbl); + return wkit->xrayatstol(stol); } - /// Electron scattering factor of an element or ion a given Q -double felectronatq(const string& smbl, double q) -{ - const double stol = q / (4 * M_PI); - SetOfWKFormulas::const_iterator wkit = findWKFormula(smbl); - return wkit->electronatstol(stol); +double felectronatq(const string& smbl, double q) { + const double stol = q / (4 * M_PI); + SetOfWKFormulas::const_iterator wkit = findWKFormula(smbl); + return wkit->electronatstol(stol); } - /// Number of electrons for an element or ion -int electronnumber(const string& smbl) -{ - const ElectronNumberStorage& entable = getElectronNumberTable(); - ElectronNumberStorage::const_iterator it; - it = entable.find(smbl); - // try to build standard symbol when not found - if (it == entable.end()) - { - ostringstream smbl1; - smbl1 << atomBareSymbol(smbl); - int v = atomValence(smbl); - if (v) smbl1 << abs(v) << ((v > 0) ? '+' : '-'); - it = entable.find(smbl1.str()); - } - // throw exception if still not found - if (it == entable.end()) - { - ostringstream emsg; - emsg << "Unknown atom symbol '" << smbl << "'."; - throw invalid_argument(emsg.str()); - } - return it->second; +int electronnumber(const string& smbl) { + const ElectronNumberStorage& entable = getElectronNumberTable(); + ElectronNumberStorage::const_iterator it; + it = entable.find(smbl); + // try to build standard symbol when not found + if (it == entable.end()) { + ostringstream smbl1; + smbl1 << atomBareSymbol(smbl); + int v = atomValence(smbl); + if (v) smbl1 << abs(v) << ((v > 0) ? '+' : '-'); + it = entable.find(smbl1.str()); + } + // throw exception if still not found + if (it == entable.end()) { + ostringstream emsg; + emsg << "Unknown atom symbol '" << smbl << "'."; + throw invalid_argument(emsg.str()); + } + return it->second; } - /// Coherent scattering length of an element or isotope in fm -double bcneutron(const string& smbl) -{ - const NeutronBCStorage& bctable = getNeutronBCTable(); - NeutronBCStorage::const_iterator it = bctable.find(smbl); - if (it != bctable.end()) return it->second; - size_t pe = smbl.find_last_not_of("+-012345678 \t"); - string smblnocharge = - (pe != string::npos) ? smbl.substr(0, pe + 1) : smbl; - it = bctable.find(smblnocharge); - if (it == bctable.end()) - { - string emsg("Unknown atom or isotope symbol '"); - emsg += smbl + "'."; - throw invalid_argument(emsg); - } - return it->second; +double bcneutron(const string& smbl) { + const NeutronBCStorage& bctable = getNeutronBCTable(); + NeutronBCStorage::const_iterator it = bctable.find(smbl); + if (it != bctable.end()) return it->second; + size_t pe = smbl.find_last_not_of("+-012345678 \t"); + string smblnocharge = (pe != string::npos) ? smbl.substr(0, pe + 1) : smbl; + it = bctable.find(smblnocharge); + if (it == bctable.end()) { + string emsg("Unknown atom or isotope symbol '"); + emsg += smbl + "'."; + throw invalid_argument(emsg); + } + return it->second; } -} // namespace srreal -} // namespace diffpy +} // namespace srreal +} // namespace diffpy diff --git a/src/diffpy/srreal/scatteringfactordata.hpp b/src/diffpy/srreal/scatteringfactordata.hpp index 1844002f..69270ab2 100644 --- a/src/diffpy/srreal/scatteringfactordata.hpp +++ b/src/diffpy/srreal/scatteringfactordata.hpp @@ -45,7 +45,7 @@ int electronnumber(const std::string& smbl); /// Coherent scattering length of an element or isotope in fm double bcneutron(const std::string& smbl); -} // namespace srreal -} // namespace diffpy +} // namespace srreal +} // namespace diffpy #endif // SCATTERINGFACTORS_HPP_INCLUDED diff --git a/src/diffpy/validators.hpp b/src/diffpy/validators.hpp index 6c6907d0..200040e8 100644 --- a/src/diffpy/validators.hpp +++ b/src/diffpy/validators.hpp @@ -1,20 +1,20 @@ /***************************************************************************** -* -* 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.txt for a list of people who contributed. -* See LICENSE_DANSE.txt for license information. -* -****************************************************************************** -* -* Convenience functions for argument checking. -* -*****************************************************************************/ + * + * 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.txt for a list of people who contributed. + * See LICENSE_DANSE.txt for license information. + * + ****************************************************************************** + * + * Convenience functions for argument checking. + * + *****************************************************************************/ #ifndef VALIDATORS_HPP_INCLUDED #define VALIDATORS_HPP_INCLUDED @@ -30,58 +30,50 @@ namespace validators { /// Throw invalid_argument for a negative value template -void ensureNonNegative(const std::string& vname, const T& value) -{ - if (value < 0) - { - std::string emsg(vname); - emsg += " cannot be negative."; - throw std::invalid_argument(emsg); - } +void ensureNonNegative(const std::string& vname, const T& value) { + if (value < 0) { + std::string emsg(vname); + emsg += " cannot be negative."; + throw std::invalid_argument(emsg); + } } /// Throw invalid_argument for a value that is not greater than DOUBLE_EPS template -void ensureEpsilonPositive(const std::string& vname, const T& value) -{ - using diffpy::mathutils::eps_gt; - if (!eps_gt(double(value), 0.0)) - { - std::string emsg(vname); - emsg += " must be epsilon positive."; - throw std::invalid_argument(emsg); - } +void ensureEpsilonPositive(const std::string& vname, const T& value) { + using diffpy::mathutils::eps_gt; + if (!eps_gt(double(value), 0.0)) { + std::string emsg(vname); + emsg += " must be epsilon positive."; + throw std::invalid_argument(emsg); + } } /// Throw invalid_argument if the argument is not true. For boost smart /// pointers this is equivalent to a truth check of p.get(). template -void ensureNonNull(const std::string& vname, const T& p) -{ - if (!p) - { - std::string emsg(vname); - emsg += " cannot be NULL."; - throw std::invalid_argument(emsg); - } +void ensureNonNull(const std::string& vname, const T& p) { + if (!p) { + std::string emsg(vname); + emsg += " cannot be NULL."; + throw std::invalid_argument(emsg); + } } /// Throw runtime_error if a file argument is not true. template -void ensureFileOK(const std::string& fname, const T& fp) -{ - if (!fp) - { - std::string emsg = "Cannot open '"; - emsg += fname + "'."; - throw std::runtime_error(emsg); - } +void ensureFileOK(const std::string& fname, const T& fp) { + if (!fp) { + std::string emsg = "Cannot open '"; + emsg += fname + "'."; + throw std::runtime_error(emsg); + } } -} // namespace validators -} // namespace diffpy +} // namespace validators +} // namespace diffpy #endif // VALIDATORS_HPP_INCLUDED diff --git a/src/diffpy/version.cpp b/src/diffpy/version.cpp index 803f0a6b..685f298d 100644 --- a/src/diffpy/version.cpp +++ b/src/diffpy/version.cpp @@ -1,20 +1,20 @@ /***************************************************************************** -* -* libdiffpy Complex Modeling Initiative -* (c) 2014 Brookhaven Science Associates, -* Brookhaven National Laboratory. -* All rights reserved. -* -* File coded by: Pavol Juhas -* -* See AUTHORS.txt for a list of people who contributed. -* See LICENSE.txt for license information. -* -****************************************************************************** -* -* Definitions for the libdiffpy_version_info constants. -* -*****************************************************************************/ + * + * libdiffpy Complex Modeling Initiative + * (c) 2014 Brookhaven Science Associates, + * Brookhaven National Laboratory. + * All rights reserved. + * + * File coded by: Pavol Juhas + * + * See AUTHORS.txt for a list of people who contributed. + * See LICENSE.txt for license information. + * + ****************************************************************************** + * + * Definitions for the libdiffpy_version_info constants. + * + *****************************************************************************/ #include diff --git a/src/tests/TestAtomRadiiTable.hpp b/src/tests/TestAtomRadiiTable.hpp index 392f8fd2..5033f662 100644 --- a/src/tests/TestAtomRadiiTable.hpp +++ b/src/tests/TestAtomRadiiTable.hpp @@ -1,20 +1,20 @@ /***************************************************************************** -* -* 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.txt for a list of people who contributed. -* See LICENSE_DANSE.txt for license information. -* -****************************************************************************** -* -* class TestAtomRadiiTable -- unit tests for the AtomRadiiTable class -* -*****************************************************************************/ + * + * 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.txt for a list of people who contributed. + * See LICENSE_DANSE.txt for license information. + * + ****************************************************************************** + * + * class TestAtomRadiiTable -- unit tests for the AtomRadiiTable class + * + *****************************************************************************/ #include #include @@ -26,112 +26,82 @@ 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()); - } - +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.hpp b/src/tests/TestAtomicStructureAdapter.hpp index c530d6b6..a07c1aa4 100644 --- a/src/tests/TestAtomicStructureAdapter.hpp +++ b/src/tests/TestAtomicStructureAdapter.hpp @@ -1,21 +1,21 @@ /***************************************************************************** -* -* libdiffpy Complex Modeling Initiative -* (c) 2013 Brookhaven Science Associates, -* Brookhaven National Laboratory. -* All rights reserved. -* -* File coded by: Pavol Juhas -* -* See AUTHORS.txt for a list of people who contributed. -* See LICENSE.txt for license information. -* -****************************************************************************** -* -* class TestAtomicStructureAdapter -- unit tests for an adapter that -* stores data in a series of Atom objects -* -*****************************************************************************/ + * + * libdiffpy Complex Modeling Initiative + * (c) 2013 Brookhaven Science Associates, + * Brookhaven National Laboratory. + * All rights reserved. + * + * File coded by: Pavol Juhas + * + * See AUTHORS.txt for a list of people who contributed. + * See LICENSE.txt for license information. + * + ****************************************************************************** + * + * class TestAtomicStructureAdapter -- unit tests for an adapter that + * stores data in a series of Atom objects + * + *****************************************************************************/ #include @@ -34,124 +34,110 @@ using namespace std; // class TestAtomicStructureAdapter ////////////////////////////////////////////////////////////////////////////// -class TestAtomicStructureAdapter : public CxxTest::TestSuite -{ - private: - - StructureAdapterPtr mstru; - AtomicStructureAdapterPtr mpstru; - - public: - - void setUp() - { - mstru = StructureAdapterPtr(new AtomicStructureAdapter); - mpstru = - boost::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 = - boost::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 = - boost::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 = - boost::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 : public CxxTest::TestSuite { + private: + StructureAdapterPtr mstru; + AtomicStructureAdapterPtr mpstru; + + public: + void setUp() { + mstru = StructureAdapterPtr(new AtomicStructureAdapter); + mpstru = boost::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 = + boost::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 = + boost::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 = + boost::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 +} // namespace srreal +} // namespace diffpy using diffpy::srreal::TestAtomicStructureAdapter; diff --git a/src/tests/TestAttributes.hpp b/src/tests/TestAttributes.hpp index 992dc4c4..29fe2272 100644 --- a/src/tests/TestAttributes.hpp +++ b/src/tests/TestAttributes.hpp @@ -1,20 +1,20 @@ /***************************************************************************** -* -* libdiffpy Complex Modeling Initiative -* (c) 2019 Brookhaven Science Associates, -* Brookhaven National Laboratory. -* All rights reserved. -* -* File coded by: Pavol Juhas -* -* See AUTHORS.txt for a list of people who contributed. -* See LICENSE.txt for license information. -* -****************************************************************************** -* -* class TestAttributes -- test the helper Attributes class -* -*****************************************************************************/ + * + * libdiffpy Complex Modeling Initiative + * (c) 2019 Brookhaven Science Associates, + * Brookhaven National Laboratory. + * All rights reserved. + * + * File coded by: Pavol Juhas + * + * See AUTHORS.txt for a list of people who contributed. + * See LICENSE.txt for license information. + * + ****************************************************************************** + * + * class TestAttributes -- test the helper Attributes class + * + *****************************************************************************/ #include @@ -24,65 +24,48 @@ namespace { -class Example : public diffpy::Attributes -{ - public: +class Example : public diffpy::Attributes { + public: + Example() : ma(0.0) { + this->registerDoubleAttribute("a", this, &Example::geta, &Example::seta); + this->registerDoubleAttribute("b", this, &Example::geta); + } - Example() : ma(0.0) - { - this->registerDoubleAttribute("a", this, - &Example::geta, &Example::seta); - this->registerDoubleAttribute("b", this, - &Example::geta); - } - - double geta() const { return ma; } - void seta(double a) { ma = a; } - - private: - - double ma; + double geta() const { return ma; } + void seta(double a) { ma = a; } + private: + double ma; }; -} // namespace +} // namespace // --------------------------------------------------------------------------- -class TestAttributes : public CxxTest::TestSuite -{ - private: - - // 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()); - } - - - 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); - } +class TestAttributes : public CxxTest::TestSuite { + private: + // 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()); + } + + 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); + } }; // class TestAttributes diff --git a/src/tests/TestBVParametersTable.hpp b/src/tests/TestBVParametersTable.hpp index 41a985e8..f0d832e4 100644 --- a/src/tests/TestBVParametersTable.hpp +++ b/src/tests/TestBVParametersTable.hpp @@ -1,20 +1,20 @@ /***************************************************************************** -* -* 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.txt for a list of people who contributed. -* See LICENSE_DANSE.txt for license information. -* -****************************************************************************** -* -* class TestBVParametersTable -- unit tests for BVParametersTable class -* -*****************************************************************************/ + * + * 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.txt for a list of people who contributed. + * See LICENSE_DANSE.txt for license information. + * + ****************************************************************************** + * + * class TestBVParametersTable -- unit tests for BVParametersTable class + * + *****************************************************************************/ #include @@ -24,206 +24,173 @@ 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 : 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 diff --git a/src/tests/TestBVSCalculator.hpp b/src/tests/TestBVSCalculator.hpp index f8001d51..2180856f 100644 --- a/src/tests/TestBVSCalculator.hpp +++ b/src/tests/TestBVSCalculator.hpp @@ -1,20 +1,20 @@ /***************************************************************************** -* -* 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.txt for a list of people who contributed. -* See LICENSE_DANSE.txt for license information. -* -****************************************************************************** -* -* class TestBVSCalculator -- unit tests suite -* -*****************************************************************************/ + * + * 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.txt for a list of people who contributed. + * See LICENSE_DANSE.txt for license information. + * + ****************************************************************************** + * + * class TestBVSCalculator -- unit tests suite + * + *****************************************************************************/ #include @@ -24,144 +24,126 @@ #include "test_helpers.hpp" using namespace std; -using diffpy::srreal::BVSCalculator; using diffpy::srreal::BVParametersTablePtr; -using diffpy::srreal::StructureAdapterPtr; +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; - boost::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 = - boost::dynamic_pointer_cast< - PeriodicStructureAdapter>(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); - boost::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 : public CxxTest::TestSuite { + private: + StructureAdapterPtr mnacl; + boost::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 = + boost::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); + boost::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 diff --git a/src/tests/TestBVSObjCryst.hpp b/src/tests/TestBVSObjCryst.hpp index 84830445..4be8f53c 100644 --- a/src/tests/TestBVSObjCryst.hpp +++ b/src/tests/TestBVSObjCryst.hpp @@ -1,21 +1,21 @@ /***************************************************************************** -* -* 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.txt for a list of people who contributed. -* See LICENSE_DANSE.txt for license information. -* -****************************************************************************** -* -* class TestBVSObjCryst -- unit tests for BVS calculation for an ObjCryst -* crystal structure -* -*****************************************************************************/ + * + * 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.txt for a list of people who contributed. + * See LICENSE_DANSE.txt for license information. + * + ****************************************************************************** + * + * class TestBVSObjCryst -- unit tests for BVS calculation for an ObjCryst + * crystal structure + * + *****************************************************************************/ #include @@ -31,44 +31,35 @@ using namespace diffpy::srreal; // class Test ////////////////////////////////////////////////////////////////////////////// -class TestBVSObjCryst : public CxxTest::TestSuite -{ - private: +class TestBVSObjCryst : public CxxTest::TestSuite { + private: + unique_ptr mnacl; + unique_ptr mbvc; - unique_ptr mnacl; - unique_ptr mbvc; + public: + void setUp() { + if (!mnacl.get()) { + mnacl.reset(loadTestCrystal("NaCl.cif")); + } + mbvc.reset(new BVSCalculator); + } - public: + 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 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); - } + 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 diff --git a/src/tests/TestDebyePDFCalculator.hpp b/src/tests/TestDebyePDFCalculator.hpp index 5bd380d3..d48225d8 100644 --- a/src/tests/TestDebyePDFCalculator.hpp +++ b/src/tests/TestDebyePDFCalculator.hpp @@ -1,20 +1,20 @@ /***************************************************************************** -* -* 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.txt for a list of people who contributed. -* See LICENSE_DANSE.txt for license information. -* -****************************************************************************** -* -* class TestDebyePDFCalculator -- unit tests for DebyePDFCalculator class -* -*****************************************************************************/ + * + * 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.txt for a list of people who contributed. + * See LICENSE_DANSE.txt for license information. + * + ****************************************************************************** + * + * class TestDebyePDFCalculator -- unit tests for DebyePDFCalculator class + * + *****************************************************************************/ #include @@ -30,334 +30,283 @@ using namespace std; using namespace diffpy::srreal; -class TestDebyePDFCalculator : public CxxTest::TestSuite -{ - private: - - boost::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 = boost::make_shared(); - mstru10 = boost::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 = boost::make_shared(*mstru10); - (*mstru10d1)[0].atomtype = "Au"; - mstru10r = boost::make_shared(); - mstru10r->assign(mstru10->rbegin(), mstru10->rend()); - mstru9 = boost::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); - boost::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 : public CxxTest::TestSuite { + private: + boost::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 = boost::make_shared(); + mstru10 = boost::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 = boost::make_shared(*mstru10); + (*mstru10d1)[0].atomtype = "Au"; + mstru10r = boost::make_shared(); + mstru10r->assign(mstru10->rbegin(), mstru10->rend()); + mstru9 = boost::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); + boost::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 diff --git a/src/tests/TestEmptyStructureAdapter.hpp b/src/tests/TestEmptyStructureAdapter.hpp index d002a229..7ce5ba37 100644 --- a/src/tests/TestEmptyStructureAdapter.hpp +++ b/src/tests/TestEmptyStructureAdapter.hpp @@ -1,20 +1,20 @@ /***************************************************************************** -* -* libdiffpy Complex Modeling Initiative -* (c) 2016 Brookhaven Science Associates, -* Brookhaven National Laboratory. -* All rights reserved. -* -* File coded by: Pavol Juhas -* -* See AUTHORS.txt for a list of people who contributed. -* See LICENSE.txt for license information. -* -****************************************************************************** -* -* class TestEmptyStructureAdapter -- test adapter that is always empty -* -*****************************************************************************/ + * + * libdiffpy Complex Modeling Initiative + * (c) 2016 Brookhaven Science Associates, + * Brookhaven National Laboratory. + * All rights reserved. + * + * File coded by: Pavol Juhas + * + * See AUTHORS.txt for a list of people who contributed. + * See LICENSE.txt for license information. + * + ****************************************************************************** + * + * class TestEmptyStructureAdapter -- test adapter that is always empty + * + *****************************************************************************/ #include @@ -30,52 +30,40 @@ 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 = - boost::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 : 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 = + boost::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 +} // namespace srreal +} // namespace diffpy using diffpy::srreal::TestEmptyStructureAdapter; diff --git a/src/tests/TestHasClassRegistry.hpp b/src/tests/TestHasClassRegistry.hpp index 8f325a0e..346c9e16 100644 --- a/src/tests/TestHasClassRegistry.hpp +++ b/src/tests/TestHasClassRegistry.hpp @@ -1,20 +1,20 @@ /***************************************************************************** -* -* libdiffpy Complex Modeling Initiative -* (c) 2016 Brookhaven Science Associates, -* Brookhaven National Laboratory. -* All rights reserved. -* -* File coded by: Pavol Juhas -* -* See AUTHORS.txt for a list of people who contributed. -* See LICENSE.txt for license information. -* -****************************************************************************** -* -* class TestHasClassRegistry -- test registration machinery for named classes. -* -*****************************************************************************/ + * + * libdiffpy Complex Modeling Initiative + * (c) 2016 Brookhaven Science Associates, + * Brookhaven National Laboratory. + * All rights reserved. + * + * File coded by: Pavol Juhas + * + * See AUTHORS.txt for a list of people who contributed. + * See LICENSE.txt for license information. + * + ****************************************************************************** + * + * class TestHasClassRegistry -- test registration machinery for named classes. + * + *****************************************************************************/ #include @@ -27,60 +27,47 @@ 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 : 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 diff --git a/src/tests/TestLattice.hpp b/src/tests/TestLattice.hpp index 5752aa2f..48d6fa5c 100644 --- a/src/tests/TestLattice.hpp +++ b/src/tests/TestLattice.hpp @@ -1,20 +1,20 @@ /***************************************************************************** -* -* 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.txt for a list of people who contributed. -* See LICENSE_DANSE.txt for license information. -* -****************************************************************************** -* -* class TestLattice -- unit tests for the Lattice class -* -*****************************************************************************/ + * + * 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.txt for a list of people who contributed. + * See LICENSE_DANSE.txt for license information. + * + ****************************************************************************** + * + * class TestLattice -- unit tests for the Lattice class + * + *****************************************************************************/ #include @@ -25,151 +25,124 @@ 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 : 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 diff --git a/src/tests/TestNoMetaStructureAdapter.hpp b/src/tests/TestNoMetaStructureAdapter.hpp index 09194799..4fa84399 100644 --- a/src/tests/TestNoMetaStructureAdapter.hpp +++ b/src/tests/TestNoMetaStructureAdapter.hpp @@ -1,21 +1,21 @@ /***************************************************************************** -* -* 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.txt for a list of people who contributed. -* See LICENSE_DANSE.txt for license information. -* -****************************************************************************** -* -* class TestNoMetaStructureAdapter -- unit tests for an adapter that -* avoids metadata forwarding from the diffpy.Structure classes. -* -*****************************************************************************/ + * + * 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.txt for a list of people who contributed. + * See LICENSE_DANSE.txt for license information. + * + ****************************************************************************** + * + * class TestNoMetaStructureAdapter -- unit tests for an adapter that + * avoids metadata forwarding from the diffpy.Structure classes. + * + *****************************************************************************/ #include #include @@ -35,78 +35,65 @@ using namespace diffpy::srreal; // class TestNoMetaStructureAdapter ////////////////////////////////////////////////////////////////////////////// -class TestNoMetaStructureAdapter : public CxxTest::TestSuite -{ - private: - - StructureAdapterPtr m_pswt; - - public: - - void setUp() - { - CxxTest::setAbortTestOnFail(true); - if (!m_pswt) - { - StructureAdapterPtr pswt; - pswt = loadTestPeriodicStructure("PbScW25TiO3.stru"); - PeriodicAdapterWithPQConfigPtr pswt_pqcfg; - pswt_pqcfg = addCustomPQConfig(pswt); - pswt_pqcfg->cfg["scale"] = 2.5; - m_pswt = pswt_pqcfg; - } - CxxTest::setAbortTestOnFail(false); - } - - - void test_PDFCalcMeta() - { - PDFCalculator pdfc; - pdfc.setRstep(0.1); - pdfc.setRmax(5.0); - TS_ASSERT_EQUALS(1.0, 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(adpt0, adpt1); - } - - - void test_serialization() - { - using namespace diffpy::serialization; - stringstream storage(ios::in | ios::out | ios::binary); - oarchive oa(storage, ios::binary); - StructureAdapterPtr pswtbare0(nometa(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 : public CxxTest::TestSuite { + private: + StructureAdapterPtr m_pswt; + + public: + void setUp() { + CxxTest::setAbortTestOnFail(true); + if (!m_pswt) { + StructureAdapterPtr pswt; + pswt = loadTestPeriodicStructure("PbScW25TiO3.stru"); + PeriodicAdapterWithPQConfigPtr pswt_pqcfg; + pswt_pqcfg = addCustomPQConfig(pswt); + pswt_pqcfg->cfg["scale"] = 2.5; + m_pswt = pswt_pqcfg; + } + CxxTest::setAbortTestOnFail(false); + } + + void test_PDFCalcMeta() { + PDFCalculator pdfc; + pdfc.setRstep(0.1); + pdfc.setRmax(5.0); + TS_ASSERT_EQUALS(1.0, 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(adpt0, adpt1); + } + + void test_serialization() { + using namespace diffpy::serialization; + stringstream storage(ios::in | ios::out | ios::binary); + oarchive oa(storage, ios::binary); + StructureAdapterPtr pswtbare0(nometa(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 diff --git a/src/tests/TestObjCrystStructureAdapter.hpp b/src/tests/TestObjCrystStructureAdapter.hpp index b3320d8c..c1153572 100644 --- a/src/tests/TestObjCrystStructureAdapter.hpp +++ b/src/tests/TestObjCrystStructureAdapter.hpp @@ -1,23 +1,23 @@ /***************************************************************************** -* -* 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.txt for a list of people who contributed. -* See LICENSE_DANSE.txt for license information. -* -****************************************************************************** -* -* class TestObjCrystStructureAdapter -- unit tests for an adapter -* to the ObjCryst::Crystal class -* -* class TestObjCrystStructureBondGenerator -- unit tests for bond generator -* -*****************************************************************************/ + * + * 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.txt for a list of people who contributed. + * See LICENSE_DANSE.txt for license information. + * + ****************************************************************************** + * + * class TestObjCrystStructureAdapter -- unit tests for an adapter + * to the ObjCryst::Crystal class + * + * class TestObjCrystStructureBondGenerator -- unit tests for bond generator + * + *****************************************************************************/ #include #include @@ -41,268 +41,227 @@ using ObjCryst::ScatteringPowerAtom; namespace { -int countBonds(diffpy::srreal::BaseBondGenerator& bnds) -{ - int rv = 0; - for (bnds.rewind(); !bnds.finished(); bnds.next()) ++rv; - return rv; +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; +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 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; +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 +} // 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( - boost::dynamic_pointer_cast(acto)); - StructureAdapterPtr acto2 = acto->clone(); - CrystalStructureAdapterPtr ccto2( - boost::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 = - boost::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 : 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( + boost::dynamic_pointer_cast(acto)); + StructureAdapterPtr acto2 = acto->clone(); + CrystalStructureAdapterPtr ccto2( + boost::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 = + boost::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 @@ -310,405 +269,337 @@ class TestObjCrystStructureAdapter : public CxxTest::TestSuite // 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 neigbhors 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 tripple axis and - // have the same msd towards the central Li - else - { - TS_ASSERT_DELTA(0.00486942, testmsd1(stru, bnds), epsu); - } - } - } - - +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 : 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 : 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; +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 - +} // namespace // End of file diff --git a/src/tests/TestOverlapCalculator.hpp b/src/tests/TestOverlapCalculator.hpp index 6f1dce84..ea9b01a5 100644 --- a/src/tests/TestOverlapCalculator.hpp +++ b/src/tests/TestOverlapCalculator.hpp @@ -1,20 +1,20 @@ /***************************************************************************** -* -* 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.txt for a list of people who contributed. -* See LICENSE_DANSE.txt for license information. -* -****************************************************************************** -* -* class TestOverlapCalculator -- unit tests for the OverlapCalculator class -* -*****************************************************************************/ + * + * 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.txt for a list of people who contributed. + * See LICENSE_DANSE.txt for license information. + * + ****************************************************************************** + * + * class TestOverlapCalculator -- unit tests for the OverlapCalculator class + * + *****************************************************************************/ #include @@ -31,332 +31,293 @@ using namespace std; using namespace diffpy::srreal; -class TestOverlapCalculator : public CxxTest::TestSuite -{ - private: - - boost::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() - { - using namespace boost; - 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() - { - using namespace boost; - 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); - boost::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 : public CxxTest::TestSuite { + private: + boost::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() { + using namespace boost; + 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() { + using namespace boost; + 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); + boost::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 diff --git a/src/tests/TestPDFBaseline.hpp b/src/tests/TestPDFBaseline.hpp index 34494f9e..2be1d58a 100644 --- a/src/tests/TestPDFBaseline.hpp +++ b/src/tests/TestPDFBaseline.hpp @@ -1,20 +1,20 @@ /***************************************************************************** -* -* libdiffpy Complex Modeling Initiative -* (c) 2019 Brookhaven Science Associates, -* Brookhaven National Laboratory. -* All rights reserved. -* -* File coded by: Pavol Juhas -* -* See AUTHORS.txt for a list of people who contributed. -* See LICENSE.txt for license information. -* -****************************************************************************** -* -* class TestPDFBaseline -- test PDFBaseline and derived classes -* -*****************************************************************************/ + * + * libdiffpy Complex Modeling Initiative + * (c) 2019 Brookhaven Science Associates, + * Brookhaven National Laboratory. + * All rights reserved. + * + * File coded by: Pavol Juhas + * + * See AUTHORS.txt for a list of people who contributed. + * See LICENSE.txt for license information. + * + ****************************************************************************** + * + * class TestPDFBaseline -- test PDFBaseline and derived classes + * + *****************************************************************************/ #include @@ -30,70 +30,58 @@ 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 : 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 +} // namespace srreal +} // namespace diffpy using diffpy::srreal::TestPDFBaseline; diff --git a/src/tests/TestPDFCalculator.hpp b/src/tests/TestPDFCalculator.hpp index ff70c7ad..f02be8e3 100644 --- a/src/tests/TestPDFCalculator.hpp +++ b/src/tests/TestPDFCalculator.hpp @@ -1,20 +1,20 @@ /***************************************************************************** -* -* 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.txt for a list of people who contributed. -* See LICENSE_DANSE.txt for license information. -* -****************************************************************************** -* -* class TestPDFCalculator -- unit tests for PDFCalculator class -* -*****************************************************************************/ + * + * 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.txt for a list of people who contributed. + * See LICENSE_DANSE.txt for license information. + * + ****************************************************************************** + * + * class TestPDFCalculator -- unit tests for PDFCalculator class + * + *****************************************************************************/ #include @@ -29,254 +29,210 @@ using namespace std; using namespace diffpy::srreal; -class TestPDFCalculator : public CxxTest::TestSuite -{ - private: - - boost::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); - boost::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 : public CxxTest::TestSuite { + private: + boost::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); + boost::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 diff --git a/src/tests/TestPQEvaluator.hpp b/src/tests/TestPQEvaluator.hpp index 0e4141c2..99ddc262 100644 --- a/src/tests/TestPQEvaluator.hpp +++ b/src/tests/TestPQEvaluator.hpp @@ -1,20 +1,20 @@ /***************************************************************************** -* -* libdiffpy Complex Modeling Initiative -* (c) 2013 Brookhaven Science Associates, -* Brookhaven National Laboratory. -* All rights reserved. -* -* File coded by: Pavol Juhas -* -* See AUTHORS.txt for a list of people who contributed. -* See LICENSE.txt for license information. -* -****************************************************************************** -* -* class TestPQEvaluator -- unit tests for the PairQuantity evaluator class -* -*****************************************************************************/ + * + * libdiffpy Complex Modeling Initiative + * (c) 2013 Brookhaven Science Associates, + * Brookhaven National Laboratory. + * All rights reserved. + * + * File coded by: Pavol Juhas + * + * See AUTHORS.txt for a list of people who contributed. + * See LICENSE.txt for license information. + * + ****************************************************************************** + * + * class TestPQEvaluator -- unit tests for the PairQuantity evaluator class + * + *****************************************************************************/ #include @@ -38,260 +38,226 @@ 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 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 = boost::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 = boost::make_shared(*mstru10); - (*mstru10d1)[0].atomtype = "Au"; - mstru10r = boost::make_shared(); - mstru10r->assign(mstru10->rbegin(), mstru10->rend()); - mstru9 = boost::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 = - boost::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 : 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 = boost::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 = boost::make_shared(*mstru10); + (*mstru10d1)[0].atomtype = "Au"; + mstru10r = boost::make_shared(); + mstru10r->assign(mstru10->rbegin(), mstru10->rend()); + mstru9 = boost::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 = + boost::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 +} // namespace srreal +} // namespace diffpy using diffpy::srreal::TestPQEvaluator; diff --git a/src/tests/TestPairCounter.hpp b/src/tests/TestPairCounter.hpp index e697086c..5858f063 100644 --- a/src/tests/TestPairCounter.hpp +++ b/src/tests/TestPairCounter.hpp @@ -1,20 +1,20 @@ /***************************************************************************** -* -* 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.txt for a list of people who contributed. -* See LICENSE_DANSE.txt for license information. -* -****************************************************************************** -* -* class TestPairCounter -- unit tests for PairCounter class -* -*****************************************************************************/ + * + * 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.txt for a list of people who contributed. + * See LICENSE_DANSE.txt for license information. + * + ****************************************************************************** + * + * class TestPairCounter -- unit tests for PairCounter class + * + *****************************************************************************/ #include @@ -24,133 +24,113 @@ 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)); +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_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_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); } - - - 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()); + 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]); } - - - 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]); + 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 diff --git a/src/tests/TestPeakProfile.hpp b/src/tests/TestPeakProfile.hpp index 44b9875f..63b168ee 100644 --- a/src/tests/TestPeakProfile.hpp +++ b/src/tests/TestPeakProfile.hpp @@ -1,20 +1,20 @@ /***************************************************************************** -* -* 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.txt for a list of people who contributed. -* See LICENSE_DANSE.txt for license information. -* -****************************************************************************** -* -* class TestPeakProfile -- unit tests for various PeakProfile classes -* -*****************************************************************************/ + * + * 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.txt for a list of people who contributed. + * See LICENSE_DANSE.txt for license information. + * + ****************************************************************************** + * + * class TestPeakProfile -- unit tests for various PeakProfile classes + * + *****************************************************************************/ #include #include @@ -30,136 +30,113 @@ 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 : 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 diff --git a/src/tests/TestPeakWidthModel.hpp b/src/tests/TestPeakWidthModel.hpp index d42e0200..bd48a956 100644 --- a/src/tests/TestPeakWidthModel.hpp +++ b/src/tests/TestPeakWidthModel.hpp @@ -1,20 +1,20 @@ /***************************************************************************** -* -* libdiffpy Complex Modeling Initiative -* (c) 2019 Brookhaven Science Associates, -* Brookhaven National Laboratory. -* All rights reserved. -* -* File coded by: Pavol Juhas -* -* See AUTHORS.txt for a list of people who contributed. -* See LICENSE.txt for license information. -* -****************************************************************************** -* -* class TestPeakWidthModel -- test PeakWidthModel and derived classes -* -*****************************************************************************/ + * + * libdiffpy Complex Modeling Initiative + * (c) 2019 Brookhaven Science Associates, + * Brookhaven National Laboratory. + * All rights reserved. + * + * File coded by: Pavol Juhas + * + * See AUTHORS.txt for a list of people who contributed. + * See LICENSE.txt for license information. + * + ****************************************************************************** + * + * class TestPeakWidthModel -- test PeakWidthModel and derived classes + * + *****************************************************************************/ #include @@ -35,170 +35,147 @@ 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 : 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 +} // namespace srreal +} // namespace diffpy using diffpy::srreal::TestPeakWidthModel; diff --git a/src/tests/TestPeriodicStructureAdapter.hpp b/src/tests/TestPeriodicStructureAdapter.hpp index f00e6a0e..05411413 100644 --- a/src/tests/TestPeriodicStructureAdapter.hpp +++ b/src/tests/TestPeriodicStructureAdapter.hpp @@ -1,23 +1,23 @@ /***************************************************************************** -* -* libdiffpy Complex Modeling Initiative -* (c) 2013 Brookhaven Science Associates, -* Brookhaven National Laboratory. -* All rights reserved. -* -* File coded by: Pavol Juhas -* -* See AUTHORS.txt for a list of people who contributed. -* See LICENSE.txt for license information. -* -****************************************************************************** -* -* class TestPeriodicStructureAdapter -- unit tests for an adapter -* for periodic structures -* -* class TestPeriodicStructureBondGenerator -- unit tests for bond generator -* -*****************************************************************************/ + * + * libdiffpy Complex Modeling Initiative + * (c) 2013 Brookhaven Science Associates, + * Brookhaven National Laboratory. + * All rights reserved. + * + * File coded by: Pavol Juhas + * + * See AUTHORS.txt for a list of people who contributed. + * See LICENSE.txt for license information. + * + ****************************************************************************** + * + * class TestPeriodicStructureAdapter -- unit tests for an adapter + * for periodic structures + * + * class TestPeriodicStructureBondGenerator -- unit tests for bond generator + * + *****************************************************************************/ #include #include @@ -37,220 +37,183 @@ using namespace std; namespace { -int countBonds(BaseBondGenerator& bnds) -{ - int rv = 0; - for (bnds.rewind(); !bnds.finished(); bnds.next()) ++rv; - return rv; +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; +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 testmsd1(const Tstru& stru, const Tbnds& bnds) { + bool anisotropy1 = stru->siteAnisotropy(bnds->site1()); + double rv = + meanSquareDisplacement(bnds->Ucartesian1(), bnds->r01(), anisotropy1); + return rv; } -} // namespace +} // 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 = - boost::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 = - boost::dynamic_pointer_cast(m_kbise); - PeriodicStructureAdapterPtr pkbise1 = - boost::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 : 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 = + boost::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 = + boost::dynamic_pointer_cast(m_kbise); + PeriodicStructureAdapterPtr pkbise1 = + boost::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 @@ -258,169 +221,149 @@ class TestPeriodicStructureAdapter : public CxxTest::TestSuite // 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 neigbhors 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 tripple axis and - // have the same msd towards the central Li - else - { - TS_ASSERT_DELTA(0.00486942, testmsd1(stru, bnds), epsu); - } - } - } +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 +} // namespace srreal +} // namespace diffpy using diffpy::srreal::TestPeriodicStructureAdapter; using diffpy::srreal::TestPeriodicStructureBondGenerator; diff --git a/src/tests/TestPointsInSphere.hpp b/src/tests/TestPointsInSphere.hpp index 6be10856..814569d1 100644 --- a/src/tests/TestPointsInSphere.hpp +++ b/src/tests/TestPointsInSphere.hpp @@ -1,22 +1,22 @@ /*********************************************************************** -* -* 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.txt for a list of people who contributed. -* See LICENSE_DANSE.txt for license information. -* -************************************************************************ -* -* Unit tests for PointsInSphere module -* -* Comments: -* -***********************************************************************/ + * + * 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.txt for a list of people who contributed. + * See LICENSE_DANSE.txt for license information. + * + ************************************************************************ + * + * Unit tests for PointsInSphere module + * + * Comments: + * + ***********************************************************************/ #include @@ -31,32 +31,32 @@ 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; +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) { + 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; +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. @@ -67,7 +67,7 @@ ostream& operator<<(ostream &s, const vidxgroup &x) } */ -} // namespace +} // namespace //////////////////////////////////////////////////////////////////////// // TestPointsInSphere @@ -75,146 +75,111 @@ ostream& operator<<(ostream &s, const vidxgroup &x) 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); - } +class TestPointsInSphere : public CxxTest::TestSuite { + private: + LatticeParameters *latpar; - void tearDown() - { - delete latpar; - } + public: + void setUp() { latpar = new LatticeParameters(1, 1, 1, 90, 90, 90); } -private: + void tearDown() { delete latpar; } - int count(double Rmin, double Rmax) - { - int c = 0; - PointsInSphere sph(Rmin, Rmax, *latpar); - for (; !sph.finished(); sph.next()) ++c; - return c; - } + 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; + 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())); } - -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)); + 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_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)); + } + + 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 diff --git a/src/tests/TestR3linalg.hpp b/src/tests/TestR3linalg.hpp index 8f779dfb..dfc23175 100644 --- a/src/tests/TestR3linalg.hpp +++ b/src/tests/TestR3linalg.hpp @@ -1,20 +1,20 @@ /***************************************************************************** -* -* 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.txt for a list of people who contributed. -* See LICENSE_DANSE.txt for license information. -* -****************************************************************************** -* -* class TestR3linalg -- unit test suite for the R3linalg module -* -*****************************************************************************/ + * + * 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.txt for a list of people who contributed. + * See LICENSE_DANSE.txt for license information. + * + ****************************************************************************** + * + * class TestR3linalg -- unit test suite for the R3linalg module + * + *****************************************************************************/ #include @@ -24,115 +24,74 @@ 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 : 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 diff --git a/src/tests/TestRuntimePath.hpp b/src/tests/TestRuntimePath.hpp index 36b52189..ea0d46f7 100644 --- a/src/tests/TestRuntimePath.hpp +++ b/src/tests/TestRuntimePath.hpp @@ -1,20 +1,20 @@ /***************************************************************************** -* -* libdiffpy Complex Modeling Initiative -* (c) 2013 Brookhaven Science Associates, -* Brookhaven National Laboratory. -* All rights reserved. -* -* File coded by: Pavol Juhas -* -* See AUTHORS.txt for a list of people who contributed. -* See LICENSE.txt for license information. -* -****************************************************************************** -* -* class TestRuntimePath -- unit tests for the resolution of datafile paths -* -*****************************************************************************/ + * + * libdiffpy Complex Modeling Initiative + * (c) 2013 Brookhaven Science Associates, + * Brookhaven National Laboratory. + * All rights reserved. + * + * File coded by: Pavol Juhas + * + * See AUTHORS.txt for a list of people who contributed. + * See LICENSE.txt for license information. + * + ****************************************************************************** + * + * class TestRuntimePath -- unit tests for the resolution of datafile paths + * + *****************************************************************************/ #include #include @@ -26,63 +26,48 @@ using diffpy::runtimepath::datapath; - -class TestRuntimePath : public CxxTest::TestSuite -{ - - private: - - bool mhas_diffpyruntime; - std::string mdiffpyruntime; - - bool filereadable(std::string& fname) - { - std::ifstream fp(fname.c_str()); - return bool(fp); - } - - public: - - void setUp() - { - char* pe = getenv("DIFFPYRUNTIME"); - mhas_diffpyruntime = pe; - if (mhas_diffpyruntime) mdiffpyruntime = pe; - unsetenv("DIFFPYRUNTIME"); - } - - - void tearDown() - { - unsetenv("DIFFPYRUNTIME"); - if (mhas_diffpyruntime) - { - setenv("DIFFPYRUNTIME", mdiffpyruntime.c_str(), 1); - } - } - - - 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"); - } - - - void test_envvar_lookup() - { - using namespace std; - setenv("DIFFPYRUNTIME", "does/not/exist", 0); - TS_ASSERT_THROWS(datapath("f0_WaasKirf.dat"), runtime_error); - TS_ASSERT_THROWS(datapath("NaCl.cif"), runtime_error); - string td = prepend_testdata_dir(""); - setenv("DIFFPYRUNTIME", td.c_str(), 1); - string fnacl = prepend_testdata_dir("NaCl.cif"); - TS_ASSERT_EQUALS(fnacl, datapath("NaCl.cif")); - } - +class TestRuntimePath : public CxxTest::TestSuite { + private: + bool mhas_diffpyruntime; + std::string mdiffpyruntime; + + bool filereadable(std::string& fname) { + std::ifstream fp(fname.c_str()); + return bool(fp); + } + + public: + void setUp() { + char* pe = getenv("DIFFPYRUNTIME"); + mhas_diffpyruntime = pe; + if (mhas_diffpyruntime) mdiffpyruntime = pe; + unsetenv("DIFFPYRUNTIME"); + } + + void tearDown() { + unsetenv("DIFFPYRUNTIME"); + if (mhas_diffpyruntime) { + setenv("DIFFPYRUNTIME", mdiffpyruntime.c_str(), 1); + } + } + + 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"); + } + + void test_envvar_lookup() { + using namespace std; + setenv("DIFFPYRUNTIME", "does/not/exist", 0); + TS_ASSERT_THROWS(datapath("f0_WaasKirf.dat"), runtime_error); + TS_ASSERT_THROWS(datapath("NaCl.cif"), runtime_error); + string td = prepend_testdata_dir(""); + setenv("DIFFPYRUNTIME", td.c_str(), 1); + string fnacl = prepend_testdata_dir("NaCl.cif"); + TS_ASSERT_EQUALS(fnacl, datapath("NaCl.cif")); + } }; // End of file diff --git a/src/tests/TestScatteringFactorTable.hpp b/src/tests/TestScatteringFactorTable.hpp index 5c517a2d..cfb3e64a 100644 --- a/src/tests/TestScatteringFactorTable.hpp +++ b/src/tests/TestScatteringFactorTable.hpp @@ -1,21 +1,21 @@ /***************************************************************************** -* -* 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.txt for a list of people who contributed. -* See LICENSE_DANSE.txt for license information. -* -****************************************************************************** -* -* class TestScatteringFactorTable -- unit tests for implementations -* of the ScatteringFactorTable class -* -*****************************************************************************/ + * + * 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.txt for a list of people who contributed. + * See LICENSE_DANSE.txt for license information. + * + ****************************************************************************** + * + * class TestScatteringFactorTable -- unit tests for implementations + * of the ScatteringFactorTable class + * + *****************************************************************************/ #include #include @@ -28,184 +28,158 @@ 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()); - } - +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.hpp b/src/tests/TestSphericalShapeEnvelope.hpp index 2bf667c8..2b6fde07 100644 --- a/src/tests/TestSphericalShapeEnvelope.hpp +++ b/src/tests/TestSphericalShapeEnvelope.hpp @@ -1,20 +1,20 @@ /***************************************************************************** -* -* 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.txt for a list of people who contributed. -* See LICENSE_DANSE.txt for license information. -* -****************************************************************************** -* -* class TestSphericalShapeEnvelope -- unit tests for SphericalShapeEnvelope -* -*****************************************************************************/ + * + * 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.txt for a list of people who contributed. + * See LICENSE_DANSE.txt for license information. + * + ****************************************************************************** + * + * class TestSphericalShapeEnvelope -- unit tests for SphericalShapeEnvelope + * + *****************************************************************************/ #include @@ -24,66 +24,48 @@ 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 : 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 diff --git a/src/tests/TestStepCutEnvelope.hpp b/src/tests/TestStepCutEnvelope.hpp index ffb49a0f..1abbc86e 100644 --- a/src/tests/TestStepCutEnvelope.hpp +++ b/src/tests/TestStepCutEnvelope.hpp @@ -1,20 +1,20 @@ /***************************************************************************** -* -* 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.txt for a list of people who contributed. -* See LICENSE_DANSE.txt for license information. -* -****************************************************************************** -* -* class TestStepCutEnvelope -- unit tests for StepCutEnvelope class -* -*****************************************************************************/ + * + * 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.txt for a list of people who contributed. + * See LICENSE_DANSE.txt for license information. + * + ****************************************************************************** + * + * class TestStepCutEnvelope -- unit tests for StepCutEnvelope class + * + *****************************************************************************/ #include @@ -24,67 +24,49 @@ 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 : 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 diff --git a/src/tests/objcryst_helpers.cpp b/src/tests/objcryst_helpers.cpp index 76a61df1..a43da6f0 100644 --- a/src/tests/objcryst_helpers.cpp +++ b/src/tests/objcryst_helpers.cpp @@ -1,20 +1,20 @@ /***************************************************************************** -* -* 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.txt for a list of people who contributed. -* See LICENSE_DANSE.txt for license information. -* -****************************************************************************** -* -* shared helpers for loading diffpy structure objects. -* -*****************************************************************************/ + * + * 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.txt for a list of people who contributed. + * See LICENSE_DANSE.txt for license information. + * + ****************************************************************************** + * + * shared helpers for loading diffpy structure objects. + * + *****************************************************************************/ #include #include @@ -29,22 +29,20 @@ using namespace std; namespace { -void NoMessages(const string&) { } +void NoMessages(const string&) {} -} // namespace +} // 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; - ObjCryst::CIF cif(in); - ObjCryst::Crystal* crst = ObjCryst::CreateCrystalFromCIF(cif, false); - ObjCryst::fpObjCrystInformUser = OCMessages; - return crst; +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; + ObjCryst::CIF cif(in); + ObjCryst::Crystal* crst = ObjCryst::CreateCrystalFromCIF(cif, false); + ObjCryst::fpObjCrystInformUser = OCMessages; + return crst; } // End of file diff --git a/src/tests/objcryst_helpers.hpp b/src/tests/objcryst_helpers.hpp index 55d0e35e..437598a3 100644 --- a/src/tests/objcryst_helpers.hpp +++ b/src/tests/objcryst_helpers.hpp @@ -1,20 +1,20 @@ /***************************************************************************** -* -* 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.txt for a list of people who contributed. -* See LICENSE_DANSE.txt for license information. -* -****************************************************************************** -* -* shared helpers for loading diffpy structure objects. -* -*****************************************************************************/ + * + * 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.txt for a list of people who contributed. + * See LICENSE_DANSE.txt for license information. + * + ****************************************************************************** + * + * shared helpers for loading diffpy structure objects. + * + *****************************************************************************/ #ifndef OBJCRYST_HELPERS_HPP_INCLUDED #define OBJCRYST_HELPERS_HPP_INCLUDED diff --git a/src/tests/serialization_helpers.hpp b/src/tests/serialization_helpers.hpp index 4e689474..6ae112df 100644 --- a/src/tests/serialization_helpers.hpp +++ b/src/tests/serialization_helpers.hpp @@ -1,20 +1,20 @@ /***************************************************************************** -* -* 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.txt for a list of people who contributed. -* See LICENSE_DANSE.txt for license information. -* -****************************************************************************** -* -* helper template function for testing of serialization. -* -*****************************************************************************/ + * + * 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.txt for a list of people who contributed. + * See LICENSE_DANSE.txt for license information. + * + ****************************************************************************** + * + * helper template function for testing of serialization. + * + *****************************************************************************/ #ifndef SERIALIZATION_HELPERS_HPP_INCLUDED #define SERIALIZATION_HELPERS_HPP_INCLUDED @@ -22,17 +22,14 @@ #include #include - /// Return a duplicate object by saving and loading from a string template -T dumpandload(const T& src) -{ - using namespace diffpy; - std::string data = serialization_tostring(src); - T rv; - serialization_fromstring(rv, data); - return rv; +T dumpandload(const T& src) { + using namespace diffpy; + std::string data = serialization_tostring(src); + T rv; + serialization_fromstring(rv, data); + return rv; } - #endif // SERIALIZATION_HELPERS_HPP_INCLUDED diff --git a/src/tests/test_custompqconfig.hpp b/src/tests/test_custompqconfig.hpp index 04b75465..8dac6524 100644 --- a/src/tests/test_custompqconfig.hpp +++ b/src/tests/test_custompqconfig.hpp @@ -1,20 +1,20 @@ /***************************************************************************** -* -* libdiffpy Complex Modeling Initiative -* (c) 2014 Brookhaven Science Associates, -* Brookhaven National Laboratory. -* All rights reserved. -* -* File coded by: Pavol Juhas -* -* See AUTHORS.txt for a list of people who contributed. -* See LICENSE.txt for license information. -* -****************************************************************************** -* -* Helper class for testing the effect of customPQConfig. -* -*****************************************************************************/ + * + * libdiffpy Complex Modeling Initiative + * (c) 2014 Brookhaven Science Associates, + * Brookhaven National Laboratory. + * All rights reserved. + * + * File coded by: Pavol Juhas + * + * See AUTHORS.txt for a list of people who contributed. + * See LICENSE.txt for license information. + * + ****************************************************************************** + * + * Helper class for testing the effect of customPQConfig. + * + *****************************************************************************/ #ifndef TEST_CUSTOMPQCONFIG_HPP_INCLUDED #define TEST_CUSTOMPQCONFIG_HPP_INCLUDED @@ -22,48 +22,39 @@ #include #include -using diffpy::srreal::StructureAdapterPtr; using diffpy::srreal::PeriodicStructureAdapter; using diffpy::srreal::PeriodicStructureAdapterPtr; +using diffpy::srreal::StructureAdapterPtr; // --------------------------------------------------------------------------- -class PeriodicAdapterWithPQConfig : - public PeriodicStructureAdapter -{ - public: - - // constructors - PeriodicAdapterWithPQConfig() { } - PeriodicAdapterWithPQConfig(const PeriodicStructureAdapter& src) - : PeriodicStructureAdapter(src) - { } - - // overloads - void customPQConfig(diffpy::srreal::PairQuantity* pq) const - { - std::map::const_iterator it; - for (it = cfg.begin(); it != cfg.end(); ++it) - { - pq->setDoubleAttr(it->first, it->second); - } - } - - // data - std::map cfg; - - private: - - // serialization - friend class boost::serialization::access; - template - void serialize(Archive& ar, const unsigned int version) - { - using boost::serialization::base_object; - ar & base_object(*this); - ar & cfg; - } - +class PeriodicAdapterWithPQConfig : public PeriodicStructureAdapter { + public: + // constructors + PeriodicAdapterWithPQConfig() {} + PeriodicAdapterWithPQConfig(const PeriodicStructureAdapter& src) + : PeriodicStructureAdapter(src) {} + + // overloads + void customPQConfig(diffpy::srreal::PairQuantity* pq) const { + std::map::const_iterator it; + for (it = cfg.begin(); it != cfg.end(); ++it) { + pq->setDoubleAttr(it->first, it->second); + } + } + + // data + std::map cfg; + + private: + // serialization + friend class boost::serialization::access; + template + void serialize(Archive& ar, const unsigned int version) { + using boost::serialization::base_object; + ar& base_object(*this); + ar & cfg; + } }; BOOST_CLASS_EXPORT(PeriodicAdapterWithPQConfig) @@ -71,16 +62,14 @@ BOOST_CLASS_EXPORT(PeriodicAdapterWithPQConfig) // Typedefs and Functions ---------------------------------------------------- typedef boost::shared_ptr - PeriodicAdapterWithPQConfigPtr; - -inline -PeriodicAdapterWithPQConfigPtr -addCustomPQConfig(StructureAdapterPtr stru) -{ - PeriodicStructureAdapterPtr pstru = - boost::dynamic_pointer_cast(stru); - return PeriodicAdapterWithPQConfigPtr( - new PeriodicAdapterWithPQConfig(*pstru)); + PeriodicAdapterWithPQConfigPtr; + +inline PeriodicAdapterWithPQConfigPtr addCustomPQConfig( + StructureAdapterPtr stru) { + PeriodicStructureAdapterPtr pstru = + boost::dynamic_pointer_cast(stru); + return PeriodicAdapterWithPQConfigPtr( + new PeriodicAdapterWithPQConfig(*pstru)); } #endif // TEST_CUSTOMPQCONFIG_HPP_INCLUDED diff --git a/src/tests/test_helpers.cpp b/src/tests/test_helpers.cpp index d8f5bdf9..3f6821f9 100644 --- a/src/tests/test_helpers.cpp +++ b/src/tests/test_helpers.cpp @@ -1,20 +1,20 @@ /***************************************************************************** -* -* 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.txt for a list of people who contributed. -* See LICENSE_DANSE.txt for license information. -* -****************************************************************************** -* -* Definitions of helper functions for other unit tests. -* -*****************************************************************************/ + * + * 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.txt for a list of people who contributed. + * See LICENSE_DANSE.txt for license information. + * + ****************************************************************************** + * + * Definitions of helper functions for other unit tests. + * + *****************************************************************************/ #include #include @@ -38,104 +38,89 @@ // Path utilities ------------------------------------------------------------ -std::string prepend_tests_dir(const std::string& f) -{ - std::string rv = STRINGIFY(DIFFPYTESTSDIRPATH); - rv = rv + '/' + f; - return rv; +std::string prepend_tests_dir(const std::string& f) { + std::string rv = STRINGIFY(DIFFPYTESTSDIRPATH); + rv = rv + '/' + f; + return rv; } - -std::string prepend_testdata_dir(const std::string& f) -{ - using std::string; - string ft = "testdata/"; - ft += f; - string rv = prepend_tests_dir(ft); - return rv; +std::string prepend_testdata_dir(const std::string& f) { + using std::string; + string ft = "testdata/"; + ft += f; + string rv = prepend_tests_dir(ft); + return rv; } // Load PeriodicStructureAdapter from a PdfFit formatted file. -diffpy::srreal::StructureAdapterPtr - loadTestPeriodicStructure(const std::string& tailname) -{ - using namespace std; - using namespace diffpy::srreal; - using diffpy::runtimepath::LineReader; - using diffpy::mathutils::eps_eq; - // instantiate PeriodicStructureAdapter - StructureAdapterPtr pstru(new PeriodicStructureAdapter); - PeriodicStructureAdapter& stru = - static_cast(*pstru); - // open data file - string fullpath = prepend_testdata_dir(tailname); - ifstream fp(fullpath.c_str()); - LineReader line; - string fileformat; - // process structure file header - while (fp >> line) - { - if (line.wcount() >= 2 && boost::iequals(line.words[0], "format")) - { - fileformat = line.words[1]; - assert(boost::iequals(fileformat, "pdffit")); - } - if (line.wcount() == 7 && line.words[0] == "cell") - { - boost::replace_all(line.line, ",", " "); - string skip; - double a, b, c, alpha, beta, gamma; - istringstream fpwords(line.line); - fpwords >> skip >> a >> b >> c >> alpha >> beta >> gamma; - stru.setLatPar(a, b, c, alpha, beta, gamma); - } if (line.wcount() == 1 && line.words[0] == "atoms") break; +diffpy::srreal::StructureAdapterPtr loadTestPeriodicStructure( + const std::string& tailname) { + using namespace std; + using namespace diffpy::srreal; + using diffpy::mathutils::eps_eq; + using diffpy::runtimepath::LineReader; + // instantiate PeriodicStructureAdapter + StructureAdapterPtr pstru(new PeriodicStructureAdapter); + PeriodicStructureAdapter& stru = + static_cast(*pstru); + // open data file + string fullpath = prepend_testdata_dir(tailname); + ifstream fp(fullpath.c_str()); + LineReader line; + string fileformat; + // process structure file header + while (fp >> line) { + if (line.wcount() >= 2 && boost::iequals(line.words[0], "format")) { + fileformat = line.words[1]; + assert(boost::iequals(fileformat, "pdffit")); + } + if (line.wcount() == 7 && line.words[0] == "cell") { + boost::replace_all(line.line, ",", " "); + string skip; + double a, b, c, alpha, beta, gamma; + istringstream fpwords(line.line); + fpwords >> skip >> a >> b >> c >> alpha >> beta >> gamma; + stru.setLatPar(a, b, c, alpha, beta, gamma); + } + if (line.wcount() == 1 && line.words[0] == "atoms") break; + } + assert(!fileformat.empty()); + // load atoms + while (true) { + Atom ai; + double ignore; + R3::Vector& xyz = ai.xyz_cartn; + R3::Matrix& Uc = ai.uij_cartn; + fp >> ai.atomtype >> xyz[0] >> xyz[1] >> xyz[2] >> ai.occupancy >> ignore >> + ignore >> ignore >> ignore >> Uc(0, 0) >> Uc(1, 1) >> Uc(2, 2) >> + ignore >> ignore >> ignore >> Uc(0, 1) >> Uc(0, 2) >> Uc(1, 2) >> + ignore >> ignore >> ignore; + Uc(1, 0) = Uc(0, 1); + Uc(2, 0) = Uc(0, 2); + Uc(2, 1) = Uc(1, 2); + if (!fp) break; + // convert string symbol to standard case + boost::to_lower(ai.atomtype); + string::iterator tt = ai.atomtype.begin(); + for (; tt != ai.atomtype.end(); ++tt) { + if (isalpha(*tt)) { + *tt = toupper(*tt); + break; + } } - assert(!fileformat.empty()); - // load atoms - while (true) - { - Atom ai; - double ignore; - R3::Vector& xyz = ai.xyz_cartn; - R3::Matrix& Uc = ai.uij_cartn; - fp >> - ai.atomtype >> xyz[0] >> xyz[1] >> xyz[2] >> ai.occupancy >> - ignore >> ignore >> ignore >> ignore >> - Uc(0, 0) >> Uc(1, 1) >> Uc(2, 2) >> - ignore >> ignore >> ignore >> - Uc(0, 1) >> Uc(0, 2) >> Uc(1, 2) >> - ignore >> ignore >> ignore; - Uc(1, 0) = Uc(0, 1); - Uc(2, 0) = Uc(0, 2); - Uc(2, 1) = Uc(1, 2); - if (!fp) break; - // convert string symbol to standard case - boost::to_lower(ai.atomtype); - string::iterator tt = ai.atomtype.begin(); - for (; tt != ai.atomtype.end(); ++tt) - { - if (isalpha(*tt)) - { - *tt = toupper(*tt); - break; - } - } - // convert coordinates and the ADP matrix and determine anisotropy - stru.toCartesian(ai); + // convert coordinates and the ADP matrix and determine anisotropy + stru.toCartesian(ai); + ai.anisotropy = !eps_eq(Uc(0, 0), Uc(1, 1)) || !eps_eq(Uc(0, 0), Uc(2, 2)); + for (int i = 0; !ai.anisotropy && i < R3::Ndim; ++i) { + for (int j = i + 1; j < R3::Ndim; ++j) { ai.anisotropy = - !eps_eq(Uc(0, 0), Uc(1, 1)) || !eps_eq(Uc(0, 0), Uc(2, 2)); - for (int i = 0; !ai.anisotropy && i < R3::Ndim; ++i) - { - for (int j = i + 1; j < R3::Ndim; ++j) - { - ai.anisotropy = ai.anisotropy || - !eps_eq(0.0, Uc(i, j)) || !eps_eq(0.0, Uc(j, i)); - } - } - stru.append(ai); + ai.anisotropy || !eps_eq(0.0, Uc(i, j)) || !eps_eq(0.0, Uc(j, i)); + } } - return pstru; + stru.append(ai); + } + return pstru; } // End of test_helpers.cpp diff --git a/src/tests/test_helpers.hpp b/src/tests/test_helpers.hpp index f1fd3c23..2c75fe23 100644 --- a/src/tests/test_helpers.hpp +++ b/src/tests/test_helpers.hpp @@ -1,20 +1,20 @@ /***************************************************************************** -* -* 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.txt for a list of people who contributed. -* See LICENSE_DANSE.txt for license information. -* -****************************************************************************** -* -* Helper functions used in other unit tests. -* -*****************************************************************************/ + * + * 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.txt for a list of people who contributed. + * See LICENSE_DANSE.txt for license information. + * + ****************************************************************************** + * + * Helper functions used in other unit tests. + * + *****************************************************************************/ #ifndef TEST_HELPERS_HPP_INCLUDED #define TEST_HELPERS_HPP_INCLUDED @@ -25,7 +25,7 @@ std::string prepend_tests_dir(const std::string& f); std::string prepend_testdata_dir(const std::string& f); -diffpy::srreal::StructureAdapterPtr - loadTestPeriodicStructure(const std::string& tailname); +diffpy::srreal::StructureAdapterPtr loadTestPeriodicStructure( + const std::string& tailname); #endif // TEST_HELPERS_HPP_INCLUDED From 5e9ea98127d4ec7de13aafc8959967cc8a8087ae Mon Sep 17 00:00:00 2001 From: Tieqiong Zhang Date: Wed, 25 Jun 2025 17:31:44 -0500 Subject: [PATCH 09/56] add comments into ignore_words --- .codespell/ignore_words.txt | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/.codespell/ignore_words.txt b/.codespell/ignore_words.txt index 7d03c5ba..7825d133 100644 --- a/.codespell/ignore_words.txt +++ b/.codespell/ignore_words.txt @@ -1,2 +1,10 @@ +# Please include explanations for each ignored word (lowercase). +# See https://docs.openverse.org/meta/codespell.html for docs. + +# src/diffpy/srreal/BaseDebyeSum.cpp:231,233 +# variable name multipl + +# src/tests/TestScatteringFactorTable.hpp:73-80 +# Custom alias name of C in test Ccustom From fdb9d4b34479488c3d8c4825c624455dfa454b8b Mon Sep 17 00:00:00 2001 From: Tieqiong Date: Tue, 16 Sep 2025 23:21:28 +0000 Subject: [PATCH 10/56] bump fallback_version rename doc to docs objcryst link name for linux --- {doc => docs}/Makefile | 0 {doc => docs}/source/conf.py | 0 {doc => docs}/source/index.rst | 0 {doc => docs}/source/license.rst | 0 {doc => docs}/source/release.rst | 0 site_scons/fallback_version.py | 2 +- src/SConscript | 2 +- 7 files changed, 2 insertions(+), 2 deletions(-) rename {doc => docs}/Makefile (100%) rename {doc => docs}/source/conf.py (100%) rename {doc => docs}/source/index.rst (100%) rename {doc => docs}/source/license.rst (100%) rename {doc => docs}/source/release.rst (100%) diff --git a/doc/Makefile b/docs/Makefile similarity index 100% rename from doc/Makefile rename to docs/Makefile diff --git a/doc/source/conf.py b/docs/source/conf.py similarity index 100% rename from doc/source/conf.py rename to docs/source/conf.py diff --git a/doc/source/index.rst b/docs/source/index.rst similarity index 100% rename from doc/source/index.rst rename to docs/source/index.rst diff --git a/doc/source/license.rst b/docs/source/license.rst similarity index 100% rename from doc/source/license.rst rename to docs/source/license.rst diff --git a/doc/source/release.rst b/docs/source/release.rst similarity index 100% rename from doc/source/release.rst rename to docs/source/release.rst diff --git a/site_scons/fallback_version.py b/site_scons/fallback_version.py index c147df59..058b9857 100644 --- a/site_scons/fallback_version.py +++ b/site_scons/fallback_version.py @@ -7,4 +7,4 @@ Update FALLBACK_VERSION when tagging a new release. """ -FALLBACK_VERSION = "1.4.0.post0" +FALLBACK_VERSION = "1.4.1rc1.post0" diff --git a/src/SConscript b/src/SConscript index f53292a4..cf2c7a12 100644 --- a/src/SConscript +++ b/src/SConscript @@ -157,7 +157,7 @@ env_lib = env.Clone() # Setup GSL, the GNU Scientific library. # The dladdr call in runtimepath.cpp requires the dl library for Windows. if env['has_objcryst'] == True: - env_lib.AppendUnique(LIBS=['dl', 'gsl', 'gslcblas','boost_serialization', 'libobjcryst']) + env_lib.AppendUnique(LIBS=['dl', 'gsl', 'gslcblas','boost_serialization', 'libObjCryst']) else: env_lib.AppendUnique(LIBS=['dl', 'gsl', 'gslcblas','boost_serialization']) From 01bd9e43f38dcd4751efeb6025b42664a73e01a4 Mon Sep 17 00:00:00 2001 From: Tieqiong Zhang Date: Tue, 16 Sep 2025 18:34:15 -0500 Subject: [PATCH 11/56] rename doc to docs --- .flake8 | 2 +- .gitattributes | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.flake8 b/.flake8 index 52d7edb1..1efa4c9d 100644 --- a/.flake8 +++ b/.flake8 @@ -6,7 +6,7 @@ exclude = __pycache__, build, dist, - doc/source/conf.py + docs/source/conf.py site_scons/* # bring it back after updating scons tools max-line-length = 115 # Ignore some style 'errors' produced while formatting by 'black' diff --git a/.gitattributes b/.gitattributes index 503fc76f..91a78d45 100644 --- a/.gitattributes +++ b/.gitattributes @@ -2,5 +2,5 @@ /.gitignore export-ignore /.travis* export-ignore /conda-recipe/ export-ignore -/doc export-ignore +/docs export-ignore gitarchive.cfg export-subst From c4f614459c6a530fb62c51b6cbd446bdc64960f4 Mon Sep 17 00:00:00 2001 From: Caden Myers Date: Mon, 19 Oct 2026 15:33:12 -0400 Subject: [PATCH 12/56] skpkg: copy over all code into skpkg standard dir and add necessary reqs to requirements/ --- .codecov.yml | 14 + .codespell/ignore_lines.txt | 2 + .codespell/ignore_words.txt | 8 + .flake8 | 13 + .github/ISSUE_TEMPLATE/bug_feature.md | 16 + .github/ISSUE_TEMPLATE/release_checklist.md | 47 +++ .../pull_request_template.md | 15 + .../build-and-publish-docs-on-dispatch.yml | 18 + .../workflows/build-wheel-release-upload.yml | 76 +++++ .github/workflows/check-news-item.yml | 12 + .github/workflows/matrix-and-codecov.yml | 21 ++ .github/workflows/tests-on-pr.yml | 15 + .gitignore | 111 ++++-- .isort.cfg | 5 + .pre-commit-config.yaml | 66 ++++ .readthedocs.yaml | 13 + ...est_383658db2a6c0cca38b6f776638a25f9_0.cpp | 5 + ...a25f9_0_9bc8887b698aff96a593dc32c66c9ad1.o | Bin 0 -> 608 bytes ...est_e492448966d6e04631c094ba6d6e8a32_0.cpp | 7 + ...ftest_e492448966d6e04631c094ba6d6e8a32_0.o | Bin 0 -> 608 bytes ...d6e8a32_0_32bd209fc335e052be8df6851f49edb1 | Bin 0 -> 4272 bytes ...d6e8a32_0_e1a1914d3e176b585cd892b23cd56a48 | Bin 0 -> 4272 bytes ...est_f043931bac80a040e9dbafdbb5991c64_0.cpp | 8 + ...ftest_f043931bac80a040e9dbafdbb5991c64_0.o | Bin 0 -> 19944 bytes ...5991c64_0_4168d21565c3518ee97cd47e0a4b72d9 | Bin 0 -> 26112 bytes ...5991c64_0_4eeef59bb4603aa6f185d0625b6d5bda | Bin 0 -> 26112 bytes .sconsign.dblite | Bin 0 -> 1342638 bytes AUTHORS.rst | 10 + CHANGELOG.rst | 5 + CODE-OF-CONDUCT.rst | 133 ++++++++ LICENSE.rst | 29 ++ MANIFEST.in | 12 + README.rst | 137 ++++++++ cookiecutter.json | 20 ++ docs/Makefile | 194 +++++++++++ docs/make.bat | 36 ++ docs/source/_static/.placeholder | 0 docs/source/api/libdiffpy.example_package.rst | 31 ++ docs/source/api/libdiffpy.rst | 30 ++ docs/source/conf.py | 322 ++++++++++++++++++ docs/source/getting-started.rst | 79 +++++ docs/source/img/.placeholder | 0 docs/source/img/scikit-package-logo-text.png | Bin 0 -> 187608 bytes docs/source/index.rst | 56 +++ docs/source/license.rst | 38 +++ docs/source/release.rst | 5 + docs/source/snippets/.placeholder | 0 docs/source/snippets/example-table.rst | 28 ++ news/TEMPLATE.rst | 23 ++ pyproject.toml | 85 +++++ requirements/build.txt | 2 + requirements/conda.txt | 3 + requirements/docs.txt | 5 + requirements/pip.txt | 1 + requirements/tests.txt | 1 + setup.py | 37 ++ src/libdiffpy/__init__.py | 23 ++ src/libdiffpy/version.py | 26 ++ tests/conftest.py | 19 ++ tests/test_version.py | 10 + 60 files changed, 1847 insertions(+), 25 deletions(-) create mode 100644 .codecov.yml create mode 100644 .codespell/ignore_lines.txt create mode 100644 .codespell/ignore_words.txt create mode 100644 .flake8 create mode 100644 .github/ISSUE_TEMPLATE/bug_feature.md create mode 100644 .github/ISSUE_TEMPLATE/release_checklist.md create mode 100644 .github/PULL_REQUEST_TEMPLATE/pull_request_template.md create mode 100644 .github/workflows/build-and-publish-docs-on-dispatch.yml create mode 100644 .github/workflows/build-wheel-release-upload.yml create mode 100644 .github/workflows/check-news-item.yml create mode 100644 .github/workflows/matrix-and-codecov.yml create mode 100644 .github/workflows/tests-on-pr.yml create mode 100644 .isort.cfg create mode 100644 .pre-commit-config.yaml create mode 100644 .readthedocs.yaml create mode 100644 .sconf_temp/conftest_383658db2a6c0cca38b6f776638a25f9_0.cpp create mode 100644 .sconf_temp/conftest_383658db2a6c0cca38b6f776638a25f9_0_9bc8887b698aff96a593dc32c66c9ad1.o create mode 100644 .sconf_temp/conftest_e492448966d6e04631c094ba6d6e8a32_0.cpp create mode 100644 .sconf_temp/conftest_e492448966d6e04631c094ba6d6e8a32_0.o create mode 100755 .sconf_temp/conftest_e492448966d6e04631c094ba6d6e8a32_0_32bd209fc335e052be8df6851f49edb1 create mode 100755 .sconf_temp/conftest_e492448966d6e04631c094ba6d6e8a32_0_e1a1914d3e176b585cd892b23cd56a48 create mode 100644 .sconf_temp/conftest_f043931bac80a040e9dbafdbb5991c64_0.cpp create mode 100644 .sconf_temp/conftest_f043931bac80a040e9dbafdbb5991c64_0.o create mode 100755 .sconf_temp/conftest_f043931bac80a040e9dbafdbb5991c64_0_4168d21565c3518ee97cd47e0a4b72d9 create mode 100755 .sconf_temp/conftest_f043931bac80a040e9dbafdbb5991c64_0_4eeef59bb4603aa6f185d0625b6d5bda create mode 100644 .sconsign.dblite create mode 100644 AUTHORS.rst create mode 100644 CHANGELOG.rst create mode 100644 CODE-OF-CONDUCT.rst create mode 100644 LICENSE.rst create mode 100644 MANIFEST.in create mode 100644 README.rst create mode 100644 cookiecutter.json create mode 100644 docs/Makefile create mode 100644 docs/make.bat create mode 100644 docs/source/_static/.placeholder create mode 100644 docs/source/api/libdiffpy.example_package.rst create mode 100644 docs/source/api/libdiffpy.rst create mode 100644 docs/source/conf.py create mode 100644 docs/source/getting-started.rst create mode 100644 docs/source/img/.placeholder create mode 100644 docs/source/img/scikit-package-logo-text.png create mode 100644 docs/source/index.rst create mode 100644 docs/source/license.rst create mode 100644 docs/source/release.rst create mode 100644 docs/source/snippets/.placeholder create mode 100644 docs/source/snippets/example-table.rst create mode 100644 news/TEMPLATE.rst create mode 100644 pyproject.toml create mode 100644 requirements/build.txt create mode 100644 requirements/conda.txt create mode 100644 requirements/docs.txt create mode 100644 requirements/pip.txt create mode 100644 requirements/tests.txt create mode 100644 setup.py create mode 100644 src/libdiffpy/__init__.py create mode 100644 src/libdiffpy/version.py create mode 100644 tests/conftest.py create mode 100644 tests/test_version.py diff --git a/.codecov.yml b/.codecov.yml new file mode 100644 index 00000000..4af5eb24 --- /dev/null +++ b/.codecov.yml @@ -0,0 +1,14 @@ +coverage: + status: + project: # more options at https://docs.codecov.com/docs/commit-status + default: + target: auto # use the coverage from the base commit, fail if coverage is lower + threshold: 0% # allow the coverage to drop by + +comment: + layout: " diff, flags, files" + behavior: default + require_changes: false + 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] diff --git a/.codespell/ignore_lines.txt b/.codespell/ignore_lines.txt new file mode 100644 index 00000000..07fa7c8c --- /dev/null +++ b/.codespell/ignore_lines.txt @@ -0,0 +1,2 @@ +;; Please include filenames and explanations for each ignored line. +;; See https://docs.openverse.org/meta/codespell.html for docs. diff --git a/.codespell/ignore_words.txt b/.codespell/ignore_words.txt new file mode 100644 index 00000000..04b4fcfa --- /dev/null +++ b/.codespell/ignore_words.txt @@ -0,0 +1,8 @@ +;; Please include explanations for each ignored word (lowercase). +;; See https://docs.openverse.org/meta/codespell.html for docs. + +;; abbreviation for "materials" often used in a journal title +mater + +;; Frobenius norm used in np.linalg.norm +fro diff --git a/.flake8 b/.flake8 new file mode 100644 index 00000000..88077af0 --- /dev/null +++ b/.flake8 @@ -0,0 +1,13 @@ +# As of now, flake8 does not natively support configuration via pyproject.toml +# https://github.com/microsoft/vscode-flake8/issues/135 +[flake8] +exclude = + .git, + __pycache__, + build, + dist, + docs/source/conf.py +max-line-length = 79 +# Ignore some style 'errors' produced while formatting by 'black' +# https://black.readthedocs.io/en/stable/guides/using_black_with_other_tools.html#labels-why-pycodestyle-warnings +extend-ignore = E203 diff --git a/.github/ISSUE_TEMPLATE/bug_feature.md b/.github/ISSUE_TEMPLATE/bug_feature.md new file mode 100644 index 00000000..b3454deb --- /dev/null +++ b/.github/ISSUE_TEMPLATE/bug_feature.md @@ -0,0 +1,16 @@ +--- +name: Bug Report or Feature Request +about: Report a bug or suggest a new feature! +title: "" +labels: "" +assignees: "" +--- + +### Problem + + + +### Proposed solution diff --git a/.github/ISSUE_TEMPLATE/release_checklist.md b/.github/ISSUE_TEMPLATE/release_checklist.md new file mode 100644 index 00000000..56c5fca3 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/release_checklist.md @@ -0,0 +1,47 @@ +--- +name: Release +about: Checklist and communication channel for PyPI and GitHub release +title: "Ready for PyPI/GitHub release" +labels: "release" +assignees: "" +--- + +### PyPI/GitHub rc-release preparation checklist: + +- [ ] All PRs/issues attached to the release are merged. +- [ ] All the badges on the README are passing. +- [ ] License information is verified as correct. If you are unsure, please comment below. +- [ ] Locally rendered documentation contains all appropriate pages, tutorials, and other human-written text is up-to-date with any changes in the code. +- [ ] All API references are included. To check this, run `conda install scikit-package` and then `package build api-doc`. Review any edits made by rerendering the docs locally. +- [ ] Installation instructions in the README, documentation, and the website are updated. +- [ ] Successfully run any tutorial examples or do functional testing with the latest Python version. +- [ ] Grammar and writing quality are checked (no typos). +- [ ] Install `pip install build twine`, run `python -m build` and `twine check dist/*` to ensure that the package can be built and is correctly formatted for PyPI release. +- [ ] Dispatch matrix testing to test the release on all Python versions and systems. If you do not have permission to run this workflow, tag the maintainer and say `@maintainer, please dispatch matrix testing workflow`. + +Please tag the maintainer (e.g., @username) in the comment here when you are ready for the PyPI/GitHub release. Include any additional comments necessary, such as version information and details about the pre-release here: + +### PyPI/GitHub full-release preparation checklist: + +- [ ] Create a new conda environment and install the rc from PyPI (`pip install ==??`) +- [ ] License information on PyPI is correct. +- [ ] Docs are deployed successfully to `https:///`. +- [ ] Successfully run all tests, tutorial examples or do functional testing. + +Please let the maintainer know that all checks are done and the package is ready for full release. + +### conda-forge release preparation checklist: + + + +- [ ] Ensure that the full release has appeared on PyPI successfully. +- [ ] New package dependencies listed in `conda.txt` and `tests.txt` are added to `meta.yaml` in the feedstock. +- [ ] Close any open issues on the feedstock. Reach out to the maintainer if you have questions. +- [ ] Tag the maintainer for conda-forge release. + +### Post-release checklist + + + +- [ ] Run tutorial examples and conduct functional testing using the installation guide in the README. Attach screenshots/results as comments. +- [ ] Documentation (README, tutorials, API references, and websites) is deployed without broken links or missing figures. diff --git a/.github/PULL_REQUEST_TEMPLATE/pull_request_template.md b/.github/PULL_REQUEST_TEMPLATE/pull_request_template.md new file mode 100644 index 00000000..1099d862 --- /dev/null +++ b/.github/PULL_REQUEST_TEMPLATE/pull_request_template.md @@ -0,0 +1,15 @@ +### What problem does this PR address? + + + +### What should the reviewer(s) do? + + + + diff --git a/.github/workflows/build-and-publish-docs-on-dispatch.yml b/.github/workflows/build-and-publish-docs-on-dispatch.yml new file mode 100644 index 00000000..1d6992ea --- /dev/null +++ b/.github/workflows/build-and-publish-docs-on-dispatch.yml @@ -0,0 +1,18 @@ +name: Build and Publish Docs on Dispatch + +on: + workflow_dispatch: + +jobs: + get-python-version: + uses: scikit-package/release-scripts/.github/workflows/_get-python-version-latest.yml@v0 + with: + python_version: 0 + + docs: + uses: scikit-package/release-scripts/.github/workflows/_release-docs.yml@v0 + with: + project: libdiffpy + c_extension: true + headless: false + python_version: ${{ fromJSON(needs.get-python-version.outputs.latest_python_version) }} diff --git a/.github/workflows/build-wheel-release-upload.yml b/.github/workflows/build-wheel-release-upload.yml new file mode 100644 index 00000000..9109858f --- /dev/null +++ b/.github/workflows/build-wheel-release-upload.yml @@ -0,0 +1,76 @@ +name: Build Wheel and Release + +# Trigger on tag push or manual dispatch. +# Tag and release privilege are verified inside the reusable workflow. +on: + workflow_dispatch: + push: + tags: + - "*" + +# ── Release modality ────────────────────────────────────────────────────────── +# Three options are provided below. Only ONE job should be active at a time. +# To switch: comment out the active job and uncomment your preferred option, +# then commit the change to main before tagging a release. +# ───────────────────────────────────────────────────────────────────────────── + +jobs: + # Option 1 (default): Release to GitHub, publish to PyPI, and deploy docs. + # + # The wheel is uploaded to PyPI so users can install with `pip install`. + # A GitHub release is created with the changelog as the release body, and + # the Sphinx documentation is rebuilt and deployed to GitHub Pages. + # + # Choose this for open-source packages distributed via PyPI and/or + # conda-forge where broad public availability is the goal. + build-release: + uses: scikit-package/release-scripts/.github/workflows/_build-wheel-release-upload.yml@v0 + with: + project: libdiffpy + c_extension: true + maintainer_github_username: sbillinge + secrets: + PYPI_TOKEN: ${{ secrets.PYPI_TOKEN }} + PAT_TOKEN: ${{ secrets.PAT_TOKEN }} + + # Option 2: Release to GitHub and deploy docs, without publishing to PyPI. + # + # A GitHub release is created and the Sphinx docs are deployed, but the + # wheel is not uploaded to PyPI. The source code remains publicly visible + # on GitHub and can be installed directly from there. + # + # Choose this when the package is public but you prefer to keep it off the + # default pip index — for example, if you distribute via conda-forge only, + # or if the package is not yet ready for a permanent PyPI presence. + # + # To use: comment out Option 1 above and uncomment the lines below. + # build-release-no-pypi: + # uses: scikit-package/release-scripts/.github/workflows/_build-release-github-no-pypi.yml@v0 + # with: + # project: libdiffpy + # c_extension: true + # maintainer_github_username: sbillinge + # secrets: + # PAT_TOKEN: ${{ secrets.PAT_TOKEN }} + + # Option 3: Release to GitHub with wheel, license, and instructions bundled + # as a downloadable zip attached to the GitHub release asset. + # + # The wheel is built and packaged together with INSTRUCTIONS.txt and the + # LICENSE file into a zip that is attached directly to the GitHub release. + # Users with access to the (private) repo download the zip, follow the + # instructions inside, and install locally with pip. No PyPI or conda-forge + # upload occurs, and no docs are deployed. + # + # Choose this for private or restricted packages where distribution must be + # controlled: only users with repo access can download the release asset, + # making the GitHub release itself the distribution channel. + # + # To use: comment out Option 1 above and uncomment the lines below. + # build-release-private: + # uses: scikit-package/release-scripts/.github/workflows/_build-release-github-private-pure.yml@v0 + # with: + # project: libdiffpy + # maintainer_github_username: sbillinge + # secrets: + # PAT_TOKEN: ${{ secrets.PAT_TOKEN }} diff --git a/.github/workflows/check-news-item.yml b/.github/workflows/check-news-item.yml new file mode 100644 index 00000000..fc34d9f5 --- /dev/null +++ b/.github/workflows/check-news-item.yml @@ -0,0 +1,12 @@ +name: Check for News + +on: + pull_request_target: + branches: + - main # GitHub does not evaluate expressions in trigger filters; edit this value if your base branch is not main + +jobs: + check-news-item: + uses: scikit-package/release-scripts/.github/workflows/_check-news-item.yml@v0 + with: + project: libdiffpy diff --git a/.github/workflows/matrix-and-codecov.yml b/.github/workflows/matrix-and-codecov.yml new file mode 100644 index 00000000..969d4952 --- /dev/null +++ b/.github/workflows/matrix-and-codecov.yml @@ -0,0 +1,21 @@ +name: Matrix and Codecov + +on: + # push: + # branches: + # - main + release: + types: + - prereleased + - published + workflow_dispatch: + +jobs: + matrix-coverage: + uses: scikit-package/release-scripts/.github/workflows/_matrix-and-codecov-on-merge-to-main.yml@v0 + with: + project: libdiffpy + c_extension: true + headless: false + secrets: + CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }} diff --git a/.github/workflows/tests-on-pr.yml b/.github/workflows/tests-on-pr.yml new file mode 100644 index 00000000..a9d864b5 --- /dev/null +++ b/.github/workflows/tests-on-pr.yml @@ -0,0 +1,15 @@ +name: Tests on PR + +on: + pull_request: + workflow_dispatch: + +jobs: + tests-on-pr: + uses: scikit-package/release-scripts/.github/workflows/_tests-on-pr.yml@v0 + with: + project: libdiffpy + c_extension: true + headless: false + secrets: + CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }} diff --git a/.gitignore b/.gitignore index 496ca8a9..099e2948 100644 --- a/.gitignore +++ b/.gitignore @@ -1,32 +1,93 @@ -# Compiled Object files -*.slo -*.lo -*.o +# Byte-compiled / optimized / DLL files +__pycache__/ +*.py[cod] +*$py.class -# Compiled Dynamic libraries +# C extensions *.so -*.dylib - -# Compiled Static libraries -*.lai -*.la -*.a.sconf_temp - -# SCons build files -*.pyc -.gdb_history -.sconf_temp/ -.sconsign.dblite -build -config.log + +# Distribution / packaging +.Python +env/ +build/ +_build/ +develop-eggs/ +dist/ +downloads/ +eggs/ +.eggs/ +lib/ +lib64/ +parts/ +sdist/ +var/ +venv/ +*.egg-info/ +.installed.cfg +*.egg +bin/ +temp/ +tags/ errors.err -/sconscript.local -/sconsvars.py -tags -# eclipse +# PyInstaller +# Usually these files are written by a python script from a template +# before PyInstaller builds the exe, so as to inject date/other infos into it. +*.manifest +*.spec + +# Installer logs +pip-log.txt +pip-delete-this-directory.txt +MANIFEST + +# Unit test / coverage reports +htmlcov/ +.tox/ +.coverage +.coverage.* +.cache +nosetests.xml +coverage.xml +*,cover +.hypothesis/ + +# Translations +*.mo +*.pot + +# Mr Developer +.mr.developer.cfg .project .pydevproject -# source distribution tarball -libdiffpy-*.tar.gz +# Django stuff: +*.log + +# Sphinx documentation +docs/build/ +docs/source/generated/ + +# pytest +.pytest_cache/ + +# PyBuilder +target/ + +# Editor files +# mac +.DS_Store +*~ + +# vim +*.swp +*.swo + +# pycharm +.idea/ + +# VSCode +.vscode/ + +# Ipython Notebook +.ipynb_checkpoints diff --git a/.isort.cfg b/.isort.cfg new file mode 100644 index 00000000..86f162b8 --- /dev/null +++ b/.isort.cfg @@ -0,0 +1,5 @@ +[settings] +# Keep import statement below line_length character limit +line_length = 79 +multi_line_output = 3 +include_trailing_comma = True diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml new file mode 100644 index 00000000..a9fa4c7a --- /dev/null +++ b/.pre-commit-config.yaml @@ -0,0 +1,66 @@ +default_language_version: + python: python3.13 +ci: + autofix_commit_msg: | + [pre-commit.ci] auto fixes from pre-commit hooks + autofix_prs: true + autoupdate_branch: "pre-commit-autoupdate" + autoupdate_commit_msg: "[pre-commit.ci] pre-commit autoupdate" + autoupdate_schedule: monthly + skip: [no-commit-to-branch] + submodules: false +repos: + - repo: https://github.com/pre-commit/pre-commit-hooks + rev: v6.0.0 + hooks: + - id: check-yaml + - id: end-of-file-fixer + - id: trailing-whitespace + - id: check-case-conflict + - id: check-merge-conflict + - id: check-toml + - id: check-added-large-files + - repo: https://github.com/psf/black + rev: 26.3.1 + hooks: + - id: black + - repo: https://github.com/pycqa/flake8 + rev: 7.3.0 + hooks: + - id: flake8 + - repo: https://github.com/pycqa/isort + rev: 9.0.0a3 + hooks: + - id: isort + args: ["--profile", "black"] + - repo: https://github.com/kynan/nbstripout + rev: 0.9.1 + hooks: + - id: nbstripout + - repo: https://github.com/pre-commit/pre-commit-hooks + rev: v6.0.0 + hooks: + - id: no-commit-to-branch + name: Prevent Commit to Main Branch + args: ["--branch", "main"] + stages: [pre-commit] + - repo: https://github.com/codespell-project/codespell + rev: v2.4.2 + hooks: + - id: codespell + additional_dependencies: + - tomli + # prettier - multi formatter for .json, .yml, and .md files + - repo: https://github.com/pre-commit/mirrors-prettier + rev: v4.0.0-alpha.8 + hooks: + - id: prettier + additional_dependencies: + - "prettier@^3.2.4" + # docformatter - PEP 257 compliant docstring formatter + - repo: https://github.com/PyCQA/docformatter + rev: v1.7.8 + hooks: + - id: docformatter + additional_dependencies: [tomli] + args: [--in-place, --config, ./pyproject.toml] diff --git a/.readthedocs.yaml b/.readthedocs.yaml new file mode 100644 index 00000000..aaa88895 --- /dev/null +++ b/.readthedocs.yaml @@ -0,0 +1,13 @@ +version: 2 + +build: + os: "ubuntu-22.04" + tools: + python: "latest" + +python: + install: + - requirements: requirements/docs.txt + +sphinx: + configuration: docs/source/conf.py diff --git a/.sconf_temp/conftest_383658db2a6c0cca38b6f776638a25f9_0.cpp b/.sconf_temp/conftest_383658db2a6c0cca38b6f776638a25f9_0.cpp new file mode 100644 index 00000000..303968b4 --- /dev/null +++ b/.sconf_temp/conftest_383658db2a6c0cca38b6f776638a25f9_0.cpp @@ -0,0 +1,5 @@ +#include +#if BOOST_VERSION < 105600 +#error Installed boost is too old! +#endif +int main() { return 0; } diff --git a/.sconf_temp/conftest_383658db2a6c0cca38b6f776638a25f9_0_9bc8887b698aff96a593dc32c66c9ad1.o b/.sconf_temp/conftest_383658db2a6c0cca38b6f776638a25f9_0_9bc8887b698aff96a593dc32c66c9ad1.o new file mode 100644 index 0000000000000000000000000000000000000000..cad0f2929cb5ecd7cbd55624a57a7b4c67745c6e GIT binary patch literal 608 zcmX^A>+L^w1_nlE1|R{%EI_;i#83cYAdm!N3lJX%$bgF9fU1GtHx zttf%8A^Z^6h!6xH#^Qi7K<0tm0tYM%4RAJ)5g(tNpIeZaToPZJSDu-d5+Cp5f-Da+ zM*%AM0B9b_Ee=c&e*&3676Qb_r)I>b6(#1TLd4?ZQQfBjRq6oK0@Mxj8%T(ofdS@q zunHp}DFMWgVBu$AU+L^w1_nlE1|R{%EI_;i#83cYAdm!N3lJX%$bgF9fU1GtHx zttf%8A^Z^6h!6xH#^Qi7K<0tm0tYM%4RAJ)5g(tNpIeZaToPZJSDu-d5+Cp5f-Da+ zM*%AM0B9b_Ee=c&e*&3676Qb_r)I>b6(#1TLd4?ZQQfBjRq6oK0@Mxj8%T(ofdS@q zunHp}DFMWgVBu$AUv>=uNDKG> zR&&~ST)zl|3(Nq8ey>|-1@%H>uhjsjTmx9g>HNv-Z{Ld_4?ZowEj{{hvGUGi^Sgo93SN3q& zFsLIUuDggeiDyNQB=K7d3;EJ0g6}J5{8#Y#IZ5wG{A8~e`R)9@Q4suZ0PLrU2fw{r zhVy*>r97>@IVydA{)Js!H;L0LXT%v+uoC=UDHZ)p9yHIOuUUO6+8!TO;$k&B0XqRZ z0XqRZ0XqRZ0XqRZ0Xu>JZ~`uR;vI>gq%Q(v*Rj|^J1M!ul^2dl8Q>qngladHzw2-H uBVRRruiDppezMA5^ZL@g%$;UVB<6+>DTTeO8Dlp)DZnt@~!vYXm?>ZM>$ z>VM+hi{Q$j_wCzVHQ+|7%Vhkr!O^93s9MrCUw?_%QT@_Qn|$FNOh zZ4@aZwHL*9jPv=9P&@OTNL+|#Qg?#=ER=E(Jk^0Ob>HqXdT z`FgPW@P6g)>Zn)rlj}3-?{yN$GjpEcn?mdx7d*pThN`b?ZT+kk z>26R&fx3=}xb6k4X*?@(L>k|ipD!+sBlx~@&cBM!&q;en;>W`e(w~|JktlaXTi&yxmB|tUv=tYEz5vqz%pPNunbrR zECZGS%YbFzKV-l~O`MUalo-A5T<0OCDop43?e043e!(t nob1$eAWmbZfCGi|MZt-O8ouYe!ST#N>|z4{J&iPJa4dcSh-!%o literal 0 HcmV?d00001 diff --git a/.sconf_temp/conftest_f043931bac80a040e9dbafdbb5991c64_0.cpp b/.sconf_temp/conftest_f043931bac80a040e9dbafdbb5991c64_0.cpp new file mode 100644 index 00000000..65fb0cf4 --- /dev/null +++ b/.sconf_temp/conftest_f043931bac80a040e9dbafdbb5991c64_0.cpp @@ -0,0 +1,8 @@ + + +#include "ObjCryst/ObjCryst/Crystal.h" + +int main(void) { + +return 0; +} diff --git a/.sconf_temp/conftest_f043931bac80a040e9dbafdbb5991c64_0.o b/.sconf_temp/conftest_f043931bac80a040e9dbafdbb5991c64_0.o new file mode 100644 index 0000000000000000000000000000000000000000..3b4f37f1a9da9d2db2738d8c858a8abc7fee627f GIT binary patch literal 19944 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 literal 0 HcmV?d00001 diff --git a/.sconf_temp/conftest_f043931bac80a040e9dbafdbb5991c64_0_4168d21565c3518ee97cd47e0a4b72d9 b/.sconf_temp/conftest_f043931bac80a040e9dbafdbb5991c64_0_4168d21565c3518ee97cd47e0a4b72d9 new file mode 100755 index 0000000000000000000000000000000000000000..8dfedf30c0aba7297e072ba6b709114f85a85618 GIT binary patch literal 26112 zcmeHQdw5jUwcj(zLY_PnG=U6EB$7ZtFpA@3GD)VL%%n3X6)e5x zR#4Na_0igDX)V`2ta|IUPq7aTf=Tev#$Hrxtw51>YOs3mwOsm;+~3-JpEGCnNg(ij z_mBJCIp4Rk&pK=E-(Gv|efHUV?VaRTe|zU-E@QSV#+V&674)8ivF&Uo?il+B$P4oM znrc^iT0CnT>1q09OJ~RA25U$nhF9@GG< z;w^FuJl!VZ$?y7gu6`p^pRXej@C7#qg58==^S7#aOWXplOo9+^##7*6Pav+vLXi#w zKfOJ|7YMvtBna{5OU3s2dLlQ4B5l4>uP<= z8&@}r-h%$X`$@6X*87X(ss9p@4n2b3qNZ*Fer&uG926OVNBQ-O+Otq+kfz&Z64WSm znM8DJwu^Y5uQi_PZN!sZB#~E*z|;Ga{#Z|Sb9J8V67Oe2{pwYox_bTHYLa%LUi_+& z>1>e_jv1?pv0m~Ut6^-jld($4Z*JhO;cZp|@eueMk)CK~EN=>9lW>os_=Q)65mEcj z1YHRl?;2Xm7@#ay5uUg~FoDhQiO1%LL#^}MwuIZj({&z*?0Ed>^yu;KD^Gr9Rbb_1 z@2qdGz%w^U)X9ZG!^Q7;n>y~wz63H}KcsYBjQSCd8-t(uqH=3MBs2HZnpn4A>m+Mh zqtUqLiw9#Ne>ilrUkgPe^I%}86*+|6i;#|=zp?e>%VS&OnkkdaM@^N=M>VmrRJnn< zbLW*CnaIxca#muPw55CogPKuFuxs8jBNOsa^OGOG3+KC$&XGK|>;J~(O*m@jqKl5^ zX|_GbRR0*|)+eTWxBpfv_Vyi2M$wNcO(3lwrP)8+A4FIp~IFeqgH3qx?tE_GRPx3Ac{eP7S&XV9})av#UrGvGNe)w@e?ac`gJ_4Yx};kk1MJV%)K z&YlBQWQ0@`j&CBLx3A>5=YUPi_a;7*Ox{9*Tlc!TBkKDGOrkbv?)69;`u;mP9cilm z$qitWm(erszuFD8*VL|SmNq|xs;AlP+WtOF7IsfYCU4&!*z+N>Cs8)w8Jbf6%zAv& z+{=UEhQ812Lk(UI!}|3@Wc{Nbh6Rbzzg&cN>zg*~xM#?w6~g=j$$m)c6VqnZ_q{2w ze~bsf2BOp?-|=J%DJ2|Bb#DNUym27;NeBc^m%yn{RLug` zbh632D;##$MmoYl_;?szH|#4Ac)x>v+`9(eOcHSKUSE4dt#8ww5&H>|QvF#t7k&Ca@NSv=^bVQ*Ny?sB`f+kmVusP>w@UH-TwHoN7f+jI z$gh|3%}C`dQ9qbVov?l|8(&ezuB(-%x3|wr>z^=*TDt_s?x%{Id#G#sV;EV4Cokkh zP-~aMcHY{<)HZ$S1!$X>2Rw(#k;CMK-RJOHWK$*kW*qkn+cYN{=1}qg1gMwM%)bQB zn|XM;ch}lrJR0#wG&IZ0-d&ozIpEi{dEgEVcn;~<)b6d6E8z%|9rP9ep4$CTatj2) z+8zO1pIA8H89vv$YhAFbI~enG>_N`~ttS>7@VudeCX1ntR+-BUo^CSuA_&k$jDz{SOMh47Rdb&m?57BFMI$yYa<~JN|3M<-~p4J zgc~IFgyTS|TKTOcxOHy{h5+70b4Id_ZfbaF1261gvJokD)5%CN7>Mx-urCvAdVgJy z;h+hk1?WfKM2XSok{==q4koh+DzWr6h|OJgpA;WOikE(vi%nhj5h;I;%P(apxIVFo z8Ss5l{%tO=PV)SzkHE*J_%=!tjt4G8GDsaAJwC1D*AIH+;~Yxip*(sQHVH4aZJ={B)t|G zJm|GJklYP{`L%cwycqqc>&E<%c$jzHeS@B1nnMhD_UoY3CDwA>(pNZc+PK=uflcG; zCIfjk-rd>g%`~o}r$sv48)Z*}t$cX>`(&z>;|qXI9}cGgM7(B20!|LE1o=nwh7{zr zCdlMt0&j%{?^y^0-qWPgx5tF{SQ=i78SgrQcXFm`U(CRZ=y(_ghTzM_SST3L#`9$| zUm$L?Ai532X=cO{@2=%htYu_qFlqPI@+Nz9+n`N!lmxSv)Wr zeOWZp))0(z2-x8ctuwAP`X*9OIKFGKRf9lyvs)laZ=(8yN}oO*H5@dfCJO}QQVZlv z1M>VdNE-he@fIlG=Hzb7sbSkx3(x@wgl+ptC9S1DEP>D&{Tdm@hiB@H$xjNrS#HXM z0pIxQ-eKTvv*7&}eN*;C6>p}%djkuQbi73dUX=y!8{^}>jFkxL`y~rr5&}`*w@8KR zOFa?aj>bTsr`sP1P*cz>X^YWiwh6Fzr>n;8AVJzJq-5mWk*On7Mv6y@Mw}xBBY7j{ zLErFFD=+(CvIQv(`qPlQX4H_$`^e<6$NSl2fN8uxfh8`D_frWi;pkAruS@Y?C`~vv zD&n6?@vA~ytB7|<@smP)i6Y)A#rF&Gk@M3{+a$$Xgm}LqzCwyygt$impCQGU3-J|- zn3ncb?`cAOr6N9z)fI_9K#DiL7iH6HZG6XeOZo4({L)S?H+|>+MaqA{<<(7Gp8C#x zR*E0w;-#-o<6jB`Js`!mP?~VWAeNs&aC_6Zfi$i^jr(aDw=IpkEsa}5z7-!@lrSZY zo0a19X{Yk_mEUINBdpIq_d>JVilMC1b&FuYdh}ndSSwK zJ68Smi3J0meoQIlx0q&VyS=**$#U2EH5#jjFoUBvuAiE23NoYVx+o`Km-LGX<#6(1 zqkvhW0D+8gAIRaLdj(cS5Qbic z4`@GLUDxCq7jD19Z`nZ240Ew@6R-FPGl{Jc@%t1v57pf*N<7`dpxh$yfKx9jx+ zr<)73TML|F6nLIdU{_j!Sz{C^Yj}RDhFN-nX*IkDGj^qhkK>W3;iI?{E#$QY9yZS}7LkGZ9Oo z57AS|MH947BOPNxXLPN52p#%;Qhyav*9;!-6r8i2s4mIEhPy_=qC@ofQ9-(uCuEMf|iB|3--4QN-Vn;-3le z$`2z&?-TOW-0nM4{45tQJ&FZ}If8P#6n~S8OaFsk;l+yk$S?> z$QSZ>XN?&81IU-7EA~K~E|?FBEo21^?R|=(3c;BSPWRQlaS`t}rL1aEDO1PAdG{ z`&^+UrO+W1E|&@qp5zL-h63#!{An4KMoZs@b4E*@SnQ9M7V)3O{O1MyX9NFv4gYxu z|M?Kt-^+gv@SjicpZDS?_K+|S7#+1a*;R~P<=o7!spI^GY$IbEosZjDTPY* z&VFQr?1atvsGV)IH;^r(w#94>V{4p8v)HC4#=d8B9m!%px2<68vv%j20=MVDPFY-{?lX=c3^4aI|TXh?c*;qGY z-A=6Ww*q#L&G|qP`<1PRvAgWfuN1Kz_RAT2)$Y2lh#j?G#@IKqoL?+rk7WVtSeEmi zB6fnzeLCBDR}p)jY`o9my0eIV%TdnQD~^J{6|&FgxUhoXku#UEft-Tlh3tbI*ZX*! ziw95U7W`)+`*W`AJv^od-_0xdb0IsJ=Q@VRc~`<|`Odcs*;Dx_WHjHIEM$oS4`agx z&Oa2gHwsYR!;_qE6|x^rYG&*|Q5q`*)}F5OAFE^}~g(Hw)QM3U9ZGI`1g7 zIi?pp>##|l`Uje9wR0QHqw1@$A|$p0zMN=twwvxR54P zOb_xQxX16su2h`Wg*t<6F~8iUio1cJbI)<+jG!KD4?@!Bj>?cKD&%ep@pTz= z$CkIp{FtPUEB1GFheKLVTX3B0!EiX#9gl|Em_8*ZC^?ztxcWR8&({Xq!?Z}m#1(x( zPGOlo<&3m3eXrjld5ZnqH9at}H=|?&YaW{- zub2xL_tLzS_-vUXgvxnLg?Wl;E>E#Tz*9_jd5Q=#PbbL~(_hYGqm`%FXyz%l;(3Zt z15YQ*6yYe&BQV5MOq+R%s3%VmIp!(i-aJK!o~PKi;OR$YifK0IX}(YOz&0jNX}^Sc zF^w`zlrVC`cP^Pqgp?r&FiZ}x25nABs5}6{H!TBqAN=uB)mGMBv z0~rrwJdp7~#se7-WIT}ZK*j?Z4`e*>|IGt#RJaM$fFJ<&g_(#2;0Bji_ltY6);8T2 zBN`wUu>2wAvm=0D$H52t9X4j)3(B)IdlTqupg(}hvY7p5&;V#kHnX>ao&edgv9%WT z5a@l-iX3LYAM`e;CYRa21WJOI4v`6;uVwWjeV4nu4BM4W{urIvU0&{KTGd?UTf1s? zvsUTzm6unv`s1Mh{`dg>E`EJrb#o;i20HyQpBD3nw0M2M zS;i^g%g2H*KLhx>a?huf!WVXQr>e-e=~eRFM*b0vXEiS9H;_-cb3!p_$oRU!Wsw{f)nYsDqcHL#THeu zdJJEWhfZc#s$|yG`zt>hqiQZNYZ~1VH{swZRdrBt z07q3_G$txpio+ulSH^jbi7CrdE2me+yR9DuQj{yletlDCLQbh~crJCY zCN<$K`~=mQHL;drs);yObox?ES;UE>(`QLs`LLb(~!Kj`H26qU4@jEsS$fAjWV3LFb zey!mk>Nz$}b=gF4q%oK_<+*Q_npg0U`sB9SJ1zQAU`uQk{aiuf=M z4Rq3@j?J5W_|psYryX#(Z;S6!Wq7(?{2eF^$vs^`JtK}aZVqYSTH3-*eEt}@sSC>l zEHZqI@Ac7!V;0+qeL=ggs}q0eDj4X|g1*k6zYXU#^Sk_^2>rI^A!ki7uA1VVw&G#? zPTSqtz4rI=?{@ST4Lf#X;ZcVT$LY3SepZjxA$LfACzdG04@v$B$-A&IN&5YgZ<745 zBJtSFr8A1ezbW|! z$-gIgI0XBUY-SMqd*k@z{1r?ZU2FOfW*X(Yapc-kDU5&Q#^rvr{8KPY*- zP2)Eeijj!tYV|q{0`$5qqi;U7}?cLa}KBCeAPpCiUWLtZF&w7R&?Q{?j$ekRl|09^>0 z1-b}yDQG^3&P=p`t^?5-?oFT@KxX|?@Uuab?_$s#&?TU`AR0ReYaWR3%0T5HI;%nF zH0X>5ozI|h=v)S!$)NKXbQXioVbB>2I)6cDFDSlN1F8ir11$&DfjpoUpp_sms2+3` zXceddbTw!-s1eizS_4`O!tZ9Vb)fa2Yd{-78$s8CsLoVRABg-z_11mh#y8pex%KhS zt@T@;32UWA_R{9snXuNWBX`!dZzilI{sLr$b>S)kW!7j>CafhR>Bb5bH<_>&|Ldcf zu$JlblnHAYn@+~7qQ`3aACyp{F_qRaC_P4%32T`mgNUPL!diNW?*DXHD=i)+qg+O? zYQixu)4)@8#-Tv#j_R4kHLdxp&nEb1g{eBT(5MyQY{HpVl&Uic6pxYpj2mQm1>ZyU z1;YMF2cn(fF#g$q424n%GqtyWW(_5A&pLmdu*;PHtQy*C##zBwtvSQ~VzuK80jw6B zO^36ZaW*JcTh6H4N+VV_G%l-c!2hD?Y4z3n0{Hu*_;Uc7uf43Qa&cvOt3R-)%wJYj z7F^uc>ThprZLMCsxID0+%2zfo(A~{eE?;i?4-w(uLd%oImM7)q)<>1rN7dFx3#^Y8 zroSBx)e9!F31R+zpw8Il396gl4b>GBLDF}6#zk2?5tP*S3f~kN7i>bqk_5v})_66W Z(D0P9iDn_=Vohk=mw}2D2*N?^e*hDt#qIzA literal 0 HcmV?d00001 diff --git a/.sconf_temp/conftest_f043931bac80a040e9dbafdbb5991c64_0_4eeef59bb4603aa6f185d0625b6d5bda b/.sconf_temp/conftest_f043931bac80a040e9dbafdbb5991c64_0_4eeef59bb4603aa6f185d0625b6d5bda new file mode 100755 index 0000000000000000000000000000000000000000..1b10a2d444060bff29440afbd0a8ea7975fcb70a GIT binary patch literal 26112 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 literal 0 HcmV?d00001 diff --git a/.sconsign.dblite b/.sconsign.dblite new file mode 100644 index 0000000000000000000000000000000000000000..0fd0c4022014516f2b77904f31a2215430674ed9 GIT binary patch literal 1342638 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
rO=S+3{{}| ziExz1U!%zm2%L?6lf;xlRrml2zi)m%c6;W)aYnd_LqMv|q;||6`5N0!5=&VZ%Ym<%q%HweZPl zLP7+N{Hvq?nTB%$uh9+QE0pxK1~jrrPGIiHatQl{26f8PeV0b&3H;!(alr!_Xcs;q z98ZorNQmvi4YgGZ`$Emq(F+>?B%xsdt_&KAGXMYt$1ri|7q6cYLUd zM^wPoxQ(Kq(E-{8lnbi^cFUh|&ed=KsK%ovP=po(v@17F$Sr-s&63_rm@K-5sVDAw z&9}qD6=Q~1j8}$x=pwot^CTw4Kw?Y{1LqnWhdCvT^biT<0nv9$v z{VF;u|2sFVPo6v|CxgU8>-j3YcBxh(^qn9QW|?O&mB3mq;X$=N4@a3qH^UVvh* zVxjq0%mp75@GMp3H);+nTw<~a0r)nATw0(bIm9Eh;*%s5gg52!f2>hfxZOm1SO`!` zG$nKyj2v(?SO7Wv4kh(avvh>o=w#9@JfeZV%g9%9nm{9T~ur{Pf9q3zG`MWgx4mA%C z5|4yzuz>O$bP;3GHxyqa>gd0wdz`LcHZ|x(p>mZAzA-Fra;dnb>|zHk^fY2qlJLbq zixUuHX;Q)0J=kSDKB3xr!f; zRbrp8N;t>L=&ojTz$I7@ z;$47Sz={NEBNG}BLX#IJL{uLCVa>O*+JwDwg(0CYISI6MDNdcxC^-D;#=ThcIEhQC z2}4QH4jf+r7fBjH(uWaFK4lQf=(

e3rX`2wxzJNg5u0AhHG{5)hJaS4Y20v+rWp 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

|u1x4& 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

?J-Lu!~rBPSf8Pj7qLyW_nmp;n5r=!X9VbR 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%0A%v{a4i&2kInH7Ykux%Z)L63xdwR#3eWmAq2D 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%0AsHka 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 N&7~Zne%8%f{}`_. + +Community Impact Guidelines were inspired by `Mozilla's code of conduct enforcement ladder `_. + +For answers to common questions about this code of conduct, see the `FAQ `_. `Translations are available `_ diff --git a/LICENSE.rst b/LICENSE.rst new file mode 100644 index 00000000..42df4af5 --- /dev/null +++ b/LICENSE.rst @@ -0,0 +1,29 @@ +BSD 3-Clause License + +Copyright (c) 2026, libdiffpy contributors. +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +1. Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + +2. Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + +3. Neither the name of the copyright holder nor the names of its contributors + may be used to endorse or promote products derived from this software + without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/MANIFEST.in b/MANIFEST.in new file mode 100644 index 00000000..f1a78eec --- /dev/null +++ b/MANIFEST.in @@ -0,0 +1,12 @@ +graft src +graft tests +graft requirements + +include AUTHORS.rst LICENSE*.rst README.rst + +# Exclude all bytecode files and __pycache__ directories +global-exclude *.py[cod] # Exclude all .pyc, .pyo, and .pyd files. +global-exclude .DS_Store # Exclude Mac filesystem artifacts. +global-exclude __pycache__ # Exclude Python cache directories. +global-exclude .git* # Exclude git files and directories. +global-exclude .idea # Exclude PyCharm project settings. diff --git a/README.rst b/README.rst new file mode 100644 index 00000000..be78cb85 --- /dev/null +++ b/README.rst @@ -0,0 +1,137 @@ +|Icon| |title|_ +=============== + +.. |title| replace:: libdiffpy +.. _title: https://diffpy.github.io/libdiffpy + +.. |Icon| image:: https://avatars.githubusercontent.com/diffpy + :target: https://diffpy.github.io/libdiffpy + :height: 100px + +|PyPI| |Forge| |PythonVersion| |PR| + +|CI| |Codecov| |Black| |Tracking| + +.. |Black| image:: https://img.shields.io/badge/code_style-black-black + :target: https://github.com/psf/black + +.. |CI| image:: https://github.com/diffpy/libdiffpy/actions/workflows/matrix-and-codecov-on-merge-to-main.yml/badge.svg + :target: https://github.com/diffpy/libdiffpy/actions/workflows/matrix-and-codecov-on-merge-to-main.yml + +.. |Codecov| image:: https://codecov.io/gh/diffpy/libdiffpy/branch/main/graph/badge.svg + :target: https://codecov.io/gh/diffpy/libdiffpy + +.. |Forge| image:: https://img.shields.io/conda/vn/conda-forge/libdiffpy + :target: https://anaconda.org/conda-forge/libdiffpy + +.. |PR| image:: https://img.shields.io/badge/PR-Welcome-29ab47ff + :target: https://github.com/diffpy/libdiffpy/pulls + +.. |PyPI| image:: https://img.shields.io/pypi/v/libdiffpy + :target: https://pypi.org/project/libdiffpy/ + +.. |PythonVersion| image:: https://img.shields.io/pypi/pyversions/libdiffpy + :target: https://pypi.org/project/libdiffpy/ + +.. |Tracking| image:: https://img.shields.io/badge/issue_tracking-github-blue + :target: https://github.com/diffpy/libdiffpy/issues + +DiffPy C++ library for calculation of PDF and other real-space quantities + +* LONGER DESCRIPTION HERE + +For more information about the libdiffpy library, please consult our `online documentation `_. + +Citation +-------- + +If you use libdiffpy in a scientific publication, we would like you to cite this package as + + libdiffpy Package, https://github.com/diffpy/libdiffpy + +Installation +------------ + +The preferred method is to use `Miniconda Python +`_ +and install from the "conda-forge" channel of Conda packages. + +To add "conda-forge" to the conda channels, run the following in a terminal. :: + + conda config --add channels conda-forge + +We want to install our packages in a suitable conda environment. +The following creates and activates a new environment named ``libdiffpy_env`` :: + + conda create -n libdiffpy_env libdiffpy + conda activate libdiffpy_env + +The output should print the latest version displayed on the badges above. + +If the above does not work, you can use ``pip`` to download and install the latest release from +`Python Package Index `_. +To install using ``pip`` into your ``libdiffpy_env`` environment, type :: + + pip install libdiffpy + +If you prefer to install from sources, after installing the dependencies, obtain the source archive from +`GitHub `_. Once installed, ``cd`` into your ``libdiffpy`` directory +and run the following :: + + pip install . + +This package also provides command-line utilities. To check the software has been installed correctly, type :: + + libdiffpy --version + +You can also type the following command to verify the installation. :: + + python -c "import libdiffpy; print(libdiffpy.__version__)" + + +To view the basic usage and available commands, type :: + + libdiffpy -h + +Getting Started +--------------- + +You may consult our `online documentation `_ for tutorials and API references. + +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. To install libdiffpy +in a development mode, with its sources being directly used by Python +rather than copied to a package directory, use the following in the root +directory :: + + pip install -e . + +To ensure code quality and to prevent accidental commits into the default branch, please set up the use of our pre-commit +hooks. + +1. Install pre-commit in your working environment by running ``conda install pre-commit``. + +2. Initialize pre-commit (one time only) ``pre-commit install``. + +Thereafter your code will be linted by black and isort and checked against flake8 before you can commit. +If it fails by black or isort, just rerun and it should pass (black and isort will modify the files so should +pass after they are modified). If the flake8 test fails please see the error messages and fix them manually before +trying to commit again. + +Improvements and fixes are always appreciated. + +Before contributing, please read our `Code of Conduct `_. + +Contact +------- + +For more information on libdiffpy please visit the project `web-page `_ or email the maintainers ``Simon Billinge(sbillinge@ucsb.edu)``. + +Acknowledgements +---------------- + +``libdiffpy`` is built and maintained with `scikit-package `_. diff --git a/cookiecutter.json b/cookiecutter.json new file mode 100644 index 00000000..1158d18a --- /dev/null +++ b/cookiecutter.json @@ -0,0 +1,20 @@ +{ + "author_names": "Simon Billinge", + "author_emails": "sbillinge@ucsb.edu", + "maintainer_names": "Simon Billinge", + "maintainer_emails": "sbillinge@ucsb.edu", + "maintainer_github_usernames": "sbillinge", + "contributors": "Pavol Juhas, Chris Farrow, Simon Billinge, Billinge Group members", + "project_name": "libdiffpy", + "license_holders": "libdiffpy contributors", + "github_username_or_orgname": "diffpy", + "github_repo_name": "libdiffpy", + "conda_pypi_package_dist_name": "libdiffpy", + "package_dir_name": "libdiffpy", + "project_short_description": "DiffPy C++ library for calculation of PDF and other real-space quantities", + "project_keywords": "diffraction, PDF, X-ray, neutron", + "minimum_supported_python_version": "3.12", + "maximum_supported_python_version": "3.14", + "project_needs_c_code_compiled": "Yes", + "project_has_gui_tests": "No" +} diff --git a/docs/Makefile b/docs/Makefile new file mode 100644 index 00000000..8234d806 --- /dev/null +++ b/docs/Makefile @@ -0,0 +1,194 @@ +# Makefile for Sphinx documentation +# + +# You can set these variables from the command line. +SPHINXOPTS = +SPHINXBUILD = sphinx-build +PAPER = +BUILDDIR = build +BASENAME = $(subst .,,$(subst $() $(),,libdiffpy)) + +# User-friendly check for sphinx-build +ifeq ($(shell which $(SPHINXBUILD) >/dev/null 2>&1; echo $$?), 1) +$(error The '$(SPHINXBUILD)' command was not found. Make sure you have Sphinx installed, then set the SPHINXBUILD environment variable to point to the full path of the '$(SPHINXBUILD)' executable. Alternatively you can add the directory with the executable to your PATH. If you don't have Sphinx installed, grab it from http://sphinx-doc.org/) +endif + +# Internal variables. +PAPEROPT_a4 = -D latex_paper_size=a4 +PAPEROPT_letter = -D latex_paper_size=letter +ALLSPHINXOPTS = -d $(BUILDDIR)/doctrees $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) source +# the i18n builder cannot share the environment and doctrees with the others +I18NSPHINXOPTS = $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) source + +.PHONY: help clean html dirhtml singlehtml pickle json htmlhelp qthelp devhelp epub latex latexpdf text man changes linkcheck doctest gettext + +help: + @echo "Please use \`make ' where is one of" + @echo " html to make standalone HTML files" + @echo " dirhtml to make HTML files named index.html in directories" + @echo " singlehtml to make a single large HTML file" + @echo " pickle to make pickle files" + @echo " json to make JSON files" + @echo " htmlhelp to make HTML files and a HTML help project" + @echo " qthelp to make HTML files and a qthelp project" + @echo " devhelp to make HTML files and a Devhelp project" + @echo " epub to make an epub" + @echo " latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter" + @echo " latexpdf to make LaTeX files and run them through pdflatex" + @echo " latexpdfja to make LaTeX files and run them through platex/dvipdfmx" + @echo " text to make text files" + @echo " man to make manual pages" + @echo " texinfo to make Texinfo files" + @echo " info to make Texinfo files and run them through makeinfo" + @echo " gettext to make PO message catalogs" + @echo " changes to make an overview of all changed/added/deprecated items" + @echo " xml to make Docutils-native XML files" + @echo " pseudoxml to make pseudoxml-XML files for display purposes" + @echo " linkcheck to check all external links for integrity" + @echo " doctest to run all doctests embedded in the documentation (if enabled)" + +clean: + rm -rf $(BUILDDIR)/* + +html: + $(SPHINXBUILD) -b html $(ALLSPHINXOPTS) $(BUILDDIR)/html + @echo + @echo "Build finished. The HTML pages are in $(BUILDDIR)/html." + +dirhtml: + $(SPHINXBUILD) -b dirhtml $(ALLSPHINXOPTS) $(BUILDDIR)/dirhtml + @echo + @echo "Build finished. The HTML pages are in $(BUILDDIR)/dirhtml." + +singlehtml: + $(SPHINXBUILD) -b singlehtml $(ALLSPHINXOPTS) $(BUILDDIR)/singlehtml + @echo + @echo "Build finished. The HTML page is in $(BUILDDIR)/singlehtml." + +pickle: + $(SPHINXBUILD) -b pickle $(ALLSPHINXOPTS) $(BUILDDIR)/pickle + @echo + @echo "Build finished; now you can process the pickle files." + +json: + $(SPHINXBUILD) -b json $(ALLSPHINXOPTS) $(BUILDDIR)/json + @echo + @echo "Build finished; now you can process the JSON files." + +htmlhelp: + $(SPHINXBUILD) -b htmlhelp $(ALLSPHINXOPTS) $(BUILDDIR)/htmlhelp + @echo + @echo "Build finished; now you can run HTML Help Workshop with the" \ + ".hhp project file in $(BUILDDIR)/htmlhelp." + +qthelp: + $(SPHINXBUILD) -b qthelp $(ALLSPHINXOPTS) $(BUILDDIR)/qthelp + @echo + @echo "Build finished; now you can run "qcollectiongenerator" with the" \ + ".qhcp project file in $(BUILDDIR)/qthelp, like this:" + @echo "# qcollectiongenerator $(BUILDDIR)/qthelp/$(BASENAME).qhcp" + @echo "To view the help file:" + @echo "# assistant -collectionFile $(BUILDDIR)/qthelp/$(BASENAME).qhc" + +devhelp: + $(SPHINXBUILD) -b devhelp $(ALLSPHINXOPTS) $(BUILDDIR)/devhelp + @echo + @echo "Build finished." + @echo "To view the help file:" + @echo "# mkdir -p $$HOME/.local/share/devhelp/$(BASENAME)" + @echo "# ln -s $(BUILDDIR)/devhelp $$HOME/.local/share/devhelp/$(BASENAME)" + @echo "# devhelp" + +epub: + $(SPHINXBUILD) -b epub $(ALLSPHINXOPTS) $(BUILDDIR)/epub + @echo + @echo "Build finished. The epub file is in $(BUILDDIR)/epub." + +latex: + $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex + @echo + @echo "Build finished; the LaTeX files are in $(BUILDDIR)/latex." + @echo "Run \`make' in that directory to run these through (pdf)latex" \ + "(use \`make latexpdf' here to do that automatically)." + +latexpdf: + $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex + @echo "Running LaTeX files through pdflatex..." + $(MAKE) -C $(BUILDDIR)/latex all-pdf + @echo "pdflatex finished; the PDF files are in $(BUILDDIR)/latex." + +latexpdfja: + $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex + @echo "Running LaTeX files through platex and dvipdfmx..." + $(MAKE) -C $(BUILDDIR)/latex all-pdf-ja + @echo "pdflatex finished; the PDF files are in $(BUILDDIR)/latex." + +text: + $(SPHINXBUILD) -b text $(ALLSPHINXOPTS) $(BUILDDIR)/text + @echo + @echo "Build finished. The text files are in $(BUILDDIR)/text." + +man: + $(SPHINXBUILD) -b man $(ALLSPHINXOPTS) $(BUILDDIR)/man + @echo + @echo "Build finished. The manual pages are in $(BUILDDIR)/man." + +texinfo: + $(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo + @echo + @echo "Build finished. The Texinfo files are in $(BUILDDIR)/texinfo." + @echo "Run \`make' in that directory to run these through makeinfo" \ + "(use \`make info' here to do that automatically)." + +info: + $(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo + @echo "Running Texinfo files through makeinfo..." + make -C $(BUILDDIR)/texinfo info + @echo "makeinfo finished; the Info files are in $(BUILDDIR)/texinfo." + +gettext: + $(SPHINXBUILD) -b gettext $(I18NSPHINXOPTS) $(BUILDDIR)/locale + @echo + @echo "Build finished. The message catalogs are in $(BUILDDIR)/locale." + +changes: + $(SPHINXBUILD) -b changes $(ALLSPHINXOPTS) $(BUILDDIR)/changes + @echo + @echo "The overview file is in $(BUILDDIR)/changes." + +linkcheck: + $(SPHINXBUILD) -b linkcheck $(ALLSPHINXOPTS) $(BUILDDIR)/linkcheck + @echo + @echo "Link check complete; look for any errors in the above output " \ + "or in $(BUILDDIR)/linkcheck/output.txt." + +doctest: + $(SPHINXBUILD) -b doctest $(ALLSPHINXOPTS) $(BUILDDIR)/doctest + @echo "Testing of doctests in the sources finished, look at the " \ + "results in $(BUILDDIR)/doctest/output.txt." + +# Manual publishing to the gh-pages branch + +GITREPOPATH = $(shell cd $(CURDIR) && git rev-parse --git-dir) +GITREMOTE = origin +GITREMOTEURL = $(shell git config --get remote.$(GITREMOTE).url) +GITLASTCOMMIT = $(shell git rev-parse --short HEAD) + +publish: + @test -d build/html || \ + ( echo >&2 "Run 'make html' first!"; false ) + git show-ref --verify --quiet refs/heads/gh-pages || \ + git branch --track gh-pages $(GITREMOTE)/gh-pages + test -d build/gh-pages || \ + git clone -s -b gh-pages $(GITREPOPATH) build/gh-pages + cd build/gh-pages && \ + git pull $(GITREMOTEURL) gh-pages + rsync -acv --delete --exclude=.git --exclude=.rsync-exclude \ + --exclude-from=build/gh-pages/.rsync-exclude \ + --link-dest=$(CURDIR)/build/html build/html/ build/gh-pages/ + cd build/gh-pages && \ + git add --all . && \ + git diff --cached --quiet || \ + git commit -m "Sync with the source at $(GITLASTCOMMIT)." + cd build/gh-pages && \ + git push origin gh-pages diff --git a/docs/make.bat b/docs/make.bat new file mode 100644 index 00000000..2be83069 --- /dev/null +++ b/docs/make.bat @@ -0,0 +1,36 @@ +@ECHO OFF + +pushd %~dp0 + +REM Command file for Sphinx documentation + +if "%SPHINXBUILD%" == "" ( + set SPHINXBUILD=sphinx-build +) +set SOURCEDIR=source +set BUILDDIR=build +set SPHINXPROJ=PackagingScientificPython + +if "%1" == "" goto help + +%SPHINXBUILD% >NUL 2>NUL +if errorlevel 9009 ( + echo. + echo.The 'sphinx-build' command was not found. Make sure you have Sphinx + echo.installed, then set the SPHINXBUILD environment variable to point + echo.to the full path of the 'sphinx-build' executable. Alternatively you + echo.may add the Sphinx directory to PATH. + echo. + echo.If you don't have Sphinx installed, grab it from + echo.http://sphinx-doc.org/ + exit /b 1 +) + +%SPHINXBUILD% -M %1 %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% +goto end + +:help +%SPHINXBUILD% -M help %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% + +:end +popd diff --git a/docs/source/_static/.placeholder b/docs/source/_static/.placeholder new file mode 100644 index 00000000..e69de29b diff --git a/docs/source/api/libdiffpy.example_package.rst b/docs/source/api/libdiffpy.example_package.rst new file mode 100644 index 00000000..a15ce6af --- /dev/null +++ b/docs/source/api/libdiffpy.example_package.rst @@ -0,0 +1,31 @@ +.. _example_package documentation: + +|title| +======= + +.. |title| replace:: libdiffpy.example_package package + +.. automodule:: libdiffpy.example_package + :members: + :undoc-members: + :show-inheritance: + +|foo| +----- + +.. |foo| replace:: libdiffpy.example_package.foo module + +.. automodule:: libdiffpy.example_package.foo + :members: + :undoc-members: + :show-inheritance: + +|bar| +----- + +.. |bar| replace:: libdiffpy.example_package.bar module + +.. automodule:: libdiffpy.example_package.foo + :members: + :undoc-members: + :show-inheritance: diff --git a/docs/source/api/libdiffpy.rst b/docs/source/api/libdiffpy.rst new file mode 100644 index 00000000..ca658409 --- /dev/null +++ b/docs/source/api/libdiffpy.rst @@ -0,0 +1,30 @@ +:tocdepth: -1 + +|title| +======= + +.. |title| replace:: libdiffpy package + +.. automodule:: libdiffpy + :members: + :undoc-members: + :show-inheritance: + +Subpackages +----------- + +.. toctree:: + libdiffpy.example_package + +Submodules +---------- + +|module| +-------- + +.. |module| replace:: libdiffpy.example_submodule module + +.. automodule:: libdiffpy.example_submodule + :members: + :undoc-members: + :show-inheritance: diff --git a/docs/source/conf.py b/docs/source/conf.py new file mode 100644 index 00000000..051bfb53 --- /dev/null +++ b/docs/source/conf.py @@ -0,0 +1,322 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- +# +# libdiffpy documentation build configuration file, created by # noqa: E501 +# sphinx-quickstart on Thu Jan 30 15:49:41 2014. +# +# This file is execfile()d with the current directory set to its +# containing dir. +# +# Note that not all possible configuration values are present in this +# autogenerated file. +# +# All configuration values have a default; values that are commented out +# serve to show the default. + +import sys +import time +from importlib.metadata import version +from pathlib import Path + +# Attempt to import the version dynamically from GitHub tag. +try: + fullversion = version("libdiffpy") +except Exception: + fullversion = "No version found. The correct version will appear in the released version." # noqa: E501 + +# If extensions (or modules to document with autodoc) are in another directory, +# add these directories to sys.path here. If the directory is relative to the +# documentation root, use Path().resolve() to make it absolute, like shown here. # noqa: E501 +# sys.path.insert(0, str(Path(".").resolve())) +sys.path.insert(0, str(Path("../..").resolve())) +sys.path.insert(0, str(Path("../../src").resolve())) + +# abbreviations +ab_authors = "Pavol Juhas, Chris Farrow, Simon Billinge, Billinge Group members" + +# -- General configuration ------------------------------------------------ + +# If your documentation needs a minimal Sphinx version, state it here. +# needs_sphinx = '1.0' + +# Add any Sphinx extension module names here, as strings. They can be +# extensions coming with Sphinx (named 'sphinx.ext.*') or your custom +# ones. +extensions = [ + "sphinx.ext.autodoc", + "sphinx.ext.napoleon", + "sphinx.ext.todo", + "sphinx.ext.viewcode", + "sphinx.ext.intersphinx", + "sphinx_rtd_theme", + "sphinx_copybutton", + "m2r2", +] + +# Add any paths that contain templates here, relative to this directory. +templates_path = ["_templates"] + +# The suffix(es) of source filenames. +# You can specify multiple suffix as a list of string: +# +source_suffix = [".rst", ".md"] + +# The encoding of source files. +# source_encoding = 'utf-8-sig' + +# The master toctree document. +master_doc = "index" + +# General information about the project. +project = "libdiffpy" +copyright = "%Y, libdiffpy contributors" + +# The version info for the project you're documenting, acts as replacement for +# |version| and |release|, also used in various other places throughout the +# built documents. + +# The short X.Y version. +version = "".join(fullversion.split(".post")[:1]) +# The full version, including alpha/beta/rc tags. +release = fullversion + +# The language for content autogenerated by Sphinx. Refer to documentation +# for a list of supported languages. +# language = None + +# There are two options for replacing |today|: either, you set today to some +# non-false value, then it is used: +# today = '' +today = time.strftime("%B %d, %Y", time.localtime()) +year = today.split()[-1] +# Else, today_fmt is used as the format for a strftime call. +# today_fmt = '%B %d, %Y' +# substitute YEAR in the copyright string +copyright = copyright.replace("%Y", year) + +# For sphinx_copybutton extension. +# Do not copy "$" for shell commands in code-blocks. +copybutton_prompt_text = r"^\$ " +copybutton_prompt_is_regexp = True + +# List of patterns, relative to source directory, that match files and +# directories to ignore when looking for source files. +exclude_patterns = ["build"] + +# The reST default role (used for this markup: `text`) to use for all +# documents. +# default_role = None + +# If true, '()' will be appended to :func: etc. cross-reference text. +# add_function_parentheses = True + +# If true, the current module name will be prepended to all description +# unit titles (such as .. function::). +# add_module_names = True + +# If true, sectionauthor and moduleauthor directives will be shown in the +# output. They are ignored by default. +# show_authors = False + +# The name of the Pygments (syntax highlighting) style to use. +pygments_style = "sphinx" + +# A list of ignored prefixes for module index sorting. +modindex_common_prefix = ["libdiffpy"] + +# Display all warnings for missing links. +nitpicky = True + +# -- Options for HTML output ---------------------------------------------- + +# The theme to use for HTML and HTML Help pages. See the documentation for +# a list of builtin themes. +# +html_theme = "sphinx_rtd_theme" + +html_context = { + "display_github": True, + "github_user": "diffpy", + "github_repo": "libdiffpy", + "github_version": "main", + "conf_py_path": "/docs/source/", +} + +# Theme options are theme-specific and customize the look and feel of a theme +# further. For a list of options available for each theme, see the +# documentation. +# +html_theme_options = { + "navigation_with_keys": "true", +} + +# Add any paths that contain custom themes here, relative to this directory. +# html_theme_path = [] + +# The name for this set of Sphinx documents. If None, it defaults to +# " v documentation". +# html_title = None + +# A shorter title for the navigation bar. Default is the same as html_title. +# html_short_title = None + +# The name of an image file (relative to this directory) to place at the top +# of the sidebar. +# html_logo = None + +# The name of an image file (within the static path) to use as favicon of the +# docs. This file should be a Windows icon file (.ico) being 16x16 or 32x32 +# pixels large. +# html_favicon = None + +# Add any paths that contain custom static files (such as style sheets) here, +# relative to this directory. They are copied after the builtin static files, +# so a file named "default.css" will overwrite the builtin "default.css". +# html_static_path = ['_static'] + +# Add any extra paths that contain custom files (such as robots.txt or +# .htaccess) here, relative to this directory. These files are copied +# directly to the root of the documentation. +# html_extra_path = [] + +# If not '', a 'Last updated on:' timestamp is inserted at every page bottom, +# using the given strftime format. +# html_last_updated_fmt = '%b %d, %Y' + +# If true, SmartyPants will be used to convert quotes and dashes to +# typographically correct entities. +# html_use_smartypants = True + +# Custom sidebar templates, maps document names to template names. +# html_sidebars = {} + +# Additional templates that should be rendered to pages, maps page names to +# template names. +# html_additional_pages = {} + +# If false, no module index is generated. +# html_domain_indices = True + +# If false, no index is generated. +# html_use_index = True + +# If true, the index is split into individual pages for each letter. +# html_split_index = False + +# If true, links to the reST sources are added to the pages. +# html_show_sourcelink = True + +# If true, "Created using Sphinx" is shown in the HTML footer. Default is True. +# html_show_sphinx = True + +# If true, "(C) Copyright ..." is shown in the HTML footer. Default is True. +# html_show_copyright = True + +# If true, an OpenSearch description file will be output, and all pages will +# contain a tag referring to it. The value of this option must be the +# base URL from which the finished HTML is served. +# html_use_opensearch = '' + +# This is the file name suffix for HTML files (e.g. ".xhtml"). +# html_file_suffix = None + +# Output file base name for HTML help builder. +basename = "libdiffpy".replace(" ", "").replace(".", "") +htmlhelp_basename = basename + "doc" + + +# -- Options for LaTeX output --------------------------------------------- + +latex_elements = { + # The paper size ('letterpaper' or 'a4paper'). + # 'papersize': 'letterpaper', + # The font size ('10pt', '11pt' or '12pt'). + # 'pointsize': '10pt', + # Additional stuff for the LaTeX preamble. + # 'preamble': '', +} + +# Grouping the document tree into LaTeX files. List of tuples +# (source start file, target name, title, +# author, documentclass [howto, manual, or own class]). +latex_documents = [ + ( + "index", + "libdiffpy.tex", + "libdiffpy Documentation", + ab_authors, + "manual", + ), +] + +# The name of an image file (relative to this directory) to place at the top of +# the title page. +# latex_logo = None + +# For "manual" documents, if this is true, then toplevel headings are parts, +# not chapters. +# latex_use_parts = False + +# If true, show page references after internal links. +# latex_show_pagerefs = False + +# If true, show URL addresses after external links. +# latex_show_urls = False + +# Documents to append as an appendix to all manuals. +# latex_appendices = [] + +# If false, no module index is generated. +# latex_domain_indices = True + + +# -- Options for manual page output --------------------------------------- + +# One entry per manual page. List of tuples +# (source start file, name, description, authors, manual section). +man_pages = [ + ( + "index", + "libdiffpy", + "libdiffpy Documentation", + ab_authors, + 1, + ) +] + +# If true, show URL addresses after external links. +# man_show_urls = False + + +# -- Options for Texinfo output ------------------------------------------- + +# Grouping the document tree into Texinfo files. List of tuples +# (source start file, target name, title, author, +# dir menu entry, description, category) +texinfo_documents = [ + ( + "index", + "libdiffpy", + "libdiffpy Documentation", + ab_authors, + "libdiffpy", + "One line description of project.", + "Miscellaneous", + ), +] + +# Documents to append as an appendix to all manuals. +# texinfo_appendices = [] + +# If false, no module index is generated. +# texinfo_domain_indices = True + +# How to display URL addresses: 'footnote', 'no', or 'inline'. +# texinfo_show_urls = 'footnote' + +# If true, do not generate a @detailmenu in the "Top" node's menu. +# texinfo_no_detailmenu = False + + +# Example configuration for intersphinx: refer to the Python standard library. +# intersphinx_mapping = {'http://docs.python.org/': None} diff --git a/docs/source/getting-started.rst b/docs/source/getting-started.rst new file mode 100644 index 00000000..6b62bf84 --- /dev/null +++ b/docs/source/getting-started.rst @@ -0,0 +1,79 @@ +:tocdepth: -1 + +.. index:: getting-started + +.. _getting-started: + +================ +Getting started +================ + +Here are some example templates provided to help you get started with writing your documentation. You can use these templates to create your own documentation. + +Reuse ``.rst`` files across multiple pages +------------------------------------------ + +Here is how you can reuse a reusable block of ``.rst`` files across multiple pages: + +.. include:: snippets/example-table.rst + +.. warning:: + + Ensure that the ``.rst`` file you are including is not too long. If it is too long, it may be better to split it into multiple files and include them separately. + +Refer to a specific section in the documentation +------------------------------------------------ + +You can use the ``ref`` tag to refer to a specific section in the documentation. For example, you can refer to the section below using the ``:ref:`` tag as shown :ref:`here `. + +.. note:: + + Please check the raw ``.rst`` file of this page to see the exact use of the ``:ref:`` tag. + +Embed your code snippets in the documentation +--------------------------------------------- + +Here is how you can write a block of code in the documentation. You can use the ``code-block`` directive to write a block of code in the documentation. For example, you can write a block of code as shown below: + +.. code-block:: bash + + # Create a new environment, without build dependencies (pure Python package) + conda create -n -env python=3.14 \ + --file requirements/tests.txt \ + --file requirements/conda.txt + + # Create a new environment, with build dependencies (non-pure Python package) + conda create -n -env python=3.14 \ + --file requirements/tests.txt \ + --file requirements/conda.txt \ + --file requirements/build.txt + + # Activate the environment + conda activate _env + + # Install your package locally + # `--no-deps` to NOT install packages again from `requirements.pip.txt` + pip install -e . --no-deps + + # Run pytest locally + pytest + + # ... run example tutorials + +.. _attach-image: + +Attach an image to the documentation +------------------------------------ + +Here is how you attach an image to the documentation. The ``/docs/source/img/scikit-package-logo-text.png`` example image is provided in the template. + +.. image:: ./img/scikit-package-logo-text.png + :alt: codecov-in-pr-comment + :width: 400px + :align: center + + +Other useful directives +----------------------- + +Here is how you can do menu selection :menuselection:`Admin --> Settings` and display labels for buttons like :guilabel:`Privacy level`. diff --git a/docs/source/img/.placeholder b/docs/source/img/.placeholder new file mode 100644 index 00000000..e69de29b diff --git a/docs/source/img/scikit-package-logo-text.png b/docs/source/img/scikit-package-logo-text.png new file mode 100644 index 0000000000000000000000000000000000000000..823178dcb1316d5cfd09bc43d8210e5c4ce38f28 GIT binary patch literal 187608 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 h|9pew|C30$uOMa3u}J+)EaB{~z=&QIY@v literal 0 HcmV?d00001 diff --git a/docs/source/index.rst b/docs/source/index.rst new file mode 100644 index 00000000..3b09b717 --- /dev/null +++ b/docs/source/index.rst @@ -0,0 +1,56 @@ +####### +|title| +####### + +.. |title| replace:: libdiffpy documentation + +``libdiffpy`` - DiffPy C++ library for calculation of PDF and other real-space quantities + +| Software version |release| +| Last updated |today|. + +=============== +Getting started +=============== + +Welcome to the ``libdiffpy`` documentation! + +To get started, please visit the :ref:`Getting started ` page. + +======= +Authors +======= + +``libdiffpy`` is developed by Pavol Juhas, Chris Farrow, Simon Billinge, Billinge Group members. This project is maintained by Simon Billinge. For a detailed list of contributors see +https://github.com/diffpy/libdiffpy/graphs/contributors. + +============ +Installation +============ + +See the `README `_ +file included with the distribution. + +================ +Acknowledgements +================ + +``libdiffpy`` is built and maintained with `scikit-package `_. + +================= +Table of contents +================= +.. toctree:: + :maxdepth: 2 + + getting-started + Package API + release + license + +======= +Indices +======= + +* :ref:`genindex` +* :ref:`search` diff --git a/docs/source/license.rst b/docs/source/license.rst new file mode 100644 index 00000000..75f32ed6 --- /dev/null +++ b/docs/source/license.rst @@ -0,0 +1,38 @@ +:tocdepth: -1 + +.. index:: license + +License +####### + +OPEN SOURCE LICENSE AGREEMENT +============================= +BSD 3-Clause License + +Copyright (c) 2026, libdiffpy contributors. +All Rights Reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +1. Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + +2. Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + +3. Neither the name of the copyright holder nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/docs/source/release.rst b/docs/source/release.rst new file mode 100644 index 00000000..27cd0cc9 --- /dev/null +++ b/docs/source/release.rst @@ -0,0 +1,5 @@ +:tocdepth: -1 + +.. index:: release notes + +.. include:: ../../CHANGELOG.rst diff --git a/docs/source/snippets/.placeholder b/docs/source/snippets/.placeholder new file mode 100644 index 00000000..e69de29b diff --git a/docs/source/snippets/example-table.rst b/docs/source/snippets/example-table.rst new file mode 100644 index 00000000..7c4c11da --- /dev/null +++ b/docs/source/snippets/example-table.rst @@ -0,0 +1,28 @@ +.. list-table:: 5 levels of reusing/sharing code + :widths: 5 15 40 40 + :header-rows: 1 + + * - Level + - Name + - Scope + - How to setup + * - 1 + - ``function`` + - Reuse code in the single file. + - See Level 1 tutorial + * - 2 + - ``module`` + - Reuse code across files. + - See Level 2 tutorial + * - 3 + - ``workspace`` + - Reuse code across project folders. + - ``package create workspace`` + * - 4 + - ``system`` + - Reuse code across any files in the computer. + - ``package create system`` + * - 5 + - ``public`` + - Share code as publicly installable package. + - ``package create public`` diff --git a/news/TEMPLATE.rst b/news/TEMPLATE.rst new file mode 100644 index 00000000..790d30b1 --- /dev/null +++ b/news/TEMPLATE.rst @@ -0,0 +1,23 @@ +**Added:** + +* + +**Changed:** + +* + +**Deprecated:** + +* + +**Removed:** + +* + +**Fixed:** + +* + +**Security:** + +* diff --git a/pyproject.toml b/pyproject.toml new file mode 100644 index 00000000..c6b58d89 --- /dev/null +++ b/pyproject.toml @@ -0,0 +1,85 @@ +[build-system] +requires = ["setuptools>=62.0", "setuptools-git-versioning>=2.0"] +build-backend = "setuptools.build_meta" + +[project] +name = "libdiffpy" +dynamic=['version', 'dependencies'] +authors = [ + {name='Simon Billinge', email='sbillinge@ucsb.edu'}, +] +maintainers = [ + {name='Simon Billinge', email='sbillinge@ucsb.edu'}, +] +description = "DiffPy C++ library for calculation of PDF and other real-space quantities" +keywords = ['diffraction', 'PDF', 'X-ray', 'neutron'] +readme = "README.rst" +requires-python = ">=3.12, <3.15" +classifiers = [ + 'Development Status :: 5 - Production/Stable', + 'Environment :: Console', + 'Intended Audience :: Developers', + 'Intended Audience :: Science/Research', + 'License :: OSI Approved :: BSD License', + 'Operating System :: MacOS :: MacOS X', + 'Operating System :: Microsoft :: Windows', + 'Operating System :: POSIX', + 'Operating System :: Unix', + 'Programming Language :: Python :: 3.12', + 'Programming Language :: Python :: 3.13', + 'Programming Language :: Python :: 3.14', + 'Topic :: Scientific/Engineering :: Physics', + 'Topic :: Scientific/Engineering :: Chemistry', +] + +[project.urls] +Homepage = "https://github.com/diffpy/libdiffpy/" +Issues = "https://github.com/diffpy/libdiffpy/issues/" + +[tool.setuptools-git-versioning] +enabled = true +template = "{tag}" +dev_template = "{tag}" +dirty_template = "{tag}" + +[tool.setuptools.packages.find] +where = ["src"] # list of folders that contain the packages (["."] by default) +include = ["*"] # package names should match these glob patterns (["*"] by default) +exclude = [] # exclude packages matching these glob patterns (empty by default) +namespaces = false # to disable scanning PEP 420 namespaces (true by default) + +[tool.setuptools.dynamic] +dependencies = {file = ["requirements/pip.txt"]} + +[tool.codespell] +exclude-file = ".codespell/ignore_lines.txt" +ignore-words = ".codespell/ignore_words.txt" +skip = "*.cif,*.dat" + +[tool.docformatter] +recursive = true +wrap-summaries = 72 +wrap-descriptions = 72 + +[tool.black] +line-length = 79 +include = '\.pyi?$' +exclude = ''' +/( + \.git + | \.hg + | \.mypy_cache + | \.tox + | \.venv + | \.rst + | \.txt + | _build + | buck-out + | build + | dist + + # The following are specific to Black, you probably don't want those. + | blib2to3 + | tests/data +)/ +''' diff --git a/requirements/build.txt b/requirements/build.txt new file mode 100644 index 00000000..802684b1 --- /dev/null +++ b/requirements/build.txt @@ -0,0 +1,2 @@ +cxx-compiler +scons diff --git a/requirements/conda.txt b/requirements/conda.txt new file mode 100644 index 00000000..78514c11 --- /dev/null +++ b/requirements/conda.txt @@ -0,0 +1,3 @@ +libboost-devel +gsl +libobjcryst diff --git a/requirements/docs.txt b/requirements/docs.txt new file mode 100644 index 00000000..1de813f9 --- /dev/null +++ b/requirements/docs.txt @@ -0,0 +1,5 @@ +sphinx +sphinx_rtd_theme +sphinx-copybutton +doctr +m2r2 diff --git a/requirements/pip.txt b/requirements/pip.txt new file mode 100644 index 00000000..24ce15ab --- /dev/null +++ b/requirements/pip.txt @@ -0,0 +1 @@ +numpy diff --git a/requirements/tests.txt b/requirements/tests.txt new file mode 100644 index 00000000..6a9dfd04 --- /dev/null +++ b/requirements/tests.txt @@ -0,0 +1 @@ +cxxtest diff --git a/setup.py b/setup.py new file mode 100644 index 00000000..199f3bea --- /dev/null +++ b/setup.py @@ -0,0 +1,37 @@ +#!/usr/bin/env python + +# Extensions script for libdiffpy + +import os +import re +import sys +import glob +from setuptools import setup +from setuptools import Extension + +# Define extension arguments here +ext_kws = { + 'libraries' : [], + 'extra_compile_args' : [], + 'extra_link_args' : [], + 'include_dirs' : [], +} + +def create_extensions(): + "Initialize Extension objects for the setup function." + ext = Extension('libdiffpy.libdiffpy', + glob.glob('src/extensions/*.cpp'), + **ext_kws) + return [ext] + + +# Extensions not included in pyproject.toml +setup_args = dict( + ext_modules = [], +) + + +if __name__ == '__main__': + setup_args['ext_modules'] = create_extensions() + setup(**setup_args) + diff --git a/src/libdiffpy/__init__.py b/src/libdiffpy/__init__.py new file mode 100644 index 00000000..d78bd746 --- /dev/null +++ b/src/libdiffpy/__init__.py @@ -0,0 +1,23 @@ +#!/usr/bin/env python +############################################################################## +# +# (c) 2026 libdiffpy contributors. +# All rights reserved. +# +# File coded by: Pavol Juhas, Chris Farrow, Simon Billinge, Billinge Group members. +# +# See GitHub contributions for a more detailed list of contributors. +# https://github.com/diffpy/libdiffpy/graphs/contributors +# +# See LICENSE.rst for license information. +# +############################################################################## +"""DiffPy C++ library for calculation of PDF and other real-space quantities""" + +# package version +from libdiffpy.version import __version__ # noqa + +# silence the pyflakes syntax checker +assert __version__ or True + +# End of file diff --git a/src/libdiffpy/version.py b/src/libdiffpy/version.py new file mode 100644 index 00000000..6eddb6e1 --- /dev/null +++ b/src/libdiffpy/version.py @@ -0,0 +1,26 @@ +#!/usr/bin/env python +############################################################################## +# +# (c) 2026 libdiffpy contributors. +# All rights reserved. +# +# File coded by: Pavol Juhas, Chris Farrow, Simon Billinge, Billinge Group members. +# +# See GitHub contributions for a more detailed list of contributors. +# https://github.com/diffpy/libdiffpy/graphs/contributors # noqa: E501 +# +# See LICENSE.rst for license information. +# +############################################################################## +"""Definition of __version__.""" + +# We do not use the other three variables, but can be added back if needed. +# __all__ = ["__date__", "__git_commit__", "__timestamp__", "__version__"] + +# obtain version information +from importlib.metadata import PackageNotFoundError, version + +try: + __version__ = version("libdiffpy") +except PackageNotFoundError: + __version__ = "unknown" diff --git a/tests/conftest.py b/tests/conftest.py new file mode 100644 index 00000000..e3b63139 --- /dev/null +++ b/tests/conftest.py @@ -0,0 +1,19 @@ +import json +from pathlib import Path + +import pytest + + +@pytest.fixture +def user_filesystem(tmp_path): + base_dir = Path(tmp_path) + home_dir = base_dir / "home_dir" + home_dir.mkdir(parents=True, exist_ok=True) + cwd_dir = base_dir / "cwd_dir" + cwd_dir.mkdir(parents=True, exist_ok=True) + + home_config_data = {"username": "home_username", "email": "home@email.com"} + with open(home_dir / "diffpyconfig.json", "w") as f: + json.dump(home_config_data, f) + + yield tmp_path diff --git a/tests/test_version.py b/tests/test_version.py new file mode 100644 index 00000000..1591df44 --- /dev/null +++ b/tests/test_version.py @@ -0,0 +1,10 @@ +"""Unit tests for __version__.py.""" + +import libdiffpy # noqa + + +def test_package_version(): + """Ensure the package version is defined and not set to the initial + placeholder.""" + assert hasattr(libdiffpy, "__version__") + assert libdiffpy.__version__ != "0.0.0" From f01301e01e3000ea9c1fdd4c4ddf1f661b6937db Mon Sep 17 00:00:00 2001 From: Caden Myers Date: Mon, 19 Oct 2026 15:42:09 -0400 Subject: [PATCH 13/56] Move README.md to README.rst --- README.md | 160 ----------------------------------------------------- README.rst | 141 +++++++++++++++++++++++++++++++++------------- 2 files changed, 102 insertions(+), 199 deletions(-) delete mode 100644 README.md diff --git a/README.md b/README.md deleted file mode 100644 index 4ec67f65..00000000 --- a/README.md +++ /dev/null @@ -1,160 +0,0 @@ -[![Build Status](https://travis-ci.org/diffpy/libdiffpy.svg?branch=master)](https://travis-ci.org/diffpy/libdiffpy) -[![codecov](https://codecov.io/gh/diffpy/libdiffpy/branch/master/graph/badge.svg)](https://codecov.io/gh/diffpy/libdiffpy) - -# libdiffpy - -C++ calculators of PDF, bond valence sum and other pair quantities - -libdiffpy is a C++ library for calculating atomic pair distribution function -(PDF), bond valence sums, atom overlaps for a hard-sphere model, bond -distances and directions up to a specified maximum distance. The atomic -structure models are represented by classes for non-periodic, periodic or -structures with space group symmetries. libdiffpy supports Crystal and -Molecule classes from the [ObjCryst crystallographic library]( -https://sourceforge.net/projects/objcryst). -Calculators support two evaluation models - BASIC, which performs a full -pair-summation every time, and OPTIMIZED, which updates only pair -contributions that have changed since the last evaluation. libdiffpy supports -object serialization and parallel computations using parallel map function. -PDF calculations can be performed in two modes - either as a real-space -summation of peak profiles (PDFCalculator) or as a reciprocal-space Debye -summation and Fourier transform of the total scattering structure function -(DebyePDFCalculator). - -The calculator objects in libdiffpy share a common procedure for iteration -over atom pairs and only specialize the processing of pair contributions. -New calculator classes can thus be readily defined for any quantity that is -obtained by iteration over atom pairs. - -For more information see user manual at -http://www.diffpy.org/doc/libdiffpy. - - -## REQUIREMENTS - -libdiffpy library requires C++ compiler and the following software: - -* `scons` - software constructions tool (1.0 or later) -* `libboost-dev` - Boost C++ libraries development files (1.56 or later) -* `GSL` - GNU Scientific Library for C - -Recommended software: - -* `libobjcryst` - C++ library for free objects for crystallography, - https://github.com/diffpy/libobjcryst -* `cxxtest` - CxxTest Unit Testing Framework, http://cxxtest.com - -The required software is commonly available in the system package manager. -For example, on Ubuntu Linux the required software can be installed using - -```sh -sudo apt-get install \ - build-essential scons libboost-dev libgsl0-dev -``` - -libdiffpy is also available as a pre-compiled package for -[Anaconda Python](https://www.anaconda.com/download). A Python -interface to libdiffpy is provided by Anaconda package diffpy.srreal. - - -## INSTALLATION - -### Installation from sources - -Use sources from the git repository or extract the latest source -bundle from https://github.com/diffpy/libdiffpy/releases/latest. - -```sh -tar xzf libdiffpy-VERSION.tar.gz -cd libdiffpy-VERSION -``` - -To build and install the libdiffpy library use - -```sh -sudo scons -j4 install -``` - -This installs libdiffpy for all users under the `/usr/local` directory. -If administrator (root) access is not available, see the output from -`scons --help` for options to install to a user-writable location. - -To verify libdiffpy installation, compile and run the included -test code [examples/testlib.cpp](examples/testlib.cpp) - -```sh -cd examples -c++ testlib.cpp -ldiffpy -./a.out -``` - -If compilation fails because of missing header files or missing libdiffpy -library, adjust the `CPATH` and `LIBRARY_PATH` environment variables or -use the `-I` or `-L` compiler options. If the shared library libdiffpy -is unavailable at runtime, add a `-Wl,-rpath,SomePath` option to the -c++ command or adjust the `LD_LIBRARY_PATH` environment variable. - -### Installation for Anaconda Python - -The libdiffpy library can be installed from the "diffpy" channel -of Anaconda packages - -```sh -conda config --add channels diffpy -conda install libdiffpy -``` - -libdiffpy is also included in the "diffpy-cmi" collection of packages -for structure analysis - -```sh -conda install diffpy-cmi -``` - -When compiling with the Anaconda version of libdiffpy it is essential to -specify header path, library path and runtime path of the active Anaconda -environment - -```sh -# resolve prefix directory P of the active Anaconda environment -P="$(conda info --json | grep default_prefix | cut -d\" -f4)" -cd examples -c++ testlib.cpp -I$P/include -L$P/lib -Wl,-rpath,$P/lib -ldiffpy -./a.out -``` - -Note the Anaconda package for libdiffpy is built with C++ compilers -provided by Anaconda. This may cause incompatibility with system C++. -In such case please use Anaconda C++ compiler to link with libdiffpy. - - -## DEVELOPMENT - -libdiffpy is an open-source software developed as a part of the -DiffPy-CMI complex modeling initiative at the Brookhaven National -Laboratory. The libdiffpy sources are hosted at -https://github.com/diffpy/libdiffpy. - -Feel free to fork the project and contribute. When developing it is -preferable to compile with `build=develop` option, which compiles the -library with debug information and C-assertions checks: - -```sh -scons -j4 build=develop -``` - -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. The library integrity can be verified by executing unit tests with -`scons -j4 test` (requires the CxxTest framework). - -[Instructions for compiling and modifying source code in development mode](docs/development_instructions.md) - -## CONTACTS - -For more information about libdiffpy please visit the project web-page - -http://www.diffpy.org/ - -or email Prof. Simon Billinge at sb2896@columbia.edu. diff --git a/README.rst b/README.rst index be78cb85..ae0b1147 100644 --- a/README.rst +++ b/README.rst @@ -8,18 +8,12 @@ :target: https://diffpy.github.io/libdiffpy :height: 100px -|PyPI| |Forge| |PythonVersion| |PR| +|Forge| |PR| -|CI| |Codecov| |Black| |Tracking| +|CI| |Tracking| -.. |Black| image:: https://img.shields.io/badge/code_style-black-black - :target: https://github.com/psf/black - -.. |CI| image:: https://github.com/diffpy/libdiffpy/actions/workflows/matrix-and-codecov-on-merge-to-main.yml/badge.svg - :target: https://github.com/diffpy/libdiffpy/actions/workflows/matrix-and-codecov-on-merge-to-main.yml - -.. |Codecov| image:: https://codecov.io/gh/diffpy/libdiffpy/branch/main/graph/badge.svg - :target: https://codecov.io/gh/diffpy/libdiffpy +.. |CI| image:: https://github.com/diffpy/libdiffpy/actions/workflows/matrix-and-codecov.yml/badge.svg + :target: https://github.com/diffpy/libdiffpy/actions/workflows/matrix-and-codecov.yml .. |Forge| image:: https://img.shields.io/conda/vn/conda-forge/libdiffpy :target: https://anaconda.org/conda-forge/libdiffpy @@ -27,18 +21,34 @@ .. |PR| image:: https://img.shields.io/badge/PR-Welcome-29ab47ff :target: https://github.com/diffpy/libdiffpy/pulls -.. |PyPI| image:: https://img.shields.io/pypi/v/libdiffpy - :target: https://pypi.org/project/libdiffpy/ - -.. |PythonVersion| image:: https://img.shields.io/pypi/pyversions/libdiffpy - :target: https://pypi.org/project/libdiffpy/ - .. |Tracking| image:: https://img.shields.io/badge/issue_tracking-github-blue :target: https://github.com/diffpy/libdiffpy/issues -DiffPy C++ library for calculation of PDF and other real-space quantities - -* LONGER DESCRIPTION HERE +C++ calculators of PDF, bond valence sum and other pair quantities. + +libdiffpy is a C++ library for calculating atomic pair distribution function +(PDF), bond valence sums, atom overlaps for a hard-sphere model, bond +distances and directions up to a specified maximum distance. The atomic +structure models are represented by classes for non-periodic, periodic or +structures with space group symmetries. libdiffpy supports Crystal and +Molecule classes from the `ObjCryst crystallographic library +`_. +Calculators support two evaluation models - BASIC, which performs a full +pair-summation every time, and OPTIMIZED, which updates only pair +contributions that have changed since the last evaluation. libdiffpy supports +object serialization and parallel computations using parallel map function. +PDF calculations can be performed in two modes - either as a real-space +summation of peak profiles (PDFCalculator) or as a reciprocal-space Debye +summation and Fourier transform of the total scattering structure function +(DebyePDFCalculator). + +The calculator objects in libdiffpy share a common procedure for iteration +over atom pairs and only specialize the processing of pair contributions. +New calculator classes can thus be readily defined for any quantity that is +obtained by iteration over atom pairs. + +A Python interface to libdiffpy is provided by the `diffpy.srreal +`_ package. For more information about the libdiffpy library, please consult our `online documentation `_. @@ -66,32 +76,74 @@ The following creates and activates a new environment named ``libdiffpy_env`` :: conda create -n libdiffpy_env libdiffpy conda activate libdiffpy_env -The output should print the latest version displayed on the badges above. +When compiling with the conda version of libdiffpy it is essential to +specify the header path, library path and runtime path of the active conda +environment. To verify the installation, compile and run the included +test code `examples/testlib.cpp `_ :: + + cd examples + c++ testlib.cpp -I$CONDA_PREFIX/include -L$CONDA_PREFIX/lib -Wl,-rpath,$CONDA_PREFIX/lib -ldiffpy + ./a.out + +Note the conda package for libdiffpy is built with C++ compilers +provided by conda-forge. This may cause incompatibility with system C++. +In such case please use the conda-forge C++ compiler to link with libdiffpy. + +Requirements +^^^^^^^^^^^^ + +To build libdiffpy from sources, a C++ compiler and the following software are required: -If the above does not work, you can use ``pip`` to download and install the latest release from -`Python Package Index `_. -To install using ``pip`` into your ``libdiffpy_env`` environment, type :: +* ``scons`` - software constructions tool (1.0 or later) +* ``libboost-dev`` - Boost C++ libraries development files (1.56 or later) +* ``GSL`` - GNU Scientific Library for C - pip install libdiffpy +Recommended software: -If you prefer to install from sources, after installing the dependencies, obtain the source archive from -`GitHub `_. Once installed, ``cd`` into your ``libdiffpy`` directory -and run the following :: +* ``libobjcryst`` - C++ library for free objects for crystallography, + https://github.com/diffpy/libobjcryst +* ``cxxtest`` - CxxTest Unit Testing Framework, http://cxxtest.com - pip install . +The required software is commonly available in the system package manager. +For example, on Ubuntu Linux the required software can be installed using :: -This package also provides command-line utilities. To check the software has been installed correctly, type :: + sudo apt-get install build-essential scons libboost-dev libgsl0-dev - libdiffpy --version +Alternatively, all of the above can be installed into a conda environment from +the ``requirements`` folder :: -You can also type the following command to verify the installation. :: + conda create -n libdiffpy_env --file requirements/build.txt --file requirements/conda.txt --file requirements/tests.txt + conda activate libdiffpy_env + +Installation from sources +^^^^^^^^^^^^^^^^^^^^^^^^^ + +Use sources from the git repository or extract the latest source +bundle from https://github.com/diffpy/libdiffpy/releases/latest. :: + + tar xzf libdiffpy-VERSION.tar.gz + cd libdiffpy-VERSION + +To build and install the libdiffpy library use :: - python -c "import libdiffpy; print(libdiffpy.__version__)" + sudo scons -j4 install +This installs libdiffpy for all users under the ``/usr/local`` directory. +If administrator (root) access is not available, see the output from +``scons --help`` for options to install to a user-writable location. -To view the basic usage and available commands, type :: +To verify the libdiffpy installation, compile and run the included +test code `examples/testlib.cpp `_ :: - libdiffpy -h + cd examples + c++ testlib.cpp -ldiffpy + ./a.out + +If compilation fails because of missing header files or missing libdiffpy +library, adjust the ``CPATH`` and ``LIBRARY_PATH`` environment variables or +use the ``-I`` or ``-L`` compiler options. If the shared library libdiffpy +is unavailable at runtime, add a ``-Wl,-rpath,SomePath`` option to the +c++ command or adjust the ``LD_LIBRARY_PATH`` environment variable. Getting Started --------------- @@ -103,12 +155,19 @@ 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. To install libdiffpy -in a development mode, with its sources being directly used by Python -rather than copied to a package directory, use the following in the root -directory :: +Feel free to fork the project and contribute. When developing it is +preferable to compile with the ``build=develop`` option, which compiles the +library with debug information and C-assertions checks :: + + scons -j4 build=develop - pip install -e . +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. The library integrity can be verified by executing unit tests with +``scons test`` (requires the CxxTest framework). + +See also the `instructions for compiling and modifying source code in development mode `_. To ensure code quality and to prevent accidental commits into the default branch, please set up the use of our pre-commit hooks. @@ -134,4 +193,8 @@ For more information on libdiffpy please visit the project `web-page `_. From 34f33d7b780a65673196758ed664959405b88612 Mon Sep 17 00:00:00 2001 From: Caden Myers Date: Mon, 19 Oct 2026 15:49:11 -0400 Subject: [PATCH 14/56] Move from LICENSE.txt to LICENSE.rst and change all in-file references to point to the rst file --- LICENSE.rst | 168 +++++++++++++++--- LICENSE.txt | 137 -------------- LICENSE_DANSE.txt | 2 +- src/diffpy/EventTicker.cpp | 2 +- src/diffpy/EventTicker.hpp | 2 +- src/diffpy/features.tpl | 2 +- src/diffpy/runtimepath.cpp | 2 +- src/diffpy/runtimepath.hpp | 2 +- src/diffpy/serialization.ipp | 2 +- src/diffpy/srreal/AtomUtils.cpp | 2 +- src/diffpy/srreal/AtomUtils.hpp | 2 +- src/diffpy/srreal/CrystalStructureAdapter.cpp | 2 +- src/diffpy/srreal/CrystalStructureAdapter.hpp | 2 +- src/diffpy/srreal/EmptyStructureAdapter.cpp | 2 +- .../srreal/PeriodicStructureAdapter.cpp | 2 +- .../srreal/PeriodicStructureAdapter.hpp | 2 +- src/diffpy/srreal/StructureDifference.cpp | 2 +- src/diffpy/srreal/StructureDifference.hpp | 2 +- src/diffpy/srreal/forwardtypes.hpp | 2 +- src/diffpy/srreal/scatteringfactordata.cpp | 2 +- src/diffpy/srreal/scatteringfactordata.hpp | 2 +- src/diffpy/version.cpp | 2 +- src/diffpy/version.tpl | 2 +- src/tests/TestAtomicStructureAdapter.hpp | 2 +- src/tests/TestAttributes.hpp | 2 +- src/tests/TestEmptyStructureAdapter.hpp | 2 +- src/tests/TestHasClassRegistry.hpp | 2 +- src/tests/TestPDFBaseline.hpp | 2 +- src/tests/TestPQEvaluator.hpp | 2 +- src/tests/TestPeakWidthModel.hpp | 2 +- src/tests/TestPeriodicStructureAdapter.hpp | 2 +- src/tests/TestRuntimePath.hpp | 2 +- src/tests/test_custompqconfig.hpp | 2 +- 33 files changed, 170 insertions(+), 197 deletions(-) delete mode 100644 LICENSE.txt diff --git a/LICENSE.rst b/LICENSE.rst index 42df4af5..fa74e759 100644 --- a/LICENSE.rst +++ b/LICENSE.rst @@ -1,29 +1,139 @@ -BSD 3-Clause License - -Copyright (c) 2026, libdiffpy contributors. -All rights reserved. - -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions are met: - -1. Redistributions of source code must retain the above copyright notice, this - list of conditions and the following disclaimer. - -2. Redistributions in binary form must reproduce the above copyright notice, - this list of conditions and the following disclaimer in the documentation - and/or other materials provided with the distribution. - -3. Neither the name of the copyright holder nor the names of its contributors - may be used to endorse or promote products derived from this software - without specific prior written permission. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" -AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE -FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL -DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR -SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER -CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, -OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +OPEN SOURCE LICENSE AGREEMENT +============================= + +Copyright (c) 2009-2011, University of Tennessee + +Copyright (c) 1989, 1991 Free Software Foundation, Inc. + +Copyright (c) 2006, The Regents of the University of California through Lawrence Berkeley National Laboratory + +Copyright (c) 2014, Australian Synchrotron Research Program Inc., ("ASRP") + +Copyright (c) 2006-2007, Board of Trustees of Michigan State University + +Copyright (c) 2008-2012, The Trustees of Columbia University in the City of New York + +Copyright (c) 2014-2019, Brookhaven Science Associates, Brookhaven National Laboratory + +The "DiffPy-CMI" is distributed subject to the following license conditions: + +.. code-block:: text + + SOFTWARE LICENSE AGREEMENT + + Software: DiffPy-CMI + + + (1) The "Software", below, refers to the aforementioned DiffPy-CMI (in either + source code, or binary form and accompanying documentation). + + Part of the software was derived from the DANSE, ObjCryst++ (with permission), + PyCifRW, Python periodictable, CCTBX, and SasView open source projects, of + which the original Copyrights are contained in each individual file. + + Each licensee is addressed as "you" or "Licensee." + + + (2) The copyright holders shown above and their third-party Licensors hereby + grant licensee a royalty-free nonexclusive license, subject to the limitations + stated herein and U.S. Government license rights. + + + (3) You may modify and make a copy or copies of the software for use within + your organization, if you meet the following conditions: + + (a) Copies in source code must include the copyright notice and this + software license agreement. + + (b) Copies in binary form must include the copyright notice and this + Software License Agreement in the documentation and/or other materials + provided with the copy. + + + (4) You may modify a copy or copies of the Software or any portion of it, thus + forming a work based on the Software, and distribute copies of such work + outside your organization, if you meet all of the following conditions: + + (a) Copies in source code must include the copyright notice and this + Software License Agreement; + + (b) Copies in binary form must include the copyright notice and this + Software License Agreement in the documentation and/or other materials + provided with the copy; + + (c) Modified copies and works based on the Software must carry prominent + notices stating that you changed specified portions of the Software. + + (d) Neither the name of Brookhaven Science Associates or Brookhaven + National Laboratory nor the names of its contributors may be used to + endorse or promote products derived from this software without specific + written permission. + + + (5) Portions of the Software resulted from work developed under a U.S. + Government contract and are subject to the following license: + The Government is granted for itself and others acting on its behalf a + paid-up, nonexclusive, irrevocable worldwide license in this computer software + to reproduce, prepare derivative works, and perform publicly and display + publicly. + + + (6) WARRANTY DISCLAIMER. THE SOFTWARE IS SUPPLIED "AS IS" WITHOUT + WARRANTY OF ANY KIND. THE COPYRIGHT HOLDERS, THEIR THIRD PARTY + LICENSORS, THE UNITED STATES, THE UNITED STATES DEPARTMENT OF ENERGY, AND + THEIR EMPLOYEES: (1) DISCLAIM ANY WARRANTIES, EXPRESS OR IMPLIED, INCLUDING + BUT NOT LIMITED TO ANY IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A + PARTICULAR PURPOSE, TITLE OR NON-INFRINGEMENT, (2) DO NOT ASSUME ANY LEGAL + LIABILITY OR RESPONSIBILITY FOR THE ACCURACY, COMPLETENESS, OR USEFULNESS OF + THE SOFTWARE, (3) DO NOT REPRESENT THAT USE OF THE SOFTWARE WOULD NOT INFRINGE + PRIVATELY OWNED RIGHTS, (4) DO NOT WARRANT THAT THE SOFTWARE WILL FUNCTION + UNINTERRUPTED, THAT IT IS ERROR-FREE OR THAT ANY ERRORS WILL BE CORRECTED. + + + (7) LIMITATION OF LIABILITY. IN NO EVENT WILL THE COPYRIGHT HOLDERS, THEIR + THIRD PARTY LICENSORS, THE UNITED STATES, THE UNITED STATES DEPARTMENT OF + ENERGY, OR THEIR EMPLOYEES: BE LIABLE FOR ANY INDIRECT, INCIDENTAL, + CONSEQUENTIAL, SPECIAL OR PUNITIVE DAMAGES OF ANY KIND OR NATURE, INCLUDING + BUT NOT LIMITED TO LOSS OF PROFITS OR LOSS OF DATA, FOR ANY REASON WHATSOEVER, + WHETHER SUCH LIABILITY IS ASSERTED ON THE BASIS OF CONTRACT, TORT (INCLUDING + NEGLIGENCE OR STRICT LIABILITY), OR OTHERWISE, EVEN IF ANY OF SAID PARTIES HAS + BEEN WARNED OF THE POSSIBILITY OF SUCH LOSS OR DAMAGES. + + +Brookhaven National Laboratory Notice +===================================== + +Acknowledgment of sponsorship +----------------------------- + +This software was produced by the Brookhaven National Laboratory, under +Contract DE-AC02-98CH10886 with the Department of Energy. + + +Government disclaimer of liability +---------------------------------- + +Neither the United States nor the United States Department of Energy, nor +any of their employees, makes any warranty, express or implied, or assumes +any legal liability or responsibility for the accuracy, completeness, or +usefulness of any data, apparatus, product, or process disclosed, or +represents that its use would not infringe privately owned rights. + + +Brookhaven disclaimer of liability +---------------------------------- + +Brookhaven National Laboratory makes no representations or warranties, +express or implied, nor assumes any liability for the use of this software. + + +Maintenance of notice +--------------------- + +In the interest of clarity regarding the origin and status of this +software, Brookhaven National Laboratory requests that any recipient of it +maintain this notice affixed to any distribution by the recipient that +contains a copy or derivative of this software. + + +END OF LICENSE diff --git a/LICENSE.txt b/LICENSE.txt deleted file mode 100644 index f6d92af7..00000000 --- a/LICENSE.txt +++ /dev/null @@ -1,137 +0,0 @@ -OPEN SOURCE LICENSE AGREEMENT -============================= - -Copyright (c) 2009-2011, University of Tennessee -Copyright (c) 1989, 1991 Free Software Foundation, Inc. -Copyright (c) 2006, The Regents of the University of California through - Lawrence Berkeley National Laboratory -Copyright (c) 2014, Australian Synchrotron Research Program Inc., ("ASRP") -Copyright (c) 2006-2007, Board of Trustees of Michigan State University -Copyright (c) 2008-2012, The Trustees of Columbia University in the City - of New York - -Copyright (c) 2014-2019, Brookhaven Science Associates, Brookhaven National - Laboratory - - -The "DiffPy-CMI" is distributed subject to the following license conditions: - - -SOFTWARE LICENSE AGREEMENT - - Software: DiffPy-CMI - - -(1) The "Software", below, refers to the aforementioned DiffPy-CMI (in either -source code, or binary form and accompanying documentation). - -Part of the software was derived from the DANSE, ObjCryst++ (with permission), -PyCifRW, Python periodictable, CCTBX, and SasView open source projects, of -which the original Copyrights are contained in each individual file. - -Each licensee is addressed as "you" or "Licensee." - - -(2) The copyright holders shown above and their third-party Licensors hereby -grant licensee a royalty-free nonexclusive license, subject to the limitations -stated herein and U.S. Government license rights. - - -(3) You may modify and make a copy or copies of the software for use within -your organization, if you meet the following conditions: - - (a) Copies in source code must include the copyright notice and this - software license agreement. - - (b) Copies in binary form must include the copyright notice and this - Software License Agreement in the documentation and/or other materials - provided with the copy. - - -(4) You may modify a copy or copies of the Software or any portion of it, thus -forming a work based on the Software, and distribute copies of such work -outside your organization, if you meet all of the following conditions: - - (a) Copies in source code must include the copyright notice and this - Software License Agreement; - - (b) Copies in binary form must include the copyright notice and this - Software License Agreement in the documentation and/or other materials - provided with the copy; - - (c) Modified copies and works based on the Software must carry prominent - notices stating that you changed specified portions of the Software. - - (d) Neither the name of Brookhaven Science Associates or Brookhaven - National Laboratory nor the names of its contributors may be used to - endorse or promote products derived from this software without specific - written permission. - - -(5) Portions of the Software resulted from work developed under a U.S. -Government contract and are subject to the following license: -The Government is granted for itself and others acting on its behalf a -paid-up, nonexclusive, irrevocable worldwide license in this computer software -to reproduce, prepare derivative works, and perform publicly and display -publicly. - - -(6) WARRANTY DISCLAIMER. THE SOFTWARE IS SUPPLIED "AS IS" WITHOUT -WARRANTY OF ANY KIND. THE COPYRIGHT HOLDERS, THEIR THIRD PARTY -LICENSORS, THE UNITED STATES, THE UNITED STATES DEPARTMENT OF ENERGY, AND -THEIR EMPLOYEES: (1) DISCLAIM ANY WARRANTIES, EXPRESS OR IMPLIED, INCLUDING -BUT NOT LIMITED TO ANY IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A -PARTICULAR PURPOSE, TITLE OR NON-INFRINGEMENT, (2) DO NOT ASSUME ANY LEGAL -LIABILITY OR RESPONSIBILITY FOR THE ACCURACY, COMPLETENESS, OR USEFULNESS OF -THE SOFTWARE, (3) DO NOT REPRESENT THAT USE OF THE SOFTWARE WOULD NOT INFRINGE -PRIVATELY OWNED RIGHTS, (4) DO NOT WARRANT THAT THE SOFTWARE WILL FUNCTION -UNINTERRUPTED, THAT IT IS ERROR-FREE OR THAT ANY ERRORS WILL BE CORRECTED. - - -(7) LIMITATION OF LIABILITY. IN NO EVENT WILL THE COPYRIGHT HOLDERS, THEIR -THIRD PARTY LICENSORS, THE UNITED STATES, THE UNITED STATES DEPARTMENT OF -ENERGY, OR THEIR EMPLOYEES: BE LIABLE FOR ANY INDIRECT, INCIDENTAL, -CONSEQUENTIAL, SPECIAL OR PUNITIVE DAMAGES OF ANY KIND OR NATURE, INCLUDING -BUT NOT LIMITED TO LOSS OF PROFITS OR LOSS OF DATA, FOR ANY REASON WHATSOEVER, -WHETHER SUCH LIABILITY IS ASSERTED ON THE BASIS OF CONTRACT, TORT (INCLUDING -NEGLIGENCE OR STRICT LIABILITY), OR OTHERWISE, EVEN IF ANY OF SAID PARTIES HAS -BEEN WARNED OF THE POSSIBILITY OF SUCH LOSS OR DAMAGES. - - -Brookhaven National Laboratory Notice -===================================== - -Acknowledgment of sponsorship ------------------------------ - -This software was produced by the Brookhaven National Laboratory, under -Contract DE-AC02-98CH10886 with the Department of Energy. - - -Government disclaimer of liability ----------------------------------- - -Neither the United States nor the United States Department of Energy, nor -any of their employees, makes any warranty, express or implied, or assumes -any legal liability or responsibility for the accuracy, completeness, or -usefulness of any data, apparatus, product, or process disclosed, or -represents that its use would not infringe privately owned rights. - - -Brookhaven disclaimer of liability ----------------------------------- - -Brookhaven National Laboratory makes no representations or warranties, -express or implied, nor assumes any liability for the use of this software. - - -Maintenance of notice ---------------------- - -In the interest of clarity regarding the origin and status of this -software, Brookhaven National Laboratory requests that any recipient of it -maintain this notice affixed to any distribution by the recipient that -contains a copy or derivative of this software. - - -END OF LICENSE diff --git a/LICENSE_DANSE.txt b/LICENSE_DANSE.txt index 3e8768db..83c9a71d 100644 --- a/LICENSE_DANSE.txt +++ b/LICENSE_DANSE.txt @@ -6,7 +6,7 @@ the City of New York. All rights reserved. For more information please visit the project web-page: http://www.diffpy.org/ -or email Prof. Simon Billinge at sb2896@columbia.edu +or email Prof. Simon Billinge at sbillinge@ucsb.edu Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions diff --git a/src/diffpy/EventTicker.cpp b/src/diffpy/EventTicker.cpp index 5caf20fb..5042cc7f 100644 --- a/src/diffpy/EventTicker.cpp +++ b/src/diffpy/EventTicker.cpp @@ -8,7 +8,7 @@ * File coded by: Pavol Juhas * * See AUTHORS.txt for a list of people who contributed. -* See LICENSE.txt for license information. +* See LICENSE.rst for license information. * ****************************************************************************** * diff --git a/src/diffpy/EventTicker.hpp b/src/diffpy/EventTicker.hpp index 54d11ef7..0efbe168 100644 --- a/src/diffpy/EventTicker.hpp +++ b/src/diffpy/EventTicker.hpp @@ -8,7 +8,7 @@ * File coded by: Pavol Juhas * * See AUTHORS.txt for a list of people who contributed. -* See LICENSE.txt for license information. +* See LICENSE.rst for license information. * ****************************************************************************** * diff --git a/src/diffpy/features.tpl b/src/diffpy/features.tpl index da1e913d..654604dd 100644 --- a/src/diffpy/features.tpl +++ b/src/diffpy/features.tpl @@ -8,7 +8,7 @@ * File coded by: Pavol Juhas * * See AUTHORS.txt for a list of people who contributed. -* See LICENSE.txt for license information. +* See LICENSE.rst for license information. * ****************************************************************************** * diff --git a/src/diffpy/runtimepath.cpp b/src/diffpy/runtimepath.cpp index be9bb00a..7b6db35f 100644 --- a/src/diffpy/runtimepath.cpp +++ b/src/diffpy/runtimepath.cpp @@ -8,7 +8,7 @@ * File coded by: Pavol Juhas * * See AUTHORS.txt for a list of people who contributed. -* See LICENSE.txt for license information. +* See LICENSE.rst for license information. * ****************************************************************************** * diff --git a/src/diffpy/runtimepath.hpp b/src/diffpy/runtimepath.hpp index c894122f..96b14677 100644 --- a/src/diffpy/runtimepath.hpp +++ b/src/diffpy/runtimepath.hpp @@ -8,7 +8,7 @@ * File coded by: Pavol Juhas * * See AUTHORS.txt for a list of people who contributed. -* See LICENSE.txt for license information. +* See LICENSE.rst for license information. * ****************************************************************************** * diff --git a/src/diffpy/serialization.ipp b/src/diffpy/serialization.ipp index f73d1f6f..863671f8 100644 --- a/src/diffpy/serialization.ipp +++ b/src/diffpy/serialization.ipp @@ -8,7 +8,7 @@ * File coded by: Pavol Juhas * * See AUTHORS.txt for a list of people who contributed. -* See LICENSE.txt for license information. +* See LICENSE.rst for license information. * ****************************************************************************** * diff --git a/src/diffpy/srreal/AtomUtils.cpp b/src/diffpy/srreal/AtomUtils.cpp index 37cd41ac..7e0ed837 100644 --- a/src/diffpy/srreal/AtomUtils.cpp +++ b/src/diffpy/srreal/AtomUtils.cpp @@ -8,7 +8,7 @@ * File coded by: Pavol Juhas * * See AUTHORS.txt for a list of people who contributed. -* See LICENSE.txt for license information. +* See LICENSE.rst for license information. * ****************************************************************************** * diff --git a/src/diffpy/srreal/AtomUtils.hpp b/src/diffpy/srreal/AtomUtils.hpp index 30071147..afdcd51f 100644 --- a/src/diffpy/srreal/AtomUtils.hpp +++ b/src/diffpy/srreal/AtomUtils.hpp @@ -8,7 +8,7 @@ * File coded by: Pavol Juhas * * See AUTHORS.txt for a list of people who contributed. -* See LICENSE.txt for license information. +* See LICENSE.rst for license information. * ****************************************************************************** * diff --git a/src/diffpy/srreal/CrystalStructureAdapter.cpp b/src/diffpy/srreal/CrystalStructureAdapter.cpp index 6d51c2ed..9a2151d2 100644 --- a/src/diffpy/srreal/CrystalStructureAdapter.cpp +++ b/src/diffpy/srreal/CrystalStructureAdapter.cpp @@ -8,7 +8,7 @@ * File coded by: Pavol Juhas * * See AUTHORS.txt for a list of people who contributed. -* See LICENSE.txt for license information. +* See LICENSE.rst for license information. * ****************************************************************************** * diff --git a/src/diffpy/srreal/CrystalStructureAdapter.hpp b/src/diffpy/srreal/CrystalStructureAdapter.hpp index 423ef48e..f8ad6dc8 100644 --- a/src/diffpy/srreal/CrystalStructureAdapter.hpp +++ b/src/diffpy/srreal/CrystalStructureAdapter.hpp @@ -8,7 +8,7 @@ * File coded by: Pavol Juhas * * See AUTHORS.txt for a list of people who contributed. -* See LICENSE.txt for license information. +* See LICENSE.rst for license information. * ****************************************************************************** * diff --git a/src/diffpy/srreal/EmptyStructureAdapter.cpp b/src/diffpy/srreal/EmptyStructureAdapter.cpp index a5606acb..9fc29db1 100644 --- a/src/diffpy/srreal/EmptyStructureAdapter.cpp +++ b/src/diffpy/srreal/EmptyStructureAdapter.cpp @@ -8,7 +8,7 @@ * File coded by: Pavol Juhas * * See AUTHORS.txt for a list of people who contributed. -* See LICENSE.txt for license information. +* See LICENSE.rst for license information. * ****************************************************************************** * diff --git a/src/diffpy/srreal/PeriodicStructureAdapter.cpp b/src/diffpy/srreal/PeriodicStructureAdapter.cpp index 29bd4da5..746376ec 100644 --- a/src/diffpy/srreal/PeriodicStructureAdapter.cpp +++ b/src/diffpy/srreal/PeriodicStructureAdapter.cpp @@ -8,7 +8,7 @@ * File coded by: Pavol Juhas * * See AUTHORS.txt for a list of people who contributed. -* See LICENSE.txt for license information. +* See LICENSE.rst for license information. * ****************************************************************************** * diff --git a/src/diffpy/srreal/PeriodicStructureAdapter.hpp b/src/diffpy/srreal/PeriodicStructureAdapter.hpp index 0d783515..210c1ec7 100644 --- a/src/diffpy/srreal/PeriodicStructureAdapter.hpp +++ b/src/diffpy/srreal/PeriodicStructureAdapter.hpp @@ -8,7 +8,7 @@ * File coded by: Pavol Juhas * * See AUTHORS.txt for a list of people who contributed. -* See LICENSE.txt for license information. +* See LICENSE.rst for license information. * ****************************************************************************** * diff --git a/src/diffpy/srreal/StructureDifference.cpp b/src/diffpy/srreal/StructureDifference.cpp index 90a57cb6..5c8cdc19 100644 --- a/src/diffpy/srreal/StructureDifference.cpp +++ b/src/diffpy/srreal/StructureDifference.cpp @@ -8,7 +8,7 @@ * File coded by: Pavol Juhas * * See AUTHORS.txt for a list of people who contributed. -* See LICENSE.txt for license information. +* See LICENSE.rst for license information. * ****************************************************************************** * diff --git a/src/diffpy/srreal/StructureDifference.hpp b/src/diffpy/srreal/StructureDifference.hpp index 4be27e58..22f781fa 100644 --- a/src/diffpy/srreal/StructureDifference.hpp +++ b/src/diffpy/srreal/StructureDifference.hpp @@ -8,7 +8,7 @@ * File coded by: Pavol Juhas * * See AUTHORS.txt for a list of people who contributed. -* See LICENSE.txt for license information. +* See LICENSE.rst for license information. * ****************************************************************************** * diff --git a/src/diffpy/srreal/forwardtypes.hpp b/src/diffpy/srreal/forwardtypes.hpp index acaafa1e..35751dc2 100644 --- a/src/diffpy/srreal/forwardtypes.hpp +++ b/src/diffpy/srreal/forwardtypes.hpp @@ -8,7 +8,7 @@ * File coded by: Pavol Juhas * * See AUTHORS.txt for a list of people who contributed. -* See LICENSE.txt for license information. +* See LICENSE.rst for license information. * ****************************************************************************** * diff --git a/src/diffpy/srreal/scatteringfactordata.cpp b/src/diffpy/srreal/scatteringfactordata.cpp index 808a47e2..4b80a7b8 100644 --- a/src/diffpy/srreal/scatteringfactordata.cpp +++ b/src/diffpy/srreal/scatteringfactordata.cpp @@ -8,7 +8,7 @@ * File coded by: Pavol Juhas * * See AUTHORS.txt for a list of people who contributed. -* See LICENSE.txt for license information. +* See LICENSE.rst for license information. * ****************************************************************************** * diff --git a/src/diffpy/srreal/scatteringfactordata.hpp b/src/diffpy/srreal/scatteringfactordata.hpp index 1844002f..a4d4b4a2 100644 --- a/src/diffpy/srreal/scatteringfactordata.hpp +++ b/src/diffpy/srreal/scatteringfactordata.hpp @@ -8,7 +8,7 @@ * File coded by: Pavol Juhas * * See AUTHORS.txt for a list of people who contributed. -* See LICENSE.txt for license information. +* See LICENSE.rst for license information. * ****************************************************************************** * diff --git a/src/diffpy/version.cpp b/src/diffpy/version.cpp index 803f0a6b..6f6e572c 100644 --- a/src/diffpy/version.cpp +++ b/src/diffpy/version.cpp @@ -8,7 +8,7 @@ * File coded by: Pavol Juhas * * See AUTHORS.txt for a list of people who contributed. -* See LICENSE.txt for license information. +* See LICENSE.rst for license information. * ****************************************************************************** * diff --git a/src/diffpy/version.tpl b/src/diffpy/version.tpl index 548fb3ab..c47a9e26 100644 --- a/src/diffpy/version.tpl +++ b/src/diffpy/version.tpl @@ -8,7 +8,7 @@ * File coded by: Pavol Juhas * * See AUTHORS.txt for a list of people who contributed. -* See LICENSE.txt for license information. +* See LICENSE.rst for license information. * ****************************************************************************** * diff --git a/src/tests/TestAtomicStructureAdapter.hpp b/src/tests/TestAtomicStructureAdapter.hpp index 80cf2922..e9021157 100644 --- a/src/tests/TestAtomicStructureAdapter.hpp +++ b/src/tests/TestAtomicStructureAdapter.hpp @@ -8,7 +8,7 @@ * File coded by: Pavol Juhas * * See AUTHORS.txt for a list of people who contributed. -* See LICENSE.txt for license information. +* See LICENSE.rst for license information. * ****************************************************************************** * diff --git a/src/tests/TestAttributes.hpp b/src/tests/TestAttributes.hpp index 992dc4c4..2cee1476 100644 --- a/src/tests/TestAttributes.hpp +++ b/src/tests/TestAttributes.hpp @@ -8,7 +8,7 @@ * File coded by: Pavol Juhas * * See AUTHORS.txt for a list of people who contributed. -* See LICENSE.txt for license information. +* See LICENSE.rst for license information. * ****************************************************************************** * diff --git a/src/tests/TestEmptyStructureAdapter.hpp b/src/tests/TestEmptyStructureAdapter.hpp index 932dde17..2ce5f4fc 100644 --- a/src/tests/TestEmptyStructureAdapter.hpp +++ b/src/tests/TestEmptyStructureAdapter.hpp @@ -8,7 +8,7 @@ * File coded by: Pavol Juhas * * See AUTHORS.txt for a list of people who contributed. -* See LICENSE.txt for license information. +* See LICENSE.rst for license information. * ****************************************************************************** * diff --git a/src/tests/TestHasClassRegistry.hpp b/src/tests/TestHasClassRegistry.hpp index 8f325a0e..4e6a8058 100644 --- a/src/tests/TestHasClassRegistry.hpp +++ b/src/tests/TestHasClassRegistry.hpp @@ -8,7 +8,7 @@ * File coded by: Pavol Juhas * * See AUTHORS.txt for a list of people who contributed. -* See LICENSE.txt for license information. +* See LICENSE.rst for license information. * ****************************************************************************** * diff --git a/src/tests/TestPDFBaseline.hpp b/src/tests/TestPDFBaseline.hpp index 34494f9e..c6104d24 100644 --- a/src/tests/TestPDFBaseline.hpp +++ b/src/tests/TestPDFBaseline.hpp @@ -8,7 +8,7 @@ * File coded by: Pavol Juhas * * See AUTHORS.txt for a list of people who contributed. -* See LICENSE.txt for license information. +* See LICENSE.rst for license information. * ****************************************************************************** * diff --git a/src/tests/TestPQEvaluator.hpp b/src/tests/TestPQEvaluator.hpp index 688729af..05829a8d 100644 --- a/src/tests/TestPQEvaluator.hpp +++ b/src/tests/TestPQEvaluator.hpp @@ -8,7 +8,7 @@ * File coded by: Pavol Juhas * * See AUTHORS.txt for a list of people who contributed. -* See LICENSE.txt for license information. +* See LICENSE.rst for license information. * ****************************************************************************** * diff --git a/src/tests/TestPeakWidthModel.hpp b/src/tests/TestPeakWidthModel.hpp index d42e0200..e0f794e0 100644 --- a/src/tests/TestPeakWidthModel.hpp +++ b/src/tests/TestPeakWidthModel.hpp @@ -8,7 +8,7 @@ * File coded by: Pavol Juhas * * See AUTHORS.txt for a list of people who contributed. -* See LICENSE.txt for license information. +* See LICENSE.rst for license information. * ****************************************************************************** * diff --git a/src/tests/TestPeriodicStructureAdapter.hpp b/src/tests/TestPeriodicStructureAdapter.hpp index 19979d16..388ee5b2 100644 --- a/src/tests/TestPeriodicStructureAdapter.hpp +++ b/src/tests/TestPeriodicStructureAdapter.hpp @@ -8,7 +8,7 @@ * File coded by: Pavol Juhas * * See AUTHORS.txt for a list of people who contributed. -* See LICENSE.txt for license information. +* See LICENSE.rst for license information. * ****************************************************************************** * diff --git a/src/tests/TestRuntimePath.hpp b/src/tests/TestRuntimePath.hpp index 36b52189..3961ef6b 100644 --- a/src/tests/TestRuntimePath.hpp +++ b/src/tests/TestRuntimePath.hpp @@ -8,7 +8,7 @@ * File coded by: Pavol Juhas * * See AUTHORS.txt for a list of people who contributed. -* See LICENSE.txt for license information. +* See LICENSE.rst for license information. * ****************************************************************************** * diff --git a/src/tests/test_custompqconfig.hpp b/src/tests/test_custompqconfig.hpp index a71ec108..873268b4 100644 --- a/src/tests/test_custompqconfig.hpp +++ b/src/tests/test_custompqconfig.hpp @@ -8,7 +8,7 @@ * File coded by: Pavol Juhas * * See AUTHORS.txt for a list of people who contributed. -* See LICENSE.txt for license information. +* See LICENSE.rst for license information. * ****************************************************************************** * From 1cbca7a04bc3463e7c39cad0b74ef9f1aa05ad88 Mon Sep 17 00:00:00 2001 From: Caden Myers Date: Mon, 19 Oct 2026 15:49:57 -0400 Subject: [PATCH 15/56] remove old conda-recipe that was used for the old diffpy channel --- conda-recipe/build.sh | 12 ------- conda-recipe/conda_build_config.yaml | 11 ------ conda-recipe/meta.yaml | 51 ---------------------------- conda-recipe/run_test.sh | 16 --------- conda-recipe/sconscript.local | 18 ---------- conda-recipe/setup.py | 22 ------------ 6 files changed, 130 deletions(-) delete mode 100644 conda-recipe/build.sh delete mode 100644 conda-recipe/conda_build_config.yaml delete mode 100644 conda-recipe/meta.yaml delete mode 100644 conda-recipe/run_test.sh delete mode 100644 conda-recipe/sconscript.local delete mode 100644 conda-recipe/setup.py diff --git a/conda-recipe/build.sh b/conda-recipe/build.sh deleted file mode 100644 index 591d6c57..00000000 --- a/conda-recipe/build.sh +++ /dev/null @@ -1,12 +0,0 @@ -#!/bin/bash - -MYNCPU=$(( (CPU_COUNT > 8) ? 8 : CPU_COUNT )) - -# drop linker flags that spuriously remove linkage with libgslcblas -LDFLAGS="${LDFLAGS/-Wl,-dead_strip_dylibs/}" - -# Apply sconscript.local customizations. -cp ${RECIPE_DIR}/sconscript.local ./ - -# Build and install the library. -scons -j $MYNCPU lib install prefix=$PREFIX diff --git a/conda-recipe/conda_build_config.yaml b/conda-recipe/conda_build_config.yaml deleted file mode 100644 index 30b82637..00000000 --- a/conda-recipe/conda_build_config.yaml +++ /dev/null @@ -1,11 +0,0 @@ -libobjcryst: - - 2026.3.* - -gsl: - - 2.8 - -boost: - - 1.88 - -pin_run_as_build: - libboost: x.x diff --git a/conda-recipe/meta.yaml b/conda-recipe/meta.yaml deleted file mode 100644 index e5fd1eb5..00000000 --- a/conda-recipe/meta.yaml +++ /dev/null @@ -1,51 +0,0 @@ -{% set setupdata = load_setup_py_data(from_recipe_dir=True) %} - -package: - name: libdiffpy - version: {{ setupdata['version'] }} - -source: - # git_url: https://github.com/diffpy/libdiffpy.git - git_url: .. - -build: - number: 0 - run_exports: - - {{ pin_subpackage('libdiffpy', max_pin='x.x') }} - -requirements: - build: - - {{ compiler('cxx') }} - - python - - scons - host: - - libboost {{ boost }} - - gsl {{ gsl }} - - libobjcryst {{ libobjcryst }} - - run: - # NOTE libobjcryst is implicitly added by libobjcryst run_exports - - libboost - - {{ pin_compatible('gsl', min_pin='x.x', max_pin='x') }} - -test: - requires: - - {{ compiler('cxx') }} - - cxxtest - - scons - - source_files: - - SConstruct - - site_scons/ - - src/SConscript - - src/SConscript.configure - - src/tests/ - - # commands: - # Execute the run_test.sh script. - -about: - home: https://github.com/diffpy/libdiffpy - summary: C++ calculators of PDF, bond valence sum and other pair quantities. - license: Modified BSD License - license_file: LICENSE.txt diff --git a/conda-recipe/run_test.sh b/conda-recipe/run_test.sh deleted file mode 100644 index ae0d976d..00000000 --- a/conda-recipe/run_test.sh +++ /dev/null @@ -1,16 +0,0 @@ -#!/bin/bash - -export CPATH="${PREFIX}/include:${CPATH}" -export LIBRARY_PATH="${PREFIX}/lib:${LIBRARY_PATH}" - -MYNCPU=$(( (CPU_COUNT > 8) ? 8 : CPU_COUNT )) - -# Apply sconscript.local customizations. -cp ${RECIPE_DIR}/sconscript.local ./ - -# Build the unit tests program using the installed library. -scons -j $MYNCPU alltests prefix=$PREFIX test_installed=true - -# Execute the unit tests. -MYALLTESTSFAST=$(ls -t ${PWD}/build/fast*/tests/alltests | head -1) -${MYALLTESTSFAST} diff --git a/conda-recipe/sconscript.local b/conda-recipe/sconscript.local deleted file mode 100644 index f8740395..00000000 --- a/conda-recipe/sconscript.local +++ /dev/null @@ -1,18 +0,0 @@ -# Customize scons build environment. - -Import('env') - -import os - -# Apply environment settings for Anaconda compilers -env.Replace(CXX=os.environ['CXX']) -env.MergeFlags(os.environ['CFLAGS']) -env.MergeFlags(os.environ['CPPFLAGS']) -env.MergeFlags(os.environ['CXXFLAGS']) -env.MergeFlags(os.environ['LDFLAGS']) - -# Silence copious warnings from the boost headers. -P = os.environ['PREFIX'] -env.Prepend(CCFLAGS=['-isystem{}/include'.format(P)]) - -# vim: ft=python diff --git a/conda-recipe/setup.py b/conda-recipe/setup.py deleted file mode 100644 index e03b9486..00000000 --- a/conda-recipe/setup.py +++ /dev/null @@ -1,22 +0,0 @@ -#!/usr/bin/env python - -"""\ -Dummy distutils setup script that provides version data for meta.yaml. -""" - -import os -import sys -from distutils.core import setup - -MYDIR = os.path.dirname(os.path.abspath(__file__)) -SITESCONSDIR = os.path.abspath(os.path.join(MYDIR, '../site_scons')) -sys.path.insert(0, SITESCONSDIR) - -from libdiffpybuildutils import gitinfo - -ginfo = gitinfo() - -setup( - name='libdiffpy', - version=ginfo['version'], -) From e53318f743bda88ee03392c7905fcd35324032c3 Mon Sep 17 00:00:00 2001 From: Caden Myers Date: Tue, 20 Oct 2026 11:20:48 -0400 Subject: [PATCH 16/56] rm unnecessary setup.py --- setup.py | 37 ------------------------------------- 1 file changed, 37 deletions(-) delete mode 100644 setup.py diff --git a/setup.py b/setup.py deleted file mode 100644 index 199f3bea..00000000 --- a/setup.py +++ /dev/null @@ -1,37 +0,0 @@ -#!/usr/bin/env python - -# Extensions script for libdiffpy - -import os -import re -import sys -import glob -from setuptools import setup -from setuptools import Extension - -# Define extension arguments here -ext_kws = { - 'libraries' : [], - 'extra_compile_args' : [], - 'extra_link_args' : [], - 'include_dirs' : [], -} - -def create_extensions(): - "Initialize Extension objects for the setup function." - ext = Extension('libdiffpy.libdiffpy', - glob.glob('src/extensions/*.cpp'), - **ext_kws) - return [ext] - - -# Extensions not included in pyproject.toml -setup_args = dict( - ext_modules = [], -) - - -if __name__ == '__main__': - setup_args['ext_modules'] = create_extensions() - setup(**setup_args) - From f6df5e9bb826c97cfe0e36f00ea989fe2b13c750 Mon Sep 17 00:00:00 2001 From: Caden Myers Date: Tue, 20 Oct 2026 11:22:09 -0400 Subject: [PATCH 17/56] replace AUTHORS.txt with .rst --- AUTHORS.txt | 9 --------- CHANGELOG.md | 41 ----------------------------------------- 2 files changed, 50 deletions(-) delete mode 100644 AUTHORS.txt delete mode 100644 CHANGELOG.md diff --git a/AUTHORS.txt b/AUTHORS.txt deleted file mode 100644 index a0efd439..00000000 --- a/AUTHORS.txt +++ /dev/null @@ -1,9 +0,0 @@ -Authors: - -Pavol Juhas -Chris Farrow -Simon J.L. Billinge - -Contributors: - -https://github.com/diffpy/libdiffpy/graphs/contributors diff --git a/CHANGELOG.md b/CHANGELOG.md deleted file mode 100644 index aea699b4..00000000 --- a/CHANGELOG.md +++ /dev/null @@ -1,41 +0,0 @@ -# Release notes - -## Version 1.4.0 -- 2019-03-09 - -Notable differences from version 1.3.4. - -### Added - -- Test coverage analysis at https://codecov.io/gh/diffpy/libdiffpy. -- Make scons scripts compatible with Python 3 and Python 2. -- Atomic numbers and possible ions for elements 99-103. -- Define libdiffpy version requirements for client Anaconda packages. -- Support free-standing attribute getter and setter functions. -- `uisowidth` and `bisowidth` attributes to `ConstantPeakWidth` class. - This simplifies PDF modeling of structures with uniform isotropic - atom displacements. - - -### Changed - -- Build Anaconda package with Anaconda C++ compiler. -- Adopt language standard c++11. -- Replaced boost unordered set and map types with their STL version. -- Switch to direct serialization of `PeakWidthModel`, `PeakProfile`, - `PDFEnvelope`, `PDFBaseline`, and `ScatteringFactorTable` classes and - derived types. - -### Deprecated - -- `StructureAdapter::customPQConfig` function because it permits unexpected - changes in calculators setup and complicates parallel evaluation. - -### Removed - -- Virtual class inheritance of `BaseDebyeSum` and `DebyePDFCalculator`. - -### Fixed - -- Incorrect results for crystals with off-origin inversion center. -- Incomplete `scons install` when shared library fails to compile. -- Use of invalid iterators when removing `map` items. From fa9cdced568a7766193c42d40c6627a7835c232c Mon Sep 17 00:00:00 2001 From: Caden Myers Date: Tue, 20 Oct 2026 11:22:41 -0400 Subject: [PATCH 18/56] replace CHANGELOG.md with .rst --- CHANGELOG.rst | 40 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 40 insertions(+) diff --git a/CHANGELOG.rst b/CHANGELOG.rst index f29d3b53..44e268d1 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -3,3 +3,43 @@ Release notes ============= .. current developments + +1.4.0 +===== + +Released 2019-03-09. Notable differences from version 1.3.4. + +**Added:** + +* Test coverage analysis at https://codecov.io/gh/diffpy/libdiffpy. +* Make scons scripts compatible with Python 3 and Python 2. +* Atomic numbers and possible ions for elements 99-103. +* Define libdiffpy version requirements for client Anaconda packages. +* Support free-standing attribute getter and setter functions. +* ``uisowidth`` and ``bisowidth`` attributes to ``ConstantPeakWidth`` class. + This simplifies PDF modeling of structures with uniform isotropic + atom displacements. + +**Changed:** + +* Build Anaconda package with Anaconda C++ compiler. +* Adopt language standard c++11. +* Replaced boost unordered set and map types with their STL version. +* Switch to direct serialization of ``PeakWidthModel``, ``PeakProfile``, + ``PDFEnvelope``, ``PDFBaseline``, and ``ScatteringFactorTable`` classes and + derived types. + +**Deprecated:** + +* ``StructureAdapter::customPQConfig`` function because it permits unexpected + changes in calculators setup and complicates parallel evaluation. + +**Fixed:** + +* Incorrect results for crystals with off-origin inversion center. +* Incomplete ``scons install`` when shared library fails to compile. +* Use of invalid iterators when removing ``map`` items. + +**Removed:** + +* Virtual class inheritance of ``BaseDebyeSum`` and ``DebyePDFCalculator``. From aaec8e55169f163fc0778db1fbea4c619409c574 Mon Sep 17 00:00:00 2001 From: Caden Myers Date: Tue, 20 Oct 2026 11:25:15 -0400 Subject: [PATCH 19/56] replaces AUTHORS.rst references in all source files --- .gitattributes | 1 - doc/manual/source/release.rst | 2 +- src/diffpy/Attributes.cpp | 2 +- src/diffpy/Attributes.hpp | 2 +- src/diffpy/Attributes.ipp | 2 +- src/diffpy/EventTicker.cpp | 2 +- src/diffpy/EventTicker.hpp | 2 +- src/diffpy/HasClassRegistry.hpp | 2 +- src/diffpy/HasClassRegistry.ipp | 2 +- src/diffpy/features.tpl | 2 +- src/diffpy/mathutils.hpp | 2 +- src/diffpy/mathutils.ipp | 2 +- src/diffpy/runtimepath.cpp | 2 +- src/diffpy/runtimepath.hpp | 2 +- src/diffpy/serialization.hpp | 2 +- src/diffpy/serialization.ipp | 2 +- src/diffpy/srreal/AtomRadiiTable.cpp | 2 +- src/diffpy/srreal/AtomRadiiTable.hpp | 2 +- src/diffpy/srreal/AtomUtils.cpp | 2 +- src/diffpy/srreal/AtomUtils.hpp | 2 +- src/diffpy/srreal/AtomicStructureAdapter.cpp | 2 +- src/diffpy/srreal/AtomicStructureAdapter.hpp | 2 +- src/diffpy/srreal/BVParam.cpp | 2 +- src/diffpy/srreal/BVParam.hpp | 2 +- src/diffpy/srreal/BVParametersTable.cpp | 2 +- src/diffpy/srreal/BVParametersTable.hpp | 2 +- src/diffpy/srreal/BVSCalculator.cpp | 2 +- src/diffpy/srreal/BVSCalculator.hpp | 2 +- src/diffpy/srreal/BaseBondGenerator.cpp | 2 +- src/diffpy/srreal/BaseBondGenerator.hpp | 2 +- src/diffpy/srreal/BaseDebyeSum.cpp | 2 +- src/diffpy/srreal/BaseDebyeSum.hpp | 2 +- src/diffpy/srreal/BondCalculator.cpp | 2 +- src/diffpy/srreal/BondCalculator.hpp | 2 +- src/diffpy/srreal/ConstantPeakWidth.cpp | 2 +- src/diffpy/srreal/ConstantPeakWidth.hpp | 2 +- src/diffpy/srreal/ConstantRadiiTable.cpp | 2 +- src/diffpy/srreal/ConstantRadiiTable.hpp | 2 +- src/diffpy/srreal/CroppedGaussianProfile.cpp | 2 +- src/diffpy/srreal/CroppedGaussianProfile.hpp | 2 +- src/diffpy/srreal/CrystalStructureAdapter.cpp | 2 +- src/diffpy/srreal/CrystalStructureAdapter.hpp | 2 +- src/diffpy/srreal/DebyePDFCalculator.cpp | 2 +- src/diffpy/srreal/DebyePDFCalculator.hpp | 2 +- src/diffpy/srreal/DebyeWallerPeakWidth.cpp | 2 +- src/diffpy/srreal/DebyeWallerPeakWidth.hpp | 2 +- src/diffpy/srreal/EmptyStructureAdapter.cpp | 2 +- src/diffpy/srreal/GaussianProfile.cpp | 2 +- src/diffpy/srreal/GaussianProfile.hpp | 2 +- src/diffpy/srreal/JeongPeakWidth.cpp | 2 +- src/diffpy/srreal/JeongPeakWidth.hpp | 2 +- src/diffpy/srreal/Lattice.cpp | 2 +- src/diffpy/srreal/Lattice.hpp | 2 +- src/diffpy/srreal/LinearBaseline.cpp | 2 +- src/diffpy/srreal/LinearBaseline.hpp | 2 +- src/diffpy/srreal/NoMetaStructureAdapter.cpp | 2 +- src/diffpy/srreal/NoMetaStructureAdapter.hpp | 2 +- src/diffpy/srreal/NoSymmetryStructureAdapter.cpp | 2 +- src/diffpy/srreal/NoSymmetryStructureAdapter.hpp | 2 +- src/diffpy/srreal/ObjCrystStructureAdapter.cpp | 2 +- src/diffpy/srreal/ObjCrystStructureAdapter.hpp | 2 +- src/diffpy/srreal/OverlapCalculator.cpp | 2 +- src/diffpy/srreal/OverlapCalculator.hpp | 2 +- src/diffpy/srreal/PDFBaseline.cpp | 2 +- src/diffpy/srreal/PDFBaseline.hpp | 2 +- src/diffpy/srreal/PDFCalculator.cpp | 2 +- src/diffpy/srreal/PDFCalculator.hpp | 2 +- src/diffpy/srreal/PDFEnvelope.cpp | 2 +- src/diffpy/srreal/PDFEnvelope.hpp | 2 +- src/diffpy/srreal/PDFUtils.cpp | 2 +- src/diffpy/srreal/PDFUtils.hpp | 2 +- src/diffpy/srreal/PDFUtils.ipp | 2 +- src/diffpy/srreal/PQEvaluator.cpp | 2 +- src/diffpy/srreal/PQEvaluator.hpp | 2 +- src/diffpy/srreal/PairCounter.cpp | 2 +- src/diffpy/srreal/PairCounter.hpp | 2 +- src/diffpy/srreal/PairQuantity.cpp | 2 +- src/diffpy/srreal/PairQuantity.hpp | 2 +- src/diffpy/srreal/PeakProfile.cpp | 2 +- src/diffpy/srreal/PeakProfile.hpp | 2 +- src/diffpy/srreal/PeakWidthModel.cpp | 2 +- src/diffpy/srreal/PeakWidthModel.hpp | 2 +- src/diffpy/srreal/PeriodicStructureAdapter.cpp | 2 +- src/diffpy/srreal/PeriodicStructureAdapter.hpp | 2 +- src/diffpy/srreal/PointsInSphere.cpp | 2 +- src/diffpy/srreal/PointsInSphere.hpp | 2 +- src/diffpy/srreal/QResolutionEnvelope.cpp | 2 +- src/diffpy/srreal/QResolutionEnvelope.hpp | 2 +- src/diffpy/srreal/QuantityType.hpp | 2 +- src/diffpy/srreal/R3linalg.cpp | 2 +- src/diffpy/srreal/R3linalg.hpp | 2 +- src/diffpy/srreal/SFTElectron.cpp | 2 +- src/diffpy/srreal/SFTElectron.hpp | 2 +- src/diffpy/srreal/SFTElectronNumber.cpp | 2 +- src/diffpy/srreal/SFTElectronNumber.hpp | 2 +- src/diffpy/srreal/SFTNeutron.cpp | 2 +- src/diffpy/srreal/SFTNeutron.hpp | 2 +- src/diffpy/srreal/SFTXray.cpp | 2 +- src/diffpy/srreal/SFTXray.hpp | 2 +- src/diffpy/srreal/ScaleEnvelope.cpp | 2 +- src/diffpy/srreal/ScaleEnvelope.hpp | 2 +- src/diffpy/srreal/ScatteringFactorTable.cpp | 2 +- src/diffpy/srreal/ScatteringFactorTable.hpp | 2 +- src/diffpy/srreal/SphericalShapeEnvelope.cpp | 2 +- src/diffpy/srreal/SphericalShapeEnvelope.hpp | 2 +- src/diffpy/srreal/StepCutEnvelope.cpp | 2 +- src/diffpy/srreal/StepCutEnvelope.hpp | 2 +- src/diffpy/srreal/StructureAdapter.cpp | 2 +- src/diffpy/srreal/StructureAdapter.hpp | 2 +- src/diffpy/srreal/StructureDifference.cpp | 2 +- src/diffpy/srreal/StructureDifference.hpp | 2 +- src/diffpy/srreal/ZeroBaseline.cpp | 2 +- src/diffpy/srreal/ZeroBaseline.hpp | 2 +- src/diffpy/srreal/forwardtypes.hpp | 2 +- src/diffpy/srreal/scatteringfactordata.cpp | 2 +- src/diffpy/srreal/scatteringfactordata.hpp | 2 +- src/diffpy/validators.hpp | 2 +- src/diffpy/version.cpp | 2 +- src/diffpy/version.tpl | 2 +- src/tests/TestAtomRadiiTable.hpp | 2 +- src/tests/TestAtomicStructureAdapter.hpp | 2 +- src/tests/TestAttributes.hpp | 2 +- src/tests/TestBVParametersTable.hpp | 2 +- src/tests/TestBVSCalculator.hpp | 2 +- src/tests/TestBVSObjCryst.hpp | 2 +- src/tests/TestDebyePDFCalculator.hpp | 2 +- src/tests/TestEmptyStructureAdapter.hpp | 2 +- src/tests/TestHasClassRegistry.hpp | 2 +- src/tests/TestLattice.hpp | 2 +- src/tests/TestNoMetaStructureAdapter.hpp | 2 +- src/tests/TestObjCrystStructureAdapter.hpp | 2 +- src/tests/TestOverlapCalculator.hpp | 2 +- src/tests/TestPDFBaseline.hpp | 2 +- src/tests/TestPDFCalculator.hpp | 2 +- src/tests/TestPQEvaluator.hpp | 2 +- src/tests/TestPairCounter.hpp | 2 +- src/tests/TestPeakProfile.hpp | 2 +- src/tests/TestPeakWidthModel.hpp | 2 +- src/tests/TestPeriodicStructureAdapter.hpp | 2 +- src/tests/TestPointsInSphere.hpp | 2 +- src/tests/TestR3linalg.hpp | 2 +- src/tests/TestRuntimePath.hpp | 2 +- src/tests/TestScatteringFactorTable.hpp | 2 +- src/tests/TestSphericalShapeEnvelope.hpp | 2 +- src/tests/TestStepCutEnvelope.hpp | 2 +- src/tests/objcryst_helpers.cpp | 2 +- src/tests/objcryst_helpers.hpp | 2 +- src/tests/serialization_helpers.hpp | 2 +- src/tests/test_custompqconfig.hpp | 2 +- src/tests/test_helpers.cpp | 2 +- src/tests/test_helpers.hpp | 2 +- 151 files changed, 150 insertions(+), 151 deletions(-) diff --git a/.gitattributes b/.gitattributes index 503fc76f..0628b707 100644 --- a/.gitattributes +++ b/.gitattributes @@ -1,6 +1,5 @@ /.gitattributes export-ignore /.gitignore export-ignore /.travis* export-ignore -/conda-recipe/ export-ignore /doc export-ignore gitarchive.cfg export-subst diff --git a/doc/manual/source/release.rst b/doc/manual/source/release.rst index 7ec4f81d..2f0bd47f 100644 --- a/doc/manual/source/release.rst +++ b/doc/manual/source/release.rst @@ -1,3 +1,3 @@ .. index:: release notes -.. mdinclude:: ../../../CHANGELOG.md +.. include:: ../../../CHANGELOG.rst diff --git a/src/diffpy/Attributes.cpp b/src/diffpy/Attributes.cpp index ab2db890..f555863a 100644 --- a/src/diffpy/Attributes.cpp +++ b/src/diffpy/Attributes.cpp @@ -7,7 +7,7 @@ * * File coded by: Pavol Juhas * -* See AUTHORS.txt for a list of people who contributed. +* See AUTHORS.rst for a list of people who contributed. * See LICENSE_DANSE.txt for license information. * ****************************************************************************** diff --git a/src/diffpy/Attributes.hpp b/src/diffpy/Attributes.hpp index 31e1e180..af4c074c 100644 --- a/src/diffpy/Attributes.hpp +++ b/src/diffpy/Attributes.hpp @@ -7,7 +7,7 @@ * * File coded by: Pavol Juhas * -* See AUTHORS.txt for a list of people who contributed. +* See AUTHORS.rst for a list of people who contributed. * See LICENSE_DANSE.txt for license information. * ****************************************************************************** diff --git a/src/diffpy/Attributes.ipp b/src/diffpy/Attributes.ipp index 005c8e31..e35a3ba0 100644 --- a/src/diffpy/Attributes.ipp +++ b/src/diffpy/Attributes.ipp @@ -7,7 +7,7 @@ * * File coded by: Pavol Juhas * -* See AUTHORS.txt for a list of people who contributed. +* See AUTHORS.rst for a list of people who contributed. * See LICENSE_DANSE.txt for license information. * ****************************************************************************** diff --git a/src/diffpy/EventTicker.cpp b/src/diffpy/EventTicker.cpp index 5042cc7f..c2d30c3e 100644 --- a/src/diffpy/EventTicker.cpp +++ b/src/diffpy/EventTicker.cpp @@ -7,7 +7,7 @@ * * File coded by: Pavol Juhas * -* See AUTHORS.txt for a list of people who contributed. +* See AUTHORS.rst for a list of people who contributed. * See LICENSE.rst for license information. * ****************************************************************************** diff --git a/src/diffpy/EventTicker.hpp b/src/diffpy/EventTicker.hpp index 0efbe168..cbe394fa 100644 --- a/src/diffpy/EventTicker.hpp +++ b/src/diffpy/EventTicker.hpp @@ -7,7 +7,7 @@ * * File coded by: Pavol Juhas * -* See AUTHORS.txt for a list of people who contributed. +* See AUTHORS.rst for a list of people who contributed. * See LICENSE.rst for license information. * ****************************************************************************** diff --git a/src/diffpy/HasClassRegistry.hpp b/src/diffpy/HasClassRegistry.hpp index 538bd0ad..87832b18 100644 --- a/src/diffpy/HasClassRegistry.hpp +++ b/src/diffpy/HasClassRegistry.hpp @@ -7,7 +7,7 @@ * * File coded by: Pavol Juhas * -* See AUTHORS.txt for a list of people who contributed. +* See AUTHORS.rst for a list of people who contributed. * See LICENSE_DANSE.txt for license information. * ****************************************************************************** diff --git a/src/diffpy/HasClassRegistry.ipp b/src/diffpy/HasClassRegistry.ipp index afc744c8..10577942 100644 --- a/src/diffpy/HasClassRegistry.ipp +++ b/src/diffpy/HasClassRegistry.ipp @@ -7,7 +7,7 @@ * * File coded by: Pavol Juhas * -* See AUTHORS.txt for a list of people who contributed. +* See AUTHORS.rst for a list of people who contributed. * See LICENSE_DANSE.txt for license information. * ****************************************************************************** diff --git a/src/diffpy/features.tpl b/src/diffpy/features.tpl index 654604dd..526bdf5f 100644 --- a/src/diffpy/features.tpl +++ b/src/diffpy/features.tpl @@ -7,7 +7,7 @@ * * File coded by: Pavol Juhas * -* See AUTHORS.txt for a list of people who contributed. +* See AUTHORS.rst for a list of people who contributed. * See LICENSE.rst for license information. * ****************************************************************************** diff --git a/src/diffpy/mathutils.hpp b/src/diffpy/mathutils.hpp index 46894afd..bce53a2a 100644 --- a/src/diffpy/mathutils.hpp +++ b/src/diffpy/mathutils.hpp @@ -7,7 +7,7 @@ * * File coded by: Pavol Juhas * -* See AUTHORS.txt for a list of people who contributed. +* See AUTHORS.rst for a list of people who contributed. * See LICENSE_DANSE.txt for license information. * ****************************************************************************** diff --git a/src/diffpy/mathutils.ipp b/src/diffpy/mathutils.ipp index c4fc0a44..4181895e 100644 --- a/src/diffpy/mathutils.ipp +++ b/src/diffpy/mathutils.ipp @@ -7,7 +7,7 @@ * * File coded by: Pavol Juhas * -* See AUTHORS.txt for a list of people who contributed. +* See AUTHORS.rst for a list of people who contributed. * See LICENSE_DANSE.txt for license information. * ****************************************************************************** diff --git a/src/diffpy/runtimepath.cpp b/src/diffpy/runtimepath.cpp index 7b6db35f..05551408 100644 --- a/src/diffpy/runtimepath.cpp +++ b/src/diffpy/runtimepath.cpp @@ -7,7 +7,7 @@ * * File coded by: Pavol Juhas * -* See AUTHORS.txt for a list of people who contributed. +* See AUTHORS.rst for a list of people who contributed. * See LICENSE.rst for license information. * ****************************************************************************** diff --git a/src/diffpy/runtimepath.hpp b/src/diffpy/runtimepath.hpp index 96b14677..587516c7 100644 --- a/src/diffpy/runtimepath.hpp +++ b/src/diffpy/runtimepath.hpp @@ -7,7 +7,7 @@ * * File coded by: Pavol Juhas * -* See AUTHORS.txt for a list of people who contributed. +* See AUTHORS.rst for a list of people who contributed. * See LICENSE.rst for license information. * ****************************************************************************** diff --git a/src/diffpy/serialization.hpp b/src/diffpy/serialization.hpp index 9d80d14d..e8433ab6 100644 --- a/src/diffpy/serialization.hpp +++ b/src/diffpy/serialization.hpp @@ -7,7 +7,7 @@ * * File coded by: Pavol Juhas * -* See AUTHORS.txt for a list of people who contributed. +* See AUTHORS.rst for a list of people who contributed. * See LICENSE_DANSE.txt for license information. * ****************************************************************************** diff --git a/src/diffpy/serialization.ipp b/src/diffpy/serialization.ipp index 863671f8..f9e6b342 100644 --- a/src/diffpy/serialization.ipp +++ b/src/diffpy/serialization.ipp @@ -7,7 +7,7 @@ * * File coded by: Pavol Juhas * -* See AUTHORS.txt for a list of people who contributed. +* See AUTHORS.rst for a list of people who contributed. * See LICENSE.rst for license information. * ****************************************************************************** diff --git a/src/diffpy/srreal/AtomRadiiTable.cpp b/src/diffpy/srreal/AtomRadiiTable.cpp index 20995f69..a28225c2 100644 --- a/src/diffpy/srreal/AtomRadiiTable.cpp +++ b/src/diffpy/srreal/AtomRadiiTable.cpp @@ -7,7 +7,7 @@ * * File coded by: Pavol Juhas * -* See AUTHORS.txt for a list of people who contributed. +* See AUTHORS.rst for a list of people who contributed. * See LICENSE_DANSE.txt for license information. * ****************************************************************************** diff --git a/src/diffpy/srreal/AtomRadiiTable.hpp b/src/diffpy/srreal/AtomRadiiTable.hpp index 830adc40..45af7a72 100644 --- a/src/diffpy/srreal/AtomRadiiTable.hpp +++ b/src/diffpy/srreal/AtomRadiiTable.hpp @@ -7,7 +7,7 @@ * * File coded by: Pavol Juhas * -* See AUTHORS.txt for a list of people who contributed. +* See AUTHORS.rst for a list of people who contributed. * See LICENSE_DANSE.txt for license information. * ****************************************************************************** diff --git a/src/diffpy/srreal/AtomUtils.cpp b/src/diffpy/srreal/AtomUtils.cpp index 7e0ed837..bc39cbed 100644 --- a/src/diffpy/srreal/AtomUtils.cpp +++ b/src/diffpy/srreal/AtomUtils.cpp @@ -7,7 +7,7 @@ * * File coded by: Pavol Juhas * -* See AUTHORS.txt for a list of people who contributed. +* See AUTHORS.rst for a list of people who contributed. * See LICENSE.rst for license information. * ****************************************************************************** diff --git a/src/diffpy/srreal/AtomUtils.hpp b/src/diffpy/srreal/AtomUtils.hpp index afdcd51f..346d7c97 100644 --- a/src/diffpy/srreal/AtomUtils.hpp +++ b/src/diffpy/srreal/AtomUtils.hpp @@ -7,7 +7,7 @@ * * File coded by: Pavol Juhas * -* See AUTHORS.txt for a list of people who contributed. +* See AUTHORS.rst for a list of people who contributed. * See LICENSE.rst for license information. * ****************************************************************************** diff --git a/src/diffpy/srreal/AtomicStructureAdapter.cpp b/src/diffpy/srreal/AtomicStructureAdapter.cpp index d4856c81..6d432820 100644 --- a/src/diffpy/srreal/AtomicStructureAdapter.cpp +++ b/src/diffpy/srreal/AtomicStructureAdapter.cpp @@ -7,7 +7,7 @@ * * File coded by: Pavol Juhas * -* See AUTHORS.txt for a list of people who contributed. +* See AUTHORS.rst for a list of people who contributed. * See LICENSE_DANSE.txt for license information. * ****************************************************************************** diff --git a/src/diffpy/srreal/AtomicStructureAdapter.hpp b/src/diffpy/srreal/AtomicStructureAdapter.hpp index e6eb22fa..248d1b11 100644 --- a/src/diffpy/srreal/AtomicStructureAdapter.hpp +++ b/src/diffpy/srreal/AtomicStructureAdapter.hpp @@ -7,7 +7,7 @@ * * File coded by: Pavol Juhas * -* See AUTHORS.txt for a list of people who contributed. +* See AUTHORS.rst for a list of people who contributed. * See LICENSE_DANSE.txt for license information. * ****************************************************************************** diff --git a/src/diffpy/srreal/BVParam.cpp b/src/diffpy/srreal/BVParam.cpp index 74601708..9db906bd 100644 --- a/src/diffpy/srreal/BVParam.cpp +++ b/src/diffpy/srreal/BVParam.cpp @@ -7,7 +7,7 @@ * * File coded by: Pavol Juhas * -* See AUTHORS.txt for a list of people who contributed. +* See AUTHORS.rst for a list of people who contributed. * See LICENSE_DANSE.txt for license information. * ****************************************************************************** diff --git a/src/diffpy/srreal/BVParam.hpp b/src/diffpy/srreal/BVParam.hpp index 061fb98c..ced19a5f 100644 --- a/src/diffpy/srreal/BVParam.hpp +++ b/src/diffpy/srreal/BVParam.hpp @@ -7,7 +7,7 @@ * * File coded by: Pavol Juhas * -* See AUTHORS.txt for a list of people who contributed. +* See AUTHORS.rst for a list of people who contributed. * See LICENSE_DANSE.txt for license information. * ****************************************************************************** diff --git a/src/diffpy/srreal/BVParametersTable.cpp b/src/diffpy/srreal/BVParametersTable.cpp index 896cd463..2d53afe4 100644 --- a/src/diffpy/srreal/BVParametersTable.cpp +++ b/src/diffpy/srreal/BVParametersTable.cpp @@ -7,7 +7,7 @@ * * File coded by: Pavol Juhas * -* See AUTHORS.txt for a list of people who contributed. +* See AUTHORS.rst for a list of people who contributed. * See LICENSE_DANSE.txt for license information. * ****************************************************************************** diff --git a/src/diffpy/srreal/BVParametersTable.hpp b/src/diffpy/srreal/BVParametersTable.hpp index 9a900f4e..7fd6fb92 100644 --- a/src/diffpy/srreal/BVParametersTable.hpp +++ b/src/diffpy/srreal/BVParametersTable.hpp @@ -7,7 +7,7 @@ * * File coded by: Pavol Juhas * -* See AUTHORS.txt for a list of people who contributed. +* See AUTHORS.rst for a list of people who contributed. * See LICENSE_DANSE.txt for license information. * ****************************************************************************** diff --git a/src/diffpy/srreal/BVSCalculator.cpp b/src/diffpy/srreal/BVSCalculator.cpp index 44e5de46..0b3d07ce 100644 --- a/src/diffpy/srreal/BVSCalculator.cpp +++ b/src/diffpy/srreal/BVSCalculator.cpp @@ -7,7 +7,7 @@ * * File coded by: Pavol Juhas * -* See AUTHORS.txt for a list of people who contributed. +* See AUTHORS.rst for a list of people who contributed. * See LICENSE_DANSE.txt for license information. * ****************************************************************************** diff --git a/src/diffpy/srreal/BVSCalculator.hpp b/src/diffpy/srreal/BVSCalculator.hpp index c864b31b..86a6f7aa 100644 --- a/src/diffpy/srreal/BVSCalculator.hpp +++ b/src/diffpy/srreal/BVSCalculator.hpp @@ -7,7 +7,7 @@ * * File coded by: Pavol Juhas * -* See AUTHORS.txt for a list of people who contributed. +* See AUTHORS.rst for a list of people who contributed. * See LICENSE_DANSE.txt for license information. * ****************************************************************************** diff --git a/src/diffpy/srreal/BaseBondGenerator.cpp b/src/diffpy/srreal/BaseBondGenerator.cpp index 6fa3b002..c4374ea2 100644 --- a/src/diffpy/srreal/BaseBondGenerator.cpp +++ b/src/diffpy/srreal/BaseBondGenerator.cpp @@ -7,7 +7,7 @@ * * File coded by: Pavol Juhas * -* See AUTHORS.txt for a list of people who contributed. +* See AUTHORS.rst for a list of people who contributed. * See LICENSE_DANSE.txt for license information. * ****************************************************************************** diff --git a/src/diffpy/srreal/BaseBondGenerator.hpp b/src/diffpy/srreal/BaseBondGenerator.hpp index 691c4666..bcee24c4 100644 --- a/src/diffpy/srreal/BaseBondGenerator.hpp +++ b/src/diffpy/srreal/BaseBondGenerator.hpp @@ -7,7 +7,7 @@ * * File coded by: Pavol Juhas * -* See AUTHORS.txt for a list of people who contributed. +* See AUTHORS.rst for a list of people who contributed. * See LICENSE_DANSE.txt for license information. * ****************************************************************************** diff --git a/src/diffpy/srreal/BaseDebyeSum.cpp b/src/diffpy/srreal/BaseDebyeSum.cpp index e4165694..b60b33c5 100644 --- a/src/diffpy/srreal/BaseDebyeSum.cpp +++ b/src/diffpy/srreal/BaseDebyeSum.cpp @@ -7,7 +7,7 @@ * * File coded by: Pavol Juhas * -* See AUTHORS.txt for a list of people who contributed. +* See AUTHORS.rst for a list of people who contributed. * See LICENSE_DANSE.txt for license information. * ****************************************************************************** diff --git a/src/diffpy/srreal/BaseDebyeSum.hpp b/src/diffpy/srreal/BaseDebyeSum.hpp index 5eaea6f5..e70f98df 100644 --- a/src/diffpy/srreal/BaseDebyeSum.hpp +++ b/src/diffpy/srreal/BaseDebyeSum.hpp @@ -7,7 +7,7 @@ * * File coded by: Pavol Juhas * -* See AUTHORS.txt for a list of people who contributed. +* See AUTHORS.rst for a list of people who contributed. * See LICENSE_DANSE.txt for license information. * ****************************************************************************** diff --git a/src/diffpy/srreal/BondCalculator.cpp b/src/diffpy/srreal/BondCalculator.cpp index 21e8435a..958a0872 100644 --- a/src/diffpy/srreal/BondCalculator.cpp +++ b/src/diffpy/srreal/BondCalculator.cpp @@ -7,7 +7,7 @@ * * File coded by: Pavol Juhas * -* See AUTHORS.txt for a list of people who contributed. +* See AUTHORS.rst for a list of people who contributed. * See LICENSE_DANSE.txt for license information. * ****************************************************************************** diff --git a/src/diffpy/srreal/BondCalculator.hpp b/src/diffpy/srreal/BondCalculator.hpp index 49c6876f..50aa5799 100644 --- a/src/diffpy/srreal/BondCalculator.hpp +++ b/src/diffpy/srreal/BondCalculator.hpp @@ -7,7 +7,7 @@ * * File coded by: Pavol Juhas * -* See AUTHORS.txt for a list of people who contributed. +* See AUTHORS.rst for a list of people who contributed. * See LICENSE_DANSE.txt for license information. * ****************************************************************************** diff --git a/src/diffpy/srreal/ConstantPeakWidth.cpp b/src/diffpy/srreal/ConstantPeakWidth.cpp index 32b8bc78..599ca85b 100644 --- a/src/diffpy/srreal/ConstantPeakWidth.cpp +++ b/src/diffpy/srreal/ConstantPeakWidth.cpp @@ -7,7 +7,7 @@ * * File coded by: Pavol Juhas * -* See AUTHORS.txt for a list of people who contributed. +* See AUTHORS.rst for a list of people who contributed. * See LICENSE_DANSE.txt for license information. * ****************************************************************************** diff --git a/src/diffpy/srreal/ConstantPeakWidth.hpp b/src/diffpy/srreal/ConstantPeakWidth.hpp index 8c0f552b..a1edceb8 100644 --- a/src/diffpy/srreal/ConstantPeakWidth.hpp +++ b/src/diffpy/srreal/ConstantPeakWidth.hpp @@ -7,7 +7,7 @@ * * File coded by: Pavol Juhas * -* See AUTHORS.txt for a list of people who contributed. +* See AUTHORS.rst for a list of people who contributed. * See LICENSE_DANSE.txt for license information. * ****************************************************************************** diff --git a/src/diffpy/srreal/ConstantRadiiTable.cpp b/src/diffpy/srreal/ConstantRadiiTable.cpp index 4a1e3130..ba1145e0 100644 --- a/src/diffpy/srreal/ConstantRadiiTable.cpp +++ b/src/diffpy/srreal/ConstantRadiiTable.cpp @@ -7,7 +7,7 @@ * * File coded by: Pavol Juhas * -* See AUTHORS.txt for a list of people who contributed. +* See AUTHORS.rst for a list of people who contributed. * See LICENSE_DANSE.txt for license information. * ****************************************************************************** diff --git a/src/diffpy/srreal/ConstantRadiiTable.hpp b/src/diffpy/srreal/ConstantRadiiTable.hpp index 5a047c75..f2f04684 100644 --- a/src/diffpy/srreal/ConstantRadiiTable.hpp +++ b/src/diffpy/srreal/ConstantRadiiTable.hpp @@ -7,7 +7,7 @@ * * File coded by: Pavol Juhas * -* See AUTHORS.txt for a list of people who contributed. +* See AUTHORS.rst for a list of people who contributed. * See LICENSE_DANSE.txt for license information. * ****************************************************************************** diff --git a/src/diffpy/srreal/CroppedGaussianProfile.cpp b/src/diffpy/srreal/CroppedGaussianProfile.cpp index aabdba0e..19fe7468 100644 --- a/src/diffpy/srreal/CroppedGaussianProfile.cpp +++ b/src/diffpy/srreal/CroppedGaussianProfile.cpp @@ -7,7 +7,7 @@ * * File coded by: Pavol Juhas * -* See AUTHORS.txt for a list of people who contributed. +* See AUTHORS.rst for a list of people who contributed. * See LICENSE_DANSE.txt for license information. * ****************************************************************************** diff --git a/src/diffpy/srreal/CroppedGaussianProfile.hpp b/src/diffpy/srreal/CroppedGaussianProfile.hpp index ed1573ea..a1a92890 100644 --- a/src/diffpy/srreal/CroppedGaussianProfile.hpp +++ b/src/diffpy/srreal/CroppedGaussianProfile.hpp @@ -7,7 +7,7 @@ * * File coded by: Pavol Juhas * -* See AUTHORS.txt for a list of people who contributed. +* See AUTHORS.rst for a list of people who contributed. * See LICENSE_DANSE.txt for license information. * ****************************************************************************** diff --git a/src/diffpy/srreal/CrystalStructureAdapter.cpp b/src/diffpy/srreal/CrystalStructureAdapter.cpp index 9a2151d2..38ceda6c 100644 --- a/src/diffpy/srreal/CrystalStructureAdapter.cpp +++ b/src/diffpy/srreal/CrystalStructureAdapter.cpp @@ -7,7 +7,7 @@ * * File coded by: Pavol Juhas * -* See AUTHORS.txt for a list of people who contributed. +* See AUTHORS.rst for a list of people who contributed. * See LICENSE.rst for license information. * ****************************************************************************** diff --git a/src/diffpy/srreal/CrystalStructureAdapter.hpp b/src/diffpy/srreal/CrystalStructureAdapter.hpp index f8ad6dc8..767aa805 100644 --- a/src/diffpy/srreal/CrystalStructureAdapter.hpp +++ b/src/diffpy/srreal/CrystalStructureAdapter.hpp @@ -7,7 +7,7 @@ * * File coded by: Pavol Juhas * -* See AUTHORS.txt for a list of people who contributed. +* See AUTHORS.rst for a list of people who contributed. * See LICENSE.rst for license information. * ****************************************************************************** diff --git a/src/diffpy/srreal/DebyePDFCalculator.cpp b/src/diffpy/srreal/DebyePDFCalculator.cpp index d77198ec..e200755d 100644 --- a/src/diffpy/srreal/DebyePDFCalculator.cpp +++ b/src/diffpy/srreal/DebyePDFCalculator.cpp @@ -7,7 +7,7 @@ * * File coded by: Pavol Juhas, Chris Farrow * -* See AUTHORS.txt for a list of people who contributed. +* See AUTHORS.rst for a list of people who contributed. * See LICENSE_DANSE.txt for license information. * ****************************************************************************** diff --git a/src/diffpy/srreal/DebyePDFCalculator.hpp b/src/diffpy/srreal/DebyePDFCalculator.hpp index 512e2884..e23b2b5c 100644 --- a/src/diffpy/srreal/DebyePDFCalculator.hpp +++ b/src/diffpy/srreal/DebyePDFCalculator.hpp @@ -7,7 +7,7 @@ * * File coded by: Pavol Juhas, Chris Farrow * -* See AUTHORS.txt for a list of people who contributed. +* See AUTHORS.rst for a list of people who contributed. * See LICENSE_DANSE.txt for license information. * ****************************************************************************** diff --git a/src/diffpy/srreal/DebyeWallerPeakWidth.cpp b/src/diffpy/srreal/DebyeWallerPeakWidth.cpp index 10c6bfe6..bb82a6a3 100644 --- a/src/diffpy/srreal/DebyeWallerPeakWidth.cpp +++ b/src/diffpy/srreal/DebyeWallerPeakWidth.cpp @@ -7,7 +7,7 @@ * * File coded by: Christopher Farrow, Pavol Juhas * -* See AUTHORS.txt for a list of people who contributed. +* See AUTHORS.rst for a list of people who contributed. * See LICENSE_DANSE.txt for license information. * ****************************************************************************** diff --git a/src/diffpy/srreal/DebyeWallerPeakWidth.hpp b/src/diffpy/srreal/DebyeWallerPeakWidth.hpp index c900f7f1..7f4a5b08 100644 --- a/src/diffpy/srreal/DebyeWallerPeakWidth.hpp +++ b/src/diffpy/srreal/DebyeWallerPeakWidth.hpp @@ -7,7 +7,7 @@ * * File coded by: Christopher Farrow, Pavol Juhas * -* See AUTHORS.txt for a list of people who contributed. +* See AUTHORS.rst for a list of people who contributed. * See LICENSE_DANSE.txt for license information. * ****************************************************************************** diff --git a/src/diffpy/srreal/EmptyStructureAdapter.cpp b/src/diffpy/srreal/EmptyStructureAdapter.cpp index 9fc29db1..1dc5b2a5 100644 --- a/src/diffpy/srreal/EmptyStructureAdapter.cpp +++ b/src/diffpy/srreal/EmptyStructureAdapter.cpp @@ -7,7 +7,7 @@ * * File coded by: Pavol Juhas * -* See AUTHORS.txt for a list of people who contributed. +* See AUTHORS.rst for a list of people who contributed. * See LICENSE.rst for license information. * ****************************************************************************** diff --git a/src/diffpy/srreal/GaussianProfile.cpp b/src/diffpy/srreal/GaussianProfile.cpp index 97622474..3b833752 100644 --- a/src/diffpy/srreal/GaussianProfile.cpp +++ b/src/diffpy/srreal/GaussianProfile.cpp @@ -7,7 +7,7 @@ * * File coded by: Pavol Juhas * -* See AUTHORS.txt for a list of people who contributed. +* See AUTHORS.rst for a list of people who contributed. * See LICENSE_DANSE.txt for license information. * ****************************************************************************** diff --git a/src/diffpy/srreal/GaussianProfile.hpp b/src/diffpy/srreal/GaussianProfile.hpp index 21b65b65..8885e9bc 100644 --- a/src/diffpy/srreal/GaussianProfile.hpp +++ b/src/diffpy/srreal/GaussianProfile.hpp @@ -7,7 +7,7 @@ * * File coded by: Pavol Juhas * -* See AUTHORS.txt for a list of people who contributed. +* See AUTHORS.rst for a list of people who contributed. * See LICENSE_DANSE.txt for license information. * ****************************************************************************** diff --git a/src/diffpy/srreal/JeongPeakWidth.cpp b/src/diffpy/srreal/JeongPeakWidth.cpp index 7cd9679e..ca2ef297 100644 --- a/src/diffpy/srreal/JeongPeakWidth.cpp +++ b/src/diffpy/srreal/JeongPeakWidth.cpp @@ -7,7 +7,7 @@ * * File coded by: Christopher Farrow, Pavol Juhas * -* See AUTHORS.txt for a list of people who contributed. +* See AUTHORS.rst for a list of people who contributed. * See LICENSE_DANSE.txt for license information. * ****************************************************************************** diff --git a/src/diffpy/srreal/JeongPeakWidth.hpp b/src/diffpy/srreal/JeongPeakWidth.hpp index 1acd9a19..f1606b8b 100644 --- a/src/diffpy/srreal/JeongPeakWidth.hpp +++ b/src/diffpy/srreal/JeongPeakWidth.hpp @@ -7,7 +7,7 @@ * * File coded by: Christopher Farrow, Pavol Juhas * -* See AUTHORS.txt for a list of people who contributed. +* See AUTHORS.rst for a list of people who contributed. * See LICENSE_DANSE.txt for license information. * ****************************************************************************** diff --git a/src/diffpy/srreal/Lattice.cpp b/src/diffpy/srreal/Lattice.cpp index 6fd171d4..3b62e841 100644 --- a/src/diffpy/srreal/Lattice.cpp +++ b/src/diffpy/srreal/Lattice.cpp @@ -7,7 +7,7 @@ * * File coded by: Pavol Juhas * -* See AUTHORS.txt for a list of people who contributed. +* See AUTHORS.rst for a list of people who contributed. * See LICENSE_DANSE.txt for license information. * ****************************************************************************** diff --git a/src/diffpy/srreal/Lattice.hpp b/src/diffpy/srreal/Lattice.hpp index 4e9146c5..b7304662 100644 --- a/src/diffpy/srreal/Lattice.hpp +++ b/src/diffpy/srreal/Lattice.hpp @@ -7,7 +7,7 @@ * * File coded by: Pavol Juhas * -* See AUTHORS.txt for a list of people who contributed. +* See AUTHORS.rst for a list of people who contributed. * See LICENSE_DANSE.txt for license information. * ****************************************************************************** diff --git a/src/diffpy/srreal/LinearBaseline.cpp b/src/diffpy/srreal/LinearBaseline.cpp index 861d82a3..42475228 100644 --- a/src/diffpy/srreal/LinearBaseline.cpp +++ b/src/diffpy/srreal/LinearBaseline.cpp @@ -7,7 +7,7 @@ * * File coded by: Pavol Juhas * -* See AUTHORS.txt for a list of people who contributed. +* See AUTHORS.rst for a list of people who contributed. * See LICENSE_DANSE.txt for license information. * ****************************************************************************** diff --git a/src/diffpy/srreal/LinearBaseline.hpp b/src/diffpy/srreal/LinearBaseline.hpp index ab86aa7a..a4176fd6 100644 --- a/src/diffpy/srreal/LinearBaseline.hpp +++ b/src/diffpy/srreal/LinearBaseline.hpp @@ -7,7 +7,7 @@ * * File coded by: Pavol Juhas * -* See AUTHORS.txt for a list of people who contributed. +* See AUTHORS.rst for a list of people who contributed. * See LICENSE_DANSE.txt for license information. * ****************************************************************************** diff --git a/src/diffpy/srreal/NoMetaStructureAdapter.cpp b/src/diffpy/srreal/NoMetaStructureAdapter.cpp index f5d7366c..1675ed78 100644 --- a/src/diffpy/srreal/NoMetaStructureAdapter.cpp +++ b/src/diffpy/srreal/NoMetaStructureAdapter.cpp @@ -7,7 +7,7 @@ * * File coded by: Pavol Juhas * -* See AUTHORS.txt for a list of people who contributed. +* See AUTHORS.rst for a list of people who contributed. * See LICENSE_DANSE.txt for license information. * ****************************************************************************** diff --git a/src/diffpy/srreal/NoMetaStructureAdapter.hpp b/src/diffpy/srreal/NoMetaStructureAdapter.hpp index da454fcc..c5296a90 100644 --- a/src/diffpy/srreal/NoMetaStructureAdapter.hpp +++ b/src/diffpy/srreal/NoMetaStructureAdapter.hpp @@ -7,7 +7,7 @@ * * File coded by: Pavol Juhas * -* See AUTHORS.txt for a list of people who contributed. +* See AUTHORS.rst for a list of people who contributed. * See LICENSE_DANSE.txt for license information. * ****************************************************************************** diff --git a/src/diffpy/srreal/NoSymmetryStructureAdapter.cpp b/src/diffpy/srreal/NoSymmetryStructureAdapter.cpp index 1502f63b..ac7d7781 100644 --- a/src/diffpy/srreal/NoSymmetryStructureAdapter.cpp +++ b/src/diffpy/srreal/NoSymmetryStructureAdapter.cpp @@ -7,7 +7,7 @@ * * File coded by: Pavol Juhas * -* See AUTHORS.txt for a list of people who contributed. +* See AUTHORS.rst for a list of people who contributed. * See LICENSE_DANSE.txt for license information. * ****************************************************************************** diff --git a/src/diffpy/srreal/NoSymmetryStructureAdapter.hpp b/src/diffpy/srreal/NoSymmetryStructureAdapter.hpp index 5d8d87c4..4f9561e4 100644 --- a/src/diffpy/srreal/NoSymmetryStructureAdapter.hpp +++ b/src/diffpy/srreal/NoSymmetryStructureAdapter.hpp @@ -7,7 +7,7 @@ * * File coded by: Pavol Juhas * -* See AUTHORS.txt for a list of people who contributed. +* See AUTHORS.rst for a list of people who contributed. * See LICENSE_DANSE.txt for license information. * ****************************************************************************** diff --git a/src/diffpy/srreal/ObjCrystStructureAdapter.cpp b/src/diffpy/srreal/ObjCrystStructureAdapter.cpp index 2b5e5660..bee38875 100644 --- a/src/diffpy/srreal/ObjCrystStructureAdapter.cpp +++ b/src/diffpy/srreal/ObjCrystStructureAdapter.cpp @@ -7,7 +7,7 @@ * * File coded by: Chris Farrow * -* See AUTHORS.txt for a list of people who contributed. +* See AUTHORS.rst for a list of people who contributed. * See LICENSE_DANSE.txt for license information. * ****************************************************************************** diff --git a/src/diffpy/srreal/ObjCrystStructureAdapter.hpp b/src/diffpy/srreal/ObjCrystStructureAdapter.hpp index 0cb21cc7..77268c78 100644 --- a/src/diffpy/srreal/ObjCrystStructureAdapter.hpp +++ b/src/diffpy/srreal/ObjCrystStructureAdapter.hpp @@ -7,7 +7,7 @@ * * File coded by: Chris Farrow * -* See AUTHORS.txt for a list of people who contributed. +* See AUTHORS.rst for a list of people who contributed. * See LICENSE_DANSE.txt for license information. * ****************************************************************************** diff --git a/src/diffpy/srreal/OverlapCalculator.cpp b/src/diffpy/srreal/OverlapCalculator.cpp index 4fc638a9..2bf0360c 100644 --- a/src/diffpy/srreal/OverlapCalculator.cpp +++ b/src/diffpy/srreal/OverlapCalculator.cpp @@ -7,7 +7,7 @@ * * File coded by: Pavol Juhas * -* See AUTHORS.txt for a list of people who contributed. +* See AUTHORS.rst for a list of people who contributed. * See LICENSE_DANSE.txt for license information. * ****************************************************************************** diff --git a/src/diffpy/srreal/OverlapCalculator.hpp b/src/diffpy/srreal/OverlapCalculator.hpp index 0a6b5763..820b9f9a 100644 --- a/src/diffpy/srreal/OverlapCalculator.hpp +++ b/src/diffpy/srreal/OverlapCalculator.hpp @@ -7,7 +7,7 @@ * * File coded by: Pavol Juhas * -* See AUTHORS.txt for a list of people who contributed. +* See AUTHORS.rst for a list of people who contributed. * See LICENSE_DANSE.txt for license information. * ****************************************************************************** diff --git a/src/diffpy/srreal/PDFBaseline.cpp b/src/diffpy/srreal/PDFBaseline.cpp index 96525875..1c4938f6 100644 --- a/src/diffpy/srreal/PDFBaseline.cpp +++ b/src/diffpy/srreal/PDFBaseline.cpp @@ -7,7 +7,7 @@ * * File coded by: Pavol Juhas * -* See AUTHORS.txt for a list of people who contributed. +* See AUTHORS.rst for a list of people who contributed. * See LICENSE_DANSE.txt for license information. * ****************************************************************************** diff --git a/src/diffpy/srreal/PDFBaseline.hpp b/src/diffpy/srreal/PDFBaseline.hpp index 5329828f..bed16f07 100644 --- a/src/diffpy/srreal/PDFBaseline.hpp +++ b/src/diffpy/srreal/PDFBaseline.hpp @@ -7,7 +7,7 @@ * * File coded by: Pavol Juhas * -* See AUTHORS.txt for a list of people who contributed. +* See AUTHORS.rst for a list of people who contributed. * See LICENSE_DANSE.txt for license information. * ****************************************************************************** diff --git a/src/diffpy/srreal/PDFCalculator.cpp b/src/diffpy/srreal/PDFCalculator.cpp index 47c7189a..262b4cbf 100644 --- a/src/diffpy/srreal/PDFCalculator.cpp +++ b/src/diffpy/srreal/PDFCalculator.cpp @@ -7,7 +7,7 @@ * * File coded by: Pavol Juhas * -* See AUTHORS.txt for a list of people who contributed. +* See AUTHORS.rst for a list of people who contributed. * See LICENSE_DANSE.txt for license information. * ****************************************************************************** diff --git a/src/diffpy/srreal/PDFCalculator.hpp b/src/diffpy/srreal/PDFCalculator.hpp index 79220e0a..9a96b8f1 100644 --- a/src/diffpy/srreal/PDFCalculator.hpp +++ b/src/diffpy/srreal/PDFCalculator.hpp @@ -7,7 +7,7 @@ * * File coded by: Pavol Juhas * -* See AUTHORS.txt for a list of people who contributed. +* See AUTHORS.rst for a list of people who contributed. * See LICENSE_DANSE.txt for license information. * ****************************************************************************** diff --git a/src/diffpy/srreal/PDFEnvelope.cpp b/src/diffpy/srreal/PDFEnvelope.cpp index 04023176..8c646107 100644 --- a/src/diffpy/srreal/PDFEnvelope.cpp +++ b/src/diffpy/srreal/PDFEnvelope.cpp @@ -7,7 +7,7 @@ * * File coded by: Pavol Juhas * -* See AUTHORS.txt for a list of people who contributed. +* See AUTHORS.rst for a list of people who contributed. * See LICENSE_DANSE.txt for license information. * ****************************************************************************** diff --git a/src/diffpy/srreal/PDFEnvelope.hpp b/src/diffpy/srreal/PDFEnvelope.hpp index f7035737..5e82036d 100644 --- a/src/diffpy/srreal/PDFEnvelope.hpp +++ b/src/diffpy/srreal/PDFEnvelope.hpp @@ -7,7 +7,7 @@ * * File coded by: Pavol Juhas * -* See AUTHORS.txt for a list of people who contributed. +* See AUTHORS.rst for a list of people who contributed. * See LICENSE_DANSE.txt for license information. * ****************************************************************************** diff --git a/src/diffpy/srreal/PDFUtils.cpp b/src/diffpy/srreal/PDFUtils.cpp index 37a50b67..1a4c540b 100644 --- a/src/diffpy/srreal/PDFUtils.cpp +++ b/src/diffpy/srreal/PDFUtils.cpp @@ -7,7 +7,7 @@ * * File coded by: Pavol Juhas * -* See AUTHORS.txt for a list of people who contributed. +* See AUTHORS.rst for a list of people who contributed. * See LICENSE_DANSE.txt for license information. * ****************************************************************************** diff --git a/src/diffpy/srreal/PDFUtils.hpp b/src/diffpy/srreal/PDFUtils.hpp index 97e2764a..64cb8c86 100644 --- a/src/diffpy/srreal/PDFUtils.hpp +++ b/src/diffpy/srreal/PDFUtils.hpp @@ -7,7 +7,7 @@ * * File coded by: Pavol Juhas * -* See AUTHORS.txt for a list of people who contributed. +* See AUTHORS.rst for a list of people who contributed. * See LICENSE_DANSE.txt for license information. * ****************************************************************************** diff --git a/src/diffpy/srreal/PDFUtils.ipp b/src/diffpy/srreal/PDFUtils.ipp index 28d62b5c..26f64b7d 100644 --- a/src/diffpy/srreal/PDFUtils.ipp +++ b/src/diffpy/srreal/PDFUtils.ipp @@ -7,7 +7,7 @@ * * File coded by: Pavol Juhas * -* See AUTHORS.txt for a list of people who contributed. +* See AUTHORS.rst for a list of people who contributed. * See LICENSE_DANSE.txt for license information. * ****************************************************************************** diff --git a/src/diffpy/srreal/PQEvaluator.cpp b/src/diffpy/srreal/PQEvaluator.cpp index 71874053..df3b13bd 100644 --- a/src/diffpy/srreal/PQEvaluator.cpp +++ b/src/diffpy/srreal/PQEvaluator.cpp @@ -7,7 +7,7 @@ * * File coded by: Pavol Juhas * -* See AUTHORS.txt for a list of people who contributed. +* See AUTHORS.rst for a list of people who contributed. * See LICENSE_DANSE.txt for license information. * ****************************************************************************** diff --git a/src/diffpy/srreal/PQEvaluator.hpp b/src/diffpy/srreal/PQEvaluator.hpp index f446517b..315125f7 100644 --- a/src/diffpy/srreal/PQEvaluator.hpp +++ b/src/diffpy/srreal/PQEvaluator.hpp @@ -7,7 +7,7 @@ * * File coded by: Pavol Juhas * -* See AUTHORS.txt for a list of people who contributed. +* See AUTHORS.rst for a list of people who contributed. * See LICENSE_DANSE.txt for license information. * ****************************************************************************** diff --git a/src/diffpy/srreal/PairCounter.cpp b/src/diffpy/srreal/PairCounter.cpp index e11c9f91..02e416be 100644 --- a/src/diffpy/srreal/PairCounter.cpp +++ b/src/diffpy/srreal/PairCounter.cpp @@ -7,7 +7,7 @@ * * File coded by: Pavol Juhas * -* See AUTHORS.txt for a list of people who contributed. +* See AUTHORS.rst for a list of people who contributed. * See LICENSE_DANSE.txt for license information. * ****************************************************************************** diff --git a/src/diffpy/srreal/PairCounter.hpp b/src/diffpy/srreal/PairCounter.hpp index eef4b92a..6461bff5 100644 --- a/src/diffpy/srreal/PairCounter.hpp +++ b/src/diffpy/srreal/PairCounter.hpp @@ -7,7 +7,7 @@ * * File coded by: Pavol Juhas * -* See AUTHORS.txt for a list of people who contributed. +* See AUTHORS.rst for a list of people who contributed. * See LICENSE_DANSE.txt for license information. * ****************************************************************************** diff --git a/src/diffpy/srreal/PairQuantity.cpp b/src/diffpy/srreal/PairQuantity.cpp index f420df5d..178c6815 100644 --- a/src/diffpy/srreal/PairQuantity.cpp +++ b/src/diffpy/srreal/PairQuantity.cpp @@ -7,7 +7,7 @@ * * File coded by: Pavol Juhas * -* See AUTHORS.txt for a list of people who contributed. +* See AUTHORS.rst for a list of people who contributed. * See LICENSE_DANSE.txt for license information. * ****************************************************************************** diff --git a/src/diffpy/srreal/PairQuantity.hpp b/src/diffpy/srreal/PairQuantity.hpp index b80086ec..ffdf41ac 100644 --- a/src/diffpy/srreal/PairQuantity.hpp +++ b/src/diffpy/srreal/PairQuantity.hpp @@ -7,7 +7,7 @@ * * File coded by: Pavol Juhas * -* See AUTHORS.txt for a list of people who contributed. +* See AUTHORS.rst for a list of people who contributed. * See LICENSE_DANSE.txt for license information. * ****************************************************************************** diff --git a/src/diffpy/srreal/PeakProfile.cpp b/src/diffpy/srreal/PeakProfile.cpp index ced61f2b..fdb66200 100644 --- a/src/diffpy/srreal/PeakProfile.cpp +++ b/src/diffpy/srreal/PeakProfile.cpp @@ -7,7 +7,7 @@ * * File coded by: Pavol Juhas * -* See AUTHORS.txt for a list of people who contributed. +* See AUTHORS.rst for a list of people who contributed. * See LICENSE_DANSE.txt for license information. * ****************************************************************************** diff --git a/src/diffpy/srreal/PeakProfile.hpp b/src/diffpy/srreal/PeakProfile.hpp index 8f6d4e14..d6fd9ad8 100644 --- a/src/diffpy/srreal/PeakProfile.hpp +++ b/src/diffpy/srreal/PeakProfile.hpp @@ -7,7 +7,7 @@ * * File coded by: Pavol Juhas * -* See AUTHORS.txt for a list of people who contributed. +* See AUTHORS.rst for a list of people who contributed. * See LICENSE_DANSE.txt for license information. * ****************************************************************************** diff --git a/src/diffpy/srreal/PeakWidthModel.cpp b/src/diffpy/srreal/PeakWidthModel.cpp index 0418d9e1..999209e8 100644 --- a/src/diffpy/srreal/PeakWidthModel.cpp +++ b/src/diffpy/srreal/PeakWidthModel.cpp @@ -7,7 +7,7 @@ * * File coded by: Pavol Juhas * -* See AUTHORS.txt for a list of people who contributed. +* See AUTHORS.rst for a list of people who contributed. * See LICENSE_DANSE.txt for license information. * ****************************************************************************** diff --git a/src/diffpy/srreal/PeakWidthModel.hpp b/src/diffpy/srreal/PeakWidthModel.hpp index 25a589bf..7db9ac70 100644 --- a/src/diffpy/srreal/PeakWidthModel.hpp +++ b/src/diffpy/srreal/PeakWidthModel.hpp @@ -7,7 +7,7 @@ * * File coded by: Pavol Juhas * -* See AUTHORS.txt for a list of people who contributed. +* See AUTHORS.rst for a list of people who contributed. * See LICENSE_DANSE.txt for license information. * ****************************************************************************** diff --git a/src/diffpy/srreal/PeriodicStructureAdapter.cpp b/src/diffpy/srreal/PeriodicStructureAdapter.cpp index 746376ec..29a20fce 100644 --- a/src/diffpy/srreal/PeriodicStructureAdapter.cpp +++ b/src/diffpy/srreal/PeriodicStructureAdapter.cpp @@ -7,7 +7,7 @@ * * File coded by: Pavol Juhas * -* See AUTHORS.txt for a list of people who contributed. +* See AUTHORS.rst for a list of people who contributed. * See LICENSE.rst for license information. * ****************************************************************************** diff --git a/src/diffpy/srreal/PeriodicStructureAdapter.hpp b/src/diffpy/srreal/PeriodicStructureAdapter.hpp index 210c1ec7..ed540e1c 100644 --- a/src/diffpy/srreal/PeriodicStructureAdapter.hpp +++ b/src/diffpy/srreal/PeriodicStructureAdapter.hpp @@ -7,7 +7,7 @@ * * File coded by: Pavol Juhas * -* See AUTHORS.txt for a list of people who contributed. +* See AUTHORS.rst for a list of people who contributed. * See LICENSE.rst for license information. * ****************************************************************************** diff --git a/src/diffpy/srreal/PointsInSphere.cpp b/src/diffpy/srreal/PointsInSphere.cpp index 88c24ef5..4b732026 100644 --- a/src/diffpy/srreal/PointsInSphere.cpp +++ b/src/diffpy/srreal/PointsInSphere.cpp @@ -7,7 +7,7 @@ * * File coded by: Pavol Juhas * -* See AUTHORS.txt for a list of people who contributed. +* See AUTHORS.rst for a list of people who contributed. * See LICENSE_DANSE.txt for license information. * ****************************************************************************** diff --git a/src/diffpy/srreal/PointsInSphere.hpp b/src/diffpy/srreal/PointsInSphere.hpp index 40451aad..27da5b61 100644 --- a/src/diffpy/srreal/PointsInSphere.hpp +++ b/src/diffpy/srreal/PointsInSphere.hpp @@ -7,7 +7,7 @@ * * File coded by: Pavol Juhas * -* See AUTHORS.txt for a list of people who contributed. +* See AUTHORS.rst for a list of people who contributed. * See LICENSE_DANSE.txt for license information. * ****************************************************************************** diff --git a/src/diffpy/srreal/QResolutionEnvelope.cpp b/src/diffpy/srreal/QResolutionEnvelope.cpp index 53049a23..c20d3642 100644 --- a/src/diffpy/srreal/QResolutionEnvelope.cpp +++ b/src/diffpy/srreal/QResolutionEnvelope.cpp @@ -7,7 +7,7 @@ * * File coded by: Pavol Juhas * -* See AUTHORS.txt for a list of people who contributed. +* See AUTHORS.rst for a list of people who contributed. * See LICENSE_DANSE.txt for license information. * ****************************************************************************** diff --git a/src/diffpy/srreal/QResolutionEnvelope.hpp b/src/diffpy/srreal/QResolutionEnvelope.hpp index dccb3b37..52421fc8 100644 --- a/src/diffpy/srreal/QResolutionEnvelope.hpp +++ b/src/diffpy/srreal/QResolutionEnvelope.hpp @@ -7,7 +7,7 @@ * * File coded by: Pavol Juhas * -* See AUTHORS.txt for a list of people who contributed. +* See AUTHORS.rst for a list of people who contributed. * See LICENSE_DANSE.txt for license information. * ****************************************************************************** diff --git a/src/diffpy/srreal/QuantityType.hpp b/src/diffpy/srreal/QuantityType.hpp index 11a1f678..07dda8d7 100644 --- a/src/diffpy/srreal/QuantityType.hpp +++ b/src/diffpy/srreal/QuantityType.hpp @@ -7,7 +7,7 @@ * * File coded by: Pavol Juhas * -* See AUTHORS.txt for a list of people who contributed. +* See AUTHORS.rst for a list of people who contributed. * See LICENSE_DANSE.txt for license information. * ****************************************************************************** diff --git a/src/diffpy/srreal/R3linalg.cpp b/src/diffpy/srreal/R3linalg.cpp index 19a70cc7..c3473a67 100644 --- a/src/diffpy/srreal/R3linalg.cpp +++ b/src/diffpy/srreal/R3linalg.cpp @@ -7,7 +7,7 @@ * * File coded by: Pavol Juhas * -* See AUTHORS.txt for a list of people who contributed. +* See AUTHORS.rst for a list of people who contributed. * See LICENSE_DANSE.txt for license information. * ****************************************************************************** diff --git a/src/diffpy/srreal/R3linalg.hpp b/src/diffpy/srreal/R3linalg.hpp index bffd5c7c..8cd07dbc 100644 --- a/src/diffpy/srreal/R3linalg.hpp +++ b/src/diffpy/srreal/R3linalg.hpp @@ -7,7 +7,7 @@ * * File coded by: Pavol Juhas * -* See AUTHORS.txt for a list of people who contributed. +* See AUTHORS.rst for a list of people who contributed. * See LICENSE_DANSE.txt for license information. * ****************************************************************************** diff --git a/src/diffpy/srreal/SFTElectron.cpp b/src/diffpy/srreal/SFTElectron.cpp index 556aedea..d85bc661 100644 --- a/src/diffpy/srreal/SFTElectron.cpp +++ b/src/diffpy/srreal/SFTElectron.cpp @@ -7,7 +7,7 @@ * * File coded by: Pavol Juhas * -* See AUTHORS.txt for a list of people who contributed. +* See AUTHORS.rst for a list of people who contributed. * See LICENSE_DANSE.txt for license information. * ****************************************************************************** diff --git a/src/diffpy/srreal/SFTElectron.hpp b/src/diffpy/srreal/SFTElectron.hpp index 86b158dd..f77cd34d 100644 --- a/src/diffpy/srreal/SFTElectron.hpp +++ b/src/diffpy/srreal/SFTElectron.hpp @@ -7,7 +7,7 @@ * * File coded by: Pavol Juhas * -* See AUTHORS.txt for a list of people who contributed. +* See AUTHORS.rst for a list of people who contributed. * See LICENSE_DANSE.txt for license information. * ****************************************************************************** diff --git a/src/diffpy/srreal/SFTElectronNumber.cpp b/src/diffpy/srreal/SFTElectronNumber.cpp index 47f65d8e..a200171c 100644 --- a/src/diffpy/srreal/SFTElectronNumber.cpp +++ b/src/diffpy/srreal/SFTElectronNumber.cpp @@ -7,7 +7,7 @@ * * File coded by: Pavol Juhas * -* See AUTHORS.txt for a list of people who contributed. +* See AUTHORS.rst for a list of people who contributed. * See LICENSE_DANSE.txt for license information. * ****************************************************************************** diff --git a/src/diffpy/srreal/SFTElectronNumber.hpp b/src/diffpy/srreal/SFTElectronNumber.hpp index 3736861b..306d1d9d 100644 --- a/src/diffpy/srreal/SFTElectronNumber.hpp +++ b/src/diffpy/srreal/SFTElectronNumber.hpp @@ -7,7 +7,7 @@ * * File coded by: Pavol Juhas * -* See AUTHORS.txt for a list of people who contributed. +* See AUTHORS.rst for a list of people who contributed. * See LICENSE_DANSE.txt for license information. * ****************************************************************************** diff --git a/src/diffpy/srreal/SFTNeutron.cpp b/src/diffpy/srreal/SFTNeutron.cpp index 96d9af20..72ec80c3 100644 --- a/src/diffpy/srreal/SFTNeutron.cpp +++ b/src/diffpy/srreal/SFTNeutron.cpp @@ -7,7 +7,7 @@ * * File coded by: Pavol Juhas * -* See AUTHORS.txt for a list of people who contributed. +* See AUTHORS.rst for a list of people who contributed. * See LICENSE_DANSE.txt for license information. * ****************************************************************************** diff --git a/src/diffpy/srreal/SFTNeutron.hpp b/src/diffpy/srreal/SFTNeutron.hpp index d2264dc6..a2fb2ad3 100644 --- a/src/diffpy/srreal/SFTNeutron.hpp +++ b/src/diffpy/srreal/SFTNeutron.hpp @@ -7,7 +7,7 @@ * * File coded by: Pavol Juhas * -* See AUTHORS.txt for a list of people who contributed. +* See AUTHORS.rst for a list of people who contributed. * See LICENSE_DANSE.txt for license information. * ****************************************************************************** diff --git a/src/diffpy/srreal/SFTXray.cpp b/src/diffpy/srreal/SFTXray.cpp index 9d24562a..3b589b93 100644 --- a/src/diffpy/srreal/SFTXray.cpp +++ b/src/diffpy/srreal/SFTXray.cpp @@ -7,7 +7,7 @@ * * File coded by: Pavol Juhas * -* See AUTHORS.txt for a list of people who contributed. +* See AUTHORS.rst for a list of people who contributed. * See LICENSE_DANSE.txt for license information. * ****************************************************************************** diff --git a/src/diffpy/srreal/SFTXray.hpp b/src/diffpy/srreal/SFTXray.hpp index 833ff611..e29d27de 100644 --- a/src/diffpy/srreal/SFTXray.hpp +++ b/src/diffpy/srreal/SFTXray.hpp @@ -7,7 +7,7 @@ * * File coded by: Pavol Juhas * -* See AUTHORS.txt for a list of people who contributed. +* See AUTHORS.rst for a list of people who contributed. * See LICENSE_DANSE.txt for license information. * ****************************************************************************** diff --git a/src/diffpy/srreal/ScaleEnvelope.cpp b/src/diffpy/srreal/ScaleEnvelope.cpp index 65dab37a..2e3f2685 100644 --- a/src/diffpy/srreal/ScaleEnvelope.cpp +++ b/src/diffpy/srreal/ScaleEnvelope.cpp @@ -7,7 +7,7 @@ * * File coded by: Pavol Juhas * -* See AUTHORS.txt for a list of people who contributed. +* See AUTHORS.rst for a list of people who contributed. * See LICENSE_DANSE.txt for license information. * ****************************************************************************** diff --git a/src/diffpy/srreal/ScaleEnvelope.hpp b/src/diffpy/srreal/ScaleEnvelope.hpp index a01bb462..b02ef37a 100644 --- a/src/diffpy/srreal/ScaleEnvelope.hpp +++ b/src/diffpy/srreal/ScaleEnvelope.hpp @@ -7,7 +7,7 @@ * * File coded by: Pavol Juhas * -* See AUTHORS.txt for a list of people who contributed. +* See AUTHORS.rst for a list of people who contributed. * See LICENSE_DANSE.txt for license information. * ****************************************************************************** diff --git a/src/diffpy/srreal/ScatteringFactorTable.cpp b/src/diffpy/srreal/ScatteringFactorTable.cpp index b5c4872a..26109dfd 100644 --- a/src/diffpy/srreal/ScatteringFactorTable.cpp +++ b/src/diffpy/srreal/ScatteringFactorTable.cpp @@ -7,7 +7,7 @@ * * File coded by: Pavol Juhas * -* See AUTHORS.txt for a list of people who contributed. +* See AUTHORS.rst for a list of people who contributed. * See LICENSE_DANSE.txt for license information. * ****************************************************************************** diff --git a/src/diffpy/srreal/ScatteringFactorTable.hpp b/src/diffpy/srreal/ScatteringFactorTable.hpp index e876748d..a81d5831 100644 --- a/src/diffpy/srreal/ScatteringFactorTable.hpp +++ b/src/diffpy/srreal/ScatteringFactorTable.hpp @@ -7,7 +7,7 @@ * * File coded by: Pavol Juhas * -* See AUTHORS.txt for a list of people who contributed. +* See AUTHORS.rst for a list of people who contributed. * See LICENSE_DANSE.txt for license information. * ****************************************************************************** diff --git a/src/diffpy/srreal/SphericalShapeEnvelope.cpp b/src/diffpy/srreal/SphericalShapeEnvelope.cpp index c7f7388c..f5c2e0d8 100644 --- a/src/diffpy/srreal/SphericalShapeEnvelope.cpp +++ b/src/diffpy/srreal/SphericalShapeEnvelope.cpp @@ -7,7 +7,7 @@ * * File coded by: Pavol Juhas * -* See AUTHORS.txt for a list of people who contributed. +* See AUTHORS.rst for a list of people who contributed. * See LICENSE_DANSE.txt for license information. * ****************************************************************************** diff --git a/src/diffpy/srreal/SphericalShapeEnvelope.hpp b/src/diffpy/srreal/SphericalShapeEnvelope.hpp index 67ba9740..eeecae77 100644 --- a/src/diffpy/srreal/SphericalShapeEnvelope.hpp +++ b/src/diffpy/srreal/SphericalShapeEnvelope.hpp @@ -7,7 +7,7 @@ * * File coded by: Pavol Juhas * -* See AUTHORS.txt for a list of people who contributed. +* See AUTHORS.rst for a list of people who contributed. * See LICENSE_DANSE.txt for license information. * ****************************************************************************** diff --git a/src/diffpy/srreal/StepCutEnvelope.cpp b/src/diffpy/srreal/StepCutEnvelope.cpp index 0c186693..700cf781 100644 --- a/src/diffpy/srreal/StepCutEnvelope.cpp +++ b/src/diffpy/srreal/StepCutEnvelope.cpp @@ -7,7 +7,7 @@ * * File coded by: Pavol Juhas * -* See AUTHORS.txt for a list of people who contributed. +* See AUTHORS.rst for a list of people who contributed. * See LICENSE_DANSE.txt for license information. * ****************************************************************************** diff --git a/src/diffpy/srreal/StepCutEnvelope.hpp b/src/diffpy/srreal/StepCutEnvelope.hpp index 597b5a64..b32c796e 100644 --- a/src/diffpy/srreal/StepCutEnvelope.hpp +++ b/src/diffpy/srreal/StepCutEnvelope.hpp @@ -7,7 +7,7 @@ * * File coded by: Pavol Juhas * -* See AUTHORS.txt for a list of people who contributed. +* See AUTHORS.rst for a list of people who contributed. * See LICENSE_DANSE.txt for license information. * ****************************************************************************** diff --git a/src/diffpy/srreal/StructureAdapter.cpp b/src/diffpy/srreal/StructureAdapter.cpp index d3468ea4..954c27d7 100644 --- a/src/diffpy/srreal/StructureAdapter.cpp +++ b/src/diffpy/srreal/StructureAdapter.cpp @@ -7,7 +7,7 @@ * * File coded by: Pavol Juhas * -* See AUTHORS.txt for a list of people who contributed. +* See AUTHORS.rst for a list of people who contributed. * See LICENSE_DANSE.txt for license information. * ****************************************************************************** diff --git a/src/diffpy/srreal/StructureAdapter.hpp b/src/diffpy/srreal/StructureAdapter.hpp index 3d047b11..e4840767 100644 --- a/src/diffpy/srreal/StructureAdapter.hpp +++ b/src/diffpy/srreal/StructureAdapter.hpp @@ -7,7 +7,7 @@ * * File coded by: Pavol Juhas * -* See AUTHORS.txt for a list of people who contributed. +* See AUTHORS.rst for a list of people who contributed. * See LICENSE_DANSE.txt for license information. * ****************************************************************************** diff --git a/src/diffpy/srreal/StructureDifference.cpp b/src/diffpy/srreal/StructureDifference.cpp index 5c8cdc19..53759441 100644 --- a/src/diffpy/srreal/StructureDifference.cpp +++ b/src/diffpy/srreal/StructureDifference.cpp @@ -7,7 +7,7 @@ * * File coded by: Pavol Juhas * -* See AUTHORS.txt for a list of people who contributed. +* See AUTHORS.rst for a list of people who contributed. * See LICENSE.rst for license information. * ****************************************************************************** diff --git a/src/diffpy/srreal/StructureDifference.hpp b/src/diffpy/srreal/StructureDifference.hpp index 22f781fa..ee3a1b47 100644 --- a/src/diffpy/srreal/StructureDifference.hpp +++ b/src/diffpy/srreal/StructureDifference.hpp @@ -7,7 +7,7 @@ * * File coded by: Pavol Juhas * -* See AUTHORS.txt for a list of people who contributed. +* See AUTHORS.rst for a list of people who contributed. * See LICENSE.rst for license information. * ****************************************************************************** diff --git a/src/diffpy/srreal/ZeroBaseline.cpp b/src/diffpy/srreal/ZeroBaseline.cpp index eede7bd5..9415fe15 100644 --- a/src/diffpy/srreal/ZeroBaseline.cpp +++ b/src/diffpy/srreal/ZeroBaseline.cpp @@ -7,7 +7,7 @@ * * File coded by: Pavol Juhas * -* See AUTHORS.txt for a list of people who contributed. +* See AUTHORS.rst for a list of people who contributed. * See LICENSE_DANSE.txt for license information. * ****************************************************************************** diff --git a/src/diffpy/srreal/ZeroBaseline.hpp b/src/diffpy/srreal/ZeroBaseline.hpp index 06ba3978..70ba54f7 100644 --- a/src/diffpy/srreal/ZeroBaseline.hpp +++ b/src/diffpy/srreal/ZeroBaseline.hpp @@ -7,7 +7,7 @@ * * File coded by: Pavol Juhas * -* See AUTHORS.txt for a list of people who contributed. +* See AUTHORS.rst for a list of people who contributed. * See LICENSE_DANSE.txt for license information. * ****************************************************************************** diff --git a/src/diffpy/srreal/forwardtypes.hpp b/src/diffpy/srreal/forwardtypes.hpp index 35751dc2..0384ee9c 100644 --- a/src/diffpy/srreal/forwardtypes.hpp +++ b/src/diffpy/srreal/forwardtypes.hpp @@ -7,7 +7,7 @@ * * File coded by: Pavol Juhas * -* See AUTHORS.txt for a list of people who contributed. +* See AUTHORS.rst for a list of people who contributed. * See LICENSE.rst for license information. * ****************************************************************************** diff --git a/src/diffpy/srreal/scatteringfactordata.cpp b/src/diffpy/srreal/scatteringfactordata.cpp index 4b80a7b8..d33e2533 100644 --- a/src/diffpy/srreal/scatteringfactordata.cpp +++ b/src/diffpy/srreal/scatteringfactordata.cpp @@ -7,7 +7,7 @@ * * File coded by: Pavol Juhas * -* See AUTHORS.txt for a list of people who contributed. +* See AUTHORS.rst for a list of people who contributed. * See LICENSE.rst for license information. * ****************************************************************************** diff --git a/src/diffpy/srreal/scatteringfactordata.hpp b/src/diffpy/srreal/scatteringfactordata.hpp index a4d4b4a2..bae6e603 100644 --- a/src/diffpy/srreal/scatteringfactordata.hpp +++ b/src/diffpy/srreal/scatteringfactordata.hpp @@ -7,7 +7,7 @@ * * File coded by: Pavol Juhas * -* See AUTHORS.txt for a list of people who contributed. +* See AUTHORS.rst for a list of people who contributed. * See LICENSE.rst for license information. * ****************************************************************************** diff --git a/src/diffpy/validators.hpp b/src/diffpy/validators.hpp index 6c6907d0..4f40684e 100644 --- a/src/diffpy/validators.hpp +++ b/src/diffpy/validators.hpp @@ -7,7 +7,7 @@ * * File coded by: Pavol Juhas * -* See AUTHORS.txt for a list of people who contributed. +* See AUTHORS.rst for a list of people who contributed. * See LICENSE_DANSE.txt for license information. * ****************************************************************************** diff --git a/src/diffpy/version.cpp b/src/diffpy/version.cpp index 6f6e572c..ba4f9f8e 100644 --- a/src/diffpy/version.cpp +++ b/src/diffpy/version.cpp @@ -7,7 +7,7 @@ * * File coded by: Pavol Juhas * -* See AUTHORS.txt for a list of people who contributed. +* See AUTHORS.rst for a list of people who contributed. * See LICENSE.rst for license information. * ****************************************************************************** diff --git a/src/diffpy/version.tpl b/src/diffpy/version.tpl index c47a9e26..06b0093a 100644 --- a/src/diffpy/version.tpl +++ b/src/diffpy/version.tpl @@ -7,7 +7,7 @@ * * File coded by: Pavol Juhas * -* See AUTHORS.txt for a list of people who contributed. +* See AUTHORS.rst for a list of people who contributed. * See LICENSE.rst for license information. * ****************************************************************************** diff --git a/src/tests/TestAtomRadiiTable.hpp b/src/tests/TestAtomRadiiTable.hpp index 392f8fd2..6a5fce87 100644 --- a/src/tests/TestAtomRadiiTable.hpp +++ b/src/tests/TestAtomRadiiTable.hpp @@ -7,7 +7,7 @@ * * File coded by: Pavol Juhas * -* See AUTHORS.txt for a list of people who contributed. +* See AUTHORS.rst for a list of people who contributed. * See LICENSE_DANSE.txt for license information. * ****************************************************************************** diff --git a/src/tests/TestAtomicStructureAdapter.hpp b/src/tests/TestAtomicStructureAdapter.hpp index e9021157..85113634 100644 --- a/src/tests/TestAtomicStructureAdapter.hpp +++ b/src/tests/TestAtomicStructureAdapter.hpp @@ -7,7 +7,7 @@ * * File coded by: Pavol Juhas * -* See AUTHORS.txt for a list of people who contributed. +* See AUTHORS.rst for a list of people who contributed. * See LICENSE.rst for license information. * ****************************************************************************** diff --git a/src/tests/TestAttributes.hpp b/src/tests/TestAttributes.hpp index 2cee1476..1183b9f2 100644 --- a/src/tests/TestAttributes.hpp +++ b/src/tests/TestAttributes.hpp @@ -7,7 +7,7 @@ * * File coded by: Pavol Juhas * -* See AUTHORS.txt for a list of people who contributed. +* See AUTHORS.rst for a list of people who contributed. * See LICENSE.rst for license information. * ****************************************************************************** diff --git a/src/tests/TestBVParametersTable.hpp b/src/tests/TestBVParametersTable.hpp index 41a985e8..38df3a90 100644 --- a/src/tests/TestBVParametersTable.hpp +++ b/src/tests/TestBVParametersTable.hpp @@ -7,7 +7,7 @@ * * File coded by: Pavol Juhas * -* See AUTHORS.txt for a list of people who contributed. +* See AUTHORS.rst for a list of people who contributed. * See LICENSE_DANSE.txt for license information. * ****************************************************************************** diff --git a/src/tests/TestBVSCalculator.hpp b/src/tests/TestBVSCalculator.hpp index 511a0f4d..1b268030 100644 --- a/src/tests/TestBVSCalculator.hpp +++ b/src/tests/TestBVSCalculator.hpp @@ -7,7 +7,7 @@ * * File coded by: Pavol Juhas * -* See AUTHORS.txt for a list of people who contributed. +* See AUTHORS.rst for a list of people who contributed. * See LICENSE_DANSE.txt for license information. * ****************************************************************************** diff --git a/src/tests/TestBVSObjCryst.hpp b/src/tests/TestBVSObjCryst.hpp index 84830445..ab3a7968 100644 --- a/src/tests/TestBVSObjCryst.hpp +++ b/src/tests/TestBVSObjCryst.hpp @@ -7,7 +7,7 @@ * * File coded by: Pavol Juhas * -* See AUTHORS.txt for a list of people who contributed. +* See AUTHORS.rst for a list of people who contributed. * See LICENSE_DANSE.txt for license information. * ****************************************************************************** diff --git a/src/tests/TestDebyePDFCalculator.hpp b/src/tests/TestDebyePDFCalculator.hpp index 51cc251c..2c78ee69 100644 --- a/src/tests/TestDebyePDFCalculator.hpp +++ b/src/tests/TestDebyePDFCalculator.hpp @@ -7,7 +7,7 @@ * * File coded by: Pavol Juhas * -* See AUTHORS.txt for a list of people who contributed. +* See AUTHORS.rst for a list of people who contributed. * See LICENSE_DANSE.txt for license information. * ****************************************************************************** diff --git a/src/tests/TestEmptyStructureAdapter.hpp b/src/tests/TestEmptyStructureAdapter.hpp index 2ce5f4fc..89e2af2b 100644 --- a/src/tests/TestEmptyStructureAdapter.hpp +++ b/src/tests/TestEmptyStructureAdapter.hpp @@ -7,7 +7,7 @@ * * File coded by: Pavol Juhas * -* See AUTHORS.txt for a list of people who contributed. +* See AUTHORS.rst for a list of people who contributed. * See LICENSE.rst for license information. * ****************************************************************************** diff --git a/src/tests/TestHasClassRegistry.hpp b/src/tests/TestHasClassRegistry.hpp index 4e6a8058..8ce9aac5 100644 --- a/src/tests/TestHasClassRegistry.hpp +++ b/src/tests/TestHasClassRegistry.hpp @@ -7,7 +7,7 @@ * * File coded by: Pavol Juhas * -* See AUTHORS.txt for a list of people who contributed. +* See AUTHORS.rst for a list of people who contributed. * See LICENSE.rst for license information. * ****************************************************************************** diff --git a/src/tests/TestLattice.hpp b/src/tests/TestLattice.hpp index 5752aa2f..2c66c0ad 100644 --- a/src/tests/TestLattice.hpp +++ b/src/tests/TestLattice.hpp @@ -7,7 +7,7 @@ * * File coded by: Pavol Juhas * -* See AUTHORS.txt for a list of people who contributed. +* See AUTHORS.rst for a list of people who contributed. * See LICENSE_DANSE.txt for license information. * ****************************************************************************** diff --git a/src/tests/TestNoMetaStructureAdapter.hpp b/src/tests/TestNoMetaStructureAdapter.hpp index aabf5dd0..6fdb1920 100644 --- a/src/tests/TestNoMetaStructureAdapter.hpp +++ b/src/tests/TestNoMetaStructureAdapter.hpp @@ -7,7 +7,7 @@ * * File coded by: Pavol Juhas * -* See AUTHORS.txt for a list of people who contributed. +* See AUTHORS.rst for a list of people who contributed. * See LICENSE_DANSE.txt for license information. * ****************************************************************************** diff --git a/src/tests/TestObjCrystStructureAdapter.hpp b/src/tests/TestObjCrystStructureAdapter.hpp index cd4bf902..7428a03b 100644 --- a/src/tests/TestObjCrystStructureAdapter.hpp +++ b/src/tests/TestObjCrystStructureAdapter.hpp @@ -7,7 +7,7 @@ * * File coded by: Pavol Juhas * -* See AUTHORS.txt for a list of people who contributed. +* See AUTHORS.rst for a list of people who contributed. * See LICENSE_DANSE.txt for license information. * ****************************************************************************** diff --git a/src/tests/TestOverlapCalculator.hpp b/src/tests/TestOverlapCalculator.hpp index 53654bfe..69a379d7 100644 --- a/src/tests/TestOverlapCalculator.hpp +++ b/src/tests/TestOverlapCalculator.hpp @@ -7,7 +7,7 @@ * * File coded by: Pavol Juhas * -* See AUTHORS.txt for a list of people who contributed. +* See AUTHORS.rst for a list of people who contributed. * See LICENSE_DANSE.txt for license information. * ****************************************************************************** diff --git a/src/tests/TestPDFBaseline.hpp b/src/tests/TestPDFBaseline.hpp index c6104d24..3d7b38d6 100644 --- a/src/tests/TestPDFBaseline.hpp +++ b/src/tests/TestPDFBaseline.hpp @@ -7,7 +7,7 @@ * * File coded by: Pavol Juhas * -* See AUTHORS.txt for a list of people who contributed. +* See AUTHORS.rst for a list of people who contributed. * See LICENSE.rst for license information. * ****************************************************************************** diff --git a/src/tests/TestPDFCalculator.hpp b/src/tests/TestPDFCalculator.hpp index c14d6397..0a5c4ada 100644 --- a/src/tests/TestPDFCalculator.hpp +++ b/src/tests/TestPDFCalculator.hpp @@ -7,7 +7,7 @@ * * File coded by: Pavol Juhas * -* See AUTHORS.txt for a list of people who contributed. +* See AUTHORS.rst for a list of people who contributed. * See LICENSE_DANSE.txt for license information. * ****************************************************************************** diff --git a/src/tests/TestPQEvaluator.hpp b/src/tests/TestPQEvaluator.hpp index 05829a8d..ed99ded3 100644 --- a/src/tests/TestPQEvaluator.hpp +++ b/src/tests/TestPQEvaluator.hpp @@ -7,7 +7,7 @@ * * File coded by: Pavol Juhas * -* See AUTHORS.txt for a list of people who contributed. +* See AUTHORS.rst for a list of people who contributed. * See LICENSE.rst for license information. * ****************************************************************************** diff --git a/src/tests/TestPairCounter.hpp b/src/tests/TestPairCounter.hpp index e697086c..1ec6c390 100644 --- a/src/tests/TestPairCounter.hpp +++ b/src/tests/TestPairCounter.hpp @@ -7,7 +7,7 @@ * * File coded by: Pavol Juhas * -* See AUTHORS.txt for a list of people who contributed. +* See AUTHORS.rst for a list of people who contributed. * See LICENSE_DANSE.txt for license information. * ****************************************************************************** diff --git a/src/tests/TestPeakProfile.hpp b/src/tests/TestPeakProfile.hpp index 44b9875f..fba070c0 100644 --- a/src/tests/TestPeakProfile.hpp +++ b/src/tests/TestPeakProfile.hpp @@ -7,7 +7,7 @@ * * File coded by: Pavol Juhas * -* See AUTHORS.txt for a list of people who contributed. +* See AUTHORS.rst for a list of people who contributed. * See LICENSE_DANSE.txt for license information. * ****************************************************************************** diff --git a/src/tests/TestPeakWidthModel.hpp b/src/tests/TestPeakWidthModel.hpp index e0f794e0..846b81ae 100644 --- a/src/tests/TestPeakWidthModel.hpp +++ b/src/tests/TestPeakWidthModel.hpp @@ -7,7 +7,7 @@ * * File coded by: Pavol Juhas * -* See AUTHORS.txt for a list of people who contributed. +* See AUTHORS.rst for a list of people who contributed. * See LICENSE.rst for license information. * ****************************************************************************** diff --git a/src/tests/TestPeriodicStructureAdapter.hpp b/src/tests/TestPeriodicStructureAdapter.hpp index 388ee5b2..935e7ec4 100644 --- a/src/tests/TestPeriodicStructureAdapter.hpp +++ b/src/tests/TestPeriodicStructureAdapter.hpp @@ -7,7 +7,7 @@ * * File coded by: Pavol Juhas * -* See AUTHORS.txt for a list of people who contributed. +* See AUTHORS.rst for a list of people who contributed. * See LICENSE.rst for license information. * ****************************************************************************** diff --git a/src/tests/TestPointsInSphere.hpp b/src/tests/TestPointsInSphere.hpp index 6be10856..ffaec7e3 100644 --- a/src/tests/TestPointsInSphere.hpp +++ b/src/tests/TestPointsInSphere.hpp @@ -7,7 +7,7 @@ * * File coded by: Pavol Juhas * -* See AUTHORS.txt for a list of people who contributed. +* See AUTHORS.rst for a list of people who contributed. * See LICENSE_DANSE.txt for license information. * ************************************************************************ diff --git a/src/tests/TestR3linalg.hpp b/src/tests/TestR3linalg.hpp index 8f779dfb..7a3da835 100644 --- a/src/tests/TestR3linalg.hpp +++ b/src/tests/TestR3linalg.hpp @@ -7,7 +7,7 @@ * * File coded by: Pavol Juhas * -* See AUTHORS.txt for a list of people who contributed. +* See AUTHORS.rst for a list of people who contributed. * See LICENSE_DANSE.txt for license information. * ****************************************************************************** diff --git a/src/tests/TestRuntimePath.hpp b/src/tests/TestRuntimePath.hpp index 3961ef6b..644732e3 100644 --- a/src/tests/TestRuntimePath.hpp +++ b/src/tests/TestRuntimePath.hpp @@ -7,7 +7,7 @@ * * File coded by: Pavol Juhas * -* See AUTHORS.txt for a list of people who contributed. +* See AUTHORS.rst for a list of people who contributed. * See LICENSE.rst for license information. * ****************************************************************************** diff --git a/src/tests/TestScatteringFactorTable.hpp b/src/tests/TestScatteringFactorTable.hpp index 5c517a2d..2315ba46 100644 --- a/src/tests/TestScatteringFactorTable.hpp +++ b/src/tests/TestScatteringFactorTable.hpp @@ -7,7 +7,7 @@ * * File coded by: Pavol Juhas * -* See AUTHORS.txt for a list of people who contributed. +* See AUTHORS.rst for a list of people who contributed. * See LICENSE_DANSE.txt for license information. * ****************************************************************************** diff --git a/src/tests/TestSphericalShapeEnvelope.hpp b/src/tests/TestSphericalShapeEnvelope.hpp index 2bf667c8..d8b602d1 100644 --- a/src/tests/TestSphericalShapeEnvelope.hpp +++ b/src/tests/TestSphericalShapeEnvelope.hpp @@ -7,7 +7,7 @@ * * File coded by: Pavol Juhas * -* See AUTHORS.txt for a list of people who contributed. +* See AUTHORS.rst for a list of people who contributed. * See LICENSE_DANSE.txt for license information. * ****************************************************************************** diff --git a/src/tests/TestStepCutEnvelope.hpp b/src/tests/TestStepCutEnvelope.hpp index ffb49a0f..d3be0593 100644 --- a/src/tests/TestStepCutEnvelope.hpp +++ b/src/tests/TestStepCutEnvelope.hpp @@ -7,7 +7,7 @@ * * File coded by: Pavol Juhas * -* See AUTHORS.txt for a list of people who contributed. +* See AUTHORS.rst for a list of people who contributed. * See LICENSE_DANSE.txt for license information. * ****************************************************************************** diff --git a/src/tests/objcryst_helpers.cpp b/src/tests/objcryst_helpers.cpp index 76a61df1..88c06ffa 100644 --- a/src/tests/objcryst_helpers.cpp +++ b/src/tests/objcryst_helpers.cpp @@ -7,7 +7,7 @@ * * File coded by: Pavol Juhas * -* See AUTHORS.txt for a list of people who contributed. +* See AUTHORS.rst for a list of people who contributed. * See LICENSE_DANSE.txt for license information. * ****************************************************************************** diff --git a/src/tests/objcryst_helpers.hpp b/src/tests/objcryst_helpers.hpp index 55d0e35e..3fcfa1d9 100644 --- a/src/tests/objcryst_helpers.hpp +++ b/src/tests/objcryst_helpers.hpp @@ -7,7 +7,7 @@ * * File coded by: Pavol Juhas * -* See AUTHORS.txt for a list of people who contributed. +* See AUTHORS.rst for a list of people who contributed. * See LICENSE_DANSE.txt for license information. * ****************************************************************************** diff --git a/src/tests/serialization_helpers.hpp b/src/tests/serialization_helpers.hpp index 4e689474..e656a9e0 100644 --- a/src/tests/serialization_helpers.hpp +++ b/src/tests/serialization_helpers.hpp @@ -7,7 +7,7 @@ * * File coded by: Pavol Juhas * -* See AUTHORS.txt for a list of people who contributed. +* See AUTHORS.rst for a list of people who contributed. * See LICENSE_DANSE.txt for license information. * ****************************************************************************** diff --git a/src/tests/test_custompqconfig.hpp b/src/tests/test_custompqconfig.hpp index 873268b4..5478f3fe 100644 --- a/src/tests/test_custompqconfig.hpp +++ b/src/tests/test_custompqconfig.hpp @@ -7,7 +7,7 @@ * * File coded by: Pavol Juhas * -* See AUTHORS.txt for a list of people who contributed. +* See AUTHORS.rst for a list of people who contributed. * See LICENSE.rst for license information. * ****************************************************************************** diff --git a/src/tests/test_helpers.cpp b/src/tests/test_helpers.cpp index d8f5bdf9..0781ada4 100644 --- a/src/tests/test_helpers.cpp +++ b/src/tests/test_helpers.cpp @@ -7,7 +7,7 @@ * * File coded by: Pavol Juhas * -* See AUTHORS.txt for a list of people who contributed. +* See AUTHORS.rst for a list of people who contributed. * See LICENSE_DANSE.txt for license information. * ****************************************************************************** diff --git a/src/tests/test_helpers.hpp b/src/tests/test_helpers.hpp index f1fd3c23..ef0d2430 100644 --- a/src/tests/test_helpers.hpp +++ b/src/tests/test_helpers.hpp @@ -7,7 +7,7 @@ * * File coded by: Pavol Juhas * -* See AUTHORS.txt for a list of people who contributed. +* See AUTHORS.rst for a list of people who contributed. * See LICENSE_DANSE.txt for license information. * ****************************************************************************** From d57f82ae36cb79c2424a4e217c8fcc2aad980eaf Mon Sep 17 00:00:00 2001 From: Caden Myers Date: Tue, 20 Oct 2026 11:39:40 -0400 Subject: [PATCH 20/56] rm scikit-package doc fillers --- docs/source/api/libdiffpy.example_package.rst | 31 ------- docs/source/api/libdiffpy.rst | 19 ----- docs/source/getting-started.rst | 79 ------------------ docs/source/img/scikit-package-logo-text.png | Bin 187608 -> 0 bytes docs/source/index.rst | 9 -- docs/source/snippets/example-table.rst | 28 ------- 6 files changed, 166 deletions(-) delete mode 100644 docs/source/api/libdiffpy.example_package.rst delete mode 100644 docs/source/getting-started.rst delete mode 100644 docs/source/img/scikit-package-logo-text.png delete mode 100644 docs/source/snippets/example-table.rst diff --git a/docs/source/api/libdiffpy.example_package.rst b/docs/source/api/libdiffpy.example_package.rst deleted file mode 100644 index a15ce6af..00000000 --- a/docs/source/api/libdiffpy.example_package.rst +++ /dev/null @@ -1,31 +0,0 @@ -.. _example_package documentation: - -|title| -======= - -.. |title| replace:: libdiffpy.example_package package - -.. automodule:: libdiffpy.example_package - :members: - :undoc-members: - :show-inheritance: - -|foo| ------ - -.. |foo| replace:: libdiffpy.example_package.foo module - -.. automodule:: libdiffpy.example_package.foo - :members: - :undoc-members: - :show-inheritance: - -|bar| ------ - -.. |bar| replace:: libdiffpy.example_package.bar module - -.. automodule:: libdiffpy.example_package.foo - :members: - :undoc-members: - :show-inheritance: diff --git a/docs/source/api/libdiffpy.rst b/docs/source/api/libdiffpy.rst index ca658409..3f7fdd00 100644 --- a/docs/source/api/libdiffpy.rst +++ b/docs/source/api/libdiffpy.rst @@ -9,22 +9,3 @@ :members: :undoc-members: :show-inheritance: - -Subpackages ------------ - -.. toctree:: - libdiffpy.example_package - -Submodules ----------- - -|module| --------- - -.. |module| replace:: libdiffpy.example_submodule module - -.. automodule:: libdiffpy.example_submodule - :members: - :undoc-members: - :show-inheritance: diff --git a/docs/source/getting-started.rst b/docs/source/getting-started.rst deleted file mode 100644 index 6b62bf84..00000000 --- a/docs/source/getting-started.rst +++ /dev/null @@ -1,79 +0,0 @@ -:tocdepth: -1 - -.. index:: getting-started - -.. _getting-started: - -================ -Getting started -================ - -Here are some example templates provided to help you get started with writing your documentation. You can use these templates to create your own documentation. - -Reuse ``.rst`` files across multiple pages ------------------------------------------- - -Here is how you can reuse a reusable block of ``.rst`` files across multiple pages: - -.. include:: snippets/example-table.rst - -.. warning:: - - Ensure that the ``.rst`` file you are including is not too long. If it is too long, it may be better to split it into multiple files and include them separately. - -Refer to a specific section in the documentation ------------------------------------------------- - -You can use the ``ref`` tag to refer to a specific section in the documentation. For example, you can refer to the section below using the ``:ref:`` tag as shown :ref:`here `. - -.. note:: - - Please check the raw ``.rst`` file of this page to see the exact use of the ``:ref:`` tag. - -Embed your code snippets in the documentation ---------------------------------------------- - -Here is how you can write a block of code in the documentation. You can use the ``code-block`` directive to write a block of code in the documentation. For example, you can write a block of code as shown below: - -.. code-block:: bash - - # Create a new environment, without build dependencies (pure Python package) - conda create -n -env python=3.14 \ - --file requirements/tests.txt \ - --file requirements/conda.txt - - # Create a new environment, with build dependencies (non-pure Python package) - conda create -n -env python=3.14 \ - --file requirements/tests.txt \ - --file requirements/conda.txt \ - --file requirements/build.txt - - # Activate the environment - conda activate _env - - # Install your package locally - # `--no-deps` to NOT install packages again from `requirements.pip.txt` - pip install -e . --no-deps - - # Run pytest locally - pytest - - # ... run example tutorials - -.. _attach-image: - -Attach an image to the documentation ------------------------------------- - -Here is how you attach an image to the documentation. The ``/docs/source/img/scikit-package-logo-text.png`` example image is provided in the template. - -.. image:: ./img/scikit-package-logo-text.png - :alt: codecov-in-pr-comment - :width: 400px - :align: center - - -Other useful directives ------------------------ - -Here is how you can do menu selection :menuselection:`Admin --> Settings` and display labels for buttons like :guilabel:`Privacy level`. diff --git a/docs/source/img/scikit-package-logo-text.png b/docs/source/img/scikit-package-logo-text.png deleted file mode 100644 index 823178dcb1316d5cfd09bc43d8210e5c4ce38f28..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 187608 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 h|9pew|C30$uOMa3u}J+)EaB{~z=&QIY@v diff --git a/docs/source/index.rst b/docs/source/index.rst index 3b09b717..e80726b5 100644 --- a/docs/source/index.rst +++ b/docs/source/index.rst @@ -9,14 +9,6 @@ | Software version |release| | Last updated |today|. -=============== -Getting started -=============== - -Welcome to the ``libdiffpy`` documentation! - -To get started, please visit the :ref:`Getting started ` page. - ======= Authors ======= @@ -43,7 +35,6 @@ Table of contents .. toctree:: :maxdepth: 2 - getting-started Package API release license diff --git a/docs/source/snippets/example-table.rst b/docs/source/snippets/example-table.rst deleted file mode 100644 index 7c4c11da..00000000 --- a/docs/source/snippets/example-table.rst +++ /dev/null @@ -1,28 +0,0 @@ -.. list-table:: 5 levels of reusing/sharing code - :widths: 5 15 40 40 - :header-rows: 1 - - * - Level - - Name - - Scope - - How to setup - * - 1 - - ``function`` - - Reuse code in the single file. - - See Level 1 tutorial - * - 2 - - ``module`` - - Reuse code across files. - - See Level 2 tutorial - * - 3 - - ``workspace`` - - Reuse code across project folders. - - ``package create workspace`` - * - 4 - - ``system`` - - Reuse code across any files in the computer. - - ``package create system`` - * - 5 - - ``public`` - - Share code as publicly installable package. - - ``package create public`` From 0ca168c08036737fca982689a2f3c82297ee4c9b Mon Sep 17 00:00:00 2001 From: Caden Myers Date: Tue, 20 Oct 2026 14:29:38 -0400 Subject: [PATCH 21/56] read .sconf_temp/ to the gitignore --- .gitignore | 24 ++++++++++++++++++ ...est_383658db2a6c0cca38b6f776638a25f9_0.cpp | 5 ---- ...a25f9_0_9bc8887b698aff96a593dc32c66c9ad1.o | Bin 608 -> 0 bytes ...est_e492448966d6e04631c094ba6d6e8a32_0.cpp | 7 ----- ...ftest_e492448966d6e04631c094ba6d6e8a32_0.o | Bin 608 -> 0 bytes ...d6e8a32_0_32bd209fc335e052be8df6851f49edb1 | Bin 4272 -> 0 bytes ...d6e8a32_0_e1a1914d3e176b585cd892b23cd56a48 | Bin 4272 -> 0 bytes ...est_f043931bac80a040e9dbafdbb5991c64_0.cpp | 8 ------ ...ftest_f043931bac80a040e9dbafdbb5991c64_0.o | Bin 19944 -> 0 bytes ...5991c64_0_4168d21565c3518ee97cd47e0a4b72d9 | Bin 26112 -> 0 bytes ...5991c64_0_4eeef59bb4603aa6f185d0625b6d5bda | Bin 26112 -> 0 bytes .sconsign.dblite | Bin 1342638 -> 0 bytes 12 files changed, 24 insertions(+), 20 deletions(-) delete mode 100644 .sconf_temp/conftest_383658db2a6c0cca38b6f776638a25f9_0.cpp delete mode 100644 .sconf_temp/conftest_383658db2a6c0cca38b6f776638a25f9_0_9bc8887b698aff96a593dc32c66c9ad1.o delete mode 100644 .sconf_temp/conftest_e492448966d6e04631c094ba6d6e8a32_0.cpp delete mode 100644 .sconf_temp/conftest_e492448966d6e04631c094ba6d6e8a32_0.o delete mode 100755 .sconf_temp/conftest_e492448966d6e04631c094ba6d6e8a32_0_32bd209fc335e052be8df6851f49edb1 delete mode 100755 .sconf_temp/conftest_e492448966d6e04631c094ba6d6e8a32_0_e1a1914d3e176b585cd892b23cd56a48 delete mode 100644 .sconf_temp/conftest_f043931bac80a040e9dbafdbb5991c64_0.cpp delete mode 100644 .sconf_temp/conftest_f043931bac80a040e9dbafdbb5991c64_0.o delete mode 100755 .sconf_temp/conftest_f043931bac80a040e9dbafdbb5991c64_0_4168d21565c3518ee97cd47e0a4b72d9 delete mode 100755 .sconf_temp/conftest_f043931bac80a040e9dbafdbb5991c64_0_4eeef59bb4603aa6f185d0625b6d5bda delete mode 100644 .sconsign.dblite diff --git a/.gitignore b/.gitignore index 099e2948..a73791eb 100644 --- a/.gitignore +++ b/.gitignore @@ -6,6 +6,30 @@ __pycache__/ # C extensions *.so +# Compiled object files +*.slo +*.lo +*.o + +# Compiled dynamic libraries +*.dylib + +# Compiled static libraries +*.lai +*.la +*.a + +# SCons build files +.sconf_temp/ +.sconsign.dblite +/sconscript.local +/sconsvars.py +.gdb_history +tags + +# Source distribution tarball +libdiffpy-*.tar.gz + # Distribution / packaging .Python env/ diff --git a/.sconf_temp/conftest_383658db2a6c0cca38b6f776638a25f9_0.cpp b/.sconf_temp/conftest_383658db2a6c0cca38b6f776638a25f9_0.cpp deleted file mode 100644 index 303968b4..00000000 --- a/.sconf_temp/conftest_383658db2a6c0cca38b6f776638a25f9_0.cpp +++ /dev/null @@ -1,5 +0,0 @@ -#include -#if BOOST_VERSION < 105600 -#error Installed boost is too old! -#endif -int main() { return 0; } diff --git a/.sconf_temp/conftest_383658db2a6c0cca38b6f776638a25f9_0_9bc8887b698aff96a593dc32c66c9ad1.o b/.sconf_temp/conftest_383658db2a6c0cca38b6f776638a25f9_0_9bc8887b698aff96a593dc32c66c9ad1.o deleted file mode 100644 index cad0f2929cb5ecd7cbd55624a57a7b4c67745c6e..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 608 zcmX^A>+L^w1_nlE1|R{%EI_;i#83cYAdm!N3lJX%$bgF9fU1GtHx zttf%8A^Z^6h!6xH#^Qi7K<0tm0tYM%4RAJ)5g(tNpIeZaToPZJSDu-d5+Cp5f-Da+ zM*%AM0B9b_Ee=c&e*&3676Qb_r)I>b6(#1TLd4?ZQQfBjRq6oK0@Mxj8%T(ofdS@q zunHp}DFMWgVBu$AU+L^w1_nlE1|R{%EI_;i#83cYAdm!N3lJX%$bgF9fU1GtHx zttf%8A^Z^6h!6xH#^Qi7K<0tm0tYM%4RAJ)5g(tNpIeZaToPZJSDu-d5+Cp5f-Da+ zM*%AM0B9b_Ee=c&e*&3676Qb_r)I>b6(#1TLd4?ZQQfBjRq6oK0@Mxj8%T(ofdS@q zunHp}DFMWgVBu$AUv>=uNDKG> zR&&~ST)zl|3(Nq8ey>|-1@%H>uhjsjTmx9g>HNv-Z{Ld_4?ZowEj{{hvGUGi^Sgo93SN3q& zFsLIUuDggeiDyNQB=K7d3;EJ0g6}J5{8#Y#IZ5wG{A8~e`R)9@Q4suZ0PLrU2fw{r zhVy*>r97>@IVydA{)Js!H;L0LXT%v+uoC=UDHZ)p9yHIOuUUO6+8!TO;$k&B0XqRZ z0XqRZ0XqRZ0XqRZ0Xu>JZ~`uR;vI>gq%Q(v*Rj|^J1M!ul^2dl8Q>qngladHzw2-H uBVRRruiDppezMA5^ZL@g%$;UVB<6+>DTTeO8Dlp)DZnt@~!vYXm?>ZM>$ z>VM+hi{Q$j_wCzVHQ+|7%Vhkr!O^93s9MrCUw?_%QT@_Qn|$FNOh zZ4@aZwHL*9jPv=9P&@OTNL+|#Qg?#=ER=E(Jk^0Ob>HqXdT z`FgPW@P6g)>Zn)rlj}3-?{yN$GjpEcn?mdx7d*pThN`b?ZT+kk z>26R&fx3=}xb6k4X*?@(L>k|ipD!+sBlx~@&cBM!&q;en;>W`e(w~|JktlaXTi&yxmB|tUv=tYEz5vqz%pPNunbrR zECZGS%YbFzKV-l~O`MUalo-A5T<0OCDop43?e043e!(t nob1$eAWmbZfCGi|MZt-O8ouYe!ST#N>|z4{J&iPJa4dcSh-!%o diff --git a/.sconf_temp/conftest_f043931bac80a040e9dbafdbb5991c64_0.cpp b/.sconf_temp/conftest_f043931bac80a040e9dbafdbb5991c64_0.cpp deleted file mode 100644 index 65fb0cf4..00000000 --- a/.sconf_temp/conftest_f043931bac80a040e9dbafdbb5991c64_0.cpp +++ /dev/null @@ -1,8 +0,0 @@ - - -#include "ObjCryst/ObjCryst/Crystal.h" - -int main(void) { - -return 0; -} diff --git a/.sconf_temp/conftest_f043931bac80a040e9dbafdbb5991c64_0.o b/.sconf_temp/conftest_f043931bac80a040e9dbafdbb5991c64_0.o deleted file mode 100644 index 3b4f37f1a9da9d2db2738d8c858a8abc7fee627f..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 19944 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 diff --git a/.sconf_temp/conftest_f043931bac80a040e9dbafdbb5991c64_0_4168d21565c3518ee97cd47e0a4b72d9 b/.sconf_temp/conftest_f043931bac80a040e9dbafdbb5991c64_0_4168d21565c3518ee97cd47e0a4b72d9 deleted file mode 100755 index 8dfedf30c0aba7297e072ba6b709114f85a85618..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 26112 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 Z(D0P9iDn_=Vohk=mw}2D2*N?^e*hDt#qIzA diff --git a/.sconf_temp/conftest_f043931bac80a040e9dbafdbb5991c64_0_4eeef59bb4603aa6f185d0625b6d5bda b/.sconf_temp/conftest_f043931bac80a040e9dbafdbb5991c64_0_4eeef59bb4603aa6f185d0625b6d5bda deleted file mode 100755 index 1b10a2d444060bff29440afbd0a8ea7975fcb70a..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 26112 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 diff --git a/.sconsign.dblite b/.sconsign.dblite deleted file mode 100644 index 0fd0c4022014516f2b77904f31a2215430674ed9..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1342638 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

rO=S+3{{}| ziExz1U!%zm2%L?6lf;xlRrml2zi)m%c6;W)aYnd_LqMv|q;||6`5N0!5=&VZ%Ym<%q%HweZPl zLP7+N{Hvq?nTB%$uh9+QE0pxK1~jrrPGIiHatQl{26f8PeV0b&3H;!(alr!_Xcs;q z98ZorNQmvi4YgGZ`$Emq(F+>?B%xsdt_&KAGXMYt$1ri|7q6cYLUd zM^wPoxQ(Kq(E-{8lnbi^cFUh|&ed=KsK%ovP=po(v@17F$Sr-s&63_rm@K-5sVDAw z&9}qD6=Q~1j8}$x=pwot^CTw4Kw?Y{1LqnWhdCvT^biT<0nv9$v z{VF;u|2sFVPo6v|CxgU8>-j3YcBxh(^qn9QW|?O&mB3mq;X$=N4@a3qH^UVvh* zVxjq0%mp75@GMp3H);+nTw<~a0r)nATw0(bIm9Eh;*%s5gg52!f2>hfxZOm1SO`!` zG$nKyj2v(?SO7Wv4kh(avvh>o=w#9@JfeZV%g9%9nm{9T~ur{Pf9q3zG`MWgx4mA%C z5|4yzuz>O$bP;3GHxyqa>gd0wdz`LcHZ|x(p>mZAzA-Fra;dnb>|zHk^fY2qlJLbq zixUuHX;Q)0J=kSDKB3xr!f; zRbrp8N;t>L=&ojTz$I7@ z;$47Sz={NEBNG}BLX#IJL{uLCVa>O*+JwDwg(0CYISI6MDNdcxC^-D;#=ThcIEhQC z2}4QH4jf+r7fBjH(uWaFK4lQf=(

e3rX`2wxzJNg5u0AhHG{5)hJaS4Y20v+rWp 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

|u1x4& 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

?J-Lu!~rBPSf8Pj7qLyW_nmp;n5r=!X9VbR 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%0A%v{a4i&2kInH7Ykux%Z)L63xdwR#3eWmAq2D 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%0AsHka zz+W62S>*Rw-ktyr;gMRR@RXE_l$|GudrDARa51lX`-dz)k2zLZnzw>j)^c89L+XHv z4oE)ii2nJrEY>C`3W{o$7|t1r0?2@v0Am9)hgiPX=HmI5ZIi*pgel$~F%5_@aA98r z>;T+#8-|Szjox8__faRYX|Q#CFFYhiK5H{TAd*6HT-sDV!xGbi5#!wRqH9wI0r&*M zTvdz%p%DwH1M(dUR!QQ5k;l>yTSkOaaH(YdfW~O6SyH`io^APdkYLEPLbArBY_TtW z5aKW65^qz2#i)h*Zp)F0saS!ClTa#a;dp2xjUXVFTbH1UjF9;C&)xR#K}i@DXQR~!3FWMy=0|$K#z%3HK|aHwlTDywilE2U&);lWcRC~B z)Dx5jc*>OE66iso>X4yhX!Xy3#WHma9r+vSSV&~m!3VOAnMAY%cnyTAdiw=S0#2fg zSSA;%m{0-*;>m88goNmj{iDDAYb|*k0Cyx33+gJ+aEW~(l+GaYq#}lNNWJ|d78hBO z?*?>$03AeBm2l8A2m{6dj7Mav7w+dQ=N-ix&<*tzc}enB6P^=g!~)-7?a6A?+wZa1 zI?#h4K`etnr47*URdC@1)8M`ATGT7*=NBwL50F4*WUHXHL4eW$*bok1wqnO%B5mt_ z-g00&a#f&G?m`1*>aaWznkhShB$`85Y5#5mo$#Z~aZ9C|h;0C?Fwv-W2*~@4%w8Mn zU$-0?DZvqGAjNJWW)cko0Ur7zWS_)ly*{vi*8(zQQ3wfh(sm}0QzB{+L2)7gs2JC$ zrtTFMfE^DTAofY=36e-L0kSEALkJyIN61h#wehKN8q0T z+>2A7-u^Dj&xrw2z;L1q&K|-tVuY?r2p7=c_pO1~-DmkZaR|sDr;Ah?4iKFP8Jr}O za?u^Rta|&8SO7P|DbA~8yKLHy7_p|oAr>!T<_RV4g{c z*t}p*uwDA+e`Yyx5zIS2uF8o2(RZPLi3IQiaw3gm_SE2CZ}~YYCCp1NJ`O3iq<{cc zL_6?#WVZ%0@&}e914zZj6ABBbF2!k_05EPimKu_<)u_?$SbiRsu;sW^<_wS(a2W~@ zP7|!3H6f8#gMXQ2@DQdTJuFcVN(NMqwZhXDLNN;FrT6?7f7-T>>yT!VNI_VGGh#9E z*W#OC#KH04Rn^F>M006WWobsB)KWs3)+SdKT7j3^Cx;@FVA{O+B z@D5IjZ$~A?l)#gu+Sa|x^7f8iJ7jWj$ix;%+*tGgW|0^Lb^Gn4>1Ye3 z*rq9&{y{u|;3Gk{1JrB8HczpT$h;a46XF@5`T@v+y71Q+T@ht+5Y*eh!Lo1-m?AI- zBpyKyFg2T9NNm6()`B8W_47AbEG-NyUV?}c{V0ZxD2L=Aic`_v6Dg>LyY$Fc&&A0K z=!J2nQOnOoNEc$X$l{@2(Ux)WS&u^aRQ|F0`FB`C1pG1|Ev6X=1MP(_;Z!J(1%83{OihBE9masU2m7>cNCaxjtZBfZ% z@d)>%YB_+UQa}HHENc(n&Y}y{8xWIXaXGL7O81B&TqFo;?k|7Lvi69YG8{wL!v&&N z1Mo+ygkTP>CVwC6o%e&5!3+6?t}sTB48255OtCMi3;{0m0w19U|6P^<1x0K|u|k8v zMOfM+dFXmDOTqtG!d9mQziqL1WZ$F_Il~2cBd%c^IdglL)<>xFeRB}#i zN^H*egHnp0LP)?d&NBV>Tb7vzpe1=MiR1;bfP^O#;crFX2&7}I`@gmbf6BlY6$LhaCW<8)1nfh%(fvN zs$)1RJ_6VeIYbQvK$V>S_C1@e$0G!oSvWwht`#nvm>j$i_#4rYG4r+~BP{=rydyb> z$)OmHtxMq#6GBeg=)u3o5+Xp^w@{!3yltD|+<*YdtSGf)9c#iJwd?MF)3&)F??fL3 ztpkk)To+=nz??ZaCH9`JsDA!yuiE}O5T`&-1U493qH|G`LXRL(!%3TL=tnIxA3(OF zBt%`9@{D9#pUUlP!TMNf=0L4D6UgE2Qv%$QU4628nEED1NS4Hf^5~ zVZg!*-a_u5fE5dw2y#j~S!deX{GtV~#J6Q(&LoL06KEJM1i3_sj|V-)Pphf>Wy_{z z*vuJqD^*xl05RlZAYhnG{tl*0AJ`XJ1`j|+^3#*K7YLRR^hT8KA*!bc6ve3V?cZ&Rzuoe7wEDynu(?2VNN`26K}kGbmO0`4=)wPx z#mAu#f*xS~orXAnDb)+!tAP z9oSM@031*omx-~0*Tl?o*wAlbwe-|IZ27q`-bf?iugOAb6%oNMVkW7hMni%ySFQX1 zVj)u#OawYcD0Bdv9Gi=&gNw(v5}pwBs-J(2<=bO2XrUl06qXR6)FzRkaD)jr5~5?h z>po&xca($>3OH5LSk%NEa>_^G2GN}LRAcC$f1btK0M7`nC}q_6WwNjkAS|!wQwiS7 zVyMAKmUSm6>mb7@pF+qBq)&nnh!;h8D#L115v$Qy>K9E)>*9 zB%_I5xX|L?@ergP7#LOzOHW!bXgEaT2~cdvQvXxS!ck{IDnJIqTMIh=1OJo|BshH9 zs(|yWng4Z*i%j^LR65}n(ZE8hA%+zx2|^llqGS2xL@@jfi;rW2v+96^=r*Jd7f+j` zXoty_Rvd=L?lm?+FwzMCV)DnB5WFF>YpA$^>JYfNG-&J(TC5EjQzYpCNev!avMCTS z@&NE8awNmj{2DNI?4L=a6a z&K*CGM46yi&%CW>2H1!4W%e8@2F@KSBhdhfmVkkvms1P(N0z~Z=wc*>b~HfTA}tgc zh>|H}5Ok5y*5>pNwu2MQD(9OuBAg)@1SSU2K-kKRV66=O!Mi_q`{zNQSI}wxitq#Agn1;WHmIGqSZp0OT0jK2OVlgK zc7Whg#z2V}C(%^=eAwb)p|6QOD=~9aZxY^#W(rD9sICFjGF5PTiFmMcv8FBZDP6l%NPVfp&=( zi&hO#3PY&g{u0aEp+N|2Md3pth_N0Dn22a8ECB8AbUO6ywnHoBMiB5qYcX(6D^8Ym z=P(LWq^IsSi@}ndzXWo@DT2|m;~{29Em4RftN?{qgKsR{6hkDUX&^KyyFg$^)rRaq zj9)?}RHnnDyDaidX!wxs6|is|67Zf#N^BVg|CFu~MosX~SVBXRT?AivRZJArJ>@Id ze5k%u3QtggslmU|^6l(ZwyZb?NCn9E4%9vqF|p`BNXpdD|8L8#qXJz<3H2}PN(v{C z#pCUB*b)X+>OKG7zqNhh*z!`TMa>Z+45UDAoilDLV z4Sq&sno|0J#1YkEqww0xk;TVLp;`+n%ftykhXjab^8`J7Iw?}8k4$Xw@l4#I=Z16x6rUBv2v_7NTBSUDsx(4%VVK5nTYz@UQb z!%X59q>h4$P(m$I5u^;FB~d^B8q0xYmC45_R-xEW%3}$M5a&U3z?iXfHNEpB%dQg@ zH+@yA1_=kT6?G*131f@Hr;OjoB;RC-EJ;HG{Ao?q7FaPSl|vxf7wjH}%LtDivz$1@ zLFBFUTmUXaG9{I~oiJ;NB4C${;?DP2u$!UV19Z4hsYvbwr6nT?AH$AK;K)*mOzs3h$;fp2_p6r0LZ8D1;a?PWA4m} zAF}K;Y#>OgFOh^BK`bXN zA@A1a@zu86l~9--b(mA?4B)+q3_v+Ebluo&s9`nukJ|EfmRQQ`00hCeSa?n;b??+- zvsrPB>gS(f(a!U4V#(NMOghqQX?>eXbs%C0dM%dv+uv<5P;3~Ib)paYRw51KA*5iU zWTy=vvArJrKeKoi&`b^lZip0EO6*V3P>!sK=$8 zoDMHosSxZ`&HTTzm`4sKwZ;_)JUpIo8fXnL6JVx@iOI#)&;O-``G+hA;Uo~IJB$=S zkOrWf7=)SRW=25r3l;+fz=8-GCI`teESk6_*vp183`!8lq}1E*dFA$zL0dxAKRFNS zCipNvqjd)Ie<|pO$JY_XH(TD0f|$36`jZMPcnp{vsZ}8`BY>y=L%scFmU2?Ka*DP= z##lPk_JEw|_vs*%Yz3X0S**4&7l$uN5S#(jfIE7V`0!L|tR|Al0xA?P`ND`J9 z#!J)#9CxW_Bq9J~r*xaF9rcYG{H`UQK`@SfmKqyc=^)zxq=d@CGvU@73heaMz3G#- zXP$Cp6cboWG8X|E$lA%oDOc)f$B8Ccz5VxWZh}(<-a!>50Q<1pKpL@?2xpK!wc1?# zhGo-|H&8Jy-7$zF1srA7DJLX_lm?XsYVZvfuPXg1=&0SHzvas@kW#dQa2@dlz=Hv> z+7#S$0U{(3!3TR41_rQ0+T}^>T)@g)+4)N!`DI(;OL>S?9AF%PIi#PB=up92X_eif z{n2ss3;xgTxtI0`h%6`$jOiwc(h2geXBx;TX+e_eLv!2jZT}n@S&gm>|0W<8RkUzQ zILCy#P%=-$$NoJFybP8q0%{nHf}E4ANvtH;8xV{%ozsWr(JR|;2Z*L=Eb}L&d|-S4 zyAntd$g`URgOPdvhqr%D@Qbyff)`z7K|v?y1%QzPUv?87Q=94!x7$A_+rY47Xx)Jr z33I4lpoSW~52zXKL-d(@oel7zfbB!aL!Y zh-6Vk3Q$P667yH7S2Sc&@308eL}!GE6ZR1nlpRA1iRc87n<@Y)GFET@cb1=H9MNH- zxS(SN9VRG^73LfcwZ%uV7uC;y&XPN}G;`uh2}bA?ftO;jfQh8!r-B{T)p3_T@`tuv zFIBf#M@npnLctFp76~&bI0Gj~JD}hH)gRklI(ADy-i}Kn0Ss`hJryh1Uw$ccv^sT{ zAF!ZRlsS;y0~n!9Wt#!&qFMmlXF#lmE~mZp)BnMfEpizcV~Pi0;D|_}uVDs>tZ@raj{{*L~J9BMw|B1!l(1(b&Fc2Z+9awubuCO{H>qZaXkXx+ZfB6Ugi|zS`_ku`)zPf)CZ)FeU9F5n3>f(cY#|6`WP z1Ji~A2bjZEAfOQt2iy*e2bsKq6x)y!%IYvR=*z&Q>D9t^0q~B9e1H+q(`&O;S>7Il zv{9AI@k8tYodZ;b6`<4>LK2chg@~8_4Ob*=PaXn4rWv0rn*ihk2*(zji(4PQzFMF>oh800$`vDkeOV+yx8mIk%;;M=#~&)R|L66{a`fp80+fPP+t z%>mNX4kxOH{|_xS(TG6F=_vXVLL5CVyN8gQG@Mpr#ETkq`!mbWaghyvmUlyDqWYs{ z4_=6`01u&TOuzjjZ{8j}XPE3Ek;9TXb^til9&}J%Q!F)SsXlg zB?%JH##3`fK>$&^`u2BPvI~}tUd@P9(RA}#@+4?UId90}r6+>=`G?=NJ@^836D$EJ zJCGBwJ`ybyn*lOC@_fER{rsLE-~Kt4mg-*sQg#fD1Ekx0jBpM4oP_v>Tfk zbb)bng!?v&r;!S3b{Aa-$WIayOUPMic}n9NczJ_dd9Nk2LT^(_3M&6DtUo~;L#Np< z2B-n5)$GXgEeW}(JCVA9(^EHrXJ^OJ5RTlcLfwhDUw!-6+EU^Wq?2D$pU4ZbvsBbV z4@pNmsq@w6{WmQ?$JR*47gXV-!WCQ)$P?;tBv^>i%Uqwh&$TQZ@dOe}IvJpy0Lnq7 zCWTs3`w@^+YD)9+?^_P6+%bc-ps5s25uhGTJxL9zG`bf8R_)_6%iD3q{0HoY7#%GE z@F(*~$OK?YUTWZXAFu?-_}4%h0iblE=^r9!tgRGjcAR`5QEe_hZW%n;ivS1Yl0-K- z0ym@s)Y@SNXaLMCsD=B#EEpCnhR{l+k|ct_heZ0Adj!>_77&x#+FZAMJHjTWoDGk? zqlJK|ji5;ozENXH%U}KTJ&Tv2EDe<)r#2K)f;J*XAGMeZvSHfgq)zX7`rJ7Bya`R9i%}0{9kx@dNhj6&^mw%z(~@+5F7$bfa9b+?B`f4EkPu_Gaed+EMZNLk{q!SNL&Lz zLHA5G`2XFqaH!GvNXpfSYB6-wfD@aLA3@Uz@~_>*%PqE!_+7*&(mas(AEgkO1_eCm zZipM`v8CSr9hSF4B2xLzkt3cYFp!*-2AE{ld`C;Mr~3I%TD~2l21kQ{8BsAT3$SY| z%~lD^=mUwpRzLr3i+_hV=7gXP#KZz}OGh>2V?+q>FZe2D>n=V2DYkS3lP*06c^QtK zk_Wsz`WGT(Q2-(3S-=0%`~I%&0n~94hX8*9eop!0Fc0*fsmGFy~K*NYt}!%?~esFUs*2Bf}C|Gp@TZY45nGGNb zy#g2Ruswj>i=#@vOZZGUWziKAS(6n3sUm_=8|czESRa7J62S>{12GXCI^|hqu@Ke^w z<02xtafXX6V<;~Pny3o^*MKb^36)23{R5FciU5i$h@Gf+ zC11o4sh|IaMIepAB0Q$>5fe-!eG2kH;i+8(#zP;IX{YsB7R!sJMmr*vYnWXM_T=1h z&7**M0ugB2_d<*1g;WRdBo&ml1jM(9^Z}(%>`FzN*(v+uwkQ+b8BO>g%utWe=LgW5 zyb>six?yM^J^260Vz{xE1S<`s2XS^Qr5Ip=U@&|PF;Jq{{iBw*6VY-ZDk%ci4*3EM z6iP`9KlTR=ozbWJ(=B$N(ne@5UP;X)d^lO%;l3Ff#`Bj#wlg>s2lthNm z&C)wqO1>xrL$U`QQ)!$3cFUd@#Tq(%?q!pni~*yKQPb|?@_%WWI^2CtQZ11d;xQ5h{)CMb&bA{^(>d*bVu`R} zX-J{zecO?X9Kw2yY>(dYW|MzYGw{ z)_@gh`OsjU^qF9j%R;GjX2Sxg0!31gp%(7bEDJ|Pvh*DStVQD}cb0(M!+vlINHGYP z)X%@klG~tNp@#@!0^wPu>I=Odi2|OJBo{nQ{rrs&Zm+u>6*_T{BLLk>pW%|^3;4BX*#0>XKR1U+wIV?dx_rcAV9SSe+i(RW6Gi@rHWeKRm?cLa z!|h$3qsq!nu^4bzwnlq%pehOPT7%i!rnK^{ajL`EaGm}0U} zq+`6H+__j(qe!+k-%`bl;)>FKsz~rtOdA0bXg6jPQIG!l$1QuF)KBUlh|^GELGu9y zN;43Q2SGdjLoM9Ye*0xh`xs0TshA+gVsWC?kl5iD;#B5r$QorGcuj{pt5a(Iae!`uX!M3nyJ- zOWEbP2grFT5=RZit^;I1-=u&3Zp(QmtfnFtmIvG)EQ`iTJr%YERfjZ2AWl|qw}~Yg zt@vb`UBj|rrZCR5UFO7c@)(HnGM8SK*aAa9Rb9lf-1;q-};i9R?+sq2OB*j_qfNbU@ya0f9D2aUBGZmy9cN(7A> zJH7O+-)2jU5L&`YU|g|QwDW`C=4dWlcu87KTlrRs7 zFoE@u=jlFcmmaumeE_|ONJrtz5G*2*h4_-|Mer1~Zsv>D3wY_xziWE{X*~2kAOO}A z(?yUBs0YD}t_BXo5SM)Ozulff>GKW_HYC?nYa{w(`w4SOB_8pq4vsJW@%GQ<4q;py zR)^RegDH|U3eZ4#M0?z*`0~7&ur*Z~M)?3R#sf~>$MC{*Xtv;RBW14zO#u&g=;P>umrF;H))wh`?> z325u{=HK3)0SG#jlQb6*dJb_EeV7P40O7%QA{_C00WV*)v_z$#NAz^$BS`DydW~Ef zJP_DZv%qz8dj9V|-F`csiBKLC6KOm^7>EdJ57Yr<<%FF^SB{Tc&_rS?P(Y#-YSqzM zlb<2ff$u^bMFeIfhTmJ-4iI`#X?cXufHBkNow!SmFT!E=kx`6x=_mil_5iM7r7T`L zKSN|7@}g}RR3G?++^a&&sHX2ui}^&^E@&eBI-(MR6QLZXx`dzyDY9{e)7M-49-jzX z!y3@R2XTNj;HLDFaztGJM)_&<`t8|gyGiRUr1g;X1Rscvh&jmE*pf6lQHS@kt(H#e zoax62`vW#tpi(2!fi6bZhj56_s_NUn&XQSjKOnfFSbsuBuBbqq2)2difODfut=_m_ zXvwa)XGsV)8d_32%vJ>>z;8(xIhu;G1Jv957jAz$urWA}RNzQkMVL=gdmvH1h0SDM zB=l7lG!9Tn&^@@<+xQ?!ewX=tR4 zR-O~BetyvsZNX8qX5^Q!7~l$Ax`iQU(ePcU8;w}+Gk3PXou~|679a*F8YD|D!xEJQ z7Y&oqBWhM}w{_E%%fV0%BXGdp%l?Dg$6C_Sm!6SWRQ2<3upHWgJx&i2DV8VL=kTI; zVDQx5QqzdzQ$PO=OEDPYQ^Y0G5|wKjXyC$C^z2zWl+cz}0w4ABr&)F#8HMzAg!&Lg z1spFNvm|J^K50y%5A0JcGta+>T$Sa9bxKjAA!>t;!Dk@3AcWL!e~Kl>2SF!9q-`)w zVF3iO)5C;w)uS{U>GjmvT7PqcH8l!sacmQ^4pHVHDhW^{@$Fp1roR21mcc_nNMOU* z5hqb!1Ct<3DY7;xfiURKue9WLQiMeD6#EY3lTZhlNQde?!C7L!4cWS_s)tzvI;5da zftEvzySbu_F)Ylpgd9sD4mR4IG z%@`4I%Y9#xUC^wJ?Zcj-MXZiAZ4^Hd7GN8}5rZYGCN(C;AYLTlL;G!Xr}$?ULrwc3 zkXH;nuqz%nz+957$rUYfxsfq=TRU6=GfFi>PL7BPktQ(&dJq9ZOG%y4pTQXQ1Xa!I1(+7%v4ZJVFB zxCz+!AsXerD(a01?C3m3)&QO*tx*i><7+JoM;6QIg)RcRr{F?ZMjE~iwfpG%w7Gb% z<=cgL;olZ*1GzOv3~iIN59E$a+AieU(!SqPXF{zsWzxtq(7wU5P)sO>lEP`ZViV3< zef!T^4lGkd0U{!KB2yNg0w4PSLLf`f^Y#+s80h6PwIbRQ&UK!B7OB|5)gc{|>iG#ppT`9RP|=Y8k{1QMXY z1n7pb`a#RLli1K1fSVN%0K%+evw<|oiHKLYwcPmjKe22Z90Z31S0Syxfu5l{u(zTj zra2OPsrvSpTJlM`zDGDqg2=P<)Y(EK8XRDnVH~6ZE~P4f<>ZFa zUxYjv`Mi4jr&v5Jg;dx?;98tt!gTOtqi*(^W$^I1Xe*?N2C`j%a-sRC zou_n~wq0Zk>g|7Uc{{ogwLBF?$lr)Vf#8@rk|O4uXcbYfP6=LOS$9$~smVb4h?0gN zn)Q=h2&#^TXIO6a_Lo>roM_F3phg47HWwIB(gO6xD9Jel+S+`;;$^7ICjn-X5aFO> z0!1LjMuJMaG{9p++VM{~m}*ZDc@PVeH`zILD!$`TxwKf+K>GZlI#g zz+C}Bl2#&Pq(3eNibxC9;6H3xI0O**2JnbXuB(G~AkZO1qIDTy4DMR}{M#)DmK#}6 zBtet`GPCvN3M}Ym!QqLpQ1q#vf6(%C@GDwHQB5XI6~Q2InM4+370g6#RvVnzW**cX z(10M& z7>Y00U1_I+V}@k|0pKbiE-pj6sdwFTEel7YD|nAIhy<_(`{1lnfhlMJ$&JC1U9cqJ z*kwEg-Of2-99(I?E#a)xK!a!|`t7!uxXT!+16xsIJk6Kb zuGhf0QJh2qjCB)LkQBS~0N@a5JZ_MDHu)rp^UzTso0fMe0l2+qT3$NNvL zNwD-Vq4O1UCOtLrivS2HZBRRtk!pkSCzh~O%E8#^(o=$u$30-bz&;Q^N?l~8lc7(w z92(J%5d{-i%eCKf!2%;-?Z6H|AdF1zFKoFKgXX{u&40murN9ijPm~IXo|z4GXly+S z$dj=$()xz-QK)dP0H(Ks^!8;46!EH`|B%H+!{XCkf;>#5F*sRbD&Y0h_v+^#w4Aw6L}0KEl$Z#$OEie7!&2e$uvWS! z$)$IHfekf<62jzxPa?pg>ELkxu{3|9?*jC&&ZaK?%ztP*Hnh(QMaM{-1c+5rm_bra zQxj5Dt`w>j^mOS@{*eteMQe=OP+CYQ(w74B!KE9h94Jx*OEYlezi(N6{tdP{Fct1q zEIpwH&;yDZGIU|s)dc=?3+ySfa;l;@Y|>#6Qw8D0&ZWPvm?MKR+P4^NdVXOtP!bT! za@4>u5iepx$u(av2lq3N`p2wdArQgrOHD1T}w>J2ru?8g%YR zp$5KA{rrFHx^?vjnpZ(F*U0 zn2elS8oO$H`@0s3mvjv9MlQu=(_)=qb-1?y_?>p*sDgEP_$!vA1ehA5h;8fv*r_E) z^^L5G#2D8jmC0)A{(H;R$sM@x$zU(oH?mKfFXLeWuqD7ITT?&(K8u?k%o^$^Q2?ei zj*zngwIo6fIzkzV`1e{o9Wo>gt;jA>nwA6%+;2pZ0^vu%Www2P$yaZ08|rPTeW$}F zHj)ZJs$@8@(w3KkFcK_ntbXHV+dl^?liE`hboe$YgN4~ZY)xdzauV^YZ~xQJ+x|IM zs8C`ejv4ifT;?sEPC=?sZ#rJ^hKp>30t{P2X>k+|z6i$+{@N zWzFt?%dML)uFsZx?M`2voWA6Gku9=hr(O2_(T$x~T<(}Z@Km2PwhsRgCc&9tKda|>(tj96Fr0uTmEDxH!v$or*4-ZdHcZ?_QoE+^fPZ#^$ ze$7V=V|jzm$Df@pdH#*@$)D&~4#Rkr+c28LgMN8seD7tqN9`Z&?Q~bGy~VO0pZ8k# z=Pg;f(|KZE@Ag1*MX7w~PEN*=zS`}<^5xY*J3i}*+q29;E>9Nq^7PvBq+87YL%r9H zZ~79SZ#uYoG(Ppq-JW`SrA1(K~W0{}%44%h}j`pfktlqxy zqV^oV=;XA;q8hJpJhB@Tch>jI>*H!W8msBSXPm0>d0*x>AA<=vyS~_&9(<)~#^*Xt zz~*!JPp(c6c64u>559JI{8)8-c(yWQjwf|~{9d&Wce6M-J32Z%o~*zZdz$M5o_o|y z^Wo?SWxy-PReKuWdXp`Vj=Q7dL-9D>kr`aFan@~}mJZCW+QZ`a-t-(DRrPj~mb)8({>9lyOf zJnnX8NB%~)iCpj4!{eQk)8pmAmF3%~$is0K=1@k0^_{vRx;d;L#V2|(#pcuZx~tu^ z85|=U^SNU}&>wfxlsL||`EcT=<}oIGoDZ*=#=xF8i93#jnp4oIAZ*|*vj}zoA zmYC>qLMlHmn1+AcU0Gs?jya7(bUD7m??FSZ;A8Xob3E;Nck|rE_5I1NcwWmZIERDX<=);j zC7yTT?$H7a!;6d9obBbo>B?z(UdP9(<++?2f~e!eYu)i<=d=CxkFJfwc8q*BC;F&5 z-8~_;o`ML^lYMk$nrzR9{p8AWvA;YzA)`5$Svfg7;L(e7+T2$wKUQf;nbK0p^fQ_HIUO#t#d8Jy7*^if;pIvvW2m3jY>j$4q zX{6@?uid&IA2ScQxpC#%@?bnHp2xg8 z=Xn9&c5waRbh6T(PvFVn9{9y%HGDt&#(ur5rj6(M5MJFspD|y(E{X8;Y|k5nLl{Tn zG4wnev)y-0c&Er_%BhbIPnOpglro>59e2}|_{s1_jsa@<;IXs)Yt``-Ir*9I>EVzV zo2(vuGG$(V%*XW9lKv^Bhrn9z5e2d^9M2P$aal8wK)^qI0P8w6&RDLK=939a3OCR$Nxp1PKeNG6RhcIJm(#}3TI9yq`>oYq5Q@v=sZoqG+yL3 z$9soWyCCX1+(T7{7;cb>c^?1iYF-=-|2*H5Okh~a#b7bUaeFx2mB=zy;menLn(qT< zU_qnaDSmMjea+KgoEVyDj8k#45Z%x?A-*R3wBx1KX*Y1tH`dnAgA@*HI*!%D`JVR0 z>;N4Dfq9~*^U|Ci`N>W;A1ny^mE&q!G+(1WIYr;vEE+VnevdV>&9iQ?7GXH%CY!Ud zVkyT#`-(EAHPmNje1R|hj(+k^cl~rajpIukQP_EkjF+68)#KA0gAjT8-f~|=n94ai zKGYZ~wr?D%BN;!>!?c>KEiRhTYGp_~Snbt;Tr?QdiF5XKEUR{JQCG*u%ayLeQQNhl z66*2jr>H>|M~Aa%bxfEy-msdSaYY@?_Mk$d;UXc($>Aggcg$OiowqXGN;T`~6ix9B zL2%P8CRyD$BVM0>^>En^CVcI5U*{H;OlPJ-T)2{0I-bgn{V_a zgJoTLHAf750iWa15o5SIi;Ky=I=W(WZx3p?#X8}4 zw16AafeBp6OZ+|r5*RSv3fgWsm!>D zevs--Ii_PUGRrZm6H*Ur<&C4WvA^NySmIZ_%{Vvb@f9mo=J1S6B8c$|+Aysw-X@yn{oE&%zy}p3;%3 z*$6)O8ThJD#L*cUPo_Wwb2)BB#{g(EryBh7gmXCBVPnXWT8`xlQad{i-REQvm~_=n zlj3vaGTGFXO8Eid{y1{Sl**Wy5$vYUZz5J47u51>YAeTKJ05uB(bMr9kpus;!sAhC zdaJWChEA^*DWP6=Cp#+;eEa}MXx4bixU3VfGEo@|%Icq>H__#Fe3N69=)eD)vc&)D zkCS&Y7ZEvnbmJqgVv4GE+3f6AC)BA;7Q^S!nBTDe0LM6R(@G1aFwVL!QplL1ez_+_ zE}D(?X;GCg0Q(ug?uy77c)7BB7)rU!m!a!N|rN zC=4YroT5udi)%dkM21anbGn>6UO3MA#^X;wXvR5rJn{OutDYBE$0s|QDA^_Rd2H5z zvEy0S173-)6rBDQZdi)t>7!m}DD@ua#WC2{ub_?^xUE^^Gmchv|llj{GJ-I;^P{6+=PS-NLGxAc4Uc7l#TlJLkKjPBUBaR@Ey&d zGT{3JRm&4#l2wNUM|)xdVn$iA z06Fq6gQ+D*)}X9oqa1S|HivuC`-h{skUT;eL`U?|d=0wU{S_>z-r@CAP@Er26Q&7$ zRIhO~Wag)gohQ5)oYe-J{<`R{H{H?NZ8#>?tEa=2f|E1sj?On{{bKJ>>N^)F=+P&j z$#H&GFKSTbH&;&=2WNXz=;)|>Sv`1V9L@%W0}tTq!pNl@Lwxi3r@ItBZ|>!DF|{UL@`Y|e;z@IeFJ2s-PKTm#dz_I4 z9!WTOHnf7IAD0H~9p}Y(i^7mKcE9xaAqrHPKu2))1-If(@SG#*X?%|TT{Gl({;x{I zXgq$7$HVxl$(9X$6s2e2_y)%`)p)}ShY^kAWQQFiOXKkqldxij9CI7v(QQ|sUD@f5 zk5>$cW5~Vn@S)dXB~Ijcr4{tKvI_85*qvhpYkb0LW!LS*d>#U2KBi6D?l)^T>U>`X zpSymQV_0a++4fh>{F+b0xcwSwjJW=!b9&cggr!X8e2lRAF7>W+F~as&%|_U#r_@LF z`qA7>YR{vm9L?m~+Ml48CF(xzC_tV{V0;?=nGIS~ygn`SOq8yK29CLjaYW~_PqSrE zW{%X8@glYC&2HB`worE3Co4OD&~#*m%=%ndS?inT(|In7q`zs!${u?>I4bO0pPv2d z`f~qlpVV^Q@BI8zFV<-flNZKgVp+c*z`+&}?nhFxBTA zspqePLZ7ZwZMJD&17Vz#E49CCKCC|DEBmWvSL%ocE1+5Wgb#eslxX;6QLfU^g?kA6 zoDEG!2)e&FmY8jT{Ei23^CidDmw4nq*sk&+V;1Rm_A45A(DrqLv;01L$W6OdGX=*q zPfz;9c5bZdtKIyQp=Wgr6!c)%{%yqo?#6=wb#w=D(McwC(xu9}lNCO3M#5Q-GFRJuB$P%z4~;S9Nz-*>Lx&Z|)<#wu?>y{;$>N>Y z(KX-V$atU7R-EPwr`$ua!5TU`awE6iVhzXR=M$ye#$4r}RHrHhe$36v8DI3cubZ>r zC**B&BJ7i#nS%KNSR5D1@st}gH$7b#8I!?s^sBa)Oi0M;J!&ubgkGjE+I-o(6P|!~ z&HKPH(=sM;g;-4=Hr&>;=4O0CvbHzP(|6u4n&-~ZB^YzJLPWdkN5_lhifcGM9}6^X zKw%!IwK8e6lPB`E^SV8uZ=DzM=v=?mI=zQa)GOzyeZt;2Ppe}tY@7{6UV*Vpz>QDC zwaFz3lXyP0gw{9B%jDB?S>Lo`t$fBB=IRW*;$rpkF&gl>`xL%ESuqH03)( zTaNLYd{WFSZw7WXKHTTtUO5&OMk&_q9N#3~MZL)<$+PiIgWg2F$}y2LY|qB4)LYKu zgC=(|yhR0{=dnu0t7Z#y?r)kMgjXtdY?!+Twbk|08S?3XriQ#c|9E+2cY1-N4b`af zbT)BrD`aRPdpZmjb2-TQzKwi-F?Qr04bb_&?Nlxtop5uQo1{5WDQdhEh9aHHteB@L%EY2pYF=vOd3`$j8^i<57Tu^Q_-EiEX_P32N z^0>~9B$T{BufzCIM@fe~)K}YGV-cxzgxH_9q2rg2*Jp3?%8sz|23I&;L9(s`9jDh( zq_OdmsRVI@rFGN^ZoFfQ1a?#`nIzjN-#+zydx0eKH-;lQPP?N^wV%b6XVsl{92@)& zFA_d;-2uXp!k&lT9G*cASI1W-Q|p1q)tBHn9!VJ{*Az6}?%^K2ho><*da|(ub?4;p zOd35)eeSvl*l}9tl49D$8^y(b58vVHw5+lj$6*haZ=ljB$!tL)R~FT=G-yrSu8^P( z>&<-g8hs0sV_LNJniKBkSe{7a&)2NpI39UN_iKCA!Qr^r;|m=9h2h;SFz5IZpKP6a z$r=+-r5ZjdPtCCt{np);<$**;-NAIU9iwYw_C)oH#iudH4k+hB0jZA3|HfO!riRnp zDSm4cQ9JfX!E8W;TW_7gwI}kWGtTA{eBby|143LAy$9zvx5gXid3~Z@IM1o0jWqV` zfGw^}@=$eZz?kRtD%qtyj$9$8jt=H=ziQ((KJDc&dDEttU!#C?@3Xh{u6Yxl|EuQh z=Y0m1y&Psyq1=v$+F!mY)O)VF84V5PtUc%BfU-uZ!4v-CRpo(W^rOFc0I#;NL&rFG z;{{tHZ^t-SWj%w^wr-}b)sE2c{9J(ytF`d-OWZct{MZ3Z@^#E-!4|~9Uj>W=k`#?Nt-RByaQ$!HzV=mes!8&c8a z^6Y4RyR`?CINd9w6FNuj&1!Ndh5yM3wMq-KAk#7aG2gJgM~B_IYOLvKgUxr1$8LRP zq+;78e@V zcHD~dgQxp#xwd9~6|IMegKL^5r~PX8@MR9)9QVS8&lw%t7L@G>H(Fn^s$AT9+1uP^ z$Jlrp+gzuE+YqN;aBPM+CzN1DIu#<}7pr>wH_o`k6ZOIwo$y(+pusVPtJ6wc`v0=Tv4n?*6YmU23-&K#yrfZGPV>DWX-o-R+k ze*OA5{f@Pj_}ZX1EvC66A5wI7fSJybt>` zT=_b-7??iDQS`AHmC&VQ!CfoMD`$siR8*WEO-t|S^5@rZnGPCMT<0Jk9=FwN-PS~& zEg0Qe&+8$UIyyc)oqeK*Q~tIyo;QB32Y#I0t(3guV|~H1Q+T>YJspkndg-|^%)GnB zgK;_lxF5gY!=eMut~Zgd`ILu>Wt#6w^RWH{vwl!XRUQG!`2D_4s7g=fWWq6Ao43I9 z>_N;Rb8;}>8tGG}v}u<$JK~s?8uobeJ#y0-t>&&6bd2M&7%v?2u(mp_#wGXZT!vg} zigo9$>UahQ{(4B3)@6C)$Q^CU`oYqObWt_U9HTgL;=_~stFIi#)$8~nZkt|;_W{u<1J*)bz9G#8Nb(FGfx_~1|r>T}&72P-j%2BeS49m`tX6;MM(p3WF=#TWn@#gJP%Dq-i+#MHD zKYRG|wdLvVV*Z425gkp8eg{#sn67zh9G#MW^nl*2rHZ}|r69b=-8wXPOvvX$X&j&9 z>fp+hsyH5ck^1cahz0v@CJP|M2 z{34$ryz+Z%%+AqX&Sz}M*|3<+9!ckT&>UUKmUTEet1%Mevwc?Mh~?4bvhXykIc?FgiSBuC(cvxUy+wzYc-^9H zw@;e1y5G$)51XL7;gV-UZLY8Az`u^Fvz2t~T%Lcj*L72s)ww)>ICGo(Dx!G)WsbVV%T9(Nd655qi2t_?C5odi zDWW0@yS5Fxs_Tos?31AH!?x(6N`~@v|KmHB4CQ-nH->U&&($Djgba_I9_`(79!M2- zL0S}LSR{Q(fn=EFQB>x6QMYZ?C5;-$S3Tu%1|m1zJkCJo*?;s4?s(FZ)*rQcRyEAo zzn$0Usb5OX@ib%AIF~mcE{dkg!#eJoBu>k)?9;3c(=aXLswvB+z9qAL^M&=qWN6lAU(h_eVE&j2WwBSswioMpNy6;O2{lA1z)v&FSAfY`YhqLUeYgpZ)OI z3hJ{T?hdYg#^Wwzo3#ZK*d=M0Rc#+NSsRCS-NjYb6v4QVZy5K~6=VL+9(hmxw7)jo z#&_$cBQI|hYTbJIBhP!%liu;v_k1dUk^iv!Z{A0)yIsC`*O&ZF8ht)8{5L9=TQ@JR z{`d+68aHlz%dK%?Zaxj9u0LG;?~lBeX?o<9U&7zNlAA$aF7y6s`QPV%|3W=Z9yi~e z++uAm)O1|x4%*e7j3He# zyIwj^PLGH0sz3stt_<;3bx?Id681^dM%n1_r=|L_=NTh;Cg$*zyE=7UxASnCqzPMg zPFF{1RC1#G7S|bwNiK`1(mUr5&560~;Hsx19fWNZgi(#1?USa5*dR=-+rDH=CvmM8 z=a()TQ}V#t*6FVG&a)C%Ne~5j*K@G*wha2RPI??w*tCR=nVyw*KF1i*T_-~h(I0o7 zj&>MD?An?s>dGq2`nsvAyyGACZ68N@sdnx=?l6XOr`%lq*s247^PD72mj^kgIq8$G z$&0GV>Zr}5s%-P5=p!|dZ@t?X$OYXmzo_ec!s8ZO92ZH{6lE5-+AI97xmYh)k7nJ5 zm}{gD$=`|9{8&L;LltF3mgij_hhg7$S>I$;-4;>DKh)d$fBl4pcT!!&lldB6Lz>ao zW!^PuT(%86JSmc@i}7L&K7>4DcJ04ohF%w>Eje9;{Y+CCR7H;cW5t@d&nu=XEyA{r ztEf)fY{ z@>oBcn3y}oeKI$3*M|wQa@K@d#Hp&tBgz5unaq4A;oW@1g zBxP-6G#@d|%>(op+n=*=eniQ5FvVANCD|WdqR%o?AfSXUP~*DxG{almHJE9S+q=zELXNae#n0C%>2TCneUTAZ z7ZLNPhVo`}itbRNANLul`Z%cCAn4L4t4bm?0$6~QJnOSEYjlM6>*n?$#W_Ai_byv` zr5$%3PMSBsgAtic-vgj1&t;$3$d3v>s=@fJU~AqYXjszgEEH(eM> z;tBR#7BO3;*1^{=nhxXM@pf~s?ljH7wEDgQa?29(u0C%fvDH;ob!l1Z(R|RHln2Mr zY~8i)yiR!$#^SojFftH~ChlU4bJ?T>`PgPXq+RnM-7_X#W0O}uo&qaerAa^p8s@MB z1=s(?xq0#Vww_WD@{2M8G~JIK;BEVEiiU9U9piw9Jk26bo74y;{*h^n73hD`jp`NW(WFqdVU{H@vq2gZDF>==a1B`> zanLipP(Na35s!=ccpF>WmQfm$r9;@(HH1XjMn#gPMH%Phh&nO-O*81a%^NAwb!0Pf z0GtdZ6M=?Rq@z*SN)%ePrA90kCL3|PwD<92gEKJPA`jX$@6xV;rHc4-Sw?kGM8N-g z5WjD1+_Oh+-F$_c+G|4gZoT~HZX3AQALlPK_X>F`DamsBmxiys`B#>EjRD5FziV7s;Eu$`u*-h#<?>^L(j9-7in5&O)Xe_8Ny${3{*+kwLzOBlh7f+edf6C9aqt-mFfr` zV|;E&=-OwPgQBV{lu<1ZFz9%%tkmzA@{EVa(SR&;v`0DXhpX0~0v8R>uF$~*` z6g4c?1qHWOZY<5Fx=B1*=eq#hNrc%pIx5AnATwc)Dgp3rl3K9 zi6QV^V+1fxtw zhO`GtIfq9jvd6Jyc@V})A3lC4@E1=u4AO-Tf#za4b`o;lzo0AufMwH!NmoNr#x;Je z25KnKAA*{xE%Pr;K>W^S3z_1L2$YN!HYG`NAl?j!K8It*+14Our~&jT|8Gpl?zUAc zF6YUB3?TFf>pacUAPmIJWQbT=P?tUmN)7kiZZ67g)l{?OJccCC0eFP+PwOV>!Z^(; zg0U`(3l^tK)Pnq`nGD{c8p}8jqo`|~BoopiYXbP{a{M?8RKhEP=ITuAH%eoF-nQyB z=R5|G$tp+5RA&iD3g~v7h8>W5QGa012*b?29W6sG~gX%n#CJac*;Xwe~ zil!lekKrQ0S8{?JkZm=T-!fO_nd_k}Ru@1!4=1T$j9{qYiPNrwZzuo`>OLqF7{Js( z{Xb-G)TjG08~_xc#%128VG;bLFU0_CLhu}OF1OZuwLJ)wK zI0`TnbRECn`Na)JlZfw+cxTx7PdSLQOgKRjdoH^#{Xa% zuV)atO@-OcQvv!If{RF`R%aEgZ`anCF#rp8YHpw-?=)?>-lY9CPvALcrLKUd8@wJ$ zR`7i=^ti@?uwObOG`eO^%ALbarHiSxhV#56h?qk7!i<0)>KWe_#UXGco2Ja8L~oIM z%q?<3w<2*KN1nw2X;IHSpr}G_Qy0uj1uv8X5yaYvTrkIR*Sh@E&xVb<96@gcbWTYI zWMK|y-3LXN_XBLK-TqgaL%DsfQg@z@lnzi)k7Gk8(2$jd0Y?T^Ez}C|d8;XnkN-8p z$J{mD8scrQLVNXXmPSEGK!i+`R0pYBmJ_kDHihObf7o<2_ikUtfm=gy;~xb6kUW3eFLhA_hv4E0L%6MzH#@IhZ>K4$Rwj!LyJ~No0g>OwjVW zOY1VAYzNjoZZ!q=26G^HRD0K|8)GS}^L#W^Eszzr>{674X!UD!;7uL(P1C}*>pcCH zriFdR`i@5DQ2;>I0W~JbpDF}m1z2<(vq8|&q2x_=l8>7^Ui-_N;T}y?>@58DraC&HXk`kz-k2OSc zr#T__sOGJn&M&8<3SuS%R4rju03`!7=TaxxP$;0c>$?pW=jQEmn=0oS380G_M9KpD z6ShVLSwvgb6pEtD&nlgGzsignZwEs56f0600`ZBeg50VXoleg7h(g41Kz#HSRQvSG z=NLBRw$+BF-i;KcXUhwBN{TUrYm6937P9=w-)7ik41J!ne3CO>YTkPEsQwDXz-B(*5B5&EKEB?Lnf zULX~ZD2xCZH)@(bWZLp)teZ$XkAnJNOheIkm~<+t0Y-Rtgz%=#n<6juQH@Mt$1{;- zD*NTf+g2z87zE|Ypz$PGeU3rTn?`CzDpc$0(Ehv$irhYTfOeh}@}`K)g}{``u09Wm zM2O&t42hN#>Q~et-ft!m7j)-n=WzsWjo_Q;BY}bok_2}TT!tbu0v91zR^#|tGbFrg zqj8<{P`acnNMblKsKZF8(vZ?x$`y&6<@ji~@+@<+KE3j!Pl%#6D|@gAgv6x?eF#d4 zmr-g#altyP+1SXZ?x;M1Ly)sYiRnV*NX`KIkX2ETVcQWtq88KM{srcu+`fl4*3U7) ztzyJ56%-yqh{7D>)?m@%sO>-zbvAd=9K=1eV>;^&s9^OL5OyJ@Qq+<`ps^t+ysKhZ zD>5mJ+D1P2LmRMN)n2U_i=Rn>mJ+BEcZ(1f=Sp-)9s|mp#e{~vUYYMUr3V+s3ni(r z_cIb;_D-baLJp(57jluED}#yvcViuTY^blEA?4Pdc$lIlkIm>p(dA8r-m1=e;P;MN z-i~V3P7~BWV8*3)%1-s?amiFrIzaw*Xxiak67)rBpQ0(GI5Y0GJ^ev*x8ARJtG@xE zuurJ|?g6ST`I}W-l+@RrS%aUHRQ$Y@;Mz%tPCPmg^-c}6?>fYeW-e4Y{d*{RBZ&Dg* z>XHrd5lJP9(QWF;(%%ciZWg35f{PmRKqdKwa}5{R?X zk>ZbH88skUU@v>AHcBg>VvgcY8k9WdZ-bE06)47IdefBjY#_Ttr&Pum8IWHJ z;nfM=fbA|edtG(xZzP27c|wZ~iaK$>340{#GHNTM2xulU>Z)I8PRYfgb(_DDAPFQQ zNs1N~KmjULP-%eDhLoFgoaY)e+Z;(x*^b8;379M)=Z^?n5xBJ#$pqqG1oR;U8pW_` zbv8zFPm7*aLIQu&g7OG3H6tnlM1-5E`!FKiN$FZu)2B(FoNqT5=w9mcj)#jSh?P8@ zpgt_Aza^81ka6|}gP}-}vsB`dVT5%Q`~R7vx&3I**D(M(<c;32X zo54o?VZ6vs_L8uBkFUox!N6Yb2iNXg}uWo%VxT$z&S0JmQFOBgXW_$k@OfMmc0YT zhQ=6IttaGoJHpGa3b%k<> zo=|aM>VRL_IwGLx2>5fF93kPwM?Y@AviF$f7SEDWWVtE=Z59`q9@9eBQ_4s&G*S~N zH~L`#lfw9c&@$!{TrlDP)JcEorx*zBos5aT6~4+qkZAP7IiPgv9fIUT2v*WxA`iSYF?3+2sfK49m z-Pk`oKH8lw4R9V#B#j0dVoWqZ9g#R)D@t165#m%NZ$>YK6BEpTU@h2^g{pk1bLdnP zR4GIUs!9M{8mLJ>MR+IxbxJREg7O}d024JEDxs5Q<0-BgDzn=PsH16V=N3|f#}=ae zjLn3h)@yb5o3{7we3^jrDj}*v0fXk9ZlzRJlriTj6X_P2bpw*D4$_5fK%0YPT!T*HesQ|*wz=vDX`o&-q!%Oo8B^X~BovuJG&F78QMMTsTIld( z^Hkrn8i_w^M`tH8>$Jy1fJwnk0&hz;*MUt1h=tYiyw04KdsrTzI^a5Q0A`!8DWD4j z(2k7i$cThRD#QzkJ~Knz_WdScCws*oeG7>#(k;4r7Bt66X+x35ZGxeJDnbmYSLp5L zI3(t{ddR)5v=8;OTN>G~sL4=tRg}BM4fSuRbucP)f}p{iTA;5n-I4f3j}^7ZfjGtq zy#uJIMXLkIfF?w$dCX-txgpEXq(GxGD;<dM{iMJRW*{IF-IcHb9Ufu zN=Qv;_60TwipzvR-2r}px#wjG-EC+=Vw0eH2$Q(jqX6Qp^L?P{bpuG36)B~DBITv+ z9xbpCQ&Xfxx}p)8Cz-P|4C?q?21Scynr;E?0#&g{s3Q0i6>H#V7`o$;lH)B}+P%&f7C)5C5XVUKuaK%9cu&~Z3_cAF58$(! zncp&Mu5R8xc}UNWDTBERanh_#iJ*x}7Kq>qEV$?zTE6Ju?qjCy6UXGS{}-$TW)5j4 zd~{O5(o>$s5P*%OB2CA;UwfYsOi4n34Q`32=+@?G2iM4*rL4K6wICV+&NY*Q2XBeW zlfnR8KVt3~$s^ClH%do*rOKV_P84nI&Ru^6Ua2pr^8tnrlRADE3G((c>7U zNIz`O(!Js>JRdJ)azb0fiIOZORNgEY8Q4PlS`DRlLv4*Q`*Uwns%wsrN+Exl_os-!1a0~Qu zqRFUqlvCF59cJ^82Sr1NWPJDd@S0qaPLF;+L+S*bZlb)BZkoV0_9z~0mC#JHh$EUhTJz?_e$sa6S^u@(>nE=*(JGerYm%b6Lm^9MPAO9W>;~?IT($*+OOa`YIzcZ? zBROny(44DFiW7ZqbL5Cpi70XeG>X#WGD3Jn4{3U-!7ys};UAb*V2IF$1Lv<%V~T<{ zLwi=GsPUxpA7cOjLwrPAA(SP`4t$wu2kv0)jt)HmwRuG&U*bgWo1vc)8HU8s%#CuKGF`WI?I(a|)Oa(2jXA}xl0uThf+h<=OpZuP59wB~*6n>J+Wz1a&2owTV(7|u>IFZF^fiI;~S4%IL7>t4X7y8oFO$Tie~3$%6Q zjK;*g#`GG`8u&xY&Fao$f`+A>4#^)PF6y=^^^;QFp7VyF4Oy3(pX5cxV%~$z~z9Mo)NlU{QT5pD84C&`cQ62*M$e>Vu)%2$q_p8S`k0c6OvkHbQ ztV?bPBxt4aOB^=zbIk#+sLfOB^j!0l3oq=Bh_4*@QaNDcsNUuJ8{7f(0K5V10f-Ax2BN(-+DV+6e*BIh@bW}K zN{^M2c2-3pS3n@7Lc~hDEnFros|%wvaHHot`fn~|^(k_oQ#An}UV!=l+J1En`c<^W z1jxi;fx!{{YjelmBV+N)wn49PmjQxelvIeFLF~ZnI>3#H$g4Hl#=grO457_%-MYV9 z45(T_Sxd)S$Z~0rP2&%^P`PQ4&=@>O?c1L>^9BjC9`#DNiLsUHCn|l29T4oN^wTR+ z8nt3-XfC0~a>G1B7sim-)5eAUgenOcN>E8-a~yU6``2;(7>bKRrwcDIjS6!jq4`z8 zF8$aQ=Al7qN0i5lY37&1XrWMK!?0zD)76|jXwJzqhp~7DQDcTd#bARXkj6Qvpg~R$ zNk-(Ew3#w`X5Vj44K+7Sa6d+pYRZFWOlJ7NP+vl1f z`+`E?{X>6zek$F{2`~ZLNu0Q7C#=ZB<+4MrHV!KtfbW=l_r4j7^x5s5&caA>5aYlg zp?L-@rP`8~aWz@fhOG6co@pcmcMFcpWi29*^s{KObOaMAIC>9GHh;q6(6_f^BNe$( zg!5@;Ixyg((^$OXqI9E;q}d``7(h0;i30@_XnRr7_n1wkEMI8CM|Z8J$5-kmQVSGR z$lbUYPCAFv<)@?0p9zA9Fi_*?o3r!4dMI8ehiO3|E|<>Ag=-XYQ!PXQ(X)-@B3hkn z@0gP_@15(uW`{ZgFb5PmV8&cAM0f{~h0~;$Tb@C)sfGGfb9Uyv!&tmdk2K{)5*Q;q z1L6zOtPqHEvlnR!*3gjGM&|T9QxEBA&r1m;lv(wkp1 z``>3;=KEwgL$#_;2^=@h(rlJ)Aq^=v0#fRl+1upV6gLC)X|nIOmu*{~780c8Zg(%Y z@=Dr-)d|3esj7nN>mwS?>FEpPI`#cg;Vi zb6x3EUESAJcR$bl-1qPI{e2frHm)%jbS`OL|g+Ac1tIO(BYpsG3N0snaZc=q2`VT(M(Uk+BpAX8HPNFws2)EoVo z>pfza&=Ir#r=DuY`& z-nyt3EwF6za9);wRr3pde=TJn50kiw*|^P1kw%C?+SwK{*yAUo@k!s;u~uTfkym$C zwwEQAmnwNQqUbREeGOe09Spht;-(o(Szw=bC3!+$m~Z6yEpfD*q7P}jFdLMAP=TDp z;<6zykKqR)L0y%atm@bDs%PGBm9)u#L{{#F5p!&0t8iqlxiGSZ;=j*MBn9ApGFQMJ zFsup}VXBOw4_;*fhl6EPCkSjX4>l0y1DI~0|CsOTm&Va^z>ZTi&xfw1PQbEBmYYlxlvlmy^1;rgn*Wy**sH%!$&}7_P3+W8rn# zrsg=dqP*x-UnS4d_oxQny>vqu&LqnkoGulHE^sQs1*hd3GEtE@Yw)B*4 zJXwil$ zrcpeDX3g>+u^@PHRazH)S^lsX~t)5Pcnsm+*i%+l&GD-(Nw4$_zXWtL6+O$5?S zA+xvUY=< z1PfN%pn1b=_%AirR6|mYq4zc#nmn_{VTwBZQeF+rtGC5^%H4H>#~ekm;Ujhr9~G65 z{cU&>dpg+{_-*-e=3D)M8&Z0!BV@3i75s@F?-UK|Rs8R+&(a(U2`YJOpPx5tj~plo zN)KEnDr~Nm%UpGSsiA=OzxWMi$zph1Io7FPJEsA;e({Vcgh@{*?c zOA3bW3V*G8!O)%i8w_1$4SIrFe`2Ic%D{9h?9is^QTVU&`z+|^`x>wH$y=LxyDYzk z$xBBgjUzGXX&;F?yIg%$ZDt-YiS+b&P5!t-@^R1~CHHK(z1?H3*z4htu(PEg=lUWI zo(6TU0mBMU-sIbJMZ4Ub4D>ny>D$ zWOZB5F#Rw0$k;*}Cq`x#A2sTfZp>ZV2GcA@I#kWcqK-MMhVp%}zsj5k6?_D+4?QItXbl$YFO{mSwPgOD>)s_%9U@zvU2QSZZ*`*>1|^0g(uH#p*hx z>6SojG#>Ud`Jlltx25#3yp;o5^Tm|iydBovj2X$vMFV+BjCmn9fsuGZ+R9|hT*-w4 zadPjM)+-^fC`m_mSi9y~BqE>-$72H6B5ounQmRdI!7ZrwX~lFdcUxu=%D79*!CIBerp63FDmDIB8M#pYyG9 zezRJ8s?<&3M$*F1!qNk3rJQHP!ajmEZ9omlXZ?8IG9Sn%CQ-r{nq)#;RkOMnsODKh8n_oRPx_@Wy_yCyPQyEo;ld2nAm zQI_C5GHchUVVYZPpQ^7P;_z8f1vPWS9uSz@wj_}WvNLtnb6 zXVWLTF?3$mX}p$Cwz919gZTWcSu4a!ms!KdEusY(x(TENv>LL6i!Zvq=;h?2`m>oa zVr-gEJl&?*kA;8oVDtQ)%k$0iwSF9I9;P_AbiEBC-)4QAW#|0ZKf7B98b6PM>>C-P ziq1ozaQT}Fo{N}>!O2@_EoVhvttQKmxn{Op2n*|(Iy-J0$}X&MHA7*jc_Iv>-sz6g zoAmYB>9adu%GS=T*ivli{s{RH_{|)fo>ebwXaL&aCN5+x+0(}yh99nWw%6e-YfokS z?bNc`U@a)h3FyTU0#}U32JQ+(hRD^(j0gU5jx#X4(N%wW(&mzua|+P3RF!R zp0RvuX8jbh_>p`O5AF+umeY^$)DOk{jnsletO87!c%;x&AVaaQkqXpFVW%I>E%dsR_ha+uOk$HCvMHI5OkOW)$zvZq?o72^C%vyuw_dnXrdOzYB{{Oi36Mw-E{D zsTBHW^C$H1?Pi5$9dxm>0^%*!hz?+Zp-EwkSI1xwDo4`KNef-v=rY~lD>*9v&|-s^ z-q(1MOk9Bm(z!&Fsft2!HK6GkPNTk0Hql#i;)`)k;ctweuIB^&>cZeg=1TJ!SC_>= zm@~9euyo@M85!`}dhxY{rwQLBemT)zm{lT(UC94enouTAO&td52KX^UcI)9;nH{qfYOuf0wsh=X4ci)-dJ{CMpXA z7RG7}%Z!ZO-$aB{0>TXNKemR;(Nt8$bjlI(O5x9oXM&n8Y@U&{q&0dJF(In>r2Bqbk>R$qq9gEe7}+}tr!9Q(-w`Q>~akH(yLq2@C5-KaLJ)k0`L9(v@OF=w2z zALIK+1Sh|d<5rKNp`XGO_vJ^(rRL7YX0b1aXQf87$Gyb(C2I09RVlE)!n8gn$jP^C z+<_l=x;v2d_LJ4lou$3&AN)ijvAo+Mx0=U zFw@7?#Qiurl37p2{T|Ir-v?HzRY@XCTRR&Y>zjoHicXh4{U+0D=MCYK|1rrI)JJ4W z9+>Xr-^@+OF0EP0Mybn4nIJ*N2wDwJ(uzYFY1^SWL4y%?oqXk0ZaeePAzSg8O{AAn zNC~%EFesl4-s=q;J2G~r!-kE_`)8=I$%6iU&Kh`3m9VliZ33g+uM$fcIpG77_|xDx zkk#SQ5;&WUrrq6DKhtUV^Eukp199Sdp%$)@QpFmmsYaB(Si#8NYKY2thzVm5nmnKH zIQGKUs9qe%3n#>6P5^FZI*sA}OmzFPCtIAYVB+yu# zBEfrc2FooO){IS|00uK57f7DTYW{6sU1Ux?Q|xK*Scz^e>;>T<;c-xLgfs;vD1-Z@ zMecjD?r8VovrQ22$~+Nn_y!4`+PA}!MSNnQtWEN6J~6+tFFNRoMJRTsLtJN-l~Wf)G%-ySij5XI zY4bMdr{~;X&#RdW)#mbcDZivn&k;hzrhekGsO*la8)7{mM56vl9>#0(*Ye2zB{W~m zqNXadQ`|lcnxSrJZXiev#iTBBbs|ir$~vO&%pc4n+sbd@M-&TBrSGbnFX>wF>5LWXs#HFp%{rZks4{y7aKiLNWudxYXmMHXWjM_H z<=glE;LUq7TR01)4u3A-aMl3or;{$7LqsN4;y7`%{52D-F6VITRV6vRwJx?4$uUIW z?H+3)G^Az{!8%D=;>-cFTVjEIe0L!XO?zEc@^qaIM%hDzBC2?~%(;E7^iufe`9^tgzL2#BH3}_i zr%>bHm%L0BH&VyM$tw#F3c}Ptohiq#mktL1L;hgGk@)cAwr9#bAKq}J12iC%dV*t& z37I=osLuFxGKkV|`w!&B_ocZ;uPU^TW>B-P=jA`LXZr&w5i{28Z4Zh{#)vZ6Ft5s= z%eC1KPyfXYUgnuNXbo%K*=!10&R(^V9L{Gf-WIqWT?tAi|ljY zK>HbHw#Z+Q6Nm3xG)E;HT8u}60Ac_%M^h!ETbPmH9HIfWU{*nVbzbvb^9jwnHR^&g z4`$F~%cQSw+rC3~+A|3{QaoCyva)IhX?JoyuV$_&#);jUQuqfzg}cvm)bdP_=D-kq zw5Yl11Hy+a{iweuU&-9$VzZenmug^j@J##Ay7ko@e<(<6NVt{oyRB(|lJELbK5Cnr zHL9xl^oK@72uc-(K&S&X0{n7aWvnZ*YzIC}|VMhJhXJ9l(9|RF^ z3y0Vj9s?AnDJkjwNAhF$p?&Vtn)tb;a;?`jG{9dH*ITh~k;y>u(`< zGtDG^k=fFixs79)#1>mDS<*3I%z0Hc!5A%+6*lBA&i06T#FR9=!o2Fpnju?g`L=|b zCo6e%zK0*GrcYPGg>?n>Q_obJg+#s)<|MfYoJm9PhT>-<1B0H!NE9@aUQKVx@x%uY z7BpMmEVrHrI-c@nV8{f$L*wZi!*PF8aOvs>=|_GsU(W;c*F;1&Ex4II93D{;X4ux$ z6Op89tq#5lKZF4~U3w#Ya(?(-nOAz{PCq3BL7^N;<&2E0c>-tM)$T(A(B;wx^BwtT zb$|3ri*(dt?XBcS&aSX%=W(O7iW3+u4%n&buG?vW^#wWi>)MKI8`2uNfMpIGLpSLu zwH@1L5A4txPkm%68&R@6hSG29d$RT|rcJLu-Lz?54=s+ZL(=(oT$#_EzR{1vxzhr? zoL4LJ|LC(jcdD8x;*vRXu=1Fd^2_0H5D9<)P?*WdbEhBA55$W^FSF%B%}eBdhCDTR z20m{_N940D9Jy)`C~P{-{g?UJAiNF^ZMW+?ca~b+GTT~0Dr35E6mmARF2-{j$&}M- zl5S11lkk0xGd_w_|CtWz0BtFP>qXXa~j{1*<95K?Fgetq=RrKhjxtIY|xL z$Zow+N{7Djx5n@HKH17X@Of5Na^FGb$Dn$K8|o|e)%dQh0L>>%W}J0Nn|tF2e*M_m z4m!GdHBhdTYWVnzJAgN~xHpK=8k@3f893-OP0ZYG{&fxpUtWzF<*e8@8Nu_W$UPt7 z5Lwc6-66}DS@cX}lA7$N*X6zLqX99(BC5@23&+%vy~_crIa^?}Q$7jx8gRMPyBI~O zkCQe1-jmr*ny+agR4cqDHg^NA5}Yp7odQ@*d;r6hY%5_E=wiv5zU&WWYkDl6Q`kAW z3=1zx-w=_#Jkc3S@ourd<%t7nJNPW9gvp{F|AX10A_M-=v1KWa&{78!FaI(1iX;f{ z4Wrzfb3P5lN{zt98D{;N$8PF<@FeMJ%JowcZKn`{z4>d&CaWtdZP5q-e z&;3JUnz_D(VR&!_9o#(SR@bN3qKBfBPy|NGr;Cyq1)x%;qh?KBI6CY#6fx%QHbs5rkLd*I_t@tSmp(Ffl0!{zrq+ff-@2A`j>SXCh3AZYjWE<*Nq=k&jW#~aKGz|?BEn-%L4d$L)0sK>Pz zKH(8!-$h(f12PZn_S5tI-FcVz*#2AJ+Mcc~Pl_(eK9A^FLcmdrP-g^-A?YFu9?oh( zASwLu%6yu0v7|)0MFEJ|Fr~E>;ox)Qpx7bHRCy{_@_@eY$*!3P;^4fqG{czR*(~K< zPF^RuknsRndo4&8vxtZjST;9z9Hr&KSLCPC{c+J99++i%t%&yJRW8|)Z*hp&_0bU> z!#r3d(kL|=`MH;;4=0YKQyOV3K7hUJU%xnuzA`zB&hMC5&HoF}?#WYO_=dqgSQibE zPz&na2?t(QZqlOe!h`;e{Ny?AN^)v7JJkn2VDTD+1*#I!s1Z_tz{A<2H<+bFgFp2| zx@i{v2a5`~%>JF=BFd(+ZtkYetU<%Tr1bdS9HlF6#bDmYdW%xasH?8S z+nPQ`_z^h<{dgvaxss2$?%!7(uU7TudZEcav{@J(?q)oU7M1}9(Nt^#1q2VQ3+@1S>2+WTBRGTZWUt5 zL_{{91F}^qM@A*oqg@f}OgDi#?Ix`Xep^lE4KCk7vF%*qsPK?p&=P(@@{aSXu?Oxs3P+p>qmO|$+_g#JfUsU}_)#S3p zOeKsAjIkI5CGX@P=g;H813virnqQTg$0|@d_*?1)q)|z&l`Vs=*+&cE!q}wqj=!C+ z=X^QJ9d8K?-U+u(-M^427Ad0kC|;N#N})-|<>UPQoTCvevqzY;@Fu7T$Gi%zQ3DWV zPn!drClbx1s$ZDHrWZ@%AsvZCjF4;#SvyjAKr_S)atH-XaPv!C9uNUIt81|}|?2Za8 zX+W%s0BNUqLE+9aOP@>c*mEg%jF1r5dUbk^iqxJRuqejvAQX=>dlBuDZ+9<$E|(AV zg=Jol2SDd;a7yW7wk4b)66>f7Rg@06^aVY7Y#|3oR+)v!KA@D~ZZfC=2m<}WO^nkB zbiWTWPCwjF%QYPL9SIvtMU3!1Xg7w+LuREFO%z^e?2T}EWzv~ZOa4UuR4x)3SI-ve zGO!zi>4Q4qo28BFYQm6+r(tvML`iX{kIr}Mh1ey9jVP|_J9o=&xv zR;pi+Ka@wNtMwB3_VTTrbIr;w4Q2zCzqV9{RC2H6dp<;IZG z2_hmR(}Mj-zK#WPIYvl97A~`IX^9{L+{Yr|rW1nKbaT}rxdfZ zixafReXc~N{?opgyO~pkyxv?Cg_)+(x4e5{dl`7NU9E2H-T3hD|JJwezIcnp)ZWb} z@5_Luiz~~k#k{FCoD;+#JOn)ahEhVvoUl~V5t*f`&E(^LQ8xC7{d4_v`=?&tZ1%4I z@WnZ*^nHFDqDpH|FZ#=~yIb5l?EyfE>mvT0PykXR+L9E!IT7!Q7-j9pR;k0UQbUb6Lij(8y~SXY z%ZmnPYe{I1lFfS|PaJz-ZtKUUVrqGk>Rx*QkP^4hGB zTZ9fCIc#Pm4f>+9obZ8VoPq5ZpvE-qjb4{CaUXhixmn@yv>68R>$mL&QD`C1O zujnUpO3H)P%E~$?fp*K}Q*gHJ%qdW1GFj9<$X5d=gHi=ZapkKY2U6%LFmN%&+w(5#tqDnnCZ~!ALTr-2iDe?*Oo14T5Wh~g_a{}m=z4P3{t46a{~28j$JV>c52vTOYFFlLO{I*B4&2vS+Fj--VU8q>s zzlDTO&y8rwUCwMApF+liAeR=5H`zJ=IX?mKmnEiJk_=@iQb@Dr(+RnTZyF#WDHC^a z${$9L2{>wH7sJK;)VVfyL|mG?8kN~Hd{>NHO*4lU=bg-1TS4$3ZeVE1%cqa>=Im+0(%8*# zVpsJwlk8i+j~T|&Gfnl>;X^$&%4Sf*$=!u60XKk85P4JA{EMqTAZ3SF5~~fo<7v$)5hg{D#<%f1B^OGT&FDVPFPGk1r!n z@-$Q+qm43!{jE(tsf;#5q^30`%d zfFZp@XTLw&IS(%a@`|sBm6}l-K43_8`4+I^m={>G2fYI7sKGKsZ7VPCxolyHfSIG1mfb! zbRoAVpUunvQ}%_OKYW;{J_sl3E_MZx>!qhb>0%6mnZmIJ4^BmxY??3q=yVaWY2I+k zJAFTjf_dEf+9A39MejGi-hVsUL=OW2>!*&oy8XiZ?CvDa3PaO6X3<7Mdw9-q9E#(94lx!4Cy*kRLLF5cN0E%2ho9yDEaJPlh&3Q`BvC0w0G>5+6PTUhL_ zQ_}SoEIl~-?6zG+EzVsfzgn(W6sdJ0__(HH7>rcy3KNy9Q-0>wvx~7rk9z$5Uz=OBSUY z>~g?lC97LDTpLI1w25PQ#ibDD_!a5#xGOba+T3bNfh?%V${i_Wyx}Iqqtatoy>!%$ ztb+LYCFk^SzcO2hY13*`l_JTZa)@ju#Rvv2${Yc;+2J-8{>QdOQsobG|L~ zeq{<&oOSzjh-M?S4zHfEEVq8ZlQel_f|NJ#8(CYrd%nh@D6|6&f`l2MH1_0lrbBOQA zMpwIstmRXe6XPl9WDLjY@Jle5H=z;5+iQ_rDM+jId-=^)_B<|OPA}bAEkr7*+vd)Z zvc~Uitdpg+)VvJA>>?(hU&-h4rTH}R>aESDS-ZWwQp%B($B?8aXtq$priBD$WHo^r z3xwiio*f~B93H)Rdm~WeGCMWs*lb#nI+5oGJqmQFtyQsAr%9ish2Dqf3Y3y$pFY$3 z9#%lpF$xk#qv#w;kwB&B7`|k#n-|`g?T>R$RfR}{G_KSokt6GZ^EX`h&3lK@Z5wmU zQq=J!$6$!nW_xq}nP&5;LZ2)F#hkg)20MV$!c%R;nbj{MqA&{p{idvC+da3sQOF22 zz7pmbTpZ-B3rdqc0-EV!wK!OXVI`l+Mh-Pz*{HT}$0VW9*k>RKNAfAF1fI_rdbmA3 zR8Brwosa>im&-rSx9GWzTZJ}9m@5bWx_#_N~a7o!U&u{diA(5eO70O|x%Ffm}+m>ufh zn4ci`&r9{C{gC-GUk&c6FSf533cG7%x}O~mH8X?KMlqCp!QYwb^mn5~UF93!Obc;2 z5zn}8W2*3!2Iu10wuWZ5y`Ev)h_*0U%12}ywp{yrAXwkK{=FCG?eDkxanSyrtM)^+ z&n}YUo0)s5gp7h?W+X|?!j6XDYDD79PDWDhViEhiw zNxtJB&fmtx`q^90lyZ4RsztO=BXI`9Ix*4fR2_Maq69gUWD2Nb%%$_UH+BlO-KaQW zGwU#&ji)q=dS0=CI46BV6J>PrEdICLqVxRn=KgVW>Nb;qHKCkCzJ-2`MMW9MuUQ}m zevUC-iYtCV{+V7_SzaskD7etzmk>zndig!HqFzH{7u^gs)QejtQQ4|^EJobNU>6xw5P_b^xv#yn2w1g__X+JITn&C zWb%ZEv)vgLVHp#tg;&tt^>1F}IRA|#$NB1XdF`p4Rkqt_eN-DFuo3chWFs(U;%+w_ ztAoP4w+D8=%c%B?{&2Q|FY#s4Runp9=b_)Cgj!LLp$h}RaXzb#%?!E9+>oy3Hx7}+ zq*&`oK-m!ZFe;x;$8s$s2r+pDDGDiP3z$l=-;mQDu3Cl-XeHe;A2s80cfX`c)>-2& z(|sqc+2Um8Hxa$la^zorWcFq*t{Tvk%0hIyQXy0n3e59Vgq#aN6)a`^9iCAN34BjJ zH9NPuRS5GQVtRFYd^Xgla_-m%xQ^Rxn&?RF&D@|K$~VWA&GlP5%`?^0g@8O3_JPQl zSApJxh3ocF4^?A={Rzv@jPxi*&eGVnylTSxD zIdt$zm3n*EUwdwjjC_nA2gu0z2-BW##Ip;H7*Co1>ue?iArNhvq%hV|6@;BKBeni09RPRpU2^|yc_pL{C7+1V zV#D^yu_r)o01pn=)&aD9;tXmq?ry(qt}MO%@|(YvkNPfb?`)KsYndH|%bs*8^SNOQ)0jdynyi4pOy*KIkQzI=_<%@s5dP@C(!Fs^gYu{svb|ib z%!8APzMZ(DIT2b0t$00RJst_YVL~YIk31!LF7L^jq1}tJ#`$`d*|Kmn!crFzdNC)R zM$rUUqw?oRE)^_Qn+@_U`@jKpuH>HOrNdkxe93qV@jb!0_c z`4#=F9|u=-IBzV;io3nEs2I=gMI9s`xI(1`j9i&Eh#rH0wyf8WSn0bk_}-bTsvI|^ zzA28u2o<)j9(6c^4$r$0Btv}?YEtrPe`bD5Uzx3wwyrd@LMsto$E{oGGI8K>rovsD zq2n;@GEd+#ldRDvM#`Il)C=x8jNNa;sHPY&RhlVK#sjF`GIax?pJZ4wF?{cI4`ApO`8E^~!uNP8!?;+9+z2KJmVOs+5Ile#F2Im; zDhrRs&tyq|KVK40lY@P;R5B8PnUku5=x7iYA`@xgVx@X$CMMTdpDE4bBl1oC;QSj4 zXXuhoC_b@pKAeDTrac4f0jpVii18FrYbNcxoNwoJJ>^pE3?2nm_3*LOft7>O7Q)vi zlB6`8wo_dAujV-H<%2C<=G)>4-N#Kv#lgUt7_v-<=ni9n8v_;DBt=f%nLn6Ej=iX* zQr6j19^RcPue3IU6SGpmxC0|f$}!;%vaplg{M$LC^g+j$WO%#8&S-VH5SMxs%%zYU zb2;sTj+L^)Ld`L~OgfcxTfaOvVtDl6wJn8msN|X?+L88+S|%S2HC15g6+<8r?KTZ! z=0^X!`KNZYrqa(z@)*wDaPC7$B!X_%rsFZ1kr09sna;WYbiSthyut(HvZX>>yds|& z;#NtoV;Y3yg^b5&WYG62Asfkl`ilJexU_ICd#cPjP^-fF=Q;WJI9!gPTCH{ZWq?=?yk5P41{c*h`B?9^RPbJHFW%gL2rv8M})zo3Y za9B?N9}9<^?@AnU?pw&Am*#2q^QV7yQ5B}rSP2eegtSgj@)_Ql5ApmIY1@-{cw$rb zJ^3yo7>E?OrKXhV*`Q2y?~JD16g@FgRgdkQZX3U*!-hH8LGR3o##c$xs--a7D8!?L zY;?uXIcg#yrDnQG&PhoDc;WHZOA*}P{D=%EsUmzxO?(V@FTC!c5qRS(xg9Pf6dHf z#Lr|F3RRg2!0jGMXuI7?%e?Jh&6e?KFj@ISF3$r0D@jx`cyyvj&cP$r-C~qgt0L1{GA#J(1Wqs~8PvUmOW7Uj*aC*{pc_U*3EZgqoeVRXfE6=PGwOPSt^ zk6+fFks5P@lh4F2%<9tJbIYZy!>Q$bNT9ZGI3D7ci9ghDBAuDqXh4&l^UC~#zwium zr$Q8vp7Cp6YMIzF5)%VYlh3Y!Kj`0hKSTV!`jgWAyL-90v$45U>~ZK=2(g3572fyy zOnAwd75lD7gncj??La`G)Ey|KlWBAE5%Z>eKVO)8@t4^|#=d6WaG3g0a!>(}hQQ@$K zO3u0)`A2na9_zDI@R0yI3CKNyi3kan>AVJI#K^>jJIY&2*K#g@F_)LgE%hFSoEJTl zuq?$E;OB?154|yr^2w_oz{Bag`K|ojoacGlF0(=U3{(5uH}M1pWG~i2V#tq5df2Ri z2PALi+wwQ_utglCMrRy(1mPM1q%EeqY^KAJ)^HJ~RgB6u| zG%}HCJ_k|PRqn)ag<#*MTp0GTSx4z82OK>r|^vr|LpgDpRB++e5&x%LOV+8 z2zM2^s8W=B;PV-IwY7<6!GKL|*KU0NLodl!bM@dI4|-)5qrBs0;aKMDLDyv%#4%tH z>B=BCFktfK|BJV#4<@$vDP^o#=khdgHjau+6b?WJUE&jUx-Hx~MZv)jLP(Xn|0 zIGj;)CcNn|gr+o-UEY$t4#C5T1K(o->gUU`$JII0K5$^bB}Q*Ggw0}eknvDhb(>*2 z7P^|lvn9Fwx`m46nJHOkFmpmrHF|AG1>q=Dg;as}Co^M~(*5_!`mJSj-@WU*HXz0Wb@1o%XK}$YN(IeL$AY;> zy~(@@H{MVPn-%J!j65czb~-TJ%vW)4ZM~4l%}8TPb`39|YEx*JC{mm+ynU6$Y%h~1 zQDu8Gj+9rQuI{V0wh+npuHRayQQmy0M%jIEYkg-Ex)6Nfn0}n`I?v5B>Tx93YeUUB zlpv5yTaziPNzO<8lk2>jyP2QQmUs4=)4j99nAg4Q)#9~%%F%1Pa`;cr_zMH>d1Y?e9VEuOe13UQOolP`cbzbZZT;)Q+6>0Ve?eQxZm)!RGsPipV_Gm8RrB`H9q zV63iI2Z51i_X6*Cgvv2!>JnMaGznfI)+!ReW*B?^F7NNxvqN%zfv;ADdKl^JvQ7%?^J zk^!?Hs7S8pn}&Du33|szrDxObrFfvl-~#xd6q1%ibndVdx&p4b&5-kUC^L9*CWXl3 zAN<^Pz!b}TWk4{~@lfhaCzyzS8USOJjfXAedoRTZ-;(>Z8;K+v?9`k{M!BspZf0?U zrO&VqT`8>Mp@r?fh}qnQeOT%G;Stk&(;^-Lcu4%FUNurd;EwFYVH@ZM}4aol`T!Q*C%y1lITecR&xoF(_? zl{jeUG=MXH$PAg0Qw0L}Nz51=in#6jrsv2q!oXb&Ip29~3{maC0x3JoeBDMtdK~$h zA;Xu3C3k?Ktg(nT(Dj<$p|>*}&`o~3sS62VYWiEI9$0-r3qmj(C@QAY9v>qEqFzet z_;Z;jLu}=ejvl-&cW&)nf6Jnhf5TBFf1%mjJoDT#)*X`H0VXx2D}6Hbv5yBC!)e3L z>0QE^|3EgQjw=4s3m!m6tiXK<(Ua3vfbw(S;tTx1Cx}nn;O?-A0>`XzKpP{9z_G+I zi@(o_o!E_^`=ES8a_`@wAd|i8Z(4kduRr=0UwDg848wl4FHcev-FL0XA6FAHD>MVd z)4PiV>~sEWS}VpoEZKQH=SzIyD_onc?|s?chyCfj&)R#%?u+ZKKQUEO-xVfw;n_&l$Xc@dV{RJ19gfFvFP zW4t?{=SnFy2X`6Fz5DO||MVFiI2=8^00069I0OK~f$#+Y;0atzSr;{msO~L3LjHQ7 z9QOH2P&S$;evCICyOX}f-AnO6i}jgfu{6Ln8M)N^u z;4k}yY@-}3rPw=yh;(?^A)a%2=+TRE=9#g6K)f5sT1>&BbMH=<5)D*&w$cG2aC=d^ zMIpX8bao#C2y785tTcMq?VZiDKFJc_QrkYJi)e|+H<-o@MT|&Pb%JK@0<3>T1|dfw z_h|*ofCF02p(n%A;#K~!qgVL?IcHwViRoZK8h)0X(~U;@tq9VjylrbZ;!8h zPE&kO-rzw6aCK1uyeO>zf)DfpZ}15?ZZjt4LC_P^r3;f1?%~w&;Qs$>3pU)BCsW5Dj;~gCmRG4d zDLW_4Sr_b1dc(d!@o)fhm%2e8YtA;5`P24+clsv-ayQ?YPX^{sc6sgilRfn}r|eh# z$GDFZ+&Jd#w5kySI1WSqak|0&V?OME;CWv3sh1PFSO;`RSO^L=Q=vg`FdfGub1WDf zB>VlC-O=Ug^3x?f7U^|oAr?lQo;$>}fU45yB`3DVJ<+>IAKv*~i+asB&`x*%sUOPB zJApjJEoM$Dbb-}Si$|uoajB-QR88_T**l6uprqBt;>%y)@1MRX8UOfE$#^*%UY%V! z4PPIjRsCMim0|QoeJNcAzs6uiKJMTy^~PIorAK&F{ckK-_3yvEgNpHU76aX#WT1Qh z!5e%5ID+B|Ev6&mWyoxGgd5R~usQ>k0Rd@ic9-Vu+uxbK#@+i4pK7rJ1tWlSu;A@B z7ZstpNY3o#$@mdUx@g$R>GaO&35zKPPZ)c523$)hup{VWf?+E#Lh!HCYADbxSRKL}? zvy_@AH$@NSFyQ6+6D_v072WE8zM(4bW+b*DEQA?XQo7b63RB9$=X_AMvlj~~wyV*w z+A~?eQ;~9dGubG9r9`5lr0lO!*1~_u781MqR>58UoT=NBCZ~scd~31CZzp^F@(alS zVhkrl#)by@vu7OgCu_~Zc3A?EV4DcHJgM)$@$>)b7`j{*`;YK*!m8fP7@{AJE4?v& zus*Y0Q*&pS`)B`Fw#Un2|E{$bU_@^X?{A4%m>A{CJf6sP;x8Hm?8!X2bukYy= zKPXb3kG1{6_MUHV)<)1Pv#q90(c*xzyQ#e~yM!9fWVoNWz5n4KIgVKr9c}fgsx62m zz@Xo1n{F8-jbzJmWZ|WgNny`_;PZWG#m?|0OYo$`X!Ci zjVm9VucUPA6w)@#?X7j*W}mwNPM%4ClMBt;cB+N+?d&#A1bX4f;x%z8P~Ac$u@yB< za%$#u#glgQ&12BS6@sy)qbGXm2Ap0_Z8SDO?qE?xgf+sp8kaED1a6cJYj1qWi}HQ6 zd+FAuK&5>k?9?lnL9B-}jy_Zo^^}FTqG-gLYC<$T%&l~GascW4O0!i+3L*AFYCzDD z#O`W9x)qrtOr2<0`b(YO8{d=z##dtzCErY|?@z9&Pl4!D5pkYL7n8i@IkDKOQ&qF=-Qsm}bFHdf)Z~V&p zyEP?|xo3yq;oc#M;5&dDqTrr-QIPOLs|e##G~C2hRI zKY8!I!v($Vg?IS8JjE`r_*UCsjWUYVuBDC~)eLaS*%-xl@A3}+(zD0kVM!b(KTG$n zzi^H|@9P^#w3IEP2aqry8Db4vqsY}2x&pa8*>~Z_S6q7TIeIT%;9a`7t8=5n7mzqe z99IZjl1Zl;FbqO44Kx?sbTk!M!_xBPlk}0#NWa3}D~m^342n~YV;GP+Gv z1|`)CM_!kQw72J(VH(~Z84{k|m)K%>IfQx-`n>rtd|~nv$AMuyR=|uy>4~lQ4?KtW zHZAx8=A}V~gg?7L!q*Zc{Dn*AK$6npvBNllRRK}oq*ZeQCQNCySk?I(k@qLP&l}(T zr}A2%_%$QQ$=qE@CYV? zgz)g3z>m*(77zYFx{|nNN=hD&?ec>%>a!L*eDj4J{yb_OiP1nQzFKA(#)U)tJ+wU{ zY#5nQ{#|zXH#~3*4li9uiTRv|i}YF~*oZs$e6X^46pNVc)b9DlTh2fK*H)~QLTZx1 zP6^$yDuxTJ4LmbHeS5f;Nhx*X(#3o&vCB(xm{*p!wlln}UQmkOctk0>GFxAnF0C}P zvu+Zp)FoVMOJnIz$%H17qo=&o#kPu)RvMptet+Syw-{yBy?Tpt!0gu+Z!zM$7P636 z?7r|C_m!a%Tjw}+JsK14M9Q`1y)z55k6vY@aPn*X^RLT4#@(y)Ph)fW*6l*xcoEks z4sJp*-VA0f%pJDH{^H$dDS2W!+AB__XiB;`ylmk`^%KcN14;mF93X5^&g>4h^Sa{`MD|k#lUA zf(Kvnbh?y#vX>4jC}jO$gxyIhs0R+Om>0mP-6vYUbgmI1rbZEGYQhN(mNCFb8bPJL zOPI=IUz5JY-TRLoYcW6yOU?cfOvWt>@fk~HI&m>_E2)NZGgET--+$dnt0`AWP2&m{ zcOa4xHijh@F|h359Gp|jOhBpkW~+%EUeea4+T5(3LXW5y5 zHk(@So}X6Rg$x#bL!JqlL$53LKuiSz7~Pkoh$*)`xk*O~hkOySe_vM+)7e^CXNwzt zxQ^yeI=tq0&&NMFLYTX|x^c=HpVqaZFhDNn3b z57E@;F*b&#KF5jk>>fpWxM{pfxfR1nygwc>P=HF_9$WDK$<_bOr!pE%X=MC}n|4Sm z{`|#d@)^lx^1vaf>V-mS_lX{yu#IR$yBn#J0?m}Wy@792KEj}T7r5!8dwKJ;d;igc zEmkVk$Z6!nGy1hbL>N|#Bb6)MEP=h~kICWw;~BL#T}`o4scPh+$Rg@7B9svt33UA_ zb+2&vrsh17tme{JrG;BuH6{IzlEyNd9em_MJ>N;x^Dp!TsihSe=4kGlJDKpX02FrV zdq!HjStVm@aB^R8<1c;1F?97_bOzlSqgE!9Y-1RsbP-1N!8HCI=!KxwL^*!rkxxAS z`5kpC#Ws9TWtgaj6ZoqAN2jl9#OiWV(*NAPHLRUre?wgljgX>EX94`g3KDJpD3YkJpK>z1;sH{ zvbsu-sRQVuyEvj=@xl?+e8Th6p?p|G2f?PPIa!RIO!(1`DOECcTleHf@5Y;dDSw^! z1iX+hM};(McgDor?!fW5-2=9&0;$_tnTgYv{ziHSrmHEo*=xo&5sV#__Q{l0C>kf5 z^wzLyhQq-y1uw4rX11Dpb37Tbj@1QbuM;RHQ!>;qoJq1Is5ma!?SmH-&}iKR`g zGfdyswe55{aWItx=A$D&?Dw9>G;-Q3qI%D6k0*1go{sv1Ayb`E1*{zO)k+QBsHTtc z^~nwU2VY9xT=aOKa5|8sUf*mEU)G}w&C%sVbM)ZW#?tm}>XfOVjGe8s&RawI7?02l zb#UC1y8}vS_HD#tL@uFiKl#o5?-}JuoYUcR-CAjyjWS;DlZms4zONU6yqz=|iX)j! zP*VtiRCP!7(#^k}XQw9Xd3L#3DU@OD90XamxJ4Oh_Jl{U9dJ0J7l>Jd%qQ#lTN!$> z`+(>3%<@{dvsCc$328|9L)SGKVF!DA9F{Z3_Eqx9#O3IW;4{#sr+VLYXoO4jh_^Fya#_@_Td5RJlA0W@SYL_UwY>(uGjQrJ2vcJxoOYJ)}Y^5)j| z(!%!h^et<{XfZ-0$-=NDAV-64*Jy1pQ}(EwlZSCEF!17LQ*IeV%-#MJWosy^P@=a1 zc6TZ`kRS9=@MWrY{H`p%V)tnLcIs!*?@oO-UFC6&Q9WQ1$eALNu($@RO;ZHz#n;n1 z!Xg;4mH9$wOxV2O-D_i-&R~qlHk=rwHHvgj2I&>l%@={Jvboe$_3fqQGE1o(>)y+Z zln}9CI)q~QWQ4tIWz4G6=dt-<_B_tXSS+)KK4=xc9hljhG!20}d)In0<>=72Ri|ms z^49zYyt2Jnt!;TGgdAn2n_b>oLV_t?dptfyELJTfHLPe}y>R7@uHQBWNw zrLG8d`y@p$fBSD919Yw|tZ)zF@J@Xk3)E%7hvS5eN$?8Gp=2I)qXvdI57S!VkN;G5 z{1o<(bsV#9mC-{RW!BS-McG1jjHK#WzaFMX@;9$N&qB(*9bsuMXI3(@j$KdcT*F}*vgzu&!{E#%6=M7G$w88NaMQL79&Y}PeFKYY|B}eV*#UR5!3NO zuGxQOM#I1K5y#$*;-TOaqDEfOyDFcIy$3yozpO!`k1qNvh&Gwi-2A{yV(gK`N{Rt| z-Bx?V%M;ev8a2+ij<$7xLJ9a76mN?X-wc}d-> z=Tnk2k}Twxo+E86eRUr=`~HUo7w6v|a&cai`#%coi8FmZD&k8vp54B^zP5LEcd9An;ormMZ8YX_d~B}q&(?nYt-CLdqI>Vg4}W8w(FtO*@jw5Mz8B%kD?8KXNoJ!& zV>{c+E+d@h^NXp*dlQ=4m1eD)2%uR$>yxPNOomf`GVE6G}~NXU0Q7pJ^fF8X(@?iyZ12YI_|Fcs>yAeX@_$*$ZeVq5Z?Ys zwxEki=4& zmdr3Nu2$Qd%XjAR+1Wh`Clm#>mOuPjy_!~=wPYA6*+-zr$;pN-H{Sk<$8ekb77ui3 zV{`q^vt>}pdPWj686gTlLdQe=@ge#`Ex>nDRZ^*s?#mmGJa}w57oTqG?foF+Q*UKG zruig+F{CyC(Rp||_vG0Kqk|P_vXFoD)yMFfhxeaLbH{9LYioJE&`6;gOc}Wkxj8}u zoIDNk8?D>`4RtsXL7%MY^Y)HmT9>vwn(9`e51sIX!9aV8^%h>cn8Tvxko()76lc<; zWWVuGj%&gnUc9EI(ocswOC?-x48a)AJ*tj(E8hYzexu!B_{0<>h$ z+Q@Wwo(+I;u#n-4y|#gBu*$Nl@~ zS>-Idb=Ff6k?Y|1YCY~Q0|HKw8sf%`5M36So5xy-5C8seJ!~cZ!N(l`!nT<;Ih8jH zU7pO&4AD9Us#6JU^0m55N!hSfB=8OCV6vG$GE3Uoy?_5qPTugT&mR8XN_NFYx7G6H zH{1ehGF-*#SRqfkc!a4xl^=tb7gl7&u5y+^9Zv=zP9_x^e}C7`Y)mpdA@jAnfolkrszcp4{FyU;n|`G9FvRkw%@q62zuu0h7#(J!TUJVeot--fI9s$-wn*e;`}bHHW!GuecuyoyT5LK`0gHuMb2Mp7Jlc9;1Wz4I9l3snfD?E;b_1WH+fA6~% zxApb6EtHS51X8-V_?PBe^X#_0t|H1+V|SL%Zfr5(O!}kX@|*4H@Gfq_Uy)yxmz@eL zrABB}JKY-Rvj@&;rA6pZ99xLXX=}S$E5rw)@Ir`X$%K|zdVt+5?;kLG*WY!1UWNXo9|u+F1pyS>x6k-C#$cg3 zt7NzhyNb;|8eh*SK{+)th#uZ0k@Ib@$ga$b^QYM)TEJljH4#eE zWepkD=~~_Nf&Ae6W$)$6!QoP3D@Xf;u9D7)ZlV#rvtmwoKZbgVGZ3vvV!N<%0vN%r?Q^01$C5 z9JB!;!wH%)W^S*c1<+d%`U?UM$O=#2nE)Vn>Ok2Dkx zlvyC-FJ}|@-TUXSrkO4s9nPmN!8IQ}r-tyHD8sq8;qB4d3LzaKmy8q8>gKoPJ;?(H ztC=%Y>oRXiUqnX(dYysFV{mKiGf*+qT;{-v>2kjOq3kUMs<^gQXc-c+#y+9Zgd7X4 z+^~n$O%Ddq(Ji0>C1=WevP8e#OZx-IGLNHE&4$!q<7!%YDV+NUPCL53i8IAKES>TE z#)oB(<8s;0m`nzZ9&Bga#f72WIA2-cdMHAxMV%%OEln=1FUYWJh^~x_O6GyE*qjS(>f@dE-(IB zr!{~5XSV@pdKjjCvJ{FP?%d9L^yXj4y0+c@2ijgd&{LmwBYXr_ z21=K4-I%9z#uF)2jnk#oCJaa$fG^G$a%u5q3$0^lgv~p^vCqny!7tXUdz!5P_)Y?J zTC?2A))8Cylv>Y4hx+6q?&i5Zy4M!}d}-NMKjXW&3vCF$OkC+&a@cH4G02RG|FMx1 zUz3xuhj+YxdhP`Fa7zB=@uux?g7CA8-t8xn-tGKk=hmWDIlBiCXO{{;cH}GT^de5Z zJ423`HmpWxe2wnHX!*q)XuLdKetNmwRC#Whz*i}zmgw)5F3M1&JD&0O9SoZkZG7zq zrh91j>UId#me)3R3SkhPz5)Vua6|yajM8ebs(XDjXTBI5GLkF(k-Rd0vSV@grPa#D?dq_1S=zV7FH?n$5WP8u zhztamcVIWYeyc^o#q`+ty-tkKZ$6sklI%W){Be&KHEGK$7JC^JGOmAm@doE{K57ZO z%JlVJ&bRdA%lP#7XjN1Tyn?iQio%&>7tGYw%&Sv8NBC?CY&AJ5@Yu#ieUBEU)e7>B z=>|B!Z$$b$qs5mt6MNn9)S+2QPKCGU*uneT>qOS~XkCp2 zdRgys%vvm8s@nI5sn{mACi5CpDZ28;e7is9Y2K?vIlt|q(FBN$SrHkl(NLTatwQMzTMI0Xl^fFb-PzBj(2Z)E|prm7>IpXE1#8dFPQ2|1dz;Pl3sDD#j)xF!l&E~8RHz$PzD)H6rcFbQqatk)y|!zz3cz(!W^IZzx+7B zXCB)Z2Pj+{wQG$q+APRrXSY=XLl|cn9HA+qRkhlvgHtYVoQ+s~H}%O~?w$NjyY#yD zQaR&u1_eU4x^3Th05zSVsY@Hk^47!*`0Dl#ay2r?u zH`@USqy^fGv)#OVep1y>mHDi3Nr$X2Q5|Y#BBTdCFPylV_CcmkD}{YQP~!ANR-RZ{$L~(o~yeo`+EYswNPA!d%uZ z(C*Sn;|Ip*ZNl{~S;e;jb;raWZR~5zLq-RESmIRJT$66my_{xpB^FX}gOCT+(i(UO;N9Tnhl94Rj)bAx zu!>5HlfQC&B|BTU3qF$xpD5!R|M^pX27Co(q$lygvXE-GGL`7(@>vbK`J`Jtuc%jN!9H% zUW@@WOYDAols=0*Xr;hF???o)cm268rRQ%~TW6gGB~^q8hmAR({Ep0@TND!_G&|C9 z_*k7-AHMb%({?9@AfI@;3O_(yZF#e~6t89P`fC^JiH}Lt6ED25NQv(Z$7E2#9NMj5 z9~mi%R8Z45e3_6JPBM$SxtUwKT#m z$a{3Y5JngkLUgrO-!4W^GLLCuA9n}LR1~*8cLVpr4&Z~Tt(P{e-<{WSk6KP_$mLr* z>pNRZ$S0-revUz37l9Hi7`EwG{7D%Z@H9Y#9;a~ipU&HkN33buY}K2~@b0CWHn$XK z38*=2;h)F{A#!2#WqBHq@rbvfy_S?#t^CRi`;vmsX4n%=piQNpH6voQSVe{dksTNJ zA+}7ikT>OfD4xyE+WO|S+4O7*E~%wy=Jp*PMH9k-;lTSt+d(j3(3$%Sz9WA&_gm8T z&ISj9mF1@jp+I_8K-7eKf?lxCv_b)zZ3}jf`S*IC*S$Oh9D>s#8P9nspZd5g>lEIs~i%9(Dhvn18#d*EDuev{V zkBj4FOXEpK=tiqEobho+$m6}hY!;Cx{nqkx=<59S7W~F^3^w*4&S#D0KuI$04E4B8 z3(*J|ku2rKe<-awb|0KCrSw;ZQDAl_V|l^^OpWUNVfYHI5~GAPo{m_4KEF23ukNfY zToF$Feq88iLjo<)BLn$x+sm~H=2dSzt}{I5r!u52KJ||~-KTzV%j{j>Sg_!lgavmY zTtLn`1BKRa46EXRBafjwaSBg5e39VusEg>WCs$z)<~!!n!bzpjP2Q2&jO)f6DI{+^ zARFfmUCf}>*uh!z`>%2Jgyj`p=I6e8E^c zEHzGl@(yvajRWLTiRuYfpl^r4Y=4Ttkw@ZuNnVAXkG!TbKsXx(15mPER5Do#tX;WZ zU~V<-u}NqvZ4*AOlf9C%9Cch3WR>1%2-suipe7%5$IiPJO`SlcbRBQW{k1P2fG=ep z$Q0QRSFCGufbm9ScA;JTkl6(A$^SR)FF!6{$V2n(0asv3xYMlFg=%r4$6>e@jAlbN z9K#95QMU@$&{n&HqLGfUekSMAl$*%XzC&G2$ZbptvF?Dvn=>VYKAa$hptO0*@KQIUmW1Z366px>kRhYXMpI%>{E|ogz==nO`PUZfQ(L%4) zn{pcxGl^MhYm93fbo$1V|dMd z=q}3hm*&0n)zuB~-o~>B=8&hZ8I0|_wyRu@;ml}IGtecSof$h-bcvZ>wv{&km*-2_ zDK#{vXy}joLqu2#bl;TjwL24>rwbSjN2BycSkIryB@2nf(K6SycQXz3W3Sy2f7WA2 zf`TIam!BdPVj3E7^V$4nx@H}#m9-SBEMr89QWh6thperqJsRW02q88P1M4NXpKu+y=f(vC2LR;8;<{qSx+mG6~!MH?$Sg<3Rx zQT^(SLrq%*6Gf!5E_k@C`Nmy5gXFQSQ5;-IMEDe$KR#!8GLyeZYYf-XJabEQ znYRJ@*6JW^)PHUDg`O%lXUsxgVP(O8+viro--;n>fcd={bDeCj+z{#*u{Rp3*qIe?B*RHBoP$ z(kAB+2z*ilVcxuV{l^w5RX?1hR9&gV@7fr6u@}#7iU__&>KDzv zXibIY$!PcEk7ZZE70m4Evf#Bs-xL`kaLmM*ZH~T;-5x$X%040dba4IDZnloJu{(7U#hkH&8q)3h!0Ta~ ztV^D_8eBAV7v`Q8x{n+?{)?LBv^lTDAKNF$cLA~eL-7_}-5N+s!DdZPnBU4b$oXbeOn8}UMUtk#%2|&>spWJT5_F`;+_i4@POilw^g^4=TFdMY(J;6@M&7zzUY{_RIH`c$saNwe>UqEl!lo3DlYpyxebCwL~Ew?%HR3W?2%kq zJPoFWWgZA;Oz}@c@<@%D{W=m==KN|A;Y%`4pWV_0gV_up`KmRf@bH;MzO(Qy?qTeO=>1cS z_nY&tto)^bMP^Jgb2%C_MtB5nI6D$vOuYBTP1+DVnA=c2m|Rdx)yjfmb?WLNVq(pb zroaV;Ih{c$f0}MH!Ndz5OnPB{_&vO&ybamX$Ulq^*uk1u5IvGd>EQfkC+!_S|5Nj; zq3|UYHLj-B&=GxIUB!}??X;+S{&o@7mmJCO&au>o(-#zR5ryi^2?$6Vsk66rDrp`F zn($r8;F0^59m=1Q&m14zuXhbh3WisV5l&__4x^SF4R6X!v@NAuoGn;hcaS`xQ7+a! zAI4#2Y6J+ zdL!32#jbI+jLl>r%bER4TvMl{Y#sQ#?_J+pG_qfiG_sG(-Sgt>SUULk=KsZKx1$}z z1%>1s&!msA-h`{`y*(R}FOH81fN+$fghGtehU&Y>Mfg%kqK)C3qFkhF5lxtJ_y^3v>R ze(-QXC2Q2yIN1DQTmrWWWphZI)Pra;!wG6gvYvmCPq2^HLvLD53mwn!R%0ghL>tW6 zi2Q}zx0soXrV3ciU$UNeZD-Ht(ZlBx;7E|vN*R)l+ajqujGrXy@;DO{E$oQpQWtTA zI3jI5&*zue{c|Iz^^ImTa`eg^k>G%V+e$18zz-ZOqKf2LXIz;sX!?eKvt1g?rg_G?cb0oXUey#z&Y1ZDabnUY%w8>~eSuw4l_!83{9M z(RMoRWE*dbS|m}bH&ED_UO=hgo57aXG#(iw9@1o`mSv5v-`jt`zw6f3CeLjGPx%o2ehq7jjfR%+CmiZ>ffJ z2qB(yy)70LQc>m1HNBxQouT0!hELi8?>?1X1rHov1qGEtT1#oenoOi3(6xkC830k+ z%wqnMnV&t7Z=JGpmT<~eir12r%(=NwjG|yS6M|P%*b`(jaujeowy} z6PD>%(`(qz_ER&CJd%7%pOV*853TNOFY=L#)((!C5TE!9D+>685woMG14DKo<02#r z`jvbWKS+OK#vKIhlF%_Kf-pIBWU5~|Q~*7L(TTja_Cf4xu*!;r%i zOxj(|Ubt0@uZZo-Yt)@U&zTOz{&v21AE9zvXj%&&eLZ#^Tz*7Z)E-Q4ARxeQs$EJ$ z`J-8b`RW`Ey%JVX1z(d-7;VNA>G__CmAohp+J225&{hs7uj#jPXVIgQbMa{{ua~*B zoxXfS+yIWe#@97p)T9HnMWx|!n5^j2^XB_<+1i)m4uQX(w(l>q#>jiX>=ZCs`?9!b zBn$cCes%rH_U!)73gCD|HZe{2!utlWAXvDb!zbKWTC1rn)2KieBb zh`!-UvWR!&pYAY4yS~V zFXZu86M6iDjfxucJUCs|&dOqbe0HJOIxqwt^M>u@%v+`;upOy3A#SJUzrMqeTmDnkDNwWYMLOfg%_w& zlq=a?zOq;5clo0c9Wqx#R1sLabh}yEDD%1b?18Z+ylm4)z)%i@!K$u@+6XH4Y~A7%SAb08%X0i%pI)h?8@Z9Y+u57p>-Zb+uIZd`HhMYl8vuxQF509;%32 z!ZthN*AcDLdi2Hl5a@}yEqXLF2iN4%GwYjARr4^bGJA-5*%;i`;8nxe_}`)WwYcxw zOpv7vuXt4sb6wmrv#$z`7uYG8eovj$ls?tK=qGf#-H3-~-$|%;B-{9+ym`29b$P2k zK+g+36Rpdrk9#f~ujUvuA=;~j zS0U&Cp}G|V)~+nhHXM60?4L~F`iPgOJ!tp>e(LG&_=B@z|C)a?j`n?~h~nu_+V zJ8N~^`DKnJH;5N^$9;3{|pBQsfAO@15brpYnzqj~dsd9ArMFFwjFWPpJzkwu<0 zAlE>+#gY%W5?3stPLnFzzAInIHN(ZS4}}GvBy_TTFNuyz(GMtebAe6=j1^P(G=C^@ zHl8xCejm2lyZ-+y&cy%w@JxKg?nP#UxGnPses;g6j#F?j7LnZhW; zN1c;T#ShF&!wWlGRVhPBSl|ShEX8z83Msoz16ISLBw1q!W|Y=(_D#o@IlX6VO!*Hx zuy1EkC>4hg%KVL(Rz&me6Z)0vxr6J?T|3$ihH7dHekN zeEZyg@JtKh3#>oqGCUA3?>%kC$QYfrYc$5N2e2L;$FKd>^oHNPG+Ezd+gb?HG^Zz9 z#hD^u3Qq^(KzxfZyX1v5W0!bTIsZgntX{g+Fc03Z+)hu1hHLx62eid{3ga1)$;p^D zW6anF>o!@(G5G&{bUCGNCONneqfwiXlEV1-e%wE>B#~Y|I2ns}uf1f}OJgem94r<_(3-3zcE@T#6l3n@Wh1 zMkJH^ZKTB=3X{qK&3%5->!uy)7eA+=c-8;c>h#vu%HH+I|G@mfyXwcmfp^(ISlXH` zHJf0_y|a7TNx$QCJWtr}h;T!j!WM+jt!w^;8NoRh@eDz`-H*@;@{jrYOY}IAg;|q&xha4mJW05)e*A}b!={zic{#2svGAcj)Q$ae6uw?FP-A>Sp#RWw2 zNNu{s=xl)x{LRHWJ}a-suf$V{1Fz5%nL_?*-XC%Ry(5fSf*6sUsH%nlw2N^G?7yF< zaub^i`|qd3^Wb1k0Ye5Tsn{JY1Z>h}Lfu2lWRSI>FzmmV&&UeA-HWjbB32;>r++P- zHs1H3*OO$?_py+ThEp%EBix%2Vp}|n{nzqea>?fWL|k55VeVgIA?zE5y|)fChd-wd%c8y{Z|QnQQD=<4W^ z{8WxM&L0V9q1CF$r^_PKL>>i+(~>7Kpd_UoMbe+KrK*Ja9WCVJ^Fm?1QP(Ox{poiI zjJ_IBRlxI1#U-(a2MP5Q76)t3g+mALrvBpNy(WBFml zwI=*>P%niK1F7dD;Qf`nH;2mSmkpPKdOsHW^lL!|piTi~|@!cvNq zKhqW}bioP>rR}|U?#$dN2;FXX+fKXN?e4Zk5?t<|bGy?{cb56nutW($Vl){^)lC42 zhG%W-CYGrwjYu3E84tq$-nOk+PZ z^k}M)QdS!lI5{v7134{t2gVXAseGc?k1sz|*^gYiuSWCAD#=vS}~o3GauqQ|M%RhGPvn$;Gha-muTFQ|K{w3_SM6_m z3Kc@>TVD0v%Hm#ETHN&3GejiI_*Y>KV>t`d=lQuPZVQ^b4)U>E z8}kYR?$TlowJLj4)&e0w(-3P_mrC3e>oPvUYHDs#{TFPA>HvJQ@@jTzf$K|KGi7mJ zR0bsItgjCbO<=fpASJ@UTc7{}OQp0$pI@kKQCWP}&#gw1;qs4uph3+kv+j1;8^A&U ziiuD~hXs^FGGyOna!hbn*Jqdj7b2 znSY0D`=$D)06Zw|(Jt{RFK24zP>=VeCs4mFFBSov7%H-RstK(& z9@CWM-4>uK@>N-YrezN4B@K!PF@6avWCFO7gU_Uc#kwB=>7~uNp}LyN=1fbTM@|;R z0e&fhpSP%*N1CZ=)x&x(9}w>%Nh1oSnj#-7(|m$&JEs@rtq<1qNk5Ajxs2^nnz|;XY z$j9|kF>N6=yY$g@&q?%@B{>L+nbANSBbGSm@PxFNYr<4%Yf;GtD%SK*)sIyYovE2X zI&){GzFe#P=V;s=0^(=FsxkTkloa3;&7Z{~f@C$n0Lw%^2uy!0WvAMWZ6*X$I_Bpp zn-k@=cf8o*vIho_9a+1$zERttvP0eX|G2tLgK1x;LS;N{r!i%OUW$~gU#iY(QV`Y`o=9E>CK;=?_Rp|Pi{|zeP8l_d12qyt5=o> z0a0?{;;o~L{``Np`G?LO*t+`I@=A6>viIKek8TdmU6W6L?sZ!S z5gNO6140E95mM~%A>}{SrFIlrhCMQ~bc6ZMU3>f`|9S4Z-NW^lwUv>{pg7II_>~z) z*}VMvTvt$&t2eV0%Q5`I6_dk-T=j&syAX*XGJSf?z|ZO#(emp(d2Lz zOJ8HhKQR2CQN;bl zfo{Wi1fW%V`)6N0Ib5|^9}j~Z5u7VFf<^(vN!S?L)IOIghQB3${x3duMa}bD;4Q@G znS^>iTYan18;>VZd0?+Iiqh7Zr8npA|B>%bK3{nltd2#s9_+X9PO>rOHSiZAC71wW z_`&@7ANtTsYo0$JLZnI&0@X-z$MuP#%r(Jg*5Qcf+60I439k9~SJX_<7RPVtKEz_> zXtwqKiH54xRlYoG^+KD^EWI^ebi~9Iy;zD@@8BzGgGXLS-=LFE zaOf*9tJ%bXYVUGvI9DR~TbWNhe`0U%@kPsGC5tS8o`a)b@}}#dyIzLV*-5P;t@( z3;6`!d8%##rCtaa7X?dU&qL|5BN0gflY$2c5@5VSKEd(#|JLM5pwl6dvk*ZVGCSr4 z9pG*QL|2HqAWI*^d&M{S)gP((20>KT%7TwkI#e5jkiSfT@|ID@-UL+G%+l@o1Ydu1 z-3!PSK#7`-&U0M|WI!U-l=_WSD?zP~3ErJgaOjrW2|$WUz{;y}W>t!)ds0Q(iVPAk zpCHNHp5XY$Yj$AR4STTT477Obs$coYNHQQzC<(BcG5pT_`Om(jP8S7CvA~sb(H4;b zB?oIyWJGI8OI&}DzQB9)3GVof+AUO$9Dx;;$Suwu=EQKW76n`skHcPz6&x)l*sMDT z;#Ufpl~}P?Sd<)Ccy)R)FnJZFZCZdyk-{HI)fSG_myG zVuHhIg5zI!?W7Pnwh?|&k~!l3_=*HsdJ$-l<`OBr(hD4TtnNO*PB2z9s6u^!6iP%8 z3PDuVI7hsgAUfLKf#cV|aq{^Z;qAKci)uS4sv?})0$i&=(i2Z=48Jdb|E~>RUL(W~ z?HqPhjwN1G$4J~Q^%l^GKsLaoTh)~{%SOWZe=@m|1b7jpd09tEzGP$j2=F#0b z7>LCy<(O4H>ujej%=h5YqMgF~NcN)J?$d7mDi54ndiUK&J|0 zLy@Pp;X7qX6Q9aga7XP5<|u&i0AZxFAEkWr_;Hm5yHb!74x<)!W~rFqsczj0cpVCG zGA`i|pLX~gpfITN1x|B!&`m2iov+|vy)IrD*|2^@031fm&PwRzLF9>psy6r$XO_<7 z6MXG6byCPz59Zhi>Pi9P8IzTF9d{OxceD#3dMKaZGoP)Kf-c8;adAG-(`*72ihf8$ zPsJCja?np-p_t&{rMe0HYPO>>0YNE%IyQ1&)?zrsILTy8ctkTxkK`+O>MeB>FwMtp zslu6TB?~I9tauGRG##wo9K)F0Qqslm z&!7L#pQw9&Pn0#F7dw96ZO>}&V;?z1*w8Jhou2>GX@cWV|6P6#GR=;oEI3us+^k>v z;H5)lPsv(>$s8^y)Iy1oE(}-mG5aKQaF4|c6|TMz#XtV4$>BkVpom;Aj1GR@B~N3BhJ=lY z{{s?S>FM9BxfQv1^JH%UBJ(1$8K_7)OxWhALLJAbYT0@EKfZGE=@Ek#3eqd2F9-5! z-=zXbc+t)+E_$ER@cEkWKF11BsRrm>J+ao{p4t&@0vc}_Yz(B*@B=l&f#(aY4znOJ zV;TrYkEyo^iX+8UCs2C#&s{mWcsfsxd{)VOn`fyNz|oOYqY!-mLBJ{Qy!$;hPiK8P zh%9+@s$3jpwjS?taW7ISxzHBh{dW&cJ{{b_oScFb5w0D!8Dls)C3K;6J3eUn!(Dmx zom|jE{upxumo)bhr~HWNvs9NTIP}pE#CcuH8LN_TZ|tO1!V#6_KdolbqHBXOW@F+4LOD_q*x0b<}amEy+B9kfqSGl(N-j7XwIEv0Tv$+&X z%_)i)LE=UBt?|H-wUW|@dsR(XMNq3x!xAt-3}jJUSr-RF{VT#mgt3Zr`Kp zLIef@1S}%i_HgW#v(VJQZc4*HS2J7{a7Qf%V?{Vi$A~PD+;m?i7%72vl7`_lv*_0V z-7cVH6ywNCA?vs=qCQKGJBAm!?q^>=xp>hJ?L>mnA~1{&H%-@tD?X)>N7qvln*9=ZR06o18_9EV(@|gm%oZ5nmG6Fk&Eh2{m?``wT`BE$Uoec~ za61lIkn~^q#QlBEkqItX7Pirjou~^XC!84Of^vU^Z>6m}QFCA!HfV~tIPFMYOF9ap zfR11aci4ZC;oJFeAE{Y9=ZJ%$0$S-WytZ~vDG6h*#SNHThM+?1hD#V-LBn7^R#91&I=O|32snDtS!Z3~v}It&ZL3oUos-}bT8K{hmeE!{ zFeWj6H7Pj56EHC^c$+_VIrN1b6o$-ly5g=;sX{19 z?|$O?$)^w4kUVZY?(76jMye51X0~Wv5Fu1{Y51X<13PlC-3dXi7~NYvp3;zm{MnCBzWXeH3gV-nTcrQkV~%cD_}Nv(@eEAz9ijbxjm!slET4jM>G_kk zo**v((eLJE*UpuNcB^JMmvDhmGan&BmU$SpmVieNJPS81`8UPlf8|Y+i*IGRL<9-o zAETi2`Wa54$RU9Iix^mG@yH}hK|LgHG@kveyi4`;$Ch(*I#e)`91hl|w1fgS{J zq)EUTXrNI5&{Kd6KB@Hd57i8piZ+HJNnb3=@bzShd<&NqroT~?obBxVOwI84zW7xJ zpj&A8G4GsFv(U+A(SoszlXqQg-2*klp{2!jLQk^CkR*$>1uDdV$cT635uRfB@7LHi zHp|uI)UizTFv)UB_AJbS!eAocn=38;qctWm-#2bzX&1^B3cs>ih85Kr=-za0Y4{Io z7C%d5T620_R68hnkT?-(3=ALnCt>2UJ^x2FZlE*+tT-*O%zxGqPg?Ff;sb!LXri*N z`t9G?u?pwr~^?N8sj;36u$^=GWTb6T{B`QG{?Rqmyza8Dn*DihMW2b7u zkYv(X4v%{XE=T_{Cs5o$STo_aC-wAu`2@XxovWEZN>|GnTYy3n$ZSJtiUv@mY8?%r zc|s)BV_v)T%s1*{x$2`4g~|>(DRKdvR*ol#RH;5q9a%ak-+VN^!m}TKQ}MmCt(Su~ zif@HxSeJ4;0;w85T(C~56Y4bxD#Z;G_tMsZ!TRdv+AvGOnyt4@B>Hd5Gy1n3-q5H! z*xZ0%vbuh0>+O5~@aU6pCC@AmAIF3qzx;_kf4NW`L}&m>m9nEhS*8S@an)%@ia_w)1R;pvUF&Edx8T6Q=nB#YO5XVcg_ zA3oV%>pzj+rMu4`XJ%i2?sZzt!%iRRuM9U=66Ia&K=YIO>)D;F=SO!ZjId}9k72hp zKFP4Vvcbjd^k)7_jh`nT@| z=o1$YV)gXJC!WYQ*6Mfh-A&`S>)2|a-gs&sphH*s7k0_ZtxXRlVwD|g zR49`&xu0hoJ^y63f*v(}xtsT~sOMC0^YjlM4cfgO1=l>Fzr1#2)n!sB>PFRe(trmy z`#DH*$WiknsHd{BXcX3+gK%8k+Dh7{Mu9n*tu3#P_PBkTpSY^Hd;Q+?r!SntB$l2y z-}GS~Je94luAn1dJ^#YK=&9KP6=z?&cly|wqbqPp*5D_{fJWgj4LGs+#Gv@Dw=}(u zVJEW9oyRn)AHRS9TK{5NQ{zE7J?yV!m0fC7!G>%Q%H{Kq9%Hwyu040pH!8056_-8o z^tt|p>SG(#_vwx7!ja95%35a|_pZ+5X%wIx-FDXwfnD0o#v}NFY;Coos2Xp=^_|TW zQ?Z$&{zgBZl`(XS#AIY8w{F_=O}DMma~CdL+WN^of2OV?OuwA^?s?4NlHHtU+}m(2 z{c<^;eQ3{Tmrd+6>E1GbPk;T0cKh{H*`sKF*9x@v7x4SEl)c@8z|Nx?7|v-S`}twR z%a!f@H0SdiP^YH9vi#BKpw74U`{T5Ty}yO4Wtr%#t`+aqc$#)O9SvWl^+vU~J+9<_qz5*B=RN$7G`P_|NL>*%4oDi^xaJxUBn@o5#cExU zG_>(j+3iTAQH`H+mnV@1H6DU(S7LvU*yT&4L5&}84`(6`e(#a+3@_f~@QYtT4rF2(FLpO9 zBcCH*OO_|~wyr$C94(#YO5#OD2YpZ(-~ts~$=hpBk$LY%pE@v)ls^CUyteu2w;$fl N&7~Zne%8%f{} Date: Tue, 20 Oct 2026 14:57:38 -0400 Subject: [PATCH 22/56] ci: update CI to release to Github on tag without a wheel being built --- .../workflows/build-wheel-release-upload.yml | 76 ------------------- .github/workflows/release-github-on-tag.yml | 26 +++++++ 2 files changed, 26 insertions(+), 76 deletions(-) delete mode 100644 .github/workflows/build-wheel-release-upload.yml create mode 100644 .github/workflows/release-github-on-tag.yml diff --git a/.github/workflows/build-wheel-release-upload.yml b/.github/workflows/build-wheel-release-upload.yml deleted file mode 100644 index 9109858f..00000000 --- a/.github/workflows/build-wheel-release-upload.yml +++ /dev/null @@ -1,76 +0,0 @@ -name: Build Wheel and Release - -# Trigger on tag push or manual dispatch. -# Tag and release privilege are verified inside the reusable workflow. -on: - workflow_dispatch: - push: - tags: - - "*" - -# ── Release modality ────────────────────────────────────────────────────────── -# Three options are provided below. Only ONE job should be active at a time. -# To switch: comment out the active job and uncomment your preferred option, -# then commit the change to main before tagging a release. -# ───────────────────────────────────────────────────────────────────────────── - -jobs: - # Option 1 (default): Release to GitHub, publish to PyPI, and deploy docs. - # - # The wheel is uploaded to PyPI so users can install with `pip install`. - # A GitHub release is created with the changelog as the release body, and - # the Sphinx documentation is rebuilt and deployed to GitHub Pages. - # - # Choose this for open-source packages distributed via PyPI and/or - # conda-forge where broad public availability is the goal. - build-release: - uses: scikit-package/release-scripts/.github/workflows/_build-wheel-release-upload.yml@v0 - with: - project: libdiffpy - c_extension: true - maintainer_github_username: sbillinge - secrets: - PYPI_TOKEN: ${{ secrets.PYPI_TOKEN }} - PAT_TOKEN: ${{ secrets.PAT_TOKEN }} - - # Option 2: Release to GitHub and deploy docs, without publishing to PyPI. - # - # A GitHub release is created and the Sphinx docs are deployed, but the - # wheel is not uploaded to PyPI. The source code remains publicly visible - # on GitHub and can be installed directly from there. - # - # Choose this when the package is public but you prefer to keep it off the - # default pip index — for example, if you distribute via conda-forge only, - # or if the package is not yet ready for a permanent PyPI presence. - # - # To use: comment out Option 1 above and uncomment the lines below. - # build-release-no-pypi: - # uses: scikit-package/release-scripts/.github/workflows/_build-release-github-no-pypi.yml@v0 - # with: - # project: libdiffpy - # c_extension: true - # maintainer_github_username: sbillinge - # secrets: - # PAT_TOKEN: ${{ secrets.PAT_TOKEN }} - - # Option 3: Release to GitHub with wheel, license, and instructions bundled - # as a downloadable zip attached to the GitHub release asset. - # - # The wheel is built and packaged together with INSTRUCTIONS.txt and the - # LICENSE file into a zip that is attached directly to the GitHub release. - # Users with access to the (private) repo download the zip, follow the - # instructions inside, and install locally with pip. No PyPI or conda-forge - # upload occurs, and no docs are deployed. - # - # Choose this for private or restricted packages where distribution must be - # controlled: only users with repo access can download the release asset, - # making the GitHub release itself the distribution channel. - # - # To use: comment out Option 1 above and uncomment the lines below. - # build-release-private: - # uses: scikit-package/release-scripts/.github/workflows/_build-release-github-private-pure.yml@v0 - # with: - # project: libdiffpy - # maintainer_github_username: sbillinge - # secrets: - # PAT_TOKEN: ${{ secrets.PAT_TOKEN }} diff --git a/.github/workflows/release-github-on-tag.yml b/.github/workflows/release-github-on-tag.yml new file mode 100644 index 00000000..a5511623 --- /dev/null +++ b/.github/workflows/release-github-on-tag.yml @@ -0,0 +1,26 @@ +name: Build Wheel and Release + +# Trigger on tag push or manual dispatch. +# Tag and release privilege are verified inside the reusable workflow. +on: + workflow_dispatch: + push: + tags: + - "*" + +jobs: + check-tag-privilege: + uses: scikit-package/release-scripts/.github/workflows/_check-tag-privilege.yml@v0 + with: + maintainer_github_username: sbillinge + + check-pat-token-validity: + uses: scikit-package/release-scripts/.github/workflows/_check-pat-token-validity.yml@v0 + secrets: + PAT_TOKEN: ${{ secrets.PAT_TOKEN }} + + release-github: + needs: [check-tag-privilege, check-pat-token-validity] + uses: scikit-package/release-scripts/.github/workflows/_release-github.yml@v0 + secrets: + PAT_TOKEN: ${{ secrets.PAT_TOKEN }} From 4b5e22858846df8a7c576a90aceec226b88ce2eb Mon Sep 17 00:00:00 2001 From: Caden Myers Date: Tue, 20 Oct 2026 15:12:27 -0400 Subject: [PATCH 23/56] ci: create custom CI workflow for testing on PR --- .github/workflows/tests-on-pr.yml | 67 +++++++++++++++++++++++++++---- 1 file changed, 60 insertions(+), 7 deletions(-) diff --git a/.github/workflows/tests-on-pr.yml b/.github/workflows/tests-on-pr.yml index a9d864b5..e8bcb746 100644 --- a/.github/workflows/tests-on-pr.yml +++ b/.github/workflows/tests-on-pr.yml @@ -6,10 +6,63 @@ on: jobs: tests-on-pr: - uses: scikit-package/release-scripts/.github/workflows/_tests-on-pr.yml@v0 - with: - project: libdiffpy - c_extension: true - headless: false - secrets: - CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }} + runs-on: ubuntu-latest + defaults: + run: + shell: bash -l {0} + + steps: + - name: Check out libdiffpy + uses: actions/checkout@v6 + with: + fetch-depth: 0 + + - name: Initialize miniconda + uses: conda-incubator/setup-miniconda@v3 + with: + activate-environment: test + channels: conda-forge + auto-update-conda: true + auto-activate-base: false + python-version: "3.14" # cxxtest has no Python 3.15 build yet + + - name: Conda config + run: >- + conda config --set always_yes yes + --set changeps1 no + + - name: Install requirements + run: >- + conda install + --file requirements/build.txt + --file requirements/conda.txt + --file requirements/tests.txt + + - name: Configure SCons for the conda environment + run: | + cat > sconscript.local <<'EOF' + Import('env') + import os + prefix = os.environ['CONDA_PREFIX'] + env.Replace(CXX=os.environ['CXX']) + 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')) + EOF + + - name: Build and run C++ tests with coverage + run: scons build=coverage test + + - name: Generate coverage report + run: >- + gcovr --gcov-executable "${CXX%c++}gcov" + --root . --filter 'build/coverage-.*/diffpy/' + --xml coverage.xml + + - name: Upload coverage to Codecov + uses: codecov/codecov-action@v5 + with: + files: coverage.xml + verbose: true + fail_ci_if_error: true + token: ${{ secrets.CODECOV_TOKEN }} From 1ce68b3a2f555bbd9de245eee2c923b91004d8ca Mon Sep 17 00:00:00 2001 From: Caden Myers Date: Tue, 20 Oct 2026 15:15:11 -0400 Subject: [PATCH 24/56] remove default channel to get rid of warning --- .github/workflows/tests-on-pr.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/tests-on-pr.yml b/.github/workflows/tests-on-pr.yml index e8bcb746..72e6224b 100644 --- a/.github/workflows/tests-on-pr.yml +++ b/.github/workflows/tests-on-pr.yml @@ -22,9 +22,9 @@ jobs: with: activate-environment: test channels: conda-forge + conda-remove-defaults: "true" auto-update-conda: true auto-activate-base: false - python-version: "3.14" # cxxtest has no Python 3.15 build yet - name: Conda config run: >- From 89d34c73528e94825b7f052d4df6ceabf8817e02 Mon Sep 17 00:00:00 2001 From: Caden Myers Date: Tue, 20 Oct 2026 15:34:39 -0400 Subject: [PATCH 25/56] fix CXX keyerror and codecov --- .codecov.yml | 4 ++++ .github/workflows/tests-on-pr.yml | 8 +++++--- requirements/tests.txt | 1 + 3 files changed, 10 insertions(+), 3 deletions(-) diff --git a/.codecov.yml b/.codecov.yml index 4af5eb24..8803dce5 100644 --- a/.codecov.yml +++ b/.codecov.yml @@ -12,3 +12,7 @@ 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/workflows/tests-on-pr.yml b/.github/workflows/tests-on-pr.yml index 72e6224b..00e87a43 100644 --- a/.github/workflows/tests-on-pr.yml +++ b/.github/workflows/tests-on-pr.yml @@ -44,18 +44,20 @@ jobs: Import('env') import os prefix = os.environ['CONDA_PREFIX'] - env.Replace(CXX=os.environ['CXX']) + 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')) EOF - name: Build and run C++ tests with coverage - run: scons build=coverage test + run: | + "$CONDA_PREFIX/bin/c++" --version + scons build=coverage test - name: Generate coverage report run: >- - gcovr --gcov-executable "${CXX%c++}gcov" + gcovr --gcov-executable "$CONDA_PREFIX/bin/gcov" --root . --filter 'build/coverage-.*/diffpy/' --xml coverage.xml diff --git a/requirements/tests.txt b/requirements/tests.txt index 6a9dfd04..a5570111 100644 --- a/requirements/tests.txt +++ b/requirements/tests.txt @@ -1 +1,2 @@ cxxtest +gcovr From 652f0c12771d47a01f59813685de19f0d4b7f1b7 Mon Sep 17 00:00:00 2001 From: Caden Myers Date: Tue, 20 Oct 2026 17:41:48 -0400 Subject: [PATCH 26/56] rm python-specific skpkg files and travis file --- .travis-sconscript.local | 34 --------- .travis.yml | 153 -------------------------------------- MANIFEST.in | 12 --- requirements/pip.txt | 1 - src/libdiffpy/__init__.py | 23 ------ src/libdiffpy/version.py | 26 ------- tests/conftest.py | 19 ----- tests/test_version.py | 10 --- 8 files changed, 278 deletions(-) delete mode 100644 .travis-sconscript.local delete mode 100644 .travis.yml delete mode 100644 MANIFEST.in delete mode 100644 requirements/pip.txt delete mode 100644 src/libdiffpy/__init__.py delete mode 100644 src/libdiffpy/version.py delete mode 100644 tests/conftest.py delete mode 100644 tests/test_version.py diff --git a/.travis-sconscript.local b/.travis-sconscript.local deleted file mode 100644 index c7fb22be..00000000 --- a/.travis-sconscript.local +++ /dev/null @@ -1,34 +0,0 @@ -# Customize scons build environment. - -Import('env') - -import os - -# Workaround: DEBUG_CPPFLAGS is defined on Linux, but not on Mac OS X. -# Make sure it is always sensibly defined. -os.environ.setdefault('DEBUG_CPPFLAGS', os.environ['CPPFLAGS']) - -# Apply environment settings for Anaconda compilers -env.Replace(CXX=os.environ['CXX']) -if env['build'] == 'fast': - env.MergeFlags(os.environ['CFLAGS']) - env.MergeFlags(os.environ['CPPFLAGS']) - env.MergeFlags(os.environ['CXXFLAGS']) -else: - env.MergeFlags(os.environ['DEBUG_CFLAGS']) - env.MergeFlags(os.environ['DEBUG_CPPFLAGS']) - env.MergeFlags(os.environ['DEBUG_CXXFLAGS']) -env.MergeFlags(os.environ['LDFLAGS']) - -# Silence copious warnings from the boost headers. -P = os.environ['MYPREFIX'] -env.Prepend(CCFLAGS=['-isystem{}/include'.format(P)]) - -# Define path to the shared libraries from Anaconda environment. -L = P + '/lib' -env.Append(LIBPATH=L) -env.Append(LINKFLAGS='-Wl,-rpath,{!r}'.format(L)) -if env['PLATFORM'] == 'posix': - env.Append(LINKFLAGS='-Wl,-rpath-link,{!r}'.format(L)) - -# vim: ft=python diff --git a/.travis.yml b/.travis.yml deleted file mode 100644 index c7af470c..00000000 --- a/.travis.yml +++ /dev/null @@ -1,153 +0,0 @@ -dist: xenial -language: cpp - -os: - - linux - - osx - -env: - - MYUSEMC=true MYPYTHON_VERSION=2.7 - - MYUSEMC=true MYPYTHON_VERSION=3.7 - - MYUSEMC=false - -matrix: - exclude: - - os: osx - env: MYUSEMC=true MYPYTHON_VERSION=2.7 - -git: - depth: 999999 - -branches: - except: - - /^v[0-9]/ - - -before_install: - - MYNAME=libdiffpy - - MYCOMMIT="$(git rev-parse HEAD)" - # travis virtual hosts have 2 cores as per https://goo.gl/DQy4oW - - MYNCPU=2 - - umask 022 - - git fetch origin --tags - - NOSYS=true; NOAPT=true; NOBREW=true; NOMC=true - - if ${MYUSEMC}; then - NOMC=false; - elif [[ ${TRAVIS_OS_NAME} == linux ]]; then - NOAPT=false; NOSYS=false; - MYPIPFLAGS="--user"; - elif [[ ${TRAVIS_OS_NAME} == osx ]]; then - NOBREW=false; NOSYS=false; - MYPIPFLAGS="--user"; - fi - - MYMCREPO=https://repo.anaconda.com/miniconda - - case ${TRAVIS_OS_NAME} in - linux) - MYMCBUNDLE=Miniconda3-latest-Linux-x86_64.sh; - MYCXXCOMPILER=gxx_linux-64 - ;; - osx) - MYMCBUNDLE=Miniconda3-latest-MacOSX-x86_64.sh; - MYCXXCOMPILER=clangxx_osx-64 - ;; - *) - echo "Unsupported operating system." >&2; - exit 2 ;; - esac - - - mkdir -p ~/pkgs - - - $NOMC || pushd ~/pkgs - - $NOMC || wget --timestamping ${MYMCREPO}/${MYMCBUNDLE} - - $NOMC || test -x ~/mc/bin/conda || bash ${MYMCBUNDLE} -b -f -p ~/mc - - $NOMC || popd - - $NOMC || source ~/mc/bin/activate base - - $NOMC || conda update --yes conda - - $NOMC || conda install --yes conda-build conda-verify jinja2 - - $NOMC || conda create --name=testenv --yes $MYCXXCOMPILER - - $NOMC || conda config --add channels diffpy - - - $NOAPT || test "${TRAVIS_OS_NAME}" = "linux" || exit $? - - $NOAPT || sudo apt-get update -qq - - $NOAPT || sudo apt-get install -y - libgsl0-dev libboost-all-dev python-pip - build-essential scons cxxtest - - - $NOBREW || test "${TRAVIS_OS_NAME}" = "osx" || exit $? - - - $NOBREW || brew update - - $NOBREW || brew install gsl - - $NOBREW || brew install scons - - $NOBREW || brew install cxxtest - - $NOBREW || PATH="$(brew --prefix python)/libexec/bin:${PATH}" - - $NOBREW || export CXXTEST="$(brew --prefix cxxtest)"/libexec/bin/cxxtestgen - - - $NOSYS || scons sdist - - $NOSYS || MYTARBUNDLE="$(ls -t "${PWD}"/*.tar.gz | head -1)" - - $NOSYS || pushd ~/pkgs - - $NOSYS || tar xzf "${MYTARBUNDLE}" - - $NOSYS || git clone https://github.com/diffpy/libobjcryst.git - - $NOSYS || popd - - -install: - - $NOMC || conda build --python=${MYPYTHON_VERSION} conda-recipe - - $NOMC || conda render --python=${MYPYTHON_VERSION} --output conda-recipe | - sed 's,.*/,,; s/[.]tar[.]bz2$//; s/-/=/g' > /tmp/mypackage.txt - - $NOMC || source activate base - - $NOMC || conda build purge - - $NOMC || source activate testenv - - $NOMC || conda install --yes --use-local --file=/tmp/mypackage.txt - - $NOMC || conda install --yes cxxtest scons - - $NOMC || MYPREFIX="${HOME}/mc/envs/testenv" - - - MYSUDO= - - $NOAPT || MYSUDO=sudo - - $NOSYS || $MYSUDO scons -j $MYNCPU -C ~/pkgs/libobjcryst install - - $NOSYS || cd ~/pkgs/"$(basename "${MYTARBUNDLE}" .tar.gz)" - - $NOSYS || scons -j $MYNCPU build=fast lib - - $NOSYS || $MYSUDO scons build=fast install - - $NOSYS || scons -Q build=fast --clean lib - - $NOSYS || MYPREFIX=/usr/local - - # Verify version of the installed library. - - MYINCLUDE="${MYPREFIX}/include" - - MYVERSION="${MYINCLUDE}/diffpy/version.hpp" - - MYGIT_REV=$(grep '^#define DIFFPY_GIT_SHA' "$MYVERSION" | cut -d '"' -f 2) - - if [[ "${MYCOMMIT}" != "${MYGIT_REV}" ]]; then - echo "Version mismatch ${MYCOMMIT} vs ${MYGIT_REV}."; - exit 1; - fi - - - $NOMC || export MYPREFIX - # drop linker flags that spuriously remove linkage with libgslcblas - - $NOMC || LDFLAGS="${LDFLAGS/-Wl,-dead_strip_dylibs/}" - - $NOMC || cp .travis-sconscript.local sconscript.local - - scons -j $MYNCPU build=fast prefix="${MYPREFIX}" test_installed=true alltests - - MYALLTESTSFAST=$(ls -t ${PWD}/build/fast*/tests/alltests | head -1) - - scons -j $MYNCPU build=debug lib alltests - # APT and BREW jobs test builds from expanded source archive, but codecov - # requires compilation from a git repository. Tell scons to do so. - - scons -j $MYNCPU -C $TRAVIS_BUILD_DIR build=coverage lib alltests - - -before_script: - - $NOBREW || USER_BASE="$(python -c 'import site; print(site.USER_BASE)')" - - $NOBREW || PATH="${USER_BASE}/bin:${PATH}" - - -script: - - scons -Q build=debug test - - scons -Q build=debug enable_objcryst=false test - - ${MYALLTESTSFAST} - - scons -j $MYNCPU -C $TRAVIS_BUILD_DIR build=coverage test - - -after_success: - - cd $TRAVIS_BUILD_DIR - - pip install $MYPIPFLAGS codecov - - codecov - - -deploy: - - provider: releases - - draft: true diff --git a/MANIFEST.in b/MANIFEST.in deleted file mode 100644 index f1a78eec..00000000 --- a/MANIFEST.in +++ /dev/null @@ -1,12 +0,0 @@ -graft src -graft tests -graft requirements - -include AUTHORS.rst LICENSE*.rst README.rst - -# Exclude all bytecode files and __pycache__ directories -global-exclude *.py[cod] # Exclude all .pyc, .pyo, and .pyd files. -global-exclude .DS_Store # Exclude Mac filesystem artifacts. -global-exclude __pycache__ # Exclude Python cache directories. -global-exclude .git* # Exclude git files and directories. -global-exclude .idea # Exclude PyCharm project settings. diff --git a/requirements/pip.txt b/requirements/pip.txt deleted file mode 100644 index 24ce15ab..00000000 --- a/requirements/pip.txt +++ /dev/null @@ -1 +0,0 @@ -numpy diff --git a/src/libdiffpy/__init__.py b/src/libdiffpy/__init__.py deleted file mode 100644 index d78bd746..00000000 --- a/src/libdiffpy/__init__.py +++ /dev/null @@ -1,23 +0,0 @@ -#!/usr/bin/env python -############################################################################## -# -# (c) 2026 libdiffpy contributors. -# All rights reserved. -# -# File coded by: Pavol Juhas, Chris Farrow, Simon Billinge, Billinge Group members. -# -# See GitHub contributions for a more detailed list of contributors. -# https://github.com/diffpy/libdiffpy/graphs/contributors -# -# See LICENSE.rst for license information. -# -############################################################################## -"""DiffPy C++ library for calculation of PDF and other real-space quantities""" - -# package version -from libdiffpy.version import __version__ # noqa - -# silence the pyflakes syntax checker -assert __version__ or True - -# End of file diff --git a/src/libdiffpy/version.py b/src/libdiffpy/version.py deleted file mode 100644 index 6eddb6e1..00000000 --- a/src/libdiffpy/version.py +++ /dev/null @@ -1,26 +0,0 @@ -#!/usr/bin/env python -############################################################################## -# -# (c) 2026 libdiffpy contributors. -# All rights reserved. -# -# File coded by: Pavol Juhas, Chris Farrow, Simon Billinge, Billinge Group members. -# -# See GitHub contributions for a more detailed list of contributors. -# https://github.com/diffpy/libdiffpy/graphs/contributors # noqa: E501 -# -# See LICENSE.rst for license information. -# -############################################################################## -"""Definition of __version__.""" - -# We do not use the other three variables, but can be added back if needed. -# __all__ = ["__date__", "__git_commit__", "__timestamp__", "__version__"] - -# obtain version information -from importlib.metadata import PackageNotFoundError, version - -try: - __version__ = version("libdiffpy") -except PackageNotFoundError: - __version__ = "unknown" diff --git a/tests/conftest.py b/tests/conftest.py deleted file mode 100644 index e3b63139..00000000 --- a/tests/conftest.py +++ /dev/null @@ -1,19 +0,0 @@ -import json -from pathlib import Path - -import pytest - - -@pytest.fixture -def user_filesystem(tmp_path): - base_dir = Path(tmp_path) - home_dir = base_dir / "home_dir" - home_dir.mkdir(parents=True, exist_ok=True) - cwd_dir = base_dir / "cwd_dir" - cwd_dir.mkdir(parents=True, exist_ok=True) - - home_config_data = {"username": "home_username", "email": "home@email.com"} - with open(home_dir / "diffpyconfig.json", "w") as f: - json.dump(home_config_data, f) - - yield tmp_path diff --git a/tests/test_version.py b/tests/test_version.py deleted file mode 100644 index 1591df44..00000000 --- a/tests/test_version.py +++ /dev/null @@ -1,10 +0,0 @@ -"""Unit tests for __version__.py.""" - -import libdiffpy # noqa - - -def test_package_version(): - """Ensure the package version is defined and not set to the initial - placeholder.""" - assert hasattr(libdiffpy, "__version__") - assert libdiffpy.__version__ != "0.0.0" From efeae878cec2f71ff022841ce2b4a27073f49e3a Mon Sep 17 00:00:00 2001 From: Caden Myers Date: Tue, 20 Oct 2026 17:48:36 -0400 Subject: [PATCH 27/56] remove unnecessary pypi specific information --- .gitattributes | 1 - .pre-commit-config.yaml | 4 ---- pyproject.toml | 53 ----------------------------------------- 3 files changed, 58 deletions(-) diff --git a/.gitattributes b/.gitattributes index 0628b707..04b6c92b 100644 --- a/.gitattributes +++ b/.gitattributes @@ -1,5 +1,4 @@ /.gitattributes export-ignore /.gitignore export-ignore -/.travis* export-ignore /doc export-ignore gitarchive.cfg export-subst diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index a9fa4c7a..74f98b2c 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -33,10 +33,6 @@ repos: hooks: - id: isort args: ["--profile", "black"] - - repo: https://github.com/kynan/nbstripout - rev: 0.9.1 - hooks: - - id: nbstripout - repo: https://github.com/pre-commit/pre-commit-hooks rev: v6.0.0 hooks: diff --git a/pyproject.toml b/pyproject.toml index c6b58d89..8cfa9203 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,56 +1,3 @@ -[build-system] -requires = ["setuptools>=62.0", "setuptools-git-versioning>=2.0"] -build-backend = "setuptools.build_meta" - -[project] -name = "libdiffpy" -dynamic=['version', 'dependencies'] -authors = [ - {name='Simon Billinge', email='sbillinge@ucsb.edu'}, -] -maintainers = [ - {name='Simon Billinge', email='sbillinge@ucsb.edu'}, -] -description = "DiffPy C++ library for calculation of PDF and other real-space quantities" -keywords = ['diffraction', 'PDF', 'X-ray', 'neutron'] -readme = "README.rst" -requires-python = ">=3.12, <3.15" -classifiers = [ - 'Development Status :: 5 - Production/Stable', - 'Environment :: Console', - 'Intended Audience :: Developers', - 'Intended Audience :: Science/Research', - 'License :: OSI Approved :: BSD License', - 'Operating System :: MacOS :: MacOS X', - 'Operating System :: Microsoft :: Windows', - 'Operating System :: POSIX', - 'Operating System :: Unix', - 'Programming Language :: Python :: 3.12', - 'Programming Language :: Python :: 3.13', - 'Programming Language :: Python :: 3.14', - 'Topic :: Scientific/Engineering :: Physics', - 'Topic :: Scientific/Engineering :: Chemistry', -] - -[project.urls] -Homepage = "https://github.com/diffpy/libdiffpy/" -Issues = "https://github.com/diffpy/libdiffpy/issues/" - -[tool.setuptools-git-versioning] -enabled = true -template = "{tag}" -dev_template = "{tag}" -dirty_template = "{tag}" - -[tool.setuptools.packages.find] -where = ["src"] # list of folders that contain the packages (["."] by default) -include = ["*"] # package names should match these glob patterns (["*"] by default) -exclude = [] # exclude packages matching these glob patterns (empty by default) -namespaces = false # to disable scanning PEP 420 namespaces (true by default) - -[tool.setuptools.dynamic] -dependencies = {file = ["requirements/pip.txt"]} - [tool.codespell] exclude-file = ".codespell/ignore_lines.txt" ignore-words = ".codespell/ignore_words.txt" From 5e913d4cab6f18d5640f1f8d0525961ab292df1a Mon Sep 17 00:00:00 2001 From: Caden Myers Date: Mon, 26 Oct 2026 10:07:24 -0400 Subject: [PATCH 28/56] doc: move doc/manual to docs/ and reference the real license --- doc/manual/Makefile | 203 ------------------------ doc/manual/source/conf.py | 287 ---------------------------------- doc/manual/source/index.rst | 68 -------- doc/manual/source/license.rst | 140 ----------------- doc/manual/source/release.rst | 3 - docs/source/api/libdiffpy.rst | 11 -- 6 files changed, 712 deletions(-) delete mode 100644 doc/manual/Makefile delete mode 100644 doc/manual/source/conf.py delete mode 100644 doc/manual/source/index.rst delete mode 100644 doc/manual/source/license.rst delete mode 100644 doc/manual/source/release.rst delete mode 100644 docs/source/api/libdiffpy.rst diff --git a/doc/manual/Makefile b/doc/manual/Makefile deleted file mode 100644 index 64f3fb54..00000000 --- a/doc/manual/Makefile +++ /dev/null @@ -1,203 +0,0 @@ -# Makefile for Sphinx documentation -# - -# You can set these variables from the command line. -SPHINXOPTS = -SPHINXBUILD = sphinx-build -PAPER = -BUILDDIR = build - -# User-friendly check for sphinx-build -ifeq ($(shell which $(SPHINXBUILD) >/dev/null 2>&1; echo $$?), 1) -$(error The '$(SPHINXBUILD)' command was not found. Make sure you have Sphinx installed, then set the SPHINXBUILD environment variable to point to the full path of the '$(SPHINXBUILD)' executable. Alternatively you can add the directory with the executable to your PATH. If you don't have Sphinx installed, grab it from http://sphinx-doc.org/) -endif - -# Internal variables. -PAPEROPT_a4 = -D latex_paper_size=a4 -PAPEROPT_letter = -D latex_paper_size=letter -ALLSPHINXOPTS = -d $(BUILDDIR)/doctrees $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) source -# the i18n builder cannot share the environment and doctrees with the others -I18NSPHINXOPTS = $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) source - -.PHONY: help clean html dirhtml singlehtml pickle json htmlhelp qthelp devhelp epub latex latexpdf text man changes linkcheck doctest gettext publish - -help: - @echo "Please use \`make ' where is one of" - @echo " html to make standalone HTML files" - @echo " dirhtml to make HTML files named index.html in directories" - @echo " singlehtml to make a single large HTML file" - @echo " pickle to make pickle files" - @echo " json to make JSON files" - @echo " htmlhelp to make HTML files and a HTML help project" - @echo " qthelp to make HTML files and a qthelp project" - @echo " devhelp to make HTML files and a Devhelp project" - @echo " epub to make an epub" - @echo " latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter" - @echo " latexpdf to make LaTeX files and run them through pdflatex" - @echo " latexpdfja to make LaTeX files and run them through platex/dvipdfmx" - @echo " text to make text files" - @echo " man to make manual pages" - @echo " texinfo to make Texinfo files" - @echo " info to make Texinfo files and run them through makeinfo" - @echo " gettext to make PO message catalogs" - @echo " changes to make an overview of all changed/added/deprecated items" - @echo " xml to make Docutils-native XML files" - @echo " pseudoxml to make pseudoxml-XML files for display purposes" - @echo " linkcheck to check all external links for integrity" - @echo " doctest to run all doctests embedded in the documentation (if enabled)" - -clean: - -rm -rf $(BUILDDIR)/* - -html: - $(SPHINXBUILD) -b html $(ALLSPHINXOPTS) $(BUILDDIR)/html - @echo - @echo "Build finished. The HTML pages are in $(BUILDDIR)/html." - -dirhtml: - $(SPHINXBUILD) -b dirhtml $(ALLSPHINXOPTS) $(BUILDDIR)/dirhtml - @echo - @echo "Build finished. The HTML pages are in $(BUILDDIR)/dirhtml." - -singlehtml: - $(SPHINXBUILD) -b singlehtml $(ALLSPHINXOPTS) $(BUILDDIR)/singlehtml - @echo - @echo "Build finished. The HTML page is in $(BUILDDIR)/singlehtml." - -pickle: - $(SPHINXBUILD) -b pickle $(ALLSPHINXOPTS) $(BUILDDIR)/pickle - @echo - @echo "Build finished; now you can process the pickle files." - -json: - $(SPHINXBUILD) -b json $(ALLSPHINXOPTS) $(BUILDDIR)/json - @echo - @echo "Build finished; now you can process the JSON files." - -htmlhelp: - $(SPHINXBUILD) -b htmlhelp $(ALLSPHINXOPTS) $(BUILDDIR)/htmlhelp - @echo - @echo "Build finished; now you can run HTML Help Workshop with the" \ - ".hhp project file in $(BUILDDIR)/htmlhelp." - -qthelp: - $(SPHINXBUILD) -b qthelp $(ALLSPHINXOPTS) $(BUILDDIR)/qthelp - @echo - @echo "Build finished; now you can run "qcollectiongenerator" with the" \ - ".qhcp project file in $(BUILDDIR)/qthelp, like this:" - @echo "# qcollectiongenerator $(BUILDDIR)/qthelp/libdiffpy.qhcp" - @echo "To view the help file:" - @echo "# assistant -collectionFile $(BUILDDIR)/qthelp/libdiffpy.qhc" - -devhelp: - $(SPHINXBUILD) -b devhelp $(ALLSPHINXOPTS) $(BUILDDIR)/devhelp - @echo - @echo "Build finished." - @echo "To view the help file:" - @echo "# mkdir -p $$HOME/.local/share/devhelp/libdiffpy" - @echo "# ln -s $(BUILDDIR)/devhelp $$HOME/.local/share/devhelp/libdiffpy" - @echo "# devhelp" - -epub: - $(SPHINXBUILD) -b epub $(ALLSPHINXOPTS) $(BUILDDIR)/epub - @echo - @echo "Build finished. The epub file is in $(BUILDDIR)/epub." - -latex: - $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex - @echo - @echo "Build finished; the LaTeX files are in $(BUILDDIR)/latex." - @echo "Run \`make' in that directory to run these through (pdf)latex" \ - "(use \`make latexpdf' here to do that automatically)." - -latexpdf: - $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex - @echo "Running LaTeX files through pdflatex..." - $(MAKE) -C $(BUILDDIR)/latex all-pdf - @echo "pdflatex finished; the PDF files are in $(BUILDDIR)/latex." - -latexpdfja: - $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex - @echo "Running LaTeX files through platex and dvipdfmx..." - $(MAKE) -C $(BUILDDIR)/latex all-pdf-ja - @echo "pdflatex finished; the PDF files are in $(BUILDDIR)/latex." - -text: - $(SPHINXBUILD) -b text $(ALLSPHINXOPTS) $(BUILDDIR)/text - @echo - @echo "Build finished. The text files are in $(BUILDDIR)/text." - -man: - $(SPHINXBUILD) -b man $(ALLSPHINXOPTS) $(BUILDDIR)/man - @echo - @echo "Build finished. The manual pages are in $(BUILDDIR)/man." - -texinfo: - $(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo - @echo - @echo "Build finished. The Texinfo files are in $(BUILDDIR)/texinfo." - @echo "Run \`make' in that directory to run these through makeinfo" \ - "(use \`make info' here to do that automatically)." - -info: - $(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo - @echo "Running Texinfo files through makeinfo..." - make -C $(BUILDDIR)/texinfo info - @echo "makeinfo finished; the Info files are in $(BUILDDIR)/texinfo." - -gettext: - $(SPHINXBUILD) -b gettext $(I18NSPHINXOPTS) $(BUILDDIR)/locale - @echo - @echo "Build finished. The message catalogs are in $(BUILDDIR)/locale." - -changes: - $(SPHINXBUILD) -b changes $(ALLSPHINXOPTS) $(BUILDDIR)/changes - @echo - @echo "The overview file is in $(BUILDDIR)/changes." - -linkcheck: - $(SPHINXBUILD) -b linkcheck $(ALLSPHINXOPTS) $(BUILDDIR)/linkcheck - @echo - @echo "Link check complete; look for any errors in the above output " \ - "or in $(BUILDDIR)/linkcheck/output.txt." - -doctest: - $(SPHINXBUILD) -b doctest $(ALLSPHINXOPTS) $(BUILDDIR)/doctest - @echo "Testing of doctests in the sources finished, look at the " \ - "results in $(BUILDDIR)/doctest/output.txt." - -xml: - $(SPHINXBUILD) -b xml $(ALLSPHINXOPTS) $(BUILDDIR)/xml - @echo - @echo "Build finished. The XML files are in $(BUILDDIR)/xml." - -pseudoxml: - $(SPHINXBUILD) -b pseudoxml $(ALLSPHINXOPTS) $(BUILDDIR)/pseudoxml - @echo - @echo "Build finished. The pseudo-XML files are in $(BUILDDIR)/pseudoxml." - -# Manual publishing to the gh-pages branch - -GITREPOPATH = $(shell cd $(CURDIR) && git rev-parse --git-dir) -GITREMOTE = origin -GITREMOTEURL = $(shell git config --get remote.$(GITREMOTE).url) -GITLASTCOMMIT = $(shell git rev-parse --short HEAD) - -publish: - @test -d build/html || \ - ( echo >&2 "Run 'make html' first!"; false ) - git show-ref --verify --quiet refs/heads/gh-pages || \ - git branch --track gh-pages $(GITREMOTE)/gh-pages - test -d build/gh-pages || \ - git clone -s -b gh-pages $(GITREPOPATH) build/gh-pages - cd build/gh-pages && \ - git pull $(GITREMOTEURL) gh-pages - rsync -acv --delete --exclude=.git --exclude=.rsync-exclude \ - --exclude-from=build/gh-pages/.rsync-exclude \ - --link-dest=$(CURDIR)/build/html build/html/ build/gh-pages/ - cd build/gh-pages && \ - git add --all . && \ - git diff --cached --quiet || \ - git commit -m "Sync with the source at $(GITLASTCOMMIT)." - cd build/gh-pages && \ - git push origin gh-pages diff --git a/doc/manual/source/conf.py b/doc/manual/source/conf.py deleted file mode 100644 index 12452388..00000000 --- a/doc/manual/source/conf.py +++ /dev/null @@ -1,287 +0,0 @@ -#!/usr/bin/env python -# -*- coding: utf-8 -*- -# -# libdiffpy documentation build configuration file, created by -# sphinx-quickstart on Sun Feb 9 11:31:20 2014. -# -# This file is execfile()d with the current directory set to its -# containing dir. -# -# Note that not all possible configuration values are present in this -# autogenerated file. -# -# All configuration values have a default; values that are commented out -# serve to show the default. - -import sys -import os -import time - -# If extensions (or modules to document with autodoc) are in another directory, -# add these directories to sys.path here. If the directory is relative to the -# documentation root, use os.path.abspath to make it absolute, like shown here. -#sys.path.insert(0, os.path.abspath('.')) -sys.path.insert(0, os.path.abspath('../../../site_scons')) - -# abbreviations -ab_authors = u'Pavol Juhás, Christopher L. Farrow, Simon J.L. Billinge group' - -# -- General configuration ------------------------------------------------ - -# If your documentation needs a minimal Sphinx version, state it here. -#needs_sphinx = '1.0' - -# Add any Sphinx extension module names here, as strings. They can be -# extensions coming with Sphinx (named 'sphinx.ext.*') or your custom -# ones. -extensions = [ - 'sphinx.ext.intersphinx', - 'sphinx.ext.ifconfig', - 'm2r', -] - -# Add any paths that contain templates here, relative to this directory. -templates_path = ['_templates'] - -# The suffix of source filenames. -source_suffix = ['.rst', '.md'] - -# The encoding of source files. -#source_encoding = 'utf-8-sig' - -# The master toctree document. -master_doc = 'index' - -# General information about the project. -project = u'libdiffpy' -copyright = u'%Y, Brookhaven National Laboratory' - -# The version info for the project you're documenting, acts as replacement for -# |version| and |release|, also used in various other places throughout the -# built documents. -from libdiffpybuildutils import getversion -gver = getversion() -# The short X.Y version. -vfmt = '%(major)i.%(minor)i' + ('.%(micro)i' if gver['micro'] else '') -version = vfmt % gver -# The full version, including alpha/beta/rc tags. -release = gver['version'] - -# The language for content autogenerated by Sphinx. Refer to documentation -# for a list of supported languages. -#language = None - -# There are two options for replacing |today|: either, you set today to some -# non-false value, then it is used: -#today = '' -fulldate = gver['date'] -today_seconds = time.strptime(fulldate.split()[0], '%Y-%m-%d') -today = time.strftime('%B %d, %Y', today_seconds) -year = today.split()[-1] -# Else, today_fmt is used as the format for a strftime call. -#today_fmt = '%B %d, %Y' -# substitute YEAR in the copyright string -copyright = copyright.replace('%Y', year) - -# List of patterns, relative to source directory, that match files and -# directories to ignore when looking for source files. -exclude_patterns = [] - -# The reST default role (used for this markup: `text`) to use for all -# documents. -#default_role = None - -# If true, '()' will be appended to :func: etc. cross-reference text. -#add_function_parentheses = True - -# If true, the current module name will be prepended to all description -# unit titles (such as .. function::). -#add_module_names = True - -# If true, sectionauthor and moduleauthor directives will be shown in the -# output. They are ignored by default. -#show_authors = False - -# The name of the Pygments (syntax highlighting) style to use. -pygments_style = 'sphinx' - -# A list of ignored prefixes for module index sorting. -#modindex_common_prefix = [] - -# If true, keep warnings as "system message" paragraphs in the built documents. -#keep_warnings = False - -# Display all warnings for missing links. -nitpicky = True - -# -- Options for HTML output ---------------------------------------------- - -# The theme to use for HTML and HTML Help pages. See the documentation for -# a list of builtin themes. -# -html_theme = 'sphinx_py3doc_enhanced_theme' - -# Theme options are theme-specific and customize the look and feel of a theme -# further. For a list of options available for each theme, see the -# documentation. -# -html_theme_options = { - 'collapsiblesidebar' : 'true', - 'navigation_with_keys' : 'true', -} - -# Add any paths that contain custom themes here, relative to this directory. -#html_theme_path = [] - -# The name for this set of Sphinx documents. If None, it defaults to -# " v documentation". -#html_title = None - -# A shorter title for the navigation bar. Default is the same as html_title. -#html_short_title = None - -# The name of an image file (relative to this directory) to place at the top -# of the sidebar. -#html_logo = None - -# The name of an image file (within the static path) to use as favicon of the -# docs. This file should be a Windows icon file (.ico) being 16x16 or 32x32 -# pixels large. -#html_favicon = None - -# Add any paths that contain custom static files (such as style sheets) here, -# relative to this directory. They are copied after the builtin static files, -# so a file named "default.css" will overwrite the builtin "default.css". -# html_static_path = ['_static'] - -# Add any extra paths that contain custom files (such as robots.txt or -# .htaccess) here, relative to this directory. These files are copied -# directly to the root of the documentation. -#html_extra_path = [] - -# If not '', a 'Last updated on:' timestamp is inserted at every page bottom, -# using the given strftime format. -#html_last_updated_fmt = '%b %d, %Y' - -# If true, SmartyPants will be used to convert quotes and dashes to -# typographically correct entities. -#html_use_smartypants = True - -# Custom sidebar templates, maps document names to template names. -#html_sidebars = {} - -# Additional templates that should be rendered to pages, maps page names to -# template names. -#html_additional_pages = {} - -# If false, no module index is generated. -#html_domain_indices = True - -# If false, no index is generated. -#html_use_index = True - -# If true, the index is split into individual pages for each letter. -#html_split_index = False - -# If true, links to the reST sources are added to the pages. -#html_show_sourcelink = True - -# If true, "Created using Sphinx" is shown in the HTML footer. Default is True. -#html_show_sphinx = True - -# If true, "(C) Copyright ..." is shown in the HTML footer. Default is True. -#html_show_copyright = True - -# If true, an OpenSearch description file will be output, and all pages will -# contain a tag referring to it. The value of this option must be the -# base URL from which the finished HTML is served. -#html_use_opensearch = '' - -# This is the file name suffix for HTML files (e.g. ".xhtml"). -#html_file_suffix = None - -# Output file base name for HTML help builder. -htmlhelp_basename = 'libdiffpydoc' - - -# -- Options for LaTeX output --------------------------------------------- - -latex_elements = { -# The paper size ('letterpaper' or 'a4paper'). -#'papersize': 'letterpaper', - -# The font size ('10pt', '11pt' or '12pt'). -#'pointsize': '10pt', - -# Additional stuff for the LaTeX preamble. -#'preamble': '', -} - -# Grouping the document tree into LaTeX files. List of tuples -# (source start file, target name, title, -# author, documentclass [howto, manual, or own class]). -latex_documents = [ - ('index', 'libdiffpy.tex', u'libdiffpy Documentation', - ab_authors, 'manual'), -] - -# The name of an image file (relative to this directory) to place at the top of -# the title page. -#latex_logo = None - -# For "manual" documents, if this is true, then toplevel headings are parts, -# not chapters. -#latex_use_parts = False - -# If true, show page references after internal links. -#latex_show_pagerefs = False - -# If true, show URL addresses after external links. -#latex_show_urls = False - -# Documents to append as an appendix to all manuals. -#latex_appendices = [] - -# If false, no module index is generated. -#latex_domain_indices = True - - -# -- Options for manual page output --------------------------------------- - -# One entry per manual page. List of tuples -# (source start file, name, description, authors, manual section). -man_pages = [ - ('index', 'libdiffpy', u'libdiffpy Documentation', - ab_authors, 1) -] - -# If true, show URL addresses after external links. -#man_show_urls = False - - -# -- Options for Texinfo output ------------------------------------------- - -# Grouping the document tree into Texinfo files. List of tuples -# (source start file, target name, title, author, -# dir menu entry, description, category) -texinfo_documents = [ - ('index', 'libdiffpy', u'libdiffpy Documentation', - ab_authors, 'libdiffpy', 'One line description of project.', - 'Miscellaneous'), -] - -# Documents to append as an appendix to all manuals. -#texinfo_appendices = [] - -# If false, no module index is generated. -#texinfo_domain_indices = True - -# How to display URL addresses: 'footnote', 'no', or 'inline'. -#texinfo_show_urls = 'footnote' - -# If true, do not generate a @detailmenu in the "Top" node's menu. -#texinfo_no_detailmenu = False - - -# Example configuration for intersphinx: refer to the Python standard library. -# intersphinx_mapping = {'http://docs.python.org/': None} diff --git a/doc/manual/source/index.rst b/doc/manual/source/index.rst deleted file mode 100644 index b16f66e6..00000000 --- a/doc/manual/source/index.rst +++ /dev/null @@ -1,68 +0,0 @@ -.. libdiffpy documentation master file, created by - sphinx-quickstart on Sun Feb 9 11:31:20 2014. - You can adapt this file completely to your liking, but it should at least - contain the root `toctree` directive. - - -#################################################### -libdiffpy documentation -#################################################### - -libdiffpy - C++ calculators of PDF, bond valence sum and other pair quantities - -| Software version |release|. -| Last updated |today|. - -libdiffpy is a C++ library for calculating atomic pair distribution function -(PDF), bond valence sums, atom overlaps for a hard-sphere model, bond -distances and directions up to specified maximum distance. The atomic -structure models are represented by classes for non-periodic, periodic or -structures with space group symmetries. libdiffpy supports Crystal and -Molecule classes from the ObjCryst crystallographic library. Calculators -support two evaluation models - BASIC, which performs a full pair-summation -every time, and OPTIMIZED, which updates only pair contributions that have -changed since the last evaluation. libdiffpy supports object serialization -and parallel computations using parallel map function. PDF calculations can -be performed in two modes - either as a real-space summation of peak profiles -(PDFCalculator) or as a reciprocal-space Debye summation and Fourier transform -of the total scattering structure function (DebyePDFCalculator). - -The calculator objects in libdiffpy share common procedure for iteration -over atom pairs and only specialize the processing of pair contributions. -New calculator class can thus be readily defined for any quantity that is -obtained by iteration over atom pairs. - - -================ -Authors -================ - -This code was written by members of the Billinge Group at Columbia -University and Brookhaven National Laboratory including Pavol Juhás, -Christopher Farrow and Simon Billinge. -For a full list of contributors see -https://github.com/diffpy/libdiffpy/graphs/contributors. - -====================================== -Installation -====================================== - -See the `README `_ -file included with the distribution. - -====================================== -Table of contents -====================================== - -.. toctree:: - :titlesonly: - - license - release - -======================================================================== -Indices -======================================================================== - -* :ref:`genindex` -* :ref:`search` diff --git a/doc/manual/source/license.rst b/doc/manual/source/license.rst deleted file mode 100644 index 7d41179e..00000000 --- a/doc/manual/source/license.rst +++ /dev/null @@ -1,140 +0,0 @@ -License -####### - -OPEN SOURCE LICENSE AGREEMENT -============================= - -| Copyright (c) 2009-2011, University of Tennessee -| Copyright (c) 1989, 1991 Free Software Foundation, Inc. -| Copyright (c) 2006, The Regents of the University of California through - Lawrence Berkeley National Laboratory -| Copyright (c) 2014, Australian Synchrotron Research Program Inc., ("ASRP") -| Copyright (c) 2006-2007, Board of Trustees of Michigan State University -| Copyright (c) 2008-2012, The Trustees of Columbia University in - the City of New York -| Copyright (c) 2014-2019, Brookhaven Science Associates, - Brookhaven National Laboratory - - -The "DiffPy-CMI" is distributed subject to the following license conditions: - - -SOFTWARE LICENSE AGREEMENT -========================== - -Software: **DiffPy-CMI** - - -(1) The "Software", below, refers to the aforementioned DiffPy-CMI (in either -source code, or binary form and accompanying documentation). - -Part of the software was derived from the DANSE, ObjCryst++ (with permission), -PyCifRW, Python periodictable, CCTBX, and SasView open source projects, of -which the original Copyrights are contained in each individual file. - -Each licensee is addressed as "you" or "Licensee." - - -(2) The copyright holders shown above and their third-party Licensors hereby -grant licensee a royalty-free nonexclusive license, subject to the limitations -stated herein and U.S. Government license rights. - - -(3) You may modify and make a copy or copies of the software for use within -your organization, if you meet the following conditions: - - (a) Copies in source code must include the copyright notice and this - software license agreement. - - (b) Copies in binary form must include the copyright notice and this - Software License Agreement in the documentation and/or other materials - provided with the copy. - - -(4) You may modify a copy or copies of the Software or any portion of it, thus -forming a work based on the Software, and distribute copies of such work -outside your organization, if you meet all of the following conditions: - - (a) Copies in source code must include the copyright notice and this - Software License Agreement; - - (b) Copies in binary form must include the copyright notice and this - Software License Agreement in the documentation and/or other materials - provided with the copy; - - (c) Modified copies and works based on the Software must carry prominent - notices stating that you changed specified portions of the Software. - - (d) Neither the name of Brookhaven Science Associates or Brookhaven - National Laboratory nor the names of its contributors may be used to - endorse or promote products derived from this software without specific - written permission. - - -(5) Portions of the Software resulted from work developed under a U.S. -Government contract and are subject to the following license: -The Government is granted for itself and others acting on its behalf a -paid-up, nonexclusive, irrevocable worldwide license in this computer software -to reproduce, prepare derivative works, and perform publicly and display -publicly. - - -(6) WARRANTY DISCLAIMER. THE SOFTWARE IS SUPPLIED "AS IS" WITHOUT -WARRANTY OF ANY KIND. THE COPYRIGHT HOLDERS, THEIR THIRD PARTY -LICENSORS, THE UNITED STATES, THE UNITED STATES DEPARTMENT OF ENERGY, AND -THEIR EMPLOYEES: (1) DISCLAIM ANY WARRANTIES, EXPRESS OR IMPLIED, INCLUDING -BUT NOT LIMITED TO ANY IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A -PARTICULAR PURPOSE, TITLE OR NON-INFRINGEMENT, (2) DO NOT ASSUME ANY LEGAL -LIABILITY OR RESPONSIBILITY FOR THE ACCURACY, COMPLETENESS, OR USEFULNESS OF -THE SOFTWARE, (3) DO NOT REPRESENT THAT USE OF THE SOFTWARE WOULD NOT INFRINGE -PRIVATELY OWNED RIGHTS, (4) DO NOT WARRANT THAT THE SOFTWARE WILL FUNCTION -UNINTERRUPTED, THAT IT IS ERROR-FREE OR THAT ANY ERRORS WILL BE CORRECTED. - - -(7) LIMITATION OF LIABILITY. IN NO EVENT WILL THE COPYRIGHT HOLDERS, THEIR -THIRD PARTY LICENSORS, THE UNITED STATES, THE UNITED STATES DEPARTMENT OF -ENERGY, OR THEIR EMPLOYEES: BE LIABLE FOR ANY INDIRECT, INCIDENTAL, -CONSEQUENTIAL, SPECIAL OR PUNITIVE DAMAGES OF ANY KIND OR NATURE, INCLUDING -BUT NOT LIMITED TO LOSS OF PROFITS OR LOSS OF DATA, FOR ANY REASON WHATSOEVER, -WHETHER SUCH LIABILITY IS ASSERTED ON THE BASIS OF CONTRACT, TORT (INCLUDING -NEGLIGENCE OR STRICT LIABILITY), OR OTHERWISE, EVEN IF ANY OF SAID PARTIES HAS -BEEN WARNED OF THE POSSIBILITY OF SUCH LOSS OR DAMAGES. - - -Brookhaven National Laboratory Notice -===================================== - -Acknowledgment of sponsorship ------------------------------ - -This software was produced by the Brookhaven National Laboratory, under -Contract DE-AC02-98CH10886 with the Department of Energy. - - -Government disclaimer of liability ----------------------------------- - -Neither the United States nor the United States Department of Energy, nor -any of their employees, makes any warranty, express or implied, or assumes -any legal liability or responsibility for the accuracy, completeness, or -usefulness of any data, apparatus, product, or process disclosed, or -represents that its use would not infringe privately owned rights. - - -Brookhaven disclaimer of liability ----------------------------------- - -Brookhaven National Laboratory makes no representations or warranties, -express or implied, nor assumes any liability for the use of this software. - - -Maintenance of notice ---------------------- - -In the interest of clarity regarding the origin and status of this -software, Brookhaven National Laboratory requests that any recipient of it -maintain this notice affixed to any distribution by the recipient that -contains a copy or derivative of this software. - - -.. rubric:: END OF LICENSE diff --git a/doc/manual/source/release.rst b/doc/manual/source/release.rst deleted file mode 100644 index 2f0bd47f..00000000 --- a/doc/manual/source/release.rst +++ /dev/null @@ -1,3 +0,0 @@ -.. index:: release notes - -.. include:: ../../../CHANGELOG.rst diff --git a/docs/source/api/libdiffpy.rst b/docs/source/api/libdiffpy.rst deleted file mode 100644 index 3f7fdd00..00000000 --- a/docs/source/api/libdiffpy.rst +++ /dev/null @@ -1,11 +0,0 @@ -:tocdepth: -1 - -|title| -======= - -.. |title| replace:: libdiffpy package - -.. automodule:: libdiffpy - :members: - :undoc-members: - :show-inheritance: From e787297d3be9a5c6c299c2a7c0325ec7c9e148c8 Mon Sep 17 00:00:00 2001 From: Caden Myers Date: Mon, 26 Oct 2026 10:26:09 -0400 Subject: [PATCH 29/56] doc: manual edits to docs, including a description in index.rst and adding license.rst --- .gitattributes | 2 +- docs/source/conf.py | 21 +++-- docs/source/index.rst | 20 ++++- docs/source/license.rst | 164 +++++++++++++++++++++++++++++++++------- 4 files changed, 168 insertions(+), 39 deletions(-) diff --git a/.gitattributes b/.gitattributes index 04b6c92b..cb852f47 100644 --- a/.gitattributes +++ b/.gitattributes @@ -1,4 +1,4 @@ /.gitattributes export-ignore /.gitignore export-ignore -/doc export-ignore +/docs export-ignore gitarchive.cfg export-subst diff --git a/docs/source/conf.py b/docs/source/conf.py index 051bfb53..784ff33b 100644 --- a/docs/source/conf.py +++ b/docs/source/conf.py @@ -15,24 +15,29 @@ import sys import time -from importlib.metadata import version from pathlib import Path -# Attempt to import the version dynamically from GitHub tag. -try: - fullversion = version("libdiffpy") -except Exception: - fullversion = "No version found. The correct version will appear in the released version." # noqa: E501 - # If extensions (or modules to document with autodoc) are in another directory, # add these directories to sys.path here. If the directory is relative to the # documentation root, use Path().resolve() to make it absolute, like shown here. # noqa: E501 # 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. +try: + from libdiffpybuildutils import getversion + + fullversion = getversion()["version"] +except Exception: + fullversion = "No version found. The correct version will appear in the released version." # noqa: E501 # abbreviations -ab_authors = "Pavol Juhas, Chris Farrow, Simon Billinge, Billinge Group members" +ab_authors = ( + "Pavol Juhas, Chris Farrow, Simon Billinge, Billinge Group members" +) # -- General configuration ------------------------------------------------ diff --git a/docs/source/index.rst b/docs/source/index.rst index e80726b5..4164f2d9 100644 --- a/docs/source/index.rst +++ b/docs/source/index.rst @@ -9,6 +9,25 @@ | Software version |release| | Last updated |today|. +libdiffpy is a C++ library for calculating atomic pair distribution function +(PDF), bond valence sums, atom overlaps for a hard-sphere model, bond +distances and directions up to specified maximum distance. The atomic +structure models are represented by classes for non-periodic, periodic or +structures with space group symmetries. libdiffpy supports Crystal and +Molecule classes from the ObjCryst crystallographic library. Calculators +support two evaluation models - BASIC, which performs a full pair-summation +every time, and OPTIMIZED, which updates only pair contributions that have +changed since the last evaluation. libdiffpy supports object serialization +and parallel computations using parallel map function. PDF calculations can +be performed in two modes - either as a real-space summation of peak profiles +(PDFCalculator) or as a reciprocal-space Debye summation and Fourier transform +of the total scattering structure function (DebyePDFCalculator). + +The calculator objects in libdiffpy share common procedure for iteration +over atom pairs and only specialize the processing of pair contributions. +New calculator class can thus be readily defined for any quantity that is +obtained by iteration over atom pairs. + ======= Authors ======= @@ -35,7 +54,6 @@ Table of contents .. toctree:: :maxdepth: 2 - Package API release license diff --git a/docs/source/license.rst b/docs/source/license.rst index 75f32ed6..38f27eec 100644 --- a/docs/source/license.rst +++ b/docs/source/license.rst @@ -7,32 +7,138 @@ License OPEN SOURCE LICENSE AGREEMENT ============================= -BSD 3-Clause License - -Copyright (c) 2026, libdiffpy contributors. -All Rights Reserved. - -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions are met: - -1. Redistributions of source code must retain the above copyright notice, this - list of conditions and the following disclaimer. - -2. Redistributions in binary form must reproduce the above copyright notice, - this list of conditions and the following disclaimer in the documentation - and/or other materials provided with the distribution. - -3. Neither the name of the copyright holder nor the names of its - contributors may be used to endorse or promote products derived from - this software without specific prior written permission. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" -AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE -FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL -DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR -SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER -CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, -OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +| Copyright (c) 2009-2011, University of Tennessee +| Copyright (c) 1989, 1991 Free Software Foundation, Inc. +| Copyright (c) 2006, The Regents of the University of California through + Lawrence Berkeley National Laboratory +| Copyright (c) 2014, Australian Synchrotron Research Program Inc., ("ASRP") +| Copyright (c) 2006-2007, Board of Trustees of Michigan State University +| Copyright (c) 2008-2012, The Trustees of Columbia University in + the City of New York +| Copyright (c) 2014-2019, Brookhaven Science Associates, + Brookhaven National Laboratory + + +The "DiffPy-CMI" is distributed subject to the following license conditions: + + +SOFTWARE LICENSE AGREEMENT +========================== + +Software: **DiffPy-CMI** + + +(1) The "Software", below, refers to the aforementioned DiffPy-CMI (in either +source code, or binary form and accompanying documentation). + +Part of the software was derived from the DANSE, ObjCryst++ (with permission), +PyCifRW, Python periodictable, CCTBX, and SasView open source projects, of +which the original Copyrights are contained in each individual file. + +Each licensee is addressed as "you" or "Licensee." + + +(2) The copyright holders shown above and their third-party Licensors hereby +grant licensee a royalty-free nonexclusive license, subject to the limitations +stated herein and U.S. Government license rights. + + +(3) You may modify and make a copy or copies of the software for use within +your organization, if you meet the following conditions: + + (a) Copies in source code must include the copyright notice and this + software license agreement. + + (b) Copies in binary form must include the copyright notice and this + Software License Agreement in the documentation and/or other materials + provided with the copy. + + +(4) You may modify a copy or copies of the Software or any portion of it, thus +forming a work based on the Software, and distribute copies of such work +outside your organization, if you meet all of the following conditions: + + (a) Copies in source code must include the copyright notice and this + Software License Agreement; + + (b) Copies in binary form must include the copyright notice and this + Software License Agreement in the documentation and/or other materials + provided with the copy; + + (c) Modified copies and works based on the Software must carry prominent + notices stating that you changed specified portions of the Software. + + (d) Neither the name of Brookhaven Science Associates or Brookhaven + National Laboratory nor the names of its contributors may be used to + endorse or promote products derived from this software without specific + written permission. + + +(5) Portions of the Software resulted from work developed under a U.S. +Government contract and are subject to the following license: +The Government is granted for itself and others acting on its behalf a +paid-up, nonexclusive, irrevocable worldwide license in this computer software +to reproduce, prepare derivative works, and perform publicly and display +publicly. + + +(6) WARRANTY DISCLAIMER. THE SOFTWARE IS SUPPLIED "AS IS" WITHOUT +WARRANTY OF ANY KIND. THE COPYRIGHT HOLDERS, THEIR THIRD PARTY +LICENSORS, THE UNITED STATES, THE UNITED STATES DEPARTMENT OF ENERGY, AND +THEIR EMPLOYEES: (1) DISCLAIM ANY WARRANTIES, EXPRESS OR IMPLIED, INCLUDING +BUT NOT LIMITED TO ANY IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A +PARTICULAR PURPOSE, TITLE OR NON-INFRINGEMENT, (2) DO NOT ASSUME ANY LEGAL +LIABILITY OR RESPONSIBILITY FOR THE ACCURACY, COMPLETENESS, OR USEFULNESS OF +THE SOFTWARE, (3) DO NOT REPRESENT THAT USE OF THE SOFTWARE WOULD NOT INFRINGE +PRIVATELY OWNED RIGHTS, (4) DO NOT WARRANT THAT THE SOFTWARE WILL FUNCTION +UNINTERRUPTED, THAT IT IS ERROR-FREE OR THAT ANY ERRORS WILL BE CORRECTED. + + +(7) LIMITATION OF LIABILITY. IN NO EVENT WILL THE COPYRIGHT HOLDERS, THEIR +THIRD PARTY LICENSORS, THE UNITED STATES, THE UNITED STATES DEPARTMENT OF +ENERGY, OR THEIR EMPLOYEES: BE LIABLE FOR ANY INDIRECT, INCIDENTAL, +CONSEQUENTIAL, SPECIAL OR PUNITIVE DAMAGES OF ANY KIND OR NATURE, INCLUDING +BUT NOT LIMITED TO LOSS OF PROFITS OR LOSS OF DATA, FOR ANY REASON WHATSOEVER, +WHETHER SUCH LIABILITY IS ASSERTED ON THE BASIS OF CONTRACT, TORT (INCLUDING +NEGLIGENCE OR STRICT LIABILITY), OR OTHERWISE, EVEN IF ANY OF SAID PARTIES HAS +BEEN WARNED OF THE POSSIBILITY OF SUCH LOSS OR DAMAGES. + + +Brookhaven National Laboratory Notice +===================================== + +Acknowledgment of sponsorship +----------------------------- + +This software was produced by the Brookhaven National Laboratory, under +Contract DE-AC02-98CH10886 with the Department of Energy. + + +Government disclaimer of liability +---------------------------------- + +Neither the United States nor the United States Department of Energy, nor +any of their employees, makes any warranty, express or implied, or assumes +any legal liability or responsibility for the accuracy, completeness, or +usefulness of any data, apparatus, product, or process disclosed, or +represents that its use would not infringe privately owned rights. + + +Brookhaven disclaimer of liability +---------------------------------- + +Brookhaven National Laboratory makes no representations or warranties, +express or implied, nor assumes any liability for the use of this software. + + +Maintenance of notice +--------------------- + +In the interest of clarity regarding the origin and status of this +software, Brookhaven National Laboratory requests that any recipient of it +maintain this notice affixed to any distribution by the recipient that +contains a copy or derivative of this software. + + +.. rubric:: END OF LICENSE From d67c1924d5b5302d3cc585472c05a67cc20438bc Mon Sep 17 00:00:00 2001 From: Caden Myers Date: Mon, 26 Oct 2026 10:28:30 -0400 Subject: [PATCH 30/56] style: fix pre-commit (codespell typos, black/isort, exclude vendored cxxtest and data) --- .codespell/ignore_words.txt | 6 ++ .github/workflows/check-news-item.yml | 2 +- .pre-commit-config.yaml | 6 ++ news/docs-migration.rst | 25 ++++++++ site_scons/fallback_version.py | 11 ++-- site_scons/libdiffpybuildutils.py | 73 +++++++++++----------- src/diffpy/HasClassRegistry.hpp | 4 +- src/diffpy/srreal/PairQuantity.cpp | 2 +- src/diffpy/srreal/PeakProfile.hpp | 2 +- src/diffpy/srreal/PointsInSphere.cpp | 4 +- src/diffpy/srreal/QuantityType.hpp | 2 +- src/diffpy/srreal/R3linalg.cpp | 2 +- src/diffpy/srreal/R3linalg.hpp | 2 +- src/tests/TestObjCrystStructureAdapter.hpp | 4 +- src/tests/TestPeriodicStructureAdapter.hpp | 4 +- 15 files changed, 94 insertions(+), 55 deletions(-) create mode 100644 news/docs-migration.rst diff --git a/.codespell/ignore_words.txt b/.codespell/ignore_words.txt index 04b4fcfa..9cc611c5 100644 --- a/.codespell/ignore_words.txt +++ b/.codespell/ignore_words.txt @@ -6,3 +6,9 @@ mater ;; Frobenius norm used in np.linalg.norm fro + +;; variable name for a pair type multiplicity in BaseDebyeSum.cpp +multipl + +;; custom scattering factor label used in TestScatteringFactorTable.hpp +ccustom diff --git a/.github/workflows/check-news-item.yml b/.github/workflows/check-news-item.yml index fc34d9f5..6a572b63 100644 --- a/.github/workflows/check-news-item.yml +++ b/.github/workflows/check-news-item.yml @@ -3,7 +3,7 @@ name: Check for News on: pull_request_target: branches: - - main # GitHub does not evaluate expressions in trigger filters; edit this value if your base branch is not main + - main # GitHub does not evaluate expressions in trigger filters; edit this value if your base branch is not main jobs: check-news-item: diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 74f98b2c..eefb5210 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -9,6 +9,12 @@ ci: autoupdate_schedule: monthly skip: [no-commit-to-branch] submodules: false +# Leave the vendored CxxTest SCons tool and the data tables untouched. +exclude: | + (?x)^( + site_scons/site_tools/cxxtest\.py| + .*\.(cif|dat) + )$ repos: - repo: https://github.com/pre-commit/pre-commit-hooks rev: v6.0.0 diff --git a/news/docs-migration.rst b/news/docs-migration.rst new file mode 100644 index 00000000..e13f7faa --- /dev/null +++ b/news/docs-migration.rst @@ -0,0 +1,25 @@ +**Added:** + +* Library overview from the old manual on the ``docs/`` index page. + +**Changed:** + +* Moved the Sphinx documentation from ``doc/manual/`` to the scikit-package ``docs/`` layout. +* Read the documentation version from git tags through ``site_scons/libdiffpybuildutils.py``. + +**Deprecated:** + +* + +**Removed:** + +* Old ``doc/manual/`` Sphinx project. +* Python ``automodule`` API page, which cannot document a C++ library. + +**Fixed:** + +* Pre-commit hooks now pass: fixed spelling in C++ comments, reformatted ``site_scons`` helpers, and excluded the vendored CxxTest tool and data tables. + +**Security:** + +* diff --git a/site_scons/fallback_version.py b/site_scons/fallback_version.py index 01b1119b..59b8cdd0 100644 --- a/site_scons/fallback_version.py +++ b/site_scons/fallback_version.py @@ -1,10 +1,11 @@ #!/usr/bin/env python -''' -Definition of FALLBACK_VERSION. Use it when git repository is -not available for example, when building from git zip archive. +"""Definition of FALLBACK_VERSION. + +Use it when git repository is not available for example, when building +from git zip archive. Update FALLBACK_VERSION when tagging a new release. -''' +""" -FALLBACK_VERSION = '1.4.0.post0' +FALLBACK_VERSION = "1.4.0.post0" diff --git a/site_scons/libdiffpybuildutils.py b/site_scons/libdiffpybuildutils.py index a8d8afbd..28580aa2 100644 --- a/site_scons/libdiffpybuildutils.py +++ b/site_scons/libdiffpybuildutils.py @@ -1,7 +1,6 @@ #!/usr/bin/env python -'''Utility functions used by scons and sphinx for version extraction. -''' +"""Utility functions used by scons and sphinx for version extraction.""" import os import re @@ -21,79 +20,81 @@ def gitinfo(): - 'Extract dictionary of version data from git records.' - from subprocess import Popen, PIPE + "Extract dictionary of version data from git records." + from subprocess import PIPE, Popen + global _cached_gitinfo if _cached_gitinfo is not None: return _cached_gitinfo - nullfile = open(os.devnull, 'w') - kw = dict(stdout=PIPE, stderr=nullfile, cwd=MYDIR, - universal_newlines=True) - proc = Popen(['git', 'describe', '--match=v[[:digit:]]*'], **kw) + nullfile = open(os.devnull, "w") + kw = dict(stdout=PIPE, stderr=nullfile, cwd=MYDIR, universal_newlines=True) + proc = Popen(["git", "describe", "--match=v[[:digit:]]*"], **kw) desc = proc.stdout.read() if proc.wait(): _cached_gitinfo = {} return gitinfo() - proc = Popen(['git', 'log', '-1', '--format=%H %ci'], **kw) + proc = Popen(["git", "log", "-1", "--format=%H %ci"], **kw) glog = proc.stdout.read() - words = desc.strip().split('-') - vtag = words[0].lstrip('v') - vnum = int((words[1:2] or ['0'])[0]) + words = desc.strip().split("-") + vtag = words[0].lstrip("v") + vnum = int((words[1:2] or ["0"])[0]) rv = {} - rv['commit'], rv['date'] = glog.strip().split(None, 1) - rv['patchnumber'] = vnum - rv['version'] = vtag + rv["commit"], rv["date"] = glog.strip().split(None, 1) + rv["patchnumber"] = vnum + rv["version"] = vtag if vnum: - rv['version'] += '.post' + str(vnum) + rv["version"] += ".post" + str(vnum) _cached_gitinfo = rv return gitinfo() + _cached_gitinfo = None def getversion(): """Extract version from gitinfo and/or gitarchive.cfg file. - Process gitinfo first and make sure it matches FALLBACK_VERSION - in gitarchive.cfg. Use expanded data from gitarchive.cfg when - these sources are from git archive bundle. + Process gitinfo first and make sure it matches FALLBACK_VERSION in + gitarchive.cfg. Use expanded data from gitarchive.cfg when these + sources are from git archive bundle. """ from fallback_version import FALLBACK_VERSION - gitarchivecfgfile = os.path.join(MYDIR, 'gitarchive.cfg') + + 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) + gaitems = re.findall(r"^(\w+) *= *(\S.*?)\s*$", gacontent, re.M) ga = dict(gaitems) gi = gitinfo() rv = {} if gi: afb = FALLBACK_VERSION - gfb = gi['version'].split('.post')[0] + '.post0' + gfb = gi["version"].split(".post")[0] + ".post0" if gfb != afb: raise RuntimeError(EMSG_BAD_FALLBACK_VERSION.format(afb, gfb)) rv.update(gi) else: # Not a git repository. Require that gitarchive.cfg is expanded. - if '$Format:' in ga['commit']: + if "$Format:" in ga["commit"]: raise RuntimeError(EMSG_GIT_MISSING) - rv['commit'] = ga['commit'] - rv['date'] = ga['date'] + rv["commit"] = ga["commit"] + rv["date"] = ga["date"] # First assume we have an undetermined post-release. Keep version # suffix as ".post0", but set patchnumber to ensure we are post # FALLBACK_VERSION. - rv['version'] = FALLBACK_VERSION - rv['patchnumber'] = 1 + rv["version"] = FALLBACK_VERSION + rv["patchnumber"] = 1 # If we are at version tag turn this to regular release. - mx = re.search(r'\btag: v(\d[^,]*)', ga['refnames']) + mx = re.search(r"\btag: v(\d[^,]*)", ga["refnames"]) if mx: - rv['version'] = mx.group(1) - rv['patchnumber'] = 0 + rv["version"] = mx.group(1) + rv["patchnumber"] = 0 # rv['version'] is resolved, let's parse its subfields. - vbase = rv['version'].split('.post')[0] - mx = re.match(r'^(\d+)\.(\d+)(?:\.(\d+))?((?:[ab]|rc)\d*)?', vbase) - rv['major'] = int(mx.group(1)) - rv['minor'] = int(mx.group(2)) - rv['micro'] = int(mx.group(3) or 0) - rv['prerelease'] = mx.group(4) + vbase = rv["version"].split(".post")[0] + mx = re.match(r"^(\d+)\.(\d+)(?:\.(\d+))?((?:[ab]|rc)\d*)?", vbase) + rv["major"] = int(mx.group(1)) + rv["minor"] = int(mx.group(2)) + rv["micro"] = int(mx.group(3) or 0) + rv["prerelease"] = mx.group(4) return rv diff --git a/src/diffpy/HasClassRegistry.hpp b/src/diffpy/HasClassRegistry.hpp index 87832b18..762e2992 100644 --- a/src/diffpy/HasClassRegistry.hpp +++ b/src/diffpy/HasClassRegistry.hpp @@ -16,7 +16,7 @@ * functions that produce derived classes. To be used as a base for a * specific class branch. For a proper function, the HasClassRegistry * template must be instantiated in just one cpp file that includes the -* implementatios in HasClassRegistry.ipp. +* implementations in HasClassRegistry.ipp. * * A Base class with registry should be declared as * class Base : public HasClassRegistry @@ -62,7 +62,7 @@ class HasClassRegistry /// Clear registration of the specified type under its standard name /// or any alias. Return the number of unset names and aliases or - /// 0 if the specifed string type was not registered. + /// 0 if the specified string type was not registered. static int deregisterType(const std::string& tp); /// Create new instance of a specified string type. diff --git a/src/diffpy/srreal/PairQuantity.cpp b/src/diffpy/srreal/PairQuantity.cpp index 178c6815..f31886c4 100644 --- a/src/diffpy/srreal/PairQuantity.cpp +++ b/src/diffpy/srreal/PairQuantity.cpp @@ -448,7 +448,7 @@ void PairQuantity::updateMaskData() // rebuild minvertpairmask according to mtypemask minvertpairmask.clear(); TypeMaskStorage::const_iterator tpmsk; - // build a list of type masks with all-masks at the begining + // build a list of type masks with all-masks at the beginning list< pair > orderedpairs; for (tpmsk = mtypemask.begin(); tpmsk != mtypemask.end(); ++tpmsk) { diff --git a/src/diffpy/srreal/PeakProfile.hpp b/src/diffpy/srreal/PeakProfile.hpp index d6fd9ad8..c31540f3 100644 --- a/src/diffpy/srreal/PeakProfile.hpp +++ b/src/diffpy/srreal/PeakProfile.hpp @@ -13,7 +13,7 @@ ****************************************************************************** * * class PeakProfile -- base class for calculation of peak profiles. -* When possible total integrated area of the profile should eqaul 1. +* When possible total integrated area of the profile should equal 1. * The operator()(x, fwhm) returns amplitude of a zero-centered profile. * Methods xboundlo(fwhm), xboundhi(fwhm) return low and high x-boundaries, * where amplitude relative to the maximum becomes smaller than precision diff --git a/src/diffpy/srreal/PointsInSphere.cpp b/src/diffpy/srreal/PointsInSphere.cpp index 4b732026..d149d570 100644 --- a/src/diffpy/srreal/PointsInSphere.cpp +++ b/src/diffpy/srreal/PointsInSphere.cpp @@ -14,7 +14,7 @@ * * classes PointsInSphere, ReflectionsInQminQmax, ReflectionsInDmaxDmin * -* Comments: sequencers for lattice points insided 3D sphere +* Comments: sequencers for lattice points inside 3D sphere * *****************************************************************************/ @@ -117,7 +117,7 @@ void PointsInSphere::rewind() mHalfSpan = Rmax()*latpar.ar; hi_m = int(ceil(mHalfSpan)); this->_m = -hi_m; - // make indices n, o invalid, reset the neares point + // make indices n, o invalid, reset the nearest point this->_n = hi_n = 0; this->_o = hi_o = outside_o = 0; n0plane = o0plane = o0line = 0.0; diff --git a/src/diffpy/srreal/QuantityType.hpp b/src/diffpy/srreal/QuantityType.hpp index 07dda8d7..d577f43b 100644 --- a/src/diffpy/srreal/QuantityType.hpp +++ b/src/diffpy/srreal/QuantityType.hpp @@ -16,7 +16,7 @@ * * QuantityType -- type that stores PairQuantity results, an array of doubles * It is a unique derived class from vector to avoid conflicts with -* boost_python convertors in cctbx. +* boost_python converters in cctbx. * *****************************************************************************/ diff --git a/src/diffpy/srreal/R3linalg.cpp b/src/diffpy/srreal/R3linalg.cpp index c3473a67..c648eba8 100644 --- a/src/diffpy/srreal/R3linalg.cpp +++ b/src/diffpy/srreal/R3linalg.cpp @@ -12,7 +12,7 @@ * ****************************************************************************** * -* R3linalg -- vector and matrix types and linar algebra operations in R3 space +* R3linalg -- vector and matrix types and linear algebra operations in R3 space * *****************************************************************************/ diff --git a/src/diffpy/srreal/R3linalg.hpp b/src/diffpy/srreal/R3linalg.hpp index 8cd07dbc..6b6ad054 100644 --- a/src/diffpy/srreal/R3linalg.hpp +++ b/src/diffpy/srreal/R3linalg.hpp @@ -12,7 +12,7 @@ * ****************************************************************************** * -* R3linalg -- vector and matrix types and linar algebra operations in R3 space +* R3linalg -- vector and matrix types and linear algebra operations in R3 space * *****************************************************************************/ diff --git a/src/tests/TestObjCrystStructureAdapter.hpp b/src/tests/TestObjCrystStructureAdapter.hpp index 7428a03b..eaa4da27 100644 --- a/src/tests/TestObjCrystStructureAdapter.hpp +++ b/src/tests/TestObjCrystStructureAdapter.hpp @@ -357,7 +357,7 @@ class TestObjCrystStructureBondGenerator : public CxxTest::TestSuite // 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 neigbhors within radius 3.6 + // which sums to 18 neighbors within radius 3.6 m_nibnds->setRmin(0); TS_ASSERT_EQUALS(18, countBonds(*m_nibnds)); } @@ -444,7 +444,7 @@ class TestObjCrystStructureBondGenerator : public CxxTest::TestSuite { TS_ASSERT_DELTA(0.00356, testmsd1(stru, bnds), epsu); } - // other 3 tantalums are related by tripple axis and + // other 3 tantalums are related by triple axis and // have the same msd towards the central Li else { diff --git a/src/tests/TestPeriodicStructureAdapter.hpp b/src/tests/TestPeriodicStructureAdapter.hpp index 935e7ec4..a6d562d4 100644 --- a/src/tests/TestPeriodicStructureAdapter.hpp +++ b/src/tests/TestPeriodicStructureAdapter.hpp @@ -318,7 +318,7 @@ class TestPeriodicStructureBondGenerator : public CxxTest::TestSuite // 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 neigbhors within radius 3.6 + // which sums to 18 neighbors within radius 3.6 m_nibnds->setRmin(0); TS_ASSERT_EQUALS(18, countBonds(*m_nibnds)); } @@ -408,7 +408,7 @@ class TestPeriodicStructureBondGenerator : public CxxTest::TestSuite { TS_ASSERT_DELTA(0.00356, testmsd1(stru, bnds), epsu); } - // other 3 tantalums are related by tripple axis and + // other 3 tantalums are related by triple axis and // have the same msd towards the central Li else { From f34124c703d6c1b3370670afa08469bf9561aeee Mon Sep 17 00:00:00 2001 From: Caden Myers Date: Mon, 26 Oct 2026 11:20:24 -0400 Subject: [PATCH 31/56] ci: modify skpkg ci for libdiffpy --- .github/ISSUE_TEMPLATE/release_checklist.md | 38 ++++----- .../build-and-publish-docs-on-dispatch.yml | 50 ++++++++--- .github/workflows/matrix-and-codecov.yml | 84 +++++++++++++++---- .github/workflows/release-github-on-tag.yml | 10 ++- news/ci.rst | 26 ++++++ 5 files changed, 162 insertions(+), 46 deletions(-) create mode 100644 news/ci.rst diff --git a/.github/ISSUE_TEMPLATE/release_checklist.md b/.github/ISSUE_TEMPLATE/release_checklist.md index 56c5fca3..63e624ba 100644 --- a/.github/ISSUE_TEMPLATE/release_checklist.md +++ b/.github/ISSUE_TEMPLATE/release_checklist.md @@ -1,41 +1,41 @@ --- name: Release -about: Checklist and communication channel for PyPI and GitHub release -title: "Ready for PyPI/GitHub release" +about: Checklist and communication channel for GitHub and conda-forge release +title: "Ready for GitHub/conda-forge release" labels: "release" assignees: "" --- -### PyPI/GitHub rc-release preparation checklist: +### GitHub rc-release preparation checklist: - [ ] All PRs/issues attached to the release are merged. - [ ] All the badges on the README are passing. - [ ] License information is verified as correct. If you are unsure, please comment below. -- [ ] Locally rendered documentation contains all appropriate pages, tutorials, and other human-written text is up-to-date with any changes in the code. -- [ ] All API references are included. To check this, run `conda install scikit-package` and then `package build api-doc`. Review any edits made by rerendering the docs locally. +- [ ] 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. -- [ ] Successfully run any tutorial examples or do functional testing with the latest Python version. - [ ] Grammar and writing quality are checked (no typos). -- [ ] Install `pip install build twine`, run `python -m build` and `twine check dist/*` to ensure that the package can be built and is correctly formatted for PyPI release. -- [ ] Dispatch matrix testing to test the release on all Python versions and systems. If you do not have permission to run this workflow, tag the maintainer and say `@maintainer, please dispatch matrix testing workflow`. +- [ ] `FALLBACK_VERSION` in `site_scons/fallback_version.py` is updated to `.post0`. Without this, the SCons build fails after tagging. +- [ ] `scons test` passes locally 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 on the tag and blocks the release if it fails. -Please tag the maintainer (e.g., @username) in the comment here when you are ready for the PyPI/GitHub release. Include any additional comments necessary, such as version information and details about the pre-release here: +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: -### PyPI/GitHub full-release preparation checklist: +### GitHub full-release preparation checklist: -- [ ] Create a new conda environment and install the rc from PyPI (`pip install ==??`) -- [ ] License information on PyPI is correct. -- [ ] Docs are deployed successfully to `https:///`. -- [ ] Successfully run all tests, tutorial examples or do functional testing. +- [ ] The rc GitHub release and its source tarball appear correctly on the releases page. +- [ ] Docs are deployed successfully to `https://www.diffpy.org/libdiffpy`. +- [ ] Successfully run all tests and do functional testing, e.g., with `diffpy.srreal` built against the rc. Please let the maintainer know that all checks are done and the package is ready for full release. ### conda-forge release preparation checklist: - + -- [ ] Ensure that the full release has appeared on PyPI successfully. -- [ ] New package dependencies listed in `conda.txt` and `tests.txt` are added to `meta.yaml` in the feedstock. +- [ ] Ensure that the full release has appeared on GitHub successfully. +- [ ] `version` and `sha256` of the GitHub source tarball are updated in `meta.yaml` in the feedstock. +- [ ] New package dependencies listed in `build.txt`, `conda.txt` and `tests.txt` are added to `meta.yaml` in the feedstock. +- [ ] `license_file` in `meta.yaml` points to `LICENSE.rst` (renamed from `LICENSE.txt`). - [ ] Close any open issues on the feedstock. Reach out to the maintainer if you have questions. - [ ] Tag the maintainer for conda-forge release. @@ -43,5 +43,5 @@ Please let the maintainer know that all checks are done and the package is ready -- [ ] Run tutorial examples and conduct functional testing using the installation guide in the README. Attach screenshots/results as comments. -- [ ] Documentation (README, tutorials, API references, and websites) is deployed without broken links or missing figures. +- [ ] Install the released package from conda-forge and conduct functional testing using the installation guide in the README. Attach screenshots/results as comments. +- [ ] Documentation (README, API references, and websites) is deployed without broken links or missing figures. diff --git a/.github/workflows/build-and-publish-docs-on-dispatch.yml b/.github/workflows/build-and-publish-docs-on-dispatch.yml index 1d6992ea..dfc69fd1 100644 --- a/.github/workflows/build-and-publish-docs-on-dispatch.yml +++ b/.github/workflows/build-and-publish-docs-on-dispatch.yml @@ -3,16 +3,44 @@ name: Build and Publish Docs on Dispatch on: workflow_dispatch: -jobs: - get-python-version: - uses: scikit-package/release-scripts/.github/workflows/_get-python-version-latest.yml@v0 - with: - python_version: 0 +permissions: + contents: write +jobs: docs: - uses: scikit-package/release-scripts/.github/workflows/_release-docs.yml@v0 - with: - project: libdiffpy - c_extension: true - headless: false - python_version: ${{ fromJSON(needs.get-python-version.outputs.latest_python_version) }} + runs-on: ubuntu-latest + defaults: + run: + shell: bash -l {0} + + steps: + - name: Check out libdiffpy + uses: actions/checkout@v6 + with: + fetch-depth: 0 # Version is read from git tags + + - name: Initialize miniconda + uses: conda-incubator/setup-miniconda@v3 + with: + activate-environment: docs + channels: conda-forge + conda-remove-defaults: "true" + auto-update-conda: true + auto-activate-base: false + + - name: Conda config + run: >- + conda config --set always_yes yes + --set changeps1 no + + - name: Install docs requirements + run: conda install --file requirements/docs.txt + + - name: Build documents + run: make -C docs html + + - name: Deploy + uses: peaceiris/actions-gh-pages@v4 + with: + github_token: ${{ secrets.GITHUB_TOKEN }} + publish_dir: ./docs/build/html diff --git a/.github/workflows/matrix-and-codecov.yml b/.github/workflows/matrix-and-codecov.yml index 969d4952..356438e2 100644 --- a/.github/workflows/matrix-and-codecov.yml +++ b/.github/workflows/matrix-and-codecov.yml @@ -1,21 +1,77 @@ name: Matrix and Codecov +# Called by release-github-on-tag.yml so a failing test blocks the release. on: - # push: - # branches: - # - main - release: - types: - - prereleased - - published + workflow_call: + secrets: + CODECOV_TOKEN: + required: false workflow_dispatch: jobs: matrix-coverage: - uses: scikit-package/release-scripts/.github/workflows/_matrix-and-codecov-on-merge-to-main.yml@v0 - with: - project: libdiffpy - c_extension: true - headless: false - secrets: - CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }} + runs-on: ${{ matrix.os }} + strategy: + fail-fast: false + matrix: + os: [ubuntu-latest, macos-latest, macos-15-intel] + defaults: + run: + shell: bash -l {0} + + steps: + - name: Check out libdiffpy + uses: actions/checkout@v6 + with: + fetch-depth: 0 + + - name: Initialize miniconda + uses: conda-incubator/setup-miniconda@v3 + with: + activate-environment: test + channels: conda-forge + conda-remove-defaults: "true" + auto-update-conda: true + auto-activate-base: false + + - name: Conda config + run: >- + conda config --set always_yes yes + --set changeps1 no + + - name: Install requirements + run: >- + conda install + --file requirements/build.txt + --file requirements/conda.txt + --file requirements/tests.txt + + - name: Configure SCons for the conda environment + run: | + cat > sconscript.local <<'EOF' + Import('env') + import os + prefix = os.environ['CONDA_PREFIX'] + 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')) + EOF + + - name: Build and run C++ tests with coverage + run: scons build=coverage test + + - name: Generate coverage report + if: matrix.os == 'ubuntu-latest' + run: >- + gcovr --gcov-executable "$CONDA_PREFIX/bin/gcov" + --root . --filter 'build/coverage-.*/diffpy/' + --xml coverage.xml + + - name: Upload coverage to Codecov + if: matrix.os == 'ubuntu-latest' + uses: codecov/codecov-action@v5 + with: + files: coverage.xml + verbose: true + token: ${{ secrets.CODECOV_TOKEN }} diff --git a/.github/workflows/release-github-on-tag.yml b/.github/workflows/release-github-on-tag.yml index a5511623..8a15be37 100644 --- a/.github/workflows/release-github-on-tag.yml +++ b/.github/workflows/release-github-on-tag.yml @@ -1,4 +1,4 @@ -name: Build Wheel and Release +name: Release on GitHub # Trigger on tag push or manual dispatch. # Tag and release privilege are verified inside the reusable workflow. @@ -19,8 +19,14 @@ jobs: secrets: PAT_TOKEN: ${{ secrets.PAT_TOKEN }} + matrix-tests: + needs: [check-tag-privilege] + uses: ./.github/workflows/matrix-and-codecov.yml + secrets: + CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }} + release-github: - needs: [check-tag-privilege, check-pat-token-validity] + needs: [check-tag-privilege, check-pat-token-validity, matrix-tests] uses: scikit-package/release-scripts/.github/workflows/_release-github.yml@v0 secrets: PAT_TOKEN: ${{ secrets.PAT_TOKEN }} diff --git a/news/ci.rst b/news/ci.rst new file mode 100644 index 00000000..6385ed34 --- /dev/null +++ b/news/ci.rst @@ -0,0 +1,26 @@ +**Added:** + +* + +**Changed:** + +* Run matrix testing with SCons on Ubuntu and macOS (arm64 and Intel) instead of the Python-only pytest matrix. +* Run matrix testing before every GitHub release and block the release if any test fails. +* Build and deploy docs with Sphinx directly instead of the Python-only scikit-package docs workflow. +* Update the release checklist for GitHub and conda-forge releases without PyPI. + +**Deprecated:** + +* + +**Removed:** + +* + +**Fixed:** + +* + +**Security:** + +* From ae48804dcd2e2e5de93d75a6aff53b02b83f6b43 Mon Sep 17 00:00:00 2001 From: Caden Myers Date: Mon, 26 Oct 2026 11:34:51 -0400 Subject: [PATCH 32/56] ci: publish the docs after a release succeeds --- .github/workflows/build-and-publish-docs-on-dispatch.yml | 3 +++ .github/workflows/release-github-on-tag.yml | 6 ++++++ news/ci.rst | 1 + 3 files changed, 10 insertions(+) diff --git a/.github/workflows/build-and-publish-docs-on-dispatch.yml b/.github/workflows/build-and-publish-docs-on-dispatch.yml index dfc69fd1..2928fff9 100644 --- a/.github/workflows/build-and-publish-docs-on-dispatch.yml +++ b/.github/workflows/build-and-publish-docs-on-dispatch.yml @@ -1,6 +1,8 @@ name: Build and Publish Docs on Dispatch +# Called by release-github-on-tag.yml after a successful release. on: + workflow_call: workflow_dispatch: permissions: @@ -17,6 +19,7 @@ jobs: - name: Check out libdiffpy uses: actions/checkout@v6 with: + ref: ${{ github.ref }} # The release tag is recreated after the changelog update fetch-depth: 0 # Version is read from git tags - name: Initialize miniconda diff --git a/.github/workflows/release-github-on-tag.yml b/.github/workflows/release-github-on-tag.yml index 8a15be37..cbac8a43 100644 --- a/.github/workflows/release-github-on-tag.yml +++ b/.github/workflows/release-github-on-tag.yml @@ -30,3 +30,9 @@ jobs: uses: scikit-package/release-scripts/.github/workflows/_release-github.yml@v0 secrets: PAT_TOKEN: ${{ secrets.PAT_TOKEN }} + + docs-release: + needs: [release-github] + permissions: + contents: write + uses: ./.github/workflows/build-and-publish-docs-on-dispatch.yml diff --git a/news/ci.rst b/news/ci.rst index 6385ed34..43a7fa4d 100644 --- a/news/ci.rst +++ b/news/ci.rst @@ -7,6 +7,7 @@ * Run matrix testing with SCons on Ubuntu and macOS (arm64 and Intel) instead of the Python-only pytest matrix. * Run matrix testing before every GitHub release and block the release if any test fails. * Build and deploy docs with Sphinx directly instead of the Python-only scikit-package docs workflow. +* Publish docs automatically after a successful GitHub release. * Update the release checklist for GitHub and conda-forge releases without PyPI. **Deprecated:** From 8e625c2788615cf3b1afd3a5ffe5902174dc3c5b Mon Sep 17 00:00:00 2001 From: Caden Myers Date: Mon, 26 Oct 2026 11:54:38 -0400 Subject: [PATCH 33/56] run news check on any PR --- .github/workflows/check-news-item.yml | 2 -- 1 file changed, 2 deletions(-) diff --git a/.github/workflows/check-news-item.yml b/.github/workflows/check-news-item.yml index fc34d9f5..e9a4b785 100644 --- a/.github/workflows/check-news-item.yml +++ b/.github/workflows/check-news-item.yml @@ -2,8 +2,6 @@ name: Check for News on: pull_request_target: - branches: - - main # GitHub does not evaluate expressions in trigger filters; edit this value if your base branch is not main jobs: check-news-item: From 11ea2c73f75580fe5325a24651d25df935ad5613 Mon Sep 17 00:00:00 2001 From: Caden Myers Date: Mon, 26 Oct 2026 15:22:03 -0400 Subject: [PATCH 34/56] ci: revert matrix-and-codecov.yml to the skpkg-migration version Co-Authored-By: Claude Opus 5.5 --- .github/workflows/matrix-and-codecov.yml | 84 ++++-------------------- 1 file changed, 14 insertions(+), 70 deletions(-) diff --git a/.github/workflows/matrix-and-codecov.yml b/.github/workflows/matrix-and-codecov.yml index 356438e2..969d4952 100644 --- a/.github/workflows/matrix-and-codecov.yml +++ b/.github/workflows/matrix-and-codecov.yml @@ -1,77 +1,21 @@ name: Matrix and Codecov -# Called by release-github-on-tag.yml so a failing test blocks the release. on: - workflow_call: - secrets: - CODECOV_TOKEN: - required: false + # push: + # branches: + # - main + release: + types: + - prereleased + - published workflow_dispatch: jobs: matrix-coverage: - runs-on: ${{ matrix.os }} - strategy: - fail-fast: false - matrix: - os: [ubuntu-latest, macos-latest, macos-15-intel] - defaults: - run: - shell: bash -l {0} - - steps: - - name: Check out libdiffpy - uses: actions/checkout@v6 - with: - fetch-depth: 0 - - - name: Initialize miniconda - uses: conda-incubator/setup-miniconda@v3 - with: - activate-environment: test - channels: conda-forge - conda-remove-defaults: "true" - auto-update-conda: true - auto-activate-base: false - - - name: Conda config - run: >- - conda config --set always_yes yes - --set changeps1 no - - - name: Install requirements - run: >- - conda install - --file requirements/build.txt - --file requirements/conda.txt - --file requirements/tests.txt - - - name: Configure SCons for the conda environment - run: | - cat > sconscript.local <<'EOF' - Import('env') - import os - prefix = os.environ['CONDA_PREFIX'] - 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')) - EOF - - - name: Build and run C++ tests with coverage - run: scons build=coverage test - - - name: Generate coverage report - if: matrix.os == 'ubuntu-latest' - run: >- - gcovr --gcov-executable "$CONDA_PREFIX/bin/gcov" - --root . --filter 'build/coverage-.*/diffpy/' - --xml coverage.xml - - - name: Upload coverage to Codecov - if: matrix.os == 'ubuntu-latest' - uses: codecov/codecov-action@v5 - with: - files: coverage.xml - verbose: true - token: ${{ secrets.CODECOV_TOKEN }} + uses: scikit-package/release-scripts/.github/workflows/_matrix-and-codecov-on-merge-to-main.yml@v0 + with: + project: libdiffpy + c_extension: true + headless: false + secrets: + CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }} From 767958bb7a442e1ea48f24ec0435ade830c81488 Mon Sep 17 00:00:00 2001 From: Caden Myers Date: Mon, 26 Oct 2026 15:23:51 -0400 Subject: [PATCH 35/56] rm matrix testing from release CI --- .github/workflows/release-github-on-tag.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/release-github-on-tag.yml b/.github/workflows/release-github-on-tag.yml index cbac8a43..71b8bc26 100644 --- a/.github/workflows/release-github-on-tag.yml +++ b/.github/workflows/release-github-on-tag.yml @@ -26,7 +26,7 @@ jobs: CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }} release-github: - needs: [check-tag-privilege, check-pat-token-validity, matrix-tests] + needs: [check-tag-privilege, check-pat-token-validity] uses: scikit-package/release-scripts/.github/workflows/_release-github.yml@v0 secrets: PAT_TOKEN: ${{ secrets.PAT_TOKEN }} From e4b9ff0b586b0865fd0c0849c2ca03cd1e888538 Mon Sep 17 00:00:00 2001 From: Caden Myers Date: Mon, 26 Oct 2026 15:24:32 -0400 Subject: [PATCH 36/56] rm matrix testing --- .github/workflows/release-github-on-tag.yml | 6 ------ 1 file changed, 6 deletions(-) diff --git a/.github/workflows/release-github-on-tag.yml b/.github/workflows/release-github-on-tag.yml index 71b8bc26..5816ff38 100644 --- a/.github/workflows/release-github-on-tag.yml +++ b/.github/workflows/release-github-on-tag.yml @@ -19,12 +19,6 @@ jobs: secrets: PAT_TOKEN: ${{ secrets.PAT_TOKEN }} - matrix-tests: - needs: [check-tag-privilege] - uses: ./.github/workflows/matrix-and-codecov.yml - secrets: - CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }} - release-github: needs: [check-tag-privilege, check-pat-token-validity] uses: scikit-package/release-scripts/.github/workflows/_release-github.yml@v0 From 5d15b53fd0c981b65d737aeb1db32e64efdd3370 Mon Sep 17 00:00:00 2001 From: Caden Myers Date: Mon, 26 Oct 2026 15:37:34 -0400 Subject: [PATCH 37/56] ci: Use reusable workflow for doc publishing --- .../build-and-publish-docs-on-dispatch.yml | 53 ++++--------------- .github/workflows/release-github-on-tag.yml | 7 ++- news/ci.rst | 3 -- 3 files changed, 17 insertions(+), 46 deletions(-) diff --git a/.github/workflows/build-and-publish-docs-on-dispatch.yml b/.github/workflows/build-and-publish-docs-on-dispatch.yml index 2928fff9..1d6992ea 100644 --- a/.github/workflows/build-and-publish-docs-on-dispatch.yml +++ b/.github/workflows/build-and-publish-docs-on-dispatch.yml @@ -1,49 +1,18 @@ name: Build and Publish Docs on Dispatch -# Called by release-github-on-tag.yml after a successful release. on: - workflow_call: workflow_dispatch: -permissions: - contents: write - jobs: - docs: - runs-on: ubuntu-latest - defaults: - run: - shell: bash -l {0} - - steps: - - name: Check out libdiffpy - uses: actions/checkout@v6 - with: - ref: ${{ github.ref }} # The release tag is recreated after the changelog update - fetch-depth: 0 # Version is read from git tags - - - name: Initialize miniconda - uses: conda-incubator/setup-miniconda@v3 - with: - activate-environment: docs - channels: conda-forge - conda-remove-defaults: "true" - auto-update-conda: true - auto-activate-base: false + get-python-version: + uses: scikit-package/release-scripts/.github/workflows/_get-python-version-latest.yml@v0 + with: + python_version: 0 - - name: Conda config - run: >- - conda config --set always_yes yes - --set changeps1 no - - - name: Install docs requirements - run: conda install --file requirements/docs.txt - - - name: Build documents - run: make -C docs html - - - name: Deploy - uses: peaceiris/actions-gh-pages@v4 - with: - github_token: ${{ secrets.GITHUB_TOKEN }} - publish_dir: ./docs/build/html + docs: + uses: scikit-package/release-scripts/.github/workflows/_release-docs.yml@v0 + with: + project: libdiffpy + c_extension: true + headless: false + python_version: ${{ fromJSON(needs.get-python-version.outputs.latest_python_version) }} diff --git a/.github/workflows/release-github-on-tag.yml b/.github/workflows/release-github-on-tag.yml index 5816ff38..b771e7c5 100644 --- a/.github/workflows/release-github-on-tag.yml +++ b/.github/workflows/release-github-on-tag.yml @@ -29,4 +29,9 @@ jobs: needs: [release-github] permissions: contents: write - uses: ./.github/workflows/build-and-publish-docs-on-dispatch.yml + uses: scikit-package/release-scripts/.github/workflows/_release-docs.yml@v0 + with: + project: libdiffpy + c_extension: true + headless: false + python_version: 0 diff --git a/news/ci.rst b/news/ci.rst index 43a7fa4d..8197c60b 100644 --- a/news/ci.rst +++ b/news/ci.rst @@ -4,9 +4,6 @@ **Changed:** -* Run matrix testing with SCons on Ubuntu and macOS (arm64 and Intel) instead of the Python-only pytest matrix. -* Run matrix testing before every GitHub release and block the release if any test fails. -* Build and deploy docs with Sphinx directly instead of the Python-only scikit-package docs workflow. * Publish docs automatically after a successful GitHub release. * Update the release checklist for GitHub and conda-forge releases without PyPI. From 69c43f493b013e13a8e004e830bda197501beaea Mon Sep 17 00:00:00 2001 From: SYMSCAE Date: Wed, 28 Oct 2026 14:57:09 -0700 Subject: [PATCH 38/56] style: apply clang-format (Google style) to C++ sources --- .clang-format | 10 + .pre-commit-config.yaml | 6 + examples/testlib.cpp | 16 +- src/diffpy/Attributes.cpp | 256 ++-- src/diffpy/Attributes.hpp | 281 ++-- src/diffpy/EventTicker.cpp | 112 +- src/diffpy/EventTicker.hpp | 151 +-- src/diffpy/Export.hpp | 12 +- src/diffpy/HasClassRegistry.hpp | 130 +- src/diffpy/mathutils.hpp | 126 +- src/diffpy/runtimepath.cpp | 251 ++-- src/diffpy/runtimepath.hpp | 73 +- src/diffpy/serialization.hpp | 48 +- src/diffpy/srreal/AtomRadiiTable.cpp | 164 +-- src/diffpy/srreal/AtomRadiiTable.hpp | 117 +- src/diffpy/srreal/AtomUtils.cpp | 106 +- src/diffpy/srreal/AtomUtils.hpp | 36 +- src/diffpy/srreal/AtomicStructureAdapter.cpp | 411 +++--- src/diffpy/srreal/AtomicStructureAdapter.hpp | 268 ++-- src/diffpy/srreal/BVParam.cpp | 168 +-- src/diffpy/srreal/BVParam.hpp | 138 +- src/diffpy/srreal/BVParametersTable.cpp | 251 ++-- src/diffpy/srreal/BVParametersTable.hpp | 139 +- src/diffpy/srreal/BVSCalculator.cpp | 317 ++--- src/diffpy/srreal/BVSCalculator.hpp | 165 ++- src/diffpy/srreal/BaseBondGenerator.cpp | 320 ++--- src/diffpy/srreal/BaseBondGenerator.hpp | 171 ++- src/diffpy/srreal/BaseDebyeSum.cpp | 383 +++--- src/diffpy/srreal/BaseDebyeSum.hpp | 198 ++- src/diffpy/srreal/BondCalculator.cpp | 417 +++--- src/diffpy/srreal/BondCalculator.hpp | 212 ++- src/diffpy/srreal/ConstantPeakWidth.cpp | 135 +- src/diffpy/srreal/ConstantPeakWidth.hpp | 101 +- src/diffpy/srreal/ConstantRadiiTable.cpp | 77 +- src/diffpy/srreal/ConstantRadiiTable.hpp | 89 +- src/diffpy/srreal/CroppedGaussianProfile.cpp | 104 +- src/diffpy/srreal/CroppedGaussianProfile.hpp | 95 +- src/diffpy/srreal/CrystalStructureAdapter.cpp | 475 +++---- src/diffpy/srreal/CrystalStructureAdapter.hpp | 265 ++-- src/diffpy/srreal/DebyePDFCalculator.cpp | 510 ++++--- src/diffpy/srreal/DebyePDFCalculator.hpp | 220 ++- src/diffpy/srreal/DebyeWallerPeakWidth.cpp | 86 +- src/diffpy/srreal/DebyeWallerPeakWidth.hpp | 85 +- src/diffpy/srreal/EmptyStructureAdapter.cpp | 144 +- src/diffpy/srreal/GaussianProfile.cpp | 110 +- src/diffpy/srreal/GaussianProfile.hpp | 100 +- src/diffpy/srreal/JeongPeakWidth.cpp | 194 ++- src/diffpy/srreal/JeongPeakWidth.hpp | 136 +- src/diffpy/srreal/Lattice.cpp | 471 +++---- src/diffpy/srreal/Lattice.hpp | 727 ++++------ src/diffpy/srreal/LinearBaseline.cpp | 88 +- src/diffpy/srreal/LinearBaseline.hpp | 92 +- src/diffpy/srreal/NoMetaStructureAdapter.cpp | 186 ++- src/diffpy/srreal/NoMetaStructureAdapter.hpp | 131 +- .../srreal/NoSymmetryStructureAdapter.cpp | 186 ++- .../srreal/NoSymmetryStructureAdapter.hpp | 135 +- .../srreal/ObjCrystStructureAdapter.cpp | 303 ++--- .../srreal/ObjCrystStructureAdapter.hpp | 41 +- src/diffpy/srreal/OverlapCalculator.cpp | 873 ++++++------ src/diffpy/srreal/OverlapCalculator.hpp | 241 ++-- src/diffpy/srreal/PDFBaseline.cpp | 34 +- src/diffpy/srreal/PDFBaseline.hpp | 69 +- src/diffpy/srreal/PDFCalculator.cpp | 1067 +++++++-------- src/diffpy/srreal/PDFCalculator.hpp | 377 +++--- src/diffpy/srreal/PDFEnvelope.cpp | 164 +-- src/diffpy/srreal/PDFEnvelope.hpp | 139 +- src/diffpy/srreal/PDFUtils.cpp | 127 +- src/diffpy/srreal/PDFUtils.hpp | 66 +- src/diffpy/srreal/PQEvaluator.cpp | 682 +++++----- src/diffpy/srreal/PQEvaluator.hpp | 221 ++-- src/diffpy/srreal/PairCounter.cpp | 46 +- src/diffpy/srreal/PairCounter.hpp | 69 +- src/diffpy/srreal/PairQuantity.cpp | 704 +++++----- src/diffpy/srreal/PairQuantity.hpp | 265 ++-- src/diffpy/srreal/PeakProfile.cpp | 57 +- src/diffpy/srreal/PeakProfile.hpp | 107 +- src/diffpy/srreal/PeakWidthModel.cpp | 75 +- src/diffpy/srreal/PeakWidthModel.hpp | 145 +- .../srreal/PeriodicStructureAdapter.cpp | 289 ++-- .../srreal/PeriodicStructureAdapter.hpp | 171 ++- src/diffpy/srreal/PointsInSphere.cpp | 502 +++---- src/diffpy/srreal/PointsInSphere.hpp | 424 +++--- src/diffpy/srreal/QResolutionEnvelope.cpp | 90 +- src/diffpy/srreal/QResolutionEnvelope.hpp | 92 +- src/diffpy/srreal/QuantityType.hpp | 90 +- src/diffpy/srreal/R3linalg.cpp | 166 +-- src/diffpy/srreal/R3linalg.hpp | 364 +++-- src/diffpy/srreal/SFTElectron.cpp | 80 +- src/diffpy/srreal/SFTElectron.hpp | 85 +- src/diffpy/srreal/SFTElectronNumber.cpp | 77 +- src/diffpy/srreal/SFTElectronNumber.hpp | 87 +- src/diffpy/srreal/SFTNeutron.cpp | 80 +- src/diffpy/srreal/SFTNeutron.hpp | 87 +- src/diffpy/srreal/SFTXray.cpp | 78 +- src/diffpy/srreal/SFTXray.hpp | 85 +- src/diffpy/srreal/ScaleEnvelope.cpp | 87 +- src/diffpy/srreal/ScaleEnvelope.hpp | 92 +- src/diffpy/srreal/ScatteringFactorTable.cpp | 191 ++- src/diffpy/srreal/ScatteringFactorTable.hpp | 160 ++- src/diffpy/srreal/SphericalShapeEnvelope.cpp | 96 +- src/diffpy/srreal/SphericalShapeEnvelope.hpp | 92 +- src/diffpy/srreal/StepCutEnvelope.cpp | 88 +- src/diffpy/srreal/StepCutEnvelope.hpp | 84 +- src/diffpy/srreal/StructureAdapter.cpp | 158 +-- src/diffpy/srreal/StructureAdapter.hpp | 186 ++- src/diffpy/srreal/StructureDifference.cpp | 93 +- src/diffpy/srreal/StructureDifference.hpp | 95 +- src/diffpy/srreal/ZeroBaseline.cpp | 64 +- src/diffpy/srreal/ZeroBaseline.hpp | 72 +- src/diffpy/srreal/forwardtypes.hpp | 36 +- src/diffpy/srreal/scatteringfactordata.cpp | 613 ++++----- src/diffpy/srreal/scatteringfactordata.hpp | 4 +- src/diffpy/validators.hpp | 94 +- src/diffpy/version.cpp | 32 +- src/tests/TestAtomRadiiTable.hpp | 214 ++- src/tests/TestAtomicStructureAdapter.hpp | 250 ++-- src/tests/TestAttributes.hpp | 119 +- src/tests/TestBVParametersTable.hpp | 399 +++--- src/tests/TestBVSCalculator.hpp | 274 ++-- src/tests/TestBVSObjCryst.hpp | 95 +- src/tests/TestDebyePDFCalculator.hpp | 637 ++++----- src/tests/TestEmptyStructureAdapter.hpp | 106 +- src/tests/TestHasClassRegistry.hpp | 127 +- src/tests/TestLattice.hpp | 295 ++--- src/tests/TestNoMetaStructureAdapter.hpp | 165 ++- src/tests/TestObjCrystStructureAdapter.hpp | 1175 ++++++++--------- src/tests/TestOverlapCalculator.hpp | 641 +++++---- src/tests/TestPDFBaseline.hpp | 142 +- src/tests/TestPDFCalculator.hpp | 484 +++---- src/tests/TestPQEvaluator.hpp | 488 ++++--- src/tests/TestPairCounter.hpp | 258 ++-- src/tests/TestPeakProfile.hpp | 269 ++-- src/tests/TestPeakWidthModel.hpp | 331 +++-- src/tests/TestPeriodicStructureAdapter.hpp | 703 +++++----- src/tests/TestPointsInSphere.hpp | 311 ++--- src/tests/TestR3linalg.hpp | 209 ++- src/tests/TestRuntimePath.hpp | 131 +- src/tests/TestScatteringFactorTable.hpp | 364 +++-- src/tests/TestSphericalShapeEnvelope.hpp | 134 +- src/tests/TestStepCutEnvelope.hpp | 136 +- src/tests/objcryst_helpers.cpp | 58 +- src/tests/objcryst_helpers.hpp | 32 +- src/tests/serialization_helpers.hpp | 47 +- src/tests/test_custompqconfig.hpp | 115 +- src/tests/test_helpers.cpp | 195 ++- src/tests/test_helpers.hpp | 36 +- 146 files changed, 13653 insertions(+), 16738 deletions(-) create mode 100644 .clang-format diff --git a/.clang-format b/.clang-format new file mode 100644 index 00000000..0e711179 --- /dev/null +++ b/.clang-format @@ -0,0 +1,10 @@ +BasedOnStyle: Google + +IndentWidth: 2 +ContinuationIndentWidth: 2 +TabWidth: 2 +UseTab: Never + +ColumnLimit: 80 + +SortIncludes: false diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index eefb5210..cffbf824 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -39,6 +39,12 @@ repos: hooks: - id: isort args: ["--profile", "black"] + - repo: https://github.com/pre-commit/mirrors-clang-format + rev: v20.1.6 + hooks: + - id: clang-format + name: clang-format (C/C++) + files: \.(c|cpp|cc|cxx|h|hpp|hh)$ - repo: https://github.com/pre-commit/pre-commit-hooks rev: v6.0.0 hooks: diff --git a/examples/testlib.cpp b/examples/testlib.cpp index 18abadfd..79ad5101 100644 --- a/examples/testlib.cpp +++ b/examples/testlib.cpp @@ -4,16 +4,14 @@ // c++ testlib.cpp -ldiffpy // ./a.out - #include #include -int main(int argc, char* argv[]) -{ - using namespace diffpy::srreal; - ScatteringFactorTablePtr ntbl; - ntbl = ScatteringFactorTable::createByType("neutron"); - ntbl->lookup("Pb"); - std::cout << "Installation of libdiffpy shared library works!\n"; - return 0; +int main(int argc, char* argv[]) { + using namespace diffpy::srreal; + ScatteringFactorTablePtr ntbl; + ntbl = ScatteringFactorTable::createByType("neutron"); + ntbl->lookup("Pb"); + std::cout << "Installation of libdiffpy shared library works!\n"; + return 0; } diff --git a/src/diffpy/Attributes.cpp b/src/diffpy/Attributes.cpp index f555863a..bebab133 100644 --- a/src/diffpy/Attributes.cpp +++ b/src/diffpy/Attributes.cpp @@ -1,21 +1,21 @@ /***************************************************************************** -* -* 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.txt for license information. -* -****************************************************************************** -* -* class Attributes - interface for calling setter and getter methods using -* their string names. -* -*****************************************************************************/ + * + * 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.txt for license information. + * + ****************************************************************************** + * + * class Attributes - interface for calling setter and getter methods using + * their string names. + * + *****************************************************************************/ #include @@ -32,192 +32,136 @@ namespace attributes { // Public Methods ------------------------------------------------------------ -double Attributes::getDoubleAttr(const string& name) const -{ - this->checkAttributeName(name); - GetDoubleAttrVisitor vg(name); - this->accept(vg); - return vg.getValue(); +double Attributes::getDoubleAttr(const string& name) const { + this->checkAttributeName(name); + GetDoubleAttrVisitor vg(name); + this->accept(vg); + return vg.getValue(); } - -void Attributes::setDoubleAttr(const string& name, double value) -{ - this->checkAttributeName(name); - SetDoubleAttrVisitor vs(name, value); - this->accept(vs); +void Attributes::setDoubleAttr(const string& name, double value) { + this->checkAttributeName(name); + SetDoubleAttrVisitor vs(name, value); + this->accept(vs); } - -bool Attributes::hasDoubleAttr(const string& name) const -{ - CountDoubleAttrVisitor vc(name); - this->accept(vc); - return vc.count(); +bool Attributes::hasDoubleAttr(const string& name) const { + CountDoubleAttrVisitor vc(name); + this->accept(vc); + return vc.count(); } - -set Attributes::namesOfDoubleAttributes() const -{ - const bool excludereadonly = false; - NamesOfDoubleAttributesVisitor vn(excludereadonly); - this->accept(vn); - set rv = vn.names(); - return rv; +set Attributes::namesOfDoubleAttributes() const { + const bool excludereadonly = false; + NamesOfDoubleAttributesVisitor vn(excludereadonly); + this->accept(vn); + set rv = vn.names(); + return rv; } - -set Attributes::namesOfWritableDoubleAttributes() const -{ - const bool excludereadonly = true; - NamesOfDoubleAttributesVisitor vn(excludereadonly); - this->accept(vn); - set rv = vn.names(); - return rv; +set Attributes::namesOfWritableDoubleAttributes() const { + const bool excludereadonly = true; + NamesOfDoubleAttributesVisitor vn(excludereadonly); + this->accept(vn); + set rv = vn.names(); + return rv; } // Private Methods ----------------------------------------------------------- -void Attributes::checkAttributeName(const string& name) const -{ - CountDoubleAttrVisitor v(name); - this->accept(v); - if (v.count() == 0) - { - ostringstream emsg; - emsg << "Invalid attribute name '" << name << "'."; - throw DoubleAttributeError(emsg.str()); - } - else if (v.count() > 1) - { - ostringstream emsg; - emsg << "Duplicate attribute '" << name << "'."; - throw logic_error(emsg.str()); - } +void Attributes::checkAttributeName(const string& name) const { + CountDoubleAttrVisitor v(name); + this->accept(v); + if (v.count() == 0) { + ostringstream emsg; + emsg << "Invalid attribute name '" << name << "'."; + throw DoubleAttributeError(emsg.str()); + } else if (v.count() > 1) { + ostringstream emsg; + emsg << "Duplicate attribute '" << name << "'."; + throw logic_error(emsg.str()); + } } // Visitor Classes ----------------------------------------------------------- // CountDoubleAttrVisitor -Attributes::CountDoubleAttrVisitor:: -CountDoubleAttrVisitor(const string& name) : - mname(name), - mcount(0) -{ } +Attributes::CountDoubleAttrVisitor::CountDoubleAttrVisitor(const string& name) + : mname(name), mcount(0) {} - -void Attributes::CountDoubleAttrVisitor:: -visit(const Attributes& a) -{ - mcount += a.mdoubleattrs.count(mname); +void Attributes::CountDoubleAttrVisitor::visit(const Attributes& a) { + mcount += a.mdoubleattrs.count(mname); } - -int Attributes::CountDoubleAttrVisitor:: -count() const -{ - return mcount; -} +int Attributes::CountDoubleAttrVisitor::count() const { return mcount; } // GetDoubleAttrVisitor -Attributes::GetDoubleAttrVisitor:: -GetDoubleAttrVisitor(const string& name) : - mname(name) -{ } - - -void Attributes::GetDoubleAttrVisitor:: -visit(const Attributes& a) -{ - DoubleAttributeStorage::const_iterator ai; - ai = a.mdoubleattrs.find(mname); - if (ai != a.mdoubleattrs.end()) - { - mvalue = ai->second->getValue(&a); - } -} +Attributes::GetDoubleAttrVisitor::GetDoubleAttrVisitor(const string& name) + : mname(name) {} - -double Attributes::GetDoubleAttrVisitor:: -getValue() const -{ - return mvalue; +void Attributes::GetDoubleAttrVisitor::visit(const Attributes& a) { + DoubleAttributeStorage::const_iterator ai; + ai = a.mdoubleattrs.find(mname); + if (ai != a.mdoubleattrs.end()) { + mvalue = ai->second->getValue(&a); + } } -// SetDoubleAttrVisitor +double Attributes::GetDoubleAttrVisitor::getValue() const { return mvalue; } -Attributes::SetDoubleAttrVisitor:: -SetDoubleAttrVisitor(const string& name, double value) : - mname(name), - mvalue(value) -{ } +// SetDoubleAttrVisitor +Attributes::SetDoubleAttrVisitor::SetDoubleAttrVisitor(const string& name, + double value) + : mname(name), mvalue(value) {} -void Attributes::SetDoubleAttrVisitor:: -visit(const Attributes& a) -{ - const char* emsg = "Cannot set attribute of a const instance."; - throw logic_error(emsg); +void Attributes::SetDoubleAttrVisitor::visit(const Attributes& a) { + const char* emsg = "Cannot set attribute of a const instance."; + throw logic_error(emsg); } - -void Attributes::SetDoubleAttrVisitor:: -visit(Attributes& a) -{ - DoubleAttributeStorage::iterator ai; - ai = a.mdoubleattrs.find(mname); - if (ai != a.mdoubleattrs.end()) - { - ai->second->setValue(&a, mvalue); - } +void Attributes::SetDoubleAttrVisitor::visit(Attributes& a) { + DoubleAttributeStorage::iterator ai; + ai = a.mdoubleattrs.find(mname); + if (ai != a.mdoubleattrs.end()) { + ai->second->setValue(&a, mvalue); + } } // NamesOfDoubleAttributesVisitor -Attributes::NamesOfDoubleAttributesVisitor:: -NamesOfDoubleAttributesVisitor(bool excludereadonly) -{ - mexcludereadonly = excludereadonly; +Attributes::NamesOfDoubleAttributesVisitor::NamesOfDoubleAttributesVisitor( + bool excludereadonly) { + mexcludereadonly = excludereadonly; } - -void Attributes::NamesOfDoubleAttributesVisitor:: -visit(const Attributes& a) -{ - DoubleAttributeStorage::const_iterator ati; - for (ati = a.mdoubleattrs.begin(); ati != a.mdoubleattrs.end(); ++ati) - { - if (mexcludereadonly && ati->second->isreadonly()) continue; - mnames.insert(ati->first); - } +void Attributes::NamesOfDoubleAttributesVisitor::visit(const Attributes& a) { + DoubleAttributeStorage::const_iterator ati; + for (ati = a.mdoubleattrs.begin(); ati != a.mdoubleattrs.end(); ++ati) { + if (mexcludereadonly && ati->second->isreadonly()) continue; + mnames.insert(ati->first); + } } - -const set& Attributes::NamesOfDoubleAttributesVisitor:: -names() const -{ - return mnames; +const set& Attributes::NamesOfDoubleAttributesVisitor::names() const { + return mnames; } // Non-member Helpers -------------------------------------------------------- -void registerBaseDoubleAttribute(Attributes* obj, - const string& name, BaseDoubleAttribute* pa) -{ - obj->mdoubleattrs[name].reset(pa); +void registerBaseDoubleAttribute(Attributes* obj, const string& name, + BaseDoubleAttribute* pa) { + obj->mdoubleattrs[name].reset(pa); } - -void throwDoubleAttributeReadOnly() -{ - const char* emsg = - "Cannot change value of read-only DoubleAttribute."; - throw DoubleAttributeError(emsg); +void throwDoubleAttributeReadOnly() { + const char* emsg = "Cannot change value of read-only DoubleAttribute."; + throw DoubleAttributeError(emsg); } -} // namespace attributes -} // namespace diffpy +} // namespace attributes +} // namespace diffpy // End of file diff --git a/src/diffpy/Attributes.hpp b/src/diffpy/Attributes.hpp index df50e153..e2eafe02 100644 --- a/src/diffpy/Attributes.hpp +++ b/src/diffpy/Attributes.hpp @@ -1,21 +1,21 @@ /***************************************************************************** -* -* 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.txt for license information. -* -****************************************************************************** -* -* class Attributes - interface for calling setter and getter methods using -* their string names. -* -*****************************************************************************/ + * + * 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.txt for license information. + * + ****************************************************************************** + * + * class Attributes - interface for calling setter and getter methods using + * their string names. + * + *****************************************************************************/ #ifndef ATTRIBUTES_HPP_INCLUDED #define ATTRIBUTES_HPP_INCLUDED @@ -37,39 +37,30 @@ class Attributes; /// @brief custom exception for Attributes-related errors. This is /// thrown for invalid names or for attempts to set a read-only attribute. -class DoubleAttributeError : public std::runtime_error -{ - public: - DoubleAttributeError(const std::string msg="") : - std::runtime_error(msg) - { } +class DoubleAttributeError : public std::runtime_error { + public: + DoubleAttributeError(const std::string msg = "") : std::runtime_error(msg) {} }; /// @class BaseDoubleAttribute /// @brief abstract base class for accessing a particular double attribute -class DLL_EXPORT BaseDoubleAttribute -{ - public: - - virtual ~BaseDoubleAttribute() { } - virtual double getValue(const Attributes* obj) const = 0; - virtual void setValue(Attributes* obj, double value) = 0; - virtual bool isreadonly() const = 0; +class DLL_EXPORT BaseDoubleAttribute { + public: + virtual ~BaseDoubleAttribute() {} + virtual double getValue(const Attributes* obj) const = 0; + virtual void setValue(Attributes* obj, double value) = 0; + virtual bool isreadonly() const = 0; }; +class BaseAttributesVisitor { + public: + virtual ~BaseAttributesVisitor() {} -class BaseAttributesVisitor -{ - public: - - virtual ~BaseAttributesVisitor() { } - - virtual void visit(const Attributes& a) = 0; - virtual void visit(Attributes& a) - { - visit(static_cast(a)); - } + virtual void visit(const Attributes& a) = 0; + virtual void visit(Attributes& a) { + visit(static_cast(a)); + } }; /// @class Attributes @@ -77,124 +68,106 @@ class BaseAttributesVisitor /// should derive from Attributes and register their setter and /// getter methods in their constructors. -class DLL_EXPORT Attributes -{ - public: - - // class is virtual - virtual ~Attributes() { } - - // assignments in derived classes should not change mdoubleattrs - // mdoubleattrs should be only changed via registerDoubleAttribute - Attributes& operator=(const Attributes& other) { return *this; } - - // methods - double getDoubleAttr(const std::string& name) const; - void setDoubleAttr(const std::string& name, double value); - bool hasDoubleAttr(const std::string& name) const; - std::set namesOfDoubleAttributes() const; - std::set namesOfWritableDoubleAttributes() const; - // visitors - virtual void accept(BaseAttributesVisitor& v) { v.visit(*this); } - virtual void accept(BaseAttributesVisitor& v) const { v.visit(*this); } - - protected: - - friend DLL_EXPORT void registerBaseDoubleAttribute(Attributes*, - const std::string&, attributes::BaseDoubleAttribute* pa); - template - void registerDoubleAttribute(const std::string& name, T* obj, Getter); - template - void registerDoubleAttribute(const std::string& name, T* obj, Getter, Setter); - - private: - - // types - typedef std::map > - DoubleAttributeStorage; - // data - DoubleAttributeStorage mdoubleattrs; - - // methods - void checkAttributeName(const std::string& name) const; - - // visitor classes - - class CountDoubleAttrVisitor : public BaseAttributesVisitor - { - public: - - CountDoubleAttrVisitor(const std::string& name); - virtual void visit(const Attributes& a); - int count() const; - - private: - - // data - const std::string& mname; - int mcount; - }; - - - class GetDoubleAttrVisitor : public BaseAttributesVisitor - { - public: - - GetDoubleAttrVisitor(const std::string& name); - virtual void visit(const Attributes& a); - double getValue() const; - - private: - - // data - const std::string& mname; - double mvalue; - }; - - - class SetDoubleAttrVisitor : public BaseAttributesVisitor - { - public: - - SetDoubleAttrVisitor(const std::string& name, double value); - virtual void visit(const Attributes& a); - virtual void visit(Attributes& a); - - private: - - // data - const std::string& mname; - double mvalue; - }; - - - class NamesOfDoubleAttributesVisitor : public BaseAttributesVisitor - { - public: - - NamesOfDoubleAttributesVisitor(bool excludereadonly); - virtual void visit(const Attributes& a); - const std::set& names() const; - - private: - - // data - bool mexcludereadonly; - std::set mnames; - }; +class DLL_EXPORT Attributes { + public: + // class is virtual + virtual ~Attributes() {} + + // assignments in derived classes should not change mdoubleattrs + // mdoubleattrs should be only changed via registerDoubleAttribute + Attributes& operator=(const Attributes& other) { return *this; } + + // methods + double getDoubleAttr(const std::string& name) const; + void setDoubleAttr(const std::string& name, double value); + bool hasDoubleAttr(const std::string& name) const; + std::set namesOfDoubleAttributes() const; + std::set namesOfWritableDoubleAttributes() const; + // visitors + virtual void accept(BaseAttributesVisitor& v) { v.visit(*this); } + virtual void accept(BaseAttributesVisitor& v) const { v.visit(*this); } + + protected: + friend DLL_EXPORT void registerBaseDoubleAttribute( + Attributes*, const std::string&, attributes::BaseDoubleAttribute* pa); + template + void registerDoubleAttribute(const std::string& name, T* obj, Getter); + template + void registerDoubleAttribute(const std::string& name, T* obj, Getter, Setter); + + private: + // types + typedef std::map > + DoubleAttributeStorage; + // data + DoubleAttributeStorage mdoubleattrs; + + // methods + void checkAttributeName(const std::string& name) const; + + // visitor classes + + class CountDoubleAttrVisitor : public BaseAttributesVisitor { + public: + CountDoubleAttrVisitor(const std::string& name); + virtual void visit(const Attributes& a); + int count() const; + + private: + // data + const std::string& mname; + int mcount; + }; + + class GetDoubleAttrVisitor : public BaseAttributesVisitor { + public: + GetDoubleAttrVisitor(const std::string& name); + virtual void visit(const Attributes& a); + double getValue() const; + + private: + // data + const std::string& mname; + double mvalue; + }; + + class SetDoubleAttrVisitor : public BaseAttributesVisitor { + public: + SetDoubleAttrVisitor(const std::string& name, double value); + virtual void visit(const Attributes& a); + virtual void visit(Attributes& a); + + private: + // data + const std::string& mname; + double mvalue; + }; + + class NamesOfDoubleAttributesVisitor : public BaseAttributesVisitor { + public: + NamesOfDoubleAttributesVisitor(bool excludereadonly); + virtual void visit(const Attributes& a); + const std::set& names() const; + + private: + // data + bool mexcludereadonly; + std::set mnames; + }; }; // class Attributes // non-member helpers DLL_EXPORT void registerBaseDoubleAttribute(Attributes* obj, - const std::string& name, BaseDoubleAttribute* pa); + const std::string& name, + BaseDoubleAttribute* pa); DLL_EXPORT void throwDoubleAttributeReadOnly(); -} // namespace attributes -} // namespace diffpy +} // namespace attributes +} // namespace diffpy // Implementation ------------------------------------------------------------ @@ -202,8 +175,8 @@ DLL_EXPORT void throwDoubleAttributeReadOnly(); // make selected classes visible in diffpy namespace namespace diffpy { - using attributes::Attributes; - using attributes::BaseAttributesVisitor; -} +using attributes::Attributes; +using attributes::BaseAttributesVisitor; +} // namespace diffpy #endif // ATTRIBUTES_HPP_INCLUDED diff --git a/src/diffpy/EventTicker.cpp b/src/diffpy/EventTicker.cpp index c2d30c3e..68f3e708 100644 --- a/src/diffpy/EventTicker.cpp +++ b/src/diffpy/EventTicker.cpp @@ -1,24 +1,24 @@ /***************************************************************************** -* -* 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 EventTicker for managing modification times of dependent objects. -* -* This is used to determine if cached values need update, due to a change -* of their dependencies. The EventTicker class is inspired by the -* ObjCryst::RefinableObjClock class. -* -*****************************************************************************/ + * + * 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 EventTicker for managing modification times of dependent objects. + * + * This is used to determine if cached values need update, due to a change + * of their dependencies. The EventTicker class is inspired by the + * ObjCryst::RefinableObjClock class. + * + *****************************************************************************/ #include #include @@ -32,77 +32,55 @@ namespace eventticker { // Constructor --------------------------------------------------------------- -EventTicker::EventTicker() : mtick(0, 0) -{ } +EventTicker::EventTicker() : mtick(0, 0) {} // Public Methods ------------------------------------------------------------ -void EventTicker::click() -{ - ++gtick.second; - if (0 >= gtick.second) - { - ++gtick.first; - gtick.second = 0; - } - mtick = gtick; +void EventTicker::click() { + ++gtick.second; + if (0 >= gtick.second) { + ++gtick.first; + gtick.second = 0; + } + mtick = gtick; } - -void EventTicker::updateFrom(const EventTicker& other) -{ - if (other > *this) mtick = other.mtick; +void EventTicker::updateFrom(const EventTicker& other) { + if (other > *this) mtick = other.mtick; } +EventTicker::value_type EventTicker::value() const { return mtick; } -EventTicker::value_type EventTicker::value() const -{ - return mtick; +bool EventTicker::operator<(const EventTicker& other) const { + return mtick < other.mtick; } - -bool EventTicker::operator<(const EventTicker& other) const -{ - return mtick < other.mtick; -} - - -bool EventTicker::operator<=(const EventTicker& other) const -{ - return mtick <= other.mtick; +bool EventTicker::operator<=(const EventTicker& other) const { + return mtick <= other.mtick; } - -bool EventTicker::operator>(const EventTicker& other) const -{ - return mtick > other.mtick; +bool EventTicker::operator>(const EventTicker& other) const { + return mtick > other.mtick; } - -bool EventTicker::operator>=(const EventTicker& other) const -{ - return mtick >= other.mtick; +bool EventTicker::operator>=(const EventTicker& other) const { + return mtick >= other.mtick; } - -bool EventTicker::operator==(const EventTicker& other) const -{ - return mtick == other.mtick; +bool EventTicker::operator==(const EventTicker& other) const { + return mtick == other.mtick; } - -bool EventTicker::operator!=(const EventTicker& other) const -{ - return mtick != other.mtick; +bool EventTicker::operator!=(const EventTicker& other) const { + return mtick != other.mtick; } - // Static Global Data -------------------------------------------------------- EventTicker::value_type EventTicker::gtick(0, 0); -} // namespace eventticker -} // namespace diffpy +} // namespace eventticker +} // namespace diffpy // Serialization ------------------------------------------------------------- diff --git a/src/diffpy/EventTicker.hpp b/src/diffpy/EventTicker.hpp index b41cac9d..67d7e15c 100644 --- a/src/diffpy/EventTicker.hpp +++ b/src/diffpy/EventTicker.hpp @@ -1,24 +1,24 @@ /***************************************************************************** -* -* 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 EventTicker for managing modification times of dependent objects. -* -* This is used to determine if cached values need update, due to a change -* of their dependencies. The EventTicker class is inspired by the -* ObjCryst::RefinableObjClock class. -* -*****************************************************************************/ + * + * 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 EventTicker for managing modification times of dependent objects. + * + * This is used to determine if cached values need update, due to a change + * of their dependencies. The EventTicker class is inspired by the + * ObjCryst::RefinableObjClock class. + * + *****************************************************************************/ #ifndef EVENTTICKER_HPP_INCLUDED #define EVENTTICKER_HPP_INCLUDED @@ -31,65 +31,60 @@ namespace diffpy { namespace eventticker { -class DLL_EXPORT EventTicker -{ - public: - - // constructor - EventTicker(); - - // types - typedef std::pair value_type; - - // methods - /// record an event and advance the global ticker. - void click(); - /// advance internal ticker if the other ticker is newer. - void updateFrom(const EventTicker& other); - /// return the internal ticker value - value_type value() const; - - // comparison - bool operator<(const EventTicker&) const; - bool operator<=(const EventTicker&) const; - bool operator>(const EventTicker&) const; - bool operator>=(const EventTicker&) const; - bool operator==(const EventTicker&) const; - bool operator!=(const EventTicker&) const; - - private: - - // global counter - static value_type gtick; - - // data - value_type mtick; - - // serialization - friend class boost::serialization::access; - template - void save(Archive& ar, const unsigned int version) const - { - ar << mtick << gtick; - } - - template - void load(Archive& ar, const unsigned int version) - { - value_type ga; - ar >> mtick >> ga; - if (ga > gtick) - { - if (ga.first != gtick.first) mtick.first = mtick.second = 0; - else mtick.second += gtick.second - ga.second; - } - } - - BOOST_SERIALIZATION_SPLIT_MEMBER() - +class DLL_EXPORT EventTicker { + public: + // constructor + EventTicker(); + + // types + typedef std::pair value_type; + + // methods + /// record an event and advance the global ticker. + void click(); + /// advance internal ticker if the other ticker is newer. + void updateFrom(const EventTicker& other); + /// return the internal ticker value + value_type value() const; + + // comparison + bool operator<(const EventTicker&) const; + bool operator<=(const EventTicker&) const; + bool operator>(const EventTicker&) const; + bool operator>=(const EventTicker&) const; + bool operator==(const EventTicker&) const; + bool operator!=(const EventTicker&) const; + + private: + // global counter + static value_type gtick; + + // data + value_type mtick; + + // serialization + friend class boost::serialization::access; + template + void save(Archive& ar, const unsigned int version) const { + ar << mtick << gtick; + } + + template + void load(Archive& ar, const unsigned int version) { + value_type ga; + ar >> mtick >> ga; + if (ga > gtick) { + if (ga.first != gtick.first) + mtick.first = mtick.second = 0; + else + mtick.second += gtick.second - ga.second; + } + } + + BOOST_SERIALIZATION_SPLIT_MEMBER() }; -} // namespace eventticker -} // namespace diffpy +} // namespace eventticker +} // namespace diffpy #endif // EVENTTICKER_HPP_INCLUDED diff --git a/src/diffpy/Export.hpp b/src/diffpy/Export.hpp index 142b61fb..a998ff22 100644 --- a/src/diffpy/Export.hpp +++ b/src/diffpy/Export.hpp @@ -1,11 +1,11 @@ #pragma once #if defined(_WIN32) - #if defined(BUILD_DLL) - #define DLL_EXPORT __declspec(dllexport) - #else - #define DLL_EXPORT __declspec(dllimport) - #endif +#if defined(BUILD_DLL) +#define DLL_EXPORT __declspec(dllexport) #else - #define DLL_EXPORT +#define DLL_EXPORT __declspec(dllimport) +#endif +#else +#define DLL_EXPORT #endif diff --git a/src/diffpy/HasClassRegistry.hpp b/src/diffpy/HasClassRegistry.hpp index 762e2992..28b6f3d6 100644 --- a/src/diffpy/HasClassRegistry.hpp +++ b/src/diffpy/HasClassRegistry.hpp @@ -1,28 +1,28 @@ /***************************************************************************** -* -* 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.txt for license information. -* -****************************************************************************** -* -* class HasClassRegistry -- template class providing registry for factory -* functions that produce derived classes. To be used as a base for a -* specific class branch. For a proper function, the HasClassRegistry -* template must be instantiated in just one cpp file that includes the -* implementations in HasClassRegistry.ipp. -* -* A Base class with registry should be declared as -* class Base : public HasClassRegistry -* The derived classes need to implement create, clone and type methods. -* -*****************************************************************************/ + * + * 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.txt for license information. + * + ****************************************************************************** + * + * class HasClassRegistry -- template class providing registry for factory + * functions that produce derived classes. To be used as a base for a + * specific class branch. For a proper function, the HasClassRegistry + * template must be instantiated in just one cpp file that includes the + * implementations in HasClassRegistry.ipp. + * + * A Base class with registry should be declared as + * class Base : public HasClassRegistry + * The derived classes need to implement create, clone and type methods. + * + *****************************************************************************/ #ifndef HASCLASSREGISTRY_HPP_INCLUDED #define HASCLASSREGISTRY_HPP_INCLUDED @@ -37,63 +37,59 @@ namespace diffpy { template -class HasClassRegistry -{ - public: +class HasClassRegistry { + public: + // types + typedef std::shared_ptr SharedPtr; - // types - typedef std::shared_ptr SharedPtr; + // Contains virtual methods + virtual ~HasClassRegistry() {} - // Contains virtual methods - virtual ~HasClassRegistry() { } + // methods that have to be implemented in the TBase-derived classes + virtual SharedPtr create() const = 0; + virtual SharedPtr clone() const = 0; + virtual const std::string& type() const = 0; - // methods that have to be implemented in the TBase-derived classes - virtual SharedPtr create() const = 0; - virtual SharedPtr clone() const = 0; - virtual const std::string& type() const = 0; + // methods provided by HasClassRegistry - // methods provided by HasClassRegistry + /// Add a prototype of this instance to the registry. + virtual bool registerThisType() const; - /// Add a prototype of this instance to the registry. - virtual bool registerThisType() const; + /// Make registered type tp available under a different alias. + static bool aliasType(const std::string& tp, const std::string& al); - /// Make registered type tp available under a different alias. - static bool aliasType(const std::string& tp, const std::string& al); + /// Clear registration of the specified type under its standard name + /// or any alias. Return the number of unset names and aliases or + /// 0 if the specified string type was not registered. + static int deregisterType(const std::string& tp); - /// Clear registration of the specified type under its standard name - /// or any alias. Return the number of unset names and aliases or - /// 0 if the specified string type was not registered. - static int deregisterType(const std::string& tp); + /// Create new instance of a specified string type. + static SharedPtr createByType(const std::string& tp); - /// Create new instance of a specified string type. - static SharedPtr createByType(const std::string& tp); + /// Return true if string is a registered string type or its alias. + static bool isRegisteredType(const std::string& tp); - /// Return true if string is a registered string type or its alias. - static bool isRegisteredType(const std::string& tp); + /// Return a map of string aliases to standard type strings. + static std::map getAliasedTypes(); - /// Return a map of string aliases to standard type strings. - static std::map getAliasedTypes(); + /// Return a set of all registered string types + static std::set getRegisteredTypes(); - /// Return a set of all registered string types - static std::set getRegisteredTypes(); + protected: + /// Optional setup for the registered object. + /// + /// This is used for increasing reference count on Python-extended + /// classes, so they are not destroyed before destroying the C++ + /// object registry. + virtual void setupRegisteredObject(SharedPtr p) const {} - protected: + private: + typedef std::map RegistryStorage; - /// Optional setup for the registered object. - /// - /// This is used for increasing reference count on Python-extended - /// classes, so they are not destroyed before destroying the C++ - /// object registry. - virtual void setupRegisteredObject(SharedPtr p) const { } - - private: - - typedef std::map RegistryStorage; - - /// Return a singleton instance of internal registry - static RegistryStorage& getRegistry(); + /// Return a singleton instance of internal registry + static RegistryStorage& getRegistry(); }; -} // namespace diffpy +} // namespace diffpy #endif // HASCLASSREGISTRY_HPP_INCLUDED diff --git a/src/diffpy/mathutils.hpp b/src/diffpy/mathutils.hpp index bce53a2a..256be8cb 100644 --- a/src/diffpy/mathutils.hpp +++ b/src/diffpy/mathutils.hpp @@ -1,20 +1,20 @@ /***************************************************************************** -* -* 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.txt for license information. -* -****************************************************************************** -* -* Various common mathematical constants and functions. -* -*****************************************************************************/ + * + * 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.txt for license information. + * + ****************************************************************************** + * + * Various common mathematical constants and functions. + * + *****************************************************************************/ #ifndef MATHUTILS_HPP_INCLUDED #define MATHUTILS_HPP_INCLUDED @@ -43,68 +43,56 @@ double asind(double x); // round-off aware comparison operations -bool eps_eq(const double& x, const double& y, double eps=SQRT_DOUBLE_EPS); -bool eps_gt(const double& x, const double& y, double eps=SQRT_DOUBLE_EPS); -bool eps_lt(const double& x, const double& y, double eps=SQRT_DOUBLE_EPS); +bool eps_eq(const double& x, const double& y, double eps = SQRT_DOUBLE_EPS); +bool eps_gt(const double& x, const double& y, double eps = SQRT_DOUBLE_EPS); +bool eps_lt(const double& x, const double& y, double eps = SQRT_DOUBLE_EPS); // binary functor for round-off aware less-than comparison -class EpsilonLess -{ - public: - - // constructor - EpsilonLess(double eps=SQRT_DOUBLE_EPS) : meps(eps) { } - - // comparison method - bool operator()(const double& x, const double& y) const - { - return eps_lt(x, y, meps); - } - template - bool operator()(const Seq0& v0, const Seq1& v1) const - { - bool rv = std::lexicographical_compare( - v0.begin(), v0.end(), - v1.begin(), v1.end(), *this); - return rv; - } - - private: - - double meps; +class EpsilonLess { + public: + // constructor + EpsilonLess(double eps = SQRT_DOUBLE_EPS) : meps(eps) {} + + // comparison method + bool operator()(const double& x, const double& y) const { + return eps_lt(x, y, meps); + } + template + bool operator()(const Seq0& v0, const Seq1& v1) const { + bool rv = std::lexicographical_compare(v0.begin(), v0.end(), v1.begin(), + v1.end(), *this); + return rv; + } + + private: + double meps; }; // binary functor for round-off aware equality comparison -class EpsilonEqual -{ - public: - - // constructor - EpsilonEqual(double eps=SQRT_DOUBLE_EPS) : meps(eps) { } - - // comparison method - bool operator()(const double& x, const double& y) const - { - return eps_eq(x, y, meps); - } - template - bool operator()(const Seq0& v0, const Seq1& v1) const - { - bool rv = (v0.size() == v1.size()) && - std::equal(v0.begin(), v0.end(), v1.begin(), *this); - return rv; - } - - private: - - double meps; +class EpsilonEqual { + public: + // constructor + EpsilonEqual(double eps = SQRT_DOUBLE_EPS) : meps(eps) {} + + // comparison method + bool operator()(const double& x, const double& y) const { + return eps_eq(x, y, meps); + } + template + bool operator()(const Seq0& v0, const Seq1& v1) const { + bool rv = (v0.size() == v1.size()) && + std::equal(v0.begin(), v0.end(), v1.begin(), *this); + return rv; + } + + private: + double meps; }; - -} // namespace mathutils -} // namespace diffpy +} // namespace mathutils +} // namespace diffpy // Implementation ------------------------------------------------------------ diff --git a/src/diffpy/runtimepath.cpp b/src/diffpy/runtimepath.cpp index 05551408..cde1d5c9 100644 --- a/src/diffpy/runtimepath.cpp +++ b/src/diffpy/runtimepath.cpp @@ -1,20 +1,20 @@ /***************************************************************************** -* -* 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. -* -****************************************************************************** -* -* Functions for resolving paths to static data files at runtime. -* -*****************************************************************************/ + * + * 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. + * + ****************************************************************************** + * + * Functions for resolving paths to static data files at runtime. + * + *****************************************************************************/ #include #include @@ -48,175 +48,138 @@ namespace fs = std::filesystem; const char* runtimerelpath = #ifdef DIFFPYRUNTIMERELPATH - STRINGIFY(DIFFPYRUNTIMERELPATH) + STRINGIFY(DIFFPYRUNTIMERELPATH) #else - "../share/diffpy/libdiffpy-" - STRINGIFY(DIFFPY_VERSION_MAJOR) - "." - STRINGIFY(DIFFPY_VERSION_MINOR) + "../share/diffpy/libdiffpy-" STRINGIFY(DIFFPY_VERSION_MAJOR) "." STRINGIFY( + DIFFPY_VERSION_MINOR) #endif -; + ; // Locate the module containing this code, including an embedding extension. -fs::path modulepath() -{ - static const char anchor = 0; +fs::path modulepath() { + static const char anchor = 0; #ifdef _WIN32 - HMODULE module = nullptr; - if (!GetModuleHandleExW( - GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS | - GET_MODULE_HANDLE_EX_FLAG_UNCHANGED_REFCOUNT, - reinterpret_cast(&anchor), &module)) - { - throw system_error(GetLastError(), system_category(), - "Cannot locate the libdiffpy module"); + HMODULE module = nullptr; + if (!GetModuleHandleExW(GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS | + GET_MODULE_HANDLE_EX_FLAG_UNCHANGED_REFCOUNT, + reinterpret_cast(&anchor), &module)) { + throw system_error(GetLastError(), system_category(), + "Cannot locate the libdiffpy module"); + } + vector buffer(256); + for (;;) { + DWORD size = GetModuleFileNameW(module, buffer.data(), + static_cast(buffer.size())); + if (size == 0) { + throw system_error(GetLastError(), system_category(), + "Cannot resolve the libdiffpy module path"); } - vector buffer(256); - for (;;) - { - DWORD size = GetModuleFileNameW(module, buffer.data(), - static_cast(buffer.size())); - if (size == 0) - { - throw system_error(GetLastError(), system_category(), - "Cannot resolve the libdiffpy module path"); - } - if (size < buffer.size()) - { - return fs::path(wstring(buffer.data(), size)); - } - buffer.resize(2 * buffer.size()); + if (size < buffer.size()) { + return fs::path(wstring(buffer.data(), size)); } + buffer.resize(2 * buffer.size()); + } #else - Dl_info info = {}; - if (!dladdr(&anchor, &info) || !info.dli_fname) - { - throw runtime_error("Cannot resolve the libdiffpy module path"); - } - return fs::path(info.dli_fname); + Dl_info info = {}; + if (!dladdr(&anchor, &info) || !info.dli_fname) { + throw runtime_error("Cannot resolve the libdiffpy module path"); + } + return fs::path(info.dli_fname); #endif } - // Validate an explicit directory without falling back to packaged data. -string canonicalDirectory(const fs::path& directory) -{ - if (!fs::is_directory(directory)) - { - throw runtime_error("Directory '" + directory.string() + - "' does not exist."); - } - return fs::canonical(directory).generic_string(); +string canonicalDirectory(const fs::path& directory) { + if (!fs::is_directory(directory)) { + throw runtime_error("Directory '" + directory.string() + + "' does not exist."); + } + return fs::canonical(directory).generic_string(); } - -string diffpyruntime() -{ - // check the DIFFPYRUNTIME environment variable. - const char* pe = getenv("DIFFPYRUNTIME"); - if (pe && *pe != '\0') - { - return canonicalDirectory(pe); - } - static const fs::path module = fs::absolute(modulepath()); - // first candidate is resolved in relative data path - const fs::path d1 = module.parent_path() / runtimerelpath; - if (fs::is_directory(d1)) - { - return fs::canonical(d1).generic_string(); - } - // second candidate is resolved with respect to physical library location - // according to the source tree layout - const fs::path d2 = fs::canonical(module).parent_path() / - "../../src/runtime"; - if (fs::is_directory(d2)) - { - return fs::canonical(d2).generic_string(); - } - // nothing worked - throw exception about the first candidate path. - return canonicalDirectory(d1); +string diffpyruntime() { + // check the DIFFPYRUNTIME environment variable. + const char* pe = getenv("DIFFPYRUNTIME"); + if (pe && *pe != '\0') { + return canonicalDirectory(pe); + } + static const fs::path module = fs::absolute(modulepath()); + // first candidate is resolved in relative data path + const fs::path d1 = module.parent_path() / runtimerelpath; + if (fs::is_directory(d1)) { + return fs::canonical(d1).generic_string(); + } + // second candidate is resolved with respect to physical library location + // according to the source tree layout + const fs::path d2 = fs::canonical(module).parent_path() / "../../src/runtime"; + if (fs::is_directory(d2)) { + return fs::canonical(d2).generic_string(); + } + // nothing worked - throw exception about the first candidate path. + return canonicalDirectory(d1); } -} // namespace +} // namespace // Implementation ------------------------------------------------------------ namespace diffpy { namespace runtimepath { -string datapath(const std::string& f) -{ - string rv = diffpyruntime(); - rv += (f.empty() ? "" : "/") + f; - return rv; +string datapath(const std::string& f) { + string rv = diffpyruntime(); + rv += (f.empty() ? "" : "/") + f; + return rv; } // class LineReader ---------------------------------------------------------- // Constructor -LineReader::LineReader() : - lineno(0), - commentmark("#") -{ } +LineReader::LineReader() : lineno(0), commentmark("#") {} // Methods -bool LineReader::isignored() const -{ - return words.empty() || this->iscomment(); +bool LineReader::isignored() const { + return words.empty() || this->iscomment(); } - -bool LineReader::iscomment() const -{ - bool rv = !commentmark.empty() && !words.empty() && +bool LineReader::iscomment() const { + bool rv = !commentmark.empty() && !words.empty() && (0 == words[0].compare(0, commentmark.size(), commentmark)); - return rv; + return rv; } +bool LineReader::isblank() const { return words.empty(); } -bool LineReader::isblank() const -{ - return words.empty(); -} - +size_t LineReader::wcount() const { return words.size(); } -size_t LineReader::wcount() const -{ - return words.size(); -} - - -runtime_error LineReader::format_error( - const string& filename, const string& edetail) -{ - ostringstream emsg; - emsg << "Invalid data format in " << - filename << " line " << this->lineno << '.'; - if (!edetail.empty()) emsg << '\n' << edetail; - return runtime_error(emsg.str()); +runtime_error LineReader::format_error(const string& filename, + const string& edetail) { + ostringstream emsg; + emsg << "Invalid data format in " << filename << " line " << this->lineno + << '.'; + if (!edetail.empty()) emsg << '\n' << edetail; + return runtime_error(emsg.str()); } // Non-member operators -istream& operator>> (istream& fp, LineReader& line) -{ - getline(fp, line.line); - if (fp) line.lineno++; - istringstream fpline(line.line); - line.words.clear(); - string w; - if (line.separator.empty()) { - while (fpline >> w) line.words.push_back(w); - } - else { - assert(line.separator.size() == 1); - const char& sep = line.separator[0]; - while (getline(fpline, w, sep)) line.words.push_back(w); - } - return fp; +istream& operator>>(istream& fp, LineReader& line) { + getline(fp, line.line); + if (fp) line.lineno++; + istringstream fpline(line.line); + line.words.clear(); + string w; + if (line.separator.empty()) { + while (fpline >> w) line.words.push_back(w); + } else { + assert(line.separator.size() == 1); + const char& sep = line.separator[0]; + while (getline(fpline, w, sep)) line.words.push_back(w); + } + return fp; } -} // namespace runtimepath -} // namespace diffpy +} // namespace runtimepath +} // namespace diffpy diff --git a/src/diffpy/runtimepath.hpp b/src/diffpy/runtimepath.hpp index 587516c7..e7eb4562 100644 --- a/src/diffpy/runtimepath.hpp +++ b/src/diffpy/runtimepath.hpp @@ -1,20 +1,20 @@ /***************************************************************************** -* -* 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. -* -****************************************************************************** -* -* Functions for resolving paths to static data files at runtime. -* -*****************************************************************************/ + * + * 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. + * + ****************************************************************************** + * + * Functions for resolving paths to static data files at runtime. + * + *****************************************************************************/ #ifndef RUNTIMEPATH_HPP_INCLUDED #define RUNTIMEPATH_HPP_INCLUDED @@ -42,32 +42,31 @@ namespace runtimepath { std::string datapath(const std::string& f); /// Helper class for loading text data -class LineReader -{ - public: - // constructor - LineReader(); +class LineReader { + public: + // constructor + LineReader(); - // methods - bool isignored() const; - bool iscomment() const; - bool isblank() const; - size_t wcount() const; - std::runtime_error format_error( - const std::string& filename, const std::string& edetail); + // methods + bool isignored() const; + bool iscomment() const; + bool isblank() const; + size_t wcount() const; + std::runtime_error format_error(const std::string& filename, + const std::string& edetail); - // data - int lineno; - std::string commentmark; - std::string separator; - std::string line; - std::vector words; + // data + int lineno; + std::string commentmark; + std::string separator; + std::string line; + std::vector words; }; // non-member functions for the LineReader class -std::istream& operator>> (std::istream&, LineReader&); +std::istream& operator>>(std::istream&, LineReader&); -} // namespace runtimepath -} // namespace diffpy +} // namespace runtimepath +} // namespace diffpy #endif // RUNTIMEPATH_HPP_INCLUDED diff --git a/src/diffpy/serialization.hpp b/src/diffpy/serialization.hpp index f1e91f27..d8f26f82 100644 --- a/src/diffpy/serialization.hpp +++ b/src/diffpy/serialization.hpp @@ -1,26 +1,26 @@ /***************************************************************************** -* -* 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.txt for license information. -* -****************************************************************************** -* -* Declaration of template functions: -* diffpy::serialization_tostring -* diffpy::serialization_fromstring -* -* Shared definitions of serialization archive types: -* diffpy::serialization::iarchive -* diffpy::serialization::oarchive -* -*****************************************************************************/ + * + * 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.txt for license information. + * + ****************************************************************************** + * + * Declaration of template functions: + * diffpy::serialization_tostring + * diffpy::serialization_fromstring + * + * Shared definitions of serialization archive types: + * diffpy::serialization::iarchive + * diffpy::serialization::oarchive + * + *****************************************************************************/ #ifndef SERIALIZATION_HPP_INCLUDED #define SERIALIZATION_HPP_INCLUDED @@ -42,8 +42,8 @@ namespace serialization { typedef ::boost::archive::binary_iarchive iarchive; typedef ::boost::archive::binary_oarchive oarchive; -} // namespace serialization -} // namespace diffpy +} // namespace serialization +} // namespace diffpy // Pull in the inline definitions and macro logic #include "serialization.ipp" diff --git a/src/diffpy/srreal/AtomRadiiTable.cpp b/src/diffpy/srreal/AtomRadiiTable.cpp index 85b8abeb..bd9d6dfd 100644 --- a/src/diffpy/srreal/AtomRadiiTable.cpp +++ b/src/diffpy/srreal/AtomRadiiTable.cpp @@ -1,20 +1,20 @@ /***************************************************************************** -* -* 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.txt for license information. -* -****************************************************************************** -* -* class AtomRadiiTable -- storage of empirical atomic radii -* -*****************************************************************************/ + * + * 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.txt for license information. + * + ****************************************************************************** + * + * class AtomRadiiTable -- storage of empirical atomic radii + * + *****************************************************************************/ #include #include @@ -37,97 +37,75 @@ using namespace std; // Public Methods ------------------------------------------------------------ -double AtomRadiiTable::lookup(const string& smbl) const -{ - CustomRadiiStorage::const_iterator tb; - tb = mcustomradius.find(smbl); - if (tb != mcustomradius.end()) return tb->second; - return this->standardLookup(smbl); +double AtomRadiiTable::lookup(const string& smbl) const { + CustomRadiiStorage::const_iterator tb; + tb = mcustomradius.find(smbl); + if (tb != mcustomradius.end()) return tb->second; + return this->standardLookup(smbl); } - -void AtomRadiiTable::setCustom(const string& smbl, double radius) -{ - mcustomradius[smbl] = radius; +void AtomRadiiTable::setCustom(const string& smbl, double radius) { + mcustomradius[smbl] = radius; } - -void AtomRadiiTable::fromString(const string& s) -{ - // create deblanked string - string s1; - remove_copy_if(s.begin(), s.end(), back_inserter(s1), ::isspace); - // replace commas with space so we can use the split function - replace(s1.begin(), s1.end(), ',', ' '); - istringstream ss1(s1); - CustomRadiiStorage rds; - for (string w; ss1 >> w;) - { - string::size_type p = w.find(':'); - if (p == string::npos) - { - ostringstream emsg; - emsg << "Invalid radius specification, missing ':' in '" << - w << "'."; - throw invalid_argument(emsg.str()); - } - string smbl = w.substr(0, p); - double value; - istringstream ssv(w.substr(p + 1)); - if (!(ssv >> value)) - { - ostringstream emsg; - emsg << "Invalid floating point number in '" << w << "'."; - throw invalid_argument(emsg.str()); - } - rds[smbl] = value; +void AtomRadiiTable::fromString(const string& s) { + // create deblanked string + string s1; + remove_copy_if(s.begin(), s.end(), back_inserter(s1), ::isspace); + // replace commas with space so we can use the split function + replace(s1.begin(), s1.end(), ',', ' '); + istringstream ss1(s1); + CustomRadiiStorage rds; + for (string w; ss1 >> w;) { + string::size_type p = w.find(':'); + if (p == string::npos) { + ostringstream emsg; + emsg << "Invalid radius specification, missing ':' in '" << w << "'."; + throw invalid_argument(emsg.str()); } - // everything worked up to here, we can do the assignment - CustomRadiiStorage::const_iterator kv = rds.begin(); - for (; kv != rds.end(); ++kv) mcustomradius[kv->first] = kv->second; -} - - -void AtomRadiiTable::resetCustom(const string& smbl) -{ - mcustomradius.erase(smbl); + string smbl = w.substr(0, p); + double value; + istringstream ssv(w.substr(p + 1)); + if (!(ssv >> value)) { + ostringstream emsg; + emsg << "Invalid floating point number in '" << w << "'."; + throw invalid_argument(emsg.str()); + } + rds[smbl] = value; + } + // everything worked up to here, we can do the assignment + CustomRadiiStorage::const_iterator kv = rds.begin(); + for (; kv != rds.end(); ++kv) mcustomradius[kv->first] = kv->second; } - -void AtomRadiiTable::resetAll() -{ - mcustomradius.clear(); +void AtomRadiiTable::resetCustom(const string& smbl) { + mcustomradius.erase(smbl); } +void AtomRadiiTable::resetAll() { mcustomradius.clear(); } -const AtomRadiiTable::CustomRadiiStorage& -AtomRadiiTable::getAllCustom() const -{ - return mcustomradius; +const AtomRadiiTable::CustomRadiiStorage& AtomRadiiTable::getAllCustom() const { + return mcustomradius; } - -string AtomRadiiTable::toString(string separator) const -{ - CustomRadiiStorage::const_iterator tb; - vector symbols; - for (tb = mcustomradius.begin(); tb != mcustomradius.end(); ++tb) - { - symbols.push_back(tb->first); - } - sort(symbols.begin(), symbols.end()); - ostringstream rv; - vector::const_iterator smbi; - for (smbi = symbols.begin(); smbi != symbols.end(); ++smbi) - { - if (smbi != symbols.begin()) rv << separator; - rv << *smbi << ':' << mcustomradius.at(*smbi); - } - return rv.str(); +string AtomRadiiTable::toString(string separator) const { + CustomRadiiStorage::const_iterator tb; + vector symbols; + for (tb = mcustomradius.begin(); tb != mcustomradius.end(); ++tb) { + symbols.push_back(tb->first); + } + sort(symbols.begin(), symbols.end()); + ostringstream rv; + vector::const_iterator smbi; + for (smbi = symbols.begin(); smbi != symbols.end(); ++smbi) { + if (smbi != symbols.begin()) rv << separator; + rv << *smbi << ':' << mcustomradius.at(*smbi); + } + return rv.str(); } -} // srreal -} // diffpy +} // namespace srreal +} // namespace diffpy // Serialization ------------------------------------------------------------- diff --git a/src/diffpy/srreal/AtomRadiiTable.hpp b/src/diffpy/srreal/AtomRadiiTable.hpp index c3674352..8cf54f2f 100644 --- a/src/diffpy/srreal/AtomRadiiTable.hpp +++ b/src/diffpy/srreal/AtomRadiiTable.hpp @@ -1,24 +1,24 @@ /***************************************************************************** -* -* 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.txt for license information. -* -****************************************************************************** -* -* class AtomRadiiTable -- storage of empirical atomic radii -* This is an abstract base class that provides a createByType factory -* function for creating a registered concrete instances. -* The derived classes has to overload the standardLookup method and also -* create, clone and type methods from the HasClassRegistry base. -* -*****************************************************************************/ + * + * 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.txt for license information. + * + ****************************************************************************** + * + * class AtomRadiiTable -- storage of empirical atomic radii + * This is an abstract base class that provides a createByType factory + * function for creating a registered concrete instances. + * The derived classes has to overload the standardLookup method and also + * create, clone and type methods from the HasClassRegistry base. + * + *****************************************************************************/ #ifndef ATOMRADIITABLE_HPP_INCLUDED #define ATOMRADIITABLE_HPP_INCLUDED @@ -37,51 +37,46 @@ namespace diffpy { namespace srreal { -class DLL_EXPORT AtomRadiiTable : - public diffpy::HasClassRegistry -{ - public: - - // types - typedef std::unordered_map CustomRadiiStorage; - - // methods - /// fast value lookup, which does not change the table. - double lookup(const std::string& smbl) const; - /// overloadable lookup function that retrieved standard values - virtual double standardLookup(const std::string& smbl) const = 0; - /// set custom radius for a specified atom symbol - void setCustom(const std::string& smbl, double radius); - /// set custom radii from a string in (A1:r1, A2:r2, ...) format - void fromString(const std::string& s); - /// reset custom value for the specified atom type - void resetCustom(const std::string& smbl); - /// reset all custom values - void resetAll(); - /// return all custom radii defined in this table - const CustomRadiiStorage& getAllCustom() const; - /// convert all custom radii to a string in (A1:r1,A2:r2,...) format - std::string toString(std::string separator=",") const; - - private: - - // data - CustomRadiiStorage mcustomradius; - - // serialization - friend class boost::serialization::access; - template - void serialize(Archive& ar, const unsigned int version) - { - ar & mcustomradius; - } - +class DLL_EXPORT AtomRadiiTable + : public diffpy::HasClassRegistry { + public: + // types + typedef std::unordered_map CustomRadiiStorage; + + // methods + /// fast value lookup, which does not change the table. + double lookup(const std::string& smbl) const; + /// overloadable lookup function that retrieved standard values + virtual double standardLookup(const std::string& smbl) const = 0; + /// set custom radius for a specified atom symbol + void setCustom(const std::string& smbl, double radius); + /// set custom radii from a string in (A1:r1, A2:r2, ...) format + void fromString(const std::string& s); + /// reset custom value for the specified atom type + void resetCustom(const std::string& smbl); + /// reset all custom values + void resetAll(); + /// return all custom radii defined in this table + const CustomRadiiStorage& getAllCustom() const; + /// convert all custom radii to a string in (A1:r1,A2:r2,...) format + std::string toString(std::string separator = ",") const; + + private: + // data + CustomRadiiStorage mcustomradius; + + // serialization + friend class boost::serialization::access; + template + void serialize(Archive& ar, const unsigned int version) { + ar & mcustomradius; + } }; typedef AtomRadiiTable::SharedPtr AtomRadiiTablePtr; -} // namespace srreal -} // namespace diffpy +} // namespace srreal +} // namespace diffpy // Serialization ------------------------------------------------------------- diff --git a/src/diffpy/srreal/AtomUtils.cpp b/src/diffpy/srreal/AtomUtils.cpp index bc39cbed..7b0b4b9e 100644 --- a/src/diffpy/srreal/AtomUtils.cpp +++ b/src/diffpy/srreal/AtomUtils.cpp @@ -1,72 +1,64 @@ /***************************************************************************** -* -* 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. -* -****************************************************************************** -* -* Small routines related to atom properties. -* -*****************************************************************************/ + * + * 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. + * + ****************************************************************************** + * + * Small routines related to atom properties. + * + *****************************************************************************/ #include namespace diffpy { namespace srreal { -std::string atomBareSymbol(const std::string& atomtype) -{ - std::string::size_type pb, pe; - pb = atomtype.find_first_not_of("0123456789- \t"); - pe = atomtype.find_last_not_of("+-012345678 \t"); - std::string::size_type npos = std::string::npos; - std::string rv; - if (pb != npos && pe != npos) rv = atomtype.substr(pb, pe - pb + 1); - return rv; +std::string atomBareSymbol(const std::string& atomtype) { + std::string::size_type pb, pe; + pb = atomtype.find_first_not_of("0123456789- \t"); + pe = atomtype.find_last_not_of("+-012345678 \t"); + std::string::size_type npos = std::string::npos; + std::string rv; + if (pb != npos && pe != npos) rv = atomtype.substr(pb, pe - pb + 1); + return rv; } - /// Return valence of possibly ionic symbol such as "S2-" or "Cl-". -int atomValence(const std::string& atomtype) -{ - std::string::const_reverse_iterator ci; - int rv = 0; - for (ci = atomtype.rbegin(); ci != atomtype.rend(); ++ci) - { - if (isspace(*ci)) continue; - // read trailing sign - if (rv == 0) - { - if (*ci == '+') - { - rv = 1; - continue; - } - if (*ci == '-') - { - rv = -1; - continue; - } - break; - } - // allow one [1-8] digit before the +/- sign - if (rv && '0' <= *ci && *ci <= '8') - { - rv *= (*ci - '0'); - } - break; +int atomValence(const std::string& atomtype) { + std::string::const_reverse_iterator ci; + int rv = 0; + for (ci = atomtype.rbegin(); ci != atomtype.rend(); ++ci) { + if (isspace(*ci)) continue; + // read trailing sign + if (rv == 0) { + if (*ci == '+') { + rv = 1; + continue; + } + if (*ci == '-') { + rv = -1; + continue; + } + break; + } + // allow one [1-8] digit before the +/- sign + if (rv && '0' <= *ci && *ci <= '8') { + rv *= (*ci - '0'); } - return rv; + break; + } + return rv; } -} // namespace srreal -} // namespace diffpy +} // namespace srreal +} // namespace diffpy // End of file diff --git a/src/diffpy/srreal/AtomUtils.hpp b/src/diffpy/srreal/AtomUtils.hpp index 346d7c97..489ffedf 100644 --- a/src/diffpy/srreal/AtomUtils.hpp +++ b/src/diffpy/srreal/AtomUtils.hpp @@ -1,20 +1,20 @@ /***************************************************************************** -* -* 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. -* -****************************************************************************** -* -* Small routines related to atom properties. -* -*****************************************************************************/ + * + * 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. + * + ****************************************************************************** + * + * Small routines related to atom properties. + * + *****************************************************************************/ #ifndef ATOMUTILS_HPP_INCLUDED #define ATOMUTILS_HPP_INCLUDED @@ -31,7 +31,7 @@ std::string atomBareSymbol(const std::string& atomtype); /// Return valence of possibly ionic symbol such as "S2-" or "Cl-". int atomValence(const std::string& atomtype); -} // namespace srreal -} // namespace diffpy +} // namespace srreal +} // namespace diffpy #endif // ATOMUTILS_HPP_INCLUDED diff --git a/src/diffpy/srreal/AtomicStructureAdapter.cpp b/src/diffpy/srreal/AtomicStructureAdapter.cpp index 6d432820..dd3d09e2 100644 --- a/src/diffpy/srreal/AtomicStructureAdapter.cpp +++ b/src/diffpy/srreal/AtomicStructureAdapter.cpp @@ -1,21 +1,21 @@ /***************************************************************************** -* -* libdiffpy by DANSE Diffraction group -* Simon J. L. Billinge -* (c) 2012 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.txt for license information. -* -****************************************************************************** -* -* class AtomicStructureAdapter -- universal structure adapter for -* a non-periodic set of atoms. -* -*****************************************************************************/ + * + * libdiffpy by DANSE Diffraction group + * Simon J. L. Billinge + * (c) 2012 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.txt for license information. + * + ****************************************************************************** + * + * class AtomicStructureAdapter -- universal structure adapter for + * a non-periodic set of atoms. + * + *****************************************************************************/ #include #include @@ -34,62 +34,48 @@ namespace srreal { // class Atom ////////////////////////////////////////////////////////////////////////////// -bool operator< (const Atom& a0, const Atom& a1) -{ - if (a0.atomtype < a1.atomtype) return true; - if (a0.atomtype > a1.atomtype) return false; - R3::Vector::const_iterator p0 = a0.xyz_cartn.begin(); - R3::Vector::const_iterator p1 = a1.xyz_cartn.begin(); - for (; p0 != a0.xyz_cartn.end(); ++p0, ++p1) - { - if (*p0 < *p1) return true; - if (*p0 > *p1) return false; - } - if (a0.occupancy < a1.occupancy) return true; - if (a0.occupancy > a1.occupancy) return false; - if (a0.anisotropy < a1.anisotropy) return true; - if (a0.anisotropy > a1.anisotropy) return false; - typedef R3::Matrix::array_type::const_iterator CIter; - CIter u0 = a0.uij_cartn.data().begin(); - CIter u0last = a0.uij_cartn.data().end(); - CIter u1 = a1.uij_cartn.data().begin(); - for (; u0 != u0last; ++u0, ++u1) - { - if (*u0 < *u1) return true; - if (*u0 > *u1) return false; - } - return false; -} - - -bool operator==(const Atom& a0, const Atom& a1) -{ - bool rv = (&a0 == &a1) || ( - a0.atomtype == a1.atomtype && - (a0.xyz_cartn == a1.xyz_cartn) && - a0.occupancy == a1.occupancy && - a0.anisotropy == a1.anisotropy && - a0.uij_cartn == a1.uij_cartn - ); - return rv; -} - - -bool operator!=(const Atom& a0, const Atom& a1) -{ - return !(a0 == a1); -} - - -size_t hash_value(const Atom& a) -{ - size_t seed = 0; - boost::hash_combine(seed, a.atomtype); - boost::hash_combine(seed, a.xyz_cartn); - boost::hash_combine(seed, a.occupancy); - boost::hash_combine(seed, a.anisotropy); - boost::hash_combine(seed, a.uij_cartn); - return seed; +bool operator<(const Atom& a0, const Atom& a1) { + if (a0.atomtype < a1.atomtype) return true; + if (a0.atomtype > a1.atomtype) return false; + R3::Vector::const_iterator p0 = a0.xyz_cartn.begin(); + R3::Vector::const_iterator p1 = a1.xyz_cartn.begin(); + for (; p0 != a0.xyz_cartn.end(); ++p0, ++p1) { + if (*p0 < *p1) return true; + if (*p0 > *p1) return false; + } + if (a0.occupancy < a1.occupancy) return true; + if (a0.occupancy > a1.occupancy) return false; + if (a0.anisotropy < a1.anisotropy) return true; + if (a0.anisotropy > a1.anisotropy) return false; + typedef R3::Matrix::array_type::const_iterator CIter; + CIter u0 = a0.uij_cartn.data().begin(); + CIter u0last = a0.uij_cartn.data().end(); + CIter u1 = a1.uij_cartn.data().begin(); + for (; u0 != u0last; ++u0, ++u1) { + if (*u0 < *u1) return true; + if (*u0 > *u1) return false; + } + return false; +} + +bool operator==(const Atom& a0, const Atom& a1) { + bool rv = (&a0 == &a1) || + (a0.atomtype == a1.atomtype && (a0.xyz_cartn == a1.xyz_cartn) && + a0.occupancy == a1.occupancy && a0.anisotropy == a1.anisotropy && + a0.uij_cartn == a1.uij_cartn); + return rv; +} + +bool operator!=(const Atom& a0, const Atom& a1) { return !(a0 == a1); } + +size_t hash_value(const Atom& a) { + size_t seed = 0; + boost::hash_combine(seed, a.atomtype); + boost::hash_combine(seed, a.xyz_cartn); + boost::hash_combine(seed, a.occupancy); + boost::hash_combine(seed, a.anisotropy); + boost::hash_combine(seed, a.uij_cartn); + return seed; } ////////////////////////////////////////////////////////////////////////////// @@ -98,58 +84,41 @@ size_t hash_value(const Atom& a) // Public Methods ------------------------------------------------------------ -StructureAdapterPtr AtomicStructureAdapter::clone() const -{ - StructureAdapterPtr rv(new AtomicStructureAdapter(*this)); - return rv; +StructureAdapterPtr AtomicStructureAdapter::clone() const { + StructureAdapterPtr rv(new AtomicStructureAdapter(*this)); + return rv; } - -BaseBondGeneratorPtr AtomicStructureAdapter::createBondGenerator() const -{ - BaseBondGeneratorPtr bnds(new BaseBondGenerator(shared_from_this())); - return bnds; +BaseBondGeneratorPtr AtomicStructureAdapter::createBondGenerator() const { + BaseBondGeneratorPtr bnds(new BaseBondGenerator(shared_from_this())); + return bnds; } +int AtomicStructureAdapter::countSites() const { return matoms.size(); } -int AtomicStructureAdapter::countSites() const -{ - return matoms.size(); +const string& AtomicStructureAdapter::siteAtomType(int idx) const { + assert(0 <= idx && idx < this->countSites()); + return matoms[idx].atomtype; } - -const string& AtomicStructureAdapter::siteAtomType(int idx) const -{ - assert(0 <= idx && idx < this->countSites()); - return matoms[idx].atomtype; +const R3::Vector& AtomicStructureAdapter::siteCartesianPosition(int idx) const { + assert(0 <= idx && idx < this->countSites()); + return matoms[idx].xyz_cartn; } - -const R3::Vector& AtomicStructureAdapter::siteCartesianPosition(int idx) const -{ - assert(0 <= idx && idx < this->countSites()); - return matoms[idx].xyz_cartn; +double AtomicStructureAdapter::siteOccupancy(int idx) const { + assert(0 <= idx && idx < this->countSites()); + return matoms[idx].occupancy; } - -double AtomicStructureAdapter::siteOccupancy(int idx) const -{ - assert(0 <= idx && idx < this->countSites()); - return matoms[idx].occupancy; +bool AtomicStructureAdapter::siteAnisotropy(int idx) const { + assert(0 <= idx && idx < this->countSites()); + return matoms[idx].anisotropy; } - -bool AtomicStructureAdapter::siteAnisotropy(int idx) const -{ - assert(0 <= idx && idx < this->countSites()); - return matoms[idx].anisotropy; -} - - -const R3::Matrix& AtomicStructureAdapter::siteCartesianUij(int idx) const -{ - assert(0 <= idx && idx < this->countSites()); - return matoms[idx].uij_cartn; +const R3::Matrix& AtomicStructureAdapter::siteCartesianUij(int idx) const { + assert(0 <= idx && idx < this->countSites()); + return matoms[idx].uij_cartn; } // helper for diff @@ -157,150 +126,124 @@ namespace { typedef std::pair atomindex; -bool cmpatomindex(const atomindex& ai0, const atomindex& ai1) -{ - return (*(ai0.first) < *(ai1.first)); -} - -} // namespace - -StructureDifference -AtomicStructureAdapter::diff(StructureAdapterConstPtr other) const -{ - using std::min; - StructureDifference sd = this->StructureAdapter::diff(other); - if (sd.stru0 == sd.stru1) return sd; - typedef std::shared_ptr APtr; - APtr pother = std::dynamic_pointer_cast(other); - if (!pother) return sd; - // try fast side-by-side comparison - sd.diffmethod = StructureDifference::Method::SIDEBYSIDE; - const AtomicStructureAdapter& astru0 = *this; - const AtomicStructureAdapter& astru1 = *pother; - sd.pop0.clear(); - sd.add1.clear(); - const_iterator ai0 = astru0.matoms.begin(); - const_iterator ai1 = astru1.matoms.begin(); - int nboth = min(astru0.countSites(), astru1.countSites()); - for (int i = 0; i < nboth; ++i, ++ai0, ++ai1) - { - if (*ai0 != *ai1) - { - sd.pop0.push_back(i); - sd.add1.push_back(i); - } - } - for (int i = nboth; ai0 != astru0.matoms.end(); ++i, ++ai0) - { - sd.pop0.push_back(i); - } - for (int i = nboth; ai1 != astru1.matoms.end(); ++i, ++ai1) - { - sd.add1.push_back(i); +bool cmpatomindex(const atomindex& ai0, const atomindex& ai1) { + return (*(ai0.first) < *(ai1.first)); +} + +} // namespace + +StructureDifference AtomicStructureAdapter::diff( + StructureAdapterConstPtr other) const { + using std::min; + StructureDifference sd = this->StructureAdapter::diff(other); + if (sd.stru0 == sd.stru1) return sd; + typedef std::shared_ptr APtr; + APtr pother = std::dynamic_pointer_cast(other); + if (!pother) return sd; + // try fast side-by-side comparison + sd.diffmethod = StructureDifference::Method::SIDEBYSIDE; + const AtomicStructureAdapter& astru0 = *this; + const AtomicStructureAdapter& astru1 = *pother; + sd.pop0.clear(); + sd.add1.clear(); + const_iterator ai0 = astru0.matoms.begin(); + const_iterator ai1 = astru1.matoms.begin(); + int nboth = min(astru0.countSites(), astru1.countSites()); + for (int i = 0; i < nboth; ++i, ++ai0, ++ai1) { + if (*ai0 != *ai1) { + sd.pop0.push_back(i); + sd.add1.push_back(i); } - if (sd.allowsfastupdate()) return sd; - // here the structures differ too much when compared side by side. - // Let's compare assuming no relation in atom site order. - sd.pop0.clear(); - sd.add1.clear(); - // let's build sorted vectors of atoms in stru0 and stru1 - sd.diffmethod = StructureDifference::Method::SORTED; - std::vector satoms0, satoms1; - satoms0.reserve(astru0.countSites()); - const_iterator ai = astru0.matoms.begin(); - for (int i = 0; ai != astru0.matoms.end(); ++ai, ++i) - { - satoms0.push_back(atomindex(&(*ai), i)); - } - // use negative index for stru1 atoms so we can tell them apart - // in the output of set_symmetric_difference - satoms1.reserve(astru1.countSites()); - ai = astru1.matoms.begin(); - for (int i = -1; ai != astru1.matoms.end(); ++ai, --i) - { - satoms1.push_back(atomindex(&(*ai), i)); - } - sort(satoms0.begin(), satoms0.end(), cmpatomindex); - sort(satoms1.begin(), satoms1.end(), cmpatomindex); - std::vector symdiffatoms(satoms0.size() + satoms1.size()); - std::vector::iterator ii; - ii = std::set_symmetric_difference(satoms0.begin(), satoms0.end(), - satoms1.begin(), satoms1.end(), - symdiffatoms.begin(), cmpatomindex); - symdiffatoms.erase(ii, symdiffatoms.end()); - for (ii = symdiffatoms.begin(); ii != symdiffatoms.end(); ++ii) - { - if (ii->second >= 0) sd.pop0.push_back(ii->second); - else sd.add1.push_back(-1 * ii->second - 1); - } - assert(sd.pop0.size() <= min(satoms0.size(), symdiffatoms.size())); - assert(sd.add1.size() <= min(satoms1.size(), symdiffatoms.size())); - sort(sd.pop0.begin(), sd.pop0.end()); - sort(sd.add1.begin(), sd.add1.end()); - return sd; + } + for (int i = nboth; ai0 != astru0.matoms.end(); ++i, ++ai0) { + sd.pop0.push_back(i); + } + for (int i = nboth; ai1 != astru1.matoms.end(); ++i, ++ai1) { + sd.add1.push_back(i); + } + if (sd.allowsfastupdate()) return sd; + // here the structures differ too much when compared side by side. + // Let's compare assuming no relation in atom site order. + sd.pop0.clear(); + sd.add1.clear(); + // let's build sorted vectors of atoms in stru0 and stru1 + sd.diffmethod = StructureDifference::Method::SORTED; + std::vector satoms0, satoms1; + satoms0.reserve(astru0.countSites()); + const_iterator ai = astru0.matoms.begin(); + for (int i = 0; ai != astru0.matoms.end(); ++ai, ++i) { + satoms0.push_back(atomindex(&(*ai), i)); + } + // use negative index for stru1 atoms so we can tell them apart + // in the output of set_symmetric_difference + satoms1.reserve(astru1.countSites()); + ai = astru1.matoms.begin(); + for (int i = -1; ai != astru1.matoms.end(); ++ai, --i) { + satoms1.push_back(atomindex(&(*ai), i)); + } + sort(satoms0.begin(), satoms0.end(), cmpatomindex); + sort(satoms1.begin(), satoms1.end(), cmpatomindex); + std::vector symdiffatoms(satoms0.size() + satoms1.size()); + std::vector::iterator ii; + ii = std::set_symmetric_difference(satoms0.begin(), satoms0.end(), + satoms1.begin(), satoms1.end(), + symdiffatoms.begin(), cmpatomindex); + symdiffatoms.erase(ii, symdiffatoms.end()); + for (ii = symdiffatoms.begin(); ii != symdiffatoms.end(); ++ii) { + if (ii->second >= 0) + sd.pop0.push_back(ii->second); + else + sd.add1.push_back(-1 * ii->second - 1); + } + assert(sd.pop0.size() <= min(satoms0.size(), symdiffatoms.size())); + assert(sd.add1.size() <= min(satoms1.size(), symdiffatoms.size())); + sort(sd.pop0.begin(), sd.pop0.end()); + sort(sd.add1.begin(), sd.add1.end()); + return sd; } typedef AtomicStructureAdapter::iterator iterator; -iterator AtomicStructureAdapter::insert(int idx, const Atom& atom) -{ - assert(0 <= idx && idx <= this->countSites()); - return this->insert(matoms.begin() + idx, atom); +iterator AtomicStructureAdapter::insert(int idx, const Atom& atom) { + assert(0 <= idx && idx <= this->countSites()); + return this->insert(matoms.begin() + idx, atom); } - -iterator AtomicStructureAdapter::insert(iterator ii, const Atom& atom) -{ - return matoms.insert(ii, atom); +iterator AtomicStructureAdapter::insert(iterator ii, const Atom& atom) { + return matoms.insert(ii, atom); } - -void AtomicStructureAdapter::append(const Atom& atom) -{ - matoms.push_back(atom); -} - - -void AtomicStructureAdapter::clear() -{ - matoms.clear(); +void AtomicStructureAdapter::append(const Atom& atom) { + matoms.push_back(atom); } +void AtomicStructureAdapter::clear() { matoms.clear(); } -iterator AtomicStructureAdapter::erase(int idx) -{ - assert(0 <= idx && idx < this->countSites()); - return matoms.erase(matoms.begin() + idx); +iterator AtomicStructureAdapter::erase(int idx) { + assert(0 <= idx && idx < this->countSites()); + return matoms.erase(matoms.begin() + idx); } - -iterator AtomicStructureAdapter::erase(iterator pos) -{ - return matoms.erase(pos); +iterator AtomicStructureAdapter::erase(iterator pos) { + return matoms.erase(pos); } - -iterator AtomicStructureAdapter::erase(iterator first, iterator last) -{ - return matoms.erase(first, last); +iterator AtomicStructureAdapter::erase(iterator first, iterator last) { + return matoms.erase(first, last); } - -Atom& AtomicStructureAdapter::operator[](int idx) -{ - assert(0 <= idx && idx < this->countSites()); - return matoms[idx]; +Atom& AtomicStructureAdapter::operator[](int idx) { + assert(0 <= idx && idx < this->countSites()); + return matoms[idx]; } - -const Atom& AtomicStructureAdapter::operator[](int idx) const -{ - assert(0 <= idx && idx < this->countSites()); - return matoms[idx]; +const Atom& AtomicStructureAdapter::operator[](int idx) const { + assert(0 <= idx && idx < this->countSites()); + return matoms[idx]; } -} // namespace srreal -} // namespace diffpy +} // namespace srreal +} // namespace diffpy // Serialization ------------------------------------------------------------- diff --git a/src/diffpy/srreal/AtomicStructureAdapter.hpp b/src/diffpy/srreal/AtomicStructureAdapter.hpp index 49e6050e..e3ad296b 100644 --- a/src/diffpy/srreal/AtomicStructureAdapter.hpp +++ b/src/diffpy/srreal/AtomicStructureAdapter.hpp @@ -1,21 +1,21 @@ /***************************************************************************** -* -* libdiffpy by DANSE Diffraction group -* Simon J. L. Billinge -* (c) 2012 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.txt for license information. -* -****************************************************************************** -* -* class AtomicStructureAdapter -- universal structure adapter for -* a non-periodic set of atoms. -* -*****************************************************************************/ + * + * libdiffpy by DANSE Diffraction group + * Simon J. L. Billinge + * (c) 2012 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.txt for license information. + * + ****************************************************************************** + * + * class AtomicStructureAdapter -- universal structure adapter for + * a non-periodic set of atoms. + * + *****************************************************************************/ #ifndef ATOMICSTRUCTUREADAPTER_HPP_INCLUDED #define ATOMICSTRUCTUREADAPTER_HPP_INCLUDED @@ -29,149 +29,133 @@ namespace diffpy { namespace srreal { -class DLL_EXPORT Atom -{ - public: - - // constructor - Atom() : - xyz_cartn(0.0, 0.0, 0.0), - occupancy(1.0), - anisotropy(false), - uij_cartn(R3::zeromatrix()) - { }; - - // data - std::string atomtype; - R3::Vector xyz_cartn; - double occupancy; - bool anisotropy; - R3::Matrix uij_cartn; - - private: - - // serialization - friend class boost::serialization::access; - template - void serialize(Archive& ar, const unsigned int version) - { - ar & atomtype; - ar & xyz_cartn; - ar & occupancy; - ar & anisotropy; - ar & uij_cartn; - } - +class DLL_EXPORT Atom { + public: + // constructor + Atom() + : xyz_cartn(0.0, 0.0, 0.0), + occupancy(1.0), + anisotropy(false), + uij_cartn(R3::zeromatrix()) {}; + + // data + std::string atomtype; + R3::Vector xyz_cartn; + double occupancy; + bool anisotropy; + R3::Matrix uij_cartn; + + private: + // serialization + friend class boost::serialization::access; + template + void serialize(Archive& ar, const unsigned int version) { + ar & atomtype; + ar & xyz_cartn; + ar & occupancy; + ar & anisotropy; + ar & uij_cartn; + } }; // Functions related to class Atom DLL_EXPORT bool operator<(const Atom&, const Atom&); -inline bool operator>(const Atom& a0, const Atom& a1) { return a1 < a0; } -inline bool operator<=(const Atom& a0, const Atom& a1) { return !(a1 < a0); } -inline bool operator>=(const Atom& a0, const Atom& a1) { return !(a0 < a1); } +inline bool operator>(const Atom& a0, const Atom& a1) { return a1 < a0; } +inline bool operator<=(const Atom& a0, const Atom& a1) { return !(a1 < a0); } +inline bool operator>=(const Atom& a0, const Atom& a1) { return !(a0 < a1); } DLL_EXPORT bool operator==(const Atom&, const Atom&); DLL_EXPORT bool operator!=(const Atom&, const Atom&); DLL_EXPORT size_t hash_value(const Atom&); - -class DLL_EXPORT AtomicStructureAdapter : public StructureAdapter -{ - public: - - typedef std::vector AtomVector; - typedef AtomVector::value_type value_type; - typedef AtomVector::iterator iterator; - typedef AtomVector::const_iterator const_iterator; - typedef AtomVector::reverse_iterator reverse_iterator; - typedef AtomVector::const_reverse_iterator const_reverse_iterator; - typedef AtomVector::difference_type difference_type; - typedef AtomVector::size_type size_type; - - // methods - overloaded - virtual StructureAdapterPtr clone() const; - virtual BaseBondGeneratorPtr createBondGenerator() const; - virtual int countSites() const; - // reusing StructureAdapter::numberDensity() - virtual const std::string& siteAtomType(int idx) const; - virtual const R3::Vector& siteCartesianPosition(int idx) const; - // reusing StructureAdapter::siteMultiplicity() - virtual double siteOccupancy(int idx) const; - virtual bool siteAnisotropy(int idx) const; - virtual const R3::Matrix& siteCartesianUij(int idx) const; - virtual StructureDifference diff(StructureAdapterConstPtr other) const; - - // methods - own - iterator insert(int, const Atom&); - iterator insert(iterator position, const Atom&); - template - void insert(iterator position, Iter first, Iter last) - { - matoms.insert(position, first, last); - } - void append(const Atom&); - void clear(); - iterator erase(int idx); - iterator erase(iterator pos); - iterator erase(iterator first, iterator last); - void reserve(size_t sz) { matoms.reserve(sz); } - size_type size() const { return matoms.size(); } - Atom& operator[](int); - const Atom& operator[](int) const; - Atom& at(int idx) { return (*this)[idx]; } - const Atom& at(int idx) const { return (*this)[idx]; } - template - void assign (Iter first, Iter last) { matoms.assign(first, last); } - void assign (size_t n, const Atom& a) { matoms.assign(n, a); } - // iterator forwarding - iterator begin() { return matoms.begin(); } - iterator end() { return matoms.end(); } - const_iterator begin() const { return matoms.begin(); } - const_iterator end() const { return matoms.end(); } - reverse_iterator rbegin() { return matoms.rbegin(); } - reverse_iterator rend() { return matoms.rend(); } - const_reverse_iterator rbegin() const { return matoms.rbegin(); } - const_reverse_iterator rend() const { return matoms.rend(); } - - private: - - // data - AtomVector matoms; - - // comparison - friend bool operator==( - const AtomicStructureAdapter& stru0, - const AtomicStructureAdapter& stru1) - { - return (stru0.matoms == stru1.matoms); - } - - // serialization - friend class boost::serialization::access; - template - void serialize(Archive& ar, const unsigned int version) - { - ar & boost::serialization::base_object(*this); - ar & matoms; - } - +class DLL_EXPORT AtomicStructureAdapter : public StructureAdapter { + public: + typedef std::vector AtomVector; + typedef AtomVector::value_type value_type; + typedef AtomVector::iterator iterator; + typedef AtomVector::const_iterator const_iterator; + typedef AtomVector::reverse_iterator reverse_iterator; + typedef AtomVector::const_reverse_iterator const_reverse_iterator; + typedef AtomVector::difference_type difference_type; + typedef AtomVector::size_type size_type; + + // methods - overloaded + virtual StructureAdapterPtr clone() const; + virtual BaseBondGeneratorPtr createBondGenerator() const; + virtual int countSites() const; + // reusing StructureAdapter::numberDensity() + virtual const std::string& siteAtomType(int idx) const; + virtual const R3::Vector& siteCartesianPosition(int idx) const; + // reusing StructureAdapter::siteMultiplicity() + virtual double siteOccupancy(int idx) const; + virtual bool siteAnisotropy(int idx) const; + virtual const R3::Matrix& siteCartesianUij(int idx) const; + virtual StructureDifference diff(StructureAdapterConstPtr other) const; + + // methods - own + iterator insert(int, const Atom&); + iterator insert(iterator position, const Atom&); + template + void insert(iterator position, Iter first, Iter last) { + matoms.insert(position, first, last); + } + void append(const Atom&); + void clear(); + iterator erase(int idx); + iterator erase(iterator pos); + iterator erase(iterator first, iterator last); + void reserve(size_t sz) { matoms.reserve(sz); } + size_type size() const { return matoms.size(); } + Atom& operator[](int); + const Atom& operator[](int) const; + Atom& at(int idx) { return (*this)[idx]; } + const Atom& at(int idx) const { return (*this)[idx]; } + template + void assign(Iter first, Iter last) { + matoms.assign(first, last); + } + void assign(size_t n, const Atom& a) { matoms.assign(n, a); } + // iterator forwarding + iterator begin() { return matoms.begin(); } + iterator end() { return matoms.end(); } + const_iterator begin() const { return matoms.begin(); } + const_iterator end() const { return matoms.end(); } + reverse_iterator rbegin() { return matoms.rbegin(); } + reverse_iterator rend() { return matoms.rend(); } + const_reverse_iterator rbegin() const { return matoms.rbegin(); } + const_reverse_iterator rend() const { return matoms.rend(); } + + private: + // data + AtomVector matoms; + + // comparison + friend bool operator==(const AtomicStructureAdapter& stru0, + const AtomicStructureAdapter& stru1) { + return (stru0.matoms == stru1.matoms); + } + + // serialization + friend class boost::serialization::access; + template + void serialize(Archive& ar, const unsigned int version) { + ar& boost::serialization::base_object(*this); + ar & matoms; + } }; typedef std::shared_ptr AtomicStructureAdapterPtr; // Complementary comparison function -inline -bool operator!=( - const AtomicStructureAdapter& stru0, - const AtomicStructureAdapter& stru1) -{ - return !(stru0 == stru1); +inline bool operator!=(const AtomicStructureAdapter& stru0, + const AtomicStructureAdapter& stru1) { + return !(stru0 == stru1); } -} // namespace srreal -} // namespace diffpy +} // namespace srreal +} // namespace diffpy // Serialization ------------------------------------------------------------- diff --git a/src/diffpy/srreal/BVParam.cpp b/src/diffpy/srreal/BVParam.cpp index 9db906bd..ba6d63bd 100644 --- a/src/diffpy/srreal/BVParam.cpp +++ b/src/diffpy/srreal/BVParam.cpp @@ -1,20 +1,20 @@ /***************************************************************************** -* -* 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.txt for license information. -* -****************************************************************************** -* -* class BVParam -- bond valence parameters for a cation-anion pair -* -*****************************************************************************/ + * + * 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.txt for license information. + * + ****************************************************************************** + * + * class BVParam -- bond valence parameters for a cation-anion pair + * + *****************************************************************************/ #include #include @@ -30,109 +30,87 @@ namespace srreal { // Constructor --------------------------------------------------------------- -BVParam::BVParam() -{ - mvalence0 = 0; - mvalence1 = 0; - mRo = 0.0; - mB = 0.0; +BVParam::BVParam() { + mvalence0 = 0; + mvalence1 = 0; + mRo = 0.0; + mB = 0.0; } -BVParam::BVParam(const string& atom0, int valence0, - const string& atom1, int valence1, - double Ro, double B, string ref_id) -{ - matom0 = atom0; - mvalence0 = valence0; - matom1 = atom1; - mvalence1 = valence1; - if (mvalence0 < mvalence1) - { - swap(matom0, matom1); - swap(mvalence0, mvalence1); - } - mRo = Ro; - mB = B; - mref_id = ref_id; +BVParam::BVParam(const string& atom0, int valence0, const string& atom1, + int valence1, double Ro, double B, string ref_id) { + matom0 = atom0; + mvalence0 = valence0; + matom1 = atom1; + mvalence1 = valence1; + if (mvalence0 < mvalence1) { + swap(matom0, matom1); + swap(mvalence0, mvalence1); + } + mRo = Ro; + mB = B; + mref_id = ref_id; } // Public Methods ------------------------------------------------------------ -bool BVParam::operator==(const BVParam& other) const -{ - return (this == &other) || ( - mvalence0 == other.mvalence0 && - mvalence1 == other.mvalence1 && - mRo == other.mRo && - mB == other.mB && - matom0 == other.matom0 && - matom1 == other.matom1 && - mref_id == other.mref_id); +bool BVParam::operator==(const BVParam& other) const { + return (this == &other) || + (mvalence0 == other.mvalence0 && mvalence1 == other.mvalence1 && + mRo == other.mRo && mB == other.mB && matom0 == other.matom0 && + matom1 == other.matom1 && mref_id == other.mref_id); } - -bool BVParam::operator!=(const BVParam& other) const -{ - return !(*this == other); +bool BVParam::operator!=(const BVParam& other) const { + return !(*this == other); } - -double BVParam::bondvalence(double distance) const -{ - double rv = (mB > 0.0) ? exp((mRo - distance) / mB) : 0.0; - return rv; +double BVParam::bondvalence(double distance) const { + double rv = (mB > 0.0) ? exp((mRo - distance) / mB) : 0.0; + return rv; } - -double BVParam::bondvalenceToDistance(double bvalence) const -{ - double rv = mRo - mB * log(bvalence); - return rv; +double BVParam::bondvalenceToDistance(double bvalence) const { + double rv = mRo - mB * log(bvalence); + return rv; } - -void BVParam::setFromCifLine(const std::string& cifline) -{ - string a0, a1, ref; - int v0, v1; - double Ro, B; - istringstream linefp(cifline); - linefp >> a0 >> v0 >> a1 >> v1 >> Ro >> B >> ref; - if (!linefp) - { - const char* emsg = "Cannot parse cif line."; - throw invalid_argument(emsg); - } - BVParam bp1(a0, v0, a1, v1, Ro, B, ref); - *this = bp1; +void BVParam::setFromCifLine(const std::string& cifline) { + string a0, a1, ref; + int v0, v1; + double Ro, B; + istringstream linefp(cifline); + linefp >> a0 >> v0 >> a1 >> v1 >> Ro >> B >> ref; + if (!linefp) { + const char* emsg = "Cannot parse cif line."; + throw invalid_argument(emsg); + } + BVParam bp1(a0, v0, a1, v1, Ro, B, ref); + *this = bp1; } // class BVParam::BondHash --------------------------------------------------- -size_t BVParam::BondHash::operator()(const BVParam& bp) const -{ - size_t seed = 0; - boost::hash_combine(seed, bp.matom0); - boost::hash_combine(seed, bp.mvalence0); - boost::hash_combine(seed, bp.matom1); - boost::hash_combine(seed, bp.mvalence1); - return seed; +size_t BVParam::BondHash::operator()(const BVParam& bp) const { + size_t seed = 0; + boost::hash_combine(seed, bp.matom0); + boost::hash_combine(seed, bp.mvalence0); + boost::hash_combine(seed, bp.matom1); + boost::hash_combine(seed, bp.mvalence1); + return seed; } // class BVParam::BondEqual -------------------------------------------------- -bool BVParam::BondEqual::operator()( - const BVParam& bp0, const BVParam& bp1) const -{ - return (&bp0 == &bp1) || ( - bp0.mvalence0 == bp1.mvalence0 && - bp0.mvalence1 == bp1.mvalence1 && - bp0.matom0 == bp1.matom0 && - bp0.matom1 == bp1.matom1); +bool BVParam::BondEqual::operator()(const BVParam& bp0, + const BVParam& bp1) const { + return (&bp0 == &bp1) || + (bp0.mvalence0 == bp1.mvalence0 && bp0.mvalence1 == bp1.mvalence1 && + bp0.matom0 == bp1.matom0 && bp0.matom1 == bp1.matom1); } -} // namespace srreal -} // namespace diffpy +} // namespace srreal +} // namespace diffpy DIFFPY_INSTANTIATE_SERIALIZATION(diffpy::srreal::BVParam) diff --git a/src/diffpy/srreal/BVParam.hpp b/src/diffpy/srreal/BVParam.hpp index cbb8d9a2..b0e3cbd9 100644 --- a/src/diffpy/srreal/BVParam.hpp +++ b/src/diffpy/srreal/BVParam.hpp @@ -1,20 +1,20 @@ /***************************************************************************** -* -* 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.txt for license information. -* -****************************************************************************** -* -* class BVParam -- bond valence parameters for a cation-anion pair -* -*****************************************************************************/ + * + * 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.txt for license information. + * + ****************************************************************************** + * + * class BVParam -- bond valence parameters for a cation-anion pair + * + *****************************************************************************/ #ifndef BVPARAM_HPP_INCLUDED #define BVPARAM_HPP_INCLUDED @@ -27,69 +27,63 @@ namespace diffpy { namespace srreal { -class DLL_EXPORT BVParam -{ - public: +class DLL_EXPORT BVParam { + public: + // constructors + BVParam(); + BVParam(const std::string& atom0, int valence0, const std::string& atom1, + int valence1, double Ro = 0.0, double B = 0.0, + std::string ref_id = ""); - // constructors - BVParam(); - BVParam(const std::string& atom0, int valence0, - const std::string& atom1, int valence1, - double Ro=0.0, double B=0.0, std::string ref_id=""); + // methods + /// comparison binary_function + bool operator==(const BVParam& other) const; + bool operator!=(const BVParam& other) const; + /// Return bond valence at a specified distance + double bondvalence(double distance) const; + /// Return distance corresponding to a specified bond valence + double bondvalenceToDistance(double bvalence) const; + /// obtain data from a cif record in bvparm.cif + void setFromCifLine(const std::string&); - // methods - /// comparison binary_function - bool operator==(const BVParam& other) const; - bool operator!=(const BVParam& other) const; - /// Return bond valence at a specified distance - double bondvalence(double distance) const; - /// Return distance corresponding to a specified bond valence - double bondvalenceToDistance(double bvalence) const; - /// obtain data from a cif record in bvparm.cif - void setFromCifLine(const std::string&); + // data + std::string matom0; + int mvalence0; + std::string matom1; + int mvalence1; + double mRo; + double mB; + std::string mref_id; - // data - std::string matom0; - int mvalence0; - std::string matom1; - int mvalence1; - double mRo; - double mB; - std::string mref_id; + // hash function for cation-anion bond type only + class DLL_EXPORT BondHash { + public: + size_t operator()(const BVParam&) const; + }; - // hash function for cation-anion bond type only - class DLL_EXPORT BondHash - { - public: - size_t operator()(const BVParam&) const; - }; + // equality test for cation-anion bond type + class DLL_EXPORT BondEqual { + public: + bool operator()(const BVParam& bp0, const BVParam& bp1) const; + }; - // equality test for cation-anion bond type - class DLL_EXPORT BondEqual - { - public: - bool operator()(const BVParam& bp0, const BVParam& bp1) const; - }; - - private: - - // serialization - friend class boost::serialization::access; - template - void serialize(Archive& ar, const unsigned int version) - { - ar & matom0; - ar & mvalence0; - ar & matom1; - ar & mvalence1; - ar & mRo; - ar & mB; - ar & mref_id; - } + private: + // serialization + friend class boost::serialization::access; + template + void serialize(Archive& ar, const unsigned int version) { + ar & matom0; + ar & mvalence0; + ar & matom1; + ar & mvalence1; + ar & mRo; + ar & mB; + ar & mref_id; + } }; // class BVParam -} // namespace srreal -} // namespace diffpy +} // namespace srreal +} // namespace diffpy #endif // BVPARAM_HPP_INCLUDED diff --git a/src/diffpy/srreal/BVParametersTable.cpp b/src/diffpy/srreal/BVParametersTable.cpp index 2d53afe4..8a7fbae7 100644 --- a/src/diffpy/srreal/BVParametersTable.cpp +++ b/src/diffpy/srreal/BVParametersTable.cpp @@ -1,20 +1,20 @@ /***************************************************************************** -* -* 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.txt for license information. -* -****************************************************************************** -* -* class BVParametersTable -- table of bond valence sum parameters -* -*****************************************************************************/ + * + * 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.txt for license information. + * + ****************************************************************************** + * + * class BVParametersTable -- table of bond valence sum parameters + * + *****************************************************************************/ #include #include @@ -32,169 +32,134 @@ namespace srreal { // Static Methods ------------------------------------------------------------ -const BVParam& BVParametersTable::none() -{ - static const BVParam bpnone; - return bpnone; +const BVParam& BVParametersTable::none() { + static const BVParam bpnone; + return bpnone; } // Public Methods ------------------------------------------------------------ -int BVParametersTable::getAtomValence(const string& smbl) const -{ - AtomTypeValence::const_iterator ii = matomvalence.find(smbl); - int rv = (ii != matomvalence.end()) ? ii->second : atomValence(smbl); - return rv; +int BVParametersTable::getAtomValence(const string& smbl) const { + AtomTypeValence::const_iterator ii = matomvalence.find(smbl); + int rv = (ii != matomvalence.end()) ? ii->second : atomValence(smbl); + return rv; } - -void BVParametersTable::setAtomValence(const string& smbl, int value) -{ - matomvalence[smbl] = value; +void BVParametersTable::setAtomValence(const string& smbl, int value) { + matomvalence[smbl] = value; } - -void BVParametersTable::resetAtomValences() -{ - matomvalence.clear(); +void BVParametersTable::resetAtomValences() { matomvalence.clear(); } + +const BVParam& BVParametersTable::lookup(const BVParam& bpk) const { + SetOfBVParam::const_iterator bpit; + bpit = mcustomtable.find(bpk); + if (bpit != mcustomtable.end()) return *bpit; + const SetOfBVParam& stdtable = this->getStandardSetOfBVParam(); + bpit = stdtable.find(bpk); + if (bpit != stdtable.end()) return *bpit; + // unspecified cations are marked with valence 9 + // perform this search only if the first atom is indeed a cation + if (!(bpk.mvalence0 > 0)) return this->none(); + BVParam bpk9 = bpk; + bpk9.mvalence0 = 9; + bpit = mcustomtable.find(bpk9); + if (bpit != mcustomtable.end()) return *bpit; + bpit = stdtable.find(bpk9); + if (bpit != stdtable.end()) return *bpit; + // not found - return blank BVParam + return this->none(); } - -const BVParam& BVParametersTable::lookup(const BVParam& bpk) const -{ - SetOfBVParam::const_iterator bpit; - bpit = mcustomtable.find(bpk); - if (bpit != mcustomtable.end()) return *bpit; - const SetOfBVParam& stdtable = this->getStandardSetOfBVParam(); - bpit = stdtable.find(bpk); - if (bpit != stdtable.end()) return *bpit; - // unspecified cations are marked with valence 9 - // perform this search only if the first atom is indeed a cation - if (!(bpk.mvalence0 > 0)) return this->none(); - BVParam bpk9 = bpk; - bpk9.mvalence0 = 9; - bpit = mcustomtable.find(bpk9); - if (bpit != mcustomtable.end()) return *bpit; - bpit = stdtable.find(bpk9); - if (bpit != stdtable.end()) return *bpit; - // not found - return blank BVParam - return this->none(); +const BVParam& BVParametersTable::lookup(const string& smbl0, + const string& smbl1) const { + const int v0 = this->getAtomValence(smbl0); + const int v1 = this->getAtomValence(smbl1); + const BVParam& rv = + this->lookup(atomBareSymbol(smbl0), v0, atomBareSymbol(smbl1), v1); + return rv; } - -const BVParam& -BVParametersTable::lookup(const string& smbl0, const string& smbl1) const -{ - const int v0 = this->getAtomValence(smbl0); - const int v1 = this->getAtomValence(smbl1); - const BVParam& rv = this->lookup( - atomBareSymbol(smbl0), v0, - atomBareSymbol(smbl1), v1); - return rv; -} - - const BVParam& BVParametersTable::lookup(const string& atom0, int valence0, - const string& atom1, int valence1) const -{ - BVParam bpk(atom0, valence0, atom1, valence1); - const BVParam& rv = this->lookup(bpk); - return rv; + const string& atom1, + int valence1) const { + BVParam bpk(atom0, valence0, atom1, valence1); + const BVParam& rv = this->lookup(bpk); + return rv; } - -void BVParametersTable::setCustom(const BVParam& bp) -{ - this->resetCustom(bp); - mcustomtable.insert(bp); +void BVParametersTable::setCustom(const BVParam& bp) { + this->resetCustom(bp); + mcustomtable.insert(bp); } - void BVParametersTable::setCustom(const string& atom0, int valence0, - const string& atom1, int valence1, - double Ro, double b, std::string ref_id) -{ - BVParam bp(atom0, valence0, atom1, valence1, Ro, b, ref_id); - this->setCustom(bp); + const string& atom1, int valence1, double Ro, + double b, std::string ref_id) { + BVParam bp(atom0, valence0, atom1, valence1, Ro, b, ref_id); + this->setCustom(bp); } - -void BVParametersTable::resetCustom(const BVParam& bp) -{ - mcustomtable.erase(bp); +void BVParametersTable::resetCustom(const BVParam& bp) { + mcustomtable.erase(bp); } - void BVParametersTable::resetCustom(const string& atom0, int valence0, - const string& atom1, int valence1) -{ - BVParam bp(atom0, valence0, atom1, valence1); - this->resetCustom(bp); + const string& atom1, int valence1) { + BVParam bp(atom0, valence0, atom1, valence1); + this->resetCustom(bp); } +void BVParametersTable::resetAll() { mcustomtable.clear(); } -void BVParametersTable::resetAll() -{ - mcustomtable.clear(); +const BVParametersTable::SetOfBVParam& BVParametersTable::getAllCustom() const { + return mcustomtable; } - -const BVParametersTable::SetOfBVParam& -BVParametersTable::getAllCustom() const -{ - return mcustomtable; -} - - -BVParametersTable::SetOfBVParam -BVParametersTable::getAll() const -{ - // insert inserts only those items that are not yet in the set, therefore - // we start with the custom table and then add the standard entries. - SetOfBVParam rv = mcustomtable; - const SetOfBVParam& stdtable = this->getStandardSetOfBVParam(); - rv.insert(stdtable.begin(), stdtable.end()); - return rv; +BVParametersTable::SetOfBVParam BVParametersTable::getAll() const { + // insert inserts only those items that are not yet in the set, therefore + // we start with the custom table and then add the standard entries. + SetOfBVParam rv = mcustomtable; + const SetOfBVParam& stdtable = this->getStandardSetOfBVParam(); + rv.insert(stdtable.begin(), stdtable.end()); + return rv; } // Private Methods ----------------------------------------------------------- const BVParametersTable::SetOfBVParam& -BVParametersTable::getStandardSetOfBVParam() const -{ - using namespace diffpy::runtimepath; - using diffpy::validators::ensureFileOK; - static unique_ptr the_set; - if (!the_set) - { - the_set.reset(new SetOfBVParam); - string bvparmfile = datapath("bvparm2011sel.cif"); - ifstream fp(bvparmfile.c_str()); - ensureFileOK(bvparmfile, fp); - // read the header up to _valence_param_B and then up to an empty line. - LineReader lnrd; - lnrd.commentmark = '#'; - while (fp >> lnrd) - { - if (lnrd.wcount() && lnrd.words[0] == "_valence_param_B") break; - } - // skip to an empty line - while (fp >> lnrd && !lnrd.isblank()) { } - // load data lines skipping the empty or commented entries - while (fp >> lnrd) - { - if (lnrd.isignored()) continue; - BVParam bp; - bp.setFromCifLine(lnrd.line); - assert(!the_set->count(bp)); - the_set->insert(bp); - } +BVParametersTable::getStandardSetOfBVParam() const { + using namespace diffpy::runtimepath; + using diffpy::validators::ensureFileOK; + static unique_ptr the_set; + if (!the_set) { + the_set.reset(new SetOfBVParam); + string bvparmfile = datapath("bvparm2011sel.cif"); + ifstream fp(bvparmfile.c_str()); + ensureFileOK(bvparmfile, fp); + // read the header up to _valence_param_B and then up to an empty line. + LineReader lnrd; + lnrd.commentmark = '#'; + while (fp >> lnrd) { + if (lnrd.wcount() && lnrd.words[0] == "_valence_param_B") break; + } + // skip to an empty line + while (fp >> lnrd && !lnrd.isblank()) { + } + // load data lines skipping the empty or commented entries + while (fp >> lnrd) { + if (lnrd.isignored()) continue; + BVParam bp; + bp.setFromCifLine(lnrd.line); + assert(!the_set->count(bp)); + the_set->insert(bp); } - return *the_set; + } + return *the_set; } -} // namespace srreal -} // namespace diffpy +} // namespace srreal +} // namespace diffpy // Serialization ------------------------------------------------------------- diff --git a/src/diffpy/srreal/BVParametersTable.hpp b/src/diffpy/srreal/BVParametersTable.hpp index 69b5cec1..7e43882c 100644 --- a/src/diffpy/srreal/BVParametersTable.hpp +++ b/src/diffpy/srreal/BVParametersTable.hpp @@ -1,20 +1,20 @@ /***************************************************************************** -* -* 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.txt for license information. -* -****************************************************************************** -* -* class BVParametersTable -- table of bond valence sum parameters -* -*****************************************************************************/ + * + * 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.txt for license information. + * + ****************************************************************************** + * + * class BVParametersTable -- table of bond valence sum parameters + * + *****************************************************************************/ #ifndef BVPARAMETERSTABLE_HPP_INCLUDED #define BVPARAMETERSTABLE_HPP_INCLUDED @@ -34,65 +34,60 @@ namespace srreal { typedef std::shared_ptr BVParametersTablePtr; -class DLL_EXPORT BVParametersTable -{ - - public: - - // types - typedef std::unordered_set SetOfBVParam; - - // static methods - static const BVParam& none(); - - // methods - int getAtomValence(const std::string&) const; - void setAtomValence(const std::string&, int value); - void resetAtomValences(); - const BVParam& lookup(const BVParam&) const; - const BVParam& lookup( - const std::string& smbl0, const std::string& smbl1) const; - const BVParam& lookup(const std::string& atom0, int valence0, - const std::string& atom1, int valence1) const; - void setCustom(const BVParam&); - void setCustom(const std::string& atom0, int valence0, - const std::string& atom1, int valence1, - double Ro, double b, std::string ref_id=""); - void resetCustom(const BVParam&); - void resetCustom(const std::string& atom0, int valence0, - const std::string& atom1, int valence1); - void resetAll(); - const SetOfBVParam& getAllCustom() const; - SetOfBVParam getAll() const; - - private: - - // types - typedef std::unordered_map AtomTypeValence; - - // data - SetOfBVParam mcustomtable; - AtomTypeValence matomvalence; - - // methods - const SetOfBVParam& getStandardSetOfBVParam() const; - - // serialization - friend class boost::serialization::access; - template - void serialize(Archive& ar, const unsigned int version) - { - ar & mcustomtable; - if (version >= 1) { - ar & matomvalence; - } - } +class DLL_EXPORT BVParametersTable { + public: + // types + typedef std::unordered_set + SetOfBVParam; + + // static methods + static const BVParam& none(); + + // methods + int getAtomValence(const std::string&) const; + void setAtomValence(const std::string&, int value); + void resetAtomValences(); + const BVParam& lookup(const BVParam&) const; + const BVParam& lookup(const std::string& smbl0, + const std::string& smbl1) const; + const BVParam& lookup(const std::string& atom0, int valence0, + const std::string& atom1, int valence1) const; + void setCustom(const BVParam&); + void setCustom(const std::string& atom0, int valence0, + const std::string& atom1, int valence1, double Ro, double b, + std::string ref_id = ""); + void resetCustom(const BVParam&); + void resetCustom(const std::string& atom0, int valence0, + const std::string& atom1, int valence1); + void resetAll(); + const SetOfBVParam& getAllCustom() const; + SetOfBVParam getAll() const; + + private: + // types + typedef std::unordered_map AtomTypeValence; + + // data + SetOfBVParam mcustomtable; + AtomTypeValence matomvalence; + + // methods + const SetOfBVParam& getStandardSetOfBVParam() const; + + // serialization + friend class boost::serialization::access; + template + void serialize(Archive& ar, const unsigned int version) { + ar & mcustomtable; + if (version >= 1) { + ar & matomvalence; + } + } }; // class BVParametersTable -} // namespace srreal -} // namespace diffpy +} // namespace srreal +} // namespace diffpy // Serialization ------------------------------------------------------------- diff --git a/src/diffpy/srreal/BVSCalculator.cpp b/src/diffpy/srreal/BVSCalculator.cpp index 0b3d07ce..668698aa 100644 --- a/src/diffpy/srreal/BVSCalculator.cpp +++ b/src/diffpy/srreal/BVSCalculator.cpp @@ -1,20 +1,20 @@ /***************************************************************************** -* -* 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.txt for license information. -* -****************************************************************************** -* -* class BVSCalculator -- concrete counter of pairs in a structure. -* -*****************************************************************************/ + * + * 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.txt for license information. + * + ****************************************************************************** + * + * class BVSCalculator -- concrete counter of pairs in a structure. + * + *****************************************************************************/ #include #include @@ -32,209 +32,168 @@ namespace srreal { // Constructor --------------------------------------------------------------- -BVSCalculator::BVSCalculator() -{ - // default configuration - const double valence_precision = 1e-5; - // use very large rmax, it will be cropped by rmaxFromPrecision - this->setRmax(100); - BVParametersTablePtr bvtb(new BVParametersTable); - this->setBVParamTable(bvtb); - this->setValencePrecision(valence_precision); - this->setStructure(mstructure); - // attributes - this->registerDoubleAttribute("valenceprecision", this, - &BVSCalculator::getValencePrecision, - &BVSCalculator::setValencePrecision); - this->registerDoubleAttribute("rmaxused", this, - &BVSCalculator::getRmaxUsed); +BVSCalculator::BVSCalculator() { + // default configuration + const double valence_precision = 1e-5; + // use very large rmax, it will be cropped by rmaxFromPrecision + this->setRmax(100); + BVParametersTablePtr bvtb(new BVParametersTable); + this->setBVParamTable(bvtb); + this->setValencePrecision(valence_precision); + this->setStructure(mstructure); + // attributes + this->registerDoubleAttribute("valenceprecision", this, + &BVSCalculator::getValencePrecision, + &BVSCalculator::setValencePrecision); + this->registerDoubleAttribute("rmaxused", this, &BVSCalculator::getRmaxUsed); } // Public Methods ------------------------------------------------------------ // results -QuantityType BVSCalculator::valences() const -{ - QuantityType rv(mstructure_cache.valences.begin(), - mstructure_cache.valences.end()); - return rv; +QuantityType BVSCalculator::valences() const { + QuantityType rv(mstructure_cache.valences.begin(), + mstructure_cache.valences.end()); + return rv; } - -QuantityType BVSCalculator::bvdiff() const -{ - QuantityType vobs = this->valences(); - assert(vobs.size() == this->value().size()); - int cntsites = this->countSites(); - QuantityType rv(cntsites); - const QuantityType& vsim = this->value(); - for (int i = 0; i < cntsites; ++i) - { - rv[i] = fabs(vobs[i]) - fabs(vsim[i]); - } - return rv; +QuantityType BVSCalculator::bvdiff() const { + QuantityType vobs = this->valences(); + assert(vobs.size() == this->value().size()); + int cntsites = this->countSites(); + QuantityType rv(cntsites); + const QuantityType& vsim = this->value(); + for (int i = 0; i < cntsites; ++i) { + rv[i] = fabs(vobs[i]) - fabs(vsim[i]); + } + return rv; } - -double BVSCalculator::bvmsdiff() const -{ - QuantityType bd = this->bvdiff(); - int cntsites = this->countSites(); - assert(int(bd.size()) == cntsites); - double sumofsquares = 0.0; - for (int i = 0; i < cntsites; ++i) - { - sumofsquares += mstructure->siteMultiplicity(i) * - mstructure->siteOccupancy(i) * bd[i] * bd[i]; - } - double totocc = mstructure->totalOccupancy(); - double rv = (totocc > 0.0) ? (sumofsquares / totocc) : 0.0; - return rv; +double BVSCalculator::bvmsdiff() const { + QuantityType bd = this->bvdiff(); + int cntsites = this->countSites(); + assert(int(bd.size()) == cntsites); + double sumofsquares = 0.0; + for (int i = 0; i < cntsites; ++i) { + sumofsquares += mstructure->siteMultiplicity(i) * + mstructure->siteOccupancy(i) * bd[i] * bd[i]; + } + double totocc = mstructure->totalOccupancy(); + double rv = (totocc > 0.0) ? (sumofsquares / totocc) : 0.0; + return rv; } - -double BVSCalculator::bvrmsdiff() const -{ - double rvsq = this->bvmsdiff(); - return sqrt(rvsq); +double BVSCalculator::bvrmsdiff() const { + double rvsq = this->bvmsdiff(); + return sqrt(rvsq); } - -void BVSCalculator::setBVParamTable(BVParametersTablePtr bvtb) -{ - ensureNonNull("BVParametersTable", bvtb); - mbvptable = bvtb; +void BVSCalculator::setBVParamTable(BVParametersTablePtr bvtb) { + ensureNonNull("BVParametersTable", bvtb); + mbvptable = bvtb; } +BVParametersTablePtr& BVSCalculator::getBVParamTable() { return mbvptable; } -BVParametersTablePtr& BVSCalculator::getBVParamTable() -{ - return mbvptable; +const BVParametersTablePtr& BVSCalculator::getBVParamTable() const { + return mbvptable; } - -const BVParametersTablePtr& BVSCalculator::getBVParamTable() const -{ - return mbvptable; +void BVSCalculator::setValencePrecision(double eps) { + ensureEpsilonPositive("valenceprecision", eps); + mvalenceprecision = eps; } +double BVSCalculator::getValencePrecision() const { return mvalenceprecision; } -void BVSCalculator::setValencePrecision(double eps) -{ - ensureEpsilonPositive("valenceprecision", eps); - mvalenceprecision = eps; -} - - -double BVSCalculator::getValencePrecision() const -{ - return mvalenceprecision; -} - - -double BVSCalculator::getRmaxUsed() const -{ - double rv = min(this->getRmax(), - this->rmaxFromPrecision(this->getValencePrecision())); - return rv; +double BVSCalculator::getRmaxUsed() const { + double rv = + min(this->getRmax(), this->rmaxFromPrecision(this->getValencePrecision())); + return rv; } // Protected Methods --------------------------------------------------------- // PairQuantity overloads -void BVSCalculator::resetValue() -{ - // structure data need to be cached for rmaxFromPrecision - this->cacheStructureData(); - this->resizeValue(this->countSites()); - this->PairQuantity::resetValue(); +void BVSCalculator::resetValue() { + // structure data need to be cached for rmaxFromPrecision + this->cacheStructureData(); + this->resizeValue(this->countSites()); + this->PairQuantity::resetValue(); } - -void BVSCalculator::configureBondGenerator(BaseBondGenerator& bnds) const -{ - bnds.setRmax(this->getRmaxUsed()); +void BVSCalculator::configureBondGenerator(BaseBondGenerator& bnds) const { + bnds.setRmax(this->getRmaxUsed()); } - void BVSCalculator::addPairContribution(const BaseBondGenerator& bnds, - int summationscale) -{ - const string& a0 = mstructure_cache.baresymbols[bnds.site0()]; - const string& a1 = mstructure_cache.baresymbols[bnds.site1()]; - int v0 = mstructure_cache.valences[bnds.site0()]; - int v1 = mstructure_cache.valences[bnds.site1()]; - const BVParametersTable& bvtb = *(this->getBVParamTable()); - const BVParam& bp = bvtb.lookup(a0, v0, a1, v1); - // do nothing if there are no bond parameters for this pair - if (&bp == &bvtb.none()) return; - double valencehalf = bp.bondvalence(bnds.distance()) / 2.0; - int pm0 = (v0 >= 0) ? 1 : -1; - int pm1 = (v1 >= 0) ? 1 : -1; - const double& o0 = mstructure->siteOccupancy(bnds.site0()); - const double& o1 = mstructure->siteOccupancy(bnds.site1()); - mvalue[bnds.site0()] += summationscale * pm0 * valencehalf * o1; - mvalue[bnds.site1()] += summationscale * pm1 * valencehalf * o0; + int summationscale) { + const string& a0 = mstructure_cache.baresymbols[bnds.site0()]; + const string& a1 = mstructure_cache.baresymbols[bnds.site1()]; + int v0 = mstructure_cache.valences[bnds.site0()]; + int v1 = mstructure_cache.valences[bnds.site1()]; + const BVParametersTable& bvtb = *(this->getBVParamTable()); + const BVParam& bp = bvtb.lookup(a0, v0, a1, v1); + // do nothing if there are no bond parameters for this pair + if (&bp == &bvtb.none()) return; + double valencehalf = bp.bondvalence(bnds.distance()) / 2.0; + int pm0 = (v0 >= 0) ? 1 : -1; + int pm1 = (v1 >= 0) ? 1 : -1; + const double& o0 = mstructure->siteOccupancy(bnds.site0()); + const double& o1 = mstructure->siteOccupancy(bnds.site1()); + mvalue[bnds.site0()] += summationscale * pm0 * valencehalf * o1; + mvalue[bnds.site1()] += summationscale * pm1 * valencehalf * o0; } // Private Methods ----------------------------------------------------------- -void BVSCalculator::cacheStructureData() -{ - int cntsites = this->countSites(); - mstructure_cache.baresymbols.resize(cntsites); - mstructure_cache.valences.resize(cntsites); - const BVParametersTable& bvtb = *(this->getBVParamTable()); - for (int i = 0; i < cntsites; ++i) - { - const string& smbl = mstructure->siteAtomType(i); - mstructure_cache.baresymbols[i] = atomBareSymbol(smbl); - mstructure_cache.valences[i] = bvtb.getAtomValence(smbl); - } -} - - -double BVSCalculator::rmaxFromPrecision(double eps) const -{ - const BVParametersTable& bvtb = *(this->getBVParamTable()); - // build a set of unique (symbol, valence) pairs - typedef std::unordered_set< - pair, - boost::hash< pair > - > SymbolValenceSet; - SymbolValenceSet allsymvals; - assert(int(mstructure_cache.baresymbols.size()) == this->countSites()); - assert(int(mstructure_cache.valences.size()) == this->countSites()); - for (int i = 0; i < this->countSites(); ++i) - { - allsymvals.insert(make_pair(mstructure_cache.baresymbols[i], - mstructure_cache.valences[i])); - } - // find all used bond parameters - BVParametersTable::SetOfBVParam bpused; - SymbolValenceSet::const_iterator sv0, sv1; - for (sv0 = allsymvals.begin(); sv0 != allsymvals.end(); ++sv0) - { - for (sv1 = sv0; sv1 != allsymvals.end(); ++sv1) - { - const BVParam& bp = bvtb.lookup( - sv0->first, sv0->second, sv1->first, sv1->second); - bpused.insert(bp); - } - } - double rv = 0.0; - BVParametersTable::SetOfBVParam::iterator bpit; - for (bpit = bpused.begin(); bpit != bpused.end(); ++bpit) - { - rv = max(rv, bpit->bondvalenceToDistance(eps)); +void BVSCalculator::cacheStructureData() { + int cntsites = this->countSites(); + mstructure_cache.baresymbols.resize(cntsites); + mstructure_cache.valences.resize(cntsites); + const BVParametersTable& bvtb = *(this->getBVParamTable()); + for (int i = 0; i < cntsites; ++i) { + const string& smbl = mstructure->siteAtomType(i); + mstructure_cache.baresymbols[i] = atomBareSymbol(smbl); + mstructure_cache.valences[i] = bvtb.getAtomValence(smbl); + } +} + +double BVSCalculator::rmaxFromPrecision(double eps) const { + const BVParametersTable& bvtb = *(this->getBVParamTable()); + // build a set of unique (symbol, valence) pairs + typedef std::unordered_set, + boost::hash > > + SymbolValenceSet; + SymbolValenceSet allsymvals; + assert(int(mstructure_cache.baresymbols.size()) == this->countSites()); + assert(int(mstructure_cache.valences.size()) == this->countSites()); + for (int i = 0; i < this->countSites(); ++i) { + allsymvals.insert( + make_pair(mstructure_cache.baresymbols[i], mstructure_cache.valences[i])); + } + // find all used bond parameters + BVParametersTable::SetOfBVParam bpused; + SymbolValenceSet::const_iterator sv0, sv1; + for (sv0 = allsymvals.begin(); sv0 != allsymvals.end(); ++sv0) { + for (sv1 = sv0; sv1 != allsymvals.end(); ++sv1) { + const BVParam& bp = + bvtb.lookup(sv0->first, sv0->second, sv1->first, sv1->second); + bpused.insert(bp); } - return rv; + } + double rv = 0.0; + BVParametersTable::SetOfBVParam::iterator bpit; + for (bpit = bpused.begin(); bpit != bpused.end(); ++bpit) { + rv = max(rv, bpit->bondvalenceToDistance(eps)); + } + return rv; } -} // namespace srreal -} // namespace diffpy +} // namespace srreal +} // namespace diffpy // Serialization ------------------------------------------------------------- diff --git a/src/diffpy/srreal/BVSCalculator.hpp b/src/diffpy/srreal/BVSCalculator.hpp index dc1f3160..4ac3b153 100644 --- a/src/diffpy/srreal/BVSCalculator.hpp +++ b/src/diffpy/srreal/BVSCalculator.hpp @@ -1,20 +1,20 @@ /***************************************************************************** -* -* 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.txt for license information. -* -****************************************************************************** -* -* class BVSCalculator -- bond valence sums calculator -* -*****************************************************************************/ + * + * 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.txt for license information. + * + ****************************************************************************** + * + * class BVSCalculator -- bond valence sums calculator + * + *****************************************************************************/ #ifndef BVSCALCULATOR_HPP_INCLUDED #define BVSCALCULATOR_HPP_INCLUDED @@ -27,78 +27,73 @@ namespace diffpy { namespace srreal { -class DLL_EXPORT BVSCalculator : public PairQuantity -{ - public: - - // constructor - BVSCalculator(); - - // results - /// expected valence per each site - QuantityType valences() const; - /// difference between expected and calculated absolute valence at - /// each site. Positive for underbonding, negative for overbonding. - QuantityType bvdiff() const; - /// mean square difference of BVS from the expected values - double bvmsdiff() const; - /// root mean square difference of BVS from the expected values - double bvrmsdiff() const; - - // access and configuration of BVS parameters - void setBVParamTable(BVParametersTablePtr); - BVParametersTablePtr& getBVParamTable(); - const BVParametersTablePtr& getBVParamTable() const; - - // R-range configuration using the valence precision - /// set cutoff value for bond valence contributions - void setValencePrecision(double); - /// return cutoff value for bond valence contributions - double getValencePrecision() const; - /// effective rmax value, where valence contributions become smaller - /// than the valence precision cutoff. Always less or equal to rmax. - double getRmaxUsed() const; - - protected: - - // PairQuantity overloads - virtual void resetValue(); - virtual void configureBondGenerator(BaseBondGenerator&) const; - virtual void addPairContribution(const BaseBondGenerator&, int); - - private: - - // methods - void cacheStructureData(); - /// rmax necessary for achieving the specified valence precision - double rmaxFromPrecision(double) const; - - // data - // configuration - BVParametersTablePtr mbvptable; - double mvalenceprecision; - // cache - struct { - std::vector baresymbols; - std::vector valences; - } mstructure_cache; - - // serialization - friend class boost::serialization::access; - template - void serialize(Archive& ar, const unsigned int version) - { - ar & boost::serialization::base_object(*this); - ar & mbvptable; - ar & mvalenceprecision; - ar & mstructure_cache.baresymbols; - ar & mstructure_cache.valences; - } +class DLL_EXPORT BVSCalculator : public PairQuantity { + public: + // constructor + BVSCalculator(); + + // results + /// expected valence per each site + QuantityType valences() const; + /// difference between expected and calculated absolute valence at + /// each site. Positive for underbonding, negative for overbonding. + QuantityType bvdiff() const; + /// mean square difference of BVS from the expected values + double bvmsdiff() const; + /// root mean square difference of BVS from the expected values + double bvrmsdiff() const; + + // access and configuration of BVS parameters + void setBVParamTable(BVParametersTablePtr); + BVParametersTablePtr& getBVParamTable(); + const BVParametersTablePtr& getBVParamTable() const; + + // R-range configuration using the valence precision + /// set cutoff value for bond valence contributions + void setValencePrecision(double); + /// return cutoff value for bond valence contributions + double getValencePrecision() const; + /// effective rmax value, where valence contributions become smaller + /// than the valence precision cutoff. Always less or equal to rmax. + double getRmaxUsed() const; + + protected: + // PairQuantity overloads + virtual void resetValue(); + virtual void configureBondGenerator(BaseBondGenerator&) const; + virtual void addPairContribution(const BaseBondGenerator&, int); + + private: + // methods + void cacheStructureData(); + /// rmax necessary for achieving the specified valence precision + double rmaxFromPrecision(double) const; + + // data + // configuration + BVParametersTablePtr mbvptable; + double mvalenceprecision; + // cache + struct { + std::vector baresymbols; + std::vector valences; + } mstructure_cache; + + // serialization + friend class boost::serialization::access; + template + void serialize(Archive& ar, const unsigned int version) { + ar& boost::serialization::base_object(*this); + ar & mbvptable; + ar & mvalenceprecision; + ar & mstructure_cache.baresymbols; + ar & mstructure_cache.valences; + } }; // class BVSCalculator -} // namespace srreal -} // namespace diffpy +} // namespace srreal +} // namespace diffpy // Serialization ------------------------------------------------------------- diff --git a/src/diffpy/srreal/BaseBondGenerator.cpp b/src/diffpy/srreal/BaseBondGenerator.cpp index c4374ea2..52ddbb07 100644 --- a/src/diffpy/srreal/BaseBondGenerator.cpp +++ b/src/diffpy/srreal/BaseBondGenerator.cpp @@ -1,21 +1,21 @@ /***************************************************************************** -* -* 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.txt for license information. -* -****************************************************************************** -* -* class BaseBondGenerator -- semi-abstract class for generation -* of all atom pairs containing specified anchor atom. -* -*****************************************************************************/ + * + * 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.txt for license information. + * + ****************************************************************************** + * + * class BaseBondGenerator -- semi-abstract class for generation + * of all atom pairs containing specified anchor atom. + * + *****************************************************************************/ #include #include @@ -26,253 +26,175 @@ using diffpy::mathutils::eps_eq; namespace diffpy { namespace srreal { -//using namespace std; +// using namespace std; // Constructor --------------------------------------------------------------- -BaseBondGenerator::BaseBondGenerator(StructureAdapterConstPtr stru) : - msite_anchor(0), - mrmin(0.0), - mrmax(DEFAULT_BONDGENERATOR_RMAX), - mstructure(stru), - mr0(R3::zerovector), - mr1(R3::zerovector), - mr01(R3::zerovector), - mdistance(0.0) -{ - int cnt = stru->countSites(); - msite_all.resize(cnt); - for (int i = 0; i != cnt; ++i) msite_all[i] = i; - msite_first = msite_all.begin(); - msite_last = msite_all.end(); - this->setFinishedFlag(); +BaseBondGenerator::BaseBondGenerator(StructureAdapterConstPtr stru) + : msite_anchor(0), + mrmin(0.0), + mrmax(DEFAULT_BONDGENERATOR_RMAX), + mstructure(stru), + mr0(R3::zerovector), + mr1(R3::zerovector), + mr01(R3::zerovector), + mdistance(0.0) { + int cnt = stru->countSites(); + msite_all.resize(cnt); + for (int i = 0; i != cnt; ++i) msite_all[i] = i; + msite_first = msite_all.begin(); + msite_last = msite_all.end(); + this->setFinishedFlag(); } // Public Methods ------------------------------------------------------------ // loop control -void BaseBondGenerator::rewind() -{ - msite_current = msite_first; - // avoid calling rewindSymmetry at an invalid site - if (this->finished()) return; - this->rewindSymmetry(); - this->advanceWhileInvalid(); +void BaseBondGenerator::rewind() { + msite_current = msite_first; + // avoid calling rewindSymmetry at an invalid site + if (this->finished()) return; + this->rewindSymmetry(); + this->advanceWhileInvalid(); } - -void BaseBondGenerator::next() -{ - this->getNextBond(); - this->advanceWhileInvalid(); +void BaseBondGenerator::next() { + this->getNextBond(); + this->advanceWhileInvalid(); } - -bool BaseBondGenerator::finished() const -{ - return msite_current >= msite_last; -} +bool BaseBondGenerator::finished() const { return msite_current >= msite_last; } // configuration -void BaseBondGenerator::selectAnchorSite(int anchor) -{ - assert(0 <= anchor && anchor < mstructure->countSites()); - msite_anchor = anchor; - mr0 = mstructure->siteCartesianPosition(msite_anchor); - this->setFinishedFlag(); +void BaseBondGenerator::selectAnchorSite(int anchor) { + assert(0 <= anchor && anchor < mstructure->countSites()); + msite_anchor = anchor; + mr0 = mstructure->siteCartesianPosition(msite_anchor); + this->setFinishedFlag(); } - -void BaseBondGenerator::selectSiteRange(int first, int last) -{ - assert(0 <= first); - assert(last <= mstructure->countSites()); - msite_first = msite_all.begin() + first; - msite_last = msite_all.begin() + last; - this->setFinishedFlag(); +void BaseBondGenerator::selectSiteRange(int first, int last) { + assert(0 <= first); + assert(last <= mstructure->countSites()); + msite_first = msite_all.begin() + first; + msite_last = msite_all.begin() + last; + this->setFinishedFlag(); } - -void BaseBondGenerator::selectSites(const SiteIndices& selection) -{ - msite_selection = selection; - msite_first = msite_selection.begin(); - msite_last = msite_selection.end(); - this->setFinishedFlag(); +void BaseBondGenerator::selectSites(const SiteIndices& selection) { + msite_selection = selection; + msite_first = msite_selection.begin(); + msite_last = msite_selection.end(); + this->setFinishedFlag(); } - -void BaseBondGenerator::selectSites( - SiteIndices::const_iterator first, - SiteIndices::const_iterator last) -{ - msite_first = first; - msite_last = last; - this->setFinishedFlag(); +void BaseBondGenerator::selectSites(SiteIndices::const_iterator first, + SiteIndices::const_iterator last) { + msite_first = first; + msite_last = last; + this->setFinishedFlag(); } - -void BaseBondGenerator::setRmin(double rmin) -{ - if (rmin != mrmin) this->setFinishedFlag(); - mrmin = rmin; +void BaseBondGenerator::setRmin(double rmin) { + if (rmin != mrmin) this->setFinishedFlag(); + mrmin = rmin; } - -void BaseBondGenerator::setRmax(double rmax) -{ - if (rmax != mrmax) this->setFinishedFlag(); - mrmax = rmax; +void BaseBondGenerator::setRmax(double rmax) { + if (rmax != mrmax) this->setFinishedFlag(); + mrmax = rmax; } // data query -const double& BaseBondGenerator::getRmin() const -{ - return mrmin; -} - - -const double& BaseBondGenerator::getRmax() const -{ - return mrmax; -} - - -int BaseBondGenerator::site0() const -{ - return msite_anchor; -} - - -int BaseBondGenerator::site1() const -{ - return *msite_current; -} +const double& BaseBondGenerator::getRmin() const { return mrmin; } +const double& BaseBondGenerator::getRmax() const { return mrmax; } -int BaseBondGenerator::multiplicity() const -{ - return mstructure->siteMultiplicity(this->site0()); -} +int BaseBondGenerator::site0() const { return msite_anchor; } +int BaseBondGenerator::site1() const { return *msite_current; } -const R3::Vector& BaseBondGenerator::r0() const -{ - return mr0; +int BaseBondGenerator::multiplicity() const { + return mstructure->siteMultiplicity(this->site0()); } +const R3::Vector& BaseBondGenerator::r0() const { return mr0; } -const R3::Vector& BaseBondGenerator::r1() const -{ - return mr1; -} +const R3::Vector& BaseBondGenerator::r1() const { return mr1; } +const double& BaseBondGenerator::distance() const { return mdistance; } -const double& BaseBondGenerator::distance() const -{ - return mdistance; -} - +const R3::Vector& BaseBondGenerator::r01() const { return mr01; } -const R3::Vector& BaseBondGenerator::r01() const -{ - return mr01; +const R3::Matrix& BaseBondGenerator::Ucartesian0() const { + return mstructure->siteCartesianUij(this->site0()); } - -const R3::Matrix& BaseBondGenerator::Ucartesian0() const -{ - return mstructure->siteCartesianUij(this->site0()); -} - - -const R3::Matrix& BaseBondGenerator::Ucartesian1() const -{ - return mstructure->siteCartesianUij(this->site1()); +const R3::Matrix& BaseBondGenerator::Ucartesian1() const { + return mstructure->siteCartesianUij(this->site1()); } - -double BaseBondGenerator::msd() const -{ - static R3::Vector s; - s = this->r01(); - double msd0 = meanSquareDisplacement(this->Ucartesian0(), s, - mstructure->siteAnisotropy(this->site0())); - double msd1 = meanSquareDisplacement(this->Ucartesian1(), s, - mstructure->siteAnisotropy(this->site1())); - double rv = msd0 + msd1; - return rv; +double BaseBondGenerator::msd() const { + static R3::Vector s; + s = this->r01(); + double msd0 = meanSquareDisplacement( + this->Ucartesian0(), s, mstructure->siteAnisotropy(this->site0())); + double msd1 = meanSquareDisplacement( + this->Ucartesian1(), s, mstructure->siteAnisotropy(this->site1())); + double rv = msd0 + msd1; + return rv; } // Protected Methods --------------------------------------------------------- -bool BaseBondGenerator::iterateSymmetry() -{ - return false; -} - +bool BaseBondGenerator::iterateSymmetry() { return false; } -void BaseBondGenerator::rewindSymmetry() -{ - mr1 = mstructure->siteCartesianPosition(this->site1()); - this->updateDistance(); +void BaseBondGenerator::rewindSymmetry() { + mr1 = mstructure->siteCartesianPosition(this->site1()); + this->updateDistance(); } - -void BaseBondGenerator::getNextBond() -{ - if (this->iterateSymmetry()) return; - ++msite_current; - // avoid calling rewindSymmetry at an invalid site - if (!(this->finished())) this->rewindSymmetry(); +void BaseBondGenerator::getNextBond() { + if (this->iterateSymmetry()) return; + ++msite_current; + // avoid calling rewindSymmetry at an invalid site + if (!(this->finished())) this->rewindSymmetry(); } - -void BaseBondGenerator::updateDistance() -{ - if ((mdistance = fabs(mr01[0] = mr1[0] - mr0[0])) > mrmax) return; - if ((mdistance = fabs(mr01[1] = mr1[1] - mr0[1])) > mrmax) return; - if ((mdistance = fabs(mr01[2] = mr1[2] - mr0[2])) > mrmax) return; - mdistance = R3::norm(mr01); +void BaseBondGenerator::updateDistance() { + if ((mdistance = fabs(mr01[0] = mr1[0] - mr0[0])) > mrmax) return; + if ((mdistance = fabs(mr01[1] = mr1[1] - mr0[1])) > mrmax) return; + if ((mdistance = fabs(mr01[2] = mr1[2] - mr0[2])) > mrmax) return; + mdistance = R3::norm(mr01); } // Private Methods ----------------------------------------------------------- -void BaseBondGenerator::advanceWhileInvalid() -{ - while (!this->finished() && - (this->bondOutOfRange() || this->atSelfPair())) - { - this->getNextBond(); - } +void BaseBondGenerator::advanceWhileInvalid() { + while (!this->finished() && (this->bondOutOfRange() || this->atSelfPair())) { + this->getNextBond(); + } } // Private Methods ----------------------------------------------------------- -bool BaseBondGenerator::bondOutOfRange() const -{ - const double& d = this->distance(); - bool rv = (d < this->getRmin()) || (d > this->getRmax()); - return rv; +bool BaseBondGenerator::bondOutOfRange() const { + const double& d = this->distance(); + bool rv = (d < this->getRmin()) || (d > this->getRmax()); + return rv; } - -bool BaseBondGenerator::atSelfPair() const -{ - bool rv = eps_eq(this->distance(), 0.0); - return rv; +bool BaseBondGenerator::atSelfPair() const { + bool rv = eps_eq(this->distance(), 0.0); + return rv; } +void BaseBondGenerator::setFinishedFlag() { msite_current = msite_last; } -void BaseBondGenerator::setFinishedFlag() -{ - msite_current = msite_last; -} - -} // namespace srreal -} // namespace diffpy +} // namespace srreal +} // namespace diffpy // End of file diff --git a/src/diffpy/srreal/BaseBondGenerator.hpp b/src/diffpy/srreal/BaseBondGenerator.hpp index 2284cd30..a23d02ea 100644 --- a/src/diffpy/srreal/BaseBondGenerator.hpp +++ b/src/diffpy/srreal/BaseBondGenerator.hpp @@ -1,21 +1,21 @@ /***************************************************************************** -* -* 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.txt for license information. -* -****************************************************************************** -* -* class BaseBondGenerator -- semi-abstract class for a generation -* of all atom pairs containing specified anchor atom. -* -*****************************************************************************/ + * + * 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.txt for license information. + * + ****************************************************************************** + * + * class BaseBondGenerator -- semi-abstract class for a generation + * of all atom pairs containing specified anchor atom. + * + *****************************************************************************/ #ifndef BASEBONDGENERATOR_HPP_INCLUDED #define BASEBONDGENERATOR_HPP_INCLUDED @@ -31,79 +31,72 @@ namespace srreal { /// Use zero default for rmax so any misconfiguration of r-limits is obvious. const double DEFAULT_BONDGENERATOR_RMAX = 0.0; -class DLL_EXPORT BaseBondGenerator -{ - public: - - // constructor - BaseBondGenerator(StructureAdapterConstPtr); - virtual ~BaseBondGenerator() { } - - // methods - // loop control - virtual void rewind(); - bool finished() const; - void next(); - - // configuration - virtual void selectAnchorSite(int); - void selectSiteRange(int first, int last); - void selectSites(const SiteIndices&); - void selectSites( - SiteIndices::const_iterator first, - SiteIndices::const_iterator last); - virtual void setRmin(double); - virtual void setRmax(double); - - // get data - const double& getRmin() const; - const double& getRmax() const; - int site0() const; - int site1() const; - int multiplicity() const; - const R3::Vector& r0() const; - const R3::Vector& r1() const; - const double& distance() const; - const R3::Vector& r01() const; - virtual const R3::Matrix& Ucartesian0() const; - virtual const R3::Matrix& Ucartesian1() const; - double msd() const; - - protected: - - // data - int msite_anchor; - SiteIndices::const_iterator msite_first; - SiteIndices::const_iterator msite_last; - SiteIndices::const_iterator msite_current; - double mrmin; - double mrmax; - StructureAdapterConstPtr mstructure; - R3::Vector mr0; - R3::Vector mr1; - R3::Vector mr01; - double mdistance; - SiteIndices msite_all; - SiteIndices msite_selection; - - // methods - virtual bool iterateSymmetry(); - virtual void rewindSymmetry(); - virtual void getNextBond(); - void updateDistance(); - - private: - - // methods - void advanceWhileInvalid(); - bool bondOutOfRange() const; - bool atSelfPair() const; - void setFinishedFlag(); - +class DLL_EXPORT BaseBondGenerator { + public: + // constructor + BaseBondGenerator(StructureAdapterConstPtr); + virtual ~BaseBondGenerator() {} + + // methods + // loop control + virtual void rewind(); + bool finished() const; + void next(); + + // configuration + virtual void selectAnchorSite(int); + void selectSiteRange(int first, int last); + void selectSites(const SiteIndices&); + void selectSites(SiteIndices::const_iterator first, + SiteIndices::const_iterator last); + virtual void setRmin(double); + virtual void setRmax(double); + + // get data + const double& getRmin() const; + const double& getRmax() const; + int site0() const; + int site1() const; + int multiplicity() const; + const R3::Vector& r0() const; + const R3::Vector& r1() const; + const double& distance() const; + const R3::Vector& r01() const; + virtual const R3::Matrix& Ucartesian0() const; + virtual const R3::Matrix& Ucartesian1() const; + double msd() const; + + protected: + // data + int msite_anchor; + SiteIndices::const_iterator msite_first; + SiteIndices::const_iterator msite_last; + SiteIndices::const_iterator msite_current; + double mrmin; + double mrmax; + StructureAdapterConstPtr mstructure; + R3::Vector mr0; + R3::Vector mr1; + R3::Vector mr01; + double mdistance; + SiteIndices msite_all; + SiteIndices msite_selection; + + // methods + virtual bool iterateSymmetry(); + virtual void rewindSymmetry(); + virtual void getNextBond(); + void updateDistance(); + + private: + // methods + void advanceWhileInvalid(); + bool bondOutOfRange() const; + bool atSelfPair() const; + void setFinishedFlag(); }; - -} // namespace srreal -} // namespace diffpy +} // namespace srreal +} // namespace diffpy #endif // BASEBONDGENERATOR_HPP_INCLUDED diff --git a/src/diffpy/srreal/BaseDebyeSum.cpp b/src/diffpy/srreal/BaseDebyeSum.cpp index b60b33c5..3a133b72 100644 --- a/src/diffpy/srreal/BaseDebyeSum.cpp +++ b/src/diffpy/srreal/BaseDebyeSum.cpp @@ -1,20 +1,20 @@ /***************************************************************************** -* -* 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.txt for license information. -* -****************************************************************************** -* -* class BaseDebyeSum -- base class for concrete Debye sum calculators -* -*****************************************************************************/ + * + * 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.txt for license information. + * + ****************************************************************************** + * + * class BaseDebyeSum -- base class for concrete Debye sum calculators + * + *****************************************************************************/ #include #include @@ -29,8 +29,8 @@ using namespace std; using namespace diffpy::validators; -using diffpy::mathutils::eps_gt; using diffpy::mathutils::eps_eq; +using diffpy::mathutils::eps_gt; namespace diffpy { namespace srreal { @@ -42,256 +42,205 @@ namespace { /// Default cutoff for the Q-decreasing scale of the sine contributions. const double DEFAULT_DEBYE_PRECISION = 1e-6; -} // namespace +} // namespace // Constructor --------------------------------------------------------------- -BaseDebyeSum::BaseDebyeSum() : - mqmin(0.0), - mqmax(DEFAULT_QGRID_QMAX), - mqstep(DEFAULT_QGRID_QSTEP), - mdebyeprecision(DEFAULT_DEBYE_PRECISION) -{ - mstructure_cache.totaloccupancy = 0.0; - // default configuration - this->setPeakWidthModelByType("jeong"); - this->setEvaluatorType(OPTIMIZED); - // attributes - this->registerDoubleAttribute("debyeprecision", this, - &BaseDebyeSum::getDebyePrecision, - &BaseDebyeSum::setDebyePrecision); +BaseDebyeSum::BaseDebyeSum() + : mqmin(0.0), + mqmax(DEFAULT_QGRID_QMAX), + mqstep(DEFAULT_QGRID_QSTEP), + mdebyeprecision(DEFAULT_DEBYE_PRECISION) { + mstructure_cache.totaloccupancy = 0.0; + // default configuration + this->setPeakWidthModelByType("jeong"); + this->setEvaluatorType(OPTIMIZED); + // attributes + this->registerDoubleAttribute("debyeprecision", this, + &BaseDebyeSum::getDebyePrecision, + &BaseDebyeSum::setDebyePrecision); } // Public Methods ------------------------------------------------------------ // PairQuantity overloads -eventticker::EventTicker& BaseDebyeSum::ticker() const -{ - mticker.updateFrom(this->PeakWidthModelOwner::ticker()); - return mticker; +eventticker::EventTicker& BaseDebyeSum::ticker() const { + mticker.updateFrom(this->PeakWidthModelOwner::ticker()); + return mticker; } // results -QuantityType BaseDebyeSum::getF() const -{ - QuantityType rv = this->value(); - const double& totocc = mstructure_cache.totaloccupancy; - const int npts = pdfutils_qmaxSteps(this); - for (int kq = pdfutils_qminSteps(this); kq < npts; ++kq) - { - double sfavg = this->sfAverageAtkQ(kq); - double fscale = (sfavg * totocc) == 0 ? 0.0 : - 1.0 / (sfavg * sfavg * totocc); - rv[kq] *= fscale; - } - return rv; +QuantityType BaseDebyeSum::getF() const { + QuantityType rv = this->value(); + const double& totocc = mstructure_cache.totaloccupancy; + const int npts = pdfutils_qmaxSteps(this); + for (int kq = pdfutils_qminSteps(this); kq < npts; ++kq) { + double sfavg = this->sfAverageAtkQ(kq); + double fscale = + (sfavg * totocc) == 0 ? 0.0 : 1.0 / (sfavg * sfavg * totocc); + rv[kq] *= fscale; + } + return rv; } // Q-range methods -QuantityType BaseDebyeSum::getQgrid() const -{ - return pdfutils_getQgrid(this); -} +QuantityType BaseDebyeSum::getQgrid() const { return pdfutils_getQgrid(this); } // Q-range configuration -void BaseDebyeSum::setQmin(double qmin) -{ - ensureNonNegative("Qmin", qmin); - if (mqmin != qmin) mticker.click(); - mqmin = qmin; -} - - -const double& BaseDebyeSum::getQmin() const -{ - return mqmin; -} - - -void BaseDebyeSum::setQmax(double qmax) -{ - ensureNonNegative("Qmax", qmax); - if (mqmax != qmax) mticker.click(); - mqmax = qmax; -} - - -const double& BaseDebyeSum::getQmax() const -{ - return mqmax; +void BaseDebyeSum::setQmin(double qmin) { + ensureNonNegative("Qmin", qmin); + if (mqmin != qmin) mticker.click(); + mqmin = qmin; } +const double& BaseDebyeSum::getQmin() const { return mqmin; } -void BaseDebyeSum::setQstep(double qstep) -{ - ensureEpsilonPositive("Qstep", qstep); - if (mqstep != qstep) mticker.click(); - mqstep = qstep; +void BaseDebyeSum::setQmax(double qmax) { + ensureNonNegative("Qmax", qmax); + if (mqmax != qmax) mticker.click(); + mqmax = qmax; } +const double& BaseDebyeSum::getQmax() const { return mqmax; } -const double& BaseDebyeSum::getQstep() const -{ - return mqstep; +void BaseDebyeSum::setQstep(double qstep) { + ensureEpsilonPositive("Qstep", qstep); + if (mqstep != qstep) mticker.click(); + mqstep = qstep; } +const double& BaseDebyeSum::getQstep() const { return mqstep; } -void BaseDebyeSum::setDebyePrecision(double precision) -{ - if (mdebyeprecision != precision) mticker.click(); - mdebyeprecision = precision; +void BaseDebyeSum::setDebyePrecision(double precision) { + if (mdebyeprecision != precision) mticker.click(); + mdebyeprecision = precision; } - -const double& BaseDebyeSum::getDebyePrecision() const -{ - return mdebyeprecision; +const double& BaseDebyeSum::getDebyePrecision() const { + return mdebyeprecision; } // Protected Methods --------------------------------------------------------- // PairQuantity overloads -void BaseDebyeSum::resetValue() -{ - this->cacheStructureData(); - this->resizeValue(pdfutils_qmaxSteps(this)); - this->PairQuantity::resetValue(); +void BaseDebyeSum::resetValue() { + this->cacheStructureData(); + this->resizeValue(pdfutils_qmaxSteps(this)); + this->PairQuantity::resetValue(); } - void BaseDebyeSum::addPairContribution(const BaseBondGenerator& bnds, - int summationscale) -{ - const double dist = bnds.distance(); - if (eps_eq(0.0, dist)) return; - // calculate sigma parameter for the Debye-Waller dampign Gaussian - const double fwhm = this->getPeakWidthModel()->calculate(bnds); - const double fwhmtosigma = 1.0 / (2 * sqrt(2 * M_LN2)); - const double dwsigma = fwhmtosigma * fwhm; - const int nqpts = pdfutils_qmaxSteps(this); - const int smscale = summationscale * bnds.multiplicity(); - const double& sineprec = this->getDebyePrecision(); - for (int kq = pdfutils_qminSteps(this); kq < nqpts; ++kq) - { - const double q = kq * this->getQstep(); - const double dwscale = exp(-0.5 * pow(dwsigma * q, 2)); - const double sinescale = smscale * dwscale * - this->sfSiteAtkQ(bnds.site0(), kq) * - this->sfSiteAtkQ(bnds.site1(), kq) / dist; - if (eps_eq(0.0, sinescale, sineprec)) break; - mvalue[kq] += sinescale * sin(q * dist); - } -} - - -void BaseDebyeSum::stashPartialValue() -{ - mdbsumstash = this->value(); -} - - -void BaseDebyeSum::restorePartialValue() -{ - assert(mdbsumstash.size() == mvalue.size()); - mvalue.swap(mdbsumstash); - mdbsumstash.clear(); -} - - -double BaseDebyeSum::sfSiteAtQ(int siteidx, const double& q) const -{ - return 1.0; + int summationscale) { + const double dist = bnds.distance(); + if (eps_eq(0.0, dist)) return; + // calculate sigma parameter for the Debye-Waller dampign Gaussian + const double fwhm = this->getPeakWidthModel()->calculate(bnds); + const double fwhmtosigma = 1.0 / (2 * sqrt(2 * M_LN2)); + const double dwsigma = fwhmtosigma * fwhm; + const int nqpts = pdfutils_qmaxSteps(this); + const int smscale = summationscale * bnds.multiplicity(); + const double& sineprec = this->getDebyePrecision(); + for (int kq = pdfutils_qminSteps(this); kq < nqpts; ++kq) { + const double q = kq * this->getQstep(); + const double dwscale = exp(-0.5 * pow(dwsigma * q, 2)); + const double sinescale = smscale * dwscale * + this->sfSiteAtkQ(bnds.site0(), kq) * + this->sfSiteAtkQ(bnds.site1(), kq) / dist; + if (eps_eq(0.0, sinescale, sineprec)) break; + mvalue[kq] += sinescale * sin(q * dist); + } +} + +void BaseDebyeSum::stashPartialValue() { mdbsumstash = this->value(); } + +void BaseDebyeSum::restorePartialValue() { + assert(mdbsumstash.size() == mvalue.size()); + mvalue.swap(mdbsumstash); + mdbsumstash.clear(); +} + +double BaseDebyeSum::sfSiteAtQ(int siteidx, const double& q) const { + return 1.0; } // Private Methods ----------------------------------------------------------- -double BaseDebyeSum::sfSiteAtkQ(int siteidx, int kq) const -{ - assert(0 <= siteidx && siteidx < int(mstructure_cache.typeofsite.size())); - int typeidx = mstructure_cache.typeofsite[siteidx]; - assert(typeidx < int(mstructure_cache.sftypeatkq.size())); - const QuantityType& sfarray = mstructure_cache.sftypeatkq[typeidx]; - assert(0 <= kq && kq < int(sfarray.size())); - return sfarray[kq]; -} - - -double BaseDebyeSum::sfAverageAtkQ(int kq) const -{ - assert(kq < int(mstructure_cache.sfaverageatkq.size())); - return mstructure_cache.sfaverageatkq[kq]; -} - - -void BaseDebyeSum::cacheStructureData() -{ - using std::placeholders::_1; - int cntsites = this->countSites(); - const int nqpts = pdfutils_qmaxSteps(this); - QuantityType zeros(nqpts, 0.0); - unordered_map atomtypeidx; - // sftypeatkq - mstructure_cache.typeofsite.clear(); - mstructure_cache.typeofsite.reserve(cntsites); - mstructure_cache.sftypeatkq.clear(); - for (int siteidx = 0; siteidx < cntsites; ++siteidx) - { - const string& smbl = mstructure->siteAtomType(siteidx); - if (!atomtypeidx.count(smbl)) - { - atomtypeidx.insert(make_pair(smbl, int(atomtypeidx.size()))); - } - int tpidx = atomtypeidx[smbl]; - mstructure_cache.typeofsite.push_back(tpidx); - // do nothing if the type has been already cached - if (tpidx < int(mstructure_cache.sftypeatkq.size())) continue; - assert(tpidx == int(mstructure_cache.sftypeatkq.size())); - // here we need to build a new array - mstructure_cache.sftypeatkq.push_back(zeros); - QuantityType& sfarray = mstructure_cache.sftypeatkq.back(); - for (int kq = pdfutils_qminSteps(this); kq < nqpts; ++kq) - { - double q = this->getQstep() * kq; - sfarray[kq] = this->sfSiteAtQ(siteidx, q); - } +double BaseDebyeSum::sfSiteAtkQ(int siteidx, int kq) const { + assert(0 <= siteidx && siteidx < int(mstructure_cache.typeofsite.size())); + int typeidx = mstructure_cache.typeofsite[siteidx]; + assert(typeidx < int(mstructure_cache.sftypeatkq.size())); + const QuantityType& sfarray = mstructure_cache.sftypeatkq[typeidx]; + assert(0 <= kq && kq < int(sfarray.size())); + return sfarray[kq]; +} + +double BaseDebyeSum::sfAverageAtkQ(int kq) const { + assert(kq < int(mstructure_cache.sfaverageatkq.size())); + return mstructure_cache.sfaverageatkq[kq]; +} + +void BaseDebyeSum::cacheStructureData() { + using std::placeholders::_1; + int cntsites = this->countSites(); + const int nqpts = pdfutils_qmaxSteps(this); + QuantityType zeros(nqpts, 0.0); + unordered_map atomtypeidx; + // sftypeatkq + mstructure_cache.typeofsite.clear(); + mstructure_cache.typeofsite.reserve(cntsites); + mstructure_cache.sftypeatkq.clear(); + for (int siteidx = 0; siteidx < cntsites; ++siteidx) { + const string& smbl = mstructure->siteAtomType(siteidx); + if (!atomtypeidx.count(smbl)) { + atomtypeidx.insert(make_pair(smbl, int(atomtypeidx.size()))); } - assert(cntsites == int(mstructure_cache.typeofsite.size())); - assert(atomtypeidx.size() == mstructure_cache.sftypeatkq.size()); - // totaloccupancy - mstructure_cache.totaloccupancy = mstructure->totalOccupancy(); - // sfaverageatkq - QuantityType& sfak = mstructure_cache.sfaverageatkq; - sfak = zeros; - int ntps = mstructure_cache.sftypeatkq.size(); - vector tpmultipl(ntps, 0); - for (int siteidx = 0; siteidx < cntsites; ++siteidx) - { - int tpidx = mstructure_cache.typeofsite[siteidx]; - assert(tpidx < ntps); - tpmultipl[tpidx] += mstructure->siteMultiplicity(siteidx); + int tpidx = atomtypeidx[smbl]; + mstructure_cache.typeofsite.push_back(tpidx); + // do nothing if the type has been already cached + if (tpidx < int(mstructure_cache.sftypeatkq.size())) continue; + assert(tpidx == int(mstructure_cache.sftypeatkq.size())); + // here we need to build a new array + mstructure_cache.sftypeatkq.push_back(zeros); + QuantityType& sfarray = mstructure_cache.sftypeatkq.back(); + for (int kq = pdfutils_qminSteps(this); kq < nqpts; ++kq) { + double q = this->getQstep() * kq; + sfarray[kq] = this->sfSiteAtQ(siteidx, q); } - for (int tpidx = 0; tpidx < ntps; ++tpidx) - { - QuantityType& sfarray = mstructure_cache.sftypeatkq[tpidx]; - const int multipl = tpmultipl[tpidx]; - for (int kq = pdfutils_qminSteps(this); kq < nqpts; ++kq) - { - sfak[kq] += sfarray[kq] * multipl; - } + } + assert(cntsites == int(mstructure_cache.typeofsite.size())); + assert(atomtypeidx.size() == mstructure_cache.sftypeatkq.size()); + // totaloccupancy + mstructure_cache.totaloccupancy = mstructure->totalOccupancy(); + // sfaverageatkq + QuantityType& sfak = mstructure_cache.sfaverageatkq; + sfak = zeros; + int ntps = mstructure_cache.sftypeatkq.size(); + vector tpmultipl(ntps, 0); + for (int siteidx = 0; siteidx < cntsites; ++siteidx) { + int tpidx = mstructure_cache.typeofsite[siteidx]; + assert(tpidx < ntps); + tpmultipl[tpidx] += mstructure->siteMultiplicity(siteidx); + } + for (int tpidx = 0; tpidx < ntps; ++tpidx) { + QuantityType& sfarray = mstructure_cache.sftypeatkq[tpidx]; + const int multipl = tpmultipl[tpidx]; + for (int kq = pdfutils_qminSteps(this); kq < nqpts; ++kq) { + sfak[kq] += sfarray[kq] * multipl; } - const double& totocc = mstructure_cache.totaloccupancy; - const double tosc = eps_gt(totocc, 0.0) ? (1.0 / totocc) : 1.0; - transform(sfak.begin(), sfak.end(), sfak.begin(), + } + const double& totocc = mstructure_cache.totaloccupancy; + const double tosc = eps_gt(totocc, 0.0) ? (1.0 / totocc) : 1.0; + transform(sfak.begin(), sfak.end(), sfak.begin(), bind(multiplies(), tosc, _1)); } -} // namespace srreal -} // namespace diffpy +} // namespace srreal +} // namespace diffpy // Serialization ------------------------------------------------------------- diff --git a/src/diffpy/srreal/BaseDebyeSum.hpp b/src/diffpy/srreal/BaseDebyeSum.hpp index 79cee73c..63197658 100644 --- a/src/diffpy/srreal/BaseDebyeSum.hpp +++ b/src/diffpy/srreal/BaseDebyeSum.hpp @@ -1,20 +1,20 @@ /***************************************************************************** -* -* 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.txt for license information. -* -****************************************************************************** -* -* class BaseDebyeSum -- base class for concrete Debye sum calculators -* -*****************************************************************************/ + * + * 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.txt for license information. + * + ****************************************************************************** + * + * class BaseDebyeSum -- base class for concrete Debye sum calculators + * + *****************************************************************************/ #ifndef BASEDEBYESUM_HPP_INCLUDED #define BASEDEBYESUM_HPP_INCLUDED @@ -28,95 +28,89 @@ namespace diffpy { namespace srreal { -class DLL_EXPORT BaseDebyeSum : - public PairQuantity, - public PeakWidthModelOwner -{ - public: - - // constructor - BaseDebyeSum(); - - // PairQuantity overloads - virtual eventticker::EventTicker& ticker() const; - - // results - /// F values on a full Q-grid starting at 0 - QuantityType getF() const; - - // Q-range methods - /// Full Q-grid starting at 0 - QuantityType getQgrid() const; - // Q-range configuration - void setQmin(double); - const double& getQmin() const; - void setQmax(double); - const double& getQmax() const; - void setQstep(double); - const double& getQstep() const; - - // Summation cutoff due to Q-dependent pair scaling or large distance - /// set relative cutoff value for Debye sum contribution - void setDebyePrecision(double); - /// return relative cutoff value for Debye sum contribution - const double& getDebyePrecision() const; - - protected: - - // PairQuantity overloads - virtual void resetValue(); - virtual void addPairContribution(const BaseBondGenerator&, int); - // support for PQEvaluatorOptimized - virtual void stashPartialValue(); - virtual void restorePartialValue(); - - // own methods - virtual double sfSiteAtQ(int, const double& Q) const; - - private: - - // methods - /// cache structure factors data for a quick access during summation - double sfSiteAtkQ(int siteidx, int kq) const; - double sfAverageAtkQ(int kq) const; - void cacheStructureData(); - - // data - // configuration - double mqmin; - double mqmax; - double mqstep; - double mdebyeprecision; - struct { - std::vector typeofsite; - std::vector sftypeatkq; - QuantityType sfaverageatkq; - double totaloccupancy; - } mstructure_cache; - QuantityType mdbsumstash; - - // serialization - friend class boost::serialization::access; - template - void serialize(Archive& ar, const unsigned int version) - { - using boost::serialization::base_object; - ar & base_object(*this); - ar & base_object(*this); - ar & mqmin; - ar & mqmax; - ar & mqstep; - ar & mdebyeprecision; - ar & mstructure_cache.typeofsite; - ar & mstructure_cache.sftypeatkq; - ar & mstructure_cache.sfaverageatkq; - ar & mstructure_cache.totaloccupancy; - } +class DLL_EXPORT BaseDebyeSum : public PairQuantity, + public PeakWidthModelOwner { + public: + // constructor + BaseDebyeSum(); + + // PairQuantity overloads + virtual eventticker::EventTicker& ticker() const; + + // results + /// F values on a full Q-grid starting at 0 + QuantityType getF() const; + + // Q-range methods + /// Full Q-grid starting at 0 + QuantityType getQgrid() const; + // Q-range configuration + void setQmin(double); + const double& getQmin() const; + void setQmax(double); + const double& getQmax() const; + void setQstep(double); + const double& getQstep() const; + + // Summation cutoff due to Q-dependent pair scaling or large distance + /// set relative cutoff value for Debye sum contribution + void setDebyePrecision(double); + /// return relative cutoff value for Debye sum contribution + const double& getDebyePrecision() const; + + protected: + // PairQuantity overloads + virtual void resetValue(); + virtual void addPairContribution(const BaseBondGenerator&, int); + // support for PQEvaluatorOptimized + virtual void stashPartialValue(); + virtual void restorePartialValue(); + + // own methods + virtual double sfSiteAtQ(int, const double& Q) const; + + private: + // methods + /// cache structure factors data for a quick access during summation + double sfSiteAtkQ(int siteidx, int kq) const; + double sfAverageAtkQ(int kq) const; + void cacheStructureData(); + + // data + // configuration + double mqmin; + double mqmax; + double mqstep; + double mdebyeprecision; + struct { + std::vector typeofsite; + std::vector sftypeatkq; + QuantityType sfaverageatkq; + double totaloccupancy; + } mstructure_cache; + QuantityType mdbsumstash; + + // serialization + friend class boost::serialization::access; + template + void serialize(Archive& ar, const unsigned int version) { + using boost::serialization::base_object; + ar& base_object(*this); + ar& base_object(*this); + ar & mqmin; + ar & mqmax; + ar & mqstep; + ar & mdebyeprecision; + ar & mstructure_cache.typeofsite; + ar & mstructure_cache.sftypeatkq; + ar & mstructure_cache.sfaverageatkq; + ar & mstructure_cache.totaloccupancy; + } }; // class BaseDebyeSum -} // namespace srreal -} // namespace diffpy +} // namespace srreal +} // namespace diffpy // Serialization ------------------------------------------------------------- diff --git a/src/diffpy/srreal/BondCalculator.cpp b/src/diffpy/srreal/BondCalculator.cpp index 958a0872..730e8bb6 100644 --- a/src/diffpy/srreal/BondCalculator.cpp +++ b/src/diffpy/srreal/BondCalculator.cpp @@ -1,20 +1,20 @@ /***************************************************************************** -* -* 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.txt for license information. -* -****************************************************************************** -* -* class BondCalculator -- bond distance calculator -* -*****************************************************************************/ + * + * 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.txt for license information. + * + ****************************************************************************** + * + * class BondCalculator -- bond distance calculator + * + *****************************************************************************/ #include #include @@ -36,263 +36,214 @@ namespace { const double DEFAULT_BONDCALCULATOR_RMAX = 5.0; -} // namespace +} // namespace class BondOp { - - public: - - static bool compare( - const BondCalculator::BondEntry& be0, - const BondCalculator::BondEntry& be1) - { - if (be0.distance < be1.distance) return true; - if (be0.distance > be1.distance) return false; - if (be0.site0 < be1.site0) return true; - if (be0.site0 > be1.site0) return false; - if (be0.site1 < be1.site1) return true; - if (be0.site1 > be1.site1) return false; - if (be0.direction0 < be1.direction0) return true; - if (be0.direction0 > be1.direction0) return false; - if (be0.direction1 < be1.direction1) return true; - if (be0.direction1 > be1.direction1) return false; - if (be0.direction2 < be1.direction2) return true; - if (be0.direction2 > be1.direction2) return false; - return false; - } - - - static bool reverse_compare( - const BondCalculator::BondEntry& be0, - const BondCalculator::BondEntry& be1) - { - return compare(be1, be0); - } - - - static void bmerge( - BondCalculator::BondDataStorage& dstbonds, - const BondCalculator::BondDataStorage& srcbonds) - { - dstbonds.resize(dstbonds.size() + srcbonds.size()); - merge(dstbonds.rbegin() + srcbonds.size(), dstbonds.rend(), - srcbonds.rbegin(), srcbonds.rend(), - dstbonds.rbegin(), reverse_compare); - } - - - static BondCalculator::BondEntry entryFrom( - const BaseBondGenerator& bnds) - { - BondCalculator::BondEntry rv; - rv.distance = bnds.distance(); - rv.site0 = bnds.site0(); - rv.site1 = bnds.site1(); - rv.direction0 = bnds.r01()[0]; - rv.direction1 = bnds.r01()[1]; - rv.direction2 = bnds.r01()[2]; - return rv; - } - + public: + static bool compare(const BondCalculator::BondEntry& be0, + const BondCalculator::BondEntry& be1) { + if (be0.distance < be1.distance) return true; + if (be0.distance > be1.distance) return false; + if (be0.site0 < be1.site0) return true; + if (be0.site0 > be1.site0) return false; + if (be0.site1 < be1.site1) return true; + if (be0.site1 > be1.site1) return false; + if (be0.direction0 < be1.direction0) return true; + if (be0.direction0 > be1.direction0) return false; + if (be0.direction1 < be1.direction1) return true; + if (be0.direction1 > be1.direction1) return false; + if (be0.direction2 < be1.direction2) return true; + if (be0.direction2 > be1.direction2) return false; + return false; + } + + static bool reverse_compare(const BondCalculator::BondEntry& be0, + const BondCalculator::BondEntry& be1) { + return compare(be1, be0); + } + + static void bmerge(BondCalculator::BondDataStorage& dstbonds, + const BondCalculator::BondDataStorage& srcbonds) { + dstbonds.resize(dstbonds.size() + srcbonds.size()); + merge(dstbonds.rbegin() + srcbonds.size(), dstbonds.rend(), + srcbonds.rbegin(), srcbonds.rend(), dstbonds.rbegin(), + reverse_compare); + } + + static BondCalculator::BondEntry entryFrom(const BaseBondGenerator& bnds) { + BondCalculator::BondEntry rv; + rv.distance = bnds.distance(); + rv.site0 = bnds.site0(); + rv.site1 = bnds.site1(); + rv.direction0 = bnds.r01()[0]; + rv.direction1 = bnds.r01()[1]; + rv.direction2 = bnds.r01()[2]; + return rv; + } }; // class BondOp // Constructor --------------------------------------------------------------- -BondCalculator::BondCalculator() -{ - this->setRmax(DEFAULT_BONDCALCULATOR_RMAX); - this->setEvaluatorType(OPTIMIZED); - mevaluator->setFlag(USEFULLSUM, true); - mevaluator->setFlag(FIXEDSITEINDEX, true); +BondCalculator::BondCalculator() { + this->setRmax(DEFAULT_BONDCALCULATOR_RMAX); + this->setEvaluatorType(OPTIMIZED); + mevaluator->setFlag(USEFULLSUM, true); + mevaluator->setFlag(FIXEDSITEINDEX, true); } // Public Methods ------------------------------------------------------------ -QuantityType BondCalculator::distances() const -{ - QuantityType rv; - rv.reserve(this->count()); - BondDataStorage::const_iterator bi = mbonds.begin(); - for (; bi != mbonds.end(); bi++) rv.push_back(bi->distance); - return rv; +QuantityType BondCalculator::distances() const { + QuantityType rv; + rv.reserve(this->count()); + BondDataStorage::const_iterator bi = mbonds.begin(); + for (; bi != mbonds.end(); bi++) rv.push_back(bi->distance); + return rv; } - -vector BondCalculator::directions() const -{ - vector rv; - rv.reserve(this->count()); - BondDataStorage::const_iterator bi = mbonds.begin(); - for (; bi != mbonds.end(); ++bi) - { - R3::Vector dir(bi->direction0, bi->direction1, bi->direction2); - rv.push_back(dir); - } - return rv; +vector BondCalculator::directions() const { + vector rv; + rv.reserve(this->count()); + BondDataStorage::const_iterator bi = mbonds.begin(); + for (; bi != mbonds.end(); ++bi) { + R3::Vector dir(bi->direction0, bi->direction1, bi->direction2); + rv.push_back(dir); + } + return rv; } - -SiteIndices BondCalculator::sites0() const -{ - SiteIndices rv; - rv.reserve(this->count()); - BondDataStorage::const_iterator bi = mbonds.begin(); - for (; bi != mbonds.end(); ++bi) rv.push_back(bi->site0); - return rv; +SiteIndices BondCalculator::sites0() const { + SiteIndices rv; + rv.reserve(this->count()); + BondDataStorage::const_iterator bi = mbonds.begin(); + for (; bi != mbonds.end(); ++bi) rv.push_back(bi->site0); + return rv; } - -SiteIndices BondCalculator::sites1() const -{ - SiteIndices rv; - rv.reserve(this->count()); - BondDataStorage::const_iterator bi = mbonds.begin(); - for (; bi != mbonds.end(); ++bi) rv.push_back(bi->site1); - return rv; +SiteIndices BondCalculator::sites1() const { + SiteIndices rv; + rv.reserve(this->count()); + BondDataStorage::const_iterator bi = mbonds.begin(); + for (; bi != mbonds.end(); ++bi) rv.push_back(bi->site1); + return rv; } - -vector BondCalculator::types0() const -{ - return siteIndicesToTypes(mstructure, this->sites0()); +vector BondCalculator::types0() const { + return siteIndicesToTypes(mstructure, this->sites0()); } - -vector BondCalculator::types1() const -{ - return siteIndicesToTypes(mstructure, this->sites1()); +vector BondCalculator::types1() const { + return siteIndicesToTypes(mstructure, this->sites1()); } - -void BondCalculator::filterCone(R3::Vector coneaxis, double degrees) -{ - using namespace diffpy::validators; - double nmconeaxis = R3::norm(coneaxis); - ensureEpsilonPositive("magnitude of cone vector", nmconeaxis); - coneaxis /= nmconeaxis; - mfilter_directions.push_back(coneaxis); - mfilter_degrees.push_back(degrees); - mticker.click(); +void BondCalculator::filterCone(R3::Vector coneaxis, double degrees) { + using namespace diffpy::validators; + double nmconeaxis = R3::norm(coneaxis); + ensureEpsilonPositive("magnitude of cone vector", nmconeaxis); + coneaxis /= nmconeaxis; + mfilter_directions.push_back(coneaxis); + mfilter_degrees.push_back(degrees); + mticker.click(); } - -void BondCalculator::filterOff() -{ - if (!mfilter_directions.empty()) mticker.click(); - mfilter_directions.clear(); - mfilter_degrees.clear(); +void BondCalculator::filterOff() { + if (!mfilter_directions.empty()) mticker.click(); + mfilter_directions.clear(); + mfilter_degrees.clear(); } // PairQuantity overloads -string BondCalculator::getParallelData() const -{ - ostringstream storage(ios::binary); - diffpy::serialization::oarchive oa(storage, ios::binary); - oa << mpopbonds << maddbonds; - return storage.str(); +string BondCalculator::getParallelData() const { + ostringstream storage(ios::binary); + diffpy::serialization::oarchive oa(storage, ios::binary); + oa << mpopbonds << maddbonds; + return storage.str(); } // Protected Methods --------------------------------------------------------- -void BondCalculator::resetValue() -{ - mvalue.clear(); - mbonds.clear(); - maddbonds.clear(); - mpopbonds.clear(); - this->PairQuantity::resetValue(); -} - - -void BondCalculator::addPairContribution( - const BaseBondGenerator& bnds, - int summationscale) -{ - assert(summationscale == +1 || summationscale == -1); - static R3::Vector ru01; - const R3::Vector& r01 = bnds.r01(); - ru01 = r01 / bnds.distance(); - if (!(this->checkConeFilters(ru01))) return; - BondDataStorage& bes = (summationscale > 0) ? maddbonds : mpopbonds; - bes.push_back(BondOp::entryFrom(bnds)); -} - - -void BondCalculator::executeParallelMerge(const std::string& pdata) -{ - istringstream storage(pdata, ios::binary); - diffpy::serialization::iarchive ia(storage, ios::binary); - BondDataStorage bpop, badd; - ia >> bpop >> badd; - BondOp::bmerge(mpopbonds, bpop); - BondOp::bmerge(maddbonds, badd); -} - - -void BondCalculator::finishValue() -{ - // filter-out entries marked for removal - assert(mpopbonds.size() <= mbonds.size()); - sort(mpopbonds.begin(), mpopbonds.end(), BondOp::compare); - sort(maddbonds.begin(), maddbonds.end(), BondOp::compare); - if (mevaluator->isParallel()) return; - BondDataStorage::iterator last; - last = set_difference(mbonds.begin(), mbonds.end(), - mpopbonds.begin(), mpopbonds.end(), - mbonds.begin(), BondOp::compare); - mbonds.erase(last, mbonds.end()); - if (mbonds.empty()) mbonds.swap(maddbonds); - else BondOp::bmerge(mbonds, maddbonds); - mvalue = this->distances(); - mpopbonds.clear(); - maddbonds.clear(); -} - - -void BondCalculator::stashPartialValue() -{ - mstashedvalue.bonds.swap(mbonds); - mstashedvalue.popbonds.swap(mpopbonds); - // No need to stash maddbonds as they are evaluated after partial value. -} - - -void BondCalculator::restorePartialValue() -{ - mbonds.swap(mstashedvalue.bonds); - mpopbonds.swap(mstashedvalue.popbonds); - mstashedvalue.bonds.clear(); - mstashedvalue.popbonds.clear(); +void BondCalculator::resetValue() { + mvalue.clear(); + mbonds.clear(); + maddbonds.clear(); + mpopbonds.clear(); + this->PairQuantity::resetValue(); +} + +void BondCalculator::addPairContribution(const BaseBondGenerator& bnds, + int summationscale) { + assert(summationscale == +1 || summationscale == -1); + static R3::Vector ru01; + const R3::Vector& r01 = bnds.r01(); + ru01 = r01 / bnds.distance(); + if (!(this->checkConeFilters(ru01))) return; + BondDataStorage& bes = (summationscale > 0) ? maddbonds : mpopbonds; + bes.push_back(BondOp::entryFrom(bnds)); +} + +void BondCalculator::executeParallelMerge(const std::string& pdata) { + istringstream storage(pdata, ios::binary); + diffpy::serialization::iarchive ia(storage, ios::binary); + BondDataStorage bpop, badd; + ia >> bpop >> badd; + BondOp::bmerge(mpopbonds, bpop); + BondOp::bmerge(maddbonds, badd); +} + +void BondCalculator::finishValue() { + // filter-out entries marked for removal + assert(mpopbonds.size() <= mbonds.size()); + sort(mpopbonds.begin(), mpopbonds.end(), BondOp::compare); + sort(maddbonds.begin(), maddbonds.end(), BondOp::compare); + if (mevaluator->isParallel()) return; + BondDataStorage::iterator last; + last = set_difference(mbonds.begin(), mbonds.end(), mpopbonds.begin(), + mpopbonds.end(), mbonds.begin(), BondOp::compare); + mbonds.erase(last, mbonds.end()); + if (mbonds.empty()) + mbonds.swap(maddbonds); + else + BondOp::bmerge(mbonds, maddbonds); + mvalue = this->distances(); + mpopbonds.clear(); + maddbonds.clear(); +} + +void BondCalculator::stashPartialValue() { + mstashedvalue.bonds.swap(mbonds); + mstashedvalue.popbonds.swap(mpopbonds); + // No need to stash maddbonds as they are evaluated after partial value. +} + +void BondCalculator::restorePartialValue() { + mbonds.swap(mstashedvalue.bonds); + mpopbonds.swap(mstashedvalue.popbonds); + mstashedvalue.bonds.clear(); + mstashedvalue.popbonds.clear(); } // Private Methods ----------------------------------------------------------- -int BondCalculator::count() const -{ - return mbonds.size(); -} - +int BondCalculator::count() const { return mbonds.size(); } -bool BondCalculator::checkConeFilters(const R3::Vector& ru01) const -{ - using diffpy::mathutils::eps_eq; - if (mfilter_directions.empty()) return true; - assert(eps_eq(1.0, R3::norm(ru01))); - vector::const_iterator coneax = mfilter_directions.begin(); - vector::const_iterator deg = mfilter_degrees.begin(); - for (; coneax != mfilter_directions.end(); ++coneax, ++deg) - { - if (180.0 <= *deg) return true; - double angledegrees = 180.0 / M_PI * acos(R3::dot(ru01, *coneax)); - if (angledegrees <= *deg) return true; - } - return false; +bool BondCalculator::checkConeFilters(const R3::Vector& ru01) const { + using diffpy::mathutils::eps_eq; + if (mfilter_directions.empty()) return true; + assert(eps_eq(1.0, R3::norm(ru01))); + vector::const_iterator coneax = mfilter_directions.begin(); + vector::const_iterator deg = mfilter_degrees.begin(); + for (; coneax != mfilter_directions.end(); ++coneax, ++deg) { + if (180.0 <= *deg) return true; + double angledegrees = 180.0 / M_PI * acos(R3::dot(ru01, *coneax)); + if (angledegrees <= *deg) return true; + } + return false; } -} // namespace srreal -} // namespace diffpy +} // namespace srreal +} // namespace diffpy // Serialization ------------------------------------------------------------- diff --git a/src/diffpy/srreal/BondCalculator.hpp b/src/diffpy/srreal/BondCalculator.hpp index c180f2ba..fd28f5b5 100644 --- a/src/diffpy/srreal/BondCalculator.hpp +++ b/src/diffpy/srreal/BondCalculator.hpp @@ -1,20 +1,20 @@ /***************************************************************************** -* -* 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.txt for license information. -* -****************************************************************************** -* -* class BondCalculator -- bond distance calculator -* -*****************************************************************************/ + * + * 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.txt for license information. + * + ****************************************************************************** + * + * class BondCalculator -- bond distance calculator + * + *****************************************************************************/ #ifndef BONDCALCULATOR_HPP_INCLUDED #define BONDCALCULATOR_HPP_INCLUDED @@ -26,109 +26,97 @@ namespace diffpy { namespace srreal { -class DLL_EXPORT BondCalculator : public PairQuantity -{ - public: - - // constructor - BondCalculator(); - - // methods - template QuantityType operator()(const T&); - QuantityType distances() const; - std::vector directions() const; - SiteIndices sites0() const; - SiteIndices sites1() const; - std::vector types0() const; - std::vector types1() const; - void filterCone(R3::Vector coneaxis, double degrees); - void filterOff(); - - // PairQuantity overloads - virtual std::string getParallelData() const; - - protected: - - // PairQuantity overloads - virtual void resetValue(); - virtual void addPairContribution(const BaseBondGenerator&, int); - virtual void executeParallelMerge(const std::string& pdata); - virtual void finishValue(); - - // support for PQEvaluatorOptimized - virtual void stashPartialValue(); - virtual void restorePartialValue(); - - friend class BondOp; - class BondEntry { - - public: - - double distance; - int site0; - int site1; - double direction0; - double direction1; - double direction2; - - private: - - friend class boost::serialization::access; - template - void serialize(Archive& ar, const unsigned int version) - { - ar & distance & site0 & site1; - ar & direction0 & direction1 & direction2; - } - - }; - - typedef std::vector BondDataStorage; - - private: - - // serialization - friend class boost::serialization::access; - template - void serialize(Archive& ar, const unsigned int version) - { - using boost::serialization::base_object; - ar & base_object(*this); - ar & mbonds; - ar & mfilter_directions; - ar & mfilter_degrees; - } - - // methods - int count() const; - bool checkConeFilters(const R3::Vector& ru01) const; - - // data - std::vector mfilter_directions; - std::vector mfilter_degrees; - BondDataStorage mbonds; - BondDataStorage mpopbonds; - BondDataStorage maddbonds; - // support for PQEvaluatorOptimized - struct { - BondDataStorage bonds; - BondDataStorage popbonds; - } mstashedvalue; - +class DLL_EXPORT BondCalculator : public PairQuantity { + public: + // constructor + BondCalculator(); + + // methods + template + QuantityType operator()(const T&); + QuantityType distances() const; + std::vector directions() const; + SiteIndices sites0() const; + SiteIndices sites1() const; + std::vector types0() const; + std::vector types1() const; + void filterCone(R3::Vector coneaxis, double degrees); + void filterOff(); + + // PairQuantity overloads + virtual std::string getParallelData() const; + + protected: + // PairQuantity overloads + virtual void resetValue(); + virtual void addPairContribution(const BaseBondGenerator&, int); + virtual void executeParallelMerge(const std::string& pdata); + virtual void finishValue(); + + // support for PQEvaluatorOptimized + virtual void stashPartialValue(); + virtual void restorePartialValue(); + + friend class BondOp; + class BondEntry { + public: + double distance; + int site0; + int site1; + double direction0; + double direction1; + double direction2; + + private: + friend class boost::serialization::access; + template + void serialize(Archive& ar, const unsigned int version) { + ar & distance & site0 & site1; + ar & direction0 & direction1 & direction2; + } + }; + + typedef std::vector BondDataStorage; + + private: + // serialization + friend class boost::serialization::access; + template + void serialize(Archive& ar, const unsigned int version) { + using boost::serialization::base_object; + ar& base_object(*this); + ar & mbonds; + ar & mfilter_directions; + ar & mfilter_degrees; + } + + // methods + int count() const; + bool checkConeFilters(const R3::Vector& ru01) const; + + // data + std::vector mfilter_directions; + std::vector mfilter_degrees; + BondDataStorage mbonds; + BondDataStorage mpopbonds; + BondDataStorage maddbonds; + // support for PQEvaluatorOptimized + struct { + BondDataStorage bonds; + BondDataStorage popbonds; + } mstashedvalue; }; // Public Template Methods --------------------------------------------------- template -QuantityType BondCalculator::operator()(const T& stru) -{ - this->eval(stru); - return this->distances(); +QuantityType BondCalculator::operator()(const T& stru) { + this->eval(stru); + return this->distances(); } - -} // namespace srreal -} // namespace diffpy +} // namespace srreal +} // namespace diffpy // Serialization ------------------------------------------------------------- diff --git a/src/diffpy/srreal/ConstantPeakWidth.cpp b/src/diffpy/srreal/ConstantPeakWidth.cpp index 599ca85b..28a956a8 100644 --- a/src/diffpy/srreal/ConstantPeakWidth.cpp +++ b/src/diffpy/srreal/ConstantPeakWidth.cpp @@ -1,20 +1,20 @@ /***************************************************************************** -* -* 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.txt for license information. -* -****************************************************************************** -* -* class ConstantPeakWidth -- constant peak width model for testing -* -*****************************************************************************/ + * + * 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.txt for license information. + * + ****************************************************************************** + * + * class ConstantPeakWidth -- constant peak width model for testing + * + *****************************************************************************/ #include #include @@ -31,102 +31,81 @@ namespace { using diffpy::mathutils::GAUSS_SIGMA_TO_FWHM; const double UtoB = 8 * M_PI * M_PI; - -double getuisowidth(const ConstantPeakWidth* ppwm) -{ - const double rmsd = ppwm->getWidth() / GAUSS_SIGMA_TO_FWHM; - const int pm = (rmsd >= 0) ? +1 : -1; - return pm * 0.5 * rmsd * rmsd; +double getuisowidth(const ConstantPeakWidth* ppwm) { + const double rmsd = ppwm->getWidth() / GAUSS_SIGMA_TO_FWHM; + const int pm = (rmsd >= 0) ? +1 : -1; + return pm * 0.5 * rmsd * rmsd; } -void setuisowidth(ConstantPeakWidth* ppwm, const double& uiso) -{ - const int pm = (uiso >= 0) ? +1 : -1; - const double uisoplus = pm * uiso; - const double fwhm = pm * GAUSS_SIGMA_TO_FWHM * sqrt(2.0 * uisoplus); - ppwm->setWidth(fwhm); +void setuisowidth(ConstantPeakWidth* ppwm, const double& uiso) { + const int pm = (uiso >= 0) ? +1 : -1; + const double uisoplus = pm * uiso; + const double fwhm = pm * GAUSS_SIGMA_TO_FWHM * sqrt(2.0 * uisoplus); + ppwm->setWidth(fwhm); } - -double getbisowidth(const ConstantPeakWidth* ppwm) -{ - return UtoB * getuisowidth(ppwm); +double getbisowidth(const ConstantPeakWidth* ppwm) { + return UtoB * getuisowidth(ppwm); } -void setbisowidth(ConstantPeakWidth* ppwm, const double& biso) -{ - setuisowidth(ppwm, biso / UtoB); +void setbisowidth(ConstantPeakWidth* ppwm, const double& biso) { + setuisowidth(ppwm, biso / UtoB); } -} // namespace +} // namespace // Constructors -------------------------------------------------------------- -ConstantPeakWidth::ConstantPeakWidth() : mwidth(0.0) -{ - this->registerDoubleAttribute("width", this, - &ConstantPeakWidth::getWidth, &ConstantPeakWidth::setWidth); - this->registerDoubleAttribute("bisowidth", this, - &getbisowidth, &setbisowidth); - this->registerDoubleAttribute("uisowidth", this, - &getuisowidth, &setuisowidth); +ConstantPeakWidth::ConstantPeakWidth() : mwidth(0.0) { + this->registerDoubleAttribute("width", this, &ConstantPeakWidth::getWidth, + &ConstantPeakWidth::setWidth); + this->registerDoubleAttribute("bisowidth", this, &getbisowidth, + &setbisowidth); + this->registerDoubleAttribute("uisowidth", this, &getuisowidth, + &setuisowidth); } - -PeakWidthModelPtr ConstantPeakWidth::create() const -{ - PeakWidthModelPtr rv(new ConstantPeakWidth()); - return rv; +PeakWidthModelPtr ConstantPeakWidth::create() const { + PeakWidthModelPtr rv(new ConstantPeakWidth()); + return rv; } - -PeakWidthModelPtr ConstantPeakWidth::clone() const -{ - PeakWidthModelPtr rv(new ConstantPeakWidth(*this)); - return rv; +PeakWidthModelPtr ConstantPeakWidth::clone() const { + PeakWidthModelPtr rv(new ConstantPeakWidth(*this)); + return rv; } // Public Methods ------------------------------------------------------------ -const string& ConstantPeakWidth::type() const -{ - static const string rv = "constant"; - return rv; +const string& ConstantPeakWidth::type() const { + static const string rv = "constant"; + return rv; } - -double ConstantPeakWidth::calculate(const BaseBondGenerator& bnds) const -{ - return this->getWidth(); +double ConstantPeakWidth::calculate(const BaseBondGenerator& bnds) const { + return this->getWidth(); } - -double ConstantPeakWidth::maxWidth( - StructureAdapterPtr stru, double rmin, double rmax) const -{ - return this->getWidth(); +double ConstantPeakWidth::maxWidth(StructureAdapterPtr stru, double rmin, + double rmax) const { + return this->getWidth(); } // data access -const double& ConstantPeakWidth::getWidth() const -{ - return mwidth; -} - +const double& ConstantPeakWidth::getWidth() const { return mwidth; } -void ConstantPeakWidth::setWidth(double width) -{ - if (mwidth != width) mticker.click(); - mwidth = width; +void ConstantPeakWidth::setWidth(double width) { + if (mwidth != width) mticker.click(); + mwidth = width; } // Registration -------------------------------------------------------------- bool reg_ConstantPeakWidth = ConstantPeakWidth().registerThisType(); -} // namespace srreal -} // namespace diffpy +} // namespace srreal +} // namespace diffpy // Serialization ------------------------------------------------------------- diff --git a/src/diffpy/srreal/ConstantPeakWidth.hpp b/src/diffpy/srreal/ConstantPeakWidth.hpp index 70097a55..843bf32a 100644 --- a/src/diffpy/srreal/ConstantPeakWidth.hpp +++ b/src/diffpy/srreal/ConstantPeakWidth.hpp @@ -1,20 +1,20 @@ /***************************************************************************** -* -* 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.txt for license information. -* -****************************************************************************** -* -* class ConstantPeakWidth -- constant peak width -* -*****************************************************************************/ + * + * 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.txt for license information. + * + ****************************************************************************** + * + * class ConstantPeakWidth -- constant peak width + * + *****************************************************************************/ #ifndef CONSTANTPEAKWIDTH_HPP_INCLUDED #define CONSTANTPEAKWIDTH_HPP_INCLUDED @@ -26,46 +26,39 @@ namespace diffpy { namespace srreal { - -class DLL_EXPORT ConstantPeakWidth : public PeakWidthModel -{ - public: - - // constructors - ConstantPeakWidth(); - virtual PeakWidthModelPtr create() const; - virtual PeakWidthModelPtr clone() const; - - // methods - virtual const std::string& type() const; - virtual double calculate(const BaseBondGenerator&) const; - virtual double maxWidth(StructureAdapterPtr, - double rmin, double rmax) const; - - // data access - const double& getWidth() const; - void setWidth(double); - - private: - - // data - double mwidth; - - // serialization - friend class boost::serialization::access; - - template - void serialize(Archive& ar, const unsigned int version) - { - using boost::serialization::base_object; - ar & base_object(*this); - ar & mwidth; - } - +class DLL_EXPORT ConstantPeakWidth : public PeakWidthModel { + public: + // constructors + ConstantPeakWidth(); + virtual PeakWidthModelPtr create() const; + virtual PeakWidthModelPtr clone() const; + + // methods + virtual const std::string& type() const; + virtual double calculate(const BaseBondGenerator&) const; + virtual double maxWidth(StructureAdapterPtr, double rmin, double rmax) const; + + // data access + const double& getWidth() const; + void setWidth(double); + + private: + // data + double mwidth; + + // serialization + friend class boost::serialization::access; + + template + void serialize(Archive& ar, const unsigned int version) { + using boost::serialization::base_object; + ar& base_object(*this); + ar & mwidth; + } }; -} // namespace srreal -} // namespace diffpy +} // namespace srreal +} // namespace diffpy // Serialization ------------------------------------------------------------- diff --git a/src/diffpy/srreal/ConstantRadiiTable.cpp b/src/diffpy/srreal/ConstantRadiiTable.cpp index ba1145e0..c3cf5170 100644 --- a/src/diffpy/srreal/ConstantRadiiTable.cpp +++ b/src/diffpy/srreal/ConstantRadiiTable.cpp @@ -1,21 +1,21 @@ /***************************************************************************** -* -* 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.txt for license information. -* -****************************************************************************** -* -* class ConstantRadiiTable -- this AtomRadiiTable returns same value -* for all elements unless redefined by the setCustom method. -* -*****************************************************************************/ + * + * 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.txt for license information. + * + ****************************************************************************** + * + * class ConstantRadiiTable -- this AtomRadiiTable returns same value + * for all elements unless redefined by the setCustom method. + * + *****************************************************************************/ #include #include @@ -34,56 +34,41 @@ using namespace std; // Constructors -------------------------------------------------------------- -ConstantRadiiTable::ConstantRadiiTable() : mdefaultradius(0.0) -{ } +ConstantRadiiTable::ConstantRadiiTable() : mdefaultradius(0.0) {} // Public Methods ------------------------------------------------------------ // HasClassRegistry methods -AtomRadiiTablePtr ConstantRadiiTable::create() const -{ - return std::make_shared(); +AtomRadiiTablePtr ConstantRadiiTable::create() const { + return std::make_shared(); } - -AtomRadiiTablePtr ConstantRadiiTable::clone() const -{ - return std::make_shared(*this); +AtomRadiiTablePtr ConstantRadiiTable::clone() const { + return std::make_shared(*this); } - -const string& ConstantRadiiTable::type() const -{ - static string rv = "constant"; - return rv; +const string& ConstantRadiiTable::type() const { + static string rv = "constant"; + return rv; } // own methods -double ConstantRadiiTable::standardLookup(const string& smbl) const -{ - return mdefaultradius; -} - - -void ConstantRadiiTable::setDefault(double value) -{ - mdefaultradius = value; +double ConstantRadiiTable::standardLookup(const string& smbl) const { + return mdefaultradius; } +void ConstantRadiiTable::setDefault(double value) { mdefaultradius = value; } -double ConstantRadiiTable::getDefault() const -{ - return mdefaultradius; -} +double ConstantRadiiTable::getDefault() const { return mdefaultradius; } // Registration -------------------------------------------------------------- bool reg_ConstantRadiiTable = ConstantRadiiTable().registerThisType(); -} // srreal -} // diffpy +} // namespace srreal +} // namespace diffpy // Serialization ------------------------------------------------------------- diff --git a/src/diffpy/srreal/ConstantRadiiTable.hpp b/src/diffpy/srreal/ConstantRadiiTable.hpp index e4c44d8a..54b3d9e3 100644 --- a/src/diffpy/srreal/ConstantRadiiTable.hpp +++ b/src/diffpy/srreal/ConstantRadiiTable.hpp @@ -1,20 +1,20 @@ /***************************************************************************** -* -* 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.txt for license information. -* -****************************************************************************** -* -* class ConstantRadiiTable -- concrete AtomRadiiTable for a uniform radius -* -*****************************************************************************/ + * + * 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.txt for license information. + * + ****************************************************************************** + * + * class ConstantRadiiTable -- concrete AtomRadiiTable for a uniform radius + * + *****************************************************************************/ #ifndef CONSTANTRADIITABLE_HPP_INCLUDED #define CONSTANTRADIITABLE_HPP_INCLUDED @@ -26,42 +26,37 @@ namespace diffpy { namespace srreal { -class DLL_EXPORT ConstantRadiiTable : public AtomRadiiTable -{ - public: +class DLL_EXPORT ConstantRadiiTable : public AtomRadiiTable { + public: + // constructor + ConstantRadiiTable(); - // constructor - ConstantRadiiTable(); + // methods - HasClassRegistry + SharedPtr create() const; + SharedPtr clone() const; + const std::string& type() const; + // method overloads + double standardLookup(const std::string& smbl) const; + // methods specific for this class + void setDefault(double); + double getDefault() const; - // methods - HasClassRegistry - SharedPtr create() const; - SharedPtr clone() const; - const std::string& type() const; - // method overloads - double standardLookup(const std::string& smbl) const; - // methods specific for this class - void setDefault(double); - double getDefault() const; - - private: - - // data - double mdefaultradius; - - // serialization - friend class boost::serialization::access; - template - void serialize(Archive& ar, const unsigned int version) - { - using boost::serialization::base_object; - ar & base_object(*this); - ar & mdefaultradius; - } + private: + // data + double mdefaultradius; + // serialization + friend class boost::serialization::access; + template + void serialize(Archive& ar, const unsigned int version) { + using boost::serialization::base_object; + ar& base_object(*this); + ar & mdefaultradius; + } }; -} // namespace srreal -} // namespace diffpy +} // namespace srreal +} // namespace diffpy // Serialization ------------------------------------------------------------- diff --git a/src/diffpy/srreal/CroppedGaussianProfile.cpp b/src/diffpy/srreal/CroppedGaussianProfile.cpp index 19fe7468..17733f45 100644 --- a/src/diffpy/srreal/CroppedGaussianProfile.cpp +++ b/src/diffpy/srreal/CroppedGaussianProfile.cpp @@ -1,22 +1,22 @@ /***************************************************************************** -* -* 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.txt for license information. -* -****************************************************************************** -* -* class CroppedGaussianProfile -- Gaussian profile cropped to zero beyond -* xboundhi and scaled so that its integrated area equals 1. -* Registered as "croppedgaussian". -* -*****************************************************************************/ + * + * 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.txt for license information. + * + ****************************************************************************** + * + * class CroppedGaussianProfile -- Gaussian profile cropped to zero beyond + * xboundhi and scaled so that its integrated area equals 1. + * Registered as "croppedgaussian". + * + *****************************************************************************/ #include @@ -32,63 +32,53 @@ namespace srreal { // Constructors -------------------------------------------------------------- -CroppedGaussianProfile::CroppedGaussianProfile() : mscale(0.0) -{ - // refresh mscale - this->setPrecision(this->getPrecision()); +CroppedGaussianProfile::CroppedGaussianProfile() : mscale(0.0) { + // refresh mscale + this->setPrecision(this->getPrecision()); } - -PeakProfilePtr CroppedGaussianProfile::create() const -{ - PeakProfilePtr rv(new CroppedGaussianProfile()); - return rv; +PeakProfilePtr CroppedGaussianProfile::create() const { + PeakProfilePtr rv(new CroppedGaussianProfile()); + return rv; } - -PeakProfilePtr CroppedGaussianProfile::clone() const -{ - PeakProfilePtr rv(new CroppedGaussianProfile(*this)); - return rv; +PeakProfilePtr CroppedGaussianProfile::clone() const { + PeakProfilePtr rv(new CroppedGaussianProfile(*this)); + return rv; } // Public Methods ------------------------------------------------------------ -const string& CroppedGaussianProfile::type() const -{ - static string rv = "croppedgaussian"; - return rv; +const string& CroppedGaussianProfile::type() const { + static string rv = "croppedgaussian"; + return rv; } - -double CroppedGaussianProfile::operator()(double x, double fwhm) const -{ - double xrel = x / fwhm; - double rv = (fabs(xrel) >= mhalfboundrel) ? 0.0 : - 2 * sqrt(M_LN2 / M_PI) / fwhm * - mscale * exp(-4 * M_LN2 * xrel * xrel); - return rv; +double CroppedGaussianProfile::operator()(double x, double fwhm) const { + double xrel = x / fwhm; + double rv = + (fabs(xrel) >= mhalfboundrel) + ? 0.0 + : 2 * sqrt(M_LN2 / M_PI) / fwhm * mscale * exp(-4 * M_LN2 * xrel * xrel); + return rv; } - -void CroppedGaussianProfile::setPrecision(double eps) -{ - this->GaussianProfile::setPrecision(eps); - double eps1 = this->getPrecision(); - mscale = 1.0; - if (eps1 < 1) - { - double area = gsl_sf_erf(sqrt(-log(eps1))); - mscale = 1.0 / area; - } +void CroppedGaussianProfile::setPrecision(double eps) { + this->GaussianProfile::setPrecision(eps); + double eps1 = this->getPrecision(); + mscale = 1.0; + if (eps1 < 1) { + double area = gsl_sf_erf(sqrt(-log(eps1))); + mscale = 1.0 / area; + } } // Registration -------------------------------------------------------------- bool reg_CroppedGaussianProfile = CroppedGaussianProfile().registerThisType(); -} // namespace srreal -} // namespace diffpy +} // namespace srreal +} // namespace diffpy // Serialization ------------------------------------------------------------- diff --git a/src/diffpy/srreal/CroppedGaussianProfile.hpp b/src/diffpy/srreal/CroppedGaussianProfile.hpp index ce180059..680f835a 100644 --- a/src/diffpy/srreal/CroppedGaussianProfile.hpp +++ b/src/diffpy/srreal/CroppedGaussianProfile.hpp @@ -1,22 +1,22 @@ /***************************************************************************** -* -* 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.txt for license information. -* -****************************************************************************** -* -* class CroppedGaussianProfile -- Gaussian profile cropped to zero beyond -* xboundhi and scaled so that its integrated area equals 1. -* Registered as "croppedgaussian". -* -*****************************************************************************/ + * + * 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.txt for license information. + * + ****************************************************************************** + * + * class CroppedGaussianProfile -- Gaussian profile cropped to zero beyond + * xboundhi and scaled so that its integrated area equals 1. + * Registered as "croppedgaussian". + * + *****************************************************************************/ #ifndef CROPPEDGAUSSIANPROFILE_HPP_INCLUDED #define CROPPEDGAUSSIANPROFILE_HPP_INCLUDED @@ -28,40 +28,35 @@ namespace diffpy { namespace srreal { -class DLL_EXPORT CroppedGaussianProfile : public GaussianProfile -{ - public: - - // constructors - CroppedGaussianProfile(); - PeakProfilePtr create() const; - PeakProfilePtr clone() const; - - // methods - const std::string& type() const; - double operator()(double x, double fwhm) const; - void setPrecision(double eps); - - private: - - // data - double mscale; - - // serialization - friend class boost::serialization::access; - - template - void serialize(Archive& ar, const unsigned int version) - { - using boost::serialization::base_object; - ar & base_object(*this); - ar & mscale; - } - +class DLL_EXPORT CroppedGaussianProfile : public GaussianProfile { + public: + // constructors + CroppedGaussianProfile(); + PeakProfilePtr create() const; + PeakProfilePtr clone() const; + + // methods + const std::string& type() const; + double operator()(double x, double fwhm) const; + void setPrecision(double eps); + + private: + // data + double mscale; + + // serialization + friend class boost::serialization::access; + + template + void serialize(Archive& ar, const unsigned int version) { + using boost::serialization::base_object; + ar& base_object(*this); + ar & mscale; + } }; -} // namespace srreal -} // namespace diffpy +} // namespace srreal +} // namespace diffpy // Serialization ------------------------------------------------------------- diff --git a/src/diffpy/srreal/CrystalStructureAdapter.cpp b/src/diffpy/srreal/CrystalStructureAdapter.cpp index 38ceda6c..90da49c6 100644 --- a/src/diffpy/srreal/CrystalStructureAdapter.cpp +++ b/src/diffpy/srreal/CrystalStructureAdapter.cpp @@ -1,24 +1,24 @@ /***************************************************************************** -* -* 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 CrystalStructureAdapter -- universal adapter for crystal structure -* composed of asymmetric unit cell and list of symmetry operations in -* the space group -* -* class CrystalStructureBondGenerator -- bond generator -* -*****************************************************************************/ + * + * 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 CrystalStructureAdapter -- universal adapter for crystal structure + * composed of asymmetric unit cell and list of symmetry operations in + * the space group + * + * class CrystalStructureBondGenerator -- bond generator + * + *****************************************************************************/ #include @@ -43,238 +43,195 @@ const double DEFAULT_SYMMETRY_PRECISION = 5e-5; // Constructor --------------------------------------------------------------- -CrystalStructureAdapter::CrystalStructureAdapter() : - PeriodicStructureAdapter(), - msymmetry_precision(DEFAULT_SYMMETRY_PRECISION), - msymmetry_cached(false) -{ } +CrystalStructureAdapter::CrystalStructureAdapter() + : PeriodicStructureAdapter(), + msymmetry_precision(DEFAULT_SYMMETRY_PRECISION), + msymmetry_cached(false) {} // Public Methods ------------------------------------------------------------ -StructureAdapterPtr CrystalStructureAdapter::clone() const -{ - StructureAdapterPtr rv(new CrystalStructureAdapter(*this)); - return rv; +StructureAdapterPtr CrystalStructureAdapter::clone() const { + StructureAdapterPtr rv(new CrystalStructureAdapter(*this)); + return rv; } - -BaseBondGeneratorPtr CrystalStructureAdapter::createBondGenerator() const -{ - this->updateSymmetryPositions(); - BaseBondGeneratorPtr bnds( - new CrystalStructureBondGenerator(shared_from_this())); - return bnds; -} - - -int CrystalStructureAdapter::siteMultiplicity(int idx) const -{ - if (!this->isSymmetryCached()) this->updateSymmetryPositions(); - int rv = msymatoms[idx].size(); - return rv; +BaseBondGeneratorPtr CrystalStructureAdapter::createBondGenerator() const { + this->updateSymmetryPositions(); + BaseBondGeneratorPtr bnds( + new CrystalStructureBondGenerator(shared_from_this())); + return bnds; } - -StructureDifference -CrystalStructureAdapter::diff(StructureAdapterConstPtr other) const -{ - StructureDifference sd = this->StructureAdapter::diff(other); - if (sd.stru0 == sd.stru1) return sd; - typedef std::shared_ptr CPtr; - CPtr cother = std::dynamic_pointer_cast(other); - if (!cother) return sd; - // compare symmetry operations in both adapters - assert(cother == sd.stru1); - const bool sameprecision = ( - this->getSymmetryPrecision() == cother->getSymmetryPrecision()); - if (!sameprecision) return sd; - if (msymops != cother->msymops) return sd; - sd = this->PeriodicStructureAdapter::diff(other); - return sd; +int CrystalStructureAdapter::siteMultiplicity(int idx) const { + if (!this->isSymmetryCached()) this->updateSymmetryPositions(); + int rv = msymatoms[idx].size(); + return rv; } - -void CrystalStructureAdapter::setSymmetryPrecision(double eps) -{ - using namespace diffpy::validators; - ensureEpsilonPositive("symmetryprecision", eps); - if (eps != msymmetry_precision) msymmetry_cached = false; - msymmetry_precision = eps; +StructureDifference CrystalStructureAdapter::diff( + StructureAdapterConstPtr other) const { + StructureDifference sd = this->StructureAdapter::diff(other); + if (sd.stru0 == sd.stru1) return sd; + typedef std::shared_ptr CPtr; + CPtr cother = std::dynamic_pointer_cast(other); + if (!cother) return sd; + // compare symmetry operations in both adapters + assert(cother == sd.stru1); + const bool sameprecision = + (this->getSymmetryPrecision() == cother->getSymmetryPrecision()); + if (!sameprecision) return sd; + if (msymops != cother->msymops) return sd; + sd = this->PeriodicStructureAdapter::diff(other); + return sd; } - -const double& CrystalStructureAdapter::getSymmetryPrecision() const -{ - return msymmetry_precision; +void CrystalStructureAdapter::setSymmetryPrecision(double eps) { + using namespace diffpy::validators; + ensureEpsilonPositive("symmetryprecision", eps); + if (eps != msymmetry_precision) msymmetry_cached = false; + msymmetry_precision = eps; } - -int CrystalStructureAdapter::countSymOps() const -{ - return msymops.size(); +const double& CrystalStructureAdapter::getSymmetryPrecision() const { + return msymmetry_precision; } +int CrystalStructureAdapter::countSymOps() const { return msymops.size(); } -void CrystalStructureAdapter::clearSymOps() -{ - msymops.clear(); - msymmetry_cached = false; +void CrystalStructureAdapter::clearSymOps() { + msymops.clear(); + msymmetry_cached = false; } - -void CrystalStructureAdapter::addSymOp(const SymOpRotTrans& op) -{ - msymops.push_back(op); - msymmetry_cached = false; +void CrystalStructureAdapter::addSymOp(const SymOpRotTrans& op) { + msymops.push_back(op); + msymmetry_cached = false; } - -void CrystalStructureAdapter::addSymOp(const R3::Matrix& R, const R3::Vector& t) -{ - SymOpRotTrans op = {R, t}; - this->addSymOp(op); - assert(!msymmetry_cached); +void CrystalStructureAdapter::addSymOp(const R3::Matrix& R, + const R3::Vector& t) { + SymOpRotTrans op = {R, t}; + this->addSymOp(op); + assert(!msymmetry_cached); } - -const SymOpRotTrans& CrystalStructureAdapter::getSymOp(int i) const -{ - return msymops[i]; +const SymOpRotTrans& CrystalStructureAdapter::getSymOp(int i) const { + return msymops[i]; } - const CrystalStructureAdapter::AtomVector& -CrystalStructureAdapter::getEquivalentAtoms(int idx) const -{ - assert(0 <= idx && idx < this->countSites()); - if (!this->isSymmetryCached()) this->updateSymmetryPositions(); - return msymatoms[idx]; +CrystalStructureAdapter::getEquivalentAtoms(int idx) const { + assert(0 <= idx && idx < this->countSites()); + if (!this->isSymmetryCached()) this->updateSymmetryPositions(); + return msymatoms[idx]; } - -CrystalStructureAdapter::AtomVector -CrystalStructureAdapter::expandLatticeAtom(const Atom& a0) const -{ - using mathutils::eps_eq; - AtomVector eqsites; - vector eqsumpos; - vector eqduplicity; - eqsumpos.reserve(this->countSymOps()); - eqduplicity.reserve(this->countSymOps()); - const Lattice& L = this->getLattice(); - SymOpVector::const_iterator op = msymops.begin(); - Atom a1 = a0; - for (; op != msymops.end(); ++op) - { - // positions and Uij-s are actually fractional here - a1.xyz_cartn = R3::mxvecproduct(op->R, a0.xyz_cartn); - a1.xyz_cartn += op->t; - // check if a1 is a duplicate of an existing symmetry site - int ieq = this->findEqualPosition(eqsites, a1); - if (ieq < 0) - { - // a1 is a new symmetry site - R3::Matrix utmp = R3::prod(a0.uij_cartn, R3::trans(op->R)); - a1.uij_cartn = R3::prod(op->R, utmp); - eqsites.push_back(a1); - eqsumpos.push_back(R3::zerovector); - eqduplicity.push_back(0); - ieq = eqsites.size() - 1; - } - eqsumpos[ieq] += L.ucvFractional(a1.xyz_cartn); - eqduplicity[ieq] += 1; - } - // assume P1 if symmetry operations were not defined - if (msymops.empty()) - { - assert(eqsites.empty()); - assert(eqsumpos.empty()); - assert(eqduplicity.empty()); - eqsites.push_back(a0); - eqsumpos.push_back(a0.xyz_cartn); - eqduplicity.push_back(1); - } - // calculate mean values from equivalent sites and adjust any roundoffs - assert(eqsites.size() == eqduplicity.size()); - assert(eqsites.size() == eqsumpos.size()); - iterator ai = eqsites.begin(); - vector::const_iterator sii = eqsumpos.begin(); - vector::const_iterator dpi = eqduplicity.begin(); - for (; ai != eqsites.end(); ++ai, ++sii, ++dpi) - { - ai->xyz_cartn = (*sii) / (*dpi); +CrystalStructureAdapter::AtomVector CrystalStructureAdapter::expandLatticeAtom( + const Atom& a0) const { + using mathutils::eps_eq; + AtomVector eqsites; + vector eqsumpos; + vector eqduplicity; + eqsumpos.reserve(this->countSymOps()); + eqduplicity.reserve(this->countSymOps()); + const Lattice& L = this->getLattice(); + SymOpVector::const_iterator op = msymops.begin(); + Atom a1 = a0; + for (; op != msymops.end(); ++op) { + // positions and Uij-s are actually fractional here + a1.xyz_cartn = R3::mxvecproduct(op->R, a0.xyz_cartn); + a1.xyz_cartn += op->t; + // check if a1 is a duplicate of an existing symmetry site + int ieq = this->findEqualPosition(eqsites, a1); + if (ieq < 0) { + // a1 is a new symmetry site + R3::Matrix utmp = R3::prod(a0.uij_cartn, R3::trans(op->R)); + a1.uij_cartn = R3::prod(op->R, utmp); + eqsites.push_back(a1); + eqsumpos.push_back(R3::zerovector); + eqduplicity.push_back(0); + ieq = eqsites.size() - 1; } - return eqsites; + eqsumpos[ieq] += L.ucvFractional(a1.xyz_cartn); + eqduplicity[ieq] += 1; + } + // assume P1 if symmetry operations were not defined + if (msymops.empty()) { + assert(eqsites.empty()); + assert(eqsumpos.empty()); + assert(eqduplicity.empty()); + eqsites.push_back(a0); + eqsumpos.push_back(a0.xyz_cartn); + eqduplicity.push_back(1); + } + // calculate mean values from equivalent sites and adjust any roundoffs + assert(eqsites.size() == eqduplicity.size()); + assert(eqsites.size() == eqsumpos.size()); + iterator ai = eqsites.begin(); + vector::const_iterator sii = eqsumpos.begin(); + vector::const_iterator dpi = eqduplicity.begin(); + for (; ai != eqsites.end(); ++ai, ++sii, ++dpi) { + ai->xyz_cartn = (*sii) / (*dpi); + } + return eqsites; } - -void CrystalStructureAdapter::updateSymmetryPositions() const -{ - // build asymmetric unit in lattice coordinates - AtomVector lcatoms(this->begin(), this->end()); - AtomVector::iterator lcai = lcatoms.begin(); - for (; lcai != lcatoms.end(); ++lcai) this->toFractional(*lcai); - // build symmetry positions for all atoms in the asymmetric unit - msymatoms.resize(this->countSites()); - assert(lcatoms.size() == msymatoms.size()); - lcai = lcatoms.begin(); - std::vector::iterator saii = msymatoms.begin(); - for (; lcai != lcatoms.end(); ++lcai, ++saii) - { - *saii = this->expandLatticeAtom(*lcai); - iterator ai = saii->begin(); - for (; ai != saii->end(); ++ai) this->toCartesian(*ai); - } - msymmetry_cached = true; +void CrystalStructureAdapter::updateSymmetryPositions() const { + // build asymmetric unit in lattice coordinates + AtomVector lcatoms(this->begin(), this->end()); + AtomVector::iterator lcai = lcatoms.begin(); + for (; lcai != lcatoms.end(); ++lcai) this->toFractional(*lcai); + // build symmetry positions for all atoms in the asymmetric unit + msymatoms.resize(this->countSites()); + assert(lcatoms.size() == msymatoms.size()); + lcai = lcatoms.begin(); + std::vector::iterator saii = msymatoms.begin(); + for (; lcai != lcatoms.end(); ++lcai, ++saii) { + *saii = this->expandLatticeAtom(*lcai); + iterator ai = saii->begin(); + for (; ai != saii->end(); ++ai) this->toCartesian(*ai); + } + msymmetry_cached = true; } // Private Methods ----------------------------------------------------------- -int CrystalStructureAdapter::findEqualPosition( - const AtomVector& eqsites, const Atom& a0) const -{ - const double symeps = this->getSymmetryPrecision(); - const Lattice& L = this->getLattice(); - R3::Vector dxyz; - const_iterator ai = eqsites.begin(); - for (; ai != eqsites.end(); ++ai) - { - dxyz = ai->xyz_cartn - a0.xyz_cartn; - dxyz[0] -= round(dxyz[0]); - dxyz[1] -= round(dxyz[1]); - dxyz[2] -= round(dxyz[2]); - if (L.norm(dxyz) <= symeps) return (ai - eqsites.begin()); - } - return -1; +int CrystalStructureAdapter::findEqualPosition(const AtomVector& eqsites, + const Atom& a0) const { + const double symeps = this->getSymmetryPrecision(); + const Lattice& L = this->getLattice(); + R3::Vector dxyz; + const_iterator ai = eqsites.begin(); + for (; ai != eqsites.end(); ++ai) { + dxyz = ai->xyz_cartn - a0.xyz_cartn; + dxyz[0] -= round(dxyz[0]); + dxyz[1] -= round(dxyz[1]); + dxyz[2] -= round(dxyz[2]); + if (L.norm(dxyz) <= symeps) return (ai - eqsites.begin()); + } + return -1; } -bool CrystalStructureAdapter::isSymmetryCached() const -{ - msymmetry_cached = msymmetry_cached && - (int(msymatoms.size()) == this->countSites()); - return msymmetry_cached; +bool CrystalStructureAdapter::isSymmetryCached() const { + msymmetry_cached = + msymmetry_cached && (int(msymatoms.size()) == this->countSites()); + return msymmetry_cached; } // Comparison functions ------------------------------------------------------ -bool operator==( - const CrystalStructureAdapter& stru0, - const CrystalStructureAdapter& stru1) -{ - const PeriodicStructureAdapter& pstru0 = stru0; - const PeriodicStructureAdapter& pstru1 = stru1; - bool rv = (&stru0 == &stru1) || ( - (pstru0 == pstru1) && - (stru0.msymops == stru1.msymops)); - return rv; +bool operator==(const CrystalStructureAdapter& stru0, + const CrystalStructureAdapter& stru1) { + const PeriodicStructureAdapter& pstru0 = stru0; + const PeriodicStructureAdapter& pstru1 = stru1; + bool rv = (&stru0 == &stru1) || + ((pstru0 == pstru1) && (stru0.msymops == stru1.msymops)); + return rv; } - -bool operator!=( - const CrystalStructureAdapter& stru0, - const CrystalStructureAdapter& stru1) -{ - return !(stru0 == stru1); +bool operator!=(const CrystalStructureAdapter& stru0, + const CrystalStructureAdapter& stru1) { + return !(stru0 == stru1); } ////////////////////////////////////////////////////////////////////////////// @@ -284,85 +241,73 @@ bool operator!=( // Constructor --------------------------------------------------------------- CrystalStructureBondGenerator::CrystalStructureBondGenerator( - StructureAdapterConstPtr adpt) : PeriodicStructureBondGenerator(adpt) -{ - mcstructure = dynamic_cast(adpt.get()); - assert(mcstructure); - msymidx = 0; - mpuc1 = &(R3::zeromatrix()); + StructureAdapterConstPtr adpt) + : PeriodicStructureBondGenerator(adpt) { + mcstructure = dynamic_cast(adpt.get()); + assert(mcstructure); + msymidx = 0; + mpuc1 = &(R3::zeromatrix()); } // Public Methods ------------------------------------------------------------ -void CrystalStructureBondGenerator::selectAnchorSite(int anchor) -{ - this->BaseBondGenerator::selectAnchorSite(anchor); - const Atom& a0 = this->symatoms(anchor)[0]; - mr0 = a0.xyz_cartn; +void CrystalStructureBondGenerator::selectAnchorSite(int anchor) { + this->BaseBondGenerator::selectAnchorSite(anchor); + const Atom& a0 = this->symatoms(anchor)[0]; + mr0 = a0.xyz_cartn; } - -const R3::Matrix& CrystalStructureBondGenerator::Ucartesian1() const -{ - return *mpuc1; +const R3::Matrix& CrystalStructureBondGenerator::Ucartesian1() const { + return *mpuc1; } // Protected Methods --------------------------------------------------------- -bool CrystalStructureBondGenerator::iterateSymmetry() -{ - // Iterate the sphere at a fixed symmetry position. - if (this->PeriodicStructureBondGenerator::iterateSymmetry()) - { - this->updater1(); - return true; - } - // Advance to the next symmetry position. We are done if they - // were all already used. - const AtomVector& sa = this->symatoms(this->site1()); - ++msymidx; - if (msymidx >= sa.size()) return false; - // rewind the sphere for the new symmetry position. - // this calls CrystalStructureBondGenerator::updater1() - this->PeriodicStructureBondGenerator::rewindSymmetry(); +bool CrystalStructureBondGenerator::iterateSymmetry() { + // Iterate the sphere at a fixed symmetry position. + if (this->PeriodicStructureBondGenerator::iterateSymmetry()) { + this->updater1(); return true; + } + // Advance to the next symmetry position. We are done if they + // were all already used. + const AtomVector& sa = this->symatoms(this->site1()); + ++msymidx; + if (msymidx >= sa.size()) return false; + // rewind the sphere for the new symmetry position. + // this calls CrystalStructureBondGenerator::updater1() + this->PeriodicStructureBondGenerator::rewindSymmetry(); + return true; } - -void CrystalStructureBondGenerator::rewindSymmetry() -{ - msymidx = 0; - // this calls CrystalStructureBondGenerator::updater1() - this->PeriodicStructureBondGenerator::rewindSymmetry(); +void CrystalStructureBondGenerator::rewindSymmetry() { + msymidx = 0; + // this calls CrystalStructureBondGenerator::updater1() + this->PeriodicStructureBondGenerator::rewindSymmetry(); } - -void CrystalStructureBondGenerator::getNextBond() -{ - this->BaseBondGenerator::getNextBond(); +void CrystalStructureBondGenerator::getNextBond() { + this->BaseBondGenerator::getNextBond(); } - -void CrystalStructureBondGenerator::updater1() -{ - const AtomVector& sa = this->symatoms(this->site1()); - assert(msymidx < sa.size()); - mr1 = mrcsphere + sa[msymidx].xyz_cartn; - mpuc1 = &(sa[msymidx].uij_cartn); - this->updateDistance(); +void CrystalStructureBondGenerator::updater1() { + const AtomVector& sa = this->symatoms(this->site1()); + assert(msymidx < sa.size()); + mr1 = mrcsphere + sa[msymidx].xyz_cartn; + mpuc1 = &(sa[msymidx].uij_cartn); + this->updateDistance(); } // Private Methods ----------------------------------------------------------- const CrystalStructureAdapter::AtomVector& -CrystalStructureBondGenerator::symatoms(int idx) -{ - assert(0 <= idx && idx < int(mcstructure->msymatoms.size())); - return mcstructure->msymatoms[idx]; +CrystalStructureBondGenerator::symatoms(int idx) { + assert(0 <= idx && idx < int(mcstructure->msymatoms.size())); + return mcstructure->msymatoms[idx]; } -} // namespace srreal -} // namespace diffpy +} // namespace srreal +} // namespace diffpy // Serialization ------------------------------------------------------------- diff --git a/src/diffpy/srreal/CrystalStructureAdapter.hpp b/src/diffpy/srreal/CrystalStructureAdapter.hpp index c38944a2..0a502c3b 100644 --- a/src/diffpy/srreal/CrystalStructureAdapter.hpp +++ b/src/diffpy/srreal/CrystalStructureAdapter.hpp @@ -1,24 +1,24 @@ /***************************************************************************** -* -* 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 CrystalStructureAdapter -- universal adapter for crystal structure -* composed of asymmetric unit cell and list of symmetry operations in -* the space group -* -* class CrystalStructureBondGenerator -- bond generator -* -*****************************************************************************/ + * + * 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 CrystalStructureAdapter -- universal adapter for crystal structure + * composed of asymmetric unit cell and list of symmetry operations in + * the space group + * + * class CrystalStructureBondGenerator -- bond generator + * + *****************************************************************************/ #ifndef CRYSTALSTRUCTUREADAPTER_HPP_INCLUDED #define CRYSTALSTRUCTUREADAPTER_HPP_INCLUDED @@ -31,151 +31,132 @@ namespace diffpy { namespace srreal { /// store rotation matrix and translation vector for a symmetry operation -class SymOpRotTrans -{ - public: - - R3::Matrix R; - R3::Vector t; - - private: - - // serialization - friend class boost::serialization::access; - template - void serialize(Archive& ar, const unsigned int version) - { - ar & R & t; - } - +class SymOpRotTrans { + public: + R3::Matrix R; + R3::Vector t; + + private: + // serialization + friend class boost::serialization::access; + template + void serialize(Archive& ar, const unsigned int version) { + ar & R & t; + } }; // Comparison functions -inline -bool operator==(const SymOpRotTrans& op0, const SymOpRotTrans& op1) -{ - return (op0.R == op1.R) && (op0.t == op1.t); +inline bool operator==(const SymOpRotTrans& op0, const SymOpRotTrans& op1) { + return (op0.R == op1.R) && (op0.t == op1.t); } -inline -bool operator!=(const SymOpRotTrans& op0, const SymOpRotTrans& op1) -{ - return !(op0 == op1); +inline bool operator!=(const SymOpRotTrans& op0, const SymOpRotTrans& op1) { + return !(op0 == op1); } - -class DLL_EXPORT CrystalStructureAdapter : public PeriodicStructureAdapter -{ - friend class CrystalStructureBondGenerator; - - public: - - typedef std::vector SymOpVector; - // constructor - CrystalStructureAdapter(); - - // methods - overloaded - virtual StructureAdapterPtr clone() const; - virtual BaseBondGeneratorPtr createBondGenerator() const; - virtual int siteMultiplicity(int idx) const; - virtual StructureDifference diff(StructureAdapterConstPtr other) const; - - // methods - own - void setSymmetryPrecision(double eps); - const double& getSymmetryPrecision() const; - int countSymOps() const; - void clearSymOps(); - void addSymOp(const SymOpRotTrans&); - void addSymOp(const R3::Matrix& R, const R3::Vector& t); - const SymOpRotTrans& getSymOp(int i) const; - /// return all symmetry equivalent atoms in the unit cell for site i - const AtomVector& getEquivalentAtoms(int idx) const; - /// return all symmetry related atoms in fractional coordinates - AtomVector expandLatticeAtom(const Atom&) const; - void updateSymmetryPositions() const; - - private: - - // data - /// array of symmetry operations - SymOpVector msymops; - double msymmetry_precision; - mutable std::vector msymatoms; - mutable bool msymmetry_cached; - - // symmetry helpers - /// return index of AtomVector atom at an equal position or -1 - int findEqualPosition(const AtomVector&, const Atom&) const; - /// fuzzy check if symmetry positions are up to date - /// this only detects addition or removal of atom in the asymmetric - /// unit, but does not check for changes in atom positions. - bool isSymmetryCached() const; - - // comparison - friend DLL_EXPORT bool operator==( - const CrystalStructureAdapter&, const CrystalStructureAdapter&); - - // serialization - friend class boost::serialization::access; - template - void serialize(Archive& ar, const unsigned int version) - { - ar & boost::serialization::base_object(*this); - ar & msymops; - ar & msymmetry_precision; - ar & msymatoms; - ar & msymmetry_cached; - } - +class DLL_EXPORT CrystalStructureAdapter : public PeriodicStructureAdapter { + friend class CrystalStructureBondGenerator; + + public: + typedef std::vector SymOpVector; + // constructor + CrystalStructureAdapter(); + + // methods - overloaded + virtual StructureAdapterPtr clone() const; + virtual BaseBondGeneratorPtr createBondGenerator() const; + virtual int siteMultiplicity(int idx) const; + virtual StructureDifference diff(StructureAdapterConstPtr other) const; + + // methods - own + void setSymmetryPrecision(double eps); + const double& getSymmetryPrecision() const; + int countSymOps() const; + void clearSymOps(); + void addSymOp(const SymOpRotTrans&); + void addSymOp(const R3::Matrix& R, const R3::Vector& t); + const SymOpRotTrans& getSymOp(int i) const; + /// return all symmetry equivalent atoms in the unit cell for site i + const AtomVector& getEquivalentAtoms(int idx) const; + /// return all symmetry related atoms in fractional coordinates + AtomVector expandLatticeAtom(const Atom&) const; + void updateSymmetryPositions() const; + + private: + // data + /// array of symmetry operations + SymOpVector msymops; + double msymmetry_precision; + mutable std::vector msymatoms; + mutable bool msymmetry_cached; + + // symmetry helpers + /// return index of AtomVector atom at an equal position or -1 + int findEqualPosition(const AtomVector&, const Atom&) const; + /// fuzzy check if symmetry positions are up to date + /// this only detects addition or removal of atom in the asymmetric + /// unit, but does not check for changes in atom positions. + bool isSymmetryCached() const; + + // comparison + friend DLL_EXPORT bool operator==(const CrystalStructureAdapter&, + const CrystalStructureAdapter&); + + // serialization + friend class boost::serialization::access; + template + void serialize(Archive& ar, const unsigned int version) { + ar& boost::serialization::base_object(*this); + ar & msymops; + ar & msymmetry_precision; + ar & msymatoms; + ar & msymmetry_cached; + } }; - typedef std::shared_ptr CrystalStructureAdapterPtr; // Comparison functions -DLL_EXPORT bool operator==(const CrystalStructureAdapter&, const CrystalStructureAdapter&); -DLL_EXPORT bool operator!=(const CrystalStructureAdapter&, const CrystalStructureAdapter&); - - -class DLL_EXPORT CrystalStructureBondGenerator : public PeriodicStructureBondGenerator -{ - public: - - // constructors - CrystalStructureBondGenerator(StructureAdapterConstPtr); - - // configuration - virtual void selectAnchorSite(int); - - // data access - virtual const R3::Matrix& Ucartesian1() const; +DLL_EXPORT bool operator==(const CrystalStructureAdapter&, + const CrystalStructureAdapter&); +DLL_EXPORT bool operator!=(const CrystalStructureAdapter&, + const CrystalStructureAdapter&); - protected: +class DLL_EXPORT CrystalStructureBondGenerator + : public PeriodicStructureBondGenerator { + public: + // constructors + CrystalStructureBondGenerator(StructureAdapterConstPtr); - // methods - virtual bool iterateSymmetry(); - virtual void rewindSymmetry(); - virtual void getNextBond(); - virtual void updater1(); + // configuration + virtual void selectAnchorSite(int); - // data - const CrystalStructureAdapter* mcstructure; - size_t msymidx; - const R3::Matrix* mpuc1; + // data access + virtual const R3::Matrix& Ucartesian1() const; - private: + protected: + // methods + virtual bool iterateSymmetry(); + virtual void rewindSymmetry(); + virtual void getNextBond(); + virtual void updater1(); - typedef CrystalStructureAdapter::AtomVector AtomVector; + // data + const CrystalStructureAdapter* mcstructure; + size_t msymidx; + const R3::Matrix* mpuc1; - // methods - const AtomVector& symatoms(int idx); + private: + typedef CrystalStructureAdapter::AtomVector AtomVector; + // methods + const AtomVector& symatoms(int idx); }; -} // namespace srreal -} // namespace diffpy +} // namespace srreal +} // namespace diffpy BOOST_CLASS_EXPORT_KEY(diffpy::srreal::CrystalStructureAdapter) diff --git a/src/diffpy/srreal/DebyePDFCalculator.cpp b/src/diffpy/srreal/DebyePDFCalculator.cpp index e200755d..d162ca7f 100644 --- a/src/diffpy/srreal/DebyePDFCalculator.cpp +++ b/src/diffpy/srreal/DebyePDFCalculator.cpp @@ -1,20 +1,20 @@ /***************************************************************************** -* -* 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, Chris Farrow -* -* See AUTHORS.rst for a list of people who contributed. -* See LICENSE_DANSE.txt for license information. -* -****************************************************************************** -* -* class DebyePDFCalculator -- calculate PDF from the Debye equation. -* -*****************************************************************************/ + * + * 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, Chris Farrow + * + * See AUTHORS.rst for a list of people who contributed. + * See LICENSE_DANSE.txt for license information. + * + ****************************************************************************** + * + * class DebyePDFCalculator -- calculate PDF from the Debye equation. + * + *****************************************************************************/ #include #include @@ -38,191 +38,154 @@ const double DEFAULT_DEBYEPDFCALCULATOR_QMAX = 25.0; // Constructor --------------------------------------------------------------- -DebyePDFCalculator::DebyePDFCalculator() : - mqminpdf(0.0), - moptimumqstep(false), - mrstep(DEFAULT_PDFCALCULATOR_RSTEP), - mmaxextension(DEFAULT_PDFCALCULATOR_MAXEXTENSION), - mrcalclosteps(0), - mrcalchisteps(0), - mrlimits_are_cached(false) -{ - // default configuration - this->setScatteringFactorTableByType("xray"); - this->setRmax(DEFAULT_PDFCALCULATOR_RMAX); - this->setQmax(DEFAULT_DEBYEPDFCALCULATOR_QMAX); - this->setOptimumQstep(); - // envelopes - this->addEnvelopeByType("scale"); - this->addEnvelopeByType("qresolution"); - // cache all internal data according to an empty structure. - // this rebuilds mstructure_cache and mrlimits_cache. - this->setStructure(mstructure); - // attributes - this->registerDoubleAttribute("qmin", this, - &DebyePDFCalculator::getQmin, &DebyePDFCalculator::setQmin); - this->registerDoubleAttribute("qmax", this, - &DebyePDFCalculator::getQmax, &DebyePDFCalculator::setQmax); - this->registerDoubleAttribute("qstep", this, - &DebyePDFCalculator::getQstep, &DebyePDFCalculator::setQstep); - this->registerDoubleAttribute("rmin", this, - &DebyePDFCalculator::getRmin, &DebyePDFCalculator::setRmin); - this->registerDoubleAttribute("rmax", this, - &DebyePDFCalculator::getRmax, &DebyePDFCalculator::setRmax); - this->registerDoubleAttribute("rstep", this, - &DebyePDFCalculator::getRstep, &DebyePDFCalculator::setRstep); - this->registerDoubleAttribute("maxextension", this, - &DebyePDFCalculator::getMaxExtension, - &DebyePDFCalculator::setMaxExtension); - this->registerDoubleAttribute("extendedrmin", this, - &DebyePDFCalculator::rcalclo); - this->registerDoubleAttribute("extendedrmax", this, - &DebyePDFCalculator::rcalchi); +DebyePDFCalculator::DebyePDFCalculator() + : mqminpdf(0.0), + moptimumqstep(false), + mrstep(DEFAULT_PDFCALCULATOR_RSTEP), + mmaxextension(DEFAULT_PDFCALCULATOR_MAXEXTENSION), + mrcalclosteps(0), + mrcalchisteps(0), + mrlimits_are_cached(false) { + // default configuration + this->setScatteringFactorTableByType("xray"); + this->setRmax(DEFAULT_PDFCALCULATOR_RMAX); + this->setQmax(DEFAULT_DEBYEPDFCALCULATOR_QMAX); + this->setOptimumQstep(); + // envelopes + this->addEnvelopeByType("scale"); + this->addEnvelopeByType("qresolution"); + // cache all internal data according to an empty structure. + // this rebuilds mstructure_cache and mrlimits_cache. + this->setStructure(mstructure); + // attributes + this->registerDoubleAttribute("qmin", this, &DebyePDFCalculator::getQmin, + &DebyePDFCalculator::setQmin); + this->registerDoubleAttribute("qmax", this, &DebyePDFCalculator::getQmax, + &DebyePDFCalculator::setQmax); + this->registerDoubleAttribute("qstep", this, &DebyePDFCalculator::getQstep, + &DebyePDFCalculator::setQstep); + this->registerDoubleAttribute("rmin", this, &DebyePDFCalculator::getRmin, + &DebyePDFCalculator::setRmin); + this->registerDoubleAttribute("rmax", this, &DebyePDFCalculator::getRmax, + &DebyePDFCalculator::setRmax); + this->registerDoubleAttribute("rstep", this, &DebyePDFCalculator::getRstep, + &DebyePDFCalculator::setRstep); + this->registerDoubleAttribute("maxextension", this, + &DebyePDFCalculator::getMaxExtension, + &DebyePDFCalculator::setMaxExtension); + this->registerDoubleAttribute("extendedrmin", this, + &DebyePDFCalculator::rcalclo); + this->registerDoubleAttribute("extendedrmax", this, + &DebyePDFCalculator::rcalchi); } // Public Methods ------------------------------------------------------------ // PairQuantity overloads -eventticker::EventTicker& DebyePDFCalculator::ticker() const -{ - // collect composite tickers for the BaseDebyeSum parent class - eventticker::EventTicker& tic = this->BaseDebyeSum::ticker(); - assert(&mticker == &tic); - tic.updateFrom(this->ScatteringFactorTableOwner::ticker()); - return tic; +eventticker::EventTicker& DebyePDFCalculator::ticker() const { + // collect composite tickers for the BaseDebyeSum parent class + eventticker::EventTicker& tic = this->BaseDebyeSum::ticker(); + assert(&mticker == &tic); + tic.updateFrom(this->ScatteringFactorTableOwner::ticker()); + return tic; } // results -QuantityType DebyePDFCalculator::getPDF() const -{ - QuantityType rgrid = this->getRgrid(); - QuantityType pdf0 = this->getPDFAtQmin(this->getQmin()); - QuantityType pdf1 = this->applyEnvelopes(rgrid, pdf0); - return pdf1; +QuantityType DebyePDFCalculator::getPDF() const { + QuantityType rgrid = this->getRgrid(); + QuantityType pdf0 = this->getPDFAtQmin(this->getQmin()); + QuantityType pdf1 = this->applyEnvelopes(rgrid, pdf0); + return pdf1; } - -QuantityType DebyePDFCalculator::getRDF() const -{ - QuantityType rgrid = this->getRgrid(); - QuantityType rv = this->getRDFperR(); - assert(rv.size() == rgrid.size()); - transform(rgrid.begin(), rgrid.end(), rv.begin(), rv.begin(), +QuantityType DebyePDFCalculator::getRDF() const { + QuantityType rgrid = this->getRgrid(); + QuantityType rv = this->getRDFperR(); + assert(rv.size() == rgrid.size()); + transform(rgrid.begin(), rgrid.end(), rv.begin(), rv.begin(), multiplies()); - return rv; + return rv; } - -QuantityType DebyePDFCalculator::getRDFperR() const -{ - return this->getPDFAtQmin(0.0); +QuantityType DebyePDFCalculator::getRDFperR() const { + return this->getPDFAtQmin(0.0); } // Q-range configuration -void DebyePDFCalculator::setQmin(double qmin) -{ - // NOTE: do not update ticker here, because the actual - // Debye summation is always conducted from Qmin=0. - ensureNonNegative("Qmin", qmin); - this->BaseDebyeSum::setQmin(0.0); - mqminpdf = qmin; +void DebyePDFCalculator::setQmin(double qmin) { + // NOTE: do not update ticker here, because the actual + // Debye summation is always conducted from Qmin=0. + ensureNonNegative("Qmin", qmin); + this->BaseDebyeSum::setQmin(0.0); + mqminpdf = qmin; } +const double& DebyePDFCalculator::getQmin() const { return mqminpdf; } -const double& DebyePDFCalculator::getQmin() const -{ - return mqminpdf; +void DebyePDFCalculator::setQmax(double qmax) { + this->BaseDebyeSum::setQmax(qmax); + this->updateQstep(); } - -void DebyePDFCalculator::setQmax(double qmax) -{ - this->BaseDebyeSum::setQmax(qmax); - this->updateQstep(); +void DebyePDFCalculator::setQstep(double qstep) { + this->BaseDebyeSum::setQstep(qstep); + moptimumqstep = false; + this->updateQstep(); } - -void DebyePDFCalculator::setQstep(double qstep) -{ - this->BaseDebyeSum::setQstep(qstep); - moptimumqstep = false; - this->updateQstep(); -} - - -void DebyePDFCalculator::setOptimumQstep() -{ - moptimumqstep = true; - this->updateQstep(); +void DebyePDFCalculator::setOptimumQstep() { + moptimumqstep = true; + this->updateQstep(); } - -bool DebyePDFCalculator::isOptimumQstep() const -{ - return moptimumqstep; -} +bool DebyePDFCalculator::isOptimumQstep() const { return moptimumqstep; } // R-range methods -QuantityType DebyePDFCalculator::getRgrid() const -{ - return pdfutils_getRgrid(this); +QuantityType DebyePDFCalculator::getRgrid() const { + return pdfutils_getRgrid(this); } // R-range configuration -void DebyePDFCalculator::setRmin(double rmin) -{ - ensureNonNegative("Rmin", rmin); - if (mrmin != rmin) mrlimits_are_cached = false; - this->PairQuantity::setRmin(rmin); +void DebyePDFCalculator::setRmin(double rmin) { + ensureNonNegative("Rmin", rmin); + if (mrmin != rmin) mrlimits_are_cached = false; + this->PairQuantity::setRmin(rmin); } - -void DebyePDFCalculator::setRmax(double rmax) -{ - ensureNonNegative("Rmax", rmax); - if (mrmax != rmax) mrlimits_are_cached = false; - this->PairQuantity::setRmax(rmax); - this->updateQstep(); +void DebyePDFCalculator::setRmax(double rmax) { + ensureNonNegative("Rmax", rmax); + if (mrmax != rmax) mrlimits_are_cached = false; + this->PairQuantity::setRmax(rmax); + this->updateQstep(); } - -void DebyePDFCalculator::setRstep(double rstep) -{ - // NOTE: do not update ticker here, rstep is only used in the FFT - // and not in the Debye summation. - ensureEpsilonPositive("Rstep", rstep); - if (mrstep != rstep) mrlimits_are_cached = false; - mrstep = rstep; +void DebyePDFCalculator::setRstep(double rstep) { + // NOTE: do not update ticker here, rstep is only used in the FFT + // and not in the Debye summation. + ensureEpsilonPositive("Rstep", rstep); + if (mrstep != rstep) mrlimits_are_cached = false; + mrstep = rstep; } +const double& DebyePDFCalculator::getRstep() const { return mrstep; } -const double& DebyePDFCalculator::getRstep() const -{ - return mrstep; +void DebyePDFCalculator::setMaxExtension(double maxextension) { + ensureNonNegative("maxextension", maxextension); + if (mmaxextension != maxextension) { + mticker.click(); + mrlimits_are_cached = false; + } + mmaxextension = maxextension; + this->updateQstep(); } - -void DebyePDFCalculator::setMaxExtension(double maxextension) -{ - ensureNonNegative("maxextension", maxextension); - if (mmaxextension != maxextension) - { - mticker.click(); - mrlimits_are_cached = false; - } - mmaxextension = maxextension; - this->updateQstep(); -} - - -const double& DebyePDFCalculator::getMaxExtension() const -{ - return mmaxextension; +const double& DebyePDFCalculator::getMaxExtension() const { + return mmaxextension; } // Protected Methods --------------------------------------------------------- @@ -233,167 +196,142 @@ namespace { // single implementation for constant and non-constant accept template -void debyepdfcalc_accept(T* obj, diffpy::BaseAttributesVisitor& v) -{ - obj->getPeakWidthModel()->accept(v); - // PDF envelopes - set evnames = obj->usedEnvelopeTypes(); - set::const_iterator nm = evnames.begin(); - for (; nm != evnames.end(); ++nm) - { - obj->getEnvelopeByType(*nm)->accept(v); - } - // finally call standard accept - obj->diffpy::Attributes::accept(v); +void debyepdfcalc_accept(T* obj, diffpy::BaseAttributesVisitor& v) { + obj->getPeakWidthModel()->accept(v); + // PDF envelopes + set evnames = obj->usedEnvelopeTypes(); + set::const_iterator nm = evnames.begin(); + for (; nm != evnames.end(); ++nm) { + obj->getEnvelopeByType(*nm)->accept(v); + } + // finally call standard accept + obj->diffpy::Attributes::accept(v); } -} // namespace - +} // namespace -void DebyePDFCalculator::accept(diffpy::BaseAttributesVisitor& v) -{ - debyepdfcalc_accept(this, v); +void DebyePDFCalculator::accept(diffpy::BaseAttributesVisitor& v) { + debyepdfcalc_accept(this, v); } - -void DebyePDFCalculator::accept(diffpy::BaseAttributesVisitor& v) const -{ - debyepdfcalc_accept(this, v); +void DebyePDFCalculator::accept(diffpy::BaseAttributesVisitor& v) const { + debyepdfcalc_accept(this, v); } // BaseDebyeSum overloads -void DebyePDFCalculator::resetValue() -{ - this->cacheRlimitsData(); - this->updateQstep(); - this->BaseDebyeSum::resetValue(); +void DebyePDFCalculator::resetValue() { + this->cacheRlimitsData(); + this->updateQstep(); + this->BaseDebyeSum::resetValue(); } - -void DebyePDFCalculator::configureBondGenerator(BaseBondGenerator& bnds) const -{ - bnds.setRmin(this->rcalclo()); - bnds.setRmax(this->rcalchi()); +void DebyePDFCalculator::configureBondGenerator(BaseBondGenerator& bnds) const { + bnds.setRmin(this->rcalclo()); + bnds.setRmax(this->rcalchi()); } - -double DebyePDFCalculator::sfSiteAtQ(int siteidx, const double& Q) const -{ - const ScatteringFactorTablePtr& sftable = this->getScatteringFactorTable(); - const string& smbl = mstructure->siteAtomType(siteidx); - const double occupancy = mstructure->siteOccupancy(siteidx); - double rv = sftable->lookup(smbl, Q) * occupancy; - return rv; +double DebyePDFCalculator::sfSiteAtQ(int siteidx, const double& Q) const { + const ScatteringFactorTablePtr& sftable = this->getScatteringFactorTable(); + const string& smbl = mstructure->siteAtomType(siteidx); + const double occupancy = mstructure->siteOccupancy(siteidx); + double rv = sftable->lookup(smbl, Q) * occupancy; + return rv; } // Private Methods ----------------------------------------------------------- -QuantityType DebyePDFCalculator::getPDFAtQmin(double qmin) const -{ - // build a zero padded F vector that gives dr <= rstep - QuantityType fpad = this->getF(); - // zero all F values below qmin - int nqmin = pdfutils_qminSteps(qmin, this->getQstep()); - if (nqmin > int(fpad.size())) nqmin = fpad.size(); - fill(fpad.begin(), fpad.begin() + nqmin, 0.0); - int nfromdr = int(ceil(M_PI / this->getRstep() / this->getQstep())); - if (nfromdr > int(fpad.size())) fpad.resize(nfromdr, 0.0); - QuantityType gpad = fftftog(fpad, this->getQstep()); - const double drpad = M_PI / (gpad.size() * this->getQstep()); - QuantityType rgrid = this->getRgrid(); - QuantityType pdf0(rgrid.size()); - QuantityType::const_iterator ri = rgrid.begin(); - QuantityType::iterator pdfi = pdf0.begin(); - for (; ri != rgrid.end(); ++ri, ++pdfi) - { - double xdrp = *ri / drpad; - int iplo = int(xdrp); - int iphi = iplo + 1; - double wphi = xdrp - iplo; - double wplo = 1.0 - wphi; - assert(iphi < int(gpad.size())); - *pdfi = wplo * gpad[iplo] + wphi * gpad[iphi]; - } - return pdf0; +QuantityType DebyePDFCalculator::getPDFAtQmin(double qmin) const { + // build a zero padded F vector that gives dr <= rstep + QuantityType fpad = this->getF(); + // zero all F values below qmin + int nqmin = pdfutils_qminSteps(qmin, this->getQstep()); + if (nqmin > int(fpad.size())) nqmin = fpad.size(); + fill(fpad.begin(), fpad.begin() + nqmin, 0.0); + int nfromdr = int(ceil(M_PI / this->getRstep() / this->getQstep())); + if (nfromdr > int(fpad.size())) fpad.resize(nfromdr, 0.0); + QuantityType gpad = fftftog(fpad, this->getQstep()); + const double drpad = M_PI / (gpad.size() * this->getQstep()); + QuantityType rgrid = this->getRgrid(); + QuantityType pdf0(rgrid.size()); + QuantityType::const_iterator ri = rgrid.begin(); + QuantityType::iterator pdfi = pdf0.begin(); + for (; ri != rgrid.end(); ++ri, ++pdfi) { + double xdrp = *ri / drpad; + int iplo = int(xdrp); + int iphi = iplo + 1; + double wphi = xdrp - iplo; + double wplo = 1.0 - wphi; + assert(iphi < int(gpad.size())); + *pdfi = wplo * gpad[iplo] + wphi * gpad[iphi]; + } + return pdf0; } - -void DebyePDFCalculator::updateQstep() -{ - double rmaxext = this->rcalchi(); - // Use at least 4 steps to Qmax even for tiny rmaxext. - // Avoid division by zero. - double oqstep = (this->getQmax() * rmaxext / M_PI > 4) ? - (M_PI / rmaxext) : (this->getQmax() / 4); - // if custom qstep is higher than the optimum one, - // force adjustment to the optimum value. - if (this->getQstep() >= oqstep) moptimumqstep = true; - if (moptimumqstep) this->BaseDebyeSum::setQstep(oqstep); +void DebyePDFCalculator::updateQstep() { + double rmaxext = this->rcalchi(); + // Use at least 4 steps to Qmax even for tiny rmaxext. + // Avoid division by zero. + double oqstep = (this->getQmax() * rmaxext / M_PI > 4) + ? (M_PI / rmaxext) + : (this->getQmax() / 4); + // if custom qstep is higher than the optimum one, + // force adjustment to the optimum value. + if (this->getQstep() >= oqstep) moptimumqstep = true; + if (moptimumqstep) this->BaseDebyeSum::setQstep(oqstep); } - -double DebyePDFCalculator::rcalclo() const -{ - if (!mrlimits_are_cached) this->cacheRlimitsData(); - double rv = mrcalclosteps * this->getRstep(); - return rv; +double DebyePDFCalculator::rcalclo() const { + if (!mrlimits_are_cached) this->cacheRlimitsData(); + double rv = mrcalclosteps * this->getRstep(); + return rv; } - -double DebyePDFCalculator::rcalchi() const -{ - if (!mrlimits_are_cached) this->cacheRlimitsData(); - double rv = mrcalchisteps * this->getRstep(); - return rv; +double DebyePDFCalculator::rcalchi() const { + if (!mrlimits_are_cached) this->cacheRlimitsData(); + double rv = mrcalchisteps * this->getRstep(); + return rv; } - -double DebyePDFCalculator::extFromTerminationRipples() const -{ - // number of termination ripples for extending the r-range - const int nripples = 6; - // extension due to termination ripples - double rv = (this->getQmax() > 0.0) ? - (nripples*2*M_PI / this->getQmax()) : 0.0; - return rv; +double DebyePDFCalculator::extFromTerminationRipples() const { + // number of termination ripples for extending the r-range + const int nripples = 6; + // extension due to termination ripples + double rv = + (this->getQmax() > 0.0) ? (nripples * 2 * M_PI / this->getQmax()) : 0.0; + return rv; } - -double DebyePDFCalculator::extFromPeakTails() const -{ - const PeakWidthModel& pwm = *(this->getPeakWidthModel()); - double maxfwhm = pwm.maxWidth( - mstructure, this->getRmin(), this->getRmax()); - // assume Gaussian peak profile - GaussianProfile pkf; - pkf.setPrecision(DEFAULT_PEAKPRECISION); - // Gaussian function is symmetric, no need to use xboundlo - double rv = pkf.xboundhi(maxfwhm); - return rv; +double DebyePDFCalculator::extFromPeakTails() const { + const PeakWidthModel& pwm = *(this->getPeakWidthModel()); + double maxfwhm = pwm.maxWidth(mstructure, this->getRmin(), this->getRmax()); + // assume Gaussian peak profile + GaussianProfile pkf; + pkf.setPrecision(DEFAULT_PEAKPRECISION); + // Gaussian function is symmetric, no need to use xboundlo + double rv = pkf.xboundhi(maxfwhm); + return rv; } - -void DebyePDFCalculator::cacheRlimitsData() const -{ - const int minreductionsteps = 50; - double ext_total = min(this->getMaxExtension(), +void DebyePDFCalculator::cacheRlimitsData() const { + const int minreductionsteps = 50; + double ext_total = + min(this->getMaxExtension(), this->extFromTerminationRipples() + this->extFromPeakTails()); - // abbreviations - const double& rmin = this->getRmin(); - const double& rmax = this->getRmax(); - const double& dr = this->getRstep(); - mrcalclosteps = max(0, pdfutils_rminSteps(rmin - ext_total, dr)); - const int nhi = pdfutils_rmaxSteps(rmax + ext_total, dr); - if (nhi <= (mrcalchisteps - minreductionsteps) || nhi > mrcalchisteps) - { - mrcalchisteps = nhi; - } - mrlimits_are_cached = true; + // abbreviations + const double& rmin = this->getRmin(); + const double& rmax = this->getRmax(); + const double& dr = this->getRstep(); + mrcalclosteps = max(0, pdfutils_rminSteps(rmin - ext_total, dr)); + const int nhi = pdfutils_rmaxSteps(rmax + ext_total, dr); + if (nhi <= (mrcalchisteps - minreductionsteps) || nhi > mrcalchisteps) { + mrcalchisteps = nhi; + } + mrlimits_are_cached = true; } -} // namespace srreal -} // namespace diffpy +} // namespace srreal +} // namespace diffpy // Serialization ------------------------------------------------------------- diff --git a/src/diffpy/srreal/DebyePDFCalculator.hpp b/src/diffpy/srreal/DebyePDFCalculator.hpp index 0ebde0d3..555950af 100644 --- a/src/diffpy/srreal/DebyePDFCalculator.hpp +++ b/src/diffpy/srreal/DebyePDFCalculator.hpp @@ -1,20 +1,20 @@ /***************************************************************************** -* -* 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, Chris Farrow -* -* See AUTHORS.rst for a list of people who contributed. -* See LICENSE_DANSE.txt for license information. -* -****************************************************************************** -* -* class DebyePDFCalculator -- calculate PDF from the Debye equation. -* -*****************************************************************************/ + * + * 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, Chris Farrow + * + * See AUTHORS.rst for a list of people who contributed. + * See LICENSE_DANSE.txt for license information. + * + ****************************************************************************** + * + * class DebyePDFCalculator -- calculate PDF from the Debye equation. + * + *****************************************************************************/ // Comments from Chris Farrow's sources for PDFNanoProfile: // @@ -57,106 +57,100 @@ namespace diffpy { namespace srreal { -class DLL_EXPORT DebyePDFCalculator : - public BaseDebyeSum, - public ScatteringFactorTableOwner, - public PDFEnvelopeOwner -{ - public: - - // constructor - DebyePDFCalculator(); - - // PairQuantity overloads - virtual eventticker::EventTicker& ticker() const; - - // results - /// PDF on the specified r-grid - QuantityType getPDF() const; - QuantityType getRDF() const; - QuantityType getRDFperR() const; - - // Q-range configuration - void setQmin(double); - const double& getQmin() const; - void setQmax(double); - void setQstep(double); - void setOptimumQstep(); - bool isOptimumQstep() const; - - // R-range methods - QuantityType getRgrid() const; - // R-range configuration - virtual void setRmin(double); - virtual void setRmax(double); - void setRstep(double); - const double& getRstep() const; - /// maximum total extension of the r-range accounting for both - /// termination ripples and peak tails - void setMaxExtension(double); - /// maximum total extension of the r-range accounting for both - /// termination ripples and peak tails - const double& getMaxExtension() const; - - protected: - - // Attributes overload to direct visitors around data structures - virtual void accept(diffpy::BaseAttributesVisitor& v); - virtual void accept(diffpy::BaseAttributesVisitor& v) const; - - // BaseDebyeSum overloads - virtual void resetValue(); - virtual void configureBondGenerator(BaseBondGenerator&) const; - virtual double sfSiteAtQ(int, const double& Q) const; - - private: - - // methods - QuantityType getPDFAtQmin(double qmin) const; - void updateQstep(); - /// complete lower bound extension of the calculated grid - double rcalclo() const; - /// complete upper bound extension of the calculated grid - double rcalchi() const; - /// r-range extension to allow propagation of termination ripples - double extFromTerminationRipples() const; - /// r-range extension to account for tails from out-of-range peaks - double extFromPeakTails() const; - void cacheRlimitsData() const; - - // data - double mqminpdf; - bool moptimumqstep; - double mrstep; - double mmaxextension; - mutable int mrcalclosteps; - mutable int mrcalchisteps; - mutable bool mrlimits_are_cached; - - // serialization - friend class boost::serialization::access; - template - void serialize(Archive& ar, const unsigned int version) - { - using boost::serialization::base_object; - ar & base_object(*this); - ar & base_object(*this); - ar & base_object(*this); - ar & mqminpdf; - ar & moptimumqstep; - ar & mrstep; - ar & mmaxextension; - ar & mrcalclosteps; - ar & mrcalchisteps; - if (version >= 1) { - ar & mrlimits_are_cached; - } - } +class DLL_EXPORT DebyePDFCalculator : public BaseDebyeSum, + public ScatteringFactorTableOwner, + public PDFEnvelopeOwner { + public: + // constructor + DebyePDFCalculator(); + + // PairQuantity overloads + virtual eventticker::EventTicker& ticker() const; + + // results + /// PDF on the specified r-grid + QuantityType getPDF() const; + QuantityType getRDF() const; + QuantityType getRDFperR() const; + + // Q-range configuration + void setQmin(double); + const double& getQmin() const; + void setQmax(double); + void setQstep(double); + void setOptimumQstep(); + bool isOptimumQstep() const; + + // R-range methods + QuantityType getRgrid() const; + // R-range configuration + virtual void setRmin(double); + virtual void setRmax(double); + void setRstep(double); + const double& getRstep() const; + /// maximum total extension of the r-range accounting for both + /// termination ripples and peak tails + void setMaxExtension(double); + /// maximum total extension of the r-range accounting for both + /// termination ripples and peak tails + const double& getMaxExtension() const; + + protected: + // Attributes overload to direct visitors around data structures + virtual void accept(diffpy::BaseAttributesVisitor& v); + virtual void accept(diffpy::BaseAttributesVisitor& v) const; + + // BaseDebyeSum overloads + virtual void resetValue(); + virtual void configureBondGenerator(BaseBondGenerator&) const; + virtual double sfSiteAtQ(int, const double& Q) const; + + private: + // methods + QuantityType getPDFAtQmin(double qmin) const; + void updateQstep(); + /// complete lower bound extension of the calculated grid + double rcalclo() const; + /// complete upper bound extension of the calculated grid + double rcalchi() const; + /// r-range extension to allow propagation of termination ripples + double extFromTerminationRipples() const; + /// r-range extension to account for tails from out-of-range peaks + double extFromPeakTails() const; + void cacheRlimitsData() const; + + // data + double mqminpdf; + bool moptimumqstep; + double mrstep; + double mmaxextension; + mutable int mrcalclosteps; + mutable int mrcalchisteps; + mutable bool mrlimits_are_cached; + + // serialization + friend class boost::serialization::access; + template + void serialize(Archive& ar, const unsigned int version) { + using boost::serialization::base_object; + ar& base_object(*this); + ar& base_object(*this); + ar& base_object(*this); + ar & mqminpdf; + ar & moptimumqstep; + ar & mrstep; + ar & mmaxextension; + ar & mrcalclosteps; + ar & mrcalchisteps; + if (version >= 1) { + ar & mrlimits_are_cached; + } + } }; // class DebyePDFCalculator -} // namespace srreal -} // namespace diffpy +} // namespace srreal +} // namespace diffpy // Serialization ------------------------------------------------------------- diff --git a/src/diffpy/srreal/DebyeWallerPeakWidth.cpp b/src/diffpy/srreal/DebyeWallerPeakWidth.cpp index bb82a6a3..dc6d30b1 100644 --- a/src/diffpy/srreal/DebyeWallerPeakWidth.cpp +++ b/src/diffpy/srreal/DebyeWallerPeakWidth.cpp @@ -1,21 +1,21 @@ /***************************************************************************** -* -* 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: Christopher Farrow, Pavol Juhas -* -* See AUTHORS.rst for a list of people who contributed. -* See LICENSE_DANSE.txt for license information. -* -****************************************************************************** -* -* class DebyeWallerPeakWidth -- peak width calculated assuming independent -* thermal vibrations of atoms forming a pair. -* -*****************************************************************************/ + * + * 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: Christopher Farrow, Pavol Juhas + * + * See AUTHORS.rst for a list of people who contributed. + * See LICENSE_DANSE.txt for license information. + * + ****************************************************************************** + * + * class DebyeWallerPeakWidth -- peak width calculated assuming independent + * thermal vibrations of atoms forming a pair. + * + *****************************************************************************/ #include #include @@ -28,52 +28,44 @@ using namespace std; // Constructors -------------------------------------------------------------- -PeakWidthModelPtr DebyeWallerPeakWidth::create() const -{ - PeakWidthModelPtr rv(new DebyeWallerPeakWidth()); - return rv; +PeakWidthModelPtr DebyeWallerPeakWidth::create() const { + PeakWidthModelPtr rv(new DebyeWallerPeakWidth()); + return rv; } - -PeakWidthModelPtr DebyeWallerPeakWidth::clone() const -{ - PeakWidthModelPtr rv(new DebyeWallerPeakWidth(*this)); - return rv; +PeakWidthModelPtr DebyeWallerPeakWidth::clone() const { + PeakWidthModelPtr rv(new DebyeWallerPeakWidth(*this)); + return rv; } // Public Methods ------------------------------------------------------------ -const string& DebyeWallerPeakWidth::type() const -{ - static const string rv = "debye-waller"; - return rv; +const string& DebyeWallerPeakWidth::type() const { + static const string rv = "debye-waller"; + return rv; } - -double DebyeWallerPeakWidth::calculate(const BaseBondGenerator& bnds) const -{ - using diffpy::mathutils::GAUSS_SIGMA_TO_FWHM; - double msdval = bnds.msd(); - double rv = (msdval < 0.0) ? 0.0 : GAUSS_SIGMA_TO_FWHM * sqrt(msdval); - return rv; +double DebyeWallerPeakWidth::calculate(const BaseBondGenerator& bnds) const { + using diffpy::mathutils::GAUSS_SIGMA_TO_FWHM; + double msdval = bnds.msd(); + double rv = (msdval < 0.0) ? 0.0 : GAUSS_SIGMA_TO_FWHM * sqrt(msdval); + return rv; } - -double DebyeWallerPeakWidth::maxWidth(StructureAdapterPtr stru, - double rmin, double rmax) const -{ - using diffpy::mathutils::GAUSS_SIGMA_TO_FWHM; - double maxmsd = 2 * maxUii(stru); - double rv = (maxmsd <= 0.0) ? 0.0 : GAUSS_SIGMA_TO_FWHM * sqrt(maxmsd); - return rv; +double DebyeWallerPeakWidth::maxWidth(StructureAdapterPtr stru, double rmin, + double rmax) const { + using diffpy::mathutils::GAUSS_SIGMA_TO_FWHM; + double maxmsd = 2 * maxUii(stru); + double rv = (maxmsd <= 0.0) ? 0.0 : GAUSS_SIGMA_TO_FWHM * sqrt(maxmsd); + return rv; } // Registration -------------------------------------------------------------- bool reg_DebyeWallerPeakWidth = DebyeWallerPeakWidth().registerThisType(); -} // namespace srreal -} // namespace diffpy +} // namespace srreal +} // namespace diffpy // Serialization ------------------------------------------------------------- diff --git a/src/diffpy/srreal/DebyeWallerPeakWidth.hpp b/src/diffpy/srreal/DebyeWallerPeakWidth.hpp index 065ba77d..551aa7db 100644 --- a/src/diffpy/srreal/DebyeWallerPeakWidth.hpp +++ b/src/diffpy/srreal/DebyeWallerPeakWidth.hpp @@ -1,21 +1,21 @@ /***************************************************************************** -* -* 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: Christopher Farrow, Pavol Juhas -* -* See AUTHORS.rst for a list of people who contributed. -* See LICENSE_DANSE.txt for license information. -* -****************************************************************************** -* -* class DebyeWallerPeakWidth -- peak width calculated assuming independent -* thermal vibrations of atoms forming a pair. -* -*****************************************************************************/ + * + * 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: Christopher Farrow, Pavol Juhas + * + * See AUTHORS.rst for a list of people who contributed. + * See LICENSE_DANSE.txt for license information. + * + ****************************************************************************** + * + * class DebyeWallerPeakWidth -- peak width calculated assuming independent + * thermal vibrations of atoms forming a pair. + * + *****************************************************************************/ #ifndef DEBYEWALLERPEAKWIDTH_HPP_INCLUDED #define DEBYEWALLERPEAKWIDTH_HPP_INCLUDED @@ -27,37 +27,30 @@ namespace diffpy { namespace srreal { - -class DLL_EXPORT DebyeWallerPeakWidth : public PeakWidthModel -{ - public: - - // constructors - virtual PeakWidthModelPtr create() const; - virtual PeakWidthModelPtr clone() const; - - // methods - virtual const std::string& type() const; - virtual double calculate(const BaseBondGenerator&) const; - virtual double maxWidth(StructureAdapterPtr, - double rmin, double rmax) const; - - private: - - // serialization - friend class boost::serialization::access; - - template - void serialize(Archive& ar, const unsigned int version) - { - using boost::serialization::base_object; - ar & base_object(*this); - } - +class DLL_EXPORT DebyeWallerPeakWidth : public PeakWidthModel { + public: + // constructors + virtual PeakWidthModelPtr create() const; + virtual PeakWidthModelPtr clone() const; + + // methods + virtual const std::string& type() const; + virtual double calculate(const BaseBondGenerator&) const; + virtual double maxWidth(StructureAdapterPtr, double rmin, double rmax) const; + + private: + // serialization + friend class boost::serialization::access; + + template + void serialize(Archive& ar, const unsigned int version) { + using boost::serialization::base_object; + ar& base_object(*this); + } }; -} // namespace srreal -} // namespace diffpy +} // namespace srreal +} // namespace diffpy // Serialization ------------------------------------------------------------- diff --git a/src/diffpy/srreal/EmptyStructureAdapter.cpp b/src/diffpy/srreal/EmptyStructureAdapter.cpp index 1dc5b2a5..fb3e3c86 100644 --- a/src/diffpy/srreal/EmptyStructureAdapter.cpp +++ b/src/diffpy/srreal/EmptyStructureAdapter.cpp @@ -1,20 +1,20 @@ /***************************************************************************** -* -* 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 EmptyStructureAdapter -- immutable class that is always empty. -* -*****************************************************************************/ + * + * 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 EmptyStructureAdapter -- immutable class that is always empty. + * + *****************************************************************************/ #include @@ -30,101 +30,69 @@ namespace { std::out_of_range outofrange("Index out of range."); -} // namespace +} // namespace ////////////////////////////////////////////////////////////////////////////// // class EmptyStructureAdapter ////////////////////////////////////////////////////////////////////////////// -class EmptyStructureAdapter : public StructureAdapter -{ - public: +class EmptyStructureAdapter : public StructureAdapter { + public: + // virtual methods from the base class - // virtual methods from the base class + virtual StructureAdapterPtr clone() const { + StructureAdapterPtr rv = + std::const_pointer_cast(shared_from_this()); + return rv; + } - virtual StructureAdapterPtr clone() const - { - StructureAdapterPtr rv = - std::const_pointer_cast(shared_from_this()); - return rv; - } + virtual BaseBondGeneratorPtr createBondGenerator() const { + return std::make_shared(shared_from_this()); + } + virtual int countSites() const { return 0; } - virtual BaseBondGeneratorPtr createBondGenerator() const - { - return std::make_shared(shared_from_this()); - } + // reusing StructureAdapter::totalOccupancy() + // reusing StructureAdapter::numberDensity() + virtual const std::string& siteAtomType(int idx) const { throw outofrange; } - virtual int countSites() const - { - return 0; - } + virtual const R3::Vector& siteCartesianPosition(int idx) const { + throw outofrange; + } - // reusing StructureAdapter::totalOccupancy() - // reusing StructureAdapter::numberDensity() + virtual int siteMultiplicity(int idx) const { throw outofrange; } - virtual const std::string& siteAtomType(int idx) const - { - throw outofrange; - } + virtual double siteOccupancy(int idx) const { throw outofrange; } + virtual bool siteAnisotropy(int idx) const { throw outofrange; } - virtual const R3::Vector& siteCartesianPosition(int idx) const - { - throw outofrange; - } + virtual const R3::Matrix& siteCartesianUij(int idx) const { + throw outofrange; + } + // reusing StructureAdapter::customPQConfig() + // reusing StructureAdapter::diff() - virtual int siteMultiplicity(int idx) const - { - throw outofrange; - } - - - virtual double siteOccupancy(int idx) const - { - throw outofrange; - } - - - virtual bool siteAnisotropy(int idx) const - { - throw outofrange; - } - - - virtual const R3::Matrix& siteCartesianUij(int idx) const - { - throw outofrange; - } - - // reusing StructureAdapter::customPQConfig() - // reusing StructureAdapter::diff() - - private: - - // serialization - friend class boost::serialization::access; - template - void serialize(Archive& ar, const unsigned int version) - { - ar & boost::serialization::base_object(*this); - } - + private: + // serialization + friend class boost::serialization::access; + template + void serialize(Archive& ar, const unsigned int version) { + ar& boost::serialization::base_object(*this); + } }; // Routines ------------------------------------------------------------------ -StructureAdapterPtr emptyStructureAdapter() -{ - static StructureAdapterPtr stru(new EmptyStructureAdapter); - assert(stru && stru->countSites() == 0); - return stru; +StructureAdapterPtr emptyStructureAdapter() { + static StructureAdapterPtr stru(new EmptyStructureAdapter); + assert(stru && stru->countSites() == 0); + return stru; } -} // namespace srreal -} // namespace diffpy +} // namespace srreal +} // namespace diffpy BOOST_CLASS_EXPORT(diffpy::srreal::EmptyStructureAdapter) DIFFPY_INSTANTIATE_PTR_SERIALIZATION(diffpy::srreal::EmptyStructureAdapter) diff --git a/src/diffpy/srreal/GaussianProfile.cpp b/src/diffpy/srreal/GaussianProfile.cpp index 3b833752..c9115c71 100644 --- a/src/diffpy/srreal/GaussianProfile.cpp +++ b/src/diffpy/srreal/GaussianProfile.cpp @@ -1,21 +1,21 @@ /***************************************************************************** -* -* 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.txt for license information. -* -****************************************************************************** -* -* class GaussianProfile -- concrete implementation of the PeakProfile class. -* GaussianProfile is registered as "gaussian". -* -*****************************************************************************/ + * + * 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.txt for license information. + * + ****************************************************************************** + * + * class GaussianProfile -- concrete implementation of the PeakProfile class. + * GaussianProfile is registered as "gaussian". + * + *****************************************************************************/ #include @@ -32,72 +32,60 @@ using diffpy::mathutils::DOUBLE_EPS; // Constructors -------------------------------------------------------------- -GaussianProfile::GaussianProfile() -{ - // call setPrecision to update mhalfboundrel - this->setPrecision(DOUBLE_EPS); +GaussianProfile::GaussianProfile() { + // call setPrecision to update mhalfboundrel + this->setPrecision(DOUBLE_EPS); } - -PeakProfilePtr GaussianProfile::create() const -{ - PeakProfilePtr rv(new GaussianProfile()); - return rv; +PeakProfilePtr GaussianProfile::create() const { + PeakProfilePtr rv(new GaussianProfile()); + return rv; } - -PeakProfilePtr GaussianProfile::clone() const -{ - PeakProfilePtr rv(new GaussianProfile(*this)); - return rv; +PeakProfilePtr GaussianProfile::clone() const { + PeakProfilePtr rv(new GaussianProfile(*this)); + return rv; } // Public Methods ------------------------------------------------------------ -const string& GaussianProfile::type() const -{ - static string rv = "gaussian"; - return rv; +const string& GaussianProfile::type() const { + static string rv = "gaussian"; + return rv; } - -double GaussianProfile::operator()(double x, double fwhm) const -{ - if ( fwhm <= 0 ) return 0.0; - double xrel = x / fwhm; - double rv = 2 * sqrt(M_LN2 / M_PI) / fwhm * exp(-4 * M_LN2 * xrel * xrel); - return rv; +double GaussianProfile::operator()(double x, double fwhm) const { + if (fwhm <= 0) return 0.0; + double xrel = x / fwhm; + double rv = 2 * sqrt(M_LN2 / M_PI) / fwhm * exp(-4 * M_LN2 * xrel * xrel); + return rv; } - -double GaussianProfile::xboundlo(double fwhm) const -{ - return -1 * this->GaussianProfile::xboundhi(fwhm); +double GaussianProfile::xboundlo(double fwhm) const { + return -1 * this->GaussianProfile::xboundhi(fwhm); } - -double GaussianProfile::xboundhi(double fwhm) const -{ - double rv = (fwhm <= 0.0) ? 0.0 : (mhalfboundrel * fwhm); - return rv; +double GaussianProfile::xboundhi(double fwhm) const { + double rv = (fwhm <= 0.0) ? 0.0 : (mhalfboundrel * fwhm); + return rv; } - -void GaussianProfile::setPrecision(double eps) -{ - // correct any settings below DOUBLE_EPS - double eps1 = max(eps, DOUBLE_EPS); - this->PeakProfile::setPrecision(eps1); - if (eps1 < 1.0) mhalfboundrel = sqrt(-log(eps1) / (4 * M_LN2)); - else mhalfboundrel = 0.0; +void GaussianProfile::setPrecision(double eps) { + // correct any settings below DOUBLE_EPS + double eps1 = max(eps, DOUBLE_EPS); + this->PeakProfile::setPrecision(eps1); + if (eps1 < 1.0) + mhalfboundrel = sqrt(-log(eps1) / (4 * M_LN2)); + else + mhalfboundrel = 0.0; } // Registration -------------------------------------------------------------- bool reg_GaussianProfile = GaussianProfile().registerThisType(); -} // namespace srreal -} // namespace diffpy +} // namespace srreal +} // namespace diffpy // Serialization ------------------------------------------------------------- diff --git a/src/diffpy/srreal/GaussianProfile.hpp b/src/diffpy/srreal/GaussianProfile.hpp index ac6a80e4..305fb31e 100644 --- a/src/diffpy/srreal/GaussianProfile.hpp +++ b/src/diffpy/srreal/GaussianProfile.hpp @@ -1,21 +1,21 @@ /***************************************************************************** -* -* 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.txt for license information. -* -****************************************************************************** -* -* class GaussianProfile -- concrete implementation of the PeakProfile class. -* GaussianProfile is registered as "gaussian". -* -*****************************************************************************/ + * + * 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.txt for license information. + * + ****************************************************************************** + * + * class GaussianProfile -- concrete implementation of the PeakProfile class. + * GaussianProfile is registered as "gaussian". + * + *****************************************************************************/ #ifndef GAUSSIANPROFILE_HPP_INCLUDED #define GAUSSIANPROFILE_HPP_INCLUDED @@ -27,44 +27,38 @@ namespace diffpy { namespace srreal { -class DLL_EXPORT GaussianProfile : public PeakProfile -{ - public: - - // constructors - GaussianProfile(); - PeakProfilePtr create() const; - PeakProfilePtr clone() const; - - // methods - const std::string& type() const; - double operator()(double x, double fwhm) const; - double xboundlo(double fwhm) const; - double xboundhi(double fwhm) const; - void setPrecision(double eps); - - protected: - - // data - double mhalfboundrel; - - private: - - // serialization - friend class boost::serialization::access; - - template - void serialize(Archive& ar, const unsigned int version) - { - using boost::serialization::base_object; - ar & base_object(*this); - ar & mhalfboundrel; - } - +class DLL_EXPORT GaussianProfile : public PeakProfile { + public: + // constructors + GaussianProfile(); + PeakProfilePtr create() const; + PeakProfilePtr clone() const; + + // methods + const std::string& type() const; + double operator()(double x, double fwhm) const; + double xboundlo(double fwhm) const; + double xboundhi(double fwhm) const; + void setPrecision(double eps); + + protected: + // data + double mhalfboundrel; + + private: + // serialization + friend class boost::serialization::access; + + template + void serialize(Archive& ar, const unsigned int version) { + using boost::serialization::base_object; + ar& base_object(*this); + ar & mhalfboundrel; + } }; -} // namespace srreal -} // namespace diffpy +} // namespace srreal +} // namespace diffpy // Serialization ------------------------------------------------------------- diff --git a/src/diffpy/srreal/JeongPeakWidth.cpp b/src/diffpy/srreal/JeongPeakWidth.cpp index ca2ef297..196a95f7 100644 --- a/src/diffpy/srreal/JeongPeakWidth.cpp +++ b/src/diffpy/srreal/JeongPeakWidth.cpp @@ -1,22 +1,22 @@ /***************************************************************************** -* -* 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: Christopher Farrow, Pavol Juhas -* -* See AUTHORS.rst for a list of people who contributed. -* See LICENSE_DANSE.txt for license information. -* -****************************************************************************** -* -* class DebyeWallerPeakWidth -- peak width model based on -* I.-K. Jeong, et al., Phys. Rev. B 67, 104301 (2003) -* http://link.aps.org/doi/10.1103/PhysRevB.67.104301 -* -*****************************************************************************/ + * + * 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: Christopher Farrow, Pavol Juhas + * + * See AUTHORS.rst for a list of people who contributed. + * See LICENSE_DANSE.txt for license information. + * + ****************************************************************************** + * + * class DebyeWallerPeakWidth -- peak width model based on + * I.-K. Jeong, et al., Phys. Rev. B 67, 104301 (2003) + * http://link.aps.org/doi/10.1103/PhysRevB.67.104301 + * + *****************************************************************************/ #include #include @@ -29,135 +29,105 @@ using namespace std; // Constructors -------------------------------------------------------------- -JeongPeakWidth::JeongPeakWidth() : - mdelta1(0.0), mdelta2(0.0), mqbroad(0.0), mqbroad_seperable(0.0) -{ - this->registerDoubleAttribute("delta1", - this, &JeongPeakWidth::getDelta1, &JeongPeakWidth::setDelta1); - this->registerDoubleAttribute("delta2", - this, &JeongPeakWidth::getDelta2, &JeongPeakWidth::setDelta2); - this->registerDoubleAttribute("qbroad", - this, &JeongPeakWidth::getQbroad, &JeongPeakWidth::setQbroad); - this->registerDoubleAttribute("qbroad_seperable", - this, &JeongPeakWidth::getQbroad_seperable, &JeongPeakWidth::setQbroad_seperable); +JeongPeakWidth::JeongPeakWidth() + : mdelta1(0.0), mdelta2(0.0), mqbroad(0.0), mqbroad_seperable(0.0) { + this->registerDoubleAttribute("delta1", this, &JeongPeakWidth::getDelta1, + &JeongPeakWidth::setDelta1); + this->registerDoubleAttribute("delta2", this, &JeongPeakWidth::getDelta2, + &JeongPeakWidth::setDelta2); + this->registerDoubleAttribute("qbroad", this, &JeongPeakWidth::getQbroad, + &JeongPeakWidth::setQbroad); + this->registerDoubleAttribute("qbroad_seperable", this, + &JeongPeakWidth::getQbroad_seperable, + &JeongPeakWidth::setQbroad_seperable); } - -PeakWidthModelPtr JeongPeakWidth::create() const -{ - PeakWidthModelPtr rv(new JeongPeakWidth()); - return rv; +PeakWidthModelPtr JeongPeakWidth::create() const { + PeakWidthModelPtr rv(new JeongPeakWidth()); + return rv; } - -PeakWidthModelPtr JeongPeakWidth::clone() const -{ - PeakWidthModelPtr rv(new JeongPeakWidth(*this)); - return rv; +PeakWidthModelPtr JeongPeakWidth::clone() const { + PeakWidthModelPtr rv(new JeongPeakWidth(*this)); + return rv; } // Public Methods ------------------------------------------------------------ -const string& JeongPeakWidth::type() const -{ - static const string rv = "jeong"; - return rv; -} - - -double JeongPeakWidth::calculate(const BaseBondGenerator& bnds) const -{ - double r = bnds.distance(); - double corr = this->msdSharpeningRatio(r); - // avoid calculating square root of negative value - double fwhm = (corr <= 0) ? 0.0 : - (sqrt(corr) * this->DebyeWallerPeakWidth::calculate(bnds) + - pow(this->getQbroad_seperable()*r, 2)); - return fwhm; -} - - -double JeongPeakWidth::maxWidth(StructureAdapterPtr stru, - double rmin, double rmax) const -{ - double maxwidth0 = this->DebyeWallerPeakWidth::maxWidth(stru, rmin, rmax); - double maxmsdsharp = max( - this->msdSharpeningRatio(rmin), - this->msdSharpeningRatio(rmax)); - // if the sharpening factor is larger than 1 adjust the maximum width - double rv = maxwidth0 * sqrt(max(1.0, maxmsdsharp))+ pow(this->getQbroad_seperable()*rmax, 2); - return rv; +const string& JeongPeakWidth::type() const { + static const string rv = "jeong"; + return rv; } -const double& JeongPeakWidth::getDelta1() const -{ - return mdelta1; +double JeongPeakWidth::calculate(const BaseBondGenerator& bnds) const { + double r = bnds.distance(); + double corr = this->msdSharpeningRatio(r); + // avoid calculating square root of negative value + double fwhm = (corr <= 0) + ? 0.0 + : (sqrt(corr) * this->DebyeWallerPeakWidth::calculate(bnds) + + pow(this->getQbroad_seperable() * r, 2)); + return fwhm; } - -void JeongPeakWidth::setDelta1(double delta1) -{ - if (mdelta1 != delta1) mticker.click(); - mdelta1 = delta1; +double JeongPeakWidth::maxWidth(StructureAdapterPtr stru, double rmin, + double rmax) const { + double maxwidth0 = this->DebyeWallerPeakWidth::maxWidth(stru, rmin, rmax); + double maxmsdsharp = + max(this->msdSharpeningRatio(rmin), this->msdSharpeningRatio(rmax)); + // if the sharpening factor is larger than 1 adjust the maximum width + double rv = maxwidth0 * sqrt(max(1.0, maxmsdsharp)) + + pow(this->getQbroad_seperable() * rmax, 2); + return rv; } +const double& JeongPeakWidth::getDelta1() const { return mdelta1; } -const double& JeongPeakWidth::getDelta2() const -{ - return mdelta2; +void JeongPeakWidth::setDelta1(double delta1) { + if (mdelta1 != delta1) mticker.click(); + mdelta1 = delta1; } +const double& JeongPeakWidth::getDelta2() const { return mdelta2; } -void JeongPeakWidth::setDelta2(double delta2) -{ - if (mdelta2 != delta2) mticker.click(); - mdelta2 = delta2; +void JeongPeakWidth::setDelta2(double delta2) { + if (mdelta2 != delta2) mticker.click(); + mdelta2 = delta2; } +const double& JeongPeakWidth::getQbroad() const { return mqbroad; } -const double& JeongPeakWidth::getQbroad() const -{ - return mqbroad; +const double& JeongPeakWidth::getQbroad_seperable() const { + return mqbroad_seperable; } - -const double& JeongPeakWidth::getQbroad_seperable() const -{ - return mqbroad_seperable; +void JeongPeakWidth::setQbroad_seperable(double qbroad_seperable) { + if (mqbroad_seperable != qbroad_seperable) mticker.click(); + mqbroad_seperable = qbroad_seperable; } - -void JeongPeakWidth::setQbroad_seperable(double qbroad_seperable) -{ - if (mqbroad_seperable != qbroad_seperable) mticker.click(); - mqbroad_seperable = qbroad_seperable; -} - - -void JeongPeakWidth::setQbroad(double qbroad) -{ - if (mqbroad != qbroad) mticker.click(); - mqbroad = qbroad; +void JeongPeakWidth::setQbroad(double qbroad) { + if (mqbroad != qbroad) mticker.click(); + mqbroad = qbroad; } // Private Methods ----------------------------------------------------------- -double JeongPeakWidth::msdSharpeningRatio(const double& r) const -{ - using diffpy::mathutils::DOUBLE_EPS; - // avoid division by zero - if (r < DOUBLE_EPS) return 0.0; - double rv = 1.0 - this->getDelta1()/r - this->getDelta2()/pow(r, 2) + - pow(this->getQbroad()*r, 2); - return rv; +double JeongPeakWidth::msdSharpeningRatio(const double& r) const { + using diffpy::mathutils::DOUBLE_EPS; + // avoid division by zero + if (r < DOUBLE_EPS) return 0.0; + double rv = 1.0 - this->getDelta1() / r - this->getDelta2() / pow(r, 2) + + pow(this->getQbroad() * r, 2); + return rv; } // Registration -------------------------------------------------------------- bool reg_JeongPeakWidth = JeongPeakWidth().registerThisType(); -} // namespace srreal -} // namespace diffpy +} // namespace srreal +} // namespace diffpy // Serialization ------------------------------------------------------------- diff --git a/src/diffpy/srreal/JeongPeakWidth.hpp b/src/diffpy/srreal/JeongPeakWidth.hpp index dd06a5cf..3ca3ffdb 100644 --- a/src/diffpy/srreal/JeongPeakWidth.hpp +++ b/src/diffpy/srreal/JeongPeakWidth.hpp @@ -1,22 +1,22 @@ /***************************************************************************** -* -* 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: Christopher Farrow, Pavol Juhas -* -* See AUTHORS.rst for a list of people who contributed. -* See LICENSE_DANSE.txt for license information. -* -****************************************************************************** -* -* class DebyeWallerPeakWidth -- peak width model based on -* I.-K. Jeong, et al., Phys. Rev. B 67, 104301 (2003) -* http://link.aps.org/doi/10.1103/PhysRevB.67.104301 -* -*****************************************************************************/ + * + * 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: Christopher Farrow, Pavol Juhas + * + * See AUTHORS.rst for a list of people who contributed. + * See LICENSE_DANSE.txt for license information. + * + ****************************************************************************** + * + * class DebyeWallerPeakWidth -- peak width model based on + * I.-K. Jeong, et al., Phys. Rev. B 67, 104301 (2003) + * http://link.aps.org/doi/10.1103/PhysRevB.67.104301 + * + *****************************************************************************/ #ifndef JEONGPEAKWIDTH_HPP_INCLUDED #define JEONGPEAKWIDTH_HPP_INCLUDED @@ -28,62 +28,54 @@ namespace diffpy { namespace srreal { - -class DLL_EXPORT JeongPeakWidth : public DebyeWallerPeakWidth -{ - public: - - // constructors - JeongPeakWidth(); - virtual PeakWidthModelPtr create() const; - virtual PeakWidthModelPtr clone() const; - - // methods - virtual const std::string& type() const; - virtual double calculate(const BaseBondGenerator&) const; - virtual double maxWidth(StructureAdapterPtr, - double rmin, double rmax) const; - - // data access - const double& getDelta1() const; - void setDelta1(double); - const double& getDelta2() const; - void setDelta2(double); - const double& getQbroad() const; - void setQbroad(double); - const double& getQbroad_seperable() const; - void setQbroad_seperable(double); - - private: - - // data - double mdelta1; - double mdelta2; - double mqbroad; - double mqbroad_seperable; - - // methods - double msdSharpeningRatio(const double& r) const; - - // serialization - friend class boost::serialization::access; - - template - void serialize(Archive& ar, const unsigned int version) - { - using boost::serialization::base_object; - ar & base_object(*this); - ar & mdelta1; - ar & mdelta2; - ar & mqbroad; - ar & mqbroad_seperable; - } - +class DLL_EXPORT JeongPeakWidth : public DebyeWallerPeakWidth { + public: + // constructors + JeongPeakWidth(); + virtual PeakWidthModelPtr create() const; + virtual PeakWidthModelPtr clone() const; + + // methods + virtual const std::string& type() const; + virtual double calculate(const BaseBondGenerator&) const; + virtual double maxWidth(StructureAdapterPtr, double rmin, double rmax) const; + + // data access + const double& getDelta1() const; + void setDelta1(double); + const double& getDelta2() const; + void setDelta2(double); + const double& getQbroad() const; + void setQbroad(double); + const double& getQbroad_seperable() const; + void setQbroad_seperable(double); + + private: + // data + double mdelta1; + double mdelta2; + double mqbroad; + double mqbroad_seperable; + + // methods + double msdSharpeningRatio(const double& r) const; + + // serialization + friend class boost::serialization::access; + + template + void serialize(Archive& ar, const unsigned int version) { + using boost::serialization::base_object; + ar& base_object(*this); + ar & mdelta1; + ar & mdelta2; + ar & mqbroad; + ar & mqbroad_seperable; + } }; - -} // namespace srreal -} // namespace diffpy +} // namespace srreal +} // namespace diffpy // Serialization ------------------------------------------------------------- diff --git a/src/diffpy/srreal/Lattice.cpp b/src/diffpy/srreal/Lattice.cpp index 3b62e841..9ca8469d 100644 --- a/src/diffpy/srreal/Lattice.cpp +++ b/src/diffpy/srreal/Lattice.cpp @@ -1,21 +1,21 @@ /***************************************************************************** -* -* 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.txt for license information. -* -****************************************************************************** -* -* class Lattice -- vector and matrix conversions between general and -* Cartesian coordinate systems -* -*****************************************************************************/ + * + * 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.txt for license information. + * + ****************************************************************************** + * + * class Lattice -- vector and matrix conversions between general and + * Cartesian coordinate systems + * + *****************************************************************************/ #include #include @@ -31,282 +31,247 @@ using namespace diffpy::srreal; // Constructors -------------------------------------------------------------- -Lattice::Lattice() -{ - mbaserot = R3::identity(); - this->setLatPar(1.0, 1.0, 1.0, 90.0, 90.0, 90.0); +Lattice::Lattice() { + mbaserot = R3::identity(); + this->setLatPar(1.0, 1.0, 1.0, 90.0, 90.0, 90.0); } - -Lattice::Lattice(double a0, double b0, double c0, - double alpha0, double beta0, double gamma0) -{ - mbaserot = R3::identity(); - this->setLatPar(a0, b0, c0, alpha0, beta0, gamma0); +Lattice::Lattice(double a0, double b0, double c0, double alpha0, double beta0, + double gamma0) { + mbaserot = R3::identity(); + this->setLatPar(a0, b0, c0, alpha0, beta0, gamma0); } // Public Methods ------------------------------------------------------------ -void Lattice::setLatPar(double a0, double b0, double c0, - double alpha0, double beta0, double gamma0) -{ - using diffpy::mathutils::cosd; - using diffpy::mathutils::sind; - using diffpy::mathutils::acosd; - ma = a0; - mb = b0; - mc = c0; - malpha = alpha0; - mbeta = beta0; - mgamma = gamma0; - mcosa = cosd(malpha); - msina = sind(malpha); - mcosb = cosd(mbeta); - msinb = sind(mbeta); - mcosg = cosd(mgamma); - msing = sind(mgamma); - // Vnorm is a volume of unit cell with a=b=c=1 - double Vnorm = this->volumeNormal(); - // reciprocal lattice - mar = msina / (ma * Vnorm); - mbr = msinb / (mb * Vnorm); - mcr = msing / (mc * Vnorm); - mcosar = (mcosb*mcosg - mcosa) / (msinb * msing); - mcosbr = (mcosa*mcosg - mcosb) / (msina * msing); - mcosgr = (mcosa*mcosb - mcosg) / (msina * msinb); - msinar = sqrt(1.0 - mcosar*mcosar); - msinbr = sqrt(1.0 - mcosbr*mcosbr); - msingr = sqrt(1.0 - mcosgr*mcosgr); - malphar = acosd(mcosar); - mbetar = acosd(mcosbr); - mgammar = acosd(mcosgr); - // metric tensor - this->updateMetrics(); - // standard cartesian coordinates of lattice vectors - this->updateStandardBase(); - // calculate unit cell rotation matrix baserot, base = stdbase * baserot - mbase = R3::prod(mstdbase, mbaserot); - mva = R3::row(mbase, 0); - mvb = R3::row(mbase, 1); - mvc = R3::row(mbase, 2); - mrecbase = R3::inverse(mbase); - mvar = R3::column(mrecbase, 0); - mvbr = R3::column(mrecbase, 1); - mvcr = R3::column(mrecbase, 2); - R3::row(mnormbase, 0) = R3::row(mbase, 0) * mar; - R3::row(mnormbase, 1) = R3::row(mbase, 1) * mbr; - R3::row(mnormbase, 2) = R3::row(mbase, 2) * mcr; - R3::column(mrecnormbase, 0) = R3::column(mrecbase, 0) / mar; - R3::column(mrecnormbase, 1) = R3::column(mrecbase, 1) / mbr; - R3::column(mrecnormbase, 2) = R3::column(mrecbase, 2) / mcr; +void Lattice::setLatPar(double a0, double b0, double c0, double alpha0, + double beta0, double gamma0) { + using diffpy::mathutils::acosd; + using diffpy::mathutils::cosd; + using diffpy::mathutils::sind; + ma = a0; + mb = b0; + mc = c0; + malpha = alpha0; + mbeta = beta0; + mgamma = gamma0; + mcosa = cosd(malpha); + msina = sind(malpha); + mcosb = cosd(mbeta); + msinb = sind(mbeta); + mcosg = cosd(mgamma); + msing = sind(mgamma); + // Vnorm is a volume of unit cell with a=b=c=1 + double Vnorm = this->volumeNormal(); + // reciprocal lattice + mar = msina / (ma * Vnorm); + mbr = msinb / (mb * Vnorm); + mcr = msing / (mc * Vnorm); + mcosar = (mcosb * mcosg - mcosa) / (msinb * msing); + mcosbr = (mcosa * mcosg - mcosb) / (msina * msing); + mcosgr = (mcosa * mcosb - mcosg) / (msina * msinb); + msinar = sqrt(1.0 - mcosar * mcosar); + msinbr = sqrt(1.0 - mcosbr * mcosbr); + msingr = sqrt(1.0 - mcosgr * mcosgr); + malphar = acosd(mcosar); + mbetar = acosd(mcosbr); + mgammar = acosd(mcosgr); + // metric tensor + this->updateMetrics(); + // standard cartesian coordinates of lattice vectors + this->updateStandardBase(); + // calculate unit cell rotation matrix baserot, base = stdbase * baserot + mbase = R3::prod(mstdbase, mbaserot); + mva = R3::row(mbase, 0); + mvb = R3::row(mbase, 1); + mvc = R3::row(mbase, 2); + mrecbase = R3::inverse(mbase); + mvar = R3::column(mrecbase, 0); + mvbr = R3::column(mrecbase, 1); + mvcr = R3::column(mrecbase, 2); + R3::row(mnormbase, 0) = R3::row(mbase, 0) * mar; + R3::row(mnormbase, 1) = R3::row(mbase, 1) * mbr; + R3::row(mnormbase, 2) = R3::row(mbase, 2) * mcr; + R3::column(mrecnormbase, 0) = R3::column(mrecbase, 0) / mar; + R3::column(mrecnormbase, 1) = R3::column(mrecbase, 1) / mbr; + R3::column(mrecnormbase, 2) = R3::column(mrecbase, 2) / mcr; } -void Lattice::setLatBase(const R3::Vector& va0, - const R3::Vector& vb0, - const R3::Vector& vc0) -{ - using diffpy::mathutils::acosd; - R3::row(mbase, 0) = va0; - R3::row(mbase, 1) = vb0; - R3::row(mbase, 2) = vc0; - double detbase = R3::determinant(mbase); - if (fabs(detbase) < 1.0e-8) - { - throw invalid_argument("base vectors are degenerate"); - } - else if (detbase < 0.0) - { - throw invalid_argument("base is not right-handed"); - } - mva = va0; - mvb = vb0; - mvc = vc0; - ma = R3::norm(mva); - mb = R3::norm(mvb); - mc = R3::norm(mvc); - mcosa = R3::dot(mvb, mvc) / (mb*mc); - mcosb = R3::dot(mva, mvc) / (ma*mc); - mcosg = R3::dot(mva, mvb) / (ma*mb); - msina = sqrt(1.0 - mcosa*mcosa); - msinb = sqrt(1.0 - mcosb*mcosb); - msing = sqrt(1.0 - mcosg*mcosg); - malpha = acosd(mcosa); - mbeta = acosd(mcosb); - mgamma = acosd(mcosg); - // Vnorm is a volume of unit cell with a=b=c=1 - double Vnorm = this->volumeNormal(); - // reciprocal lattice - mar = msina/(ma*Vnorm); - mbr = msinb/(mb*Vnorm); - mcr = msing/(mc*Vnorm); - mcosar = (mcosb*mcosg - mcosa)/(msinb*msing); - mcosbr = (mcosa*mcosg - mcosb)/(msina*msing); - mcosgr = (mcosa*mcosb - mcosg)/(msina*msinb); - msinar = sqrt(1.0 - mcosar*mcosar); - msinbr = sqrt(1.0 - mcosbr*mcosbr); - msingr = sqrt(1.0 - mcosgr*mcosgr); - malphar = acosd(mcosar); - mbetar = acosd(mcosbr); - mgammar = acosd(mcosgr); - // standard orientation of lattice vectors - this->updateStandardBase(); - // calculate unit cell rotation matrix, base = stdbase*baserot - mbaserot = R3::prod(R3::inverse(mstdbase), mbase); - mrecbase = R3::inverse(mbase); - mvar = R3::column(mrecbase, 0); - mvbr = R3::column(mrecbase, 1); - mvcr = R3::column(mrecbase, 2); - // bases normalized to unit reciprocal vectors - R3::row(mnormbase, 0) = R3::row(mbase, 0) * mar; - R3::row(mnormbase, 1) = R3::row(mbase, 1) * mbr; - R3::row(mnormbase, 2) = R3::row(mbase, 2) * mcr; - R3::column(mrecnormbase, 0) = R3::column(mrecbase, 0) / mar; - R3::column(mrecnormbase, 1) = R3::column(mrecbase, 1) / mbr; - R3::column(mrecnormbase, 2) = R3::column(mrecbase, 2) / mcr; - this->updateMetrics(); +void Lattice::setLatBase(const R3::Vector& va0, const R3::Vector& vb0, + const R3::Vector& vc0) { + using diffpy::mathutils::acosd; + R3::row(mbase, 0) = va0; + R3::row(mbase, 1) = vb0; + R3::row(mbase, 2) = vc0; + double detbase = R3::determinant(mbase); + if (fabs(detbase) < 1.0e-8) { + throw invalid_argument("base vectors are degenerate"); + } else if (detbase < 0.0) { + throw invalid_argument("base is not right-handed"); + } + mva = va0; + mvb = vb0; + mvc = vc0; + ma = R3::norm(mva); + mb = R3::norm(mvb); + mc = R3::norm(mvc); + mcosa = R3::dot(mvb, mvc) / (mb * mc); + mcosb = R3::dot(mva, mvc) / (ma * mc); + mcosg = R3::dot(mva, mvb) / (ma * mb); + msina = sqrt(1.0 - mcosa * mcosa); + msinb = sqrt(1.0 - mcosb * mcosb); + msing = sqrt(1.0 - mcosg * mcosg); + malpha = acosd(mcosa); + mbeta = acosd(mcosb); + mgamma = acosd(mcosg); + // Vnorm is a volume of unit cell with a=b=c=1 + double Vnorm = this->volumeNormal(); + // reciprocal lattice + mar = msina / (ma * Vnorm); + mbr = msinb / (mb * Vnorm); + mcr = msing / (mc * Vnorm); + mcosar = (mcosb * mcosg - mcosa) / (msinb * msing); + mcosbr = (mcosa * mcosg - mcosb) / (msina * msing); + mcosgr = (mcosa * mcosb - mcosg) / (msina * msinb); + msinar = sqrt(1.0 - mcosar * mcosar); + msinbr = sqrt(1.0 - mcosbr * mcosbr); + msingr = sqrt(1.0 - mcosgr * mcosgr); + malphar = acosd(mcosar); + mbetar = acosd(mcosbr); + mgammar = acosd(mcosgr); + // standard orientation of lattice vectors + this->updateStandardBase(); + // calculate unit cell rotation matrix, base = stdbase*baserot + mbaserot = R3::prod(R3::inverse(mstdbase), mbase); + mrecbase = R3::inverse(mbase); + mvar = R3::column(mrecbase, 0); + mvbr = R3::column(mrecbase, 1); + mvcr = R3::column(mrecbase, 2); + // bases normalized to unit reciprocal vectors + R3::row(mnormbase, 0) = R3::row(mbase, 0) * mar; + R3::row(mnormbase, 1) = R3::row(mbase, 1) * mbr; + R3::row(mnormbase, 2) = R3::row(mbase, 2) * mcr; + R3::column(mrecnormbase, 0) = R3::column(mrecbase, 0) / mar; + R3::column(mrecnormbase, 1) = R3::column(mrecbase, 1) / mbr; + R3::column(mrecnormbase, 2) = R3::column(mrecbase, 2) / mcr; + this->updateMetrics(); } - // volumeNormal is a volume of unit cell with a=b=c=1 -double Lattice::volumeNormal() const -{ - double rv = sqrt(1.0 + - 2.0 * mcosa * mcosb * mcosg - - mcosa * mcosa - mcosb * mcosb - mcosg * mcosg); - return rv; +double Lattice::volumeNormal() const { + double rv = sqrt(1.0 + 2.0 * mcosa * mcosb * mcosg - mcosa * mcosa - + mcosb * mcosb - mcosg * mcosg); + return rv; } - -double Lattice::volume() const -{ - double rv = this->volumeNormal() * this->a() * this->b() * this->c(); - return rv; +double Lattice::volume() const { + double rv = this->volumeNormal() * this->a() * this->b() * this->c(); + return rv; } - -const R3::Vector& Lattice::cartesian(const R3::Vector& lv) const -{ - static R3::Vector res; - res = R3::mxvecproduct(lv, mbase); - return res; +const R3::Vector& Lattice::cartesian(const R3::Vector& lv) const { + static R3::Vector res; + res = R3::mxvecproduct(lv, mbase); + return res; } -const R3::Vector& Lattice::fractional(const R3::Vector& cv) const -{ - static R3::Vector res; - res = R3::mxvecproduct(cv, mrecbase); - return res; +const R3::Vector& Lattice::fractional(const R3::Vector& cv) const { + static R3::Vector res; + res = R3::mxvecproduct(cv, mrecbase); + return res; } -const R3::Vector& Lattice::ucvCartesian(const R3::Vector& cv) const -{ - static R3::Vector res; - res = cartesian(ucvFractional(fractional(cv))); - return res; +const R3::Vector& Lattice::ucvCartesian(const R3::Vector& cv) const { + static R3::Vector res; + res = cartesian(ucvFractional(fractional(cv))); + return res; } -const R3::Vector& Lattice::ucvFractional(const R3::Vector& lv) const -{ - using mathutils::eps_eq; - static R3::Vector res; - res = lv - floor(lv); - if (eps_eq(res[0], 1.0)) res[0] = 0.0; - if (eps_eq(res[1], 1.0)) res[1] = 0.0; - if (eps_eq(res[2], 1.0)) res[2] = 0.0; - return res; +const R3::Vector& Lattice::ucvFractional(const R3::Vector& lv) const { + using mathutils::eps_eq; + static R3::Vector res; + res = lv - floor(lv); + if (eps_eq(res[0], 1.0)) res[0] = 0.0; + if (eps_eq(res[1], 1.0)) res[1] = 0.0; + if (eps_eq(res[2], 1.0)) res[2] = 0.0; + return res; } -const R3::Matrix& Lattice::cartesianMatrix(const R3::Matrix& Ml) const -{ - static R3::Matrix res0, res1; - res0 = prod(Ml, mnormbase); - res1 = prod(R3::trans(mnormbase), res0); - return res1; +const R3::Matrix& Lattice::cartesianMatrix(const R3::Matrix& Ml) const { + static R3::Matrix res0, res1; + res0 = prod(Ml, mnormbase); + res1 = prod(R3::trans(mnormbase), res0); + return res1; } -const R3::Matrix& Lattice::fractionalMatrix(const R3::Matrix& Mc) const -{ - static R3::Matrix res0, res1; - res0 = prod(Mc, mrecnormbase); - res1 = prod(R3::trans(mrecnormbase), res0); - return res1; +const R3::Matrix& Lattice::fractionalMatrix(const R3::Matrix& Mc) const { + static R3::Matrix res0, res1; + res0 = prod(Mc, mrecnormbase); + res1 = prod(R3::trans(mrecnormbase), res0); + return res1; } - -const R3::Vector& Lattice::ucMaxDiagonal() const -{ - static list ucdiagonals; - if (ucdiagonals.empty()) - { - ucdiagonals.push_back(R3::Vector(+1, +1, +1)); - ucdiagonals.push_back(R3::Vector(-1, +1, +1)); - ucdiagonals.push_back(R3::Vector(+1, -1, +1)); - ucdiagonals.push_back(R3::Vector(+1, +1, -1)); - } - double maxnorm = -1; - list::iterator ucd; - list::iterator maxucd = ucdiagonals.end(); - for (ucd = ucdiagonals.begin(); ucd != ucdiagonals.end(); ++ucd) - { - double normucd = this->norm(*ucd); - if (normucd > maxnorm) - { - maxnorm = normucd; - maxucd = ucd; - } +const R3::Vector& Lattice::ucMaxDiagonal() const { + static list ucdiagonals; + if (ucdiagonals.empty()) { + ucdiagonals.push_back(R3::Vector(+1, +1, +1)); + ucdiagonals.push_back(R3::Vector(-1, +1, +1)); + ucdiagonals.push_back(R3::Vector(+1, -1, +1)); + ucdiagonals.push_back(R3::Vector(+1, +1, -1)); + } + double maxnorm = -1; + list::iterator ucd; + list::iterator maxucd = ucdiagonals.end(); + for (ucd = ucdiagonals.begin(); ucd != ucdiagonals.end(); ++ucd) { + double normucd = this->norm(*ucd); + if (normucd > maxnorm) { + maxnorm = normucd; + maxucd = ucd; } - assert(maxucd != ucdiagonals.end()); - return *maxucd; + } + assert(maxucd != ucdiagonals.end()); + return *maxucd; } - -double Lattice::ucMaxDiagonalLength() const -{ - const R3::Vector& ucmd = this->ucMaxDiagonal(); - double res = this->norm(ucmd); - return res; +double Lattice::ucMaxDiagonalLength() const { + const R3::Vector& ucmd = this->ucMaxDiagonal(); + double res = this->norm(ucmd); + return res; } - -bool Lattice::operator==(const Lattice& other) const -{ - bool rv = (this->base() == other.base()); - return rv; +bool Lattice::operator==(const Lattice& other) const { + bool rv = (this->base() == other.base()); + return rv; } - -bool Lattice::operator!=(const Lattice& other) const -{ - return !(*this == other); +bool Lattice::operator!=(const Lattice& other) const { + return !(*this == other); } // Private Methods ----------------------------------------------------------- -void Lattice::updateMetrics() -{ - mmetrics(0,0) = ma * ma; - mmetrics(1,1) = mb * mb; - mmetrics(2,2) = mc * mc; - mmetrics(0,1) = mmetrics(1,0) = ma * mb * mcosg; - mmetrics(0,2) = mmetrics(2,0) = ma * mc * mcosb; - mmetrics(1,2) = mmetrics(2,1) = mb * mc * mcosa; +void Lattice::updateMetrics() { + mmetrics(0, 0) = ma * ma; + mmetrics(1, 1) = mb * mb; + mmetrics(2, 2) = mc * mc; + mmetrics(0, 1) = mmetrics(1, 0) = ma * mb * mcosg; + mmetrics(0, 2) = mmetrics(2, 0) = ma * mc * mcosb; + mmetrics(1, 2) = mmetrics(2, 1) = mb * mc * mcosa; } -void Lattice::updateStandardBase() -{ - // a - mstdbase(0,0) = 1.0 / mar; - mstdbase(1,0) = 0.0; - mstdbase(2,0) = 0.0; - // b - mstdbase(0,1) = -mcosgr / msingr / mar; - mstdbase(1,1) = mb * msina; - mstdbase(2,1) = 0.0; - // c - mstdbase(0,2) = mcosb * ma; - mstdbase(1,2) = mb * mcosa; - mstdbase(2,2) = mc; +void Lattice::updateStandardBase() { + // a + mstdbase(0, 0) = 1.0 / mar; + mstdbase(1, 0) = 0.0; + mstdbase(2, 0) = 0.0; + // b + mstdbase(0, 1) = -mcosgr / msingr / mar; + mstdbase(1, 1) = mb * msina; + mstdbase(2, 1) = 0.0; + // c + mstdbase(0, 2) = mcosb * ma; + mstdbase(1, 2) = mb * mcosa; + mstdbase(2, 2) = mc; } - // End of file diff --git a/src/diffpy/srreal/Lattice.hpp b/src/diffpy/srreal/Lattice.hpp index b7304662..83483029 100644 --- a/src/diffpy/srreal/Lattice.hpp +++ b/src/diffpy/srreal/Lattice.hpp @@ -1,21 +1,21 @@ /***************************************************************************** -* -* 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.txt for license information. -* -****************************************************************************** -* -* class Lattice -- vector and matrix conversions between general and -* Cartesian coordinate systems -* -*****************************************************************************/ + * + * 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.txt for license information. + * + ****************************************************************************** + * + * class Lattice -- vector and matrix conversions between general and + * Cartesian coordinate systems + * + *****************************************************************************/ #ifndef LATTICE_HPP_INCLUDED #define LATTICE_HPP_INCLUDED @@ -27,171 +27,162 @@ namespace diffpy { namespace srreal { -class Lattice -{ - public: - - // constructors - - Lattice(); - Lattice(double a, double b, double c, - double alpha, double beta, double gamma); - // create from base vectors - template - Lattice(const V& va, const V& vb, const V& vc); - - // methods - - // set lattice parameters - void setLatPar( - double a, double b, double c, - double alpha, double beta, double gamma ); - // set lattice base vectors - void setLatBase( - const R3::Vector& va, - const R3::Vector& vb, - const R3::Vector& vc); - template - void setLatBase(const V& va, const V& vb, const V& vc); - // direct lattice - double a() const; - double b() const; - double c() const; - double alpha() const; - double beta() const; - double gamma() const; - double cosalpha() const; - double cosbeta() const; - double cosgamma() const; - double sinalpha() const; - double sinbeta() const; - double singamma() const; - const R3::Vector& va() const; - const R3::Vector& vb() const; - const R3::Vector& vc() const; - double volumeNormal() const; - double volume() const; - // reciprocal lattice - double ar() const; - double br() const; - double cr() const; - double alphar() const; - double betar() const; - double gammar() const; - double cosalphar() const; - double cosbetar() const; - double cosgammar() const; - double sinalphar() const; - double sinbetar() const; - double singammar() const; - const R3::Vector& var() const; - const R3::Vector& vbr() const; - const R3::Vector& vcr() const; - // lattice related tensors - // metrics tensor - const R3::Matrix& metrics() const; - // matrix of base vectors, base() = stdbase() * baserot() - const R3::Matrix& base() const; - // standard base vectors - const R3::Matrix& stdbase() const; - // base rotation matrix - const R3::Matrix& baserot() const; - // inverse of base matrix - const R3::Matrix& recbase() const; - // vector operations using lattice coordinates - template - double dot(const V& u, const V& v) const; - template - double norm(const V& u) const; - template - double distance(const V& u, const V& v) const; - // angle in degrees - template - double angledeg(const V& u, const V& v) const; - // angle in radians - template - double anglerad(const V& u, const V& v) const; - // conversion of coordinates and tensors - const R3::Vector& cartesian(const R3::Vector& lv) const; - template - const R3::Vector& cartesian(const V& lv) const; - const R3::Vector& fractional(const R3::Vector& cv) const; - template - const R3::Vector& fractional(const V& cv) const; - const R3::Vector& ucvCartesian(const R3::Vector& cv) const; - template - const R3::Vector& ucvCartesian(const V& cv) const; - const R3::Vector& ucvFractional(const R3::Vector& lv) const; - template - const R3::Vector& ucvFractional(const V& lv) const; - const R3::Matrix& cartesianMatrix(const R3::Matrix& Ml) const; - const R3::Matrix& fractionalMatrix(const R3::Matrix& Mc) const; - // largest cell diagonal in fractional coordinates - const R3::Vector& ucMaxDiagonal() const; - double ucMaxDiagonalLength() const; - - // comparison - bool operator==(const Lattice&) const; - bool operator!=(const Lattice&) const; - - private: - - // methods - void updateMetrics(); - void updateStandardBase(); - - // data - direct lattice parameters - double ma, mb, mc; - double malpha, mbeta, mgamma; - double mcosa, mcosb, mcosg; - double msina, msinb, msing; - R3::Vector mva, mvb, mvc; - - // data - reciprocal lattice parameters - double mar, mbr, mcr; - double malphar, mbetar, mgammar; - double mcosar, mcosbr, mcosgr; - double msinar, msinbr, msingr; - R3::Vector mvar, mvbr, mvcr; - - // data - tensors - // mbase = mstdbase * mbaserot - R3::Matrix mmetrics; // metrics tensor - R3::Matrix mbase; // lattice base - R3::Matrix mstdbase; // standard unit cell base - R3::Matrix mbaserot; // base rotation matrix - R3::Matrix mrecbase; // inverse of base matrix - // base multiplied by magnitudes of reciprocal vectors - R3::Matrix mnormbase; - R3::Matrix mrecnormbase; // inverse of mnormbase - - // serialization - friend class boost::serialization::access; - template - void serialize(Archive& ar, const unsigned int version) - { - ar & ma & mb & mc; - ar & malpha & mbeta & mgamma; - ar & mcosa & mcosb & mcosg; - ar & msina & msinb & msing; - ar & mva & mvb & mvc; - ar & mar & mbr & mcr; - ar & malphar & mbetar & mgammar; - ar & mcosar & mcosbr & mcosgr; - ar & msinar & msinbr & msingr; - ar & mvar & mvbr & mvcr; - ar & mmetrics; - ar & mbase; - ar & mstdbase; - ar & mbaserot; - ar & mrecbase; - ar & mnormbase; - ar & mrecnormbase; - } - +class Lattice { + public: + // constructors + + Lattice(); + Lattice(double a, double b, double c, double alpha, double beta, + double gamma); + // create from base vectors + template + Lattice(const V& va, const V& vb, const V& vc); + + // methods + + // set lattice parameters + void setLatPar(double a, double b, double c, double alpha, double beta, + double gamma); + // set lattice base vectors + void setLatBase(const R3::Vector& va, const R3::Vector& vb, + const R3::Vector& vc); + template + void setLatBase(const V& va, const V& vb, const V& vc); + // direct lattice + double a() const; + double b() const; + double c() const; + double alpha() const; + double beta() const; + double gamma() const; + double cosalpha() const; + double cosbeta() const; + double cosgamma() const; + double sinalpha() const; + double sinbeta() const; + double singamma() const; + const R3::Vector& va() const; + const R3::Vector& vb() const; + const R3::Vector& vc() const; + double volumeNormal() const; + double volume() const; + // reciprocal lattice + double ar() const; + double br() const; + double cr() const; + double alphar() const; + double betar() const; + double gammar() const; + double cosalphar() const; + double cosbetar() const; + double cosgammar() const; + double sinalphar() const; + double sinbetar() const; + double singammar() const; + const R3::Vector& var() const; + const R3::Vector& vbr() const; + const R3::Vector& vcr() const; + // lattice related tensors + // metrics tensor + const R3::Matrix& metrics() const; + // matrix of base vectors, base() = stdbase() * baserot() + const R3::Matrix& base() const; + // standard base vectors + const R3::Matrix& stdbase() const; + // base rotation matrix + const R3::Matrix& baserot() const; + // inverse of base matrix + const R3::Matrix& recbase() const; + // vector operations using lattice coordinates + template + double dot(const V& u, const V& v) const; + template + double norm(const V& u) const; + template + double distance(const V& u, const V& v) const; + // angle in degrees + template + double angledeg(const V& u, const V& v) const; + // angle in radians + template + double anglerad(const V& u, const V& v) const; + // conversion of coordinates and tensors + const R3::Vector& cartesian(const R3::Vector& lv) const; + template + const R3::Vector& cartesian(const V& lv) const; + const R3::Vector& fractional(const R3::Vector& cv) const; + template + const R3::Vector& fractional(const V& cv) const; + const R3::Vector& ucvCartesian(const R3::Vector& cv) const; + template + const R3::Vector& ucvCartesian(const V& cv) const; + const R3::Vector& ucvFractional(const R3::Vector& lv) const; + template + const R3::Vector& ucvFractional(const V& lv) const; + const R3::Matrix& cartesianMatrix(const R3::Matrix& Ml) const; + const R3::Matrix& fractionalMatrix(const R3::Matrix& Mc) const; + // largest cell diagonal in fractional coordinates + const R3::Vector& ucMaxDiagonal() const; + double ucMaxDiagonalLength() const; + + // comparison + bool operator==(const Lattice&) const; + bool operator!=(const Lattice&) const; + + private: + // methods + void updateMetrics(); + void updateStandardBase(); + + // data - direct lattice parameters + double ma, mb, mc; + double malpha, mbeta, mgamma; + double mcosa, mcosb, mcosg; + double msina, msinb, msing; + R3::Vector mva, mvb, mvc; + + // data - reciprocal lattice parameters + double mar, mbr, mcr; + double malphar, mbetar, mgammar; + double mcosar, mcosbr, mcosgr; + double msinar, msinbr, msingr; + R3::Vector mvar, mvbr, mvcr; + + // data - tensors + // mbase = mstdbase * mbaserot + R3::Matrix mmetrics; // metrics tensor + R3::Matrix mbase; // lattice base + R3::Matrix mstdbase; // standard unit cell base + R3::Matrix mbaserot; // base rotation matrix + R3::Matrix mrecbase; // inverse of base matrix + // base multiplied by magnitudes of reciprocal vectors + R3::Matrix mnormbase; + R3::Matrix mrecnormbase; // inverse of mnormbase + + // serialization + friend class boost::serialization::access; + template + void serialize(Archive& ar, const unsigned int version) { + ar & ma & mb & mc; + ar & malpha & mbeta & mgamma; + ar & mcosa & mcosb & mcosg; + ar & msina & msinb & msing; + ar & mva & mvb & mvc; + ar & mar & mbr & mcr; + ar & malphar & mbetar & mgammar; + ar & mcosar & mcosbr & mcosgr; + ar & msinar & msinbr & msingr; + ar & mvar & mvbr & mvcr; + ar & mmetrics; + ar & mbase; + ar & mstdbase; + ar & mbaserot; + ar & mrecbase; + ar & mnormbase; + ar & mrecnormbase; + } }; - ////////////////////////////////////////////////////////////////////////////// // Definitions ////////////////////////////////////////////////////////////////////////////// @@ -199,366 +190,170 @@ class Lattice // Template Constructor ------------------------------------------------------ template -Lattice::Lattice(const V& va0, const V& vb0, const V& vc0) -{ - this->setLatBase(va0, vb0, vc0); +Lattice::Lattice(const V& va0, const V& vb0, const V& vc0) { + this->setLatBase(va0, vb0, vc0); } // Template and Inline Methods ----------------------------------------------- template -void Lattice::setLatBase(const V& va0, const V& vb0, const V& vc0) -{ - R3::Vector va1(va0[0], va0[1], va0[2]); - R3::Vector vb1(vb0[0], vb0[1], vb0[2]); - R3::Vector vc1(vc0[0], vc0[1], vc0[2]); - this->setLatBase(va1, vb1, vc1); +void Lattice::setLatBase(const V& va0, const V& vb0, const V& vc0) { + R3::Vector va1(va0[0], va0[1], va0[2]); + R3::Vector vb1(vb0[0], vb0[1], vb0[2]); + R3::Vector vc1(vc0[0], vc0[1], vc0[2]); + this->setLatBase(va1, vb1, vc1); } // direct lattice -inline -double Lattice::a() const -{ - return ma; -} - - -inline -double Lattice::b() const -{ - return mb; -} - - -inline -double Lattice::c() const -{ - return mc; -} - - -inline -double Lattice::alpha() const -{ - return malpha; -} - - -inline -double Lattice::beta() const -{ - return mbeta; -} - - -inline -double Lattice::gamma() const -{ - return mgamma; -} - - -inline -double Lattice::cosalpha() const -{ - return mcosa; -} - +inline double Lattice::a() const { return ma; } -inline -double Lattice::cosbeta() const -{ - return mcosb; -} +inline double Lattice::b() const { return mb; } +inline double Lattice::c() const { return mc; } -inline -double Lattice::cosgamma() const -{ - return mcosg; -} - +inline double Lattice::alpha() const { return malpha; } -inline -double Lattice::sinalpha() const -{ - return msina; -} +inline double Lattice::beta() const { return mbeta; } +inline double Lattice::gamma() const { return mgamma; } -inline -double Lattice::sinbeta() const -{ - return msinb; -} +inline double Lattice::cosalpha() const { return mcosa; } +inline double Lattice::cosbeta() const { return mcosb; } -inline -double Lattice::singamma() const -{ - return msing; -} +inline double Lattice::cosgamma() const { return mcosg; } +inline double Lattice::sinalpha() const { return msina; } -inline -const R3::Vector& Lattice::va() const -{ - return mva; -} +inline double Lattice::sinbeta() const { return msinb; } +inline double Lattice::singamma() const { return msing; } -inline -const R3::Vector& Lattice::vb() const -{ - return mvb; -} +inline const R3::Vector& Lattice::va() const { return mva; } +inline const R3::Vector& Lattice::vb() const { return mvb; } -inline -const R3::Vector& Lattice::vc() const -{ - return mvc; -} +inline const R3::Vector& Lattice::vc() const { return mvc; } // reciprocal lattice -inline -double Lattice::ar() const -{ - return mar; -} +inline double Lattice::ar() const { return mar; } +inline double Lattice::br() const { return mbr; } -inline -double Lattice::br() const -{ - return mbr; -} +inline double Lattice::cr() const { return mcr; } +inline double Lattice::alphar() const { return malphar; } -inline -double Lattice::cr() const -{ - return mcr; -} - +inline double Lattice::betar() const { return mbetar; } -inline -double Lattice::alphar() const -{ - return malphar; -} +inline double Lattice::gammar() const { return mgammar; } +inline double Lattice::cosalphar() const { return mcosar; } -inline -double Lattice::betar() const -{ - return mbetar; -} +inline double Lattice::cosbetar() const { return mcosbr; } +inline double Lattice::cosgammar() const { return mcosgr; } -inline -double Lattice::gammar() const -{ - return mgammar; -} +inline double Lattice::sinalphar() const { return msinar; } +inline double Lattice::sinbetar() const { return msinbr; } -inline -double Lattice::cosalphar() const -{ - return mcosar; -} +inline double Lattice::singammar() const { return msingr; } +inline const R3::Vector& Lattice::var() const { return mvar; } -inline -double Lattice::cosbetar() const -{ - return mcosbr; -} +inline const R3::Vector& Lattice::vbr() const { return mvbr; } - -inline -double Lattice::cosgammar() const -{ - return mcosgr; -} - - -inline -double Lattice::sinalphar() const -{ - return msinar; -} - - -inline -double Lattice::sinbetar() const -{ - return msinbr; -} - - -inline -double Lattice::singammar() const -{ - return msingr; -} - - -inline -const R3::Vector& Lattice::var() const -{ - return mvar; -} - - -inline -const R3::Vector& Lattice::vbr() const -{ - return mvbr; -} - - -inline -const R3::Vector& Lattice::vcr() const -{ - return mvcr; -} +inline const R3::Vector& Lattice::vcr() const { return mvcr; } // lattice related tensors -inline -const R3::Matrix& Lattice::metrics() const -{ - return mmetrics; -} - - -inline -const R3::Matrix& Lattice::base() const -{ - return mbase; -} - - -inline -const R3::Matrix& Lattice::stdbase() const -{ - return mstdbase; -} +inline const R3::Matrix& Lattice::metrics() const { return mmetrics; } +inline const R3::Matrix& Lattice::base() const { return mbase; } -inline -const R3::Matrix& Lattice::baserot() const -{ - return mbaserot; -} +inline const R3::Matrix& Lattice::stdbase() const { return mstdbase; } +inline const R3::Matrix& Lattice::baserot() const { return mbaserot; } -inline -const R3::Matrix& Lattice::recbase() const -{ - return mrecbase; -} - +inline const R3::Matrix& Lattice::recbase() const { return mrecbase; } template -double Lattice::dot(const V& u, const V& v) const -{ - const R3::Matrix& M = this->metrics(); - double dp = - u[0] * v[0] * M(0,0) + - u[1] * v[1] * M(1,1) + - u[2] * v[2] * M(2,2) + - (u[0] * v[1] + u[1] * v[0]) * M(0,1) + - (u[0] * v[2] + u[2] * v[0]) * M(0,2) + - (u[2] * v[1] + u[1] * v[2]) * M(1,2); - return dp; +double Lattice::dot(const V& u, const V& v) const { + const R3::Matrix& M = this->metrics(); + double dp = u[0] * v[0] * M(0, 0) + u[1] * v[1] * M(1, 1) + + u[2] * v[2] * M(2, 2) + (u[0] * v[1] + u[1] * v[0]) * M(0, 1) + + (u[0] * v[2] + u[2] * v[0]) * M(0, 2) + + (u[2] * v[1] + u[1] * v[2]) * M(1, 2); + return dp; } - template -double Lattice::norm(const V& u) const -{ - return sqrt(this->dot(u, u)); +double Lattice::norm(const V& u) const { + return sqrt(this->dot(u, u)); } - template -double Lattice::distance(const V& u, const V& v) const -{ - static R3::Vector duv; - duv[0] = u[0] - v[0]; - duv[1] = u[1] - v[1]; - duv[2] = u[2] - v[2]; - return this->norm(duv); +double Lattice::distance(const V& u, const V& v) const { + static R3::Vector duv; + duv[0] = u[0] - v[0]; + duv[1] = u[1] - v[1]; + duv[2] = u[2] - v[2]; + return this->norm(duv); } - template -double Lattice::angledeg(const V& u, const V& v) const -{ - using diffpy::mathutils::acosd; - double ca = dot(u, v)/(norm(u) * norm(v)); - return acosd(ca); +double Lattice::angledeg(const V& u, const V& v) const { + using diffpy::mathutils::acosd; + double ca = dot(u, v) / (norm(u) * norm(v)); + return acosd(ca); } - template -double Lattice::anglerad(const V& u, const V& v) const -{ - double ca = dot(u, v)/(norm(u) * norm(v)); - return acos(ca); +double Lattice::anglerad(const V& u, const V& v) const { + double ca = dot(u, v) / (norm(u) * norm(v)); + return acos(ca); } - template -const R3::Vector& Lattice::cartesian(const V& lv) const -{ - static R3::Vector lvcopy; - lvcopy[0] = lv[0]; - lvcopy[1] = lv[1]; - lvcopy[2] = lv[2]; - return this->cartesian(lvcopy); +const R3::Vector& Lattice::cartesian(const V& lv) const { + static R3::Vector lvcopy; + lvcopy[0] = lv[0]; + lvcopy[1] = lv[1]; + lvcopy[2] = lv[2]; + return this->cartesian(lvcopy); } - template -const R3::Vector& Lattice::fractional(const V& cv) const -{ - static R3::Vector cvcopy; - cvcopy[0] = cv[0]; - cvcopy[1] = cv[1]; - cvcopy[2] = cv[2]; - return this->fractional(cvcopy); +const R3::Vector& Lattice::fractional(const V& cv) const { + static R3::Vector cvcopy; + cvcopy[0] = cv[0]; + cvcopy[1] = cv[1]; + cvcopy[2] = cv[2]; + return this->fractional(cvcopy); } - template -const R3::Vector& Lattice::ucvCartesian(const V& cv) const -{ - static R3::Vector cvcopy; - cvcopy[0] = cv[0]; - cvcopy[1] = cv[1]; - cvcopy[2] = cv[2]; - return this->ucvCartesian(cvcopy); +const R3::Vector& Lattice::ucvCartesian(const V& cv) const { + static R3::Vector cvcopy; + cvcopy[0] = cv[0]; + cvcopy[1] = cv[1]; + cvcopy[2] = cv[2]; + return this->ucvCartesian(cvcopy); } - template -const R3::Vector& Lattice::ucvFractional(const V& cv) const -{ - static R3::Vector cvcopy; - cvcopy[0] = cv[0]; - cvcopy[1] = cv[1]; - cvcopy[2] = cv[2]; - return ucvFractional(cvcopy); +const R3::Vector& Lattice::ucvFractional(const V& cv) const { + static R3::Vector cvcopy; + cvcopy[0] = cv[0]; + cvcopy[1] = cv[1]; + cvcopy[2] = cv[2]; + return ucvFractional(cvcopy); } - -} // namespace srreal -} // namespace diffpy +} // namespace srreal +} // namespace diffpy #endif // LATTICE_HPP_INCLUDED diff --git a/src/diffpy/srreal/LinearBaseline.cpp b/src/diffpy/srreal/LinearBaseline.cpp index 42475228..95486a38 100644 --- a/src/diffpy/srreal/LinearBaseline.cpp +++ b/src/diffpy/srreal/LinearBaseline.cpp @@ -1,20 +1,20 @@ /***************************************************************************** -* -* 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.txt for license information. -* -****************************************************************************** -* -* class LinearBaseline -- linear PDF baseline -* -*****************************************************************************/ + * + * 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.txt for license information. + * + ****************************************************************************** + * + * class LinearBaseline -- linear PDF baseline + * + *****************************************************************************/ #include #include @@ -26,61 +26,43 @@ namespace srreal { // Constructors -------------------------------------------------------------- -LinearBaseline::LinearBaseline() -{ - this->setSlope(0.0); - this->registerDoubleAttribute("slope", this, - &LinearBaseline::getSlope, - &LinearBaseline::setSlope); +LinearBaseline::LinearBaseline() { + this->setSlope(0.0); + this->registerDoubleAttribute("slope", this, &LinearBaseline::getSlope, + &LinearBaseline::setSlope); } - -PDFBaselinePtr LinearBaseline::create() const -{ - PDFBaselinePtr rv(new LinearBaseline()); - return rv; +PDFBaselinePtr LinearBaseline::create() const { + PDFBaselinePtr rv(new LinearBaseline()); + return rv; } - -PDFBaselinePtr LinearBaseline::clone() const -{ - PDFBaselinePtr rv(new LinearBaseline(*this)); - return rv; +PDFBaselinePtr LinearBaseline::clone() const { + PDFBaselinePtr rv(new LinearBaseline(*this)); + return rv; } // Public Methods ------------------------------------------------------------ -const string& LinearBaseline::type() const -{ - static string rv = "linear"; - return rv; -} - - -double LinearBaseline::operator()(const double& r) const -{ - return (this->getSlope() * r); +const string& LinearBaseline::type() const { + static string rv = "linear"; + return rv; } - -void LinearBaseline::setSlope(double sc) -{ - mslope = sc; +double LinearBaseline::operator()(const double& r) const { + return (this->getSlope() * r); } +void LinearBaseline::setSlope(double sc) { mslope = sc; } -const double& LinearBaseline::getSlope() const -{ - return mslope; -} - +const double& LinearBaseline::getSlope() const { return mslope; } // Registration -------------------------------------------------------------- bool reg_LinearBaseline = LinearBaseline().registerThisType(); -} // namespace srreal -} // namespace diffpy +} // namespace srreal +} // namespace diffpy // Serialization ------------------------------------------------------------- diff --git a/src/diffpy/srreal/LinearBaseline.hpp b/src/diffpy/srreal/LinearBaseline.hpp index 4728cd5c..43f2b89d 100644 --- a/src/diffpy/srreal/LinearBaseline.hpp +++ b/src/diffpy/srreal/LinearBaseline.hpp @@ -1,20 +1,20 @@ /***************************************************************************** -* -* 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.txt for license information. -* -****************************************************************************** -* -* class LinearBaseline -- linear PDF baseline -* -*****************************************************************************/ + * + * 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.txt for license information. + * + ****************************************************************************** + * + * class LinearBaseline -- linear PDF baseline + * + *****************************************************************************/ #ifndef LINEARBASELINE_HPP_INCLUDED #define LINEARBASELINE_HPP_INCLUDED @@ -29,41 +29,37 @@ namespace srreal { /// @class LinearBaseline /// @brief linear PDF baseline -class DLL_EXPORT LinearBaseline : public PDFBaseline -{ - public: - - // constructors - LinearBaseline(); - PDFBaselinePtr create() const; - PDFBaselinePtr clone() const; - - // methods - const std::string& type() const; - double operator()(const double& r) const; - void setSlope(double sc); - const double& getSlope() const; - - private: - - // data - double mslope; - - // serialization - friend class boost::serialization::access; - - template - void serialize(Archive& ar, const unsigned int version) - { - using boost::serialization::base_object; - ar & base_object(*this); - ar & mslope; - } +class DLL_EXPORT LinearBaseline : public PDFBaseline { + public: + // constructors + LinearBaseline(); + PDFBaselinePtr create() const; + PDFBaselinePtr clone() const; + + // methods + const std::string& type() const; + double operator()(const double& r) const; + void setSlope(double sc); + const double& getSlope() const; + + private: + // data + double mslope; + + // serialization + friend class boost::serialization::access; + + template + void serialize(Archive& ar, const unsigned int version) { + using boost::serialization::base_object; + ar& base_object(*this); + ar & mslope; + } }; // class LinearBaseline -} // namespace srreal -} // namespace diffpy +} // namespace srreal +} // namespace diffpy // Serialization ------------------------------------------------------------- diff --git a/src/diffpy/srreal/NoMetaStructureAdapter.cpp b/src/diffpy/srreal/NoMetaStructureAdapter.cpp index 1675ed78..1b373d14 100644 --- a/src/diffpy/srreal/NoMetaStructureAdapter.cpp +++ b/src/diffpy/srreal/NoMetaStructureAdapter.cpp @@ -1,26 +1,26 @@ /***************************************************************************** -* -* 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.txt for license information. -* -****************************************************************************** -* -* class NoMetaStructureAdapter -- StructureAdapter proxy class that disables -* customPQConfig of another StructureAdapter instance. This may be used -* for preventing the adapter for diffpy.Structure class from applying -* scale, spdiameter and such metadata to the PDFCalculator. -* -* nometa -- factory function that returns a NoMetaStructureAdapter -* instance wrapped in StructureAdapterPtr -* -*****************************************************************************/ + * + * 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.txt for license information. + * + ****************************************************************************** + * + * class NoMetaStructureAdapter -- StructureAdapter proxy class that disables + * customPQConfig of another StructureAdapter instance. This may be used + * for preventing the adapter for diffpy.Structure class from applying + * scale, spdiameter and such metadata to the PDFCalculator. + * + * nometa -- factory function that returns a NoMetaStructureAdapter + * instance wrapped in StructureAdapterPtr + * + *****************************************************************************/ #include #include @@ -36,129 +36,97 @@ namespace srreal { // Constructor --------------------------------------------------------------- NoMetaStructureAdapter::NoMetaStructureAdapter( - StructureAdapterPtr srcstructure) -{ - std::shared_ptr nmptr = - std::dynamic_pointer_cast(srcstructure); - if (nmptr) srcstructure = nmptr->getSourceStructure(); - msrcstructure = srcstructure.get() ? - srcstructure : emptyStructureAdapter(); + StructureAdapterPtr srcstructure) { + std::shared_ptr nmptr = + std::dynamic_pointer_cast(srcstructure); + if (nmptr) srcstructure = nmptr->getSourceStructure(); + msrcstructure = srcstructure.get() ? srcstructure : emptyStructureAdapter(); } // Public Methods ------------------------------------------------------------ -StructureAdapterPtr NoMetaStructureAdapter::clone() const -{ - std::shared_ptr rv(new NoMetaStructureAdapter); - if (msrcstructure) rv->msrcstructure = msrcstructure->clone(); - return rv; +StructureAdapterPtr NoMetaStructureAdapter::clone() const { + std::shared_ptr rv(new NoMetaStructureAdapter); + if (msrcstructure) rv->msrcstructure = msrcstructure->clone(); + return rv; } - -BaseBondGeneratorPtr NoMetaStructureAdapter::createBondGenerator() const -{ - BaseBondGeneratorPtr bnds = msrcstructure->createBondGenerator(); - return bnds; +BaseBondGeneratorPtr NoMetaStructureAdapter::createBondGenerator() const { + BaseBondGeneratorPtr bnds = msrcstructure->createBondGenerator(); + return bnds; } - -int NoMetaStructureAdapter::countSites() const -{ - return msrcstructure->countSites(); +int NoMetaStructureAdapter::countSites() const { + return msrcstructure->countSites(); } - -double NoMetaStructureAdapter::numberDensity() const -{ - return msrcstructure->numberDensity(); +double NoMetaStructureAdapter::numberDensity() const { + return msrcstructure->numberDensity(); } - -const std::string& NoMetaStructureAdapter::siteAtomType(int idx) const -{ - return msrcstructure->siteAtomType(idx); +const std::string& NoMetaStructureAdapter::siteAtomType(int idx) const { + return msrcstructure->siteAtomType(idx); } - -const R3::Vector& NoMetaStructureAdapter::siteCartesianPosition( - int idx) const -{ - return msrcstructure->siteCartesianPosition(idx); +const R3::Vector& NoMetaStructureAdapter::siteCartesianPosition(int idx) const { + return msrcstructure->siteCartesianPosition(idx); } - -int NoMetaStructureAdapter::siteMultiplicity(int idx) const -{ - return msrcstructure->siteMultiplicity(idx); +int NoMetaStructureAdapter::siteMultiplicity(int idx) const { + return msrcstructure->siteMultiplicity(idx); } - -double NoMetaStructureAdapter::siteOccupancy(int idx) const -{ - return msrcstructure->siteOccupancy(idx); +double NoMetaStructureAdapter::siteOccupancy(int idx) const { + return msrcstructure->siteOccupancy(idx); } - -bool NoMetaStructureAdapter::siteAnisotropy(int idx) const -{ - return msrcstructure->siteAnisotropy(idx); +bool NoMetaStructureAdapter::siteAnisotropy(int idx) const { + return msrcstructure->siteAnisotropy(idx); } - -const R3::Matrix& NoMetaStructureAdapter::siteCartesianUij(int idx) const -{ - return msrcstructure->siteCartesianUij(idx); +const R3::Matrix& NoMetaStructureAdapter::siteCartesianUij(int idx) const { + return msrcstructure->siteCartesianUij(idx); } - -void NoMetaStructureAdapter::customPQConfig(PairQuantity* pq) const -{ - // disable customPQConfig of the wrapped PairQuantity +void NoMetaStructureAdapter::customPQConfig(PairQuantity* pq) const { + // disable customPQConfig of the wrapped PairQuantity } - -StructureDifference -NoMetaStructureAdapter::diff(StructureAdapterConstPtr other) const -{ - StructureDifference sd = this->StructureAdapter::diff(other); - if (sd.stru0 == sd.stru1) return sd; - typedef std::shared_ptr PPtr; - PPtr pother = std::dynamic_pointer_cast(other); - if (!pother) return sd; - sd = this->getSourceStructure()->diff(pother->getSourceStructure()); - assert(sd.stru0 == this->getSourceStructure()); - assert(sd.stru1 == pother->getSourceStructure()); - sd.stru0 = this->shared_from_this(); - sd.stru1 = other; - return sd; +StructureDifference NoMetaStructureAdapter::diff( + StructureAdapterConstPtr other) const { + StructureDifference sd = this->StructureAdapter::diff(other); + if (sd.stru0 == sd.stru1) return sd; + typedef std::shared_ptr PPtr; + PPtr pother = std::dynamic_pointer_cast(other); + if (!pother) return sd; + sd = this->getSourceStructure()->diff(pother->getSourceStructure()); + assert(sd.stru0 == this->getSourceStructure()); + assert(sd.stru1 == pother->getSourceStructure()); + sd.stru0 = this->shared_from_this(); + sd.stru1 = other; + return sd; } - -StructureAdapterPtr -NoMetaStructureAdapter::getSourceStructure() -{ - return msrcstructure; +StructureAdapterPtr NoMetaStructureAdapter::getSourceStructure() { + return msrcstructure; } - -StructureAdapterConstPtr -NoMetaStructureAdapter::getSourceStructure() const -{ - return msrcstructure; +StructureAdapterConstPtr NoMetaStructureAdapter::getSourceStructure() const { + return msrcstructure; } // Routines ------------------------------------------------------------------ -StructureAdapterPtr nometa(StructureAdapterPtr stru) -{ - StructureAdapterPtr rv = - std::dynamic_pointer_cast(stru) ? stru : - StructureAdapterPtr(new NoMetaStructureAdapter(stru)); - return rv; +StructureAdapterPtr nometa(StructureAdapterPtr stru) { + StructureAdapterPtr rv = + std::dynamic_pointer_cast(stru) + ? stru + : StructureAdapterPtr(new NoMetaStructureAdapter(stru)); + return rv; } -} // namespace srreal -} // namespace diffpy +} // namespace srreal +} // namespace diffpy // Serialization ------------------------------------------------------------- diff --git a/src/diffpy/srreal/NoMetaStructureAdapter.hpp b/src/diffpy/srreal/NoMetaStructureAdapter.hpp index b9937f4a..df2ce5d4 100644 --- a/src/diffpy/srreal/NoMetaStructureAdapter.hpp +++ b/src/diffpy/srreal/NoMetaStructureAdapter.hpp @@ -1,26 +1,26 @@ /***************************************************************************** -* -* 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.txt for license information. -* -****************************************************************************** -* -* class NoMetaStructureAdapter -- StructureAdapter proxy class that disables -* customPQConfig of another StructureAdapter instance. This may be used -* for preventing the adapter for diffpy.Structure class from applying -* scale, spdiameter and such metadata to the PDFCalculator. -* -* nometa -- factory function that returns a NoMetaStructureAdapter -* instance wrapped in StructureAdapterPtr -* -*****************************************************************************/ + * + * 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.txt for license information. + * + ****************************************************************************** + * + * class NoMetaStructureAdapter -- StructureAdapter proxy class that disables + * customPQConfig of another StructureAdapter instance. This may be used + * for preventing the adapter for diffpy.Structure class from applying + * scale, spdiameter and such metadata to the PDFCalculator. + * + * nometa -- factory function that returns a NoMetaStructureAdapter + * instance wrapped in StructureAdapterPtr + * + *****************************************************************************/ #ifndef NOMETASTRUCTUREADAPTER_HPP_INCLUDED #define NOMETASTRUCTUREADAPTER_HPP_INCLUDED @@ -32,46 +32,41 @@ namespace diffpy { namespace srreal { -class DLL_EXPORT NoMetaStructureAdapter : public StructureAdapter -{ - public: - - // constructors - NoMetaStructureAdapter() { } - NoMetaStructureAdapter(StructureAdapterPtr); - - // methods - overloaded - virtual StructureAdapterPtr clone() const; - virtual BaseBondGeneratorPtr createBondGenerator() const; - virtual int countSites() const; - virtual double numberDensity() const; - virtual const std::string& siteAtomType(int idx) const; - virtual const R3::Vector& siteCartesianPosition(int idx) const; - virtual int siteMultiplicity(int idx) const; - virtual double siteOccupancy(int idx) const; - virtual bool siteAnisotropy(int idx) const; - virtual const R3::Matrix& siteCartesianUij(int idx) const; - virtual void customPQConfig(PairQuantity* pq) const; - virtual StructureDifference diff(StructureAdapterConstPtr) const; - - // methods - own - StructureAdapterPtr getSourceStructure(); - StructureAdapterConstPtr getSourceStructure() const; - - private: - - // data - StructureAdapterPtr msrcstructure; - - // serialization - friend class boost::serialization::access; - template - void serialize(Archive& ar, const unsigned int version) - { - ar & boost::serialization::base_object(*this); - ar & msrcstructure; - } - +class DLL_EXPORT NoMetaStructureAdapter : public StructureAdapter { + public: + // constructors + NoMetaStructureAdapter() {} + NoMetaStructureAdapter(StructureAdapterPtr); + + // methods - overloaded + virtual StructureAdapterPtr clone() const; + virtual BaseBondGeneratorPtr createBondGenerator() const; + virtual int countSites() const; + virtual double numberDensity() const; + virtual const std::string& siteAtomType(int idx) const; + virtual const R3::Vector& siteCartesianPosition(int idx) const; + virtual int siteMultiplicity(int idx) const; + virtual double siteOccupancy(int idx) const; + virtual bool siteAnisotropy(int idx) const; + virtual const R3::Matrix& siteCartesianUij(int idx) const; + virtual void customPQConfig(PairQuantity* pq) const; + virtual StructureDifference diff(StructureAdapterConstPtr) const; + + // methods - own + StructureAdapterPtr getSourceStructure(); + StructureAdapterConstPtr getSourceStructure() const; + + private: + // data + StructureAdapterPtr msrcstructure; + + // serialization + friend class boost::serialization::access; + template + void serialize(Archive& ar, const unsigned int version) { + ar& boost::serialization::base_object(*this); + ar & msrcstructure; + } }; // Routines ------------------------------------------------------------------ @@ -79,17 +74,15 @@ class DLL_EXPORT NoMetaStructureAdapter : public StructureAdapter /// create NoMetaStructureAdapter from an existing StructureAdapter DLL_EXPORT StructureAdapterPtr nometa(StructureAdapterPtr stru); - /// create NoMetaStructureAdapter from an adaptable structure object template -StructureAdapterPtr nometa(const T& stru) -{ - StructureAdapterPtr bstru = convertToStructureAdapter(stru); - return nometa(bstru); +StructureAdapterPtr nometa(const T& stru) { + StructureAdapterPtr bstru = convertToStructureAdapter(stru); + return nometa(bstru); } -} // namespace srreal -} // namespace diffpy +} // namespace srreal +} // namespace diffpy // Serialization ------------------------------------------------------------- diff --git a/src/diffpy/srreal/NoSymmetryStructureAdapter.cpp b/src/diffpy/srreal/NoSymmetryStructureAdapter.cpp index ac7d7781..9add118d 100644 --- a/src/diffpy/srreal/NoSymmetryStructureAdapter.cpp +++ b/src/diffpy/srreal/NoSymmetryStructureAdapter.cpp @@ -1,28 +1,28 @@ /***************************************************************************** -* -* 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.txt for license information. -* -****************************************************************************** -* -* class NoSymmetryStructureAdapter -- StructureAdapter class that removes -* any symmetry expansions (rotations or periodic translations) from -* another StructureAdapter instance. This can be used to use only -* the asymmetric unit from any adapter to crystal structure. -* -* class NoSymmetryBondGenerator -- bond generator -* -* nosymmetry -- factory function that creates a NoSymmetryStructureAdapter -* instance inside StructureAdapterPtr -* -*****************************************************************************/ + * + * 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.txt for license information. + * + ****************************************************************************** + * + * class NoSymmetryStructureAdapter -- StructureAdapter class that removes + * any symmetry expansions (rotations or periodic translations) from + * another StructureAdapter instance. This can be used to use only + * the asymmetric unit from any adapter to crystal structure. + * + * class NoSymmetryBondGenerator -- bond generator + * + * nosymmetry -- factory function that creates a NoSymmetryStructureAdapter + * instance inside StructureAdapterPtr + * + *****************************************************************************/ #include #include @@ -38,124 +38,94 @@ namespace srreal { // Constructor --------------------------------------------------------------- NoSymmetryStructureAdapter::NoSymmetryStructureAdapter( - StructureAdapterPtr srcstructure) -{ - std::shared_ptr nmptr = - std::dynamic_pointer_cast(srcstructure); - if (nmptr) srcstructure = nmptr->getSourceStructure(); - msrcstructure = srcstructure.get() ? - srcstructure : emptyStructureAdapter(); + StructureAdapterPtr srcstructure) { + std::shared_ptr nmptr = + std::dynamic_pointer_cast(srcstructure); + if (nmptr) srcstructure = nmptr->getSourceStructure(); + msrcstructure = srcstructure.get() ? srcstructure : emptyStructureAdapter(); } // Public Methods ------------------------------------------------------------ -StructureAdapterPtr NoSymmetryStructureAdapter::clone() const -{ - std::shared_ptr - rv(new NoSymmetryStructureAdapter); - if (msrcstructure) rv->msrcstructure = msrcstructure->clone(); - return rv; +StructureAdapterPtr NoSymmetryStructureAdapter::clone() const { + std::shared_ptr rv( + new NoSymmetryStructureAdapter); + if (msrcstructure) rv->msrcstructure = msrcstructure->clone(); + return rv; } - -BaseBondGeneratorPtr NoSymmetryStructureAdapter::createBondGenerator() const -{ - BaseBondGeneratorPtr bnds(new BaseBondGenerator(shared_from_this())); - return bnds; +BaseBondGeneratorPtr NoSymmetryStructureAdapter::createBondGenerator() const { + BaseBondGeneratorPtr bnds(new BaseBondGenerator(shared_from_this())); + return bnds; } - -int NoSymmetryStructureAdapter::countSites() const -{ - return msrcstructure->countSites(); +int NoSymmetryStructureAdapter::countSites() const { + return msrcstructure->countSites(); } +double NoSymmetryStructureAdapter::numberDensity() const { return 0.0; } -double NoSymmetryStructureAdapter::numberDensity() const -{ - return 0.0; +const std::string& NoSymmetryStructureAdapter::siteAtomType(int idx) const { + return msrcstructure->siteAtomType(idx); } - -const std::string& NoSymmetryStructureAdapter::siteAtomType(int idx) const -{ - return msrcstructure->siteAtomType(idx); -} - - const R3::Vector& NoSymmetryStructureAdapter::siteCartesianPosition( - int idx) const -{ - return msrcstructure->siteCartesianPosition(idx); + int idx) const { + return msrcstructure->siteCartesianPosition(idx); } - -double NoSymmetryStructureAdapter::siteOccupancy(int idx) const -{ - return msrcstructure->siteOccupancy(idx); +double NoSymmetryStructureAdapter::siteOccupancy(int idx) const { + return msrcstructure->siteOccupancy(idx); } - -bool NoSymmetryStructureAdapter::siteAnisotropy(int idx) const -{ - return msrcstructure->siteAnisotropy(idx); +bool NoSymmetryStructureAdapter::siteAnisotropy(int idx) const { + return msrcstructure->siteAnisotropy(idx); } - -const R3::Matrix& NoSymmetryStructureAdapter::siteCartesianUij(int idx) const -{ - return msrcstructure->siteCartesianUij(idx); +const R3::Matrix& NoSymmetryStructureAdapter::siteCartesianUij(int idx) const { + return msrcstructure->siteCartesianUij(idx); } - -void NoSymmetryStructureAdapter::customPQConfig(PairQuantity* pq) const -{ - msrcstructure->customPQConfig(pq); +void NoSymmetryStructureAdapter::customPQConfig(PairQuantity* pq) const { + msrcstructure->customPQConfig(pq); } - -StructureDifference -NoSymmetryStructureAdapter::diff(StructureAdapterConstPtr other) const -{ - StructureDifference sd = this->StructureAdapter::diff(other); - if (sd.stru0 == sd.stru1) return sd; - typedef std::shared_ptr PPtr; - PPtr pother = std::dynamic_pointer_cast(other); - if (!pother) return sd; - sd = this->getSourceStructure()->diff(pother->getSourceStructure()); - assert(sd.stru0 == this->getSourceStructure()); - assert(sd.stru1 == pother->getSourceStructure()); - sd.stru0 = this->shared_from_this(); - sd.stru1 = other; - return sd; +StructureDifference NoSymmetryStructureAdapter::diff( + StructureAdapterConstPtr other) const { + StructureDifference sd = this->StructureAdapter::diff(other); + if (sd.stru0 == sd.stru1) return sd; + typedef std::shared_ptr PPtr; + PPtr pother = std::dynamic_pointer_cast(other); + if (!pother) return sd; + sd = this->getSourceStructure()->diff(pother->getSourceStructure()); + assert(sd.stru0 == this->getSourceStructure()); + assert(sd.stru1 == pother->getSourceStructure()); + sd.stru0 = this->shared_from_this(); + sd.stru1 = other; + return sd; } - -StructureAdapterPtr -NoSymmetryStructureAdapter::getSourceStructure() -{ - return msrcstructure; +StructureAdapterPtr NoSymmetryStructureAdapter::getSourceStructure() { + return msrcstructure; } - -StructureAdapterConstPtr -NoSymmetryStructureAdapter::getSourceStructure() const -{ - return msrcstructure; +StructureAdapterConstPtr NoSymmetryStructureAdapter::getSourceStructure() + const { + return msrcstructure; } // Routines ------------------------------------------------------------------ -StructureAdapterPtr nosymmetry(StructureAdapterPtr stru) -{ - StructureAdapterPtr rv = - std::dynamic_pointer_cast(stru) ? stru : - StructureAdapterPtr(new NoSymmetryStructureAdapter(stru)); - return rv; +StructureAdapterPtr nosymmetry(StructureAdapterPtr stru) { + StructureAdapterPtr rv = + std::dynamic_pointer_cast(stru) + ? stru + : StructureAdapterPtr(new NoSymmetryStructureAdapter(stru)); + return rv; } -} // namespace srreal -} // namespace diffpy +} // namespace srreal +} // namespace diffpy // Serialization ------------------------------------------------------------- diff --git a/src/diffpy/srreal/NoSymmetryStructureAdapter.hpp b/src/diffpy/srreal/NoSymmetryStructureAdapter.hpp index 02a4e17e..c4b3431b 100644 --- a/src/diffpy/srreal/NoSymmetryStructureAdapter.hpp +++ b/src/diffpy/srreal/NoSymmetryStructureAdapter.hpp @@ -1,28 +1,28 @@ /***************************************************************************** -* -* 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.txt for license information. -* -****************************************************************************** -* -* class NoSymmetryStructureAdapter -- StructureAdapter class that removes -* any symmetry expansions (rotations or periodic translations) from -* another StructureAdapter instance. This can be used to use only -* the asymmetric unit from any adapter to crystal structure. -* -* class NoSymmetryBondGenerator -- bond generator -* -* nosymmetry -- factory function that creates a NoSymmetryStructureAdapter -* instance inside StructureAdapterPtr -* -*****************************************************************************/ + * + * 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.txt for license information. + * + ****************************************************************************** + * + * class NoSymmetryStructureAdapter -- StructureAdapter class that removes + * any symmetry expansions (rotations or periodic translations) from + * another StructureAdapter instance. This can be used to use only + * the asymmetric unit from any adapter to crystal structure. + * + * class NoSymmetryBondGenerator -- bond generator + * + * nosymmetry -- factory function that creates a NoSymmetryStructureAdapter + * instance inside StructureAdapterPtr + * + *****************************************************************************/ #ifndef NOSYMMETRYSTRUCTUREADAPTER_HPP_INCLUDED #define NOSYMMETRYSTRUCTUREADAPTER_HPP_INCLUDED @@ -34,46 +34,41 @@ namespace diffpy { namespace srreal { -class DLL_EXPORT NoSymmetryStructureAdapter : public StructureAdapter -{ - public: - - // constructors - NoSymmetryStructureAdapter() { } - NoSymmetryStructureAdapter(StructureAdapterPtr); - - // methods - overloaded - virtual StructureAdapterPtr clone() const; - virtual BaseBondGeneratorPtr createBondGenerator() const; - virtual int countSites() const; - virtual double numberDensity() const; - virtual const std::string& siteAtomType(int idx) const; - virtual const R3::Vector& siteCartesianPosition(int idx) const; - // reusing base-class StructureAdapter::siteMultiplicity() - virtual double siteOccupancy(int idx) const; - virtual bool siteAnisotropy(int idx) const; - virtual const R3::Matrix& siteCartesianUij(int idx) const; - virtual void customPQConfig(PairQuantity* pq) const; - virtual StructureDifference diff(StructureAdapterConstPtr) const; - - // methods - own - StructureAdapterPtr getSourceStructure(); - StructureAdapterConstPtr getSourceStructure() const; - - private: - - // data - StructureAdapterPtr msrcstructure; - - // serialization - friend class boost::serialization::access; - template - void serialize(Archive& ar, const unsigned int version) - { - ar & boost::serialization::base_object(*this); - ar & msrcstructure; - } - +class DLL_EXPORT NoSymmetryStructureAdapter : public StructureAdapter { + public: + // constructors + NoSymmetryStructureAdapter() {} + NoSymmetryStructureAdapter(StructureAdapterPtr); + + // methods - overloaded + virtual StructureAdapterPtr clone() const; + virtual BaseBondGeneratorPtr createBondGenerator() const; + virtual int countSites() const; + virtual double numberDensity() const; + virtual const std::string& siteAtomType(int idx) const; + virtual const R3::Vector& siteCartesianPosition(int idx) const; + // reusing base-class StructureAdapter::siteMultiplicity() + virtual double siteOccupancy(int idx) const; + virtual bool siteAnisotropy(int idx) const; + virtual const R3::Matrix& siteCartesianUij(int idx) const; + virtual void customPQConfig(PairQuantity* pq) const; + virtual StructureDifference diff(StructureAdapterConstPtr) const; + + // methods - own + StructureAdapterPtr getSourceStructure(); + StructureAdapterConstPtr getSourceStructure() const; + + private: + // data + StructureAdapterPtr msrcstructure; + + // serialization + friend class boost::serialization::access; + template + void serialize(Archive& ar, const unsigned int version) { + ar& boost::serialization::base_object(*this); + ar & msrcstructure; + } }; // Routines ------------------------------------------------------------------ @@ -81,17 +76,15 @@ class DLL_EXPORT NoSymmetryStructureAdapter : public StructureAdapter /// create NoSymmetryStructureAdapter from an existing StructureAdapter DLL_EXPORT StructureAdapterPtr nosymmetry(StructureAdapterPtr stru); - /// create NoSymmetryStructureAdapter from an adaptable structure object template -StructureAdapterPtr nosymmetry(const T& stru) -{ - StructureAdapterPtr bstru = convertToStructureAdapter(stru); - return nosymmetry(bstru); +StructureAdapterPtr nosymmetry(const T& stru) { + StructureAdapterPtr bstru = convertToStructureAdapter(stru); + return nosymmetry(bstru); } -} // namespace srreal -} // namespace diffpy +} // namespace srreal +} // namespace diffpy // Serialization ------------------------------------------------------------- diff --git a/src/diffpy/srreal/ObjCrystStructureAdapter.cpp b/src/diffpy/srreal/ObjCrystStructureAdapter.cpp index bee38875..21d0777a 100644 --- a/src/diffpy/srreal/ObjCrystStructureAdapter.cpp +++ b/src/diffpy/srreal/ObjCrystStructureAdapter.cpp @@ -1,22 +1,22 @@ /***************************************************************************** -* -* 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: Chris Farrow -* -* See AUTHORS.rst for a list of people who contributed. -* See LICENSE_DANSE.txt for license information. -* -****************************************************************************** -* -* StructureAdapterPtr createStructureAdapter(const ObjCryst::Crystal&) -* StructureAdapterPtr createStructureAdapter(const ObjCryst::Molecule&) -* -- structure adapter factories for ObjCryst objects. -* -*****************************************************************************/ + * + * 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: Chris Farrow + * + * See AUTHORS.rst for a list of people who contributed. + * See LICENSE_DANSE.txt for license information. + * + ****************************************************************************** + * + * StructureAdapterPtr createStructureAdapter(const ObjCryst::Crystal&) + * StructureAdapterPtr createStructureAdapter(const ObjCryst::Molecule&) + * -- structure adapter factories for ObjCryst objects. + * + *****************************************************************************/ #include #include @@ -39,168 +39,147 @@ namespace { // Utility functions --------------------------------------------------------- -R3::Matrix -getUij(const ObjCryst::ScatteringPower* sp) -{ - const double BtoU = 1.0 / (8 * M_PI * M_PI); - R3::Matrix Uij = R3::zeromatrix(); - if (sp->IsIsotropic()) - { - Uij(0,0) = Uij(1,1) = Uij(2,2) = sp->GetBiso() * BtoU; - } - else - { - Uij(0,0) = sp->GetBij(1,1) * BtoU; - Uij(1,1) = sp->GetBij(2,2) * BtoU; - Uij(2,2) = sp->GetBij(3,3) * BtoU; - Uij(0,1) = Uij(1,0) = sp->GetBij(1,2) * BtoU; - Uij(0,2) = Uij(2,0) = sp->GetBij(1,3) * BtoU; - Uij(1,2) = Uij(2,1) = sp->GetBij(2,3) * BtoU; - } - return Uij; +R3::Matrix getUij(const ObjCryst::ScatteringPower* sp) { + const double BtoU = 1.0 / (8 * M_PI * M_PI); + R3::Matrix Uij = R3::zeromatrix(); + if (sp->IsIsotropic()) { + Uij(0, 0) = Uij(1, 1) = Uij(2, 2) = sp->GetBiso() * BtoU; + } else { + Uij(0, 0) = sp->GetBij(1, 1) * BtoU; + Uij(1, 1) = sp->GetBij(2, 2) * BtoU; + Uij(2, 2) = sp->GetBij(3, 3) * BtoU; + Uij(0, 1) = Uij(1, 0) = sp->GetBij(1, 2) * BtoU; + Uij(0, 2) = Uij(2, 0) = sp->GetBij(1, 3) * BtoU; + Uij(1, 2) = Uij(2, 1) = sp->GetBij(2, 3) * BtoU; + } + return Uij; } - -CrystalStructureAdapter::SymOpVector -fetchSymmetryOperations(const ObjCryst::SpaceGroup& spacegroup) -{ - CrystalStructureAdapter::SymOpVector rv; - const int nbsym = spacegroup.GetNbSymmetrics(); - rv.resize(nbsym); - const vector& sgsymops = - spacegroup.GetSymmetryOperations(); - // copy convert the ObjCryst symmetry operations - int last = sgsymops.size(); - assert(last <= nbsym); - for (int i = 0; i != last; ++i) - { - CrystalStructureAdapter::SymOpVector::value_type& op = rv[i]; - copy(sgsymops[i].mx, sgsymops[i].mx + 9, op.R.data().begin()); - copy(sgsymops[i].tr, sgsymops[i].tr + 3, op.t.data().begin()); +CrystalStructureAdapter::SymOpVector fetchSymmetryOperations( + const ObjCryst::SpaceGroup& spacegroup) { + CrystalStructureAdapter::SymOpVector rv; + const int nbsym = spacegroup.GetNbSymmetrics(); + rv.resize(nbsym); + const vector& sgsymops = + spacegroup.GetSymmetryOperations(); + // copy convert the ObjCryst symmetry operations + int last = sgsymops.size(); + assert(last <= nbsym); + for (int i = 0; i != last; ++i) { + CrystalStructureAdapter::SymOpVector::value_type& op = rv[i]; + copy(sgsymops[i].mx, sgsymops[i].mx + 9, op.R.data().begin()); + copy(sgsymops[i].tr, sgsymops[i].tr + 3, op.t.data().begin()); + } + // apply all origin translations + const vector& sgtrans = + spacegroup.GetTranslationVectors(); + const int nbtran = spacegroup.GetNbTranslationVectors(); + assert(int(sgtrans.size()) == nbtran); + assert(nbtran * last <= nbsym); + for (int nt = 0; nt < nbtran; ++nt) { + const REAL* pt = sgtrans[nt].tr; + R3::Vector sgt(pt[0], pt[1], pt[2]); + for (int i = 0; i < last; ++i) { + int k = i + nt * last; + assert(k < nbsym); + rv[k] = rv[i]; + rv[k].t += sgt; } - // apply all origin translations - const vector& sgtrans = - spacegroup.GetTranslationVectors(); - const int nbtran = spacegroup.GetNbTranslationVectors(); - assert(int(sgtrans.size()) == nbtran); - assert(nbtran * last <= nbsym); - for (int nt = 0; nt < nbtran; ++nt) - { - const REAL* pt = sgtrans[nt].tr; - R3::Vector sgt(pt[0], pt[1], pt[2]); - for (int i = 0; i < last; ++i) - { - int k = i + nt * last; - assert(k < nbsym); - rv[k] = rv[i]; - rv[k].t += sgt; - } + } + last = sgsymops.size() * nbtran; + // apply the inversion center if necessary + if (last < nbsym) { + assert(spacegroup.IsCentrosymmetric()); + assert(nbsym == 2 * last); + CrystVector_REAL xyzinv = spacegroup.GetInversionCenter(); + // tinv is the overall translation associated with inversion center. + // it is double the inversion center position. + R3::Vector tinv(xyzinv(0), xyzinv(1), xyzinv(2)); + tinv *= 2.0; + // now apply the inversion center here + for (int i = 0, j = last; i < last; ++i, ++j) { + rv[j].R = -1 * rv[i].R; + rv[j].t = tinv - rv[i].t; } - last = sgsymops.size() * nbtran; - // apply the inversion center if necessary - if (last < nbsym) - { - assert(spacegroup.IsCentrosymmetric()); - assert(nbsym == 2 * last); - CrystVector_REAL xyzinv = spacegroup.GetInversionCenter(); - // tinv is the overall translation associated with inversion center. - // it is double the inversion center position. - R3::Vector tinv(xyzinv(0), xyzinv(1), xyzinv(2)); - tinv *= 2.0; - // now apply the inversion center here - for (int i = 0, j = last; i < last; ++i, ++j) - { - rv[j].R = -1 * rv[i].R; - rv[j].t = tinv - rv[i].t; - } - last *= 2; - } - assert(nbsym == last); - return rv; + last *= 2; + } + assert(nbsym == last); + return rv; } -} // namespace +} // namespace ////////////////////////////////////////////////////////////////////////////// // StructureAdapter factory for ObjCryst::Crystal ////////////////////////////////////////////////////////////////////////////// -StructureAdapterPtr -createStructureAdapter(const ObjCryst::Crystal& cryst) -{ - const double radtodeg = 180 / M_PI; - CrystalStructureAdapterPtr adpt(new CrystalStructureAdapter); - adpt->setLatPar( - cryst.GetLatticePar(0), - cryst.GetLatticePar(1), - cryst.GetLatticePar(2), - radtodeg * cryst.GetLatticePar(3), - radtodeg * cryst.GetLatticePar(4), - radtodeg * cryst.GetLatticePar(5)); - // find out number of scatterers in the asymmetric unit - const ObjCryst::ScatteringComponentList& scl = - cryst.GetScatteringComponentList(); - size_t nbComponent = scl.GetNbComponent(); - adpt->reserve(nbComponent); - Atom ai; - const Lattice& L = adpt->getLattice(); - for (size_t i = 0; i < nbComponent; ++i) - { - const ObjCryst::ScatteringComponent& sc = scl(i); - const ObjCryst::ScatteringPower* sp = sc.mpScattPow; - // Skip over this if it is a dummy atom. A dummy atom has no - // mpScattPow, and therefore no type. It's just in a structure as a - // reference position. - if (sp == NULL) continue; - ai.occupancy = sc.mOccupancy; - ai.anisotropy = !(sp->IsIsotropic()); - ai.atomtype = sp->GetSymbol(); - R3::Vector xyz(sc.mX, sc.mY, sc.mZ); - ai.xyz_cartn = L.cartesian(xyz); - // Store Uij - R3::Matrix uijl = getUij(sp); - ai.uij_cartn = ai.anisotropy ? L.cartesianMatrix(uijl) : uijl; - adpt->append(ai); - } - const ObjCryst::SpaceGroup& spacegroup = cryst.GetSpaceGroup(); - CrystalStructureAdapter::SymOpVector symops = - fetchSymmetryOperations(spacegroup); - CrystalStructureAdapter::SymOpVector::const_iterator op; - for (op = symops.begin(); op != symops.end(); ++op) adpt->addSymOp(*op); - adpt->updateSymmetryPositions(); - return adpt; +StructureAdapterPtr createStructureAdapter(const ObjCryst::Crystal& cryst) { + const double radtodeg = 180 / M_PI; + CrystalStructureAdapterPtr adpt(new CrystalStructureAdapter); + adpt->setLatPar(cryst.GetLatticePar(0), cryst.GetLatticePar(1), + cryst.GetLatticePar(2), radtodeg * cryst.GetLatticePar(3), + radtodeg * cryst.GetLatticePar(4), + radtodeg * cryst.GetLatticePar(5)); + // find out number of scatterers in the asymmetric unit + const ObjCryst::ScatteringComponentList& scl = + cryst.GetScatteringComponentList(); + size_t nbComponent = scl.GetNbComponent(); + adpt->reserve(nbComponent); + Atom ai; + const Lattice& L = adpt->getLattice(); + for (size_t i = 0; i < nbComponent; ++i) { + const ObjCryst::ScatteringComponent& sc = scl(i); + const ObjCryst::ScatteringPower* sp = sc.mpScattPow; + // Skip over this if it is a dummy atom. A dummy atom has no + // mpScattPow, and therefore no type. It's just in a structure as a + // reference position. + if (sp == NULL) continue; + ai.occupancy = sc.mOccupancy; + ai.anisotropy = !(sp->IsIsotropic()); + ai.atomtype = sp->GetSymbol(); + R3::Vector xyz(sc.mX, sc.mY, sc.mZ); + ai.xyz_cartn = L.cartesian(xyz); + // Store Uij + R3::Matrix uijl = getUij(sp); + ai.uij_cartn = ai.anisotropy ? L.cartesianMatrix(uijl) : uijl; + adpt->append(ai); + } + const ObjCryst::SpaceGroup& spacegroup = cryst.GetSpaceGroup(); + CrystalStructureAdapter::SymOpVector symops = + fetchSymmetryOperations(spacegroup); + CrystalStructureAdapter::SymOpVector::const_iterator op; + for (op = symops.begin(); op != symops.end(); ++op) adpt->addSymOp(*op); + adpt->updateSymmetryPositions(); + return adpt; } ////////////////////////////////////////////////////////////////////////////// // StructureAdapter factory for ObjCryst::Molecule ////////////////////////////////////////////////////////////////////////////// -StructureAdapterPtr -createStructureAdapter(const ObjCryst::Molecule& molecule) -{ - AtomicStructureAdapterPtr adpt(new AtomicStructureAdapter); - size_t nbComponent = molecule.GetNbComponent(); - adpt->reserve(nbComponent); - Atom ai; - for (size_t i = 0; i < nbComponent; ++i) - { - const ObjCryst::MolAtom& atom = molecule.GetAtom(i); - if (atom.IsDummy()) continue; - ai.occupancy = atom.GetOccupancy(); - const ObjCryst::ScatteringPower* sp = &(atom.GetScatteringPower()); - ai.anisotropy = !(sp->IsIsotropic()); - ai.atomtype = sp->GetSymbol(); - ai.xyz_cartn[0] = atom.X(); - ai.xyz_cartn[1] = atom.Y(); - ai.xyz_cartn[2] = atom.Z(); - // Store Uij - ai.uij_cartn = getUij(sp); - adpt->append(ai); - } - return adpt; +StructureAdapterPtr createStructureAdapter(const ObjCryst::Molecule& molecule) { + AtomicStructureAdapterPtr adpt(new AtomicStructureAdapter); + size_t nbComponent = molecule.GetNbComponent(); + adpt->reserve(nbComponent); + Atom ai; + for (size_t i = 0; i < nbComponent; ++i) { + const ObjCryst::MolAtom& atom = molecule.GetAtom(i); + if (atom.IsDummy()) continue; + ai.occupancy = atom.GetOccupancy(); + const ObjCryst::ScatteringPower* sp = &(atom.GetScatteringPower()); + ai.anisotropy = !(sp->IsIsotropic()); + ai.atomtype = sp->GetSymbol(); + ai.xyz_cartn[0] = atom.X(); + ai.xyz_cartn[1] = atom.Y(); + ai.xyz_cartn[2] = atom.Z(); + // Store Uij + ai.uij_cartn = getUij(sp); + adpt->append(ai); + } + return adpt; } -} // namespace srreal -} // namespace diffpy +} // namespace srreal +} // namespace diffpy // End of file diff --git a/src/diffpy/srreal/ObjCrystStructureAdapter.hpp b/src/diffpy/srreal/ObjCrystStructureAdapter.hpp index 5851b021..56b0b946 100644 --- a/src/diffpy/srreal/ObjCrystStructureAdapter.hpp +++ b/src/diffpy/srreal/ObjCrystStructureAdapter.hpp @@ -1,22 +1,22 @@ /***************************************************************************** -* -* 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: Chris Farrow -* -* See AUTHORS.rst for a list of people who contributed. -* See LICENSE_DANSE.txt for license information. -* -****************************************************************************** -* -* StructureAdapterPtr createStructureAdapter(const ObjCryst::Crystal&) -* StructureAdapterPtr createStructureAdapter(const ObjCryst::Molecule&) -* -- structure adapter factories for ObjCryst objects. -* -*****************************************************************************/ + * + * 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: Chris Farrow + * + * See AUTHORS.rst for a list of people who contributed. + * See LICENSE_DANSE.txt for license information. + * + ****************************************************************************** + * + * StructureAdapterPtr createStructureAdapter(const ObjCryst::Crystal&) + * StructureAdapterPtr createStructureAdapter(const ObjCryst::Molecule&) + * -- structure adapter factories for ObjCryst objects. + * + *****************************************************************************/ #ifndef OBJCRYSTSTRUCTUREADAPTER_HPP_INCLUDED #define OBJCRYSTSTRUCTUREADAPTER_HPP_INCLUDED @@ -43,12 +43,11 @@ createStructureAdapter(const ObjCryst::Crystal& cryst); // as if in a cartesian cell. If this is not what is intended, pass the // molecule as a scattering component within an ObjCryst::Crystal. - DLL_EXPORT StructureAdapterPtr createStructureAdapter(const ObjCryst::Molecule& molecule); -} // namespace srreal -} // namespace diffpy +} // namespace srreal +} // namespace diffpy // Required for argument-lookup visibility when instantiating the // convertToStructureAdapter template. diff --git a/src/diffpy/srreal/OverlapCalculator.cpp b/src/diffpy/srreal/OverlapCalculator.cpp index 2bf0360c..5541e0f3 100644 --- a/src/diffpy/srreal/OverlapCalculator.cpp +++ b/src/diffpy/srreal/OverlapCalculator.cpp @@ -1,20 +1,20 @@ /***************************************************************************** -* -* 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.txt for license information. -* -****************************************************************************** -* -* class OverlapCalculator -- calculator of atom radii overlaps -* -*****************************************************************************/ + * + * 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.txt for license information. + * + ****************************************************************************** + * + * class OverlapCalculator -- calculator of atom radii overlaps + * + *****************************************************************************/ #include @@ -35,533 +35,440 @@ namespace srreal { namespace { enum { - DISTANCE_OFFSET, - DIRECTION0_OFFSET, - DIRECTION1_OFFSET, - DIRECTION2_OFFSET, - SITE0_OFFSET, - SITE1_OFFSET, - CHUNK_SIZE, + DISTANCE_OFFSET, + DIRECTION0_OFFSET, + DIRECTION1_OFFSET, + DIRECTION2_OFFSET, + SITE0_OFFSET, + SITE1_OFFSET, + CHUNK_SIZE, }; - template -vector vectorconvert(const QuantityType& v) -{ - vector rv; - rv.reserve(v.size()); - QuantityType::const_iterator x = v.begin(); - for (; x != v.end(); ++x) rv.push_back(T(*x)); - return rv; +vector vectorconvert(const QuantityType& v) { + vector rv; + rv.reserve(v.size()); + QuantityType::const_iterator x = v.begin(); + for (; x != v.end(); ++x) rv.push_back(T(*x)); + return rv; } - -} // namespace +} // namespace // Constructor --------------------------------------------------------------- -OverlapCalculator::OverlapCalculator() -{ - mevaluator->setFlag(USEFULLSUM, true); - // default configuration - AtomRadiiTablePtr table(new ConstantRadiiTable); - this->setAtomRadiiTable(table); - this->cacheStructureData(); - // use very large rmax, it will be cropped by rmaxused - this->setRmax(100); - mneighborids_cached = false; - // attributes - this->registerDoubleAttribute("rmaxused", this, - &OverlapCalculator::getRmaxUsed); +OverlapCalculator::OverlapCalculator() { + mevaluator->setFlag(USEFULLSUM, true); + // default configuration + AtomRadiiTablePtr table(new ConstantRadiiTable); + this->setAtomRadiiTable(table); + this->cacheStructureData(); + // use very large rmax, it will be cropped by rmaxused + this->setRmax(100); + mneighborids_cached = false; + // attributes + this->registerDoubleAttribute("rmaxused", this, + &OverlapCalculator::getRmaxUsed); }; // Public Methods ------------------------------------------------------------ -QuantityType OverlapCalculator::overlaps() const -{ - int n = this->count(); - QuantityType rv; - rv.reserve(n); - for (int idx = 0; idx < n; ++idx) - { - double olp = this->suboverlap(idx); - if (olp <= 0.0) continue; - rv.push_back(olp); - } - return rv; -} - - -QuantityType OverlapCalculator::distances() const -{ - return this->subvector(DISTANCE_OFFSET, OVERLAPPING); -} - - -vector OverlapCalculator::directions() const -{ - int n = this->count(); - vector rv; - rv.reserve(n); - for (int index = 0; index < n; ++index) - { - if (this->suboverlap(index) <= 0.0) continue; - rv.push_back(this->subdirection(index)); - } - return rv; -} - - -SiteIndices OverlapCalculator::sites0() const -{ - QuantityType rv0 = this->subvector(SITE0_OFFSET, OVERLAPPING); - return vectorconvert(rv0); -} - - -SiteIndices OverlapCalculator::sites1() const -{ - QuantityType rv0 = this->subvector(SITE1_OFFSET, OVERLAPPING); - return vectorconvert(rv0); +QuantityType OverlapCalculator::overlaps() const { + int n = this->count(); + QuantityType rv; + rv.reserve(n); + for (int idx = 0; idx < n; ++idx) { + double olp = this->suboverlap(idx); + if (olp <= 0.0) continue; + rv.push_back(olp); + } + return rv; } - -vector OverlapCalculator::types0() const -{ - return siteIndicesToTypes(mstructure, this->sites0()); +QuantityType OverlapCalculator::distances() const { + return this->subvector(DISTANCE_OFFSET, OVERLAPPING); } - -vector OverlapCalculator::types1() const -{ - return siteIndicesToTypes(mstructure, this->sites1()); +vector OverlapCalculator::directions() const { + int n = this->count(); + vector rv; + rv.reserve(n); + for (int index = 0; index < n; ++index) { + if (this->suboverlap(index) <= 0.0) continue; + rv.push_back(this->subdirection(index)); + } + return rv; } - -QuantityType OverlapCalculator::siteSquareOverlaps() const -{ - int n = this->count(); - int cntsites = this->countSites(); - QuantityType rv(cntsites, 0.0); - for (int index = 0; index < n; ++index) - { - double olp = this->suboverlap(index); - if (olp <= 0.0) continue; - double sqoverlap = olp * olp; - int i = int(this->subvalue(SITE0_OFFSET, index)); - int j = int(this->subvalue(SITE1_OFFSET, index)); - rv[i] += sqoverlap * mstructure->siteOccupancy(j); - } - // overlaps are shared by 2 atoms - QuantityType::iterator xi; - for (xi = rv.begin(); xi != rv.end(); ++xi) *xi /= 2; - return rv; -} - - -double OverlapCalculator::totalSquareOverlap() const -{ - int cntsites = this->countSites(); - QuantityType sqoverlap = this->siteSquareOverlaps(); - double rv = 0.0; - for (int i = 0; i < cntsites; ++i) - { - rv += sqoverlap[i] * - mstructure->siteMultiplicity(i) * mstructure->siteOccupancy(i); - } - return rv; -} - - -double OverlapCalculator::meanSquareOverlap() const -{ - double totocc = mstructure->totalOccupancy(); - double rv = (totocc > 0) ? (this->totalSquareOverlap() / totocc) : 0.0; - return rv; +SiteIndices OverlapCalculator::sites0() const { + QuantityType rv0 = this->subvector(SITE0_OFFSET, OVERLAPPING); + return vectorconvert(rv0); } - -double OverlapCalculator::flipDiffTotal(int i, int j) const -{ - int cntsites = this->countSites(); - if (i < 0 || i >= cntsites || j < 0 || j >= cntsites) - { - const char* emsg = "Index out of range."; - throw invalid_argument(emsg); - } - bool sameradii = (i == j) || - (mstructure_cache.siteradii[i] == mstructure_cache.siteradii[j]); - if (sameradii) return 0.0; - // here we have to remove the overlap contributions for i and j - double rv = 0.0; - const list& ineighbors = this->getNeighborIds(i); - const list& jneighbors = this->getNeighborIds(j); - unordered_set allids; - allids.insert(ineighbors.begin(), ineighbors.end()); - allids.insert(jneighbors.begin(), jneighbors.end()); - unordered_set::const_iterator idx; - for (idx = allids.begin(); idx != allids.end(); ++idx) - { - int i1 = int(this->subvalue(SITE0_OFFSET, *idx)); - int j1 = int(this->subvalue(SITE1_OFFSET, *idx)); - double sqscale = - ((i1 == j1) ? 1 : 2) * - mstructure->siteOccupancy(i1) * mstructure->siteOccupancy(j1) * - mstructure->siteMultiplicity(i1) / 2; - double olp0 = this->suboverlap(*idx); - double olp1 = this->suboverlap(*idx, i, j); - rv -= sqscale * olp0 * olp0; - rv += sqscale * olp1 * olp1; - } - return rv; +SiteIndices OverlapCalculator::sites1() const { + QuantityType rv0 = this->subvector(SITE1_OFFSET, OVERLAPPING); + return vectorconvert(rv0); } - -double OverlapCalculator::flipDiffMean(int i, int j) const -{ - double totocc = mstructure->totalOccupancy(); - double rv = (totocc > 0) ? (this->flipDiffTotal(i, j) / totocc) : 0.0; - return rv; +vector OverlapCalculator::types0() const { + return siteIndicesToTypes(mstructure, this->sites0()); } - -vector OverlapCalculator::gradients() const -{ - using diffpy::mathutils::eps_gt; - int n = this->count(); - int cntsites = this->countSites(); - vector rv(cntsites, R3::Vector(0.0, 0.0, 0.0)); - R3::Vector gij; - for (int index = 0; index < n; ++index) - { - double olp = this->suboverlap(index); - if (olp <= 0.0) continue; - const double& dst = this->subvalue(DISTANCE_OFFSET, index); - assert(eps_gt(dst, 0.0)); - int j = int(this->subvalue(SITE1_OFFSET, index)); - gij = -2.0 * olp / dst * this->subdirection(index); - rv[j] += gij; - } - return rv; -} - - -unordered_set OverlapCalculator::getNeighborSites(int i) const -{ - unordered_set rv; - const list& ineighbors = this->getNeighborIds(i); - list::const_iterator idx; - for (idx = ineighbors.begin(); idx != ineighbors.end(); ++idx) - { - double olp = this->suboverlap(*idx); - if (olp <= 0.0) continue; - assert(i == int(this->subvalue(SITE0_OFFSET, *idx))); - int j1 = int(this->subvalue(SITE1_OFFSET, *idx)); - rv.insert(j1); - } - return rv; +vector OverlapCalculator::types1() const { + return siteIndicesToTypes(mstructure, this->sites1()); } - -QuantityType OverlapCalculator::coordinations() const -{ - int cntsites = this->countSites(); - QuantityType rv(cntsites, 0.0); - int n = this->count(); - for (int index = 0; index < n; ++index) - { - double olp = this->suboverlap(index); - if (olp <= 0.0) continue; - int j0 = int(this->subvalue(SITE0_OFFSET, index)); - int j1 = int(this->subvalue(SITE1_OFFSET, index)); - rv[j0] += mstructure->siteOccupancy(j1); - } - return rv; -} - - -unordered_map -OverlapCalculator::coordinationByTypes(int i) const -{ - unordered_map rv; - const list& ineighbors = this->getNeighborIds(i); - list::const_iterator idx; - for (idx = ineighbors.begin(); idx != ineighbors.end(); ++idx) - { - double olp = this->suboverlap(*idx); - if (olp <= 0.0) continue; - int j0 = int(this->subvalue(SITE0_OFFSET, *idx)); - int j1 = int(this->subvalue(SITE1_OFFSET, *idx)); - if (j0 == i) - { - const string& tp = mstructure->siteAtomType(j1); - rv[tp] += mstructure->siteOccupancy(j1); - } - else - { - assert(j1 == i); - const string& tp = mstructure->siteAtomType(j0); - rv[tp] += mstructure->siteOccupancy(j0) * +QuantityType OverlapCalculator::siteSquareOverlaps() const { + int n = this->count(); + int cntsites = this->countSites(); + QuantityType rv(cntsites, 0.0); + for (int index = 0; index < n; ++index) { + double olp = this->suboverlap(index); + if (olp <= 0.0) continue; + double sqoverlap = olp * olp; + int i = int(this->subvalue(SITE0_OFFSET, index)); + int j = int(this->subvalue(SITE1_OFFSET, index)); + rv[i] += sqoverlap * mstructure->siteOccupancy(j); + } + // overlaps are shared by 2 atoms + QuantityType::iterator xi; + for (xi = rv.begin(); xi != rv.end(); ++xi) *xi /= 2; + return rv; +} + +double OverlapCalculator::totalSquareOverlap() const { + int cntsites = this->countSites(); + QuantityType sqoverlap = this->siteSquareOverlaps(); + double rv = 0.0; + for (int i = 0; i < cntsites; ++i) { + rv += sqoverlap[i] * mstructure->siteMultiplicity(i) * + mstructure->siteOccupancy(i); + } + return rv; +} + +double OverlapCalculator::meanSquareOverlap() const { + double totocc = mstructure->totalOccupancy(); + double rv = (totocc > 0) ? (this->totalSquareOverlap() / totocc) : 0.0; + return rv; +} + +double OverlapCalculator::flipDiffTotal(int i, int j) const { + int cntsites = this->countSites(); + if (i < 0 || i >= cntsites || j < 0 || j >= cntsites) { + const char* emsg = "Index out of range."; + throw invalid_argument(emsg); + } + bool sameradii = (i == j) || (mstructure_cache.siteradii[i] == + mstructure_cache.siteradii[j]); + if (sameradii) return 0.0; + // here we have to remove the overlap contributions for i and j + double rv = 0.0; + const list& ineighbors = this->getNeighborIds(i); + const list& jneighbors = this->getNeighborIds(j); + unordered_set allids; + allids.insert(ineighbors.begin(), ineighbors.end()); + allids.insert(jneighbors.begin(), jneighbors.end()); + unordered_set::const_iterator idx; + for (idx = allids.begin(); idx != allids.end(); ++idx) { + int i1 = int(this->subvalue(SITE0_OFFSET, *idx)); + int j1 = int(this->subvalue(SITE1_OFFSET, *idx)); + double sqscale = ((i1 == j1) ? 1 : 2) * mstructure->siteOccupancy(i1) * + mstructure->siteOccupancy(j1) * + mstructure->siteMultiplicity(i1) / 2; + double olp0 = this->suboverlap(*idx); + double olp1 = this->suboverlap(*idx, i, j); + rv -= sqscale * olp0 * olp0; + rv += sqscale * olp1 * olp1; + } + return rv; +} + +double OverlapCalculator::flipDiffMean(int i, int j) const { + double totocc = mstructure->totalOccupancy(); + double rv = (totocc > 0) ? (this->flipDiffTotal(i, j) / totocc) : 0.0; + return rv; +} + +vector OverlapCalculator::gradients() const { + using diffpy::mathutils::eps_gt; + int n = this->count(); + int cntsites = this->countSites(); + vector rv(cntsites, R3::Vector(0.0, 0.0, 0.0)); + R3::Vector gij; + for (int index = 0; index < n; ++index) { + double olp = this->suboverlap(index); + if (olp <= 0.0) continue; + const double& dst = this->subvalue(DISTANCE_OFFSET, index); + assert(eps_gt(dst, 0.0)); + int j = int(this->subvalue(SITE1_OFFSET, index)); + gij = -2.0 * olp / dst * this->subdirection(index); + rv[j] += gij; + } + return rv; +} + +unordered_set OverlapCalculator::getNeighborSites(int i) const { + unordered_set rv; + const list& ineighbors = this->getNeighborIds(i); + list::const_iterator idx; + for (idx = ineighbors.begin(); idx != ineighbors.end(); ++idx) { + double olp = this->suboverlap(*idx); + if (olp <= 0.0) continue; + assert(i == int(this->subvalue(SITE0_OFFSET, *idx))); + int j1 = int(this->subvalue(SITE1_OFFSET, *idx)); + rv.insert(j1); + } + return rv; +} + +QuantityType OverlapCalculator::coordinations() const { + int cntsites = this->countSites(); + QuantityType rv(cntsites, 0.0); + int n = this->count(); + for (int index = 0; index < n; ++index) { + double olp = this->suboverlap(index); + if (olp <= 0.0) continue; + int j0 = int(this->subvalue(SITE0_OFFSET, index)); + int j1 = int(this->subvalue(SITE1_OFFSET, index)); + rv[j0] += mstructure->siteOccupancy(j1); + } + return rv; +} + +unordered_map OverlapCalculator::coordinationByTypes( + int i) const { + unordered_map rv; + const list& ineighbors = this->getNeighborIds(i); + list::const_iterator idx; + for (idx = ineighbors.begin(); idx != ineighbors.end(); ++idx) { + double olp = this->suboverlap(*idx); + if (olp <= 0.0) continue; + int j0 = int(this->subvalue(SITE0_OFFSET, *idx)); + int j1 = int(this->subvalue(SITE1_OFFSET, *idx)); + if (j0 == i) { + const string& tp = mstructure->siteAtomType(j1); + rv[tp] += mstructure->siteOccupancy(j1); + } else { + assert(j1 == i); + const string& tp = mstructure->siteAtomType(j0); + rv[tp] += mstructure->siteOccupancy(j0) * mstructure->siteMultiplicity(j0) / mstructure->siteMultiplicity(j1); - } } - return rv; -} - - -vector< unordered_set > OverlapCalculator::neighborhoods() const -{ - int cntsites = this->countSites(); - typedef unordered_set SiteSet; - typedef std::vector< std::shared_ptr > SiteSetPointers; - SiteSetPointers rvptr(cntsites); - int n = this->count(); - for (int index = 0; index < n; ++index) - { - double olp = this->suboverlap(index); - if (olp <= 0.0) continue; - int j0 = int(this->subvalue(SITE0_OFFSET, index)); - int j1 = int(this->subvalue(SITE1_OFFSET, index)); - if (!rvptr[j0].get()) - { - rvptr[j0].reset(new SiteSet); - rvptr[j0]->insert(j0); - } - if (!rvptr[j1].get()) - { - rvptr[j0]->insert(j1); - rvptr[j1] = rvptr[j0]; - } - assert(rvptr[j0].get() && rvptr[j1].get()); - if (rvptr[j0].get() == rvptr[j1].get()) continue; - // here we need to unify the j0 and j1 sets - SiteSetPointers::value_type ss1 = rvptr[j1]; - SiteSet::const_iterator idx1 = ss1->begin(); - for (; idx1 != ss1->end(); ++idx1) - { - rvptr[j0]->insert(*idx1); - rvptr[*idx1] = rvptr[j0]; - } + } + return rv; +} + +vector > OverlapCalculator::neighborhoods() const { + int cntsites = this->countSites(); + typedef unordered_set SiteSet; + typedef std::vector > SiteSetPointers; + SiteSetPointers rvptr(cntsites); + int n = this->count(); + for (int index = 0; index < n; ++index) { + double olp = this->suboverlap(index); + if (olp <= 0.0) continue; + int j0 = int(this->subvalue(SITE0_OFFSET, index)); + int j1 = int(this->subvalue(SITE1_OFFSET, index)); + if (!rvptr[j0].get()) { + rvptr[j0].reset(new SiteSet); + rvptr[j0]->insert(j0); } - // helper function that initializes neighbor set at site i - auto initsiteset = [&](int i, int& counter) { - if (!rvptr[i]) { - rvptr[i].reset(new SiteSet(&i, &i + 1)); - ++counter; - } - }; - // count initialized items in rvptr - int cnt = count_if(rvptr.begin(), rvptr.end(), - [](SiteSetPointers::value_type& p) { return bool(p); }); - // create self-neighborhoods unless prohibited by mask - for (int j0 = 0; j0 < cntsites && cnt < cntsites; ++j0) - { - for (int j1 = j0; j1 < cntsites; ++j1) - { - if (rvptr[j0] && rvptr[j1]) continue; - if (!this->getPairMask(j0, j1)) continue; - initsiteset(j0, cnt); - initsiteset(j1, cnt); - } + if (!rvptr[j1].get()) { + rvptr[j0]->insert(j1); + rvptr[j1] = rvptr[j0]; } - unordered_set duplicate; - vector< unordered_set > rv; - SiteSetPointers::const_iterator ssp; - for (ssp = rvptr.begin(); ssp != rvptr.end(); ++ssp) - { - if (!ssp->get() || duplicate.count(ssp->get())) continue; - duplicate.insert(ssp->get()); - rv.push_back(**ssp); + assert(rvptr[j0].get() && rvptr[j1].get()); + if (rvptr[j0].get() == rvptr[j1].get()) continue; + // here we need to unify the j0 and j1 sets + SiteSetPointers::value_type ss1 = rvptr[j1]; + SiteSet::const_iterator idx1 = ss1->begin(); + for (; idx1 != ss1->end(); ++idx1) { + rvptr[j0]->insert(*idx1); + rvptr[*idx1] = rvptr[j0]; } - return rv; + } + // helper function that initializes neighbor set at site i + auto initsiteset = [&](int i, int& counter) { + if (!rvptr[i]) { + rvptr[i].reset(new SiteSet(&i, &i + 1)); + ++counter; + } + }; + // count initialized items in rvptr + int cnt = count_if(rvptr.begin(), rvptr.end(), + [](SiteSetPointers::value_type& p) { return bool(p); }); + // create self-neighborhoods unless prohibited by mask + for (int j0 = 0; j0 < cntsites && cnt < cntsites; ++j0) { + for (int j1 = j0; j1 < cntsites; ++j1) { + if (rvptr[j0] && rvptr[j1]) continue; + if (!this->getPairMask(j0, j1)) continue; + initsiteset(j0, cnt); + initsiteset(j1, cnt); + } + } + unordered_set duplicate; + vector > rv; + SiteSetPointers::const_iterator ssp; + for (ssp = rvptr.begin(); ssp != rvptr.end(); ++ssp) { + if (!ssp->get() || duplicate.count(ssp->get())) continue; + duplicate.insert(ssp->get()); + rv.push_back(**ssp); + } + return rv; } - -void OverlapCalculator::setAtomRadiiTable(AtomRadiiTablePtr table) -{ - ensureNonNull("AtomRadiiTable", table); - matomradiitable = table; +void OverlapCalculator::setAtomRadiiTable(AtomRadiiTablePtr table) { + ensureNonNull("AtomRadiiTable", table); + matomradiitable = table; } - -void OverlapCalculator::setAtomRadiiTableByType(const std::string& tp) -{ - matomradiitable = AtomRadiiTable::createByType(tp); +void OverlapCalculator::setAtomRadiiTableByType(const std::string& tp) { + matomradiitable = AtomRadiiTable::createByType(tp); } - -AtomRadiiTablePtr& OverlapCalculator::getAtomRadiiTable() -{ - return matomradiitable; +AtomRadiiTablePtr& OverlapCalculator::getAtomRadiiTable() { + return matomradiitable; } - -const AtomRadiiTablePtr& OverlapCalculator::getAtomRadiiTable() const -{ - return matomradiitable; +const AtomRadiiTablePtr& OverlapCalculator::getAtomRadiiTable() const { + return matomradiitable; } - -double OverlapCalculator::getRmaxUsed() const -{ - assert(this->countSites() == int(mstructure_cache.siteradii.size())); - double rv = min(this->getRmax(), mstructure_cache.maxseparation); - return rv; +double OverlapCalculator::getRmaxUsed() const { + assert(this->countSites() == int(mstructure_cache.siteradii.size())); + double rv = min(this->getRmax(), mstructure_cache.maxseparation); + return rv; } // Protected Methods --------------------------------------------------------- -void OverlapCalculator::resetValue() -{ - mvalue.clear(); - mneighborids.clear(); - mneighborids_cached = false; - this->cacheStructureData(); - this->PairQuantity::resetValue(); +void OverlapCalculator::resetValue() { + mvalue.clear(); + mneighborids.clear(); + mneighborids_cached = false; + this->cacheStructureData(); + this->PairQuantity::resetValue(); } - -void OverlapCalculator::configureBondGenerator(BaseBondGenerator& bnds) const -{ - bnds.setRmin(this->getRmin()); - bnds.setRmax(this->getRmaxUsed()); +void OverlapCalculator::configureBondGenerator(BaseBondGenerator& bnds) const { + bnds.setRmin(this->getRmin()); + bnds.setRmax(this->getRmaxUsed()); } - -void OverlapCalculator::addPairContribution( - const BaseBondGenerator& bnds, int summationscale) -{ - assert(summationscale == 1); - assert(bnds.distance() <= mstructure_cache.maxseparation); - const R3::Vector& r01 = bnds.r01(); - int baseidx = mvalue.size(); - mvalue.insert(mvalue.end(), CHUNK_SIZE, 0.0); - mvalue[baseidx + DISTANCE_OFFSET] = bnds.distance(); - mvalue[baseidx + DIRECTION0_OFFSET] = r01[0]; - mvalue[baseidx + DIRECTION1_OFFSET] = r01[1]; - mvalue[baseidx + DIRECTION2_OFFSET] = r01[2]; - mvalue[baseidx + SITE0_OFFSET] = bnds.site0(); - mvalue[baseidx + SITE1_OFFSET] = bnds.site1(); +void OverlapCalculator::addPairContribution(const BaseBondGenerator& bnds, + int summationscale) { + assert(summationscale == 1); + assert(bnds.distance() <= mstructure_cache.maxseparation); + const R3::Vector& r01 = bnds.r01(); + int baseidx = mvalue.size(); + mvalue.insert(mvalue.end(), CHUNK_SIZE, 0.0); + mvalue[baseidx + DISTANCE_OFFSET] = bnds.distance(); + mvalue[baseidx + DIRECTION0_OFFSET] = r01[0]; + mvalue[baseidx + DIRECTION1_OFFSET] = r01[1]; + mvalue[baseidx + DIRECTION2_OFFSET] = r01[2]; + mvalue[baseidx + SITE0_OFFSET] = bnds.site0(); + mvalue[baseidx + SITE1_OFFSET] = bnds.site1(); } - -void OverlapCalculator::executeParallelMerge(const std::string& pdata) -{ - istringstream storage(pdata, ios::binary); - diffpy::serialization::iarchive ia(storage, ios::binary); - QuantityType pvalue; - ia >> pvalue; - mvalue.insert(mvalue.end(), pvalue.begin(), pvalue.end()); +void OverlapCalculator::executeParallelMerge(const std::string& pdata) { + istringstream storage(pdata, ios::binary); + diffpy::serialization::iarchive ia(storage, ios::binary); + QuantityType pvalue; + ia >> pvalue; + mvalue.insert(mvalue.end(), pvalue.begin(), pvalue.end()); } // Private Methods ----------------------------------------------------------- -int OverlapCalculator::count() const -{ - int rv = int(mvalue.size()) / CHUNK_SIZE; - return rv; -} - - -QuantityType OverlapCalculator::subvector(int offset, - OverlapCalculator::OverlapFlag flag) const -{ - assert(0 <= offset && offset < CHUNK_SIZE); - assert(flag == ALLVALUES || flag == OVERLAPPING); +int OverlapCalculator::count() const { + int rv = int(mvalue.size()) / CHUNK_SIZE; + return rv; +} + +QuantityType OverlapCalculator::subvector( + int offset, OverlapCalculator::OverlapFlag flag) const { + assert(0 <= offset && offset < CHUNK_SIZE); + assert(flag == ALLVALUES || flag == OVERLAPPING); + int n = this->count(); + QuantityType rv; + rv.reserve(n); + for (int index = 0; index < n; ++index) { + if (OVERLAPPING == flag && this->suboverlap(index) <= 0.0) continue; + rv.push_back(this->subvalue(offset, index)); + } + return rv; +} + +const double& OverlapCalculator::subvalue(int offset, int index) const { + assert(0 <= offset && offset < CHUNK_SIZE); + assert(0 <= index && index < this->count()); + return mvalue[offset + CHUNK_SIZE * index]; +} + +const R3::Vector& OverlapCalculator::subdirection(int index) const { + static R3::Vector rv; + rv[0] = this->subvalue(DIRECTION0_OFFSET, index); + rv[1] = this->subvalue(DIRECTION1_OFFSET, index); + rv[2] = this->subvalue(DIRECTION2_OFFSET, index); + return rv; +} + +double OverlapCalculator::suboverlap(int index, int flipi, int flipj) const { + assert(0 <= flipi && flipi < this->countSites()); + assert(0 <= flipj && flipj < this->countSites()); + int i = int(this->subvalue(SITE0_OFFSET, index)); + int j = int(this->subvalue(SITE1_OFFSET, index)); + const double& radiusi = (flipi == flipj) ? mstructure_cache.siteradii[i] + : (i == flipi) ? mstructure_cache.siteradii[flipj] + : (i == flipj) ? mstructure_cache.siteradii[flipi] + : mstructure_cache.siteradii[i]; + const double& radiusj = (flipi == flipj) ? mstructure_cache.siteradii[j] + : (j == flipi) ? mstructure_cache.siteradii[flipj] + : (j == flipj) ? mstructure_cache.siteradii[flipi] + : mstructure_cache.siteradii[j]; + const double& dij = this->subvalue(DISTANCE_OFFSET, index); + double sepij = radiusi + radiusj; + double rv = (dij < sepij) ? (sepij - dij) : 0.0; + return rv; +} + +void OverlapCalculator::cacheStructureData() { + int cntsites = this->countSites(); + mstructure_cache.siteradii.resize(cntsites); + const AtomRadiiTablePtr& table = this->getAtomRadiiTable(); + for (int i = 0; i < cntsites; ++i) { + const string& smbl = mstructure->siteAtomType(i); + mstructure_cache.siteradii[i] = table->lookup(smbl); + } + double maxradius = mstructure_cache.siteradii.empty() + ? 0.0 + : *max_element(mstructure_cache.siteradii.begin(), + mstructure_cache.siteradii.end()); + mstructure_cache.maxseparation = 2 * maxradius; +} + +const std::list& OverlapCalculator::getNeighborIds(int k) const { + typedef std::list NbList; + assert(0 <= k && k < this->countSites()); + if (!mneighborids_cached) { + assert(mneighborids.empty()); int n = this->count(); - QuantityType rv; - rv.reserve(n); - for (int index = 0; index < n; ++index) - { - if (OVERLAPPING == flag && this->suboverlap(index) <= 0.0) continue; - rv.push_back(this->subvalue(offset, index)); - } - return rv; -} - - -const double& OverlapCalculator::subvalue(int offset, int index) const -{ - assert(0 <= offset && offset < CHUNK_SIZE); - assert(0 <= index && index < this->count()); - return mvalue[offset + CHUNK_SIZE * index]; -} - - -const R3::Vector& OverlapCalculator::subdirection(int index) const -{ - static R3::Vector rv; - rv[0] = this->subvalue(DIRECTION0_OFFSET, index); - rv[1] = this->subvalue(DIRECTION1_OFFSET, index); - rv[2] = this->subvalue(DIRECTION2_OFFSET, index); - return rv; -} - - -double OverlapCalculator::suboverlap(int index, int flipi, int flipj) const -{ - assert(0 <= flipi && flipi < this->countSites()); - assert(0 <= flipj && flipj < this->countSites()); - int i = int(this->subvalue(SITE0_OFFSET, index)); - int j = int(this->subvalue(SITE1_OFFSET, index)); - const double& radiusi = (flipi == flipj) ? mstructure_cache.siteradii[i] : - (i == flipi) ? mstructure_cache.siteradii[flipj] : - (i == flipj) ? mstructure_cache.siteradii[flipi] : - mstructure_cache.siteradii[i]; - const double& radiusj = (flipi == flipj) ? mstructure_cache.siteradii[j] : - (j == flipi) ? mstructure_cache.siteradii[flipj] : - (j == flipj) ? mstructure_cache.siteradii[flipi] : - mstructure_cache.siteradii[j]; - const double& dij = this->subvalue(DISTANCE_OFFSET, index); - double sepij = radiusi + radiusj; - double rv = (dij < sepij) ? (sepij - dij) : 0.0; - return rv; -} - - -void OverlapCalculator::cacheStructureData() -{ - int cntsites = this->countSites(); - mstructure_cache.siteradii.resize(cntsites); - const AtomRadiiTablePtr& table = this->getAtomRadiiTable(); - for (int i = 0; i < cntsites; ++i) - { - const string& smbl = mstructure->siteAtomType(i); - mstructure_cache.siteradii[i] = table->lookup(smbl); - } - double maxradius = mstructure_cache.siteradii.empty() ? - 0.0 : *max_element(mstructure_cache.siteradii.begin(), - mstructure_cache.siteradii.end()); - mstructure_cache.maxseparation = 2 * maxradius; -} - - -const std::list& -OverlapCalculator::getNeighborIds(int k) const -{ - typedef std::list NbList; - assert(0 <= k && k < this->countSites()); - if (!mneighborids_cached) - { - assert(mneighborids.empty()); - int n = this->count(); - for (int idx = 0; idx < n; ++idx) - { - int i = int(this->subvalue(SITE0_OFFSET, idx)); - mneighborids[i].push_back(idx); - } - mneighborids_cached = true; + for (int idx = 0; idx < n; ++idx) { + int i = int(this->subvalue(SITE0_OFFSET, idx)); + mneighborids[i].push_back(idx); } - static NbList noneighbors; - NeighborIdsStorage::const_iterator nbit = mneighborids.find(k); - const NbList& rv = - (nbit == mneighborids.end()) ? noneighbors : nbit->second; - return rv; + mneighborids_cached = true; + } + static NbList noneighbors; + NeighborIdsStorage::const_iterator nbit = mneighborids.find(k); + const NbList& rv = (nbit == mneighborids.end()) ? noneighbors : nbit->second; + return rv; } -} // namespace srreal -} // namespace diffpy +} // namespace srreal +} // namespace diffpy // Serialization ------------------------------------------------------------- diff --git a/src/diffpy/srreal/OverlapCalculator.hpp b/src/diffpy/srreal/OverlapCalculator.hpp index c1078475..6a5226b2 100644 --- a/src/diffpy/srreal/OverlapCalculator.hpp +++ b/src/diffpy/srreal/OverlapCalculator.hpp @@ -1,20 +1,20 @@ /***************************************************************************** -* -* 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.txt for license information. -* -****************************************************************************** -* -* class OverlapCalculator -- calculator of atom radii overlaps -* -*****************************************************************************/ + * + * 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.txt for license information. + * + ****************************************************************************** + * + * class OverlapCalculator -- calculator of atom radii overlaps + * + *****************************************************************************/ #ifndef OVERLAPCALCULATOR_HPP_INCLUDED #define OVERLAPCALCULATOR_HPP_INCLUDED @@ -29,122 +29,113 @@ namespace diffpy { namespace srreal { -class DLL_EXPORT OverlapCalculator : public PairQuantity -{ - public: - - // constructor - OverlapCalculator(); - - // methods - /// return siteSquareOverlaps for the specified structure - template QuantityType operator()(const T&); - /// overlap values for all overlapping sites in the structure - QuantityType overlaps() const; - /// distances all overlapping sites in the structure - QuantityType distances() const; - /// directions between the centers of overlapping sites - std::vector directions() const; - /// indices of the first site for all overlapping pairs - SiteIndices sites0() const; - /// indices of the second site for all overlapping pairs - SiteIndices sites1() const; - /// atom symbols for the first site of all overlapping pairs - std::vector types0() const; - /// atom symbols for the second site of all overlapping pairs - std::vector types1() const; - /// sum of squared overlaps at each structure site - QuantityType siteSquareOverlaps() const; - /// sum of squared overlaps for all atoms in the structure - double totalSquareOverlap() const; - /// mean square per one atom in the structure - double meanSquareOverlap() const; - /// difference in the totalSquareOverlap for a flip of two sites - double flipDiffTotal(int i, int j) const; - /// difference in the meanSquareOverlap for a flip of two sites - double flipDiffMean(int i, int j) const; - /// gradients of totalSquareOverlap at each site in the structure - std::vector gradients() const; - /// indices of the neighboring sites - std::unordered_set getNeighborSites(int i) const; - /// coordination number at each site of the structure - QuantityType coordinations() const; - /// coordination number split per each type of neighboring atoms - std::unordered_map - coordinationByTypes(int i) const; - /// sets of site inidices per each sites neighborhood in the structure - std::vector< std::unordered_set > neighborhoods() const; - - // access and configuration of the atom radii - void setAtomRadiiTable(AtomRadiiTablePtr); - void setAtomRadiiTableByType(const std::string& tp); - AtomRadiiTablePtr& getAtomRadiiTable(); - const AtomRadiiTablePtr& getAtomRadiiTable() const; - - /// effective rmax value, usually a double of the maximum atom radius. - double getRmaxUsed() const; - - - protected: - - // PairQuantity overloads - virtual void resetValue(); - virtual void configureBondGenerator(BaseBondGenerator&) const; - virtual void addPairContribution(const BaseBondGenerator&, int); - virtual void executeParallelMerge(const std::string&); - - private: - - // types - enum OverlapFlag {ALLVALUES, OVERLAPPING}; - typedef std::unordered_map > NeighborIdsStorage; - - // methods - int count() const; - QuantityType subvector(int offset, OverlapFlag flag) const; - const double& subvalue(int offset, int index) const; - const R3::Vector& subdirection(int index) const; - double suboverlap(int index, int iflip=0, int jflip=0) const; - void cacheStructureData(); - const std::list& getNeighborIds(int i) const; - - // data - AtomRadiiTablePtr matomradiitable; - mutable NeighborIdsStorage mneighborids; - mutable bool mneighborids_cached; - // cache - struct { - QuantityType siteradii; - double maxseparation; - } mstructure_cache; - - // serialization - friend class boost::serialization::access; - template - void serialize(Archive& ar, const unsigned int version) - { - using boost::serialization::base_object; - ar & base_object(*this); - ar & matomradiitable; - ar & mneighborids; - ar & mstructure_cache.siteradii; - ar & mstructure_cache.maxseparation; - } - +class DLL_EXPORT OverlapCalculator : public PairQuantity { + public: + // constructor + OverlapCalculator(); + + // methods + /// return siteSquareOverlaps for the specified structure + template + QuantityType operator()(const T&); + /// overlap values for all overlapping sites in the structure + QuantityType overlaps() const; + /// distances all overlapping sites in the structure + QuantityType distances() const; + /// directions between the centers of overlapping sites + std::vector directions() const; + /// indices of the first site for all overlapping pairs + SiteIndices sites0() const; + /// indices of the second site for all overlapping pairs + SiteIndices sites1() const; + /// atom symbols for the first site of all overlapping pairs + std::vector types0() const; + /// atom symbols for the second site of all overlapping pairs + std::vector types1() const; + /// sum of squared overlaps at each structure site + QuantityType siteSquareOverlaps() const; + /// sum of squared overlaps for all atoms in the structure + double totalSquareOverlap() const; + /// mean square per one atom in the structure + double meanSquareOverlap() const; + /// difference in the totalSquareOverlap for a flip of two sites + double flipDiffTotal(int i, int j) const; + /// difference in the meanSquareOverlap for a flip of two sites + double flipDiffMean(int i, int j) const; + /// gradients of totalSquareOverlap at each site in the structure + std::vector gradients() const; + /// indices of the neighboring sites + std::unordered_set getNeighborSites(int i) const; + /// coordination number at each site of the structure + QuantityType coordinations() const; + /// coordination number split per each type of neighboring atoms + std::unordered_map coordinationByTypes(int i) const; + /// sets of site inidices per each sites neighborhood in the structure + std::vector > neighborhoods() const; + + // access and configuration of the atom radii + void setAtomRadiiTable(AtomRadiiTablePtr); + void setAtomRadiiTableByType(const std::string& tp); + AtomRadiiTablePtr& getAtomRadiiTable(); + const AtomRadiiTablePtr& getAtomRadiiTable() const; + + /// effective rmax value, usually a double of the maximum atom radius. + double getRmaxUsed() const; + + protected: + // PairQuantity overloads + virtual void resetValue(); + virtual void configureBondGenerator(BaseBondGenerator&) const; + virtual void addPairContribution(const BaseBondGenerator&, int); + virtual void executeParallelMerge(const std::string&); + + private: + // types + enum OverlapFlag { ALLVALUES, OVERLAPPING }; + typedef std::unordered_map > NeighborIdsStorage; + + // methods + int count() const; + QuantityType subvector(int offset, OverlapFlag flag) const; + const double& subvalue(int offset, int index) const; + const R3::Vector& subdirection(int index) const; + double suboverlap(int index, int iflip = 0, int jflip = 0) const; + void cacheStructureData(); + const std::list& getNeighborIds(int i) const; + + // data + AtomRadiiTablePtr matomradiitable; + mutable NeighborIdsStorage mneighborids; + mutable bool mneighborids_cached; + // cache + struct { + QuantityType siteradii; + double maxseparation; + } mstructure_cache; + + // serialization + friend class boost::serialization::access; + template + void serialize(Archive& ar, const unsigned int version) { + using boost::serialization::base_object; + ar& base_object(*this); + ar & matomradiitable; + ar & mneighborids; + ar & mstructure_cache.siteradii; + ar & mstructure_cache.maxseparation; + } }; // Public Template Methods --------------------------------------------------- template -QuantityType OverlapCalculator::operator()(const T& stru) -{ - this->eval(stru); - return this->siteSquareOverlaps(); +QuantityType OverlapCalculator::operator()(const T& stru) { + this->eval(stru); + return this->siteSquareOverlaps(); } - -} // namespace srreal -} // namespace diffpy +} // namespace srreal +} // namespace diffpy // Serialization ------------------------------------------------------------- diff --git a/src/diffpy/srreal/PDFBaseline.cpp b/src/diffpy/srreal/PDFBaseline.cpp index 7ca11767..db394935 100644 --- a/src/diffpy/srreal/PDFBaseline.cpp +++ b/src/diffpy/srreal/PDFBaseline.cpp @@ -1,20 +1,20 @@ /***************************************************************************** -* -* 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.txt for license information. -* -****************************************************************************** -* -* class PDFBaseline -- abstract base class for PDF baseline functions -* -*****************************************************************************/ + * + * 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.txt for license information. + * + ****************************************************************************** + * + * class PDFBaseline -- abstract base class for PDF baseline functions + * + *****************************************************************************/ #include #include @@ -25,7 +25,7 @@ namespace diffpy { // Unique instantiation of the template registry base class. template class DLL_EXPORT HasClassRegistry; -} // namespace diffpy +} // namespace diffpy // Serialization ------------------------------------------------------------- diff --git a/src/diffpy/srreal/PDFBaseline.hpp b/src/diffpy/srreal/PDFBaseline.hpp index b7f21954..3d47f631 100644 --- a/src/diffpy/srreal/PDFBaseline.hpp +++ b/src/diffpy/srreal/PDFBaseline.hpp @@ -1,23 +1,23 @@ /***************************************************************************** -* -* 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.txt for license information. -* -****************************************************************************** -* -* class PDFBaseline -- abstract base class for PDF baseline functions -* A concrete instance of PDFBaseline is a functor, that calculates -* baseline value at a given pair distance r. The baseline is added to -* (R(r) * r) before multiplication by any envelope functions. -* -*****************************************************************************/ + * + * 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.txt for license information. + * + ****************************************************************************** + * + * class PDFBaseline -- abstract base class for PDF baseline functions + * A concrete instance of PDFBaseline is a functor, that calculates + * baseline value at a given pair distance r. The baseline is added to + * (R(r) * r) before multiplication by any envelope functions. + * + *****************************************************************************/ #ifndef PDFBASELINE_HPP_INCLUDED #define PDFBASELINE_HPP_INCLUDED @@ -38,28 +38,23 @@ namespace srreal { /// @class PDFBaseline /// @brief abstract base class for PDF baseline function -class DLL_EXPORT PDFBaseline : - public Attributes, - public HasClassRegistry -{ - public: - - // methods - virtual double operator()(const double& r) const = 0; - - private: - - // serialization - friend class boost::serialization::access; - template - void serialize(Archive& ar, const unsigned int version) { } - +class DLL_EXPORT PDFBaseline : public Attributes, + public HasClassRegistry { + public: + // methods + virtual double operator()(const double& r) const = 0; + + private: + // serialization + friend class boost::serialization::access; + template + void serialize(Archive& ar, const unsigned int version) {} }; typedef PDFBaseline::SharedPtr PDFBaselinePtr; -} // namespace srreal -} // namespace diffpy +} // namespace srreal +} // namespace diffpy // Serialization ------------------------------------------------------------- diff --git a/src/diffpy/srreal/PDFCalculator.cpp b/src/diffpy/srreal/PDFCalculator.cpp index 262b4cbf..4ccbf046 100644 --- a/src/diffpy/srreal/PDFCalculator.cpp +++ b/src/diffpy/srreal/PDFCalculator.cpp @@ -1,20 +1,20 @@ /***************************************************************************** -* -* 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.txt for license information. -* -****************************************************************************** -* -* class PDFCalculator -- real space PDF calculator -* -*****************************************************************************/ + * + * 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.txt for license information. + * + ****************************************************************************** + * + * class PDFCalculator -- real space PDF calculator + * + *****************************************************************************/ #include #include @@ -42,406 +42,326 @@ namespace { /// Return true if qstep value can be cheaply recomputed in initial setup. template -bool _initialQstepUpdate(const PDFC* pc) -{ - // Check if we use the initial empty structure and constructor - // has completed, i.e., the last attribute is available. - const bool rv = - (pc->getStructure() == emptyStructureAdapter()) && - pc->hasDoubleAttr("extendedrmax"); - return rv; +bool _initialQstepUpdate(const PDFC* pc) { + // Check if we use the initial empty structure and constructor + // has completed, i.e., the last attribute is available. + const bool rv = (pc->getStructure() == emptyStructureAdapter()) && + pc->hasDoubleAttr("extendedrmax"); + return rv; } -} // namespace +} // namespace // Constructor --------------------------------------------------------------- -PDFCalculator::PDFCalculator() : - mqmin(0.0), - mqmax(DOUBLE_MAX), - mrstep(DEFAULT_PDFCALCULATOR_RSTEP), - mmaxextension(DEFAULT_PDFCALCULATOR_MAXEXTENSION) -{ - // default configuration - mrmax = DEFAULT_PDFCALCULATOR_RMAX; - this->setPeakWidthModelByType("jeong"); - this->setPeakProfileByType("gaussian"); - this->getPeakProfile()->setPrecision(DEFAULT_PEAKPRECISION); - this->setBaselineByType("linear"); - this->setScatteringFactorTableByType("xray"); - // envelopes - this->addEnvelopeByType("scale"); - this->addEnvelopeByType("qresolution"); - // cache all internal data according to an empty structure. - // this rebuilds mstructure_cache and mrlimits_cache. - this->setStructure(mstructure); - // setup the OPTIMIZED evaluator last so that its validation passes. - this->setEvaluatorType(OPTIMIZED); - // attributes - this->registerDoubleAttribute("qmin", this, - &PDFCalculator::getQmin, &PDFCalculator::setQmin); - this->registerDoubleAttribute("qmax", this, - &PDFCalculator::getQmax, &PDFCalculator::setQmax); - this->registerDoubleAttribute("qstep", this, - &PDFCalculator::getQstep); - this->registerDoubleAttribute("rmin", this, - &PDFCalculator::getRmin, &PDFCalculator::setRmin); - this->registerDoubleAttribute("rmax", this, - &PDFCalculator::getRmax, &PDFCalculator::setRmax); - this->registerDoubleAttribute("rstep", this, - &PDFCalculator::getRstep, &PDFCalculator::setRstep); - this->registerDoubleAttribute("maxextension", this, - &PDFCalculator::getMaxExtension, &PDFCalculator::setMaxExtension); - this->registerDoubleAttribute("extendedrmin", this, - &PDFCalculator::getExtendedRmin); - this->registerDoubleAttribute("extendedrmax", this, - &PDFCalculator::getExtendedRmax); +PDFCalculator::PDFCalculator() + : mqmin(0.0), + mqmax(DOUBLE_MAX), + mrstep(DEFAULT_PDFCALCULATOR_RSTEP), + mmaxextension(DEFAULT_PDFCALCULATOR_MAXEXTENSION) { + // default configuration + mrmax = DEFAULT_PDFCALCULATOR_RMAX; + this->setPeakWidthModelByType("jeong"); + this->setPeakProfileByType("gaussian"); + this->getPeakProfile()->setPrecision(DEFAULT_PEAKPRECISION); + this->setBaselineByType("linear"); + this->setScatteringFactorTableByType("xray"); + // envelopes + this->addEnvelopeByType("scale"); + this->addEnvelopeByType("qresolution"); + // cache all internal data according to an empty structure. + // this rebuilds mstructure_cache and mrlimits_cache. + this->setStructure(mstructure); + // setup the OPTIMIZED evaluator last so that its validation passes. + this->setEvaluatorType(OPTIMIZED); + // attributes + this->registerDoubleAttribute("qmin", this, &PDFCalculator::getQmin, + &PDFCalculator::setQmin); + this->registerDoubleAttribute("qmax", this, &PDFCalculator::getQmax, + &PDFCalculator::setQmax); + this->registerDoubleAttribute("qstep", this, &PDFCalculator::getQstep); + this->registerDoubleAttribute("rmin", this, &PDFCalculator::getRmin, + &PDFCalculator::setRmin); + this->registerDoubleAttribute("rmax", this, &PDFCalculator::getRmax, + &PDFCalculator::setRmax); + this->registerDoubleAttribute("rstep", this, &PDFCalculator::getRstep, + &PDFCalculator::setRstep); + this->registerDoubleAttribute("maxextension", this, + &PDFCalculator::getMaxExtension, + &PDFCalculator::setMaxExtension); + this->registerDoubleAttribute("extendedrmin", this, + &PDFCalculator::getExtendedRmin); + this->registerDoubleAttribute("extendedrmax", this, + &PDFCalculator::getExtendedRmax); } // Public Methods ------------------------------------------------------------ // PairQuantity overloads -eventticker::EventTicker& PDFCalculator::ticker() const -{ - eventticker::EventTicker& tic = this->PairQuantity::ticker(); - assert(&mticker == &tic); - tic.updateFrom(this->PeakWidthModelOwner::ticker()); - tic.updateFrom(this->ScatteringFactorTableOwner::ticker()); - const PeakProfilePtr& pkpf = this->getPeakProfile(); - if (pkpf) tic.updateFrom(pkpf->ticker()); - return tic; +eventticker::EventTicker& PDFCalculator::ticker() const { + eventticker::EventTicker& tic = this->PairQuantity::ticker(); + assert(&mticker == &tic); + tic.updateFrom(this->PeakWidthModelOwner::ticker()); + tic.updateFrom(this->ScatteringFactorTableOwner::ticker()); + const PeakProfilePtr& pkpf = this->getPeakProfile(); + if (pkpf) tic.updateFrom(pkpf->ticker()); + return tic; } // results -QuantityType PDFCalculator::getPDF() const -{ - QuantityType pdf = this->getExtendedPDF(); - this->cutRipplePoints(pdf); - return pdf; -} - - -QuantityType PDFCalculator::getRDF() const -{ - QuantityType rdf = this->getExtendedRDF(); - this->cutRipplePoints(rdf); - return rdf; -} - - -QuantityType PDFCalculator::getRDFperR() const -{ - QuantityType rdfperr = this->getExtendedRDFperR(); - this->cutRipplePoints(rdfperr); - return rdfperr; -} - - -QuantityType PDFCalculator::getF() const -{ - QuantityType f_ext = this->getExtendedF(); - assert(pdfutils_qmaxSteps(this) <= int(f_ext.size())); - QuantityType rv(f_ext.begin(), f_ext.begin() + pdfutils_qmaxSteps(this)); - return rv; +QuantityType PDFCalculator::getPDF() const { + QuantityType pdf = this->getExtendedPDF(); + this->cutRipplePoints(pdf); + return pdf; } - -QuantityType PDFCalculator::getExtendedPDF() const -{ - QuantityType rgrid_ext = this->getExtendedRgrid(); - // Skip FFT when qmax is not specified and qmin does not exclude the - // the F(Q=Qstep) point (excluding F(0) == 0 makes no difference to G). - const bool skipfft = - !eps_lt(this->getQmax(), M_PI / this->getRstep()) && - !(1 < pdfutils_qminSteps(this)); - if (skipfft) - { - QuantityType rdfpr = this->getExtendedRDFperR(); - QuantityType rdfprb = this->applyBaseline(rgrid_ext, rdfpr); - QuantityType pdf = this->applyEnvelopes(rgrid_ext, rdfprb); - return pdf; - } - // FFT required here - // we need a full range PDF to apply termination ripples correctly - QuantityType f_ext = this->getExtendedF(); - // zero all F points at Q < Qmin - QuantityType::iterator ii_qmin = - f_ext.begin() + min(pdfutils_qminSteps(this), int(f_ext.size())); - fill(f_ext.begin(), ii_qmin, 0.0); - // zero all F points at Q >= Qmax - assert(pdfutils_qmaxSteps(this) <= int(f_ext.size())); - QuantityType::iterator ii_qmax = f_ext.begin() + pdfutils_qmaxSteps(this); - fill(ii_qmax, f_ext.end(), 0.0); - QuantityType pdf1 = fftftog(f_ext, this->getQstep()); - // cut away the FFT padded points - assert(this->extendedRmaxSteps() <= int(pdf1.size())); - pdf1.erase(pdf1.begin() + this->extendedRmaxSteps(), pdf1.end()); - pdf1.erase(pdf1.begin(), pdf1.begin() + this->extendedRminSteps()); - QuantityType pdf2 = this->applyEnvelopes(rgrid_ext, pdf1); - return pdf2; -} - - -QuantityType PDFCalculator::getExtendedRDF() const -{ - QuantityType rdf(this->countExtendedPoints()); - const double& totocc = mstructure_cache.totaloccupancy; - double sfavg = this->sfAverage(); - double rdf_scale = (totocc * sfavg == 0.0) ? 0.0 : - 1.0 / (totocc * sfavg * sfavg); - QuantityType::iterator iirdf = rdf.begin(); - QuantityType::const_iterator iival, iival_last; - iival = this->value().begin() + - this->extendedRminSteps() - this->rcalcloSteps(); - iival_last = this->value().begin() + - this->extendedRmaxSteps() - this->rcalcloSteps(); - assert(iival >= this->value().begin()); - assert(iival_last <= this->value().end()); - assert(rdf.size() == size_t(iival_last - iival)); - for (; iirdf != rdf.end(); ++iival, ++iirdf) - { - *iirdf = *iival * rdf_scale; - } - return rdf; +QuantityType PDFCalculator::getRDF() const { + QuantityType rdf = this->getExtendedRDF(); + this->cutRipplePoints(rdf); + return rdf; } - -QuantityType PDFCalculator::getExtendedRDFperR() const -{ - QuantityType rdf_ext = this->getExtendedRDF(); - QuantityType rgrid_ext = this->getExtendedRgrid(); - assert(rdf_ext.size() == rgrid_ext.size()); - QuantityType::const_iterator ri = rgrid_ext.begin(); - QuantityType::iterator rdfi = rdf_ext.begin(); - for (; ri != rgrid_ext.end(); ++ri, ++rdfi) - { - *rdfi = eps_gt(*ri, 0) ? (*rdfi / *ri) : 0.0; - } - return rdf_ext; +QuantityType PDFCalculator::getRDFperR() const { + QuantityType rdfperr = this->getExtendedRDFperR(); + this->cutRipplePoints(rdfperr); + return rdfperr; } - -QuantityType PDFCalculator::getExtendedF() const -{ - QuantityType rdfperr_ext = this->getExtendedRDFperR(); - QuantityType rgrid_ext = this->getExtendedRgrid(); - QuantityType rdfperr_ext1 = this->applyBaseline(rgrid_ext, rdfperr_ext); - const double rmin_ext = this->getExtendedRmin(); - QuantityType rv = fftgtof(rdfperr_ext1, this->getRstep(), rmin_ext); - assert(rv.empty() || eps_eq(M_PI, - this->getQstep() * rv.size() * this->getRstep())); - return rv; +QuantityType PDFCalculator::getF() const { + QuantityType f_ext = this->getExtendedF(); + assert(pdfutils_qmaxSteps(this) <= int(f_ext.size())); + QuantityType rv(f_ext.begin(), f_ext.begin() + pdfutils_qmaxSteps(this)); + return rv; } - -QuantityType PDFCalculator::getExtendedRgrid() const -{ - QuantityType rv; - rv.reserve(this->countExtendedPoints()); - // make sure exact value of rmin will be in the extended grid - for (int i = this->extendedRminSteps(); i < this->extendedRmaxSteps(); ++i) - { - rv.push_back(i * this->getRstep()); - } - assert(rv.empty() || !eps_lt(rv.front(), this->getExtendedRmin())); - assert(rv.empty() || !eps_gt(rv.back(), this->getExtendedRmax())); - return rv; +QuantityType PDFCalculator::getExtendedPDF() const { + QuantityType rgrid_ext = this->getExtendedRgrid(); + // Skip FFT when qmax is not specified and qmin does not exclude the + // the F(Q=Qstep) point (excluding F(0) == 0 makes no difference to G). + const bool skipfft = !eps_lt(this->getQmax(), M_PI / this->getRstep()) && + !(1 < pdfutils_qminSteps(this)); + if (skipfft) { + QuantityType rdfpr = this->getExtendedRDFperR(); + QuantityType rdfprb = this->applyBaseline(rgrid_ext, rdfpr); + QuantityType pdf = this->applyEnvelopes(rgrid_ext, rdfprb); + return pdf; + } + // FFT required here + // we need a full range PDF to apply termination ripples correctly + QuantityType f_ext = this->getExtendedF(); + // zero all F points at Q < Qmin + QuantityType::iterator ii_qmin = + f_ext.begin() + min(pdfutils_qminSteps(this), int(f_ext.size())); + fill(f_ext.begin(), ii_qmin, 0.0); + // zero all F points at Q >= Qmax + assert(pdfutils_qmaxSteps(this) <= int(f_ext.size())); + QuantityType::iterator ii_qmax = f_ext.begin() + pdfutils_qmaxSteps(this); + fill(ii_qmax, f_ext.end(), 0.0); + QuantityType pdf1 = fftftog(f_ext, this->getQstep()); + // cut away the FFT padded points + assert(this->extendedRmaxSteps() <= int(pdf1.size())); + pdf1.erase(pdf1.begin() + this->extendedRmaxSteps(), pdf1.end()); + pdf1.erase(pdf1.begin(), pdf1.begin() + this->extendedRminSteps()); + QuantityType pdf2 = this->applyEnvelopes(rgrid_ext, pdf1); + return pdf2; +} + +QuantityType PDFCalculator::getExtendedRDF() const { + QuantityType rdf(this->countExtendedPoints()); + const double& totocc = mstructure_cache.totaloccupancy; + double sfavg = this->sfAverage(); + double rdf_scale = + (totocc * sfavg == 0.0) ? 0.0 : 1.0 / (totocc * sfavg * sfavg); + QuantityType::iterator iirdf = rdf.begin(); + QuantityType::const_iterator iival, iival_last; + iival = + this->value().begin() + this->extendedRminSteps() - this->rcalcloSteps(); + iival_last = + this->value().begin() + this->extendedRmaxSteps() - this->rcalcloSteps(); + assert(iival >= this->value().begin()); + assert(iival_last <= this->value().end()); + assert(rdf.size() == size_t(iival_last - iival)); + for (; iirdf != rdf.end(); ++iival, ++iirdf) { + *iirdf = *iival * rdf_scale; + } + return rdf; +} + +QuantityType PDFCalculator::getExtendedRDFperR() const { + QuantityType rdf_ext = this->getExtendedRDF(); + QuantityType rgrid_ext = this->getExtendedRgrid(); + assert(rdf_ext.size() == rgrid_ext.size()); + QuantityType::const_iterator ri = rgrid_ext.begin(); + QuantityType::iterator rdfi = rdf_ext.begin(); + for (; ri != rgrid_ext.end(); ++ri, ++rdfi) { + *rdfi = eps_gt(*ri, 0) ? (*rdfi / *ri) : 0.0; + } + return rdf_ext; +} + +QuantityType PDFCalculator::getExtendedF() const { + QuantityType rdfperr_ext = this->getExtendedRDFperR(); + QuantityType rgrid_ext = this->getExtendedRgrid(); + QuantityType rdfperr_ext1 = this->applyBaseline(rgrid_ext, rdfperr_ext); + const double rmin_ext = this->getExtendedRmin(); + QuantityType rv = fftgtof(rdfperr_ext1, this->getRstep(), rmin_ext); + assert(rv.empty() || + eps_eq(M_PI, this->getQstep() * rv.size() * this->getRstep())); + return rv; +} + +QuantityType PDFCalculator::getExtendedRgrid() const { + QuantityType rv; + rv.reserve(this->countExtendedPoints()); + // make sure exact value of rmin will be in the extended grid + for (int i = this->extendedRminSteps(); i < this->extendedRmaxSteps(); ++i) { + rv.push_back(i * this->getRstep()); + } + assert(rv.empty() || !eps_lt(rv.front(), this->getExtendedRmin())); + assert(rv.empty() || !eps_gt(rv.back(), this->getExtendedRmax())); + return rv; } // Q-range methods -QuantityType PDFCalculator::getQgrid() const -{ - return pdfutils_getQgrid(this); -} +QuantityType PDFCalculator::getQgrid() const { return pdfutils_getQgrid(this); } // Q-range configuration -void PDFCalculator::setQmin(double qmin) -{ - ensureNonNegative("Qmin", qmin); - mqmin = qmin; +void PDFCalculator::setQmin(double qmin) { + ensureNonNegative("Qmin", qmin); + mqmin = qmin; } +const double& PDFCalculator::getQmin() const { return mqmin; } -const double& PDFCalculator::getQmin() const -{ - return mqmin; +void PDFCalculator::setQmax(double qmax) { + ensureNonNegative("Qmax", qmax); + double qmax1 = (qmax > 0.0) ? qmax : DOUBLE_MAX; + if (qmax1 < mqmax) mticker.click(); + mqmax = qmax1; + if (_initialQstepUpdate(this)) this->resetValue(); } - -void PDFCalculator::setQmax(double qmax) -{ - ensureNonNegative("Qmax", qmax); - double qmax1 = (qmax > 0.0) ? qmax : DOUBLE_MAX; - if (qmax1 < mqmax) mticker.click(); - mqmax = qmax1; - if (_initialQstepUpdate(this)) this->resetValue(); +const double& PDFCalculator::getQmax() const { + static double rv; + rv = min(mqmax, M_PI / this->getRstep()); + return rv; } - -const double& PDFCalculator::getQmax() const -{ - static double rv; - rv = min(mqmax, M_PI / this->getRstep()); - return rv; -} - - -const double& PDFCalculator::getQstep() const -{ - static double rv; - // replicate the zero padding as done in fftgtof - int Npad1 = this->extendedRmaxSteps(); - int Npad2 = (Npad1 > 0) ? (1 << int(ceil(log2(Npad1)))) : 0; - rv = (Npad2 > 0) ? M_PI / (Npad2 * this->getRstep()) : 0.0; - return rv; +const double& PDFCalculator::getQstep() const { + static double rv; + // replicate the zero padding as done in fftgtof + int Npad1 = this->extendedRmaxSteps(); + int Npad2 = (Npad1 > 0) ? (1 << int(ceil(log2(Npad1)))) : 0; + rv = (Npad2 > 0) ? M_PI / (Npad2 * this->getRstep()) : 0.0; + return rv; } // R-range methods -QuantityType PDFCalculator::getRgrid() const -{ - return pdfutils_getRgrid(this); -} +QuantityType PDFCalculator::getRgrid() const { return pdfutils_getRgrid(this); } // R-range configuration -void PDFCalculator::setRmin(double rmin) -{ - ensureNonNegative("Rmin", rmin); - this->PairQuantity::setRmin(rmin); -} - - -void PDFCalculator::setRmax(double rmax) -{ - ensureNonNegative("Rmax", rmax); - this->PairQuantity::setRmax(rmax); - if (_initialQstepUpdate(this)) this->resetValue(); -} - - -void PDFCalculator::setRstep(double rstep) -{ - ensureEpsilonPositive("Rstep", rstep); - if (mrstep != rstep) mticker.click(); - mrstep = rstep; - if (_initialQstepUpdate(this)) this->resetValue(); +void PDFCalculator::setRmin(double rmin) { + ensureNonNegative("Rmin", rmin); + this->PairQuantity::setRmin(rmin); } - -const double& PDFCalculator::getRstep() const -{ - return mrstep; +void PDFCalculator::setRmax(double rmax) { + ensureNonNegative("Rmax", rmax); + this->PairQuantity::setRmax(rmax); + if (_initialQstepUpdate(this)) this->resetValue(); } - -void PDFCalculator::setMaxExtension(double maxextension) -{ - ensureNonNegative("maxextension", maxextension); - if (mmaxextension != maxextension) mticker.click(); - mmaxextension = maxextension; +void PDFCalculator::setRstep(double rstep) { + ensureEpsilonPositive("Rstep", rstep); + if (mrstep != rstep) mticker.click(); + mrstep = rstep; + if (_initialQstepUpdate(this)) this->resetValue(); } +const double& PDFCalculator::getRstep() const { return mrstep; } -const double& PDFCalculator::getMaxExtension() const -{ - return mmaxextension; +void PDFCalculator::setMaxExtension(double maxextension) { + ensureNonNegative("maxextension", maxextension); + if (mmaxextension != maxextension) mticker.click(); + mmaxextension = maxextension; } +const double& PDFCalculator::getMaxExtension() const { return mmaxextension; } -double PDFCalculator::getExtendedRmin() const -{ - double rv = this->extendedRminSteps() * this->getRstep(); - return rv; +double PDFCalculator::getExtendedRmin() const { + double rv = this->extendedRminSteps() * this->getRstep(); + return rv; } - -double PDFCalculator::getExtendedRmax() const -{ - double rv = this->extendedRmaxSteps() * this->getRstep(); - return rv; +double PDFCalculator::getExtendedRmax() const { + double rv = this->extendedRmaxSteps() * this->getRstep(); + return rv; } // PDF peak profile configuration -void PDFCalculator::setPeakProfile(PeakProfilePtr pkf) -{ - ensureNonNull("PeakProfile", pkf); - if (mpeakprofile != pkf) mticker.click(); - mpeakprofile = pkf; +void PDFCalculator::setPeakProfile(PeakProfilePtr pkf) { + ensureNonNull("PeakProfile", pkf); + if (mpeakprofile != pkf) mticker.click(); + mpeakprofile = pkf; } - -void PDFCalculator::setPeakProfileByType(const string& tp) -{ - PeakProfilePtr pkf = PeakProfile::createByType(tp); - if (mpeakprofile.get()) - { - pkf->setPrecision(mpeakprofile->getPrecision()); - } - this->setPeakProfile(pkf); +void PDFCalculator::setPeakProfileByType(const string& tp) { + PeakProfilePtr pkf = PeakProfile::createByType(tp); + if (mpeakprofile.get()) { + pkf->setPrecision(mpeakprofile->getPrecision()); + } + this->setPeakProfile(pkf); } - -PeakProfilePtr& PDFCalculator::getPeakProfile() -{ - assert(mpeakprofile.get()); - return mpeakprofile; +PeakProfilePtr& PDFCalculator::getPeakProfile() { + assert(mpeakprofile.get()); + return mpeakprofile; } - -const PeakProfilePtr& PDFCalculator::getPeakProfile() const -{ - assert(mpeakprofile.get()); - return mpeakprofile; +const PeakProfilePtr& PDFCalculator::getPeakProfile() const { + assert(mpeakprofile.get()); + return mpeakprofile; } // PDF baseline methods -QuantityType PDFCalculator::applyBaseline( - const QuantityType& x, const QuantityType& y) const -{ - assert(x.size() == y.size()); - QuantityType z = y; - const PDFBaseline& baseline = *(this->getBaseline()); - QuantityType::const_iterator xi = x.begin(); - QuantityType::iterator zi = z.begin(); - for (; xi != x.end(); ++xi, ++zi) - { - *zi += baseline(*xi); - } - return z; +QuantityType PDFCalculator::applyBaseline(const QuantityType& x, + const QuantityType& y) const { + assert(x.size() == y.size()); + QuantityType z = y; + const PDFBaseline& baseline = *(this->getBaseline()); + QuantityType::const_iterator xi = x.begin(); + QuantityType::iterator zi = z.begin(); + for (; xi != x.end(); ++xi, ++zi) { + *zi += baseline(*xi); + } + return z; } - -void PDFCalculator::setBaseline(PDFBaselinePtr baseline) -{ - ensureNonNull("PDFBaseline", baseline); - mbaseline = baseline; +void PDFCalculator::setBaseline(PDFBaselinePtr baseline) { + ensureNonNull("PDFBaseline", baseline); + mbaseline = baseline; } - -void PDFCalculator::setBaselineByType(const std::string& tp) -{ - mbaseline = PDFBaseline::createByType(tp); +void PDFCalculator::setBaselineByType(const std::string& tp) { + mbaseline = PDFBaseline::createByType(tp); } - -PDFBaselinePtr& PDFCalculator::getBaseline() -{ - assert(mbaseline.get()); - return mbaseline; +PDFBaselinePtr& PDFCalculator::getBaseline() { + assert(mbaseline.get()); + return mbaseline; } - -const PDFBaselinePtr& PDFCalculator::getBaseline() const -{ - assert(mbaseline.get()); - return mbaseline; +const PDFBaselinePtr& PDFCalculator::getBaseline() const { + assert(mbaseline.get()); + return mbaseline; } // Protected Methods --------------------------------------------------------- @@ -451,310 +371,257 @@ const PDFBaselinePtr& PDFCalculator::getBaseline() const namespace { template -void pdfcalc_accept(T* obj, diffpy::BaseAttributesVisitor& v) -{ - obj->getPeakWidthModel()->accept(v); - obj->getPeakProfile()->accept(v); - obj->getBaseline()->accept(v); - // PDF envelopes - set evnames = obj->usedEnvelopeTypes(); - set::const_iterator nm = evnames.begin(); - for (; nm != evnames.end(); ++nm) - { - obj->getEnvelopeByType(*nm)->accept(v); - } - // finally call standard accept - obj->diffpy::Attributes::accept(v); +void pdfcalc_accept(T* obj, diffpy::BaseAttributesVisitor& v) { + obj->getPeakWidthModel()->accept(v); + obj->getPeakProfile()->accept(v); + obj->getBaseline()->accept(v); + // PDF envelopes + set evnames = obj->usedEnvelopeTypes(); + set::const_iterator nm = evnames.begin(); + for (; nm != evnames.end(); ++nm) { + obj->getEnvelopeByType(*nm)->accept(v); + } + // finally call standard accept + obj->diffpy::Attributes::accept(v); } -} // namespace +} // namespace - -void PDFCalculator::accept(diffpy::BaseAttributesVisitor& v) -{ - pdfcalc_accept(this, v); +void PDFCalculator::accept(diffpy::BaseAttributesVisitor& v) { + pdfcalc_accept(this, v); } - -void PDFCalculator::accept(diffpy::BaseAttributesVisitor& v) const -{ - pdfcalc_accept(this, v); +void PDFCalculator::accept(diffpy::BaseAttributesVisitor& v) const { + pdfcalc_accept(this, v); } // PairQuantity overloads -void PDFCalculator::resetValue() -{ - // calcPoints requires that structure and rlimits data are cached. - this->cacheStructureData(); - this->cacheRlimitsData(); - // when applicable, configure linear baseline - if (this->getBaseline()->type() == "linear") - { - double partialpdfscale = - (0.0 == mstructure_cache.totaloccupancy) ? 0.0 : - mstructure_cache.activeoccupancy / mstructure_cache.totaloccupancy; - double pnumdensity = partialpdfscale * mstructure->numberDensity(); - PDFBaseline& bl = *(this->getBaseline()); - bl.setDoubleAttr("slope", -4 * M_PI * pnumdensity); - } - this->resizeValue(this->countCalcPoints()); - this->PairQuantity::resetValue(); -} - - -void PDFCalculator::configureBondGenerator(BaseBondGenerator& bnds) const -{ - bnds.setRmin(this->rcalclo()); - bnds.setRmax(this->rcalchi()); +void PDFCalculator::resetValue() { + // calcPoints requires that structure and rlimits data are cached. + this->cacheStructureData(); + this->cacheRlimitsData(); + // when applicable, configure linear baseline + if (this->getBaseline()->type() == "linear") { + double partialpdfscale = + (0.0 == mstructure_cache.totaloccupancy) + ? 0.0 + : mstructure_cache.activeoccupancy / mstructure_cache.totaloccupancy; + double pnumdensity = partialpdfscale * mstructure->numberDensity(); + PDFBaseline& bl = *(this->getBaseline()); + bl.setDoubleAttr("slope", -4 * M_PI * pnumdensity); + } + this->resizeValue(this->countCalcPoints()); + this->PairQuantity::resetValue(); +} + +void PDFCalculator::configureBondGenerator(BaseBondGenerator& bnds) const { + bnds.setRmin(this->rcalclo()); + bnds.setRmax(this->rcalchi()); } - void PDFCalculator::addPairContribution(const BaseBondGenerator& bnds, - int summationscale) -{ - double sfprod = this->sfSite(bnds.site0()) * this->sfSite(bnds.site1()); - double peakscale = sfprod * bnds.multiplicity() * summationscale; - double fwhm = this->getPeakWidthModel()->calculate(bnds); - const PeakProfile& pkf = *(this->getPeakProfile()); - double dist = bnds.distance(); - double xlo = dist + pkf.xboundlo(fwhm); - double xhi = dist + pkf.xboundhi(fwhm); - int i = max(0, this->calcIndex(xlo)); - int ilast = min(this->countCalcPoints(), this->calcIndex(xhi) + 1); - assert(ilast <= int(mvalue.size())); - assert(eps_gt(dist, 0.0)); - for (; i < ilast; ++i) - { - double x = (this->rcalcloSteps() + i) * this->getRstep() - dist; - double y = pkf(x, fwhm); - // Contributions in G(r) need to be normalized by pair distance, - // not by r as done in PDFfit or PDFfit2. Here we rescale RDF - // in such way that division by r will give a correct result. - double yrdf = y * (x / dist + 1); - mvalue[i] += peakscale * yrdf; - } -} - - -void PDFCalculator::stashPartialValue() -{ - mstashedvalue.value = this->value(); - mstashedvalue.rclosteps = this->rcalcloSteps(); -} - - -void PDFCalculator::restorePartialValue() -{ - assert(!mstashedvalue.value.empty()); - assert(!mvalue.empty()); - QuantityType::const_iterator si = mstashedvalue.value.begin(); - QuantityType::const_iterator slast = mstashedvalue.value.end(); - QuantityType::iterator ti = mvalue.begin(); - QuantityType::iterator tlast = mvalue.end(); - int leftshift = this->rcalcloSteps() - mstashedvalue.rclosteps; - int sz = mstashedvalue.value.size(); - if (leftshift >= 0) si += min(leftshift, sz); - else ti += min(-leftshift, int(mvalue.size())); - for (; si != slast && ti != tlast; ++si, ++ti) *ti = *si; - mstashedvalue.value.clear(); + int summationscale) { + double sfprod = this->sfSite(bnds.site0()) * this->sfSite(bnds.site1()); + double peakscale = sfprod * bnds.multiplicity() * summationscale; + double fwhm = this->getPeakWidthModel()->calculate(bnds); + const PeakProfile& pkf = *(this->getPeakProfile()); + double dist = bnds.distance(); + double xlo = dist + pkf.xboundlo(fwhm); + double xhi = dist + pkf.xboundhi(fwhm); + int i = max(0, this->calcIndex(xlo)); + int ilast = min(this->countCalcPoints(), this->calcIndex(xhi) + 1); + assert(ilast <= int(mvalue.size())); + assert(eps_gt(dist, 0.0)); + for (; i < ilast; ++i) { + double x = (this->rcalcloSteps() + i) * this->getRstep() - dist; + double y = pkf(x, fwhm); + // Contributions in G(r) need to be normalized by pair distance, + // not by r as done in PDFfit or PDFfit2. Here we rescale RDF + // in such way that division by r will give a correct result. + double yrdf = y * (x / dist + 1); + mvalue[i] += peakscale * yrdf; + } +} + +void PDFCalculator::stashPartialValue() { + mstashedvalue.value = this->value(); + mstashedvalue.rclosteps = this->rcalcloSteps(); +} + +void PDFCalculator::restorePartialValue() { + assert(!mstashedvalue.value.empty()); + assert(!mvalue.empty()); + QuantityType::const_iterator si = mstashedvalue.value.begin(); + QuantityType::const_iterator slast = mstashedvalue.value.end(); + QuantityType::iterator ti = mvalue.begin(); + QuantityType::iterator tlast = mvalue.end(); + int leftshift = this->rcalcloSteps() - mstashedvalue.rclosteps; + int sz = mstashedvalue.value.size(); + if (leftshift >= 0) + si += min(leftshift, sz); + else + ti += min(-leftshift, int(mvalue.size())); + for (; si != slast && ti != tlast; ++si, ++ti) *ti = *si; + mstashedvalue.value.clear(); } // calculation specific -double PDFCalculator::rcalclo() const -{ - double rv = this->rcalcloSteps() * this->getRstep(); - return rv; +double PDFCalculator::rcalclo() const { + double rv = this->rcalcloSteps() * this->getRstep(); + return rv; } - -double PDFCalculator::rcalchi() const -{ - double rv = this->rcalchiSteps() * this->getRstep(); - return rv; +double PDFCalculator::rcalchi() const { + double rv = this->rcalchiSteps() * this->getRstep(); + return rv; } - -double PDFCalculator::extFromTerminationRipples() const -{ - // number of termination ripples for extending the r-range - const int nripples = 6; - // extension due to termination ripples. - // apply only when qmax is below the Nyquist frequency for rstep. - const double& qmax = this->getQmax(); - const double& dr = this->getRstep(); - double rv = (eps_gt(qmax, 0.0) && eps_lt(qmax, M_PI / dr)) ? - (nripples * 2 * M_PI / qmax) : 0.0; - return rv; +double PDFCalculator::extFromTerminationRipples() const { + // number of termination ripples for extending the r-range + const int nripples = 6; + // extension due to termination ripples. + // apply only when qmax is below the Nyquist frequency for rstep. + const double& qmax = this->getQmax(); + const double& dr = this->getRstep(); + double rv = (eps_gt(qmax, 0.0) && eps_lt(qmax, M_PI / dr)) + ? (nripples * 2 * M_PI / qmax) + : 0.0; + return rv; } - -double PDFCalculator::extFromPeakTails() const -{ - // assume uncorrelated neighbors with maxUii - const PeakWidthModel& pwm = *(this->getPeakWidthModel()); - double maxfwhm = pwm.maxWidth( - mstructure, this->getRmin(), this->getRmax()); - const PeakProfile& pkf = *(this->getPeakProfile()); - double xleft = fabs(pkf.xboundlo(maxfwhm)); - double xright = fabs(pkf.xboundhi(maxfwhm)); - double rv = max(xleft, xright); - return rv; +double PDFCalculator::extFromPeakTails() const { + // assume uncorrelated neighbors with maxUii + const PeakWidthModel& pwm = *(this->getPeakWidthModel()); + double maxfwhm = pwm.maxWidth(mstructure, this->getRmin(), this->getRmax()); + const PeakProfile& pkf = *(this->getPeakProfile()); + double xleft = fabs(pkf.xboundlo(maxfwhm)); + double xright = fabs(pkf.xboundhi(maxfwhm)); + double rv = max(xleft, xright); + return rv; } +int PDFCalculator::rcalcloSteps() const { return mrlimits_cache.rcalclosteps; } -int PDFCalculator::rcalcloSteps() const -{ - return mrlimits_cache.rcalclosteps; -} +int PDFCalculator::rcalchiSteps() const { return mrlimits_cache.rcalchisteps; } - -int PDFCalculator::rcalchiSteps() const -{ - return mrlimits_cache.rcalchisteps; +int PDFCalculator::extendedRminSteps() const { + return mrlimits_cache.extendedrminsteps; } - -int PDFCalculator::extendedRminSteps() const -{ - return mrlimits_cache.extendedrminsteps; +int PDFCalculator::extendedRmaxSteps() const { + return mrlimits_cache.extendedrmaxsteps; } - -int PDFCalculator::extendedRmaxSteps() const -{ - return mrlimits_cache.extendedrmaxsteps; +int PDFCalculator::countExtendedPoints() const { + int rv = this->extendedRmaxSteps() - this->extendedRminSteps(); + assert(rv >= 0); + return rv; } - -int PDFCalculator::countExtendedPoints() const -{ - int rv = this->extendedRmaxSteps() - this->extendedRminSteps(); - assert(rv >= 0); - return rv; +int PDFCalculator::countCalcPoints() const { + int rv = this->rcalchiSteps() - this->rcalcloSteps(); + assert(rv >= 0); + return rv; } - -int PDFCalculator::countCalcPoints() const -{ - int rv = this->rcalchiSteps() - this->rcalcloSteps(); - assert(rv >= 0); - return rv; +int PDFCalculator::calcIndex(double r) const { + int rv = int(floor(r / this->getRstep())) - this->rcalcloSteps(); + return rv; } - -int PDFCalculator::calcIndex(double r) const -{ - int rv = int(floor(r / this->getRstep())) - this->rcalcloSteps(); - return rv; +void PDFCalculator::cutRipplePoints(QuantityType& y) const { + if (y.empty()) return; + assert(int(y.size()) == + this->extendedRmaxSteps() - this->extendedRminSteps()); + int ncutlo = pdfutils_rminSteps(this) - this->extendedRminSteps(); + int ncuthi = this->extendedRmaxSteps() - pdfutils_rmaxSteps(this); + assert(ncutlo + ncuthi <= int(y.size())); + y.erase(y.end() - ncuthi, y.end()); + y.erase(y.begin(), y.begin() + ncutlo); } - -void PDFCalculator::cutRipplePoints(QuantityType& y) const -{ - if (y.empty()) return; - assert(int(y.size()) == - this->extendedRmaxSteps() - this->extendedRminSteps()); - int ncutlo = pdfutils_rminSteps(this) - this->extendedRminSteps(); - int ncuthi = this->extendedRmaxSteps() - pdfutils_rmaxSteps(this); - assert(ncutlo + ncuthi <= int(y.size())); - y.erase(y.end() - ncuthi, y.end()); - y.erase(y.begin(), y.begin() + ncutlo); +const double& PDFCalculator::sfSite(int siteidx) const { + assert(0 <= siteidx && siteidx < int(mstructure_cache.sfsite.size())); + return mstructure_cache.sfsite[siteidx]; } +double PDFCalculator::sfAverage() const { return mstructure_cache.sfaverage; } -const double& PDFCalculator::sfSite(int siteidx) const -{ - assert(0 <= siteidx && siteidx < int(mstructure_cache.sfsite.size())); - return mstructure_cache.sfsite[siteidx]; -} - - -double PDFCalculator::sfAverage() const -{ - return mstructure_cache.sfaverage; -} - - -void PDFCalculator::cacheStructureData() -{ - int cntsites = this->countSites(); - // sfsite - unordered_map fcache; - mstructure_cache.sfsite.resize(cntsites); - const ScatteringFactorTablePtr sftable = this->getScatteringFactorTable(); - for (int i = 0; i < cntsites; ++i) - { - const string& smbl = mstructure->siteAtomType(i); - unordered_map::iterator ff = fcache.find(smbl); - if (ff == fcache.end()) - { - const double value = sftable->lookup(smbl); - ff = fcache.insert(make_pair(smbl, value)).first; - } - mstructure_cache.sfsite[i] = ff->second * mstructure->siteOccupancy(i); - } - // sfaverage - double totocc = mstructure->totalOccupancy(); - double totsf = 0.0; - for (int i = 0; i < cntsites; ++i) - { - totsf += this->sfSite(i) * mstructure->siteMultiplicity(i); - } - mstructure_cache.sfaverage = (totocc == 0.0) ? 0.0 : (totsf / totocc); - // totaloccupancy - mstructure_cache.totaloccupancy = totocc; - // active occupancy - double invmasktotal = 0.0; - for (auto&& ij : minvertpairmask) - { - const int& i = ij.first; - const int& j = ij.second; - bool outofbounds = (i < 0 || i >= cntsites || j < 0 || j >= cntsites); - if (outofbounds) continue; - int sumscale = (i == j) ? 1 : 2; - double occij = sumscale * - mstructure->siteOccupancy(i) * mstructure->siteMultiplicity(i) * - mstructure->siteOccupancy(j) * mstructure->siteMultiplicity(j); - invmasktotal += occij; - } - if (totocc > 0.0) invmasktotal /= totocc; - mstructure_cache.activeoccupancy = (mdefaultpairmask) ? - (totocc - invmasktotal) : invmasktotal; -} - - -void PDFCalculator::cacheRlimitsData() -{ - mrlimits_cache.extendedrminsteps = 0; - mrlimits_cache.extendedrmaxsteps = 0; - mrlimits_cache.rcalclosteps = 0; - mrlimits_cache.rcalchisteps = 0; - if (pdfutils_rminSteps(this) >= pdfutils_rmaxSteps(this)) return; - // obtain extension magnitudes and rescale to fit maximum extension - double ext_ripples = this->extFromTerminationRipples(); - double ext_pktails = this->extFromPeakTails(); - double ext_total = ext_ripples + ext_pktails; - if (ext_total > this->getMaxExtension()) - { - double sc = this->getMaxExtension() / ext_total; - ext_ripples *= sc; - ext_pktails *= sc; - ext_total = this->getMaxExtension(); +void PDFCalculator::cacheStructureData() { + int cntsites = this->countSites(); + // sfsite + unordered_map fcache; + mstructure_cache.sfsite.resize(cntsites); + const ScatteringFactorTablePtr sftable = this->getScatteringFactorTable(); + for (int i = 0; i < cntsites; ++i) { + const string& smbl = mstructure->siteAtomType(i); + unordered_map::iterator ff = fcache.find(smbl); + if (ff == fcache.end()) { + const double value = sftable->lookup(smbl); + ff = fcache.insert(make_pair(smbl, value)).first; } - // abbreviations - const double& rmin = this->getRmin(); - const double& rmax = this->getRmax(); - const double& dr = this->getRstep(); - mrlimits_cache.extendedrminsteps = max(0, pdfutils_rminSteps(rmin - ext_ripples, dr)); - mrlimits_cache.extendedrmaxsteps = pdfutils_rmaxSteps(rmax + ext_ripples, dr); - mrlimits_cache.rcalclosteps = max(0, pdfutils_rminSteps(rmin - ext_total, dr)); - mrlimits_cache.rcalchisteps = pdfutils_rmaxSteps(rmax + ext_total, dr); -} - -} // namespace diffpy -} // namespace srreal + mstructure_cache.sfsite[i] = ff->second * mstructure->siteOccupancy(i); + } + // sfaverage + double totocc = mstructure->totalOccupancy(); + double totsf = 0.0; + for (int i = 0; i < cntsites; ++i) { + totsf += this->sfSite(i) * mstructure->siteMultiplicity(i); + } + mstructure_cache.sfaverage = (totocc == 0.0) ? 0.0 : (totsf / totocc); + // totaloccupancy + mstructure_cache.totaloccupancy = totocc; + // active occupancy + double invmasktotal = 0.0; + for (auto&& ij : minvertpairmask) { + const int& i = ij.first; + const int& j = ij.second; + bool outofbounds = (i < 0 || i >= cntsites || j < 0 || j >= cntsites); + if (outofbounds) continue; + int sumscale = (i == j) ? 1 : 2; + double occij = sumscale * mstructure->siteOccupancy(i) * + mstructure->siteMultiplicity(i) * + mstructure->siteOccupancy(j) * + mstructure->siteMultiplicity(j); + invmasktotal += occij; + } + if (totocc > 0.0) invmasktotal /= totocc; + mstructure_cache.activeoccupancy = + (mdefaultpairmask) ? (totocc - invmasktotal) : invmasktotal; +} + +void PDFCalculator::cacheRlimitsData() { + mrlimits_cache.extendedrminsteps = 0; + mrlimits_cache.extendedrmaxsteps = 0; + mrlimits_cache.rcalclosteps = 0; + mrlimits_cache.rcalchisteps = 0; + if (pdfutils_rminSteps(this) >= pdfutils_rmaxSteps(this)) return; + // obtain extension magnitudes and rescale to fit maximum extension + double ext_ripples = this->extFromTerminationRipples(); + double ext_pktails = this->extFromPeakTails(); + double ext_total = ext_ripples + ext_pktails; + if (ext_total > this->getMaxExtension()) { + double sc = this->getMaxExtension() / ext_total; + ext_ripples *= sc; + ext_pktails *= sc; + ext_total = this->getMaxExtension(); + } + // abbreviations + const double& rmin = this->getRmin(); + const double& rmax = this->getRmax(); + const double& dr = this->getRstep(); + mrlimits_cache.extendedrminsteps = + max(0, pdfutils_rminSteps(rmin - ext_ripples, dr)); + mrlimits_cache.extendedrmaxsteps = pdfutils_rmaxSteps(rmax + ext_ripples, dr); + mrlimits_cache.rcalclosteps = + max(0, pdfutils_rminSteps(rmin - ext_total, dr)); + mrlimits_cache.rcalchisteps = pdfutils_rmaxSteps(rmax + ext_total, dr); +} + +} // namespace srreal +} // namespace diffpy // Serialization ------------------------------------------------------------- diff --git a/src/diffpy/srreal/PDFCalculator.hpp b/src/diffpy/srreal/PDFCalculator.hpp index 6531c969..ce776f4c 100644 --- a/src/diffpy/srreal/PDFCalculator.hpp +++ b/src/diffpy/srreal/PDFCalculator.hpp @@ -1,20 +1,20 @@ /***************************************************************************** -* -* 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.txt for license information. -* -****************************************************************************** -* -* class PDFCalculator -- real space PDF calculator -* -*****************************************************************************/ + * + * 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.txt for license information. + * + ****************************************************************************** + * + * class PDFCalculator -- real space PDF calculator + * + *****************************************************************************/ #ifndef PDFCALCULATOR_HPP_INCLUDED #define PDFCALCULATOR_HPP_INCLUDED @@ -31,184 +31,179 @@ namespace diffpy { namespace srreal { -class DLL_EXPORT PDFCalculator : - public PairQuantity, - public PeakWidthModelOwner, - public ScatteringFactorTableOwner, - public PDFEnvelopeOwner -{ - public: - - // constructor - PDFCalculator(); - - // PairQuantity overloads - virtual eventticker::EventTicker& ticker() const; - - // results - QuantityType getPDF() const; - QuantityType getRDF() const; - QuantityType getRDFperR() const; - QuantityType getF() const; - - /// PDF on an r-range extended for termination ripples - QuantityType getExtendedPDF() const; - /// RDF on an r-range extended for termination ripples - QuantityType getExtendedRDF() const; - /// RDF divided by r on an r-range extended for termination ripples - QuantityType getExtendedRDFperR() const; - /// F(Q) on a zero-padded grid that reaches r-sampling Qmax = PI/dr - QuantityType getExtendedF() const; - /// r-grid extended for termination ripples - QuantityType getExtendedRgrid() const; - - // Q-range methods - QuantityType getQgrid() const; - // Q-range configuration - void setQmin(double); - const double& getQmin() const; - void setQmax(double); - const double& getQmax() const; - const double& getQstep() const; - - // R-range methods - QuantityType getRgrid() const; - // R-range configuration - virtual void setRmin(double); - virtual void setRmax(double); - void setRstep(double); - const double& getRstep() const; - /// maximum total extension of the r-range accounting for both - /// termination ripples and peak tails - void setMaxExtension(double); - /// maximum total extension of the r-range accounting for both - /// termination ripples and peak tails - const double& getMaxExtension() const; - /// lower bound for the r-range extended for termination ripples - double getExtendedRmin() const; - /// upper bound of the r-range extended for termination ripples - double getExtendedRmax() const; - - // PDF profile configuration - void setPeakProfile(PeakProfilePtr); - void setPeakProfileByType(const std::string& tp); - PeakProfilePtr& getPeakProfile(); - const PeakProfilePtr& getPeakProfile() const; - - // PDF baseline configuration - // application on an array - QuantityType applyBaseline(const QuantityType& x, const QuantityType& y) const; - void setBaseline(PDFBaselinePtr); - void setBaselineByType(const std::string& tp); - PDFBaselinePtr& getBaseline(); - const PDFBaselinePtr& getBaseline() const; - - protected: - - // Attributes overload to direct visitors around data structures - virtual void accept(diffpy::BaseAttributesVisitor& v); - virtual void accept(diffpy::BaseAttributesVisitor& v) const; - - // PairQuantity overloads - virtual void resetValue(); - virtual void configureBondGenerator(BaseBondGenerator&) const; - virtual void addPairContribution(const BaseBondGenerator&, int); - // support for PQEvaluatorOptimized - virtual void stashPartialValue(); - virtual void restorePartialValue(); - - private: - - // methods - calculation specific - /// complete lower bound extension of the calculated grid - double rcalclo() const; - /// complete upper bound extension of the calculated grid - double rcalchi() const; - /// r-range extension to allow propagation of termination ripples - double extFromTerminationRipples() const; - /// r-range extension to account for tails from out-of-range peaks - double extFromPeakTails() const; - /// number of dr steps at rcalclo from 0 - int rcalcloSteps() const; - /// number of dr steps at rhalchi from 0 - int rcalchiSteps() const; - /// number of dr steps at lower extension due to termination ripples - int extendedRminSteps() const; - /// number of dr steps at upper extension due to termination ripples - int extendedRmaxSteps() const; - /// number of points in the r-grid extended with termination ripples - int countExtendedPoints() const; - /// number of points in the complete calculated r-grid - int countCalcPoints() const; - /// index of a nearby point in the complete calculated r-grid - int calcIndex(double r) const; - /// reduce extended grid to user-requested results grid - /// by cutting away the points for termination ripples - void cutRipplePoints(QuantityType& y) const; - - // structure factors - fast lookup by site index - /// effective scattering factor at a given site scaled by occupancy - const double& sfSite(int) const; - /// average scattering factor - double sfAverage() const; - void cacheStructureData(); - void cacheRlimitsData(); - - // data - // configuration - double mqmin; - double mqmax; - double mrstep; - double mmaxextension; - PeakProfilePtr mpeakprofile; - PDFBaselinePtr mbaseline; - struct { - std::vector sfsite; - double sfaverage; - double totaloccupancy; - double activeoccupancy; - } mstructure_cache; - struct { - int extendedrminsteps; - int extendedrmaxsteps; - int rcalclosteps; - int rcalchisteps; - } mrlimits_cache; - // support for PQEvaluatorOptimized - struct { - QuantityType value; - int rclosteps; - } mstashedvalue; - // serialization - friend class boost::serialization::access; - template - void serialize(Archive& ar, const unsigned int version) - { - using boost::serialization::base_object; - ar & base_object(*this); - ar & base_object(*this); - ar & base_object(*this); - ar & base_object(*this); - ar & mqmin; - ar & mqmax; - ar & mrstep; - ar & mmaxextension; - ar & mpeakprofile; - ar & mbaseline; - ar & mstructure_cache.sfsite; - ar & mstructure_cache.sfaverage; - ar & mstructure_cache.totaloccupancy; - ar & mstructure_cache.activeoccupancy; - ar & mrlimits_cache.extendedrminsteps; - ar & mrlimits_cache.extendedrmaxsteps; - ar & mrlimits_cache.rcalclosteps; - ar & mrlimits_cache.rcalchisteps; - } +class DLL_EXPORT PDFCalculator : public PairQuantity, + public PeakWidthModelOwner, + public ScatteringFactorTableOwner, + public PDFEnvelopeOwner { + public: + // constructor + PDFCalculator(); + + // PairQuantity overloads + virtual eventticker::EventTicker& ticker() const; + + // results + QuantityType getPDF() const; + QuantityType getRDF() const; + QuantityType getRDFperR() const; + QuantityType getF() const; + + /// PDF on an r-range extended for termination ripples + QuantityType getExtendedPDF() const; + /// RDF on an r-range extended for termination ripples + QuantityType getExtendedRDF() const; + /// RDF divided by r on an r-range extended for termination ripples + QuantityType getExtendedRDFperR() const; + /// F(Q) on a zero-padded grid that reaches r-sampling Qmax = PI/dr + QuantityType getExtendedF() const; + /// r-grid extended for termination ripples + QuantityType getExtendedRgrid() const; + + // Q-range methods + QuantityType getQgrid() const; + // Q-range configuration + void setQmin(double); + const double& getQmin() const; + void setQmax(double); + const double& getQmax() const; + const double& getQstep() const; + + // R-range methods + QuantityType getRgrid() const; + // R-range configuration + virtual void setRmin(double); + virtual void setRmax(double); + void setRstep(double); + const double& getRstep() const; + /// maximum total extension of the r-range accounting for both + /// termination ripples and peak tails + void setMaxExtension(double); + /// maximum total extension of the r-range accounting for both + /// termination ripples and peak tails + const double& getMaxExtension() const; + /// lower bound for the r-range extended for termination ripples + double getExtendedRmin() const; + /// upper bound of the r-range extended for termination ripples + double getExtendedRmax() const; + + // PDF profile configuration + void setPeakProfile(PeakProfilePtr); + void setPeakProfileByType(const std::string& tp); + PeakProfilePtr& getPeakProfile(); + const PeakProfilePtr& getPeakProfile() const; + + // PDF baseline configuration + // application on an array + QuantityType applyBaseline(const QuantityType& x, + const QuantityType& y) const; + void setBaseline(PDFBaselinePtr); + void setBaselineByType(const std::string& tp); + PDFBaselinePtr& getBaseline(); + const PDFBaselinePtr& getBaseline() const; + + protected: + // Attributes overload to direct visitors around data structures + virtual void accept(diffpy::BaseAttributesVisitor& v); + virtual void accept(diffpy::BaseAttributesVisitor& v) const; + + // PairQuantity overloads + virtual void resetValue(); + virtual void configureBondGenerator(BaseBondGenerator&) const; + virtual void addPairContribution(const BaseBondGenerator&, int); + // support for PQEvaluatorOptimized + virtual void stashPartialValue(); + virtual void restorePartialValue(); + + private: + // methods - calculation specific + /// complete lower bound extension of the calculated grid + double rcalclo() const; + /// complete upper bound extension of the calculated grid + double rcalchi() const; + /// r-range extension to allow propagation of termination ripples + double extFromTerminationRipples() const; + /// r-range extension to account for tails from out-of-range peaks + double extFromPeakTails() const; + /// number of dr steps at rcalclo from 0 + int rcalcloSteps() const; + /// number of dr steps at rhalchi from 0 + int rcalchiSteps() const; + /// number of dr steps at lower extension due to termination ripples + int extendedRminSteps() const; + /// number of dr steps at upper extension due to termination ripples + int extendedRmaxSteps() const; + /// number of points in the r-grid extended with termination ripples + int countExtendedPoints() const; + /// number of points in the complete calculated r-grid + int countCalcPoints() const; + /// index of a nearby point in the complete calculated r-grid + int calcIndex(double r) const; + /// reduce extended grid to user-requested results grid + /// by cutting away the points for termination ripples + void cutRipplePoints(QuantityType& y) const; + + // structure factors - fast lookup by site index + /// effective scattering factor at a given site scaled by occupancy + const double& sfSite(int) const; + /// average scattering factor + double sfAverage() const; + void cacheStructureData(); + void cacheRlimitsData(); + + // data + // configuration + double mqmin; + double mqmax; + double mrstep; + double mmaxextension; + PeakProfilePtr mpeakprofile; + PDFBaselinePtr mbaseline; + struct { + std::vector sfsite; + double sfaverage; + double totaloccupancy; + double activeoccupancy; + } mstructure_cache; + struct { + int extendedrminsteps; + int extendedrmaxsteps; + int rcalclosteps; + int rcalchisteps; + } mrlimits_cache; + // support for PQEvaluatorOptimized + struct { + QuantityType value; + int rclosteps; + } mstashedvalue; + // serialization + friend class boost::serialization::access; + template + void serialize(Archive& ar, const unsigned int version) { + using boost::serialization::base_object; + ar& base_object(*this); + ar& base_object(*this); + ar& base_object(*this); + ar& base_object(*this); + ar & mqmin; + ar & mqmax; + ar & mrstep; + ar & mmaxextension; + ar & mpeakprofile; + ar & mbaseline; + ar & mstructure_cache.sfsite; + ar & mstructure_cache.sfaverage; + ar & mstructure_cache.totaloccupancy; + ar & mstructure_cache.activeoccupancy; + ar & mrlimits_cache.extendedrminsteps; + ar & mrlimits_cache.extendedrmaxsteps; + ar & mrlimits_cache.rcalclosteps; + ar & mrlimits_cache.rcalchisteps; + } }; // class PDFCalculator -} // namespace srreal -} // namespace diffpy +} // namespace srreal +} // namespace diffpy // Serialization ------------------------------------------------------------- diff --git a/src/diffpy/srreal/PDFEnvelope.cpp b/src/diffpy/srreal/PDFEnvelope.cpp index 2f385990..33b37a4d 100644 --- a/src/diffpy/srreal/PDFEnvelope.cpp +++ b/src/diffpy/srreal/PDFEnvelope.cpp @@ -1,20 +1,20 @@ /***************************************************************************** -* -* 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.txt for license information. -* -****************************************************************************** -* -* class PDFEnvelope -- abstract base class for PDF envelope functions -* -*****************************************************************************/ + * + * 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.txt for license information. + * + ****************************************************************************** + * + * class PDFEnvelope -- abstract base class for PDF envelope functions + * + *****************************************************************************/ #include #include @@ -39,103 +39,79 @@ namespace srreal { // public methods -QuantityType PDFEnvelopeOwner::applyEnvelopes( - const QuantityType& x, const QuantityType& y) const -{ - assert(x.size() == y.size()); - QuantityType z = y; - EnvelopeStorage::const_iterator evit; - for (evit = menvelope.begin(); evit != menvelope.end(); ++evit) - { - PDFEnvelope& fenvelope = *(evit->second); - QuantityType::const_iterator xi = x.begin(); - QuantityType::iterator zi = z.begin(); - for (; xi != x.end(); ++xi, ++zi) - { - *zi *= fenvelope(*xi); - } +QuantityType PDFEnvelopeOwner::applyEnvelopes(const QuantityType& x, + const QuantityType& y) const { + assert(x.size() == y.size()); + QuantityType z = y; + EnvelopeStorage::const_iterator evit; + for (evit = menvelope.begin(); evit != menvelope.end(); ++evit) { + PDFEnvelope& fenvelope = *(evit->second); + QuantityType::const_iterator xi = x.begin(); + QuantityType::iterator zi = z.begin(); + for (; xi != x.end(); ++xi, ++zi) { + *zi *= fenvelope(*xi); } - return z; + } + return z; } - -void PDFEnvelopeOwner::addEnvelope(PDFEnvelopePtr envlp) -{ - ensureNonNull("PDFEnvelope", envlp); - menvelope[envlp->type()] = envlp; +void PDFEnvelopeOwner::addEnvelope(PDFEnvelopePtr envlp) { + ensureNonNull("PDFEnvelope", envlp); + menvelope[envlp->type()] = envlp; } - -void PDFEnvelopeOwner::addEnvelopeByType(const string& tp) -{ - // this throws invalid_argument for invalid type - PDFEnvelopePtr envlp = PDFEnvelope::createByType(tp); - // we get here only when createByType was successful - menvelope[envlp->type()] = envlp; +void PDFEnvelopeOwner::addEnvelopeByType(const string& tp) { + // this throws invalid_argument for invalid type + PDFEnvelopePtr envlp = PDFEnvelope::createByType(tp); + // we get here only when createByType was successful + menvelope[envlp->type()] = envlp; } - -void PDFEnvelopeOwner::popEnvelope(PDFEnvelopePtr envlp) -{ - EnvelopeStorage::iterator evit = menvelope.begin(); - for (; evit != menvelope.end(); ++evit) - { - if (evit->second == envlp) - { - menvelope.erase(evit); - break; - } +void PDFEnvelopeOwner::popEnvelope(PDFEnvelopePtr envlp) { + EnvelopeStorage::iterator evit = menvelope.begin(); + for (; evit != menvelope.end(); ++evit) { + if (evit->second == envlp) { + menvelope.erase(evit); + break; } + } } - -void PDFEnvelopeOwner::popEnvelopeByType(const string& tp) -{ - menvelope.erase(tp); +void PDFEnvelopeOwner::popEnvelopeByType(const string& tp) { + menvelope.erase(tp); } - -const PDFEnvelopePtr& PDFEnvelopeOwner::getEnvelopeByType(const string& tp) const -{ - // call non-constant method - const PDFEnvelopePtr& rv = - const_cast(this)->getEnvelopeByType(tp); - return rv; +const PDFEnvelopePtr& PDFEnvelopeOwner::getEnvelopeByType( + const string& tp) const { + // call non-constant method + const PDFEnvelopePtr& rv = + const_cast(this)->getEnvelopeByType(tp); + return rv; } - -PDFEnvelopePtr& PDFEnvelopeOwner::getEnvelopeByType(const string& tp) -{ - if (!menvelope.count(tp)) - { - ostringstream emsg; - emsg << "Invalid or missing PDFEnvelope type '" << tp << "'."; - throw invalid_argument(emsg.str()); - } - PDFEnvelopePtr& rv = menvelope[tp]; - return rv; +PDFEnvelopePtr& PDFEnvelopeOwner::getEnvelopeByType(const string& tp) { + if (!menvelope.count(tp)) { + ostringstream emsg; + emsg << "Invalid or missing PDFEnvelope type '" << tp << "'."; + throw invalid_argument(emsg.str()); + } + PDFEnvelopePtr& rv = menvelope[tp]; + return rv; } - -set PDFEnvelopeOwner::usedEnvelopeTypes() const -{ - set rv; - EnvelopeStorage::const_iterator evit; - for (evit = menvelope.begin(); evit != menvelope.end(); ++evit) - { - rv.insert(rv.end(), evit->first); - } - return rv; +set PDFEnvelopeOwner::usedEnvelopeTypes() const { + set rv; + EnvelopeStorage::const_iterator evit; + for (evit = menvelope.begin(); evit != menvelope.end(); ++evit) { + rv.insert(rv.end(), evit->first); + } + return rv; } +void PDFEnvelopeOwner::clearEnvelopes() { menvelope.clear(); } -void PDFEnvelopeOwner::clearEnvelopes() -{ - menvelope.clear(); -} - -} // namespace srreal -} // namespace diffpy +} // namespace srreal +} // namespace diffpy // Serialization ------------------------------------------------------------- diff --git a/src/diffpy/srreal/PDFEnvelope.hpp b/src/diffpy/srreal/PDFEnvelope.hpp index 70b2bc0d..897db502 100644 --- a/src/diffpy/srreal/PDFEnvelope.hpp +++ b/src/diffpy/srreal/PDFEnvelope.hpp @@ -1,23 +1,23 @@ /***************************************************************************** -* -* 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.txt for license information. -* -****************************************************************************** -* -* class PDFEnvelope -- abstract base class for PDF envelope functions -* A concrete instance of PDFEnvelope is a functor, that calculates -* PDF scaling coefficients at a given pair distance r. Several functors -* can be defined and applied in PDFCalculator. -* -*****************************************************************************/ + * + * 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.txt for license information. + * + ****************************************************************************** + * + * class PDFEnvelope -- abstract base class for PDF envelope functions + * A concrete instance of PDFEnvelope is a functor, that calculates + * PDF scaling coefficients at a given pair distance r. Several functors + * can be defined and applied in PDFCalculator. + * + *****************************************************************************/ #ifndef PDFENVELOPE_HPP_INCLUDED #define PDFENVELOPE_HPP_INCLUDED @@ -40,76 +40,63 @@ namespace diffpy { namespace srreal { - /// @class PDFEnvelope /// @brief abstract base class for PDF envelope scaling function -class DLL_EXPORT PDFEnvelope : - public diffpy::Attributes, - public diffpy::HasClassRegistry -{ - public: - - // methods - virtual double operator()(const double& r) const = 0; - - private: - - // serialization - friend class boost::serialization::access; - template - void serialize(Archive& ar, const unsigned int version) - { } - +class DLL_EXPORT PDFEnvelope : public diffpy::Attributes, + public diffpy::HasClassRegistry { + public: + // methods + virtual double operator()(const double& r) const = 0; + + private: + // serialization + friend class boost::serialization::access; + template + void serialize(Archive& ar, const unsigned int version) {} }; - typedef PDFEnvelope::SharedPtr PDFEnvelopePtr; - /// @class PDFEnvelopeOwner /// @brief storage of one or more PDFEnvelope functions and their /// application to unscaled x, y arrays -class DLL_EXPORT PDFEnvelopeOwner -{ - public: - - // application on (x, y) data - QuantityType applyEnvelopes(const QuantityType& x, const QuantityType& y) const; - - // access and configuration of PDF envelope functions - // configuration of envelopes - void addEnvelope(PDFEnvelopePtr); - void addEnvelopeByType(const std::string& tp); - void popEnvelope(PDFEnvelopePtr); - void popEnvelopeByType(const std::string& tp); - const PDFEnvelopePtr& getEnvelopeByType(const std::string& tp) const; - PDFEnvelopePtr& getEnvelopeByType(const std::string& tp); - std::set usedEnvelopeTypes() const; - void clearEnvelopes(); - - private: - - // types - typedef std::map EnvelopeStorage; - - // data - EnvelopeStorage menvelope; - - // serialization - friend class boost::serialization::access; - - template - void serialize(Archive& ar, const unsigned int version) - { - ar & menvelope; - } - +class DLL_EXPORT PDFEnvelopeOwner { + public: + // application on (x, y) data + QuantityType applyEnvelopes(const QuantityType& x, + const QuantityType& y) const; + + // access and configuration of PDF envelope functions + // configuration of envelopes + void addEnvelope(PDFEnvelopePtr); + void addEnvelopeByType(const std::string& tp); + void popEnvelope(PDFEnvelopePtr); + void popEnvelopeByType(const std::string& tp); + const PDFEnvelopePtr& getEnvelopeByType(const std::string& tp) const; + PDFEnvelopePtr& getEnvelopeByType(const std::string& tp); + std::set usedEnvelopeTypes() const; + void clearEnvelopes(); + + private: + // types + typedef std::map EnvelopeStorage; + + // data + EnvelopeStorage menvelope; + + // serialization + friend class boost::serialization::access; + + template + void serialize(Archive& ar, const unsigned int version) { + ar & menvelope; + } }; -} // namespace srreal -} // namespace diffpy +} // namespace srreal +} // namespace diffpy // Serialization ------------------------------------------------------------- diff --git a/src/diffpy/srreal/PDFUtils.cpp b/src/diffpy/srreal/PDFUtils.cpp index 1a4c540b..5e282763 100644 --- a/src/diffpy/srreal/PDFUtils.cpp +++ b/src/diffpy/srreal/PDFUtils.cpp @@ -1,20 +1,20 @@ /***************************************************************************** -* -* 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.txt for license information. -* -****************************************************************************** -* -* Various common routines useful for PDF calculation. -* -*****************************************************************************/ + * + * 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.txt for license information. + * + ****************************************************************************** + * + * Various common routines useful for PDF calculation. + * + *****************************************************************************/ #include @@ -38,63 +38,56 @@ namespace { const char* EMSGFFT = "Fourier Transformation failed."; -} // namespace +} // namespace // PDFUtils functions -------------------------------------------------------- -QuantityType fftgtof(const QuantityType& g, double rstep, double rmin) -{ - if (g.empty()) return g; - int padrmin = int(round(rmin / rstep)); - int Npad1 = padrmin + g.size(); - // pad to the next power of 2 for fast Fourier transformation - int Npad2 = (1 << int(ceil(log2(Npad1)))); - // sine transformations needs an odd extension - // gpadc array has to be doubled for complex coefficients - int Npad4 = 4 * Npad2; - valarray gpadc(0.0, Npad4); - QuantityType::const_iterator gi; - // copy the original g signal - int ilo = padrmin; - for (gi = g.begin(); gi != g.end(); ++gi, ++ilo) - { - gpadc[2 * ilo] = *gi; - } - // copy the odd part of g skipping the first point, - // because it is periodic image of gpadc[0] - int ihi = 2 * Npad2 - 1; - for (ilo = 1; ilo < Npad2; ++ilo, --ihi) - { - gpadc[2 * ihi] = -1 * gpadc[2 * ilo]; - } - int status; - status = gsl_fft_complex_radix2_inverse(&(gpadc[0]), 1, 2 * Npad2); - if (status != GSL_SUCCESS) throw invalid_argument(EMSGFFT); - QuantityType f(Npad2); - for (int i = 0; i < Npad2; ++i) - { - f[i] = gpadc[2 * i + 1] * Npad2 * rstep; - } - // real components should be all close to zero - assert(fabs(valarray(gpadc[slice(0, 2 * Npad2, 2)]).min()) <= - SQRT_DOUBLE_EPS * gpadc.max()); - assert(fabs(valarray(gpadc[slice(0, 2 * Npad2, 2)]).max()) <= - SQRT_DOUBLE_EPS * gpadc.max()); - return f; +QuantityType fftgtof(const QuantityType& g, double rstep, double rmin) { + if (g.empty()) return g; + int padrmin = int(round(rmin / rstep)); + int Npad1 = padrmin + g.size(); + // pad to the next power of 2 for fast Fourier transformation + int Npad2 = (1 << int(ceil(log2(Npad1)))); + // sine transformations needs an odd extension + // gpadc array has to be doubled for complex coefficients + int Npad4 = 4 * Npad2; + valarray gpadc(0.0, Npad4); + QuantityType::const_iterator gi; + // copy the original g signal + int ilo = padrmin; + for (gi = g.begin(); gi != g.end(); ++gi, ++ilo) { + gpadc[2 * ilo] = *gi; + } + // copy the odd part of g skipping the first point, + // because it is periodic image of gpadc[0] + int ihi = 2 * Npad2 - 1; + for (ilo = 1; ilo < Npad2; ++ilo, --ihi) { + gpadc[2 * ihi] = -1 * gpadc[2 * ilo]; + } + int status; + status = gsl_fft_complex_radix2_inverse(&(gpadc[0]), 1, 2 * Npad2); + if (status != GSL_SUCCESS) throw invalid_argument(EMSGFFT); + QuantityType f(Npad2); + for (int i = 0; i < Npad2; ++i) { + f[i] = gpadc[2 * i + 1] * Npad2 * rstep; + } + // real components should be all close to zero + assert(fabs(valarray(gpadc[slice(0, 2 * Npad2, 2)]).min()) <= + SQRT_DOUBLE_EPS * gpadc.max()); + assert(fabs(valarray(gpadc[slice(0, 2 * Npad2, 2)]).max()) <= + SQRT_DOUBLE_EPS * gpadc.max()); + return f; } - -QuantityType fftftog(const QuantityType& f, double qstep, double qmin) -{ - // ftog is the same as gtof, just normalized by 2 / PI - QuantityType g = fftgtof(f, qstep, qmin); - QuantityType::iterator gi; - for (gi = g.begin(); gi != g.end(); ++gi) *gi *= 2.0 / M_PI; - return g; +QuantityType fftftog(const QuantityType& f, double qstep, double qmin) { + // ftog is the same as gtof, just normalized by 2 / PI + QuantityType g = fftgtof(f, qstep, qmin); + QuantityType::iterator gi; + for (gi = g.begin(); gi != g.end(); ++gi) *gi *= 2.0 / M_PI; + return g; } - -} // namespace srreal -} // namespace diffpy +} // namespace srreal +} // namespace diffpy // End of file diff --git a/src/diffpy/srreal/PDFUtils.hpp b/src/diffpy/srreal/PDFUtils.hpp index 4f332468..12e5efc9 100644 --- a/src/diffpy/srreal/PDFUtils.hpp +++ b/src/diffpy/srreal/PDFUtils.hpp @@ -1,23 +1,23 @@ /***************************************************************************** -* -* 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.txt for license information. -* -****************************************************************************** -* -* Various common routines useful for PDF calculation: -* meanSquareDisplacement -* maxUii -* fftftog and fftgtof -* -*****************************************************************************/ + * + * 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.txt for license information. + * + ****************************************************************************** + * + * Various common routines useful for PDF calculation: + * meanSquareDisplacement + * maxUii + * fftftog and fftgtof + * + *****************************************************************************/ #ifndef PDFUTILS_HPP_INCLUDED #define PDFUTILS_HPP_INCLUDED @@ -45,23 +45,31 @@ const double DEFAULT_QGRID_QMAX = 10.0; const double DEFAULT_QGRID_QSTEP = 0.05; /// fast Fourier transformation converting G(r) to F(Q) -DLL_EXPORT QuantityType fftgtof(const QuantityType& g, double rstep, double rmin=0.0); +DLL_EXPORT QuantityType fftgtof(const QuantityType& g, double rstep, + double rmin = 0.0); /// fast Fourier transformation converting F(Q) to G(r) -DLL_EXPORT QuantityType fftftog(const QuantityType& f, double qstep, double qmin=0.0); +DLL_EXPORT QuantityType fftftog(const QuantityType& f, double qstep, + double qmin = 0.0); /// shared methods for PDFCalculator and DebyePDFCalculator -template QuantityType pdfutils_getQgrid(const T* pdfc); -template int pdfutils_qminSteps(const T* pdfc); -template int pdfutils_qmaxSteps(const T* pdfc); -template QuantityType pdfutils_getRgrid(const T* pdfc); +template +QuantityType pdfutils_getQgrid(const T* pdfc); +template +int pdfutils_qminSteps(const T* pdfc); +template +int pdfutils_qmaxSteps(const T* pdfc); +template +QuantityType pdfutils_getRgrid(const T* pdfc); int pdfutils_rminSteps(const double& rmin, const double& rstep); -template int pdfutils_rminSteps(const T* pdfc); +template +int pdfutils_rminSteps(const T* pdfc); int pdfutils_rmaxSteps(const double& rmax, const double& rstep); -template int pdfutils_rmaxSteps(const T* pdfc); +template +int pdfutils_rmaxSteps(const T* pdfc); -} // namespace srreal -} // namespace diffpy +} // namespace srreal +} // namespace diffpy // Implementation ------------------------------------------------------------ diff --git a/src/diffpy/srreal/PQEvaluator.cpp b/src/diffpy/srreal/PQEvaluator.cpp index df3b13bd..ab66bc9b 100644 --- a/src/diffpy/srreal/PQEvaluator.cpp +++ b/src/diffpy/srreal/PQEvaluator.cpp @@ -1,25 +1,24 @@ /***************************************************************************** -* -* 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.txt for license information. -* -****************************************************************************** -* -* class PQEvaluatorBasic -- robust PairQuantity evaluator, the result -* is always calculated from scratch. -* -* class PQEvaluatorOptimized -- optimized PairQuantity evaluator with fast -* quantity updates -* -*****************************************************************************/ - + * + * 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.txt for license information. + * + ****************************************************************************** + * + * class PQEvaluatorBasic -- robust PairQuantity evaluator, the result + * is always calculated from scratch. + * + * class PQEvaluatorOptimized -- optimized PairQuantity evaluator with fast + * quantity updates + * + *****************************************************************************/ #include #include @@ -42,429 +41,340 @@ namespace { // tolerated load variance for splitting outer loop for parallel evaluation const double CPU_LOAD_VARIANCE = 0.1; -SiteIndices -complementary_indices(const int sz, const SiteIndices& indices0) -{ - SiteIndices rv; - rv.reserve(sz); - SiteIndices::const_iterator ii0 = indices0.begin(); - for (int k = 0; k < sz; ++k) - { - if (ii0 == indices0.end() || k < *ii0) - { - rv.push_back(k); - } - else - { - assert(k == *ii0); - ++ii0; - } +SiteIndices complementary_indices(const int sz, const SiteIndices& indices0) { + SiteIndices rv; + rv.reserve(sz); + SiteIndices::const_iterator ii0 = indices0.begin(); + for (int k = 0; k < sz; ++k) { + if (ii0 == indices0.end() || k < *ii0) { + rv.push_back(k); + } else { + assert(k == *ii0); + ++ii0; } - return rv; + } + return rv; } -} // namespace +} // namespace ////////////////////////////////////////////////////////////////////////////// // class PQEvaluatorBasic ////////////////////////////////////////////////////////////////////////////// -PQEvaluatorBasic::PQEvaluatorBasic() : - mconfigflags(0), - mcpuindex(0), mncpu(1), mtypeused(NONE) -{ } - - -PQEvaluatorType PQEvaluatorBasic::typeint() const -{ - return BASIC; -} - - -PQEvaluatorType PQEvaluatorBasic::typeintused() const -{ - return mtypeused; -} - - -void PQEvaluatorBasic::validate(PairQuantity& pq) const -{ - return; -} - - -void PQEvaluatorBasic::updateValue( - PairQuantity& pq, StructureAdapterPtr stru) -{ - mtypeused = BASIC; - pq.setStructure(stru); - BaseBondGeneratorPtr bnds = pq.mstructure->createBondGenerator(); - pq.configureBondGenerator(*bnds); - int cntsites = pq.mstructure->countSites(); - // loop counter - long n = mcpuindex; - // split outer loop for many atoms. The CPUs should have similar load. - bool chop_outer = (mncpu <= ((cntsites - 1) * CPU_LOAD_VARIANCE + 1)); - bool chop_inner = !chop_outer; - const bool hasmask = pq.hasMask(); - if (!this->isParallel()) chop_outer = chop_inner = false; - const bool usefullsum = this->getFlag(USEFULLSUM); - for (int i0 = 0; i0 < cntsites; ++i0) - { - if (chop_outer && (n++ % mncpu)) continue; - bnds->selectAnchorSite(i0); - int i1hi = usefullsum ? cntsites : (i0 + 1); - bnds->selectSiteRange(0, i1hi); - for (bnds->rewind(); !bnds->finished(); bnds->next()) - { - if (chop_inner && (n++ % mncpu)) continue; - int i1 = bnds->site1(); - if (hasmask && !pq.getPairMask(i0, i1)) continue; - int summationscale = (usefullsum || i0 == i1) ? 1 : 2; - pq.addPairContribution(*bnds, summationscale); - } +PQEvaluatorBasic::PQEvaluatorBasic() + : mconfigflags(0), mcpuindex(0), mncpu(1), mtypeused(NONE) {} + +PQEvaluatorType PQEvaluatorBasic::typeint() const { return BASIC; } + +PQEvaluatorType PQEvaluatorBasic::typeintused() const { return mtypeused; } + +void PQEvaluatorBasic::validate(PairQuantity& pq) const { return; } + +void PQEvaluatorBasic::updateValue(PairQuantity& pq, StructureAdapterPtr stru) { + mtypeused = BASIC; + pq.setStructure(stru); + BaseBondGeneratorPtr bnds = pq.mstructure->createBondGenerator(); + pq.configureBondGenerator(*bnds); + int cntsites = pq.mstructure->countSites(); + // loop counter + long n = mcpuindex; + // split outer loop for many atoms. The CPUs should have similar load. + bool chop_outer = (mncpu <= ((cntsites - 1) * CPU_LOAD_VARIANCE + 1)); + bool chop_inner = !chop_outer; + const bool hasmask = pq.hasMask(); + if (!this->isParallel()) chop_outer = chop_inner = false; + const bool usefullsum = this->getFlag(USEFULLSUM); + for (int i0 = 0; i0 < cntsites; ++i0) { + if (chop_outer && (n++ % mncpu)) continue; + bnds->selectAnchorSite(i0); + int i1hi = usefullsum ? cntsites : (i0 + 1); + bnds->selectSiteRange(0, i1hi); + for (bnds->rewind(); !bnds->finished(); bnds->next()) { + if (chop_inner && (n++ % mncpu)) continue; + int i1 = bnds->site1(); + if (hasmask && !pq.getPairMask(i0, i1)) continue; + int summationscale = (usefullsum || i0 == i1) ? 1 : 2; + pq.addPairContribution(*bnds, summationscale); } - mvalue_ticker.click(); + } + mvalue_ticker.click(); } - -void PQEvaluatorBasic::setFlag(PQEvaluatorFlag flag, bool value) -{ - if (value) mconfigflags |= int(flag); - else mconfigflags &= ~int(flag); +void PQEvaluatorBasic::setFlag(PQEvaluatorFlag flag, bool value) { + if (value) + mconfigflags |= int(flag); + else + mconfigflags &= ~int(flag); } - -bool PQEvaluatorBasic::getFlag(PQEvaluatorFlag flag) const -{ - return mconfigflags & int(flag); +bool PQEvaluatorBasic::getFlag(PQEvaluatorFlag flag) const { + return mconfigflags & int(flag); } - -void PQEvaluatorBasic::setupParallelRun(int cpuindex, int ncpu) -{ - // make sure ncpu is at least one - if (ncpu < 1) - { - const char* emsg = "Number of CPU ncpu must be at least 1."; - throw invalid_argument(emsg); - } - mcpuindex = cpuindex; - mncpu = ncpu; +void PQEvaluatorBasic::setupParallelRun(int cpuindex, int ncpu) { + // make sure ncpu is at least one + if (ncpu < 1) { + const char* emsg = "Number of CPU ncpu must be at least 1."; + throw invalid_argument(emsg); + } + mcpuindex = cpuindex; + mncpu = ncpu; } - -bool PQEvaluatorBasic::isParallel() const -{ - return mncpu > 1; -} +bool PQEvaluatorBasic::isParallel() const { return mncpu > 1; } ////////////////////////////////////////////////////////////////////////////// // class PQEvaluatorOptimized ////////////////////////////////////////////////////////////////////////////// -PQEvaluatorType PQEvaluatorOptimized::typeint() const -{ - return OPTIMIZED; -} +PQEvaluatorType PQEvaluatorOptimized::typeint() const { return OPTIMIZED; } - -void PQEvaluatorOptimized::validate(PairQuantity& pq) const -{ - // Check if PairQuantity provides stashPartialValue. - pq.stashPartialValue(); - pq.restorePartialValue(); +void PQEvaluatorOptimized::validate(PairQuantity& pq) const { + // Check if PairQuantity provides stashPartialValue. + pq.stashPartialValue(); + pq.restorePartialValue(); } - -void PQEvaluatorOptimized::updateValue( - PairQuantity& pq, StructureAdapterPtr stru) -{ - mtypeused = OPTIMIZED; - // revert to normal calculation if there is no structure or - // if PairQuantity uses mask - if (pq.ticker() >= mvalue_ticker || !mlast_structure) - { - return this->updateValueCompletely(pq, stru); +void PQEvaluatorOptimized::updateValue(PairQuantity& pq, + StructureAdapterPtr stru) { + mtypeused = OPTIMIZED; + // revert to normal calculation if there is no structure or + // if PairQuantity uses mask + if (pq.ticker() >= mvalue_ticker || !mlast_structure) { + return this->updateValueCompletely(pq, stru); + } + // do not do fast updates if they take more work + StructureDifference sd = mlast_structure->diff(stru); + if (!sd.allowsfastupdate()) { + return this->updateValueCompletely(pq, stru); + } + if ((this->getFlag(FIXEDSITEINDEX) || pq.hasPairMask()) && + sd.diffmethod != StructureDifference::Method::SIDEBYSIDE) { + return this->updateValueCompletely(pq, stru); + } + // Remove contributions from the extra sites in the old structure + assert(sd.stru0 == mlast_structure); + int cntsites0 = sd.stru0->countSites(); + BaseBondGeneratorPtr bnds0 = sd.stru0->createBondGenerator(); + pq.configureBondGenerator(*bnds0); + // loop counter + long n = mcpuindex; + bool usefullsum = this->getFlag(USEFULLSUM); + // the loop is adjusted according to usefullsum and split within + // the outer loop in case of parallel evaluation. + SiteIndices anchors = sd.pop0; + SiteIndices unchanged; + if (!sd.pop0.empty()) { + unchanged = complementary_indices(cntsites0, sd.pop0); + anchors.insert(anchors.end(), unchanged.begin(), unchanged.end()); + } + bnds0->selectSites(anchors.begin(), anchors.end()); + SiteIndices::const_iterator last_anchor = + usefullsum ? anchors.end() : (anchors.begin() + sd.pop0.size()); + SiteIndices::const_iterator ii0; + bool needsreselection = usefullsum; + const bool hasmask = pq.hasMask(); + for (ii0 = anchors.begin(); ii0 != last_anchor; ++ii0) { + if (n++ % mncpu) continue; + const int& i0 = *ii0; + bnds0->selectAnchorSite(i0); + // when using half sum, deselect visited popped sites + if (!usefullsum) bnds0->selectSites(ii0, anchors.end()); + // when using full sum, select only the popped sites when + // anchored at an unchanged atom + else if (needsreselection && ii0 >= (anchors.begin() + sd.pop0.size())) { + bnds0->selectSites(sd.pop0.begin(), sd.pop0.end()); + needsreselection = false; } - // do not do fast updates if they take more work - StructureDifference sd = mlast_structure->diff(stru); - if (!sd.allowsfastupdate()) - { - return this->updateValueCompletely(pq, stru); + for (bnds0->rewind(); !bnds0->finished(); bnds0->next()) { + int i1 = bnds0->site1(); + if (hasmask && !pq.getPairMask(i0, i1)) continue; + const int summationscale = (usefullsum || i0 == i1) ? -1 : -2; + pq.addPairContribution(*bnds0, summationscale); } - if ((this->getFlag(FIXEDSITEINDEX) || pq.hasPairMask()) && - sd.diffmethod != StructureDifference::Method::SIDEBYSIDE) - { - return this->updateValueCompletely(pq, stru); + } + // Add contributions from the new atoms in the updated structure + // save current value to override the resetValue call from setStructure + assert(sd.stru1); + pq.stashPartialValue(); + // setStructure(stru1) calls stru1->customPQConfig(pq), which may totally + // change pq configuration. If so, revert to full calculation. + assert(pq.ticker() < mvalue_ticker); + pq.setStructure(sd.stru1); + if (pq.ticker() >= mvalue_ticker) { + return this->updateValueCompletely(pq, stru); + } + pq.restorePartialValue(); + int cntsites1 = sd.stru1->countSites(); + BaseBondGeneratorPtr bnds1 = sd.stru1->createBondGenerator(); + pq.configureBondGenerator(*bnds1); + anchors = sd.add1; + unchanged.clear(); + if (!sd.add1.empty()) { + unchanged = complementary_indices(cntsites1, sd.add1); + anchors.insert(anchors.begin(), unchanged.begin(), unchanged.end()); + } + bnds1->selectSites(sd.add1.begin(), sd.add1.end()); + SiteIndices::const_iterator first_anchor = + usefullsum ? anchors.begin() : (anchors.end() - sd.add1.size()); + SiteIndices::const_iterator ii1; + needsreselection = usefullsum; + for (ii1 = first_anchor; ii1 != anchors.end(); ++ii1) { + if (n++ % mncpu) continue; + const int& i0 = *ii1; + bnds1->selectAnchorSite(i0); + // when using half sum, activate the added site + if (!usefullsum) bnds1->selectSites(anchors.begin(), ii1 + 1); + // when using full sum select unchanged atoms once anchored + // at an added atom. + else if (needsreselection && ii1 >= (anchors.end() - sd.add1.size())) { + bnds1->selectSites(anchors.begin(), anchors.end()); + needsreselection = false; } - // Remove contributions from the extra sites in the old structure - assert(sd.stru0 == mlast_structure); - int cntsites0 = sd.stru0->countSites(); - BaseBondGeneratorPtr bnds0 = sd.stru0->createBondGenerator(); - pq.configureBondGenerator(*bnds0); - // loop counter - long n = mcpuindex; - bool usefullsum = this->getFlag(USEFULLSUM); - // the loop is adjusted according to usefullsum and split within - // the outer loop in case of parallel evaluation. - SiteIndices anchors = sd.pop0; - SiteIndices unchanged; - if (!sd.pop0.empty()) - { - unchanged = complementary_indices(cntsites0, sd.pop0); - anchors.insert(anchors.end(), unchanged.begin(), unchanged.end()); + for (bnds1->rewind(); !bnds1->finished(); bnds1->next()) { + int i1 = bnds1->site1(); + if (hasmask && !pq.getPairMask(i0, i1)) continue; + const int summationscale = (usefullsum || i0 == i1) ? +1 : +2; + pq.addPairContribution(*bnds1, summationscale); } - bnds0->selectSites(anchors.begin(), anchors.end()); - SiteIndices::const_iterator last_anchor = usefullsum ? - anchors.end() : (anchors.begin() + sd.pop0.size()); - SiteIndices::const_iterator ii0; - bool needsreselection = usefullsum; - const bool hasmask = pq.hasMask(); - for (ii0 = anchors.begin(); ii0 != last_anchor; ++ii0) - { - if (n++ % mncpu) continue; - const int& i0 = *ii0; - bnds0->selectAnchorSite(i0); - // when using half sum, deselect visited popped sites - if (!usefullsum) bnds0->selectSites(ii0, anchors.end()); - // when using full sum, select only the popped sites when - // anchored at an unchanged atom - else if (needsreselection && ii0 >= (anchors.begin() + sd.pop0.size())) - { - bnds0->selectSites(sd.pop0.begin(), sd.pop0.end()); - needsreselection = false; - } - for (bnds0->rewind(); !bnds0->finished(); bnds0->next()) - { - int i1 = bnds0->site1(); - if (hasmask && !pq.getPairMask(i0, i1)) continue; - const int summationscale = (usefullsum || i0 == i1) ? -1 : -2; - pq.addPairContribution(*bnds0, summationscale); - } - } - // Add contributions from the new atoms in the updated structure - // save current value to override the resetValue call from setStructure - assert(sd.stru1); - pq.stashPartialValue(); - // setStructure(stru1) calls stru1->customPQConfig(pq), which may totally - // change pq configuration. If so, revert to full calculation. - assert(pq.ticker() < mvalue_ticker); - pq.setStructure(sd.stru1); - if (pq.ticker() >= mvalue_ticker) - { - return this->updateValueCompletely(pq, stru); - } - pq.restorePartialValue(); - int cntsites1 = sd.stru1->countSites(); - BaseBondGeneratorPtr bnds1 = sd.stru1->createBondGenerator(); - pq.configureBondGenerator(*bnds1); - anchors = sd.add1; - unchanged.clear(); - if (!sd.add1.empty()) - { - unchanged = complementary_indices(cntsites1, sd.add1); - anchors.insert(anchors.begin(), unchanged.begin(), unchanged.end()); - } - bnds1->selectSites(sd.add1.begin(), sd.add1.end()); - SiteIndices::const_iterator first_anchor = usefullsum ? - anchors.begin() : (anchors.end() - sd.add1.size()); - SiteIndices::const_iterator ii1; - needsreselection = usefullsum; - for (ii1 = first_anchor; ii1 != anchors.end(); ++ii1) - { - if (n++ % mncpu) continue; - const int& i0 = *ii1; - bnds1->selectAnchorSite(i0); - // when using half sum, activate the added site - if (!usefullsum) bnds1->selectSites(anchors.begin(), ii1 + 1); - // when using full sum select unchanged atoms once anchored - // at an added atom. - else if (needsreselection && ii1 >= (anchors.end() - sd.add1.size())) - { - bnds1->selectSites(anchors.begin(), anchors.end()); - needsreselection = false; - } - for (bnds1->rewind(); !bnds1->finished(); bnds1->next()) - { - int i1 = bnds1->site1(); - if (hasmask && !pq.getPairMask(i0, i1)) continue; - const int summationscale = (usefullsum || i0 == i1) ? +1 : +2; - pq.addPairContribution(*bnds1, summationscale); - } - } - mlast_structure = pq.getStructure()->clone(); - mvalue_ticker.click(); + } + mlast_structure = pq.getStructure()->clone(); + mvalue_ticker.click(); } - -void PQEvaluatorOptimized::updateValueCompletely( - PairQuantity& pq, StructureAdapterPtr stru) -{ - this->PQEvaluatorBasic::updateValue(pq, stru); - mlast_structure = pq.getStructure()->clone(); +void PQEvaluatorOptimized::updateValueCompletely(PairQuantity& pq, + StructureAdapterPtr stru) { + this->PQEvaluatorBasic::updateValue(pq, stru); + mlast_structure = pq.getStructure()->clone(); } // Helper classes and functions for PQEvaluatorCheck ------------------------- namespace { -bool cmpfabs(const double& x, const double& y) -{ - return fabs(x) < fabs(y); -} - - -class pqresults -{ - public: - - pqresults(const PairQuantity& pq) : - msaved_value(pq.value()) - { } - - - virtual ~pqresults() { } +bool cmpfabs(const double& x, const double& y) { return fabs(x) < fabs(y); } +class pqresults { + public: + pqresults(const PairQuantity& pq) : msaved_value(pq.value()) {} - virtual bool compare(const PairQuantity& pq) const - { - QuantityType::const_iterator smx = max_element( - msaved_value.begin(), msaved_value.end(), cmpfabs); - const double eps = R3::SQRT_DOUBLE_EPS * - (msaved_value.empty() ? 1.0 : max(*smx, 1.0)); - mathutils::EpsilonEqual allclose(eps); - const QuantityType& cur_value = pq.value(); - return allclose(msaved_value, cur_value); - } + virtual ~pqresults() {} - private: + virtual bool compare(const PairQuantity& pq) const { + QuantityType::const_iterator smx = + max_element(msaved_value.begin(), msaved_value.end(), cmpfabs); + const double eps = + R3::SQRT_DOUBLE_EPS * (msaved_value.empty() ? 1.0 : max(*smx, 1.0)); + mathutils::EpsilonEqual allclose(eps); + const QuantityType& cur_value = pq.value(); + return allclose(msaved_value, cur_value); + } - // data - QuantityType msaved_value; + private: + // data + QuantityType msaved_value; }; - -class pqresults_bondcalculator : public pqresults -{ - public: - - pqresults_bondcalculator(const BondCalculator& bc) : - pqresults(bc), - msaved_directions(bc.directions()), - msaved_sites0(bc.sites0()), - msaved_sites1(bc.sites1()), - msaved_types0(bc.types0()), - msaved_types1(bc.types1()) - { } - - - virtual bool compare(const PairQuantity& pq) const - { - if (!(this->pqresults::compare(pq))) return false; - const BondCalculator& bc = dynamic_cast(pq); - if (msaved_sites0 != bc.sites0()) return false; - if (msaved_sites1 != bc.sites1()) return false; - if (msaved_types0 != bc.types0()) return false; - if (msaved_types1 != bc.types1()) return false; - const vector cur_directions = bc.directions(); - const bool szneq = - msaved_directions.size() != cur_directions.size(); - if (szneq) return false; - vector::const_iterator sv, cv; - sv = msaved_directions.begin(); - cv = cur_directions.begin(); - mathutils::EpsilonEqual allclose; - for (; sv != msaved_directions.end(); ++sv, ++cv) - { - if (!allclose(*sv, *cv)) return false; - } - return true; - } - - private: - - // data - vector msaved_directions; - SiteIndices msaved_sites0; - SiteIndices msaved_sites1; - vector msaved_types0; - vector msaved_types1; +class pqresults_bondcalculator : public pqresults { + public: + pqresults_bondcalculator(const BondCalculator& bc) + : pqresults(bc), + msaved_directions(bc.directions()), + msaved_sites0(bc.sites0()), + msaved_sites1(bc.sites1()), + msaved_types0(bc.types0()), + msaved_types1(bc.types1()) {} + + virtual bool compare(const PairQuantity& pq) const { + if (!(this->pqresults::compare(pq))) return false; + const BondCalculator& bc = dynamic_cast(pq); + if (msaved_sites0 != bc.sites0()) return false; + if (msaved_sites1 != bc.sites1()) return false; + if (msaved_types0 != bc.types0()) return false; + if (msaved_types1 != bc.types1()) return false; + const vector cur_directions = bc.directions(); + const bool szneq = msaved_directions.size() != cur_directions.size(); + if (szneq) return false; + vector::const_iterator sv, cv; + sv = msaved_directions.begin(); + cv = cur_directions.begin(); + mathutils::EpsilonEqual allclose; + for (; sv != msaved_directions.end(); ++sv, ++cv) { + if (!allclose(*sv, *cv)) return false; + } + return true; + } + + private: + // data + vector msaved_directions; + SiteIndices msaved_sites0; + SiteIndices msaved_sites1; + vector msaved_types0; + vector msaved_types1; }; - -pqresults* create_pqresults(const PairQuantity& pq) -{ - const BondCalculator* pbc = dynamic_cast(&pq); - if (pbc) return new pqresults_bondcalculator(*pbc); - return new pqresults(pq); +pqresults* create_pqresults(const PairQuantity& pq) { + const BondCalculator* pbc = dynamic_cast(&pq); + if (pbc) return new pqresults_bondcalculator(*pbc); + return new pqresults(pq); } -} // namespace +} // namespace ////////////////////////////////////////////////////////////////////////////// // class PQEvaluatorCheck ////////////////////////////////////////////////////////////////////////////// -PQEvaluatorType PQEvaluatorCheck::typeint() const -{ - return CHECK; -} - - -void PQEvaluatorCheck::updateValue( - PairQuantity& pq, StructureAdapterPtr stru) -{ - this->PQEvaluatorOptimized::updateValue(pq, stru); - if (mtypeused == BASIC) return; - unique_ptr results(create_pqresults(pq)); - this->PQEvaluatorBasic::updateValue(pq, stru); - mtypeused = CHECK; - if (!results->compare(pq)) - { - const char* emsg = "Inconsistent results from OPTIMIZED evaluation."; - throw logic_error(emsg); - } +PQEvaluatorType PQEvaluatorCheck::typeint() const { return CHECK; } + +void PQEvaluatorCheck::updateValue(PairQuantity& pq, StructureAdapterPtr stru) { + this->PQEvaluatorOptimized::updateValue(pq, stru); + if (mtypeused == BASIC) return; + unique_ptr results(create_pqresults(pq)); + this->PQEvaluatorBasic::updateValue(pq, stru); + mtypeused = CHECK; + if (!results->compare(pq)) { + const char* emsg = "Inconsistent results from OPTIMIZED evaluation."; + throw logic_error(emsg); + } } // Factory for PairQuantity evaluators --------------------------------------- -PQEvaluatorPtr createPQEvaluator(PQEvaluatorType pqtp, PQEvaluatorPtr pqevsrc) -{ - PQEvaluatorPtr rv; - switch (pqtp) - { - case BASIC: - rv.reset(new PQEvaluatorBasic()); - break; - - case OPTIMIZED: - rv.reset(new PQEvaluatorOptimized()); - break; - - case CHECK: - rv.reset(new PQEvaluatorCheck()); - break; - - default: - ostringstream emsg; - emsg << "Invalid PQEvaluatorType value " << pqtp; - throw invalid_argument(emsg.str()); - } - if (pqevsrc) - { - rv->mconfigflags = pqevsrc->mconfigflags; - rv->mcpuindex = pqevsrc->mcpuindex; - rv->mncpu = pqevsrc->mncpu; - rv->mvalue_ticker = pqevsrc->mvalue_ticker; - rv->mtypeused = pqevsrc->mtypeused; - } - return rv; +PQEvaluatorPtr createPQEvaluator(PQEvaluatorType pqtp, PQEvaluatorPtr pqevsrc) { + PQEvaluatorPtr rv; + switch (pqtp) { + case BASIC: + rv.reset(new PQEvaluatorBasic()); + break; + + case OPTIMIZED: + rv.reset(new PQEvaluatorOptimized()); + break; + + case CHECK: + rv.reset(new PQEvaluatorCheck()); + break; + + default: + ostringstream emsg; + emsg << "Invalid PQEvaluatorType value " << pqtp; + throw invalid_argument(emsg.str()); + } + if (pqevsrc) { + rv->mconfigflags = pqevsrc->mconfigflags; + rv->mcpuindex = pqevsrc->mcpuindex; + rv->mncpu = pqevsrc->mncpu; + rv->mvalue_ticker = pqevsrc->mvalue_ticker; + rv->mtypeused = pqevsrc->mtypeused; + } + return rv; } - -} // namespace srreal -} // namespace diffpy +} // namespace srreal +} // namespace diffpy // Serialization ------------------------------------------------------------- diff --git a/src/diffpy/srreal/PQEvaluator.hpp b/src/diffpy/srreal/PQEvaluator.hpp index 4d53adfd..3896b701 100644 --- a/src/diffpy/srreal/PQEvaluator.hpp +++ b/src/diffpy/srreal/PQEvaluator.hpp @@ -1,25 +1,24 @@ /***************************************************************************** -* -* 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.txt for license information. -* -****************************************************************************** -* -* class PQEvaluatorBasic -- robust PairQuantity evaluator, the result -* is always calculated from scratch. -* -* class PQEvaluatorOptimized -- optimized PairQuantity evaluator with fast -* quantity updates -* -*****************************************************************************/ - + * + * 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.txt for license information. + * + ****************************************************************************** + * + * class PQEvaluatorBasic -- robust PairQuantity evaluator, the result + * is always calculated from scratch. + * + * class PQEvaluatorOptimized -- optimized PairQuantity evaluator with fast + * quantity updates + * + *****************************************************************************/ #ifndef PQEVALUATOR_HPP_INCLUDED #define PQEVALUATOR_HPP_INCLUDED @@ -44,118 +43,102 @@ class PairQuantity; typedef std::shared_ptr PQEvaluatorPtr; -enum PQEvaluatorType {NONE, BASIC, OPTIMIZED, CHECK}; +enum PQEvaluatorType { NONE, BASIC, OPTIMIZED, CHECK }; enum PQEvaluatorFlag { - // sum over full matrix of atom pairs, use pair symmetry otherwise. - USEFULLSUM = 1, - // allow fast updates only if unchanged atoms keep their indices. - FIXEDSITEINDEX = 2, + // sum over full matrix of atom pairs, use pair symmetry otherwise. + USEFULLSUM = 1, + // allow fast updates only if unchanged atoms keep their indices. + FIXEDSITEINDEX = 2, }; -class DLL_EXPORT PQEvaluatorBasic -{ - public: - - friend DLL_EXPORT - PQEvaluatorPtr createPQEvaluator(PQEvaluatorType, PQEvaluatorPtr); - // constructor - PQEvaluatorBasic(); - virtual ~PQEvaluatorBasic() { } - - // methods - virtual PQEvaluatorType typeint() const; - PQEvaluatorType typeintused() const; - virtual void updateValue(PairQuantity&, StructureAdapterPtr); - virtual void validate(PairQuantity&) const; - void setFlag(PQEvaluatorFlag flag, bool value); - bool getFlag(PQEvaluatorFlag flag) const; - void setupParallelRun(int cpuindex, int ncpu); - bool isParallel() const; - - protected: - - - // data - /// per-bit storage of boolean configuration flags - int mconfigflags; - /// zero-based index of this CPU - int mcpuindex; - /// total number of the CPU units - int mncpu; - /// ticker for recording when was the value updated - eventticker::EventTicker mvalue_ticker; - /// type of PQEvaluator that was actually used - PQEvaluatorType mtypeused; - - private: - - // serialization - friend class boost::serialization::access; - template - void serialize(Archive& ar, const unsigned int version) - { - ar & mconfigflags & mcpuindex & mncpu & mvalue_ticker; - } +class DLL_EXPORT PQEvaluatorBasic { + public: + friend DLL_EXPORT PQEvaluatorPtr createPQEvaluator(PQEvaluatorType, + PQEvaluatorPtr); + // constructor + PQEvaluatorBasic(); + virtual ~PQEvaluatorBasic() {} + + // methods + virtual PQEvaluatorType typeint() const; + PQEvaluatorType typeintused() const; + virtual void updateValue(PairQuantity&, StructureAdapterPtr); + virtual void validate(PairQuantity&) const; + void setFlag(PQEvaluatorFlag flag, bool value); + bool getFlag(PQEvaluatorFlag flag) const; + void setupParallelRun(int cpuindex, int ncpu); + bool isParallel() const; + + protected: + // data + /// per-bit storage of boolean configuration flags + int mconfigflags; + /// zero-based index of this CPU + int mcpuindex; + /// total number of the CPU units + int mncpu; + /// ticker for recording when was the value updated + eventticker::EventTicker mvalue_ticker; + /// type of PQEvaluator that was actually used + PQEvaluatorType mtypeused; + + private: + // serialization + friend class boost::serialization::access; + template + void serialize(Archive& ar, const unsigned int version) { + ar & mconfigflags & mcpuindex & mncpu & mvalue_ticker; + } }; - -class DLL_EXPORT PQEvaluatorOptimized : public PQEvaluatorBasic -{ - public: - - // methods - virtual PQEvaluatorType typeint() const; - virtual void validate(PairQuantity&) const; - virtual void updateValue(PairQuantity&, StructureAdapterPtr); - - private: - - // data - StructureAdapterPtr mlast_structure; - - // helper method - void updateValueCompletely(PairQuantity&, StructureAdapterPtr); - - // serialization - friend class boost::serialization::access; - template - void serialize(Archive& ar, const unsigned int version) - { - using boost::serialization::base_object; - ar & base_object(*this); - ar & mlast_structure; - } +class DLL_EXPORT PQEvaluatorOptimized : public PQEvaluatorBasic { + public: + // methods + virtual PQEvaluatorType typeint() const; + virtual void validate(PairQuantity&) const; + virtual void updateValue(PairQuantity&, StructureAdapterPtr); + + private: + // data + StructureAdapterPtr mlast_structure; + + // helper method + void updateValueCompletely(PairQuantity&, StructureAdapterPtr); + + // serialization + friend class boost::serialization::access; + template + void serialize(Archive& ar, const unsigned int version) { + using boost::serialization::base_object; + ar& base_object(*this); + ar & mlast_structure; + } }; - -class DLL_EXPORT PQEvaluatorCheck : public PQEvaluatorOptimized -{ - public: - - // methods - virtual PQEvaluatorType typeint() const; - virtual void updateValue(PairQuantity&, StructureAdapterPtr); - - private: - - // serialization - friend class boost::serialization::access; - template - void serialize(Archive& ar, const unsigned int version) - { - using boost::serialization::base_object; - ar & base_object(*this); - } +class DLL_EXPORT PQEvaluatorCheck : public PQEvaluatorOptimized { + public: + // methods + virtual PQEvaluatorType typeint() const; + virtual void updateValue(PairQuantity&, StructureAdapterPtr); + + private: + // serialization + friend class boost::serialization::access; + template + void serialize(Archive& ar, const unsigned int version) { + using boost::serialization::base_object; + ar& base_object(*this); + } }; // Factory function for PairQuantity evaluators ------------------------------ DLL_EXPORT PQEvaluatorPtr createPQEvaluator( - PQEvaluatorType pqtp, PQEvaluatorPtr pqevsrc=PQEvaluatorPtr()); + PQEvaluatorType pqtp, PQEvaluatorPtr pqevsrc = PQEvaluatorPtr()); -} // namespace srreal -} // namespace diffpy +} // namespace srreal +} // namespace diffpy // Serialization ------------------------------------------------------------- diff --git a/src/diffpy/srreal/PairCounter.cpp b/src/diffpy/srreal/PairCounter.cpp index 02e416be..461fe9f7 100644 --- a/src/diffpy/srreal/PairCounter.cpp +++ b/src/diffpy/srreal/PairCounter.cpp @@ -1,20 +1,20 @@ /***************************************************************************** -* -* 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.txt for license information. -* -****************************************************************************** -* -* class PairCounter -- concrete counter of pairs in a structure. -* -*****************************************************************************/ + * + * 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.txt for license information. + * + ****************************************************************************** + * + * class PairCounter -- concrete counter of pairs in a structure. + * + *****************************************************************************/ #include @@ -22,19 +22,17 @@ using namespace diffpy::srreal; // Constructor --------------------------------------------------------------- -PairCounter::PairCounter() -{ - // use very large default rmax. - this->setRmax(1000); - this->resizeValue(1); +PairCounter::PairCounter() { + // use very large default rmax. + this->setRmax(1000); + this->resizeValue(1); } // Protected Methods --------------------------------------------------------- void PairCounter::addPairContribution(const BaseBondGenerator& bnds, - int summationscale) -{ - mvalue.front() += summationscale / 2.0; + int summationscale) { + mvalue.front() += summationscale / 2.0; } // End of file diff --git a/src/diffpy/srreal/PairCounter.hpp b/src/diffpy/srreal/PairCounter.hpp index 6461bff5..86a8d136 100644 --- a/src/diffpy/srreal/PairCounter.hpp +++ b/src/diffpy/srreal/PairCounter.hpp @@ -1,20 +1,20 @@ /***************************************************************************** -* -* 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.txt for license information. -* -****************************************************************************** -* -* class PairCounter -- concrete counter of pairs in a structure. -* -*****************************************************************************/ + * + * 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.txt for license information. + * + ****************************************************************************** + * + * class PairCounter -- concrete counter of pairs in a structure. + * + *****************************************************************************/ #ifndef PAIRCOUNTER_HPP_INCLUDED #define PAIRCOUNTER_HPP_INCLUDED @@ -24,35 +24,30 @@ namespace diffpy { namespace srreal { -class PairCounter : public PairQuantity -{ - public: +class PairCounter : public PairQuantity { + public: + // constructor + PairCounter(); - // constructor - PairCounter(); - - // methods - template int operator()(const T&); - - protected: - - // methods - virtual void addPairContribution(const BaseBondGenerator&, int); + // methods + template + int operator()(const T&); + protected: + // methods + virtual void addPairContribution(const BaseBondGenerator&, int); }; // Public Template Methods --------------------------------------------------- template -int PairCounter::operator()(const T& stru) -{ - this->eval(stru); - int cnt = int(this->value().front()); - return cnt; +int PairCounter::operator()(const T& stru) { + this->eval(stru); + int cnt = int(this->value().front()); + return cnt; } - -} // namespace srreal -} // namespace diffpy +} // namespace srreal +} // namespace diffpy #endif // PAIRCOUNTER_HPP_INCLUDED diff --git a/src/diffpy/srreal/PairQuantity.cpp b/src/diffpy/srreal/PairQuantity.cpp index f31886c4..aa91aaa8 100644 --- a/src/diffpy/srreal/PairQuantity.cpp +++ b/src/diffpy/srreal/PairQuantity.cpp @@ -1,20 +1,20 @@ /***************************************************************************** -* -* libdiffpy by DANSE Diffraction group -* Simon J. L. Billinge -* (c) 2008 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.txt for license information. -* -****************************************************************************** -* -* class PairQuantity -- general implementation of pair quantity calculator -* -*****************************************************************************/ + * + * libdiffpy by DANSE Diffraction group + * Simon J. L. Billinge + * (c) 2008 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.txt for license information. + * + ****************************************************************************** + * + * class PairQuantity -- general implementation of pair quantity calculator + * + *****************************************************************************/ #include #include @@ -36,456 +36,349 @@ const string PairQuantity::ALLATOMSSTR = "all"; // Constructor --------------------------------------------------------------- -PairQuantity::PairQuantity() : - mstructure(emptyStructureAdapter()), - mrmin(0.0), - mrmax(DEFAULT_BONDGENERATOR_RMAX), - mdefaultpairmask(true) -{ - this->setEvaluatorType(BASIC); - // attributes - this->registerDoubleAttribute("rmin", this, - &PairQuantity::getRmin, &PairQuantity::setRmin); - this->registerDoubleAttribute("rmax", this, - &PairQuantity::getRmax, &PairQuantity::setRmax); +PairQuantity::PairQuantity() + : mstructure(emptyStructureAdapter()), + mrmin(0.0), + mrmax(DEFAULT_BONDGENERATOR_RMAX), + mdefaultpairmask(true) { + this->setEvaluatorType(BASIC); + // attributes + this->registerDoubleAttribute("rmin", this, &PairQuantity::getRmin, + &PairQuantity::setRmin); + this->registerDoubleAttribute("rmax", this, &PairQuantity::getRmax, + &PairQuantity::setRmax); } // Public Methods ------------------------------------------------------------ -const QuantityType& PairQuantity::eval() -{ - return this->eval(mstructure); -} - +const QuantityType& PairQuantity::eval() { return this->eval(mstructure); } -const QuantityType& PairQuantity::eval(StructureAdapterPtr stru) -{ - mevaluator->updateValue(*this, stru); - this->finishValue(); - return this->value(); +const QuantityType& PairQuantity::eval(StructureAdapterPtr stru) { + mevaluator->updateValue(*this, stru); + this->finishValue(); + return this->value(); } - -void PairQuantity::setStructure(StructureAdapterPtr stru) -{ - mstructure = stru.get() ? stru : emptyStructureAdapter(); - mstructure->customPQConfig(this); - this->updateMaskData(); - this->resetValue(); +void PairQuantity::setStructure(StructureAdapterPtr stru) { + mstructure = stru.get() ? stru : emptyStructureAdapter(); + mstructure->customPQConfig(this); + this->updateMaskData(); + this->resetValue(); } +StructureAdapterPtr& PairQuantity::getStructure() { return mstructure; } -StructureAdapterPtr& PairQuantity::getStructure() -{ - return mstructure; +const StructureAdapterPtr& PairQuantity::getStructure() const { + return mstructure; } +const QuantityType& PairQuantity::value() const { return mvalue; } -const StructureAdapterPtr& PairQuantity::getStructure() const -{ - return mstructure; +void PairQuantity::mergeParallelData(const string& pdata, int ncpu) { + if (mmergedvaluescount >= ncpu) { + const char* emsg = "Number of merged values exceeds NCPU."; + throw runtime_error(emsg); + } + this->executeParallelMerge(pdata); + ++mmergedvaluescount; + if (mmergedvaluescount == ncpu) this->finishValue(); } - -const QuantityType& PairQuantity::value() const -{ - return mvalue; +string PairQuantity::getParallelData() const { + ostringstream storage(ios::binary); + diffpy::serialization::oarchive oa(storage, ios::binary); + oa << this->value(); + return storage.str(); } - -void PairQuantity::mergeParallelData(const string& pdata, int ncpu) -{ - if (mmergedvaluescount >= ncpu) - { - const char* emsg = "Number of merged values exceeds NCPU."; - throw runtime_error(emsg); - } - this->executeParallelMerge(pdata); - ++mmergedvaluescount; - if (mmergedvaluescount == ncpu) this->finishValue(); -} - - -string PairQuantity::getParallelData() const -{ - ostringstream storage(ios::binary); - diffpy::serialization::oarchive oa(storage, ios::binary); - oa << this->value(); - return storage.str(); +void PairQuantity::setRmin(double rmin) { + if (mrmin != rmin) mticker.click(); + mrmin = rmin; } +const double& PairQuantity::getRmin() const { return mrmin; } -void PairQuantity::setRmin(double rmin) -{ - if (mrmin != rmin) mticker.click(); - mrmin = rmin; +void PairQuantity::setRmax(double rmax) { + if (mrmax != rmax) mticker.click(); + mrmax = rmax; } +const double& PairQuantity::getRmax() const { return mrmax; } -const double& PairQuantity::getRmin() const -{ - return mrmin; +void PairQuantity::setEvaluatorType(PQEvaluatorType evtp) { + if (mevaluator.get() && mevaluator->typeint() == evtp) return; + PQEvaluatorPtr pqev = createPQEvaluator(evtp, mevaluator); + // validate the new evaluator object + try { + pqev->validate(*this); + } catch (const logic_error& e) { + string emsg("EvaluatorType not supported. "); + emsg += e.what(); + throw invalid_argument(emsg); + } + // validator is good here, use it now. + mevaluator = pqev; + this->resetValue(); } - -void PairQuantity::setRmax(double rmax) -{ - if (mrmax != rmax) mticker.click(); - mrmax = rmax; +PQEvaluatorType PairQuantity::getEvaluatorType() const { + return mevaluator->typeint(); } - -const double& PairQuantity::getRmax() const -{ - return mrmax; +PQEvaluatorType PairQuantity::getEvaluatorTypeUsed() const { + return mevaluator->typeintused(); } - -void PairQuantity::setEvaluatorType(PQEvaluatorType evtp) -{ - if (mevaluator.get() && mevaluator->typeint() == evtp) return; - PQEvaluatorPtr pqev = createPQEvaluator(evtp, mevaluator); - // validate the new evaluator object - try - { - pqev->validate(*this); - } - catch (const logic_error& e) - { - string emsg("EvaluatorType not supported. "); - emsg += e.what(); - throw invalid_argument(emsg); - } - // validator is good here, use it now. - mevaluator = pqev; - this->resetValue(); +void PairQuantity::setupParallelRun(int cpuindex, int ncpu) { + mevaluator->setupParallelRun(cpuindex, ncpu); } - -PQEvaluatorType PairQuantity::getEvaluatorType() const -{ - return mevaluator->typeint(); +void PairQuantity::maskAllPairs(bool mask) { + bool nochange = minvertpairmask.empty() && msiteallmask.empty() && + mtypemask.empty() && (mdefaultpairmask == mask); + if (!nochange) mticker.click(); + minvertpairmask.clear(); + msiteallmask.clear(); + mtypemask.clear(); + mdefaultpairmask = mask; } - -PQEvaluatorType PairQuantity::getEvaluatorTypeUsed() const -{ - return mevaluator->typeintused(); +void PairQuantity::invertMask() { + mticker.click(); + mdefaultpairmask = !mdefaultpairmask; + unordered_map::iterator mm; + for (mm = msiteallmask.begin(); mm != msiteallmask.end(); ++mm) { + mm->second = !(mm->second); + } + TypeMaskStorage::iterator tpmsk; + for (tpmsk = mtypemask.begin(); tpmsk != mtypemask.end(); ++tpmsk) { + tpmsk->second = !(tpmsk->second); + } } - -void PairQuantity::setupParallelRun(int cpuindex, int ncpu) -{ - mevaluator->setupParallelRun(cpuindex, ncpu); -} - - -void PairQuantity::maskAllPairs(bool mask) -{ - bool nochange = minvertpairmask.empty() && msiteallmask.empty() && - mtypemask.empty() && (mdefaultpairmask == mask); - if (!nochange) mticker.click(); - minvertpairmask.clear(); - msiteallmask.clear(); +void PairQuantity::setPairMask(int i, int j, bool mask) { + if (i < 0) i = ALLATOMSINT; + if (j < 0) j = ALLATOMSINT; + // short circuit for all-all + if (ALLATOMSINT == i && ALLATOMSINT == j) { + this->maskAllPairs(mask); + return; + } + bool modified = false; + // update ticker if we are switching from type-mask mode + if (!mtypemask.empty()) { mtypemask.clear(); - mdefaultpairmask = mask; -} - - -void PairQuantity::invertMask() -{ - mticker.click(); - mdefaultpairmask = !mdefaultpairmask; - unordered_map::iterator mm; - for (mm = msiteallmask.begin(); mm != msiteallmask.end(); ++mm) - { - mm->second = !(mm->second); + modified = true; + } + // handle one ALLATOMSINT argument + if (ALLATOMSINT == i || ALLATOMSINT == j) { + int k = (ALLATOMSINT != i) ? i : j; + pair::iterator, bool> pmm; + pmm = msiteallmask.emplace(k, mask); + if (pmm.second || pmm.first->second != mask) modified = true; + pmm.first->second = mask; + int cntsites = this->countSites(); + for (int l = 0; l < cntsites; ++l) { + this->setPairMaskValue(k, l, mask); } + if (modified) mticker.click(); + return; + } + // here neither i nor j is ALLATOMSINT + unordered_map::iterator ii, jj; + ii = msiteallmask.find(i); + if (ii != msiteallmask.end() && mask != ii->second) { + msiteallmask.erase(ii); + modified = true; + } + jj = msiteallmask.find(j); + if (jj != msiteallmask.end() && mask != jj->second) { + msiteallmask.erase(jj); + modified = true; + } + modified = this->setPairMaskValue(i, j, mask) ? true : modified; + if (modified) mticker.click(); +} + +bool PairQuantity::getPairMask(int i, int j) const { + pair ij = (i > j) ? make_pair(j, i) : make_pair(i, j); + bool rv = minvertpairmask.count(ij) ? !mdefaultpairmask : mdefaultpairmask; + return rv; +} + +void PairQuantity::setTypeMask(string smbli, string smblj, bool mask) { + static string upcaseall; + if (upcaseall.empty()) { + upcaseall = ALLATOMSSTR; + transform(upcaseall.begin(), upcaseall.end(), upcaseall.begin(), ::toupper); + } + // accept "ALL" (upper ALLATOMSSTR) for smbli and smblj + if (upcaseall == smbli) smbli = ALLATOMSSTR; + if (upcaseall == smblj) smblj = ALLATOMSSTR; + pair allall(ALLATOMSSTR, ALLATOMSSTR); + pair smblij = + (smbli > smblj) ? make_pair(smblj, smbli) : make_pair(smbli, smblj); + // short circuit for all-all + if (allall == smblij) { + this->maskAllPairs(mask); + return; + } + // msiteallmask is only relevant for index-based masking. + // clean it up to avoid unexpected effects when switching to index masks. + msiteallmask.clear(); + bool modified = false; + // when all is used, remove all typemask elements with the other type + if (ALLATOMSSTR == smbli || ALLATOMSSTR == smblj) { + const string& sk = (ALLATOMSSTR != smbli) ? smbli : smblj; TypeMaskStorage::iterator tpmsk; - for (tpmsk = mtypemask.begin(); tpmsk != mtypemask.end(); ++tpmsk) - { - tpmsk->second = !(tpmsk->second); - } -} - - -void PairQuantity::setPairMask(int i, int j, bool mask) -{ - if (i < 0) i = ALLATOMSINT; - if (j < 0) j = ALLATOMSINT; - // short circuit for all-all - if (ALLATOMSINT == i && ALLATOMSINT == j) - { - this->maskAllPairs(mask); - return; - } - bool modified = false; - // update ticker if we are switching from type-mask mode - if (!mtypemask.empty()) - { - mtypemask.clear(); - modified = true; - } - // handle one ALLATOMSINT argument - if (ALLATOMSINT == i || ALLATOMSINT == j) - { - int k = (ALLATOMSINT != i) ? i : j; - pair::iterator, bool> pmm; - pmm = msiteallmask.emplace(k, mask); - if (pmm.second || pmm.first->second != mask) modified = true; - pmm.first->second = mask; - int cntsites = this->countSites(); - for (int l = 0; l < cntsites; ++l) - { - this->setPairMaskValue(k, l, mask); - } - if (modified) mticker.click(); - return; - } - // here neither i nor j is ALLATOMSINT - unordered_map::iterator ii, jj; - ii = msiteallmask.find(i); - if (ii != msiteallmask.end() && mask != ii->second) - { - msiteallmask.erase(ii); - modified = true; - } - jj = msiteallmask.find(j); - if (jj != msiteallmask.end() && mask != jj->second) - { - msiteallmask.erase(jj); - modified = true; + TypeMaskStorage::iterator skip = mtypemask.find(smblij); + for (tpmsk = mtypemask.begin(); tpmsk != mtypemask.end();) { + const bool eraseitem = (tpmsk != skip) && (sk == tpmsk->first.first || + sk == tpmsk->first.second); + if (eraseitem) { + if (mask != tpmsk->second) modified = true; + tpmsk = mtypemask.erase(tpmsk); + } else + ++tpmsk; } - modified = this->setPairMaskValue(i, j, mask) ? true : modified; - if (modified) mticker.click(); -} - - -bool PairQuantity::getPairMask(int i, int j) const -{ - pair ij = (i > j) ? make_pair(j, i) : make_pair(i, j); - bool rv = minvertpairmask.count(ij) ? - !mdefaultpairmask : mdefaultpairmask; - return rv; -} - - -void PairQuantity:: -setTypeMask(string smbli, string smblj, bool mask) -{ - static string upcaseall; - if (upcaseall.empty()) - { - upcaseall = ALLATOMSSTR; - transform(upcaseall.begin(), upcaseall.end(), - upcaseall.begin(), ::toupper); - } - // accept "ALL" (upper ALLATOMSSTR) for smbli and smblj - if (upcaseall == smbli) smbli = ALLATOMSSTR; - if (upcaseall == smblj) smblj = ALLATOMSSTR; - pair allall(ALLATOMSSTR, ALLATOMSSTR); - pair smblij = (smbli > smblj) ? - make_pair(smblj, smbli) : make_pair(smbli, smblj); - // short circuit for all-all - if (allall == smblij) - { - this->maskAllPairs(mask); - return; - } - // msiteallmask is only relevant for index-based masking. - // clean it up to avoid unexpected effects when switching to index masks. - msiteallmask.clear(); - bool modified = false; - // when all is used, remove all typemask elements with the other type - if (ALLATOMSSTR == smbli || ALLATOMSSTR == smblj) - { - const string& sk = (ALLATOMSSTR != smbli) ? smbli : smblj; - TypeMaskStorage::iterator tpmsk; - TypeMaskStorage::iterator skip = mtypemask.find(smblij); - for (tpmsk = mtypemask.begin(); tpmsk != mtypemask.end();) - { - const bool eraseitem = (tpmsk != skip) && - (sk == tpmsk->first.first || sk == tpmsk->first.second); - if (eraseitem) - { - if (mask != tpmsk->second) modified = true; - tpmsk = mtypemask.erase(tpmsk); - } - else ++tpmsk; - } - } - pair pmm; - pmm = mtypemask.emplace(smblij, mask); - if (pmm.second || pmm.first->second != mask) modified = true; - pmm.first->second = mask; - if (modified) mticker.click(); -} - - -bool PairQuantity::getTypeMask(const string& smbli, const string& smblj) const -{ - pair smblij = (smbli > smblj) ? - make_pair(smblj, smbli) : make_pair(smbli, smblj); - pair alli = (smbli > ALLATOMSSTR) ? - make_pair(ALLATOMSSTR, smbli) : make_pair(smbli, ALLATOMSSTR); - pair allj = (smblj > ALLATOMSSTR) ? - make_pair(ALLATOMSSTR, smblj) : make_pair(smblj, ALLATOMSSTR); - bool rv = mtypemask.count(smblij) ? mtypemask.at(smblij) : - mtypemask.count(alli) ? mtypemask.at(alli) : - mtypemask.count(allj) ? mtypemask.at(allj) : - mdefaultpairmask; - return rv; + } + pair pmm; + pmm = mtypemask.emplace(smblij, mask); + if (pmm.second || pmm.first->second != mask) modified = true; + pmm.first->second = mask; + if (modified) mticker.click(); +} + +bool PairQuantity::getTypeMask(const string& smbli, const string& smblj) const { + pair smblij = + (smbli > smblj) ? make_pair(smblj, smbli) : make_pair(smbli, smblj); + pair alli = (smbli > ALLATOMSSTR) + ? make_pair(ALLATOMSSTR, smbli) + : make_pair(smbli, ALLATOMSSTR); + pair allj = (smblj > ALLATOMSSTR) + ? make_pair(ALLATOMSSTR, smblj) + : make_pair(smblj, ALLATOMSSTR); + bool rv = mtypemask.count(smblij) ? mtypemask.at(smblij) + : mtypemask.count(alli) ? mtypemask.at(alli) + : mtypemask.count(allj) ? mtypemask.at(allj) + : mdefaultpairmask; + return rv; } // Protected Methods --------------------------------------------------------- -void PairQuantity::resizeValue(size_t sz) -{ - mvalue.resize(sz); -} - - -void PairQuantity::resetValue() -{ - mmergedvaluescount = 0; - fill(mvalue.begin(), mvalue.end(), 0.0); -} - +void PairQuantity::resizeValue(size_t sz) { mvalue.resize(sz); } -void PairQuantity::configureBondGenerator(BaseBondGenerator& bnds) const -{ - bnds.setRmin(this->getRmin()); - bnds.setRmax(this->getRmax()); +void PairQuantity::resetValue() { + mmergedvaluescount = 0; + fill(mvalue.begin(), mvalue.end(), 0.0); } - -void PairQuantity::executeParallelMerge(const string& pdata) -{ - istringstream storage(pdata, ios::binary); - diffpy::serialization::iarchive ia(storage, ios::binary); - QuantityType pvalue; - ia >> pvalue; - if (pvalue.size() != mvalue.size()) - { - throw invalid_argument("Merged data array must have the same size."); - } - transform(mvalue.begin(), mvalue.end(), pvalue.begin(), - mvalue.begin(), plus()); +void PairQuantity::configureBondGenerator(BaseBondGenerator& bnds) const { + bnds.setRmin(this->getRmin()); + bnds.setRmax(this->getRmax()); } - -int PairQuantity::countSites() const -{ - int rv = mstructure.get() ? mstructure->countSites() : 0; - return rv; +void PairQuantity::executeParallelMerge(const string& pdata) { + istringstream storage(pdata, ios::binary); + diffpy::serialization::iarchive ia(storage, ios::binary); + QuantityType pvalue; + ia >> pvalue; + if (pvalue.size() != mvalue.size()) { + throw invalid_argument("Merged data array must have the same size."); + } + transform(mvalue.begin(), mvalue.end(), pvalue.begin(), mvalue.begin(), + plus()); } - -bool PairQuantity::hasMask() const -{ - bool rv = !(mdefaultpairmask && minvertpairmask.empty() && - msiteallmask.empty() && mtypemask.empty()); - return rv; +int PairQuantity::countSites() const { + int rv = mstructure.get() ? mstructure->countSites() : 0; + return rv; } - -bool PairQuantity::hasPairMask() const -{ - bool rv = this->hasMask() && !this->hasTypeMask(); - return rv; +bool PairQuantity::hasMask() const { + bool rv = !(mdefaultpairmask && minvertpairmask.empty() && + msiteallmask.empty() && mtypemask.empty()); + return rv; } - -bool PairQuantity::hasTypeMask() const -{ - return !mtypemask.empty(); +bool PairQuantity::hasPairMask() const { + bool rv = this->hasMask() && !this->hasTypeMask(); + return rv; } +bool PairQuantity::hasTypeMask() const { return !mtypemask.empty(); } -void PairQuantity::stashPartialValue() -{ - const char* emsg = - "stashPartialValue() is not defined in the calculator class."; - throw logic_error(emsg); +void PairQuantity::stashPartialValue() { + const char* emsg = + "stashPartialValue() is not defined in the calculator class."; + throw logic_error(emsg); } - -void PairQuantity::restorePartialValue() -{ - const char* emsg = - "restorePartialValue() is not defined in the calculator class."; - throw logic_error(emsg); +void PairQuantity::restorePartialValue() { + const char* emsg = + "restorePartialValue() is not defined in the calculator class."; + throw logic_error(emsg); } // Private Methods ----------------------------------------------------------- -void PairQuantity::updateMaskData() -{ - int cntsites = this->countSites(); - // Propagate masks with ALLATOMSINT to all valid indices. - if (mtypemask.empty()) - { - unordered_map::const_iterator ia; - for (ia = msiteallmask.begin(); ia != msiteallmask.end(); ++ia) - { - for (int j = 0; j < cntsites; ++j) - { - this->setPairMaskValue(ia->first, j, ia->second); - } - } +void PairQuantity::updateMaskData() { + int cntsites = this->countSites(); + // Propagate masks with ALLATOMSINT to all valid indices. + if (mtypemask.empty()) { + unordered_map::const_iterator ia; + for (ia = msiteallmask.begin(); ia != msiteallmask.end(); ++ia) { + for (int j = 0; j < cntsites; ++j) { + this->setPairMaskValue(ia->first, j, ia->second); + } } - // For type masking propagate atom types to corresponding atom indices. - else - { - // build a list of indices per each unique atom type - unordered_map< string, list > siteindices; - for (int i = 0; i < cntsites; ++i) - { - const string& smbl = mstructure->siteAtomType(i); - siteindices[smbl].push_back(i); - siteindices[ALLATOMSSTR].push_back(i); - } - // rebuild minvertpairmask according to mtypemask - minvertpairmask.clear(); - TypeMaskStorage::const_iterator tpmsk; - // build a list of type masks with all-masks at the beginning - list< pair > orderedpairs; - for (tpmsk = mtypemask.begin(); tpmsk != mtypemask.end(); ++tpmsk) - { - bool hasall = (ALLATOMSSTR == tpmsk->first.first || - ALLATOMSSTR == tpmsk->first.second); - if (hasall) orderedpairs.push_front(tpmsk->first); - else orderedpairs.push_back(tpmsk->first); - } - list< pair >::const_iterator tpp; - for (tpp = orderedpairs.begin(); tpp != orderedpairs.end(); ++tpp) - { - const list& isites = siteindices[tpp->first]; - const list& jsites = siteindices[tpp->second]; - bool msk = mtypemask.at(*tpp); - list::const_iterator ii, jj; - for (ii = isites.begin(); ii != isites.end(); ++ii) - { - jj = (&isites == &jsites) ? ii : jsites.begin(); - for (; jj != jsites.end(); ++jj) - { - this->setPairMaskValue(*ii, *jj, msk); - } - } + } + // For type masking propagate atom types to corresponding atom indices. + else { + // build a list of indices per each unique atom type + unordered_map > siteindices; + for (int i = 0; i < cntsites; ++i) { + const string& smbl = mstructure->siteAtomType(i); + siteindices[smbl].push_back(i); + siteindices[ALLATOMSSTR].push_back(i); + } + // rebuild minvertpairmask according to mtypemask + minvertpairmask.clear(); + TypeMaskStorage::const_iterator tpmsk; + // build a list of type masks with all-masks at the beginning + list > orderedpairs; + for (tpmsk = mtypemask.begin(); tpmsk != mtypemask.end(); ++tpmsk) { + bool hasall = (ALLATOMSSTR == tpmsk->first.first || + ALLATOMSSTR == tpmsk->first.second); + if (hasall) + orderedpairs.push_front(tpmsk->first); + else + orderedpairs.push_back(tpmsk->first); + } + list >::const_iterator tpp; + for (tpp = orderedpairs.begin(); tpp != orderedpairs.end(); ++tpp) { + const list& isites = siteindices[tpp->first]; + const list& jsites = siteindices[tpp->second]; + bool msk = mtypemask.at(*tpp); + list::const_iterator ii, jj; + for (ii = isites.begin(); ii != isites.end(); ++ii) { + jj = (&isites == &jsites) ? ii : jsites.begin(); + for (; jj != jsites.end(); ++jj) { + this->setPairMaskValue(*ii, *jj, msk); } + } } + } } - -bool PairQuantity::setPairMaskValue(int i, int j, bool mask) -{ - assert(i >= 0 && j >= 0); - pair ij = (i > j) ? make_pair(j, i) : make_pair(i, j); - bool rv; - rv = (mask == mdefaultpairmask) ? - minvertpairmask.erase(ij) : - minvertpairmask.insert(ij).second; - return rv; +bool PairQuantity::setPairMaskValue(int i, int j, bool mask) { + assert(i >= 0 && j >= 0); + pair ij = (i > j) ? make_pair(j, i) : make_pair(i, j); + bool rv; + rv = (mask == mdefaultpairmask) ? minvertpairmask.erase(ij) + : minvertpairmask.insert(ij).second; + return rv; } // Other functions ----------------------------------------------------------- @@ -493,16 +386,15 @@ bool PairQuantity::setPairMaskValue(int i, int j, bool mask) /// The purpose of this function is to support Python pickling of /// PairQuantity objects that hold Python-derived StructureAdapter classes. /// Use it only if you absolutely have to and you know what you do. -StructureAdapterPtr -replacePairQuantityStructure(PairQuantity& pq, StructureAdapterPtr stru) -{ - StructureAdapterPtr rv = pq.mstructure; - pq.mstructure = stru; - return rv; +StructureAdapterPtr replacePairQuantityStructure(PairQuantity& pq, + StructureAdapterPtr stru) { + StructureAdapterPtr rv = pq.mstructure; + pq.mstructure = stru; + return rv; } -} // namespace srreal -} // namespace diffpy +} // namespace srreal +} // namespace diffpy // Serialization ------------------------------------------------------------- diff --git a/src/diffpy/srreal/PairQuantity.hpp b/src/diffpy/srreal/PairQuantity.hpp index 204223f9..6cf6c8b0 100644 --- a/src/diffpy/srreal/PairQuantity.hpp +++ b/src/diffpy/srreal/PairQuantity.hpp @@ -1,20 +1,20 @@ /***************************************************************************** -* -* 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.txt for license information. -* -****************************************************************************** -* -* class PairQuantity -- general implementation of pair quantity calculator -* -*****************************************************************************/ + * + * 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.txt for license information. + * + ****************************************************************************** + * + * class PairQuantity -- general implementation of pair quantity calculator + * + *****************************************************************************/ #ifndef PAIRQUANTITY_HPP_INCLUDED #define PAIRQUANTITY_HPP_INCLUDED @@ -39,133 +39,124 @@ namespace srreal { class BaseBondGenerator; -class DLL_EXPORT PairQuantity : public diffpy::Attributes -{ - public: - - // class constants - static const int ALLATOMSINT; - static const std::string ALLATOMSSTR; - - // constructor - PairQuantity(); - virtual ~PairQuantity() { } - - // methods - const QuantityType& eval(); - template const QuantityType& eval(const T&); - const QuantityType& eval(StructureAdapterPtr); - const QuantityType& value() const; - void mergeParallelData(const std::string& pdata, int ncpu); - virtual std::string getParallelData() const; - - // configuration - template void setStructure(const T&); - void setStructure(StructureAdapterPtr); - StructureAdapterPtr& getStructure(); - const StructureAdapterPtr& getStructure() const; - virtual void setRmin(double); - const double& getRmin() const; - virtual void setRmax(double); - const double& getRmax() const; - void setEvaluatorType(PQEvaluatorType evtp); - PQEvaluatorType getEvaluatorType() const; - PQEvaluatorType getEvaluatorTypeUsed() const; - void setupParallelRun(int cpuindex, int ncpu); - void maskAllPairs(bool mask); - void invertMask(); - void setPairMask(int i, int j, bool mask); - bool getPairMask(int i, int j) const; - void setTypeMask(std::string, std::string, bool mask); - bool getTypeMask(const std::string&, const std::string&) const; - - // ticker for any updates in configuration - virtual eventticker::EventTicker& ticker() const { return mticker; } - - protected: - - friend class PQEvaluatorBasic; - friend class PQEvaluatorOptimized; - friend DLL_EXPORT StructureAdapterPtr - replacePairQuantityStructure(PairQuantity&, StructureAdapterPtr); - - // methods - virtual void resizeValue(size_t); - virtual void resetValue(); - virtual void configureBondGenerator(BaseBondGenerator&) const; - virtual void addPairContribution(const BaseBondGenerator&, int) { } - virtual void executeParallelMerge(const std::string& pdata); - virtual void finishValue() { } - int countSites() const; - // support methods for PQEvaluatorOptimized - bool hasMask() const; - bool hasPairMask() const; - bool hasTypeMask() const; - virtual void stashPartialValue(); - virtual void restorePartialValue(); - - // data - typedef std::unordered_set< - std::pair, - boost::hash< std::pair > - > PairMaskStorage; - typedef std::unordered_map< - std::pair, bool, - boost::hash< std::pair > - > TypeMaskStorage; - QuantityType mvalue; - StructureAdapterPtr mstructure; - double mrmin; - double mrmax; - PQEvaluatorPtr mevaluator; - bool mdefaultpairmask; - PairMaskStorage minvertpairmask; - std::unordered_map msiteallmask; - TypeMaskStorage mtypemask; - int mmergedvaluescount; - mutable eventticker::EventTicker mticker; - - private: - - // methods - void updateMaskData(); - bool setPairMaskValue(int i, int j, bool mask); - - // serialization - friend class boost::serialization::access; - template - void serialize(Archive& ar, const unsigned int version) - { - ar & mvalue; - ar & mstructure; - ar & mrmin; - ar & mrmax; - ar & mevaluator; - ar & mdefaultpairmask; - ar & minvertpairmask; - ar & msiteallmask; - ar & mtypemask; - ar & mmergedvaluescount; - ar & mticker; - } - +class DLL_EXPORT PairQuantity : public diffpy::Attributes { + public: + // class constants + static const int ALLATOMSINT; + static const std::string ALLATOMSSTR; + + // constructor + PairQuantity(); + virtual ~PairQuantity() {} + + // methods + const QuantityType& eval(); + template + const QuantityType& eval(const T&); + const QuantityType& eval(StructureAdapterPtr); + const QuantityType& value() const; + void mergeParallelData(const std::string& pdata, int ncpu); + virtual std::string getParallelData() const; + + // configuration + template + void setStructure(const T&); + void setStructure(StructureAdapterPtr); + StructureAdapterPtr& getStructure(); + const StructureAdapterPtr& getStructure() const; + virtual void setRmin(double); + const double& getRmin() const; + virtual void setRmax(double); + const double& getRmax() const; + void setEvaluatorType(PQEvaluatorType evtp); + PQEvaluatorType getEvaluatorType() const; + PQEvaluatorType getEvaluatorTypeUsed() const; + void setupParallelRun(int cpuindex, int ncpu); + void maskAllPairs(bool mask); + void invertMask(); + void setPairMask(int i, int j, bool mask); + bool getPairMask(int i, int j) const; + void setTypeMask(std::string, std::string, bool mask); + bool getTypeMask(const std::string&, const std::string&) const; + + // ticker for any updates in configuration + virtual eventticker::EventTicker& ticker() const { return mticker; } + + protected: + friend class PQEvaluatorBasic; + friend class PQEvaluatorOptimized; + friend DLL_EXPORT StructureAdapterPtr + replacePairQuantityStructure(PairQuantity&, StructureAdapterPtr); + + // methods + virtual void resizeValue(size_t); + virtual void resetValue(); + virtual void configureBondGenerator(BaseBondGenerator&) const; + virtual void addPairContribution(const BaseBondGenerator&, int) {} + virtual void executeParallelMerge(const std::string& pdata); + virtual void finishValue() {} + int countSites() const; + // support methods for PQEvaluatorOptimized + bool hasMask() const; + bool hasPairMask() const; + bool hasTypeMask() const; + virtual void stashPartialValue(); + virtual void restorePartialValue(); + + // data + typedef std::unordered_set, + boost::hash > > + PairMaskStorage; + typedef std::unordered_map, bool, + boost::hash > > + TypeMaskStorage; + QuantityType mvalue; + StructureAdapterPtr mstructure; + double mrmin; + double mrmax; + PQEvaluatorPtr mevaluator; + bool mdefaultpairmask; + PairMaskStorage minvertpairmask; + std::unordered_map msiteallmask; + TypeMaskStorage mtypemask; + int mmergedvaluescount; + mutable eventticker::EventTicker mticker; + + private: + // methods + void updateMaskData(); + bool setPairMaskValue(int i, int j, bool mask); + + // serialization + friend class boost::serialization::access; + template + void serialize(Archive& ar, const unsigned int version) { + ar & mvalue; + ar & mstructure; + ar & mrmin; + ar & mrmax; + ar & mevaluator; + ar & mdefaultpairmask; + ar & minvertpairmask; + ar & msiteallmask; + ar & mtypemask; + ar & mmergedvaluescount; + ar & mticker; + } }; // Template Public Methods --------------------------------------------------- template -const QuantityType& PairQuantity::eval(const T& stru) -{ - StructureAdapterPtr pstru = convertToStructureAdapter(stru); - return this->eval(pstru); +const QuantityType& PairQuantity::eval(const T& stru) { + StructureAdapterPtr pstru = convertToStructureAdapter(stru); + return this->eval(pstru); } - template -void PairQuantity::setStructure(const T& stru) -{ - StructureAdapterPtr pstru = convertToStructureAdapter(stru); - this->setStructure(pstru); +void PairQuantity::setStructure(const T& stru) { + StructureAdapterPtr pstru = convertToStructureAdapter(stru); + this->setStructure(pstru); } // Other functions ----------------------------------------------------------- @@ -176,8 +167,8 @@ void PairQuantity::setStructure(const T& stru) DLL_EXPORT StructureAdapterPtr replacePairQuantityStructure(PairQuantity& pq, StructureAdapterPtr stru); -} // namespace srreal -} // namespace diffpy +} // namespace srreal +} // namespace diffpy // Serialization ------------------------------------------------------------- diff --git a/src/diffpy/srreal/PeakProfile.cpp b/src/diffpy/srreal/PeakProfile.cpp index 5f12038f..23d0f109 100644 --- a/src/diffpy/srreal/PeakProfile.cpp +++ b/src/diffpy/srreal/PeakProfile.cpp @@ -1,20 +1,20 @@ /***************************************************************************** -* -* 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.txt for license information. -* -****************************************************************************** -* -* class PeakProfile -- base class for calculation of peak profiles. -* -*****************************************************************************/ + * + * 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.txt for license information. + * + ****************************************************************************** + * + * class PeakProfile -- base class for calculation of peak profiles. + * + *****************************************************************************/ #include #include @@ -33,28 +33,23 @@ namespace srreal { // Constructors -------------------------------------------------------------- -PeakProfile::PeakProfile() : mprecision(0.0) -{ - this->registerDoubleAttribute("peakprecision", - this, &PeakProfile::getPrecision, &PeakProfile::setPrecision); +PeakProfile::PeakProfile() : mprecision(0.0) { + this->registerDoubleAttribute("peakprecision", this, + &PeakProfile::getPrecision, + &PeakProfile::setPrecision); } // Public Methods ------------------------------------------------------------ -void PeakProfile::setPrecision(double eps) -{ - if (mprecision != eps) mticker.click(); - mprecision = eps; +void PeakProfile::setPrecision(double eps) { + if (mprecision != eps) mticker.click(); + mprecision = eps; } +const double& PeakProfile::getPrecision() const { return mprecision; } -const double& PeakProfile::getPrecision() const -{ - return mprecision; -} - -} // namespace srreal -} // namespace diffpy +} // namespace srreal +} // namespace diffpy // Serialization ------------------------------------------------------------- diff --git a/src/diffpy/srreal/PeakProfile.hpp b/src/diffpy/srreal/PeakProfile.hpp index 3c03d109..239d83df 100644 --- a/src/diffpy/srreal/PeakProfile.hpp +++ b/src/diffpy/srreal/PeakProfile.hpp @@ -1,25 +1,25 @@ /***************************************************************************** -* -* 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.txt for license information. -* -****************************************************************************** -* -* class PeakProfile -- base class for calculation of peak profiles. -* When possible total integrated area of the profile should equal 1. -* The operator()(x, fwhm) returns amplitude of a zero-centered profile. -* Methods xboundlo(fwhm), xboundhi(fwhm) return low and high x-boundaries, -* where amplitude relative to the maximum becomes smaller than precision -* set by setPrecision(). -* -*****************************************************************************/ + * + * 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.txt for license information. + * + ****************************************************************************** + * + * class PeakProfile -- base class for calculation of peak profiles. + * When possible total integrated area of the profile should equal 1. + * The operator()(x, fwhm) returns amplitude of a zero-centered profile. + * Methods xboundlo(fwhm), xboundhi(fwhm) return low and high x-boundaries, + * where amplitude relative to the maximum becomes smaller than precision + * set by setPrecision(). + * + *****************************************************************************/ #ifndef PEAKPROFILE_HPP_INCLUDED #define PEAKPROFILE_HPP_INCLUDED @@ -38,46 +38,39 @@ namespace diffpy { namespace srreal { -class DLL_EXPORT PeakProfile : - public diffpy::Attributes, - public diffpy::HasClassRegistry -{ - public: - - // constructors - PeakProfile(); - // methods - virtual double operator()(double x, double fwhm) const = 0; - virtual double xboundlo(double fwhm) const = 0; - virtual double xboundhi(double fwhm) const = 0; - virtual void setPrecision(double eps); - const double& getPrecision() const; - virtual eventticker::EventTicker& ticker() const { return mticker; } - - protected: - - // data - mutable eventticker::EventTicker mticker; - - private: - - // data - double mprecision; - - // serialization - friend class boost::serialization::access; - template - void serialize(Archive& ar, const unsigned int version) - { - ar & mticker & mprecision; - } - +class DLL_EXPORT PeakProfile : public diffpy::Attributes, + public diffpy::HasClassRegistry { + public: + // constructors + PeakProfile(); + // methods + virtual double operator()(double x, double fwhm) const = 0; + virtual double xboundlo(double fwhm) const = 0; + virtual double xboundhi(double fwhm) const = 0; + virtual void setPrecision(double eps); + const double& getPrecision() const; + virtual eventticker::EventTicker& ticker() const { return mticker; } + + protected: + // data + mutable eventticker::EventTicker mticker; + + private: + // data + double mprecision; + + // serialization + friend class boost::serialization::access; + template + void serialize(Archive& ar, const unsigned int version) { + ar & mticker & mprecision; + } }; typedef PeakProfile::SharedPtr PeakProfilePtr; -} // namespace srreal -} // namespace diffpy +} // namespace srreal +} // namespace diffpy // Serialization ------------------------------------------------------------- diff --git a/src/diffpy/srreal/PeakWidthModel.cpp b/src/diffpy/srreal/PeakWidthModel.cpp index 53ac94ff..4279e398 100644 --- a/src/diffpy/srreal/PeakWidthModel.cpp +++ b/src/diffpy/srreal/PeakWidthModel.cpp @@ -1,28 +1,28 @@ /***************************************************************************** -* -* 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.txt for license information. -* -****************************************************************************** -* -* class PeakWidthModel -- base class for calculation of peak widths. -* -*****************************************************************************/ + * + * 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.txt for license information. + * + ****************************************************************************** + * + * class PeakWidthModel -- base class for calculation of peak widths. + * + *****************************************************************************/ #include #include #include #include -using std::string; using diffpy::validators::ensureNonNull; +using std::string; namespace diffpy { @@ -33,41 +33,30 @@ namespace srreal { // class PeakWidthModelOwner ------------------------------------------------- -void PeakWidthModelOwner::setPeakWidthModel(PeakWidthModelPtr pwm) -{ - ensureNonNull("PeakWidthModel", pwm); - if (mpwmodel != pwm) mprivateticker.click(); - mpwmodel = pwm; +void PeakWidthModelOwner::setPeakWidthModel(PeakWidthModelPtr pwm) { + ensureNonNull("PeakWidthModel", pwm); + if (mpwmodel != pwm) mprivateticker.click(); + mpwmodel = pwm; } - -void PeakWidthModelOwner::setPeakWidthModelByType(const string& tp) -{ - mpwmodel = PeakWidthModel::createByType(tp); - mprivateticker.click(); +void PeakWidthModelOwner::setPeakWidthModelByType(const string& tp) { + mpwmodel = PeakWidthModel::createByType(tp); + mprivateticker.click(); } +PeakWidthModelPtr& PeakWidthModelOwner::getPeakWidthModel() { return mpwmodel; } -PeakWidthModelPtr& PeakWidthModelOwner::getPeakWidthModel() -{ - return mpwmodel; +const PeakWidthModelPtr& PeakWidthModelOwner::getPeakWidthModel() const { + return mpwmodel; } - -const PeakWidthModelPtr& PeakWidthModelOwner::getPeakWidthModel() const -{ - return mpwmodel; -} - - -eventticker::EventTicker& PeakWidthModelOwner::ticker() const -{ - if (mpwmodel) mprivateticker.updateFrom(mpwmodel->ticker()); - return mprivateticker; +eventticker::EventTicker& PeakWidthModelOwner::ticker() const { + if (mpwmodel) mprivateticker.updateFrom(mpwmodel->ticker()); + return mprivateticker; } -} // namespace srreal -} // namespace diffpy +} // namespace srreal +} // namespace diffpy // Serialization ------------------------------------------------------------- diff --git a/src/diffpy/srreal/PeakWidthModel.hpp b/src/diffpy/srreal/PeakWidthModel.hpp index 15a04c88..ea8a438c 100644 --- a/src/diffpy/srreal/PeakWidthModel.hpp +++ b/src/diffpy/srreal/PeakWidthModel.hpp @@ -1,26 +1,26 @@ /***************************************************************************** -* -* 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.txt for license information. -* -****************************************************************************** -* -* class PeakWidthModel -- base class for calculation of peak widths. -* The calculate function takes a BondGenerator instance and -* returns full width at half maximum, based on peak model parameters -* and anisotropic displacement parameters of atoms in the pair. -* -* class PeakWidthModelOwner -- to be used as a base class for classes -* that own PeakWidthModel -* -*****************************************************************************/ + * + * 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.txt for license information. + * + ****************************************************************************** + * + * class PeakWidthModel -- base class for calculation of peak widths. + * The calculate function takes a BondGenerator instance and + * returns full width at half maximum, based on peak model parameters + * and anisotropic displacement parameters of atoms in the pair. + * + * class PeakWidthModelOwner -- to be used as a base class for classes + * that own PeakWidthModel + * + *****************************************************************************/ #ifndef PEAKWIDTHMODEL_HPP_INCLUDED #define PEAKWIDTHMODEL_HPP_INCLUDED @@ -40,69 +40,56 @@ namespace diffpy { namespace srreal { -class DLL_EXPORT PeakWidthModel : - public diffpy::Attributes, - public diffpy::HasClassRegistry -{ - public: - - // methods - virtual double calculate(const BaseBondGenerator&) const = 0; - virtual double maxWidth(StructureAdapterPtr, - double rmin, double rmax) const = 0; - virtual eventticker::EventTicker& ticker() const { return mticker; } - - protected: - - // data - mutable eventticker::EventTicker mticker; - - private: - - // serialization - friend class boost::serialization::access; - template - void serialize(Archive& ar, const unsigned int version) - { - ar & mticker; - } - +class DLL_EXPORT PeakWidthModel + : public diffpy::Attributes, + public diffpy::HasClassRegistry { + public: + // methods + virtual double calculate(const BaseBondGenerator&) const = 0; + virtual double maxWidth(StructureAdapterPtr, double rmin, + double rmax) const = 0; + virtual eventticker::EventTicker& ticker() const { return mticker; } + + protected: + // data + mutable eventticker::EventTicker mticker; + + private: + // serialization + friend class boost::serialization::access; + template + void serialize(Archive& ar, const unsigned int version) { + ar & mticker; + } }; - typedef PeakWidthModel::SharedPtr PeakWidthModelPtr; - -class DLL_EXPORT PeakWidthModelOwner -{ - public: - - // PDF peak width configuration - void setPeakWidthModel(PeakWidthModelPtr); - void setPeakWidthModelByType(const std::string& tp); - PeakWidthModelPtr& getPeakWidthModel(); - const PeakWidthModelPtr& getPeakWidthModel() const; - eventticker::EventTicker& ticker() const; - - private: - - // data - PeakWidthModelPtr mpwmodel; - mutable eventticker::EventTicker mprivateticker; - - // serialization - friend class boost::serialization::access; - - template - void serialize(Archive& ar, const unsigned int version) - { - ar & mpwmodel & mprivateticker; - } - +class DLL_EXPORT PeakWidthModelOwner { + public: + // PDF peak width configuration + void setPeakWidthModel(PeakWidthModelPtr); + void setPeakWidthModelByType(const std::string& tp); + PeakWidthModelPtr& getPeakWidthModel(); + const PeakWidthModelPtr& getPeakWidthModel() const; + eventticker::EventTicker& ticker() const; + + private: + // data + PeakWidthModelPtr mpwmodel; + mutable eventticker::EventTicker mprivateticker; + + // serialization + friend class boost::serialization::access; + + template + void serialize(Archive& ar, const unsigned int version) { + ar & mpwmodel & mprivateticker; + } }; -} // namespace srreal -} // namespace diffpy +} // namespace srreal +} // namespace diffpy // Serialization ------------------------------------------------------------- diff --git a/src/diffpy/srreal/PeriodicStructureAdapter.cpp b/src/diffpy/srreal/PeriodicStructureAdapter.cpp index 29a20fce..280b41d5 100644 --- a/src/diffpy/srreal/PeriodicStructureAdapter.cpp +++ b/src/diffpy/srreal/PeriodicStructureAdapter.cpp @@ -1,23 +1,23 @@ /***************************************************************************** -* -* 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 PeriodicStructureAdapter -- universal adapter for structure with -* periodic boundary conditions that has no space group symmetry -* -* class PeriodicStructureBondGenerator -- bond generator -* -*****************************************************************************/ + * + * 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 PeriodicStructureAdapter -- universal adapter for structure with + * periodic boundary conditions that has no space group symmetry + * + * class PeriodicStructureBondGenerator -- bond generator + * + *****************************************************************************/ #include @@ -37,93 +37,69 @@ namespace srreal { // Public Methods ------------------------------------------------------------ -StructureAdapterPtr PeriodicStructureAdapter::clone() const -{ - StructureAdapterPtr rv(new PeriodicStructureAdapter(*this)); - return rv; +StructureAdapterPtr PeriodicStructureAdapter::clone() const { + StructureAdapterPtr rv(new PeriodicStructureAdapter(*this)); + return rv; } - -BaseBondGeneratorPtr PeriodicStructureAdapter::createBondGenerator() const -{ - BaseBondGeneratorPtr bnds( - new PeriodicStructureBondGenerator(shared_from_this())); - return bnds; -} - - -double PeriodicStructureAdapter::numberDensity() const -{ - const Lattice& L = this->getLattice(); - double rv = this->totalOccupancy() / L.volume(); - return rv; +BaseBondGeneratorPtr PeriodicStructureAdapter::createBondGenerator() const { + BaseBondGeneratorPtr bnds( + new PeriodicStructureBondGenerator(shared_from_this())); + return bnds; } - -StructureDifference -PeriodicStructureAdapter::diff(StructureAdapterConstPtr other) const -{ - StructureDifference sd = this->StructureAdapter::diff(other); - if (sd.stru0 == sd.stru1) return sd; - typedef std::shared_ptr PPtr; - PPtr pother = std::dynamic_pointer_cast(other); - if (!pother) return sd; - assert(pother == sd.stru1); - if (this->getLattice() != pother->getLattice()) return sd; - sd = this->AtomicStructureAdapter::diff(other); - return sd; +double PeriodicStructureAdapter::numberDensity() const { + const Lattice& L = this->getLattice(); + double rv = this->totalOccupancy() / L.volume(); + return rv; } - -void PeriodicStructureAdapter::setLatPar( - double a, double b, double c, - double alphadeg, double betadeg, double gammadeg) -{ - mlattice.setLatPar(a, b, c, alphadeg, betadeg, gammadeg); +StructureDifference PeriodicStructureAdapter::diff( + StructureAdapterConstPtr other) const { + StructureDifference sd = this->StructureAdapter::diff(other); + if (sd.stru0 == sd.stru1) return sd; + typedef std::shared_ptr PPtr; + PPtr pother = std::dynamic_pointer_cast(other); + if (!pother) return sd; + assert(pother == sd.stru1); + if (this->getLattice() != pother->getLattice()) return sd; + sd = this->AtomicStructureAdapter::diff(other); + return sd; } - -const Lattice& PeriodicStructureAdapter::getLattice() const -{ - return mlattice; +void PeriodicStructureAdapter::setLatPar(double a, double b, double c, + double alphadeg, double betadeg, + double gammadeg) { + mlattice.setLatPar(a, b, c, alphadeg, betadeg, gammadeg); } +const Lattice& PeriodicStructureAdapter::getLattice() const { return mlattice; } -void PeriodicStructureAdapter::toCartesian(Atom& a) const -{ - const Lattice& L = this->getLattice(); - a.xyz_cartn = L.cartesian(a.xyz_cartn); - a.uij_cartn = L.cartesianMatrix(a.uij_cartn); +void PeriodicStructureAdapter::toCartesian(Atom& a) const { + const Lattice& L = this->getLattice(); + a.xyz_cartn = L.cartesian(a.xyz_cartn); + a.uij_cartn = L.cartesianMatrix(a.uij_cartn); } - -void PeriodicStructureAdapter::toFractional(Atom& a) const -{ - const Lattice& L = this->getLattice(); - a.xyz_cartn = L.fractional(a.xyz_cartn); - a.uij_cartn = L.fractionalMatrix(a.uij_cartn); +void PeriodicStructureAdapter::toFractional(Atom& a) const { + const Lattice& L = this->getLattice(); + a.xyz_cartn = L.fractional(a.xyz_cartn); + a.uij_cartn = L.fractionalMatrix(a.uij_cartn); } // Comparison functions ------------------------------------------------------ -bool operator==( - const PeriodicStructureAdapter& stru0, - const PeriodicStructureAdapter& stru1) -{ - const AtomicStructureAdapter& astru0 = stru0; - const AtomicStructureAdapter& astru1 = stru1; - bool rv = - (astru0 == astru1) && - (stru0.getLattice() == stru1.getLattice()); - return rv; +bool operator==(const PeriodicStructureAdapter& stru0, + const PeriodicStructureAdapter& stru1) { + const AtomicStructureAdapter& astru0 = stru0; + const AtomicStructureAdapter& astru1 = stru1; + bool rv = (astru0 == astru1) && (stru0.getLattice() == stru1.getLattice()); + return rv; } - -bool operator!=( - const PeriodicStructureAdapter& stru0, - const PeriodicStructureAdapter& stru1) -{ - return !(stru0 == stru1); +bool operator!=(const PeriodicStructureAdapter& stru0, + const PeriodicStructureAdapter& stru1) { + return !(stru0 == stru1); } ////////////////////////////////////////////////////////////////////////////// @@ -133,107 +109,92 @@ bool operator!=( // Constructor --------------------------------------------------------------- PeriodicStructureBondGenerator::PeriodicStructureBondGenerator( - StructureAdapterConstPtr adpt) : BaseBondGenerator(adpt) -{ - mpstructure = dynamic_cast(adpt.get()); - assert(mpstructure); - int cntsites = mpstructure->countSites(); - mcartesian_positions_uc.reserve(cntsites); - const Lattice& L = mpstructure->getLattice(); - PeriodicStructureAdapter::const_iterator ai = mpstructure->begin(); - R3::Vector xyzc; - for (; ai != mpstructure->end(); ++ai) - { - xyzc = L.ucvCartesian(ai->xyz_cartn); - mcartesian_positions_uc.push_back(xyzc); - } + StructureAdapterConstPtr adpt) + : BaseBondGenerator(adpt) { + mpstructure = dynamic_cast(adpt.get()); + assert(mpstructure); + int cntsites = mpstructure->countSites(); + mcartesian_positions_uc.reserve(cntsites); + const Lattice& L = mpstructure->getLattice(); + PeriodicStructureAdapter::const_iterator ai = mpstructure->begin(); + R3::Vector xyzc; + for (; ai != mpstructure->end(); ++ai) { + xyzc = L.ucvCartesian(ai->xyz_cartn); + mcartesian_positions_uc.push_back(xyzc); + } } // Public Methods ------------------------------------------------------------ -void PeriodicStructureBondGenerator::rewind() -{ - // Delay msphere instantiation to here instead of in constructor, - // so it is possible to use setRmin, setRmax. - if (!msphere.get()) - { - const Lattice& L = mpstructure->getLattice(); - double buffzone = L.ucMaxDiagonalLength(); - double rsphmin = this->getRmin() - buffzone; - double rsphmax = this->getRmax() + buffzone; - msphere.reset(new PointsInSphere(rsphmin, rsphmax, L)); - } - // BaseBondGenerator::rewind calls this->rewindSymmetry, - // which takes care of msphere configuration - this->BaseBondGenerator::rewind(); +void PeriodicStructureBondGenerator::rewind() { + // Delay msphere instantiation to here instead of in constructor, + // so it is possible to use setRmin, setRmax. + if (!msphere.get()) { + const Lattice& L = mpstructure->getLattice(); + double buffzone = L.ucMaxDiagonalLength(); + double rsphmin = this->getRmin() - buffzone; + double rsphmax = this->getRmax() + buffzone; + msphere.reset(new PointsInSphere(rsphmin, rsphmax, L)); + } + // BaseBondGenerator::rewind calls this->rewindSymmetry, + // which takes care of msphere configuration + this->BaseBondGenerator::rewind(); } - -void PeriodicStructureBondGenerator::selectAnchorSite(int anchor) -{ - this->BaseBondGenerator::selectAnchorSite(anchor); - mr0 = mcartesian_positions_uc[anchor]; +void PeriodicStructureBondGenerator::selectAnchorSite(int anchor) { + this->BaseBondGenerator::selectAnchorSite(anchor); + mr0 = mcartesian_positions_uc[anchor]; } - -void PeriodicStructureBondGenerator::setRmin(double rmin) -{ - // destroy msphere so it will be created on rewind with new rmin - if (this->getRmin() != rmin) msphere.reset(); - this->BaseBondGenerator::setRmin(rmin); +void PeriodicStructureBondGenerator::setRmin(double rmin) { + // destroy msphere so it will be created on rewind with new rmin + if (this->getRmin() != rmin) msphere.reset(); + this->BaseBondGenerator::setRmin(rmin); } - -void PeriodicStructureBondGenerator::setRmax(double rmax) -{ - // destroy msphere so it will be created on rewind with new rmax - if (this->getRmax() != rmax) msphere.reset(); - this->BaseBondGenerator::setRmax(rmax); +void PeriodicStructureBondGenerator::setRmax(double rmax) { + // destroy msphere so it will be created on rewind with new rmax + if (this->getRmax() != rmax) msphere.reset(); + this->BaseBondGenerator::setRmax(rmax); } // Protected Methods --------------------------------------------------------- -bool PeriodicStructureBondGenerator::iterateSymmetry() -{ - msphere->next(); - bool done = msphere->finished(); - mrcsphere = done ? R3::zerovector : - mpstructure->getLattice().cartesian(msphere->mno()); - return !done; +bool PeriodicStructureBondGenerator::iterateSymmetry() { + msphere->next(); + bool done = msphere->finished(); + mrcsphere = + done ? R3::zerovector : mpstructure->getLattice().cartesian(msphere->mno()); + return !done; } - -void PeriodicStructureBondGenerator::rewindSymmetry() -{ - msphere->rewind(); - mrcsphere = msphere->finished() ? R3::zerovector : - mpstructure->getLattice().cartesian(msphere->mno()); - this->updater1(); +void PeriodicStructureBondGenerator::rewindSymmetry() { + msphere->rewind(); + mrcsphere = msphere->finished() + ? R3::zerovector + : mpstructure->getLattice().cartesian(msphere->mno()); + this->updater1(); } - -void PeriodicStructureBondGenerator::getNextBond() -{ - ++msite_current; - // go back to the first site if there is next symmetry element - if (msite_current >= msite_last && this->iterateSymmetry()) - { - msite_current = msite_first; - } - // update values only if not finished - if (!this->finished()) this->updater1(); +void PeriodicStructureBondGenerator::getNextBond() { + ++msite_current; + // go back to the first site if there is next symmetry element + if (msite_current >= msite_last && this->iterateSymmetry()) { + msite_current = msite_first; + } + // update values only if not finished + if (!this->finished()) this->updater1(); } // Private Methods ----------------------------------------------------------- -void PeriodicStructureBondGenerator::updater1() -{ - mr1 = mrcsphere + mcartesian_positions_uc[this->site1()]; - this->updateDistance(); +void PeriodicStructureBondGenerator::updater1() { + mr1 = mrcsphere + mcartesian_positions_uc[this->site1()]; + this->updateDistance(); } -} // namespace srreal -} // namespace diffpy +} // namespace srreal +} // namespace diffpy // Serialization ------------------------------------------------------------- diff --git a/src/diffpy/srreal/PeriodicStructureAdapter.hpp b/src/diffpy/srreal/PeriodicStructureAdapter.hpp index c2635745..ddeeb613 100644 --- a/src/diffpy/srreal/PeriodicStructureAdapter.hpp +++ b/src/diffpy/srreal/PeriodicStructureAdapter.hpp @@ -1,23 +1,23 @@ /***************************************************************************** -* -* 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 PeriodicStructureAdapter -- universal adapter for structure with -* periodic boundary conditions that has no space group symmetry -* -* class PeriodicStructureBondGenerator -- bond generator -* -*****************************************************************************/ + * + * 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 PeriodicStructureAdapter -- universal adapter for structure with + * periodic boundary conditions that has no space group symmetry + * + * class PeriodicStructureBondGenerator -- bond generator + * + *****************************************************************************/ #ifndef PERIODICSTRUCTUREADAPTER_HPP_INCLUDED #define PERIODICSTRUCTUREADAPTER_HPP_INCLUDED @@ -33,85 +33,76 @@ namespace srreal { class PointsInSphere; -class DLL_EXPORT PeriodicStructureAdapter : public AtomicStructureAdapter -{ - public: - - // methods - overloaded - virtual StructureAdapterPtr clone() const; - virtual BaseBondGeneratorPtr createBondGenerator() const; - virtual double numberDensity() const; - virtual StructureDifference diff(StructureAdapterConstPtr other) const; - - // methods - own - void setLatPar( - double a, double b, double c, - double alphadeg, double betadeg, double gammadeg); - const Lattice& getLattice() const; - void toCartesian(Atom&) const; - void toFractional(Atom&) const; - - private: - - // data - Lattice mlattice; - - // serialization - friend class boost::serialization::access; - template - void serialize(Archive& ar, const unsigned int version) - { - ar & boost::serialization::base_object(*this); - ar & mlattice; - } - +class DLL_EXPORT PeriodicStructureAdapter : public AtomicStructureAdapter { + public: + // methods - overloaded + virtual StructureAdapterPtr clone() const; + virtual BaseBondGeneratorPtr createBondGenerator() const; + virtual double numberDensity() const; + virtual StructureDifference diff(StructureAdapterConstPtr other) const; + + // methods - own + void setLatPar(double a, double b, double c, double alphadeg, double betadeg, + double gammadeg); + const Lattice& getLattice() const; + void toCartesian(Atom&) const; + void toFractional(Atom&) const; + + private: + // data + Lattice mlattice; + + // serialization + friend class boost::serialization::access; + template + void serialize(Archive& ar, const unsigned int version) { + ar& boost::serialization::base_object(*this); + ar & mlattice; + } }; typedef std::shared_ptr PeriodicStructureAdapterPtr; // Comparison functions -DLL_EXPORT bool operator==(const PeriodicStructureAdapter&, const PeriodicStructureAdapter&); -DLL_EXPORT bool operator!=(const PeriodicStructureAdapter&, const PeriodicStructureAdapter&); - - -class PeriodicStructureBondGenerator : public BaseBondGenerator -{ - public: - - // constructors - PeriodicStructureBondGenerator(StructureAdapterConstPtr); - - // methods - // loop control - virtual void rewind(); - - // configuration - virtual void selectAnchorSite(int); - virtual void setRmin(double); - virtual void setRmax(double); - - protected: - - // data - const PeriodicStructureAdapter* mpstructure; - std::unique_ptr msphere; - R3::Vector mrcsphere; - - // methods - virtual bool iterateSymmetry(); - virtual void rewindSymmetry(); - virtual void getNextBond(); - virtual void updater1(); - - private: - - // data - std::vector mcartesian_positions_uc; +DLL_EXPORT bool operator==(const PeriodicStructureAdapter&, + const PeriodicStructureAdapter&); +DLL_EXPORT bool operator!=(const PeriodicStructureAdapter&, + const PeriodicStructureAdapter&); + +class PeriodicStructureBondGenerator : public BaseBondGenerator { + public: + // constructors + PeriodicStructureBondGenerator(StructureAdapterConstPtr); + + // methods + // loop control + virtual void rewind(); + + // configuration + virtual void selectAnchorSite(int); + virtual void setRmin(double); + virtual void setRmax(double); + + protected: + // data + const PeriodicStructureAdapter* mpstructure; + std::unique_ptr msphere; + R3::Vector mrcsphere; + + // methods + virtual bool iterateSymmetry(); + virtual void rewindSymmetry(); + virtual void getNextBond(); + virtual void updater1(); + + private: + // data + std::vector mcartesian_positions_uc; }; -} // namespace srreal -} // namespace diffpy +} // namespace srreal +} // namespace diffpy // Serialization ------------------------------------------------------------- diff --git a/src/diffpy/srreal/PointsInSphere.cpp b/src/diffpy/srreal/PointsInSphere.cpp index d149d570..076ce5cf 100644 --- a/src/diffpy/srreal/PointsInSphere.cpp +++ b/src/diffpy/srreal/PointsInSphere.cpp @@ -1,22 +1,22 @@ /***************************************************************************** -* -* 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.txt for license information. -* -****************************************************************************** -* -* classes PointsInSphere, ReflectionsInQminQmax, ReflectionsInDmaxDmin -* -* Comments: sequencers for lattice points inside 3D sphere -* -*****************************************************************************/ + * + * 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.txt for license information. + * + ****************************************************************************** + * + * classes PointsInSphere, ReflectionsInQminQmax, ReflectionsInDmaxDmin + * + * Comments: sequencers for lattice points inside 3D sphere + * + *****************************************************************************/ #include #include @@ -31,53 +31,56 @@ using pointsinsphere::LatticeParameters; // Constructor --------------------------------------------------------------- -LatticeParameters::LatticeParameters( double _a, double _b, double _c, - double _alpha, double _beta, double _gamma ) : - a(_a), b(_b), c(_c), - alpha(_alpha), beta(_beta), gamma(_gamma) -{ - update(); +LatticeParameters::LatticeParameters(double _a, double _b, double _c, + double _alpha, double _beta, double _gamma) + : a(_a), b(_b), c(_c), alpha(_alpha), beta(_beta), gamma(_gamma) { + update(); } // Public Methods ------------------------------------------------------------ -void LatticeParameters::update() -{ - using diffpy::mathutils::cosd; - using diffpy::mathutils::sind; - ca = cosd(alpha); cb = cosd(beta); cg = cosd(gamma); - sa = sind(alpha); sb = sind(beta); sg = sind(gamma); - // Vunit is a volume of unit cell with a=b=c=1 - const double Vunit = sqrt(1.0 + 2.0*ca*cb*cg - ca*ca - cb*cb - cg*cg); - ar = sa/(a*Vunit); - br = sb/(b*Vunit); - cr = sg/(c*Vunit); - car = (cb*cg - ca)/(sb*sg); sar = sqrt(1.0 - car*car); - cbr = (ca*cg - cb)/(sa*sg); sbr = sqrt(1.0 - cbr*cbr); - cgr = (ca*cb - cg)/(sa*sb); sgr = sqrt(1.0 - cgr*cgr); - alphar = 180.0/M_PI*acos(car); - betar = 180.0/M_PI*acos(cbr); - gammar = 180.0/M_PI*acos(cgr); -} - - -LatticeParameters LatticeParameters::reciprocal() const -{ - using namespace std; - LatticeParameters rec(*this); - swap(rec.a, rec.ar); - swap(rec.b, rec.br); - swap(rec.c, rec.cr); - swap(rec.alpha, rec.alphar); - swap(rec.beta, rec.betar); - swap(rec.gamma, rec.gammar); - swap(rec.ca, rec.car); - swap(rec.cb, rec.cbr); - swap(rec.cg, rec.cgr); - swap(rec.sa, rec.sar); - swap(rec.sb, rec.sbr); - swap(rec.sg, rec.sgr); - return rec; +void LatticeParameters::update() { + using diffpy::mathutils::cosd; + using diffpy::mathutils::sind; + ca = cosd(alpha); + cb = cosd(beta); + cg = cosd(gamma); + sa = sind(alpha); + sb = sind(beta); + sg = sind(gamma); + // Vunit is a volume of unit cell with a=b=c=1 + const double Vunit = + sqrt(1.0 + 2.0 * ca * cb * cg - ca * ca - cb * cb - cg * cg); + ar = sa / (a * Vunit); + br = sb / (b * Vunit); + cr = sg / (c * Vunit); + car = (cb * cg - ca) / (sb * sg); + sar = sqrt(1.0 - car * car); + cbr = (ca * cg - cb) / (sa * sg); + sbr = sqrt(1.0 - cbr * cbr); + cgr = (ca * cb - cg) / (sa * sb); + sgr = sqrt(1.0 - cgr * cgr); + alphar = 180.0 / M_PI * acos(car); + betar = 180.0 / M_PI * acos(cbr); + gammar = 180.0 / M_PI * acos(cgr); +} + +LatticeParameters LatticeParameters::reciprocal() const { + using namespace std; + LatticeParameters rec(*this); + swap(rec.a, rec.ar); + swap(rec.b, rec.br); + swap(rec.c, rec.cr); + swap(rec.alpha, rec.alphar); + swap(rec.beta, rec.betar); + swap(rec.gamma, rec.gammar); + swap(rec.ca, rec.car); + swap(rec.cb, rec.cbr); + swap(rec.cg, rec.cgr); + swap(rec.sa, rec.sar); + swap(rec.sb, rec.sbr); + swap(rec.sg, rec.sgr); + return rec; } ////////////////////////////////////////////////////////////////////////////// @@ -87,186 +90,140 @@ LatticeParameters LatticeParameters::reciprocal() const // Constructors -------------------------------------------------------------- PointsInSphere::PointsInSphere(double rmin, double rmax, - const LatticeParameters& _latpar ) : - _Rmin(rmin), _Rmax(rmax), - latpar(_latpar), - _m(_mno[0]), _n(_mno[1]), _o(_mno[2]) -{ - init(); - rewind(); -} - - -PointsInSphere::PointsInSphere(double rmin, double rmax, - double _a, double _b, double _c, - double _alpha, double _beta, double _gamma) : - _Rmin(rmin), _Rmax(rmax), - latpar(_a, _b, _c, _alpha, _beta, _gamma), - _m(_mno[0]), _n(_mno[1]), _o(_mno[2]) -{ - init(); - rewind(); + const LatticeParameters& _latpar) + : _Rmin(rmin), + _Rmax(rmax), + latpar(_latpar), + _m(_mno[0]), + _n(_mno[1]), + _o(_mno[2]) { + init(); + rewind(); +} + +PointsInSphere::PointsInSphere(double rmin, double rmax, double _a, double _b, + double _c, double _alpha, double _beta, + double _gamma) + : _Rmin(rmin), + _Rmax(rmax), + latpar(_a, _b, _c, _alpha, _beta, _gamma), + _m(_mno[0]), + _n(_mno[1]), + _o(_mno[2]) { + init(); + rewind(); } // Public Methods ------------------------------------------------------------ // loop control -void PointsInSphere::rewind() -{ - mHalfSpan = Rmax()*latpar.ar; - hi_m = int(ceil(mHalfSpan)); - this->_m = -hi_m; - // make indices n, o invalid, reset the nearest point - this->_n = hi_n = 0; - this->_o = hi_o = outside_o = 0; - n0plane = o0plane = o0line = 0.0; - // unset excluded zone - oExclHalfSpan = 0.0; - // get the first inside point - next_o(); +void PointsInSphere::rewind() { + mHalfSpan = Rmax() * latpar.ar; + hi_m = int(ceil(mHalfSpan)); + this->_m = -hi_m; + // make indices n, o invalid, reset the nearest point + this->_n = hi_n = 0; + this->_o = hi_o = outside_o = 0; + n0plane = o0plane = o0line = 0.0; + // unset excluded zone + oExclHalfSpan = 0.0; + // get the first inside point + next_o(); } +void PointsInSphere::next() { next_o(); } -void PointsInSphere::next() -{ - next_o(); -} - - -bool PointsInSphere::finished() const -{ - return !(m() < this->hi_m); -} +bool PointsInSphere::finished() const { return !(m() < this->hi_m); } // data access -const double& PointsInSphere::Rmin() const -{ - return this->_Rmin; -} +const double& PointsInSphere::Rmin() const { return this->_Rmin; } +const double& PointsInSphere::Rmax() const { return this->_Rmax; } -const double& PointsInSphere::Rmax() const -{ - return this->_Rmax; -} +const int* PointsInSphere::mno() const { return this->_mno; } +int PointsInSphere::m() const { return this->_mno[0]; } -const int* PointsInSphere::mno() const -{ - return this->_mno; -} +int PointsInSphere::n() const { return this->_mno[1]; } +int PointsInSphere::o() const { return this->_mno[2]; } -int PointsInSphere::m() const -{ - return this->_mno[0]; -} - - -int PointsInSphere::n() const -{ - return this->_mno[1]; -} - - -int PointsInSphere::o() const -{ - return this->_mno[2]; -} - - -double PointsInSphere::r() const -{ - const double &a = latpar.a, &b = latpar.b, &c = latpar.c; - const double &ca = latpar.ca, &cb = latpar.cb, &cg = latpar.cg; - return sqrt( m()*m()*a*a + n()*n()*b*b + o()*o()*c*c - + 2*m()*n()*a*b*cg + 2*m()*o()*a*c*cb + 2*n()*o()*b*c*ca ); +double PointsInSphere::r() const { + const double &a = latpar.a, &b = latpar.b, &c = latpar.c; + const double &ca = latpar.ca, &cb = latpar.cb, &cg = latpar.cg; + return sqrt(m() * m() * a * a + n() * n() * b * b + o() * o() * c * c + + 2 * m() * n() * a * b * cg + 2 * m() * o() * a * c * cb + + 2 * n() * o() * b * c * ca); } // Private Methods ----------------------------------------------------------- -void PointsInSphere::next_m() -{ - this->_m += 1; - if (finished()) - { - return; +void PointsInSphere::next_m() { + this->_m += 1; + if (finished()) { + return; + } + // not finished here + n0plane = m() * dn0dm; + o0plane = m() * do0dm; + RplaneSquare = RmaxSquare - pow(m() / latpar.ar, 2); + nHalfSpan = RplaneSquare > 0.0 ? sqrt(RplaneSquare) * b2r : 0.0; + this->_n = int(floor(n0plane - nHalfSpan)); + hi_n = int(ceil(n0plane + nHalfSpan)); +} + +void PointsInSphere::next_n() { + do { + this->_n += 1; + if (n() < hi_n) { + o0line = o0plane + (n() - n0plane) * do0dn; + double RlineSquare = RplaneSquare - pow((n() - n0plane) / b2r, 2); + oHalfSpan = RlineSquare > 0.0 ? sqrt(RlineSquare) * c1r : 0.0; + // parentheses improve round-off errors around [0,0,0] + double RExclSquare = (RlineSquare - RmaxSquare) + RminSquare; + oExclHalfSpan = RExclSquare > 0.0 ? sqrt(RExclSquare) * c1r : 0.0; + this->_o = int(floor(o0line - oHalfSpan)); + outside_o = int(ceil(o0line + oHalfSpan)); + hi_o = outside_o; + if (oExclHalfSpan) { + int hole_o = int(ceil(o0line - oExclHalfSpan)); + if (fabs(hole_o - o0line) < oExclHalfSpan) hi_o = hole_o; + } + return; } - // not finished here - n0plane = m()*dn0dm; - o0plane = m()*do0dm; - RplaneSquare = RmaxSquare - pow(m()/latpar.ar,2); - nHalfSpan = RplaneSquare > 0.0 ? sqrt(RplaneSquare)*b2r : 0.0; - this->_n = int(floor(n0plane - nHalfSpan)); - hi_n = int(ceil(n0plane + nHalfSpan)); + next_m(); + } while (!finished()); } - -void PointsInSphere::next_n() -{ - do - { - this->_n += 1; - if (n() < hi_n) - { - o0line = o0plane + (n()-n0plane)*do0dn; - double RlineSquare = RplaneSquare - pow((n()-n0plane)/b2r,2); - oHalfSpan = RlineSquare > 0.0 ? sqrt(RlineSquare)*c1r : 0.0; - // parentheses improve round-off errors around [0,0,0] - double RExclSquare = (RlineSquare - RmaxSquare) + RminSquare; - oExclHalfSpan = RExclSquare > 0.0 ? sqrt(RExclSquare)*c1r : 0.0; - this->_o = int(floor(o0line - oHalfSpan)); - outside_o = int(ceil(o0line + oHalfSpan)); - hi_o = outside_o; - if (oExclHalfSpan) - { - int hole_o = int(ceil(o0line - oExclHalfSpan)); - if (fabs(hole_o-o0line) < oExclHalfSpan) hi_o = hole_o; - } - return; - } - next_m(); +void PointsInSphere::next_o() { + do { + this->_o += 1; + if (o() < hi_o) { + return; } - while (!finished()); -} - - -void PointsInSphere::next_o() -{ - do - { - this->_o += 1; - if (o() < hi_o) - { - return; - } - if (hi_o != outside_o) - { - hi_o = outside_o; - this->_o = int( ceil(o0line+oExclHalfSpan) ) - 1; - continue; - } - next_n(); + if (hi_o != outside_o) { + hi_o = outside_o; + this->_o = int(ceil(o0line + oExclHalfSpan)) - 1; + continue; } - while (!finished()); + next_n(); + } while (!finished()); } - -void PointsInSphere::init() -{ - RminSquare = (_Rmin < 0.0) ? -(_Rmin*_Rmin) : _Rmin*_Rmin; - RmaxSquare = (_Rmax < 0.0) ? -(_Rmax*_Rmax) : _Rmax*_Rmax; - dn0dm = latpar.cgr*latpar.br/latpar.ar; - do0dm = latpar.cbr*latpar.cr/latpar.ar; - // 2D reciprocal parameters in bc plane - b2r = 1.0/(latpar.b*latpar.sa); - c2r = 1.0/(latpar.c*latpar.sa); - ca2r = -latpar.ca; - do0dn = ca2r*c2r/b2r; - // 1D reciprocal along c axis - c1r = 1.0/latpar.c; +void PointsInSphere::init() { + RminSquare = (_Rmin < 0.0) ? -(_Rmin * _Rmin) : _Rmin * _Rmin; + RmaxSquare = (_Rmax < 0.0) ? -(_Rmax * _Rmax) : _Rmax * _Rmax; + dn0dm = latpar.cgr * latpar.br / latpar.ar; + do0dm = latpar.cbr * latpar.cr / latpar.ar; + // 2D reciprocal parameters in bc plane + b2r = 1.0 / (latpar.b * latpar.sa); + c2r = 1.0 / (latpar.c * latpar.sa); + ca2r = -latpar.ca; + do0dn = ca2r * c2r / b2r; + // 1D reciprocal along c axis + c1r = 1.0 / latpar.c; } ////////////////////////////////////////////////////////////////////////////// @@ -276,90 +233,48 @@ void PointsInSphere::init() // Constructors -------------------------------------------------------------- ReflectionsInQminQmax::ReflectionsInQminQmax(double qmin, double qmax, - const LatticeParameters& _latpar) : - _Qmin(qmin), _Qmax(qmax), - latpar(_latpar), - sph(qmin*M_1_PI/2.0, qmax*M_1_PI/2.0, latpar.reciprocal()) -{ } - + const LatticeParameters& _latpar) + : _Qmin(qmin), + _Qmax(qmax), + latpar(_latpar), + sph(qmin * M_1_PI / 2.0, qmax * M_1_PI / 2.0, latpar.reciprocal()) {} ReflectionsInQminQmax::ReflectionsInQminQmax(double qmin, double qmax, - double _a, double _b, double _c, - double _alpha, double _beta, double _gamma ) : - _Qmin(qmin), _Qmax(qmax), - latpar(_a, _b, _c, _alpha, _beta, _gamma), - sph(qmin*M_1_PI/2.0, qmax*M_1_PI/2.0, latpar.reciprocal()) -{ } + double _a, double _b, double _c, + double _alpha, double _beta, + double _gamma) + : _Qmin(qmin), + _Qmax(qmax), + latpar(_a, _b, _c, _alpha, _beta, _gamma), + sph(qmin * M_1_PI / 2.0, qmax * M_1_PI / 2.0, latpar.reciprocal()) {} // Public Methods ------------------------------------------------------------ // loop control -void ReflectionsInQminQmax::rewind() -{ - this->sph.rewind(); -} +void ReflectionsInQminQmax::rewind() { this->sph.rewind(); } +void ReflectionsInQminQmax::next() { this->sph.next(); } -void ReflectionsInQminQmax::next() -{ - this->sph.next(); -} - - -bool ReflectionsInQminQmax::finished() const -{ - return this->sph.finished(); -} +bool ReflectionsInQminQmax::finished() const { return this->sph.finished(); } // data access -const double& ReflectionsInQminQmax::Qmin() const -{ - return this->_Qmin; -} - - -const double& ReflectionsInQminQmax::Qmax() const -{ - return this->_Qmax; -} - - -const int* ReflectionsInQminQmax::hkl() const -{ - return this->sph.mno(); -} +const double& ReflectionsInQminQmax::Qmin() const { return this->_Qmin; } +const double& ReflectionsInQminQmax::Qmax() const { return this->_Qmax; } -int ReflectionsInQminQmax::h() const -{ - return this->sph.m(); -} +const int* ReflectionsInQminQmax::hkl() const { return this->sph.mno(); } +int ReflectionsInQminQmax::h() const { return this->sph.m(); } -int ReflectionsInQminQmax::k() const -{ - return this->sph.n(); -} +int ReflectionsInQminQmax::k() const { return this->sph.n(); } +int ReflectionsInQminQmax::l() const { return this->sph.o(); } -int ReflectionsInQminQmax::l() const -{ - return this->sph.o(); -} - - -double ReflectionsInQminQmax::Q() const -{ - return 2.0*M_PI*sph.r(); -} +double ReflectionsInQminQmax::Q() const { return 2.0 * M_PI * sph.r(); } - -double ReflectionsInQminQmax::d() const -{ - return 1.0/sph.r(); -} +double ReflectionsInQminQmax::d() const { return 1.0 / sph.r(); } ////////////////////////////////////////////////////////////////////////////// // class ReflectionsInDmaxDmin @@ -368,33 +283,26 @@ double ReflectionsInQminQmax::d() const // Constructors -------------------------------------------------------------- ReflectionsInDmaxDmin::ReflectionsInDmaxDmin(double dmax, double dmin, - const LatticeParameters& _latpar) : - ReflectionsInQminQmax(2.0*M_PI/dmax, 2.0*M_PI/dmin, _latpar), - _Dmin(dmin), _Dmax(dmax) -{ } - + const LatticeParameters& _latpar) + : ReflectionsInQminQmax(2.0 * M_PI / dmax, 2.0 * M_PI / dmin, _latpar), + _Dmin(dmin), + _Dmax(dmax) {} ReflectionsInDmaxDmin::ReflectionsInDmaxDmin(double dmax, double dmin, - double _a, double _b, double _c, - double _alpha, double _beta, double _gamma) : - ReflectionsInQminQmax(2.0*M_PI/dmax, 2.0*M_PI/dmin, - _a, _b, _c, _alpha, _beta, _gamma), - _Dmin(dmin), _Dmax(dmax) -{ } + double _a, double _b, double _c, + double _alpha, double _beta, + double _gamma) + : ReflectionsInQminQmax(2.0 * M_PI / dmax, 2.0 * M_PI / dmin, _a, _b, _c, + _alpha, _beta, _gamma), + _Dmin(dmin), + _Dmax(dmax) {} // Public Methods ------------------------------------------------------------ // data access -const double& ReflectionsInDmaxDmin::Dmin() const -{ - return this->_Dmin; -} - +const double& ReflectionsInDmaxDmin::Dmin() const { return this->_Dmin; } -const double& ReflectionsInDmaxDmin::Dmax() const -{ - return this->_Dmax; -} +const double& ReflectionsInDmaxDmin::Dmax() const { return this->_Dmax; } // End of file diff --git a/src/diffpy/srreal/PointsInSphere.hpp b/src/diffpy/srreal/PointsInSphere.hpp index 27da5b61..283e79d8 100644 --- a/src/diffpy/srreal/PointsInSphere.hpp +++ b/src/diffpy/srreal/PointsInSphere.hpp @@ -1,52 +1,52 @@ /***************************************************************************** -* -* 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.txt for license information. -* -****************************************************************************** -* -* classes PointsInSphere, ReflectionsInQminQmax, ReflectionsInDmaxDmin -* -* Constructors: -* -* PointsInSphere(Rmin, Rmax, a, b, c, alpha, beta, gamma) -* ReflectionsInQminQmax(Qmin, Qmax, a, b, c, alpha, beta, gamma) -* ReflectionsInDmaxDmin(Dmax, Dmin, a, b, c, alpha, beta, gamma) -* -* template PointsInSphere(Rmin, Rmax, const Lattice&) -* -* where class Lattice must provide methods a(), b(), c(), -* alpha(), beta(), gamma() -* -* Examples: -* -* PointsInSphere sph(Rmin, Rmax, a, b, c, alpha, beta, gamma) -* for (sph.rewind(); !sph.finished(); sph.next()) -* { -* // lattice indices are in sph.m(), sph.n(), sph.o() or sph.mno() -* // sph.r() is distance from origin, -* // where sph.Rmin() < sph.r() < sph.Rmax() -* } -* -* ReflectionsInQminQmax ref(Qmin, Qmax, a, b, c, alpha, beta, gamma) -* for (ReflectionsInQminQmax ref(Qmin, Qmax, a, b, c, alpha, beta, gamma); -* !ref.finished(); ref.next() ) -* { -* // Miller indices are in ref.h(), ref.k(), ref.l() or ref.hkl() -* // ref.Q() is magnitude of Q vector -* // ref.d() is lattice plane spacing -* } -* -* Tip: add epsilon to Rmax to avoid roundoff issues -* -*****************************************************************************/ + * + * 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.txt for license information. + * + ****************************************************************************** + * + * classes PointsInSphere, ReflectionsInQminQmax, ReflectionsInDmaxDmin + * + * Constructors: + * + * PointsInSphere(Rmin, Rmax, a, b, c, alpha, beta, gamma) + * ReflectionsInQminQmax(Qmin, Qmax, a, b, c, alpha, beta, gamma) + * ReflectionsInDmaxDmin(Dmax, Dmin, a, b, c, alpha, beta, gamma) + * + * template PointsInSphere(Rmin, Rmax, const Lattice&) + * + * where class Lattice must provide methods a(), b(), c(), + * alpha(), beta(), gamma() + * + * Examples: + * + * PointsInSphere sph(Rmin, Rmax, a, b, c, alpha, beta, gamma) + * for (sph.rewind(); !sph.finished(); sph.next()) + * { + * // lattice indices are in sph.m(), sph.n(), sph.o() or sph.mno() + * // sph.r() is distance from origin, + * // where sph.Rmin() < sph.r() < sph.Rmax() + * } + * + * ReflectionsInQminQmax ref(Qmin, Qmax, a, b, c, alpha, beta, gamma) + * for (ReflectionsInQminQmax ref(Qmin, Qmax, a, b, c, alpha, beta, gamma); + * !ref.finished(); ref.next() ) + * { + * // Miller indices are in ref.h(), ref.k(), ref.l() or ref.hkl() + * // ref.Q() is magnitude of Q vector + * // ref.d() is lattice plane spacing + * } + * + * Tip: add epsilon to Rmax to avoid roundoff issues + * + *****************************************************************************/ #ifndef POINTSINSPHERE_HPP_INCLUDED #define POINTSINSPHERE_HPP_INCLUDED @@ -58,198 +58,180 @@ namespace srreal { namespace pointsinsphere { -class LatticeParameters -{ - public: - - // data - // input arguments - double a, b, c, alpha, beta, gamma; - // cosines and sines of direct lattice angles - double ca, cb, cg, sa, sb, sg; - // reciprocal lattice and its cosines and sines - double ar, br, cr, alphar, betar, gammar; - double car, cbr, cgr, sar, sbr, sgr; - - // constructor - LatticeParameters(double _a, double _b, double _c, - double _alpha, double _beta, double _gamma); - - // methods - // calculate all properties from current lattice parameters - void update(); - // return a reciprocal of this lattice - LatticeParameters reciprocal() const; - +class LatticeParameters { + public: + // data + // input arguments + double a, b, c, alpha, beta, gamma; + // cosines and sines of direct lattice angles + double ca, cb, cg, sa, sb, sg; + // reciprocal lattice and its cosines and sines + double ar, br, cr, alphar, betar, gammar; + double car, cbr, cgr, sar, sbr, sgr; + + // constructor + LatticeParameters(double _a, double _b, double _c, double _alpha, + double _beta, double _gamma); + + // methods + // calculate all properties from current lattice parameters + void update(); + // return a reciprocal of this lattice + LatticeParameters reciprocal() const; }; -} // namespace pointsinsphere - - -class PointsInSphere -{ - public: - - // constructors - PointsInSphere(double rmin, double rmax, - const pointsinsphere::LatticeParameters& _latpar); - PointsInSphere(double rmin, double rmax, - double _a, double _b, double _c, - double _alpha, double _beta, double _gamma); - template - PointsInSphere(double rmin, double rmax, const L&); - - // methods - // loop control - void rewind(); - void next(); - bool finished() const; - // data access - const double& Rmin() const; - const double& Rmax() const; - const int* mno() const; - int m() const; - int n() const; - int o() const; - double r() const; - - private: - - // data - // inputs - const double _Rmin; - const double _Rmax; - const pointsinsphere::LatticeParameters latpar; - // output - int _mno[3]; - int& _m; - int& _n; - int& _o; - // calculated constants set by init() - double RminSquare, RmaxSquare; - // 2D reciprocal parameters and cosine in bc plane - double b2r, c2r, ca2r; - // reciprocal c - double c1r; - // offset of the nearest point to [0,0,0] - double dn0dm, do0dm, do0dn; - // loop variables - double n0plane, o0plane, o0line; - double mHalfSpan, nHalfSpan, oHalfSpan; - // o indices excluded due to Rmin - double oExclHalfSpan; - int hi_m, hi_n, hi_o, outside_o; - double RplaneSquare; - - // methods - // loop advance - void next_m(); - void next_n(); - void next_o(); - void init(); +} // namespace pointsinsphere + +class PointsInSphere { + public: + // constructors + PointsInSphere(double rmin, double rmax, + const pointsinsphere::LatticeParameters& _latpar); + PointsInSphere(double rmin, double rmax, double _a, double _b, double _c, + double _alpha, double _beta, double _gamma); + template + PointsInSphere(double rmin, double rmax, const L&); + + // methods + // loop control + void rewind(); + void next(); + bool finished() const; + // data access + const double& Rmin() const; + const double& Rmax() const; + const int* mno() const; + int m() const; + int n() const; + int o() const; + double r() const; + + private: + // data + // inputs + const double _Rmin; + const double _Rmax; + const pointsinsphere::LatticeParameters latpar; + // output + int _mno[3]; + int& _m; + int& _n; + int& _o; + // calculated constants set by init() + double RminSquare, RmaxSquare; + // 2D reciprocal parameters and cosine in bc plane + double b2r, c2r, ca2r; + // reciprocal c + double c1r; + // offset of the nearest point to [0,0,0] + double dn0dm, do0dm, do0dn; + // loop variables + double n0plane, o0plane, o0line; + double mHalfSpan, nHalfSpan, oHalfSpan; + // o indices excluded due to Rmin + double oExclHalfSpan; + int hi_m, hi_n, hi_o, outside_o; + double RplaneSquare; + + // methods + // loop advance + void next_m(); + void next_n(); + void next_o(); + void init(); }; - -class ReflectionsInQminQmax -{ - public: - - // constructors - ReflectionsInQminQmax(double _Qmin, double _Qmax, - const pointsinsphere::LatticeParameters& _latpar); - ReflectionsInQminQmax(double _Qmin, double _Qmax, - double _a, double _b, double _c, - double _alpha, double _beta, double _gamma); - template - ReflectionsInQminQmax(double _Qmin, double _Qmax, const L&); - - // methods - // loop control - void rewind(); - void next(); - bool finished() const; - // data access - const double& Qmin() const; - const double& Qmax() const; - const int* hkl() const; - int h() const; - int k() const; - int l() const; - double Q() const; - double d() const; - - private: - - // data - // inputs - const double _Qmin; - const double _Qmax; - const pointsinsphere::LatticeParameters latpar; - // composite - PointsInSphere sph; +class ReflectionsInQminQmax { + public: + // constructors + ReflectionsInQminQmax(double _Qmin, double _Qmax, + const pointsinsphere::LatticeParameters& _latpar); + ReflectionsInQminQmax(double _Qmin, double _Qmax, double _a, double _b, + double _c, double _alpha, double _beta, double _gamma); + template + ReflectionsInQminQmax(double _Qmin, double _Qmax, const L&); + + // methods + // loop control + void rewind(); + void next(); + bool finished() const; + // data access + const double& Qmin() const; + const double& Qmax() const; + const int* hkl() const; + int h() const; + int k() const; + int l() const; + double Q() const; + double d() const; + + private: + // data + // inputs + const double _Qmin; + const double _Qmax; + const pointsinsphere::LatticeParameters latpar; + // composite + PointsInSphere sph; }; - -class ReflectionsInDmaxDmin : public ReflectionsInQminQmax -{ - public: - - // constructors - ReflectionsInDmaxDmin(double dmax, double dmin, - const pointsinsphere::LatticeParameters& _latpar); - ReflectionsInDmaxDmin(double dmax, double dmin, - double _a, double _b, double _c, - double _alpha, double _beta, double _gamma); - template - ReflectionsInDmaxDmin(double dmax, double dmin, const L&); - - // methods - // data access - const double& Dmin() const; - const double& Dmax() const; - - private: - - // data - // inputs - const double _Dmin; - const double _Dmax; +class ReflectionsInDmaxDmin : public ReflectionsInQminQmax { + public: + // constructors + ReflectionsInDmaxDmin(double dmax, double dmin, + const pointsinsphere::LatticeParameters& _latpar); + ReflectionsInDmaxDmin(double dmax, double dmin, double _a, double _b, + double _c, double _alpha, double _beta, double _gamma); + template + ReflectionsInDmaxDmin(double dmax, double dmin, const L&); + + // methods + // data access + const double& Dmin() const; + const double& Dmax() const; + + private: + // data + // inputs + const double _Dmin; + const double _Dmax; }; // Template Constructor for PointsInSphere ----------------------------------- template -PointsInSphere::PointsInSphere(double rmin, double rmax, const L& lat) : - _Rmin(rmin), _Rmax(rmax), - latpar(lat.a(), lat.b(), lat.c(), lat.alpha(), lat.beta(), lat.gamma()), - _m(_mno[0]), _n(_mno[1]), _o(_mno[2]) -{ - init(); - rewind(); +PointsInSphere::PointsInSphere(double rmin, double rmax, const L& lat) + : _Rmin(rmin), + _Rmax(rmax), + latpar(lat.a(), lat.b(), lat.c(), lat.alpha(), lat.beta(), lat.gamma()), + _m(_mno[0]), + _n(_mno[1]), + _o(_mno[2]) { + init(); + rewind(); } // Template Constructor for ReflectionsInQminQmax ---------------------------- template -ReflectionsInQminQmax::ReflectionsInQminQmax( - double _qmin, double _qmax, const L& lat) : - _Qmin(_qmin), _Qmax(_qmax), - latpar(lat.a(), lat.b(), lat.c(), - lat.alpha(), lat.beta(), lat.gamma()), - sph(_qmin*M_1_PI/2.0, _qmax*M_1_PI/2.0, latpar.reciprocal()) -{ } +ReflectionsInQminQmax::ReflectionsInQminQmax(double _qmin, double _qmax, + const L& lat) + : _Qmin(_qmin), + _Qmax(_qmax), + latpar(lat.a(), lat.b(), lat.c(), lat.alpha(), lat.beta(), lat.gamma()), + sph(_qmin * M_1_PI / 2.0, _qmax * M_1_PI / 2.0, latpar.reciprocal()) {} // Template Constructor for ReflectionsInDmaxDmin ---------------------------- template -ReflectionsInDmaxDmin::ReflectionsInDmaxDmin( - double dmax, double dmin, const L& lat) : - ReflectionsInQminQmax(2.0*M_PI/dmax, 2.0*M_PI/dmin, lat), - _Dmin(dmin), _Dmax(dmax) -{ } - - -} // namespace srreal -} // namespace diffpy +ReflectionsInDmaxDmin::ReflectionsInDmaxDmin(double dmax, double dmin, + const L& lat) + : ReflectionsInQminQmax(2.0 * M_PI / dmax, 2.0 * M_PI / dmin, lat), + _Dmin(dmin), + _Dmax(dmax) {} + +} // namespace srreal +} // namespace diffpy #endif // POINTSINSPHERE_HPP_INCLUDED diff --git a/src/diffpy/srreal/QResolutionEnvelope.cpp b/src/diffpy/srreal/QResolutionEnvelope.cpp index c20d3642..76586bc1 100644 --- a/src/diffpy/srreal/QResolutionEnvelope.cpp +++ b/src/diffpy/srreal/QResolutionEnvelope.cpp @@ -1,20 +1,20 @@ /***************************************************************************** -* -* 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.txt for license information. -* -****************************************************************************** -* -* class QResolutionEnvelope -- Gaussian envelope due to limited Q resolution -* -*****************************************************************************/ + * + * 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.txt for license information. + * + ****************************************************************************** + * + * class QResolutionEnvelope -- Gaussian envelope due to limited Q resolution + * + *****************************************************************************/ #include @@ -28,62 +28,44 @@ namespace srreal { // Constructor --------------------------------------------------------------- -QResolutionEnvelope::QResolutionEnvelope() -{ - this->setQdamp(0.0); - this->registerDoubleAttribute("qdamp", this, - &QResolutionEnvelope::getQdamp, &QResolutionEnvelope::setQdamp); +QResolutionEnvelope::QResolutionEnvelope() { + this->setQdamp(0.0); + this->registerDoubleAttribute("qdamp", this, &QResolutionEnvelope::getQdamp, + &QResolutionEnvelope::setQdamp); } - -PDFEnvelopePtr QResolutionEnvelope::create() const -{ - PDFEnvelopePtr rv(new QResolutionEnvelope()); - return rv; +PDFEnvelopePtr QResolutionEnvelope::create() const { + PDFEnvelopePtr rv(new QResolutionEnvelope()); + return rv; } - -PDFEnvelopePtr QResolutionEnvelope::clone() const -{ - PDFEnvelopePtr rv(new QResolutionEnvelope(*this)); - return rv; +PDFEnvelopePtr QResolutionEnvelope::clone() const { + PDFEnvelopePtr rv(new QResolutionEnvelope(*this)); + return rv; } // Public Methods ------------------------------------------------------------ -const string& QResolutionEnvelope::type() const -{ - static string rv = "qresolution"; - return rv; +const string& QResolutionEnvelope::type() const { + static string rv = "qresolution"; + return rv; } - -double QResolutionEnvelope::operator()(const double& r) const -{ - double rv = (mqdamp > 0.0) ? - exp(-pow(r * mqdamp, 2) / 2) : - 1.0; - return rv; +double QResolutionEnvelope::operator()(const double& r) const { + double rv = (mqdamp > 0.0) ? exp(-pow(r * mqdamp, 2) / 2) : 1.0; + return rv; } +void QResolutionEnvelope::setQdamp(double sc) { mqdamp = sc; } -void QResolutionEnvelope::setQdamp(double sc) -{ - mqdamp = sc; -} - - -const double& QResolutionEnvelope::getQdamp() const -{ - return mqdamp; -} +const double& QResolutionEnvelope::getQdamp() const { return mqdamp; } // Registration -------------------------------------------------------------- bool reg_QResolutionEnvelope = QResolutionEnvelope().registerThisType(); -} // namespace srreal -} // namespace diffpy +} // namespace srreal +} // namespace diffpy // Serialization ------------------------------------------------------------- diff --git a/src/diffpy/srreal/QResolutionEnvelope.hpp b/src/diffpy/srreal/QResolutionEnvelope.hpp index 22363c3b..a12fad3b 100644 --- a/src/diffpy/srreal/QResolutionEnvelope.hpp +++ b/src/diffpy/srreal/QResolutionEnvelope.hpp @@ -1,20 +1,20 @@ /***************************************************************************** -* -* 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.txt for license information. -* -****************************************************************************** -* -* class QResolutionEnvelope -- Gaussian envelope due to limited Q resolution -* -*****************************************************************************/ + * + * 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.txt for license information. + * + ****************************************************************************** + * + * class QResolutionEnvelope -- Gaussian envelope due to limited Q resolution + * + *****************************************************************************/ #ifndef QRESOLUTIONENVELOPE_HPP_INCLUDED #define QRESOLUTIONENVELOPE_HPP_INCLUDED @@ -29,41 +29,37 @@ namespace srreal { /// @class QResolutionEnvelope /// @brief wide Gaussian PDF scaling envelope caused by finite Q resolution -class DLL_EXPORT QResolutionEnvelope : public PDFEnvelope -{ - public: - - // constructors - QResolutionEnvelope(); - virtual PDFEnvelopePtr create() const; - virtual PDFEnvelopePtr clone() const; - - // methods - virtual const std::string& type() const; - virtual double operator()(const double& r) const; - void setQdamp(double sc); - const double& getQdamp() const; - - private: - - // data - double mqdamp; - - // serialization - friend class boost::serialization::access; - - template - void serialize(Archive& ar, const unsigned int version) - { - using boost::serialization::base_object; - ar & base_object(*this); - ar & mqdamp; - } +class DLL_EXPORT QResolutionEnvelope : public PDFEnvelope { + public: + // constructors + QResolutionEnvelope(); + virtual PDFEnvelopePtr create() const; + virtual PDFEnvelopePtr clone() const; + + // methods + virtual const std::string& type() const; + virtual double operator()(const double& r) const; + void setQdamp(double sc); + const double& getQdamp() const; + + private: + // data + double mqdamp; + + // serialization + friend class boost::serialization::access; + + template + void serialize(Archive& ar, const unsigned int version) { + using boost::serialization::base_object; + ar& base_object(*this); + ar & mqdamp; + } }; // class QResolutionEnvelope -} // namespace srreal -} // namespace diffpy +} // namespace srreal +} // namespace diffpy // Serialization ------------------------------------------------------------- diff --git a/src/diffpy/srreal/QuantityType.hpp b/src/diffpy/srreal/QuantityType.hpp index a8ba3aab..6fccadc1 100644 --- a/src/diffpy/srreal/QuantityType.hpp +++ b/src/diffpy/srreal/QuantityType.hpp @@ -1,24 +1,24 @@ /***************************************************************************** -* -* 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.txt for license information. -* -****************************************************************************** -* -* Shared types and small functions used by PairQuantity calculators. -* -* QuantityType -- type that stores PairQuantity results, an array of doubles -* It is a unique derived class from vector to avoid conflicts with -* boost_python converters in cctbx. -* -*****************************************************************************/ + * + * 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.txt for license information. + * + ****************************************************************************** + * + * Shared types and small functions used by PairQuantity calculators. + * + * QuantityType -- type that stores PairQuantity results, an array of doubles + * It is a unique derived class from vector to avoid conflicts with + * boost_python converters in cctbx. + * + *****************************************************************************/ #ifndef QUANTITYTYPE_HPP_INCLUDED #define QUANTITYTYPE_HPP_INCLUDED @@ -31,35 +31,29 @@ namespace diffpy { namespace srreal { -class DLL_EXPORT QuantityType : public std::vector -{ - private: - - typedef std::vector Base; - - public: - - // Constructors from std::vector - QuantityType() : Base() { } - QuantityType(const Base& src) : Base(src) { } - explicit QuantityType(Base::size_type n) : Base(n) { } - explicit QuantityType(Base::size_type n, double x) : Base(n, x) { } - template - QuantityType(Iter first, Iter last) : Base(first, last) { } - - private: - - // serialization - friend class boost::serialization::access; - template - void serialize(Archive& ar, const unsigned int version) - { - ar & boost::serialization::base_object(*this); - } - +class DLL_EXPORT QuantityType : public std::vector { + private: + typedef std::vector Base; + + public: + // Constructors from std::vector + QuantityType() : Base() {} + QuantityType(const Base& src) : Base(src) {} + explicit QuantityType(Base::size_type n) : Base(n) {} + explicit QuantityType(Base::size_type n, double x) : Base(n, x) {} + template + QuantityType(Iter first, Iter last) : Base(first, last) {} + + private: + // serialization + friend class boost::serialization::access; + template + void serialize(Archive& ar, const unsigned int version) { + ar& boost::serialization::base_object(*this); + } }; -} // namespace srreal -} // namespace diffpy +} // namespace srreal +} // namespace diffpy #endif // QUANTITYTYPE_HPP_INCLUDED diff --git a/src/diffpy/srreal/R3linalg.cpp b/src/diffpy/srreal/R3linalg.cpp index a3e07894..83044c46 100644 --- a/src/diffpy/srreal/R3linalg.cpp +++ b/src/diffpy/srreal/R3linalg.cpp @@ -1,20 +1,20 @@ /***************************************************************************** -* -* 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.txt for license information. -* -****************************************************************************** -* -* R3linalg -- vector and matrix types and linear algebra operations in R3 space -* -*****************************************************************************/ + * + * 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.txt for license information. + * + ****************************************************************************** + * + * R3linalg -- vector and matrix types and linear algebra operations in R3 space + * + *****************************************************************************/ #include #include @@ -29,112 +29,92 @@ namespace R3 { DLL_EXPORT const int Ndim = 3; // Functions -const Matrix& identity() -{ - static Matrix mx = ublas::identity_matrix(Ndim); - return mx; +const Matrix& identity() { + static Matrix mx = ublas::identity_matrix(Ndim); + return mx; } - -const Matrix& zeromatrix() -{ - static Matrix mx = ublas::zero_matrix(Ndim, Ndim); - return mx; +const Matrix& zeromatrix() { + static Matrix mx = ublas::zero_matrix(Ndim, Ndim); + return mx; } - -double determinant(const Matrix& A) -{ - gsl_matrix* gA = gsl_matrix_alloc(Ndim, Ndim); - for (int i = 0; i != Ndim; ++i) - { - for (int j = 0; j != Ndim; ++j) - { - gsl_matrix_set(gA, i, j, A(i,j)); - } +double determinant(const Matrix& A) { + gsl_matrix* gA = gsl_matrix_alloc(Ndim, Ndim); + for (int i = 0; i != Ndim; ++i) { + for (int j = 0; j != Ndim; ++j) { + gsl_matrix_set(gA, i, j, A(i, j)); } - gsl_permutation* gP = gsl_permutation_alloc(Ndim); - int signum; - gsl_linalg_LU_decomp(gA, gP, &signum); - double det = gsl_linalg_LU_det(gA, signum); - gsl_permutation_free(gP); - gsl_matrix_free(gA); - return det; + } + gsl_permutation* gP = gsl_permutation_alloc(Ndim); + int signum; + gsl_linalg_LU_decomp(gA, gP, &signum); + double det = gsl_linalg_LU_det(gA, signum); + gsl_permutation_free(gP); + gsl_matrix_free(gA); + return det; } - -const Matrix& inverse(const Matrix& A) -{ - static Matrix B; - gsl_matrix* gA = gsl_matrix_alloc(Ndim, Ndim); - for (int i = 0; i != Ndim; ++i) - { - for (int j = 0; j != Ndim; ++j) - { - gsl_matrix_set(gA, i, j, A(i,j)); - } +const Matrix& inverse(const Matrix& A) { + static Matrix B; + gsl_matrix* gA = gsl_matrix_alloc(Ndim, Ndim); + for (int i = 0; i != Ndim; ++i) { + for (int j = 0; j != Ndim; ++j) { + gsl_matrix_set(gA, i, j, A(i, j)); } - gsl_permutation* gP = gsl_permutation_alloc(Ndim); - int signum; - gsl_linalg_LU_decomp(gA, gP, &signum); - double* bdata = &(B.data()[0]); - gsl_matrix_view gB = gsl_matrix_view_array(bdata, Ndim, Ndim); - gsl_linalg_LU_invert(gA, gP, &gB.matrix); - gsl_permutation_free(gP); - gsl_matrix_free(gA); - return B; + } + gsl_permutation* gP = gsl_permutation_alloc(Ndim); + int signum; + gsl_linalg_LU_decomp(gA, gP, &signum); + double* bdata = &(B.data()[0]); + gsl_matrix_view gB = gsl_matrix_view_array(bdata, Ndim, Ndim); + gsl_linalg_LU_invert(gA, gP, &gB.matrix); + gsl_permutation_free(gP); + gsl_matrix_free(gA); + return B; } - -size_t hash_value(const Vector& v) -{ - return boost::hash_range(v.begin(), v.end()); +size_t hash_value(const Vector& v) { + return boost::hash_range(v.begin(), v.end()); } - -size_t hash_value(const Matrix& A) -{ - return boost::hash_range(A.data().begin(), A.data().end()); +size_t hash_value(const Matrix& A) { + return boost::hash_range(A.data().begin(), A.data().end()); } -} // namespace R3 -} // namespace srreal +} // namespace R3 +} // namespace srreal namespace mathutils { // EpsilonLess specialization ------------------------------------------------ -template<> +template <> bool EpsilonLess::operator()( - const srreal::R3::Matrix& A, const srreal::R3::Matrix& B) const -{ - bool rv = std::lexicographical_compare( - A.data().begin(), A.data().end(), - B.data().begin(), B.data().end(), *this); - return rv; + const srreal::R3::Matrix& A, const srreal::R3::Matrix& B) const { + bool rv = std::lexicographical_compare( + A.data().begin(), A.data().end(), B.data().begin(), B.data().end(), *this); + return rv; } // EpsilonEqual specializations ---------------------------------------------- -template<> +template <> bool EpsilonEqual::operator()( - const srreal::R3::Vector& u, const srreal::R3::Vector& v) const -{ - bool rv = std::equal(u.begin(), u.end(), v.begin(), *this); - return rv; + const srreal::R3::Vector& u, const srreal::R3::Vector& v) const { + bool rv = std::equal(u.begin(), u.end(), v.begin(), *this); + return rv; } - -template<> +template <> bool EpsilonEqual::operator()( - const srreal::R3::Matrix& A, const srreal::R3::Matrix& B) const -{ - bool rv = std::equal(A.data().begin(), A.data().end(), - B.data().begin(), *this); - return rv; + const srreal::R3::Matrix& A, const srreal::R3::Matrix& B) const { + bool rv = + std::equal(A.data().begin(), A.data().end(), B.data().begin(), *this); + return rv; } -} // namespace mathutils -} // namespace diffpy +} // namespace mathutils +} // namespace diffpy // End of file diff --git a/src/diffpy/srreal/R3linalg.hpp b/src/diffpy/srreal/R3linalg.hpp index 324437ff..5839eda2 100644 --- a/src/diffpy/srreal/R3linalg.hpp +++ b/src/diffpy/srreal/R3linalg.hpp @@ -1,20 +1,20 @@ /***************************************************************************** -* -* 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.txt for license information. -* -****************************************************************************** -* -* R3linalg -- vector and matrix types and linear algebra operations in R3 space -* -*****************************************************************************/ + * + * 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.txt for license information. + * + ****************************************************************************** + * + * R3linalg -- vector and matrix types and linear algebra operations in R3 space + * + *****************************************************************************/ #ifndef R3LINALG_HPP_INCLUDED #define R3LINALG_HPP_INCLUDED @@ -35,9 +35,9 @@ namespace R3 { // Declarations -------------------------------------------------------------- namespace ublas = boost::numeric::ublas; +using ublas::column; using ublas::prod; using ublas::row; -using ublas::column; using ublas::trans; // Constants @@ -48,102 +48,92 @@ const ublas::zero_vector zerovector(Ndim); // Classes -class DLL_EXPORT Vector : public ublas::vector > -{ - typedef ublas::vector > - BaseVector; - - public: - - // constructors - Vector() : BaseVector(3) { } - - Vector(const double& x, const double& y, const double& z) - : BaseVector(3) - { - Vector::iterator xi = this->begin(); - *(xi++) = x; - *(xi++) = y; - *(xi++) = z; - } - - template - Vector(const ublas::vector_expression& r) : BaseVector(r) - { } - - template - void operator=(const ublas::vector_expression& r) - { - BaseVector::operator=(r); - } - - template - void operator=(const BaseVector& r) - { - BaseVector::operator=(r); - } - - private: - - // serialization - friend class boost::serialization::access; - template - void serialize(Archive& ar, const unsigned int version) - { - using boost::serialization::base_object; - ar & base_object(*this); - } +class DLL_EXPORT Vector + : public ublas::vector > { + typedef ublas::vector > BaseVector; + + public: + // constructors + Vector() : BaseVector(3) {} + + Vector(const double& x, const double& y, const double& z) : BaseVector(3) { + Vector::iterator xi = this->begin(); + *(xi++) = x; + *(xi++) = y; + *(xi++) = z; + } + + template + Vector(const ublas::vector_expression& r) : BaseVector(r) {} + + template + void operator=(const ublas::vector_expression& r) { + BaseVector::operator=(r); + } + + template + void operator=(const BaseVector& r) { + BaseVector::operator=(r); + } + + private: + // serialization + friend class boost::serialization::access; + template + void serialize(Archive& ar, const unsigned int version) { + using boost::serialization::base_object; + ar& base_object(*this); + } }; - -class DLL_EXPORT Matrix : public ublas::matrix > -{ - typedef ublas::matrix > BaseMatrix; - - public: - - // constructors - Matrix() : BaseMatrix(3, 3) { } - - Matrix(const double& x0, const double& x1, const double& x2, - const double& x3, const double& x4, const double& x5, - const double& x6, const double& x7, const double& x8) : - BaseMatrix(3, 3) - { - Matrix::array_type::iterator xi = this->data().begin(); - *(xi++) = x0; *(xi++) = x1; *(xi++) = x2; - *(xi++) = x3; *(xi++) = x4; *(xi++) = x5; - *(xi++) = x6; *(xi++) = x7; *(xi++) = x8; - } - - template - Matrix(const ublas::matrix_expression& r) : BaseMatrix(r) - { } - - template - void operator=(const ublas::matrix_expression& r) - { - BaseMatrix::operator=(r); - } - - template - void operator=(const BaseMatrix& r) - { - BaseMatrix::operator=(r); - } - - private: - - // serialization - friend class boost::serialization::access; - template - void serialize(Archive& ar, const unsigned int version) - { - using boost::serialization::base_object; - ar & base_object(*this); - } +class DLL_EXPORT Matrix + : public ublas::matrix > { + typedef ublas::matrix > + BaseMatrix; + + public: + // constructors + Matrix() : BaseMatrix(3, 3) {} + + Matrix(const double& x0, const double& x1, const double& x2, const double& x3, + const double& x4, const double& x5, const double& x6, const double& x7, + const double& x8) + : BaseMatrix(3, 3) { + Matrix::array_type::iterator xi = this->data().begin(); + *(xi++) = x0; + *(xi++) = x1; + *(xi++) = x2; + *(xi++) = x3; + *(xi++) = x4; + *(xi++) = x5; + *(xi++) = x6; + *(xi++) = x7; + *(xi++) = x8; + } + + template + Matrix(const ublas::matrix_expression& r) : BaseMatrix(r) {} + + template + void operator=(const ublas::matrix_expression& r) { + BaseMatrix::operator=(r); + } + + template + void operator=(const BaseMatrix& r) { + BaseMatrix::operator=(r); + } + + private: + // serialization + friend class boost::serialization::access; + template + void serialize(Archive& ar, const unsigned int version) { + using boost::serialization::base_object; + ar& base_object(*this); + } }; // Functions @@ -154,44 +144,35 @@ double determinant(const Matrix& A); const Matrix& inverse(const Matrix& A); const Vector& floor(const Vector&); -template double norm(const V&); -template double distance(const V& u, const V& v); -template double dot(const V& u, const V& v); -template Vector cross(const V& u, const V& v); -template const Vector& mxvecproduct(const Matrix&, const V&); -template const Vector& mxvecproduct(const V&, const Matrix&); +template +double norm(const V&); +template +double distance(const V& u, const V& v); +template +double dot(const V& u, const V& v); +template +Vector cross(const V& u, const V& v); +template +const Vector& mxvecproduct(const Matrix&, const V&); +template +const Vector& mxvecproduct(const V&, const Matrix&); // Equality ------------------------------------------------------------------ -inline -bool operator==(const Vector& u, const Vector& v) -{ - bool rv = std::equal(u.begin(), u.end(), v.begin()); - return rv; +inline bool operator==(const Vector& u, const Vector& v) { + bool rv = std::equal(u.begin(), u.end(), v.begin()); + return rv; } +inline bool operator!=(const Vector& u, const Vector& v) { return !(u == v); } -inline -bool operator!=(const Vector& u, const Vector& v) -{ - return !(u == v); +inline bool operator==(const Matrix& A, const Matrix& B) { + bool rv = (&A == &B) || + std::equal(A.data().begin(), A.data().end(), B.data().begin()); + return rv; } - -inline -bool operator==(const Matrix& A, const Matrix& B) -{ - bool rv = (&A == &B) || - std::equal(A.data().begin(), A.data().end(), B.data().begin()); - return rv; -} - - -inline -bool operator!=(const Matrix& A, const Matrix& B) -{ - return !(A == B); -} +inline bool operator!=(const Matrix& A, const Matrix& B) { return !(A == B); } // Hashing ------------------------------------------------------------------- @@ -200,95 +181,82 @@ size_t hash_value(const Matrix& A); // Inlined functions --------------------------------------------------------- -inline -const Vector& floor(const Vector& v) -{ - static Vector res; - Vector::const_iterator xi = v.begin(); - Vector::iterator xo = res.begin(); - for (; xi != v.end(); ++xi, ++xo) *xo = std::floor(*xi); - return res; +inline const Vector& floor(const Vector& v) { + static Vector res; + Vector::const_iterator xi = v.begin(); + Vector::iterator xo = res.begin(); + for (; xi != v.end(); ++xi, ++xo) *xo = std::floor(*xi); + return res; } // Template functions -------------------------------------------------------- template -double norm(const V& u) -{ - return sqrt(R3::dot(u, u)); +double norm(const V& u) { + return sqrt(R3::dot(u, u)); } - template -double distance(const V& u, const V& v) -{ - static R3::Vector duv; - duv[0] = u[0] - v[0]; - duv[1] = u[1] - v[1]; - duv[2] = u[2] - v[2]; - return R3::norm(duv); +double distance(const V& u, const V& v) { + static R3::Vector duv; + duv[0] = u[0] - v[0]; + duv[1] = u[1] - v[1]; + duv[2] = u[2] - v[2]; + return R3::norm(duv); } - template -double dot(const V& u, const V& v) -{ - return (u[0]*v[0] + u[1]*v[1] + u[2]*v[2]); +double dot(const V& u, const V& v) { + return (u[0] * v[0] + u[1] * v[1] + u[2] * v[2]); } - template -Vector cross(const V& u, const V& v) -{ - Vector res; - res[0] = u[1]*v[2] - u[2]*v[1]; - res[1] = u[2]*v[0] - u[0]*v[2]; - res[2] = u[0]*v[1] - u[1]*v[0]; - return res; +Vector cross(const V& u, const V& v) { + Vector res; + res[0] = u[1] * v[2] - u[2] * v[1]; + res[1] = u[2] * v[0] - u[0] * v[2]; + res[2] = u[0] * v[1] - u[1] * v[0]; + return res; } - template -const Vector& mxvecproduct(const Matrix& M, const V& u) -{ - static Vector res; - res[0] = M(0,0)*u[0] + M(0,1)*u[1] + M(0,2)*u[2]; - res[1] = M(1,0)*u[0] + M(1,1)*u[1] + M(1,2)*u[2]; - res[2] = M(2,0)*u[0] + M(2,1)*u[1] + M(2,2)*u[2]; - return res; +const Vector& mxvecproduct(const Matrix& M, const V& u) { + static Vector res; + res[0] = M(0, 0) * u[0] + M(0, 1) * u[1] + M(0, 2) * u[2]; + res[1] = M(1, 0) * u[0] + M(1, 1) * u[1] + M(1, 2) * u[2]; + res[2] = M(2, 0) * u[0] + M(2, 1) * u[1] + M(2, 2) * u[2]; + return res; } - template -const Vector& mxvecproduct(const V& u, const Matrix& M) -{ - static Vector res; - res[0] = u[0]*M(0,0) + u[1]*M(1,0) + u[2]*M(2,0); - res[1] = u[0]*M(0,1) + u[1]*M(1,1) + u[2]*M(2,1); - res[2] = u[0]*M(0,2) + u[1]*M(1,2) + u[2]*M(2,2); - return res; +const Vector& mxvecproduct(const V& u, const Matrix& M) { + static Vector res; + res[0] = u[0] * M(0, 0) + u[1] * M(1, 0) + u[2] * M(2, 0); + res[1] = u[0] * M(0, 1) + u[1] * M(1, 1) + u[2] * M(2, 1); + res[2] = u[0] * M(0, 2) + u[1] * M(1, 2) + u[2] * M(2, 2); + return res; } -} // namespace R3 -} // namespace srreal +} // namespace R3 +} // namespace srreal namespace mathutils { // Template specializations -------------------------------------------------- -template<> +template <> bool EpsilonLess::operator()( - const srreal::R3::Matrix& A, const srreal::R3::Matrix& B) const; + const srreal::R3::Matrix& A, const srreal::R3::Matrix& B) const; -template<> +template <> bool EpsilonEqual::operator()( - const srreal::R3::Vector& u, const srreal::R3::Vector& v) const; + const srreal::R3::Vector& u, const srreal::R3::Vector& v) const; -template<> +template <> bool EpsilonEqual::operator()( - const srreal::R3::Matrix& A, const srreal::R3::Matrix& B) const; + const srreal::R3::Matrix& A, const srreal::R3::Matrix& B) const; -} // namespace mathutils -} // namespace diffpy +} // namespace mathutils +} // namespace diffpy #endif // R3LINALG_HPP_INCLUDED diff --git a/src/diffpy/srreal/SFTElectron.cpp b/src/diffpy/srreal/SFTElectron.cpp index d85bc661..c80e6e8b 100644 --- a/src/diffpy/srreal/SFTElectron.cpp +++ b/src/diffpy/srreal/SFTElectron.cpp @@ -1,25 +1,25 @@ /***************************************************************************** -* -* 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.txt for license information. -* -****************************************************************************** -* -* class SFTElectron -* -* Implementation of electron ScatteringFactorTable using the formula in -* International Tables Volume C, page 224. -* The instance can be also created by calling the -* createByType("electron") factory. -* -*****************************************************************************/ + * + * 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.txt for license information. + * + ****************************************************************************** + * + * class SFTElectron + * + * Implementation of electron ScatteringFactorTable using the formula in + * International Tables Volume C, page 224. + * The instance can be also created by calling the + * createByType("electron") factory. + * + *****************************************************************************/ #include #include @@ -34,44 +34,38 @@ using namespace std; // HasClassRegistry methods -ScatteringFactorTablePtr SFTElectron::create() const -{ - ScatteringFactorTablePtr rv(new SFTElectron()); - return rv; +ScatteringFactorTablePtr SFTElectron::create() const { + ScatteringFactorTablePtr rv(new SFTElectron()); + return rv; } -ScatteringFactorTablePtr SFTElectron::clone() const -{ - ScatteringFactorTablePtr rv(new SFTElectron(*this)); - return rv; +ScatteringFactorTablePtr SFTElectron::clone() const { + ScatteringFactorTablePtr rv(new SFTElectron(*this)); + return rv; } -const string& SFTElectron::type() const -{ - static string rv = "electron"; - return rv; +const string& SFTElectron::type() const { + static string rv = "electron"; + return rv; } // own methods - overloads -const string& SFTElectron::radiationType() const -{ - static string rv = "E"; - return rv; +const string& SFTElectron::radiationType() const { + static string rv = "E"; + return rv; } - -double SFTElectron::standardLookup(const string& smbl, double q) const -{ - return felectronatq(smbl, q); +double SFTElectron::standardLookup(const string& smbl, double q) const { + return felectronatq(smbl, q); } // Registration -------------------------------------------------------------- bool reg_SFTElectron = SFTElectron().registerThisType(); -} // namespace srreal -} // namespace diffpy +} // namespace srreal +} // namespace diffpy // Serialization ------------------------------------------------------------- diff --git a/src/diffpy/srreal/SFTElectron.hpp b/src/diffpy/srreal/SFTElectron.hpp index ce8c93f9..edaeb49e 100644 --- a/src/diffpy/srreal/SFTElectron.hpp +++ b/src/diffpy/srreal/SFTElectron.hpp @@ -1,24 +1,24 @@ /***************************************************************************** -* -* 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.txt for license information. -* -****************************************************************************** -* -* class SFTElectron -* -* SFTElectron gives Q-dependent electron scattering factor according to -* the formula in International Tables Volume C, page 224. -* The formula diverges at Q = 0, where SFTElectron returns DOUBLE_MAX. -* -*****************************************************************************/ + * + * 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.txt for license information. + * + ****************************************************************************** + * + * class SFTElectron + * + * SFTElectron gives Q-dependent electron scattering factor according to + * the formula in International Tables Volume C, page 224. + * The formula diverges at Q = 0, where SFTElectron returns DOUBLE_MAX. + * + *****************************************************************************/ #ifndef SFTELECTRON_HPP_INCLUDED #define SFTELECTRON_HPP_INCLUDED @@ -31,36 +31,31 @@ namespace diffpy { namespace srreal { -class DLL_EXPORT SFTElectron : public ScatteringFactorTable -{ - public: +class DLL_EXPORT SFTElectron : public ScatteringFactorTable { + public: + // HasClassRegistry methods + ScatteringFactorTablePtr create() const; + ScatteringFactorTablePtr clone() const; + const std::string& type() const; + // own methods + const std::string& radiationType() const; + // method overloads + double standardLookup(const std::string& smbl, double q) const; - // HasClassRegistry methods - ScatteringFactorTablePtr create() const; - ScatteringFactorTablePtr clone() const; - const std::string& type() const; - // own methods - const std::string& radiationType() const; - // method overloads - double standardLookup(const std::string& smbl, double q) const; + private: + // serialization + friend class boost::serialization::access; - private: - - // serialization - friend class boost::serialization::access; - - template - void serialize(Archive& ar, const unsigned int version) - { - using boost::serialization::base_object; - ar & base_object(*this); - } + template + void serialize(Archive& ar, const unsigned int version) { + using boost::serialization::base_object; + ar& base_object(*this); + } }; // class SFTElectron - -} // namespace srreal -} // namespace diffpy +} // namespace srreal +} // namespace diffpy // Serialization ------------------------------------------------------------- diff --git a/src/diffpy/srreal/SFTElectronNumber.cpp b/src/diffpy/srreal/SFTElectronNumber.cpp index a200171c..823586d0 100644 --- a/src/diffpy/srreal/SFTElectronNumber.cpp +++ b/src/diffpy/srreal/SFTElectronNumber.cpp @@ -1,23 +1,23 @@ /***************************************************************************** -* -* 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.txt for license information. -* -****************************************************************************** -* -* class SFTElectronNumber -* -* ScatteringFactorTable implementation where scattering power equals number -* of valence electrons and is constant with Q. -* -*****************************************************************************/ + * + * 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.txt for license information. + * + ****************************************************************************** + * + * class SFTElectronNumber + * + * ScatteringFactorTable implementation where scattering power equals number + * of valence electrons and is constant with Q. + * + *****************************************************************************/ #include #include @@ -32,45 +32,38 @@ namespace srreal { // HasClassRegistry methods -ScatteringFactorTablePtr SFTElectronNumber::create() const -{ - ScatteringFactorTablePtr rv(new SFTElectronNumber()); - return rv; +ScatteringFactorTablePtr SFTElectronNumber::create() const { + ScatteringFactorTablePtr rv(new SFTElectronNumber()); + return rv; } - -ScatteringFactorTablePtr SFTElectronNumber::clone() const -{ - ScatteringFactorTablePtr rv(new SFTElectronNumber(*this)); - return rv; +ScatteringFactorTablePtr SFTElectronNumber::clone() const { + ScatteringFactorTablePtr rv(new SFTElectronNumber(*this)); + return rv; } -const string& SFTElectronNumber::type() const -{ - static string rv = "electronnumber"; - return rv; +const string& SFTElectronNumber::type() const { + static string rv = "electronnumber"; + return rv; } // own methods - overloads -const string& SFTElectronNumber::radiationType() const -{ - static string rv = "EN"; - return rv; +const string& SFTElectronNumber::radiationType() const { + static string rv = "EN"; + return rv; } - -double SFTElectronNumber::standardLookup(const string& smbl, double q) const -{ - return electronnumber(smbl); +double SFTElectronNumber::standardLookup(const string& smbl, double q) const { + return electronnumber(smbl); } // Registration -------------------------------------------------------------- bool reg_SFTElectronNumber = SFTElectronNumber().registerThisType(); -} // namespace srreal -} // namespace diffpy +} // namespace srreal +} // namespace diffpy // Serialization ------------------------------------------------------------- diff --git a/src/diffpy/srreal/SFTElectronNumber.hpp b/src/diffpy/srreal/SFTElectronNumber.hpp index 7d3b5ea9..64fec744 100644 --- a/src/diffpy/srreal/SFTElectronNumber.hpp +++ b/src/diffpy/srreal/SFTElectronNumber.hpp @@ -1,23 +1,23 @@ /***************************************************************************** -* -* 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.txt for license information. -* -****************************************************************************** -* -* class SFTElectronNumber -* -* ScatteringFactorTable with scattering power equal number -* of valence electrons without any Q-dependence -* -*****************************************************************************/ + * + * 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.txt for license information. + * + ****************************************************************************** + * + * class SFTElectronNumber + * + * ScatteringFactorTable with scattering power equal number + * of valence electrons without any Q-dependence + * + *****************************************************************************/ #ifndef SFTELECTRONNUMBER_HPP_INCLUDED #define SFTELECTRONNUMBER_HPP_INCLUDED @@ -29,36 +29,31 @@ namespace diffpy { namespace srreal { -class DLL_EXPORT SFTElectronNumber : public ScatteringFactorTable -{ - public: - - // HasClassRegistry methods - ScatteringFactorTablePtr create() const; - ScatteringFactorTablePtr clone() const; - const std::string& type() const; - // own methods - const std::string& radiationType() const; - // method overloads - double standardLookup(const std::string& smbl, double q) const; - - private: - - // serialization - friend class boost::serialization::access; - - template - void serialize(Archive& ar, const unsigned int version) - { - using boost::serialization::base_object; - ar & base_object(*this); - } +class DLL_EXPORT SFTElectronNumber : public ScatteringFactorTable { + public: + // HasClassRegistry methods + ScatteringFactorTablePtr create() const; + ScatteringFactorTablePtr clone() const; + const std::string& type() const; + // own methods + const std::string& radiationType() const; + // method overloads + double standardLookup(const std::string& smbl, double q) const; + + private: + // serialization + friend class boost::serialization::access; + + template + void serialize(Archive& ar, const unsigned int version) { + using boost::serialization::base_object; + ar& base_object(*this); + } }; // class SFTElectronNumber - -} // namespace srreal -} // namespace diffpy +} // namespace srreal +} // namespace diffpy // Serialization ------------------------------------------------------------- diff --git a/src/diffpy/srreal/SFTNeutron.cpp b/src/diffpy/srreal/SFTNeutron.cpp index 72ec80c3..de963445 100644 --- a/src/diffpy/srreal/SFTNeutron.cpp +++ b/src/diffpy/srreal/SFTNeutron.cpp @@ -1,25 +1,25 @@ /***************************************************************************** -* -* 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.txt for license information. -* -****************************************************************************** -* -* class SFTNeutron -* -* Implementation of neutron ScatteringFactorTable using coherent cross -* sections data from Paul Kienzle periodictable library for Python. -* approximation. The instance can be also created by calling the -* createByType("neutron") factory. -* -*****************************************************************************/ + * + * 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.txt for license information. + * + ****************************************************************************** + * + * class SFTNeutron + * + * Implementation of neutron ScatteringFactorTable using coherent cross + * sections data from Paul Kienzle periodictable library for Python. + * approximation. The instance can be also created by calling the + * createByType("neutron") factory. + * + *****************************************************************************/ #include #include @@ -34,44 +34,38 @@ using namespace std; // HasClassRegistry methods -ScatteringFactorTablePtr SFTNeutron::create() const -{ - ScatteringFactorTablePtr rv(new SFTNeutron()); - return rv; +ScatteringFactorTablePtr SFTNeutron::create() const { + ScatteringFactorTablePtr rv(new SFTNeutron()); + return rv; } -ScatteringFactorTablePtr SFTNeutron::clone() const -{ - ScatteringFactorTablePtr rv(new SFTNeutron(*this)); - return rv; +ScatteringFactorTablePtr SFTNeutron::clone() const { + ScatteringFactorTablePtr rv(new SFTNeutron(*this)); + return rv; } -const string& SFTNeutron::type() const -{ - static string rv = "neutron"; - return rv; +const string& SFTNeutron::type() const { + static string rv = "neutron"; + return rv; } // own methods - overloads -const string& SFTNeutron::radiationType() const -{ - static string rv = "N"; - return rv; +const string& SFTNeutron::radiationType() const { + static string rv = "N"; + return rv; } - -double SFTNeutron::standardLookup(const string& smbl, double q) const -{ - return bcneutron(smbl); +double SFTNeutron::standardLookup(const string& smbl, double q) const { + return bcneutron(smbl); } // Registration -------------------------------------------------------------- bool reg_SFTNeutron = SFTNeutron().registerThisType(); -} // namespace srreal -} // namespace diffpy +} // namespace srreal +} // namespace diffpy // Serialization ------------------------------------------------------------- diff --git a/src/diffpy/srreal/SFTNeutron.hpp b/src/diffpy/srreal/SFTNeutron.hpp index 9712c406..a1cd26bd 100644 --- a/src/diffpy/srreal/SFTNeutron.hpp +++ b/src/diffpy/srreal/SFTNeutron.hpp @@ -1,23 +1,23 @@ /***************************************************************************** -* -* 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.txt for license information. -* -****************************************************************************** -* -* class SFTNeutron -* -* Neutron ScatteringFactorTable using coherent cross sections extracted -* from Paul Kienzle periodictable library for Python. -* -*****************************************************************************/ + * + * 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.txt for license information. + * + ****************************************************************************** + * + * class SFTNeutron + * + * Neutron ScatteringFactorTable using coherent cross sections extracted + * from Paul Kienzle periodictable library for Python. + * + *****************************************************************************/ #ifndef SFTNEUTRON_HPP_INCLUDED #define SFTNEUTRON_HPP_INCLUDED @@ -29,36 +29,31 @@ namespace diffpy { namespace srreal { -class DLL_EXPORT SFTNeutron : public ScatteringFactorTable -{ - public: - - // HasClassRegistry methods - ScatteringFactorTablePtr create() const; - ScatteringFactorTablePtr clone() const; - const std::string& type() const; - // own methods - const std::string& radiationType() const; - // method overloads - double standardLookup(const std::string& smbl, double q) const; - - private: - - // serialization - friend class boost::serialization::access; - - template - void serialize(Archive& ar, const unsigned int version) - { - using boost::serialization::base_object; - ar & base_object(*this); - } +class DLL_EXPORT SFTNeutron : public ScatteringFactorTable { + public: + // HasClassRegistry methods + ScatteringFactorTablePtr create() const; + ScatteringFactorTablePtr clone() const; + const std::string& type() const; + // own methods + const std::string& radiationType() const; + // method overloads + double standardLookup(const std::string& smbl, double q) const; + + private: + // serialization + friend class boost::serialization::access; + + template + void serialize(Archive& ar, const unsigned int version) { + using boost::serialization::base_object; + ar& base_object(*this); + } }; // class SFTNeutron - -} // namespace srreal -} // namespace diffpy +} // namespace srreal +} // namespace diffpy // Serialization ------------------------------------------------------------- diff --git a/src/diffpy/srreal/SFTXray.cpp b/src/diffpy/srreal/SFTXray.cpp index 3b589b93..a37782b6 100644 --- a/src/diffpy/srreal/SFTXray.cpp +++ b/src/diffpy/srreal/SFTXray.cpp @@ -1,24 +1,24 @@ /***************************************************************************** -* -* 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.txt for license information. -* -****************************************************************************** -* -* class SFTXray -* -* Implementation of x-ray ScatteringFactorTable using Waasmaier and Kirfel -* approximation. The instance can be also created by calling the -* createByType("xray") factory. -* -*****************************************************************************/ + * + * 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.txt for license information. + * + ****************************************************************************** + * + * class SFTXray + * + * Implementation of x-ray ScatteringFactorTable using Waasmaier and Kirfel + * approximation. The instance can be also created by calling the + * createByType("xray") factory. + * + *****************************************************************************/ #include #include @@ -33,44 +33,38 @@ using namespace std; // HasClassRegistry methods -ScatteringFactorTablePtr SFTXray::create() const -{ - ScatteringFactorTablePtr rv(new SFTXray()); - return rv; +ScatteringFactorTablePtr SFTXray::create() const { + ScatteringFactorTablePtr rv(new SFTXray()); + return rv; } -ScatteringFactorTablePtr SFTXray::clone() const -{ - ScatteringFactorTablePtr rv(new SFTXray(*this)); - return rv; +ScatteringFactorTablePtr SFTXray::clone() const { + ScatteringFactorTablePtr rv(new SFTXray(*this)); + return rv; } -const string& SFTXray::type() const -{ - static string rv = "xray"; - return rv; +const string& SFTXray::type() const { + static string rv = "xray"; + return rv; } // own methods - overloads -const string& SFTXray::radiationType() const -{ - static string rv = "X"; - return rv; +const string& SFTXray::radiationType() const { + static string rv = "X"; + return rv; } - -double SFTXray::standardLookup(const string& smbl, double q) const -{ - return fxrayatq(smbl, q); +double SFTXray::standardLookup(const string& smbl, double q) const { + return fxrayatq(smbl, q); } // Registration -------------------------------------------------------------- bool reg_SFTXray = SFTXray().registerThisType(); -} // namespace srreal -} // namespace diffpy +} // namespace srreal +} // namespace diffpy // Serialization ------------------------------------------------------------- diff --git a/src/diffpy/srreal/SFTXray.hpp b/src/diffpy/srreal/SFTXray.hpp index fb8ef08a..c2467064 100644 --- a/src/diffpy/srreal/SFTXray.hpp +++ b/src/diffpy/srreal/SFTXray.hpp @@ -1,22 +1,22 @@ /***************************************************************************** -* -* 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.txt for license information. -* -****************************************************************************** -* -* class SFTXray -* -* X-ray ScatteringFactorTable using Waasmaier and Kirfel approximation. -* -*****************************************************************************/ + * + * 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.txt for license information. + * + ****************************************************************************** + * + * class SFTXray + * + * X-ray ScatteringFactorTable using Waasmaier and Kirfel approximation. + * + *****************************************************************************/ #ifndef SFTXRAY_HPP_INCLUDED #define SFTXRAY_HPP_INCLUDED @@ -28,36 +28,31 @@ namespace diffpy { namespace srreal { -class DLL_EXPORT SFTXray : public ScatteringFactorTable -{ - public: - - // HasClassRegistry methods - ScatteringFactorTablePtr create() const; - ScatteringFactorTablePtr clone() const; - const std::string& type() const; - // own methods - const std::string& radiationType() const; - // method overloads - double standardLookup(const std::string& smbl, double q) const; - - private: - - // serialization - friend class boost::serialization::access; - - template - void serialize(Archive& ar, const unsigned int version) - { - using boost::serialization::base_object; - ar & base_object(*this); - } +class DLL_EXPORT SFTXray : public ScatteringFactorTable { + public: + // HasClassRegistry methods + ScatteringFactorTablePtr create() const; + ScatteringFactorTablePtr clone() const; + const std::string& type() const; + // own methods + const std::string& radiationType() const; + // method overloads + double standardLookup(const std::string& smbl, double q) const; + + private: + // serialization + friend class boost::serialization::access; + + template + void serialize(Archive& ar, const unsigned int version) { + using boost::serialization::base_object; + ar& base_object(*this); + } }; // class SFTXray - -} // namespace srreal -} // namespace diffpy +} // namespace srreal +} // namespace diffpy // Serialization ------------------------------------------------------------- diff --git a/src/diffpy/srreal/ScaleEnvelope.cpp b/src/diffpy/srreal/ScaleEnvelope.cpp index 2e3f2685..2248c4c0 100644 --- a/src/diffpy/srreal/ScaleEnvelope.cpp +++ b/src/diffpy/srreal/ScaleEnvelope.cpp @@ -1,20 +1,20 @@ /***************************************************************************** -* -* 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.txt for license information. -* -****************************************************************************** -* -* class ScaleEnvelope -- constant scaling factor -* -*****************************************************************************/ + * + * 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.txt for license information. + * + ****************************************************************************** + * + * class ScaleEnvelope -- constant scaling factor + * + *****************************************************************************/ #include #include @@ -26,60 +26,43 @@ namespace srreal { // Constructors -------------------------------------------------------------- -ScaleEnvelope::ScaleEnvelope() -{ - this->setScale(1.0); - this->registerDoubleAttribute("scale", - this, &ScaleEnvelope::getScale, &ScaleEnvelope::setScale); +ScaleEnvelope::ScaleEnvelope() { + this->setScale(1.0); + this->registerDoubleAttribute("scale", this, &ScaleEnvelope::getScale, + &ScaleEnvelope::setScale); } - -PDFEnvelopePtr ScaleEnvelope::create() const -{ - PDFEnvelopePtr rv(new ScaleEnvelope()); - return rv; +PDFEnvelopePtr ScaleEnvelope::create() const { + PDFEnvelopePtr rv(new ScaleEnvelope()); + return rv; } - -PDFEnvelopePtr ScaleEnvelope::clone() const -{ - PDFEnvelopePtr rv(new ScaleEnvelope(*this)); - return rv; +PDFEnvelopePtr ScaleEnvelope::clone() const { + PDFEnvelopePtr rv(new ScaleEnvelope(*this)); + return rv; } // Public Methods ------------------------------------------------------------ -const string& ScaleEnvelope::type() const -{ - static string rv = "scale"; - return rv; -} - - -double ScaleEnvelope::operator()(const double& r) const -{ - return this->getScale(); +const string& ScaleEnvelope::type() const { + static string rv = "scale"; + return rv; } - -void ScaleEnvelope::setScale(double sc) -{ - mscale = sc; +double ScaleEnvelope::operator()(const double& r) const { + return this->getScale(); } +void ScaleEnvelope::setScale(double sc) { mscale = sc; } -const double& ScaleEnvelope::getScale() const -{ - return mscale; -} - +const double& ScaleEnvelope::getScale() const { return mscale; } // Registration -------------------------------------------------------------- bool reg_ScaleEnvelope = ScaleEnvelope().registerThisType(); -} // namespace srreal -} // namespace diffpy +} // namespace srreal +} // namespace diffpy // Serialization ------------------------------------------------------------- diff --git a/src/diffpy/srreal/ScaleEnvelope.hpp b/src/diffpy/srreal/ScaleEnvelope.hpp index 93d54575..5336f362 100644 --- a/src/diffpy/srreal/ScaleEnvelope.hpp +++ b/src/diffpy/srreal/ScaleEnvelope.hpp @@ -1,20 +1,20 @@ /***************************************************************************** -* -* 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.txt for license information. -* -****************************************************************************** -* -* class ScaleEnvelope -- constant scaling factor -* -*****************************************************************************/ + * + * 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.txt for license information. + * + ****************************************************************************** + * + * class ScaleEnvelope -- constant scaling factor + * + *****************************************************************************/ #ifndef SCALEENVELOPE_HPP_INCLUDED #define SCALEENVELOPE_HPP_INCLUDED @@ -29,41 +29,37 @@ namespace srreal { /// @class ScaleEnvelope /// @brief constant PDF scaling factor -class DLL_EXPORT ScaleEnvelope : public PDFEnvelope -{ - public: - - // constructors - ScaleEnvelope(); - PDFEnvelopePtr create() const; - PDFEnvelopePtr clone() const; - - // methods - const std::string& type() const; - double operator()(const double& r) const; - void setScale(double sc); - const double& getScale() const; - - private: - - // data - double mscale; - - // serialization - friend class boost::serialization::access; - - template - void serialize(Archive& ar, const unsigned int version) - { - using boost::serialization::base_object; - ar & base_object(*this); - ar & mscale; - } +class DLL_EXPORT ScaleEnvelope : public PDFEnvelope { + public: + // constructors + ScaleEnvelope(); + PDFEnvelopePtr create() const; + PDFEnvelopePtr clone() const; + + // methods + const std::string& type() const; + double operator()(const double& r) const; + void setScale(double sc); + const double& getScale() const; + + private: + // data + double mscale; + + // serialization + friend class boost::serialization::access; + + template + void serialize(Archive& ar, const unsigned int version) { + using boost::serialization::base_object; + ar& base_object(*this); + ar & mscale; + } }; // class ScaleEnvelope -} // namespace srreal -} // namespace diffpy +} // namespace srreal +} // namespace diffpy // Serialization ------------------------------------------------------------- diff --git a/src/diffpy/srreal/ScatteringFactorTable.cpp b/src/diffpy/srreal/ScatteringFactorTable.cpp index 4af77224..d68552ed 100644 --- a/src/diffpy/srreal/ScatteringFactorTable.cpp +++ b/src/diffpy/srreal/ScatteringFactorTable.cpp @@ -1,20 +1,20 @@ /***************************************************************************** -* -* 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.txt for license information. -* -****************************************************************************** -* -* class ScatteringFactorTable -- base class for looking up scattering factors -* -*****************************************************************************/ + * + * 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.txt for license information. + * + ****************************************************************************** + * + * class ScatteringFactorTable -- base class for looking up scattering factors + * + *****************************************************************************/ #include #include @@ -35,131 +35,104 @@ namespace srreal { // public methods -bool ScatteringFactorTable::registerThisType() const -{ - typedef diffpy::HasClassRegistry HCRBase; - bool rv = this->HCRBase::registerThisType(); - rv = rv && ScatteringFactorTable::aliasType( - this->type(), this->radiationType()); - return rv; +bool ScatteringFactorTable::registerThisType() const { + typedef diffpy::HasClassRegistry HCRBase; + bool rv = this->HCRBase::registerThisType(); + rv = + rv && ScatteringFactorTable::aliasType(this->type(), this->radiationType()); + return rv; } - -double ScatteringFactorTable::lookup(const string& smbl, double q) const -{ - double rv; - CustomDataStorage::const_iterator csft = mcustom.find(smbl); - if (csft != mcustom.end()) - { - const string& srcsmbl = csft->second.first; - const double& scale = csft->second.second; - rv = this->standardLookup(srcsmbl, q) * scale; - } - else { - rv = this->standardLookup(smbl, q); - } - return rv; +double ScatteringFactorTable::lookup(const string& smbl, double q) const { + double rv; + CustomDataStorage::const_iterator csft = mcustom.find(smbl); + if (csft != mcustom.end()) { + const string& srcsmbl = csft->second.first; + const double& scale = csft->second.second; + rv = this->standardLookup(srcsmbl, q) * scale; + } else { + rv = this->standardLookup(smbl, q); + } + return rv; } - -void ScatteringFactorTable::setCustomAs( - const string& smbl, const string& srcsmbl) -{ - const double scale = 1.0; - CustomDataStorage::mapped_type entry(srcsmbl, scale); - if (mcustom.count(smbl) && mcustom.at(smbl) == entry) return; - mcustom[smbl] = entry; - mticker.click(); +void ScatteringFactorTable::setCustomAs(const string& smbl, + const string& srcsmbl) { + const double scale = 1.0; + CustomDataStorage::mapped_type entry(srcsmbl, scale); + if (mcustom.count(smbl) && mcustom.at(smbl) == entry) return; + mcustom[smbl] = entry; + mticker.click(); } - -void ScatteringFactorTable::setCustomAs( - const string& smbl, const string& srcsmbl, - double value, double q) -{ - double fsrc = this->standardLookup(srcsmbl, q); - double scale = value / fsrc; - CustomDataStorage::mapped_type entry(srcsmbl, scale); - if (mcustom.count(smbl) && mcustom.at(smbl) == entry) return; - mcustom[smbl] = entry; - mticker.click(); +void ScatteringFactorTable::setCustomAs(const string& smbl, + const string& srcsmbl, double value, + double q) { + double fsrc = this->standardLookup(srcsmbl, q); + double scale = value / fsrc; + CustomDataStorage::mapped_type entry(srcsmbl, scale); + if (mcustom.count(smbl) && mcustom.at(smbl) == entry) return; + mcustom[smbl] = entry; + mticker.click(); } - -void ScatteringFactorTable::resetCustom(const string& smbl) -{ - if (mcustom.count(smbl)) mticker.click(); - mcustom.erase(smbl); +void ScatteringFactorTable::resetCustom(const string& smbl) { + if (mcustom.count(smbl)) mticker.click(); + mcustom.erase(smbl); } - -void ScatteringFactorTable::resetAll() -{ - if (!mcustom.empty()) mticker.click(); - mcustom.clear(); +void ScatteringFactorTable::resetAll() { + if (!mcustom.empty()) mticker.click(); + mcustom.clear(); } - -unordered_set ScatteringFactorTable::getCustomSymbols() const -{ - unordered_set rv; - CustomDataStorage::const_iterator csft; - for (csft = mcustom.begin(); csft != mcustom.end(); ++csft) - { - rv.insert(csft->first); - } - return rv; +unordered_set ScatteringFactorTable::getCustomSymbols() const { + unordered_set rv; + CustomDataStorage::const_iterator csft; + for (csft = mcustom.begin(); csft != mcustom.end(); ++csft) { + rv.insert(csft->first); + } + return rv; } // class ScatteringFactorTableOwner ------------------------------------------ void ScatteringFactorTableOwner::setScatteringFactorTable( - ScatteringFactorTablePtr sft) -{ - ensureNonNull("ScatteringFactorTable", sft); - if (msftable != sft) mprivateticker.click(); - msftable = sft; + ScatteringFactorTablePtr sft) { + ensureNonNull("ScatteringFactorTable", sft); + if (msftable != sft) mprivateticker.click(); + msftable = sft; } - void ScatteringFactorTableOwner::setScatteringFactorTableByType( - const string& tp) -{ - msftable = ScatteringFactorTable::createByType(tp); - mprivateticker.click(); + const string& tp) { + msftable = ScatteringFactorTable::createByType(tp); + mprivateticker.click(); } - ScatteringFactorTablePtr& -ScatteringFactorTableOwner::getScatteringFactorTable() -{ - return msftable; +ScatteringFactorTableOwner::getScatteringFactorTable() { + return msftable; } - const ScatteringFactorTablePtr& -ScatteringFactorTableOwner::getScatteringFactorTable() const -{ - return msftable; +ScatteringFactorTableOwner::getScatteringFactorTable() const { + return msftable; } - -const string& ScatteringFactorTableOwner::getRadiationType() const -{ - static string empty; - const string& tp = msftable.get() ? msftable->radiationType() : empty; - return tp; +const string& ScatteringFactorTableOwner::getRadiationType() const { + static string empty; + const string& tp = msftable.get() ? msftable->radiationType() : empty; + return tp; } - -eventticker::EventTicker& ScatteringFactorTableOwner::ticker() const -{ - if (msftable) mprivateticker.updateFrom(msftable->ticker()); - return mprivateticker; +eventticker::EventTicker& ScatteringFactorTableOwner::ticker() const { + if (msftable) mprivateticker.updateFrom(msftable->ticker()); + return mprivateticker; } -} // namespace srreal -} // namespace diffpy +} // namespace srreal +} // namespace diffpy // Serialization ------------------------------------------------------------- diff --git a/src/diffpy/srreal/ScatteringFactorTable.hpp b/src/diffpy/srreal/ScatteringFactorTable.hpp index 3e399744..0199e333 100644 --- a/src/diffpy/srreal/ScatteringFactorTable.hpp +++ b/src/diffpy/srreal/ScatteringFactorTable.hpp @@ -1,23 +1,23 @@ /***************************************************************************** -* -* 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.txt for license information. -* -****************************************************************************** -* -* class ScatteringFactorTable -- base class for looking up scattering factors -* -* class ScatteringFactorTableOwner -- to be used as a base class for classes -* that own ScatteringFactorTable -* -*****************************************************************************/ + * + * 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.txt for license information. + * + ****************************************************************************** + * + * class ScatteringFactorTable -- base class for looking up scattering factors + * + * class ScatteringFactorTableOwner -- to be used as a base class for classes + * that own ScatteringFactorTable + * + *****************************************************************************/ #ifndef SCATTERINGFACTORTABLE_HPP_INCLUDED #define SCATTERINGFACTORTABLE_HPP_INCLUDED @@ -38,78 +38,68 @@ namespace diffpy { namespace srreal { -class DLL_EXPORT ScatteringFactorTable : - public diffpy::HasClassRegistry -{ - public: - - // HasClassRegistry override - bool registerThisType() const; - - // own methods - virtual const std::string& radiationType() const = 0; - double lookup(const std::string& smbl, double q=0.0) const; - virtual double standardLookup(const std::string&, double) const = 0; - void setCustomAs(const std::string& smbl, const std::string& srcsmbl); - void setCustomAs(const std::string& smbl, const std::string& srcsmbl, - double value, double q=0.0); - void resetCustom(const std::string& smbl); - void resetAll(); - std::unordered_set getCustomSymbols() const; - - typedef std::unordered_map > CustomDataStorage; - virtual eventticker::EventTicker& ticker() const { return mticker; } - - protected: - - // data - CustomDataStorage mcustom; - mutable eventticker::EventTicker mticker; - - private: - - // serialization - friend class boost::serialization::access; - template - void serialize(Archive& ar, const unsigned int version) - { - ar & mcustom & mticker; - } - +class DLL_EXPORT ScatteringFactorTable + : public diffpy::HasClassRegistry { + public: + // HasClassRegistry override + bool registerThisType() const; + + // own methods + virtual const std::string& radiationType() const = 0; + double lookup(const std::string& smbl, double q = 0.0) const; + virtual double standardLookup(const std::string&, double) const = 0; + void setCustomAs(const std::string& smbl, const std::string& srcsmbl); + void setCustomAs(const std::string& smbl, const std::string& srcsmbl, + double value, double q = 0.0); + void resetCustom(const std::string& smbl); + void resetAll(); + std::unordered_set getCustomSymbols() const; + + typedef std::unordered_map > + CustomDataStorage; + virtual eventticker::EventTicker& ticker() const { return mticker; } + + protected: + // data + CustomDataStorage mcustom; + mutable eventticker::EventTicker mticker; + + private: + // serialization + friend class boost::serialization::access; + template + void serialize(Archive& ar, const unsigned int version) { + ar & mcustom & mticker; + } }; typedef ScatteringFactorTable::SharedPtr ScatteringFactorTablePtr; -class DLL_EXPORT ScatteringFactorTableOwner -{ - public: - - // access and configuration of scattering factors - void setScatteringFactorTable(ScatteringFactorTablePtr); - void setScatteringFactorTableByType(const std::string& tp); - ScatteringFactorTablePtr& getScatteringFactorTable(); - const ScatteringFactorTablePtr& getScatteringFactorTable() const; - const std::string& getRadiationType() const; - eventticker::EventTicker& ticker() const; - - private: - - // data - ScatteringFactorTablePtr msftable; - mutable eventticker::EventTicker mprivateticker; - - // serialization - friend class boost::serialization::access; - template - void serialize(Archive& ar, const unsigned int version) - { - ar & msftable & mprivateticker; - } +class DLL_EXPORT ScatteringFactorTableOwner { + public: + // access and configuration of scattering factors + void setScatteringFactorTable(ScatteringFactorTablePtr); + void setScatteringFactorTableByType(const std::string& tp); + ScatteringFactorTablePtr& getScatteringFactorTable(); + const ScatteringFactorTablePtr& getScatteringFactorTable() const; + const std::string& getRadiationType() const; + eventticker::EventTicker& ticker() const; + + private: + // data + ScatteringFactorTablePtr msftable; + mutable eventticker::EventTicker mprivateticker; + + // serialization + friend class boost::serialization::access; + template + void serialize(Archive& ar, const unsigned int version) { + ar & msftable & mprivateticker; + } }; -} // namespace srreal -} // namespace diffpy +} // namespace srreal +} // namespace diffpy // Serialization ------------------------------------------------------------- diff --git a/src/diffpy/srreal/SphericalShapeEnvelope.cpp b/src/diffpy/srreal/SphericalShapeEnvelope.cpp index f5c2e0d8..26b1a47f 100644 --- a/src/diffpy/srreal/SphericalShapeEnvelope.cpp +++ b/src/diffpy/srreal/SphericalShapeEnvelope.cpp @@ -1,20 +1,20 @@ /***************************************************************************** -* -* 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.txt for license information. -* -****************************************************************************** -* -* class SphericalShapeEnvelope -- PDF envelope due to spherical shape factor -* -*****************************************************************************/ + * + * 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.txt for license information. + * + ****************************************************************************** + * + * class SphericalShapeEnvelope -- PDF envelope due to spherical shape factor + * + *****************************************************************************/ #include @@ -28,64 +28,50 @@ namespace srreal { // Constructor --------------------------------------------------------------- -SphericalShapeEnvelope::SphericalShapeEnvelope() -{ - this->setSPDiameter(0.0); - this->registerDoubleAttribute("spdiameter", this, - &SphericalShapeEnvelope::getSPDiameter, - &SphericalShapeEnvelope::setSPDiameter); +SphericalShapeEnvelope::SphericalShapeEnvelope() { + this->setSPDiameter(0.0); + this->registerDoubleAttribute("spdiameter", this, + &SphericalShapeEnvelope::getSPDiameter, + &SphericalShapeEnvelope::setSPDiameter); } - -PDFEnvelopePtr SphericalShapeEnvelope::create() const -{ - PDFEnvelopePtr rv(new SphericalShapeEnvelope()); - return rv; +PDFEnvelopePtr SphericalShapeEnvelope::create() const { + PDFEnvelopePtr rv(new SphericalShapeEnvelope()); + return rv; } - -PDFEnvelopePtr SphericalShapeEnvelope::clone() const -{ - PDFEnvelopePtr rv(new SphericalShapeEnvelope(*this)); - return rv; +PDFEnvelopePtr SphericalShapeEnvelope::clone() const { + PDFEnvelopePtr rv(new SphericalShapeEnvelope(*this)); + return rv; } // Public Methods ------------------------------------------------------------ -const string& SphericalShapeEnvelope::type() const -{ - static string rv = "sphericalshape"; - return rv; -} - - -double SphericalShapeEnvelope::operator()(const double& r) const -{ - if (mspdiameter <= 0.0) return 1.0; - if (r > mspdiameter) return 0.0; - double rdratio = r / mspdiameter; - double rv = 1.0 - 1.5 * rdratio + 0.5 * pow(rdratio, 3); - return rv; +const string& SphericalShapeEnvelope::type() const { + static string rv = "sphericalshape"; + return rv; } - -void SphericalShapeEnvelope::setSPDiameter(double spd) -{ - mspdiameter = spd; +double SphericalShapeEnvelope::operator()(const double& r) const { + if (mspdiameter <= 0.0) return 1.0; + if (r > mspdiameter) return 0.0; + double rdratio = r / mspdiameter; + double rv = 1.0 - 1.5 * rdratio + 0.5 * pow(rdratio, 3); + return rv; } +void SphericalShapeEnvelope::setSPDiameter(double spd) { mspdiameter = spd; } -const double& SphericalShapeEnvelope::getSPDiameter() const -{ - return mspdiameter; +const double& SphericalShapeEnvelope::getSPDiameter() const { + return mspdiameter; } // Registration -------------------------------------------------------------- bool reg_SphericalShapeEnvelope = SphericalShapeEnvelope().registerThisType(); -} // namespace srreal -} // namespace diffpy +} // namespace srreal +} // namespace diffpy // Serialization ------------------------------------------------------------- diff --git a/src/diffpy/srreal/SphericalShapeEnvelope.hpp b/src/diffpy/srreal/SphericalShapeEnvelope.hpp index 4f543764..287f191f 100644 --- a/src/diffpy/srreal/SphericalShapeEnvelope.hpp +++ b/src/diffpy/srreal/SphericalShapeEnvelope.hpp @@ -1,20 +1,20 @@ /***************************************************************************** -* -* 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.txt for license information. -* -****************************************************************************** -* -* class SphericalShapeEnvelope -- PDF envelope due to spherical shape factor -* -*****************************************************************************/ + * + * 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.txt for license information. + * + ****************************************************************************** + * + * class SphericalShapeEnvelope -- PDF envelope due to spherical shape factor + * + *****************************************************************************/ #ifndef SPHERICALSHAPEENVELOPE_HPP_INCLUDED #define SPHERICALSHAPEENVELOPE_HPP_INCLUDED @@ -31,41 +31,37 @@ namespace srreal { /// Spherical shape envelope is not applied for spherical particle /// diameter, spdiameter, smaller or equal to zero. -class DLL_EXPORT SphericalShapeEnvelope : public PDFEnvelope -{ - public: - - // constructors - SphericalShapeEnvelope(); - virtual PDFEnvelopePtr create() const; - virtual PDFEnvelopePtr clone() const; - - // methods - virtual const std::string& type() const; - virtual double operator()(const double& r) const; - void setSPDiameter(double spd); - const double& getSPDiameter() const; - - private: - - // data - double mspdiameter; - - // serialization - friend class boost::serialization::access; - - template - void serialize(Archive& ar, const unsigned int version) - { - using boost::serialization::base_object; - ar & base_object(*this); - ar & mspdiameter; - } +class DLL_EXPORT SphericalShapeEnvelope : public PDFEnvelope { + public: + // constructors + SphericalShapeEnvelope(); + virtual PDFEnvelopePtr create() const; + virtual PDFEnvelopePtr clone() const; + + // methods + virtual const std::string& type() const; + virtual double operator()(const double& r) const; + void setSPDiameter(double spd); + const double& getSPDiameter() const; + + private: + // data + double mspdiameter; + + // serialization + friend class boost::serialization::access; + + template + void serialize(Archive& ar, const unsigned int version) { + using boost::serialization::base_object; + ar& base_object(*this); + ar & mspdiameter; + } }; // class SphericalShapeEnvelope -} // namespace srreal -} // namespace diffpy +} // namespace srreal +} // namespace diffpy // Serialization ------------------------------------------------------------- diff --git a/src/diffpy/srreal/StepCutEnvelope.cpp b/src/diffpy/srreal/StepCutEnvelope.cpp index 700cf781..8f6aa509 100644 --- a/src/diffpy/srreal/StepCutEnvelope.cpp +++ b/src/diffpy/srreal/StepCutEnvelope.cpp @@ -1,20 +1,20 @@ /***************************************************************************** -* -* 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.txt for license information. -* -****************************************************************************** -* -* class StepCutEnvelope -- empirical step-function PDF envelope. -* -*****************************************************************************/ + * + * 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.txt for license information. + * + ****************************************************************************** + * + * class StepCutEnvelope -- empirical step-function PDF envelope. + * + *****************************************************************************/ #include #include @@ -26,60 +26,44 @@ namespace srreal { // Constructor --------------------------------------------------------------- -StepCutEnvelope::StepCutEnvelope() -{ - this->setStepCut(0.0); - this->registerDoubleAttribute("stepcut", this, - &StepCutEnvelope::getStepCut, &StepCutEnvelope::setStepCut); +StepCutEnvelope::StepCutEnvelope() { + this->setStepCut(0.0); + this->registerDoubleAttribute("stepcut", this, &StepCutEnvelope::getStepCut, + &StepCutEnvelope::setStepCut); } - -PDFEnvelopePtr StepCutEnvelope::create() const -{ - PDFEnvelopePtr rv(new StepCutEnvelope()); - return rv; +PDFEnvelopePtr StepCutEnvelope::create() const { + PDFEnvelopePtr rv(new StepCutEnvelope()); + return rv; } - -PDFEnvelopePtr StepCutEnvelope::clone() const -{ - PDFEnvelopePtr rv(new StepCutEnvelope(*this)); - return rv; +PDFEnvelopePtr StepCutEnvelope::clone() const { + PDFEnvelopePtr rv(new StepCutEnvelope(*this)); + return rv; } // Public Methods ------------------------------------------------------------ -const string& StepCutEnvelope::type() const -{ - static string rv = "stepcut"; - return rv; +const string& StepCutEnvelope::type() const { + static string rv = "stepcut"; + return rv; } - -double StepCutEnvelope::operator()(const double& r) const -{ - double rv = (mstepcut > 0.0 && r > mstepcut) ? 0.0 : 1.0; - return rv; +double StepCutEnvelope::operator()(const double& r) const { + double rv = (mstepcut > 0.0 && r > mstepcut) ? 0.0 : 1.0; + return rv; } +void StepCutEnvelope::setStepCut(double sc) { mstepcut = sc; } -void StepCutEnvelope::setStepCut(double sc) -{ - mstepcut = sc; -} - - -const double& StepCutEnvelope::getStepCut() const -{ - return mstepcut; -} +const double& StepCutEnvelope::getStepCut() const { return mstepcut; } // Registration -------------------------------------------------------------- bool reg_StepCutEnvelope = StepCutEnvelope().registerThisType(); -} // namespace srreal -} // namespace diffpy +} // namespace srreal +} // namespace diffpy // Serialization ------------------------------------------------------------- diff --git a/src/diffpy/srreal/StepCutEnvelope.hpp b/src/diffpy/srreal/StepCutEnvelope.hpp index 736b7bd7..61b27a26 100644 --- a/src/diffpy/srreal/StepCutEnvelope.hpp +++ b/src/diffpy/srreal/StepCutEnvelope.hpp @@ -1,20 +1,20 @@ /***************************************************************************** -* -* 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.txt for license information. -* -****************************************************************************** -* -* class StepCutEnvelope -- empirical step-function PDF envelope. -* -*****************************************************************************/ + * + * 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.txt for license information. + * + ****************************************************************************** + * + * class StepCutEnvelope -- empirical step-function PDF envelope. + * + *****************************************************************************/ #ifndef STEPCUTENVELOPE_HPP_INCLUDED #define STEPCUTENVELOPE_HPP_INCLUDED @@ -30,42 +30,38 @@ namespace srreal { /// @brief empirical step-function PDF envelope. The envelope is not /// applied when the cutoff radius stepcut is smaller or equal zero. -class DLL_EXPORT StepCutEnvelope : public PDFEnvelope -{ - public: +class DLL_EXPORT StepCutEnvelope : public PDFEnvelope { + public: + // constructors + StepCutEnvelope(); + virtual PDFEnvelopePtr create() const; + virtual PDFEnvelopePtr clone() const; - // constructors - StepCutEnvelope(); - virtual PDFEnvelopePtr create() const; - virtual PDFEnvelopePtr clone() const; + // methods - // methods + virtual const std::string& type() const; + virtual double operator()(const double& r) const; + void setStepCut(double sc); + const double& getStepCut() const; - virtual const std::string& type() const; - virtual double operator()(const double& r) const; - void setStepCut(double sc); - const double& getStepCut() const; + private: + // data + double mstepcut; - private: + // serialization + friend class boost::serialization::access; - // data - double mstepcut; - - // serialization - friend class boost::serialization::access; - - template - void serialize(Archive& ar, const unsigned int version) - { - using boost::serialization::base_object; - ar & base_object(*this); - ar & mstepcut; - } + template + void serialize(Archive& ar, const unsigned int version) { + using boost::serialization::base_object; + ar& base_object(*this); + ar & mstepcut; + } }; // class StepCutEnvelope -} // namespace srreal -} // namespace diffpy +} // namespace srreal +} // namespace diffpy // Serialization ------------------------------------------------------------- diff --git a/src/diffpy/srreal/StructureAdapter.cpp b/src/diffpy/srreal/StructureAdapter.cpp index 954c27d7..e95fe68f 100644 --- a/src/diffpy/srreal/StructureAdapter.cpp +++ b/src/diffpy/srreal/StructureAdapter.cpp @@ -1,21 +1,21 @@ /***************************************************************************** -* -* 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.txt for license information. -* -****************************************************************************** -* -* class StructureAdapter -- abstract base class for interfacing general -* structure objects with srreal classes such as PairQuantity -* -*****************************************************************************/ + * + * 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.txt for license information. + * + ****************************************************************************** + * + * class StructureAdapter -- abstract base class for interfacing general + * structure objects with srreal classes such as PairQuantity + * + *****************************************************************************/ #include #include @@ -33,98 +33,70 @@ namespace srreal { // Public Methods ------------------------------------------------------------ -double StructureAdapter::totalOccupancy() const -{ - double total_occupancy = 0.0; - int cntsites = this->countSites(); - for (int i = 0; i < cntsites; ++i) - { - total_occupancy += this->siteOccupancy(i) * this->siteMultiplicity(i); - } - return total_occupancy; -} - - -double StructureAdapter::numberDensity() const -{ - return 0.0; -} - - -const string& StructureAdapter::siteAtomType(int idx) const -{ - static string rv = ""; - return rv; +double StructureAdapter::totalOccupancy() const { + double total_occupancy = 0.0; + int cntsites = this->countSites(); + for (int i = 0; i < cntsites; ++i) { + total_occupancy += this->siteOccupancy(i) * this->siteMultiplicity(i); + } + return total_occupancy; } +double StructureAdapter::numberDensity() const { return 0.0; } -int StructureAdapter::siteMultiplicity(int idx) const -{ - return 1; +const string& StructureAdapter::siteAtomType(int idx) const { + static string rv = ""; + return rv; } +int StructureAdapter::siteMultiplicity(int idx) const { return 1; } -double StructureAdapter::siteOccupancy(int idx) const -{ - return 1.0; -} - +double StructureAdapter::siteOccupancy(int idx) const { return 1.0; } -StructureDifference -StructureAdapter::diff(StructureAdapterConstPtr other) const -{ - StructureDifference sd(this->shared_from_this(), other); - return sd; +StructureDifference StructureAdapter::diff( + StructureAdapterConstPtr other) const { + StructureDifference sd(this->shared_from_this(), other); + return sd; } // Routines ------------------------------------------------------------------ -double meanSquareDisplacement(const R3::Matrix& Uijcartn, - const R3::Vector& s, bool anisotropy) -{ - double rv; - if (anisotropy) - { - assert(R3::norm(s) > 0); - assert(eps_eq(Uijcartn(0,1), Uijcartn(1,0))); - assert(eps_eq(Uijcartn(0,2), Uijcartn(2,0))); - assert(eps_eq(Uijcartn(1,2), Uijcartn(2,1))); - static R3::Vector sn; - sn = s / R3::norm(s); - rv = Uijcartn(0,0) * sn(0) * sn(0) + - Uijcartn(1,1) * sn(1) * sn(1) + - Uijcartn(2,2) * sn(2) * sn(2) + - 2 * Uijcartn(0,1) * sn(0) * sn(1) + - 2 * Uijcartn(0,2) * sn(0) * sn(2) + - 2 * Uijcartn(1,2) * sn(1) * sn(2); - } - else - { - assert(eps_eq(Uijcartn(0,0), Uijcartn(1,1))); - assert(eps_eq(Uijcartn(0,0), Uijcartn(2,2))); - rv = Uijcartn(0,0); - } - return rv; +double meanSquareDisplacement(const R3::Matrix& Uijcartn, const R3::Vector& s, + bool anisotropy) { + double rv; + if (anisotropy) { + assert(R3::norm(s) > 0); + assert(eps_eq(Uijcartn(0, 1), Uijcartn(1, 0))); + assert(eps_eq(Uijcartn(0, 2), Uijcartn(2, 0))); + assert(eps_eq(Uijcartn(1, 2), Uijcartn(2, 1))); + static R3::Vector sn; + sn = s / R3::norm(s); + rv = Uijcartn(0, 0) * sn(0) * sn(0) + Uijcartn(1, 1) * sn(1) * sn(1) + + Uijcartn(2, 2) * sn(2) * sn(2) + 2 * Uijcartn(0, 1) * sn(0) * sn(1) + + 2 * Uijcartn(0, 2) * sn(0) * sn(2) + + 2 * Uijcartn(1, 2) * sn(1) * sn(2); + } else { + assert(eps_eq(Uijcartn(0, 0), Uijcartn(1, 1))); + assert(eps_eq(Uijcartn(0, 0), Uijcartn(2, 2))); + rv = Uijcartn(0, 0); + } + return rv; } - -double maxUii(StructureAdapterPtr stru) -{ - if (!stru) return 0.0; - double rv = 0.0; - for (int i = 0; i < stru->countSites(); ++i) - { - const R3::Matrix& U = stru->siteCartesianUij(i); - for (int k = 0; k < R3::Ndim; k++) - { - if (U(k,k) > rv) rv = U(k,k); - } +double maxUii(StructureAdapterPtr stru) { + if (!stru) return 0.0; + double rv = 0.0; + for (int i = 0; i < stru->countSites(); ++i) { + const R3::Matrix& U = stru->siteCartesianUij(i); + for (int k = 0; k < R3::Ndim; k++) { + if (U(k, k) > rv) rv = U(k, k); } - return rv; + } + return rv; } -} // namespace srreal -} // namespace diffpy +} // namespace srreal +} // namespace diffpy DIFFPY_INSTANTIATE_PTR_SERIALIZATION(diffpy::srreal::StructureAdapter) diff --git a/src/diffpy/srreal/StructureAdapter.hpp b/src/diffpy/srreal/StructureAdapter.hpp index 9071ed73..5aae9226 100644 --- a/src/diffpy/srreal/StructureAdapter.hpp +++ b/src/diffpy/srreal/StructureAdapter.hpp @@ -1,21 +1,21 @@ /***************************************************************************** -* -* 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.txt for license information. -* -****************************************************************************** -* -* class StructureAdapter -- abstract base class for interfacing general -* structure objects with srreal classes such as PairQuantity -* -*****************************************************************************/ + * + * 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.txt for license information. + * + ****************************************************************************** + * + * class StructureAdapter -- abstract base class for interfacing general + * structure objects with srreal classes such as PairQuantity + * + *****************************************************************************/ #ifndef STRUCTUREADAPTER_HPP_INCLUDED #define STRUCTUREADAPTER_HPP_INCLUDED @@ -39,70 +39,64 @@ namespace srreal { class PairQuantity; class StructureDifference; - /// @class StructureAdapter /// @brief abstract adaptor to structure data needed by /// PairQuantity calculator -class DLL_EXPORT StructureAdapter : - public std::enable_shared_from_this -{ - public: - - virtual ~StructureAdapter() { } - - // methods - virtual StructureAdapterPtr clone() const = 0; +class DLL_EXPORT StructureAdapter + : public std::enable_shared_from_this { + public: + virtual ~StructureAdapter() {} - /// factory for creating compatible BondGenerator instance. - virtual BaseBondGeneratorPtr createBondGenerator() const = 0; + // methods + virtual StructureAdapterPtr clone() const = 0; - /// number of independent sites in the structure, before - /// any symmetry expansion. - virtual int countSites() const = 0; + /// factory for creating compatible BondGenerator instance. + virtual BaseBondGeneratorPtr createBondGenerator() const = 0; - /// total number of atoms in the structure unit accounting - /// for possibly fractional occupancies. - double totalOccupancy() const; + /// number of independent sites in the structure, before + /// any symmetry expansion. + virtual int countSites() const = 0; - /// number density in the structure model or 0 when not defined. - virtual double numberDensity() const; + /// total number of atoms in the structure unit accounting + /// for possibly fractional occupancies. + double totalOccupancy() const; - /// symbol for element or ion at the independent site @param idx - virtual const std::string& siteAtomType(int idx) const; + /// number density in the structure model or 0 when not defined. + virtual double numberDensity() const; - /// Cartesian coordinates of the independent site @param idx - virtual const R3::Vector& siteCartesianPosition(int idx) const = 0; + /// symbol for element or ion at the independent site @param idx + virtual const std::string& siteAtomType(int idx) const; - /// multiplicity of the independent site @param idx in the structure - virtual int siteMultiplicity(int idx) const; + /// Cartesian coordinates of the independent site @param idx + virtual const R3::Vector& siteCartesianPosition(int idx) const = 0; - /// site occupancy at the independent site @param idx - virtual double siteOccupancy(int idx) const; + /// multiplicity of the independent site @param idx in the structure + virtual int siteMultiplicity(int idx) const; - /// flag for anisotropic atom displacements at independent site - /// @param idx - virtual bool siteAnisotropy(int idx) const = 0; + /// site occupancy at the independent site @param idx + virtual double siteOccupancy(int idx) const; - /// tensor of atom displacement parameters converted in - /// Cartesian coordinate system at independent site @param idx - virtual const R3::Matrix& siteCartesianUij(int idx) const = 0; + /// flag for anisotropic atom displacements at independent site + /// @param idx + virtual bool siteAnisotropy(int idx) const = 0; - /// this method allows custom special configuration for a concrete - /// pair of StructureAdapter and PairQuantity objects. - [[deprecated]] virtual void customPQConfig(PairQuantity* pq) const - { } + /// tensor of atom displacement parameters converted in + /// Cartesian coordinate system at independent site @param idx + virtual const R3::Matrix& siteCartesianUij(int idx) const = 0; - /// Return difference from the other StructureAdapter - virtual StructureDifference diff(StructureAdapterConstPtr) const; + /// this method allows custom special configuration for a concrete + /// pair of StructureAdapter and PairQuantity objects. + [[deprecated]] virtual void customPQConfig(PairQuantity* pq) const {} - private: - - // serialization - friend class boost::serialization::access; - template - void serialize(Archive& ar, const unsigned int version) { } + /// Return difference from the other StructureAdapter + virtual StructureDifference diff(StructureAdapterConstPtr) const; + private: + // serialization + friend class boost::serialization::access; + template + void serialize(Archive& ar, const unsigned int version) {} }; // Routines ------------------------------------------------------------------ @@ -112,25 +106,23 @@ DLL_EXPORT StructureAdapterPtr emptyStructureAdapter(); /// Calculate MSD along specified direction in Cartesian space. double meanSquareDisplacement(const R3::Matrix& Uijcartn, const R3::Vector& s, - bool anisotropy=true); + bool anisotropy = true); /// Maximum diagonal Uii element from all atoms in the structure. double maxUii(StructureAdapterPtr stru); /// Translate an index container to a vector of string symbols template -std::vector -siteIndicesToTypes(const StructureAdapterPtr& stru, const T& indices) -{ - std::vector rv; - rv.reserve(stru->countSites()); - typename T::const_iterator i; - for (i = indices.begin(); i != indices.end(); ++i) - { - const std::string& smbl = stru->siteAtomType(*i); - rv.push_back(smbl); - } - return rv; +std::vector siteIndicesToTypes(const StructureAdapterPtr& stru, + const T& indices) { + std::vector rv; + rv.reserve(stru->countSites()); + typename T::const_iterator i; + for (i = indices.begin(); i != indices.end(); ++i) { + const std::string& smbl = stru->siteAtomType(*i); + rv.push_back(smbl); + } + return rv; } // Conversion helpers -------------------------------------------------------- @@ -142,43 +134,37 @@ siteIndicesToTypes(const StructureAdapterPtr& stru, const T& indices) /// This converter provides by-value support for StructureAdapter instances. -inline -StructureAdapterPtr createStructureAdapter(const StructureAdapter& stru) -{ - return stru.clone(); +inline StructureAdapterPtr createStructureAdapter( + const StructureAdapter& stru) { + return stru.clone(); } /// convertToStructureAdapter is an interface function that should be used /// by the clients that need to convert to StructureAdapterPtr. template -StructureAdapterPtr convertToStructureAdapter(const T& stru) -{ - StructureAdapterPtr rv = createStructureAdapter(stru); - return rv; +StructureAdapterPtr convertToStructureAdapter(const T& stru) { + StructureAdapterPtr rv = createStructureAdapter(stru); + return rv; } - template -StructureAdapterPtr convertToStructureAdapter(const std::shared_ptr& stru) -{ - StructureAdapterPtr rv = - std::dynamic_pointer_cast(stru); - assert(rv); - return rv; +StructureAdapterPtr convertToStructureAdapter(const std::shared_ptr& stru) { + StructureAdapterPtr rv = + std::dynamic_pointer_cast(stru); + assert(rv); + return rv; } - -inline -StructureAdapterPtr convertToStructureAdapter(StructureAdapterConstPtr stru) -{ - StructureAdapterPtr rv = - std::const_pointer_cast(stru); - return rv; +inline StructureAdapterPtr convertToStructureAdapter( + StructureAdapterConstPtr stru) { + StructureAdapterPtr rv = + std::const_pointer_cast(stru); + return rv; } -} // namespace srreal -} // namespace diffpy +} // namespace srreal +} // namespace diffpy // Serialization ------------------------------------------------------------- diff --git a/src/diffpy/srreal/StructureDifference.cpp b/src/diffpy/srreal/StructureDifference.cpp index 53759441..872abada 100644 --- a/src/diffpy/srreal/StructureDifference.cpp +++ b/src/diffpy/srreal/StructureDifference.cpp @@ -1,23 +1,23 @@ /***************************************************************************** -* -* 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 StructureDifference for storing differences between two -* instances of the StructureAdapter class. -* -* StructureDifference is returned by the StructureAdapter::diff method. -* -*****************************************************************************/ + * + * 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 StructureDifference for storing differences between two + * instances of the StructureAdapter class. + * + * StructureDifference is returned by the StructureAdapter::diff method. + * + *****************************************************************************/ #include #include @@ -31,40 +31,33 @@ namespace srreal { // Constructors -------------------------------------------------------------- -StructureDifference::StructureDifference() : diffmethod(Method::NONE) -{ } - - -StructureDifference::StructureDifference( - StructureAdapterConstPtr oldstru, - StructureAdapterConstPtr newstru) - : - stru0(oldstru), stru1(newstru), diffmethod(Method::NONE) -{ - if (stru0 && stru0 == stru1) - { - diffmethod = Method::SIDEBYSIDE; - return; - } - // assume completely different structures - int v; - SiteIndices::iterator ii; - int n0 = stru0 ? stru0->countSites() : 0; - pop0.resize(n0); - for (ii = pop0.begin(), v = 0; ii != pop0.end();) *(ii++) = v++; - int n1 = stru1 ? stru1->countSites() : 0; - add1.resize(n1); - for (ii = add1.begin(), v = 0; ii != add1.end();) *(ii++) = v++; +StructureDifference::StructureDifference() : diffmethod(Method::NONE) {} + +StructureDifference::StructureDifference(StructureAdapterConstPtr oldstru, + StructureAdapterConstPtr newstru) + : stru0(oldstru), stru1(newstru), diffmethod(Method::NONE) { + if (stru0 && stru0 == stru1) { + diffmethod = Method::SIDEBYSIDE; + return; + } + // assume completely different structures + int v; + SiteIndices::iterator ii; + int n0 = stru0 ? stru0->countSites() : 0; + pop0.resize(n0); + for (ii = pop0.begin(), v = 0; ii != pop0.end();) *(ii++) = v++; + int n1 = stru1 ? stru1->countSites() : 0; + add1.resize(n1); + for (ii = add1.begin(), v = 0; ii != add1.end();) *(ii++) = v++; } // Public Methods ------------------------------------------------------------ -bool StructureDifference::allowsfastupdate() const -{ - int N0 = stru0 ? stru0->countSites() : 0; - double popbound = (1 - sqrt(0.5)) * N0; - return int(pop0.size()) < popbound; +bool StructureDifference::allowsfastupdate() const { + int N0 = stru0 ? stru0->countSites() : 0; + double popbound = (1 - sqrt(0.5)) * N0; + return int(pop0.size()) < popbound; } -} // namespace srreal -} // namespace diffpy +} // namespace srreal +} // namespace diffpy diff --git a/src/diffpy/srreal/StructureDifference.hpp b/src/diffpy/srreal/StructureDifference.hpp index 42e01ab7..feb9d394 100644 --- a/src/diffpy/srreal/StructureDifference.hpp +++ b/src/diffpy/srreal/StructureDifference.hpp @@ -1,23 +1,23 @@ /***************************************************************************** -* -* 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 StructureDifference for storing differences between two -* instances of the StructureAdapter class. -* -* StructureDifference is returned by the StructureAdapter::diff method. -* -*****************************************************************************/ + * + * 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 StructureDifference for storing differences between two + * instances of the StructureAdapter class. + * + * StructureDifference is returned by the StructureAdapter::diff method. + * + *****************************************************************************/ #ifndef STRUCTUREDIFFERENCE_HPP_INCLUDED #define STRUCTUREDIFFERENCE_HPP_INCLUDED @@ -30,42 +30,39 @@ namespace diffpy { namespace srreal { class DLL_EXPORT StructureDifference { + public: + // constructors + StructureDifference(); + StructureDifference(StructureAdapterConstPtr, StructureAdapterConstPtr); - public: + // enumeration type for difference methods + struct Method { + enum Type { NONE, SIDEBYSIDE, SORTED }; + }; - // constructors - StructureDifference(); - StructureDifference(StructureAdapterConstPtr, StructureAdapterConstPtr); + // data - // enumeration type for difference methods - struct Method { - enum Type {NONE, SIDEBYSIDE, SORTED}; - }; + /// pointer to the original StructureAdapter + StructureAdapterConstPtr stru0; + /// pointer to the new StructureAdapter + StructureAdapterConstPtr stru1; + /// indices of atoms in stru0 that are not in stru1. + /// These atoms need to be removed for a fast update of PairQuantity. + SiteIndices pop0; + /// indices of atoms in stru1 that are not in stru0 + /// These atoms need to be added in a fast update of PairQuantity. + SiteIndices add1; + /// type of comparison used in obtaining this difference + Method::Type diffmethod; - // data - - /// pointer to the original StructureAdapter - StructureAdapterConstPtr stru0; - /// pointer to the new StructureAdapter - StructureAdapterConstPtr stru1; - /// indices of atoms in stru0 that are not in stru1. - /// These atoms need to be removed for a fast update of PairQuantity. - SiteIndices pop0; - /// indices of atoms in stru1 that are not in stru0 - /// These atoms need to be added in a fast update of PairQuantity. - SiteIndices add1; - /// type of comparison used in obtaining this difference - Method::Type diffmethod; - - // methods - - /// Return true if PairQuantity can be fast-updated after changing - /// structure from stru0 to stru1. - bool allowsfastupdate() const; + // methods + /// Return true if PairQuantity can be fast-updated after changing + /// structure from stru0 to stru1. + bool allowsfastupdate() const; }; -} // namespace srreal -} // namespace diffpy +} // namespace srreal +} // namespace diffpy #endif // STRUCTUREDIFFERENCE_HPP_INCLUDED diff --git a/src/diffpy/srreal/ZeroBaseline.cpp b/src/diffpy/srreal/ZeroBaseline.cpp index 9415fe15..23677794 100644 --- a/src/diffpy/srreal/ZeroBaseline.cpp +++ b/src/diffpy/srreal/ZeroBaseline.cpp @@ -1,20 +1,20 @@ /***************************************************************************** -* -* 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.txt for license information. -* -****************************************************************************** -* -* class ZeroBaseline -- linear PDF baseline -* -*****************************************************************************/ + * + * 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.txt for license information. + * + ****************************************************************************** + * + * class ZeroBaseline -- linear PDF baseline + * + *****************************************************************************/ #include #include @@ -26,39 +26,31 @@ namespace srreal { // Constructors -------------------------------------------------------------- -PDFBaselinePtr ZeroBaseline::create() const -{ - PDFBaselinePtr rv(new ZeroBaseline()); - return rv; +PDFBaselinePtr ZeroBaseline::create() const { + PDFBaselinePtr rv(new ZeroBaseline()); + return rv; } - -PDFBaselinePtr ZeroBaseline::clone() const -{ - PDFBaselinePtr rv(new ZeroBaseline(*this)); - return rv; +PDFBaselinePtr ZeroBaseline::clone() const { + PDFBaselinePtr rv(new ZeroBaseline(*this)); + return rv; } // Public Methods ------------------------------------------------------------ -const string& ZeroBaseline::type() const -{ - static string rv = "zero"; - return rv; +const string& ZeroBaseline::type() const { + static string rv = "zero"; + return rv; } - -double ZeroBaseline::operator()(const double& r) const -{ - return 0.0; -} +double ZeroBaseline::operator()(const double& r) const { return 0.0; } // Registration -------------------------------------------------------------- bool reg_ZeroBaseline = ZeroBaseline().registerThisType(); -} // namespace srreal -} // namespace diffpy +} // namespace srreal +} // namespace diffpy // Serialization ------------------------------------------------------------- diff --git a/src/diffpy/srreal/ZeroBaseline.hpp b/src/diffpy/srreal/ZeroBaseline.hpp index 1a9669c5..c09c851f 100644 --- a/src/diffpy/srreal/ZeroBaseline.hpp +++ b/src/diffpy/srreal/ZeroBaseline.hpp @@ -1,20 +1,20 @@ /***************************************************************************** -* -* 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.txt for license information. -* -****************************************************************************** -* -* class ZeroBaseline -- linear PDF baseline -* -*****************************************************************************/ + * + * 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.txt for license information. + * + ****************************************************************************** + * + * class ZeroBaseline -- linear PDF baseline + * + *****************************************************************************/ #ifndef ZEROBASELINE_HPP_INCLUDED #define ZEROBASELINE_HPP_INCLUDED @@ -29,34 +29,30 @@ namespace srreal { /// @class ZeroBaseline /// @brief trivial zero baseline -class DLL_EXPORT ZeroBaseline : public PDFBaseline -{ - public: +class DLL_EXPORT ZeroBaseline : public PDFBaseline { + public: + // constructors + PDFBaselinePtr create() const; + PDFBaselinePtr clone() const; - // constructors - PDFBaselinePtr create() const; - PDFBaselinePtr clone() const; + // methods + const std::string& type() const; + double operator()(const double& r) const; - // methods - const std::string& type() const; - double operator()(const double& r) const; + private: + // serialization + friend class boost::serialization::access; - private: - - // serialization - friend class boost::serialization::access; - - template - void serialize(Archive& ar, const unsigned int version) - { - using boost::serialization::base_object; - ar & base_object(*this); - } + template + void serialize(Archive& ar, const unsigned int version) { + using boost::serialization::base_object; + ar& base_object(*this); + } }; // class ZeroBaseline -} // namespace srreal -} // namespace diffpy +} // namespace srreal +} // namespace diffpy // Serialization ------------------------------------------------------------- diff --git a/src/diffpy/srreal/forwardtypes.hpp b/src/diffpy/srreal/forwardtypes.hpp index 0384ee9c..ccc3bdb3 100644 --- a/src/diffpy/srreal/forwardtypes.hpp +++ b/src/diffpy/srreal/forwardtypes.hpp @@ -1,20 +1,20 @@ /***************************************************************************** -* -* 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. -* -****************************************************************************** -* -* Forward declaration of commonly used pointer types. -* -*****************************************************************************/ + * + * 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. + * + ****************************************************************************** + * + * Forward declaration of commonly used pointer types. + * + *****************************************************************************/ #ifndef FORWARDTYPES_HPP_INCLUDED #define FORWARDTYPES_HPP_INCLUDED @@ -33,7 +33,7 @@ typedef std::shared_ptr StructureAdapterConstPtr; /// type for holding indices for atom sites typedef std::vector SiteIndices; -} // namespace srreal -} // namespace diffpy +} // namespace srreal +} // namespace diffpy #endif // FORWARDTYPES_HPP_INCLUDED diff --git a/src/diffpy/srreal/scatteringfactordata.cpp b/src/diffpy/srreal/scatteringfactordata.cpp index d33e2533..6d38fc9f 100644 --- a/src/diffpy/srreal/scatteringfactordata.cpp +++ b/src/diffpy/srreal/scatteringfactordata.cpp @@ -44,308 +44,260 @@ namespace { const int WKTerms = 5; -class WaasKirfFormula -{ - public: - - // Constructor - WaasKirfFormula() - { - fill(a, a + WKTerms, 0.0); - fill(b, b + WKTerms, 0.0); - c = 0.0; - } - - WaasKirfFormula(const WaasKirfFormula& wk0) - { - symbol = wk0.symbol; - copy(wk0.a, wk0.a + WKTerms, a); - copy(wk0.b, wk0.b + WKTerms, b); - c = wk0.c; - } - - // methods - double xrayatstol(double stol) const - { - double rv = c; - for (int i = 0; i < WKTerms; ++i) - { - rv += a[i] * exp(-b[i] * stol * stol); - } - return rv; - } - - double Z() const - { - double rv = c; - for (int i = 0; i < WKTerms; ++i) rv += a[i]; - return round(rv); - } - - double electronatstol(double stol) const - { - using diffpy::mathutils::eps_eq; - using diffpy::mathutils::DOUBLE_MAX; - if (eps_eq(stol, 0.0)) return DOUBLE_MAX; - double rv = 0.023934 * - (this->Z() - this->xrayatstol(stol)) / (stol * stol); - return rv; - } - - // data - string symbol; - double a[WKTerms]; - double b[WKTerms]; - double c; +class WaasKirfFormula { + public: + // Constructor + WaasKirfFormula() { + fill(a, a + WKTerms, 0.0); + fill(b, b + WKTerms, 0.0); + c = 0.0; + } + + WaasKirfFormula(const WaasKirfFormula& wk0) { + symbol = wk0.symbol; + copy(wk0.a, wk0.a + WKTerms, a); + copy(wk0.b, wk0.b + WKTerms, b); + c = wk0.c; + } + + // methods + double xrayatstol(double stol) const { + double rv = c; + for (int i = 0; i < WKTerms; ++i) { + rv += a[i] * exp(-b[i] * stol * stol); + } + return rv; + } + + double Z() const { + double rv = c; + for (int i = 0; i < WKTerms; ++i) rv += a[i]; + return round(rv); + } + + double electronatstol(double stol) const { + using diffpy::mathutils::DOUBLE_MAX; + using diffpy::mathutils::eps_eq; + if (eps_eq(stol, 0.0)) return DOUBLE_MAX; + double rv = 0.023934 * (this->Z() - this->xrayatstol(stol)) / (stol * stol); + return rv; + } + + // data + string symbol; + double a[WKTerms]; + double b[WKTerms]; + double c; }; - -class wksmbl_equal -{ - public: - bool operator() ( - const WaasKirfFormula& wk0, const WaasKirfFormula& wk1) const - { - return wk0.symbol == wk1.symbol; - } +class wksmbl_equal { + public: + bool operator()(const WaasKirfFormula& wk0, + const WaasKirfFormula& wk1) const { + return wk0.symbol == wk1.symbol; + } }; - -class wksmbl_hash -{ - public: - size_t operator()(const WaasKirfFormula& wk) const - { - hash hasher; - return hasher(wk.symbol); - } +class wksmbl_hash { + public: + size_t operator()(const WaasKirfFormula& wk) const { + hash hasher; + return hasher(wk.symbol); + } }; - -typedef unordered_set SetOfWKFormulas; - - -const SetOfWKFormulas& getWKFormulasSet() -{ - using namespace diffpy::runtimepath; - using diffpy::validators::ensureFileOK; - static unique_ptr the_set; - if (the_set) return *the_set; - the_set.reset(new SetOfWKFormulas); - string wkfile = datapath("f0_WaasKirf.dat"); - ifstream fp(wkfile.c_str()); - ensureFileOK(wkfile, fp); - LineReader line; - line.commentmark = '#'; - for (WaasKirfFormula wk; fp >> line;) - { - if (line.iscomment() && (0 == line.words[0].compare(0, 2, "#S"))) - { - if (line.wcount() < 3) - { - throw line.format_error(wkfile, - "Expected at least 3 columns of data."); - } - wk.symbol = line.words[2]; - continue; - } - if (line.iscomment() && (0 == line.words[0].compare(0, 2, "#L"))) - { - if (wk.symbol.empty()) - { - throw line.format_error(wkfile, - "Missing \"#S\" line with atom symbol definition."); - } - fp >> line; - if (line.wcount() != 11) - { - throw line.format_error(wkfile, "Expected 11 values."); - } - istringstream fpline(line.line); - fpline >> - wk.a[0] >> wk.a[1] >> wk.a[2] >> wk.a[3] >> wk.a[4] >> - wk.c >> wk.b[0] >> wk.b[1] >> wk.b[2] >> wk.b[3] >> wk.b[4]; - if (!fpline) - { - throw line.format_error(wkfile, - "Line should contain 11 floating point values."); - } - if (the_set->count(wk)) - { - string emsg = "Duplicate atom symbol \""; - emsg += wk.symbol + "\"."; - throw line.format_error(wkfile, emsg); - } - the_set->insert(wk); - wk.symbol.clear(); - } - } - return getWKFormulasSet(); -} - - -SetOfWKFormulas::const_iterator findWKFormula(const string& smbl) -{ - using diffpy::srreal::atomBareSymbol; - using diffpy::srreal::atomValence; - WaasKirfFormula wk; - wk.symbol = smbl; - const SetOfWKFormulas& swk = getWKFormulasSet(); - SetOfWKFormulas::const_iterator wkit = swk.find(wk); - if (wkit != swk.end()) return wkit; - // try again with an adjusted charge suffix - wk.symbol = atomBareSymbol(smbl); - int v = atomValence(smbl); - if (v == 0) wkit = swk.find(wk); - else if (abs(v) == 1) - { - wk.symbol += ((v > 0) ? "1+" : "1-"); - wkit = swk.find(wk); +typedef unordered_set + SetOfWKFormulas; + +const SetOfWKFormulas& getWKFormulasSet() { + using namespace diffpy::runtimepath; + using diffpy::validators::ensureFileOK; + static unique_ptr the_set; + if (the_set) return *the_set; + the_set.reset(new SetOfWKFormulas); + string wkfile = datapath("f0_WaasKirf.dat"); + ifstream fp(wkfile.c_str()); + ensureFileOK(wkfile, fp); + LineReader line; + line.commentmark = '#'; + for (WaasKirfFormula wk; fp >> line;) { + if (line.iscomment() && (0 == line.words[0].compare(0, 2, "#S"))) { + if (line.wcount() < 3) { + throw line.format_error(wkfile, "Expected at least 3 columns of data."); + } + wk.symbol = line.words[2]; + continue; } - // raise exception if no match - if (wkit == swk.end()) - { - string emsg("Unknown atom or ion symbol '"); - emsg += smbl + "'."; - throw invalid_argument(emsg); + if (line.iscomment() && (0 == line.words[0].compare(0, 2, "#L"))) { + if (wk.symbol.empty()) { + throw line.format_error( + wkfile, "Missing \"#S\" line with atom symbol definition."); + } + fp >> line; + if (line.wcount() != 11) { + throw line.format_error(wkfile, "Expected 11 values."); + } + istringstream fpline(line.line); + fpline >> wk.a[0] >> wk.a[1] >> wk.a[2] >> wk.a[3] >> wk.a[4] >> wk.c >> + wk.b[0] >> wk.b[1] >> wk.b[2] >> wk.b[3] >> wk.b[4]; + if (!fpline) { + throw line.format_error( + wkfile, "Line should contain 11 floating point values."); + } + if (the_set->count(wk)) { + string emsg = "Duplicate atom symbol \""; + emsg += wk.symbol + "\"."; + throw line.format_error(wkfile, emsg); + } + the_set->insert(wk); + wk.symbol.clear(); } - return wkit; + } + return getWKFormulasSet(); } +SetOfWKFormulas::const_iterator findWKFormula(const string& smbl) { + using diffpy::srreal::atomBareSymbol; + using diffpy::srreal::atomValence; + WaasKirfFormula wk; + wk.symbol = smbl; + const SetOfWKFormulas& swk = getWKFormulasSet(); + SetOfWKFormulas::const_iterator wkit = swk.find(wk); + if (wkit != swk.end()) return wkit; + // try again with an adjusted charge suffix + wk.symbol = atomBareSymbol(smbl); + int v = atomValence(smbl); + if (v == 0) + wkit = swk.find(wk); + else if (abs(v) == 1) { + wk.symbol += ((v > 0) ? "1+" : "1-"); + wkit = swk.find(wk); + } + // raise exception if no match + if (wkit == swk.end()) { + string emsg("Unknown atom or ion symbol '"); + emsg += smbl + "'."; + throw invalid_argument(emsg); + } + return wkit; +} // Electron numbers for elements and ions -typedef unordered_map ElectronNumberStorage; - -ElectronNumberStorage& getElectronNumberTable() -{ - using namespace diffpy::runtimepath; - using diffpy::validators::ensureFileOK; - static unique_ptr entable; - typedef ElectronNumberStorage::value_type ENPair; - if (!entable) - { - entable.reset(new ElectronNumberStorage); - string ionfile = datapath("ionlist.dat"); - ifstream fp0(ionfile.c_str()); - ensureFileOK(ionfile, fp0); - LineReader line; - while (fp0 >> line) - { - if (line.isignored()) continue; - istringstream fpline(line.line); - string element; - int z = 0; - fpline >> element >> z; - if (!fpline) - { - throw line.format_error(ionfile, - "Expected at least 2 columns for (symbol, Z)."); - } - entable->insert(ENPair(element, z)); - for (int v; fpline >> v;) - { - ostringstream smbl; - smbl << element << abs(v) << ((v > 0) ? '+' : '-'); - entable->insert(ENPair(smbl.str(), z - v)); - } - } - const size_t mintablesize = 447; - if (entable->size() < mintablesize) - { - ostringstream emsg; - emsg << "Incomplete file. Expected " << mintablesize << - " items loaded " << entable->size() << "."; - throw line.format_error(ionfile, emsg.str()); - } +typedef unordered_map ElectronNumberStorage; + +ElectronNumberStorage& getElectronNumberTable() { + using namespace diffpy::runtimepath; + using diffpy::validators::ensureFileOK; + static unique_ptr entable; + typedef ElectronNumberStorage::value_type ENPair; + if (!entable) { + entable.reset(new ElectronNumberStorage); + string ionfile = datapath("ionlist.dat"); + ifstream fp0(ionfile.c_str()); + ensureFileOK(ionfile, fp0); + LineReader line; + while (fp0 >> line) { + if (line.isignored()) continue; + istringstream fpline(line.line); + string element; + int z = 0; + fpline >> element >> z; + if (!fpline) { + throw line.format_error(ionfile, + "Expected at least 2 columns for (symbol, Z)."); + } + entable->insert(ENPair(element, z)); + for (int v; fpline >> v;) { + ostringstream smbl; + smbl << element << abs(v) << ((v > 0) ? '+' : '-'); + entable->insert(ENPair(smbl.str(), z - v)); + } + } + const size_t mintablesize = 447; + if (entable->size() < mintablesize) { + ostringstream emsg; + emsg << "Incomplete file. Expected " << mintablesize << " items loaded " + << entable->size() << "."; + throw line.format_error(ionfile, emsg.str()); } - return *entable; + } + return *entable; } // Neutron scattering lengths -typedef unordered_map NeutronBCStorage; - -const NeutronBCStorage& getNeutronBCTable() -{ - using namespace diffpy::runtimepath; - using diffpy::validators::ensureFileOK; - typedef NeutronBCStorage::value_type BCPair; - static unique_ptr bctable; - if (bctable) return *bctable; - bctable.reset(new NeutronBCStorage); - string nsffile = datapath("nsftable.dat"); - ifstream fp(nsffile.c_str()); - ensureFileOK(nsffile, fp); - LineReader line; - line.commentmark = '#'; - line.separator = ','; - while (fp >> line) - { - if (line.isignored()) continue; - if (line.wcount() != 11) - { - throw line.format_error(nsffile, - "Expected 11 comma-separated items."); - } - if (line.words[3].empty()) continue; - string smbl = line.words[0]; - size_t p0 = smbl.find_first_not_of("0123456789-"); - if (p0 == string::npos) - { - throw line.format_error(nsffile, "Missing or invalid atom symbol."); - } - smbl.erase(0, p0); - size_t p1 = smbl.find_last_of('-'); - if (p1 != string::npos) - { - smbl = smbl.substr(p1 + 1) + "-" + smbl.substr(0, p1); - } - istringstream fpbc(line.words[3]); - double bc; - fpbc >> bc; - if (!fpbc) - { - throw line.format_error(nsffile, "Invalid b_c value."); - } - if (bctable->count(smbl)) - { - string emsg = "Duplicate atom symbol \""; - emsg += smbl + "\"."; - throw line.format_error(nsffile, emsg); - } - bctable->insert(BCPair(smbl, bc)); - // elements are not explicitly included if there is just one isotope - // or if all isotopes are unstable - size_t p2 = smbl.find_first_of('-'); - if (p2 != string::npos) - { - string el = smbl.substr(p2 + 1); - // there is just one isotope - const string& chlf = line.words[1]; - bool addel = (chlf == "100") || - (!bctable->count(el) && *chlf.rbegin() == 'Y'); - if (addel) - { - if (bctable->count(el)) - { - string emsg = "Duplicate element entry for \""; - emsg += el + "\"."; - throw line.format_error(nsffile, emsg);; - } - bctable->insert(BCPair(el, bc)); - } +typedef unordered_map NeutronBCStorage; + +const NeutronBCStorage& getNeutronBCTable() { + using namespace diffpy::runtimepath; + using diffpy::validators::ensureFileOK; + typedef NeutronBCStorage::value_type BCPair; + static unique_ptr bctable; + if (bctable) return *bctable; + bctable.reset(new NeutronBCStorage); + string nsffile = datapath("nsftable.dat"); + ifstream fp(nsffile.c_str()); + ensureFileOK(nsffile, fp); + LineReader line; + line.commentmark = '#'; + line.separator = ','; + while (fp >> line) { + if (line.isignored()) continue; + if (line.wcount() != 11) { + throw line.format_error(nsffile, "Expected 11 comma-separated items."); + } + if (line.words[3].empty()) continue; + string smbl = line.words[0]; + size_t p0 = smbl.find_first_not_of("0123456789-"); + if (p0 == string::npos) { + throw line.format_error(nsffile, "Missing or invalid atom symbol."); + } + smbl.erase(0, p0); + size_t p1 = smbl.find_last_of('-'); + if (p1 != string::npos) { + smbl = smbl.substr(p1 + 1) + "-" + smbl.substr(0, p1); + } + istringstream fpbc(line.words[3]); + double bc; + fpbc >> bc; + if (!fpbc) { + throw line.format_error(nsffile, "Invalid b_c value."); + } + if (bctable->count(smbl)) { + string emsg = "Duplicate atom symbol \""; + emsg += smbl + "\"."; + throw line.format_error(nsffile, emsg); + } + bctable->insert(BCPair(smbl, bc)); + // elements are not explicitly included if there is just one isotope + // or if all isotopes are unstable + size_t p2 = smbl.find_first_of('-'); + if (p2 != string::npos) { + string el = smbl.substr(p2 + 1); + // there is just one isotope + const string& chlf = line.words[1]; + bool addel = + (chlf == "100") || (!bctable->count(el) && *chlf.rbegin() == 'Y'); + if (addel) { + if (bctable->count(el)) { + string emsg = "Duplicate element entry for \""; + emsg += el + "\"."; + throw line.format_error(nsffile, emsg); + ; } + bctable->insert(BCPair(el, bc)); + } } - // define aliases for neutron, deuterium and tritium - bctable->insert(BCPair("n", bctable->at("1-n"))); - bctable->insert(BCPair("D", bctable->at("2-H"))); - bctable->insert(BCPair("T", bctable->at("3-H"))); - return getNeutronBCTable(); + } + // define aliases for neutron, deuterium and tritium + bctable->insert(BCPair("n", bctable->at("1-n"))); + bctable->insert(BCPair("D", bctable->at("2-H"))); + bctable->insert(BCPair("T", bctable->at("3-H"))); + return getNeutronBCTable(); } -} // namespace +} // namespace // Implementation ------------------------------------------------------------ @@ -353,74 +305,61 @@ namespace diffpy { namespace srreal { /// X-ray scattering factor of an element or ion a given Q -double fxrayatq(const string& smbl, double q) -{ - const double stol = q / (4 * M_PI); - return fxrayatstol(smbl, stol); +double fxrayatq(const string& smbl, double q) { + const double stol = q / (4 * M_PI); + return fxrayatstol(smbl, stol); } - /// X-ray scattering factor of an element or ion a given sin(theta)/lambda -double fxrayatstol(const string& smbl, double stol) -{ - SetOfWKFormulas::const_iterator wkit = findWKFormula(smbl); - return wkit->xrayatstol(stol); +double fxrayatstol(const string& smbl, double stol) { + SetOfWKFormulas::const_iterator wkit = findWKFormula(smbl); + return wkit->xrayatstol(stol); } - /// Electron scattering factor of an element or ion a given Q -double felectronatq(const string& smbl, double q) -{ - const double stol = q / (4 * M_PI); - SetOfWKFormulas::const_iterator wkit = findWKFormula(smbl); - return wkit->electronatstol(stol); +double felectronatq(const string& smbl, double q) { + const double stol = q / (4 * M_PI); + SetOfWKFormulas::const_iterator wkit = findWKFormula(smbl); + return wkit->electronatstol(stol); } - /// Number of electrons for an element or ion -int electronnumber(const string& smbl) -{ - const ElectronNumberStorage& entable = getElectronNumberTable(); - ElectronNumberStorage::const_iterator it; - it = entable.find(smbl); - // try to build standard symbol when not found - if (it == entable.end()) - { - ostringstream smbl1; - smbl1 << atomBareSymbol(smbl); - int v = atomValence(smbl); - if (v) smbl1 << abs(v) << ((v > 0) ? '+' : '-'); - it = entable.find(smbl1.str()); - } - // throw exception if still not found - if (it == entable.end()) - { - ostringstream emsg; - emsg << "Unknown atom symbol '" << smbl << "'."; - throw invalid_argument(emsg.str()); - } - return it->second; +int electronnumber(const string& smbl) { + const ElectronNumberStorage& entable = getElectronNumberTable(); + ElectronNumberStorage::const_iterator it; + it = entable.find(smbl); + // try to build standard symbol when not found + if (it == entable.end()) { + ostringstream smbl1; + smbl1 << atomBareSymbol(smbl); + int v = atomValence(smbl); + if (v) smbl1 << abs(v) << ((v > 0) ? '+' : '-'); + it = entable.find(smbl1.str()); + } + // throw exception if still not found + if (it == entable.end()) { + ostringstream emsg; + emsg << "Unknown atom symbol '" << smbl << "'."; + throw invalid_argument(emsg.str()); + } + return it->second; } - /// Coherent scattering length of an element or isotope in fm -double bcneutron(const string& smbl) -{ - const NeutronBCStorage& bctable = getNeutronBCTable(); - NeutronBCStorage::const_iterator it = bctable.find(smbl); - if (it != bctable.end()) return it->second; - size_t pe = smbl.find_last_not_of("+-012345678 \t"); - string smblnocharge = - (pe != string::npos) ? smbl.substr(0, pe + 1) : smbl; - it = bctable.find(smblnocharge); - if (it == bctable.end()) - { - string emsg("Unknown atom or isotope symbol '"); - emsg += smbl + "'."; - throw invalid_argument(emsg); - } - return it->second; +double bcneutron(const string& smbl) { + const NeutronBCStorage& bctable = getNeutronBCTable(); + NeutronBCStorage::const_iterator it = bctable.find(smbl); + if (it != bctable.end()) return it->second; + size_t pe = smbl.find_last_not_of("+-012345678 \t"); + string smblnocharge = (pe != string::npos) ? smbl.substr(0, pe + 1) : smbl; + it = bctable.find(smblnocharge); + if (it == bctable.end()) { + string emsg("Unknown atom or isotope symbol '"); + emsg += smbl + "'."; + throw invalid_argument(emsg); + } + return it->second; } -} // namespace srreal -} // namespace diffpy +} // namespace srreal +} // namespace diffpy diff --git a/src/diffpy/srreal/scatteringfactordata.hpp b/src/diffpy/srreal/scatteringfactordata.hpp index bae6e603..3d209f75 100644 --- a/src/diffpy/srreal/scatteringfactordata.hpp +++ b/src/diffpy/srreal/scatteringfactordata.hpp @@ -45,7 +45,7 @@ int electronnumber(const std::string& smbl); /// Coherent scattering length of an element or isotope in fm double bcneutron(const std::string& smbl); -} // namespace srreal -} // namespace diffpy +} // namespace srreal +} // namespace diffpy #endif // SCATTERINGFACTORS_HPP_INCLUDED diff --git a/src/diffpy/validators.hpp b/src/diffpy/validators.hpp index 4f40684e..a07c009c 100644 --- a/src/diffpy/validators.hpp +++ b/src/diffpy/validators.hpp @@ -1,20 +1,20 @@ /***************************************************************************** -* -* 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.txt for license information. -* -****************************************************************************** -* -* Convenience functions for argument checking. -* -*****************************************************************************/ + * + * 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.txt for license information. + * + ****************************************************************************** + * + * Convenience functions for argument checking. + * + *****************************************************************************/ #ifndef VALIDATORS_HPP_INCLUDED #define VALIDATORS_HPP_INCLUDED @@ -30,58 +30,50 @@ namespace validators { /// Throw invalid_argument for a negative value template -void ensureNonNegative(const std::string& vname, const T& value) -{ - if (value < 0) - { - std::string emsg(vname); - emsg += " cannot be negative."; - throw std::invalid_argument(emsg); - } +void ensureNonNegative(const std::string& vname, const T& value) { + if (value < 0) { + std::string emsg(vname); + emsg += " cannot be negative."; + throw std::invalid_argument(emsg); + } } /// Throw invalid_argument for a value that is not greater than DOUBLE_EPS template -void ensureEpsilonPositive(const std::string& vname, const T& value) -{ - using diffpy::mathutils::eps_gt; - if (!eps_gt(double(value), 0.0)) - { - std::string emsg(vname); - emsg += " must be epsilon positive."; - throw std::invalid_argument(emsg); - } +void ensureEpsilonPositive(const std::string& vname, const T& value) { + using diffpy::mathutils::eps_gt; + if (!eps_gt(double(value), 0.0)) { + std::string emsg(vname); + emsg += " must be epsilon positive."; + throw std::invalid_argument(emsg); + } } /// Throw invalid_argument if the argument is not true. For boost smart /// pointers this is equivalent to a truth check of p.get(). template -void ensureNonNull(const std::string& vname, const T& p) -{ - if (!p) - { - std::string emsg(vname); - emsg += " cannot be NULL."; - throw std::invalid_argument(emsg); - } +void ensureNonNull(const std::string& vname, const T& p) { + if (!p) { + std::string emsg(vname); + emsg += " cannot be NULL."; + throw std::invalid_argument(emsg); + } } /// Throw runtime_error if a file argument is not true. template -void ensureFileOK(const std::string& fname, const T& fp) -{ - if (!fp) - { - std::string emsg = "Cannot open '"; - emsg += fname + "'."; - throw std::runtime_error(emsg); - } +void ensureFileOK(const std::string& fname, const T& fp) { + if (!fp) { + std::string emsg = "Cannot open '"; + emsg += fname + "'."; + throw std::runtime_error(emsg); + } } -} // namespace validators -} // namespace diffpy +} // namespace validators +} // namespace diffpy #endif // VALIDATORS_HPP_INCLUDED diff --git a/src/diffpy/version.cpp b/src/diffpy/version.cpp index ba4f9f8e..af8acb30 100644 --- a/src/diffpy/version.cpp +++ b/src/diffpy/version.cpp @@ -1,20 +1,20 @@ /***************************************************************************** -* -* libdiffpy Complex Modeling Initiative -* (c) 2014 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. -* -****************************************************************************** -* -* Definitions for the libdiffpy_version_info constants. -* -*****************************************************************************/ + * + * libdiffpy Complex Modeling Initiative + * (c) 2014 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. + * + ****************************************************************************** + * + * Definitions for the libdiffpy_version_info constants. + * + *****************************************************************************/ #include diff --git a/src/tests/TestAtomRadiiTable.hpp b/src/tests/TestAtomRadiiTable.hpp index 6a5fce87..1db124bc 100644 --- a/src/tests/TestAtomRadiiTable.hpp +++ b/src/tests/TestAtomRadiiTable.hpp @@ -1,20 +1,20 @@ /***************************************************************************** -* -* 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.txt for license information. -* -****************************************************************************** -* -* class TestAtomRadiiTable -- unit tests for the AtomRadiiTable class -* -*****************************************************************************/ + * + * 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.txt for license information. + * + ****************************************************************************** + * + * class TestAtomRadiiTable -- unit tests for the AtomRadiiTable class + * + *****************************************************************************/ #include #include @@ -26,112 +26,82 @@ 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()); - } - +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.hpp b/src/tests/TestAtomicStructureAdapter.hpp index 85113634..63c9db6b 100644 --- a/src/tests/TestAtomicStructureAdapter.hpp +++ b/src/tests/TestAtomicStructureAdapter.hpp @@ -1,21 +1,21 @@ /***************************************************************************** -* -* 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 -* -*****************************************************************************/ + * + * 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 @@ -34,124 +34,110 @@ 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 : 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 +} // namespace srreal +} // namespace diffpy using diffpy::srreal::TestAtomicStructureAdapter; diff --git a/src/tests/TestAttributes.hpp b/src/tests/TestAttributes.hpp index 1183b9f2..64192d4e 100644 --- a/src/tests/TestAttributes.hpp +++ b/src/tests/TestAttributes.hpp @@ -1,20 +1,20 @@ /***************************************************************************** -* -* 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 TestAttributes -- test the helper Attributes class -* -*****************************************************************************/ + * + * 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 TestAttributes -- test the helper Attributes class + * + *****************************************************************************/ #include @@ -24,65 +24,48 @@ namespace { -class Example : public diffpy::Attributes -{ - public: +class Example : public diffpy::Attributes { + public: + Example() : ma(0.0) { + this->registerDoubleAttribute("a", this, &Example::geta, &Example::seta); + this->registerDoubleAttribute("b", this, &Example::geta); + } - Example() : ma(0.0) - { - this->registerDoubleAttribute("a", this, - &Example::geta, &Example::seta); - this->registerDoubleAttribute("b", this, - &Example::geta); - } - - double geta() const { return ma; } - void seta(double a) { ma = a; } - - private: - - double ma; + double geta() const { return ma; } + void seta(double a) { ma = a; } + private: + double ma; }; -} // namespace +} // namespace // --------------------------------------------------------------------------- -class TestAttributes : public CxxTest::TestSuite -{ - private: - - // 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()); - } - - - 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); - } +class TestAttributes : public CxxTest::TestSuite { + private: + // 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()); + } + + 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); + } }; // class TestAttributes diff --git a/src/tests/TestBVParametersTable.hpp b/src/tests/TestBVParametersTable.hpp index 38df3a90..69e2c065 100644 --- a/src/tests/TestBVParametersTable.hpp +++ b/src/tests/TestBVParametersTable.hpp @@ -1,20 +1,20 @@ /***************************************************************************** -* -* 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.txt for license information. -* -****************************************************************************** -* -* class TestBVParametersTable -- unit tests for BVParametersTable class -* -*****************************************************************************/ + * + * 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.txt for license information. + * + ****************************************************************************** + * + * class TestBVParametersTable -- unit tests for BVParametersTable class + * + *****************************************************************************/ #include @@ -24,206 +24,173 @@ 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 : 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 diff --git a/src/tests/TestBVSCalculator.hpp b/src/tests/TestBVSCalculator.hpp index 1b268030..f125393e 100644 --- a/src/tests/TestBVSCalculator.hpp +++ b/src/tests/TestBVSCalculator.hpp @@ -1,20 +1,20 @@ /***************************************************************************** -* -* 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.txt for license information. -* -****************************************************************************** -* -* class TestBVSCalculator -- unit tests suite -* -*****************************************************************************/ + * + * 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.txt for license information. + * + ****************************************************************************** + * + * class TestBVSCalculator -- unit tests suite + * + *****************************************************************************/ #include @@ -24,144 +24,126 @@ #include "test_helpers.hpp" using namespace std; -using diffpy::srreal::BVSCalculator; using diffpy::srreal::BVParametersTablePtr; -using diffpy::srreal::StructureAdapterPtr; +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< - PeriodicStructureAdapter>(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 : 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 diff --git a/src/tests/TestBVSObjCryst.hpp b/src/tests/TestBVSObjCryst.hpp index ab3a7968..ffc54037 100644 --- a/src/tests/TestBVSObjCryst.hpp +++ b/src/tests/TestBVSObjCryst.hpp @@ -1,21 +1,21 @@ /***************************************************************************** -* -* 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.txt for license information. -* -****************************************************************************** -* -* class TestBVSObjCryst -- unit tests for BVS calculation for an ObjCryst -* crystal structure -* -*****************************************************************************/ + * + * 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.txt for license information. + * + ****************************************************************************** + * + * class TestBVSObjCryst -- unit tests for BVS calculation for an ObjCryst + * crystal structure + * + *****************************************************************************/ #include @@ -31,44 +31,35 @@ using namespace diffpy::srreal; // class Test ////////////////////////////////////////////////////////////////////////////// -class TestBVSObjCryst : public CxxTest::TestSuite -{ - private: +class TestBVSObjCryst : public CxxTest::TestSuite { + private: + unique_ptr mnacl; + unique_ptr mbvc; - unique_ptr mnacl; - unique_ptr mbvc; + public: + void setUp() { + if (!mnacl.get()) { + mnacl.reset(loadTestCrystal("NaCl.cif")); + } + mbvc.reset(new BVSCalculator); + } - public: + 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 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); - } + 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 diff --git a/src/tests/TestDebyePDFCalculator.hpp b/src/tests/TestDebyePDFCalculator.hpp index 2c78ee69..7f534efb 100644 --- a/src/tests/TestDebyePDFCalculator.hpp +++ b/src/tests/TestDebyePDFCalculator.hpp @@ -1,20 +1,20 @@ /***************************************************************************** -* -* 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.txt for license information. -* -****************************************************************************** -* -* class TestDebyePDFCalculator -- unit tests for DebyePDFCalculator class -* -*****************************************************************************/ + * + * 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.txt for license information. + * + ****************************************************************************** + * + * class TestDebyePDFCalculator -- unit tests for DebyePDFCalculator class + * + *****************************************************************************/ #include @@ -30,334 +30,283 @@ 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 : 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 diff --git a/src/tests/TestEmptyStructureAdapter.hpp b/src/tests/TestEmptyStructureAdapter.hpp index 89e2af2b..00388375 100644 --- a/src/tests/TestEmptyStructureAdapter.hpp +++ b/src/tests/TestEmptyStructureAdapter.hpp @@ -1,20 +1,20 @@ /***************************************************************************** -* -* 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 -* -*****************************************************************************/ + * + * 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 @@ -30,52 +30,40 @@ 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 : 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 +} // namespace srreal +} // namespace diffpy using diffpy::srreal::TestEmptyStructureAdapter; diff --git a/src/tests/TestHasClassRegistry.hpp b/src/tests/TestHasClassRegistry.hpp index 8ce9aac5..303479b5 100644 --- a/src/tests/TestHasClassRegistry.hpp +++ b/src/tests/TestHasClassRegistry.hpp @@ -1,20 +1,20 @@ /***************************************************************************** -* -* 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. -* -*****************************************************************************/ + * + * 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 @@ -27,60 +27,47 @@ 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 : 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 diff --git a/src/tests/TestLattice.hpp b/src/tests/TestLattice.hpp index 2c66c0ad..dfa43ffe 100644 --- a/src/tests/TestLattice.hpp +++ b/src/tests/TestLattice.hpp @@ -1,20 +1,20 @@ /***************************************************************************** -* -* 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.txt for license information. -* -****************************************************************************** -* -* class TestLattice -- unit tests for the Lattice class -* -*****************************************************************************/ + * + * 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.txt for license information. + * + ****************************************************************************** + * + * class TestLattice -- unit tests for the Lattice class + * + *****************************************************************************/ #include @@ -25,151 +25,124 @@ 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 : 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 diff --git a/src/tests/TestNoMetaStructureAdapter.hpp b/src/tests/TestNoMetaStructureAdapter.hpp index 6fdb1920..46228443 100644 --- a/src/tests/TestNoMetaStructureAdapter.hpp +++ b/src/tests/TestNoMetaStructureAdapter.hpp @@ -1,21 +1,21 @@ /***************************************************************************** -* -* 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.txt for license information. -* -****************************************************************************** -* -* class TestNoMetaStructureAdapter -- unit tests for an adapter that -* avoids metadata forwarding from the diffpy.Structure classes. -* -*****************************************************************************/ + * + * 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.txt for license information. + * + ****************************************************************************** + * + * class TestNoMetaStructureAdapter -- unit tests for an adapter that + * avoids metadata forwarding from the diffpy.Structure classes. + * + *****************************************************************************/ #include #include @@ -34,78 +34,65 @@ using namespace diffpy::srreal; // class TestNoMetaStructureAdapter ////////////////////////////////////////////////////////////////////////////// -class TestNoMetaStructureAdapter : public CxxTest::TestSuite -{ - private: - - StructureAdapterPtr m_pswt; - - public: - - void setUp() - { - CxxTest::setAbortTestOnFail(true); - if (!m_pswt) - { - StructureAdapterPtr pswt; - pswt = loadTestPeriodicStructure("PbScW25TiO3.stru"); - PeriodicAdapterWithPQConfigPtr pswt_pqcfg; - pswt_pqcfg = addCustomPQConfig(pswt); - pswt_pqcfg->cfg["scale"] = 2.5; - m_pswt = pswt_pqcfg; - } - CxxTest::setAbortTestOnFail(false); - } - - - void test_PDFCalcMeta() - { - PDFCalculator pdfc; - pdfc.setRstep(0.1); - pdfc.setRmax(5.0); - TS_ASSERT_EQUALS(1.0, 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(adpt0, adpt1); - } - - - void test_serialization() - { - using namespace diffpy::serialization; - stringstream storage(ios::in | ios::out | ios::binary); - oarchive oa(storage, ios::binary); - StructureAdapterPtr pswtbare0(nometa(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 : public CxxTest::TestSuite { + private: + StructureAdapterPtr m_pswt; + + public: + void setUp() { + CxxTest::setAbortTestOnFail(true); + if (!m_pswt) { + StructureAdapterPtr pswt; + pswt = loadTestPeriodicStructure("PbScW25TiO3.stru"); + PeriodicAdapterWithPQConfigPtr pswt_pqcfg; + pswt_pqcfg = addCustomPQConfig(pswt); + pswt_pqcfg->cfg["scale"] = 2.5; + m_pswt = pswt_pqcfg; + } + CxxTest::setAbortTestOnFail(false); + } + + void test_PDFCalcMeta() { + PDFCalculator pdfc; + pdfc.setRstep(0.1); + pdfc.setRmax(5.0); + TS_ASSERT_EQUALS(1.0, 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(adpt0, adpt1); + } + + void test_serialization() { + using namespace diffpy::serialization; + stringstream storage(ios::in | ios::out | ios::binary); + oarchive oa(storage, ios::binary); + StructureAdapterPtr pswtbare0(nometa(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 diff --git a/src/tests/TestObjCrystStructureAdapter.hpp b/src/tests/TestObjCrystStructureAdapter.hpp index eaa4da27..8035105d 100644 --- a/src/tests/TestObjCrystStructureAdapter.hpp +++ b/src/tests/TestObjCrystStructureAdapter.hpp @@ -1,23 +1,23 @@ /***************************************************************************** -* -* 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.txt for license information. -* -****************************************************************************** -* -* class TestObjCrystStructureAdapter -- unit tests for an adapter -* to the ObjCryst::Crystal class -* -* class TestObjCrystStructureBondGenerator -- unit tests for bond generator -* -*****************************************************************************/ + * + * 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.txt for license information. + * + ****************************************************************************** + * + * class TestObjCrystStructureAdapter -- unit tests for an adapter + * to the ObjCryst::Crystal class + * + * class TestObjCrystStructureBondGenerator -- unit tests for bond generator + * + *****************************************************************************/ #include #include @@ -41,268 +41,227 @@ using ObjCryst::ScatteringPowerAtom; namespace { -int countBonds(diffpy::srreal::BaseBondGenerator& bnds) -{ - int rv = 0; - for (bnds.rewind(); !bnds.finished(); bnds.next()) ++rv; - return rv; +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; +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 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; +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 +} // 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 : 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 @@ -310,405 +269,337 @@ class TestObjCrystStructureAdapter : public CxxTest::TestSuite // 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 : 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 : 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 : 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; +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 - +} // namespace // End of file diff --git a/src/tests/TestOverlapCalculator.hpp b/src/tests/TestOverlapCalculator.hpp index 69a379d7..820cfbaf 100644 --- a/src/tests/TestOverlapCalculator.hpp +++ b/src/tests/TestOverlapCalculator.hpp @@ -1,20 +1,20 @@ /***************************************************************************** -* -* 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.txt for license information. -* -****************************************************************************** -* -* class TestOverlapCalculator -- unit tests for the OverlapCalculator class -* -*****************************************************************************/ + * + * 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.txt for license information. + * + ****************************************************************************** + * + * class TestOverlapCalculator -- unit tests for the OverlapCalculator class + * + *****************************************************************************/ #include @@ -31,330 +31,291 @@ 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 : 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 diff --git a/src/tests/TestPDFBaseline.hpp b/src/tests/TestPDFBaseline.hpp index 3d7b38d6..f459663a 100644 --- a/src/tests/TestPDFBaseline.hpp +++ b/src/tests/TestPDFBaseline.hpp @@ -1,20 +1,20 @@ /***************************************************************************** -* -* 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 -* -*****************************************************************************/ + * + * 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 @@ -30,70 +30,58 @@ 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 : 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 +} // namespace srreal +} // namespace diffpy using diffpy::srreal::TestPDFBaseline; diff --git a/src/tests/TestPDFCalculator.hpp b/src/tests/TestPDFCalculator.hpp index 0a5c4ada..ce2fbe9b 100644 --- a/src/tests/TestPDFCalculator.hpp +++ b/src/tests/TestPDFCalculator.hpp @@ -1,20 +1,20 @@ /***************************************************************************** -* -* 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.txt for license information. -* -****************************************************************************** -* -* class TestPDFCalculator -- unit tests for PDFCalculator class -* -*****************************************************************************/ + * + * 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.txt for license information. + * + ****************************************************************************** + * + * class TestPDFCalculator -- unit tests for PDFCalculator class + * + *****************************************************************************/ #include @@ -29,254 +29,210 @@ 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 : 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 diff --git a/src/tests/TestPQEvaluator.hpp b/src/tests/TestPQEvaluator.hpp index ed99ded3..1d1cb19d 100644 --- a/src/tests/TestPQEvaluator.hpp +++ b/src/tests/TestPQEvaluator.hpp @@ -1,20 +1,20 @@ /***************************************************************************** -* -* 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 -* -*****************************************************************************/ + * + * 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 @@ -38,260 +38,226 @@ 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 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 : 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 +} // namespace srreal +} // namespace diffpy using diffpy::srreal::TestPQEvaluator; diff --git a/src/tests/TestPairCounter.hpp b/src/tests/TestPairCounter.hpp index 1ec6c390..0b37659d 100644 --- a/src/tests/TestPairCounter.hpp +++ b/src/tests/TestPairCounter.hpp @@ -1,20 +1,20 @@ /***************************************************************************** -* -* 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.txt for license information. -* -****************************************************************************** -* -* class TestPairCounter -- unit tests for PairCounter class -* -*****************************************************************************/ + * + * 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.txt for license information. + * + ****************************************************************************** + * + * class TestPairCounter -- unit tests for PairCounter class + * + *****************************************************************************/ #include @@ -24,133 +24,113 @@ 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)); +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_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_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); } - - - 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()); + 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]); } - - - 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]); + 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 diff --git a/src/tests/TestPeakProfile.hpp b/src/tests/TestPeakProfile.hpp index fba070c0..b72fe313 100644 --- a/src/tests/TestPeakProfile.hpp +++ b/src/tests/TestPeakProfile.hpp @@ -1,20 +1,20 @@ /***************************************************************************** -* -* 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.txt for license information. -* -****************************************************************************** -* -* class TestPeakProfile -- unit tests for various PeakProfile classes -* -*****************************************************************************/ + * + * 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.txt for license information. + * + ****************************************************************************** + * + * class TestPeakProfile -- unit tests for various PeakProfile classes + * + *****************************************************************************/ #include #include @@ -30,136 +30,113 @@ 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 : 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 diff --git a/src/tests/TestPeakWidthModel.hpp b/src/tests/TestPeakWidthModel.hpp index 846b81ae..9d19a701 100644 --- a/src/tests/TestPeakWidthModel.hpp +++ b/src/tests/TestPeakWidthModel.hpp @@ -1,20 +1,20 @@ /***************************************************************************** -* -* 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 -* -*****************************************************************************/ + * + * 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 @@ -35,170 +35,147 @@ 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 : 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 +} // namespace srreal +} // namespace diffpy using diffpy::srreal::TestPeakWidthModel; diff --git a/src/tests/TestPeriodicStructureAdapter.hpp b/src/tests/TestPeriodicStructureAdapter.hpp index a6d562d4..f4c711a9 100644 --- a/src/tests/TestPeriodicStructureAdapter.hpp +++ b/src/tests/TestPeriodicStructureAdapter.hpp @@ -1,23 +1,23 @@ /***************************************************************************** -* -* 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 -* -*****************************************************************************/ + * + * 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 @@ -37,220 +37,183 @@ using namespace std; namespace { -int countBonds(BaseBondGenerator& bnds) -{ - int rv = 0; - for (bnds.rewind(); !bnds.finished(); bnds.next()) ++rv; - return rv; +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; +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 testmsd1(const Tstru& stru, const Tbnds& bnds) { + bool anisotropy1 = stru->siteAnisotropy(bnds->site1()); + double rv = + meanSquareDisplacement(bnds->Ucartesian1(), bnds->r01(), anisotropy1); + return rv; } -} // namespace +} // 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 : 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 @@ -258,169 +221,149 @@ class TestPeriodicStructureAdapter : public CxxTest::TestSuite // 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 : 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 +} // namespace srreal +} // namespace diffpy using diffpy::srreal::TestPeriodicStructureAdapter; using diffpy::srreal::TestPeriodicStructureBondGenerator; diff --git a/src/tests/TestPointsInSphere.hpp b/src/tests/TestPointsInSphere.hpp index ffaec7e3..5cf4a1c1 100644 --- a/src/tests/TestPointsInSphere.hpp +++ b/src/tests/TestPointsInSphere.hpp @@ -1,22 +1,22 @@ /*********************************************************************** -* -* 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.txt for license information. -* -************************************************************************ -* -* Unit tests for PointsInSphere module -* -* Comments: -* -***********************************************************************/ + * + * 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.txt for license information. + * + ************************************************************************ + * + * Unit tests for PointsInSphere module + * + * Comments: + * + ***********************************************************************/ #include @@ -31,32 +31,32 @@ 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; +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) { + 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; +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. @@ -67,7 +67,7 @@ ostream& operator<<(ostream &s, const vidxgroup &x) } */ -} // namespace +} // namespace //////////////////////////////////////////////////////////////////////// // TestPointsInSphere @@ -75,146 +75,111 @@ ostream& operator<<(ostream &s, const vidxgroup &x) 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); - } +class TestPointsInSphere : public CxxTest::TestSuite { + private: + LatticeParameters *latpar; - void tearDown() - { - delete latpar; - } + public: + void setUp() { latpar = new LatticeParameters(1, 1, 1, 90, 90, 90); } -private: + void tearDown() { delete latpar; } - int count(double Rmin, double Rmax) - { - int c = 0; - PointsInSphere sph(Rmin, Rmax, *latpar); - for (; !sph.finished(); sph.next()) ++c; - return c; - } + 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; + 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())); } - -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)); + 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_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)); + } + + 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 diff --git a/src/tests/TestR3linalg.hpp b/src/tests/TestR3linalg.hpp index 7a3da835..f55ef0b5 100644 --- a/src/tests/TestR3linalg.hpp +++ b/src/tests/TestR3linalg.hpp @@ -1,20 +1,20 @@ /***************************************************************************** -* -* 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.txt for license information. -* -****************************************************************************** -* -* class TestR3linalg -- unit test suite for the R3linalg module -* -*****************************************************************************/ + * + * 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.txt for license information. + * + ****************************************************************************** + * + * class TestR3linalg -- unit test suite for the R3linalg module + * + *****************************************************************************/ #include @@ -24,115 +24,74 @@ 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 : 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 diff --git a/src/tests/TestRuntimePath.hpp b/src/tests/TestRuntimePath.hpp index 644732e3..1b559216 100644 --- a/src/tests/TestRuntimePath.hpp +++ b/src/tests/TestRuntimePath.hpp @@ -1,20 +1,20 @@ /***************************************************************************** -* -* 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 TestRuntimePath -- unit tests for the resolution of datafile paths -* -*****************************************************************************/ + * + * 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 TestRuntimePath -- unit tests for the resolution of datafile paths + * + *****************************************************************************/ #include #include @@ -26,63 +26,48 @@ using diffpy::runtimepath::datapath; - -class TestRuntimePath : public CxxTest::TestSuite -{ - - private: - - bool mhas_diffpyruntime; - std::string mdiffpyruntime; - - bool filereadable(std::string& fname) - { - std::ifstream fp(fname.c_str()); - return bool(fp); - } - - public: - - void setUp() - { - char* pe = getenv("DIFFPYRUNTIME"); - mhas_diffpyruntime = pe; - if (mhas_diffpyruntime) mdiffpyruntime = pe; - unsetenv("DIFFPYRUNTIME"); - } - - - void tearDown() - { - unsetenv("DIFFPYRUNTIME"); - if (mhas_diffpyruntime) - { - setenv("DIFFPYRUNTIME", mdiffpyruntime.c_str(), 1); - } - } - - - 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"); - } - - - void test_envvar_lookup() - { - using namespace std; - setenv("DIFFPYRUNTIME", "does/not/exist", 0); - TS_ASSERT_THROWS(datapath("f0_WaasKirf.dat"), runtime_error); - TS_ASSERT_THROWS(datapath("NaCl.cif"), runtime_error); - string td = prepend_testdata_dir(""); - setenv("DIFFPYRUNTIME", td.c_str(), 1); - string fnacl = prepend_testdata_dir("NaCl.cif"); - TS_ASSERT_EQUALS(fnacl, datapath("NaCl.cif")); - } - +class TestRuntimePath : public CxxTest::TestSuite { + private: + bool mhas_diffpyruntime; + std::string mdiffpyruntime; + + bool filereadable(std::string& fname) { + std::ifstream fp(fname.c_str()); + return bool(fp); + } + + public: + void setUp() { + char* pe = getenv("DIFFPYRUNTIME"); + mhas_diffpyruntime = pe; + if (mhas_diffpyruntime) mdiffpyruntime = pe; + unsetenv("DIFFPYRUNTIME"); + } + + void tearDown() { + unsetenv("DIFFPYRUNTIME"); + if (mhas_diffpyruntime) { + setenv("DIFFPYRUNTIME", mdiffpyruntime.c_str(), 1); + } + } + + 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"); + } + + void test_envvar_lookup() { + using namespace std; + setenv("DIFFPYRUNTIME", "does/not/exist", 0); + TS_ASSERT_THROWS(datapath("f0_WaasKirf.dat"), runtime_error); + TS_ASSERT_THROWS(datapath("NaCl.cif"), runtime_error); + string td = prepend_testdata_dir(""); + setenv("DIFFPYRUNTIME", td.c_str(), 1); + string fnacl = prepend_testdata_dir("NaCl.cif"); + TS_ASSERT_EQUALS(fnacl, datapath("NaCl.cif")); + } }; // End of file diff --git a/src/tests/TestScatteringFactorTable.hpp b/src/tests/TestScatteringFactorTable.hpp index 2315ba46..bcd7a714 100644 --- a/src/tests/TestScatteringFactorTable.hpp +++ b/src/tests/TestScatteringFactorTable.hpp @@ -1,21 +1,21 @@ /***************************************************************************** -* -* 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.txt for license information. -* -****************************************************************************** -* -* class TestScatteringFactorTable -- unit tests for implementations -* of the ScatteringFactorTable class -* -*****************************************************************************/ + * + * 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.txt for license information. + * + ****************************************************************************** + * + * class TestScatteringFactorTable -- unit tests for implementations + * of the ScatteringFactorTable class + * + *****************************************************************************/ #include #include @@ -28,184 +28,158 @@ 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()); - } - +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.hpp b/src/tests/TestSphericalShapeEnvelope.hpp index d8b602d1..73d0ec58 100644 --- a/src/tests/TestSphericalShapeEnvelope.hpp +++ b/src/tests/TestSphericalShapeEnvelope.hpp @@ -1,20 +1,20 @@ /***************************************************************************** -* -* 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.txt for license information. -* -****************************************************************************** -* -* class TestSphericalShapeEnvelope -- unit tests for SphericalShapeEnvelope -* -*****************************************************************************/ + * + * 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.txt for license information. + * + ****************************************************************************** + * + * class TestSphericalShapeEnvelope -- unit tests for SphericalShapeEnvelope + * + *****************************************************************************/ #include @@ -24,66 +24,48 @@ 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 : 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 diff --git a/src/tests/TestStepCutEnvelope.hpp b/src/tests/TestStepCutEnvelope.hpp index d3be0593..3c982ceb 100644 --- a/src/tests/TestStepCutEnvelope.hpp +++ b/src/tests/TestStepCutEnvelope.hpp @@ -1,20 +1,20 @@ /***************************************************************************** -* -* 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.txt for license information. -* -****************************************************************************** -* -* class TestStepCutEnvelope -- unit tests for StepCutEnvelope class -* -*****************************************************************************/ + * + * 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.txt for license information. + * + ****************************************************************************** + * + * class TestStepCutEnvelope -- unit tests for StepCutEnvelope class + * + *****************************************************************************/ #include @@ -24,67 +24,49 @@ 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 : 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 diff --git a/src/tests/objcryst_helpers.cpp b/src/tests/objcryst_helpers.cpp index 88c06ffa..78eb39ce 100644 --- a/src/tests/objcryst_helpers.cpp +++ b/src/tests/objcryst_helpers.cpp @@ -1,20 +1,20 @@ /***************************************************************************** -* -* 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.txt for license information. -* -****************************************************************************** -* -* shared helpers for loading diffpy structure objects. -* -*****************************************************************************/ + * + * 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.txt for license information. + * + ****************************************************************************** + * + * shared helpers for loading diffpy structure objects. + * + *****************************************************************************/ #include #include @@ -29,22 +29,20 @@ using namespace std; namespace { -void NoMessages(const string&) { } +void NoMessages(const string&) {} -} // namespace +} // 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; - ObjCryst::CIF cif(in); - ObjCryst::Crystal* crst = ObjCryst::CreateCrystalFromCIF(cif, false); - ObjCryst::fpObjCrystInformUser = OCMessages; - return crst; +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; + ObjCryst::CIF cif(in); + ObjCryst::Crystal* crst = ObjCryst::CreateCrystalFromCIF(cif, false); + ObjCryst::fpObjCrystInformUser = OCMessages; + return crst; } // End of file diff --git a/src/tests/objcryst_helpers.hpp b/src/tests/objcryst_helpers.hpp index 3fcfa1d9..48e8b2f2 100644 --- a/src/tests/objcryst_helpers.hpp +++ b/src/tests/objcryst_helpers.hpp @@ -1,20 +1,20 @@ /***************************************************************************** -* -* 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.txt for license information. -* -****************************************************************************** -* -* shared helpers for loading diffpy structure objects. -* -*****************************************************************************/ + * + * 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.txt for license information. + * + ****************************************************************************** + * + * shared helpers for loading diffpy structure objects. + * + *****************************************************************************/ #ifndef OBJCRYST_HELPERS_HPP_INCLUDED #define OBJCRYST_HELPERS_HPP_INCLUDED diff --git a/src/tests/serialization_helpers.hpp b/src/tests/serialization_helpers.hpp index e656a9e0..574f13fd 100644 --- a/src/tests/serialization_helpers.hpp +++ b/src/tests/serialization_helpers.hpp @@ -1,20 +1,20 @@ /***************************************************************************** -* -* 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.txt for license information. -* -****************************************************************************** -* -* helper template function for testing of serialization. -* -*****************************************************************************/ + * + * 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.txt for license information. + * + ****************************************************************************** + * + * helper template function for testing of serialization. + * + *****************************************************************************/ #ifndef SERIALIZATION_HELPERS_HPP_INCLUDED #define SERIALIZATION_HELPERS_HPP_INCLUDED @@ -22,17 +22,14 @@ #include #include - /// Return a duplicate object by saving and loading from a string template -T dumpandload(const T& src) -{ - using namespace diffpy; - std::string data = serialization_tostring(src); - T rv; - serialization_fromstring(rv, data); - return rv; +T dumpandload(const T& src) { + using namespace diffpy; + std::string data = serialization_tostring(src); + T rv; + serialization_fromstring(rv, data); + return rv; } - #endif // SERIALIZATION_HELPERS_HPP_INCLUDED diff --git a/src/tests/test_custompqconfig.hpp b/src/tests/test_custompqconfig.hpp index 5478f3fe..0405befd 100644 --- a/src/tests/test_custompqconfig.hpp +++ b/src/tests/test_custompqconfig.hpp @@ -1,20 +1,20 @@ /***************************************************************************** -* -* libdiffpy Complex Modeling Initiative -* (c) 2014 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. -* -****************************************************************************** -* -* Helper class for testing the effect of customPQConfig. -* -*****************************************************************************/ + * + * libdiffpy Complex Modeling Initiative + * (c) 2014 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. + * + ****************************************************************************** + * + * Helper class for testing the effect of customPQConfig. + * + *****************************************************************************/ #ifndef TEST_CUSTOMPQCONFIG_HPP_INCLUDED #define TEST_CUSTOMPQCONFIG_HPP_INCLUDED @@ -22,48 +22,39 @@ #include #include -using diffpy::srreal::StructureAdapterPtr; using diffpy::srreal::PeriodicStructureAdapter; using diffpy::srreal::PeriodicStructureAdapterPtr; +using diffpy::srreal::StructureAdapterPtr; // --------------------------------------------------------------------------- -class PeriodicAdapterWithPQConfig : - public PeriodicStructureAdapter -{ - public: - - // constructors - PeriodicAdapterWithPQConfig() { } - PeriodicAdapterWithPQConfig(const PeriodicStructureAdapter& src) - : PeriodicStructureAdapter(src) - { } - - // overloads - void customPQConfig(diffpy::srreal::PairQuantity* pq) const - { - std::map::const_iterator it; - for (it = cfg.begin(); it != cfg.end(); ++it) - { - pq->setDoubleAttr(it->first, it->second); - } - } - - // data - std::map cfg; - - private: - - // serialization - friend class boost::serialization::access; - template - void serialize(Archive& ar, const unsigned int version) - { - using boost::serialization::base_object; - ar & base_object(*this); - ar & cfg; - } - +class PeriodicAdapterWithPQConfig : public PeriodicStructureAdapter { + public: + // constructors + PeriodicAdapterWithPQConfig() {} + PeriodicAdapterWithPQConfig(const PeriodicStructureAdapter& src) + : PeriodicStructureAdapter(src) {} + + // overloads + void customPQConfig(diffpy::srreal::PairQuantity* pq) const { + std::map::const_iterator it; + for (it = cfg.begin(); it != cfg.end(); ++it) { + pq->setDoubleAttr(it->first, it->second); + } + } + + // data + std::map cfg; + + private: + // serialization + friend class boost::serialization::access; + template + void serialize(Archive& ar, const unsigned int version) { + using boost::serialization::base_object; + ar& base_object(*this); + ar & cfg; + } }; BOOST_CLASS_EXPORT(PeriodicAdapterWithPQConfig) @@ -71,16 +62,14 @@ BOOST_CLASS_EXPORT(PeriodicAdapterWithPQConfig) // Typedefs and Functions ---------------------------------------------------- typedef std::shared_ptr - PeriodicAdapterWithPQConfigPtr; - -inline -PeriodicAdapterWithPQConfigPtr -addCustomPQConfig(StructureAdapterPtr stru) -{ - PeriodicStructureAdapterPtr pstru = - std::dynamic_pointer_cast(stru); - return PeriodicAdapterWithPQConfigPtr( - new PeriodicAdapterWithPQConfig(*pstru)); + PeriodicAdapterWithPQConfigPtr; + +inline PeriodicAdapterWithPQConfigPtr addCustomPQConfig( + StructureAdapterPtr stru) { + PeriodicStructureAdapterPtr pstru = + std::dynamic_pointer_cast(stru); + return PeriodicAdapterWithPQConfigPtr( + new PeriodicAdapterWithPQConfig(*pstru)); } #endif // TEST_CUSTOMPQCONFIG_HPP_INCLUDED diff --git a/src/tests/test_helpers.cpp b/src/tests/test_helpers.cpp index 0781ada4..ef7d1563 100644 --- a/src/tests/test_helpers.cpp +++ b/src/tests/test_helpers.cpp @@ -1,20 +1,20 @@ /***************************************************************************** -* -* 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.txt for license information. -* -****************************************************************************** -* -* Definitions of helper functions for other unit tests. -* -*****************************************************************************/ + * + * 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.txt for license information. + * + ****************************************************************************** + * + * Definitions of helper functions for other unit tests. + * + *****************************************************************************/ #include #include @@ -38,104 +38,89 @@ // Path utilities ------------------------------------------------------------ -std::string prepend_tests_dir(const std::string& f) -{ - std::string rv = STRINGIFY(DIFFPYTESTSDIRPATH); - rv = rv + '/' + f; - return rv; +std::string prepend_tests_dir(const std::string& f) { + std::string rv = STRINGIFY(DIFFPYTESTSDIRPATH); + rv = rv + '/' + f; + return rv; } - -std::string prepend_testdata_dir(const std::string& f) -{ - using std::string; - string ft = "testdata/"; - ft += f; - string rv = prepend_tests_dir(ft); - return rv; +std::string prepend_testdata_dir(const std::string& f) { + using std::string; + string ft = "testdata/"; + ft += f; + string rv = prepend_tests_dir(ft); + return rv; } // Load PeriodicStructureAdapter from a PdfFit formatted file. -diffpy::srreal::StructureAdapterPtr - loadTestPeriodicStructure(const std::string& tailname) -{ - using namespace std; - using namespace diffpy::srreal; - using diffpy::runtimepath::LineReader; - using diffpy::mathutils::eps_eq; - // instantiate PeriodicStructureAdapter - StructureAdapterPtr pstru(new PeriodicStructureAdapter); - PeriodicStructureAdapter& stru = - static_cast(*pstru); - // open data file - string fullpath = prepend_testdata_dir(tailname); - ifstream fp(fullpath.c_str()); - LineReader line; - string fileformat; - // process structure file header - while (fp >> line) - { - if (line.wcount() >= 2 && boost::iequals(line.words[0], "format")) - { - fileformat = line.words[1]; - assert(boost::iequals(fileformat, "pdffit")); - } - if (line.wcount() == 7 && line.words[0] == "cell") - { - boost::replace_all(line.line, ",", " "); - string skip; - double a, b, c, alpha, beta, gamma; - istringstream fpwords(line.line); - fpwords >> skip >> a >> b >> c >> alpha >> beta >> gamma; - stru.setLatPar(a, b, c, alpha, beta, gamma); - } if (line.wcount() == 1 && line.words[0] == "atoms") break; +diffpy::srreal::StructureAdapterPtr loadTestPeriodicStructure( + const std::string& tailname) { + using namespace std; + using namespace diffpy::srreal; + using diffpy::mathutils::eps_eq; + using diffpy::runtimepath::LineReader; + // instantiate PeriodicStructureAdapter + StructureAdapterPtr pstru(new PeriodicStructureAdapter); + PeriodicStructureAdapter& stru = + static_cast(*pstru); + // open data file + string fullpath = prepend_testdata_dir(tailname); + ifstream fp(fullpath.c_str()); + LineReader line; + string fileformat; + // process structure file header + while (fp >> line) { + if (line.wcount() >= 2 && boost::iequals(line.words[0], "format")) { + fileformat = line.words[1]; + assert(boost::iequals(fileformat, "pdffit")); + } + if (line.wcount() == 7 && line.words[0] == "cell") { + boost::replace_all(line.line, ",", " "); + string skip; + double a, b, c, alpha, beta, gamma; + istringstream fpwords(line.line); + fpwords >> skip >> a >> b >> c >> alpha >> beta >> gamma; + stru.setLatPar(a, b, c, alpha, beta, gamma); + } + if (line.wcount() == 1 && line.words[0] == "atoms") break; + } + assert(!fileformat.empty()); + // load atoms + while (true) { + Atom ai; + double ignore; + R3::Vector& xyz = ai.xyz_cartn; + R3::Matrix& Uc = ai.uij_cartn; + fp >> ai.atomtype >> xyz[0] >> xyz[1] >> xyz[2] >> ai.occupancy >> ignore >> + ignore >> ignore >> ignore >> Uc(0, 0) >> Uc(1, 1) >> Uc(2, 2) >> + ignore >> ignore >> ignore >> Uc(0, 1) >> Uc(0, 2) >> Uc(1, 2) >> + ignore >> ignore >> ignore; + Uc(1, 0) = Uc(0, 1); + Uc(2, 0) = Uc(0, 2); + Uc(2, 1) = Uc(1, 2); + if (!fp) break; + // convert string symbol to standard case + boost::to_lower(ai.atomtype); + string::iterator tt = ai.atomtype.begin(); + for (; tt != ai.atomtype.end(); ++tt) { + if (isalpha(*tt)) { + *tt = toupper(*tt); + break; + } } - assert(!fileformat.empty()); - // load atoms - while (true) - { - Atom ai; - double ignore; - R3::Vector& xyz = ai.xyz_cartn; - R3::Matrix& Uc = ai.uij_cartn; - fp >> - ai.atomtype >> xyz[0] >> xyz[1] >> xyz[2] >> ai.occupancy >> - ignore >> ignore >> ignore >> ignore >> - Uc(0, 0) >> Uc(1, 1) >> Uc(2, 2) >> - ignore >> ignore >> ignore >> - Uc(0, 1) >> Uc(0, 2) >> Uc(1, 2) >> - ignore >> ignore >> ignore; - Uc(1, 0) = Uc(0, 1); - Uc(2, 0) = Uc(0, 2); - Uc(2, 1) = Uc(1, 2); - if (!fp) break; - // convert string symbol to standard case - boost::to_lower(ai.atomtype); - string::iterator tt = ai.atomtype.begin(); - for (; tt != ai.atomtype.end(); ++tt) - { - if (isalpha(*tt)) - { - *tt = toupper(*tt); - break; - } - } - // convert coordinates and the ADP matrix and determine anisotropy - stru.toCartesian(ai); + // convert coordinates and the ADP matrix and determine anisotropy + stru.toCartesian(ai); + ai.anisotropy = !eps_eq(Uc(0, 0), Uc(1, 1)) || !eps_eq(Uc(0, 0), Uc(2, 2)); + for (int i = 0; !ai.anisotropy && i < R3::Ndim; ++i) { + for (int j = i + 1; j < R3::Ndim; ++j) { ai.anisotropy = - !eps_eq(Uc(0, 0), Uc(1, 1)) || !eps_eq(Uc(0, 0), Uc(2, 2)); - for (int i = 0; !ai.anisotropy && i < R3::Ndim; ++i) - { - for (int j = i + 1; j < R3::Ndim; ++j) - { - ai.anisotropy = ai.anisotropy || - !eps_eq(0.0, Uc(i, j)) || !eps_eq(0.0, Uc(j, i)); - } - } - stru.append(ai); + ai.anisotropy || !eps_eq(0.0, Uc(i, j)) || !eps_eq(0.0, Uc(j, i)); + } } - return pstru; + stru.append(ai); + } + return pstru; } // End of test_helpers.cpp diff --git a/src/tests/test_helpers.hpp b/src/tests/test_helpers.hpp index ef0d2430..cf01875a 100644 --- a/src/tests/test_helpers.hpp +++ b/src/tests/test_helpers.hpp @@ -1,20 +1,20 @@ /***************************************************************************** -* -* 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.txt for license information. -* -****************************************************************************** -* -* Helper functions used in other unit tests. -* -*****************************************************************************/ + * + * 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.txt for license information. + * + ****************************************************************************** + * + * Helper functions used in other unit tests. + * + *****************************************************************************/ #ifndef TEST_HELPERS_HPP_INCLUDED #define TEST_HELPERS_HPP_INCLUDED @@ -25,7 +25,7 @@ std::string prepend_tests_dir(const std::string& f); std::string prepend_testdata_dir(const std::string& f); -diffpy::srreal::StructureAdapterPtr - loadTestPeriodicStructure(const std::string& tailname); +diffpy::srreal::StructureAdapterPtr loadTestPeriodicStructure( + const std::string& tailname); #endif // TEST_HELPERS_HPP_INCLUDED From 076961951eee70e89a9d8d059f7c5a933ef29def Mon Sep 17 00:00:00 2001 From: SYMSCAE Date: Wed, 28 Oct 2026 15:01:22 -0700 Subject: [PATCH 39/56] chore: add .git-blame-ignore-revs and news for the Windows merge --- .git-blame-ignore-revs | 8 ++++++++ news/windows.rst | 28 ++++++++++++++++++++++++++++ 2 files changed, 36 insertions(+) create mode 100644 .git-blame-ignore-revs create mode 100644 news/windows.rst diff --git a/.git-blame-ignore-revs b/.git-blame-ignore-revs new file mode 100644 index 00000000..d2c8baf2 --- /dev/null +++ b/.git-blame-ignore-revs @@ -0,0 +1,8 @@ +# Commits that only reformat code. Use with +# git config blame.ignoreRevsFile .git-blame-ignore-revs +# GitHub's blame view reads this file automatically. + +# style: apply clang-format (Google style) to C++ sources +69c43f493b013e13a8e004e830bda197501beaea +# add precommit hooks and configuration files (upstream clang-format reformat) +ba376c74884c9e57eb6fb86f17f2e482389ac547 diff --git a/news/windows.rst b/news/windows.rst new file mode 100644 index 00000000..c4e6764a --- /dev/null +++ b/news/windows.rst @@ -0,0 +1,28 @@ +**Added:** + +* Support building libdiffpy on Windows with MSVC, clang or MinGW, merged from the upstream ``windows`` branch. +* Export the public API from the Windows DLL with the ``DLL_EXPORT`` macro in ``diffpy/Export.hpp``. +* Install headers, libraries and data under ``$PREFIX`` or ``$CONDA_PREFIX`` by default, using the ``Library/`` layout on Windows. +* Add a Windows build and test job to the pull request CI. +* Add a clang-format pre-commit hook and ``.git-blame-ignore-revs`` for the reformat commits. + +**Changed:** + +* Format all C++ sources with clang-format (Google style). +* Require ObjCryst by default. Build with ``enable_objcryst=False`` to skip it. + +**Deprecated:** + +* + +**Removed:** + +* + +**Fixed:** + +* + +**Security:** + +* From a1e7fa7be108effe8f556216121c4bb6bc1110cc Mon Sep 17 00:00:00 2001 From: SYMSCAE Date: Wed, 28 Oct 2026 15:05:43 -0700 Subject: [PATCH 40/56] build: fix Windows build settings --- SConstruct | 6 +++++- news/windows.rst | 1 - src/SConscript | 12 +++++++++--- src/diffpy/SConscript | 3 +++ src/tests/SConscript | 6 +++++- 5 files changed, 22 insertions(+), 6 deletions(-) diff --git a/SConstruct b/SConstruct index f427e541..dd5d40a6 100644 --- a/SConstruct +++ b/SConstruct @@ -70,7 +70,11 @@ if env['PLATFORM'] == "win32": lib_path = pjoin(env['prefix'], 'Library', 'lib') shared_path = pjoin(env['prefix'], 'Library', 'share') - env['ENV']['TMP'] = os.environ['TMP'] + # 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') diff --git a/news/windows.rst b/news/windows.rst index c4e6764a..98a49908 100644 --- a/news/windows.rst +++ b/news/windows.rst @@ -3,7 +3,6 @@ * Support building libdiffpy on Windows with MSVC, clang or MinGW, merged from the upstream ``windows`` branch. * Export the public API from the Windows DLL with the ``DLL_EXPORT`` macro in ``diffpy/Export.hpp``. * Install headers, libraries and data under ``$PREFIX`` or ``$CONDA_PREFIX`` by default, using the ``Library/`` layout on Windows. -* Add a Windows build and test job to the pull request CI. * Add a clang-format pre-commit hook and ``.git-blame-ignore-revs`` for the reformat commits. **Changed:** diff --git a/src/SConscript b/src/SConscript index 42f0b15e..7d69998a 100644 --- a/src/SConscript +++ b/src/SConscript @@ -48,8 +48,7 @@ if env['PLATFORM'] == 'darwin': # Compiler specific options if env['PLATFORM'] == 'win32': - # BUILD_DLL makes DLL_EXPORT export symbols, see diffpy/Export.hpp. - env.AppendUnique(CPPDEFINES=['BUILD_DLL', '_USE_MATH_DEFINES']) + env.AppendUnique(CPPDEFINES=['_USE_MATH_DEFINES']) if env['tool'] == 'clang': # clang++ on Windows env['CC'] = 'clang' @@ -63,7 +62,10 @@ if env['PLATFORM'] == 'win32': env.AppendUnique(LINKFLAGS='-m64') else: # Visual C++ - env.PrependUnique(CCFLAGS=['/Ox', '/EHsc', '/MD', '/Wall']) + # /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']) @@ -167,6 +169,10 @@ if 'sdist' in COMMAND_LINE_TARGETS: # 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': diff --git a/src/diffpy/SConscript b/src/diffpy/SConscript index f8b8ad6f..01126c05 100644 --- a/src/diffpy/SConscript +++ b/src/diffpy/SConscript @@ -12,6 +12,9 @@ if env['PLATFORM'] == "win32": 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 diff --git a/src/tests/SConscript b/src/tests/SConscript index 261c59f5..c41e79cd 100644 --- a/src/tests/SConscript +++ b/src/tests/SConscript @@ -15,7 +15,11 @@ else: use_our_library = not env['test_installed'] env_test.PrependUnique(LIBS='diffpy', LIBPATH=lib_dir, delete_existing=1) -env_test.PrependUnique(LINKFLAGS="-Wl,-rpath,%r" % lib_dir) +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 -------------------------------------------------------------------- From 8eca86a88d9af0cee2a0d4d95a011f0e0b29d0fb Mon Sep 17 00:00:00 2001 From: SYMSCAE Date: Wed, 28 Oct 2026 15:32:47 -0700 Subject: [PATCH 41/56] fix: resolve release versions from lightweight Git tags --- site_scons/fallback_version.py | 2 +- site_scons/libdiffpybuildutils.py | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/site_scons/fallback_version.py b/site_scons/fallback_version.py index 59b8cdd0..d9fca94a 100644 --- a/site_scons/fallback_version.py +++ b/site_scons/fallback_version.py @@ -8,4 +8,4 @@ Update FALLBACK_VERSION when tagging a new release. """ -FALLBACK_VERSION = "1.4.0.post0" +FALLBACK_VERSION = "1.4.1.post0" diff --git a/site_scons/libdiffpybuildutils.py b/site_scons/libdiffpybuildutils.py index 28580aa2..755fc610 100644 --- a/site_scons/libdiffpybuildutils.py +++ b/site_scons/libdiffpybuildutils.py @@ -28,7 +28,8 @@ def gitinfo(): return _cached_gitinfo nullfile = open(os.devnull, "w") kw = dict(stdout=PIPE, stderr=nullfile, cwd=MYDIR, universal_newlines=True) - proc = Popen(["git", "describe", "--match=v[[:digit:]]*"], **kw) + # Release tags may be lightweight, as with v1.4.1. + proc = Popen(["git", "describe", "--tags", "--match=v[[:digit:]]*"], **kw) desc = proc.stdout.read() if proc.wait(): _cached_gitinfo = {} From c4af821abb42ef4396b42271f8c67276cc972a47 Mon Sep 17 00:00:00 2001 From: SYMSCAE Date: Wed, 28 Oct 2026 16:19:58 -0700 Subject: [PATCH 42/56] fix: run C++ matrix tests and repair Windows DLL support --- .github/scripts/configure-matrix.py | 32 ++++++ .github/workflows/matrix-and-codecov.yml | 98 +++++++++++++++++-- SConstruct | 1 + src/diffpy/runtimepath.hpp | 7 +- src/diffpy/srreal/AtomUtils.hpp | 5 +- src/diffpy/srreal/Lattice.hpp | 3 +- src/diffpy/srreal/PairCounter.hpp | 3 +- .../srreal/PeriodicStructureAdapter.hpp | 2 +- src/diffpy/srreal/PointsInSphere.hpp | 9 +- src/diffpy/srreal/QuantityType.hpp | 6 +- src/diffpy/srreal/R3linalg.hpp | 20 ++-- src/diffpy/srreal/StructureAdapter.hpp | 7 +- src/diffpy/srreal/scatteringfactordata.hpp | 11 ++- src/tests/SConscript | 7 +- src/tests/TestRuntimePath.hpp | 23 +++-- 15 files changed, 186 insertions(+), 48 deletions(-) create mode 100644 .github/scripts/configure-matrix.py diff --git a/.github/scripts/configure-matrix.py b/.github/scripts/configure-matrix.py new file mode 100644 index 00000000..b4930d42 --- /dev/null +++ b/.github/scripts/configure-matrix.py @@ -0,0 +1,32 @@ +"""Configure SCons and CxxTest for the CI matrix.""" + +from pathlib import Path + +test_requirements = Path("requirements/tests.txt").read_text().splitlines() +Path("build").mkdir(exist_ok=True) +Path("build/matrix-tests.txt").write_text( + "\n".join(line for line in test_requirements if line.strip() != "cxxtest") + + "\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': + 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 969d4952..fdbde6a1 100644 --- a/.github/workflows/matrix-and-codecov.yml +++ b/.github/workflows/matrix-and-codecov.yml @@ -12,10 +12,94 @@ on: jobs: matrix-coverage: - uses: scikit-package/release-scripts/.github/workflows/_matrix-and-codecov-on-merge-to-main.yml@v0 - with: - project: libdiffpy - c_extension: true - headless: false - secrets: - CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }} + name: C++ tests (${{ matrix.os }}, Python ${{ matrix.python-version }}) + runs-on: ${{ matrix.os }} + timeout-minutes: 45 + strategy: + fail-fast: false + matrix: + os: [ubuntu-latest, windows-latest, macos-latest, macos-15-intel] + python-version: ["3.12", "3.13", "3.14"] + include: + - os: ubuntu-latest + shell: bash -l {0} + build: coverage + - os: windows-latest + shell: cmd /C CALL {0} + build: fast + - os: macos-latest + shell: bash -l {0} + build: fast + - os: macos-15-intel + shell: bash -l {0} + build: fast + defaults: + run: + shell: ${{ matrix.shell }} + + steps: + - name: Check out libdiffpy + uses: actions/checkout@v6 + with: + fetch-depth: 0 + + - name: Fetch upstream release tags + run: git fetch --tags https://github.com/diffpy/libdiffpy.git + + # CxxTest is header-only and its generator runs directly from source. + # The conda-forge package is not available on every matrix platform. + - name: Check out CxxTest 4.4 + uses: actions/checkout@v6 + with: + repository: CxxTest/cxxtest + ref: 191adddb3876ab389c0c856e1c03874bf70f8ee4 + path: build/cxxtest + + - name: Initialize miniconda + uses: conda-incubator/setup-miniconda@v3 + with: + activate-environment: test + channels: conda-forge + conda-remove-defaults: "true" + auto-update-conda: true + auto-activate-base: false + python-version: ${{ matrix.python-version }} + + - name: Prepare C++ test configuration + run: python .github/scripts/configure-matrix.py + + - name: Install requirements + run: >- + conda install --yes --override-channels -c conda-forge + --file requirements/build.txt + --file requirements/conda.txt + --file build/matrix-tests.txt + + - name: Build and run C++ tests + run: scons -j2 build=${{ matrix.build }} test + + - name: Generate coverage report + if: runner.os == 'Linux' && matrix.python-version == '3.14' + run: >- + gcovr --gcov-executable "$CONDA_PREFIX/bin/gcov" + --root . --filter 'build/coverage-.*/diffpy/' + --xml coverage.xml + + - name: Save coverage report + if: runner.os == 'Linux' && matrix.python-version == '3.14' + uses: actions/upload-artifact@v4 + with: + name: coverage-report + path: coverage.xml + if-no-files-found: error + + - name: Upload coverage to Codecov + # Fork push builds have no upstream Codecov token; keep their artifact. + if: >- + runner.os == 'Linux' && matrix.python-version == '3.14' && + github.repository == 'diffpy/libdiffpy' + uses: codecov/codecov-action@v5 + with: + files: coverage.xml + fail_ci_if_error: true + token: ${{ secrets.CODECOV_TOKEN }} diff --git a/SConstruct b/SConstruct index dd5d40a6..a371be1a 100644 --- a/SConstruct +++ b/SConstruct @@ -33,6 +33,7 @@ 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()) ) diff --git a/src/diffpy/runtimepath.hpp b/src/diffpy/runtimepath.hpp index e7eb4562..36f7c2e2 100644 --- a/src/diffpy/runtimepath.hpp +++ b/src/diffpy/runtimepath.hpp @@ -23,6 +23,7 @@ #include #include #include +#include namespace diffpy { namespace runtimepath { @@ -39,10 +40,10 @@ namespace runtimepath { /// /// Throw runtime_error if the base directory cannot be found, including an /// invalid explicit override. File existence is checked by the data readers. -std::string datapath(const std::string& f); +DLL_EXPORT std::string datapath(const std::string& f); /// Helper class for loading text data -class LineReader { +class DLL_EXPORT LineReader { public: // constructor LineReader(); @@ -64,7 +65,7 @@ class LineReader { }; // non-member functions for the LineReader class -std::istream& operator>>(std::istream&, LineReader&); +DLL_EXPORT std::istream& operator>>(std::istream&, LineReader&); } // namespace runtimepath } // namespace diffpy diff --git a/src/diffpy/srreal/AtomUtils.hpp b/src/diffpy/srreal/AtomUtils.hpp index 489ffedf..ad82bb87 100644 --- a/src/diffpy/srreal/AtomUtils.hpp +++ b/src/diffpy/srreal/AtomUtils.hpp @@ -20,16 +20,17 @@ #define ATOMUTILS_HPP_INCLUDED #include +#include namespace diffpy { namespace srreal { /// Return bare element symbol from atom symbol that may be isotope or ion, /// for example "Ca2+" or "12-C". -std::string atomBareSymbol(const std::string& atomtype); +DLL_EXPORT std::string atomBareSymbol(const std::string& atomtype); /// Return valence of possibly ionic symbol such as "S2-" or "Cl-". -int atomValence(const std::string& atomtype); +DLL_EXPORT int atomValence(const std::string& atomtype); } // namespace srreal } // namespace diffpy diff --git a/src/diffpy/srreal/Lattice.hpp b/src/diffpy/srreal/Lattice.hpp index 83483029..cd897655 100644 --- a/src/diffpy/srreal/Lattice.hpp +++ b/src/diffpy/srreal/Lattice.hpp @@ -22,12 +22,13 @@ #include #include +#include #include namespace diffpy { namespace srreal { -class Lattice { +class DLL_EXPORT Lattice { public: // constructors diff --git a/src/diffpy/srreal/PairCounter.hpp b/src/diffpy/srreal/PairCounter.hpp index 86a8d136..f4e14125 100644 --- a/src/diffpy/srreal/PairCounter.hpp +++ b/src/diffpy/srreal/PairCounter.hpp @@ -19,12 +19,13 @@ #ifndef PAIRCOUNTER_HPP_INCLUDED #define PAIRCOUNTER_HPP_INCLUDED +#include #include namespace diffpy { namespace srreal { -class PairCounter : public PairQuantity { +class DLL_EXPORT PairCounter : public PairQuantity { public: // constructor PairCounter(); diff --git a/src/diffpy/srreal/PeriodicStructureAdapter.hpp b/src/diffpy/srreal/PeriodicStructureAdapter.hpp index ddeeb613..3b2cb43b 100644 --- a/src/diffpy/srreal/PeriodicStructureAdapter.hpp +++ b/src/diffpy/srreal/PeriodicStructureAdapter.hpp @@ -70,7 +70,7 @@ DLL_EXPORT bool operator==(const PeriodicStructureAdapter&, DLL_EXPORT bool operator!=(const PeriodicStructureAdapter&, const PeriodicStructureAdapter&); -class PeriodicStructureBondGenerator : public BaseBondGenerator { +class DLL_EXPORT PeriodicStructureBondGenerator : public BaseBondGenerator { public: // constructors PeriodicStructureBondGenerator(StructureAdapterConstPtr); diff --git a/src/diffpy/srreal/PointsInSphere.hpp b/src/diffpy/srreal/PointsInSphere.hpp index 283e79d8..bb6f8657 100644 --- a/src/diffpy/srreal/PointsInSphere.hpp +++ b/src/diffpy/srreal/PointsInSphere.hpp @@ -52,13 +52,14 @@ #define POINTSINSPHERE_HPP_INCLUDED #include +#include namespace diffpy { namespace srreal { namespace pointsinsphere { -class LatticeParameters { +class DLL_EXPORT LatticeParameters { public: // data // input arguments @@ -82,7 +83,7 @@ class LatticeParameters { } // namespace pointsinsphere -class PointsInSphere { +class DLL_EXPORT PointsInSphere { public: // constructors PointsInSphere(double rmin, double rmax, @@ -141,7 +142,7 @@ class PointsInSphere { void init(); }; -class ReflectionsInQminQmax { +class DLL_EXPORT ReflectionsInQminQmax { public: // constructors ReflectionsInQminQmax(double _Qmin, double _Qmax, @@ -176,7 +177,7 @@ class ReflectionsInQminQmax { PointsInSphere sph; }; -class ReflectionsInDmaxDmin : public ReflectionsInQminQmax { +class DLL_EXPORT ReflectionsInDmaxDmin : public ReflectionsInQminQmax { public: // constructors ReflectionsInDmaxDmin(double dmax, double dmin, diff --git a/src/diffpy/srreal/QuantityType.hpp b/src/diffpy/srreal/QuantityType.hpp index 6fccadc1..531a25e4 100644 --- a/src/diffpy/srreal/QuantityType.hpp +++ b/src/diffpy/srreal/QuantityType.hpp @@ -26,12 +26,12 @@ #include #include -#include - namespace diffpy { namespace srreal { -class DLL_EXPORT QuantityType : public std::vector { +// This header-only type needs no DLL export. Exporting the class also exports +// std::vector on MSVC, conflicting with other libraries using it. +class QuantityType : public std::vector { private: typedef std::vector Base; diff --git a/src/diffpy/srreal/R3linalg.hpp b/src/diffpy/srreal/R3linalg.hpp index 5839eda2..5331138f 100644 --- a/src/diffpy/srreal/R3linalg.hpp +++ b/src/diffpy/srreal/R3linalg.hpp @@ -138,10 +138,10 @@ class DLL_EXPORT Matrix // Functions -const Matrix& identity(); -const Matrix& zeromatrix(); -double determinant(const Matrix& A); -const Matrix& inverse(const Matrix& A); +DLL_EXPORT const Matrix& identity(); +DLL_EXPORT const Matrix& zeromatrix(); +DLL_EXPORT double determinant(const Matrix& A); +DLL_EXPORT const Matrix& inverse(const Matrix& A); const Vector& floor(const Vector&); template @@ -176,8 +176,8 @@ inline bool operator!=(const Matrix& A, const Matrix& B) { return !(A == B); } // Hashing ------------------------------------------------------------------- -size_t hash_value(const Vector& v); -size_t hash_value(const Matrix& A); +DLL_EXPORT size_t hash_value(const Vector& v); +DLL_EXPORT size_t hash_value(const Matrix& A); // Inlined functions --------------------------------------------------------- @@ -245,15 +245,17 @@ namespace mathutils { // Template specializations -------------------------------------------------- template <> -bool EpsilonLess::operator()( +DLL_EXPORT bool EpsilonLess::operator()( const srreal::R3::Matrix& A, const srreal::R3::Matrix& B) const; template <> -bool EpsilonEqual::operator()( +DLL_EXPORT bool +EpsilonEqual::operator()( const srreal::R3::Vector& u, const srreal::R3::Vector& v) const; template <> -bool EpsilonEqual::operator()( +DLL_EXPORT bool +EpsilonEqual::operator()( const srreal::R3::Matrix& A, const srreal::R3::Matrix& B) const; } // namespace mathutils diff --git a/src/diffpy/srreal/StructureAdapter.hpp b/src/diffpy/srreal/StructureAdapter.hpp index 5aae9226..a580f6f9 100644 --- a/src/diffpy/srreal/StructureAdapter.hpp +++ b/src/diffpy/srreal/StructureAdapter.hpp @@ -105,11 +105,12 @@ class DLL_EXPORT StructureAdapter DLL_EXPORT StructureAdapterPtr emptyStructureAdapter(); /// Calculate MSD along specified direction in Cartesian space. -double meanSquareDisplacement(const R3::Matrix& Uijcartn, const R3::Vector& s, - bool anisotropy = true); +DLL_EXPORT double meanSquareDisplacement(const R3::Matrix& Uijcartn, + const R3::Vector& s, + bool anisotropy = true); /// Maximum diagonal Uii element from all atoms in the structure. -double maxUii(StructureAdapterPtr stru); +DLL_EXPORT double maxUii(StructureAdapterPtr stru); /// Translate an index container to a vector of string symbols template diff --git a/src/diffpy/srreal/scatteringfactordata.hpp b/src/diffpy/srreal/scatteringfactordata.hpp index 3d209f75..24861d03 100644 --- a/src/diffpy/srreal/scatteringfactordata.hpp +++ b/src/diffpy/srreal/scatteringfactordata.hpp @@ -26,24 +26,25 @@ #define SCATTERINGFACTORS_HPP_INCLUDED #include +#include namespace diffpy { namespace srreal { /// X-ray scattering factor of an element or ion a given Q -double fxrayatq(const std::string& smbl, double q); +DLL_EXPORT double fxrayatq(const std::string& smbl, double q); /// X-ray scattering factor of an element or ion a given sin(theta)/lambda -double fxrayatstol(const std::string& smbl, double stol); +DLL_EXPORT double fxrayatstol(const std::string& smbl, double stol); /// Electron scattering factor of an element or ion a given Q -double felectronatq(const std::string& smbl, double q); +DLL_EXPORT double felectronatq(const std::string& smbl, double q); /// Number of electrons for an element or ion -int electronnumber(const std::string& smbl); +DLL_EXPORT int electronnumber(const std::string& smbl); /// Coherent scattering length of an element or isotope in fm -double bcneutron(const std::string& smbl); +DLL_EXPORT double bcneutron(const std::string& smbl); } // namespace srreal } // namespace diffpy diff --git a/src/tests/SConscript b/src/tests/SConscript index c41e79cd..0dec35f2 100644 --- a/src/tests/SConscript +++ b/src/tests/SConscript @@ -37,14 +37,17 @@ def srcincluded(f): return rv # alltests -- the unit test driver source files -test_sources = [f for f in GlobSources('Test*.hpp') if srcincluded(f)] +# 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 +thisdir = Dir('.').srcnode().abspath.replace('\\', '/') env_th = env_test.Clone() env_th.AppendUnique(CPPDEFINES=dict(DIFFPYTESTSDIRPATH=thisdir)) thobj = env_th.Object('test_helpers.cpp') diff --git a/src/tests/TestRuntimePath.hpp b/src/tests/TestRuntimePath.hpp index 1b559216..99c7c879 100644 --- a/src/tests/TestRuntimePath.hpp +++ b/src/tests/TestRuntimePath.hpp @@ -36,19 +36,28 @@ class TestRuntimePath : public CxxTest::TestSuite { return bool(fp); } + void setRuntime(const char* value) { +#ifdef _WIN32 + TS_ASSERT_EQUALS(_putenv_s("DIFFPYRUNTIME", value ? value : ""), 0); +#else + if (value) { + TS_ASSERT_EQUALS(setenv("DIFFPYRUNTIME", value, 1), 0); + } else { + TS_ASSERT_EQUALS(unsetenv("DIFFPYRUNTIME"), 0); + } +#endif + } + public: void setUp() { char* pe = getenv("DIFFPYRUNTIME"); mhas_diffpyruntime = pe; if (mhas_diffpyruntime) mdiffpyruntime = pe; - unsetenv("DIFFPYRUNTIME"); + setRuntime(nullptr); } void tearDown() { - unsetenv("DIFFPYRUNTIME"); - if (mhas_diffpyruntime) { - setenv("DIFFPYRUNTIME", mdiffpyruntime.c_str(), 1); - } + setRuntime(mhas_diffpyruntime ? mdiffpyruntime.c_str() : nullptr); } void test_datapath() { @@ -60,11 +69,11 @@ class TestRuntimePath : public CxxTest::TestSuite { void test_envvar_lookup() { using namespace std; - setenv("DIFFPYRUNTIME", "does/not/exist", 0); + 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(""); - setenv("DIFFPYRUNTIME", td.c_str(), 1); + setRuntime(td.c_str()); string fnacl = prepend_testdata_dir("NaCl.cif"); TS_ASSERT_EQUALS(fnacl, datapath("NaCl.cif")); } From 97130d56c2c6f18d9f673f4df83100796e764cce Mon Sep 17 00:00:00 2001 From: SYMSCAE Date: Wed, 28 Oct 2026 16:41:25 -0700 Subject: [PATCH 43/56] ci: speed up environment setup and C++ builds --- .github/scripts/configure-matrix.py | 31 ++++++++++++++++++++--- .github/workflows/matrix-and-codecov.yml | 32 ++++++++++++++---------- .github/workflows/tests-on-pr.yml | 8 ++++-- 3 files changed, 52 insertions(+), 19 deletions(-) diff --git a/.github/scripts/configure-matrix.py b/.github/scripts/configure-matrix.py index b4930d42..7687a055 100644 --- a/.github/scripts/configure-matrix.py +++ b/.github/scripts/configure-matrix.py @@ -1,11 +1,25 @@ """Configure SCons and CxxTest for the CI matrix.""" +import json from pathlib import Path -test_requirements = Path("requirements/tests.txt").read_text().splitlines() +requirements = ["python"] +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": + requirements.append(spec) + Path("build").mkdir(exist_ok=True) -Path("build/matrix-tests.txt").write_text( - "\n".join(line for line in test_requirements if line.strip() != "cxxtest") +Path("build/matrix-environment.yml").write_text( + json.dumps( + { + "name": "test", + "channels": ["conda-forge"], + "dependencies": requirements, + }, + indent=2, + ) + "\n" ) @@ -19,7 +33,16 @@ env.Replace(CXXTEST=os.path.join(cxxtest, 'bin', 'cxxtestgen'), CXXTEST_INSTALL_DIR=cxxtest, CXXTEST_PYTHON=sys.executable) -if env['PLATFORM'] != 'win32': +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')) diff --git a/.github/workflows/matrix-and-codecov.yml b/.github/workflows/matrix-and-codecov.yml index fdbde6a1..4379fce8 100644 --- a/.github/workflows/matrix-and-codecov.yml +++ b/.github/workflows/matrix-and-codecov.yml @@ -10,6 +10,10 @@ on: - published workflow_dispatch: +concurrency: + group: matrix-${{ github.ref }} + cancel-in-progress: ${{ github.event_name != 'release' }} + jobs: matrix-coverage: name: C++ tests (${{ matrix.os }}, Python ${{ matrix.python-version }}) @@ -24,15 +28,19 @@ jobs: - os: ubuntu-latest shell: bash -l {0} build: coverage + jobs: 4 - os: windows-latest shell: cmd /C CALL {0} build: fast + jobs: 4 - os: macos-latest shell: bash -l {0} build: fast + jobs: 2 - os: macos-15-intel shell: bash -l {0} build: fast + jobs: 4 defaults: run: shell: ${{ matrix.shell }} @@ -55,28 +63,26 @@ jobs: ref: 191adddb3876ab389c0c856e1c03874bf70f8ee4 path: build/cxxtest - - name: Initialize miniconda + - name: Prepare C++ test configuration + shell: python {0} + run: | + import runpy + runpy.run_path('.github/scripts/configure-matrix.py') + + - name: Create build environment uses: conda-incubator/setup-miniconda@v3 with: activate-environment: test channels: conda-forge conda-remove-defaults: "true" - auto-update-conda: true + auto-update-conda: false auto-activate-base: false python-version: ${{ matrix.python-version }} - - - name: Prepare C++ test configuration - run: python .github/scripts/configure-matrix.py - - - name: Install requirements - run: >- - conda install --yes --override-channels -c conda-forge - --file requirements/build.txt - --file requirements/conda.txt - --file build/matrix-tests.txt + environment-file: build/matrix-environment.yml + channel-priority: strict - name: Build and run C++ tests - run: scons -j2 build=${{ matrix.build }} test + run: scons -j${{ matrix.jobs }} build=${{ matrix.build }} test - name: Generate coverage report if: runner.os == 'Linux' && matrix.python-version == '3.14' diff --git a/.github/workflows/tests-on-pr.yml b/.github/workflows/tests-on-pr.yml index 00e87a43..ed8cea1a 100644 --- a/.github/workflows/tests-on-pr.yml +++ b/.github/workflows/tests-on-pr.yml @@ -4,6 +4,10 @@ on: pull_request: workflow_dispatch: +concurrency: + group: tests-on-pr-${{ github.event.pull_request.number || github.ref }} + cancel-in-progress: true + jobs: tests-on-pr: runs-on: ubuntu-latest @@ -23,7 +27,7 @@ jobs: activate-environment: test channels: conda-forge conda-remove-defaults: "true" - auto-update-conda: true + auto-update-conda: false auto-activate-base: false - name: Conda config @@ -53,7 +57,7 @@ jobs: - name: Build and run C++ tests with coverage run: | "$CONDA_PREFIX/bin/c++" --version - scons build=coverage test + scons -j4 build=coverage test - name: Generate coverage report run: >- From 345f7a9f1beab739e004a3375abaa3a36d2fa041 Mon Sep 17 00:00:00 2001 From: SYMSCAE Date: Wed, 28 Oct 2026 16:58:43 -0700 Subject: [PATCH 44/56] Remove git blame settings --- .git-blame-ignore-revs | 8 -------- 1 file changed, 8 deletions(-) delete mode 100644 .git-blame-ignore-revs diff --git a/.git-blame-ignore-revs b/.git-blame-ignore-revs deleted file mode 100644 index d2c8baf2..00000000 --- a/.git-blame-ignore-revs +++ /dev/null @@ -1,8 +0,0 @@ -# Commits that only reformat code. Use with -# git config blame.ignoreRevsFile .git-blame-ignore-revs -# GitHub's blame view reads this file automatically. - -# style: apply clang-format (Google style) to C++ sources -69c43f493b013e13a8e004e830bda197501beaea -# add precommit hooks and configuration files (upstream clang-format reformat) -ba376c74884c9e57eb6fb86f17f2e482389ac547 From 44489a2ca2e9266c3a99a48f4da3a785330aa42b Mon Sep 17 00:00:00 2001 From: SYMSCAE Date: Wed, 28 Oct 2026 21:24:52 -0700 Subject: [PATCH 45/56] build: standardize release tags for libdiffpy 1.5.0 --- site_scons/fallback_version.py | 2 +- site_scons/libdiffpybuildutils.py | 99 +++++++++++++++++++------------ 2 files changed, 61 insertions(+), 40 deletions(-) diff --git a/site_scons/fallback_version.py b/site_scons/fallback_version.py index d9fca94a..fb729e15 100644 --- a/site_scons/fallback_version.py +++ b/site_scons/fallback_version.py @@ -8,4 +8,4 @@ Update FALLBACK_VERSION when tagging a new release. """ -FALLBACK_VERSION = "1.4.1.post0" +FALLBACK_VERSION = "1.5.0.post0" diff --git a/site_scons/libdiffpybuildutils.py b/site_scons/libdiffpybuildutils.py index 755fc610..853d7709 100644 --- a/site_scons/libdiffpybuildutils.py +++ b/site_scons/libdiffpybuildutils.py @@ -4,6 +4,7 @@ import os import re +import subprocess MYDIR = os.path.dirname(os.path.abspath(__file__)) @@ -18,35 +19,50 @@ Inconsistent FALLBACK_VERSION {} vs {} from git tag. """ +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." - from subprocess import PIPE, Popen - global _cached_gitinfo if _cached_gitinfo is not None: return _cached_gitinfo - nullfile = open(os.devnull, "w") - kw = dict(stdout=PIPE, stderr=nullfile, cwd=MYDIR, universal_newlines=True) - # Release tags may be lightweight, as with v1.4.1. - proc = Popen(["git", "describe", "--tags", "--match=v[[:digit:]]*"], **kw) - desc = proc.stdout.read() - if proc.wait(): + 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 gitinfo() - proc = Popen(["git", "log", "-1", "--format=%H %ci"], **kw) - glog = proc.stdout.read() - words = desc.strip().split("-") - vtag = words[0].lstrip("v") - vnum = int((words[1:2] or ["0"])[0]) + return _cached_gitinfo rv = {} - rv["commit"], rv["date"] = glog.strip().split(None, 1) - rv["patchnumber"] = vnum - rv["version"] = vtag - if vnum: - rv["version"] += ".post" + str(vnum) + 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 gitinfo() + return _cached_gitinfo _cached_gitinfo = None @@ -56,11 +72,17 @@ def getversion(): """Extract version from gitinfo and/or gitarchive.cfg file. Process gitinfo first and make sure it matches FALLBACK_VERSION in - gitarchive.cfg. Use expanded data from gitarchive.cfg when these - sources are from git archive bundle. + fallback_version.py. Use expanded data from gitarchive.cfg when + these sources are from git archive bundle. """ 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." + ) gitarchivecfgfile = os.path.join(MYDIR, "gitarchive.cfg") assert os.path.isfile(gitarchivecfgfile) with open(gitarchivecfgfile) as fp: @@ -69,33 +91,32 @@ def getversion(): ga = dict(gaitems) gi = gitinfo() rv = {} - if gi: + 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" in gi: afb = FALLBACK_VERSION gfb = gi["version"].split(".post")[0] + ".post0" if gfb != afb: raise RuntimeError(EMSG_BAD_FALLBACK_VERSION.format(afb, gfb)) - rv.update(gi) else: - # Not a git repository. Require that gitarchive.cfg is expanded. - if "$Format:" in ga["commit"]: - raise RuntimeError(EMSG_GIT_MISSING) - rv["commit"] = ga["commit"] - rv["date"] = ga["date"] - # First assume we have an undetermined post-release. Keep version - # suffix as ".post0", but set patchnumber to ensure we are post - # FALLBACK_VERSION. + # 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 - # If we are at version tag turn this to regular release. - mx = re.search(r"\btag: v(\d[^,]*)", ga["refnames"]) - if mx: - rv["version"] = mx.group(1) - rv["patchnumber"] = 0 # rv['version'] is resolved, let's parse its subfields. vbase = rv["version"].split(".post")[0] - mx = re.match(r"^(\d+)\.(\d+)(?:\.(\d+))?((?:[ab]|rc)\d*)?", vbase) + 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) or 0) + rv["micro"] = int(mx.group(3)) rv["prerelease"] = mx.group(4) return rv From 6da496199e9dd8d6cf75910baa423fe635cfb222 Mon Sep 17 00:00:00 2001 From: SYMSCAE Date: Wed, 28 Oct 2026 21:40:58 -0700 Subject: [PATCH 46/56] build: revert fallback_version.py --- site_scons/fallback_version.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/site_scons/fallback_version.py b/site_scons/fallback_version.py index fb729e15..d9fca94a 100644 --- a/site_scons/fallback_version.py +++ b/site_scons/fallback_version.py @@ -8,4 +8,4 @@ Update FALLBACK_VERSION when tagging a new release. """ -FALLBACK_VERSION = "1.5.0.post0" +FALLBACK_VERSION = "1.4.1.post0" From c6c3613bfdc58f016bd446f7ec1e43a51d7b93a5 Mon Sep 17 00:00:00 2001 From: Caden Myers Date: Thu, 29 Oct 2026 15:12:26 -0400 Subject: [PATCH 47/56] skpkg: restructure readme to roughly scikit-package standards --- README.rst | 87 ++++++++++++++++-------------------------------------- 1 file changed, 25 insertions(+), 62 deletions(-) diff --git a/README.rst b/README.rst index ae0b1147..13a2858b 100644 --- a/README.rst +++ b/README.rst @@ -10,11 +10,14 @@ |Forge| |PR| -|CI| |Tracking| +|CI| |Codecov| |Tracking| .. |CI| image:: https://github.com/diffpy/libdiffpy/actions/workflows/matrix-and-codecov.yml/badge.svg :target: https://github.com/diffpy/libdiffpy/actions/workflows/matrix-and-codecov.yml +.. |Codecov| image:: https://codecov.io/gh/diffpy/libdiffpy/branch/main/graph/badge.svg + :target: https://codecov.io/gh/diffpy/libdiffpy + .. |Forge| image:: https://img.shields.io/conda/vn/conda-forge/libdiffpy :target: https://anaconda.org/conda-forge/libdiffpy @@ -76,74 +79,30 @@ The following creates and activates a new environment named ``libdiffpy_env`` :: conda create -n libdiffpy_env libdiffpy conda activate libdiffpy_env -When compiling with the conda version of libdiffpy it is essential to -specify the header path, library path and runtime path of the active conda -environment. To verify the installation, compile and run the included -test code `examples/testlib.cpp `_ :: +To verify the installation, compile and run the included test code +`examples/testlib.cpp `_ :: cd examples c++ testlib.cpp -I$CONDA_PREFIX/include -L$CONDA_PREFIX/lib -Wl,-rpath,$CONDA_PREFIX/lib -ldiffpy ./a.out -Note the conda package for libdiffpy is built with C++ compilers -provided by conda-forge. This may cause incompatibility with system C++. +The conda package for libdiffpy is built with C++ compilers provided by +conda-forge, which may be incompatible with the system C++ compiler. In such case please use the conda-forge C++ compiler to link with libdiffpy. -Requirements -^^^^^^^^^^^^ - -To build libdiffpy from sources, a C++ compiler and the following software are required: - -* ``scons`` - software constructions tool (1.0 or later) -* ``libboost-dev`` - Boost C++ libraries development files (1.56 or later) -* ``GSL`` - GNU Scientific Library for C - -Recommended software: - -* ``libobjcryst`` - C++ library for free objects for crystallography, - https://github.com/diffpy/libobjcryst -* ``cxxtest`` - CxxTest Unit Testing Framework, http://cxxtest.com - -The required software is commonly available in the system package manager. -For example, on Ubuntu Linux the required software can be installed using :: - - sudo apt-get install build-essential scons libboost-dev libgsl0-dev - -Alternatively, all of the above can be installed into a conda environment from -the ``requirements`` folder :: +If you prefer to install from sources, obtain the source archive from +`GitHub `_ and ``cd`` into your +``libdiffpy`` directory. Create a conda environment with the build +dependencies listed in the ``requirements`` folder :: conda create -n libdiffpy_env --file requirements/build.txt --file requirements/conda.txt --file requirements/tests.txt conda activate libdiffpy_env -Installation from sources -^^^^^^^^^^^^^^^^^^^^^^^^^ - -Use sources from the git repository or extract the latest source -bundle from https://github.com/diffpy/libdiffpy/releases/latest. :: +Then build and install libdiffpy into the active conda environment :: - tar xzf libdiffpy-VERSION.tar.gz - cd libdiffpy-VERSION + scons -j4 install -To build and install the libdiffpy library use :: - - sudo scons -j4 install - -This installs libdiffpy for all users under the ``/usr/local`` directory. -If administrator (root) access is not available, see the output from -``scons --help`` for options to install to a user-writable location. - -To verify the libdiffpy installation, compile and run the included -test code `examples/testlib.cpp `_ :: - - cd examples - c++ testlib.cpp -ldiffpy - ./a.out - -If compilation fails because of missing header files or missing libdiffpy -library, adjust the ``CPATH`` and ``LIBRARY_PATH`` environment variables or -use the ``-I`` or ``-L`` compiler options. If the shared library libdiffpy -is unavailable at runtime, add a ``-Wl,-rpath,SomePath`` option to the -c++ command or adjust the ``LD_LIBRARY_PATH`` environment variable. +See the output from ``scons --help`` for the available build targets and options. Getting Started --------------- @@ -161,11 +120,14 @@ library with debug information and C-assertions checks :: scons -j4 build=develop +The library integrity can be verified by executing the unit tests :: + + scons -j4 test + 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. The library integrity can be verified by executing unit tests with -``scons test`` (requires the CxxTest framework). +script. See also the `instructions for compiling and modifying source code in development mode `_. @@ -176,10 +138,11 @@ hooks. 2. Initialize pre-commit (one time only) ``pre-commit install``. -Thereafter your code will be linted by black and isort and checked against flake8 before you can commit. -If it fails by black or isort, just rerun and it should pass (black and isort will modify the files so should -pass after they are modified). If the flake8 test fails please see the error messages and fix them manually before -trying to commit again. +Thereafter C++ sources will be formatted by clang-format, Python build scripts will be linted by black and +isort and checked against flake8, and spelling will be checked by codespell before you can commit. +If it fails by clang-format, black or isort, just rerun and it should pass (these hooks modify the files so +should pass after they are modified). If the flake8 or codespell checks fail please see the error messages and +fix them manually before trying to commit again. Improvements and fixes are always appreciated. From f17f096a37d7e9afbd5ca2ad444d689bf50ff7ef Mon Sep 17 00:00:00 2001 From: Caden Myers Date: Thu, 29 Oct 2026 15:19:38 -0400 Subject: [PATCH 48/56] doc: move dev instructions to rst format --- docs/development_instructions.md | 242 ------------------------------- docs/source/development.rst | 98 +++++++++++++ 2 files changed, 98 insertions(+), 242 deletions(-) delete mode 100644 docs/development_instructions.md create mode 100644 docs/source/development.rst diff --git a/docs/development_instructions.md b/docs/development_instructions.md deleted file mode 100644 index c609b246..00000000 --- a/docs/development_instructions.md +++ /dev/null @@ -1,242 +0,0 @@ -# Compile and Modify diffpy Source Code - ---- - -## I. Prerequisites - -### 1) Install System Dependencies - -Open a terminal and run the following commands in order: - -```bash -# Update package list -sudo apt update - -# Install build toolchain, CMake, Git, Python development headers, etc. -sudo apt install -y build-essential cmake git python3-dev - -# Install SCons (used to build libdiffpy) -sudo apt install -y scons - -# Install GSL (GNU Scientific Library) and ObjCryst (X-ray crystallography library) -sudo apt install -y libgsl-dev libobjcryst-dev -``` - ---- - -### 2) Create a Conda Virtual Environment - -> If you haven't installed Miniconda, please download and install it first - -```bash -# Create your environment (e.g., named your_env_name) with Python 3.11 -conda create -n your_env_name python=3.11 -y - -# Activate the environment (shell prompt will show (your_env_name)) -conda activate your_env_name - -# Install Python dependencies -conda install -y numpy - -# Install additional scientific libraries -conda install -c conda-forge boost-cpp gsl pyobjcryst -``` - -All subsequent operations should be performed within the (your_env_name) environment! - ---- - -## II. Get the Source Code - -Assume our working directory is `~/workspace` (adjust to your actual setup): - -```bash -# Switch to the working directory -cd ~/workspace - -# Clone the two core repositories -git clone https://github.com/diffpy/libdiffpy.git diffpy.libdiffpy -git clone https://github.com/diffpy/diffpy.srreal.git diffpy.srreal -``` - -> The directory structure should be: `~/workspace/` -> -> ``` -> ~/workspace/ -> ├── diffpy.libdiffpy/ ← C++ core library -> └── diffpy.srreal/ ← Python interface layer -> ``` - ---- - -## III. Initial Build (Developer Mode) - -> We use `--inplace` so after modifying code you only need to recompile, no `pip install` required. - ---- - -### Step 1: Build libdiffpy (C++ Library) - -```bash -# Enter the libdiffpy directory -cd ~/workspace/diffpy.libdiffpy - -# Clean old builds (optional but recommended) -rm -rf build/ - -# Build the C++ library with SCons -scons -``` - -Success indicators: - -- `build/fast-x86_64/libdiffpy.so` is generated -- No errors in terminal output; warnings can be ignored - -Verify it was generated: - -```bash -ls -l build/fast-x86_64/libdiffpy.so -``` - -> If you see `ObjCryst not found` or `gsl-config not found`, make sure you ran the `apt install` commands in Step 1. - ---- - -### Step 2: Set Environment Variables - -To allow srreal to find the newly built `libdiffpy.so`, set the following variables and replace the paths with your actual paths: - -```bash -# Set the libdiffpy build path -export DIFFPY_LIBDIFFPY_BUILD="$HOME/workspace/diffpy.libdiffpy/build/fast-x86_64" - -# Tell the linker and runtime where to find .so files -export LD_LIBRARY_PATH="$DIFFPY_LIBDIFFPY_BUILD:$LD_LIBRARY_PATH" -export LIBRARY_PATH="$DIFFPY_LIBDIFFPY_BUILD:$LIBRARY_PATH" - -# Let Python import srreal source directly (developer mode) -export PYTHONPATH="$HOME/workspace/diffpy.srreal/src:$PYTHONPATH" -``` - ---- - -### Step 3: Build diffpy.srreal (Python Extension) - -```bash -# Enter the srreal directory -cd ~/workspace/diffpy.srreal - -# Clean old builds -rm -rf build/ - -# Build the Python extension (generate .so in place) -python setup.py build_ext --inplace -``` - -Success indicators: - -- `diffpy/srreal/srreal_ext.cpython-*.so` is generated -- No `cannot find -ldiffpy` errors - ---- - -### Step 4: Verify the Installation - -```bash -# Test whether it can be imported -python -c "from diffpy.srreal.pdfcalculator import PDFCalculator; print('srreal Success!')" -``` - -If you see `srreal Success!`, everything is working! - ---- - -## IV. Daily Development: Modify Code + Rebuild - -### Scenario A: Only Modified diffpy.srreal Python or C++ Code - -```bash -# 1. Activate your Conda environment -conda activate your_env_name - -# 2. Enter the srreal directory -cd ~/workspace/diffpy.srreal - -# 3. Clean and rebuild -rm -rf build/ -python setup.py build_ext --inplace -``` - -No need to rebuild libdiffpy - ---- - -### Scenario B: Modified diffpy.libdiffpy C++ Code - -```bash -# 1. Activate your Conda environment -conda activate your_env_name - -# 2. Rebuild libdiffpy -cd ~/workspace/diffpy.libdiffpy -rm -rf build/ -scons - -# 3. Update environment variables (ensure they point to the latest .so) -export DIFFPY_LIBDIFFPY_BUILD="$HOME/workspace/diffpy.libdiffpy/build/fast-x86_64" -export LD_LIBRARY_PATH="$DIFFPY_LIBDIFFPY_BUILD:$LD_LIBRARY_PATH" - -# 4. Rebuild srreal (since it depends on libdiffpy) -cd ~/workspace/diffpy.srreal -rm -rf build/ -python setup.py build_ext --inplace -``` - -Key point: if you change the underlying C++ library, you must rebuild the Python extension - ---- - -## V. Common Issues and Fixes - -### Issue 1: ModuleNotFoundError: No module named 'numpy' - -→ Fix: run `conda install numpy` in your Conda environment - -### Issue 2: cannot find -ldiffpy - -→ Fix: - -1. Confirm `libdiffpy.so` has been generated -2. Confirm `LD_LIBRARY_PATH` and `LIBRARY_PATH` are set correctly - -### Issue 3: gsl-config: not found - -→ Fix: run `sudo apt install libgsl-dev` - -### Issue 4: Checking for C++ library ObjCryst... no - -→ Fix: run `sudo apt install libobjcryst-dev` - -If you encounter other issues, you can search relevant forums or contact qiaohai@tongji.edu.cn - ---- - -## VI. Appendix: One-Click Environment Variable Setup - -You can append the following to `~/.bashrc` so you don't have to set environment variables every time: - -```bash -# diffpy development environment -export DIFFPY_WORKSPACE="$HOME/workspace" -export DIFFPY_LIBDIFFPY_BUILD="$DIFFPY_WORKSPACE/diffpy.libdiffpy/build/fast-x86_64" -export LD_LIBRARY_PATH="$DIFFPY_LIBDIFFPY_BUILD:$LD_LIBRARY_PATH" -export LIBRARY_PATH="$DIFFPY_LIBDIFFPY_BUILD:$LIBRARY_PATH" -export PYTHONPATH="$DIFFPY_WORKSPACE/diffpy.srreal/src:$PYTHONPATH" -``` - -Then run: - -```bash -source ~/.bashrc -``` diff --git a/docs/source/development.rst b/docs/source/development.rst new file mode 100644 index 00000000..602ff55b --- /dev/null +++ b/docs/source/development.rst @@ -0,0 +1,98 @@ +:tocdepth: -1 + +.. index:: development + +================= +Development guide +================= + +This page describes how to build and test libdiffpy from sources when +modifying the library. See the +`README `_ +for how to fork the repository and set up the pre-commit hooks. + +Set up the build environment +---------------------------- + +From the root of your libdiffpy clone, create a conda environment with the +build and test dependencies listed in the ``requirements`` folder :: + + conda create -n libdiffpy_env --file requirements/build.txt --file requirements/conda.txt --file requirements/tests.txt + conda activate libdiffpy_env + +Build the library +----------------- + +Build the shared library with :: + + scons -j4 + +The ``build`` option selects the compiler settings, for example +``scons -j4 build=develop``. On Linux and macOS the build variants 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 +* ``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')) + +Run the unit tests +------------------ + +The unit tests use the CxxTest framework. Build and run all of them with :: + + scons -j4 test + +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 +separated by commas :: + + scons -j4 test tests=PDFCalculator,Lattice + +To run the tests against the installed library instead of the one in the +``build`` directory, use ``scons test test_installed=True``. + +Measure test coverage +--------------------- + +Build with the ``coverage`` variant, run the tests and summarize the results +with ``gcovr`` :: + + scons -j4 build=coverage test + gcovr --root . --filter 'build/coverage-.*/diffpy/' + +Use ``scons 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. From cc7e5ca2ec54b7837bf26497c7a9940c09879c0c Mon Sep 17 00:00:00 2001 From: Caden Myers Date: Thu, 29 Oct 2026 15:20:00 -0400 Subject: [PATCH 49/56] doc: add 1.4.1 release to the CHANGELOG --- CHANGELOG.rst | 32 ++++++++++++++++++++++++++++++++ 1 file changed, 32 insertions(+) diff --git a/CHANGELOG.rst b/CHANGELOG.rst index 44e268d1..0c8cb096 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -4,6 +4,38 @@ Release notes .. current developments +1.4.1 +===== + +Released 2025-06-25. Notable differences from version 1.4.0. + +**Added:** + +* ``qbroad_seperable`` attribute to ``JeongPeakWidth`` for a Q-dependent + peak broadening term, see https://doi.org/10.1107/S0108767391011327. +* Build scripts for Windows. +* GitHub release with the source tarball SHA256 created automatically + after a tag push. + +**Changed:** + +* Require libobjcryst 2017.2.1 or later. +* Add the conda environment include and library paths to the build when + ``CONDA_PREFIX`` is set. +* Do not create ``OverlapCalculator`` self-neighborhoods at sites excluded + by the pair mask. + +**Removed:** + +* Runtime check for the ObjCryst inversion center bug, which is fixed in + libobjcryst 2017.2.1. + +**Fixed:** + +* ``OverlapCalculator`` now respects its ``rmin`` value. +* Build with ``REAL=double`` to match the floating-point type of libobjcryst. +* Read the installation prefix from the ``PREFIX`` environment variable. + 1.4.0 ===== From 54a61a3240c32abe1cb9e40b78637a8cf46dd632 Mon Sep 17 00:00:00 2001 From: Caden Myers Date: Thu, 29 Oct 2026 15:20:37 -0400 Subject: [PATCH 50/56] doc: update release checklist to be more accurate --- .github/ISSUE_TEMPLATE/release_checklist.md | 2 +- README.rst | 5 +++-- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/.github/ISSUE_TEMPLATE/release_checklist.md b/.github/ISSUE_TEMPLATE/release_checklist.md index 63e624ba..bc397e2b 100644 --- a/.github/ISSUE_TEMPLATE/release_checklist.md +++ b/.github/ISSUE_TEMPLATE/release_checklist.md @@ -16,7 +16,7 @@ assignees: "" - [ ] Grammar and writing quality are checked (no typos). - [ ] `FALLBACK_VERSION` in `site_scons/fallback_version.py` is updated to `.post0`. Without this, the SCons build fails after tagging. - [ ] `scons test` passes locally 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 on the tag and blocks the release if it fails. +- [ ] 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/README.rst b/README.rst index 13a2858b..2237552a 100644 --- a/README.rst +++ b/README.rst @@ -116,7 +116,7 @@ If you see a bug or want to request a feature, please `report it as an issue `_. +See also the `development guide `_ for build variants, running a subset of +the unit tests, coverage analysis and testing changes with diffpy.srreal. To ensure code quality and to prevent accidental commits into the default branch, please set up the use of our pre-commit hooks. From 17700a1836da5340ae6b278dd67e60742c97a852 Mon Sep 17 00:00:00 2001 From: Caden Myers Date: Thu, 29 Oct 2026 15:23:09 -0400 Subject: [PATCH 51/56] doc: rm unused import from docs.txt and other minor doc changes --- docs/source/conf.py | 2 +- docs/source/index.rst | 1 + requirements/docs.txt | 1 - 3 files changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/source/conf.py b/docs/source/conf.py index 784ff33b..2da4f391 100644 --- a/docs/source/conf.py +++ b/docs/source/conf.py @@ -305,7 +305,7 @@ "libdiffpy Documentation", ab_authors, "libdiffpy", - "One line description of project.", + "DiffPy C++ library for calculation of PDF and other real-space quantities", # noqa: E501 "Miscellaneous", ), ] diff --git a/docs/source/index.rst b/docs/source/index.rst index 4164f2d9..fa495292 100644 --- a/docs/source/index.rst +++ b/docs/source/index.rst @@ -54,6 +54,7 @@ Table of contents .. toctree:: :maxdepth: 2 + development release license diff --git a/requirements/docs.txt b/requirements/docs.txt index 1de813f9..9a46bd52 100644 --- a/requirements/docs.txt +++ b/requirements/docs.txt @@ -1,5 +1,4 @@ sphinx sphinx_rtd_theme sphinx-copybutton -doctr m2r2 From 79d75836dea7e688875f5285c577e808f304d55d Mon Sep 17 00:00:00 2001 From: Caden Myers Date: Thu, 29 Oct 2026 15:23:17 -0400 Subject: [PATCH 52/56] news --- news/easy-changes.rst | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) create mode 100644 news/easy-changes.rst diff --git a/news/easy-changes.rst b/news/easy-changes.rst new file mode 100644 index 00000000..3cc923d0 --- /dev/null +++ b/news/easy-changes.rst @@ -0,0 +1,26 @@ +**Added:** + +* Codecov badge in the README. +* Development guide in the documentation for build variants, running a subset of the unit tests, coverage analysis and testing changes with ``diffpy.srreal``. +* Release notes for version 1.4.1 in ``CHANGELOG.rst``. + +**Changed:** + +* Restructure the README to follow the scikit-package template and point to the ``requirements`` folder instead of listing dependencies. +* Describe the clang-format and codespell pre-commit hooks in the README. + +**Deprecated:** + +* + +**Removed:** + +* Outdated ``docs/development_instructions.md``, replaced by the development guide. + +**Fixed:** + +* + +**Security:** + +* From 1116d1591835159ea269ae0bc1bcd8801e40d3b2 Mon Sep 17 00:00:00 2001 From: SYMSCAE Date: Thu, 29 Oct 2026 13:04:42 -0700 Subject: [PATCH 53/56] ci: remove redundant runs on different python versions, unify env init --- .github/actions/setup-tests/action.yml | 33 +++++++++++ ...configure-matrix.py => configure-tests.py} | 4 +- .github/workflows/matrix-and-codecov.yml | 59 +++++-------------- .github/workflows/release-github-on-tag.yml | 7 ++- .github/workflows/tests-on-pr.yml | 34 +---------- 5 files changed, 58 insertions(+), 79 deletions(-) create mode 100644 .github/actions/setup-tests/action.yml rename .github/scripts/{configure-matrix.py => configure-tests.py} (93%) diff --git a/.github/actions/setup-tests/action.yml b/.github/actions/setup-tests/action.yml new file mode 100644 index 00000000..6f36304c --- /dev/null +++ b/.github/actions/setup-tests/action.yml @@ -0,0 +1,33 @@ +name: Set up C++ tests + +runs: + using: composite + steps: + - name: Fetch upstream release tags + 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: | + import runpy + runpy.run_path('.github/scripts/configure-tests.py') + + - name: Create build environment + uses: conda-incubator/setup-miniconda@v3 + with: + activate-environment: test + channels: conda-forge + conda-remove-defaults: "true" + auto-update-conda: false + auto-activate-base: false + python-version: "3.14" + environment-file: build/test-environment.yml + channel-priority: strict diff --git a/.github/scripts/configure-matrix.py b/.github/scripts/configure-tests.py similarity index 93% rename from .github/scripts/configure-matrix.py rename to .github/scripts/configure-tests.py index 7687a055..9b3e3fc9 100644 --- a/.github/scripts/configure-matrix.py +++ b/.github/scripts/configure-tests.py @@ -1,4 +1,4 @@ -"""Configure SCons and CxxTest for the CI matrix.""" +"""Configure SCons, CxxTest and the conda environment for CI tests.""" import json from pathlib import Path @@ -11,7 +11,7 @@ requirements.append(spec) Path("build").mkdir(exist_ok=True) -Path("build/matrix-environment.yml").write_text( +Path("build/test-environment.yml").write_text( json.dumps( { "name": "test", diff --git a/.github/workflows/matrix-and-codecov.yml b/.github/workflows/matrix-and-codecov.yml index 4379fce8..285c9f0f 100644 --- a/.github/workflows/matrix-and-codecov.yml +++ b/.github/workflows/matrix-and-codecov.yml @@ -1,29 +1,29 @@ name: Matrix and Codecov on: - # push: - # branches: - # - main - release: - types: - - prereleased - - published + push: + branches: + - main + + workflow_call: + secrets: + CODECOV_TOKEN: + required: false workflow_dispatch: concurrency: group: matrix-${{ github.ref }} - cancel-in-progress: ${{ github.event_name != 'release' }} + cancel-in-progress: ${{ !startsWith(github.ref, 'refs/tags/') }} jobs: matrix-coverage: - name: C++ tests (${{ matrix.os }}, Python ${{ matrix.python-version }}) + name: C++ tests (${{ matrix.os }}) runs-on: ${{ matrix.os }} timeout-minutes: 45 strategy: fail-fast: false matrix: os: [ubuntu-latest, windows-latest, macos-latest, macos-15-intel] - python-version: ["3.12", "3.13", "3.14"] include: - os: ubuntu-latest shell: bash -l {0} @@ -51,48 +51,21 @@ jobs: with: fetch-depth: 0 - - name: Fetch upstream release tags - run: git fetch --tags https://github.com/diffpy/libdiffpy.git - - # CxxTest is header-only and its generator runs directly from source. - # The conda-forge package is not available on every matrix platform. - - 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: | - import runpy - runpy.run_path('.github/scripts/configure-matrix.py') - - - name: Create build environment - uses: conda-incubator/setup-miniconda@v3 - with: - activate-environment: test - channels: conda-forge - conda-remove-defaults: "true" - auto-update-conda: false - auto-activate-base: false - python-version: ${{ matrix.python-version }} - environment-file: build/matrix-environment.yml - channel-priority: strict + - name: Set up C++ tests + uses: ./.github/actions/setup-tests - name: Build and run C++ tests run: scons -j${{ matrix.jobs }} build=${{ matrix.build }} test - name: Generate coverage report - if: runner.os == 'Linux' && matrix.python-version == '3.14' + if: runner.os == 'Linux' run: >- gcovr --gcov-executable "$CONDA_PREFIX/bin/gcov" --root . --filter 'build/coverage-.*/diffpy/' --xml coverage.xml - name: Save coverage report - if: runner.os == 'Linux' && matrix.python-version == '3.14' + if: runner.os == 'Linux' uses: actions/upload-artifact@v4 with: name: coverage-report @@ -101,9 +74,7 @@ jobs: - name: Upload coverage to Codecov # Fork push builds have no upstream Codecov token; keep their artifact. - if: >- - runner.os == 'Linux' && matrix.python-version == '3.14' && - github.repository == 'diffpy/libdiffpy' + if: runner.os == 'Linux' && github.repository == 'diffpy/libdiffpy' uses: codecov/codecov-action@v5 with: files: coverage.xml diff --git a/.github/workflows/release-github-on-tag.yml b/.github/workflows/release-github-on-tag.yml index b771e7c5..a75982e8 100644 --- a/.github/workflows/release-github-on-tag.yml +++ b/.github/workflows/release-github-on-tag.yml @@ -19,8 +19,13 @@ jobs: secrets: PAT_TOKEN: ${{ secrets.PAT_TOKEN }} + matrix: + uses: ./.github/workflows/matrix-and-codecov.yml + secrets: + CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }} + release-github: - needs: [check-tag-privilege, check-pat-token-validity] + needs: [check-tag-privilege, check-pat-token-validity, matrix] uses: scikit-package/release-scripts/.github/workflows/_release-github.yml@v0 secrets: PAT_TOKEN: ${{ secrets.PAT_TOKEN }} diff --git a/.github/workflows/tests-on-pr.yml b/.github/workflows/tests-on-pr.yml index ed8cea1a..35e3916e 100644 --- a/.github/workflows/tests-on-pr.yml +++ b/.github/workflows/tests-on-pr.yml @@ -21,38 +21,8 @@ jobs: with: fetch-depth: 0 - - name: Initialize miniconda - uses: conda-incubator/setup-miniconda@v3 - with: - activate-environment: test - channels: conda-forge - conda-remove-defaults: "true" - auto-update-conda: false - auto-activate-base: false - - - name: Conda config - run: >- - conda config --set always_yes yes - --set changeps1 no - - - name: Install requirements - run: >- - conda install - --file requirements/build.txt - --file requirements/conda.txt - --file requirements/tests.txt - - - name: Configure SCons for the conda environment - run: | - cat > sconscript.local <<'EOF' - 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')) - EOF + - name: Set up C++ tests + uses: ./.github/actions/setup-tests - name: Build and run C++ tests with coverage run: | From 20b7c79c9561c0edb8f3fc3df4b3290027afb4a1 Mon Sep 17 00:00:00 2001 From: SYMSCAE Date: Fri, 30 Oct 2026 18:05:40 -0700 Subject: [PATCH 54/56] doc: don't install as python package when making docs --- .github/workflows/build-and-publish-docs-on-dispatch.yml | 1 + .github/workflows/release-github-on-tag.yml | 1 + 2 files changed, 2 insertions(+) diff --git a/.github/workflows/build-and-publish-docs-on-dispatch.yml b/.github/workflows/build-and-publish-docs-on-dispatch.yml index 1d6992ea..66cea46a 100644 --- a/.github/workflows/build-and-publish-docs-on-dispatch.yml +++ b/.github/workflows/build-and-publish-docs-on-dispatch.yml @@ -15,4 +15,5 @@ jobs: project: libdiffpy c_extension: true headless: false + install_project: false python_version: ${{ fromJSON(needs.get-python-version.outputs.latest_python_version) }} diff --git a/.github/workflows/release-github-on-tag.yml b/.github/workflows/release-github-on-tag.yml index a75982e8..4cba2c3b 100644 --- a/.github/workflows/release-github-on-tag.yml +++ b/.github/workflows/release-github-on-tag.yml @@ -39,4 +39,5 @@ jobs: project: libdiffpy c_extension: true headless: false + install_project: false python_version: 0 From eb449a0ccaebad12a880b042f09cc79e7a16122f Mon Sep 17 00:00:00 2001 From: SYMSCAE Date: Fri, 30 Oct 2026 21:06:36 -0700 Subject: [PATCH 55/56] doc: comment --- .github/workflows/build-and-publish-docs-on-dispatch.yml | 1 + .github/workflows/release-github-on-tag.yml | 1 + 2 files changed, 2 insertions(+) diff --git a/.github/workflows/build-and-publish-docs-on-dispatch.yml b/.github/workflows/build-and-publish-docs-on-dispatch.yml index 66cea46a..b37a4391 100644 --- a/.github/workflows/build-and-publish-docs-on-dispatch.yml +++ b/.github/workflows/build-and-publish-docs-on-dispatch.yml @@ -15,5 +15,6 @@ jobs: project: libdiffpy c_extension: true headless: false + # libdiffpy is a C++ library and does not need a Python package installed for docs. install_project: false python_version: ${{ fromJSON(needs.get-python-version.outputs.latest_python_version) }} diff --git a/.github/workflows/release-github-on-tag.yml b/.github/workflows/release-github-on-tag.yml index 4cba2c3b..308e192e 100644 --- a/.github/workflows/release-github-on-tag.yml +++ b/.github/workflows/release-github-on-tag.yml @@ -39,5 +39,6 @@ jobs: project: libdiffpy c_extension: true headless: false + # libdiffpy is a C++ library and does not need a Python package installed for docs. install_project: false python_version: 0 From 33d9f7c4f62129cd18dc94bacb851795b0995387 Mon Sep 17 00:00:00 2001 From: SYMSCAE Date: Fri, 30 Oct 2026 21:12:01 -0700 Subject: [PATCH 56/56] doc: more descriptions --- .github/workflows/build-and-publish-docs-on-dispatch.yml | 2 ++ .github/workflows/release-github-on-tag.yml | 2 ++ 2 files changed, 4 insertions(+) diff --git a/.github/workflows/build-and-publish-docs-on-dispatch.yml b/.github/workflows/build-and-publish-docs-on-dispatch.yml index b37a4391..18add042 100644 --- a/.github/workflows/build-and-publish-docs-on-dispatch.yml +++ b/.github/workflows/build-and-publish-docs-on-dispatch.yml @@ -15,6 +15,8 @@ jobs: project: libdiffpy c_extension: true headless: false + # true: install the project with `python -m pip install -e . --no-deps`. + # false: skip project installation; still install dependencies and build docs. # libdiffpy is a C++ library and does not need a Python package installed for docs. install_project: false python_version: ${{ fromJSON(needs.get-python-version.outputs.latest_python_version) }} diff --git a/.github/workflows/release-github-on-tag.yml b/.github/workflows/release-github-on-tag.yml index 308e192e..604f78fb 100644 --- a/.github/workflows/release-github-on-tag.yml +++ b/.github/workflows/release-github-on-tag.yml @@ -39,6 +39,8 @@ jobs: project: libdiffpy c_extension: true headless: false + # true: install the project with `python -m pip install -e . --no-deps`. + # false: skip project installation; still install dependencies and build docs. # libdiffpy is a C++ library and does not need a Python package installed for docs. install_project: false python_version: 0