Code Monkey home page Code Monkey logo

hklpy's Introduction

hklpy

Name Downloads Version Platforms PyPI
Conda Package Conda Downloads Conda Version Conda Platforms PyPi

Controls for using diffractometers within the Bluesky Framework.

Based on the hkl C library (described here as libhkl), with slightly cleaner abstractions when compared to the auto-generated gobject-introspection classes. Integrates with ophyd pseudopositioners.

References

Conda Recipes

Install the most recent build: conda install hklpy -c conda-forge

The recipes for libhkl (hkl) and hklpy are available in the following conda-forge feedstocks:

hklpy's People

Contributors

danielballan avatar dchabot avatar dependabot[bot] avatar ericdill avatar klauer avatar licode avatar mrakitin avatar prjemian avatar stuwilkins avatar tacaswell avatar

Stargazers

 avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar

hklpy's Issues

Cannot set calc constraints

When trying to set calc constraints , received TypeError: argument of type 'NoneType' is not iterable exception.

In this example, fourc is an instance of E4CV with specific EPICS motors. The ipython session:

In [1]: fourc.calc["tth"]                                                                                                                   
---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
<ipython-input-1-b53b540f6eef> in <module>
----> 1 fourc.calc["tth"]

~/Apps/anaconda/envs/bluesky/lib/python3.7/site-packages/hkl/calc.py in __getitem__(self, axis)
    408         return CalcParameter(self._get_axis_by_name(axis),
    409                              units=self._unit_name, name=axis, inverted=axis in
--> 410                              self._inverted_axes, geometry=self._geometry)
    411 
    412     def __setitem__(self, axis, value):

TypeError: argument of type 'NoneType' is not iterable

In [2]: fourc.calc._inverted_axes is None                                                                                                   
Out[2]: True

simplify access to (and representation of) an orientation reflection's values

The API to access all the values of a single reflection used to orient a sample now is only through libhkl (only the reciprocal space values are available, via obj.calc.sample.reflections). For example:

In [73]: fourc.calc.sample._sample.reflections_get()
Out[73]: 
[<Hkl.SampleReflection object at 0x7f52e68cb040 (HklSampleReflection at 0x56289c027bc0)>,
 <Hkl.SampleReflection object at 0x7f52e68cb4c0 (HklSampleReflection at 0x56289bf04510)>]
In [87]: fourc.calc.sample._sample.reflections_get()[0].geometry_get().axis_names_get()
Out[87]: ['omega', 'chi', 'phi', 'tth']

# get axis positions in radians
In [88]: fourc.calc.sample._sample.reflections_get()[0].geometry_get().axis_values_get(0)
Out[88]: [0.6471680866394973, 1.5550883635269475, 1.377064779823526, 1.3308135546456763]

# get axis positions in degrees
In [89]: fourc.calc.sample._sample.reflections_get()[0].geometry_get().axis_values_get(1)
Out[89]: [37.08, 89.1, 78.9, 76.25]

In [90]: fourc.calc.sample._sample.reflections_get()[0].geometry_get().wavelength_get(0)
Out[90]: 7.1010423825887745

In [91]: fourc.calc.sample._sample.reflections_get()[0].geometry_get().wavelength_get(1)
Out[91]: 7.1010423825887745

In [92]: fourc.calc.sample._sample.reflections_get()[0].hkl_get()
Out[92]: (h=0.0, k=0.0, l=1.0)

This should all be much easier for the user to access.

Also, the default string representation of a reflection should be informative for the user, not this:

In [94]: fourc.calc.sample._sample.reflections_get()[0]
Out[94]: <Hkl.SampleReflection object at 0x7f52f0565580 (HklSampleReflection at 0x56289c027bc0)>

More like a Reflection() object (does not exist now in hklpy), such as:

Reflection(
    reflection=(h=0, k=0, l=1),
    wavelength=7.101042382588774,
    axes={'omega': 37.08, 'chi': 89.1, 'phi': 78.9, 'tth': 76.25}
)

where reflection is a namedtuple, as in:

namedtuple('ReflectionTuple', 'h k l')

Resolve th angle mismatch for (113): mode? constraints?

@ambarb : Found the FIXME item in tst_e4cv_fourc.ipynb:

(bluesky_2021_1) prjemian@poof ~/.../Bluesky/hklpy $ git grep FIXME | grep examples/
examples/archive/e6c_sixc.ipynb:    "# FIXME:  must get same numbers, probably in different rows\n",
examples/archive/hkl-example.ipynb:    "# FIXME: So far, only works when previous constraints are removed.  Wrong mode?\n",
examples/archive/hkl-example.ipynb:    "# FIXME: pick mode\n",
examples/archive/tardis_example.ipynb:    "# FIXME: hack to get around what should have been done at init of tardis_calc instance\n",
examples/archive/tardis_example.ipynb:    "    # energy=tardis_calc.energy, # FIXME: unexpected keyword argument\n",
examples/tst_e4cv_fourc.ipynb:    "# FIXME: (113) `th` values do not match."

Originally posted by @prjemian in #52 (comment)

REQUIREMENTS - collecting scientific measurement requirements

In coordination with @prjemian, we are collecting community requirements for HKL-based data collection in bluesky. This issue is the start of what we hope to be a community wide discussion. Discussion points that become requirements will be documented here, which is initially co-managed by @prjemian and @ambarb. For now, our requirements document just suggests topics that we should discuss.

We invite all to participate (either as individuals or a collective representation of a beamline or facility). This can include links to more detailed documents (as attachments or appropriate links).

ClobberError conflict with typhos

When installing hklpy and typhos as part of a bluesky environment for the APS, two ClobberError reports were generated (BCDA-APS/use_bluesky#63) during the environment creation process (on linux x86_64, linux mint).

ClobberError: This transaction has incompatible packages due to a shared path.
  packages: nsls2forge/noarch::hklpy-0.3.14-py_0, conda-forge/noarch::typhos-1.0.2-py_0
  path: 'lib/python3.8/site-packages/tests/__init__.py'


ClobberError: This transaction has incompatible packages due to a shared path.
  packages: nsls2forge/noarch::hklpy-0.3.14-py_0, conda-forge/noarch::typhos-1.0.2-py_0
  path: 'lib/python3.8/site-packages/tests/__pycache__/__init__.cpython-38.pyc'

Make new examples for the documentation

As identified in #24, the items in the examples directory are out of date. Time to create new examples. The notebook format is great for working up a notebook but not so good for integration with the other documentation.

I plan to create new notebooks for documentation, archiving the previous documentation for now. Each notebook will be converted into reStructured text using nbconvert and deposited into the docs/source/example directory in the repository. The conversion task can be directed using a Makefile. Minor hand editing may be needed in these .rst files regarding figures.

Examples needed (all will use simulated motors unless stated):

  • E4CV
  • E4CV - renamed axes
  • E4CV - EPICS motors
  • E6C
  • K4CV
  • Check hklpy UB matrix operations with E6C
  • Compare computations with SPEC

uncaught errors when adding pseudo-positioner

When testing the addition of positioners to a Diffractometer object, it was discovered that there are uncaught errors when adding a pseudo-positioner.

============================= test session starts ==============================
platform linux -- Python 3.7.7, pytest-6.0.2, py-1.9.0, pluggy-0.13.1
rootdir: /tmp/pytest-of-prjemian/pytest-13/test_extra_pseudo_ok0
collected 0 items / 1 error

==================================== ERRORS ====================================
___________________ ERROR collecting test_extra_pseudo_ok.py ___________________
/home/prjemian/Apps/anaconda/envs/hkl/lib/python3.7/site-packages/ophyd/device.py:223: in __get__
    return instance._signals[self.attr]
E   KeyError: 'extra'

During handling of the above exception, another exception occurred:
...

ignore warnings due to "import pint"

import pint

adds this warning to our test suite now:

================================================================================ warnings summary =================================================================================
/home/prjemian/Apps/anaconda/envs/bluesky_2020_9/lib/python3.8/site-packages/past/builtins/misc.py:45
  /home/prjemian/Apps/anaconda/envs/bluesky_2020_9/lib/python3.8/site-packages/past/builtins/misc.py:45: DeprecationWarning: the imp module is deprecated in favour of importlib; see the module's documentation for alternative uses
    from imp import reload

-- Docs: https://docs.pytest.org/en/stable/warnings.html
========================================================================== 4 passed, 1 warning in 1.61s ===========================================================================

Originally posted by @prjemian in #43 (comment)

Missing required package meta-data

Missing required package meta-data:

(bluesky_2021_1) prjemian@poof /tmp/hklpy $ python3 setup.py sdist bdist_wheel
running sdist
running egg_info
creating hklpy.egg-info
writing hklpy.egg-info/PKG-INFO
writing dependency_links to hklpy.egg-info/dependency_links.txt
writing top-level names to hklpy.egg-info/top_level.txt
writing manifest file 'hklpy.egg-info/SOURCES.txt'
reading manifest file 'hklpy.egg-info/SOURCES.txt'
reading manifest template 'MANIFEST.in'
writing manifest file 'hklpy.egg-info/SOURCES.txt'
running check
warning: Check: missing required meta-data: url

warning: Check: missing meta-data: either (author and author_email) or (maintainer and maintainer_email) must be supplied

creating hklpy-0.3.15
creating hklpy-0.3.15/hkl
creating hklpy-0.3.15/hkl/tests

Should have found that out BEFORE tagging. Now, need to delete the tag before proceeding.

Originally posted by @prjemian in #70 (comment)

provide SPEC-compatibility functions from APS 29-ID

Various functions are provided in the APS 29-ID (restricted-access) repository that make it easier for SPEC users to remember the new tools in Bluesky's hklpy package.

Such as:

def calcUB(eV,r1,r2):
    """Compute the UB matrix with two reflections for a given energy."""
    fourc.calc.wavelength = eV2Lambda(eV)
    fourc.calc.sample.compute_UB(r1, r2)
    print(fourc.calc.sample.UB)

def setor(h,k,l,tth0,omega0,chi0,phi0):
    """Define a reflection in fourc"""
    r1 = fourc.calc.sample.add_reflection(     
        h,k,l,
        position=fourc.calc.Position(tth=tth0, omega=omega0, chi=chi0, phi=phi0))
    return(r1)

Here are some of the functions:

function docstring
cahkl Calculate omega,chi,phi,tth,kth,kap,kphi for a given h,k,l reflection
listSamples List all samples currently defined in fourc; specify current one.
newSample Add new sample to diffractometer.
scanHKL_E Uses bluesky pyhkl to calculate the motor positions for an energy scan at constant Q
setor Define a reflection in fourc

Investigate these functions and generalize, yet simplify, for SPEC users.

Thanks, @rodolakis!

wish list: get wavelength from beam line controls

Other things we need to consider are:

  • ability to convert the RBV of beamline

Also, convert energy units appropriately (soft x-ray beamlines at NSLS-II use eV, not keV). In other words, rely on EPICS UNITS to decide how to convert.

If we continue to require a manual setting of the hklpy calculation energy, then these things are transparent. I am just anticipating something more sophisticated as I am not sure how much you want to change.

Originally posted by @ambarb in #17 (comment)

Notes on design conversation for an hkl solver API

Conversation with @prjemian, @stuwilkins, @jklynch, @MikeHart85 and me

Review of terminology coordinate systems

  • B goes from HKL to an orthonormal basis in the crystal reference frame
  • U goes from the crystal reference frame to the reciprocal lab frame (expressing how the crystal is stuck onto the diffractometer)
  • Solving the diffractometer equation goes from the reciprocal lab frame to diffractometer angles. (Some people loosely call this "real space" but perhaps they shouldn't. It's angles.)

Desired API

The desired "solver API" into the HKL computation code should be transformations from reciprocal space to diffractometer angle space and vice versa, each taking three arguments:

  • a structure (dict or struct) describing a geometry (motors, reference positions, and constraints)
  • observed mapping between real and reciprocal space to give you the "U" of the UB matrix
  • the crystallography to give you the "B"

Underneath this API could be many different solvers:

  • custom project
  • pybind-wrapped components from hkl
  • SPEC

It should be easy to switch between solvers at run time so that new things can be validated.

update badges to match ophyd

The ophyd project has the latest update on badges. Let's match what is on that README page for the 0.3.15 release by #70:

Build Status

Name Downloads Version Platforms
Conda Recipe Conda Downloads Conda Version Conda Platforms

review examples provided here

In various comments in a separate issue&PR, changes to the examples were discussed.

  • Examine the current examples and decide if each should be kept.
  • Are some examples overly specific to one instrument? Is that a good thing? Generic is also good, when possible.
  • Add examples as deemed appropriate.
    • 4circle
    • Show how to use simulated motors. (hint: really easy)
    • Show how to swap between simulated motors and real motors.
  • Examine for technical problems such as wavelength in nm (see #17).

no version number available from import!!!

In [1]: import gi
   ...: gi.require_version("Hkl", "5.0")

In [2]: import hkl

In [3]: hkl.__version__
---------------------------------------------------------------------------
AttributeError                            Traceback (most recent call last)
<ipython-input-3-25eb45a6aff7> in <module>
----> 1 hkl.__version__

AttributeError: module 'hkl' has no attribute '__version__'

yet

(base) ~/.../Bluesky/hklpy $ pwd
~/Documents/projects/Bluesky/hklpy
(base) ~/.../Bluesky/hklpy $ python ./setup.py version
running version
keywords are unexpanded, not using
got version from VCS {'version': '0.3.14+250.gb2652eb', 'full-revisionid': 'b2652eb57e4ccc632ad2a1891bb5bf2c641810aa', 'dirty': False, 'error': None}
Version: 0.3.14+250.gb2652eb
 full-revisionid: b2652eb57e4ccc632ad2a1891bb5bf2c641810aa
 dirty: False

add "wavelength" to Diffractometer class

As suggested, add wavelength to hkl.diffract.Diffractometer, comparable to the energy attribute. Consider deprecating the energy attribute since it is implemented specific to X-ray probes.

Since wavelength is the scientific term used in the computations, consider deprecating the energy attribute since energy is implemented here as specific to X-ray probes. If energy is to be deprecated, the final removal of energy from hklpy would be a breaking change (upgrade the major version number). That should be considered with other changes (such as the consideration of units handling for wavelength and lattice parameters).

Release process for v0.3.15

The hklpy package is ready for 0.3.15 release. What is the procedure to tag and release (including publishing conda & pip builds)?

compute_UB() should return the UB matrix

The return value from compute_UB() is success of computation (1 if ok, 0 if not). This is not useful. Much more informative to return either the UB matrix or None.

ENH more robust forward calcuation

@cmazzoli

This is happening with the failure to forward calculate the current position. I am manually setting omega to be a value. The calculation value is updated but not the calculation limits.

tardis.omega.set(0)
n [60]: tardis.calc['omega'].value
Out[60]: 0

In [61]: tardis.calc['omega'].limits
Out[61]: (3.0, 3.0)

I am manually moving omega, but in the in it will not be continuously moved. Shorter, more integrated method to freeze a calculation would be great.

rr001m = tardis.calc.sample.add_reflection(0, 0, 1, position=tardis.calc.Position(theta=157.5, omega=8, chi=0.0, phi=0.0, delta=-.3, gamma=12.8))
r_3dmag_0k0_side= tardis.calc.sample.add_reflection(0,-2/3, 0, position=tardis.calc.Position(theta=-94.547, omega=0, chi=0, phi=0.0, delta=126.55, gamma=3.900))
tardis.calc.sample.compute_UB(rr001m, r_3dmag_0k0_side)
In [41]: wh_pos(tardis)

+--------------+---------------------------------+--------------------------------+--------------------------------+
| Positioner   |                           Value |                      Low Limit |                     High Limit |
+--------------+---------------------------------+--------------------------------+--------------------------------+
| tardis       | [ 0.       -0.662072 -0.17091 ] | TardisPseudoPos(h=0, k=0, l=0) | TardisPseudoPos(h=0, k=0, l=0) |
| tardis_chi   |                         0.00000 |                              0 |                              0 |
| tardis_delta |                       126.55000 |                       -3.20000 |                      181.00000 |
| tardis_gamma |                         3.89970 |                       -2.50000 |                      180.50000 |
| tardis_h     |                         0.00000 |                              0 |                              0 |
| tardis_k     |                        -0.66207 |                              0 |                              0 |
| tardis_l     |                        -0.17091 |                              0 |                              0 |
| tardis_omega |                               0 |                              0 |                              0 |
| tardis_phi   |                         0.00000 |                              0 |                              0 |
| tardis_theta |                       -94.54700 |                     -170.00000 |                      158.00000 |
+--------------+---------------------------------+--------------------------------+--------------------------------+

In [42]: tardis.position
Out[42]: TardisPseudoPos(h=3.019043153215566e-07, k=-0.6620717318271894, l=-0.17091012908220807)

In [43]: tardis.forward(0,-0.66207,-0.1709)
---------------------------------------------------------------------------
GError                                    Traceback (most recent call last)
/opt/conda_envs/collection-17Q1.0/lib/python3.5/site-packages/hkl/engine.py in pseudo_positions(self, values)
    213             geometry_list = self._engine.pseudo_axis_values_set(values,
--> 214                                                                 self._units)
    215         except GLib.GError as ex:

GError: no remaining solutions

During handling of the above exception, another exception occurred:

ValueError                                Traceback (most recent call last)
/opt/conda_envs/collection-17Q1.0/lib/python3.5/site-packages/hkl/calc.py in forward_iter(self, start, end, max_iters, threshold, decision_fcn)
    490         try:
--> 491             self.engine.pseudo_positions = end
    492             return self.engine.solutions

/opt/conda_envs/collection-17Q1.0/lib/python3.5/site-packages/hkl/engine.py in pseudo_positions(self, values)
    215         except GLib.GError as ex:
--> 216             raise ValueError('Calculation failed (%s)' % ex)
    217 

ValueError: Calculation failed (no remaining solutions)

During handling of the above exception, another exception occurred:

UnreachableError                          Traceback (most recent call last)
/home/xf23id1/Beamline/ScienceComm/2017_04_MuR/user_defs.py in <module>()
----> 1 tardis.forward(0,-0.66207,-0.1709)

/opt/conda_envs/collection-17Q1.0/lib/python3.5/site-packages/ophyd/pseudopos.py in wrapped(self, *args, **kwargs)
    198                 pos, new_kwargs = self.to_real_tuple(*args, **kwargs)
    199 
--> 200             return method(self, pos, **new_kwargs)
    201 
    202         return wrapped

/opt/conda_envs/collection-17Q1.0/lib/python3.5/site-packages/hkl/diffract.py in forward(self, pseudo)
    231     def forward(self, pseudo):
    232         solutions = self._calc.forward_iter(start=self.position, end=pseudo,
--> 233                                             max_iters=100)
    234         logger.debug('pseudo to real: {}'.format(solutions))
    235         return self._decision_fcn(pseudo, solutions)

/opt/conda_envs/collection-17Q1.0/lib/python3.5/site-packages/hkl/calc.py in wrapped(self, *args, **kwargs)
     42             initial_pos = self.physical_positions
     43             try:
---> 44                 return func(self, *args, **kwargs)
     45             finally:
     46                 self.physical_positions = initial_pos

/opt/conda_envs/collection-17Q1.0/lib/python3.5/site-packages/hkl/calc.py in forward_iter(self, start, end, max_iters, threshold, decision_fcn)
    496                                    ''.format(iters, max_iters, valid_pseudo,
    497                                              valid_real),
--> 498                                    pseudo=valid_pseudo, physical=valid_real)
    499 
    500     @_keep_physical_position

UnreachableError: Unable to solve. iterations=100/100
Last valid position: None
None 
HklSample(name='main', lattice=LatticeTuple(a=5.42, b=5.42, c=12.72, alpha=90.0, beta=90.0, gamma=90.0), ux=Parameter(name='None (internally: ux)', limits=(-180.0, 180.0), value=-3.761652562247211, fit=True, inverted=False, units='Degree'), uy=Parameter(name='None (internally: uy)', limits=(-180.0, 180.0), value=13.391338088391683, fit=True, inverted=False, units='Degree'), uz=Parameter(name='None (internally: uz)', limits=(-180.0, 180.0), value=-21.305927243686735, fit=True, inverted=False, units='Degree'), U=array([[ 0.90632282,  0.35346852,  0.23160084],
       [-0.37672075,  0.92412563,  0.0638223 ],
       [-0.1914691 , -0.14509245,  0.97071508]]), UB=array([[ 1.05066314,  0.40976166,  0.11440181],
       [-0.43671703,  1.07130122,  0.03152573],
       [-0.22196233, -0.16819977,  0.4794955 ]]), reflections=[(0.0, 0.0, 1.0), (0.0, 0.0, 1.0), (1.0, 0.0, 0.0), (0.0, -2.0, 0.0), (0.0, -2.0, 0.0), (0.0, -0.6666666666666666, 0.0)])

Show the constraints in the geometry examples

In the notebook link, I don't see anywhere where the calculation limits are set so the values that are displayed may not even be possible to reach with a physical diffractometer. The exercise seems just to be to show people that the coordinate system is correctly defined.

Originally posted by @ambarb in #52 (comment)

calc should use wavelength in angstrom

Following the E6C example from the hkl source code, wavelength should be specified in angstrom. The problem is in this hklpy package, conversion from energy to wavelength uses nm.

hklpy/hkl/calc.py

Lines 143 to 150 in 169dd09

@property
def energy(self):
'''The energy associated with the geometry, in keV'''
return NM_KEV / self.wavelength
@energy.setter
def energy(self, energy):
self.wavelength = NM_KEV / energy

The voltage-wavelength product is specified as NM_KEV which expresses a clear intent of this package to use nm.

NM_KEV = 1.239842 # lambda = 1.24 / E (nm, keV or um, eV)

However, when energy is specified, it results in the wavelength expressed in nm, and an incorrect orientation matrix is computed. The underlying hkl code uses angstrom.

Should discuss before making a change here.

update _energy_changed() in Diffractometer

In #25, #26, and #42, an example was prepared to show how to connect energy from the local controls with the calc engine energy. The additional features were:

  • allow for an offset energy
  • allow for conversion of energy units to the keV used by the calc engine
  • add a flag to allow/block automatic updates of calc engine when control energy changes
  • direct command to bypass that lock yet keep the offset and units conversion

These features can be added to the Diffractometer class. But the signals should be Signal, not EpicsSignal, in this code. Documentation should describe how to override with EpicsSignal for an actual EPICS use case.

signal default value
offset 0
units keV
flag True

This change will bring this requested support into the mainstream.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.