From 1e6d920d949144778482ef393db0971b1af1b711 Mon Sep 17 00:00:00 2001 From: Luca Rosignoli Date: Fri, 8 Dec 2023 15:52:38 +0100 Subject: [PATCH 1/4] Latest version of the test script. Constrain on max displacement and user query to continue. --- ...ty_Timeout_motion_limit_small_steps.ipynb} | 254 ++++++++++++++---- 1 file changed, 198 insertions(+), 56 deletions(-) rename notebooks/tel_and_site/subsys_req_ver/m2/m2_functional_verification/{LVV-T1782_M2_RMP_functionality_check_small_range_Timeout.ipynb => LVV-T1782_M2_RMP_functionality_Timeout_motion_limit_small_steps.ipynb} (74%) diff --git a/notebooks/tel_and_site/subsys_req_ver/m2/m2_functional_verification/LVV-T1782_M2_RMP_functionality_check_small_range_Timeout.ipynb b/notebooks/tel_and_site/subsys_req_ver/m2/m2_functional_verification/LVV-T1782_M2_RMP_functionality_Timeout_motion_limit_small_steps.ipynb similarity index 74% rename from notebooks/tel_and_site/subsys_req_ver/m2/m2_functional_verification/LVV-T1782_M2_RMP_functionality_check_small_range_Timeout.ipynb rename to notebooks/tel_and_site/subsys_req_ver/m2/m2_functional_verification/LVV-T1782_M2_RMP_functionality_Timeout_motion_limit_small_steps.ipynb index 454d1b45..29c235ad 100644 --- a/notebooks/tel_and_site/subsys_req_ver/m2/m2_functional_verification/LVV-T1782_M2_RMP_functionality_check_small_range_Timeout.ipynb +++ b/notebooks/tel_and_site/subsys_req_ver/m2/m2_functional_verification/LVV-T1782_M2_RMP_functionality_Timeout_motion_limit_small_steps.ipynb @@ -5,12 +5,12 @@ "id": "cff92e66-8d7e-46a0-92a5-bede9f097f62", "metadata": {}, "source": [ - "# M2 Rigid Body Position Small range\n", - " \n", + "# M2 Rigid Body Position\n", + "\n", "This Jupyter notebook performs Rigid Body Position (RBP) displacements of the M2 and verify how the force on the axial and tangent actuators change.\n", - "Each M2 RBP Degree of Freedom (DoF) is actuated individually with a progressive movements, the stop positions and the increment are user defined.\n", + "The M2 Rigid Body Movement (RBM) along each Degree of Freedom (DoF) is actuated individually with the target position query to the user.\n", "\n", - "For each DoF the 'Start' input is query to the user (response either y (yes) or n (no)).\n", + "In addition, a 'Start' input is query to the user (response either y [yes] or n [no]) with a timeout exit in order to avoid endless execution of the script.\n", "\n", "The `move_m2_rbp()` defined below move the M2 as rigid body using the `m2.cmd_positionMirror.set_start()` function and controlling new telemetry using the `seqNum` attribute, an integer number in the telemetry call attributes that increment only when new telemetry data are retrived.\n", "\n", @@ -43,6 +43,13 @@ "execution_count": null, "id": "6945ae01-c911-44cb-98bf-0399914c4da0", "metadata": { + "execution": { + "iopub.execute_input": "2023-11-28T13:34:16.472026Z", + "iopub.status.busy": "2023-11-28T13:34:16.471800Z", + "iopub.status.idle": "2023-11-28T13:34:17.440220Z", + "shell.execute_reply": "2023-11-28T13:34:17.439200Z", + "shell.execute_reply.started": "2023-11-28T13:34:16.472001Z" + }, "tags": [] }, "outputs": [], @@ -60,7 +67,7 @@ " LIMIT_FORCE_TANGENT_CLOSED_LOOP,\n", " LIMIT_FORCE_AXIAL_CLOSED_LOOP,\n", ")\n", - "import logging" + "import logging\n" ] }, { @@ -76,7 +83,15 @@ "cell_type": "code", "execution_count": null, "id": "e6bd97f6-e66f-4818-856a-5dcfd40e00f0", - "metadata": {}, + "metadata": { + "execution": { + "iopub.execute_input": "2023-11-28T13:34:18.299173Z", + "iopub.status.busy": "2023-11-28T13:34:18.298874Z", + "iopub.status.idle": "2023-11-28T13:34:18.309360Z", + "shell.execute_reply": "2023-11-28T13:34:18.308543Z", + "shell.execute_reply.started": "2023-11-28T13:34:18.299147Z" + } + }, "outputs": [], "source": [ "class MyLogger:\n", @@ -134,7 +149,15 @@ "cell_type": "code", "execution_count": null, "id": "5734533b-cddb-44b3-b5f6-48d4d32e3709", - "metadata": {}, + "metadata": { + "execution": { + "iopub.execute_input": "2023-11-28T13:34:21.600126Z", + "iopub.status.busy": "2023-11-28T13:34:21.599582Z", + "iopub.status.idle": "2023-11-28T13:34:21.606397Z", + "shell.execute_reply": "2023-11-28T13:34:21.605658Z", + "shell.execute_reply.started": "2023-11-28T13:34:21.600094Z" + } + }, "outputs": [], "source": [ "async def get_hardpoint_steps():\n", @@ -169,6 +192,13 @@ "execution_count": null, "id": "288f62eb-f07e-47cb-8ce0-1990b4c01914", "metadata": { + "execution": { + "iopub.execute_input": "2023-11-28T13:34:24.001071Z", + "iopub.status.busy": "2023-11-28T13:34:24.000619Z", + "iopub.status.idle": "2023-11-28T13:34:24.008772Z", + "shell.execute_reply": "2023-11-28T13:34:24.007921Z", + "shell.execute_reply.started": "2023-11-28T13:34:24.001040Z" + }, "tags": [] }, "outputs": [], @@ -203,6 +233,13 @@ "execution_count": null, "id": "ebbb566e-2247-4256-8d26-40054560bc7c", "metadata": { + "execution": { + "iopub.execute_input": "2023-11-28T13:34:25.591946Z", + "iopub.status.busy": "2023-11-28T13:34:25.591566Z", + "iopub.status.idle": "2023-11-28T13:34:25.599743Z", + "shell.execute_reply": "2023-11-28T13:34:25.599083Z", + "shell.execute_reply.started": "2023-11-28T13:34:25.591918Z" + }, "tags": [] }, "outputs": [], @@ -234,6 +271,8 @@ " # of the loop and wait.\n", " continue\n", "\n", + " init_seqnum = eval_seqnum\n", + "\n", " hardpoint = await get_hardpoint_steps()\n", " logger.info(f\"New hardpoint telemetry data: \\n{hardpoint}\")\n", "\n", @@ -264,12 +303,19 @@ "execution_count": null, "id": "db171d7f-32d7-4e04-8726-94d163cea5eb", "metadata": { + "execution": { + "iopub.execute_input": "2023-11-28T13:34:26.934163Z", + "iopub.status.busy": "2023-11-28T13:34:26.933802Z", + "iopub.status.idle": "2023-11-28T13:34:26.948481Z", + "shell.execute_reply": "2023-11-28T13:34:26.948071Z", + "shell.execute_reply.started": "2023-11-28T13:34:26.934135Z" + }, "tags": [] }, "outputs": [], "source": [ "async def checking_actuator_force(\n", - " logger: logging.Logger, wait_force_telemetry: float = 1.0\n", + " logger: logging.Logger, wait_force_telemetry: float = 1.0, gain: float = 0.95\n", "):\n", " # Start checking all the actuator forces.\n", " # The logic of this block is to gather at least 'min_force_sample' new telemetry measurements,\n", @@ -353,19 +399,19 @@ " limit_reach = True\n", " max_id = -1\n", "\n", - " if max_tangent_force_error[1] > TANGENT_LINK_LOAD_BEARING_LINK: # 950:\n", + " if max_tangent_force_error[1] > gain*TANGENT_LINK_LOAD_BEARING_LINK: # 950:\n", " max_id = max_tangent_force_error[0][0]\n", "\n", - " elif max_tangent_force[1] > LIMIT_FORCE_TANGENT_CLOSED_LOOP: # 4870:\n", + " elif max_tangent_force[1] > gain*LIMIT_FORCE_TANGENT_CLOSED_LOOP: # 4870:\n", " max_id = max_tangent_force[0][0]\n", "\n", - " elif max_sum_tangent_force_error[1] > TANGENT_LINK_THETA_Z_MOMENT: # 950:\n", + " elif max_sum_tangent_force_error[1] > gain*TANGENT_LINK_THETA_Z_MOMENT: # 950:\n", " max_id = sum_tangent_force_error[0][0]\n", "\n", - " elif max_weight_tangent_force_error[1] > TANGENT_LINK_TOTAL_WEIGHT_ERROR: # 1900:\n", + " elif max_weight_tangent_force_error[1] > gain*TANGENT_LINK_TOTAL_WEIGHT_ERROR: # 1900:\n", " max_id = weight_tangent_force_error[0][0]\n", "\n", - " elif max_axial_force[1] > LIMIT_FORCE_AXIAL_CLOSED_LOOP: # 420:\n", + " elif max_axial_force[1] > gain*LIMIT_FORCE_AXIAL_CLOSED_LOOP: # 420:\n", " max_id = max_axial_force[0][0]\n", "\n", " else:\n", @@ -387,10 +433,22 @@ "execution_count": null, "id": "7fd71907-e1d9-4a89-a0fb-1458e3c2f9ca", "metadata": { + "execution": { + "iopub.execute_input": "2023-11-28T13:34:28.103001Z", + "iopub.status.busy": "2023-11-28T13:34:28.102623Z", + "iopub.status.idle": "2023-11-28T13:34:28.112446Z", + "shell.execute_reply": "2023-11-28T13:34:28.111336Z", + "shell.execute_reply.started": "2023-11-28T13:34:28.102971Z" + }, "tags": [] }, "outputs": [], "source": [ + "# Function that managed the M2 rigid body movement, it calls first the move_axis()\n", + "# function to perform the displacement, then the wait_m2_to_settle() wait and check \n", + "# until the completion of such movement. Eventually, the checking_actuator_force()\n", + "# controls all the forces and stop if some threshold is met.\n", + "\n", "async def move_m2_rbp(logger: logging.Logger, axis: str = None, position: float = 0.0):\n", " logger.info(f\"Moving to {position} micron along {axis}-axis\")\n", " await move_axis(logger, axis, position)\n", @@ -427,7 +485,9 @@ "\n", " else:\n", " logger.info(\"CURRENT STATUS\")\n", - " logger.info(log)" + " logger.info(log)\n", + " \n", + " return limit_reach" ] }, { @@ -435,6 +495,13 @@ "execution_count": null, "id": "e5a86bda-e165-4950-9bad-8fcbf8123211", "metadata": { + "execution": { + "iopub.execute_input": "2023-11-28T13:34:29.318449Z", + "iopub.status.busy": "2023-11-28T13:34:29.317945Z", + "iopub.status.idle": "2023-11-28T13:34:29.322992Z", + "shell.execute_reply": "2023-11-28T13:34:29.322250Z", + "shell.execute_reply.started": "2023-11-28T13:34:29.318418Z" + }, "tags": [] }, "outputs": [], @@ -450,6 +517,13 @@ "execution_count": null, "id": "7625f8d5-5177-4c90-b5d4-709790bf4e99", "metadata": { + "execution": { + "iopub.execute_input": "2023-11-28T13:34:31.054302Z", + "iopub.status.busy": "2023-11-28T13:34:31.053805Z", + "iopub.status.idle": "2023-11-28T13:34:31.059554Z", + "shell.execute_reply": "2023-11-28T13:34:31.058689Z", + "shell.execute_reply.started": "2023-11-28T13:34:31.054273Z" + }, "tags": [] }, "outputs": [], @@ -464,11 +538,28 @@ " return False" ] }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## N.B: CONSERVATIVE LIMIT FOR M2 TANGENT FORCE ERROR.\n", + "Based on previous multiple faults of m2 during rigid body movements a maximum displacement has been implement (the `max_step` parameter).\n", + "\n", + "For more information see the [SITCOMTN-99](https://github.com/lsst-sitcom/sitcomtn-099)" + ] + }, { "cell_type": "code", "execution_count": null, "id": "2c63f99c-4da1-4567-8586-2c2ffc0b876d", "metadata": { + "execution": { + "iopub.execute_input": "2023-11-28T13:41:56.186693Z", + "iopub.status.busy": "2023-11-28T13:41:56.185955Z", + "iopub.status.idle": "2023-11-28T13:41:56.195342Z", + "shell.execute_reply": "2023-11-28T13:41:56.194480Z", + "shell.execute_reply.started": "2023-11-28T13:41:56.186660Z" + }, "tags": [] }, "outputs": [], @@ -477,9 +568,11 @@ "# This function return as soon as one of the two coroutine ends.\n", "# This implement a timeout for the Start query function.\n", "async def main(\n", - " axis: str, max_position: float, increment: float, logger: logging.Logger\n", + " axis: str, logger: logging.Logger, max_position: float = None, increment: float = None \n", "):\n", - " t = 5.0\n", + " t = 120 # timeout time in sec\n", + "\n", + " max_step = 200 #Conservative value to avoid Tangent Force Error fault.\n", "\n", " task1 = asyncio.create_task(timeout(t), name=\"Timeout\")\n", " task2 = asyncio.create_task(start_m2_move(), name=\"M2 movement\")\n", @@ -499,10 +592,29 @@ " el.cancel()\n", "\n", " if task_done == \"M2 movement\" and res:\n", - " for position in np.arange(0, max_position, increment):\n", - " if position == 0:\n", - " continue\n", - " await move_m2_rbp(logger, axis, position)" + " position = input('Insert position')\n", + "\n", + " match axis:\n", + " case 'x':\n", + " current_position = m2.tel_position.get().x\n", + " case 'y':\n", + " current_position = m2.tel_position.get().y\n", + " case 'z':\n", + " current_position = m2.tel_position.get().z\n", + " case _:\n", + " raise ValueError(\"Unrecognized axis\")\n", + " \n", + " if len(position) == 0:\n", + " logger.warning('Position invalid, quit test.')\n", + " return True\n", + " elif abs(current_position - float(position)) > max_step:\n", + " logger.warning('The request position is beyond the safe movement range')\n", + " return True\n", + " \n", + " limit_reach = await move_m2_rbp(logger, axis, float(position))\n", + " return limit_reach\n", + " else:\n", + " return True" ] }, { @@ -526,6 +638,13 @@ "execution_count": null, "id": "845bb53a-a792-48ce-8765-d3144311a8cb", "metadata": { + "execution": { + "iopub.execute_input": "2023-11-28T13:34:35.063876Z", + "iopub.status.busy": "2023-11-28T13:34:35.063504Z", + "iopub.status.idle": "2023-11-28T13:34:35.448354Z", + "shell.execute_reply": "2023-11-28T13:34:35.447726Z", + "shell.execute_reply.started": "2023-11-28T13:34:35.063847Z" + }, "tags": [] }, "outputs": [], @@ -541,6 +660,13 @@ "execution_count": null, "id": "c6679c96-f1b3-4d15-a220-355d33f72f5e", "metadata": { + "execution": { + "iopub.execute_input": "2023-11-28T13:34:51.064261Z", + "iopub.status.busy": "2023-11-28T13:34:51.063881Z", + "iopub.status.idle": "2023-11-28T13:34:51.072134Z", + "shell.execute_reply": "2023-11-28T13:34:51.071234Z", + "shell.execute_reply.started": "2023-11-28T13:34:51.064230Z" + }, "tags": [] }, "outputs": [], @@ -575,6 +701,13 @@ "execution_count": null, "id": "add9094e-4451-42d1-b8e1-dee22f2f6ad8", "metadata": { + "execution": { + "iopub.execute_input": "2023-11-28T16:50:31.520036Z", + "iopub.status.busy": "2023-11-28T16:50:31.519806Z", + "iopub.status.idle": "2023-11-28T16:50:31.524039Z", + "shell.execute_reply": "2023-11-28T16:50:31.523398Z", + "shell.execute_reply.started": "2023-11-28T16:50:31.520016Z" + }, "tags": [] }, "outputs": [], @@ -624,7 +757,7 @@ "outputs": [], "source": [ "# Disable --> Enabled\n", - "await m2.cmd_enable.set_start(timeout=450)" + "await m2.cmd_enable.set_start(timeout=550)" ] }, { @@ -669,6 +802,25 @@ "4. Configurable_File_Description_20180831T092556_surrogate_handling.csv" ] }, + { + "cell_type": "code", + "execution_count": null, + "id": "688b4bc5-24d2-44ec-97e4-a91f6f5b7408", + "metadata": { + "execution": { + "iopub.execute_input": "2023-11-28T13:34:59.110971Z", + "iopub.status.busy": "2023-11-28T13:34:59.109951Z", + "iopub.status.idle": "2023-11-28T13:34:59.115199Z", + "shell.execute_reply": "2023-11-28T13:34:59.114500Z", + "shell.execute_reply.started": "2023-11-28T13:34:59.110940Z" + } + }, + "outputs": [], + "source": [ + "ccfile = m2.evt_config.get().get_vars()['configuration']\n", + "print(ccfile)" + ] + }, { "cell_type": "code", "execution_count": null, @@ -704,25 +856,18 @@ "print(m2.evt_forceBalanceSystemStatus.get())" ] }, - { - "cell_type": "code", - "execution_count": null, - "id": "67c85328-a6f8-49af-a010-ad702ba81f58", - "metadata": { - "tags": [] - }, - "outputs": [], - "source": [ - "# state_m2 = m2.evt_summaryState.get()\n", - "# if state_m2.summaryState != salobj.State.ENABLED:\n", - "# await salobj.set_summary_state(m2, salobj.State.ENABLED, timeout=460)" - ] - }, { "cell_type": "code", "execution_count": null, "id": "586294a7-d612-4ee6-b677-855eb87b2194", "metadata": { + "execution": { + "iopub.execute_input": "2023-11-28T14:19:52.621965Z", + "iopub.status.busy": "2023-11-28T14:19:52.621604Z", + "iopub.status.idle": "2023-11-28T14:48:08.570387Z", + "shell.execute_reply": "2023-11-28T14:48:08.569407Z", + "shell.execute_reply.started": "2023-11-28T14:19:52.621937Z" + }, "tags": [] }, "outputs": [], @@ -744,17 +889,24 @@ "\n", "logger.info(log)\n", "\n", - "max_position = 20.0\n", - "increment = 5.0\n", - "await m2.cmd_positionMirror.set_start() # Restoring Zero position\n", - "await main(axis, max_position + increment, increment, logger)" + "keep_continue = False\n", + "while not keep_continue:\n", + " keep_continue = await main(axis=axis, logger=logger)" ] }, { "cell_type": "code", "execution_count": null, "id": "f25238a7-6869-4f91-a95d-100d520c1676", - "metadata": {}, + "metadata": { + "execution": { + "iopub.execute_input": "2023-11-28T16:50:51.220893Z", + "iopub.status.busy": "2023-11-28T16:50:51.220542Z", + "iopub.status.idle": "2023-11-28T18:00:15.412295Z", + "shell.execute_reply": "2023-11-28T18:00:15.411397Z", + "shell.execute_reply.started": "2023-11-28T16:50:51.220865Z" + } + }, "outputs": [], "source": [ "# Y-DOF\n", @@ -774,10 +926,9 @@ "\n", "logger.info(log)\n", "\n", - "max_position = 20.0\n", - "increment = 2.0\n", - "await m2.cmd_positionMirror.set_start() # Restoring Zero position\n", - "await main(axis, max_position + increment, increment, logger)" + "keep_continue = False\n", + "while not keep_continue:\n", + " keep_continue = await main(axis=axis, logger=logger)" ] }, { @@ -804,19 +955,10 @@ "\n", "logger.info(log)\n", "\n", - "max_position = 20.0\n", - "increment = 2.0\n", - "await m2.cmd_positionMirror.set_start() # Restoring Zero position\n", - "await main(axis, max_position + increment, increment, logger)" + "keep_continue = False\n", + "while not keep_continue:\n", + " keep_continue = await main(axis=axis, logger=logger)" ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "a2a48693-4a83-47df-bd81-0bee2489307c", - "metadata": {}, - "outputs": [], - "source": [] } ], "metadata": { From b5f016bbc86702984fd5e4ecbfa2c85517d12774 Mon Sep 17 00:00:00 2001 From: Luca Rosignoli Date: Tue, 16 Jul 2024 15:58:01 +0200 Subject: [PATCH 2/4] Added an analysis script folder and updated the LVV-T1782 RMP test script --- ...ity_Timeout_motion_limit_small_steps.ipynb | 215 +++++- .../analysis script/bump_test_analysis.ipynb | 654 ++++++++++++++++++ 2 files changed, 835 insertions(+), 34 deletions(-) create mode 100644 notebooks/tel_and_site/subsys_req_ver/m2/m2_functional_verification/analysis script/bump_test_analysis.ipynb diff --git a/notebooks/tel_and_site/subsys_req_ver/m2/m2_functional_verification/LVV-T1782_M2_RMP_functionality_Timeout_motion_limit_small_steps.ipynb b/notebooks/tel_and_site/subsys_req_ver/m2/m2_functional_verification/LVV-T1782_M2_RMP_functionality_Timeout_motion_limit_small_steps.ipynb index 29c235ad..b7e57ec8 100644 --- a/notebooks/tel_and_site/subsys_req_ver/m2/m2_functional_verification/LVV-T1782_M2_RMP_functionality_Timeout_motion_limit_small_steps.ipynb +++ b/notebooks/tel_and_site/subsys_req_ver/m2/m2_functional_verification/LVV-T1782_M2_RMP_functionality_Timeout_motion_limit_small_steps.ipynb @@ -67,7 +67,7 @@ " LIMIT_FORCE_TANGENT_CLOSED_LOOP,\n", " LIMIT_FORCE_AXIAL_CLOSED_LOOP,\n", ")\n", - "import logging\n" + "import logging" ] }, { @@ -203,28 +203,62 @@ }, "outputs": [], "source": [ - "async def move_axis(logger: logging.Logger, axis: str = None, position: float = 0.0):\n", + "async def move_axis(\n", + " logger: logging.Logger, axis: str = None, position: float = 0.0\n", + "):\n", + "\n", " # ABSOLUTE position displacement\n", + "\n", " # Try to move or catch the Exception (i.e. it is in FAULT state)\n", + "\n", " # If incorrect axis is provided then raise a ValueError exception\n", "\n", + "\n", " match axis:\n", " case \"x\":\n", + "\n", " try:\n", + "\n", " await m2.cmd_positionMirror.set_start(x=position)\n", " except Exception as e:\n", + "\n", " logger.error(\"EXCEPTION OCCURRED\")\n", + "\n", " case \"y\":\n", + "\n", " try:\n", + "\n", " await m2.cmd_positionMirror.set_start(y=position)\n", " except Exception as e:\n", + "\n", " logger.error(\"EXCEPTION OCCURRED\")\n", + "\n", " case \"z\":\n", " try:\n", " await m2.cmd_positionMirror.set_start(z=position)\n", " except Exception as e:\n", " logger.error(\"EXCEPTION OCCURRED\")\n", + " case \"xRot\":\n", + " try:\n", + " await m2.cmd_positionMirror.set_start(xRot=position)\n", + " except Exception as e:\n", + " logger.error(\"EXCEPTION OCCURRED\")\n", + " case \"yRot\":\n", + " try:\n", + " await m2.cmd_positionMirror.set_start(yRot=position)\n", + " except Exception as e:\n", + " logger.error(\"EXCEPTION OCCURRED\")\n", + " case \"zRot\":\n", + "\n", + " try:\n", + "\n", + " await m2.cmd_positionMirror.set_start(zRot=position)\n", + " except Exception as e:\n", + "\n", + " logger.error(\"EXCEPTION OCCURRED\")\n", + "\n", " case _:\n", + "\n", " raise ValueError(\"Unrecognized axis\")" ] }, @@ -315,7 +349,9 @@ "outputs": [], "source": [ "async def checking_actuator_force(\n", - " logger: logging.Logger, wait_force_telemetry: float = 1.0, gain: float = 0.95\n", + " logger: logging.Logger,\n", + " wait_force_telemetry: float = 1.0,\n", + " gain: float = 0.95,\n", "):\n", " # Start checking all the actuator forces.\n", " # The logic of this block is to gather at least 'min_force_sample' new telemetry measurements,\n", @@ -352,7 +388,9 @@ "\n", " # Lists of floats\n", " sum_tangent_force_error.append(abs(m2.tel_forceErrorTangent.get().sum))\n", - " weight_tangent_force_error.append(abs(m2.tel_forceErrorTangent.get().weight))\n", + " weight_tangent_force_error.append(\n", + " abs(m2.tel_forceErrorTangent.get().weight)\n", + " )\n", "\n", " await asyncio.sleep(wait_force_telemetry)\n", "\n", @@ -369,7 +407,8 @@ " ]\n", " max_tangent_force = [\n", " np.unravel_index(\n", - " np.abs(np.array(tangent_force)).argmax(), np.array(tangent_force).shape\n", + " np.abs(np.array(tangent_force)).argmax(),\n", + " np.array(tangent_force).shape,\n", " ),\n", " np.abs(np.array(tangent_force)).max(),\n", " ]\n", @@ -399,19 +438,28 @@ " limit_reach = True\n", " max_id = -1\n", "\n", - " if max_tangent_force_error[1] > gain*TANGENT_LINK_LOAD_BEARING_LINK: # 950:\n", + " if (\n", + " max_tangent_force_error[1] > gain * TANGENT_LINK_LOAD_BEARING_LINK\n", + " ): # 950:\n", " max_id = max_tangent_force_error[0][0]\n", "\n", - " elif max_tangent_force[1] > gain*LIMIT_FORCE_TANGENT_CLOSED_LOOP: # 4870:\n", + " elif (\n", + " max_tangent_force[1] > gain * LIMIT_FORCE_TANGENT_CLOSED_LOOP\n", + " ): # 4870:\n", " max_id = max_tangent_force[0][0]\n", "\n", - " elif max_sum_tangent_force_error[1] > gain*TANGENT_LINK_THETA_Z_MOMENT: # 950:\n", + " elif (\n", + " max_sum_tangent_force_error[1] > gain * TANGENT_LINK_THETA_Z_MOMENT\n", + " ): # 950:\n", " max_id = sum_tangent_force_error[0][0]\n", "\n", - " elif max_weight_tangent_force_error[1] > gain*TANGENT_LINK_TOTAL_WEIGHT_ERROR: # 1900:\n", + " elif (\n", + " max_weight_tangent_force_error[1]\n", + " > gain * TANGENT_LINK_TOTAL_WEIGHT_ERROR\n", + " ): # 1900:\n", " max_id = weight_tangent_force_error[0][0]\n", "\n", - " elif max_axial_force[1] > gain*LIMIT_FORCE_AXIAL_CLOSED_LOOP: # 420:\n", + " elif max_axial_force[1] > gain * LIMIT_FORCE_AXIAL_CLOSED_LOOP: # 420:\n", " max_id = max_axial_force[0][0]\n", "\n", " else:\n", @@ -445,11 +493,14 @@ "outputs": [], "source": [ "# Function that managed the M2 rigid body movement, it calls first the move_axis()\n", - "# function to perform the displacement, then the wait_m2_to_settle() wait and check \n", + "# function to perform the displacement, then the wait_m2_to_settle() wait and check\n", "# until the completion of such movement. Eventually, the checking_actuator_force()\n", "# controls all the forces and stop if some threshold is met.\n", "\n", - "async def move_m2_rbp(logger: logging.Logger, axis: str = None, position: float = 0.0):\n", + "\n", + "async def move_m2_rbp(\n", + " logger: logging.Logger, axis: str = None, position: float = 0.0\n", + "):\n", " logger.info(f\"Moving to {position} micron along {axis}-axis\")\n", " await move_axis(logger, axis, position)\n", " await wait_m2_to_settle(logger)\n", @@ -486,7 +537,7 @@ " else:\n", " logger.info(\"CURRENT STATUS\")\n", " logger.info(log)\n", - " \n", + "\n", " return limit_reach" ] }, @@ -568,11 +619,14 @@ "# This function return as soon as one of the two coroutine ends.\n", "# This implement a timeout for the Start query function.\n", "async def main(\n", - " axis: str, logger: logging.Logger, max_position: float = None, increment: float = None \n", + " axis: str,\n", + " logger: logging.Logger,\n", + " max_position: float = None,\n", + " increment: float = None,\n", "):\n", - " t = 120 # timeout time in sec\n", + " t = 120 # timeout time in sec\n", "\n", - " max_step = 200 #Conservative value to avoid Tangent Force Error fault.\n", + " max_step = 200 # Conservative value to avoid Tangent Force Error fault.\n", "\n", " task1 = asyncio.create_task(timeout(t), name=\"Timeout\")\n", " task2 = asyncio.create_task(start_m2_move(), name=\"M2 movement\")\n", @@ -592,25 +646,27 @@ " el.cancel()\n", "\n", " if task_done == \"M2 movement\" and res:\n", - " position = input('Insert position')\n", + " position = input(\"Insert position\")\n", "\n", " match axis:\n", - " case 'x':\n", + " case \"x\":\n", " current_position = m2.tel_position.get().x\n", - " case 'y':\n", + " case \"y\":\n", " current_position = m2.tel_position.get().y\n", - " case 'z':\n", + " case \"z\":\n", " current_position = m2.tel_position.get().z\n", " case _:\n", " raise ValueError(\"Unrecognized axis\")\n", - " \n", + "\n", " if len(position) == 0:\n", - " logger.warning('Position invalid, quit test.')\n", + " logger.warning(\"Position invalid, quit test.\")\n", " return True\n", " elif abs(current_position - float(position)) > max_step:\n", - " logger.warning('The request position is beyond the safe movement range')\n", + " logger.warning(\n", + " \"The request position is beyond the safe movement range\"\n", + " )\n", " return True\n", - " \n", + "\n", " limit_reach = await move_m2_rbp(logger, axis, float(position))\n", " return limit_reach\n", " else:\n", @@ -732,7 +788,7 @@ "metadata": {}, "outputs": [], "source": [ - "#Fault --> Standby\n", + "# Fault --> Standby\n", "await m2.cmd_standby.set_start(timeout=30)" ] }, @@ -786,7 +842,7 @@ "metadata": {}, "outputs": [], "source": [ - "#Disable --> Standby\n", + "# Disable --> Standby\n", "await m2.cmd_standby.set_start(timeout=30)" ] }, @@ -817,7 +873,7 @@ }, "outputs": [], "source": [ - "ccfile = m2.evt_config.get().get_vars()['configuration']\n", + "ccfile = m2.evt_config.get().get_vars()[\"configuration\"]\n", "print(ccfile)" ] }, @@ -830,14 +886,18 @@ }, "outputs": [], "source": [ - "#Setting the right LUT ***ONLY IN DISABLE STATE ***\n", - "ccfile = m2.evt_config.get().get_vars()['configuration']\n", + "# Setting the right LUT ***ONLY IN DISABLE STATE ***\n", + "ccfile = m2.evt_config.get().get_vars()[\"configuration\"]\n", "\n", - "if 'surrogate_optical' not in ccfile:\n", - " config_files = m2.evt_configurationFiles.get().get_vars()['files'].split(',')\n", - " ncfile = [el for el in config_files if 'surrogate_optical' in el][0]\n", + "if \"surrogate_optical\" not in ccfile:\n", + " config_files = (\n", + " m2.evt_configurationFiles.get().get_vars()[\"files\"].split(\",\")\n", + " )\n", + " ncfile = [el for el in config_files if \"surrogate_optical\" in el][0]\n", " await m2.cmd_setConfigurationFile.set_start(file=ncfile)\n", - " logger.info(f'Current config file (switch to enable state to make the change effective): {m2.evt_config.get().get_vars()[\"configuration\"]}')" + " logger.info(\n", + " f'Current config file (switch to enable state to make the change effective): {m2.evt_config.get().get_vars()[\"configuration\"]}'\n", + " )" ] }, { @@ -849,7 +909,7 @@ }, "outputs": [], "source": [ - "#Debug cell\n", + "# Debug cell\n", "print(m2.evt_interlock.get())\n", "print(m2.evt_innerLoopControlMode.get())\n", "print(m2.evt_errorCode.get())\n", @@ -959,6 +1019,93 @@ "while not keep_continue:\n", " keep_continue = await main(axis=axis, logger=logger)" ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "a8091cd6", + "metadata": {}, + "outputs": [], + "source": [ + "# xRot-DOF\n", + "\n", + "axis = \"xRot\"\n", + "\n", + "log = (\n", + " f\"** STARTING MOVING ON {axis}-axis**\"\n", + " f\"\\nM2 POSITION:\"\n", + " f\"\\nX: {m2.tel_position.get().x:.2f}\"\n", + " f\"\\nY: {m2.tel_position.get().y:.2f}\"\n", + " f\"\\nZ: {m2.tel_position.get().z:.2f}\"\n", + " f\"\\nXROT: {m2.tel_position.get().xRot:.2f}\"\n", + " f\"\\nYROT: {m2.tel_position.get().yRot:.2f}\"\n", + " f\"\\nZROT: {m2.tel_position.get().zRot:.2f}\"\n", + ")\n", + "\n", + "logger.info(log)\n", + "\n", + "keep_continue = False\n", + "while not keep_continue:\n", + " keep_continue = await main(axis=axis, logger=logger)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "449b7eab", + "metadata": {}, + "outputs": [], + "source": [ + "# yRot-DOF\n", + "\n", + "axis = \"yRot\"\n", + "\n", + "log = (\n", + " f\"** STARTING MOVING ON {axis}-axis**\"\n", + " f\"\\nM2 POSITION:\"\n", + " f\"\\nX: {m2.tel_position.get().x:.2f}\"\n", + " f\"\\nY: {m2.tel_position.get().y:.2f}\"\n", + " f\"\\nZ: {m2.tel_position.get().z:.2f}\"\n", + " f\"\\nXROT: {m2.tel_position.get().xRot:.2f}\"\n", + " f\"\\nYROT: {m2.tel_position.get().yRot:.2f}\"\n", + " f\"\\nZROT: {m2.tel_position.get().zRot:.2f}\"\n", + ")\n", + "\n", + "logger.info(log)\n", + "\n", + "keep_continue = False\n", + "while not keep_continue:\n", + " keep_continue = await main(axis=axis, logger=logger)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "9c2b1d04", + "metadata": {}, + "outputs": [], + "source": [ + "# zRot-DOF\n", + "\n", + "axis = \"zRot\"\n", + "\n", + "log = (\n", + " f\"** STARTING MOVING ON {axis}-axis**\"\n", + " f\"\\nM2 POSITION:\"\n", + " f\"\\nX: {m2.tel_position.get().x:.2f}\"\n", + " f\"\\nY: {m2.tel_position.get().y:.2f}\"\n", + " f\"\\nZ: {m2.tel_position.get().z:.2f}\"\n", + " f\"\\nXROT: {m2.tel_position.get().xRot:.2f}\"\n", + " f\"\\nYROT: {m2.tel_position.get().yRot:.2f}\"\n", + " f\"\\nZROT: {m2.tel_position.get().zRot:.2f}\"\n", + ")\n", + "\n", + "logger.info(log)\n", + "\n", + "keep_continue = False\n", + "while not keep_continue:\n", + " keep_continue = await main(axis=axis, logger=logger)" + ] } ], "metadata": { diff --git a/notebooks/tel_and_site/subsys_req_ver/m2/m2_functional_verification/analysis script/bump_test_analysis.ipynb b/notebooks/tel_and_site/subsys_req_ver/m2/m2_functional_verification/analysis script/bump_test_analysis.ipynb new file mode 100644 index 00000000..07933bb6 --- /dev/null +++ b/notebooks/tel_and_site/subsys_req_ver/m2/m2_functional_verification/analysis script/bump_test_analysis.ipynb @@ -0,0 +1,654 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# This script is intended to inspect a Bump test and it compares the measured forces with the stiffness matrix." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "import lsst_efd_client\n", + "import numpy as np\n", + "import pandas as pd\n", + "from astropy.time import Time\n", + "import matplotlib.pyplot as plt\n", + "import yaml" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "async def get_merge_query(\n", + " efd_client: lsst_efd_client.EfdClient,\n", + " topics: list,\n", + " start_time: Time,\n", + " end_time: Time,\n", + " tolerance=None,\n", + " direction: str = None,\n", + ") -> pd.DataFrame:\n", + " \"\"\"Function to merge multiple topics into a single dataframe.\n", + "\n", + " Args:\n", + " efd_client (lsst_efd_client.EfdClient): EFD client object.\n", + " topics (list): EFD topics to query and merge.\n", + " start_time (Time): Start time of the query.\n", + " end_time (Time): End time of the query.\n", + " tolerance (float, optional): Tolerance time for matching, see pandas.merge_asof for more information. Defaults to None will use defualt value.\n", + " direction (str, optional): Join method to pass to pd.merge_asod method. Defaults to None will use default value.\n", + "\n", + " Returns:\n", + " pd.DataFrame: Merged dataframe.\n", + " \"\"\"\n", + " query_df = list()\n", + " for topic in topics:\n", + " topic_fields = await efd_client.get_fields(topic)\n", + " query = efd_client.build_time_range_query(\n", + " topic, topic_fields, start_time, end_time\n", + " )\n", + " data_query = await efd_client.influx_client.query(query)\n", + "\n", + " if len(data_query) == 0:\n", + " print(f\"{topic} is not present.\")\n", + " else:\n", + " data_query.rename(\n", + " columns={\n", + " col: f\"{col}_{topic.split('.')[-1]}\"\n", + " for col in data_query.columns\n", + " },\n", + " inplace=True,\n", + " )\n", + " query_df.append(data_query)\n", + "\n", + " if len(query_df) == 1:\n", + " return query_df[0]\n", + " elif len(query_df) == 0:\n", + " print(\"No Dataframe retrieve\")\n", + " return None\n", + "\n", + " query_df.sort(key=lambda el: len(el), reverse=False)\n", + " merge_df = query_df[0].copy()\n", + "\n", + " for i in range(1, len(query_df)):\n", + " col_left = f\"merged_{i}\"\n", + " col_right = f\"eval_{i}\"\n", + "\n", + " if tolerance is None and direction is None:\n", + " merge_df = lsst_efd_client.rendezvous_dataframes(\n", + " merge_df,\n", + " query_df[i],\n", + " direction=\"nearest\",\n", + " suffixes=[f\"_{col_left}\", f\"_{col_right}\"],\n", + " )\n", + "\n", + " elif tolerance is None and direction is not None:\n", + " merge_df = lsst_efd_client.rendezvous_dataframes(\n", + " merge_df,\n", + " query_df[i],\n", + " direction=direction,\n", + " suffixes=[f\"_{col_left}\", f\"_{col_right}\"],\n", + " )\n", + "\n", + " elif tolerance is not None and direction is None:\n", + " merge_df = lsst_efd_client.rendezvous_dataframes(\n", + " merge_df,\n", + " query_df[i],\n", + " direction=\"nearest\",\n", + " tolerance=tolerance,\n", + " suffixes=[f\"_{col_left}\", f\"_{col_right}\"],\n", + " )\n", + " else:\n", + " merge_df = lsst_efd_client.rendezvous_dataframes(\n", + " merge_df,\n", + " query_df[i],\n", + " direction=direction,\n", + " tolerance=tolerance,\n", + " suffixes=[f\"_{col_left}\", f\"_{col_right}\"],\n", + " )\n", + "\n", + " return merge_df" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "def get_residual(\n", + " query_df: pd.DataFrame,\n", + " moved_act: list,\n", + " act_movement: dict,\n", + " stiff_matrix: np.array,\n", + " which: str = \"axial\",\n", + "):\n", + " \"\"\"Generate the residual of each actuator w.r.t to the nominal value in the stiff matrix.\n", + "\n", + " Args:\n", + " query_df (pd.DataFrame): DataFrame with the bump test data.\n", + " moved_act (list): List of moved actuators.\n", + " act_movement (dict): Dictionary with the start and end times of the actuator movement.\n", + " stiff_matrix (np.array): Stiffness matrix of the mirror.\n", + " which (str, optional): Which actuators to work on (axial or tangent). Defaults to \"axial\".\n", + "\n", + " Returns:\n", + " residual (list): residual of each actuator w.r.t to the nominal value\n", + " \"\"\"\n", + "\n", + " p = True\n", + " residual = []\n", + " for i, m_act in enumerate(moved_act):\n", + " if which == \"axial\":\n", + " stiff_to_use = stiff_matrix[int(m_act), :72]\n", + " else:\n", + " stiff_to_use = stiff_matrix[int(m_act) + 72, 72:]\n", + "\n", + " idi = query_df.loc[\n", + " act_movement[list(act_movement.keys())[i]][0][0]\n", + " ].name\n", + " idi = query_df.index.to_list().index(idi)\n", + " idi = query_df.index[idi + 15]\n", + "\n", + " idf = query_df.loc[\n", + " act_movement[list(act_movement.keys())[i]][-1][-1]\n", + " ].name\n", + " idf = query_df.index.to_list().index(idf)\n", + " idf = query_df.index[idf - 10]\n", + "\n", + " if p:\n", + " fig, axs = plt.subplots(\n", + " nrows=2,\n", + " ncols=1,\n", + " layout=\"constrained\",\n", + " subplot_kw={\"xlabel\": \"Time, UTC\"},\n", + " )\n", + " query_df.loc[\n", + " act_movement[list(act_movement.keys())[i]][0][\n", + " 0\n", + " ] : act_movement[list(act_movement.keys())[i]][-1][-1],\n", + " f\"steps{i}_{which}ActuatorSteps\",\n", + " ].plot(ax=axs[0], title=\"Actuator step\", marker=\"+\", color=\"blue\")\n", + " query_df.loc[\n", + " act_movement[list(act_movement.keys())[i]][0][\n", + " 0\n", + " ] : act_movement[list(act_movement.keys())[i]][-1][-1],\n", + " f\"measured{i}_{which}Force\",\n", + " ].plot(\n", + " ax=axs[1], title=\"Measured Force\", marker=\"+\", color=\"orange\"\n", + " )\n", + "\n", + " axs[0].axvline(idi, color=\"r\")\n", + " axs[0].axvline(idf, color=\"r\")\n", + " axs[1].axvline(idi, color=\"r\")\n", + " axs[1].axvline(idf, color=\"r\")\n", + "\n", + " axs[0].set_ylabel(\"Steps\")\n", + " axs[1].set_ylabel(\"Force, N\")\n", + "\n", + " p = False\n", + "\n", + " residual.append(\n", + " (\n", + " stiff_to_use\n", + " * (\n", + " query_df.loc[\n", + " idi,\n", + " sorted(\n", + " [\n", + " col\n", + " for col in query_df.columns\n", + " if \"steps\" in col\n", + " ],\n", + " key=lambda x: int(\n", + " x.split(\"_\")[0].replace(\"steps\", \"\")\n", + " ),\n", + " ),\n", + " ].values\n", + " - query_df.loc[\n", + " idf,\n", + " sorted(\n", + " [\n", + " col\n", + " for col in query_df.columns\n", + " if \"steps\" in col\n", + " ],\n", + " key=lambda x: int(\n", + " x.split(\"_\")[0].replace(\"steps\", \"\")\n", + " ),\n", + " ),\n", + " ].values\n", + " )\n", + " - (\n", + " query_df.loc[\n", + " idi,\n", + " sorted(\n", + " [\n", + " col\n", + " for col in query_df.columns\n", + " if \"measured\" in col\n", + " ],\n", + " key=lambda x: int(\n", + " x.split(\"_\")[0].replace(\"measured\", \"\")\n", + " ),\n", + " ),\n", + " ].values\n", + " - query_df.loc[\n", + " idf,\n", + " sorted(\n", + " [\n", + " col\n", + " for col in query_df.columns\n", + " if \"measured\" in col\n", + " ],\n", + " key=lambda x: int(\n", + " x.split(\"_\")[0].replace(\"measured\", \"\")\n", + " ),\n", + " ),\n", + " ].values\n", + " )\n", + " )\n", + " )\n", + "\n", + " return residual" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "def get_moved_actuator(query_df: pd.DataFrame, which: str = \"axial\"):\n", + " \"\"\"Get the moved actuators and the time interval of the movement.\n", + "\n", + " Args:\n", + " query_df: DataFrame with the bump test data.\n", + "\n", + " Returns:\n", + " moved_act: List of moved actuators.\n", + " act_movement: Dictionary with the start and end times of the actuator movement.\n", + " which (str, optional): Which actuators to work on (axial or tangent). Defaults to \"axial\".\n", + " \"\"\"\n", + " which_force = \"axialForce\" if which == \"axial\" else \"tangentForce\"\n", + " moved_act = []\n", + " for col in query_df.columns:\n", + " if (\n", + " \"applied\" in col\n", + " and which_force in col\n", + " and query_df[col].any() != 0\n", + " ):\n", + " moved_act.append(\n", + " int(col.replace(\"applied\", \"\").replace(f\"_{which_force}\", \"\"))\n", + " )\n", + "\n", + " moved_act = sorted(moved_act)\n", + " print(\"Bumped Actuators: \", *moved_act)\n", + " act_movement = {k: [] for k in moved_act}\n", + " for act in moved_act:\n", + " tg_ser = query_df[f\"applied{act}_{which_force}\"]\n", + " c = tg_ser.index[0]\n", + " for i, row in tg_ser.items():\n", + " if i <= c:\n", + " continue\n", + " if row != 0:\n", + " start = i\n", + " for ii, rrow in tg_ser.loc[i:].items():\n", + " if rrow == 0:\n", + " end = ii\n", + " c = ii\n", + " break\n", + " act_movement[act].append((start, end))\n", + " if len(act_movement[act]) > 2:\n", + " act_movement[act] = act_movement[act][0:2]\n", + " return moved_act, act_movement" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "def plot_residual_map(\n", + " ax: plt.Axes, cell_geom: np.array, residual: list, m_act: int\n", + "):\n", + " \"\"\"Plot the residual map.\n", + "\n", + " Args:\n", + " fig (plt.Figure): Matplotlib figure object.\n", + " cell_geom (np.array): file containing the position of each atuator in the cell\n", + " residual (list): residual of each actuator w.r.t to the nominal value\n", + " m_act (int): id of the moved actuator\n", + " \"\"\"\n", + "\n", + " act_map = [(x, y, val) for (x, y), val in zip(cell_geom, residual)]\n", + " list_res = list(residual)\n", + "\n", + " vmin = min(list_res)\n", + " vmax = max(list_res)\n", + "\n", + " ax.set_title(f\"#{m_act}\")\n", + " ax.scatter(\n", + " [el[0] for el in act_map],\n", + " [el[1] for el in act_map],\n", + " c=[el[2] for el in act_map],\n", + " cmap=\"coolwarm\",\n", + " vmin=vmin,\n", + " vmax=vmax,\n", + " )\n", + " ax.set_xlabel(\"X (M)\")\n", + " ax.set_ylabel(\"Y (M)\")\n", + "\n", + " cbar = ax.figure.colorbar(\n", + " ax.collections[0],\n", + " ax=ax,\n", + " orientation=\"vertical\",\n", + " label=r\"$\\Delta$ Force (N)\",\n", + " )" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# Initialize the EFD client and upload the cell geometry for plotting.\n", + "efd = lsst_efd_client.EfdClient(\"usdf_efd\")\n", + "\n", + "cell_geom = np.array(\n", + " yaml.safe_load(\n", + " open(\n", + " r\"C:\\Users\\utente\\Desktop\\PhD\\LSST\\M2\\bending_modes\\cell_geom.yaml\"\n", + " )\n", + " )[\"locAct_axial\"]\n", + ")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Axial Actuators" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "stiff_matrix = np.array(\n", + " yaml.safe_load(\n", + " open(\n", + " r\"C:\\Users\\utente\\Desktop\\PhD\\LSST\\M2\\force_balance\\stiff_matrix_surrogate.yaml\"\n", + " )\n", + " )[\"stiff\"]\n", + ")\n", + "\n", + "topics = [\n", + " \"lsst.sal.MTM2.axialActuatorSteps\",\n", + " \"lsst.sal.MTM2.axialForce\",\n", + "]" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# Bump 100N (surrogate)\n", + "# time_start = Time(\"2024-06-24T18:54:00\")\n", + "# time_stop = Time(\"2024-06-24T21:21:00\")\n", + "\n", + "# Bump 10N no tangent (surrogate)\n", + "# time_start = Time(\"2023-08-30T17:23:00\")\n", + "# time_stop = Time(\"2023-08-30T22:18:00\")\n", + "\n", + "# Bump 10N with tangent (glass)\n", + "time_start = Time(\"2024-07-15T16:27:40\")\n", + "time_stop = Time(\"2024-07-15T17:08:30\")\n", + "\n", + "stiff_matrix = np.array(\n", + " yaml.safe_load(\n", + " open(\n", + " r\"C:\\Users\\utente\\Desktop\\PhD\\LSST\\M2\\force_balance\\stiff_matrix_m2.yaml\"\n", + " )\n", + " )[\"stiff\"]\n", + ")\n", + "\n", + "query_df = await get_merge_query(efd, topics, time_start, time_stop)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "moved_act, act_movement = get_moved_actuator(query_df, which=\"axial\")" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# A diagnostic plot on when the measurement are taken will be displayed.\n", + "residual = get_residual(\n", + " query_df, moved_act, act_movement, stiff_matrix, which=\"axial\"\n", + ")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Plot the residuals statistic (mean and PtV) over all the 72 axial actuators" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "residual_mean = np.array(residual).mean(axis=1)\n", + "residual_ptv = np.array(residual).ptp(axis=1)\n", + "\n", + "residual_mean[[5, 15, 25]] = np.nan # Removing the hardpoints\n", + "residual_ptv[[5, 15, 25]] = np.nan # Removing the hardpoints\n", + "\n", + "\n", + "fig, axs = plt.subplots(\n", + " nrows=2,\n", + " ncols=1,\n", + " layout=\"constrained\",\n", + " subplot_kw={\"xlabel\": \"Actuator ID\"},\n", + ")\n", + "\n", + "axs[0].plot(\n", + " moved_act,\n", + " residual_mean,\n", + " marker=\"x\",\n", + " ls=\"--\",\n", + ")\n", + "axs[0].axvline(x=30.5, color=\"green\", label=\"B-ring\")\n", + "axs[0].axvline(x=54.5, color=\"orange\", label=\"C-ring\")\n", + "axs[0].axvline(x=72, color=\"red\", label=\"D-ring\")\n", + "axs[0].set_ylabel(\"Mean Measured - Stiff, N\")\n", + "\n", + "axs[1].plot(\n", + " moved_act,\n", + " residual_ptv,\n", + " marker=\"x\",\n", + " ls=\"--\",\n", + ")\n", + "axs[1].axvline(x=30.5, color=\"green\", label=\"B-ring\")\n", + "axs[1].axvline(x=54.5, color=\"orange\", label=\"C-ring\")\n", + "axs[1].axvline(x=72, color=\"red\", label=\"D-ring\")\n", + "\n", + "\n", + "axs[1].set_ylabel(\"PtV Measured - Stiff, N\")\n", + "\n", + "handles, labels = axs[0].get_legend_handles_labels()\n", + "fig.legend(handles=handles, labels=labels, loc=\"upper right\")" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "fig = plt.figure(layout=\"constrained\", figsize=(20, 25))\n", + "for a, val in act_movement.items():\n", + " ax = fig.add_subplot(12, 6, a + 1)\n", + " ax.set_box_aspect(1)\n", + " plot_residual_map(ax, cell_geom, residual[a], moved_act[a])" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Tangent Link" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "topics = [\n", + " \"lsst.sal.MTM2.tangentActuatorSteps\",\n", + " \"lsst.sal.MTM2.tangentForce\",\n", + "]\n", + "\n", + "# Bump 10N with tangent\n", + "time_start = Time(\"2024-07-15T16:27:40\")\n", + "time_stop = Time(\"2024-07-15T17:08:30\")\n", + "\n", + "query_df = await get_merge_query(efd, topics, time_start, time_stop)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "tangent_ang = np.array(\n", + " yaml.safe_load(\n", + " open(\n", + " r\"C:\\Users\\utente\\Desktop\\PhD\\LSST\\M2\\bending_modes\\cell_geom.yaml\"\n", + " )\n", + " )[\"locAct_tangent\"]\n", + ")\n", + "\n", + "tangent_rad = yaml.safe_load(\n", + " open(r\"C:\\Users\\utente\\Desktop\\PhD\\LSST\\M2\\bending_modes\\cell_geom.yaml\")\n", + ")[\"radiusActTangent\"]\n", + "\n", + "cell_geom = [\n", + " (\n", + " np.sin(np.deg2rad(ang)) * tangent_rad,\n", + " np.cos(np.deg2rad(ang)) * tangent_rad,\n", + " )\n", + " for ang in tangent_ang\n", + "]" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "moved_act, act_movement = get_moved_actuator(query_df, which=\"tangent\")\n", + "residual = get_residual(\n", + " query_df, moved_act, act_movement, stiff_matrix, which=\"tangent\"\n", + ")" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "residual_mean = np.array(residual).mean(axis=1)\n", + "residual_ptv = np.array(residual).ptp(axis=1)\n", + "\n", + "\n", + "fig, axs = plt.subplots(\n", + " nrows=2,\n", + " ncols=1,\n", + " layout=\"constrained\",\n", + " subplot_kw={\"xlabel\": \"Actuator ID\"},\n", + ")\n", + "\n", + "axs[0].plot(\n", + " moved_act,\n", + " residual_mean,\n", + " marker=\"x\",\n", + " ls=\"--\",\n", + ")\n", + "axs[0].set_ylabel(\"Mean Measured - Stiff, N\")\n", + "\n", + "axs[1].plot(\n", + " moved_act,\n", + " residual_ptv,\n", + " marker=\"x\",\n", + " ls=\"--\",\n", + ")\n", + "axs[1].set_ylabel(\"PtV Measured - Stiff, N\")" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "fig = plt.figure(layout=\"constrained\", figsize=(12, 6))\n", + "for a, val in act_movement.items():\n", + " ax = fig.add_subplot(2, 3, a + 1)\n", + " ax.set_box_aspect(1)\n", + " plot_residual_map(ax, cell_geom, residual[a], moved_act[a])" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "LSST-env", + "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.11.7" + } + }, + "nbformat": 4, + "nbformat_minor": 2 +} From e6d618c780497b9994550a418e328212bf7a4808 Mon Sep 17 00:00:00 2001 From: Luca Rosignoli Date: Wed, 17 Jul 2024 15:05:27 +0200 Subject: [PATCH 3/4] Gabriele's suggestions and comment fixed --- ...ity_Timeout_motion_limit_small_steps.ipynb | 42 +++++++++---------- 1 file changed, 19 insertions(+), 23 deletions(-) diff --git a/notebooks/tel_and_site/subsys_req_ver/m2/m2_functional_verification/LVV-T1782_M2_RMP_functionality_Timeout_motion_limit_small_steps.ipynb b/notebooks/tel_and_site/subsys_req_ver/m2/m2_functional_verification/LVV-T1782_M2_RMP_functionality_Timeout_motion_limit_small_steps.ipynb index b7e57ec8..b618edad 100644 --- a/notebooks/tel_and_site/subsys_req_ver/m2/m2_functional_verification/LVV-T1782_M2_RMP_functionality_Timeout_motion_limit_small_steps.ipynb +++ b/notebooks/tel_and_site/subsys_req_ver/m2/m2_functional_verification/LVV-T1782_M2_RMP_functionality_Timeout_motion_limit_small_steps.ipynb @@ -208,31 +208,20 @@ "):\n", "\n", " # ABSOLUTE position displacement\n", - "\n", " # Try to move or catch the Exception (i.e. it is in FAULT state)\n", - "\n", " # If incorrect axis is provided then raise a ValueError exception\n", "\n", - "\n", " match axis:\n", " case \"x\":\n", - "\n", " try:\n", - "\n", " await m2.cmd_positionMirror.set_start(x=position)\n", " except Exception as e:\n", - "\n", " logger.error(\"EXCEPTION OCCURRED\")\n", - "\n", " case \"y\":\n", - "\n", " try:\n", - "\n", " await m2.cmd_positionMirror.set_start(y=position)\n", " except Exception as e:\n", - "\n", " logger.error(\"EXCEPTION OCCURRED\")\n", - "\n", " case \"z\":\n", " try:\n", " await m2.cmd_positionMirror.set_start(z=position)\n", @@ -249,16 +238,11 @@ " except Exception as e:\n", " logger.error(\"EXCEPTION OCCURRED\")\n", " case \"zRot\":\n", - "\n", " try:\n", - "\n", " await m2.cmd_positionMirror.set_start(zRot=position)\n", " except Exception as e:\n", - "\n", " logger.error(\"EXCEPTION OCCURRED\")\n", - "\n", " case _:\n", - "\n", " raise ValueError(\"Unrecognized axis\")" ] }, @@ -501,7 +485,10 @@ "async def move_m2_rbp(\n", " logger: logging.Logger, axis: str = None, position: float = 0.0\n", "):\n", - " logger.info(f\"Moving to {position} micron along {axis}-axis\")\n", + " if \"Rot\" in axis:\n", + " logger.info(f\"Moving to {position} micro rad. along {axis}-axis\")\n", + " else:\n", + " logger.info(f\"Moving to {position} micron along {axis}-axis\")\n", " await move_axis(logger, axis, position)\n", " await wait_m2_to_settle(logger)\n", "\n", @@ -626,7 +613,10 @@ "):\n", " t = 120 # timeout time in sec\n", "\n", - " max_step = 200 # Conservative value to avoid Tangent Force Error fault.\n", + " if \"Rot\" in axis:\n", + " max_step = 150 # Conservative value to avoid Tangent Force Error fault.\n", + " else:\n", + " max_step = 200 # Conservative value to avoid Tangent Force Error fault.\n", "\n", " task1 = asyncio.create_task(timeout(t), name=\"Timeout\")\n", " task2 = asyncio.create_task(start_m2_move(), name=\"M2 movement\")\n", @@ -655,6 +645,12 @@ " current_position = m2.tel_position.get().y\n", " case \"z\":\n", " current_position = m2.tel_position.get().z\n", + " case \"xRot\":\n", + " current_position = m2.tel_position.get().xRot\n", + " case \"yRot\":\n", + " current_position = m2.tel_position.get().yRot\n", + " case \"zRot\":\n", + " current_position = m2.tel_position.get().zRot\n", " case _:\n", " raise ValueError(\"Unrecognized axis\")\n", "\n", @@ -889,11 +885,11 @@ "# Setting the right LUT ***ONLY IN DISABLE STATE ***\n", "ccfile = m2.evt_config.get().get_vars()[\"configuration\"]\n", "\n", - "if \"surrogate_optical\" not in ccfile:\n", + "if \"M2_optical\" not in ccfile:\n", " config_files = (\n", " m2.evt_configurationFiles.get().get_vars()[\"files\"].split(\",\")\n", " )\n", - " ncfile = [el for el in config_files if \"surrogate_optical\" in el][0]\n", + " ncfile = [el for el in config_files if \"M2_optical\" in el][0]\n", " await m2.cmd_setConfigurationFile.set_start(file=ncfile)\n", " logger.info(\n", " f'Current config file (switch to enable state to make the change effective): {m2.evt_config.get().get_vars()[\"configuration\"]}'\n", @@ -1110,9 +1106,9 @@ ], "metadata": { "kernelspec": { - "display_name": "LSST", + "display_name": "LSST-env", "language": "python", - "name": "lsst" + "name": "python3" }, "language_info": { "codemirror_mode": { @@ -1124,7 +1120,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.11.4" + "version": "3.11.7" } }, "nbformat": 4, From 3acc88fc9a2766a8c7d99b9c1b603d6d584524b7 Mon Sep 17 00:00:00 2001 From: Luca Rosignoli Date: Fri, 19 Jul 2024 14:50:45 +0200 Subject: [PATCH 4/4] update RBM testscript (change on rotations units from microrad to arcsec) --- ...ity_Timeout_motion_limit_small_steps.ipynb | 76 +++++++++---------- 1 file changed, 38 insertions(+), 38 deletions(-) diff --git a/notebooks/tel_and_site/subsys_req_ver/m2/m2_functional_verification/LVV-T1782_M2_RMP_functionality_Timeout_motion_limit_small_steps.ipynb b/notebooks/tel_and_site/subsys_req_ver/m2/m2_functional_verification/LVV-T1782_M2_RMP_functionality_Timeout_motion_limit_small_steps.ipynb index b618edad..95aeb535 100644 --- a/notebooks/tel_and_site/subsys_req_ver/m2/m2_functional_verification/LVV-T1782_M2_RMP_functionality_Timeout_motion_limit_small_steps.ipynb +++ b/notebooks/tel_and_site/subsys_req_ver/m2/m2_functional_verification/LVV-T1782_M2_RMP_functionality_Timeout_motion_limit_small_steps.ipynb @@ -486,7 +486,7 @@ " logger: logging.Logger, axis: str = None, position: float = 0.0\n", "):\n", " if \"Rot\" in axis:\n", - " logger.info(f\"Moving to {position} micro rad. along {axis}-axis\")\n", + " logger.info(f\"Moving to {position} arcsec along {axis}-axis\")\n", " else:\n", " logger.info(f\"Moving to {position} micron along {axis}-axis\")\n", " await move_axis(logger, axis, position)\n", @@ -509,12 +509,12 @@ " f\"\\nWeight tangent force error(<2000): {weight_tangent_force_error[max_id]:.2f}\"\n", " f\"\\nMax measured axial force(<489): {[round(el, 2) for el in axial_force[max_id]]}\"\n", " f\"\\nM2 LAST POSITION:\"\n", - " f\"\\nX: {m2.tel_position.get().x:.2f}\"\n", - " f\"\\nY: {m2.tel_position.get().y:.2f}\"\n", - " f\"\\nZ: {m2.tel_position.get().z:.2f}\"\n", - " f\"\\nXROT: {m2.tel_position.get().xRot:.2f}\"\n", - " f\"\\nYROT: {m2.tel_position.get().yRot:.2f}\"\n", - " f\"\\nZROT: {m2.tel_position.get().zRot:.2f}\"\n", + " f\"\\nX: {m2.tel_position.get().x:.2f}, $\\mu$m\"\n", + " f\"\\nY: {m2.tel_position.get().y:.2f}, $\\mu$m\"\n", + " f\"\\nZ: {m2.tel_position.get().z:.2f}, $\\mu$m\"\n", + " f\"\\nXROT: {m2.tel_position.get().xRot:.2f}, arcsec\"\n", + " f\"\\nYROT: {m2.tel_position.get().yRot:.2f}, arcsec\"\n", + " f\"\\nZROT: {m2.tel_position.get().zRot:.2f}, arcsec\"\n", " )\n", "\n", " if limit_reach:\n", @@ -614,7 +614,7 @@ " t = 120 # timeout time in sec\n", "\n", " if \"Rot\" in axis:\n", - " max_step = 150 # Conservative value to avoid Tangent Force Error fault.\n", + " max_step = 50 # Conservative value to avoid Tangent Force Error fault.\n", " else:\n", " max_step = 200 # Conservative value to avoid Tangent Force Error fault.\n", "\n", @@ -972,12 +972,12 @@ "log = (\n", " f\"** STARTING MOVING ON {axis}-axis**\"\n", " f\"\\nM2 POSITION:\"\n", - " f\"\\nX: {m2.tel_position.get().x:.2f}\"\n", - " f\"\\nY: {m2.tel_position.get().y:.2f}\"\n", - " f\"\\nZ: {m2.tel_position.get().z:.2f}\"\n", - " f\"\\nXROT: {m2.tel_position.get().xRot:.2f}\"\n", - " f\"\\nYROT: {m2.tel_position.get().yRot:.2f}\"\n", - " f\"\\nZROT: {m2.tel_position.get().zRot:.2f}\"\n", + " f\"\\nX: {m2.tel_position.get().x:.2f}, $\\mu$m\"\n", + " f\"\\nY: {m2.tel_position.get().y:.2f}, $\\mu$m\"\n", + " f\"\\nZ: {m2.tel_position.get().z:.2f}, $\\mu$m\"\n", + " f\"\\nXROT: {m2.tel_position.get().xRot:.2f}, arcsec\"\n", + " f\"\\nYROT: {m2.tel_position.get().yRot:.2f}, arcsec\"\n", + " f\"\\nZROT: {m2.tel_position.get().zRot:.2f}, arcsec\"\n", ")\n", "\n", "logger.info(log)\n", @@ -1001,12 +1001,12 @@ "log = (\n", " f\"** STARTING MOVING ON {axis}-axis**\"\n", " f\"\\nM2 POSITION:\"\n", - " f\"\\nX: {m2.tel_position.get().x:.2f}\"\n", - " f\"\\nY: {m2.tel_position.get().y:.2f}\"\n", - " f\"\\nZ: {m2.tel_position.get().z:.2f}\"\n", - " f\"\\nXROT: {m2.tel_position.get().xRot:.2f}\"\n", - " f\"\\nYROT: {m2.tel_position.get().yRot:.2f}\"\n", - " f\"\\nZROT: {m2.tel_position.get().zRot:.2f}\"\n", + " f\"\\nX: {m2.tel_position.get().x:.2f}, $\\mu$m\"\n", + " f\"\\nY: {m2.tel_position.get().y:.2f}, $\\mu$m\"\n", + " f\"\\nZ: {m2.tel_position.get().z:.2f}, $\\mu$m\"\n", + " f\"\\nXROT: {m2.tel_position.get().xRot:.2f}, arcsec\"\n", + " f\"\\nYROT: {m2.tel_position.get().yRot:.2f}, arcsec\"\n", + " f\"\\nZROT: {m2.tel_position.get().zRot:.2f}, arcsec\"\n", ")\n", "\n", "logger.info(log)\n", @@ -1030,12 +1030,12 @@ "log = (\n", " f\"** STARTING MOVING ON {axis}-axis**\"\n", " f\"\\nM2 POSITION:\"\n", - " f\"\\nX: {m2.tel_position.get().x:.2f}\"\n", - " f\"\\nY: {m2.tel_position.get().y:.2f}\"\n", - " f\"\\nZ: {m2.tel_position.get().z:.2f}\"\n", - " f\"\\nXROT: {m2.tel_position.get().xRot:.2f}\"\n", - " f\"\\nYROT: {m2.tel_position.get().yRot:.2f}\"\n", - " f\"\\nZROT: {m2.tel_position.get().zRot:.2f}\"\n", + " f\"\\nX: {m2.tel_position.get().x:.2f}, $\\mu$m\"\n", + " f\"\\nY: {m2.tel_position.get().y:.2f}, $\\mu$m\"\n", + " f\"\\nZ: {m2.tel_position.get().z:.2f}, $\\mu$m\"\n", + " f\"\\nXROT: {m2.tel_position.get().xRot:.2f}, arcsec\"\n", + " f\"\\nYROT: {m2.tel_position.get().yRot:.2f}, arcsec\"\n", + " f\"\\nZROT: {m2.tel_position.get().zRot:.2f}, arcsec\"\n", ")\n", "\n", "logger.info(log)\n", @@ -1059,12 +1059,12 @@ "log = (\n", " f\"** STARTING MOVING ON {axis}-axis**\"\n", " f\"\\nM2 POSITION:\"\n", - " f\"\\nX: {m2.tel_position.get().x:.2f}\"\n", - " f\"\\nY: {m2.tel_position.get().y:.2f}\"\n", - " f\"\\nZ: {m2.tel_position.get().z:.2f}\"\n", - " f\"\\nXROT: {m2.tel_position.get().xRot:.2f}\"\n", - " f\"\\nYROT: {m2.tel_position.get().yRot:.2f}\"\n", - " f\"\\nZROT: {m2.tel_position.get().zRot:.2f}\"\n", + " f\"\\nX: {m2.tel_position.get().x:.2f}, $\\mu$m\"\n", + " f\"\\nY: {m2.tel_position.get().y:.2f}, $\\mu$m\"\n", + " f\"\\nZ: {m2.tel_position.get().z:.2f}, $\\mu$m\"\n", + " f\"\\nXROT: {m2.tel_position.get().xRot:.2f}, arcsec\"\n", + " f\"\\nYROT: {m2.tel_position.get().yRot:.2f}, arcsec\"\n", + " f\"\\nZROT: {m2.tel_position.get().zRot:.2f}, arcsec\"\n", ")\n", "\n", "logger.info(log)\n", @@ -1088,12 +1088,12 @@ "log = (\n", " f\"** STARTING MOVING ON {axis}-axis**\"\n", " f\"\\nM2 POSITION:\"\n", - " f\"\\nX: {m2.tel_position.get().x:.2f}\"\n", - " f\"\\nY: {m2.tel_position.get().y:.2f}\"\n", - " f\"\\nZ: {m2.tel_position.get().z:.2f}\"\n", - " f\"\\nXROT: {m2.tel_position.get().xRot:.2f}\"\n", - " f\"\\nYROT: {m2.tel_position.get().yRot:.2f}\"\n", - " f\"\\nZROT: {m2.tel_position.get().zRot:.2f}\"\n", + " f\"\\nX: {m2.tel_position.get().x:.2f}, $\\mu$m\"\n", + " f\"\\nY: {m2.tel_position.get().y:.2f}, $\\mu$m\"\n", + " f\"\\nZ: {m2.tel_position.get().z:.2f}, $\\mu$m\"\n", + " f\"\\nXROT: {m2.tel_position.get().xRot:.2f}, arcsec\"\n", + " f\"\\nYROT: {m2.tel_position.get().yRot:.2f}, arcsec\"\n", + " f\"\\nZROT: {m2.tel_position.get().zRot:.2f}, arcsec\"\n", ")\n", "\n", "logger.info(log)\n",