Skip to content

Commit 428e799

Browse files
tfamprikisTheo Famprikisstefsmeets
authored
Add polar plot of orientation vectors (#358)
* added polar plot of orientation vectors * Add plotly boilerplate (not implemented) * Add test for polar plot * Fix formatting * Fix formatting --------- Co-authored-by: Theo Famprikis <tfamprikis@tudelft.nl> Co-authored-by: Stef Smeets <s.smeets@esciencecenter.nl>
1 parent e902b09 commit 428e799

File tree

8 files changed

+124
-0
lines changed

8 files changed

+124
-0
lines changed

src/gemdat/orientations.py

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -272,6 +272,11 @@ def plot_rectilinear(self, *, module, **kwargs):
272272
"""See [gemdat.plots.rectilinear][] for more info."""
273273
return module.rectilinear(orientations=self, **kwargs)
274274

275+
@plot_backend
276+
def plot_polar(self, *, module, **kwargs):
277+
"""See [gemdat.plots.polar][] for more info."""
278+
return module.polar(orientations=self, **kwargs)
279+
275280
@plot_backend
276281
def plot_bond_length_distribution(self, *, module, **kwargs):
277282
"""See [gemdat.plots.bond_length_distribution][] for more info."""

src/gemdat/plots/__init__.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44

55
from .matplotlib import (
66
jumps_3d_animation,
7+
polar,
78
)
89
from .plotly import (
910
autocorrelation,
@@ -43,6 +44,7 @@
4344
'msd_per_element',
4445
'msd_per_element',
4546
'plot_3d',
47+
'polar',
4648
'radial_distribution',
4749
'rectilinear',
4850
'shape',

src/gemdat/plots/matplotlib/__init__.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
from ._jumps_vs_distance import jumps_vs_distance
1717
from ._jumps_vs_time import jumps_vs_time
1818
from ._msd_per_element import msd_per_element
19+
from ._polar import polar
1920
from ._radial_distribution import radial_distribution
2021
from ._rectilinear import rectilinear
2122
from ._shape import shape
@@ -35,6 +36,7 @@
3536
'jumps_vs_distance',
3637
'jumps_vs_time',
3738
'msd_per_element',
39+
'polar',
3840
'radial_distribution',
3941
'rectilinear',
4042
'shape',

src/gemdat/plots/matplotlib/_polar.py

Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
from __future__ import annotations
2+
3+
from typing import TYPE_CHECKING
4+
5+
import matplotlib.pyplot as plt
6+
import numpy as np
7+
8+
if TYPE_CHECKING:
9+
import matplotlib.figure
10+
11+
from gemdat.orientations import Orientations
12+
13+
14+
def polar(
15+
*,
16+
orientations: Orientations,
17+
shape: tuple[int, int] = (90, 360),
18+
normalize_histo: bool = True,
19+
) -> matplotlib.figure.Figure:
20+
"""Plot a polar projection of a spherical function. This function uses the
21+
transformed trajectory.
22+
23+
Parameters
24+
----------
25+
orientations : Orientations
26+
The unit vector trajectories
27+
shape : tuple
28+
The shape of the spherical sector in which the trajectory is plotted
29+
normalize_histo : bool, optional
30+
If True, normalize the histogram by the area of the bins, by default True
31+
32+
Returns
33+
-------
34+
fig : matplotlib.figure.Figure
35+
Output figure
36+
"""
37+
from gemdat.orientations import calculate_spherical_areas
38+
39+
az, el, _ = orientations.vectors_spherical.T
40+
az = az.flatten()
41+
el = el.flatten()
42+
43+
hist, *_ = np.histogram2d(el, az, shape)
44+
45+
if normalize_histo:
46+
areas = calculate_spherical_areas(shape)
47+
hist = hist / areas
48+
# Drop the bins at the poles where normalization is not possible
49+
hist = hist[1:-1, :]
50+
51+
axis_theta, axis_phi = hist.shape
52+
53+
phi = np.radians(np.linspace(0, 360, axis_phi))
54+
theta = np.linspace(0, 180, axis_theta)
55+
56+
theta, phi = np.meshgrid(theta, phi)
57+
58+
fig, (ax1, ax2) = plt.subplots(1, 2, subplot_kw=dict(projection='polar'))
59+
60+
cs1 = ax1.contourf(phi, theta, hist.T)
61+
ax1.set_title('θ < 90°')
62+
ax1.set_rmax(90)
63+
ax1.set_yticklabels([])
64+
65+
ax2.contourf(phi, 180 - theta, hist.T)
66+
ax2.set_title('θ > 90°')
67+
ax2.set_rmax(90)
68+
ax2.set_yticklabels([])
69+
70+
fig.colorbar(cs1, ax=[ax1, ax2], orientation='horizontal', label='Areal Probability')
71+
72+
plt.subplots_adjust(wspace=0.5, bottom=0.35) # Increase horizontal spacing
73+
74+
return fig

src/gemdat/plots/plotly/__init__.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
from ._jumps_vs_time import jumps_vs_time
1717
from ._msd_per_element import msd_per_element
1818
from ._plot3d import plot_3d
19+
from ._polar import polar
1920
from ._radial_distribution import radial_distribution
2021
from ._rectilinear import rectilinear
2122
from ._shape import shape
@@ -36,6 +37,7 @@
3637
'jumps_vs_time',
3738
'msd_per_element',
3839
'plot_3d',
40+
'polar',
3941
'radial_distribution',
4042
'rectilinear',
4143
'shape',

src/gemdat/plots/plotly/_polar.py

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
from __future__ import annotations
2+
3+
from typing import TYPE_CHECKING
4+
5+
if TYPE_CHECKING:
6+
import plotly.graph_objects as go
7+
8+
from gemdat.orientations import Orientations
9+
10+
11+
def polar(
12+
*,
13+
orientations: Orientations,
14+
shape: tuple[int, int] = (90, 360),
15+
normalize_histo: bool = True,
16+
) -> go.Figure:
17+
"""Plot a polar projection of a spherical function. This function uses the
18+
transformed trajectory.
19+
20+
Parameters
21+
----------
22+
orientations : Orientations
23+
The unit vector trajectories
24+
shape : tuple
25+
The shape of the spherical sector in which the trajectory is plotted
26+
normalize_histo : bool, optional
27+
If True, normalize the histogram by the area of the bins, by default True
28+
29+
Returns
30+
-------
31+
fig : plotly.graph_objects.Figure
32+
Output figure
33+
"""
34+
raise NotImplementedError('Use the matplotlib version.')
Loading

tests/integration/plot_mpl_test.py

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -129,3 +129,8 @@ def test_bond_length_distribution(vasp_orientations):
129129
@image_comparison2(baseline_images=['autocorrelation'])
130130
def test_autocorrelation(vasp_orientations):
131131
vasp_orientations.plot_autocorrelation(backend=BACKEND)
132+
133+
134+
@image_comparison2(baseline_images=['polar'])
135+
def test_polar(vasp_orientations):
136+
vasp_orientations.plot_polar(backend=BACKEND)

0 commit comments

Comments
 (0)