Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Extra motors in hklpy #39

Closed
gfabbris opened this issue Oct 13, 2020 · 9 comments
Closed

Extra motors in hklpy #39

gfabbris opened this issue Oct 13, 2020 · 9 comments
Assignees
Labels
question Further information is requested

Comments

@gfabbris
Copy link
Contributor

@prjemian: I cannot place any motor other than the 4C ones in:

class FourCircleDiffractometer(DiffractometerMixin, E4CV):

It raises this error:

Screen Shot 2020-10-12 at 6 55 33 PM

Is this the expected behavior?

@gfabbris gfabbris added the question Further information is requested label Oct 13, 2020
@gfabbris gfabbris self-assigned this Oct 13, 2020
@prjemian
Copy link
Contributor

prjemian commented Oct 13, 2020 via email

@prjemian
Copy link
Contributor

This seems to be a feature from ophyd. hklpy.diffract.Diffractometer subclasses ophyd.pseudopos.PseudoPositioner. PseudoPositioner.__init__() makes two lists of positioners: https://github.com/bluesky/ophyd/blob/1b0be51a095388376f90162fb08c5c91f8291175/ophyd/pseudopos.py#L397-L400

        self._real = [getattr(self, attr)
                      for attr, cpt in self._get_real_positioners()]
        self._pseudo = [getattr(self, attr)
                        for attr, cpt in self._get_pseudo_positioners()]

The HKL calculation engines expect the pseudo & real positioners to be appropriate to the geometry and calculation engine. This makes it hard to remove the error you report.

Have not tested it yet but it seems possible to add a Device as a Component of a Diffractometer subclass. In that Device, one or more motors could be grouped.

@prjemian
Copy link
Contributor

BUT, on further reading of the source code of _get_real_positioners(), it may be possible:

    def _get_real_positioners(cls):
        '''Inspect the components and find the real positioners

        All `Positioner` components which are not `PseudoSingle`s will be
        returned, by default.

        The built-in mechanism to override the list of real positioners on a
        PseudoPositioner is to define '_real' on the class-level.  It should be
        a list of attribute names. This allows you to group real motors
        logically on the device but not have them included in motions or
        calculations.

@prjemian
Copy link
Contributor

Similar for _get_pseudo_positioners():

        The built-in mechanism to override the list of pseudo positioners on a
        PseudoPositioner is to define '_pseudo' on the class-level.  It should
        be a list of attribute names.

@prjemian
Copy link
Contributor

Summarizing, your problem was anticipated in ophyd and a solution method is available. Define _real = ["omega", "chi", "phi", "tth"] next to your Component declarations, then add any other real motors as you wish. Similarly, if you add additional PseudoPositioner (or subclass of) objects, add a _pseudo = ["h", "k", "l"] list.

@prjemian
Copy link
Contributor

Just tested and confirm this works. Starting with a plain 4-circle simulator:

class FourcPlain(E4CV):
    h = Cpt(PseudoSingle, '')
    k = Cpt(PseudoSingle, '')
    l = Cpt(PseudoSingle, '')

    omega = Cpt(SoftPositioner, limits=(-180, 180))
    chi = Cpt(SoftPositioner, limits=(-180, 180))
    phi = Cpt(SoftPositioner, limits=(-180, 180))
    tth = Cpt(SoftPositioner, limits=(-180, 180))

    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)

        for p in self.real_positioners:
            p._set_position(0)  # give each a starting position

fourc = FourcPlain("", name="fourc")

Fails, crashing the kernel (and session)

class FourcProblem(FourcPlain):
    m1 = Cpt(EpicsMotor, "sky:m1")

fourc = FourcProblem("", name="fourc")
# Note: executing this will crash the kernel

Succeeds (with my "sky:m1" motor PV)

class FourcFixed(FourcPlain):
    _real = ["omega", "chi", "phi", "tth", ]
    m1 = Cpt(EpicsMotor, "sky:m1")

fourc = FourcFixed("", name="fourc")

But note, fourc.wh() needs to be fixed for my new understanding now.

@prjemian
Copy link
Contributor

Currently, fourc.wh() (you'd need to add the apstools.diffractometer.DiffractometerMixin to get the why() method) reports:

In [7]: fourc.wh()
===================== =========
term                  value    
===================== =========
diffractometer        fourc    
mode                  bissector
wavelength (angstrom) 1.54     
h                     0.0      
k                     0.0      
l                     0.0      
omega                 0        
chi                   0        
phi                   0        
tth                   0        
===================== =========

Out[7]: <pyRestTable.rest_table.Table at 0x7ff1c3bcd110>

and this is missing the fourc.m1 motor:

In [9]: fourc.summary()
data keys (* hints)
-------------------
 fourc_chi
*fourc_h
 fourc_h_setpoint
*fourc_k
 fourc_k_setpoint
*fourc_l
 fourc_l_setpoint
 fourc_omega
 fourc_phi
 fourc_tth

read attrs
----------
h                    PseudoSingle        ('fourc_h')
h.readback           AttributeSignal     ('fourc_h')
h.setpoint           AttributeSignal     ('fourc_h_setpoint')
k                    PseudoSingle        ('fourc_k')
k.readback           AttributeSignal     ('fourc_k')
k.setpoint           AttributeSignal     ('fourc_k_setpoint')
l                    PseudoSingle        ('fourc_l')
l.readback           AttributeSignal     ('fourc_l')
l.setpoint           AttributeSignal     ('fourc_l_setpoint')
omega                SoftPositioner      ('fourc_omega')
chi                  SoftPositioner      ('fourc_chi')
phi                  SoftPositioner      ('fourc_phi')
tth                  SoftPositioner      ('fourc_tth')

config keys
-----------
fourc_UB
fourc_energy
fourc_m1_acceleration
fourc_m1_motor_egu
fourc_m1_user_offset
fourc_m1_user_offset_dir
fourc_m1_velocity

configuration attrs
-------------------
energy               Signal              ('fourc_energy')
UB                   AttributeSignal     ('fourc_UB')
h                    PseudoSingle        ('fourc_h')
k                    PseudoSingle        ('fourc_k')
l                    PseudoSingle        ('fourc_l')
m1                   EpicsMotor          ('fourc_m1')
m1.user_offset       EpicsSignal         ('fourc_m1_user_offset')
m1.user_offset_dir   EpicsSignal         ('fourc_m1_user_offset_dir')
m1.velocity          EpicsSignal         ('fourc_m1_velocity')
m1.acceleration      EpicsSignal         ('fourc_m1_acceleration')
m1.motor_egu         EpicsSignal         ('fourc_m1_motor_egu')

unused attrs
------------
sample_name          AttributeSignal     ('fourc_sample_name')
lattice              ArrayAttributeSignal('fourc_lattice')
lattice_reciprocal   AttributeSignal     ('fourc_lattice_reciprocal')
U                    AttributeSignal     ('fourc_U')
reflections          ArrayAttributeSignal('fourc_reflections')
ux                   AttributeSignal     ('fourc_ux')
uy                   AttributeSignal     ('fourc_uy')
uz                   AttributeSignal     ('fourc_uz')

@gfabbris
Copy link
Contributor Author

Awesome, this works well. Thanks @prjemian!

@gfabbris
Copy link
Contributor Author

Closed by #40.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
question Further information is requested
Projects
None yet
Development

No branches or pull requests

2 participants