Skip to content

Commit

Permalink
Add plotly option to add_fig_kwargs
Browse files Browse the repository at this point in the history
  • Loading branch information
gmatteo committed Jul 21, 2024
1 parent 24115c5 commit 220f9dc
Show file tree
Hide file tree
Showing 7 changed files with 261 additions and 13 deletions.
50 changes: 50 additions & 0 deletions .github/workflows/gh-pages.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
name: Deploy documentation with GitHub Pages dependencies preinstalled

on:
push:
branches: ["develop"]
workflow_dispatch: # enable manual workflow execution

# Set permissions of the GITHUB_TOKEN to allow deployment to GitHub Pages
permissions:
contents: read
pages: write
id-token: write

# Allow only one concurrent deployment, skipping runs queued between the run in-progress and latest queued.
# However, do NOT cancel in-progress runs as we want to allow these production deployments to complete.
concurrency:
group: "pages"
cancel-in-progress: false

jobs:
build:
# prevent this action from running on forks
if: github.repository == 'abinit/abipy'
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4

- name: Setup Pages
uses: actions/configure-pages@v3

- name: Build with Jekyll
uses: actions/jekyll-build-pages@v1
with:
source: ./docs
destination: ./_site

- name: Upload artifact
uses: actions/upload-pages-artifact@v2

deploy:
environment:
name: github-pages
url: ${{ steps.deployment.outputs.page_url }}
runs-on: ubuntu-latest
needs: build
steps:
- name: Deploy to GitHub Pages
id: deployment
uses: actions/deploy-pages@v2
81 changes: 81 additions & 0 deletions .github/workflows/test.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
# Runs the complete test suite.
name: Tests

on:
push:
branches: [develop]
paths-ignore: ["**/*.md", docs/**]
pull_request:
branches: [develop]
paths-ignore: ["**/*.md", docs/**]
workflow_dispatch:
workflow_call: # make this workflow reusable by release.yml

permissions:
contents: read

jobs:
test:
# prevent this action from running on forks
if: github.repository == 'abinit/abipy'
defaults:
run:
shell: bash -l {0} # enables conda/mamba env activation by reading bash profile
strategy:
fail-fast: false
matrix:
# maximize CI coverage of different platforms and python versions while minimizing the
# total number of jobs. We run all pytest splits with the oldest supported python
# version (currently 3.9) on windows (seems most likely to surface errors) and with
# newest version (currently 3.12) on ubuntu (to get complete coverage on unix).
config:
- os: windows-latest
python: "3.9"
resolution: highest
extras: ci,optional
- os: ubuntu-latest
python: '>3.9'
resolution: lowest-direct
extras: ci,optional
- os: macos-latest
python: '3.10'
resolution: lowest-direct
extras: ci # test with only required dependencies installed

# pytest-split automatically distributes work load so parallel jobs finish in similar time
# update durations file with `pytest --store-durations --durations-path tests/files/.pytest-split-durations`
split: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]

runs-on: ${{ matrix.config.os }}

#env:
# PMG_MAPI_KEY: ${{ secrets.PMG_MAPI_KEY }}

steps:
- name: Check out repo
uses: actions/checkout@v4

- name: Set up micromamba
uses: mamba-org/setup-micromamba@main

- name: Create mamba environment
run: |
micromamba create -n abipy python=${{ matrix.config.python }} --yes
- name: Install uv
run: micromamba run -n abipy pip install uv

#- name: Install ubuntu-only conda dependencies
# if: matrix.config.os == 'ubuntu-latest'
# run: |
# micromamba install -n abipy -c conda-forge enumlib packmol bader openbabel openff-toolkit --yes

- name: Install pymatgen and dependencies
run: |
micromamba activate abipy
uv pip install --editable '.[${{ matrix.config.extras }}]' --resolution=${{ matrix.config.resolution }}
- name: pytest split ${{ matrix.split }}
run: |
micromamba activate abipy
pytest --splits 10 --group ${{ matrix.split }} --durations-path tests/files/.pytest-split-durations tests
3 changes: 0 additions & 3 deletions abipy/eph/varpeq.py
Original file line number Diff line number Diff line change
Expand Up @@ -159,9 +159,6 @@ def to_string(self, verbose=0) -> str:
app(self.structure.to_string(verbose=verbose, title="Structure"))
app("")
app(self.ebands.to_string(with_structure=False, verbose=verbose, title="Electronic Bands"))
#if verbose > 1:
# app("")
# app(self.hdr.to_string(verbose=verbose, title="Abinit Header"))

app("")
app("VARPEQ parameters:")
Expand Down
2 changes: 1 addition & 1 deletion abipy/ppcodes/oncv_parser.py
Original file line number Diff line number Diff line change
Expand Up @@ -531,7 +531,7 @@ def kin_densities(self) -> dict[str, RadialFunction]:
@lazy_property
def vtaus(self) -> dict[str, RadialFunction]:
"""
Dictionary with Vtau pototentials on the radial mesh.
Dictionary with Vtau ptotentials on the radial mesh.
"""
if not self.is_metapsp:
raise ValueEror("kin_densities are only available in pseudos generated with metapsp")
Expand Down
11 changes: 8 additions & 3 deletions abipy/ppcodes/oncv_plotter.py
Original file line number Diff line number Diff line change
Expand Up @@ -327,11 +327,12 @@ def plotly_vtau(self, *args, **kwargs):
from plotly.tools import mpl_to_plotly
return mpl_to_plotly(self.plot_vtau(*args, show=False, **kwargs))

def plot_vtau(self, ax=None, fontsize: int = 8, **kwargs) -> Figure:
def plot_vtau(self, xscale="log", ax=None, fontsize: int = 8, **kwargs) -> Figure:
"""
Plot v_tau and v_tau(model+pseudo) potentials on axis ax.
Args:
xscale: "log" to plot vtau in log scale or "linear". For other options see matplotlib.
ax: |matplotlib-Axes| or None if a new figure should be created.
"""
ax, fig, plt = get_ax_fig_plt(ax)
Expand All @@ -347,23 +348,26 @@ def plot_vtau(self, ax=None, fontsize: int = 8, **kwargs) -> Figure:
fontsize=fontsize,
)
self._add_rc_vlines_ax(ax, with_lloc=True)
ax.set_xscale(xscale)

return fig

def plotly_tau(self, *args, **kwargs):
"""Generate plotly figure from matplotly."""
from plotly.tools import mpl_to_plotly
return mpl_to_plotly(self.plot_tau(*args, show=False, **kwargs))

def plot_tau(self, ax=None, fontsize: int = 8, **kwargs) -> Figure:
def plot_tau(self, ax=None, yscale="log", fontsize: int = 8, **kwargs) -> Figure:
"""
Plot kinetic energy densities tauPS and tau(M+PS) on axis ax.
Args:
yscale: "log" to plot tau in log scale or "linear". For other options see matplotlib.
ax: |matplotlib-Axes| or None if a new figure should be created.
"""
ax, fig, plt = get_ax_fig_plt(ax)

for key, den in self.parser.kin_densities.items():
#mode = "ae" if key == "tau_ps" else "ps"
ax.plot(den.rmesh, den.values,
label=den.name)
#**self._mpl_opts_laeps(0, mode))
Expand All @@ -373,6 +377,7 @@ def plot_tau(self, ax=None, fontsize: int = 8, **kwargs) -> Figure:
fontsize=fontsize,
)
self._add_rc_vlines_ax(ax, with_lloc=True)
ax.set_yscale(yscale)

return fig

Expand Down
4 changes: 4 additions & 0 deletions abipy/ppcodes/tests/test_oncvpsp.py
Original file line number Diff line number Diff line change
Expand Up @@ -474,6 +474,10 @@ def _call_plotter_methods(self, plotter):
assert plotter.plot_atanlogder_econv(show=False)
assert plotter.plot_den_formfact(ecut=20, show=False)

if isinstance(plotter, OncvParser) and plotter.parser.is_metapsp:
assert plotter.plot_vtau(show=False)
assert plotter.plot_tau(show=False)

#if self.has_plotly():

def test_psp8_get_densities(self):
Expand Down
123 changes: 117 additions & 6 deletions abipy/tools/plotting.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,12 +19,12 @@
from collections import namedtuple, OrderedDict
from typing import Any, Callable, Iterator
from monty.string import list_strings
from pymatgen.util.plotting import add_fig_kwargs
#from pymatgen.util.plotting import add_fig_kwargs
from abipy.tools import duck
from abipy.tools.iotools import dataframe_from_filepath
from abipy.tools.typing import Figure, Axes, VectorLike
from abipy.tools.numtools import data_from_cplx_mode
from plotly.tools import mpl_to_plotly


__all__ = [
"set_axlims",
Expand Down Expand Up @@ -66,6 +66,115 @@
)



def add_fig_kwargs(func):
"""Decorator that adds keyword arguments for functions returning matplotlib
figures.
The function should return either a matplotlib figure or None to signal
some sort of error/unexpected event.
See doc string below for the list of supported options.
"""

@functools.wraps(func)
def wrapper(*args, **kwargs):
# pop the kwds used by the decorator.
title = kwargs.pop("title", None)
size_kwargs = kwargs.pop("size_kwargs", None)
show = kwargs.pop("show", True)
savefig = kwargs.pop("savefig", None)
tight_layout = kwargs.pop("tight_layout", False)
ax_grid = kwargs.pop("ax_grid", None)
ax_annotate = kwargs.pop("ax_annotate", None)
fig_close = kwargs.pop("fig_close", False)
plotly = kwargs.pop("fig_close", False)

# Call func and return immediately if None is returned.
fig = func(*args, **kwargs)
if fig is None:
return fig

# Operate on matplotlib figure.
if title is not None:
fig.suptitle(title)

if size_kwargs is not None:
fig.set_size_inches(size_kwargs.pop("w"), size_kwargs.pop("h"), **size_kwargs)

if ax_grid is not None:
for ax in fig.axes:
ax.grid(bool(ax_grid))

if ax_annotate:
tags = ascii_letters
if len(fig.axes) > len(tags):
tags = (1 + len(ascii_letters) // len(fig.axes)) * ascii_letters
for ax, tag in zip(fig.axes, tags):
ax.annotate(f"({tag})", xy=(0.05, 0.95), xycoords="axes fraction")

if tight_layout:
try:
fig.tight_layout()
except Exception as exc:
# For some unknown reason, this problem shows up only on travis.
# https://stackoverflow.com/questions/22708888/valueerror-when-using-matplotlib-tight-layout
print("Ignoring Exception raised by fig.tight_layout\n", str(exc))

if savefig:
fig.savefig(savefig)

if plotly:
try:
plotly_fig = mpl_to_ply(fig, latex=False)
if show: plotly_fig.show()
return plotly_fig
except Exception as exc:
raise
#print(str(exc))
pass

import matplotlib.pyplot as plt
if show:
plt.show()

if fig_close:
plt.close(fig=fig)

return fig

# Add docstring to the decorated method.
doc_str = """\n\n
Keyword arguments controlling the display of the figure:
================ ====================================================
kwargs Meaning
================ ====================================================
title Title of the plot (Default: None).
show True to show the figure (default: True).
savefig "abc.png" or "abc.eps" to save the figure to a file.
size_kwargs Dictionary with options passed to fig.set_size_inches
e.g. size_kwargs=dict(w=3, h=4)
tight_layout True to call fig.tight_layout (default: False)
ax_grid True (False) to add (remove) grid from all axes in fig.
Default: None i.e. fig is left unchanged.
ax_annotate Add labels to subplots e.g. (a), (b).
Default: False
fig_close Close figure. Default: False.
plotly Try to convert mpl figure to plotly.
================ ====================================================
"""

if wrapper.__doc__ is not None:
# Add s at the end of the docstring.
wrapper.__doc__ += f"\n{doc_str}"
else:
# Use s
wrapper.__doc__ = doc_str

return wrapper


class FilesPlotter:
"""
Use matplotlib to plot multiple png files on a grid.
Expand Down Expand Up @@ -1743,9 +1852,7 @@ def add_plotly_fig_kwargs(func: Callable) -> Callable:
sort of error/unexpected event.
See doc string below for the list of supported options.
"""
from functools import wraps

@wraps(func)
@functools.wraps(func)
def wrapper(*args, **kwargs):
# pop the kwds used by the decorator.
title = kwargs.pop("title", None)
Expand Down Expand Up @@ -2647,6 +2754,8 @@ def add_colorscale_dropwdowns(fig):
return fig


#TODO: Add plotly option to add_fig_kwargs

def mpl_to_ply(fig: Figure, latex: bool= False):
"""
Nasty workaround for plotly latex rendering in legend/breaking exception
Expand Down Expand Up @@ -2699,6 +2808,7 @@ def parse_latex(label):
text.set_text(new_label)

# Convert to plotly figure
from plotly.tools import mpl_to_plotly
plotly_fig = mpl_to_plotly(fig)

plotly_fig.update_layout(template = "plotly_white", title = {
Expand Down Expand Up @@ -2726,7 +2836,6 @@ def parse_latex(label):
for trace in plotly_fig.data:
# Retrieve the current label and remove any $ signs
new_label = trace.name.replace("$", "")

# Update the trace's name (which is used for the legend label)
trace.name = new_label

Expand Down Expand Up @@ -2784,3 +2893,5 @@ def plot(self, deg_list: list[int],
#
# def extrapolate_to_zero(self, deg: int):



0 comments on commit 220f9dc

Please sign in to comment.