66import subprocess
77import sys
88from pathlib import Path
9+ from shutil import rmtree as shrm
910
1011FORTRAN_EXTS = {".f", ".for", ".f90", ".F", ".F90"}
12+ TAPENADE_USELESS_ZONES = 3
1113
1214def is_fortran(p: Path) -> bool:
1315 return p.suffix in FORTRAN_EXTS
@@ -8030,7 +8032,7 @@ def main():
80308032 help="AD modes to generate: d (forward scalar), dv (forward vector), b (reverse scalar), bv (reverse vector), all (all modes). Default: all")
80318033 ap.add_argument("--nbdirsmax", type=int, default=4, help="Maximum number of derivative directions for vector mode (default: 4)")
80328034 ap.add_argument("--flat", action="store_true", help="Use flat directory structure (all files in function directory, single DIFFSIZES.inc)")
8033- ap.add_argument("--debug- genlib", default=None, required=False)
8035+ ap.add_argument("--genlib", default=None, required=False, help="Generate Tapenade external library" )
80348036 ap.add_argument("--extra", nargs=argparse.REMAINDER, help="Extra args passed to Tapenade after -d/-r", default=[])
80358037 args = ap.parse_args()
80368038
@@ -8085,10 +8087,10 @@ def main():
80858087 modes = {"d", "dv", "b", "bv"}
80868088
80878089 # Determine which specific modes to run
8088- run_d = "d" in modes
8089- run_dv = "dv" in modes
8090- run_b = "b" in modes
8091- run_bv = "bv" in modes
8090+ run_d = "d" in modes or args.genlib
8091+ run_dv = not args.genlib and "dv" in modes
8092+ run_b = not args.genlib and "b" in modes
8093+ run_bv = not args.genlib and "bv" in modes
80928094
80938095 # List of non-differentiable functions to skip entirely
80948096 # See SKIPPED_FUNCTIONS.md for detailed documentation on why each is skipped
@@ -8123,9 +8125,6 @@ def run_task(task):
81238125
81248126 # Parse the Fortran function to get signature
81258127 func_name, inputs, outputs, inout_vars, func_type, all_params, parse_warnings, param_types, has_sufficient_docs = parse_fortran_function(src)
8126- print(f"INPUTS = {inputs}")
8127- print(f"ALL_PARAMS = {all_params}")
8128- print(f"PARAM_TYPES = {param_types}")
81298128
81308129 if not func_name:
81318130 print(f"Skipping {src}: Could not parse function signature", file=sys.stderr)
@@ -8161,29 +8160,21 @@ def run_task(task):
81618160 flat_mode = args.flat
81628161 mode_dirs = {}
81638162
8164- if (args.debug_genlib):
8165- # if (False):
8163+ if (args.genlib):
81668164 # When generating the general lib useful to Tapenade, we will save everything in a tmp file
81678165 # and only the lib in a local folder used to concatenate everything afterwards.
81688166 tmp_dir = Path("TMPGENLIB").resolve()
81698167 tmp_dir.mkdir(parents=True, exist_ok=True)
81708168 func_out_dir = tmp_dir
81718169 genlib_dir = out_dir
81728170 genlib_dir.mkdir(parents=True, exist_ok=True)
8173- if run_d:
8174- mode_dirs['d'] = tmp_dir
8175- if run_b:
8176- mode_dirs['b'] = tmp_dir
8177- if run_dv:
8178- mode_dirs['dv'] = tmp_dir
8179- if run_bv:
8180- mode_dirs['bv'] = tmp_dir
8171+ mode_dirs['d'] = tmp_dir
81818172
81828173 def convert_tap_result2genlib_format(l: str) :
81838174 out = []
81848175 infos = l.split("[")[1]
81858176 use_infos = True
8186- for c in infos[3 :]: # Don't bother with the first 3 elements
8177+ for c in infos[TAPENADE_USELESS_ZONES :]: # Don't bother with the first
81878178 if(c == "]"):
81888179 break
81898180 if use_infos:
@@ -8206,14 +8197,11 @@ def parse_tap_trace4inout(fname):
82068197
82078198 # Now we read the next one, and start looking at the arguments
82088199 var2idx_mapping = dict()
8209- idx2var_mapping = dict()
82108200 l = f.readline().strip()
8211- for v in l.split(" ")[3:]: # The first three variables are useless in the sense that they are only used internally for tapenade
8212- # v is a string resembling "[id]varName" where id is an identifier internal to tapenade for the zone of variable varName
8201+ for v in l.split(" ")[TAPENADE_USELESS_ZONES:]: # The first variables are useless
82138202 not_quite_id, var_name = v.split("]")
8214- idx = int(not_quite_id[1:])
8203+ idx = int(not_quite_id[1:])-TAPENADE_USELESS_ZONES
82158204 var2idx_mapping[var_name] = idx
8216- idx2var_mapping[idx] = var_name
82178205
82188206 # Now that the mapping has been parsed, we move towards the end of the analysis phase, and extract the summary
82198207 sought_after = "terminateFGForUnit Unit"
@@ -8335,7 +8323,7 @@ def parse_tap_trace4inout(fname):
83358323 for dep_file in main_file_removed:
83368324 cmd.append(str(dep_file))
83378325 cmd.extend(list(args.extra))
8338- if (args.debug_genlib ):
8326+ if (args.genlib ):
83398327 cmd = cmd + ["-traceinout", src.stem]
83408328
83418329 try:
@@ -8344,6 +8332,7 @@ def parse_tap_trace4inout(fname):
83448332 # Format command for logging (properly quoted for shell copy-paste)
83458333 print("CMD:", cmd)
83468334 cmd_str = ' '.join(shlex.quote(str(arg)) for arg in cmd)
8335+ print(cmd_str)
83478336 logf.write(f"Command: {cmd_str}\n")
83488337 logf.write(f"Function: {func_name}\n")
83498338 logf.write(f"Parsed inputs: {inputs}\n")
@@ -8384,41 +8373,43 @@ def parse_tap_trace4inout(fname):
83848373 print(f" ERROR: Exception during forward mode execution: {e}", file=sys.stderr)
83858374 return_codes["forward"] = 999
83868375
8387- if (args.debug_genlib ) : # Everything went well, and we are trying to generate the external lib
8376+ if (args.genlib ) : # Everything went well, and we are trying to generate the external lib
83888377 read_not_written, not_read_then_written, read_then_written, var2idx = parse_tap_trace4inout(mode_log_path)
8389- print(var2idx)
8390- param_2_tap_reordering = [var2idx[p.lower()]-3 for p in all_params]
8391- print("Reordering is {}".format(param_2_tap_reordering))
8392- with open("externalLib", "a") as f:
8378+ if func_type == 'FUNCTION':
8379+ param_for_genlib = all_params + [src.stem]
8380+ else:
8381+ param_for_genlib = all_params
8382+ param_2_tap_reordering = [var2idx[p.lower()] for p in param_for_genlib]
8383+ with open("DiffBlasGenLib", "a") as f:
83938384 f.write(("function " if func_type == 'FUNCTION' else "subroutine ") + src.stem + ":\n")
83948385 indent = " "
83958386 f.write(indent + "external:\n")
8396- shape = "(" + ", ".join(["param " + str(i) for i in range(1,len(all_params)+1)]) + ")" ## TODO: Need to add ', return' in case of a function,. dpeending on whether it is within the all params or not
8387+ shape = "(" + ", ".join(["param " + str(i) for i in range(1,len(all_params)+1)] + ["result" if func_type == 'FUNCTION' else ""] ) + ")" ## TODO: Need to add ', return' in case of a function,. dpeending on whether it is within the all params or not
83978388 f.write(indent + "shape: " + shape + "\n")
83988389 types = []
8399- for p in all_params :
8390+ for p in param_for_genlib :
84008391 current_type = ""
8401- if p in param_types['real_vars']:
8392+ if p.upper() in param_types['real_vars'] or p.lower() in param_types['real_vars']:
84028393 current_type = "metavar float" # We should probably be more precise in order to handle mixed precision things
84038394 # Namely, adapt to
84048395 # modifiedType(modifiers(ident double), float() for double / REAL*8
84058396 # float() for single precision
8406- elif p in param_types['complex_vars']:
8397+ elif p.upper() in param_types['complex_vars'] or p.lower() in param_types['complex_vars']:
84078398 current_type = "metavar complex"
84088399 # Similar to the real variables, we should be able to be more precise in terms of precision of the complex variable
8409- elif p in param_types['integer_vars']:
8400+ elif p.upper() in param_types['integer_vars'] or p.lower() in param_types['integer_vars']:
84108401 current_type = "metavar integer"
8411- elif p in param_types['char_vars']:
8402+ elif p.upper() in param_types['char_vars'] or p.lower() in param_types['char_vars']:
84128403 current_type = "character()"
8413- if p in param_types['array_vars']: # Will be "is_matrix_var" or "is_array_var" or something along those lines
8404+ if p.upper() in param_types['array_vars'] or p.lower() in param_types['array_vars']:
84148405 current_type = "arrayType(" + current_type + ", dimColons())"
8415-
84168406 types.append(current_type)
84178407 types = "(" + ", ".join(types) + ")"
84188408 f.write(indent + "type: " + types + "\n")
84198409 f.write(indent + "ReadNotWritten: (" + ", ".join([read_not_written[i] for i in param_2_tap_reordering]) + ")\n")
84208410 f.write(indent + "NotReadThenWritten: (" + ", ".join([not_read_then_written[i] for i in param_2_tap_reordering]) + ")\n")
84218411 f.write(indent + "ReadThenWritten: (" + ", ".join([read_then_written[i] for i in param_2_tap_reordering]) + ")\n")
8412+ f.write("\n")
84228413
84238414 # Run scalar reverse mode (b)
84248415 if run_b:
@@ -8818,37 +8809,31 @@ def parse_tap_trace4inout(fname):
88188809
88198810
88208811
8821- if (args.debug_genlib):
8822- '''
8823- WORKING HERE
8824- XXXXXXXXXX
8825- Need to figure out:
8826- -> The various parameters of a subroutine, their types
8827- -> Convert these types into Tapenade's GenLib format
8828- -> Link the number of a parameter in the prototype of the subroutine with its rank in tapenade's inout analysis
8829- -> Dump everything into a single genlib file
8830- '''
8831- # Add tests for existence of file / correct extension / ...
8832- file_path = fortran_dir / args.debug_genlib
8833-
8834- tasks = []
8835- func_stem = file_path.stem.lower()
8836- rel = file_path.relative_to(fortran_dir)
8837- out_dir = out_root / rel.parent
8838- out_dir.mkdir(parents=True, exist_ok=True)
8839- log_path = out_dir / (file_path.stem + ".tapenade.log")
8840- # Explicitely force the diff modes for now:
8841- run_d, run_dv, run_b, run_bv = True, False, True, False
8842- tasks.append((file_path, out_dir, log_path, run_d, run_dv, run_b, run_bv))
8843- constraints = parse_parameter_constraints(file_path)
8844- parsed_function = parse_fortran_function(file_path, suppress_warnings=False)
8845- args_are = ["function_name", "inputs", "outputs", "inout_vars", "func_type", "params", "warnings", "param_types", "has_sufficient_docs"]
8846- for idx, v in enumerate(parsed_function):
8847- print(f"{args_are[idx]} == {v}")
8848-
8849- run_task(tasks[0])
8850-
8851- return
8812+ # if (args.genlib):
8813+ # '''
8814+ # WORKING HERE
8815+ # XXXXXXXXXX
8816+ # Need to figure out:
8817+ # -> The various parameters of a subroutine, their types
8818+ # -> Convert these types into Tapenade's GenLib format
8819+ # -> Link the number of a parameter in the prototype of the subroutine with its rank in tapenade's inout analysis
8820+ # -> Dump everything into a single genlib file
8821+ # '''
8822+ # # Add tests for existence of file / correct extension / ...
8823+ # file_path = fortran_dir / args.genlib
8824+
8825+ # tasks = []
8826+ # func_stem = file_path.stem.lower()
8827+ # rel = file_path.relative_to(fortran_dir)
8828+ # out_dir = out_root / rel.parent
8829+ # out_dir.mkdir(parents=True, exist_ok=True)
8830+ # log_path = out_dir / (file_path.stem + ".tapenade.log")
8831+ # # Explicitely force the diff modes for now:
8832+ # run_d, run_dv, run_b, run_bv = True, False, False, False
8833+ # tasks.append((file_path, out_dir, log_path, run_d, run_dv, run_b, run_bv))
8834+ # run_task(tasks[0])
8835+
8836+ # return
88528837
88538838
88548839
0 commit comments