#=============================================== # ChemicalComposition #=============================================== package ChemicalComposition; use TkFittingSimple; @ISA = qw(TkFittingSimple); #公開したいサブルーチン #@EXPORT = qw(DelSpace Reduce01 MakePath RegExpQuote); use strict; use IniFile; use Sci qw($pi $kB $e $e0 $me $h $hbar $KToeV erfc); use Sci::ChemicalReaction; #============================================================ # 大域変数・変数等取得関数 #============================================================ my $ProgramName = "ChemicalComposition/2011"; #============================================================ # コンストラクタ、デストラクタ #============================================================ #呼び出されることはない sub new { my ($class, $app, @a) = @_; my $self = TkFittingSimple->new($app, $ProgramName, @a); my $this = bless $self, $class; $this->{ini} = new IniFile(); $this->{nMaxReagents} = 5; return $this; } sub DESTROY { my $this = shift; $this->SUPER::DESTROY(@_); } #============================================================ # 継承クラスで定義しなおす関数 #============================================================ sub CreateWidgets { my ($this, $DoSuperOnly) = @_; my $App = $this->App(); my $mw = $this->mw(); my $args = $App->Args(); my $IniFile = $this->App()->IniFile(); $mw->SetTitle("$ProgramName / TkPlot"); $this->App()->SetProgram("$ProgramName / TkPlot"); #Paneの設定 my @args = (-expand => 'yes', -fill => 'both'); # $this->{StatusBar} = $this->CreateStatusBar('bottom'); # $this->{ToolBar} = $this->CreateToolBar('top'); my $balloon = $this->Balloon(-statusbar => $this->StatusBar() ); $this->SetBalloon($balloon); $App->{LeftFrameWidth} = 50 if(!$App->{LeftFrameWidth}); $App->{EditBoxWidth} = 40 if(!$App->{EditBoxWidth}); # $this->{LeftFrame1} = $this->CreateLeftFrame('left', @args); $this->{LeftFrame1} = $this->MyFrame( # -width => $App->{'LeftFrameWidth'}, )->pack(-side => 'top', @args); #=================================================== # Notebookを作製 #=================================================== 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(ChemicalComposition FindReaction Batch)], # [qw(Main ViewRange LSQ GaussLorentz Setup GraphFrame)], [qw()], # [qw(nPESStepBG nGaussLorentz)], ); $mw->{NoteBook}->pack(-fill => 'both', -expand => 'yes'); $this->UpdateParameters(); #=================================================== # 右ペインを作製 #=================================================== $this->{RightFrame1} = $this->MyFrame( # -width => $App->{'LeftFrameWidth'}, )->pack(-side => 'top', @args); $this->{TextBox} = $this->{RightFrame1}->Scrolled( 'MyText', # -readonly => 1, # -width => $App->{EditBoxWidth}, -height => 20, -scrollbars => 'e', -takefocus => 1, )->pack(-side => 'top', -expand => 'yes', -fill => 'both'); $this->SetMinSize(); $this->update(); #=================================================== #Windowが初めて表示されたときに、InitWindowを実行する #=================================================== $this->CheckArguments(); # $this->InitWindowPosition() if($^O ne 'MSWin32'); $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()); } ); return $this; } sub BuildChemicalCompositionPane { my ($this, $MainPaneFrame, $EntryWidth) = @_; my $ini = $this->{ini}; $ini->{ChemicalFormula1} = "LaO" if(!defined $ini->{ChemicalFormula1}); $ini->{Weight1} = 1.0 if(!defined $ini->{Weight1}); $ini->{ChemicalFormula2} = "FeAs" if(!defined $ini->{ChemicalFormula2}); $ini->{Weight2} = 3.0 if(!defined $ini->{Weight2}); $ini->{ChemicalFormula3} = "Fe2As" if(!defined $ini->{ChemicalFormula3}); $ini->{Weight3} = 4.0 if(!defined $ini->{Weight3}); 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->MyLabFrame( -label => 'Reagentns', -labelside => 'acrosstop', )->pack(-anchor => 'nw'); $EntryWidth = 15; for(my $i = 1 ; $i <= $this->{nMaxReagents} ; $i++) { $this->MakeLabelEntry($Frame, "ChemicalFormula$i", "ChemicalFormula:", $this->{ini}->pVariable("ChemicalFormula$i", ''), "%s", $EntryWidth, "", $i-1, 0); $this->MakeLabelEntry($Frame, "Weight$i", "Weight:", $this->{ini}->pVariable("Weight$i", ''), "%8.4g", 8, "g", $i-1, 1); } $Frame = $MainPaneFrame->MyFrame()->pack(-anchor => 'nw', -fill => 'x'); $this->{CcalcButton} = $Frame->MyButton( -text => '&Calc', -takefocus => 1, -command => sub { $this->ButtonPressed('RButtonDown', 'CalcChemicalComposition'); }, )->pack(-side => 'left'); } sub BuildFindReactionPane { my ($this, $MainPaneFrame, $EntryWidth) = @_; my $ini = $this->{ini}; $ini->{Reagents} = "K2O+Sr+FeAs+Fe2As" if(!defined $ini->{Reagents}); $ini->{Products} = "Sr0.9K0.1Fe2As2" if(!defined $ini->{Products}); $EntryWidth = 60;# if(!defined $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->MyLabFrame( -label => 'Reagentns', -labelside => 'acrosstop', )->pack(-anchor => 'nw'); $this->MakeLabelEntry($Frame, "Reagents", "Reagents:", $this->{ini}->pVariable("Reagents", ''), "%s", $EntryWidth, "", 0, 0); $this->MakeLabelEntry($Frame, "Products", "Products:", $this->{ini}->pVariable("Products", ''), "%s", $EntryWidth, "", 1, 0); $Frame = $MainPaneFrame->MyFrame()->pack(-anchor => 'nw', -fill => 'x'); $this->{CcalcButton} = $Frame->MyButton( -text => '&Calc', -takefocus => 1, -command => sub { $this->ButtonPressed('RButtonDown', 'CalFindReaction'); }, )->pack(-side => 'left'); } sub BuildBatchPane { my ($this, $MainPaneFrame, $EntryWidth) = @_; my $ini = $this->{ini}; $ini->{Reaction} = "2LaO+Fe1.8Ca0.2As+As=>2LaFe0.9Ca0.1AsO" if(!defined $ini->{Reaction}); $ini->{TargetWeight} = 1.0 if(!defined $ini->{TargetWeight}); $EntryWidth = 60;# if(!defined $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->MyLabFrame( -label => 'Reagentns', -labelside => 'acrosstop', )->pack(-anchor => 'nw'); $this->MakeLabelEntry($Frame, "Reaction", "Reaction:", $this->{ini}->pVariable("Reaction", ''), "%s", $EntryWidth, "", 0, 0); $this->MakeLabelEntry($Frame, "TargetWeight", "Weight:", $this->{ini}->pVariable("TargetWeight", ''), "%8.4g", $EntryWidth, "g", 1, 0); $Frame = $MainPaneFrame->MyFrame()->pack(-anchor => 'nw', -fill => 'x'); $this->{CcalcButton} = $Frame->MyButton( -text => '&Calc', -takefocus => 1, -command => sub { $this->ButtonPressed('RButtonDown', 'CalcBatch'); }, )->pack(-side => 'left'); } sub H2 { my ($this, @a) = @_; $this->App()->PrintRawHTML(@a); } sub H3 { my ($this, @a) = @_; $this->App()->PrintRawHTML(@a); } sub BeginRow { my ($this, @a) = @_; } sub EndRow { my ($this, @a) = @_; } sub PrintRawHTML { my ($this, @a) = @_; for(my $i = 0 ; $i < @a ; $i++) { my $s = $a[$i]; $s =~ s/
/\n/sig; $s =~ s/<[^>]*>//sg; $this->App()->print($s); } } sub BeginTable { my ($this, @a) = @_; } sub EndTable { my ($this) = @_; } sub TableCell { my ($this) = @_; } sub TableRowByArray { my ($this, $p) = @_; $this->App()->print(@$p); } sub ClearTextBox { my ($this) = @_; $this->{TextBox}->SetText(''); } sub AddText { my ($this, @a) = @_; my $s = $this->{TextBox}->GetText(); # $s =~ s/\r\n/\n/sg; # $s =~ s/\n\r/\n/sg; # $s =~ s/\r//sg; $s =~ s/[\r\n]$//sg; $this->{TextBox}->SetText($s . join('', @a) . "\n"); } sub CalcChemicalComposition { my ($this) = @_; $this->CalTotalFormula($this, $this->{ini}); } sub CalTotalFormula { my ($this, $App, $pParams) = @_; $this->ClearTextBox(); $this->AddText("\nCalculate total chemical formula from a reagent mixture\n"); $App->H2("Calculate total chemical formula from a reagent mixture\n"); my %molHash; my $TotalWeight = 0.0; $App->BeginTable(1); $App->TableRowByArray( ["", "<=>Compound", "<=>Z", "<=>n", "<=>Mass (g/mol)", "", ""] ); for(my $i = 1 ; $i <= $this->{nMaxReagents} ; $i++) { #$App->print("i=$i\n"); my $R = new ChemicalReaction(undef, undef, 'Composition'); my $cf = $pParams->{"ChemicalFormula$i"}; my $ch = $R->ReactionToHTML($cf); my $w = $pParams->{"Weight$i"}; last if(!defined $cf or $cf eq '' or !defined $w or $w == 0.0); $TotalWeight += $w; my $MW = $R->MolecularWeight($cf); my $mol = $w / $MW; $App->TableRowByArray( ["==>$i", "==>$ch", "", "", "==>$MW"] ); $this->AddText("$i: Compound [$ch] MW=$MW\n"); $R->AnalyzeAReaction('Reagents', $cf); my $pHash = $R->ReagentsHash(); my $pElements = $R->ElementsArray("Reagents"); for(my $j = 0 ; $j < @$pElements ; $j++) { my $e = $pElements->[$j]; my $n = $R->nElement("Reagents", $e); my $pAtomDB = AtomType::GetAtomDBHash($e); # $App->printf(" $e: Z=%7.3f: M=%7.3f: n=$n\n", $pAtomDB->{Z}, $pAtomDB->{mass}); my $Z = sprintf("%7.0f", $pAtomDB->{Z}); my $Mass = sprintf("%7.4f", $pAtomDB->{mass}); $App->TableRowByArray( ["", "==>$e", "==>$Z", "==>$n", "==>$Mass"] ); $this->AddText("$i:$j: Compound [$e] Z=$Z n=$n M=$Mass\n"); $molHash{$e} += $n * $mol; } my $s = sprintf(" Mol = Weight / Molar mass
= (%5.3f g) / (%7.3f g/mol) = %7.5f mol\n", $w, $MW, $mol); $App->PrintRawHTML("$s\n"); $s = sprintf(" Mol = Weight / Molar mass = (%5.3f g) / (%7.3f g/mol) = %7.5f mol\n", $w, $MW, $mol); $this->AddText(" $s"); } $App->EndTable(); $App->PrintRawHTML("

Total weight: $TotalWeight g

\n"); $this->AddText("Total weight: $TotalWeight g\n"); my $MaxMol = -1.0e10; foreach my $e (sort keys %molHash) { $MaxMol = $molHash{$e} if($molHash{$e} > 0.0 and $MaxMol < $molHash{$e}); } $this->AddText("\n\nAmounts of each element:\n"); $App->H2("Amounts of each element:\n"); my %nRelAtom; $App->BeginTable(1); $App->TableRowByArray( ["<=>Element", "<=>mol", "<=>relative mol"] ); foreach my $e (sort keys %molHash) { # $App->printf("$e: %7.5f mol\n", $molHash{$e}); my $mol = sprintf("%7.5f", $molHash{$e}); my $rel = int($molHash{$e} / $MaxMol * 10000.0 + 0.5001); $nRelAtom{$e} = $rel; $App->TableRowByArray( ["==>$e", "==>$mol", "==>$rel"] ); $this->AddText(" Compound [$e] $mol mol rel=$rel\nAmounts of each element:\n"); } my ($OriginalComposition, $NormalizedComposition) = ChemicalReaction->NormalizeChemicalComposition(\%nRelAtom); $NormalizedComposition =~ s/\s//g; my $R = new ChemicalReaction(undef, undef, 'Composition'); my $nh = $R->ReactionToHTML($NormalizedComposition); $App->TableRowByArray( ["==>$nh::colspan=3"] ); $this->AddText(" Normalized compositon: $NormalizedComposition\n"); $App->EndTable(); return 1; } sub CalcBatch { my ($this) = @_; $this->BatchCalculation($this, $this->{ini}, 1); } sub BatchCalculation { my ($this, $App, $pParams, $CallFindReaction) = @_; $CallFindReaction = 1 if(!defined $CallFindReaction); $this->ClearTextBox(); $this->AddText("\nBatch calculation\n"); $App->H2("Batch calculation\n"); my $Reaction = $pParams->{Reaction}; my $TotalWeight = $pParams->{TargetWeight}; my $R = new ChemicalReaction(undef, undef, 'Composition'); $R->Analyze($Reaction, 0); $App->PrintRawHTML("

Reaction: ", $pParams->{Reaction}, "

\n"); $App->PrintRawHTML("

Reagents: ", $pParams->{Reagents}, "

\n"); $this->AddText("Reaction: ", $pParams->{Reaction}, "\n"); $this->AddText("Reagents: ", $pParams->{Reagents}, "\n"); my ($pChemicals, $pCompound, $pnCompound) = $R->ReactionToCompounds($R->{Initial}); my $TotalReagentsMolWeight = 0.0; for(my $i = 0 ; $i < @$pChemicals ; $i++) { my $c = $pCompound->[$i]; my $n = $pnCompound->[$i]; $TotalReagentsMolWeight += $n * $R->MolecularWeight($c); } #$App->print("Total MW=$TotalReagentsMolWeight\n"); $this->AddText("\nFor Reagents [$pParams->{Reagents}]\n"); $App->BeginTable(1); $App->TableRowByArray( ["", "<=>Compound", "<=>n", "<=>Z", "<=>Mass (g/mol)", "<=>Weight (g)"] ); for(my $i = 0 ; $i < @$pChemicals ; $i++) { my $c = $pCompound->[$i]; my $hh = $R->ReactionToHTML($pChemicals->[$i]); my $ch = $R->ReactionToHTML($pCompound->[$i]); my $MW = $R->MolecularWeight($c); my $w = sprintf("%7.4f", $pnCompound->[$i] * $MW * $TotalWeight / $TotalReagentsMolWeight); my $mw = sprintf("%7.3f", $MW); $App->TableRowByArray( ["$hh", "==>$ch", "==>$pnCompound->[$i]", "", "==>$mw", "==>$w"] ); $this->AddText("$i: Compound [$c]: Chemical [$pChemicals->[$i]] MW=$mw w=$w g\n"); my ($pElement, $pnElement) = $R->CompoundToElements($c); for(my $j = 0 ; $j < @$pElement ; $j++) { my $e = $pElement->[$j]; my $n = $pnElement->[$j]; #$R->nElement("Reagents", $e); my $pAtomDB = AtomType::GetAtomDBHash($e); my $Z = sprintf("%7.0f", $pAtomDB->{Z}); my $mass = sprintf("%7.3f", $pAtomDB->{mass}); $App->TableRowByArray( ["", "==>$e", "==>$n", "==>$Z", "==>$mass"] ); $this->AddText("$i:$j: Compound [$e] n=$n Z=$Z M=$mass\n"); } } $App->EndTable(); # my $TotalReagentsMolWeight = 0; my $pElements = $R->ElementsArray("Reagents"); my %nReagentsElement; $App->BeginTable(1); $App->TableRowByArray( ["<=>Element", "<=>n", "<=>Z", "<=>Mass (g/mol)"] ); for(my $i = 0 ; $i < @$pElements ; $i++) { my $e = $pElements->[$i]; my $n = $R->nElement("Reagents", $e); my $pAtomDB = AtomType::GetAtomDBHash($e); $nReagentsElement{$e} += $n; # $TotalReagentsMolWeight += $n * $pAtomDB->{mass}; my $Z = sprintf("%7.0f", $pAtomDB->{Z}); my $mass = sprintf("%7.3f", $pAtomDB->{mass}); $App->TableRowByArray( ["==>$e", "==>$n", "==>$Z", "==>$mass"] ); $this->AddText("$i: Compound [$e] n=$n Z=$Z M=$mass\n"); } my $TRMW = sprintf("%7.3f", $TotalReagentsMolWeight); $App->TableRowByArray( ["Total molar mass::colspan=3", "==>$TRMW"] ); $App->EndTable(); $this->AddText("\nFor Products [$pParams->{Products}]\n"); $App->PrintRawHTML("

Products: ", $R->ReactionToHTML($R->Products()), "

\n"); ($pChemicals, $pCompound, $pnCompound) = $R->ReactionToCompounds($R->{Final}); my $TotalProductsMolWeight = 0.0; for(my $i = 0 ; $i < @$pChemicals ; $i++) { my $c = $pCompound->[$i]; my $n = $pnCompound->[$i]; $TotalProductsMolWeight += $n * $R->MolecularWeight($c); } $App->BeginTable(1); $App->TableRowByArray( ["", "<=>Compound", "<=>n", "<=>Z", "<=>Mass (g/mol)", "<=>Weight (g)"] ); for(my $i = 0 ; $i < @$pChemicals ; $i++) { my $c = $pCompound->[$i]; my $hh = $R->ReactionToHTML($pChemicals->[$i]); my $ch = $R->ReactionToHTML($pCompound->[$i]); my $MW = $R->MolecularWeight($c); my $w = sprintf("%7.4f", $pnCompound->[$i] * $MW * $TotalWeight / $TotalReagentsMolWeight); my $mw = sprintf("%7.3f", $MW); $App->TableRowByArray( ["$hh", "==>$ch", "==>$pnCompound->[$i]", "", "==>$mw", "==>$w"] ); $this->AddText("$hh: Compound [$ch] n=$pnCompound->[$i] mw=$mw\n"); my ($pElement, $pnElement) = $R->CompoundToElements($c); for(my $j = 0 ; $j < @$pElement ; $j++) { my $e = $pElement->[$j]; my $n = $pnElement->[$j]; #$R->nElement("Products", $e); my $pAtomDB = AtomType::GetAtomDBHash($e); my $Z = sprintf("%7.0f", $pAtomDB->{Z}); my $mass = sprintf("%7.3f", $pAtomDB->{mass}); $App->TableRowByArray( ["", "==>$e", "==>$n", "==>$Z", "==>$mass"] ); $this->AddText("$i: Compound [$e] n=$n Z=$Z M=$mass\n"); } } $App->EndTable(); $pElements = $R->ElementsArray("Products"); # my $TotalProductsMolWeight = 0.0; my %nProductsElement; $App->BeginTable(1); $App->TableRowByArray( ["<=>Element", "<=>n", "<=>Z", "<=>Mass (g/mol)"] ); for(my $i = 0 ; $i < @$pElements ; $i++) { my $e = $pElements->[$i]; my $n = $R->nElement("Products", $e); my $pAtomDB = AtomType::GetAtomDBHash($e); $nProductsElement{$e} += $n; # $TotalProductsMolWeight += $n * $pAtomDB->{mass}; my $Z = sprintf("%7.0f", $pAtomDB->{Z}); my $mass = sprintf("%7.3f", $pAtomDB->{mass}); $App->TableRowByArray( ["==>$e", "==>$n", "==>$Z", "==>$mass"] ); $this->AddText("$i: Element [$e] n=$n Z=$Z M=$mass\n"); } my $TPMW = sprintf("%7.3f", $TotalProductsMolWeight); $App->TableRowByArray( ["Total molar mass::colspan=3", "==>$TPMW"] ); $this->AddText("Total molar mass: $TPMW\n"); $App->EndTable(); my %Unbalanced; foreach my $e (keys %nReagentsElement) { if($nReagentsElement{$e} != 0 and $nReagentsElement{$e} ne $nProductsElement{$e}) { $Unbalanced{$e} = 1; } } foreach my $e (keys %nProductsElement) { if($nProductsElement{$e} != 0 and $nProductsElement{$e} ne $nReagentsElement{$e}) { $Unbalanced{$e} = 1; } } my $NotBalanced = join(',', sort keys %Unbalanced); if($NotBalanced ne '') { $App->H1("Error: Unbalance reaction [$NotBalanced]!!!\n"); if($CallFindReaction) { $App->H2("Speculate a correct reaction\n"); ($pParams->{Reagents}, $pParams->{Products}) = $R->SplitReaction($Reaction); $App->HR(); return $this->FindReaction($App, 0, 1); } return; } my $pReagentHash = $R->ReagentsHash(); my $pReagentCompounds = $R->pCompounds('Reagents'); my $pReagentElements = $R->pElements("Reagents"); my @MolWeight; for(my $i = 0 ; $i < @$pReagentCompounds ; $i++) { my $c = $pReagentCompounds->[$i]; my $nCompound = $R->nCompound('Reagents', $c); $MolWeight[$i] = $R->MolecularWeight($c); } $App->H3("Batch\n"); $this->AddText("\nBatch\n"); my @BatchWeight; my $SumWeight = 0.0; $App->BeginTable(1); $App->TableRowByArray( ["Compound", "<=>n", "==>Mass (g/mol)", "<=>Weight (g)"] ); for(my $i = 0 ; $i < @$pReagentCompounds ; $i++) { my $c = $pReagentCompounds->[$i]; my $ch = $R->ReactionToHTML($pReagentCompounds->[$i]); my $nCompound = $R->nCompound('Reagents', $c); $BatchWeight[$i] = $TotalWeight / $TotalProductsMolWeight * $nCompound * $MolWeight[$i]; $SumWeight += $BatchWeight[$i]; my $bw = sprintf("%7.4f", $BatchWeight[$i]); my $mw = sprintf("%7.3f", $MolWeight[$i]); $App->TableRowByArray( ["$ch", "==>$nCompound", "==>$mw g/mol", "==>$bw g"] ); $App->EndRow(); $this->AddText("$i: Compound [$pReagentCompounds->[$i]] n=$nCompound MW=$mw g/mol w=$bw g\n"); } my $tw = sprintf("%7.4f", $SumWeight); $App->TableRowByArray( ["==>Total::colspan=3", "==>$tw g"] ); $this->AddText("Total: $tw g\n"); $App->EndTable(); return 1; } sub CalFindReaction { my ($this) = @_; $this->FindReaction($this, $this->{ini}, 0); } sub FindReaction { my ($this, $App, $pParams, $CallBatch) = @_; $CallBatch = 0 if(!defined $CallBatch); my %Gasses = ( 'H' => 'H2', 'N' => 'N2', 'O' => 'O2', 'F' => 'F2', 'Cl' => 'Cl2', 'Br' => 'Br2', 'I' => 'I2', ); my $R = new ChemicalReaction(undef, undef, "Composition"); $this->ClearTextBox(); $this->AddText("\nFind Reaction\n"); $App->H2("Find Reaction\n"); my $reaction = "$pParams->{Reagents}=>$pParams->{Products}"; my $rh = $R->ReactionToHTML($pParams->{Reagents}) . " => " . $R->ReactionToHTML($pParams->{Products}); $App->PrintRawHTML("

Reagents: $rh

\n"); $this->AddText("Reagents: $pParams->{Reagents}\n"); $R->Analyze($reaction, 0); my $pReagentHash = $R->ReagentsHash(); my $pReagentCompounds = $R->pCompounds('Reagents'); my $pReagentElements = $R->pElements("Reagents"); my $pProductHash = $R->ProductsHash(); my $pProductCompounds = $R->pCompounds('Products'); my $pProductElements = $R->pElements("Products"); # $pHash->{$element}{nElement} += $N; # $pHash->{nElement}{$element} += $N; # $App->H3("Compounds in products\n"); # for(my $i = 0 ; $i < @$pProductCompounds ; $i++) { # my $c = $pProductCompounds->[$i]; # $App->print(" $i: $c\n"); # } my %nElements; for(my $i = 0 ; $i < @$pReagentElements ; $i++) { my $e = $pReagentElements->[$i]; $nElements{$e} = 0; } # $App->H3("Target composition\n"); for(my $i = 0 ; $i < @$pProductElements ; $i++) { my $e = $pProductElements->[$i]; my $n = $R->nElement("Reagents", $e); $nElements{$e} += $n; } # foreach my $e (sort keys %nElements) { # my $n = $nElements{$e}; # $App->print(" $e: $n\n"); # } $this->AddText("\nMass balance\n"); $App->H3("Mass balance\n"); $App->BeginTable(); $App->BeginRow(); $App->TableCell(""); for(my $i = 0 ; $i < @$pReagentCompounds ; $i++) { my $c = $pReagentCompounds->[$i]; $App->TableCell("" . $R->ReactionToHTML($c) . ""); $this->AddText("Compound[$i]: $c\n"); } $App->TableCell(""); $App->TableCell(""); $App->TableCell("" . $R->ReactionToHTML($pParams->{Products}) . ""); $this->AddText("Products: $pParams->{Products}\n"); $App->EndRow(); my $nArray = scalar keys %nElements; my $nColumn = scalar @$pReagentCompounds; my $ii = 0; my $pA; my $pV; foreach my $e (sort keys %nElements) { my $n = $nElements{$e}; $App->BeginRow(); $App->TableCell("$e"); for(my $i = 0 ; $i < @$pReagentCompounds ; $i++) { my $c = $pReagentCompounds->[$i]; my ($pe, $pn) = $R->CompoundToElements($c); my $IsExist = 0; for(my $j = 0 ; $j < @$pe ; $j++) { #$App->print("$e: $i,$j: $c: $pe->[$j]\n"); if($pe->[$j] eq $e) { $App->TableCell("==>$pn->[$j]"); $pA->[$ii][$i] = $pn->[$j]; $IsExist = 1; last; } } if(!$IsExist) { $App->TableCell("==>0"); $pA->[$ii][$i] = 0; } } my $c = $pReagentCompounds->[$ii]; my $ch = ($c)? $R->ReactionToHTML($c) : ''; $App->TableCell(($ch)? "n$c" : ''); if(int(($nArray-1) / 2) == $ii) { $App->TableCell("="); } else { $App->TableCell(""); } $n = $pProductHash->{$e}{nElement}; $n = 0 if(!defined $n); $App->TableCell($n); $pV->[$ii] = $n; $App->EndRow(); $ii++; } $App->EndTable(); if($nArray != $nColumn) { $App->H3("Error: nRow [$nArray] is not equal to nColumn [$nColumn]\n"); $this->AddText("Error: nRow [$nArray] is not equal to nColumn [$nColumn]\n"); return; } #$App->print("nArray=$nArray\n"); my $A = new Math::MatrixReal($nArray, $nArray); #$App->print("A=\n"); for(my $i = 0 ; $i < $nArray ; $i++) { for(my $j = 0 ; $j < $nArray ; $j++) { # $App->print("$pA->[$i][$j], "); $A->assign($i+1, $j+1, $pA->[$i][$j]); } last if(!defined $pA->[$i][0]); # $App->print("\n"); } my $V = new Math::MatrixReal($nArray, 1); # $App->print("V=\n"); for(my $i = 0 ; $i < $nArray ; $i++) { # $App->print("$pV->[$i], "); $V->assign($i+1, 1, $pV->[$i]); } #$App->print("\n"); my $Inv = $A->inverse(); if(!defined $Inv) { $App->H3("Error: Some of the reagents are not independent. Reconsider the selection.\n"); $this->AddText("Error: Some of the reagents are not independent. Reconsider the selection.\n"); return; } my $N = $Inv * $V; my $r = ''; for(my $i = 0 ; $i < $nArray ; $i++) { my $n = $N->element($i+1, 1); next if($n == 0); $n = '' if($n == 1); $n = '-' if($n == -1); my $c = $pReagentCompounds->[$i]; $r .= "+$n$c"; } $r =~ s/^\+//g; $r =~ s/\+\-/\-/g; $rh = $R->ReactionToHTML($r); $App->PrintRawHTML("

" . $rh . " => " . $R->ReactionToHTML($pParams->{Products}) . "

\n"); $this->AddText($r . " => " . $pParams->{Products} . "\n"); if($CallBatch) { $pParams->{Reaction} = "$r=>$pParams->{Products}"; $this->BatchCalculation($App, $pParams, 1, 0); } } 1;