├── LICENSE ├── Lava_Wrapper_User_Manual_v1.docx ├── README.md └── main ├── Al99.eam.alloy ├── INCAR.inp ├── KPOINTS ├── Lava_Calculator.py ├── Lava_Config.py ├── Lava_Generator.py ├── Lava_Lammps_Header.py ├── Lava_Plotter.py ├── Lava_Utility.py ├── Lava_Vasp_Header.py ├── Lava_Wrapper.py ├── RunLammps.inp ├── RunLammps.sh ├── __pycache__ ├── Lava_Calculator.cpython-38.pyc ├── Lava_Config.cpython-38.pyc ├── Lava_Generator.cpython-38.pyc ├── Lava_Lammps_Header.cpython-38.pyc ├── Lava_Plotter.cpython-38.pyc ├── Lava_Utility.cpython-38.pyc └── Lava_Vasp_Header.cpython-38.pyc └── lmp_mpi /LICENSE: -------------------------------------------------------------------------------- 1 | BSD 3-Clause License 2 | 3 | Copyright (c) 2022, Khanh Q Dang 4 | All rights reserved. 5 | 6 | Redistribution and use in source and binary forms, with or without 7 | modification, are permitted provided that the following conditions are met: 8 | 9 | 1. Redistributions of source code must retain the above copyright notice, this 10 | list of conditions and the following disclaimer. 11 | 12 | 2. Redistributions in binary form must reproduce the above copyright notice, 13 | this list of conditions and the following disclaimer in the documentation 14 | and/or other materials provided with the distribution. 15 | 16 | 3. Neither the name of the copyright holder nor the names of its 17 | contributors may be used to endorse or promote products derived from 18 | this software without specific prior written permission. 19 | 20 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 21 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 22 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 23 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 24 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 25 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 26 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 27 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 28 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 29 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 30 | -------------------------------------------------------------------------------- /Lava_Wrapper_User_Manual_v1.docx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lanl/LAVA/2417ac296b21a453a60192e539d7ebcdb80f2172/Lava_Wrapper_User_Manual_v1.docx -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | LAVA version 1.0 2 | 3 | Los Alamos National Laboratory C22050 4 | 5 | © 2022. Triad National Security, LLC. All rights reserved. 6 | This program was produced under U.S. Government contract 89233218CNA000001 for Los Alamos National Laboratory (LANL), which is operated by Triad National Security, LLC for the U.S. Department of Energy/National Nuclear Security Administration. All rights in the program are reserved by Triad National Security, LLC, and the U.S. Department of Energy/National Nuclear Security Administration. The Government is granted for itself and others acting on its behalf a nonexclusive, paid-up, irrevocable worldwide license in this material to reproduce, prepare derivative works, distribute copies to the public, perform publicly and display publicly, and to permit others to do so. 7 | 8 | # LAVA 9 | 10 | **1. Overview** 11 | 12 | The Lava Wrapper is a general-purpose calculator that provides a python interface to enable one-click calculation of the many common properties with lammps and vasp. The name Lava is derived from the “La” in Lammps and “va” in vasp. It provides a set of classes and functions to generate configurations, run lammps/vasp calculation, retrieve the output, postprocess and plot the results. All the above tasks are hard-coded into the script, without the need to call additional libraries. 13 | 14 | **2. Features** 15 | 16 | Lava Wrapper comprises of eight major python modules. The following flow chart gives an overall idea of the workings of Lava Wrapper. Lava_Wrapper.py provides the outmost layer of abstraction, where the users can specify the set of calculations to perform, invoking the corresponding functions in Lava_Calculator.py, which reads in configurations generated by Lava_Generator.py, submits lammps/vasp run, extracts and postprocess the output, invoking utility functions in Lava_Utility.py. In this process, two other modules, Lava_Lammps _Header.py and Lava_Vasp_Header.py, are responsible for generating Lammps input script and vasp INCAR file, respectively. Finally, the results are plotted with Lava_Plotter.py. The last module, Lava_Config.py is used for storing global settings. 17 | 18 | ![image](https://user-images.githubusercontent.com/106281982/171067764-f44ea45b-d270-4b85-93c4-c9e56a06004a.png) 19 | 20 | Fig. 1 Flow-chart of Lava Wrapper. 21 | 22 | The following is a summary of each module’s functionality: 23 | Lava_Wrapper.py: The outmost wrapper where the users specify the type of calculations, and the parameters pertaining to these calculations. 24 | Lava_Calculator.py: The core of Lava Wrapper, responsible for performing the various types of calculations through the following steps: generate the relevant input file, perform calculations, retrieve and postprocess the output, and plot the results. 25 | Lava_Generator.py: Functions for the generation of various crystal structures and defect configurations. The following 8 bulk crystal structures are currently supported: Simple Cubic (SC), Body Centered Cubic (BCC), Face Centered Cubic (FCC), Hexagonal Close Packed (HCP), Diamond Cubic (DC), double HCP, A5_Beta_Sn, A15_Beta_W. The following defect configurations: vacancy, interstitial, surface, stacking faults, twin faults can also be added to the generated crystal structures. 26 | Lava_Utility.py: Utility functions for file management, data processing, etc. 27 | Lava_Lammps_Header.py: Functions for generating lammps input script, depending on the type of calculation. 28 | Lava_Vasp_Header.py: Functions for generating vasp input script, namely, the INCAR file, depending on the type of calculation. 29 | Lava_Plotter.py: Functions for plotting the results. 30 | Lava_Config.py: Specification the lammps executable, the lammps potential file, as well as the element, provided by the user. The first two files are only required for running the wrapper in lammps mode. 31 | 32 | **3. Types of Calculations**** 33 | 34 | Currently, Lava Wrapper incorporates the following types of calculations. For some calculations the output a written to “Summary.dat” file that serves as the general output for Lava_Wrapper, and for other types of calculation where the results are tabulable, then it is written to a separate data file and plotted as well. 35 | 36 | ![image](https://user-images.githubusercontent.com/106281982/171067562-aa8b6181-480c-459a-84b6-aac9ec9d4e02.png) 37 | 38 | **4. Running LAVA** 39 | 40 | To run Lava Wrapper, load python3 first and then issue the following command on the terminal: 41 | 42 | _python3 Lava_Wrapper.py mode_ 43 | 44 | wherein the highlighted mode can either be Lammps or Vasp, depending on which mode you wish to run. 45 | Example scripts and output of Lava Wrapper are given below, when invoked for Al using Mishin-Farkas potential. This can be set up by specifying the lammps executable as well as potential file name in Lava_Config.py in the following way: 46 | 47 | _lammps_executable = "lmp_mpi"_ 48 | 49 | _element = "Al"_ 50 | 51 | _potential_file = 'Al99.eam.alloy'_ 52 | 53 | 54 | Make sure that the lammps executable and the potential file is in the same folder as the Lava Wrapper scripts. The lmp_mpi executable is built from the 55 | "LAMMPS 3 Mar 2020" version, the users are encouraged to build their own executables. 56 | 57 | To turn on/off the calculated properties, the users can comment/uncomment the calculations in the LAVA_Wrapper.py file. More details about each calculation are provided in the manual. 58 | 59 | -------------------------------------------------------------------------------- /main/INCAR.inp: -------------------------------------------------------------------------------- 1 | GGA=PE 2 | ISMEAR=-5 3 | POTIM=0.1 4 | SYMPREC=1E-6 5 | EDIFF=1E-6 6 | EDIFFG=-0.01 7 | ENCUT=500 8 | ALGO=N 9 | NPAR=4 10 | NSW=NSW_val 11 | IBRION=IBRION_val 12 | ISIF=ISIF_val 13 | -------------------------------------------------------------------------------- /main/KPOINTS: -------------------------------------------------------------------------------- 1 | 0 2 | Auto 3 | 80 4 | -------------------------------------------------------------------------------- /main/Lava_Calculator.py: -------------------------------------------------------------------------------- 1 | ############################################################################### 2 | # This module provides functions for setting up all kinds of calculations 3 | # These functions are invoked in "Lava_Wrapper.py" 4 | # Refer to the definition of these functions 5 | # if you wish to have more control over the calculations 6 | ############################################################################### 7 | 8 | import numpy as np 9 | import os 10 | from os import sys 11 | from shutil import copyfile 12 | import math 13 | from Lava_Lammps_Header import * 14 | from Lava_Vasp_Header import * 15 | from Lava_Utility import * 16 | from Lava_Generator import * 17 | from Lava_Plotter import * 18 | 19 | ############################################################################### 20 | 21 | # Output directory and name 22 | filedir = os.path.dirname(os.path.realpath('__file__')) 23 | filename= os.path.join(filedir,"Summary.dat") 24 | fout = open(filename,"a+") 25 | 26 | 27 | def get_phase_params(phase_list): 28 | # Scaling constants for the initial a, b/a, and c/a and number of atoms per cell 29 | # Calculate the volume for FCC phase (V_base=1^3/4=0.25) 30 | # scale the lattice parameters of all phases to that volume 31 | # scale with a base value of 4.0 for a 32 | # This provodes a good initial lattice parameters 33 | a_base, V_base = 5, 0.25 34 | phase_params = {"SC": [1, 1, 1, 1], \ 35 | "BCC": [1, 1, 1, 2], \ 36 | "FCC": [1, 1, 1, 4], \ 37 | "HCP": [1, math.sqrt(3), 1.6, 4], \ 38 | "DC": [1, 1, 1, 8], \ 39 | "DHCP": [1, math.sqrt(3), 3.2, 8], \ 40 | "A5_Beta_Sn": [1, 1, 0.5, 4], \ 41 | "A5_Gamma_Sn": [1, 0.9, 0.5, 4], \ 42 | "A15_Beta_W": [1, 1, 1, 8], \ 43 | "L12": [1, 1, 1, 4], \ 44 | "9R": [1, 1/math.sqrt(3), 3*math.sqrt(2), 18]} 45 | for phase in phase_params: 46 | alat, blat, clat, atoms_per_cell = phase_params[phase] 47 | alat = (V_base*atoms_per_cell/blat/clat)**(1/3) 48 | phase_params[phase][0] *= alat*a_base 49 | return {phase:phase_params[phase] for phase in phase_list} 50 | 51 | 52 | # Lattice constants and cohesive energy 53 | def get_cohesive_energy(phase_list, NSW=100, IBRION=1, ISIF=3, lat_rep=[2,2,2], orz=[0,0,1], mode=None): 54 | # The directory where all the calculations are run 55 | dirname = "get_cohesive_energy_%s" %mode 56 | fout.write("############ Lattice constants ############\n") 57 | phase_params = get_phase_params(phase_list) 58 | if os.path.isfile("user_provided.data"): phase_list.append("user_provided.data") 59 | # Write out a lattice parameter file for later use 60 | def write_latt_file_initialize(): 61 | print ("Writing out lattice parameter file: %s_latt.dat" %mode) 62 | filename_latt = os.path.join(filedir,"%s_latt.dat" %mode) 63 | fout_latt = open(filename_latt,"w") 64 | fout_latt.write("phase a b/a c/a atoms_per_cell Ecoh\n") 65 | return fout_latt 66 | def write_latt_data(): 67 | fout.write("%s: alat = %.4f, blat = %.4f, clat = %.4f, atoms_per_cell = %d, Ecoh = %.4f\n" \ 68 | %(phase, a, b/a, c/a, natoms, pe)) 69 | def write_delta_FCC_HCP(): 70 | if "FCC" in phase_list and "HCP" in phase_list: 71 | fout.write("DE(FCC->HCP) = %.4f\n" %(phase_lat["FCC"][-1] - phase_lat["HCP"][-1])) 72 | if mode == "Lammps": get_lammps_script(Lammps_file="Lammps_Relax_Box.in").Relax_Box() 73 | if not os.path.isdir(dirname): 74 | os.mkdir(dirname) 75 | for phase in phase_list: 76 | output_dir = make_dir(os.path.join(filedir, dirname, phase)) 77 | if phase != "user_provided.data": 78 | # Initial a, b/a, c/a and number of atoms per cell 79 | alat, blat, clat, atoms_per_cell = phase_params[phase] 80 | # Write lammps data file "Temp.data" if mode == Lammps 81 | # Qrite POSCAR file "POSCAR" if mode == Vasp 82 | data, NAN, box_2d = Build_Crystal(phase, alat, clat, lat_rep, orz).Build() 83 | Write_Data_wrapper(data, NAN, box_2d, output_dir=output_dir, mode=mode) 84 | else: 85 | copyfile('user_provided.data', '%s/Temp.data' %(output_dir)) 86 | submit_job_wrapper(output_dir, filedir, NSW, IBRION, ISIF, lammps_file="Lammps_Relax_Box.in", mode=mode) 87 | # Extract results now if on Lammps mode 88 | # Else wait until the folders are created and simulations are finished 89 | if mode == "Lammps" or os.path.isdir(dirname): 90 | fout_latt = write_latt_file_initialize() 91 | phase_lat = {} 92 | for phase in phase_list: 93 | # Get a, b/a, c/a and Ecoh and append to phase_lat 94 | target_file = target_file_wrapper(filedir, dirname, phase, mode=mode) 95 | pe = get_output(target_file, mode=mode).grep_E_per_atom() 96 | lat_rep_temp = lat_rep if phase != "user_provided.data" else [1,1,1] 97 | a, b, c = get_output(target_file, mode=mode).grep_latt(lat_rep=lat_rep_temp) 98 | natoms = phase_params[phase][3] if phase != "user_provided.data" else get_output(target_file, mode=mode).grep_natoms() 99 | phase_lat[phase] = (a, b/a, c/a, natoms, pe) 100 | write_latt_data() 101 | fout_latt.write("%s %.4f %.4f %.4f %d %.4f\n" %(phase, *phase_lat[phase])) 102 | fout_latt.close() 103 | write_delta_FCC_HCP() 104 | 105 | 106 | # Cold curve with varying V 107 | def get_cold_curve_V(phase_list, V_start=None, V_stop=None, npoints=None, NSW=1, IBRION=1, ISIF=2, \ 108 | lat_rep=[1,1,1], orz=[0,0,1], V_list=None, mass=None, mode=None, \ 109 | dirname=None, output_name=None, plot_axis_x="volume", plot_axis_y="energy"): 110 | if not dirname: 111 | dirname = "get_cold_curve_V_%s" %mode 112 | fout.write("############ Cold curve with varying V ###########\n") 113 | if not V_list: V_list = {phase:np.linspace(V_start,V_stop,npoints+1,endpoint=True) for phase in phase_list} 114 | if not output_name: output_name = "EOS_V_%s" %mode 115 | def get_density(mass, V): 116 | return mass*1e-23/(V*1e-24) 117 | def write_EOS_initialize(): 118 | filename_EOS = os.path.join(filedir,output_name+'.dat') 119 | fout_EOS = open(filename_EOS,"w") 120 | for phase in phase_list: fout_EOS.write("#%s " %phase) 121 | fout_EOS.write("\n") 122 | return fout_EOS 123 | def write_EOS_final(): 124 | for i in range(npoints): 125 | for phase in phase_list: 126 | fout_EOS.write("%.4f %.4f %.4f " %(phase_output[phase][i])) 127 | fout_EOS.write("\n") 128 | fout_EOS.close() 129 | if mode == "Lammps": get_lammps_script(Lammps_file="Lammps_Minimize.in").Minimize() 130 | if not os.path.isdir(dirname): 131 | os.mkdir(dirname) 132 | phase_lat = read_latt_file(os.path.join(filedir,"%s_latt.dat" %mode)) 133 | for phase in phase_list: 134 | output_dir = make_dir(os.path.join(filedir, dirname, phase)) 135 | _, blat, clat, atoms_per_cell, _ = phase_lat[phase] 136 | for count, V in enumerate(V_list[phase]): 137 | V = round_digits(V) 138 | alat = (atoms_per_cell*V/blat/clat)**(1/3) 139 | output_dir = make_dir(os.path.join(filedir, dirname, phase, str(V))) 140 | data, NAN, box_2d = Build_Crystal(phase, alat, clat, lat_rep, orz).Build() 141 | Write_Data_wrapper(data, NAN, box_2d, output_dir=output_dir, mode=mode) 142 | submit_job_wrapper(output_dir, filedir, NSW, IBRION, ISIF, lammps_file="Lammps_Minimize.in", mode=mode) 143 | if mode == "Lammps" or os.path.isdir(dirname): 144 | fout_EOS = write_EOS_initialize() 145 | phase_output = {phase:[] for phase in phase_list} 146 | for phase in phase_list: 147 | for count, V in enumerate(V_list[phase]): 148 | V = round_digits(V) 149 | target_file = target_file_wrapper(filedir, dirname, phase, str(V), mode=mode) 150 | pe = get_output(target_file, mode=mode).grep_E_per_atom() 151 | density = get_density(mass, V) 152 | phase_output[phase].append((density, V, pe)) 153 | write_EOS_final() 154 | #plot_EOS(output_name+'.dat', output_name, plot_axis_x=plot_axis_x, plot_axis_y=plot_axis_y) 155 | 156 | 157 | # Cold curve with varying R 158 | def get_cold_curve_R(phase_list, alat_expanse=None, npoints=None, NSW=1, IBRION=1, ISIF=2, \ 159 | lat_rep=[1,1,1], orz=[0,0,1], mass=None, mode=None, \ 160 | plot_axis_x="volume", plot_axis_y="energy"): 161 | dirname = "get_cold_curve_R_%s" %mode 162 | fout.write("############ Cold curve with varying R ############\n") 163 | phase_lat = read_latt_file(os.path.join(filedir,"%s_latt.dat" %mode)) 164 | V_list = {} 165 | for phase in phase_list: 166 | alat, blat, clat, atoms_per_cell, _ = phase_lat[phase] 167 | alat_list = np.linspace(alat-alat_expanse,alat+alat_expanse,npoints+1,endpoint=True) 168 | V_list[phase] = [alat**3*blat*clat/atoms_per_cell for alat in alat_list] 169 | # Invoke get_cold_curve_V with V_list 170 | get_cold_curve_V(phase_list, npoints=npoints, lat_rep=lat_rep, orz=orz, V_list=V_list, \ 171 | mass=mass, mode=mode, dirname=dirname, output_name="EOS_R_%s" %mode, \ 172 | plot_axis_x=plot_axis_x, plot_axis_y=plot_axis_y) 173 | 174 | 175 | # Thermal expansion 176 | def get_thermal_expansion(T_dict, lat_rep=[10,10,10], orz=[0,0,1], mode=None): 177 | dirname = "get_thermal_expansion_%s" %mode 178 | fout.write("############ Thermal expansion ############\n") 179 | def write_thermal_initialize(): 180 | fout_thermal = open(os.path.join(filedir,"Thermal_expansion_%s_%s.dat" %(phase, mode)),"w") 181 | fout_thermal.write("#Temp alat blat clat average_lat d_lat(%)\n") 182 | return fout_thermal 183 | def write_thermal(): 184 | average_lat = (lx+ly+lz)/3 185 | fout_thermal.write("%.1f %.4f %.4f %.4f %.4f %.4f\n" %(T, lx, ly, lz, \ 186 | average_lat, 100*(average_lat/average_lat_ini-1))) 187 | if mode == "Vasp": 188 | print ("Thermal expansion not supported for Vasp !!!") 189 | return 190 | phase_lat = read_latt_file(os.path.join(filedir,"%s_latt.dat" %mode)) 191 | if not os.path.isdir(dirname): 192 | os.mkdir(dirname) 193 | for phase in T_dict: 194 | alat, blat, clat, _, _ = phase_lat[phase] 195 | data, NAN, box_2d = Build_Crystal(phase, alat, clat, lat_rep, orz).Build() 196 | for T in T_dict[phase]: 197 | output_dir = make_dir(os.path.join(filedir, dirname, phase+"_"+str(T))) 198 | Write_Data_wrapper(data, NAN, box_2d, output_dir=output_dir, mode=mode) 199 | get_lammps_script(Lammps_file="Lammps_Fix_npt.in").Fix_npt(T) 200 | run_lammps(output_dir, filedir, lammps_file="Lammps_Fix_npt.in") 201 | if os.path.isdir(dirname): 202 | for phase in T_dict: 203 | alat, blat, clat, _, _ = phase_lat[phase] 204 | average_lat_ini = alat*(1+blat+clat)/3 205 | fout_thermal = write_thermal_initialize() 206 | for T in T_dict[phase]: 207 | target_file = target_file_wrapper(filedir, dirname, phase+"_"+str(T), mode=mode) 208 | lx, ly, lz = get_output(target_file, mode=mode).grep_latt(lat_rep=lat_rep) 209 | write_thermal() 210 | fout_thermal.close() 211 | #plot_thermal_expansion("Thermal_expansion_%s_%s.dat" %(phase, mode), "Thermal_expansion_%s_%s" %(phase, mode)) 212 | 213 | 214 | # Radial distribution function for liquid at different temperature (Lammps only) 215 | def get_liquid_rdf(T_dict, lat_rep=[40,40,40], orz=[0,0,1], mode=None): 216 | dirname = "get_liquid_rdf_%s" %mode 217 | fout.write("########## Radial distribution function for liquid ##########\n") 218 | def write_RDF_initialize(): 219 | fout_RDF = open(os.path.join(filedir,"Liquid_RDF_%s_%s.dat" %(phase, mode)),"w") 220 | for T in T_dict[phase]: fout_RDF.write("#%d " %T) 221 | fout_RDF.write("\n") 222 | return fout_RDF 223 | def write_RDF_final(): 224 | for i in range(npoints): 225 | for T in T_dict[phase]: 226 | fout_RDF.write("%.3f %.3f " %(RDF_data[T][i][0], RDF_data[T][i][1])) 227 | fout_RDF.write("\n") 228 | fout_RDF.close() 229 | if mode == "Vasp": 230 | print ("RDF not supported for Vasp !!!") 231 | return 232 | RDF_data = {} 233 | if not os.path.isdir(dirname): 234 | os.mkdir(dirname) 235 | phase_lat = read_latt_file(os.path.join(filedir,"%s_latt.dat" %mode)) 236 | for phase in T_dict: 237 | alat, _, clat, _, _ = phase_lat[phase] 238 | data, NAN, box_2d = Build_Crystal(phase, alat, clat, lat_rep, orz).Build() 239 | for T in T_dict[phase]: 240 | output_dir = make_dir(os.path.join(filedir, dirname, phase+"_"+str(T))) 241 | Write_Data_wrapper(data, NAN, box_2d, output_dir=output_dir, mode=mode) 242 | get_lammps_script(Lammps_file="Lammps_liquid_RDF.in").Fix_liquid_rdf(T) 243 | run_lammps(output_dir, filedir, lammps_file="Lammps_liquid_RDF.in") 244 | if os.path.isdir(dirname): 245 | for phase in T_dict: 246 | fout_RDF = write_RDF_initialize() 247 | for T in T_dict[phase]: 248 | # Read in RDF output file 249 | target_file = os.path.join(filedir, dirname, phase+"_"+str(T), "Liquid_RDF_%d.dat" % T) 250 | _, array_list = enumerate_files(target_file, skip_line=4, delimiter=' ') 251 | # Extract column 2 (r) and 3 (g(r)) 252 | RDF_data[T] = np.array(array_list[0])[:,1:3] 253 | npoints = RDF_data[T].shape[0] 254 | write_RDF_final() 255 | #plot_RDF("Liquid_RDF_%s_%s.dat" %(phase, mode), "Liquid_RDF_%s_%s" %(phase, mode)) 256 | 257 | 258 | # Melting point Tm with 2-phase method (Lammps_serial only) 259 | # The idea is to start at the estimated melting point T1 between (Tleft,Tright) 260 | # and run nph to get the solid to melt, which means the temperature (T1) is above the melting temperature. It means the T1_new will be between (Tleft,T1). 261 | # or to get the liquid to solidify, which means the temperature (T1) is below the melting temperature. It means the T1_new will be between (T1,Tright). 262 | # In either scenario, fix/nph will bring the overall T closer to Tm 263 | # Thus one can keep on rerunning from the final temperature of the previous run till it converges 264 | def get_melting_point(phase_list, Tleft, Tright, T_error, lat_rep=[10,10,50], orz=[0,0,1], mode=None): 265 | dirname = "get_melting_temperature_%s" %mode 266 | fout.write("############ Melting temperature ############\n") 267 | if mode == "Vasp": 268 | print ("2-phase melting not supported for Vasp !!!") 269 | return 270 | if not os.path.isdir(dirname): 271 | os.mkdir(dirname) 272 | phase_lat = read_latt_file(os.path.join(filedir,"%s_latt.dat" %mode)) 273 | for phase in phase_list: 274 | alat, _, clat, _, _ = phase_lat[phase] 275 | data, NAN, box_2d = Build_Crystal(phase, alat, clat, lat_rep, orz).Build() 276 | delta_T1, T1_new = sys.float_info.max, (Tleft+Tright)/2 277 | while delta_T1 > T_error: 278 | T1 = (Tleft+Tright)/2 279 | output_dir = make_dir(os.path.join(filedir, dirname, phase+"_"+str(T1))) 280 | Write_Data_wrapper(data, NAN, box_2d, output_dir=output_dir, mode=mode) 281 | target_file = target_file_wrapper(output_dir, mode=mode) 282 | get_lammps_script(Lammps_file="Lammps_2_phase_melt.in").Two_phase_melt(T1) 283 | run_lammps(output_dir, filedir, lammps_file="Lammps_2_phase_melt.in") 284 | T1_new = get_output(target_file, mode=mode).grep_temp() 285 | if (T1_new>=T1): 286 | Tleft=T1 287 | else: 288 | Tright=T1 289 | delta_T1 = abs(Tright-Tleft)/2 290 | fout.write("Left T: %.1f K, Right T: %.1f K, delta_T: %.1f K\n" %(Tleft,Tright,delta_T1)) 291 | fout.write("delta_T = %.1f < %.1f (T_error), convergence achieved.\n" %(delta_T1,T_error)) 292 | fout.write("Melting point: %.1f +- %.1f K\n" %((Tleft+Tright)/2,T_error)) 293 | 294 | # Elastic constants 295 | def get_elastic_properties(phase_list, NSW=100, IBRION=6, ISIF=3, NFREE=2, \ 296 | lat_rep=[2,2,2], orz=[0,0,1], mode=None, use_built_in_method=False): 297 | dirname = "get_elastic_properties_%s" %mode 298 | fout.write("############ Elastic constants ############\n") 299 | deform_list = ["x", "y", "z", "yz", "xz", "xy"] 300 | delta_list_scale = np.array([0, 1e-4, -1e-4]) 301 | pxx, pyy, pzz, pyz, pxz, pxy = [np.zeros((6,3)) for _ in range(6)] 302 | # Write elastic moduli for cubic and non-cubic systems 303 | # More terms are non-zero due to reduced symmetry 304 | def write_elastic_moduli(): 305 | if phase.upper() in ["SC", "BCC", "FCC", "DC", "DIAMOND"]: 306 | fout.write("%s:\nC11 = %.3f GPa\nC12 = %.3f GPa\nC44 = %.3f GPa\n" %(phase, C[0,0], C[0,1], C[3,3])) 307 | else: 308 | fout.write("%s:\nC11 = %.3f GPa\nC12 = %.3f GPa\nC13 = %.3f GPa\nC33 = %.3f GPa\nC44 = %.3f GPa\nC55 = %.3f GPa\nC66 = %.3f GPa\n" \ 309 | %(phase, C[0,0], C[0,1], C[0,2], C[2,2], C[3,3], C[4,4], C[5,5])) 310 | fout.write("Bulk modulus = %.3f GPa\nShear modulus = %.3f GPa\nPoisson Ratio = %.3f\n" %(B, G, Po)) 311 | if not os.path.isdir(dirname): 312 | os.mkdir(dirname) 313 | phase_lat = read_latt_file(os.path.join(filedir,"%s_latt.dat" %mode)) 314 | for phase in phase_list: 315 | output_dir = make_dir(os.path.join(filedir, dirname, phase)) 316 | alat, _, clat, _, _ = phase_lat[phase] 317 | data, NAN, box_2d = Build_Crystal(phase, alat, clat, lat_rep, orz).Build() 318 | lx0, ly0, lz0 = [box_2d[i,1]-box_2d[i,0] for i in range(3)] 319 | delta_list = np.array([lx0, ly0, lz0, lz0, lz0, ly0]) 320 | for i in range(6): # Loop over six deformation paths 321 | for j in range(3): # Loop over 0, +delta, -delta 322 | runpath = "deform_%s_%f" %(deform_list[i], delta_list_scale[j]) 323 | output_dir = make_dir(os.path.join(filedir, dirname, phase, runpath)) 324 | if not use_built_in_method: NSW, IBRION, ISIF = 1, 1, 2 325 | if use_built_in_method and mode == "Lammps": 326 | get_lammps_script(Lammps_file="Lammps_Deform.in").Deform(deform=deform_list[i], \ 327 | delta=delta_list_scale[j]*delta_list[i]) 328 | Write_Data_wrapper(data, NAN, box_2d, output_dir=output_dir, mode=mode) 329 | run_lammps(output_dir, filedir, lammps_file="Lammps_Deform.in") 330 | else: 331 | if mode == "Lammps": get_lammps_script(Lammps_file="Lammps_Minimize.in").Minimize() 332 | data, NAN, box_2d = Build_Crystal(phase, alat, clat, lat_rep, orz).Build() 333 | Write_Data_wrapper(data, NAN, box_2d, output_dir=output_dir, add_tilt=deform_list[i], \ 334 | tilt_factor=delta_list_scale[j]*delta_list[i], mode=mode) 335 | submit_job_wrapper(output_dir, filedir, NSW, IBRION, ISIF, lammps_file="Lammps_Minimize.in", mode=mode) 336 | if os.path.isdir(dirname): 337 | for phase in phase_list: 338 | for i in range(6): # Loop over six deformation paths 339 | for j in range(3): # Loop over 0, +delta, -delta 340 | runpath = "deform_%s_%f" %(deform_list[i], delta_list_scale[j]) 341 | target_file = target_file_wrapper(filedir, dirname, phase, runpath, mode=mode) 342 | if mode == "Vasp" and use_built_in_method: 343 | C = get_elastic_moduli_vasp(target_file)/10 # Convert from kbar to GPa 344 | else: 345 | pxx[i,j], pyy[i,j], pzz[i,j], pyz[i,j], pxz[i,j], pxy[i,j] = \ 346 | get_output(target_file, mode=mode).grep_stress() 347 | # C[i,j] represents Cij, Bulk modulus (B), sheat modulus (G), and poisson_ratio (Po) 348 | if mode == "Vasp" and use_built_in_method: 349 | B, G, Po = (C[0,0]+C[1,1]+C[2,2]+2*C[0,1]+2*C[1,2]+2*C[0,2])/9, (C[0,0]-C[0,1])/2, 1/(1.0+C[0,0]/C[0,1]) 350 | else: 351 | C, B, G, Po = get_elastic_moduli(pxx, pyy, pzz, pyz, pxz, pxy) 352 | write_elastic_moduli() 353 | 354 | 355 | # Deformation path (Bain/trigonal path) 356 | # More details see: Mishin, Y., et al. (2001). "Structural stability and lattice defects 357 | # in copper:Ab initio, tight-binding, and embedded-atom calculations." Physical Review B 63(22). 358 | def get_deformation_path(clat_start, clat_end, npoints_c, Vol_scale=1, NSW=1, IBRION=1, ISIF=2, \ 359 | lat_rep=[1,1,1], orz=None, path_name=None, dirname=None, from_2D=False, mode=None): 360 | if not from_2D: 361 | dirname = "get_%s_path_%s" %(path_name, mode) 362 | fout.write("######## Volume-conserving %s deformation path ########\n" %path_name.lower()) 363 | phase = "FCC" 364 | # Initialize deformation path output data 365 | def get_path_initialize(filedir, path_name, mode): 366 | fout_path = open(os.path.join(filedir,"%s_%s.dat" %(path_name, mode)),"w") 367 | fout_path.write("#V c/a E\n") 368 | return fout_path 369 | if mode == "Lammps": get_lammps_script(Lammps_file="Lammps_Static.in").Static() 370 | Vol, V_cube_root = round_digits(Vol_scale), round_digits(Vol_scale**(1/3)) 371 | if from_2D or not os.path.isdir(dirname): 372 | if not from_2D: os.mkdir(dirname) 373 | phase_lat = read_latt_file(os.path.join(filedir,"%s_latt.dat" %mode)) 374 | alat0, _, clat0, _, _ = phase_lat[phase] 375 | for clat in np.linspace(clat_start,clat_end,npoints_c+1,endpoint=True): 376 | alat = get_params_alat(clat, alat0, clat0) 377 | applied_strain = [alat/alat0*V_cube_root-1, alat/alat0*V_cube_root-1, alat/alat0*clat/clat0*V_cube_root-1] 378 | clat = round_digits(clat) 379 | output_dir = make_dir(os.path.join(filedir, dirname, str(Vol)+"-"+str(clat))) 380 | data, NAN, box_2d = Build_Crystal(phase, alat0, clat0, lat_rep, orz).Build() 381 | Write_Data_wrapper(data, NAN, box_2d, output_dir=output_dir, applied_strain=applied_strain, mode=mode) 382 | submit_job_wrapper(output_dir, filedir, NSW, IBRION, ISIF, lammps_file="Lammps_Static.in", mode=mode) 383 | if not from_2D and os.path.isdir(dirname): 384 | fout_path = get_path_initialize(filedir, path_name, mode) 385 | for clat in np.linspace(clat_start,clat_end,npoints_c+1,endpoint=True): 386 | clat = round_digits(clat) 387 | target_file = target_file_wrapper(filedir, dirname, str(Vol)+"-"+str(clat), mode=mode) 388 | pe = get_output(target_file, mode=mode).grep_E_per_atom() 389 | fout_path.write("%.4f %.4f %.4f\n" %(Vol, clat, pe)) 390 | fout_path.close() 391 | #plot_Bain("%s_%s.dat" %(path_name, mode),"%s_%s" %(path_name, mode)) 392 | 393 | 394 | # Bain/trigonal deformation path 395 | def get_deformation_path_2D(clat_start, clat_end, npoints_c, Vol_start, Vol_end, npoints_V, NSW=1, IBRION=1, ISIF=2, \ 396 | lat_rep=[1,1,1], orz=None, path_name=None, mode=None): 397 | dirname = "get_%s_path_2D_%s" %(path_name, mode) 398 | fout.write("######## %s deformation path ########\n" %path_name) 399 | outfile_list = ["2D_%s_V_%s.dat" %(path_name, mode), "2D_%s_c_a_%s.dat" %(path_name, mode), \ 400 | "2D_%s_E_%s.dat" %(path_name, mode)] 401 | phase = "FCC" 402 | if not os.path.isdir(dirname): 403 | os.mkdir(dirname) 404 | phase_lat = read_latt_file(os.path.join(filedir,"%s_latt.dat" %mode)) 405 | alat0, _, clat0, _, _ = phase_lat[phase] 406 | for Vol in np.linspace(Vol_start,Vol_end,npoints_V+1,endpoint=True): 407 | Vol = round_digits(Vol) 408 | # Invoke get_deformation_path with a given volume 409 | get_deformation_path(clat_start, clat_end, npoints_c, Vol_scale=Vol, NSW=NSW, IBRION=IBRION, ISIF=ISIF,\ 410 | lat_rep=lat_rep, orz=orz, path_name=path_name, dirname=dirname, from_2D=True, mode=mode) 411 | if os.path.isdir(dirname): 412 | fout_path_X, fout_path_Y, fout_path_Z = open_files_for_write(outfile_list, filedir) 413 | for count, Vol in enumerate(np.linspace(Vol_start,Vol_end,npoints_V+1,endpoint=True)): 414 | Vol = round_digits(Vol) 415 | if count > 0: write_to_file_chars([fout_path_X,"\n"], [fout_path_Y, "\n"], [fout_path_Z, "\n"]) 416 | for clat in np.linspace(clat_start,clat_end,npoints_c+1,endpoint=True): 417 | clat = round_digits(clat) 418 | target_file = target_file_wrapper(filedir, dirname, str(Vol)+"-"+str(clat), mode=mode) 419 | pe = get_output(target_file, mode=mode).grep_E_per_atom() 420 | write_to_file_chars([fout_path_X, "%.4f " %Vol], [fout_path_Y, "%.4f " %clat],\ 421 | [fout_path_Z, "%.4f " %pe]) 422 | close_files(fout_path_X, fout_path_Y, fout_path_Z) 423 | #plot_Bain_2D(*outfile_list, "2D_%s_%s" %(path_name, mode)) 424 | 425 | 426 | # Vacancy/interstitial formation energy 427 | # lat_dim is the desired box size used to decide lat_rep 428 | def get_vacancy_interstitial_energy(phase_list, NSW=100, IBRION=1, ISIF=2, \ 429 | lat_rep=None, orz=[0,0,1], lat_dim=[12,12,12], mode=None): 430 | dirname = "get_vacancy_interstitial_energy_%s" %mode 431 | fout.write("########## Vacancy/interstitial formation energetics ########\n") 432 | if mode in ["Lammps_serial", "Lammps"]: 433 | get_lammps_script(Lammps_file="Lammps_Minimize.in").Minimize() 434 | # A utility function for calculate vacancy/interstitial energy 435 | # where pe is the total energy of the system with vacancy/intersitial 436 | # natom, ecoh are the total number of atoms and cohesive energy 437 | def get_E(pe, natom, ecoh): return pe-natom*ecoh 438 | def write_def_energy(): 439 | Evac = get_E(pe['Add_Vacancy'], natom['Add_Vacancy'], pe['Add_None']/natom['Add_None']) 440 | Eint = get_E(pe['Add_Interstitial'], natom['Add_Interstitial'], pe['Add_None']/natom['Add_None']) 441 | fout.write("%s:\nVacancy formation energy: %.3f eV\n" %(phase, Evac)) 442 | fout.write("Interstitial formation energy: %.3f eV\n" %Eint) 443 | natom, pe = {}, {} 444 | if not os.path.isdir(dirname): 445 | os.mkdir(dirname) 446 | phase_lat = read_latt_file(os.path.join(filedir,"%s_latt.dat" %mode)) 447 | for phase in phase_list: 448 | alat, blat, clat, _, _ = phase_lat[phase] 449 | lat_rep_temp = set_lat_rep(lat_rep, lat_dim, alat, alat*blat, alat*clat) 450 | data0, NAN0, box_2d0 = Build_Crystal(phase, alat, clat, lat_rep_temp, orz).Build() 451 | for def_type in ('Add_None', 'Add_Vacancy', 'Add_Interstitial'): 452 | output_dir = make_dir(os.path.join(filedir, dirname, phase+"_"+def_type)) 453 | def_instance = getattr(Introduce_Defects(nvac=1, nint=1), def_type) 454 | data, NAN, box_2d = def_instance(data=data0, NAN=NAN0, box_2d=box_2d0) 455 | Write_Data_wrapper(data, NAN, box_2d, output_dir=output_dir, mode=mode) 456 | submit_job_wrapper(output_dir, filedir, NSW, IBRION, ISIF, lammps_file="Lammps_Minimize.in", mode=mode) 457 | if os.path.isdir(dirname): 458 | for phase in phase_list: 459 | for def_type in ('Add_None', 'Add_Vacancy', 'Add_Interstitial'): 460 | target_file = target_file_wrapper(filedir, dirname, phase+"_"+def_type, mode=mode) 461 | natom[def_type], pe[def_type] = get_output(target_file, mode=mode).grep_natoms(), \ 462 | get_output(target_file, mode=mode).grep_E() 463 | write_def_energy() 464 | 465 | 466 | # Surface energy 467 | def get_surface_energy(surface_dict, NSW=100, IBRION=1, ISIF=2, \ 468 | lat_rep=None, lat_dim=[3,3,20], vac=10, mode=None): 469 | dirname = "get_surface_energy_%s" %mode 470 | fout.write(" ############ Surface energetics ############\n") 471 | if mode == "Lammps": get_lammps_script(Lammps_file="Lammps_Minimize.in").Minimize() 472 | phase_lat = read_latt_file(os.path.join(filedir,"%s_latt.dat" %mode)) 473 | if not os.path.isdir(dirname): 474 | os.mkdir(dirname) 475 | for phase in surface_dict: 476 | alat, blat, clat, _, ecoh = phase_lat[phase] 477 | for surface in surface_dict[phase]: 478 | surface_ind = ''.join([str(i) for i in surface]) 479 | output_dir = make_dir(os.path.join(filedir, dirname, phase+"_"+surface_ind)) 480 | if phase not in ["HCP", "DHCP"] or surface == (0,0,0,1): 481 | if surface == (0,0,0,1): surface = (0,0,1) 482 | lat_rep_temp = get_lat_rep_fcc(phase, alat, clat, surface, lat_rep, lat_dim) 483 | data, NAN, box_2d = Build_Crystal(phase, alat, clat, lat_rep_temp, surface, vac=vac).Build() 484 | else: 485 | surface = to_miller_indices(*surface) 486 | nlayer, lat_rep_temp = get_lat_rep_hcp(phase, alat, clat, surface, lat_rep, lat_dim) 487 | data, NAN, box_2d = Build_Crystal(phase, alat, clat, lat_rep_temp, surface, nlayer=nlayer, vac=vac).Build() 488 | Write_Data_wrapper(data, NAN, box_2d, output_dir=output_dir, mode=mode) 489 | submit_job_wrapper(output_dir, filedir, NSW, IBRION, ISIF, lammps_file="Lammps_Minimize.in", mode=mode) 490 | if os.path.isdir(dirname): 491 | for phase in surface_dict: 492 | *_, ecoh = phase_lat[phase] 493 | for surface in surface_dict[phase]: 494 | surface_ind = ''.join([str(i) for i in surface]) 495 | target_file = target_file_wrapper(filedir, dirname, phase+"_"+surface_ind, mode=mode) 496 | natom, pe = get_output(target_file, mode=mode).grep_natoms(), \ 497 | get_output(target_file, mode=mode).grep_E() 498 | a, b, _ = get_output(target_file, mode=mode).grep_latt() 499 | Esuf = get_E_surface(pe, natom, ecoh, 2*a*b) 500 | fout.write("(%s) Surface energy: %.3f mJ/m2\n" %(surface_ind, Esuf)) 501 | 502 | 503 | # Stacking fault energy 504 | def get_stacking_fault(phase, sf_mesh, NSW=100, IBRION=1, ISIF=2, mode=None): 505 | dirname = "get_stacking_fault_%s" %mode 506 | fout.write("############ Stacking fault energy ############\n") 507 | # Construct gamma surface 508 | # FCC -> X: [11-2], Y: [1-10], Z:[111] 509 | # BCC -> X: [111], Y: [-110], Z: [11-2] 510 | # HCP -> X: [11-20], Y: [10-10], Z: [0001] 511 | # A5_Beta_Sn -> X: [001], Y: [010], Z: [100] 512 | orz_map = {"FCC": [1,1,1], "BCC": [1,1,-2], "HCP": [0,0,1], "A5_Beta_Sn":[1,0,0]} 513 | # Replicate the cell by 5x10x12 for lammps, and 1x2x6 for vasp 514 | lat_rep = [5,10,12] if mode in ["Lammps_serial", "Lammps"] else [1,2,6] 515 | Esf = np.zeros((sf_mesh[0]+1,sf_mesh[1]+1)) 516 | Eusf, Essf = sys.float_info.min, sys.float_info.max 517 | outfile_list = ["SFE_X_%s.dat" %mode, "SFE_Y_%s.dat" %mode, "SFE_Z_%s.dat" %mode] 518 | if mode == "Lammps": get_lammps_script(Lammps_file="Lammps_SFE.in").Freeze_X_Y() 519 | 520 | if not os.path.isdir(dirname): 521 | os.mkdir(dirname) 522 | # Store all the stacking fault configurations in the subfolder "Configurations" 523 | file_store = make_dir(os.path.join(filedir, dirname, "Configurations")) 524 | phase_lat = read_latt_file(os.path.join(filedir,"%s_latt.dat" %mode)) 525 | alat, _, clat, _, _ = phase_lat[phase] 526 | data, NAN, box_2d = Build_Crystal(phase, alat, clat, lat_rep, orz_map[phase], vac=10).Build() 527 | Write_Data_wrapper(data, NAN, box_2d, output_dir=dirname, output_file="SFE.base", freeze_Z=True, mode=mode) 528 | input_format = "data" if mode == "Lammps" else "poscar" 529 | data, NAN, box_2d = Introduce_Defects(input_dir=dirname, input_file="SFE.base", sf_mesh=sf_mesh \ 530 | ).Stacking_fault(input_format=input_format, lat_rep=lat_rep) 531 | # Copy files to the subfolder "Configurations" 532 | output_file = "Temp.data" if mode == "Lammps" else "POSCAR" 533 | for j in range(sf_mesh[1]+1): # Y 534 | for i in range(sf_mesh[0]+1): # X 535 | output_dir = make_dir(os.path.join(filedir, dirname, "SFE_%d_%d" %(j,i))) 536 | Write_Data_wrapper(data[i,j,:,:], NAN, box_2d, output_dir=output_dir, freeze_Z=True, mode=mode) 537 | copyfile('%s/%s' %(output_dir, output_file), '%s/SFE_%d_%d' %(file_store, j, i)) 538 | submit_job_wrapper(output_dir, filedir, NSW, IBRION, ISIF, lammps_file="Lammps_SFE.in", mode=mode) 539 | if os.path.isdir(dirname): 540 | fout_SFE_X, fout_SFE_Y, fout_SFE_Z = open_files_for_write(outfile_list, filedir) 541 | for j in range(sf_mesh[1]+1): # Y 542 | if j > 0: write_to_file_chars([fout_SFE_X,"\n"], [fout_SFE_Y, "\n"], [fout_SFE_Z, "\n"]) 543 | for i in range(sf_mesh[0]+1): # X 544 | target_file = target_file_wrapper(filedir, dirname, "SFE_%d_%d" %(j,i), mode=mode) 545 | pe = get_output(target_file, mode=mode).grep_E() 546 | a, b, _ = get_output(target_file, mode=mode).grep_latt() 547 | if i==0 and j==0: pe0 = pe 548 | Esf[i,j] = get_SFE(pe, pe0, a*b) 549 | write_to_file_chars([fout_SFE_X, "%.3f " %(i/sf_mesh[0])], [fout_SFE_Y, "%.3f " %(j/sf_mesh[1])],\ 550 | [fout_SFE_Z, "%.3f " %Esf[i,j]]) 551 | # Get Eusf and Esf 552 | Eusf, Essf = get_usf_sf(sf_mesh, Esf, Eusf, Essf, i, j) 553 | fout.write("Eus = %.3f mJ/m2, Esf = %.3f mJ/m2\n" %(Eusf, Essf)) 554 | close_files(fout_SFE_X, fout_SFE_Y, fout_SFE_Z) 555 | #plot_Gamma_2D(*outfile_list, "2D_Gamma_%s" %mode) 556 | #plot_Gamma_3D(*outfile_list, "3D_Gamma_%s" %mode) 557 | 558 | 559 | def get_general_stacking_fault(phase, sf_mesh, NSW=100, IBRION=1, ISIF=2, mode=None): 560 | dirname = "get_general_stacking_fault_%s" %mode 561 | fout.write("############ General stacking fault energy ############\n") 562 | orz_map = {"FCC": [1,1,1], "BCC": [1,1,-2], "HCP": [0,0,1],"A5_Beta_Sn":[1,0,0]} 563 | lat_rep = [5,10,12] if mode == "Lammps" else [1,2,6] 564 | Esf = np.zeros((sf_mesh[0]+1,sf_mesh[1]+1)) 565 | Eusf, Essf, Eutf = sys.float_info.min, sys.float_info.max, sys.float_info.min 566 | # Initialize Bain path output data 567 | def get_GSFE_initialize(filedir, mode): 568 | fout_GSFE = open(os.path.join(filedir,"GSFE_%s.dat" %mode),"w") 569 | fout_GSFE.write("X E_slip E_twin\n") 570 | return fout_GSFE 571 | def write_GSFE_final(): 572 | for i in range(sf_mesh[0]+1): 573 | fout_GSFE.write("%.3f %.3f %.3f\n" %(i/sf_mesh[0], Esf[i,0], Esf[i,1])) 574 | fout_GSFE.close() 575 | fout.write("Eus = %.3f mJ/m2, Esf = %.3f mJ/m2, Eutf = %.3f mJ/m2\n" %(Eusf, Essf, Eutf)) 576 | if mode == "Lammps": get_lammps_script(Lammps_file="Lammps_SFE.in").Freeze_X_Y() 577 | if not os.path.isdir(dirname): 578 | os.mkdir(dirname) 579 | # Store all the stacking fault configurations in the subfolder "Configurations" 580 | file_store = make_dir(os.path.join(filedir,dirname,"Configurations")) 581 | phase_lat = read_latt_file(os.path.join(filedir,"%s_latt.dat" %mode)) 582 | alat, _, clat, _, _ = phase_lat[phase] 583 | # Construct surface 584 | data, NAN, box_2d = Build_Crystal(phase, alat, clat, lat_rep, orz_map[phase], vac=10).Build() 585 | Write_Data_wrapper(data, NAN, box_2d, output_dir=dirname, output_file="SFE.base", freeze_Z=True, mode=mode) 586 | input_format = "data" if mode == "Lammps" else "poscar" 587 | # Stacking fault (Full slip) 588 | data, NAN, box_2d = Introduce_Defects(input_dir=dirname, input_file="SFE.base", sf_mesh=sf_mesh, \ 589 | shift_plane=0).Stacking_fault(input_format=input_format, lat_rep=lat_rep) 590 | # Copy files to the subfolder "Configurations" 591 | output_file = "Temp.data" if mode == "Lammps" else "POSCAR" 592 | for i in range(sf_mesh[0]+1): # X 593 | output_dir = make_dir(os.path.join(filedir, dirname, "SFE.slip.%d" %i)) 594 | Write_Data_wrapper(data[i,0,:,:], NAN, box_2d, output_dir=output_dir, freeze_Z=True, mode=mode) 595 | copyfile('%s/%s' %(output_dir, output_file), '%s/SFE.slip.%d' %(file_store, i)) 596 | submit_job_wrapper(output_dir, filedir, NSW, IBRION, ISIF, lammps_file="Lammps_SFE.in", mode=mode) 597 | Write_Data_wrapper(data[int(sf_mesh[0]/3),0,:,:], NAN, box_2d, output_dir=dirname, \ 598 | output_file="SFE.base.I1", freeze_Z=True, mode=mode) 599 | # Twin growth 600 | for k in range(1,3): # Different planes 601 | data, NAN, box_2d = Introduce_Defects(input_dir=dirname, input_file="SFE.base.I1", sf_mesh=[int(sf_mesh[0]/3),1], \ 602 | shift_plane=k).Stacking_fault(input_format=input_format, lat_rep=lat_rep) 603 | for i in range(1,int(sf_mesh[0]/3)+1): # X 604 | j = int(sf_mesh[0]/3)*k+i 605 | output_dir = make_dir(os.path.join(filedir,dirname,"SFE.twin.%d" %j)) 606 | Write_Data_wrapper(data[i,0,:,:], NAN, box_2d, output_dir=output_dir, freeze_Z=True, mode=mode) 607 | copyfile('%s/%s' %(output_dir, output_file), '%s/SFE.twin.%d' %(file_store, j)) 608 | submit_job_wrapper(output_dir, filedir, NSW, IBRION, ISIF, lammps_file="Lammps_SFE.in", mode=mode) 609 | copyfile('%s/%s' %(output_dir, output_file), '%s/SFE.base.I1' %dirname) 610 | 611 | if os.path.isdir(dirname): 612 | fout_GSFE = get_GSFE_initialize(filedir, mode) 613 | for i in range(sf_mesh[0]+1): # X 614 | target_file = target_file_wrapper(filedir, dirname, "SFE.slip.%d" %i, mode=mode) 615 | pe = get_output(target_file, mode=mode).grep_E() 616 | a, b, _ = get_output(target_file, mode=mode).grep_latt() 617 | if i==0: pe0 = pe 618 | Esf[i,0] = get_SFE(pe, pe0, a*b) 619 | Esf[i,1] = Esf[i,0] 620 | Eusf, Essf = get_usf_sf(sf_mesh, Esf, Eusf, Essf, i, 0) 621 | # Twin growth 622 | for k in range(1,3): # Different planes 623 | for i in range(1,int(sf_mesh[0]/3)+1): # X 624 | j = int(sf_mesh[0]/3)*k+i 625 | target_file = target_file_wrapper(filedir, dirname, "SFE.twin.%d" %j, mode=mode) 626 | pe = get_output(target_file, mode=mode).grep_E() 627 | a, b, _ = get_output(target_file, mode=mode).grep_latt() 628 | Esf[i+k*int(sf_mesh[0]/3),1] = get_SFE(pe, pe0, a*b) 629 | # Get Eutf 630 | Eutf = get_utf(sf_mesh, Esf, Eutf) 631 | write_GSFE_final() 632 | plot_GSFE("GSFE_%s.dat" %mode,"GSFE_%s" %mode) 633 | 634 | 635 | # Uniaxial deformation (Lammps_serial only) 636 | # Perform uniaxial deformation along desired direction with erate/trate via a fix/deform command 637 | def uniaxial_deform(phase_list, temp, deform_mode , rate, final_strain,lat_rep, orz=[0,0,1], mode=None): 638 | dirname = "uniaxial_deform_%s" %mode 639 | fout.write("############ Uniaxial deformation ############\n") 640 | if mode == "Vasp": 641 | print ("Uniaxial deformation not supported for Vasp !!!") 642 | return 643 | if not os.path.isdir(dirname): 644 | os.mkdir(dirname) 645 | phase_lat = read_latt_file(os.path.join(filedir,"%s_latt.dat" %mode)) 646 | for phase in phase_list: 647 | alat, _, clat, _, _ = phase_lat[phase] 648 | data, NAN, box_2d = Build_Crystal(phase, alat, clat, lat_rep, orz).Build() 649 | for d_mode in deform_mode: 650 | output_dir = make_dir(os.path.join(filedir, dirname, phase+"_"+str(temp)+"_"+str(d_mode)+"_"+str(rate))) 651 | Write_Data_wrapper(data, NAN, box_2d, output_dir=output_dir, mode=mode) 652 | target_file = target_file_wrapper(output_dir, mode=mode) 653 | get_lammps_script(Lammps_file="Lammps_Uniaxial_Deform.in").uniaxial_deform(phase,temp,d_mode,rate,final_strain) 654 | run_lammps(output_dir, filedir, lammps_file="Lammps_Uniaxial_Deform.in") 655 | current_file= os.getcwd() 656 | copyfile('%s/Stress_strain_%s_%s.txt' %(output_dir,d_mode,phase),'%s/Stress_strain_%s_%s.txt' %(current_file,d_mode,phase)) 657 | plot_stress_strain('%s/Stress_strain_%s_%s.txt' %(current_file,d_mode,phase),d_mode, 'Stress_strain_%s_%s' %(d_mode,phase)) 658 | if __name__ == "__main__": 659 | main() 660 | -------------------------------------------------------------------------------- /main/Lava_Config.py: -------------------------------------------------------------------------------- 1 | ############################################################################### 2 | # Config file for Lava Wrapper 3 | # Includes the lammps executable name and potential file name 4 | ############################################################################### 5 | 6 | lammps_executable = "lmp_mpi" 7 | element = "Al" 8 | potential_file = ["Al99.eam.alloy"] 9 | 10 | 11 | potential_file_description = \ 12 | "pair_style eam/alloy\n" + \ 13 | "pair_coeff * * %s %s\n" %(potential_file[0], element) 14 | 15 | 16 | #potential_file_description = \ 17 | #"pair_style meam/c\n" + \ 18 | #"pair_coeff * * %s %s %s %s\n" %(potential_file[0],element,potential_file[1],element) 19 | 20 | 21 | -------------------------------------------------------------------------------- /main/Lava_Generator.py: -------------------------------------------------------------------------------- 1 | ############################################################################### 2 | # This module provides classes for generating bulk and defect configurations 3 | # for performing the calculations 4 | # Supports three file formats: lammps data file, POSCAR file, and extended xyz file 5 | ############################################################################### 6 | 7 | import numpy as np 8 | import os 9 | from os import sys 10 | import math 11 | import random 12 | import re 13 | from numpy.linalg import inv 14 | from Lava_Config import * 15 | 16 | ############################################################################### 17 | 18 | class Build_Crystal: 19 | def __init__(self, phase, alat, clat, lat_rep, orz, nlayer=24, vac=0, d_tol=0.01, verbose=False): 20 | self.phase = phase 21 | self.alat = alat 22 | self.clat = clat 23 | self.lat_rep = lat_rep 24 | self.orz = orz 25 | self.nlayer = nlayer 26 | self.vac = vac 27 | self.d_tol = d_tol 28 | self.verbose = verbose 29 | self.nx, self.ny, self.nz = self.lat_rep 30 | 31 | def Build(self): 32 | phase_list = ["SC", "BCC", "FCC", "HCP", "DC", "DIAMOND", "DHCP", \ 33 | "W", "OMEGA", "A5_BETA_SN", "A5_GAMMA_SN", "A15_BETA_W", "L12", "9R"] 34 | if self.phase.upper() in phase_list: 35 | if self.phase.upper() == "DIAMOND": self.phase = "DC" 36 | if self.phase.upper() == "OMEGA": self.phase = "W" 37 | ncell,basis,atype,cube = getattr(self, "Build_"+self.phase)() 38 | else: 39 | print ("{0} is not implemented.".format(self.phase)) 40 | sys.exit() 41 | angle,nrep,zrep = self.Calculate_Angle() 42 | data_rep = self.Replicate_Cell(ncell,basis*cube,atype,cube,nrep,nrep,nrep) 43 | data_build, NAN, box = self.Build_Cell(data_rep,angle,zrep) 44 | data = self.Replicate_Cell(NAN,data_build[:,-3:],data_build[:,1],box,self.nx,self.ny,self.nz) 45 | data[:,-3:] = data[:,-3:] * self.alat 46 | # Add surface, half on each side 47 | data[:,-1] = data[:,-1] + self.vac/2 48 | NAN *= self.nx*self.ny*self.nz 49 | box_2d = np.zeros((3,2)) 50 | box_2d[:,1] = np.multiply(box, [self.alat*self.nx,self.alat*self.ny,self.alat*self.nz]) + [0,0,self.vac] 51 | return (data, NAN, box_2d) 52 | 53 | def Calculate_Angle(self): 54 | nrep, zrep = 20, True 55 | blat, multilplier = 1, 1 56 | if self.phase.upper() in ["HCP", "DHCP", "W", "OMEGA"]: 57 | blat, multilplier = 1.73205, 2 58 | if self.orz[0] != 0 or self.orz[1] != 0: 59 | nrep, zrep = math.ceil(self.nlayer/math.sqrt(self.orz[0]**2+self.orz[1]**2+self.orz[2]**2)), False 60 | assert self.nlayer > 0, "Number of layers must be defined for HCP along none-[001] direction" 61 | if self.nz > 1: 62 | self.nz = 1 63 | print ("Warning: No replication allowed for HCP along none-[001] direction") 64 | if self.orz[2] != 0: 65 | angle = math.atan(self.clat*np.sqrt(self.orz[0]**2+(self.orz[1]/blat)**2)/self.orz[2]*multilplier) 66 | else: 67 | angle = math.pi/2 68 | return (angle, nrep, zrep) 69 | 70 | def get_atype(self,ncell): 71 | return np.array([1]*ncell,dtype=np.int) 72 | 73 | def Build_SC(self): 74 | ncell = 1 75 | basis = np.array([0,0,0]) 76 | cube = np.array([1,1,self.clat]) 77 | return (ncell,basis,self.get_atype(ncell),cube) 78 | 79 | def Build_BCC(self): 80 | ncell = 2 81 | basis = np.array([[0,0,0], [0.5,0.5,0.5]]) 82 | cube = np.array([1,1,self.clat]) 83 | return (ncell,basis,self.get_atype(ncell),cube) 84 | 85 | def Build_FCC(self): 86 | ncell = 4 87 | basis = np.array([[0,0,0], [0,0.5,0.5], [0.5,0,0.5],[0.5,0.5,0]]) 88 | cube = np.array([1,1,self.clat]) 89 | return (ncell,basis,self.get_atype(ncell),cube) 90 | 91 | def Build_HCP(self): 92 | ncell = 4 93 | basis = np.array([[0,0,0], [0.5,0.5,0], [0.5,5./6,0.5],[0,1./3,0.5]]) 94 | cube = np.array([1,1.73205,self.clat]) 95 | return (ncell,basis,self.get_atype(ncell),cube) 96 | 97 | def Build_DC(self): 98 | ncell = 8 99 | basis = np.array([[0,0,0], [0,0.5,0.5], [0.5,0,0.5],[0.5,0.5,0],\ 100 | [0.25,0.25,0.25], [0.25,0.75,0.75], [0.75,0.25,0.75], [0.75,0.75,0.25]]) 101 | cube = np.array([1,1,self.clat]) 102 | return (ncell,basis,self.get_atype(ncell),cube) 103 | 104 | def Build_DHCP(self): 105 | ncell = 8 106 | basis = np.array([[0,0,0], [0.5,0.5,0], [0.5,5./6,0.25],[0,1./3,0.25],\ 107 | [0,0,0.5], [0.5,0.5,0.5], [0.5,1./6,0.75], [0,2./3,0.75]]) 108 | cube = np.array([1,1.73205,self.clat]) 109 | return (ncell,basis,self.get_atype(ncell),cube) 110 | 111 | def Build_W(self): 112 | ncell = 6 113 | basis = np.array([[0,0,0], [0.5,0.5,0], [0.5,1./6,0.5],\ 114 | [0,1./3,0.5], [0,2./3,0.5], [0.5,5./6,0.5]]) 115 | cube = np.array([1,1.73205,self.clat]) 116 | return (ncell,basis,self.get_atype(ncell),cube) 117 | 118 | def Build_A5_Beta_Sn(self): 119 | ncell = 4 120 | basis = np.array([[0,0,0], [0.5,0.5,0.5], [0,0.5,0.25], [0.5,0,0.75]]) 121 | cube = np.array([1,1,self.clat]) 122 | return (ncell,basis,self.get_atype(ncell),cube) 123 | 124 | def Build_A5_Gamma_Sn(self): 125 | ncell = 4 126 | basis = np.array([[0,0,0], [0.5,0,0], [0,0.5,0.5], [0.5,0.5,0.5]]) 127 | cube = np.array([1,0.9,self.clat]) 128 | return (ncell,basis,self.get_atype(ncell),cube) 129 | 130 | def Build_A15_Beta_W(self): 131 | ncell = 8 132 | basis = np.array([[0,0,0], [0.5,0.25,0], [0.5,0.75,0],[0,0.5,0.25],\ 133 | [0.5,0.5,0.5], [0.25,0,0.5], [0.75,0,0.5], [0,0.5,0.75]]) 134 | cube = np.array([1,1,self.clat]) 135 | return (ncell,basis,self.get_atype(ncell),cube) 136 | 137 | def Build_L12(self): 138 | ncell = 3 139 | basis = np.array([[0,0.5,0.5], [0.5,0,0.5],[0.5,0.5,0]]) 140 | cube = np.array([1,1,self.clat]) 141 | return (ncell,basis,self.get_atype(ncell),cube) 142 | 143 | def Build_9R(self): 144 | ncell = 18 145 | basis = np.array([[0,0,0], [0.5,0.5,0], [1./6,0.5,1./9],[2./3,0,1./9],\ 146 | [1./3,0,2./9], [5./6,0.5,2./9], [2./3,0,3./9], [1./6,0.5,3./9],\ 147 | [5./6,0.5,4./9], [1./3,0,4./9], [0,0,5./9], [0.5,0.5,5./9],\ 148 | [1./3,0,6./9], [5./6,0.5,6./9], [0.5,0.5,7./9], [0,0,7./9],\ 149 | [2./3,0,8./9], [1./6,0.5,8./9]]) 150 | cube = np.array([1,1/1.73205,self.clat]) 151 | return (ncell,basis,self.get_atype(ncell),cube) 152 | 153 | # Replicate the cell by nx * ny * nz times 154 | def Replicate_Cell(self,ncell,basis,atype,cube,nrep_x,nrep_y,nrep_z): 155 | NAN = ncell*nrep_x*nrep_y*nrep_z 156 | data_rep = np.zeros((NAN,5)) 157 | data_rep[:ncell,-3:] = basis 158 | for i in range(nrep_x): 159 | for j in range(nrep_y): 160 | for k in range(nrep_z): 161 | inc = i*nrep_y*nrep_z + j*nrep_z + k 162 | data_rep[inc*ncell:(inc+1)*ncell,1] = atype[:] 163 | data_rep[inc*ncell:(inc+1)*ncell,-3:] = data_rep[:ncell,-3:] + np.multiply(cube, np.array([i, j, k])) 164 | return (data_rep) 165 | 166 | # Cut an orthorgonal box based on search_id, xlen_id, ylen_id, zlen_id 167 | def Build_Cell(self,data_rep,angle,zrep=True): 168 | data_build, NAN, atom_layer, search_id, xlen_id, ylen_id, zlen_id, \ 169 | = self.Find_Orthogonal_Cell(data_rep,angle,zrep) 170 | x0, y0, z0 = data_build[search_id,-3], data_build[search_id,-2], data_build[search_id,-1] 171 | xlo, xhi = x0-self.d_tol, data_build[xlen_id,-3]-self.d_tol 172 | ylo, yhi = y0-self.d_tol, data_build[ylen_id,-2]-self.d_tol 173 | zlo, zhi = z0-self.d_tol, data_build[zlen_id,-1]-self.d_tol 174 | if self.phase.upper() == "DHCP": 175 | zhi = zhi + zhi - zlo 176 | elif self.phase.upper() == "9R": 177 | zhi = zhi + (zhi-zlo)/5*4 178 | elif self.phase.upper() == "A5_GAMMA_SN": 179 | xhi = xhi + xhi - xlo 180 | atom_count = 0 181 | # By default, xlo, ylo and zlo are set to 0 182 | for i in range(NAN): 183 | if data_build[i,-3] > xlo and data_build[i,-3] < xhi and data_build[i,-2] > ylo \ 184 | and data_build[i,-2] < yhi and data_build[i,-1] > zlo and data_build[i,-1] < zhi: 185 | data_build[atom_count,0] = atom_count + 1 186 | data_build[atom_count,1] = data_build[i,1] 187 | data_build[atom_count,2:] = data_build[i,2:] - [x0,y0,z0] 188 | atom_count+=1 189 | box = [xhi-xlo, yhi-ylo, zhi-zlo] 190 | return (data_build[:atom_count,:], atom_count, box) 191 | 192 | # Build the cell by constructing a orthogonal box 193 | def Find_Orthogonal_Cell(self,data_rep,angle,zrep): 194 | data_build = self.Rotate_Cell(data_rep,angle) 195 | data_build = data_build[np.argsort(data_build[:, -1])] 196 | y_min, y_max = np.amin(data_build[:,-2],axis=0), np.amax(data_build[:,-2],axis=0) 197 | y_cen = (y_min+y_max)/2 198 | NAN = data_build.shape[0] 199 | atom_layer = np.zeros(NAN,dtype=np.int) 200 | atom_sorted, current_layer, z_min = 0, 0, data_build[0,-1] 201 | # Sort the atom to different layers based on Z coordinates 202 | for i in range(NAN): 203 | dz = np.abs(data_build[i,-1]-z_min) 204 | if dz < self.d_tol: 205 | atom_layer[i] = current_layer + 1 206 | atom_sorted+=1 207 | else: 208 | current_layer+=1 209 | atom_layer[i] = current_layer + 1 210 | z_min = data_build[i,-1] 211 | if self.verbose: 212 | print ("All atoms sorted: {0} layers located!".format(current_layer)) 213 | print ("Now searching for periodic images of atoms in X, Y and Z direction") 214 | search_id, search_layer = np.argmin(data_build[:,-1],axis=0), 1 215 | for j in range(NAN): 216 | # x0, y0, z0 defines the origin for the search 217 | x0, y0, z0 = data_build[search_id,-3], data_build[search_id,-2], data_build[search_id,-1] 218 | search_x_bound = sys.float_info.max 219 | if self.verbose: 220 | print ("Searching start from atom {0}: {1},{2},{3} in layer {4}".format(search_id,x0,y0,z0,search_layer)) 221 | image_x, image_y, image_z, xlen_id, ylen_id, zlen_id = \ 222 | self.Image_Search(data_build,atom_layer,search_layer,NAN,x0,y0,z0,zrep) 223 | if image_x and image_y and image_z: 224 | if self.verbose: 225 | print ("Image atom {0} found in X direction: {1},{2},{3}"\ 226 | .format(xlen_id,data_build[xlen_id,-3],data_build[xlen_id,-2],data_build[xlen_id,-1])) 227 | print ("Image atom {0} found in Y direction: {1},{2},{3}"\ 228 | .format(ylen_id,data_build[ylen_id,-3],data_build[ylen_id,-2],data_build[ylen_id,-1])) 229 | if zrep: 230 | print ("Image atom {0} found in Z direction: {1},{2},{3}"\ 231 | .format(zlen_id,data_build[zlen_id,-3],data_build[zlen_id,-2],data_build[zlen_id,-1])) 232 | else: 233 | print ("{0} layers cut in Z direction".format(self.nlayer)) 234 | break 235 | else: 236 | search_layer+=1 237 | if search_layer > current_layer: 238 | break 239 | for i in range(NAN): 240 | if atom_layer[i] == search_layer: 241 | if data_build[i,-3] < search_x_bound and np.abs(data_build[i,-2]-y_cen) < 2*self.alat: 242 | search_x_bound = data_build[i,-3] 243 | search_id = i 244 | if not (image_x and image_y and image_z): 245 | print ("No unit cell found!: {0}, {1}, {2}".format(image_x, image_y, image_z)) 246 | sys.exit() 247 | return (data_build, NAN, atom_layer, search_id, xlen_id, ylen_id, zlen_id) 248 | 249 | # Rotate the coordinates based on orz 250 | def Rotate_Cell(self,data_rep,angle): 251 | if self.verbose: 252 | print ("Rotation angle = {0} --> {1} degree".format(angle,angle*180/math.pi)) 253 | #Construct the Rodrigues rotation formula 254 | #[0 -rz ry 255 | #rz 0 -rx 256 | #-ry rx 0] 257 | if self.orz[0] != 0 or self.orz[1] != 0: 258 | orz_R = math.sqrt(self.orz[0]**2+self.orz[1]**2) 259 | orz_norm = np.array([self.orz[1], -self.orz[0], 0])/orz_R 260 | rx,ry,rz = orz_norm[0], orz_norm[1], orz_norm[2] 261 | Q_s = np.array([[0,-rz,ry], [rz,0,-rx], [-ry,rx,0]]) 262 | Q = np.eye(3) + math.sin(angle)*Q_s + 2*(math.sin(angle/2))**2*(Q_s.dot(Q_s)) 263 | Q_r = np.array([[-ry,rx,0], [rx,ry,0], [0,0,1]]) 264 | else: 265 | Q, Q_r = np.eye(3), np.eye(3) 266 | if self.verbose: 267 | print ("Rotation matrix Q = [[{0},{1},{2}], [{3},{4},{5}], [{6},{7},{8}]]"\ 268 | .format(Q[0,0],Q[0,1],Q[0,2],Q[1,0],Q[1,1],Q[1,2],Q[2,0],Q[2,1],Q[2,2])) 269 | print ("Rotation matrix Q_r= [[{0},{1},{2}], [{3},{4},{5}], [{6},{7},{8}]]"\ 270 | .format(Q_r[0,0],Q_r[0,1],Q_r[0,2],Q_r[1,0],Q_r[1,1],Q_r[1,2],Q_r[2,0],Q_r[2,1],Q_r[2,2])) 271 | data_build = np.zeros((data_rep.shape[0],5)) 272 | data_build[:,:2] = data_rep[:,:2] 273 | data_build[:,-3:] = data_rep[:,-3:].dot(Q).dot(inv(Q_r)) 274 | return (data_build) 275 | 276 | # Search for image atoms along all dimensions 277 | def Image_Search(self,data_build,atom_layer,search_layer,NAN,x0,y0,z0,zrep): 278 | image_x, image_y, image_z = False, False, False 279 | xlen_id, ylen_id, zlen_id = 0, 0, 0 280 | xlen, ylen, zlen = sys.float_info.max, sys.float_info.max, sys.float_info.max 281 | for i in range(NAN): 282 | dx, dy, dz = np.abs(data_build[i,-3]-x0), np.abs(data_build[i,-2]-y0), np.abs(data_build[i,-1]-z0) 283 | if atom_layer[i] == search_layer: 284 | if data_build[i,-3] > x0 and dx > self.d_tol and dy < self.d_tol and dz < self.d_tol: 285 | if xlen > dx: 286 | xlen, xlen_id, image_x = dx, i, True 287 | if data_build[i,-2] > y0 and dx < self.d_tol and dy > self.d_tol and dz < self.d_tol: 288 | if ylen > dy: 289 | ylen, ylen_id, image_y = dy, i, True 290 | if zrep: # non-hcp, and hcp along none-[0001] 291 | if atom_layer[i] > search_layer and data_build[i,-1] > z0 and dx < self.d_tol and dy < self.d_tol and dz > self.d_tol: 292 | if zlen > dz: 293 | zlen, zlen_id, image_z = dz, i, True 294 | else: # others 295 | if atom_layer[i]-search_layer == self.nlayer: 296 | if zlen > dz: 297 | zlen, zlen_id, image_z = dz, i, True 298 | return(image_x,image_y,image_z,xlen_id,ylen_id,zlen_id) 299 | 300 | 301 | class Introduce_Defects: 302 | def __init__(self, input_dir=None, input_file=None, nvac=1, nint=1, sf_mesh=[12,12], \ 303 | shift_plane=0, Rc=3.46, max_neigh=50, verbose=False): 304 | self.input_dir = input_dir 305 | self.input_file = input_file 306 | self.nvac = nvac 307 | self.nint = nint 308 | self.sf_mesh = sf_mesh 309 | self.shift_plane = shift_plane 310 | self.Rc = Rc 311 | self.max_neigh = max_neigh 312 | self.verbose = verbose 313 | 314 | def Read_Input_Data(self): 315 | filename = os.path.join(self.input_dir,'%s' % self.input_file) 316 | infile = open(filename, "r") 317 | header, num_header = [], 9 318 | for i, line in enumerate(infile): 319 | header.append(line) 320 | if i == num_header-1: break 321 | NAN = int(np.fromstring(header[1],sep=' ',count=1)) 322 | data = np.zeros((NAN,5)) 323 | for i,line in enumerate(infile): 324 | data[i,:] = np.fromstring(line, sep=' ', count=5) 325 | XL, XH = float(re.split('\s+', header[3])[1]), float(re.split('\s+', header[3])[2]) 326 | YL, YH = float(re.split('\s+', header[4])[1]), float(re.split('\s+', header[4])[2]) 327 | ZL, ZH = float(re.split('\s+', header[5])[1]), float(re.split('\s+', header[5])[2]) 328 | box_2d = np.array([[XL,XH], [YL,YH], [ZL,ZH]]) 329 | return (data, NAN, box_2d) 330 | 331 | def Read_Input_POSCAR(self, freeze_Z=False): 332 | filename = os.path.join(self.input_dir,'%s' % self.input_file) 333 | infile = open(filename, "r") 334 | header = [] 335 | num_header = 8 if freeze_Z else 7 336 | for i, line in enumerate(infile): 337 | header.append(line) 338 | if i == num_header-1: 339 | break 340 | NAN = int(np.fromstring(header[5],sep=' ',count=1)) 341 | data = np.zeros((NAN,5)) 342 | for i,line in enumerate(infile): 343 | data[i,:2] = i+1, 1 344 | data[i,2:] = np.fromstring(line, sep=' ', count=3) 345 | XL, XH = 0, float(re.split('\s+', header[2])[1]) 346 | YL, YH = 0, float(re.split('\s+', header[3])[2]) 347 | ZL, ZH = 0, float(re.split('\s+', header[4])[3]) 348 | box_2d = np.array([[XL,XH], [YL,YH], [ZL,ZH]]) 349 | return (data, NAN, box_2d) 350 | 351 | def neigh_build_wrapper(self, data, NAN, box_2d): 352 | XL, XH, YL, YH, ZL, ZH = box_2d.flatten() 353 | XBIN, YBIN, ZBIN = int(math.floor((XH-XL)/self.Rc)), int(math.floor((YH-YL)/self.Rc)), \ 354 | int(math.floor((ZH-ZL)/self.Rc)) 355 | max_atoms = max(3*int((NAN/(XBIN*YBIN*ZBIN))),20) 356 | if self.verbose: 357 | XBINSIZE, YBINSIZE, ZBINSIZE = (XH-XL)/XBIN, (YH-YL)/YBIN, (ZH-ZL)/ZBIN 358 | print ("For link cell, bin size = {0}, {1}, {2}, number of bins = {3}, {4}, {5} in x, y and z direction." \ 359 | .format(XBINSIZE,YBINSIZE,ZBINSIZE,XBIN,YBIN,ZBIN)) 360 | neigh_list, neigh_id_list, neigh_count = self.neigh_build(data,NAN,XL,XH,XBIN,YL,YH,YBIN,ZL,ZH,ZBIN,max_atoms) 361 | return (data, NAN, box_2d, neigh_list, neigh_id_list, neigh_count) 362 | 363 | # Introduce vacancies 364 | def Add_Vacancy(self, input_format='data', data=None, NAN=None, box_2d=None): 365 | if NAN: 366 | pass 367 | elif input_format == 'data': 368 | data, NAN, box_2d = self.Read_Input_Data() 369 | elif input_format == 'poscar': 370 | data, NAN, box_2d = self.Read_Input_POSCAR() 371 | else: 372 | print ("Wrong input type!") 373 | sys.exit() 374 | atom_delete = np.zeros(NAN, dtype=bool) 375 | atom_delete[random.sample(range(1, NAN), self.nvac)] = True 376 | data_out = np.zeros((NAN-self.nvac,5)) 377 | atom_count = 0 378 | for i in range(NAN): 379 | if not atom_delete[i]: 380 | data_out[atom_count,:] = data[i,:] 381 | atom_count+=1 382 | return (data_out, atom_count, box_2d) 383 | 384 | # Introduce interstitials 385 | def Add_Interstitial(self, input_format=None, data=None, NAN=None, box_2d=None): 386 | if NAN: 387 | data, NAN, box_2d, neigh_list, neigh_id_list, neigh_count = self.neigh_build_wrapper(data, NAN, box_2d) 388 | elif input_format == 'data': 389 | data, NAN, box_2d, neigh_list, neigh_id_list, neigh_count = self.neigh_build_wrapper(self.Read_Input_Data()) 390 | elif input_format == 'poscar': 391 | data, NAN, box_2d, neigh_list, neigh_id_list, neigh_count = self.neigh_build_wrapper(self.Read_Input_POSCAR()) 392 | else: 393 | print ("Wrong input type!") 394 | sys.exit() 395 | atom_insert = np.zeros(NAN, dtype=bool) 396 | atom_insert[random.sample(range(NAN), self.nint)] = True 397 | data_out = np.zeros((NAN+self.nint,5)) 398 | data_out[:NAN,:] = data[:,:] 399 | atom_count = NAN 400 | box_len = [box_2d[0,1] - box_2d[0,0], box_2d[1,1] - box_2d[1,0], box_2d[2,1] - box_2d[2,0]] 401 | for i in range(NAN): 402 | if atom_insert[i]: 403 | j = neigh_id_list[i,random.randint(1,neigh_count[i])] 404 | dd = data[i,:] - data[j,:] 405 | for k in range(2,5): 406 | if abs(dd[k]) < box_len[k-2]/2: 407 | data_out[atom_count,k] = (data[i,k] + data[j,k])/2 408 | else: 409 | data_out[atom_count,k] = (data[i,k] + data[j,k] - box_len[k-2])/2 410 | if data_out[atom_count,k] < 0: data_out[atom_count,k] += box_len[k-2] 411 | data_out[atom_count,0], data_out[atom_count,1] = atom_count, 1 412 | atom_count+=1 413 | if atom_count == NAN+self.nint: break 414 | return (data_out, atom_count, box_2d) 415 | 416 | def Add_None(self, data=None, NAN=None, box_2d=None): 417 | return (data, NAN, box_2d) 418 | 419 | # Displace to generate stacking fault 420 | def Stacking_fault(self, input_format='data', lat_rep=[1,1,1], freeze_Z=True): 421 | # x [1 1 -2], y [1 -1 0], z [1 1 1] 422 | # dx = math.sqrt(6)/2*alat, dy = math.sqrt(2)*alat 423 | if input_format == 'data': 424 | data, NAN, box_2d = self.Read_Input_Data() 425 | elif input_format == 'poscar': 426 | data, NAN, box_2d = self.Read_Input_POSCAR(freeze_Z=True) 427 | else: 428 | print ("Wrong input type!") 429 | sys.exit() 430 | XL, XH, YL, YH, ZL, ZH = box_2d.flatten() 431 | box_len = [XH-XL, YH-YL, ZH-ZL] 432 | z_cutoff = (ZL+ZH)/2-0.1 + self.shift_plane*(YH-YL)/lat_rep[1]*math.sqrt(2)/math.sqrt(3) 433 | dx, dy = -(XH-XL)/lat_rep[0]/self.sf_mesh[0], (YH-YL)/lat_rep[1]/self.sf_mesh[1] 434 | if self.shift_plane > 0: 435 | dx, dy = dx/3, dy/3 436 | data_out = np.zeros((self.sf_mesh[0]+1,self.sf_mesh[1]+1,NAN,5)) 437 | for i in range(self.sf_mesh[0]+1): 438 | for j in range(self.sf_mesh[1]+1): 439 | data_out[i,j,:,:] = data[:,:] 440 | for k in range(NAN): 441 | if data[k,-1] > z_cutoff: 442 | data_out[i,j,k,-3] += i*dx 443 | data_out[i,j,k,-2] += j*dy 444 | for kk in range(-3,-1): 445 | if data_out[i,j,k,kk] < box_2d[kk,0]: 446 | data_out[i,j,k,kk] += box_len[kk] 447 | elif data_out[i,j,k,kk] > box_2d[kk,1]: 448 | data_out[i,j,k,kk] -= box_len[kk] 449 | return (data_out, NAN, box_2d) 450 | 451 | # Build linked cells in all direction 452 | def linked_cell(self,data,NAN,XL,XH,XBIN,YL,YH,YBIN,ZL,ZH,ZBIN,max_atoms): 453 | CELL_X = np.linspace(XL,XH,XBIN+1,endpoint=True) 454 | CELL_Y = np.linspace(YL,YH,YBIN+1,endpoint=True) 455 | CELL_Z = np.linspace(ZL,ZH,ZBIN+1,endpoint=True) 456 | Bin_count = np.zeros((XBIN,YBIN,ZBIN),dtype=np.int) 457 | Bin_list = np.zeros((XBIN,YBIN,ZBIN,max_atoms),dtype=np.int) 458 | for i in range(NAN): 459 | for kx in range(XBIN): 460 | if CELL_X[kx] <= data[i,2] < CELL_X[kx+1]: 461 | break 462 | for ky in range(YBIN): 463 | if CELL_Y[ky] <= data[i,3] < CELL_Y[ky+1]: 464 | break 465 | for kz in range(ZBIN): 466 | if CELL_Z[kz] <= data[i,4] < CELL_Z[kz+1]: 467 | break 468 | Bin_list[kx,ky,kz,Bin_count[kx,ky,kz]] = i 469 | Bin_count[kx,ky,kz]+=1 470 | return (Bin_count, Bin_list) 471 | 472 | # Create a neighbor list 473 | def neigh_build(self,data,NAN,XL,XH,XBIN,YL,YH,YBIN,ZL,ZH,ZBIN,max_atoms): 474 | Bin_count, Bin_list = self.linked_cell(data,NAN,XL,XH,XBIN,YL,YH,YBIN,ZL,ZH,ZBIN,max_atoms) 475 | neigh_count = np.zeros(NAN,dtype=np.int) 476 | neigh_id_list = np.zeros((NAN,self.max_neigh),dtype=np.int) 477 | neigh_list = -np.zeros((NAN,self.max_neigh)) 478 | for kx in range(XBIN): 479 | XBIN_LIST = range(XBIN) if XBIN <= 2 else set([kx-1,kx,kx-XBIN+1 and kx+1]) 480 | for ky in range(YBIN): 481 | YBIN_LIST = range(YBIN) if YBIN <= 2 else set([ky-1,ky,ky-YBIN+1 and ky+1]) 482 | for kz in range(ZBIN): 483 | ZBIN_LIST = range(ZBIN) if ZBIN <= 2 else set([kz-1,kz,kz-ZBIN+1 and kz+1]) 484 | for i in range(Bin_count[kx,ky,kz]): 485 | index1 = Bin_list[kx,ky,kz,i] 486 | for kkx in XBIN_LIST: 487 | for kky in YBIN_LIST: 488 | for kkz in ZBIN_LIST: 489 | for j in range(Bin_count[kkx,kky,kkz]): 490 | index2 = Bin_list[kkx,kky,kkz,j] 491 | dx = abs(data[index1,2]-data[index2,2]) 492 | ddx = dx if dx < (XH-XL)/2 else XH-XL-dx 493 | if ddx <= self.Rc: 494 | dy = abs(data[index1,3]-data[index2,3]) 495 | ddy = dy if dy < (YH-YL)/2 else YH-YL-dy 496 | if ddy <= self.Rc: 497 | dz = abs(data[index1,4]-data[index2,4]) 498 | ddz = dz if dz < (XH-ZL)/2 else ZH-ZL-dz 499 | if ddz <= self.Rc: 500 | R = math.sqrt(ddx**2+ddy**2+ddz**2) 501 | if R > 0.01 and R < self.Rc: 502 | neigh_list[index1,neigh_count[index1]] = R 503 | neigh_id_list[index1,neigh_count[index1]] = index2 504 | neigh_count[index1]+=1 505 | return (neigh_list, neigh_id_list, neigh_count) 506 | 507 | 508 | class Write_Data: 509 | # add_tilt: xy, xz, yz, or None 510 | def __init__(self, data, NAN, box_2d, output_dir, output_file, add_tilt=None, tilt_factor=0): 511 | self.data = data 512 | self.NAN = NAN 513 | self.box_2d = box_2d 514 | self.output_dir = output_dir 515 | self.output_file = output_file 516 | self.add_tilt = add_tilt 517 | self.tilt_factor = tilt_factor 518 | self.filename= os.path.join(self.output_dir,'%s' %(self.output_file)) 519 | self.fout = open(self.filename,"w") 520 | self.xlo, self.xhi = self.box_2d[0] 521 | self.ylo, self.yhi = self.box_2d[1] 522 | self.zlo, self.zhi = self.box_2d[2] 523 | # Transform coordinates based on tilt 524 | self.tilt = dict.fromkeys(("x", "y", "z", "xy", "xz", "yz"), 0) 525 | if self.add_tilt: 526 | self.tilt[self.add_tilt] = self.tilt_factor 527 | self.transform_cord() 528 | 529 | def transform_cord(self): 530 | dx, dy, dz = np.zeros(self.NAN), np.zeros(self.NAN), np.zeros(self.NAN) 531 | for i in range(self.NAN): 532 | xx, yy, zz = self.data[i,2:5] 533 | if self.add_tilt == True: 534 | pass 535 | elif self.add_tilt == "x": 536 | dx[i] = (xx-self.xlo)/(self.xhi-self.xlo) 537 | elif self.add_tilt == "y": 538 | dy[i] = (yy-self.ylo)/(self.yhi-self.ylo) 539 | elif self.add_tilt == "z": 540 | dz[i] = (zz-self.zlo)/(self.zhi-self.zlo) 541 | elif self.add_tilt == "xy": 542 | dx[i] = (yy-self.ylo)/(self.yhi-self.ylo) 543 | elif self.add_tilt == "xz": 544 | dx[i] = (zz-self.zlo)/(self.zhi-self.zlo) 545 | elif self.add_tilt == "yz": 546 | dy[i] = (zz-self.zlo)/(self.zhi-self.zlo) 547 | else: 548 | print ("Error: Wrong tilt!") 549 | sys.exit() 550 | self.data[:,2] += dx*self.tilt_factor 551 | self.data[:,3] += dy*self.tilt_factor 552 | self.data[:,4] += dz*self.tilt_factor 553 | self.xhi += int(self.add_tilt=="x")*self.tilt_factor 554 | self.yhi += int(self.add_tilt=="y")*self.tilt_factor 555 | self.zhi += int(self.add_tilt=="z")*self.tilt_factor 556 | 557 | def apply_strain(self, applied_strain): 558 | xs, ys, zs = applied_strain 559 | self.xhi *= (1+xs) 560 | self.yhi *= (1+ys) 561 | self.zhi *= (1+zs) 562 | for i in range(self.NAN): 563 | self.data[i,2] *= (1+xs) 564 | self.data[i,3] *= (1+ys) 565 | self.data[i,4] *= (1+zs) 566 | 567 | def write_datafile(self, applied_strain=None): 568 | if applied_strain: self.apply_strain(applied_strain) 569 | self.fout.write("# LAMMPS data file: %s\n" %element) 570 | self.fout.write("%d atoms\n" %(self.NAN)) 571 | self.fout.write("%d atom types\n" %(np.amax(self.data[:,1]))) 572 | self.fout.write(" %22.16f %22.16f xlo xhi\n" %(self.xlo,self.xhi)) 573 | self.fout.write(" %22.16f %22.16f ylo yhi\n" %(self.ylo,self.yhi)) 574 | self.fout.write(" %22.16f %22.16f zlo zhi\n" %(self.zlo,self.zhi)) 575 | if self.add_tilt in ["xy", "xz", "yz"]: 576 | self.fout.write(" %22.16f %22.16f %22.16f xy xz yz" % (self.tilt["xy"], self.tilt["xz"], self.tilt["yz"])) 577 | self.fout.write("\nAtoms\n\n") 578 | for i in range(self.NAN): 579 | self.fout.write("%4d %3d %22.16f %22.16f %22.16f\n" %(i+1, \ 580 | self.data[i,1], self.data[i,2], self.data[i,3], self.data[i,4])) 581 | self.fout.close() 582 | 583 | def write_poscar(self, freeze_Z=False, applied_strain=None): 584 | if applied_strain: self.apply_strain(applied_strain) 585 | self.fout.write("# %s structure\n" %element) 586 | self.fout.write("1.00\n") 587 | # Box: [[xx,yx,zx], [xy,yy,zy], [xz,yz,zz]] 588 | self.fout.write("%12.6f %12.6f %12.6f\n" %(self.xhi,0,0)) 589 | self.fout.write("%12.6f %12.6f %12.6f\n" %(self.tilt["xy"],self.yhi,0)) 590 | self.fout.write("%12.6f %12.6f %12.6f\n" %(self.tilt["xz"],self.tilt["yz"],self.zhi)) 591 | self.fout.write("%d\n" %(self.NAN)) 592 | if freeze_Z: self.fout.write("Selective Dynamics\n") 593 | self.fout.write("Cartesian\n") 594 | for i in range(self.NAN): 595 | if freeze_Z: 596 | self.fout.write("%22.16f %22.16f %22.16f F F T\n" %(self.data[i,2], self.data[i,3], self.data[i,4])) 597 | else: 598 | self.fout.write("%22.16f %22.16f %22.16f\n" %(self.data[i,2], self.data[i,3], self.data[i,4])) 599 | self.fout.close() 600 | 601 | def write_extended_xyz(self, applied_strain=None): 602 | if applied_strain: self.apply_strain(applied_strain) 603 | self.fout.write("%d\n" %(self.NAN)) 604 | tilt_xy, tilt_xz, tilt_yz = self.tilt_factor 605 | self.fout.write("Lattice=\"%12.6f %12.6f %12.6f %12.6f %12.6f %12.6f %12.6f %12.6f %12.6f\" \ 606 | Properties=species:S:1:pos:R:3 Time=0.0\n" %(self.xhi,0,0,\ 607 | self.tilt["xy"],self.yhi,0,self.tilt["xz"],self.tilt["yz"],self.zhi)) 608 | for i in range(self.NAN): 609 | self.fout.write("%s\t%12.6f\t%12.6f\t%12.6f\n" %(element, \ 610 | self.data[i,2], self.data[i,3], self.data[i,4])) 611 | self.fout.close() 612 | 613 | 614 | def main(): 615 | global filedir 616 | filedir = os.path.dirname(os.path.realpath('__file__')) 617 | # Generate bulk lattice: phase alat clat orz replication vacuum tilt 618 | if len(sys.argv) >= 15: 619 | latt=sys.argv[1] 620 | phase=sys.argv[2] 621 | alat=float(sys.argv[3]) 622 | clat=float(sys.argv[4]) 623 | orz=sys.argv[5] 624 | orz_1=int(sys.argv[6]) 625 | orz_2=int(sys.argv[7]) 626 | orz_3=int(sys.argv[8]) 627 | replicate=sys.argv[9] 628 | nx=int(sys.argv[10]) 629 | ny=int(sys.argv[11]) 630 | nz=int(sys.argv[12]) 631 | add_vac=sys.argv[13] 632 | vac=float(sys.argv[14]) 633 | add_tilt=False 634 | if len(sys.argv) >= 17: 635 | tilt=sys.argv[15] 636 | add_tilt=bool(sys.argv[16]) 637 | # Build lattice 638 | data, NAN, box_2d = Build_Crystal(phase, alat, clat, rep=[nx,ny,nz], orz=[orz_1,orz_2,orz_3], vac=vac).Build() 639 | Write_Data(data, NAN, box_2d, output_dir=filedir, output_file="Temp.data", add_tilt=add_tilt).write_datafile() 640 | 641 | # Generate defect configurations 642 | elif len(sys.argv) == 4: 643 | input_file=sys.argv[1] 644 | defect=sys.argv[2] 645 | n_def=int(sys.argv[3]) 646 | # Build defects 647 | if defect.upper() == "VACANCY": 648 | data, NAN, box_2d = Introduce_Defects(input_dir=filedir, input_file="Temp.data", nvac=n_def).Add_Vacancy() 649 | Write_Data(data, NAN, box_2d, output_dir=filedir, output_file="Temp.data").write_datafile() 650 | # Add interstitial 651 | if defect.upper() == "INTERSTITIAL": 652 | data, NAN, box_2d = Introduce_Defects(input_dir=filedir, input_file="Temp.data", nint=n_def).Add_Interstitial() 653 | Write_Data(data, NAN, box_2d, output_dir=filedir, output_file="Temp.data").write_datafile() 654 | 655 | # Stacking fault curve, support only FCC lattice for now 656 | elif len(sys.argv) == 6: 657 | sf_name=sys.argv[1] 658 | alat=float(sys.argv[2]) 659 | mesh_x=int(sys.argv[3]) 660 | mesh_y=int(sys.argv[4]) 661 | shift_plane = int(sys.argv[5]) 662 | # Displace to build stacking faults 663 | data, NAN, box_2d = Build_Crystal("FCC", alat, 1.000, rep=[5,5,12], orz=[1,1,1],vac=10).Build() 664 | Write_Data(data, NAN, box_2d, output_dir=filedir, output_file="Temp.data").write_datafile() 665 | data, NAN, box_2d = Introduce_Defects(input_dir=filedir, input_file="Temp.data",sf_mesh=[mesh_x,mesh_y],shift_plane=shift_plane).Stacking_fault(rep=rep) 666 | for i in range(mesh_x+1): 667 | for j in range(mesh_y+1): 668 | Write_Data(data[i,j,:,:], NAN, box_2d, output_dir=filedir, output_file="Temp.data").write_datafile() 669 | 670 | else: 671 | print ("Error: Wrong number of arguments!!!") 672 | sys.exit() 673 | 674 | 675 | if __name__ == "__main__": 676 | main() 677 | -------------------------------------------------------------------------------- /main/Lava_Lammps_Header.py: -------------------------------------------------------------------------------- 1 | ############################################################################### 2 | # This module provides header file for Lammps input script 3 | ############################################################################### 4 | 5 | import numpy as np 6 | import math 7 | import re 8 | from Lava_Config import * 9 | 10 | ############################################################################### 11 | 12 | lammps_potential_specification = \ 13 | "%s\n" %(potential_file_description) + \ 14 | "neighbor 2.0 bin\n" + \ 15 | "neigh_modify delay 10 check yes\n" 16 | 17 | lammps_head = \ 18 | "units metal\n" + \ 19 | "boundary p p p\n" + \ 20 | "atom_style atomic\n\n" + \ 21 | "read_data Temp.data\n" + \ 22 | "timestep 0.002\n" 23 | 24 | lammps_thermo = \ 25 | "thermo 50\n" + \ 26 | "thermo_style custom step pe temp press pxx pyy pzz pyz pxz pxy lx ly lz\n" 27 | 28 | lammps_minimize = \ 29 | "min_style cg\n" + \ 30 | "minimize 1e-10 1e-10 5000 10000\n" 31 | 32 | 33 | lammps_fix_box = \ 34 | "fix 1 all box/relax aniso 0.0 vmax 0.001\n" 35 | 36 | """ 37 | lammps_fix_box = \ 38 | "fix 1 all box/relax aniso 0.0 couple xy vmax 0.001\n" 39 | """ 40 | 41 | lammps_freeze_x_y = \ 42 | "fix 1 all setforce 0 0 NULL\n" + \ 43 | "fix 2 all temp/berendsen 0.0 0.0 .2\n" + \ 44 | "fix 3 all nve\n" + \ 45 | "run 500\n" 46 | 47 | lammps_write_data = \ 48 | "write_data Relaxed.data\n" 49 | 50 | lammps_compute_energy = \ 51 | "compute peratom all pe/atom\n" 52 | 53 | lammps_thermo_melt = \ 54 | "thermo 500\n" + \ 55 | "thermo_style custom step etotal pe ke press pxx pyy pzz temp c_LiquidTemp c_SolidTemp\n" 56 | 57 | class get_lammps_script: 58 | 59 | def __init__(self, Lammps_file = "Lammps.in"): 60 | self.Lammps_file = Lammps_file 61 | self.outfile = open(self.Lammps_file,"w") 62 | self.outfile.write("%s\n" %(lammps_head)) 63 | self.outfile.write("%s\n" %(lammps_potential_specification)) 64 | 65 | def Static(self): 66 | self.outfile.write("%s\n" %(lammps_thermo)) 67 | self.outfile.write("run 0\n") 68 | 69 | def Minimize(self): 70 | self.outfile.write("%s\n" %(lammps_thermo)) 71 | self.outfile.write("%s\n" %(lammps_minimize)) 72 | self.outfile.write("%s\n" %(lammps_write_data)) 73 | 74 | def Relax_Box(self): 75 | self.outfile.write("%s\n" %(lammps_fix_box)) 76 | self.outfile.write("%s\n" %(lammps_thermo)) 77 | self.outfile.write("%s\n" %(lammps_minimize)) 78 | self.outfile.write("%s\n" %(lammps_write_data)) 79 | 80 | def Deform(self, deform = "Z", delta = 0): 81 | self.outfile.write("%s\n" %(lammps_thermo)) 82 | if len(deform) == 1: # x or y or z 83 | self.outfile.write("change_box all %s delta 0 %f remap units box\n" %(deform,delta)) 84 | else: # yz or xz or xy 85 | self.outfile.write("change_box all %s delta %f remap units box\n" %(deform,delta)) 86 | self.outfile.write("run 0\n") 87 | 88 | def Freeze_X_Y(self): 89 | self.outfile.write("%s\n" %(lammps_thermo)) 90 | self.outfile.write("%s\n" %(lammps_freeze_x_y)) 91 | self.outfile.write("%s\n" %(lammps_write_data)) 92 | 93 | def Fix_npt(self, T): 94 | T = max(T,1) # Target temperature cannot be zero in fix npt 95 | self.outfile.write("velocity all create %d 4928459 dist gaussian\n" %(2*T)) 96 | self.outfile.write("fix 1 all npt temp %d %d .1 iso 0 0 10\n" %(T, T)) 97 | self.outfile.write("%s\n" %(lammps_thermo)) 98 | self.outfile.write("run 25000 # equilibrate for 50 ps\n") 99 | 100 | def Fix_rdf(self): 101 | self.outfile.write("compute rdf all rdf 500\n") 102 | self.outfile.write("fix rdf all ave/time 1 1 1 c_rdf[*] ave running file RDF.dat mode vector\n") 103 | self.outfile.write("run 0\n") 104 | 105 | def Fix_liquid_rdf(self, T): 106 | self.outfile.write("variable Tm equal %d\n" %T) 107 | self.outfile.write("variable Tm2 equal 2*${Tm}\n\n") 108 | self.outfile.write("velocity all create ${Tm2} 4928459 dist gaussian\n") 109 | self.outfile.write("fix 1 all npt temp ${Tm} ${Tm} 0.1 iso 0 0 0.2\n\n") 110 | self.outfile.write("%s\n" %(lammps_thermo)) 111 | self.outfile.write("run 25000 # equilibrate for 100 ps\n\n") 112 | self.outfile.write("compute rdf all rdf 500 cutoff 6.0\n") 113 | self.outfile.write("fix rdf all ave/time 1 1 1 c_rdf[*] ave running file Liquid_RDF_${Tm}.dat mode vector\n") 114 | self.outfile.write("run 0\n") 115 | 116 | def Two_phase_melt(self, T): 117 | self.outfile.write("variable Tm equal %d\n" %T) 118 | self.outfile.write("variable Tm2 equal 2*${Tm}\n") 119 | self.outfile.write("variable Tm_melt equal ${Tm}*1.2+300\n\n") 120 | self.outfile.write("variable lz equal \"lz\"\n") 121 | self.outfile.write("variable z_cen equal ${lz}/2\n") 122 | self.outfile.write("region liquid block INF INF INF INF INF ${z_cen} units box\n") 123 | self.outfile.write("group liquid region liquid\n") 124 | self.outfile.write("group solid subtract all liquid\n") 125 | self.outfile.write("#set group liquid type 2\n\n") 126 | self.outfile.write("compute 2 all pe/atom\n") 127 | self.outfile.write("compute LiquidTemp liquid temp\n") 128 | self.outfile.write("compute SolidTemp solid temp\n\n") 129 | self.outfile.write("%s\n" %(lammps_minimize)) 130 | self.outfile.write("%s\n" %(lammps_thermo_melt)) 131 | self.outfile.write("#--------Equilibrate at Tm--------#\n") 132 | self.outfile.write("reset_timestep 0\n") 133 | self.outfile.write("velocity all create ${Tm2} 4928459 dist gaussian\n") 134 | self.outfile.write("fix 1 all npt temp ${Tm} ${Tm} 0.1 iso 0 0 0.2\n\n") 135 | self.outfile.write("#dump dnve all custom 5000 Step1_Equi.* id type x y z c_2\n") 136 | self.outfile.write("run 10000\nunfix 1\n#undump dnve\n\n") 137 | self.outfile.write("#--------Melt liquid at Tm_melt--------#\n") 138 | self.outfile.write("reset_timestep 0\n") 139 | self.outfile.write("fix 1 liquid npt temp ${Tm_melt} ${Tm_melt} 2 x 0 0 1.0\n\n") 140 | self.outfile.write("#dump dnve all custom 5000 Step2_Heat.* id type x y z c_2\n") 141 | self.outfile.write("run 15000\nunfix 1\n#undump dnve\n\n") 142 | self.outfile.write("#--------Cool liquid to Tm--------#\n") 143 | self.outfile.write("reset_timestep 0\n") 144 | self.outfile.write("fix 1 liquid npt temp ${Tm} ${Tm} 2 x 0 0 1.0\n\n") 145 | self.outfile.write("#dump dnve all custom 5000 Step3_Cool.* id type x y z c_2\n") 146 | self.outfile.write("run 15000\nunfix 1\n#undump dnve\n\n") 147 | self.outfile.write("#--------Fix nph on entire system--------#\n") 148 | self.outfile.write("reset_timestep 0\n") 149 | self.outfile.write("fix 1 all nph iso 0.0 0.0 0.2\n\n") 150 | self.outfile.write("dump dnve all custom 25000 Melt_nph.* id type x y z c_2\n") 151 | self.outfile.write("run 250000\n") 152 | 153 | def uniaxial_deform(self,phase,temp,deform_mode,rate,final_strain): 154 | self.outfile.write("%s\n" %(lammps_compute_energy)) 155 | self.outfile.write("variable Tm equal %d\n" %temp) 156 | self.outfile.write("variable phase string %s\n" %phase) 157 | self.outfile.write("variable mode equal %s\n" %deform_mode) 158 | self.outfile.write("variable strainf equal %f\n" %final_strain) 159 | self.outfile.write("variable rate equal %f\n" %rate) 160 | self.outfile.write("variable iter equal v_strainf/(0.002*v_rate)\n") 161 | self.outfile.write("#--------Equilibrate at Tm--------#\n") 162 | self.outfile.write("reset_timestep 0\n") 163 | self.outfile.write("velocity all create ${Tm} 4928459 dist gaussian\n") 164 | self.outfile.write("fix 1 all npt temp ${Tm} ${Tm} 0.1 x 0 0 0.1 y 0 0 0.1 z 0 0 0.1\n\n") 165 | self.outfile.write("thermo 1000\n") 166 | self.outfile.write("thermo_style custom step lx ly lz press pxx pyy pzz pe temp\n") 167 | self.outfile.write("#--------Equilibration--------#\n") 168 | self.outfile.write("run 10000\nunfix 1\n\n") 169 | self.outfile.write("fix 1 all npt temp ${Tm} ${Tm} 0.1 x 0 0 0.1 y 0 0 0.1 z 0 0 0.1\n\n") 170 | self.outfile.write("run 10000\nunfix 1\n\n") 171 | self.outfile.write("#--------Store final cell length for strain calculations--------#\n") 172 | self.outfile.write("variable tmp equal lx\n") 173 | self.outfile.write("variable Lx0 equal ${tmp}\n") 174 | self.outfile.write("variable tmp equal ly\n") 175 | self.outfile.write("variable Ly0 equal ${tmp}\n") 176 | self.outfile.write("variable tmp equal lz\n") 177 | self.outfile.write("variable Lz0 equal ${tmp}\n") 178 | self.outfile.write("print 'Initial Length, Lx0: ${Lx0}'\n") 179 | self.outfile.write("print 'Initial Length, Ly0: ${Ly0}'\n") 180 | self.outfile.write("print 'Initial Length, Lz0: ${Lz0}'\n") 181 | self.outfile.write("#--------DEFORMATION--------#\n") 182 | self.outfile.write("reset_timestep 0\n") 183 | if (deform_mode==1): 184 | self.outfile.write("fix 1 all npt temp ${Tm} ${Tm} 1 y 0 0 1 z 0 0 1\n") 185 | self.outfile.write("fix 2 all deform 1 x erate ${rate} units box remap x\n") 186 | self.outfile.write("variable strain equal (lx-v_Lx0)/v_Lx0\n") 187 | elif (deform_mode==2): 188 | self.outfile.write("fix 1 all npt temp ${Tm} ${Tm} 1 x 0 0 1 z 0 0 1\n") 189 | self.outfile.write("fix 2 all deform 1 y erate ${rate} units box remap x\n") 190 | self.outfile.write("variable strain equal (ly-v_Ly0)/v_Ly0\n") 191 | elif (deform_mode==3): 192 | self.outfile.write("fix 1 all npt temp ${Tm} ${Tm} 1 x 0 0 1 y 0 0 1\n") 193 | self.outfile.write("fix 2 all deform 1 z erate ${rate} units box remap x\n") 194 | self.outfile.write("variable strain equal (lz-v_Lz0)/v_Lz0\n") 195 | elif (deform_mode==4): 196 | self.outfile.write("change_box all triclinic\n") 197 | self.outfile.write("fix 1 all npt temp ${Tm} ${Tm} 1 x 0 0 1 y 0 0 1 z 0 0 1 xz 0 0 1 xy 0 0 1\n") 198 | self.outfile.write("fix 2 all deform 1 yz erate ${rate} units box remap x\n") 199 | self.outfile.write("variable strain equal yz/v_Lz0\n") 200 | elif (deform_mode==5): 201 | self.outfile.write("change_box all triclinic\n") 202 | self.outfile.write("fix 1 all npt temp ${Tm} ${Tm} 1 x 0 0 1 y 0 0 1 z 0 0 1 yz 0 0 1 xy 0 0 1\n") 203 | self.outfile.write("fix 2 all deform 1 xz erate ${rate} units box remap x\n") 204 | self.outfile.write("variable strain equal xz/v_Lz0\n") 205 | elif (deform_mode==6): 206 | self.outfile.write("change_box all triclinic\n") 207 | self.outfile.write("fix 1 all npt temp ${Tm} ${Tm} 1 x 0 0 1 y 0 0 1 z 0 0 1 yz 0 0 1 xz 0 0 1\n") 208 | self.outfile.write("fix 2 all deform 1 xy erate ${rate} units box remap x\n") 209 | self.outfile.write("variable strain equal xy/v_Ly0\n") 210 | self.outfile.write("#--------Output strain and stress info to file--------#\n") 211 | self.outfile.write("#for units metal, pressure is in [bars] = 100 [kPa] = 1/10000 [GPa]#\n") 212 | self.outfile.write("#p2, p3, p4 are in GPa#\n") 213 | self.outfile.write("variable p1 equal v_strain\n") 214 | self.outfile.write("variable p2 equal -pxx/10000\n") 215 | self.outfile.write("variable p3 equal -pyy/10000\n") 216 | self.outfile.write("variable p4 equal -pzz/10000\n") 217 | self.outfile.write("variable p5 equal -pyz/10000\n") 218 | self.outfile.write("variable p6 equal -pxz/10000\n") 219 | self.outfile.write("variable p7 equal -pxy/10000\n") 220 | self.outfile.write("fix 3 all print 100 '${p1} ${p2} ${p3} ${p4} ${p5} ${p6} ${p7}' file Stress_strain_${mode}_${phase}.txt screen no\n") 221 | self.outfile.write("dump 1 all custom 250 uniaxial_deform id type x y z c_peratom\n") 222 | self.outfile.write("thermo_style custom step v_strain temp v_p2 v_p3 v_p4 v_p5 v_p6 v_p7 ke pe press\n") 223 | self.outfile.write("run ${iter}\n") 224 | self.outfile.write("%s\n" %(lammps_write_data)) 225 | -------------------------------------------------------------------------------- /main/Lava_Plotter.py: -------------------------------------------------------------------------------- 1 | ############################################################################### 2 | # This module provides utility functions/wrappers 3 | # for plotting outputs as ".jpeg' format 4 | ############################################################################### 5 | 6 | import numpy as np 7 | import os 8 | import math 9 | from numpy import genfromtxt 10 | from io import BytesIO 11 | import matplotlib.pyplot as plt 12 | from numpy import ma 13 | from matplotlib import ticker, cm 14 | from matplotlib.ticker import MaxNLocator 15 | from mpl_toolkits.mplot3d import Axes3D 16 | from Lava_Utility import * 17 | 18 | ############################################################################### 19 | 20 | # This is for reading in a list of files. 21 | def enumerate_files(*filelist, skip_line=0, delimiter=None): 22 | filedir = os.path.dirname(os.path.realpath('__file__')) 23 | array_list, skipped_lines_list = [], [] 24 | for file in filelist: 25 | with open (os.path.join(filedir,'%s' %file)) as infile: 26 | array, skipped_lines = [], [] 27 | # Skip the first few lines specified by skip_line 28 | for i, line in enumerate(infile): 29 | if i >= skip_line: 30 | array.append(genfromtxt(BytesIO(line.encode('utf8')), delimiter=delimiter)) 31 | else: 32 | skipped_lines.append(line) 33 | array_list.append(array) 34 | skipped_lines_list.append(skipped_lines) 35 | return (skipped_lines_list, array_list) 36 | 37 | 38 | def plot_1D_wrapper(plt, xlabel_params=None, ylabel_params=None, tick_params=None, \ 39 | legend_params=None, title_params=None, output_file=None): 40 | if xlabel_params: plt.xlabel(xlabel_params['title'], size=xlabel_params['size']) 41 | if ylabel_params: plt.ylabel(ylabel_params['title'], size=ylabel_params['size']) 42 | if tick_params: plt.tick_params(labelsize=tick_params['labelsize'], pad=tick_params['pad']) 43 | if legend_params: plt.legend(loc=legend_params['loc'], prop={'size': legend_params['size']}) 44 | if title_params: plt.title(title_params['title'], size=title_params['size']) 45 | if output_file: plt.savefig(output_file) 46 | 47 | 48 | def plot_2D_wrapper(fig, plt, ax, cs, xlabel_params=None, ylabel_params=None, tick_params=None, 49 | cbar_params=None, title_params=None, output_file=None): 50 | if xlabel_params: ax.set_xlabel(xlabel_params['title'], size=xlabel_params['size']) 51 | if ylabel_params: ax.set_ylabel(ylabel_params['title'], size=ylabel_params['size']) 52 | if tick_params: ax.tick_params(labelsize=tick_params['labelsize'], pad=tick_params['pad']) 53 | for tick in ax.xaxis.get_major_ticks(): tick.label.set_fontsize(26) 54 | for tick in ax.yaxis.get_major_ticks(): tick.label.set_fontsize(26) 55 | if cbar_params: 56 | cbar = fig.colorbar(cs) 57 | cbar.ax.tick_params(labelsize=tick_params['labelsize'], pad=tick_params['pad']) 58 | cbar.set_label(cbar_params['title'], size = cbar_params['size'], \ 59 | labelpad = cbar_params['labelpad'], rotation=90) 60 | if title_params: plt.title(title_params['title'], size=title_params['size']) 61 | if output_file: plt.savefig(output_file) 62 | 63 | 64 | def get_color_list(): 65 | colors = ['#1f77b4','#ff7f0e','#2ca02c','#d62728', 66 | '#9467bd','#8c564b','#e377c2','#7f7f7f'] 67 | return colors 68 | 69 | 70 | def plot_EOS(input_file, output_file, plot_axis_x="volume", plot_axis_y="energy"): 71 | # Load input 72 | skipped_lines, array_list = enumerate_files(input_file, skip_line=1, delimiter=' ') 73 | phase_list = skipped_lines[0][0].split (" ") 74 | EOS_data = np.array(array_list[0]) 75 | # Plot 76 | plot_axis_map = {"density":0, "volume":1, "energy":2} 77 | plot_axis_title_map = {"density": 'Density, (g/$\mathregular{{cm}^3}$)', 78 | "volume": 'Volume, ($\mathregular{Å^3}$)', 79 | "energy": 'Energy, (eV/atom)'} 80 | x_axis, y_axis = plot_axis_map[plot_axis_x], plot_axis_map[plot_axis_y] 81 | colors = get_color_list() 82 | fig = plt.figure(figsize=(16,12)) 83 | for i in range(len(phase_list)-1): 84 | plt.plot(EOS_data[:,3*i+x_axis], EOS_data[:,3*i+y_axis],'-*',color=colors[i], 85 | markersize=16, linewidth=3, label=str(phase_list[i]).strip(' #')) 86 | plot_1D_wrapper(plt, 87 | xlabel_params={'title': plot_axis_title_map[plot_axis_x], 'size': 30}, 88 | ylabel_params={'title': plot_axis_title_map[plot_axis_y], 'size': 30}, 89 | tick_params={'labelsize': 28, 'pad': 12}, 90 | legend_params={'loc': 1, 'size': 26}, 91 | output_file="%s.jpeg" %(output_file)) 92 | 93 | 94 | def plot_RDF(input_file, output_file): 95 | # Load input 96 | skipped_lines, array_list = enumerate_files(input_file, skip_line=1, delimiter=' ') 97 | T_list = skipped_lines[0][0].split (" ") 98 | RDF_data = np.array(array_list[0]) 99 | # Plot 100 | colors = get_color_list() 101 | fig = plt.figure(figsize=(16,12)) 102 | for i in range(len(T_list)-1): 103 | plt.plot(RDF_data[:,2*i], RDF_data[:,2*i+1],'-', color=colors[i], linewidth=3, \ 104 | label=str(T_list[i]).strip(' #')+" K") 105 | plot_1D_wrapper(plt, 106 | xlabel_params={'title': 'r, (Å)', 'size': 30}, 107 | ylabel_params={'title': 'g(r)', 'size': 30}, 108 | tick_params={'labelsize': 28, 'pad': 12}, 109 | legend_params={'loc': 1, 'size': 26}, 110 | output_file="%s.jpeg" %(output_file)) 111 | 112 | 113 | def plot_thermal_expansion(input_file, output_file): 114 | # Load input 115 | skipped_lines, array_list = enumerate_files(input_file, skip_line=1, delimiter=' ') 116 | Thermal_expansion_data = np.array(array_list[0]) 117 | # Plot 118 | colors = get_color_list() 119 | fig = plt.figure(figsize=(16,12)) 120 | plt.plot(Thermal_expansion_data[:,0], Thermal_expansion_data[:,4],'-*', \ 121 | markersize=20, color=colors[0], linewidth=4) 122 | plot_1D_wrapper(plt, 123 | xlabel_params={'title': 'Temperature, (K)', 'size': 30}, 124 | ylabel_params={'title': 'Lattice constant, (Å)', 'size': 30}, 125 | tick_params={'labelsize': 28, 'pad': 12}, 126 | legend_params={'loc': 1, 'size': 26}, 127 | output_file="%s.jpeg" %(output_file)) 128 | 129 | 130 | def plot_Bain(input_file, output_file): 131 | # Load input 132 | _, array_list = enumerate_files(input_file, skip_line=1, delimiter=' ') 133 | Bain_data = np.array(array_list[0]) 134 | # Plot 135 | colors = get_color_list() 136 | fig = plt.figure(figsize=(16,12)) 137 | plt.plot(Bain_data[:,1], Bain_data[:,2],'-*', markersize=16, color=colors[0],linewidth=3) 138 | plot_1D_wrapper(plt, 139 | xlabel_params={'title': 'c/a', 'size': 30}, 140 | ylabel_params={'title': 'Energy, (eV/atom)', 'size': 30}, 141 | tick_params={'labelsize': 28, 'pad': 12}, 142 | legend_params={'loc': 1, 'size': 26}, 143 | output_file="%s.jpeg" %(output_file)) 144 | 145 | 146 | def plot_Bain_2D(input_X, input_Y, input_Z, output_file): 147 | # Load input 148 | _, array_list = enumerate_files(input_X, input_Y, input_Z, delimiter=' ') 149 | Mesh_X, Mesh_Y, Mesh_Z = np.array(array_list[0]), np.array(array_list[1]), np.array(array_list[2]) 150 | # Plot 151 | fig, ax = plt.subplots() 152 | plt.gcf().set_size_inches(16, 12) 153 | levels = MaxNLocator(nbins=20).tick_values(Mesh_Z.min(), Mesh_Z.max()) 154 | cs = ax.contourf(Mesh_X, Mesh_Y, Mesh_Z, levels=levels, cmap="RdBu_r") 155 | plot_2D_wrapper(fig, plt, ax, cs, 156 | xlabel_params={'title': 'Volume', 'size': 30}, 157 | ylabel_params={'title': 'c/a', 'size': 30}, 158 | tick_params={'labelsize': 28, 'pad': 12}, 159 | cbar_params={'title': 'Energy, (eV)', 'size': 26, 'labelpad': 28}, 160 | output_file="%s.jpeg" %(output_file)) 161 | 162 | 163 | def plot_Gamma_2D(input_X, input_Y, input_Z, output_file): 164 | # Load input 165 | _, array_list = enumerate_files(input_X, input_Y, input_Z, delimiter=' ') 166 | Mesh_X, Mesh_Y, Mesh_Z = np.array(array_list[0]), np.array(array_list[1]), np.array(array_list[2]) 167 | # Plot 168 | fig, ax = plt.subplots() 169 | plt.gcf().set_size_inches(16, 12) 170 | levels = MaxNLocator(nbins=20).tick_values(Mesh_Z.min(), Mesh_Z.max()) 171 | cs = ax.contourf(Mesh_X, Mesh_Y, Mesh_Z, levels=levels, cmap="RdBu_r") 172 | plot_2D_wrapper(fig, plt, ax, cs, 173 | xlabel_params={'title': 'Normalized <112> displacement', 'size': 30}, 174 | ylabel_params={'title': 'Normalized <110> displacement', 'size': 30}, 175 | tick_params={'labelsize': 28, 'pad': 16}, 176 | cbar_params={'title': 'Energy, (mJ/$\mathregular{m^{2}}$)', 'size': 26, 'labelpad': 26}, 177 | output_file="%s.jpeg" %(output_file)) 178 | 179 | 180 | def plot_GSFE(input_file, output_file): 181 | # Load input 182 | _, array_list = enumerate_files(input_file, skip_line=1, delimiter=' ') 183 | GSFE_data = np.array(array_list[0]) 184 | # Plot 185 | colors = get_color_list() 186 | labels = ['Slip', 'Twin'] 187 | labels = ['Slip'] 188 | fig = plt.figure(figsize=(16,12)) 189 | for i in range(1): 190 | plt.plot(GSFE_data[:,0], GSFE_data[:,i+1],'-', color=colors[i], label=labels[i], linewidth=3) 191 | plot_1D_wrapper(plt, 192 | xlabel_params={'title': 'Normalized <001> displacement', 'size': 30}, 193 | ylabel_params={'title': 'Energy, (mJ/$\mathregular{m^{2}}$)', 'size': 30}, 194 | tick_params={'labelsize': 28, 'pad': 12}, 195 | legend_params={'loc': 'upper left', 'size': 26}, 196 | output_file="%s.jpeg" %(output_file)) 197 | 198 | 199 | def plot_Gamma_3D(input_X, input_Y, input_Z, output_file): 200 | # Load input 201 | _, array_list = enumerate_files(input_X, input_Y, input_Z, delimiter=' ') 202 | Mesh_X, Mesh_Y, Mesh_Z = np.array(array_list[0]), np.array(array_list[1]), np.array(array_list[2]) 203 | # Plot 204 | fig = plt.figure(figsize=(16,12)) 205 | ax = plt.axes(projection='3d') 206 | ax.plot_surface(Mesh_X, Mesh_Y, Mesh_Z, rstride=1, cstride=1, cmap='viridis', edgecolor='none') 207 | ax.set_xlabel('Normalized <112> displacement', size = 20, labelpad=20) 208 | ax.set_ylabel('Normalized <110> displacement', size = 20, labelpad=20) 209 | ax.set_zlabel('Energy, (mJ/$\mathregular{m^{2}}$)', size = 20, labelpad=28) 210 | ax.tick_params(labelsize=28, pad=8) 211 | for tick in ax.xaxis.get_major_ticks(): tick.label.set_fontsize(20) 212 | for tick in ax.yaxis.get_major_ticks(): tick.label.set_fontsize(20) 213 | for tick in ax.zaxis.get_major_ticks(): tick.label.set_fontsize(20) 214 | plt.savefig("%s.jpeg" %(output_file)) 215 | 216 | 217 | def plot_stress_strain(input_file,mode, output_file): 218 | # Load input 219 | skipped_lines, array_list = enumerate_files(input_file, skip_line=1, delimiter=' ') 220 | stress_strain_data = np.array(array_list[0]) 221 | strain = np.array(stress_strain_data[:,0]) 222 | if (mode==1): 223 | stress = np.array(stress_strain_data[:,1]) 224 | elif (mode==2): 225 | stress = np.array(stress_strain_data[:,2]) 226 | elif (mode==3): 227 | stress = np.array(stress_strain_data[:,3]) 228 | elif (mode==4): 229 | stress = np.array(stress_strain_data[:,4]) 230 | elif (mode==5): 231 | stress = np.array(stress_strain_data[:,5]) 232 | elif (mode==6): 233 | stress = np.array(stress_strain_data[:,6]) 234 | # Plot 235 | colors = get_color_list() 236 | fig = plt.figure(figsize=(16,12)) 237 | plt.plot(strain, stress,'-*', \ 238 | markersize=20, color=colors[0], linewidth=4) 239 | plot_1D_wrapper(plt, 240 | xlabel_params={'title': 'Strain', 'size': 30}, 241 | ylabel_params={'title': 'Stress (GPa)', 'size': 30}, 242 | tick_params={'labelsize': 28, 'pad': 12}, 243 | legend_params={'loc': 1, 'size': 26}, 244 | output_file="%s.jpeg" %(output_file)) 245 | -------------------------------------------------------------------------------- /main/Lava_Utility.py: -------------------------------------------------------------------------------- 1 | ############################################################################### 2 | # This module provides utility classes/functions 3 | # for extracting output from lammps/vasp runs 4 | # and other functions for file mamagement, data processing, etc 5 | ############################################################################### 6 | 7 | import numpy as np 8 | from numpy import genfromtxt 9 | import os 10 | from os import sys 11 | import math 12 | import re 13 | from io import BytesIO 14 | from shutil import copyfile 15 | import subprocess 16 | from Lava_Vasp_Header import * 17 | from Lava_Generator import * 18 | from Lava_Config import * 19 | 20 | ############################################################################### 21 | # General utility functions # 22 | ############################################################################### 23 | 24 | # Submit lammps/vasp job 25 | def submit_job_wrapper(output_dir, filedir, NSW, IBRION, ISIF, lammps_file=None, mode=None): 26 | if mode == "Lammps": 27 | # Copy all files to output_dir and submit lammps run 28 | run_lammps(output_dir, filedir, lammps_file=lammps_file) 29 | else: 30 | # Generate INCAR file, copy all files to output_dir, and submit vasp run 31 | run_vasp(output_dir, filedir, NSW, IBRION, ISIF) 32 | 33 | # Create directory and return the name 34 | def make_dir(dirname): 35 | os.mkdir(dirname) 36 | return (dirname) 37 | 38 | # Copy lammps/vasp input files. 39 | def copy_file(destination, *argv): 40 | for file in argv: 41 | copyfile('%s' %(file), '%s/%s' %(destination,file)) 42 | 43 | # Open a list of files in filedir for writing 44 | def open_files_for_write(filelist, filedir): 45 | return [open(os.path.join(filedir,file),"w") for file in filelist] 46 | 47 | # Close a list of files (filelist) 48 | def close_files(*filelist): 49 | for file in filelist: file.close() 50 | 51 | # Write a list of characters to a list of files 52 | # file_char_list is a list of pairs of file and char 53 | def write_to_file_chars(*file_char_list): 54 | for file, char in file_char_list: file.write(char) 55 | 56 | # Read in Lammps_latt.dat or Vasp_latt.dat 57 | def read_latt_file(filename): 58 | phase_lat = {} 59 | with open(filename) as infile: 60 | for i, line in enumerate(infile): 61 | if i > 0: 62 | latt = re.split('\s+', line.strip()) 63 | phase_lat[latt[0]] = tuple(float(entry) for entry in latt[1:]) 64 | return phase_lat 65 | 66 | # This wrapper selects the write method (write_datafile or write_poscar) based on the mode 67 | def Write_Data_wrapper(data, NAN, box_2d, output_dir=None, output_file=None, \ 68 | add_tilt=None, tilt_factor=None, freeze_Z=None, applied_strain=None, mode=None): 69 | if not output_file: output_file="POSCAR" if mode == "Vasp" else "Temp.data" 70 | if mode == "Lammps": 71 | Write_Data(data, NAN, box_2d, output_dir=output_dir, output_file=output_file, \ 72 | add_tilt=add_tilt, tilt_factor=tilt_factor).write_datafile(applied_strain=applied_strain) 73 | else: 74 | Write_Data(data, NAN, box_2d, output_dir=output_dir, output_file=output_file, \ 75 | add_tilt=add_tilt, tilt_factor=tilt_factor).write_poscar(freeze_Z=freeze_Z,applied_strain=applied_strain) 76 | 77 | # This wrapper retrives the output path and name (log.lammps or OUTCAR) based on the mode 78 | def target_file_wrapper(*subdir, mode=None): 79 | fname = {"Lammps": "log.lammps", "Vasp": "OUTCAR"} 80 | return os.path.join(*subdir,fname[mode]) 81 | 82 | # Get the replication number 83 | # lat_dim is the desired dimension 84 | # alat, blat and clat are x, y and z dimention 85 | def set_lat_rep(lat_rep, lat_dim, alat, blat, clat): 86 | if lat_rep: 87 | return lat_rep 88 | else: 89 | return [math.ceil(lat_dim[i]/lat) for i, lat in enumerate([alat, blat, clat])] 90 | 91 | # Get surface energy scaled by area 92 | def get_E_surface(pe, natom, ecoh, area): 93 | return (pe-natom*ecoh)/area*1.60217657e-16*1.0e20 # Convert from eV/Ang to mJ/m2 94 | 95 | # Get stacking fault energy scaled by area 96 | def get_SFE(pe, pe0, area): 97 | return (pe-pe0)/area*1.60217657e-16*1.0e20 # Convert from eV/Ang to mJ/m2 98 | 99 | # Get unstable stacking fault energy Eusf and stable stacking fault energy Essf 100 | def get_usf_sf(sf_mesh, Esf, Eusf, Essf, i, j): 101 | if j == 0: 102 | if i/sf_mesh[0] < 0.34 and Esf[i,j] > Eusf: # Eusf usually occurs at 0.208 (5/24) 103 | Eusf = Esf[i,j] 104 | if i/sf_mesh[0] > 0.25 and i/sf_mesh[0] < 0.50 and Esf[i,j] < Essf: # Esf usually occurs at 0.333 105 | Essf = Esf[i,j] 106 | return (Eusf, Essf) 107 | 108 | # Get unstable twinning energy Eutf 109 | def get_utf(sf_mesh, Esf, Eutf): 110 | for i in range(sf_mesh[0]+1): 111 | if i/sf_mesh[0] > 0.40 and i/sf_mesh[0] < 0.60 and Esf[i,1] > Eutf: # Eutf usually occurs at 0.5 112 | Eutf = Esf[i,1] 113 | return Eutf 114 | 115 | # Convert bravais-miller indice (hcp) to bravais indice 116 | def to_miller_indices(u, v, t, w): 117 | # u: (11-20) -> (100), v: (10-10) -> (010), w: (0001) -> (001) 118 | # Convert from Miller-Bravais indices to Miller indices 119 | # (u, v, t, w) = v*(11-20) + (u-v)*(10-10) + w*(0001) 120 | # = v*(100) + (u-v)*(010) + w*(001) 121 | # = (v, u-v, w) 122 | if (u+v+t): 123 | print ("Invalid Miller-Bravais indices!") 124 | sys.exit() 125 | else: 126 | return [v, u-v, w] 127 | 128 | # Get cell replication based on a give dimension 129 | def get_lat_rep_fcc(phase, alat, clat, surface, lat_rep, lat_dim): 130 | _, _, box_2d = Build_Crystal(phase, alat, clat, [1,1,1], surface).Build() 131 | XL, XH, YL, YH, ZL, ZH = box_2d.flatten() 132 | return set_lat_rep(lat_rep, lat_dim, XH-ZL, YH-YL, ZH-ZL) 133 | 134 | # Get cell replication based on a give dimension (hcp) 135 | def get_lat_rep_hcp(phase, alat, clat, surface, lat_rep, lat_dim): 136 | nlayer=24 137 | _, _, box_2d = Build_Crystal(phase, alat, clat, [1,1,1], surface, nlayer=nlayer).Build() 138 | XL, XH, YL, YH, ZL, ZH = box_2d.flatten() 139 | nlayer = math.ceil(nlayer/((ZH-ZL)/lat_dim[2])) 140 | # Make sure nlayer is a multiplier of 4 141 | nlayer = 4*math.ceil(nlayer/4) 142 | return (nlayer, set_lat_rep(lat_rep, lat_dim, XH-ZL, YH-YL, ZH-ZL)) 143 | 144 | # Get the corresponding alat at the same volume for a given clat/alat 145 | def get_params_alat(clat, alat0, clat0): 146 | return alat0*(clat0/clat)**(1/3) 147 | 148 | # Round floating points to the corresponding significant digits 149 | def round_digits(param, significant_digits=4): 150 | return round(param, significant_digits) 151 | 152 | ############################################################################### 153 | # Lammps-specific utility functions # 154 | ############################################################################### 155 | # Submit Lammps job 156 | def run_lammps(output_dir, filedir, lammps_file): 157 | lammps_infile = ''.join(open('RunLammps.inp',"r").readlines()) 158 | for str_set in (("lammps_executable", os.path.join(filedir,lammps_executable)), ("lammps.in", lammps_file)): 159 | lammps_infile = lammps_infile.replace(*str_set) 160 | with open("RunLammps.sh", "w") as outfile: 161 | outfile.write(lammps_infile) 162 | #copy_file(output_dir, lammps_file, potential_file, "library-Al.meam", "RunLammps.sh") 163 | copy_file(output_dir, lammps_file, "RunLammps.sh") 164 | for file in potential_file: copy_file(output_dir, file) 165 | os.chdir(output_dir) 166 | subprocess.call("chmod +x RunLammps.sh", shell=True) 167 | subprocess.call("./RunLammps.sh", shell=True) 168 | os.chdir(filedir) 169 | print (r"Lammps job submitted at %s: %s" %(output_dir, lammps_file)) 170 | 171 | ############################################################################### 172 | # Vasp-specific utility functions # 173 | ############################################################################### 174 | # Submit vasp job 175 | def run_vasp(output_dir, filedir, *argv): 176 | get_vasp_script(*argv) 177 | copy_file(output_dir,'INCAR','KPOINTS','POTCAR','RunVasp.sh') 178 | os.chdir(output_dir) 179 | subprocess.call("sbatch RunVasp.sh", shell=True) 180 | os.chdir(filedir) 181 | print (r"Vasp job submitted at %s" %output_dir) 182 | 183 | # Extracte elastic moduli from vasp OUTCAR file 184 | def get_elastic_moduli_vasp(filename): 185 | dp, C = np.zeros((6,6)), np.zeros((6,6)) 186 | with open(filename) as infile: 187 | data = infile.read().split('\n') 188 | if ' TOTAL ELASTIC MODULI (kBar)' in data: 189 | index = data.index(' TOTAL ELASTIC MODULI (kBar)') 190 | for i, line in enumerate(data[index+3:index+9]): 191 | line = re.split('\s+', line.strip()) 192 | dp[i,:] = [float(entry) for entry in line[1:]] 193 | for i in range(6): 194 | for j in range(i,6): 195 | C[i,j] = (dp[i,j] + dp[j,i])/2 196 | return C 197 | 198 | ############################################################################### 199 | # Utility functions for extracting output # 200 | ############################################################################### 201 | # Extract lattice parameter, energy from lammps under serial mode 202 | # utilizing lammps-python interface 203 | class get_lammps_output_serial: 204 | def __init__(self, lmp): 205 | self.lmp = lmp 206 | 207 | def grep_E(self): 208 | return self.lmp.get_thermo("pe") 209 | 210 | def grep_natoms(self): 211 | return self.lmp.get_natoms() 212 | 213 | def grep_E_per_atom(self): 214 | return self.lmp.get_thermo("pe")/self.lmp.get_natoms() 215 | 216 | def grep_latt(self, lat_rep=[1,1,1]): 217 | latt = ["lx", "ly", "lz"] 218 | return [self.lmp.get_thermo(latt[i])/lat_rep[i] for i in range(3)] 219 | 220 | def grep_stress(self): 221 | p_list = ["pxx", "pyy", "pzz", "pyz", "pxz", "pxy"] 222 | return [lmp.get_thermo(p) for p in p_list] 223 | 224 | # Extract lattice parameter, energy from lammps/vasp output 225 | class get_output: 226 | # Lammps 227 | # thermo: step pe temp press pxx pyy pzz pyz pxz pxy lx ly lz 228 | # 0 1 2 3 4 5 6 7 8 9 10 11 12 229 | # Vasp 230 | # pressure: pxx, pyy, pzz, pxy, pyz, pxz 231 | def __init__(self, target_file, mode=None): 232 | self.target_file = target_file 233 | self.mode = mode 234 | 235 | def grep_E(self): 236 | def grep_E_lammps(): 237 | with open(self.target_file) as infile: 238 | for line in infile: 239 | if re.match(r"Loop time", line): 240 | E = re.split('\s+', line_hist.strip())[1] 241 | return float(E) 242 | else: 243 | line_hist = line 244 | def grep_E_vasp(): 245 | with open(self.target_file) as infile: 246 | for line in infile: 247 | if re.match(".*free energy", line): 248 | match_line = line 249 | E = re.split('\s+', match_line.strip())[4] 250 | return float(E) 251 | return (grep_E_lammps() if self.mode == "Lammps" else grep_E_vasp()) 252 | 253 | def grep_latt(self, lat_rep=[1,1,1]): 254 | def grep_latt_lammps(): 255 | with open(self.target_file) as infile: 256 | for line in infile: 257 | if re.match(r"Loop time", line): 258 | latt = re.split('\s+', line_hist.strip())[-3:] 259 | return [float(latt[i])/lat_rep[i] for i in range(3)] 260 | else: 261 | line_hist = line 262 | def grep_latt_vasp(): 263 | with open(self.target_file) as infile: 264 | for line in infile: 265 | if re.match(".*length of vectors", line): 266 | match_line = next(infile) 267 | latt = re.split('\s+', match_line.strip())[:3] 268 | return [float(latt[i])/lat_rep[i] for i in range(3)] 269 | return (grep_latt_lammps() if self.mode == "Lammps" else grep_latt_vasp()) 270 | 271 | def grep_natoms(self): 272 | def grep_natoms_lammps(): 273 | with open(self.target_file) as infile: 274 | for line in infile: 275 | if re.match(r"Loop time", line): 276 | natoms = re.search(r"with(.*?)atoms", line).group(1) 277 | return int(natoms) 278 | def grep_natoms_vasp(): 279 | with open(self.target_file) as infile: 280 | for line in infile: 281 | if re.match(".*ions per type", line): 282 | natoms = re.split('\s+', line.strip())[4] 283 | return int(natoms) 284 | return (grep_natoms_lammps() if self.mode == "Lammps" else grep_natoms_vasp()) 285 | 286 | def grep_E_per_atom(self): 287 | return self.grep_E()/self.grep_natoms() 288 | 289 | def grep_stress(self): 290 | def grep_stress_lammps(): 291 | with open(self.target_file) as infile: 292 | for line in infile: 293 | if re.match(r"Loop time", line): 294 | stress = re.split('\s+', line_hist.strip())[4:10] 295 | return ([float(entry) for entry in stress]) 296 | else: 297 | line_hist = line 298 | def grep_stress_vasp(): 299 | with open(self.target_file) as infile: 300 | for line in infile: 301 | if re.match(".*in kB", line): 302 | match_line = line 303 | # Notice the order in p is different for vasp as compared to lammps 304 | pxx, pyy, pzz, pxy, pyz, pxz = re.split('\s+', match_line.strip())[2:8] 305 | press = [pxx, pyy, pzz, pyz, pxz, pxy] 306 | return ([float(p) for p in press]) 307 | return (grep_stress_lammps() if self.mode == "Lammps" else grep_stress_vasp()) 308 | 309 | def grep_temp(self): 310 | temp=np.zeros(10) 311 | with open(self.target_file) as infile: 312 | for line in infile: 313 | if re.match(r" 250000", line): 314 | temp[0] = re.split('\s+', line.strip())[-3] 315 | if re.match(r" 245500", line): 316 | temp[1] = re.split('\s+', line.strip())[-3] 317 | if re.match(r" 246000", line): 318 | temp[2] = re.split('\s+', line.strip())[-3] 319 | if re.match(r" 246500", line): 320 | temp[3] = re.split('\s+', line.strip())[-3] 321 | if re.match(r" 247000", line): 322 | temp[4] = re.split('\s+', line.strip())[-3] 323 | if re.match(r" 247500", line): 324 | temp[5] = re.split('\s+', line.strip())[-3] 325 | if re.match(r" 248000", line): 326 | temp[6] = re.split('\s+', line.strip())[-3] 327 | if re.match(r" 248500", line): 328 | temp[7] = re.split('\s+', line.strip())[-3] 329 | if re.match(r" 249000", line): 330 | temp[8] = re.split('\s+', line.strip())[-3] 331 | if re.match(r" 249500", line): 332 | temp[9] = re.split('\s+', line.strip())[-3] 333 | else: 334 | line_hist = line 335 | return float(np.average(temp)) 336 | 337 | # Calculate elastic moduli from pressure 338 | # and other properties such as bulk modulus, etc. 339 | def get_elastic_moduli(pxx, pyy, pzz, pyz, pxz, pxy): 340 | # C is elastic moduli 341 | dp, C = np.zeros((6,6)), np.zeros((6,6)) 342 | for i in range(6): 343 | dp[i,:] = [ (p[i,2]-p[i,1])/2 for p in [pxx, pyy, pzz, pyz, pxz, pxy] ] 344 | for i in range(6): 345 | for j in range(i,6): 346 | C[i,j] = (dp[i,j] + dp[j,i])/2 347 | # Bulk modulus (B), shear modulus (G), and poisson_ratio (Po) 348 | B, G, Po = (C[0,0]+C[1,1]+C[2,2]+2*C[0,1]+2*C[1,2]+2*C[0,2])/9, (C[0,0]-C[0,1])/2, 1.0/(1.0+C[0,0]/C[0,1]) 349 | return (C, B, G, Po) 350 | -------------------------------------------------------------------------------- /main/Lava_Vasp_Header.py: -------------------------------------------------------------------------------- 1 | ############################################################################### 2 | # This module provides header file for vasp INCAR 3 | # Settings in incar_header are global settings that 4 | # are copied to every INCAR file in run time 5 | # Simulation-speficic settings such as NSW, IBRION, ISIF 6 | # are set independently for each type of calculation 7 | # passed as parameters when invoking functions in Lava_Wrapper.py 8 | ############################################################################### 9 | 10 | import numpy as np 11 | import math 12 | import re 13 | 14 | ############################################################################### 15 | 16 | # VASP INCAR header 17 | incar_header = \ 18 | "GGA=PE\n" + \ 19 | "ISMEAR=-5\n" + \ 20 | "POTIM=0.1\n" + \ 21 | "SYMPREC=1E-6\n" + \ 22 | "EDIFF=1E-6\n" + \ 23 | "EDIFFG=-0.01\n" + \ 24 | "ENCUT=500\n" + \ 25 | "ALGO=N\n" + \ 26 | "NPAR=4\n" 27 | 28 | # This is for generating VASP INCAR file 29 | def get_vasp_script(NSW,IBRION,ISIF,NFREE=0): 30 | incar_file = ''.join(open('INCAR.inp',"r").readlines()) 31 | for str_set in (("NSW_val", str(NSW)), ("IBRION_val", str(IBRION)), ("ISIF_val", str(ISIF))): 32 | incar_file = incar_file.replace(*str_set) 33 | if NFREE != 0: 34 | incar_file += "NFREE=%d\n" %NFREE 35 | with open("INCAR","w") as outfile: 36 | outfile.write(incar_file) -------------------------------------------------------------------------------- /main/Lava_Wrapper.py: -------------------------------------------------------------------------------- 1 | ############################################################################### 2 | # Lava Wrapper, where 'Lava' comes from 'La' in lammps and 'va' in vasp 3 | # Provides a one-click interface for calculating common properties in Lammps and Vasp 4 | # Invoke it this way: python3 Lava_Wrapper Lammps/Vasp 5 | # The last argument being Lammps or Vasp 6 | # The results are plotted in JPEG format as well 7 | # Refer to "Summary.dat" for more output 8 | # The types of calculations currently supported are 9 | # 1. get_cohesive_energy: calculate lattice constants and cohesive energy for a list of phases 10 | # 2. get_cold_curve_V: calculate cold curve for a range of volumes for a list of phases 11 | # 3. get_cold_curve_R: calculate cold curve for a list of phases, where the range of volumes are 12 | # centered around the lattice constant of each phases 13 | # 4. get_liquid_rdf: calculate RDF for a list of phases with a range of temperatures 14 | # 5. get_melting_point: calculate melting point using 2-phase method for a list of phases 15 | # 6. get_elastic_properties: calculate elastic properties for a list of phases 16 | # 7. get_thermal_expansion: calculate thermal expansion for a list of phases with a range of temperatures 17 | # 8. get_deformation_path: 1-D mesh volume-conserving Bain/Trigonal path, varying c/a ratio 18 | # 9. get_deformation_path_2D: 2-D contour plot Bain/Trigonal path, varying c/a ratio and volume 19 | # 10. get_vacancy_interstitial_energy: calculate vacancy/interstitial formation energy 20 | # for a list of phases 21 | # 11. get_surface_energy: calculate surface energy for a list of phases with a list of surface indices 22 | # 12. get_stacking_fault: calculate stacking fault energy with a 2-D mesh for FCC/BCC/HCP phase 23 | # 13. get_general_stacking_fault: calculate generalized stacking fault energy for FCC/BCC 24 | ############################################################################### 25 | 26 | import numpy as np 27 | import os 28 | from os import sys 29 | import Lava_Calculator 30 | import Lava_Plotter 31 | 32 | ############################################################################### 33 | 34 | def Calculations(mode=None): 35 | 36 | ### Cold curve based on V 37 | # V_start, V_stop, and number of points 38 | # A mass is supplied in unit of 10^-23 g 39 | # for the purpose of calculating density 40 | # npoints is the number of points for the cold curve 41 | phase_list = ["FCC"] 42 | npoints = 30 43 | #Lava_Calculator.get_cold_curve_V(phase_list, V_start=20, V_stop=40, npoints=npoints, mass=4.48, mode=mode) 44 | 45 | ### Cold curve based on R 46 | ## alat is lattice constant 47 | ## Expanse of lattice (from alat-alat_expanse to alat+alat_expanse) 48 | phase_list = ["FCC"] 49 | npoints = 30 50 | #Lava_Calculator.get_cold_curve_R(phase_list, alat_expanse=1, npoints=npoints, mass=4.48, mode=mode) 51 | 52 | ### Radial distribution function for liquid at different T (Lammps only) 53 | # T_dict is a dictionary with the keys corresponding to a phase 54 | # and values corresponding to a list of temperature 55 | T_dict = {"FCC": range(300,1500,300)} 56 | #Lava_Calculator.get_liquid_rdf(T_dict, mode=mode) 57 | 58 | ### Thermal expansion (Lammps only) 59 | # Refer to "get_liquid_rdf" for T_dict 60 | T_dict = {"FCC": range(100,1100,100)} 61 | #Lava_Calculator.get_thermal_expansion(T_dict, mode=mode) 62 | 63 | ### Melting point using 2 phase method (Lammps only) 64 | ## get_melting_point(Tm, T_error): Tm is trial melting point, T_error is convergence criteria 65 | phase_list = ["FCC"] 66 | #Lava_Calculator.get_melting_point(phase_list, 400,1000, 10, mode=mode) 67 | 68 | ### Elastic properties 69 | phase_list = ["FCC"] 70 | #Lava_Calculator.get_elastic_properties(phase_list, mode=mode) 71 | 72 | ### Volume-conserving Bain path (Default mesh passes right through BCC and FCC) 73 | npoints = 24 74 | #Lava_Calculator.get_deformation_path(0.5, 1.2, npoints, orz=[0,0,1], path_name="Bain", mode=mode) 75 | 76 | ### Volume-conserving Bain path (2D) 77 | # Number of simulations will be npoints^2 78 | npoints = 24 79 | #Lava_Calculator.get_deformation_path_2D(0.5, 1.2, npoints, 0.8, 1.2, npoints, orz=[0,0,1], path_name="Bain", mode=mode) 80 | 81 | ### Volume-conserving Trigonal path (Default mesh passes right through SC, BCC and FCC) 82 | npoints = 24 83 | #Lava_Calculator.get_deformation_path(0.175, 1.175, npoints, orz=[1,1,1], path_name="Trigonal", mode=mode) 84 | 85 | ### Trigonal path 86 | npoints = 24 87 | #Lava_Calculator.get_deformation_path_2D(0.175, 1.175, npoints, 0.8, 1.2, npoints, orz=[1,1,1], path_name="Trigonal", mode=mode) 88 | 89 | ### Vacancy/interstitial formation energy 90 | ## The unit cells are replicated to lat_dim 91 | phase_list = ["FCC"] 92 | #Lava_Calculator.get_vacancy_interstitial_energy(phase_list, lat_dim=[12,12,12], mode=mode) 93 | 94 | ### Surface energy 95 | # Miller-Bravais indices (uvtw) to Miller indices (v, u-v, w) 96 | # For example, (11-2n) to (10n), (10-1n) to (01n), (0001) to (001) 97 | # surface_dict is a dictionary with the keys corresponding to a phase 98 | # and values corresponding to a list of surface miller indices 99 | surface_dict = {"FCC": [(0,0,1), (1,1,0), (1,1,1)]} 100 | #Lava_Calculator.get_surface_energy(surface_dict, mode=mode) 101 | 102 | ### Stacking fault energy: 2-D grid, very expensive for VASP 103 | # Current supports the following three Gamma surfaces system 104 | # FCC -> X: [11-2], Y: [1-10], Z:[111] 105 | # BCC -> X: [111], Y: [-110], Z: [11-2] 106 | # HCP -> X: [11-20], Y: [10-10], Z: [0001] 107 | phase = "FCC" 108 | sf_mesh = [24,24] 109 | #Lava_Calculator.get_stacking_fault(phase, sf_mesh, mode=mode) 110 | 111 | ### General stacking fault curve (full slip vs twin) 112 | # sf_mesh[0] must be a multiply of 3, and sf_mesh[1] must be 1 113 | phase = "FCC" 114 | sf_mesh = [24,1] 115 | #Lava_Calculator.get_general_stacking_fault(phase, sf_mesh, mode=mode) 116 | 117 | # Uniaxial deformation (Lammps_serial only) 118 | # Perform uniaxial deformation along desired direction with erate/trate via a fix/deform command 119 | # deform_mode indicates which deformation mode is performed 120 | # 1=x 2=y,3=z, 4=yz, 5=xz, 6=xy 121 | phase_list = ["FCC"] 122 | temp = 300 123 | lat_rep=[15,15,15] 124 | deform_mode = [1,2,3,4,5,6] 125 | rate = 0.01 126 | final_strain = 0.25 127 | Lava_Calculator.uniaxial_deform(phase_list, temp, deform_mode , rate, final_strain, lat_rep,mode=mode) 128 | 129 | def main(): 130 | # Specify mode: Lammps or Vasp 131 | list_of_mode = ["Lammps", "Vasp"] 132 | if len(sys.argv) != 2: 133 | print ("Please specify one of the following two modes: Lammps or Vasp") 134 | sys.exit() 135 | else: 136 | mode = sys.argv[1] 137 | if mode not in list_of_mode: 138 | print ("Wrong mode specified: %s!" %(mode)) 139 | print ('Must be either "Lammps" or "Vasp"!') 140 | print ("Invoke it this way: 'python3 Lava_Wrapper Lammps/Vasp'") 141 | sys.exit() 142 | 143 | # Check whether there is a lattice data file for use in lammps 144 | if mode == "Lammps": 145 | if not os.path.isfile('Lammps_latt.dat'): 146 | # Lattice constants and cohesive energy 147 | # List of phases supported are the following 148 | phase_list = ["FCC"] 149 | Lava_Calculator.get_cohesive_energy(phase_list, mode=mode) 150 | 151 | Calculations(mode=mode) 152 | 153 | # Check whether there is a lattice data file for use in vasp 154 | elif mode == "Vasp": 155 | if not os.path.isfile('Vasp_latt.dat'): 156 | ### Lattice constants and cohesive energy 157 | phase_list =["FCC"] 158 | Lava_Calculator.get_cohesive_energy(phase_list, mode=mode) 159 | else: 160 | Calculations(mode=mode) 161 | 162 | 163 | if __name__ == "__main__": 164 | main() 165 | -------------------------------------------------------------------------------- /main/RunLammps.inp: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | #SBATCH --time=1:00:00 3 | #SBATCH --ntasks=40 4 | #SBATCH --partition=C5 5 | #SBATCH --job-name=Lava_Wrapper 6 | 7 | module purge 8 | module load gnu8 9 | module load openmpi3 10 | 11 | 12 | mpirun lammps_executable -in lammps.in -sf opt 13 | -------------------------------------------------------------------------------- /main/RunLammps.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | #SBATCH --time=1:00:00 3 | #SBATCH --ntasks=40 4 | #SBATCH --partition=C5 5 | #SBATCH --job-name=Lava_Wrapper 6 | 7 | module purge 8 | module load gnu8 9 | module load openmpi3 10 | 11 | 12 | mpirun /home/kqdang/LAVA_with_user_defined_phase/Lava_latest_03_09_22/lmp_mpi -in Lammps_Uniaxial_Deform.in -sf opt 13 | -------------------------------------------------------------------------------- /main/__pycache__/Lava_Calculator.cpython-38.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lanl/LAVA/2417ac296b21a453a60192e539d7ebcdb80f2172/main/__pycache__/Lava_Calculator.cpython-38.pyc -------------------------------------------------------------------------------- /main/__pycache__/Lava_Config.cpython-38.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lanl/LAVA/2417ac296b21a453a60192e539d7ebcdb80f2172/main/__pycache__/Lava_Config.cpython-38.pyc -------------------------------------------------------------------------------- /main/__pycache__/Lava_Generator.cpython-38.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lanl/LAVA/2417ac296b21a453a60192e539d7ebcdb80f2172/main/__pycache__/Lava_Generator.cpython-38.pyc -------------------------------------------------------------------------------- /main/__pycache__/Lava_Lammps_Header.cpython-38.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lanl/LAVA/2417ac296b21a453a60192e539d7ebcdb80f2172/main/__pycache__/Lava_Lammps_Header.cpython-38.pyc -------------------------------------------------------------------------------- /main/__pycache__/Lava_Plotter.cpython-38.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lanl/LAVA/2417ac296b21a453a60192e539d7ebcdb80f2172/main/__pycache__/Lava_Plotter.cpython-38.pyc -------------------------------------------------------------------------------- /main/__pycache__/Lava_Utility.cpython-38.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lanl/LAVA/2417ac296b21a453a60192e539d7ebcdb80f2172/main/__pycache__/Lava_Utility.cpython-38.pyc -------------------------------------------------------------------------------- /main/__pycache__/Lava_Vasp_Header.cpython-38.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lanl/LAVA/2417ac296b21a453a60192e539d7ebcdb80f2172/main/__pycache__/Lava_Vasp_Header.cpython-38.pyc -------------------------------------------------------------------------------- /main/lmp_mpi: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lanl/LAVA/2417ac296b21a453a60192e539d7ebcdb80f2172/main/lmp_mpi --------------------------------------------------------------------------------