"""Provide a simple parser for pseudo potentials
The psp8 format is defined in abinit manual
https://docs.abinit.org/developers/psp8_info/
The first
"""
import os
import re
import shutil
from pathlib import Path
from warnings import warn
import numpy as np
from ase.data import atomic_names, chemical_symbols
[docs]
class NoMatchingPseudopotential(FileNotFoundError):
def __init__(self, message):
self.message = message
[docs]
class MultiplePseudoPotentialFiles(Exception):
def __init__(self, message):
self.message = message
[docs]
def infer_pseudo_path(symbol, search_path):
"""Given an element symbol like 'Na', get the file name
of the search_path (resolved) that search through the search path
TODO: shall we support multiple directories?
TODO: add a `setup` option like VASP?
"""
search_path = Path(search_path).resolve()
potfiles = (
list(search_path.glob("*.psp8"))
+ list(search_path.glob("*.psp"))
+ list(search_path.glob("*.pot"))
)
candidates = []
for pf in potfiles:
try:
psp8_data = parse_psp8_header(open(pf, "r").read())
except Exception as e:
print(e)
psp8_data = None
if psp8_data:
if psp8_data["symbol"] == symbol:
candidates.append(pf)
if len(candidates) == 0:
raise NoMatchingPseudopotential(
(
f"No pseudopotential file for {symbol} found "
"under the search path {search_path}!"
)
)
elif len(candidates) > 1:
msg = (
f"There are multiple psp8 files for {symbol}:\n"
f"{candidates}. Please select the desired pseudopotential file!"
)
raise MultiplePseudoPotentialFiles(msg)
else:
return candidates[0]
[docs]
def copy_psp_file(source_pot, target_dir, use_symbol=False):
"""Copy the pseudo potential file `source_pot` under `target_dir`
if use_symbol is True, rename the potential to '{symbol}.psp8'
the function returns the name of the pseudo potential file
"""
source_pot = Path(source_pot)
target_dir = Path(target_dir)
psp8_data = parse_psp8_header(open(source_pot, "r").read())
symbol = psp8_data["symbol"]
if use_symbol:
potname = f"{symbol}.psp8"
else:
potname = source_pot.name
target_pot = target_dir / potname
# shutil will copy
shutil.copy(source_pot, target_pot)
return potname
[docs]
def find_pseudo_path(symbol, search_path=None, pseudopotential_mapping={}):
"""Get the pseudo potential file at best effort
Searching priorities
1) if pseudopotential_mapping has symbol as key, use the file name
There are two possibilities
i) filename does not contain directory information: i.e. Na-pbe.pot
use search_path / filename for the mapping
ii) filename contains directory information, directly use the file name
2) No pseudopotential_mapping is given, get the psp from search_path
"""
mapping_psp = pseudopotential_mapping.get(symbol, None)
if mapping_psp is None:
if search_path is None:
raise NoMatchingPseudopotential(
(
f"No psudopotentials found for {symbol} "
"because neither search_path nor psp name is provided."
)
)
return infer_pseudo_path(symbol, search_path)
else:
str_psp = str(mapping_psp)
mapping_psp = Path(mapping_psp)
# if psp contains any path information (/, \\), treat is as a direct file
is_node_file_name = (mapping_psp.name == str_psp) and (os.sep not in str_psp)
if is_node_file_name:
if search_path is None:
raise NoMatchingPseudopotential(
(
f"You provide the pseudopotential name {mapping_psp} but no search path is defined. I cannot locate the pseudopotential file."
)
)
mapping_psp = Path(search_path) / str_psp
if not mapping_psp.is_file():
warn(
(
f"Pseudopotential file {mapping_psp} is defined by user input but cannot be found!\n"
"Please check your setup. I'll write the .ion file anyway."
)
)
return mapping_psp