Новосибирский институт органической химии им. Н.Н. Ворожцова СО РАН Лаборатория изучения механизмов органических реакций |
|||||||||||||||||||||||||||||||||||||||||||||||||||||||||
addXX#!/usr/bin/perl -ws use vars qw($h $Z); if (!@ARGV or $h) { (my $program = $0) =~ s/.*[\/\\]//; print " Usage: $program [-Z] 'i,j,k... l,m... ...' file.xyz ... Зависимости: perl Добавляет фиктивные атомы (XX) в геометрический центр каждого из наборов атомов, перечисленных в первом параметре. i,j,k,l,m - номера атомов. Если после набора атомов в круглых скобках даны расстояния (все без пробелов), то XX добавляются на этих расстояниях над центром кольца. Если дана опция -Z и хотя бы тройка атомов, выполняется переориентация молекулы: центр координат помещается в центр перечисленных атомов, ось Z направлена перпендикулярно плоскости, задаваемой этими атомами, a ось X - в сторону первого из атомов тройки. Нужно все это, в частности, для определения точек, в которых считать NICS. file.xyz - файл в молденовском xyz-формате. Примеры: $program 1-6 file.xyz - добавить XX в центр атомов с 1 по 6-й (например, в центр бензольного кольца) cat file.xyz | $program '1,2 3-5' - добавить два XX, между 1-м и 2-м и между 3-5 атомами $program '1-6(0,-1,1)' file.xyz - добавить XX в центр кольца, заданного атомами 1-6, а также на расстоянии 1 ангстрем под и над кольцом. $program -Z '1-6()' file.xyz - просто переориентировать молекулу, не добавляя XX \n"; exit; } my ($N,$energy,@atom,@x,@y,@z,@charge); @at_num = split ' ', shift @ARGV; while (&read_molden()) { $OK = 1; $first_at_num = 1; AT_NUM: foreach (@at_num) { if ( m/([^(]+)\(([^)]*)\)/ ) { @nums = get_atom_numbers($1); if (@nums<3) { warn "Not enough numbers: @nums\n"; undef $OK; next AT_NUM; } @distances = split /,/, $2; foreach (@distances) { warn "Invalid: @distances\n" and next AT_NUM unless m/^-?\d+(\.\d+)?$/; } #warn "@nums (@distances)\n"; if ($Z && $first_at_num) { XXorientation(@nums); $first_at_num = undef; } ($x,$y,$z) = centre_coordinates(@nums); undef $OK and next AT_NUM unless defined $x; ($A,$B,$C) = plane_normal(@nums); undef $OK and next AT_NUM unless defined $A; foreach (@distances) { add_XX($x + $A*$_, $y + $B*$_, $z + $C*$_); } } else { @nums = get_atom_numbers($_); next AT_NUM unless @nums; (warn("Only one number: @nums\n"), undef $OK, next AT_NUM) if @nums==1; if ($Z && $first_at_num) { XXorientation(@nums); $first_at_num = undef; } ($x,$y,$z) = centre_coordinates(@nums); undef $OK and next AT_NUM unless defined $x; add_XX($x, $y, $z); } } &write_molden if $OK; } sub XXorientation { my @nums = @_; warn "Not enough numbers (@nums) for reorientation\n" and return undef if @nums<3; my ($x,$y,$z) = centre_coordinates(@nums); warn "Can't centre_coordinates(@nums)\n" and return undef unless defined $x; my ($A,$B,$C) = plane_normal(@nums); warn "Can't plane_normal(@nums)\n" and return undef unless defined $A; add_XX($x, $y, $z); add_XX($x+$A, $y+$B, $z+$C); re_orientation($#atom-1,$#atom,$nums[0]); $#atom -= 2; $#x -= 2; $#y -= 2; $#z -= 2; my @ttt = @x; @x = @y; @y = @z; @z = @ttt; } sub centre_coordinates { my $x = 0; my $y = 0; my $z = 0; my @nums = @_; return undef unless @nums; foreach (@nums) { warn "Invalid atom number: $_" and return undef if $_>$#atom; $x += $x[$_]; $y += $y[$_]; $z += $z[$_]; } $x /= $#nums+1; $y /= $#nums+1; $z /= $#nums+1; return ($x,$y,$z); } sub plane_normal { my @nums = @_; my ($A,$B,$C); return undef if @nums<3; my ($x,$y,$z) = centre_coordinates(@nums); my (@x1,@y1,@z1); foreach (@nums) { $x1[$_] = $x[$_]-$x; $y1[$_] = $y[$_]-$y; $z1[$_] = $z[$_]-$z; } my ($XX,$XY,$XZ,$YY,$YZ,$ZZ); foreach (@nums) { #$X += $x[$_]; $Y += $y[$_]; $Z += $z[$_]; $XX += $x1[$_]*$x1[$_]; $YY += $y1[$_]*$y1[$_]; $ZZ += $z1[$_]*$z1[$_]; $XY += $x1[$_]*$y1[$_]; $YZ += $y1[$_]*$z1[$_]; $XZ += $z1[$_]*$x1[$_]; } #print STDERR "$XX $XY $XZ\n$YY $YZ\n$ZZ\n"; my @eigen = jacobi($XX,$XY,$XZ,$YY,$YZ,$ZZ) or die "Eigen error\n"; ($A,$B,$C) = @{$eigen[2][1]}; # my $d = 1e-3; # if (@nums == 3) { # my ($i,$j,$k) = @nums; # $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[$_]; # } # print STDERR "$X,$Y,$Z,$XX,$YY,$ZZ,$XY,$YZ,$XZ\n"; # # if (abs($XX)<$d && abs($YY)<$d or # abs($XX)<$d && abs($ZZ)<$d or # abs($YY)<$d && abs($ZZ)<$d) { # return undef; # } # # if (abs($ZZ)<$d) { # $A = 0; # $B = 0; # $C = 1; # } # elsif (abs($YY)<$d) { # $A = 0; # $B = 1; # $C = 0; # } # elsif (abs($XX)<$d) { # $A = 1; # $B = 0; # $C = 0; # } # else { # # 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*($YY*$ZZ-$YZ**2)+$XY*($YZ*$Z-$Y*$ZZ)+$XZ*($Y*$YZ-$YY*$Z)); # $B = -($XX*($YZ*$Z-$Y*$ZZ)+$X*$XY*$ZZ+$XZ*(-$XY*$Z-$X*$YZ)+$XZ**2*$Y); # $C = ($XX*($YY*$Z-$Y*$YZ)-$XY**2*$Z+$X*$XY*$YZ+$XZ*($XY*$Y-$X*$YY)); ## $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); #print STDERR "@nums $A $B $C\n"; $A /= $normal; $B /= $normal; $C /= $normal; return ($A,$B,$C); } #sub plane_norm { # my @nums = @_; # return undef if @nums<3; # my $A = 0; my $B = 0; my $C = 0; # for ($num=0; $num<=$#nums-2; $num++) { # $i = $nums[$num]; $j = $nums[$num+1]; $k = $nums[$num+2]; # $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]); # } # $A /= $#nums-1; $B /= $#nums-1; $C /= $#nums-1; # 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); #} sub add_XX { push @x, $_[0]; push @y, $_[1]; push @z, $_[2]; push @atom, 'XX'; } sub centre_to_atom { my $i = shift; return undef if $i > @atom; my ($xi,$yi,$zi) = ($x[$i],$y[$i],$z[$i]); for ($n=1; $n<=$#atom; $n++) { $x[$n] -= $xi; $y[$n] -= $yi; $z[$n] -= $zi; } } sub re_orientation { my ($i,$j,$k) = @_; centre_to_atom($i); #&write_molden; 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($i,$j,$k); for ($n=1; $n<@atom; $n++) { if ($n != $i && $n != $j && $n != $k) { my $xn = ($x[$n]*$x[$j] + $y[$n]*$y[$j] + $z[$n]*$z[$j])/$xj; my $yn = ($x[$n]*$x[$k] + $y[$n]*$y[$k] + $z[$n]*$z[$k] - $xn*$xk) / $yk; my $zn = sqrt($x[$n]**2 + $y[$n]**2 + $z[$n]**2 - $xn**2 - $yn**2); my ($A2,$B2,$C2) = plane_normal($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); #&write_molden; } sub get_atom_numbers { my ($vector,@result); $vector = shift; $vector =~ s/^\s+//; # Убираем пробелы в начале $vector =~ s/\s+$//; # Убираем пробелы в конце $vector =~ s/\s+/,/g; # Заменяем пробелы на запятые unless ($vector =~ /^(\d+(-\d+)?,)*\d+(-\d+)?$/g) { warn "Not valid atom numbers\n"; return (); } foreach (split /,/, $vector) { push @result, /(\d+)-(\d+)/ ? ($1<=$2 ? $1..$2 : reverse($2..$1)) : $_; } return @result; } sub read_molden { return undef if eof(); # 1-st string if (<>=~/^\s*(\d+)\s*$/) { ($N,$energy,@atom,@x,@y,@z,@ppm) = undef; $N = $1; } else { return undef; } # 2-nd string ($energy) = <>=~/(-?\d+\.\d+)/; # Geometry for ($i=1;$i<=$N;$i++) { ($atom[$i],$x[$i],$y[$i],$z[$i],$ppm[$i],undef) = split ' ', <>; return undef unless $atom[$i]=~/^\w\w?/ && $x[$i]=~/^-?\d+\.\d+/ && $y[$i]=~/^-?\d+\.\d+/ && $z[$i]=~/^-?\d+\.\d+/; } return $N; } sub write_molden { if (@atom && @x && @y && @z) { print " $#atom\n"; print " Energy $energy" if $energy; print "\n"; for ($i=1; $i<=$#atom; $i++) { if (defined($atom[$i]) && defined($x[$i]) && defined($y[$i]) && defined($z[$i])) { printf " %-2s %12.8f %12.8f %12.8f",$atom[$i],$x[$i],$y[$i],$z[$i]; printf $atom[$i] eq 'H' ? " %10.3f" : " %9.2f" , $ppm[$i] if $ppm[$i]; print "\n"; } } } } sub jacobi { ########################################################################### ## Собственные числа и векторы симметричной матрицы методом Якоби. ## Код из Жориной программы dte (C). ## Принимает массив ($Ixx,$Ixy,$Ixz,$Iyy,$Iyz,$Izz). ## Возвращает отсортированный (по убыванию собств. чисел) массив двухэлементных ## массивов. 1-й элемент - собств. число, 2-й - соответствующий ему вектор. ########################################################################### my @a = ([ $_[0],$_[1],$_[2] ], [ $_[1],$_[3],$_[4] ], [ $_[2],$_[4],$_[5] ]); my ($j, $iq, $ip, $i, $n); my ($tresh, $theta, $tau, $t, $sm, $s, $h, $g, $c); my $maxit = 50; $n = 3; my @v = ([1,0,0], [0,1,0], [0,0,1]); my @b = my @d = ($a[0][0],$a[1][1],$a[2][2]); my @z = (0,0,0); my $nrot = 0; for ($i=0; $i<$maxit; $i++) { $sm = abs($a[0][1]) + abs($a[0][2]) + abs($a[1][2]); if ($sm == 0) { return sort {$b->[0] <=> $a->[0]} ([$d[0], [$v[0][0],$v[1][0],$v[2][0]]], [$d[1], [$v[0][1],$v[1][1],$v[2][1]]], [$d[2], [$v[0][2],$v[1][2],$v[2][2]]]); } # The normal return, which relies on # quadratic convergence to machine underflow. $tresh = ($i < 4) ? 0.2*$sm/($n*$n) : 0; # ...on the first three sweeps. # ...thereafter. for ($ip=0; $ip<$n-1; $ip++) { for ($iq=$ip+1; $iq<$n; $iq++) { $g = 100*abs($a[$ip][$iq]); # After four sweeps, skip the rotation if the off-diagonal element is small. if ($i>4 && abs($d[$ip])+$g == abs($d[$ip]) && abs($d[$iq])+$g == abs($d[$iq])) { $a[$ip][$iq] = 0; } elsif (abs ($a[$ip][$iq]) > $tresh) { $h = $d[$iq]-$d[$ip]; if (abs($h)+$g == abs($h)) { $t = $a[$ip][$iq]/$h; } # $t = 1/(2theta) else { $theta = 0.5*$h/$a[$ip][$iq]; $t = 1/(abs($theta)+sqrt(1+$theta*$theta)); if ($theta < 0) {$t = -$t;} } $c = 1/sqrt(1+$t*$t); $s = $t*$c; $tau = $s/(1+$c); $h = $t*$a[$ip][$iq]; $z[$ip] -= $h; $z[$iq] += $h; $d[$ip] -= $h; $d[$iq] += $h; $a[$ip][$iq] = 0; for ($j=0; $j<$ip; $j++) { # Case of rotations 0 <= $j < p. $g = $a[$j][$ip]; $h = $a[$j][$iq]; $a[$j][$ip] = $g-$s*($h+$g*$tau); $a[$j][$iq] = $h+$s*($g-$h*$tau); } for ($j=$ip+1; $j<$iq; $j++) { # Case of rotations p < $j < q. $g = $a[$ip][$j]; $h = $a[$j][$iq]; $a[$ip][$j] = $g-$s*($h+$g*$tau); $a[$j][$iq] = $h+$s*($g-$h*$tau); } for ($j=$iq+1; $j<$n; $j++) { # Case of rotations q < $j < $n. $g = $a[$ip][$j]; $h = $a[$iq][$j]; $a[$ip][$j] = $g-$s*($h+$g*$tau); $a[$iq][$j] = $h+$s*($g-$h*$tau); } for ($j=0; $j<$n; $j++) { $g = $v[$j][$ip]; $h = $v[$j][$iq]; $v[$j][$ip] = $g-$s*($h+$g*$tau); $v[$j][$iq] = $h+$s*($g-$h*$tau); } ++$nrot; } } } for ($ip=0; $ip<$n; $ip++) { $b[$ip] += $z[$ip]; $d[$ip] = $b[$ip]; # Update $d with the sum of tapq, $z[$ip] = 0; # and reinitialize $z. } } return undef; } |
|||||||||||||||||||||||||||||||||||||||||||||||||||||||||