diff --git a/README.rst b/README.rst index 784f9c85b..2075744cc 100644 --- a/README.rst +++ b/README.rst @@ -74,12 +74,8 @@ The installation process is greatly simplified if you install the required python packages through `Anaconda `_ (or conda). See `Installing conda`_ to install conda itself. We routinely use conda_ to test new developments with multiple Python versions and multiple virtual environments. -The anaconda distribution already provides the most critical dependencies (matplotlib_, scipy_, numpy_, netcdf4-python_) -in the form of pre-compiled packages that can be easily installed with e.g.:: - conda install numpy scipy netcdf4 - -Create a new conda_ environment (let's call it ``abienv``) with:: +Create a new conda_ environment based on python 3.11 (let's call it ``abienv``) with:: conda create --name abienv python=3.11 @@ -89,15 +85,11 @@ and activate it with:: You should see the name of the conda environment in the shell prompt. -Now add ``conda-forge`` to your conda channels with:: - - conda config --add channels conda-forge - -This is the channel from which we will download pymatgen, abipy and abinit. - Finally, install AbiPy with:: - conda install abipy + conda install abipy -c conda-forge + +Please note that, at present, conda-forge does not provide executables Additional information on the steps required to install AbiPy with anaconda are available in the `anaconda howto `_. @@ -199,7 +191,6 @@ Otherwise, follow the usual abinit installation instructions, and make sure abin abinit --version - Configuration files for Abipy ============================= @@ -379,6 +370,7 @@ The following scripts can be invoked directly from the terminal: * ``abirun.py`` Execute AbiPy flow from terminal. * ``abidoc.py`` Document Abinit input variables and Abipy configuration files. * ``abinp.py`` Build input files (simplified interface for the AbiPy factory functions). +* ``abipsp.py`` Download pseudopotential tables from the PseudoDojo. Use ``SCRIPT --help`` to get the list of supported commands and ``SCRIPT COMMAND --help`` to get the documentation for ``COMMAND``. diff --git a/abipy/abio/robots.py b/abipy/abio/robots.py index defed2ace..809be67c9 100644 --- a/abipy/abio/robots.py +++ b/abipy/abio/robots.py @@ -1266,7 +1266,7 @@ def plot_abs_conv(ax1, ax2, xs, yvals, abs_conv, xlabel, fontsize, hatch, **kwar """ y_xmax = yvals[-1] span_style = dict(alpha=0.2, color="green", hatch=hatch) - ax1.axhspan(y_xmax - abs_conv, y_xmax + abs_conv, label=r"$|y-y(x_{max})}| \leq %s$" % abs_conv, **span_style) + ax1.axhspan(y_xmax - abs_conv, y_xmax + abs_conv, label=r"$|y-y(x_{max})| \leq %s$" % abs_conv, **span_style) # Plot |y - y_xmax| in log scale on ax2. ax2.plot(xs, np.abs(yvals - y_xmax), **kwargs) diff --git a/abipy/ml/aseml.py b/abipy/ml/aseml.py index 5d40abbb5..823bdc154 100644 --- a/abipy/ml/aseml.py +++ b/abipy/ml/aseml.py @@ -1011,8 +1011,24 @@ def traj(self): raise RuntimeError("Cannot read ASE traj as traj_path is None") return read(self.traj_path, index=":") - #def __str__(self): - #def to_string(self, verbose=0) + def __str__(self) -> str: + return to_string() + + def to_string(self, verbose: int = 0) -> str: + """ + String representation with verbosity level verbose + """ + lines = [] + app = lines.append + app("Initial structure:") + s0 = Structure.as_structure(self.r0.atoms) + app(str(s0)) + app("") + app("Relaxed structure:") + s1 = Structure.as_structure(self.r1.atoms) + app(str(s1)) + + return "\n".join(lines) def summarize(self, tags=None, mode="smart", stream=sys.stdout): """""" @@ -2042,6 +2058,8 @@ def run(self): relax = relax_atoms(self.atoms, **relax_kws) relax.summarize(tags=["unrelaxed", "relaxed"]) + print(relax.to_string(verbose=self.verbose)) + # Write files with final structure and dynamics. formats = ["poscar",] outpath_fmt = write_atoms(self.atoms, workdir, self.verbose, formats=formats) diff --git a/abipy/scripts/abicomp.py b/abipy/scripts/abicomp.py index fc4d6ec9d..3e2b9010e 100755 --- a/abipy/scripts/abicomp.py +++ b/abipy/scripts/abicomp.py @@ -592,6 +592,25 @@ def abicomp_abiwan(options): return _invoke_robot(options) +def abicomp_abiwan_ebands(options): + """ + Compare Wannier-interpolated band structure with ab-initio results. + """ + if len(options.paths) != 2: + raise ValueError("Two arguments with ABIWAN.nc and nc file with ElectronBands are required.") + from abipy.wannier90 import AbiwanFile + abiwan_path, ebands_path = options.paths[0], options.paths[1] + if not abiwan_path.endswith("ABIWAN.nc"): + abiwan_path, ebands_path = ebands_path, abiwan_path + + abiwan = AbiwanFile(abiwan_path) + print(abiwan) + abiwan.hwan.plot() + abiwan.plot_with_ebands(ebands_path) + + return 0 + + def abicomp_pseudos(options): """"Compare multiple pseudos and print table to terminal.""" # Make sure entries in index are unique. @@ -841,6 +860,7 @@ def get_epilog(): abicomp.py edos *_WFK.nc -nb => Compare electron DOS in the jupyter notebook. abicomp.py optic DIR -nb => Compare optic results in the jupyter notebook. abicomp.py abiwan *_ABIWAN.nc --expose => Compare ABIWAN results, produce matplotlib figures. + abicomp.py abiwan_ebands out_ABIWAN.nc out_GSR.nc --expose => Compare Wannier-interpolated band structure with ab-initio results. ######### # Phonons @@ -995,7 +1015,6 @@ def get_parser(with_epilog=False): # Parent parser for commands supporting expose expose_parser = argparse.ArgumentParser(add_help=False) - expose_parser.add_argument("-e", '--expose', default=False, action="store_true", help='Execute robot.expose to produce a pre-defined list of (matplotlib|plotly) figures.') expose_parser.add_argument("-s", "--slide-mode", default=False, action="store_true", @@ -1148,6 +1167,9 @@ def get_parser(with_epilog=False): p_abiwan = subparsers.add_parser('abiwan', parents=robot_parents, help=abicomp_abiwan.__doc__) p_gwr = subparsers.add_parser('gwr', parents=robot_parents, help=abicomp_gwr.__doc__) + # Subparser for abiwan_ebands command. + p_abiwan_ebands = subparsers.add_parser('abiwan_ebands', parents=[copts_parser], help=abicomp_abiwan_ebands.__doc__) + # Subparser for pseudos command. p_pseudos = subparsers.add_parser('pseudos', parents=[copts_parser], help=abicomp_pseudos.__doc__) p_pspsp = subparsers.add_parser('psps', parents=robot_parents, help=abicomp_psps.__doc__) diff --git a/abipy/wannier90/abiwan.py b/abipy/wannier90/abiwan.py index 6daedf237..83a69d252 100644 --- a/abipy/wannier90/abiwan.py +++ b/abipy/wannier90/abiwan.py @@ -351,6 +351,16 @@ def interpolate_ebands(self, vertices_names=None, line_density=20, occfacts, self.ebands.nelect, self.nspinor, self.nspden, smearing=self.ebands.smearing) + @add_fig_kwargs + def plot_with_ebands(self, ebands, **kwargs): + """ + Receiven an ab-initio electronic strucuture, interpolate the energies on the same list of k-points + and compare the two. + """ + plotter = self.get_plotter_from_ebands(ebands) + linestyle_dict = {"Interpolated": dict(linewidth=0, color="red", marker="o")} + return plotter.combiplot(linestyle_dict=linestyle_dict, **kwargs) + def get_plotter_from_ebands(self, ebands: ElectronBands) -> ElectronBandsPlotter: """ Interpolate energies using the k-points given in input |ElectronBands| ebands. @@ -608,7 +618,7 @@ def get_interpolated_ebands_plotter(self, vertices_names=None, knames=None, line diff_str = self.has_different_structures() if diff_str: cprint(diff_str, "yellow") - # Need KpointList object (assume same structures in Robot) + # Need KpointList object (assuming same structures in the Robot) nc0 = self.abifiles[0] if kpoints is None: if ngkpt is not None: @@ -658,6 +668,6 @@ def write_notebook(self, nbpath=None) -> str: # Mixins #nb.cells.extend(self.get_baserobot_code_cells()) - #nb.cells.extend(self.get_ebands_code_cells()) + #nb.cells.extend(self.get_ebands_code_cells())wannier90.wout return self._write_nb_nbpath(nb, nbpath) diff --git a/abipy/wannier90/win.py b/abipy/wannier90/win.py index 5143d7ce7..110f68d4e 100644 --- a/abipy/wannier90/win.py +++ b/abipy/wannier90/win.py @@ -11,8 +11,10 @@ from abipy.abio.inputs import AbstractInput #from abipy.tools.typing import Figure +import abipy.core.abinit_units as abu -def structure2wannier90(structure) -> str: + +def structure2wannier90(structure, units="Bohr") -> str: """ Return string with stucture in wannier90 format. """ @@ -27,8 +29,17 @@ def structure2wannier90(structure) -> str: # Write lattice vectors. # Set small values to zero. This usually happens when the CIF file # does not give structure parameters with enough digits. - app("begin unit_cell_cart\nAng") + if units == "Bohr": + fact = abu.Ang_Bohr + app("begin unit_cell_cart\nBohr") + elif units == "Ang": + fact = 1.0 + app("begin unit_cell_cart\nAng") + else: + raise ValueError(f"Invalid {units =}") + for r in np.where(np.abs(structure.lattice.matrix) > 1e-8, structure.lattice.matrix, 0.0): + r = r * fact app(" %.10f %.10f %.10f" % (r[0], r[1], r[2])) app("end unit_cell_cart\n") app("begin atoms_frac") diff --git a/abipy/wannier90/wout.py b/abipy/wannier90/wout.py index 970a8d886..7d4b8d723 100644 --- a/abipy/wannier90/wout.py +++ b/abipy/wannier90/wout.py @@ -46,7 +46,7 @@ def __init__(self, filepath): def close(self) -> None: """Close file. Required by abc protocol.""" - def __str__(self) ->str: + def __str__(self) -> str: return self.to_string() def to_string(self, verbose=0) -> str: @@ -337,7 +337,7 @@ def plot(self, fontsize=8, **kwargs) -> Figure: def plot_centers_spread(self, fontsize=8, **kwargs) -> Figure: """ Plot the convergence of the Wannier centers and spread - as function of iteration number + as a function of the iteration number Args: fontsize: legend and label fontsize.