Source code for schrodinger.application.matsci.qexsd.qespresso.cards

#
# Copyright (c), 2015-2016, Quantum Espresso Foundation and SISSA (Scuola
# Internazionale Superiore di Studi Avanzati). All rights reserved.
# This file is distributed under the terms of the MIT License. See the
# file 'LICENSE' in the root directory of the present distribution, or
# http://opensource.org/licenses/MIT.
# Authors: Davide Brunato, Giovanni Borghi
#
"""
Conversion functions for Quantum Espresso cards.
"""

import collections
import logging

# from .utils import set_logger

logger = logging.getLogger('qespresso')


#
# Functions for QE cards
#
[docs]def get_atomic_species_card(name, **kwargs): """ Convert XML data to ATOMIC_SPECIES card :param name: Card name :param kwargs: Dictionary with converted data from XML file :return: List of strings """ try: atomic_species = kwargs['atomic_species'] species = atomic_species['species'] except KeyError as err: logger.error( "Missing required arguments when building ATOMIC_SPECIES card! %s" % err) return [] lines = [name] try: lines.append(' {0} {1} {2}'.format(species['name'], species['mass'], species['pseudo_file'])) except TypeError: for specie in species: lines.append(' {0} {1} {2}'.format(specie['name'], specie['mass'], specie['pseudo_file'])) return lines
[docs]def get_atomic_positions_cell_card(name, **kwargs): """ Convert XML data to ATOMIC_POSITIONS card :param name: Card name :param kwargs: Dictionary with converted data from XML file :return: List of strings """ try: atomic_structure = kwargs['atomic_structure'] except KeyError: logger.error( "Missing required arguments when building ATOMIC_POSITIONS card!") return [] # Find atoms atomic_positions = atomic_structure.get('atomic_positions', {}) wyckoff_positions = atomic_structure.get('wyckoff_positions', {}) crystal_positions = atomic_structure.get('crystal_positions', {}) units = 'bohr' try: is_wyckoff = False is_crystal = False if atomic_positions: atoms = atomic_positions['atom'] elif wyckoff_positions: atoms = wyckoff_positions['atom'] is_wyckoff = True units = 'crystal_sg' elif crystal_positions: atoms = crystal_positions['atom'] is_crystal = True units = 'crystal' else: atoms = [] except KeyError: logger.error("Cannot find any atoms for building ATOMIC_POSITIONS!") return [] else: if not isinstance(atoms, list): atoms = [atoms] # Check atoms with position constraints free_positions = kwargs.get('free_positions', []) if free_positions: if isinstance(free_positions, collections.Mapping): # Cover cases when free_positions are defined with: # <free_positions rank="2" dims="3 2" order="F"> free_positions = free_positions['_text'] # Cover the case when free positions are provided for only one atom if type(free_positions[0]) not in (list, tuple): free_positions = [free_positions] if len(free_positions) != len(atoms): logger.error( "ATOMIC_POSITIONS: incorrect number of position constraints!") # Add atomic positions lines = ['%s %s' % (name, units)] for k in range(len(atoms)): line = '{:4}'.format(atoms[k]['name']) line += ' {:12.8f} {:12.8f} {:12.8f}'.format(*atoms[k]['_text']) #coords = ' '.join([str(value) for value in atoms[k]['_text']]) if free_positions: #free_pos = ' '.join([str(value) for value in free_positions[k]]) line += ' {:4d}{:4d}{:4d}'.format(*map(int, free_positions[k])) lines.append(line) return lines
[docs]def get_atomic_constraints_card(name, **kwargs): """ Convert XML data to CONSTRAINTS card :param name: Card name :param kwargs: Dictionary with converted data from XML file :return: List of strings """ try: atomic_constraints = kwargs['atomic_constraints'] except KeyError: return [] num_of_constraints = atomic_constraints['num_of_constraints'] tolerance = atomic_constraints.get('tolerance') list_of_constraints = atomic_constraints['atomic_constraint'] if len(list_of_constraints) != num_of_constraints: logger.error( "The number of provided constraints is different from num_of_constraints" ) lines = [name] if tolerance: lines.append('{} {}'.format(num_of_constraints, tolerance)) else: lines.append('{}'.format(num_of_constraints)) for constr in list_of_constraints: c_type = constr['constr_type'] parms = constr['constr_parms'] if c_type in ['distance', 'planar_angle', 'torsional_angle']: parms = [int(_) for _ in parms] elif c_type in ['type_coord', 'atom_coord']: parms = [int(_) for _ in parms[:2]] + parms[2:] elif c_type in ['bennet_proj']: parms = [int(parms[0])] + parms[1:] target = constr['constr_target'] constr_line = "'{}' " + len(parms) * ' {} ' + ' {}' lines.append(constr_line.format(*((c_type,) + tuple(parms) + (target,)))) return lines
[docs]def get_k_points_card(name, **kwargs): """ Convert XML data to K_POINTS card :param name: Card name :param kwargs: Dictionary with converted data from XML file :return: List of strings """ k_point = nk = monkhorst_pack = None gamma_only = kwargs.get('gamma_only', False) try: if not gamma_only: k_points_ibz = kwargs['k_points_IBZ'] monkhorst_pack = k_points_ibz.get('monkhorst_pack', {}) if monkhorst_pack: k_attrib = 'automatic' else: k_attrib = None k_point = k_points_ibz['k_point'] nk = k_points_ibz['nk'] else: k_attrib = 'gamma' except KeyError as err: logger.error( "Missing required arguments when building K_POINTS card! %s" % err) return [] else: if not isinstance(k_point, list): k_point = [k_point] lines = [name] if k_attrib is None else ['%s %s' % (name, k_attrib)] if k_attrib is None: lines.append(' %d' % nk) for point in k_point: lines.append(' {0} {1}'.format( ' '.join([str(value) for value in point['_text']]), point['weight'])) elif k_attrib == 'automatic': lines.append(' %(nk1)s %(nk2)s %(nk3)s %(k1)s %(k2)s %(k3)s' % monkhorst_pack) return lines
[docs]def get_atomic_forces_card(name, **kwargs): """ Convert XML data to ATOMIC_FORCES card :param name: Card name :param kwargs: Dictionary with converted data from XML file :return: List of strings """ try: external_atomic_forces = kwargs['external_atomic_forces'] except KeyError: logger.debug( "Missing required arguments when building ATOMIC_FORCES card!") return [] if len(external_atomic_forces) == 0: return [] # Warning if number of atoms in atomic positions differ with forces try: atomic_positions = kwargs.get('atomic_positions', {}) except KeyError: atomic_positions = kwargs.get('crystal_positions', {}) atoms = atomic_positions.get('atom', []) if atoms and len(atoms) != len(external_atomic_forces): logger.error("incorrect number of atomic forces") # Build input card text lines lines = [name] for forces in external_atomic_forces: lines.append(' {0}'.format(' '.join([str(value) for value in forces]))) return lines
[docs]def get_cell_parameters_card(name, **kwargs): """ Convert XML data to CELL_PARAMETERS card :param name: Card name :param kwargs: Dictionary with converted data from XML file :return: List of strings """ try: atomic_structure = kwargs['atomic_structure'] except KeyError: logger.error( "Missing required arguments when building ATOMIC_POSITIONS card!") return [] # Add cell parameters card cells = atomic_structure.get('cell', {}) if cells: lines = ['%s bohr' % name] for key in sorted(cells): if key not in ['a1', 'a2', 'a3']: continue lines.append((3 * '{:12.8f} ').format(*cells[key])) return lines return []
# # Phonon Card #
[docs]def get_qpoints_card(name, **kwargs): """ :param name: :param kwargs: :return: """ try: ldisp = kwargs['ldisp'] except KeyError: ldisp = False if ldisp: return [] try: qplot = kwargs['qplot'] except KeyError: qplot = False try: ldisp = kwargs['ldisp'] except KeyError: ldisp = False if not (qplot or ldisp): try: xq = kwargs['xq_dir'] except KeyError: xq = [0.e0, 0.e0, 0.e0] line = "{:6.4f} {:8.4f} {:8.4f}".format(xq[0], xq[1], xq[2]) return [line] lines = [] if (qplot): try: nqs = kwargs['nqs'] except KeyError: nqs = 1 raise RuntimeWarning( "qplot was set to true in input but no value for nqs was provided assuming nqs = 1" ) lines.append('{:4d}'.format(nqs)) q_points_list = kwargs['q_points_list']['q_point'] for q_point in q_points_list: vector = ' '.join([str(coord) for coord in q_point['_text']]) lines.append(' %s %s' % (vector, q_point['weight'])) return lines
[docs]def get_climbing_images(name, **kwargs): manual_images = False try: if kwargs['climbingImage'] == 'manual' or kwargs[ 'climbingImage'] == 'MANUAL': manual_images = True except KeyError: manual_images = False if manual_images: if isinstance(kwargs['climbingImageIndex'], list): line = [int(l) for l in kwargs['climbingImageIndex']] fmt = len(line) * ' %d, ' line = fmt % tuple(line) else: line = ' %d ' % int(kwargs['climbingImageIndex']) return [line] return ['']
[docs]def get_neb_images_positions_card(name, **kwargs): """ Extract atomic posisitions for each image provided in engine with an atomic_structure element :param name: Card name :param kwargs: List of dictionaries each containing an atomic_structure element. :return: List of lines """ images = kwargs.get('atomic_structure', []) try: assert isinstance(images, list) except AssertionError: images = [images] if len(images) < 2: logger.error( "At least the atomic structures for first and last image should be provided" ) return [] first_positions = images[0].get('crystal_positions') units = 'crystal' if first_positions is None: first_positions = images[0].get('atomic_positions', {}) units = 'bohr' his_nat = int(images[0].get('nat', 0)) if units == 'crystal': last_positions = images[-1].get('crystal_positions', {}) else: last_positions = images[-1].get('atomic_positions', {}) if len(kwargs['atomic_structure']) > 2: if units == 'crystal': interm_pos = [ ats.get('crystal_positions', {}) for ats in images[1:-1] ] else: interm_pos = [ ats.get('atomic_positions', {}) for ats in images[1:-1] ] else: interm_pos = [] lines = ['%s ' % 'BEGIN_POSITIONS'] lines.append('%s ' % 'FIRST_IMAGE') atoms = first_positions.get('atom', []) my_nat = len(atoms) if my_nat <= 0: logger.error("No atomic coordinates provided for first image") return '' if my_nat != his_nat: logger.error( "nat provided in first image differs from number of atoms in atomic_positions!!!" ) free_positions = kwargs.get('free_positions', []) if isinstance(free_positions, collections.Mapping): # Cover cases when free_positions are defined with: # <free_positions rank="2" dims="3 2" order="F"> free_positions = free_positions['_text'] if free_positions and len(free_positions) != len(atoms): logger.error( "ATOMIC_POSITIONS: incorrect number of position constraints!") lines.append('%s { %s }' % ('ATOMIC_POSITIONS', units)) for k in range(len(atoms)): sp_name = '{:4}'.format(atoms[k]['name']) coords = '{:12.8f} {:12.8f} {:12.8f}'.format(*atoms[k]['_text']) if k < len(free_positions): free_pos = '{:4d}{:4d}{:4d}'.format( *[int(value) for value in free_positions[k]]) lines.append('%s %s %s' % (sp_name, coords, free_pos)) else: lines.append('%s %s' % (sp_name, coords)) for inter in interm_pos: atoms = inter['atom'] if len(atoms) != my_nat: logger.error('Found images with differing number of atoms !!!') lines.append('%s ' % 'INTERMEDIATE_IMAGE') lines.append('%s { %s }' % ('ATOMIC_POSITIONS', units)) for k in range(len(atoms)): sp_name = '{:4}'.format(atoms[k]['name']) coords = '{:12.8f} {:12.8f} {:12.8f}'.format(*atoms[k]['_text']) if k < len(free_positions): free_pos = '{:4d}{:4d}{:4d}'.format( *[int(value) for value in free_positions[k]]) lines.append('%s %s %s' % (sp_name, coords, free_pos)) else: lines.append('%s %s' % (sp_name, coords)) atoms = last_positions['atom'] if len(atoms) != my_nat: logger.error('Found images with differing number of atoms !!!') lines.append('%s ' % 'LAST_IMAGE') lines.append('%s { %s }' % ('ATOMIC_POSITIONS', units)) for k in range(len(atoms)): sp_name = '{:4}'.format(atoms[k]['name']) coords = '{:12.8f} {:12.8f} {:12.8f}'.format(*atoms[k]['_text']) if k < len(free_positions): free_pos = '{:4d}{:4d}{:4d}'.format( *[int(value) for value in free_positions[k]]) lines.append('%s %s %s' % (sp_name, coords, free_pos)) else: lines.append('%s %s' % (sp_name, coords)) lines.append('%s ' % 'END_POSITIONS') return lines
[docs]def get_neb_cell_parameters_card(name, **kwargs): """ Extract cell parameter from the firt of the atomic_structure elements provided in engine :param name: Card name :param kwargs: list of the atomic_structure dictionaries translate for xml element engine :return: list of text lines for the cell parameters card """ images = kwargs.get('atomic_structure', []) try: assert isinstance(images, list) except AssertionError: images = [images] if len(images) < 1: logger.error(" No atomic_strucure element found in kwargs !!!") return '' atomic_structure = images[0] cells = atomic_structure.get('cell', {}) if cells: lines = ['%s bohr' % name] for key in sorted(cells): if key not in ['a1', 'a2', 'a3']: continue lines.append((3 * '{:12.8f}').format(*cells[key])) return lines return []
[docs]def get_neb_atomic_forces_card(name, **kwargs): pass