Новосибирский институт органической химии им. Н.Н. Ворожцова СО РАН

Лаборатория изучения механизмов органических реакций

mgp2xyz


#!/usr/bin/perl -ws

our ($h, $help, $NACP, $all_BCP);

if ($h or $help) {
(my $program = $0) =~ s/^.*[\/\\]//;
print "Usage: $program file.sum
Зависимости: perl

Окучивает для молдена результаты AIMAll (http://aim.tkgristmill.com).
Понимает sum- и mgp-файлы (в последних нет зарядов на атомах).
Создаются xyz-файлы с координатами атомов и критических точек. 
На экран печатается номер крит. точки в xyz-файле и информация о ней.
Обозначения критических точек можно задать свои, см. в начале скрипта, 
например, NACP и NNACP - XX; BCP, RCP и CCP - He. Сейчас все XX.
По умолчанию критические точки на всех ядрах (NACP) и между связями (BCP), 
которые нарисует молден, не включаются. С опциями -NACP и -all_BCP включаются.
Пятой колонкой в xyz-файле идут для атомов заряды q(A) и для критических точек 
элекронная плотность Rho (их можно посмотреть в молдене Label - atom+charge). 
";

exit;
}

# Обозначения критических точек (нужно фикт. атом XX или элемент)
# Элемент лучше брать какой-нибудь неходовой, например He, 
# и, возможно, уменьшить радиус этого элементы в файле .moldenrc
my %CP_name = (
  NACP  => 'XX',   # Nuclear Attractor Critical Point                                      
  NNACP => 'XX',   # Non-Nuclear Attractor Critical Point                                 
  BCP   => 'XX',   # Bond Critical Point                                                    
  RCP   => 'XX',   # Ring Critical Point                                                    
  CCP   => 'XX',   # Cage Critical Point                                                    
);

my $bohr = 0.529177249; # bohr => angstrom
my $num = qr/-?\d+\.\d+E[+-]\d+/; # Number

# Ковалентные радиусы из файла .moldenrc
my %radius = qw(
 H 0.43  Al 1.55  Mn 1.55  Rb 1.67  In 1.83  Pm 2.00  Ta 1.63  At 0.20  Bk 0.20
He 0.741 Si 1.40  Fe 1.54  Sr 1.32  Sn 1.66  Sm 2.00   W 1.57  Rn 0.20  Cf 1.73
Li 0.88   P 1.25  Co 1.53   Y 1.98  Sb 1.66  Eu 2.19  Re 1.55  Fr 0.20
Be 0.55   S 1.22  Ni 1.70  Zr 1.76  Te 1.67  Gd 1.99  Os 1.57  Ra 2.10
 B 1.03  Cl 1.19  Cu 1.72  Nb 1.68   I 1.60  Tb 1.96  Ir 1.52  Ac 2.08
 C 0.90  Ar 0.995 Zn 1.65  Mo 1.67  Xe 1.75  Dy 1.95  Pt 1.70  Th 1.99
 N 0.88   K 1.53  Ga 1.42  Tc 1.55  Cs 1.87  Ho 1.94  Au 1.70  Pa 1.81
 O 0.88  Ca 1.19  Ge 1.37  Ru 1.60  Ba 1.54  Er 1.93  Hg 1.90   U 1.78
 F 0.84  Sc 1.64  As 1.41  Rh 1.65  La 2.07  Tm 1.92  Tl 1.75  Np 1.75
Ne 0.815 Ti 1.67  Se 1.42  Pd 1.70  Ce 2.03  Yb 2.14  Pb 1.74  Pu 0.20
Na 1.17   V 1.53  Br 1.41  Ag 1.79  Pr 2.02  Lu 1.92  Bi 1.74  Am 1.71
Mg 1.30  Cr 1.55  Kr 1.069 Cd 1.89  Nd 2.01  Hf 1.77  Po 1.88  Cm 0.20
);



foreach my $file (@ARGV) {
  (my $file_name = $file) =~ s/\.[^.]*$//;
  open MGP, '<', $file or die "Can't open $file: $!\n";
  my $mol = [{}];
  my $N;
  print "\n$file\n";
  
  # Go
  while (<MGP>) {
    
    # Cartesian Coordinates
    if (m/^Nuclear Charges and Cartesian Coordinates:/) {
      <MGP>; <MGP>; <MGP>;
      while (<MGP>) {
        last if m/^\s*$/; # Termination by empty string
        my (undef,$Charge,$X,$Y,$Z) = split;
        push @$mol, [element(int $Charge),$X*$bohr,$Y*$bohr,$Z*$bohr];
      }
      $N = $#{$mol}; 
    }
    
    # Atomic Properties (in sum file only)
    if (m/^Some Atomic Properties:/) {
      while (<MGP>) {
        # Net Charge of Atoms
        if (m/^Atom A          q\(A\)/) {
          <MGP>;
          for (my $i=1; $i<=$N; $i++) {
            my $q_A = (split ' ', <MGP>)[1];
            $mol->[$i][4] = $q_A if $q_A =~ /^$num$/;
          }
        }
        # Delete bad charges
        if (m/^Warning!  Significant integration error for the following atom:\s+[A-Za-z]+(\d+)/) {
          undef $mol->[$1][4];
        }
        # Energy
        if (m/^Molecular energy E\(Mol\) from the wfn file:\s+($num)/) {
          $mol->[0]{Energy} = $1;
        }
        last if m/Electron Density Critical Point Analysis of Molecular Structure/; # Termination
      }
    }
    
    # Critical Points
    if (m/^CP#\s+\d+\s+Coords =\s*/) {
      my ($X,$Y,$Z) = split ' ', $';
      my ($Type,$Rho,$Type_str);
      while (<MGP>) {
        last if m/^\s*$/;
        if (m/^\s+Type = \(\S+\)\s+([A-Z]+)/ ) {
          $Type = $1;
          $Type_str = $_;
        }
        if (m/^\s+Rho =\s*(\S+)/) {
          $Rho = $1;
        }
      }
      
      my $need_CP = 1;
      $need_CP = 0 if 
        ( $Type eq 'NACP' && 
          ! $NACP
        ) ||
        ( $Type eq 'BCP' && 
          ! $all_BCP &&
          $Type_str =~ /[A-Za-z]+(\d+)\s+[A-Za-z]+(\d+)\s*$/ &&
          distance($mol,$1,$2) < $radius{$mol->[$1][0]}+$radius{$mol->[$2][0]}
        );
      
      if ($need_CP) {
        push @$mol, [$CP_name{$Type},$X*$bohr,$Y*$bohr,$Z*$bohr,$Rho];
        print $#{$mol}, $Type_str;
      }
    }
  }
  close MGP;
  
  write_molden($mol, "$file_name.xyz");
}

sub element {
 ############################################################################
 ## Возвращает атомный номер по символу либо символ по номеру
 ## или undef при отсутствии элемента в таблице Менделеева
 ############################################################################
  # Таблица Менделеева
  my @ATOM = qw (XX
                 H                                                  He
                 Li Be                               B  C  N  O  F  Ne
                 Na Mg                               Al Si P  S  Cl Ar
                 K  Ca Sc Ti V  Cr Mn Fe Co Ni Cu Zn Ga Ge As Se Br Kr
                 Rb Sr Y  Zr Nb Mo Tc Ru Rh Pd Ag Cd In Sn Sb Te I  Xe
                 Cs Ba La
                         Ce Pr Nd Pm Sm Eu Gd Tb Dy Ho Er Tm Yb
                       Lu Hf Ta W  Re Os Ir Pt Au Hg Tl Pb Bi Po At Rn
                 Fr Ra Ac
                         Th Pa U  Np Pu Am Cm Bk Cf Es Fm Md No
                       Lr Rf Db Sg Bh Hs Mt Ds Rg
                );
  my $atom = shift;
  if ($atom=~/^\d+$/) {
    return undef if $atom > $#ATOM;
    return $ATOM[$atom];
  }
  $atom = ucfirst $atom;
  my %ATOM;
  @ATOM{@ATOM} = 0..$#ATOM;
  return $ATOM{$atom} if exists $ATOM{$atom};
  return undef;
}

sub write_molden {
  my $file = pop @_;
  open F, '>', $file or do {warn "Can't write to $file: $!\n"; return};
  foreach my $mol (@_) {;
    my $N = $#{$mol};
    print F " $N\n";
    printf F " Energy %.8f", $mol->[0]{Energy} if $mol->[0]{Energy};
    print F "\n";
    for (my $i=1; $i<=$N; $i++) {
      printf F " %-2s %12.8f %12.8f %12.8f", @{$mol->[$i]}[0..3];
      printf F " %8.4f", $mol->[$i][4] if defined $mol->[$i][4];
      print F "\n";
    }
  }
  close F;
}

sub distance {
  my ($mol,$n1,$n2) = @_;
  my $dist = ($mol->[$n1][1] - $mol->[$n2][1])**2 +
             ($mol->[$n1][2] - $mol->[$n2][2])**2 +
             ($mol->[$n1][3] - $mol->[$n2][3])**2;
  return sqrt($dist);
}