{ "cells": [ { "cell_type": "markdown", "id": "0", "metadata": {}, "source": [ "# Extending the SM by a gauged U(1)$_X$: a Z′ tutorial in `feynlag`\n", "\n", "A **Z′ boson** is what you get when the SM gauge group grows an extra abelian\n", "factor U(1)$_X$. The SM fermion content (plus three right-handed neutrinos)\n", "admits exactly two independent family-universal anomaly-free abelian charges —\n", "hypercharge $Y$ and $B\\!-\\!L$ — so the most general consistent assignment is a\n", "linear combination\n", "\n", "$$X = a\\,Y + b\\,(B\\!-\\!L)$$\n", "\n", "with $a$, $b$ free parameters. This notebook keeps them (and the singlet's\n", "charge $q_S$) **symbolic all the way through the pipeline** — declaration,\n", "invariance checking, gauge currents, vertex extraction.\n", "\n", "Because the Higgs carries hypercharge, it carries $X$-charge $a/2$, and the\n", "$Z$ and $Z'$ **mix at tree level** — the physically interesting (and\n", "technically instructive) regime. What's new relative to\n", "[`SM_Feynman_Rules_Tutorial.ipynb`](SM_Feynman_Rules_Tutorial.ipynb) (read\n", "that first — `ExternalParameter`, `Dmu`, `Scalar`, `Model` are not\n", "re-explained here):\n", "\n", "- **two independent U(1) factors** in one model (groups are distinct objects;\n", " a field's `reps` dict just gains a second U(1) entry),\n", "- an **SM-singlet complex scalar** $S$ whose VEV $v_X$ breaks U(1)$_X$, with a\n", " Higgs-portal coupling $\\lambda_{HS}|H|^2|S|^2$ mixing $h$ and $s$,\n", "- **three right-handed neutrinos** (required for the $B\\!-\\!L$ anomaly to\n", " cancel) with a Dirac Yukawa via $\\tilde H$,\n", "- a **3×3 neutral gauge mass matrix** diagonalized by *two chained 2×2\n", " rotations* — Weinberg first, then $Z$–$Z'$.\n", "\n", "The headline formulas we'll derive and verify:\n", "\n", "$$\\tan 2\\theta' = \\frac{2\\,a\\,g_Z g_X v^2}{a^2 g_X^2 v^2 + 4 g_X^2 q_S^2 v_X^2 - g_Z^2 v^2},\n", "\\qquad\n", "\\tan 2\\theta_s = \\frac{\\lambda_{HS}\\, v\\, v_X}{\\lambda v^2 - \\lambda_S v_X^2},\n", "\\qquad\n", "m_Z^2\\, m_{Z'}^2 = \\tfrac{1}{4} g_Z^2 g_X^2 q_S^2 v^2 v_X^2 .$$\n", "\n", "$a \\to 0$ is the pure gauged-$B\\!-\\!L$ limit: no mixing, $m_{Z'} = g_X q_S v_X$." ] }, { "cell_type": "code", "execution_count": null, "id": "1", "metadata": {}, "outputs": [], "source": [ "import sympy as sp\n", "\n", "from feynlag import (\n", " Bilinear, DiracGamma, Dmu, ExternalParameter, InternalParameter,\n", " Lagrangian, Model, Rotation, SU2, SU3, Scalar, U1, WeylFermion,\n", " conjugate_pair, dag, diagonalize_orthogonal_2x2, diracPL, diracPR,\n", " extract_fermion_vertices, fermion_feynman_rule, fermion_gauge_current,\n", " fermion_mass_matrix, rotation_2x2,\n", ")" ] }, { "cell_type": "markdown", "id": "2", "metadata": {}, "source": [ "## 1. Symmetries and parameters\n", "\n", "Two things distinguish this from the plain-SM setup:\n", "\n", "- **A second U(1).** `U1X = U1(\"U1X\", coupling=gX)` — groups compare by\n", " identity, so `U1Y` and `U1X` coexist as separate keys in every field's\n", " `reps` dict, each carrying its own coupling and its own gauge boson.\n", "- **Symbolic charges.** `aX`, `bX`, `qS` are ordinary `ExternalParameter`s\n", " (dimensionless, so the default `unit_dim=0` is right). A U(1) \"generator\"\n", " in `feynlag` is just the 1×1 matrix `[[charge]]`, and nothing downstream\n", " assumes the charge is a number.\n", "\n", "The charge map `qcharge(Y, B−L) = a·Y + b·(B−L)` below is the entire flavor\n", "structure of the extension — every field's $X$-charge follows from its known\n", "$Y$ and $B\\!-\\!L$." ] }, { "cell_type": "code", "execution_count": null, "id": "3", "metadata": {}, "outputs": [], "source": [ "gw = ExternalParameter(\"g_w\", 0.6535, positive=True)\n", "g1 = ExternalParameter(\"g_1\", 0.3580, positive=True)\n", "gX = ExternalParameter(\"g_X\", 0.5, positive=True)\n", "gs = ExternalParameter(\"g_s\", 1.22, positive=True)\n", "SU2L, U1Y = SU2(\"SU2L\", coupling=gw), U1(\"U1Y\", coupling=g1)\n", "U1X = U1(\"U1X\", coupling=gX)\n", "SU3c = SU3(\"SU3c\", coupling=gs)\n", "\n", "aX = ExternalParameter(\"a_X\", 0.4) # X = aX*Y + bX*(B-L), kept symbolic\n", "bX = ExternalParameter(\"b_X\", -0.7)\n", "qS = ExternalParameter(\"q_S\", 1.0) # singlet X-charge, also symbolic\n", "\n", "v = ExternalParameter(\"v\", 246.0, positive=True, unit_dim=1)\n", "vX = ExternalParameter(\"v_X\", 2000.0, positive=True, unit_dim=1)\n", "lam = ExternalParameter(\"lambda\", 0.129)\n", "lamS = ExternalParameter(\"lambda_S\", 0.2)\n", "lamHS = ExternalParameter(\"lambda_HS\", 0.05)\n", "mu2 = InternalParameter(\"mu_2\", unit_dim=2)\n", "muS2 = InternalParameter(\"mu_{S2}\", unit_dim=2)\n", "\n", "\n", "def qcharge(Y, BmL):\n", " \"\"\"The anomaly-free charge map X = a*Y + b*(B-L).\"\"\"\n", " return aX.s * Y + bX.s * BmL" ] }, { "cell_type": "markdown", "id": "4", "metadata": {}, "source": [ "## 2. Fields\n", "\n", "Two scalars now:\n", "\n", "- the SM Higgs doublet `H`, which picks up $X$-charge $a/2$ (it has $Y=1/2$,\n", " $B\\!-\\!L=0$) — **this is what turns on the tree-level $Z$–$Z'$ mixing**;\n", "- an SM-singlet complex scalar `S` with the free charge $q_S$ and VEV $v_X$.\n", "\n", "`S` is a one-component complex scalar; `expand_vev` on a complex component\n", "automatically creates the real fluctuation symbols and registers\n", "$S \\to (v_X + S0_r + i\\,S0_i)/\\sqrt{2}$ — same $1/\\sqrt2$ convention as the\n", "Higgs. ($S0_r$ is the CP-even \"dark Higgs\" $s$; $S0_i$ will turn out to be\n", "the Goldstone the $Z'$ eats.)" ] }, { "cell_type": "code", "execution_count": null, "id": "5", "metadata": {}, "outputs": [ { "data": { "text/plain": [ "{S0: sqrt(2)*(I*S0_i + S0_r + v_X)/2}" ] }, "execution_count": null, "metadata": {}, "output_type": "execute_result" } ], "source": [ "H = Scalar(\"H\", reps={SU2L: 2, U1Y: sp.Rational(1, 2),\n", " U1X: qcharge(sp.Rational(1, 2), 0)},\n", " component_names=[\"Gp\", \"H0\"])\n", "H.expand_vev({H.components[1]: v})\n", "\n", "S = Scalar(\"S\", reps={U1X: qS.s}, component_names=[\"S0\"])\n", "S.expand_vev({S.components[0]: vX})\n", "\n", "S.shift_map # the automatic complex-VEV expansion" ] }, { "cell_type": "markdown", "id": "6", "metadata": {}, "source": [ "The fermions are the SM's three generations plus **three right-handed\n", "neutrinos** (`nuR`, with $Y=0$, $B\\!-\\!L=-1$, hence $X$-charge $-b$ — they\n", "carry *only* the new gauge charge). Without them the $B\\!-\\!L$ part of $X$\n", "would be anomalous; with them, *any* $(a, b)$ is anomaly-free. Every SM\n", "field's `reps` dict simply gains its `U1X: qcharge(...)` entry — nothing\n", "else about the declarations changes." ] }, { "cell_type": "code", "execution_count": null, "id": "7", "metadata": {}, "outputs": [], "source": [ "Ll = WeylFermion(\"Ll\", reps={SU2L: 2, U1Y: -sp.Rational(1, 2),\n", " U1X: qcharge(-sp.Rational(1, 2), -1)},\n", " chirality=\"L\", nflavors=3, component_names=[\"nuL\", \"eL\"])\n", "eR = WeylFermion(\"eR\", reps={U1Y: -1, U1X: qcharge(-1, -1)},\n", " chirality=\"R\", nflavors=3, component_names=[\"eR\"])\n", "nuR = WeylFermion(\"nuR\", reps={U1X: qcharge(0, -1)},\n", " chirality=\"R\", nflavors=3, component_names=[\"nuR\"])\n", "\n", "QL = WeylFermion(\"QL\", reps={SU2L: 2, U1Y: sp.Rational(1, 6), SU3c: 3,\n", " U1X: qcharge(sp.Rational(1, 6), sp.Rational(1, 3))},\n", " chirality=\"L\", nflavors=3,\n", " component_names=[\"uL_1\", \"uL_2\", \"uL_3\",\n", " \"dL_1\", \"dL_2\", \"dL_3\"])\n", "uR = WeylFermion(\"uR\", reps={U1Y: sp.Rational(2, 3), SU3c: 3,\n", " U1X: qcharge(sp.Rational(2, 3), sp.Rational(1, 3))},\n", " chirality=\"R\", nflavors=3,\n", " component_names=[\"uR_1\", \"uR_2\", \"uR_3\"])\n", "dR = WeylFermion(\"dR\", reps={U1Y: -sp.Rational(1, 3), SU3c: 3,\n", " U1X: qcharge(-sp.Rational(1, 3), sp.Rational(1, 3))},\n", " chirality=\"R\", nflavors=3,\n", " component_names=[\"dR_1\", \"dR_2\", \"dR_3\"])\n", "\n", "W, B = SU2L.bosons(\"W\"), U1Y.bosons(\"B\")\n", "X = U1X.bosons(\"X\")\n", "G = SU3c.bosons(\"G\")\n", "\n", "Gp, H0 = H.components\n", "nuL, eL = Ll.components\n", "nuLbar, eLbar = Ll.bar_components\n", "eRc, eRbar = eR.components[0], eR.bar_components[0]\n", "nuRc, nuRbar = nuR.components[0], nuR.bar_components[0]" ] }, { "cell_type": "markdown", "id": "8", "metadata": {}, "source": [ "## 3. The Lagrangian\n", "\n", "### Scalar potential with a Higgs portal\n", "\n", "$$V = -\\mu^2|H|^2 + \\lambda|H|^4 - \\mu_S^2|S|^2 + \\lambda_S|S|^4\n", " + \\lambda_{HS}|H|^2|S|^2$$\n", "\n", "The portal term is the only renormalizable, gauge-invariant coupling between\n", "the two scalar sectors — it will mix $h$ and $s$ after both VEVs turn on.\n", "The kinetic terms are built with `Dmu`, which gauges **every** group in a\n", "field's `reps` automatically: `Dmu(H)` includes $W$, $B$ *and* $X$;\n", "`Dmu(S)` includes only $X$." ] }, { "cell_type": "code", "execution_count": null, "id": "9", "metadata": {}, "outputs": [], "source": [ "HdH = (dag(H) * H.mat)[0]\n", "SdS = (dag(S) * S.mat)[0]\n", "V = (-mu2.s * HdH + lam.s * HdH**2\n", " - muS2.s * SdS + lamS.s * SdS**2\n", " + lamHS.s * HdH * SdS)\n", "DH, DS = Dmu(H), Dmu(S)" ] }, { "cell_type": "markdown", "id": "10", "metadata": {}, "source": [ "### Yukawas: charged leptons, and neutrinos via $\\tilde H$\n", "\n", "The charged-lepton Yukawa is the standard\n", "$-Y_e^{ij}\\,\\bar L_i H\\, e_{Rj} + \\text{h.c.}$ The **neutrino Dirac Yukawa**\n", "$-Y_\\nu^{ij}\\,\\bar L_i \\tilde H\\, \\nu_{Rj} + \\text{h.c.}$ needs the conjugate\n", "doublet $\\tilde H = i\\sigma_2 H^* = (H^{0*}, -G^{+*})$, written inline\n", "component-by-component — the same pattern the up-quark Yukawa uses in the SM\n", "tutorial. Check the $X$-charge balance:\n", "$-q_L + q_{\\tilde H} + q_{\\nu_R} = (a/2 + b) + (-a/2) + (-b) = 0$ — invariant\n", "by construction, for *any* $a, b$. (`check_invariance()` will verify this\n", "symbolically in §4.)" ] }, { "cell_type": "code", "execution_count": null, "id": "11", "metadata": {}, "outputs": [], "source": [ "i, j = sp.symbols(\"i j\", integer=True)\n", "Ye, Yv = sp.IndexedBase(\"Ye\"), sp.IndexedBase(\"Yv\")\n", "\n", "LYuk = -(Ye[i, j] * Gp * Bilinear(nuLbar[i], diracPR, eRc[j])\n", " + Ye[i, j] * H0 * Bilinear(eLbar[i], diracPR, eRc[j]))\n", "LYuk += -(sp.conjugate(Ye[i, j]) * sp.conjugate(Gp)\n", " * Bilinear(eRbar[j], diracPL, nuL[i])\n", " + sp.conjugate(Ye[i, j]) * sp.conjugate(H0)\n", " * Bilinear(eRbar[j], diracPL, eL[i]))\n", "\n", "LYukN = -(Yv[i, j] * sp.conjugate(H0)\n", " * Bilinear(nuLbar[i], diracPR, nuRc[j])\n", " + Yv[i, j] * (-sp.conjugate(Gp))\n", " * Bilinear(eLbar[i], diracPR, nuRc[j]))\n", "LYukN += -(sp.conjugate(Yv[i, j]) * H0\n", " * Bilinear(nuRbar[j], diracPL, nuL[i])\n", " + sp.conjugate(Yv[i, j]) * (-Gp)\n", " * Bilinear(nuRbar[j], diracPL, eL[i]))" ] }, { "cell_type": "markdown", "id": "12", "metadata": {}, "source": [ "The quark Yukawas are verbatim from the SM tutorial (color summed in\n", "Python, flavor-generic, no CKM), and the gauge currents come from\n", "`fermion_gauge_current` — which, like `Dmu`, is generic over the `reps`\n", "dict, so every fermion's $X$-boson current (with its *symbolic* charge)\n", "appears automatically." ] }, { "cell_type": "code", "execution_count": null, "id": "13", "metadata": {}, "outputs": [], "source": [ "Yu, Yd = sp.IndexedBase(\"Yu\"), sp.IndexedBase(\"Yd\")\n", "qL_u, qL_d = QL.components[:3], QL.components[3:]\n", "qLbar_u, qLbar_d = QL.bar_components[:3], QL.bar_components[3:]\n", "uRc, uRbar = uR.components, uR.bar_components\n", "dRc, dRbar = dR.components, dR.bar_components\n", "\n", "\n", "def qcolor(bar_list, gamma, field_list, idx1, idx2):\n", " return sum(Bilinear(bar_list[c][idx1], gamma, field_list[c][idx2])\n", " for c in range(3))\n", "\n", "\n", "LYuk_d = -(Yd[i, j] * Gp * qcolor(qLbar_u, diracPR, dRc, i, j)\n", " + Yd[i, j] * H0 * qcolor(qLbar_d, diracPR, dRc, i, j))\n", "LYuk_d += -(sp.conjugate(Yd[i, j]) * sp.conjugate(Gp)\n", " * qcolor(dRbar, diracPL, qL_u, j, i)\n", " + sp.conjugate(Yd[i, j]) * sp.conjugate(H0)\n", " * qcolor(dRbar, diracPL, qL_d, j, i))\n", "\n", "LYuk_u = -(Yu[i, j] * sp.conjugate(H0) * qcolor(qLbar_u, diracPR, uRc, i, j)\n", " + Yu[i, j] * (-sp.conjugate(Gp)) * qcolor(qLbar_d, diracPR, uRc, i, j))\n", "LYuk_u += -(sp.conjugate(Yu[i, j]) * H0 * qcolor(uRbar, diracPL, qL_u, j, i)\n", " + sp.conjugate(Yu[i, j]) * (-Gp) * qcolor(uRbar, diracPL, qL_d, j, i))\n", "\n", "current = (fermion_gauge_current(Ll, i) + fermion_gauge_current(eR, i)\n", " + fermion_gauge_current(nuR, i))\n", "current_quarks = (fermion_gauge_current(QL, i)\n", " + fermion_gauge_current(uR, i)\n", " + fermion_gauge_current(dR, i))\n", "\n", "L = Lagrangian()\n", "L.add((dag(DH) * DH)[0] + (dag(DS) * DS)[0], sector=\"kinetic\")\n", "L.add(-V, sector=\"potential\")\n", "L.add(LYuk + LYukN, sector=\"yukawa\")\n", "L.add(current, sector=\"yukawa\")\n", "L.add(LYuk_d + LYuk_u, sector=\"yukawa\")\n", "L.add(current_quarks, sector=\"yukawa\");" ] }, { "cell_type": "markdown", "id": "14", "metadata": {}, "source": [ "## 4. Assemble the Model and check invariance\n", "\n", "This is the first model in `feynlag` with **symbol-valued gauge charges**\n", "going through the invariance checker: the infinitesimal-transformation\n", "residuals now carry factors of $a$, $b$, $q_S$, and invariance means they\n", "cancel *identically in those symbols* — a strictly stronger statement than\n", "for numeric charges (it certifies the whole $(a,b)$ family at once, and\n", "would catch any term that's only invariant for a special charge choice)." ] }, { "cell_type": "code", "execution_count": null, "id": "15", "metadata": {}, "outputs": [ { "data": { "text/plain": [ "InvarianceReport(33 checks, OK)" ] }, "execution_count": null, "metadata": {}, "output_type": "execute_result" } ], "source": [ "model = Model(\"SM-U1X\", gauge_groups=[SU2L, U1Y, U1X, SU3c],\n", " fields=[H, S, Ll, eR, nuR, QL, uR, dR, W, B, X, G],\n", " parameters=[gw, g1, gX, gs, aX, bX, qS, v, vX,\n", " lam, lamS, lamHS, mu2, muS2], lagrangian=L)\n", "\n", "report = model.check_invariance()\n", "report.raise_on_failure()\n", "report" ] }, { "cell_type": "markdown", "id": "16", "metadata": {}, "source": [ "## 5. EWSB: two tadpoles\n", "\n", "Two VEVs, two tadpole conditions, two internal parameters ($\\mu^2$,\n", "$\\mu_S^2$) to solve for. The portal coupling links them: each $\\mu^2$ picks\n", "up a $\\lambda_{HS}$-term proportional to the *other* sector's VEV." ] }, { "cell_type": "code", "execution_count": null, "id": "17", "metadata": {}, "outputs": [ { "data": { "text/plain": [ "{mu_2: (2*lambda*v**2 + lambda_HS*v_X**2)/2,\n", " mu_{S2}: (lambda_HS*v**2 + 2*lambda_S*v_X**2)/2}" ] }, "execution_count": null, "metadata": {}, "output_type": "execute_result" } ], "source": [ "model.solve_tadpoles([mu2, muS2])" ] }, { "cell_type": "markdown", "id": "18", "metadata": {}, "source": [ "## 6. The scalar sector: portal mixing and Goldstone counting\n", "\n", "The CP-even block over $(h_0, s_0) = (H0_r, S0_r)$:" ] }, { "cell_type": "code", "execution_count": null, "id": "19", "metadata": {}, "outputs": [ { "data": { "text/latex": [ "$\\displaystyle \\left[\\begin{matrix}2 \\lambda v^{2} & \\lambda_{HS} v v_{X}\\\\\\lambda_{HS} v v_{X} & 2 \\lambda_{S} v_{X}^{2}\\end{matrix}\\right]$" ], "text/plain": [ "Matrix([\n", "[ 2*lambda*v**2, lambda_HS*v*v_X],\n", "[lambda_HS*v*v_X, 2*lambda_S*v_X**2]])" ] }, "execution_count": null, "metadata": {}, "output_type": "execute_result" } ], "source": [ "h0, G0 = sp.Symbol(\"H0_r\", real=True), sp.Symbol(\"H0_i\", real=True)\n", "s0, aX0 = sp.Symbol(\"S0_r\", real=True), sp.Symbol(\"S0_i\", real=True)\n", "\n", "Meven = model.mass_matrix([h0, s0])\n", "Meven" ] }, { "cell_type": "markdown", "id": "20", "metadata": {}, "source": [ "Goldstone counting is a sharp consistency check: two neutral gauge bosons\n", "are about to become massive ($Z$, $Z'$), so exactly two neutral\n", "pseudoscalars must be exactly massless — and indeed the whole CP-odd\n", "$(G^0, \\mathrm{Im}\\,S)$ block vanishes identically after the tadpole\n", "substitution (as does the charged-Goldstone mass, eaten by $W^\\pm$):" ] }, { "cell_type": "code", "execution_count": null, "id": "21", "metadata": {}, "outputs": [ { "data": { "text/latex": [ "$\\displaystyle \\left[\\begin{matrix}0 & 0\\\\0 & 0\\end{matrix}\\right]$" ], "text/plain": [ "Matrix([\n", "[0, 0],\n", "[0, 0]])" ] }, "execution_count": null, "metadata": {}, "output_type": "execute_result" } ], "source": [ "Modd = model.mass_matrix([G0, aX0])\n", "Mch = model.mass_matrix([Gp], charged=True)\n", "assert Modd == sp.zeros(2, 2) and sp.simplify(Mch[0, 0]) == 0\n", "Modd" ] }, { "cell_type": "markdown", "id": "22", "metadata": {}, "source": [ "`diagonalize_orthogonal_2x2` with a free angle symbol `thetas` gives the\n", "mass-basis rotation $(h_1, h_2)$ with the defining relation attached\n", "($h_1 \\to h$, the SM Higgs, as $\\theta_s \\to 0$):" ] }, { "cell_type": "code", "execution_count": null, "id": "23", "metadata": {}, "outputs": [ { "data": { "text/latex": [ "$\\displaystyle \\tan{\\left(2 \\theta_{s} \\right)} = \\frac{\\lambda_{HS} v v_{X}}{\\lambda v^{2} - \\lambda_{S} v_{X}^{2}}$" ], "text/plain": [ "Eq(tan(2*theta_s), lambda_HS*v*v_X/(lambda*v**2 - lambda_S*v_X**2))" ] }, "execution_count": null, "metadata": {}, "output_type": "execute_result" } ], "source": [ "h1, h2 = sp.symbols(\"h1 h2\", real=True)\n", "thetas = sp.Symbol(\"theta_s\", real=True)\n", "rot_s = diagonalize_orthogonal_2x2(Meven, [h0, s0], [h1, h2], angle=thetas)\n", "model.rotate(rot_s)\n", "rot_s.angle_relation" ] }, { "cell_type": "markdown", "id": "24", "metadata": {}, "source": [ "## 7. The neutral gauge sector: one 3×3, two chained 2×2 rotations\n", "\n", "With the Higgs charged under all three neutral generators, the\n", "$(W_3, B, X)$ mass matrix is a genuine 3×3 — the $a$-proportional entries\n", "in the third row/column are the Higgs's doing, and $\\det = 0$ promises\n", "exactly one massless photon:" ] }, { "cell_type": "code", "execution_count": null, "id": "25", "metadata": {}, "outputs": [ { "data": { "text/latex": [ "$\\displaystyle \\left[\\begin{matrix}\\frac{g_{w}^{2} v^{2}}{4} & - \\frac{g_{1} g_{w} v^{2}}{4} & - \\frac{a_{X} g_{X} g_{w} v^{2}}{4}\\\\- \\frac{g_{1} g_{w} v^{2}}{4} & \\frac{g_{1}^{2} v^{2}}{4} & \\frac{a_{X} g_{1} g_{X} v^{2}}{4}\\\\- \\frac{a_{X} g_{X} g_{w} v^{2}}{4} & \\frac{a_{X} g_{1} g_{X} v^{2}}{4} & \\frac{g_{X}^{2} \\left(a_{X}^{2} v^{2} + 4 q_{S}^{2} v_{X}^{2}\\right)}{4}\\end{matrix}\\right]$" ], "text/plain": [ "Matrix([\n", "[ g_w**2*v**2/4, -g_1*g_w*v**2/4, -a_X*g_X*g_w*v**2/4],\n", "[ -g_1*g_w*v**2/4, g_1**2*v**2/4, a_X*g_1*g_X*v**2/4],\n", "[-a_X*g_X*g_w*v**2/4, a_X*g_1*g_X*v**2/4, g_X**2*(a_X**2*v**2 + 4*q_S**2*v_X**2)/4]])" ] }, "execution_count": null, "metadata": {}, "output_type": "execute_result" } ], "source": [ "W1, W2, W3 = W.components\n", "B0 = B.components[0]\n", "X0 = X.components[0]\n", "\n", "M3 = model.gauge_mass_matrix([W3, B0, X0])\n", "assert sp.simplify(M3.det()) == 0\n", "M3" ] }, { "cell_type": "markdown", "id": "26", "metadata": {}, "source": [ "There is no symbolic 3×3 eigen-diagonalizer worth using (nested radicals —\n", "see `vacuum/diagonalize.py`'s docstring), and none is needed: the photon\n", "direction is fixed by symmetry alone. **Rotation 1 is the standard Weinberg\n", "rotation**, untouched by the extension — and it decouples the photon\n", "*exactly*, because the unbroken electric charge doesn't care about $X$.\n", "What survives is an intermediate $Z_0$ that still mixes with $X$:" ] }, { "cell_type": "code", "execution_count": null, "id": "27", "metadata": {}, "outputs": [ { "data": { "text/latex": [ "$\\displaystyle \\left[\\begin{matrix}\\frac{v^{2} \\left(g_{1}^{2} + g_{w}^{2}\\right)}{4} & 0 & - \\frac{a_{X} g_{X} v^{2} \\sqrt{g_{1}^{2} + g_{w}^{2}}}{4}\\\\0 & 0 & 0\\\\- \\frac{a_{X} g_{X} v^{2} \\sqrt{g_{1}^{2} + g_{w}^{2}}}{4} & 0 & g_{X}^{2} \\left(\\frac{a_{X}^{2} v^{2}}{4} + q_{S}^{2} v_{X}^{2}\\right)\\end{matrix}\\right]$" ], "text/plain": [ "Matrix([\n", "[ v**2*(g_1**2 + g_w**2)/4, 0, -a_X*g_X*v**2*sqrt(g_1**2 + g_w**2)/4],\n", "[ 0, 0, 0],\n", "[-a_X*g_X*v**2*sqrt(g_1**2 + g_w**2)/4, 0, g_X**2*(a_X**2*v**2/4 + q_S**2*v_X**2)]])" ] }, "execution_count": null, "metadata": {}, "output_type": "execute_result" } ], "source": [ "Z0, A, Z, Zp = sp.symbols(\"Z0 A Z Zp\", real=True)\n", "thetaW = sp.atan(g1.s / gw.s)\n", "model.rotate(Rotation([W3, B0], [Z0, A], rotation_2x2(-thetaW)))\n", "\n", "cw, sw = sp.cos(thetaW), sp.sin(thetaW)\n", "R1e = sp.Matrix([[cw, -sw, 0], [sw, cw, 0], [0, 0, 1]]) # (W3,B,X) -> (Z0,A,X)\n", "M3r = sp.simplify(R1e * M3 * R1e.T)\n", "assert M3r[1, :] == sp.zeros(1, 3) # photon row exactly zero\n", "M3r" ] }, { "cell_type": "markdown", "id": "28", "metadata": {}, "source": [ "**Rotation 2 diagonalizes the surviving $(Z_0, X)$ block.** Note what makes\n", "this chaining legal: `physical_lagrangian` applies registered rotations\n", "**sequentially, in registration order** — each one is a single `xreplace`\n", "pass. The Weinberg rotation leaves the plain symbol `Z0` in the Lagrangian,\n", "and the second rotation's substitution consumes exactly that symbol. (Order\n", "matters: register Weinberg first. If the rotations were applied as one\n", "simultaneous substitution instead, `Z0` — absent from the original\n", "Lagrangian — would never match anything.)\n", "\n", "With a free angle symbol `thetap`, every downstream coupling comes out in\n", "terms of $\\theta'$, and the defining $\\tan 2\\theta'$ relation is attached to\n", "the rotation:" ] }, { "cell_type": "code", "execution_count": null, "id": "29", "metadata": {}, "outputs": [ { "data": { "text/latex": [ "$\\displaystyle \\tan{\\left(2 \\theta_{p} \\right)} = \\frac{2 a_{X} g_{X} v^{2} \\sqrt{g_{1}^{2} + g_{w}^{2}}}{a_{X}^{2} g_{X}^{2} v^{2} - g_{1}^{2} v^{2} + 4 g_{X}^{2} q_{S}^{2} v_{X}^{2} - g_{w}^{2} v^{2}}$" ], "text/plain": [ "Eq(tan(2*theta_p), 2*a_X*g_X*v**2*sqrt(g_1**2 + g_w**2)/(a_X**2*g_X**2*v**2 - g_1**2*v**2 + 4*g_X**2*q_S**2*v_X**2 - g_w**2*v**2))" ] }, "execution_count": null, "metadata": {}, "output_type": "execute_result" } ], "source": [ "blockZX = M3r.extract([0, 2], [0, 2])\n", "thetap = sp.Symbol(\"theta_p\", real=True)\n", "rot_zzp = diagonalize_orthogonal_2x2(blockZX, [Z0, X0], [Z, Zp], angle=thetap)\n", "model.rotate(rot_zzp)\n", "rot_zzp.angle_relation" ] }, { "cell_type": "markdown", "id": "30", "metadata": {}, "source": [ "## 8. Dual verification, masses, and the W± rotation\n", "\n", "Per `CONVENTIONS.md`, every derived rotation gets verified twice: exactly\n", "(substitute the closed-form angle and check the off-diagonals vanish) and\n", "numerically at the benchmark point. The benchmark lands where it should —\n", "$m_Z \\approx 91.6$ GeV, and $m_{Z'} \\approx g_X q_S v_X = 1000$ GeV up to a\n", "small mixing correction:" ] }, { "cell_type": "code", "execution_count": null, "id": "31", "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "m_Z = 91.62 GeV\n", "m_Z' = 1000.31 GeV\n" ] } ], "source": [ "check_rot = Rotation([Z0, X0], [Z, Zp], rotation_2x2(rot_zzp.angle_solution))\n", "ok, residuals = check_rot.check(blockZX)\n", "assert ok, residuals\n", "\n", "bench = {gw.s: 0.6535, g1.s: 0.3580, gX.s: 0.5, aX.s: 0.4,\n", " qS.s: 1.0, v.s: 246.0, vX.s: 2000.0}\n", "mZ2, mZp2 = [complex(m.subs(bench)).real\n", " for m in check_rot.masses_squared(blockZX.subs(bench),\n", " simplifier=sp.nsimplify)]\n", "print(f\"m_Z = {mZ2**0.5:.2f} GeV\")\n", "print(f\"m_Z' = {mZp2**0.5:.2f} GeV\")" ] }, { "cell_type": "code", "execution_count": null, "id": "32", "metadata": {}, "outputs": [ { "data": { "text/latex": [ "$\\displaystyle \\left[\\begin{matrix}Wp\\\\Wm\\end{matrix}\\right] = \\left[\\begin{matrix}\\frac{\\sqrt{2} W_{1}}{2} - \\frac{\\sqrt{2} i W_{2}}{2}\\\\\\frac{\\sqrt{2} W_{1}}{2} + \\frac{\\sqrt{2} i W_{2}}{2}\\end{matrix}\\right]$" ], "text/plain": [ "Rotation(W_1→Wp, W_2→Wm, kind='unitary')" ] }, "execution_count": null, "metadata": {}, "output_type": "execute_result" } ], "source": [ "Wp, Wm = sp.symbols(\"Wp Wm\")\n", "Umix = sp.Matrix([[1, -sp.I], [1, sp.I]]) / sp.sqrt(2)\n", "model.rotate(Rotation([W1, W2], [Wp, Wm], Umix, kind=\"unitary\"))" ] }, { "cell_type": "markdown", "id": "33", "metadata": {}, "source": [ "## 9. Extracting the physical couplings\n", "\n", "The boson list now holds **eleven** physical fields — two CP-even scalars,\n", "two pseudoscalars, the charged Goldstones, and five gauge bosons. First a\n", "bosonic highlight from the kinetic sector: the $h_1 Z Z'$ vertex, which\n", "exists *only* because of the mixings (it vanishes as $\\theta' \\to 0$ with\n", "$\\theta_s \\to 0$... every factor of it is BSM):" ] }, { "cell_type": "code", "execution_count": null, "id": "34", "metadata": {}, "outputs": [ { "data": { "text/latex": [ "$\\displaystyle \\frac{i \\left(- a_{X} g_{X} v \\left(g_{1}^{2} + g_{w}^{2}\\right)^{3} \\cos{\\left(2 \\theta_{p} \\right)} \\cos{\\left(\\theta_{s} \\right)} - \\frac{g_{1}^{2} g_{w}^{2} v \\left(g_{1}^{2} + g_{w}^{2}\\right)^{\\frac{3}{2}} \\left(\\sin{\\left(2 \\theta_{p} - \\theta_{s} \\right)} + \\sin{\\left(2 \\theta_{p} + \\theta_{s} \\right)}\\right)}{2} + g_{X}^{2} \\left(g_{1}^{2} + g_{w}^{2}\\right)^{\\frac{5}{2}} \\left(a_{X}^{2} v \\cos{\\left(\\theta_{s} \\right)} + 4 q_{S}^{2} v_{X} \\sin{\\left(\\theta_{s} \\right)}\\right) \\sin{\\left(\\theta_{p} \\right)} \\cos{\\left(\\theta_{p} \\right)} - \\frac{v \\left(g_{1}^{2} + g_{w}^{2}\\right)^{\\frac{3}{2}} \\left(g_{1}^{4} + g_{w}^{4}\\right) \\left(\\sin{\\left(2 \\theta_{p} - \\theta_{s} \\right)} + \\sin{\\left(2 \\theta_{p} + \\theta_{s} \\right)}\\right)}{4}\\right)}{2 \\left(g_{1}^{2} + g_{w}^{2}\\right)^{\\frac{5}{2}}}$" ], "text/plain": [ "I*(-a_X*g_X*v*(g_1**2 + g_w**2)**3*cos(2*theta_p)*cos(theta_s) - g_1**2*g_w**2*v*(g_1**2 + g_w**2)**(3/2)*(sin(2*theta_p - theta_s) + sin(2*theta_p + theta_s))/2 + g_X**2*(g_1**2 + g_w**2)**(5/2)*(a_X**2*v*cos(theta_s) + 4*q_S**2*v_X*sin(theta_s))*sin(theta_p)*cos(theta_p) - v*(g_1**2 + g_w**2)**(3/2)*(g_1**4 + g_w**4)*(sin(2*theta_p - theta_s) + sin(2*theta_p + theta_s))/4)/(2*(g_1**2 + g_w**2)**(5/2))" ] }, "execution_count": null, "metadata": {}, "output_type": "execute_result" } ], "source": [ "Gm, cmap = conjugate_pair(Gp, \"Gm\")\n", "boson_fields = [h1, h2, G0, aX0, Gp, Gm, Z, A, Zp, Wp, Wm]\n", "\n", "rules = model.feynman_rules(boson_fields, sector=\"kinetic\",\n", " conjugate_map=cmap, simplifier=sp.simplify)\n", "key = tuple(sorted((h1, Z, Zp), key=lambda f: f.sort_key()))\n", "rules[key]" ] }, { "cell_type": "markdown", "id": "35", "metadata": {}, "source": [ "### Z′ couplings — the headline\n", "\n", "`physical_lagrangian(sector=\"yukawa\")` applies the vacuum shift, both\n", "tadpole solutions, and all five registered rotations;\n", "`extract_fermion_vertices` groups the result by `(bar, gamma, field)`.\n", "Every $Z' f f$ coupling should come out as\n", "\n", "$$g_{Z'ff} = -\\sin\\theta'\\; g_Z\\,(T^3 - Q\\,s_W^2)\\; +\\; \\cos\\theta'\\; g_X\\, q_f$$\n", "\n", "— the weak-basis $Z_0$ and $X$ couplings reshuffled by the mixing angle." ] }, { "cell_type": "code", "execution_count": null, "id": "36", "metadata": {}, "outputs": [], "source": [ "LYuk_phys = model.physical_lagrangian(sector=\"yukawa\")\n", "table = extract_fermion_vertices(LYuk_phys, boson_fields)\n", "\n", "mu = sp.Symbol(\"mu\", integer=True)\n", "gammaL = DiracGamma(mu) * diracPL\n", "gammaR = DiracGamma(mu) * diracPR\n", "\n", "\n", "def coeff(bar, gamma, field, boson):\n", " entry = table.get((bar, gamma, field))\n", " if entry is None:\n", " return sp.S.Zero\n", " return entry.get(1, {}).get((boson,), sp.S.Zero)" ] }, { "cell_type": "code", "execution_count": null, "id": "37", "metadata": {}, "outputs": [ { "data": { "text/latex": [ "$\\displaystyle - a_{X} g_{X} \\cos{\\left(\\theta_{p} \\right)} - b_{X} g_{X} \\cos{\\left(\\theta_{p} \\right)} - \\frac{g_{1}^{2} \\sin{\\left(\\theta_{p} \\right)}}{\\sqrt{g_{1}^{2} + g_{w}^{2}}}$" ], "text/plain": [ "-a_X*g_X*cos(theta_p) - b_X*g_X*cos(theta_p) - g_1**2*sin(theta_p)/sqrt(g_1**2 + g_w**2)" ] }, "execution_count": null, "metadata": {}, "output_type": "execute_result" } ], "source": [ "sp.simplify(coeff(eRbar[i], gammaR, eRc[i], Zp)) # Z' e_R e_R" ] }, { "cell_type": "code", "execution_count": null, "id": "38", "metadata": {}, "outputs": [ { "data": { "text/latex": [ "$\\displaystyle - b_{X} g_{X} \\cos{\\left(\\theta_{p} \\right)}$" ], "text/plain": [ "-b_X*g_X*cos(theta_p)" ] }, "execution_count": null, "metadata": {}, "output_type": "execute_result" } ], "source": [ "sp.simplify(coeff(nuRbar[i], gammaR, nuRc[i], Zp)) # Z' nu_R nu_R: pure gX*(-b)*cos" ] }, { "cell_type": "markdown", "id": "39", "metadata": {}, "source": [ "$\\nu_R$ has $T^3 = Q = Y = 0$: its whole gauge life is the $X$ charge $-b$,\n", "so its $Z'$ coupling is pure $\\cos\\theta'\\, g_X\\,(-b)$ — a Z′ portal to\n", "otherwise-sterile neutrinos.\n", "\n", "### Modified Z couplings\n", "\n", "The same reshuffle gives every SM fermion a $\\sin\\theta' \\cdot g_X q_f$\n", "correction to its $Z$ coupling ($\\theta' \\to 0$ recovers the SM — the\n", "practical handle electroweak precision data uses to bound $\\theta'$):" ] }, { "cell_type": "code", "execution_count": null, "id": "40", "metadata": {}, "outputs": [ { "data": { "text/latex": [ "$\\displaystyle - a_{X} g_{X} \\sin{\\left(\\theta_{p} \\right)} - b_{X} g_{X} \\sin{\\left(\\theta_{p} \\right)} + \\frac{g_{1}^{2} \\cos{\\left(\\theta_{p} \\right)}}{\\sqrt{g_{1}^{2} + g_{w}^{2}}}$" ], "text/plain": [ "-a_X*g_X*sin(theta_p) - b_X*g_X*sin(theta_p) + g_1**2*cos(theta_p)/sqrt(g_1**2 + g_w**2)" ] }, "execution_count": null, "metadata": {}, "output_type": "execute_result" } ], "source": [ "zeR = sp.simplify(coeff(eRbar[i], gammaR, eRc[i], Z))\n", "zeR" ] }, { "cell_type": "code", "execution_count": null, "id": "41", "metadata": {}, "outputs": [ { "data": { "text/latex": [ "$\\displaystyle \\frac{g_{1}^{2}}{\\sqrt{g_{1}^{2} + g_{w}^{2}}}$" ], "text/plain": [ "g_1**2/sqrt(g_1**2 + g_w**2)" ] }, "execution_count": null, "metadata": {}, "output_type": "execute_result" } ], "source": [ "zeR.subs(thetap, 0) # SM limit: gZ * sw^2" ] }, { "cell_type": "markdown", "id": "42", "metadata": {}, "source": [ "### Photon couplings — a sanity check\n", "\n", "The second rotation touched $Z_0$ and $X$ but never $A$, so photon\n", "couplings are *exactly* the SM electric charges, for any $a, b, \\theta'$ —\n", "and the neutrinos (both chiralities) stay dark:" ] }, { "cell_type": "code", "execution_count": null, "id": "43", "metadata": {}, "outputs": [ { "data": { "text/latex": [ "$\\displaystyle - \\frac{g_{1} g_{w}}{\\sqrt{g_{1}^{2} + g_{w}^{2}}}$" ], "text/plain": [ "-g_1*g_w/sqrt(g_1**2 + g_w**2)" ] }, "execution_count": null, "metadata": {}, "output_type": "execute_result" } ], "source": [ "sp.simplify(coeff(eLbar[i], gammaL, eL[i], A)) # -e, exactly" ] }, { "cell_type": "code", "execution_count": null, "id": "44", "metadata": {}, "outputs": [ { "data": { "text/latex": [ "$\\displaystyle 0$" ], "text/plain": [ "0" ] }, "execution_count": null, "metadata": {}, "output_type": "execute_result" } ], "source": [ "coeff(nuRbar[i], gammaR, nuRc[i], A) # 0 identically" ] }, { "cell_type": "markdown", "id": "45", "metadata": {}, "source": [ "### Higgs-portal couplings\n", "\n", "The scalar rotation splits every SM Yukawa coupling between the two mass\n", "eigenstates: $h_1 \\bar f f \\propto \\cos\\theta_s$ (the SM value, suppressed)\n", "and $h_2 \\bar f f \\propto \\sin\\theta_s$ (how the mostly-singlet scalar\n", "talks to SM fermions at all):" ] }, { "cell_type": "code", "execution_count": null, "id": "46", "metadata": {}, "outputs": [ { "data": { "text/latex": [ "$\\displaystyle - \\frac{\\sqrt{2} \\cos{\\left(\\theta_{s} \\right)} {Ye}_{i,j}}{2}$" ], "text/plain": [ "-sqrt(2)*cos(theta_s)*Ye[i, j]/2" ] }, "execution_count": null, "metadata": {}, "output_type": "execute_result" } ], "source": [ "sp.simplify(coeff(eLbar[i], diracPR, eRc[j], h1))" ] }, { "cell_type": "code", "execution_count": null, "id": "47", "metadata": {}, "outputs": [ { "data": { "text/latex": [ "$\\displaystyle \\frac{\\sqrt{2} \\sin{\\left(\\theta_{s} \\right)} {Ye}_{i,j}}{2}$" ], "text/plain": [ "sqrt(2)*sin(theta_s)*Ye[i, j]/2" ] }, "execution_count": null, "metadata": {}, "output_type": "execute_result" } ], "source": [ "sp.simplify(coeff(eLbar[i], diracPR, eRc[j], h2))" ] }, { "cell_type": "markdown", "id": "48", "metadata": {}, "source": [ "### Neutrino Dirac masses\n", "\n", "The $\\tilde H$ Yukawa delivers a flavor-generic Dirac mass matrix\n", "$M_\\nu = Y_\\nu\\, v/\\sqrt2$ (a Majorana $\\nu_R$ mass from\n", "$S\\,\\bar\\nu_R^c \\nu_R$ — i.e. a type-I seesaw — would need\n", "charge-conjugation machinery `feynlag` doesn't have yet):" ] }, { "cell_type": "code", "execution_count": null, "id": "49", "metadata": {}, "outputs": [ { "data": { "text/latex": [ "$\\displaystyle \\left[\\begin{matrix}\\frac{\\sqrt{2} v {Yv}_{0,0}}{2} & \\frac{\\sqrt{2} v {Yv}_{0,1}}{2} & \\frac{\\sqrt{2} v {Yv}_{0,2}}{2}\\\\\\frac{\\sqrt{2} v {Yv}_{1,0}}{2} & \\frac{\\sqrt{2} v {Yv}_{1,1}}{2} & \\frac{\\sqrt{2} v {Yv}_{1,2}}{2}\\\\\\frac{\\sqrt{2} v {Yv}_{2,0}}{2} & \\frac{\\sqrt{2} v {Yv}_{2,1}}{2} & \\frac{\\sqrt{2} v {Yv}_{2,2}}{2}\\end{matrix}\\right]$" ], "text/plain": [ "Matrix([\n", "[sqrt(2)*v*Yv[0, 0]/2, sqrt(2)*v*Yv[0, 1]/2, sqrt(2)*v*Yv[0, 2]/2],\n", "[sqrt(2)*v*Yv[1, 0]/2, sqrt(2)*v*Yv[1, 1]/2, sqrt(2)*v*Yv[1, 2]/2],\n", "[sqrt(2)*v*Yv[2, 0]/2, sqrt(2)*v*Yv[2, 1]/2, sqrt(2)*v*Yv[2, 2]/2]])" ] }, "execution_count": null, "metadata": {}, "output_type": "execute_result" } ], "source": [ "M_nu = fermion_mass_matrix(LYukN, nuLbar, nuRc, model.vacuum, 3, (i, j),\n", " gamma=diracPR)\n", "M_nu" ] }, { "cell_type": "markdown", "id": "50", "metadata": {}, "source": [ "## 10. Recap\n", "\n", "From one charge map $X = aY + b(B\\!-\\!L)$ and one singlet scalar, `feynlag`\n", "mechanically derived: symbolic-charge gauge invariance of the whole\n", "Lagrangian, two coupled tadpoles, the portal-mixed scalar spectrum with its\n", "two exactly-massless Goldstones, the 3×3 neutral gauge mass matrix\n", "diagonalized by chained Weinberg and $Z$–$Z'$ rotations with a closed-form\n", "$\\tan 2\\theta'$, and the full physical coupling table — $Z'ff$ for every\n", "species, the $\\theta'$-shifted $Z$ couplings, untouched photon couplings,\n", "and $\\cos\\theta_s/\\sin\\theta_s$-split Higgs couplings.\n", "\n", "- Exact pinned formulas for everything here (plus the $a \\to 0$ B−L limit,\n", " the W-sector non-renormalization, and the mass invariants):\n", " [`tests/test_u1x.py`](../tests/test_u1x.py).\n", "- The full flat script: [`examples/sm_u1x.py`](sm_u1x.py).\n", "- The plain-SM baseline:\n", " [`SM_Feynman_Rules_Tutorial.ipynb`](SM_Feynman_Rules_Tutorial.ipynb).\n", "- Not attempted here: kinetic mixing $\\varepsilon B_{\\mu\\nu}X^{\\mu\\nu}$\n", " (no field-strength primitive in the library yet) and UFO export (the UFO\n", " particle table needs numeric charges — substitute values for $a$, $b$,\n", " $q_S$ first)." ] } ], "metadata": { "kernelspec": { "display_name": "Python (lagrangian)", "language": "python", "name": "lagrangian" }, "language_info": { "codemirror_mode": { "name": "ipython", "version": 3 }, "file_extension": ".py", "mimetype": "text/x-python", "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", "version": "3.12.13" } }, "nbformat": 4, "nbformat_minor": 5 }