# Copyright (c) Siman Development Team.
# Distributed under the terms of the GNU License.
from __future__ import division, unicode_literals, absolute_import, print_function
import itertools, os, copy, math, glob, re, shutil, sys, pickle, gzip, shutil, random
import re, io, json
import pprint
from textwrap import wrap
import numpy as np
#additional packages
try:
from tabulate import tabulate
except:
print('tabulate is not avail')
tabulate = None
try:
import pandas as pd
except:
print('pandas is not avail')
from siman import header
try:
import pymatgen
header.pymatgen_flag = True
except:
print('classes.py: pymatgen is not available')
header.pymatgen_flag = False
if header.pymatgen_flag:
from pymatgen.io.cif import CifWriter
from pymatgen.symmetry.analyzer import SpacegroupAnalyzer
from pymatgen.core.surface import Slab
from pymatgen.core.composition import Composition
#siman packages
from siman.header import printlog, runBash
from siman import set_functions
# from siman.small_functions import return_xred, makedir, angle, is_string_like, cat_files, grep_file, red_prec, list2string, is_list_like, b2s, calc_ngkpt, setting_sshpass
from siman.small_functions import return_xred, makedir, angle, is_string_like, cat_files, grep_file, red_prec, list2string, is_list_like, b2s, calc_ngkpt, setting_sshpass
from siman.functions import (read_vectors, read_list, words, read_string,
element_name_inv, invert, calculate_voronoi,
get_from_server, push_to_server, run_on_server, smoother, file_exists_on_server, check_output)
from siman.inout import write_xyz, write_lammps, read_xyz, read_poscar, write_geometry_aims, read_aims_out, read_vasp_out
from siman.geo import (image_distance, replic, calc_recip_vectors, calc_kspacings, xred2xcart, xcart2xred,
local_surrounding, local_surrounding2, determine_symmetry_positions, remove_closest, remove_vacuum, make_neutral,
rms_between_structures, rms_between_structures2)
from siman.set_functions import InputSet, aims_keys
"""
Classes used in siman
TODO:
1. Please combine calculate_nbands(), calc_kspacings(), magmom filling in make incar with actualize_set()
2. split make_incar_and_copy_all() into make_incar() and copy_calc_files_to_cluster()
3. split .read_results() into download_output_files() and .parse_outcar() and .analyze_output()
4. outcar name should be returned by write_sge_script in all cases and used, now only in u_ramping
5. write_sge() - в режиме inherit_option = continue - предыдущие outcar резервируются только один
раз с префиксом prev, повторный запуск перезатрет их, поэтому нужно писать спец код
типа
if test -f prev1.outcar
cp name prev2+name
чтобы она находила prev3 с максимальным числом, и к этому числу прибавляла единицу для нового файла
NEW:
Calculation Structure():
*self.magmom* (list) - magnetic moments for each ion in structure; has higher preference than self.set.magnetic_moments which
include only moments for atom types
"""
'aliases required for old databases to work correctly:'
from siman.core.structure import Structure
from siman.core.calculation import Calculation
from siman.calculators.vasp import CalculationVasp
# class Calculation()
[docs]class cd:
"""Context manager for changing the current working directory"""
def __init__(self, newPath):
self.newPath = os.path.expanduser(newPath)
def __enter__(self):
self.savedPath = os.getcwd()
os.chdir(self.newPath)
def __exit__(self, etype, value, traceback):
os.chdir(self.savedPath)
[docs]class empty_struct(): # here it is needed for back-compatability for reading old databases
def __init__(self):
pass
[docs]class Description():
"""
Objects of this class include just folder and description of specific calculation.
Mostly was needed for manual addition of new calculations
self.ngkpt_dict_for_kspacings (dict of lists) - the key is kspacing; the dict
contains k-meshes
for all calculations
based on this geometry structure.
can be useful for fine tuning of k-mesh for specific kspacing.
"""
def __init__(self, sectionfolder = "forgot_folder", description = "forgot_description"):
self.des = description
self.sfolder = sectionfolder
self.ngkpt_dict_for_kspacings = {} #the key is kspacing
[docs]class MP_Compound():
"""This class includes information about chemical compounds from MatProj and next operations (bulk calc, slab construction etc.)
db key is 'pretty_formula.MP': ('AgC.MP')
"""
def __init__(self):
self.pretty_formula = ""
self.material_id = "material_id"
self.elements = []
self.sg_symbol =''
self.sg_crystal_str = ''
self.band_gap = None
self.e_above_hull = None
self.icsd_ids = None
self.total_magnetization = None
self.price_per_gramm = None
self.bulk_cl = None
self.bulk_status = 'Unknown'
[docs] def copy(self):
return copy.deepcopy(self)
[docs] def calc_bulk(self, ise, bulk_cl_name = ['it','ise', '1'], it_folder = 'bulk/', status = 'add'):
from siman.header import db
from siman.calc_manage import add_loop, res_loop
name = '.'.join(bulk_cl_name)
it = '.'.join([self.pretty_formula, self.sg_crystal_str])
st = self.get_st()
self.bulk_cl = name
if status == 'add':
# if bulk_cl_name[0] not in header.struct_des:
add_loop(it,ise,1, input_st = st, it_folder = it_folder, override = 1)
self.bulk_status = 'run'
if status == 'res':
res_loop(it,ise,1)
try:
if max(db[name].maxforce_list[-1]) > 50:
self.bulk_status = 'big max_f'
else:
self.bulk_status = 'calculated'
except AttributeError:
self.bulk_status = 'Error!'
print(name, '\tUnfinished calculation!!!\n\n')
if status == 'add_scale':
# if bulk_cl_name[0] not in header.struct_des:
add_loop(it,ise,1, input_st = st, calc_method = 'uniform_scale', scale_region = (-9, 5), n_scale_images = 10, it_folder = it_folder)
self.bulk_status_scale = 'run_scale'
if status == 'res_scale':
# if bulk_cl_name[0] not in header.struct_des:
name_scale = '.'.join([it,'su',ise,'100'])
self.bulk_cl_scale = name_scale
try:
# res_loop(it+'.su',ise,list(range(1,11))+[100], up = 'up2', show = 'fit', analys_type = 'fit_a')
res_loop(it+'.su',ise,[100], up = 'up2')
except ValueError:
self.bulk_status_scale = 'Error!'
print('\n\nValueError!!!\n\n')
return
try:
if max(db[name_scale].maxforce_list[-1]) > 50:
self.bulk_status_scale = 'big max_f'
else:
self.bulk_status_scale = 'calculated'
except AttributeError:
self.bulk_status_scale = 'Error!'
print(name_scale, '\tUnfinished calculation!!!\n\n')
[docs] def calc_suf(self, **argv):
from siman.matproj_functions import calc_suf_mat
calc_suf_mat(self, **argv)
[docs] def calc_suf_stoich(self, **argv):
from siman.matproj_functions import calc_suf_stoich_mat
calc_suf_stoich_mat(self, **argv)
[docs] def add_relax(self, **argv):
from siman.matproj_functions import add_relax_mat
add_relax_mat(self, **argv)
[docs] def move_suf_en(self, **argv):
from siman.matproj_functions import move_suf_en_mat
move_suf_en_mat(self, **argv)
[docs] def get_st(self, folder = 'geo/'):
"""
check downloaded POSCAR files in geo/ folder
if not POSCAR of some structure - download it from Mat Proj
mat_in_list - data dict for any structure from MP, result of get_fata('mp-...')
"""
from siman.calc_manage import get_structure_from_matproj
from siman.inout import smart_structure_read
name = self.material_id+'.POSCAR'
# st = get_structure_from_matproj(mat_proj_id = self.material_id, it_folder = folder)
if name not in os.listdir(folder):
os.chdir(folder)
st = get_structure_from_matproj(mat_proj_id = self.material_id, it_folder = folder)
os.chdir('..')
else:
st = smart_structure_read(folder+name)
# print('ok')
return st
[docs] def e_cohesive_calc(self, e_box):
from siman.header import db
'''
return cohesive energy
e_box - dict{element: energy_of_element_in_box}
'''
# print(self.bulk_cl_scale)
try:
cl_bulk = db[self.bulk_cl_scale]
e_bulk = cl_bulk.energy_sigma0
n_at_sum = cl_bulk.end.natom
el_list = cl_bulk.end.get_elements()
e_at_sum = 0
for el in el_list:
e_at = e_box[el]
e_at_sum+=e_at
e_coh = (e_at_sum-e_bulk)/n_at_sum
print('{} \t\tE_coh = {} eV'.format(self.pretty_formula, round(e_coh,1)))
self.e_cohesive = round(e_coh,2)
except AttributeError:
self.e_cohesive = None
[docs] def e_cohesive_from_MP(self):
try:
from pymatgen.ext.matproj import MPRester
from pymatgen.io.vasp.inputs import Poscar
from pymatgen.io.cif import CifParser
pymatgen_flag = True
except:
print('pymatgen is not available')
pymatgen_flag = False
with MPRester(header.pmgkey) as m:
material_id = self.material_id
ec = round(m.get_cohesive_energy(material_id, per_atom = 1),2)
self.e_cohesive_MP = ec
print(self.pretty_formula, ec)
[docs] def calc_ec_es(self, ev = 0):
from siman.header import db
'''
'''
ec_es = []
try:
print(self.e_cohesive)
for i in self.suf_en.keys():
# print(self.suf_en)
if self.suf_en[i] !='Error':
# print(self.suf_en[i])
if ev:
ec_es.append(round(float(self.e_cohesive)/float(self.suf_en[i]/ header.eV_A_to_J_m), 2))
else:
ec_es.append(round(float(self.e_cohesive)/float(self.suf_en[i]), 2))
else:
ec_es.append('None')
self.ec_es = ec_es
except AttributeError:
self.ec_es = None