Ejemplo n.º 1
0
    def test_rotation_nd(self):
        for rand1 in np.random.uniform(-np.pi, np.pi, 100):
            # Test 2D case
            is_close(rotation_from_angle(rand1),
                     rotation_from_angle_and_plane(rand1, (1, 0), (0, 1)))
            # Test 3D case
            is_close(
                rotation_around_z(rand1),
                rotation_from_angle_and_plane(rand1, (1, 0, 0), (0, 1, 0)))
            # Test normalisation of first parameter
            is_close(
                rotation_around_z(rand1),
                rotation_from_angle_and_plane(rand1, (2, 0, 0), (0, 1, 0)))
            # Test normalisation of second parameter
            is_close(
                rotation_around_z(rand1),
                rotation_from_angle_and_plane(rand1, (1, 0, 0), (0, 2, 0)))
            # Test normalisation of both parameters
            is_close(
                rotation_around_z(rand1),
                rotation_from_angle_and_plane(rand1, (2, 0, 0), (0, 2, 0)))

            is_close(
                rotation_around_x(rand1),
                rotation_from_angle_and_plane(rand1, (0, 1, 0), (0, 0, 1)))
            is_close(
                rotation_around_y(rand1),
                rotation_from_angle_and_plane(rand1, (0, 0, 1), (1, 0, 0)))

            # Test non orthogonal vectors
            is_close(
                rotation_around_x(rand1),
                rotation_from_angle_and_plane(rand1, (0, 1, 1), (0, 1, 2)))

            # Test generic properties of higher dimensional rotations

            # create two perpendicular random vectors
            dimension = random.randint(4, 100)
            v1 = np.random.uniform(-1., 1., dimension)
            v1 = v1 / np.linalg.norm(v1)
            v2 = np.random.uniform(-1., 1., dimension)
            v2 = v2 / np.linalg.norm(v2)
            v2 = v2 - v2.dot(v1) * v1

            m = rotation_from_angle_and_plane(rand1, v1, v2)
            m_inv = rotation_from_angle_and_plane(-rand1, v1, v2)
            is_close(m.dot(m.T), np.eye(dimension))
            self.assertAlmostEqual(np.linalg.det(m), 1.0)
            is_close(m, m_inv.T)

        with self.assertRaises(ValueError):
            rotation_from_angle_and_plane(0., (1, 0, 0), (0, 1, 0, 0))
        with self.assertRaises(ValueError):
            rotation_from_angle_and_plane(0., (1, 0, 0, 0), (0, 1, 0))
        with self.assertRaises(ValueError):
            rotation_from_angle_and_plane(0., (1, 0, 0), (1, 0, 0))
        with self.assertRaises(ValueError):
            rotation_from_angle_and_plane(0., (0, 0), (1, 0))
        with self.assertRaises(ValueError):
            rotation_from_angle_and_plane(0., (1, 0), (0, 0))
Ejemplo n.º 2
0
    def test_rotation_around_axis(self):
        for random in (np.random.rand(10000) - 0.5) * 2 * np.pi:
            is_close(rotation_around_axis([1, 0, 0], random), rotation_around_x(random))
            is_close(rotation_around_axis([0, 1, 0], random), rotation_around_y(random))
            is_close(rotation_around_axis([0, 0, 1], random), rotation_around_z(random))
            # Test if non-normalised axis works, too
            is_close(rotation_around_axis([2, 0, 0], random), rotation_around_x(random))

        is_close(rotation_around_axis([1, 1, 1], 2*np.pi/3), rotation_from_angles([0, np.pi/2, np.pi/2], 'XYZ'))
Ejemplo n.º 3
0
 def test_rotation_y(self):
     is_close(rotation_around_y(0), np.eye(3))
     is_close(rotation_around_y(np.pi/2), [[0, 0, 1], [0, 1, 0], [-1, 0, 0]])
     is_close(rotation_around_y(np.pi), np.diag([-1, 1, -1]))
    def update(self, dt, controls):
        # Read controls into intermediate axes
        phase_table_switch = controls.get_switch(0)
        control_mode_switch = controls.get_switch(1)

        if (control_mode_switch == 0):
            # Movement
            self.axis_forward = Tools.toward(self.axis_forward,
                                             controls.get_axis(0), 4.0 * dt)
            self.axis_side = Tools.toward(self.axis_side, controls.get_axis(1),
                                          4.0 * dt)
            self.axis_speed = Tools.toward(
                self.axis_speed,
                Tools.ramp(controls.get_axis(2), -1.0, 0.0, 1.0, 1.0),
                2.0 * dt)
            self.axis_turn = Tools.toward(self.axis_turn, controls.get_axis(3),
                                          4.0 * dt)
            self.axis_leg_lift = Tools.toward(
                self.axis_leg_lift,
                Tools.ramp(controls.get_axis(4), -1.0, 0.5, 0.0, 1.0),
                0.5 * dt)
            self.axis_body_lift = Tools.toward(
                self.axis_body_lift,
                Tools.ramp(controls.get_axis(4), 0.0, 0.0, 1.0, 1.0), 0.5 * dt)

            # Keep attitude, no twist
            self.axis_tilt_forward = 0.0
            self.axis_tilt_side = 0.0

            self.twist = 0.0
        elif (control_mode_switch == 1):
            # Reset movement axes
            self.axis_forward = Tools.toward(self.axis_forward, 0.0, 4.0 * dt)
            self.axis_side = Tools.toward(self.axis_side, 0.0, 4.0 * dt)
            self.axis_speed = Tools.toward(self.axis_speed, 0.0, 4.0 * dt)
            self.axis_turn = Tools.toward(self.axis_turn, 0.0, 4.0 * dt)
            self.axis_leg_lift = Tools.toward(self.axis_leg_lift, 0.0,
                                              4.0 * dt)
            self.axis_body_lift = Tools.toward(self.axis_body_lift, 0.0,
                                               4.0 * dt)

            # Incremental attitude control, absolute twist
            self.axis_tilt_forward = Tools.toward(self.axis_tilt_forward,
                                                  -1.0 * controls.get_axis(0),
                                                  4.0 * dt)
            self.axis_tilt_side = Tools.toward(self.axis_tilt_side,
                                               controls.get_axis(1), 4.0 * dt)

            self.twist = controls.get_axis(3) * TWIST_LIMIT
        else:
            # Reset movement inputs
            self.axis_forward = Tools.toward(self.axis_forward, 0.0, 4.0 * dt)
            self.axis_side = Tools.toward(self.axis_side, 0.0, 4.0 * dt)
            self.axis_speed = Tools.toward(self.axis_speed, 0.0, 4.0 * dt)
            self.axis_turn = Tools.toward(self.axis_turn, 0.0, 4.0 * dt)
            self.axis_leg_lift = Tools.toward(self.axis_leg_lift, 0.0,
                                              4.0 * dt)
            self.axis_body_lift = Tools.toward(self.axis_body_lift, 0.0,
                                               4.0 * dt)

            # Absolute attitude control
            self.axis_tilt_forward = 0.0
            self.axis_tilt_side = 0.0

            self.tilt_forward = controls.get_axis(0) * TILT_LIMIT_FORWARD
            self.tilt_side = controls.get_axis(1) * TILT_LIMIT_SIDE
            self.twist = controls.get_axis(3) * TWIST_LIMIT

        # Calculate movement parameters
        minimum_movement = max(abs(self.axis_forward), abs(self.axis_side),
                               abs(self.axis_turn))
        leg_lift_enabler = Tools.ramp(minimum_movement, 0.0, 0.0, 0.1, 1.0)

        leg_lift = LEG_LIFT_HEIGHT * self.axis_leg_lift * leg_lift_enabler
        body_lift = BODY_LIFT_HEIGHT * Tools.ramp(self.axis_body_lift, 0.0,
                                                  0.0, 1.0, -1.0)
        stride_x = HALF_STRIDE_X * self.axis_forward
        stride_y = HALF_STRIDE_Y * self.axis_side
        rotation = HALF_ROTATION * self.axis_turn

        if (phase_table_switch == 1):
            phase_table = PHASE_TABLE_BUG
        else:
            phase_table = PHASE_TABLE_THREEPOINT

        gait_period = fmod(self.time * 1.0, 2.0)

        # Calculate attitude parameters
        self.tilt_forward += (self.axis_tilt_forward * TILT_SPEED * dt)
        self.tilt_side = Tools.saturate(self.tilt_side, -TILT_LIMIT_SIDE,
                                        TILT_LIMIT_SIDE)

        self.tilt_side += (self.axis_tilt_side * TILT_SPEED * dt)
        self.tilt_forward = Tools.saturate(self.tilt_forward,
                                           -TILT_LIMIT_FORWARD,
                                           TILT_LIMIT_FORWARD)

        attitude_transform = numpy.matmul(
            mgen.rotation_around_z(self.twist),
            numpy.matmul(mgen.rotation_around_y(self.tilt_forward),
                         mgen.rotation_around_x(self.tilt_side)))

        # Work out each leg
        for i in range(6):
            # Calculate stride time with phase offset
            self.leg_phase[i] = Tools.toward(self.leg_phase[i], phase_table[i],
                                             0.5 * dt)
            leg_period = fmod(gait_period + self.leg_phase[i], 2.0)

            if (leg_period < 1.0):
                stride_progress = Tools.ramp(leg_period, 0.0, -1.0, 1.0, 1.0)
            else:
                stride_progress = Tools.ramp(leg_period, 1.0, 1.0, 2.0, -1.0)

            # Calculate translation
            offset_x = stride_x * stride_progress
            offset_y = stride_y * stride_progress
            offset_z = body_lift + (leg_lift * max(0.0, sin(leg_period * pi)))
            offset = [offset_x, offset_y, offset_z]

            # Calculate rotation
            turn_transform = mgen.rotation_around_z(rotation * stride_progress)

            # Calculate final tip location with attitude transform
            tip_location_translated = numpy.add(
                HexapodConstants.TIP_LOCATION_DEFAULT[i], offset)
            tip_location = attitude_transform.dot(
                turn_transform.dot(tip_location_translated))

            # Solve for joint angles using leg model
            angles, can_calculate_angles = self.legs[i].solve_joint_angles(
                tip_location)

            # Only update angles if solution was found
            if (can_calculate_angles):
                self.joint_angles[3 * i] = angles[0]
                self.joint_angles[3 * i + 1] = angles[1]
                self.joint_angles[3 * i + 2] = angles[2]

        # Increment time
        time_multiplier = Tools.ramp(self.axis_speed, 0.0, 0.5, 1.0, 3.0)
        self.time += dt * time_multiplier
Ejemplo n.º 5
0
def run_stratcal_native(logfile=None, **options):
    """Run stratcal for native phasing (mode 6)"""

    # Programmers note:
    #Plain names are in lab coordinate system,
    # suffux _cr are in orthobnormal crystal system.

    print('@~@~ run_stratcal_native', sorted(tt for tt in options.items()))
    fp_in = options.get('input')
    input_data_exch = f90nml.read(fp_in)
    input_data_org = stratcal_exch2org(input_data_exch)

    # NB assumes only one crystal
    crystal_data = input_data_exch['crystal_list']
    sg_name = crystal_data['sg_name'].strip()
    laue_group = get_laue_group(sg_name)

    crystal_matrix = [
        crystal_data['cell_a_axis'],
        crystal_data['cell_b_axis'],
        crystal_data['cell_c_axis'],
    ]

    # coordinates of reciprocal-space crystal matrix
    # in real-space coordinate system
    # Could also be done with metric tensor
    mm = [
        np.cross(crystal_matrix[1], crystal_matrix[2]),
        np.cross(crystal_matrix[2], crystal_matrix[0]),
        np.cross(crystal_matrix[0], crystal_matrix[1]),
    ]
    # Now make orthonormal coordinate system
    if laue_group == '-1':
        mm[2] = unit_vector(np.cross(mm[0], mm[1]))
        mm[1] = unit_vector(np.cross(mm[2], mm[0]))
        mm[0] = unit_vector(mm[0])
    elif laue_group == '2/m':
        mm[2] = unit_vector(np.cross(mm[0], mm[1]))
        mm[1] = unit_vector(mm[1])
        mm[0] = unit_vector(mm[0])
    else:
        mm[2] = unit_vector(mm[2])
        mm[1] = unit_vector(np.cross(mm[2], mm[0]))
        mm[0] = unit_vector(mm[0])
    crystal_unit_np = np.array(mm)

    # NB for all except triclinic crystals, the Y and Z axes of
    # crystal_unit_np match the Y and Z axes of the real-space lattice
    # Only the X axis does not

    # scan axis in real-space coordinate system. NB assumes omega scan axis
    scan_axis = unit_vector(
        input_data_exch['stratcal_instrument_list']['omega_axis'])

    # orthog_component is a unit vector orthogonal to the symmetry axis
    # and in the same plane as the symmetry and rotation axes.
    # omega_projection_angle is the angle from the orthonormal X axis to the
    # projection of the omega axis
    scan_axis_cr = unit_vector(crystal_unit_np.dot(scan_axis))
    if laue_group == '2/m':
        # Index of symmetry axis
        symm_axis_index = 1
        symm_axis_cr = np.array((0, 1, 0))
        xx = scan_axis_cr[0]
        zz = scan_axis_cr[2]
        omega_projection_angle = -math.atan2(zz, xx)
        if zz:
            orthog_component_cr = unit_vector((xx, 00, zz))
        else:
            orthog_component_cr = np.array((1, 0, 0))
    else:
        # For triclinic there is no symmetry axis but we might as well use c*
        symm_axis_index = 2
        symm_axis_cr = np.array((0, 0, 1))
        xx = scan_axis_cr[0]
        yy = scan_axis_cr[1]
        omega_projection_angle = math.atan2(yy, xx)
        if yy:
            orthog_component_cr = unit_vector((xx, yy, 0))
        else:
            orthog_component_cr = np.array((1, 0, 0))

    cr_symm_axis = crystal_unit_np[symm_axis_index]
    symm_projection = symm_axis_cr.dot(scan_axis_cr)
    print('@~@~ scan_axis_cr', scan_axis_cr)
    # if abs(symm_projection) == 1:
    #     orthog_component = crystal_unit_np[0]
    #     omega_projection_angle = 0
    # else:
    #     orthog_component = unit_vector(
    #         scan_axis - cr_symm_axis.dot(scan_axis) * cr_symm_axis
    #     )
    #     # Angle between the projection of the omega axis in the crystal
    #     # orthogonal plane and the crystal symmetry axis
    #     omega_projection_angle = angle_between(crystal_unit_np[0],
    #                                            orthog_component)

    print('@~@~ crystal', sg_name, laue_group, '\n', crystal_unit_np)
    print('@~@~ axis', scan_axis, scan_axis_cr, symm_projection,
          orthog_component_cr)
    print('@~@~ cell lengths',
          [np.linalg.norm(crystal_matrix[ii]) for ii in range(3)])
    print('@~@~ +/- angle bc',
          angle_between(crystal_matrix[1], crystal_matrix[2], deg=True),
          angle_between(crystal_unit_np[1], crystal_unit_np[2], deg=True))
    print('@~@~ +/- angle ac',
          angle_between(crystal_matrix[0], crystal_matrix[2], deg=True),
          angle_between(crystal_unit_np[0], crystal_unit_np[2], deg=True))
    print('@~@~ +/- angle ab',
          angle_between(crystal_matrix[0], crystal_matrix[1], deg=True),
          angle_between(crystal_unit_np[0], crystal_unit_np[1], deg=True))
    print('@~@~ +/- angle a-omega',
          angle_between(crystal_matrix[0], scan_axis, deg=True),
          angle_between(crystal_unit_np[0], scan_axis_cr, deg=True))
    print('@~@~ +/- angle b-omega',
          angle_between(crystal_matrix[1], scan_axis, deg=True),
          angle_between(crystal_unit_np[1], scan_axis_cr, deg=True))
    print('@~@~ +/- angle c-omega',
          angle_between(crystal_matrix[2], scan_axis, deg=True),
          angle_between(crystal_unit_np[2], scan_axis_cr, deg=True))
    print('@~@~ omega_projection_angle', math.degrees(omega_projection_angle))

    if laue_group == '-1':
        # Triclinic, nothing doing. Pass on to default stratcal
        running_process = run_stratcal(**options)
        running_process.wait()
        return running_process.returncode

    elif laue_group == '2/m':
        # Monoclinic
        # Two directions, theta=54.7deg; phi +/-45 deg away from symmetry axis
        zval = math.tan(math.radians(35.3))
        vv = orthog_component_cr + symm_axis_cr * zval

        for ii in (1, -1):
            rot_matrix = mgen.rotation_around_y(ii * math.pi / 4)
            # We are using post-multiplication (of row vectors)
            # so we must transpose the rotation matrix.
            # See mgen docs and links therein
            rot_matrix.transpose()
            orientation = tuple(vv.dot(rot_matrix))
            orthog_vector = tuple(np.cross(orientation, symm_axis_cr))
            add_crystal_orientation(input_data_org, orientation, orthog_vector)

    elif laue_group == 'mmm':
        # Orthorhombic
        # Along a body diagonal - selecting diagonal closest to the rotation axis
        orientation = tuple(math.copysign(1, x) for x in scan_axis_cr)
        orthog_vector = tuple(np.cross(orientation, symm_axis_cr))
        add_crystal_orientation(input_data_org, orientation, orthog_vector)

    elif laue_group == '4/m':
        # 'C4'
        # One directions, theta=54.7deg
        zval = math.tan(math.radians(35.3))
        orientation = tuple(orthog_component_cr + zval * symm_axis_cr)
        orthog_vector = tuple(np.cross(orientation, symm_axis_cr))
        add_crystal_orientation(input_data_org, orientation, orthog_vector)

    elif laue_group == '4/mmm':
        # 422
        # theta=67.5, 22.5 deg from the plane of a twofold axis
        # As close as possible to omega axis
        mult, remainder = nearest_modulo(omega_projection_angle, math.pi / 2)
        aa = mult * math.pi / 2 + math.copysign(math.pi / 8, remainder)
        orientation = (math.cos(aa), math.sin(aa), math.tan(math.pi / 8))
        orthog_vector = tuple(np.cross(orientation, symm_axis_cr))
        add_crystal_orientation(input_data_org, orientation, orthog_vector)

    elif laue_group == '-3':
        # 'C3'
        # Two directions ca. 30deg and 40 deg above the plane, 60deg apart
        for ii in (1, -1):
            zval = math.tan(math.radians(35.3 + ii * 5))
            vv = orthog_component_cr + zval * symm_axis_cr
            rot_matrix = mgen.rotation_around_z(ii * math.pi / 6)
            # We are using post-multiplication (of row vectors)
            # so we must transpose the rotation matrix.
            # See mgen docs and links therein
            rot_matrix.transpose()
            orientation = tuple(vv.dot(rot_matrix))
            orthog_vector = tuple(np.cross(orientation, symm_axis_cr))
            add_crystal_orientation(input_data_org, orientation, orthog_vector)

    elif laue_group == '-3m':
        # '32'
        # One direction, theta=60deg, phi 30 deg from a twofold axis
        # NB X* is 30 deg from a twofold axis
        # As close as possible to omega axis
        mult, remainder = nearest_modulo(omega_projection_angle, math.pi / 3)
        # aa = mult*math.pi/3 + math.copysign(math.pi/6, remainder)
        aa = mult * math.pi / 3

        orientation = (math.cos(aa), math.sin(aa), math.tan(math.pi / 6))
        orthog_vector = tuple(np.cross(orientation, symm_axis_cr))
        add_crystal_orientation(input_data_org, orientation, orthog_vector)

    elif laue_group == '6/m':
        # 'C6'
        # One directions, theta=60deg
        zval = math.tan(math.pi / 6)
        orientation = tuple(orthog_component_cr + zval * symm_axis_cr)
        orthog_vector = tuple(np.cross(orientation, symm_axis_cr))
        add_crystal_orientation(input_data_org, orientation, orthog_vector)

    elif laue_group == '6/mmm':
        # '622'
        # theta=70, 15 deg from the plane of a twofold axis
        # NB X* is on a twofold axis
        # As close as possible to omega axis
        mult, remainder = nearest_modulo(omega_projection_angle, math.pi / 3)
        aa = mult * math.pi / 3 + math.copysign(math.pi / 12, remainder)
        orientation = (math.cos(aa), math.sin(aa), math.tan(math.pi / 9))
        orthog_vector = tuple(np.cross(orientation, cr_symm_axis))
        add_crystal_orientation(input_data_org, orientation, orthog_vector)

    elif laue_group == 'm-3':
        # '23'
        # CA. 1, 0.3, 0.3 (on diagonal cross of a face)
        orientation = (1, 0.3, 0.3)
        # Put in same quadrant as rotation axis
        orientation = tuple(
            math.copysign(orientation[ii], scan_axis_cr[ii])
            for ii in range(3))
        orthog_vector = tuple(np.cross(orientation, cr_symm_axis))
        add_crystal_orientation(input_data_org, orientation, orthog_vector)

    elif laue_group == 'm-3m':
        # '432'
        # Ca, 1.0, 0.2, 0.4
        orientation = (1, 0.2, 0.4)
        # Put in same quadrant as rotation axis
        orientation = tuple(
            math.copysign(orientation[ii], scan_axis_cr[ii])
            for ii in range(3))
        orthog_vector = tuple(np.cross(orientation, cr_symm_axis))
        add_crystal_orientation(input_data_org, orientation, orthog_vector)

    else:
        # sg_name must be empty string
        raise ValueError("Illegal value (empty string) for SG_NAME")

    # Add orient list count
    orient_list = input_data_org['orient_list']
    n_orients = len(orient_list)
    input_data_org['setup_list']['n_orients'] = n_orients

    # Move aside old input and save new input in input_data_org['orient_list'])ts place
    tt = os.path.splitext(fp_in)
    fp_in_backup = '_wrap'.join(tt)
    os.rename(fp_in, fp_in_backup)
    f90nml.write(input_data_org, fp_in)

    # Run stratcal
    running_process = run_stratcal(logfile=logfile, **options)
    running_process.wait()

    # NBNB TODO rewrite .out file to get correct ids etc.
    outfile = options['output']
    stem, junk = os.path.splitext(outfile)
    for ext in ('.out.def', '.out2.def'):
        fp_out = stem + ext
        if os.path.exists(fp_out):
            fp_bak = '_raw'.join((stem, ext))
            # out_data = f90nml.read(fp_out)
            # Temporary, while reading .out instead of .out.def
            out_data = f90nml.read(fp_out[:-4])
            out_data = stratcal_merge_output_2(input_data_exch, out_data)
            # out_data = f90nml.read(fp_out)
            # out_data = stratcal_merge_output(input_data_exch, out_data)
            os.rename(fp_out, fp_bak)
            f90nml.write(out_data, fp_out)

    return running_process.returncode