JupyterBook Demonstration

Hide code cell content
import mmf_setup;mmf_setup.nbinit()
import logging;logging.getLogger('matplotlib').setLevel(logging.CRITICAL)
%matplotlib inline
import numpy as np, matplotlib.pyplot as plt

This cell adds /home/docs/checkouts/readthedocs.org/user_builds/physics-581-the-standard-model/checkouts/latest/src to your path, and contains some definitions for equations and some CSS for styling the notebook. If things look a bit strange, please try the following:

  • Choose "Trust Notebook" from the "File" menu.
  • Re-execute this cell.
  • Reload the notebook.

JupyterBook Demonstration#

This document demonstrates some of the features provided by the documentation. It can serve as a starting point for your own documentation.

This documentation is formatted using an extension to Markdown called MyST which allows full interoperability with Sphinx, the system used to build the documentation. Here we demonstrate some features. For more details, please see:

Math#

You can use LaTeX math with [MathJaX][].

Example#

Here we implement a simple example of a pendulum of mass \(m\) hanging down distance \(r\) from a pivot point in a gravitational field \(g>0\) with coordinate \(\theta\) so that the mass is at \((x, z) = (r\sin\theta, -r\cos\theta)\) and \(\theta=0\) is the downward equilibrium position:

\[\begin{gather*} L(\theta, \dot{\theta}, t) = \frac{m}{2}r^2\dot{\theta}^2 + mgr\cos\theta\\ p_{\theta} = \pdiff{L}{\dot{\theta}} = mr^2 \dot{\theta}, \qquad \dot{\theta}(\theta, p_{\theta}, t) = \frac{p_{\theta}}{mr^2},\\ H(\theta, p_{\theta}) = p_{\theta}\dot{\theta} - L = \frac{p_{\theta}^2}{2mr^2} - mgr\cos\theta,\\ \vect{y} = \begin{pmatrix} \theta\\ p_{\theta} \end{pmatrix},\qquad \dot{\vect{y}} = \begin{pmatrix} 0 & 1\\ -1 & 0 \end{pmatrix} \cdot \begin{pmatrix} \pdiff{H}{\theta}\\ \pdiff{H}{p_{\theta}} \end{pmatrix} = \begin{pmatrix} p_{\theta}/mr^2\\ -mgr\sin\theta \end{pmatrix}. \end{gather*}\]

Note

On [CoCalc][], only a subset of the math will render in the live Markdown editor. This uses KaTeX, which is much faster than [MathJaX][], but more limited. The final documentation will use [MathJaX][], and loads the macros defined in Docs/_static/math_defs.tex.

SI Units#

In LaTeX, I like to use the siunitx package. This is not currently supported in MathJax (they could not get funding), but can be mocked up with some defines. The idea is that we use the macros for HTML, but substitute the appropriate \usepackage{siunitx} in the LaTeX files. (This second step is currently not implemented)

Jupyter Notebooks#

This document is actually a Jupyter notebook, synchronized with Jupytext. The top of the document contains information about the kernel that should be used etc. These are parsed using MyST-NB and the output of cells will be displayed. For example, here is a plot demonstrating Liouville’s Theorem.

Hide code cell source
plt.rcParams['figure.dpi'] = 300
from scipy.integrate import solve_ivp

alpha = 0.0
m = r = g = 1.0
w = g/r
T = 2*np.pi / w   # Period of small oscillations.


def f(t, y):
    # We will simultaneously evolve N points.
    N = len(y)//2
    theta, p_theta = np.reshape(y, (2, N))
    dy = np.array([p_theta/m/r**2*np.exp(-alpha*t),
                   -m*g*r*np.sin(theta)*np.exp(alpha*t)])
    return dy.ravel()


# Start with a circle of points in phase space centered here
def plot_set(y, dy=0.1, T=T, phase_space=True, N=10, Nt=5, c='C0', 
             Ncirc=1000, max_step=0.01, _alpha=0.7, 
             fig=None, ax=None):
    """Plot the phase flow of a circle centered about y0.
    
    Arguments
    ---------
    y : (theta0, ptheta_0)
        Center of initial circle.
    dy : float
        Radius of initial circle.
    T : float
        Time to evolve to.
    phase_space : bool
        If `True`, plot in phase space $(q, p)$, otherwise plot in
        the "physical" phase space $(q, P)$ where $P = pe^{-\alpha t}$.
    N : int
        Number of points to show on circle and along path.
    Nt : int
        Number of images along trajectory to show.
    c : color
        Color.
    alpha : float
        Transparency of regions.
    max_step : float
        Maximum spacing for times dt.
    Ncirc : int
        Minimum number of points in circle.
    """
    global alpha
    skip = int(np.ceil(Ncirc // N))
    N_ = N * skip
    th = np.linspace(0, 2*np.pi, N_ + 1)[:-1]
    z = dy * np.exp(1j*th) + np.asarray(y).view(dtype=complex)
    y0 = np.ravel([z.real, z.imag])

    skip_t = int(np.ceil(T / max_step / Nt))
    Nt_ = Nt * skip_t + 1
    t_eval = np.linspace(0, T, Nt_)
    res = solve_ivp(f, t_span=(0, T), y0=y0, t_eval=t_eval)
    assert Nt_ == len(res.t)
    thetas, p_thetas = res.y.reshape(2, N_, Nt_)
    ylabel = r"$p_{\theta}$"
    if phase_space:
        ylabel = r"$P_{\theta}=p_{\theta}e^{-\alpha t}$"
        p_thetas *= np.exp(-alpha * res.t)
    
    if ax is None:
        fig, ax = plt.subplots()

    ax.plot(thetas[::skip].T, p_thetas[::skip].T, "-k", lw=0.1)
    for n in range(Nt+1):
        tind = n*skip_t
        ax.plot(thetas[::skip, tind], p_thetas[::skip, tind], '.', ms=0.5, c=c)
        ax.fill(thetas[:, tind], p_thetas[:, tind], c=c, alpha=_alpha)
    ax.set(xlabel=r"$\theta$", ylabel=ylabel, aspect=1)
    return fig, ax


fig, ax = plt.subplots(figsize=(10,5))


for n, y in enumerate(np.linspace(0.25, 1.75, 6)):
    plot_set(y=(y, y), c=f"C{n}", ax=ax)
_images/2510d868f5d1d317620efdf4341c8fa97d189920cf126bc9d689cb37ef031f2f.png

Details

We start a code-block with:

```{code-cell}
:tags: [hide-input, full-width]
...
```

This indicates that it is a code cell, to be run with python 3, and has some tags to make the cell and output full width, but hiding the code. (There is a link to click to show the code.)

If you need more control, you can glue the output to a variable, then load it as a proper figure, insert it in a table, etc.

Manim#

We provide experimental support for Manim Community – the community edition of the animation software used by the 3Blue1Brown project. Here we demonstrate an animation of the Jacobi elliptic functions.

Hide code cell content
import manim.utils.ipython_magic
Hide code cell source
%%manim -v WARNING --progress_bar None -qm JacobiEllipse
from scipy.special import ellipj, ellipkinc
from manim import *

config.media_width = "100%"
config.media_embed = True

my_tex_template = TexTemplate()
with open("_static/math_defs.tex") as f:
    my_tex_template.add_to_preamble(f.read())

def get_scd(phi, k):
    m = k**2
    u = ellipkinc(phi, m)
    s, c, d, phi_ = ellipj(u, m)
    return s, c, d

def get_xyr(phi, k):
    s, c, d = get_scd(phi, k)
    r = 1/d
    x, y = r*c, r*s
    return x, y, r

class JacobiEllipse(Scene):
    def construct(self):
        config["tex_template"] = my_tex_template
        config["media_width"] = "100%"
        class colors:
            ellipse = YELLOW
            x = BLUE
            y = GREEN
            r = RED
            
        phi = ValueTracker(0.01)
        k = 0.8
        axes = Axes(x_range=[0, 2*np.pi, 1], 
                    y_range=[-2, 2, 1],
                    x_length=6, 
                    y_length=4,
                    axis_config=dict(include_tip=False),
                    x_axis_config=dict(numbers_to_exclude=[0]),
                   ).add_coordinates()
        
        
        plane = PolarPlane(radius_max=2).add_coordinates()

        x = lambda phi: get_xyr(phi, k)[0]
        y = lambda phi: get_xyr(phi, k)[1]
        r = lambda phi: get_xyr(phi, k)[2]
        
        g_ellipse = plane.plot_polar_graph(r, [0, 2*np.pi], color=colors.ellipse)
        
        points_colors = [(x, colors.x), (y, colors.y), (r, colors.r)]
        x_graph, y_graph, r_graph = [axes.plot(_x, color=_c) for _x, _c in points_colors]
        graphs = VGroup(x_graph, y_graph, r_graph)
        
        dot = always_redraw(lambda:
            Dot(plane.polar_to_point(r(phi.get_value()), phi.get_value()), 
                fill_color=colors.ellipse, 
                fill_opacity=0.8))

        @always_redraw
        def lines():
            c2p = plane.coords_to_point
            phi_ = phi.get_value()
            x_, y_ = x(phi_), y(phi_)
            return VGroup(
                Line(c2p(0, 0), c2p(x_, 0), color=colors.x),
                Line(c2p(0, y_), c2p(x_, y_), color=colors.x),
                Line(c2p(0, 0), c2p(0, y_), color=colors.y),
                Line(c2p(x_, 0), c2p(x_, y_), color=colors.y),
                Line(axes.c2p(phi_, y_), c2p(x_, y_), color=colors.y),
                Line(c2p(0, 0), c2p(x_, y_), color=colors.r),
            ).set_opacity(0.8)

        dots = always_redraw(lambda:
            VGroup(*(Dot(axes.c2p(phi.get_value(), _x(phi.get_value())), fill_color=_c, fill_opacity=1)
                     for _x, _c in points_colors)))

        a_group = VGroup(axes, dots, graphs)
        p_group = VGroup(plane, g_ellipse, dot, lines)
        a_group.shift(RIGHT*2)
        p_group.shift(LEFT*4)
    
        labels = VGroup(
            axes.get_graph_label(
                x_graph, label=r"x=\cn(u, k)", color=colors.x,
                x_val=2.5, direction=DOWN).shift(0.2*LEFT),
            axes.get_graph_label(
                y_graph, label=r"y=\sn(u, k)", color=colors.y,
                x_val=4.5, direction=DR),
            axes.get_graph_label(
                r_graph, label=r"r=1/\dn(u, k)", color=colors.r,
                x_val=2, direction=UR),
        )
        #labels.next_to(a_group, RIGHT)
        self.add(p_group, a_group, labels)
        self.play(phi.animate.set_value(2*np.pi), run_time=5, rate_func=linear)
Manim Community v0.18.1

[10/27/24 03:00:02] ERROR    LaTeX compilation error: LaTeX Error: Command \span already    tex_file_writing.py:314
                             defined.                                                                              
                                                                                                                   
[E 03:00:02 manim] LaTeX compilation error: LaTeX Error: Command \span already defined.

              tex_file_writing.py:314
                    ERROR    Context of error:                                              tex_file_writing.py:348
                             \DeclareMathOperator{\sgn}{sgn}                                                       
                             \DeclareMathOperator{\order}{O}                                                       
                             -> \DeclareMathOperator{\diag}{diag}                                                  
                             \DeclareMathOperator{\span}{span}                                                     
                             \newcommand{\mylabel}[1]{\label{#1}\tag{#1}}                                          
                             \newcommand{\degree}{\circ}                                                           
                                                                                                                   
[E 03:00:02 manim] Context of error: 
\DeclareMathOperator{\sgn}{sgn}
\DeclareMathOperator{\order}{O}
-> \DeclareMathOperator{\diag}{diag}
\DeclareMathOperator{\span}{span}
\newcommand{\mylabel}[1]{\label{#1}\tag{#1}}
\newcommand{\degree}{\circ}

              tex_file_writing.py:348
---------------------------------------------------------------------------
ValueError                                Traceback (most recent call last)
Cell In[4], line 1
----> 1 get_ipython().run_cell_magic('manim', '-v WARNING --progress_bar None -qm JacobiEllipse', 'from scipy.special import ellipj, ellipkinc\nfrom manim import *\n\nconfig.media_width = "100%"\nconfig.media_embed = True\n\nmy_tex_template = TexTemplate()\nwith open("_static/math_defs.tex") as f:\n    my_tex_template.add_to_preamble(f.read())\n\ndef get_scd(phi, k):\n    m = k**2\n    u = ellipkinc(phi, m)\n    s, c, d, phi_ = ellipj(u, m)\n    return s, c, d\n\ndef get_xyr(phi, k):\n    s, c, d = get_scd(phi, k)\n    r = 1/d\n    x, y = r*c, r*s\n    return x, y, r\n\nclass JacobiEllipse(Scene):\n    def construct(self):\n        config["tex_template"] = my_tex_template\n        config["media_width"] = "100%"\n        class colors:\n            ellipse = YELLOW\n            x = BLUE\n            y = GREEN\n            r = RED\n            \n        phi = ValueTracker(0.01)\n        k = 0.8\n        axes = Axes(x_range=[0, 2*np.pi, 1], \n                    y_range=[-2, 2, 1],\n                    x_length=6, \n                    y_length=4,\n                    axis_config=dict(include_tip=False),\n                    x_axis_config=dict(numbers_to_exclude=[0]),\n                   ).add_coordinates()\n        \n        \n        plane = PolarPlane(radius_max=2).add_coordinates()\n\n        x = lambda phi: get_xyr(phi, k)[0]\n        y = lambda phi: get_xyr(phi, k)[1]\n        r = lambda phi: get_xyr(phi, k)[2]\n        \n        g_ellipse = plane.plot_polar_graph(r, [0, 2*np.pi], color=colors.ellipse)\n        \n        points_colors = [(x, colors.x), (y, colors.y), (r, colors.r)]\n        x_graph, y_graph, r_graph = [axes.plot(_x, color=_c) for _x, _c in points_colors]\n        graphs = VGroup(x_graph, y_graph, r_graph)\n        \n        dot = always_redraw(lambda:\n            Dot(plane.polar_to_point(r(phi.get_value()), phi.get_value()), \n                fill_color=colors.ellipse, \n                fill_opacity=0.8))\n\n        @always_redraw\n        def lines():\n            c2p = plane.coords_to_point\n            phi_ = phi.get_value()\n            x_, y_ = x(phi_), y(phi_)\n            return VGroup(\n                Line(c2p(0, 0), c2p(x_, 0), color=colors.x),\n                Line(c2p(0, y_), c2p(x_, y_), color=colors.x),\n                Line(c2p(0, 0), c2p(0, y_), color=colors.y),\n                Line(c2p(x_, 0), c2p(x_, y_), color=colors.y),\n                Line(axes.c2p(phi_, y_), c2p(x_, y_), color=colors.y),\n                Line(c2p(0, 0), c2p(x_, y_), color=colors.r),\n            ).set_opacity(0.8)\n\n        dots = always_redraw(lambda:\n            VGroup(*(Dot(axes.c2p(phi.get_value(), _x(phi.get_value())), fill_color=_c, fill_opacity=1)\n                     for _x, _c in points_colors)))\n\n        a_group = VGroup(axes, dots, graphs)\n        p_group = VGroup(plane, g_ellipse, dot, lines)\n        a_group.shift(RIGHT*2)\n        p_group.shift(LEFT*4)\n    \n        labels = VGroup(\n            axes.get_graph_label(\n                x_graph, label=r"x=\\cn(u, k)", color=colors.x,\n                x_val=2.5, direction=DOWN).shift(0.2*LEFT),\n            axes.get_graph_label(\n                y_graph, label=r"y=\\sn(u, k)", color=colors.y,\n                x_val=4.5, direction=DR),\n            axes.get_graph_label(\n                r_graph, label=r"r=1/\\dn(u, k)", color=colors.r,\n                x_val=2, direction=UR),\n        )\n        #labels.next_to(a_group, RIGHT)\n        self.add(p_group, a_group, labels)\n        self.play(phi.animate.set_value(2*np.pi), run_time=5, rate_func=linear)\n')

File ~/checkouts/readthedocs.org/user_builds/physics-581-the-standard-model/conda/latest/lib/python3.11/site-packages/IPython/core/interactiveshell.py:2541, in InteractiveShell.run_cell_magic(self, magic_name, line, cell)
   2539 with self.builtin_trap:
   2540     args = (magic_arg_s, cell)
-> 2541     result = fn(*args, **kwargs)
   2543 # The code below prevents the output from being displayed
   2544 # when using magics with decorator @output_can_be_silenced
   2545 # when the last Python token in the expression is a ';'.
   2546 if getattr(fn, magic.MAGIC_OUTPUT_CAN_BE_SILENCED, False):

File ~/checkouts/readthedocs.org/user_builds/physics-581-the-standard-model/conda/latest/lib/python3.11/site-packages/manim/utils/ipython_magic.py:143, in ManimMagic.manim(self, line, cell, local_ns)
    141     SceneClass = local_ns[config["scene_names"][0]]
    142     scene = SceneClass(renderer=renderer)
--> 143     scene.render()
    144 finally:
    145     # Shader cache becomes invalid as the context is destroyed
    146     shader_program_cache.clear()

File ~/checkouts/readthedocs.org/user_builds/physics-581-the-standard-model/conda/latest/lib/python3.11/site-packages/manim/scene/scene.py:229, in Scene.render(self, preview)
    227 self.setup()
    228 try:
--> 229     self.construct()
    230 except EndSceneEarlyException:
    231     pass

File <string>:41, in construct(self)

File ~/checkouts/readthedocs.org/user_builds/physics-581-the-standard-model/conda/latest/lib/python3.11/site-packages/manim/mobject/graphing/coordinate_systems.py:428, in CoordinateSystem.add_coordinates(self, *axes_numbers, **kwargs)
    426     labels = axis.labels
    427 else:
--> 428     axis.add_numbers(values, **kwargs)
    429     labels = axis.numbers
    430 self.coordinate_labels.add(labels)

File ~/checkouts/readthedocs.org/user_builds/physics-581-the-standard-model/conda/latest/lib/python3.11/site-packages/manim/mobject/graphing/number_line.py:533, in NumberLine.add_numbers(self, x_values, excluding, font_size, label_constructor, **kwargs)
    530     if x in excluding:
    531         continue
    532     numbers.add(
--> 533         self.get_number_mobject(
    534             x,
    535             font_size=font_size,
    536             label_constructor=label_constructor,
    537             **kwargs,
    538         )
    539     )
    540 self.add(numbers)
    541 self.numbers = numbers

File ~/checkouts/readthedocs.org/user_builds/physics-581-the-standard-model/conda/latest/lib/python3.11/site-packages/manim/mobject/graphing/number_line.py:471, in NumberLine.get_number_mobject(self, x, direction, buff, font_size, label_constructor, **number_config)
    468 if label_constructor is None:
    469     label_constructor = self.label_constructor
--> 471 num_mob = DecimalNumber(
    472     x, font_size=font_size, mob_class=label_constructor, **number_config
    473 )
    475 num_mob.next_to(self.number_to_point(x), direction=direction, buff=buff)
    476 if x < 0 and self.label_direction[0] == 0:
    477     # Align without the minus sign

File ~/checkouts/readthedocs.org/user_builds/physics-581-the-standard-model/conda/latest/lib/python3.11/site-packages/manim/mobject/text/numbers.py:135, in DecimalNumber.__init__(self, number, num_decimal_places, mob_class, include_sign, group_with_commas, digit_buff_per_font_unit, show_ellipsis, unit, unit_buff_per_font_unit, include_background_rectangle, edge_to_fix, font_size, stroke_width, fill_opacity, **kwargs)
    117 self.initial_config = kwargs.copy()
    118 self.initial_config.update(
    119     {
    120         "num_decimal_places": num_decimal_places,
   (...)
    132     },
    133 )
--> 135 self._set_submobjects_from_number(number)
    136 self.init_colors()

File ~/checkouts/readthedocs.org/user_builds/physics-581-the-standard-model/conda/latest/lib/python3.11/site-packages/manim/mobject/text/numbers.py:160, in DecimalNumber._set_submobjects_from_number(self, number)
    157 self.submobjects = []
    159 num_string = self._get_num_string(number)
--> 160 self.add(*(map(self._string_to_mob, num_string)))
    162 # Add non-numerical bits
    163 if self.show_ellipsis:

File ~/checkouts/readthedocs.org/user_builds/physics-581-the-standard-model/conda/latest/lib/python3.11/site-packages/manim/mobject/text/numbers.py:225, in DecimalNumber._string_to_mob(self, string, mob_class, **kwargs)
    222     mob_class = self.mob_class
    224 if string not in string_to_mob_map:
--> 225     string_to_mob_map[string] = mob_class(string, **kwargs)
    226 mob = string_to_mob_map[string].copy()
    227 mob.font_size = self._font_size

File ~/checkouts/readthedocs.org/user_builds/physics-581-the-standard-model/conda/latest/lib/python3.11/site-packages/manim/mobject/text/tex_mobject.py:293, in MathTex.__init__(self, arg_separator, substrings_to_isolate, tex_to_color_map, tex_environment, *tex_strings, **kwargs)
    280     if self.brace_notation_split_occurred:
    281         logger.error(
    282             dedent(
    283                 """\
   (...)
    291             ),
    292         )
--> 293     raise compilation_error
    294 self.set_color_by_tex_to_color_map(self.tex_to_color_map)
    296 if self.organize_left_to_right:

File ~/checkouts/readthedocs.org/user_builds/physics-581-the-standard-model/conda/latest/lib/python3.11/site-packages/manim/mobject/text/tex_mobject.py:272, in MathTex.__init__(self, arg_separator, substrings_to_isolate, tex_to_color_map, tex_environment, *tex_strings, **kwargs)
    270 self.tex_strings = self._break_up_tex_strings(tex_strings)
    271 try:
--> 272     super().__init__(
    273         self.arg_separator.join(self.tex_strings),
    274         tex_environment=self.tex_environment,
    275         tex_template=self.tex_template,
    276         **kwargs,
    277     )
    278     self._break_up_by_substrings()
    279 except ValueError as compilation_error:

File ~/checkouts/readthedocs.org/user_builds/physics-581-the-standard-model/conda/latest/lib/python3.11/site-packages/manim/mobject/text/tex_mobject.py:81, in SingleStringMathTex.__init__(self, tex_string, stroke_width, should_center, height, organize_left_to_right, tex_environment, tex_template, font_size, **kwargs)
     79 assert isinstance(tex_string, str)
     80 self.tex_string = tex_string
---> 81 file_name = tex_to_svg_file(
     82     self._get_modified_expression(tex_string),
     83     environment=self.tex_environment,
     84     tex_template=self.tex_template,
     85 )
     86 super().__init__(
     87     file_name=file_name,
     88     should_center=should_center,
   (...)
     95     **kwargs,
     96 )
     97 self.init_colors()

File ~/checkouts/readthedocs.org/user_builds/physics-581-the-standard-model/conda/latest/lib/python3.11/site-packages/manim/utils/tex_file_writing.py:63, in tex_to_svg_file(expression, environment, tex_template)
     60 if svg_file.exists():
     61     return svg_file
---> 63 dvi_file = compile_tex(
     64     tex_file,
     65     tex_template.tex_compiler,
     66     tex_template.output_format,
     67 )
     68 svg_file = convert_to_svg(dvi_file, tex_template.output_format)
     69 if not config["no_latex_cleanup"]:

File ~/checkouts/readthedocs.org/user_builds/physics-581-the-standard-model/conda/latest/lib/python3.11/site-packages/manim/utils/tex_file_writing.py:213, in compile_tex(tex_file, tex_compiler, output_format)
    211         log_file = tex_file.with_suffix(".log")
    212         print_all_tex_errors(log_file, tex_compiler, tex_file)
--> 213         raise ValueError(
    214             f"{tex_compiler} error converting to"
    215             f" {output_format[1:]}. See log output above or"
    216             f" the log file: {log_file}",
    217         )
    218 return result

ValueError: latex error converting to dvi. See log output above or the log file: media/Tex/6056ff848b3908bc.log

Warning

We need a Manim >= 0.15.0 to get this to work, resolving issue 2441 by embeding the video in the output.