Source code for imeall.calc_chemoelastic

import os
import sys
import argparse
import subprocess
import numpy as np
import ase.units as units


from imeall import app
from imeall.models import PotentialParameters
from quippy import Atoms, Potential, AtomsReader, set_fortran_indexing

set_fortran_indexing(False)
[docs]def strain_energy(ats, cursor_step=0.2): """Create an array tracking the accumulation of energy above the bulk energy along the z-axis. For interfacial structures this has pronounced speakes in the region of the interface. Args: ats(:py:class:`Atoms`) object with a potential calculator attached. cursor_step(float): step distance along z to add atomic energies to the cumulative energy. Returns: list: Cumulative energy distribution along the z-axis. """ cell = ats.get_cell() A = cell[0][0]*cell[1][1] z_height = cell[2][2] ener_z = np.transpose(np.vstack((ats.get_potential_energies(), [at.position[2] for at in ats]))) ener_z = np.array(sorted(ener_z, key=lambda x: x[1])) cursor = cursor_step elastic_energy = [] while cursor < (z_height + cursor_step): try: cum_energy = 16.02*sum([x+4.01298214176 for x in np.array(filter(lambda x: x[1]<= cursor, ener_z))[:,0]])/(A) except IndexError: cursor += cursor_step #initial step doesn't capture atoms continue elastic_energy.append((cursor, cum_energy)) cursor += cursor_step return elastic_energy
[docs]def calc_chemomechanical(ats): """Calculate elastic and chemical contributions to the total energy. Requires :py:class:`Atoms` object with a :py:class:`Potential` capable of returning a per atom energy. :py:class:`Atoms` object must have at least structure_type and local_energy properties. For a bcc lattice structure_type=3. Args: ats(:py:class:`Atoms`): Returns: list:[(chemical_energy/total_energy)*gb_energy, (elastic_energy/total_energy)*gb_energy, gb_energy] """ #case quip types to numpy arrays stack and transpose loc_en = np.array(ats.properties['local_energy']) struct_type = np.array(ats.properties['structure_type']) joined = np.vstack((loc_en, struct_type)).transpose() cell = ats.get_cell() A = cell[0][0]*cell[1][1] #compute relative total energy contributions of the two types gs = np.zeros(np.shape(joined)) #zero energies gs[:,0] = -4.01298 joined = joined-gs #select bcc and non_bcc non_bcc = joined[joined[:,1]==3] bcc = joined[joined[:,1] != 3] chemical_energy = np.sum(non_bcc[:,0]) elastic_energy = np.sum(bcc[:,0]) total_energy = chemical_energy + elastic_energy gb_energy = 16.02*(total_energy)/(2.*A) print 'Chemical {}%, Elastic {}%'.format(round(100*chemical_energy/total_energy, 2), round(100*elastic_energy/total_energy, 2)) return [(chemical_energy/total_energy)*gb_energy, (elastic_energy/total_energy)*gb_energy, gb_energy]
[docs]def calc_chemoelast(input_file): """Adds the structure type using an ovitos script to the :py:class:`Atoms` object and calculates the breakdown of the energy contributions. Args: input_file(str):Relaxed grain boundary structure file. Returns: list(float):[(chemical_energy/total_energy)*gb_energy, (elastic_energy/total_energy)*gb_energy, gb_energy] """ potparam = PotentialParameters() ener_bulk_dict = potparam.gs_ener_per_atom() r_scale_dict = potparam.eam_rscale() r_scale = r_scale_dict['PotBH.xml'] E_bulk = ener_bulk_dict['PotBH.xml'] try: POT_DIR = os.environ ['POTDIR'] except KeyError: sys.exit("PLEASE SET export POTDIR='path/to/potfiles/'") eam_pot = 'PotBH.xml' eam_pot = os.path.join(POT_DIR, eam_pot) pot = Potential('IP EAM_ErcolAd do_rescale_r=T r_scale={0}'.format(r_scale), param_filename=eam_pot) ats = AtomsReader(input_file)[-1] ats.set_calculator(pot) gb_energy = potparam.calc_e_gb(ats, E_bulk) print gb_energy, 'J/^2m' ats.write('full.xyz') elastic_energy = strain_energy(ats) with open('elast.dat','w') as f: for x in elastic_energy: print >> f, x[0], x[1] #generates output.xyz imeall_root = os.path.join(app.root_path, 'ovito_scripts/attach_cna.py') args_str = 'ovitos {imeall_root} -i {input_file}'.format(imeall_root=app.root_path, input_file='full.xyz').split() job = subprocess.Popen(args_str) job.wait() ats = Atoms('output.xyz') #print the three contributions x = calc_chemomechanical(ats) try: assert round(gb_energy,2) == round(x[2],2) except AssertionError: print "WARNING ENERGIES DON'T MATCH", gb_energy, x[2] return x
if __name__=='__main__': parser = argparse.ArgumentParser() parser.add_argument('--input_file', '-i', help='Input grain boundary struct file to produce cumulative energy') args = parser.parse_args() add_structure_type(input_file=args.input_file)