Example #1
0
 def test_process_elastic_calcs_toec(self):
     # Test TOEC tasks
     test_struct = PymatgenTest.get_structure('Sn')  # use cubic test struct
     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)
     strains = [
         Strain.from_voigt(s * np.array(strain_state))
         for s, strain_state in product(stencil, strain_states)
     ]
     strains = [s for s in strains if not np.allclose(s, 0)]
     sym_reduced = symmetry_reduce(strains, test_struct)
     opt_task = {
         "output": {
             "structure": test_struct.as_dict()
         },
         "input": {
             "structure": test_struct.as_dict()
         }
     }
     defo_tasks = []
     for n, strain in enumerate(sym_reduced):
         defo = strain.get_deformation_matrix()
         new_struct = defo.apply_to_structure(test_struct)
         defo_task = {
             "output": {
                 "structure": new_struct.as_dict(),
                 "stress": (strain * 5).tolist()
             },
             "input": None,
             "task_id": n,
             "completed_at": datetime.utcnow()
         }
         defo_task.update({
             "transmuter": {
                 "transformation_params": [{
                     "deformation": defo
                 }]
             }
         })
         defo_tasks.append(defo_task)
     explicit, derived = process_elastic_calcs(opt_task, defo_tasks)
     self.assertEqual(len(explicit), len(sym_reduced))
     self.assertEqual(len(derived), len(strains) - len(sym_reduced))
     for calc in derived:
         self.assertTrue(
             np.allclose(calc['strain'], calc['cauchy_stress'] / -0.5))
Example #2
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 #3
0
    Args:
        strain (Strain): Input strain

    Returns:
        6-tuple corresponding to strain state
    """
    vstrain = strain.zeroed(5e-5).voigt
    vstrain[vstrain == 0] = np.inf
    min_nonzero = np.argmin(np.abs(vstrain))
    vstrain[vstrain == np.inf] = 0
    strain_state = vstrain / vstrain[min_nonzero]
    return strain_state.round(4)


allowed_strain_states = get_default_strain_states(3)


# TODO: make it so opt_doc not necessary?
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