Spaces:
Sleeping
Sleeping
File size: 11,285 Bytes
de8e0be |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 |
from manim import *
import numpy as np
# Render with:
# manim -ql storyboard.py StoryboardScene -o video.mp4
class StoryboardScene(MovingCameraScene):
def construct(self):
# Palette
DIAMOND_COLOR = GREY_B
N_COLOR = TEAL_C
VACANCY_COLOR = RED_B
LEVEL_COLOR = BLUE_E
SINGLET_COLOR = YELLOW_B
EMIT_COLOR = RED_B
# Helper: build a simple 2D diamond-like lattice with an NV- center (N + vacancy)
def create_lattice(rows=6, cols=8, spacing=0.6, r_dot=0.06):
dots = VGroup()
positions = {}
for i in range(rows):
for j in range(cols):
offset = (spacing / 2) if (i % 2 == 1) else 0
x = (j * spacing) + offset - (cols * spacing) / 2
y = (i * spacing * 0.85) - (rows * spacing * 0.85) / 2
d = Dot(point=np.array([x, y, 0]), radius=r_dot, color=DIAMOND_COLOR)
dots.add(d)
positions[(i, j)] = d.get_center()
# Choose central site for N and adjacent site for vacancy
ic = rows // 2
jc = cols // 2
nv_pos = positions[(ic, jc)]
# Pick vacancy to the right if possible
if (ic, jc + 1) in positions:
vac_pos = positions[(ic, jc + 1)]
else:
vac_pos = positions[(ic, jc - 1)]
# Replace central carbon with Nitrogen
N = Dot(point=nv_pos, radius=r_dot * 1.5, color=N_COLOR)
N_label = Text("N", font_size=22, color=N_COLOR).next_to(N, UP)
# Remove the dot at vacancy position
vac_circle = Circle(radius=r_dot * 1.6, color=VACANCY_COLOR, stroke_width=4).move_to(vac_pos)
V_label = MathTex(r"V^{-}", color=VACANCY_COLOR).scale(0.6).next_to(vac_circle, DOWN)
# Remove the vacancy carbon dot from dots (find by position)
to_remove = None
for d in dots:
if np.allclose(d.get_center(), vac_pos):
to_remove = d
break
if to_remove:
dots.remove(to_remove)
lattice_group = VGroup(dots, N, vac_circle, N_label, V_label)
# Find three nearest neighbors to N (excluding vacancy) for later bonds
carbon_positions = [d.get_center() for d in dots]
dists = sorted([(np.linalg.norm(p - nv_pos), p) for p in carbon_positions], key=lambda x: x[0])
neighbors = []
for _, p in dists:
if len(neighbors) >= 4:
break
if np.allclose(p, vac_pos):
continue
neighbors.append(p)
neighbors = neighbors[:3]
bonds = VGroup(*[Line(N.get_center(), p, color=DIAMOND_COLOR, stroke_width=3) for p in neighbors])
return lattice_group, dots, N, vac_circle, N_label, V_label, bonds, nv_pos
# Helper: Energy level diagram for NV-
def create_energy_diagram():
# Axis
E_axis = Arrow(start=LEFT * 5 + DOWN * 2.5, end=LEFT * 5 + UP * 2.5, buff=0, stroke_width=4, color=GREY_B)
E_label = Text("Energy", font_size=24, color=GREY_B).next_to(E_axis, RIGHT, buff=0.2)
# Ground triplet (3A2): ms=0 lower, ms=±1 slightly higher (two lines)
xL, xR = -3.0, 3.0
y_g0 = -1.6
y_gpm1a = -1.2
y_gpm1b = -1.05
g0 = Line([xL, y_g0, 0], [xR, y_g0, 0], color=LEVEL_COLOR, stroke_width=5)
gpm1a = Line([xL, y_gpm1a, 0], [xR, y_gpm1a, 0], color=LEVEL_COLOR, stroke_width=5)
gpm1b = Line([xL, y_gpm1b, 0], [xR, y_gpm1b, 0], color=LEVEL_COLOR, stroke_width=5)
g_label = MathTex(r"{}^3A_2", color=LEVEL_COLOR).scale(0.8).next_to(g0, LEFT, buff=0.5)
g_m0 = MathTex(r"m_s=0", color=LEVEL_COLOR).scale(0.6).next_to(g0, RIGHT, buff=0.3)
g_mpm1 = MathTex(r"m_s=\pm 1", color=LEVEL_COLOR).scale(0.6).next_to(gpm1b, RIGHT, buff=0.3)
# Excited triplet (3E): similar splitting
y_e0 = 1.6
y_epm1a = 2.0
y_epm1b = 2.15
e0 = Line([xL, y_e0, 0], [xR, y_e0, 0], color=LEVEL_COLOR, stroke_width=5)
epm1a = Line([xL, y_epm1a, 0], [xR, y_epm1a, 0], color=LEVEL_COLOR, stroke_width=5)
epm1b = Line([xL, y_epm1b, 0], [xR, y_epm1b, 0], color=LEVEL_COLOR, stroke_width=5)
e_label = MathTex(r"{}^3E", color=LEVEL_COLOR).scale(0.8).next_to(e0, LEFT, buff=0.5)
e_m0 = MathTex(r"m_s=0", color=LEVEL_COLOR).scale(0.6).next_to(e0, RIGHT, buff=0.3)
e_mpm1 = MathTex(r"m_s=\pm 1", color=LEVEL_COLOR).scale(0.6).next_to(epm1b, RIGHT, buff=0.3)
# Singlet shelving state (simplified as one line)
y_s = 0.3
s = Line([xL * 0.5, y_s, 0], [xR * 0.5, y_s, 0], color=SINGLET_COLOR, stroke_width=5)
s_label = Text("Singlet", font_size=24, color=SINGLET_COLOR).next_to(s, RIGHT, buff=0.3)
# Transitions
radiative = Arrow(start=[0.0, y_e0 - 0.05, 0], end=[0.0, y_g0 + 0.05, 0],
buff=0, color=EMIT_COLOR, stroke_width=5)
y_epm_avg = 0.5 * (y_epm1a + y_epm1b)
nr1 = Arrow(start=[1.5, y_epm_avg - 0.05, 0], end=[1.0, y_s + 0.05, 0],
buff=0, color=SINGLET_COLOR, stroke_width=5)
nr2 = Arrow(start=[1.0, y_s - 0.05, 0], end=[0.5, y_g0 + 0.05, 0],
buff=0, color=SINGLET_COLOR, stroke_width=5)
levels = VGroup(g0, gpm1a, gpm1b, e0, epm1a, epm1b, s)
labels = VGroup(g_label, e_label, g_m0, g_mpm1, e_m0, e_mpm1, s_label)
arrows = VGroup(radiative, nr1, nr2)
axis = VGroup(E_axis, E_label)
diagram = VGroup(axis, levels, labels, arrows)
return diagram, axis, levels, labels, arrows
# -------------------------
# Scene 1: Introduction to NV- Center (8 s)
# -------------------------
lattice_group, dots, N_dot, vac_circle, N_label, V_label, bonds, nv_pos = create_lattice()
title = Text("NV- center in diamond", font_size=30, color=GREY_B).to_edge(UP)
highlight = Circle(radius=0.6, color=N_COLOR, stroke_width=3).move_to((N_dot.get_center() + vac_circle.get_center()) / 2)
self.play(FadeIn(lattice_group), FadeIn(title), run_time=1.0)
self.play(Create(highlight), run_time=0.6)
self.play(highlight.animate.scale(1.2), run_time=0.7)
self.wait(5.7) # total 8.0 s
# -------------------------
# Scene 2: Structure of the NV- Center (8 s)
# -------------------------
self.play(self.camera.frame.animate.scale(0.6).move_to(N_dot.get_center()), run_time=1.2)
if len(bonds) > 0:
self.play(Create(bonds), run_time=0.8)
self.play(Indicate(N_label, color=N_COLOR), Indicate(V_label, color=VACANCY_COLOR), run_time=0.6)
self.wait(5.4) # total 8.0 s
# -------------------------
# Scene 3: Energy Level Diagram (12 s)
# -------------------------
# Clear lattice and show energy levels
self.play(FadeOut(VGroup(lattice_group, title, highlight, bonds)), run_time=0.6)
energy_diagram, axis, levels, labels, arrows = create_energy_diagram()
energy_diagram.shift(DOWN * 0.3) # small center tweak
self.play(FadeIn(axis), FadeIn(levels), run_time=2.0)
self.play(FadeIn(labels), run_time=0.8)
self.play(GrowArrow(arrows[0]), run_time=0.8) # radiative
self.play(GrowArrow(arrows[1]), run_time=0.8) # nonradiative e->singlet
self.play(GrowArrow(arrows[2]), run_time=0.8) # singlet->ground
self.wait(6.2) # total 12.0 s
# -------------------------
# Scene 4: Emission in the Red Region (7 s)
# -------------------------
self.play(FadeOut(energy_diagram), run_time=0.6)
# Minimal NV- motif for emission
nv_group = VGroup(
Dot(ORIGIN, radius=0.09, color=N_COLOR), # place at origin for this beat
Circle(radius=0.12, color=VACANCY_COLOR, stroke_width=4).shift(RIGHT * 0.25)
)
nv_group.move_to(ORIGIN)
red_label = Text("Red emission ~637 nm", font_size=28, color=EMIT_COLOR).to_edge(UP).shift(DOWN * 0.2)
self.play(FadeIn(nv_group), run_time=0.6)
# Emission pulses from NV center (from N position)
emitter_point = nv_group[0].get_center() # Dot at origin
pulse1 = Circle(radius=0.15, color=EMIT_COLOR, stroke_width=4).move_to(emitter_point)
pulse2 = Circle(radius=0.15, color=EMIT_COLOR, stroke_width=4).move_to(emitter_point)
self.play(pulse1.animate.scale(3.0), run_time=1.0)
self.play(FadeOut(pulse1), run_time=0.3)
self.play(pulse2.animate.scale(3.0), run_time=1.0)
self.play(FadeOut(pulse2), run_time=0.3)
self.play(FadeIn(red_label), run_time=0.6)
self.wait(2.6) # total 7.0 s
# -------------------------
# Scene 5: Real-World Applications (10 s)
# -------------------------
self.play(nv_group.animate.shift(LEFT * 3.0), run_time=0.6)
# Magnet icon (quantum sensing): simple U-shape from rectangles
bar_left = Rectangle(height=1.6, width=0.25, color=BLUE_E, fill_opacity=1).move_to(RIGHT * 2.2 + UP * 0.6)
bar_right = Rectangle(height=1.6, width=0.25, color=BLUE_E, fill_opacity=1).move_to(RIGHT * 3.0 + UP * 0.6)
bridge = Rectangle(height=0.25, width=0.8, color=BLUE_E, fill_opacity=1).move_to(RIGHT * 2.6 + UP * 1.4)
magnet = VGroup(bar_left, bar_right, bridge)
# Chip icon (quantum tech/computing)
chip_body = Rectangle(height=1.2, width=1.8, color=GREY_B, fill_opacity=0).move_to(RIGHT * 2.6 + DOWN * 1.0)
trace1 = Line(chip_body.get_left() + RIGHT * 0.15 + UP * 0.3, chip_body.get_right() - RIGHT * 0.15 + UP * 0.3,
color=TEAL_C, stroke_width=3)
trace2 = Line(chip_body.get_left() + RIGHT * 0.15, chip_body.get_right() - RIGHT * 0.15,
color=TEAL_C, stroke_width=3)
trace3 = Line(chip_body.get_left() + RIGHT * 0.15 + DOWN * 0.3, chip_body.get_right() - RIGHT * 0.15 + DOWN * 0.3,
color=TEAL_C, stroke_width=3)
chip = VGroup(chip_body, trace1, trace2, trace3)
# Arrows from NV to icons
arrow_to_magnet = Arrow(start=nv_group.get_right(), end=bar_left.get_left() + LEFT * 0.1, color=GREY_B, buff=0.1)
arrow_to_chip = Arrow(start=nv_group.get_right(), end=chip_body.get_left() + LEFT * 0.1, color=GREY_B, buff=0.1)
label_magnet = Text("Quantum sensing", font_size=26, color=BLUE_E).next_to(magnet, UP, buff=0.25)
label_chip = Text("Quantum tech & computing", font_size=26, color=TEAL_C).next_to(chip, DOWN, buff=0.25)
self.play(FadeIn(magnet), run_time=0.6)
self.play(FadeIn(chip), run_time=0.6)
self.play(GrowArrow(arrow_to_magnet), GrowArrow(arrow_to_chip), run_time=0.8)
self.play(FadeIn(label_magnet), FadeIn(label_chip), run_time=0.6)
self.wait(6.8) # total 10.0 s (Grand total 45.0 s) |