From 7088751dde62e597dbfac6c1f8a18bd96b04a648 Mon Sep 17 00:00:00 2001 From: gmatteo Date: Wed, 27 Sep 2023 11:36:50 +0200 Subject: [PATCH] Add kwargs to draw_displaced_parabolas --- README.rst | 78 +++--------------------------------- abipy/lumi/deltaSCF.py | 90 ++++++++++++++++++++---------------------- abipy/ml/aseml.py | 33 ++++++++++++++++ abipy/ml/chgnet.py | 6 +++ abipy/ml/matgl.py | 7 +++- docs/coding_guide.rst | 20 +++++++++- 6 files changed, 111 insertions(+), 123 deletions(-) diff --git a/README.rst b/README.rst index 669e04633..c14fb6636 100644 --- a/README.rst +++ b/README.rst @@ -79,9 +79,9 @@ 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``) based on e.g. python3.6 with:: +Create a new conda_ environment (let's call it ``abienv``) with:: - conda create --name abienv python=3.6 + conda create --name abienv and activate it with:: @@ -116,10 +116,9 @@ For pip, use:: pip install -r requirements.txt pip install -r requirements-optional.txt -If you are using conda_ (see `Installing conda`_ to install conda itself), create a new environment (``abienv``) -based on python3.9 with:: +If you are using conda_ (see `Installing conda`_ to install conda itself), create a new environment (``abienv``) with:: - conda create -n abienv python=3.9 + conda create -n abienv source activate abienv Add ``conda-forge``, and ``abinit`` to your channels with:: @@ -407,7 +406,7 @@ A brief install guide, in case you have not yet used conda ... For a more extens `Anaconda Howto `_. Download the `miniconda installer `_. -Select python3.6 and the version corresponding to your operating system. +Select the version corresponding to your operating system. As an example, if you are a Linux user, download and install `miniconda` on your local machine with:: @@ -431,73 +430,6 @@ Source your ``.bashrc`` file to activate the changes done by ``miniconda`` to yo .. _troubleshooting: -Troubleshooting -=============== - -GLIBC error ------------ - -The python interpreter may raise the following exception when importing one of the pymatgen modules:: - - from pymatgen.util.coord import pbc_shortest_vectors - File "/python3.6/site-packages/pymatgen/util/coord.py", line 11, in - from . import coord_cython as cuc - ImportError: /lib64/libc.so.6: version `GLIBC_2.14' not found (required by /python3.6/site-packages/pymatgen/util/coord_cython.cpython-36m-x86_64-linux-gnu.so)` - -This means that the pre-compiled version of pymatgen is not compatible with the GLIBC version available on your machine. -To solve the problem, we suggest to build and install pymatgen from source using the local version of GLIBC and the gcc compiler. -In the example below, we use a conda environment to install most of the dependencies with the exception of pymatgen and abipy. - -Let's start by creating a conda environment with:: - - conda create -n glibc_env python=3.6 - source activate glibc_env - conda config --add channels conda-forge - -Use pip to install spglib:: - - pip install spglib - -and try to ``import spglib`` inside the python terminal. - -Download the pymatgen repository from github with:: - - git clone https://github.com/materialsproject/pymatgen.git - cd pymatgen - -If git is not installed, use ``conda install git`` - -Now use conda to install the pymatgen requirements listed in ``requirements.txt`` -but before that make sure that ``gcc`` is in ``$PATH``. -If you are working on a cluster, you may want to issue:: - - module purge - -to avoid compiling C code with the intel compiler (it's possible to use ``icc`` but ``gcc`` is less problematic). - -Remove the line:: - - enum34==1.1.6; python_version < '3.4' - -from ``requirements.txt`` as this syntax is not supported by conda then issue:: - - conda install -y --file requirements.txt - -At this point, we can build pymatgen and the C extensions:: - - python setup.py install - -then ``cd`` to another directory (important) and test the build inside the python terminal with:: - - import spglib - import pymatgen - -Finally, we can install Abipy from source with:: - - git clone https://github.com/abinit/abipy.git - cd abipy && conda install -y --file ./requirements.txt - - License ======= diff --git a/abipy/lumi/deltaSCF.py b/abipy/lumi/deltaSCF.py index 67d4d26bb..27f2b33ca 100644 --- a/abipy/lumi/deltaSCF.py +++ b/abipy/lumi/deltaSCF.py @@ -1,3 +1,5 @@ +from __future__ import annotations + import numpy as np import json from abipy.core.structure import Structure @@ -15,10 +17,10 @@ class DeltaSCF(): """ - Object to post-process the results from a LumiWork, following a one-effective phonon mode model (1D-CCM). - For equations, notations and formalism, please refer to : + Object to post-process the results from a LumiWork, following a one-effective phonon mode model (1D-CCM). + For equations, notations and formalism, please refer to : https://doi.org/10.1103/PhysRevB.96.125132 - https://doi.org/10.1002/adom.202100649 + https://doi.org/10.1002/adom.202100649 """ @classmethod @@ -203,7 +205,7 @@ def get_dict_per_atom(self,index,defect_symbol): def get_dataframe_atoms(self,defect_symbol): """ Panda dataframe with relevant properties per atom. - units used are + units used are mass - amu DeltaR - Angstrom DeltaQ^2 - amu.Angstrom^2 @@ -229,11 +231,11 @@ def get_dict_per_specie(self,specie): d[r"$\Delta Q^2$"]=specie.atomic_mass*sum(dr_sp) return d - + def get_dataframe_species(self): """ Panda dataframe with relevant properties per species. - units used are + units used are mass - amu DeltaR - Angstrom DeltaQ^2 - amu.Angstrom^2 @@ -265,7 +267,7 @@ def amu_list(self): def delta_q(self,unit='atomic'): """ - Total Delta_Q + Total Delta_Q Args: unit: amu^1/2.Angstrom if unit = 'atomic', kg^1/2.m if 'SI' """ @@ -363,7 +365,7 @@ def S_abs(self): def FWHM_1D(self,T=0): """ - Full width at half-maximum following a semi-classical approx in the 1D-CCM + Full width at half-maximum following a semi-classical approx in the 1D-CCM (eq.20-21 of https://doi.org/10.1103/PhysRevB.96.125132) Args: T: Temperature @@ -380,7 +382,7 @@ def FC_factor_approx(self,n): """ FC factor between initial vib. state m=0 to final vib. state n approx of same eff frequency in gs and ex and T = 0K. - See eq. (9) of https://doi.org/10.1002/adom.202100649 + See eq. (9) of https://doi.org/10.1002/adom.202100649 """ FC = np.exp(self.S_em()) * self.S_em() ** n / math.factorial(n) return FC @@ -388,7 +390,7 @@ def FC_factor_approx(self,n): def lineshape_1D_zero_temp(self,energy_range=[0.5,5],max_m=25,phonon_width=0.01,with_omega_cube=True,normalized='Area'): """ Compute the emission lineshape following the effective phonon 1D-CCM at T=0K. - See eq. (9) of https://doi.org/10.1002/adom.202100649 + See eq. (9) of https://doi.org/10.1002/adom.202100649 Args: energy_range: Energy range at which the intensities are computed, ex : [0.5,5] max_m: Maximal vibrational state m considered @@ -404,7 +406,7 @@ def lineshape_1D_zero_temp(self,energy_range=[0.5,5],max_m=25,phonon_width=0.01, E_x = np.linspace(energy_range[0], energy_range[1], n_x) list_n = np.arange(0, max_m) - A = np.zeros(n_x) + A = np.zeros(n_x) sigma = phonon_width / (2.35482) for n in list_n: #gaussian_1D = np.zeros(n_x) @@ -418,12 +420,12 @@ def lineshape_1D_zero_temp(self,energy_range=[0.5,5],max_m=25,phonon_width=0.01, A=A*E_x**3 if normalized=="Area": - C = 1 / (simps(A, E_x)) + C = 1 / (simps(A, E_x)) if normalized=="Sum": C=1/(max(A)) return E_x, C*A - + @add_fig_kwargs def plot_lineshape_1D_zero_temp(self,energy_range=[0.5,5],max_m=25,phonon_width=0.01,with_omega_cube="True", normalized='Area', ax=None, **kwargs): @@ -437,7 +439,7 @@ def plot_lineshape_1D_zero_temp(self,energy_range=[0.5,5],max_m=25,phonon_width= with_omega_cube: Considered or not the omega^3 dependence of the intensity normlized: Normalisation procedure. 'Area' if Area under the curve = 1 'Sum' if maximum of the curve = 1. - + Returns: |matplotlib-Figure| """ ax, fig, plt = get_ax_fig_plt(ax=ax) @@ -448,7 +450,7 @@ def plot_lineshape_1D_zero_temp(self,energy_range=[0.5,5],max_m=25,phonon_width= ax.set_xlabel(r'Energy (eV)') ax.set_ylabel(r'Intensity ') return fig - + def get_dict_results(self): d=dict([ @@ -470,8 +472,8 @@ def get_dict_results(self): def get_dataframe(self,label=None): """ Panda dataframe with the main results of a LumiWork : transition energies, delta Q, Huang Rhys factor,... - Units used are Angstrom, eV, amu. - DeltaSCF object should be instantiated with the four points files, not with relax files only. + Units used are Angstrom, eV, amu. + DeltaSCF object should be instantiated with the four points files, not with relax files only. """ rows=[] index=[] @@ -482,7 +484,7 @@ def get_dataframe(self,label=None): df=pd.DataFrame(rows,index=index) return df - + @add_fig_kwargs def displacements_visu(self,a_g=10,**kwargs): """ @@ -490,7 +492,7 @@ def displacements_visu(self,a_g=10,**kwargs): Difference between ground state and excited state atomic positions. The colors of the atoms are based on Delta_Q_^2 per atom. For displacement visualisation with VESTA, check https://github.com/lucydot/vesta_vectors - + Args: a_g = coefficient that multiplies the displacement magnitudes Returns: |matplotlib-Figure| @@ -526,9 +528,9 @@ def plot_delta_R_distance(self, defect_symbol,colors=["k","r","g","b","c","m"],a Plot \DeltaR vs distance from defect for each atom, colored by species. Args: ax: |matplotlib-Axes| or None if a new figure should be created. - defect_symbol: defect_symbol, defect location will be the reference + defect_symbol: defect_symbol, defect location will be the reference colors: list of colors for the species - + Returns: |matplotlib-Figure| """ @@ -542,25 +544,25 @@ def plot_delta_R_distance(self, defect_symbol,colors=["k","r","g","b","c","m"],a dfs.append(df.loc[df['symbol'] == symbol]) xs.append(dfs[i]["dist. from defect"]) ys.append(dfs[i]["$\\Delta R$"]) - + ax, fig, plt = get_ax_fig_plt(ax=ax) for i, symbol in enumerate(symbols): ax.stem(xs[i], ys[i], label=symbol, linefmt=colors[i], markerfmt="o" + colors[i],**kwargs) ax.set_xlabel(r'Distance from defect ($\AA$)') ax.set_ylabel(r'$\Delta R $ ($\AA$)') ax.legend() - + return fig - + @add_fig_kwargs def plot_delta_F_distance(self, defect_symbol,colors=["k","r","g","b","c","m"],ax=None, **kwargs): """ Plot \DeltaF vs distance from defect for each atom, colored by species. Args: ax: |matplotlib-Axes| or None if a new figure should be created. - defect_symbol: defect_symbol, defect location will be the reference + defect_symbol: defect_symbol, defect location will be the reference colors: list of colors for the species - + Returns: |matplotlib-Figure| """ @@ -574,16 +576,16 @@ def plot_delta_F_distance(self, defect_symbol,colors=["k","r","g","b","c","m"],a dfs.append(df.loc[df['symbol'] == symbol]) xs.append(dfs[i]["dist. from defect"]) ys.append(dfs[i]["$\\Delta F$"]) - + ax, fig, plt = get_ax_fig_plt(ax=ax) for i, symbol in enumerate(symbols): ax.stem(xs[i], ys[i], label=symbol, linefmt=colors[i], markerfmt="o" + colors[i],**kwargs) ax.set_xlabel(r'Distance from defect ($\AA$)') ax.set_ylabel(r'$\Delta F$ ($eV/\AA$)') ax.legend() - + return fig - + @add_fig_kwargs def plot_four_BandStructures(self,nscf_files,ax_mat=None,ylims=[-5,5],**kwargs): """" @@ -597,31 +599,31 @@ def plot_four_BandStructures(self,nscf_files,ax_mat=None,ylims=[-5,5],**kwargs): ax_mat, fig, plt = get_axarray_fig_plt(ax_mat, nrows=1, ncols=4, sharex=True, sharey=True, squeeze=False) - + titles = [r'$A_g$', r'$A_g^*$', r'$A_e^*$', r'$A_e$'] e0 = ebands[0].fermie - + for i,eband in enumerate(ebands): eband.plot_ax(ax=ax_mat[0,i],spin=0, e0=e0,color="k",**kwargs) eband.plot_ax(ax=ax_mat[0,i],spin=1, e0=e0,color="r",**kwargs) eband.decorate_ax(ax=ax_mat[0,i],title=titles[i]) - + ax_mat[0,0].set_ylim(ylims) ax_mat[0,1].set_ylabel("") ax_mat[0,2].set_ylabel("") ax_mat[0,3].set_ylabel("") - + return fig - + @add_fig_kwargs - def draw_displaced_parabolas(self,ax=None,scale_eff_freq=4,font_size=8): + def draw_displaced_parabolas(self,ax=None,scale_eff_freq=4,font_size=8, **kwargs): """ Draw the four points diagram with relevant transition energies. Args: ax: |matplotlib-Axes| or None if a new figure should be created. - scale_eff_freq: scaling factor to adjust the parabolas curvatures. + scale_eff_freq: scaling factor to adjust the parabolas curvatures. font_size: font size for the annotations - + Returns: |matplotlib-Figure| """ ax,fig,plt=get_ax_fig_plt(ax=ax) @@ -638,7 +640,7 @@ def draw_displaced_parabolas(self,ax=None,scale_eff_freq=4,font_size=8): E_gs=0.5*omega_gs_sq.real*(Qs)**2+0 # ref at (0,0) E_ex=0.5*omega_ex_sq.real*(Qs-delta_Q)**2+ self.E_zpl()# min at (delta_Q,ae_energy) - + # parabolas ax.plot(Qs,E_gs,'k',zorder=1) @@ -650,8 +652,8 @@ def draw_displaced_parabolas(self,ax=None,scale_eff_freq=4,font_size=8): ax.scatter(xs,ys,s=50,color='k',zorder=2) - # arrows - + # arrows + ax.annotate("", xy=(0, E_zpl+0.95*new_FC_ex), xytext=(0, 0), arrowprops=dict(arrowstyle="->",color="b",lw=1)) ax.annotate(r' $E_{abs}$='+format(self.E_abs(),".2f")+' eV ', xy=(0,(E_zpl+new_FC_ex)/2),ha='left',fontsize=font_size) @@ -676,7 +678,7 @@ def draw_displaced_parabolas(self,ax=None,scale_eff_freq=4,font_size=8): ax.annotate("", xy=(0, -new_FC_gs*0.2), xytext=(delta_Q, -new_FC_gs*0.2), arrowprops=dict(arrowstyle="<->",color="k",lw=0.6)) ax.annotate(r'$\Delta Q$ ='+format(self.delta_q(),".2f"), xy=(delta_Q/2, -new_FC_gs*0.4),ha='center',fontsize=font_size) - + ax.set_ylim(-new_FC_gs*1.5,E_zpl+2*new_FC_ex) ax.set_xlim(-0.5*delta_Q,2*delta_Q) @@ -693,9 +695,3 @@ def draw_displaced_parabolas(self,ax=None,scale_eff_freq=4,font_size=8): return fig - - - - - - diff --git a/abipy/ml/aseml.py b/abipy/ml/aseml.py index e2cf3b59a..041d63dfd 100644 --- a/abipy/ml/aseml.py +++ b/abipy/ml/aseml.py @@ -1148,7 +1148,9 @@ class CalcBuilder: "matgl", "chgnet", "alignn", + #"pyace", #"quip", + #"nequip", ] def __init__(self, name: str, **kwargs): @@ -1272,6 +1274,37 @@ class MyAlignnCalculator(_MyCalculator, AlignnAtomwiseCalculator): cls = MyAlignnCalculator if with_delta else AlignnAtomwiseCalculator return cls(path=model_name) + if self.nn_type == "pyace": + try: + from pyace import PyACECalculator + except ImportError as exc: + raise ImportError("pyace not installed. See https://pacemaker.readthedocs.io/en/latest/pacemaker/install/") from exc + + class MyPyACECalculator(_MyCalculator, PyACECalculator): + """Add abi_forces and abi_stress""" + + if self.model_path is None: + raise RuntimeError("PyACECalculator requires model_path e.g. nn_name='pyace:FILEPATH'") + + cls = MyPyACECalculator if with_delta else PyACECalculator + return cls(basis_set=self.model_path) + + if self.nn_type == "nequip": + try: + from nequip.ase.nequip_calculator import NequIPCalculator + except ImportError as exc: + raise ImportError("nequip not installed. See https://github.com/mir-group/nequip") from exc + + class MyNequIPCalculator(_MyCalculator, NequIPCalculator): + """Add abi_forces and abi_stress""" + + if self.model_path is None: + raise RuntimeError("NequIPCalculator requires model_path e.g. nn_name='nequip:FILEPATH'") + + cls = MyNequIPCalculator if with_delta else NequIPCalculator + return cls.from_deployed_model(modle_path=self.model_path, species_to_type_name=None) + + #if self.nn_type == "quip": # try: # from quippy.potential import Potential diff --git a/abipy/ml/chgnet.py b/abipy/ml/chgnet.py index 9c3d722e7..d3ee0af61 100644 --- a/abipy/ml/chgnet.py +++ b/abipy/ml/chgnet.py @@ -14,6 +14,12 @@ class ChgnetSystem: See also https://github.com/CederGroupHub/chgnet/blob/main/examples/fine_tuning.ipynb """ def __init__(self, filepaths, workdir, verbose): + """ + Args: + filepaths: List of files with ab-initio results. + workdir: Working directory. + verbose: Verbosity level. + """ self.filepaths = list_strings(filepaths) self.workdir = workdir self.verbose = verbose diff --git a/abipy/ml/matgl.py b/abipy/ml/matgl.py index 7f793e5ff..389a98062 100644 --- a/abipy/ml/matgl.py +++ b/abipy/ml/matgl.py @@ -25,8 +25,13 @@ class MatglSystem: """ See also: https://github.com/materialsvirtuallab/matgl/blob/main/examples/Training%20a%20M3GNet%20Potential%20with%20PyTorch%20Lightning.ipynb """ - def __init__(self, filepaths, workdir, verbose): + """ + Args: + filepaths: List of files with ab-initio results. + workdir: Working directory. + verbose: Verbosity level. + """ self.filepaths = list_strings(filepaths) self.workdir = workdir self.verbose = verbose diff --git a/docs/coding_guide.rst b/docs/coding_guide.rst index d8e4854b2..b7238e437 100644 --- a/docs/coding_guide.rst +++ b/docs/coding_guide.rst @@ -20,8 +20,6 @@ When committing changes to AbiPy, there are a few things to bear in mind. .. versionadded:: 0.2 Add new argument ``foobar`` -* Are your changes python2.7 compatible? - * Can you pass the automatic tests? * Can you add a test to test your changes? @@ -102,6 +100,7 @@ Writing examples We have examples in subdirectories of :file:`abipy/examples`, and these are automatically generated when the website is built to show up both in the :file:`examples` and :file:`gallery` sections of the website. + Many people find these examples from the website, and do not have ready access to the :file:`examples` directory in which they reside. Thus any example data that is required for the example should be added to the :file:`abipy/data` directory @@ -114,3 +113,20 @@ Abipy has a testing infrastructure based on :mod:`unittest` and pytest_. Common test support is provided by :mod:`abipy.core.testing`, data files are stored in :file:`abipy/data`, in particular in :file:`abipy/data/refs` that contains several output files that can be used for writing unit tests and examples. + +To install pytest with useful plugins, use:: + + pip install -r requirements-tests.txt + + +in the top-level directory of the package. +To run the tests associated to the abio.inputs module, use:: + + pytest -v abio/tests/test_inputs.py + +or use:: + + pytest -v + +to run the entire test suite. +