{ "cells": [ { "cell_type": "markdown", "id": "5da97dab", "metadata": {}, "source": [ "# HFSS-Mechanical MRI analysis\n", "\n", "This example uses a coil tuned to 63.8 MHz to determine the temperature\n", "rise in a gel phantom near an implant given a background SAR of 1 W/kg.\n", "\n", "Here is the workflow:" ] }, { "cell_type": "markdown", "id": "93b10518", "metadata": {}, "source": [ "Step 1: Simulate the coil loaded by the empty phantom:\n", "Scale input to coil ports to produce desired background SAR of 1 W/kg at the location\n", "that is to contain the implant." ] }, { "cell_type": "markdown", "id": "98343d48", "metadata": {}, "source": [ "Step 2: Simulate the coil loaded by the phantom containing the implant in the proper location:\n", "View SAR in the tissue surrounding implant." ] }, { "cell_type": "markdown", "id": "444ad6af", "metadata": {}, "source": [ "Step 3: Thermal simulation:\n", "Link HFSS to the transient thermal solver to find the temperature rise in the tissue near the implant\n", "versus the time.\n", "\n", "Keywords: **multiphysics**, **HFSS**, **Mechanical AEDT**, **Circuit**." ] }, { "cell_type": "markdown", "id": "c89feeb5", "metadata": {}, "source": [ "## Perform imports and define constants\n", "Perform required imports." ] }, { "cell_type": "code", "execution_count": null, "id": "51f2bb5e", "metadata": {}, "outputs": [], "source": [ "import os.path\n", "import tempfile\n", "import time\n", "\n", "from ansys.aedt.core import Hfss, Icepak, Mechanical, downloads\n" ] }, { "cell_type": "markdown", "id": "665c38b6", "metadata": {}, "source": [ "Define constants." ] }, { "cell_type": "code", "execution_count": null, "id": "06281afd", "metadata": { "lines_to_next_cell": 2 }, "outputs": [], "source": [ "AEDT_VERSION = \"2024.2\"\n", "NUM_CORES = 4\n", "NG_MODE = False # Open AEDT UI when it is launched." ] }, { "cell_type": "markdown", "id": "ed53c1ef", "metadata": {}, "source": [ "## Create temporary directory\n", "\n", "Create a temporary directory where downloaded data or\n", "dumped data can be stored.\n", "If you'd like to retrieve the project data for subsequent use,\n", "the temporary folder name is given by ``temp_folder.name``." ] }, { "cell_type": "code", "execution_count": null, "id": "8bf2a29c", "metadata": { "lines_to_next_cell": 2 }, "outputs": [], "source": [ "temp_folder = tempfile.TemporaryDirectory(suffix=\".ansys\")" ] }, { "cell_type": "markdown", "id": "d433e2d9", "metadata": {}, "source": [ "## Load project\n", "\n", "Open AEDT and the ``background_SAR.aedt`` project. This project\n", "contains the phantom and airbox. The phantom consists of two objects: ``phantom`` and ``implant_box``.\n", "\n", "Separate objects are used to selectively assign mesh operations.\n", "Material properties defined in this project already contain electrical and thermal properties." ] }, { "cell_type": "code", "execution_count": null, "id": "ef005db9", "metadata": {}, "outputs": [], "source": [ "project_path = downloads.download_file(source=\"mri\", destination=temp_folder.name)\n", "project_name = os.path.join(project_path, \"background_SAR.aedt\")\n", "hfss = Hfss(\n", " project=project_name,\n", " version=AEDT_VERSION,\n", " non_graphical=NG_MODE,\n", " new_desktop=True,\n", ")" ] }, { "cell_type": "markdown", "id": "0675c2be", "metadata": {}, "source": [ "## Insert 3D component\n", "\n", "The MRI coil is saved as a separate 3D component." ] }, { "cell_type": "markdown", "id": "9e3c7f33", "metadata": {}, "source": [ "‒ 3D components store geometry (including parameters),\n", "material properties, boundary conditions, mesh assignments,\n", "and excitations.\n", "‒ 3D components make it easy to reuse and share parts of a simulation." ] }, { "cell_type": "code", "execution_count": null, "id": "727f61b8", "metadata": {}, "outputs": [], "source": [ "component_file = os.path.join(project_path, \"coil.a3dcomp\")\n", "hfss.modeler.insert_3d_component(input_file=component_file)" ] }, { "cell_type": "markdown", "id": "864e01d4", "metadata": {}, "source": [ "## Define convergence criteria\n", "\n", " On the **Expression Cache** tab, define additional convergence criteria for self\n", " impedance of the four coil ports." ] }, { "cell_type": "markdown", "id": "3e0dd58d", "metadata": {}, "source": [ "Set each of these convergence criteria to 2.5 ohm.\n", "This example limits the number of passes to two to reduce simulation time." ] }, { "cell_type": "code", "execution_count": null, "id": "044ee0a4", "metadata": {}, "outputs": [], "source": [ "im_traces = hfss.get_traces_for_plot(\n", " get_mutual_terms=False, category=\"im(Z\", first_element_filter=\"Coil1_p*\"\n", ")\n", "\n", "hfss.setups[0].enable_expression_cache(\n", " report_type=\"Modal Solution Data\",\n", " expressions=im_traces,\n", " isconvergence=True,\n", " isrelativeconvergence=False,\n", " conv_criteria=2.5,\n", " use_cache_for_freq=False,\n", ")\n", "hfss.setups[0].props[\"MaximumPasses\"] = 1" ] }, { "cell_type": "markdown", "id": "6b8219bd", "metadata": {}, "source": [ "## Edit sources\n", "\n", "The 3D component of the MRI coil contains all the ports,\n", "but the sources for these ports are not yet defined.\n", "Browse to and select the ``sources.csv`` file.\n", "The sources in this file were determined by tuning the coil at 63.8 MHz.\n", "Notice that ``input_scale`` is a multiplier that lets you quickly adjust the coil excitation power." ] }, { "cell_type": "code", "execution_count": null, "id": "dc12be6d", "metadata": {}, "outputs": [], "source": [ "hfss.edit_sources_from_file(os.path.join(project_path, \"sources.csv\"))" ] }, { "cell_type": "markdown", "id": "a45ed572", "metadata": {}, "source": [ "## Run simulation\n", "\n", "Save and analyze the project." ] }, { "cell_type": "code", "execution_count": null, "id": "ee1ac40e", "metadata": {}, "outputs": [], "source": [ "hfss.save_project(file_name=os.path.join(project_path, \"solved.aedt\"))\n", "hfss.analyze(cores=NUM_CORES)" ] }, { "cell_type": "markdown", "id": "94801d56", "metadata": {}, "source": [ "## Plot SAR on cut plane in phantom\n", "\n", "Ensure that the SAR averaging method is set to ``Gridless``.\n", "Plot ``Average_SAR`` on the global YZ plane.\n", "Draw ``Point1`` at the origin of the implant coordinate system." ] }, { "cell_type": "code", "execution_count": null, "id": "587b3470", "metadata": {}, "outputs": [], "source": [ "hfss.sar_setup(\n", " assignment=-1,\n", " average_sar_method=1,\n", " tissue_mass=1,\n", " material_density=1,\n", ")\n", "hfss.post.create_fieldplot_cutplane(\n", " assignment=[\"implant:YZ\"], quantity=\"Average_SAR\", filter_objects=[\"implant_box\"]\n", ")\n", "\n", "hfss.modeler.set_working_coordinate_system(\"implant\")\n", "hfss.modeler.create_point(position=[0, 0, 0], name=\"Point1\")\n", "\n", "plot = hfss.post.plot_field(\n", " quantity=\"Average_SAR\",\n", " assignment=\"implant:YZ\",\n", " plot_type=\"CutPlane\",\n", " show_legend=False,\n", " filter_objects=[\"implant_box\"],\n", " export_path=hfss.working_directory,\n", " show=False,\n", ")" ] }, { "cell_type": "markdown", "id": "bf3f1ed9", "metadata": {}, "source": [ "## Adjust input Power to MRI coil\n", "\n", "Adjust the MRI coil’s input power so that the average SAR at ``Point1`` is 1 W/kg.\n", "Note that the SAR and input power are linearly related.\n", "\n", "To determine therequired input, calculate\n", "``input_scale = 1/AverageSAR`` at ``Point1``." ] }, { "cell_type": "code", "execution_count": null, "id": "727c5886", "metadata": {}, "outputs": [], "source": [ "sol_data = hfss.post.get_solution_data(\n", " expressions=\"Average_SAR\",\n", " primary_sweep_variable=\"Freq\",\n", " context=\"Point1\",\n", " report_category=\"Fields\",\n", ")\n", "sol_data.data_real()\n", "\n", "hfss[\"input_scale\"] = 1 / sol_data.data_real()[0]" ] }, { "cell_type": "markdown", "id": "a5c48eb5", "metadata": {}, "source": [ "## Analyze phantom with implant\n", "\n", "Import the implant geometry.\n", "Subtract the rod from the implant box.\n", "Assign titanium to the imported object rod.\n", "Analyze the project." ] }, { "cell_type": "code", "execution_count": null, "id": "8af41dad", "metadata": {}, "outputs": [], "source": [ "hfss.modeler.import_3d_cad(os.path.join(project_path, \"implant_rod.sat\"))\n", "\n", "hfss.modeler[\"implant_box\"].subtract(tool_list=\"rod\", keep_originals=True)\n", "hfss.modeler[\"rod\"].material_name = \"titanium\"\n", "hfss.analyze(cores=NUM_CORES)\n", "hfss.save_project()" ] }, { "cell_type": "markdown", "id": "35579517", "metadata": {}, "source": [ "## Run a thermal simulation\n", "\n", "Initialize a new Mechanical transient thermal analysis.\n", "This type of analysis is available in AEDT in 2023 R2 and later as a beta feature." ] }, { "cell_type": "code", "execution_count": null, "id": "a7e75560", "metadata": {}, "outputs": [], "source": [ "mech = Mechanical(solution_type=\"Transient Thermal\", version=AEDT_VERSION)" ] }, { "cell_type": "markdown", "id": "67ab36c8", "metadata": {}, "source": [ "## Copy geometries\n", "\n", "Copy bodies from the HFSS project. The 3D component is not copied." ] }, { "cell_type": "code", "execution_count": null, "id": "9d938c68", "metadata": {}, "outputs": [], "source": [ "mech.copy_solid_bodies_from(hfss)" ] }, { "cell_type": "markdown", "id": "b35aa317", "metadata": {}, "source": [ "## Link sources to EM losses\n", "\n", "Link sources to the EM losses.\n", "Assign external convection." ] }, { "cell_type": "code", "execution_count": null, "id": "a96c25dd", "metadata": {}, "outputs": [], "source": [ "exc = mech.assign_em_losses(\n", " design=hfss.design_name,\n", " setup=hfss.setups[0].name,\n", " sweep=\"LastAdaptive\",\n", " map_frequency=hfss.setups[0].props[\"Frequency\"],\n", " surface_objects=mech.get_all_conductors_names(),\n", ")\n", "mech.assign_uniform_convection(\n", " assignment=mech.modeler[\"Region\"].faces, convection_value=1\n", ")" ] }, { "cell_type": "markdown", "id": "cb4d2941", "metadata": {}, "source": [ "## Create setup\n", "\n", "Create a setup and edit properties." ] }, { "cell_type": "code", "execution_count": null, "id": "59599caf", "metadata": {}, "outputs": [], "source": [ "setup = mech.create_setup()\n", "# setup.add_mesh_link(\"backgroundSAR\")\n", "# mech.create_dataset1d_design(\"PowerMap\", [0, 239, 240, 360], [1, 1, 0, 0])\n", "# exc.props[\"LossMultiplier\"] = \"pwl(PowerMap,Time)\"\n", "\n", "mech.modeler.set_working_coordinate_system(\"implant\")\n", "mech.modeler.create_point(position=[0, 0, 0], name=\"Point1\")\n", "setup.props[\"Stop Time\"] = 30\n", "setup.props[\"Time Step\"] = \"10s\"\n", "setup.props[\"SaveFieldsType\"] = \"Every N Steps\"\n", "setup.props[\"N Steps\"] = \"2\"" ] }, { "cell_type": "markdown", "id": "5400ee6c", "metadata": {}, "source": [ "## Analyze project\n", "\n", "Analyze the Mechanical project." ] }, { "cell_type": "code", "execution_count": null, "id": "fb63908f", "metadata": {}, "outputs": [], "source": [ "mech.analyze(cores=NUM_CORES)" ] }, { "cell_type": "markdown", "id": "6b139172", "metadata": {}, "source": [ "## Plot fields\n", "\n", "Plot the temperature on cut plane.\n", "Plot the temperature on the point." ] }, { "cell_type": "code", "execution_count": null, "id": "53dce997", "metadata": {}, "outputs": [], "source": [ "mech.post.create_fieldplot_cutplane(\n", " assignment=[\"implant:YZ\"],\n", " quantity=\"Temperature\",\n", " filter_objects=[\"implant_box\"],\n", " intrinsics={\"Time\": \"10s\"},\n", ")\n", "mech.save_project()\n", "\n", "data = mech.post.get_solution_data(\n", " expressions=\"Temperature\",\n", " primary_sweep_variable=\"Time\",\n", " context=\"Point1\",\n", " report_category=\"Fields\",\n", ")\n", "data.plot()\n", "\n", "mech.post.plot_animated_field(\n", " quantity=\"Temperature\",\n", " assignment=\"implant:YZ\",\n", " plot_type=\"CutPlane\",\n", " intrinsics={\"Time\": \"10s\"},\n", " variation_variable=\"Time\",\n", " variations=[\"10s\", \"30s\"],\n", " filter_objects=[\"implant_box\"],\n", " show=False,\n", ")" ] }, { "cell_type": "markdown", "id": "bba4216c", "metadata": {}, "source": [ "## Run a new thermal simulation\n", "\n", "Initialize a new Icepak transient thermal analysis." ] }, { "cell_type": "code", "execution_count": null, "id": "703ac726", "metadata": {}, "outputs": [], "source": [ "ipk = Icepak(solution_type=\"Transient\", version=AEDT_VERSION)\n", "ipk.design_solutions.problem_type = \"TemperatureOnly\"" ] }, { "cell_type": "markdown", "id": "e31eea62", "metadata": {}, "source": [ "## Copy geometries\n", "\n", "Copy bodies from the HFSS project. The 3D component is not copied." ] }, { "cell_type": "code", "execution_count": null, "id": "e49bd7be", "metadata": {}, "outputs": [], "source": [ "ipk.modeler.delete(\"Region\")\n", "ipk.copy_solid_bodies_from(hfss)" ] }, { "cell_type": "markdown", "id": "c68fb534", "metadata": {}, "source": [ "## Link sources to EM losses\n", "\n", "Link sources to the EM losses.\n", "Assign external convection." ] }, { "cell_type": "code", "execution_count": null, "id": "1f536e2f", "metadata": {}, "outputs": [], "source": [ "ipk.assign_em_losses(\n", " design=hfss.design_name,\n", " setup=hfss.setups[0].name,\n", " sweep=\"LastAdaptive\",\n", " map_frequency=hfss.setups[0].props[\"Frequency\"],\n", " surface_objects=ipk.get_all_conductors_names(),\n", ")" ] }, { "cell_type": "markdown", "id": "9431166e", "metadata": {}, "source": [ "## Create setup\n", "\n", "Create a setup and edit properties.\n", "Simulation takes 30 seconds." ] }, { "cell_type": "code", "execution_count": null, "id": "7142d80a", "metadata": {}, "outputs": [], "source": [ "setup = ipk.create_setup()\n", "\n", "setup.props[\"Stop Time\"] = 30\n", "setup.props[\"N Steps\"] = 2\n", "setup.props[\"Time Step\"] = 5\n", "setup.props[\"Convergence Criteria - Energy\"] = 1e-12" ] }, { "cell_type": "markdown", "id": "d5927ba2", "metadata": {}, "source": [ "## Create mesh region\n", "\n", "Create a mesh region and change the accuracy level to 4." ] }, { "cell_type": "code", "execution_count": null, "id": "dc33a5a6", "metadata": {}, "outputs": [], "source": [ "bound = ipk.modeler[\"implant_box\"].bounding_box\n", "mesh_box = ipk.modeler.create_box(\n", " origin=bound[:3],\n", " sizes=[bound[3] - bound[0], bound[4] - bound[1], bound[5] - bound[2]],\n", ")\n", "mesh_box.model = False\n", "mesh_region = ipk.mesh.assign_mesh_region([mesh_box.name])\n", "mesh_region.UserSpecifiedSettings = False\n", "mesh_region.Level = 4\n", "mesh_region.update()" ] }, { "cell_type": "markdown", "id": "c79ab85f", "metadata": {}, "source": [ "## Create point monitor\n", "\n", "Create a point monitor." ] }, { "cell_type": "code", "execution_count": null, "id": "906c7c65", "metadata": {}, "outputs": [], "source": [ "ipk.modeler.set_working_coordinate_system(\"implant\")\n", "ipk.monitor.assign_point_monitor(point_position=[0, 0, 0], monitor_name=\"Point1\")\n", "ipk.assign_openings(ipk.modeler[\"Region\"].top_face_z)\n", "# -" ] }, { "cell_type": "markdown", "id": "2aaf7484", "metadata": {}, "source": [ "## Release AEDT\n", "\n", "Release AEDT and close the example." ] }, { "cell_type": "code", "execution_count": null, "id": "8271c17b", "metadata": {}, "outputs": [], "source": [ "hfss.save_project()\n", "hfss.release_desktop()\n", "# Wait 3 seconds to allow AEDT to shut down before cleaning the temporary directory.\n", "time.sleep(3)" ] }, { "cell_type": "markdown", "id": "90bdd279", "metadata": {}, "source": [ "## Clean up\n", "\n", "All project files are saved in the folder ``temp_folder.name``. If you've run this example as a Jupyter notebook, you\n", "can retrieve those project files. The following cell removes all temporary files, including the project folder." ] }, { "cell_type": "code", "execution_count": null, "id": "3687a447", "metadata": {}, "outputs": [], "source": [ "temp_folder.cleanup()" ] } ], "metadata": { "jupytext": { "cell_metadata_filter": "-all", "main_language": "python", "notebook_metadata_filter": "-all" } }, "nbformat": 4, "nbformat_minor": 5 }