"""utils
This module should contain small usefull functions.
"""
import os
import math
import argparse
import inspect
from pygromos.utils.typing import Dict, List
spacer0 = "!" * 90 + "\n"
spacer = "#" * 80 + "\n"
spacer2 = "=" * 60 + "\n"
spacer3 = "_" * 40 + "\n"
time_wait_s_for_filesystem = 0
def _cartesian_distance(x1: float, x2: float, y1: float, y2: float, z1: float, z2: float) -> float:
return math.sqrt((x1 - x2) ** 2 + (y1 - y2) ** 2 + (z1 - z2) ** 2)
"""
File and submission
"""
[docs]def str2bool(v):
if isinstance(v, bool):
return v
if v.lower() in ("yes", "true", "t", "y", "1"):
return True
elif v.lower() in ("no", "false", "f", "n", "0"):
return False
else:
raise argparse.ArgumentTypeError("Boolean value expected.")
[docs]def dynamic_parser(func: callable, title: str):
"""
This function builds dynamically a parser obj for any function, that has parameters with annotated types.
Result is beeing able to parse any function dynamically via bash.
Parameters
----------
func : callable
the function that should be parsed
title : str
title for the parser
Returns
-------
args
The parsed arguments
Raises
------
IOError
error if a parsing arg is unknown
"""
parser = argparse.ArgumentParser(description=title)
args = inspect.getfullargspec(func)
total_defaults = len(args.defaults)
total_args = len(args.args)
total_required = total_args - total_defaults
parser.description = func.__name__ + " - " + func.__doc__.split("Parameters")[0]
for argument, argument_type in args.annotations.items():
index = args.args.index(argument)
required = True if (index < total_required) else False
default = None if (required) else args.defaults[index - total_required]
if argument_type is bool:
argument_type = str2bool
parser.add_argument("-" + argument, type=argument_type, required=required, default=default)
args, unkown_args = parser.parse_known_args()
if len(unkown_args) > 0:
raise IOError(__name__ + " got unexpected argument(s) for parser:\n" + str(unkown_args))
return args
[docs]def dict_to_nice_string(control_dict: Dict) -> str:
"""
Converts a dictionary of options (like template_control_dict)
to a more human readable format. Which can then be printed to a text file,
which can be manually modified before submiting analysis jobs.
Parameters
----------
control_dict : Dict
analysis control dictonary
Returns
-------
str
nice formatting of the control dictionary for printing.
"""
script_text = "control_dict = {\n"
for key, value in control_dict.items():
script_text += '\t"' + key + '": '
first = False
if type(value) == dict:
if "do" in value: # do should always be first in this list
script_text += '{"do":' + str(value["do"]) + ","
if len(value) > 1:
script_text += "\n"
first = True
for key2, value2 in value.items(): # alternative keys
# prefix
if first:
prefix = " "
first = False
else:
prefix = "\t\t"
# key_val
if key2 == "do":
continue
elif type(value2) == dict:
script_text += prefix + '"' + str(key2) + '": ' + _inline_dict(value2, "\t\t\t") + ",\n"
else:
script_text += prefix + '"' + str(key2) + '": ' + str(value2) + ","
script_text += prefix + " },\n"
else:
script_text += str(value) + ",\n"
script_text += "}\n"
return script_text
[docs]def _inline_dict(in_dict: Dict, prefix: str = "\t") -> str:
"""
translate dictionary to one code line. can be used for meta-scripting
Parameters
----------
in_dict: Dict
analysis control dict
prefix : str, optional
prfix symbol to dict write out.
Returns
-------
str
code line.
"""
msg = "{\n"
for key, value in in_dict.items():
if type(value) == dict:
msg += prefix + '"' + str(key) + '": ' + _inline_dict(in_dict=value, prefix=prefix + "\t") + ","
else:
msg += prefix + '"' + str(key) + '": ' + str(value) + ",\n"
return msg + prefix + "}"
[docs]def write_job_script(
out_script_path: str,
target_function: callable,
variable_dict: dict,
python_cmd: str = "python3",
verbose: bool = False,
) -> str:
"""
this function writes submission commands into a file. The command will be started from a bash env into python.
Parameters
----------
out_script_path: str
path of the output script.
target_function : callable
the function, that shall be submitted
variable_dict : dict
variables for this function
python_cmd : str, optional
which python command shall be supplied
verbose : bool, optional
c'est la vie
Returns
-------
str
returns an out script path.
Raises
------
IOERROR
if outpath is not possible
ValueError
if required variable from the var-dict for the function is missing
"""
if not os.path.exists(os.path.dirname(out_script_path)):
raise IOError(
"Could not find path of dir, that should contain the schedule script!\n\t Got Path: " + out_script_path
)
import pygromos
# Build str:
s = inspect.signature(target_function) # to lazy for numpydoc
import_string = ""
import_string += "#IMPORTS\n"
import_string += "import sys\n"
import_string += 'sys.path.append("' + os.path.abspath(os.path.dirname(pygromos.__file__) + "/..") + '")\n'
import_string += "from " + str(target_function.__module__) + " import " + target_function.__name__
vars_string = "#VARIABLES: \n"
cmd_options = ""
from pygromos.files.gromos_system.gromos_system import Gromos_System
from pygromos.simulations.hpc_queuing.submission_systems._submission_system import _SubmissionSystem
missed_keys = []
for key in s.parameters:
if key in variable_dict:
value = variable_dict[key]
if isinstance(value, Gromos_System) or issubclass(
value.__class__, _SubmissionSystem
): # this is a nasty way! ... tends to fail!
sys = value
vars_string += sys.get_script_generation_command(var_name=key)
elif isinstance(value, Dict):
vars_string += dict_to_nice_string(value)
elif isinstance(value, List):
vars_string += key + "= [ " + ", ".join(map(str, value)) + "]\n"
elif isinstance(value, str):
vars_string += key + ' = "' + str(value) + '"\n'
else:
vars_string += key + " = " + str(value) + "\n"
cmd_options += key + "=" + key + ", "
elif s.parameters[key].default == inspect._empty:
missed_keys.append(key)
if len(missed_keys) > 0:
raise ValueError(
"Found some variables missing in variable dict,that are required!\n\t" + "\n\t".join(missed_keys)
)
cmd_string = "\n#DO\n"
cmd_string += target_function.__name__ + "(" + cmd_options + ")"
cmd_string += "\nexit(0)\n\n"
script_text = (
"#!/usr/bin/env " + python_cmd + "\n\n" + import_string + "\n\n" + vars_string + "\n\n" + cmd_string + "\n"
)
if verbose:
print(script_text)
# write out file
out_script_file = open(out_script_path, "w")
out_script_file.write(script_text)
out_script_file.close()
os.system("chmod +x " + out_script_path)
return out_script_path
[docs]def nice_s_vals(svals: List[float]) -> List[float]:
"""
This helper function formats s-vals for RE-EDS to nice readable values.
Is/was used in RE-EDS applications. Main functionality is rounding the different s-vals to their significance digits.
Parameters
----------
svals : List[float]
smoothing parameters of a reeds approach
Returns
-------
List[float]
nicely rounded s-values, such that only significant digits for each number is around.
"""
nicer_labels = []
for val in svals:
nicer_labels.append(round(float(val), str(val).count("0") + 2))
return nicer_labels