#=============================================== # MultiSpectraFit #=============================================== package MultiSpectraFit; use TkFittingCommon2; @ISA = qw(TkFittingCommon2); #公開したいサブルーチン #@EXPORT = qw(DelSpace Reduce01 MakePath RegExpQuote); use strict; use Utils; use IniFile; use CSV; use MyTk::GraphFrameArray; use MyTk::MyDragDrop; use GraphData; use Sci::Science; use Sci::PES; use Sci::Ellipsometry; use Sci::Optimize; #============================================================ # 変数等取得関数 #============================================================ my $ProgramName = "MultiSpectraFit/2013"; #============================================================ # コンストラクタ、デストラクタ #============================================================ #呼び出されることはない sub new { my ($class, $app, @a) = @_; # my ($module, $app, $canvas) = @_; my $self = TkFittingCommon2->new($app, @a); my $this = bless $self, $class; # my $this = {}; # bless $this; $this->SetApplication($app) if($app); # $this->SetCanvas($canvas) if($canvas); 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 $mw = $this->mw(); $this->{ini} = new IniFile(undef, 0, 0); $this->{dini} = new IniFile(undef, 0, 0); $this->{ini}->{nSpectra} = 2; $this->InitializeParameters($this->{ini}, $this->{dini}); $this->SUPER::CreateWidgets(); return if($DoSuperOnly); $mw->SetTitle("$ProgramName / TkPlot"); $this->App()->SetProgram("$ProgramName / TkPlot"); #=================================================== # ペインを作製 #=================================================== my $EntryWidth = $this->{EntryWidth} = 12; $mw->{NoteBook} = $this->MakeNotebookPanes($mw, $EntryWidth, $this->{ini}->pVariable("nFilmLayers", 1), $this->{ini}->pVariable("nLorentz", 3), $this->{ini}->pVariable("nDrude", 0), $this->{ini}->pVariable("nStepBG", 2), $this->{ini}->pVariable("nGL", 6), [qw(Main Vars ViewRange LSQ Setup GraphFrame)], # [qw(Main ViewRange LSQ GaussLorentz Setup GraphFrame)], [qw()], # [qw(nPESStepBG nGaussLorentz)], ); $mw->{NoteBook}->pack(-fill => 'both', -expand => 'yes'); $this->UpdateParameters(); #ツールバーのOpenボタンをファイル読み込みにバインドする my $Samplefmask = '*.csv;*.spe;*.jel;*.asp;*.smo;*isa;*.ref;*.pal;*.bef;*.aft'; $mw->{OpenButton}->configure( -command => sub { $this->{SampleCSV} = $this->ChooseFile("open", $Samplefmask, "ChooseSampleFile", "SampleCSV", $this->{SampleFileFrame}); }, ) if($mw->{OpenButton}); my $DragDrop = new MyDragDrop(); $DragDrop->ConfigureDrop( $this->mw(), [ "\\.prm\$", $this->{ParamFileEntry}, sub { $this->ReadParameterFile($_[0]); }, ], [ ".*", $this->{Sample1CSVFileEntry}, sub { $this->{Sample1CSVFileEntry}->SetText($_[0]); $this->ReadSampleDataFile($_[0], 1, 1); }, ], ); my $IniFile = $this->App()->IniFile(); $this->SetGeometry($IniFile->GetString("Window", "geometry", '')); # $this->OnDestroy( sub { $this->Close(); } ); # $this->{FileNameEntry}->bind('' => sub { $this->Close(); } ); $this->bind('' => sub { $this->App()->IniFile()->WriteString("Window", "geometry", $this->geometry()); } ); } sub BuildMainPane { my ($this, $MainPaneFrame, $EntryWidth) = @_; my $Frame = $MainPaneFrame->MyFrame()->pack(-anchor => 'nw', -fill => 'x'); my $savefmask = '*.prm'; $this->{ParameterFileFrame} = $this->MakeChooseFileEntry( $Frame, 'Param', 'Param:', $this->{ini}->pVariable("ParameterFile", ""), '&Choose', sub { $this->ChooseFile("save", $savefmask, "ChooseParameterFile", @_); }, 1, sub { $this->SaveParameterFile("SaveParam", '', @_); }, 1, sub { $this->EditFile('EditParam', @_); }, [] ); $this->{ParameterFileFrame}->pack(-anchor => 'nw', -fill => 'x'); $Frame = $MainPaneFrame->MyFrame()->pack(-anchor => 'nw', -fill => 'x'); my $Samplefmask = '*.*'; #'*.csv;*.spe;*.jel;*.asp;*.smo;*isa;*.ref;*.pal;*.bef;*.aft'; $this->{SampleFileFrame} = $this->MakeChooseFileEntry( $Frame, 'SampleCSV', 'Sample:', $this->{ini}->pVariable("SampleCSVFile", ""), '&Choose', sub { $this->{SampleCSV} = $this->ChooseFile("open", $Samplefmask, "ChooseSampleFile", @_); $this->AssignGraphData(); $this->Draw(); }, 0, sub {}, 1, sub { $this->EditFile('EditSampleCSV', @_); }, ['X1', 'Y1'] ); $this->{SampleFileFrame}->pack(-anchor => 'nw', -fill => 'x'); $this->{SampleFileFrame}->{pXDataLabel} = ['.*E.*', '.*eV.*']; $this->{SampleFileFrame}->{pYDataLabel} = ['.*signal.*', 'i.*', '.*']; for(my $i = 0 ; $i < $this->{ini}->{nSpectra} ; $i++) { my $i1 = $i; $Frame = $MainPaneFrame->MyFrame()->pack(-anchor => 'nw', -fill => 'x'); $this->{"Spectrum${i}FileFrame"} = $this->MakeChooseFileEntry( $Frame, "Spectrum$i", "Spectrum$i:", $this->{ini}->pVariable("SpectrumCSVFile${i}", ""), '&Choose', sub { $this->{"SpectrumCSV${i1}"} = $this->ChooseFile("open", $Samplefmask, "ChooseSpectrumFile$i1", @_); $this->AssignGraphData(); $this->Draw(); }, 0, sub {}, 1, sub { $this->EditFile("EditSpectrumCSV$i", @_); }, ['X1', 'Y1'] ); $this->{"Spectrum${i}FileFrame"}->pack(-anchor => 'nw', -fill => 'x'); $this->{"Spectrum${i}FileFrame"}->{pXDataLabel} = ['.*E.*', '.*eV.*']; $this->{"Spectrum${i}FileFrame"}->{pYDataLabel} = ['.*signal.*', 'i.*', '.*']; } $Frame = $MainPaneFrame->MyFrame()->pack(-anchor => 'nw', -fill => 'x'); $this->mw()->{LSQFrame2} = $this->MakeLSQPanes( $Frame, $EntryWidth, 'Fitting', [qw(FittingRange)], [], [], ['FitXMin', 'Emin:', 'FitXMax', 'Emax:', 'nSkipData', 'nSkip:'], ['EPS', 'EPS:', 'nMaxIter', 'MaxIter:', 'nSaveSpectraInterval', 'SaveIntvl:'], ); $this->mw()->{LSQFrame2}->pack(-anchor => 'nw', -expand => 'yes', -fill => 'x'); $Frame = $MainPaneFrame->MyLabFrame( -label => 'Constant BG', -labelside => 'acrosstop', )->pack(-anchor => 'nw'); $this->MakeCheckEntry($Frame, "BG", "BG:", $this->{dini}->pVariable('BGcheck', 0), $this->{ini}->pVariable('BG', 0.0), "%g", $EntryWidth, "", 0, 0); $Frame = $MainPaneFrame->MyLabFrame( -label => 'Spectra parameters', -labelside => 'acrosstop', )->pack(-anchor => 'nw'); $EntryWidth = 12; for(my $i = 0 ; $i < $this->{ini}->{nSpectra} ; $i++) { $this->MakeCheckEntry($Frame, "k$i", "Spectrum$i k:", $this->{dini}->pVariable("k${i}check", 1), $this->{ini}->pVariable("k$i", 1.0), "%g", $EntryWidth, "", 2*$i, 0); $this->MakeCheckEntry($Frame, "dE$i", "dE:", $this->{dini}->pVariable("dE${i}check", 1), $this->{ini}->pVariable("dE$i", 0.0), "%g", $EntryWidth, "", 2*$i, 1); } $this->BuildFooter($MainPaneFrame, $EntryWidth); $this->UpdateParameters(); } sub BuildVarsPane { my ($this, $MainPaneFrame, $EntryWidth) = @_; my $Frame = $MainPaneFrame->MyLabFrame( -label => 'Constant BG', -labelside => 'acrosstop', )->pack(-anchor => 'nw'); my $row = 1; $this->MakeParameterConditionHeading($Frame, 0, 0); my @array = qw(BG); foreach my $key (@array) { $this->MakeParameterConditionColumn($Frame, "$key", "$key:", "%12.6g", $this->{dini}->pVariable("$key", 0.0), $this->{dini}->pVariable("${key}check", 0), $this->{dini}->pVariable("${key}scale", 0.1), $this->{dini}->pVariable("${key}min", ''), $this->{dini}->pVariable("${key}max", ''), $EntryWidth, $row++); } $Frame = $MainPaneFrame->MyLabFrame( -label => 'Spectra parameters', -labelside => 'acrosstop', )->pack(-anchor => 'nw'); $row = 1; $this->MakeParameterConditionHeading($Frame, 0, 0); @array = qw(k dE); for(my $i = 0 ; $i < $this->{ini}->{nSpectra} ; $i++) { foreach my $key0 (@array) { my $key = "$key0$i"; $this->MakeParameterConditionColumn($Frame, "$key", "$key:", "%12.6g", $this->{dini}->pVariable("$key", 0.0), $this->{dini}->pVariable("${key}check", 0), $this->{dini}->pVariable("${key}scale", 0.1), $this->{dini}->pVariable("${key}min", ''), $this->{dini}->pVariable("${key}max", ''), $EntryWidth, $row++); } } $this->BuildFooter($MainPaneFrame, $EntryWidth); $this->UpdateParameters(); } sub BuildLSQPane { my ($this, $MainPaneFrame, $EntryWidth) = @_; my $Frame = $MainPaneFrame->MyFrame()->pack(-anchor => 'nw', -fill => 'x'); $this->mw()->{LSQFrame} = $this->MakeLSQPanes( $Frame, $EntryWidth, 'Fitting', [qw(LSQMethod FittingRange FittingConditions)], # [qw(LSQMethod FitTo FittingRange FittingConditions)], ['Amoeba::Simplex', 'PDL::Simplex', 'ModifiedNewton'], ['PES'], ['FitXMin', 'Emin:', 'FitXMax', 'Emax:', 'nSkipData', 'nSkip:'], ['EPS', 'EPS:', 'nMaxIter', 'MaxIter:', 'nSaveSpectraInterval', 'SaveIntvl:'], ); $this->mw()->{LSQFrame}->pack(-anchor => 'nw', -expand => 'yes', -fill => 'x'); # $this->mw()->{LSQFrame}->{FitToListBox}->SetText('alpha'); $this->BuildFooter($MainPaneFrame, $EntryWidth); } sub BuildViewRangePane { my ($this, $MainFrame, $EntryWidth) = @_; $this->SUPER::BuildViewRangePane($MainFrame, $EntryWidth, [ ['x'], ['x'] ], [ ['x'], ['x'] ] ); if($this->mw()->{NoteBook}->{ViewRangePaneFrame}->{GraphFrame1ViewY0Button}) { $this->mw()->{NoteBook}->{ViewRangePaneFrame}->{GraphFrame1ViewY0Button}->Select(); $this->mw()->{NoteBook}->{ViewRangePaneFrame}->{GraphFrame1ViewY0Entry}->SetText(0.0); } $this->BuildFooter($MainFrame, $EntryWidth); } sub BuildFooter { my ($this, $MainFrame, $EntryWidth) = @_; my $Frame1 = $MainFrame->MyFrame()->pack(-anchor => 'nw', -fill => 'x'); # my $ButtonFrame = $this->MakeFittingButtons($Frame1, ("RePlot", "RestoreScale", "Re&calc", "&Fit", "Flip X", "&Save")); my $ButtonFrame = $this->MakeFittingButtons($Frame1, ("Re&calc", "&Fit", "&Save")); $ButtonFrame->pack(-anchor => 'nw', -expand => 'yes', -fill => 'x'); # $this->{RestoreScaleButton}->configure( # -command => sub { $this->RestoreScale(); }, # ); } #sub RestoreScale #{ # my ($this, $ResetViewRange, $CreateGraphFrame, $AssignGraphData, $Draw) = @_; # $ResetViewRange = 1 if(!defined $ResetViewRange); # $this->RePlot($ResetViewRange, $CreateGraphFrame, $AssignGraphData, $Draw, $this->{ini}->{FlipX}); #} #sub RestoreViewScale #{ # my ($this, $ResetViewRange, $CreateGraphFrame, $AssignGraphData, $Draw) = @_; # $this->RestoreScale(); #} sub RePlot { my ($this, $ResetViewRange, $CreateGraphFrame, $AssignGraphData, $Draw) = @_; $this->Recalc(); } sub ChooseFile { #action = 'open'; #'save' #fmask = '*.csv;*.spe;*.jel;*.asp;*.smo;*isa;*.ref;*.pal;*.bef;*.aft;*.prm'; my ($this, $action, $fmask, $Option, $name, $Frame) = @_; #print "TkFittingCommon2::ChooseFile: a=$action, f=$fmask, n=$name, F=$Frame, o=$Option\n"; my $App = $this->App(); my $canvas = $this->Canvas(); my $mw = $this->mw(); my $Entry = $Frame->{PathEntry}; my $filepath = $this->ChooseAFile($name, $action, $fmask, '', "Choose file", $Entry, $Option); return undef if(!defined $filepath or $filepath eq ''); my $ret; if($Option eq 'ChooseParameterFile') { $ret = $this->ReadParameterFile($filepath, 1, $Frame); } elsif($Option =~ /ChooseSample.*File/i) { $ret = $this->ReadSampleDataFile($filepath, 1, $Frame); } elsif($Option =~ /ChooseSpectrumFile(\d*)/i) { my $idx = $1; #print "Option=$Option idx=$idx\n"; $ret = $this->ReadSpectrumDataFile($filepath, $idx, 1, $Frame); } elsif($Option =~ /ChooseSubstrateFile/i) { $ret = $this->ReadSubstrateDataFile($filepath, 1, $Frame); } return undef unless($ret); $this->CreateGraphFrame() if(!$this->GetGraphFrameArray()); return $ret; } #=================================================== # データ処理 #=================================================== sub BuildOptimize { my ($this, $IsPrint) = @_; $IsPrint = 1 if(!defined $IsPrint); my $ini = $this->{ini}; my $dini = $this->{dini}; my $optimize = $this->{Optimize} = new Optimize;; $optimize->AddParameters( "BG", \$ini->{BG}, $dini->{BGcheck}, $dini->{BGscale}, $dini->{BGmin}, $dini->{BGmax}, sub { $ini->{BG} = $_[0]; }, ); for(my $i = 0 ; $i < $this->{ini}->{nSpectra} ; $i++) { my $ii = $i; $optimize->AddParameters( "k$ii", \$ini->{"k$ii"}, $dini->{"k${ii}check"}, $dini->{"k${ii}scale"}, $dini->{"k${ii}min"}, $dini->{"k${ii}max"}, sub { $ini->{"k$ii"} = $_[0]; }, "dE$ii", \$ini->{"dE$ii"}, $dini->{"dE${ii}check"}, $dini->{"dE${ii}scale"}, $dini->{"dE${ii}min"}, $dini->{"dE${ii}max"}, sub { $ini->{"dE$ii"} = $_[0]; }, ); } } sub Recalc { my ($this) = @_; return if(!defined $this->{DataFile}); my $ini = $this->{ini}; my $X1Label = $this->{SampleFileFrame}->{X1ListBox}->GetText(); my $Y1Label = $this->{SampleFileFrame}->{Y1ListBox}->GetText(); my $XMin = $ini->{FitXMin}; my $XMax = $ini->{FitXMax}; my $nSkipData = $ini->{nSkipData}; my $pXS = $this->{DataFile}->GetData($X1Label) if($X1Label); my $pYS = $this->{DataFile}->GetData($Y1Label) if($Y1Label); my (@pSpectrumData); my $nCurves = 0; for(my $i = 0 ; $i < $this->{ini}->{nSpectra} ; $i++) { my ($DataKey, $FrameKey) = ("SpectrumFile$i", "Spectrum${i}FileFrame"); #print "Recalc: $i: $DataKey\n"; my $pData = $this->{$DataKey}; next if(!defined $pData); my $nData = $pData->nData(); next if($nData <= 0); my $Frame = $this->{$FrameKey}; $X1Label = $Frame->{X1ListBox}->GetText(); $Y1Label = $Frame->{Y1ListBox}->GetText(); $X1Label = 'X' if(!$X1Label); $Y1Label = 'Y1' if(!$Y1Label); $pData->GetData($Frame->{X1ListBox}->GetText()); $pData->GetData($Frame->{Y1ListBox}->GetText()); $pSpectrumData[$nCurves] = $pData; $nCurves++; } my (@X, @Y); my $c = 0; for(my $i = 0 ; $i < @$pXS ; $i++) { my $x = $pXS->[$i]; next if($x < $XMin or $XMax < $x); next if($nSkipData > 0 and $i % $nSkipData != 0); $X[$c] = $x; $Y[$c] = $pYS->[$i]; $c++; } my ($pYTotal, $ppCalY) = $this->CalTotalSpectrum(\@X, \@pSpectrumData, $this->{ini}); $this->{pObsX} = \@X; $this->{pObsY} = \@Y; $this->{pCalX} = \@X; $this->{pCalY} = $pYTotal; $this->{ppCalY} = $ppCalY; $this->SUPER::RePlot(); } sub Fit { my ($this) = @_; return if(!defined $this->{DataFile}); my $ini = $this->{ini}; $this->UpdateParameters(); $this->BuildOptimize(); my $Method = $this->mw()->{LSQFrame}->{LSQMethodListBox}->GetText(); #$this->{LSQMethodListBox}->GetText(); my $optimize = $this->{Optimize}; print "Method: $Method\n"; print "nMaxIter: $ini->{nMaxIter}\n"; my $X1Label = $this->{SampleFileFrame}->{X1ListBox}->GetText(); my $Y1Label = $this->{SampleFileFrame}->{Y1ListBox}->GetText(); my $XMin = $ini->{FitXMin}; my $XMax = $ini->{FitXMax}; my $nSkipData = $ini->{nSkipData}; my $pXS = $this->{DataFile}->GetData($X1Label) if($X1Label); my $pYS = $this->{DataFile}->GetData($Y1Label) if($Y1Label); my (@pSpectrumData); my $nCurves = 0; for(my $i = 0 ; $i < $this->{ini}->{nSpectra} ; $i++) { my ($DataKey, $FrameKey) = ("SpectrumFile$i", "Spectrum${i}FileFrame"); #print "Recalc: $i: $DataKey\n"; my $pData = $this->{$DataKey}; next if(!defined $pData); my $nData = $pData->nData(); next if($nData <= 0); my $Frame = $this->{$FrameKey}; $X1Label = $Frame->{X1ListBox}->GetText(); $Y1Label = $Frame->{Y1ListBox}->GetText(); $X1Label = 'X' if(!$X1Label); $Y1Label = 'Y1' if(!$Y1Label); $pData->GetData($Frame->{X1ListBox}->GetText()); $pData->GetData($Frame->{Y1ListBox}->GetText()); $pSpectrumData[$nCurves] = $pData; $nCurves++; } my (@X, @Y); my $c = 0; for(my $i = 0 ; $i < @$pXS ; $i++) { my $x = $pXS->[$i]; next if($x < $XMin or $XMax < $x); next if($nSkipData > 0 and $i % $nSkipData != 0); $X[$c] = $x; $Y[$c] = $pYS->[$i]; $c++; } #========================================================= # 最適化の実行 #========================================================= $optimize->SetnS2Calculation(0); my ($OptVars, $MinVal) = $optimize->Optimize( $Method, undef, undef, undef, $ini->{EPS}, $ini->{nMaxIter}, $ini->{iPrintLevel}, sub { $this->CalS2(\@X, \@Y, \@pSpectrumData, @_); }, undef, sub { Optimize::BuildDifferentialMatrixes(@_); }, ); print "\nOptimized at:\n"; $optimize->RecoverParameters($OptVars); $optimize->PrintParameters(1, $OptVars, $MinVal, 1); $this->SetS2($MinVal); $this->UpdateParameters(); $this->Recalc(); } sub CalTotalSpectrum { my ($this, $pX, $ppSpectrumData, $ini) = @_; my $BG = $ini->{BG}; my (@k, @dE); my @pCalY; my @YTotal; for(my $j = 0 ; $j < @$pX ; $j++) { $YTotal[$j] = $BG; # $YTotal[$j] = 0.0; } for(my $i = 0 ; $i < $ini->{nSpectra} ; $i++) { my $k = $ini->{"k$i"}; my $dE = $ini->{"dE$i"}; my $pData = $ppSpectrumData->[$i]; next if(!defined $pData); my @YCal; for(my $j = 0 ; $j < @$pX ; $j++) { my $x = $pX->[$j]; my $y = $k * $pData->YVal($x + $dE); $YCal[$j] = $y; $YTotal[$j] += $y; } $pCalY[$i] = \@YCal; } return (\@YTotal, \@pCalY); } sub CalS2 { my ($this, $pX, $pY, $ppSpectrumData, $pVars, $iPrintLevel) = @_; #print "pVars=", join(',', @$pVars), "\n"; my $optimize = $this->{Optimize}; my $ini = $this->{ini}; $optimize->RecoverParameters($pVars); my %p; my $idx = 0; $p{nSpectra} = $ini->{nSpectra}; $p{BG} = $pVars->[$idx++]; for(my $i = 0 ; $i < $this->{ini}->{nSpectra} ; $i++) { $p{"k$i"} = $pVars->[$idx++]; $p{"dE$i"} = $pVars->[$idx++]; } my ($pYTotal, $ppCalY) = $this->CalTotalSpectrum($pX, $ppSpectrumData, \%p); my $S2 = 0.0; my $I2 = 0.0; my $c = 0; for(my $i = 0 ; $i < @$pX ; $i++) { my $x = $pX->[$i]; my $d = $pYTotal->[$i] - $pY->[$i]; $S2 += $d * $d; $I2 += $pY->[$i] * $pY->[$i]; $c++; } # $S2 = sqrt($S2 / $c) if($c > 0); $S2 = sqrt($S2 / $I2); $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->SetS2($S2); $this->Recalc(); $this->RePlot(); } $this->UpdateParameters(); $this->mw()->update(); return $S2; } sub Save { my ($this) = @_; $this->Recalc() if(!defined $this->{pCalX}); my $path = $this->{ini}->{ParameterFile}; $path = $this->{ini}->{SampleCSVFile} if(!$path); my ($drive, $directory, $filename, $ext1, $lastdir, $filebody) = Deps::SplitFilePath($path); my $dir = Deps::ExtractDirectory($path); $path = Deps::MakePath($dir, "MultiFit-$filebody.csv", 0); $this->print("\nSave decomposed spectra to [$path].\n"); my $out = new JFile($path, "w"); if(!$out) { $this->print("Error: Can not write to [$path].\n"); return; } # $out->print("E,PES(obs),PES(cal)"); # $out->print(",BG(cal)\n"); # # my $pE = $this->{pCalX}; # my $pObs = $this->{pObsY}; # my $pPES = $this->{pCalY}; # my $pBG = $this->{pCalBG}; # for(my $i = 0 ; $i < @$pE ; $i++) { # $out->print("$pE->[$i],$pObs->[$i],$pPES->[$i]"); # $out->print(",$pBG->[$i]\n"); # } $out->print("E,PES(sample),PES(fitted)"); for(my $i = 0 ; $i < $this->{ini}->{nSpectra} ; $i++) { $out->print(",PES(Spectrum$i)"); } $out->print("\n"); my $pCalX = $this->{pCalX}; my $pObs = $this->{pObsY}; my $pCalY = $this->{pCalY}; for(my $i = 0 ; $i < @$pCalX ; $i++) { $out->print("$pCalX->[$i],$pObs->[$i],$pCalY->[$i]"); for(my $is = 0 ; $is < $this->{ini}->{nSpectra} ; $is++) { my $pCalX = $this->{pCalX}; my $pCalY = $this->{ppCalY}->[$is]; if(defined $pCalY->[$i]) { $out->print(",$pCalY->[$i]"); } else { $out->print(","); } } $out->print("\n"); } $out->Close(); } sub SaveParameterFile { my ($this, $Option, $filepath, $name, $Frame) = @_; $filepath = $Frame->{PathEntry}->GetText() if($filepath eq ''); $this->{ini}->{SampleX1} = $this->{SampleFileFrame}->{X1ListBox}->GetText(); $this->{ini}->{SampleY1} = $this->{SampleFileFrame}->{Y1ListBox}->GetText(); # $this->{ini}->{AppFunction} = $this->{ChooseAppFunctionListBox}->GetText(); # $this->{ini}->{ConductionType} = $this->{ChooseConductionTypeListBox}->GetText(); $this->{ini}->{YAxisPlotType} = $this->mw()->{NoteBook}->{ViewRangePaneFrame}->{GraphFrame1YAxisListBox}->GetText(); if(!defined $filepath or $filepath eq '') { $filepath = $this->{ini}->{SampleCSVFile}; # $filepath = $this->{ini}->{SubstrateCSVFile} if(!defined $filepath or $filepath eq ''); $filepath = 'save.prm' if(!defined $filepath or $filepath eq ''); $filepath =~ s/\.csv$/\.prm/i; $filepath = Deps::ExtractFileName($filepath); $filepath = $this->ChooseSaveFile($filepath, '*.prm'); if($filepath) { my $ext = Deps::ExtractExtention($filepath); if(!defined $ext or $ext eq '') { $filepath = Deps::ReplaceExtension($filepath, ".prm"); } $this->{ini}->{ParameterFile} = $filepath; } else { return; } } $this->{ini}->SetIniFile($filepath, undef, 1); $this->App()->print("\nSaveParameterFile:\n"); $this->ComposeParameters($this->{dini}, $this->{ini}); $this->{ini}->WriteAll(); $this->DecomposeParameters($this->{ini}, $this->{dini}); } sub ReadParameterFile { my ($this, $filepath) = @_; my $ini = $this->{ini}; my $dini = $this->{dini}; $this->Initialize(1, 1); $this->App()->print("\nRead paramter from [$filepath].\n"); $this->{ini}->ReadAll($filepath); $this->DecomposeParameters($ini, $dini); $this->RefreshMainPane($this->{EntryWidth}); my $DataFrame = $this->{SampleFileFrame}; $this->{ini}->{SampleX1} = $this->{ini}->{X} if(!defined $this->{ini}->{SampleX1}); $this->{ini}->{SampleY1} = $this->{ini}->{Y1} if(!defined $this->{ini}->{SampleY1}); $DataFrame->{X1ListBox}->SetText($this->{ini}->{SampleX1}) if($this->{ini}->{SampleX1}); $DataFrame->{Y1ListBox}->SetText($this->{ini}->{SampleY1}) if($this->{ini}->{SampleY1}); # $this->{ChooseAppFunctionListBox}->SetText($this->{ini}->{AppFunction}); # $this->{ChooseConductionTypeListBox}->SetText($this->{ini}->{ConductionType}); if($this->{ini}->{SampleCSVFile}) { my $path = $this->{ini}->{SampleCSVFile}; next if($path eq ''); $path = $this->GetValidDataPath($path, $filepath, $this->{ini}->{ParameterFile}); my $ret = $this->ReadSampleDataFile($path, 1, 0, 0); return undef unless($ret); $this->{ini}->{SampleCSVFile} = $path; $this->{ini}->{ParameterFile} = $filepath; } for(my $i = 0 ; $i < $this->{ini}->{nSpectra} ; $i++) { my $FileKey = "SpectrumCSVFile$i"; my $path = $this->{ini}->{$FileKey}; next if($path eq ''); $path = $this->GetValidDataPath($path, $filepath, $this->{ini}->{ParameterFile}); my $ret = $this->ReadSpectrumDataFile($path, $i, 1, 0, 0); return undef unless($ret); $this->{ini}->{$FileKey} = $path; $this->{ini}->{ParameterFile} = $filepath; } # $this->RePlot(0, 1, 0, 0); $this->RefreshViewRangePane($this->{EntryWidth}); my $YAxisListBox = $this->{ini}->{YAxisPlotType}; #$this->mw()->{NoteBook}->{ViewRangePaneFrame}->{GraphFrame1YAxisListBox}; if($YAxisListBox) { $this->mw()->{NoteBook}->{ViewRangePaneFrame}->{GraphFrame1YAxisListBox}->SetText($this->{ini}->{YAxisPlotType}) if($this->{ini}->{YAxisPlotType}); } $this->CreateGraphFrame() if(!$this->GetGraphFrameArray()); $this->AssignGraphData(); $this->Draw(); # $this->RePlot(1, 0, 1, 1); return 1; } sub ReadSampleDataFile { my ($this, $filepath, $IsPrint, $UpdateRange, $IsPlot, $Frame) = @_; return undef if($filepath eq ''); $UpdateRange = 1 if(!defined $UpdateRange); $IsPlot = 1 if(!defined $IsPlot); my $App = $this->App(); $Frame = $this->{SampleFileFrame} if(!defined $Frame); my $pXDataLabel = $Frame->{pXDataLabel}; my $pYDataLabel = $Frame->{pYDataLabel}; #print "type=$type\n"; $this->Initialize(0, 1); my $pData = $this->{DataFile} = new Ellipsometry; $this->{Path} = $filepath; my ($nData, $pLabelArray, @pDataArray) = $pData->Read($filepath); if(!defined $nData) { $App->print("Error: Can not read [$filepath].\n"); return $this->{Path} = undef; } if(!Utils::IsConstantStepArray($pDataArray[0], 0.01)) { $App->print("Error: The data step for X is not constant.\n"); return $this->{Path} = undef; } my $nDataArray = $this->{DataFile}->nDataArray(); $Frame->{X1ListBox}->DeleteAllItem() if(defined $Frame->{X1ListBox}); $Frame->{Y1ListBox}->DeleteAllItem() if(defined $Frame->{Y1ListBox}); for(my $i = 0 ; $i < $nDataArray ; $i++) { $Frame->{X1ListBox}->AddItem($pLabelArray->[$i]) if(defined $Frame->{X1ListBox}); $Frame->{Y1ListBox}->AddItem($pLabelArray->[$i]) if(defined $Frame->{Y1ListBox}); } $Frame->{X1ListBox}->SetCurSel(@$pXDataLabel) if(defined $Frame->{X1ListBox}); $Frame->{Y1ListBox}->SetCurSel(@$pYDataLabel) if(defined $Frame->{Y1ListBox}); my $pX = $pData->GetXData(@$pXDataLabel); $pData->GetYData(@$pYDataLabel); if($pX->[1] < $pX->[0]) { $this->{DataFile}->ReverseData(); } $pData->CalMinMax(); ($this->{ini}->{FitEmin}, $this->{ini}->{FitEmax}) = $pData->GetXMinMax() if(!defined $this->{ini}->{FitEmin} or $this->{ini}->{FitEmin} eq ''); if($UpdateRange or !defined $this->{ini}->{FitXMin}) { ($this->{ini}->{FitXMin}, $this->{ini}->{FitXMax}) = Utils::CalMinMax($pDataArray[0]); } if($IsPlot) { $this->RePlot($UpdateRange); } else { $this->CreateGraphFrame() if(!$this->GetGraphFrameArray()); $this->AssignGraphData(); } return $filepath; } sub ReadSpectrumDataFile { my ($this, $filepath, $idx, $IsPrint, $UpdateRange, $IsPlot, $Frame) = @_; $UpdateRange = 1 if(!defined $UpdateRange); $IsPlot = 1 if(!defined $IsPlot); my $App = $this->App(); $Frame = $this->{"Spectrum${idx}FileFrame"} if(!defined $Frame); my $pXDataLabel = $Frame->{pXDataLabel}; my $pYDataLabel = $Frame->{pYDataLabel}; my $pData = $this->{"SpectrumFile$idx"} = new Ellipsometry; #print "Read [$filepath] as Spectrum data #$idx\n"; $this->{"SpectrumFilePath$idx"} = $filepath; my ($nData, $pLabelArray, @pDataArray) = $pData->Read($filepath); if(!defined $nData) { $App->print("Error: Can not read [$filepath].\n"); return $this->{"SpectrumFilePath$idx"} = undef; } if(!Utils::IsConstantStepArray($pDataArray[0], 0.01)) { $App->print("Error: The data step for X is not constant.\n"); return $this->{"SpectrumFilePath$idx"} = undef; } my $nDataArray = $this->{"SpectrumFile$idx"}->nDataArray(); $Frame->{X1ListBox}->DeleteAllItem() if(defined $Frame->{X1ListBox}); $Frame->{Y1ListBox}->DeleteAllItem() if(defined $Frame->{Y1ListBox}); for(my $i = 0 ; $i < $nDataArray ; $i++) { $Frame->{X1ListBox}->AddItem($pLabelArray->[$i]) if(defined $Frame->{X1ListBox}); $Frame->{Y1ListBox}->AddItem($pLabelArray->[$i]) if(defined $Frame->{Y1ListBox}); } $Frame->{X1ListBox}->SetCurSel(@$pXDataLabel) if(defined $Frame->{X1ListBox}); $Frame->{Y1ListBox}->SetCurSel(@$pYDataLabel) if(defined $Frame->{Y1ListBox}); my $pX = $pData->GetXData(@$pXDataLabel); $pData->GetYData(@$pYDataLabel); if($pX->[1] < $pX->[0]) { $this->{"SpectrumFile$idx"}->ReverseData(); } $pData->CalMinMax(); $this->CreateGraphFrame() if(!$this->GetGraphFrameArray()); # $this->AssignGraphData(); # $this->RePlot($UpdateRange); return $filepath; } sub CreateGraphFrame { my ($this, $canvas, $TargetData, $DoUpdate) = @_; return if(!$DoUpdate and $this->GetGraphFrameArray()); $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 $RefreshGraphScaleFrame = (defined $this->{GraphFrameArray})? 0 : 1; my $GraphFrameArray = $this->{GraphFrameArray} = new GraphFrameArray($this->mw()); $GraphFrameArray->SetCanvasSize($w, $h); $GraphFrameArray->AddGraphFrame(); my $GraphFrame0 = $GraphFrameArray->GetGraphFrame(0); my $FramePosStr0 = $App->{GraphFrame0Position}; $GraphFrame0->SetPositionByStr($FramePosStr0); my $XScale0 = $GraphFrame0->GetXScale(0); my $YScale0 = $GraphFrame0->GetYScale(0); $XScale0->SetScaleStringVisible(1); $XScale0->SetCaptionVisible(1); $GraphFrame0->SetViewRange(0, 0, 1, 1); if($RefreshGraphScaleFrame) { my $EntryWidth = $this->{EntryWidth}; $this->RefreshViewRangePane($EntryWidth); } } sub AssignGraphData { my ($this, $ResetViewRange) = @_; $ResetViewRange = 1 if(!defined $ResetViewRange); my $ini = $this->{ini}; # return if(!defined $ini->{SampleCSVFile}); return if(!defined $this->mw()->{NoteBook}->{ViewRangePaneFrame}->{GraphFrame1YAxisListBox}); my $GraphFrameArray = $this->GetGraphFrameArray(); my $GraphFrame0 = $GraphFrameArray->GetGraphFrame(0); $GraphFrame0->ClearAllData(); my ($X1Label, $Y1Label); my @colors = ("black", "blue", "red", "green", "cyan", "purple", "yellow", "darkgray"); for(my $i = -1 ; $i < $this->{ini}->{nSpectra} ; $i++) { my ($DataKey, $FrameKey); if($i == -1) { $DataKey = "DataFile"; $FrameKey = "SampleFileFrame"; } else { $DataKey = "SpectrumFile$i"; $FrameKey = "Spectrum${i}FileFrame"; } my $pData = $this->{$DataKey}; next if(!defined $pData); my $nData = $pData->nData(); next if($nData <= 0); my $Frame = $this->{$FrameKey}; $X1Label = $Frame->{X1ListBox}->GetText(); $Y1Label = $Frame->{Y1ListBox}->GetText(); $X1Label = 'X' if(!$X1Label); $Y1Label = 'Y1' if(!$Y1Label); my $pX = $pData->GetData($Frame->{X1ListBox}->GetText()); my $pY1 = $pData->GetData($Frame->{Y1ListBox}->GetText()); $GraphFrame0->SetXCaption($X1Label); $GraphFrame0->SetYCaption($Y1Label); my $color = $colors[($i+1) % @colors]; my $label = ($i == -1)? "Sample data" : "Spectrum$i"; my $fname = ($i == -1)? $ini->{"SampleCSVFile"} : $ini->{"SpectrumCSVFile$i"}; if($pX) { $GraphFrame0->AddGraphData($pX, $pY1, 2, $color, "", 6, $color, 0, $color, "XAutoSkip", "Energy", "$fname - $label") if($pY1); } } my $pCalX = $this->{pCalX}; my $pCalY = $this->{pCalY}; if($pCalX) { $GraphFrame0->AddGraphData($pCalX, $pCalY, 0, "", "circle", 3, "", 1, "red", "XAutoSkip", "Energy", "Calc (total)") if($pCalY); } for(my $i = 0 ; $i < $this->{ini}->{nSpectra} ; $i++) { my $pCalX = $this->{pCalX}; my $pCalY = $this->{ppCalY}->[$i]; next if(!defined $pCalY); $GraphFrame0->AddGraphData($pCalX, $pCalY, 0, "", "cross", 3, "", 1, "green", "XAutoSkip", "Energy", "Calc ($i)") if($pCalY); } $GraphFrame0->SetXScalePlotType('x'); $GraphFrame0->SetYScalePlotType('x'); $this->SetViewRange($ResetViewRange);#, $FlipX); } sub Initialize { my ($this, $InitializeParameterFile, $InitializeData) = @_; $this->{DataFile} = new Ellipsometry; if($InitializeParameterFile) { $this->{ini}->{SampleCSVFile} = ''; # $this->{ini}->{FlipX} = 0; $this->{ini}->{FitXMin} = ''; $this->{ini}->{FitXMax} = ''; } if($InitializeData) { $this->{pCalX} = undef; $this->{pCalY} = undef; $this->{pCalBG} = undef; } } sub InitializeParameters { my ($this, $ini, $dini) = @_; $this->AddParameters($ini, $dini, "BG", 0.0, 1, 1000.0, 0.0, ''); for(my $i = 0 ; $i < $this->{ini}->{nSpectra} ; $i++) { $this->AddParameters($ini, $dini, "k$i", 1.0, 1, 0.1, 0.0, ''); $this->AddParameters($ini, $dini, "dE$i", 0.0, 1, 0.1, '', ''); } } 1;