diff --git a/news/pdf3d-calculator.rst b/news/pdf3d-calculator.rst new file mode 100644 index 0000000..932c74e --- /dev/null +++ b/news/pdf3d-calculator.rst @@ -0,0 +1,23 @@ +**Added:** + +* Expose ``PDF3DCalculator`` through the Python bindings. + +**Changed:** + +* + +**Deprecated:** + +* + +**Removed:** + +* + +**Fixed:** + +* + +**Security:** + +* diff --git a/src/diffpy/srreal/pdfcalculator.py b/src/diffpy/srreal/pdfcalculator.py index 0ed0835..55fdc0b 100644 --- a/src/diffpy/srreal/pdfcalculator.py +++ b/src/diffpy/srreal/pdfcalculator.py @@ -16,10 +16,12 @@ Top-level classes for PDF calculation: DebyePDFCalculator -- simulate PDF by evaluating Debye sum in Q-space PDFCalculator -- calculate PDF by peak summation in real space + PDF3DCalculator -- calculate 3D real-space PDF on a cubic grid """ from diffpy.srreal.srreal_ext import ( DebyePDFCalculator, + PDF3DCalculator, PDFCalculator, fftftog, fftgtof, @@ -31,7 +33,7 @@ # exported items __all__ = """ - DebyePDFCalculator PDFCalculator + DebyePDFCalculator PDFCalculator PDF3DCalculator fftftog fftgtof """.split() @@ -341,4 +343,100 @@ def _init_kwargs1(self, **kwargs): # End of class PDFCalculator + +# PDF3DCalculator ------------------------------------------------------------ + +# shared interface of the PDF calculator classes + +_defineCommonInterface(PDF3DCalculator) + +# Property wrappers to double attributes of the C++ PDF3DCalculator + +PDF3DCalculator.calculation_mode3d = propertyFromExtDoubleAttr( + "calculation_mode3d", + ( + "3D calculation mode: 0 = full ADP-broadened PDF3D," + " 1 = raw pair-vector histogram. [0]" + ), +) +PDF3DCalculator.histogram_weight_mode3d = propertyFromExtDoubleAttr( + "histogram_weight_mode3d", + ( + "Histogram weight mode when calculation_mode3d=1:" + " 0 = scattering weighted, 1 = raw pair count. [0]" + ), +) +PDF3DCalculator.enable_nn_delta3d = propertyFromExtDoubleAttr( + "enable_nn_delta3d", + "Enable 3D covariance delta correction. Use 1.0 for on and 0.0 for off. [0]", +) +PDF3DCalculator.nn_delta3d = propertyFromExtDoubleAttr( + "nn_delta3d", + "Legacy shell-constant 3D delta subtracted along the pair direction. [0 A**2]", +) +PDF3DCalculator.nn_delta_positive_eta3d = propertyFromExtDoubleAttr( + "nn_delta_positive_eta3d", + "Safety factor for the positive-definite covariance bound. [0.9]", +) +PDF3DCalculator.delta1_3d = propertyFromExtDoubleAttr( + "delta1_3d", + "Coefficient for the 1/r part of distance-decay 3D covariance delta. [0 A]", +) +PDF3DCalculator.delta2_3d = propertyFromExtDoubleAttr( + "delta2_3d", + ( + "Coefficient for the 1/r**2 part of distance-decay 3D covariance" + " delta. [0 A**2]" + ), +) +PDF3DCalculator.delta_shell_index3d = propertyFromExtDoubleAttr( + "delta_shell_index3d", + "Shell index for the legacy shell-constant 3D delta mode. [1]", +) +PDF3DCalculator.delta_shell_tolerance3d = propertyFromExtDoubleAttr( + "delta_shell_tolerance3d", + "Distance tolerance used to group neighbor shells for legacy 3D delta. [0.05 A]", +) +PDF3DCalculator.delta_key_tolerance3d = propertyFromExtDoubleAttr( + "delta_key_tolerance3d", + "Coordinate rounding tolerance for matching legacy 3D delta shell pairs. [1e-6 A]", +) +PDF3DCalculator.use_adp_scale_sensitivity3d = propertyFromExtDoubleAttr( + "use_adp_scale_sensitivity3d", + ( + "Enable ADP scale sensitivity for 3D delta." + " Use 1.0 for on and 0.0 for off. [0]" + ), +) +PDF3DCalculator.adp_scale3d = propertyFromExtDoubleAttr( + "adp_scale3d", + "Scale factor applied to pair covariance in 3D delta calculations. [1]", +) +PDF3DCalculator.rho0_background_scale3d = propertyFromExtDoubleAttr( + "rho0_background_scale3d", + "Scale factor for the rho0 background subtraction in 3D PDF output. [1]", +) + +# method overrides to support optional keyword arguments + + +def _init_kwargs2(self, **kwargs): + """Create a new instance of PDF3DCalculator. + Keyword arguments can be used to configure the calculator properties, + for example: + + p3c = PDF3DCalculator(rmax=10.0, gridstep=0.1, qmax=12) + + Raise ValueError for invalid keyword argument. + """ + PDF3DCalculator.__boostpython__init(self) + setattrFromKeywordArguments(self, **kwargs) + return + + +PDF3DCalculator.__boostpython__init = PDF3DCalculator.__init__ +PDF3DCalculator.__init__ = _init_kwargs2 + +# End of class PDF3DCalculator + # End of file diff --git a/src/extensions/wrap_PDFCalculators.cpp b/src/extensions/wrap_PDFCalculators.cpp index df4cdd2..f44cda8 100644 --- a/src/extensions/wrap_PDFCalculators.cpp +++ b/src/extensions/wrap_PDFCalculators.cpp @@ -12,7 +12,7 @@ * ****************************************************************************** * -* Bindings to DebyePDFCalculator and PDFCalculator classes. +* Bindings to DebyePDFCalculator, PDFCalculator and PDF3DCalculator classes. * *****************************************************************************/ @@ -22,6 +22,7 @@ #include #include +#include #include "srreal_converters.hpp" #include "srreal_pickling.hpp" @@ -138,6 +139,52 @@ object or a string type of a registered PDFBaseline class.\n\ Use PDFBaseline.getRegisteredTypes() for the set of allowed values.\n\ "; +const char* doc_PDF3DCalculator = "\ +Calculate 3D real-space pair distribution function on a cubic grid.\n\ +"; + +const char* doc_PDF3DCalculator_pdf3d = "\ +3D PDF data as a flattened NumPy array of non-zero bins: [x, y, z, G, ...].\n\ +Reshape in Python with arr.reshape(-1, 4).\n\ +"; + +const char* doc_PDF3DCalculator_radialhistogram3d = "\ +1D radial histogram from the 3D pair-vector accumulation as [r, weight, ...].\n\ +Reshape in Python with arr.reshape(-1, 2).\n\ +"; + +const char* doc_PDF3DCalculator_gridstep = "\ +Spacing of the 3D grid in Angstrom. Must be positive.\n\ +"; + +const char* doc_PDF3DCalculator_accumblocksize = "\ +Block size used by tiled accumulation loops in 3D real-space summation.\n\ +"; + +const char* doc_PDF3DCalculator_qmin = "\ +Lower bound of the Q-range for 3D calculation in 1/A.\n\ +"; + +const char* doc_PDF3DCalculator_qmax = "\ +Upper bound of the Q-range for 3D calculation in 1/A.\n\ +"; + +const char* doc_PDF3DCalculator_usecqwindow3d = "\ +If True, apply qmin/qmax 3D q-window in C++. If False, export unwindowed G(r) for external FFT post-processing.\n\ +"; + +const char* doc_PDF3DCalculator_exportGrid3DBinary = "\ +Export dense 3D grid (nz, ny, nx) directly to a binary file.\n\ +path -- output file path\n\ +usefloat32 -- True for float32 output, False for float64\n\ +applypost -- apply q-window/rdf scale/rho0/qdamp before export\n\ +"; + +const char* doc_PDF3DCalculator_setDeltaPairTypes3D = "\ +Set atom-type filter for the 3D covariance delta correction.\n\ +Use \"*\" for either argument to match all atom types.\n\ +"; + const char* doc_fftftog = "\ Perform sine-fast Fourier transform from F(Q) to G(r).\n\ The length of the output array is padded to the next power of 2.\n\ @@ -173,6 +220,9 @@ DECLARE_PYARRAY_METHOD_WRAPPER(getF, getF_asarray) DECLARE_PYARRAY_METHOD_WRAPPER(getQgrid, getQgrid_asarray) DECLARE_PYLIST_METHOD_WRAPPER(usedEnvelopeTypes, usedEnvelopeTypes_aslist) +DECLARE_PYARRAY_METHOD_WRAPPER(get3DPDF, get3DPDF_asarray) +DECLARE_PYARRAY_METHOD_WRAPPER(getRadialHistogram3D, getRadialHistogram3D_asarray) + // wrappers for the peakprofile property PeakProfilePtr getpeakprofile(PDFCalculator& obj) @@ -180,6 +230,16 @@ PeakProfilePtr getpeakprofile(PDFCalculator& obj) return obj.getPeakProfile(); } +static double getqmin_value(PDF3DCalculator& obj) +{ + return obj.getQmin(); +} + +static double getqmax_value(PDF3DCalculator& obj) +{ + return obj.getQmax(); +} + DECLARE_BYTYPE_SETTER_WRAPPER(setPeakProfile, setpeakprofile) // wrappers for the baseline property @@ -436,6 +496,38 @@ class PDFCalculatorPickleSuite : } }; + +class PDF3DCalculatorPickleSuite : + public PairQuantityPickleSuite +{ + private: + + typedef PairQuantityPickleSuite Super; + + public: + + static tuple getstate(object obj) + { + tuple rv( + getstate_super(obj) + + getstate_common(obj) + ); + return rv; + } + + + static void setstate(object obj, tuple state) + { + ensure_tuple_length(state, 4); + // restore the state using boost serialization + tuple st0 = extract(state[0]); + Super::setstate(obj, st0); + // other items are non-None only when restoring Python class + stl_input_iterator st(state); + setstate_common(obj, st); + } +}; + } // namespace nswrap_PDFCalculators // Wrapper definition -------------------------------------------------------- @@ -475,6 +567,48 @@ void wrap_PDFCalculators() .def_pickle(PDFCalculatorPickleSuite()) ; + // PDF3DCalculator + class_ > + pdf3d_class("PDF3DCalculator", doc_PDF3DCalculator); + + pdf3d_class + .def(init<>()) + .add_property("pdf3d", get3DPDF_asarray, + doc_PDF3DCalculator_pdf3d) + .add_property("radialhistogram3d", + getRadialHistogram3D_asarray, + doc_PDF3DCalculator_radialhistogram3d) + .add_property("gridstep", + &PDF3DCalculator::getGridStep, + &PDF3DCalculator::setGridStep, + doc_PDF3DCalculator_gridstep) + .add_property("accumblocksize", + &PDF3DCalculator::getAccumBlockSize, + &PDF3DCalculator::setAccumBlockSize, + doc_PDF3DCalculator_accumblocksize) + .add_property("qmin", + &getqmin_value, + &PDF3DCalculator::setQmin, + doc_PDF3DCalculator_qmin) + .add_property("qmax", + &getqmax_value, + &PDF3DCalculator::setQmax, + doc_PDF3DCalculator_qmax) + .add_property("usecqwindow3d", + &PDF3DCalculator::getUseCQWindow3D, + &PDF3DCalculator::setUseCQWindow3D, + doc_PDF3DCalculator_usecqwindow3d) + .def("exportGrid3DBinary", &PDF3DCalculator::exportGrid3DBinary, + (bp::arg("path"), bp::arg("usefloat32")=true, + bp::arg("applypost")=true), + doc_PDF3DCalculator_exportGrid3DBinary) + .def("setDeltaPairTypes3D", &PDF3DCalculator::setDeltaPairTypes3D, + (bp::arg("atomtype0"), bp::arg("atomtype1")), + doc_PDF3DCalculator_setDeltaPairTypes3D) + .def_pickle(PDF3DCalculatorPickleSuite()) + ; + // FFT functions def("fftftog", fftftog_array_step, (bp::arg("f"), bp::arg("qstep"), bp::arg("qmin")=0.0),