Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions .codespell/ignore_words.txt
Original file line number Diff line number Diff line change
Expand Up @@ -15,3 +15,6 @@ regist
;; src/pyobjcryst/crystal.py:548
;; alabelstyle parameter
inFront

;; tests/test_reflectionprofile.py: unittest assertions flagged as typos
assertin
9 changes: 9 additions & 0 deletions docs/source/api/pyobjcryst.rst
Original file line number Diff line number Diff line change
Expand Up @@ -117,6 +117,7 @@ pyobjcryst.powderpattern module
:members:
:undoc-members:
:show-inheritance:
:exclude-members: ReflectionProfileType

pyobjcryst.pyobjcryst_app module
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
Expand All @@ -142,6 +143,14 @@ pyobjcryst.refineableobj module
:undoc-members:
:show-inheritance:

pyobjcryst.reflectionprofile module
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

.. automodule:: pyobjcryst.reflectionprofile
:members:
:undoc-members:
:show-inheritance:

pyobjcryst.scatterer module
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

Expand Down
25 changes: 25 additions & 0 deletions news/79.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
**Added:**

* Exposed `ReflectionProfile` methods (`GetProfile`, `GetFullProfileWidth`, `XMLOutput`, `XMLInput`) via Python bindings. Added unit tests.
* The binding to `ReflectionProfile.GetProfile(x, xcenter, h, k, l)` accepts python sequences / `numpy` arrays for the `x` argument, thanks to the helper function `assignCrystVector`.

**Changed:**

* None.

**Deprecated:**

* None.

**Removed:**

* None.

**Fixed:**

* Building with `pip install .` now uses `sysconfig` to locate `ObjCryst++`` libraries outside conda environments.
* Missing definition of `ScatteringData` in `powderpatterndiffraction_ext.ccp`

**Security:**

* None.
32 changes: 18 additions & 14 deletions setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
import glob
import os
import sys
import sysconfig
from ctypes.util import find_library
from pathlib import Path

Expand All @@ -21,7 +22,7 @@

def get_boost_libraries():
# the names we'll search for
major, minor = sys.version_info[:2]
major, minor = sys.version_info.major, sys.version_info.minor
candidates = [
f"boost_python{major}{minor}",
f"boost_python{major}",
Expand Down Expand Up @@ -50,19 +51,22 @@ def get_boost_libraries():

def get_env_config():
conda_prefix = os.environ.get("CONDA_PREFIX")
if not conda_prefix:
raise EnvironmentError(
"CONDA_PREFIX environment variable is not set. "
"Please activate your conda environment before running setup.py."
)
if os.name == "nt":
inc = Path(conda_prefix) / "Library" / "include"
lib = Path(conda_prefix) / "Library" / "lib"
else:
inc = Path(conda_prefix) / "include"
lib = Path(conda_prefix) / "lib"

return {"include_dirs": [str(inc)], "library_dirs": [str(lib)]}
if conda_prefix:
if os.name == "nt":
inc = Path(conda_prefix) / "Library" / "include"
lib = Path(conda_prefix) / "Library" / "lib"
else:
inc = Path(conda_prefix) / "include"
lib = Path(conda_prefix) / "lib"
return {"include_dirs": [str(inc)], "library_dirs": [str(lib)]}

# no conda env: fallback to system/venv Python include/lib dirs
py_inc = sysconfig.get_paths().get("include")
libdir = sysconfig.get_config_var("LIBDIR") or "/usr/lib"
return {
"include_dirs": [p for p in [py_inc] if p],
"library_dirs": [libdir],
}


def create_extensions():
Expand Down
1 change: 1 addition & 0 deletions src/extensions/powderpatterndiffraction_ext.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@

#include <ObjCryst/ObjCryst/General.h>
#include <ObjCryst/ObjCryst/PowderPattern.h>
#include <ObjCryst/ObjCryst/ScatteringData.h>

namespace bp = boost::python;
using namespace boost::python;
Expand Down
110 changes: 73 additions & 37 deletions src/extensions/reflectionprofile_ext.cpp
Original file line number Diff line number Diff line change
@@ -1,27 +1,29 @@
/*****************************************************************************
*
* pyobjcryst
*
* File coded by: Vincent Favre-Nicolin
*
* See AUTHORS.txt for a list of people who contributed.
* See LICENSE.txt for license information.
*
******************************************************************************
*
* boost::python bindings to ObjCryst::ReflectionProfile.
*
* Changes from ObjCryst::ReflectionProfile
*
* Other Changes
*
*****************************************************************************/
*
* pyobjcryst
*
* File coded by: Vincent Favre-Nicolin
*
* See AUTHORS.txt for a list of people who contributed.
* See LICENSE.txt for license information.
*
******************************************************************************
*
* boost::python bindings to ObjCryst::ReflectionProfile.
*
* Changes from ObjCryst::ReflectionProfile
*
* Other Changes
*
*****************************************************************************/

#include <boost/python/class.hpp>
#include <boost/python/manage_new_object.hpp>
#include <boost/python/pure_virtual.hpp>
#undef B0

#include "helpers.hpp" // assignCrystVector helper for numpy/sequence inputs

#include <iostream>

#include <ObjCryst/ObjCryst/ReflectionProfile.h>
Expand All @@ -30,59 +32,93 @@ namespace bp = boost::python;
using namespace boost::python;
using namespace ObjCryst;

namespace {

class ReflectionProfileWrap :
public ReflectionProfile, public wrapper<ReflectionProfile>
namespace
{
public:

class ReflectionProfileWrap : public ReflectionProfile, public wrapper<ReflectionProfile>
{
public:
// Pure virtual functions

ReflectionProfile* CreateCopy() const
ReflectionProfile *CreateCopy() const
{
return this->get_override("CreateCopy")();
}

CrystVector_REAL GetProfile(
const CrystVector_REAL& x, const REAL xcenter,
const REAL h, const REAL k, const REAL l) const
const CrystVector_REAL &x, const REAL xcenter,
const REAL h, const REAL k, const REAL l) const
{
bp::override f = this->get_override("GetProfile");
return f(x, xcenter, h, k, l);
}

REAL GetFullProfileWidth(
const REAL relativeIntensity, const REAL xcenter,
const REAL h, const REAL k, const REAL l)
const REAL relativeIntensity, const REAL xcenter,
const REAL h, const REAL k, const REAL l)
{
bp::override f = this->get_override("GetFullProfileWidth");
return f(relativeIntensity, xcenter, h, k, l);
}

void XMLOutput(ostream& os, int indent) const
void XMLOutput(ostream &os, int indent) const
{
bp::override f = this->get_override("XMLOutput");
f(os, indent);
}

void XMLInput(istream& is, const XMLCrystTag& tag)
void XMLInput(istream &is, const XMLCrystTag &tag)
{
bp::override f = this->get_override("GetProfile");
bp::override f = this->get_override("XMLInput");
f(is, tag);
}
};
};

} // namespace
// Accept python sequences/ndarrays for x and forward to the C++ API.
CrystVector_REAL _GetProfile(
const ReflectionProfile &rp, bp::object x, const REAL xcenter,
const REAL h, const REAL k, const REAL l)
{
CrystVector_REAL cvx;
assignCrystVector(cvx, x);
return rp.GetProfile(cvx, xcenter, h, k, l);
}

} // namespace

void wrap_reflectionprofile()
{
class_<ReflectionProfileWrap, bases<RefinableObj>, boost::noncopyable>(
"ReflectionProfile")
// TODO add pure_virtual bindings to the remaining public methods
"ReflectionProfile")
.def("CreateCopy",
pure_virtual(&ReflectionProfile::CreateCopy),
return_value_policy<manage_new_object>())
;
pure_virtual(&ReflectionProfile::CreateCopy),
(return_value_policy<manage_new_object>()),
"Return a new ReflectionProfile instance copied from this one.")
// Two overloads for GetProfile:
// - Native CrystVector signature (for C++ callers / already-converted vectors).
// - Python-friendly wrapper that accepts sequences/ndarrays and converts them.
.def(
"GetProfile",
pure_virtual((CrystVector_REAL (ReflectionProfile::*)(const CrystVector_REAL &, REAL, REAL, REAL, REAL) const) & ReflectionProfile::GetProfile),
(bp::arg("x"), bp::arg("xcenter"), bp::arg("h"),
bp::arg("k"), bp::arg("l")),
"Compute the profile values at positions `x` for reflection (h, k, l) centered at `xcenter`.")
.def(
"GetProfile", &_GetProfile,
(bp::arg("x"), bp::arg("xcenter"), bp::arg("h"), bp::arg("k"),
bp::arg("l")),
"Compute the profile values at positions `x` (sequence/ndarray accepted) for reflection (h, k, l) centered at `xcenter`.")
.def("GetFullProfileWidth",
pure_virtual((REAL (ReflectionProfile::*)(const REAL, const REAL, const REAL, const REAL, const REAL) const) & ReflectionProfile::GetFullProfileWidth),
(bp::arg("relativeIntensity"), bp::arg("xcenter"),
bp::arg("h"), bp::arg("k"), bp::arg("l")),
"Return the full profile width at a given relative intensity for reflection (h, k, l) around `xcenter`.")
.def("XMLOutput",
pure_virtual((void (ReflectionProfile::*)(ostream &, int) const) & ReflectionProfile::XMLOutput),
(bp::arg("os"), bp::arg("indent")),
"Write this ReflectionProfile as XML to a file-like object. `indent` controls indentation depth.")
.def("XMLInput",
pure_virtual((void (ReflectionProfile::*)(istream &, const XMLCrystTag &))&ReflectionProfile::XMLInput),
(bp::arg("is"), bp::arg("tag")),
"Load ReflectionProfile parameters from an XML stream and tag.");
}
37 changes: 34 additions & 3 deletions src/pyobjcryst/reflectionprofile.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,12 +9,43 @@
# See LICENSE.txt for license information.
#
##############################################################################
"""Python wrapping of PowderPattern.h.
"""Python wrapping of ReflectionProfile.h.

See the online ObjCryst++ documentation (https://objcryst.readthedocs.io).

Changes from ObjCryst::ReflectionProfile::
In development !
A ``ReflectionProfile`` describes the shape of a single Bragg
reflection (pseudo-Voigt by default) and is owned by a
``PowderPatternDiffraction``. The Python bindings expose the
public methods ``GetProfile``, ``GetFullProfileWidth``,
``XMLOutput`` / ``XMLInput`` and ``CreateCopy``. ``GetProfile``
accepts a Python sequence or numpy array for ``x``.

Example
-------

Sample the profile of a single ``(h, k, l)`` reflection from an
existing ``PowderPatternDiffraction`` and broaden it by tweaking
the ``W`` Caglioti parameter::

import numpy as np
from pyobjcryst.powderpattern import PowderPattern

pp = PowderPattern()
pp.SetWavelength(0.7)
x = np.deg2rad(np.linspace(0, 40, 1000))
pp.SetPowderPatternX(x)
pp.SetPowderPatternObs(np.ones_like(x))
ppd = pp.AddPowderPatternDiffraction(crystal) # crystal: pyobjcryst.Crystal

profile = ppd.GetProfile()
window = x[100:200]
xcenter = float(window[len(window) // 2])

y = profile.GetProfile(window, xcenter, 1, 0, 0)
fwhm = profile.GetFullProfileWidth(0.5, xcenter, 1, 0, 0)

profile.GetPar("W").SetValue(0.05)
y_broader = profile.GetProfile(window, xcenter, 1, 0, 0)
"""

__all__ = ["ReflectionProfile", "ReflectionProfileType"]
Expand Down
Loading
Loading