#!/usr/bin/env python # -*- coding: utf-8 -*- """ Welcom the ei_handler. We will create all the ezfio related stuff from a EZFIO.cfg file. Usage: ei_handler.py [--path_module=] [--irpf90] [--ezfio_config] [--ocaml] [--ezfio_default] ei_handler.py list_supported_type ei_handler.py ocaml_global By default all the option are executed. Options: -h --help --irpf90 Create the `/ezfio_interface.irpf90` which contains all the providers needed --ezfio_config Create the `_ezfio_interface_config` in `${QP_EZFIO}/config/` --ocaml Create all the stuff needed by *qp_edit*: -`Input_.ml` and - _ezfio_interface_default` ocaml_global Create the *qp_edit* Format specification : Required: [] The name of the provider in irp.f90 and in the EZFIO lib doc: The plain text documentation type: A Fancy_type supported by the ocaml. type `ei_handler.py get_supported_type` for a list interface: The interface is list of string sepeared by "," which can contain : - ezfio (if you only whant the ezfiolib) - provider (if you want the provider) - ocaml (if you want the ocaml gestion) Optional: default: The default value needed, if 'ocaml' is in interface list. ! No list is allowed for now ! size: The size information. (by default is one) Example : 1, =sum(ao_num); (ao_num,3) ATTENTION : The module and the value are separed by a "." not a "_". For exemple (determinants.n_det) ezfio_name: The name for the EZFIO lib (by default is ) ezfio_dir: Will be the folder of EZFIO. (by default is ) Example of EZFIO.cfg: ``` [thresh_SCF] doc: Threshold on the convergence of the Hartree Fock energy type: Threshold default: 1.e-10 interface: provider,ezfio,ocaml size: 1 [energy] type: double precision doc: Calculated HF energy interface: ezfio ``` """ from docopt import docopt import sys import os import os.path import ConfigParser from collections import defaultdict from collections import namedtuple from decorator import cache from os import listdir from os.path import isdir, join, exists from qp_path import QP_ROOT, QP_SRC, QP_OCAML, QP_EZFIO Type = namedtuple('Type', 'fancy ocaml fortran') Module = namedtuple('Module', 'path lower') def is_bool(str_): """ Take a string, if is a bool return the conversion into fortran and ocaml. """ if "true" in str_.strip().lower(): return Type(None, "true", ".True.") elif "false" in str_.strip().lower(): return Type(None, "false", ".False") else: raise TypeError @cache def get_type_dict(): """ This function makes the correspondance between the type of value read in ezfio.cfg into the f90 and Ocaml Type return fancy_type[fancy_type] = namedtuple('Type', 'ocaml fortran') For example fancy_type['Ndet'].fortran = interger .ocaml = int """ # ~#~#~#~ # # I n i t # # ~#~#~#~ # fancy_type = defaultdict(dict) # ~#~#~#~#~#~#~#~ # # R a w _ t y p e # # ~#~#~#~#~#~#~#~ # fancy_type['integer'] = Type(None, "int", "integer") fancy_type['integer*8'] = Type(None, "int", "integer*8") fancy_type['int'] = Type(None, "int", "integer") fancy_type['int64'] = Type(None, "int64", "integer*8") fancy_type['float'] = Type(None, "float", "double precision") fancy_type['double precision'] = Type(None, "float", "double precision") fancy_type['logical'] = Type(None, "bool", "logical") fancy_type['bool'] = Type(None, "bool", "logical") fancy_type['character*(32)'] = Type(None, "string", "character*(32)") fancy_type['character*(64)'] = Type(None, "string", "character*(64)") fancy_type['character*(256)'] = Type(None, "string", "character*(256)") # ~#~#~#~#~#~#~#~ # # q p _ t y p e s # # ~#~#~#~#~#~#~#~ # # Dict to change ocaml LowLevel type into FortranLowLevel type ocaml_to_fortran = {"int": "integer", "int64": "integer*8", "float": "double precision", "logical": "logical", "string": "character*32"} # Read and parse qptype generate src = join(QP_OCAML, "qptypes_generator.ml") with open(src, "r") as f: r = f.read() # Generate l_gen = [i for i in r.splitlines() if i.strip().startswith("*")] # Untouch b = r.find('let untouched = "') e = r.find(';;', b) l_un = [i for i in r[b:e].splitlines() if i.strip().startswith("module")] # ~#~#~#~#~#~#~#~#~#~#~#~#~#~#~#~#~ # # q p _ t y p e s _ g e n e r a t e # # ~#~#~#~#~#~#~#~#~#~#~#~#~#~#~#~#~ # # Read the fancy_type, the ocaml. and convert the ocaml to the fortran for i in l_gen + l_un: str_fancy_type = i.split()[1].strip() str_ocaml_type = i.split()[3] if str_ocaml_type != 'sig': str_fortran_type = ocaml_to_fortran[str_ocaml_type] else: str_fortran_type = 'character*(32)' str_ocaml_type = 'string' fancy_type[str_fancy_type] = Type(str_fancy_type, str_ocaml_type, str_fortran_type) # ~#~#~#~#~#~#~#~ # # F i n a l i z e # # ~#~#~#~#~#~#~#~ # return dict(fancy_type) type_dict = get_type_dict() def get_dict_config_file(module_obj): """ Input: module_obj.path is the config file (for example FULL_PATH/EZFIO.cfg) module_obj.lower is the MODULE name lowered (Ex fullci) Return a dict d[provider_name] = {type, doc, ezfio_name, ezfio_dir, size, interface, default} """ # ~#~#~#~ # # I n i t # # ~#~#~#~ # d = defaultdict(dict) l_info_optional = ["ezfio_dir", "ezfio_name", "size"] # ~#~#~#~#~#~#~#~#~#~#~ # # L o a d _ C o n f i g # # ~#~#~#~#~#~#~#~#~#~#~ # config_file = ConfigParser.ConfigParser() config_file.readfp(open(module_obj.path)) # ~#~#~#~#~#~#~#~#~ # # F i l l _ d i c t # # ~#~#~#~#~#~#~#~#~ # def error(o, p, c): "o option ; p provider_name ;c module_obj.path" print "You need a {0} for {1} in {2}".format(o, p, c) for section in config_file.sections(): # pvd = provider pvd = section.lower() d[pvd]["module"] = module_obj # Create the dictionary which contains the default value d_default = {"ezfio_name": pvd, "ezfio_dir": module_obj.lower, "size": "1"} # Check if type is avalaible try: type_ = config_file.get(section, "type").strip() except ConfigParser.NoOptionError: error("type", pvd, module_obj.path) sys.exit(1) if type_ not in type_dict: print "{0} not avalaible. Choose in:".format(type_).strip() print ", ".join(sorted([i for i in type_dict])) sys.exit(1) else: d[pvd]["type"] = type_dict[type_] # Fill the dict with REQUIRED information try: d[pvd]["doc"] = config_file.get(section, "doc") except ConfigParser.NoOptionError: error("doc", pvd, module_obj.path) sys.exit(1) try: interface = [i.lower().strip() for i in config_file.get(section, "interface").split(",")] except ConfigParser.NoOptionError: error("doc", pvd, module_obj.path) sys.exit(1) else: if not any(i in ["ezfio", "provider", "ocaml"] for i in interface): print "Bad keyword for interface for {0}".format(pvd) sys.exit(1) else: d[pvd]["interface"] = interface # Fill the dict with OPTIONAL information for option in l_info_optional: try: d[pvd][option] = config_file.get(section, option).lower() except ConfigParser.NoOptionError: if option in d_default: d[pvd][option] = d_default[option] # If interface is input we need a default value information try: default_raw = config_file.get(section, "default") except ConfigParser.NoOptionError: if "ocaml" in d[pvd]["interface"]: error("default", pvd, module_obj.path) sys.exit(1) else: pass else: try: d[pvd]["default"] = is_bool(default_raw) except TypeError: d[pvd]["default"] = Type(None, default_raw, default_raw) return dict(d) def create_ezfio_provider(dict_ezfio_cfg): import re """ From dict d[provider_name] = {type, doc, ezfio_name, ezfio_dir, interface, default size} create the a list which contains all the code for the provider output = output_dict_info['ezfio_dir' return [code, ...] """ from ezfio_generate_provider import EZFIO_Provider dict_code_provider = dict() ez_p = EZFIO_Provider() for provider_name, dict_info in dict_ezfio_cfg.iteritems(): if "provider" in dict_info["interface"]: ez_p.set_type(dict_info['type'].fortran) ez_p.set_name(provider_name) ez_p.set_doc(dict_info['doc']) ez_p.set_ezfio_dir(dict_info['ezfio_dir']) ez_p.set_ezfio_name(dict_info['ezfio_name']) ez_p.set_output("output_%s" % dict_info['module'].lower) # (nuclei.nucl_num,pseudo.klocmax) => (nucl_num,klocmax) ez_p.set_size(re.sub(r'\w+\.', "", dict_info['size'])) dict_code_provider[provider_name] = str(ez_p) + "\n" return dict_code_provider def save_ezfio_provider(path_head, dict_code_provider): """ Write in path_head/"ezfio_interface.irp.f" the value of dict_code_provider """ path = "{0}/ezfio_interface.irp.f".format(path_head) l_output = ["! DO NOT MODIFY BY HAND", "! Created by $QP_ROOT/scripts/ezfio_interface.py", "! from file {0}/EZFIO.cfg".format(path_head), "\n"] l_output += [code for code in dict_code_provider.values()] output = "\n".join(l_output) with open(path, "w+") as f: f.write(output) def create_ezfio_stuff(dict_ezfio_cfg, config_or_default="config"): """ From dict_ezfio_cfg[provider_name] = {type, default, ezfio_name,ezfio_dir,doc} Return the string ezfio_interface_config """ def size_format_to_ezfio(size_raw): """ If size_raw == "=" is a formula -> do nothing; return Else convert the born of a multidimential array (12,begin:end) into (12,begin+end+1) for example If the value are between parenthses -> do nothing; return """ size_raw = str(size_raw) if size_raw.startswith('='): size_convert = size_raw.replace('.', '_') else: size_raw = provider_info["size"].translate(None, "()") size_raw = size_raw.replace('.', '_') a_size_raw = [] for dim in size_raw.split(","): try: (begin, end) = map(str.strip, dim.split(":")) except ValueError: a_size_raw.append(dim) else: if begin[0] == '-': a_size_raw.append("{0}+{1}+1".format(end, begin[1:])) else: a_size_raw.append("{0}-{1}+1".format(end, begin)) size_raw = ",".join(a_size_raw) size_convert = "({0})".format(size_raw) return size_convert def create_format_string(size): """ Take a size number and return the string format for being right align with this offset """ return "{{0:<{0}}}".format(size).format # ~#~#~#~#~#~#~#~#~#~#~# # # F o r m a t _ i n f o # # ~#~#~#~#~#~#~#~#~#~#~# # lenmax_name = max([len(i) for i in dict_ezfio_cfg]) lenmax_type = max([len(i["type"].fortran) for i in dict_ezfio_cfg.values()]) str_name_format = create_format_string(lenmax_name + 2) str_type_format = create_format_string(lenmax_type + 2) # ~#~#~#~#~#~#~#~#~#~#~#~#~#~#~#~#~# # # C r e a t e _ t h e _ s t r i n g # # ~#~#~#~#~#~#~#~#~#~#~#~#~#~#~#~#~# # # Checking is many ezfio_dir provided l_ezfio_dir = [d['ezfio_dir'] for d in dict_ezfio_cfg.values()] if not l_ezfio_dir.count(l_ezfio_dir[0]) == len(l_ezfio_dir): print >> sys.stderr, "You have many ezfio_dir. Not supported yet" raise TypeError else: result = [l_ezfio_dir[0]] for provider_name, provider_info in sorted(dict_ezfio_cfg.iteritems()): # Get the value from dict name_raw = provider_info["ezfio_name"].lower() fortran_type_raw = provider_info["type"].fortran if "size" in provider_info and not provider_info["size"] == "1": size_raw = provider_info["size"] else: size_raw = None # It is the last so we don't need to right align it str_size = size_format_to_ezfio(size_raw) if size_raw else "" if "default" in provider_info and provider_info["default"].fortran.startswith("="): str_default = provider_info["default"].fortran.replace('.', '_') else: str_default = "" # Get the string in to good format (left align and co) str_name = str_name_format(name_raw) str_fortran_type = str_type_format(fortran_type_raw) # Return the string if config_or_default == "config": s = " {0} {1} {2} {3}".format(str_name, str_fortran_type, str_size, str_default) elif config_or_default == "default": try: str_value = provider_info["default"].ocaml except KeyError: continue else: s = " {0} {1}".format(str_name, str_value) else: raise KeyError # Append result.append(s) result.append("\n") return "\n".join(result) def create_ezfio_config(dict_ezfio_cfg): return create_ezfio_stuff(dict_ezfio_cfg, config_or_default="config") def save_ezfio_config(module_lower, str_ezfio_config): """ Write the str_ezfio_config in "$QP_ROOT/EZFIO/{0}.ezfio_interface_config".format(module_lower) """ name = "{0}.ezfio_interface_config".format(module_lower) path = os.path.join(QP_EZFIO, "config", name) with open(path, "w+") as f: f.write(str_ezfio_config) def create_ezfio_default(dict_ezfio_cfg): return create_ezfio_stuff(dict_ezfio_cfg, config_or_default="default") def save_ezfio_default(module_lower, str_ezfio_default): """ Write the str_ezfio_config in "$QP_ROOT/data/ezfio_defaults/{0}.ezfio_interface_default".format(module_lower) """ root_ezfio_default = "{0}/data/ezfio_defaults/".format( QP_ROOT) path = "{0}/{1}.ezfio_interface_default".format(root_ezfio_default, module_lower) with open(path, "w+") as f: f.write(str_ezfio_default) def create_ocaml_input(dict_ezfio_cfg, module_lower): # ~#~#~#~# # # I n i t # # ~#~#~#~# # from ezfio_generate_ocaml import EZFIO_ocaml l_ezfio_name = [] l_type = [] l_doc = [] for k, v in dict_ezfio_cfg.iteritems(): if "ocaml" in v['interface']: l_ezfio_name.append(v['ezfio_name']) l_type.append(v["type"]) l_doc.append(v["doc"]) if not l_ezfio_name: raise ValueError e_glob = EZFIO_ocaml(l_ezfio_name=l_ezfio_name, l_type=l_type, l_doc=l_doc) # ~#~#~#~#~#~#~#~# # # C r e a t i o n # # ~#~#~#~#~#~#~#~# # template = ['(* =~=~ *)', '(* Init *)', '(* =~=~ *)', ""] template += ["open Qptypes;;", "open Qputils;;", "open Core.Std;;", "", "module {0} : sig".format(module_lower.capitalize())] template += [e_glob.create_type()] template += [" val read : unit -> t option", " val write : t-> unit", " val to_string : t -> string", " val to_rst : t -> Rst_string.t", " val of_rst : Rst_string.t -> t option", "end = struct"] template += [e_glob.create_type()] template += ['', ' let get_default = Qpackage.get_ezfio_default "{0}";;'.format(module_lower), ''] template += ['(* =~=~=~=~=~=~==~=~=~=~=~=~ *)', '(* Generate Special Function *)', '(* =~=~=~==~=~~=~=~=~=~=~=~=~ *)', ""] for provider_name, d_val in sorted(dict_ezfio_cfg.iteritems()): if 'default' not in d_val: continue ezfio_dir = d_val["ezfio_dir"] ezfio_name = d_val["ezfio_name"] e = EZFIO_ocaml(ezfio_dir=ezfio_dir, ezfio_name=ezfio_name, type=d_val["type"]) template += [e.create_read(), e.create_write(), ""] template += ['(* =~=~=~=~=~=~=~=~=~=~=~=~ *)', '(* Generate Global Function *)', '(* =~=~=~=~=~=~=~=~=~=~=~=~ *)', ""] template += [e_glob.create_read_global(), e_glob.create_write_global(), e_glob.create_to_string(), e_glob.create_to_rst()] template += [" include Generic_input_of_rst;;", " let of_rst = of_rst t_of_sexp;;", "", "end"] return "\n".join(template) def save_ocaml_input(module_lower, str_ocaml_input): """ Write the str_ocaml_input in qp_path.QP_OCAML/Input_{0}.ml".format(module_lower) """ name = "Input_{0}.ml".format(module_lower) path = join(QP_OCAML, name) with open(path, "w+") as f: f.write(str_ocaml_input) def get_l_module_with_auto_generate_ocaml_lower(): """ Get all modules which have EZFIO.cfg with Ocaml data (NB `search` in all the lines and `match` only in one) """ # ~#~#~#~#~#~#~#~ # # L _ f o l d e r # # ~#~#~#~#~#~#~#~ # l_folder = [f for f in listdir(QP_SRC) if isdir(join(QP_SRC, f))] # ~#~#~#~#~#~#~#~#~#~#~#~#~#~ # # L _ m o d u l e _ l o w e r # # ~#~#~#~#~#~#~#~#~#~#~#~#~#~ # l_module_lower = [] import re p = re.compile(ur'interface:.*ocaml') for f in l_folder: path = "{0}/{1}/EZFIO.cfg".format(QP_SRC, f) if exists(path): with open(path, 'r') as file_: if p.search(file_.read()): l_module_lower.append(f.lower()) # ~#~#~#~#~#~ # # R e t u r n # # ~#~#~#~#~#~ # return l_module_lower def create_ocaml_input_global(l_module_with_auto_generate_ocaml_lower): """ Create the Input_auto_generated.ml and qp_edit.ml str """ # ~#~#~#~#~#~#~#~# # # C r e a t i o n # # ~#~#~#~#~#~#~#~# # from ezfio_generate_ocaml import EZFIO_ocaml path = QP_ROOT + "/scripts/ezfio_interface/qp_edit_template" with open(path, "r") as f: template_raw = f.read() e = EZFIO_ocaml(l_module_lower=l_module_with_auto_generate_ocaml_lower) template = template_raw.format(keywords=e.create_qp_keywords(), keywords_to_string=e.create_qp_keywords_to_string(), section_to_rst=e.create_qp_section_to_rst(), write=e.create_qp_write(), tasks=e.create_qp_tasks()) input_auto = e.create_input_auto_generated() return (template, input_auto) def save_ocaml_input_auto(str_ocaml_input_global): """ Write the str_ocaml_input in qp_path.QP_OCAML/Input_auto_generated.ml """ name = "Input_auto_generated.ml" path = join(QP_OCAML, name) with open(path, "w+") as f: f.write(str_ocaml_input_global) def save_ocaml_qp_edit(str_ocaml_qp_edit): """ Write the str_ocaml_qp_edit in qp_path.QP_OCAML/qp_edit.ml """ name = "qp_edit.ml" path = join(QP_OCAML, name) with open(path, "w+") as f: f.write(str_ocaml_qp_edit) def code_generation(arguments, dict_ezfio_cfg, m): module_lower = m.lower path_dirname = m.path.replace("/EZFIO.cfg", "") # ~#~#~#~#~#~#~#~#~#~ # # W h a t _ t o _ d o # # ~#~#~#~#~#~#~#~#~#~ # if any([arguments[i] for i in ["--irpf90", "--ezfio_config", "--ocaml", "--ezfio_default"]]): # User changer somme argument, do what he want do_all = False else: # Do all the stuff do_all = True # ~#~#~#~#~#~#~ # # I R P . f 9 0 # # ~#~#~#~#~#~#~ # if do_all or arguments["--irpf90"]: l_str_code = create_ezfio_provider(dict_ezfio_cfg) save_ezfio_provider(path_dirname, l_str_code) # ~#~#~#~#~#~#~#~#~#~#~#~ # # e z f i o _ c o n f i g # # ~#~#~#~#~#~#~#~#~#~#~#~ # if do_all or arguments["--ezfio_config"]: str_ezfio_config = create_ezfio_config(dict_ezfio_cfg) save_ezfio_config(module_lower, str_ezfio_config) # ~#~#~#~#~#~#~#~#~#~#~#~#~#~#~#~#~#~#~ # # O c a m l & e z f i o _ d e f a u l t # # ~#~#~#~#~#~#~#~#~#~#~#~#~#~#~#~#~#~#~ # if do_all or arguments["--ocaml"]: try: str_ocaml_input = create_ocaml_input(dict_ezfio_cfg, module_lower) except ValueError: pass else: save_ocaml_input(module_lower, str_ocaml_input) str_ezfio_default = create_ezfio_default(dict_ezfio_cfg) save_ezfio_default(module_lower, str_ezfio_default) if __name__ == "__main__": arguments = docopt(__doc__) # ___ # | ._ o _|_ # _|_ | | | |_ # if arguments["list_supported_type"]: for i in get_type_dict(): print i sys.exit(0) if arguments["ocaml_global"]: # ~#~#~#~# # # I n i t # # ~#~#~#~# # l_module = get_l_module_with_auto_generate_ocaml_lower() str_ocaml_qp_edit, str_ocaml_input_auto = create_ocaml_input_global(l_module) save_ocaml_input_auto(str_ocaml_input_auto) save_ocaml_qp_edit(str_ocaml_qp_edit) sys.exit(0) # ~#~#~#~#~#~#~#~#~#~#~#~#~#~# # # G e t _ m o d u l e _ d i r # # ~#~#~#~#~#~#~#~#~#~#~#~#~#~# # if arguments["--path_module"]: path_dirname = os.path.abspath(arguments["--path_module"]) else: path_dirname = os.getcwd() root_module = os.path.split(path_dirname)[1] l_module = [root_module] # ~#~#~#~#~#~#~#~#~#~#~#~#~#~#~#~#~#~#~#~# # # G e t _ l _ d i c t _ e z f i o _ c f g # # ~#~#~#~#~#~#~#~#~#~#~#~#~#~#~#~#~#~#~#~# # l_module_with_ezfio = [] for f in l_module: path = join(QP_SRC, f, "EZFIO.cfg") if exists(path): l_module_with_ezfio.append(Module(path, f.lower())) l_dict_ezfio_cfg = [(m, get_dict_config_file(m)) for m in l_module_with_ezfio] # _ # / _ _| _ _ _ ._ _ ._ _. _|_ o _ ._ # \_ (_) (_| (/_ (_| (/_ | | (/_ | (_| |_ | (_) | | # _| for (m, dict_ezfio_cfg) in l_dict_ezfio_cfg: code_generation(arguments, dict_ezfio_cfg, m)