diff --git a/pyiron_contrib/__init__.py b/pyiron_contrib/__init__.py index 7551db454..0864b1fa0 100644 --- a/pyiron_contrib/__init__.py +++ b/pyiron_contrib/__init__.py @@ -4,65 +4,70 @@ import warnings try: - from pyiron import Project + from pyiron import Project except: - warnings.warn("pyiron module not found, importing Project from pyiron_base") - from pyiron_base import Project + warnings.warn("pyiron module not found, importing Project from pyiron_base") + from pyiron_base import Project from pyiron_base import JOB_CLASS_DICT from pyiron_contrib.generic.storage_interface_toolkit import StorageInterfaceFactory -Project.register_tools('storage_interface', StorageInterfaceFactory) +Project.register_tools("storage_interface", StorageInterfaceFactory) # Make classes available for new pyiron version -JOB_CLASS_DICT['ProtoMinimGradDes'] = 'pyiron_contrib.protocol.compound.minimize' -JOB_CLASS_DICT['ProtoMD'] = 'pyiron_contrib.protocol.compound.molecular_dynamics' -JOB_CLASS_DICT['ProtoConfinedMD'] = 'pyiron_contrib.protocol.compound.molecular_dynamics' -JOB_CLASS_DICT['ProtoHarmMD'] = 'pyiron_contrib.protocol.compound.molecular_dynamics' -JOB_CLASS_DICT['ProtoNEBSer'] = 'pyiron_contrib.protocol.compound.nudged_elastic_band' -JOB_CLASS_DICT['ProtocolQMMM'] = 'pyiron_contrib.protocol.compound.qmmm' -JOB_CLASS_DICT['ProtoHarmTILDSer'] = 'pyiron_contrib.protocol.compound.thermodynamic_integration' -JOB_CLASS_DICT['ProtoHarmTILDPar'] = 'pyiron_contrib.protocol.compound.thermodynamic_integration' -JOB_CLASS_DICT['ProtoVacTILDSer'] = 'pyiron_contrib.protocol.compound.thermodynamic_integration' -JOB_CLASS_DICT['ProtoVacTILDPar'] = 'pyiron_contrib.protocol.compound.thermodynamic_integration' -JOB_CLASS_DICT['ProtoVacForm'] = 'pyiron_contrib.protocol.compound.thermodynamic_integration' -JOB_CLASS_DICT['ProtoFTSEvoSer'] = 'pyiron_contrib.protocol.compound.finite_temperature_string' -JOB_CLASS_DICT['ProtoFTSEvoPar'] = 'pyiron_contrib.protocol.compound.finite_temperature_string' -JOB_CLASS_DICT['ImageJob'] = 'pyiron_contrib.image.job' -JOB_CLASS_DICT['LangevinAse'] = 'pyiron_contrib.atomistics.interactive.langevin' -JOB_CLASS_DICT['Mixer'] = 'pyiron_contrib.atomistics.interactive.mixer' -JOB_CLASS_DICT['ParameterMaster'] = 'pyiron_contrib.atomistics.dft.parametermaster' -JOB_CLASS_DICT['MonteCarloMaster'] = 'pyiron_contrib.atomistics.interactive.montecarlo' -JOB_CLASS_DICT['RandSpg'] = 'pyiron_contrib.atomistics.randspg.randspg' -JOB_CLASS_DICT['Fenics'] = 'pyiron_contrib.continuum.fenics.job.generic' -JOB_CLASS_DICT['FenicsLinearElastic'] = 'pyiron_contrib.continuum.fenics.job.elastic' -JOB_CLASS_DICT['TrainingContainer'] = 'pyiron_contrib.atomistics.atomistics.job.trainingcontainer' -JOB_CLASS_DICT['RandomDisMaster'] = 'pyiron_contrib.atomistics.mlip.masters' -JOB_CLASS_DICT['RandomMDMaster'] = 'pyiron_contrib.atomistics.mlip.masters' -JOB_CLASS_DICT['RunnerFit'] = 'pyiron_contrib.atomistics.runner.job' -JOB_CLASS_DICT['MlipSelect'] = 'pyiron_contrib.atomistics.mlip.mlipselect' -JOB_CLASS_DICT['Mlip'] = 'pyiron_contrib.atomistics.mlip.mlip' -JOB_CLASS_DICT['LammpsMlip'] = 'pyiron_contrib.atomistics.mlip.lammps' -JOB_CLASS_DICT['MlipJob'] = 'pyiron_contrib.atomistics.mlip.mlipjob' -JOB_CLASS_DICT['Atomicrex'] = 'pyiron_contrib.atomistics.atomicrex.atomicrex_job' -JOB_CLASS_DICT['StructureMasterInt'] = 'pyiron_contrib.atomistics.atomistics.job.structurelistmasterinteractive' -JOB_CLASS_DICT['StorageJob'] = 'pyiron_contrib.RDM.storagejob' -JOB_CLASS_DICT['MlipDescriptors'] = 'pyiron_contrib.atomistics.mlip.mlipdescriptors' -JOB_CLASS_DICT['PacemakerJob'] = 'pyiron_contrib.atomistics.pacemaker.job' -JOB_CLASS_DICT['MeamFit'] = 'pyiron_contrib.atomistics.meamfit.meamfit' -JOB_CLASS_DICT['Cp2kJob'] = 'pyiron_contrib.atomistics.cp2k.job' -JOB_CLASS_DICT['PiMD'] = 'pyiron_contrib.atomistics.ipi.ipi_jobs' -JOB_CLASS_DICT['GleMD'] = 'pyiron_contrib.atomistics.ipi.ipi_jobs' -JOB_CLASS_DICT['PigletMD'] = 'pyiron_contrib.atomistics.ipi.ipi_jobs' -JOB_CLASS_DICT['LammpsInteractiveWithoutOutput'] = 'pyiron_contrib.nofiles.lammps' -JOB_CLASS_DICT['SQSJobWithoutOutput'] = 'pyiron_contrib.nofiles.sqs' -JOB_CLASS_DICT['ElasticMatrixJobWithoutFiles'] = 'pyiron_contrib.nofiles.elastic' -JOB_CLASS_DICT['MurnaghanWithoutFiles'] = 'pyiron_contrib.nofiles.murn' -JOB_CLASS_DICT['PhonopyJobWithoutFiles'] = 'pyiron_contrib.nofiles.phonopy' -JOB_CLASS_DICT['SQSMasterMPI'] = 'pyiron_contrib.nofiles.master' -JOB_CLASS_DICT['LAMMPSMinimizeMPI'] = 'pyiron_contrib.nofiles.master' -JOB_CLASS_DICT['LAMMPSElasticMPI'] = 'pyiron_contrib.nofiles.master' -JOB_CLASS_DICT['LAMMPSMinimizeElasticMPI'] = 'pyiron_contrib.nofiles.master' +JOB_CLASS_DICT.update( + { + "ProtoMinimGradDes": "pyiron_contrib.protocol.compound.minimize", + "ProtoMD": "pyiron_contrib.protocol.compound.molecular_dynamics", + "ProtoConfinedMD": "pyiron_contrib.protocol.compound.molecular_dynamics", + "ProtoHarmMD": "pyiron_contrib.protocol.compound.molecular_dynamics", + "ProtoNEBSer": "pyiron_contrib.protocol.compound.nudged_elastic_band", + "ProtocolQMMM": "pyiron_contrib.protocol.compound.qmmm", + "ProtoHarmTILDSer": "pyiron_contrib.protocol.compound.thermodynamic_integration", + "ProtoHarmTILDPar": "pyiron_contrib.protocol.compound.thermodynamic_integration", + "ProtoVacTILDSer": "pyiron_contrib.protocol.compound.thermodynamic_integration", + "ProtoVacTILDPar": "pyiron_contrib.protocol.compound.thermodynamic_integration", + "ProtoVacForm": "pyiron_contrib.protocol.compound.thermodynamic_integration", + "ProtoFTSEvoSer": "pyiron_contrib.protocol.compound.finite_temperature_string", + "ProtoFTSEvoPar": "pyiron_contrib.protocol.compound.finite_temperature_string", + "ImageJob": "pyiron_contrib.image.job", + "LangevinAse": "pyiron_contrib.atomistics.interactive.langevin", + "Mixer": "pyiron_contrib.atomistics.interactive.mixer", + "ParameterMaster": "pyiron_contrib.atomistics.dft.parametermaster", + "MonteCarloMaster": "pyiron_contrib.atomistics.interactive.montecarlo", + "RandSpg": "pyiron_contrib.atomistics.randspg.randspg", + "Fenics": "pyiron_contrib.continuum.fenics.job.generic", + "FenicsLinearElastic": "pyiron_contrib.continuum.fenics.job.elastic", + "TrainingContainer": "pyiron_contrib.atomistics.atomistics.job.trainingcontainer", + "RandomDisMaster": "pyiron_contrib.atomistics.mlip.masters", + "RandomMDMaster": "pyiron_contrib.atomistics.mlip.masters", + "RunnerFit": "pyiron_contrib.atomistics.runner.job", + "MlipSelect": "pyiron_contrib.atomistics.mlip.mlipselect", + "Mlip": "pyiron_contrib.atomistics.mlip.mlip", + "LammpsMlip": "pyiron_contrib.atomistics.mlip.lammps", + "MlipJob": "pyiron_contrib.atomistics.mlip.mlipjob", + "Atomicrex": "pyiron_contrib.atomistics.atomicrex.atomicrex_job", + "StructureMasterInt": "pyiron_contrib.atomistics.atomistics.job.structurelistmasterinteractive", + "StorageJob": "pyiron_contrib.RDM.storagejob", + "MlipDescriptors": "pyiron_contrib.atomistics.mlip.mlipdescriptors", + "PacemakerJob": "pyiron_contrib.atomistics.pacemaker.job", + "MeamFit": "pyiron_contrib.atomistics.meamfit.meamfit", + "Cp2kJob": "pyiron_contrib.atomistics.cp2k.job", + "PiMD": "pyiron_contrib.atomistics.ipi.ipi_jobs", + "GleMD": "pyiron_contrib.atomistics.ipi.ipi_jobs", + "PigletMD": "pyiron_contrib.atomistics.ipi.ipi_jobs", + "LammpsInteractiveWithoutOutput": "pyiron_contrib.nofiles.lammps", + "SQSJobWithoutOutput": "pyiron_contrib.nofiles.sqs", + "ElasticMatrixJobWithoutFiles": "pyiron_contrib.nofiles.elastic", + "MurnaghanWithoutFiles": "pyiron_contrib.nofiles.murn", + "PhonopyJobWithoutFiles": "pyiron_contrib.nofiles.phonopy", + "SQSMasterMPI": "pyiron_contrib.nofiles.master", + "LAMMPSMinimizeMPI": "pyiron_contrib.nofiles.master", + "LAMMPSElasticMPI": "pyiron_contrib.nofiles.master", + "LAMMPSMinimizeElasticMPI": "pyiron_contrib.nofiles.master", + "FitsnapJob": "pyiron_contrib.atomistics.fitsnap.job", + } +) from ._version import get_versions diff --git a/pyiron_contrib/atomistics/fitsnap/common.py b/pyiron_contrib/atomistics/fitsnap/common.py new file mode 100644 index 000000000..587afb8db --- /dev/null +++ b/pyiron_contrib/atomistics/fitsnap/common.py @@ -0,0 +1,89 @@ +import numpy as np +from pybispectrum import calc_bispectrum_names +from fitsnap3lib.scrapers.ase_funcs import get_apre, create_shared_arrays + + +def ase_scraper( + s, frames, energies, forces, stresses=[[0, 0, 0], [0, 0, 0], [0, 0, 0]] +): + """ + Custom function to allocate shared arrays used in Calculator and build the internal list of + dictionaries `data` of configuration info. Customized version of `fitsnap3lib.scrapers.ase_funcs`. + + Args: + s: fitsnap instance. + frames: list or array of ASE atoms objects. + energies: array of energies. + forces: array of forces for all configurations. + stresses: array of stresses for all configurations. + + Creates a list of data dictionaries `s.data` suitable for fitsnap descriptor calculation. + If running in parallel, this list will be distributed over procs, so that each proc will have a + portion of the list. + """ + + create_shared_arrays(s, frames) + s.data = [ + collate_data(a, e, f, s) + for (a, e, f, s) in zip(frames, energies, forces, stresses) + ] + + +def collate_data(atoms, energy, forces, stresses): + """ + Function to organize fitting data for FitSNAP from ASE atoms objects. + + Args: + atoms: ASE atoms object for a single configuration of atoms. + energy: energy of a configuration. + forces: numpy array of forces for a configuration. + stresses: numpy array of stresses for a configuration. + + Returns a fitsnap data dictionary for a single configuration. + """ + + # make a data dictionary for this config + + apre = get_apre(cell=atoms.cell) + R = np.dot(np.linalg.inv(atoms.cell), apre) + + positions = np.matmul(atoms.get_positions(), R) + cell = apre.T + + data = {} + data["PositionsStyle"] = "angstrom" + data["AtomTypeStyle"] = "chemicalsymbol" + data["StressStyle"] = "bar" + data["LatticeStyle"] = "angstrom" + data["EnergyStyle"] = "electronvolt" + data["ForcesStyle"] = "electronvoltperangstrom" + data["Group"] = "Displaced_BCC" + data["File"] = None + data["Stress"] = stresses + data["Positions"] = positions + data["Energy"] = energy + data["AtomTypes"] = atoms.get_chemical_symbols() + data["NumAtoms"] = len(atoms) + data["Forces"] = forces + data["QMLattice"] = cell + data["test_bool"] = 0 + data["Lattice"] = cell + data["Rotation"] = np.array([[1, 0, 0], [0, 1, 0], [0, 0, 1]]) + data["Translation"] = np.zeros((len(atoms), 3)) + data["eweight"] = 1.0 + data["fweight"] = 1.0 / 150.0 + data["vweight"] = 0.0 + + return data + + +def subsample_twojmax(total_bispect, twojmax_lst): + bi_spect_names_str_lst = [str(lst) for lst in total_bispect] + twojmax_master_str_lst = [ + [str(lst) for lst in calc_bispectrum_names(twojmax=tjm)] for tjm in twojmax_lst + ] + ind_lst = [ + [desc in desc_lst for desc in bi_spect_names_str_lst] + for desc_lst in twojmax_master_str_lst + ] + return ind_lst diff --git a/pyiron_contrib/atomistics/fitsnap/job.py b/pyiron_contrib/atomistics/fitsnap/job.py new file mode 100644 index 000000000..c1b69604f --- /dev/null +++ b/pyiron_contrib/atomistics/fitsnap/job.py @@ -0,0 +1,101 @@ +from mpi4py import MPI +from fitsnap3lib.fitsnap import FitSnap +from pyiron_base import PythonTemplateJob, DataContainer +from pyiron_lanl.fitsnap.common import ase_scraper + + +default_input = settings = { + "BISPECTRUM": { + "numTypes": 1, + "twojmax": 8, + "rcutfac": 4.812302818, + "rfac0": 0.99363, + "rmin0": 0.0, + "wj": 1.0, + "radelem": 0.5, + "type": "Be", + "wselfallflag": 0, + "chemflag": 0, + "bzeroflag": 0, + "quadraticflag": 0, + }, + "CALCULATOR": { + "calculator": "LAMMPSSNAP", + "energy": 1, # Calculate energy descriptors + "force": 1, # Calculate force descriptors + "stress": 0, # Calculate virial descriptors + }, + "REFERENCE": { + "units": "metal", + "atom_style": "atomic", + "pair_style": "hybrid/overlay zero 10.0 zbl 4.0 4.8", + "pair_coeff1": "* * zero", + "pair_coeff2": "1 1 zbl 74 74", + }, + "SOLVER": {"solver": "SVD", "compute_testerrs": 1, "detailed_errors": 1}, + "EXTRAS": { + "dump_descriptors": 0, + "dump_truth": 0, + "dump_weights": 0, + "dump_dataframe": 0, + }, + "MEMORY": {"override": 0}, +} + + +class FitsnapJob(PythonTemplateJob): + def __init__(self, project, job_name): + super(FitsnapJob, self).__init__(project, job_name) + self.__version__ = "0.1" + self.__name__ = "FitsnapJob" + self.input.update(default_input) + self._lst_of_struct = [] + self._lst_of_energies = [] + self._lst_of_forces = [] + self._coefficients = [] + + @property + def list_of_structures(self): + return self._lst_of_struct + + @list_of_structures.setter + def list_of_structures(self, structure_lst): + self._lst_of_struct = structure_lst + + @property + def list_of_energies(self): + return self._lst_of_energies + + @list_of_energies.setter + def list_of_energies(self, energies): + self._lst_of_energies = energies + + @property + def coefficients(self): + return self._coefficients + + @property + def list_of_forces(self): + return self._lst_of_forces + + @list_of_forces.setter + def list_of_forces(self, forces): + self._lst_of_forces = forces + + def run_static(self): + comm = MPI.COMM_WORLD + input_dict = self.input.to_builtin() + snap = FitSnap(input_dict, comm=comm, arglist=["--overwrite"]) + ase_scraper( + snap, self._lst_of_struct, self._lst_of_energies, self._lst_of_forces + ) + snap.process_configs() + snap.solver.perform_fit() + self._coefficients = snap.solver.fit + self.status.finished = True + + def to_hdf(self, hdf=None, group_name=None): + super(FitsnapJob, self).to_hdf(hdf=hdf, group_name=group_name) + + def from_hdf(self, hdf=None, group_name=None): + super(FitsnapJob, self).from_hdf(hdf=hdf, group_name=group_name)