################################################################################################################################################
## The Renderer ##
## ##
## This script contains the rendering functions for ABIN LAUNCHER, ##
## consult the documentation at https://chains-ulb.readthedocs.io/ for details ##
################################################################################################################################################
import os
import yaml
from jinja2 import Environment, FileSystemLoader
import abin_errors
[docs]def jinja_render(templates_dir:str, template_file:str, render_vars:dict):
"""Renders a file based on its Jinja template.
Parameters
----------
templates_dir : str
The path towards the directory where the Jinja template is located.
template_file : str
The name of the Jinja template file.
render_vars : dict
Dictionary containing the definitions of all the variables present in the Jinja template.
Returns
-------
output_text : str
Content of the rendered file.
"""
file_loader = FileSystemLoader(templates_dir)
env = Environment(loader=file_loader)
template = env.get_template(template_file)
output_text = template.render(render_vars)
return output_text
# =================================================================== #
# =================================================================== #
# Rendering functions #
# =================================================================== #
# =================================================================== #
def basic_rendering(mendeleev:dict, clusters_cfg:dict, config:dict, file_data:dict, job_specs:dict, misc:dict):
"""Renders the job script and the input file associated with simple jobs. The templates filenames are specified in the YAML configuration file and the jinja variables are taken as is from it (outside of the variables related to the clusters YAML configuration file).
Parameters
----------
mendeleev : dict
Content of AlexGustafsson's Mendeleev Table YAML file (found at https://github.com/AlexGustafsson/molecular-data).
Unused in this function.
clusters_cfg : dict
Content of the YAML clusters configuration file.
config : dict
Content of the YAML configuration file.
file_data : dict
Information extracted by the scanning function from the geometry file.
job_specs : dict
Contains all information related to the job.
misc : dict
Contains all the additional variables that did not pertain to the other arguments.
Returns
-------
rendered_content : dict
Dictionary containing the text of all the rendered files in the form of <filename>: <rendered_content>.
rendered_script : str
Name of the rendered job script, necessary to launch the job.
Notes
-----
Pay a particular attention to the render_vars dictionaries, they contain all the definitions of the variables appearing in your Jinja templates.
"""
# ========================================================= #
# Preparation step #
# ========================================================= #
# Check config file
# =================
# Check if a "templates" block has been defined in the config file
if not config.get('templates'):
raise abin_errors.AbinError ('ERROR: There is no "templates" key defined in the "%s" configuration file.' % misc['config_name'])
# Check if a "jinja_variables" block has been defined in the config file
if not config.get('jinja_variables'):
raise abin_errors.AbinError ('ERROR: There is no "jinja_variables" key defined in the "%s" configuration file.' % misc['config_name'])
# Define the templates
# ====================
# Define the names of the templates.
try:
template_input = config['templates']['input']
template_script = config['templates']['job_script']
except KeyError as error:
raise abin_errors.AbinError ('ERROR: The "%s" key is missing in the "templates" block of the "%s" configuration file.' % (error,misc['config_name']))
# Check if the specified templates exist in the "templates" directory of ABIN LAUNCHER.
abin_errors.check_abspath(os.path.join(misc['templates_dir'],template_input),"Jinja template for the input file","file")
abin_errors.check_abspath(os.path.join(misc['templates_dir'],template_script),"Jinja template for the job script","file")
# Define rendered files
# =====================
# Define the names of the rendered files.
rendered_input = misc['mol_name'] + os.path.splitext(template_input.rstrip(".jinja"))[1]
rendered_script = template_script.rstrip(".jinja")
# Initialize the dictionary that will be returned by the function
rendered_content = {}
# ========================================================= #
# Rendering the input file #
# ========================================================= #
print("{:<80}".format("\nRendering the jinja template for the input file ... "), end="")
# Preparing the Jinja variables
# =============================
# Variables given by the config file
input_render_vars = config['jinja_variables']
# Other useful variables
input_render_vars.update({
"mol_name" : misc['mol_name'],
"config_file" : misc['config_name'],
"coordinates" : file_data['atomic_coordinates'],
"job_cores" : job_specs['cores'],
"job_mem_per_cpu" : job_specs['mem_per_cpu'] # in MB
})
# Rendering the file
# ==================
rendered_content[rendered_input] = jinja_render(misc['templates_dir'], template_input, input_render_vars)
print('%12s' % "[ DONE ]")
# ========================================================= #
# Rendering the job script #
# ========================================================= #
# Preparing the Jinja variables
# =============================
# Variables given by the config file
script_render_vars = config['jinja_variables']
# Variables not associated with the config file
script_render_vars.update({
"mol_name" : misc['mol_name'],
"config_file" : misc['config_name'],
"job_script" : rendered_script,
"job_walltime" : job_specs['walltime'],
"job_cores" : job_specs['cores'],
"job_mem_per_cpu" : job_specs['mem_per_cpu'], # in MB
"cluster_name" : job_specs['cluster_name'],
"partition" : job_specs['partition']
})
# Variables associated with the clusters configuration file
try:
script_render_vars.update({
"set_env" : clusters_cfg[job_specs['cluster_name']]['profiles'][job_specs['profile']]['set_env'],
"command" : clusters_cfg[job_specs['cluster_name']]['profiles'][job_specs['profile']]['command']
})
except KeyError as error:
raise abin_errors.AbinError ('ERROR: The "%s" key is missing in the "%s" profile of the clusters configuration file.' % (error,job_specs['profile']))
# Rendering the file
# ==================
rendered_content[rendered_script] = jinja_render(misc['templates_dir'], template_script, script_render_vars)
print('%12s' % "[ DONE ]")
return rendered_content, rendered_script
######################################################################################################################################
def chains_gaussian_render(mendeleev:dict, clusters_cfg:dict, config:dict, file_data:dict, job_specs:dict, misc:dict):
"""Renders the job script and the input file associated with the GAUSSIAN program in CHAINS.
Parameters
----------
mendeleev : dict
Content of AlexGustafsson's Mendeleev Table YAML file (found at https://github.com/AlexGustafsson/molecular-data).
Unused in this function.
clusters_cfg : dict
Content of the YAML clusters configuration file.
config : dict
Content of the YAML configuration file.
file_data : dict
Information extracted by the scanning function from the geometry file.
job_specs : dict
Contains all information related to the job.
misc : dict
Contains all the additional variables that did not pertain to the other arguments.
Returns
-------
rendered_content : dict
Dictionary containing the text of all the rendered files in the form of <filename>: <rendered_content>.
rendered_script : str
Name of the rendered job script, necessary to launch the job.
Notes
-----
Pay a particular attention to the render_vars dictionaries, they contain all the definitions of the variables appearing in your Jinja templates.
"""
# ========================================================= #
# Preparation step #
# ========================================================= #
# Check config file
# =================
# Check if a "general" block has been defined in the config file
if not config.get('general'):
raise abin_errors.AbinError ('ERROR: There is no "general" key defined in the "%s" configuration file.' % misc['config_name'])
# Check if a "gaussian" block has been defined in the config file
if not config.get('gaussian'):
raise abin_errors.AbinError ('ERROR: There is no "gaussian" key defined in the "%s" configuration file.' % misc['config_name'])
# Check the options defined in the config file
auto_restart = config['gaussian'].get('auto_restart',False)
if not isinstance(auto_restart, bool):
raise abin_errors.AbinError ('ERROR: The "auto_restart" value given in the "gaussian" block of the "%s" configuration file is not a boolean (neither "True" nor "False").' % misc['config_name'])
benchmark = config['gaussian'].get('benchmark',False)
if not isinstance(benchmark, bool):
raise abin_errors.AbinError ('ERROR: The "benchmark" value given in the "gaussian" block of the "%s" configuration file is not a boolean (neither "True" nor "False").' % misc['config_name'])
copy_files = config['gaussian'].get('copy_files',False)
if not isinstance(copy_files, bool):
raise abin_errors.AbinError ('ERROR: The "copy_files" value given in the "gaussian" block of the "%s" configuration file is not a boolean (neither "True" nor "False").' % misc['config_name'])
if copy_files:
ip_calc = str(config['gaussian'].get('ip_calc',"no_key")).lower()
if ip_calc == "no_key":
# If there was no "ip_calc" key in the config file, use the scale index to define how the ionization potential will be calculated
if job_specs['scale_index'] > 650:
ip_calc = "vertical"
else:
ip_calc = "adiabatic"
elif ip_calc not in ["none","vertical","adiabatic"]:
raise abin_errors.AbinError ('ERROR: The "ip_calc" value given in the "gaussian" block of the "%s" configuration file is neither "None", "Vertical" nor "Adiabatic" (This is not case sensitive).' % misc['config_name'])
else:
ip_calc = None
# Define the templates
# ====================
# Define the names of the templates.
template_input = "gaussian.com.jinja"
template_script = "gaussian_job.sh.jinja"
# Check if the specified templates exist in the "templates" directory of ABIN LAUNCHER.
abin_errors.check_abspath(os.path.join(misc['templates_dir'],template_input),"Jinja template for the gaussian input file","file")
abin_errors.check_abspath(os.path.join(misc['templates_dir'],template_script),"Jinja template for the gaussian job script","file")
# Define rendered files
# =====================
# Define the names of the rendered files.
rendered_input = misc['mol_name'] + ".com"
rendered_script = "gaussian_job.sh"
# Initialize the dictionary that will be returned by the function
rendered_content = {}
# ========================================================= #
# Rendering the input file #
# ========================================================= #
print("{:<80}".format("\nRendering the jinja template for the gaussian input file ... "), end="")
# Defining the mandatory Jinja variables
# ======================================
# Variables not associated with the config file
input_render_vars = {
"mol_name" : misc['mol_name'],
"mem_total" : job_specs['cores'] * job_specs['mem_per_cpu'],
"job_cores" : job_specs['cores'],
"coordinates" : file_data['atomic_coordinates'],
"ip_calc" : ip_calc # Associated with the config file, but it has already been verified
}
# Variables associated with the "general" block of the config file
try:
input_render_vars.update({
"charge" : config['general']['charge'],
"multiplicity" : config['general']['multiplicity']
})
except KeyError as error:
raise abin_errors.AbinError ('ERROR: The "%s" key is missing in the "general" block of the "%s" configuration file.' % (error,misc['config_name']))
# Check if a "keywords" block has been defined in the "gaussian" block of the config file
if not config['gaussian'].get('keywords'):
raise abin_errors.AbinError ('ERROR: There is no "keywords" key in the "gaussian" block of the "%s" configuration file.' % misc['config_name'])
# Variables associated with the "keywords" block of the "gaussian" block in the config file
try:
input_render_vars.update({
"method" : config['gaussian']['keywords']['method'],
"basis_set" : config['gaussian']['keywords']['basis_set'],
"other" : config['gaussian']['keywords']['other']
})
except KeyError as error:
raise abin_errors.AbinError ('ERROR: The "%s" key is missing in the "keywords" block of the "gaussian" block in the "%s" configuration file.' % (error,misc['config_name']))
# Defining the specific Jinja variables
# =====================================
# Variables specific to the ip_calc == vertical portion of the template
if ip_calc == "vertical":
# Determine the new charge and multiplicity of the cation
charge_cation = int(config['general']['charge']) + 1
if int(config['general']['multiplicity']) == 1: # In the case of a singlet ground state, we break a pair of electrons and thus "create" a new unpaired electron
multiplicity_cation = 2
else:
multiplicity_cation = int(config['general']['multiplicity']) - 1 # We removed one unpaired electron
# Variables associated with the config file but they have already been verified
input_render_vars.update({
"charge_cation" : charge_cation,
"multiplicity_cation" : multiplicity_cation
})
# Rendering the file
# ==================
rendered_content[rendered_input] = jinja_render(misc['templates_dir'], template_input, input_render_vars)
print('%12s' % "[ DONE ]")
# ========================================================= #
# Rendering the job script #
# ========================================================= #
# Get the path to the "check_scripts" directory because the job script needs to execute gaussian_check.py
chains_path = os.path.dirname(misc['code_dir'])
check_script_path = os.path.join(chains_path,"check_scripts")
# If we need to copy the output files to their respective results directory (or compute the ionization potentials through the cation), load the CHAINS configuration file to get the necessary information
if copy_files:
chains_config_file = abin_errors.check_abspath(os.path.join(chains_path,"configs","chains_config.yml"),"CHAINS configuration YAML file","file")
print ("{:<80}".format("\nLoading CHAINS configuration YAML file ..."), end="")
with open(chains_config_file, 'r') as chains:
chains_config = yaml.load(chains, Loader=yaml.FullLoader)
print('%12s' % "[ DONE ]")
print("{:<80}".format("\nRendering the jinja template for the gaussian job script ..."), end="")
# Defining the mandatory Jinja variables
# ======================================
# Variables not associated with the config file
script_render_vars = {
"mol_name" : misc['mol_name'],
"config_file" : misc['config_name'],
"job_walltime" : job_specs['walltime'],
"job_cores" : job_specs['cores'],
"job_mem_per_cpu" : job_specs['mem_per_cpu'], # in MB
"cluster_name" : job_specs['cluster_name'],
"partition" : job_specs['partition'],
"chains_dir" : chains_path,
"check_dir" : check_script_path,
"auto_restart" : auto_restart, # Associated with the config file, but it has already been verified
"copy_files" : copy_files, # Associated with the config file, but it has already been verified
"benchmark" : benchmark # Associated with the config file, but it has already been verified
}
# Variables associated with the "general" block of the config file
try:
script_render_vars.update({
"user_email" : config['general']['user_email'],
"mail_type" : config['general']['mail_type']
})
except KeyError as error:
raise abin_errors.AbinError ('ERROR: The "%s" key is missing in the "general" block of the "%s" configuration file.' % (error,misc['config_name']))
# Variables associated with the clusters configuration file
try:
script_render_vars.update({
"set_env" : clusters_cfg[job_specs['cluster_name']]['profiles'][job_specs['profile']]['set_env'],
"command" : clusters_cfg[job_specs['cluster_name']]['profiles'][job_specs['profile']]['command']
})
except KeyError as error:
raise abin_errors.AbinError ('ERROR: The "%s" key is missing in the "%s" profile of the clusters configuration file.' % (error,job_specs['profile']))
# Defining the specific Jinja variables
# =====================================
# Variables specific to the copy_files portion of the template
if copy_files:
# Variables not associated with the config file
script_render_vars.update({
"ip_calc" : ip_calc, # Associated with the config file, but it has already been verified
"job_script" : rendered_script
})
# Variables associated with the CHAINS configuration file
try:
script_render_vars.update({
"output_dir" : chains_config['output_gaussian'],
"results_dir" : chains_config['results_dir']
})
if ip_calc == 'vertical' or ip_calc == 'adiabatic':
script_render_vars.update({
"ip_file" : chains_config['ip_file']
})
except KeyError as error:
raise abin_errors.AbinError ('ERROR: The "%s" key is missing in the CHAINS configuration file (chains_config.yml).' % error)
# Variables specific to the benchmarking template
if benchmark:
script_render_vars.update({
"benchmark_path" : "${CECIHOME}/BENCHMARK",
"prefix": job_specs['profile'] + "_" + job_specs['cluster_name'],
"profile" : job_specs['profile'],
"cluster_name" : job_specs['cluster_name'],
"jobscale_label" : job_specs['scale_label'],
"job_walltime" : job_specs['walltime'],
"job_mem_per_cpu" : job_specs['mem_per_cpu'], # in MB
"scaling_function" : job_specs['scaling_fct'],
"scale_index" : job_specs['scale_index']
})
# Rendering the file
# ==================
rendered_content[rendered_script] = jinja_render(misc['templates_dir'], template_script, script_render_vars)
print('%12s' % "[ DONE ]")
return rendered_content, rendered_script
######################################################################################################################################
def chains_gaussian_cation_render(mendeleev:dict, clusters_cfg:dict, config:dict, file_data:dict, job_specs:dict, misc:dict):
"""Renders the job script and the input file associated with cation calculations using the GAUSSIAN program in CHAINS.
Parameters
----------
mendeleev : dict
Content of AlexGustafsson's Mendeleev Table YAML file (found at https://github.com/AlexGustafsson/molecular-data).
Unused in this function.
clusters_cfg : dict
Content of the YAML clusters configuration file.
config : dict
Content of the YAML configuration file.
file_data : dict
Information extracted by the scanning function from the geometry file.
job_specs : dict
Contains all information related to the job.
misc : dict
Contains all the additional variables that did not pertain to the other arguments.
Returns
-------
rendered_content : dict
Dictionary containing the text of all the rendered files in the form of <filename>: <rendered_content>.
rendered_script : str
Name of the rendered job script, necessary to launch the job.
Notes
-----
Pay a particular attention to the render_vars dictionaries, they contain all the definitions of the variables appearing in your Jinja templates.
"""
# ========================================================= #
# Preparation step #
# ========================================================= #
# Check config file
# =================
# Check if a "general" block has been defined in the config file
if not config.get('general'):
raise abin_errors.AbinError ('ERROR: There is no "general" key defined in the "%s" configuration file.' % misc['config_name'])
# Check if a "gaussian" block has been defined in the config file
if not config.get('gaussian'):
raise abin_errors.AbinError ('ERROR: There is no "gaussian" key defined in the "%s" configuration file.' % misc['config_name'])
# Check the options defined in the config file
benchmark = config['gaussian'].get('benchmark',False)
if not isinstance(benchmark, bool):
raise abin_errors.AbinError ('ERROR: The "benchmark" value given in the "gaussian" block of the "%s" configuration file is not a boolean (neither "True" nor "False").' % misc['config_name'])
# Define the templates
# ====================
# Define the names of the templates.
template_input = "gaussian_cation.com.jinja"
template_script = "gaussian_cation_job.sh.jinja"
# Check if the specified templates exist in the "templates" directory of ABIN LAUNCHER.
abin_errors.check_abspath(os.path.join(misc['templates_dir'],template_input),"Jinja template for the gaussian cation input file","file")
abin_errors.check_abspath(os.path.join(misc['templates_dir'],template_script),"Jinja template for the gaussian cation job script","file")
# Define rendered files
# =====================
# Define the names of the rendered files.
rendered_input = misc['mol_name'] + ".com"
rendered_script = "gaussian_cation_job.sh"
# Initialize the dictionary that will be returned by the function
rendered_content = {}
# ========================================================= #
# Rendering the input file #
# ========================================================= #
print("{:<80}".format("\nRendering the jinja template for the gaussian cation input file ... "), end="")
# Defining the mandatory Jinja variables
# ======================================
# Variables not associated with the config file
input_render_vars = {
"mol_name" : misc['mol_name'],
"mem_total" : job_specs['cores'] * job_specs['mem_per_cpu'],
"job_cores" : job_specs['cores'],
"coordinates" : file_data['atomic_coordinates']
}
# Variables associated with the "general" block of the config file
try:
charge = int(config['general']['charge'])
multiplicity = int(config['general']['multiplicity'])
except KeyError as error:
raise abin_errors.AbinError ('ERROR: The "%s" key is missing in the "general" block of the "%s" configuration file.' % (error,misc['config_name']))
# Determine the new charge and multiplicity of the cation
charge_cation = charge + 1
if multiplicity == 1: # In the case of a singlet ground state, we break a pair of electrons and thus "create" a new unpaired electron
multiplicity_cation = 2
else:
multiplicity_cation = multiplicity - 1 # We removed one unpaired electron
# Add the new variables
input_render_vars.update({
"charge" : charge,
"multiplicity" : multiplicity,
"charge_cation" : charge_cation,
"multiplicity_cation" : multiplicity_cation
})
# Check if a "keywords" block has been defined in the "gaussian" block of the config file
if not config['gaussian'].get('keywords'):
raise abin_errors.AbinError ('ERROR: There is no "keywords" key in the "gaussian" block of the "%s" configuration file.' % misc['config_name'])
# Variables associated with the "keywords" block of the "gaussian" block in the config file
try:
input_render_vars.update({
"method" : config['gaussian']['keywords']['method'],
"basis_set" : config['gaussian']['keywords']['basis_set'],
"other" : config['gaussian']['keywords']['other']
})
except KeyError as error:
raise abin_errors.AbinError ('ERROR: The "%s" key is missing in the "keywords" block of the "gaussian" block in the "%s" configuration file.' % (error,misc['config_name']))
# Rendering the file
# ==================
rendered_content[rendered_input] = jinja_render(misc['templates_dir'], template_input, input_render_vars)
print('%12s' % "[ DONE ]")
# ========================================================= #
# Rendering the job script #
# ========================================================= #
# Get the path to the "check_scripts" directory because the job script needs to execute gaussian_check.py
chains_path = os.path.dirname(misc['code_dir'])
check_script_path = os.path.join(chains_path,"check_scripts")
# Load the CHAINS configuration file to get the path towards the IP file
chains_config_file = abin_errors.check_abspath(os.path.join(chains_path,"configs","chains_config.yml"),"CHAINS configuration YAML file","file")
print ("{:<80}".format("\nLoading CHAINS configuration YAML file ..."), end="")
with open(chains_config_file, 'r') as chains:
chains_config = yaml.load(chains, Loader=yaml.FullLoader)
print('%12s' % "[ DONE ]")
print("{:<80}".format("\nRendering the jinja template for the gaussian job script ..."), end="")
# Defining the mandatory Jinja variables
# ======================================
# Variables not associated with the config file
script_render_vars = {
"mol_name" : misc['mol_name'],
"config_file" : misc['config_name'],
"job_walltime" : job_specs['walltime'],
"job_cores" : job_specs['cores'],
"job_mem_per_cpu" : job_specs['mem_per_cpu'], # in MB
"cluster_name" : job_specs['cluster_name'],
"partition" : job_specs['partition'],
"chains_dir" : chains_path,
"check_dir" : check_script_path,
"benchmark" : benchmark # Associated with the config file, but it has already been verified
}
# Variables associated with the "general" block of the config file
try:
script_render_vars.update({
"user_email" : config['general']['user_email'],
"mail_type" : config['general']['mail_type']
})
except KeyError as error:
raise abin_errors.AbinError ('ERROR: The "%s" key is missing in the "general" block of the "%s" configuration file.' % (error,misc['config_name']))
# Variables associated with the clusters configuration file
try:
script_render_vars.update({
"set_env" : clusters_cfg[job_specs['cluster_name']]['profiles'][job_specs['profile']]['set_env'],
"command" : clusters_cfg[job_specs['cluster_name']]['profiles'][job_specs['profile']]['command']
})
except KeyError as error:
raise abin_errors.AbinError ('ERROR: The "%s" key is missing in the "%s" profile of the clusters configuration file.' % (error,job_specs['profile']))
# Variables associated with the CHAINS configuration file
try:
script_render_vars.update({
"ip_file" : chains_config['ip_file']
})
except KeyError as error:
raise abin_errors.AbinError ('ERROR: The "%s" key is missing in the CHAINS configuration file (chains_config.yml).' % error)
# Defining the specific Jinja variables
# =====================================
# Variables specific to the benchmarking template
if benchmark:
script_render_vars.update({
"benchmark_path" : "${CECIHOME}/BENCHMARK",
"prefix": job_specs['profile'] + "_" + job_specs['cluster_name'],
"profile" : job_specs['profile'],
"cluster_name" : job_specs['cluster_name'],
"jobscale_label" : job_specs['scale_label'],
"job_walltime" : job_specs['walltime'],
"job_mem_per_cpu" : job_specs['mem_per_cpu'], # in MB
"scaling_function" : job_specs['scaling_fct'],
"scale_index" : job_specs['scale_index']
})
# Rendering the file
# ==================
rendered_content[rendered_script] = jinja_render(misc['templates_dir'], template_script, script_render_vars)
print('%12s' % "[ DONE ]")
return rendered_content, rendered_script
######################################################################################################################################
def chains_qchem_render(mendeleev:dict, clusters_cfg:dict, config:dict, file_data:dict, job_specs:dict, misc:dict):
"""Renders the job script and the input file associated with the Q-CHEM program in CHAINS.
Parameters
----------
mendeleev : dict
Content of AlexGustafsson's Mendeleev Table YAML file (found at https://github.com/AlexGustafsson/molecular-data).
Unused in this function.
clusters_cfg : dict
Content of the YAML clusters configuration file.
config : dict
Content of the YAML configuration file.
file_data : dict
Information extracted by the scanning function from the geometry file.
job_specs : dict
Contains all information related to the job.
misc : dict
Contains all the additional variables that did not pertain to the other arguments.
Returns
-------
rendered_content : dict
Dictionary containing the text of all the rendered files in the form of <filename>: <rendered_content>.
rendered_script : str
Name of the rendered job script, necessary to launch the job.
Notes
-----
Pay a particular attention to the render_vars dictionaries, they contain all the definitions of the variables appearing in your Jinja templates.
"""
# ========================================================= #
# Preparation step #
# ========================================================= #
# Check config file
# =================
# Check if a "general" block has been defined in the config file
if not config.get('general'):
raise abin_errors.AbinError ('ERROR: There is no "general" key defined in the "%s" configuration file.' % misc['config_name'])
# Check if a "qchem" block has been defined in the config file
if not config.get('qchem'):
raise abin_errors.AbinError ('ERROR: There is no "qchem" key defined in the "%s" configuration file.' % misc['config_name'])
# Check the options defined in the config file
copy_files = config['qchem'].get('copy_files',False)
if not isinstance(copy_files, bool):
raise abin_errors.AbinError ('ERROR: The "copy_files" value given in the "qchem" block of the "%s" configuration file is not a boolean (neither "True" nor "False").' % misc['config_name'])
benchmark = config['qchem'].get('benchmark',False)
if not isinstance(benchmark, bool):
raise abin_errors.AbinError ('ERROR: The "benchmark" value given in the "qchem" block of the "%s" configuration file is not a boolean (neither "True" nor "False").' % misc['config_name'])
# Define the templates
# ====================
# Define the names of the templates.
template_input = "qchem.in.jinja"
template_script = "qchem_job.sh.jinja"
# Check if the specified templates exist in the "templates" directory of ABIN LAUNCHER.
abin_errors.check_abspath(os.path.join(misc['templates_dir'],template_input),"Jinja template for the qchem input file","file")
abin_errors.check_abspath(os.path.join(misc['templates_dir'],template_script),"Jinja template for the qchem job script","file")
# Define rendered files
# =====================
# Define the names of the rendered files.
rendered_input = misc['mol_name'] + ".in"
rendered_script = "qchem_job.sh"
# Initialize the dictionary that will be returned by the function
rendered_content = {}
# ========================================================= #
# Rendering the input file #
# ========================================================= #
print("{:<80}".format("\nRendering the jinja template for the qchem input file ... "), end="")
# Defining the Jinja variables
# ============================
# Define the memory usage
mem_total = job_specs['cores'] * job_specs['mem_per_cpu']
mem_static = int(0.02*mem_total) if int(0.02*mem_total) > 200 else 200
# Variables not associated with the config file
input_render_vars = {
"mem_total" : mem_total,
"mem_static" : mem_static,
"coordinates" : file_data['atomic_coordinates']
}
# Variables associated with the "general" block of the config file
try:
input_render_vars.update({
"charge" : config['general']['charge'],
"multiplicity" : config['general']['multiplicity']
})
except KeyError as error:
raise abin_errors.AbinError ('ERROR: The "%s" key is missing in the "general" block of the "%s" configuration file.' % (error,misc['config_name']))
# Check if a "keywords" block has been defined in the "qchem" block of the config file
if not config['qchem'].get('keywords'):
raise abin_errors.AbinError ('ERROR: There is no "keywords" key in the "qchem" block of the "%s" configuration file.' % misc['config_name'])
# Variables associated with the "keywords" block of the "qchem" block in the config file
try:
input_render_vars.update({
"job_type" : config['qchem']['keywords']['job_type'],
"exchange" : config['qchem']['keywords']['exchange'],
"basis_set" : config['qchem']['keywords']['basis_set'],
"scf_algorithm" : config['qchem']['keywords']['scf_algorithm'],
"max_scf_cycles" : config['qchem']['keywords']['max_scf_cycles'],
"cis_n_roots" : config['qchem']['keywords']['cis_n_roots'],
"iqmol_fchk": config['qchem']['keywords']['iqmol_fchk'],
"cis_ampl_anal": config['qchem']['keywords']['cis_ampl_anal']
})
except KeyError as error:
raise abin_errors.AbinError ('ERROR: The "%s" key is missing in the "keywords" block of the "qchem" block in the "%s" configuration file.' % (error,misc['config_name']))
# Rendering the file
# ==================
rendered_content[rendered_input] = jinja_render(misc['templates_dir'], template_input, input_render_vars)
print('%12s' % "[ DONE ]")
# ========================================================= #
# Rendering the job script #
# ========================================================= #
# Get the path to the "check_scripts" directory because the job script needs to execute qchem_check.py
chains_path = os.path.dirname(misc['code_dir'])
check_script_path = os.path.join(chains_path,"check_scripts")
# If we need to copy the output files to their respective results directory, load the CHAINS configuration file to get the necessary information
if copy_files:
chains_config_file = abin_errors.check_abspath(os.path.join(chains_path,"configs","chains_config.yml"),"CHAINS configuration YAML file","file")
print ("{:<80}".format("\nLoading CHAINS configuration YAML file ..."), end="")
with open(chains_config_file, 'r') as chains:
chains_config = yaml.load(chains, Loader=yaml.FullLoader)
print('%12s' % "[ DONE ]")
print("{:<80}".format("\nRendering the jinja template for the qchem job script ..."), end="")
# Defining the mandatory Jinja variables
# ======================================
# Variables not associated with the config file
script_render_vars = {
"mol_name" : misc['mol_name'],
"config_file" : misc['config_name'],
"job_walltime" : job_specs['walltime'],
"job_cores" : job_specs['cores'],
"job_mem_per_cpu" : job_specs['mem_per_cpu'], # in MB
"partition" : job_specs['partition'],
"chains_dir" : chains_path,
"check_dir" : check_script_path,
"copy_files" : copy_files, # Associated with the config file, but it has already been verified
"benchmark" : benchmark # Associated with the config file, but it has already been verified
}
# Variables associated with the "general" block of the config file
try:
script_render_vars.update({
"user_email" : config['general']['user_email'],
"mail_type" : config['general']['mail_type']
})
except KeyError as error:
raise abin_errors.AbinError ('ERROR: The "%s" key is missing in the "general" block of the "%s" configuration file.' % (error,misc['config_name']))
# Variables associated with the clusters configuration file
try:
script_render_vars.update({
"set_env" : clusters_cfg[job_specs['cluster_name']]['profiles'][job_specs['profile']]['set_env'],
"command" : clusters_cfg[job_specs['cluster_name']]['profiles'][job_specs['profile']]['command']
})
except KeyError as error:
raise abin_errors.AbinError ('ERROR: The "%s" key is missing in the "%s" profile of the clusters configuration file.' % (error,job_specs['profile']))
# Defining the specific Jinja variables
# =====================================
# Variables specific to the copy_files portion of the template
if copy_files:
# Variables not associated with the config file
script_render_vars.update({
"job_script" : rendered_script
})
# Variables associated with the CHAINS configuration file
try:
if config['qchem']['keywords']['basis_set'].lower() == "def2-tzvp": # The output directories are different for the TZVP basis set
script_render_vars.update({
"output_dir" : chains_config['output_qchem_tzvp'],
"results_dir" : chains_config['results_dir_tzvp']
})
else:
script_render_vars.update({
"output_dir" : chains_config['output_qchem'],
"results_dir" : chains_config['results_dir']
})
except KeyError as error:
raise abin_errors.AbinError ('ERROR: The "%s" key is missing in the CHAINS configuration file (chains_config.yml).' % error)
# Variables specific to the benchmarking template
if benchmark:
script_render_vars.update({
"benchmark_path" : "${CECIHOME}/BENCHMARK",
"prefix": job_specs['profile'] + "_" + job_specs['cluster_name'],
"profile" : job_specs['profile'],
"cluster_name" : job_specs['cluster_name'],
"jobscale_label" : job_specs['scale_label'],
"job_walltime" : job_specs['walltime'],
"job_mem_per_cpu" : job_specs['mem_per_cpu'], # in MB
"scaling_function" : job_specs['scaling_fct'],
"scale_index" : job_specs['scale_index']
})
# Rendering the file
# ==================
rendered_content[rendered_script] = jinja_render(misc['templates_dir'], template_script, script_render_vars)
print('%12s' % "[ DONE ]")
return rendered_content, rendered_script
######################################################################################################################################
def chains_orca_render(mendeleev:dict, clusters_cfg:dict, config:dict, file_data:dict, job_specs:dict, misc:dict):
"""Renders the job script and the input file associated with the ORCA program in CHAINS.
Parameters
----------
mendeleev : dict
Content of AlexGustafsson's Mendeleev Table YAML file (found at https://github.com/AlexGustafsson/molecular-data).
Unused in this function.
clusters_cfg : dict
Content of the YAML clusters configuration file.
config : dict
Content of the YAML configuration file.
file_data : dict
Information extracted by the scanning function from the geometry file.
job_specs : dict
Contains all information related to the job.
misc : dict
Contains all the additional variables that did not pertain to the other arguments.
Returns
-------
rendered_content : dict
Dictionary containing the text of all the rendered files in the form of <filename>: <rendered_content>.
rendered_script : str
Name of the rendered job script, necessary to launch the job.
Notes
-----
Pay a particular attention to the render_vars dictionaries, they contain all the definitions of the variables appearing in your Jinja templates.
"""
# ========================================================= #
# Preparation step #
# ========================================================= #
# Check config file
# =================
# Check if a "general" block has been defined in the config file
if not config.get('general'):
raise abin_errors.AbinError ('ERROR: There is no "general" key defined in the "%s" configuration file.' % misc['config_name'])
# Check if an "orca" block has been defined in the config file
if not config.get('orca'):
raise abin_errors.AbinError ('ERROR: There is no "orca" key defined in the "%s" configuration file.' % misc['config_name'])
# Check the options defined in the config file
copy_files = config['orca'].get('copy_files',True)
if not isinstance(copy_files, bool):
raise abin_errors.AbinError ('ERROR: The "copy_files" value given in the "orca" block of the "%s" configuration file is not a boolean (neither "True" nor "False").' % misc['config_name'])
benchmark = config['orca'].get('benchmark',False)
if not isinstance(benchmark, bool):
raise abin_errors.AbinError ('ERROR: The "benchmark" value given in the "orca" block of the "%s" configuration file is not a boolean (neither "True" nor "False").' % misc['config_name'])
# Define the templates
# ====================
# Define the names of the templates.
template_input = "orca.inp.jinja"
template_script = "orca_job.sh.jinja"
# Check if the specified templates exist in the "templates" directory of ABIN LAUNCHER.
abin_errors.check_abspath(os.path.join(misc['templates_dir'],template_input),"Jinja template for the orca input file","file")
abin_errors.check_abspath(os.path.join(misc['templates_dir'],template_script),"Jinja template for the orca job script","file")
# Define rendered files
# =====================
# Define the names of the rendered files.
rendered_input = misc['mol_name'] + ".inp"
rendered_script = "orca_job.sh"
# Initialize the dictionary that will be returned by the function
rendered_content = {}
# ========================================================= #
# Rendering the input file #
# ========================================================= #
print("{:<80}".format("\nRendering the jinja template for the orca input file ... "), end="")
# Defining the Jinja variables
# ============================
# It is recommended to set the memory per CPU to 75% of the physical memory available (see https://sites.google.com/site/orcainputlibrary/orca-common-problems)
orca_mem_per_cpu = int(0.75 * job_specs['mem_per_cpu']) # in MB
# Variables not associated with the config file
input_render_vars = {
"mol_name" : misc['mol_name'],
"orca_mem_per_cpu" : orca_mem_per_cpu,
"job_cores" : job_specs['cores']
}
# Variables associated with the "general" block of the config file
try:
input_render_vars.update({
"charge" : config['general']['charge'],
"multiplicity" : config['general']['multiplicity']
})
except KeyError as error:
raise abin_errors.AbinError ('ERROR: The "%s" key is missing in the "general" block of the "%s" configuration file.' % (error,misc['config_name']))
# Check if a "keywords" block has been defined in the "orca" block of the config file
if not config['orca'].get('keywords'):
raise abin_errors.AbinError ('ERROR: There is no "keywords" key in the "orca" block of the "%s" configuration file.' % misc['config_name'])
# Variables associated with the "keywords" block of the "orca" block in the config file
try:
input_render_vars.update({
"method" : config['orca']['keywords']['method'],
"basis_set" : config['orca']['keywords']['basis_set'],
"aux_basis_set" : config['orca']['keywords']['aux_basis_set'],
"other" : config['orca']['keywords']['other'],
"nroots" : config['orca']['keywords']['nroots'],
"printlevel_tdm" : config['orca']['keywords']['printlevel_tdm'],
"printlevel_soc" : config['orca']['keywords']['printlevel_soc']
})
except KeyError as error:
raise abin_errors.AbinError ('ERROR: The "%s" key is missing in the "keywords" block of the "orca" block in the "%s" configuration file.' % (error,misc['config_name']))
# Rendering the file
# ==================
rendered_content[rendered_input] = jinja_render(misc['templates_dir'], template_input, input_render_vars)
print('%12s' % "[ DONE ]")
# ========================================================= #
# Rendering the job script #
# ========================================================= #
# Get the path to the "check_scripts" directory because the job script needs to execute orca_check.py
chains_path = os.path.dirname(misc['code_dir'])
check_script_path = os.path.join(chains_path,"check_scripts")
# If we need to copy the output files to their respective results directory, load the CHAINS configuration file to get the necessary information
if copy_files:
chains_config_file = abin_errors.check_abspath(os.path.join(chains_path,"configs","chains_config.yml"),"CHAINS configuration YAML file","file")
print ("{:<80}".format("\nLoading CHAINS configuration YAML file ..."), end="")
with open(chains_config_file, 'r') as chains:
chains_config = yaml.load(chains, Loader=yaml.FullLoader)
print('%12s' % "[ DONE ]")
print("{:<80}".format("\nRendering the jinja template for the orca job script ..."), end="")
# Defining the mandatory Jinja variables
# ======================================
# Variables not associated with the config file
script_render_vars = {
"mol_name" : misc['mol_name'],
"config_file" : misc['config_name'],
"job_walltime" : job_specs['walltime'],
"job_cores" : job_specs['cores'],
"job_mem_per_cpu" : job_specs['mem_per_cpu'], # in MB
"cluster_name" : job_specs['cluster_name'],
"partition" : job_specs['partition'],
"chains_dir" : chains_path,
"check_dir" : check_script_path,
"copy_files" : copy_files, # Associated with the config file, but it has already been verified
"benchmark" : benchmark # Associated with the config file, but it has already been verified
}
# Variables associated with the "general" block of the config file
try:
script_render_vars.update({
"user_email" : config['general']['user_email'],
"mail_type" : config['general']['mail_type']
})
except KeyError as error:
raise abin_errors.AbinError ('ERROR: The "%s" key is missing in the "general" block of the "%s" configuration file.' % (error,misc['config_name']))
# Variables associated with the clusters configuration file
try:
script_render_vars.update({
"set_env" : clusters_cfg[job_specs['cluster_name']]['profiles'][job_specs['profile']]['set_env'],
"command" : clusters_cfg[job_specs['cluster_name']]['profiles'][job_specs['profile']]['command']
})
except KeyError as error:
raise abin_errors.AbinError ('ERROR: The "%s" key is missing in the "%s" profile of the clusters configuration file.' % (error,job_specs['profile']))
# Defining the specific Jinja variables
# =====================================
# Variables specific to the copy_files portion of the template
if copy_files:
# Variables not associated with the config file
script_render_vars.update({
"config_file" : misc['config_name'],
"job_script" : rendered_script
})
# Variables associated with the CHAINS configuration file
try:
script_render_vars.update({
"output_dir" : chains_config['output_orca'],
"results_dir" : chains_config['results_dir']
})
except KeyError as error:
raise abin_errors.AbinError ('ERROR: The "%s" key is missing in the CHAINS configuration file (chains_config.yml).' % error)
# Variables specific to the benchmarking template
if benchmark:
script_render_vars.update({
"benchmark_path" : "${CECIHOME}/BENCHMARK",
"prefix": job_specs['profile'] + "_" + job_specs['cluster_name'],
"profile" : job_specs['profile'],
"cluster_name" : job_specs['cluster_name'],
"jobscale_label" : job_specs['scale_label'],
"job_walltime" : job_specs['walltime'],
"job_mem_per_cpu" : job_specs['mem_per_cpu'], # in MB
"scaling_function" : job_specs['scaling_fct'],
"scale_index" : job_specs['scale_index']
})
# Rendering the file
# ==================
rendered_content[rendered_script] = jinja_render(misc['templates_dir'], template_script, script_render_vars)
print('%12s' % "[ DONE ]")
return rendered_content, rendered_script