ndim.py

Equation definitions and derivations using SymPy.


Requires Python packages/modules:

class gme.core.ndim.NdimMixin

Non-dimensionalization supplement to equation definition class.

define_nodimensionalized_Hamiltons_eqns()None

Group Hamilton’s equations into matrix form.

nondimensionalize()None

Non-dimensionalization.

Non-dimensionalize variables, Hamiltonian, and Hamilton’s equations; define dimensionless channel incision number \(\mathsf{Ci}\).

Code

"""
Equation definitions and derivations using :mod:`SymPy <sympy>`.

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

Requires Python packages/modules:
  -  :mod:`SymPy <sympy>`
  -  `GMPLib`_
  -  `GME`_

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

---------------------------------------------------------------------
"""
# Disable these pylint errors because it doesn't understand SymPy syntax
#   - notably minus signs in equations flag an error
# pylint: disable=invalid-unary-operand-type, not-callable

# Library
import warnings
import logging
from typing import Dict

# SymPy
from sympy import (
    Eq,
    Rational,
    sqrt,
    simplify,
    factor,
    solve,
    Matrix,
    sin,
    cos,
    tan,
    asin,
    Abs,
    deg,
    diff,
)

# GMPLib
from gmplib.utils import e2d

# GME
from gme.core.symbols import (
    rx,
    rz,
    px,
    pz,
    beta,
    Lc,
    rxhat,
    rzhat,
    rvec,
    varepsilon,
    varepsilonhat,
    varphi_0,
    varphi_r,
    varphi_rxhat_fn,
    xiv,
    xih,
    xiv_0,
    xih_0,
    beta_0,
    eta,
    mu,
    H,
    Ci,
    th_0,
    tv_0,
    t,
    that,
    pxhat,
    pzhat,
    rdotxhat_thatfn,
    rdotzhat_thatfn,
    pdotxhat_thatfn,
    pdotzhat_thatfn,
)

warnings.filterwarnings("ignore")

__all__ = ["NdimMixin"]


class NdimMixin:
    """Non-dimensionalization supplement to equation definition class."""

    # Prerequisites
    varphi_rx_eqn: Eq
    xi_varphi_beta_eqn: Eq
    pz_xiv_eqn: Eq
    H_varphi_rx_eqn: Eq

    # Definitions
    rx_rxhat_eqn: Eq
    rz_rzhat_eqn: Eq
    varepsilon_varepsilonhat_eqn: Eq
    varepsilonhat_varepsilon_eqn: Eq
    varphi_rxhat_eqn: Eq
    xi_rxhat_eqn: Eq
    xih0_beta0_eqn: Eq
    xiv0_beta0_eqn: Eq
    xih0_xiv0_beta0_eqn: Eq
    xih_xiv_tanbeta_eqn: Eq
    xiv_xih_tanbeta_eqn: Eq
    th0_xih0_eqn: Eq
    tv0_xiv0_eqn: Eq
    th0_beta0_eqn: Eq
    tv0_beta0_eqn: Eq
    t_that_eqn: Eq
    px_pxhat_eqn: Eq
    pz_pzhat_eqn: Eq
    pzhat_xiv_eqn: Eq
    H_varphi_rxhat_eqn: Eq
    H_split: Eq
    H_Ci_eqn: Eq
    degCi_H0p5_eqn: Eq
    sinCi_xih0_eqn: Eq
    Ci_xih0_eqn: Eq
    sinCi_beta0_eqn: Eq
    Ci_beta0_eqn: Eq
    beta0_Ci_eqn: Eq
    rdotxhat_eqn: Eq
    rdotzhat_eqn: Eq
    pdotxhat_eqn: Eq
    pdotzhat_eqn: Eq
    xih0_Ci_eqn: Eq
    xih0_Lc_varphi0_Ci_eqn: Eq
    xiv0_xih0_Ci_eqn: Eq
    xiv0_Lc_varphi0_Ci_eqn: Eq
    varphi0_Lc_xiv0_Ci_eqn: Eq
    ratio_xiv0_xih0_eqn: Eq
    hamiltons_ndim_eqns: Eq

    def nondimensionalize(self) -> None:
        r"""
        Non-dimensionalization.

        Non-dimensionalize variables, Hamiltonian, and Hamilton's equations;
        define dimensionless channel incision number :math:`\mathsf{Ci}`.
        """
        logging.info("gme.core.ndim.nondimensionalize")
        varsub: Dict = {}

        self.rx_rxhat_eqn = Eq(rx, Lc * rxhat)
        self.rz_rzhat_eqn = Eq(rz, Lc * rzhat)

        self.varepsilon_varepsilonhat_eqn = Eq(varepsilon, Lc * varepsilonhat)
        self.varepsilonhat_varepsilon_eqn = Eq(
            varepsilonhat,
            solve(self.varepsilon_varepsilonhat_eqn, varepsilonhat)[0],
        )

        self.varphi_rxhat_eqn = Eq(
            varphi_rxhat_fn(rxhat),
            factor(
                self.varphi_rx_eqn.rhs.subs(e2d(self.rx_rxhat_eqn))
                .subs(e2d(self.varepsilon_varepsilonhat_eqn))
                .subs(varsub)
            ),
        )

        self.xi_rxhat_eqn = simplify(
            self.xi_varphi_beta_eqn.subs(
                {varphi_r(rvec): varphi_rxhat_fn(rxhat)}
            ).subs(e2d(self.varphi_rxhat_eqn))
        )

        self.xih0_beta0_eqn = simplify(
            Eq(
                xih_0,
                (self.xi_rxhat_eqn.rhs / sin(beta_0))
                .subs(e2d(self.varphi_rx_eqn))
                .subs({Abs(sin(beta)): sin(beta_0)})
                .subs({rxhat: 0})
                .subs(varsub),
            )
        )
        self.xiv0_beta0_eqn = simplify(
            Eq(
                xiv_0,
                (self.xi_rxhat_eqn.rhs / cos(beta_0))
                .subs(e2d(self.varphi_rx_eqn))
                .subs({Abs(sin(beta)): sin(beta_0)})
                .subs({rxhat: 0})
                .subs(varsub),
            )
        )
        self.xih0_xiv0_beta0_eqn = Eq(
            (xih_0),
            xiv_0
            * simplify(
                (xih_0 / xiv_0)
                .subs(e2d(self.xiv0_beta0_eqn))
                .subs(e2d(self.xih0_beta0_eqn))
            ),
        )
        self.xih_xiv_tanbeta_eqn = Eq(xih, xiv / tan(beta))
        self.xiv_xih_tanbeta_eqn = Eq(
            xiv, solve(self.xih_xiv_tanbeta_eqn, xiv)[0]
        )
        # Eq(xiv, xih*tan(beta))

        self.th0_xih0_eqn = Eq(th_0, (Lc / xih_0))
        self.tv0_xiv0_eqn = Eq(tv_0, (Lc / xiv_0))

        self.th0_beta0_eqn = factor(
            simplify(self.th0_xih0_eqn.subs(e2d(self.xih0_beta0_eqn)))
        )
        self.tv0_beta0_eqn = simplify(
            self.tv0_xiv0_eqn.subs(e2d(self.xiv0_beta0_eqn))
        )

        self.t_that_eqn = Eq(t, th_0 * that)
        self.px_pxhat_eqn = Eq(px, pxhat / xih_0)
        self.pz_pzhat_eqn = Eq(pz, pzhat / xih_0)

        self.pzhat_xiv_eqn = Eq(
            pzhat, solve(self.pz_xiv_eqn.subs(e2d(self.pz_pzhat_eqn)), pzhat)[0]
        )

        self.H_varphi_rxhat_eqn = factor(
            simplify(
                self.H_varphi_rx_eqn.subs(varsub)
                .subs(e2d(self.varepsilon_varepsilonhat_eqn))
                .subs(e2d(self.rx_rxhat_eqn))
                .subs(e2d(self.px_pxhat_eqn))
                .subs(e2d(self.pz_pzhat_eqn))
            )
        )

        self.H_split = (
            2
            * pxhat ** (-2 * eta)
            * (pxhat ** 2 + pzhat ** 2) ** (eta - 1)
            * (1 - rxhat + varepsilonhat) ** (-4 * mu)
        )
        self.H_Ci_eqn = Eq(
            H,
            simplify(
                ((sin(Ci)) ** (2 * (1 - eta)) / self.H_split).subs(
                    e2d(self.H_varphi_rxhat_eqn)
                )
            ),
        )
        self.degCi_H0p5_eqn = Eq(
            Ci, deg(solve(self.H_Ci_eqn.subs({H: Rational(1, 2)}), Ci)[0])
        )
        self.sinCi_xih0_eqn = Eq(
            sin(Ci),
            (
                sqrt(
                    simplify(
                        (H * self.H_split).subs(e2d(self.H_varphi_rxhat_eqn))
                    )
                )
            )
            ** (1 / (1 - eta)),
        )
        self.Ci_xih0_eqn = Eq(Ci, asin(self.sinCi_xih0_eqn.rhs))
        self.sinCi_beta0_eqn = Eq(
            sin(Ci),
            factor(
                simplify(self.sinCi_xih0_eqn.rhs.subs(e2d(self.xih0_beta0_eqn)))
            )
            .subs(
                {
                    sin(beta_0)
                    * sin(beta_0) ** (-eta): (sin(beta_0) ** (1 - eta))
                }
            )
            .subs(
                {(sin(beta_0) ** (1 - eta)) ** (-1 / (eta - 1)): sin(beta_0)}
            ),
        )
        self.Ci_beta0_eqn = Eq(Ci, asin(self.sinCi_beta0_eqn.rhs))
        self.beta0_Ci_eqn = Eq(beta_0, solve(self.Ci_beta0_eqn, beta_0)[1])

        self.rdotxhat_eqn = Eq(
            rdotxhat_thatfn(that), simplify(diff(self.H_Ci_eqn.rhs, pxhat))
        )
        self.rdotzhat_eqn = Eq(
            rdotzhat_thatfn(that), simplify(diff(self.H_Ci_eqn.rhs, pzhat))
        )

        self.pdotxhat_eqn = Eq(
            pdotxhat_thatfn(that), simplify(-diff(self.H_Ci_eqn.rhs, rxhat))
        )
        self.pdotzhat_eqn = Eq(
            pdotzhat_thatfn(that), simplify(-diff(self.H_Ci_eqn.rhs, rzhat))
        )

        self.xih0_Ci_eqn = factor(
            self.xih0_beta0_eqn.subs(e2d(self.beta0_Ci_eqn))
        )

        # .subs({varepsilonhat:0})
        self.xih0_Lc_varphi0_Ci_eqn = self.xih0_Ci_eqn
        self.xiv0_xih0_Ci_eqn = self.xiv_xih_tanbeta_eqn.subs(
            {xiv: xiv_0, xih: xih_0, beta: beta_0}
        ).subs(e2d(self.beta0_Ci_eqn))
        # .subs({varepsilonhat:0})
        self.xiv0_Lc_varphi0_Ci_eqn = simplify(
            self.xiv0_xih0_Ci_eqn.subs(e2d(self.xih0_Lc_varphi0_Ci_eqn))
        )
        self.varphi0_Lc_xiv0_Ci_eqn = Eq(
            varphi_0, solve(self.xiv0_Lc_varphi0_Ci_eqn, varphi_0)[0]
        ).subs({Abs(cos(Ci)): cos(Ci)})

        self.ratio_xiv0_xih0_eqn = Eq(
            xiv_0 / xih_0, (xiv_0 / xih_0).subs(e2d(self.xiv0_xih0_Ci_eqn))
        )

    def define_nodimensionalized_Hamiltons_eqns(self) -> None:
        """Group Hamilton's equations into matrix form."""
        logging.info("gme.core.ndim.define_nodimensionalized_Hamiltons_eqns")
        self.hamiltons_ndim_eqns = Matrix(
            (
                self.rdotxhat_eqn.rhs,
                self.rdotzhat_eqn.rhs,
                self.pdotxhat_eqn.rhs,
                self.pdotzhat_eqn.rhs,
            )
        )


#