Source code for kqcircuits.simulations.export.sonnet.sonnet_export

# This code is part of KQCircuits
# Copyright (C) 2021 IQM Finland Oy
#
# This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public
# License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later
# version.
#
# This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied
# warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License along with this program. If not, see
# https://www.gnu.org/licenses/gpl-3.0.html.
#
# The software distribution should follow IQM trademark policy for open-source software
# (meetiqm.com/iqm-open-source-trademark-policy). IQM welcomes contributions to the code.
# Please see our contribution agreements for individuals (meetiqm.com/iqm-individual-contributor-license-agreement)
# and organizations (meetiqm.com/iqm-organization-contributor-license-agreement).


import os.path
from pathlib import Path

from kqcircuits.pya_resolver import pya
from kqcircuits.defaults import default_layers
from kqcircuits.simulations.export.sonnet import parser
from kqcircuits.simulations.export.util import find_edge_from_point_in_polygons
from kqcircuits.simulations.port import InternalPort, EdgePort
from kqcircuits.simulations.simulation import Simulation, get_simulation_layer_by_name
from kqcircuits.util.export_helper import write_commit_reference_file


[docs] def poly_and_edge_indices(polygons, dbu, port, number, location, group): i, j, _ = find_edge_from_point_in_polygons(polygons, location, dbu, tolerance=5.0) # hardcoded, feel free to change return parser.port( portnum=number, ipolygon=i + 1, ivertex=j, port_type=("CUP" if group else "STD"), group=group, resist=port.resistance, react=port.reactance, induct=port.inductance, capac=port.capacitance, )
[docs] def export_sonnet_son( simulation: Simulation, path: Path, detailed_resonance=False, lower_accuracy=False, current=False, control="ABS", fill_type="Staircase", simulation_safety=0, ): """ Export simulation into son file. Arguments: simulation: The simulation to be exported. path: Location where to write son files. detailed_resonance: More info in www.sonnetsoftware.com/support/downloads/techdocs/Enhanced_Resonance_Detection_Feature.pdf lower_accuracy: False sets Sonnet to Fine/Edge meshing and True to Coarse/Edge Meshing. fill_type: Value 'Staircase' sets the default fill type for polygons. The other option is 'Conformal' which can be faster but less accurate. A good workflow could be to set everything to Staircase and then set some meanders to Conformal in Sonnet. current: True computes currents in Sonnet which adds to simulation time but can used to easily see connection errors. control: Selects what analysis control is used in Sonnet. Options are 'Simple', 'ABS' and 'Sweep' for parameter sweeping. simulation_safety: Adds extra ground area to a simulation environment (in µm). Returns: Path to exported son file. """ if simulation is None or not isinstance(simulation, Simulation): raise ValueError("Cannot export without simulation") def get_sonnet_strings(material_type, grid_size, symmetry): layout = simulation.cell.layout() dbu = layout.dbu layer_pad = layout.layer(get_simulation_layer_by_name("1t1_airbridge_pads")) layer_bridge = layout.layer(get_simulation_layer_by_name("1t1_airbridge_flyover")) layer_son = layout.layer(get_simulation_layer_by_name("1t1_signal")) layer_son_ground = layout.layer(get_simulation_layer_by_name("1t1_ground")) simpolygons = [p.polygon for p in simulation.cell.shapes(layer_son).each()] + [ p.polygon for p in simulation.cell.shapes(layer_son_ground).each() ] airbridge_polygons = [p.polygon for p in simulation.cell.shapes(layer_bridge).each()] airpads_polygons = [p.polygon for p in simulation.cell.shapes(layer_pad).each()] for p in airpads_polygons: p.isVia = True level_iter = iter( len(simpolygons) * [(2 if material_type == "Si+Al" else 0)] + len(airbridge_polygons) * [1] + len(airpads_polygons) * [2] ) polys = parser.polygons( simpolygons + airbridge_polygons + airpads_polygons, pya.DVector(-simulation.box.p1.x, -simulation.box.p2.y), dbu, # get the bottom left corner ilevel=level_iter, fill_type=("V" if (fill_type == "Conformal") else "N"), ) # find port edges sstring_ports = "" refplane_dirs = [] port_ipolys = [] group_ascii = ord("A") calgroup = "" if simulation.use_ports: for port in simulation.ports: if isinstance(port, InternalPort): sstring_ports += poly_and_edge_indices( simpolygons + airbridge_polygons, dbu, port, port.number, port.signal_location, chr(group_ascii) ) sstring_ports += poly_and_edge_indices( simpolygons + airbridge_polygons, dbu, port, -port.number, port.ground_location, chr(group_ascii), ) calgroup += f'CUPGRP "{chr(group_ascii)}"\nID 28\nGNDREF F\nTWTYPE FEED\nEND\n' group_ascii += 1 elif isinstance(port, EdgePort): # TODO: re-implement calibration # # refplane_dirs.append(port.side) # ipoly = poly_and_edge_indeces(cell, simpolygons + airbridge_polygons, dbu, port, ls) # logging.info(re.findall(r'POLY (\d+)', ipoly)) # port_ipolys.append(re.findall(r'POLY (\d+)', ipoly)) # scan ipolygon sstring_ports += poly_and_edge_indices( simpolygons + airbridge_polygons, dbu, port, port.number, port.signal_location, "" ) sonnet_box = parser.box( xwidth=simulation.box.width(), ywidth=simulation.box.height(), xcells=int(simulation.box.width() / grid_size), ycells=int(simulation.box.height() / grid_size), materials_type=material_type, ) return { "polygons": polys, "box": sonnet_box, "ports": sstring_ports, "calgroup": calgroup, "refplanes": parser.refplanes(refplane_dirs, simulation_safety, port_ipolys), "symmetry": parser.symmetry(symmetry), "nports": len({abs(port.number) for port in simulation.ports}), "resonance_abs": "DET_ABS_RES Y" if detailed_resonance else "DET_ABS_RES N", "lower_accuracy": "1" if lower_accuracy else "0", "current": "j" if current else "", } # detect airbridges shapes_in_air = simulation.layout.begin_shapes( simulation.cell, simulation.layout.layer(default_layers["1t1_airbridge_flyover"]) ) materials_type = "Si+Al" if not shapes_in_air.shape().is_null() else "Si BT" sonnet_strings = get_sonnet_strings(materials_type, 1, False) sonnet_strings["control"] = parser.control(control) son_filename = str(path.joinpath(simulation.name + ".son")) parser.apply_template( os.path.join(os.path.dirname(os.path.abspath(parser.__file__)), "template.son"), son_filename, sonnet_strings ) return son_filename
[docs] def export_sonnet( simulations, path: Path, detailed_resonance=False, lower_accuracy=False, current=False, control="ABS", fill_type="Staircase", simulation_safety=0, ): """ Export Sonnet simulations by writing son files. Arguments: simulations: List of simulations to be exported. path: Location where to write export files. detailed_resonance: More info in www.sonnetsoftware.com/support/downloads/techdocs/Enhanced_Resonance_Detection_Feature.pdf lower_accuracy: False sets Sonnet to Fine/Edge meshing and True to Coarse/Edge Meshing. fill_type: Value 'Staircase' sets the default fill type for polygons. The other option is 'Conformal' which can be faster but less accurate. A good workflow could be to set everything to Staircase and then set some meanders to Conformal in Sonnet. current: True computes currents in Sonnet which adds to simulation time but can used to easily see connection errors. control: Selects what analysis control is used in Sonnet. Options are 'Simple', 'ABS' and 'Sweep' for parameter sweeping. simulation_safety: Adds extra ground area to a simulation environment (in µm). Returns: List of paths to exported son files. """ write_commit_reference_file(path) son_filenames = [] for simulation in simulations: son_filenames.append( export_sonnet_son( simulation, path, detailed_resonance=detailed_resonance, lower_accuracy=lower_accuracy, current=current, control=control, fill_type=fill_type, simulation_safety=simulation_safety, ) ) return son_filenames