Skip to content

defectpl.plot

Plotter

Plotter()

Publication-quality static figure generator for DefectPL outputs.

Each method produces a single matplotlib figure and either displays it interactively (plot=True) or saves it at 600 dpi to out_dir (plot=False, the default used by :meth:~defectpl.defectpl.Photoluminescence.generate_plots).

All energy inputs follow the internal convention of eV; axis labels are converted to meV where appropriate for readability.

Methods:

Name Description
plot_penergy_vs_pmode

Phonon energy vs mode index (dispersionless Γ-point spectrum).

plot_ipr_vs_penergy

Phonon IPR vs phonon energy.

plot_loc_rat_vs_penergy

Localization ratio β_k = N·IPR_k vs phonon energy.

plot_qk_vs_penergy

Mode-projected displacement q_k vs phonon energy.

plot_HR_factor_vs_penergy

Partial Huang–Rhys factors S_k vs phonon energy.

plot_S_omega_vs_penergy

Continuous spectral density S(ω) vs phonon energy.

plot_S_omega_Sks_vs_penergy

S(ω) and S_k overlaid on dual y-axes.

plot_S_omega_Sks_Loc_rat_vs_penergy

S(ω) + S_k scatter coloured by localization ratio.

plot_S_omega_Sks_ipr_vs_penergy

S(ω) + S_k scatter coloured by IPR (traditional convention).

plot_ipr_alkauskas_vs_penergy

Phonon IPR (Alkauskas eq. 12 convention) vs phonon energy.

plot_S_omega_Sks_ipr_alkauskas_vs_penergy

S(ω) + S_k scatter coloured by Alkauskas IPR.

plot_nk_vs_penergy

Bose-Einstein phonon occupation n̄_k(T) vs phonon energy.

plot_C_omega_vs_penergy

Thermal spectral density C(ℏω, T) vs phonon energy.

plot_intensity_vs_penergy

Normalised PL intensity spectrum vs photon energy.

plot_absorption_vs_penergy

Normalised absorption spectrum vs photon energy.

plot_pl_absorption_vs_penergy

PL and absorption overlaid on the same axes.

Source code in defectpl/plot.py
def __init__(self):
    pass

plot_penergy_vs_pmode

plot_penergy_vs_pmode(frequencies, plot=False, out_dir='./', file_name='penergy_vs_pmode', fig_format='pdf', figsize=(3.3, 2.5))

Plot phonon energy (meV) vs mode index.

Parameters:

Name Type Description Default
frequencies (ndarray, shape(nmodes))

Phonon frequencies in eV.

required
plot bool

Show interactively instead of saving. Default False.

False
out_dir str or Path

Output directory. Default "./"

'./'
file_name str

Base file name (no extension). Default "penergy_vs_pmode".

'penergy_vs_pmode'
fig_format str

"pdf", "png", "svg", or "jpg". Default "pdf".

'pdf'
figsize tuple of float

Figure size in inches. Default (3.3, 2.5).

(3.3, 2.5)
Source code in defectpl/plot.py
def plot_penergy_vs_pmode(
    self,
    frequencies: np.ndarray,
    plot: bool = False,
    out_dir: Union[str, Path] = "./",
    file_name: str = "penergy_vs_pmode",
    fig_format: str = "pdf",
    figsize: Tuple[float, float] = (3.3, 2.5),
):
    """
    Plot phonon energy (meV) vs mode index.

    Parameters
    ----------
    frequencies : numpy.ndarray, shape (nmodes,)
        Phonon frequencies in **eV**.
    plot : bool, optional
        Show interactively instead of saving.  Default False.
    out_dir : str or Path, optional
        Output directory.  Default ``"./"``
    file_name : str, optional
        Base file name (no extension).  Default ``"penergy_vs_pmode"``.
    fig_format : str, optional
        ``"pdf"``, ``"png"``, ``"svg"``, or ``"jpg"``.  Default ``"pdf"``.
    figsize : tuple of float, optional
        Figure size in inches.  Default ``(3.3, 2.5)``.
    """
    fig, ax = plt.subplots(figsize=figsize)
    freq_mev = np.array(frequencies) * 1000.0
    mode_idx = np.arange(1, len(freq_mev) + 1)

    ax.plot(mode_idx, freq_mev, color="black")
    ax.set_xlabel("Phonon Mode Index")
    ax.set_ylabel("Phonon Energy (meV)")

    self._save_or_show(fig, out_dir, file_name, fig_format, plot)

plot_ipr_vs_penergy

plot_ipr_vs_penergy(frequencies, iprs, plot=False, out_dir='./', file_name='ipr_vs_penergy', fig_format='pdf', figsize=(3.3, 2.5))

Scatter plot of phonon IPR vs phonon energy (meV).

IPR = Σp²/(Σp)² ranges from 1/N (fully delocalized) to 1 (fully localized).

Parameters:

Name Type Description Default
frequencies (ndarray, shape(nmodes))

Phonon frequencies in eV.

required
iprs (ndarray, shape(nmodes))

Inverse participation ratios per mode.

required
plot bool

See :meth:plot_penergy_vs_pmode.

False
out_dir bool

See :meth:plot_penergy_vs_pmode.

False
file_name bool

See :meth:plot_penergy_vs_pmode.

False
fig_format bool

See :meth:plot_penergy_vs_pmode.

False
figsize bool

See :meth:plot_penergy_vs_pmode.

False
Source code in defectpl/plot.py
def plot_ipr_vs_penergy(
    self,
    frequencies: np.ndarray,
    iprs: np.ndarray,
    plot: bool = False,
    out_dir: Union[str, Path] = "./",
    file_name: str = "ipr_vs_penergy",
    fig_format: str = "pdf",
    figsize: Tuple[float, float] = (3.3, 2.5),
):
    """
    Scatter plot of phonon IPR vs phonon energy (meV).

    IPR = Σp²/(Σp)² ranges from 1/N (fully delocalized) to 1 (fully localized).

    Parameters
    ----------
    frequencies : numpy.ndarray, shape (nmodes,)
        Phonon frequencies in **eV**.
    iprs : numpy.ndarray, shape (nmodes,)
        Inverse participation ratios per mode.
    plot, out_dir, file_name, fig_format, figsize
        See :meth:`plot_penergy_vs_pmode`.
    """
    fig, ax = plt.subplots(figsize=figsize)
    freq_mev = np.array(frequencies) * 1000.0

    ax.scatter(freq_mev, iprs, edgecolor="black", alpha=0.85)
    ax.set_xlabel("Phonon Energy (meV)")
    ax.set_ylabel("IPR")

    self._save_or_show(fig, out_dir, file_name, fig_format, plot)

plot_loc_rat_vs_penergy

plot_loc_rat_vs_penergy(frequencies, localization_ratio, plot=False, out_dir='./', file_name='loc_rat_vs_penergy', fig_format='pdf', figsize=(3.3, 2.5))

Scatter plot of localization ratio β_k = N·IPR_k vs phonon energy (meV).

β = 1 means perfectly delocalized; β = N means fully localized on one atom.

Parameters:

Name Type Description Default
frequencies (ndarray, shape(nmodes))

Phonon frequencies in eV.

required
localization_ratio (ndarray, shape(nmodes))

Localization ratio per mode.

required
plot bool

See :meth:plot_penergy_vs_pmode.

False
out_dir bool

See :meth:plot_penergy_vs_pmode.

False
file_name bool

See :meth:plot_penergy_vs_pmode.

False
fig_format bool

See :meth:plot_penergy_vs_pmode.

False
figsize bool

See :meth:plot_penergy_vs_pmode.

False
Source code in defectpl/plot.py
def plot_loc_rat_vs_penergy(
    self,
    frequencies: np.ndarray,
    localization_ratio: np.ndarray,
    plot: bool = False,
    out_dir: Union[str, Path] = "./",
    file_name: str = "loc_rat_vs_penergy",
    fig_format: str = "pdf",
    figsize: Tuple[float, float] = (3.3, 2.5),
):
    """
    Scatter plot of localization ratio β_k = N·IPR_k vs phonon energy (meV).

    β = 1 means perfectly delocalized; β = N means fully localized on one atom.

    Parameters
    ----------
    frequencies : numpy.ndarray, shape (nmodes,)
        Phonon frequencies in **eV**.
    localization_ratio : numpy.ndarray, shape (nmodes,)
        Localization ratio per mode.
    plot, out_dir, file_name, fig_format, figsize
        See :meth:`plot_penergy_vs_pmode`.
    """
    fig, ax = plt.subplots(figsize=figsize)
    freq_mev = np.array(frequencies) * 1000.0

    ax.scatter(freq_mev, localization_ratio, edgecolor="black", alpha=0.85)
    ax.set_xlabel("Phonon Energy (meV)")
    ax.set_ylabel("Localization Ratio")

    self._save_or_show(fig, out_dir, file_name, fig_format, plot)

plot_qk_vs_penergy

plot_qk_vs_penergy(frequencies, qks, plot=False, out_dir='./', file_name='qk_vs_penergy', fig_format='pdf', figsize=(3.3, 2.5))

Scatter plot of mode-projected displacement q_k vs phonon energy (meV).

Parameters:

Name Type Description Default
frequencies (ndarray, shape(nmodes))

Phonon frequencies in eV.

required
qks (ndarray, shape(nmodes))

Mode configurational displacements in amu^(½)·Å.

required
plot bool

See :meth:plot_penergy_vs_pmode.

False
out_dir bool

See :meth:plot_penergy_vs_pmode.

False
file_name bool

See :meth:plot_penergy_vs_pmode.

False
fig_format bool

See :meth:plot_penergy_vs_pmode.

False
figsize bool

See :meth:plot_penergy_vs_pmode.

False
Source code in defectpl/plot.py
def plot_qk_vs_penergy(
    self,
    frequencies: np.ndarray,
    qks: np.ndarray,
    plot: bool = False,
    out_dir: Union[str, Path] = "./",
    file_name: str = "qk_vs_penergy",
    fig_format: str = "pdf",
    figsize: Tuple[float, float] = (3.3, 2.5),
):
    """
    Scatter plot of mode-projected displacement q_k vs phonon energy (meV).

    Parameters
    ----------
    frequencies : numpy.ndarray, shape (nmodes,)
        Phonon frequencies in **eV**.
    qks : numpy.ndarray, shape (nmodes,)
        Mode configurational displacements in amu^(1/2)·Å.
    plot, out_dir, file_name, fig_format, figsize
        See :meth:`plot_penergy_vs_pmode`.
    """
    fig, ax = plt.subplots(figsize=figsize)
    freq_mev = np.array(frequencies) * 1000.0

    ax.scatter(freq_mev, qks, edgecolor="black", alpha=0.85)
    ax.set_xlabel("Phonon Energy (meV)")
    ax.set_ylabel(r"Displacement $q_k$ ($\mathrm{amu}^{1/2}\cdot\mathrm{\AA}$)")

    self._save_or_show(fig, out_dir, file_name, fig_format, plot)

plot_HR_factor_vs_penergy

plot_HR_factor_vs_penergy(frequencies, Sks, plot=False, out_dir='./', file_name='HR_factor_vs_penergy', fig_format='pdf', figsize=(3.3, 2.5))

Scatter plot of partial Huang–Rhys factors S_k vs phonon energy (meV).

Parameters:

Name Type Description Default
frequencies (ndarray, shape(nmodes))

Phonon frequencies in eV.

required
Sks (ndarray, shape(nmodes))

Partial (mode-resolved) Huang–Rhys factors.

required
plot bool

See :meth:plot_penergy_vs_pmode.

False
out_dir bool

See :meth:plot_penergy_vs_pmode.

False
file_name bool

See :meth:plot_penergy_vs_pmode.

False
fig_format bool

See :meth:plot_penergy_vs_pmode.

False
figsize bool

See :meth:plot_penergy_vs_pmode.

False
Source code in defectpl/plot.py
def plot_HR_factor_vs_penergy(
    self,
    frequencies: np.ndarray,
    Sks: np.ndarray,
    plot: bool = False,
    out_dir: Union[str, Path] = "./",
    file_name: str = "HR_factor_vs_penergy",
    fig_format: str = "pdf",
    figsize: Tuple[float, float] = (3.3, 2.5),
):
    """
    Scatter plot of partial Huang–Rhys factors S_k vs phonon energy (meV).

    Parameters
    ----------
    frequencies : numpy.ndarray, shape (nmodes,)
        Phonon frequencies in **eV**.
    Sks : numpy.ndarray, shape (nmodes,)
        Partial (mode-resolved) Huang–Rhys factors.
    plot, out_dir, file_name, fig_format, figsize
        See :meth:`plot_penergy_vs_pmode`.
    """
    fig, ax = plt.subplots(figsize=figsize)
    freq_mev = np.array(frequencies) * 1000.0

    ax.scatter(freq_mev, Sks, edgecolor="black", alpha=0.85)
    ax.set_xlabel("Phonon Energy (meV)")
    ax.set_ylabel(r"Partial HR Factor ($S_k$)")

    self._save_or_show(fig, out_dir, file_name, fig_format, plot)

plot_S_omega_vs_penergy

plot_S_omega_vs_penergy(frequencies, S_omega, omega_range, plot=False, out_dir='./', file_name='S_omega_vs_penergy', max_freq=None, fig_format='pdf', figsize=(3.3, 2.5))

Line plot of the continuous Huang–Rhys spectral density S(ω) in 1/meV vs energy (meV).

S(ω) is the phonon sideband weight per unit energy; its integral equals the total Huang–Rhys factor S.

Parameters:

Name Type Description Default
frequencies (ndarray, shape(nmodes))

Phonon frequencies in eV (used to set x-axis upper limit).

required
S_omega (array - like, shape(n_grid))

Spectral density in eV^(-1).

required
omega_range list[ω_min, ω_max, n_points]

Energy grid parameters in eV matching the S(ω) array.

required
max_freq float

Upper frequency cut-off for the plot in eV.

None
plot bool

See :meth:plot_penergy_vs_pmode.

False
out_dir bool

See :meth:plot_penergy_vs_pmode.

False
file_name bool

See :meth:plot_penergy_vs_pmode.

False
fig_format bool

See :meth:plot_penergy_vs_pmode.

False
figsize bool

See :meth:plot_penergy_vs_pmode.

False
Source code in defectpl/plot.py
def plot_S_omega_vs_penergy(
    self,
    frequencies: np.ndarray,
    S_omega: Union[list, np.ndarray],
    omega_range: List[Union[int, float]],
    plot: bool = False,
    out_dir: Union[str, Path] = "./",
    file_name: str = "S_omega_vs_penergy",
    max_freq: Optional[float] = None,
    fig_format: str = "pdf",
    figsize: Tuple[float, float] = (3.3, 2.5),
):
    """
    Line plot of the continuous Huang–Rhys spectral density S(ω) in 1/meV vs energy (meV).

    S(ω) is the phonon sideband weight per unit energy; its integral equals
    the total Huang–Rhys factor S.

    Parameters
    ----------
    frequencies : numpy.ndarray, shape (nmodes,)
        Phonon frequencies in **eV** (used to set x-axis upper limit).
    S_omega : array-like, shape (n_grid,)
        Spectral density in **eV^(-1)**.
    omega_range : list [ω_min, ω_max, n_points]
        Energy grid parameters in **eV** matching the S(ω) array.
    max_freq : float, optional
        Upper frequency cut-off for the plot in **eV**.
    plot, out_dir, file_name, fig_format, figsize
        See :meth:`plot_penergy_vs_pmode`.
    """
    fig, ax = plt.subplots(figsize=figsize)
    omega_set = np.linspace(omega_range[0], omega_range[1], int(omega_range[2]))

    cutoff = max_freq if max_freq is not None else float(max(frequencies))
    mask = omega_set <= cutoff

    S_omega_mev = np.array(S_omega)[mask] / 1000.0
    energies_mev = omega_set[mask] * 1000.0

    ax.plot(energies_mev, S_omega_mev, color="black")
    ax.set_xlabel("Phonon Energy (meV)")
    ax.set_ylabel(r"$S(\hbar\omega)$ ($1/\mathrm{meV}$)")
    ax.set_xlim(0, cutoff * 1000.0)

    self._save_or_show(fig, out_dir, file_name, fig_format, plot)

plot_S_omega_Sks_vs_penergy

plot_S_omega_Sks_vs_penergy(frequencies, S_omega, omega_range, Sks, plot=False, out_dir='./', file_name='S_omega_Sks_vs_penergy', max_freq=None, fig_format='pdf', figsize=(3.5, 2.5))

Dual-axis plot of S(ω) (left axis) and partial S_k scatter (right axis).

Parameters:

Name Type Description Default
frequencies ndarray

See :meth:plot_S_omega_vs_penergy.

required
S_omega ndarray

See :meth:plot_S_omega_vs_penergy.

required
omega_range ndarray

See :meth:plot_S_omega_vs_penergy.

required
Sks (ndarray, shape(nmodes))

Partial Huang–Rhys factors.

required
max_freq float

Upper frequency cut-off in eV.

None
plot bool

See :meth:plot_penergy_vs_pmode.

False
out_dir bool

See :meth:plot_penergy_vs_pmode.

False
file_name bool

See :meth:plot_penergy_vs_pmode.

False
fig_format bool

See :meth:plot_penergy_vs_pmode.

False
figsize bool

See :meth:plot_penergy_vs_pmode.

False
Source code in defectpl/plot.py
def plot_S_omega_Sks_vs_penergy(
    self,
    frequencies: np.ndarray,
    S_omega: Union[list, np.ndarray],
    omega_range: List[Union[int, float]],
    Sks: np.ndarray,
    plot: bool = False,
    out_dir: Union[str, Path] = "./",
    file_name: str = "S_omega_Sks_vs_penergy",
    max_freq: Optional[float] = None,
    fig_format: str = "pdf",
    figsize: Tuple[float, float] = (3.5, 2.5),
):
    """
    Dual-axis plot of S(ω) (left axis) and partial S_k scatter (right axis).

    Parameters
    ----------
    frequencies, S_omega, omega_range
        See :meth:`plot_S_omega_vs_penergy`.
    Sks : numpy.ndarray, shape (nmodes,)
        Partial Huang–Rhys factors.
    max_freq : float, optional
        Upper frequency cut-off in **eV**.
    plot, out_dir, file_name, fig_format, figsize
        See :meth:`plot_penergy_vs_pmode`.
    """
    fig, ax1 = plt.subplots(figsize=figsize)
    omega_set = np.linspace(omega_range[0], omega_range[1], int(omega_range[2]))

    cutoff = max_freq if max_freq is not None else float(max(frequencies))
    mask = omega_set <= cutoff

    # Pull standard colors directly from cycler layout
    colors = plt.rcParams["axes.prop_cycle"].by_key()["color"]
    c1, c2 = colors[2], colors[0]  # Safe default color indices mapping

    ax1.set_xlabel("Phonon Energy (meV)")
    ax1.set_ylabel(r"$S(\hbar\omega)$ ($1/\mathrm{meV}$)", color=c1)
    ax1.plot(omega_set[mask] * 1000.0, np.array(S_omega)[mask] / 1000.0, color=c1)
    ax1.tick_params(axis="y", labelcolor=c1)

    ax2 = ax1.twinx()
    ax2.set_ylabel(r"Partial HR Factor ($S_k$)", color=c2)
    ax2.scatter(
        np.array(frequencies) * 1000.0, Sks, color=c2, edgecolor="black", alpha=0.85
    )
    ax2.tick_params(axis="y", labelcolor=c2)

    ax1.set_xlim(0, cutoff * 1000.0)

    self._save_or_show(fig, out_dir, file_name, fig_format, plot)

plot_S_omega_Sks_Loc_rat_vs_penergy

plot_S_omega_Sks_Loc_rat_vs_penergy(frequencies, S_omega, omega_range, Sks, localization_ratio, plot=False, out_dir='./', file_name='S_omega_HRf_loc_rat_vs_penergy', max_freq=None, pylim=[None, None], fig_format='pdf', figsize=(4.2, 2.5), cmap='viridis')

S(ω) line + S_k scatter coloured by localization ratio β_k.

Identifies phonon modes that simultaneously carry large HR weight and are localized near the defect.

Parameters:

Name Type Description Default
frequencies ndarray

See :meth:plot_S_omega_vs_penergy.

required
S_omega ndarray

See :meth:plot_S_omega_vs_penergy.

required
omega_range ndarray

See :meth:plot_S_omega_vs_penergy.

required
Sks (ndarray, shape(nmodes))

Partial Huang–Rhys factors (right y-axis).

required
localization_ratio (ndarray, shape(nmodes))

Colour-code values β_k = N·IPR_k.

required
pylim list of [float or None, float or None]

[ymin, ymax] for the right (S_k) axis.

[None, None]
cmap str

Matplotlib colormap name. Default "viridis".

'viridis'
plot bool

See :meth:plot_penergy_vs_pmode.

False
out_dir bool

See :meth:plot_penergy_vs_pmode.

False
file_name bool

See :meth:plot_penergy_vs_pmode.

False
fig_format bool

See :meth:plot_penergy_vs_pmode.

False
figsize bool

See :meth:plot_penergy_vs_pmode.

False
Source code in defectpl/plot.py
def plot_S_omega_Sks_Loc_rat_vs_penergy(
    self,
    frequencies: np.ndarray,
    S_omega: Union[list, np.ndarray],
    omega_range: List[Union[int, float]],
    Sks: np.ndarray,
    localization_ratio: np.ndarray,
    plot: bool = False,
    out_dir: Union[str, Path] = "./",
    file_name: str = "S_omega_HRf_loc_rat_vs_penergy",
    max_freq: Optional[float] = None,
    pylim: List[Optional[float]] = [None, None],
    fig_format: str = "pdf",
    figsize: Tuple[float, float] = (4.2, 2.5),
    cmap: str = "viridis",
):
    """
    S(ω) line + S_k scatter coloured by localization ratio β_k.

    Identifies phonon modes that simultaneously carry large HR weight *and*
    are localized near the defect.

    Parameters
    ----------
    frequencies, S_omega, omega_range
        See :meth:`plot_S_omega_vs_penergy`.
    Sks : numpy.ndarray, shape (nmodes,)
        Partial Huang–Rhys factors (right y-axis).
    localization_ratio : numpy.ndarray, shape (nmodes,)
        Colour-code values β_k = N·IPR_k.
    pylim : list of [float or None, float or None], optional
        ``[ymin, ymax]`` for the right (S_k) axis.
    cmap : str, optional
        Matplotlib colormap name.  Default ``"viridis"``.
    plot, out_dir, file_name, fig_format, figsize
        See :meth:`plot_penergy_vs_pmode`.
    """
    # FIX: Explicitly set layout="constrained" to cleanly handle twinx + colorbar spacing
    fig, ax1 = plt.subplots(figsize=figsize, layout="constrained")
    omega_set = np.linspace(omega_range[0], omega_range[1], int(omega_range[2]))

    cutoff = max_freq if max_freq is not None else float(max(frequencies))
    mask = omega_set <= cutoff

    ax1.set_xlabel("Phonon Energy (meV)")
    ax1.set_ylabel(r"$S(\hbar\omega)$ ($1/\mathrm{meV}$)", color="black")
    ax1.plot(
        omega_set[mask] * 1000.0, np.array(S_omega)[mask] / 1000.0, color="black"
    )
    ax1.tick_params(axis="y", labelcolor="black")

    ax2 = ax1.twinx()
    ax2.set_ylabel(r"Partial HR Factor ($S_k$)", color="black")

    sc = ax2.scatter(
        np.array(frequencies) * 1000.0,
        Sks,
        c=localization_ratio,
        cmap=plt.get_cmap(cmap),
        edgecolor="black",
        alpha=0.85,
    )
    ax2.tick_params(axis="y", labelcolor="black")
    if pylim[0] is not None or pylim[1] is not None:
        ax2.set_ylim(pylim[0], pylim[1])

    ax1.set_xlim(0, cutoff * 1000.0)

    # When using layout="constrained", we can target ax2 directly with an explicit pad
    cbar = fig.colorbar(sc, ax=ax2, pad=0.12)
    cbar.set_label("Localization Ratio")

    self._save_or_show(fig, out_dir, file_name, fig_format, plot)

plot_S_omega_Sks_ipr_vs_penergy

plot_S_omega_Sks_ipr_vs_penergy(frequencies, S_omega, omega_range, Sks, iprs, plot=False, out_dir='./', file_name='S_omega_HRf_ipr_vs_penergy', max_freq=None, fig_format='pdf', figsize=(4.2, 2.5), cmap='viridis')

S(ω) line + S_k scatter coloured by phonon IPR.

Parameters:

Name Type Description Default
frequencies ndarray

See :meth:plot_S_omega_vs_penergy.

required
S_omega ndarray

See :meth:plot_S_omega_vs_penergy.

required
omega_range ndarray

See :meth:plot_S_omega_vs_penergy.

required
Sks (ndarray, shape(nmodes))

Partial Huang–Rhys factors (right y-axis).

required
iprs (ndarray, shape(nmodes))

Phonon inverse participation ratios used as colour code.

required
cmap str

Matplotlib colormap name. Default "viridis".

'viridis'
plot bool

See :meth:plot_penergy_vs_pmode.

False
out_dir bool

See :meth:plot_penergy_vs_pmode.

False
file_name bool

See :meth:plot_penergy_vs_pmode.

False
fig_format bool

See :meth:plot_penergy_vs_pmode.

False
figsize bool

See :meth:plot_penergy_vs_pmode.

False
Source code in defectpl/plot.py
def plot_S_omega_Sks_ipr_vs_penergy(
    self,
    frequencies: np.ndarray,
    S_omega: Union[list, np.ndarray],
    omega_range: List[Union[int, float]],
    Sks: np.ndarray,
    iprs: np.ndarray,
    plot: bool = False,
    out_dir: Union[str, Path] = "./",
    file_name: str = "S_omega_HRf_ipr_vs_penergy",
    max_freq: Optional[float] = None,
    fig_format: str = "pdf",
    figsize: Tuple[float, float] = (4.2, 2.5),
    cmap: str = "viridis",
):
    """
    S(ω) line + S_k scatter coloured by phonon IPR.

    Parameters
    ----------
    frequencies, S_omega, omega_range
        See :meth:`plot_S_omega_vs_penergy`.
    Sks : numpy.ndarray, shape (nmodes,)
        Partial Huang–Rhys factors (right y-axis).
    iprs : numpy.ndarray, shape (nmodes,)
        Phonon inverse participation ratios used as colour code.
    cmap : str, optional
        Matplotlib colormap name.  Default ``"viridis"``.
    plot, out_dir, file_name, fig_format, figsize
        See :meth:`plot_penergy_vs_pmode`.
    """
    # FIX: Explicitly set layout="constrained" to cleanly handle twinx + colorbar spacing
    fig, ax1 = plt.subplots(figsize=figsize, layout="constrained")
    omega_set = np.linspace(omega_range[0], omega_range[1], int(omega_range[2]))

    cutoff = max_freq if max_freq is not None else float(max(frequencies))
    mask = omega_set <= cutoff

    ax1.set_xlabel("Phonon Energy (meV)")
    ax1.set_ylabel(r"$S(\hbar\omega)$ ($1/\mathrm{meV}$)", color="black")
    ax1.plot(
        omega_set[mask] * 1000.0, np.array(S_omega)[mask] / 1000.0, color="black"
    )
    ax1.tick_params(axis="y", labelcolor="black")

    ax2 = ax1.twinx()
    ax2.set_ylabel(r"Partial HR Factor ($S_k$)", color="black")

    sc = ax2.scatter(
        np.array(frequencies) * 1000.0,
        Sks,
        c=iprs,
        cmap=plt.get_cmap(cmap),
        edgecolor="black",
        alpha=0.85,
    )
    ax2.tick_params(axis="y", labelcolor="black")
    ax1.set_xlim(0, cutoff * 1000.0)

    # When using layout="constrained", we can target ax2 directly with an explicit pad
    cbar = fig.colorbar(sc, ax=ax2, pad=0.12)
    cbar.set_label("Inverse Participation Ratio")

    self._save_or_show(fig, out_dir, file_name, fig_format, plot)

plot_ipr_alkauskas_vs_penergy

plot_ipr_alkauskas_vs_penergy(frequencies, iprs_alkauskas, plot=False, out_dir='./', file_name='ipr_alkauskas_vs_penergy', fig_format='pdf', figsize=(3.3, 2.5))

Scatter plot of phonon IPR (Alkauskas eq. 12 convention) vs phonon energy (meV).

IPR_k = (Σp)²/Σp² ranges from 1 (fully localized) to N (fully delocalized). This is the reciprocal of the traditional IPR plotted by :meth:plot_ipr_vs_penergy.

Parameters:

Name Type Description Default
frequencies (ndarray, shape(nmodes))

Phonon frequencies in eV.

required
iprs_alkauskas (ndarray, shape(nmodes))

Alkauskas-convention IPR per mode (Alkauskas 2014, eq. 12).

required
plot bool

See :meth:plot_penergy_vs_pmode.

False
out_dir bool

See :meth:plot_penergy_vs_pmode.

False
file_name bool

See :meth:plot_penergy_vs_pmode.

False
fig_format bool

See :meth:plot_penergy_vs_pmode.

False
figsize bool

See :meth:plot_penergy_vs_pmode.

False
Source code in defectpl/plot.py
def plot_ipr_alkauskas_vs_penergy(
    self,
    frequencies: np.ndarray,
    iprs_alkauskas: np.ndarray,
    plot: bool = False,
    out_dir: Union[str, Path] = "./",
    file_name: str = "ipr_alkauskas_vs_penergy",
    fig_format: str = "pdf",
    figsize: Tuple[float, float] = (3.3, 2.5),
):
    """
    Scatter plot of phonon IPR (Alkauskas eq. 12 convention) vs phonon energy (meV).

    IPR_k = (Σp)²/Σp² ranges from 1 (fully localized) to N (fully delocalized).
    This is the reciprocal of the traditional IPR plotted by :meth:`plot_ipr_vs_penergy`.

    Parameters
    ----------
    frequencies : numpy.ndarray, shape (nmodes,)
        Phonon frequencies in **eV**.
    iprs_alkauskas : numpy.ndarray, shape (nmodes,)
        Alkauskas-convention IPR per mode (Alkauskas 2014, eq. 12).
    plot, out_dir, file_name, fig_format, figsize
        See :meth:`plot_penergy_vs_pmode`.
    """
    fig, ax = plt.subplots(figsize=figsize)
    freq_mev = np.array(frequencies) * 1000.0

    ax.scatter(freq_mev, iprs_alkauskas, edgecolor="black", alpha=0.85)
    ax.set_xlabel("Phonon Energy (meV)")
    ax.set_ylabel(r"$\mathrm{IPR}_k$ (Alkauskas)")

    self._save_or_show(fig, out_dir, file_name, fig_format, plot)

plot_S_omega_Sks_ipr_alkauskas_vs_penergy

plot_S_omega_Sks_ipr_alkauskas_vs_penergy(frequencies, S_omega, omega_range, Sks, iprs_alkauskas, plot=False, out_dir='./', file_name='S_omega_HRf_ipr_alkauskas_vs_penergy', max_freq=None, fig_format='pdf', figsize=(4.2, 2.5), cmap='viridis')

S(ω) line + S_k scatter coloured by Alkauskas-convention phonon IPR.

Modes with low IPR_k (close to 1) are localized; modes with high IPR_k (close to N) are bulk-like delocalized.

Parameters:

Name Type Description Default
frequencies ndarray

See :meth:plot_S_omega_vs_penergy.

required
S_omega ndarray

See :meth:plot_S_omega_vs_penergy.

required
omega_range ndarray

See :meth:plot_S_omega_vs_penergy.

required
Sks (ndarray, shape(nmodes))

Partial Huang–Rhys factors (right y-axis).

required
iprs_alkauskas (ndarray, shape(nmodes))

Alkauskas-convention IPR per mode used as colour code.

required
cmap str

Matplotlib colormap name. Default "viridis".

'viridis'
plot bool

See :meth:plot_penergy_vs_pmode.

False
out_dir bool

See :meth:plot_penergy_vs_pmode.

False
file_name bool

See :meth:plot_penergy_vs_pmode.

False
fig_format bool

See :meth:plot_penergy_vs_pmode.

False
figsize bool

See :meth:plot_penergy_vs_pmode.

False
Source code in defectpl/plot.py
def plot_S_omega_Sks_ipr_alkauskas_vs_penergy(
    self,
    frequencies: np.ndarray,
    S_omega: Union[list, np.ndarray],
    omega_range: List[Union[int, float]],
    Sks: np.ndarray,
    iprs_alkauskas: np.ndarray,
    plot: bool = False,
    out_dir: Union[str, Path] = "./",
    file_name: str = "S_omega_HRf_ipr_alkauskas_vs_penergy",
    max_freq: Optional[float] = None,
    fig_format: str = "pdf",
    figsize: Tuple[float, float] = (4.2, 2.5),
    cmap: str = "viridis",
):
    """
    S(ω) line + S_k scatter coloured by Alkauskas-convention phonon IPR.

    Modes with low IPR_k (close to 1) are localized; modes with high IPR_k
    (close to N) are bulk-like delocalized.

    Parameters
    ----------
    frequencies, S_omega, omega_range
        See :meth:`plot_S_omega_vs_penergy`.
    Sks : numpy.ndarray, shape (nmodes,)
        Partial Huang–Rhys factors (right y-axis).
    iprs_alkauskas : numpy.ndarray, shape (nmodes,)
        Alkauskas-convention IPR per mode used as colour code.
    cmap : str, optional
        Matplotlib colormap name.  Default ``"viridis"``.
    plot, out_dir, file_name, fig_format, figsize
        See :meth:`plot_penergy_vs_pmode`.
    """
    fig, ax1 = plt.subplots(figsize=figsize, layout="constrained")
    omega_set = np.linspace(omega_range[0], omega_range[1], int(omega_range[2]))

    cutoff = max_freq if max_freq is not None else float(max(frequencies))
    mask = omega_set <= cutoff

    ax1.set_xlabel("Phonon Energy (meV)")
    ax1.set_ylabel(r"$S(\hbar\omega)$ ($1/\mathrm{meV}$)", color="black")
    ax1.plot(
        omega_set[mask] * 1000.0, np.array(S_omega)[mask] / 1000.0, color="black"
    )
    ax1.tick_params(axis="y", labelcolor="black")

    ax2 = ax1.twinx()
    ax2.set_ylabel(r"Partial HR Factor ($S_k$)", color="black")

    sc = ax2.scatter(
        np.array(frequencies) * 1000.0,
        Sks,
        c=iprs_alkauskas,
        cmap=plt.get_cmap(cmap),
        edgecolor="black",
        alpha=0.85,
    )
    ax2.tick_params(axis="y", labelcolor="black")
    ax1.set_xlim(0, cutoff * 1000.0)

    cbar = fig.colorbar(sc, ax=ax2, pad=0.12)
    cbar.set_label(r"$\mathrm{IPR}_k$ (Alkauskas)")

    self._save_or_show(fig, out_dir, file_name, fig_format, plot)

plot_intensity_vs_penergy

plot_intensity_vs_penergy(frequencies, I, resolution, xlim, plot=False, out_dir='./', file_name='intensity_vs_penergy', iylim=None, fig_format='pdf', figsize=(3.3, 2.5))

Line plot of the normalised PL intensity spectrum vs photon energy (eV).

Parameters:

Name Type Description Default
frequencies ndarray

Phonon frequencies in eV (unused directly; kept for API consistency).

required
I ndarray

Complex or real intensity array from :func:~defectpl.utils.calc_Spectrum_Intensity. Absolute value is taken and the result is normalised to its maximum.

required
resolution int

Number of grid points per eV; sets the energy-axis scale.

required
xlim tuple of float

(x_min, x_max) for the photon energy axis in eV.

required
iylim tuple of float

(y_min, y_max) for the intensity axis.

None
plot bool

See :meth:plot_penergy_vs_pmode.

False
out_dir bool

See :meth:plot_penergy_vs_pmode.

False
file_name bool

See :meth:plot_penergy_vs_pmode.

False
fig_format bool

See :meth:plot_penergy_vs_pmode.

False
figsize bool

See :meth:plot_penergy_vs_pmode.

False
Source code in defectpl/plot.py
def plot_intensity_vs_penergy(
    self,
    frequencies: np.ndarray,
    I: np.ndarray,
    resolution: int,
    xlim: Tuple[float, float],
    plot: bool = False,
    out_dir: Union[str, Path] = "./",
    file_name: str = "intensity_vs_penergy",
    iylim: Optional[Tuple[float, float]] = None,
    fig_format: str = "pdf",
    figsize: Tuple[float, float] = (3.3, 2.5),
):
    """
    Line plot of the normalised PL intensity spectrum vs photon energy (eV).

    Parameters
    ----------
    frequencies : numpy.ndarray
        Phonon frequencies in eV (unused directly; kept for API consistency).
    I : numpy.ndarray
        Complex or real intensity array from :func:`~defectpl.utils.calc_Spectrum_Intensity`.
        Absolute value is taken and the result is normalised to its maximum.
    resolution : int
        Number of grid points per eV; sets the energy-axis scale.
    xlim : tuple of float
        ``(x_min, x_max)`` for the photon energy axis in eV.
    iylim : tuple of float, optional
        ``(y_min, y_max)`` for the intensity axis.
    plot, out_dir, file_name, fig_format, figsize
        See :meth:`plot_penergy_vs_pmode`.
    """
    fig, ax = plt.subplots(figsize=figsize)

    x_energy_ev = np.arange(len(I)) / float(resolution)
    I_abs = np.abs(I)
    I_norm = I_abs / np.max(I_abs) if np.max(I_abs) > 0 else I_abs

    ax.plot(x_energy_ev, I_norm, color="black")
    ax.set_ylabel("PL Intensity (arb. u.)")
    ax.set_xlabel("Photon Energy (eV)")
    ax.set_xlim(xlim[0], xlim[1])

    if iylim:
        ax.set_ylim(iylim[0], iylim[1])
    ax.set_yticks([])  # Clean look for publication spectra

    self._save_or_show(fig, out_dir, file_name, fig_format, plot)

plot_nk_vs_penergy

plot_nk_vs_penergy(frequencies, nks, temperature=0.0, plot=False, out_dir='./', file_name='nk_vs_penergy', fig_format='pdf', figsize=(3.3, 2.5))

Scatter plot of Bose-Einstein phonon occupation n̄_k(T) vs phonon energy (meV).

Parameters:

Name Type Description Default
frequencies (ndarray, shape(nmodes))

Phonon frequencies in eV.

required
nks (ndarray, shape(nmodes))

Bose-Einstein occupation numbers per mode.

required
temperature float

Temperature in K, displayed in the axis label. Default 0.

0.0
plot bool

See :meth:plot_penergy_vs_pmode.

False
out_dir bool

See :meth:plot_penergy_vs_pmode.

False
file_name bool

See :meth:plot_penergy_vs_pmode.

False
fig_format bool

See :meth:plot_penergy_vs_pmode.

False
figsize bool

See :meth:plot_penergy_vs_pmode.

False
Source code in defectpl/plot.py
def plot_nk_vs_penergy(
    self,
    frequencies: np.ndarray,
    nks: np.ndarray,
    temperature: float = 0.0,
    plot: bool = False,
    out_dir: Union[str, Path] = "./",
    file_name: str = "nk_vs_penergy",
    fig_format: str = "pdf",
    figsize: Tuple[float, float] = (3.3, 2.5),
):
    """
    Scatter plot of Bose-Einstein phonon occupation n̄_k(T) vs phonon energy (meV).

    Parameters
    ----------
    frequencies : numpy.ndarray, shape (nmodes,)
        Phonon frequencies in **eV**.
    nks : numpy.ndarray, shape (nmodes,)
        Bose-Einstein occupation numbers per mode.
    temperature : float, optional
        Temperature in K, displayed in the axis label.  Default 0.
    plot, out_dir, file_name, fig_format, figsize
        See :meth:`plot_penergy_vs_pmode`.
    """
    fig, ax = plt.subplots(figsize=figsize)
    freq_mev = np.array(frequencies) * 1000.0

    ax.scatter(freq_mev, nks, edgecolor="black", alpha=0.85)
    ax.set_xlabel("Phonon Energy (meV)")
    ax.set_ylabel(rf"$\bar{{n}}_k$ ({temperature:.0f} K)")

    self._save_or_show(fig, out_dir, file_name, fig_format, plot)

plot_C_omega_vs_penergy

plot_C_omega_vs_penergy(frequencies, C_omega, omega_range, plot=False, out_dir='./', file_name='C_omega_vs_penergy', max_freq=None, fig_format='pdf', figsize=(3.3, 2.5))

Line plot of the thermal spectral density C(ℏω, T) in 1/meV vs energy (meV).

C(ω, T) = Σ_k n̄_k(T) S_k δ(ℏω − ℏω_k) broadened with Gaussians. At T = 0 this plot is a flat zero line.

Parameters:

Name Type Description Default
frequencies (ndarray, shape(nmodes))

Phonon frequencies in eV.

required
C_omega (array - like, shape(n_grid))

Thermal spectral density in eV^(-1).

required
omega_range list[ω_min, ω_max, n_points]

Energy grid parameters in eV.

required
max_freq float

Upper frequency cut-off in eV.

None
plot bool

See :meth:plot_penergy_vs_pmode.

False
out_dir bool

See :meth:plot_penergy_vs_pmode.

False
file_name bool

See :meth:plot_penergy_vs_pmode.

False
fig_format bool

See :meth:plot_penergy_vs_pmode.

False
figsize bool

See :meth:plot_penergy_vs_pmode.

False
Source code in defectpl/plot.py
def plot_C_omega_vs_penergy(
    self,
    frequencies: np.ndarray,
    C_omega: Union[list, np.ndarray],
    omega_range: List[Union[int, float]],
    plot: bool = False,
    out_dir: Union[str, Path] = "./",
    file_name: str = "C_omega_vs_penergy",
    max_freq: Optional[float] = None,
    fig_format: str = "pdf",
    figsize: Tuple[float, float] = (3.3, 2.5),
):
    """
    Line plot of the thermal spectral density C(ℏω, T) in 1/meV vs energy (meV).

    C(ω, T) = Σ_k n̄_k(T) S_k δ(ℏω − ℏω_k) broadened with Gaussians.
    At T = 0 this plot is a flat zero line.

    Parameters
    ----------
    frequencies : numpy.ndarray, shape (nmodes,)
        Phonon frequencies in **eV**.
    C_omega : array-like, shape (n_grid,)
        Thermal spectral density in **eV^(-1)**.
    omega_range : list [ω_min, ω_max, n_points]
        Energy grid parameters in **eV**.
    max_freq : float, optional
        Upper frequency cut-off in **eV**.
    plot, out_dir, file_name, fig_format, figsize
        See :meth:`plot_penergy_vs_pmode`.
    """
    fig, ax = plt.subplots(figsize=figsize)
    omega_set = np.linspace(omega_range[0], omega_range[1], int(omega_range[2]))

    cutoff = max_freq if max_freq is not None else float(max(frequencies))
    mask = omega_set <= cutoff

    C_omega_mev = np.array(C_omega)[mask] / 1000.0
    energies_mev = omega_set[mask] * 1000.0

    ax.plot(energies_mev, C_omega_mev, color="black")
    ax.set_xlabel("Phonon Energy (meV)")
    ax.set_ylabel(r"$C(\hbar\omega,T)$ ($1/\mathrm{meV}$)")
    ax.set_xlim(0, cutoff * 1000.0)

    self._save_or_show(fig, out_dir, file_name, fig_format, plot)

plot_absorption_vs_penergy

plot_absorption_vs_penergy(frequencies, absorption, resolution, xlim, plot=False, out_dir='./', file_name='absorption_vs_penergy', iylim=None, fig_format='pdf', figsize=(3.3, 2.5))

Line plot of the normalised absorption spectrum vs photon energy (eV).

The sideband lies on the high-energy side of the ZPL (phonon absorption raises the photon energy), opposite to PL emission.

Parameters:

Name Type Description Default
frequencies ndarray

Phonon frequencies in eV (unused directly; kept for API consistency).

required
absorption ndarray

Complex or real absorption array from :func:~defectpl.utils.calc_Absorption_Intensity. Absolute value is taken and the result is normalised.

required
resolution int

Number of grid points per eV.

required
xlim tuple of float

(x_min, x_max) for the photon energy axis in eV.

required
iylim tuple of float

(y_min, y_max) for the intensity axis.

None
plot bool

See :meth:plot_penergy_vs_pmode.

False
out_dir bool

See :meth:plot_penergy_vs_pmode.

False
file_name bool

See :meth:plot_penergy_vs_pmode.

False
fig_format bool

See :meth:plot_penergy_vs_pmode.

False
figsize bool

See :meth:plot_penergy_vs_pmode.

False
Source code in defectpl/plot.py
def plot_absorption_vs_penergy(
    self,
    frequencies: np.ndarray,
    absorption: np.ndarray,
    resolution: int,
    xlim: Tuple[float, float],
    plot: bool = False,
    out_dir: Union[str, Path] = "./",
    file_name: str = "absorption_vs_penergy",
    iylim: Optional[Tuple[float, float]] = None,
    fig_format: str = "pdf",
    figsize: Tuple[float, float] = (3.3, 2.5),
):
    """
    Line plot of the normalised absorption spectrum vs photon energy (eV).

    The sideband lies on the **high-energy side** of the ZPL (phonon
    absorption raises the photon energy), opposite to PL emission.

    Parameters
    ----------
    frequencies : numpy.ndarray
        Phonon frequencies in eV (unused directly; kept for API consistency).
    absorption : numpy.ndarray
        Complex or real absorption array from
        :func:`~defectpl.utils.calc_Absorption_Intensity`.
        Absolute value is taken and the result is normalised.
    resolution : int
        Number of grid points per eV.
    xlim : tuple of float
        ``(x_min, x_max)`` for the photon energy axis in eV.
    iylim : tuple of float, optional
        ``(y_min, y_max)`` for the intensity axis.
    plot, out_dir, file_name, fig_format, figsize
        See :meth:`plot_penergy_vs_pmode`.
    """
    fig, ax = plt.subplots(figsize=figsize)

    x_energy_ev = np.arange(len(absorption)) / float(resolution)
    I_abs = np.abs(absorption)
    I_norm = I_abs / np.max(I_abs) if np.max(I_abs) > 0 else I_abs

    ax.plot(x_energy_ev, I_norm, color="black")
    ax.set_ylabel("Absorption (arb. u.)")
    ax.set_xlabel("Photon Energy (eV)")
    ax.set_xlim(xlim[0], xlim[1])

    if iylim:
        ax.set_ylim(iylim[0], iylim[1])
    ax.set_yticks([])

    self._save_or_show(fig, out_dir, file_name, fig_format, plot)

plot_pl_absorption_vs_penergy

plot_pl_absorption_vs_penergy(frequencies, intensity, absorption, resolution, xlim, plot=False, out_dir='./', file_name='pl_absorption_vs_penergy', fig_format='pdf', figsize=(3.3, 2.5))

Overlay of normalised PL (solid) and absorption (dashed) spectra.

Displays the Stokes shift between the PL emission peak and the absorption onset on a shared photon-energy axis.

Parameters:

Name Type Description Default
frequencies ndarray

Phonon frequencies in eV (unused directly; kept for API consistency).

required
intensity ndarray

PL intensity array from :func:~defectpl.utils.calc_Spectrum_Intensity.

required
absorption ndarray

Absorption array from :func:~defectpl.utils.calc_Absorption_Intensity.

required
resolution int

Number of grid points per eV.

required
xlim tuple of float

(x_min, x_max) for the photon energy axis in eV.

required
plot bool

See :meth:plot_penergy_vs_pmode.

False
out_dir bool

See :meth:plot_penergy_vs_pmode.

False
file_name bool

See :meth:plot_penergy_vs_pmode.

False
fig_format bool

See :meth:plot_penergy_vs_pmode.

False
figsize bool

See :meth:plot_penergy_vs_pmode.

False
Source code in defectpl/plot.py
def plot_pl_absorption_vs_penergy(
    self,
    frequencies: np.ndarray,
    intensity: np.ndarray,
    absorption: np.ndarray,
    resolution: int,
    xlim: Tuple[float, float],
    plot: bool = False,
    out_dir: Union[str, Path] = "./",
    file_name: str = "pl_absorption_vs_penergy",
    fig_format: str = "pdf",
    figsize: Tuple[float, float] = (3.3, 2.5),
):
    """
    Overlay of normalised PL (solid) and absorption (dashed) spectra.

    Displays the Stokes shift between the PL emission peak and the
    absorption onset on a shared photon-energy axis.

    Parameters
    ----------
    frequencies : numpy.ndarray
        Phonon frequencies in eV (unused directly; kept for API consistency).
    intensity : numpy.ndarray
        PL intensity array from :func:`~defectpl.utils.calc_Spectrum_Intensity`.
    absorption : numpy.ndarray
        Absorption array from :func:`~defectpl.utils.calc_Absorption_Intensity`.
    resolution : int
        Number of grid points per eV.
    xlim : tuple of float
        ``(x_min, x_max)`` for the photon energy axis in eV.
    plot, out_dir, file_name, fig_format, figsize
        See :meth:`plot_penergy_vs_pmode`.
    """
    fig, ax = plt.subplots(figsize=figsize)

    x_ev = np.arange(len(intensity)) / float(resolution)

    I_pl = np.abs(intensity)
    I_pl = I_pl / np.max(I_pl) if np.max(I_pl) > 0 else I_pl

    I_abs = np.abs(absorption)
    I_abs = I_abs / np.max(I_abs) if np.max(I_abs) > 0 else I_abs

    colors = plt.rcParams["axes.prop_cycle"].by_key()["color"]
    ax.plot(x_ev, I_pl, color=colors[0], label="PL")
    ax.plot(x_ev, I_abs, color=colors[1], linestyle="--", label="Absorption")

    ax.set_ylabel("Intensity (arb. u.)")
    ax.set_xlabel("Photon Energy (eV)")
    ax.set_xlim(xlim[0], xlim[1])
    ax.set_yticks([])
    ax.legend(loc="best")

    self._save_or_show(fig, out_dir, file_name, fig_format, plot)