![]() |
Новосибирский институт органической химии им. Н.Н. Ворожцова СО РАН Лаборатория изучения механизмов органических реакций
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
rotepi#!/usr/bin/perl -ws #use Data::Dump 'pp'; #$Data::Dump::LINEWIDTH = 80; our ($h,$help,$rot,$epi,$angle,$reflect,$xtb); if ($h || $help) { (my $program = $0) =~ s/^.*[\/\\]//; print <<HELP; Usage: $program -rot=1,2 -angle=90 file.xyz Rotates a fragment of a molecule in xyz format around a given bond. Dependencies: Grimme's xtb (for -xtb option) Options: -rot=i,j,k atomic numbers i and j defined the rotatable bond. The bond must not be part of a cycle. k is optional and may be found automatically. -rot does not work when j and k are in the same cycle. -angle rotation angle in degrees (default 180). -reflect reflection of transformed part about ijk plane Set -angle=0 for only reflection -epi=i,j,k epimerization about i (rotation of a fragment including atoms j and k around the bissector of j-i-k angle. j and k are optional and may be found automatically. -angle and -reflect work for -epi too. -xtb=n Rough optimization with xtb (for linux). This option is useful if the transformation of the molecule results in very close atoms (there will be a warning about this). \'-xtb\' is \'-xtb=10\' (10 optimization steps). HELP exit; } # Ковалентные радиусы из Dalton Trans., 2008, 2832-2838 my %radius = qw( H 0.31 He 0.28 Li 1.28 Be 0.96 B 0.84 C 0.73 N 0.71 O 0.66 F 0.57 Ne 0.58 Na 1.66 Mg 1.41 Al 1.21 Si 1.11 P 1.07 S 1.05 Cl 1.02 Ar 1.06 K 2.03 Ca 1.76 Sc 1.70 Ti 1.60 V 1.53 Cr 1.39 Mn 1.50 Fe 1.44 Co 1.38 Ni 1.24 Cu 1.32 Zn 1.22 Ga 1.22 Ge 1.20 As 1.19 Se 1.20 Br 1.20 Kr 1.16 Rb 2.20 Sr 1.95 Y 1.90 Zr 1.75 Nb 1.64 Mo 1.54 Tc 1.47 Ru 1.46 Rh 1.42 Pd 1.39 Ag 1.45 Cd 1.44 In 1.42 Sn 1.39 Sb 1.39 Te 1.38 I 1.39 Xe 1.40 Cs 2.44 Ba 2.15 La 2.07 Ce 2.04 Pr 2.03 Nd 2.01 Pm 1.99 Sm 1.98 Eu 1.98 Gd 1.96 Tb 1.94 Dy 1.92 Ho 1.92 Er 1.89 Tm 1.90 Yb 1.87 Lu 1.87 Hf 1.75 Ta 1.70 W 1.62 Re 1.51 Os 1.44 Ir 1.41 Pt 1.36 Au 1.36 Hg 1.32 Tl 1.45 Pb 1.46 Bi 1.48 Po 1.40 At 1.50 Rn 1.50 Fr 2.60 Ra 2.21 Ac 2.15 Th 2.06 Pa 2.00 U 1.96 Np 1.90 Pu 1.87 Am 1.80 Cm 1.69 ); #my $ij = shift; die "You must give -rot=i,j or -epi=i,j,k option\n" unless $rot or $epi; die "Inconsistent options -rot and -epi\n" if $rot and $epi; $angle = 180 unless defined $angle; my @mols = read_molden(); my $xtb_x; if ($xtb) { $xtb_x = `which xtb`; chomp $xtb_x; if (-x $xtb_x) { $xtb = 10 if $xtb==1; open L, '>', "tmp_xtb.inp" or die "Can't write to tmp_xtb.inp :$!\n"; print L "\$opt\n maxcycle=$xtb\n"; close L; } else { warn "No xtb found\n"; undef $xtb; } } MOL: foreach my $mol (@mols) { do {warn "less then 4 atoms\n"; next} if @$mol<5; my ($topo,%bonds) = find_bonds($mol); #pp $topo; pp %bonds; exit; my %oldbonds = copy_hh(%bonds); #$topo =~ s/;/\n/g; print "$topo\n"; if ($xtb) { write_molden($mol, "tmp_old.xyz"); `$xtb_x topo tmp_old.xyz > /dev/null 2>&1`; } ####################### Rotation ########################### if ($rot) { # Rotatable bonds my ($i,$j,$k) = split m/\D+/, $rot; die "Invalid -rot=i,j option\n" unless $i && $j; my @rot_bonds; my %bonds_rot = copy_hh(%bonds); foreach (keys %bonds) { my @ar = keys %{$bonds{$_}}; if (@ar==1) { delete $bonds_rot{$_}{$ar[0]}; delete $bonds_rot{$ar[0]}{$_}; } } foreach my $i (sort {$a<=>$b} keys %bonds_rot) { foreach my $j (sort {$a<=>$b} keys %{$bonds_rot{$i}}) { if ($i > $j) { delete $bonds_rot{$i}{$j}; next; } if (part($i,$j,%bonds)==@$mol-1) { delete $bonds_rot{$i}{$j}; next; } #print "$i-$j\n"; push @rot_bonds, "$i-$j"; } } if (! exists $bonds{$i}{$j}) { warn "Can not rotate: $i-$j does not appear to be a bond.\n"; warn "Rotatable bonds: @rot_bonds\n"; next; } my @part_j = part($i,$j,%bonds); my @part_i = part($j,$i,%bonds); #print "@part_j\n@part_i\n"; if (@part_j==@$mol-1) { warn "Can not rotate: bond $i-$j seems to be part of a cycle.\n"; warn "Rotatable bonds: @rot_bonds\n"; next; } unless ($k) { foreach (sort {$a<=>$b} keys %{$bonds{$j}}) { next if $_==$i; $k = $_; last; } } #print "$i,$j,$k\n"; re_orientation($mol,$i,$j,$k); #write_molden($mol); my $mol_j; $mol_j->[0] = {}; foreach (@part_j) { push @$mol_j, $mol->[$_]; $mol->[$_][3] *= -1 if $reflect; } rot_x($mol_j,$angle); # Ссылки в $mol_j - из $mol, поэтому поворот и в $mol тоже } ####################### Epimerizaion ########################### elsif ($epi) { my ($i,$j,$k) = split m/[,-]/, $epi; do {warn "Invalid -epi=$epi option\n"; next} if $i && $j && !$k; my $N = @$mol-1; if ($i && !$j && !$k) { # Only one atom is given my @near = find_near($mol,$i); #print "@near\n"; exit; my @jk; foreach (@near) { if (part($i,$_,%bonds)<$N) { push @jk, $_; } } #print "@jk\n"; exit; if (@jk>=2) { ($j,$k) = @jk; } else { # Try spiro SPIRO: for (my $n=0; $n<@near; $n++) { my $jj = $near[$n]; for (my $m=$n+1; $m<@near; $m++) { my $kk = $near[$m]; my %bbb = copy_hh(%bonds); delete $bbb{$i}{$kk}; delete $bbb{$kk}{$i}; my @part_jk = part($i,$jj,%bbb); if (@part_jk<$N && grep {m/^$kk$/} @part_jk) { ($j,$k) = ($jj,$kk); last SPIRO; } else { warn "Can't find j and k for i=$i\n"; next MOL; } } } } } #warn "$i,$j,$k\n"; if (! exists $bonds{$i}{$j}) { warn "$i-$j does not appear to be a bond.\n"; next; } if (! exists $bonds{$i}{$k}) { warn "$i-$k does not appear to be a bond.\n"; next; } my @part_j = part($i,$j,%bonds); #pp @part_j; my @part_k = part($i,$k,%bonds); #print "@part_j\n@part_k\n"; my @part_jk; if (@part_j<$N && @part_k<$N) { # Both i-j and i-k start open-chains push @part_jk, @part_j, @part_k; } elsif (@part_j==$N && @part_k==$N) { # Both i-j and i-k are in the same cycle - try spiro my %bbb = copy_hh(%bonds); delete $bbb{$i}{$k}; delete $bbb{$k}{$i}; @part_jk = part($i,$j,%bbb); unless (@part_jk<$N && grep {m/^$k$/} @part_jk) { warn "Unsuitable topology for -epi=$epi\n"; next MOL; } } else { warn "Unsuitable topology for -epi=$epi\n"; next MOL; } #print "@part_jk\n"; # XX on the bisector of the angle j-i-k re_orientation($mol,$i,$j,$k); my $xj = $mol->[$j][1]; my $xk = $mol->[$k][1]; my $yk = $mol->[$k][2]; # unit segment on i-j my ($x1j,$y1j) = (1,0); # unit segment on i-k my $x1k = 1/sqrt(1+($yk/$xk)**2); $x1k *= -1 if $xk < 0; my $y1k = $x1k*$yk/$xk; my $x = ($x1j+$x1k)/2; my $y = ($y1j+$y1k)/2; my $XX = ['XX', $x, $y, 0 ]; # # XX at the middle of j,k # my $XX = ['XX', ($mol->[$j][1] + $mol->[$k][1])/2, # ($mol->[$j][2] + $mol->[$k][2])/2, # ($mol->[$j][3] + $mol->[$k][3])/2 ]; push @$mol, $XX; #write_molden($mol); exit; re_orientation($mol,$i,@$mol-1,$j); my $mol_jk; $mol_jk->[0] = {}; foreach (@part_jk) { push @$mol_jk, $mol->[$_]; $mol->[$_][3] *= -1 if $reflect; } rot_x($mol_jk,$angle); # Ссылки в $mol_j - из $mol, поэтому поворот и в $mol тоже pop @$mol; } ############################################################# foreach (keys %{$mol->[0]}) { if ($_ ne 'Charge' or $_ ne 'Mult') { delete $mol->[0]{$_}; } } write_molden($mol) unless $xtb; (my $new_topo,%new_bonds) = find_bonds($mol); my @overlaps; foreach my $i (keys %new_bonds) { foreach my $j (keys %{$new_bonds{$i}}) { next if $i > $j; next if exists $oldbonds{$i}{$j}; push @overlaps, sprintf("%s %.2f","$mol->[$i][0]$i-$mol->[$j][0]$j",$new_bonds{$i}{$j}); } } if (@overlaps) { warn "New short distances: ", join(', ',@overlaps),"\n"; } # if ($new_topo ne $topo) { # warn "Topology has changed!\nOld: $topo\nNew: $new_topo\n"; # } if ($xtb) { write_molden($mol, "tmp_new.xyz"); `$xtb_x tmp_new.xyz --gff --silent --opt -I tmp_xtb.inp`; (my $xtb_mol) = read_molden('xtbopt.xyz'); #pp $xtb_mol; $xtb_mol->[0] = {}; $xtb_mol->[0]{Charge} = $mol->[0]{Charge} if exists $mol->[0]{Charge}; $xtb_mol->[0]{Mult} = $mol->[0]{Mult} if exists $mol->[0]{Mult}; write_molden($xtb_mol); # my @args = ($xtb_x, "tmp_new.xyz", '--gff', '--silent', '--opt', '-I', 'tmp_xtb.inp'); # #print "Run `@args`\n"; # system(@args) == 0 or die "System `@args` failed: $?\n"; } } foreach (qw/gfnff_charges gfnff_topo tmp_new.xyz tmp_old.xyz tmp_xtb.inp xtbopt.log xtbopt.xyz .xtboptok/) { -e && unlink; } sub find_bonds { # External hash %radius (Ковалентные радиусы из Dalton Trans., 2008, 2832-2838) my $mol = shift; my %bonds; my $mult = 1.3; for (my $i=1; $i<@$mol; $i++) { for (my $j=$i+1; $j<@$mol; $j++) { my $dist = dist($mol,$i,$j); if ($dist < ($radius{$mol->[$i][0]}+$radius{$mol->[$j][0]})*$mult) { $bonds{$i}{$j} = $dist; $bonds{$j}{$i} = $dist; } } } #pp %bonds; my ($topo,%topo); foreach my $at (sort {$a<=>$b} keys %bonds) { foreach (sort {$a<=>$b} keys %{$bonds{$at}}) { next if exists $topo{$_}{$at}; $topo{$at}{$_} = 1; } } while (my ($k,$v) = each %topo) { delete $topo{$k} unless (keys %$v); } #pp %topo; foreach (sort {$a<=>$b} keys %topo) { $topo .= ("$_-" . join(',', sort {$a<=>$b} keys %{$topo{$_}}) . ";"); } $topo =~ s/;$//; return $topo, %bonds; } sub find_near { # External hash %radius (Ковалентные радиусы из Dalton Trans., 2008, 2832-2838) my $mol = shift; my $at = shift; my @near; my $mult = 1.3; for (my $i=1; $i<@$mol; $i++) { next if $i==$at; my $dist = dist($mol,$i,$at); if ($dist < ($radius{$mol->[$i][0]}+$radius{$mol->[$at][0]})*$mult) { push @near, $i; } } return @near; } sub part { # Задается связь i-j и хэша хэшей связей # Выдает массив номеров атомов от j и дальше my $i = shift; my $j = shift; my %bonds = copy_hh(@_); #pp %bonds; delete $bonds{$i}{$j} if exists $bonds{$i}{$j}; delete $bonds{$j}{$i} if exists $bonds{$j}{$i}; #pp %bonds; my @part = ($j); my @shell = ($j); while (@shell) { my @shell_; foreach my $at (@shell) { foreach (keys %{$bonds{$at}}) { push @shell_, $_; delete $bonds{$at}{$_}; delete $bonds{$_}{$at}; } } #print "@shell\n"; # @shell = sort {$a==$i ? 1 : -1} @shell_; # sort - for -epi # for -epi: i should be at the end of shell @shell = grep {$_ != $i} @shell_; push @shell, $i if @shell != @shell_; push @part, @shell; } my %h; @part = grep {! $h{$_}++} @part; #print "@part\n"; return @part; } #sub part2 { # # Задаются связи i-j, i-k (как i,j,k) и хэш хэшей связей # # Выдает массив номеров атомов от j,k и дальше # my $i = shift; # my $j = shift; # my $k = shift; # my %bonds = copy_hh(@_); # #pp %bonds; # # delete $bonds{$i}{$j}; # delete $bonds{$j}{$i}; # delete $bonds{$i}{$k}; # delete $bonds{$k}{$i}; # #pp %bonds; # my @part = ($j); # my @shell = ($j); # while (@shell) { # my @shell_; # foreach my $at (@shell) { # foreach (keys %{$bonds{$at}}) { # push @shell_, $_; # delete $bonds{$at}{$_}; # delete $bonds{$_}{$at}; # } # } # #print "@shell\n"; ## @shell = sort {$a==$i ? 1 : -1} @shell_; # sort - for -epi # # for -epi: i should be at the end of shell # @shell = grep {$_ != $i} @shell_; # push @shell, $i if @shell != @shell_; # push @part, @shell; # } # my %h; # @part = grep {! $h{$_}++} @part; # #print "@part\n"; # return @part; #} sub copy_hh { # Независимая копия хэша хэшей my %hh = @_; my %new_hh; while (my ($k,$v) = each %hh) { $new_hh{$k} = {%$v}; } return %new_hh; } # Читает xyz. Параметры - имена xyz-файлов. Если параметров нет, то <>. # Возвращает массив найденных молекул. sub read_molden { local @ARGV = @_ ? @_ : @ARGV; my $num = qr/-?\d+(?:\.\d+)?/; my @mols; my $line; LOOP: while ($line || defined($line = <>)) { #print $line; if ($line =~/^\s*(\d+)\s*$/) { my @mol; my $N = $1; last LOOP if eof(); next LOOP if eof(ARGV); $line = <>; if ($line =~ /^\s*(.*?)\s*$/) { $mol[0]{Title} = $1; } if ($line =~ /\s($num)\s/o) { $mol[0]{Energy} = $1; } if ($line =~ /Symmetry\s+(\w+)/) { $mol[0]{Symmetry} = $1; } #($mol[0]) = $line =~ /($num)/o; for (my $i=1; $i<=$N; $i++) { last LOOP if eof(); next LOOP if eof(ARGV); $line = <>; #print $line; if ($line =~ /^\s*([A-Za-z]{1,2})\s+($num)\s+($num)\s+($num)\s*(.*)/io) { $mol[$i] = [$1,$2,$3,$4,$5]; $mol[$i][0] = ucfirst(lc $mol[$i][0]); } else { next LOOP; } } push @mols, \@mol; last LOOP if eof(); } else { undef $line; } } return @mols; } # Печатает xyz. Параметры -- список молекул, в конце м.б. имя файла. # Если последний элемент списка - имя файла (не ссылка на массив), # то печать в этот файл, иначе - на stdout sub write_molden { # my $fh; my $file; if (ref($_[-1]) ne 'ARRAY') { $file = pop @_; } my @ar; foreach my $mol (@_) { my $N = $#{$mol}; push @ar, " $N\n"; push @ar, "$mol->[0]{Name} " if exists $mol->[0]{Name}; #print $fh " Symmetry $mol->[0]{Symmetry} " if $mol->[0]{Symmetry}; push @ar, "\n"; for (my $i=1; $i<=$N; $i++) { my ($atom,$x,$y,$z) = @{$mol->[$i]}; push @ar, sprintf " %-2s %12.8f %12.8f %12.8f", $atom, $x, $y, $z; #printf $fh uc($atom) eq 'H' ? " %10.3f" : " %9.2f" , $ppm if $ppm; push @ar, "\n"; } } if ($file) { open L, '>', $file or die "Can't write to $file: $!\n"; print L @ar; close L; } else { print @ar; } } #sub write_molden { ## my $fh = \*STDOUT; # my $fh = STDOUT; # my $to_file; # if (ref($_[-1]) ne 'ARRAY') { # $to_file = 1; # my $file = pop @_; # open $fh, '>', $file or die "Can't write to $file: $!\n"; # } # # foreach my $mol (@_) { # my $N = $#{$mol}; # print $fh " $N\n"; # print $fh "$mol->[0]{Name} " if exists $mol->[0]{Name}; # #print $fh " Symmetry $mol->[0]{Symmetry} " if $mol->[0]{Symmetry}; # print $fh "\n"; # for (my $i=1; $i<=$N; $i++) { # my ($atom,$x,$y,$z) = @{$mol->[$i]}; # printf $fh " %-2s %12.8f %12.8f %12.8f", $atom, $x, $y, $z; # #printf $fh uc($atom) eq 'H' ? " %10.3f" : " %9.2f" , $ppm if $ppm; # print $fh "\n"; # } # } # $fh = \*STDOUT; # #close $fh if $to_file; #} # my ($x,$y,$z) = get_xyz($mol); # Возвращает список ссылок на массивы 1..$N (@x,@y,@z) молекулы # (нулевые элементы пустые) sub get_xyz { my $mol = shift; my (@x,@y,@z); my $N = $#{$mol}; for (my $i=1; $i<=$N; $i++) { $x[$i] = $mol->[$i][1]; $y[$i] = $mol->[$i][2]; $z[$i] = $mol->[$i][3]; } return (\@x,\@y,\@z); } # put_xyz($mol,\@x,\@y,\@z); # Помещает координаты из массивов 1..$N (@x,@y,@z) в молекулу sub put_xyz { my ($mol,$x,$y,$z) = @_; my $N = $#{$mol}; return undef if $#{$x} != $N || $#{$y} != $N || $#{$z} != $N; for (my $i=1; $i<=$N; $i++) { $mol->[$i][1] = $x->[$i]; $mol->[$i][2] = $y->[$i]; $mol->[$i][3] = $z->[$i]; } 1 } # centre_to_atom($mol,$i) # Помещает центр координат молекулы на атом $i sub centre_to_atom { my ($mol,$i) = @_; my $N = $#{$mol}; return undef if $i > $N; my ($xi,$yi,$zi) = ($mol->[$i][1],$mol->[$i][2],$mol->[$i][3]); for (my $n=1; $n<=$N; $n++) { $mol->[$n][1] -= $xi; $mol->[$n][2] -= $yi; $mol->[$n][3] -= $zi; } 1 } # ($A,$B,$C) = plane_normal($mol,$i,$j,$k); # Возвращает вектор нормаль к плоскости, проходящей через атомы $i, $j, $k. # Если атомов больше трех, плоскость через них проводится по наименьшим квадратам. sub plane_normal { my ($mol,@nums) = @_; my ($x,$y,$z) = get_xyz($mol); my @x = @$x; my @y = @$y; my @z = @$z; my ($A,$B,$C); return undef if @nums<3; if (@nums == 3) { my ($i,$j,$k) = @nums; #print "@nums\n"; $A = ($y[$j]-$y[$i])*($z[$k]-$z[$i])-($z[$j]-$z[$i])*($y[$k]-$y[$i]); $B = ($z[$j]-$z[$i])*($x[$k]-$x[$i])-($x[$j]-$x[$i])*($z[$k]-$z[$i]); $C = ($x[$j]-$x[$i])*($y[$k]-$y[$i])-($y[$j]-$y[$i])*($x[$k]-$x[$i]); #print "@nums $A $B $C\n"; } else { my ($X,$Y,$Z,$XX,$YY,$ZZ,$XY,$YZ,$XZ); foreach (@nums) { $X += $x[$_]; $Y += $y[$_]; $Z += $z[$_]; $XX += $x[$_]*$x[$_]; $YY += $y[$_]*$y[$_]; $ZZ += $z[$_]*$z[$_]; $XY += $x[$_]*$y[$_]; $YZ += $y[$_]*$z[$_]; $XZ += $z[$_]*$x[$_]; } # A*XX + B*XY + C*XZ = -X XX XY XZ | -X -X XY XZ XX -X XZ XX XY -X # A*XY + B*YY + C*YZ = -Y XY YY YZ | -Y -Y YY YZ XY -Y YZ XY YY -Y # A*XZ + B*YZ + C*ZZ = -Z XZ YZ ZZ | -Z -Z YZ ZZ XZ -Z ZZ XZ YZ -Z $A = $X*($YZ*$YZ-$YY*$ZZ) + $Y*($XY*$ZZ-$YZ*$XZ) + $Z*($YY*$XZ-$XY*$YZ); $B = $X*($XY*$ZZ-$XZ*$YZ) + $Y*($XZ*$XZ-$XX*$ZZ) + $Z*($XX*$YZ-$XY*$XZ); $C = $X*($YY*$XZ-$YZ*$XY) + $Y*($YZ*$XX-$XY*$XZ) + $Z*($XY*$XY-$YY*$XX); } my $normal = sqrt($A**2 + $B**2 +$C**2); $A /= $normal; $B /= $normal; $C /= $normal; #print "@nums $A $B $C\n"; return ($A,$B,$C); } # re_orientation($mol,$i,$j,$k); # Переориентирует молекулу так чтобы у атомов $i,$j,$k были координаты # $i: 0.00 0.00 0.00 # $j: f.ff 0.00 0.00 # $k: f.ff f.ff 0.00 # Dependecies: plane_normal, colinearity, put_xyz sub re_orientation { my ($mol,$i,$j,$k) = @_; centre_to_atom($mol,$i); #&write_molden; my ($x,$y,$z) = get_xyz($mol); my @x = @$x; my @y = @$y; my @z = @$z; my $xj = sqrt($x[$j]**2 + $y[$j]**2 + $z[$j]**2); my $xk = ($x[$k]*$x[$j] + $y[$k]*$y[$j] + $z[$k]*$z[$j]) / $xj; my $yk = sqrt($x[$k]**2 + $y[$k]**2 + $z[$k]**2 - $xk**2); my ($A1,$B1,$C1) = plane_normal($mol,$i,$j,$k); for (my $n=1; $n<=$#{$mol}; $n++) { if ($n != $i && $n != $j && $n != $k) { my ($xn,$yn,$zn); if (colinearity($mol,$j,$k,$n)) { # Try colinearity my $slope; my $xkj = $x[$k]-$x[$j]; my $ykj = $y[$k]-$y[$j]; my $zkj = $z[$k]-$z[$j]; if (abs($xkj)>=abs($ykj) && abs($xkj)>=abs($zkj)) { $slope = ($x[$n]-$x[$j])/$xkj; } elsif (abs($ykj)>=abs($zkj)) { $slope = ($y[$n]-$y[$j])/$ykj; } else { $slope = ($z[$n]-$z[$j])/$zkj; } $zn = 0; $yn = $slope*$yk; $xn = $slope*($xk-$xj)+$xj; } else { $xn = ($x[$n]*$x[$j] + $y[$n]*$y[$j] + $z[$n]*$z[$j])/$xj; $yn = ($x[$n]*$x[$k] + $y[$n]*$y[$k] + $z[$n]*$z[$k] - $xn*$xk) / $yk; my $sqr = $x[$n]**2 + $y[$n]**2 + $z[$n]**2 - $xn**2 - $yn**2; $sqr = 0 if $sqr < 0; $zn = sqrt($sqr); my ($A2,$B2,$C2) = plane_normal($mol,$j,$k,$n); my $Vec = ($B1*$C2-$C1*$B2)*($x[$k]-$x[$j])+ ($C1*$A2-$A1*$C2)*($y[$k]-$y[$j])+ ($A1*$B2-$B1*$A2)*($z[$k]-$z[$j]); $zn = -$zn if $Vec < 0; #print "$Vec\n"; } ($x[$n],$y[$n],$z[$n]) = ($xn,$yn,$zn); } } ($x[$j],$y[$j],$z[$j],$x[$k],$y[$k],$z[$k]) = ($xj,0,0,$xk,$yk,0); put_xyz($mol,\@x,\@y,\@z); #&write_molden; } sub colinearity { my ($mol,$i,$j,$k) = @_; my $eps = 1e-6; my $rij = sqrt(($mol->[$i][1]-$mol->[$j][1])**2+ ($mol->[$i][2]-$mol->[$j][2])**2+ ($mol->[$i][3]-$mol->[$j][3])**2); my $rik = sqrt(($mol->[$i][1]-$mol->[$k][1])**2+ ($mol->[$i][2]-$mol->[$k][2])**2+ ($mol->[$i][3]-$mol->[$k][3])**2); my $rjk = sqrt(($mol->[$k][1]-$mol->[$j][1])**2+ ($mol->[$k][2]-$mol->[$j][2])**2+ ($mol->[$k][3]-$mol->[$j][3])**2); my ($r1,$r2,$r3) = sort {$a<=>$b} ($rij,$rik,$rjk); if ($r1<$eps) { warn "Two atoms from $i,$j,$k coincide\n"; return 1; } if ($r1+$r2-$r3<$eps) { return 1; } return undef; } sub dist { my ($mol,$i,$j) = @_; my $rij = sqrt(($mol->[$i][1]-$mol->[$j][1])**2 + ($mol->[$i][2]-$mol->[$j][2])**2 + ($mol->[$i][3]-$mol->[$j][3])**2 ); return $rij; } sub rot_x { my ($mol,$degrees) = @_; my $rad = $degrees/180*3.14159265358979; my $cos = cos($rad); my $sin = sin($rad); for (my $i=1; $i<@$mol; $i++) { my ($y,$z) = ($mol->[$i][2],$mol->[$i][3]); $mol->[$i][2] = $sin*$z+$cos*$y; $mol->[$i][3] = $cos*$z-$sin*$y; } } sub copy_mol { my $mol = shift; my $new_mol; my $N = $#{$mol}; $new_mol->[0] = $mol->[0] if $mol->[0]; for (my $i=1; $i<=$N; $i++) { $new_mol->[$i] = [@{$mol->[$i]}]; } return $new_mol; } |
|||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||