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

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

Edisp


#!/usr/bin/perl -ws

our ($h,$help,$func,$debug);
if ($h || $help) {
  (my $program = $0) =~ s/^.*[\/\\]//;
  print <<HELP;
Usage: $program file.xyz [files.xyz]
Dependences: perl, dftd3

Run Grimme dftd3 program with BJ-damping 
and add Edisp correction (kcal/mole) into 2nd string of xyz-files

Options:
-finc=functional  Functional name in TurboMole style (-finc=pbe by default)
-debug
HELP
  if ($help) {
    print <<HELP;
    
DFT-D3: 
http://www.thch.uni-bonn.de/tc/index.php?section=downloads&subsection=DFT-D3
Please cite
S. Grimme, J. Antony, S. Ehrlich and H. Krieg,  J. Chem. Phys. 132 (2010), 154104
S. Grimme, S. Ehrlich and L. Goerigk, J. Comput. Chem. 32 (2011), 1456-1465
HELP
  }
  exit;
}

#DFT-D3 program (http://www.thch.uni-bonn.de/tc/downloads/DFT-D3/data/dftd3.tgz)
my $dftd3_x = '/usr/local/bin/dftd3';
if (! -x $dftd3_x) {
  die "No $dftd3_x!\n";
}

$func ||= 'pbe';

# Known functionals (names in TurboMole style)
# http://www.thch.uni-bonn.de/tc/downloads/DFT-D3/functionalsbj.html
my @func = qw (
b-p b-lyp revpbe rpbe b97-d pbe rpw86-pbe b3-lyp tpss hf tpss0 pbe0 hse06 
revpbe38 pw6b95 b2-plyp dsd-blyp dsd-blyp-fc bop mpwlyp o-lyp pbesol bpbe opbe 
ssb revssb otpss b3pw91 bh-lyp revpbe0 tpssh mpw1b95 pwb6k b1b95 bmk cam-b3lyp 
lc-wpbe b2gp-plyp ptpss pwpb95 hf/mixed hf/sv hf/minis b3-lyp/6-31gd hcth120 
dftb3 pw1pw pwgga hsesol hf3c hf3cv);

# Some synonims of functionals
$func = lc($func);
my %synonims = qw(
b3lyp  b3-lyp
blyp   b-lyp
plyp   o-lyp
pbe1   pbe0
);
if (exists $synonims{$func}) {
  $func = $synonims{$func};
}

if (! grep {$func eq $_} @func) {
  die "Known functionals: @func\n";
}

foreach my $file (@ARGV) {
  my @mols = read_molden($file);
  
  # Remove old Edisp
  foreach my $mol (@mols) {
    while (1) {
      $mol->[0] =~ s/Edisp\s+-?\d+(\.\d+)?\s*// or last;
    }
    $mol->[0] =~ s/\s+$//;
  }
  
  if (@mols > 1) {
    my $tmp = 'dftd3.xyz';
    foreach my $mol (@mols) {
      write_molden($mol, $tmp);
      my $D3 = D3correction($tmp);
      $mol->[0] .= "  Edisp $D3";
    }
    unlink $tmp;
    write_molden(@mols,$file);
  }
  else {
    my $D3 = D3correction($file);
    $mols[0][0] .= "  Edisp $D3";
    write_molden($mols[0], $file);
  }
}

sub D3correction {
  my $file = shift; # filename of xyz as input
  my $D3;           # return value
  my @args = ($dftd3_x, $file, '-func', $func, '-bj');
  print "Run `@args`\n" if $debug;
  open DFTD3, '-|', @args or do {warn "Can't run @args : $!\n"; return undef};
  while (<DFTD3>) {
    print if $debug;
    if (/^\s*Edisp \/kcal,au:\s+(\S+)/) {
      $D3 = $1;
    }
  }
  close DFTD3;
  if (! defined($D3)) {
    die "Problems in subroutine D3correction\n";
  }
  return sprintf("%.2f", $D3);
}

sub read_molden {
 ############################################################################
 ## Молекула -- ссылка на массив, 0-й элемент -- свойства, следующие - атомы.
 ## Свойства -- ссылка на хэш с ключами Energy, Symmetry
 ## Атом -- ссылка на массив [atom, x, y, z, ppm] (ppm может не быть).
 ##
 ## Читает xyz. Параметры - имена xyz-файлов. Если параметров нет, то <>.
 ## Возвращает массив найденных молекул.
 ############################################################################
  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 = <>;
      chomp $line;
      $mol[0] = $line;
      for (my $i=1; $i<=$N; $i++) {
        last LOOP if eof();
        next LOOP if eof(ARGV);
        $line = <>;
        #print $line;
        if ($line =~ /^\s*([A-Z]{1,2})\s+($num)\s+($num)\s+($num)\s*(.*)/io) {
          $mol[$i] = [$1,$2,$3,$4,$5];
        } else {
          next LOOP;
        }
      }
      push @mols, \@mol;
      last LOOP if eof();
    } else {
      undef $line;
    }
  }
  return @mols;
}

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$mol->[0]\n";
    for (my $i=1; $i<=$N; $i++) {
      printf F " %-2s %12.8f %12.8f %12.8f", $mol->[$i][0],$mol->[$i][1],$mol->[$i][2],$mol->[$i][3];
      print F "  $mol->[$i][4]" if defined $mol->[$i][4];
      print F "\n";
    }
  }
  close F;
}