{ "cells": [ { "cell_type": "markdown", "id": "5f2f6e2c-cd1c-4086-86ae-4bd5659f994e", "metadata": {}, "source": [ "# Utility Functions" ] }, { "attachments": {}, "cell_type": "markdown", "id": "6ff65e69-39a7-40b3-9e34-9c4619674178", "metadata": {}, "source": [ "### Fundamental Frequencies" ] }, { "attachments": {}, "cell_type": "markdown", "id": "7393f536-7254-43c7-bcd8-c5d59bf5be69", "metadata": {}, "source": [ "Get dimensionless fundamental frequencies from [Schmidt 2002](https://arxiv.org/abs/gr-qc/0202090):" ] }, { "cell_type": "code", "execution_count": 1, "id": "fb77fea3", "metadata": {}, "outputs": [], "source": [ "import numpy as np" ] }, { "cell_type": "code", "execution_count": 2, "id": "30b46c2c-7fa9-498a-9060-74bab347b534", "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "p\t\te\t\tx\tOmegaPhi\tOmegaTheta\tOmegaR\n", "[[ 8. 0.2 0.3 0.04344785 0.04162395 0.02349858]\n", " [ 8.22222222 0.22222222 0.31111111 0.0412017 0.03953685 0.02299122]\n", " [ 8.44444444 0.24444444 0.32222222 0.03905666 0.03753599 0.02241441]\n", " [ 8.66666667 0.26666667 0.33333333 0.03700405 0.03561442 0.02178189]\n", " [ 8.88888889 0.28888889 0.34444444 0.03503633 0.03376608 0.02110469]\n", " [ 9.11111111 0.31111111 0.35555556 0.0331469 0.03198567 0.02039181]\n", " [ 9.33333333 0.33333333 0.36666667 0.03133003 0.03026855 0.01965064]\n", " [ 9.55555556 0.35555556 0.37777778 0.02958068 0.02861065 0.01888732]\n", " [ 9.77777778 0.37777778 0.38888889 0.02789444 0.02700838 0.01810703]\n", " [10. 0.4 0.4 0.02626744 0.02545863 0.01731413]]\n" ] } ], "source": [ "from few.utils.utility import get_fundamental_frequencies\n", "\n", "a = 0.5\n", "\n", "num = 10\n", "p = np.linspace(8.0, 10.0, num)\n", "e = np.linspace(0.2, 0.4, num)\n", "x = np.linspace(0.3, 0.4, num)\n", "\n", "OmegaPhi, OmegaTheta, OmegaR = get_fundamental_frequencies(a, p, e, x)\n", "\n", "out = np.array([p, e, x, OmegaPhi, OmegaTheta, OmegaR]).T\n", "print(\"p\\t\\te\\t\\tx\\tOmegaPhi\\tOmegaTheta\\tOmegaR\")\n", "print(out)" ] }, { "attachments": {}, "cell_type": "markdown", "id": "4896ae05-a1fd-4d02-9895-52d405cdf546", "metadata": {}, "source": [ "### Separatrix in Generic Kerr" ] }, { "attachments": {}, "cell_type": "markdown", "id": "29897dd6-bf8d-4a60-aa96-1b50fb616202", "metadata": {}, "source": [ "Get the separatrix in generic Kerr from [Stein & Warburton 2020](https://arxiv.org/abs/1912.07609):" ] }, { "cell_type": "code", "execution_count": 3, "id": "ad57031e-8a85-4513-b108-fe891d0e8cfa", "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "e\t\tx\tseparatrix\n", "[[0.2 0.3 5.69505166]\n", " [0.22222222 0.31111111 5.71503602]\n", " [0.24444444 0.32222222 5.73483554]\n", " [0.26666667 0.33333333 5.75445904]\n", " [0.28888889 0.34444444 5.7739152 ]\n", " [0.31111111 0.35555556 5.79321253]\n", " [0.33333333 0.36666667 5.81235943]\n", " [0.35555556 0.37777778 5.83136416]\n", " [0.37777778 0.38888889 5.85023483]\n", " [0.4 0.4 5.86897945]]\n" ] } ], "source": [ "from few.utils.utility import get_separatrix\n", "\n", "a = 0.5\n", "\n", "num = 10\n", "e = np.linspace(0.2, 0.4, num)\n", "x = np.linspace(0.3, 0.4, num)\n", "\n", "p_sep = get_separatrix(a, e, x)\n", "\n", "out = np.array([e, x, p_sep]).T\n", "print(\"e\\t\\tx\\tseparatrix\")\n", "print(out)" ] }, { "attachments": {}, "cell_type": "markdown", "id": "6ce3b068-dcd7-4b2c-bc32-b0ed6e9b2a47", "metadata": {}, "source": [ "### Converting between the integrals of motion $(E, L, Q)$ and the orbital elements $(p, e, x)$" ] }, { "attachments": {}, "cell_type": "markdown", "id": "bb23be6e-d08f-48ad-b524-ce7184257966", "metadata": {}, "source": [ "We can convert to the three constants of motion $(E,L,Q)$ in generic Kerr spacetime, and go back to $(p, e, x)$ via [Hughes 2024](https://arxiv.org/abs/2401.09577):" ] }, { "cell_type": "code", "execution_count": 4, "id": "79674a24-5f46-415e-9856-18d20f5a4cef", "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "To ELQ:\n", "p\t\te\t\tx\tE\tL\tQ\n", "[[ 8. 0.2 0.3 0.94869805 1.0488073 11.14493312]\n", " [ 8.2 0.22 0.31 0.94995592 1.09042731 11.20587846]\n", " [ 8.4 0.24 0.32 0.9512292 1.1326752 11.26724045]\n", " [ 8.6 0.26 0.33 0.95251428 1.17554328 11.32839477]\n", " [ 8.8 0.28 0.34 0.95380826 1.21902438 11.38878189]\n", " [ 9. 0.3 0.35 0.95510877 1.26311183 11.44789629]\n", " [ 9.2 0.32 0.36 0.95641391 1.30779937 11.50527773]\n", " [ 9.4 0.34 0.37 0.9577221 1.35308111 11.5605041 ]\n", " [ 9.6 0.36 0.38 0.9590321 1.3989515 11.61318563]\n", " [ 9.8 0.38 0.39 0.96034289 1.44540531 11.66296002]\n", " [10. 0.4 0.4 0.96165365 1.49243756 11.70948848]]\n", "To pex:\n", "p\t\te\t\tx\tE\tL\tQ\n", "[[ 8. 0.2 0.3 0.94869805 1.0488073 11.14493312]\n", " [ 8.2 0.22 0.31 0.94995592 1.09042731 11.20587846]\n", " [ 8.4 0.24 0.32 0.9512292 1.1326752 11.26724045]\n", " [ 8.6 0.26 0.33 0.95251428 1.17554328 11.32839477]\n", " [ 8.8 0.28 0.34 0.95380826 1.21902438 11.38878189]\n", " [ 9. 0.3 0.35 0.95510877 1.26311183 11.44789629]\n", " [ 9.2 0.32 0.36 0.95641391 1.30779937 11.50527773]\n", " [ 9.4 0.34 0.37 0.9577221 1.35308111 11.5605041 ]\n", " [ 9.6 0.36 0.38 0.9590321 1.3989515 11.61318563]\n", " [ 9.8 0.38 0.39 0.96034289 1.44540531 11.66296002]\n", " [10. 0.4 0.4 0.96165365 1.49243756 11.70948848]]\n" ] } ], "source": [ "from few.utils.utility import get_kerr_geo_constants_of_motion, ELQ_to_pex\n", "\n", "a = 0.5\n", "\n", "num = 11\n", "p = np.linspace(8.0, 10.0, num)\n", "e = np.linspace(0.2, 0.4, num)\n", "x = np.linspace(0.3, 0.4, num)\n", "\n", "print(\"To ELQ:\")\n", "E, L, Q = get_kerr_geo_constants_of_motion(a, p, e, x)\n", "\n", "out = np.array([p, e, x, E, L, Q]).T\n", "print(\"p\\t\\te\\t\\tx\\tE\\tL\\tQ\")\n", "print(out)\n", "\n", "print(\"To pex:\")\n", "p_new, e_new, x_new = ELQ_to_pex(a, E, L, Q)\n", "\n", "out = np.array([p_new, e_new, x_new, E, L, Q]).T\n", "print(\"p\\t\\te\\t\\tx\\tE\\tL\\tQ\")\n", "print(out)" ] }, { "attachments": {}, "cell_type": "markdown", "id": "232cb15d-f715-40a0-b7ed-bc6c040f45fd", "metadata": {}, "source": [ "### Convert between $x_I$ and $Y$" ] }, { "attachments": {}, "cell_type": "markdown", "id": "9faaadc4-1b5e-4840-a195-7195df06fe54", "metadata": {}, "source": [ "$Y\\equiv\\cos{\\iota}=L/\\sqrt{L^2 + Q}$ is different than $x_I\\equiv \\cos{I}$, which is accepted for relativistic waveforms and in the generic waveform interface discussed above. $I$ is the inclination angle of the orbital plane to the to equatorial plane." ] }, { "cell_type": "code", "execution_count": 5, "id": "5aee24b3-d8e0-4aa9-9d6f-003a1d7b5eb1", "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "To Y:\n", "p\t\te\t\tx\tY\n", "[[ 8. 0.2 0.3 0.29972126]\n", " [ 8.2 0.22 0.31 0.30972412]\n", " [ 8.4 0.24 0.32 0.31972764]\n", " [ 8.6 0.26 0.33 0.32973176]\n", " [ 8.8 0.28 0.34 0.33973642]\n", " [ 9. 0.3 0.35 0.34974158]\n", " [ 9.2 0.32 0.36 0.35974718]\n", " [ 9.4 0.34 0.37 0.36975319]\n", " [ 9.6 0.36 0.38 0.37975956]\n", " [ 9.8 0.38 0.39 0.38976626]\n", " [10. 0.4 0.4 0.39977325]]\n", "To x:\n", "p\t\te\t\tx\tY\n", "[[ 8. 0.2 0.3 0.29972126]\n", " [ 8.2 0.22 0.31 0.30972412]\n", " [ 8.4 0.24 0.32 0.31972764]\n", " [ 8.6 0.26 0.33 0.32973176]\n", " [ 8.8 0.28 0.34 0.33973642]\n", " [ 9. 0.3 0.35 0.34974158]\n", " [ 9.2 0.32 0.36 0.35974718]\n", " [ 9.4 0.34 0.37 0.36975319]\n", " [ 9.6 0.36 0.38 0.37975956]\n", " [ 9.8 0.38 0.39 0.38976626]\n", " [10. 0.4 0.4 0.39977325]]\n" ] } ], "source": [ "from few.utils.pn_map import xI_to_Y, Y_to_xI\n", "\n", "a = 0.5\n", "\n", "num = 11\n", "p = np.linspace(8.0, 10.0, num)\n", "e = np.linspace(0.2, 0.4, num)\n", "x = np.linspace(0.3, 0.4, num)\n", "\n", "Y = xI_to_Y(a, p, e, x)\n", "\n", "out = np.array([p, e, x, Y]).T\n", "print(\"To Y:\\np\\t\\te\\t\\tx\\tY\")\n", "print(out)\n", "\n", "x_new = Y_to_xI(a, p, e, Y)\n", "out = np.array([p, e, x_new, Y]).T\n", "print(\"To x:\\np\\t\\te\\t\\tx\\tY\")\n", "print(out)" ] }, { "attachments": {}, "cell_type": "markdown", "id": "f26944b1-436b-460a-8117-ddb6044b1520", "metadata": {}, "source": [ "### Get $\\mu$ based on desired duration of waveform" ] }, { "attachments": {}, "cell_type": "markdown", "id": "ffdcb4a4-ae1a-427a-b0ce-8864b83e4944", "metadata": {}, "source": [ "If you have a desired length of waveform to analyze, this function will give you the value of $\\mu$ that corresponds to the proper evolution time. " ] }, { "cell_type": "code", "execution_count": 6, "id": "95302ff8-b0f6-4a9a-a2ac-6d462e0321b5", "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "mu = 18.804307109481528 will create a waveform that is 1.5 years long, given the other input parameters.\n" ] } ], "source": [ "from few.trajectory.inspiral import EMRIInspiral\n", "from few.trajectory.ode import SchwarzEccFlux\n", "from few.utils.utility import get_mu_at_t\n", "\n", "traj_module = EMRIInspiral(func=SchwarzEccFlux)\n", "\n", "# set initial parameters\n", "M = 1e6\n", "p0 = 11.0\n", "e0 = 0.7\n", "\n", "traj_args = [M, 0.0, p0, e0, 1.0]\n", "traj_kwargs = {}\n", "index_of_mu = 1\n", "\n", "t_out = 1.5\n", "# run trajectory\n", "mu_new = get_mu_at_t(\n", " traj_module,\n", " t_out,\n", " traj_args,\n", " index_of_mu=index_of_mu,\n", " traj_kwargs=traj_kwargs,\n", " xtol=2e-12,\n", " rtol=8.881784197001252e-16,\n", " bounds=None,\n", ")\n", "\n", "print(\n", " \"mu = {} will create a waveform that is {} years long, given the other input parameters.\".format(\n", " mu_new, t_out\n", " )\n", ")" ] }, { "attachments": {}, "cell_type": "markdown", "id": "3a952e78-d8bd-4743-af88-b60abbaea9ed", "metadata": {}, "source": [ "### Get $p_0$ based on desired duration of waveform" ] }, { "attachments": {}, "cell_type": "markdown", "id": "c358fa64-698b-4e6a-81f4-68b950543dab", "metadata": {}, "source": [ "If you have a desired length of waveform to analyze, this function will give you the value of $p_0$ that corresponds to the proper evolution time. " ] }, { "cell_type": "code", "execution_count": 7, "id": "b3bd3ec6-df17-4e4b-b878-3e8a59b5a3d6", "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "p0 = 13.059789978787903 will create a waveform that is 1.5 years long, given the other input parameters.\n" ] } ], "source": [ "from few.utils.utility import get_p_at_t\n", "\n", "traj_module = EMRIInspiral(func=SchwarzEccFlux)\n", "\n", "# set initial parameters\n", "M = 1e6\n", "mu = 5e1\n", "e0 = 0.7\n", "\n", "traj_args = [M, mu, 0.0, e0, 1.0]\n", "traj_kwargs = {}\n", "index_of_p = 3\n", "\n", "t_out = 1.5\n", "# run trajectory\n", "p_new = get_p_at_t(\n", " traj_module,\n", " t_out,\n", " traj_args,\n", " index_of_p=3,\n", " index_of_a=2,\n", " index_of_e=4,\n", " index_of_x=5,\n", " traj_kwargs={},\n", " xtol=2e-12,\n", " rtol=8.881784197001252e-16,\n", " bounds=None,\n", ")\n", "\n", "print(\n", " \"p0 = {} will create a waveform that is {} years long, given the other input parameters.\".format(\n", " p_new, t_out\n", " )\n", ")" ] } ], "metadata": { "kernelspec": { "display_name": "few-venv", "language": "python", "name": "python3" }, "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.3" } }, "nbformat": 4, "nbformat_minor": 5 }