Skip to content

Commit

Permalink
Merge branch 'dev' into extend_floorplot
Browse files Browse the repository at this point in the history
  • Loading branch information
eltos authored Jan 24, 2025
2 parents 58db1f7 + a2a2e86 commit 03db083
Show file tree
Hide file tree
Showing 15 changed files with 224 additions and 122 deletions.
7 changes: 6 additions & 1 deletion .github/workflows/release.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ jobs:
- run: |
VERSION=$(git describe --tags)
echo "Version: $VERSION"
if [[ $VERSION =~ v[0-9]+(\.[0-9]+)*$ ]] ; then
if [[ $VERSION =~ v[0-9]+\.[0-9]+\.[0-9]+(-rc[0-9]+)?$ ]] ; then
echo "Ready for publication"
else
echo "Not publishing dirty versions"
Expand Down Expand Up @@ -80,6 +80,8 @@ jobs:
sphinx-build docs _build/html
VERSION=$(python -c "from docs.conf import version;print('.'.join(version.split('.')[:2]))")
echo "VERSION=$VERSION" >> $GITHUB_ENV
IS_CLEAN=$(python -c "from docs.conf import version;import re;print(1 if re.fullmatch('\d+(.\d+(.\d+)?)?',version) else 0)")
echo "IS_CLEAN=$IS_CLEAN" >> $GITHUB_ENV
- name: Deploy docs
run: |
git config user.name "GitHub Actions"
Expand All @@ -88,6 +90,9 @@ jobs:
# Copy to version folder
rm -rf $VERSION
cp -r _build/html $VERSION
if [[ "$IS_CLEAN" != "1" ]] ; then
touch $VERSION/.pre-release
fi
# Update docs
./update.py
cat versions.json
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/test.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ jobs:
extra_args: --all-files --hook-stage=manual

versioning:
if: ${{ github.ref_name != 'main' }}
if: ${{ github.ref_type == 'branch' && github.ref_name != 'main'}}
runs-on: ubuntu-24.04
steps:
- uses: actions/checkout@v4
Expand Down
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ A plotting library for [Xsuite](https://github.com/xsuite) and simmilar accelera
## Usage

```bash
pip install xplt
pip install xplt[full]
```

Read the docs at https://xsuite.github.io/xplt
Expand Down
6 changes: 5 additions & 1 deletion docs/quickstart.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,13 @@
## Installation

```bash
pip install xplt
pip install xplt[full]
```

It is recommended to install the library with all optional dependencies, to make use its full functionality.
For a minimal installation without `[full]`, certain features like automatic unit conversion and resolving or support for pandas dataframes are disabled.


## Gallery

Click on the plots below to show the respective section in the [User guide](usage):
Expand Down
2 changes: 1 addition & 1 deletion examples/concepts.ipynb
Original file line number Diff line number Diff line change
Expand Up @@ -276,7 +276,7 @@
],
"source": [
"plot.add_dataset(\n",
" \"ref\",\n",
" \"ref\", # optional ID to update data later\n",
" particles=data2,\n",
" plot_kwargs=dict(label=\"Data B\", c=\"C2\", lw=2),\n",
" autoscale=True,\n",
Expand Down
8 changes: 7 additions & 1 deletion examples/timestructure.ipynb
Original file line number Diff line number Diff line change
Expand Up @@ -627,6 +627,10 @@
"execution_count": 11,
"id": "e5c7d119-18e1-45a9-83b0-901d954d8784",
"metadata": {
"editable": true,
"slideshow": {
"slide_type": ""
},
"tags": [
"remove-stderr"
]
Expand Down Expand Up @@ -872,7 +876,9 @@
"slideshow": {
"slide_type": ""
},
"tags": []
"tags": [
"remove-stderr"
]
},
"outputs": [
{
Expand Down
6 changes: 4 additions & 2 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,6 @@ dependencies = [
"matplotlib>=3.6",
"numpy>=1.17.0",
"scipy>=1.2.0",
"pint>=0.24.1",
]
dynamic = ["version"]

Expand All @@ -36,7 +35,10 @@ documentation = "https://xsuite.github.io/xplt"
repository = "https://github.com/xsuite/xplt"

[project.optional-dependencies]
all = ["pandas"]
full = [
"pandas",
"pint>=0.24.1",
]


# Build tools
Expand Down
73 changes: 42 additions & 31 deletions tests/test_examples.py
Original file line number Diff line number Diff line change
Expand Up @@ -67,39 +67,50 @@ def keep(out):
reference_outputs = list(filter(keep, reference_cell.get("outputs", [])))
actual_outputs = list(filter(keep, actual_cell.get("outputs", [])))

print("reference_outputs:", [o.get("output_type") for o in reference_outputs])
print("actual_outputs:", [o.get("output_type") for o in actual_outputs])

# compare the filtered outputs
for i, ref_out in enumerate(reference_outputs):
output_type = ref_out.get("output_type")
try:
# compare the filtered outputs
for i, ref_out in enumerate(reference_outputs):
output_type = ref_out.get("output_type")

try:
act_out = actual_outputs[i]
except IndexError:
raise ValueError(
f"Expected {len(reference_outputs)} cell outputs, but only {len(actual_outputs)} found"
)

try:
act_out = actual_outputs[i]
except IndexError:
raise ValueError(
f"Expected {len(reference_outputs)} cell outputs, but only {len(actual_outputs)} found"
)
if output_type == "stream": # compare plain text
cls.compare_outputs_text(ref_out.get("text"), act_out.get("text"))

else:
output_data = ref_out.get("data", {})

for mime, rule in {
"image/png": cls.compare_outputs_image,
"text/markdown": cls.compare_outputs_markup,
"text/html": cls.compare_outputs_html,
}.items():
if mime in output_data:
rule(output_data.get(mime), act_out.get("data", {}).get(mime))
break
else: # unknown output type
raise NotImplementedError(
f"No rule to compare output type '{ref_out.get('output_type')}' with data '{ref_out.get('data', {}).keys()}'. \n"
"Currently only 'image/png', 'text/markdown' and 'text/html' are supported."
)

if output_type == "stream": # compare plain text
cls.compare_outputs_text(ref_out.get("text"), act_out.get("text"))

else:
output_data = ref_out.get("data", {})

for mime, rule in {
"image/png": cls.compare_outputs_image,
"text/markdown": cls.compare_outputs_markup,
"text/html": cls.compare_outputs_html,
}.items():
if mime in output_data:
rule(output_data.get(mime), act_out.get("data", {}).get(mime))
break
else: # unknown output type
raise NotImplementedError(
f"No rule to compare output type '{ref_out.get('output_type')}' with data '{ref_out.get('data', {}).keys()}'. \n"
"Currently only 'image/png', 'text/markdown' and 'text/html' are supported."
)
except:
for label, outputs in (
("Reference outputs:", reference_outputs),
("Actual outputs:", actual_outputs),
):
print(label)
for o in outputs:
print(o.get("output_type"))
print(o.get("text", ""))
print()

raise

@classmethod
def compare_outputs_text(cls, reference_text, actual_text):
Expand Down
5 changes: 3 additions & 2 deletions xplt/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
__contact__ = "[email protected]"


__version__ = "0.11.2"
__version__ = "0.11.3-rc6"


# expose the following in global namespace
Expand All @@ -16,6 +16,7 @@
from .properties import *
from .timestructure import *
from .twiss import *
from .util import AUTO

# allow usage of xplt.mpl.* without importing matplotlib
import matplotlib as mpl
Expand Down Expand Up @@ -45,10 +46,10 @@ class TimeVariationScalePlot(SpillQualityTimescalePlot):
_hooks.try_register_hooks()

import matplotlib.style as _mpl_style
import pint.formatting as _pint_formatting


def apply_style():
"""Apply xplt's matplotlib style sheet and update rcParams"""
_mpl_style.use("xplt.xplt")
# import pint.formatting as _pint_formatting
# _pint_formatting.format_default = "X" # use explicit format instead!
33 changes: 24 additions & 9 deletions xplt/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@

import re
import types
import uuid

import matplotlib as mpl
import matplotlib.pyplot as plt
Expand All @@ -20,10 +21,17 @@
import matplotlib.patches
import matplotlib.path
import numpy as np
import pint

from .util import defaults, flattened, defaults_for
from .properties import Property, find_property, DataProperty, arb_unit
from .util import defaults, flattened, defaults_for, AUTO
from .properties import (
Property,
find_property,
DataProperty,
arb_unit,
_convert_value_to_unit,
_fmt_qty,
_has_pint,
)


class ManifoldMultipleLocator(mpl.ticker.MaxNLocator):
Expand Down Expand Up @@ -328,7 +336,9 @@ def __init__(
self._user_properties[name] = (
DataProperty(name, arg) if isinstance(arg, str) else arg
)
self._display_units = defaults(display_units, s="m", x="mm", y="mm", p="mrad")
self._display_units = defaults(
display_units, **dict(s="m", x="mm", y="mm", p="mrad") if _has_pint() else {}
)

if annotation is None:
annotation = ax is None
Expand Down Expand Up @@ -542,7 +552,7 @@ def factor_for(self, p):
Returns:
float: Factor to convert parameter into display unit
"""
return pint.Quantity(1, self.prop(p).unit).to(self.display_unit_for(p)).m
return _convert_value_to_unit(1, self.prop(p).unit, self.display_unit_for(p))

def display_unit_for(self, p):
"""Return display unit for parameter
Expand Down Expand Up @@ -680,9 +690,9 @@ def label_for(self, *pp, unit=True, description=True):
if units[0] == arb_unit: # arbitrary unit
append = " / " + arb_unit
else:
display_unit = pint.Unit(units[0]) # all have the same unit (see above)
if display_unit != pint.Unit("1"):
append = f" / ${display_unit:~X}$" # see "NIST Guide to the SI"
display_unit = units[0] # all have the same unit (see above)
if display_unit and display_unit != "1":
append = f" / ${_fmt_qty(None, display_unit)}$" # see "NIST Guide to the SI"
if append:
# heuristic: if labels contain expressions with +, - or ± then add parentheses
if re.findall(r"([-+±]|\\pm)(?![^(]*\))(?![^{]*\})", label.split(" ")[-1]):
Expand Down Expand Up @@ -879,9 +889,12 @@ def _create_artists(self, callback, dataset_id=None):
Signature: (i, j, k, axis, p) -> artist
Where i, j, k are the subplot, twin-axis, trace indices respectively;
axis is the axis and the string p is the property to plot.
dataset_id (str | None): The dataset identifier if this plot represents multiple datasets
dataset_id (str | None | AUTO): The dataset identifier if this plot represents multiple datasets.
If :const:`xplt.AUTO`, a new UUID is generated.
"""

if dataset_id is AUTO:
dataset_id = str(uuid.uuid4())
if dataset_id in self._artists:
raise ValueError(f"Dataset identifier `{dataset_id}` already exists")
if dataset_id is not None and not isinstance(dataset_id, str):
Expand All @@ -899,6 +912,8 @@ def _create_artists(self, callback, dataset_id=None):

self.legend(show="auto")

return dataset_id

@property
def artists(self):
"""Convenient access to artist
Expand Down
Loading

0 comments on commit 03db083

Please sign in to comment.