package CrystalObject;
use Exporter;
@ISA = qw(Exporter);
#公開したいサブルーチン
@EXPORT = qw();
use strict;
#use Math::Matrix;
use Math::MatrixReal;
use Math::Vector::Real;
use Sci qw($pi $a0 $torad $todeg);# acos asin);
use Sci::Algorism;
use Sci::MyMatrixReal;
use Math::Complex;
use Crystal::PointGroup;
use Crystal::SpaceGroup;
use Crystal::AtomType;
use Crystal::AtomSite;
#use Crystal::Rietan;
use Crystal::RietanFP;
#==================================================================
# 静的メンバー関数
#==================================================================
sub RoundSymmetricPosition
{
my ($x) = @_;
return 1.0 / 3.0 if($x == 0.3333 or $x == 0.333);
return -1.0 / 3.0 if($x == -0.3333 or $x == -0.333);
return 2.0 / 3.0 if($x == 0.6667 or $x == 0.667);
return -2.0 / 3.0 if($x == -0.6667 or $x == -0.667);
return 1.0 / 6.0 if($x == 0.1667 or $x == 0.167);
return -1.0 / 6.0 if($x == -0.1667 or $x == -0.167);
return 5.0 / 6.0 if($x == 0.8333 or $x == 0.833);
return -5.0 / 6.0 if($x == -0.8333 or $x == -0.833);
return $x;
}
sub PolarizationFactor
{
my ($Theta, $MonochromaterAlpha) = @_;
my $cos2a = 1.0;
if(defined $MonochromaterAlpha) {
$cos2a = cos(2.0 * $MonochromaterAlpha * $torad);
$cos2a = $cos2a * $cos2a;
}
my $cos2Q = cos($torad * 2.0 * $Theta);
return 1.0 + $cos2a * $cos2Q * $cos2Q;
}
sub LorentzFactor
{
my ($Theta) = @_;
my $cosQ = cos($torad * $Theta);
my $sinQ = sin($torad * $Theta);
# my $sinQ = sin($torad * 2.0 * $Theta);
return 1.0 / $cosQ / $sinQ / $sinQ;
# my $cosQ = cos($torad * $Theta);
my $sin2Q = sin($torad * 2.0 * $Theta);
return $cosQ / $sin2Q / $sin2Q;
}
sub TemperatureFactor
{
# Biso in Angstrom, $s in nm^-1
my ($Biso, $s) = @_;
$s = 0.1 * $s;
my $M = $Biso * $s * $s;
return exp(-$M);
}
#==================================================================
# 変数取得関数
#==================================================================
sub SampleName { return shift->{SampleName}; }
sub SetSampleName { my ($this,$s)=@_; return $this->{SampleName} = $s; }
sub SetCrystalName { my ($this,$name) = @_; return $this->{CrystalName} = $name; }
sub CrystalName { my ($this) = @_; return $this->{CrystalName}; }
sub SPGName { my ($this) = @_; my $SPG = $this->GetCSpaceGroup(); return $SPG->SPGName(); }
sub iSPG { my ($this) = @_; my $SPG = $this->GetCSpaceGroup(); return $SPG->iSPG(); }
sub iSet { my ($this) = @_; my $SPG = $this->GetCSpaceGroup(); return $SPG->iSet(); }
sub FormulaUnit { return shift->{'FormulaUnit'}; }
sub SetFormulaUnit { my($this,$Z)=@_; return $this->{'FormulaUnit'} = $Z; }
sub ChemicalComposition { return shift->{'ChemicalComposition'}; }
sub SetChemicalComposition { my($this,$c)=@_; return $this->{'ChemicalComposition'} = $c; }
sub SumChemicalComposition { return shift->{'SumChemicalComposition'}; }
sub SetSumChemicalComposition { my($this,$c)=@_; return $this->{'SumChemicalComposition'} = $c; }
sub SetnAtomType { my ($this,$n) = @_; return $this->{nAtomType} = $n; }
sub nAtomType { my ($this) = @_; return $this->{nAtomType}; }
sub SetnAsymmetricAtomSite { my ($this,$n) = @_; return $this->{nAsymmetricAtomSite} = $n; }
sub nAsymmetricAtomSite { my ($this) = @_; return $this->{nAsymmetricAtomSite}; }
sub SetnTotalExpandedAtomSite { my ($this,$n) = @_; return $this->{nTotalExpandedAtomSite} = $n; }
sub nTotalExpandedAtomSite { my ($this) = @_; return $this->{nTotalExpandedAtomSite}; }
sub LatticeSystem {
my ($this) = @_;
return $this->{LatticeSystem} if($this->{LatticeSystem});
my $SPG = $this->GetCSpaceGroup();
return $SPG->LatticeSystem();
}
sub SetLatticeSystem
{
my ($this, $latticesystem, $SearchByLatticeParameter, $tollat, $tolangle) = @_;
my $SPG = $this->GetCSpaceGroup();
my $ls;
# my $ls = $SPG->LatticeSystem();
# if($ls eq '') {
$SPG->SetLatticeSystem($latticesystem, $SearchByLatticeParameter, $tollat, $tolangle);
$ls = $SPG->LatticeSystem();
# }
$this->{LatticeSystem} = $ls;
return $ls;
}
sub SetTemperature
{
my ($this,$T) = @_;
return $this->{Temperature} = $T;
}
sub Temperature
{
my ($this) = @_;
return $this->{Temperature};
}
#sub SpaceGroupObject::SetP1
#{
# my ($this) = @_;
# $this->ClearSymmetryOperation();
# $this->SetSPGName("P 1");
# $this->SetiSPG(1);
# $this->SetiSet(1);
# $this->AnalyzeTranslation();
# $this->AddSymmetryOperation('x,y,z');
# $this->SetLatticeSystem('triclinic');
#}
sub BurstToP1
{
my ($this) = @_;
my @ExpandedAtomSiteList = $this->GetCExpandedAtomSiteList();
my $SPG = $this->GetCSpaceGroup();
$this->SetSpaceGroup("P 1", 1, 1);
$this->SetLatticeSystem('triclinic', 0);
$SPG->SetP1();
$this->SetCAsymmetricAtomSiteList(@ExpandedAtomSiteList);
$this->ExpandCoordinates();
#my $ls = $this->LatticeSystem();
#print "ls=$ls\n";
#my $SPG = $this->GetCSpaceGroup();
#my $bl = $SPG->GetBravaisLattice();
#print "bl=$bl\n";
#exit;
return $this;
}
sub SetSpaceGroup
{
my ($this, $SPGName, $iSPG, $iSet) = @_;
my $SPG = new SpaceGroup();
$SPG->SetSPGName($SPGName);
$SPG->SetiSPG($iSPG);
$SPG->SetiSet($iSet) if(!defined $iSet);
$SPG->AddSymmetryOperation("x,y,z");
my $ret = $this->SetCSpaceGroup($SPG);
return $ret;
}
sub SetCSpaceGroup
{
my ($this, $SPG) = @_;
$SPG->SetLatticeParameters($this->LatticeParameters()) if(!$SPG->{a});
$this->{SpaceGroup} = $SPG;
$this->SetLatticeSystem() if(!$SPG->LatticeSystem());
return $SPG;
}
sub GetCSpaceGroup
{
my ($this) = @_;
if(not defined $this->{"SpaceGroup"} or $this->{"SpaceGroup"} eq '') {
my $SPG = new SpaceGroup();
$SPG->SetP1();
$this->{"SpaceGroup"} = $SPG;
}
return $this->{"SpaceGroup"};
}
sub GetBravaisLattice { my ($this) = @_; my $SPG = $this->GetCSpaceGroup();
return $SPG->GetBravaisLattice();
}
sub SetCAtomTypeList
{
my ($this, @atomlist) = @_;
$this->{nAtomType} = @atomlist;
return $this->{RefAtomTypeList} = \@atomlist;
}
sub ClearAtomTypeList
{
my ($this) = @_;
$this->{RefAtomTypeList} = [];
# delete $this->{RefAtomTypeList};
#print "p=$this->{RefAtomTypeList}\n";
}
sub GetpAtomType
{
my ($this) = @_;
my $RefAtomList = $this->{RefAtomTypeList};
}
sub GetCAtomTypeList
{
my ($this) = @_;
#$this->{RefAtomTypeList} = [];
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 AddAtomType
{
my ($this, $atomname, $CheckRegistered, $occ) = @_;
$CheckRegistered = 1 if(!defined $CheckRegistered);
$occ = 1.0 if(!defined $occ);
#print "AddAtomType: occ=$occ ($atomname: $CheckRegistered)\n";
my $atomlistref = $this->{RefAtomTypeList};
if($atomlistref) {
if($CheckRegistered and $this->FindAtomTypeByName($atomname)) {
return;
}
#print "atname[$atomname]: passed\n";
my $at = new AtomType();
$at->SetAtomName($atomname);
$at->SetOccupancy($occ);
push(@$atomlistref, $at);
$this->{"nAtomType"} = scalar @$atomlistref;
}
else {
my @atomlist;
my $at = new AtomType();
$at->SetAtomName($atomname);
$at->SetOccupancy($occ);
push(@atomlist, $at);
$this->{"nAtomType"} = @atomlist;
$this->{"RefAtomTypeList"} = \@atomlist;
}
#my $ref = $this->{"RefAtomTypeList"} ;
#print "ref: ", $ref->[$this->{"nAtomType"}-1]->AtomName(), "
";
return $this->{"nAtomType"};
}
sub SetCAsymmetricAtomSiteList
{
my ($this, @atomlist) = @_;
$this->{nAsymmetricAtomSite} = @atomlist;
return $this->{RefAsymmetricAtomSiteList} = \@atomlist;
}
sub GetpAtomSite
{
my ($this, $mode) = @_;
return $this->GetpAtomSiteList($mode);
}
sub GetpAtomSiteList
{
my ($this, $mode) = @_;
if($mode == 0) {
return $this->{"RefAsymmetricAtomSiteList"};
}
return $this->{RefExpandedAtomSiteList};
}
sub GetpAsymmetricAtomSite
{
my ($this) = @_;
return $this->{"RefAsymmetricAtomSiteList"};
}
sub GetAtomSiteList
{
my ($this, $mode) = @_;
if(!defined $mode or $mode == 0) {
return $this->GetCAsymmetricAtomSiteList();
}
else {
return $this->GetCExpandedAtomSiteList();
}
}
sub GetCAsymmetricAtomSiteList
{
my ($this) = @_;
my $RefAtomList = $this->{"RefAsymmetricAtomSiteList"};
return () if($RefAtomList eq '');
return @$RefAtomList;
}
sub GetCAtomSite
{
my ($this,$i) = @_;
return $this->GetAtomSite($i);
}
sub GetAtomSite
{
my ($this,$i) = @_;
my $RefAtomList = $this->{RefAsymmetricAtomSiteList};
my $atomsite = $RefAtomList->[$i];
if(!$atomsite) {
return 0;
}
return $atomsite;
}
sub GetpExpandedAtomSite
{
my ($this) = @_;
return $this->{RefExpandedAtomSiteList};
}
sub GetCExpandedAtomSiteList
{
my ($this) = @_;
my $RefAtomList = $this->{RefExpandedAtomSiteList};
return () if($RefAtomList eq '');
return @$RefAtomList;
}
sub GetCExpandedAtomSite
{
my ($this,$i) = @_;
my $RefAtomList = $this->{RefExpandedAtomSiteList};
my $atomsite = $RefAtomList->[$i];
if(!$atomsite) {
return 0;
}
return $atomsite;
}
sub FindAtomSite
{
my ($this, $mode, $atomname) = @_;
my $p = $this->GetpAtomSiteList($mode);
for(my $i = 0 ; $i < @$p ; $i++) {
my $atom = $p->[$i];
my $name = $atom->AtomNameOnly();
if(uc $atomname eq uc $name) {
return $atom;
}
}
}
sub FindiAtom
{
my ($this, $mode, $atomname) = @_;
my $p = $this->GetpAtomSiteList($mode);
for(my $i = 0 ; $i < @$p ; $i++) {
my $atom = $p->[$i];
my $name = $atom->AtomNameOnly();
if(uc $atomname eq uc $name) {
return $i;
}
}
}
sub FindNearestSite
{
my ($this, $x0, $y0, $z0) = @_;
my $p = $this->GetpAtomSiteList(1);
my $minidx = -1;
my $mindis = 1.0e10;
for(my $i = 0 ; $i < @$p ; $i++) {
my $atom = $p->[$i];
my ($x1, $y1, $z1) = $atom->Position(1);
my $dis = $this->GetNearestInterAtomicDistance(
$x0, $y0, $z0, $x1, $y1, $z1);
if($dis < $mindis) {
$mindis = $dis;
$minidx = $i;
}
}
return $minidx;
}
sub GetpAtomPosition
{
my ($this, $mode) = @_;
my $pSite = $this->GetpAtomSite($mode);
my @Pos;
for(my $i = 0 ; $i < @$pSite ; $i++) {
my ($x, $y, $z) = $pSite->[$i]->Position(0);
$Pos[$i][0] = $x;
$Pos[$i][1] = $y;
$Pos[$i][2] = $z;
}
return \@Pos;
}
sub GetpDuplicatedAtomPosition
{
my ($this, $mode) = @_;
my @Pos = $this->DuplicateAtomPositions($mode);
return \@Pos;
}
sub DuplicateAtomPositions
{
my ($this, $mode) = @_;
my $pSite = $this->GetpAtomSite($mode);
my @Pos;
for(my $i = 0 ; $i < @$pSite ; $i++) {
my ($x, $y, $z) = $pSite->[$i]->Position(0);
$Pos[$i][0] = $x;
$Pos[$i][1] = $y;
$Pos[$i][2] = $z;
}
return @Pos;
}
sub Volume
{
my ($this,$UseAtomicUnit) = @_;
my $k = 1.0 / 0.529177;
$k = 1.0 unless($UseAtomicUnit);
$k = $k*$k*$k;
return $this->{"Volume"} * $k;
}
sub ReciprocalVolume
{
my ($this,$UseAtomicUnit) = @_;
my $k = 1.0 / 0.529177;
$k = 1.0 unless($UseAtomicUnit);
$k = $k*$k*$k;
$this->{RVolume} = $this->CalculateReciprocalVolume() if($this->{RVolume} == 0.0);
return $this->{RVolume} / $k;
}
sub Duplicate
{
my ($this) = @_;
my $Crystal = new Crystal;
$Crystal->SetSampleName($this->SampleName());
$Crystal->SetCrystalName($this->CrystalName());
$Crystal->SetLatticeSystem($this->LatticeSystem());
my $SPGName = $this->SPGName();
my $iSPG = $this->iSPG();
my $iSet = $this->iSet();
$Crystal->SetSpaceGroup($SPGName, $iSPG, $iSet);
$Crystal->SetCSpaceGroup($this->GetCSpaceGroup());
$Crystal->SetLatticeParameters($this->LatticeParameters());
$Crystal->SetCAtomTypeList($this->GetCAtomTypeList());
$Crystal->SetCAsymmetricAtomSiteList($this->GetCAsymmetricAtomSiteList());
$Crystal->SetTemperature($this->Temperature());
$Crystal->CalcMetrics();
my @v = $this->LatticeVectors();
if(@v > 0) {
$Crystal->SetLatticeVectors(@v);
}
$Crystal->ExpandCoordinates();
return $Crystal;
}
sub Symmetrize
{
my ($this, $tollatt, $tolangle, $tolpos) = @_;
$tollatt = 1.0e-5 if(!defined $tollatt);
$tolangle = $tollatt if(!defined $tolangle);
$tolpos = $tollatt if(!defined $tolpos);
my ($a,$b,$c,$alpha,$beta,$gamma) = $this->LatticeParameters();
$alpha = 90 if(abs($alpha-90.0) < $tolangle);
$beta = 90 if(abs($beta -90.0) < $tolangle);
$gamma = 90 if(abs($gamma-90.0) < $tolangle);
$this->LatticeParameters($a, $b, $c, $alpha, $beta, $gamma);
$this->SetLatticeSystem('', 1, $tollatt, $tolangle);
my $SPG = $this->GetCSpaceGroup();
my $ls = $SPG->LatticeSystem();
if($ls eq 'cubic') {
my $ar = ($a+$b+$c) / 3.0;
$this->SetLatticeParameters($ar,$ar,$ar,90,90,90);
}
elsif($ls eq 'rhombohedral' or $ls eq 'trigonal') {
my $ar = ($a+$b+$c) / 3.0;
my $al = ($alpha+$beta+$gamma) / 3.0;
$this->SetLatticeParameters($ar,$ar,$ar,$alpha,$alpha,$alpha);
}
elsif($ls eq 'tetragonal') {
my $ar = ($a+$b) / 2.0;
$this->SetLatticeParameters($ar,$ar,$c,90,90,90);
}
elsif($ls eq 'hexagonal') {
my $ar = ($a+$b) / 2.0;
$this->SetLatticeParameters($ar,$ar,$c,90,90,120);
}
elsif($ls eq 'orthorhombic') {
$this->SetLatticeParameters($a,$b,$c,90,90,90);
}
my @site = $this->GetCAsymmetricAtomSiteList();
for(my $i = 0 ; $i < @site ; $i++) {
my ($x, $y, $z) = $site[$i]->Position();
$x = $this->RoundParameter($x, $tolpos);
$y = $this->RoundParameter($y, $tolpos);
$z = $this->RoundParameter($z, $tolpos);
$site[$i]->SetPosition($x, $y, $z);
}
$this->ExpandCoordinates();
}
sub SymmetrizeParameter
{
my ($this, $ls, $x, $y, $z, $tol) = @_;
$ls = $this->LatticeSystem() if(!defined $ls or $ls eq '');
if($ls eq 'cubic' or $ls eq 'rhombohedral' or $ls eq 'trigonal') {
$x = $y = $z = ($x+$y+$z) / 3.0;
}
elsif($ls eq 'tetragonal' or $ls eq 'hexagonal') {
$x = $y = ($x+$y) / 2.0;
}
$x = $this->RoundParameter($x, $tol);
$y = $this->RoundParameter($y, $tol);
$z = $this->RoundParameter($z, $tol);
return ($x, $y, $z);
}
sub RoundParameter
{
my ($this, $x, $tol) = @_;
return $tol * int( ($x+0.1*$tol) / $tol );
}
sub GetCnMultiplicityExpandedAtomSiteList
{
my ($this) = @_;
my $RefnAtomList = $this->{"RefnExpandedAtomSite"};
return () if($RefnAtomList eq '');
return @$RefnAtomList;
}
#How to build Crystal (Ex. from CIF.pm)
# my $crystal = new Crystal();
# $crystal->SetLatticeParameters(
# $this->LatticeParameters() );
# my $SPG = $this->GetCSpaceGroup();
# $crystal->SetCSpaceGroup($SPG);
# $crystal->SetCAtomTypeList(
# $this->GetCAtomTypeList() );
# $crystal->SetCAsymmetricAtomSiteList(
# $this->GetCAsymmetricAtomSiteList() );
#==================================================================
# コンストラクタ、デストラクト
#==================================================================
sub new
{
my ($module) = @_;
my $this = {};
bless $this;
require Math::Matrix;
require Math::MatrixReal;
my $SPG = new SpaceGroup();
$SPG->SetP1();
$this->{"SpaceGroup"} = $SPG;
my @AtomTypeList;
# my $at1 = new AtomType();
# push(@AtomTypeList, $at1);
# pop(@AtomTypeList);
$this->{"RefAtomTypeList"} = \@AtomTypeList;;
$this->{"nAtomType"} = 0;
my @AsAtomSiteList;
my $at2 = new AtomSite();
push(@AsAtomSiteList, $at2);
pop(@AsAtomSiteList);
$this->{"RefAsymmetricAtomSiteList"} = \@AsAtomSiteList;
$this->{"nAsymmetricAtomSite"} = 0;
my @ExAtomSiteList;
my $at3 = new AtomSite();
push(@ExAtomSiteList, $at3);
pop(@ExAtomSiteList);
$this->{"RefExpandedAtomSiteList"} = \@ExAtomSiteList;
my @nAtomList;
$this->{"RefnExpandedAtomSite"} = \@nAtomList;
$this->{"nTotalExpandedAtomSite"} = 0;
return $this;
}
sub DESTROY
{
my $this = shift;
}
#==================================================================
# 一般メンバー関数
#==================================================================
sub PreferentialOrientation
{
my ($this, $IsSheet, $ho, $ko, $lo, $p1, $p2, $h, $k, $l) = @_;
return 1.0 if($p1 == 1.0 or $p2 == 0.0);
my @VO = $this->GetVectorFromHKL($ho, $ko, $lo);
my @Vhkl = $this->GetVectorFromHKL($h, $k, $l);
my $eVO = Sci::NormalizeVector(\@VO);
my $eVhkl = Sci::NormalizeVector(\@Vhkl);
my $c = Sci::InnerProduct($eVO, $eVhkl);
my $Q = acos(abs($c));
$Q = $pi / 2.0 - $Q if(!$IsSheet);
#print "PO [$IsSheet]: $ho,$ko,$lo - $h,$k,$l => $Q\n";
return $p1 + (1.0 - $p1) * exp(-$p2 * $Q * $Q);
}
sub IsVariablePosition
{
my ($this, $x, $y, $z, $pos)= @_;
my @x1 = ($x, $y, $z);
my $c0 = new Crystal();
$c0->SetLatticeParameters($this->LatticeParameters());
$c0->SetCSpaceGroup($this->GetCSpaceGroup());
$c0->AddAtomType('H');
$c0->AddAtomSite('H1', 'H', $x1[0], $x1[1], $x1[2]);
$c0->ExpandCoordinates();
my @sites = $c0->GetCExpandedAtomSiteList();
my $nSite = @sites;
my $VariablePos = 1.0;
$x1[$pos] += 0.14142;
my $c1 = new Crystal();
$c1->SetLatticeParameters($this->LatticeParameters());
$c1->SetCSpaceGroup($this->GetCSpaceGroup());
$c1->AddAtomType('H');
$c1->AddAtomSite('H1', 'H', $x1[0], $x1[1], $x1[2]);
$c1->ExpandCoordinates();
@sites = $c1->GetCExpandedAtomSiteList();
$VariablePos = 0.0 if($nSite < @sites);
return $VariablePos;
}
sub SetMinimalLatticeParameters
{
my ($this, @a) = @_;
my $ls = lc $this->LatticeSystem();
#print "ls: $ls\n";
if($ls eq 'cubic') {
$this->SetLatticeParameters($a[0], $a[0], $a[0], 90.0, 90.0, 90.0);
}
elsif($ls =~ /tetra/) {
$this->SetLatticeParameters($a[0], $a[0], $a[1], 90.0, 90.0, 90.0);
}
elsif($ls =~ /ortho/) {
$this->SetLatticeParameters($a[0], $a[1], $a[2], 90.0, 90.0, 90.0);
}
elsif($ls =~ /(rhomb|trig)/) {
$this->SetLatticeParameters($a[0], $a[0], $a[0], $a[1], $a[1], $a[1]);
}
elsif($ls =~ /hex/) {
$this->SetLatticeParameters($a[0], $a[0], $a[1], 90.0, 90.0, 120.0);
}
elsif($ls =~ /mono/) {
$this->SetLatticeParameters($a[0], $a[1], $a[2], 90.0, $a[3], 90.0);
}
else {
$this->SetLatticeParameters($a[0], $a[1], $a[2], $a[3], $a[4], $a[5]);
}
my ($a,$b,$c,$alpha,$beta,$gamma) = $this->LatticeParameters();
#print "latt: $a $b $c $alpha $beta $gamma\n";
return ($a,$b,$c,$alpha,$beta,$gamma);
}
sub SetLatticeParameters
{
my ($this, $a, $b, $c, $alpha, $beta, $gamma) = @_;
# return if(!defined $a or $a == 0);
$this->{a} = $a;
$this->{b} = $b;
$this->{c} = $c;
$this->{alpha} = $alpha;
$this->{beta} = $beta;
$this->{gamma} = $gamma;
#print "CrystalObject::SetLatticeParameters\n";
$this->CalcMetrics();
$this->GetCSpaceGroup()->SetLatticeParameters($a, $b, $c, $alpha, $beta, $gamma);
return ($a, $b, $c, $alpha, $beta, $gamma);
}
sub LatticeParameters
{
my ($this, $UseAtomicUnit) = @_;
my $a = $this->{'a'};
#$a = 1.0 if(not defined $a);
my $b = $this->{'b'};
#$b = $a if(not defined $b);
my $c = $this->{'c'};
#$c = $b if(not defined $c);
my $alpha = $this->{'alpha'};
#$alpha = 90.0 if(not defined $alpha);
my $beta = $this->{'beta'};
#$beta = 90.0 if(not defined $beta);
my $gamma = $this->{'gamma'};
#$gamma = 90.0 if(not defined $gamma);
my $k = ($UseAtomicUnit)? 1.0 / 0.529177 : 1.0;
return ($k*$a, $k*$b, $k*$c, $alpha, $beta, $gamma);
}
sub ReciprocalLatticeParameters
{
my ($this, $UseAtomicUnit) = @_;
my $Ra = $this->{Ra};
my $Rb = $this->{Rb};
my $Rc = $this->{Rc};
my $Ralpha = $this->{Ralpha};
my $Rbeta = $this->{Rbeta};
my $Rgamma = $this->{Rgamma};
my $k = ($UseAtomicUnit)? 0.529177 : 1.0;
return ($k*$Ra, $k*$Rb, $k*$Rc, $Ralpha, $Rbeta, $Rgamma);
}
sub GetReciprocalDistanceFromK
{
my ($this, $kx0, $ky0, $kz0, $kx1, $ky1, $kz1) = @_;
#printf "k: (%6.3g,%6.3g,%6.3g) - (%6.3g,%6.3g,%6.3g): ",
# $kx0,$ky0,$kz0,$kx1,$ky1,$kz1;
$kx0 = $kx1 - $kx0 if(defined $kx1);
$ky0 = $ky1 - $ky0 if(defined $ky1);
$kz0 = $kz1 - $kz0 if(defined $kz1);
#printf "(%6.3g,%6.3g,%6.3g)\n", $kx0, $ky0, $kz0;
if(0) {
my $Ra = $this->{'Ra'};
my $Rb = $this->{'Rb'};
my $Rc = $this->{'Rc'};
#print "Rec lat: $Ra $Rb $Rc\n";
$kx0 = $kx0 / $Ra;
$ky0 = $ky0 / $Rb;
$kz0 = $kz0 / $Rc;
}
my $Rg11 = $this->{"Rg11"};
my $Rg12 = $this->{"Rg12"};
my $Rg13 = $this->{"Rg13"};
my $Rg21 = $this->{"Rg21"};
my $Rg22 = $this->{"Rg22"};
my $Rg23 = $this->{"Rg23"};
my $Rg31 = $this->{"Rg31"};
my $Rg32 = $this->{"Rg32"};
my $Rg33 = $this->{"Rg33"};
#print "Rg: ($Rg11,$Rg22,$Rg33,$Rg12,$Rg23,$Rg13)
\n";
#print "($h,$k,$l)-($Rg11,$Rg22,$Rg33,$Rg12,$Rg23,$Rg13)
\n";
my $d = $Rg11*$kx0*$kx0 + $Rg22*$ky0*$ky0 + $Rg33*$kz0*$kz0
+ 2.0*$Rg12*$kx0*$ky0 + 2.0*$Rg23*$ky0*$kz0 + 2.0*$Rg13*$kx0*$kz0;
$d = 0.0 if($d < 0.0);
$d = sqrt($d);
#print "d=$d
\n";
#print "enter: $d ($h,$k,$l),($Rg11,$Rg22,$Rg33,$Rg12,$Rg23,$Rg13)
";
return $d;
}
sub CalculateHKLInterplanarSpacing
{
my ($this, $h, $k, $l) = @_;
my $Rg11 = $this->{"Rg11"};
my $Rg12 = $this->{"Rg12"};
my $Rg13 = $this->{"Rg13"};
my $Rg21 = $this->{"Rg21"};
my $Rg22 = $this->{"Rg22"};
my $Rg23 = $this->{"Rg23"};
my $Rg31 = $this->{"Rg31"};
my $Rg32 = $this->{"Rg32"};
my $Rg33 = $this->{"Rg33"};
#print "($h,$k,$l)-($Rg11,$Rg22,$Rg33,$Rg12,$Rg23,$Rg13)
\n";
my $d = $Rg11*$h*$h + $Rg22*$k*$k + $Rg33*$l*$l
+ 2.0*$Rg12*$h*$k + 2.0*$Rg23*$k*$l + 2.0*$Rg13*$l*$h;
#print "d=$d
";
#print "enter: $d ($h,$k,$l),($Rg11,$Rg22,$Rg33,$Rg12,$Rg23,$Rg13)
";
return 0.0 if($d == 0.0);
$d = sqrt(1.0 / $d);
#print "d=$d
";
return $d;
}
sub CalculateDiffractionAngleFromInterplanarSpacing
{
my ($this, $wl, $d) = @_;
return 0.0 if($d == 0.0);
my $sq = $wl * 0.5 / $d;
#print "sq: $sq
";
return 0.0 if(abs($sq) > 1.0);
my $Q = Sci::dasin($sq);
return $Q;
}
sub CalMetrics
{
my ($this) = @_;
return $this->CalcMetrics();
}
sub CalcMetrics
{
my ($this) = @_;
my $SPG = $this->GetCSpaceGroup();
my $SPGName = $SPG->SPGName();
my $ls = $this->LatticeSystem();
my $pT = $this->GetMatrixConventionalToPrimitiveCell(0);
my ($a,$b,$c,$alpha,$beta,$gamma) = $this->LatticeParameters();
if($a == 0.0) {
print "Error: CrystalObject.pm::CalcMetrics: Invalid Lattice Parameters.
\n";
print "Lattice: $a $b $c $alpha $beta $gamma
\n";
}
my $torad = Sci::torad(); #3.1415926535 / 180.0;
$this->{g11} = $a * $a;
$this->{g12} = $this->{g21} = $a * $b * cos($torad * $gamma);
$this->{g12} = $this->{g21} = 0.0 if(abs($this->{"g12"}) < 1.0e-7);
$this->{g13} = $this->{g31} = $a * $c * cos($torad * $beta);
$this->{g13} = $this->{g31} = 0.0 if(abs($this->{"g13"}) < 1.0e-7);
$this->{g22} = $b * $b;
$this->{g23} = $this->{g32} = $b * $c * cos($torad * $alpha);
$this->{g23} = $this->{g32} = 0.0 if(abs($this->{"g23"}) < 1.0e-7);
$this->{g33} = $c * $c;
my $V = $this->CalculateVolume();
#print "V=$V
";
#print "a=$a\n";
my ($ax, $ay, $az, $bx, $by, $bz, $cx, $cy, $cz);
#print "SPG:[$this->{ConventionalSPGName}][$SPGName] LS: $ls\n";
#exit;
if($ls eq 'trigonal' and abs($alpha - 60.0) < 1.0e-3) {
$this->{ConventionalSPGName} = 'F';
my $ac = $a * sqrt(2.0);
$this->{ConventionalLatticeParameters} = [$ac, $ac, $ac, 90.0, 90.0, 90.0];
}
if((defined $this->{ConventionalSPGName} and $this->{ConventionalSPGName} =~ /^\s*[FI]/i)) {
#print "CrystalObject::CalcMetrics: Lattice Vectors for primitive cell (SPG: $this->{ConventionalSPGName} / $SPGName)\n";
#print "********[$this->{ConventionalSPGName}][$SPGName]\n";
#exit;
my $pT = $this->GetMatrixConventionalToPrimitiveCell(1);
my $pl = $this->{ConventionalLatticeParameters};
my ($a, $b, $c, $alpha, $beta, $gamma) = @$pl;
#print "a=$a, $b, $c, $alpha\n";
#exit;
#Replace with those for [ABC] after check for [FI]
# $ax = $c * $pT->[0][0];
# $ay = $c * $pT->[0][1];
# $az = $c * $pT->[0][2];
# $bx = $a * $pT->[1][0];
# $by = $a * $pT->[1][1];
# $bz = $a * $pT->[1][2];
# $cx = $b * $pT->[2][0];
# $cy = $b * $pT->[2][1];
# $cz = $b * $pT->[2][2];
$ax = $a * $pT->[0][0];
$ay = $b * $pT->[0][1];
$az = $c * $pT->[0][2];
$bx = $a * $pT->[1][0];
$by = $b * $pT->[1][1];
$bz = $c * $pT->[1][2];
$cx = $a * $pT->[2][0];
$cy = $b * $pT->[2][1];
$cz = $c * $pT->[2][2];
#print "X: $pT->[0][0] $pT->[0][1] $pT->[0][2]\n";
#print "Y: $pT->[1][0] $pT->[1][1] $pT->[1][2]\n";
#print "Z: $pT->[2][0] $pT->[2][1] $pT->[2][2]\n";
#exit;
}
elsif(defined $this->{ConventionalSPGName} and $this->{ConventionalSPGName} =~ /^\s*[ABC]/i) {
#print "CrystalObject::CalcMetrics: Lattice Vectors for primitive cell (SPG: $this->{ConventionalSPGName} / $SPGName)\n";
#print "********[$this->{ConventionalSPGName}][$SPGName]\n";
my $pT = $this->GetMatrixConventionalToPrimitiveCell(1);
my $pl = $this->{ConventionalLatticeParameters};
my ($a, $b, $c, $alpha, $beta, $gamma) = @$pl;
$ax = $a * $pT->[0][0];
$ay = $b * $pT->[0][1];
$az = $c * $pT->[0][2];
$bx = $a * $pT->[1][0];
$by = $b * $pT->[1][1];
$bz = $c * $pT->[1][2];
$cx = $a * $pT->[2][0];
$cy = $b * $pT->[2][1];
$cz = $c * $pT->[2][2];
#print "latt: $a, $b, $c, $alpha, $beta, $gamma\n";
#print "pT ($pT->[0][0], $pT->[0][1], $pT->[0][2])\n";
#print "pT ($pT->[1][0], $pT->[1][1], $pT->[1][2])\n";
#print "pT ($pT->[2][0], $pT->[2][1], $pT->[2][2])\n";
#print "a=($ax, $ay, $az)\n";
#print "b=($bx, $by, $bz)\n";
#print "c=($cx, $cy, $cz)\n";
#print "X: $pT->[0][0] $pT->[0][1] $pT->[0][2]\n";
#print "Y: $pT->[1][0] $pT->[1][1] $pT->[1][2]\n";
#print "Z: $pT->[2][0] $pT->[2][1] $pT->[2][2]\n";
#exit;
}
elsif($ls =~ /hex/i) {
# elsif($SPGName =~ /^\s*P\s*6/) {
#print "CrystalObject::CalcMetrics: Lattice Vectors for hexagonal (SPG: $SPGName)\n";
# $ax = $a;
# $ay = 0.0;
# $az = 0.0;
# $bx = $ax / 2.0;
# $by = sqrt(3) / 2.0 * $ay;
# $bz = 0.0;
$ax = $a / 2.0;
$ay = -sqrt(3.0) / 2.0 * $a;
$az = 0.0;
$bx = $ax;
$by = -$ay;
$bz = 0.0;
$cx = 0.0;
$cy = 0.0;
$cz = $c;
}
elsif($ls =~ /^(trig|rhomb)/i) {
# elsif($SPGName =~ /^\s*R/ and abs($alpha - 90.0) > 1.0e-2 and abs($alpha - $beta) < 1.0e-2) {
#print "CrystalObject::CalcMetrics: Lattice Vectors for rhombohedral (SPG: $SPGName)\n";
my $cosa = cos($torad * $alpha);
my $axy = $a * sqrt( (1.0 - $cosa) / 6.0);
$ax = sqrt(3.0) * $axy;
$ay = $axy;
$az = $axy * sqrt(6.0 / (1.0 - $cosa) - 4.0);
$bx = -$ax;
$by = $ay;
$bz = $az;
$cx = 0.0;
$cy = -2.0 * $axy;
$cz = $az;
#print "alpha=$alpha, cosa=$cosa, axy=$axy, a=$a\n";
#print "a=sqrt(a2+b2+c2)=", sqrt($ax*$ax+$ay*$ay+$az*$az), "\n";
}
else {
#print "CrystalObject::CalcMetrics: Lattice Vectors for conventional cell (SPG: $SPGName)\n";
$ax = $a;
$ay = 0.0;
$az = 0.0;
$bx = $b * Sci::dcos($gamma);
$by = $b * Sci::dsin($gamma);
$bz = 0.0;
$cx = $c * Sci::dcos($beta);
$cy = ($c * Sci::dcos($alpha) - $cx * Sci::dcos($gamma)) / Sci::dsin($gamma);
$cz = sqrt($c*$c - $cx*$cx - $cy*$cy);
}
$this->{a11} = $ax;
$this->{a12} = $ay;
$this->{a13} = $az;
$this->{a21} = $bx;
$this->{a22} = $by;
$this->{a23} = $bz;
$this->{a31} = $cx;
$this->{a32} = $cy;
$this->{a33} = $cz;
my $sina = Sci::dsin($alpha);
my $sinb = Sci::dsin($beta);
my $sinc = Sci::dsin($gamma);
my $cosa = Sci::dcos($alpha);
my $cosb = Sci::dcos($beta);
my $cosc = Sci::dcos($gamma);
$this->{Rg11} = $b*$b*$c*$c*$sina*$sina / $V / $V;
$this->{Rg12} = $this->{Rg21} = $a*$b*$c*$c * ($cosa*$cosb - $cosc) / $V / $V;
$this->{Rg12} = $this->{Rg21} = 0.0 if(abs($this->{"g12"}) < 1.0e-7);
$this->{Rg13} = $this->{Rg31} = $a*$b*$b*$c * ($cosc*$cosa - $cosb) / $V / $V;
$this->{Rg13} = $this->{Rg31} = 0.0 if(abs($this->{"g13"}) < 1.0e-7);
$this->{Rg22} = $a*$a*$c*$c*$sinb*$sinb / $V / $V;
$this->{Rg23} = $this->{Rg32} = $a*$a*$b*$c * ($cosb*$cosc - $cosa) / $V / $V;
$this->{Rg23} = $this->{Rg32} = 0.0 if(abs($this->{"g23"}) < 1.0e-7);
$this->{Rg33} = $a*$a*$b*$b*$sinc*$sinc / $V / $V;
my $Ra = $this->{Ra} = sqrt($this->{Rg11});
my $Rb = $this->{Rb} = sqrt($this->{Rg22});
my $Rc = $this->{Rc} = sqrt($this->{Rg33});
my $Ralpha = $this->{Ralpha} = $todeg * acos($this->{Rg23} / $this->{Rb} / $this->{Rc});
my $Rbeta = $this->{Rbeta} = $todeg * acos($this->{Rg31} / $this->{Rc} / $this->{Ra});
my $Rgamma = $this->{Rgamma} = $todeg * acos($this->{Rg12} / $this->{Ra} / $this->{Rb});
#print "RLatt: $Ra, $Rb, $Rc, $Ralpha, $Rbeta, $Rgamma\n";
my $kV = 1.0 / $V;
my ($Rax, $Ray, $Raz) = Sci::VectorProduct([$bx, $by, $bz], [$cx, $cy, $cz], $kV);
my ($Rbx, $Rby, $Rbz) = Sci::VectorProduct([$cx, $cy, $cz], [$ax, $ay, $az], $kV);
my ($Rcx, $Rcy, $Rcz) = Sci::VectorProduct([$ax, $ay, $az], [$bx, $by, $bz], $kV);
#my $cc = Sci::ScalarProduct([$Rax, $Ray, $Raz], [$Rax, $Ray, $Raz]);
#print "cc=$cc, $this->{Rg11}\n";
#$cc = Sci::ScalarProduct([$Rax, $Ray, $Raz], [$Rbx, $Rby, $Rbz]);
#print "cc=$cc, $this->{Rg12}\n";
#printf "Rx*1=(%7.3f, %7.3f, %7.3f)\n", $Rax, $Ray, $Raz;
#printf "Ry*1=(%7.3f, %7.3f, %7.3f)\n", $Rbx, $Rby, $Rbz;
#printf "Rz*1=(%7.3f, %7.3f, %7.3f)\n", $Rcx, $Rcy, $Rcz;
if(0) {
my $Rax = $Ra;
my $Ray = 0.0;
my $Raz = 0.0;
my $Rbx = $Rb * Sci::dcos($Rgamma);
my $Rby = $Rb * Sci::dsin($Rgamma);
my $Rbz = 0.0;
my $Rcx = $Rc * Sci::dcos($Rbeta);
my $Rcy = ($Rc * Sci::dcos($Ralpha) - $Rcx * Sci::dcos($Rgamma)) / Sci::dsin($Rgamma);
my $Rcz = sqrt($Rc*$Rc - $Rcx*$Rcx - $Rcy*$Rcy);
}
$this->{Ra11} = $Rax;
$this->{Ra12} = $Ray;
$this->{Ra13} = $Raz;
$this->{Ra21} = $Rbx;
$this->{Ra22} = $Rby;
$this->{Ra23} = $Rbz;
$this->{Ra31} = $Rcx;
$this->{Ra32} = $Rcy;
$this->{Ra33} = $Rcz;
#printf "Rx*2=(%7.3f, %7.3f, %7.3f)\n", $Rax, $Ray, $Raz;
#printf "Ry*2=(%7.3f, %7.3f, %7.3f)\n", $Rbx, $Rby, $Rbz;
#printf "Rz*2=(%7.3f, %7.3f, %7.3f)\n", $Rcx, $Rcy, $Rcz;
return 1;
}
sub LatticeVectorsByMatrix
{
my ($this, $UseAtomicUnit) = @_;
my @v = $this->LatticeVectors($UseAtomicUnit);
my @a1 = ($v[0], $v[1], $v[2]);
my @a2 = ($v[3], $v[4], $v[5]);
my @a3 = ($v[6], $v[7], $v[8]);
return (\@a1, \@a2, \@a3);
}
# @v in Math::Vector::Real
sub LatticeVectorsByVectors
{
my ($this, $UseAtomicUnit) = @_;
my @v = $this->LatticeVectors($UseAtomicUnit);
my $a1 = V($v[0], $v[1], $v[2]);
my $a2 = V($v[3], $v[4], $v[5]);
my $a3 = V($v[6], $v[7], $v[8]);
return ($a1, $a2, $a3);
}
sub LatticeVectors
{
my ($this, $UseAtomicUnit) = @_;
#my $SPG = $this->GetCSpaceGroup();
#my $SPGName = $SPG->SPGName();
#print "SPG in CrystalObject.pm:[$SPGName]\n";
#exit;
my $k = ($UseAtomicUnit)? 1.0 / 0.529177 : 1.0;
return ($k*$this->{a11}, $k*$this->{a12}, $k*$this->{a13},
$k*$this->{a21}, $k*$this->{a22}, $k*$this->{a23},
$k*$this->{a31}, $k*$this->{a32}, $k*$this->{a33});
}
sub ReciprocalLatticeVectorsByMatrix
{
my ($this, $UseAtomicUnit) = @_;
my @v = $this->ReciprocalLatticeVectors($UseAtomicUnit);
my @a1 = $v[0,2];
my @a2 = $v[3,5];
my @a3 = $v[6,8];
return (\@a1, \@a2, \@a3);
}
sub ReciprocalLatticeVectorsByVectors
{
my ($this, $UseAtomicUnit) = @_;
my @v = $this->ReciprocalLatticeVectors($UseAtomicUnit);
my $a1 = V($v[0,2]);
my $a2 = V($v[3,5]);
my $a3 = V($v[6,8]);
return ($a1, $a2, $a3);
}
sub ReciprocalLatticeVectors
{
my ($this, $UseAtomicUnit) = @_;
my $k = ($UseAtomicUnit)? 1.0 / 0.529177 : 1.0;
return ($k*$this->{Ra11}, $k*$this->{Ra12}, $k*$this->{Ra13},
$k*$this->{Ra21}, $k*$this->{Ra22}, $k*$this->{Ra23},
$k*$this->{Ra31}, $k*$this->{Ra32}, $k*$this->{Ra33});
}
sub GetLatticeRange
{
my ($this, $MaximumDistance) = @_;
my ($a,$b,$c,$alpha,$beta,$gamma) = $this->LatticeParameters();
my $sinA = abs(sin($alpha * $torad));
my $sinB = abs(sin($beta * $torad));
my $sinG = abs(sin($gamma * $torad));
$sinA = $sinB if($sinB < $sinA);
$sinA = $sinG if($sinG < $sinA);
my $nx = int($MaximumDistance / $a / $sinA);
my $ny = int($MaximumDistance / $b / $sinA);
my $nz = int($MaximumDistance / $c / $sinA);
return ($nx+1, $ny+1, $nz+1);
}
sub GetYRangeForIteration
{
my ($this, $ix) = @_;
my $ls = lc $this->LatticeSystem();
#print "ls: $ls\n";
if($ls eq 'cubic' or $ls =~ /(rhomb|trig)/) {
return $ix;
}
elsif($ls =~ /(rhomb|trig)/) {
return $ix;
}
elsif($ls =~ /tetra/ or $ls =~ /hex/) {
return $ix;
}
elsif($ls =~ /ortho/) {
return 0;
}
elsif($ls =~ /mono/) {
return 0;
}
else {
return 0;
}
}
sub GetZRangeForIteration
{
my ($this, $ix, $iy) = @_;
my $ls = lc $this->LatticeSystem();
#print "ls: $ls\n";
if($ls eq 'cubic' or $ls =~ /(rhomb|trig)/) {
return $iy;
}
elsif($ls =~ /tetra/ or $ls =~ /hex/) {
return 0;
}
elsif($ls =~ /ortho/) {
return 0;
}
elsif($ls =~ /mono/) {
return 0;
}
else {
return 0;
}
}
sub ConvertLatticeIndexByLatticeSystem
{
my ($this, $ix, $iy, $iz, $SortDirection) = @_;
$SortDirection = 1 if(!defined $SortDirection);
if($SortDirection == 0) {
return (0, $ix, $iy, $iz);
}
my $ls = lc $this->LatticeSystem();
#print "ls: $ls\n";
my $IsConverted = 0;
if($ls eq 'cubic' or $ls =~ /(rhomb|trig)/) {
$IsConverted = 1 if($ix > $iy or $iy > $iz or $ix > $iz);
if($SortDirection > 0) {
return ($IsConverted, sort { return $a <=> $b; } ($ix, $iy, $iz) );
}
return ($IsConverted, sort { return $b <=> $a; } ($ix, $iy, $iz) );
}
elsif($ls =~ /tetra/ or $ls =~ /hex/) {
$IsConverted = 1 if($ix > $iy);
my @a;
if($SortDirection > 0) {
@a = sort { return $a <=> $b; } ($ix, $iy);
}
else {
@a = sort { return $b <=> $a; } ($ix, $iy);
}
return ($IsConverted, @a, $iz);
}
else {
# die "\nCrystaObject::ConvertLatticeIndexByLatticeSystem: Error: Lattice system [$ls] is not supported.\n";
# print "\nCrystaObject::ConvertLatticeIndexByLatticeSystem: Warning: Lattice system [$ls] is not supported.\n";
}
return ($IsConverted, $ix, $iy, $iz);
}
sub Metrics
{
my ($this) = @_;
my $g11 = $this->{g11};
my $g12 = $this->{g12};
my $g13 = $this->{g13};
my $g21 = $this->{g21};
my $g22 = $this->{g22};
my $g23 = $this->{g23};
my $g31 = $this->{g31};
my $g32 = $this->{g32};
my $g33 = $this->{g33};
return ($g11, $g12, $g13,
$g21, $g22, $g23,
$g31, $g32, $g33)
}
sub GetPerpendicularHKL
{
my ($this, $h1, $k1, $l1, $h2, $k2, $l2) = @_;
my @v1 = $this->GetVectorFromHKL($h1, $k1, $l1);
my @v2 = $this->GetVectorFromHKL($h2, $k2, $l2);
my @v3 = Sci::VectorProduct(\@v1, \@v2);
# my @v3 = ($v1[1]*$v2[2] - $v1[2]*$v2[1],
# $v1[2]*$v2[0] - $v1[0]*$v2[2],
# $v1[0]*$v2[1] - $v1[1]*$v2[0]);
#printf "v1=(%7.5f, %7.5f, %7.5f)\n", @v1;
#printf "v2=(%7.5f, %7.5f, %7.5f)\n", @v2;
#printf "v3=(%7.5f, %7.5f, %7.5f)\n", @v3;
my $R = new Math::MatrixReal(3, 3);
$R->assign(1, 1, $this->{Ra11});
$R->assign(1, 2, $this->{Ra21});
$R->assign(1, 3, $this->{Ra31});
$R->assign(2, 1, $this->{Ra12});
$R->assign(2, 2, $this->{Ra22});
$R->assign(2, 3, $this->{Ra32});
$R->assign(3, 1, $this->{Ra13});
$R->assign(3, 2, $this->{Ra23});
$R->assign(3, 3, $this->{Ra33});
#print "R=$R\n";
my $RR = $R->inverse();
#print "RR=$RR\n";
my @hkl = ($RR->element(1,1)*$v3[0] + $RR->element(1,2)*$v3[1] + $RR->element(1,3)*$v3[2],
$RR->element(2,1)*$v3[0] + $RR->element(2,2)*$v3[1] + $RR->element(2,3)*$v3[2],
$RR->element(3,1)*$v3[0] + $RR->element(3,2)*$v3[1] + $RR->element(3,3)*$v3[2]);
my ($min, $max) = Utils::CalMinMax(\@hkl, 1);
#printf "hkl=%6.3f %6.3f %6.3f\n", @hkl;
$min = 1.0e30;
for(my $i = 0 ; $i < 3 ; $i++) {
$hkl[$i] = 0.0 if(abs($hkl[$i]) < $max * 1.0e-15);
$min = $hkl[$i] if($hkl[$i] != 0 and $min > $hkl[$i]);
}
if($min < 1.0e30) {
for(my $i = 0 ; $i < 3 ; $i++) {
$hkl[$i] /= $min;
}
}
#printf "hkl=%6.3f %6.3f %6.3f\n", @hkl;
#my @v32 = $this->GetVectorFromHKL(@hkl);
#printf "v3=(%7.5f, %7.5f, %7.5f)\n", @v32;
return @hkl;
}
sub GetVectorFromHKL
{
my ($this, $h, $k, $l) = @_;
my @V = ($this->{Ra11} * $h + $this->{Ra21} * $k + $this->{Ra31} * $l,
$this->{Ra12} * $h + $this->{Ra22} * $k + $this->{Ra32} * $l,
$this->{Ra13} * $h + $this->{Ra23} * $k + $this->{Ra33} * $l);
#print "hkl=$h,$k,$l: ", join(',', @V), ")\n";
return @V;
}
sub RMetrics
{
my ($this) = @_;
my $Rg11 = $this->{Rg11};
my $Rg12 = $this->{Rg12};
my $Rg13 = $this->{Rg13};
my $Rg21 = $this->{Rg21};
my $Rg22 = $this->{Rg22};
my $Rg23 = $this->{Rg23};
my $Rg31 = $this->{Rg31};
my $Rg32 = $this->{Rg32};
my $Rg33 = $this->{Rg33};
return ($Rg11, $Rg12, $Rg13,
$Rg21, $Rg22, $Rg23,
$Rg31, $Rg32, $Rg33)
}
# @v in Math::Vector::Real
sub SetLatticeVectorsByVectors
{
my ($this, @v) = @_;
#print "v1=", $v[0], "\n";
#print "v2=", $v[1], "\n";
#print "v3=", $v[2], "\n";
$this->SetLatticeVector(
$v[0][0], $v[0][1], $v[0][2],
$v[1][0], $v[1][1], $v[1][2],
$v[2][0], $v[2][1], $v[2][2]);
}
sub SetLatticeVectors
{
my ($this, $a11, $a12, $a13,
$a21, $a22, $a23, $a31, $a32, $a33) = @_;
return $this->SetLatticeVector(
$a11, $a12, $a13,
$a21, $a22, $a23,
$a31, $a32, $a33);
}
sub SetLatticeVector
{
my ($this, $a11, $a12, $a13,
$a21, $a22, $a23, $a31, $a32, $a33) = @_;
#print "($this, $a11, $a12, $a13, $a21, $a22, $a23, $a31, $a32, $a33)\n";
$this->{a11} = $a11;
$this->{a12} = $a12;
$this->{a13} = $a13;
$this->{a21} = $a21;
$this->{a22} = $a22;
$this->{a23} = $a23;
$this->{a31} = $a31;
$this->{a32} = $a32;
$this->{a33} = $a33;
#print "SetLatticeVector: a1=$this->{a11},$this->{a12},$this->{a13}\n";
#print "SetLatticeVector: a2=$this->{a21},$this->{a22},$this->{a23}\n";
#print "SetLatticeVector: a3=$this->{a31},$this->{a32},$this->{a33}\n";
$this->CalMetricsFromLatticeVector();
$this->CalLatticeParametersFromMetrics();
return;
}
sub SetMetrics
{
my ($this,$g11,$g12,$g13,$g21,$g22,$g23,$g31,$g32,$g33)
= @_;
$this->{'g11'} = $g11;
$this->{'g12'} = $g12;
$this->{'g13'} = $g13;
$this->{'g21'} = $g21;
$this->{'g22'} = $g22;
$this->{'g23'} = $g23;
$this->{'g31'} = $g31;
$this->{'g32'} = $g32;
$this->{'g33'} = $g33;
return ($this,$g11,$g12,$g13,$g21,$g22,$g23,$g31,$g32,$g33);
}
sub CalLatticeParametersFromMetrics
{
my ($this) = @_;
my $a = sqrt($this->{'g11'});
my $b = sqrt($this->{'g22'});
my $c = sqrt($this->{'g33'});
my $alpha = $this->{'g23'} / $b / $c;
$alpha = Sci::dacos($alpha);
my $beta = $this->{'g13'} / $a / $c;
$beta = Sci::dacos($beta);
my $gamma = $this->{'g12'} / $a / $b;
$gamma = Sci::dacos($gamma);
$a = Sci::Round($a, 6);
$b = Sci::Round($b, 6);
$c = Sci::Round($c, 6);
#print "a,b,c=$a,$b,$c\n";
$alpha = Sci::Round($alpha, 4);
$beta = Sci::Round($beta , 4);
$gamma = Sci::Round($gamma, 4);
#print "alpha=$alpha,$beta,$gamma\n";
$this->SetLatticeParameters($a,$b,$c,$alpha,$beta,$gamma);
}
sub CalMetricsFromLatticeVector
{
my ($this) = @_;
my ($a11, $a12, $a13, $a21, $a22, $a23, $a31, $a32, $a33) = (
$this->{a11}, $this->{a12}, $this->{a13},
$this->{a21}, $this->{a22}, $this->{a23},
$this->{a31}, $this->{a32}, $this->{a33});
$this->{g11} = $a11*$a11 + $a12*$a12 + $a13*$a13;
$this->{g22} = $a21*$a21 + $a22*$a22 + $a23*$a23;
$this->{g33} = $a31*$a31 + $a32*$a32 + $a33*$a33;
$this->{g12} = $this->{g21} = $a11*$a21 + $a12*$a22 + $a13*$a23;
$this->{g23} = $this->{g32} = $a21*$a31 + $a22*$a32 + $a23*$a33;
$this->{g31} = $this->{g13} = $a31*$a11 + $a32*$a12 + $a33*$a13;
return ($this->{g11}, $this->{g12}, $this->{g13},
$this->{g21}, $this->{g22}, $this->{g23},
$this->{g31}, $this->{g32}, $this->{g33})
}
sub CalculateLatticeConstantFromVector
{
my ($this) = @_;
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};
#print "a1 ($a11, $a12, $a13)\n";
#print "a2 ($a21, $a22, $a23)\n";
#print "a3 ($a31, $a32, $a33)\n";
my $a = sqrt($a11*$a11 + $a12*$a12 + $a13*$a13);
my $b = sqrt($a21*$a21 + $a22*$a22 + $a23*$a23);
my $c = sqrt($a31*$a31 + $a32*$a32 + $a33*$a33);
#print "a=$a, $b, $c\n";
my $ab = $a11*$a21 + $a12*$a22 + $a13*$a23;
my $bc = $a21*$a31 + $a22*$a32 + $a23*$a33;
my $ca = $a31*$a11 + $a32*$a12 + $a33*$a13;
#print "ab=$ab, $bc, $ca\n";
#exit;
my $cosg = $ab / $a / $b;
my $cosa = $bc / $b / $c;
my $cosb = $ca / $c / $a;
my $alpha = Sci::dacos($cosa);
my $beta = Sci::dacos($cosb);
my $gamma = Sci::dacos($cosg);
$a = Utils::Round($a+0.0, 6); #sprintf("%11.6f", $a) + 0.0;
$b = Utils::Round($b+0.0, 6); #sprintf("%11.6f", $b) + 0.0;
$c = Utils::Round($c+0.0, 6); #sprintf("%11.6f", $c) + 0.0;
$alpha = Utils::Round($alpha+0.0, 6); #sprintf("%12.6f", $alpha) + 0.0;
$beta = Utils::Round($beta+0.0, 6); #sprintf("%12.6f", $beta) + 0.0;
$gamma = Utils::Round($gamma+0.0, 6); #sprintf("%12.6f", $gamma) + 0.0;
#print "a=$a $b $c $alpha $beta $gamma\n";
return ($a,$b,$c,$alpha,$beta,$gamma);
}
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 FractionalToCartesianByOutputMode
{
my ($this, $x,$y,$z) = @_;
my ($a11, $a12, $a13, $a21, $a22, $a23, $a31, $a32, $a33);
if($this->{'OutputMode'} eq 'expanded' or
$this->{'OutputMode'} eq 'choose' ) {
my $SuperLattice = $this->{'CSuperLattice'};
$a11 = $SuperLattice->{"a11"};
$a12 = $SuperLattice->{"a12"};
$a13 = $SuperLattice->{"a13"};
$a21 = $SuperLattice->{"a21"};
$a22 = $SuperLattice->{"a22"};
$a23 = $SuperLattice->{"a23"};
$a31 = $SuperLattice->{"a31"};
$a32 = $SuperLattice->{"a32"};
$a33 = $SuperLattice->{"a33"};
}
else {
$a11 = $this->{"a11"};
$a12 = $this->{"a12"};
$a13 = $this->{"a13"};
$a21 = $this->{"a21"};
$a22 = $this->{"a22"};
$a23 = $this->{"a23"};
$a31 = $this->{"a31"};
$a32 = $this->{"a32"};
$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 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 CartesianToFractional
{
my ($this, $xc,$yc,$zc) = @_;
my $A = new Math::MatrixReal(3, 3);
$A->assign(1, 1, $this->{"a11"});
$A->assign(1, 2, $this->{"a21"});
$A->assign(1, 3, $this->{"a31"});
# $A->assign(1, 2, $this->{"a12"});
# $A->assign(1, 3, $this->{"a13"});
$A->assign(2, 1, $this->{"a12"});
# $A->assign(2, 1, $this->{"a21"});
$A->assign(2, 2, $this->{"a22"});
$A->assign(2, 3, $this->{"a32"});
# $A->assign(2, 3, $this->{"a23"});
$A->assign(3, 1, $this->{"a13"});
$A->assign(3, 2, $this->{"a23"});
# $A->assign(3, 1, $this->{"a31"});
# $A->assign(3, 2, $this->{"a32"});
$A->assign(3, 3, $this->{"a33"});
# print "A:
\n";print $A;print "
\n";
my $RA = $A->inverse();
# print "RA:
\n";print $RA;print "
\n";
my $X = new Math::MatrixReal(3,1);
$X->assign(1, 1, $xc);
$X->assign(2, 1, $yc);
$X->assign(3, 1, $zc);
# print "X:
\n";print $X;print "
\n";
my $XX = $RA * $X;
# print "XX:
\n";print $XX;print "
\n";
return ($XX->element(1,1),
$XX->element(2,1),
$XX->element(3,1));
}
sub CalculateVolume
{
my ($this) = @_;
my $a = $this->{"a"};
my $b = $this->{"b"};
my $c = $this->{"c"};
my $alpha = $this->{"alpha"};
my $beta = $this->{"beta"};
my $gamma = $this->{"gamma"};
my $cosa = Sci::dcos($alpha);
my $cosb = Sci::dcos($beta);
my $cosg = Sci::dcos($gamma);
my $vol = 1.0 - $cosa*$cosa - $cosb*$cosb - $cosg*$cosg
+ 2.0 * $cosa*$cosb*$cosg;
$vol = $a*$b*$c*sqrt($vol);
$vol = sprintf("%14.6g", $vol) + 0.0;
$this->{"Volume"} = Sci::Round($vol, 4);
return $vol;
}
sub CalculateReciprocalVolume
{
my ($this) = @_;
my $a = $this->{Ra};
my $b = $this->{Rb};
my $c = $this->{Rc};
my $alpha = $this->{Ralpha};
my $beta = $this->{Rbeta};
my $gamma = $this->{Rgamma};
my $cosa = Sci::dcos($alpha);
my $cosb = Sci::dcos($beta);
my $cosg = Sci::dcos($gamma);
my $Rvol = 1.0 - $cosa*$cosa - $cosb*$cosb - $cosg*$cosg
+ 2.0 * $cosa*$cosb*$cosg;
$Rvol = $a*$b*$c*sqrt($Rvol);
$Rvol = sprintf("%14.6g", $Rvol) + 0.0;
$this->{RVolume} = Sci::Round($Rvol, 4);
return $Rvol;
}
sub FindAtomTypeByName
{
my ($this, $atomname) = @_;
my $atomlistref = $this->{"RefAtomTypeList"};
my $IsPartial = 0;
for(my $i = 0 ; $i < @$atomlistref ; $i++) {
my $type = $atomlistref->[$i];
my $name = $type->Label();
#print "i=$i: name=$name\n";
if($name =~ /\[.*?\]/) {
$IsPartial = 1;
last;
}
}
#print "IsP=$IsPartial\n";
if(!$IsPartial) {
$atomname =~ s/([\.\d+-]+)//;
}
for(my $i = 0 ; $i < @$atomlistref ; $i++) {
my $type = $atomlistref->[$i];
my $name;
if($IsPartial) {
$name = $type->Label();
}
else {
$name = $type->AtomNameOnly();
$name =~ s/([\d+-]+)//;
}
#print "name: $atomname <=> $name\n";
return $type if(uc $name eq uc $atomname);
}
return undef;
}
sub SplitAtomType
{
my ($this) = @_;
my @AtomTypeList = $this->GetCAtomTypeList();
my @AtomSiteList = $this->GetCAsymmetricAtomSiteList();
$this->ClearAtomTypeList();
$this->ClearAtomSiteList();
my %at;
#my $n = @AtomSiteList;
#print "nsite=$n\n";
for(my $i = 0 ; $i < @AtomSiteList ; $i++) {
my $site = $AtomSiteList[$i];
my $name = $site->AtomNameOnly();
my $label = $site->Label();
my $charge = $site->Charge();
my $occ = $site->Occupancy();
my ($x, $y, $z) = $site->Position();
my ($fx,$fy,$fz) = $site->Force();
$at{$name}++;
my $name2 = "$name\{" . Utils::IntToAlpha($at{$name} - 1) . "\}$charge";
#print "name2=$name2\n";
$this->AddAtomSite($label, $name2, $x, $y, $z, $occ, $fx, $fy, $fz);
# $this->AddAtomType($name2, 1);
#print "Site (indep type) $i: $name2 (Z=$charge) (occ=$occ): $key\n";
}
$this->ExpandCoordinates();
}
sub SplitPartialSites
{
my ($this) = @_;
my @AtomTypeList = $this->GetCAtomTypeList();
my @AtomSiteList = $this->GetCAsymmetricAtomSiteList();
# my @ExpandedAtomSiteList = $this->GetCExpandedAtomSiteList();
$this->ClearAtomTypeList();
$this->ClearAtomSiteList();
my %at;
for(my $i = 0 ; $i < @AtomSiteList ; $i++) {
my $site = $AtomSiteList[$i];
my $name = $site->AtomNameOnly();
my $label = $site->Label();
my $charge = $site->Charge();
my $occ = $site->Occupancy();
my ($x, $y, $z) = $site->Position();
my ($fx,$fy,$fz) = $site->Force();
my $key;
if(abs($occ - 1.0) < 1.0e-4) {
$key = "$name$charge";
}
else {
$key = sprintf("$name${charge}[%6.4f]", $occ);
}
$this->AddAtomSite($label, $key, $x, $y, $z, $occ, $fx, $fy, $fz);
next if($at{$key});
$at{$key}++;
$this->AddAtomType($key, 1);
print "Site (indep type) $i: $name (Z=$charge) (occ=$occ): $key\n";
}
my @ATL = $this->GetCAtomTypeList();
for(my $i = 0 ; $i < @ATL ; $i++) {
my $type = $ATL[$i];
my $nameraw = $type->Label();
my $name = $type->AtomNameOnly();
my $charge = $type->Charge();
my $occ = $type->Occupancy();
my $key = sprintf("$name${charge}[%6.4f]", $occ);
print "Type $i: {$nameraw} $name (Z=$charge) (occ=$occ): $key\n";
}
my @ASL = $this->GetCAsymmetricAtomSiteList();
for(my $i = 0 ; $i < @ASL ; $i++) {
my $site = $ASL[$i];
my $nameraw = $site->AtomName();
my $name = $site->AtomNameOnly();
my $charge = $site->Charge();
my $occ = $site->Occupancy();
my $key = sprintf("$name${charge}[%6.4f]", $occ);
print "c $i: {$nameraw}: $name (Z=$charge) (occ=$occ): $key\n";
}
$this->ExpandCoordinates();
#exit;
}
sub TotalCharge
{
my ($this) = @_;
$this->ExpandCoordinates();
my @ExpandedAtomSiteList = $this->GetCExpandedAtomSiteList();
my $nTotalExpandedAtomSite = $this->nTotalExpandedAtomSite();
my $Z = 0.0;
for(my $i = 0 ; $i < $nTotalExpandedAtomSite ; $i++) {
my $atom = $ExpandedAtomSiteList[$i];
my $charge = $atom->Charge();
my $occ = $atom->Occupancy();
$Z += $occ * $charge;
}
return $Z;
}
sub CheckTotalCharge
{
my ($this, $EPS) = @_;
$EPS = 1.0e-3 if($EPS eq '');
my $Z = $this->TotalCharge();
if(abs($Z) > $EPS) {
print "Error in CrystalObject::CheckTotalCharge: Total charge ($Z) is greater than EPS=$EPS.\n";
exit 0;
}
return 1;
}
sub GetCBravaisLatticeAsymmetricAtomSiteList
{
my ($this) = @_;
$this->ExpandCoordinates();
my $SPG = $this->GetCSpaceGroup();
my @ExpandedAtomSiteList = $this->GetCExpandedAtomSiteList();
my $nTotalExpandedAtomSite = $this->nTotalExpandedAtomSite();
my (@Tx, @Ty, @Tz);
my $nT = $SPG->nTranslation();
for(my $it = 1 ; $it <= $nT ; $it++) {
($Tx[$it-1], $Ty[$it-1], $Tz[$it-1]) = $SPG->TranslationVector($it);
}
my @AtomSite;
my $c = 0;
for(my $i = 0 ; $i < $nTotalExpandedAtomSite ; $i++) {
my $atom = $ExpandedAtomSiteList[$i];
my $label = $atom->Label();
my $atomname = $atom->AtomNameOnly();
my $charge = $atom->Charge();
my ($x,$y,$z) = $atom->Position();
my $occ = $atom->Occupancy();
#print "$i: $atomname $charge [$label]: ($x, $y, $z) [occ=$occ]\n";
my $IsExist = 0;
for(my $it = 1 ; $it < $nT ; $it++) {
my ($tx, $ty, $tz) = ($Tx[$it], $Ty[$it], $Tz[$it]);
my $x1 = Utils::Reduce01($x + $tx);
my $y1 = Utils::Reduce01($y + $ty);
my $z1 = Utils::Reduce01($z + $tz);
for(my $j = 0 ; $j < $c ; $j++) {
my $atom2 = $AtomSite[$j];
my ($x2, $y2, $z2) = $atom2->Position();
my $dis = $this->GetNearestInterAtomicDistance($x1, $y1, $z1, $x2, $y2, $z2);
#print "$i-$it-$j: ($x,$y,$z): ($x1,$y1,$z1)-($x2,$y2,$z2)=$dis\n";
if($dis < 1.0e-6) {
$IsExist = 1;
last;
}
}
last if($IsExist);
}
if(!$IsExist) {
$AtomSite[$c] = $atom;
$c++;
}
}
return @AtomSite;
}
sub ClearAtomSiteList
{
my ($this) = @_;
$this->{RefAsymmetricAtomSiteList} = [];
$this->{RefExpandedAtomSiteList} = [];
}
sub SetAtomSite
{
my ($this, $iatom, $label, $atomname, $x, $y, $z, $occ, $fx, $fy, $fz) = @_;
my $at = $this->{RefAsymmetricAtomSiteList}[$iatom];
return undef if(!$at);
$at->SetLabel($label) if(defined $label);
$at->SetAtomName($atomname) if(defined $atomname);
$at->SetPosition($x, $y, $z) if(defined $x);
$at->SetOccupancy($occ) if(defined $occ);
$at->SetForce($fx, $fy, $fz) if(defined $fz);
# $at->SetId(@$atomlistref + 1);
return $at;
}
sub AddAtomSite
{
my ($this, $label, $atomname, $x, $y, $z, $occ, $fx, $fy, $fz) = @_;
#print "AddAtomSite: F=$fx, $y, $fz\n";
# return $this->{nAsymmetricAtomSite} if($atomname eq '');
my $atomlistref = $this->{RefAsymmetricAtomSiteList};
if($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->{nAsymmetricAtomSite} = scalar @$atomlistref;
}
else {
my @atomlist;
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(1);
push(@atomlist, $at);
$this->{nAsymmetricAtomSite} = @atomlist;
$this->{RefAsymmetricAtomSiteList} = \@atomlist;
}
$this->AddAtomType($atomname, undef, $occ) if($atomname ne '');
return $this->{nAsymmetricAtomSite};
}
sub GetimaxByRmax
{
my ($this, $Rmax) = @_;
my ($a,$b,$c,$alpha,$beta,$gamma) = $this->LatticeParameters();
return (int($Rmax/$a)+1, int($Rmax/$b)+1, int($Rmax/$c)+1);
}
sub FindCoordinationsForAllAtoms
{
# $iTargetSites: 0: Expanded 1: Asymmetric sites
my ($Crystal, $iTargetSites, $CoordRMax, $IsPrint) = @_;
$IsPrint = 0 if(!defined $IsPrint);
my (@SiteList, $nSite);
if($iTargetSites == 0) {
@SiteList = $Crystal->GetCExpandedAtomSiteList();
}
else {
@SiteList = $Crystal->GetCAsymmetricAtomSiteList();
}
$nSite = @SiteList;
for(my $i = 0 ; $i < $nSite ; $i++) {
my $pAtom0 = $SiteList[$i];
my $label0 = $pAtom0->Label();
my $type0 = $pAtom0->AtomName();
my ($x0,$y0,$z0) = $pAtom0->Position(1);
# my $occupancy0 = $pAtom0->Occupancy();
# my $iAtomType0 = $Crystal->FindiAtomType($type);
my $id0 = ($iTargetSites == 0)? $i : $pAtom0->{IdAsymmetricAtomSite} - 1;
print "Coordination arround $i-th atom [$label0: $type0 (id=$id0)] ($x0, $y0, $z0)\n" if($IsPrint);
$pAtom0->{idSite}= ($iTargetSites == 0)? $i : $pAtom0->{IdAsymmetricAtomSite} - 1;
$Crystal->FindCoordinationsAroundAtom($pAtom0, $iTargetSites, $CoordRMax);
my $idSite0 = $pAtom0->{idSite};
my $pCoord = $pAtom0->{pCoordAtoms};
for(my $i = 0 ; $i < @$pCoord ; $i++) {
my $pCA = $pCoord->[$i];
my $iSite1 = $pCA->{iSite};
my $idSite1 = $pCA->{idSite};
my $label1 = $pCA->Label();
my $type1 = $pCA->AtomName();
my ($x1, $y1, $z1) = $pCA->Position();
my ($X1, $Y1, $Z1) = @{$pCA->{pCoord}};
my $r = $pCA->{r};
printf " %03d: [%3s: %4s (id = %03d)] (%8.4f, %8.4f, %8.4f): r = %8.4f frac:(%6.3f, %6.3f, %6.3f)\n",
$iSite1, $label1, $type1, $idSite1, $X1, $Y1, $Z1, $r, $x1, $y1, $z1 if($IsPrint);
}
}
return \@SiteList;
}
sub FindCoordinationsAroundAtom
{
my ($Crystal, $pAtom0, $iTargetSites, $CoordRMax) = @_;
my ($x0,$y0,$z0) = $pAtom0->Position(1);
my ($ixmax, $iymax, $izmax) = $Crystal->GetimaxByRmax($CoordRMax);
my @ExpandedAtomSiteList = $Crystal->GetCExpandedAtomSiteList();
my $nTotalExpandedAtomSite = $Crystal->nTotalExpandedAtomSite();
my @CoordAtoms;
my $c = 0;
for(my $i = 0 ; $i < $nTotalExpandedAtomSite ; $i++) {
my $pAtom1 = $ExpandedAtomSiteList[$i];
my $label1 = $pAtom1->Label();
my $type1 = $pAtom1->AtomName();
my $id1 = ($iTargetSites == 0)? $i : $pAtom1->{IdAsymmetricAtomSite} - 1;
my ($x1,$y1,$z1) = $pAtom1->Position(1);
my @dislist = $Crystal->GetNearestInterAtomicDistances(
$x0, $y0, $z0, $x1, $y1, $z1, $CoordRMax, 0, $ixmax, $iymax, $izmax);
my $p = $ExpandedAtomSiteList[$id1];
my $n1 = $pAtom1->AtomName();
my $n = $p->AtomName();
my $l = $p->Label();
if($n1 ne $n) {
print "Error: Atom name mismatch ($n1 ne $n) for ($label1 vs $l) (i=$i vs id=$id1)\n";
exit;
}
for(my $j = 0 ; $j < @dislist ; $j++) {
my $piCell = $dislist[$j]->{piCell};
my $r = $dislist[$j]->{r};
$CoordAtoms[$c] = new AtomSite;
%{$CoordAtoms[$c]} = %$pAtom1;
$CoordAtoms[$c]->{pSite} = $pAtom1;
$CoordAtoms[$c]->{iSite} = $i;
$CoordAtoms[$c]->{idSite} = $id1;
$CoordAtoms[$c]->{piCell} = $piCell;
$CoordAtoms[$c]->{pCoord} = [$x1 + $piCell->[0], $y1 + $piCell->[1], $z1 + $piCell->[2]];
$CoordAtoms[$c]->{r} = $r;
my $l = $CoordAtoms[$c];
#print " $i: [$label: $type (id = $id1)] ($l->{pCoord}[0], $l->{pCoord}[1], $l->{pCoord}[2]): r = $l->{r}\n";
$c++;
}
}
$pAtom0->{pCoordAtoms} = \@CoordAtoms;
return $pAtom0;
}
sub GetNearestInterAtomicDistances
{
my ($this, $x0, $y0, $z0, $x1, $y1, $z1, $CoordRMax, $AllowZero, $ixmax, $iymax, $izmax) = @_;
$AllowZero = 1 if(!defined $AllowZero);
if(!defined $ixmax) {
($ixmax, $iymax, $izmax) = $this->GetimaxByRmax($CoordRMax);
}
my @list;
my $c = 0;
for(my $ix = -$ixmax ; $ix <= $ixmax ; $ix++) {
for(my $iy = -$iymax ; $iy <= $iymax ; $iy++) {
for(my $iz = -$izmax ; $iz <= $izmax ; $iz++) {
my $r = $this->GetInterAtomicDistance($x0, $y0, $z0, $x1+$ix, $y1+$iy, $z1+$iz);
#print "$ix,$iy,$iz: r=$r\n";
if($r < $CoordRMax) {
next if(!$AllowZero and $r < 1.0e-5);
$list[$c++] = {
piCell => [$ix, $iy, $iz],
r => $r,
};
}
}
}
}
return @list;
}
sub GetNearestInterAtomicDistance
{
my ($this, $x0, $y0, $z0, $x1, $y1, $z1, $AllowZero) = @_;
$AllowZero = 1 if(!defined $AllowZero);
$x0 = $this->FindNearestEquivalentFractionCoordinate($x0, $x1);
$y0 = $this->FindNearestEquivalentFractionCoordinate($y0, $y1);
$z0 = $this->FindNearestEquivalentFractionCoordinate($z0, $z1);
my $r = $this->GetInterAtomicDistance($x0, $y0, $z0, $x1, $y1, $z1);
#print "r=$r\n";
if(!$AllowZero and $r <= 1.0e-5) {
$r = 1.0e10;
for(my $ix = -1 ; $ix <= 1 ; $ix++) {
for(my $iy = -1 ; $iy <= 1 ; $iy++) {
for(my $iz = -1 ; $iz <= 1 ; $iz++) {
my $r2 = $this->GetInterAtomicDistance($x0, $y0, $z0, $x1+$ix, $y1+$iy, $z1+$iz);
#print "$ix,$iy,$iz: r2=$r2\n";
$r = $r2 if($r2 > 1.0e-5 and $r > $r2);
}
}
}
}
return $r;
}
sub GetInterAtomicDistance
{
my ($this, $x0, $y0, $z0, $x1, $y1, $z1) = @_;;
my $dx = $x1 - $x0;
my $dy = $y1 - $y0;
my $dz = $z1 - $z0;
my $g11 = $this->{"g11"};
my $g22 = $this->{"g22"};
my $g33 = $this->{"g33"};
my $g12 = 2.0* $this->{"g12"};
my $g13 = 2.0* $this->{"g13"};
my $g23 = 2.0* $this->{"g23"};
my $r2 = $g11*$dx*$dx + $g22*$dy*$dy + $g33*$dz*$dz
+ $g12*$dx*$dy + $g13*$dx*$dz + $g23*$dy*$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 $g11 = $this->{"g11"};
my $g22 = $this->{"g22"};
my $g33 = $this->{"g33"};
my $g12 = 2.0* $this->{"g12"};
my $g13 = 2.0* $this->{"g13"};
my $g23 = 2.0* $this->{"g23"};
my $ip = $g11*$dx01*$dx02 + $g22*$dy01*$dy02 + $g33*$dz01*$dz02
+ $g12*$dx01*$dy02 + $g13*$dx01*$dz02
+ $g12*$dy01*$dx02 + $g23*$dy01*$dz02
+ $g13*$dz01*$dx02 + $g23*$dz01*$dy02;
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) = @_;
#print "F:$atomname\n";
$atomname =~ s/^(\D+)(\d.*)$/$1/;
$atomname = uc $atomname;
#print "F:$atomname\n";
my @AtomTypeList = $this->GetCAtomTypeList();
my $nAtomType = @AtomTypeList;
for(my $i = 0 ; $i < $nAtomType ; $i++) {
my $name = $AtomTypeList[$i]->AtomName();
$name =~ s/^(\D+)(\d.*)$/$1/;
#print "$i: $name\n";
return $i+1 if($atomname eq uc $name);
}
return -1;
}
sub CalCartesianCoordinate
{
my ($this, $x, $y, $z) = @_;
my ($ax, $ay, $az, $bx, $by, $bz, $cx, $cy, $cz) = $this->LatticeVectorsByOutputMode(0);
my $rx = $ax * $x + $bx * $y + $cx * $z;
my $ry = $ay * $x + $by * $y + $cy * $z;
my $rz = $az * $x + $bz * $y + $cz * $z;
return ($rx, $ry, $rz);
}
sub CalFractionalCoordinate
{
my ($this, $rx, $ry, $rz) = @_;
my ($ax, $ay, $az, $bx, $by, $bz, $cx, $cy, $cz) = $this->LatticeVectorsByOutputMode(0);
my $T = new Math::MatrixReal(3, 3);
$T->assign(1, 1, $ax);
$T->assign(1, 2, $bx);
$T->assign(1, 3, $cx);
$T->assign(2, 1, $ay);
$T->assign(2, 2, $by);
$T->assign(2, 3, $cy);
$T->assign(3, 1, $az);
$T->assign(3, 2, $bz);
$T->assign(3, 3, $cz);
my $RT = $T->inverse();
my $x = $RT->element(1,1) * $rx + $RT->element(1,2) * $ry + $RT->element(1,3) * $rz;
my $y = $RT->element(2,1) * $rx + $RT->element(2,2) * $ry + $RT->element(2,3) * $rz;
my $z = $RT->element(3,1) * $rx + $RT->element(3,2) * $ry + $RT->element(3,3) * $rz;
return ($x, $y, $z);
}
sub FindIdenticalSite
{
my ($this, $atomname, $x, $y, $z, $TolD, $iSiteStart) = @_;
$TolD = 0.01 if(!defined $TolD);
$iSiteStart = 0 if(!defined $iSiteStart);
$atomname =~ s/\d[\+\-]?//;
my @AtomSiteList = $this->GetCExpandedAtomSiteList();
my $nSite = @AtomSiteList;
for(my $ia = $iSiteStart ; $ia < $nSite ; $ia++) {
my $site = $AtomSiteList[$ia];
my $name = $site->AtomNameOnly();
next if(uc $name ne uc $atomname);
my ($x1, $y1, $z1) = $site->Position();
my $dis = $this->GetNearestInterAtomicDistance($x, $y, $z, $x1, $y1, $z1);
return $ia if($dis < $TolD);
}
return undef;
}
sub FindIdenticalAsymmetricSite
{
my ($this, $atomname, $x, $y, $z) = @_;
$atomname =~ s/\d[\+\-]?//;
my @AtomSiteList = $this->GetCAsymmetricAtomSiteList();
my $nAsymmetricAtomSite = @AtomSiteList;
for(my $ia = 0 ; $ia < $nAsymmetricAtomSite ; $ia++) {
my $site = $AtomSiteList[$ia];
my $name = $site->AtomNameOnly();
next if(uc $name ne uc $atomname);
my ($x1, $y1, $z1) = $site->Position();
my $dis = $this->GetNearestInterAtomicDistance($x, $y, $z, $x1, $y1, $z1);
return $ia if($dis < 0.01);
}
return undef;
}
sub ExpandCoordinates
{
my ($this, $DoTranslation) = @_;
$DoTranslation = 1 if(!defined $DoTranslation);
#初期化
$this->{nTotalExpandedAtomSite} = 0;
$this->{Density} = 0.0;
#同一原子の判断半径を決める
my ($a, $b, $c) = $this->LatticeParameters();
$a = $b if($a < $b);
$a = $c if($a < $c);
my $OverlapCriteriaRadius = 0.001 * $a;
#非対称構造中のパラメータ
my @AtomTypeList = $this->GetCAtomTypeList();
my $nAtomType = @AtomTypeList;
my @AtomSiteList = $this->GetCAsymmetricAtomSiteList();
my $nAsymmetricAtomSite = @AtomSiteList;
my $SPG = $this->GetCSpaceGroup();
my $nTranslation = $SPG->nTranslation();
$nTranslation = 1 if(!$DoTranslation);
my $nSymmetryOperation = $SPG->nSymmetryOperation();
my $nTotalSymmetry = $nTranslation * $nSymmetryOperation;
#print "nTotalSymmetry: $nTotalSymmetry\n";
#座標展開を開始
my @ExpandedAtomSiteList;
my @nExpandedAtomSite;
my $count = 0;
#print "nAAS=$nAsymmetricAtomSite\n";
for(my $ia = 0 ; $ia < $nAsymmetricAtomSite ; $ia++) {
$nExpandedAtomSite[$ia] = 0;
my $atom = $AtomSiteList[$ia];
my $label = $atom->Label();
my $AtomName = $atom->AtomName();
my $iAtomType = $this->FindiAtomType($AtomName);
my ($x,$y,$z) = $atom->Position(1);
my ($fx,$fy,$fz) = $atom->Force();
$x = CrystalObject::RoundSymmetricPosition($x);
$y = CrystalObject::RoundSymmetricPosition($y);
$z = CrystalObject::RoundSymmetricPosition($z);
my $Occupancy = $atom->Occupancy();
my ($vx,$vy,$vz) = $atom->Velocity();
$atom->SetId($ia+1);
$ExpandedAtomSiteList[$count] = new AtomSite();
$ExpandedAtomSiteList[$count]->SetIdAsymmetricAtomSite($ia+1);
$ExpandedAtomSiteList[$count]->SetiAtomType($iAtomType);
$ExpandedAtomSiteList[$count]->SetLabel($label);
$ExpandedAtomSiteList[$count]->SetAtomName($AtomName);
$ExpandedAtomSiteList[$count]->SetPosition($x,$y,$z);
$ExpandedAtomSiteList[$count]->SetForce($fx,$fy,$fz) if(defined $fx);
$ExpandedAtomSiteList[$count]->SetOccupancy($Occupancy);
$ExpandedAtomSiteList[$count]->SetVelocity($vx,$vy,$vz) if(defined $vx);
$nExpandedAtomSite[$ia] += 1;
$count++;
#print "($ia) x,y,z: $x,$y,$z
\n";
for(my $is = 0 ; $is < $nTotalSymmetry ; $is++) {
my ($xs, $ys, $zs) = $SPG->DoSymmetryOperation($is, $x, $y, $z, 1);
#print " ($is) xs,ys,zs: $xs,$ys,$zs
\n";
my ($fxs, $fys, $fzs) = $SPG->DoVelocitySymmetryOperation($is, $fx, $fy, $fz, 1);
my ($vxs, $vys, $vzs) = $SPG->DoVelocitySymmetryOperation($is, $vx, $vy, $vz, 1);
my $IsPassed = 1;
for(my $it = 0 ; $it < $count ; $it++) {
my $iat = $ExpandedAtomSiteList[$it]->iAtomType();
next if($iat != $iAtomType);
my ($x0,$y0,$z0) = $ExpandedAtomSiteList[$it]->Position(1);
my $dis = $this->GetNearestInterAtomicDistance(
$x0, $y0, $z0, $xs, $ys, $zs);
#print "$label(ia=$ia:is=$is:it=$it): ";
#printf "(%7.4f,%7.4f,%7.4f)-(%7.4f,%7.4f,%7.4f): dis: %f (%f)
\n",
# $x0, $y0, $z0, $xs, $ys, $zs, $dis, $OverlapCriteriaRadius;
if($dis < $OverlapCriteriaRadius) {
$IsPassed = 0;
last;
}
}
next unless($IsPassed);
$ExpandedAtomSiteList[$count] = new AtomSite();
$ExpandedAtomSiteList[$count]->SetIdAsymmetricAtomSite($ia+1);
$ExpandedAtomSiteList[$count]->SetiAtomType($iAtomType);
$ExpandedAtomSiteList[$count]->SetLabel($label);
$ExpandedAtomSiteList[$count]->SetAtomName($AtomName);
$ExpandedAtomSiteList[$count]->SetPosition($xs,$ys,$zs);
$ExpandedAtomSiteList[$count]->SetForce($fxs,$fys,$fzs);
$ExpandedAtomSiteList[$count]->SetOccupancy($Occupancy);
$ExpandedAtomSiteList[$count]->SetVelocity($vxs,$vys,$vzs);
#Idは1からはじまる
$ExpandedAtomSiteList[$count]->SetId($ia+1);
#print "v: ($vx,$vy,$vz) ($vxs, $vys, $vzs)
\n";
$nExpandedAtomSite[$ia] += 1;
$count++;
}
}
$this->{nTotalExpandedAtomSite} = $count;
$this->{RefExpandedAtomSiteList} = \@ExpandedAtomSiteList;
$this->{RefnExpandedAtomSite} = \@nExpandedAtomSite;
#多重度を、非対称原子に設定
for(my $ia = 0 ; $ia < @AtomSiteList ; $ia++) {
my $atom = $AtomSiteList[$ia];
#print "atom: ($ia): m=$nExpandedAtomSite[$ia]
\n";
$atom->SetMultiplicity($nExpandedAtomSite[$ia]);
}
#多重度を、全原子に設定
for(my $i = 0 ; $i < @ExpandedAtomSiteList ; $i++) {
my $atom = $ExpandedAtomSiteList[$i];
my $ia = $atom->Id() - 1;
#print "atom: ($i,$ia): m=$nExpandedAtomSite[$ia]
\n";
$atom->SetMultiplicity($nExpandedAtomSite[$ia]);
}
my $Density = $this->CalculateDensity();
$this->{Density} = $Density;
$this->AnalyzeChemicalComposition();
return;
}
sub AnalyzeChemicalComposition
{
my ($this) = @_;
my @ExpandedAtomSiteList = $this->GetCExpandedAtomSiteList();
my %AtomCount;
for(my $i = 0 ; $i < @ExpandedAtomSiteList ; $i++) {
my $site = $ExpandedAtomSiteList[$i];
my $name = $site->AtomNameOnly(1);
# $name =~ s/\{[^\{\}]*\}//g;
$AtomCount{$name} += $site->Occupancy();
#print "$i: $name [$AtomCount{$name}]\n";
}
my @Names = keys %AtomCount;
foreach my $key (@Names) {
$AtomCount{$key} = Utils::RoundParameter($AtomCount{$key}, 1.0e-4);
}
my $Composition = '';
my $MinN = 1000000;
for(my $i = 0 ; $i < @Names ; $i++) {
my $name = $Names[$i];
next if($name =~ /^(O|S|Se|Te|F|Cl|I|N|As)$/i);
my $n = $AtomCount{$name};
$MinN = $n if($MinN > $n);
$n = '' if($n == 1);
$Composition = "$Composition $name$n";
#print "i=$i: [$n] [$MinN] [$name]\n";
}
for(my $i = 0 ; $i < @Names ; $i++) {
my $name = $Names[$i];
next unless($name =~ /^(O|S|Se|Te|F|Cl|I|N|As)$/i);
my $n = $AtomCount{$name};
$MinN = $n if($MinN > $n);
$n = '' if($n == 1);
$Composition = "$Composition $name$n";
}
$Composition =~ s/^\s*//;
$this->SetSumChemicalComposition($Composition);
my @elements = Algorism::Factorize($MinN);
my $fac = 1;
#print "$MinN = ";
for(my $i = 0 ; $i < @elements ; $i++) {
my $num = $elements[$i];
#print " * " if($i > 0);
#print "$num";
my $IsCommonFactor = 1;
for(my $j = 0 ; $j < @Names ; $j++) {
my $name = $Names[$j];
my $n = $AtomCount{$name};
unless(Algorism::IsFactor($n, $num*$fac)) {
$IsCommonFactor = 0;
}
}
$fac *= $num if($IsCommonFactor);
#print "fac[$i]=$fac\n";
}
#print "\n";
$this->SetFormulaUnit($fac);
#print "Z = $fac\n";
$Composition = '';
for(my $i = 0 ; $i < @Names ; $i++) {
my $name = $Names[$i];
next if($name =~ /^(O|S|Se|Te|F|Cl|I|N|As|)$/i);
my $n = $AtomCount{$name} / $fac;
$n = '' if($n == 1);
$Composition = "$Composition $name$n";
}
for(my $i = 0 ; $i < @Names ; $i++) {
my $name = $Names[$i];
next unless($name =~ /^(O|S|Se|Te|F|Cl|I|N|As|)$/i);
my $n = $AtomCount{$name} / $fac;
$n = '' if($n == 1);
$Composition = "$Composition $name$n";
}
$Composition =~ s/^\s*//;
$this->SetChemicalComposition($Composition);
}
sub CalculateDensity
{
my ($this) = @_;
my $vol = $this->Volume();
# return $this->{Density} if($vol == 0.0);
my $Density = 0.0;
my @AtomTypeList = $this->GetCAtomTypeList();
my @AtomSiteList = $this->GetCAsymmetricAtomSiteList();
my $nAsymmetricAtomSite = $this->{'nAsymmetricAtomSite'};
#my $n = @AtomTypeList;
#print "n=$n, $nAsymmetricAtomSite\n";
my @nMultiplicityExpandedAtomSiteList
= $this->GetCnMultiplicityExpandedAtomSiteList();
for(my $i = 0 ; $i < $nAsymmetricAtomSite ; $i++) {
my $AtomSite = $AtomSiteList[$i];
my $AtomName = $AtomSite->AtomNameOnly(0);
# my $AtomName = $AtomSite->AtomNameOnly(1);
#print "an=$AtomName i=$i\n";
my $iAtomType = $this->FindiAtomType($AtomName);
if($iAtomType < 0) {
print "Error in CrystalObject::CalculateDensity: Invalid iAtomType=$iAtomType\n";
exit;
}
#print "iat=$iAtomType\n";
my $AtomType = $AtomTypeList[$iAtomType-1];
my $occupancy = $AtomSite->Occupancy();
#print "i=$i: $AtomSite, $AtomName, $iAtomType, $AtomType, $occupancy\n";
my $AtomicMass = $AtomType->AtomicMass();
my $mult = $nMultiplicityExpandedAtomSiteList[$i];
#print "$i: $AtomName ($iAtomType): $AtomicMass\n";
#print "$i: $AtomName ($AtomicMass) [$mult]\n";
$AtomicMass = 0.0 if(not defined $AtomicMass);
$occupancy = 1.0 if(not defined $occupancy);
$mult = 1.0 if(not defined $mult);
$Density += $AtomicMass*$mult*$occupancy;
}
my $NA = Sci::NA(); #6.0221367e23;
#print "vol: $vol NA: $NA\n";
$this->{"Density"} = ($vol == 0)? 0.0 : Sci::Round($Density / $NA / $vol * 1.0e24, 4);
return $this->{"Density"};
}
sub SetDensity
{
my ($this, $density) = @_;
return $this->{"Density"} = $density;
}
sub Density
{
my ($this) = @_;
return $this->{"Density"};
}
sub FindMass
{
my ($this, $atomname) = @_;
my @AtomTypeList = $this->GetCAtomTypeList();
for(my $i = 0 ; $i < @AtomTypeList ; $i++) {
my $atom = $AtomTypeList[$i];
my $name = $atom->AtomNameOnly();
next if(uc $name ne uc $atomname);
return $atom->AtomicMass();
}
return undef;
}
sub ConvertLattice
{
my ($this, $T, $LatticeParameterOnly, $CheckConsistency, $IsPrint) = @_;
my $nTotalExpandedAtomSite = $this->nTotalExpandedAtomSite();
my $SPG = $this->GetCSpaceGroup();
my $SPGName = $SPG->SPGName();
my ($a, $b, $c, $alpha, $beta, $gamma) = $this->LatticeParameters();
#実空間座標(x,y,z)の変換行列
my $tRT = new Math::MatrixReal(3, 3);
$tRT->transpose($T);
$tRT = $tRT->inverse();
my $g = new Math::MatrixReal(3, 3);
$g->assign(1, 1, $this->{'g11'});
$g->assign(1, 2, $this->{'g12'});
$g->assign(1, 3, $this->{'g13'});
$g->assign(2, 1, $this->{'g21'});
$g->assign(2, 2, $this->{'g22'});
$g->assign(2, 3, $this->{'g23'});
$g->assign(3, 1, $this->{'g31'});
$g->assign(3, 2, $this->{'g32'});
$g->assign(3, 3, $this->{'g33'});
my $g2 = new Math::MatrixReal(3, 3);
if(0) {
# if($SPGName =~ /^\s*[FIABC]/i) {
for(my $i = 1 ; $i <= 3 ; $i++) {
for(my $j = 1 ; $j <= 3 ; $j++) {
my $tij = $T->element($i,$j);
#print "$i,$j: $tij\n";
$g2->assign($i, $j, $tij);
}
}
}
else {
for(my $i1 = 1 ; $i1 <= 3 ; $i1++) {
for(my $i2 = 1 ; $i2 <= 3 ; $i2++) {
my $val = 0.0;
for(my $j1 = 1 ; $j1 <= 3 ; $j1++) {
for(my $j2 = 1 ; $j2 <= 3 ; $j2++) {
$val += $g->element($j1,$j2)
* $T->element($i1,$j1)
* $T->element($i2,$j2);
}
}
#print "val: $i1,$i2: $val\n";
$g2->assign($i1, $i2, $val);
}
}
}
#print "SPGName:$SPGName\n";
$this->{ConventionalSPGName} = $SPGName;
$this->{ConventionalLatticeParameters} = [$a, $b, $c, $alpha, $beta, $gamma];
print "***ConvL:$a, $b, $c, $alpha, $beta, $gamma\n";
my $NewCrystal = new Crystal();
$NewCrystal->{ConventionalSPGName} = $SPGName;
$NewCrystal->{ConventionalLatticeParameters} = [$a, $b, $c, $alpha, $beta, $gamma];
$NewCrystal->SetSpaceGroup("P 1", 1);
$NewCrystal->SetMetrics(
$g2->element(1,1), $g2->element(1,2), $g2->element(1,3),
$g2->element(2,1), $g2->element(2,2), $g2->element(2,3),
$g2->element(3,1), $g2->element(3,2), $g2->element(3,3));
$NewCrystal->CalLatticeParametersFromMetrics();
# Repeat to get proper lattice vectors for Primitive cell reduced by conventional cell
$NewCrystal->CalcMetrics();
#return $NewCrystal;
#my ($a2, $b2, $c2, $alpha2, $beta2, $gamma2) = $NewCrystal->LatticeParameters();
#die "**a'=$a2 b'=$b2 c'=$c2 alpha'=$alpha2 beta'=$beta2 gamma'=$gamma2\n";
return $NewCrystal if($LatticeParameterOnly);
$this->ExpandCoordinates();
my $amin = 0;
my $amax = 0;
for(my $i = 1 ; $i <= 3 ; $i++) {
my $e = $T->element($i,1);
$amin = int($e-0.999) if($e < $amin);
$amax = int($e-0.001) if($e > $amax);
}
my $bmin = 0;
my $bmax = 0;
for(my $i = 1 ; $i <= 3 ; $i++) {
my $e = $T->element($i,2);
$bmin = int($e-0.999) if($e < $bmin);
$bmax = int($e-0.001) if($e > $bmax);
}
my $cmin = 0;
my $cmax = 0;
for(my $i = 1 ; $i <= 3 ; $i++) {
my $e = $T->element($i,3);
$cmin = int($e-0.999) if($e < $cmin);
$cmax = int($e-0.001) if($e > $cmax);
}
if($CheckConsistency) {
$amax++; $bmax++; $cmax++;
}
#$bmin = 0;
print "Expansion range: ($amin,$bmin,$cmin) - ($amax,$bmax,$cmax)
\n" if($IsPrint);
my @ExpandedAtomSiteList = $this->GetCExpandedAtomSiteList();
$nTotalExpandedAtomSite = $this->nTotalExpandedAtomSite();
my $count = 0;
my %AtomTypeCount;
for(my $i = 0 ; $i < $nTotalExpandedAtomSite ; $i++) {
my $atom = $ExpandedAtomSiteList[$i];
my $atomnameraw = $atom->AtomName();
my $atomname = $atom->AtomNameOnly();
my $charge = $atom->Charge();
my ($x,$y,$z) = $atom->Position(1);
my $occ = $atom->Occupancy();
my $key;
if(abs($occ - 1.0) < 1.0e-4) {
$key = "$atomname$charge";
}
else {
$key = sprintf("$atomname${charge}[%6.4f]", $occ);
}
#print "i=$i atom=$atomname: key-name: $key : $atomnameraw\n";
for(my $ia = $amin ; $ia <= $amax ; $ia++) {
for(my $ib = $bmin ; $ib <= $bmax ; $ib++) {
for(my $ic = $cmin ; $ic <= $cmax ; $ic++) {
#print("ia,b,c=", $ia, $ib, $ic);
my $xyz = new Math::MatrixReal(3, 1);
$xyz->assign(1,1, $x+$ia);
$xyz->assign(2,1, $y+$ib);
$xyz->assign(3,1, $z+$ic);
$xyz = $tRT * $xyz;
my $x1 = Utils::Reduce01($xyz->element(1,1));
my $y1 = Utils::Reduce01($xyz->element(2,1));
my $z1 = Utils::Reduce01($xyz->element(3,1));
#print " (", $x+$ia, ", ", $y+$ib, ", ", $z+$ic, ") - ($x1,$y1,$z1)
\n";
my $WillAdd = 1;
my $MinimumDistance = 0.1;
if($NewCrystal->nAsymmetricAtomSite() == 0) {
$AtomTypeCount{$atomname}++;
my $label = $atomname . $AtomTypeCount{$atomname};
$NewCrystal->AddAtomSite($label, $key, $x1, $y1, $z1, $occ);
# $NewCrystal->AddAtomSite($label, $atomname, $x1, $y1, $z1, $occ);
#print " Passed0: $label: $key: ($x1,$y1,$z1) occ=$occ
\n";
next;
}
my @AtomList = $NewCrystal->GetCAsymmetricAtomSiteList();
my $nAtom = @AtomList;
#print(" *** nAtom=$nAtom\n");
for(my $j = 0 ; $j < @AtomList ; $j++) {
#print("j=$j\n");
my $atom0 = $AtomList[$j];
# my $atom0 = $NewCrystal->GetCAtomSite($j);
my $atomnameraw0 = $atom0->AtomName();
my $atomname0 = $atom0->AtomNameOnly();
my $charge0 = $atom0->Charge();
my ($x0,$y0,$z0) = $atom0->Position(1);
my $occ0 = $atom0->Occupancy();
#print("atom: $atomnameraw0 [Z=$charge0]\n");
#print " is=18 $atomnameraw0 $atomname0\n" if($i == 18);
my $key0;
if(abs($occ0 - 1.0) < 1.0e-4) {
$key0 = "$atomname0$charge0";
}
else {
$key0 = sprintf("$atomname0${charge0}[%6.4f]", $occ0);
}
#print("key: $key ne $key0?\n");
#print " keys(is=$i iatom=$j ($ia,$ib,$ic)): $key $key0\n" if($key =~ /Si/);
next if($key ne $key0);
# next if($atomname ne $atomname0);
my $dis = $NewCrystal->GetNearestInterAtomicDistance(
$x0, $y0, $z0, $x1, $y1, $z1);
#print " $nAtom: ($i,$j): ($ia,$ib,$ic): ($x0,$y0,$z0)-($x1,$y1,$z1) ";
#print " dis: $dis
minR=$MinimumDistance\n";
if($dis < $MinimumDistance) {
#print " Dropped: : $key: ($x1,$y1,$z1)-($x0,$y0,$z0) occ=$occ
\n";
$WillAdd = 0;
last;
}
}
if($WillAdd) {
$AtomTypeCount{$atomname}++;
my $label = $atomname . $AtomTypeCount{$atomname};
#print " Passed: $label: $key: ($x1,$y1,$z1) occ=$occ
\n";
$NewCrystal->AddAtomSite($label, $key, $x1, $y1, $z1, $occ);
# $NewCrystal->AddAtomSite($label, $atomname, $x1, $y1, $z1, $occ);
}
}
}
}
$count++;
}
$NewCrystal->ExpandCoordinates();
return $NewCrystal;
}
sub SetIsSelected
{
my($this, $index, $IsSelected) = @_;
my $RefAtomList = $this->{"RefExpandedAtomSiteList"};
my $atom = $RefAtomList->[$index];
if($atom ne '') {
return $atom->SetIsSelected($IsSelected);
}
return 0;
}
sub IsSelected
{
my($this, $index) = @_;
my $RefAtomList = $this->{"RefExpandedAtomSiteList"};
my $atom = $RefAtomList->[$index];
if($atom ne '') {
return $atom->IsSelected();
}
return 0;
}
sub SameIdSiteIndex
{
my ($this, $idm1, $sid) = @_;
my $SameIdSiteIndex = $this->{'SameIdSiteIndex'};
return $SameIdSiteIndex->{"[$idm1][$sid]"};
}
sub SortAtomTypeOrder
{
my ($this, $sorttype) = @_;
#print "