'''
Created on 12-Jul-2021

@author: rishi_priyansh
'''
import inspect
import traceback
import random

from sympy import (latex, Symbol, symbols, UnevaluatedExpr as ueval, Function, Derivative,
                   Eq, solve, conjugate, Integral, Rational
                   )
import sympy as sp
from xv.km.util import SympyUnitSystem

from ._base_helper import BaseHelper
from sympy.physics.quantum.constants import hbar

from xv.util import list_to_str

# _quantum_atomic_model_helper
# QuantumAtomicModelHelper


class QuantumAtomicModelHelper(BaseHelper):
    def __init__(self, **kwargs):
        super().__init__(**kwargs)
        pass
    
    def get_problem_basic_concepts_overview(self, **kwargs):
        sInfo = f"""Processing {self.__class__.__name__}.{inspect.stack()[0][3]}: 
        """
        self._log(sInfo.strip())

        methods = [
            self._get_electrostatic_potential_energy_of_an_electron,
            self._get_graph_electrostatic_potential_energy_of_an_electron,
            self._get_heisenbergs_uncertainty_principal,
            self._get_momentum_of_electron_in_an_atom,
            self._get_electrostatic_kinetic_energy_of_an_electron,
            self._get_total_mechanical_energy_of_an_electron,
            self._get_schrodinger_eq,
            self._get_probability_density_of_schrodinger_eq,
            self._get_ground_state_schrodinger_wavefunction,
            self._get_total_probability_of_ground_state_schrodinger_wavefunction,
            self._get_momentum_space_ground_state_schrodinger_wavefunction
            ]

        index = kwargs.get('index', None)
        try:
            if index is None:
                indexes = list(range(len(methods)))
                identifier = f"{self.__class__.__name__}{id(self)}.{inspect.stack()[0][3]}.random_choices"
                index = self.get_no_repeat_random_choice(identifier, indexes)
            
            method = methods[index]
            return method(**kwargs)
        
        except Exception as e:
            sError = f"""Error {self.__class__.__name__}.{inspect.stack()[0][3]}: {e}
            methods = {methods}
            index = {index}
            """
            self._log(sError)
            self._log(traceback.format_exc())
            return None
        
        pass
    
    def _get_electrostatic_potential_energy_of_an_electron(self, **kwargs):
        Q_1, Q_2, epsilon_0, r, e = symbols('Q_1, Q_2, epsilon_0, r, e')
        V = Function('V')
        V_r = V(r)    
        expr = Q_1*Q_2/(4*sp.pi*epsilon_0*r)
        expr_example = -e**2/(4*sp.pi*epsilon_0*r)

        prob_s = f"""
        Write expression for electrostatic potential energy of an electron in a Hydrogen atom.
        """
        prob_s = prob_s.replace("\n", " ").strip()

        soln_s = f"""

        see image::\\images\\physics\\electrostatic_potential_energy_of_an_electron_0001.png  

        $\\displaystyle
        \\begin{{aligned}}

        & \\displaystyle 
        {latex(V_r)}
        = {latex(expr)}
        \\\\ \\\\

        \\end{{aligned}}
        $

        see image::\\images\\physics\\electrostatic_potential_energy_of_an_electron_0002.png  
        In a Hydrogen atom, 
        $\\displaystyle
        \\begin{{aligned}}

        & \\displaystyle 
        {latex(V_r)}
        = {latex(expr_example)}
        \\\\ \\\\

        \\end{{aligned}}
        $
        """
        soln_s = soln_s.strip()
        
        from xv.km.kelements import KProblemTemplate
        problemTemplate = KProblemTemplate()
        soln_s = problemTemplate.transform_image_links(soln_s.strip())
        
        answer_s = f"""
        
        """
        answer_s = answer_s.replace("\n", " ").strip()
        
        result = {
            'prob_s': prob_s.strip(), 
            'soln_s': soln_s.strip(), 
            'answer_s': answer_s.strip(), 
            }
        
        return result

    def _get_graph_electrostatic_potential_energy_of_an_electron(self, **kwargs):
        Q_1, Q_2, epsilon_0, r, e = symbols('Q_1, Q_2, epsilon_0, r, e')
        V = Function('V')
        V_r = V(r)    
        expr = Q_1*Q_2/(4*sp.pi*epsilon_0*r)

        prob_s = f"""
        Graph the expression of electrostatic potential energy of an electron in a Hydrogen atom.
        """
        prob_s = prob_s.replace("\n", " ").strip()

        soln_s = f"""
        
        The graph of $\\displaystyle
        {latex(V_r)}
        = {latex(expr)}
        $

        see image::\\images\\physics\\graph_electrostatic_potential_energy_of_an_electron_0001.png  
        
        """
        soln_s = soln_s.strip()
        
        from xv.km.kelements import KProblemTemplate
        problemTemplate = KProblemTemplate()
        soln_s = problemTemplate.transform_image_links(soln_s.strip())
        
        answer_s = f"""
        
        """
        answer_s = answer_s.replace("\n", " ").strip()
        
        result = {
            'prob_s': prob_s.strip(), 
            'soln_s': soln_s.strip(), 
            'answer_s': answer_s.strip(), 
            }
        
        return result

    def _get_heisenbergs_uncertainty_principal(self, **kwargs):
        delta_x = Symbol('\\Delta x')
        delta_p = Symbol('\\Delta p')
        expr = delta_x*delta_p
        
        prob_s = f"""
        What is Heisenberg's uncertainty principal?
        """
        prob_s = prob_s.replace("\n", " ").strip()

        soln_s = f"""
        It is not possible to have precise knowledge of a particles position and its momentum at the same time

        The better you know where a particle is...the less precisely you know its momentum

        $\\displaystyle
        {latex(expr)}
        \\gtrsim {latex(hbar)}
        $
        ${latex(delta_x)}$ : Uncertainty in particle position
        ${latex(delta_p)}$ Uncertainty in particle momentum
        ${latex(hbar)}=1.05 \\times 10^{{-34}} \\mathrm{{JS}}$: Reduced Planck constant

        
        
        """
        soln_s = soln_s.strip()
        
        from xv.km.kelements import KProblemTemplate
        problemTemplate = KProblemTemplate()
        soln_s = problemTemplate.transform_image_links(soln_s.strip())
        
        answer_s = f"""
        
        """
        answer_s = answer_s.replace("\n", " ").strip()
        
        result = {
            'prob_s': prob_s.strip(), 
            'soln_s': soln_s.strip(), 
            'answer_s': answer_s.strip(), 
            }
        
        return result

    def _get_momentum_of_electron_in_an_atom(self, **kwargs):
        delta_x = Symbol('\\Delta x')
        delta_p = Symbol('\\Delta p')
        a, p = symbols('a, p')
        expr = delta_x*delta_p
        prob_s = f"""
        Find momentum of an electron in an atom with size ${latex(a)}$
        """
        prob_s = prob_s.replace("\n", " ").strip()

        soln_s = f"""
        
        
        $\\displaystyle
        \\begin{{aligned}}
        & \\text{{The electron can be anywhere inside the atom, Uncertainty of electron's position is }} \\\\
        & {latex(delta_x)} \\approx {latex(a)} \\\\
        & \\text{{Applying to this equation }} {latex(expr)} \\gtrsim {latex(hbar)}\\\\
        \\Rightarrow & {latex(delta_p)} \\gtrsim \\frac{{{latex(hbar)}}}{{{latex(a)}}} \\\\
        \\Rightarrow & {latex(delta_p)} \\approx \\frac{{{latex(hbar)}}}{{{latex(a)}}} \\\\
        \\end{{aligned}}
        $
        If the electron's momentum vector has a magnitude $p$ can point in any direction, the component along any direction can have a max range of $+p$ to $-p$
        The uncertainty in any single component of momentum: 
        $\\displaystyle {latex(delta_p)} \\approx {latex(p)} $
        
        Thus, 
        $\\displaystyle {latex(p)} \\approx \\frac{{{latex(hbar)}}}{{{latex(a)}}} $

        """
        soln_s = soln_s.strip()
        
        from xv.km.kelements import KProblemTemplate
        problemTemplate = KProblemTemplate()
        soln_s = problemTemplate.transform_image_links(soln_s.strip())
        
        answer_s = f"""
        
        """
        answer_s = answer_s.replace("\n", " ").strip()
        
        result = {
            'prob_s': prob_s.strip(), 
            'soln_s': soln_s.strip(), 
            'answer_s': answer_s.strip(), 
            }
        
        return result

    def _get_electrostatic_kinetic_energy_of_an_electron(self, **kwargs):
        K, m = symbols('K, m')
        a, p = symbols('a, p')
        expr = p**2/(2*m)
        expr_1 = hbar**2/(2*m*a**2)

        prob_s = f"""
        Write expression for electrostatic kinetic energy of an electron in a Hydrogen atom.

        """
        prob_s = prob_s.replace("\n", " ").strip()

        soln_s = f"""

        Using the momentum of the electron in a Hydrogen atom.
        $\\displaystyle {latex(p)} \\approx \\frac{{{latex(hbar)}}}{{{latex(a)}}} $

        $\\displaystyle
        \\begin{{aligned}}

        & 
        {latex(K)}
        = {latex(expr)}
        \\\\ \\\\

        \\Rightarrow & 
        {latex(K)}
        = {latex(expr_1)} \\qquad \\text {{for electron in a Hydrogen atom}}
        \\\\ \\\\

        \\end{{aligned}}
        $
        
        """
        soln_s = soln_s.strip()
        
        from xv.km.kelements import KProblemTemplate
        problemTemplate = KProblemTemplate()
        soln_s = problemTemplate.transform_image_links(soln_s.strip())
        
        answer_s = f"""
        
        """
        answer_s = answer_s.replace("\n", " ").strip()
        
        result = {
            'prob_s': prob_s.strip(), 
            'soln_s': soln_s.strip(), 
            'answer_s': answer_s.strip(), 
            }
        
        return result

    def _get_total_mechanical_energy_of_an_electron(self, **kwargs):
        E, K, m, V, Q_1, Q_2, epsilon_0, r, e = symbols('E, K, m, V, Q_1, Q_2, epsilon_0, r, e')
        a, p = symbols('a, p')
        expr_potential = -e**2/(4*sp.pi*epsilon_0*r)
        expr_kinetic = hbar**2/(2*m*a**2)

        prob_s = f"""
        Write expression for total mechanical energy of an electron in a Hydrogen atom.

        """
        prob_s = prob_s.replace("\n", " ").strip()

        soln_s = f"""

        The total mechanical energy: 
        $\\displaystyle
        \\begin{{aligned}}

        & 
        {latex(E)}
        = {latex(K)} + {latex(V)}
        \\\\ \\\\

        \\Rightarrow & 
        {latex(E)}
        = {latex(expr_potential + expr_kinetic)}
        \\\\ \\\\

        \\end{{aligned}}
        $
        
        """
        soln_s = soln_s.strip()
        
        from xv.km.kelements import KProblemTemplate
        problemTemplate = KProblemTemplate()
        soln_s = problemTemplate.transform_image_links(soln_s.strip())
        
        answer_s = f"""
        
        """
        answer_s = answer_s.replace("\n", " ").strip()
        
        result = {
            'prob_s': prob_s.strip(), 
            'soln_s': soln_s.strip(), 
            'answer_s': answer_s.strip(), 
            }
        
        return result

    def get_problem_bohr_radius(self, **kwargs):
        E, K, m, V, Q_1, Q_2, epsilon_0, r, e = symbols('E, K, m, V, Q_1, Q_2, epsilon_0, r, e')
        a, a0, p = symbols('a, a_0, p')
        expr_potential = -e**2/(4*sp.pi*epsilon_0*a)
        expr_kinetic = hbar**2/(2*m*a**2)


        d_expr_potential = Derivative(expr_potential, a)
        d_expr_kinetic = Derivative(expr_kinetic, a)

        expr_a = 4*sp.pi*epsilon_0*hbar**2/(m*e**2)

        su_system = self.get_sympy_unit_system()
        meter =  su_system.to_unit('meter')
        a0_val = 5.29e-11*meter

        prob_s = f"""
        Derive Bohr's raduis of an Hydrogen atom 

        """
        prob_s = prob_s.replace("\n", " ").strip()

        soln_s = f"""

        We need find raduis at the lowest energy, which is known as Bohr's radius ${latex(a0)}$
        The total mechanical energy: 
        $\\displaystyle
        \\begin{{aligned}}

        & 
        {latex(E)}
        = {latex(expr_potential + expr_kinetic)}
        \\\\ \\\\

        & 
        {latex(Derivative(E, a))}
        = {latex(d_expr_potential + d_expr_kinetic)}
        \\\\ \\\\

        & 
        {latex(Derivative(E, a))}
        = {latex(d_expr_potential.doit() + d_expr_kinetic.doit())}
        \\\\ \\\\

        & 
        0
        =  {latex(d_expr_potential.doit() + d_expr_kinetic.doit())} \\qquad \\text{{For max/min}} {latex(Derivative(E, a))} = 0
        \\\\ \\\\

        & 
        {latex(-d_expr_kinetic.doit())} 
        = {latex(d_expr_potential.doit())}
        \\\\ \\\\

        & 
        {latex(a)} 
        = {latex(expr_a)} = {latex(a0)} 
        \\\\ \\\\

        & 
        {latex(a0)} 
        = {latex(a0_val)} 
        \\\\ \\\\

        \\end{{aligned}}
        $
        
        """
        soln_s = soln_s.strip()
        
        from xv.km.kelements import KProblemTemplate
        problemTemplate = KProblemTemplate()
        soln_s = problemTemplate.transform_image_links(soln_s.strip())
        
        answer_s = f"""
        
        """
        answer_s = answer_s.replace("\n", " ").strip()
        
        result = {
            'prob_s': prob_s.strip(), 
            'soln_s': soln_s.strip(), 
            'answer_s': answer_s.strip(), 
            }
        
        return result

    def get_problem_lowest_mechanical_energy_of_electron(self, **kwargs):
        E, K, m, V, Q_1, Q_2, epsilon_0, r, e = symbols('E, K, m, V, Q_1, Q_2, epsilon_0, r, e')
        a, a0, p = symbols('a, a_0, p')
        expr_potential = -e**2/(4*sp.pi*epsilon_0*a)
        expr_kinetic = hbar**2/(2*m*a**2)
        expr = expr_potential + expr_kinetic

        su_system = self.get_sympy_unit_system()
        electronvolts =  su_system.to_unit('electronvolts')
        E_val = -13.6*electronvolts

        prob_s = f"""
        Derive expression for lowest mechanical energy of electron of an Hydrogen atom 

        """
        prob_s = prob_s.replace("\n", " ").strip()

        soln_s = f"""

        The lowest energy of an electron will be at Bohr's radius ${latex(a0)}$
        The total mechanical energy: 
        $\\displaystyle
        \\begin{{aligned}}

        & 
        {latex(E)}
        = {latex(expr)}
        \\\\ \\\\

        & 
        {latex(E)}
        = {latex(expr.subs(a, a0))} \\qquad \\text{{Replace }} {latex(a)} = {latex(a0)}
        \\\\ \\\\

        & 
        {latex(E)}
        = {latex(E_val)}
        \\\\ \\\\

        

        \\end{{aligned}}
        $
        
        """
        soln_s = soln_s.strip()
        
        from xv.km.kelements import KProblemTemplate
        problemTemplate = KProblemTemplate()
        soln_s = problemTemplate.transform_image_links(soln_s.strip())
        
        answer_s = f"""
        
        """
        answer_s = answer_s.replace("\n", " ").strip()
        
        result = {
            'prob_s': prob_s.strip(), 
            'soln_s': soln_s.strip(), 
            'answer_s': answer_s.strip(), 
            }
        
        return result

    def _get_schrodinger_eq(self, **kwargs):
        
        prob_s = f"""
        What is Schrodinger Equation?

        """
        prob_s = prob_s.replace("\n", " ").strip()

        soln_s = f"""

        $\\displaystyle -\\frac{{\hbar^2}}{{2 m}} \\nabla^2 \\Psi+V \\Psi=E \\Psi$
        $\\Psi$ is the wavefunction
        $V$ is the potential energy
        $E$ is the total energy
        1) Specify the potential energy function $V$
        2) Solve the Schrödinger Equation
        3) Obtain quantised energy levels and $\\Psi$

        """
        soln_s = soln_s.strip()
        
        from xv.km.kelements import KProblemTemplate
        problemTemplate = KProblemTemplate()
        soln_s = problemTemplate.transform_image_links(soln_s.strip())
        
        answer_s = f"""
        
        """
        answer_s = answer_s.replace("\n", " ").strip()
        
        result = {
            'prob_s': prob_s.strip(), 
            'soln_s': soln_s.strip(), 
            'answer_s': answer_s.strip(), 
            }
        
        return result

    def _get_probability_density_of_schrodinger_eq(self, **kwargs):
        P = symbols('P')
        Psi = symbols('Psi', complex = True)
        Psi_c = conjugate(Psi)
        prob_s = f"""
        Write Probability density of Schrodinger wavefunction

        """
        prob_s = prob_s.replace("\n", " ").strip()

        soln_s = f"""
        The Probability density of Schrodinger wavefunction ${latex(Psi)}$
        $\\displaystyle
        \\begin{{aligned}}

        {latex(P)}
        & = \\left\\|{latex(Psi)}\\right\\|^{2}
        \\\\ \\\\

        & = {latex(Psi_c)} \\cdot {latex(Psi)}
        \\\\ \\\\

        \\end{{aligned}}
        $
        """
        soln_s = soln_s.strip()
        
        from xv.km.kelements import KProblemTemplate
        problemTemplate = KProblemTemplate()
        soln_s = problemTemplate.transform_image_links(soln_s.strip())
        
        answer_s = f"""
        
        """
        answer_s = answer_s.replace("\n", " ").strip()
        
        result = {
            'prob_s': prob_s.strip(), 
            'soln_s': soln_s.strip(), 
            'answer_s': answer_s.strip(), 
            }
        
        return result

    def _get_ground_state_schrodinger_wavefunction(self, **kwargs):
        r, a0 = symbols('r, a_0')
        Psi = Function('Psi', complex = True)(r)
        norm = 1/sp.sqrt(sp.pi*a0**3)
        expr = norm*sp.exp(-r/a0)
        prob_s = f"""
        Define ground state Schrodinger wavefunction

        """
        prob_s = prob_s.replace("\n", " ").strip()

        soln_s = f"""
        The ground state wavefunction ${latex(Psi)}$ inherits this spherical symmetry
         
        $\\displaystyle
        \\begin{{aligned}}

        & 
        {latex(Psi)}
        = {latex(expr)}
        \\\\ \\\\

        \\end{{aligned}}
        $
        ${latex(Psi)}$ only depends on the radial distance between the proton and electron. ${latex(r)}$
        $\\displaystyle {latex(norm)}$ is normalisation factor of ground state wavefunction
        """
        soln_s = soln_s.strip()
        
        from xv.km.kelements import KProblemTemplate
        problemTemplate = KProblemTemplate()
        soln_s = problemTemplate.transform_image_links(soln_s.strip())
        
        answer_s = f"""
        
        """
        answer_s = answer_s.replace("\n", " ").strip()
        
        result = {
            'prob_s': prob_s.strip(), 
            'soln_s': soln_s.strip(), 
            'answer_s': answer_s.strip(), 
            }
        
        return result

    def _get_momentum_space_ground_state_schrodinger_wavefunction(self, **kwargs):
        r, p, a0 = symbols('r, p, a_0')
        Phi = Function('Phi', complex = True)(p)
        Psi = Function('Psi', complex = True)(r)
        psi_norm = 1/sp.sqrt(sp.pi*a0**3)
        psi_expr = psi_norm*sp.exp(-r/a0)

        phi_norm = (2*a0/hbar)**Rational(3, 2)/sp.pi
        phi_expr = phi_norm/(1 + (a0*p/hbar)**2)**2

        prob_s = f"""
        Define momentum-space ground state Schrodinger wavefunction

        """
        prob_s = prob_s.replace("\n", " ").strip()

        soln_s = f"""
        Using the ground state wavefunction $\\displaystyle {latex(Psi)} = {latex(psi_expr)}$ and applying Fourier transformation on ${latex(Psi)}$
        $\\displaystyle
        \\begin{{aligned}}

        & 
        {latex(Phi)}
        = {latex(phi_expr)}
        \\\\ \\\\

        \\end{{aligned}}
        $
        
        """
        soln_s = soln_s.strip()
        
        from xv.km.kelements import KProblemTemplate
        problemTemplate = KProblemTemplate()
        soln_s = problemTemplate.transform_image_links(soln_s.strip())
        
        answer_s = f"""
        
        """
        answer_s = answer_s.replace("\n", " ").strip()
        
        result = {
            'prob_s': prob_s.strip(), 
            'soln_s': soln_s.strip(), 
            'answer_s': answer_s.strip(), 
            }
        
        return result

    def _get_total_probability_of_ground_state_schrodinger_wavefunction(self, **kwargs):
        r, a0, V = symbols('r, a_0, V')
        Psi = Function('Psi', complex = True)(r)
        
        Psi_sqr = Symbol(f'\\left\\|{latex(Psi)}\\right\\|^{2}')
        norm = 1/sp.sqrt(sp.pi*a0**3)
        expr = norm*sp.exp(-r/a0)
        int_expr = Integral(Psi_sqr, V)
        prob_s = f"""
        Find total probability of ground state Schrodinger wavefunction

        """
        prob_s = prob_s.replace("\n", " ").strip()

        soln_s = f"""
        The probability density is probability per unit volume, to get the total probability of the wavefunction, integrate it with total space. 
        $\\displaystyle
        \\begin{{aligned}}

        & 
        \\text{{Total Probability}}
        = {latex(int_expr)} = 1
        \\\\ \\\\

        \\end{{aligned}}
        $
        
        """
        soln_s = soln_s.strip()
        
        from xv.km.kelements import KProblemTemplate
        problemTemplate = KProblemTemplate()
        soln_s = problemTemplate.transform_image_links(soln_s.strip())
        
        answer_s = f"""
        
        """
        answer_s = answer_s.replace("\n", " ").strip()
        
        result = {
            'prob_s': prob_s.strip(), 
            'soln_s': soln_s.strip(), 
            'answer_s': answer_s.strip(), 
            }
        
        return result

    def _get_total_probability_of_momentum_space_ground_state_schrodinger_wavefunction(self, **kwargs):
        r, p, a0, V = symbols('r, p, a_0, V')
        Phi = Function('Phi', complex = True)(r)
        
        Phi_sqr = Symbol(f'\\left\\|{latex(Phi)}\\right\\|^{2}')
        phi_norm = (2*a0/hbar)**Rational(3, 2)/sp.pi
        phi_expr = phi_norm/(1 + (a0*p/hbar)**2)**2

        int_expr = Integral(Phi_sqr, V)
        prob_s = f"""
        Find total probability of momentum-space ground state Schrodinger wavefunction

        """
        prob_s = prob_s.replace("\n", " ").strip()

        soln_s = f"""
        The probability density is probability per unit volume, to get the total probability of the wavefunction, integrate it with total space. 
        $\\displaystyle
        \\begin{{aligned}}

        & 
        \\text{{Total Probability}}
        = {latex(int_expr)} = 1
        \\\\ \\\\

        \\end{{aligned}}
        $
        
        """
        soln_s = soln_s.strip()
        
        from xv.km.kelements import KProblemTemplate
        problemTemplate = KProblemTemplate()
        soln_s = problemTemplate.transform_image_links(soln_s.strip())
        
        answer_s = f"""
        
        """
        answer_s = answer_s.replace("\n", " ").strip()
        
        result = {
            'prob_s': prob_s.strip(), 
            'soln_s': soln_s.strip(), 
            'answer_s': answer_s.strip(), 
            }
        
        return result

    def get_problem_derive_probability_of_ground_state_schrodinger_wavefunction(self, **kwargs):
        r, a0, V, dr = symbols('r, a_0, V, dr')
        P = Function('P')(r)
        Psi = Function('Psi', complex = True)(r)
        
        norm = 1/sp.sqrt(sp.pi*a0**3)
        psi_expr = norm*sp.exp(-r/a0)
        prob_s = f"""
        Derive probability of ground state Schrodinger wavefunction
        """
        prob_s = prob_s.replace("\n", " ").strip()

        soln_s = f"""
        The ground state Schrondinger wavefunction 
        $\\displaystyle
        \\begin{{aligned}}

        & 
        {latex(Psi)}
        = {latex(psi_expr)}
        \\\\ \\\\

        & 
        \\left\\|{latex(Psi)}\\right\\|^{2}
        = {latex(psi_expr**2)}
        \\\\ \\\\

        \\end{{aligned}}
        $
        This function is symmetric about the origin
        $\\left\\|{latex(Psi)}\\right\\|^{2}$ represents the probability per unit volume it does not directly tell us the likelihood of finding the electron at a specific radial distance we must account for how the volume of available space changes with increasing distance from the nucleus
        
        $\\left\\|{latex(Psi)}\\right\\|^{2}$ is spherically symmetric only depends on ${latex(r)}$
        All points at the same radial distance have the same value for $\\left\\|{latex(Psi)}\\right\\|^{2}$

        The Spherical shell volume
        see image::\\images\\physics\\problem_derive_probability_of_ground_state_schrodinger_wavefunction_0001.png  

        $\\displaystyle
        \\begin{{aligned}}

        & 
        \\text{{Shell Volume}}
        = {latex(4*sp.pi*r**2)} \\cdot {latex(dr)}
        \\\\ \\\\
        
        \\end{{aligned}}
        $

        $\\displaystyle
        \\begin{{aligned}}
        &\\text{{Probability of locating the electron within blue shaded region }} 
        \\\\ \\\\
        
        &= \\left\\|{latex(Psi)}\\right\\|^{2} \\cdot \\text{{Shell Volume}}
        \\\\ \\\\

        &= \\left\\|{latex(Psi)}\\right\\|^{2} \\cdot {latex(4*sp.pi*r**2)} {latex(dr)}
        \\\\ \\\\
        
        &= {latex(psi_expr**2)} \\cdot {latex(4*sp.pi*r**2)} {latex(dr)}
        \\\\ \\\\
        
        &= {latex(psi_expr**2*4*sp.pi*r**2)} {latex(dr)}
        \\\\ \\\\
        
        &= {latex(P)} {latex(dr)} \\qquad \\text{{where }} {latex(P)} = {latex(psi_expr**2*4*sp.pi*r**2)} \\text{{and }} {latex(P)} \\text{{ known as radial probability function }}
        \\\\ \\\\
        
        \\end{{aligned}}
        $
        
        """
        soln_s = soln_s.strip()
        
        from xv.km.kelements import KProblemTemplate
        problemTemplate = KProblemTemplate()
        soln_s = problemTemplate.transform_image_links(soln_s.strip())
        
        answer_s = f"""
        
        """
        answer_s = answer_s.replace("\n", " ").strip()
        
        result = {
            'prob_s': prob_s.strip(), 
            'soln_s': soln_s.strip(), 
            'answer_s': answer_s.strip(), 
            }
        
        return result

    def get_problem_derive_probability_of_momentum_space_ground_state_schrodinger_wavefunction(self, **kwargs):
        r, p, a0, V, dp = symbols('r, p, a_0, V, dp')
        P = Function('P')(p)
        Phi = Function('Phi', complex = True)(r)
        
        phi_norm = (2*a0/hbar)**Rational(3, 2)/sp.pi
        phi_expr = phi_norm/(1 + (a0*p/hbar)**2)**2

        prob_s = f"""
        Derive probability of momentum-space ground state Schrodinger wavefunction
        """
        prob_s = prob_s.replace("\n", " ").strip()

        soln_s = f"""
        The momentum-space ground state Schrondinger wavefunction 
        $\\displaystyle
        \\begin{{aligned}}

        & 
        {latex(Phi)}
        = {latex(phi_expr)}
        \\\\ \\\\

        & 
        \\left\\|{latex(Phi)}\\right\\|^{2}
        = {latex(phi_expr**2)}
        \\\\ \\\\

        \\end{{aligned}}
        $
        This function is symmetric about the origin
        $\\left\\|{latex(Phi)}\\right\\|^{2}$ represents the probability per unit volume 

        $\\left\\|{latex(Phi)}\\right\\|^{2}$ is spherically symmetric only depends on ${latex(p)}$

        The Spherical shell volume
        see image::\\images\\physics\\problem_derive_probability_of_momentum_space_ground_state_schrodinger_wavefunction_0001.png  

        $\\displaystyle
        \\begin{{aligned}}

        & 
        \\text{{Shell Volume}}
        = {latex(4*sp.pi*p**2)} \\cdot {latex(dp)}
        \\\\ \\\\
        
        \\end{{aligned}}
        $

        $\\displaystyle
        \\begin{{aligned}}
        & \\text{{Probability of measuring the momentum of the electron within blue shaded region }} 
        \\\\ \\\\
        
        &= \\left\\|{latex(Phi)}\\right\\|^{2} \\cdot \\text{{Shell Volume}}
        \\\\ \\\\

        &= \\left\\|{latex(Phi)}\\right\\|^{2} \\cdot {latex(4*sp.pi*p**2)} {latex(dp)}
        \\\\ \\\\
        
        &= {latex(phi_expr**2)} \\cdot {latex(4*sp.pi*p**2)} {latex(dp)}
        \\\\ \\\\
        
        &= {latex(phi_expr**2*4*sp.pi*p**2)} {latex(dp)}
        \\\\ \\\\
        
        &= {latex(P)} {latex(dp)} \\qquad \\text{{where }} {latex(P)} = {latex(phi_expr**2*4*sp.pi*p**2)} \\text{{ and }} {latex(P)} \\text{{ known as radial momentum probability function }}
        \\\\ \\\\
        
        \\end{{aligned}}
        $
        
        """
        soln_s = soln_s.strip()
        
        from xv.km.kelements import KProblemTemplate
        problemTemplate = KProblemTemplate()
        soln_s = problemTemplate.transform_image_links(soln_s.strip())
        
        answer_s = f"""
        
        """
        answer_s = answer_s.replace("\n", " ").strip()
        
        result = {
            'prob_s': prob_s.strip(), 
            'soln_s': soln_s.strip(), 
            'answer_s': answer_s.strip(), 
            }
        
        return result

    def get_problem_derive_maximum_of_radial_probability_function(self, **kwargs):
        r, a0, V, dr = symbols('r, a_0, V, dr')
        P = Function('P')(r)
        Psi = Function('Psi', complex = True)(r)
        
        norm = 1/sp.sqrt(sp.pi*a0**3)
        psi_expr = norm*sp.exp(-r/a0)
        u = 4*r**2/a0**3
        v = sp.exp(-r/a0)**2
        P_expr = u*v

        prob_s = f"""
        Derive maximum of radial probability function of ground state Schrodinger wavefunction
        """
        prob_s = prob_s.replace("\n", " ").strip()

        soln_s = f"""
        The radial probability function of ground state Schrodinger wavefunction
        $\\displaystyle
        \\begin{{aligned}}

        & 
        {latex(P)}
        = {latex(P_expr)}
        \\\\ \\\\

        & 
        {latex(Derivative(P, r))}
        = {latex(v)} \\cdot {latex(Derivative(u, r))} + {latex(u)} \\cdot {latex(Derivative(v, r))}
        \\\\ \\\\

        & 
        {latex(Derivative(P, r))}
        = {latex(v)} \\cdot {latex(Derivative(u, r).doit())} + {latex(u)} \\cdot {latex(Derivative(v, r).doit())}
        \\\\ \\\\

        & 
        {latex(Derivative(P, r))}
        = {latex(v*Derivative(u, r).doit())} + {latex(u*Derivative(v, r).doit())}
        \\\\ \\\\

        & 
        {latex(Derivative(P, r))}
        = {latex((v*Derivative(u, r).doit()+u*Derivative(v, r).doit()).simplify(force = True))}
        \\\\ \\\\

        & 
        0
        = {latex((v*Derivative(u, r).doit()+u*Derivative(v, r).doit()).simplify(force = True))} \\qquad \\text{{ For max/min }} {latex(Derivative(P, r))} = 0
        \\\\ \\\\

        & 
        {latex(r)}
        = {latex(a0)} \\qquad \\text{{ The peak of the radial probability density curve occurs at exactly the Bohr radius }}
        \\\\ \\\\

                
        \\end{{aligned}}
        $
        
        
        """
        soln_s = soln_s.strip()
        
        from xv.km.kelements import KProblemTemplate
        problemTemplate = KProblemTemplate()
        soln_s = problemTemplate.transform_image_links(soln_s.strip())
        
        answer_s = f"""
        
        """
        answer_s = answer_s.replace("\n", " ").strip()
        
        result = {
            'prob_s': prob_s.strip(), 
            'soln_s': soln_s.strip(), 
            'answer_s': answer_s.strip(), 
            }
        
        return result

    def get_problem_derive_expected_value_of_radial_probability_function(self, **kwargs):
        r_exec = Symbol(r'\left< r \right>')
        r, a0, V, dr = symbols('r, a_0, V, dr', real = True)
        P = Function('P')(r)
        Psi = Function('Psi', complex = True)(r)
        
        Psi_sqr = Symbol(f'\\left\\|{latex(Psi)}\\right\\|^{2}')
        norm = 1/sp.sqrt(sp.pi*a0**3)
        psi_expr = norm*sp.exp(-r/a0)
        inte_expr = Integral(r*P, (r, 0, sp.oo))
        inte_expr_1 = Integral(r*Psi_sqr*4*sp.pi*r**2, (r, 0, sp.oo))
        inte_expr_2 = Integral(r*psi_expr**2*4*sp.pi*r**2, (r, 0, sp.oo))
        inte_expr_2_ans = inte_expr_2.doit().args[0][0] 

        prob_s = f"""
        Derive expected value of radial probability function of ground state Schrodinger wavefunction
        """
        prob_s = prob_s.replace("\n", " ").strip()

        soln_s = f"""
        The expected value of radial probability function of ground state Schrodinger wavefunction
        $\\displaystyle
        \\begin{{aligned}}

        & 
        {latex(r_exec)}
        = {latex(inte_expr)} 
        \\\\ \\\\

        & 
        {latex(r_exec)}
        = {latex(inte_expr_1)} 
        \\\\ \\\\

        & 
        {latex(r_exec)}
        = {latex(inte_expr_2)} \\qquad \\text{{As }} {latex(Psi_sqr)} = {latex(psi_expr**2)}
        \\\\ \\\\

        & 
        {latex(r_exec)}
        = {latex(inte_expr_2_ans)} 
        \\\\ \\\\

                
        \\end{{aligned}}
        $
        
        
        """
        soln_s = soln_s.strip()
        
        from xv.km.kelements import KProblemTemplate
        problemTemplate = KProblemTemplate()
        soln_s = problemTemplate.transform_image_links(soln_s.strip())
        
        answer_s = f"""
        
        """
        answer_s = answer_s.replace("\n", " ").strip()
        
        result = {
            'prob_s': prob_s.strip(), 
            'soln_s': soln_s.strip(), 
            'answer_s': answer_s.strip(), 
            }
        
        return result

    def get_problem_derive_expected_value_of_radial_momentum_probability_function(self, **kwargs):
        p_exec = Symbol(r'\left< p \right>')
        r, p, a0, V, dr = symbols('r, p, a_0, V, dr', real = True)
        P = Function('P')(p)
        Phi = Function('Phi', complex = True)(p)
        
        Phi_sqr = Symbol(f'\\left\\|{latex(Phi)}\\right\\|^{2}')
        phi_norm = (2*a0/hbar)**Rational(3, 2)/sp.pi
        phi_expr = phi_norm/(1 + (a0*p/hbar)**2)**2

        inte_expr = Integral(p*P, (p, 0, sp.oo))
        inte_expr_1 = Integral(p*Phi_sqr*4*sp.pi*p**2, (p, 0, sp.oo))
        inte_expr_2 = Integral(p*phi_expr**2*4*sp.pi*p**2, (p, 0, sp.oo))
        inte_expr_2_ans = inte_expr_2.doit().args[0][0] 

        prob_s = f"""
        Derive expected value of radial momentum probability function of momentum-space ground state Schrodinger wavefunction
        """
        prob_s = prob_s.replace("\n", " ").strip()

        soln_s = f"""
        The expected value of radial momentum probability function of momentum-space ground state Schrodinger wavefunction
        $\\displaystyle
        \\begin{{aligned}}

        & 
        {latex(p_exec)}
        = {latex(inte_expr)} 
        \\\\ \\\\

        & 
        {latex(p_exec)}
        = {latex(inte_expr_1)} 
        \\\\ \\\\

        & 
        {latex(p_exec)}
        = {latex(inte_expr_2)} \\qquad \\text{{As }} {latex(Phi_sqr)} = {latex(phi_expr**2)}
        \\\\ \\\\

        & 
        {latex(p_exec)}
        = {latex(inte_expr_2_ans)} 
        \\\\ \\\\

                
        \\end{{aligned}}
        $
        
        
        """
        soln_s = soln_s.strip()
        
        from xv.km.kelements import KProblemTemplate
        problemTemplate = KProblemTemplate()
        soln_s = problemTemplate.transform_image_links(soln_s.strip())
        
        answer_s = f"""
        
        """
        answer_s = answer_s.replace("\n", " ").strip()
        
        result = {
            'prob_s': prob_s.strip(), 
            'soln_s': soln_s.strip(), 
            'answer_s': answer_s.strip(), 
            }
        
        return result

    def get_problem_derive_expected_value_of_radial_momentum_probability_function_squared(self, **kwargs):
        p_sqr_exec = Symbol(r'\left< p^{2} \right>')
        r, p, a0, V, dr = symbols('r, p, a_0, V, dr', real = True)
        P = Function('P')(p)
        Phi = Function('Phi', complex = True)(p)
        
        Phi_sqr = Symbol(f'\\left\\|{latex(Phi)}\\right\\|^{2}')
        phi_norm = (2*a0/hbar)**Rational(3, 2)/sp.pi
        phi_expr = phi_norm/(1 + (a0*p/hbar)**2)**2

        inte_expr = Integral(p**2*P, (p, 0, sp.oo))
        inte_expr_1 = Integral(p**2*Phi_sqr*4*sp.pi*p**2, (p, 0, sp.oo))
        inte_expr_2 = Integral(p**2*phi_expr**2*4*sp.pi*p**2, (p, 0, sp.oo))
        inte_expr_2_ans = inte_expr_2.doit().args[0][0] 

        prob_s = f"""
        Derive expected value of radial momentum probability function squared ${latex(p_sqr_exec)}$ of momentum-space ground state Schrodinger wavefunction
        """
        prob_s = prob_s.replace("\n", " ").strip()

        soln_s = f"""
        The expected value of radial momentum probability function squared of momentum-space ground state Schrodinger wavefunction
        $\\displaystyle
        \\begin{{aligned}}

        & 
        {latex(p_sqr_exec)}
        = {latex(inte_expr)} 
        \\\\ \\\\

        & 
        {latex(p_sqr_exec)}
        = {latex(inte_expr_1)} 
        \\\\ \\\\

        & 
        {latex(p_sqr_exec)}
        = {latex(inte_expr_2)} \\qquad \\text{{As }} {latex(Phi_sqr)} = {latex(phi_expr**2)}
        \\\\ \\\\

        & 
        {latex(p_sqr_exec)}
        = {latex(inte_expr_2_ans)} 
        \\\\ \\\\

                
        \\end{{aligned}}
        $
        
        
        """
        soln_s = soln_s.strip()
        
        from xv.km.kelements import KProblemTemplate
        problemTemplate = KProblemTemplate()
        soln_s = problemTemplate.transform_image_links(soln_s.strip())
        
        answer_s = f"""
        
        """
        answer_s = answer_s.replace("\n", " ").strip()
        
        result = {
            'prob_s': prob_s.strip(), 
            'soln_s': soln_s.strip(), 
            'answer_s': answer_s.strip(), 
            }
        
        return result

    def get_problem_expected_value_potential_energy_of_ground_state_schrodinger_wavefunction(self, **kwargs):
        V_exec = Symbol(r'\left< V \right>')
        r, a0, V, dr = symbols('r, a_0, V, dr', real = True)
        epsilon_0, e = symbols('epsilon_0, e')
        V = Function('V')(r)
        P = Function('P')(r)
        Psi = Function('Psi', complex = True)(r)
        
        Psi_sqr = Symbol(f'\\left\\|{latex(Psi)}\\right\\|^{2}')
        norm = 1/sp.sqrt(sp.pi*a0**3)
        psi_expr = norm*sp.exp(-r/a0)
        V_expr = -e**2/(4*sp.pi*epsilon_0*r)
        inte_expr = Integral(V*P, (r, 0, sp.oo))
        inte_expr_1 = Integral(V*Psi_sqr*4*sp.pi*r**2, (r, 0, sp.oo))
        inte_expr_2 = Integral(V*psi_expr**2*4*sp.pi*r**2, (r, 0, sp.oo))
        inte_expr_3 = Integral(V_expr*psi_expr**2*4*sp.pi*r**2, (r, 0, sp.oo))
        inte_expr_3_ans = inte_expr_3.doit().args[0][0] 

        prob_s = f"""
        Derive expected value potential energy of ground state Schrodinger wavefunction
        """
        prob_s = prob_s.replace("\n", " ").strip()

        soln_s = f"""
        The expected value potential energy of ground state Schrodinger wavefunction
        $\\displaystyle
        \\begin{{aligned}}

        & 
        {latex(V_exec)}
        = {latex(inte_expr)} 
        \\\\ \\\\

        & 
        {latex(V_exec)}
        = {latex(inte_expr_1)} 
        \\\\ \\\\

        & 
        {latex(V_exec)}
        = {latex(inte_expr_2)} \\qquad \\text{{As }} {latex(Psi_sqr)} = {latex(psi_expr**2)}
        \\\\ \\\\

        & 
        {latex(V_exec)}
        = {latex(inte_expr_3)} \\qquad \\text{{As }} {latex(V)} = {latex(V_expr)}
        \\\\ \\\\

        & 
        {latex(V_exec)}
        = {latex(inte_expr_3_ans)} 
        \\\\ \\\\

                
        \\end{{aligned}}
        $
        The expected value value of potential energy is when the distance proton and electron is at Bohr's radius ${latex(V_exec)}$
        
        
        """
        soln_s = soln_s.strip()
        
        from xv.km.kelements import KProblemTemplate
        problemTemplate = KProblemTemplate()
        soln_s = problemTemplate.transform_image_links(soln_s.strip())
        
        answer_s = f"""
        
        """
        answer_s = answer_s.replace("\n", " ").strip()
        
        result = {
            'prob_s': prob_s.strip(), 
            'soln_s': soln_s.strip(), 
            'answer_s': answer_s.strip(), 
            }
        
        return result

    def get_problem_expected_value_kinetic_energy_of_ground_state_schrodinger_wavefunction(self, **kwargs):
        K_exec = Symbol(r'\left< K \right>')
        p_sqr_exec = Symbol(r'\left< p^{2} \right>')
        K, r, p, m, a0, V, dr = symbols('K, r, p, m, a_0, V, dr', real = True)
        epsilon_0, e = symbols('epsilon_0, e')
        
        K_expr = p**2/(2*m)
        K_exec_expr = p_sqr_exec/(2*m)
        p_sqr_exec_expr = hbar**2/a0**2
        prob_s = f"""
        Derive expected value of kinetic energy of ground state Schrodinger wavefunction
        """
        prob_s = prob_s.replace("\n", " ").strip()

        soln_s = f"""
        The expected value of kinetic energy of ground state Schrodinger wavefunction
        $\\displaystyle
        \\begin{{aligned}}

        & 
        \\text{{Classical mechanics: }} \\quad
        {latex(K)} = {latex(K_expr)} 
        \\\\ \\\\

        & 
        \\text{{Quantum mechanics: }} \\quad
        {latex(K_exec)} = {latex(K_exec_expr)} 
        \\\\ \\\\

        & 
        {latex(K_exec)} = {latex(K_exec_expr.subs(p_sqr_exec, p_sqr_exec_expr))} \\qquad \\text{{As }} {latex(p_sqr_exec)} = {latex(p_sqr_exec_expr)}
        \\\\ \\\\

        
                
        \\end{{aligned}}
        $
        
        
        """
        soln_s = soln_s.strip()
        
        from xv.km.kelements import KProblemTemplate
        problemTemplate = KProblemTemplate()
        soln_s = problemTemplate.transform_image_links(soln_s.strip())
        
        answer_s = f"""
        
        """
        answer_s = answer_s.replace("\n", " ").strip()
        
        result = {
            'prob_s': prob_s.strip(), 
            'soln_s': soln_s.strip(), 
            'answer_s': answer_s.strip(), 
            }
        
        return result

    def get_problem_expected_value_total_energy_of_ground_state_schrodinger_wavefunction(self, **kwargs):
        E_exec = Symbol(r'\left< E \right>')
        K_exec = Symbol(r'\left< K \right>')
        V_exec = Symbol(r'\left< V \right>')
        K, r, p, m, a0, V, dr = symbols('K, r, p, m, a_0, V, dr', real = True)
        epsilon_0, e = symbols('epsilon_0, e')
        
        K_exec_expr = hbar**2/(2*m*a0**2)
        V_exec_expr = -e**2/(4*sp.pi*epsilon_0*a0)

        prob_s = f"""
        Derive expected value of total energy of ground state Schrodinger wavefunction
        """
        prob_s = prob_s.replace("\n", " ").strip()

        soln_s = f"""
        The expected value of total energy of ground state Schrodinger wavefunction
        $\\displaystyle
        \\begin{{aligned}}

        & 
        {latex(E_exec)}
         = {latex(K_exec)} + {latex(V_exec)} 
        \\\\ \\\\

        & 
        {latex(E_exec)}
         = {latex(K_exec_expr + V_exec_expr)} 
        \\\\ \\\\

        & 
        \\text{{We derived this same equation while calculating for the lowest total energy}}
        \\\\ \\\\

        
        
                
        \\end{{aligned}}
        $
        
        
        """
        soln_s = soln_s.strip()
        
        from xv.km.kelements import KProblemTemplate
        problemTemplate = KProblemTemplate()
        soln_s = problemTemplate.transform_image_links(soln_s.strip())
        
        answer_s = f"""
        
        """
        answer_s = answer_s.replace("\n", " ").strip()
        
        result = {
            'prob_s': prob_s.strip(), 
            'soln_s': soln_s.strip(), 
            'answer_s': answer_s.strip(), 
            }
        
        return result

    pass