1
0
mirror of https://github.com/TREX-CoE/qmckl.git synced 2024-11-19 20:42:50 +01:00
qmckl/tools/lib.org

485 lines
14 KiB
Org Mode
Raw Normal View History

# -*- mode: org -*-
2021-04-09 11:26:04 +02:00
* Library of org-mode functions :noexport:
** Defines the name of the current file
#+NAME: filename
#+begin_src elisp :tangle no
2021-04-09 11:26:04 +02:00
(file-name-nondirectory (substring buffer-file-name 0 -4))
#+end_src
2021-04-09 11:26:04 +02:00
** Function to get the value of a property.
#+NAME: get_value
#+begin_src elisp :var key="Type"
2021-09-14 09:56:31 +02:00
(org-with-point-at org-babel-current-src-block-location
(org-entry-get nil key t))
2021-04-09 11:26:04 +02:00
#+end_src
2021-04-09 11:26:04 +02:00
** Table of function arguments
2021-03-30 14:51:23 +02:00
2021-04-09 11:26:04 +02:00
#+NAME: test
2022-01-05 15:56:25 +01:00
| Variable | Type | In/Out | Description |
|-----------+------------------+--------+-----------------------------------------------|
| ~context~ | ~qmckl_context~ | in | Global state |
| ~transa~ | ~char~ | in | Array ~A~ is ~'N'~: Normal, ~'T'~: Transposed |
| ~transb~ | ~char~ | in | Array ~B~ is ~'N'~: Normal, ~'T'~: Transposed |
| ~m~ | ~int64_t~ | in | Number of points in the first set |
| ~n~ | ~int64_t~ | in | Number of points in the second set |
| ~A~ | ~double[][lda]~ | in | Array containing the $m \times 3$ matrix $A$ |
| ~lda~ | ~int64_t~ | in | Leading dimension of array ~A~ |
| ~B~ | ~double[][ldb]~ | in | Array containing the $n \times 3$ matrix $B$ |
| ~ldb~ | ~int64_t~ | in | Leading dimension of array ~B~ |
| ~C~ | ~double[n][ldc]~ | out | Array containing the $m \times n$ matrix $C$ |
| ~ldc~ | ~int64_t~ | in | Leading dimension of array ~C~ |
2021-04-09 11:26:04 +02:00
*** Fortran-C type conversions
2021-04-09 11:26:04 +02:00
#+NAME:f_of_c
2023-09-22 16:41:43 +02:00
#+BEGIN_SRC python :var table=test :var rettyp="qmckl_exit_code" :var fname=[] :results value :noweb yes :wrap "src f90 :tangle (eval f) :comments org :exports none"
f_of_c_d = { '' : ''
2023-09-22 16:41:43 +02:00
, 'qmckl_context' : 'integer (qmckl_context)'
, 'qmckl_exit_code' : 'integer (qmckl_exit_code)'
2021-09-07 16:36:26 +02:00
, 'bool' : 'logical*8'
2021-03-30 14:51:23 +02:00
, 'int32_t' : 'integer (c_int32_t)'
, 'int64_t' : 'integer (c_int64_t)'
, 'uint32_t' : 'integer (c_int32_t)'
, 'uint64_t' : 'integer (c_int64_t)'
2021-03-30 14:51:23 +02:00
, 'float' : 'real (c_float )'
, 'double' : 'real (c_double )'
2023-09-11 17:05:41 +02:00
, 'char' : 'character(c_char )'
}
2021-04-09 11:26:04 +02:00
#+END_SRC
2021-03-30 14:51:23 +02:00
2021-04-09 11:26:04 +02:00
#+NAME:c_of_f
#+BEGIN_SRC python :var table=test :var rettyp="integer" :var fname=[] :results value :noweb yes :wrap "src f90 :tangle (eval f) :comments org :exports none"
ctypeid_d = { '' : ''
2023-09-22 16:41:43 +02:00
, 'qmckl_context' : 'integer(qmckl_context)'
, 'qmckl_exit_code' : 'integer(qmckl_exit_code)'
2021-03-30 14:51:23 +02:00
, 'integer' : 'integer(c_int32_t)'
, 'integer*8' : 'integer(c_int64_t)'
, 'real' : 'real(c_float)'
, 'real*8' : 'real(c_double)'
, 'character' : 'character(c_char)'
2021-09-07 16:36:26 +02:00
, 'character' : 'character(c_char)'
}
2021-04-09 11:26:04 +02:00
#+END_SRC
2021-03-30 14:51:23 +02:00
2021-04-09 11:26:04 +02:00
*** Parse the table
2021-04-09 11:26:04 +02:00
#+NAME: parse_table
#+BEGIN_SRC python :results none :noweb yes :exports none
def parse_table(table):
result = []
2021-06-22 23:33:09 +02:00
for line in [ [x.replace('~','') for x in y] for y in table]:
2022-01-05 19:22:16 +01:00
d = { "name" : line[0],
"c_type" : line[1],
"inout" : line[2].lower(),
"comment" : line[3] }
# Handle inout
if d["inout"] in ["input", "in"]:
d["inout"] == "in"
elif d["inout"] in ["output", "out"]:
d["inout"] == "out"
elif d["inout"] in ["input/output", "inout"]:
d["inout"] == "inout"
2021-04-17 12:35:52 +02:00
# Find dimensions (replace [] by [*] to get * in Fortran dimensions)
2022-01-05 15:56:25 +01:00
dims = d["c_type"].replace("[]","[*]").split('[')
d["rank"] = len(dims) - 1
if d["rank"] == 0:
d["dims"] = []
else:
2022-01-05 19:22:16 +01:00
d["c_type"] = d["c_type"].split('[')[0].strip()
d["dims"] = [ x.replace(']','').strip() for x in dims[1:] ]
2021-03-30 14:51:23 +02:00
result.append(d)
return result
2021-04-09 11:26:04 +02:00
#+END_SRC
2021-04-09 11:26:04 +02:00
*** Generates a C header
2021-04-09 11:26:04 +02:00
#+NAME: generate_c_header
2022-01-05 19:22:16 +01:00
#+BEGIN_SRC python :var table=test :var rettyp="qmckl_exit_code" :var fname=[] :results drawer :noweb yes :wrap "src c :tangle (eval h_func) :comments org"
<<parse_table>>
results = []
for d in parse_table(table):
name = d["name"]
c_type = d["c_type"]
# Add star for arrays
2021-04-16 00:57:08 +02:00
if d["rank"] > 0 or d["inout"] in ["out", "inout"]:
c_type += "*"
2021-04-16 00:57:08 +02:00
if d["inout"] == "out":
c_type += " const"
# Only inputs are const
if d["inout"] == "in":
2021-04-16 00:57:08 +02:00
const = "const "
else:
2021-04-16 00:57:08 +02:00
const = ""
2021-04-16 00:57:08 +02:00
results += [ f" {const}{c_type} {name}" ]
results=',\n'.join(results)
2021-06-23 11:38:38 +02:00
template = f"""{rettyp} {fname} (\n{results} ); """
return template
2021-04-09 11:26:04 +02:00
#+END_SRC
#+NAME: generate_private_c_header
#+BEGIN_SRC python :var table=test :var rettyp="qmckl_exit_code" :var fname=[] :results drawer :noweb yes :wrap "src c :tangle (eval h_private_func) :comments org"
<<parse_table>>
results = []
for d in parse_table(table):
name = d["name"]
c_type = d["c_type"]
# Add star for arrays
if d["rank"] > 0 or d["inout"] in ["out", "inout"]:
c_type += "*"
if d["inout"] == "out":
c_type += " const"
2022-01-05 15:56:25 +01:00
# Only inputs are const
if d["inout"] == "in":
const = "const "
else:
const = ""
results += [ f" {const}{c_type} {name}" ]
results=',\n'.join(results)
template = f"""{rettyp} {fname} (\n{results} ); """
return template
#+END_SRC
2021-04-09 11:26:04 +02:00
*** Generates a C interface to the Fortran function
2021-04-09 11:26:04 +02:00
#+NAME: generate_c_interface
2023-09-22 16:41:43 +02:00
#+BEGIN_SRC python :var table=[] :var rettyp="qmckl_exit_code" :var fname=[] :results value :noweb yes :wrap "src f90 :tangle (eval f) :comments org :exports none"
<<c_of_f>>
<<f_of_c>>
<<parse_table>>
d = parse_table(table)
args = ", ".join([ x["name"] for x in d ])
2021-06-22 23:33:09 +02:00
if len(args) > 100:
args = args.replace(",",""", &
""")
rettyp_c = ctypeid_d[rettyp.lower()]
results = [ f"{rettyp_c} function {fname} &"
, f" ({args}) &"
, " bind(C) result(info)"
2021-03-30 14:51:23 +02:00
, ""
, " use, intrinsic :: iso_c_binding"
, " implicit none"
, ""
]
for d in parse_table(table):
f_type = f_of_c_d[d["c_type"]]
inout = "intent("+d["inout"]+")"
name = d["name"]
# Input scalars are passed by value
2021-04-16 00:57:08 +02:00
if d["rank"] == 0 and d["inout"] == "in":
value = ", value"
else:
value = " "
# Append dimensions to the name
if d["rank"] == 0:
dims = ""
else:
d["dims"].reverse()
dims = "(" + ",".join(d["dims"]) + ")"
results += [ f" {f_type:20}, {inout:12}{value} :: {name}{dims}" ]
results += [ ""
, f" {rettyp_c}, external :: {fname}_f"
, f" info = {fname}_f &"
2021-03-30 14:51:23 +02:00
, f" ({args})"
, ""
, f"end function {fname}"
]
results='\n'.join(results)
return results
2021-04-09 11:26:04 +02:00
#+END_SRC
2021-04-09 11:26:04 +02:00
*** Generates a Fortran interface to the C function
2021-04-09 11:26:04 +02:00
#+NAME: generate_f_interface
2023-09-22 16:41:43 +02:00
#+BEGIN_SRC python :var table=test :var rettyp="qmckl_exit_code" :var fname=[] :results value :noweb yes :wrap "src f90 :tangle (eval fh_func) :comments org :exports none"
<<c_of_f>>
<<f_of_c>>
<<parse_table>>
d = parse_table(table)
args = ", ".join([ x["name"] for x in d ])
rettyp_c = ctypeid_d[rettyp.lower()]
results = [ f"interface"
, f" {rettyp_c} function {fname} &"
, f" ({args}) &"
, " bind(C)"
, " use, intrinsic :: iso_c_binding"
, " import"
, " implicit none"
, ""
]
for d in parse_table(table):
f_type = f_of_c_d[d["c_type"]]
inout = "intent("+d["inout"]+")"
name = d["name"]
# Input scalars are passed by value
if d["rank"] == 0 and d["inout"] == "in":
value = ", value"
else:
value = " "
# Append dimensions to the name
if d["rank"] == 0:
dims = ""
else:
d["dims"].reverse()
dims = "(" + ",".join(d["dims"]) + ")"
results += [ f" {f_type:20}, {inout:12}{value} :: {name}{dims}" ]
results += [ ""
, f" end function {fname}"
, f"end interface"
]
results='\n'.join(results)
return results
#+END_SRC
#+NAME: generate_private_f_interface
2023-09-22 16:41:43 +02:00
#+BEGIN_SRC python :var table=test :var rettyp="qmckl_exit_code" :var fname=[] :results value :noweb yes :wrap "src f90 :tangle (eval fh_private_func) :comments org :exports none"
<<c_of_f>>
<<f_of_c>>
<<parse_table>>
d = parse_table(table)
args = ", ".join([ x["name"] for x in d ])
rettyp_c = ctypeid_d[rettyp.lower()]
results = [ f"interface"
, f" {rettyp_c} function {fname} &"
, f" ({args}) &"
, " bind(C)"
, " use, intrinsic :: iso_c_binding"
2021-03-30 14:51:23 +02:00
, " import"
, " implicit none"
, ""
]
for d in parse_table(table):
f_type = f_of_c_d[d["c_type"]]
inout = "intent("+d["inout"]+")"
name = d["name"]
# Input scalars are passed by value
2021-04-16 00:57:08 +02:00
if d["rank"] == 0 and d["inout"] == "in":
value = ", value"
else:
value = " "
# Append dimensions to the name
if d["rank"] == 0:
dims = ""
else:
d["dims"].reverse()
dims = "(" + ",".join(d["dims"]) + ")"
results += [ f" {f_type:20}, {inout:12}{value} :: {name}{dims}" ]
results += [ ""
, f" end function {fname}"
, f"end interface"
]
results='\n'.join(results)
return results
2021-04-09 11:26:04 +02:00
#+END_SRC
** Creating provide functions
#+NAME: write_provider_header
#+BEGIN_SRC python :var group="GROUP" :var data="DATA" :results drawer :noweb yes :wrap "src c :comments org :tangle (eval h_private_func) :noweb yes :export none"
template = "qmckl_exit_code qmckl_provide_{{ group }}_{{ data }}(qmckl_context context);"
msg = template.replace("{{ group }}", group) \
.replace("{{ data }}", data)
return msg
#+END_SRC
#+RESULTS: write_provider_header
#+begin_src c :comments org :tangle (eval h_private_func) :noweb yes :export none
qmckl_exit_code qmckl_provide_GROUP_DATA(qmckl_context context);
#+end_src
#+NAME: write_provider_pre
#+BEGIN_SRC python :var group="GROUP" :var data="DATA" :var dimension="DIMENSION" :results drawer :noweb yes :wrap "src c :comments org :tangle (eval c) :noweb yes :export none"
template = """qmckl_exit_code qmckl_provide_{{ group }}_{{ data }}(qmckl_context context)
{
2022-08-07 14:57:10 +02:00
qmckl_exit_code rc = QMCKL_SUCCESS;
if (qmckl_context_check(context) == QMCKL_NULL_CONTEXT) {
return qmckl_failwith( context,
QMCKL_INVALID_CONTEXT,
"qmckl_provide_{{ group }}_{{ data }}",
NULL);
}
qmckl_context_struct* const ctx = (qmckl_context_struct*) context;
assert (ctx != NULL);
if (!ctx->{{ group }}.provided) {
return qmckl_failwith( context,
QMCKL_NOT_PROVIDED,
"qmckl_provide_{{ group }}_{{ data }}",
NULL);
}
/* Compute if necessary */
if (ctx->point.date > ctx->{{ group }}.{{ data }}_date) {
2022-08-07 14:57:10 +02:00
qmckl_memory_info_struct mem_info = qmckl_memory_info_struct_zero;
mem_info.size = {{ dimension }} * sizeof(double);
if (ctx->{{ group }}.{{ data }} != NULL) {
qmckl_memory_info_struct mem_info_test = qmckl_memory_info_struct_zero;
rc = qmckl_get_malloc_info(context, ctx->{{ group }}.{{ data }}, &mem_info_test);
/* if rc != QMCKL_SUCCESS, we are maybe in an _inplace function because the
memory was not allocated with qmckl_malloc */
if ((rc == QMCKL_SUCCESS) && (mem_info_test.size != mem_info.size)) {
rc = qmckl_free(context, ctx->{{ group }}.{{ data }});
assert (rc == QMCKL_SUCCESS);
ctx->{{ group }}.{{ data }} = NULL;
}
}
/* Allocate array */
if (ctx->{{ group }}.{{ data }} == NULL) {
double* {{ data }} = (double*) qmckl_malloc(context, mem_info);
if ({{ data }} == NULL) {
return qmckl_failwith( context,
QMCKL_ALLOCATION_FAILED,
"qmckl_{{ group }}_{{ data }}",
NULL);
}
ctx->{{ group }}.{{ data }} = {{ data }};
}
"""
msg = template.replace("{{ group }}", group) \
.replace("{{ data }}", data) \
.replace("{{ dimension }}", dimension)
return msg
#+END_SRC
#+RESULTS: write_provider_pre
#+begin_src c :comments org :tangle (eval c) :noweb yes :export none
qmckl_exit_code qmckl_provide_GROUP_DATA(qmckl_context context)
{
qmckl_exit_code rc;
if (qmckl_context_check(context) == QMCKL_NULL_CONTEXT) {
return qmckl_failwith( context,
QMCKL_INVALID_CONTEXT,
"qmckl_provide_GROUP_DATA",
NULL);
}
qmckl_context_struct* const ctx = (qmckl_context_struct*) context;
assert (ctx != NULL);
if (!ctx->GROUP.provided) {
return qmckl_failwith( context,
QMCKL_NOT_PROVIDED,
"qmckl_provide_GROUP_DATA",
NULL);
}
/* Compute if necessary */
if (ctx->point.date > ctx->GROUP.DATA_date) {
if (ctx->point.alloc_date > ctx->GROUP.DATA_date) {
rc = qmckl_free(context, ctx->GROUP.DATA);
assert (rc == QMCKL_SUCCESS);
ctx->GROUP.DATA = NULL;
}
/* Allocate array */
if (ctx->GROUP.DATA == NULL) {
qmckl_memory_info_struct mem_info = qmckl_memory_info_struct_zero;
mem_info.size = DIMENSION * sizeof(double);
double* DATA = (double*) qmckl_malloc(context, mem_info);
if (DATA == NULL) {
return qmckl_failwith( context,
QMCKL_ALLOCATION_FAILED,
"qmckl_GROUP_DATA",
NULL);
}
ctx->GROUP.DATA = DATA;
}
#+end_src
#+NAME: write_provider_post
#+BEGIN_SRC python :var group="BASIS" :var data="DATA" :results drawer :noweb yes :wrap "src c :comments org :tangle (eval c) :noweb yes :export none"
template = """ if (rc != QMCKL_SUCCESS) {
return rc;
}
ctx->{{ group }}.{{ data }}_date = ctx->date;
}
return QMCKL_SUCCESS;
}
"""
msg = template.replace("{{ group }}", group) \
.replace("{{ data }}", data)
return msg
#+END_SRC
#+RESULTS: write_provider_post
#+begin_src c :comments org :tangle (eval c) :noweb yes :export none
if (rc != QMCKL_SUCCESS) {
return rc;
}
ctx->BASIS.DATA_date = ctx->date;
}
return QMCKL_SUCCESS;
}
#+end_src