<jupyter_start><jupyter_text>Rename the axes of a 4-circle diffractometerFollowing the E4CV example, this example will repeat those same steps but using different names for the motor axes.E4CV name | local name--- | ---omega | thetachi | chiphi | phitth | ttheta How's it done?This change is made by:1. Defining the `FourCircle` class using our local names   (instead of the canonical E4CV names)1. Writing a dictionary to the `fourc` object that maps   the canonical E4CV names to our local names:```pythonfourc.calc.physical_axis_names = {     E4CV: local    'omega': 'theta',    'chi': 'chi',    'phi': 'phi',    'tth': 'ttheta',    }```----Note:  This example is available as a[Jupyter notebook](https://jupyter.org/) from the *hklpy* sourcecode website: https://github.com/bluesky/hklpy/tree/main/examples  Load the *hklpy* package (named _`hkl`_)As done in the E4CV example.This is needed _every_ time before the *hkl* package is first imported.<jupyter_code>import gi
gi.require_version('Hkl', '5.0')<jupyter_output><empty_output><jupyter_text>Define _this_ diffractometerFollowing the E4CV example, create a `FourCircle` class,but use our own names for the motors.E4CV name | local name--- | ---omega | thetachi | chiphi | phitth | tthetaUse the new motor names when constructing the `FourCircle` class.  Otherwise, everything else in this step the same as in the E4CV example.<jupyter_code>from hkl import E4CV, SimMixin
from ophyd import SoftPositioner
from ophyd import Component as Cpt

class FourCircle(SimMixin, E4CV):
    """
    Our 4-circle.  Eulerian, vertical scattering orientation.
    """
    # reciprocal axes defined in SimMixin

    # the motor axes are called: real in hklpy
    theta = Cpt(SoftPositioner, kind="hinted", init_pos=0)
    chi = Cpt(SoftPositioner, kind="hinted", init_pos=0)
    phi = Cpt(SoftPositioner, kind="hinted", init_pos=0)
    ttheta = Cpt(SoftPositioner, kind="hinted", init_pos=0)<jupyter_output><empty_output><jupyter_text>Create an instance of the diffractometer.As in the E4CV example, create an instance of the diffractometer.  Note that the instance still has the canonical E4CV names, as reported by *libhkl*.<jupyter_code>fourc = FourCircle("", name="fourc")
print("motor names:", fourc.calc.physical_axis_names)<jupyter_output>motor names: ['omega', 'chi', 'phi', 'tth']<jupyter_text>Switch to *our* motor namesThis is the magic step, *map* the canonical *libhkl* names ontothe names we want to use.  This is done using a Python dictionary.  The keys are the canonical names, the value of key is the local name.  *All* axesmust be in the dictionary, even if the names remain the same.<jupyter_code>fourc.calc.physical_axis_names = {
    # E4CV: local
    'omega': 'theta',
    'chi': 'chi',
    'phi': 'phi',
    'tth': 'ttheta',
    }

print("motor names:", fourc.calc.physical_axis_names)<jupyter_output>motor names: ['theta', 'chi', 'phi', 'ttheta']<jupyter_text>Add a sample with a crystal structure<jupyter_code>from hkl import Lattice
from hkl import SI_LATTICE_PARAMETER

# add the sample to the calculation engine
a0 = SI_LATTICE_PARAMETER
fourc.calc.new_sample(
    "silicon",
    lattice=Lattice(a=a0, b=a0, c=a0, alpha=90, beta=90, gamma=90)
    )<jupyter_output><empty_output><jupyter_text>Setup the **UB** orientation matrix using *hklpy*Define the crystal's orientation on the diffractometer using the 2-reflection method described by [Busing & Levy, Acta Cryst 22 (1967) 457](https://www.psi.ch/sites/default/files/import/sinq/zebra/PracticalsEN/1967-Busing-Levy-3-4-circle-Acta22.pdf). Choose the same wavelength X-rays for both reflections<jupyter_code>fourc.calc.wavelength = 1.54  # Angstrom (8.0509 keV)<jupyter_output><empty_output><jupyter_text>Find the first reflection and identify its Miller indices: (_hkl_)<jupyter_code>r1 = fourc.calc.sample.add_reflection(
    4, 0, 0,
    position=fourc.calc.Position(
        ttheta=69.0966,
        theta=-145.451,
        chi=0,
        phi=0,
    )
)<jupyter_output><empty_output><jupyter_text>Find the second reflection<jupyter_code>r2 = fourc.calc.sample.add_reflection(
    0, 4, 0,
    position=fourc.calc.Position(
        ttheta=69.0966,
        theta=-145.451,
        chi=90,
        phi=0,
    )
)<jupyter_output><empty_output><jupyter_text>Compute the **UB** orientation matrixThe `compute_UB()` method always returns 1.  Ignore it.<jupyter_code>fourc.calc.sample.compute_UB(r1, r2)<jupyter_output><empty_output><jupyter_text>Report what we have setup<jupyter_code>import pyRestTable

tbl = pyRestTable.Table()
tbl.labels = "term value".split()
tbl.addRow(("energy, keV", fourc.calc.energy))
tbl.addRow(("wavelength, angstrom", fourc.calc.wavelength))
tbl.addRow(("position", fourc.position))
tbl.addRow(("sample name", fourc.sample_name.get()))
tbl.addRow(("[U]", fourc.U.get()))
tbl.addRow(("[UB]", fourc.UB.get()))
tbl.addRow(("lattice", fourc.lattice.get()))
print(tbl)

print(f"sample\t{fourc.calc.sample}")<jupyter_output>==================== =========================================================================
term                 value                                                                    
==================== =========================================================================
energy, keV          8.050921948051947                                                        
wavelength, angstrom 1.54                                                                     
position             FourCirclePseudoPos(h=-0.0, k=0.0, l=0.0)                                
sample name          silicon                                                                  
[U]                  [[-1.22173048e-05 -1.22173048e-05 -1.00000000e+00]                       
                      [ 0.00000000e+00 -1.00000000e+00  1.22173048e-05]                       
                      [-1.00000000e+00  1.49262536e-10  1.22173048e-05]]                      
[UB]                 [[-1.41342846e-05 -1.41342846[...]<jupyter_text>Check the orientation matrixPerform checks with _forward_ (hkl to angle) and_inverse_ (angle to hkl) computations to verify the diffractometerwill move to the same positions where the reflections were identified. Constrain the motors to limited ranges* allow for slight roundoff errors* keep `ttheta` in the positive range* keep `theta` in the negative range* keep `phi` fixed at zero<jupyter_code>fourc.calc["ttheta"].limits = (-0.001, 180)
fourc.calc["theta"].limits = (-180, 0.001)

fourc.phi.move(0)
fourc.engine.mode = "constant_phi"<jupyter_output><empty_output><jupyter_text>(400) reflection Check the inverse calculation: (400)<jupyter_code>sol = fourc.inverse((-145.451, 0, 0, 69.0966))
print(f"(4 0 0) ? {sol.h:.2f} {sol.k:.2f} {sol.l:.2f}")<jupyter_output>(4 0 0) ? 4.00 0.00 0.00<jupyter_text>Check the forward calculation: (400)<jupyter_code>sol = fourc.forward((4, 0, 0))
print(
    "(400) :", 
    f"ttheta={sol.ttheta:.4f}", 
    f"theta={sol.theta:.4f}", 
    f"chi={sol.chi:.4f}", 
    f"phi={sol.phi:.4f}"
    )<jupyter_output>(400) : ttheta=69.0982 theta=-145.4502 chi=0.0000 phi=0.0000<jupyter_text>(040) reflection Check another inverse calculation: (040)<jupyter_code>sol = fourc.inverse((-145.451, 90, 0, 69.0966))
print(f"(0 4 0) ? {sol.h:.2f} {sol.k:.2f} {sol.l:.2f}")<jupyter_output>(0 4 0) ? 0.00 4.00 0.00
{'fraction_non_alphanumeric': 0.14849726775956285, 'fraction_numerical': 0.05122950819672131, 'mean_word_length': 3.558530510585305, 'pattern_counts': {'":': 0, '<': 51, '<?xml version=': 0, '>': 51, 'https://': 3, 'lorem ipsum': 0, 'www.': 1, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 4, 'word_list_counts': {'cursed_substrings.json': 8, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'chain_length': '14', 'dir': 'jupyter-structured-clean-dedup', 'id': '481003', 'license': 'BSD-3-Clause', 'n_tokens_mistral': 2697, 'n_tokens_neox': 2391, 'n_words': 621, 'path': 'examples/var_e4cv_renamed_axes.ipynb', 'repo_name': 'NSLS-II/hklpy', 'size': '7320'}