File: pymolcas.py

package info (click to toggle)
openmolcas 20.10-2
  • links: PTS, VCS
  • area: main
  • in suites: bookworm, bullseye, sid
  • size: 156,232 kB
  • sloc: fortran: 720,814; f90: 21,642; python: 8,819; ansic: 5,797; perl: 1,791; sh: 942; pascal: 459; javascript: 272; makefile: 17
file content (244 lines) | stat: -rwxr-xr-x 9,898 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
#!/usr/bin/env python
# -*- coding: utf-8 -*-

#***********************************************************************
# This file is part of OpenMolcas.                                     *
#                                                                      *
# OpenMolcas is free software; you can redistribute it and/or modify   *
# it under the terms of the GNU Lesser General Public License, v. 2.1. *
# OpenMolcas is distributed in the hope that it will be useful, but it *
# is provided "as is" and without any express or implied warranties.   *
# For more details see the full text of the license in the file        *
# LICENSE or in <http://www.gnu.org/licenses/>.                        *
#                                                                      *
# Copyright (C) 2015-2018, Ignacio Fdez. Galván                        *
#***********************************************************************

from __future__ import (unicode_literals, division, absolute_import, print_function)
try:
  from six import text_type
except ImportError:
  text_type = str
import sys
sys.dont_write_bytecode = True

warning = ''
stamp = 'd41d8cd98f00b204e9800998ecf8427e'

def main(my_name):
  if sys.hexversion < 0x03000000:
    if sys.hexversion < 0x02070000:
      return("Python 2.7 or newer is required to run this program.")
  else:
    if sys.hexversion < 0x03040000:
      return("Python 3.4 or newer is required to run this program.")

  import os
  import os.path
  import codecs
  import argparse
  import hashlib

  # Default signal handlers
  from signal import signal, SIGPIPE, SIG_DFL
  signal(SIGPIPE, SIG_DFL)

  # Unbuffered output, and utf8 encoding
  # TODO: use binary streams throughout to get rid of encoding troubles?
  sys.stdout = os.fdopen(sys.stdout.fileno(), 'wb', 0)
  sys.stderr = os.fdopen(sys.stderr.fileno(), 'wb', 0)
  try:
    sys.stdout = codecs.getwriter('utf-8')(sys.stdout.buffer)
    sys.stderr = codecs.getwriter('utf-8')(sys.stderr.buffer)
  except AttributeError:
    sys.stdout = codecs.getwriter('utf-8')(sys.stdout)
    sys.stderr = codecs.getwriter('utf-8')(sys.stderr)

  with open(my_name, 'rb') as thisfile:
    stamp = hashlib.md5(thisfile.read()).hexdigest()

  if ('PYMOLCAS_STARTED' not in os.environ):
    if (warning):
      print(warning)
    os.environ['PYMOLCAS_STARTED'] = ''

  # Define this as the driver, so scripts can use the same driver
  # (if there is a directory name, we should use the full path)
  if (os.path.dirname(sys.argv[0])):
    os.environ['MOLCAS_DRIVER'] = my_name
  else:
    os.environ['MOLCAS_DRIVER'] = sys.argv[0]

  # Command-line arguments
  parser = argparse.ArgumentParser(formatter_class=lambda prog: argparse.HelpFormatter(prog,max_help_position=42,width=120))
  parser.add_argument('-s', '--setup', help='set up a custom molcasrc file with useful environment variables', action='store_true')
  parser.add_argument('-env', '--environment', help='display information about environment', action='store_true')
  parser.add_argument('-clean', '--clean_scratch', help='clean scratch area after calculation', action='store_true')
  parser.add_argument('-new', '--new_scratch', help='clean scratch area before calculation', action='store_true')
  parser.add_argument('-old', '--old_scratch', help='reuse scratch area (default)', action='store_true')
  parser.add_argument('-ign', '--ignore_environment', help='run ignoring resource files', action='store_true')
  parser.add_argument('-val', '--validate', help='validate input only (dry run)', action='store_true')
  parser.add_argument('-np', '--nprocs', help='number of parallel (MPI) processes', type=int)
  parser.add_argument('-v', '--version', help='print version of the driver', action='store_true')
  parser.add_argument('-o', '--output', help='redirect output stream to FILE', metavar='FILE')
  parser.add_argument('-e', '--error', help='redirect error stream to FILE', metavar='FILE')
  parser.add_argument('-oe', '-eo', '--outputerror', help='redirect output and error streams to FILE', metavar='FILE')
  parser.add_argument('-f', '--files', help='redirect output/error streams to standard files (*.log and *.err)', action='store_true')
  parser.add_argument('-b', '--buffer', help='buffer size for input/error files (-1: default, 0: none, 1: line, n: n bytes)', type=int, default=-1)
  parser.add_argument('--banner', help='print banner (includes version and compilation info)', action='store_true')
  parser.add_argument('--not-here', help='do not try to find MOLCAS in the current directory (and parents)', action='store_true')
  parser.add_argument('-l', '--license', help=argparse.SUPPRESS or 'check if there is a valid license', action='store_true')
  parser.add_argument('filename', help='input file or script', nargs='?', metavar='input_file | script')
  parser.add_argument('extra', help='additional arguments passed to the script', nargs=argparse.REMAINDER, metavar='...')
  parser.usage = '{0} [options] [input_file | script ...]'.format(parser.prog)
  args = vars(parser.parse_args())

  from molcas_aux import find_molcas, find_sources, attach_streams, dotmolcas, get_utf8, set_utf8
  from write_molcasrc import write_molcasrc
  from molcas_wrapper import Molcas_wrapper, MolcasException

  # Checking for version right at the beginning, in case MOLCAS cannot be found
  if (get_utf8('MOLCAS', default='') == ''):
    set_utf8('MOLCAS', '/usr/share/openmolcas')
  if (args['version']):
    print('python driver version = {0}'.format(Molcas_wrapper.version))
    print('(after the original perl EMIL interpreter of Valera Veryazov)')
    return(0)

  if (args['setup']):
    if (write_molcasrc(dotmolcas('molcasrc'), parser.prog)):
      return(0)
    else:
      return(1)

  xbin_list={}
  find_molcas(xbin_list, here=(not args['not_here']))
  find_sources()

  # If this a program defined in xbin, call it
  import subprocess
  if (args['filename'] in xbin_list):
    target = xbin_list[args['filename']]
    print('"{0}" aliased to {1}'.format(args['filename'], target))
    command = '{0} {1}'.format(args['filename'], args['extra'])
    command = [target] + args['extra']
    return(subprocess.call(command))

  # If this is not calling a program in sbin, pass the extra options to the main program
  # Unfortunately we cannot use Molcas.in_sbin yet, because Molcas is not initialized
  if (args['filename']):
    # Specifically, verify should work with the default environment
    if (args['filename'] == 'verify'):
      args['ignore_environment'] = True
    if (args['extra']):
      in_sbin = False
      for path in ['MOLCAS', 'OPENMOLCAS_SOURCE', 'MOLCAS_SOURCE']:
        filetest = os.path.join(os.environ[path], 'bin', args['filename'])
        if (os.path.isfile(filetest) and os.access(filetest, os.X_OK)):
          in_sbin = True
      if (not in_sbin):
        for k,v in vars(parser.parse_args(args['extra'])).items():
          if (v):
            args[k] = v

  if args['outputerror']:
    args['output'] = args['outputerror']
    args['error'] = args['outputerror']

  # Define environment variables according to input arguments
  if (args['nprocs']):
    os.environ['MOLCAS_NPROCS'] = text_type(args['nprocs'])

  if (args['clean_scratch']):
    os.environ['MOLCAS_KEEP_WORKDIR'] = 'NO'

  if (args['new_scratch']):
    os.environ['MOLCAS_NEW_WORKDIR'] = 'YES'

  if (args['old_scratch']):
    os.environ['MOLCAS_NEW_WORKDIR'] = 'NO'

  # Initialize the system
  if (args['files'] and args['filename']):
    fn = os.path.splitext(args['filename'])[0]
    if (not args['output']):
      args['output'] = '{0}.log'.format(fn)
    if (not args['error']):
      args['error'] = '{0}.err'.format(fn)

  try:
    Molcas = Molcas_wrapper(warning=warning, stamp=stamp, validate=args.get('validate'))
  except MolcasException as message:
    print(text_type(message), file=sys.stderr)
    return(1)

  if (not args['ignore_environment']):
    Molcas.read_environment()

  if (args['environment']):
    Molcas.show_environment()
    return(0)

  if (args['banner']):
    Molcas.print_banner()
    return(0)

  if (args['license']):
    rc = Molcas.check_license()
    if (rc is None):
      print('molcas.exe not found')
    elif (rc):
      print('The license is expired, missing or not valid')
    else:
      print('The license is valid')
      if (Molcas.licensee):
        print('Licensed to {0}'.format(Molcas.licensee))
    return(0)

  if (not args['filename']):
    parser.description = 'MOLCAS has been found at {0}'.format(Molcas.molcas)
    parser.print_help()
    return(0)
  else:
    # could this be a program from sbin?
    if (Molcas.in_sbin(args['filename'])):
      # make sure calls to "molcas" use this file
      try:
        rc = Molcas.run_sbin([args['filename']] + args['extra'])
        if (rc is not None):
          return(rc)
      except KeyboardInterrupt:
        return(1)
    # if not, it must be an input file
    try:
      attach_streams(output=args['output'], error=args['error'], buffer_size=args['buffer'])
    except IOError as e:
      print(text_type(e), file=sys.stderr)
      return(1)
    try:
      Molcas.read_input(args['filename'])
      if (args['validate']):
        Molcas.validate()
      else:
        Molcas.auto()
    except MolcasException as message:
      print(text_type(message), file=sys.stderr)
      # Skip verification with unsupported features
      if ('Unknown module' in text_type(message)):
        Molcas.rc = '_RC_NOT_AVAILABLE_'
      else:
        Molcas.rc = '_RC_JOB_KILLED_'
    except KeyboardInterrupt:
      for f in [sys.stdout, sys.stderr]:
        print('\nInterrupted by user\n', file=f)
      Molcas.rc = '_RC_JOB_KILLED_'

  return(Molcas.rc_num)

# Run pymolcas if not called via import
if (__name__ == '__main__'):
  from molcas_aux import which
  f = which(sys.argv[0])
  if (f is None):
    f = sys.argv[0]
  sys.exit(main(f))