Skip to content

Commit

Permalink
Merge pull request #89 from pyiron/merge
Browse files Browse the repository at this point in the history
Merge
  • Loading branch information
jan-janssen committed Feb 28, 2021
2 parents dbc138a + 94ea11e commit 105bed5
Show file tree
Hide file tree
Showing 11 changed files with 1,049 additions and 14 deletions.
6 changes: 3 additions & 3 deletions .ci_support/environment.yml
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,9 @@ dependencies:
- coverage
- codacy-coverage
- matplotlib =3.3.4
- numpy =1.20
- pyiron_base =0.1.46
- pyiron_atomistics =0.2.3
- numpy =1.20.1
- pyiron_base =0.1.48
- pyiron_atomistics =0.2.4
- scipy =1.6.0
- seaborn =0.11.1
- scikit-image =0.18.1
6 changes: 6 additions & 0 deletions pyiron_contrib/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,12 @@
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.atomistic.atomistics.job.trainingcontainer'
JOB_CLASS_DICT['RandomDisMaster'] = 'pyiron_contrib.atomistic.mlip.masters'
JOB_CLASS_DICT['RandomMDMaster'] = 'pyiron_contrib.atomistic.mlip.masters'
JOB_CLASS_DICT['MlipSelect'] = 'pyiron_contrib.atomistic.mlip.mlipselect'
JOB_CLASS_DICT['Mlip'] = 'pyiron_contrib.atomistic.mlip.mlip'
JOB_CLASS_DICT['LammpsMlip'] = 'pyiron_contrib.atomistic.mlip.lammps'
JOB_CLASS_DICT['MlipJob'] = 'pyiron_contrib.atomistic.mlip.mlipjob'


from ._version import get_versions
Expand Down
3 changes: 3 additions & 0 deletions pyiron_contrib/atomistic/atomistics/job/trainingcontainer.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
>>> container.include_dataset(df)
You can retrieve the full database with :method:`~.TrainingContainer.to_pandas()` like this
>>> container.to_pandas()
name atoms energy forces number_of_atoms
Fe_bcc ...
Expand All @@ -40,6 +41,7 @@ class TrainingContainer(GenericJob):

def __init__(self, project, job_name):
super().__init__(project=project, job_name=job_name)
self.__name__ = "TrainingContainer"
self._table = pd.DataFrame({
"name": [],
"atoms": [],
Expand Down Expand Up @@ -85,6 +87,7 @@ def to_pandas(self):
Export list of structure to pandas table for external fitting codes.
The table contains the following columns:
- 'name': human-readable name of the structure
- 'ase_atoms': the structure as a :class:`.Atoms` object
- 'energy': the energy of the full structure
- 'forces': the per atom forces as a :class:`numpy.ndarray`, shape Nx3
Expand Down
Empty file.
148 changes: 148 additions & 0 deletions pyiron_contrib/atomistic/mlip/cfgs.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,148 @@
# coding: utf-8
# http://gitlab.skoltech.ru/shapeev/mlip-dev/blob/master/src/external/python/mlippy/cfgs.py

from __future__ import print_function
import numpy as np


class Cfg:
pos = None
lat = None
types = None
energy = None
forces = None
stresses = None
desc = None
grade = None


def readcfg(f):
cfg = Cfg()
cfg.lat = np.zeros((3, 3))
size = -1
mode = -1
line = f.readline()
while line:
line = line.upper()
line = line.strip()
if mode == 0:
if line.startswith('SIZE'):
line = f.readline()
size = int(line.strip())
cfg.types = np.zeros(size)
cfg.pos = np.zeros((size, 3))
elif line.startswith('SUPERCELL'):
line = f.readline()
vals = line.strip().split()
cfg.lat[0, :] = vals[0:3]
line = f.readline()
vals = line.strip().split()
cfg.lat[1, :] = vals[0:3]
line = f.readline()
vals = line.strip().split()
cfg.lat[2, :] = vals[0:3]
elif line.startswith('ATOMDATA'):
if line.endswith('FZ'):
cfg.forces = np.zeros((size, 3))
for i in range(size):
line = f.readline()
vals = line.strip().split()
cfg.types[i] = vals[1]
cfg.pos[i, :] = vals[2:5]
if cfg.forces is not None:
cfg.forces[i, :] = vals[5:8]
elif line.startswith('ENERGY'):
line = f.readline()
cfg.energy = float(line.strip())
elif line.startswith('PLUSSTRESS'):
line = f.readline()
vals = line.strip().split()
cfg.stresses = np.zeros(6)
cfg.stresses[:] = vals[0:6]
elif line.startswith('FEATURE MV_GRADE'):
cfg.grade = float(line.split()[-1])
elif line.startswith('FEATURE PYIRON'):
cfg.desc = line.split()[-1]
if line.startswith('BEGIN_CFG'):
mode = 0
elif line.startswith('END_CFG'):
break
line = f.readline()
return cfg


def savecfg(f, cfg, desc=None):
atstr1 = 'AtomData: id type cartes_x cartes_y cartes_z fx fy fz'
atstr2 = 'AtomData: id type cartes_x cartes_y cartes_z'
size = len(cfg.types)
print('BEGIN_CFG', file=f)
print('Size', file=f)
print(' %-d' % size, file=f)
if cfg.lat is not None:
print('SuperCell', file=f)
for i in range(3):
print(' %14f%14f%14f'
% (cfg.lat[i, 0], cfg.lat[i, 1], cfg.lat[i, 2]), file=f)
if cfg.forces is not None:
print(atstr1, file=f)
else:
print(atstr2, file=f)
for i in range(size):
if cfg.forces is not None:
print(' %4d %4d %14f%14f%14f %16.8e %16.8e %16.8e' %
(i+1, cfg.types[i], cfg.pos[i, 0], cfg.pos[i, 1], cfg.pos[i, 2],
cfg.forces[i, 0], cfg.forces[i, 1], cfg.forces[i, 2]), file=f)
else:
print(' %4d %4d %14f%14f%14f' %
(i+1, cfg.types[i], cfg.pos[i, 0], cfg.pos[i, 1], cfg.pos[i, 2]),
file=f)
if cfg.energy is not None:
print('Energy\t%14f' % cfg.energy, file=f)
if cfg.stresses is not None:
print('PlusStress: xx yy zz yz xz xy', file=f)
print(' %14f%14f%14f%14f%14f%14f' %
(cfg.stresses[0], cfg.stresses[1], cfg.stresses[2],
cfg.stresses[3], cfg.stresses[4], cfg.stresses[5]), file=f)
if desc is not None:
print('Feature from %s' % desc, file=f)
if cfg.desc is not None:
print('Feature %s' % cfg.desc, file=f)
print('END_CFG', file=f)


class cfgparser:
def __init__(self, file, max_cfgs=None):
self.cfgs = []
self.file = file
self.max_cfgs = max_cfgs

def __enter__(self):
while True:
if self.max_cfgs is not None and len(self.cfgs) == self.max_cfgs:
break
cfg = readcfg(self.file)
if cfg.types is not None:
self.cfgs.append(cfg)
else:
break
return self.cfgs

def __exit__(self, *args):
self.cfgs = []


def printcfg(cfg):
savecfg(None, cfg)


def loadcfgs(filename, max_cfgs=None):
with open(filename, 'r') as file:
with cfgparser(file, max_cfgs) as cfgs:
return cfgs


def savecfgs(filename, cfgs, desc=None):
with open(filename, 'w') as file:
for cfg in cfgs:
savecfg(file, cfg, desc)
print("", file=file)
126 changes: 126 additions & 0 deletions pyiron_contrib/atomistic/mlip/lammps.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,126 @@
# 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.

import os
from pyiron.lammps.base import Input
from pyiron.lammps.interactive import LammpsInteractive
from pyiron_contrib.atomistic.mlip.mlip import read_cgfs
from pyiron_base import GenericParameters

__author__ = "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__ = "development"
__date__ = "Sep 1, 2018"


class LammpsMlip(LammpsInteractive):
def __init__(self, project, job_name):
super(LammpsMlip, self).__init__(project, job_name)
self.input = MlipInput()
self.__name__ = "LammpsMlip"
self.__version__ = None # Reset the version number to the executable is set automatically
self._executable = None
self._executable_activate()

def set_input_to_read_only(self):
"""
This function enforces read-only mode for the input classes, but it has to be implement in the individual
classes.
"""
super(LammpsMlip, self).set_input_to_read_only()
self.input.mlip.read_only = True

def write_input(self):
super(LammpsMlip, self).write_input()
if self.input.mlip['mlip:load-from'] == 'auto':
self.input.mlip['mlip:load-from'] = os.path.basename(self.potential['Filename'][0][0])
self.input.mlip.write_file(file_name="mlip.ini", cwd=self.working_directory)

def enable_active_learning(self):
self.input.mlip.load_string("""\
abinitio void
mlip mtpr
mlip:load-from Trained.mtp_
calculate-efs TRUE
fit FALSE
select TRUE
select:site-en-weight 0.0
select:energy-weight 1.0
select:force-weight 0.0
select:stress-weight 0.0
select:threshold-init 1e-5
select:threshold 2.0
select:threshold-swap 1.000001
select:threshold-break 5.0
select:save-selected selected.cfg
select:save-state selection.mvs
select:load-state state.mvs
select:efs-ignored FALSE
select:log selection.log
write-cfgs:skip 0
log lotf.log""")

def collect_output(self):
super(LammpsMlip, self).collect_output()
if 'select:save-selected' in self.input.mlip._dataset['Parameter']:
file_name = os.path.join(self.working_directory, self.input.mlip['select:save-selected'])
if os.path.exists(file_name):
cell, positions, forces, stress, energy, indicies, grades, jobids, timesteps = read_cgfs(file_name=file_name)
with self.project_hdf5.open("output/mlip") as hdf5_output:
hdf5_output['forces'] = forces
hdf5_output['energy_tot'] = energy
hdf5_output['pressures'] = stress
hdf5_output['cells'] = cell
hdf5_output['positions'] = positions
hdf5_output['indicies'] = indicies


class MlipInput(Input):
def __init__(self):
self.mlip = MlipParameter()
super(MlipInput, self).__init__()

def to_hdf(self, hdf5):
"""
Args:
hdf5:
Returns:
"""
with hdf5.open("input") as hdf5_input:
self.mlip.to_hdf(hdf5_input)
super(MlipInput, self).to_hdf(hdf5)

def from_hdf(self, hdf5):
"""
Args:
hdf5:
Returns:
"""
with hdf5.open("input") as hdf5_input:
self.mlip.from_hdf(hdf5_input)
super(MlipInput, self).from_hdf(hdf5)


class MlipParameter(GenericParameters):
def __init__(self, separator_char=' ', comment_char='#', table_name="mlip_inp"):
super(MlipParameter, self).__init__(separator_char=separator_char, comment_char=comment_char, table_name=table_name)

def load_default(self, file_content=None):
if file_content is None:
file_content = '''\
abinitio void
mlip mtpr
mlip:load-from auto
calculate-efs TRUE
fit FALSE
select FALSE
'''
self.load_string(file_content)

0 comments on commit 105bed5

Please sign in to comment.