from nwchem import *
from math import *

def pes_scan(input,start,end,nstep,theory,task):
  # 
  # Does a true multidimensional potential energy surface
  # scan, with user input specifying the minimum and maximum
  # values of each variable, and number of times to step each.
  # The number of calculations done SCALES QUICKLY; according
  # to (nstep+1)^(# of variables to scan).  This code can handle
  # any computationally feasible number of variables, including
  # just one.
  #
  # Theory selects the electronic wavefunction and task can be
  # any defined task but most likely is one of task_energy or
  # task_optimize.  If task optimize is selected, then it makes
  # most sense for one or more of the parameters to be frozen
  # in a geometry. 
  #
  # Calculations ARE performed at the end points so specifying
  # nstep=1 does one calculation at the start point, and one at
  # the end point.
  #
  # Returned is the tuple
  #
  # [(param-1,results-1), (param-2,results-2), ...]
  #
  # where param-n are the parameters of the n-th step and
  # results-n are the corresponding results returned by task()
  #
  # Example.  Scan a bond & angle computing the SCF energy
  #
  #  geom = '''
  #    geometry noprint adjust
  #      zcoord
  #        bond  3 2   %f oh
  #        angle 3 2 1 %f hon constant
  #      end
  #    end
  #  '''
  #  results = pes_scan(geom, \
  #                      [0.967, 103.3], 
  #                      [2.109,  26.96],
  #                      10, 'scf', task_energy)
  #
  # in this example using pes_scan, over 120 single point energy
  # calculations would be done.  using scan_input, only 10 would
  # be done.

  results = []
 
  if (len(start) != len(end)):
    raise NWChemError,'pes_scan: inconsistent #parameters'

  npoint = (nstep+1)**len(start)

  if (ga_nodeid() == 0): 
    print ' '
    print ' Doing a PES Scan on input '
    print ' -------------------------'
    print ' '
    print input
    print ' '
    print ' Number of points ', npoint
    print ' Minimum values ', start
    print ' Maximum values ', end
    print ' '

  step = []
  for i in range (0, len(start)):
    step.append((end[i]-start[i])/nstep)


  ylist = []
  for i in range (0, len(start)):
    xlist=[]
    for j in range (0, nstep+1):
      xlist.append(start[i]+j*step[i])
    ylist.append(xlist)

  
  zlist = []
  indexes = len(ylist) * [0]
  base = len(ylist[0])

  while 1:
    elt = []
    for i in range(0, len(indexes)):
        elt.append(ylist[i][indexes[i]])
    zlist.append(elt)
    for i in range(len(indexes)-1, -1, -1):
        if indexes[i] < base-1:
            indexes[i] = indexes[i]+1
            for j in range(i+1, len(indexes)):
                indexes[j] = 0
            break
    else: break


  for i in range(0,len(zlist)):
     new = zlist[i]
    
       
     if (ga_nodeid() == 0):
        print ' '
        print ' Scanning NWChem input - point %d of %d ' % (i+1,npoint)
        print ' '
        print input % tuple(new)
        print ' '

     input_parse(input % tuple(new))
     result = task(theory)
     if (ga_nodeid() == 0):
        print ' '
        print ' Scanning NWChem input - results from point ', i+1
        print ' '
        print result
        print ' '
     results.append((new,result));

  if (ga_nodeid() == 0):
     print ' '
     print ' Python Scan Output '
     print ' '
     for i in range(0,len(results)):
       print results[i][0], results[i][1]
     print ' '
     print ' Python Scan Output Finished '
  return tuple(results)
