def main(): from pprofile import StatisticalProfile, Profile parser = argparse.ArgumentParser() parser.add_argument('inputs', nargs='*') args = parser.parse_args() for input in args.inputs: with open(input, "rt") as h: tree = h5yaml.load(h) prefix = os.path.dirname(input).rstrip('/')+'/' def thunk(): return submain(prefix=prefix, tree=tree) STATISTICAL_PROFILE = True if STATISTICAL_PROFILE: prof = StatisticalProfile() with_item = prof(period=0.001) else: prof = Profile() with_item = prof() with with_item: thunk() with open(prefix+'cachegrind.out.0', 'wt') as file: prof.callgrind(file) with open(prefix+'pprofile.txt', 'wt') as file: prof.annotate(file)
def extract_jv(args): out_dir = outdir_path_helper(args.out_dir) input_dirs = args.input_dirs rows = [] for input_dir in tqdm.tqdm(input_dirs): submit = h5yaml.load(os.path.join(input_dir, 'submit.yaml')) spar = submit['parameters'] for f in os.listdir(input_dir): m = voltage_step_re.match(f) if not m: continue V_ext = m.group(1) filename = os.path.join(input_dir, f) tree = h5yaml.load(filename) integrals = tree['integrals'] d = dict(IB_mobility=float(spar['IB_mobility']), IB_thickness=float(spar['IB_thickness']), IB_sigma_ci=float(spar['IB_sigma_ci']), IB_sigma_iv=float(spar['IB_sigma_iv']), concentration_factor=float(spar['concentration_factor']), V=float(V_ext), j_tot_nc=(integrals['avg_j_CB:n_contact'] + integrals['avg_j_VB:n_contact'])) for k, v in integrals.items(): d["integrals:" + k] = v d['filename'] = filename rows.append(d) df = pd.DataFrame(rows) to_xcsv(df, out_dir + "JV.csv")
def voltage_step_plots(input_dir, usetex=False, pdf=False): r = [] for f in os.listdir(input_dir): before, sep, after = f.rpartition('.plot_meta.yaml') if sep and not after: r.append(os.path.join(input_dir, before)) submitfile = h5yaml.load(os.path.join(input_dir, 'submit.yaml')) for basename in r: if 'parameter=1.25' not in basename: continue plot_main(basename=basename, usetex=usetex, pdf=pdf, submitfile=submitfile)
def fixup_titles(submitfiles): for submitfile in submitfiles: t = h5yaml.load(submitfile) p = t['parameters'] p.setdefault('IB_sigma_ci', '2e-13') p.setdefault('IB_sigma_iv', '2e-13') p['title'] = title = submitfile_to_title(t) with atomic_write(submitfile, overwrite=True) as f: h5yaml.dump(t, f) olddir = osp.dirname(submitfile) newdir = osp.join(osp.dirname(olddir), title) os.rename(olddir, newdir)
def voltage_step_plots(input, production=False): tree = h5yaml.load(input) P = tree['parameters'] for d in P['voltage_steps']: prefix = osp.dirname(input).rstrip('/') + '/' fn0 = prefix + 'a parameter={} csvplot'.format(d['Vext_str']) fn = fn0 + '.csv.0' ttl = "V={} {} ".format(d['Vext_str'], P['title']) if not osp.exists(fn): continue do_main(input=fn, output_prefix=prefix + "xplot " + ttl, meta_input=fn0 + '.plot_meta.yaml', sentaurus_file=d['filename'], title=ttl, production=production, tree_voltage_step=d)
def run(submitfile): U = make_unit_registry(("mesh_unit = 1 micrometer",)) PREFIX = dirname(submitfile) submitfile_data = h5yaml.load(submitfile) P = submitfile_data['parameters'] TypicalLoggingSetup(filename_prefix=PREFIX).setup() setup_dolfin_parameters() layers = [ dict(name='pfsf', material='pfsf' , thickness=0.05), dict(name='p' , material='semiconductor', thickness=1.0), dict(name='I' , material='IB' , thickness=float(P['IB_thickness'])), dict(name='n' , material='semiconductor', thickness=1.0)] # -layer means relative to left endpoint of layer # +layer means relative to right endpoint of layer simple_overmesh_regions = [ dict(x0=('-pfsf', -0.15), x1=('-pfsf', +0.15), edge_length=0.001 ), dict(x0=('+p' , -0.05), x1=('+p' , +0.05), edge_length=0.0005), dict(x0=('+I' , -0.05), x1=('+I' , +0.05), edge_length=0.0005), dict(x0=('+n' , -0.1 ), x1=('+n' , +0.1 ), edge_length=0.001 ), ] ls = ConstructionHelperLayeredStructure() ls.params = dict(edge_length=0.01, # default edge length layers=layers, simple_overmesh_regions=simple_overmesh_regions, mesh_unit=U.um) ls.run() mesh_data = ls.mesh_data if True: Xs_df = pd.DataFrame({ 'X': list(sorted(set(mesh_data.mesh.coordinates()[:,0])))}) Xs_df.to_csv(PREFIX+"mesh_Xs.csv") if False: print("exiting early") return logging.getLogger('main').info("NUM_MESH_POINTS: {}".format( len(ls.interval_1d_tag.coordinates['coordinates']))) ## topology R = CellRegions() F = FacetRegions() topology_standard_contacts(R, F) R.cell = R.p | R.n | R.I | R.pfsf F.p_contact = F.left_contact F.pI_junction = R.p.boundary(R.I) F.In_junction = R.I.boundary(R.n) F.n_contact = F.right_contact F.IB_junctions = F.pI_junction | F.In_junction ## end topology def create_problemdata( goal='full', V_ext=None, phi_cn=None): """ goal in {'full', 'local neutrality', 'thermal equilibrium'} """ root = ProblemData( goal=goal, mesh_data=mesh_data, unit_registry=U) pdd = root.pdd CB = pdd.easy_add_band('CB') IB = pdd.easy_add_band('IB') VB = pdd.easy_add_band('VB') # material to region mapping spatial = pdd.spatial spatial.add_value_rule( R.domain, 'temperature', U('300 K')) spatial.add_value_rule( R.p, 'poisson/static_rho', U('-1e17 elementary_charge/cm^3')) spatial.add_value_rule( R.n, 'poisson/static_rho', U('+1e17 elementary_charge/cm^3')) spatial.add_value_rule( R.pfsf, 'poisson/static_rho', U('-1e19 elementary_charge/cm^3')) spatial.add_value_rule( R.I, 'poisson/static_rho', U('+0.5e17 elementary_charge/cm^3')) ib_material = IBSemiconductor(problem_data=root) ib_material.dict['IB/mobility'] = ( float(P['IB_mobility'])*U('cm^2/V/s')) for k in ['ci', 'iv']: ib_material.dict['opt_{}/sigma_opt'.format(k)] = ( float(P['IB_sigma_{}'.format(k)]) * U('cm^2')) PFrontSurfaceField(problem_data=root).register() semiconductor_material = Semiconductor(problem_data=root) ib_material.register() semiconductor_material.register() mu = pdd.mesh_util zeroE = F.exterior if goal == 'full': optical = root.optical ospatial = optical.spatial def _ediff(lower, upper): d = semiconductor_material.dict return ((d[upper+'/energy_level'] - d[lower+'/energy_level']) + U('1e-10 eV')) optical.easy_add_field( 'forward_ci', photon_energy=_ediff('IB', 'CB'), direction=(1.0, 0.0)) optical.easy_add_field( 'forward_iv', photon_energy=_ediff('VB', 'IB'), direction=(1.0, 0.0)) optical.easy_add_field( 'forward_cv', photon_energy=_ediff('VB', 'CB'), direction=(1.0, 0.0)) from simudo.util import Blackbody bb = Blackbody(temperature=6000*U.K) ofield_E_keys = ('forward_cv', 'forward_ci', 'forward_iv') for name, (lower, upper) in bb.non_overlapping_energy_ranges( {name: optical.fields[name].photon_energy for name in ofield_E_keys}).items(): flux = bb.photon_flux_integral_on_earth( lower, upper) * float(P['concentration_factor']) flux = flux.to('1/cm^2/s') ospatial.add_BC(name+'/Phi', F.left_contact, flux) logging.getLogger('main').info( "Input photon flux for field {!r}: {}" .format(name, flux)) pdd.easy_add_electro_optical_process( NonOverlappingTopHatBeerLambertIB, name='opt_ci', dst_band=CB, src_band=IB, trap_band=IB) pdd.easy_add_electro_optical_process( NonOverlappingTopHatBeerLambertIB, name='opt_iv', dst_band=IB, src_band=VB, trap_band=IB) pdd.easy_add_electro_optical_process( NonOverlappingTopHatBeerLambert, name='opt_cv', dst_band=CB, src_band=VB) # pdd.easy_add_electro_optical_process( # SRHRecombination, dst_band=CB, src_band=VB, name='SRH') # NonRadiativeTrap.easy_add_two_traps_to_pdd( pdd, 'nr', # CB, VB, IB) spatial.add_BC('CB/j', F.nonconductive, U('A/cm^2') * mu.zerovec) spatial.add_BC('VB/j', F.nonconductive, U('A/cm^2') * mu.zerovec) spatial.add_BC('IB/j', F.exterior, U('A/cm^2') * mu.zerovec) # majority contact spatial.add_BC('VB/u', F.p_contact, VB.thermal_equilibrium_u) spatial.add_BC('CB/u', F.n_contact, CB.thermal_equilibrium_u) # minority contact spatial.add_BC('VB/u', F.n_contact, VB.thermal_equilibrium_u) spatial.add_BC('CB/u', F.p_contact, CB.thermal_equilibrium_u) phi0 = pdd.poisson.thermal_equilibrium_phi spatial.add_BC('poisson/phi', F.p_contact, phi0 + V_ext) spatial.add_BC('poisson/phi', F.n_contact, phi0) zeroE -= (F.p_contact | F.n_contact).both() elif goal == 'thermal equilibrium': # to match old method, use local charge neutrality phi as # the phi boundary condition for Poisson-only thermal # equilibrium spatial.add_BC('poisson/phi', F.p_contact | F.n_contact, phi_cn) zeroE -= (F.p_contact | F.n_contact).both() spatial.add_BC('poisson/E', zeroE, U('V/m') * mu.zerovec) return root problem = create_problemdata(goal='local charge neutrality') problem.pdd.easy_auto_pre_solve() problem0 = problem problem = create_problemdata(goal='thermal equilibrium', phi_cn=problem0.pdd.poisson.phi) problem.pdd.initialize_from(problem0.pdd) problem.pdd.easy_auto_pre_solve() V_ext = U.V * dolfin.Constant(0.0) problem0 = problem problem = create_problemdata(goal='full', V_ext=V_ext) problem.pdd.initialize_from(problem0.pdd) from simudo.physics import ( VoltageStepper, OpticalIntensityAdaptiveStepper) from simudo.io.output_writer import ( OutputWriter, MetaExtractorBandInfo, MetaExtractorIntegrals) meta_extractors = ( MetaExtractorBandInfo, partial(MetaExtractorIntegrals, facets=F[{'p_contact', 'n_contact'}], cells=R[{'pfsf', 'p', 'n', 'I'}])) optics = float(P['concentration_factor']) != 0 if optics: optical_rampup_start_logparam = 20 stepper = OpticalIntensityAdaptiveStepper( solution=problem, # parameter_target_values=[1e-10, 0.1, 1.0], parameter_target_values=[0]+[ 10**-n for n in range(0, optical_rampup_start_logparam+1, 4)[::-1]], step_size=10**-optical_rampup_start_logparam, # step_size=0.5, output_writer=OutputWriter( filename_prefix=PREFIX+"I", plot_1d=True, plot_iv=False, meta_extractors=meta_extractors), selfconsistent_optics=True) stepper.do_loop() V_exts = list(np.linspace(0, 1.5, 75+1)) V_exts.extend(np.linspace(1.15, 1.30, 75+1)) V_exts = list(set(V_exts)) V_exts.sort() stepper = VoltageStepper( solution=problem, constants=[V_ext], # parameter_target_values=[1.0], # step_size=0.5, parameter_target_values=V_exts, # parameter_target_values=np.arange(0, 12)*0.1, parameter_unit=U.V, output_writer=OutputWriter( filename_prefix=PREFIX+"a", plot_1d=True, plot_iv=False, meta_extractors=meta_extractors), selfconsistent_optics=optics) stepper.do_loop() return locals()
def summary_analysis(basedirs, output_prefix=None, pdf=False, usetex=False): matplotlib_configure_CM_font(size=16, usetex=usetex) savefig_ = functools.partial(savefig, pdf=pdf) if output_prefix is None: output_prefix = osp.join(basedirs[0], 'sen_summary ') rows = [] for basedir in basedirs: for subdir_ in os.listdir(basedir): subdir = osp.join(basedir, subdir_) submit_file = osp.join(subdir, 'submit.yaml') if not osp.exists(submit_file): continue submit = h5yaml.load(submit_file) submitp = submit['parameters'] for analysis_file in glob.iglob( glob.escape(subdir) + '/xplot * sentaurus_plot_analysis.yaml'): ana = h5yaml.load(open(analysis_file)) row = ana.copy() del row['voltage_step'] row['doping_str'] = doping = submitp['doping'] row['doping'] = float(doping) row['Vext_str'] = Vext = ana['voltage_step']['Vext_str'] row['Vext'] = float(Vext) row['meshp'] = float(submitp['mesh_points']) row['meth'] = submitp['transport_method'] row['fta'] = float(submitp['fill_threshold_abs']) row['ftr'] = float(submitp['fill_threshold_rel']) row['fwz'] = bool(submitp['fill_with_zero_except_bc']) for k in ('quaddeg_super', 'quaddeg_g', 'quaddeg_rho', 'min_srv'): row[k] = submitp[k] rows.append(row) df = pd.DataFrame(rows) TRIAL_FLOODFILL = 0 TRIAL_FWZ = 1 TRIAL_CELLAVG = 2 df['numtrial'] = TRIAL_FLOODFILL def where(c, x, y): return c * x + (1 - c) * y df['numtrial'] = where(df['numtrial'] != TRIAL_FLOODFILL, df['numtrial'], where(df['fwz'] == True, TRIAL_FWZ, df['numtrial'])) df['numtrial'] = where( df['numtrial'] != TRIAL_FLOODFILL, df['numtrial'], where(((df['fta'] == 0.0) & (df['ftr'] == 0.0)), TRIAL_CELLAVG, df['numtrial'])) df.to_csv(output_prefix + 'table.csv') with open(output_prefix + 'table.txt', 'wt') as handle: print(tabulate(df, headers='keys', tablefmt='psql'), file=handle) def jrel_comparison_plot(df0, df1=None, baseline_label='baseline', trial_label=None, filename_prefix=None, total_current=False, contact_suffix0='', contact_suffix1='', is_paper_plot=False): for which_carrier, which_contact in itertools.product( 't' if total_current else 'pn', 'pn'): def _make_relvar(contact_suffix): return 'jrel__c{}{}_{}'.format(which_contact, contact_suffix, which_carrier) relvar0 = _make_relvar(contact_suffix0) relvar1 = _make_relvar(contact_suffix1) figkw = dict(figsize=(6.4, 4.8)) fig, ax = plt.subplots(**figkw) # ax.set_prop_cycle(color=['#294171', '#5A1705', '#8E887C']) for k in sorted(set(df0['doping'])): xdf0 = df0[df0['doping'] == k] doping_str = xdf0.iloc[0]['doping_str'] if is_paper_plot: label = (r"$N_A=N_D={}\;\mathrm{{cm^{{-3}}}}$".format( sci_to_latex(doping_str))) else: label = r"c={}".format(doping_str) p = ax.plot(xdf0['Vext'], abs(xdf0[relvar0]), label=label, marker='.') if df1 is None: continue xdf1 = df1[df1['doping'] == k] color = p[0].get_color() ax.plot(xdf1['Vext'], abs(xdf1[relvar1]), label='_nolegend_', color=color, linestyle='dashed', marker='.') ax.set_yscale('log') if df1 is not None: ax.plot([], [], color='black', linestyle='solid', label=baseline_label) ax.plot([], [], color='black', linestyle='dashed', label=trial_label) ax.set_xlabel(r"$V_{\mathrm{ext}}$ ($\mathrm{V}$)") if is_paper_plot: ax.set_ylabel(r"Relative error in $J$") legend_kwargs = dict(prop=dict(size=14)) else: ax.set_ylabel( r"$j_{{{},\mathrm{{rel}}}}$ at {} contact".format( which_carrier, which_contact)) legend_kwargs = {} # ax.set_xlim([-2, 2]) # ax.set_ylim([1e-8, 1e-2]) ax.legend(**legend_kwargs) if False: fig.tight_layout() else: fig.subplots_adjust(hspace=0., left=0.15, right=0.95, bottom=0.13, top=0.92) savefig_(fig, filename_prefix + ' ' + _make_relvar('')) plt.close(fig) df, df_orig = df[abs(df['Vext']) > 1e-3], df df.sort_values('Vext', inplace=True) jrel_comparison_plot(df[df['numtrial'] == TRIAL_CELLAVG], filename_prefix=output_prefix + "baseline_cellavg") if True: df = df[df['min_srv'] == '0'] df2 = df[df['numtrial'] == TRIAL_CELLAVG] if False: jrel_comparison_plot( df2[df2['meshp'] > 0], df2[df2['meshp'] < 0], baseline_label='unif', trial_label='non-unif', filename_prefix=output_prefix + "mesh_compare", total_current=True, ) if False: jrel_comparison_plot( df2[df2['quaddeg_g'] == 30], df2[df2['quaddeg_g'] == 8], baseline_label='$d_g=30$', trial_label='$d_g=6$', filename_prefix=output_prefix + "quaddeg_compare", total_current=True, ) # mixed-u-j vs mixedqfl method comparison if True: jrel_comparison_plot( df2[df['meth'] == 'mixed_qfl_j'], df2[df['meth'] == 'mixed_u_j'], baseline_label='Simudo', trial_label='TTCB...CD', contact_suffix0='I', contact_suffix1='M', filename_prefix=output_prefix + "method_compare", total_current=True, is_paper_plot=True, ) jrel_comparison_plot( df2[df['meth'] == 'mixed_u_j'], df2[df['meth'] == 'mixed_u_j'], baseline_label='TTCB...CD integrated', trial_label='TTCB...CD median', contact_suffix0='M', contact_suffix1='I', filename_prefix=output_prefix + "method_compare_extraction", total_current=True, is_paper_plot=True, ) df4 = df2[df2['quaddeg_g'] == df2['quaddeg_g'].values.max()] if True: jrel_comparison_plot( df4, df4, baseline_label='line cut', trial_label='integrated', filename_prefix=output_prefix + "linecut_vs_integrated", total_current=True, contact_suffix0='', contact_suffix1='I', ) df5 = df4[(df4['meshp'] == -1) & ( (df4['doping_str'] == '1e15') | (df4['doping_str'] == '1e18'))] df6 = df4[(df4['meshp'] == -1)] if True: jrel_comparison_plot( df5, filename_prefix=output_prefix + "paper_plot", total_current=True, contact_suffix0='I', is_paper_plot=True, ) jrel_comparison_plot( df6, filename_prefix=output_prefix + "paper_plot_all_curves", total_current=True, contact_suffix0='I', )
def do_main(input='out/stupidtest/a parameter=0.4 csvplot.csv.0', output_prefix='out/mrs2018_pnbench_', meta_input=None, sentaurus_file="data/sentaurus/diode_1d c=1e18 V=0.4.csv.xz", title=None, tree_voltage_step=None, production=False, reduce_vsize=False): df = pd.read_csv(input) if production: title = None if sentaurus_file is not None: with lzma.open(sentaurus_file, mode='rb') as handle: Sdf, Sdf_units = sentaurus_import.read_df_unitful(handle=handle, unit_registry=U) #df.sort_values('coord_x', inplace=True) #df.reset_index(drop=True, inplace=True) Sdf, Sdf_units = sentaurus_import.interpolate_df( Sdf, Sdf_units, df['coord_x'].values * U.micrometer) make_compatible_sentaurus(Sdf) else: Sdf, Sdf_units = None, None Sdf = df # to avoid a billion conditionals in the plot fig = plot_cmp_density(df, Sdf, title) savefig(fig, output_prefix + "u") plt.close(fig) fig = plot_cmp_current(df, Sdf, title) savefig(fig, output_prefix + "j") plt.close(fig) fig = plot_cmp_current(df, Sdf, title, value_only=True, grid=False) savefig(fig, output_prefix + "jv") plt.close(fig) if input.endswith( " c=1e18 Vd=-1 meshp=2000 bchack=0 ffill=1 fta=0 ftr=1e-10 fwz=0/a parameter=-0.2 csvplot.csv.0" ) and 0: fig = plot_cmp_current(df, Sdf, title, value_only=True, is_inset=True, grid=False, force_lims=[(40, 70), (-1.655e-6, -1.62e-6)]) savefig(fig, output_prefix + "jv_inset") plt.close(fig) fig = plot_cmp_current(df, Sdf, title, relative_only=True) savefig(fig, output_prefix + "jr") plt.close(fig) fig = plot_offset_partitioning(df, title) savefig(fig, output_prefix + "op") plt.close(fig) df_add_drift_diffusion_terms(df) fig = plot_drift_diffusion_currents(df, title) savefig(fig, output_prefix + "jdd") plt.close(fig) if meta_input: meta_input = h5yaml.load(meta_input) d = do_single_analysis_summary(df, Sdf, title, meta_input=meta_input, tree_voltage_step=tree_voltage_step) d['voltage_step'] = tree_voltage_step h5yaml.dump(d, output_prefix + 'sentaurus_plot_analysis.yaml')