mirror of https://github.com/abinit/abipy.git
Remove htc module
This commit is contained in:
parent
4e2c07c24a
commit
9399078334
|
@ -28,11 +28,9 @@ omit =
|
|||
*/site-packages/*
|
||||
abipy/data/*
|
||||
abipy/examples/*
|
||||
abipy/htc/*
|
||||
abipy/gui/*
|
||||
abipy/gw/*
|
||||
abipy/extensions/*
|
||||
abipy/gw/*
|
||||
abipy/scripts/*
|
||||
abipy/integration_tests/*
|
||||
./docs/
|
||||
|
|
|
@ -34,7 +34,7 @@ from abipy.core.structure import (Lattice, Structure, StructureModifier, datafra
|
|||
mp_match_structure, mp_search, cod_search)
|
||||
from abipy.core.mixins import CubeFile
|
||||
from abipy.core.kpoints import set_atol_kdiff
|
||||
from abipy.htc.input import AbiInput, LdauParams, LexxParams, input_gen
|
||||
#from abipy.htc.input import LdauParams, LexxParams
|
||||
from abipy.abio.robots import Robot
|
||||
from abipy.abio.inputs import AbinitInput, MultiDataset, AnaddbInput, OpticInput
|
||||
from abipy.abio.abivars import AbinitInputFile
|
||||
|
@ -165,8 +165,8 @@ def dir2abifiles(top, recurse=True):
|
|||
"""
|
||||
Analyze the filesystem starting from directory `top` and
|
||||
return an ordered dictionary mapping the directory name to the list
|
||||
of files supported by `abiopen` contained within that directory.
|
||||
If not `recurse`, children directories are not analyzed.
|
||||
of files supported by ``abiopen`` contained within that directory.
|
||||
If not ``recurse``, children directories are not analyzed.
|
||||
"""
|
||||
dl = collections.defaultdict(list)
|
||||
|
||||
|
@ -187,7 +187,7 @@ def dir2abifiles(top, recurse=True):
|
|||
|
||||
def isabifile(filepath):
|
||||
"""
|
||||
Return True if `filepath` can be opened with `abiopen`.
|
||||
Return True if `filepath` can be opened with ``abiopen``.
|
||||
"""
|
||||
try:
|
||||
abifile_subclass_from_filename(filepath)
|
||||
|
|
|
@ -13,7 +13,7 @@ try:
|
|||
except ImportError:
|
||||
from pymatgen.serializers.json_coders import pmg_serialize
|
||||
|
||||
from abipy.htc.input import LdauParams, LexxParams
|
||||
from abipy.flowtk.abiobjects import LdauParams, LexxParams
|
||||
from .inputs import AbinitInput, MultiDataset
|
||||
|
||||
import logging
|
||||
|
|
|
@ -26,7 +26,7 @@ except ImportError:
|
|||
from abipy.core.structure import Structure
|
||||
from abipy.core.mixins import Has_Structure
|
||||
from abipy.core.kpoints import has_timrev_from_kptopt
|
||||
from abipy.htc.variable import InputVariable
|
||||
from abipy.abio.variable import InputVariable
|
||||
from abipy.abio.abivars import is_abivar, is_anaddb_var
|
||||
from abipy.abio.abivars_db import get_abinit_variables
|
||||
from abipy.abio.input_tags import *
|
||||
|
@ -1755,7 +1755,7 @@ class AbinitInput(six.with_metaclass(abc.ABCMeta, AbstractInput, MSONable, Has_S
|
|||
|
||||
Returns:
|
||||
List of dictionaries with the Abinit variables defining the irreducible perturbation
|
||||
|
||||
|
||||
Example:
|
||||
|
||||
[{'idir': 1, 'ipert': 4, 'qpt': [0.0, 0.0, 0.0]},
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
"""Tests for structure module"""
|
||||
"""Tests for abivars module"""
|
||||
from __future__ import print_function, division, unicode_literals, absolute_import
|
||||
|
||||
import numpy as np
|
||||
|
|
|
@ -0,0 +1,17 @@
|
|||
"""Tests for variable module."""
|
||||
from __future__ import print_function, division, unicode_literals, absolute_import
|
||||
|
||||
import abipy.data as abidata
|
||||
|
||||
from abipy.core.testing import AbipyTest
|
||||
from abipy.abio.variable import InputVariable
|
||||
|
||||
|
||||
class TestInputVariable(AbipyTest):
|
||||
|
||||
def test_inputvariable(self):
|
||||
"""Testing InputVariable."""
|
||||
v = InputVariable(name="ecut", value=5)
|
||||
assert v.name == "ecut"
|
||||
assert not v.units
|
||||
assert str(v) == " ecut 5"
|
|
@ -1,14 +1,14 @@
|
|||
from __future__ import print_function, division #, unicode_literals
|
||||
from __future__ import print_function, division, absolute_import #, unicode_literals
|
||||
|
||||
import string
|
||||
import warnings
|
||||
from .utils import flatten, listify, is_number, is_iter
|
||||
|
||||
import collections
|
||||
import numpy as np
|
||||
|
||||
|
||||
__all__ = ['InputVariable', 'SpecialInputVariable']
|
||||
|
||||
__all__ = [
|
||||
'InputVariable',
|
||||
]
|
||||
|
||||
_SPECIAL_DATASET_INDICES = (':', '+', '?')
|
||||
|
||||
|
@ -60,8 +60,9 @@ def convert_number(value):
|
|||
|
||||
|
||||
class InputVariable(object):
|
||||
"""An Abinit input variable."""
|
||||
|
||||
"""
|
||||
An Abinit input variable.
|
||||
"""
|
||||
def __init__(self, name, value, units='', valperline=3):
|
||||
|
||||
self._name = name
|
||||
|
@ -89,9 +90,9 @@ class InputVariable(object):
|
|||
def name(self):
|
||||
return self._name
|
||||
|
||||
@name.setter
|
||||
def name(self, name):
|
||||
self._name = name
|
||||
#@name.setter
|
||||
#def name(self, name):
|
||||
# self._name = name
|
||||
|
||||
@property
|
||||
def basename(self):
|
||||
|
@ -111,50 +112,49 @@ class InputVariable(object):
|
|||
|
||||
def __str__(self):
|
||||
"""Declaration of the variable in the input file."""
|
||||
|
||||
value = self.value
|
||||
if value is None or not str(value):
|
||||
return ''
|
||||
|
||||
|
||||
var = self.name
|
||||
line = ' ' + var
|
||||
|
||||
|
||||
# By default, do not impose a number of decimal points
|
||||
floatdecimal = 0
|
||||
|
||||
|
||||
# For some inputs, impose number of decimal points...
|
||||
if any(inp in var for inp in ('xred', 'xcart', 'rprim', 'qpt', 'kpt')):
|
||||
#TODO Shouldn't do that
|
||||
floatdecimal = 16
|
||||
|
||||
|
||||
# ...but not for those
|
||||
if any(inp in var for inp in ('ngkpt', 'kptrlatt', 'ngqpt', 'ng2qpt')):
|
||||
#TODO Shouldn't do that
|
||||
floatdecimal = 0
|
||||
|
||||
|
||||
if isinstance(value, np.ndarray):
|
||||
n = 1
|
||||
for i in np.shape(value):
|
||||
n *= i
|
||||
value = np.reshape(value, n)
|
||||
value = list(value)
|
||||
|
||||
|
||||
# values in lists
|
||||
if isinstance(value, (list, tuple)):
|
||||
|
||||
|
||||
# Reshape a list of lists into a single list
|
||||
if all(isinstance(v, (list, tuple)) for v in value):
|
||||
line += self.format_list2d(value, floatdecimal)
|
||||
|
||||
|
||||
else:
|
||||
# Maximum number of values per line.
|
||||
#valperline = 3
|
||||
#if any(inp in var for inp in ['bdgw']):
|
||||
# #TODO Shouldn't do that
|
||||
# valperline = 2
|
||||
|
||||
|
||||
line += self.format_list(value, floatdecimal)
|
||||
|
||||
|
||||
# scalar values
|
||||
else:
|
||||
line += ' ' + str(value)
|
||||
|
@ -162,7 +162,7 @@ class InputVariable(object):
|
|||
# Add units
|
||||
if self.units:
|
||||
line += ' ' + self.units
|
||||
|
||||
|
||||
return line
|
||||
|
||||
def format_scalar(self, val, floatdecimal=0):
|
||||
|
@ -173,33 +173,32 @@ class InputVariable(object):
|
|||
sval = str(val)
|
||||
if sval.lstrip('-').lstrip('+').isdigit() and floatdecimal == 0:
|
||||
return sval
|
||||
|
||||
|
||||
try:
|
||||
fval = float(val)
|
||||
except:
|
||||
return sval
|
||||
|
||||
|
||||
if fval == 0 or (abs(fval) > 1e-3 and abs(fval) < 1e4):
|
||||
form = 'f'
|
||||
addlen = 5
|
||||
else:
|
||||
form = 'e'
|
||||
addlen = 8
|
||||
|
||||
|
||||
ndec = max(len(str(fval-int(fval)))-2, floatdecimal)
|
||||
ndec = min(ndec, 10)
|
||||
|
||||
|
||||
sval = '{v:>{l}.{p}{f}}'.format(v=fval, l=ndec+addlen, p=ndec, f=form)
|
||||
|
||||
|
||||
sval = sval.replace('e', 'd')
|
||||
|
||||
|
||||
return sval
|
||||
|
||||
def format_list2d(self, values, floatdecimal=0):
|
||||
"""Format a list of lists."""
|
||||
|
||||
lvals = flatten(values)
|
||||
|
||||
|
||||
# Determine the representation
|
||||
if all(isinstance(v, int) for v in lvals):
|
||||
type_all = int
|
||||
|
@ -210,7 +209,7 @@ class InputVariable(object):
|
|||
type_all = float
|
||||
except:
|
||||
type_all = str
|
||||
|
||||
|
||||
# Determine the format
|
||||
width = max(len(str(s)) for s in lvals)
|
||||
if type_all == int:
|
||||
|
@ -218,22 +217,22 @@ class InputVariable(object):
|
|||
elif type_all == str:
|
||||
formatspec = '>{0}'.format(width)
|
||||
else:
|
||||
|
||||
|
||||
# Number of decimal
|
||||
maxdec = max(len(str(f-int(f)))-2 for f in lvals)
|
||||
ndec = min(max(maxdec, floatdecimal), 10)
|
||||
|
||||
|
||||
if all(f == 0 or (abs(f) > 1e-3 and abs(f) < 1e4) for f in lvals):
|
||||
formatspec = '>{w}.{p}f'.format(w=ndec+5, p=ndec)
|
||||
else:
|
||||
formatspec = '>{w}.{p}e'.format(w=ndec+8, p=ndec)
|
||||
|
||||
|
||||
line = '\n'
|
||||
for L in values:
|
||||
for val in L:
|
||||
line += ' {v:{f}}'.format(v=val, f=formatspec)
|
||||
line += '\n'
|
||||
|
||||
|
||||
return line.rstrip('\n')
|
||||
|
||||
def format_list(self, values, floatdecimal=0):
|
||||
|
@ -242,17 +241,17 @@ class InputVariable(object):
|
|||
The result might be spread among several lines.
|
||||
"""
|
||||
line = ''
|
||||
|
||||
|
||||
# Format the line declaring the value
|
||||
for i, val in enumerate(values):
|
||||
line += ' ' + self.format_scalar(val, floatdecimal)
|
||||
if self.valperline is not None and (i+1) % self.valperline == 0:
|
||||
line += '\n'
|
||||
|
||||
|
||||
# Add a carriage return in case of several lines
|
||||
if '\n' in line.rstrip('\n'):
|
||||
line = '\n' + line
|
||||
|
||||
|
||||
return line.rstrip('\n')
|
||||
|
||||
@staticmethod
|
||||
|
@ -261,18 +260,17 @@ class InputVariable(object):
|
|||
Interpret a string variable and attempt to return a value of the
|
||||
appropriate type. If all else fails, return the initial string.
|
||||
"""
|
||||
|
||||
value = None
|
||||
|
||||
|
||||
try:
|
||||
for part in sval.split():
|
||||
|
||||
|
||||
if '*' in part:
|
||||
# cases like istwfk *1
|
||||
if part[0] == '*':
|
||||
value = None
|
||||
break
|
||||
|
||||
|
||||
# cases like acell 3*3.52
|
||||
else:
|
||||
n = int(part.split('*')[0])
|
||||
|
@ -281,22 +279,22 @@ class InputVariable(object):
|
|||
value = []
|
||||
value += n * [f]
|
||||
continue
|
||||
|
||||
|
||||
# Fractions
|
||||
if '/' in part:
|
||||
(num, den) = (float(part.split('/')[i]) for i in range(2))
|
||||
part = num / den
|
||||
|
||||
|
||||
# Unit
|
||||
if part in _UNITS.keys():
|
||||
|
||||
|
||||
if value is None:
|
||||
warnings.warn("Could not apply the unit token '%s'." % part)
|
||||
elif isinstance(value, list):
|
||||
value.append(part)
|
||||
else:
|
||||
value = [value, part]
|
||||
|
||||
|
||||
# Convert
|
||||
if False:
|
||||
if isinstance(value, list):
|
||||
|
@ -307,15 +305,15 @@ class InputVariable(object):
|
|||
break
|
||||
else:
|
||||
value *= _UNITS[part]
|
||||
|
||||
|
||||
continue
|
||||
|
||||
|
||||
# Convert
|
||||
try:
|
||||
val = convert_number(part)
|
||||
except:
|
||||
val = part
|
||||
|
||||
|
||||
if value is None:
|
||||
value = val
|
||||
elif isinstance(value, list):
|
||||
|
@ -324,10 +322,10 @@ class InputVariable(object):
|
|||
value = [value, val]
|
||||
except:
|
||||
value = None
|
||||
|
||||
|
||||
if value is None:
|
||||
value = sval
|
||||
|
||||
|
||||
return value
|
||||
|
||||
@classmethod
|
||||
|
@ -383,49 +381,50 @@ class InputVariable(object):
|
|||
return self.sorting_name == other.sorting_name
|
||||
|
||||
|
||||
class SpecialInputVariable(InputVariable):
|
||||
"""
|
||||
An Abinit input variable which can be set by replacing
|
||||
the special dataset indices according to
|
||||
: --> __s
|
||||
+ --> __i
|
||||
? --> __a
|
||||
hence allowing for pythonic names.
|
||||
"""
|
||||
@property
|
||||
def name(self):
|
||||
return self._name
|
||||
def is_number(s):
|
||||
"""Returns True if the argument can be made a float."""
|
||||
try:
|
||||
float(s)
|
||||
return True
|
||||
except:
|
||||
return False
|
||||
|
||||
@name.setter
|
||||
def name(self, name):
|
||||
name = self.internal_to_declared(name)
|
||||
self._name = name
|
||||
|
||||
@property
|
||||
def internal_name(self):
|
||||
return self.declared_to_internal(self.name)
|
||||
def is_iter(obj):
|
||||
"""Return True if the argument is list-like."""
|
||||
return hasattr(obj, '__iter__')
|
||||
|
||||
@staticmethod
|
||||
def internal_to_declared(name):
|
||||
"""
|
||||
Make the conversion
|
||||
__s --> :
|
||||
__i --> +
|
||||
__a --> ?
|
||||
"""
|
||||
for old, new in _SPECIAL_CONVERSION:
|
||||
name = name.replace(old, new)
|
||||
return name
|
||||
|
||||
@staticmethod
|
||||
def declared_to_internal(name):
|
||||
"""
|
||||
Make the conversion
|
||||
: --> __s
|
||||
+ --> __i
|
||||
? --> __a
|
||||
"""
|
||||
for new, old in _SPECIAL_CONVERSION:
|
||||
name = name.replace(old, new)
|
||||
return name
|
||||
#def is_scalar(obj):
|
||||
# """Return True if the argument is not list-like."""
|
||||
# return not is_iter
|
||||
|
||||
|
||||
def flatten(iterable):
|
||||
"""Make an iterable flat, i.e. a 1d iterable object."""
|
||||
iterator = iter(iterable)
|
||||
array, stack = collections.deque(), collections.deque()
|
||||
while True:
|
||||
try:
|
||||
value = next(iterator)
|
||||
except StopIteration:
|
||||
if not stack:
|
||||
return tuple(array)
|
||||
iterator = stack.pop()
|
||||
else:
|
||||
if not isinstance(value, str) \
|
||||
and isinstance(value, collections.Iterable):
|
||||
stack.append(iterator)
|
||||
iterator = iter(value)
|
||||
else:
|
||||
array.append(value)
|
||||
|
||||
#def listify(obj):
|
||||
# """Return a flat list out of the argument."""
|
||||
# if not obj:
|
||||
# obj = list()
|
||||
# elif is_iter(obj):
|
||||
# obj = list(flatten(obj))
|
||||
# else:
|
||||
# obj = [obj]
|
||||
# return deepcopy(obj)
|
|
@ -575,7 +575,7 @@ class Structure(pymatgen.Structure, NotebookWriter):
|
|||
@property
|
||||
def abi_string(self):
|
||||
"""Return a string with the ABINIT input associated to this structure."""
|
||||
from abipy.htc.variable import InputVariable
|
||||
from abipy.abio.variable import InputVariable
|
||||
lines = []
|
||||
app = lines.append
|
||||
abivars = self.to_abivars()
|
||||
|
|
|
@ -506,64 +506,3 @@ class AbipyTest(PymatgenTest):
|
|||
@wraps(get_gsinput_si)
|
||||
def get_gsinput_si(*args, **kwargs):
|
||||
return get_gsinput_si(*args, **kwargs)
|
||||
|
||||
|
||||
class AbipyFileTest(AbipyTest):
|
||||
"""
|
||||
Test class for files with a __str__ attribute.
|
||||
At setup, must set the 'file' attribute of the AbipyFileTest.
|
||||
"""
|
||||
file = None
|
||||
|
||||
@staticmethod
|
||||
def normalize(string):
|
||||
string = string.replace('\t', ' ')
|
||||
string = string.replace("$", " CASH ")
|
||||
string = string.replace("(", " LP ")
|
||||
string = string.replace(")", " RP ")
|
||||
string = string.replace("*", " STAR ")
|
||||
string = string.strip()
|
||||
string = '\n'.join([line.strip() for line in string.splitlines()])
|
||||
|
||||
return string
|
||||
|
||||
def assertContains(self, expression):
|
||||
"""
|
||||
Assert that the string representation of the file contains 'expression'
|
||||
'expression' is trimmed of leading new line.
|
||||
Each line of 'expression' is trimmed of blank spaces.
|
||||
Empty lines are ignored.
|
||||
"""
|
||||
expression = self.normalize(expression)
|
||||
ref = self.normalize(str(self.file))
|
||||
|
||||
return self.assertRegexpMatches(ref, expression)
|
||||
|
||||
def assertContainsNot(self, expression):
|
||||
"""
|
||||
Assert that the string representation of the file does not contain
|
||||
'expression'.
|
||||
"""
|
||||
expression = self.normalize(expression)
|
||||
ref = self.normalize(str(self.file))
|
||||
|
||||
return self.assertNotRegexpMatches(ref, expression)
|
||||
|
||||
def assertEmpty(self):
|
||||
"""Assert the string representation is empty."""
|
||||
s = str(self.file).strip()
|
||||
self.assertFalse(bool(s))
|
||||
|
||||
def assertOrder(self, expression1, expression2):
|
||||
"""
|
||||
Assert that the string representation of the file
|
||||
contains 'expression1' before 'expression2'.
|
||||
"""
|
||||
expression1 = self.normalize(expression1)
|
||||
expression2 = self.normalize(expression2)
|
||||
ref = self.normalize(str(self.file))
|
||||
self.assertRegexpMatches(ref, expression1)
|
||||
self.assertRegexpMatches(ref, expression2)
|
||||
last = ref.split(expression1)[-1]
|
||||
|
||||
return self.assertRegexpMatches(last, expression2)
|
||||
|
|
|
@ -7,7 +7,7 @@ from __future__ import print_function, division, unicode_literals, absolute_impo
|
|||
|
||||
import sys
|
||||
|
||||
from abipy.htc.fftbench import FFT_Benchmark
|
||||
from abipy.tools.fftprof import FFTBenchmark
|
||||
|
||||
|
||||
def main():
|
||||
|
@ -18,7 +18,7 @@ def main():
|
|||
|
||||
# Plot the benchmark results saved in the files
|
||||
for prof_file in prof_files:
|
||||
FFT_Benchmark.from_file(prof_file).plot()
|
||||
FFTBenchmark.from_file(prof_file).plot()
|
||||
|
||||
return 0
|
||||
|
||||
|
|
|
@ -15,7 +15,6 @@ from monty.functools import lazy_property
|
|||
from pymatgen.core.units import EnergyArray, ArrayWithUnit
|
||||
from pymatgen.entries.computed_entries import ComputedEntry, ComputedStructureEntry
|
||||
from abipy.core.mixins import AbinitNcFile, Has_Header, Has_Structure, Has_ElectronBands, NotebookWriter
|
||||
from prettytable import PrettyTable
|
||||
from abipy.tools.plotting import add_fig_kwargs, get_ax_fig_plt, get_axarray_fig_plt
|
||||
from abipy.abio.robots import Robot
|
||||
from abipy.electrons.ebands import ElectronsReader, RobotWithEbands
|
||||
|
@ -317,6 +316,7 @@ class EnergyTerms(AttrDict):
|
|||
@property
|
||||
def table(self):
|
||||
"""PrettyTable object with the results."""
|
||||
from prettytable import PrettyTable
|
||||
table = PrettyTable(["Term", "Value"])
|
||||
for k, doc in self._NAME2DOC.items():
|
||||
table.add_row([k, self[k]])
|
||||
|
|
|
@ -15,6 +15,8 @@ import abipy.data as abidata
|
|||
import abipy.abilab as abilab
|
||||
import abipy.flowtk as flowtk
|
||||
|
||||
from abipy.flowtk.abiobjects import LdauParams
|
||||
|
||||
|
||||
def make_scf_nscf_dos_inputs(structure, pseudos, luj_params, paral_kgb=1):
|
||||
# Input file taken from tldau_2.in
|
||||
|
@ -96,7 +98,7 @@ def build_flow(options):
|
|||
|
||||
for u in u_values:
|
||||
# Apply U-J on Ni only.
|
||||
luj_params = abilab.LdauParams(usepawu, structure)
|
||||
luj_params = LdauParams(usepawu, structure)
|
||||
luj_params.luj_for_symbol("Ni", l=2, u=u, j=0.1*u, unit="eV")
|
||||
|
||||
scf_input, nscf_input, dos_input = make_scf_nscf_dos_inputs(structure, pseudos, luj_params)
|
||||
|
|
|
@ -1,3 +1,160 @@
|
|||
from __future__ import print_function, division, unicode_literals, absolute_import
|
||||
|
||||
import collections
|
||||
|
||||
from pymatgen.core.units import Energy
|
||||
from pymatgen.io.abinit.abiobjects import *
|
||||
|
||||
|
||||
#__all__ = [
|
||||
# "LdauParams",
|
||||
# "LexxParams",
|
||||
#]
|
||||
|
||||
class LujForSpecie(collections.namedtuple("LdauForSpecie", "l u j unit")):
|
||||
"""
|
||||
This object stores the value of l, u, j used for a single atomic specie.
|
||||
"""
|
||||
def __new__(cls, l, u, j, unit):
|
||||
"""
|
||||
Args:
|
||||
l: Angular momentum (int or string).
|
||||
u: U value
|
||||
j: J Value
|
||||
unit: Energy unit for u and j.
|
||||
"""
|
||||
l = l
|
||||
u, j = Energy(u, unit), Energy(j, unit)
|
||||
return super(cls, LujForSpecie).__new__(cls, l, u, j, unit)
|
||||
|
||||
|
||||
class LdauParams(object):
|
||||
"""
|
||||
This object stores the parameters for LDA+U calculations with the PAW method
|
||||
It facilitates the specification of the U-J parameters in the Abinit input file.
|
||||
(see `to_abivars`). The U-J operator will be applied only on the atomic species
|
||||
that have been selected by calling `lui_for_symbol`.
|
||||
|
||||
To setup the Abinit variables for a LDA+U calculation in NiO with a
|
||||
U value of 5 eV applied on the nickel atoms:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
luj_params = LdauParams(usepawu=1, structure=nio_structure)
|
||||
# Apply U-J on Ni only.
|
||||
u = 5.0
|
||||
luj_params.luj_for_symbol("Ni", l=2, u=u, j=0.1*u, unit="eV")
|
||||
|
||||
print(luj_params.to_abivars())
|
||||
"""
|
||||
def __init__(self, usepawu, structure):
|
||||
"""
|
||||
Arg:
|
||||
usepawu: ABINIT variable `usepawu` defining the LDA+U method.
|
||||
structure: |Structure| object.
|
||||
"""
|
||||
self.usepawu = usepawu
|
||||
self.structure = structure
|
||||
self._params = {}
|
||||
|
||||
@property
|
||||
def symbols_by_typat(self):
|
||||
return [specie.symbol for specie in self.structure.types_of_specie]
|
||||
|
||||
def luj_for_symbol(self, symbol, l, u, j, unit="eV"):
|
||||
"""
|
||||
Args:
|
||||
symbol: Chemical symbol of the atoms on which LDA+U should be applied.
|
||||
l: Angular momentum.
|
||||
u: Value of U.
|
||||
j: Value of J.
|
||||
unit: Energy unit of U and J.
|
||||
"""
|
||||
if symbol not in self.symbols_by_typat:
|
||||
raise ValueError("Symbol %s not in symbols_by_typat:\n%s" % (symbol, self.symbols_by_typat))
|
||||
|
||||
if symbol in self._params:
|
||||
raise ValueError("Symbol %s is already present in LdauParams! Cannot overwrite:\n" % symbol)
|
||||
|
||||
self._params[symbol] = LujForSpecie(l=l, u=u, j=j, unit=unit)
|
||||
|
||||
def to_abivars(self):
|
||||
"""Returns a dict with the Abinit variables."""
|
||||
lpawu, upawu, jpawu = [], [], []
|
||||
|
||||
for symbol in self.symbols_by_typat:
|
||||
p = self._params.get(symbol, None)
|
||||
|
||||
if p is not None:
|
||||
l, u, j = p.l, p.u.to("eV"), p.j.to("eV")
|
||||
else:
|
||||
l, u, j = -1, 0, 0
|
||||
|
||||
lpawu.append(int(l))
|
||||
upawu.append(float(u))
|
||||
jpawu.append(float(j))
|
||||
|
||||
# convert upawu and jpaw to string so that we can use
|
||||
# eV unit in the Abinit input file (much more readable).
|
||||
return dict(
|
||||
usepawu=self.usepawu,
|
||||
lpawu=" ".join(map(str, lpawu)),
|
||||
upawu=" ".join(map(str, upawu)) + " eV",
|
||||
jpawu=" ".join(map(str, jpawu)) + " eV")
|
||||
|
||||
|
||||
class LexxParams(object):
|
||||
"""
|
||||
This object stores the parameters for local exact exchange calculations with the PAW method
|
||||
It facilitates the specification of the LEXX parameters in the Abinit input file.
|
||||
(see `to_abivars`). The LEXX operator will be applied only on the atomic species
|
||||
that have been selected by calling `lexx_for_symbol`.
|
||||
|
||||
To perform a LEXX calculation for NiO in which the LEXX is computed only for the l=2
|
||||
channel of the nickel atoms:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
lexx_params = LexxParams(nio_structure)
|
||||
lexx_params.lexx_for_symbol("Ni", l=2)
|
||||
|
||||
print(lexc_params.to_abivars())
|
||||
"""
|
||||
def __init__(self, structure):
|
||||
"""
|
||||
Arg:
|
||||
structure: |Structure| object.
|
||||
"""
|
||||
self.structure = structure
|
||||
self._lexx_for_symbol = {}
|
||||
|
||||
@property
|
||||
def symbols_by_typat(self):
|
||||
return [specie.symbol for specie in self.structure.types_of_specie]
|
||||
|
||||
def lexx_for_symbol(self, symbol, l):
|
||||
"""
|
||||
Enable LEXX for the given chemical symbol and the angular momentum l
|
||||
|
||||
Args:
|
||||
symbol: Chemical symbol of the atoms on which LEXX should be applied.
|
||||
l: Angular momentum.
|
||||
"""
|
||||
if symbol not in self.symbols_by_typat:
|
||||
err_msg = "Symbol %s not in symbols_by_typat:\n%s" % (symbol, self.symbols_by_typat)
|
||||
raise ValueError(err_msg)
|
||||
|
||||
if symbol in self._lexx_for_symbol:
|
||||
raise ValueError("Symbol %s is already present in LdauParams! Cannot overwrite:" % symbol)
|
||||
|
||||
self._lexx_for_symbol[symbol] = l
|
||||
|
||||
def to_abivars(self):
|
||||
"""Returns a dict with the Abinit variables."""
|
||||
lexx_typat = []
|
||||
|
||||
for symbol in self.symbols_by_typat:
|
||||
l = self._lexx_for_symbol.get(symbol, -1)
|
||||
lexx_typat.append(int(l))
|
||||
|
||||
return dict(useexexch=1, lexexch=" ".join(map(str, lexx_typat)))
|
||||
|
|
|
@ -0,0 +1,55 @@
|
|||
"""Tests for flowtk.abiobjects module."""
|
||||
from __future__ import print_function, division, unicode_literals
|
||||
|
||||
import numpy as np
|
||||
import abipy.data as abidata
|
||||
|
||||
from abipy.core.testing import AbipyTest
|
||||
from abipy.flowtk.abiobjects import LdauParams, LexxParams
|
||||
|
||||
|
||||
class LdauLexxTest(AbipyTest):
|
||||
|
||||
def test_nio(self):
|
||||
"""Test LdauParams and LexxParams."""
|
||||
aequal, atrue = self.assertEqual, self.assertTrue
|
||||
|
||||
structure = abidata.structure_from_ucell("NiO")
|
||||
pseudos = abidata.pseudos("28ni.paw", "8o.2.paw")
|
||||
|
||||
u = 8.0
|
||||
luj_params = LdauParams(usepawu=1, structure=structure)
|
||||
luj_params.luj_for_symbol("Ni", l=2, u=u, j=0.1*u, unit="eV")
|
||||
avars = luj_params.to_abivars()
|
||||
|
||||
self.serialize_with_pickle(luj_params, test_eq=False)
|
||||
|
||||
atrue(avars["usepawu"] == 1),
|
||||
aequal(avars["lpawu"], "2 -1"),
|
||||
aequal(avars["upawu"], "8.0 0.0 eV"),
|
||||
aequal(avars["jpawu"], "0.8 0.0 eV"),
|
||||
|
||||
# Cannot add UJ for non-existent species.
|
||||
with self.assertRaises(ValueError):
|
||||
luj_params.luj_for_symbol("Foo", l=2, u=u, j=0.1*u, unit="eV")
|
||||
|
||||
# Cannot overwrite UJ.
|
||||
with self.assertRaises(ValueError):
|
||||
luj_params.luj_for_symbol("Ni", l=1, u=u, j=0.1*u, unit="eV")
|
||||
|
||||
lexx_params = LexxParams(structure)
|
||||
lexx_params.lexx_for_symbol("Ni", l=2)
|
||||
avars = lexx_params.to_abivars()
|
||||
|
||||
self.serialize_with_pickle(lexx_params, test_eq=False)
|
||||
|
||||
aequal(avars["useexexch"], 1),
|
||||
aequal(avars["lexexch"], "2 -1"),
|
||||
|
||||
# Cannot add LEXX for non-existent species.
|
||||
with self.assertRaises(ValueError):
|
||||
lexx_params.lexx_for_symbol("Foo", l=2)
|
||||
|
||||
# Cannot overwrite LEXX.
|
||||
with self.assertRaises(ValueError):
|
||||
lexx_params.lexx_for_symbol("Ni", l=1)
|
|
@ -5,7 +5,7 @@ import wx
|
|||
from collections import OrderedDict
|
||||
from abipy.gui import awx
|
||||
|
||||
from abipy.htc.fftbench import FFTProf
|
||||
from abipy.tools.fftprof import FFTProf
|
||||
from abipy.gui.editor import SimpleTextViewer
|
||||
from .awx.panels import LinspaceControl
|
||||
|
||||
|
|
|
@ -1,22 +0,0 @@
|
|||
"""
|
||||
Abinit files handling library
|
||||
"""
|
||||
from __future__ import print_function, division, unicode_literals
|
||||
|
||||
__all__ = []
|
||||
|
||||
_mods = [
|
||||
'input',
|
||||
'abinitfiles',
|
||||
'filesfile',
|
||||
'variable',
|
||||
'inputfile',
|
||||
'jobfile',
|
||||
'abinitinput',
|
||||
'launcher',
|
||||
]
|
||||
|
||||
#for _mod in _mods:
|
||||
# exec('from .' + _mod + ' import *')
|
||||
# exec('__all__.extend(' + _mod + '.__all__)')
|
||||
# exec('del ' + _mod)
|
|
@ -1,195 +0,0 @@
|
|||
"""
|
||||
Base class for an ABINIT calculation.
|
||||
"""
|
||||
from __future__ import print_function, division #, unicode_literals
|
||||
from os.path import basename, dirname, join, splitext, realpath
|
||||
|
||||
__all__ = ["AbinitFiles"]
|
||||
|
||||
|
||||
# =========================================================================== #
|
||||
|
||||
class AbinitFiles(object):
|
||||
"""
|
||||
A manager for the abinit file names.
|
||||
|
||||
A calculation is organised as follow::
|
||||
|
||||
CalcDir/
|
||||
|-- CalcRoot.in
|
||||
|-- CalcRoot.out
|
||||
|-- input_data/
|
||||
| |-- idat_CalcRoot_DS2_DEN
|
||||
|-- run/
|
||||
| |-- CalcRoot.files
|
||||
| |-- CalcRoot.sh
|
||||
| |-- CalcRoot.log
|
||||
| |-- out_data/
|
||||
| | |-- odat_CalcRoot_DS1_DEN
|
||||
| | |-- odat_CalcRoot_DS1_WFK
|
||||
| |-- tmp_data/
|
||||
| | |-- tmp_CalcRoot_LOG_0001
|
||||
| | |
|
||||
"""
|
||||
|
||||
_directory = {
|
||||
'inp' : '',
|
||||
'out' : '',
|
||||
'job' : 'run',
|
||||
'files' : 'run',
|
||||
'log' : 'run',
|
||||
'idat' : 'input_data',
|
||||
'odat' : join('run', 'out_data'),
|
||||
'tmp' : join('run', 'tmp_data')}
|
||||
|
||||
_prefix = {
|
||||
'inp' : '',
|
||||
'out' : '',
|
||||
'job' : '',
|
||||
'files' : '',
|
||||
'log' : '',
|
||||
'idat' : 'idat_',
|
||||
'odat' : 'odat_',
|
||||
'tmp' : 'tmp_'}
|
||||
|
||||
_suffix = {
|
||||
'inp' : '.in',
|
||||
'out' : '.out',
|
||||
'files' : '.files',
|
||||
'log' : '.log',
|
||||
'idat' : '',
|
||||
'odat' : '',
|
||||
'tmp' : '',
|
||||
'job' : '.sh'}
|
||||
|
||||
def _form_name(self, filetype):
|
||||
d = self._directory[filetype]
|
||||
pr = self._prefix[filetype]
|
||||
sf = self._suffix[filetype]
|
||||
return join(self.absdir, d, pr + self.basename + sf)
|
||||
|
||||
def __init__(self, rootname='AbinitCalculation/root'):
|
||||
|
||||
# Take the root name
|
||||
if '.' in rootname:
|
||||
rootname = splitext(rootname)[0]
|
||||
|
||||
# The calculation must be in a directory.
|
||||
if rootname == basename(rootname):
|
||||
rootname = join(rootname, 'calc')
|
||||
|
||||
self.set_rootname(rootname)
|
||||
|
||||
def set_rootname(self, name):
|
||||
"""Set the root name."""
|
||||
self.__dict__['rootname'] = name
|
||||
self.__dict__['absdir'] = realpath(dirname(name))
|
||||
|
||||
@property
|
||||
def dirname(self):
|
||||
"""The top-level directory."""
|
||||
return dirname(self.rootname)
|
||||
|
||||
@property
|
||||
def basename(self):
|
||||
"""The basename of the root name."""
|
||||
return basename(self.rootname)
|
||||
|
||||
@property
|
||||
def name(self):
|
||||
"""The root name."""
|
||||
return self.rootname
|
||||
|
||||
@property
|
||||
def files_name(self):
|
||||
"""The name of the ".files" file given to abinit."""
|
||||
return self._form_name('files')
|
||||
|
||||
@property
|
||||
def job_name(self):
|
||||
"""The submission script name."""
|
||||
return self._form_name('job')
|
||||
|
||||
@property
|
||||
def input_name(self):
|
||||
"""The input file name."""
|
||||
return self._form_name('inp')
|
||||
|
||||
@property
|
||||
def log_name(self):
|
||||
"""The name of the log file."""
|
||||
return self._form_name('log')
|
||||
|
||||
@property
|
||||
def idat_root(self):
|
||||
"""The root name for input data files."""
|
||||
return self._form_name('idat')
|
||||
|
||||
@property
|
||||
def odat_root(self):
|
||||
"""The root name for output data files."""
|
||||
return self._form_name('odat')
|
||||
|
||||
@property
|
||||
def tmp_root(self):
|
||||
"""The root name for temporaty data files."""
|
||||
return self._form_name('tmp')
|
||||
|
||||
@property
|
||||
def output_name(self):
|
||||
"""The output file produced (based on the name only)."""
|
||||
return self._form_name('out')
|
||||
|
||||
def get_odat(self, datatype, dtset=0):
|
||||
"""
|
||||
Returns an output data file name.
|
||||
|
||||
Args:
|
||||
datatype:
|
||||
The type of datafile, e.g. 'DEN' or 'WFK'.
|
||||
|
||||
dtset:
|
||||
The dataset index from which to take the data file.
|
||||
If 0 (the default), no dataset index is used.
|
||||
"""
|
||||
file = self.odat_root
|
||||
|
||||
if int(dtset) > 0: file += '_DS' + str(dtset)
|
||||
|
||||
file += '_' + datatype.upper().lstrip('_')
|
||||
|
||||
return file
|
||||
|
||||
def get_idat(self, datatype, dtset=0):
|
||||
"""
|
||||
Returns an input data file name.
|
||||
|
||||
Args:
|
||||
datatype:
|
||||
The type of datafile, e.g. 'DEN' or 'WFK'.
|
||||
|
||||
dtset:
|
||||
The dataset index from which to take the data file.
|
||||
If 0 (the default), no dataset index is used.
|
||||
"""
|
||||
file = self.idat_root
|
||||
|
||||
if int(dtset) > 0: file += '_DS' + str(dtset)
|
||||
|
||||
file += '_' + datatype.upper().lstrip('_')
|
||||
|
||||
return file
|
||||
|
||||
def get_netcdf(self, datatype, dtset=0):
|
||||
"""
|
||||
Returns a netcdf output data file name.
|
||||
|
||||
Args:
|
||||
datatype:
|
||||
The type of datafile, 'DEN' or 'WFK'.
|
||||
|
||||
dtset:
|
||||
The dataset index from which to take the data file.
|
||||
If 0 (the default), no dataset index is used.
|
||||
"""
|
||||
return self.get_odat(datatype, dtset=dtset) + '.nc'
|
|
@ -1,313 +0,0 @@
|
|||
from __future__ import print_function, division #, unicode_literals
|
||||
|
||||
import subprocess
|
||||
|
||||
from os import makedirs, readlink, symlink
|
||||
from os.path import basename, dirname, exists, join, realpath
|
||||
|
||||
from abipy.profile import abipy_env
|
||||
from .filesfile import FilesFile
|
||||
from .abinitfiles import AbinitFiles
|
||||
from .inputfile import InputFile
|
||||
from .jobfile import JobFile, PBSJobFile, SGEJobFile, SlurmJobFile
|
||||
|
||||
__all__ = ['AbinitInput']
|
||||
|
||||
# =========================================================================== #
|
||||
|
||||
class AbinitInput(AbinitFiles):
|
||||
"""
|
||||
To Create an Abinit calculation.
|
||||
The AbinitInput contains three main internal objects:
|
||||
an :class:`~abipy.htc.InputFile`, a :class:`~abipy.htc.FilesFile`,
|
||||
and a :class:`~abipy.htc.JobFile`.
|
||||
|
||||
**Arguments**:
|
||||
name:
|
||||
A string of the form 'directory/rootname' or simply 'directory'.
|
||||
jobtype:
|
||||
The type of job, e.g. 'PBS', 'SGE', 'Slurm' or None.
|
||||
|
||||
**Keyword arguments for the inputfile**:
|
||||
variables:
|
||||
A dictionary of input variables for the calculation.
|
||||
|
||||
**Keyword arguments for the filesfile**:
|
||||
pseudodir:
|
||||
The directory in which to look for the pseudopotential files.
|
||||
pseudos:
|
||||
A list of pseudopotential files.
|
||||
|
||||
**Keyword arguments for the jobfile**:
|
||||
bindir:
|
||||
The directory in which to look for binaries.
|
||||
executable:
|
||||
The binary to be executed. Default is 'abinit'.
|
||||
mpirun:
|
||||
The mpi runner. E.g. 'mpiexec -npernode 6'.
|
||||
modules:
|
||||
List of modules which will be loaded with 'module load'.
|
||||
lines_before:
|
||||
List of lines to be executed before the main execution.
|
||||
lines_after:
|
||||
List of lines to be executed after the main execution.
|
||||
other_lines:
|
||||
List of command lines for the job submission system.
|
||||
|
||||
Any property of the JobFile or the selected subclass
|
||||
(PBSJobFile, SGEJobFile, SlurmJobFile, ...)
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
>> calc = AbinitInput('Mycalc/rootname', jobtype='PBS',
|
||||
.. pseudodir='Data',
|
||||
.. bindir='/path/to/binaries/',
|
||||
.. lines_before=['cd ${PBS_O_WORKDIR}'])
|
||||
>>
|
||||
>> # Input file properties
|
||||
>> calc.ecut = 10.
|
||||
>> calc.tolwfr = 1e-8
|
||||
>>
|
||||
>> calc.kptopt = 1
|
||||
>> calc.ngkpt = [2, 2, 2]
|
||||
>> calc.nshiftk = 1
|
||||
>> calc.shiftk = [0.0, 0.0, 0.0]
|
||||
>>
|
||||
>> unit_cell = {
|
||||
.. 'acell' : 3*[8.6277],
|
||||
.. 'rprim' : [[.0, .5, .5],
|
||||
.. [.5, .0, .5],
|
||||
.. [.5, .5, .0]],
|
||||
.. 'ntypat' : 2,
|
||||
.. 'znucl' : [30, 8],
|
||||
.. 'natom' : 2,
|
||||
.. 'typat' : [1, 2],
|
||||
.. 'xred' : [[.0, .0, .0],
|
||||
.. [.25,.25,.25]]}
|
||||
>>
|
||||
>> calc.set_variables(unit_cell)
|
||||
>>
|
||||
>> # Files file properties
|
||||
>> calc.set_pseudos('Zn.psp', 'O.psp')
|
||||
>>
|
||||
>> # Job file properties
|
||||
>> calc.set_jobname('MyTest')
|
||||
>> calc.set_nodes(2)
|
||||
>> calc.set_ppn(12)
|
||||
>> calc.set_memory('24gb')
|
||||
>> calc.set_runtime(48)
|
||||
>> calc.set_mpirun('mpiexec -npernode 6')
|
||||
>>
|
||||
>> calc.write()
|
||||
|
||||
As you can see, setting an attribute of the AbinitInput object
|
||||
will result in setting that input variable in the input file.
|
||||
|
||||
Note also that *all* of the :class:`~abipy.htc.JobFile` functions are available
|
||||
though the AbinitInput.
|
||||
"""
|
||||
|
||||
def __init__(self, name='Calculation/abinit', jobtype='PBS', **kwargs):
|
||||
|
||||
AbinitFiles.__init__(self, name)
|
||||
|
||||
# Initialize the inputfile
|
||||
self._setattr(inputfile = InputFile(self.input_name))
|
||||
|
||||
# Initialize the filesfile
|
||||
self._setattr(filesfile = FilesFile(self.files_name,
|
||||
input=self.input_name,
|
||||
output=self.output_name,
|
||||
idat_root=self.idat_root,
|
||||
odat_root=self.odat_root,
|
||||
tmp_root=self.tmp_root))
|
||||
|
||||
# Initialize the jobfile
|
||||
jobargs = dict(name=self.job_name, executable='abinit',
|
||||
input=self.files_name,
|
||||
log=self.log_name)
|
||||
|
||||
#jobtype = abipy_env.get_uservar("jobtype", kwargs)
|
||||
if jobtype is None: jobtype = ''
|
||||
|
||||
if jobtype.lower() == 'pbs':
|
||||
self._setattr(jobfile = PBSJobFile(**jobargs))
|
||||
|
||||
elif jobtype.lower() == 'sge':
|
||||
self._setattr(jobfile = SGEJobFile(**jobargs))
|
||||
|
||||
elif jobtype.lower() == 'slurm':
|
||||
self._setattr(jobfile = SlurmJobFile(**jobargs))
|
||||
|
||||
else:
|
||||
self._setattr(jobfile = JobFile(**jobargs))
|
||||
|
||||
# Create setter function for jobfile attributes.
|
||||
for prop in self.jobfile.properties():
|
||||
function = 'set_' + prop
|
||||
self.__dict__[function] = getattr(self.jobfile, function)
|
||||
|
||||
# Pairs of (target, pointer) to be linked.
|
||||
self._setattr(_to_link = list())
|
||||
|
||||
# Other arguments
|
||||
for key in self.properties():
|
||||
val = abipy_env.get_default(key, kwargs)
|
||||
if val is not None:
|
||||
getattr(self, 'set_' + key)(val)
|
||||
|
||||
def _setattr(self, **kwargs):
|
||||
self.__dict__.update(kwargs)
|
||||
|
||||
def __setattr__(self, name, val):
|
||||
setattr(self.inputfile, name, val)
|
||||
|
||||
def write(self, *args, **kwargs):
|
||||
"""
|
||||
Write all the files for the calculation.
|
||||
"""
|
||||
|
||||
force = kwargs.get("force", False)
|
||||
if not force and self.inputfile.exists:
|
||||
print("Cannot overwrite: %s\tUse -f to force file creation." % self.name)
|
||||
return
|
||||
|
||||
# Create directories
|
||||
for file in (self.input_name, self.output_name, self.job_name,
|
||||
self.files_name, self.log_name, self.idat_root,
|
||||
self.odat_root, self.tmp_root):
|
||||
directory = dirname(file)
|
||||
if not exists(directory):
|
||||
makedirs(directory)
|
||||
|
||||
# Link files
|
||||
for pair in self._to_link:
|
||||
self._link(*pair)
|
||||
|
||||
# Write files
|
||||
self.inputfile.write()
|
||||
self.filesfile.write()
|
||||
self.jobfile.write()
|
||||
|
||||
def link_idat(self, file, dtset='auto', datatype='auto'):
|
||||
"""
|
||||
Make a symbolic link for input data.
|
||||
|
||||
Args:
|
||||
file:
|
||||
The name of the file to be linked.
|
||||
dtset:
|
||||
The index of the dataset which should read the file.
|
||||
By default, it is read from the file name.
|
||||
Set to zero for no dataset index.
|
||||
datatype:
|
||||
The type of datafile, e.g. 'DEN' or 'WFK'.
|
||||
By default, it is read from the file name.
|
||||
"""
|
||||
if datatype == 'auto':
|
||||
datatype = file.split('_')[-1]
|
||||
|
||||
if dtset == 'auto':
|
||||
dtset = file.split('_DS', 1)[-1].split('_')[0]
|
||||
try:
|
||||
dtset = int(dtset)
|
||||
except:
|
||||
dtset = 0
|
||||
|
||||
self._to_link.append((file, self.get_idat(datatype, dtset)))
|
||||
|
||||
def link_odat(self, file, dtset='auto', datatype='auto'):
|
||||
"""
|
||||
Make a symbolic link for output data.
|
||||
|
||||
Args:
|
||||
file:
|
||||
The name of the file to be linked.
|
||||
dtset:
|
||||
The index of the dataset from which the file belongs.
|
||||
By default, it is read from the file name.
|
||||
Set to zero for no dataset index.
|
||||
datatype:
|
||||
The type of datafile, e.g. 'DEN' or 'WFK'.
|
||||
By default, it is read from the file name.
|
||||
"""
|
||||
if datatype == 'auto':
|
||||
datatype = file.split('_')[-1]
|
||||
|
||||
if dtset == 'auto':
|
||||
dtset = file.split('_DS', 1)[-1].split('_')[0]
|
||||
try:
|
||||
dtset = int(dtset)
|
||||
except:
|
||||
dtset = 0
|
||||
|
||||
self._to_link.append((file, self.get_odat(datatype, dtset)))
|
||||
|
||||
def link_io(self, idtset, odtset, datatype):
|
||||
"""
|
||||
Make a symbolic link from an output data to an input data.
|
||||
|
||||
Args:
|
||||
idtset:
|
||||
The dataset index of the input file.
|
||||
odtset:
|
||||
The dataset index of the output file.
|
||||
datatype:
|
||||
The type of datafile, e.g. 'DEN' or 'WFK'.
|
||||
"""
|
||||
idat = self.get_idat(datatype, idtset)
|
||||
odat = self.get_odat(datatype, odtset)
|
||||
self._to_link.append((odat, idat))
|
||||
|
||||
def set_pseudodir(self, pseudodir):
|
||||
"""Set the directory for the pseudopotentials. Linked to FilesFile."""
|
||||
return self.filesfile.set_pseudodir(pseudodir)
|
||||
|
||||
def set_pseudos(self, *pseudopotentials):
|
||||
"""Set the pseudopotential files. Linked to FilesFile."""
|
||||
return self.filesfile.set_pseudos(*pseudopotentials)
|
||||
|
||||
def read(self, *args, **kwargs):
|
||||
"""Read an input file. Linked to InputFile."""
|
||||
return self.inputfile.read(*args, **kwargs)
|
||||
|
||||
def set_variables(self, *args, **kwargs):
|
||||
"""Set input variables. Linked to InputFile."""
|
||||
return self.inputfile.set_variables(*args, **kwargs)
|
||||
|
||||
def set_comment(self, *args, **kwargs):
|
||||
"""Set a comment in the input file. Linked to InputFile."""
|
||||
return self.inputfile.set_comment(*args, **kwargs)
|
||||
|
||||
def properties(self):
|
||||
"""Return the list of properties with a 'set_' function."""
|
||||
funcs = filter(lambda s: s.startswith('set_'), dir(self))
|
||||
return [ f.split('set_', 1)[-1] for f in funcs ]
|
||||
|
||||
@staticmethod
|
||||
def _link(target, pointer):
|
||||
"""
|
||||
Create the symbolic link target <-- pointer.
|
||||
Overwrite an existing link, but don't overwrite a file.
|
||||
"""
|
||||
|
||||
atarget = realpath(target)
|
||||
pointerdir = realpath(dirname(pointer))
|
||||
apointer = join(pointerdir, basename(pointer))
|
||||
|
||||
if not exists(pointerdir):
|
||||
subprocess.call(('mkdir', '-p', pointerdir))
|
||||
|
||||
try:
|
||||
symlink(atarget, apointer)
|
||||
except OSError:
|
||||
try:
|
||||
oldtarget = realpath(readlink(apointer))
|
||||
if oldtarget == atarget:
|
||||
return
|
||||
else:
|
||||
subprocess.call(('ln', '-fs', atarget, apointer))
|
||||
except OSError:
|
||||
raise OSError("Unable to link the files. " +
|
||||
"Maybe {0} exists and is not a link.".format(apointer))
|
||||
|
|
@ -1,145 +0,0 @@
|
|||
from __future__ import print_function, division #, unicode_literals
|
||||
import warnings
|
||||
|
||||
from os import makedirs
|
||||
from os.path import dirname, join, exists, realpath
|
||||
from copy import deepcopy
|
||||
|
||||
from .abinitfiles import AbinitFiles
|
||||
|
||||
__all__ = ['FilesFile']
|
||||
|
||||
# =========================================================================== #
|
||||
|
||||
|
||||
class FilesFile(object):
|
||||
"""
|
||||
A .files file used as input for abinit.
|
||||
|
||||
**Keyword arguments:**
|
||||
|
||||
input: Input file.
|
||||
output: Output file.
|
||||
idat_root: Root for input data.
|
||||
odat_root: Root for output data.
|
||||
tmp_root: Root for temporary files.
|
||||
pseudodir: The directory for the pseudopotentials.
|
||||
pseudos: List of pseudopotential files.
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
>> filesfile = Filesfile('run/abinit.files',
|
||||
.. input='abinit.in',
|
||||
.. output='abinit.out',
|
||||
.. idat_root='run/data_files/idat_',
|
||||
.. odat_root='run/data_files/odat_',
|
||||
.. tmp_root='run/data_files/tmp_',)
|
||||
.. pseudodir='/path/to/pseudopotential/directory',)
|
||||
>>
|
||||
>> filesfile.set_pseudos(['H.psp', 'O.psp'])
|
||||
>> filesfile.write()
|
||||
>> with open('run/abinit.files', 'r') as f: print f.read()
|
||||
../CalcRoot.in
|
||||
../CalcRoot.out
|
||||
data_files/idat_
|
||||
data_files/odat_
|
||||
tmp_files/tmp_
|
||||
/path/to/pseudopotential/directory/H.psp
|
||||
/path/to/pseudopotential/directory/O.psp
|
||||
"""
|
||||
|
||||
def __init__(self, name='calc.files', **kwargs):
|
||||
|
||||
self.name = name
|
||||
|
||||
self.input = 'calc.in'
|
||||
self.output = 'calc.out'
|
||||
self.idat_root = 'idat_calc'
|
||||
self.odat_root = 'odat_calc'
|
||||
self.tmp_root = 'tmp_calc'
|
||||
|
||||
self.pseudodir = '.'
|
||||
self.pseudos = list()
|
||||
|
||||
for (arg, val) in kwargs.items():
|
||||
getattr(self, 'set_' + arg)(val)
|
||||
|
||||
def set_input(self, name):
|
||||
"""Set the input file."""
|
||||
self.input = name
|
||||
|
||||
def set_output(self, name):
|
||||
"""Set the output file."""
|
||||
self.output = name
|
||||
|
||||
def set_idat_root(self, name):
|
||||
"""Set root for input data."""
|
||||
self.idat_root = name
|
||||
|
||||
def set_odat_root(self, name):
|
||||
"""Set root for output data."""
|
||||
self.odat_root = name
|
||||
|
||||
def set_tmp_root(self, name):
|
||||
"""Set root for temporary files."""
|
||||
self.tmp_root = name
|
||||
|
||||
def set_pseudodir(self, directory):
|
||||
"""Set the directory for the pseudopotentials."""
|
||||
self.pseudodir = realpath(directory)
|
||||
|
||||
def set_pseudos(self, *pseudos):
|
||||
"""
|
||||
Sets the pseudopotential files.
|
||||
|
||||
**Arguments:**
|
||||
pseudos:
|
||||
Pseudopotential files.
|
||||
|
||||
Both of these syntax work::
|
||||
>> f.set_pseudos('H.psp', 'O.psp')
|
||||
>> f.set_pseudos(['H.psp', 'O.psp'])
|
||||
"""
|
||||
if not pseudos:
|
||||
self.pseudos = list()
|
||||
return
|
||||
|
||||
elif '__iter__' in dir(pseudos[0]):
|
||||
pseudos = tuple(pseudos[0])
|
||||
|
||||
self.pseudos = pseudos
|
||||
|
||||
@property
|
||||
def pseudos(self):
|
||||
"""Pseudopotential files."""
|
||||
return [ join(self.pseudodir, pseudo) for pseudo in self._pseudos ]
|
||||
|
||||
@pseudos.setter
|
||||
def pseudos(self, vals):
|
||||
self._pseudos = deepcopy(vals)
|
||||
|
||||
@property
|
||||
def dirname(self):
|
||||
"""The directory containing the file."""
|
||||
return dirname(self.name)
|
||||
|
||||
def check_pseudos(self):
|
||||
"""Issue a warning for each pseudopotential file not found."""
|
||||
for pseudo in self.pseudos:
|
||||
if not exists(pseudo):
|
||||
warnings.warn('Pseudopotential file not found: ' + pseudo)
|
||||
|
||||
def __str__(self):
|
||||
lines = [self.input, self.output, self.idat_root, self.odat_root,
|
||||
self.tmp_root] + self.pseudos
|
||||
return '\n'.join(lines) + '\n'
|
||||
|
||||
def write(self):
|
||||
"""Write the file."""
|
||||
self.check_pseudos()
|
||||
|
||||
if self.dirname and not exists(self.dirname):
|
||||
makedirs(self.dirname)
|
||||
|
||||
with open(self.name, 'w') as f:
|
||||
f.write(str(self))
|
1348
abipy/htc/input.py
1348
abipy/htc/input.py
File diff suppressed because it is too large
Load Diff
|
@ -1,394 +0,0 @@
|
|||
from __future__ import print_function, division #, unicode_literals
|
||||
|
||||
import string
|
||||
import os.path
|
||||
import warnings
|
||||
import numpy as np
|
||||
|
||||
from os import makedirs
|
||||
from os.path import dirname, abspath, exists
|
||||
from collections import OrderedDict
|
||||
from copy import deepcopy
|
||||
|
||||
from .utils import flatten, listify, is_number, is_iter
|
||||
from .variable import InputVariable, SpecialInputVariable, _UNITS
|
||||
|
||||
__all__ = [
|
||||
'InputFile',
|
||||
'VariableBlock',
|
||||
]
|
||||
|
||||
|
||||
_input_variable_blocks = OrderedDict((
|
||||
('Datasets', '''
|
||||
ndtset jdtset udtset
|
||||
'''),
|
||||
('Basis set', '''
|
||||
ecut ecutsm
|
||||
'''),
|
||||
('Bands', '''
|
||||
nband nbdbuf
|
||||
'''),
|
||||
('k-point grid', '''
|
||||
kptopt nkpt kpt ngkpt kptrlatt
|
||||
nshiftk shiftk kptbounds kptns
|
||||
'''),
|
||||
('Models', '''
|
||||
ixc ppmodel ppmfreq usepawu upawu jpawu
|
||||
'''),
|
||||
('PAW options', '''
|
||||
bxctmindg dmatpawu dmatpuopt dmatudiag iboxcut
|
||||
jpawu lpawu lexexch mqgriddg ngfftdg
|
||||
pawcpxocc pawcross pawecutdg pawfatbnd
|
||||
pawlcutd pawlmix pawmixdg pawnhatxc pawnphi
|
||||
pawntheta pawnzlm pawoptmix pawovlp
|
||||
pawprtden pawprtdos pawprtvol pawprtwf
|
||||
pawspnorb pawstgylm pawsushat pawusecp
|
||||
pawxcdev prtcs prtefg prtfc prtnabla
|
||||
ptcharge quadmom spnorbscl usedmatpu upawu
|
||||
useexexch usepawu usexcnhat
|
||||
'''),
|
||||
('SCF procedure', '''
|
||||
iscf nstep nline tolvrs tolwfr
|
||||
toldfe toldff tolimg tolmxf tolrff
|
||||
'''),
|
||||
('KSS generation', '''
|
||||
kssform nbandkss
|
||||
'''),
|
||||
('GW procedure', '''
|
||||
optdriver gwcalctyp spmeth nkptgw kptgw
|
||||
bdgw nqptdm qptdm
|
||||
'''),
|
||||
('GW param', '''
|
||||
ecuteps ecutsigx ecutwfn nomegasf
|
||||
nfreqim nfreqre freqremax npweps rhoqpmix
|
||||
'''),
|
||||
('GW options', '''
|
||||
userre awtr symchi gwpara symsigma gwmem fftgw
|
||||
'''),
|
||||
('Structural optimization', '''
|
||||
amu bmass delayperm diismemory dilatmx dtion dynimage
|
||||
ecutsm friction fxcartfactor getcell getxcart getxred
|
||||
goprecon goprecprm iatcon iatfix iatfixx iatfixy iatfixz
|
||||
imgmov ionmov istatimg mdtemp mdwall natfix natfixx
|
||||
natfixy natfixz natcon nconeq nimage nnos noseinert
|
||||
ntime ntimimage optcell pimass pitransform prtatlist qmass
|
||||
random_atpos restartxf signperm strfact strprecon strtarget
|
||||
tolimg tolmxf vel vis wtatcon
|
||||
'''),
|
||||
('Response function', '''
|
||||
bdeigrf elph2_imagden esmear frzfermi
|
||||
ieig2rf mkqmem mk1mem prepanl prepgkk
|
||||
prtbbb rfasr rfatpol rfddk rfdir rfelfd
|
||||
rfmeth rfphon rfstrs rfuser rf1atpol rf1dir
|
||||
rf1elfd rf1phon rf2atpol rf2dir rf2elfd
|
||||
rf2phon rf3atpol rf3dir rf3elfd rf3phon
|
||||
sciss smdelta td_maxene td_mexcit
|
||||
'''),
|
||||
('Wannier 90', '''
|
||||
w90iniprj w90prtunk
|
||||
'''),
|
||||
('Parallelisation', '''
|
||||
gwpara localrdwf ngroup_rf npband npfft
|
||||
npimage npkpt npspinor paral_kgb
|
||||
paral_rf use_gpu_cuda
|
||||
'''),
|
||||
('Unit cell', '''
|
||||
acell angdeg rprim ntypat znucl natom typat xred xcart
|
||||
'''),
|
||||
('Printing', '''
|
||||
prtvol enunit
|
||||
'''),
|
||||
('Files', '''
|
||||
irdddk irdden ird1den irdqps irdkss irdscr
|
||||
irdsuscep irdwfk irdwfq ird1wf getcell
|
||||
getddk getden getgam_eig2nkq getkss getocc
|
||||
getqps getscr getsuscep getvel getwfk
|
||||
getwfq getxcart getxred get1den get1wf
|
||||
'''),
|
||||
))
|
||||
|
||||
|
||||
# =========================================================================== #
|
||||
|
||||
|
||||
class VariableBlock(list):
|
||||
"""A block of abinit variables."""
|
||||
|
||||
def __init__(self, title, register=''):
|
||||
|
||||
# The block title
|
||||
self.title = title
|
||||
|
||||
# A register of all possible input variable.
|
||||
if isinstance(register, str):
|
||||
self.register = register.split()
|
||||
else:
|
||||
self.register = list(register)
|
||||
|
||||
def clear(self):
|
||||
del self[:]
|
||||
|
||||
def __str__(self):
|
||||
lines = ['#== {0} ==#'.format(self.title)]
|
||||
for variable in sorted(self):
|
||||
svar = str(variable)
|
||||
if svar:
|
||||
lines.append(svar)
|
||||
return '\n'.join(lines)
|
||||
|
||||
|
||||
# =========================================================================== #
|
||||
|
||||
|
||||
class InputFile(object):
|
||||
"""
|
||||
Abinit input file.
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
>> f = InputFile('myfile.in')
|
||||
>> f.read('otherfile.in')
|
||||
>>
|
||||
>> f.ndtset = 4 # Variables are set with integers,
|
||||
>> f.jdtset = [1,2,3,4] # or lists.
|
||||
>>
|
||||
>> f.ecut = 25. # Here is a floats.
|
||||
>> f.ecut = '25.' # But using strings is always possible.
|
||||
>>
|
||||
>> f.tolwfr = 1e-20 # Scientific notation
|
||||
>> f.tolwfr = '1d-20' # is translated like this.
|
||||
>>
|
||||
>> f.rprim = [[0.0,0.5,0.5], [0.5,0.0,0.5], [0.5,0.5,0.0]] # These three lines
|
||||
>> f.rprim = [ 0.0,0.5,0.5 , 0.5,0.0,0.5 , 0.5,0.5,0.0] # produce exactly
|
||||
>> f.rprim ='\\n 0.0 0.5 0.5 \\n 0.5 0.0 0.5 \\n 0.5 0.5 0.0' # the same result.
|
||||
>>
|
||||
>> f.nband4 = 300 # Dataset-specific variable.
|
||||
>>
|
||||
>> f.udtset = '2 3' # We will have to remember that:
|
||||
>> f.nband1__s = 100 # ':' <==> '__s' (start)
|
||||
>> f.nband1__i = 50 # '+' <==> '__i' (increment)
|
||||
>> f.ecut__a2 = 20.0 # '?' <==> '__a' (any)
|
||||
>>
|
||||
>> f.istwfk = '*1' # In some cases, string is the only way!
|
||||
>>
|
||||
>> f.tolvrs = None # Unset a variable. It won't appear in the file.
|
||||
>>
|
||||
>> f.fuzzy = 10 # But non-existent variables are written anyway!
|
||||
>>
|
||||
>> f.ecut = '100 eV' # This is OK but not recommended since variables
|
||||
>> f.ecut = [100, 'eV'] # are converted to default units when read from file.
|
||||
>>
|
||||
>> f.set_comment('''This is a comment.
|
||||
.. It will be printed at the top of the file.''')
|
||||
>>
|
||||
>> f.write()
|
||||
|
||||
See also the function :func:`~abipy.htc.InputFile.set_variables`.
|
||||
return self.variables
|
||||
"""
|
||||
|
||||
_blocks = _input_variable_blocks
|
||||
|
||||
def __init__(self, name='abinit.in'):
|
||||
|
||||
self.__dict__['name'] = str(name)
|
||||
self.__dict__['variables'] = dict()
|
||||
self.__dict__['_comment'] = str()
|
||||
self.__dict__['variables_blocks'] = list()
|
||||
|
||||
for (name, register) in _input_variable_blocks.items():
|
||||
self.variables_blocks.append(VariableBlock(name, register))
|
||||
self.variables_blocks.append(VariableBlock('Other'))
|
||||
|
||||
def __str__(self):
|
||||
lines = list()
|
||||
|
||||
# Comments
|
||||
if self.comment:
|
||||
lines.append(self.comment)
|
||||
lines.append('')
|
||||
|
||||
# Clear blocks
|
||||
for block in self.variables_blocks:
|
||||
block.clear()
|
||||
|
||||
# Sort variables in blocks
|
||||
for name, value in self.variables.items():
|
||||
variable = SpecialInputVariable(name, value)
|
||||
placed = False
|
||||
for block in self.variables_blocks:
|
||||
if variable.basename in block.register:
|
||||
block.append(variable)
|
||||
placed = True
|
||||
break
|
||||
if not placed:
|
||||
self.variables_blocks[-1].append(variable)
|
||||
|
||||
# Make the string
|
||||
for block in self.variables_blocks:
|
||||
if block:
|
||||
lines.append(str(block))
|
||||
lines.append('')
|
||||
block.clear()
|
||||
|
||||
return '\n'.join(lines)
|
||||
|
||||
def __setattr__(self, name, value):
|
||||
"""
|
||||
F.__setattr__('name', value) <==> F.name = value
|
||||
|
||||
Declare a variable in the internal dictionary.
|
||||
"""
|
||||
self.set_variable(name, value)
|
||||
|
||||
def set_name(self, name):
|
||||
"""Set the name of the file."""
|
||||
self.__dict__['name'] = name
|
||||
|
||||
@property
|
||||
def path(self):
|
||||
"""The absolute path."""
|
||||
return abspath(self.name)
|
||||
|
||||
@property
|
||||
def exists(self):
|
||||
"""True if self.path exists."""
|
||||
return os.path.exists(self.path)
|
||||
|
||||
@property
|
||||
def comment(self):
|
||||
return self._comment
|
||||
|
||||
def set_comment(self, comment):
|
||||
"""Set a comment to be included at the top of the file."""
|
||||
lines = [ '# ' + l.lstrip('#').strip() for l in comment.splitlines() ]
|
||||
self.__dict__['_comment'] = '\n'.join(lines)
|
||||
|
||||
def write(self, name=None):
|
||||
"""Write the inputs to the file."""
|
||||
if name is None:
|
||||
name = self.name
|
||||
|
||||
if not exists(dirname(self.name)):
|
||||
makedirs(dirname(self.name))
|
||||
|
||||
with open(name, 'w') as f:
|
||||
f.write(str(self))
|
||||
|
||||
def clear(self):
|
||||
"""Clear variables."""
|
||||
self.variables.clear()
|
||||
for block in self.variables_blocks:
|
||||
block.clear()
|
||||
|
||||
def read_string(self, bigstring):
|
||||
"""Initialize all variables from a string."""
|
||||
|
||||
# Split the big string into parts
|
||||
parts = list()
|
||||
for line in bigstring.splitlines():
|
||||
line = line.replace('=', ' ').split('#')[0].strip()
|
||||
parts.extend(line.split())
|
||||
|
||||
# Make a list of variable string declaration
|
||||
var_list, var_string = list(), ''
|
||||
for part in parts:
|
||||
if not part:
|
||||
continue
|
||||
if part[0].isalpha() and part not in _UNITS:
|
||||
if var_string:
|
||||
var_list.append(var_string)
|
||||
var_string = ''
|
||||
var_string += ' ' + part
|
||||
if var_string:
|
||||
var_list.append(var_string)
|
||||
|
||||
# Initialize all variables.
|
||||
for var_string in var_list:
|
||||
variable = SpecialInputVariable.from_str(var_string)
|
||||
self.variables[variable.name] = variable.get_value()
|
||||
|
||||
@classmethod
|
||||
def from_str(cls, bigstring):
|
||||
"""Initialize from a string."""
|
||||
inputfile = cls()
|
||||
inputfile.read_string(bigstring)
|
||||
|
||||
def read(self, file):
|
||||
"""
|
||||
Reads the content of an input file and store the variables in the
|
||||
internal dictionary with the proper type. Comments are thrown away.
|
||||
"""
|
||||
self.clear()
|
||||
with open(file, 'r') as f:
|
||||
self.read_string(f.read())
|
||||
|
||||
def set_variable(self, name, value):
|
||||
"""Set a single variable."""
|
||||
self.variables[name] = value
|
||||
|
||||
def set_variables(self, variables=None, dataset=0, **kwargs):
|
||||
"""
|
||||
Sets variables by providing a dictionary, or expanding a dictionary,
|
||||
and possibly append them by a dataset index.
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
>> kpoint_grid_shifted = {
|
||||
>> 'kptopt' : 1,
|
||||
>> 'ngkpt' : 3*[4],
|
||||
>> 'nshiftk' : 4,
|
||||
>> 'shiftk' : [[0.5,0.5,0.5],
|
||||
>> [0.5,0.0,0.0],
|
||||
>> [0.0,0.5,0.0],
|
||||
>> [0.0,0.0,0.5]],}
|
||||
>>
|
||||
>> kpoint_grid_unshifted = {
|
||||
>> 'kptopt' : 1,
|
||||
>> 'ngkpt' : 3*[4],
|
||||
>> 'nshiftk' : 1,
|
||||
>> 'shiftk' : [0,0,0],}
|
||||
>>
|
||||
>> cell = {
|
||||
>> 'ntypat' : 1
|
||||
>> 'znucl' : 6.0
|
||||
>> 'natom' : 2
|
||||
>> 'typat' : [1, 1]
|
||||
>> 'xred' : [[0,0,0],[0.25,0.25,0.25]]
|
||||
>> 'acell' : 3*[6.9]
|
||||
>> 'rprim' : [[0.0,0.5,0.5],
|
||||
>> [0.5,0.0,0.5],
|
||||
>> [0.5,0.5,0.0]]}
|
||||
>>
|
||||
>> f = InputFile()
|
||||
>> f.set_variables(ndtset=3, ecut=4.0, ecutsm=0.5)
|
||||
>>
|
||||
>> f.set_variables(cell) # These two lines
|
||||
>> f.set_variables(**cell) # are equivalent.
|
||||
>>
|
||||
>> # Here we append a dataset index at the end of all variables.
|
||||
>> f.set_variables(kpoint_grid_shifted, dataset=1)
|
||||
>> f.set_variables(kpoint_grid_unshifted, dataset=[2, 3])
|
||||
>>
|
||||
>> f.write('myfile.in') # The name was not set at initialization.
|
||||
"""
|
||||
if variables is None:
|
||||
variables = dict()
|
||||
variables.update(kwargs)
|
||||
|
||||
if not dataset:
|
||||
dataset = ['']
|
||||
|
||||
for ds in listify(dataset):
|
||||
for (key, val) in variables.items():
|
||||
newkey = key + str(ds)
|
||||
self.set_variable(newkey, val)
|
||||
|
||||
def get_variables(self):
|
||||
"""Return a dictionary of the variables."""
|
||||
return deepcopy(self.variables)
|
||||
|
||||
def get_variable(self, variable):
|
||||
"""Return the value of a variable, or None if it is not set."""
|
||||
return self.variables.get(variable)
|
|
@ -1,806 +0,0 @@
|
|||
from __future__ import print_function, division #, unicode_literals
|
||||
|
||||
from os import makedirs
|
||||
from os.path import basename, dirname, join, abspath, exists, realpath
|
||||
from .utils import listify
|
||||
|
||||
__all__ = [
|
||||
'JobFile',
|
||||
'PBSJobFile',
|
||||
'SGEJobFile',
|
||||
'SlurmJobFile',
|
||||
'MoabJobFile',
|
||||
]
|
||||
|
||||
# =========================================================================== #
|
||||
|
||||
|
||||
class JobFile(object):
|
||||
"""
|
||||
The job file is organized as follow::
|
||||
|
||||
#!/bin/csh # 1) Shell specification.
|
||||
#
|
||||
#PBS -N jobname # 2) Submission commands.
|
||||
#PBS -l walltime=48:00:00 # Depends on the subclass used.
|
||||
#PBS -l nodes=4:ppn=12
|
||||
|
||||
set MPIRUN="mpiexec" # 3) Declarations.
|
||||
set EXECUTABLE=/path/to/executable # These are also properties
|
||||
set INPUT=calculation.in # of the jobfile object.
|
||||
set LOG=calculation.log
|
||||
|
||||
module load intel-compilers # 4) Modules.
|
||||
module load MPI/Intel/mvapich2
|
||||
|
||||
cd ${PBS_O_WORKDIR} # 5) Lines before execution.
|
||||
limit stacksize unlimited
|
||||
|
||||
$MPIRUN $EXECUTABLE < $INPUT > $LOG # 6) Execution line.
|
||||
|
||||
echo "Job done!" # 7) Lines after execution.
|
||||
date
|
||||
|
||||
.. attributes:
|
||||
|
||||
shell:
|
||||
The shell binary to be used. E.g. '/bin/csh'.
|
||||
Default is '/bin/bash'.
|
||||
mpirun:
|
||||
The mpi runner. E.g. 'mpiexec -npernode 6'.
|
||||
Default is none.
|
||||
executable:
|
||||
The binary to be executed. Default is abinit.
|
||||
bindir:
|
||||
The directory in which to look for binaries. Default is none.
|
||||
input:
|
||||
The input file to feed in the executable as the standard input.
|
||||
Mandatory.
|
||||
log:
|
||||
The file into which the standard output is redirected.
|
||||
Default is 'log'.
|
||||
stderr:
|
||||
The file into which the standard error is redirected.
|
||||
Default is 'stderr'.
|
||||
modules:
|
||||
The modules which will be loaded with 'module load'.
|
||||
Default is none.
|
||||
lines_before:
|
||||
Lines before the main execution.
|
||||
Default is none.
|
||||
lines_after:
|
||||
Lines after the main execution.
|
||||
Default is none.
|
||||
other_lines:
|
||||
Other lines your job submission script would like to have.
|
||||
Must be preceded by the approbriate tag (#!, #PBS).
|
||||
Default is none.
|
||||
submission_command:
|
||||
The command which should be used to launch the job.
|
||||
E.g. 'qsub', 'bqsub', 'sbatch'.
|
||||
Default depends on the job type.
|
||||
"""
|
||||
_executable = 'abinit'
|
||||
_mpirun = ''
|
||||
_modules = list()
|
||||
_other_lines = list()
|
||||
_lines_before = list()
|
||||
_lines_after = list()
|
||||
|
||||
def __init__(self, name='job.sh', **kwargs):
|
||||
|
||||
# Name
|
||||
self.name = name
|
||||
self.absdir = realpath(dirname(self.name))
|
||||
self.absname = realpath(self.name)
|
||||
|
||||
# Shell
|
||||
self._shell = '/bin/bash'
|
||||
|
||||
# Execution lines
|
||||
self.input = ''
|
||||
self.log = 'log'
|
||||
self.stderr = 'stderr'
|
||||
self.executable = 'abinit'
|
||||
self.bindir = ''
|
||||
self.mpirun = ''
|
||||
|
||||
# Modules
|
||||
self.modules = list()
|
||||
|
||||
# Other lines
|
||||
self.other_lines = list()
|
||||
self.lines_before = list()
|
||||
self.lines_after = list()
|
||||
|
||||
# Command used to submit the job
|
||||
self.submission_command = 'qsub'
|
||||
|
||||
# Set attributes
|
||||
for (arg, val) in kwargs.items():
|
||||
try:
|
||||
getattr(self, 'set_' + arg)(val)
|
||||
except:
|
||||
pass
|
||||
|
||||
def __str__(self):
|
||||
lines = []
|
||||
def app(line):
|
||||
if '__iter__' in dir(line):
|
||||
lines.extend(line)
|
||||
else:
|
||||
lines.append(line)
|
||||
|
||||
# Shell line
|
||||
app('#!' + self.shell)
|
||||
app('')
|
||||
|
||||
# Submission instructions
|
||||
app(self._get_command_lines())
|
||||
|
||||
# Other submission inscrutions
|
||||
app(self.other_lines)
|
||||
app('')
|
||||
|
||||
# Declarations
|
||||
for (key, value) in [('MPIRUN', self.mpirun),
|
||||
('EXECUTABLE', self.executable),
|
||||
('INPUT', self.input),
|
||||
('LOG', self.log),
|
||||
('STDERR', self.stderr)]:
|
||||
app(self._declare(key, value))
|
||||
app('')
|
||||
|
||||
# Modules
|
||||
for module in self.modules:
|
||||
app('module load ' + module)
|
||||
app('')
|
||||
|
||||
# Lines before execution
|
||||
app(self.lines_before)
|
||||
app('')
|
||||
|
||||
# Execution lines
|
||||
if 'csh' in self.shell:
|
||||
execline = "($MPIRUN $EXECUTABLE < $INPUT > $LOG) >& $STDERR"
|
||||
else:
|
||||
execline = "$MPIRUN $EXECUTABLE < $INPUT > $LOG 2> $STDERR"
|
||||
app(execline)
|
||||
app('')
|
||||
|
||||
# Lines after execution
|
||||
app(self.lines_after)
|
||||
app('')
|
||||
|
||||
return "\n".join(lines)
|
||||
|
||||
def write(self, name=None):
|
||||
"""Write the file."""
|
||||
if name is None:
|
||||
name = self.name
|
||||
|
||||
if self.dirname and not exists(self.dirname):
|
||||
makedirs(self.dirname)
|
||||
|
||||
with open(name, 'w') as f:
|
||||
f.write(str(self))
|
||||
|
||||
def _get_command_lines(self):
|
||||
"""Return the lines specifying instructions for job submission."""
|
||||
return list()
|
||||
|
||||
def _declare(self, key, val):
|
||||
"""Return a lines setting a variable."""
|
||||
if 'csh' in self.shell:
|
||||
declare = 'set '
|
||||
else:
|
||||
declare = ''
|
||||
return declare + key + '=' + val
|
||||
|
||||
def _set_property(self, name, *args, **kwargs):
|
||||
"""Set a property through the corresponding set_ function."""
|
||||
return getattr(self, 'set_' + key)(*args, **kwargs)
|
||||
|
||||
@property
|
||||
def dirname(self):
|
||||
"""The directory containing the file."""
|
||||
return dirname(self.name)
|
||||
|
||||
@property
|
||||
def path(self):
|
||||
"""The path of the file."""
|
||||
return abspath(self.name)
|
||||
|
||||
@property
|
||||
def basename(self):
|
||||
"""The base name of the file."""
|
||||
return basename(self.name)
|
||||
|
||||
@classmethod
|
||||
def properties(cls):
|
||||
"""Return the list of properties with a set function."""
|
||||
funcs = filter(lambda s: s.startswith('set_'), dir(cls))
|
||||
return [ f.split('set_', 1)[-1] for f in funcs ]
|
||||
|
||||
@property
|
||||
def executable(self):
|
||||
return join(self.bindir, self._executable)
|
||||
|
||||
@executable.setter
|
||||
def executable(self, executable):
|
||||
self._executable = basename(executable)
|
||||
if basename(executable) != executable:
|
||||
self.set_bindir(dirname(executable))
|
||||
|
||||
@property
|
||||
def mpirun(self):
|
||||
return '"' + self._mpirun.strip('"').strip("'") + '"'
|
||||
|
||||
@mpirun.setter
|
||||
def mpirun(self, mpirun):
|
||||
self._mpirun = str(mpirun)
|
||||
|
||||
@property
|
||||
def shell(self):
|
||||
return self._shell
|
||||
|
||||
@shell.setter
|
||||
def shell(self, shell):
|
||||
if shell == basename(shell):
|
||||
self._shell = join('/bin', shell)
|
||||
else:
|
||||
self._shell = abspath(shell)
|
||||
|
||||
@property
|
||||
def modules(self):
|
||||
return self._modules
|
||||
|
||||
@modules.setter
|
||||
def modules(self, modules):
|
||||
self._modules = listify(modules)
|
||||
|
||||
@property
|
||||
def other_lines(self):
|
||||
return self._other_lines
|
||||
|
||||
@other_lines.setter
|
||||
def other_lines(self, lines):
|
||||
self._other_lines = listify(lines)
|
||||
|
||||
@property
|
||||
def lines_before(self):
|
||||
return self._lines_before
|
||||
|
||||
@lines_before.setter
|
||||
def lines_before(self, lines):
|
||||
self._lines_before = listify(lines)
|
||||
|
||||
@property
|
||||
def lines_after(self):
|
||||
return self._lines_after
|
||||
|
||||
@lines_after.setter
|
||||
def lines_after(self, lines):
|
||||
self._lines_after = listify(lines)
|
||||
|
||||
def set_shell(self, shell):
|
||||
"""
|
||||
Sets the shell type. The argument can either be an absolute path,
|
||||
or just the shell type e.g. bash, csh, tcsh, in which case
|
||||
the executable is assumed to be located in /bin/.
|
||||
The shell also determine how a variable is declared.
|
||||
"""
|
||||
self.shell = shell
|
||||
|
||||
def set_mpirun(self, mpirun):
|
||||
"""
|
||||
Set the mpi runner to execute the program.
|
||||
E.g. 'mpiexec -npernode 6', 'mpirun -np 12', ''.
|
||||
"""
|
||||
self.mpirun = mpirun
|
||||
|
||||
def set_bindir(self, bindir):
|
||||
"""Set the directory for binaries (abinit, mrgscr...)."""
|
||||
self.bindir = realpath(bindir)
|
||||
|
||||
def set_executable(self, executable):
|
||||
"""Set the executable to use."""
|
||||
self.executable = executable
|
||||
|
||||
def set_input(self, input):
|
||||
"""Set the input file for the main executable."""
|
||||
self.input = input
|
||||
|
||||
def set_log(self, log):
|
||||
"""Set the log file to collect standard output of the executable."""
|
||||
self.log = log
|
||||
|
||||
def set_stderr(self, stderr):
|
||||
"""Set the log file to collect standard output of the executable."""
|
||||
self.stderr = stderr
|
||||
|
||||
def set_modules(self, *modules):
|
||||
"""Set one or many modules to be loaded."""
|
||||
self.modules = modules
|
||||
|
||||
def set_other_lines(self, *lines):
|
||||
"""Set other command lines for the batch submission system."""
|
||||
self.other_lines = lines
|
||||
|
||||
def set_lines_before(self, *lines):
|
||||
"""Set one or many lines to be executed before the main execution."""
|
||||
self.lines_before = lines
|
||||
|
||||
def set_lines_after(self, *lines):
|
||||
"""Set one or many lines to be executed after the main execution."""
|
||||
self.lines_after = lines
|
||||
|
||||
def set_submission_command(self, command):
|
||||
"""
|
||||
Sets the command used for job submission,
|
||||
e.g. qsub, bqsub, sbatch, ...
|
||||
"""
|
||||
self.submission_command = command
|
||||
|
||||
# =========================================================================== #
|
||||
|
||||
|
||||
class PBSJobFile(JobFile):
|
||||
"""
|
||||
Portable Batch System.
|
||||
|
||||
.. attributes:
|
||||
|
||||
jobname:
|
||||
Name of the job.
|
||||
runtime:
|
||||
Maximum time for the job.
|
||||
nodes:
|
||||
Number of nodes on which to run the job.
|
||||
ppn:
|
||||
Number of processors per node.
|
||||
memory:
|
||||
Memory per node. E.g. '48G'.
|
||||
queue:
|
||||
The queue to which the job is submitted.
|
||||
mail:
|
||||
The mail to which a notification will be sent.
|
||||
mail_options:
|
||||
The conditions under which a mail will be sent.
|
||||
E.G. 'abe'.
|
||||
submission_command:
|
||||
default is 'qsub'.
|
||||
|
||||
See man qsub for more info.
|
||||
"""
|
||||
__doc__ += "\n" + JobFile.__doc__
|
||||
|
||||
_command = "#PBS "
|
||||
|
||||
def __init__(self, **kwargs):
|
||||
|
||||
kwargs.setdefault('submission_command', 'qsub')
|
||||
JobFile.__init__(self, **kwargs)
|
||||
|
||||
nodes = None
|
||||
def set_nodes(self, val):
|
||||
self.nodes = val
|
||||
|
||||
ppn = None
|
||||
def set_ppn(self, val):
|
||||
self.ppn = val
|
||||
|
||||
memory = None
|
||||
def set_memory(self, val):
|
||||
self.memory = val
|
||||
|
||||
runtime = None
|
||||
def set_runtime(self, val):
|
||||
"""Either set the numer of hours, or a triplet for (hours,min,sec)."""
|
||||
if isinstance(val, int):
|
||||
val = [val, 0, 0]
|
||||
self.runtime = val
|
||||
|
||||
jobname = None
|
||||
def set_jobname(self, val):
|
||||
self.jobname = val
|
||||
|
||||
queue = None
|
||||
def set_queue(self, val):
|
||||
self.queue = val
|
||||
|
||||
mail = None
|
||||
def set_mail(self, val):
|
||||
self.mail = val
|
||||
|
||||
mail_options = None
|
||||
def set_mail_options(self, val):
|
||||
self.mail_options = val
|
||||
|
||||
def _get_command_lines(self):
|
||||
"""Return the lines specifying instructions for job submission."""
|
||||
lines = list()
|
||||
def add(line):
|
||||
lines.append(self._command + line) # + '\n')
|
||||
|
||||
if self.jobname:
|
||||
add('-N ' + str(self.jobname))
|
||||
|
||||
if self.runtime:
|
||||
add('-l walltime={0}:{1}:{2}'.format(*self.runtime))
|
||||
|
||||
if self.nodes and self.ppn:
|
||||
add('-l nodes=' + str(self.nodes) + ':ppn=' + str(self.ppn))
|
||||
|
||||
if self.memory:
|
||||
add('-l mem=' + str(self.memory))
|
||||
|
||||
if self.queue:
|
||||
add('-q ' + self.queue)
|
||||
|
||||
if self.mail:
|
||||
add('-M ' + self.mail)
|
||||
|
||||
if self.mail_options:
|
||||
add('-m ' + self.mail_options)
|
||||
|
||||
return lines
|
||||
|
||||
# =========================================================================== #
|
||||
|
||||
|
||||
class SGEJobFile(JobFile):
|
||||
"""
|
||||
Sun Grid Engine.
|
||||
|
||||
.. attributes:
|
||||
|
||||
jobname:
|
||||
Name of the job.
|
||||
runtime:
|
||||
Maximum time for the job.
|
||||
nproc:
|
||||
Number of processors.
|
||||
queue:
|
||||
The queue to which the job is submitted.
|
||||
environment:
|
||||
The parallel environment under which the job is ran.
|
||||
memory:
|
||||
The requested memory, in M.
|
||||
mail:
|
||||
The mail to which a notification will be sent.
|
||||
mail_options:
|
||||
The conditions under which a mail will be sent.
|
||||
E.G. 'abe'.
|
||||
submission_command:
|
||||
default is 'qsub'.
|
||||
|
||||
See man qsub for more info.
|
||||
"""
|
||||
__doc__ += "\n" + JobFile.__doc__
|
||||
|
||||
_command = "#$ "
|
||||
|
||||
def __init__(self, **kwargs):
|
||||
|
||||
kwargs.setdefault('submission_command', 'qsub')
|
||||
JobFile.__init__(self, **kwargs)
|
||||
|
||||
jobname = None
|
||||
def set_jobname(self, val):
|
||||
self.jobname = val
|
||||
|
||||
runtime = None
|
||||
def set_runtime(self, val):
|
||||
"""Either set the numer of hours, or a triplet for (hours,min,sec)."""
|
||||
if isinstance(val, int):
|
||||
val = [val, 0, 0]
|
||||
self.runtime = val
|
||||
|
||||
nproc = None
|
||||
def set_nproc(self, val):
|
||||
self.nproc = val
|
||||
|
||||
queue = None
|
||||
def set_queue(self, val):
|
||||
self.queue = val
|
||||
|
||||
environment = None
|
||||
def set_environment(self, val):
|
||||
self.environment = val
|
||||
|
||||
memory = None
|
||||
def set_memory(self, val):
|
||||
self.memory = val
|
||||
|
||||
mail = None
|
||||
def set_mail(self, val):
|
||||
self.mail = val
|
||||
|
||||
mail_options = None
|
||||
def set_mail_options(self, val):
|
||||
self.mail_options = val
|
||||
|
||||
def _get_command_lines(self):
|
||||
"""Return the lines specifying instructions for job submission."""
|
||||
lines = list()
|
||||
def add(line):
|
||||
lines.append(self._command + line) # + '\n')
|
||||
|
||||
if self.jobname:
|
||||
add('-N ' + str(self.jobname))
|
||||
|
||||
if self.runtime:
|
||||
add('-l h_rt={0}:{1}:{2}'.format(*self.runtime))
|
||||
|
||||
if self.environment and self.nproc:
|
||||
line = '-pe ' + self.environment + ' ' + str(self.nproc)
|
||||
if self.memory:
|
||||
line += ' -l mem=' + str(self.memory)
|
||||
add(line)
|
||||
|
||||
if self.queue:
|
||||
add('-q ' + self.queue)
|
||||
|
||||
if self.mail:
|
||||
add('-M ' + self.mail)
|
||||
|
||||
if self.mail_options:
|
||||
add('-m ' + self.mail_options)
|
||||
|
||||
return lines
|
||||
|
||||
# =========================================================================== #
|
||||
|
||||
|
||||
class SlurmJobFile(JobFile):
|
||||
"""
|
||||
Simple Linux Utility for Resource Management.
|
||||
|
||||
.. Attributes:
|
||||
|
||||
jobname:
|
||||
Name of the job.
|
||||
time:
|
||||
Maximum time for the job.
|
||||
ntasks:
|
||||
The number of processes.
|
||||
cpus_per_task:
|
||||
The number of cpus per process.
|
||||
mem_per_cpu:
|
||||
The memory per cpu.
|
||||
partition:
|
||||
The partition...
|
||||
mail_user:
|
||||
The mail to which a notification is sent.
|
||||
mail_type:
|
||||
The conditions unde which to send a mail.
|
||||
submission_command:
|
||||
default is 'sbatch'.
|
||||
"""
|
||||
__doc__ += "\n" + JobFile.__doc__
|
||||
|
||||
_command = "#SBATCH "
|
||||
|
||||
def __init__(self, **kwargs):
|
||||
|
||||
kwargs.setdefault('submission_command', 'sbatch')
|
||||
JobFile.__init__(self, **kwargs)
|
||||
|
||||
jobname = None
|
||||
def set_jobname(self, val):
|
||||
self.jobname = val
|
||||
|
||||
time = None
|
||||
def set_time(self, val):
|
||||
"""Either set the number of hours, or a triplet for (hours,min,sec)."""
|
||||
if isinstance(val, int):
|
||||
val = [val, 0, 0]
|
||||
self.time = val
|
||||
|
||||
def set_runtime(self, val):
|
||||
self.set_time(val)
|
||||
|
||||
ntasks = None
|
||||
def set_ntasks(self, val):
|
||||
self.ntasks = val
|
||||
|
||||
ntasks_per_node = None
|
||||
def set_ntasks_per_node(self, val):
|
||||
self.ntasks_per_node = val
|
||||
|
||||
cpus_per_task = None
|
||||
def set_cpus_per_task(self, val):
|
||||
self.cpus_per_task = val
|
||||
|
||||
mem_per_cpu = None
|
||||
def set_mem_per_cpu(self, val):
|
||||
self.mem_per_cpu = val
|
||||
|
||||
partition = None
|
||||
def set_partition(self, val):
|
||||
self.partition = val
|
||||
|
||||
mail_user = None
|
||||
def set_mail_user(self, val):
|
||||
self.mail_user = val
|
||||
|
||||
mail_type = None
|
||||
def set_mail_type(self, val):
|
||||
self.mail_type = val
|
||||
|
||||
def _get_command_lines(self):
|
||||
"""Return the lines specifying instructions for job submission."""
|
||||
lines = list()
|
||||
def add(line):
|
||||
lines.append(self._command + line) # + '\n')
|
||||
|
||||
if self.jobname:
|
||||
add('--job-name=' + str(self.jobname))
|
||||
|
||||
if self.time:
|
||||
add('--time={0}:{1}:{2}\n'.format(*self.time))
|
||||
|
||||
if self.ntasks:
|
||||
add('--ntasks=' + str(self.ntasks))
|
||||
|
||||
if self.partition:
|
||||
add('--partition=' + self.partition)
|
||||
|
||||
if self.ntasks_per_node:
|
||||
add('--ntasks-per-node=' + str(self.ntasks_per_node))
|
||||
|
||||
if self.cpus_per_task:
|
||||
add('--cpus-per-task=' + str(self.cpus_per_task))
|
||||
|
||||
if self.mem_per_cpu:
|
||||
add('--mem-per-cpu=' + str(self.mem_per_cpu))
|
||||
|
||||
if self.mail_user:
|
||||
add('--mail-user=' + self.mail_user)
|
||||
|
||||
if self.mail_type:
|
||||
add('--mail-type=' + self.mail_type)
|
||||
|
||||
return lines
|
||||
|
||||
# =========================================================================== #
|
||||
|
||||
|
||||
class MoabJobFile(JobFile):
|
||||
"""
|
||||
Moab Workload Manager
|
||||
|
||||
.. Attributes:
|
||||
start_after:
|
||||
Declares the time after which the job is eligible for execution.
|
||||
Syntax: (brackets delimit optional items with the default being
|
||||
current date/time): [CC][YY][MM][DD]hhmm[.SS]
|
||||
account:
|
||||
Defines the account associated with the job.
|
||||
hold:
|
||||
Put a user hold on the job at submission time.
|
||||
combine:
|
||||
Combine stdout and stderr into the same output file.
|
||||
resources:
|
||||
Defines the resources that are required by the job.
|
||||
mail:
|
||||
Defines the set of conditions (a=abort,b=begin,e=end) when the
|
||||
server will send a mail message about the job to the user.
|
||||
jobname:
|
||||
Gives a user specified name to the job.
|
||||
priority:
|
||||
Assigns a user priority value to a job.
|
||||
queue:
|
||||
Run the job in the specified queue (pdebug, pbatch, etc.). A host
|
||||
may also be specified if it is not the local host.
|
||||
rerun:
|
||||
Automatically rerun the job is there is a system failure.
|
||||
env:
|
||||
Specifically adds a list of environment variables that are exported
|
||||
to the job.
|
||||
allenv:
|
||||
Declares that all environment variables in the msub environment are
|
||||
exported to the batch job.
|
||||
"""
|
||||
__doc__ += "\n" + JobFile.__doc__
|
||||
|
||||
_command = "#MSUB "
|
||||
|
||||
def __init__(self, **kwargs):
|
||||
|
||||
kwargs.setdefault('submission_command', 'srun')
|
||||
JobFile.__init__(self, **kwargs)
|
||||
|
||||
start_after = None
|
||||
def set_start_after(self, val):
|
||||
self.start_after = val
|
||||
|
||||
account = None
|
||||
def set_account(self, val):
|
||||
self.account = val
|
||||
|
||||
hold = None
|
||||
def set_hold(self, val):
|
||||
self.hold = val
|
||||
|
||||
combine = None
|
||||
def set_combine(self, val):
|
||||
self.combine = val
|
||||
|
||||
resources = dict()
|
||||
def set_resources(self, val):
|
||||
self.resources = val
|
||||
|
||||
mail = None
|
||||
def set_mail(self, val):
|
||||
self.mail = val
|
||||
|
||||
jobname = None
|
||||
def set_jobname(self, val):
|
||||
self.jobname = val
|
||||
|
||||
priority = None
|
||||
def set_priority(self, val):
|
||||
self.priority = val
|
||||
|
||||
queue = None
|
||||
def set_queue(self, val):
|
||||
self.queue = val
|
||||
|
||||
rerun = None
|
||||
def set_rerun(self, val):
|
||||
self.rerun = val
|
||||
|
||||
env = None
|
||||
def set_env(self, val):
|
||||
self.env = val
|
||||
|
||||
allenv = None
|
||||
def set_allenv(self, val):
|
||||
self.allenv = val
|
||||
|
||||
def _get_command_lines(self):
|
||||
"""Return the lines specifying instructions for job submission."""
|
||||
lines = list()
|
||||
def add(line):
|
||||
lines.append(self._command + line) # + '\n')
|
||||
|
||||
if self.start_after:
|
||||
add('-a ' + self.start_after)
|
||||
|
||||
if self.account:
|
||||
add('-A ' + self.account)
|
||||
|
||||
if self.hold is True:
|
||||
add('-h ')
|
||||
|
||||
if self.combine is True:
|
||||
add('-j oe')
|
||||
|
||||
if self.resources:
|
||||
for (arg, val) in self.resources.items():
|
||||
add('-l ' + arg + '=' + val)
|
||||
|
||||
if self.mail:
|
||||
add('-m ' + self.mail)
|
||||
|
||||
if self.jobname:
|
||||
add('-N ' + self.jobname)
|
||||
|
||||
if self.priority:
|
||||
add('-p ' + self.priority)
|
||||
|
||||
if self.queue:
|
||||
add('-q ' + self.queue)
|
||||
|
||||
if self.rerun is True:
|
||||
add('-r y')
|
||||
|
||||
if self.env:
|
||||
add('-v ' + ','.join(self.env))
|
||||
|
||||
if self.allenv is True:
|
||||
add('-V')
|
||||
|
||||
return lines
|
|
@ -1,914 +0,0 @@
|
|||
from __future__ import print_function, division #, unicode_literals
|
||||
|
||||
import sys
|
||||
import os
|
||||
import warnings
|
||||
import subprocess
|
||||
import numpy as np
|
||||
|
||||
from os.path import basename, dirname, join, relpath, abspath
|
||||
from argparse import ArgumentParser, RawDescriptionHelpFormatter
|
||||
from collections import OrderedDict
|
||||
from copy import deepcopy
|
||||
|
||||
from abipy.core import release, Structure, Density
|
||||
from .utils import parse_ewc
|
||||
from .abinitinput import AbinitInput
|
||||
|
||||
|
||||
__all__ = [
|
||||
'Launcher',
|
||||
'MassLauncher',
|
||||
]
|
||||
|
||||
# =========================================================================== #
|
||||
|
||||
class LauncherArgParser(ArgumentParser):
|
||||
"""
|
||||
Base parser used in Launcher to parse the arguments provided by the user
|
||||
when the function 'execute' is called. The parser consists of a top level
|
||||
parser responsible for parsing global options such as verbosity level,
|
||||
version, etc, and sub-parsers for the different sub-commands.
|
||||
|
||||
Every class that inherits from Launcher, MassLauncher should define
|
||||
a parser that inherits from this base class, and use register_subparser
|
||||
to extend or customize the subparsers. The list of commands supported
|
||||
by the instance is stored in self.commands so that we know whether
|
||||
a particular sub-parser can handle the argument or if it should delegate
|
||||
the superclass.
|
||||
"""
|
||||
def __init__(self, *args, **kwargs):
|
||||
|
||||
#formatter_class=RawDescriptionHelpFormatter)
|
||||
ArgumentParser.__init__(self, *args, **kwargs)
|
||||
|
||||
# Create the top-level parse responsible for parsing global options
|
||||
# such as verbosity level, version ....
|
||||
# NOTE: except for verbose and version, any other option for the top
|
||||
# level parser should be stored in a variable named --top-optname
|
||||
# so that we don't pollute the namespace of the subcommands
|
||||
|
||||
self.add_argument('-v', '--verbose', default=0, action='count', # -vv --> verbose=2
|
||||
help='verbose, can be supplied multiple times to increase verbosity')
|
||||
|
||||
self.add_argument('--version', action='version', version="abipy " + release.version)
|
||||
|
||||
# Create the parser for the sub-commands
|
||||
self.subparsers = self.add_subparsers(dest='command', help='sub-command help')
|
||||
|
||||
# Workaround. see http://stackoverflow.com/questions/8757338/sub-classing-the-argparse-argument-parser
|
||||
# I know I shouldn't do this, but I don't want to wrap the parser in the object.
|
||||
self.subparsers._parser_class = ArgumentParser
|
||||
|
||||
p_make = self.subparsers.add_parser('make', help='Make files and directories')
|
||||
p_make.add_argument('-f', '--force', action='store_true', default=False, help='Force file creation')
|
||||
self.register_subparser("make", p_make)
|
||||
|
||||
p_submit = self.subparsers.add_parser('submit', help='Submit the calculation to a batch server.')
|
||||
#p_submit.add_argument('bar', type=int, help='bar help')
|
||||
self.register_subparser("submit", p_submit)
|
||||
|
||||
p_run = self.subparsers.add_parser('run', help='Run the calculation from the shell.')
|
||||
#p_run.add_argument('-n', '--py-nthreads', metavar='NUM', type=int, default=1, help='Number of jobs.')
|
||||
self.register_subparser("run", p_run)
|
||||
|
||||
p_report = self.subparsers.add_parser('report', help='Tell if the calculation completed or not.')
|
||||
#p_report.add_argument('bar', type=int, help='bar help')
|
||||
self.register_subparser("report", p_report)
|
||||
|
||||
p_inspect = self.subparsers.add_parser('inspect', help='Inspect files using EDITOR.')
|
||||
p_inspect.add_argument('what_inspect', metavar='character(s)', default = "o",
|
||||
help='Files to inspect: i for the input, o for the output, l for log, j for the job file. f for files file\n' +
|
||||
'Characters can be concatenated. Use "ol", for example, to inspect both the output and the log file.')
|
||||
self.register_subparser("inspect", p_inspect)
|
||||
|
||||
p_clean = self.subparsers.add_parser('clean', help='Remove log, data files and temporary files.')
|
||||
#p_clean.add_argument('-f', '--force', action='store_true', help='Force')
|
||||
self.register_subparser("clean", p_clean)
|
||||
|
||||
p_destroy = self.subparsers.add_parser('destroy', help='Remove all files, including input and outputs.')
|
||||
p_destroy.add_argument('-f', '--force', action='store_true', help='Force')
|
||||
self.register_subparser("destroy", p_destroy)
|
||||
|
||||
p_show = self.subparsers.add_parser('show', help='Print the calculation name and return True.')
|
||||
#p_show.add_argument('-f', '--force', action='store_true', help='Force')
|
||||
self.register_subparser("show", p_show)
|
||||
|
||||
p_visu = self.subparsers.add_parser('visualize', #aliases=['visu'],
|
||||
help='Visualize data with an external program e.g. Xcrysden.')
|
||||
p_visu.add_argument('what_visualize', metavar='STRING', default = "crystal", help=' Type of data to visualize')
|
||||
|
||||
self.register_subparser("visualize", p_visu)
|
||||
|
||||
def myparse_args(self, args=None, namespace=None):
|
||||
"""
|
||||
Wrap the parse_args method of ArgumentParsers
|
||||
:return: options, args, kwargs
|
||||
|
||||
where options is the default output of parse_args and kwargs is a dictionary option_name -> value
|
||||
"""
|
||||
if args is None:
|
||||
self.parse_args(args=["--help"], namespace=namespace)
|
||||
|
||||
if '__iter__' not in dir(args): args = [args]
|
||||
|
||||
# Call the "true" parse_args
|
||||
options = self.parse_args(args=args, namespace=namespace)
|
||||
|
||||
args = list()
|
||||
kwargs = deepcopy(vars(options))
|
||||
|
||||
return options, args, kwargs
|
||||
|
||||
@property
|
||||
def commands(self):
|
||||
"The commands registered in the parser"
|
||||
return self._cmd2subparser.keys()
|
||||
|
||||
def can_handle(self, command):
|
||||
"True if the parser can handle command"
|
||||
return command in self.commands
|
||||
|
||||
def iter_cmdsubparser(self):
|
||||
"Iterate over (command_string, subparser)"
|
||||
for tup in self._cmd2subparser.items(): yield tup
|
||||
|
||||
def register_subparser(self, command, subparser, solve_conflict=False):
|
||||
"""
|
||||
Register the subparser associate to a given command.
|
||||
|
||||
:arg solve_conflict: By defaut it's not possible to override an existent subparser associated
|
||||
to the same command. Use solve_conflict if subparser should replace the old one.
|
||||
"""
|
||||
if not hasattr(self, "_cmd2subparser"): self._cmd2subparser = OrderedDict()
|
||||
if command in self._cmd2subparser and not solve_conflict:
|
||||
raise ValueError("Cannot overwrite subparser for command %s. Use solve_conflict=True" % command)
|
||||
self._cmd2subparser[command] = subparser
|
||||
|
||||
def unregister_subparser(self, command):
|
||||
"""Unregister the subparser associated to the given command. Return the subparser removed"""
|
||||
return self._cmd2subparser.pop(command)
|
||||
|
||||
|
||||
# =========================================================================== #
|
||||
|
||||
class LauncherError(Exception):
|
||||
"""base class for the exceptions raised by Launcher."""
|
||||
|
||||
class Launcher(AbinitInput):
|
||||
"""
|
||||
A more powerful version of :class:`~abipy.htc.AbinitInput`.
|
||||
Allows to run a calculation, either from a script or from command line.
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
>> calc = Launcher('Mycalc/root',
|
||||
.. jobtype=None,
|
||||
.. pseudodir='/path/to/pseudos/',
|
||||
.. pseudos=['14si.pspnc'],
|
||||
.. bindir='/path/to/binaries/')
|
||||
>>
|
||||
>> calc.read('myinput.in')
|
||||
>>
|
||||
>> # Write the files.
|
||||
>> calc.make()
|
||||
>>
|
||||
>> # Run the calculation with abinit.
|
||||
>> calc.run()
|
||||
>>
|
||||
>> # Inquire about the calculation status.
|
||||
>> status = calc.report()
|
||||
>>
|
||||
>> if status == 'Completed':
|
||||
.. # Remove log and data files.
|
||||
.. calc.clean(force=True)
|
||||
|
||||
You can perform all these actions from the command line, using the function 'execute'.
|
||||
"""
|
||||
Error = LauncherError
|
||||
|
||||
# Parser class and instance are stored as class attributes.
|
||||
ArgParser = LauncherArgParser
|
||||
argparser = LauncherArgParser()
|
||||
|
||||
def output_files(self):
|
||||
"""Return all output files produced, in alphabetical order."""
|
||||
base = self.output_name
|
||||
files = [base] + [ base + a for a in 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' ]
|
||||
return filter(os.path.exists, files)
|
||||
|
||||
def last_output(self):
|
||||
"""Return the last output file produced."""
|
||||
files = self.output_files()
|
||||
if not files:
|
||||
return None
|
||||
return files[-1]
|
||||
|
||||
def idat_files(self):
|
||||
"""Return all the input data files."""
|
||||
files = list()
|
||||
for file in os.listdir(dirname(self.idat_root)):
|
||||
if file.startswith(basename(self.idat_root)):
|
||||
files.append(join(dirname(self.idat_root), file))
|
||||
return files
|
||||
|
||||
def odat_files(self):
|
||||
"""Return all the output data files produced."""
|
||||
files = list()
|
||||
for file in os.listdir(dirname(self.odat_root)):
|
||||
if file.startswith(basename(self.odat_root)):
|
||||
files.append(join(dirname(self.odat_root), file))
|
||||
return files
|
||||
|
||||
def tmp_files(self):
|
||||
"""Return all the input data files produced."""
|
||||
files = list()
|
||||
for file in os.listdir(dirname(self.tmp_root)):
|
||||
if file.startswith(basename(self.tmp_root)):
|
||||
files.append(join(dirname(self.tmp_root), file))
|
||||
return files
|
||||
|
||||
def junk_files(self):
|
||||
"""Return all the junk files produced."""
|
||||
files = list()
|
||||
for file in os.listdir(self.jobfile.dirname):
|
||||
|
||||
if (file.startswith('fort.') or
|
||||
file.endswith('.dat') or
|
||||
file in ('_GWDIAG',)):
|
||||
|
||||
files.append(join(self.jobfile.dirname, file))
|
||||
|
||||
return files
|
||||
|
||||
def read_mainlog_ewc(self):
|
||||
"""
|
||||
Read errors, warnings and comments from the main output and the log file.
|
||||
|
||||
:return: Two namedtuple instances: main, log.
|
||||
The lists of strings with the corresponding messages are
|
||||
available in main.errors, main.warnings, main.comments, log.errors etc.
|
||||
"""
|
||||
from pymatgen.io.abinit.events import EventsParser
|
||||
parser = EventsParser()
|
||||
main_events = parser.parse(self.last_output())
|
||||
log_events = parser.parse(self.log_name)
|
||||
|
||||
return main_events, log_events
|
||||
|
||||
def make(self, *args, **kwargs):
|
||||
"""
|
||||
Write the files.
|
||||
|
||||
Keyword arguments:
|
||||
verbose: (0)
|
||||
Print message if verbose is not zero.
|
||||
"""
|
||||
if kwargs.get('verbose'):
|
||||
print('Writing ' + self.name)
|
||||
|
||||
self.write(*args, **kwargs)
|
||||
|
||||
def run(self, *args, **kwargs):
|
||||
"""
|
||||
Run the calculation by executing the job file from the shell.
|
||||
|
||||
Keyword arguments:
|
||||
verbose: (0)
|
||||
Print message if verbose is not zero.
|
||||
|
||||
.. warning::
|
||||
This method must be thread safe since we may want to run several indipendent
|
||||
calculations with different python threads.
|
||||
"""
|
||||
if kwargs.get('verbose'):
|
||||
print('Running ' + self.name + '\n')
|
||||
|
||||
subprocess.call((self.jobfile.shell, self.jobfile.absname))
|
||||
|
||||
def submit(self, *args, **kwargs):
|
||||
"""
|
||||
Submit the calculation to a batch server.
|
||||
|
||||
Keyword arguments:
|
||||
verbose: (0)
|
||||
Print message if verbose is not zero.
|
||||
|
||||
"""
|
||||
if kwargs.get('verbose'):
|
||||
print('Submitting ' + self.name)
|
||||
|
||||
curdir = abspath(os.curdir)
|
||||
os.chdir(self.jobfile.absdir)
|
||||
subprocess.call((self.jobfile.submission_command, self.jobfile.basename))
|
||||
os.chdir(curdir)
|
||||
|
||||
def clean(self, *args, **kwargs):
|
||||
"""
|
||||
Remove log file, data files and tmp files.
|
||||
|
||||
Keyword arguments:
|
||||
force: (False)
|
||||
Do not ask confirmation.
|
||||
verbose: (0)
|
||||
Print message if verbose is not zero.
|
||||
"""
|
||||
if kwargs.get('verbose'):
|
||||
print('Cleaning ' + self.name)
|
||||
|
||||
destroy = [self.log_name]
|
||||
for files in (self.odat_files(), self.tmp_files(), self.junk_files()):
|
||||
destroy.extend(files)
|
||||
|
||||
if destroy:
|
||||
self._remove_files(destroy, kwargs.get('force', False))
|
||||
|
||||
def destroy(self, *args, **kwargs):
|
||||
"""
|
||||
Remove all calculation files and directories, if empty.
|
||||
|
||||
Keyword arguments:
|
||||
force: (False)
|
||||
Do not ask confirmation.
|
||||
verbose: (0)
|
||||
Print message if verbose is not zero.
|
||||
|
||||
"""
|
||||
if kwargs.get('verbose'):
|
||||
print('Destroying ' + self.name)
|
||||
|
||||
destroy = [self.input_name, self.log_name, self.job_name, self.files_name]
|
||||
|
||||
for files in (self.output_files(), self.idat_files(),
|
||||
self.odat_files(), self.tmp_files(), self.junk_files()):
|
||||
destroy.extend(files)
|
||||
|
||||
if destroy:
|
||||
self._remove_files(destroy, kwargs.get('force', False))
|
||||
self._remove_directory_tree(self.dirname)
|
||||
|
||||
def show(self, form=str, *args, **kwargs):
|
||||
"""Print the calculation name and return True."""
|
||||
print(form(self.name))
|
||||
return True
|
||||
|
||||
def inspect(self, *args, **kwargs):
|
||||
"""
|
||||
Inspect the input/(last) output/ log produced by the run.
|
||||
|
||||
:arg what_inspect:
|
||||
"i" for the input, "o" for the output, "l" for log, "j" for the job file. "f" for the files file
|
||||
characters can be concatenated. what="ol", for example, will inspect both the output and the log file.
|
||||
|
||||
The environment variable EDITOR defines the application to use (default vi).
|
||||
"""
|
||||
from ..tools import Editor
|
||||
editor = Editor()
|
||||
|
||||
what_inspect = kwargs.get("what_inspect", "o")
|
||||
|
||||
filenames = []
|
||||
if "i" in what_inspect: filenames.append(self.input_name)
|
||||
if "o" in what_inspect: filenames.append(self.last_output())
|
||||
if "l" in what_inspect: filenames.append(self.log_name)
|
||||
if "j" in what_inspect: filenames.append(self.job_name)
|
||||
if "f" in what_inspect: filenames.append(self.files_name)
|
||||
editor.edit_files(filenames, ask_for_exit=True)
|
||||
|
||||
def visualize(self, *args, **kwargs):
|
||||
# TODO Here I have to decide if this method should be defined
|
||||
# in a subclass of Launcher e.g GSLauncher or in the base class
|
||||
from .utils import find_file
|
||||
what_visualize = kwargs.get("what_visualize", "crystal")
|
||||
|
||||
visualizer = "xcrysden"
|
||||
#visualizer = abipy_env.get_uservar("visualizer", kwargs)
|
||||
|
||||
# Find the correct output file
|
||||
out_files = self.odat_files()
|
||||
gsfname = find_file(out_files, "GSR")
|
||||
|
||||
if gsfname is None:
|
||||
raise RuntimeError("Cannot find GSR file among %s" % out_files)
|
||||
|
||||
if what_visualize == "crystal":
|
||||
|
||||
structure = Structure.from_file(gsfname)
|
||||
structure.visualize(visualizer)()
|
||||
|
||||
elif what_visualize == "density":
|
||||
raise NotImplementedError("den_fname?")
|
||||
|
||||
density = Density.from_file(den_fname)
|
||||
density.visualize(visualizer)()
|
||||
|
||||
elif what_visualize in ["electrons", "fermisurface",]:
|
||||
from ..electrons import ElectronBands
|
||||
energies = ElectronBands.from_file(gsfname)
|
||||
|
||||
if what_visualize == "electrons": energies.plot()
|
||||
if what_visualize == "fermisurface":
|
||||
raise RuntimeError("No hanlder found for fermisurface")
|
||||
#visu = energies.visualize(self, visualizer, structure)
|
||||
#visu()
|
||||
else:
|
||||
raise RuntimeError("No handler found for %s" % what_visualize)
|
||||
|
||||
# TODO
|
||||
#def __str__(self):
|
||||
# string = ""
|
||||
# return string
|
||||
|
||||
def report(self, *args, **kwargs):
|
||||
"""
|
||||
Print information on the calculation status and return a status.
|
||||
|
||||
Keyword arguments:
|
||||
verbose: (0)
|
||||
0 : do not print anything
|
||||
> 0 : print status
|
||||
> 1 : print number of errors, warnings and comments
|
||||
|
||||
"""
|
||||
output = self.last_output()
|
||||
|
||||
from ..tools import StringColorizer
|
||||
str_colorizer = StringColorizer(sys.stdout)
|
||||
|
||||
status2txtcolor = {
|
||||
"Completed" : lambda string : str_colorizer(string, "green"),
|
||||
"Unfinished" : lambda string : str_colorizer(string, "blue"),
|
||||
"Unstarted" : lambda string : str_colorizer(string, "cyan"),
|
||||
}
|
||||
|
||||
def color(status): return status2txtcolor[status](status)
|
||||
|
||||
verbose = kwargs.get('verbose', 0)
|
||||
|
||||
if output and self._iscomplete(output):
|
||||
status = 'Completed'
|
||||
msg = relpath(output) + ' : ' + color(status)
|
||||
|
||||
if verbose:
|
||||
|
||||
# Does not work!
|
||||
pass
|
||||
|
||||
## Read the number of errors, warnings and comments
|
||||
##for the (last) main output and the log file.
|
||||
#main, log = self.read_mainlog_ewc()
|
||||
|
||||
#main_info = main.tostream(sys.stdout)
|
||||
#log_info = log.tostream(sys.stdout)
|
||||
|
||||
#msg += "\n " + "\n ".join([main_info, log_info])
|
||||
|
||||
elif os.path.exists(self.log_name):
|
||||
status = 'Unfinished'
|
||||
msg = self.name + ' : ' + color(status)
|
||||
|
||||
else:
|
||||
status = 'Unstarted'
|
||||
msg = self.name + ' : ' + color(status)
|
||||
|
||||
if verbose:
|
||||
print(msg)
|
||||
if status == 'Completed':
|
||||
pass
|
||||
# Does not work!
|
||||
#for w in main.warnings: print(w)
|
||||
#if verbose > 1:
|
||||
# for w in log.warnings: print(w)
|
||||
|
||||
return status
|
||||
|
||||
def execute(self, *args, **kwargs):
|
||||
"""
|
||||
Execute an action from the command line.
|
||||
|
||||
* make -- Write the files.
|
||||
* submit -- Submit the calculation to a batch server.
|
||||
* run -- Run the calculation from the shell.
|
||||
* report -- Tell if the calculation completed or not.
|
||||
* inspect -- Open files in EDITOR
|
||||
* clean -- Remove log, data files and temporary files.
|
||||
* show -- Signify that the calculation exists.
|
||||
* destroy -- Remove all files, including input and outputs.
|
||||
* visualize -- Visualize data.
|
||||
|
||||
Suppose this is the content of 'myscript.py':
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
> # myscript.py
|
||||
calc = Launcher('Mycalc/root', jobtype=None,
|
||||
pseudodir='Data', pseudos=['14si.pspnc'],
|
||||
executable='abinit')
|
||||
calc.read('myinput.in')
|
||||
calc.execute()
|
||||
|
||||
Then, from the shell, one can perform the following::
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
> # bash
|
||||
> python myscript.py make
|
||||
Writing Mycalc/root
|
||||
>
|
||||
> python myscript.py show
|
||||
Mycalc/root
|
||||
>
|
||||
> python myscript.py run
|
||||
Running Mycalc/root
|
||||
>
|
||||
> python myscript.py report
|
||||
Mycalc/root.out : Completed
|
||||
>
|
||||
> python myscript.py clean
|
||||
Cleaning Mycalc/root
|
||||
|
||||
Typically, one would use the command 'submit' instead of 'run'.
|
||||
"""
|
||||
if not args:
|
||||
args = sys.argv[1:]
|
||||
|
||||
options, args, kwargs = self.argparser.myparse_args(args)
|
||||
|
||||
if self.argparser.can_handle(options.command):
|
||||
getattr(self, options.command)(*args, **kwargs)
|
||||
|
||||
else:
|
||||
raise RuntimeError("Don't know how to handle command %s. This should not happen!" % options.command)
|
||||
|
||||
@staticmethod
|
||||
def _remove_files(files, force=False):
|
||||
"""Remove a list of file, asking confirmation."""
|
||||
|
||||
files = filter(os.path.exists, files)
|
||||
|
||||
if files and not force:
|
||||
|
||||
print("About to remove the following files:")
|
||||
for file in files:
|
||||
print(file)
|
||||
|
||||
proceed = raw_input("Do you want to proceed? (y/n) ")
|
||||
if not proceed.lower().startswith('y'):
|
||||
return
|
||||
|
||||
for file in files:
|
||||
try:
|
||||
os.remove(file)
|
||||
except Exception as exc:
|
||||
warnings.warn(str(exc))
|
||||
|
||||
@staticmethod
|
||||
def _remove_directory_tree(topdir):
|
||||
"""Remove a directory hierarchy, if it contains no files."""
|
||||
dirs = [topdir]
|
||||
for d in dirs:
|
||||
for f in os.listdir(d):
|
||||
sub = os.path.join(d, f)
|
||||
if os.path.isdir(sub):
|
||||
dirs.append(sub)
|
||||
else:
|
||||
return
|
||||
|
||||
for d in reversed(dirs):
|
||||
try:
|
||||
os.rmdir(d)
|
||||
except OSError:
|
||||
warnings.warn("Directory tree partially removed: " + topdir)
|
||||
|
||||
@staticmethod
|
||||
def _iscomplete(output_file):
|
||||
"Return True if an abinit output file is complete."
|
||||
with open(output_file, 'r') as f:
|
||||
lines = f.readlines()
|
||||
lines.reverse()
|
||||
|
||||
for i in range(10):
|
||||
try:
|
||||
line = lines[i]
|
||||
except:
|
||||
return False
|
||||
|
||||
if 'Calculation completed.' in line:
|
||||
return True
|
||||
return False
|
||||
|
||||
# Does not work !
|
||||
#from pymatgen.io.abinit.utils import abinit_output_iscomplete
|
||||
#return abinit_output_iscomplete(output_file)
|
||||
|
||||
# =========================================================================== #
|
||||
|
||||
class MassLauncherArgParser(LauncherArgParser):
|
||||
"""
|
||||
top level parser and subparsers used by MassLauncher.
|
||||
Handle all the options of Launcher and add the option -c to select the calculations.
|
||||
"""
|
||||
def __init__(self, *args, **kwargs):
|
||||
|
||||
LauncherArgParser.__init__(self, *args, **kwargs)
|
||||
|
||||
for (cmd, subparser) in self.iter_cmdsubparser():
|
||||
# Add new option
|
||||
subparser.add_argument('-c', '--calc', dest='only', nargs='*', type=str,
|
||||
help="Execute command only for the selected calculations.")
|
||||
|
||||
# Add py_nthreads arg to the commands that support threads.
|
||||
if cmd in ["run",]:
|
||||
subparser.add_argument('-n', '--py_nthreads', nargs='?', type=int, default=1,
|
||||
help="The number of threads (to run simultaneously).")
|
||||
|
||||
|
||||
# Register the curried subparser so that MassLauncher will take over in execute.
|
||||
self.register_subparser(cmd, subparser, solve_conflict=True)
|
||||
|
||||
class MassLauncher(object):
|
||||
"""
|
||||
To launch several nearly-identical launchers.
|
||||
Acts like a list of launcher.
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
>> # Let's create four calculations # The rootnames are
|
||||
>> calcs = MassLauncher(4, 'Mycalcs/calc', # Mycalcs/calc1
|
||||
>> jobtype='PBS', # Mycalcs/calc2
|
||||
>> pseudodir='Data', # Mycalcs/calc3
|
||||
>> executable='abinit') # Mycalcs/calc4
|
||||
>>
|
||||
>> # == Common properties ==
|
||||
>> calcs.read('common.in')
|
||||
>>
|
||||
>> calcs.ecut = 10.
|
||||
>> calcs.tolwfr = 1e-8
|
||||
>> calcs.nstep = 0
|
||||
>> calcs.iscf = 7
|
||||
>>
|
||||
>> unit_cell = {'ntypat' : 1, 'znucl' : [14], 'natom' : 2, 'typat' : [1, 1],
|
||||
>> 'rprim' : [[.0, .5, .5], [.5, .0, .5], [.5, .5, .0]],
|
||||
>> 'acell' : 3*[10.261], 'xred' : [[.0, .0, .0], [.25,.25,.25]]}
|
||||
>> calcs.set_variables(unit_cell)
|
||||
>>
|
||||
>> calcs.set_pseudos('14si.pspnc')
|
||||
>>
|
||||
>> calcs.set_jobname('MyTest')
|
||||
>> calcs.set_nodes(1)
|
||||
>> calcs.set_ppn(12)
|
||||
>> calcs.set_memory('1gb')
|
||||
>> calcs.set_runtime(48)
|
||||
>>
|
||||
>> # == Specific properties ==
|
||||
>> ecut = 10.
|
||||
>> for calc in calcs:
|
||||
>> calc.ecut = ecut
|
||||
>> ecut += 5.
|
||||
>>
|
||||
>> # Write them all.
|
||||
>> calcs.make()
|
||||
"""
|
||||
|
||||
ArgParser = MassLauncherArgParser
|
||||
argparser = MassLauncherArgParser()
|
||||
|
||||
def __init__(self, n=0, name='Calc', *args, **kwargs):
|
||||
|
||||
self._setattr(launchers = list())
|
||||
self._setattr(ids = list())
|
||||
|
||||
if n > 0:
|
||||
index_format = '0=' + str(int(np.log10(n)) + 1)
|
||||
for i in range(1, n+1):
|
||||
index = '{i:{f}}'.format(i=i, f=index_format)
|
||||
calc_name = name + index
|
||||
launcher = Launcher(calc_name, *args, **kwargs)
|
||||
self.add_launcher(launcher, index=index)
|
||||
|
||||
def __getitem__(self, i): return self.launchers[i]
|
||||
|
||||
def __setitem__(self, i, calc): self.launchers[i] = calc
|
||||
|
||||
def __delitem__(self, i): del self.launchers[i]
|
||||
|
||||
def __iter__(self): return iter(self.launchers)
|
||||
|
||||
def __len__(self): return len(self.launchers)
|
||||
|
||||
def _setattr(self, **kwargs):
|
||||
self.__dict__.update(kwargs)
|
||||
|
||||
#def __getattr__(self, name):
|
||||
# """Return a function that passes the arguments to all launchers."""
|
||||
# def f(*args, **kwargs):
|
||||
# return [ getattr(c, name)(*args, **kwargs) for c in self ]
|
||||
# return f
|
||||
|
||||
def _distributed(self, func_name):
|
||||
"""Return a function that passes the arguments to all launchers."""
|
||||
def f(*args, **kwargs):
|
||||
return [ getattr(c, func_name)(*args, **kwargs) for c in self ]
|
||||
f.__doc__ = getattr(self[0], func_name).__doc__
|
||||
return f
|
||||
|
||||
def _make_distributed(f):
|
||||
"""Make a function distributed to all launchers."""
|
||||
def g(self, *args, **kwargs):
|
||||
return self._distributed(f.__name__)(*args, **kwargs)
|
||||
g.__doc__ = getattr(Launcher, f.__name__).__doc__
|
||||
return g
|
||||
|
||||
def __setattr__(self, name, value):
|
||||
"""Set an attribute to all launchers."""
|
||||
return [ setattr(calc, name, value) for calc in self ]
|
||||
|
||||
def add_launcher(self, launcher, index=None):
|
||||
"""Add a Launcher instance (or derivatives) to the list."""
|
||||
if index is None:
|
||||
index = str(len(self.launchers) + 1)
|
||||
# maybe needs an OrderedDict here
|
||||
self.launchers.append(launcher)
|
||||
self.ids.append(index)
|
||||
|
||||
# Create missing property setter
|
||||
if len(self.launchers) == 1:
|
||||
for prop in self.launchers[0].properties():
|
||||
setter = 'set_' + prop
|
||||
if not setter in dir(self):
|
||||
self.__dict__[setter] = self._distributed(setter)
|
||||
|
||||
def properties(self):
|
||||
"""Return the list of properties with a `set_` function."""
|
||||
funcs = filter(lambda s: s.startswith('set_'), dir(self))
|
||||
return [ f.split('set_', 1)[-1] for f in funcs ]
|
||||
|
||||
def only(self, only=None):
|
||||
"""Return a list of indexes and a list of calc which are included in 'only'."""
|
||||
if only:
|
||||
filtered = list()
|
||||
for i, calc in zip(self.ids, self):
|
||||
if str(i) in map(str, only):
|
||||
filtered.append((i, calc))
|
||||
else:
|
||||
filtered= zip(self.ids, self)
|
||||
return filtered
|
||||
|
||||
@_make_distributed
|
||||
def set_pseudodir(self): return
|
||||
|
||||
@_make_distributed
|
||||
def set_pseudos(self): return
|
||||
|
||||
@_make_distributed
|
||||
def read(self): return
|
||||
|
||||
@_make_distributed
|
||||
def set_variables(self): return
|
||||
|
||||
@_make_distributed
|
||||
def set_comment(self): return
|
||||
|
||||
@_make_distributed
|
||||
def link_idat(self): return
|
||||
|
||||
@_make_distributed
|
||||
def link_odat(self): return
|
||||
|
||||
@_make_distributed
|
||||
def link_io(self): return
|
||||
|
||||
def execute(self, *args, **kwargs):
|
||||
"""
|
||||
Execute an action given from the command line.
|
||||
|
||||
* make -- Write the files.
|
||||
* submit -- Submit the calculation to a batch server.
|
||||
* run -- Run the calculation from the shell.
|
||||
* report -- Tell if the calculation completed or not.
|
||||
* clean -- Remove log, data files and temporary files.
|
||||
* show -- Signify that the calculation exists.
|
||||
* destroy -- Remove all files, including input and outputs.
|
||||
|
||||
Command line optional arguments:
|
||||
-c [id1 [,id2 [id3, ... ]]] :
|
||||
|
||||
Select a subset of calculations.
|
||||
|
||||
With the previous example, one could issue::
|
||||
|
||||
> python myscript.py make
|
||||
Writing Mycalc/calc1
|
||||
Writing Mycalc/calc2
|
||||
Writing Mycalc/calc3
|
||||
Writing Mycalc/calc4
|
||||
>
|
||||
> python myscript.py show -c 1 2
|
||||
Mycalc/calc1
|
||||
Mycalc/calc2
|
||||
>
|
||||
> python myscript.py run -c 2 3
|
||||
Running Mycalc/calc2
|
||||
Running Mycalc/calc3
|
||||
>
|
||||
> python myscript.py report
|
||||
Mycalc/calc1 : Unstarted
|
||||
Mycalc/calc2.out : Completed
|
||||
Mycalc/calc3.out : Completed
|
||||
Mycalc/calc4 : Unstarted
|
||||
"""
|
||||
if not args:
|
||||
args = sys.argv[1:]
|
||||
|
||||
options, args, kwargs = self.argparser.myparse_args(args)
|
||||
kwargs.update(vars(options))
|
||||
|
||||
if self.argparser.can_handle(options.command):
|
||||
|
||||
try:
|
||||
nthreads = options.py_nthreads
|
||||
except AttributeError:
|
||||
nthreads = 1
|
||||
|
||||
print("About to run command", options.command," with nthreads", nthreads)
|
||||
|
||||
if nthreads == 1:
|
||||
|
||||
if options.command in dir(self):
|
||||
getattr(self, options.command)(*args, **kwargs)
|
||||
else:
|
||||
for index, calc in self.only(options.only):
|
||||
#for i, calc in zip(self.ids, self):
|
||||
# if options.only and str(i) not in map(str, options.only):
|
||||
# continue
|
||||
getattr(calc, options.command)(*args, **kwargs)
|
||||
|
||||
else:
|
||||
# Threaded version.
|
||||
from threading import Thread
|
||||
from Queue import Queue
|
||||
|
||||
def worker():
|
||||
while True:
|
||||
func, args, kwargs = q.get()
|
||||
func(*args, **kwargs)
|
||||
q.task_done()
|
||||
|
||||
q = Queue()
|
||||
for i in range(nthreads):
|
||||
t = Thread(target=worker)
|
||||
t.setDaemon(True)
|
||||
t.start()
|
||||
|
||||
#for (i, calc) in zip(self.ids, self):
|
||||
# if options.only and i not in options.only: continue
|
||||
for index, calc in self.only(options.only):
|
||||
func = getattr(calc, options.command)
|
||||
q.put((func, args, kwargs))
|
||||
|
||||
# Block until all tasks are done.
|
||||
q.join()
|
||||
|
||||
else:
|
||||
raise RuntimeError("Don't know how to handle command %s. This should not happen!" % options.command)
|
||||
|
||||
@_make_distributed
|
||||
def report(self): return
|
||||
|
||||
@_make_distributed
|
||||
def odat_files(self): return
|
||||
|
||||
@_make_distributed
|
||||
def last_output(self): return
|
||||
|
||||
#@_make_distributed
|
||||
def show(self, form=None, *args, **kwargs):
|
||||
only = kwargs.get('only')
|
||||
if form is None:
|
||||
form = str
|
||||
|
||||
#@form
|
||||
def tmpform(s):
|
||||
return str(index) + ' ' + s
|
||||
|
||||
newform = lambda s: form(tmpform(s))
|
||||
|
||||
for index, calc in self.only(only):
|
||||
calc.show(form=newform, *args, **kwargs)
|
||||
|
||||
return
|
||||
|
||||
@_make_distributed
|
||||
def make(self): return
|
||||
|
||||
@_make_distributed
|
||||
def run(self): return
|
||||
|
||||
@_make_distributed
|
||||
def submit(self): return
|
||||
|
||||
@_make_distributed
|
||||
def clean(self): return
|
||||
|
||||
@_make_distributed
|
||||
def destroy(self): return
|
|
@ -1,49 +0,0 @@
|
|||
"""Tests for htc.FilesFile."""
|
||||
from __future__ import print_function, division
|
||||
|
||||
import warnings
|
||||
|
||||
from abipy.htc.filesfile import FilesFile
|
||||
from abipy.core.testing import *
|
||||
|
||||
# =========================================================================== #
|
||||
|
||||
class TestFilesFile(AbipyFileTest):
|
||||
"""Unit tests for FilesFile."""
|
||||
|
||||
def setUp(self):
|
||||
self.file = FilesFile('MyDir/MyName.files',
|
||||
input='mycalc.in',
|
||||
output='mycalc.out',
|
||||
idat_root='i_mycalc',
|
||||
odat_root='o_mycalc',
|
||||
tmp_root='t_mycalc')
|
||||
|
||||
self.file.pseudos = ['ps1', 'ps2']
|
||||
self.file.pseudodir = '/path/to/my/pseudodir'
|
||||
|
||||
def test_check_pseudos(self):
|
||||
"""Test the user is warned of pseudopotential not found."""
|
||||
with warnings.catch_warnings(record=True) as w:
|
||||
warnings.simplefilter("always")
|
||||
self.file.check_pseudos()
|
||||
self.assertEqual(len(w), 2)
|
||||
msg = str(w[-1].message)
|
||||
self.assertRegexpMatches(msg, "file not found")
|
||||
|
||||
def test_str(self):
|
||||
"""Test the FilesFile is printed correctly."""
|
||||
self.assertContains("""
|
||||
mycalc.in
|
||||
mycalc.out
|
||||
i_mycalc
|
||||
o_mycalc
|
||||
t_mycalc
|
||||
/path/to/my/pseudodir/ps1
|
||||
/path/to/my/pseudodir/ps2
|
||||
""")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
import unittest
|
||||
unittest.main()
|
|
@ -1,211 +0,0 @@
|
|||
"""Tests for htc.FilesFile."""
|
||||
from __future__ import print_function, division, unicode_literals
|
||||
|
||||
import numpy as np
|
||||
import abipy.data as abidata
|
||||
|
||||
from abipy.core.testing import AbipyTest
|
||||
from abipy.htc.input import *
|
||||
|
||||
|
||||
class AbiInputTest(AbipyTest):
|
||||
|
||||
def test_si_input(self):
|
||||
"""Testing Silicon input with NC pseudo."""
|
||||
aequal, atrue = self.assertEqual, self.assertTrue
|
||||
|
||||
# Create an ABINIT input file with 1 dataset.
|
||||
inp = AbiInput(pseudos="14si.pspnc", pseudo_dir=abidata.pseudo_dir, ndtset=1)
|
||||
inp.set_comment("Input file with 1 dataset")
|
||||
assert inp.isnc
|
||||
|
||||
inp.set_mnemonics(True)
|
||||
assert inp.mnemonics
|
||||
|
||||
# One can set the value of the variables directly with the syntax.
|
||||
inp.ecut = 10.
|
||||
inp.tolwfr = 1e-8
|
||||
|
||||
# It's possible to use strings but use them only for special cases such as:
|
||||
inp.istwfk = '*1'
|
||||
|
||||
# One can create a dictionary mapping keywords to values
|
||||
unit_cell = {
|
||||
"acell": 3*[10.217],
|
||||
'rprim': [[.0, .5, .5],
|
||||
[.5, .0, .5],
|
||||
[.5, .5, .0]],
|
||||
'ntypat': 1,
|
||||
'znucl': [14,],
|
||||
'natom': 2,
|
||||
'typat': [1, 1],
|
||||
'xred': [[.0, .0, .0],
|
||||
[.25,.25,.25]]
|
||||
}
|
||||
|
||||
# and set the variables in the input file with the call:
|
||||
inp.set_vars(**unit_cell)
|
||||
# Now we have a structure
|
||||
assert len(inp.structure) == 2
|
||||
assert inp.num_valence_electrons == 8
|
||||
|
||||
# Alternatively, it's possible to create a dictionary on the fly with the syntax.
|
||||
inp.set_vars(kptopt=1, ngkpt=[2, 2, 2], nshiftk=1,
|
||||
shiftk=np.reshape([0.0, 0.0, 0.0], (-1,3)))
|
||||
|
||||
inp.nshiftk = len(inp.shiftk)
|
||||
assert inp.nshiftk == 1
|
||||
|
||||
inp.remove_vars("nshiftk")
|
||||
with self.assertRaises(AttributeError): print(inp.nshiftk)
|
||||
|
||||
# To print the input to stdout use:
|
||||
print(inp)
|
||||
|
||||
# Test set_structure
|
||||
new_structure = inp.structure.copy()
|
||||
new_structure.perturb(distance=0.1)
|
||||
inp.set_structure(new_structure)
|
||||
assert inp.structure == new_structure
|
||||
|
||||
# To create a new input with a different variable.
|
||||
new = inp.new_with_vars(kptopt=3)
|
||||
assert new.kptopt == 3 and inp.kptopt == 1
|
||||
|
||||
# Compatible with deepcopy, Pickle and MSONable?
|
||||
inp.deepcopy()
|
||||
self.serialize_with_pickle(inp, test_eq=False)
|
||||
self.assertMSONable(inp)
|
||||
|
||||
# A slightly more complicated example: input file with two datasets
|
||||
inp = AbiInput(pseudos="14si.pspnc", pseudo_dir=abidata.pseudo_dir, ndtset=2)
|
||||
|
||||
# Global variable common to all datasets.
|
||||
inp.tolwfr = 1e-8
|
||||
|
||||
# To specify values for the different datasets, one can use the syntax
|
||||
inp.ecut1 = 10
|
||||
inp.ecut2 = 20
|
||||
|
||||
assert inp[1]["ecut"] == inp.ecut1 and inp[2]["ecut"] == inp.ecut2
|
||||
assert inp[1].get("ecut") == inp.ecut1 and inp[2].get("foobar") is None
|
||||
|
||||
with self.assertRaises(AttributeError): print(inp.ecut)
|
||||
inp.remove_vars("ecut", dtset=2)
|
||||
assert inp.ecut1 == 10
|
||||
with self.assertRaises(AttributeError): print(inp.ecut2)
|
||||
|
||||
# or by passing the index of the dataset to set_vars via the dtset argument.
|
||||
inp.set_vars(ngkpt=[2,2,2], tsmear=0.004, dtset=1)
|
||||
inp.set_vars(kptopt=[4,4,4], tsmear=0.008, dtset=2)
|
||||
print(inp)
|
||||
|
||||
# Compatible with deepcopy, Pickle and MSONable?
|
||||
inp.deepcopy()
|
||||
self.serialize_with_pickle(inp, test_eq=False)
|
||||
self.assertMSONable(inp)
|
||||
|
||||
# pseudo file must exist.
|
||||
with self.assertRaises(inp.Error):
|
||||
AbiInput(pseudos="foobar.pspnc", pseudo_dir=abidata.pseudo_dir, ndtset=2)
|
||||
|
||||
tsmear_list = [0.005, 0.01]
|
||||
ngkpt_list = [[4,4,4], [8,8,8]]
|
||||
occopt_list = [3, 4]
|
||||
inp = AbiInput(pseudos=abidata.pseudos("14si.pspnc"), ndtset=len(tsmear_list))
|
||||
|
||||
inp.linspace("tsmear", start=tsmear_list[0], stop=tsmear_list[-1])
|
||||
print(inp)
|
||||
|
||||
inp = AbiInput(pseudos=abidata.pseudos("14si.pspnc"), ndtset=len(tsmear_list) * len(ngkpt_list))
|
||||
|
||||
inp.product("tsmear", "ngkpt", tsmear_list, ngkpt_list)
|
||||
print(inp)
|
||||
|
||||
# If you don't want to use multiple datasets in your calculation,
|
||||
# you can split the initial input into ndtset different inputs.
|
||||
separated_inps = inp.split_datasets()
|
||||
|
||||
for inp in separated_inps:
|
||||
print(inp)
|
||||
atrue(isinstance(inp, AbiInput))
|
||||
|
||||
# product accepts an arbitrary number of variables.
|
||||
inp = AbiInput(pseudos=abidata.pseudos("14si.pspnc"), ndtset=len(tsmear_list) * len(ngkpt_list) * len(occopt_list))
|
||||
|
||||
inp.product("tsmear", "ngkpt", "occopt", tsmear_list, ngkpt_list, occopt_list)
|
||||
print(inp)
|
||||
|
||||
# Split datasets.
|
||||
inp.split_datasets()
|
||||
|
||||
# Cannot split datasets when we have get* or ird* variables.
|
||||
inp[2].set_vars(getwfk=-1)
|
||||
with self.assertRaises(inp.Error): inp.split_datasets()
|
||||
|
||||
def test_niopaw_input(self):
|
||||
"""Testing AbiInput for NiO with PAW."""
|
||||
aequal = self.assertEqual
|
||||
|
||||
inp = AbiInput(pseudos=abidata.pseudos("28ni.paw", "8o.2.paw"), ndtset=2, comment="NiO calculation")
|
||||
inp.set_structure(abidata.structure_from_ucell("NiO"))
|
||||
print(inp)
|
||||
|
||||
aequal(inp.ndtset, 2)
|
||||
aequal(inp.ispaw, True)
|
||||
|
||||
# Set global variables.
|
||||
inp.set_vars(ecut=10)
|
||||
|
||||
# Compatible with deepcopy, Pickle and MSONable?
|
||||
inp.deepcopy()
|
||||
self.serialize_with_pickle(inp, test_eq=False)
|
||||
self.assertMSONable(inp)
|
||||
|
||||
# Setting an unknown variable should raise an error.
|
||||
with self.assertRaises(inp.Error): inp.set_vars(foobar=10)
|
||||
|
||||
|
||||
class LdauLexxTest(AbipyTest):
|
||||
|
||||
def test_nio(self):
|
||||
"""Test LdauParams and LexxParams."""
|
||||
aequal, atrue = self.assertEqual, self.assertTrue
|
||||
|
||||
structure = abidata.structure_from_ucell("NiO")
|
||||
pseudos = abidata.pseudos("28ni.paw", "8o.2.paw")
|
||||
|
||||
u = 8.0
|
||||
luj_params = LdauParams(usepawu=1, structure=structure)
|
||||
luj_params.luj_for_symbol("Ni", l=2, u=u, j=0.1*u, unit="eV")
|
||||
vars = luj_params.to_abivars()
|
||||
|
||||
self.serialize_with_pickle(luj_params, test_eq=False)
|
||||
|
||||
atrue(vars["usepawu"] == 1),
|
||||
aequal(vars["lpawu"], "2 -1"),
|
||||
aequal(vars["upawu"], "8.0 0.0 eV"),
|
||||
aequal(vars["jpawu"], "0.8 0.0 eV"),
|
||||
|
||||
# Cannot add UJ for non-existent species.
|
||||
with self.assertRaises(ValueError):
|
||||
luj_params.luj_for_symbol("Foo", l=2, u=u, j=0.1*u, unit="eV")
|
||||
|
||||
# Cannot overwrite UJ.
|
||||
with self.assertRaises(ValueError):
|
||||
luj_params.luj_for_symbol("Ni", l=1, u=u, j=0.1*u, unit="eV")
|
||||
|
||||
lexx_params = LexxParams(structure)
|
||||
lexx_params.lexx_for_symbol("Ni", l=2)
|
||||
vars = lexx_params.to_abivars()
|
||||
|
||||
self.serialize_with_pickle(lexx_params, test_eq=False)
|
||||
|
||||
aequal(vars["useexexch"], 1),
|
||||
aequal(vars["lexexch"], "2 -1"),
|
||||
|
||||
# Cannot add LEXX for non-existent species.
|
||||
with self.assertRaises(ValueError): lexx_params.lexx_for_symbol("Foo", l=2)
|
||||
|
||||
# Cannot overwrite LEXX.
|
||||
with self.assertRaises(ValueError): lexx_params.lexx_for_symbol("Ni", l=1)
|
|
@ -1,83 +0,0 @@
|
|||
"""Tests for htc.InputFile."""
|
||||
import warnings
|
||||
|
||||
from abipy.core.testing import AbipyFileTest
|
||||
from abipy.htc.jobfile import JobFile
|
||||
from abipy.htc.inputfile import InputFile
|
||||
from abipy.htc.variable import SpecialInputVariable
|
||||
|
||||
|
||||
class TestInputVariable(AbipyFileTest):
|
||||
"""Unit tests for AbinitVariable."""
|
||||
|
||||
def setUp(self):
|
||||
self.file = SpecialInputVariable('ecut', 10.0)
|
||||
|
||||
def test_varnames(self):
|
||||
"""Test printing of variables name."""
|
||||
self.file.name = 'ecut1'
|
||||
self.assertContains('ecut1')
|
||||
|
||||
self.file.name = 'ecut__s'
|
||||
self.assertContains('ecut:')
|
||||
|
||||
self.file.name = 'ecut__i'
|
||||
self.assertContains('ecut+')
|
||||
|
||||
self.file.name = 'ecut__a'
|
||||
self.assertContains('ecut?')
|
||||
|
||||
self.file.name = 'ecut__s2'
|
||||
self.assertContains('ecut:2')
|
||||
|
||||
self.file.name = 'ecut3__a'
|
||||
self.assertContains('ecut3?')
|
||||
|
||||
self.file.name = 'ecut__s__a'
|
||||
self.assertContains('ecut:?')
|
||||
|
||||
def test_scalar_values(self):
|
||||
"""Test printing of scalar variables."""
|
||||
self.file.value = 11.5
|
||||
self.assertContains('ecut 11.5')
|
||||
|
||||
self.file.value = 10
|
||||
self.assertContains('ecut 10')
|
||||
|
||||
self.file.value = '*1'
|
||||
self.assertContains('ecut *1')
|
||||
|
||||
self.file.value = None
|
||||
self.assertEmpty()
|
||||
|
||||
self.file.value = ''
|
||||
self.assertEmpty()
|
||||
|
||||
|
||||
class TestInputFile(AbipyFileTest):
|
||||
"""Unit tests for InputFile."""
|
||||
|
||||
def setUp(self):
|
||||
self.file = InputFile('MyDir/MyName.in')
|
||||
|
||||
def test_set_variable_attribute(self):
|
||||
"""Test setting variables by attribute."""
|
||||
self.file.ecut = 10.
|
||||
self.assertContains('ecut 10.')
|
||||
|
||||
def test_set_variable_function(self):
|
||||
"""Test setting variables with set_variable."""
|
||||
self.file.set_variable('ecut', 10.)
|
||||
self.assertContains('ecut 10.')
|
||||
|
||||
def test_set_variables_function(self):
|
||||
"""Test setting variables with set_variables."""
|
||||
|
||||
self.file.set_variables({'ecut':10., 'nstep':100})
|
||||
self.assertContains('nstep 100')
|
||||
self.assertContains('ecut 10.')
|
||||
|
||||
self.file.set_variables({'ecut':10., 'nstep':100}, 1)
|
||||
self.assertContains('nstep1 100')
|
||||
self.assertContains('ecut1 10.')
|
||||
|
|
@ -1,165 +0,0 @@
|
|||
"""Tests for htc.JobFile."""
|
||||
from __future__ import print_function, division
|
||||
|
||||
import warnings
|
||||
|
||||
from abipy.core.testing import AbipyFileTest
|
||||
from abipy.htc.jobfile import JobFile
|
||||
|
||||
|
||||
class TestJobFile(AbipyFileTest):
|
||||
"""Unit tests for JobFile."""
|
||||
|
||||
def setUp(self):
|
||||
self.file = JobFile('MyJob.sh', input='myinput')
|
||||
|
||||
def test_shell_line(self):
|
||||
"""Check the shell line is printed correctly."""
|
||||
self.assertContains("#!/bin/bash")
|
||||
self.file.shell = 'csh'
|
||||
self.assertContains("#!/bin/csh")
|
||||
|
||||
def test_declaration_lines(self):
|
||||
"""Check the declaration are printed correctly in bash."""
|
||||
self.assertContains("""
|
||||
MPIRUN=""
|
||||
EXECUTABLE=abinit
|
||||
INPUT=myinput
|
||||
LOG=log
|
||||
STDERR=stderr
|
||||
""")
|
||||
|
||||
def test_execution_line(self):
|
||||
"""Check the execution line is printed correctly in bash."""
|
||||
self.assertContains("""
|
||||
$MPIRUN $EXECUTABLE < $INPUT > $LOG 2> $STDERR
|
||||
""")
|
||||
|
||||
def test_mpirun(self):
|
||||
"""Check mpirun is set correctly."""
|
||||
self.file.mpirun = 'openmpirun -n 2'
|
||||
self.assertContains("""
|
||||
MPIRUN="openmpirun -n 2"
|
||||
""")
|
||||
|
||||
def test_modules(self):
|
||||
"""Check the modules are loaded correctly."""
|
||||
|
||||
self.file.modules = "single"
|
||||
self.assertContains("module load single")
|
||||
|
||||
m1, m2 = 'mod1', 'mod2/version/1.4-b'
|
||||
lookfor = """
|
||||
module load {0}
|
||||
module load {1}
|
||||
""".format(m1, m2)
|
||||
self.file.modules = m1, m2
|
||||
self.assertContains(lookfor)
|
||||
|
||||
self.file.modules = [m1, m2]
|
||||
self.assertContains(lookfor)
|
||||
|
||||
self.file.set_modules(m1, m2)
|
||||
self.assertContains(lookfor)
|
||||
|
||||
self.file.set_modules([m1, m2])
|
||||
self.assertContains(lookfor)
|
||||
|
||||
def test_lines_before(self):
|
||||
"""Check lines_before are printed correctly."""
|
||||
|
||||
single_line = "A single line."
|
||||
self.file.lines_before = single_line
|
||||
self.assertContains(single_line)
|
||||
|
||||
l1 = "This is my first line!"
|
||||
l2 = "And that is my ${SECOND_LINE}"
|
||||
lookfor = '\n'.join([l1, l2])
|
||||
|
||||
self.file.lines_before = l1, l2
|
||||
self.assertContains(lookfor)
|
||||
|
||||
self.file.lines_before = [l1, l2]
|
||||
self.assertContains(lookfor)
|
||||
|
||||
self.file.set_lines_before(l1, l2)
|
||||
self.assertContains(lookfor)
|
||||
|
||||
self.file.set_lines_before([l1, l2])
|
||||
self.assertContains(lookfor)
|
||||
|
||||
def test_lines_after(self):
|
||||
"""Check lines_after are printed correctly."""
|
||||
|
||||
single_line = "A single line."
|
||||
self.file.lines_after = single_line
|
||||
self.assertContains(single_line)
|
||||
|
||||
l1 = "This is my first line!"
|
||||
l2 = "And that is my ${SECOND_LINE}"
|
||||
lookfor = '\n'.join([l1, l2])
|
||||
|
||||
self.file.lines_after = l1, l2
|
||||
self.assertContains(lookfor)
|
||||
|
||||
self.file.lines_after = [l1, l2]
|
||||
self.assertContains(lookfor)
|
||||
|
||||
self.file.set_lines_after(l1, l2)
|
||||
self.assertContains(lookfor)
|
||||
|
||||
self.file.set_lines_after([l1, l2])
|
||||
self.assertContains(lookfor)
|
||||
|
||||
def test_other_lines(self):
|
||||
"""Check other_lines are printed correctly."""
|
||||
|
||||
single_line = "A single line."
|
||||
self.file.other_lines = single_line
|
||||
self.assertContains(single_line)
|
||||
|
||||
l1 = "This is my first line!"
|
||||
l2 = "And that is my ${SECOND_LINE}"
|
||||
lookfor = '\n'.join([l1, l2])
|
||||
|
||||
self.file.other_lines = l1, l2
|
||||
self.assertContains(lookfor)
|
||||
|
||||
self.file.other_lines = [l1, l2]
|
||||
self.assertContains(lookfor)
|
||||
|
||||
self.file.set_other_lines(l1, l2)
|
||||
self.assertContains(lookfor)
|
||||
|
||||
self.file.set_other_lines([l1, l2])
|
||||
self.assertContains(lookfor)
|
||||
|
||||
|
||||
class TestJobFileCSH(AbipyFileTest):
|
||||
"""Unit tests for JobFile with csh shell."""
|
||||
|
||||
def setUp(self):
|
||||
self.file = JobFile('MyJob.sh', input='myinput')
|
||||
self.file.shell='csh'
|
||||
|
||||
def test_declaration_lines(self):
|
||||
"""Check the declaration are printed correctly in csh."""
|
||||
self.assertContains("""
|
||||
set MPIRUN=""
|
||||
set EXECUTABLE=abinit
|
||||
set INPUT=myinput
|
||||
set LOG=log
|
||||
set STDERR=stderr
|
||||
""")
|
||||
|
||||
def test_execution_line(self):
|
||||
"""Check the execution line is printed correctly in csh."""
|
||||
self.assertContains("""
|
||||
($MPIRUN $EXECUTABLE < $INPUT > $LOG) >& $STDERR
|
||||
""")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
import unittest
|
||||
unittest.main()
|
||||
|
|
@ -1,199 +0,0 @@
|
|||
"""Tools and helper functions for abinit calculations"""
|
||||
from __future__ import print_function, division #, unicode_literals
|
||||
|
||||
import os.path
|
||||
import collections
|
||||
|
||||
from copy import deepcopy
|
||||
from itertools import chain
|
||||
from monty.string import list_strings
|
||||
|
||||
##########################################################################################
|
||||
|
||||
class _ewc_tuple(collections.namedtuple("ewc_tuple", "errors, warnings, comments")):
|
||||
|
||||
def tostream(self, stream):
|
||||
"Return a string that can be visualized on stream (with colors if stream support them)."
|
||||
str_colorizer = StringColorizer(stream)
|
||||
|
||||
red = lambda num : str_colorizer(str(num), "red")
|
||||
blue = lambda num : str_colorizer(str(num), "blue")
|
||||
|
||||
nums = map(len, [self.errors, self.warnings, self.comments])
|
||||
|
||||
colors = (red, blue, str)
|
||||
|
||||
for (i, n) in enumerate(nums):
|
||||
color = colors[i]
|
||||
nums[i] = color(n) if n else str(n)
|
||||
|
||||
return "%s errors, %s warnings, %s comments in main output file" % tuple(nums)
|
||||
|
||||
##########################################################################################
|
||||
|
||||
def parse_ewc(filename, nafter=5):
|
||||
"""
|
||||
Extract errors, warnings and comments from file filename.
|
||||
|
||||
:arg nafter: Save nafter lines of trailing context after matching lines.
|
||||
:return: namedtuple instance. The lists of strings with the corresponding messages are
|
||||
available in tuple.errors, tuple.warnings, tuple.comments.
|
||||
"""
|
||||
# TODO
|
||||
# we have to standardize the abinit WARNING, COMMENT and ERROR so that we can parse them easily
|
||||
# without having to use nafter.
|
||||
|
||||
errors, warnings, comments = [], [], []
|
||||
# Note the space after the name.
|
||||
exc_cases = ["ERROR ", "BUG ", "WARNING ", "COMMENT "]
|
||||
|
||||
handlers = {
|
||||
"ERROR " : errors.append,
|
||||
"BUG " : errors.append,
|
||||
"WARNING " : warnings.append,
|
||||
"COMMENT " : comments.append,
|
||||
}
|
||||
|
||||
def exc_case(line):
|
||||
for e in exc_cases:
|
||||
if e in line: return e
|
||||
else:
|
||||
return None
|
||||
|
||||
with open(filename, "r") as fh:
|
||||
lines = fh.readlines()
|
||||
nlines = len(lines)
|
||||
for (lineno, line) in enumerate(lines):
|
||||
handle = handlers.get(exc_case(line))
|
||||
if handle is None: continue
|
||||
context = lines[lineno: min(lineno+nafter, nlines)]
|
||||
handle( "".join([c for c in context]) )
|
||||
|
||||
return _ewc_tuple(errors, warnings, comments)
|
||||
|
||||
##########################################################################################
|
||||
|
||||
def find_file(files, ext, prefix=None, dataset=None, image=None):
|
||||
"""
|
||||
Given a list of file names, return the file with extension "_" + ext, None if not found.
|
||||
|
||||
The prefix, the dataset index and the image index can be specified
|
||||
|
||||
.. warning::
|
||||
|
||||
There are some border cases that will confuse the algorithm
|
||||
since the order of dataset and image is not tested.
|
||||
Solving this problem requires the knowledge of ndtset and nimages
|
||||
This code, however should work in 99.9% of the cases.
|
||||
"""
|
||||
separator = "_"
|
||||
|
||||
for filename in list_strings(files):
|
||||
# Remove Netcdf extension (if any)
|
||||
f = filename[:-3] if filename.endswith(".nc") else filename
|
||||
if separator not in f: continue
|
||||
tokens = f.split(separator)
|
||||
if tokens[-1] == ext:
|
||||
found = True
|
||||
if prefix is not None: found = found and filename.startswith(prefix)
|
||||
if dataset is not None: found = found and "DS" + str(dataset) in tokens
|
||||
if image is not None: found = found and "IMG" + str(image) in tokens
|
||||
if found: return filename
|
||||
else:
|
||||
return None
|
||||
|
||||
##########################################################################################
|
||||
|
||||
def abinit_output_iscomplete(output_file):
|
||||
"Returns True if the abinit output file is complete."
|
||||
if not os.path.exists(output_file):
|
||||
return False
|
||||
|
||||
chunk = 5 * 1024 # Read only the last 5Kb of data.
|
||||
nlines = 10 # Check only in the last 10 lines.
|
||||
|
||||
MAGIC = "Calculation completed."
|
||||
|
||||
with open(output_file, 'r') as f:
|
||||
size = f.tell()
|
||||
f.seek(max(size - chunk, 0))
|
||||
try:
|
||||
for line in f.read().splitlines()[-nlines:]:
|
||||
if MAGIC in line:
|
||||
return True
|
||||
except:
|
||||
pass
|
||||
|
||||
return False
|
||||
|
||||
|
||||
# =========================================================================== #
|
||||
|
||||
def is_number(s):
|
||||
"""Returns True if the argument can be made a float."""
|
||||
try:
|
||||
float(s)
|
||||
return True
|
||||
except:
|
||||
return False
|
||||
|
||||
def is_iter(obj):
|
||||
"""Return True if the argument is list-like."""
|
||||
return hasattr(obj, '__iter__')
|
||||
|
||||
def is_scalar(obj):
|
||||
"""Return True if the argument is not list-like."""
|
||||
return not is_iter
|
||||
|
||||
def flatten(iterable):
|
||||
"""Make an iterable flat, i.e. a 1d iterable object."""
|
||||
iterator = iter(iterable)
|
||||
array, stack = collections.deque(), collections.deque()
|
||||
while True:
|
||||
try:
|
||||
value = next(iterator)
|
||||
except StopIteration:
|
||||
if not stack:
|
||||
return tuple(array)
|
||||
iterator = stack.pop()
|
||||
else:
|
||||
if not isinstance(value, str) \
|
||||
and isinstance(value, collections.Iterable):
|
||||
stack.append(iterator)
|
||||
iterator = iter(value)
|
||||
else:
|
||||
array.append(value)
|
||||
|
||||
def listify(obj):
|
||||
"""Return a flat list out of the argument."""
|
||||
if not obj:
|
||||
obj = list()
|
||||
elif is_iter(obj):
|
||||
obj = list(flatten(obj))
|
||||
else:
|
||||
obj = [obj]
|
||||
return deepcopy(obj)
|
||||
|
||||
|
||||
class StringColorizer(object):
|
||||
COLORS = {"default": "",
|
||||
"blue": "\x1b[01;34m",
|
||||
"cyan": "\x1b[01;36m",
|
||||
"green": "\x1b[01;32m",
|
||||
"red": "\x1b[01;31m",
|
||||
# lighting colors.
|
||||
#"lred": "\x1b[01;05;37;41m"
|
||||
}
|
||||
|
||||
def __init__(self, stream):
|
||||
self.has_colours = stream_has_colours(stream)
|
||||
|
||||
def __call__(self, string, colour):
|
||||
if self.has_colours:
|
||||
code = self.COLORS.get(colour, "")
|
||||
if code:
|
||||
return code + string + "\x1b[00m"
|
||||
else:
|
||||
return string
|
||||
else:
|
||||
return string
|
|
@ -141,3 +141,5 @@ TODO list:
|
|||
it and can report this value in the final band structure.
|
||||
|
||||
* DONE ecut is not reported in the GSR file. Similar problem for the k-sampling (see SIGRES.nc)
|
||||
|
||||
* FFTProf (use file extension and interface it with abiopen)
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
Python interface to fftprof. Provides objects to benchmark
|
||||
the FFT libraries used by ABINIT and plot the results with matplotlib.
|
||||
"""
|
||||
from __future__ import print_function, division, unicode_literals
|
||||
from __future__ import print_function, division, unicode_literals, absolute_import
|
||||
|
||||
import sys
|
||||
import os
|
||||
|
@ -12,9 +12,10 @@ import numpy as np
|
|||
from subprocess import Popen, PIPE
|
||||
from monty.os.path import which
|
||||
from monty.fnmatch import WildCard
|
||||
from abipy.tools.plotting import add_fig_kwargs, get_ax_fig_plt
|
||||
|
||||
__all__ = [
|
||||
"FFT_Benchmark",
|
||||
"FFTBenchmark",
|
||||
]
|
||||
|
||||
_color_fftalg = {
|
||||
|
@ -58,14 +59,10 @@ class FFT_Test(object):
|
|||
def __init__(self, ecut, ngfft, wall_time, info):
|
||||
"""
|
||||
Args:
|
||||
ecut:
|
||||
List of cutoff energies in Hartree.
|
||||
ngfft:
|
||||
List with FFT divisions.
|
||||
wall_time:
|
||||
List of wall_time for the different ecut.
|
||||
info:
|
||||
Dictionary with extra information.
|
||||
ecut: List of cutoff energies in Hartree.
|
||||
ngfft: List with FFT divisions.
|
||||
wall_time: List of wall_time for the different ecut.
|
||||
info: Dictionary with extra information.
|
||||
"""
|
||||
self.ecut = np.asarray(ecut)
|
||||
self.necut = len(ecut)
|
||||
|
@ -110,11 +107,11 @@ class FFT_Test(object):
|
|||
return line
|
||||
|
||||
|
||||
class FFT_Benchmark(object):
|
||||
class FFTBenchmark(object):
|
||||
"""
|
||||
Container class storing the results of the FFT benchmark.
|
||||
|
||||
Use the class method `from_file` to generate a new instance.
|
||||
Use the class method ``from_file`` to generate a new instance.
|
||||
"""
|
||||
@classmethod
|
||||
def from_file(cls, fileobj):
|
||||
|
@ -128,8 +125,8 @@ class FFT_Benchmark(object):
|
|||
alg = test.fftalg
|
||||
if alg not in self._fftalgs:
|
||||
self._fftalgs.append(alg)
|
||||
self._fftalgs.sort()
|
||||
|
||||
self._fftalgs.sort()
|
||||
self.tests = [t for t in FFT_tests]
|
||||
|
||||
def iter_fftalgs(self):
|
||||
|
@ -143,26 +140,13 @@ class FFT_Benchmark(object):
|
|||
if t.fftalg == fftalg: lst.append(t)
|
||||
return lst
|
||||
|
||||
@add_fig_kwargs
|
||||
def plot(self, exclude_algs=None, exclude_threads=None, **kwargs):
|
||||
"""
|
||||
Plot the wall-time and the speed-up.
|
||||
|
||||
Args:
|
||||
|
||||
============== ==============================================================
|
||||
kwargs Meaning
|
||||
============== ==============================================================
|
||||
show True to show the figure (Default)
|
||||
|
||||
savefig 'abc.png' or 'abc.eps'* to save the figure to a file.
|
||||
============== ===============================================================
|
||||
"""
|
||||
show = kwargs.pop("show", True)
|
||||
savefig = kwargs.pop("savefig", None)
|
||||
|
||||
import matplotlib.pyplot as plt
|
||||
fig = plt.figure()
|
||||
|
||||
ax1 = fig.add_subplot(2, 1, 1)
|
||||
|
||||
exc_algs = []
|
||||
|
@ -206,7 +190,6 @@ class FFT_Benchmark(object):
|
|||
ax2.set_xticklabels(labels, fontdict=None, minor=False, rotation=35)
|
||||
|
||||
for fftalg in self.iter_fftalgs():
|
||||
|
||||
if fftalg in exc_algs: continue
|
||||
tests = self.tests_with_fftalg(fftalg)
|
||||
for t in tests:
|
||||
|
@ -247,12 +230,6 @@ class FFT_Benchmark(object):
|
|||
ideal = [1.0 for i in range(t0.necut)]
|
||||
ax2.plot(t0.ecut, ideal, "b-", linewidth=3.0)
|
||||
|
||||
if show:
|
||||
plt.show()
|
||||
|
||||
if savefig is not None:
|
||||
fig.savefig(os.path.abspath(savefig))
|
||||
|
||||
return fig
|
||||
|
||||
|
||||
|
@ -260,12 +237,9 @@ def parse_prof_file(fileobj):
|
|||
"""
|
||||
Parse the PROF file generated by fftprof.F90.
|
||||
|
||||
Args:
|
||||
fileobj:
|
||||
String or file-like object.
|
||||
Args: fileobj: String or file-like object.
|
||||
|
||||
Returns:
|
||||
Instance of FFT_Benchmark.
|
||||
Returns: Instance of FFTBenchmark.
|
||||
"""
|
||||
# The file contains
|
||||
# 1) An initial header with info on the routine.
|
||||
|
@ -321,14 +295,14 @@ def parse_prof_file(fileobj):
|
|||
except IndexError:
|
||||
line = None
|
||||
|
||||
# Instantiate FFT_Benchmark.
|
||||
# Instantiate FFTBenchmark.
|
||||
fft_tests = []
|
||||
for (idx, wall_time) in enumerate(data):
|
||||
for idx, wall_time in enumerate(data):
|
||||
info = info_of_test[idx]
|
||||
Test = FFT_Test(ecut, ngfft, wall_time, info)
|
||||
fft_tests.append(Test)
|
||||
|
||||
return FFT_Benchmark(title, fft_tests)
|
||||
return FFTBenchmark(title, fft_tests)
|
||||
|
||||
|
||||
class FFTProfError(Exception):
|
||||
|
@ -397,5 +371,5 @@ class FFTProf(object):
|
|||
if self.verbose:
|
||||
print("About to plot prof_file: ", prof_file)
|
||||
|
||||
bench = FFT_Benchmark.from_file(prof_file)
|
||||
bench = FFTBenchmark.from_file(prof_file)
|
||||
bench.plot()
|
|
@ -2,7 +2,6 @@
|
|||
"""Tests for duck module."""
|
||||
from __future__ import division, print_function, absolute_import, unicode_literals
|
||||
|
||||
|
||||
import numpy as np
|
||||
|
||||
from abipy.core.testing import AbipyTest
|
||||
|
|
|
@ -0,0 +1,34 @@
|
|||
# coding: utf-8
|
||||
"""Tests for fftprof module."""
|
||||
from __future__ import division, print_function, absolute_import, unicode_literals
|
||||
|
||||
import os
|
||||
import abipy.data as abidata
|
||||
|
||||
from abipy.core.testing import AbipyTest
|
||||
from abipy.tools.fftprof import FFTBenchmark
|
||||
|
||||
|
||||
class FftProfTest(AbipyTest):
|
||||
|
||||
def test_fft_benchmark(self):
|
||||
"""Testing FFt benchmark."""
|
||||
|
||||
# Plot the benchmark results saved in the files
|
||||
path = os.path.join(abidata.dirpath, "PROF_fourwf_cplex0_option3_istwfk1")
|
||||
bench = FFTBenchmark.from_file(path)
|
||||
|
||||
assert bench.title.strip() == " Benchmark: routine = fourwf, cplex = 0, option= 3, istwfk= 1".strip()
|
||||
assert len(bench.tests) == 4
|
||||
assert len(bench.tests_with_fftalg(112)) == 2
|
||||
assert len(bench.tests_with_fftalg(512)) == 2
|
||||
|
||||
test0 = bench.tests_with_fftalg(112)[0]
|
||||
test1 = bench.tests_with_fftalg(112)[1]
|
||||
assert len(test0.ecut) == len(test0.ngfft)
|
||||
assert str(test0)
|
||||
test0.speedup_wrt(test1)
|
||||
|
||||
if self.has_matplotlib():
|
||||
#test0.plot_ax()
|
||||
assert bench.plot(show=False)
|
|
@ -84,3 +84,11 @@ abio Package
|
|||
:members:
|
||||
:undoc-members:
|
||||
:show-inheritance:
|
||||
|
||||
:mod:`variable` Module
|
||||
----------------------
|
||||
|
||||
.. automodule:: abipy.abio.variable
|
||||
:members:
|
||||
:undoc-members:
|
||||
:show-inheritance:
|
||||
|
|
|
@ -53,6 +53,14 @@ tools Package
|
|||
:undoc-members:
|
||||
:show-inheritance:
|
||||
|
||||
:mod:`fftprof` Module
|
||||
---------------------
|
||||
|
||||
.. automodule:: abipy.tools.fftprof
|
||||
:members:
|
||||
:undoc-members:
|
||||
:show-inheritance:
|
||||
|
||||
:mod:`iotools` Module
|
||||
---------------------
|
||||
|
||||
|
|
Loading…
Reference in New Issue