class ZCG(Grating): si_n = interp.interp1d(*zip(*[[nu2lambda(float(f))*10**6, n] for (f, n) in opencsv('materials/silicon_n.csv',1)])) si_k = interp.interp1d(*zip(*[[nu2lambda(float(f))*10**6,n] for (f, n) in opencsv('materials/silicon_k.csv',1)])) def __init__(self, params, wavelengths, target = None, resample = False): super().__init__(params, wavelengths, target, resample) self.labels = ['d','ff','tline','tslab','tstep'] d, ff, tline, tslab, tstep = params assert tline > tstep, "tstep cannot be larger than tline" #create simulation self.sim = S4.New(d, 20) self.sim.AddMaterial("Vacuum", 1) self.sim.AddMaterial("Silicon", 0) #edited later in setmaterials self.sim.AddLayer('top', 0, "Vacuum") self.sim.AddLayer('step', tstep, "Vacuum") self.sim.AddLayer('lines', tline - tstep, "Vacuum") self.sim.AddLayer('slab', tslab, "Silicon") self.sim.AddLayer('bottom', 0, "Vacuum") self.sim.SetRegionRectangle('step', 'Silicon', (-d*ff/4, 0), 0, (d*ff/4, 0)) self.sim.SetRegionRectangle('lines', 'Silicon', (0,0), 0, (d*ff/2, 0)) self.sim.SetExcitationPlanewave((0, 0), 0, 1) def setmaterials(self, wl): self.sim.SetMaterial('Silicon', complex(self.__class__.si_n(wl), self.__class__.si_k(wl))**2)
class HCG(Grating): si_n = interp.interp1d(*zip( *[[nu2lambda(float(f)) * 10**6, n] for (f, n) in h.opencsv('../matdat/silicon_n.csv', 1)])) si_k = interp.interp1d(*zip( *[[nu2lambda(float(f)) * 10**6, n] for (f, n) in h.opencsv('../matdat/silicon_k.csv', 1)])) def __init__(self, params, wavelengths, target): Grating.__init__(self, params, wavelengths, target) self.d, self.ff, self.tline, self.tslab, self.tstep = params self.labels = ['d', 'ff', 'tline', 'tair', 'tstep'] if self.tstep > self.tline: raise ValueError("tstep cannot be larger than tline") def evaluate(self): if self.fom is None: S = S4.New(self.d, 20) #materials S.AddMaterial("Vacuum", 1) S.AddMaterial("Silicon", 1) #edited later per wavelength #layers S.AddLayer('top', 0, "Vacuum") S.AddLayer('step', self.tstep, "Vacuum") S.AddLayer('lines', self.tline - self.tstep, "Vacuum") S.AddLayer('airgap', self.tslab, "Vacuum") S.AddLayer('bottom', 0, "Silicon") #patterning S.SetRegionRectangle('step', 'Silicon', (-self.d * self.ff / 4, 0), 0, (self.d * self.ff / 4, 0)) S.SetRegionRectangle('lines', 'Silicon', (0, 0), 0, (self.d * self.ff / 2, 0)) #light S.SetExcitationPlanewave((0, 0), 0, 1) self.trans = [] for wl in np.linspace(*self.wls): S.SetFrequency(1 / wl) S.SetMaterial( 'Silicon', complex(self.__class__.si_n(wl), self.__class__.si_k(wl))**2) self.trans.append( (wl, float(np.real(S.GetPowerFlux('bottom')[0])))) self._calcfom() self.findpeak() return self.fom
def omega2lambda(omega): """ Convert omega to wavelength """ from scipy.constants import nu2lambda from math import pi nu = omega / (2 * pi) return nu2lambda(nu)
def add_dispersion(self, incident_angle, input_time, input_complex_time, center_freq, mixing = 1): theta_i = np.deg2rad(incident_angle) # convert angle in degree to radian d_time = np.abs(input_time[1] - input_time[0]) # time spacing d_freq = 1/d_time # frequency spacing numpoints = len(input_complex_time) # number of point in the input signal input_complex_freq = fftpack.fft(input_complex_time) # fourier transform to get frequency domain input_freq = fftpack.fftfreq(numpoints) * d_freq # calculate frequency bins input_freq = np.fft.fftshift(input_freq) # rearrange frequency bins input_freq += center_freq # center frequency bins at center frequency input_amp_freq, input_phase_freq = rect_to_polar(input_complex_freq) # split into amplitude and phase center_wavelength = nu2lambda(center_freq) # wavelength corresponding to center frequency theta_r0 = np.arcsin(center_wavelength/self.grating_d - np.sin(theta_i)) # reflected angle of center wavelength k_0 = 2 * np.pi/center_wavelength * self.grating_L # phase velocity shift reciprocal_vg = ((1 + np.sin(theta_i) * np.sin(theta_r0))/(speed_of_light * np.cos(theta_r0)))*self.grating_L # group velocity shift # add phase to input signal using grating dispersion equation add_phase = [] # phase to be added output_amp_freq = input_amp_freq # output amplitude in frequency domain for index, freq in enumerate(input_freq): wavelength = nu2lambda(freq) dw = 2*np.pi*(freq - center_freq) sin_theta_r = wavelength / self.grating_d - np.sin(theta_i) # sine of reflected angle if np.abs(sin_theta_r) <= 1: # value of sine is valid add_ph = 2 * np.pi / (wavelength) * self.grating_L * np.cos(np.arcsin(sin_theta_r)) # grating-added dispersion add_ph -= (k_0 + reciprocal_vg*dw) # remove pulse shifting (only want changes of pulse's shape) else: # value is invalid add_ph = 0 output_amp_freq[index] = 0 # this wavelength is not reflected add_phase.append(add_ph) add_phase = np.array(add_phase) output_phase_freq = input_phase_freq + mixing * add_phase # add phase to the input pulse output_complex_freq = polar_to_rect(output_amp_freq, output_phase_freq) output_complex_time = fftpack.ifft(output_complex_freq) # inverse fourier transform to get time domain return output_complex_time
def test_nu_to_lambda(): assert_equal(sc.nu2lambda(1), sc.speed_of_light)
class BiZCG(Grating): si_n = interp.interp1d(*zip( *[[nu2lambda(float(f)) * 10**6, n] for (f, n) in h.opencsv('../matdat/silicon_n.csv', 1)])) si_k = interp.interp1d(*zip( *[[nu2lambda(float(f)) * 10**6, n] for (f, n) in h.opencsv('../matdat/silicon_k.csv', 1)])) def __init__(self, params, wavelengths, target, angle=5): Grating.__init__(self, params, wavelengths, target) self.labels = ['d', 'ff', 'tline1', 'tline2', 'tslab', 'angle'] self.d, self.ff, self.tline1, self.tline2, self.tslab, self.angle = params if self.tline2 > self.tline1: self.tline2, self.tline1 = self.tline1, self.tline2 if self.tslab + self.tline2 < 0: raise ValueError("-self.tline > self.tslab") def evaluate(self): if self.fom is None: S = S4.New(self.d, 20) #materials S.AddMaterial("Vacuum", 1) S.AddMaterial("Silicon", 1) #edited later per wavelength #layers S.AddLayer('top', 0, "Vacuum") S.AddLayer('line1', self.tline1 - self.tline2, "Vacuum") S.AddLayer('line2', self.tline2 - self.tslab, "Vacuum") S.AddLayer('slab', self.tslab, "Silicon") S.AddLayer('bottom', 0, "Vacuum") #patterning S.SetRegionRectangle('line1', 'Silicon', (-3 * self.d * self.ff / 8, 0), 0, (self.d * self.ff / 8, 0)) S.SetRegionRectangle('line2', 'Silicon', (-3 * self.d * self.ff / 8, 0), 0, (self.d * self.ff / 8, 0)) S.SetRegionRectangle('line2', 'Silicon', (self.d * self.ff / 8, 0), 0, (self.d * self.ff / 8, 0)) all_trans = [] for theta in (0, self.angle): #light S.SetExcitationPlanewave((0, 0), np.sin(theta), np.cos(theta)) self.trans = [] for wl in np.linspace(*self.wls): S.SetFrequency(1 / wl) S.SetMaterial( 'Silicon', complex(self.__class__.si_n(wl), self.__class__.si_k(wl))**2) self.trans.append( (wl, float(np.real(S.GetPowerFlux('bottom')[0])))) all_trans.append(self.trans) trans_metadata = [] for trans in all_trans: self.trans = trans self.fom, self.peak, self.linewidth = 3 * (None, ) self.findpeak() self._calcfom() trans_metadata.append((self.fom, self.peak, self.linewidth)) self.trans = all_trans self.fom, self.peak, self.linewidth = max(trans_metadata) return self.fom def mutate(self): child = Grating.mutate(self) if random() > 2 / 3: child.params[3] = -min(self.tline2, 0.9 * self.tslab) child.params[5] = self.angle return child def crossbreed(self, rhs): child = Grating.mutate(self) child.params[5] = self.angle return child
def omega2lambda(omega): """Convert omega to wavelength.""" from scipy.constants import nu2lambda from math import pi nu = omega / (2 * pi) return nu2lambda(nu)
def startClicked(self): # place here the location of the Lumerical "lumapi" library spec_win = importlib.util.spec_from_file_location( "lumapi", self.ui.lineEdit_lumapi.text()) # Functions that perform the actual loading lumapi = importlib.util.module_from_spec(spec_win) spec_win.loader.exec_module(lumapi) global fdtd, CIGS_Reflection_dataframe #fdtd = lumapi.FDTD(hide=True) CIGS_Monitors = self.ui.lineEdit_CIGS_M.text() CIGS_Material = self.ui.lineEdit_Mat_CIGS.text() Substrate_Monitors = self.ui.lineEdit_Substr_M.text() Reflection_Monitors = self.ui.lineEdit_Reflection_M.text() for i, files in enumerate(sorted_FDTD): self.ui.Results_List.setItem(i, 0, QTableWidgetItem(str(files))) self.ui.Results_List.setItem(i, 1, QTableWidgetItem("0")) self.ui.Results_List.setItem(i, 2, QTableWidgetItem("0")) if i == 0: self.ui.Results_List.setHorizontalHeaderLabels( ('Simulation Files', 'CIGS Jsc', 'parasitic Jsc') ) #this only needs to run once to set the name of the headers fdtd = lumapi.FDTD(hide=False, filename=files) if not fdtd.havedata(CIGS_Monitors): fdtd.runanalysis fdtd.save if self.ui.checkBox_EQE.isChecked(): CIGS_EQE_dataframe = pandas.DataFrame(nu2lambda( np.squeeze(fdtd.getresult(CIGS_Monitors, "f"))), columns=['λ']) CIGS_Jsc_list = [] CIGS_Reflection_dataframe = pandas.DataFrame(nu2lambda( np.squeeze(fdtd.getresult(Reflection_Monitors, "f"))), columns=['λ']) if self.ui.checkBox_Parasit.isChecked(): parasitic_EQE_dataframe = pandas.DataFrame(nu2lambda( np.squeeze(fdtd.getresult(CIGS_Monitors, "f"))), columns=['λ']) parasitic_Jsc_list = [] if self.ui.checkBox_export_charge.isChecked(): model_dataframe = pandas.DataFrame(columns=[ 'FDTD_name', 'Metal_thickness', 'Dielectric_thickness', 'Encapsulation_gap', 'contact_dimension' ]) fdtd.select("::model") model_dataframe.loc[i] = [ files, fdtd.get('Metal_thickness'), fdtd.get('Dielectric_thickness'), fdtd.get('Encapsulation_gap'), fdtd.get('contact_dimension') ] if self.ui.checkBox_Mesh.isChecked(): CIGS_Mesh_list = [] # calculate stuff for the jsc AM15G = fdtd.solar(1) AM15G = np.asarray(AM15G).squeeze() wavelength = fdtd.solar(0) wavelength = np.asarray(wavelength).squeeze() qhc = (sc.e / (sc.h * sc.c)) frequency = nu2lambda(fdtd.getresult(CIGS_Monitors, "f")) frequency = frequency.flatten() interpolator = interpolate.interp1d(wavelength, AM15G) interpolated_AM15G = interpolator(frequency) if i != 0: fdtd.load(files) if not fdtd.havedata(CIGS_Monitors): fdtd.runanalysis fdtd.save if self.ui.checkBox_export_charge.isChecked(): fdtd.select("::model") model_dataframe.loc[i] = [ files, fdtd.get('Metal_thickness'), fdtd.get('Dielectric_thickness'), fdtd.get('Encapsulation_gap'), fdtd.get('contact_dimension') ] if self.ui.checkBox_EQE.isChecked(): tolerance = float( self.ui.doubleSpinBox.text() ) #takes the tolerance to the isclose function from the Spinbox UI element CIGS_EQE_data = CIGS_EQE(CIGS_Monitors, CIGS_Material, self.ui.checkBox_Gen.isChecked(), files, tolerance) CIGS_EQE_data = CIGS_EQE_data.flatten() CIGS_EQE_dataframe.insert(i + 1, "EQE " + files, CIGS_EQE_data) Jsc_integrated = integrate.simps( frequency * qhc * interpolated_AM15G * CIGS_EQE_data, frequency) * 0.1 #mA/cm^2 CIGS_Jsc_list.append(Jsc_integrated) self.ui.Results_List.setItem( i, 1, QTableWidgetItem(str(Jsc_integrated))) CIGS_Reflection_data = reflection_results(Reflection_Monitors) CIGS_Reflection_dataframe.insert(i + 1, "Reflection EQE " + files, CIGS_Reflection_data) if self.ui.checkBox_Parasit.isChecked(): tolerance = float(self.ui.doubleSpinBox.text()) parasitic_EQE_data = parasitic_eqe(Substrate_Monitors, CIGS_Material, tolerance) parasitic_EQE_data = parasitic_EQE_data.flatten() parasitic_EQE_dataframe.insert(i + 1, "parasitic EQE " + files, parasitic_EQE_data) parasitic_Jsc_integrated = integrate.simps( frequency * qhc * interpolated_AM15G * parasitic_EQE_data, frequency) * 0.1 parasitic_Jsc_list.append(parasitic_Jsc_integrated) self.ui.Results_List.setItem( i, 2, QTableWidgetItem(str(parasitic_Jsc_integrated))) if self.ui.checkBox_Mesh.isChecked(): Mesh_result = mesh_per_volume() CIGS_Mesh_list.append(Mesh_result) self.ui.Files_List.setItem(i, 1, QTableWidgetItem('Processed')) if self.ui.checkBox_EQE.isChecked(): CIGS_EQE_dataframe.to_excel('CIGS_EQE.xlsx', index=False) CIGS_Reflection_dataframe.to_excel('Reflection.xlsx', index=False) CIGS_Jsc_dict = dict(zip(sorted_FDTD, CIGS_Jsc_list)) CIGS_Jsc_dataframe = pandas.DataFrame([CIGS_Jsc_dict]) CIGS_Jsc_dataframe.to_excel('CIGS_Jsc.xlsx', index=False) if self.ui.checkBox_Parasit.isChecked(): parasitic_EQE_dataframe.to_excel('parasitic_eqe.xlsx', index=False) parasitic_Jsc_dict = dict(zip(sorted_FDTD, parasitic_Jsc_list)) parasitic_Jsc_dataframe = pandas.DataFrame([parasitic_Jsc_dict]) parasitic_Jsc_dataframe.to_excel('parasitic_Jsc.xlsx', index=False) if self.ui.checkBox_export_charge.isChecked(): model_dataframe.to_csv("model_dataframe.csv", index=False) if self.ui.checkBox_Mesh.isChecked(): CIGS_Mesh_dict = dict(zip(sorted_FDTD, CIGS_Mesh_list)) CIGS_Mesh_dataframe = pandas.DataFrame([CIGS_Mesh_dict]) CIGS_Mesh_dataframe.to_excel('Mesh_Volume.xlsx', index=False) fdtd.close()
def test_nu_to_lambda(): assert_equal(sc.nu2lambda([sc.speed_of_light, 1]), [1, sc.speed_of_light])
def wn2lambda(wn): return nu2lambda(wn2nu(wn))