# -*- mode: org -*- * Library of org-mode functions :noexport: ** Defines the name of the current file #+NAME: filename #+begin_src elisp :tangle no (file-name-nondirectory (substring buffer-file-name 0 -4)) #+end_src ** Function to get the value of a property. #+NAME: get_value #+begin_src elisp :var key="Type" (org-with-point-at org-babel-current-src-block-location (org-entry-get nil key t)) #+end_src ** Table of function arguments #+NAME: test | 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~ | *** Fortran-C type conversions #+NAME:f_of_c #+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 = { '' : '' , 'qmckl_context' : 'integer (qmckl_context)' , 'qmckl_exit_code' : 'integer (qmckl_exit_code)' , 'bool' : 'logical*8' , 'int32_t' : 'integer (c_int32_t)' , 'int64_t' : 'integer (c_int64_t)' , 'uint32_t' : 'integer (c_int32_t)' , 'uint64_t' : 'integer (c_int64_t)' , 'float' : 'real (c_float )' , 'double' : 'real (c_double )' , 'char' : 'character(c_char )' } #+END_SRC #+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 = { '' : '' , 'qmckl_context' : 'integer(qmckl_context)' , 'qmckl_exit_code' : 'integer(qmckl_exit_code)' , 'integer' : 'integer(c_int32_t)' , 'integer*8' : 'integer(c_int64_t)' , 'real' : 'real(c_float)' , 'real*8' : 'real(c_double)' , 'character' : 'character(c_char)' , 'character' : 'character(c_char)' } #+END_SRC *** Parse the table #+NAME: parse_table #+BEGIN_SRC python :results none :noweb yes :exports none def parse_table(table): result = [] for line in [ [x.replace('~','') for x in y] for y in table]: 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" # Find dimensions (replace [] by [*] to get * in Fortran dimensions) dims = d["c_type"].replace("[]","[*]").split('[') d["rank"] = len(dims) - 1 if d["rank"] == 0: d["dims"] = [] else: d["c_type"] = d["c_type"].split('[')[0].strip() d["dims"] = [ x.replace(']','').strip() for x in dims[1:] ] result.append(d) return result #+END_SRC *** Generates a C header #+NAME: generate_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_func) :comments org" <> 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" # 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 #+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" <> 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" # 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 *** Generates a C interface to the Fortran function #+NAME: generate_c_interface #+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" <> <> <> d = parse_table(table) args = ", ".join([ x["name"] for x in d ]) 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)" , "" , " use qmckl_constants" , " 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" {rettyp_c}, external :: {fname}_f" , f" info = {fname}_f &" , f" ({args})" , "" , f"end function {fname}" ] results='\n'.join(results) return results #+END_SRC *** Generates a Fortran interface to the C function #+NAME: generate_f_interface #+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" <> <> <> 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 qmckl_constants" , " 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 #+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" <> <> <> 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 qmckl_constants" , " 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 ** 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) { 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) { 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