def TOP():
    # Get technology and layout details
    from ..utils import get_layout_variables
    TECHNOLOGY, lv, ly, cell = get_layout_variables()
    dbum = TECHNOLOGY['dbu'] * 1e-6  # dbu to m conversion

    # get selected instances; only one
    from ..utils import select_instances
    from .. import _globals

    # print error message if no or more than one component selected
    selected_instances = select_instances()
    error = pya.QMessageBox()
    error.setStandardButtons(pya.QMessageBox.Ok)
    if len(selected_instances) != 1:
        error.setText("Error: Need to have one component selected.")
        response = error.exec_()
        return

    #  text = 'Information on selected components:<br><br>'
    text = ''

    # parse PCell parameters into text string and params array
    for obj in selected_instances:
        #print("  selected component: %s" % obj.inst().cell )
        c = cell.find_components(cell_selected=[obj.inst().cell], verbose=True)
        if c:
            text += c[0].display().replace(';', '<br>&nbsp;&nbsp;&nbsp;')
            if c[0].cell.is_pcell_variant():
                params = c[0].cell.pcell_parameters_by_name()
                for key in params.keys():
                    text += ("Parameter: %s, Value: %s") % (key, params[key])
                    #params.append([key,params[key]])
            text += '<br><br>'
    print(params)

    # check if selected PCell is a contra DC
    if "component: ebeam_contra_dc" not in text:
        error.setText("Error: selected component must be a contra-DC PCell.")
        response = error.exec_()
        return

    # parse into individual variables in meters
    N = params["number_of_periods"]
    period = params["grating_period"] * 1e-6
    dW_1 = params["corrugation_width1"] * 1e-6
    dW_2 = params["corrugation_width2"] * 1e-6
    W_1 = params["wg1_width"] * 1e-6
    W_2 = params["wg2_width"] * 1e-6
    gap = params["gap"] * 1e-6
    a = params["index"]

    if params["sinusoidal"] == False:
        sinusoidal = 0
    else:
        sinusoidal = 1

    from SiEPIC.lumerical.fdtd import generate_CDC_bandstructure
    from SiEPIC.lumerical.mode import find_neff_supermode

    [n_eff1_fit, n_eff2_fit, n_g1_fit,
     n_g2_fit] = find_neff_supermode(W_1, W_2, gap)
    [bandwidth, lambda_0] = generate_CDC_bandstructure(W_1, W_2, dW_1, dW_2,
                                                       period, gap, sinusoidal)
    #[bandwidth, lambda_0] = [7e-9, 1550e-9]

    os.chdir('C:\\Users\\Mustafa\\Desktop')

    # create data files
    dataFile = open("contraDC_params", "w")
    dataFile.write(
        str(gap) + ',' + str(W_1) + ',' + str(W_2) + ',' + str(dW_1) + ',' +
        str(dW_2) + ',' + str(period) + ',' + str(a) + ',' + str(N))

    dataFile = open("contraDC_mode", "w")
    dataFile.write(
        str(n_eff1_fit) + ',' + str(n_eff2_fit) + ',' + str(n_g1_fit) + ',' +
        str(n_g2_fit))

    dataFile = open("contraDC_fdtd", "w")
    dataFile.write(str(bandwidth) + ',' + str(lambda_0))

    #os.
    #os.system('python matlabenginestart.py')
    import subprocess
    p = subprocess.Popen(["start", "cmd", "/k", "python matlabenginestart.py"],
                         shell=True)
示例#2
0
def load_lumapi(verbose=False):
    import pya
    if verbose:
        print("SiEPIC.lumerical.load_lumapi")

    import sys

    try:
        import numpy
    except:
        try:
            import pip
            import pya
            install = pya.MessageBox.warning(
                "Install package?",
                "Install package 'numpy' using pip? [required for Lumerical tools]",
                pya.MessageBox.Yes + pya.MessageBox.No)
            if install == pya.MessageBox.Yes:
                # try installing using pip
                from SiEPIC.install import get_pip_main
                main = get_pip_main()
                main(['install', 'numpy'])
        except ImportError:
            pass

    import os, platform, sys, inspect

    # Load the Lumerical software location from KLayout configuration
    path = pya.Application.instance().get_config(
        'siepic_tools_Lumerical_Python_folder')

    # if it isn't defined, start with Lumerical's defaults
    if not path:
        if platform.system() == 'Darwin':
            path_fdtd = "/Applications/Lumerical/FDTD Solutions/FDTD Solutions.app/Contents/API/Python"
            if os.path.exists(path_fdtd):
                path = path_fdtd
            path_intc = "/Applications/Lumerical/INTERCONNECT/INTERCONNECT.app/Contents/API/Python"
            if os.path.exists(path_intc):
                path = path_intc
        elif platform.system() == 'Linux':
            path_fdtd = "/opt/lumerical/fdtd/api/python"
            if os.path.exists(path_fdtd):
                path = path_fdtd
            path_intc = "/opt/lumerical/interconnect/api/python"
            if os.path.exists(path_intc):
                path = path_intc
        elif platform.system() == 'Windows':
            path_fdtd = "C:\\Program Files\\Lumerical\\FDTD Solutions\\api\\python"
            if os.path.exists(path_fdtd):
                path = path_fdtd
            path_intc = "C:\\Program Files\\Lumerical\\INTERCONNECT\\api\\python"
            if os.path.exists(path_intc):
                path = path_intc
        else:
            print('Not a supported OS')
            return

    # if it is still not found, ask the user
    if not os.path.exists(path):
        print('SiEPIC.lumerical.load_api: Lumerical software not found')
        question = pya.QMessageBox()
        question.setStandardButtons(pya.QMessageBox.Yes | pya.QMessageBox.No)
        question.setDefaultButton(pya.QMessageBox.Yes)
        question.setText(
            "Lumerical software not found. \nDo you wish to locate the software?"
        )
        if (pya.QMessageBox_StandardButton(
                question.exec_()) == pya.QMessageBox.Yes):
            p = pya.QFileDialog()
            p.setFileMode(pya.QFileDialog.DirectoryOnly)
            p.exec_()
            path = p.directory().path
            if verbose:
                print(path)
        else:
            return

    # check if we have the correct path, containing lumapi.py
    if not os.path.exists(os.path.join(path, 'lumapi.py')):
        # check sub-folders for lumapi.py
        import fnmatch
        dir_path = path
        search_str = 'lumapi.py'
        matches = []
        for root, dirnames, filenames in os.walk(dir_path, followlinks=True):
            for filename in fnmatch.filter(filenames, search_str):
                matches.append(root)
        if matches:
            if verbose:
                print(matches)
            path = matches[0]

        if not os.path.exists(os.path.join(path, 'lumapi.py')):
            print('SiEPIC.lumerical.load_api: Lumerical lumapi.py not found')
            warning = pya.QMessageBox()
            warning.setStandardButtons(pya.QMessageBox.Cancel)
            warning.setText("Lumerical's lumapi.py not found.")
            warning.setInformativeText(
                "Some SiEPIC-Tools Lumerical functionality will not be available."
            )
            pya.QMessageBox_StandardButton(warning.exec_())
            return

    # Save the Lumerical software location to the KLayout configuration
    pya.Application.instance().set_config(
        'siepic_tools_Lumerical_Python_folder', path)

    CWD = os.path.dirname(os.path.abspath(__file__))

    if platform.system() == 'Darwin':
        # Check if any Lumerical tools are installed
        ##################################################################
        # Configure OSX Path to include Lumerical tools:

        # Copy the launch control file into user's Library folder
        # execute launctl to register the new paths
        import os, fnmatch
        siepic_tools_lumerical_folder = os.path.dirname(
            os.path.abspath(inspect.getfile(inspect.currentframe())))

        os.environ[
            'PATH'] += ':/Applications/Lumerical/FDTD Solutions/FDTD Solutions.app/Contents/MacOS'
        os.environ[
            'PATH'] += ':/Applications/Lumerical/INTERCONNECT/INTERCONNECT.app/Contents/MacOS'
        os.environ[
            'PATH'] += ':/Applications/Lumerical/INTERCONNECT/INTERCONNECT.app/Contents/API/Python'
        os.environ[
            'PATH'] += ':/Applications/Lumerical/INTERCONNECT/INTERCONNECT.app/Contents/API/Matlab'

        # Also add path for use in the Terminal
        home = os.path.expanduser("~")
        if not os.path.exists(home + "/.bash_profile"):
            text_bash = '\n'
            text_bash += '# Setting PATH for Lumerical API\n'
            text_bash += 'export PATH=/Applications/Lumerical/FDTD\ Solutions/FDTD\ Solutions.app/Contents/MacOS:$PATH\n'
            text_bash += 'export PATH=/Applications/Lumerical/MODE\ Solutions/MODE\ Solutions.app/Contents/MacOS:$PATH\n'
            text_bash += 'export PATH=/Applications/Lumerical/DEVICE/DEVICE.app/Contents/MacOS:$PATH\n'
            text_bash += 'export PATH=/Applications/Lumerical/INTERCONNECT/INTERCONNECT.app/Contents/MacOS:$PATH\n'
            text_bash += '\n'
            file = open(home + "/.bash_profile", 'w')
            file.write(text_bash)
            file.close()

        if not path in sys.path:
            sys.path.append(path)
        # Fix for Lumerical Python OSX API, for < March 5 2018 versions:
        if not os.path.exists(os.path.join(path, 'libinterop-api.1.dylib')):
            lumapi_osx_fix = siepic_tools_lumerical_folder + '/lumapi_osx_fix.bash'
            lumapi_osx_fix_lib = path + '/libinterop-api.so.1'
            if not os.path.exists(lumapi_osx_fix_lib):
                warning = pya.QMessageBox()
                warning.setStandardButtons(pya.QMessageBox.Ok)
                warning.setText(
                    "We need to do a fix in the Lumerical software folder for Python integration. \nPlease note that for this to work, we assume that Lumerical INTERCONNECT is installed in the default path: /Applications/Lumerical/INTERCONNECT/\nPlease enter the following in a Terminal.App window, and enter your root password when prompted. Ok to continue when done."
                )
                warning.setInformativeText("source %s" % lumapi_osx_fix)
                pya.QMessageBox_StandardButton(warning.exec_())
                if not os.path.exists(lumapi_osx_fix_lib):
                    import sys
                    if int(sys.version[0]) > 2:
                        import subprocess
                        subprocess.Popen([
                            '/Applications/Utilities/Terminal.app/Contents/MacOS/Terminal',
                            '-run', lumapi_osx_fix
                        ])
                    else:
                        import commands
                        print(
                            commands.getstatusoutput(
                                '/Applications/Utilities/Terminal.app/Contents/MacOS/Terminal %s'
                                % lumapi_osx_fix))

    # Windows
    elif platform.system() == 'Windows':
        if os.path.exists(path):
            if not path in sys.path:
                sys.path.append(path)  # windows
            os.chdir(path)
    # Linux
    elif platform.system() == 'Linux':
        if os.path.exists(path):
            if not path in sys.path:
                sys.path.append(path)  # windows
            os.chdir(path)

    # for all operating systems:
    from .. import _globals
    if not _globals.LUMAPI:
        try:
            import lumapi
            _globals.LUMAPI = lumapi
        except:
            print('import lumapi failed')
            return

    print('import lumapi success, %s' % _globals.LUMAPI)
    #    _globals.INTC = lumapi.open('interconnect')
    #    _globals.FDTD = lumapi.open('fdtd')

    os.chdir(CWD)
示例#3
0
def component_simulation(verbose=False, simulate=True):
  import sys, os, string
  from .. import _globals

  # get selected instances
  from ..utils import select_instances
  selected_instances = select_instances()

  from ..utils import get_layout_variables
  TECHNOLOGY, lv, ly, cell = get_layout_variables()
    
  
  # check that it is one or more:
  error = pya.QMessageBox()
  error.setStandardButtons(pya.QMessageBox.Ok )
  if len(selected_instances) == 0:
    error.setText("Error: Need to have a component selected.")
    return
  warning = pya.QMessageBox()
  warning.setStandardButtons(pya.QMessageBox.Yes | pya.QMessageBox.Cancel)
  warning.setDefaultButton(pya.QMessageBox.Yes)
  if len(selected_instances) > 1 :
    warning.setText("Warning: More than one component selected.")
    warning.setInformativeText("Do you want to Proceed?")
    if(pya.QMessageBox_StandardButton(warning.exec_()) == pya.QMessageBox.Cancel):
      return
  
  # Check if the component has a compact model loaded in INTERCONNECT
  # Loop if more than one component selected
  for obj in selected_instances:
# *** not working. .returns Flattened.
#    c = obj.inst().cell.find_components()[0]
    if verbose:
      print("  selected component: %s" % obj.inst().cell )
    c = cell.find_components(cell_selected=[obj.inst().cell])
    if c:
      c=c[0]
    else:
      continue
    
    if not c.has_model():
      if len(selected_instances) == 0:
        error.setText("Error: Component '%s' does not have a compact model. Cannot perform simulation." % c)
        continue

    # GUI to ask which pin to inject light into
    pin_names = [p.pin_name for p in c.pins if p.type == _globals.PIN_TYPES.OPTICAL or p.type == _globals.PIN_TYPES.OPTICALIO]
    if not pin_names:
      continue
    pin_injection = pya.InputDialog.ask_item("Pin selection", "Choose one of the pins in component '%s' to inject light into." % c.component, pin_names, 0)
    if not pin_injection:
      return
    if verbose:
      print("Pin selected from InputDialog = %s, for component '%s'." % (pin_injection, c.component) )
    
    # Write spice netlist and simulation script
    from ..utils import get_technology
    TECHNOLOGY = get_technology()  # get current technology
    import SiEPIC
    from time import strftime 
    text_main = '* Spice output from KLayout SiEPIC-Tools v%s, %s technology (SiEPIC.lumerical.interconnect.component_simulation), %s.\n\n' % (SiEPIC.__version__, TECHNOLOGY['technology_name'], strftime("%Y-%m-%d %H:%M:%S") )



    # find electrical IO pins
    electricalIO_pins = ""
    DCsources = "" # string to create DC sources for each pin
    Vn = 1
    # (2) or to individual DC sources
    # create individual sources:
    for p in c.pins:
      if p.type == _globals.PIN_TYPES.ELECTRICAL:
        NetName = " " + c.component +'_' + str(c.idx) + '_' + p.pin_name
        electricalIO_pins += NetName
        DCsources += "N" + str(Vn) + NetName + " dcsource amplitude=0 sch_x=%s sch_y=%s\n" % (-2-Vn/3., -2+Vn/8.)
        Vn += 1
    electricalIO_pins_subckt = electricalIO_pins


    # component nets: must be ordered electrical, optical IO, then optical
    nets_str = ''
    DCsources = "" # string to create DC sources for each pin
    Vn = 1
    for p in c.pins: 
      if p.type == _globals.PIN_TYPES.ELECTRICAL:
        if not p.pin_name:
          continue
        NetName = " " + c.component +'_' + str(c.idx) + '_' + p.pin_name
        nets_str += NetName
        DCsources += "N" + str(Vn) + NetName + " dcsource amplitude=0 sch_x=%s sch_y=%s\n" % (-2-Vn/3., -2+Vn/8.)
        Vn += 1
      if p.type == _globals.PIN_TYPES.OPTICAL or p.type == _globals.PIN_TYPES.OPTICALIO:
        nets_str += " " + str(p.pin_name)


        
    # *** todo: some other way of getting this information; not hard coded.
    # GUI? Defaults from PCell?
    orthogonal_identifier=1
    wavelength_start=1500
    wavelength_stop=1600
    wavelength_points=2000
    text_main += '* Optical Network Analyzer:\n'
    text_main += '.ona input_unit=wavelength input_parameter=start_and_stop\n  + minimum_loss=80\n  + analysis_type=scattering_data\n  + multithreading=user_defined number_of_threads=1\n' 
    text_main += '  + orthogonal_identifier=%s\n' % orthogonal_identifier
    text_main += '  + start=%4.3fe-9\n' % wavelength_start
    text_main += '  + stop=%4.3fe-9\n' % wavelength_stop
    text_main += '  + number_of_points=%s\n' % wavelength_points
    for i in range(0,len(pin_names)):
      text_main += '  + input(%s)=SUBCIRCUIT,%s\n' % (i+1, pin_names[i])
    text_main += '  + output=SUBCIRCUIT,%s\n\n' % (pin_injection)

    text_main += DCsources

    text_main += 'SUBCIRCUIT %s SUBCIRCUIT sch_x=-1 sch_y=-1 \n\n' % (nets_str)
    text_main += '.subckt SUBCIRCUIT %s\n' % (nets_str)
    text_main += ' %s %s %s ' % ( c.component.replace(' ', '_') +"_1", nets_str, c.component.replace(' ', '_') ) 
    if c.library != None:
      text_main += 'library="%s" %s ' % (c.library, c.params)
    text_main += '\n.ends SUBCIRCUIT\n'

    from .. import _globals
    tmp_folder = _globals.TEMP_FOLDER
    import os    
    filename = os.path.join(tmp_folder, '%s_main.spi' % c.component)
    filename2 = os.path.join(tmp_folder, '%s.lsf' % c.component)
    filename_icp = os.path.join(tmp_folder, '%s.icp' % c.component)

    # Write the Spice netlist to file
    file = open(filename, 'w')
    file.write (text_main)
    file.close()
    if verbose:
      print(text_main)

    '''
    # Ask user whether to start a new visualizer, or use an existing one.
    opt_in_labels = [o['opt_in'] for o in opt_in]
    opt_in_labels.insert(0,'All opt-in labels')
    opt_in_selection_text = pya.InputDialog.ask_item("opt_in selection", "Choose one of the opt_in labels, to fetch experimental data.",  opt_in_labels, 0)
    if not opt_in_selection_text: # user pressed cancel
      pass
    '''    

    # Write the Lumerical INTERCONNECT start-up script.
    text_lsf =  'switchtolayout;\n'
    text_lsf +=  "cd('%s');\n" % tmp_folder
    text_lsf += 'deleteall;\n'
    text_lsf += "importnetlist('%s');\n" % filename
    text_lsf += "save('%s');\n" % filename_icp
    text_lsf += 'run;\n'
    if 0:
      for i in range(0, len(pin_names)):
        text_lsf += 'h%s = haveresult("ONA_1", "input %s/mode 1/gain");\n' % (i+1, i+1)
        text_lsf += 'if (h%s>0) { visualize(getresult("ONA_1", "input %s/mode 1/gain")); } \n' % (i+1, i+1)
    if 1:
      text_lsf += 't = "";\n'
      for i in range(0, len(pin_names)):
        text_lsf += 'h%s = haveresult("ONA_1", "input %s/mode 1/gain");\n' % (i+1, i+1)
        text_lsf += 'if (h%s>0) { t%s = getresult("ONA_1", "input %s/mode 1/gain"); t=t+"t%s,"; } \n' % (i+1, i+1, i+1, i+1)
      text_lsf += 't = substring(t, 1, length(t) - 1);\n'
      text_lsf += 'eval("visualize(" + t + ");");\n'

    file = open(filename2, 'w')
    file.write (text_lsf)
    file.close()
    if verbose:
      print(text_lsf)
    
    if simulate:
      # Run using Python integration:
      try: 
        from .. import _globals
        run_INTC()
        # Run using Python integration:
        lumapi = _globals.LUMAPI
        lumapi.evalScript(_globals.INTC, "cd ('" + tmp_folder + "');")
        lumapi.evalScript(_globals.INTC, "feval('"+ c.component + "');\n")
      except:
        from .. import scripts
        scripts.open_folder(tmp_folder)
        INTC_commandline(filename)
    else:
      from .. import scripts
      scripts.open_folder(tmp_folder)
示例#4
0
def Setup_Lumerical_KLayoutPython_integration(verbose=False):
  import sys, os, string, pya

  from ..utils import get_technology, get_technology_by_name
  # get current technology
  TECHNOLOGY = get_technology(query_activecellview_technology=True)  
  # load more technology details (CML file location)
  TECHNOLOGY = get_technology_by_name(TECHNOLOGY['technology_name'])

  # location for the where the CMLs will locally be installed:
  dir_path = os.path.join(pya.Application.instance().application_data_path(), 'Lumerical_CMLs')

  try:
    libraries = []
    library_ids = [n for n in pya.Library.library_ids() if (pya.Library.library_by_id(n).technology == TECHNOLOGY['technology_name'])]
    for n in [pya.Library.library_by_id(l) for l in library_ids]:
      print(n.layout().meta_info_value("path"))
      libraries.append ( n.name() )
  except:
    pass

  if 'INTC_CMLs_path' in TECHNOLOGY:
    question = pya.QMessageBox()
    question.setStandardButtons(pya.QMessageBox.Yes | pya.QMessageBox.No)
    question.setDefaultButton(pya.QMessageBox.Yes)
    question.setText("SiEPIC-Tools will install the Compact Model Library (CML) in Lumerical INTERCONNECT for the currently active technology. \nThis includes the libraries %s.  \nProceed?" % libraries)
    informative_text = "\nTechnology: %s\n" % TECHNOLOGY['technology_name']
    for i in range(0,len(TECHNOLOGY['INTC_CMLs_path'])):
      informative_text += "Source CML file {}: {}\n".format(i+1, TECHNOLOGY['INTC_CMLs_path'][i])
    informative_text += "Install location: %s" % dir_path
    question.setInformativeText(informative_text)
    if(pya.QMessageBox_StandardButton(question.exec_()) == pya.QMessageBox.No):
      return

  
  ##################################################################
  # Load Lumerical API: 
  from .. import _globals
  run_INTC()
  lumapi = _globals.LUMAPI
  if not lumapi:
    print('SiEPIC.lumerical.interconnect.Setup_Lumerical_KLayoutPython_integration: lumapi not loaded')
    return

  import os 
  # Read INTC element library
  lumapi.evalScript(_globals.INTC, "out=library;")
  _globals.INTC_ELEMENTS=lumapi.getVar(_globals.INTC, "out")

  # Install other CMLs for each library that has a path folder
  for i in range(0,len(library_ids)):
    print(' - checking for library: %s' % (pya.Library.library_by_id(library_ids[i]).name()) )
    if 'path' in dir(pya.Library.library_by_id(library_ids[i])):
        path = pya.Library.library_by_id(library_ids[i]).path
        # look for CML files
        print(' - checking for CMLs: %s' % (path) )
        for f in os.listdir(path):
            if f.lower().endswith(".cml"):
                ff = os.path.join(path,f)
                print(' - CML file: %s' % (ff) )
                # install CML
                print("Lumerical INTC, installdesignkit ('%s', '%s', true);" % (ff, dir_path ) )
                lumapi.evalScript(_globals.INTC, "installdesignkit ('%s', '%s', true);" % (ff, dir_path ) )

  # Install technology CML if missing in INTC
  # check if the latest version of the CML is in KLayout's tech
  if 'INTC_CMLs_path' in TECHNOLOGY and TECHNOLOGY['INTC_CML_path']:
    if not ("design kits::"+TECHNOLOGY['technology_name'].lower()+"::"+TECHNOLOGY['INTC_CML_version'].lower().replace('.cml','').lower()) in _globals.INTC_ELEMENTS:
      # install CML
      print("Lumerical INTC, installdesignkit ('%s', '%s', true);" % (TECHNOLOGY['INTC_CML_path'], dir_path ) )
      lumapi.evalScript(_globals.INTC, "installdesignkit ('%s', '%s', true);" % (TECHNOLOGY['INTC_CML_path'], dir_path ) )
    
    # Install other CMLs within technology
    for i in range(0,len(TECHNOLOGY['INTC_CMLs_name'])):
      if not ("design kits::"+TECHNOLOGY['INTC_CMLs_name'][i].lower()+"::"+TECHNOLOGY['INTC_CMLs_version'][i].lower().replace('.cml','').lower()) in _globals.INTC_ELEMENTS:
        # install CML
        print("Lumerical INTC, installdesignkit ('%s', '%s', true);" % (TECHNOLOGY['INTC_CMLs_path'][i], dir_path ) )
        lumapi.evalScript(_globals.INTC, "installdesignkit ('%s', '%s', true);" % (TECHNOLOGY['INTC_CMLs_path'][i], dir_path ) )
        
  # Re-Read INTC element library
  lumapi.evalScript(_globals.INTC, "out=library;")
  _globals.INTC_ELEMENTS=lumapi.getVar(_globals.INTC, "out")
  # Close INTERCONNECT so that the library information is saved, then re-open
  lumapi.close(_globals.INTC)
  run_INTC()

  # Save INTC element library to KLayout application data path
  if not os.path.exists(dir_path):
    os.makedirs(dir_path)
  fh = open(os.path.join(dir_path,"Lumerical_INTC_CMLs.txt"), "w")
  fh.writelines(_globals.INTC_ELEMENTS)
  fh.close()
  
  integration_success_message = "message('KLayout-Lumerical INTERCONNECT integration successful, CML library/libraries:\n"
  for cml_name in TECHNOLOGY['INTC_CMLs_name']:
    integration_success_message += "design kits::"+cml_name.lower()+"\n"
  integration_success_message += "');switchtodesign;\n"
  lumapi.evalScript(_globals.INTC, integration_success_message)

  # instantiate all library elements onto the canvas
  question = pya.QMessageBox()
  question.setStandardButtons(pya.QMessageBox.Yes | pya.QMessageBox.No)
  question.setDefaultButton(pya.QMessageBox.Yes)
  question.setText("Do you wish to see all the components in %s library?" % TECHNOLOGY['technology_name'])
#  question.setInformativeText("Do you wish to see all the components in the library?")
  if(pya.QMessageBox_StandardButton(question.exec_()) == pya.QMessageBox.No):
    # lumapi.evalScript(_globals.INTC, "b=0:0.01:10; plot(b,sin(b),'Congratulations, Lumerical is now available from KLayout','','Congratulations, Lumerical is now available from KLayout');")
    return
  intc_elements = _globals.INTC_ELEMENTS.split('\n')
#  tech_elements = [ e.split('::')[-1] for e in intc_elements if "design kits::"+TECHNOLOGY['technology_name'].lower()+"::" in e ]
  tech_elements = [ e for e in intc_elements if "design kits::"+TECHNOLOGY['technology_name'].lower()+"::" in e ]
  i, x, y, num = 0, 0, 0, len(tech_elements)
  for i in range(0, num):
    lumapi.evalScript(_globals.INTC, "a=addelement('%s'); setposition(a,%s,%s); " % (tech_elements[i],x,y) )
    y += 250
    if (i+1) % int(num**0.5) == 0:
      x += 250
      y = 0
示例#5
0
def Setup_Lumerical_KLayoutPython_integration(verbose=False):
    import sys, os, string

    ##################################################################
    # Load Lumerical API:
    from .. import _globals
    run_INTC()
    lumapi = _globals.LUMAPI
    if not lumapi:
        print(
            'SiEPIC.lumerical.interconnect.Setup_Lumerical_KLayoutPython_integration: lumapi not loaded'
        )
        return

    import os
    # Read INTC element library
    lumapi.evalScript(_globals.INTC, "out=library;")
    _globals.INTC_ELEMENTS = lumapi.getVar(_globals.INTC, "out")

    # Install technology CML if missing in INTC
    dir_path = os.path.join(pya.Application.instance().application_data_path(),
                            'Lumerical_CMLs')
    from ..utils import get_technology, get_technology_by_name
    # get current technology
    TECHNOLOGY = get_technology(query_activecellview_technology=True)
    # load more technology details (CML file location)
    TECHNOLOGY = get_technology_by_name(TECHNOLOGY['technology_name'])
    # check if the latest version of the CML is in KLayout's tech
    if not ("design kits::" + TECHNOLOGY['technology_name'].lower() + "::" +
            TECHNOLOGY['INTC_CML_version'].lower().replace(
                '.cml', '').lower()) in _globals.INTC_ELEMENTS:
        # install CML
        print("Lumerical INTC, installdesignkit ('%s', '%s', true);" %
              (TECHNOLOGY['INTC_CML_path'], dir_path))
        lumapi.evalScript(
            _globals.INTC, "installdesignkit ('%s', '%s', true);" %
            (TECHNOLOGY['INTC_CML_path'], dir_path))
        # Re-Read INTC element library
        lumapi.evalScript(_globals.INTC, "out=library;")
        _globals.INTC_ELEMENTS = lumapi.getVar(_globals.INTC, "out")
        # Close INTERCONNECT so that the library information is saved, then re-open
        lumapi.close(_globals.INTC)
        run_INTC()

    # Save INTC element library to KLayout application data path
    if not os.path.exists(dir_path):
        os.makedirs(dir_path)
    fh = open(os.path.join(dir_path, "Lumerical_INTC_CMLs.txt"), "w")
    fh.writelines(_globals.INTC_ELEMENTS)
    fh.close()

    lumapi.evalScript(
        _globals.INTC,
        "message('KLayout-Lumerical INTERCONNECT integration successful, CML library (%s) is available.');switchtodesign;\n"
        % ("design kits::" + TECHNOLOGY['technology_name'].lower()))

    # instantiate all library elements onto the canvas
    question = pya.QMessageBox()
    question.setStandardButtons(pya.QMessageBox.Yes | pya.QMessageBox.No)
    question.setDefaultButton(pya.QMessageBox.Yes)
    question.setText("Do you wish to see all the components in the library?")
    #  question.setInformativeText("Do you wish to see all the components in the library?")
    if (pya.QMessageBox_StandardButton(
            question.exec_()) == pya.QMessageBox.No):
        # lumapi.evalScript(_globals.INTC, "b=0:0.01:10; plot(b,sin(b),'Congratulations, Lumerical is now available from KLayout','','Congratulations, Lumerical is now available from KLayout');")
        return
    intc_elements = _globals.INTC_ELEMENTS.split('\n')
    #  tech_elements = [ e.split('::')[-1] for e in intc_elements if "design kits::"+TECHNOLOGY['technology_name'].lower()+"::" in e ]
    tech_elements = [
        e for e in intc_elements
        if "design kits::" + TECHNOLOGY['technology_name'].lower() + "::" in e
    ]
    i, x, y, num = 0, 0, 0, len(tech_elements)
    for i in range(0, num):
        lumapi.evalScript(
            _globals.INTC, "a=addelement('%s'); setposition(a,%s,%s); " %
            (tech_elements[i], x, y))
        y += 250
        if (i + 1) % int(num**0.5) == 0:
            x += 250
            y = 0
示例#6
0
def generate_component_sparam(do_simulation=True,
                              addto_CML=True,
                              verbose=False,
                              FDTD_settings=None):
    if verbose:
        print('SiEPIC.lumerical.fdtd: generate_component_sparam()')

    # Get technology and layout details
    from ..utils import get_layout_variables
    TECHNOLOGY, lv, ly, cell = get_layout_variables()
    dbum = TECHNOLOGY['dbu'] * 1e-6  # dbu to m conversion

    # get selected instances; only one
    from ..utils import select_instances
    selected_instances = select_instances()
    error = pya.QMessageBox()
    error.setStandardButtons(pya.QMessageBox.Ok)
    if len(selected_instances) != 1:
        error.setText("Error: Need to have one component selected.")
        response = error.exec_()
        return

    # get selected component
    if verbose:
        print(" selected component: %s" % selected_instances[0].inst().cell)
    component = cell.find_components(
        cell_selected=[selected_instances[0].inst().cell])[0]

    # create an SVG icon for the component, for INTC compact model icon
    from .. import _globals
    import os
    from ..utils import svg_from_component
    svg_filename = os.path.join(_globals.TEMP_FOLDER,
                                '%s.svg' % component.instance)
    if verbose:
        print(" SVG filename: %s" % svg_filename)
    svg_from_component(component, svg_filename)

    # simulation filenames
    fsp_filename = os.path.join(_globals.TEMP_FOLDER,
                                '%s.fsp' % component.instance)
    xml_filename = os.path.join(_globals.TEMP_FOLDER,
                                '%s.xml' % component.instance)
    file_sparam = os.path.join(_globals.TEMP_FOLDER,
                               '%s.dat' % component.instance)

    # get Component pins
    pins = component.find_pins()
    pins = sorted(pins, key=lambda p: p.pin_name)
    for p in pins:
        p.pin_name = p.pin_name.replace(' ', '')  # remove spaces in pin names

    if do_simulation:
        import numpy as np
        # run Lumerical FDTD Solutions
        from .. import _globals
        run_FDTD()
        lumapi = _globals.LUMAPI
        if not lumapi:
            print(
                'SiEPIC.lumerical.fdtd.generate_component_sparam: lumapi not loaded'
            )
            return

        if verbose:
            print(lumapi)  # Python Lumerical INTERCONNECT integration handle

        # get FDTD settings from XML file
        if not FDTD_settings:
            from SiEPIC.utils import load_FDTD_settings
            FDTD_settings = load_FDTD_settings()
            if FDTD_settings:
                if verbose:
                    print(FDTD_settings)

        # Configure wavelength and polarization
        # polarization = {'quasi-TE', 'quasi-TM', 'quasi-TE and -TM'}
        mode_selection = FDTD_settings['mode_selection']
        mode_selection_index = []
        if 'fundamental TE mode' in mode_selection or '1' in mode_selection:
            mode_selection_index.append(1)
        if 'fundamental TM mode' in mode_selection or '2' in mode_selection:
            mode_selection_index.append(2)
        if not mode_selection_index:
            error = pya.QMessageBox()
            error.setStandardButtons(pya.QMessageBox.Ok)
            error.setText("Error: Invalid modes requested.")
            response = error.exec_()
            return

        # wavelength
        wavelength_start = FDTD_settings['wavelength_start']
        wavelength_stop = FDTD_settings['wavelength_stop']

        # get DevRec layer
        devrec_box = component.DevRec_polygon.bbox()
        print("%s, %s, %s, %s" %
              (devrec_box.left * dbum, devrec_box.right * dbum,
               devrec_box.bottom * dbum, devrec_box.top * dbum))

        # create FDTD simulation region (extra large)
        FDTDzspan = FDTD_settings['Initial_FDTD_Z_span']
        if mode_selection_index == 1:
            Z_symmetry = 'Symmetric'
        elif mode_selection_index == 2:
            Z_symmetry = 'Anti-Symmetric'
        else:
            Z_symmetry = FDTD_settings['Initial_Z-Boundary-Conditions']
        FDTDxmin, FDTDxmax, FDTDymin, FDTDymax = (
            devrec_box.left) * dbum - 200e-9, (
                devrec_box.right) * dbum + 200e-9, (
                    devrec_box.bottom) * dbum - 200e-9, (
                        devrec_box.top) * dbum + 200e-9
        sim_time = max(devrec_box.width(), devrec_box.height()) * dbum * 4.5
        lumapi.evalScript(_globals.FDTD, " \
      newproject; \
      addfdtd; set('x min',%s); set('x max',%s); set('y min',%s); set('y max',%s); set('z span',%s);\
      set('force symmetric z mesh', 1); set('mesh accuracy',1); \
      set('x min bc','Metal'); set('x max bc','Metal'); \
      set('y min bc','Metal'); set('y max bc','Metal'); \
      set('z min bc','%s'); set('z max bc','%s'); \
      setglobalsource('wavelength start',%s); setglobalsource('wavelength stop', %s); \
      setglobalmonitor('frequency points',%s); set('simulation time', %s/c+400e-15); \
      addmesh; set('override x mesh',0); set('override y mesh',0); set('override z mesh',1); set('z span', 0); set('dz', %s); set('z', %s); \
      ?'FDTD solver with mesh override added'; "                                                     % ( FDTDxmin,FDTDxmax,FDTDymin,FDTDymax,FDTDzspan, \
             Z_symmetry, FDTD_settings['Initial_Z-Boundary-Conditions'], \
             wavelength_start,wavelength_stop, \
             FDTD_settings['frequency_points_monitor'], sim_time, \
             FDTD_settings['thickness_Si']/4, FDTD_settings['thickness_Si']/2) )

        # add substrate and cladding:
        lumapi.evalScript(_globals.FDTD, " \
      addrect; set('x min',%s); set('x max',%s); set('y min',%s); set('y max',%s); set('z min', %s); set('z max',%s);\
      set('material', '%s'); set('alpha',0.1);    \
      ?'oxide added';    "                               % (FDTDxmin-1e-6, FDTDxmax+1e-6, FDTDymin-1e-6, FDTDymax+1e-6, \
            -FDTD_settings['thickness_BOX']-FDTD_settings['thickness_Si']/2, -FDTD_settings['thickness_Si']/2, \
            FDTD_settings['material_Clad'] ) )
        lumapi.evalScript(_globals.FDTD, " \
      addrect; set('x min',%s); set('x max',%s); set('y min',%s); set('y max',%s); set('z min', %s); set('z max',%s);\
      set('material', '%s'); set('alpha',0.1);    \
      ?'oxide added';    "                               % (FDTDxmin-1e-6, FDTDxmax+1e-6, FDTDymin-1e-6, FDTDymax+1e-6, \
            -FDTD_settings['thickness_Si']/2, FDTD_settings['thickness_Clad']-FDTD_settings['thickness_Si']/2, \
            FDTD_settings['material_Clad'] ) )

        # get polygons from component
        polygons = component.get_polygons()
        if verbose:
            print(" polygons: %s" % [p for p in polygons])
        polygons_vertices = [[
            [vertex.x * dbum, vertex.y * dbum] for vertex in p.each_point()
        ] for p in [p.to_simple_polygon() for p in polygons]]
        if verbose:
            print(" polygons' vertices: %s" % len(polygons_vertices))
        if len(polygons_vertices) < 1:
            error = pya.QMessageBox()
            error.setStandardButtons(pya.QMessageBox.Ok)
            error.setText("Error: Component needs to have polygons.")
            response = error.exec_()
            return

        # send polygons to FDTD
        for i in range(0, len(polygons_vertices)):
            lumapi.putMatrix(_globals.FDTD, "polygon_vertices",
                             np.array(polygons_vertices[i]))
            lumapi.evalScript(
                _globals.FDTD, " \
        addpoly; set('vertices',polygon_vertices); \
        set('material', '%s'); set('z span', %s);     \
        ?'Polygons added'; " %
                (FDTD_settings['material_Si'], FDTD_settings['thickness_Si']))

        # create FDTD ports
        # configure boundary conditions to be PML where we have ports
    #  FDTD_bc = {'y max bc': 'Metal', 'y min bc': 'Metal', 'x max bc': 'Metal', 'x min bc': 'Metal'}
        port_dict = {
            0.0: 'x max bc',
            90.0: 'y max bc',
            180.0: 'x min bc',
            -90.0: 'y min bc'
        }
        for p in pins:
            if p.rotation in [180.0, 0.0]:
                lumapi.evalScript(
                    _globals.FDTD, " \
          addport; set('injection axis', 'x-axis'); set('x',%s); set('y',%s); set('y span',%s); set('z span',%s); \
          " % (p.center.x * dbum, p.center.y * dbum, 2e-6, FDTDzspan))
            if p.rotation in [270.0, 90.0, -90.0]:
                lumapi.evalScript(
                    _globals.FDTD, " \
          addport; set('injection axis', 'y-axis'); set('x',%s); set('y',%s); set('x span',%s); set('z span',%s); \
          " % (p.center.x * dbum, p.center.y * dbum, 2e-6, FDTDzspan))
            if p.rotation in [0.0, 90.0]:
                p.direction = 'Backward'
            else:
                p.direction = 'Forward'
            lumapi.evalScript(_globals.FDTD, " \
        set('name','%s'); set('direction', '%s'); set('frequency points', %s); updateportmodes(%s); \
        select('FDTD'); set('%s','PML'); \
        ?'Added pin: %s, set %s to PML'; "                                                 % (p.pin_name, p.direction, 1, mode_selection_index, \
                  port_dict[p.rotation], p.pin_name, port_dict[p.rotation] )  )

        # Calculate mode sources
        # Get field profiles, to find |E| = 1e-6 points to find spans
        import sys
        if not 'win' in sys.platform:  # Windows getVar ("E") doesn't work.
            min_z, max_z = 0, 0
            for p in [pins[0]]:  # if all pins are the same, only do it once
                for m in mode_selection_index:
                    lumapi.evalScript(
                        _globals.FDTD, " \
            select('FDTD::ports::%s'); mode_profiles=getresult('FDTD::ports::%s','mode profiles'); E=mode_profiles.E%s; x=mode_profiles.x; y=mode_profiles.y; z=mode_profiles.z; \
            ?'Selected pin: %s'; " % (p.pin_name, p.pin_name, m, p.pin_name))
                    E = lumapi.getVar(_globals.FDTD, "E")
                    x = lumapi.getVar(_globals.FDTD, "x")
                    y = lumapi.getVar(_globals.FDTD, "y")
                    z = lumapi.getVar(_globals.FDTD, "z")

                    # remove the wavelength from the array,
                    # leaving two dimensions, and 3 field components
                    if p.rotation in [180.0, 0.0]:
                        Efield_xyz = np.array(E[0, :, :, 0, :])
                    else:
                        Efield_xyz = np.array(E[:, 0, :, 0, :])
                    # find the field intensity (|Ex|^2 + |Ey|^2 + |Ez|^2)
                    Efield_intensity = np.empty(
                        [Efield_xyz.shape[0], Efield_xyz.shape[1]])
                    print(Efield_xyz.shape)
                    for a in range(0, Efield_xyz.shape[0]):
                        for b in range(0, Efield_xyz.shape[1]):
                            Efield_intensity[a, b] = abs(
                                Efield_xyz[a, b, 0])**2 + abs(
                                    Efield_xyz[a, b, 1])**2 + abs(
                                        Efield_xyz[a, b, 2])**2
                    # find the max field for each z slice (b is the z axis)
                    Efield_intensity_b = np.empty([Efield_xyz.shape[1]])
                    for b in range(0, Efield_xyz.shape[1]):
                        Efield_intensity_b[b] = max(Efield_intensity[:, b])
                    # find the z thickness where the field has sufficiently decayed
                    indexes = np.argwhere(
                        Efield_intensity_b >
                        FDTD_settings['Efield_intensity_cutoff_eigenmode'])
                    min_index, max_index = int(min(indexes)), int(max(indexes))
                    if min_z > z[min_index]:
                        min_z = z[min_index]
                    if max_z < z[max_index]:
                        max_z = z[max_index]
                    if verbose:
                        print(
                            ' Port %s, mode %s field decays at: %s, %s microns'
                            % (p.pin_name, m, z[max_index], z[min_index]))

                if FDTDzspan > max_z - min_z:
                    FDTDzspan = float(max_z - min_z)
                    if verbose:
                        print(' Updating FDTD Z-span to: %s microns' %
                              (FDTDzspan))

        # Configure FDTD region, mesh accuracy 1
        # run single simulation
        lumapi.evalScript(
            _globals.FDTD, " \
      select('FDTD'); set('z span',%s);\
      save('%s');\
      ?'FDTD Z-span updated to %s'; " % (FDTDzspan, fsp_filename, FDTDzspan))

        # Calculate, plot, and get the S-Parameters, S21, S31, S41 ...
        # optionally simulate a subset of the S-Parameters
        # assume input on port 1
        # return Sparams: last mode simulated [wavelength, pin out index], and
        #        Sparams_modes: all modes [mode, wavelength, pin out index]
        def FDTD_run_Sparam_simple(pins,
                                   in_pin=None,
                                   out_pins=None,
                                   modes=[1],
                                   plots=False):
            if verbose:
                print(' Run simulation S-Param FDTD')
            Sparams_modes = []
            if not in_pin:
                in_pin = pins[0]
            for m in modes:
                lumapi.evalScript(
                    _globals.FDTD, "\
          switchtolayout; select('FDTD::ports');\
          set('source port','%s');\
          set('source mode','mode %s');\
          run; " % (in_pin.pin_name, m))
                port_pins = [in_pin] + out_pins if out_pins else pins
                for p in port_pins:
                    if verbose:
                        print(' port %s expansion' % p.pin_name)
                    lumapi.evalScript(
                        _globals.FDTD, " \
            P=Port_%s=getresult('FDTD::ports::%s','expansion for port monitor'); \
             " % (p.pin_name, p.pin_name))
                lumapi.evalScript(_globals.FDTD, "wavelengths=c/P.f*1e6;")
                wavelengths = lumapi.getVar(_globals.FDTD, "wavelengths")
                Sparams = []
                for p in port_pins[1::]:
                    if verbose:
                        print(' S_%s_%s Sparam' %
                              (p.pin_name, in_pin.pin_name))
                    lumapi.evalScript(_globals.FDTD, " \
            Sparam=S_%s_%s= Port_%s.%s/Port_%s.%s;  \
             "                         % (p.pin_name, in_pin.pin_name, \
                            p.pin_name, 'b' if p.direction=='Forward' else 'a', \
                            in_pin.pin_name, 'a' if in_pin.direction=='Forward' else 'b') )
                    Sparams.append(lumapi.getVar(_globals.FDTD, "Sparam"))
                    if plots:
                        if verbose:
                            print(' Plot S_%s_%s Sparam' %
                                  (p.pin_name, in_pin.pin_name))
                        lumapi.evalScript(
                            _globals.FDTD, " \
              plot (wavelengths, 10*log10(abs(Sparam(:,%s))^2),  'Wavelength (um)', 'Transmission (dB)', 'S_%s_%s, mode %s'); \
               " % (modes.index(m) + 1, p.pin_name, in_pin.pin_name,
                        modes.index(m) + 1))
                Sparams_modes.append(Sparams)
            return Sparams, Sparams_modes

        # Run the first FDTD simulation
        in_pin = pins[0]
        Sparams, Sparams_modes = FDTD_run_Sparam_simple(
            pins, in_pin=in_pin, modes=mode_selection_index, plots=True)

        # find the pin that has the highest Sparam (max over 1-wavelength and 2-modes)
        # use this Sparam for convergence testing
        # use the highest order mode for the convergence testing and reporting IL values.
        Sparam_pin_max_modes = []
        Mean_IL_best_port = []  # one for each mode
        for m in mode_selection_index:
            Sparam_pin_max_modes.append(
                np.amax(np.absolute(
                    np.array(Sparams)[:, :,
                                      mode_selection_index.index(m)]),
                        axis=1).argmax() + 1)
            Mean_IL_best_port.append(-10 * np.log10(
                np.mean(
                    np.absolute(Sparams)[Sparam_pin_max_modes[-1] - 1, :,
                                         mode_selection_index.index(m)])**2))

        # user verify ok?
        warning = pya.QMessageBox()
        warning.setStandardButtons(pya.QMessageBox.Yes
                                   | pya.QMessageBox.Cancel)
        warning.setDefaultButton(pya.QMessageBox.Yes)
        info_text = "First FDTD simulation complete (coarse mesh, lowest accuracy). Highest transmission S-Param: \n"
        for m in mode_selection_index:
            info_text += "mode %s, S_%s_%s has %s dB average insertion loss\n" % (
                m, pins[Sparam_pin_max_modes[mode_selection_index.index(
                    m)]].pin_name, in_pin.pin_name,
                Mean_IL_best_port[mode_selection_index.index(m)])
        warning.setInformativeText(info_text)
        warning.setText("Do you want to Proceed?")
        if verbose:
            print(info_text)
        else:
            if (pya.QMessageBox_StandardButton(
                    warning.exec_()) == pya.QMessageBox.Cancel):
                return

        # Convergence testing on S-Parameters:
        # convergence test on simulation z-span (assume symmetric increases)
        # loop in Python so we can check if it is good enough
        # use the highest order mode
        mode_convergence = [mode_selection_index[-1]]
        Sparam_pin_max = Sparam_pin_max_modes[-1]
        if FDTD_settings['convergence_tests']:
            test_converged = False
            convergence = []
            Sparams_abs_prev = np.array(
                [np.absolute(Sparams)[Sparam_pin_max - 1, :, :]])
            while not test_converged:
                FDTDzspan += FDTD_settings['convergence_test_span_incremement']
                lumapi.evalScript(
                    _globals.FDTD, " \
          switchtolayout; select('FDTD'); set('z span',%s);\
          " % (FDTDzspan))
                Sparams, Sparams_modes = FDTD_run_Sparam_simple(
                    pins,
                    in_pin=in_pin,
                    out_pins=[pins[Sparam_pin_max]],
                    modes=mode_convergence,
                    plots=True)
                Sparams_abs = np.array(np.absolute(Sparams))
                rms_error = np.sqrt(
                    np.mean((Sparams_abs_prev - Sparams_abs)**2))
                convergence.append([FDTDzspan, rms_error])
                Sparams_abs_prev = Sparams_abs
                if verbose:
                    print(' convergence: span and rms error %s' %
                          convergence[-1])
                lumapi.evalScript(
                    _globals.FDTD, " \
          ?'FDTD Z-span: %s, rms error from previous: %s (convergence testing until < %s)'; "
                    % (FDTDzspan, rms_error,
                       FDTD_settings['convergence_test_rms_error_limit']))
                if rms_error < FDTD_settings[
                        'convergence_test_rms_error_limit']:
                    test_converged = True
                    FDTDzspan += -1 * FDTD_settings[
                        'convergence_test_span_incremement']
                # check if the last 3 points have reducing rms
                if len(convergence) > 2:
                    test_rms = np.polyfit(
                        np.array(convergence)[-3:, 0],
                        np.array(convergence)[-3:, 1], 1)
                    if verbose:
                        print('  convergence rms trend: %s; fit data: %s' %
                              (test_rms, np.array(convergence)[:, -3:]))
                    if test_rms[0] > 0:
                        if verbose:
                            print(
                                ' convergence problem, not improving rms. terminating convergence test.'
                            )
                        lumapi.evalScript(
                            _globals.FDTD,
                            "?'convergence problem, not improving rms. terminating convergence test.'; "
                        )
                        test_converged = True
                        FDTDzspan += -2 * FDTD_settings[
                            'convergence_test_span_incremement']

            lumapi.putMatrix(_globals.FDTD, 'convergence', convergence)
            lumapi.evalScript(
                _globals.FDTD,
                "plot(convergence(:,1), convergence(:,2), 'Simulation span','RMS error between simulation','Convergence testing');"
            )

        # Perform quick corner analysis
        if FDTD_settings['Perform-quick-corner-analysis']:
            pass

        # Configure FDTD region, higher mesh accuracy, update FDTD ports mode source frequency points
        lumapi.evalScript(
            _globals.FDTD, " \
      switchtolayout; select('FDTD'); set('mesh accuracy',%s);\
      set('z min bc','%s'); set('z max bc','%s'); \
      ?'FDTD mesh accuracy updated %s, Z boundary conditions: %s'; " %
            (FDTD_settings['mesh_accuracy'],
             FDTD_settings['Z-Boundary-Conditions'],
             FDTD_settings['Z-Boundary-Conditions'],
             FDTD_settings['mesh_accuracy'],
             FDTD_settings['Z-Boundary-Conditions']))
        for p in pins:
            lumapi.evalScript(
                _globals.FDTD, " \
        select('FDTD::ports::%s'); set('frequency points', %s); \
        ?'updated pin: %s'; " %
                (p.pin_name, FDTD_settings['frequency_points_expansion'],
                 p.pin_name))

        # Run full S-parameters
        # add s-parameter sweep task
        lumapi.evalScript(
            _globals.FDTD, " \
      deletesweep('s-parameter sweep'); \
      addsweep(3); \
      NPorts=%s; \
      " % (len(pins)))
        for p in pins:
            for m in mode_selection_index:
                # add index entries to s-matrix mapping table
                lumapi.evalScript(
                    _globals.FDTD, " \
          index1 = struct; \
          index1.Port = '%s'; \
          index1.Mode = 'mode %s'; \
          addsweepparameter('s-parameter sweep',index1); \
        " % (p.pin_name, m))

        # run s-parameter sweep, collect results, visualize results
        # export S-parameter data to file named xxx.dat to be loaded in INTERCONNECT
        lumapi.evalScript(
            _globals.FDTD, " \
      runsweep('s-parameter sweep'); \
      S_matrix = getsweepresult('s-parameter sweep','S matrix'); \
      S_parameters = getsweepresult('s-parameter sweep','S parameters'); \
      S_diagnostic = getsweepresult('s-parameter sweep','S diagnostic'); \
      visualize(S_parameters); \
      exportsweep('s-parameter sweep','%s'); \
      " % (file_sparam))
        if verbose:
            print(" S-Parameter file: %s" % file_sparam)

        # Perform final corner analysis, for Monte Carlo simulations
        if FDTD_settings['Perform-final-corner-analysis']:
            pass

        # Write XML file for INTC scripted compact model
        # height and width are set to the first pin width/height
        xml_out = '\
      <?xml version="1.0" encoding="UTF-8"?> \n\
      <lumerical_lookup_table version="1.0" name = "index_table"> \n\
      <association> \n\
          <design> \n\
              <value name="height" type="double">%s</value> \n\
              <value name="width" type="double">%s</value> \n\
          </design> \n\
          <extracted> \n\
              <value name="sparam" type="string">%s</value> \n\
          </extracted> \n\
      </association>\n' % (FDTD_settings['thickness_Si'],
                           pins[0].path.width * dbum, component.instance)
        fh = open(xml_filename, "w")
        fh.writelines(xml_out)
        fh.close()

        if verbose:
            print(" XML file: %s" % xml_filename)

    if addto_CML:
        # Run using Python integration:
        from . import interconnect
        interconnect.run_INTC()
        from .. import _globals
        lumapi = _globals.LUMAPI

        # Copy files to the INTC Custom library folder
        lumapi.evalScript(_globals.INTC, "out=customlibrary;")
        INTC_custom = lumapi.getVar(_globals.INTC, "out")

        # Create a component
        port_dict2 = {
            0.0: 'Right',
            90.0: 'Top',
            180.0: 'Left',
            -90.0: 'Bottom'
        }
        t = 'switchtodesign; deleteall; \n'
        t += 'addelement("Optical N Port S-Parameter"); createcompound; select("COMPOUND_1");\n'
        t += 'component = "%s"; set("name",component); \n' % component.instance
        import os
        if os.path.exists(svg_filename):
            t += 'seticon(component,"%s");\n' % (svg_filename)
        else:
            print(" SiEPIC.lumerical.fdtd.component... missing SVG icon: %s" %
                  svg_filename)
        t += 'select(component+"::SPAR_1"); set("load from file", true);\n'
        t += 'set("s parameters filename", "%s");\n' % (file_sparam)
        t += 'set("load from file", false);\n'
        t += 'set("passivity", "enforce");\n'
        t += 'set("prefix", component);\n'
        t += 'setposition(component+"::SPAR_1",100,-100);\n'
        count = 0
        for p in pins:
            count += 1
            if p.rotation in [0.0, 180.0]:
                location = 1 - (p.center.y -
                                component.DevRec_polygon.bbox().bottom +
                                0.) / component.DevRec_polygon.bbox().height()
    #      print(" p.y %s, c.bottom %s, location %s: " % (p.center.y,component.polygon.bbox().bottom, location) )
            else:
                location = (p.center.x - component.DevRec_polygon.bbox().left +
                            0.) / component.DevRec_polygon.bbox().width()
                print(location)
            t += 'addport(component, "%s", "Bidirectional", "Optical Signal", "%s",%s);\n' % (
                p.pin_name, port_dict2[p.rotation], location)
            t += 'connect(component+"::RELAY_%s", "port", component+"::SPAR_1", "port %s");\n' % (
                count, count)
        t += 'select(component); addtolibrary("SiEPIC_user",true);\n'
        t += '?"created and added " + component + " to library SiEPIC_user";\n'
        lumapi.evalScript(_globals.INTC, t)

        # Script for the component, to load S-Param data:
        t = '###############################################\n'
        t += '# SiEPIC ebeam compact model library (CML)\n'
        t += '# custom generated component created by SiEPIC-Tools; script by Zeqin Lu, Xu Wang, Lukas Chrostowski\n'
        t += '?filename = %local path%+"/source_data/' + '%s/%s.xml";\n' % (
            component.instance, component.instance)

        # Monte Carlo part:
        '''
    if (MC_non_uniform==1) {
    
        x=%x coordinate%;
        y=%y coordinate%;
    
        x1_wafer = floor(x/MC_grid); # location of component on the wafer map
        y1_wafer = floor(y/MC_grid);
    
        devi_width = MC_uniformity_width(MC_resolution_x/2 + x1_wafer, MC_resolution_y/2 + y1_wafer)*1e-9;
        devi_thickness = MC_uniformity_thickness(MC_resolution_x/2 + x1_wafer, MC_resolution_y/2 + y1_wafer)*1e-9;                     
    
        initial_width = 500e-9;
        initial_thickness = 220e-9;
    
        waveguide_width = initial_width + devi_width;  # [m]
        waveguide_thickness = initial_thickness + devi_thickness; # [m]
    
    
        # effective index and group index interpolations
        # The following built-in script interpolates effective index (neff), group index (ng), and dispersion, 
        # and applies the interpolated results to the waveguide. 
    
        filename = %local path%+"/source_data/y_branch_source/y_lookup_table.xml";
        table = "index_table";
    
        design = cell(2);
        extracted = cell(1);
    
        #design (input parameters)
        design{1} = struct;
        design{1}.name = "width";
        design{1}.value = waveguide_width;
        design{2} = struct;
        design{2}.name = "height";  
        design{2}.value = waveguide_thickness; 
    
       M = lookupreadnportsparameter(filename, table, design, "y_sparam");
    
       setvalue('SPAR_1','s parameters',M);
    
    }
    else {
        filename = %local path%+"/source_data/y_branch_source/y_lookup_table.xml";
        table = "index_table";
    
        design = cell(2);
        extracted = cell(1);
    
        #design (input parameters)
        design{1} = struct;
        design{1}.name = "width";
        design{1}.value = 500e-9;
        design{2} = struct;
        design{2}.name = "height";  
        design{2}.value = 220e-9; 
    
       M = lookupreadnportsparameter(filename, table, design, "y_sparam");
    
       setvalue('SPAR_1','s parameters',M);
        
    }
    
    '''

    return component.instance, file_sparam, [], xml_filename, svg_filename
示例#7
0
def generate_GC_sparam(do_simulation=True,
                       addto_CML=True,
                       verbose=False,
                       FDTD_settings=None,
                       GC_settings=None):
    if verbose:
        print('SiEPIC.lumerical.fdtd: generate_GC_sparam()')

    # Get technology and layout details
    from ..utils import get_layout_variables
    TECHNOLOGY, lv, ly, cell = get_layout_variables()
    dbum = TECHNOLOGY['dbu'] * 1e-6  # dbu to m conversion

    if do_simulation:
        import numpy as np
        # run Lumerical FDTD Solutions
        from .. import _globals
        run_FDTD()
        lumapi = _globals.LUMAPI
        if not lumapi:
            print(
                'SiEPIC.lumerical.fdtd.generate_component_sparam: lumapi not loaded'
            )
            return

        if verbose:
            print(lumapi)  # Python Lumerical INTERCONNECT integration handle

        # get FDTD settings from XML file
        if not FDTD_settings:
            from SiEPIC.utils import load_FDTD_settings
            FDTD_settings = load_FDTD_settings()
            if FDTD_settings:
                if verbose:
                    print(FDTD_settings)

        # get GC settings from XML file
        if not GC_settings:
            from SiEPIC.utils import load_GC_settings
            GC_settings = load_GC_settings()
            if GC_settings:
                if verbose:
                    print(GC_settings)

        # Configure wavelength and polarization
        # polarization = {'quasi-TE', 'quasi-TM', 'quasi-TE and -TM'}
        mode_selection = FDTD_settings['mode_selection']
        mode_selection_index = []
        if 'fundamental TE mode' in mode_selection or '1' in mode_selection:
            mode_selection_index.append(1)
        if 'fundamental TM mode' in mode_selection or '2' in mode_selection:
            mode_selection_index.append(2)
        if not mode_selection_index:
            error = pya.QMessageBox()
            error.setStandardButtons(pya.QMessageBox.Ok)
            error.setText("Error: Invalid modes requested.")
            response = error.exec_()

        # wavelength
        wavelength_start = FDTD_settings['wavelength_start']
        wavelength_stop = FDTD_settings['wavelength_stop']

        # create FDTD simulation region (extra large)
        FDTDzspan = FDTD_settings['Initial_FDTD_Z_span']
        if mode_selection_index == 1:
            Z_symmetry = 'Symmetric'
        elif mode_selection_index == 2:
            Z_symmetry = 'Anti-Symmetric'
        else:
            Z_symmetry = FDTD_settings['Initial_Z-Boundary-Conditions']

        # search for 2D GC tempelate project file in technology
        from ..utils import get_technology
        TECHNOLOGY = get_technology()
        tech_name = TECHNOLOGY['technology_name']

        import os, fnmatch
        dir_path = pya.Application.instance().application_data_path()
        search_str = 'grating_coupler_2D.fsp'
        matches = []
        for root, dirnames, filenames in os.walk(dir_path, followlinks=True):
            for filename in fnmatch.filter(filenames, search_str):
                if tech_name in root:
                    matches.append(os.path.join(root, filename))

        filename = matches[0]
        '''
    # set 2D GC geometry parameters
    lumapi.evalScript(_globals.FDTD, 
    "load('%s'); select('grating_coupler_2D');\
    set('duty cycle',%s); set('target length',%s);\
    set('etch depth',%s); set('pitch',%s);\
    set('target length',%s); set('input length',%s);'" 
    % (filename, GC_settings['duty_cycle'], GC_settings['target_length'], GC_settings['etch_depth'],
    GC_settings['pitch'],GC_settings['target_length'],GC_settings['L_extra']))
    '''
        # set 2D GC geometry parameters
        lumapi.evalScript(
            _globals.FDTD, "load('%s'); select('grating_coupler_2D');\
    set('duty cycle',%s); set('target length',%s);\
    set('etch depth',%s); set('pitch',%s);\
    set('input length',%s);" %
            (filename, GC_settings['duty_cycle'], GC_settings['target_length'],
             GC_settings['etch_depth'], GC_settings['pitch'],
             GC_settings['length_extra']))

        # set fiber angle
        lumapi.evalScript(
            _globals.FDTD,
            "select('fiber'); set('theta0',%s);" % (GC_settings['angle']))

        # set polarization
        if GC_settings['polarization'] == 'TE':
            polarization = '2'
        elif GC_settings['polarization'] == 'TM':
            polarization = '3'
        lumapi.evalScript(
            _globals.FDTD, "select('FDTD::ports::port 1');\
    set('mode selection',%s);" % polarization)
        lumapi.evalScript(
            _globals.FDTD, "select('FDTD::ports::port 2');\
    set('mode selection',%s);" % polarization)

        # run s-parameters sweep
        lumapi.evalScript(_globals.FDTD, "runsweep('s-parameter sweep');")

        if GC_settings['particle_swarm_optimization'] == 'yes':
            # run optimization (PSO) to find optimal  duty cycle and length
            lumapi.evalScript(_globals.FDTD, "runsweep('optimization');")
            # return optimized results from PSO sweep
            lumapi.evalScript(
                _globals.FDTD,
                "bestParams = getsweepdata('optimization','bestParams');\
      pitch = bestParams(1); duty_cycle = bestParams(2);")
            GC_settings['duty_cycle'] = lumapi.getVar(_globals.FDTD,
                                                      "duty_cycle")
            GC_settings['pitch'] = lumapi.getVar(_globals.FDTD, "pitch")

        # run 3D FDTD simulation
        dir_path = pya.Application.instance().application_data_path()
        search_str = 'grating_coupler_3D.fsp'
        matches = []
        for root, dirnames, filenames in os.walk(dir_path, followlinks=True):
            for filename in fnmatch.filter(filenames, search_str):
                if tech_name in root:
                    matches.append(os.path.join(root, filename))

        filename = matches[0]

        # set 2D GC geometry parameters
        lumapi.evalScript(
            _globals.FDTD,
            "load('%s'); select('grating_coupler_3D'); set('duty cycle',%s);\
    set('etch depth',%s); set('pitch',%s);\
    set('target length',%s); set('L extra',%s);\
    set('radius',%s); set('y span',%s);\
    set('waveguide width',%s); set('waveguide length,%s);'" %
            (filename, GC_settings['duty_cycle'], GC_settings['etch_depth'],
             GC_settings['pitch'], GC_settings['target_length'],
             GC_settings['length_extra'], GC_settings['radius'],
             GC_settings['y_span'], GC_settings['waveguide_width'],
             GC_settings['waveguide_length']))

        # set polarization, update port monitors
        lumapi.evalScript(
            _globals.FDTD,
            "select('FDTD::ports::port 1'); set('mode selection',%s);\
    updateportmodes; select('FDTD::ports::port 2');\
    set('mode selection',%s); updateportmodes;" % (polarization, polarization))

        file_sparam = os.path.join(_globals.TEMP_FOLDER,
                                   '%s.dat' % "GC_sparams")
        # run s-parameter sweep, collect results, visualize results
        # export S-parameter data to file named xxx.dat to be loaded in INTERCONNECT
        lumapi.evalScript(
            _globals.FDTD, " \
      runsweep('s-parameter sweep'); \
      S_matrix = getsweepresult('s-parameter sweep','S matrix'); \
      S_parameters = getsweepresult('s-parameter sweep','S parameters'); \
      S_diagnostic = getsweepresult('s-parameter sweep','S diagnostic'); \
      visualize(S_parameters); \
      exportsweep('s-parameter sweep','%s'); \
      " % (file_sparam))
        if verbose:
            print(" S-Parameter file: %s" % file_sparam)

        # Run INTC using Python integration:
        from . import interconnect
        interconnect.run_INTC()
        from .. import _globals
        lumapi = _globals.LUMAPI

        # Copy files to the INTC Custom library folder
        lumapi.evalScript(_globals.INTC, "out=customlibrary;")
        INTC_custom = lumapi.getVar(_globals.INTC, "out")

        # Create a component
        port_dict2 = {
            0.0: 'Right',
            90.0: 'Top',
            180.0: 'Left',
            -90.0: 'Bottom'
        }
        t = 'switchtodesign; deleteall; \n'
        t += 'addelement("Optical N Port S-Parameter"); createcompound; select("COMPOUND_1");\n'
        t += 'component = "%s"; set("name",component); \n' % "GC_sparams"
        t += 'select(component+"::SPAR_1"); set("load from file", true);\n'
        t += 'set("s parameters filename", "%s");\n' % (file_sparam)
        t += 'set("load from file", false);\n'
        t += 'set("passivity", "enforce");\n'
        t += 'set("prefix", component);\n'
        t += 'setposition(component+"::SPAR_1",100,-100);\n'

        lumapi.evalScript(_globals.INTC, t)
示例#8
0
def INTC_commandline(filename2):
    print("Running Lumerical INTERCONNECT using the command interface.")
    import sys, os, string

    if sys.platform.startswith('linux'):
        import subprocess
        # Linux-specific code here...
        print("Running INTERCONNECT")
        # Location of INTERCONNECT program (this found from RPM installation)
        file_path = '/opt/lumerical/interconnect/bin/interconnect'
        subprocess.Popen([file_path, '-run', filename2])

    elif sys.platform.startswith('darwin'):
        # OSX specific
        import sys
        if int(sys.version[0]) > 2:
            import subprocess
            subprocess.Popen([
                '/usr/bin/open -n /Applications/Lumerical/INTERCONNECT/INTERCONNECT.app',
                '-run',
                '--args -run %s' % filename2
            ])
        else:
            import commands
            print("Running INTERCONNECT")
            runcmd = (
                'source ~/.bash_profile; /usr/bin/open -n /Applications/Lumerical/INTERCONNECT/INTERCONNECT.app --args -run %s'
                % filename2)
            print("Running in shell: %s" % runcmd)
            a = commands.getstatusoutput(runcmd)
            print(a)

    elif sys.platform.startswith('win'):
        # Windows specific code here
        import subprocess
        print("Running INTERCONNECT")
        #check Interconnect installation directory
        file_path_a = 'C:\\Program Files\\Lumerical\\INTERCONNECT\\bin\\interconnect.exe'
        file_path_b = 'C:\\Program Files (x86)\\Lumerical\\INTERCONNECT\\bin\\interconnect.exe'
        file_path_c = 'C:\\Program Files\\Lumerical\\v202\\bin\\interconnect.exe'
        if (os.path.isfile(file_path_a) == True):
            subprocess.Popen(args=[file_path_a, '-run', filename2], shell=True)
        elif (os.path.isfile(file_path_b) == True):
            subprocess.Popen(args=[file_path_b, '-run', filename2], shell=True)
        elif (os.path.isfile(file_path_c) == True):
            subprocess.Popen(args=[file_path_c, '-run', filename2], shell=True)
        else:
            warning_window = pya.QMessageBox()
            warning_window.setText(
                "Warning: The program could not find INTERCONNECT.")
            warning_window.setInformativeText(
                "Do you want to specify it manually?")
            warning_window.setStandardButtons(pya.QMessageBox.Yes
                                              | pya.QMessageBox.Cancel)
            warning_window.setDefaultButton(pya.QMessageBox.Yes)
            response = warning_window.exec_()
            if (response == pya.QMessageBox.Yes):
                dialog = pya.QFileDialog()
                path = str(dialog.getOpenFileName())
                path = path.replace('/', '\\')
                subprocess.Popen(args=[path, '-run', filename2], shell=True)
    def covert_buttion_clicked(self, checked):
        image_path = self.input_text.text
        print("image path: ", image_path)

        main_window = pya.Application.instance().main_window()

        #print(dir(main_window))
        #current_view = main_window.current_view()
        #print(type(current_view))
        #if not current_view:

        main_window.create_view()
        current_view = main_window.current_view()
        #raise Exception("no view availbe!")

        #cell_view = current_view.active_cellview()

        cell_view_id = current_view.create_layout(True)
        current_view.set_active_cellview_index(cell_view_id)
        cell_view = current_view.cellview(cell_view_id)

        if not cell_view.is_valid():
            raise Exception("cell view is not available")

        layout_layers = [
            pya.LayerInfo.new(0, 0),
            #pya.LayerInfo.new( 1, 0),
            #pya.LayerInfo.new( 2, 0),
            #pya.LayerInfo.new( 3, 0),
        ]

        layers = []

        for item in layout_layers:
            print(item)
            layer = cell_view.layout().insert_layer(item)
            lp = pya.LayerPropertiesNode()
            lp.source_layer = item.layer
            lp.source_datatype = item.datatype

            current_view.init_layer_properties(lp)
            current_view.insert_layer(current_view.end_layers(), lp)

            layers.append(layer)

        current_view.update_content()

        dbu = 1
        try:
            dbu = float(self.dbu_text.text)
        except ValueError:
            error_message_box = pya.QMessageBox(self)
            error_message_box.setText("Please set dbu to a number!")
            return

        image = pya.Image(image_path)
        print("image: {}, width = {}, height = {}".format(
            image_path, image.width(), image.height()))

        current_view.transaction("Image channels to RGB")
        trans = pya.ICplxTrans.from_dtrans(
            pya.DCplxTrans.new(1 / dbu) * image.trans *
            pya.DCplxTrans.new(dbu))

        # The dimension of one pixel
        pixel_width = image.pixel_width / dbu
        pixel_height = image.pixel_height / dbu
        print("pixel width: {} pixel height: {}".format(
            pixel_width, pixel_height))

        cell_view.layout().create_cell("TOP")
        cell_view.cell_name = "TOP"
        #print("cell::", type(cell_view.cell), cell_view.cell)

        try:
            threshold = int(self.threshold_text.text)
        except ValueError:
            error_message_box = pya.QMessageBox(self)
            error_message_box.setText(
                "Please set the threshold to a number (0 ~ 255)!")
            return

        print("threshold", threshold)

        for layer in layers:

            shapes = cell_view.cell.shapes(layer)

            image_width = self.raw_image.width()
            image_height = self.raw_image.height()
            for x in range(image_width):
                for y in range(image_height):
                    d = self.image_array[x][image_height - 1 - y]
                    if (not self.inverse_checkbox.isChecked() and d > threshold
                        ) or (self.inverse_checkbox.isChecked()
                              and d < threshold):
                        p1 = pya.DPoint(x * pixel_width, y * pixel_height)
                        p2 = pya.DPoint((x + 1) * pixel_width,
                                        (y + 1) * pixel_height)
                        #print("draw: box: {}, {}".format(x, y))
                        dbox = pya.DBox.new(p1, p2)
                        box = pya.Box.from_dbox(dbox)
                        poly = pya.Polygon.new(box)
                        shapes.insert(poly.transformed_cplx(trans))

        print("drawing done")

        current_view.commit()