From 1df8b4e162f0016524c5b49538f60ff85b41079a Mon Sep 17 00:00:00 2001 From: Matteo Giantomassi Date: Tue, 21 Aug 2018 22:34:08 +0200 Subject: [PATCH] Explain how to subclass Tasks and avoid pickle errors --- abipy/core/symmetries.py | 109 ++++++++++++++++++++++++++------ abipy/dfpt/ddb.py | 6 +- abipy/dfpt/tests/test_ddb.py | 2 +- abipy/examples/flows/README.txt | 2 +- abipy/examples/plot/README.txt | 6 +- abipy/integration_tests/TODO.md | 10 +-- docs/flows_howto.rst | 30 +++++++-- 7 files changed, 127 insertions(+), 38 deletions(-) diff --git a/abipy/core/symmetries.py b/abipy/core/symmetries.py index 226632b6..02694125 100644 --- a/abipy/core/symmetries.py +++ b/abipy/core/symmetries.py @@ -11,7 +11,7 @@ import numpy as np import spglib from six.moves import cStringIO -from tabulate import tabulate + from monty.string import is_string from monty.itertools import iuptri from monty.functools import lazy_property @@ -119,7 +119,7 @@ def _get_det(mat): class Operation(object): """ Abstract base class that defines the methods that must be - implememted by the concrete class representing some sort of operation + implemented by the concrete class representing some sort of operation """ @abc.abstractmethod def __eq__(self, other): @@ -148,6 +148,17 @@ class Operation(object): def isE(self): """True if self is the identity operator""" + #def commute(self, other) + # return self * other == other * self + + #def commutator(self, other) + # return self * other - other * self + + #def anticommute(self, other) + # return self * other == - other * self + + #def direct_product(self, other) + class SymmOp(Operation, SlotPickleMixin): """ @@ -236,6 +247,18 @@ class SymmOp(Operation, SlotPickleMixin): self.afm_sign == 1) # end operator protocol. + #@lazy_property + #def order(self): + # """Order of the operation.""" + # n = 0 + # o = self + # while m < 1000: + # if o.isE: return n + # n += 1 + # o = self * o + # else: + # raise ValueError("Cannot find order") + def __repr__(self): return str(self) @@ -431,7 +454,7 @@ class OpSequence(collections.Sequence): return -1 def is_group(self): - """True if the list of operations represent a group.""" + """True if this set of operations represent a group.""" check = 0 # Identity must be present. @@ -454,7 +477,7 @@ class OpSequence(collections.Sequence): return check == 0 def is_commutative(self): - """True if operations in self commute with each other.""" + """True if all operations commute with each other.""" for op1, op2 in iuptri(self, diago=False): if op1 * op2 != op2 * op1: return False @@ -462,7 +485,7 @@ class OpSequence(collections.Sequence): return True def is_abelian_group(self): - """True if self is a commutative group.""" + """True if commutative group.""" return self.is_commutative() and self.is_group() def asdict(self): @@ -472,6 +495,15 @@ class OpSequence(collections.Sequence): """ return {op: idx for idx, op in enumerate(self)} + #def is_subset(self, other) + # indmap = {} + # for i, op in self: + # j = other.find(op) + # if j != -1: indmap[i] = j + # return indmap + + #def is_superset(self, other) + @lazy_property def mult_table(self): """ @@ -849,7 +881,7 @@ class LittleGroup(OpSequence): # Add character_table from Bilbao database. bilbao_ptgrp = bilbao_ptgroup(self.kgroup.sch_symbol) - lines.extend(l.strip() for l in bilbao_ptgrp.to_string().splitlines()) + app(bilbao_ptgrp.to_string(verbose=verbose)) app("") # Write warning if non-symmorphic little group with k-point at zone border. @@ -1073,6 +1105,9 @@ class LatticeRotation(Operation): # return t +# TODO: Need to find an easy way to map classes in internal database +# onto classes computed by client code when calculation has been done +# with non-conventional settings (spglib?) class Irrep(object): """ This object represents an irreducible representation. @@ -1117,6 +1152,9 @@ class Irrep(object): def character(self): return self._character + #@lazy_property + #def dataframe(self): + def bilbao_ptgroup(sch_symbol): """ @@ -1184,29 +1222,58 @@ class BilbaoPointGroup(object): """List with the names of the irreps.""" return list(self.irreps_by_name.keys()) - @property + @lazy_property def character_table(self): - """Table of strings with the character of the irreps.""" + """ + Dataframe with irreps. + """ # 1st row: ptgroup_name class names and multiplicity of each class + #name_mult = [name + " [" + str(mult) + "]" for (name, mult) in zip(self.class_names, self.class_len)] + #table = [[self.sch_symbol] + name_mult] + #app = table.append + + ## Add row: irrep_name, character. + #for irrep in self.irreps: + # character = list(map(str, irrep.character)) + # app([irrep.name] + character) + + #from tabulate import tabulate + #s = tabulate(table[1:], headers=table[0], tablefmt="simple", numalign="left") + #print(s) + + # Caveat: class names are not necessarly unique --> use np.stack + import pandas as pd name_mult = [name + " [" + str(mult) + "]" for (name, mult) in zip(self.class_names, self.class_len)] - table = [[self.sch_symbol] + name_mult] - app = table.append + columns = ["name"] + name_mult - # Add row: irrep_name, character. - for irrep in self.irreps: - character = list(map(str, irrep.character)) - app([irrep.name] + character) + stack = np.stack([irrep.character for irrep in self.irreps]) + index = [irrep.name for irrep in self.irreps] + df = pd.DataFrame(stack, columns=name_mult, index=index) + df.index.name = "Irrep" + df.columns.name = self.sch_symbol - return table + # TODO + #print(df) + # Convert complex --> real if all entries in a colums are real. + #for k in name_mult: + # if np.all(np.isreal(df[k].values)): + # #df[k] = df[k].values.real + # df[k] = df[k].astype(float) - def to_string(self, tablefmt="simple", numalign="left"): + return df + + def to_string(self, verbose=0): """ - Write a string with the character_table to the given ``stream``. - ``tablefmt`` and ``numalign`` options are passed to ``tabulate``. + Return string with the character_table """ - s = tabulate(self.character_table[1:], headers=self.character_table[0], - tablefmt=tablefmt, numalign=numalign) - return s + return self.character_table.to_string() + + #def decompose(self, character): + # od = collections.OrderedDict() + # for irrep in self.irreps: + # irrep.name + # irrep.character + # return od #def show_irrep(self, irrep_name): # """Show the mapping rotation --> irrep mat.""" diff --git a/abipy/dfpt/ddb.py b/abipy/dfpt/ddb.py index 8a3365a6..61cbe98d 100644 --- a/abipy/dfpt/ddb.py +++ b/abipy/dfpt/ddb.py @@ -1991,7 +1991,7 @@ class DdbRobot(Robot): ("eV", "meV", "Ha", "cm-1", "Thz"). Case-insensitive. asr, chneut, dipdip: Anaddb input variable. See official documentation. with_geo: True if structure info should be added to the dataframe - with_spglib: True to compute sgplib space group and add it to the DataFrame. + with_spglib: True to compute spglib space group and add it to the DataFrame. abspath: True if paths in index should be absolute. Default: Relative to getcwd(). funcs: Function or list of functions to execute to add more data to the DataFrame. Each function receives a |DdbFile| object and returns a tuple (key, value) @@ -2080,7 +2080,7 @@ class DdbRobot(Robot): ddb_header_keys: List of keywords in the header of the DDB file whose value will be added to the Dataframe. with_structure: True to add structure parameters to the DataFrame. - with_spglib: True to compute sgplib space group and add it to the DataFrame. + with_spglib: True to compute spglib space group and add it to the DataFrame. with_path: True to add DDB path to dataframe manager: |TaskManager| object. If None, the object is initialized from the configuration file verbose: verbosity level. Set it to a value > 0 to get more information @@ -2256,7 +2256,7 @@ class DdbRobot(Robot): def write_notebook(self, nbpath=None): """ - Write a jupyter_ notebook to nbpath. If ``nbpath`` is None, a temporay file in the current + Write a jupyter_ notebook to nbpath. If ``nbpath`` is None, a temporary file in the current working directory is created. Return path to the notebook. """ nbformat, nbv, nb = self.get_nbformat_nbv_nb(title=None) diff --git a/abipy/dfpt/tests/test_ddb.py b/abipy/dfpt/tests/test_ddb.py index f978f5b1..890928e3 100644 --- a/abipy/dfpt/tests/test_ddb.py +++ b/abipy/dfpt/tests/test_ddb.py @@ -437,7 +437,7 @@ class DdbRobotTest(AbipyTest): # Test anacompare_elastic ddb_header_keys=["nkpt", "tsmear"] - r = robot.anacompare_elastic(ddb_header_keys=ddb_header_keys, + r = robot.anacompare_elastic(ddb_header_keys=ddb_header_keys, with_path=True, with_structure=True, with_spglib=False, relaxed_ion="automatic", piezo="automatic", verbose=1) df, edata_list = r.df, r.elastdata_list assert "tensor_name" in df.keys() diff --git a/abipy/examples/flows/README.txt b/abipy/examples/flows/README.txt index 04c75ef9..32e583ff 100644 --- a/abipy/examples/flows/README.txt +++ b/abipy/examples/flows/README.txt @@ -7,7 +7,7 @@ This gallery contains python scripts to generate AbiPy flows from the command li Run the scripts to generate the directory with the flow and then use :ref:`abirun.py` to execute the flow. Alternatively, one can use the ``-s`` option to generate the flow and run it immediately with the scheduler. -Use ``--help`` to further information on the available options. +Use ``--help`` for further information on the available options. .. warning:: diff --git a/abipy/examples/plot/README.txt b/abipy/examples/plot/README.txt index b7cdf97f..e916827a 100644 --- a/abipy/examples/plot/README.txt +++ b/abipy/examples/plot/README.txt @@ -11,4 +11,8 @@ the :ref:`abiopen.py` script and the command:: abiopen.py FILE -nb -or use one of the options of :ref:`abiview.py` to plot the results automatically. +issue + + abiopen.py FILE --expose + +to generate plots automatically or use one of the options of :ref:`abiview.py` to plot the results automatically. diff --git a/abipy/integration_tests/TODO.md b/abipy/integration_tests/TODO.md index a61a6839..2a88f4f9 100644 --- a/abipy/integration_tests/TODO.md +++ b/abipy/integration_tests/TODO.md @@ -32,6 +32,8 @@ TODO list: * Read forces in read_structure ? +* Automate CHANGELOG creation. + ## Medium priority * remove phononflow @@ -93,8 +95,6 @@ TODO list: * DONE plot_networkx does not work with flows containing callbacks e.g. run_qptdm_flow FIXED with graphviz -* DONE Use graphvix in flow sphinx-gallery - * Check xsf_write_data and visualization of potentials. * Add phbands.to_bxsf and histogram for phonon modes at a given q-point. @@ -132,8 +132,6 @@ TODO list: * Remove GUI code. -* _repr_html_ for structure and other basic objects (pymatgen/and abipy) - * nbjsmol (build system, refactor API?) * fatbands with SOC (waiting for Matthieu's refactoring) @@ -150,3 +148,7 @@ TODO list: * Replace SIGRES with new fileformat based on SIGEPH (long-term) * Update spack recipe, add support for EasyBuild, revamp homebrew (?) + +* Classification of phonons/electrons + +* Error handler for tolwfr to increase nband / nbdduf and resubmit diff --git a/docs/flows_howto.rst b/docs/flows_howto.rst index 508464ee..bf45f1a2 100644 --- a/docs/flows_howto.rst +++ b/docs/flows_howto.rst @@ -16,10 +16,10 @@ Feel free to suggest new entries! Suggestions: * Start with the examples available in examples/flows before embarking on large scale calculations. -* Make sure the Abinit executable compiled on the machine can be executed both on the frontend +* Make sure the Abinit executable compiled on the machine can be executed both on the front end and the compute node (ask your sysadmin) * If you are running on clusters in which the architecture of the compute node is completely different - from the one available on the frontend, use ``shell_runner`` + from the one available on the front end, use ``shell_runner`` * Use the ``debug`` command Do not: @@ -91,7 +91,7 @@ When running many calculations, Use ``prtwf -1`` to tell Abinit to produce the wavefunction file only if SCF cycle didn't converged so that AbiPy can reuse the file to restart the calculation. -Note that it's possibile to use:: +Note that it's possible to use:: flow.use_smartio() @@ -101,9 +101,25 @@ for their children. How to extend tasks/works with specialized code ----------------------------------------------- -Remember that pickle_ does not support classes defined inside scripts. +Remember that pickle_ does not support classes defined inside scripts (`__main__`). +This means that `abirun.py` will likely raise an exception when trying to +reconstruct the object from the pickle file: + +.. code-block:: python + + AttributeError: Cannot get attribute 'MyWork' on