#=============================================== # TkOpticalAbsorption #=============================================== package TkOpticalAbsorption; use clib::TkEllipsometry; @ISA = qw(TkEllipsometry); #公開したいサブルーチン #@EXPORT = qw(DelSpace Reduce01 MakePath RegExpQuote); use strict; use Sci::Algorism; my $pi = Sci::pi(); my $kB = Sci::kB(); my $c = Sci::c(); my $e = Sci::e(); my $e0 = Sci::e0(); my $me = Sci::me(); my $mp = Sci::mp(); my $mn = Sci::mn(); my $h = Sci::h(); my $hbar = Sci::hbar(); my $torad = Sci::torad(); my $todeg = Sci::todeg(); #============================================================ # 変数等取得関数 #============================================================ #============================================================ # コンストラクタ、デストラクタ #============================================================ #============================================================ # 継承クラスで定義しなおす関数 #============================================================ sub CreateWidgets { my ($this) = @_; my $App = $this->App(); my $args = $App->Args(); my $mw = $this->mw(); my $ProgramName = "OpticalAbsorptionFit2007"; $this->{ini} = new IniFile(undef, 0, 0); $this->{dini} = new IniFile(undef, 0, 0); $this->{Ellipsometry} = new Ellipsometry; $this->{nLorentz} = 3 if(!defined $this->{nLorentz}); $this->{nDrude} = 1 if(!defined $this->{nDrude}); $this->SUPER::CreateWidgets(1); $this->mw()->SetTitle("$ProgramName / TkPlot"); $this->App()->SetProgram("$ProgramName / TkPlot"); #=================================================== # ペインを作製 #=================================================== my $EntryWidth = 12; my $Frame = $mw->MyFrame()->pack(-anchor => 'nw', -fill => 'x'); $this->MakeChooseFileEntry($Frame, 'Param', 'Param:', $this->{ini}->pVariable("ParameterFile", ""), '&Choose', sub { $this->ChooseFile(@_); }, "ChooseParameterFile"); $this->{SaveParamButton} = $Frame->MyButton( -text => '&Save', -takefocus => 1, -command => sub { $this->ButtonPressed('RButtonDown', 'SaveParam'); }, )->pack(-side => 'left'); $this->{EditParamButton} = $Frame->MyButton( -text => 'ed', -takefocus => 1, -command => sub { $this->ButtonPressed('RButtonDown', 'EditParam'); }, )->pack(-side => 'left'); $Frame = $mw->MyFrame()->pack(-anchor => 'nw', -fill => 'x'); $this->MakeChooseFileEntry($Frame, 'SampleCSV', 'Sample:', $this->{ini}->pVariable("SampleCSVFile", ""), '&Choose', sub { $this->ChooseFile(@_); }, "ChooseSampleFile"); $this->{EditDataButton} = $Frame->MyButton( -text => 'ed', -takefocus => 1, -command => sub { $this->ButtonPressed('RButtonDown', 'EditSampleCSV'); }, )->pack(-side => 'left'); $Frame = $mw->MyFrame()->pack(-anchor => 'nw', -fill => 'x'); my $lsb1 = $this->{ChooseXListBox} = $Frame->MyBrowseEntry( -label => "X:", -state => "readonly", -takefocus => 1, -width => 5, -Selections => [], -SelIndex => 0, )->pack(-side => 'left', -expand => 'yes', -fill => 'x'); $lsb1->configure(-browsecmd => [\&SelChangeListBox, $this, "X", $this->{ChooseXListBox}]); my $lsb2 = $this->{ChooseY1ListBox} = $Frame->MyBrowseEntry( -label => "Y1:", -state => "readonly", -takefocus => 1, -width => 5, -Selections => [], -SelIndex => 1, )->pack(-side => 'left', -expand => 'yes', -fill => 'x'); $lsb2->configure(-browsecmd => [\&SelChangeListBox, $this, "Y1", $this->{ChooseY1ListBox}]); $Frame = $mw->MyLabFrame( -label => 'Fitting1', -labelside => 'acrosstop', )->pack(-anchor => 'nw', -fill => 'x'); my $lsb4 = $this->{ChooseLSQMethodListBox} = $Frame->MyBrowseEntry( -label => "LSQ Method:", -state => "readonly", -takefocus => 1, -width => 5, -Selections => ['Amoeba::Simplex', 'PDL::Simplex', 'ModifiedNewton'], -SelIndex => 0, )->pack(-side => 'left', -expand => 'yes', -fill => 'x'); $lsb4->configure(-browsecmd => [\&SelChangeListBox, $this, "LSQMethod", $this->{ChooseLSQMethodListBox}]); my $lsb5 = $this->{ChooseFitToListBox} = $Frame->MyBrowseEntry( -label => "Fit to:", -state => "readonly", -takefocus => 1, -width => 5, -Selections => ['alpha'], -SelIndex => 0, )->pack(-side => 'left', -expand => 'yes', -fill => 'x'); my $lsb6 = $this->{ChooseYAxisListBox} = $Frame->MyBrowseEntry( -label => "Y:", -state => "readonly", -takefocus => 1, -width => 5, -Selections => ['x', 'log(|x|)'], -SelIndex => 0, )->pack(-side => 'left', -expand => 'yes', -fill => 'x'); $Frame = $mw->MyLabFrame( -label => 'Fitting2', -labelside => 'acrosstop', )->pack(-anchor => 'nw'); $this->MakeLabelEntry($Frame, "FitEmin", "Emin:", $this->{ini}->pVariable('FitEmin', ''), "%s", $EntryWidth, "eV", 0, 0); $this->MakeLabelEntry($Frame, "FitEmax", "Emax:", $this->{ini}->pVariable('FitEmax', ''), "%s", $EntryWidth, "eV", 0, 1); $this->MakeLabelEntry($Frame, "nSkipData", "nSkip:", $this->{ini}->pVariable('nSkipData', 2), "%d", $EntryWidth, "", 0, 2); $this->MakeLabelEntry($Frame, "EPS", "EPS:", $this->{ini}->pVariable('EPS', 1.0e-5), "%12.4g", $EntryWidth, "", 1, 0); $this->MakeLabelEntry($Frame, "nMaxIter", "MaxIter:", $this->{ini}->pVariable('nMaxIter', 1000), "%d", $EntryWidth, "", 1, 1); $this->MakeLabelEntry($Frame, "nSaveSpectraInterval", "SaveIntvl:", $this->{ini}->pVariable('nSaveSpectraInterval', 0), "%d", $EntryWidth, "", 1, 2); $Frame = $mw->MyLabFrame( -label => 'Tauc model', -labelside => 'acrosstop', )->pack(-anchor => 'nw'); $this->MakeCheckEntry($Frame, "TaucEg", "Eg:", $this->{dini}->pVariable('TaucEgcheck', 0) , $this->{ini}->pVariable('TaucEg', 3.0 ), "%12.6g", $EntryWidth, "eV", 0, 0); $this->MakeCheckEntry($Frame, "TaucA", "A:", $this->{dini}->pVariable('TaucAcheck', 0) , $this->{ini}->pVariable('TaucA', 0.0), "%12.6g", $EntryWidth, "", 0, 1); $this->MakeCheckEntry($Frame, "Taucn", "n:", $this->{dini}->pVariable('Taucncheck', 0) , $this->{ini}->pVariable('Taucn', 2.0), "%12.6g", $EntryWidth, "", 0, 2); $Frame = $mw->MyLabFrame( -label => 'Urbach tail', -labelside => 'acrosstop', )->pack(-anchor => 'nw'); $this->MakeCheckEntry($Frame, "UrbachE0", "E0:", $this->{dini}->pVariable('UrbachE0check', 0) , $this->{ini}->pVariable('UrbachE0', 3.2), "%12.6g", $EntryWidth, "eV", 0, 0); $this->MakeCheckEntry($Frame, "UrbachEu", "Eu:", $this->{dini}->pVariable('UrbachEucheck', 0) , $this->{ini}->pVariable('UrbachEu', 0.1), "%12.6g", $EntryWidth, "eV", 0, 1); $Frame = $mw->MyLabFrame( -label => "Lorentz", -labelside => 'acrosstop', )->pack(-anchor => 'nw'); for(my $i = 0 ; $i < $this->{nLorentz} ; $i++) { my $i1 = $i + 1; $this->MakeCheckEntry($Frame, "E0${i1}", "E0:", $this->{dini}->pVariable("E0${i1}check", 1) , $this->{ini}->pVariable("E0${i1}", 2.64), "%12.6g", $EntryWidth, "eV", $i, 0); $this->MakeCheckEntry($Frame, "Ne${i1}", "Ne:", $this->{dini}->pVariable("Ne${i1}check", 1) , $this->{ini}->pVariable("Ne${i1}", 2e26), "%12.6g", $EntryWidth, "m-3", $i, 1); $this->MakeCheckEntry($Frame, "G${i1}", "G:", $this->{dini}->pVariable("G${i1}check", 1) , $this->{ini}->pVariable("G${i1}", 2.64), "%12.6g", $EntryWidth, "eV", $i, 2); } $Frame = $mw->MyLabFrame( -label => "Drude", -labelside => 'acrosstop', )->pack(-anchor => 'nw'); for(my $i = 0 ; $i < $this->{nDrude} ; $i++) { my $i1 = $i + 1; $this->MakeCheckEntry($Frame, "DrudeEp${i1}", "Ep:", $this->{dini}->pVariable("DrudeEp${i1}check", 1) , $this->{ini}->pVariable("DrudeEp${i1}", 0.5), "%12.6g", $EntryWidth, "eV", $i, 1); $this->MakeCheckEntry($Frame, "Drudetau${i1}", "tau:", $this->{dini}->pVariable("Drudetau${i1}check", 1) , $this->{ini}->pVariable("Drudetau${i1}", 1.0e-14), "%12.6g", $EntryWidth, "s", $i, 2); } $this->UpdateParameters(); $Frame = $mw->MyFrame()->pack(-anchor => 'nw', -fill => 'x'); $this->{RecalcButton} = $Frame->MyButton( -text => 'Re&calc', -takefocus => 1, -command => sub { $this->ButtonPressed('RButtonDown', 'Recalc'); }, )->pack(-side => 'left'); $this->{FitButton} = $Frame->MyButton( -text => '&Fit', -takefocus => 1, -command => sub { $this->ButtonPressed('RButtonDown', 'Fit'); }, )->pack(-side => 'left'); $this->{SaveComponentsButton} = $Frame->MyButton( -text => '&Save Components', -takefocus => 1, -command => sub { $this->ButtonPressed('RButtonDown', 'SaveComponents'); }, )->pack(-side => 'left'); #ツールバーのOpenボタンをファイル読み込みにバインドする $mw->{OpenButton}->configure( -command => sub { $this->ChooseFile('SampleCSVFile', $this->{SampleCSVEntry}); }, ) if($mw->{OpenButton}); } sub SelChangeListBox { my ($this, $type, $lb) = @_; #print "this:$this lb=$lb\n"; my $s = $lb->GetText(); if($type eq 'LayerModel') { if($s eq "Surface(EMA)/Film/Substrate") { $this->{SurfaceThicknessButton}->SetText("Surface thickness:"); } else { } } # $this->CreateGraphFrame(); $this->AssignGraphData(); $this->Draw(); } sub InitializeParameters { my ($this, $ini, $dini) = @_; $this->AddParameters($ini, $dini, "TaucEg", 3.0, 0, 1.0, 0.0, ''); $this->AddParameters($ini, $dini, "TaucA", 1.0e4, 0, 1.0e4, 0.0, ''); $this->AddParameters($ini, $dini, "Taucn", 2.0, 0, 1.0, 0.0, ''); $this->AddParameters($ini, $dini, "UrbachE0", 3.2, 0, 1.0, 0.0, ''); $this->AddParameters($ini, $dini, "UrbachEu", 0.1, 0, 0.05, 0.0, ''); } sub SaveComponents { my ($this) = @_; my $App = $this->App(); my $ini = $this->{ini}; my $BasePath = $ini->{SampleCSVFile}; $BasePath = $ini->{ParameterFile} if(!$BasePath); my ($drive, $directory, $filename, $ext, $lastdir, $filebody) = Deps::SplitFilePath($BasePath); my $BaseDir = "$drive$directory"; $App->print(" Base dir: $BaseDir\n"); my $el = $this->{Ellipsometry}; my $XLabel = $this->{"ChooseXListBox"}->GetText(); my $Y1Label = $this->{"ChooseY1ListBox"}->GetText(); my $pObsE = $this->{Ellipsometry}->GetData($XLabel, '.*E.*', '.*eV.*'); my $pObsAlpha = $this->{Ellipsometry}->GetData($Y1Label); my $nData = $this->{Ellipsometry}->nData(); my ($pE, $pAlpha, $pTauc, $pUrbach, $S2) = $this->CalAlphaArrays($pObsE, $pObsAlpha); my $File = Deps::MakePath($BaseDir, "Components.csv", 0); $App->print("\nSave Components to [$File]\n"); my $out = new JFile($File, "w"); if($out) { my $c = 0; $out->print("E(eV),alpha(obs),alpha(cal),alpha(Tauc),alpha(Urbach)\n"); for(my $i = 0 ; $i < $nData ; $i++) { next if($ini->{nSkipData} > 0 and $i % $ini->{nSkipData} != 0); my $E = $pObsE->[$i]; next if($E < $ini->{FitEmin} or $ini->{FitEmax} < $E); $out->print("$E,$pObsAlpha->[$i],$pAlpha->[$c],$pTauc->[$c],$pUrbach->[$c]\n"); $c++; } $out->Close(); } else { $App->print(" Error: Can not write to [$File].\n"); } } #=================================================== # データ処理 #=================================================== sub Recalc { my ($this) = @_; $this->UpdateParameters(); my $el = $this->{Ellipsometry}; my $XLabel = $this->{"ChooseXListBox"}->GetText(); my $Y1Label = $this->{"ChooseY1ListBox"}->GetText(); my $pObsE = $this->{Ellipsometry}->GetData($XLabel, '.*E.*', '.*eV.*'); my $pObsAlpha = $this->{Ellipsometry}->GetData($Y1Label); my $nData = $this->{Ellipsometry}->nData(); my ($pE, $pCalAlpha, $pTauc, $pUrbach, $S2) = $this->CalAlphaArrays($pObsE, $pObsAlpha); $this->{pEArray} = $pE; $this->{pCalAlphaArray} = $pCalAlpha; $this->App()->print("Recalc: S2=$S2\n"); $this->SetS2($S2); return $S2; } sub Fit { my ($this) = @_; my $ini = $this->{ini}; my $dini = $this->{dini}; $this->UpdateParameters(); my $Method = $this->{ChooseLSQMethodListBox}->GetText(); print "Method: $Method\n"; print "nMaxIter: $ini->{nMaxIter}\n"; my $el = $this->{Ellipsometry}; my $XLabel = $this->{"ChooseXListBox"}->GetText(); my $Y1Label = $this->{"ChooseY1ListBox"}->GetText(); my $pObsE = $el->GetData($XLabel, '.*E.*', '.*eV.*'); my $pObsAlpha = $el->GetData($Y1Label); #========================================================= # 最適化の実行 #========================================================= my $optimize = $this->{Optimize} = new Optimize(); $optimize->AddParameters( "Eg", \$ini->{TaucEg}, $dini->{TaucEgcheck}, $dini->{TaucEgscale}, $dini->{TaucEgmin}, $dini->{TaucEgmax}, sub { $ini->{TaucEg} = $_[0]; }, "A", \$ini->{TaucA}, $dini->{TaucAcheck}, $dini->{TaucAscale}, $dini->{TaucAmin}, $dini->{TaucAmax}, sub { $ini->{TaucA} = $_[0]; }, "n", \$ini->{Taucn}, $dini->{Taucncheck}, $dini->{Taucnscale}, $dini->{Taucnmin}, $dini->{Taucnmax}, sub { $ini->{Taucn} = $_[0]; }, "E0", \$ini->{UrbachE0}, $dini->{UrbachE0check}, $dini->{UrbachE0scale}, $dini->{UrbachE0min}, $dini->{UrbachE0max}, sub { $ini->{UrbachE0} = $_[0]; }, "Eu", \$ini->{UrbachEu}, $dini->{UrbachEucheck}, $dini->{UrbachEuscale}, $dini->{UrbachEumin}, $dini->{UrbachEumax}, sub { $ini->{UrbachEu} = $_[0]; }, ); $optimize->SetnS2Calculation(0); my ($OptVars, $MinVal) = $optimize->Optimize( $Method, undef, undef, undef, $ini->{EPS}, $ini->{nMaxIter}, $ini->{iPrintLevel}, sub { $this->CalS2($pObsE, $pObsAlpha, @_); }, undef, sub { Optimize::BuildDifferentialMatrixes(@_); }, ); print "\nOptimized at:\n"; $optimize->PrintParameters(1); $optimize->RecoverParameters($OptVars); $this->SetS2($MinVal); $this->UpdateParameters(); } sub CalS2 { my ($this, $pObsE, $pObsAlpha, $pVars, $iPrintLevel) = @_; #print "pVars=", join(',', @$pVars); my $optimize = $this->{Optimize}; my $ini = $this->{ini}; $optimize->RecoverParameters($pVars); my ($pE, $pAlpha, $pTauc, $pUrbach, $S2) = $this->CalAlphaArrays($pObsE, $pObsAlpha); $optimize->IncrementnS2(); my $nS2 = $optimize->nS2Calculation(); my $nS2Interval = $ini->{nSaveSpectraInterval}; print "S2[$nS2] = $S2 / "; $S2 += $optimize->CalPenalty(1.0e10, $pVars, 1); print "$S2\n"; $optimize->PrintParameters(1, $pVars, $S2, 0); if($nS2Interval > 0 and $nS2 % $nS2Interval == 0) { $this->Recalc(); # $this->CreateGraphFrame(); $this->AssignGraphData(0); $this->Draw(); } $this->mw()->update(); return $S2; } sub CalAlphaArrays { my ($this, $pObsE, $pObsAlpha) = @_; my $ini = $this->{ini}; my $el = $this->{Ellipsometry}; my $Eg = $ini->{TaucEg}; my $A = $ini->{TaucA}; my $n = $ini->{Taucn}; my $Eu = $ini->{UrbachEu}; my $E0 = $ini->{UrbachE0}; my $nData = @$pObsE; my (@E, @Alpha, @Tauc, @Urbach); my $c = 0; for(my $i = 0 ; $i < $nData ; $i++) { next if($ini->{nSkipData} > 0 and $i % $ini->{nSkipData} != 0); my $E = $pObsE->[$i]; next if($E < $ini->{FitEmin} or $ini->{FitEmax} < $E); $E[$c] = $E; if($E < $Eg) { $Alpha[$c] = 0.0; } else { $Alpha[$c] += $A * ($E - $Eg)**$n; } $Tauc[$c] = $Alpha[$c]; $c++; } my $S2 = 0.0; my $C = Algorism::InterpolateByCubicSpline(\@E, \@Alpha, $E0); #print "Alpha=$C at E0=$E0\n"; $c = 0; for(my $i = 0 ; $i < $nData ; $i++) { next if($ini->{nSkipData} > 0 and $i % $ini->{nSkipData} != 0); my $E = $pObsE->[$i]; next if($E < $ini->{FitEmin} or $ini->{FitEmax} < $E); my $Au = $C * exp(($E - $E0) / $Eu); if($E < $E0) { $Alpha[$c] = $Au; $Urbach[$c] = $Au; } else { $Urbach[$c] = 0.0; } my $d = $Alpha[$c] - $pObsAlpha->[$i]; $S2 += $d * $d; $c++; } $S2 = sqrt($S2 / $c); return (\@E, \@Alpha, \@Tauc, \@Urbach, $S2); } sub ReadSampleDataFile { my ($this, $filepath, $IsPrint) = @_; my $App = $this->App(); #print "type=$type\n"; $this->Initialize(0, 1); $this->{Path} = $filepath; my ($nData, $pLabelArray, @pDataArray) = $this->{Ellipsometry}->Read($filepath); if(!defined $nData) { $App->print("Error: Can not read [$filepath].\n"); return $this->{Path} = undef; } my $nDataArray = $this->{Ellipsometry}->nDataArray(); my $nDataArray = $this->{Ellipsometry}->nDataArray(); $this->{"ChooseXListBox"}->DeleteAllItem(); $this->{"ChooseY1ListBox"}->DeleteAllItem(); for(my $i = 0 ; $i < $nDataArray ; $i++) { $this->{"ChooseXListBox"}->AddItem($pLabelArray->[$i]); $this->{"ChooseY1ListBox"}->AddItem($pLabelArray->[$i]); } $this->{"ChooseXListBox"}->SetCurSel('.*E.*', '.*eV.*'); $this->{"ChooseY1ListBox"}->SetCurSel('alpha.*'); $this->{Ellipsometry}->GetXData('.*E.*', '.*eV.*'); $this->{Ellipsometry}->GetYData('alpha.*'); ($this->{ini}->{FitEmin}, $this->{ini}->{FitEmax}) = $this->{Ellipsometry}->CalMinMax() if(!defined $this->{ini}->{FitEmin}); $this->CreateGraphFrame(); $this->AssignGraphData(); return $filepath; } sub CreateGraphFrame { my ($this, $canvas, $TargetData) = @_; $canvas = $this->Canvas(); my $App = $this->App(); my $w = $canvas->width(); my $h = $canvas->height(); my $GraphFrameArray = $this->{'GraphFrameArray'} = new GraphFrameArray($this->mw()); $GraphFrameArray->SetCanvasSize($w, $h); $GraphFrameArray->AddGraphFrame(); my $GraphFrame0 = $GraphFrameArray->GetGraphFrame(0); my $FramePosStr0 = $App->{"GraphFrame0Position"}; $GraphFrame0->SetXCaption(""); my $XScale0 = $GraphFrame0->GetXScale(0); my $YScale0 = $GraphFrame0->GetYScale(0); $GraphFrame0->SetPositionByStr($FramePosStr0); $XScale0->SetScaleStringVisible(1); $XScale0->SetCaptionVisible(1); $GraphFrame0->SetViewRange(0, 0, 1, 1); } sub AssignGraphData { my ($this, $ResetViewRange) = @_; $ResetViewRange = 1 if(!defined $ResetViewRange); my $GraphFrameArray = $this->GetGraphFrameArray(); my $GraphFrame0 = $GraphFrameArray->GetGraphFrame(0); $GraphFrame0->ClearAllData(); my $XLabel = $this->{"ChooseXListBox"}->GetText(); my $Y1Label = $this->{"ChooseY1ListBox"}->GetText(); $GraphFrame0->SetXCaption($XLabel); $GraphFrame0->SetYCaption($Y1Label); my $pX = $this->{Ellipsometry}->GetData($XLabel, '.*E.*', '.*eV.*'); my $pY1 = $this->{Ellipsometry}->GetData($Y1Label); my $nData = $this->{Ellipsometry}->nData(); if($pX) { $GraphFrame0->AddGraphData($pX, $pY1, 2, "black", "", 6, "red", 0, "red", "XAutoSkip", "Energy", "${Y1Label}(obs)") if($pY1); } my $pCalE = $this->{pEArray}; my $pCalAalpha = $this->{pCalAlphaArray}; if($pCalE) { $GraphFrame0->AddGraphData($pCalE, $pCalAalpha, 0, "black", "circle", 3, "", 1, "black", "XAutoSkip", "Energy", "${Y1Label}(cal)") if($pCalAalpha and $pY1); } $GraphFrame0->SetYScalePlotType('x'); if($ResetViewRange) { $GraphFrame0->CalMinMax(); $GraphFrame0->AdjustViewRange(0.05, 0.05, 0.05, 0.05); $GraphFrame0->SetViewRange($GraphFrame0->GetViewRange(), 1); } my $YAxis = $this->{ChooseYAxisListBox}->GetText(); $GraphFrame0->SetScalePlotType('y', $YAxis); } 1;