ray_angles.py

Visualization of ray angles.


Requires Python packages/modules:

class gme.plot.ray_angles.RayAngles(dpi: int = 100, font_size: int = 11)

Visualization of ray angles.

Extends gme.plot.base.Graphing.

alpha_beta(gmes: Union[gme.ode.single_ray.SingleRaySolution, gme.ode.time_invariant.TimeInvariantSolution, gme.ode.velocity_boundary.VelocityBoundarySolution], gmeq: Union[gme.core.equations.Equations, gme.core.equations_extended.EquationsGeodesic, gme.core.equations_extended.EquationsIdtx, gme.core.equations_extended.EquationsIbc], sub: Dict, name: str, fig_size: Optional[Tuple[float, float]] = None, dpi: Optional[int] = None, aspect: float = 1, n_points: int = 201, x_limits: Optional[Tuple[Optional[float], Optional[float]]] = None, y_limits: Optional[Tuple[Optional[float], Optional[float]]] = None, do_legend: bool = True, do_pub_label: bool = False, pub_label_xy: Tuple[float, float] = (0.88, 0.7), pub_label: str = '', do_etaxi_label: bool = True, eta_label_xy: Tuple[float, float] = (0.5, 0.85))None

Plot ray angle.

Plot ray vector angle \(\alpha\) versus normal-slowness covector angle \(\beta\) generated by a time-invariant solution.

Parameters
  • gmes – instance of single-ray solution class defined in gme.ode.single_ray

  • gmeq – GME model equations class instance defined in gme.core.equations or similar

  • sub – dictionary of model parameter values to be used for equation substitutions

  • name – name of figure (key in figure dictionary)

  • fig_size – optional figure width and height in inches

  • dpi – optional rasterization resolution

  • n_points – optional sample rate along each curve

  • x_limits – optional [x_min, x_max] horizontal plot range

  • y_limits – optional [z_min, z_max] vertical plot range

  • do_legend – optionally plot legend?

  • pub_label_xy – optional position of ‘publication’ annotation

  • eta_label_xy – optional position of \(\eta\) annotation

  • do_pub_label – optionally do ‘publication’ annotation’?

  • do_etaxi_label – optionally do \(\eta\), \(\xi\) annotation’?

  • pub_label – optional ‘publication’ annotation text

angular_disparity(gmes: Union[gme.ode.single_ray.SingleRaySolution, gme.ode.time_invariant.TimeInvariantSolution, gme.ode.velocity_boundary.VelocityBoundarySolution], gmeq: Union[gme.core.equations.Equations, gme.core.equations_extended.EquationsGeodesic, gme.core.equations_extended.EquationsIdtx, gme.core.equations_extended.EquationsIbc], name: str, fig_size: Optional[Tuple[float, float]] = None, dpi: Optional[int] = None, n_points: int = 201, x_limits: Optional[Tuple[Optional[float], Optional[float]]] = None, y_limits: Optional[Tuple[Optional[float], Optional[float]]] = None, do_legend: bool = True, aspect: float = 0.75, do_pub_label: bool = False, pub_label: str = '', pub_label_xy: Tuple[float, float] = (0.88, 0.7), eta_label_xy: Tuple[float, float] = (0.5, 0.85))None

Plot angular disparity/anisotropy.

Plot ray vector angular disparity \(\alpha-\beta\) generated by a time-invariant solution.

Parameters
  • gmes – instance of single ray solution class defined in gme.ode.raytracing

  • gmeq – GME model equations class instance defined in gme.core.equations or similar

  • n_points – optional sample rate along each curve

  • x_limits – optional [x_min, x_max] horizontal plot range

  • y_limits – optional [z_min, z_max] vertical plot range

  • do_legend – optional plot legend?

profile_alpha(gmes: Union[gme.ode.single_ray.SingleRaySolution, gme.ode.time_invariant.TimeInvariantSolution, gme.ode.velocity_boundary.VelocityBoundarySolution], gmeq: Union[gme.core.equations.Equations, gme.core.equations_extended.EquationsGeodesic, gme.core.equations_extended.EquationsIdtx, gme.core.equations_extended.EquationsIbc], sub: Dict, name: str, fig_size: Optional[Tuple[float, float]] = None, dpi: Optional[int] = None, n_points: int = 201, do_legend: bool = True, eta_label_xy: Tuple[float, float] = (0.25, 0.5))None

Plot ray vector angle \(\alpha\) along a time-invariant profile.

Parameters
  • gmes – instance of single ray solution class defined in gme.ode.raytracing

  • gmeq – GME model equations class instance defined in gme.core.equations or similar

  • n_points – optional sample rate along each curve

  • do_legend – optional plot legend?

profile_angular_disparity(gmes: Union[gme.ode.single_ray.SingleRaySolution, gme.ode.time_invariant.TimeInvariantSolution, gme.ode.velocity_boundary.VelocityBoundarySolution], gmeq: Union[gme.core.equations.Equations, gme.core.equations_extended.EquationsGeodesic, gme.core.equations_extended.EquationsIdtx, gme.core.equations_extended.EquationsIbc], sub: Dict, name: str, fig_size: Optional[Tuple[float, float]] = None, dpi: Optional[int] = None, n_points: int = 201, do_pub_label: bool = False, pub_label: str = '(a)', pub_label_xy: Tuple[float, float] = (0.5, 0.2), eta_label_xy: Tuple[float, float] = (0.25, 0.5), var_label_xy: Tuple[float, float] = (0.8, 0.35))None

Plot angular disparity/anisotropy along a time-invariant profile.

Parameters
  • gmes – instance of single-ray solution class defined in gme.ode.single_ray

  • gmeq – GME model equations class instance defined in gme.core.equations or similar

  • sub – dictionary of model parameter values to be used for equation substitutions

  • name – name of figure (key in figure dictionary)

  • fig_size – optional figure width and height in inches

  • dpi – optional rasterization resolution

  • n_points – optional sample rate along each curve

  • pub_label_xy – optional position of ‘publication’ annotation

  • eta_label_xy – optional position of \(\eta\) annotation

  • var_label_xy – optional position of variable (e.g. \(\psi\)) annotation

  • do_pub_label – optionally do ‘publication’ annotation’?

  • pub_label – optional ‘publication’ annotation text

psi_eta_alpha(gmeq: Union[gme.core.equations.Equations, gme.core.equations_extended.EquationsGeodesic, gme.core.equations_extended.EquationsIdtx, gme.core.equations_extended.EquationsIbc], name: str, fig_size: Optional[Tuple[float, float]] = (8, 4), dpi: Optional[int] = None, n_points: int = 5000)None

Plot anisotropy.

Plot anisotropy angle \(\psi\) at a function of \(\eta\) for selected values of \(\alpha\).

Parameters
  • gmeq – GME model equations class instance defined in gme.core.equations or similar

  • name – name of figure (key in figure dictionary)

  • fig_size – optional figure width and height in inches

  • dpi – optional rasterization resolution

  • n_points – optional sample rate along each curve

Code

"""
Visualization of ray angles.

---------------------------------------------------------------------

Requires Python packages/modules:
  -  :mod:`NumPy <numpy>`
  -  :mod:`SymPy <sympy>`
  -  `Cycler`_
  -  :mod:`MatPlotLib <matplotlib>`
  -  `GME`_

.. _GMPLib: https://github.com/geomorphysics/GMPLib
.. _GME: https://github.com/geomorphysics/GME
.. _Matrix:
    https://docs.sympy.org/latest/modules/matrices/immutablematrices.html
.. _Cycler: https://matplotlib.org/cycler/

---------------------------------------------------------------------
"""
# Library
import warnings

# import logging
from typing import Tuple, Dict, Optional, Callable, Union, List

# Cycler
from cycler import cycler

# NumPy
import numpy as np

# SymPy
from sympy import deg

# MatPlotLib
import matplotlib.pyplot as plt
from matplotlib.pyplot import Axes

# GME
from gme.core.symbols import Ci
from gme.core.equations import Equations
from gme.core.equations_extended import (
    EquationsGeodesic,
    EquationsIdtx,
    EquationsIbc,
)
from gme.ode.single_ray import SingleRaySolution
from gme.ode.time_invariant import TimeInvariantSolution
from gme.ode.velocity_boundary import VelocityBoundarySolution
from gme.plot.base import Graphing

warnings.filterwarnings("ignore")

__all__ = ["RayAngles"]


class RayAngles(Graphing):
    """
    Visualization of ray angles.

    Extends :class:`gme.plot.base.Graphing`.
    """

    # def demo(
    #     self,
    #     var_types: List,
    # ) -> None:
    #     for var_type in var_types:
    #         logging.info(var_type)

    def alpha_beta(
        self,
        gmes: Union[
            SingleRaySolution, TimeInvariantSolution, VelocityBoundarySolution
        ],
        gmeq: Union[Equations, EquationsGeodesic, EquationsIdtx, EquationsIbc],
        sub: Dict,
        name: str,
        fig_size: Optional[Tuple[float, float]] = None,
        dpi: Optional[int] = None,
        aspect: float = 1,
        n_points: int = 201,
        x_limits: Optional[Tuple[Optional[float], Optional[float]]] = None,
        y_limits: Optional[Tuple[Optional[float], Optional[float]]] = None,
        do_legend: bool = True,
        do_pub_label: bool = False,
        pub_label_xy: Tuple[float, float] = (0.88, 0.7),
        pub_label: str = "",
        do_etaxi_label: bool = True,
        eta_label_xy: Tuple[float, float] = (0.5, 0.85),
    ) -> None:
        r"""
        Plot ray angle.

        Plot ray vector angle :math:`\alpha`
        versus normal-slowness covector angle :math:`\beta`
        generated by a time-invariant solution.

        Args:
            gmes:
                instance of single-ray solution class
                defined in :mod:`gme.ode.single_ray`
            gmeq:
                GME model equations class instance defined in
                :mod:`gme.core.equations` or similar
            sub:
                dictionary of model parameter values to be used for
                equation substitutions
            name:
                name of figure (key in figure dictionary)
            fig_size:
                optional figure width and height in inches
            dpi:
                optional rasterization resolution
            n_points:
                optional sample rate along each curve
            x_limits:
                optional [x_min, x_max] horizontal plot range
            y_limits:
                optional [z_min, z_max] vertical plot range
            do_legend:
                optionally plot legend?
            pub_label_xy:
                optional position of 'publication' annotation
            eta_label_xy:
                optional position of :math:`\eta` annotation
            do_pub_label:
                optionally do 'publication' annotation'?
            do_etaxi_label:
                optionally do :math:`\eta`, :math:`\xi` annotation'?
            pub_label:
                optional 'publication' annotation text
        """
        _ = self.create_figure(name, fig_size=fig_size, dpi=dpi)
        # eta_label_xy = [0.5,0.85] if eta_label_xy is None else eta_label_xy
        # pub_label_xy = [0.88,0.7] if pub_label_xy is None else pub_label_xy

        x_array = np.linspace(0, 1, n_points)
        alpha_array = np.rad2deg(gmes.alpha_interp(x_array))
        beta_p_array = np.rad2deg(gmes.beta_p_interp(x_array))
        plt.plot(
            beta_p_array,
            alpha_array - 90,
            "b",
            ls="-",
            label=r"$\alpha(\beta)-90$",
        )
        plt.xlabel(
            r"Surface normal angle  $\beta$  [${\degree}$ from vertical]"
        )
        plt.ylabel(r"Ray angle  $\alpha\,$  [${\degree}$ from horiz]")
        plt.grid(True, ls=":")

        axes = plt.gca()
        axes.set_aspect(aspect)
        xlim = axes.get_xlim()
        # ylim = axes.get_ylim()
        axes.set_yticks(
            (-40, -30, -20, -10, 0, 10, 20, 30, 40, 50, 60, 70, 80, 90)
        )
        if x_limits is None:
            axes.set_xlim(-(xlim[1] - xlim[0]) / 30, xlim[1])
        else:
            axes.set_xlim(*x_limits)
        if y_limits is not None:
            axes.set_ylim(*y_limits)
        if do_legend:
            plt.legend()
        if do_etaxi_label:
            # plt.text(0.5,0.85, r'$\eta={}$'.format(gmeq.eta_),
            plt.text(
                *eta_label_xy,
                rf"$\eta={gmeq.eta_}$"
                + r"$\quad\mathsf{Ci}=$"
                + rf"${round(float(deg(Ci.subs(sub))))}\degree$",
                transform=axes.transAxes,
                horizontalalignment="center",
                verticalalignment="center",
                fontsize=14,
                color="k",
            )
        if do_pub_label:
            plt.text(
                *pub_label_xy,
                pub_label,
                transform=axes.transAxes,
                horizontalalignment="center",
                verticalalignment="center",
                fontsize=16,
                color="k",
            )

    def angular_disparity(
        self,
        gmes: Union[
            SingleRaySolution, TimeInvariantSolution, VelocityBoundarySolution
        ],
        gmeq: Union[Equations, EquationsGeodesic, EquationsIdtx, EquationsIbc],
        name: str,
        fig_size: Optional[Tuple[float, float]] = None,
        dpi: Optional[int] = None,
        n_points: int = 201,
        x_limits: Optional[Tuple[Optional[float], Optional[float]]] = None,
        y_limits: Optional[Tuple[Optional[float], Optional[float]]] = None,
        do_legend: bool = True,
        aspect: float = 0.75,
        do_pub_label: bool = False,
        pub_label: str = "",
        pub_label_xy: Tuple[float, float] = (0.88, 0.7),
        eta_label_xy: Tuple[float, float] = (0.5, 0.85),
    ) -> None:
        r"""
        Plot angular disparity/anisotropy.

        Plot ray vector angular disparity :math:`\alpha-\beta`
        generated by a time-invariant solution.

        Args:
            gmes:
                instance of single ray solution class defined in
                :mod:`gme.ode.raytracing`
            gmeq:
                GME model equations class instance defined in
                :mod:`gme.core.equations` or similar
            n_points:
                optional sample rate along each curve
            x_limits:
                optional [x_min, x_max] horizontal plot range
            y_limits:
                optional [z_min, z_max] vertical plot range
            do_legend:
                optional plot legend?
        """
        _ = self.create_figure(name, fig_size=fig_size, dpi=dpi)
        # pub_label_xy = [0.5,0.2] if pub_label_xy is None else pub_label_xy
        # eta_label_xy = [0.5,0.81] if eta_label_xy is None else eta_label_xy
        # var_label_xy = [0.85,0.81] if var_label_xy is None else var_label_xy

        x_array = np.linspace(0, 1, n_points)
        alpha_array = np.rad2deg(gmes.alpha_interp(x_array))
        beta_p_array = np.rad2deg(gmes.beta_p_interp(x_array))
        plt.plot(
            beta_p_array,
            alpha_array - beta_p_array,
            "DarkBlue",
            ls="-",
            label=r"$\alpha(\beta)-90$",
        )
        plt.xlabel(
            r"Surface normal angle  $\beta$  [${\degree}$ from vertical]"
        )
        plt.ylabel(r"Angular disparity  $(\alpha-\beta\!)+90\,$  [${\degree}$]")
        plt.grid(True, ls=":")

        axes = plt.gca()
        axes.set_aspect(aspect)
        xlim = axes.get_xlim()
        # ylim = axes.get_ylim()
        axes.set_yticks((-20, -10, 0, 10, 20, 30, 40, 50, 60, 70, 80, 90))
        if x_limits is None:
            axes.set_xlim(-(xlim[1] - xlim[0]) / 30, xlim[1])
        else:
            axes.set_xlim(*x_limits)
        if y_limits is not None:
            axes.set_ylim(*y_limits)
        if do_legend:
            plt.legend()
        plt.text(
            *eta_label_xy,
            rf"$\eta={gmeq.eta_}$",
            transform=axes.transAxes,
            horizontalalignment="center",
            verticalalignment="center",
            fontsize=14,
            color="k",
        )
        if do_pub_label:
            plt.text(
                *pub_label_xy,
                pub_label,
                transform=axes.transAxes,
                horizontalalignment="center",
                verticalalignment="center",
                fontsize=16,
                color="k",
            )

    def profile_angular_disparity(
        self,
        gmes: Union[
            SingleRaySolution, TimeInvariantSolution, VelocityBoundarySolution
        ],
        gmeq: Union[Equations, EquationsGeodesic, EquationsIdtx, EquationsIbc],
        sub: Dict,
        name: str,
        fig_size: Optional[Tuple[float, float]] = None,
        dpi: Optional[int] = None,
        n_points: int = 201,
        do_pub_label: bool = False,
        pub_label: str = "(a)",
        pub_label_xy: Tuple[float, float] = (0.5, 0.2),
        eta_label_xy: Tuple[float, float] = (0.25, 0.5),
        var_label_xy: Tuple[float, float] = (0.8, 0.35),
    ) -> None:
        r"""
        Plot angular disparity/anisotropy along a time-invariant profile.

        Args:
            gmes:
                instance of single-ray solution class
                defined in :mod:`gme.ode.single_ray`
            gmeq:
                GME model equations class instance defined in
                :mod:`gme.core.equations` or similar
            sub:
                dictionary of model parameter values to be used for
                equation substitutions
            name:
                name of figure (key in figure dictionary)
            fig_size:
                optional figure width and height in inches
            dpi:
                optional rasterization resolution
            n_points:
                optional sample rate along each curve
            pub_label_xy:
                optional position of 'publication' annotation
            eta_label_xy:
                optional position of :math:`\eta` annotation
            var_label_xy:
                optional position of variable (e.g. :math:`\psi`) annotation
            do_pub_label:
                optionally do 'publication' annotation'?
            pub_label:
                optional 'publication' annotation text
        """
        _ = self.create_figure(name, fig_size=fig_size, dpi=dpi)
        # pub_label_xy = [0.5,0.2] if pub_label_xy is None else pub_label_xy
        # eta_label_xy = [0.25,0.5] if eta_label_xy is None else eta_label_xy
        # var_label_xy = [0.8,0.35] if var_label_xy is None else var_label_xy

        x_array: np.ndarray = np.linspace(0, 1, n_points)
        # x_dbl_array = np.linspace(0,1,n_points*2-1)
        angular_diff_array: np.ndarray = np.rad2deg(
            gmes.alpha_interp(x_array) - gmes.beta_p_interp(x_array)
        )
        plt.plot(
            x_array,
            angular_diff_array,
            "DarkBlue",
            ls="-",
            lw=1.5,
            label=r"$\alpha(x)-\beta(x)$",
        )
        axes: Axes = plt.gca()
        # ylim = plt.ylim()
        axes.set_yticks((-30, 0, 30, 60, 90))
        axes.set_ylim(-5, 95)
        plt.grid(True, ls=":")

        plt.xlabel(r"Distance, $x/L_{\mathrm{c}}$  [-]", fontsize=13)
        plt.ylabel(
            r"Anisotropy,  $\psi = \alpha-\beta+90$  [${\degree}$]", fontsize=12
        )
        if not do_pub_label:
            plt.legend(loc="lower left", fontsize=11, framealpha=0.95)
        plt.text(
            *pub_label_xy,
            pub_label if do_pub_label else rf"$\eta={gmeq.eta_}$",
            transform=axes.transAxes,
            horizontalalignment="center",
            verticalalignment="center",
            fontsize=16,
            color="k",
        )
        plt.text(
            *var_label_xy,
            r"$\psi(x)$" if do_pub_label else "",
            transform=axes.transAxes,
            horizontalalignment="center",
            verticalalignment="center",
            fontsize=18,
            color="k",
        )
        plt.text(
            *eta_label_xy,
            rf"$\eta={gmeq.eta_}$"
            + r"$\quad\mathsf{Ci}=$"
            + rf"${round(float(deg(Ci.subs(sub))))}\degree$",
            transform=axes.transAxes,
            horizontalalignment="center",
            verticalalignment="center",
            fontsize=14,
            color="k",
        )

    def profile_alpha(
        self,
        gmes: Union[
            SingleRaySolution, TimeInvariantSolution, VelocityBoundarySolution
        ],
        gmeq: Union[Equations, EquationsGeodesic, EquationsIdtx, EquationsIbc],
        sub: Dict,
        name: str,
        fig_size: Optional[Tuple[float, float]] = None,
        dpi: Optional[int] = None,
        n_points: int = 201,
        do_legend: bool = True,
        eta_label_xy: Tuple[float, float] = (0.25, 0.5),
    ) -> None:
        r"""
        Plot ray vector angle :math:`\alpha` along a time-invariant profile.

        Args:
            gmes:
                instance of single ray solution class defined in
                :mod:`gme.ode.raytracing`
            gmeq:
                GME model equations class instance defined in
                :mod:`gme.core.equations` or similar
            n_points:
                optional sample rate along each curve
            do_legend:
                optional plot legend?
        """
        _ = self.create_figure(name, fig_size=fig_size, dpi=dpi)

        x_array: np.ndarray = np.linspace(0, 1, n_points)
        alpha_array: np.ndarray = np.rad2deg(gmes.alpha_interp(x_array))
        plt.plot(
            x_array, alpha_array - 90, "DarkBlue", ls="-", label=r"$\alpha(x)$"
        )
        # x_array = np.linspace(0,1,11)
        # pz0_ = gmes.pz0
        # # TBD
        # alpha_array \
        #     = [(np.mod(180+np.rad2deg(float(
        #         atan(gmeq.tanalpha_pxpz_eqn.rhs
        #                 .subs({px:px_value(x_,pz0_),pz:pz0_})))),180))
        #        for x_ in x_array]
        plt.xlabel(r"Distance, $x/L_{\mathrm{c}}$  [-]")
        plt.ylabel(r"Ray dip  $\alpha\!\,$  [${\degree}$ from horiz]")
        plt.grid(True, ls=":")

        if do_legend:
            plt.legend()
        axes: Axes = plt.gca()
        plt.text(
            *eta_label_xy,
            rf"$\eta={gmeq.eta_}$"
            + r"$\quad\mathsf{Ci}=$"
            + rf"${round(float(deg(Ci.subs(sub))))}\degree$",
            transform=axes.transAxes,
            horizontalalignment="center",
            verticalalignment="center",
            fontsize=14,
            color="k",
        )
        # plt.text(0.8,0.7, rf'$\eta={gmeq.eta_}$', transform=axes.transAxes,
        #          horizontalalignment='center', verticalalignment='center',
        #          fontsize=14, color='k')

    def psi_eta_alpha(
        self,
        gmeq: Union[Equations, EquationsGeodesic, EquationsIdtx, EquationsIbc],
        name: str,
        fig_size: Optional[Tuple[float, float]] = (8, 4),
        dpi: Optional[int] = None,
        n_points: int = 5000,
    ) -> None:
        r"""
        Plot anisotropy.

        Plot anisotropy angle :math:`\psi` at a function of :math:`\eta`
        for selected values of :math:`\alpha`.

        Args:
            gmeq:
                GME model equations class instance defined in
                :mod:`gme.core.equations` or similar
            name:
                name of figure (key in figure dictionary)
            fig_size:
                optional figure width and height in inches
            dpi:
                optional rasterization resolution
            n_points:
                optional sample rate along each curve
        """
        _ = self.create_figure(name, fig_size=fig_size, dpi=dpi)
        axes = plt.gca()
        default_cycler = cycler(color="bgrcmyk")

        plt.grid(":")
        plt.ylabel(r"Anisotropy  $\psi(\eta; \alpha)$   [$\degree$]")
        plt.xlabel(r"Exponent  $\eta$   [-]")
        plt.xlim(0, 2)
        y_limits = (0, 90)

        from sympy import solve, lambdify
        from gme.core.symbols import alpha_ext, beta_crit, alpha, beta, eta

        alpha_ext_ = solve(gmeq.tanalpha_ext_eqn, alpha_ext)[0]
        beta_crit_ = solve(gmeq.tanbeta_crit_eqn, beta_crit)[0]
        eta_solns_ = solve(gmeq.tanalpha_ext_eqn, eta)
        eta_gt1_lambda = lambdify(alpha_ext, eta_solns_[1])
        eta_lt1_lambda = lambdify(alpha_ext, eta_solns_[0])
        psi_crit_eqn = gmeq.psi_alpha_beta_eqn.subs(
            {alpha: alpha_ext_, beta: beta_crit_}
        )
        psi_crit_lambda = lambdify(eta, psi_crit_eqn.rhs)
        alpha_list: List[float] = [0.1, 2.0, 6.4, 11.5, 19.3]

        def plot_partial(
            eta_range_: Tuple[float, float],
            axes_: Axes,
            dashes_: Tuple[float, float],
            alpha_list_: List[float],
            alpha_sign_: float,
            y_limits_: Tuple[float, float],
            loc_: Tuple[float, float],
        ) -> None:
            d2r: Callable = np.deg2rad
            r2d: Callable = np.rad2deg
            axes_.set_prop_cycle(default_cycler)
            eta_array_: np.ndarray = np.linspace(*eta_range_, n_points)
            for alpha_ in alpha_list_:
                psi_array_ = np.concatenate(
                    [
                        [
                            gmeq.psi_eta_beta_lambdas[0](
                                eta_, d2r(alpha_ * alpha_sign_)
                            )
                            for eta_ in (
                                eta_array_
                                if alpha_sign_ == -1
                                else np.flip(eta_array_)
                            )
                        ],
                        [
                            gmeq.psi_eta_beta_lambdas[1](
                                eta_, d2r(alpha_ * alpha_sign_)
                            )
                            for eta_ in (
                                np.flip(eta_array_)
                                if alpha_sign_ == -1
                                else eta_array_
                            )
                        ],
                    ]
                )
                eta_rept_array_ = np.concatenate(
                    [eta_array_, np.flip(eta_array_)]
                    if alpha_sign_ == -1
                    else [np.flip(eta_array_), eta_array_]
                )
                axes_.plot(
                    eta_rept_array_[np.isfinite(psi_array_)],
                    np.rad2deg(psi_array_[np.isfinite(psi_array_)]),
                    dashes=dashes_,
                    label=rf"$\alpha={alpha_*alpha_sign_}\degree$",
                )
            psi_crit_array: np.ndarray = r2d(psi_crit_lambda(eta_array_))
            axes_.plot(
                eta_array_,
                psi_crit_array,
                color="k",
                label=r"$\psi_c(\eta)$" if alpha_sign_ == 1 else "",
            )
            axes_.set_prop_cycle(default_cycler)
            if alpha_sign_ == 1:
                eta_array_ = eta_gt1_lambda(d2r(np.array(alpha_list_)))
                psi_crit_array = r2d(psi_crit_lambda(eta_array_))
                for (eta_, psi_crit_) in zip(eta_array_, psi_crit_array):
                    axes_.plot(eta_, psi_crit_, "o")
            else:
                eta_array_ = eta_lt1_lambda(d2r(np.array(alpha_list_)))
                psi_crit_array = r2d(psi_crit_lambda(eta_array_))
                for (eta_, psi_crit_) in zip(eta_array_, psi_crit_array):
                    axes_.plot(eta_, psi_crit_, "o")
            axes_.set_ylim(*y_limits_)
            axes_.legend(loc=loc_)

        plot_partial(
            eta_range_=(0, 1),
            axes_=axes,
            dashes_=(10, 2),
            alpha_list_=alpha_list,
            alpha_sign_=-1,
            y_limits_=y_limits,
            loc_=(1.06, 0.1)
            # loc_=(0.008, 0.46)
        )
        plot_partial(
            eta_range_=(1, 2),
            axes_=axes.twinx(),
            dashes_=(1, 0),
            alpha_list_=alpha_list[:-1],
            alpha_sign_=+1,
            y_limits_=y_limits,
            loc_=(1.06, 0.6)
            # loc_=(1.06, 0.35)
        )


#
#