diff --git a/SiEPIC_EBeam_PDK_Verification_Check.gds b/SiEPIC_EBeam_PDK_Verification_Check.gds index 67c61a03..45e6afd3 100644 Binary files a/SiEPIC_EBeam_PDK_Verification_Check.gds and b/SiEPIC_EBeam_PDK_Verification_Check.gds differ diff --git a/klayout_dot_config/libraries/SiEPIC-EBeam.gds b/klayout_dot_config/libraries/SiEPIC-EBeam.gds index 55862933..baf74a6a 100644 Binary files a/klayout_dot_config/libraries/SiEPIC-EBeam.gds and b/klayout_dot_config/libraries/SiEPIC-EBeam.gds differ diff --git a/klayout_dot_config/pymacros/INTERCONNECT.lym b/klayout_dot_config/pymacros/INTERCONNECT.lym index d12ecadb..8c0ca355 100644 --- a/klayout_dot_config/pymacros/INTERCONNECT.lym +++ b/klayout_dot_config/pymacros/INTERCONNECT.lym @@ -44,7 +44,7 @@ version = sys.version import string - +""" def netlist_extraction(topcell): # Collection of functions to extract the circuit netlist from the physical layout @@ -66,7 +66,7 @@ def netlist_extraction(topcell): identify_all_nets(optical_pins, optical_waveguides, optical_components) return optical_waveguides, optical_components - +""" @@ -94,10 +94,10 @@ LayerINTERCONNECTN = layout.layer(LayerINTERCONNECT) # extract the circuit netlist from the physical layout: -optical_waveguides, optical_components = netlist_extraction(topcell) +optical_waveguides, optical_components = netlist_extraction(topcell)[:2] # Output the Spice netlist: -text_Spice = generate_Spice_file(topcell, optical_waveguides, optical_components) +text_Spice, num_detectors = generate_Spice_file(topcell, optical_waveguides, optical_components) print text_Spice filename = '/tmp/%s.spi' % topcell.name @@ -112,11 +112,17 @@ text_lsf = 'switchtolayout;\n' text_lsf += 'deleteall;\n' text_lsf += 'importnetlist("%s");\n' % filename text_lsf += 'run;\n' -text_lsf += 't1 = getresult("ONA_1", "input 1/mode 1/gain");\n' -text_lsf += 'visualize(t1);\n' +for i in range(0, num_detectors): + text_lsf += 't%s = getresult("ONA_1", "input %s/mode 1/gain");\n' % (i+1, i+1) +text_lsf += 'visualize(t1' +for i in range(1, num_detectors): + text_lsf += ', t%s' % (i+1) +text_lsf += ');\n' + file.write (text_lsf) file.close() +print(text_lsf) if sys.platform.startswith('freebsd'): # FreeBSD-specific code here... diff --git a/klayout_dot_config/pymacros/ResultsMarker_tests.lym b/klayout_dot_config/pymacros/ResultsMarker_tests.lym new file mode 100644 index 00000000..5cc587c2 --- /dev/null +++ b/klayout_dot_config/pymacros/ResultsMarker_tests.lym @@ -0,0 +1,63 @@ + + + + + pymacros + + + + false + false + + false + + + python + + import pya + +# Experimenting with the Results database Rdb. + + + +# Configure variables to draw structures in the presently selected cell: +lv = pya.Application.instance().main_window().current_view() +if lv == None: + raise Exception("No view selected") +# Find the currently selected layout. +ly = pya.Application.instance().main_window().current_view().active_cellview().layout() +if ly == None: + raise Exception("No layout") +cv = pya.Application.instance().main_window().current_view().active_cellview() +# find the currently selected cell: +cell = pya.Application.instance().main_window().current_view().active_cellview().cell +if cell == None: + raise Exception("No cell") +# fetch the database parameters +dbu = ly.dbu + + +rdb_i = lv.create_rdb("SiEPIC_Verification") +rdb = lv.rdb(rdb_i) + +rdb_cat_id_wg = rdb.create_category("Waveguide errors") +rdb_cat_id_wg_disc = rdb.create_category(rdb_cat_id_wg, "Disconnected Waveguides") +rdb_cat_id_wg_disc.description = "Disconnected waveguides" + +rdb.top_cell_name = cell.name +rdb_cell = rdb.create_cell(cell.name) + +rdb_item = rdb.create_item(rdb_cell.rdb_id(),rdb_cat_id_wg_disc.rdb_id()) +rdb_item.add_value(pya.RdbItemValue(pya.DBox.new(0.0, 0.0, 100.0, 200.0))) + +#rdb_item.add_value(pya.RdbItemValue(DPath)) + + + +#marker = pya.Marker.new(lv) +#marker.set(pya.DBox.new(0.0, 0.0, 100.0, 200.0)) +# to hide the marker: + + +lv.show_rdb(rdb_i, cv.cell_index) + diff --git a/klayout_dot_config/pymacros/SiEPIC_EBeam_Path_to_Waveguide.lym b/klayout_dot_config/pymacros/SiEPIC_EBeam_Path_to_Waveguide.lym index c567fd01..e1732c6a 100644 --- a/klayout_dot_config/pymacros/SiEPIC_EBeam_Path_to_Waveguide.lym +++ b/klayout_dot_config/pymacros/SiEPIC_EBeam_Path_to_Waveguide.lym @@ -119,7 +119,8 @@ for o in object_selection: print ("Selected object is a shape") if o.shape.is_path(): c = o.shape.cell - if c.name == "ROUND_PATH" and c.is_pcell_variant() and c.pcell_parameters_by_name()['layer'] == LayerSi: + print (c.basic_name()) + if c.basic_name() == "ROUND_PATH" and c.is_pcell_variant() and c.pcell_parameters_by_name()['layer'] == LayerSi: # we have a waveguide GUIDING_LAYER selected print ("GUIDING_LAYER in ROUND_PATH on LayerSi. We will not convert this to waveguide!") else: diff --git a/klayout_dot_config/pymacros/SiEPIC_EBeam_Verification.lym b/klayout_dot_config/pymacros/SiEPIC_EBeam_Verification.lym index 89fbd992..4bdbc7d5 100644 --- a/klayout_dot_config/pymacros/SiEPIC_EBeam_Verification.lym +++ b/klayout_dot_config/pymacros/SiEPIC_EBeam_Verification.lym @@ -1,6 +1,6 @@ - SiEPIC: Verification and Netlist generation + SiEPIC: Verification (rdb) 0.1.2 pymacros @@ -22,7 +22,7 @@ This file is part of the SiEPIC_EBeam_PDK by Lukas Chrostowski (c) 2015 This Python file implements layout verification and netlist extraction - +displays results in Marker Database Browser, using Results Database (rdb) Version history: @@ -60,235 +60,12 @@ Lukas Chrostowski 2015/11/18 Lukas Chrostowski 2015/11/19 - moved some functions to SiEPIC_EBeam_functions - -""" - - -import pya -import math -#import numpy -MODULE_NUMPY = False -import math -import string - - - -def flag_waveguide_error(dpoints, text, optical_waveguide, topcell): - # global variable "layout_errors" of class "Layout_error" used for storing all the errors. - a1 = [] - for p in dpoints: - a1.append (pya.Point(int(p[0]), int(p[1]))) - path = pya.Path(a1, optical_waveguide.wg_width*4/dbu) - topcell.shapes(LayerErrorN).insert(path) - x,y = xy_mean_mult(dpoints, dbu) - layout_errors.append(Layout_error(text, x, y) ) - print (text) - wtext.insertHtml('%s<br>' %text) - +Lukas Chrostowski 2015/11/21 + - adding Results Database (rdb) and Marker Database Browser, for error viewing -def check_waveguides(optical_waveguides, topcell, error_layer): - # check waveguides for basic errors. - num_errors=0 - for o in optical_waveguides: - found_error = False - - # Check for paths with > 2 vertices - if (o.wg_type == 0 and o.num_points > 2): - text = " *** Layout error. Waveguide path (%s, %s): Only 2 points allowed in a path. Convert to a waveguide (ROUND_PATH) if necessary. " \ - % ( o.points[0][0]*dbu, o.points[0][1]*dbu) - dpoints = o.points - found_error = True - flag_waveguide_error(dpoints, text, o, topcell) - - # find the minimum segment to make sure that the bends have large enough radius - if (o.wg_type == 1): - # first segment: - segment = distance_xy ( o.points[0], o.points[1] ) - if segment < o.radius: - text = " *** Layout warning. Not enough space (%s) to accommodate the desired bend radius (%s) for waveguide end." % (segment, o.radius) - dpoints=[ o.points[0], o.points[1] ] - found_error = True - flag_waveguide_error(dpoints, text, o, topcell) - # last segment: - segment = distance_xy ( o.points[len(o.points)-2], o.points[len(o.points)-1] ) - if segment < o.radius: - text = " *** Layout warning. Not enough space (%s) to accommodate the desired bend radius (%s) for waveguide end." % (segment, o.radius) - dpoints=[ o.points[len(o.points)-2], o.points[len(o.points)-1] ] - flag_waveguide_error(dpoints, text, o, topcell) - found_error = True - # go through each of the middle segments: - for j in range(1, len(o.points)-2): - segment = distance_xy ( o.points[j], o.points[j+1] ) - if segment < 2*o.radius and segment != 0: - text = " *** Layout warning. Not enough space (%s) to accommodate the desired bend radius (%s) for a mid-segment." % (segment, o.radius) - dpoints=[ o.points[j], o.points[j+1] ] - flag_waveguide_error(dpoints, text, o, topcell) - found_error = True - - # Check for waveguides with too few bend points - recommended_points = points_per_circle(o.radius) - if o.bend_pts < 0.75 * recommended_points: - text = " *** Layout warning. Waveguide bends should have at least %s points per circle for a %s micron radius." \ - % (int(recommended_points), o.radius) - dpoints=o.points - found_error = True - flag_waveguide_error(dpoints, text, o, topcell) - - - - - -def check_components(cell, LayerDevRecN): - # function to go through all the cells - # check that the cell has at most ONE DevRec shape. - return False - - -def flag_component_error(box, text, x, y): - # global variable "layout_errors" of class "Layout_error" used for storing all the errors. - topcell.shapes(LayerErrorN).insert(box) - layout_errors.append(Layout_error(text, x, y) ) - print text - wtext.insertHtml('%s<br>' %text) - - -def check_connectivity(optical_components, topcell, LayerErrorN): - # Make sure that all pins/waveguides are connected, i.e., dangling pins/waveguides. - # Flag disconnected pins - # Flag disconnected waveguides - # Make sure that waveguides / pins are connected in the correct direction - - for k in range(0,len(optical_components)): - if optical_components[k].npins != len(optical_components[k].nets): - text = " *** Connectivity error. Mismatch between the number of pins (%s) on the component (%s, %s) at (%s, %s), and the number of nets (%s)." \ - % ( optical_components[k].npins, optical_components[k].component, optical_components[k].index, \ - optical_components[k].x, optical_components[k].y, len(optical_components[k].nets) ) - bbox = layout.cell(optical_components[k].instance).bbox() - box = bbox.transformed(pya.Trans(optical_components[k].rotate, optical_components[k].flip, \ - optical_components[k].x/dbu,optical_components[k].y/dbu)) -# flag_component_error(box, text, optical_components[k].x, optical_components[k].y) - - # go through all the pins in the component, and check if they aren't assigned to a net - for m in range(0,optical_components[k].npins): - pin = optical_pins[optical_components[k].pins[m]] - if pin.net == 0 and pin.pin_type == 1: # disconnected pin on a pin_type = 1 (component) - text = " *** Found disconnected pin #%s at (%s, %s)." % (pin.index, pin.x*dbu, pin.y*dbu) - box = pya.Box(pin.x-3000, pin.y-3000, pin.x+3000, pin.y+3000) -# flag_component_error(box, text, pin.x*dbu, pin.y*dbu) - - for pin in optical_pins: - if pin.net == -1 and pin.pin_type != 2: # disconnected pin not on a pin_type = 2 (optical IO) - text = " *** Found disconnected pin #%s, type %s, at (%s, %s), component #%s {%s}" \ - % (pin.index, pin.pin_type, pin.x*dbu, pin.y*dbu, \ - pin.component_n, optical_components[pin.component_n].component) - box = pya.Box(pin.x-3000, pin.y-3000, pin.x+3000, pin.y+3000) - flag_component_error(box, text, pin.x*dbu, pin.y*dbu) - - for w1 in optical_waveguides: - # examine the beginning of the waveguide: - if w1.net1 == -1: - text = " *** Found disconnected waveguide at (%s, %s), net1" \ - % (w1.points[0][0]*dbu, w1.points[0][1]*dbu) - flag_waveguide_error( [w1.points[0], w1.points[1]], text, optical_waveguides[w1.index], topcell) - # examine the end of the waveguide: - if w1.net2 == -1: - text = " *** Found disconnected waveguide at (%s, %s), net2" \ - % (w1.points[len(w1.points)-1][0]*dbu, w1.points[len(w1.points)-1][1]*dbu) - flag_waveguide_error([w1.points[len(w1.points)-1], w1.points[len(w1.points)-2]], \ - text, optical_waveguides[w1.index], topcell) - - # Make sure that waveguides / pins are connected in the correct direction (colinear check) - - - -def list_optical_components(optical_components): - # list all Optical_component objects from an array - # input array, optical_components - # example output: - # X_grating_coupler_1 N$7 N$6 grating_coupler library="custom/genericcml" sch_x=-1.42 sch_y=-0.265 sch_r=0 sch_f=false - - for o in optical_components: - nets_str = "" - for n in o.nets: - nets_str += " N$" + str(n) - flip = ' sch_f=true' if o.flip else '' - if o.rotate > 0: - rotate = ' sch_r=%s' % str(o.rotate) - else: - rotate = '' -# t = '%s %s %s library="%s" lay_x=%s lay_y=%s sch_x=%s sch_y=%s %s%s' % \ -# ( "X"+o.component+"_"+str(o.index), nets_str, o.component, o.library, str (o.x * 1e-6), o.y * 1e-6, o.x, o.y, rotate, flip) - t = ' %s %s %s library="%s" sch_x=%s sch_y=%s %s%s' % \ - ( "X"+o.component+"_"+str(o.index), nets_str, o.component, o.library, eng_str(o.x * 5e-2), eng_str(o.y * 5e-2), rotate, flip) - print (t) - wtext.insertHtml('%s<br>' %t) - -def list_optical_waveguides(list_optical_waveguides): - # list all Optical_waveguides objects from an array - # input array, optical_waveguides - # example output: - # X5 9 10 ebeam_wg_strip_1550 library="Design kits/ebeam_v1.0" wg_length=7.86299e-06 wg_width=5.085e-07 sch_x=-1.42 sch_y=-0.265 - - for o in list_optical_waveguides: - nets_str = "N$%s N$%s" %(o.net1, o.net2) - x,y = xy_mean_mult(o.points, dbu) -# t = '%s %s %s library="%s" wg_length=%s wg_width=%s lay_x=%s lay_y=%s sch_x=%5.3f sch_y=%5.3f' % \ -# ( "Xwg" + str(o.index), nets_str, o.component, o.library, eng_str(o.length*1e-6), eng_str(o.wg_width*1e-6), \ -# eng_str(x * 1e-6), eng_str(y * 1e-6), x, y) - t = ' %s %s %s library="%s" wg_length=%s wg_width=%s sch_x=%s sch_y=%s' % \ - ( "Xwg" + str(o.index), nets_str, o.component, o.library, \ - eng_str(o.length*1e-6), eng_str(o.wg_width*1e-6), eng_str(x * 5e-2), eng_str(y * 5e-2)) - print (t) - wtext.insertHtml('%s<br>' %t) - - -def gen_ui(): - global wdg - - # If the window is already here, destroy it and start over again. - if 'wdg' in globals(): - if wdg is not None and not wdg.destroyed(): - wdg.destroy() - global wtext - - def button_clicked(checked): - """ Event handler: "OK" button clicked """ - wdg.destroy() - - wdg = pya.QDialog(pya.Application.instance().main_window()) - - wdg.setAttribute(pya.Qt.WA_DeleteOnClose) - wdg.setWindowTitle("SiEPIC-EBeam-PDK Verification and Netlist Generation") - - wdg.resize(1200, 2500) - wdg.move(1, 1) - - grid = pya.QGridLayout(wdg) - - windowlabel1 = pya.QLabel(wdg) - windowlabel1.setText("Verification output:") - wtext = pya.QTextEdit(wdg) - wtext.enabled = True - wtext.setText('') - wtext.LineWrapMode = pya.QTextEdit.NoWrap - - ok = pya.QPushButton("OK", wdg) - ok.clicked(button_clicked) # attach the event handler - netlist = pya.QPushButton("Netlist Export", wdg) # not implemented - - grid.addWidget(windowlabel1, 0, 0, 1, 3) - grid.addWidget(wtext, 1, 1, 3, 3) - grid.addWidget(netlist, 4, 2) - grid.addWidget(ok, 4, 3) - - grid.setRowStretch(3, 1) - grid.setColumnStretch(1, 1) - - wdg.show() - - +""" # ************************************************************************ # ************************************************************************ @@ -296,16 +73,6 @@ def gen_ui(): # ************************************************************************ # ************************************************************************ -#from time import strftime, clock -import time - -# Create a GUI for the output: -gen_ui() -print(wdg) -wtext.insertHtml('Running SiEPIC-EBeam-PDK Verification and Netlist Generation.<br>') -wtext.insertHtml('* KLayout SiEPIC_EBeam_PDK v%s, %s.<br>' % (SiEPIC_Version, time.strftime("%Y-%m-%d %H:%M:%S") ) ) -clock_start = time.clock() - # Configure variables to draw structures in the presently selected cell: lv = pya.Application.instance().main_window().current_view() @@ -315,6 +82,7 @@ if lv == None: layout = pya.Application.instance().main_window().current_view().active_cellview().layout() if layout == None: raise Exception("No layout") +cv = pya.Application.instance().main_window().current_view().active_cellview() # find the currently selected cell: topcell = pya.Application.instance().main_window().current_view().active_cellview().cell if topcell == None: @@ -322,7 +90,6 @@ if topcell == None: # fetch the database parameters dbu = layout.dbu - # Define layers based on PDK_functions: LayerSiN = layout.layer(LayerSi) LayerTextN = layout.layer(LayerText) @@ -332,8 +99,6 @@ LayerFbrTgtN = layout.layer(LayerFbrTgt) LayerErrorN = layout.layer(LayerError) LayerINTERCONNECTN = layout.layer(LayerINTERCONNECT) -# Clear the previous errors: -clear_ErrorLayer(topcell, LayerErrorN) # initialize the arrays to keep track of layout objects reset_Optical_classes() @@ -342,91 +107,49 @@ optical_waveguides = [] optical_pins = [] optical_nets = [] +# Create a Results Database +rdb_i = lv.create_rdb("SiEPIC_Verification") +rdb = lv.rdb(rdb_i) +rdb.top_cell_name = topcell.name +rdb_cell = rdb.create_cell(topcell.name) -# Search the layout for the components and waveguides: -print ("") -print ("* calling find_all_components() – DevRec:") -find_all_components(topcell, LayerDevRecN, LayerPinRecN, LayerFbrTgtN) -clock_find_all_components = time.clock() -print ("") -print ("* calling find_all_waveguides():") -find_all_waveguides(topcell, LayerSiN) -clock_find_all_waveguides = time.clock() -print ("") -print ("* print_Optical_all, after find_all_{components, waveguides}:") -print_Optical_all(optical_components, optical_waveguides, optical_pins, optical_nets) +if 'wtext' in globals(): + # don't use the text window for output. + del(wtext) +# Search the layout for the components and waveguides: # Search the arrays to identify all the nets: -print ("") -print ("* calling identify_all_nets():") -clock_identify_all_nets0 = time.clock() -identify_all_nets(optical_pins, optical_waveguides, optical_components) -clock_identify_all_nets = time.clock() -print ("") -print ("print_Optical_all, after identify_all_nets:") -print_Optical_all(optical_components, optical_waveguides, optical_pins, optical_nets) -clock_identify_layout = time.clock() +optical_waveguides, optical_components = netlist_extraction(topcell)[:2] +# ********* Verification ********* # Check the layout for errors, using the above arrays: print ("") print ("Checking layout for errors: ") -wtext.insertHtml('<br>* Checking layout for errors:<br><br>') layout_errors=[] # Check components - overlapping -check_components(topcell, LayerDevRecN) -clock_check_components = time.clock() +number_errors = check_components(rdb, topcell, LayerDevRecN) # Check waveguides for waveguide-specific problems: -check_waveguides(optical_waveguides, topcell, LayerErrorN) -clock_check_waveguides = time.clock() +number_errors += check_waveguides(rdb, optical_waveguides, topcell, LayerErrorN) # Check connectivity between components and waveguides: -check_connectivity(optical_components, topcell, LayerErrorN) -clock_check_connectivity = time.clock() +number_errors += check_connectivity(rdb, optical_components, topcell, LayerErrorN) lv.add_missing_layers() print ("*** Number of errors found: %s." % len(layout_errors) ) -wtext.insertHtml('<br>*** Number of errors found: %s.<br>' % len(layout_errors) ) -clock_verify_layout = time.clock() - - -# Output the Spice netlist: -text_Spice = generate_Spice_file(topcell, optical_waveguides, optical_components) -wtext.insertPlainText(text_Spice) -clock_netlist = time.clock() - - -# Find the automated measurement coordinates: -wtext.insertHtml('<br>* Automated measurement coordinates:<br><br>') -print ("") -print ("Automated measurement coordinates: ") -print ("") -t = find_automated_measurement_labels(topcell, LayerTextN) -wtext.insertHtml (t) -clock_automated_measurements = time.clock() +print ("*** Number of errors found: %s." % number_errors ) - -wtext.insertHtml('<br>CPU time for searching layout: find_all_components %s, find_all_waveguides %s<br>' \ -% (clock_find_all_components-clock_start, clock_find_all_waveguides-clock_find_all_components) ) - -wtext.insertHtml('<br>CPU time for identify_all_nets %s<br>' \ -% (clock_identify_all_nets-clock_identify_all_nets0) ) - -wtext.insertHtml('<br>CPU time for verification: check_components %s, check_waveguides %s, check_connectivity %s<br>' \ -% (clock_check_components-clock_identify_all_nets, clock_check_waveguides-clock_check_components, clock_check_connectivity-clock_check_waveguides) ) - -wtext.insertHtml('<br>CPU time for Spice netlist %s, automated measurements %s<br>' \ -% (clock_netlist-clock_verify_layout, clock_automated_measurements-clock_netlist) ) - -wtext.insertHtml('<br>total CPU time: %s. <br>' % (time.clock() - clock_start) ) - - -# Done -wtext.insertHtml('<br>Done.<br>') + print ("") print ("Done. ") print ("") +#displays results in Marker Database Browser, using Results Database (rdb) +if number_errors>0: + v = pya.MessageBox.warning("Errors", "%s layout errors detected. \nPlease review errors using the 'Marker Database Browser'." % number_errors, pya.MessageBox.Ok) + lv.show_rdb(rdb_i, cv.cell_index) +else: + v = pya.MessageBox.warning("Errors", "No layout errors detected.", pya.MessageBox.Ok) diff --git a/klayout_dot_config/pymacros/SiEPIC_EBeam_Verification_text.lym b/klayout_dot_config/pymacros/SiEPIC_EBeam_Verification_text.lym new file mode 100644 index 00000000..3832264d --- /dev/null +++ b/klayout_dot_config/pymacros/SiEPIC_EBeam_Verification_text.lym @@ -0,0 +1,258 @@ + + + SiEPIC: Verification and Netlist generation + + pymacros + + + + false + false + + true + + + python + + # Python script +# SiEPIC_EBeam_Verification + +""" +This file is part of the SiEPIC_EBeam_PDK +by Lukas Chrostowski (c) 2015 + +This Python file implements layout verification and netlist extraction +displays results in a text window + + +Version history: + +Lukas Chrostowski 2015/11/11 + - Optical_net class + - find all LayerSi paths and waveguides, and save optical net vertices + - find all PinRec paths + - find all DevRec polygons and boxes + - check_waveguides + - waveguide bend minimum + - paths with corners (more than 2 points) + - bend radius less than specified + - error marker layer and class; clear errors + +Lukas Chrostowski 2015/11/12 + - determining where pins/waveguides overlap to generate a netlist + - initial Spice output for components including waveguides and nets. + - check nets & components + - incorrect number of nets connected to a component (disconnected, or too many) + +Lukas Chrostowski 2015/11/15 + - fix for Python 3.4: print ("xxx") + - moved some functions from here, to the common "SiEPIC_EBeam_functions file, so they are accessible elsewhere. + - added pin# text label on PinRec layer; sort pins in Spice output using these names. + +Lukas Chrostowski 2015/11/16 + - fixes for component pin_type = Optical IO / FbrTgt being incorrectly handled; added Optical_pin.pin_type + +Lukas Chrostowski 2015/11/17 + - debugging & fixing Verification problems + +Lukas Chrostowski 2015/11/18 + - removed need for numpy.array + +Lukas Chrostowski 2015/11/19 + - moved some functions to SiEPIC_EBeam_functions + +Lukas Chrostowski 2015/11/21 + - adding Results Database (rdb) and Marker Database Browser, for error viewing + + + +""" + + +def gen_ui(): + global wdg + + # If the window is already here, destroy it and start over again. + if 'wdg' in globals(): + if wdg is not None and not wdg.destroyed(): + wdg.destroy() + global wtext + + def button_clicked(checked): + """ Event handler: "OK" button clicked """ + wdg.destroy() + + wdg = pya.QDialog(pya.Application.instance().main_window()) + + wdg.setAttribute(pya.Qt.WA_DeleteOnClose) + wdg.setWindowTitle("SiEPIC-EBeam-PDK Verification and Netlist Generation") + + wdg.resize(1200, 2500) + wdg.move(1, 1) + + grid = pya.QGridLayout(wdg) + + windowlabel1 = pya.QLabel(wdg) + windowlabel1.setText("Verification output:") + wtext = pya.QTextEdit(wdg) + wtext.enabled = True + wtext.setText('') + wtext.LineWrapMode = pya.QTextEdit.NoWrap + + ok = pya.QPushButton("OK", wdg) + ok.clicked(button_clicked) # attach the event handler + netlist = pya.QPushButton("Netlist Export", wdg) # not implemented + + grid.addWidget(windowlabel1, 0, 0, 1, 3) + grid.addWidget(wtext, 1, 1, 3, 3) + grid.addWidget(netlist, 4, 2) + grid.addWidget(ok, 4, 3) + + grid.setRowStretch(3, 1) + grid.setColumnStretch(1, 1) + + wdg.show() + + + +# ************************************************************************ +# ************************************************************************ +# Main script: +# ************************************************************************ +# ************************************************************************ + +#from time import strftime, clock +import time + +# Create a GUI for the output: +gen_ui() + +if 'wtext' in globals(): + wtext.insertHtml('Running SiEPIC-EBeam-PDK Verification and Netlist Generation.<br>') + wtext.insertHtml('* KLayout SiEPIC_EBeam_PDK v%s, %s.<br>' % (SiEPIC_Version, time.strftime("%Y-%m-%d %H:%M:%S") ) ) +clock_start = time.clock() + + +# Configure variables to draw structures in the presently selected cell: +lv = pya.Application.instance().main_window().current_view() +if lv == None: + raise Exception("No view selected") +# Find the currently selected layout. +layout = pya.Application.instance().main_window().current_view().active_cellview().layout() +if layout == None: + raise Exception("No layout") +cv = pya.Application.instance().main_window().current_view().active_cellview() +# find the currently selected cell: +topcell = pya.Application.instance().main_window().current_view().active_cellview().cell +if topcell == None: + raise Exception("No cell") +# fetch the database parameters +dbu = layout.dbu + + +# Define layers based on PDK_functions: +LayerSiN = layout.layer(LayerSi) +LayerTextN = layout.layer(LayerText) +LayerPinRecN = layout.layer(LayerPinRec) +LayerDevRecN = layout.layer(LayerDevRec) +LayerFbrTgtN = layout.layer(LayerFbrTgt) +LayerErrorN = layout.layer(LayerError) +LayerINTERCONNECTN = layout.layer(LayerINTERCONNECT) + +# Clear the previous errors: +clear_ErrorLayer(topcell, LayerErrorN) + +# initialize the arrays to keep track of layout objects +reset_Optical_classes() +optical_components = [] +optical_waveguides = [] +optical_pins = [] +optical_nets = [] + +# Create a Results Database +rdb_i = lv.create_rdb("SiEPIC_Verification") +rdb = lv.rdb(rdb_i) +rdb.top_cell_name = topcell.name +rdb_cell = rdb.create_cell(topcell.name) + + +# Search the layout for the components and waveguides: +# Search the arrays to identify all the nets: +optical_waveguides, optical_components, clock_find_all_components, clock_find_all_waveguides, clock_identify_all_nets \ + = netlist_extraction(topcell) + +clock_identify_layout = time.clock() + + +# ********* Verification ********* +# Check the layout for errors, using the above arrays: +print ("") +print ("Checking layout for errors: ") +if 'wtext' in globals(): + wtext.insertHtml('<br>* Checking layout for errors:<br><br>') +layout_errors=[] +# Check components - overlapping +check_components(rdb, topcell, LayerDevRecN) +clock_check_components = time.clock() +# Check waveguides for waveguide-specific problems: +check_waveguides(rdb, optical_waveguides, topcell, LayerErrorN) +clock_check_waveguides = time.clock() +# Check connectivity between components and waveguides: +check_connectivity(rdb, optical_components, topcell, LayerErrorN) +clock_check_connectivity = time.clock() +lv.add_missing_layers() +print ("*** Number of errors found: %s." % len(layout_errors) ) +if 'wtext' in globals(): + wtext.insertHtml('<br>*** Number of errors found: %s.<br>' % len(layout_errors) ) +clock_verify_layout = time.clock() + + +# Output the Spice netlist: +text_Spice = generate_Spice_file(topcell, optical_waveguides, optical_components) +if 'wtext' in globals(): + wtext.insertPlainText(text_Spice) +clock_netlist = time.clock() + + +# Find the automated measurement coordinates: +if 'wtext' in globals(): + wtext.insertHtml('<br>* Automated measurement coordinates:<br><br>') +print ("") +print ("Automated measurement coordinates: ") +print ("") +t = find_automated_measurement_labels(topcell, LayerTextN) +if 'wtext' in globals(): + wtext.insertHtml (t) +clock_automated_measurements = time.clock() + +if 'wtext' in globals(): + + wtext.insertHtml('<br>CPU time for searching layout: find_all_components %s, find_all_waveguides %s<br>' \ + % (clock_find_all_components-clock_start, clock_find_all_waveguides-clock_find_all_components) ) + + wtext.insertHtml('<br>CPU time for identify_all_nets %s<br>' \ + % (clock_identify_all_nets-clock_find_all_components) ) + + wtext.insertHtml('<br>CPU time for verification: check_components %s, check_waveguides %s, check_connectivity %s<br>' \ + % (clock_check_components-clock_identify_all_nets, clock_check_waveguides-clock_check_components, clock_check_connectivity-clock_check_waveguides) ) + + wtext.insertHtml('<br>CPU time for Spice netlist %s, automated measurements %s<br>' \ + % (clock_netlist-clock_verify_layout, clock_automated_measurements-clock_netlist) ) + + wtext.insertHtml('<br>total CPU time: %s. <br>' % (time.clock() - clock_start) ) + + +# Done +if 'wtext' in globals(): + wtext.insertHtml('<br>Done.<br>') + +print ("") +print ("Done. ") +print ("") + + +#lv.show_rdb(rdb_i, cv.cell_index) + + + + diff --git a/klayout_dot_config/pymacros/SiEPIC_EBeam_Waveguide_to_Path.lym b/klayout_dot_config/pymacros/SiEPIC_EBeam_Waveguide_to_Path.lym index 1120d630..6bf781ef 100644 --- a/klayout_dot_config/pymacros/SiEPIC_EBeam_Waveguide_to_Path.lym +++ b/klayout_dot_config/pymacros/SiEPIC_EBeam_Waveguide_to_Path.lym @@ -102,7 +102,7 @@ for o in object_selection: elif o.shape: print ("Selected object is a shape.") c = o.shape.cell - if c.name == "ROUND_PATH" and c.is_pcell_variant() and c.pcell_parameters_by_name()['layer'] == LayerSi: + if c.basic_name() == "ROUND_PATH" and c.is_pcell_variant() and c.pcell_parameters_by_name()['layer'] == LayerSi: # we have a waveguide GUIDING_LAYER selected print ("Selected object is a GUIDING_LAYER in ROUND_PATH on LayerSi.") trans = o.source_trans().s_trans() diff --git a/klayout_dot_config/pymacros/SiEPIC_EBeam_functions.lym b/klayout_dot_config/pymacros/SiEPIC_EBeam_functions.lym index a2320047..2bb8d00d 100644 --- a/klayout_dot_config/pymacros/SiEPIC_EBeam_functions.lym +++ b/klayout_dot_config/pymacros/SiEPIC_EBeam_functions.lym @@ -65,6 +65,10 @@ Lukas Chrostowski 2015/11/19 - finding the laser and detectors in the layout - generating a Spice netlist including Optical Network Analyzer; ready for Lumerical INTERCONNECT - Added Python 2 & 3 compatibility for iter.next() vs. next(iter) + +Lukas Chrostowski 2015/11/21 + - bug fix in points_mult, that argument in the function was being modified. needed to copy it. + """ @@ -72,6 +76,14 @@ SiEPIC_Version = '0.1.6' import pya import math +import string + +try: + import numpy +except ImportError: + #import numpy + print ("no numpy") + MODULE_NUMPY = False # SiEPIC-EBeam-PDK Layers: @@ -85,8 +97,6 @@ LayerINTERCONNECT = pya.LayerInfo(733,0) -#import numpy -MODULE_NUMPY = False # Determine whether we have Python 2 or Python 3 import sys @@ -216,14 +226,17 @@ def reset_Optical_classes(): Layout_error.n = 0 # multiply an array of points by a constant -def points_mult(dpoints, mult): +def points_mult(dpoints1, mult): + # create a new empty list. Otherwise, this function would modify the original list + # http://stackoverflow.com/questions/240178/python-list-of-lists-changes-reflected-across-sublists-unexpectedly + newlist = [[0]*2 for n in range(len(dpoints1))] if MODULE_NUMPY: - return numpy.array(dpoints)*mult + return numpy.array(dpoints1)*mult else: for i in range(0,2): - for j in range(0,len(points)): - dpoints[j][i] *= mult - return dpoints + for j in range(0,len(dpoints1)): + newlist[j][i] = dpoints1[j][i]*mult + return newlist # calculate the mean in (x,y) for an array of points def xy_mean_mult(dpoints,dbu): @@ -355,8 +368,6 @@ def find_all_components(cell, LayerDevRecN, LayerPinRecN, LayerFbrTgtN): path= iter2.shape().path.transformed(iter2.itrans()) path= path.transformed(iter1.itrans()) points = path_to_points(path) - x = points[0][0] - y = points[0][1] #print ( "%s: PinRec in cell {%s}, path -- %s" % (i, iter2.cell().name, path) ) path_points.append(points) path_shape.append(iter2.shape()) @@ -373,8 +384,8 @@ def find_all_components(cell, LayerDevRecN, LayerPinRecN, LayerFbrTgtN): pya.Point( pin_info2[p1].pin_x, pin_info2[p1].pin_y ) ) if check_text_in_pin: points = path_points[p2] - x = points[0][0] - y = points[0][1] + x = (points[0][0]+points[1][0])/2 # midpoint of pin path + y = (points[0][1]+points[1][1])/2 pin_index = len(optical_pins) optical_pins.append (Optical_pin (pin_index, points, component_index, x, y, 1) ) optical_components[component_index].npins += 1 @@ -469,8 +480,8 @@ def points_to_Dpath(points, w): a1 = [] for p in points: a1.append (pya.DPoint(p[0], p[1])) - wg_path = pya.DPath(a1, w) - return wg_path + wg_path1 = pya.DPath(a1, w) + return wg_path1 def points_to_path(points, w): @@ -482,7 +493,7 @@ def points_to_path(points, w): def polygon_to_points(polygon): - # for some reason, you assign points to a polygon, but not read them! + # for some reason, you can assign points to a polygon, but not read them! # http://www.klayout.de/doc/code/class_SimplePolygon.html # This function reads the points one by one and returns an array of points npts = polygon.to_simple_polygon().points @@ -959,7 +970,7 @@ def generate_Spice_file(topcell, optical_waveguides, optical_components): # main circuit text += '%s%s %s sch_x=-1 sch_y=-1\n\n' % (topcell.name, opticalIO_pins, topcell.name) - return text + return text, len(detector_nets) def eng_str(x): @@ -989,8 +1000,264 @@ def eng_str(x): #return sign+str(z)+'E'+str(engr_exponent) # return sign+ '%3.3f' % z +str(str_engr_exponent) return sign+ str(z) +str(str_engr_exponent) + + +def netlist_extraction(topcell): + # Collection of functions to extract the circuit netlist from the physical layout + + import time + + # Search the layout for the components and waveguides: + print ("") + print ("* calling find_all_components() – DevRec:") + find_all_components(topcell, LayerDevRecN, LayerPinRecN, LayerFbrTgtN) + clock_find_all_components = time.clock() + + print ("") + print ("* calling find_all_waveguides():") + find_all_waveguides(topcell, LayerSiN) + clock_find_all_waveguides = time.clock() + +# print ("") +# print ("* print_Optical_all, after find_all_{components, waveguides}:") +# print_Optical_all(optical_components, optical_waveguides, optical_pins, optical_nets) + + # Search the arrays to identify all the nets: + print ("") + print ("* calling identify_all_nets():") + identify_all_nets(optical_pins, optical_waveguides, optical_components) + clock_identify_all_nets = time.clock() + + print ("") + print ("print_Optical_all, after identify_all_nets:") + print_Optical_all(optical_components, optical_waveguides, optical_pins, optical_nets) + + return optical_waveguides, optical_components, \ + clock_find_all_components, clock_find_all_waveguides, clock_identify_all_nets + + + +def flag_waveguide_error(dpoints, text, optical_waveguide, topcell): + # global variable "layout_errors" of class "Layout_error" used for storing all the errors. + a1 = [] + for p in dpoints: + a1.append (pya.Point(int(p[0]), int(p[1]))) + path = pya.Path(a1, optical_waveguide.wg_width*4/dbu) + x,y = xy_mean_mult(dpoints, dbu) + layout_errors.append(Layout_error(text, x, y) ) + print (text) + if 'wtext' in globals(): + topcell.shapes(LayerErrorN).insert(path) + wtext.insertHtml('%s<br>' %text) + + +def check_waveguides(rdb, optical_waveguides, topcell, error_layer): + # check waveguides for basic errors. + num_errors=0 + + rdb_cell = next(rdb.each_cell()) + + rdb_cat_id_wg = rdb.create_category("Waveguide errors") + + rdb_cat_id_wg_path = rdb.create_category(rdb_cat_id_wg, "Path") + rdb_cat_id_wg_path.description = "Waveguide path: Only 2 points allowed in a path. Convert to a waveguide (ROUND_PATH) if necessary." + + rdb_cat_id_wg_radius = rdb.create_category(rdb_cat_id_wg, "Radius") + rdb_cat_id_wg_radius.description = "Not enough space to accommodate the desired bend radius for the waveguide end." + + rdb_cat_id_wg_bendpts = rdb.create_category(rdb_cat_id_wg, "Bend points") + rdb_cat_id_wg_bendpts.description = "Waveguide bend should have more points per circle." + + + for o in optical_waveguides: + + # Check for paths with > 2 vertices + if (o.wg_type == 0 and o.num_points > 2): + text = " *** Layout error. Waveguide path (%s, %s): Only 2 points allowed in a path. Convert to a waveguide (ROUND_PATH) if necessary. " \ + % ( o.points[0][0]*dbu, o.points[0][1]*dbu) + dpoints = o.points + num_errors += 1 + flag_waveguide_error(dpoints, text, o, topcell) + rdb_item = rdb.create_item(rdb_cell.rdb_id(),rdb_cat_id_wg_path.rdb_id()) + rdb_item.add_value(pya.RdbItemValue( points_to_Dpath(points_mult(dpoints, dbu),o.wg_width) ) ) + + # find the minimum segment to make sure that the bends have large enough radius + if (o.wg_type == 1): + # first segment: + segment = distance_xy ( o.points[0], o.points[1] ) + if segment < o.radius: + text = " *** Layout warning. Not enough space (%s) to accommodate the desired bend radius (%s) for waveguide end." % (segment, o.radius) + dpoints=[ o.points[0], o.points[1] ] + num_errors += 1 + flag_waveguide_error(dpoints, text, o, topcell) + rdb_item = rdb.create_item(rdb_cell.rdb_id(),rdb_cat_id_wg_radius.rdb_id()) + rdb_item.add_value(pya.RdbItemValue( points_to_Dpath(points_mult(dpoints, dbu),o.wg_width) ) ) + # last segment: + segment = distance_xy ( o.points[len(o.points)-2], o.points[len(o.points)-1] ) + if segment < o.radius: + text = " *** Layout warning. Not enough space (%s) to accommodate the desired bend radius (%s) for waveguide end." % (segment, o.radius) + dpoints=[ o.points[len(o.points)-2], o.points[len(o.points)-1] ] + num_errors += 1 + flag_waveguide_error(dpoints, text, o, topcell) + rdb_item = rdb.create_item(rdb_cell.rdb_id(),rdb_cat_id_wg_radius.rdb_id()) + rdb_item.add_value(pya.RdbItemValue( points_to_Dpath(points_mult(dpoints, dbu),o.wg_width) ) ) + # go through each of the middle segments: + for j in range(1, len(o.points)-2): + segment = distance_xy ( o.points[j], o.points[j+1] ) + if segment < 2*o.radius and segment != 0: + text = " *** Layout warning. Not enough space (%s) to accommodate the desired bend radius (%s) for a mid-segment." % (segment, o.radius) + dpoints=[ o.points[j], o.points[j+1] ] + num_errors += 1 + flag_waveguide_error(dpoints, text, o, topcell) + rdb_item = rdb.create_item(rdb_cell.rdb_id(),rdb_cat_id_wg_radius.rdb_id()) + rdb_item.add_value(pya.RdbItemValue( points_to_Dpath(points_mult(dpoints, dbu),o.wg_width) ) ) + + # Check for waveguides with too few bend points + recommended_points = points_per_circle(o.radius) + if o.bend_pts < 0.75 * recommended_points: + text = " *** Layout warning. Waveguide bends should have at least %s points per circle for a %s micron radius." \ + % (int(recommended_points), o.radius) + dpoints=o.points + num_errors += 1 + flag_waveguide_error(dpoints, text, o, topcell) + rdb_item = rdb.create_item(rdb_cell.rdb_id(),rdb_cat_id_wg_bendpts.rdb_id()) + rdb_item.add_value(pya.RdbItemValue( points_to_Dpath(points_mult(dpoints, dbu),o.wg_width) ) ) + + return num_errors + + +def check_components(rdb, cell, LayerDevRecN): + # function to go through all the cells + # check that the cell has at most ONE DevRec shape. + return False + + + +def flag_component_error(box, text, x, y): + # global variable "layout_errors" of class "Layout_error" used for storing all the errors. + layout_errors.append(Layout_error(text, x, y) ) + print text + if 'wtext' in globals(): + topcell.shapes(LayerErrorN).insert(box) + wtext.insertHtml('%s<br>' %text) + + +def check_connectivity(rdb, optical_components, topcell, LayerErrorN): + # Make sure that all pins/waveguides are connected, i.e., dangling pins/waveguides. + # Flag disconnected pins + # Flag disconnected waveguides + # Make sure that waveguides / pins are connected in the correct direction (identify_nets only finds colinear pins/waveguides) + + num_errors=0 + + rdb_cell = next(rdb.each_cell()) + + rdb_cat_id = rdb.create_category("Connectivity errors") + + rdb_cat_id_discwg = rdb.create_category(rdb_cat_id, "Disconnected waveguide") + rdb_cat_id_discwg.description = "Disconnected waveguides" + + rdb_cat_id_discpin = rdb.create_category(rdb_cat_id, "Disconnected pin") + rdb_cat_id_discpin.description = "Disconnected pin" + + if 0: + for k in range(0,len(optical_components)): + if optical_components[k].npins != len(optical_components[k].nets): + text = " *** Connectivity error. Mismatch between the number of pins (%s) on the component (%s, %s) at (%s, %s), and the number of nets (%s)." \ + % ( optical_components[k].npins, optical_components[k].component, optical_components[k].index, \ + optical_components[k].x, optical_components[k].y, len(optical_components[k].nets) ) + bbox = layout.cell(optical_components[k].instance).bbox() + box = bbox.transformed(pya.Trans(optical_components[k].rotate, optical_components[k].flip, \ + optical_components[k].x/dbu,optical_components[k].y/dbu)) +# flag_component_error(box, text, optical_components[k].x, optical_components[k].y) + + # go through all the pins in the component, and check if they aren't assigned to a net + for m in range(0,optical_components[k].npins): + pin = optical_pins[optical_components[k].pins[m]] + if pin.net == 0 and pin.pin_type == 1: # disconnected pin on a pin_type = 1 (component) + text = " *** Found disconnected pin #%s at (%s, %s)." % (pin.index, pin.x*dbu, pin.y*dbu) + box = pya.Box(pin.x-3000, pin.y-3000, pin.x+3000, pin.y+3000) +# flag_component_error(box, text, pin.x*dbu, pin.y*dbu) + + for pin in optical_pins: + if pin.net == -1 and pin.pin_type != 2: # disconnected pin not on a pin_type = 2 (optical IO) + text = " *** Found disconnected pin #%s, type %s, at (%s, %s), component #%s {%s}" \ + % (pin.index, pin.pin_type, pin.x*dbu, pin.y*dbu, \ + pin.component_n, optical_components[pin.component_n].component) + box = pya.Box(pin.x-3000, pin.y-3000, pin.x+3000, pin.y+3000) + num_errors += 1 + flag_component_error(box, text, pin.x*dbu, pin.y*dbu) + rdb_item = rdb.create_item(rdb_cell.rdb_id(),rdb_cat_id_discpin.rdb_id()) + rdb_item.add_value(pya.RdbItemValue( pya.DBox(pin.x*dbu-0.3, pin.y*dbu-0.3, pin.x*dbu+0.3, pin.y*dbu+0.3) ) ) + + for w1 in optical_waveguides: + print (w1.points) + # examine the beginning of the waveguide: + if w1.net1 == -1: + text = " *** Found disconnected waveguide at (%s, %s), net1" \ + % (w1.points[0][0]*dbu, w1.points[0][1]*dbu) + dpoints = [w1.points[0], w1.points[1]] + num_errors += 1 + flag_waveguide_error( dpoints, text, optical_waveguides[w1.index], topcell) + rdb_item = rdb.create_item(rdb_cell.rdb_id(),rdb_cat_id_discwg.rdb_id()) + rdb_item.add_value(pya.RdbItemValue( points_to_Dpath(points_mult(dpoints, dbu),w1.wg_width) ) ) + # examine the end of the waveguide: + if w1.net2 == -1: + text = " *** Found disconnected waveguide at (%s, %s), net2" \ + % (w1.points[len(w1.points)-1][0]*dbu, w1.points[len(w1.points)-1][1]*dbu) + dpoints = [w1.points[len(w1.points)-1], w1.points[len(w1.points)-2]] + print (dpoints) + num_errors += 1 + flag_waveguide_error(dpoints, \ + text, optical_waveguides[w1.index], topcell) + rdb_item = rdb.create_item(rdb_cell.rdb_id(),rdb_cat_id_discwg.rdb_id()) + rdb_item.add_value(pya.RdbItemValue( points_to_Dpath(points_mult(dpoints, dbu),w1.wg_width) ) ) + + return num_errors + +def list_optical_components(optical_components): + # list all Optical_component objects from an array + # input array, optical_components + # example output: + # X_grating_coupler_1 N$7 N$6 grating_coupler library="custom/genericcml" sch_x=-1.42 sch_y=-0.265 sch_r=0 sch_f=false + + for o in optical_components: + nets_str = "" + for n in o.nets: + nets_str += " N$" + str(n) + flip = ' sch_f=true' if o.flip else '' + if o.rotate > 0: + rotate = ' sch_r=%s' % str(o.rotate) + else: + rotate = '' +# t = '%s %s %s library="%s" lay_x=%s lay_y=%s sch_x=%s sch_y=%s %s%s' % \ +# ( "X"+o.component+"_"+str(o.index), nets_str, o.component, o.library, str (o.x * 1e-6), o.y * 1e-6, o.x, o.y, rotate, flip) + t = ' %s %s %s library="%s" sch_x=%s sch_y=%s %s%s' % \ + ( "X"+o.component+"_"+str(o.index), nets_str, o.component, o.library, eng_str(o.x * 5e-2), eng_str(o.y * 5e-2), rotate, flip) + print (t) + wtext.insertHtml('%s<br>' %t) + +def list_optical_waveguides(list_optical_waveguides): + # list all Optical_waveguides objects from an array + # input array, optical_waveguides + # example output: + # X5 9 10 ebeam_wg_strip_1550 library="Design kits/ebeam_v1.0" wg_length=7.86299e-06 wg_width=5.085e-07 sch_x=-1.42 sch_y=-0.265 + + for o in list_optical_waveguides: + nets_str = "N$%s N$%s" %(o.net1, o.net2) + x,y = xy_mean_mult(o.points, dbu) +# t = '%s %s %s library="%s" wg_length=%s wg_width=%s lay_x=%s lay_y=%s sch_x=%5.3f sch_y=%5.3f' % \ +# ( "Xwg" + str(o.index), nets_str, o.component, o.library, eng_str(o.length*1e-6), eng_str(o.wg_width*1e-6), \ +# eng_str(x * 1e-6), eng_str(y * 1e-6), x, y) + t = ' %s %s %s library="%s" wg_length=%s wg_width=%s sch_x=%s sch_y=%s' % \ + ( "Xwg" + str(o.index), nets_str, o.component, o.library, \ + eng_str(o.length*1e-6), eng_str(o.wg_width*1e-6), eng_str(x * 5e-2), eng_str(y * 5e-2)) + print (t) + wtext.insertHtml('%s<br>' %t) + + def waveguide_set_target_length(): # Function to move the edges of a waveguide to obtain a target length # - Dialog prompts user for a target length, and to click on an edge to select shape and edge to move