.. DO NOT EDIT. .. THIS FILE WAS AUTOMATICALLY GENERATED BY SPHINX-GALLERY. .. TO MAKE CHANGES, EDIT THE SOURCE PYTHON FILE: .. "examples\q3d-lumerical\wf_q3l_01_ion_trap_modelling.py" .. LINE NUMBERS ARE GIVEN BELOW. .. only:: html .. note:: :class: sphx-glr-download-link-note :ref:`Go to the end ` to download the full example code. .. rst-class:: sphx-glr-example-title .. _sphx_glr_examples_q3d-lumerical_wf_q3l_01_ion_trap_modelling.py: .. _ref_wf_ml_01_ion_trap_modelling: Q3D - Simplified ion trap modeling ################################## Generate a 3D BEM model of a 3-rail electrode Ion Trap to identify the nodal point and then optimize the grating coupler design with PyAnsys (PyAEDT and PyLumerical). Problem description ------------------- Surface electrodes adjacent to grating couplers render an integrated ion trap. In this workflow surface electrodes are modeled using ANSYS Q3D and grating couplers using ANSYS Lumerical. ANSYS Q3D CG solver, based on the boundary element method, allows to evaluate the ion trap height.Then the coordinates of the ion trap are passed to an optimization algorithm to define the optimal two-dimensional grating coupler design, which will focus the laser beam at the ion trap heigth. The workflow is explained in detail in this article: https://optics.ansys.com/hc/en-us/articles/20715978394131-Integrated-Ion-Traps-using-Surface-Electrodes-and-Grating-Couplers The workflow consists of these steps: - Set up the Q3D parametric model. - Identify the electric field node point for each design point. - Export the node coordinates for the subsequent Lumerical step. - Launch the Lumerical scripts. .. GENERATED FROM PYTHON SOURCE LINES 51-66 .. code-block:: Python # Perform required imports # ------------------------ import os from pathlib import Path import shutil import tempfile import time from PIL import Image from ansys.aedt.core import Q3d from ansys.lumerical.core import FDTD .. GENERATED FROM PYTHON SOURCE LINES 73-76 Prepare and launch Q3D ---------------------- Define constants. .. GENERATED FROM PYTHON SOURCE LINES 76-105 .. code-block:: Python AEDT_VERSION = os.getenv("AEDT_VERSION", "2025.2") # Set your AEDT version here NUM_CORES = 4 NG_MODE = ( os.getenv("ON_CI", "false").lower() == "true" ) # Open AEDT UI when it is launched, unless running in CI NODE_FILENAME = "NodePositionTable.tab" LEGEND_FILENAME = "legend.txt" PARENT_DIR_PATH = Path(__file__).parent.absolute() # Create temporary directory. temp_folder = tempfile.TemporaryDirectory(suffix=".ansys") lumerical_script_folder = Path(temp_folder.name) # / "lumerical_scripts" node_path = lumerical_script_folder / NODE_FILENAME legend_path = lumerical_script_folder / LEGEND_FILENAME # Launch AEDT and start a Maxwell 2D design. project_name = os.path.join(temp_folder.name, "IonTrapQ3D.aedt") q3d = Q3d( project=project_name, design="01_Q3D_IonTrap_3rails", version=AEDT_VERSION, non_graphical=NG_MODE, new_desktop=True, ) q3d.modeler.model_units = "um" .. rst-class:: sphx-glr-script-out .. code-block:: none PyAEDT INFO: Python version 3.12.7 (tags/v3.12.7:0b05ead, Oct 1 2024, 03:06:41) [MSC v.1941 64 bit (AMD64)]. PyAEDT INFO: PyAEDT version 0.19.0. PyAEDT INFO: Initializing new Desktop session. PyAEDT INFO: Log on console is enabled. PyAEDT INFO: Log on file C:\Users\ansys\AppData\Local\Temp\pyaedt_ansys_7bdcbb94-cef8-4059-8bb8-84aa4894fbe4.log is enabled. PyAEDT INFO: Log on AEDT is disabled. PyAEDT INFO: Debug logger is disabled. PyAEDT methods will not be logged. PyAEDT INFO: Launching PyAEDT with gRPC plugin. PyAEDT INFO: New AEDT session is starting on gRPC port 57289. PyAEDT INFO: Electronics Desktop started on gRPC port: 57289 after 28.89408826828003 seconds. PyAEDT INFO: AEDT installation Path C:\Program Files\ANSYS Inc\v252\AnsysEM PyAEDT INFO: Ansoft.ElectronicsDesktop.2025.2 version started with process ID 3064. PyAEDT INFO: Project IonTrapQ3D has been created. PyAEDT INFO: Added design '01_Q3D_IonTrap_3rails' of type Q3D Extractor. PyAEDT INFO: Aedt Objects correctly read PyAEDT INFO: Modeler class has been initialized! Elapsed time: 0m 0sec .. GENERATED FROM PYTHON SOURCE LINES 106-113 Preprocess ---------- The preprocessing is performed using the following steps: 1. Define design variables. 2. Create design geometry. 3. Define excitations. 4. (Optional) Define mesh settings. .. GENERATED FROM PYTHON SOURCE LINES 113-253 .. code-block:: Python # Initialize dictionaries for design variables. geom_params = { "div": str(73 / 41), "w_rf": "41um", "w_dc": "41um*div", "w_cut": "4um", "metal_thickness": "1um", "offset_glass": "50um", "glass_thickness": "10um", "x_dummy": "2um", "y_dummy": "300um", "Z_length": "300um", } # Define design variables from dictionaries for k, v in geom_params.items(): q3d[k] = v # Create design geometry dc = q3d.modeler.create_rectangle( orientation="XY", origin=["-w_dc/2", "-metal_thickness/2", "0"], sizes=["w_dc", "metal_thickness"], name="DC", material="aluminum", ) # dc.color = (0, 0, 255) # rgb gnd = q3d.modeler.create_rectangle( orientation="XY", origin=["-(w_dc/2+w_cut+w_rf+offset_glass)", "-(metal_thickness/2+glass_thickness)", "0"], sizes=["2*(w_dc/2+w_cut+w_rf+offset_glass)", "-metal_thickness"], name="gnd", material="aluminum", ) rf = q3d.modeler.create_rectangle( orientation="XY", origin=["-(w_dc/2+w_cut+w_rf)", "-metal_thickness/2", "0"], sizes=["w_rf", "metal_thickness"], name="RF", material="aluminum", ) sub_glass = q3d.modeler.create_rectangle( orientation="XY", origin=["-(w_dc/2+w_cut+w_rf+offset_glass)", "-metal_thickness/2", "0"], sizes=["2*(w_dc/2+w_cut+w_rf+offset_glass)", "-glass_thickness"], name="substrate_glass", material="glass", ) ins = q3d.modeler.create_rectangle( orientation="XY", origin=["-(w_dc/2+w_cut)", "-metal_thickness/2", "0"], sizes=["w_cut", "metal_thickness"], name="ins", material="vacuum", ) # Create dummy objects for mesh and center line for postprocessing and region dummy = q3d.modeler.create_rectangle( orientation="XY", origin=["0", "metal_thickness/2", "0"], sizes=["-x_dummy", "y_dummy"], name="dummy", material="vacuum", ) # Extrude in z-direction q3d.modeler.sweep_along_vector( assignment=q3d.modeler._get_model_objects(), sweep_vector=[0, 0, "Z_length"], draft_angle=0, draft_type="Round", ) # Create center line for post-processing center_line_length = 300 * 1e-6 # 300 um center_line_length_str = str(center_line_length * 1e6) # in um mid_center_line_length_str = str(0.5 * center_line_length * 1e6) # in um center_line = q3d.modeler.create_polyline( points=[ ["0", "metal_thickness/2", str(mid_center_line_length_str) + "um"], [ "0", "metal_thickness/2+" + center_line_length_str + "um", str(mid_center_line_length_str) + "um", ], ], name="center_line", ) # Define excitations q3d.auto_identify_nets() # Define mesh settings q3d.mesh.assign_initial_mesh(method="AnsoftClassic") # For good quality results, uncomment the following mesh operations lines # # q3d.mesh.assign_length_mesh(assignment=center_line.id, # maximum_length=1e-7, # maximum_elements=None, # name="center_line_0.1um") # q3d.mesh.assign_length_mesh(assignment=dummy.name, # maximum_length=2e-6, # maximum_elements=1e6, # name="dummy_2um") # q3d.mesh.assign_length_mesh(assignment=ins.id, # maximum_length=8e-7, # inside_selection=False, # maximum_elements=1e6, # name="ins_0.8um") # q3d.mesh.assign_length_mesh(assignment=[dc.id, rf.id], # maximum_length=5e-6, # inside_selection=False, # maximum_elements=1e6, # name="dc_5um") # q3d.mesh.assign_length_mesh(assignment=gnd.id, # maximum_length=1e-5, # inside_selection=False, # maximum_elements=1e6, # name="gnd_10um") # Duplicate structures and assignments to complete the model q3d.modeler.duplicate_and_mirror( assignment=[rf.id, dummy.id, ins.id], origin=["0", "0", "0"], vector=["-1", "0", "0"], duplicate_assignment=True, ) .. rst-class:: sphx-glr-script-out .. code-block:: none PyAEDT INFO: Materials class has been initialized! Elapsed time: 0m 0sec PyAEDT INFO: 3 Nets have been identified: DC, gnd, RF PyAEDT INFO: Mesh class has been initialized! Elapsed time: 0m 0sec PyAEDT INFO: Mesh class has been initialized! Elapsed time: 0m 0sec ['RF_1', 'dummy_1', 'ins_1'] .. GENERATED FROM PYTHON SOURCE LINES 254-257 Run simulation and parametric sweep ----------------------------------- Create, validate, and analyze setup .. GENERATED FROM PYTHON SOURCE LINES 257-287 .. code-block:: Python setup_name = "MySetupAuto" setup1 = q3d.create_setup(props={"Name": setup_name, "AdaptiveFreq": "1Hz", "SaveFields": True}) setup1.ac_rl_enabled = False setup1.dc_enabled = False setup1.update() q3d.validate_simple() q3d.analyze_setup(name=setup_name, use_auto_settings=False, cores=NUM_CORES) # Create and solve parametric sweep # Keeping w_rf constant, recompute the w_dc values from the desired ratios w_rf/w_dc div_sweep_start = 1.4 div_sweep_stop = 2 sweep = q3d.parametrics.add( variable="div", start_point=div_sweep_start, end_point=div_sweep_stop, step=0.2, variation_type="LinearStep", name="w_dc_sweep", ) add_points = [1, 1.3] [ sweep.add_variation(sweep_variable="div", start_point=p, variation_type="SingleValue") for p in add_points ] sweep["SaveFields"] = True sweep.analyze(cores=NUM_CORES) .. rst-class:: sphx-glr-script-out .. code-block:: none PyAEDT INFO: Key Desktop/ActiveDSOConfigurations/Q3D Extractor correctly changed. PyAEDT INFO: Solving design setup MySetupAuto PyAEDT INFO: Key Desktop/ActiveDSOConfigurations/Q3D Extractor correctly changed. PyAEDT INFO: Design setup MySetupAuto solved correctly in 0.0h 0.0m 15.0s PyAEDT INFO: Parsing C:\Users\ansys\AppData\Local\Temp\tmp6s9gkcs2.ansys\IonTrapQ3D.aedt. PyAEDT INFO: File C:\Users\ansys\AppData\Local\Temp\tmp6s9gkcs2.ansys\IonTrapQ3D.aedt correctly loaded. Elapsed time: 0m 0sec PyAEDT INFO: aedt file load time 0.0 PyAEDT INFO: Project IonTrapQ3D Saved correctly PyAEDT INFO: Key Desktop/ActiveDSOConfigurations/Q3D Extractor correctly changed. PyAEDT INFO: Solving Optimetrics PyAEDT INFO: Key Desktop/ActiveDSOConfigurations/Q3D Extractor correctly changed. PyAEDT INFO: Design setup w_dc_sweep solved correctly in 0.0h 1.0m 28.0s True .. GENERATED FROM PYTHON SOURCE LINES 288-295 Postprocess ----------- Create the Ey expression in the PyAEDT advanced field calculator. Due to the symmetric nature of this specific geometry, the electric field node will be located along the center line. The electric field node is the point where the Ey will be zero and can be found directly by Q3D postprocessing features. .. GENERATED FROM PYTHON SOURCE LINES 295-335 .. code-block:: Python # Edit sources to scale the solution for the actual assigned potentials. sources_cg = { "DC": ("0V", "0deg"), "gnd": ("0V", "0deg"), "RF": ("1V", "0deg"), "RF_1": ("1V", "0deg"), } q3d.edit_sources(sources_cg) # Evaluate the E- fieled on the control line and export nodal points line_name = "Line1" q3d.insert_em_field_line(assignment="center_line", points=1000, name=line_name) my_plots = q3d.post.create_report( expressions="re(EY)", primary_sweep_variable="NormalizedDistance", report_category="Static EM Fields", plot_type="Rectangular Plot", context=line_name, plot_name="my_plot", ) my_plots.edit_x_axis_scaling(min_scale="0.01", max_scale="1") my_plots.update_trace_in_report( my_plots.get_solution_data().expressions, variations={"div": ["All"]}, context=line_name ) # Identify the zero point for each trace my_plots.add_cartesian_y_marker("0") my_plots.add_trace_characteristics( "XAtYVal", arguments=["0"], solution_range=["Full", "0.01", "1.0"] ) # Export the points at which Ey=0 to a TXT file my_plots.edit_general_settings(use_scientific_notation=True) my_plots.export_table_to_file(my_plots.plot_name, str(node_path), "Legend") .. rst-class:: sphx-glr-script-out .. code-block:: none PyAEDT INFO: Field setup NearFieldLine Line1 has been created. PyAEDT INFO: Parsing C:\Users\ansys\AppData\Local\Temp\tmp6s9gkcs2.ansys\IonTrapQ3D.aedt. PyAEDT INFO: File C:\Users\ansys\AppData\Local\Temp\tmp6s9gkcs2.ansys\IonTrapQ3D.aedt correctly loaded. Elapsed time: 0m 0sec PyAEDT INFO: aedt file load time 0.0339655876159668 PyAEDT INFO: PostProcessor class has been initialized! Elapsed time: 0m 0sec PyAEDT INFO: Post class has been initialized! Elapsed time: 0m 0sec PyAEDT INFO: Solution Data Correctly Loaded. True .. GENERATED FROM PYTHON SOURCE LINES 336-339 Prepare and run Lumerical simulation ------------------------------------ Edit the file outputted by Q3D to be read in by Lumerical .. GENERATED FROM PYTHON SOURCE LINES 339-418 .. code-block:: Python new_line = [] with open(node_path, "r", encoding="utf-8") as f: lines = f.readlines() new_line.append(lines[0]) for line in lines[1:]: new_line.append(line.split("\t")[0]) new_line.append("\n" + line.split("\t")[1].lstrip()) with open(legend_path, "w", encoding="utf-8") as f: for line in new_line: f.write(line) # Copy Lumerical scripts and illustration to the local folder gc_farfield_path = shutil.copy(PARENT_DIR_PATH / "GC_farfield.lsf", lumerical_script_folder) gc_opt_path = shutil.copy(PARENT_DIR_PATH / "GC_Opt.lsf", lumerical_script_folder) read_data_path = shutil.copy(PARENT_DIR_PATH / "Readata.lsf", lumerical_script_folder) img_path = shutil.copy(PARENT_DIR_PATH / "img_001.jpg", lumerical_script_folder) # Start the Lumerical process gc_0 = FDTD(gc_opt_path) # Run the first script: build geometry and run optimization gc_1 = FDTD(read_data_path) print( "Optimize for the nodal point located", str(gc_1.getv("T5")), "um, above the linearly apodized grating coupler", ) # Run the optimized design gc_2 = FDTD(str(lumerical_script_folder / "Testsim_Intensity_best_solution")) gc_2.save(str(lumerical_script_folder / "GC_farfields_calc")) gc_2.run() # Run the second script for calculating plots gc_2.feval(gc_farfield_path) print(f"Target focal distance of output laser beam: {gc_2.getv('Mselect') * 1000000} (um)") print(f"Actual focal distance for the optimised geometry: {gc_2.getv('Mactual') * 1000000} (um)") print(f"Relative error: {gc_2.getv('RelVal') * 100}%") print(f"FWHM of vertical direction at focus: {gc_2.getv('FWHM_X') * 1000000} (um)") print(f"FWHM of horizontal direction at focus {gc_2.getv('FWHM_Y') * 1000000} (um)") print(f"Substrate material : {gc_2.getv('Material')}") print(f"Waveguide etch depth: {gc_2.getv('GC_etch') * 1000000000} (nm)") print(f"Grating period (P): {gc_2.getv('GC_period') * 1000000000} (nm)") print(f"Grating minimum duty cycle: {gc_2.getv('GC_DCmin')}") # Display grating schema image def in_ipython(): try: from IPython import get_ipython return get_ipython() is not None except ImportError: return False schema_img = Image.open(PARENT_DIR_PATH / "img_001.jpg") # Show image inside IPython / Jupyter if in_ipython(): from IPython.display import display display(schema_img) # Show image using default image viewer else: schema_img.show() schema_img.close() .. rst-class:: sphx-glr-script-out .. code-block:: none Optimize for the nodal point located 0.18505 um, above the linearly apodized grating coupler Target focal distance of output laser beam: 0.18505 (um) Actual focal distance for the optimised geometry: 1.0 (um) Relative error: 81.49499999999999% FWHM of vertical direction at focus: 0.0 (um) FWHM of horizontal direction at focus 18.37237237237237 (um) Substrate material : SiO2 (Glass) - Palik Waveguide etch depth: 287.86253840023056 (nm) Grating period (P): 826.703897786274 (nm) Grating minimum duty cycle: 0.31089792085150775 .. GENERATED FROM PYTHON SOURCE LINES 419-423 Exit the solver --------------- Close FDTD projects and release AEDT .. GENERATED FROM PYTHON SOURCE LINES 423-435 .. code-block:: Python gc_0.close() gc_1.close() gc_2.close() q3d.save_project() q3d.desktop_class.release_desktop() # Wait three seconds to allow AEDT to shut down before cleaning the temporary directory time.sleep(3) # Clean up the temporary folder temp_folder.cleanup() .. rst-class:: sphx-glr-script-out .. code-block:: none PyAEDT INFO: Project IonTrapQ3D Saved correctly PyAEDT INFO: Desktop has been released and closed. .. rst-class:: sphx-glr-timing **Total running time of the script:** (45 minutes 7.836 seconds) .. _sphx_glr_download_examples_q3d-lumerical_wf_q3l_01_ion_trap_modelling.py: .. only:: html .. container:: sphx-glr-footer sphx-glr-footer-example .. container:: sphx-glr-download sphx-glr-download-jupyter :download:`Download Jupyter notebook: wf_q3l_01_ion_trap_modelling.ipynb ` .. container:: sphx-glr-download sphx-glr-download-python :download:`Download Python source code: wf_q3l_01_ion_trap_modelling.py ` .. container:: sphx-glr-download sphx-glr-download-zip :download:`Download zipped: wf_q3l_01_ion_trap_modelling.zip `