package MoleculeObject; use Exporter; @ISA = qw(Exporter); #公開したいサブルーチン @EXPORT = qw(); use strict; #use Math::Matrix; use Math::MatrixReal; use Sci qw($pi $a0 $torad $todeg); use Sci::Algorism; use Sci::Vector; use Crystal::AtomType; use Crystal::AtomSite; use Crystal::Rietan; #================================================================== # 静的メンバー関数 #================================================================== #================================================================== # 変数取得関数 #================================================================== sub Title { return shift->{Title}; } sub SetTitle { my ($this,$t)=@_; return $this->{Title} = $t; } sub SampleName { return shift->{SampleName}; } sub SetSampleName { my ($this,$s)=@_; return $this->{SampleName} = $s; } sub SetMoleculeName { my ($this,$name) = @_; return $this->{MoleculeName} = $name; } sub MoleculeName { my ($this) = @_; return $this->{MoleculeName}; } sub SetCalculationCondition { my ($this,$c)=@_; return $this->{SetCalculationCondition} = $c; } sub CalculationCondition { my ($this)=@_; return $this->{SetCalculationCondition}; } sub nAtomSite { return shift->{nAtomSite}; } sub SetnAtomSite { my ($this,$n)=@_; return $this->{nAtomSite} = $n; } sub nAtomType { return shift->{nAtomType}; } sub SetnAtomType { my ($this,$n)=@_; return $this->{nAtomType} = $n; } sub nZMatrix { return shift->{nZMatrix}; } sub SetnZMatrix { my ($this,$n)=@_; return $this->{nZMatrix} = $n; } sub pZMatrixList { return shift->{RefZMatrixList}; } sub ZMatrix { my ($this,$i)=@_; return $this->{RefZMatrixList}->[$i]; } sub SetnAtomType { my ($this,$n) = @_; return $this->{nAtomType} = $n; } sub nAtomType { my ($this) = @_; return $this->{nAtomType}; } sub SetCAtomTypeList { my ($this, @atomlist) = @_; $this->{nAtomType} = @atomlist; return $this->{RefAtomTypeList} = \@atomlist; } sub GetCAtomTypeList { my ($this) = @_; my $RefAtomList = $this->{RefAtomTypeList}; #print "Ref:$RefAtomList
\n"; return () if($RefAtomList eq ''); return @$RefAtomList; } sub GetCAtomType { my ($this,$i) = @_; my $RefAtomList = $this->{RefAtomTypeList}; my $atomtype = $RefAtomList->[$i]; if(!$atomtype) { return 0; } return $atomtype; } sub SetCAtomSiteList { my ($this, @atomlist) = @_; $this->{nAtomSite} = @atomlist; return $this->{RefAtomSiteList} = \@atomlist; } sub GetCAtomSiteList { my ($this) = @_; my $RefAtomList = $this->{RefAtomSiteList}; #print "Ref:$RefAtomList
\n"; return () if($RefAtomList eq ''); return @$RefAtomList; } sub GetCAtomSite { my ($this,$i) = @_; my $RefAtomList = $this->{RefAtomSiteList}; my $atomsite = $RefAtomList->[$i]; if(!$atomsite) { return 0; } return $atomsite; } #How to build Molecule (Ex. from CIF.pm) # my $mol = new Molecule(); # $mol->SetCAtomTypeList( # $this->GetCAtomTypeList() ); # $mol->SetCAtomSiteList( # $this->GetCAtomSiteList() ); #================================================================== # コンストラクタ、デストラクト #================================================================== sub new { my ($module) = @_; my $this = {}; bless $this; $this->Initialize(); return $this; } sub DESTROY { my $this = shift; } sub Initialize { my ($this) = @_; $this->{RefAtomTypeList} = []; $this->{nAtomType} = 0; $this->{RefAtomSiteList} = []; $this->{nAtomSite} = 0; $this->{RefZMatrixList} = []; $this->{nZMatrix} = 0; return $this; } #================================================================== # 一般メンバー関数 #================================================================== sub TranslateToCenter { my ($this) = @_; my ($xc, $yc, $zc) = $this->CalculateCenterPosition(); my $nSite = $this->nAtomSite(); for(my $i = 0 ; $i < $nSite ; $i++) { my $site = $this->GetCAtomSite($i); # last if(!$site); my $atomname = $site->AtomNameOnly(); my ($AtmNum, $name, $charge) = AtomType::GetAtomInformation($atomname); my ($x, $y, $z) = $site->Position(); $site->SetPosition($x-$xc, $y-$yc, $z-$zc); } } sub CalculateCenterPosition { my ($this) = @_; my ($xc, $yc, $zc) = (0, 0, 0); my $nSite = $this->nAtomSite(); for(my $i = 0 ; $i < $nSite ; $i++) { my $site = $this->GetCAtomSite($i); my $atomname = $site->AtomNameOnly(); my ($AtmNum, $name, $charge) = AtomType::GetAtomInformation($atomname); my ($x, $y, $z) = $site->Position(); $xc += $x; $yc += $y; $zc += $z; } return ($xc / $nSite, $yc / $nSite, $zc / $nSite); } sub AddZMatrix { my ($this, $atom, $R, $Angle1, $Angle2, $iAtom2, $iAtom3, $iAtom4, $idR, $idAngle1, $idAangle2) = @_; my %Z = ( AtomName => $atom, R => $R, Angle1 => $Angle1, Angle2 => $Angle2, iAtom2 => $iAtom2, iAtom3 => $iAtom3, iAtom4 => $iAtom4, idR => $idR, idAngle1 => $idAngle1, idAngle2 => $idAangle2 ); $this->AddAtomType($atom, 1); $this->{RefZMatrixList}->[$this->{nZMatrix}] = \%Z; return $this->{nZMatrix}++; } sub ZMatrixToXYZ { my ($this, $TranslateToCenter, $IsPrint) = @_; $IsPrint = 0 if(!defined $IsPrint); my %iAtom; my (@x, @y, @z); for(my $i = 0 ; $i < $this->nZMatrix() ; $i++) { my $Z = $this->ZMatrix($i); my $atom = $Z->{AtomName}; $iAtom{$atom}++; my $label = $atom . $iAtom{$atom}; my $R = $Z->{R}; my $angle1 = $Z->{Angle1}; my $angle2 = $Z->{Angle2}; my ($x, $y, $z); if($i == 0) { ($x, $y, $z) = (0, 0, 0); } elsif($i == 1) { ($x, $y, $z) = ($R, 0, 0); } elsif($i == 2) { $x = $R * cos($angle1 * $torad); $y = $R * sin($angle1 * $torad); $z = 0.0; } else { my $alpha = $torad * $angle1; my $beta = $torad * $angle2; my $sina = sin($alpha); my $cosa = cos($alpha); my $sinb = sin($beta); my $cosb = cos($beta); my $j = $Z->{iAtom2}-1; my $k = $Z->{iAtom3}-1; my $l = $Z->{iAtom4}-1; print "$i-$j-$k-$l: $R: $angle1: $angle2\n" if($IsPrint); my @a = Vector::Difference($x[$k], $y[$k], $z[$k], $x[$j], $y[$j], $z[$j]); my @b = Vector::Difference($x[$l], $y[$l], $z[$l], $x[$j], $y[$j], $z[$j]); my @c = Vector::OuterProduct(@b, @a); # my @c = Vector::OuterProduct(@a, @b); my $a = sqrt(Vector::InnerProduct(@a, @a)); my $b = sqrt(Vector::InnerProduct(@b, @b)); my $c = sqrt(Vector::InnerProduct(@c, @c)); my $ab = Vector::InnerProduct(@a, @b); printf "a=(%6.3f, %6.3f, %6.3f)\n", @a if($IsPrint); printf "b=(%6.3f, %6.3f, %6.3f)\n", @b if($IsPrint); printf "c=(%6.3f, %6.3f, %6.3f)\n", @c if($IsPrint); #my $ac = Vector::InnerProduct(@a, @c); #my $bc = Vector::InnerProduct(@b, @c); #print "a*c=$ac b*c=$bc\n" if($IsPrint); #my $ab = Vector::InnerProduct(@a, @b); #my $angleab = $todeg * acos($ab / $a / $b); #print "angle(a-b)=$angleab deg.\n" if($IsPrint); my $B = $R * $a * $sina * $cosb / $c; my $A = ($R * $a * $cosa - $B * $ab) / $a / $a; my $C = sqrt($R*$R - $A*$A * $a*$a - $B*$B * $b*$b - 2.0 * $A * $B * $ab) / $c; $C = -$C if($angle2 < 0.0); ($x, $y, $z) = ($x[$j] + ($A*$a[0] + $B*$b[0] + $C*$c[0]), $y[$j] + ($A*$a[1] + $B*$b[1] + $C*$c[1]), $z[$j] + ($A*$a[2] + $B*$b[2] + $C*$c[2])); } ($x[$i], $y[$i], $z[$i]) = ($x, $y, $z); $this->AddAtomSite($label, $atom, $x, $y, $z, 1.0); } $this->TranslateToCenter() if($TranslateToCenter); } sub FindNearestEquivalentFractionCoordinate { my ($this, $x2, $x) = @_; my $xret = $x2; my $mind = abs($x2 - $x); if($mind > abs($x2+1.0 - $x)) { $xret = $x2+1.0; $mind = abs($x2+1.0 - $x); } if($mind > abs($x2-1.0 - $x)) { $xret = $x2-1.0; $mind = abs($x2-1.0 - $x); } return $xret; } sub FractionalToCartesian { my ($this, $x,$y,$z) = @_; my $a11 = $this->{a11}; my $a12 = $this->{a12}; my $a13 = $this->{a13}; my $a21 = $this->{a21}; my $a22 = $this->{a22}; my $a23 = $this->{a23}; my $a31 = $this->{a31}; my $a32 = $this->{a32}; my $a33 = $this->{a33}; my $xc = $a11*$x + $a21*$y + $a31*$z; my $yc = $a12*$x + $a22*$y + $a32*$z; my $zc = $a13*$x + $a23*$y + $a33*$z; return ($xc,$yc,$zc); } sub FindAtomTypeByName { my ($this, $atomname) = @_; $atomname =~ s/([\d+-]+)//; my $atomlistref = $this->{RefAtomTypeList}; for(my $i = 0 ; $i < @$atomlistref ; $i++) { my $type = $atomlistref->[$i]; my $name = $type->AtomNameOnly(); #print "name: $atomname <=> $name\n"; return $type if(uc $name eq uc $atomname); } return undef; } sub AddAtomType { my ($this, $atomname, $CheckRegistered) = @_; $CheckRegistered = 1 if(!defined $CheckRegistered); my $atomlistref = $this->{RefAtomTypeList}; if($atomlistref) { if($CheckRegistered and $this->FindAtomTypeByName($atomname)) { return; } my $at = new AtomType(); $at->SetAtomName($atomname); push(@$atomlistref, $at); $this->{"nAtomType"} = scalar @$atomlistref; } else { my @atomlist; my $at = new AtomType(); $at->SetAtomName($atomname); push(@atomlist, $at); $this->{nAtomType} = @atomlist; $this->{RefAtomTypeList} = \@atomlist; } #my $ref = $this->{RefAtomTypeList} ; #print "ref: ", $ref->[$this->{nAtomType}-1]->AtomName(), "
"; return $this->{nAtomType}; } sub AddAtomSite { my ($this, $label, $atomname, $x, $y, $z, $occ, $fx, $fy, $fz) = @_; #print "AddAtomSite: F=$fx, $y, $fz\n"; # return $this->{nAtomSite} if($atomname eq ''); my $atomlistref = $this->{RefAtomSiteList}; if(!$atomlistref) { $atomlistref = []; } my $at = new AtomSite(); $at->SetLabel($label); $at->SetAtomName($atomname); $at->SetPosition($x, $y, $z); $at->SetForce($fx, $fy, $fz) if(defined $fz); $at->SetOccupancy($occ); $at->SetId(@$atomlistref + 1); push(@$atomlistref, $at); $this->{nAtomSite} = scalar @$atomlistref; $this->AddAtomType($atomname) if($atomname ne ''); return $this->{nAtomSite}; } sub GetInterAtomicDistance { my ($this, $x0, $y0, $z0, $x1, $y1, $z1) = @_;; my $dx = $x1 - $x0; my $dy = $y1 - $y0; my $dz = $z1 - $z0; my $r2 = $dx*$dx + $dy*$dy + $dz*$dz; return sqrt($r2); } sub GetInterAtomicAngle { my ($this, $x0, $y0, $z0, $x1, $y1, $z1, $x2, $y2, $z2) = @_;; my $dis01 = $this->GetInterAtomicDistance($x0,$y0,$z0,$x1,$y1,$z1); return 0.0 if($dis01 == 0.0); my $dis02 = $this->GetInterAtomicDistance($x0,$y0,$z0,$x2,$y2,$z2); return 0.0 if($dis02 == 0.0); #print "dis: $dis01, $dis02\n"; my $dx01 = $x1 - $x0; my $dy01 = $y1 - $y0; my $dz01 = $z1 - $z0; my $dx02 = $x2 - $x0; my $dy02 = $y2 - $y0; my $dz02 = $z2 - $z0; my $ip = $dx01*$dx02 + $dy01*$dy02 + $dz01*$dz02; my $cosa = $ip / $dis01 / $dis02; #print "cos: $cosa
"; my $angle = Sci::dacos($cosa); #print "cos: $cosa $angle
"; $angle = 360.0 - $angle if($angle > 180.0001); return $angle; } #iAtomTypeは1から始まる sub FindiAtomType { my ($this, $atomname) = @_; $atomname =~ s/^(\D+)(\d.*)$/$1/; $atomname = uc $atomname; my @AtomTypeList = $this->GetCAtomTypeList(); my $nAtomType = @AtomTypeList; for(my $i = 0 ; $i < $nAtomType ; $i++) { my $name = $AtomTypeList[$i]->AtomName(); $name =~ s/^(\D+)(\d.*)$/$1/; return $i+1 if($atomname eq uc $name); } return -1; } sub FillAtomTypeData { my ($this) = @_; my $ref = $this->{"RefAtomTypeList"}; for(my $i = 0 ; $i < @$ref ; $i++) { $ref->[$i]->FillAtomTypeData(); } } # $s = sin Q / lambda (in nm) sub AtomicScatteringFactor { my ($this, $s) = @_; return $this->asf($s); } # $s = sin Q / lambda (in nm) sub asfElectron { my ($this, $iAtomType, $s) = @_; $s = 1.0e-5 if($s <= 0.0); my $s2 = $s * $s; my $k = 1.0e-2 * 8.0 * $pi * $pi / ($a0 * 1.0e9); my $Z = $this->{"ASFDCAtomNameZ$iAtomType"}; my $asfXray = $this->asf($iAtomType, $s); #print "s=$s, i=$iAtomType, Z=$Z, a=$asfXray\n"; #exit; my $val = $k * ($Z - $asfXray) / $s2; #print "va [$iAtomType]=$val = $k * ($Z - $asfXray) / $s2\n"; return $val; } sub ReadASF { my ($this, $Source) = @_; my @AtomTypeList = $this->GetCAtomTypeList(); my $nAtomType = @AtomTypeList; for(my $i = 0 ; $i < $nAtomType ; $i++) { my $Rietan = new Rietan; my $AtomName; my $AtomNameOnly = $AtomTypeList[$i]->AtomNameOnly(); my $Charge = $AtomTypeList[$i]->Charge(); if($Charge == 1) { $AtomName = "$AtomNameOnly+"; } elsif($Charge == -1) { $AtomName = "$AtomNameOnly-"; } elsif($Charge > 1) { $AtomName = "$AtomNameOnly+"; } elsif($Charge < -1) { $Charge = -$Charge; $AtomName = "$AtomNameOnly-"; } #print "a1: $AtomName\n"; my @a = $Rietan->ReadASFParameters($AtomName); if(@a < 9) { $AtomName = $AtomTypeList[$i]->AtomNameOnly(); #print "a2: $AtomName\n"; @a = $Rietan->ReadASFParameters($AtomName); } if(@a < 9) { print "Error: Can not read ASFDC for [$AtomName].\n"; next; } #print "array: ", join(',', @a), "\n"; #print "i=$i: $AtomName\n"; $this->{"ASFDCRed$i"} = $Rietan; $this->{"ASFDCRed$AtomName"} = $Rietan; $this->{"ASFDCAtomName$i"} = $Rietan->{ASFDCAtomName}; $this->{"ASFDCAtomName$AtomName"} = $Rietan->{ASFDCAtomName}; my $AtomicNumber = $AtomTypeList[$i]->AtomicNumber(); $this->{"ASFDCAtomNameZ$i"} = $AtomicNumber; $this->{"ASFDCAtomNameZ$AtomNameOnly"} = $AtomicNumber; print "ASFDC AtomName [$i]: $Rietan->{ASFDCAtomName} [Zi=$AtomicNumber]\n"; #print " ao:$AtomNameOnly: $i\n"; } $this->{ASFDCRed} = 1; } # $s = sin Q / lambda (in nm) sub asf { my ($this, $iAtomType, $s, $Source) = @_; $Source = 'Cu' if(!defined $Source); $this->ReadASF($Source) if(!defined $this->{ASFDCRed}); my $Rietan = $this->{"ASFDCRed$iAtomType"}; my $pP = $Rietan->{pASFDCParameters}; my ($A1,$B1,$A2,$B2,$A3,$B3,$A4,$B4,$C, $b, $Crdf1,$Crdf2, $Fedf1,$Fedf2, $Codf1,$Codf2, $Cudf1,$Cudf2, $Modf1, $Modf2, $Agdf1,$Agdf2, $Kbdf1, $Kbdf2) = @$pP; # if($Source eq 'n') { # return $b; # } my $s2 = 0.01 * $s * $s; my $asf = $A1*exp(-$B1*$s2)+$A2*exp(-$B2*$s2)+$A3*exp(-$B3*$s2)+$A4*exp(-$B4*$s2)+$C; if($Source eq 'Cr' and defined $Crdf2) { $asf = cplx($asf + $Crdf1, $Crdf2); } elsif($Source eq 'Fe' and defined $Fedf2) { $asf = cplx($asf + $Fedf1, $Fedf2); } elsif($Source eq 'Co' and defined $Codf2) { $asf = cplx($asf + $Codf1, $Codf2); } elsif($Source eq 'Cu' and defined $Cudf2) { #print "d=$Cudf1,$Cudf2\n"; $asf = cplx($asf + $Cudf1, $Cudf2); } elsif($Source eq 'Mo' and defined $Modf2) { $asf = cplx($asf + $Modf1, $Modf2); } elsif($Source eq 'Ag' and defined $Agdf2) { $asf = cplx($asf + $Agdf1, $Agdf2); } elsif($Source eq 'Kb' and defined $Kbdf2) { $asf = cplx($asf + $Kbdf1, $Kbdf2); } return $asf; } 1;