Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Use ResourceResolvers in lammps/vasp/sphinx #1527

Merged
merged 11 commits into from
Aug 16, 2024
4 changes: 2 additions & 2 deletions .ci_support/environment-old.yml
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,8 @@ dependencies:
- pandas =2.0.3
- phonopy =2.20.0
- pint =0.18
- pyiron_base =0.9.4
- pyiron_snippets =0.1.1
- pyiron_base =0.9.12
- pyiron_snippets =0.1.3
- pylammpsmpi =0.2.18
- pyscal3 =3.2.5
- monty =2024.3.31
Expand Down
147 changes: 62 additions & 85 deletions pyiron_atomistics/atomistics/job/potentials.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,11 @@
"""

import os
from abc import ABC, abstractmethod

import pandas
from pyiron_base import state
from pyiron_snippets.resources import ResourceNotFound, ResourceResolver

__author__ = "Martin Boeckmann, Jan Janssen"
__copyright__ = (
Expand All @@ -24,7 +26,7 @@
__date__ = "Sep 1, 2017"


class PotentialAbstract(object):
class PotentialAbstract(ABC):
"""
The PotentialAbstract class loads a list of available potentials and sorts them. Afterwards the potentials can be
accessed through:
Expand All @@ -36,6 +38,14 @@ class PotentialAbstract(object):
selected_atoms:
"""

@property
@abstractmethod
def resource_plugin_name(self) -> str:
"""Return the name of the folder of this plugin/code in the pyiron resources.

One of lammps/vasp/sphinx, to be overriden in the specific sub classes."""
pass

def __init__(self, potential_df, default_df=None, selected_atoms=None):
self._potential_df = potential_df
self._default_df = default_df
Expand Down Expand Up @@ -102,108 +112,75 @@ def __getitem__(self, item):
def __str__(self):
return str(self.list())

@staticmethod
def _get_potential_df(plugin_name, file_name_lst):
@classmethod
def _get_resolver(cls):
"""Return a ResourceResolver that can be searched for potential files or potential dataframes.

This exists primarily so that the lammps and sphinx sub classes can overload it to add their conda package
specific resource paths.

Returns:
:class:`.ResourceResolver`
"""
return ResourceResolver(
state.settings.resource_paths,
cls.resource_plugin_name,
"potentials",
)

@classmethod
def _get_potential_df(cls, file_name_lst):
"""

Args:
plugin_name (str):
file_name_lst (set):

Returns:
pandas.DataFrame:
"""
env = os.environ
resource_path_lst = state.settings.resource_paths
for conda_var in ["CONDA_PREFIX", "CONDA_DIR"]:
if conda_var in env.keys(): # support iprpy-data package
path_to_add = os.path.join(env[conda_var], "share", "iprpy")
if path_to_add not in resource_path_lst:
resource_path_lst += [path_to_add]
df_lst = []
for resource_path in resource_path_lst:
if os.path.exists(os.path.join(resource_path, plugin_name, "potentials")):
resource_path = os.path.join(resource_path, plugin_name, "potentials")
if "potentials" in resource_path or "iprpy" in resource_path:
for path, folder_lst, file_lst in os.walk(resource_path):
for periodic_table_file_name in file_name_lst:
if (
periodic_table_file_name in file_lst
and periodic_table_file_name.endswith(".csv")
):
df_lst.append(
pandas.read_csv(
os.path.join(path, periodic_table_file_name),
index_col=0,
converters={
"Species": lambda x: x.replace("'", "")
.strip("[]")
.split(", "),
"Config": lambda x: x.replace("'", "")
.replace("\\n", "\n")
.strip("[]")
.split(", "),
"Filename": lambda x: x.replace("'", "")
.strip("[]")
.split(", "),
},
)
)
if len(df_lst) > 0:
return pandas.concat(df_lst)
else:
raise ValueError("Was not able to locate the potential files.")

@staticmethod
def read_csv(path):
return pandas.read_csv(
path,
index_col=0,
converters={
"Species": lambda x: x.replace("'", "").strip("[]").split(", "),
"Config": lambda x: x.replace("'", "")
.replace("\\n", "\n")
.strip("[]")
.split(", "),
"Filename": lambda x: x.replace("'", "").strip("[]").split(", "),
},
)

files = cls._get_resolver().search(file_name_lst)
return pandas.concat(map(read_csv, files), ignore_index=True)

@classmethod
def _get_potential_default_df(
plugin_name,
cls,
file_name_lst={"potentials_vasp_pbe_default.csv"},
):
"""

Args:
plugin_name (str):
file_name_lst (set):

Returns:
pandas.DataFrame:
"""
for resource_path in state.settings.resource_paths:
pot_path = os.path.join(resource_path, plugin_name, "potentials")
if os.path.exists(pot_path):
resource_path = pot_path
if "potentials" in resource_path:
for path, folder_lst, file_lst in os.walk(resource_path):
for periodic_table_file_name in file_name_lst:
if (
periodic_table_file_name in file_lst
and periodic_table_file_name.endswith(".csv")
):
return pandas.read_csv(
os.path.join(path, periodic_table_file_name),
index_col=0,
)
elif (
periodic_table_file_name in file_lst
and periodic_table_file_name.endswith(".h5")
):
return pandas.read_hdf(
os.path.join(path, periodic_table_file_name), mode="r"
)
raise ValueError("Was not able to locate the potential files.")


def find_potential_file_base(path, resource_path_lst, rel_path):
if path is not None:
for resource_path in resource_path_lst:
path_direct = os.path.join(resource_path, path)
path_indirect = os.path.join(resource_path, rel_path, path)
if os.path.exists(path_direct):
return path_direct
elif os.path.exists(path_indirect):
return path_indirect
raise ValueError(
"Either the filename or the functional has to be defined.",
path,
resource_path_lst,
)
try:
return pandas.read_csv(
cls._get_resolver().first(file_name_lst), index_col=0
)
except ResourceNotFound:
raise ValueError("Was not able to locate the potential files.") from None

@classmethod
def find_potential_file(cls, path):
res = cls._get_resolver()
try:
return res.first(path)
except ResourceNotFound:
raise ValueError(f"Could not find file '{path}' in {res}!") from None
45 changes: 22 additions & 23 deletions pyiron_atomistics/lammps/potential.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,11 +8,9 @@

import pandas as pd
from pyiron_base import GenericParameters, state
from pyiron_snippets.resources import ResourceResolver

from pyiron_atomistics.atomistics.job.potentials import (
PotentialAbstract,
find_potential_file_base,
)
from pyiron_atomistics.atomistics.job.potentials import PotentialAbstract
from pyiron_atomistics.atomistics.structure.atoms import Atoms

__author__ = "Joerg Neugebauer, Sudarsan Surendralal, Jan Janssen"
Expand Down Expand Up @@ -66,6 +64,7 @@ def remove_structure_block(self):

@property
def files(self):
env = os.environ
if len(self._df["Filename"].values[0]) > 0 and self._df["Filename"].values[
0
] != [""]:
Expand All @@ -77,27 +76,11 @@ def files(self):
for files in list(self._df["Filename"])[0]
if not os.path.isabs(files)
]
env = os.environ
resource_path_lst = state.settings.resource_paths
for conda_var in ["CONDA_PREFIX", "CONDA_DIR"]:
if conda_var in env.keys(): # support iprpy-data package
path_to_add = state.settings.convert_path_to_abs_posix(
os.path.join(env[conda_var], "share", "iprpy")
)
if path_to_add not in resource_path_lst:
resource_path_lst.append(path_to_add)
for path in relative_file_paths:
absolute_file_paths.append(
find_potential_file_base(
path=path,
resource_path_lst=resource_path_lst,
rel_path=os.path.join("lammps", "potentials"),
)
LammpsPotentialFile.find_potential_file(path)
)
if len(absolute_file_paths) != len(list(self._df["Filename"])[0]):
raise ValueError("Was not able to locate the potentials.")
else:
return absolute_file_paths
return absolute_file_paths

def copy_pot_files(self, working_directory):
if self.files is not None:
Expand Down Expand Up @@ -251,10 +234,26 @@ class LammpsPotentialFile(PotentialAbstract):
selected_atoms:
"""

resource_plugin_name = "lammps"

@classmethod
def _get_resolver(cls):
env = os.environ
return (
super()
._get_resolver()
.chain(
ResourceResolver(
[env[var] for var in ("CONDA_PREFIX", "CONDA_DIR") if var in env],
"share",
"iprpy",
)
)
)

def __init__(self, potential_df=None, default_df=None, selected_atoms=None):
if potential_df is None:
potential_df = self._get_potential_df(
plugin_name="lammps",
file_name_lst={"potentials_lammps.csv"},
)
super(LammpsPotentialFile, self).__init__(
Expand Down
14 changes: 3 additions & 11 deletions pyiron_atomistics/sphinx/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -38,9 +38,6 @@
collect_spins_dat,
)
from pyiron_atomistics.sphinx.potential import SphinxJTHPotentialFile
from pyiron_atomistics.sphinx.potential import (
find_potential_file as find_potential_file_jth,
)
from pyiron_atomistics.sphinx.structure import read_atoms
from pyiron_atomistics.sphinx.util import sxversions
from pyiron_atomistics.sphinx.volumetric_data import SphinxVolumetricData
Expand All @@ -49,9 +46,6 @@
VaspPotentialSetter,
strip_xc_from_potential_name,
)
from pyiron_atomistics.vasp.potential import (
find_potential_file as find_potential_file_vasp,
)

__author__ = "Osamu Waseda, Jan Janssen"
__copyright__ = (
Expand Down Expand Up @@ -1293,11 +1287,9 @@ def _get_potential_path(

if potformat == "JTH":
potentials = SphinxJTHPotentialFile(xc=xc)
find_potential_file = find_potential_file_jth
pot_path_dict.setdefault("PBE", "jth-gga-pbe")
elif potformat == "VASP":
potentials = VaspPotentialFile(xc=xc)
find_potential_file = find_potential_file_vasp
pot_path_dict.setdefault("PBE", "paw-gga-pbe")
pot_path_dict.setdefault("LDA", "paw-lda")
else:
Expand All @@ -1313,7 +1305,7 @@ def _get_potential_path(
if "pseudo_potcar_file" in species_obj.tags.keys():
new_element = species_obj.tags["pseudo_potcar_file"]
potentials.add_new_element(parent_element=elem, new_element=new_element)
potential_path = find_potential_file(
potential_path = potentials.find_potential_file(
path=potentials.find_default(new_element)["Filename"].values[0][0]
)
assert os.path.isfile(
Expand All @@ -1327,14 +1319,14 @@ def _get_potential_path(
potentials.add_new_element(
parent_element=elem, new_element=new_element
)
potential_path = find_potential_file(
potential_path = potentials.find_potential_file(
path=potentials.find_default(new_element)["Filename"].values[0][
0
]
)
else:
ori_paths.append(
find_potential_file(
potentials.find_potential_file(
path=potentials.find_default(elem)["Filename"].values[0][0]
)
)
Expand Down
Loading
Loading