10
0
mirror of https://github.com/LCPQ/quantum_package synced 2024-12-23 21:03:56 +01:00
quantum_package/scripts/ezfio_interface/ei_handler.py

809 lines
24 KiB
Python
Executable File

#!/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=<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 `<module>/ezfio_interface.irpf90`
which contains all the providers needed
--ezfio_config Create the `<module_lower>_ezfio_interface_config` in
`${QP_EZFIO}/config/`
--ocaml Create all the stuff needed by *qp_edit*:
-`Input_<module_lower>.ml` and
- <module_lower>_ezfio_interface_default`
ocaml_global Create the *qp_edit*
Format specification :
Required:
[<provider_name>] The name of the provider in irp.f90 and in the EZFIO lib
doc:<str> The plain text documentation
type:<str> A Fancy_type supported by the ocaml.
type `ei_handler.py get_supported_type` for a list
interface:<str> 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: <str> The default value needed,
if 'ocaml' is in interface list.
! No list is allowed for now !
size: <str> 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 example (determinants.n_det)
ezfio_name: <str> The name for the EZFIO lib
(by default is <provider_name>)
ezfio_dir: <str> Will be the folder of EZFIO.
(by default is <module_lower>)
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/ei_handler.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)