#=============================================== # TkReflectanceSpectrumBulkC12A7 #=============================================== package TkReflectanceSpectrumBulkC12A7; use clib::TkEllipsometry; @ISA = qw(TkEllipsometry); #公開したいサブルーチン #@EXPORT = qw(DelSpace Reduce01 MakePath RegExpQuote); use strict; 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, $DoSuperOnly) = @_; # $this->{ini} = new IniFile(undef, 0, 0); # $this->{dini} = new IniFile(undef, 0, 0); # $this->{Ellipsometry} = new Ellipsometry; $this->{nLorentz} = 5 if(!defined $this->{nLorentz}); $this->{nDrude} = 1 if(!defined $this->{nDrude}); $this->SUPER::CreateWidgets($DoSuperOnly); return if($DoSuperOnly); $this->mw()->SetTitle("OpticalSpectrumFit2007 / TkPlot"); $this->App()->SetProgram("OpticalSpectrumFit2007 / TkPlot"); my $lsb = $this->{ChooseFitToListBox}; $lsb->DeleteAllItem(); $lsb->AddItem('R', 'T', 'RT'); $lsb->SetText('R'); # ボタンをこのモジュールのButtonPressed関数に再バインドする $this->{"ParamFilePathButton"}->configure( -command => [ \&ChooseFile, $this, "Param", $this->{"ParamFileEntry"}, "ChooseParameterFile" ], ); $this->{"SampleCSVFilePathButton"}->configure( -command => [ \&ChooseFile, $this, "SampleCSV", $this->{"SampleCSVFileEntry"}, "ChooseSampleFile" ], ); # $this->{"SubstrateCSVFilePathButton"}->configure( # -command => [ \&ChooseFile, $this, "SubstrateCSV", $this->{"SubstrateCSVFileEntry"}, "ChooseSubstrateFile" ], # ); $this->{RecalcButton}->configure( -command => sub { $this->ButtonPressed('RButtonDown', 'Recalc'); }, ); $this->{FitButton}->configure( -command => sub { $this->ButtonPressed('RButtonDown', 'Fit'); }, ); # $this->{SaveForSCOUTButton}->configure( # -command => sub { $this->ButtonPressed('RButtonDown', 'SaveForSCOUT'); }, # ); $this->mw()->{OpenButton}->configure( -command => [ \&ChooseFile, $this, $this->{SPEFileFileEntry} ], ) if($this->mw()->{OpenButton}); } sub ChooseFile { my ($this, $type, $widget, $Option) = @_; my $App = $this->App(); my $canvas = $this->Canvas(); my $mw = $this->mw(); #print "Opt=$Option\n"; my $mode = 'open'; my $fmask = '*.csv;*.spe;*.jel;*.asp;*.smo;*isa;*.ref;*.pal;*.bef;*.aft;*.R'; if($Option eq 'ChooseParameterFile') { # $mode = 'save'; $fmask = '*.prm'; } my $dir = ''; unless($widget) { $dir = $this->App()->{WorkDir}; chdir($dir); } my $defstr = ''; my $message = 'Choose file'; my $filepath = Dialog::OpenFileDialog($mw, 'open', $fmask, $defstr, $message, $dir); if($filepath) { my $DirPath = $this->SetWorkDir($filepath); if($widget) { $widget->SetText($filepath); } } else { return undef; } my $ret; if($Option eq 'ChooseParameterFile') { $ret = $this->ReadParameterFile($filepath); return undef unless($ret); $this->Draw(); } elsif($Option eq 'ChooseSampleFile') { $ret = $this->ReadSampleDataFile($filepath); return undef unless($ret); $this->Draw(); } return $ret; } #=================================================== # データ処理 #=================================================== sub BuildLayerModel { my ($this, $IsPrint, $BuildOptimize) = @_; $IsPrint = 1 if(!defined $IsPrint); $BuildOptimize = 1 if(!defined $BuildOptimize); my $ini = $this->{ini}; my $dini = $this->{dini}; my $el = $this->{Ellipsometry}; my $optics = new Optics; my $Layers = new MultiLayer; $Layers->SetIncidentAngle($ini->{Angle}); my $pObsE = $el->GetData('.*eV.*', '.*E.*'); my $pObsPsi = $el->GetData('Psi'); my $pObsDelta = $el->GetData('Delta'); my $pObsEps1 = $el->GetData('e1'); my $pObsEps2 = $el->GetData('e2'); my $nData = @$pObsE; my $nE = $nData; $ini->{FitEmin} = $pObsE->[0] if($ini->{FitEmin} eq ''); $ini->{FitEmax} = $pObsE->[$nData-1] if($ini->{FitEmax} eq ''); my $Emin = $ini->{FitEmin}; my $Emax = $ini->{FitEmax}; my $Estep = $pObsE->[1] - $pObsE->[0]; $Estep = 0.05 if(!defined $Estep); $nData = int( ($Emax - $Emin) / $Estep + 1.001); my $Air = new OpticalMaterial("air"); my $C12A7 = new OpticalMaterial("Specify", "C12A7"); my $C12A7e = new OpticalMaterial("Specify", "C12A7e"); my $MgO = new OpticalMaterial("Specify", "MgO"); #print "e1=$ini->{e1inf}\n"; $C12A7->AddDielectricModel( "Constant1", "Constant", "e1inf", $ini->{e1inf}, "e2inf", $ini->{e2inf}, ); $C12A7->AddDielectricModel( "TaucLorentz1", "TaucLorentz", "A", $ini->{A}, "Eg", $ini->{Eg}, "En0", $ini->{En0}, "C", $ini->{C} ); $C12A7e->AddDielectricModel( "Constant1", "Constant", "e1inf", $ini->{e1inf}, "e2inf", $ini->{e2inf}, ); $C12A7e->AddDielectricModel( "TaucLorentz1", "TaucLorentz", "A", $ini->{A}, "Eg", $ini->{Eg}, "En0", $ini->{En0}, "C", $ini->{C} ); for(my $i = 0 ; $i < $this->{nLorentz} ; $i++) { my $i1 = $i + 1; $C12A7e->AddDielectricModel( "Lorentz$i1", "Lorentz", "Ne", $ini->{"Ne$i1"}, "E0", $ini->{"E0$i1"}, "G", $ini->{"G$i1"}, ); } for(my $i = 0 ; $i < $this->{nDrude} ; $i++) { my $i1 = $i + 1; $C12A7e->AddDielectricModel( "Drude$i1", "Drude", "Ep", $ini->{"DrudeEp$i1"}, "tau", $ini->{"Drudetau$i1"}, ); } if($ini->{SubstrateCSVFile}) { my @ret = $MgO->AddDielectricModel( "MgO", "EFile", "Path", $ini->{SubstrateCSVFile}, ); if(@ret == 0) { print "Error: Can not read [$ini->{SubstrateCSVFile}].\n"; return; } } my $LayerModel = $this->{ChooseLayerModelListBox}->GetText(); my $iSubstrate = 1; $Layers->AddLayer($Air, -1.0, 0.0); if($LayerModel eq "C12A7/C12A7:e-(sub)") { $iSubstrate++; $Layers->AddLayer($C12A7, $ini->{FilmThickness}*1.0e-9, 1.0); } $Layers->AddLayer($C12A7e, -1.0, 0.0); $this->{Layers} = $Layers; $Layers->PrintLayers() if($IsPrint); #========================================================= # フィッティング設定 #========================================================= return if(!$BuildOptimize); my $optimize = new Optimize(); if($LayerModel eq "C12A7/C12A7:e-(sub)") { $optimize->AddParameters( "FilmThickness", $Layers->pLayerThickness(1), $dini->{FilmThicknesscheck}, $dini->{FilmThicknessscale}, $dini->{FilmThicknessmin}, $dini->{FilmThicknessmax}, sub { $Layers->SetLayerThickness(1, $_[0]); $ini->{FilmThickness} = $_[0]*1.0e9; }, ); } $optimize->AddParameters( "C12A7e-e1", $Layers->pVariable($iSubstrate, 'Constant1::e1inf'), $dini->{e1infcheck}, $dini->{e1infscale}, $dini->{e1infmin}, $dini->{e1infmax}, sub { $Layers->SetVariable($iSubstrate, 'Constant1::e1inf', $_[0]); $ini->{e1inf} = $_[0]; }, "C12A7e-e2", $Layers->pVariable($iSubstrate, 'Constant1::e2inf'), $dini->{e2infcheck}, $dini->{e2infscale}, $dini->{e2infmin}, $dini->{e2infmax}, sub { $Layers->SetVariable($iSubstrate, 'Constant1::e2inf', $_[0]); $ini->{e2inf} = $_[0]; }, "C12A7e-TLA", $Layers->pVariable($iSubstrate, 'TaucLorentz1::A'), $dini->{Acheck}, $dini->{Ascale}, $dini->{Amin}, $dini->{Amax}, sub { $Layers->SetVariable($iSubstrate, 'TaucLorentz1::A', $_[0]); $ini->{A} = $_[0]; }, "C12A7e-TLC", $Layers->pVariable($iSubstrate, 'TaucLorentz1::C'), $dini->{Ccheck}, $dini->{Cscale}, $dini->{Cmin}, $dini->{Cmax}, sub { $Layers->SetVariable($iSubstrate, 'TaucLorentz1::C', $_[0]); $ini->{C} = $_[0]; }, "C12A7e-TLEg", $Layers->pVariable($iSubstrate, 'TaucLorentz1::Eg'), $dini->{Egcheck}, $dini->{Egscale}, $dini->{Egmin}, $dini->{Egmax}, sub { $Layers->SetVariable($iSubstrate, 'TaucLorentz1::Eg', $_[0]); $ini->{Eg} = $_[0]; }, "C12A7e-TLEn0", $Layers->pVariable($iSubstrate, 'TaucLorentz1::En0'), $dini->{En0check}, $dini->{En0scale}, $dini->{En0min}, $dini->{En0max}, sub { $Layers->SetVariable($iSubstrate, 'TaucLorentz1::En0', $_[0]); $ini->{En0} = $_[0]; }, ); for(my $i = 0 ; $i < $this->{nLorentz} ; $i++) { my $i1 = $i + 1; $optimize->AddParameters( "C12A7e-L${i1}E0", $Layers->pVariable($iSubstrate, "Lorentz${i1}::E0"), $dini->{"E0${i1}check"}, $dini->{"E0${i1}scale"}, $dini->{"E0${i1}min"}, $dini->{"E0${i1}max"}, sub { $Layers->SetVariable($iSubstrate, "Lorentz${i1}::E0", $_[0]); $ini->{"E0${i1}"} = $_[0]; }, "C12A7e-L${i1}Ne", $Layers->pVariable($iSubstrate, "Lorentz${i1}::Ne"), $dini->{"Ne${i1}check"}, $dini->{"Ne${i1}scale"}, $dini->{"Ne${i1}min"}, $dini->{"Ne${i1}max"}, sub { $Layers->SetVariable($iSubstrate, "Lorentz${i1}::Ne", $_[0]); $ini->{"Ne${i1}"} = $_[0]; $ini->{"A${i1}"} = $optics->CalLorentzAfromNe($ini->{"Ne${i1}"}, 1.0); }, "C12A7e-L${i1}G", $Layers->pVariable($iSubstrate, "Lorentz${i1}::G"), $dini->{"G${i1}check"}, $dini->{"G${i1}scale"}, $dini->{"G${i1}min"}, $dini->{"G${i1}max"}, sub { $Layers->SetVariable($iSubstrate, "Lorentz${i1}::G", $_[0]); $ini->{"G${i1}"} = $_[0]; }, ); } for(my $i = 0 ; $i < $this->{nDrude} ; $i++) { my $i1 = $i + 1; $optimize->AddParameters( "D${i1}Ep", $Layers->pVariable($iSubstrate, "Drude${i1}::Ep"), $dini->{"DrudeEp${i1}check"}, $dini->{"DrudeEp${i1}scale"}, $dini->{"DrudeEp${i1}min"}, $dini->{"DrudeEp${i1}max"}, sub { $Layers->SetVariable($iSubstrate, "Drude${i1}::Ep", $_[0]); $ini->{"DrudeEp${i1}"} = $_[0]; }, "D${i1}tau", $Layers->pVariable($iSubstrate, "Drude${i1}::tau"), $dini->{"Drudetau${i1}check"}, $dini->{"Drudetau${i1}scale"}, $dini->{"Drudetau${i1}min"}, $dini->{"Drudetau${i1}max"}, sub { $Layers->SetVariable($iSubstrate, "Drude${i1}::tau", $_[0]); $ini->{"Drudetau${i1}"} = $_[0]; }, ); } $this->{Optimize} = $optimize; } sub Recalc { my ($this) = @_; $this->UpdateParameters(); $this->BuildLayerModel(0, 0); my $el = $this->{Ellipsometry}; my $Layers = $this->{Layers}; my $pObsE = $el->GetData('.*eV.*', '.*E.*'); my $pObsR = $el->GetData('R.*'); my $pObsT = $el->GetData('T.*'); my $FitTo = $this->{ChooseFitToListBox}->GetText(); my ($pE, $pR, $pT, $S2) = $this->CalRT($Layers, $pObsE, $pObsR, $pObsT, $FitTo); $this->{pEArray} = $pE; $this->{pRArray} = $pR; $this->{pTArray} = $pT; $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(); $this->BuildLayerModel(0, 1); my $optimize = $this->{Optimize}; my $Method = $this->{ChooseLSQMethodListBox}->GetText(); my $FitTo = $this->{ChooseFitToListBox}->GetText(); print "Method: $Method\n"; print "Fit to: $FitTo\n"; print "nMaxIter: $ini->{nMaxIter}\n"; my $el = $this->{Ellipsometry}; my $pObsE = $el->GetData('.*eV.*', '.*E.*'); my $pObsR = $el->GetData('R.*'); my $pObsT = $el->GetData('T.*'); #========================================================= # 最適化の実行 #========================================================= $optimize->SetnS2Calculation(0); # $this->{nS2} = 0; my ($OptVars, $MinVal) = $optimize->Optimize( $Method, undef, undef, undef, $ini->{EPS}, $ini->{nMaxIter}, $ini->{iPrintLevel}, sub { $this->CalS2($FitTo, $pObsE, $pObsR, $pObsT, @_); }, undef, sub { Optimize::BuildDifferentialMatrixes(@_); }, ); print "\nOptimized at:\n"; $optimize->PrintParameters(1); $optimize->RecoverParameters($OptVars); $this->SetS2($MinVal); $this->UpdateParameters(); } sub CalS2 { my ($this, $FitTo, $pObsE, $pObsR, $pObsT, $pVars, $iPrintLevel) = @_; #print "pVars=", join(',', @$pVars); my $optimize = $this->{Optimize}; my $ini = $this->{ini}; $optimize->RecoverParameters($pVars); # $this->UpdateParameters(); $this->BuildLayerModel(0, 0); my ($pE, $pR, $pT, $S2) = $this->CalRT($this->{Layers}, $pObsE, $pObsR, $pObsT, $FitTo); $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); if($nS2Interval > 0 and $nS2 % $nS2Interval == 0) { $this->Recalc(); # $this->CreateGraphFrame(); $this->AssignGraphData(0); $this->Draw(); } $this->mw()->update(); return $S2; } sub CalRT { my ($this, $Layers, $pObsE, $pObsR, $pObsT, $FitTo) = @_; my $ini = $this->{ini}; my $el = $this->{Ellipsometry}; my $Angle = $ini->{Angle}; $el->SetIncidentAngle($Angle); my $nData = @$pObsE; my (@E, @R, @T); my $S2 = 0.0; 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); my ($rs, $rp, $ts, $tp, $Rs, $Rp, $Ts, $Tp) = $Layers->CalFresnelCoefficient($E); my $R = ($Rs + $Rp) / 2.0; my $T = ($Ts + $Tp) / 2.0; $E[$c] = $E; $R[$c] = $R; $T[$c] = $T; if($FitTo eq 'R' and $pObsR) { my $d = ($pObsR->[$i] - $R); $S2 += $d * $d; } elsif($FitTo eq 'T' and $pObsT) { my $d = ($pObsT->[$i] - $T); $S2 += $d * $d; } elsif($FitTo eq 'RT' and $pObsT and $pObsR) { my $d = ($pObsR->[$i] - $R); $S2 += $d * $d; $d = ($pObsT->[$i] - $T); $S2 += $d * $d; } else { print "Error: Invalid Fitting Target [$FitTo].\n"; return; } $c++; } $S2 = sqrt($S2 / $c); return (\@E, \@R, \@T, $S2); } sub ReadSampleDataFile { my ($this, $filepath, $IsPrint) = @_; my $App = $this->App(); #print "type=$type\n"; $this->{Layers} = undef; $this->{Optimize} = undef; $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(); #print "nDataArray: $nDataArray\n"; $this->{"ChooseXListBox"}->DeleteAllItem(); $this->{"ChooseY1ListBox"}->DeleteAllItem(); $this->{"ChooseY2ListBox"}->DeleteAllItem(); for(my $i = 0 ; $i < $nDataArray ; $i++) { #print "$i: $pLabelArray->[$i]\n"; $this->{"ChooseXListBox"}->AddItem($pLabelArray->[$i]); $this->{"ChooseY1ListBox"}->AddItem($pLabelArray->[$i]); $this->{"ChooseY2ListBox"}->AddItem($pLabelArray->[$i]); } $this->{"ChooseXListBox"}->SetCurSel('.*E.*', '.*eV.*'); $this->{"ChooseY1ListBox"}->SetCurSel('R', 'e1', 'n', 'Psi'); $this->{"ChooseY2ListBox"}->SetCurSel('T', 'e2', 'k', 'Delta'); $this->CreateGraphFrame(); $this->AssignGraphData(); return $filepath; } sub ReadSubstrateDataFile { my ($this, $filepath, $IsPrint) = @_; my $App = $this->App(); #print "type=$type\n"; $this->{Layers} = undef; $this->{Optimize} = undef; $this->{Substrate} = new Ellipsometry; $this->{SubstratePath} = $filepath; my ($nData, $pLabelArray, @pDataArray) = $this->{Substrate}->Read($filepath); if(!defined $nData) { $App->print("Error: Can not read [$filepath].\n"); return $this->{SubstratePath} = undef; } # $this->CreateGraphFrame(); # $this->AssignGraphData(); return $filepath; } sub CreateGraphFrame { my ($this, $canvas, $TargetData) = @_; $canvas = $this->Canvas(); my $App = $this->App(); my $font = $App->{'GraphFrameFont'}; my @font = split(/,/, $font) if($font); my $w = $canvas->width(); my $h = $canvas->height(); # my $FileType = $this->FileType(); # return undef unless($FileType); # my $pDataArray = $this->DataArray(); # return unless($pDataArray); my $GraphFrameArray = $this->{'GraphFrameArray'} = new GraphFrameArray($this->mw()); $GraphFrameArray->SetCanvasSize($w, $h); $GraphFrameArray->AddGraphFrame(); my $GraphFrame0 = $GraphFrameArray->GetGraphFrame(0); my $FramePosStr0 = $App->{"GraphFrame0Position"}; 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(); $XLabel = 'X' if(!$XLabel); my $Y1Label = $this->{"ChooseY1ListBox"}->GetText(); $Y1Label = 'Y1' if(!$Y1Label); my $Y2Label = $this->{"ChooseY2ListBox"}->GetText(); $Y2Label = 'Y2' if(!$Y2Label); $GraphFrame0->SetXCaption($XLabel); my $pX = $this->{Ellipsometry}->GetData($XLabel, '.*E.*', '.*eV.*'); my $pY1 = $this->{Ellipsometry}->GetData($Y1Label); my $pY2 = $this->{Ellipsometry}->GetData($Y2Label); my $nData = $this->{Ellipsometry}->nData(); my $YLabel = ''; $YLabel .= $Y1Label if($pY1); $YLabel .= ",$Y2Label" if($pY2); $GraphFrame0->SetYCaption("$Y1Label,$Y2Label"); if($pX) { $GraphFrame0->AddGraphData($pX, $pY1, 2, "black", "", 6, "red", 0, "red", "XAutoSkip", "Energy", "${Y1Label}(obs)") if($pY1); $GraphFrame0->AddGraphData($pX, $pY2, 2, "red", "", 6, "red", 0, "red", "XAutoSkip", "Energy", "${Y2Label}(obs)") if($pY2); } my $pCalE = $this->{pEArray}; my $pCalR = $this->{pRArray}; my $pCalT = $this->{pTArray}; if($pCalE) { $GraphFrame0->AddGraphData($pCalE, $pCalR, 0, "black", "circle", 3, "", 1, "black", "XAutoSkip", "Energy", "${Y1Label}(cal)") if($pCalR and $pY1); # 1, "black", "", 6, "red", 0, "red", "XAutoSkip", "Energy", "${Y1Label}(cal)") if($pCalR and $pY1); $GraphFrame0->AddGraphData($pCalE, $pCalT, 0, "red", "circle", 3, "", 1, "red", "XAutoSkip", "Energy", "${Y2Label}(cal)") if($pCalT and $pY2); # 1, "red", "", 6, "red", 0, "red", "XAutoSkip", "Energy", "${Y2Label}(cal)") if($pCalT and $pY2); } $GraphFrame0->SetYScalePlotType('x'); if($ResetViewRange) { $GraphFrame0->CalMinMax(); $GraphFrame0->AdjustViewRange(0.05, 0.05, 0.05, 0.05); } } 1;