Calculates 'magnetic deformation proxy',
    a measure of deformation (norm of finite strain)
    between 'non-magnetic' (non-spin-polarized) and
    ferromagnetic structures.

    Adapted from Bocarsly et al. 2017,
    doi: 10.1021/acs.chemmater.6b04729

    :param structure_A: Structure
    :param structure_B: Structure
    :return:

def magnetic_deformation(structure_A, structure_B):
    """
    Calculates 'magnetic deformation proxy',
    a measure of deformation (norm of finite strain)
    between 'non-magnetic' (non-spin-polarized) and
    ferromagnetic structures.

    Adapted from Bocarsly et al. 2017,
    doi: 10.1021/acs.chemmater.6b04729

    :param structure_A: Structure
    :param structure_B: Structure
    :return:
    """

    # retrieve orderings of both input structures
    ordering_a = CollinearMagneticStructureAnalyzer(
        structure_A, overwrite_magmom_mode="none"
    ).ordering
    ordering_b = CollinearMagneticStructureAnalyzer(
        structure_B, overwrite_magmom_mode="none"
    ).ordering

    # get a type string, this is either 'NM-FM' for between non-magnetic
    # and ferromagnetic, as in Bocarsly paper, or e.g. 'FM-AFM'
    type_str = "{}-{}".format(ordering_a.value, ordering_b.value)

    lattice_a = structure_A.lattice.matrix.T
    lattice_b = structure_B.lattice.matrix.T
    lattice_a_inv = np.linalg.inv(lattice_a)
    p = np.dot(lattice_a_inv, lattice_b)
    eta = 0.5 * (np.dot(p.T, p) - np.identity(3))
    w, v = np.linalg.eig(eta)
    deformation = 100 * (1.0 / 3.0) * np.sqrt(w[0] ** 2 + w[1] ** 2 + w[2] ** 2)

    MagneticDeformation = namedtuple("MagneticDeformation", "type deformation")

    return MagneticDeformation(deformation=deformation, type=type_str)