Files
DicePlayer/diceplayer/DPpack/.SetGlobals.py
Vitor Hideyoshi 2a4e9eff0c Initial Translation of Gaussian Processes and Packaging of DicePlayer python module
This commit adds the methods that were present in the Gaussian.py file into the SetGlobals.py file and packages the program into a diceplayer module so it can be ran using 'python3 -m diceplayer'

Signed-off-by: Vitor Hideyoshi <vitor.h.n.batista@gmail.com>
2021-12-06 21:20:35 -03:00

814 lines
28 KiB
Python

import os, sys
import shutil
import textwrap
from DPpack.PTable import *
from DPpack.Misc import *
#### Global hashes that control the behaviour of Diceplayer
player = {}
dice = {}
gaussian = {}
molcas = {}
internal = {}
#######################################################################
#### Global parameters that MAY be given by the user ####
#### (If not given by the user, default values will be used) ####
#######################################################################
## Diceplayer:
player['maxcyc'] = 1
player['initcyc'] = 1 # May restart an optimization (append to geoms.xyz from start)
player['nprocs'] = 1
player['switchcyc'] = 3 # At which step start doing only one QM calculation (geom & chg)
player['altsteps'] = 20000 # Steps for thermalization when starting from previous step
player['maxstep'] = 0.3 # Maxstep for geometry relaxation in Bohr
player['qmprog'] = "g09"
player['opt'] = "yes"
player['freq'] = "no"
player['readhessian'] = "no"
player['lps'] = "no"
player['ghosts'] = "no"
player['vdwforces'] = "no"
player['tol_factor'] = 1.2 # Factor to multiply the default tolerance values
## Dice:
dice['title'] = "Diceplayer run"
dice['progname'] = "dice"
dice['temp'] = 300.0
dice['press'] = 1.0
dice['isave'] = 1000 # ASEC construction will take this into account
dice['ncores'] = 1
## Gaussian:
gaussian['mem'] = None
gaussian['keywords'] = None
gaussian['chgmult'] = [0, 1]
gaussian['gmiddle'] = None # In each case, if a filename is given, its content will be
gaussian['gbottom'] = None # inserted in the gaussian input
gaussian['pop'] = "chelpg"
gaussian['chglevel'] = None
## Molcas:
molcas['orbfile'] = "input.exporb"
molcas['root'] = 1
########################################################################
#### Global parameters that MUST be given by the user ####
########################################################################
## Dice:
dice['dens'] = None # Investigate the possibility of using 'box = Lx Ly Lz' instead.
#dice['box'] = None # So 'geom' would be set by diceplayer and 'cutoff' would be
# switched off. One of them must be given.
dice['ljname'] = None
dice['outname'] = None
dice['nmol'] = [] # Up to 4 integer values related to up to 4 molecule types
dice['nstep'] = [] # 2 or 3 integer values related to 2 or 3 simulations
# (NVT th + NVT eq) or (NVT th + NPT th + NPT eq).
# This will control the 'nstep' keyword of Dice
## Gaussian:
gaussian['level'] = None
## Molcas:
molcas['mbottom'] = None
molcas['basis'] = None
## The following Dice keywords will be handled automatically by Diceplayer:
## * init ("yes" in the first thermalization and "yesreadxyz" for thermalizations
## starting from a previous step / "no" in subsequent simulations)
## * vstep ("0" for NVT simulations and 'nstep'/5 for NPT simulations)
## * nstep ('nstep' for NVT and "5" for NPT simulations )
## * irdf ("0" for thermalizations and "10*nprocs" for equilibrium)
## * seed (will be generated randomly each time a Dice input is created)
## The following Dice keywords will be set constant by Diceplayer for all simulations
## * mstop = 1 (So to guarantee that the ASEC will be correctly built)
## * accum = no (There is never a simulation continuation in Diceplayer)
## * iprint = 1 (Print energy info every step in Dice output)
## All the other Dice keywords will not be altered from their default values
## and therefore are not mentioned in Diceplayer
#####################################################################
#### Global parameters that are not accessible to the user ####
#### (Intended to be used only internally by the program) ####
#####################################################################
## Diceplayer:
internal['tol_rms_force'] = 3e-4 # Hartree/Bohr
internal['tol_max_force'] = 4.5e-4 # Hartree/Bohr
internal['tol_rms_step'] = 1.2e-3 # Bohr
internal['tol_max_step'] = 1.8e-3 # Bohr
internal['trust_radius'] = None
## Dice:
internal['combrule'] = None
internal['randominit'] = None
## Other global variables:
molecules = [] # Armazena todas as informacoes sobre cada tipo de molecula
# (lbl, na, rx, ry, rz, chg, eps, sig, mass)
internal['ghost_types'] = []
internal['ghost_atoms'] = [] # Store the ghost atoms (off-atom charge sites) in the QM molecule
# (rx, ry, rz, chg)
internal['lp_types'] = []
internal['lp_atoms'] = [] # Store the lone pairs (special off-atom charge sites) in the QM molecule
# (rx, ry, rz, chg)
## Numpy arrays:
step = [] ## Values in Bohr
internal['position'] = []
internal['energy'] = [] ## Values in Hartree
internal['gradient'] = [] ## Values in Hartree/Bohr
internal['hessian'] = [] ## Values in Hartree/Bohr^2
## Conversion factors:
bohr2ang = 0.52917721092
ang2bohr = 1/bohr2ang
######################################################################
#### Environment variables important for executing Diceplayer ####
######################################################################
env = ["OMP_STACKSIZE"]
####################################### functions ######################################
## Functions to process the input files and store the values in the global variables ##
##########################################################################################
# def read_keywords(infile):
# try:
# with open(infile) as fh:
# controlfile = fh.readlines()
# except EnvironmentError:
# sys.exit("Error: cannot open file {}".format(infile))
# for line in controlfile:
# key, value = line.partition("=")[::2] # Discards the '='
# key = key.strip().lower()
# if key in ('title', 'keywords'):
# value = value.strip()
# else:
# value = value.split()
# #### Read the Diceplayer related keywords
# if key in player and len(value) != 0: ## 'value' is not empty!
# if key == 'qmprog' and value[0].lower() in ("g03", "g09", "g16", "molcas"):
# player[key] = value[0].lower()
# elif key == 'opt' and value[0].lower() in ("yes", "no", "ts"):
# player[key] = value[0].lower()
# #elif key == 'zipprog' and value[0].lower() in ("zip", "gzip", "bzip"):
# #player[key] = value[0].lower()
# elif key in ('lps', 'ghosts') and value[0].lower() in ("yes", "no"):
# player[key] = value[0].lower()
# elif key in ('readhessian', 'vdwforces') and value[0].lower() in ("yes", "no"):
# player[key] = value[0].lower()
# elif key in ('maxcyc', 'initcyc', 'nprocs', 'altsteps', 'switchcyc'):
# err = "Error: expected a positive integer for keyword {} in file {}".format(key, infile)
# try:
# new_value = int(value[0])
# if new_value >= 1:
# player[key] = new_value
# elif key == 'altsteps' and new_value == 0:
# player[key] = 0
# except ValueError:
# sys.exit(err)
# elif key == 'maxstep': # Cannot be less than 0.01
# err = "Error: expected a float greater than 0.01 for keyword {} in file {}".format(key, infile)
# try:
# new_value = float(value[0])
# if new_value < 0.01:
# sys.exit(err)
# else:
# player[key] = new_value
# except ValueError:
# sys.exit(err)
# #### Read the Dice related keywords
# elif key in dice and len(value) != 0: ## 'value' is not empty!
# if key == 'title':
# dice[key] = value
# elif key in ('ljname', 'outname', 'progname'):
# dice[key] = value[0]
# elif key in ('ncores', 'isave'):
# err = "Error: expected a positive integer for keyword {} in file {}".format(key, infile)
# if not value[0].isdigit():
# sys.exit(err)
# new_value = int(value[0])
# if new_value >= 1:
# dice[key] = new_value
# elif key in ('temp', 'press', 'dens'): # Cannot be less than 1e-10
# err = "Error: expected a positive float for keyword {} in file {}".format(key, infile)
# try:
# new_value = float(value[0])
# if new_value < 1e-10:
# sys.exit(err)
# else:
# dice[key] = new_value
# except ValueError:
# sys.exit(err)
# elif key == 'nmol': # If defined, must be well defined (only positive integer values)
# err = "Error: expected 1 to 4 positive integers for keyword {} in file {}".format(key, infile)
# args = min(4, len(value))
# for i in range(args):
# if value[i].isdigit():
# new_value = int(value[i])
# if new_value < 1:
# sys.exit(err)
# else:
# dice[key].append(new_value)
# elif i == 0:
# sys.exit(err)
# else:
# break
# elif key == 'nstep': # If defined, must be well defined (only positive integer values)
# err = "Error: expected 2 or 3 positive integers for keyword {} in file {}".format(key, infile)
# if len(value) < 2:
# sys.exit(err)
# args = min(3, len(value))
# for i in range(args):
# if value[i].isdigit():
# new_value = int(value[i])
# if new_value < 1:
# sys.exit(err)
# else:
# dice[key].append(new_value)
# elif i < 2:
# sys.exit(err)
# else:
# break
# #### Read the Gaussian related keywords
# elif key in gaussian and len(value) != 0: ## 'value' is not empty!
# if key == 'mem': # Memory in MB (minimum of 100)
# err = "Error: expected a positive integer for keyword {} in file {}".format(key, infile)
# if not value[0].isdigit():
# sys.exit(err)
# new_value = int(value[0])
# if new_value >= 100:
# gaussian[key] = new_value
# elif key == 'keywords':
# gaussian[key] = value
# elif key == 'chgmult': # If defined, must be well defined (2 integer values)
# err = "Error: expected 2 integers for keyword {} in file {}".format(key, infile)
# if len(value) < 2:
# sys.exit(err)
# for i in range (2):
# try:
# gaussian[key][i] = int(value[i])
# except ValueError:
# sys.exit(err)
# elif key in ('level', 'chglevel'):
# gaussian[key] = value[0]
# elif key in ('gmiddle', 'gbottom'):
# gaussian[key] = value[0]
# elif key == 'pop' and value[0].lower() in ("chelpg", "mk", "nbo"):
# gaussian[key] = value[0].lower()
# #### Read the Molcas related keywords
# elif key in molcas and len(value) != 0: ## 'value' is not empty!
# if key == 'root': # If defined, must be well defined (only positive integer values)
# err = "Error: expected a positive integer for keyword {} in file {}".format(key, infile)
# if not value[0].isdigit():
# sys.exit(err)
# new_value = int(value[0])
# if new_value >= 1:
# molcas[key] = new_value
# elif key in ('mbottom', 'orbfile'):
# molcas[key] = value[0]
# elif key == 'basis':
# molcas[key] = value[0]
# #### End
# return
# def check_keywords(infile):
# min_steps = 20000
# if dice['ljname'] == None:
# sys.exit("Error: 'ljname' keyword not specified in file {}".format(infile))
# if dice['outname'] == None:
# sys.exit("Error: 'outname' keyword not specified in file {}".format(infile))
# if dice['dens'] == None:
# sys.exit("Error: 'dens' keyword not specified in file {}".format(infile))
# if len(dice['nmol']) == 0:
# sys.exit("Error: 'nmol' keyword not defined appropriately in file {}".format(infile))
# if len(dice['nstep']) == 0:
# sys.exit("Error: 'nstep' keyword not defined appropriately in file {}".format(infile))
# ## Check only if QM program is Gaussian:
# if player['qmprog'] in ("g03", "g09", "g16"):
# if gaussian['level'] == None:
# sys.exit("Error: 'level' keyword not specified in file {}".format(infile))
# if gaussian['gmiddle'] != None:
# if not os.path.isfile(gaussian['gmiddle']):
# sys.exit("Error: file {} not found".format(gaussian['gmiddle']))
# if gaussian['gbottom'] != None:
# if not os.path.isfile(gaussian['gbottom']):
# sys.exit("Error: file {} not found".format(gaussian['gbottom']))
# if gaussian['pop'] != "chelpg" and (player['ghosts'] == "yes" or player['lps'] == "yes"):
# sys.exit("Error: ghost atoms or lone pairs only available with 'pop = chelpg')")
# if gaussian['chglevel'] == None:
# gaussian['chglevel'] = gaussian['level']
# ## Check only if QM program is Molcas:
# if player['qmprog'] == "molcas":
# if molcas['mbottom'] == None:
# sys.exit("Error: 'mbottom' keyword not specified in file {}".format(infile))
# else:
# if not os.path.isfile(molcas['mbottom']):
# sys.exit("Error: file {} not found".format(molcas['mbottom']))
# if molcas['basis'] == None:
# sys.exit("Error: 'basis' keyword not specified in file {}".format(infile))
# if player['altsteps'] != 0:
# ### Verifica se tem mais de 1 molecula QM
# ### (No futuro usar o RMSD fit para poder substituir todas as moleculas QM
# ### no arquivo outname.xy - Need to change the make_init_file!!)
# if dice['nmol'][0] > 1:
# sys.exit("Error: altsteps > 0 only possible with 1 QM molecule (nmol = 1 n2 n3 n4)")
# # if not zero, altsteps cannot be less than min_steps
# player['altsteps'] = max(min_steps, player['altsteps'])
# # altsteps value is always the nearest multiple of 1000
# player['altsteps'] = round(player['altsteps'] / 1000) * 1000
# for i in range(len(dice['nstep'])):
# # nstep can never be less than min_steps
# dice['nstep'][i] = max(min_steps, dice['nstep'][i])
# # nstep values are always the nearest multiple of 1000
# dice['nstep'][i] = round(dice['nstep'][i] / 1000) * 1000
# # isave must be between 100 and 2000
# dice['isave'] = max(100, dice['isave'])
# dice['isave'] = min(2000, dice['isave'])
# # isave value is always the nearest multiple of 100
# dice['isave'] = round(dice['isave'] / 100) * 100
# return
# def print_keywords(fh):
# fh.write("##########################################################################################\n"
# "############# Welcome to DICEPLAYER version 1.0 #############\n"
# "##########################################################################################\n"
# "\n")
# fh.write("Your python version is {}\n".format(sys.version))
# fh.write("\n")
# fh.write("Program started on {}\n".format(weekday_date_time()))
# fh.write("\n")
# fh.write("Environment variables:\n")
# for var in env:
# fh.write("{} = {}\n".format(var,
# (os.environ[var] if var in os.environ else "Not set")))
# fh.write("\n==========================================================================================\n"
# " CONTROL variables being used in this run:\n"
# "------------------------------------------------------------------------------------------\n"
# "\n")
# for key in sorted(player):
# if player[key] != None:
# if isinstance(player[key], list):
# string = " ".join(str(x) for x in player[key])
# fh.write("{} = {}\n".format(key, string))
# else:
# fh.write("{} = {}\n".format(key, player[key]))
# fh.write("\n")
# fh.write("------------------------------------------------------------------------------------------\n"
# " DICE variables being used in this run:\n"
# "------------------------------------------------------------------------------------------\n"
# "\n")
# for key in sorted(dice):
# if dice[key] != None:
# if isinstance(dice[key], list):
# string = " ".join(str(x) for x in dice[key])
# fh.write("{} = {}\n".format(key, string))
# else:
# fh.write("{} = {}\n".format(key, dice[key]))
# fh.write("\n")
# if player['qmprog'] in ("g03", "g09", "g16"):
# fh.write("------------------------------------------------------------------------------------------\n"
# " GAUSSIAN variables being used in this run:\n"
# "------------------------------------------------------------------------------------------\n"
# "\n")
# for key in sorted(gaussian):
# if gaussian[key] != None:
# if isinstance(gaussian[key], list):
# string = " ".join(str(x) for x in gaussian[key])
# fh.write("{} = {}\n".format(key, string))
# else:
# fh.write("{} = {}\n".format(key, gaussian[key]))
# fh.write("\n")
# elif player['qmprog'] == "molcas":
# fh.write("------------------------------------------------------------------------------------------\n"
# " MOLCAS variables being used in this run:\n"
# "------------------------------------------------------------------------------------------\n"
# "\n")
# for key in sorted(molcas):
# if molcas[key] != None:
# if isinstance(molcas[key], list):
# string = " ".join(str(x) for x in molcas[key])
# fh.write("{} = {}\n".format(key, string))
# else:
# fh.write("{} = {}\n".format(key, molcas[key]))
# fh.write("\n")
# return
# def read_potential(infile): # Deve ser atualizado para o uso de
# try:
# with open(dice['ljname']) as file:
# ljfile = file.readlines()
# except EnvironmentError as err:
# sys.exit(err)
# combrule = ljfile.pop(0).split()[0]
# if combrule not in ("*", "+"):
# sys.exit("Error: expected a '*' or a '+' sign in 1st line of file {}".format(dice['ljname']))
# dice['combrule'] = combrule
# ntypes = ljfile.pop(0).split()[0]
# if not ntypes.isdigit():
# sys.exit("Error: expected an integer in the 2nd line of file {}".format(dice['ljname']))
# ntypes = int(ntypes)
# if ntypes != len(dice['nmol']):
# sys.exit("Error: number of molecule types in file {} must match that of 'nmol' keyword in file {}".format(
# dice['ljname'], infile))
# line = 2
# for i in range(ntypes):
# line += 1
# nsites = ljfile.pop(0).split()[0]
# if not nsites.isdigit():
# sys.exit("Error: expected an integer in line {} of file {}".format(line, dice['ljname']))
# nsites = int(nsites)
# molecules.append([])
# for j in range(nsites):
# line += 1
# new_atom = ljfile.pop(0).split()
# if len(new_atom) < 8:
# sys.exit("Error: expected at least 8 fields in line {} of file {}".format(line, dice['ljname']))
# molecules[i].append({})
# if not new_atom[0].isdigit():
# sys.exit("Error: expected an integer in field 1, line {} of file {}".format(line, dice['ljname']))
# molecules[i][j]['lbl'] = int(new_atom[0])
# if not new_atom[1].isdigit():
# sys.exit("Error: expected an integer in field 2, line {} of file {}".format(line, dice['ljname']))
# atnumber = int(new_atom[1])
# if atnumber == ghost_number and i == 0: # Ghost atom not allowed in the QM molecule
# sys.exit("Error: found a ghost atom in line {} of file {}".format(line, dice['ljname']))
# molecules[i][j]['na'] = atnumber
# try:
# molecules[i][j]['rx'] = float(new_atom[2])
# except:
# sys.exit("Error: expected a float in field 3, line {} of file {}".format(line, dice['ljname']))
# try:
# molecules[i][j]['ry'] = float(new_atom[3])
# except:
# sys.exit("Error: expected a float in field 4, line {} of file {}".format(line, dice['ljname']))
# try:
# molecules[i][j]['rz'] = float(new_atom[4])
# except:
# sys.exit("Error: expected a float in field 5, line {} of file {}".format(line, dice['ljname']))
# try:
# molecules[i][j]['chg'] = float(new_atom[5])
# except:
# sys.exit("Error: expected a float in field 6, line {} of file {}".format(line, dice['ljname']))
# try:
# molecules[i][j]['eps'] = float(new_atom[6])
# except:
# sys.exit("Error: expected a float in field 7, line {} of file {}".format(line, dice['ljname']))
# try:
# molecules[i][j]['sig'] = float(new_atom[7])
# except:
# sys.exit("Error: expected a float in field 8, line {} of file {}".format(line, dice['ljname']))
# molecules[i][j]['mass'] = atommass[molecules[i][j]['na']]
# if len(new_atom) > 8:
# masskey, mass = new_atom[8].partition("=")[::2]
# if masskey.lower() == 'mass' and len(mass) !=0:
# try:
# new_mass = float(mass)
# if new_mass > 0:
# molecules[i][j]['mass'] = new_mass
# except:
# sys.exit(
# "Error: expected a positive float after 'mass=' in field 9, line {} of file {}".format(
# line, dice['ljname']))
# return
def read_ghosts():
max_atom_number = len(molecules[0])
try:
with open("ghosts.in") as fh:
ghostfile = fh.readlines()
except EnvironmentError:
sys.exit("Error: cannot open file ghosts.in")
for line in ghostfile:
if len(line.split()) > 1: # Discard lines with less than 2 fields
key, *atom_numbers = line.split()
key = key.lower()
if key in ("g", "m", "z"): # Discard lines that do not start with g|m|z
ghost_types.append({})
ghost_types[-1]['type'] = key
ghost_types[-1]['numbers'] = []
for num in atom_numbers:
if not num.isdigit():
sys.exit("Error: in file ghosts.in: only positive integers allowed after letter g|m|z")
new_num = int(num)
if new_num > max_atom_number:
sys.exit("Error: in file ghosts.in: there is no atom number {}".format(new_num))
else:
ghost_types[-1]['numbers'].append(new_num)
if len(ghost_types[-1]['numbers']) < 2:
sys.exit("Error: in file ghosts.in: at least 2 atoms are necessary to make a ghost")
if len(ghost_types) == 0:
sys.exit("Error: no ghost atom found in ghosts.in")
return
def read_lps():
lp_alpha = 104.0 # Default values
lp_dist = 0.7 #
max_lp_type = 2
min_alpha = 90.0
max_alpha = 150.0
min_dist = 0.5
max_dist = 1.5
max_atom_number = len(molecules[0])
try:
with open("lps.in") as fh:
lpfile = fh.readlines()
except EnvironmentError:
sys.exit("Error: cannot open file lps.in")
for line in lpfile:
if len(line.split()) > 1: # Discard lines with less than 2 fields
type, *atom_numbers = line.split()
if type.isdigit(): # Discard lines that do not start with an integer
new_type = int(type)
if new_type > max_lp_type:
sys.exit("Error: in file lps.in: allowed LP types from 1 to {}".format(max_lp_type))
lp_types.append({})
lp_types[-1]['type'] = new_type
lp_types[-1]['numbers'] = []
# Read types 1 and 2
if new_type in (1, 2):
if len(atom_numbers) < 3:
sys.exit("Error: in file lps.in: at least 3 atoms are necessary to make LPs type 1 and 2")
for i in range(3):
num = atom_numbers.pop(0)
if not num.isdigit():
sys.exit("Error: in file lps.in: expected 3 atom numbers after LPs type 1 and 2")
new_num = int(num)
if new_num > max_atom_number or new_num < 1:
sys.exit("Error: in file lps.in: there is no atom number {}".format(new_num))
else:
lp_types[-1]['numbers'].append(new_num)
lp_types[-1]['alpha'] = lp_alpha
lp_types[-1]['dist'] = lp_dist
if len(atom_numbers) != 0:
try:
alpha = float(atom_numbers.pop(0))
if alpha > min_alpha and alpha < max_alpha:
lp_types[-1]['alpha'] = alpha
else:
atom_numbers = []
except:
atom_numbers = []
if len(atom_numbers) != 0:
try:
dist = float(atom_numbers.pop(0))
if dist > min_dist and dist < max_dist:
lp_types[-1]['dist'] = dist
except:
None
# End of types 1 and 2
if len(lp_types) == 0:
sys.exit("Error: no lone pair found in lps.in")
return
# def print_potential(fh):
# formatstr = "{:<3d} {:>3d} {:>10.5f} {:>10.5f} {:>10.5f} {:>10.6f} {:>9.5f} {:>7.4f} {:>9.4f}\n"
# fh.write("\n"
# "==========================================================================================\n")
# fh.write(" Potential parameters from file {}:\n".format(dice['ljname']))
# fh.write("------------------------------------------------------------------------------------------\n"
# "\n")
# fh.write("Combination rule: {}\n".format(dice['combrule']))
# fh.write("Types of molecules: {}\n\n".format(len(molecules)))
# i = 0
# for mol in molecules:
# i += 1
# fh.write("{} atoms in molecule type {}:\n".format(len(mol), i))
# fh.write("---------------------------------------------------------------------------------\n"
# "Lbl AN X Y Z Charge Epsilon Sigma Mass\n")
# fh.write("---------------------------------------------------------------------------------\n")
# for atom in mol:
# fh.write(formatstr.format(atom['lbl'], atom['na'], atom['rx'], atom['ry'], atom['rz'],
# atom['chg'], atom['eps'], atom['sig'], atom['mass']))
# fh.write("\n")
# if player['ghosts'] == "yes" or player['lps'] == "yes":
# fh.write("\n"
# "------------------------------------------------------------------------------------------\n"
# " Aditional potential parameters:\n"
# "------------------------------------------------------------------------------------------\n")
# if player['ghosts'] == "yes":
# fh.write("\n")
# fh.write("{} ghost atoms appended to molecule type 1 at:\n".format(len(ghost_types)))
# fh.write("---------------------------------------------------------------------------------\n")
# atoms_string = ""
# for ghost in ghost_types:
# for atom in ghost['numbers']:
# atom_sym = atomsymb[ molecules[0][atom - 1]['na'] ].strip()
# atoms_string += "{}{} ".format(atom_sym,atom)
# if ghost['type'] == "g":
# fh.write(textwrap.fill("* Geometric center of atoms {}".format(atoms_string), 80))
# elif ghost['type'] == "m":
# fh.write(textwrap.fill("* Center of mass of atoms {}".format(atoms_string), 80))
# elif ghost['type'] == "z":
# fh.write(textwrap.fill("* Center of atomic number of atoms {}".format(atoms_string), 80))
# fh.write("\n")
# if player['lps'] == 'yes':
# fh.write("\n")
# fh.write("{} lone pairs appended to molecule type 1:\n".format(len(lp_types)))
# fh.write("---------------------------------------------------------------------------------\n")
# for lp in lp_types:
# # LP type 1 or 2
# if lp['type'] in (1, 2):
# atom1_num = lp['numbers'][0]
# atom1_sym = atomsymb[ molecules[0][atom1_num - 1]['na'] ].strip()
# atom2_num = lp['numbers'][1]
# atom2_sym = atomsymb[ molecules[0][atom2_num - 1]['na'] ].strip()
# atom3_num = lp['numbers'][2]
# atom3_sym = atomsymb[ molecules[0][atom3_num - 1]['na'] ].strip()
# fh.write(textwrap.fill(
# "* Type {} on atom {}{} with {}{} {}{}. Alpha = {:<5.1f} Deg and D = {:<4.2f} Angs".format(
# lp['type'], atom1_sym, atom1_num, atom2_sym, atom2_num, atom3_sym, atom3_num, lp['alpha'],
# lp['dist']), 86))
# fh.write("\n")
# # Other LP types
# fh.write("\n"
# "==========================================================================================\n")
# return
## Creation of continue_function
# def check_executables(fh):
# fh.write("\n")
# fh.write(90 * "=")
# fh.write("\n\n")
# dice_path = shutil.which(dice['progname'])
# if dice_path != None:
# fh.write("Program {} found at {}\n".format(dice['progname'], dice_path))
# else:
# sys.exit("Error: cannot find dice executable")
# qmprog_path = shutil.which(player['qmprog'])
# if qmprog_path != None:
# fh.write("Program {} found at {}\n".format(player['qmprog'], qmprog_path))
# else:
# sys.exit("Error: cannot find {} executable".format(player['qmprog']))
# if player['qmprog'] in ("g03", "g09", "g16"):
# formchk_path = shutil.which("formchk")
# if formchk_path != None:
# fh.write("Program formchk found at {}\n".format(formchk_path))
# else:
# sys.exit("Error: cannot find formchk executable")
# return