# coding: utf-8
# Copyright (c) Max-Planck-Institut für Eisenforschung GmbH - Computational Materials Design (CM) Department
# Distributed under the terms of "New BSD License", see the LICENSE file.
from __future__ import print_function
import importlib
import inspect
import pkgutil
from six import with_metaclass
from pyiron.base.generic.util import static_isinstance
"""
Jobtype class to create GenericJob type objects
"""
__author__ = "Joerg Neugebauer, Jan Janssen"
__copyright__ = (
"Copyright 2020, Max-Planck-Institut für Eisenforschung GmbH - "
"Computational Materials Design (CM) Department"
)
__version__ = "1.0"
__maintainer__ = "Jan Janssen"
__email__ = "janssen@mpie.de"
__status__ = "production"
__date__ = "Sep 1, 2017"
JOB_CLASS_DICT = {
"Atoms": "pyiron.atomistics.structure.atoms",
"AtomisticExampleJob": "pyiron.testing.randomatomistic",
"ConvEncutParallel": "pyiron.dft.master.convergence_encut_parallel",
"ConvEncutSerial": "pyiron.dft.master.convergence_encut_serial",
"ConvergenceVolume": "pyiron.atomistics.master.convergence_volume",
"ConvKpointParallel": "pyiron.dft.master.convergence_kpoint_parallel",
"ExampleJob": "pyiron.testing.randomatomistic",
"FlexibleMaster": "pyiron.base.master.flexible",
"Gaussian": "pyiron.gaussian.gaussian",
"GpawJob": "pyiron.gpaw.gpaw",
"HessianJob": "pyiron.thermodynamics.hessian",
"Lammps": "pyiron.lammps.lammps",
"MapMaster": "pyiron.atomistics.master.parallel",
"Murnaghan": "pyiron.atomistics.master.murnaghan",
"MurnaghanDFT": "pyiron.dft.master.murnaghan_dft",
"PhonopyJob": "pyiron.atomistics.master.phonopy",
"QuickFF": "pyiron.quickff.quickff",
"ScipyMinimizer": "pyiron.interactive.scipy_minimizer",
"ScriptJob": "pyiron.base.job.script",
"SerialMaster": "pyiron.atomistics.master.serial",
"SerialMasterBase": "pyiron.base.master.serial",
"Sphinx": "pyiron.sphinx.sphinx",
"StructureContainer": "pyiron.atomistics.job.structurecontainer",
"StructureListMaster": "pyiron.atomistics.master.structure",
"SxDynMat": "pyiron.thermodynamics.sxphonons",
"SxExtOptInteractive": "pyiron.interactive.sxextoptint",
"SxHarmPotTst": "pyiron.thermodynamics.sxphonons",
"SxPhonons": "pyiron.thermodynamics.sxphonons",
"SxUniqDispl": "pyiron.thermodynamics.sxphonons",
"TableJob": "pyiron.table.datamining",
"Vasp": "pyiron.vasp.vasp",
"VaspMetadyn": "pyiron.vasp.metadyn",
"VaspSol": "pyiron.vasp.vaspsol",
"Yaff": "pyiron.yaff.yaff",
}
[docs]class Singleton(type):
"""
Implemented with suggestions from
http://stackoverflow.com/questions/6760685/creating-a-singleton-in-python
"""
_instances = {}
def __call__(cls, *args, **kwargs):
if cls not in cls._instances:
cls._instances[cls] = super(Singleton, cls).__call__(*args, **kwargs)
return cls._instances[cls]
[docs]class JobTypeChoice(with_metaclass(Singleton)):
"""
Helper class to choose the job type directly from the project, autocompletion is enabled by overwriting the
__dir__() function.
"""
def __init__(self):
self._job_class_dict = None
self.job_class_dict = JOB_CLASS_DICT
@property
def job_class_dict(self):
return self._job_class_dict
@job_class_dict.setter
def job_class_dict(self, job_class_dict):
self._job_class_dict = job_class_dict
for item in list(self._job_class_dict.keys()):
self.__setattr__(item, item)
def __dir__(self):
"""
Enable autocompletion by overwriting the __dir__() function.
"""
return list(self.job_class_dict.keys())
[docs]class JobType(object):
"""
The JobTypeBase class creates a new object of a given class type.
"""
def __new__(cls, class_name, project, job_name, job_class_dict):
"""
The __new__() method allows to create objects from other classes - the class selected by class_name
Args:
class_name (str): The specific class name of the class this object belongs to.
project (Project): Project object (defines path where job will be created and stored)
job_name (str): name of the job (must be unique within this project path)
job_class_dict (dict): dictionary with the jobtypes to choose from.
Returns:
GenericJob: object of type class_name
"""
cls.job_class_dict = job_class_dict
if isinstance(class_name, str):
job_class = cls.convert_str_to_class(
job_class_dict=cls.job_class_dict, class_name=class_name
)
elif inspect.isclass(class_name):
job_class = class_name
else:
raise TypeError()
job = job_class(project, job_name)
if job.status.aborted:
job.logger.warning(
"Job aborted - please remove it and run again! {}".format(job.job_name)
)
if not job.status.initialized:
job.from_hdf()
if job.status.finished or job.status.collect:
job.set_input_to_read_only()
return job
[docs] @staticmethod
def convert_str_to_class(job_class_dict, class_name):
"""
convert the name of a class to the corresponding class object - only for pyiron internal classes.
Args:
job_class_dict (dict):
class_name (str):
Returns:
(class):
"""
job_type_lst = class_name.split(".")
if len(job_type_lst) > 1:
class_name = class_name.split()[-1][1:-2]
job_type = class_name.split(".")[-1]
else:
job_type = job_type_lst[-1]
for job_class_name in list(
job_class_dict.keys()
): # for job_class in cls.JOB_CLASSES:
if job_type == job_class_name:
job_module = importlib.import_module(job_class_dict[job_class_name])
job_class = getattr(job_module, job_class_name)
return job_class
raise ValueError(
"Unknown job type: ",
class_name,
[job for job in list(job_class_dict.keys())],
)