Skip to content
Snippets Groups Projects
Commit 7533d541 authored by Alvin Noe Ladines's avatar Alvin Noe Ladines
Browse files

Implement text_parser changes

parent 9d4518fe
No related branches found
No related tags found
No related merge requests found
# Copyright 2015-2018 Lauri Himanen, Fawzi Mohamed, Ankit Kariryaa
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
from .metainfo import m_env
from nomad.parsing.parser import FairdiParser
from elasticparser.elastic_parser import ElasticParserInterface
class ElasticParser(FairdiParser):
def __init__(self):
super().__init__(
name='parsers/elastic', code_name='elastic', code_homepage='http://exciting-code.org/elastic',
mainfile_contents_re=r'\s*Order of elastic constants\s*=\s*[0-9]+\s*',
mainfile_name_re=(r'.*/INFO_ElaStic'))
def parse(self, filepath, archive, logger=None):
self._metainfo_env = m_env
parser = ElasticParserInterface(filepath, archive, logger)
parser.parse()
from elasticparser.elastic_parser import ElasticParser
......@@ -2,22 +2,26 @@ import os
import numpy as np
import logging
from nomad.datamodel.metainfo.public import section_run, section_system,\
section_single_configuration_calculation, section_method, section_calculation_to_calculation_refs,\
Workflow, Elastic
from nomad.datamodel.metainfo.common_dft import Run, System, SingleConfigurationCalculation,\
Method, CalculationToCalculationRefs, Workflow, Elastic
from nomad.parsing.parser import FairdiParser
from elasticparser.metainfo.elastic import x_elastic_section_strain_diagrams,\
x_elastic_section_fitting_parameters
from elasticparser.elastic_properties import ElasticProperties
from .metainfo import m_env
class ElasticParserInterface:
def __init__(self, filepath, archive, logger=None):
self.filepath = os.path.abspath(filepath)
self.archive = archive
self.logger = logger if logger is not None else logging
self.properties = ElasticProperties(self.filepath)
class ElasticParser(FairdiParser):
def __init__(self):
super().__init__(
name='parsers/elastic', code_name='elastic', code_homepage='http://exciting-code.org/elastic',
mainfile_contents_re=r'\s*Order of elastic constants\s*=\s*[0-9]+\s*',
mainfile_name_re=(r'.*/INFO_ElaStic'))
self._metainfo_env = m_env
self.properties = ElasticProperties()
def parse_strain(self):
sec_scc = self.archive.section_run[-1].section_single_configuration_calculation[-1]
......@@ -95,7 +99,6 @@ class ElasticParserInterface:
if order == 2:
matrices, moduli, eigenvalues = self.properties.get_elastic_constants_order2()
sec_scc.x_elastic_2nd_order_constants_notation_matrix = matrices['voigt']
sec_scc.x_elastic_2nd_order_constants_matrix = matrices['elastic_constant']
sec_scc.x_elastic_2nd_order_constants_compliance_matrix = matrices['compliance']
......@@ -123,14 +126,23 @@ class ElasticParserInterface:
sec_scc.x_elastic_3rd_order_constants_matrix = elastic_constant
def parse(self):
def _init_parsers(self):
self.properties.mainfile = self.filepath
self.properties.logger = self.logger
def parse(self, filepath, archive, logger):
self.filepath = os.path.abspath(filepath)
self.archive = archive
self.logger = logger if logger is not None else logging
self._init_parsers()
sec_run = self.archive.m_create(section_run)
sec_run = self.archive.m_create(Run)
sec_run.program_name = 'elastic'
sec_run.program_version = '1.0'
sec_system = sec_run.m_create(section_system)
sec_system = sec_run.m_create(System)
symbols, positions, cell = self.properties.get_structure_info()
volume = self.properties.info['equilibrium_volume']
......@@ -142,7 +154,7 @@ class ElasticParserInterface:
sec_system.x_elastic_space_group_number = self.properties.info['space_group_number']
sec_system.x_elastic_unit_cell_volume = volume
sec_method = sec_run.m_create(section_method)
sec_method = sec_run.m_create(Method)
sec_method.x_elastic_elastic_constant_order = self.properties.info['order']
sec_method.x_elastic_calculation_method = self.properties.info['calculation_method']
sec_method.x_elastic_code = self.properties.info['code_name']
......@@ -154,9 +166,9 @@ class ElasticParserInterface:
sec_method.x_elastic_number_of_deformations = len(self.properties.deformation_dirs)
references = self.properties.get_references_to_calculations()
sec_scc = sec_run.m_create(section_single_configuration_calculation)
sec_scc = sec_run.m_create(SingleConfigurationCalculation)
for reference in references:
sec_calc_ref = sec_scc.m_create(section_calculation_to_calculation_refs)
sec_calc_ref = sec_scc.m_create(CalculationToCalculationRefs)
sec_calc_ref.calculation_to_calculation_external_url = reference
sec_calc_ref.calculation_to_calculation_kind = 'source_calculation'
......
......@@ -3,16 +3,154 @@ import pint
import numpy as np
from ase import Atoms
from nomad.parsing.file_parser import Quantity, UnstructuredTextFileParser
from nomad.parsing.file_parser import Quantity, TextParser
class InfoParser(TextParser):
def __init__(self):
super().__init__(None)
def init_quantities(self):
self._quantities = [
Quantity(
'order', r'\s*Order of elastic constants\s*=\s*([0-9]+)', repeats=False,
dtype=int),
Quantity(
'calculation_method', r'\s*Method of calculation\s*=\s*([-a-zA-Z]+)\s*',
repeats=False),
Quantity(
'code_name', r'\s*DFT code name\s*=\s*([-a-zA-Z]+)', repeats=False),
Quantity(
'space_group_number', r'\s*Space-group number\s*=\s*([0-9]+)', repeats=False),
Quantity(
'equilibrium_volume', r'\s*Volume of equilibrium unit cell\s*=\s*([0-9.]+)\s*',
unit='angstrom ** 3'),
Quantity(
'max_strain', r'\s*Maximum Lagrangian strain\s*=\s*([0-9.]+)', repeats=False),
Quantity(
'n_strains', r'\s*Number of distorted structures\s*=\s*([0-9]+)', repeats=False)]
class StructureParser(TextParser):
def __init__(self):
super().__init__(None)
def init_quantities(self):
def get_sym_pos(val):
val = val.strip().replace('\n', '').split()
sym = []
pos = []
for i in range(0, len(val), 4):
sym.append(val[i + 3].strip())
pos.append([float(val[j]) for j in range(i, i + 3)])
sym_pos = dict(symbols=sym, positions=pos)
return sym_pos
self._quantities = [
Quantity(
'cellpar', r'a\s*b\s*c\n([\d\.\s]+)\n\s*alpha\s*beta\s*gamma\n([\d\.\s]+)\n+',
repeats=False),
Quantity(
'sym_pos', r'Atom positions:\n\n([\s\d\.A-Za-z]+)\n\n',
str_operation=get_sym_pos, repeats=False, convert=False)]
class DistortedParametersParser(TextParser):
def __init__(self):
super().__init__(None)
def init_quantities(self):
self._quantities = [Quantity(
'deformation', r'Lagrangian strain\s*=\s*\(([eta\s\d\.,]+)\)',
str_operation=lambda x: x.replace(',', '').split(), repeats=True, dtype=str)]
class FitParser(TextParser):
def __init__(self):
super().__init__(None)
def init_quantities(self):
def split_eta_val(val):
order, val = val.strip().split(' order fit.')
val = [float(v) for v in val.strip().split()]
return order, val[0::2], val[1::2]
self._quantities = [Quantity(
'fit', r'(\w+ order fit\.\n[\d.\s\neE\-\+]+)\n', repeats=True, convert=False,
str_operation=split_eta_val)]
class ElasticConstant2Parser(TextParser):
def __init__(self):
super().__init__(None)
def init_quantities(self):
self._quantities = [
Quantity(
'voigt', r'Symmetry[\s\S]+\n\s*\n([C\d\s\n\(\)\-\+\/\*]+)\n',
shape=(6, 6), dtype=str, repeats=False),
Quantity(
'elastic_constant', r'Elastic constant[\s\S]+in GPa\s*:\s*\n\n([\-\d\.\s\n]+)\n',
shape=(6, 6), dtype=float, unit='GPa', repeats=False),
Quantity(
'compliance', r'Elastic compliance[\s\S]+in 1/GPa\s*:\s*\n\n([\-\d\.\s\n]+)\n',
shape=(6, 6), dtype=float, unit='1/GPa', repeats=False)]
def str_to_modulus(val_in):
val_in = val_in.strip().split()
key = val_in[0]
unit = val_in[-1] if len(val_in) == 3 else None
val = float(val_in[1])
val = pint.Quantity(val, unit) if unit is not None else val
return key, val
self._quantities.append(Quantity(
'modulus', r',\s*(\w+)\s*=\s*([\-\+\w\. ]+?)\n', str_operation=str_to_modulus,
repeats=True))
self._quantities.append(Quantity(
'eigenvalues',
r'Eigenvalues of elastic constant \(stiffness\) matrix:\s*\n+([\-\d\.\n\s]+)\n',
unit='GPa', repeats=False))
class ElasticConstant3Parser(TextParser):
def __init__(self):
super().__init__(None)
def init_quantities(self):
def arrange_matrix(val):
val = val.strip().split('\n')
matrix = [v.strip().split() for v in val if v.strip()]
matrix = np.array(matrix).reshape((12, 18))
arranged = []
for i in range(2):
for j in range(3):
arranged.append(
matrix[i * 6: (i + 1) * 6, j * 6: (j + 1) * 6].tolist())
return arranged
self._quantities = [
Quantity(
'elastic_constant', r'\%\s*\n([\s0-6A-L]*)[\n\s\%1-6\-ij]*([\s0-6A-L]*)\n',
str_operation=arrange_matrix, dtype=str, repeats=False, convert=False),
Quantity(
'cijk', r'(C\d\d\d)\s*=\s*([\-\d\.]+)\s*GPa', repeats=True, convert=False)]
class ElasticProperties:
def __init__(self, mainfile):
self._mainfile = mainfile
self.maindir = os.path.dirname(mainfile)
def __init__(self):
self._mainfile = None
self.logger = None
self._deform_dirs = None
self._deform_dir_prefix = 'Dst'
self._info = None
self.info = InfoParser()
self.structure = StructureParser()
self.distorted_parameters = DistortedParametersParser()
self.fit = FitParser()
self.elastic_constant_2 = ElasticConstant2Parser()
self.elastic_constant_3 = ElasticConstant3Parser()
@property
def mainfile(self):
......@@ -20,31 +158,10 @@ class ElasticProperties:
@mainfile.setter
def mainfile(self, val):
self._info = None
self._deform_dirs = None
self._mainfile = val
@property
def info(self):
if self._info is None:
quantities = [
Quantity('order', r'\s*Order of elastic constants\s*=\s*([0-9]+)'),
Quantity('calculation_method', r'\s*Method of calculation\s*=\s*([-a-zA-Z]+)\s*'),
Quantity('code_name', r'\s*DFT code name\s*=\s*([-a-zA-Z]+)'),
Quantity('space_group_number', r'\s*Space-group number\s*=\s*([0-9]+)'),
Quantity('equilibrium_volume', r'\s*Volume of equilibrium unit cell\s*=\s*([0-9.]+)\s*', unit='angstrom ** 3'),
Quantity('max_strain', r'\s*Maximum Lagrangian strain\s*=\s*([0-9.]+)'),
Quantity('n_strains', r'\s*Number of distorted structures\s*=\s*([0-9]+)')
]
parser = UnstructuredTextFileParser(self.mainfile, quantities)
self._info = {}
for key, val in parser.items():
self._info[key] = val[0]
return self._info
self.maindir = os.path.dirname(val) if val is not None else None
self.info.mainfile = val
@property
def deformation_dirs(self):
......@@ -57,7 +174,7 @@ class ElasticProperties:
def get_references_to_calculations(self):
def output_file(dirname):
code = self.info['code_name'].lower()
code = self.info.get('code_name', '').lower()
if code == 'exciting':
return os.path.join(dirname, 'INFO.OUT')
elif code == 'wien':
......@@ -83,28 +200,16 @@ class ElasticProperties:
if not os.path.isfile(path):
return
def get_sym_pos(val):
val = val.strip().replace('\n', '').split()
sym = []
pos = []
for i in range(0, len(val), 4):
sym.append(val[i + 3].strip())
pos.append([float(val[j]) for j in range(i, i + 3)])
sym_pos = dict(symbols=sym, positions=pos)
return sym_pos
quantities = [
Quantity('cellpar', r'a\s*b\s*c\n([\d\.\s]+)\n\s*alpha\s*beta\s*gamma\n([\d\.\s]+)\n+'),
Quantity('sym_pos', r'Atom positions:\n\n([\s\d\.A-Za-z]+)\n\n', str_operation=get_sym_pos)
]
self.structure.mainfile = path
parser = UnstructuredTextFileParser(path, quantities)
cellpar = self.structure.get('cellpar', None)
sym_pos = self.structure.get('sym_pos', {})
cellpar = list(parser['cellpar'])[0]
sym_pos = list(parser['sym_pos'])[0]
sym = sym_pos.get('symbols', None)
pos = sym_pos.get('positions', None)
sym = list(sym_pos['symbols'])
pos = list(sym_pos['positions'])
if cellpar is None or sym is None or pos is None:
return
structure = Atoms(cell=cellpar, scaled_positions=pos, symbols=sym, pbc=True)
......@@ -125,13 +230,10 @@ class ElasticProperties:
path = os.path.join(deform_dir, filenames[-1])
data = np.loadtxt(path).T
strains.append(data[0])
energies.append(data[1])
energies = pint.Quantity(energies, 'hartree')
strains.append(list(data[0]))
# the peculiarity of the x_elastic_strain_diagram_values metainfo that it does
# not have the energy unit
energies = energies.to('J').magnitude
energies.append(list(pint.Quantity(data[1], 'hartree').to('J').magnitude))
return strains, energies
......@@ -167,18 +269,8 @@ class ElasticProperties:
def get_deformation_types(self):
path = os.path.join(self.maindir, 'Distorted_Parameters')
if not os.path.isfile(path):
return
quantities = [
Quantity(
'deformation', r'Lagrangian strain\s*=\s*\(([eta\s\d\.,]+)\)',
str_operation=lambda x: x.replace(',', '').split(), dtype=str)]
parser = UnstructuredTextFileParser(path, quantities)
deformation_types = [list(d) for d in list(parser['deformation'])]
return deformation_types
self.distorted_parameters.mainfile = path
return self.distorted_parameters.get('deformation')
def _get_fit(self, path_dir, file_ext):
path_dir = os.path.join(self.maindir, path_dir)
......@@ -192,32 +284,17 @@ class ElasticProperties:
if not paths:
return
eta = {'2nd': [], '3rd': [], '4th': [], '5th': [], '6th': [], '7th': []}
val = {'2nd': [], '3rd': [], '4th': [], '5th': [], '6th': [], '7th': []}
def split_eta_val(val):
val = val.strip().split()
return [val[0::2], val[1::2]]
quantities = [
Quantity(
order, r'%s order fit.\n([\d.\s\ne\-\+]+)\n' % order, str_operation=split_eta_val)
for order in eta.keys()
]
parser = UnstructuredTextFileParser(os.path.join(path_dir, paths[0]), quantities)
eta, val = {}, {}
for path in paths:
parser.mainfile = os.path.join(path_dir, path)
for key in eta.keys():
if parser[key] is not None:
eta[key].append(list(parser[key][0][0]))
val[key].append(list(parser[key][0][1]))
eta = {key: val for key, val in eta.items() if val}
val = {key: val for key, val in val.items() if val}
self.fit.mainfile = os.path.join(path_dir, path)
fit_results = self.fit.get('fit', [])
for result in fit_results:
eta.setdefault(result[0], [])
val.setdefault(result[0], [])
eta[result[0]].append(result[1])
val[result[0]].append(result[2])
return [eta, val]
return eta, val
def get_energy_fit(self):
energy_fit = dict()
......@@ -227,14 +304,18 @@ class ElasticProperties:
if result is None:
continue
result = list(result)
result[1] = {
key: pint.Quantity(val, 'GPa').to('Pa').magnitude for key, val in result[1].items()}
key: pint.Quantity(
val, 'GPa').to('Pa').magnitude for key, val in result[1].items()}
energy_fit['d2e'] = result
result = self._get_fit('Energy-vs-Strain', 'CVe.dat')
if result is not None:
result = list(result)
result[1] = {
key: pint.Quantity(val, 'hartree').to('J').magnitude for key, val in result[1].items()}
key: pint.Quantity(
val, 'hartree').to('J').magnitude for key, val in result[1].items()}
energy_fit['cross-validation'] = result
return energy_fit
......@@ -260,7 +341,7 @@ class ElasticProperties:
def get_input(self):
paths = os.listdir(self.maindir)
path = None
order = self.info['order']
order = self.info.get('order', 2)
for p in paths:
if 'ElaStic_' in p and p.endswith('.in') and str(order) in p:
path = p
......@@ -269,7 +350,7 @@ class ElasticProperties:
if path is None:
return
calc_method = self.info['calculation_method']
calc_method = self.info.get('calculation_method')
eta_ec = []
fit_ec = []
......@@ -309,89 +390,33 @@ class ElasticProperties:
def get_elastic_constants_order2(self):
path = os.path.join(self.maindir, 'ElaStic_2nd.out')
def reshape(val):
return np.array(val.split()).reshape((6, 6))
self.elastic_constant_2.mainfile = path
quantities = [
Quantity(
'voigt', r'Symmetry[\s\S]+\n\s*\n([C\d\s\n\(\)\-\+\/\*]+)\n',
str_operation=reshape, dtype=str),
Quantity(
'elastic_constant', r'Elastic constant[\s\S]+in GPa\s*:\s*\n\n([\-\d\.\s\n]+)\n',
str_operation=reshape, dtype=float, unit='GPa'),
Quantity(
'compliance', r'Elastic compliance[\s\S]+in 1/GPa\s*:\s*\n\n([\-\d\.\s\n]+)\n',
str_operation=reshape, dtype=float, unit='1/GPa')]
modulus_names = [
'B_V', 'K_V', 'G_V', 'B_R', 'K_R', 'G_R', 'B_H', 'K_H', 'G_H', 'E_V',
'nu_V', 'E_R', 'nu_R', 'E_H', 'nu_H', 'AVR']
for name in modulus_names:
unit = None
if name[0:2] in ('B_', 'K_', 'G_', 'E_'):
unit = 'GPa'
quantities.append(
Quantity(name, r'%s\s*=\s*([-+\d+\.]+)\s*' % name, unit=unit))
quantities += [
Quantity(
'eigenvalues', r'Eigenvalues of elastic constant \(stiffness\) matrix:\s*\n+([\-\d\.\n\s]+)\n', unit='GPa')]
parser = UnstructuredTextFileParser(path, quantities)
matrices = dict(
voigt=list(parser['voigt'])[0], elastic_constant=list(parser['elastic_constant'])[0],
compliance=list(parser['compliance'])[0])
matrices = dict()
for key in ['voigt', 'elastic_constant', 'compliance']:
val = self.elastic_constant_2.get(key, None)
if val is not None:
matrices[key] = val
moduli = dict()
for name in modulus_names:
if parser[name] is not None:
moduli[name] = parser[name][0]
for modulus in self.elastic_constant_2.get('modulus', []):
moduli[modulus[0]] = modulus[1]
eigenvalues = list(parser['eigenvalues'])[0]
eigenvalues = self.elastic_constant_2.get('eigenvalues')
return matrices, moduli, eigenvalues
def get_elastic_constants_order3(self):
path = os.path.join(self.maindir, 'ElaStic_3rd.out')
elastic_constant = []
self.elastic_constant_3.mainfile = path
def arrange_matrix(val):
val = val.strip().split('\n')
matrix = [v.strip().split() for v in val if v.strip()]
matrix = np.array(matrix).reshape((12, 18))
arranged = []
for i in range(2):
for j in range(3):
arranged.append(
matrix[i * 6: (i + 1) * 6, j * 6: (j + 1) * 6].tolist())
return arranged
elastic_constant_str = self.elastic_constant_3.get('elastic_constant')
if elastic_constant_str is None:
return
def get_cijk(val):
val = val.replace('=', '').replace('GPa', '')
cijk = dict()
for v in val.split('\n'):
v = v.split()
if len(v) == 2:
cijk[v[0].strip()] = float(v[1])
return cijk
space_group_number = self.info['space_group_number']
quantities = [
Quantity(
'elastic_constant', r'\%\s*\n([\s0-6A-L]*)[\n\s\%1-6\-ij]*([\s0-6A-L]*)\n',
str_operation=arrange_matrix, dtype=str),
Quantity(
'cijk', r'(C\d{3}\s*=\s*[C1-6\s=\n\-\.0-9GPa]*)\n\n', str_operation=get_cijk)
]
parsers = UnstructuredTextFileParser(path, quantities)
elastic_constant_str = list(parsers['elastic_constant'])[0]
cijk = list(parsers['cijk'])[0]
for element in self.elastic_constant_3.get('cijk', []):
cijk[element[0]] = float(element[1])
# formulas for the coefficients
coeff_A = cijk.get('C111', 0) + cijk.get('C112', 0) - cijk.get('C222', 0)
......@@ -406,6 +431,8 @@ class ElasticProperties:
coeff_J = (cijk.get('C113', 0) - cijk.get('C123', 0)) / 2
coeff_K = -(cijk.get('C144', 0) - cijk.get('C155', 0)) / 2
space_group_number = self.info.get('space_group_number')
if space_group_number <= 148: # rhombohedral II
coefficients = dict(
A=coeff_A, B=coeff_B, C=coeff_C, D=coeff_D, E=coeff_E, F=coeff_F, G=coeff_G,
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment