#=============================================== # TkFITModel #=============================================== package TkFITModel; use clib::TkArrhenius; @ISA = qw(TkArrhenius); #公開したいサブルーチン #@EXPORT = qw(DelSpace Reduce01 MakePath RegExpQuote); use strict; use Utils; use IniFile; use CSV; use MyTk::GraphFrameArray; use GraphData; use MultiColumnData; use Sci qw($pi $kB $e $e0 $me $h $hbar $KToeV erfc); use Sci::ConductingMaterial; use Sci::Optimize; #============================================================ # 変数等取得関数 #============================================================ #============================================================ # コンストラクタ、デストラクタ #============================================================ #呼び出されることはない sub new { my ($module, $app) = @_; my $this = {}; bless $this; return $this; } sub DESTROY { my $this = shift; $this->SUPER::DESTROY(@_); } #============================================================ # 継承クラスで定義しなおす関数 #============================================================ sub CreateLeftFrame { my ($this, $ConfigSide, @args) = @_; # return $this->SUPER::CreateLeftFrame(); return undef; } sub CreateSelectFilePane { my ($this) = @_; # return $this->SUPER::CreateSelectFilePane(); return undef; } sub CreateFileContentPane { return undef; } sub CreateWidgets { my ($this, $DoSuperOnly) = @_; $DoSuperOnly = 0 if(!defined $DoSuperOnly); my $App = $this->App(); my $args = $App->Args(); my $mw = $this->mw(); my $Program = "Arrhenius Plot2007"; $this->{nT} = 2; $this->{ini} = new ConductingMaterial(); $this->{dini} = new ConductingMaterial(); $this->InitializeParameters($this->{ini}, $this->{dini}); $this->{DataFile} = new MultiColumnData; $this->SUPER::CreateWidgets(1); return if($DoSuperOnly); $mw->SetTitle("$Program / TkPlot"); $this->App()->SetProgram("$Program / 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'); $this->{ChooseXListBox} = $Frame->MyBrowseEntry( -label => "X:", -state => "readonly", -takefocus => 1, -width => 5, -Selections => [], -SelIndex => 0, )->pack(-side => 'left', -expand => 'yes', -fill => 'x'); $this->{ChooseXListBox}->configure(-browsecmd => sub { $this->SelChangeListBox("X", $this->{ChooseXListBox}); } ); $this->{ChooseY1ListBox} = $Frame->MyBrowseEntry( -label => "Y1:", -state => "readonly", -takefocus => 1, -width => 5, -Selections => [], -SelIndex => 1, )->pack(-side => 'left', -expand => 'yes', -fill => 'x'); $Frame = $mw->MyFrame()->pack(-anchor => 'nw', -fill => 'x'); $this->MakeLabelEntry($Frame, "XMin", "XMin(cal):", $this->{ini}->pVariable('XMin', ''), "%s", $EntryWidth-2, "", 0, 0); $this->MakeLabelEntry($Frame, "XMin", "XMax:", $this->{ini}->pVariable('XMax', ''), "%s", $EntryWidth-2, "", 0, 1); $this->MakeLabelEntry($Frame, "XStep", "Step:", $this->{ini}->pVariable('XStep', 0), "%s", $EntryWidth-2, "", 0, 2); $this->MakeLabelEntry($Frame, "FitXMin", "XMin(fit):", $this->{ini}->pVariable('FitXMin', ''), "%s", $EntryWidth-2, "", 1, 0); $this->MakeLabelEntry($Frame, "FitXMin", "XMax:", $this->{ini}->pVariable('FitXMax', ''), "%s", $EntryWidth-2, "", 1, 1); $this->MakeLabelEntry($Frame, "nSkipData", "nSkip:", $this->{ini}->pVariable('nSkipData', 0), "%d", $EntryWidth-2, "", 1, 2); $Frame = $mw->MyLabFrame( -label => 'LSQ', -labelside => 'acrosstop', )->pack(-anchor => 'nw'); my $Frame2 = $Frame->MyFrame()->pack(-anchor => 'nw', -fill => 'x'); $this->{ChooseLSQMethodListBox} = $Frame2->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'); $this->{ChooseModelListBox} = $Frame2->MyBrowseEntry( -label => "Model:", -state => "readonly", -takefocus => 1, -width => 10, -Selections => ['FIT model'], -SelIndex => 0, )->pack(-side => 'left', -expand => 'yes', -fill => 'x'); $Frame2 = $Frame->MyFrame()->pack(-anchor => 'nw', -fill => 'x'); $this->MakeLabelEntry($Frame2, "EPS", "EPS:", $this->{ini}->pVariable('EPS', 1.0e-5), "%12.4g", $EntryWidth, "", 0, 0); $this->MakeLabelEntry($Frame2, "nMaxIter", "MaxIter:", $this->{ini}->pVariable('nMaxIter', 1000), "%d", $EntryWidth, "", 0, 1); $this->MakeLabelEntry($Frame2, "nSaveSpectraInterval", "SaveIntvl:", $this->{ini}->pVariable('nSaveSpectraInterval', 100), "%d", $EntryWidth, "", 1, 0); $Frame = $mw->MyLabFrame( -label => 'Parameters', -labelside => 'acrosstop', )->pack(-anchor => 'nw'); $this->MakeCheckEntry($Frame, "s0", "s0:", $this->{dini}->pVariable("s0check", 0), $this->{ini}->pVariable("s0", 0.0), "%g", $EntryWidth, "", 0, 0); for(my $i = 0 ; $i < $this->{nT} ; $i++) { $this->MakeCheckEntry($Frame, "T$i", "T$i:", $this->{dini}->pVariable("T${i}check", 0), $this->{ini}->pVariable("T$i", 0.0), "%g", $EntryWidth, "", $i+1, 0); } $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'); #ツールバーのOpenボタンをファイル読み込みにバインドする $mw->{OpenButton}->configure( -command => sub { $this->ChooseFile('SampleCSV', $this->{SPEFileFileEntry}); }, ) if($mw->{OpenButton}); } sub Initialize { my ($this, $InitializeParameterFile, $InitializeData) = @_; $this->{DataFile} = new Ellipsometry; if($InitializeParameterFile) { $this->{ini}->{SampleCSVFile} = ''; $this->{ini}->{FitXMin} = ''; $this->{ini}->{FitXMax} = ''; } if($InitializeData) { $this->{pCalX} = undef; $this->{pCalY} = undef; } } sub UpdateParameters { my ($this) = @_; my $ini = $this->{ini}; $this->SUPER::UpdateParameters(); } sub EntryFocusedIn { my ($this, $obj, $event, $type) = @_; if($event eq 'FocusIn') { $obj->{PrevValue} = $obj->GetText(); } else { $this->SUPER::EntryFocusedIn($obj, $event, $type); } } sub EntryFocusedOut { my ($this, $obj, $event, $type) = @_; my $ini = $this->{ini}; my $dini = $this->{dini}; if($event eq 'FocusOut') { $this->SUPER::EntryFocusedOut($obj, $event, $type); } else { $this->SUPER::EntryFocusedOut($obj, $event, $type); } } sub ButtonPressed { my ($this, $event, $type) = @_; my $App = $this->App(); if($type eq 'CalAll') { $this->CalAll(); } elsif($type eq 'SaveAll') { my $FileName = $this->{SampleCSVFileEntry}->GetText(); $FileName = Deps::ReplaceExtension($FileName, "AllData.csv"); $this->print("Save All Data\n"); $this->print(" Save to [$FileName].\n"); $this->CalAll($FileName); $this->print(" Data saved to [$FileName].\n"); } else { $this->SUPER::ButtonPressed($event, $type); } } #=================================================== # データ処理 #=================================================== sub Recalc { my ($this) = @_; return if(!defined $this->{DataFile}); printf "\nRecalc\n"; my $ini = $this->{ini}; my $XMin = $ini->{XMin}; my $XMax = $ini->{XMax}; my $XStep = $ini->{XStep}; my $nSkipData = $ini->{nSkipData}; my $Method = $this->{ChooseLSQMethodListBox}->GetText(); my $Model = $this->{ChooseModelListBox}->GetText(); my $XLabel = $this->{"ChooseXListBox"}->GetText(); my $Y1Label = $this->{"ChooseY1ListBox"}->GetText(); my $pX = $this->{DataFile}->GetData($XLabel) if($XLabel); my $pY1 = $this->{DataFile}->GetData($Y1Label) if($Y1Label); ($this->{pCalX}, $this->{pCalY1}) = $this->CalAll($Model, $pX, $pY1, $XMin, $XMax, $XStep, $nSkipData); # $this->SetS2($S2); $this->CreateGraphFrame() if(!defined $this->{GraphFrameArray}); $this->AssignGraphData(1); $this->Draw(); } sub CalAll { my ($this, $Model, $pX, $pY1, $FitXMin, $FitXMax, $XStep, $nSkipData, $IsPrint) = @_; $IsPrint = 1 if(!defined $IsPrint); my $ini = $this->{ini}; my (@X, @CalX, @CalY1); my $Sync = 0; if(defined $XStep and $XStep > 0.0) { for(my $i = 0 ; ; $i++) { my $x = $FitXMin + $i * $XStep; last if($x > $FitXMax); $X[$i] = $x; } } else { @X = @$pX; $Sync = 1; } my $nData = @X; my $c = 0; #print "CalAll [$Model] ($nData)\n"; #print "s0=$ini->{s0}, T0=$ini->{T0}, T1=$ini->{T1}\n"; for(my $i = 0 ; $i < $nData ; $i++) { next if($nSkipData > 0 and $i % $nSkipData != 0); my $x = $X[$i]; next if(defined $FitXMin and $x < $FitXMin); next if(defined $FitXMax and $FitXMax < $x); if($Model eq 'FIT model') { my $ycal = $ini->{s0} * exp( - $ini->{T1} / ($x + $ini->{T0}) ); if($Sync) { print "x=$x,$pY1->[$i],$ycal\n" if($IsPrint); } else { print "x=$x,$ycal\n" if($IsPrint); } $CalX[$c] = $x; $CalY1[$c] = $ycal; } $c++; } return (\@CalX, \@CalY1); } sub Fit { my ($this) = @_; return if(!defined $this->{DataFile}); my $App = $this->App(); my $ini = $this->{ini}; printf "\nFit\n"; $this->UpdateParameters(); $this->BuildOptimize(0); my $optimize = $this->{Optimize}; my $FitXMin = $ini->{FitXMin}; my $FitXMax = $ini->{FitXMax}; my $nSkipData = $ini->{nSkipData}; my $Method = $this->{ChooseLSQMethodListBox}->GetText(); my $Model = $this->{ChooseModelListBox}->GetText(); my $XLabel = $this->{"ChooseXListBox"}->GetText(); my $Y1Label = $this->{"ChooseY1ListBox"}->GetText(); my $pX = $this->{DataFile}->GetData($XLabel) if($XLabel); my $pY1 = $this->{DataFile}->GetData($Y1Label) if($Y1Label); my ($OptVars, $MinVal) = $optimize->Optimize( $Method, undef, undef, undef, $ini->{EPS}, $ini->{nMaxIter}, $ini->{iPrintLevel}, sub { $this->CalS2($Model, $pX, $pY1, $FitXMin, $FitXMax, $nSkipData, @_); }, undef, sub { Optimize::BuildDifferentialMatrixes(@_); }, ); print "\nOptimized at:\n"; # $optimize->PrintParameters(1); $optimize->PrintParameters(1, $OptVars, $MinVal, 1); $optimize->RecoverParameters($OptVars); $this->Recalc(); $this->SetS2($MinVal); $this->UpdateParameters(); $this->CreateGraphFrame() if(!defined $this->{GraphFrameArray}); $this->AssignGraphData(1); $this->Draw(); } sub CalS2 { my ($this, $Model, $pX, $pY1, $FitXMin, $FitXMax, $nSkipData, $pVars, $iPrintLevel) = @_; my $optimize = $this->{Optimize}; my $ini = $this->{ini}; $optimize->RecoverParameters($pVars); my ($d1, $d2); my $S2 = 0.0; my $c = 0; my ($pCalX, $pCalY1) = $this->CalAll($Model, $pX, $pY1, undef, undef, undef, 0, 0); my $nData = @$pX; for(my $i = 0 ; $i < $nData ; $i++) { next if($nSkipData > 0 and $i % $nSkipData != 0); my $x = $pX->[$i]; next if($x < $FitXMin or $FitXMax < $x); #print "X[$i]: $x < $FitXMin or $FitXMax < $x\n"; if($Model eq 'FIT model') { #print "$i: log(abs($pCalY1->[$i])) - log(abs($pY1->[$i]))\n"; my $d = log(abs($pCalY1->[$i])) - log(abs($pY1->[$i])); $S2 += $d*$d; } $c++; } $S2 /= $c if($c > 0); $optimize->IncrementnS2(); my $nS2 = $optimize->nS2Calculation(); print "S2[$nS2] = $S2 / "; $S2 += $optimize->CalPenalty(1.0e10, $pVars, 1); print "$S2\n"; $optimize->PrintParameters(1, $pVars, $S2, 0); my $nS2Interval = $ini->{nSaveSpectraInterval}; if($nS2Interval > 0 and $nS2 % $nS2Interval == 0) { $this->SetS2($S2, 0); $this->Recalc(); # $this->CreateGraphFrame(); $this->AssignGraphData(0); $this->Draw(); } $this->UpdateParameters(); $this->mw()->update(); return $S2; } sub BuildOptimize { my ($this, $IsPrint) = @_; my $ini = $this->{ini}; my $dini = $this->{dini}; my $XLabel = $this->{"ChooseXListBox"}->GetText(); my $Y1Label = $this->{"ChooseY1ListBox"}->GetText(); my $pX = $this->{DataFile}->GetData($XLabel) if($XLabel); my $pY1 = $this->{DataFile}->GetData($Y1Label) if($Y1Label); my $nData = $this->{DataFile}->nData(); my $optimize = new Optimize(); $optimize->AddParameters( "s0", \$ini->{"s0"}, $dini->{"s0check"}, $dini->{"s0scale"}, $dini->{"s0min"}, $dini->{"s0max"}, sub { $ini->{"s0"} = $_[0]; }, ); for(my $i = 0 ; $i < $this->{nT} ; $i++) { my $ii = $i; $optimize->AddParameters( "T$i", \$ini->{"T$i"}, $dini->{"T${i}check"}, $dini->{"T${i}scale"}, $dini->{"T${i}min"}, $dini->{"T${i}max"}, sub { $ini->{"T${ii}"} = $_[0]; }, ); } $this->{Optimize} = $optimize; } sub AssignGraphData { my ($this, $ResetViewRange) = @_; $ResetViewRange = 1 if(!defined $ResetViewRange); my $GraphFrameArray = $this->GetGraphFrameArray(); my $GraphFrame0 = $GraphFrameArray->GetGraphFrame(0); $GraphFrame0->ClearAllData(); my $Model = $this->{"ChooseModelListBox"}->GetText(); my $XLabel = $this->{"ChooseXListBox"}->GetText(); my $Y1Label = $this->{"ChooseY1ListBox"}->GetText(); $GraphFrame0->SetXCaption($XLabel); $GraphFrame0->SetYCaption($Y1Label); my $pX = $this->{DataFile}->GetData($XLabel) if($XLabel); my $pY1 = $this->{DataFile}->GetData($Y1Label) if($Y1Label); my $nData = $this->{DataFile}->nData(); if($pX) { $GraphFrame0->AddGraphData($pX, $pY1, 0, "black", "circle", 6, "black", 0, "black", "XAutoSkip", "1/T(K)", "log({${Y1Label}|)") if($pY1); } my $pCalX = $this->{pCalX}; my $pCalY1 = $this->{pCalY1}; if($pCalX) { $GraphFrame0->AddGraphData($pCalX, $pCalY1, 1, "red", "", 6, "red", 0, "red", "XAutoSkip", "1/T(K)", "log(|${Y1Label}|)(cal)") if($pCalY1); } if($Model eq 'FIT model') { # $GraphFrame0->SetXCaption("1/$XLabel"); # $GraphFrame0->SetYCaption("log(|${Y1Label}|)"); # $GraphFrame0->SetXScalePlotType('1/x'); # $GraphFrame0->SetYScalePlotType('log(|x|)'); } if($ResetViewRange) { $GraphFrame0->CalMinMax(); $GraphFrame0->AdjustViewRange(0.05, 0.05, 0.05, 0.05); # $GraphFrame0->SetViewYRange(0); } } sub InitializeParameters { my ($this, $ini, $dini) = @_; $this->AddParameters($ini, $dini, "s0", 0.01, 1, 0.1, 0.0, ''); for(my $i = 0 ; $i < $this->{nT} ; $i++) { $this->AddParameters($ini, $dini, "T$i", 0.0, 1, 0.5, 0.0, ''); } } 1;