Example #1
0
class DeformStructureTransformation(AbstractTransformation):
    """
    This transformation deforms a structure by a deformation gradient matrix

    Args:
        deformation (array): deformation gradient for the transformation
    """

    def __init__(self, deformation):

        self.deformation = Deformation(deformation)

    def apply_transformation(self, structure):
        return self.deformation.apply_to_structure(structure)

    def __str__(self):
        return "DeformStructureTransformation : " + \
            "Deformation = {}".format(str(self.deformation.tolist()))

    def __repr__(self):
        return self.__str__()

    @property
    def inverse(self):
        return DeformStructureTransformation(self.deformation.inv())

    @property
    def is_one_to_many(self):
        return False

    def as_dict(self):
        return {"name": self.__class__.__name__, "version": __version__,
                "init_args": {"deformation": self.deformation.tolist()},
                "@module": self.__class__.__module__,
                "@class": self.__class__.__name__}
class DeformStructureTransformation(AbstractTransformation):
    """
    This transformation deforms a structure by a deformation gradient matrix

    Args:
        deformation (array): deformation gradient for the transformation
    """

    def __init__(self, deformation=((1, 0, 0), (0, 1, 0), (0, 0, 1))):
        self._deform = Deformation(deformation)
        self.deformation = self._deform.tolist()

    def apply_transformation(self, structure):
        return self._deform.apply_to_structure(structure)

    def __str__(self):
        return "DeformStructureTransformation : " + \
            "Deformation = {}".format(str(self.deformation))

    def __repr__(self):
        return self.__str__()

    @property
    def inverse(self):
        return DeformStructureTransformation(self._deform.inv())

    @property
    def is_one_to_many(self):
        return False
Example #3
0
class DeformStructureTransformation(AbstractTransformation):
    """
    This transformation deforms a structure by a deformation gradient matrix

    Args:
        deformation (array): deformation gradient for the transformation
    """
    def __init__(self, deformation=((1, 0, 0), (0, 1, 0), (0, 0, 1))):
        self._deform = Deformation(deformation)
        self.deformation = self._deform.tolist()

    def apply_transformation(self, structure):
        return self._deform.apply_to_structure(structure)

    def __str__(self):
        return "DeformStructureTransformation : " + \
            "Deformation = {}".format(str(self.deformation))

    def __repr__(self):
        return self.__str__()

    @property
    def inverse(self):
        return DeformStructureTransformation(self._deform.inv())

    @property
    def is_one_to_many(self):
        return False
Example #4
0
 def __init__(self, deformation=((1, 0, 0), (0, 1, 0), (0, 0, 1))):
     """
     Args:
         deformation (array): deformation gradient for the transformation
     """
     self._deform = Deformation(deformation)
     self.deformation = self._deform.tolist()
Example #5
0
class DeformationTest(PymatgenTest):
    def setUp(self):
        self.norm_defo = Deformation.from_index_amount((0, 0), 0.02)
        self.ind_defo = Deformation.from_index_amount((0, 1), 0.02)
        self.non_ind_defo = Deformation([[1.0, 0.02, 0.02], [0.0, 1.0, 0.0],
                                         [0.0, 0.0, 1.0]])
        lattice = Lattice([[3.8401979337, 0.00, 0.00],
                           [1.9200989668, 3.3257101909, 0.00],
                           [0.00, -2.2171384943, 3.1355090603]])
        self.structure = Structure(lattice, ["Si", "Si"],
                                   [[0, 0, 0], [0.75, 0.5, 0.75]])

    def test_properties(self):
        # green_lagrange_strain
        self.assertArrayAlmostEqual(
            self.ind_defo.green_lagrange_strain,
            [[0., 0.01, 0.], [0.01, 0.0002, 0.], [0., 0., 0.]])
        self.assertArrayAlmostEqual(
            self.non_ind_defo.green_lagrange_strain,
            [[0., 0.01, 0.01], [0.01, 0.0002, 0.0002], [0.01, 0.0002, 0.0002]])

    def test_independence(self):
        self.assertFalse(self.non_ind_defo.is_independent())
        self.assertEqual(self.ind_defo.get_perturbed_indices()[0], (0, 1))

    def test_apply_to_structure(self):
        strained_norm = self.norm_defo.apply_to_structure(self.structure)
        strained_ind = self.ind_defo.apply_to_structure(self.structure)
        strained_non = self.non_ind_defo.apply_to_structure(self.structure)
        # Check lattices
        self.assertArrayAlmostEqual(
            strained_norm.lattice.matrix,
            [[3.9170018886, 0, 0], [1.958500946136, 3.32571019, 0],
             [0, -2.21713849, 3.13550906]])
        self.assertArrayAlmostEqual(
            strained_ind.lattice.matrix,
            [[3.84019793, 0, 0], [1.9866132, 3.32571019, 0],
             [-0.04434277, -2.21713849, 3.13550906]])
        self.assertArrayAlmostEqual(
            strained_non.lattice.matrix,
            [[3.84019793, 0, 0], [1.9866132, 3.3257102, 0],
             [0.0183674, -2.21713849, 3.13550906]])
        # Check coordinates
        self.assertArrayAlmostEqual(strained_norm.sites[1].coords,
                                    [3.91700189, 1.224e-06, 2.3516318])
        self.assertArrayAlmostEqual(strained_ind.sites[1].coords,
                                    [3.84019793, 1.224e-6, 2.3516318])
        self.assertArrayAlmostEqual(strained_non.sites[1].coords,
                                    [3.8872306, 1.224e-6, 2.3516318])

        # Check convention for applying transformation
        for vec, defo_vec in zip(self.structure.lattice.matrix,
                                 strained_non.lattice.matrix):
            new_vec = np.dot(self.non_ind_defo, np.transpose(vec))
            self.assertArrayAlmostEqual(new_vec, defo_vec)
        for coord, defo_coord in zip(self.structure.cart_coords,
                                     strained_non.cart_coords):
            new_coord = np.dot(self.non_ind_defo, np.transpose(coord))
            self.assertArrayAlmostEqual(new_coord, defo_coord)
Example #6
0
 def setUp(self):
     self.norm_defo = Deformation.from_index_amount((0, 0), 0.02)
     self.ind_defo = Deformation.from_index_amount((0, 1), 0.02)
     self.non_ind_defo = Deformation([[1.0, 0.02, 0.02], [0.0, 1.0, 0.0], [0.0, 0.0, 1.0]])
     lattice = Lattice(
         [[3.8401979337, 0.00, 0.00], [1.9200989668, 3.3257101909, 0.00], [0.00, -2.2171384943, 3.1355090603]]
     )
     self.structure = Structure(lattice, ["Si", "Si"], [[0, 0, 0], [0.75, 0.5, 0.75]])
Example #7
0
 def setUp(self):
     self.norm_defo = Deformation.from_index_amount((0, 0), 0.02)
     self.ind_defo = Deformation.from_index_amount((0, 1), 0.02)
     self.non_ind_defo = Deformation([[1.0, 0.02, 0.02], [0.0, 1.0, 0.0],
                                      [0.0, 0.0, 1.0]])
     lattice = Lattice([[3.8401979337, 0.00, 0.00],
                        [1.9200989668, 3.3257101909, 0.00],
                        [0.00, -2.2171384943, 3.1355090603]])
     self.structure = Structure(lattice, ["Si", "Si"],
                                [[0, 0, 0], [0.75, 0.5, 0.75]])
Example #8
0
class DeformationTest(PymatgenTest):
    def setUp(self):
        self.norm_defo = Deformation.from_index_amount((0, 0), 0.02)
        self.ind_defo = Deformation.from_index_amount((0, 1), 0.02)
        self.non_ind_defo = Deformation([[1.0, 0.02, 0.02],
                                         [0.0, 1.0, 0.0],
                                         [0.0, 0.0, 1.0]])
        lattice = Lattice([[3.8401979337, 0.00, 0.00],
                           [1.9200989668, 3.3257101909, 0.00],
                           [0.00, -2.2171384943, 3.1355090603]])
        self.structure = Structure(lattice, ["Si", "Si"], [[0, 0, 0],
                                                           [0.75, 0.5, 0.75]])

    def test_properties(self):
        # green_lagrange_strain
        self.assertArrayAlmostEqual(self.ind_defo.green_lagrange_strain,
                                    [[0., 0.01, 0.],
                                     [0.01, 0.0002, 0.],
                                     [0., 0., 0.]])
        self.assertArrayAlmostEqual(self.non_ind_defo.green_lagrange_strain,
                                    [[0., 0.01, 0.01],
                                     [0.01, 0.0002, 0.0002],
                                     [0.01, 0.0002, 0.0002]])

    def test_independence(self):
        self.assertFalse(self.non_ind_defo.is_independent())
        self.assertEqual(self.ind_defo.get_perturbed_indices()[0], (0, 1))

    def test_apply_to_structure(self):
        strained_norm = self.norm_defo.apply_to_structure(self.structure)
        strained_ind = self.ind_defo.apply_to_structure(self.structure)
        strained_non = self.non_ind_defo.apply_to_structure(self.structure)
        # Check lattices
        self.assertArrayAlmostEqual(strained_norm.lattice.matrix,
                                    [[3.9170018886, 0, 0],
                                     [1.958500946136, 3.32571019, 0],
                                     [0, -2.21713849, 3.13550906]])
        self.assertArrayAlmostEqual(strained_ind.lattice.matrix,
                                    [[3.84019793, 0.07680396, 0],
                                     [1.92009897, 3.36411217, 0],
                                     [0, -2.21713849, 3.13550906]])
        self.assertArrayAlmostEqual(strained_non.lattice.matrix,
                                    [[3.84019793, 0.07680396, 0.07680396],
                                     [1.92009897, 3.36411217, 0.0384019794],
                                     [0, -2.21713849, 3.13550906]])
        # Check coordinates
        self.assertArrayAlmostEqual(strained_norm.sites[1].coords,
                                    [3.91700189, 1.224e-06, 2.3516318])
        self.assertArrayAlmostEqual(strained_ind.sites[1].coords,
                                    [3.84019793, 0.07680518, 2.3516318])
        self.assertArrayAlmostEqual(strained_non.sites[1].coords,
                                    [3.84019793, 0.07680518, 2.42843575])
Example #9
0
 def apply(self, structure, strength_multiplier=1.):
     deformation = Deformation(
         np.eye(3) + strength_multiplier * self.deformation_matrix)
     new_structure = deformation.apply_to_structure(structure)
     # move positions
     if self.pos_displacement_matrices is not None:
         for idx, mat in self.pos_displacement_matrices:
             new_structure.translate_sites(
                 indices=[idx],
                 # use original cartesian positions
                 vector=strength_multiplier *
                 np.dot(mat, structure.cart_coords[idx]),
                 frac_coords=False)
     return new_structure
Example #10
0
class DeformationTest(PymatgenTest):
    def setUp(self):
        self.norm_defo = Deformation.from_index_amount((0, 0), 0.02)
        self.ind_defo = Deformation.from_index_amount((0, 1), 0.02)
        self.non_ind_defo = Deformation([[1.0, 0.02, 0.02], [0.0, 1.0, 0.0],
                                         [0.0, 0.0, 1.0]])
        lattice = Lattice([[3.8401979337, 0.00, 0.00],
                           [1.9200989668, 3.3257101909, 0.00],
                           [0.00, -2.2171384943, 3.1355090603]])
        self.structure = Structure(lattice, ["Si", "Si"],
                                   [[0, 0, 0], [0.75, 0.5, 0.75]])

    def test_properties(self):
        # green_lagrange_strain
        self.assertArrayAlmostEqual(
            self.ind_defo.green_lagrange_strain,
            [[0., 0.01, 0.], [0.01, 0.0002, 0.], [0., 0., 0.]])
        self.assertArrayAlmostEqual(
            self.non_ind_defo.green_lagrange_strain,
            [[0., 0.01, 0.01], [0.01, 0.0002, 0.0002], [0.01, 0.0002, 0.0002]])

    def test_independence(self):
        self.assertFalse(self.non_ind_defo.is_independent())
        self.assertEqual(self.ind_defo.get_perturbed_indices()[0], (0, 1))

    def test_apply_to_structure(self):
        strained_norm = self.norm_defo.apply_to_structure(self.structure)
        strained_ind = self.ind_defo.apply_to_structure(self.structure)
        strained_non = self.non_ind_defo.apply_to_structure(self.structure)
        # Check lattices
        self.assertArrayAlmostEqual(
            strained_norm.lattice.matrix,
            [[3.9170018886, 0, 0], [1.958500946136, 3.32571019, 0],
             [0, -2.21713849, 3.13550906]])
        self.assertArrayAlmostEqual(
            strained_ind.lattice.matrix,
            [[3.84019793, 0.07680396, 0], [1.92009897, 3.36411217, 0],
             [0, -2.21713849, 3.13550906]])
        self.assertArrayAlmostEqual(strained_non.lattice.matrix,
                                    [[3.84019793, 0.07680396, 0.07680396],
                                     [1.92009897, 3.36411217, 0.0384019794],
                                     [0, -2.21713849, 3.13550906]])
        # Check coordinates
        self.assertArrayAlmostEqual(strained_norm.sites[1].coords,
                                    [3.91700189, 1.224e-06, 2.3516318])
        self.assertArrayAlmostEqual(strained_ind.sites[1].coords,
                                    [3.84019793, 0.07680518, 2.3516318])
        self.assertArrayAlmostEqual(strained_non.sites[1].coords,
                                    [3.84019793, 0.07680518, 2.42843575])
Example #11
0
    def test_energy_density(self):

        film_elac = ElasticTensor.from_voigt(
            [[324.32, 187.3, 170.92, 0., 0., 0.],
             [187.3, 324.32, 170.92, 0., 0., 0.],
             [170.92, 170.92, 408.41, 0., 0., 0.],
             [0., 0., 0., 150.73, 0., 0.], [0., 0., 0., 0., 150.73, 0.],
             [0., 0., 0., 0., 0., 238.74]])

        dfm = Deformation([[-9.86004855e-01, 2.27539582e-01, -4.64426035e-17],
                           [-2.47802121e-01, -9.91208483e-01, -7.58675185e-17],
                           [-6.12323400e-17, -6.12323400e-17, 1.00000000e+00]])

        self.assertAlmostEqual(
            film_elac.energy_density(dfm.green_lagrange_strain),
            0.00125664672793)

        film_elac.energy_density(
            Strain.from_deformation([[0.99774738, 0.11520994, -0.],
                                     [-0.11520994, 0.99774738, 0.],
                                     [
                                         -0.,
                                         -0.,
                                         1.,
                                     ]]))
Example #12
0
 def test_properties(self):
     # mean_stress
     self.assertEqual(
         self.rand_stress.mean_stress,
         1. / 3. * (self.rand_stress[0, 0] + self.rand_stress[1, 1] +
                    self.rand_stress[2, 2]))
     self.assertAlmostEqual(self.symm_stress.mean_stress, 3.66)
     # deviator_stress
     self.assertArrayAlmostEqual(
         self.symm_stress.deviator_stress,
         Stress([[-3.15, 2.29, 2.42], [2.29, 1.48, 5.07],
                 [2.42, 5.07, 1.67]]))
     self.assertArrayAlmostEqual(
         self.non_symm.deviator_stress,
         [[-0.2666666667, 0.2, 0.3], [0.4, 0.133333333, 0.6],
          [0.2, 0.5, 0.133333333]])
     # deviator_principal_invariants
     self.assertArrayAlmostEqual(self.symm_stress.dev_principal_invariants,
                                 [0, 44.2563, 111.953628])
     # von_mises
     self.assertAlmostEqual(self.symm_stress.von_mises, 11.52253878275)
     # piola_kirchoff 1, 2
     f = Deformation.from_index_amount((0, 1), 0.03)
     self.assertArrayAlmostEqual(
         self.symm_stress.piola_kirchoff_1(f),
         [[0.4413, 2.29, 2.42], [2.1358, 5.14, 5.07], [2.2679, 5.07, 5.33]])
     self.assertArrayAlmostEqual(
         self.symm_stress.piola_kirchoff_2(f),
         [[0.377226, 2.1358, 2.2679], [2.1358, 5.14, 5.07],
          [2.2679, 5.07, 5.33]])
     # voigt
     self.assertArrayEqual(self.symm_stress.voigt,
                           [0.51, 5.14, 5.33, 5.07, 2.42, 2.29])
     with self.assertRaises(ValueError):
         self.non_symm.voigt
Example #13
0
    def calculate_3D_elastic_energy(self, film, match, elasticity_tensor=None):
        """
        Calculates the multi-plane elastic energy. Returns 999 if no elastic
        tensor was given on init

        """
        if elasticity_tensor is None:
            return 9999

        # Generate 3D lattice vectors for film super lattice
        film_matrix = list(match['film_sl_vecs'])
        film_matrix.append(np.cross(film_matrix[0], film_matrix[1]))

        # Generate 3D lattice vectors for substrate super lattice
        # Out of place substrate super lattice has to be same length as
        # Film out of plane vector to ensure no extra deformation in that
        # direction
        substrate_matrix = list(match['sub_sl_vecs'])
        temp_sub = np.cross(substrate_matrix[0], substrate_matrix[1])
        temp_sub = temp_sub * fast_norm(film_matrix[2]) / fast_norm(temp_sub)
        substrate_matrix.append(temp_sub)

        transform_matrix = np.transpose(
            np.linalg.solve(film_matrix, substrate_matrix))

        dfm = Deformation(transform_matrix)

        energy_density = elasticity_tensor.energy_density(
            dfm.green_lagrange_strain)

        return film.volume * energy_density / len(film.sites)
Example #14
0
 def test_properties(self):
     # mean_stress
     self.assertEqual(self.rand_stress.mean_stress,
                      1. / 3. * (self.rand_stress[0, 0] +
                                 self.rand_stress[1, 1] +
                                 self.rand_stress[2, 2]))
     self.assertAlmostEqual(self.symm_stress.mean_stress, 3.66)
     # deviator_stress
     self.assertArrayAlmostEqual(self.symm_stress.deviator_stress,
                                 Stress([[-3.15, 2.29, 2.42],
                                         [2.29, 1.48, 5.07],
                                         [2.42, 5.07, 1.67]]))
     self.assertArrayAlmostEqual(self.non_symm.deviator_stress,
                                 [[-0.2666666667, 0.2, 0.3],
                                  [0.4, 0.133333333, 0.6],
                                  [0.2, 0.5, 0.133333333]])
     # deviator_principal_invariants
     self.assertArrayAlmostEqual(self.symm_stress.dev_principal_invariants,
                                 [0, 44.2563, 111.953628])
     # von_mises
     self.assertAlmostEqual(self.symm_stress.von_mises,
                            11.52253878275)
     # piola_kirchoff 1, 2
     f = Deformation.from_index_amount((0, 1), 0.03)
     self.assertArrayAlmostEqual(self.symm_stress.piola_kirchoff_1(f),
                                 [[0.4413, 2.29, 2.42],
                                  [2.1358, 5.14, 5.07],
                                  [2.2679, 5.07, 5.33]])
     self.assertArrayAlmostEqual(self.symm_stress.piola_kirchoff_2(f),
                                 [[0.377226, 2.1358, 2.2679],
                                  [2.1358, 5.14, 5.07],
                                  [2.2679, 5.07, 5.33]])
     # voigt
     self.assertArrayEqual(self.symm_stress.voigt,
                           [0.51, 5.14, 5.33, 5.07, 2.42, 2.29])
Example #15
0
def get_wf_bulk_modulus(structure,
                        deformations,
                        vasp_input_set=None,
                        vasp_cmd="vasp",
                        db_file=None,
                        user_kpoints_settings=None,
                        eos="vinet",
                        tag=None,
                        user_incar_settings=None):
    """
    Returns the workflow that computes the bulk modulus by fitting to the given equation of state.

    Args:
        structure (Structure): input structure.
        deformations (list): list of deformation matrices(list of lists).
        vasp_input_set (VaspInputSet): for the static deformation calculations
        vasp_cmd (str): vasp command to run.
        db_file (str): path to the db file.
        user_kpoints_settings (dict): example: {"grid_density": 7000}
        eos (str): equation of state used for fitting the energies and the volumes.
            supported equation of states: "quadratic", "murnaghan", "birch", "birch_murnaghan",
            "pourier_tarantola", "vinet", "deltafactor". See pymatgen.analysis.eos.py
        tag (str): something unique to identify the tasks in this workflow. If None a random uuid
            will be assigned.
        user_incar_settings (dict):

    Returns:
        Workflow
    """

    tag = tag or "bulk_modulus group: >>{}<<".format(str(uuid4()))

    deformations = [Deformation(defo_mat) for defo_mat in deformations]

    vis_static = vasp_input_set or MPStaticSet(
        structure=structure,
        force_gamma=True,
        lepsilon=False,
        user_kpoints_settings=user_kpoints_settings,
        user_incar_settings=user_incar_settings)

    wf_bulk_modulus = get_wf_deformations(structure,
                                          deformations,
                                          name="bulk_modulus deformation",
                                          vasp_input_set=vis_static,
                                          vasp_cmd=vasp_cmd,
                                          db_file=db_file,
                                          tag=tag)

    fw_analysis = Firework(FitEOSToDb(tag=tag, db_file=db_file, eos=eos),
                           name="fit equation of state")

    wf_bulk_modulus.append_wf(Workflow.from_Firework(fw_analysis),
                              wf_bulk_modulus.leaf_fw_ids)

    wf_bulk_modulus.name = "{}:{}".format(
        structure.composition.reduced_formula, "Bulk modulus")

    return wf_bulk_modulus
Example #16
0
def get_strain_mapping(bulk_structure, deformation_calculations):
    strain_mapping = TensorMapping(tol=_mapping_tol)
    for i, calc in enumerate(deformation_calculations):
        deformed_structure = calc["bandstructure"].structure
        matrix = calculate_deformation(bulk_structure, deformed_structure)
        strain = Deformation(matrix).green_lagrange_strain
        strain_mapping[strain] = calc
    return strain_mapping
Example #17
0
def get_wf_thermal_expansion(structure, deformations, vasp_input_set=None, vasp_cmd="vasp",
                             db_file=None, user_kpoints_settings=None, t_step=10, t_min=0,
                             t_max=1000, mesh=(20, 20, 20), eos="vinet", pressure=0.0,
                             copy_vasp_outputs=False,
                             tag=None):
    """
    Returns quasi-harmonic thermal expansion workflow.
    Note: phonopy package is required for the final analysis step.

    Args:
        structure (Structure): input structure.
        deformations (list): list of deformation matrices(list of lists).
        vasp_input_set (VaspInputSet)
        vasp_cmd (str): vasp command to run.
        db_file (str): path to the db file.
        user_kpoints_settings (dict): example: {"grid_density": 7000}
        t_step (float): temperature step (in K)
        t_min (float): min temperature (in K)
        t_max (float): max temperature (in K)
        mesh (list/tuple): reciprocal space density
        eos (str): equation of state used for fitting the energies and the volumes.
            options supported by phonopy: "vinet", "murnaghan", "birch_murnaghan".
            Note: pymatgen supports more options than phonopy. see pymatgen.analysis.eos.py
        copy_vasp_outputs (bool): whether or not copy the outputs from the previous calc
            (usually structure optimization) before the deformations are performed.
        pressure (float): in GPa
        tag (str): something unique to identify the tasks in this workflow. If None a random uuid
            will be assigned.

    Returns:
        Workflow
    """
    try:
        from phonopy import Phonopy
    except ImportError:
        logger.warning("'phonopy' package NOT installed. Required for the final analysis step.")

    tag = tag or "thermal_expansion group: >>{}<<".format(str(uuid4()))

    deformations = [Deformation(defo_mat) for defo_mat in deformations]

    vis_static = vasp_input_set or MPStaticSet(structure, force_gamma=True, lepsilon=True,
                                               user_kpoints_settings=user_kpoints_settings)
    wf_alpha = get_wf_deformations(structure, deformations, name="thermal_expansion deformation",
                                   vasp_cmd=vasp_cmd, db_file=db_file, tag=tag,
                                   copy_vasp_outputs=copy_vasp_outputs,
                                   vasp_input_set=vis_static)

    fw_analysis = Firework(ThermalExpansionCoeffToDb(tag=tag, db_file=db_file, t_step=t_step,
                                                     t_min=t_min, t_max=t_max, mesh=mesh, eos=eos,
                                                     pressure=pressure),
                           name="Thermal expansion")

    wf_alpha.append_wf(Workflow.from_Firework(fw_analysis), wf_alpha.leaf_fw_ids)

    wf_alpha.name = "{}:{}".format(structure.composition.reduced_formula, "thermal expansion")

    return wf_alpha
Example #18
0
class DeformStructureTransformation(AbstractTransformation):
    """
    This transformation deforms a structure by a deformation gradient matrix
    """

    def __init__(self, deformation=((1, 0, 0), (0, 1, 0), (0, 0, 1))):
        """
        Args:
            deformation (array): deformation gradient for the transformation
        """
        self._deform = Deformation(deformation)
        self.deformation = self._deform.tolist()

    def apply_transformation(self, structure):
        """
        Apply the transformation.

        Args:
            structure (Structure): Input Structure

        Returns:
            Deformed Structure.
        """
        return self._deform.apply_to_structure(structure)

    def __str__(self):
        return f"DeformStructureTransformation : Deformation = {self.deformation}"

    def __repr__(self):
        return self.__str__()

    @property
    def inverse(self):
        """
        Returns:
            Inverse Transformation.
        """
        return DeformStructureTransformation(self._deform.inv)

    @property
    def is_one_to_many(self):
        """
        Returns: False
        """
        return False
Example #19
0
    def calculate_3D_elastic_energy(self,
                                    film,
                                    match,
                                    elasticity_tensor=None,
                                    include_strain=False):
        """
        Calculates the multi-plane elastic energy. Returns 999 if no elastic
        tensor was given on init

        Args:
            film(Structure): conventional standard structure for the film
            match(dictionary) : match dictionary from substrate analyzer
            elasticity_tensor(ElasticTensor): elasticity tensor for the film
            include_strain(bool): include strain in the output or not; changes
             return from just the energy to a tuple with the energy and strain
             in voigt notation
        """
        if elasticity_tensor is None:
            return 9999

        # Get the appropriate surface structure
        struc = SlabGenerator(self.film,
                              match["film_miller"],
                              20,
                              15,
                              primitive=False).get_slab().oriented_unit_cell

        # Generate 3D lattice vectors for film super lattice
        film_matrix = list(match["film_sl_vecs"])
        film_matrix.append(np.cross(film_matrix[0], film_matrix[1]))

        # Generate 3D lattice vectors for substrate super lattice
        # Out of plane substrate super lattice has to be same length as
        # Film out of plane vector to ensure no extra deformation in that
        # direction
        substrate_matrix = list(match["sub_sl_vecs"])
        temp_sub = np.cross(substrate_matrix[0], substrate_matrix[1])
        temp_sub = temp_sub * fast_norm(film_matrix[2]) / fast_norm(temp_sub)
        substrate_matrix.append(temp_sub)

        transform_matrix = np.transpose(
            np.linalg.solve(film_matrix, substrate_matrix))

        dfm = Deformation(transform_matrix)

        strain = dfm.green_lagrange_strain.convert_to_ieee(struc,
                                                           initial_fit=False)

        energy_density = elasticity_tensor.energy_density(strain)

        if include_strain:
            return (
                film.volume * energy_density / len(film.sites),
                strain.von_mises_strain,
            )
        return film.volume * energy_density / len(film.sites)
Example #20
0
def get_wf_bulk_modulus(structure,
                        vasp_input_set=None,
                        vasp_cmd="vasp",
                        deformations=None,
                        db_file=None,
                        user_kpoints_settings=None,
                        eos="vinet"):
    """
    Returns the workflow that computes the bulk modulus by fitting to the given equation of state.

    Args:
        structure (Structure): input structure.
        vasp_input_set (VaspInputSet)
        vasp_cmd (str): vasp command to run.
        deformations (list): list of deformation matrices(list of lists).
        db_file (str): path to the db file.
        user_kpoints_settings (dict): example: {"grid_density": 7000}
        eos (str): equation of state used for fitting the energies and the volumes.
            supported equation of states: "quadratic", "murnaghan", "birch", "birch_murnaghan",
            "pourier_tarantola", "vinet", "deltafactor". See pymatgen.analysis.eos.py

    Returns:
        Workflow
    """

    tag = datetime.utcnow().strftime('%Y-%m-%d-%H-%M-%S-%f')

    deformations = [Deformation(defo_mat) for defo_mat in deformations]
    wf_bulk_modulus = get_wf_deformations(
        structure,
        deformations,
        name="bulk_modulus deformation",
        vasp_input_set=vasp_input_set,
        lepsilon=False,
        vasp_cmd=vasp_cmd,
        db_file=db_file,
        user_kpoints_settings=user_kpoints_settings,
        tag=tag)

    fw_analysis = Firework(FitEquationOfStateTask(tag=tag,
                                                  db_file=db_file,
                                                  eos=eos),
                           name="fit equation of state")

    append_fw_wf(wf_bulk_modulus, fw_analysis)

    wf_bulk_modulus.name = "{}:{}".format(
        structure.composition.reduced_formula, "Bulk modulus")

    return wf_bulk_modulus
Example #21
0
    def from_zsl(
        cls,
        match: ZSLMatch,
        film: Structure,
        film_miller,
        substrate_miller,
        elasticity_tensor=None,
        ground_state_energy=0,
    ):
        """Generate a substrate match from a ZSL match plus metadata"""

        # Get the appropriate surface structure
        struc = SlabGenerator(film, film_miller, 20, 15,
                              primitive=False).get_slab().oriented_unit_cell

        dfm = Deformation(match.match_transformation)

        strain = dfm.green_lagrange_strain.convert_to_ieee(struc,
                                                           initial_fit=False)
        von_mises_strain = strain.von_mises_strain

        if elasticity_tensor is not None:
            energy_density = elasticity_tensor.energy_density(strain)

            elastic_energy = film.volume * energy_density / len(film.sites)
        else:
            elastic_energy = 0

        return cls(
            film_miller=film_miller,
            substrate_miller=substrate_miller,
            strain=strain,
            von_mises_strain=von_mises_strain,
            elastic_energy=elastic_energy,
            ground_state_energy=ground_state_energy,
            **{
                k: getattr(match, k)
                for k in [
                    "film_sl_vectors",
                    "substrate_sl_vectors",
                    "film_vectors",
                    "substrate_vectors",
                    "film_transformation",
                    "substrate_transformation",
                ]
            },
        )
Example #22
0
def get_wf_gibbs_free_energy(structure, deformations, vasp_input_set=None, vasp_cmd="vasp",
                             db_file=None, user_kpoints_settings=None, t_step=10, t_min=0,
                             t_max=1000, mesh=(20, 20, 20), eos="vinet", qha_type="debye_model",
                             pressure=0.0, poisson=0.25, anharmonic_contribution=False,
                             metadata=None, tag=None):
    """
    Returns quasi-harmonic gibbs free energy workflow.
    Note: phonopy package is required for the final analysis step if qha_type="phonopy"

    Args:
        structure (Structure): input structure.
        deformations (list): list of deformation matrices(list of lists).
        vasp_input_set (VaspInputSet)
        vasp_cmd (str): vasp command to run.
        db_file (str): path to the db file.
        user_kpoints_settings (dict): example: {"grid_density": 7000}
        t_step (float): temperature step (in K)
        t_min (float): min temperature (in K)
        t_max (float): max temperature (in K)
        mesh (list/tuple): reciprocal space density
        eos (str): equation of state used for fitting the energies and the volumes.
            options supported by phonopy: "vinet", "murnaghan", "birch_murnaghan".
            Note: pymatgen supports more options than phonopy. see pymatgen.analysis.eos.py
        qha_type(str): quasi-harmonic approximation type: "debye_model" or "phonopy",
            default is "debye_model"
        pressure (float): in GPa
        poisson (float): poisson ratio
        anharmonic_contribution (bool): consider anharmonic contributions to
            Gibbs energy from the Debye model. Defaults to False.
        metadata (dict): meta data
        tag (str): something unique to identify the tasks in this workflow. If None a random uuid
            will be assigned.

    Returns:
        Workflow
    """

    tag = tag or "gibbs group: >>{}<<".format(str(uuid4()))

    deformations = [Deformation(defo_mat) for defo_mat in deformations]

    # static input set for the transmuter fireworks
    vis_static = vasp_input_set
    if vis_static is None:
        lepsilon = False
        if qha_type not in ["debye_model"]:
            lepsilon = True
            try:
                from phonopy import Phonopy
            except ImportError:
                raise RuntimeError("'phonopy' package is NOT installed but is required for the final "
                                   "analysis step; you can alternatively switch to the qha_type to "
                                   "'debye_model' which does not require 'phonopy'.")
        vis_static = MPStaticSet(structure, force_gamma=True, lepsilon=lepsilon,
                                 user_kpoints_settings=user_kpoints_settings)

    wf_gibbs = get_wf_deformations(structure, deformations, name="gibbs deformation",
                                   vasp_cmd=vasp_cmd, db_file=db_file, tag=tag, metadata=metadata,
                                   vasp_input_set=vis_static)

    fw_analysis = Firework(GibbsAnalysisToDb(tag=tag, db_file=db_file, t_step=t_step, t_min=t_min,
                                             t_max=t_max, mesh=mesh, eos=eos, qha_type=qha_type,
                                             pressure=pressure, poisson=poisson, metadata=metadata,
                                             anharmonic_contribution=anharmonic_contribution,),
                           name="Gibbs Free Energy")

    wf_gibbs.append_wf(Workflow.from_Firework(fw_analysis), wf_gibbs.leaf_fw_ids)

    wf_gibbs.name = "{}:{}".format(structure.composition.reduced_formula, "gibbs free energy")

    return wf_gibbs
Example #23
0
 def __init__(self, deformation=((1, 0, 0), (0, 1, 0), (0, 0, 1))):
     self._deform = Deformation(deformation)
     self.deformation = self._deform.tolist()
    def get_interfaces(
        self,
        termination: Tuple[str, str],
        gap: float = 2.0,
        vacuum_over_film: float = 20.0,
        film_thickness: Union[float, int] = 1,
        substrate_thickness: Union[float, int] = 1,
        in_layers: bool = True,
    ) -> Iterator[Interface]:
        """
        Generates interface structures given the film and substrate structure
        as well as the desired terminations


        Args:
            terminations: termination from self.termination list
            gap: gap between film and substrate
            vacuum_over_film: vacuum over the top of the film
            film_thickness: the film thickness
            substrate_thickness: substrate thickness
            in_layers: set the thickness in layer units
        """
        film_sg = SlabGenerator(
            self.film_structure,
            self.film_miller,
            min_slab_size=film_thickness,
            min_vacuum_size=3,
            in_unit_planes=in_layers,
            center_slab=True,
            primitive=True,
            reorient_lattice=
            False,  # This is necessary to not screw up the lattice
        )

        sub_sg = SlabGenerator(
            self.substrate_structure,
            self.substrate_miller,
            min_slab_size=substrate_thickness,
            min_vacuum_size=3,
            in_unit_planes=in_layers,
            center_slab=True,
            primitive=True,
            reorient_lattice=
            False,  # This is necessary to not screw up the lattice
        )

        film_shift, sub_shift = self._terminations[termination]

        film_slab = film_sg.get_slab(shift=film_shift)
        sub_slab = sub_sg.get_slab(shift=sub_shift)

        for match in self.zsl_matches:
            # Build film superlattice
            super_film_transform = np.round(
                from_2d_to_3d(
                    get_2d_transform(film_slab.lattice.matrix[:2],
                                     match.film_sl_vectors))).astype(int)
            film_sl_slab = film_slab.copy()
            film_sl_slab.make_supercell(super_film_transform)
            assert np.allclose(
                film_sl_slab.lattice.matrix[2], film_slab.lattice.matrix[2]
            ), "2D transformation affected C-axis for Film transformation"
            assert np.allclose(
                film_sl_slab.lattice.matrix[:2], match.film_sl_vectors
            ), "Transformation didn't make proper supercell for film"

            # Build substrate superlattice
            super_sub_transform = np.round(
                from_2d_to_3d(
                    get_2d_transform(sub_slab.lattice.matrix[:2],
                                     match.substrate_sl_vectors))).astype(int)
            sub_sl_slab = sub_slab.copy()
            sub_sl_slab.make_supercell(super_sub_transform)
            assert np.allclose(
                sub_sl_slab.lattice.matrix[2], sub_slab.lattice.matrix[2]
            ), "2D transformation affected C-axis for Film transformation"
            assert np.allclose(
                sub_sl_slab.lattice.matrix[:2], match.substrate_sl_vectors
            ), "Transformation didn't make proper supercell for substrate"

            # Add extra info
            match_dict = match.as_dict()
            interface_properties = {
                k: match_dict[k]
                for k in match_dict.keys() if not k.startswith("@")
            }

            dfm = Deformation(match.match_transformation)

            strain = dfm.green_lagrange_strain
            interface_properties["strain"] = strain
            interface_properties["von_mises_strain"] = strain.von_mises_strain
            interface_properties["termination"] = termination
            interface_properties["film_thickness"] = film_thickness
            interface_properties["substrate_thickness"] = substrate_thickness

            yield (Interface.from_slabs(
                substrate_slab=sub_sl_slab,
                film_slab=film_sl_slab,
                gap=gap,
                vacuum_over_film=vacuum_over_film,
                interface_properties=interface_properties,
            ))
Example #25
0
def get_wf_thermal_expansion(structure,
                             vasp_input_set=None,
                             vasp_cmd="vasp",
                             deformations=None,
                             db_file=None,
                             user_kpoints_settings=None,
                             t_step=10,
                             t_min=0,
                             t_max=1000,
                             mesh=(20, 20, 20),
                             eos="vinet",
                             pressure=0.0):
    """
    Returns quasi-harmonic thermal expansion workflow.
    Note: phonopy package is required for the final analysis step.

    Args:
        structure (Structure): input structure.
        vasp_input_set (VaspInputSet)
        vasp_cmd (str): vasp command to run.
        deformations (list): list of deformation matrices(list of lists).
        db_file (str): path to the db file.
        user_kpoints_settings (dict): example: {"grid_density": 7000}
        t_step (float): temperature step (in K)
        t_min (float): min temperature (in K)
        t_max (float): max temperature (in K)
        mesh (list/tuple): reciprocal space density
        eos (str): equation of state used for fitting the energies and the volumes.
            options supported by phonopy: "vinet", "murnaghan", "birch_murnaghan".
            Note: pymatgen supports more options than phonopy. see pymatgen.analysis.eos.py
        pressure (float): in GPa

    Returns:
        Workflow
    """
    try:
        from phonopy import Phonopy
    except ImportError:
        logger.warn(
            "'phonopy' package NOT installed. Required for the final analysis step."
        )

    tag = datetime.utcnow().strftime('%Y-%m-%d-%H-%M-%S-%f')

    deformations = [Deformation(defo_mat) for defo_mat in deformations]
    wf_alpha = get_wf_deformations(structure,
                                   deformations,
                                   name="thermal_expansion deformation",
                                   vasp_input_set=vasp_input_set,
                                   lepsilon=True,
                                   vasp_cmd=vasp_cmd,
                                   db_file=db_file,
                                   user_kpoints_settings=user_kpoints_settings,
                                   tag=tag)

    fw_analysis = Firework(ThermalExpansionCoeffTask(tag=tag,
                                                     db_file=db_file,
                                                     t_step=t_step,
                                                     t_min=t_min,
                                                     t_max=t_max,
                                                     mesh=mesh,
                                                     eos=eos,
                                                     pressure=pressure),
                           name="Thermal expansion")

    append_fw_wf(wf_alpha, fw_analysis)

    wf_alpha.name = "{}:{}".format(structure.composition.reduced_formula,
                                   "thermal expansion")

    return wf_alpha
Example #26
0
def get_wf_elastic_constant(structure,
                            metadata,
                            strain_states=None,
                            stencils=None,
                            db_file=None,
                            conventional=False,
                            order=2,
                            vasp_input_set=None,
                            analysis=True,
                            sym_reduce=False,
                            tag='elastic',
                            copy_vasp_outputs=False,
                            **kwargs):
    """
    Returns a workflow to calculate elastic constants.

    Firework 1 : write vasp input set for structural relaxation,
                 run vasp,
                 pass run location,
                 database insertion.

    Firework 2 - number of total deformations: Static runs on the deformed structures

    last Firework : Analyze Stress/Strain data and fit the elastic tensor

    Args:
        structure (Structure): input structure to be optimized and run.
        strain_states (list of Voigt-notation strains): list of ratios of nonzero elements
            of Voigt-notation strain, e. g. [(1, 0, 0, 0, 0, 0), (0, 1, 0, 0, 0, 0), etc.].
        stencils (list of floats, or list of list of floats): values of strain to multiply
            by for each strain state, i. e. stencil for the perturbation along the strain
            state direction, e. g. [-0.01, -0.005, 0.005, 0.01].  If a list of lists,
            stencils must correspond to each strain state provided.
        db_file (str): path to file containing the database credentials.
        conventional (bool): flag to convert input structure to conventional structure,
            defaults to False.
        order (int): order of the tensor expansion to be determined.  Defaults to 2 and
            currently supports up to 3.
        vasp_input_set (VaspInputSet): vasp input set to be used.  Defaults to static
            set with ionic relaxation parameters set.  Take care if replacing this,
            default ensures that ionic relaxation is done and that stress is calculated
            for each vasp run.
        analysis (bool): flag to indicate whether analysis task should be added
            and stresses and strains passed to that task
        sym_reduce (bool): Whether or not to apply symmetry reductions
        tag (str):
        copy_vasp_outputs (bool): whether or not to copy previous vasp outputs.
        kwargs (keyword arguments): additional kwargs to be passed to get_wf_deformations

    Returns:
        Workflow
    """
    # Convert to conventional if specified
    if conventional:
        structure = SpacegroupAnalyzer(
            structure).get_conventional_standard_structure()

    uis_elastic = {
        "IBRION": 2,
        "NSW": 99,
        "ISIF": 2,
        "ISTART": 1,
        "PREC": "High"
    }
    vis = vasp_input_set or MPStaticSet(structure,
                                        user_incar_settings=uis_elastic)

    strains = []
    if strain_states is None:
        strain_states = get_default_strain_states(order)
    if stencils is None:
        stencils = [np.linspace(-0.01, 0.01, 5 +
                                (order - 2) * 2)] * len(strain_states)
    if np.array(stencils).ndim == 1:
        stencils = [stencils] * len(strain_states)
    for state, stencil in zip(strain_states, stencils):
        strains.extend(
            [Strain.from_voigt(s * np.array(state)) for s in stencil])

    # Remove zero strains
    strains = [strain for strain in strains if not (abs(strain) < 1e-10).all()]
    # Adding the zero strains for the purpose of calculating at finite pressure or thermal expansion
    _strains = [Strain.from_deformation([[1, 0, 0], [0, 1, 0], [0, 0, 1]])]
    strains.extend(_strains)
    """
    """
    vstrains = [strain.voigt for strain in strains]
    if np.linalg.matrix_rank(vstrains) < 6:
        # TODO: check for sufficiency of input for nth order
        raise ValueError(
            "Strain list is insufficient to fit an elastic tensor")

    deformations = [s.get_deformation_matrix() for s in strains]
    """
    print(strains)
    print(deformations)
    """

    if sym_reduce:
        # Note this casts deformations to a TensorMapping
        # with unique deformations as keys to symmops
        deformations = symmetry_reduce(deformations, structure)

    wf_elastic = get_wf_deformations(structure,
                                     deformations,
                                     tag=tag,
                                     db_file=db_file,
                                     vasp_input_set=vis,
                                     copy_vasp_outputs=copy_vasp_outputs,
                                     **kwargs)
    if analysis:
        defo_fws_and_tasks = get_fws_and_tasks(
            wf_elastic,
            fw_name_constraint="deformation",
            task_name_constraint="Transmuted")
        for idx_fw, idx_t in defo_fws_and_tasks:
            defo = \
            wf_elastic.fws[idx_fw].tasks[idx_t]['transformation_params'][0][
                'deformation']
            pass_dict = {
                'strain': Deformation(defo).green_lagrange_strain.tolist(),
                'stress': '>>output.ionic_steps.-1.stress',
                'deformation_matrix': defo
            }
            if sym_reduce:
                pass_dict.update({'symmops': deformations[defo]})

            mod_spec_key = "deformation_tasks->{}".format(idx_fw)
            pass_task = pass_vasp_result(pass_dict=pass_dict,
                                         mod_spec_key=mod_spec_key)
            wf_elastic.fws[idx_fw].tasks.append(pass_task)

        fw_analysis = Firework(ElasticTensorToDb(structure=structure,
                                                 db_file=db_file,
                                                 order=order,
                                                 fw_spec_field='tags',
                                                 metadata=metadata,
                                                 vasp_input_set=vis),
                               name="Analyze Elastic Data",
                               spec={"_allow_fizzled_parents": True})
        wf_elastic.append_wf(Workflow.from_Firework(fw_analysis),
                             wf_elastic.leaf_fw_ids)

    wf_elastic.name = "{}:{}".format(structure.composition.reduced_formula,
                                     "elastic constants")

    return wf_elastic
Example #27
0
def get_elastic_analysis(opt_task, defo_tasks):
    """
    Performs the analysis of opt_tasks and defo_tasks necessary for
    an elastic analysis

    Args:
        opt_task: task doc corresponding to optimization
        defo_tasks: task_doc corresponding to deformations

    Returns:
        elastic document with fitted elastic tensor and analysis

    """
    elastic_doc = {"warnings": []}
    opt_struct = Structure.from_dict(opt_task['output']['structure'])
    d_structs = [
        Structure.from_dict(d['output']['structure']) for d in defo_tasks
    ]
    defos = [
        calculate_deformation(opt_struct, def_structure)
        for def_structure in d_structs
    ]

    # Warning if deformation is not equivalent to stored deformation
    stored_defos = [d['transmuter']['transformation_params'][0]\
                     ['deformation'] for d in defo_tasks]
    if not np.allclose(defos, stored_defos, atol=1e-5):
        wmsg = "Inequivalent stored and calc. deformations."
        logger.warning(wmsg)
        elastic_doc["warnings"].append(wmsg)

    # Collect all fitting data and task ids
    defos = [Deformation(d) for d in defos]
    strains = [d.green_lagrange_strain for d in defos]
    vasp_stresses = [d['output']['stress'] for d in defo_tasks]
    cauchy_stresses = [-0.1 * Stress(s) for s in vasp_stresses]
    pk_stresses = [
        Stress(s.piola_kirchoff_2(d)) for s, d in zip(cauchy_stresses, defos)
    ]
    defo_task_ids = [d['task_id'] for d in defo_tasks]

    # Determine whether data is sufficient to fit tensor
    # If raw data is insufficient but can be symmetrically transformed
    # to provide a sufficient set, use the expanded set with appropriate
    # symmetry transformations, fstresses/strains are "fitting
    # strains" below.
    vstrains = [s.voigt for s in strains]
    if np.linalg.matrix_rank(vstrains) < 6:
        symmops = SpacegroupAnalyzer(opt_struct).get_symmetry_operations()
        fstrains = [[s.transform(symmop) for symmop in symmops]
                    for s in strains]
        fstrains = list(chain.from_iterable(fstrains))
        vfstrains = [s.voigt for s in fstrains]
        if not np.linalg.matrix_rank(vfstrains) == 6:
            logger.warning("Insufficient data to form SOEC")
            elastic_doc['warnings'].append("insufficient strains")
            return None
        else:
            fstresses = [[s.transform(symmop) for symmop in symmops]
                         for s in pk_stresses]
            fstresses = list(chain.from_iterable(fstresses))
    else:
        fstrains = strains
        fstresses = pk_stresses

    with warnings.catch_warnings():
        warnings.simplefilter('ignore')
        # TODO: more intelligently determine if independent
        # strain fitting can be done
        if len(cauchy_stresses) == 24:
            elastic_doc['legacy_fit'] = legacy_fit(strains, cauchy_stresses)
        et_raw = ElasticTensor.from_pseudoinverse(fstrains, fstresses)
        et = et_raw.voigt_symmetrized.convert_to_ieee(opt_struct)
        defo_tasks = sorted(defo_tasks, key=lambda x: x['completed_at'])
        vasp_input = opt_task['input']
        vasp_input.pop('structure')

        elastic_doc.update({
            "deformation_task_ids": defo_task_ids,
            "optimization_task_id": opt_task['task_id'],
            "pk_stresses": pk_stresses,
            "cauchy_stresses": cauchy_stresses,
            "strains": strains,
            "deformations": defos,
            "elastic_tensor": et.voigt,
            "elastic_tensor_raw": et_raw.voigt,
            "optimized_structure": opt_struct,
            "completed_at": defo_tasks[-1]['completed_at'],
            "optimization_input": vasp_input
        })

    # Process input
    elastic_doc['warnings'] = get_warnings(et, opt_struct) or None
    #TODO: process MPWorks metadata?
    #TODO: higher order
    #TODO: add some of the relevant DFT params, kpoints
    elastic_doc['state'] = "filter_failed" if elastic_doc['warnings']\
        else "successful"
    return elastic_doc
Example #28
0
    def run_task(self, fw_spec):
        ref_struct = self['structure']
        d = {"analysis": {}, "initial_structure": self['structure'].as_dict()}

        # Get optimized structure
        calc_locs_opt = [
            cl for cl in fw_spec.get('calc_locs', [])
            if 'optimiz' in cl['name']
        ]
        if calc_locs_opt:
            optimize_loc = calc_locs_opt[-1]['path']
            logger.info("Parsing initial optimization directory: {}".format(
                optimize_loc))
            drone = VaspDrone()
            optimize_doc = drone.assimilate(optimize_loc)
            opt_struct = Structure.from_dict(
                optimize_doc["calcs_reversed"][0]["output"]["structure"])
            d.update({"optimized_structure": opt_struct.as_dict()})
            ref_struct = opt_struct
            eq_stress = -0.1 * Stress(optimize_doc["calcs_reversed"][0]
                                      ["output"]["ionic_steps"][-1]["stress"])
        else:
            eq_stress = None

        if self.get("fw_spec_field"):
            d.update({
                self.get("fw_spec_field"):
                fw_spec.get(self.get("fw_spec_field"))
            })

        # Get the stresses, strains, deformations from deformation tasks
        defo_dicts = fw_spec["deformation_tasks"].values()
        stresses, strains, deformations = [], [], []
        for defo_dict in defo_dicts:
            stresses.append(Stress(defo_dict["stress"]))
            strains.append(Strain(defo_dict["strain"]))
            deformations.append(Deformation(defo_dict["deformation_matrix"]))
            # Add derived stresses and strains if symmops is present
            for symmop in defo_dict.get("symmops", []):
                stresses.append(Stress(defo_dict["stress"]).transform(symmop))
                strains.append(Strain(defo_dict["strain"]).transform(symmop))
                deformations.append(
                    Deformation(
                        defo_dict["deformation_matrix"]).transform(symmop))

        stresses = [-0.1 * s for s in stresses]
        pk_stresses = [
            stress.piola_kirchoff_2(deformation)
            for stress, deformation in zip(stresses, deformations)
        ]

        d['fitting_data'] = {
            'cauchy_stresses': stresses,
            'eq_stress': eq_stress,
            'strains': strains,
            'pk_stresses': pk_stresses,
            'deformations': deformations
        }

        logger.info("Analyzing stress/strain data")
        # TODO: @montoyjh: what if it's a cubic system? don't need 6. -computron
        # TODO: Can add population method but want to think about how it should
        #           be done. -montoyjh
        order = self.get('order', 2)
        if order > 2:
            method = 'finite_difference'
        else:
            method = self.get('fitting_method', 'finite_difference')

        if method == 'finite_difference':
            result = ElasticTensorExpansion.from_diff_fit(strains,
                                                          pk_stresses,
                                                          eq_stress=eq_stress,
                                                          order=order)
            if order == 2:
                result = ElasticTensor(result[0])
        elif method == 'pseudoinverse':
            result = ElasticTensor.from_pseudoinverse(strains, pk_stresses)
        elif method == 'independent':
            result = ElasticTensor.from_independent_strains(
                strains, pk_stresses, eq_stress=eq_stress)
        else:
            raise ValueError(
                "Unsupported method, method must be finite_difference, "
                "pseudoinverse, or independent")

        ieee = result.convert_to_ieee(ref_struct)
        d.update({
            "elastic_tensor": {
                "raw": result.voigt,
                "ieee_format": ieee.voigt
            }
        })
        if order == 2:
            d.update({
                "derived_properties":
                ieee.get_structure_property_dict(ref_struct)
            })
        else:
            soec = ElasticTensor(ieee[0])
            d.update({
                "derived_properties":
                soec.get_structure_property_dict(ref_struct)
            })

        d["formula_pretty"] = ref_struct.composition.reduced_formula
        d["fitting_method"] = method
        d["order"] = order

        d = jsanitize(d)

        # Save analysis results in json or db
        db_file = env_chk(self.get('db_file'), fw_spec)
        if not db_file:
            with open("elasticity.json", "w") as f:
                f.write(json.dumps(d, default=DATETIME_HANDLER))
        else:
            db = VaspCalcDb.from_db_file(db_file, admin=True)
            db.collection = db.db["elasticity"]
            db.collection.insert_one(d)
            logger.info("Elastic analysis complete.")

        return FWAction()
 def __init__(self, deformation=((1, 0, 0), (0, 1, 0), (0, 0, 1))):
     self._deform = Deformation(deformation)
     self.deformation = self._deform.tolist()
Example #30
0
 def test_convert_strain_to_deformation(self):
     defo = Deformation.from_index_amount((1, 2), 0.01)
     pass
 def __init__(self, deformation):
     self.deformation = Deformation(deformation)
Example #32
0
def write_vasp_inputs(Str,
                      VASPDir,
                      functional='PBE',
                      num_kpoints=25,
                      additional_vasp_settings=None,
                      strain=((1.01, 0, 0), (0, 1.05, 0), (0, 0, 1.03))):
    # This is a somewhat strange input set. Essentially the matgen input set (PBE+U), but with tigher
    # convergence.
    # This is also a somewhat outdated and convoluted way to generate VASP inputs but it should work fine.
    # These changes to the default input set give much better results.
    # Do not increaes the EDIFF to make it converge faster!!!
    # If convergence is too slow, reduce the K-points
    # This is still using PBE+U with matgen U values though. Need to use MITCompatibility (after the run)
    # to apply oxygen corrections and such.
    # In other expansions that rely on SCAN or HSE, the corrections are different - no O correction for example
    # In additional_vasp_settings, you can add to, or modify the default VASPsettings.
    VASPSettings = {
        "ALGO": 'VeryFast',
        "ISYM": 0,
        "ISMEAR": 0,
        "EDIFF": 1e-6,
        "NELM": 400,
        "NSW": 1000,
        "EDIFFG": -0.02,
        'LVTOT': False,
        'LWAVE': False,
        'LCHARG': False,
        'NELMDL': -6,
        'NELMIN': 8,
        'LSCALU': False,
        'NPAR': 2,
        'NSIM': 2,
        'POTIM': 0.25,
        'LDAU': True
    }

    if additional_vasp_settings:
        for key in additional_vasp_settings:
            VASPSettings[key] = additional_vasp_settings[key]
            print('Changed {} setting to {}.'.format(
                key, additional_vasp_settings[key]))

    if not os.path.isdir(VASPDir): os.mkdir(VASPDir)

    # Joggle the lattice to help symmetry broken relaxation. You may turn it off by setting strain=None
    if strain:
        deformation = Deformation(strain)
        defStr = deformation.apply_to_structure(Str)

    #Str=Structure(StrainedLatt,Species,FracCoords,to_unit_cell=False,coords_are_cartesian=False);
    VIO = MITRelaxSet(defStr, potcar_functional=functional)
    VIO.user_incar_settings = VASPSettings
    VIO.incar.write_file(os.path.join(VASPDir, 'INCAR'))
    VIO.poscar.write_file(os.path.join(VASPDir, 'POSCAR'))
    Kpoints.automatic(num_kpoints).write_file(os.path.join(VASPDir, 'KPOINTS'))
    # Use PAW_PBE pseudopotentials, cannot use PBE_52, this does not exist on ginar!
    # NOTE: For the POTCARs to work, you need to set up the VASP pseudopotential directory as per the
    # pymatgen instructions, and set the path to them in .pmgrc.yaml located in your home folder.
    # The pymatgen website has instuctrions for how to do this.
    POTSyms = VIO.potcar_symbols
    for i, Sym in enumerate(POTSyms):
        if Sym == 'Zr': POTSyms[i] = 'Zr_sv'
    Potcar(POTSyms,
           functional=functional).write_file(os.path.join(VASPDir, 'POTCAR'))
Example #33
0
def get_wf_gibbs_free_energy(structure,
                             vasp_input_set=None,
                             vasp_cmd="vasp",
                             deformations=None,
                             db_file=None,
                             user_kpoints_settings=None,
                             t_step=10,
                             t_min=0,
                             t_max=1000,
                             mesh=(20, 20, 20),
                             eos="vinet",
                             qha_type="debye_model",
                             pressure=0.0):
    """
    Returns quasi-harmonic gibbs free energy workflow.
    Note: phonopy package is required for the final analysis step if qha_type="phonopy"

    Args:
        structure (Structure): input structure.
        vasp_input_set (VaspInputSet)
        vasp_cmd (str): vasp command to run.
        deformations (list): list of deformation matrices(list of lists).
        db_file (str): path to the db file.
        user_kpoints_settings (dict): example: {"grid_density": 7000}
        t_step (float): temperature step (in K)
        t_min (float): min temperature (in K)
        t_max (float): max temperature (in K)
        mesh (list/tuple): reciprocal space density
        eos (str): equation of state used for fitting the energies and the volumes.
            options supported by phonopy: "vinet", "murnaghan", "birch_murnaghan".
            Note: pymatgen supports more options than phonopy. see pymatgen.analysis.eos.py
        qha_type(str): quasi-harmonic approximation type: "debye_model" or "phonopy",
            default is "debye_model"
        pressure (float): in GPa

    Returns:
        Workflow
    """
    lepsilon = False
    if qha_type not in ["debye_model"]:
        lepsilon = True
        try:
            from phonopy import Phonopy
        except ImportError:
            logger.warn(
                "'phonopy' package NOT installed. Required for the final analysis step."
                "The debye model for the quasi harmonic approximation will be used."
            )
            qha_type = "debye_model"
            lepsilon = False

    tag = datetime.utcnow().strftime('%Y-%m-%d-%H-%M-%S-%f')

    deformations = [Deformation(defo_mat) for defo_mat in deformations]
    wf_gibbs = get_wf_deformations(structure,
                                   deformations,
                                   name="gibbs deformation",
                                   vasp_input_set=vasp_input_set,
                                   lepsilon=lepsilon,
                                   vasp_cmd=vasp_cmd,
                                   db_file=db_file,
                                   user_kpoints_settings=user_kpoints_settings,
                                   tag=tag)

    fw_analysis = Firework(GibbsFreeEnergyTask(tag=tag,
                                               db_file=db_file,
                                               t_step=t_step,
                                               t_min=t_min,
                                               t_max=t_max,
                                               mesh=mesh,
                                               eos=eos,
                                               qha_type=qha_type,
                                               pressure=pressure),
                           name="Gibbs Free Energy")

    append_fw_wf(wf_gibbs, fw_analysis)

    wf_gibbs.name = "{}:{}".format(structure.composition.reduced_formula,
                                   "gibbs free energy")

    return wf_gibbs
Example #34
0
class DeformationTest(PymatgenTest):
    def setUp(self):
        self.norm_defo = Deformation.from_index_amount((0, 0), 0.02)
        self.ind_defo = Deformation.from_index_amount((0, 1), 0.02)
        self.non_ind_defo = Deformation([[1.0, 0.02, 0.02],
                                         [0.0, 1.0, 0.0],
                                         [0.0, 0.0, 1.0]])
        lattice = Lattice([[3.8401979337, 0.00, 0.00],
                           [1.9200989668, 3.3257101909, 0.00],
                           [0.00, -2.2171384943, 3.1355090603]])
        self.structure = Structure(lattice, ["Si", "Si"], [[0, 0, 0],
                                                           [0.75, 0.5, 0.75]])

    def test_properties(self):
        # green_lagrange_strain
        self.assertArrayAlmostEqual(self.ind_defo.green_lagrange_strain,
                                    [[0., 0.01, 0.],
                                     [0.01, 0.0002, 0.],
                                     [0., 0., 0.]])
        self.assertArrayAlmostEqual(self.non_ind_defo.green_lagrange_strain,
                                    [[0., 0.01, 0.01],
                                     [0.01, 0.0002, 0.0002],
                                     [0.01, 0.0002, 0.0002]])

    def test_independence(self):
        self.assertFalse(self.non_ind_defo.is_independent())
        self.assertEqual(self.ind_defo.get_perturbed_indices()[0], (0, 1))

    def test_apply_to_structure(self):
        strained_norm = self.norm_defo.apply_to_structure(self.structure)
        strained_ind = self.ind_defo.apply_to_structure(self.structure)
        strained_non = self.non_ind_defo.apply_to_structure(self.structure)
        # Check lattices
        self.assertArrayAlmostEqual(strained_norm.lattice.matrix,
                                    [[3.9170018886, 0, 0],
                                     [1.958500946136, 3.32571019, 0],
                                     [0, -2.21713849, 3.13550906]])
        self.assertArrayAlmostEqual(strained_ind.lattice.matrix,
                                    [[3.84019793, 0, 0],
                                     [1.9866132, 3.32571019, 0],
                                     [-0.04434277, -2.21713849, 3.13550906]])
        self.assertArrayAlmostEqual(strained_non.lattice.matrix,
                                    [[3.84019793, 0, 0],
                                     [1.9866132, 3.3257102, 0],
                                     [0.0183674, -2.21713849, 3.13550906]])
        # Check coordinates
        self.assertArrayAlmostEqual(strained_norm.sites[1].coords,
                                    [3.91700189, 1.224e-06, 2.3516318])
        self.assertArrayAlmostEqual(strained_ind.sites[1].coords,
                                    [3.84019793, 1.224e-6, 2.3516318])
        self.assertArrayAlmostEqual(strained_non.sites[1].coords,
                                    [3.8872306, 1.224e-6, 2.3516318])

        # Check convention for applying transformation
        for vec, defo_vec in zip(self.structure.lattice.matrix,
                strained_non.lattice.matrix):
            new_vec = np.dot(self.non_ind_defo, np.transpose(vec))
            self.assertArrayAlmostEqual(new_vec, defo_vec)
        for coord, defo_coord in zip(self.structure.cart_coords, strained_non.cart_coords):
            new_coord = np.dot(self.non_ind_defo, np.transpose(coord))
            self.assertArrayAlmostEqual(new_coord, defo_coord)
Example #35
0
def process_elastic_calcs(opt_doc, defo_docs, add_derived=True, tol=0.002):
    """
    Generates the list of calcs from deformation docs, along with 'derived
    stresses', i. e. stresses derived from symmop transformations of existing
    calcs from transformed strains resulting in an independent strain
    not in the input list

    Args:
        opt_doc (dict): document for optimization task
        defo_docs ([dict]) list of documents for deformation tasks
        add_derived (bool): flag for whether or not to add derived
            stress-strain pairs based on symmetry
        tol (float): tolerance for assigning equivalent stresses/strains

    Returns ([dict], [dict]):
        Two lists of summary documents corresponding to strains
        and stresses, one explicit and one derived
    """
    structure = Structure.from_dict(opt_doc['output']['structure'])
    input_structure = Structure.from_dict(opt_doc['input']['structure'])

    # Process explicit calcs, store in dict keyed by strain
    explicit_calcs = TensorMapping()
    for doc in defo_docs:
        calc = {
            "type": "explicit",
            "input": doc["input"],
            "output": doc["output"],
            "task_id": doc["task_id"],
            "completed_at": doc["completed_at"]
        }
        deformed_structure = Structure.from_dict(doc['output']['structure'])
        defo = Deformation(calculate_deformation(structure,
                                                 deformed_structure))
        # Warning if deformation is not equivalent to stored deformation
        stored_defo = doc['transmuter']['transformation_params'][0]\
            ['deformation']
        if not np.allclose(defo, stored_defo, atol=1e-5):
            wmsg = "Inequivalent stored and calc. deformations."
            logger.debug(wmsg)
            calc["warnings"] = wmsg
        cauchy_stress = -0.1 * Stress(doc['output']['stress'])
        pk_stress = cauchy_stress.piola_kirchoff_2(defo)
        strain = defo.green_lagrange_strain
        calc.update({
            "deformation": defo,
            "cauchy_stress": cauchy_stress,
            "strain": strain,
            "pk_stress": pk_stress
        })
        if strain in explicit_calcs:
            existing_value = explicit_calcs[strain]
            if doc['completed_at'] > existing_value['completed_at']:
                explicit_calcs[strain] = calc
        else:
            explicit_calcs[strain] = calc

    if not add_derived:
        return explicit_calcs.values(), None

    # Determine all of the implicit calculations to include
    sga = SpacegroupAnalyzer(structure, symprec=0.1)
    symmops = sga.get_symmetry_operations(cartesian=True)
    derived_calcs_by_strain = TensorMapping(tol=0.002)
    for strain, calc in explicit_calcs.items():
        # Generate all transformed strains
        task_id = calc['task_id']
        tstrains = [(symmop, strain.transform(symmop)) for symmop in symmops]
        # Filter strains by those which are independent and new
        # For second order
        if len(explicit_calcs) < 30:
            tstrains = [(symmop, tstrain) for symmop, tstrain in tstrains
                        if tstrain.get_deformation_matrix().is_independent(tol)
                        and not tstrain in explicit_calcs]
        # For third order
        else:
            strain_states = get_default_strain_states(3)
            # Default stencil in atomate, this maybe shouldn't be hard-coded
            stencil = np.linspace(-0.075, 0.075, 7)
            valid_strains = [
                Strain.from_voigt(s * np.array(strain_state))
                for s, strain_state in product(stencil, strain_states)
            ]
            valid_strains = [v for v in valid_strains if not np.allclose(v, 0)]
            valid_strains = TensorMapping(valid_strains,
                                          [True] * len(valid_strains))
            tstrains = [
                (symmop, tstrain) for symmop, tstrain in tstrains
                if tstrain in valid_strains and not tstrain in explicit_calcs
            ]
        # Add surviving tensors to derived_strains dict
        for symmop, tstrain in tstrains:
            # curr_set = derived_calcs_by_strain[tstrain]
            if tstrain in derived_calcs_by_strain:
                curr_set = derived_calcs_by_strain[tstrain]
                curr_task_ids = [c[1] for c in curr_set]
                if task_id not in curr_task_ids:
                    curr_set.append((symmop, calc['task_id']))
            else:
                derived_calcs_by_strain[tstrain] = [(symmop, calc['task_id'])]

    # Process derived calcs
    explicit_calcs_by_id = {d['task_id']: d for d in explicit_calcs.values()}
    derived_calcs = []
    for strain, calc_set in derived_calcs_by_strain.items():
        symmops, task_ids = zip(*calc_set)
        task_strains = [
            Strain(explicit_calcs_by_id[task_id]['strain'])
            for task_id in task_ids
        ]
        task_stresses = [
            explicit_calcs_by_id[task_id]['cauchy_stress']
            for task_id in task_ids
        ]
        derived_strains = [
            tstrain.transform(symmop)
            for tstrain, symmop in zip(task_strains, symmops)
        ]
        for derived_strain in derived_strains:
            if not np.allclose(derived_strain, strain, atol=2e-3):
                logger.info("Issue with derived strains")
                raise ValueError("Issue with derived strains")
        derived_stresses = [
            tstress.transform(sop)
            for sop, tstress in zip(symmops, task_stresses)
        ]
        input_docs = [{
            "task_id": task_id,
            "strain": task_strain,
            "cauchy_stress": task_stress,
            "symmop": symmop
        } for task_id, task_strain, task_stress, symmop in zip(
            task_ids, task_strains, task_stresses, symmops)]
        calc = {
            "strain": strain,
            "cauchy_stress": Stress(np.average(derived_stresses, axis=0)),
            "deformation": strain.get_deformation_matrix(),
            "input_tasks": input_docs,
            "type": "derived"
        }
        calc['pk_stress'] = calc['cauchy_stress'].piola_kirchoff_2(
            calc['deformation'])
        derived_calcs.append(calc)

    return list(explicit_calcs.values()), derived_calcs
Example #36
0
 def test_convert_strain_to_deformation(self):
     defo = Deformation.from_index_amount((1, 2), 0.01)
     pass
Example #37
0
    def _load_data(self):
        """    
        This function parses existing vasp calculations, does mapping check, assigns charges and writes into the calc_data file 
        mentioned in previous functions. What we mean by mapping check here, is to see whether a deformed structure can be mapped
        into a supercell lattice and generates a set of correlation functions in clustersupercell.corr_from_structure.
        
        We plan to do modify corr_from_structure from using pymatgen.structurematcher to a grid matcher, which will ensure higher 
        acceptance for DFT calculations, but does not necessarily improve CE hamitonian, since some highly dipoled and deformed 
        structures might have poor DFT energy, and even SABOTAGE CE!
        """
        # Every key in self.calcdata['compositions'] is a composition, and each composition contains a list of dict entrees.
        # relaxed_structure, input_structure, magmoms, total_energy.

        _is_vasp_calc = lambda fs: 'POSCAR' in fs and 'INCAR' in fs and 'KPOINTS' in fs and 'POTCAR' in fs
        # Load VASP runs from given directories

        n_matched = 0
        n_inputs = 0
        new_unassigned_strs = []
        for root, dirs, files in os.walk(self.vaspdir):
            #A calculation directories has only 3 status:
            #accepted: calculation was successful, and already entered into calcdata.mson
            #falied: calculated but not successful, either aborted or can't be read into calcdata.mson
            #For these above two, we don't want to submit a calculation or post-process again.
            #not marked: calculation run not started or not finished yet. Since analyzer is always called
            #after runner, we don't need to worry that analyzer will find unmarked folders.

            if _is_vasp_calc(files) and (not 'accepted'
                                         in files) and (not 'failed' in files):
                print("Loading VASP run in {}".format(root))
                parent_root = os.path.join(*root.split(os.sep)[0:-1])
                parent_parent_root = os.path.join(*root.split(os.sep)[0:-2])
                with open(
                        os.path.join(parent_parent_root,
                                     'composition_by_site')) as compfile:
                    composition = json.load(compfile)
                    compstring = json.dumps(composition)

                if compstring not in self.calcdata['compositions']:
                    self.calcdata['compositions'][compstring] = []

                if not os.path.isfile(os.path.join(parent_root, 'matrix')):
                    print(
                        'Warning: matrix presave not found. Will autodetect supercell matrix using structure matcher,\
                           and will suffer from numerical errors!')
                    matrix = None
                else:
                    with open(os.path.join(parent_root, 'matrix')) as mat_file:
                        matrix = json.load(mat_file)
                #Check existence of output structure
                try:
                    relaxed_struct = Poscar.from_file(
                        os.path.join(root, 'CONTCAR')).structure
                except:
                    print('Entry {} CONTCAR can not be read. Skipping.'.format(
                        root))
                    open(os.path.join(root, 'failed'), 'a').close()
                    continue

                input_struct = Poscar.from_file(
                    os.path.join(parent_root, 'POSCAR')).structure

                #Check uniqueness
                strict_sm = StructureMatcher(stol=0.1,
                                             ltol=0.1,
                                             angle_tol=1,
                                             comparator=ElementComparator())
                _is_unique = True
                for entry in self.calcdata['compositions'][compstring]:
                    entry_struct = Structure.from_dict(
                        entry['relaxed_structure'])
                    if strict_sm.fit(entry_struct, relaxed_struct):
                        _is_unique = False
                        break
                if not _is_unique:
                    print('Entry {} alredy calculated before.'.format(root))
                    open(os.path.join(root, 'accepted'), 'a').close()
                    continue
                n_inputs += 1

                # Note: the input_struct here comes from the poscar in upper root, rather than fm.0, so
                # it is not deformed.

                # Rescale volume to that of unrelaxed structure, this will lead to a better mapping back.
                # I changed it to a rescaling tensor
                relaxed_lat_mat = np.matrix(relaxed_struct.lattice.matrix)
                input_lat_mat = np.matrix(input_struct.lattice.matrix)
                o2i_deformation = Deformation(input_lat_mat.T *
                                              relaxed_lat_mat.I.T)
                relaxed_deformed = o2i_deformation.apply_to_structure(
                    relaxed_struct)
                #print(relaxed_deformed,input_struct)

                # Assign oxidation states to Mn based on magnetic moments in OUTCAR, first check existence of OUTCAR
                try:
                    Out = Outcar(os.path.join(root, 'OUTCAR'))
                except:
                    print('Entry {} OUTCAR can not be read. Skipping.'.format(
                        root))
                    open(os.path.join(root, 'failed'), 'a').close()
                    continue

                # Get final energy from OSZICAR or Vasprun. Vasprun is better but OSZICAR is much
                # faster and works fine is you separately check for convergence, sanity of
                # magnetic moments, structure geometry
                with open(os.path.join(root, 'OUTCAR')) as outfile:
                    outcar_string = outfile.read()
                if 'reached required accuracy' not in outcar_string:
                    print(
                        'Entry {} did not converge to required accuracy. Skipping.'
                        .format(root))
                    open(os.path.join(root, 'failed'), 'a').close()
                    continue
                TotE = Oszicar(os.path.join(root, 'OSZICAR')).final_energy
                # Checking convergence
                Mag = []
                for SiteInd, Site in enumerate(relaxed_struct.sites):
                    Mag.append(np.abs(Out.magnetization[SiteInd]['tot']))

                new_entry = {}
                new_entry['input_structure'] = input_struct.as_dict()
                new_entry['relaxed_structure'] = relaxed_struct.as_dict()
                new_entry['relaxed_deformed'] = relaxed_deformed.as_dict()
                new_entry['total_energy'] = TotE
                new_entry['magmoms'] = Mag
                new_entry['matrix'] = matrix

                if os.path.isfile(os.path.join(parent_parent_root, 'axis')):
                    with open(os.path.join(parent_parent_root,
                                           'axis')) as axisfile:
                        axis = json.load(axisfile)
                    if 'axis' not in new_entry:
                        new_entry['axis'] = axis

                new_unassigned_strs.append((compstring, root, new_entry))

        if len(new_unassigned_strs) == 0:
            print('No new structures appeared. Calcdata will not be updated.')
            return

        #Charge assignment
        if self.is_charged_ce:
            relaxed_deformed_pool = []
            relaxed_strs_pool = []
            mags = []
            roots = []
            energies = []
            comps = []
            inputs = []
            mats = []
            if 'axis' in new_unassigned_strs[0][2]:
                axis = []
            for compstring, root, new_entry in new_unassigned_strs:
                # Out=Outcar(os.path.join(root,'OUTCAR'))
                Mag = new_entry['magmoms']
                relaxed_struct = Structure.from_dict(
                    new_entry['relaxed_structure'])
                relaxed_deformed = Structure.from_dict(
                    new_entry['relaxed_deformed'])
                # Throw out structures where oxidation states don't make charge balanced.

                mags.append(Mag)
                roots.append(root)
                relaxed_strs_pool.append(relaxed_struct)
                relaxed_deformed_pool.append(relaxed_deformed)
                comps.append(compstring)
                inputs.append(Structure.from_dict(
                    new_entry['input_structure']))
                energies.append(new_entry['total_energy'])
                mats.append(new_entry['matrix'])
                if 'axis' in new_entry:
                    axis.append(new_entry['axis'])

            CA = ChargeAssign(relaxed_strs_pool, mags, algo=self.assign_algo)
            relaxed_strs_assigned = CA.assigned_structures
            relaxed_deformed_assigned = CA.extend_assignments(
                relaxed_deformed_pool, mags)

            for i in range(len(inputs)):
                if relaxed_strs_assigned[
                        i] is not None and relaxed_deformed_assigned[
                            i] is not None:
                    # Checking whether structure can be mapped to corr function.
                    # This is out deformation tolerance.
                    try:
                        if mats[i] is not None:
                            cesup = self.ce.supercell_from_matrix(mats[i])
                            corr = cesup.corr_from_structure(
                                relaxed_deformed_assigned[i])
                        else:
                            corr = self.ce.corr_from_structure(
                                relaxed_deformed_assigned[i])
                    except:
                        print(
                            "Entry {} too far from original lattice. Skipping."
                            .format(roots[i]))
                        open(os.path.join(roots[i], 'failed'), 'a').close()
                        continue

                    assigned_entry = {}
                    assigned_entry['input_structure'] = inputs[i].as_dict()
                    assigned_entry[
                        'relaxed_structure'] = relaxed_strs_assigned[
                            i].as_dict()
                    assigned_entry[
                        'relaxed_deformed'] = relaxed_deformed_assigned[
                            i].as_dict()
                    assigned_entry['matrix'] = mats[i]
                    assigned_entry['total_energy'] = energies[i]
                    assigned_entry['magmoms'] = mags[i]
                    if 'axis' in new_unassigned_strs[0][2]:
                        assigned_entry['axis'] = axis[i]
                    self.calcdata['compositions'][comps[i]].append(
                        assigned_entry)
                    print('Entry {} accepted!'.format(roots[i]))
                    open(os.path.join(roots[i], 'accepted'), 'a').close()
                    n_matched += 1

                else:
                    print("Entry {} can not be assigned. Skipping.".format(
                        roots[i]))
                    open(os.path.join(roots[i], 'failed'), 'a').close()
                    continue
        else:
            print('Doing non charged ce.')
            for compstring, root, new_entry in new_unassigned_strs:
                # Checking whether structure can be mapped to corr function.
                # This is out deformation tolerance.
                try:
                    if new_entry['matrix'] is not None:
                        cesup = self.ce.supercell_from_matrix(
                            new_entry['matrix'])
                        corr = cesup.corr_from_structure(
                            Structure.from_dict(new_entry['relaxed_defromed']))
                    else:
                        corr = self.ce.corr_from_structure(
                            Structure.from_dict(new_entry['relaxed_defromed']))
                except:
                    print("Entry {} too far from original lattice. Skipping.".
                          format(root))
                    open(os.path.join(root, 'failed'), 'a').close()
                    continue

                self.calcdata['compositions'][compstring].append(new_entry)
                open(os.path.join(root, 'accepted'), 'a').close()
                n_matched += 1
        # Data already deduplicated!

        print(
            '{}/{} structures matched in this run. Parsed vasp data will be saved into {}.'
            .format(n_matched, n_inputs, self.calc_data_file))
Example #38
0
def get_wf_elastic_constant(structure, vasp_input_set=None, vasp_cmd="vasp", 
                            norm_deformations=[-0.01, -0.005, 0.005, 0.01],
                            shear_deformations=[-0.06, -0.03, 0.03, 0.06],
                            db_file=None, reciprocal_density=None):
    """
    Returns a workflow to calculate elastic constants.

    Firework 1 : write vasp input set for structural relaxation,
                 run vasp,
                 pass run location,
                 database insertion.

    Firework 2 - 25: Optimize Deformed Structure
    
    Firework 26: Analyze Stress/Strain data and fit the elastic tensor

    Args:
        structure (Structure): input structure to be optimized and run
        norm_deformations (list): list of values to for normal deformations
        shear_deformations (list): list of values to for shear deformations
        vasp_input_set (DictVaspInputSet): vasp input set.
        vasp_cmd (str): command to run
        db_file (str): path to file containing the database credentials.
        reciprocal_density (int): k-points per reciprocal atom by volume

    Returns:
        Workflow
    """

    v = vasp_input_set or MPRelaxSet(structure, force_gamma=True)
    if reciprocal_density:
        v.config_dict["KPOINTS"].update(
            {"reciprocal_density":reciprocal_density})
        v = DictSet(structure, v.config_dict)
    fws = []

    fws.append(OptimizeFW(structure=structure,
                          vasp_input_set=v,
                          vasp_cmd=vasp_cmd,
                          db_file=db_file))

    deformations = []
    # Generate deformations
    for ind in [(0, 0), (1, 1), (2, 2)]:
        for amount in norm_deformations:
            defo = Deformation.from_index_amount(ind, amount)
            deformations.append(defo)

    for ind in [(0, 1), (0, 2), (1, 2)]:
        for amount in shear_deformations:
            defo = Deformation.from_index_amount(ind, amount)
            deformations.append(defo)

    def_vasp_params = {"user_incar_settings":{"ISIF":2, "IBRION":2, 
                                              "NSW":99, "LAECHG":False,
                                              "LHVAR":False, "ALGO":"Fast",
                                              "LWAVE":False}}
    if reciprocal_density:
        def_vasp_params.update(
            {"reciprocal_density":reciprocal_density})
    
    for deformation in deformations:
        fw = TransmuterFW(name="elastic deformation",
                          structure=structure,
                          transformations=['DeformStructureTransformation'],
                          transformation_params=[
                              {"deformation": deformation.tolist()}],
                          copy_vasp_outputs=True,
                          db_file=db_file,
                          vasp_cmd=vasp_cmd,
                          parents=fws[0],
                          vasp_input_params = def_vasp_params
                         )
        fw.spec['_tasks'].append(
            PassStressStrainData(deformation=deformation.tolist()).to_dict())
        fws.append(fw)
    
    fws.append(Firework(AnalyzeStressStrainData(structure=structure, 
                                                db_file=db_file),
                        name="Analyze Elastic Data", parents=fws[1:],
                        spec = {"_allow_fizzled_parents":True}))

    wfname = "{}:{}".format(structure.composition.reduced_formula,
                            "elastic constants")
    return Workflow(fws, name=wfname)
Example #39
0
 def __init__(self, deformation):
     self.deformation = Deformation(deformation)
Example #40
0
def get_wf_elastic_constant(
    structure,
    vasp_input_set=None,
    vasp_cmd="vasp",
    norm_deformations=None,
    shear_deformations=None,
    additional_deformations=None,
    db_file=None,
    user_kpoints_settings=None,
    add_analysis_task=True,
    conventional=True,
):
    """
    Returns a workflow to calculate elastic constants.

    Firework 1 : write vasp input set for structural relaxation,
                 run vasp,
                 pass run location,
                 database insertion.

    Firework 2 - number of total deformations: Static runs on the deformed structures
    
    last Firework : Analyze Stress/Strain data and fit the elastic tensor

    Args:
        structure (Structure): input structure to be optimized and run.
        norm_deformations (list): list of values to for normal deformations.
        shear_deformations (list): list of values to for shear deformations.
        additional_deformations (list of 3x3 array-likes): list of additional deformations.
        vasp_input_set (DictVaspInputSet): vasp input set.
        vasp_cmd (str): command to run.
        db_file (str): path to file containing the database credentials.
        user_kpoints_settings (dict): example: {"grid_density": 7000}
        add_analysis_task (bool): boolean indicating whether to add analysis

    Returns:
        Workflow
    """
    # Convert to conventional
    if conventional:
        structure = SpacegroupAnalyzer(structure).get_conventional_standard_structure()
    # Generate deformations
    deformations = []

    if norm_deformations is not None:
        deformations.extend(
            [
                Deformation.from_index_amount(ind, amount)
                for ind in [(0, 0), (1, 1), (2, 2)]
                for amount in norm_deformations
            ]
        )
    if shear_deformations is not None:
        deformations.extend(
            [
                Deformation.from_index_amount(ind, amount)
                for ind in [(0, 1), (0, 2), (1, 2)]
                for amount in shear_deformations
            ]
        )

    if additional_deformations:
        deformations.extend([Deformation(defo_mat) for defo_mat in additional_deformations])

    if not deformations:
        raise ValueError("deformations list empty")

    wf_elastic = get_wf_deformations(
        structure,
        deformations,
        vasp_input_set=vasp_input_set,
        lepsilon=False,
        vasp_cmd=vasp_cmd,
        db_file=db_file,
        user_kpoints_settings=user_kpoints_settings,
        pass_stress_strain=True,
        name="deformation",
        relax_deformed=True,
        tag="elastic",
    )

    if add_analysis_task:
        fw_analysis = Firework(
            ElasticTensorToDbTask(structure=structure, db_file=db_file),
            name="Analyze Elastic Data",
            spec={"_allow_fizzled_parents": True},
        )
        append_fw_wf(wf_elastic, fw_analysis)

    wf_elastic.name = "{}:{}".format(structure.composition.reduced_formula, "elastic constants")

    return wf_elastic