{ "cells": [ { "cell_type": "markdown", "metadata": {}, "source": [ "# Phase Scan Effect on Antenna Voltage Unbalance\n", "In this notebook we explore the effect of a phase scan over the voltage unbalance." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "%load_ext autoreload\n", "%autoreload 2" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "import matplotlib.pyplot as plt\n", "import numpy as np\n", "import skrf as rf\n", "from tqdm.notebook import tqdm\n", "\n", "# WEST ICRH Antenna package\n", "import sys; sys.path.append('..')\n", "from west_ic_antenna import WestIcrhAntenna" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "# nicer plot\n", "rf.stylely()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Let's use a TOPICA simulation of the WEST front face in a \"L-mode plasma\" at 55 MHz: " ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "freq = rf.Frequency(start=50, stop=60, npoints=1001, unit='MHz')\n", "plasma_TOPICA = '../west_ic_antenna/data/Sparameters/front_faces/TOPICA/S_TSproto12_55MHz_Hmode_LAD6-2.5cm.s4p'\n", "\n", "antenna = WestIcrhAntenna(frequency=freq, front_face=plasma_TOPICA)\n", "print(f'Optimal coupling resistance expected:', antenna.front_face_Rc().max())" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "First, we match the antenna, both sides at the same time to operate the antenna at 55 MHz." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "# antenna excitation to match for\n", "power = [1, 1] # W\n", "phase = [0, np.pi] # dipole\n", "\n", "Cs = antenna.match_both_sides(f_match=55e6, power=power, phase=phase, \n", " solution_number=1, verbose=False)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Let's visualize how good the antenna is matched for the target frequency, by looking to the active S parameters (ie. taking into account the antenna excitation)" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "s_act = antenna.s_act(power=power, phase=phase, Cs=Cs)\n", "\n", "fig, ax = plt.subplots()\n", "ax.plot(antenna.f_scaled, 20*np.log10(np.abs(s_act)), lw=2)\n", "ax.set_xlabel('Frequency [MHz]')\n", "ax.set_ylabel('Sact [dB]')\n", "ax.legend(('Left side', 'Right side'))\n", "ax.axvline(55, ls='--', color='gray')" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Now that the antenna is perfeclty matched, let's see how the voltages evolve with frequency. At the match frequency, the voltages (and currents) get very close: the antenna is balanced." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "fig, ax = plt.subplots()\n", "ax.plot(antenna.f_scaled, np.abs(antenna.voltages(power, phase, Cs)), lw=2)\n", "ax.axvline(55, ls='--', color='gray')\n", "ax.set_xlabel('Frequency [MHz]')\n", "ax.set_ylabel('Voltage [V]')\n", "ax.legend(('V1', 'V2', 'V3', 'V4'), ncol=4)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Note that the antenna voltages are not perfectly balanced at the match point, which is expected due to the intercouplings: " ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "fig, (ax1,ax2) = plt.subplots(2, 1, sharex=True)\n", "ax1.plot(antenna.f_scaled, np.abs(antenna.voltages(power, phase, Cs)), lw=2)\n", "[a.axvline(55, ls='--', color='gray') for a in (ax1,ax2)]\n", "ax2.set_xlabel('Frequency [MHz]')\n", "ax1.set_ylabel('Voltage [V]')\n", "ax1.legend(('V1', 'V2', 'V3', 'V4'), ncol=4)\n", "ax1.set_xlim(54.7, 55.3)\n", "ax1.set_ylim(25, 37)\n", "ax2.plot(antenna.f_scaled, 20*np.log10(np.abs(s_act)), lw=2)\n", "fig.subplots_adjust(hspace=0)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ " So let's see how the voltages evolve at the match frequency when we sweep the phase shift between sides:" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "power = [1e6, 1e6] # 1 MW each side\n", "toroidal_phases = np.deg2rad(np.linspace(start=0, stop=360, num=36))" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "antenna_single_freq = WestIcrhAntenna(frequency=rf.Frequency(55, 55, 1, unit='MHz'),\n", " front_face=plasma_TOPICA, Cs=Cs) \n", "\n", "voltages, currents, Rcs = [], [], []\n", "for toroidal_phase in tqdm(toroidal_phases):\n", " voltages.append(antenna_single_freq.voltages(power, phase=[0, toroidal_phase]))\n", " currents.append(antenna_single_freq.voltages(power, phase=[0, toroidal_phase]))\n", " Rcs.append(antenna_single_freq.Rc_WEST(power, phase=[0, toroidal_phase]))\n", "voltages = np.array(voltages).squeeze()\n", "currents = np.array(currents).squeeze()\n", "Rcs = np.array(Rcs).squeeze()" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "# export data\n", "np.savetxt('voltages_vs_phase.csv', \n", " np.c_[np.rad2deg(toroidal_phases), np.abs(voltages)])" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "fig, ax = plt.subplots()\n", "ax.plot(np.rad2deg(toroidal_phases), np.abs(voltages)/1e3, lw=2)\n", "ax.axvline(180, ls='--', color='gray')\n", "ax.set_xlabel('Toroidal Phase [deg]')\n", "ax.set_ylabel('Voltage [kV]')\n", "ax.legend(('V1', 'V2', 'V3', 'V4'), ncol=4)\n", "ax.set_ylim(0,50)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "The phase sweep generates voltage (or currents) unbalance." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "The voltage probe toroidal phase difference is also slightly affected in this case:" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "fig, ax = plt.subplots()\n", "ax.plot(np.rad2deg(toroidal_phases), np.rad2deg(np.angle(voltages[:,2]) - np.angle(voltages[:,0]))%360, lw=2)\n", "ax.plot(np.rad2deg(toroidal_phases), np.rad2deg(np.angle(voltages[:,3]) - np.angle(voltages[:,1]))%360, lw=2)\n", "ax.plot(np.rad2deg(toroidal_phases), np.rad2deg(toroidal_phases), color='k', ls='--')\n", "ax.axvline(180, ls='--', color='gray')\n", "ax.set_xlabel('Toroidal Phase [deg]')\n", "ax.set_ylabel('Phase [deg]')\n", "ax.legend(('$\\Delta \\phi$ $V_3-V_1$', '$\\Delta \\phi$ $V_4-V_2$'))" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Coupling resistance, which is deduced from the currents, is of course also affected:" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "fig, ax = plt.subplots()\n", "ax.plot(np.rad2deg(toroidal_phases), Rcs, lw=2)\n", "ax.axvline(180, ls='--', color='gray')\n", "ax.set_xlabel('Toroidal Phase [deg]')\n", "ax.set_ylabel('Coupling Resistances [Ohm]')\n", "ax.legend(('Left', 'Right'), ncol=4)\n", "ax.set_ylim(0,1.3)" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "from IPython.core.display import HTML\n", "def _set_css_style(css_file_path):\n", " \"\"\"\n", " Read the custom CSS file and load it into Jupyter\n", " Pass the file path to the CSS file\n", " \"\"\"\n", " styles = open(css_file_path, \"r\").read()\n", " s = '' % styles\n", " return HTML(s)\n", "\n", "_set_css_style('custom.css')" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [] } ], "metadata": { "kernelspec": { "display_name": "Python 3 (ipykernel)", "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.13.7" } }, "nbformat": 4, "nbformat_minor": 4 }