From 24e6350f1148e465799d2dc711cc71d4ca9a4cb8 Mon Sep 17 00:00:00 2001 From: alexvalentine94 Date: Sat, 10 Feb 2024 10:09:34 +0000 Subject: [PATCH 1/9] d1s sim. run --- jade/inputfile.py | 35 ++-------------- jade/testrun.py | 104 +++++++--------------------------------------- 2 files changed, 18 insertions(+), 121 deletions(-) diff --git a/jade/inputfile.py b/jade/inputfile.py index 9b784b75..e4cc254f 100644 --- a/jade/inputfile.py +++ b/jade/inputfile.py @@ -233,7 +233,6 @@ def update_zaidinfo(self, lib_manager): self.matlist.update_info(lib_manager) -# def add_stopCard(self, nps, ctme, precision): def add_stopCard(self, nps): """ Add STOP card @@ -242,10 +241,6 @@ def add_stopCard(self, nps): ---------- nps : int number of particles to simulate. - ctme : int - copmuter time. - precision : (str, float) - tally number, precision. Returns ------- @@ -259,21 +254,9 @@ def add_stopCard(self, nps): line = line+'NPS '+str(int(nps))+' ' except ValueError: pass # an escaped NaN - """ - if ctme is not None: - try: - line = line+'CTME '+str(int(ctme))+' ' - except ValueError: - pass # an escaped NaN - - if precision is not None: - tally = precision[0] - error = precision[1] - line = line+str(tally)+' '+str(error) - """ if line == 'STOP ': raise ValueError(""" -Specify at least one among nps, ctme or precision""") +Specify an nps for the simulation""") line = line+'\n' @@ -676,32 +659,21 @@ def add_track_contribution(self, tallyID, zaids, who='parent'): class D1S5_InputFile(D1S_Input): - def add_stopCard(self, nps, ctme, precision): + def add_stopCard(self, nps): """ STOP card is not supported in MCNP 5. This simply is translated to a - nps card. Warnings are prompt to the user if ctme or precision are - specified. + nps card. Parameters ---------- nps : int number of particles to simulate - ctme = int - computer time - precision = (str, float) - tuple indicating the tally number and the precision requested Returns ------- None. """ - if ctme is not None or precision is not None: - if self.name != 'SphereSDDR': - warnings.warn(''' -STOP card is substituted with normal NPS card for MCNP5. -specified ctme or precision parameters will be ignored -''') if nps is None: raise ValueError(' NPS value is mandatory for MCNP 5 inputs') @@ -709,7 +681,6 @@ def add_stopCard(self, nps, ctme, precision): card = par.Card([line], 5, -1) self.cards['settings'].append(card) - @contextmanager def suppress_stdout(): with open(os.devnull, "w") as devnull: diff --git a/jade/testrun.py b/jade/testrun.py index 9107feb9..1b266dc8 100644 --- a/jade/testrun.py +++ b/jade/testrun.py @@ -136,25 +136,13 @@ def __init__(self, inp, lib, config, log, confpath, runoption): except KeyError: self.d1s = False - """ - # Chek for valid code - code = config['Code'] - if code not in CODE_TAGS.keys(): - raise ValueError(code+' is not an admissible value for code.\n' + - 'Please double check the configuration file.') - else: - self.code = code # transport code to be used for the benchmark - """ # Generate input file template according to transport code if self.d1s: - d1s_ipt = os.path.join(inp, "d1s", self.name + ".i") + d1s_ipt = os.path.join(inp, "d1s", os.path.basename(inp) + ".i") self.d1s_inp = ipt.D1S5_InputFile.from_text(d1s_ipt) - # It also have additional files then that must be in the - # VRT folder (irradiation and reaction files) - # To change with revised folder structure - removed VRT. - irrfile = os.path.join(VRTpath, self.d1s_inp.name, self.inp.name + "_irrad") + irrfile = os.path.join(inp, "d1s", os.path.basename(inp) + "_irrad") reacfile = os.path.join( - VRTpath, self.d1s_inp.name, self.d1s_inp.name + "_react" + inp, "d1s", os.path.basename(inp) + "_react" ) try: self.irrad = IrradiationFile.from_text(irrfile) @@ -163,17 +151,11 @@ def __init__(self, inp, lib, config, log, confpath, runoption): self.log.adjourn( "d1S irradition and reaction files not found, skipping..." ) - # For instance in sphere test they are not provided - # There may be reasons why these files are not provided, it is - # responsability of the user to make them available or not. - # self.irrad = None - # self.react = None + self.name = self.d1s_inp.name if self.mcnp: mcnp_ipt = os.path.join(inp, "mcnp", os.path.basename(inp) + ".i") self.mcnp_inp = ipt.InputFile.from_text(mcnp_ipt) self.name = self.mcnp_inp.name - # self.irrad = None - # self.react = None if self.serpent: serpent_ipt = os.path.join(inp, "serpent", os.path.basename(inp) + ".i") self.serpent_inp = ipt.SerpentInputFile.from_text(serpent_ipt) @@ -181,37 +163,6 @@ def __init__(self, inp, lib, config, log, confpath, runoption): openmc_ipt = os.path.join(inp, "openmc") self.openmc_inp = ipt.OpenMCInputFiles.from_path(openmc_ipt) - # Need to add this back if user only running MCNP - # Add the stop card according to config - """ - config = config.dropna() - try: - nps = config['NPS cut-off'] - except KeyError: - nps = None - if nps is np.nan: - nps = None - try: - ctme = config['CTME cut-off'] - except KeyError: - ctme = None - if ctme is np.nan: - ctme = None - try: - tally = config['Relative Error cut-off'].split('-')[0] - error = config['Relative Error cut-off'].split('-')[1] - precision = (tally, error) - except KeyError: - precision = None - - self.nps = nps - self.ctme = ctme - self.precision = precision - - # Directory where the MCNP run will be performed - self.MCNPdir = None - """ - def _translate_input(self, lib, libmanager): """ Translate the input template to selected library @@ -236,7 +187,6 @@ def _translate_input(self, lib, libmanager): add = self.d1s_inp.translate( lib, libmanager, - "d1s", original_irradfile=self.irrad, original_reacfile=self.react, ) @@ -277,8 +227,6 @@ def generate_test(self, lib_directory, libmanager, run_dir=None): self._translate_input(self.lib, libmanager) # Add stop card - # Need to retain adding ctme and precision for MCNP - # self.inp.add_stopCard(self.nps, self.ctme, self.precision) if self.d1s: self.d1s_inp.add_stopCard(self.nps) if self.mcnp: @@ -301,25 +249,20 @@ def generate_test(self, lib_directory, libmanager, run_dir=None): shutil.rmtree(motherdir) os.mkdir(motherdir) - # edits_file = os.path.join(directoryVRT, 'inp_edits.txt') - # ww_file = os.path.join(directoryVRT, 'wwinp') - # if os.path.exists(directoryVRT): - # # This was tested only for sphere... be careful - # self.inp.add_edits(edits_file) # Add variance reduction - # Allow space for personalization getting additional modification self.custom_inp_modifications() if self.d1s: - os.mkdir(os.path.join(motherdir, "d1s")) - outinpfile = os.path.join(motherdir, "d1s", testname) + d1s_dir = os.path.join(motherdir, "d1s") + os.mkdir(d1s_dir) + outinpfile = os.path.join(d1s_dir, testname) self.d1s_inp.write(outinpfile) # And accessory files if needed if self.irrad is not None: - self.irrad.write(motherdir, "d1s") + self.irrad.write(d1s_dir) if self.react is not None: - self.react.write(motherdir, "d1s") - # Get VRT files if available + self.react.write(d1s_dir) + # Get WW files if available wwinp = os.path.join(self.original_inp, "d1s", "wwinp") if os.path.exists(wwinp): outfile = os.path.join(motherdir, "d1s", "wwinp") @@ -336,30 +279,13 @@ def generate_test(self, lib_directory, libmanager, run_dir=None): shutil.copyfile(wwinp, outfile) if self.serpent: - # Impliment serpent outputfile generation here + # Implement serpent outputfile generation here pass if self.openmc: - # Impliment openmc outputfile generation here + # Implement openmc outputfile generation here pass - """ - # Write new input file - outinpfile = os.path.join(motherdir, testname) - self.inp.write(outinpfile) - # And accessory files if needed - if self.irrad is not None: - self.irrad.write(motherdir) - if self.react is not None: - self.react.write(motherdir) - - # Get VRT files if available - wwinp = os.path.join(self.path_VRT, testname, 'wwinp') - if os.path.exists(wwinp): - outfile = os.path.join(motherdir, 'wwinp') - shutil.copyfile(wwinp, outfile) - """ - def custom_inp_modifications(self): """ Perform additional operation on the input before generation. In this @@ -1503,7 +1429,7 @@ def _generate_irradiation_file(self, daughters): print( CORANGE + """ - Warning: irradiations schedules were not find for all specified daughters. + Warning: irradiation schedules were not found for all specified daughters. """ + CEND ) @@ -1519,10 +1445,10 @@ class FNGTest(Test): def custom_inp_modifications(self): # Add the tracking for daughters in tally 14 zaids = self.irrad.get_daughters() - self.inp.add_track_contribution("F14:p", zaids, who="daughter") + self.d1s_inp.add_track_contribution("F14:p", zaids, who="daughter") # Add the tracking for daughters in tally 24 zaids = self.react.get_parents() - self.inp.add_track_contribution("F24:p", zaids, who="parent") + self.d1s_inp.add_track_contribution("F24:p", zaids, who="parent") class MultipleTest: From 563c7ad5c5116f51c904e0a0e4203b5412622eba Mon Sep 17 00:00:00 2001 From: alexvalentine94 Date: Sat, 10 Feb 2024 10:52:49 +0000 Subject: [PATCH 2/9] iter cyl and sddr config --- docs/source/usage/benchmarks.rst | 2 + jade/default_settings/Config.xlsx | Bin 11215 -> 15278 bytes jade/inputfile.py | 2 +- .../{mcnp => d1s}/ITER_Cyl_SDDR.i | 0 .../ITER_Cyl_SDDR => d1s}/ITER_Cyl_SDDR_irrad | 0 .../ITER_Cyl_SDDR => d1s}/ITER_Cyl_SDDR_react | 0 .../ITER_Cyl_SDDR/openmc/geometry.xml | 219 ---------- .../ITER_Cyl_SDDR/openmc/materials.xml | 158 -------- .../ITER_Cyl_SDDR/serpent/ITER_Cyl_SDDR.i | 374 ------------------ 9 files changed, 3 insertions(+), 752 deletions(-) rename jade/install_files/Benchmarks_Inputs/ITER_Cyl_SDDR/{mcnp => d1s}/ITER_Cyl_SDDR.i (100%) rename jade/install_files/Benchmarks_Inputs/ITER_Cyl_SDDR/{d1S/ITER_Cyl_SDDR => d1s}/ITER_Cyl_SDDR_irrad (100%) rename jade/install_files/Benchmarks_Inputs/ITER_Cyl_SDDR/{d1S/ITER_Cyl_SDDR => d1s}/ITER_Cyl_SDDR_react (100%) delete mode 100644 jade/install_files/Benchmarks_Inputs/ITER_Cyl_SDDR/openmc/geometry.xml delete mode 100644 jade/install_files/Benchmarks_Inputs/ITER_Cyl_SDDR/openmc/materials.xml delete mode 100644 jade/install_files/Benchmarks_Inputs/ITER_Cyl_SDDR/serpent/ITER_Cyl_SDDR.i diff --git a/docs/source/usage/benchmarks.rst b/docs/source/usage/benchmarks.rst index 3774b6a5..6a746723 100644 --- a/docs/source/usage/benchmarks.rst +++ b/docs/source/usage/benchmarks.rst @@ -27,6 +27,8 @@ benchmark in JADE. expect a single library (e.g. ``31c``), activation ones require two: an activation library and a transport one for all zaids that cannot be activated (e.g. ``99c-31c``). + * The irradiation and activation files if present for a SDDR benchmark should be + placed in the same folder as the input file. * In activation benchmarks, the library that is considered the assessed one is always the activation library (i.e. the first provided). No track is kept during the post-processing of which was the transport library used diff --git a/jade/default_settings/Config.xlsx b/jade/default_settings/Config.xlsx index edb2ba002a33a45c0b885941fdc1c1544cf14e2d..b324648b315f3eeba8c323099be2865b4285fd2b 100644 GIT binary patch literal 15278 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 R5D+}z5f9KAaE_m!{vV8az1#o* literal 11215 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 diff --git a/jade/inputfile.py b/jade/inputfile.py index e4cc254f..68c0dec7 100644 --- a/jade/inputfile.py +++ b/jade/inputfile.py @@ -695,7 +695,7 @@ def suppress_stdout(): def check_transport_activation(lib): # Operate on the newlib, should arrive in the 99c-31c format errmsg = """ - Please define the pair activation-transport lib for the FNG benchmark + Please define the pair activation-transport lib for SDDR benchmarks (e.g. 99c-31c). See additional details on the documentation. """ try: diff --git a/jade/install_files/Benchmarks_Inputs/ITER_Cyl_SDDR/mcnp/ITER_Cyl_SDDR.i b/jade/install_files/Benchmarks_Inputs/ITER_Cyl_SDDR/d1s/ITER_Cyl_SDDR.i similarity index 100% rename from jade/install_files/Benchmarks_Inputs/ITER_Cyl_SDDR/mcnp/ITER_Cyl_SDDR.i rename to jade/install_files/Benchmarks_Inputs/ITER_Cyl_SDDR/d1s/ITER_Cyl_SDDR.i diff --git a/jade/install_files/Benchmarks_Inputs/ITER_Cyl_SDDR/d1S/ITER_Cyl_SDDR/ITER_Cyl_SDDR_irrad b/jade/install_files/Benchmarks_Inputs/ITER_Cyl_SDDR/d1s/ITER_Cyl_SDDR_irrad similarity index 100% rename from jade/install_files/Benchmarks_Inputs/ITER_Cyl_SDDR/d1S/ITER_Cyl_SDDR/ITER_Cyl_SDDR_irrad rename to jade/install_files/Benchmarks_Inputs/ITER_Cyl_SDDR/d1s/ITER_Cyl_SDDR_irrad diff --git a/jade/install_files/Benchmarks_Inputs/ITER_Cyl_SDDR/d1S/ITER_Cyl_SDDR/ITER_Cyl_SDDR_react b/jade/install_files/Benchmarks_Inputs/ITER_Cyl_SDDR/d1s/ITER_Cyl_SDDR_react similarity index 100% rename from jade/install_files/Benchmarks_Inputs/ITER_Cyl_SDDR/d1S/ITER_Cyl_SDDR/ITER_Cyl_SDDR_react rename to jade/install_files/Benchmarks_Inputs/ITER_Cyl_SDDR/d1s/ITER_Cyl_SDDR_react diff --git a/jade/install_files/Benchmarks_Inputs/ITER_Cyl_SDDR/openmc/geometry.xml b/jade/install_files/Benchmarks_Inputs/ITER_Cyl_SDDR/openmc/geometry.xml deleted file mode 100644 index aee3bc6b..00000000 --- a/jade/install_files/Benchmarks_Inputs/ITER_Cyl_SDDR/openmc/geometry.xml +++ /dev/null @@ -1,219 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/jade/install_files/Benchmarks_Inputs/ITER_Cyl_SDDR/openmc/materials.xml b/jade/install_files/Benchmarks_Inputs/ITER_Cyl_SDDR/openmc/materials.xml deleted file mode 100644 index 96d9f9f9..00000000 --- a/jade/install_files/Benchmarks_Inputs/ITER_Cyl_SDDR/openmc/materials.xml +++ /dev/null @@ -1,158 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/jade/install_files/Benchmarks_Inputs/ITER_Cyl_SDDR/serpent/ITER_Cyl_SDDR.i b/jade/install_files/Benchmarks_Inputs/ITER_Cyl_SDDR/serpent/ITER_Cyl_SDDR.i deleted file mode 100644 index aad839d1..00000000 --- a/jade/install_files/Benchmarks_Inputs/ITER_Cyl_SDDR/serpent/ITER_Cyl_SDDR.i +++ /dev/null @@ -1,374 +0,0 @@ -% --- surface definitions --- % -surf 1 cylz 0.0 0.0 7.5 -surf 2 cylz 0.0 0.0 48.0 -surf 3 cylz 0.0 0.0 50.0 -surf 4 cylz 0.0 0.0 100.0 -surf 101 cylz 0.0 0.0 12.0 -surf 102 cylz 0.0 0.0 42.0 -surf 103 cylz 0.0 0.0 56.0 -surf 104 cylz 0.0 0.0 94.0 -surf 10 cylz 0.0 0.0 15.0 -surf 11 cylz 0.0 0.0 30.0 -surf 12 cylz 0.0 0.0 45.0 -surf 13 cylz 0.0 0.0 60.0 -surf 20 pz -110.0 -surf 21 pz -100.0 -surf 22 pz 0.0 -surf 23 pz 210.0 -surf 24 pz 535.0 -surf 25 pz 550.0 -surf 26 pz 580.0 -surf 27 pz 590.0 -surf 201 pz 20.0 -surf 202 pz 40.0 -surf 203 pz 60.0 -surf 204 pz 80.0 -surf 205 pz 100.0 -surf 206 pz 120.0 -surf 207 pz 140.0 -surf 208 pz 160.0 -surf 209 pz 180.0 -surf 210 pz 200.0 -surf 211 pz 220.0 -surf 212 pz 240.0 -surf 213 pz 260.0 -surf 214 pz 280.0 -surf 215 pz 300.0 -surf 216 pz 320.0 -surf 217 pz 340.0 -surf 218 pz 360.0 -surf 219 pz 380.0 -surf 220 pz 400.0 -surf 221 pz 420.0 -surf 222 pz 440.0 -surf 223 pz 460.0 -surf 224 pz 480.0 -surf 225 pz 500.0 -surf 226 pz 520.0 -surf 227 pz 540.0 -surf 228 pz 545.0 -surf 229 pz 547.0 -surf 230 pz 549.0 -% --- cell definitions --- % -cell 1100 0 2 ( 3 -103 22 -201 ) -cell 1101 0 2 ( 3 -103 201 -202 ) -cell 1102 0 2 ( 3 -103 202 -203 ) -cell 1103 0 2 ( 3 -103 203 -204 ) -cell 1104 0 2 ( 3 -103 204 -205 ) -cell 1105 0 2 ( 3 -103 205 -206 ) -cell 1106 0 2 ( 3 -103 206 -207 ) -cell 1107 0 2 ( 3 -103 207 -208 ) -cell 1108 0 2 ( 3 -103 208 -209 ) -cell 1109 0 2 ( 3 -103 209 -210 ) -cell 1110 0 2 ( 3 -103 210 -211 ) -cell 1111 0 2 ( 3 -103 211 -212 ) -cell 1112 0 2 ( 3 -103 212 -213 ) -cell 1113 0 2 ( 3 -103 213 -214 ) -cell 1114 0 2 ( 3 -103 214 -215 ) -cell 1115 0 2 ( 3 -103 215 -216 ) -cell 1116 0 2 ( 3 -103 216 -217 ) -cell 1117 0 2 ( 3 -103 217 -218 ) -cell 1118 0 2 ( 3 -103 218 -219 ) -cell 1119 0 2 ( 3 -103 219 -220 ) -cell 1120 0 2 ( 3 -103 220 -221 ) -cell 1121 0 2 ( 3 -103 221 -222 ) -cell 1122 0 2 ( 3 -103 222 -223 ) -cell 1123 0 2 ( 3 -103 223 -224 ) -cell 1124 0 2 ( 3 -103 224 -225 ) -cell 1125 0 2 ( 3 -103 225 -226 ) -cell 1126 0 2 ( 3 -103 226 -24 ) -cell 1127 0 2 ( 3 -103 24 -227 ) -cell 1128 0 2 ( 3 -103 227 -228 ) -cell 1129 0 2 ( 3 -103 228 -229 ) -cell 1130 0 2 ( 3 -103 229 -230 ) -cell 1131 0 2 ( 3 -103 230 -25 ) -cell 1200 0 2 ( 103 -104 22 -201 ) -cell 1201 0 2 ( 103 -104 201 -202 ) -cell 1202 0 2 ( 103 -104 202 -203 ) -cell 1203 0 2 ( 103 -104 203 -204 ) -cell 1204 0 2 ( 103 -104 204 -205 ) -cell 1205 0 2 ( 103 -104 205 -206 ) -cell 1206 0 2 ( 103 -104 206 -207 ) -cell 1207 0 2 ( 103 -104 207 -208 ) -cell 1208 0 2 ( 103 -104 208 -209 ) -cell 1209 0 2 ( 103 -104 209 -210 ) -cell 1210 0 2 ( 103 -104 210 -211 ) -cell 1211 0 2 ( 103 -104 211 -212 ) -cell 1212 0 2 ( 103 -104 212 -213 ) -cell 1213 0 2 ( 103 -104 213 -214 ) -cell 1214 0 2 ( 103 -104 214 -215 ) -cell 1215 0 2 ( 103 -104 215 -216 ) -cell 1216 0 2 ( 103 -104 216 -217 ) -cell 1217 0 2 ( 103 -104 217 -218 ) -cell 1218 0 2 ( 103 -104 218 -219 ) -cell 1219 0 2 ( 103 -104 219 -220 ) -cell 1220 0 2 ( 103 -104 220 -221 ) -cell 1221 0 2 ( 103 -104 221 -222 ) -cell 1222 0 2 ( 103 -104 222 -223 ) -cell 1223 0 2 ( 103 -104 223 -224 ) -cell 1224 0 2 ( 103 -104 224 -225 ) -cell 1225 0 2 ( 103 -104 225 -226 ) -cell 1226 0 2 ( 103 -104 226 -24 ) -cell 1227 0 2 ( 103 -104 24 -227 ) -cell 1228 0 2 ( 103 -104 227 -228 ) -cell 1229 0 2 ( 103 -104 228 -229 ) -cell 1230 0 2 ( 103 -104 229 -230 ) -cell 1231 0 2 ( 103 -104 230 -25 ) -cell 1300 0 2 ( 104 -4 22 -201 ) -cell 1301 0 2 ( 104 -4 201 -202 ) -cell 1302 0 2 ( 104 -4 202 -203 ) -cell 1303 0 2 ( 104 -4 203 -204 ) -cell 1304 0 2 ( 104 -4 204 -205 ) -cell 1305 0 2 ( 104 -4 205 -206 ) -cell 1306 0 2 ( 104 -4 206 -207 ) -cell 1307 0 2 ( 104 -4 207 -208 ) -cell 1308 0 2 ( 104 -4 208 -209 ) -cell 1309 0 2 ( 104 -4 209 -210 ) -cell 1310 0 2 ( 104 -4 210 -211 ) -cell 1311 0 2 ( 104 -4 211 -212 ) -cell 1312 0 2 ( 104 -4 212 -213 ) -cell 1313 0 2 ( 104 -4 213 -214 ) -cell 1314 0 2 ( 104 -4 214 -215 ) -cell 1315 0 2 ( 104 -4 215 -216 ) -cell 1316 0 2 ( 104 -4 216 -217 ) -cell 1317 0 2 ( 104 -4 217 -218 ) -cell 1318 0 2 ( 104 -4 218 -219 ) -cell 1319 0 2 ( 104 -4 219 -220 ) -cell 1320 0 2 ( 104 -4 220 -221 ) -cell 1321 0 2 ( 104 -4 221 -222 ) -cell 1322 0 2 ( 104 -4 222 -223 ) -cell 1323 0 2 ( 104 -4 223 -224 ) -cell 1324 0 2 ( 104 -4 224 -225 ) -cell 1325 0 2 ( 104 -4 225 -226 ) -cell 1326 0 2 ( 104 -4 226 -24 ) -cell 1327 0 2 ( 104 -4 24 -227 ) -cell 1328 0 2 ( 104 -4 227 -228 ) -cell 1329 0 2 ( 104 -4 228 -229 ) -cell 1330 0 2 ( 104 -4 229 -230 ) -cell 1331 0 2 ( 104 -4 230 -25 ) -cell 2100 0 1 ( 1 -101 22 -201 ) -cell 2101 0 1 ( 1 -101 201 -202 ) -cell 2102 0 1 ( 1 -101 202 -203 ) -cell 2103 0 1 ( 1 -101 203 -204 ) -cell 2104 0 1 ( 1 -101 204 -205 ) -cell 2105 0 1 ( 1 -101 205 -206 ) -cell 2106 0 1 ( 1 -101 206 -207 ) -cell 2107 0 1 ( 1 -101 207 -208 ) -cell 2108 0 1 ( 1 -101 208 -209 ) -cell 2109 0 1 ( 1 -101 209 -210 ) -cell 2110 0 1 ( 1 -101 210 -23 ) -cell 2200 0 1 ( 101 -102 22 -201 ) -cell 2201 0 1 ( 101 -102 201 -202 ) -cell 2202 0 1 ( 101 -102 202 -203 ) -cell 2203 0 1 ( 101 -102 203 -204 ) -cell 2204 0 1 ( 101 -102 204 -205 ) -cell 2205 0 1 ( 101 -102 205 -206 ) -cell 2206 0 1 ( 101 -102 206 -207 ) -cell 2207 0 1 ( 101 -102 207 -208 ) -cell 2208 0 1 ( 101 -102 208 -209 ) -cell 2209 0 1 ( 101 -102 209 -210 ) -cell 2210 0 1 ( 101 -102 210 -23 ) -cell 2300 0 1 ( 102 -2 22 -201 ) -cell 2301 0 1 ( 102 -2 201 -202 ) -cell 2302 0 1 ( 102 -2 202 -203 ) -cell 2303 0 1 ( 102 -2 203 -204 ) -cell 2304 0 1 ( 102 -2 204 -205 ) -cell 2305 0 1 ( 102 -2 205 -206 ) -cell 2306 0 1 ( 102 -2 206 -207 ) -cell 2307 0 1 ( 102 -2 207 -208 ) -cell 2308 0 1 ( 102 -2 208 -209 ) -cell 2309 0 1 ( 102 -2 209 -210 ) -cell 2310 0 1 ( 102 -2 210 -23 ) -cell 3100 0 2 ( -1 24 -227 ) -cell 3101 0 2 ( -1 227 -228 ) -cell 3102 0 2 ( -1 228 -229 ) -cell 3103 0 2 ( -1 229 -230 ) -cell 3104 0 2 ( -1 230 -25 ) -cell 3200 0 2 ( 1 -10 24 -227 ) -cell 3201 0 2 ( 1 -10 227 -228 ) -cell 3202 0 2 ( 1 -10 228 -229 ) -cell 3203 0 2 ( 1 -10 229 -230 ) -cell 3204 0 2 ( 1 -10 230 -25 ) -cell 3300 0 2 ( 10 -11 24 -227 ) -cell 3301 0 2 ( 10 -11 227 -228 ) -cell 3302 0 2 ( 10 -11 228 -229 ) -cell 3303 0 2 ( 10 -11 229 -230 ) -cell 3304 0 2 ( 10 -11 230 -25 ) -cell 3400 0 2 ( 11 -12 24 -227 ) -cell 3401 0 2 ( 11 -12 227 -228 ) -cell 3402 0 2 ( 11 -12 228 -229 ) -cell 3403 0 2 ( 11 -12 229 -230 ) -cell 3404 0 2 ( 11 -12 230 -25 ) -cell 3500 0 2 ( 12 -2 24 -227 ) -cell 3501 0 2 ( 12 -2 227 -228 ) -cell 3502 0 2 ( 12 -2 228 -229 ) -cell 3503 0 2 ( 12 -2 229 -230 ) -cell 3504 0 2 ( 12 -2 230 -25 ) -cell 3600 0 2 ( 2 -3 24 -25 ) -cell 10 0 void ( 26 -27 -10 ) -cell 11 0 void ( 26 -27 10 -11 ) -cell 12 0 void ( 26 -27 11 -12 ) -cell 13 0 void ( 26 -27 12 -13 ) -cell 14 0 void ( 26 -27 13 -4 ) -cell 15 0 void ( -4 21 -22 ) -cell 16 0 void ( -1 22 -23 ) -cell 17 0 void ( -3 23 -24 ) -cell 18 0 void ( -4 25 -26 ) -cell 19 0 void ( 2 -3 22 -23 ) -cell 50 0 void ( -4 20 -21 ) -cell 100 0 void ( 4: -20: 27 ) -% --- material definitions --- % -% M1 -mat 1 -6.536 rgb 0 208 31 -1001 1.457907e-01 -1002 1.676788e-05 -5010 7.989261e-06 -5011 3.215778e-05 -6012 8.129276e-04 -7014 2.159162e-03 -7015 7.975079e-05 -8016 7.280396e-02 -13027 8.029408e-04 -14028 7.119967e-03 -14029 3.615341e-04 -14030 2.383266e-04 -15031 3.495389e-04 -22046 1.120522e-04 -22047 1.010507e-04 -22048 1.001269e-03 -22049 7.347907e-05 -22050 7.035519e-05 -24050 6.335342e-03 -24052 1.221708e-01 -24053 1.385322e-02 -24054 3.448351e-03 -25055 1.418129e-02 -26054 2.936167e-02 -26056 4.609150e-01 -26057 1.064456e-02 -26058 1.416591e-03 -27059 3.675152e-04 -28058 6.159644e-02 -28060 2.372680e-02 -28061 1.031389e-03 -28062 3.288522e-03 -28064 8.374892e-04 -29063 1.416122e-03 -29065 6.311824e-04 -41093 4.663848e-05 -42092 1.674711e-03 -42094 1.043873e-03 -42095 1.796590e-03 -42096 1.882357e-03 -42097 1.077728e-03 -42098 2.723098e-03 -42100 1.086756e-03 -73181 2.396838e-05 -74182 6.245761e-07 -74183 3.372711e-07 -74184 7.221513e-07 -74186 6.700641e-07 -82206 4.043466e-07 -82207 3.707909e-07 -82208 8.791602e-07 -83209 1.657813e-06 -16032 9.676215e-05 -16033 7.639922e-07 -16034 4.329289e-06 -16036 1.018656e-08 -19039 5.169006e-06 -19040 6.484945e-10 -19041 3.730340e-07 -23050 3.395521e-06 -23051 1.354813e-03 -40090 4.891587e-06 -40091 1.066737e-06 -40092 1.630529e-06 -40094 1.652396e-06 -40096 2.662088e-07 -50112 7.081358e-08 -50114 4.818244e-08 -50115 2.482126e-08 -50116 1.061474e-06 -50117 5.606684e-07 -50118 1.768149e-06 -50119 6.271017e-07 -50120 2.378460e-06 -50122 3.380071e-07 -50124 4.226914e-07 -% M2 -mat 2 -7.93 rgb 0 0 255 -5010 1.022623e-05 -5011 4.116186e-05 -6012 1.039759e-03 -7014 2.769128e-03 -7015 1.022803e-04 -8016 6.948389e-05 -13027 1.029761e-03 -14028 9.119405e-03 -14029 4.630606e-04 -14030 3.052532e-04 -15031 4.478961e-04 -22046 1.435167e-04 -22047 1.294260e-04 -22048 1.282433e-03 -22049 9.411218e-05 -22050 9.011110e-05 -24050 8.123266e-03 -24052 1.566487e-01 -24053 1.776278e-02 -24054 4.421525e-03 -25055 1.819578e-02 -26054 3.763307e-02 -26056 5.907590e-01 -26057 1.364324e-02 -26058 1.815659e-03 -27059 4.708908e-04 -28058 7.895089e-02 -28060 3.041175e-02 -28061 1.321973e-03 -28062 4.215043e-03 -28064 1.073451e-03 -29063 1.811830e-03 -29065 8.075587e-04 -41093 5.978614e-05 -42092 2.151301e-03 -42094 1.340939e-03 -42095 2.307865e-03 -42096 2.418039e-03 -42097 1.384429e-03 -42098 3.498039e-03 -42100 1.396026e-03 -73181 3.069288e-05 -74182 8.001145e-07 -74183 4.320618e-07 -74184 9.251135e-07 -74186 8.583870e-07 -82206 5.156204e-07 -82207 4.728304e-07 -82208 1.121100e-06 -83209 2.129506e-06 -16032 1.234584e-04 -16033 9.747740e-07 -16034 5.523719e-06 -16036 1.299699e-08 -19039 6.619790e-06 -19040 8.305074e-10 -19041 4.777334e-07 -23050 1.089747e-07 -23051 4.348092e-05 -40090 6.275445e-06 -40091 1.368523e-06 -40092 2.091815e-06 -40094 2.119868e-06 -40096 3.415208e-07 -50112 9.077095e-08 -50114 6.176168e-08 -50115 3.181662e-08 -50116 1.360628e-06 -50117 7.186814e-07 -50118 2.266466e-06 -50119 8.038376e-07 -50120 3.048781e-06 -50122 4.332675e-07 -50124 5.418184e-07 From bce70719c0823f5d555ee2ca2f4d823f9aac808b Mon Sep 17 00:00:00 2001 From: alexvalentine94 Date: Sat, 10 Feb 2024 12:07:10 +0000 Subject: [PATCH 3/9] spheresddr running --- .../SphereSDDR/{mcnp => d1s}/SphereSDDR.i | 0 .../SphereSDDR/openmc/geometry.xml | 10 -- .../SphereSDDR/serpent/SphereSDDR.i | 10 -- jade/testrun.py | 139 ++++++++++-------- 4 files changed, 75 insertions(+), 84 deletions(-) rename jade/install_files/Benchmarks_Inputs/SphereSDDR/{mcnp => d1s}/SphereSDDR.i (100%) delete mode 100644 jade/install_files/Benchmarks_Inputs/SphereSDDR/openmc/geometry.xml delete mode 100644 jade/install_files/Benchmarks_Inputs/SphereSDDR/serpent/SphereSDDR.i diff --git a/jade/install_files/Benchmarks_Inputs/SphereSDDR/mcnp/SphereSDDR.i b/jade/install_files/Benchmarks_Inputs/SphereSDDR/d1s/SphereSDDR.i similarity index 100% rename from jade/install_files/Benchmarks_Inputs/SphereSDDR/mcnp/SphereSDDR.i rename to jade/install_files/Benchmarks_Inputs/SphereSDDR/d1s/SphereSDDR.i diff --git a/jade/install_files/Benchmarks_Inputs/SphereSDDR/openmc/geometry.xml b/jade/install_files/Benchmarks_Inputs/SphereSDDR/openmc/geometry.xml deleted file mode 100644 index d795ebde..00000000 --- a/jade/install_files/Benchmarks_Inputs/SphereSDDR/openmc/geometry.xml +++ /dev/null @@ -1,10 +0,0 @@ - - - - - - - - - - diff --git a/jade/install_files/Benchmarks_Inputs/SphereSDDR/serpent/SphereSDDR.i b/jade/install_files/Benchmarks_Inputs/SphereSDDR/serpent/SphereSDDR.i deleted file mode 100644 index f4e588e5..00000000 --- a/jade/install_files/Benchmarks_Inputs/SphereSDDR/serpent/SphereSDDR.i +++ /dev/null @@ -1,10 +0,0 @@ -% --- surface definitions --- % -surf 1 sph 0.0 0.0 0.0 5.0 -surf 2 sph 0.0 0.0 0.0 50.0 -surf 3 sph 0.0 0.0 0.0 55.0 -surf 4 sph 0.0 0.0 0.0 60.0 -% --- cell definitions --- % -cell 1 0 void ( -1 ) -cell 2 0 1 ( 1 -2 ) -cell 3 0 void ( 2 -3 ) -cell 4 0 void ( 3 ) diff --git a/jade/testrun.py b/jade/testrun.py index 1b266dc8..ee533de9 100644 --- a/jade/testrun.py +++ b/jade/testrun.py @@ -103,9 +103,6 @@ def __init__(self, inp, lib, config, log, confpath, runoption): # Path variables self.run_dir = None - # self.serpent_dir = None - # self.openmc_dir = None - # self.d1s_dir = None config = config.dropna() @@ -758,7 +755,10 @@ def generate_test(self, directory, libmanager, limit=None, lib=None): zaids = libmanager.get_libzaids(lib, "mcnp") # testname = self.inp.name - testname = "Sphere" + if self.d1s: + testname = "SphereSDDR" + else: + testname = "Sphere" motherdir = os.path.join(directory, testname) # If previous results are present they are canceled @@ -801,42 +801,12 @@ def generate_test(self, directory, libmanager, limit=None, lib=None): else: nps = self.nps - # if self.ctme is None: - # ctme = settings.loc[Z, 'CTME cut-off'] - # if ctme is np.nan: - # ctme = None - # else: - # ctme = self.ctme - # - # if self.precision is None: - # prec = settings.loc[Z, 'Relative Error cut-off'] - # if prec is np.nan: - # precision = None - # else: - # tally = prec.split('-')[0] - # error = prec.split('-')[1] - # precision = (tally, error) - # else: - # precision = self.precision - # Zaid local settings are prioritized else: nps = settings.loc[Z, "NPS cut-off"] if nps is np.nan: nps = None - # ctme = settings.loc[Z, 'CTME cut-off'] - # if ctme is np.nan: - # ctme = None - # - # prec = settings.loc[Z, 'Relative Error cut-off'] - # if prec is np.nan: - # precision = None - # else: - # tally = prec.split('-')[0] - # error = prec.split('-')[1] - # precision = (tally, error) - self.generate_zaid_test( zaid, libmanager, testname, motherdir, -1 * density, nps ) @@ -851,9 +821,6 @@ def generate_test(self, directory, libmanager, limit=None, lib=None): material, -1 * density, libmanager, testname, motherdir ) - # def generate_zaid_test(self, zaid, libmanager, testname, motherdir, - # density, nps, ctme, precision, addtag=None, - # parentlist=None, lib=None): def generate_zaid_test( self, zaid, @@ -883,10 +850,6 @@ def generate_zaid_test( Density value for the sphere. nps : float number of particles cut-off - ctme : float - computer time cut-off - precision : float - precision cut-off addtag : str, optional add tag at the end of the single zaid test name. The default is None @@ -904,15 +867,10 @@ def generate_zaid_test( # Adjourn the material cards for the zaid zaid = mat.Zaid(1, zaid[:-3], zaid[-3:], lib) name, formula = libmanager.get_zaidname(zaid) - if self.d1s: - # Add d1s function here - pass - - if self.mcnp: # Retrieve wwinp & other misc files if they exist directoryVRT = os.path.join( - self.path_VRT, "mcnp", zaid.element + zaid.isotope + self.path_VRT, "d1s", zaid.element + zaid.isotope ) edits_file = os.path.join(directoryVRT, "inp_edits.txt") ww_file = os.path.join(directoryVRT, "wwinp") @@ -922,19 +880,49 @@ def generate_zaid_test( matlist = mat.MatCardsList([material]) # Generate the new input - newinp = deepcopy(self.mcnp_inp) + newinp = deepcopy(self.d1s_inp) newinp.matlist = matlist # Assign material # adjourn density newinp.change_density(density) # assign stop card - newinp.add_stopCard(nps) # , ctme, precision) + newinp.add_stopCard(nps) # add PIKMT if requested if parentlist is not None: newinp.add_PIKMT_card(parentlist) - # if os.path.exists(directoryVRT): - # newinp.add_edits(edits_file) # Add variance reduction + # Write new input file + outfile, outdir = self._get_zaidtestname( + testname, zaid, formula, addtag=addtag + ) + outpath = os.path.join(motherdir, "d1s", outdir) + os.mkdir(outpath) + outinpfile = os.path.join(outpath, outfile) + newinp.write(outinpfile) + # Copy also wwinp file + if os.path.exists(directoryVRT): + outwwfile = os.path.join(outpath, "wwinp") + shutil.copyfile(ww_file, outwwfile) + + if self.mcnp: + # Retrieve wwinp & other misc files if they exist + directoryVRT = os.path.join( + self.path_VRT, "mcnp", zaid.element + zaid.isotope + ) + edits_file = os.path.join(directoryVRT, "inp_edits.txt") + ww_file = os.path.join(directoryVRT, "wwinp") + # Create MCNP material card + submat = mat.SubMaterial("M1", [zaid], header="C " + name + " " + formula) + material = mat.Material([zaid], None, "M1", submaterials=[submat]) + matlist = mat.MatCardsList([material]) + + # Generate the new input + newinp = deepcopy(self.mcnp_inp) + newinp.matlist = matlist # Assign material + # adjourn density + newinp.change_density(density) + # assign stop card + newinp.add_stopCard(nps) # Write new input file outfile, outdir = self._get_zaidtestname( testname, zaid, formula, addtag=addtag @@ -1048,7 +1036,40 @@ def generate_material_test( truename = material.name if self.d1s: - # Add d1s function here + # Retrieve wwinp & other misc files if they exist + directoryVRT = os.path.join(self.path_VRT, "d1s", truename) + edits_file = os.path.join(directoryVRT, "inp_edits.txt") + ww_file = os.path.join(directoryVRT, "wwinp") + newmat = deepcopy(material) + # Translate and assign the material + newmat.translate(lib, libmanager, "mcnp") + newmat.header = material.header + "C\nC True name:" + truename + newmat.name = "M1" + matlist = mat.MatCardsList([newmat]) + + # Generate the new input + newinp = deepcopy(self.d1s_inp) + newinp.matlist = matlist # Assign material + # adjourn density + newinp.change_density(density) + # add stop card + newinp.add_stopCard(self.nps) + # Add PIKMT card if required + if parentlist is not None: + newinp.add_PIKMT_card(parentlist) + + # Write new input file + outfile = testname + "_" + truename + "_" + outdir = testname + "_" + truename + outpath = os.path.join(motherdir, "d1s", outdir) + os.mkdir(outpath) + outinpfile = os.path.join(outpath, outfile) + newinp.write(outinpfile) + + # Copy also wwinp file + if os.path.exists(directoryVRT): + outwwfile = os.path.join(outpath, "wwinp") + shutil.copyfile(ww_file, outwwfile) pass if self.mcnp: @@ -1069,14 +1090,11 @@ def generate_material_test( # adjourn density newinp.change_density(density) # add stop card - newinp.add_stopCard(self.nps) # , self.ctme, self.precision) + newinp.add_stopCard(self.nps) # Add PIKMT card if required if parentlist is not None: newinp.add_PIKMT_card(parentlist) - # if os.path.exists(directoryVRT): - # newinp.add_edits(edits_file) # Add variance reduction - # Write new input file outfile = testname + "_" + truename + "_" outdir = testname + "_" + truename @@ -1183,7 +1201,6 @@ def run(self, config, libmanager, runoption: str) -> None: ) -# Fix from here class SphereTestSDDR(SphereTest): def __init__(self, *args, **keyargs): super().__init__(*args, **keyargs) @@ -1198,7 +1215,7 @@ def generate_test(self, directory, libmanager, limit=None, lib=None): ) def generate_zaid_test( - self, zaid, libmanager, testname, motherdir, density, nps, ctme, precision + self, zaid, libmanager, testname, motherdir, density, nps ): """ Generate input for a single zaid sphere SDDR benchmark run. @@ -1219,10 +1236,6 @@ def generate_zaid_test( Density value for the sphere. nps : float number of particles cut-off - ctme : float - computer time cut-off - precision : float - precision cut-off Returns ------- @@ -1244,8 +1257,6 @@ def generate_zaid_test( motherdir, density, nps, - ctme, - precision, addtag=MT, parentlist=[zaid], lib=self.activationlib, From da43f595ffdd08fcb7e9caaf86fa0580bb6f4441 Mon Sep 17 00:00:00 2001 From: alexvalentine94 Date: Sun, 11 Feb 2024 12:02:34 +0000 Subject: [PATCH 4/9] d1s related updates --- jade/computational.py | 11 ++-- jade/testrun.py | 114 +++++++++++++++++++++++++++++++++++++++++- 2 files changed, 120 insertions(+), 5 deletions(-) diff --git a/jade/computational.py b/jade/computational.py index f0856795..5e254bbd 100644 --- a/jade/computational.py +++ b/jade/computational.py @@ -87,9 +87,14 @@ def executeBenchmarksRoutines(session, lib: str, runoption, exp=False) -> None: bool(row["d1S"]), ]) ): - print("Transport code was not specified or is not available for input generation, defaulting to MCNP") - print("") - row["MCNP"]=True + if ( + testname in ["SphereSDDR", "FNG", "ITER_Cyl_SDDR"] + ): + row["d1S"] = True + else: + print("Transport code was not specified or is not available for input generation, defaulting to MCNP") + print("") + row["MCNP"] = True print(" -- " + testname.upper() + " STARTED --\n") print(" Generating input files:" + " " + str(datetime.datetime.now())) diff --git a/jade/testrun.py b/jade/testrun.py index ee533de9..d8faaa43 100644 --- a/jade/testrun.py +++ b/jade/testrun.py @@ -405,8 +405,118 @@ def job_submission( config.batch_system + " " + job_script, cwd=directory, shell=True ) - def run_d1s(self, config, libmanager, name, directory): - pass + def run_d1s( + self, + config, + lib_manager, + name: str, + directory: Path, + runoption: str, + timeout=None, + ) -> bool: + """Run D1S simulation either on the command line or submitted as a job. + + Parameters + ---------- + config : + Configuration settings + lib_manager : + libmanager + name : str + Name of the simulation + directory : str, path + Directory where the simulation will be executed + runoption: str + Whether JADE run in parallel or command line + timeout : float, optional + Maximum time to wait for simulation of complete, by default None + + Returns + ------- + bool + Flag if simulation not run + """ + + # Calculate MPI tasks and OpenMP threads + mpi_tasks = int(config.openmp_threads) * int(config.mpi_tasks) + omp_threads = 1 + run_mpi = False + if mpi_tasks > 1: + run_mpi = True + + executable = config.d1s_exec + env_variables = config.d1s_config + inputstring = "i=" + name + outputstring = "n=" + name + + #TODO change for D1S + if isinstance(self.lib, dict): + lib = list(self.lib.values())[0] + elif isinstance(self.lib, str): + lib = self.lib + + xsstring = "xs=" + str(lib_manager.data["mcnp"][lib].filename) + + if run_mpi: + run_command = [ + "mpirun", + "-n", + str(mpi_tasks), + executable, + inputstring, + outputstring, + xsstring, + ] + else: + run_command = [executable, inputstring, outputstring, xsstring] + + flagnotrun = False + + try: + cwd = os.getcwd() + os.chdir(directory) + # cancel eventual previous output file + outputfile = name + ".o" + if os.path.exists(outputfile): + os.remove(outputfile) + + # check if runtpe exists + runtpe = name + ".r" + if os.path.exists(runtpe): + command = command + " runtpe=" + name + ".r" + + if runoption.lower() == "c": + try: + if not sys.platform.startswith("win"): + unix.configure(env_variables) + print(" ".join(run_command)) + subprocess.run( + " ".join(run_command), cwd=directory, shell=True, timeout=43200 + ) + + except subprocess.TimeoutExpired: + print( + "Sesion timed out after 12 hours. Consider submitting as a job." + ) + flagnotrun = True + + elif runoption.lower() == "s": + # Run MCNP as a job + cwd = os.getcwd() + os.chdir(directory) + self.job_submission( + config, + directory, + run_command, + mpi_tasks, + omp_threads, + env_variables, + ) + os.chdir(cwd) + except subprocess.TimeoutExpired: + pass + + return flagnotrun def run_mcnp( self, From da4d960d677fd7b8bca5709b0b83685dfe58a19b Mon Sep 17 00:00:00 2001 From: alexvalentine94 Date: Mon, 12 Feb 2024 10:12:45 +0000 Subject: [PATCH 5/9] fngsddr post p --- jade/expoutput.py | 57 ++++++++++++++++++++++++++++++++++++++--------- 1 file changed, 46 insertions(+), 11 deletions(-) diff --git a/jade/expoutput.py b/jade/expoutput.py index bf14f6cf..d594e7be 100644 --- a/jade/expoutput.py +++ b/jade/expoutput.py @@ -81,7 +81,15 @@ def __init__(self, *args, **kwargs): self.path_exp_res = os.path.join(session.path_exp_res, testname) # Add the raw path data (not created because it is a comparison) - out = os.path.dirname(self.atlas_path_mcnp) + if self.mcnp: + out = os.path.dirname(self.atlas_path_mcnp) + elif self.d1s: + out = os.path.dirname(self.atlas_path_d1s) + elif self.serpent: + out = os.path.dirname(self.atlas_path_serpent) + elif self.openmc: + out = os.path.dirname(self.atlas_path_openmc) + raw_path = os.path.join(out, 'Raw_Data') if not os.path.exists(raw_path): os.mkdir(raw_path) @@ -147,7 +155,15 @@ def build_atlas(self): None. """ # Build a temporary folder for images - tmp_path = os.path.join(self.atlas_path_mcnp, 'tmp') + if self.mcnp: + tmp_path = os.path.join(self.atlas_path_mcnp, 'tmp') + elif self.d1s: + tmp_path = os.path.join(self.atlas_path_d1s, 'tmp') + elif self.openmc: + tmp_path = os.path.join(self.atlas_path_openmc, 'tmp') + elif self.serpent: + tmp_path = os.path.join(self.atlas_path_serpent, 'tmp') + os.mkdir(tmp_path) globalname = '' @@ -165,7 +181,10 @@ def build_atlas(self): # Save Atlas print(' Producing the PDF...') - atlas.save(self.atlas_path_mcnp) + if self.mcnp: + atlas.save(self.atlas_path_mcnp) + elif self.d1s: + atlas.save(self.atlas_path_d1s) # Remove tmp images shutil.rmtree(tmp_path) @@ -205,19 +224,34 @@ def _extract_outputs(self): if input not in inputs: inputs.append(input) if self.openmc: - print("Experimental comparison not impletmented \ + print("Experimental comparison not implemented \ for OpenMC") break if self.serpent: - print("Experimental comparison not impletmented \ + print("Experimental comparison not implemented \ for Serpent") break if self.d1s: - print("Experimental comparison not impletmented \ - for D1S") - break + results_path = os.path.join(test_path, + folder, "d1s") + pieces = folder.split('_') + # Get zaid + input = pieces[-1] + mfile, ofile = self._get_output_files(results_path) + # Parse output + output = MCNPoutput(mfile, ofile) + outputs[input, lib] = output + code_raw_data[input, lib] = output.tallydata + # self.raw_data[input, lib] = output.tallydata + + # Get the meaningful results + results[input, lib] = self._processMCNPdata(output) + if input not in inputs: + inputs.append(input) if self.mcnp: self.raw_data["mcnp"] = code_raw_data + if self.d1s: + self.raw_data["d1s"] = code_raw_data # Results are organized just by lib else: mfile, ofile = self._get_output_files(test_path) @@ -307,7 +341,8 @@ def _print_raw(self): if self.serpent: pass if self.d1s: - pass + raw_to_print = self.raw_data['d1s'].items() + for (folder, lib), item in raw_to_print: # Create the lib directory if it is not there cd_lib = os.path.join(self.raw_path, lib) @@ -451,7 +486,7 @@ def _pp_excel_comparison(self): ''' # Dump the global C/E table print(' Dump the C/E table in Excel...') - ex_outpath = os.path.join(self.excel_path_mcnp, 'C over E table.xlsx') + ex_outpath = os.path.join(self.excel_path_d1s, self.testname + '_CE_tables.xlsx') # Create a Pandas Excel writer using XlsxWriter as the engine. with pd.ExcelWriter(ex_outpath, engine='xlsxwriter') as writer: # --- build and dump the C/E table --- @@ -568,7 +603,7 @@ def _build_atlas(self, tmp_path, atlas): # There is the need to recover the tracked parents and daughters zaid_tracked = {} for lib in self.lib[1:]: - file = os.path.join(self.test_path[lib], folder, folder) + file = os.path.join(self.test_path[lib], folder, "d1s", folder) inp = D1S_Input.from_text(file) for tallynum in ['24', '14']: card = inp.get_card_byID('settings', 'FU' + tallynum) From 19a791bcda156e6f1e92b34df37923c001d7d63e Mon Sep 17 00:00:00 2001 From: Jez Swann Date: Mon, 12 Feb 2024 16:37:37 +0000 Subject: [PATCH 6/9] Corrected logging string typo --- jade/configuration.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/jade/configuration.py b/jade/configuration.py index c02eb4e8..3f674ab9 100644 --- a/jade/configuration.py +++ b/jade/configuration.py @@ -69,7 +69,7 @@ def _process_path(self, file_path: str) -> str: if not os.path.exists(file_path): # If to terminate or not the session is lef to the user # other codes path may be present but not used - logging.warning("Path {file_path} do not exist") + logging.warning(f"Path {file_path} do not exist") # fatal_exception(file_path + ' does not exist') return file_path From 9f9b9aa76a80488dd74a8965bbe9e3d4310117ee Mon Sep 17 00:00:00 2001 From: "Wheeler, Dylan" Date: Mon, 12 Feb 2024 16:53:09 +0000 Subject: [PATCH 7/9] general computational comparison post-processing restored --- jade/excel_support.py | 588 ----------------------------------- jade/excelsupport.py | 560 ++++++++++++++++++++++++++++++++- jade/output.py | 525 +++++++++++++++---------------- jade/sphereoutput.py | 27 +- jade/xsdirpyne.py | 37 ++- templates/AtlasTemplate.docx | Bin 162063 -> 0 bytes 6 files changed, 836 insertions(+), 901 deletions(-) delete mode 100644 jade/excel_support.py delete mode 100644 templates/AtlasTemplate.docx diff --git a/jade/excel_support.py b/jade/excel_support.py deleted file mode 100644 index 65b3f744..00000000 --- a/jade/excel_support.py +++ /dev/null @@ -1,588 +0,0 @@ -# -*- coding: utf-8 -*- -""" -Created on Thu Jan 2 12:05:36 2020 - -@author: Davide Laghi - -Copyright 2021, the JADE Development Team. All rights reserved. - -This file is part of JADE. - -JADE is free software: you can redistribute it and/or modify -it under the terms of the GNU General Public License as published by -the Free Software Foundation, either version 3 of the License, or -(at your option) any later version. - -JADE is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with JADE. If not, see . -""" -# from copy import copy -# import openpyxl -import pandas as pd - - -# def createNewWorkbook(manyWb, theOne): -# for wb in manyWb: -# for sheetName in wb.sheetnames: -# o = theOne.create_sheet(sheetName) -# safeTitle = o.title -# copySheet(wb[sheetName], theOne[safeTitle]) - - -# def copySheet(sourceSheet, newSheet): -# for row in sourceSheet.rows: -# for cell in row: -# newCell = newSheet.cell(row=cell.row, column=cell.col_idx, -# value=cell.value) -# if cell.has_style: -# newCell.font = copy(cell.font) -# newCell.border = copy(cell.border) -# newCell.fill = copy(cell.fill) -# newCell.number_format = copy(cell.number_format) -# newCell.protection = copy(cell.protection) -# newCell.alignment = copy(cell.alignment) - - -# def merge_sheets(filesInput, outfile): -# myfriends = [openpyxl.load_workbook(f, read_only=False, -# keep_vba=True) for f in filesInput] -# # theOne = openpyxl.Workbook() -# # del theOne['Sheet'] # We want our new book to be empty. Thanks. -# createNewWorkbook(myfriends, outfile) -# for excel in myfriends: -# excel.close() - -# # outfile.save(outfile) - - -def insert_df(startrow, startcolumn, df, ws, header=True): - """ - Insert a DataFrame (df) into a Worksheet (ws) using xlwings. - (startrow) and (startcolumn) identify the starting data entry - """ - columns = list(df.columns) - values = df.values - if header: - for i, column in enumerate(range(startcolumn, - startcolumn+len(columns))): - value = columns[i] - try: - ws.cell(column=column, row=startrow,value=value) - #ws.range((startrow, column)).value = value - except (AttributeError, ValueError) as e: - print(e) - print('Warning! header not printes: column,value', - column, value) - startrow = startrow+1 - - for i, row in enumerate(range(startrow, startrow+len(df))): - for j, column in enumerate(range(startcolumn, - startcolumn+len(df.columns))): - value = values[i][j] - try: - ws.cell(column=column,row=row,value=value) - #ws.range((row, column)).value = value - except (AttributeError, ValueError) as e: - print(e) - print('Warning! value not printed: row,column,value', row, - column, value) -def single_excel_writer(self, outpath, lib, tallies, stats=None): - """ - Produces single library summary excel file using XLSXwriter - - Parameters - ---------- - outpath : path or str - path to the output in Tests/Postprocessing. - lib : str - Shorthand representation of data library (i.e 00c, 31c). - tallies : Dataframe - Summary of tally tallies - errors: Dataframe - Errors on tally tallies - stats: Dataframe - Results of statistical checks - - Returns - ------- - None - """ - writer = pd.ExcelWriter(outpath, engine="xlsxwriter") - - #for df in (tallies, errors): - #df.set_index("Zaid", inplace=True) - - startrow = 8 - startcol = 1 - max_len = 0 - max_width = 0 - df_positions = [] - - - - for tally, results in tallies.items(): - #print(results) - tally_len, tally_width = results["Value"].shape - df_positions.append([startrow,startcol]) - #print(pd.Series(results["title"])) - #pd.Series(results["title"]).to_excel(writer, startrow=startrow, startcol=startcol+1, sheet_name="Values", index=False, header=False) - results["Value"].to_excel(writer, startrow=startrow+1, startcol=startcol, sheet_name="Values") - startrow = startrow + tally_len + 3 - max_len = max_len + tally_len - if tally_width > max_width: - max_width = tally_width - - wb = writer.book - tal_sheet = writer.sheets["Values"] - #errors.to_excel(writer, startrow=8, startcol=1, sheet_name="Errors") - #err_sheet = writer.sheets["Errors"] - - #tallies_len, tallies_width = tallies.shape - #errors_len, errors_width = errors.shape - - if stats is not None: - print(stats) - #stats.set_index("Zaid", inplace=True) - stats.to_excel( - writer, startrow=8, startcol=1, sheet_name="Statistical Checks" - ) - stat_sheet = writer.sheets["Statistical Checks"] - stats_len, stats_width = stats.shape - - # Formatting styles - plain_format = wb.add_format({"bg_color": "#FFFFFF"}) - oob_format = wb.add_format( - { - "align": "center", - "valign": "center", - "bg_color": "#D9D9D9", - "text_wrap": True, - } - ) - tally_format = wb.add_format({"bg_color": "#D9D9D9"}) - merge_format = wb.add_format( - {"align": "center", "valign": "center", "border": 2} - ) - title_merge_format = wb.add_format( - { - "font_size": "36", - "align": "center", - "valign": "center", - "bold": True, - "border": 2, - } - ) - subtitle_merge_format = wb.add_format( - { - "font_size": "16", - "align": "center", - "valign": "center", - "bold": True, - "border": 2, - } - ) - legend_text_format = wb.add_format({"align": "center", "bg_color": "white"}) - red_cell_format = wb.add_format({"bg_color": "red"}) - orange_cell_format = wb.add_format({"bg_color": "orange"}) - yellow_cell_format = wb.add_format({"bg_color": "yellow"}) - green_cell_format = wb.add_format({"bg_color": "#A6D86E"}) - value_allzero_format = wb.add_format({"bg_color": "#EDEDED"}) - value_belowzero_format = wb.add_format({"bg_color": "#FFC7CE"}) - value_abovezero_format = wb.add_format({"bg_color": "#C6EFCE"}) - - # tallies - - # Title Format - tal_sheet.merge_range("B1:C2", "LIBRARY", subtitle_merge_format) - tal_sheet.merge_range("D1:D2", lib, subtitle_merge_format) - tal_sheet.merge_range( - "B3:L8", "SPHERE LEAKAGE TEST RESULTS RECAP: TALLIES", title_merge_format - ) - for tal in range(len(df_positions)): - tal_sheet.merge_range(df_positions[tal][0], - df_positions[tal][1] + 1, - df_positions[tal][0], - df_positions[tal][1] + 4, - list(tallies.values())[tal]["title"], - subtitle_merge_format) - #tal_sheet.merge_range("B8:C8", "ZAID", subtitle_merge_format) - #tal_sheet.merge_range("D8:L8", "TALLY", subtitle_merge_format) - - # Freeze title - tal_sheet.freeze_panes(8, 2) - - # out of bounds - tal_sheet.set_column(0, 0, 4, oob_format) - tal_sheet.set_column(max_width + 2, 1000, 18, oob_format) - for i in range(9): - tal_sheet.set_row(i, None, oob_format) - for i in range(9 + max_len, 1000): - tal_sheet.set_row(i, None, oob_format) - - # Column widths for tallies, set up to 15th col to ensure title format correct - tal_sheet.set_column(1, 14, 20) - tal_sheet.set_column(1, max_width + 2, 20) - - # Row Heights - tal_sheet.set_row(7, 31) - #tal_sheet.set_row(8, 73.25) - - # Legend - tal_sheet.merge_range("N3:O3", "LEGEND", merge_format) - tal_sheet.merge_range("N8:O8", "According to MCNP manual", oob_format) - tal_sheet.write("N4", "", red_cell_format) - tal_sheet.write("O4", ">|5|%", legend_text_format) - tal_sheet.write("N5", "", orange_cell_format) - tal_sheet.write("O5", "|1|%≤|5|%", legend_text_format) - tal_sheet.write("N6", "", yellow_cell_format) - tal_sheet.write("O6", "|0.5|%≤|1|%", legend_text_format) - tal_sheet.write("N7", "", green_cell_format) - tal_sheet.write("O7", "<|0.5|%", legend_text_format) - - # Conditional Formatting - tal_sheet.conditional_format( - 10, - 1, - 8 + max_len, - max_width + 1, - {"type": "blanks", "format": oob_format}, - ) - tal_sheet.conditional_format( - 10, - 2, - 8 + max_len, - max_width + 1, - { - "type": "text", - "criteria": "containing", - "value": "Value = 0", - "format": value_allzero_format, - }, - ) - tal_sheet.conditional_format( - 10, - 2, - 8 + max_len, - max_width + 1, - { - "type": "text", - "criteria": "containing", - "value": "Value < 0", - "format": value_belowzero_format, - }, - ) - tal_sheet.conditional_format( - 10, - 2, - 8 + max_len, - max_width + 1, - { - "type": "text", - "criteria": "containing", - "value": "Value > 0", - "format": value_abovezero_format, - }, - ) - tal_sheet.conditional_format( - 10, - 2, - 8 + max_len, - max_width + 1, - { - "type": "cell", - "criteria": "greater than", - "value": 0.05, - "format": red_cell_format, - }, - ) - tal_sheet.conditional_format( - 10, - 2, - 8 + max_len, - max_width + 1, - { - "type": "cell", - "criteria": "between", - "minimum": 0.01, - "maximum": 0.05, - "format": orange_cell_format, - }, - ) - tal_sheet.conditional_format( - 10, - 2, - 8 + max_len, - max_width + 1, - { - "type": "cell", - "criteria": "between", - "minimum": 0.005, - "maximum": 0.01, - "format": yellow_cell_format, - }, - ) - tal_sheet.conditional_format( - 10, - 2, - 8 + max_len, - max_width + 1, - { - "type": "cell", - "criteria": "less than", - "value": -0.05, - "format": red_cell_format, - }, - ) - tal_sheet.conditional_format( - 10, - 2, - 8 + max_len, - max_width + 1, - { - "type": "cell", - "criteria": "between", - "minimum": -0.05, - "maximum": -0.01, - "format": orange_cell_format, - }, - ) - tal_sheet.conditional_format( - 10, - 2, - 8 + max_len, - max_width + 1, - { - "type": "cell", - "criteria": "between", - "minimum": -0.01, - "maximum": -0.005, - "format": yellow_cell_format, - }, - ) - tal_sheet.conditional_format( - 10, - 2, - 8 + max_len, - max_width + 1, - { - "type": "cell", - "criteria": "between", - "minimum": -0.005, - "maximum": 0.005, - "format": green_cell_format, - }, - ) - - # ERRORS - - # Title - #err_sheet.merge_range("B1:C2", "LIBRARY", subtitle_merge_format) - #err_sheet.merge_range("D1:D2", lib, subtitle_merge_format) - #err_sheet.merge_range( - # "B3:L7", "SPHERE LEAKAGE TEST RESULTS RECAP: ERRORS", title_merge_format - #) - #err_sheet.merge_range("B8:C8", "ZAID", subtitle_merge_format) - #err_sheet.merge_range("D8:L8", "TALLY", subtitle_merge_format) -# - ## Freeze title - #err_sheet.freeze_panes(9, 0) -# - ## out of bounds - #err_sheet.set_column(0, 0, 4, oob_format) - #err_sheet.set_column(errors_width + 2, 1000, 18, oob_format) - #for i in range(9): - # err_sheet.set_row(i, None, oob_format) - #for i in range(9 + errors_len, 1000): - # err_sheet.set_row(i, None, oob_format) -# - ## Column widths for errors, set up to 15th col by default to ensure title format correct - #err_sheet.set_column(1, 14, 20) - #err_sheet.set_column(1, errors_width + 2, 20) -# - ## Row Heights - #err_sheet.set_row(7, 31) - #err_sheet.set_row(8, 73.25) -# - ## Legend - #err_sheet.merge_range("N3:O3", "LEGEND", merge_format) - #err_sheet.merge_range("N8:O8", "According to MCNP manual", oob_format) - #err_sheet.write("N4", "", red_cell_format) - #err_sheet.write("O4", "> 50%", legend_text_format) - #err_sheet.write("N5", "", orange_cell_format) - #err_sheet.write("O5", "20% ≤ 50%", legend_text_format) - #err_sheet.write("N6", "", yellow_cell_format) - #err_sheet.write("O6", "10% ≤ 20%", legend_text_format) - #err_sheet.write("N7", "", green_cell_format) - #err_sheet.write("O7", "< 10%", legend_text_format) -# - ## Conditional Formatting - #err_sheet.conditional_format( - # 9, - # 3, - # 8 + errors_len, - # errors_width + 1, - # {"type": "blanks", "format": plain_format}, - #) - #err_sheet.conditional_format( - # 9, - # 3, - # 8 + errors_len, - # errors_width + 1, - # {"type": "cell", "criteria": ">", "value": 0.5, "format": red_cell_format}, - #) - #err_sheet.conditional_format( - # 9, - # 3, - # 8 + errors_len, - # errors_width + 1, - # { - # "type": "cell", - # "criteria": "between", - # "minimum": 0.2, - # "maximum": 0.5, - # "format": orange_cell_format, - # }, - #) - #err_sheet.conditional_format( - # 9, - # 3, - # 8 + errors_len, - # errors_width + 1, - # { - # "type": "cell", - # "criteria": "between", - # "minimum": 0.1, - # "maximum": 0.2, - # "format": yellow_cell_format, - # }, - #) - #err_sheet.conditional_format( - # 9, - # 3, - # 8 + errors_len, - # errors_width + 1, - # {"type": "cell", "criteria": "<", "value": -0.5, "format": red_cell_format}, - #) - #err_sheet.conditional_format( - # 9, - # 3, - # 8 + errors_len, - # errors_width + 1, - # { - # "type": "cell", - # "criteria": "between", - # "minimum": -0.5, - # "maximum": -0.2, - # "format": orange_cell_format, - # }, - #) - #err_sheet.conditional_format( - # 9, - # 3, - # 8 + errors_len, - # errors_width + 1, - # { - # "type": "cell", - # "criteria": "between", - # "minimum": -0.2, - # "maximum": -0.1, - # "format": yellow_cell_format, - # }, - #) - #err_sheet.conditional_format( - # 9, - # 3, - # 8 + errors_len, - # errors_width + 1, - # { - # "type": "cell", - # "criteria": "between", - # "minimum": -0.1, - # "maximum": 0.1, - # "format": green_cell_format, - # }, - #) - - # STAT CHECKS - if stats is not None: - # Title - stat_sheet.merge_range("B1:C2", "LIBRARY", subtitle_merge_format) - stat_sheet.merge_range("D1:D2", lib, subtitle_merge_format) - stat_sheet.merge_range( - "B3:L7", - "SPHERE LEAKAGE TEST RESULTS RECAP: STATISTICAL CHECKS", - title_merge_format, - ) - stat_sheet.merge_range("B8:C8", "ZAID", subtitle_merge_format) - stat_sheet.merge_range("D8:L8", "TALLY", subtitle_merge_format) - - # Freeze title - stat_sheet.freeze_panes(9, 0) - - # out of bounds - stat_sheet.set_column(0, 0, 4, oob_format) - stat_sheet.set_column(stats_width + 2, 1000, 18, oob_format) - for i in range(9): - stat_sheet.set_row(i, None, oob_format) - for i in range(9 + stats_len, 1000): - stat_sheet.set_row(i, None, oob_format) - - # Column widths for errors, set up to 15th col by default to ensure title format correct - stat_sheet.set_column(1, 14, 20) - stat_sheet.set_column(1, stats_width + 2, 20) - - # Row Heights - stat_sheet.set_row(7, 31) - stat_sheet.set_row(8, 73.25) - - # Formatting - stat_sheet.conditional_format( - 9, - 3, - 8 + stats_len, - stats_width + 1, - {"type": "blanks", "format": plain_format}, - ) - stat_sheet.conditional_format( - 9, - 3, - 8 + stats_len, - stats_width + 1, - { - "type": "text", - "criteria": "containing", - "value": "Passed", - "format": value_abovezero_format, - }, - ) - stat_sheet.conditional_format( - 9, - 3, - 8 + stats_len, - stats_width + 1, - { - "type": "text", - "criteria": "containing", - "value": "All zeros", - "format": value_allzero_format, - }, - ) - stat_sheet.conditional_format( - 9, - 3, - 8 + stats_len, - stats_width + 1, - { - "type": "text", - "criteria": "containing", - "value": "Missed", - "format": value_belowzero_format, - }, - ) - - wb.close() \ No newline at end of file diff --git a/jade/excelsupport.py b/jade/excelsupport.py index d413c83d..399e2fe3 100644 --- a/jade/excelsupport.py +++ b/jade/excelsupport.py @@ -89,7 +89,7 @@ def insert_df(startrow, startcolumn, df, ws, header=True): #ws.range((row, column)).value = value except (AttributeError, ValueError) as e: print(e) - print('Warning! value not printed: row,column,value', row, + print('Warning! value not printed: row, column, value', row, column, value) def single_excel_writer(self, outpath, lib, testname, tallies, stats=None): """ @@ -124,7 +124,7 @@ def single_excel_writer(self, outpath, lib, testname, tallies, stats=None): df_positions = [] - + print(tallies) for tally, results in tallies.items(): #print(results) tally_len, tally_width = results["Value"].shape @@ -459,4 +459,560 @@ def single_excel_writer(self, outpath, lib, testname, tallies, stats=None): }, ) + wb.close() + +def comp_excel_writer(self, outpath, lib_to_comp, testname, comps, abs_diffs, std_devs): + """ + Produces single library summary excel file using XLSXwriter + + Parameters + ---------- + outpath : path or str + path to the output in Tests/Postprocessing. + lib_to_comp : str + Shorthand representation of data libraries to be + compared (i.e 00c_vs_31c). + tallies : Dataframe + Summary of tally tallies + errors: Dataframe + Errors on tally tallies + stats: Dataframe + Results of statistical checks + + Returns + ------- + None + """ + writer = pd.ExcelWriter(outpath, engine="xlsxwriter") + + title = testname + " RESULTS RECAP: Comparison" + startrow = 8 + startcol = 1 + max_len = 0 + max_width = 0 + df_positions = [] + + for i in range(len(comps.keys())): + comp_len, comp_width = list(comps.values())[i]["Value"].shape + df_positions.append([startrow,startcol]) + list(comps.values())[i]["Value"].to_excel(writer, startrow=startrow+1, + startcol=startcol, + sheet_name="Comparisons (%)") + list(std_devs.values())[i]["Value"].to_excel(writer, startrow=startrow+1, + startcol=startcol, + sheet_name="Comparisons (std. dev.)") + list(abs_diffs.values())[i]["Value"].to_excel(writer, startrow=startrow+1, + startcol=startcol, + sheet_name="Comparisons (abs. diff.)") + + startrow = startrow + comp_len + 3 + max_len = max_len + comp_len + 3 + if comp_width > max_width: + max_width = comp_width + + wb = writer.book + comp_sheet = writer.sheets["Comparisons (%)"] + std_dev_sheet = writer.sheets["Comparisons (std. dev.)"] + absdiff_sheet = writer.sheets["Comparisons (abs. diff.)"] + + # Formatting styles + plain_format = wb.add_format({"bg_color": "#FFFFFF"}) + oob_format = wb.add_format( + { + "align": "center", + "valign": "center", + "bg_color": "#D9D9D9", + "text_wrap": True, + } + ) + tally_format = wb.add_format({"bg_color": "#D9D9D9"}) + merge_format = wb.add_format( + {"align": "center", "valign": "center", "border": 2} + ) + title_merge_format = wb.add_format( + { + "font_size": "36", + "align": "center", + "valign": "center", + "bold": True, + "border": 2, + } + ) + subtitle_merge_format = wb.add_format( + { + "font_size": "16", + "align": "center", + "valign": "center", + "bold": True, + "border": 2, + } + ) + legend_text_format = wb.add_format({"align": "center", "bg_color": "white"}) + red_cell_format = wb.add_format({"bg_color": "red"}) + orange_cell_format = wb.add_format({"bg_color": "orange"}) + yellow_cell_format = wb.add_format({"bg_color": "yellow"}) + green_cell_format = wb.add_format({"bg_color": "#A6D86E"}) + value_allzero_format = wb.add_format({"bg_color": "#EDEDED"}) + value_belowzero_format = wb.add_format({"bg_color": "#FFC7CE"}) + value_abovezero_format = wb.add_format({"bg_color": "#C6EFCE"}) + not_avail_format = wb.add_format({"bg_color": "#B8B8B8"}) + target_ref_format = wb.add_format({"bg_color": "#8465C5"}) + identical_format = wb.add_format({"bg_color": "#7ABD7E"}) + + # COMPARISON + + # Title Format + comp_sheet.merge_range("B1:C2", "LIBRARY", subtitle_merge_format) + comp_sheet.merge_range("D1:D2", lib_to_comp, subtitle_merge_format) + comp_sheet.merge_range( + "B3:L8", "{} RESULTS RECAP: COMPARISON (%)".format(testname), title_merge_format + ) + for tal in range(len(df_positions)): + comp_sheet.merge_range(df_positions[tal][0], + df_positions[tal][1] + 1, + df_positions[tal][0], + df_positions[tal][1] + 4, + list(comps.values())[tal]["title"], + subtitle_merge_format) + #comp_sheet.merge_range("B8:C8", "ZAID", subtitle_merge_format) + #comp_sheet.merge_range("D8:L8", "TALLY", subtitle_merge_format) + + # Freeze title + comp_sheet.freeze_panes(8, 2) + + # out of bounds + comp_sheet.set_column(0, 0, 4, oob_format) + comp_sheet.set_column(max_width + 2, max_width + 20, 18, oob_format) + for i in range(9): + comp_sheet.set_row(i, None, oob_format) + for i in range(8 + max_len, max_len + 50): + comp_sheet.set_row(i, None, oob_format) + + # Column widths for tallies, set up to 15th col to ensure title format correct + comp_sheet.set_column(1, 14, 20) + comp_sheet.set_column(1, max_width + 2, 20) + + # Row Heights + comp_sheet.set_row(7, 31) + #comp_sheet.set_row(8, 73.25) + + # Legend + comp_sheet.merge_range("N3:O3", "LEGEND", merge_format) + comp_sheet.write("N4", "", red_cell_format) + comp_sheet.write("O4", ">|20|%", legend_text_format) + comp_sheet.write("N5", "", orange_cell_format) + comp_sheet.write("O5", "|20|%≤|10|%", legend_text_format) + comp_sheet.write("N6", "", yellow_cell_format) + comp_sheet.write("O6", "|10|%≤|5|%", legend_text_format) + comp_sheet.write("N7", "", green_cell_format) + comp_sheet.write("O7", "<|5|%", legend_text_format) + + comp_sheet.conditional_format( + 10, + 2, + 8 + max_len, + max_width + 1, + {"type": "blanks", "format": oob_format}, + ) + comp_sheet.conditional_format( + 10, + 2, + 8 + max_len, + max_width + 1, + {"type": "blanks", "format": plain_format}, + ) + comp_sheet.conditional_format( + 10, + 2, + 8 + max_len, + max_width + 1, + { + "type": "text", + "criteria": "containing", + "value": "Not Available", + "format": not_avail_format, + }, + ) + comp_sheet.conditional_format( + 10, + 2, + 8 + max_len, + max_width + 1, + { + "type": "text", + "criteria": "containing", + "value": "Target = 0", + "format": target_ref_format, + }, + ) + comp_sheet.conditional_format( + 10, + 2, + 8 + max_len, + max_width + 1, + { + "type": "text", + "criteria": "containing", + "value": "Reference = 0", + "format": target_ref_format, + }, + ) + comp_sheet.conditional_format( + 10, + 2, + 8 + max_len, + max_width + 1, + { + "type": "text", + "criteria": "containing", + "value": "Identical", + "format": identical_format, + }, + ) + comp_sheet.conditional_format( + 10, + 2, + 8 + max_len, + max_width + 1, + { + "type": "cell", + "criteria": "greater than", + "value": 0.2, + "format": red_cell_format, + }, + ) + comp_sheet.conditional_format( + 10, + 2, + 8 + max_len, + max_width + 1, + { + "type": "cell", + "criteria": "between", + "minimum": 0.1, + "maximum": 0.2, + "format": orange_cell_format, + }, + ) + comp_sheet.conditional_format( + 10, + 2, + 8 + max_len, + max_width + 1, + { + "type": "cell", + "criteria": "between", + "minimum": 0.05, + "maximum": 0.1, + "format": yellow_cell_format, + }, + ) + comp_sheet.conditional_format( + 10, + 2, + 8 + max_len, + max_width + 1, + { + "type": "cell", + "criteria": "less than", + "value": -0.2, + "format": red_cell_format, + }, + ) + comp_sheet.conditional_format( + 10, + 2, + 8 + max_len, + max_width + 1, + { + "type": "cell", + "criteria": "between", + "minimum": -0.5, + "maximum": -0.1, + "format": orange_cell_format, + }, + ) + comp_sheet.conditional_format( + 10, + 2, + 8 + max_len, + max_width + 1, + { + "type": "cell", + "criteria": "between", + "minimum": -0.1, + "maximum": -0.05, + "format": yellow_cell_format, + }, + ) + comp_sheet.conditional_format( + 10, + 2, + 8 + max_len, + max_width + 1, + { + "type": "cell", + "criteria": "between", + "minimum": -0.05, + "maximum": 0.05, + "format": green_cell_format, + }, + ) + # ABSOLUTE DIFFERENCE + # Title + absdiff_sheet.merge_range("B1:C2", "LIBRARY", subtitle_merge_format) + absdiff_sheet.merge_range("D1:D2", lib_to_comp, subtitle_merge_format) + absdiff_sheet.merge_range( + "B3:L8", "{} RESULTS RECAP: COMPARISON (Absolute Difference)".format(testname), title_merge_format + ) + for tal in range(len(df_positions)): + absdiff_sheet.merge_range(df_positions[tal][0], + df_positions[tal][1] + 1, + df_positions[tal][0], + df_positions[tal][1] + 4, + list(comps.values())[tal]["title"], + subtitle_merge_format) + #absdiff_sheet.merge_range("B8:C8", "ZAID", subtitle_merge_format) + #absdiff_sheet.merge_range("D8:L8", "TALLY", subtitle_merge_format) + + # Freeze title + absdiff_sheet.freeze_panes(8, 2) + + # out of bounds + absdiff_sheet.set_column(0, 0, 4, oob_format) + absdiff_sheet.set_column(max_width + 2, max_width + 20, 18, oob_format) + for i in range(9): + absdiff_sheet.set_row(i, None, oob_format) + for i in range(8 + max_len, max_len + 50): + absdiff_sheet.set_row(i, None, oob_format) + + + # Column widths for errors, set up to 15th col by default to ensure title format correct + absdiff_sheet.set_column(1, 14, 20) + absdiff_sheet.set_column(1, max_width + 2, 20) + + # Row Heights + absdiff_sheet.set_row(7, 31) + #absdiff_sheet.set_row(8, 73.25) + + # Legend + absdiff_sheet.merge_range("N3:O3", "LEGEND", merge_format) + absdiff_sheet.merge_range("N8:O8", "According to MCNP manual", oob_format) + absdiff_sheet.write("N4", "", red_cell_format) + absdiff_sheet.write("O4", "> 50%", legend_text_format) + absdiff_sheet.write("N5", "", orange_cell_format) + absdiff_sheet.write("O5", "20% ≤ 50%", legend_text_format) + absdiff_sheet.write("N6", "", yellow_cell_format) + absdiff_sheet.write("O6", "10% ≤ 20%", legend_text_format) + absdiff_sheet.write("N7", "", green_cell_format) + absdiff_sheet.write("O7", "< 10%", legend_text_format) + + # Conditional Formatting + absdiff_sheet.conditional_format( + 10, + 1, + 8 + max_len, + max_width + 1, + {"type": "blanks", "format": oob_format}, + ) + # STANDARD DEVIATIONS + + # Title Format + std_dev_sheet.merge_range("B1:C2", "LIBRARY", subtitle_merge_format) + std_dev_sheet.merge_range("D1:D2", lib_to_comp, subtitle_merge_format) + std_dev_sheet.merge_range( + "B3:L8", "{} RESULTS RECAP: COMPARISON (Standard deviations from reference library)".format(testname), title_merge_format + ) + for tal in range(len(df_positions)): + std_dev_sheet.merge_range(df_positions[tal][0], + df_positions[tal][1] + 1, + df_positions[tal][0], + df_positions[tal][1] + 4, + list(comps.values())[tal]["title"], + subtitle_merge_format) + #std_dev_sheet.merge_range("B8:C8", "ZAID", subtitle_merge_format) + #std_dev_sheet.merge_range("D8:L8", "TALLY", subtitle_merge_format) + + # Freeze title + std_dev_sheet.freeze_panes(8, 2) + + # out of bounds + std_dev_sheet.set_column(0, 0, 4, oob_format) + std_dev_sheet.set_column(max_width + 2, max_width + 20, 18, oob_format) + for i in range(9): + std_dev_sheet.set_row(i, None, oob_format) + for i in range(8 + max_len, max_len + 50): + std_dev_sheet.set_row(i, None, oob_format) + + # Column widths for tallies, set up to 15th col to ensure title format correct + std_dev_sheet.set_column(1, 14, 20) + std_dev_sheet.set_column(1, max_width + 2, 20) + + # Row Heights + std_dev_sheet.set_row(7, 31) + #std_dev_sheet.set_row(8, 73.25) + + # Legend + std_dev_sheet.merge_range("N3:O3", "LEGEND", merge_format) + std_dev_sheet.write("N4", "", red_cell_format) + std_dev_sheet.write("O4", ">|3|%", legend_text_format) + std_dev_sheet.write("N5", "", orange_cell_format) + std_dev_sheet.write("O5", "|2|%≤|3|%", legend_text_format) + std_dev_sheet.write("N6", "", yellow_cell_format) + std_dev_sheet.write("O6", "|1|%≤|2|%", legend_text_format) + std_dev_sheet.write("N7", "", green_cell_format) + std_dev_sheet.write("O7", "<|1|%", legend_text_format) + + std_dev_sheet.conditional_format( + 10, + 1, + 8 + max_len, + max_width + 1, + {"type": "blanks", "format": oob_format}, + ) + std_dev_sheet.conditional_format( + 10, + 2, + 8 + max_len, + max_width + 1, + {"type": "blanks", "format": plain_format}, + ) + std_dev_sheet.conditional_format( + 10, + 2, + 8 + max_len, + max_width + 1, + { + "type": "text", + "criteria": "containing", + "value": "Not Available", + "format": not_avail_format, + }, + ) + std_dev_sheet.conditional_format( + 10, + 2, + 8 + max_len, + max_width + 1, + { + "type": "text", + "criteria": "containing", + "value": "Target = 0", + "format": target_ref_format, + }, + ) + std_dev_sheet.conditional_format( + 10, + 2, + 8 + max_len, + max_width + 1, + { + "type": "text", + "criteria": "containing", + "value": "Reference = 0", + "format": target_ref_format, + }, + ) + std_dev_sheet.conditional_format( + 10, + 2, + 8 + max_len, + max_width + 1, + { + "type": "text", + "criteria": "containing", + "value": "Identical", + "format": identical_format, + }, + ) + std_dev_sheet.conditional_format( + 10, + 2, + 8 + max_len, + max_width + 1, + { + "type": "cell", + "criteria": "greater than", + "value": 3, + "format": red_cell_format, + }, + ) + std_dev_sheet.conditional_format( + 10, + 2, + 8 + max_len, + max_width + 1, + { + "type": "cell", + "criteria": "between", + "minimum": 2, + "maximum": 3, + "format": orange_cell_format, + }, + ) + std_dev_sheet.conditional_format( + 10, + 2, + 8 + max_len, + max_width + 1, + { + "type": "cell", + "criteria": "between", + "minimum": 1, + "maximum": 2, + "format": yellow_cell_format, + }, + ) + std_dev_sheet.conditional_format( + 10, + 2, + 8 + max_len, + max_width + 1, + { + "type": "cell", + "criteria": "less than", + "value": -3, + "format": red_cell_format, + }, + ) + std_dev_sheet.conditional_format( + 10, + 2, + 8 + max_len, + max_width + 1, + { + "type": "cell", + "criteria": "between", + "minimum": -3, + "maximum": -2, + "format": orange_cell_format, + }, + ) + std_dev_sheet.conditional_format( + 10, + 2, + 8 + max_len, + max_width + 1, + { + "type": "cell", + "criteria": "between", + "minimum": -2, + "maximum": -1, + "format": yellow_cell_format, + }, + ) + std_dev_sheet.conditional_format( + 10, + 2, + 8 + max_len, + max_width + 1, + { + "type": "cell", + "criteria": "between", + "minimum": -1, + "maximum": 1, + "format": green_cell_format, + }, + ) + wb.close() \ No newline at end of file diff --git a/jade/output.py b/jade/output.py index dd5a9710..65deee82 100644 --- a/jade/output.py +++ b/jade/output.py @@ -242,36 +242,6 @@ def __init__(self, lib, config, session): self.atlas_path_d1s = atlas_path_d1s self.couples = couples # Couples of libraries to post process - """ - if self.mcnp: - atlas_path_mcnp = os.path.join(self.atlas_path, "mcnp") - os.mkdir(atlas_path_mcnp) - self.atlas_path_mcnp = atlas_path_mcnp - raw_path_mcnp = os.path.join(self.raw_path, "mcnp") - os.mkdir(raw_path_mcnp) - self.raw_path_mcnp = raw_path_mcnp - if self.serpent: - atlas_path_serpent = os.path.join(self.atlas_path, "serpent") - os.mkdir(atlas_path_serpent) - self.atlas_path_serpent = atlas_path_serpent - raw_path_serpent = os.path.join(self.raw_path, "serpent") - os.mkdir(raw_path_serpent) - self.raw_path_serpent = raw_path_serpent - if self.openmc: - atlas_path_openmc = os.path.join(self.atlas_path, "openmc") - os.mkdir(atlas_path_openmc) - self.atlas_path_openmc = atlas_path_openmc - raw_path_openmc = os.path.join(self.raw_path, "openmc") - os.mkdir(raw_path_openmc) - self.raw_path_openmc = raw_path_openmc - if self.d1s: - atlas_path_d1s = os.path.join(self.atlas_path, "d1s") - os.mkdir(atlas_path_d1s) - self.atlas_path_d1s = atlas_path_d1s - raw_path_d1s = os.path.join(self.raw_path, "d1s") - os.mkdir(raw_path_d1s) - self.raw_path_d1s = raw_path_d1s - """ # SINGLE-LIBRARY else: self.single = True # Indicator for single or comparison @@ -331,92 +301,6 @@ def __init__(self, lib, config, session): self.raw_path_d1s = raw_path_d1s self.atlas_path_d1s = atlas_path_d1s - - # Generate folders for each code - """if self.mcnp: - atlas_path_mcnp = os.path.join(self.atlas_path, "mcnp") - os.mkdir(atlas_path_mcnp) - self.atlas_path_mcnp = atlas_path_mcnp - raw_path_mcnp = os.path.join(self.raw_path, "mcnp") - os.mkdir(raw_path_mcnp) - self.raw_path_mcnp = raw_path_mcnp - if self.serpent: - atlas_path_serpent = os.path.join(self.atlas_path, "serpent") - os.mkdir(atlas_path_serpent) - self.atlas_path_serpent = atlas_path_serpent - raw_path_serpent = os.path.join(self.raw_path, "serpent") - os.mkdir(raw_path_serpent) - self.raw_path_serpent = raw_path_serpent - if self.openmc: - atlas_path_openmc = os.path.join(self.atlas_path, "openmc") - os.mkdir(atlas_path_openmc) - self.atlas_path_openmc = atlas_path_openmc - raw_path_openmc = os.path.join(self.raw_path, "openmc") - os.mkdir(raw_path_openmc) - self.raw_path_openmc = raw_path_openmc - if self.d1s: - atlas_path_d1s = os.path.join(self.atlas_path, "d1s") - os.mkdir(atlas_path_d1s) - self.atlas_path_d1s = atlas_path_d1s - raw_path_d1s = os.path.join(self.raw_path, "d1s") - os.mkdir(raw_path_d1s) - self.raw_path_d1s = raw_path_d1s""" - """ - # Attempt to fix folder structure - unfinished - if self.mcnp: - path_mcnp = os.path.join(out, 'mcnp') - os.mkdir(path_mcnp) - self.path_mcnp = path_mcnp - atlas_path_mcnp = os.path.join(path_mcnp, 'Atlas') - os.mkdir(atlas_path_mcnp) - self.atlas_path_mcnp = atlas_path_mcnp - excel_path_mcnp = os.path.join(path_mcnp, 'Excel') - os.mkdir(excel_path_mcnp) - self.excel_path_mcnp = excel_path_mcnp - raw_path_mcnp = os.path.join(path_mcnp, 'Raw') - os.mkdir(raw_path_mcnp) - self.raw_path_mcnp = raw_path_mcnp - if self.serpent: - path_serpent = os.path.join(out, 'serpent') - os.mkdir(path_serpent) - self.path_serpent = path_serpent - atlas_path_serpent = os.path.join(path_serpent, 'Atlas') - os.mkdir(atlas_path_serpent) - self.atlas_path_serpent = atlas_path_serpent - excel_path_serpent = os.path.join(path_serpent, 'Excel') - os.mkdir(excel_path_serpent) - self.excel_path_serpent = excel_path_serpent - raw_path_serpent = os.path.join(path_serpent, 'Raw') - os.mkdir(raw_path_serpent) - self.raw_path_serpent = raw_path_serpent - if self.openmc: - path_openmc = os.path.join(out, 'openmc') - os.mkdir(path_openmc) - self.path_openmc = path_openmc - atlas_path_openmc = os.path.join(path_openmc, 'Atlas') - os.mkdir(atlas_path_openmc) - self.atlas_path_openmc = atlas_path_openmc - excel_path_openmc = os.path.join(path_openmc, 'Excel') - os.mkdir(excel_path_openmc) - self.excel_path_openmc = excel_path_openmc - raw_path_openmc = os.path.join(path_openmc, 'Raw') - os.mkdir(raw_path_openmc) - self.raw_path_openmc = raw_path_openmc - if self.d1s: - path_d1s = os.path.join(out, 'd1s') - os.mkdir(path_d1s) - self.path_d1s = path_d1s - atlas_path_d1s = os.path.join(path_d1s, 'Atlas') - os.mkdir(atlas_path_d1s) - self.atlas_path_d1s = atlas_path_d1s - excel_path_d1s = os.path.join(path_d1s, 'Excel') - os.mkdir(excel_path_d1s) - self.excel_path_d1s = excel_path_d1s - raw_path_d1s = os.path.join(path_d1s, 'Raw') - os.mkdir(raw_path_d1s) - self.raw_path_d1s = raw_path_d1s - """ - def single_postprocess(self): """ Execute the full post-processing of a single library (i.e. excel, @@ -455,6 +339,7 @@ def single_postprocess(self): print(tally_num) try: if self.mcnp: + print(self.outputs["mcnp"]) output = self.outputs["mcnp"][tally_num] except KeyError: fatal_exception( @@ -544,7 +429,8 @@ def compare(self): self._generate_comparison_excel_output() print(" Creating Atlas...") - outpath = os.path.join(self.atlas_path, "tmp") + if self.mcnp: + outpath = os.path.join(self.atlas_path_mcnp, "tmp") os.mkdir(outpath) # Get atlas configuration @@ -552,10 +438,7 @@ def compare(self): atl_cnf.set_index("Tally", inplace=True) # Printing Atlas - template = os.path.join( - self.code_path, - "templates", - "AtlasTemplate.docx") + template = os.path.join(self.path_templates, "AtlasTemplate.docx") atlas = at.Atlas(template, self.testname + "_" + self.name) @@ -563,13 +446,13 @@ def compare(self): outputs_dic = {} for lib in self.lib: # Recover lib output - out_path = os.path.join( - self.session.path_single, - lib, - self.testname, - "Raw_Data", - lib + ".pickle", - ) + if self.mcnp: + out_path = os.path.join( + self.session.path_single, + lib, self.testname, + "mcnp", "Raw_Data", + lib + ".pickle" + ) with open(out_path, "rb") as handle: outputs = pickle.load(handle) outputs_dic[lib] = outputs @@ -583,7 +466,15 @@ def compare(self): atl_cnf_plot = atl_cnf[atl_cnf[plot_type]] for tally_num in tqdm(atl_cnf_plot.index, desc="Tallies"): # The last 'outputs' can be easily used for common data - output = outputs[tally_num] + try: + if self.mcnp: + output = self.outputs["mcnp"][tally_num] + except KeyError: + fatal_exception( + "tally n. " + + str(tally_num) + + " is in config but not in the MCNP output" + ) vals_df = output["Value"] err_df = output["Error"] quantity = str(atl_cnf_plot["Quantity"].loc[tally_num]) @@ -656,8 +547,9 @@ def compare(self): img_path = plot.plot(plot_type) atlas.insert_img(img_path) + if self.mcnp: + atlas.save(self.atlas_path_mcnp) - atlas.save(self.atlas_path) # Remove tmp images shutil.rmtree(outpath) @@ -702,6 +594,7 @@ def _generate_single_excel_output(self): outpath = os.path.join( self.excel_path_mcnp, self.testname + "_" + self.lib + ".xlsx" ) + outputs = {} #ex = ExcelOutputSheet(template, outpath) # Get results # results = [] @@ -724,7 +617,7 @@ def _generate_single_excel_output(self): self.raw_data = mcnp_output.tallydata # res, err = output.get_single_excel_data() - outputs = {} + for label in ["Value", "Error"]: # keys = {} @@ -852,9 +745,9 @@ def _generate_single_excel_output(self): self.outputs["mcnp"] = outputs # print(outputs) # Dump them for comparisons - #outpath = os.path.join(self.raw_path_mcnp, self.lib + ".pickle") - #with open(outpath, "wb") as outfile: - # pickle.dump(outputs, outfile) + raw_outpath = os.path.join(self.raw_path_mcnp, self.lib + ".pickle") + with open(raw_outpath, "wb") as outfile: + pickle.dump(outputs, outfile) # Compile general infos in the sheet #ws = ex.current_ws @@ -892,163 +785,233 @@ def _print_raw(self): def _generate_comparison_excel_output(self): # Get excel configuration + self.outputs = {} + self.results = {} + self.errors = {} + self.stat_checks = {} ex_cnf = pd.read_excel(self.cnf_path, sheet_name="Excel") ex_cnf.set_index("Tally", inplace=True) # Open the excel file - name_tag = "Generic_comparison.xlsx" - template = os.path.join(os.getcwd(), "templates", name_tag) - - mcnp_outputs = {} - iteration = 0 - for reflib, tarlib, name in self.couples: - iteration = iteration + 1 - outpath = os.path.join( - self.excel_path, - self.testname + - "_Comparison_" + - name + - ".xlsx") - ex = ExcelOutputSheet(template, outpath) - # Get results - if iteration == 1: - to_read = [reflib, tarlib] - else: - to_read = [tarlib] - - for lib in to_read: - results_path = self.test_path[lib] - - # Get mfile and outfile and possibly meshtal file - meshtalfile = None - for file in os.listdir(results_path): - if file[-1] == "m": - mfile = os.path.join(results_path, file) - elif file[-1] == "o": - ofile = os.path.join(results_path, file) - elif file[-4:] == "msht": - meshtalfile = os.path.join(results_path, file) - # Parse output - mcnp_output = MCNPoutput( - mfile, ofile, meshtal_file=meshtalfile) - mcnp_outputs[lib] = mcnp_output - - # Build the comparison - for tally in mcnp_outputs[reflib].mctal.tallies: - num = tally.tallyNumber - key = tally.tallyComment[0] + #name_tag = "Generic_comparison.xlsx" + #template = os.path.join(os.getcwd(), "templates", name_tag) - # Full tally data - tdata_ref = mcnp_outputs[reflib].tallydata[num].copy() - tdata_tar = mcnp_outputs[tarlib].tallydata[num].copy() - try: - tally_settings = ex_cnf.loc[num] - except KeyError: - print( - " Warning!: tally n." + - str(num) + - " is not in configuration") - continue - - # Re-Elaborate tdata Dataframe - x_name = tally_settings["x"] - x_tag = tally_settings["x name"] - y_name = tally_settings["y"] - y_tag = tally_settings["y name"] - ylim = tally_settings["cut Y"] - # select the index format - if x_name == "Energy": - idx_format = "0.00E+00" - # TODO all possible cases should be addressed - else: - idx_format = "0" + + if self.mcnp: + mcnp_outputs = {} + comps = {} + abs_diffs = {} + std_devs = {} + for reflib, tarlib, name in self.couples: + lib_to_comp = name + outfolder_path = self.excel_path_mcnp + outpath = os.path.join( + outfolder_path, "Comparison_" + name + "_mcnp.xlsx" + ) - if y_name != "tally": - tdata_ref.set_index(x_name, inplace=True) - tdata_tar.set_index(x_name, inplace=True) - x_set = list(set(tdata_ref.index)) - y_set = list(set(tdata_ref[y_name].values)) - rows = [] - for xval in x_set: + #ex = ExcelOutputSheet(template, outpath) + # Get results + + #for lib in to_read: + # results_path = self.test_path[lib] + for lib, results_path in { + reflib: os.path.join(self.test_path[reflib], "mcnp"), + tarlib: os.path.join(self.test_path[tarlib], "mcnp"), + }.items(): + results = [] + errors = [] + # Get mfile and outfile and possibly meshtal file + meshtalfile = None + for file in os.listdir(results_path): + if file[-1] == "m": + mfile = os.path.join(results_path, file) + elif file[-1] == "o": + ofile = os.path.join(results_path, file) + elif file[-4:] == "msht": + meshtalfile = os.path.join(results_path, file) + # Parse output + mcnp_output = MCNPoutput( + mfile, ofile, meshtal_file=meshtalfile) + mcnp_outputs[lib] = mcnp_output + # Build the comparison + for label in ["Value", "Error"]: + for tally in mcnp_outputs[reflib].mctal.tallies: + num = tally.tallyNumber + key = tally.tallyComment[0] + + # Full tally data + tdata_ref = mcnp_outputs[reflib].tallydata[num].copy() + tdata_tar = mcnp_outputs[tarlib].tallydata[num].copy() try: - ref = tdata_ref.loc[xval, "Value"].values - tar = tdata_tar.loc[xval, "Value"].values + tally_settings = ex_cnf.loc[num] + except KeyError: + print( + " Warning!: tally n." + + str(num) + + " is not in configuration") + continue + + # Re-Elaborate tdata Dataframe + x_name = tally_settings["x"] + x_tag = tally_settings["x name"] + y_name = tally_settings["y"] + y_tag = tally_settings["y name"] + ylim = tally_settings["cut Y"] + # select the index format + if label == "Value": + for dic in [comps, abs_diffs, std_devs]: + dic[num] = {"title": key, "x_label": x_tag} + + + if x_name == "Energy": + idx_format = "0.00E+00" + # TODO all possible cases should be addressed + else: + idx_format = "0" + + if y_name != "tally": + tdata_ref.set_index(x_name, inplace=True) + tdata_tar.set_index(x_name, inplace=True) + x_set = list(set(tdata_ref.index)) + y_set = list(set(tdata_ref[y_name].values)) + rows_fin = [] + rows_abs_diff = [] + rows_std_dev = [] + for xval in x_set: + try: + ref = tdata_ref.loc[xval, "Value"].values + ref_err = tdata_ref.loc[xval, "Error"].values + tar = tdata_tar.loc[xval, "Value"].values + # !!! True divide warnings are suppressed !!! + with np.errstate(divide="ignore", invalid="ignore"): + row_fin = (ref - tar) / ref + row_abs_diff = (ref - tar) + row_std_dev = row_abs_diff / (ref_err*ref) + prev_len = len(ref) + except AttributeError: + # This is raised when total values are + # collected only for one bin. + # the rest needs to be filled by nan + ref = tdata_ref.loc[xval, "Value"] + ref_err = tdata_ref.loc[xval, "Error"] + tar = tdata_tar.loc[xval, "Value"] + for i in range(prev_len - 1): + row_fin.append(np.nan) + row_abs_diff.append(np.nan) + row_std_dev.append(np.nan) + row_fin.append((ref - tar) / ref) + row_abs_diff.append(ref - tar) + row_std_dev.append((ref - tar)/(ref_err*ref)) + + rows_fin.append(row_fin) + rows_abs_diff.append(row_abs_diff) + rows_std_dev.append(row_std_dev) + try: + final = pd.DataFrame( + rows_fin, columns=y_set, index=x_set) + abs_diff = pd.DataFrame( + rows_abs_diff, columns=y_set, index=x_set) + std_dev = pd.DataFrame( + rows_std_dev, columns=y_set, index=x_set) + for df in [final, abs_diff, std_dev]: + df.index.name = x_name + df.replace(np.nan, "Not Available", inplace=True) + df.replace(float(0), "Identical", inplace=True) + df.replace(-np.inf, "Reference = 0", inplace=True) + df.replace(1, "Target = 0", inplace=True) + except ValueError: + print( + CRED + + """ + A ValueError was triggered, a probable cause may be that more than 2 binnings + are defined in tally {}. This is a fatal exception, application will now + close""".format( + str(num) + ) + + CEND + ) + # Safely exit from excel and from application + sys.exit() + + # reorder index and quick index reset + for df in [final, abs_diff, std_dev]: + df.reset_index(inplace=True) + df = self._reorder_df(df, x_name) + df.set_index(x_name, inplace=True) + comps[num][label] = final + abs_diffs[num][label] = abs_diff + std_devs[num][label] = std_dev + # insert the df in pieces + #ex.insert_cutted_df( + # "B", + # main_value_df, + # "Comparison", + # ylim, + # header=(key, "Tally n." + str(num)), + # index_name=x_tag, + # cols_name=y_tag, + # index_num_format=idx_format, + # values_format="0.00%", + #) + else: + # reorder dfs + try: + tdata_ref = self._reorder_df(tdata_ref, x_name) + except KeyError: + print( + CRED + + """ + {} is not available in tally {}. Please check the configuration file. + The application will now exit """.format( + x_name, str(num) + ) + + CEND + ) + # Safely exit from excel and from application + sys.exit() + + del tdata_ref["Error"] + tdata_ref.set_index(x_name, inplace=True) + + tdata_tar = self._reorder_df(tdata_tar, x_name) + del tdata_tar["Error"] + tdata_tar.set_index(x_name, inplace=True) + # !!! True divide warnings are suppressed !!! with np.errstate(divide="ignore", invalid="ignore"): - row = (ref - tar) / ref - prev_len = len(ref) - except AttributeError: - # This is raised when total values are - # collected only for one bin. - # the rest needs to be filled by nan - ref = tdata_ref.loc[xval, "Value"] - tar = tdata_tar.loc[xval, "Value"] - row = [] - for i in range(prev_len - 1): - row.append(np.nan) - row.append((ref - tar) / ref) - - rows.append(row) - - main_value_df = pd.DataFrame( - rows, columns=y_set, index=x_set) - main_value_df.index.name = x_name - # reorder index and quick index reset - main_value_df.reset_index(inplace=True) - main_value_df = self._reorder_df(main_value_df, x_name) - main_value_df.set_index(x_name, inplace=True) - - # insert the df in pieces - ex.insert_cutted_df( - "B", - main_value_df, - "Comparison", - ylim, - header=(key, "Tally n." + str(num)), - index_name=x_tag, - cols_name=y_tag, - index_num_format=idx_format, - values_format="0.00%", - ) - else: - # reorder dfs - tdata_ref = self._reorder_df(tdata_ref, x_name) - del tdata_ref["Error"] - tdata_ref.set_index(x_name, inplace=True) - - tdata_tar = self._reorder_df(tdata_tar, x_name) - del tdata_tar["Error"] - tdata_tar.set_index(x_name, inplace=True) - - # !!! True divide warnings are suppressed !!! - with np.errstate(divide="ignore", invalid="ignore"): - df = (tdata_ref - tdata_tar) / tdata_ref - - # Insert DF - ex.insert_df( - "B", - df, - "Comparison", - print_index=True, - header=(key, "Tally n." + str(num)), - values_format="0.00%", - ) + comp_df = (tdata_ref - tdata_tar) / tdata_ref + abs_diff_df = (tdata_ref - tdata_tar) + std_dev_df = abs_diff_df + comps[num][label] = comp_df + abs_diffs[num][label] = abs_diff_df + std_devs[num][label] = abs_diff_df + # Insert DF + #ex.insert_df( + # "B", + # df, + # "Comparison", + # print_index=True, + # header=(key, "Tally n." + str(num)), + # values_format="0.00%", + #) - # Compile general infos in the sheet - ws = ex.current_ws - title = self.testname + " RESULTS RECAP: Comparison" - ws.range("A3").value = title - ws.range("C1").value = tarlib + " Vs " + reflib - - # Add single pp sheets - for lib in [reflib, tarlib]: - cp = self.state.get_path( - "single", [lib, self.testname, "Excel"]) - file = os.listdir(cp)[0] - cp = os.path.join(cp, file) - ex.copy_sheets(cp) - - ex.save() + # Compile general infos in the sheet + #ws = ex.current_ws + #title = self.testname + " RESULTS RECAP: Comparison" + #ws.range("A3").value = title + #ws.range("C1").value = tarlib + " Vs " + reflib + + # Add single pp sheets + #for lib in [reflib, tarlib]: + # cp = self.state.get_path( + # "single", [lib, self.testname, "Excel"]) + # file = os.listdir(cp)[0] + # cp = os.path.join(cp, file) + # ex.copy_sheets(cp) + + #ex.save() + self.outputs["mcnp"] = comps + exsupp.comp_excel_writer(self, outpath, lib_to_comp, self.testname, comps, abs_diffs, std_devs) class MCNPoutput: diff --git a/jade/sphereoutput.py b/jade/sphereoutput.py index 0d2da12e..a2f1260e 100644 --- a/jade/sphereoutput.py +++ b/jade/sphereoutput.py @@ -1325,8 +1325,7 @@ def pp_excel_comparison(self): # Build the final excel data final = (comp_dfs[0].loc[newidx] - comp_dfs[1].loc[newidx]) / comp_dfs[ - 0 - ].loc[newidx] + 0].loc[newidx] absdiff = comp_dfs[0].loc[newidx] - comp_dfs[1].loc[newidx] #self.diff_data["openmc"] = final @@ -1839,7 +1838,7 @@ def sphere_comp_excel_writer(self, outpath, name, final, absdiff, std_dev, summa { "type": "cell", "criteria": "greater than", - "value": 0.2, + "value": 3, "format": red_cell_format, }, ) @@ -1851,8 +1850,8 @@ def sphere_comp_excel_writer(self, outpath, name, final, absdiff, std_dev, summa { "type": "cell", "criteria": "between", - "minimum": 0.1, - "maximum": 0.2, + "minimum": 3, + "maximum": 2, "format": orange_cell_format, }, ) @@ -1864,8 +1863,8 @@ def sphere_comp_excel_writer(self, outpath, name, final, absdiff, std_dev, summa { "type": "cell", "criteria": "between", - "minimum": 0.05, - "maximum": 0.1, + "minimum": 2, + "maximum": 1, "format": yellow_cell_format, }, ) @@ -1877,7 +1876,7 @@ def sphere_comp_excel_writer(self, outpath, name, final, absdiff, std_dev, summa { "type": "cell", "criteria": "less than", - "value": -0.2, + "value": -3, "format": red_cell_format, }, ) @@ -1889,8 +1888,8 @@ def sphere_comp_excel_writer(self, outpath, name, final, absdiff, std_dev, summa { "type": "cell", "criteria": "between", - "minimum": -0.5, - "maximum": -0.1, + "minimum": -3, + "maximum": -2, "format": orange_cell_format, }, ) @@ -1902,8 +1901,8 @@ def sphere_comp_excel_writer(self, outpath, name, final, absdiff, std_dev, summa { "type": "cell", "criteria": "between", - "minimum": -0.1, - "maximum": -0.05, + "minimum": -2, + "maximum": -1, "format": yellow_cell_format, }, ) @@ -1915,8 +1914,8 @@ def sphere_comp_excel_writer(self, outpath, name, final, absdiff, std_dev, summa { "type": "cell", "criteria": "between", - "minimum": -0.05, - "maximum": 0.05, + "minimum": -1, + "maximum": 1, "format": green_cell_format, }, ) diff --git a/jade/xsdirpyne.py b/jade/xsdirpyne.py index 0ca81e35..f9cc7bea 100644 --- a/jade/xsdirpyne.py +++ b/jade/xsdirpyne.py @@ -139,22 +139,27 @@ def read(self): self.tables.append(table) # All tables have at least 7 attributes - table.name = words[0] - table.awr = float(words[1]) - table.filename = words[2] - table.access = words[3] - table.filetype = int(words[4]) - table.address = int(words[5]) - table.tablelength = int(words[6]) - - if len(words) > 7: - table.recordlength = int(words[7]) - if len(words) > 8: - table.entries = int(words[8]) - if len(words) > 9: - table.temperature = float(words[9]) - if len(words) > 10: - table.ptable = (words[10] == 'ptable') + try: + table.name = words[0] + table.awr = float(words[1]) + table.filename = words[2] + table.access = words[3] + table.filetype = int(words[4]) + table.address = int(words[5]) + table.tablelength = int(words[6]) + + if len(words) > 7: + table.recordlength = int(words[7]) + if len(words) > 8: + table.entries = int(words[8]) + if len(words) > 9: + table.temperature = float(words[9]) + if len(words) > 10: + table.ptable = (words[10] == 'ptable') + except ValueError: + problem_line = words[0] + print("Error reading line corresponding to {}, passing for now, please adjust XSDIR file".format(problem_line)) + pass def find_table(self, name, mode='default'): """Find all tables for a given ZIAD. diff --git a/templates/AtlasTemplate.docx b/templates/AtlasTemplate.docx deleted file mode 100644 index a05728979ac3736d0bc643eab6346567da8e3b54..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 162063 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

VD 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�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±5@U+f$~dx7u_D+6C)|)XIfZ4r>NG^i1 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 Date: Mon, 12 Feb 2024 17:03:38 +0000 Subject: [PATCH 8/9] error message suppresion in atlas generation --- jade/plotter.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/jade/plotter.py b/jade/plotter.py index 81a4d76c..ef6a7e06 100644 --- a/jade/plotter.py +++ b/jade/plotter.py @@ -38,7 +38,7 @@ # ============================================================================ # Specify parameters for plots # ============================================================================ -np.seterr(divide="ignore") # Suppressing divide by zero error in plots +np.seterr(divide="ignore", invalid="ignore") # Suppressing divide by zero error in plots DEFAULT_EXTENSION = ".png" SMALL_SIZE = 22 From c8a20baf007af18b7fcf121420335d267e33b6db Mon Sep 17 00:00:00 2001 From: Davide Laghi Date: Tue, 13 Feb 2024 12:26:47 +0100 Subject: [PATCH 9/9] Check availability of libs in libmanager in different codes Changes in main.py, gui.py, status.py come mainly from black formatter and a bit of type hints added. Main bulk of changes is in libmanager, where a check for real availability of the libraries has been repristinated and the .libraries attribute now contains different list for different codes. tests have been updated accordingly --- jade/gui.py | 436 +++++++++++++++++++++++---------------- jade/libmanager.py | 295 ++++++++++++++++---------- jade/main.py | 104 +++++----- jade/status.py | 110 +++++----- tests/libmanager_test.py | 141 +++++++------ 5 files changed, 627 insertions(+), 459 deletions(-) diff --git a/jade/gui.py b/jade/gui.py index 0ffe1161..fbcf3cdf 100644 --- a/jade/gui.py +++ b/jade/gui.py @@ -31,28 +31,37 @@ from jade.__version__ import __version__ from jade.status import EXP_TAG from tqdm import tqdm +from jade.main import Session -date = '10/05/2022' +date = "10/05/2022" version = __version__ -POWERED_BY = 'NIER, UNIBO, F4E, UKAEA' +POWERED_BY = "NIER, UNIBO, F4E, UKAEA" def clear_screen(): - if os.name == 'nt': - os.system('cls') + if os.name == "nt": + os.system("cls") else: - os.system('clear') + os.system("clear") -exit_text = '\nSession concluded normally \n' +exit_text = "\nSession concluded normally \n" -header = """ +header = ( + """ *********************************************** - Welcome to JADE """+version+""" + Welcome to JADE """ + + version + + """ A nuclear libraries V&V Test Suite - Release date: """+date+'\n' - -principal_menu = header+""" + Release date: """ + + date + + "\n" +) + +principal_menu = ( + header + + """ MAIN MENU Powered by {} @@ -79,7 +88,10 @@ def clear_screen(): ----------------------------------------------- * Exit (exit) -""".format(POWERED_BY) +""".format( + POWERED_BY + ) +) def mainloop(session): @@ -91,144 +103,164 @@ def mainloop(session): clear_screen() print(principal_menu) while True: - option = input(' Enter action: ') + option = input(" Enter action: ") - if option == 'comp': + if option == "comp": comploop(session) - elif option == 'exp': + elif option == "exp": exploop(session) - elif option == 'qual': + elif option == "qual": clear_screen() print(principal_menu) - print(' Currently not developed. Please select another option') + print(" Currently not developed. Please select another option") - elif option == 'post': + elif option == "post": pploop(session) - elif option == 'printlib': + elif option == "printlib": uty.print_libraries(session.lib_manager) - elif option == 'restore': + elif option == "restore": uty.restore_default_config(session) - elif option == 'trans': + elif option == "trans": newlib = session.lib_manager.select_lib() if newlib == "back": mainloop(session) if newlib == "exit": session.log.adjourn(exit_text) sys.exit() - inputfile = input(' MCNP input file: ') + inputfile = input(" MCNP input file: ") if newlib in session.lib_manager.libraries: ans = uty.translate_input(session, newlib, inputfile) if ans: - print(' Translation successfully completed!\n') - session.log.adjourn('file'+inputfile + - ' successfully translated to ' + newlib) + print(" Translation successfully completed!\n") + session.log.adjourn( + "file" + inputfile + " successfully translated to " + newlib + ) else: - print(''' + print( + """ Error: The file does not exist or can't be opened - ''') + """ + ) else: - print(''' + print( + """ Error: The selected library is not available. Check your available libraries using 'printlib' - ''') + """ + ) - elif option == 'printmat': - inputfile = input(' MCNP Input file of interest: ') + elif option == "printmat": + inputfile = input(" MCNP Input file of interest: ") ans = uty.print_material_info(session, inputfile) if ans: - print(' Material infos printed') + print(" Material infos printed") else: - print(''' + print( + """ Error: Either the input or output files do not exist or can't be opened - ''') + """ + ) - elif option == 'generate': - inputfile = uty.select_inputfile(' MCNP input file: ') + elif option == "generate": + inputfile = uty.select_inputfile(" MCNP input file: ") message = " Fraction type (either 'mass' or 'atom'): " - options = ['mass', 'atom'] + options = ["mass", "atom"] fraction_type = uty.input_with_options(message, options) - materials = input(' Source materials (e.g. m1-m10): ') - percentages = input(' Materials percentages (e.g. 0.1-0.9): ') + materials = input(" Source materials (e.g. m1-m10): ") + percentages = input(" Materials percentages (e.g. 0.1-0.9): ") lib = session.lib_manager.select_lib() if lib == "back": mainloop(session) if lib == "exit": session.log.adjourn(exit_text) sys.exit() - materials = materials.split('-') - percentages = percentages.split('-') + materials = materials.split("-") + percentages = percentages.split("-") if len(materials) == len(percentages): - ans = uty.generate_material(session, inputfile, - materials, percentages, lib, - fractiontype=fraction_type) + ans = uty.generate_material( + session, + inputfile, + materials, + percentages, + lib, + fractiontype=fraction_type, + ) if ans: - print(' Material generated') + print(" Material generated") else: - print(''' + print( + """ Error: Either the input or output files can't be opened - ''') + """ + ) else: - print(''' + print( + """ Error: The number of materials and percentages must be the same - ''') + """ + ) - elif option == 'switch': + elif option == "switch": # Select MCNP input - inputfile = uty.select_inputfile(' MCNP input file: ') + inputfile = uty.select_inputfile(" MCNP input file: ") # Select fraction type - options = ['mass', 'atom'] + options = ["mass", "atom"] message = " Fraction to switch to (either 'mass' or 'atom'): " fraction_type = uty.input_with_options(message, options) # Switch fraction ans = uty.switch_fractions(session, inputfile, fraction_type) if ans: - print(' Fractions have been switched') + print(" Fractions have been switched") else: - print(''' + print( + """ Error: - Either the input or output files can't be opened''') + Either the input or output files can't be opened""" + ) - elif option == 'acelib': + elif option == "acelib": uty.change_ACElib_suffix() - print('\n Suffix change was completed\n') + print("\n Suffix change was completed\n") - elif option == 'react': + elif option == "react": uty.get_reaction_file(session) - print('\n Reaction file has been dumped\n') + print("\n Reaction file has been dumped\n") - elif option == 'rmvruntpe': + elif option == "rmvruntpe": uty.clean_runtpe(session.path_run) - print('\n Runtpe files have been removed\n') + print("\n Runtpe files have been removed\n") - elif option == 'comparelib': + elif option == "comparelib": uty.print_XS_EXFOR(session) - elif option == 'exit': - session.log.adjourn('\nSession concluded normally \n') + elif option == "exit": + session.log.adjourn("\nSession concluded normally \n") sys.exit() else: clear_screen() print(principal_menu) - print(' Please enter a valid option!') + print(" Please enter a valid option!") -computational_menu = header+""" +computational_menu = ( + header + + """ COMPUTATIONAL BENCHMARK MENU Powered by {} @@ -239,10 +271,13 @@ def mainloop(session): * Continue assessment (continue) * Back to main menu (back) * Exit (exit) -""".format(POWERED_BY) +""".format( + POWERED_BY + ) +) -def comploop(session): +def comploop(session: Session): """ This handle the actions related to the computational benchmarck menu @@ -252,12 +287,12 @@ def comploop(session): clear_screen() print(computational_menu) while True: - option = input(' Enter action: ') + option = input(" Enter action: ") - if option == 'printlib': + if option == "printlib": uty.print_libraries(session.lib_manager) - elif option == 'assess': + elif option == "assess": # Select and check library lib = session.lib_manager.select_lib() if lib == "back": @@ -270,26 +305,31 @@ def comploop(session): comploop(session) if runoption == "exit": session.log.adjourn(exit_text) - sys.exit() + sys.exit() ans = session.state.check_override_run(lib, session) # If checks are ok perform assessment if ans: - # Logging - bartext = 'Computational benchmark execution started' + # Logging + bartext = "Computational benchmark execution started" session.log.bar_adjourn(bartext) - session.log.adjourn('Selected Library: '+lib, - spacing=False, time=True) - print(' ########################### COMPUTATIONAL BENCHMARKS EXECUTION ###########################\n') + session.log.adjourn( + "Selected Library: " + lib, spacing=False, time=True + ) + print( + " ########################### COMPUTATIONAL BENCHMARKS EXECUTION ###########################\n" + ) cmp.executeBenchmarksRoutines(session, lib, runoption) # Core function - print(' ####################### COMPUTATIONAL BENCHMARKS RUN ENDED ###############################\n') - t = 'Computational benchmark execution ended' + print( + " ####################### COMPUTATIONAL BENCHMARKS RUN ENDED ###############################\n" + ) + t = "Computational benchmark execution ended" session.log.bar_adjourn(t) else: clear_screen() print(computational_menu) - print(' Assessment cancelled.') + print(" Assessment cancelled.") - elif option == 'continue': + elif option == "continue": # Select and check library # Warning: this is done only for sphere test at the moment lib = session.lib_manager.select_lib() @@ -304,49 +344,55 @@ def comploop(session): unfinished = None if unfinished is None: - print(' The selected library was not assessed') + print(" The selected library was not assessed") elif len(unfinished) == 0: - print(' The assessment is already completed') + print(" The assessment is already completed") else: - print(' Completing sphere assessment:') - session.log.adjourn('Assessment of: '+lib+' started', - spacing=False, time=True) + print(" Completing sphere assessment:") + session.log.adjourn( + "Assessment of: " + lib + " started", spacing=False, time=True + ) flagOk = True for directory in tqdm(unfinished): path = os.path.join(motherdir, directory) - name = directory+'_' + name = directory + "_" - flag = testrun.Test._runMCNP('mcnp6', name, path, - cpu=session.conf.cpu) + flag = testrun.Test._runMCNP( + "mcnp6", name, path, cpu=session.conf.cpu + ) if flag: flagOk = False - session.log.adjourn(name + - ' reached timeout, eliminate folder') + session.log.adjourn(name + " reached timeout, eliminate folder") if not flagOk: - print(""" + print( + """ Some MCNP run reached timeout, they are listed in the log file. - Please remove their folders before attempting to postprocess the library""") + Please remove their folders before attempting to postprocess the library""" + ) - print(' Assessment completed') + print(" Assessment completed") - session.log.adjourn('Assessment of: '+lib+' completed', - spacing=True, time=True) + session.log.adjourn( + "Assessment of: " + lib + " completed", spacing=True, time=True + ) - elif option == 'back': + elif option == "back": mainloop(session) - elif option == 'exit': + elif option == "exit": session.log.adjourn(exit_text) sys.exit() else: clear_screen() print(computational_menu) - print(' Please enter a valid option!') + print(" Please enter a valid option!") -experimental_menu = header+""" +experimental_menu = ( + header + + """ EXPERIMENTAL BENCHMARK MENU Powered by {} @@ -357,7 +403,10 @@ def comploop(session): * Continue assessment (continue) * Back to main menu (back) * Exit (exit) -""".format(POWERED_BY) +""".format( + POWERED_BY + ) +) def exploop(session): @@ -370,12 +419,12 @@ def exploop(session): clear_screen() print(experimental_menu) while True: - option = input(' Enter action: ') + option = input(" Enter action: ") - if option == 'printlib': + if option == "printlib": uty.print_libraries(session.lib_manager) - elif option == 'assess': + elif option == "assess": # Update the configuration file session.conf.read_settings() # Select and check library @@ -392,53 +441,59 @@ def exploop(session): session.log.adjourn(exit_text) sys.exit() # it may happen that lib are two but only the first is the assessed - pieces = lib.split('-') + pieces = lib.split("-") if len(pieces) > 1: libtocheck = pieces[0] else: libtocheck = lib - ans = session.state.check_override_run(libtocheck, - session, exp=True) + ans = session.state.check_override_run(libtocheck, session, exp=True) # If checks are ok perform assessment if ans: # Logging runoption = session.conf.run_option(exp=True) - bartext = 'Experimental benchmark execution started' + bartext = "Experimental benchmark execution started" session.log.bar_adjourn(bartext) - session.log.adjourn('Selected Library: '+lib, - spacing=False, time=True) - print(' ########################### EXPERIMENTAL BENCHMARKS EXECUTION ###########################\n') + session.log.adjourn( + "Selected Library: " + lib, spacing=False, time=True + ) + print( + " ########################### EXPERIMENTAL BENCHMARKS EXECUTION ###########################\n" + ) # Core function cmp.executeBenchmarksRoutines(session, lib, runoption, exp=True) - print(' ####################### EXPERIMENTAL BENCHMARKS RUN ENDED ###############################\n') - t = 'Experimental benchmark execution ended' + print( + " ####################### EXPERIMENTAL BENCHMARKS RUN ENDED ###############################\n" + ) + t = "Experimental benchmark execution ended" session.log.bar_adjourn(t) else: clear_screen() print(computational_menu) - print(' Assessment canceled.') + print(" Assessment canceled.") - elif option == 'continue': + elif option == "continue": # Update the configuration file session.conf.read_settings() clear_screen() print(principal_menu) - print(' Currently not developed. Please select another option') + print(" Currently not developed. Please select another option") - elif option == 'back': + elif option == "back": mainloop(session) - elif option == 'exit': + elif option == "exit": session.log.adjourn(exit_text) sys.exit() else: clear_screen() print(computational_menu) - print(' Please enter a valid option!') + print(" Please enter a valid option!") -pp_menu = header+""" +pp_menu = ( + header + + """ POST PROCESSING MENU Powered by {} @@ -450,7 +505,10 @@ def exploop(session): * Compare Vs Experiments (compexp) * Back to main menu (back) * Exit (exit) -""".format(POWERED_BY) +""".format( + POWERED_BY + ) +) def pploop(session): @@ -463,13 +521,13 @@ def pploop(session): clear_screen() print(pp_menu) while True: - option = input(' Enter action: ') + option = input(" Enter action: ") - if option == 'printlib': + if option == "printlib": lib_tested = list(session.state.run_tree.keys()) print(lib_tested) - elif option == 'pp': + elif option == "pp": # Update the configuration file session.conf.read_settings() # Select and check library @@ -482,20 +540,22 @@ def pploop(session): if ans: lib = to_single_pp[0] # Check active tests - #to_perform = session.check_active_tests('Post-Processing') + # to_perform = session.check_active_tests('Post-Processing') # For the moment no pp is foreseen for experimental benchmarks # to_perf_exp = session.check_active_tests('Post-Processing', # exp=True) # to_perform.extend(to_perf_exp) # Logging - bartext = 'Post-Processing started' + bartext = "Post-Processing started" session.log.bar_adjourn(bartext) - session.log.adjourn('Selected Library: '+lib, spacing=False) - print('\n ########################### POST-PROCESSING STARTED ###########################\n') + session.log.adjourn("Selected Library: " + lib, spacing=False) + print( + "\n ########################### POST-PROCESSING STARTED ###########################\n" + ) # Core function - pp.postprocessBenchmark(session, lib) - #for testname in to_perform: + pp.postprocessBenchmark(session, lib) + # for testname in to_perform: # try: # pp.postprocessBenchmark(session, lib, testname) # except PermissionError as e: @@ -504,11 +564,13 @@ def pploop(session): # print(' '+str(e)) # print(' Please close all excel/word files and retry') # continue - print('\n ######################### POST-PROCESSING ENDED ###############################\n') - t = 'Post-Processing completed' + print( + "\n ######################### POST-PROCESSING ENDED ###############################\n" + ) + t = "Post-Processing completed" session.log.bar_adjourn(t, spacing=False) - elif option == 'compare': + elif option == "compare": # Update the configuration file session.conf.read_settings() @@ -517,30 +579,36 @@ def pploop(session): if ans: # Logging - bartext = 'Comparison Post-Processing started' + bartext = "Comparison Post-Processing started" session.log.bar_adjourn(bartext) - session.log.adjourn('Selected Library: '+lib_input, - spacing=True) - print('\n ########################### COMPARISON STARTED ###########################\n') + session.log.adjourn("Selected Library: " + lib_input, spacing=True) + print( + "\n ########################### COMPARISON STARTED ###########################\n" + ) # Check active tests - to_perform = session.check_active_tests('Post-Processing') + to_perform = session.check_active_tests("Post-Processing") # Execute single pp for lib in to_single_pp: - print('to single pp', to_single_pp) + print("to single pp", to_single_pp) for testname in to_perform: - print("to_perform",to_perform) + print("to_perform", to_perform) try: - print(' Single PP of library '+lib+' required') + print(" Single PP of library " + lib + " required") pp.postprocessBenchmark(session, lib) - session.log.adjourn(""" -Additional Post-Processing of library:"""+lib+' completed\n', spacing=False) + session.log.adjourn( + """ +Additional Post-Processing of library:""" + + lib + + " completed\n", + spacing=False, + ) except PermissionError as e: clear_screen() print(pp_menu) - print(' '+str(e)) - print(' Please close all excel/word files and retry') + print(" " + str(e)) + print(" Please close all excel/word files and retry") continue # Execute Comparison @@ -550,72 +618,78 @@ def pploop(session): except PermissionError as e: clear_screen() print(pp_menu) - print(' '+str(e)) - print(' Please close all excel/word files and retry') + print(" " + str(e)) + print(" Please close all excel/word files and retry") continue - print('\n ######################### COMPARISON ENDED ###############################\n') - t = 'Post-Processing completed' + print( + "\n ######################### COMPARISON ENDED ###############################\n" + ) + t = "Post-Processing completed" session.log.bar_adjourn(t, spacing=False) - elif option == 'compexp': + elif option == "compexp": # Update the configuration file session.conf.read_settings() # Select and check library - ans, to_single_pp, lib_input = session.state.check_override_pp(session, exp=True) + ans, to_single_pp, lib_input = session.state.check_override_pp( + session, exp=True + ) if ans: # Logging - bartext = 'Comparison Post-Processing started' + bartext = "Comparison Post-Processing started" session.log.bar_adjourn(bartext) - session.log.adjourn('Selected Library: '+lib_input, - spacing=True) - print('\n ########################### COMPARISON STARTED ###########################\n') + session.log.adjourn("Selected Library: " + lib_input, spacing=True) + print( + "\n ########################### COMPARISON STARTED ###########################\n" + ) # Check active tests - to_perform = session.check_active_tests('Post-Processing', - exp=True) - -# # Execut single pp -# for lib in to_single_pp: -# for testname in to_perform: -# try: -# print(' Single PP of library '+lib+' required') -# pp.postprocessBenchmark(session, lib, testname) -# session.log.adjourn(""" -# Additional Post-Processing of library:"""+lib+' completed\n', spacing=False) -# except PermissionError as e: -# clear_screen() -# print(pp_menu) -# print(' '+str(e)) -# print(' Please close all excel/word files and retry') -# continue + to_perform = session.check_active_tests("Post-Processing", exp=True) + + # # Execut single pp + # for lib in to_single_pp: + # for testname in to_perform: + # try: + # print(' Single PP of library '+lib+' required') + # pp.postprocessBenchmark(session, lib, testname) + # session.log.adjourn(""" + # Additional Post-Processing of library:"""+lib+' completed\n', spacing=False) + # except PermissionError as e: + # clear_screen() + # print(pp_menu) + # print(' '+str(e)) + # print(' Please close all excel/word files and retry') + # continue # Execute Comparison - lib_input = EXP_TAG+'-'+lib_input # Insert the exp tag + lib_input = EXP_TAG + "-" + lib_input # Insert the exp tag for testname in to_perform: try: - pp.compareBenchmark(session, lib_input, testname, exp = True) + pp.compareBenchmark(session, lib_input, testname, exp=True) except PermissionError as e: clear_screen() print(pp_menu) - print(' '+str(e)) - print(' Please close all excel/word files and retry') + print(" " + str(e)) + print(" Please close all excel/word files and retry") continue - print('\n ######################### COMPARISON ENDED ###############################\n') - t = 'Post-Processing completed' + print( + "\n ######################### COMPARISON ENDED ###############################\n" + ) + t = "Post-Processing completed" session.log.bar_adjourn(t, spacing=False) - elif option == 'back': + elif option == "back": mainloop(session) - elif option == 'exit': + elif option == "exit": session.log.adjourn(exit_text) sys.exit() else: clear_screen() print(pp_menu) - print(' Please enter a valid option!') + print(" Please enter a valid option!") diff --git a/jade/libmanager.py b/jade/libmanager.py index f995c341..f7d01e53 100644 --- a/jade/libmanager.py +++ b/jade/libmanager.py @@ -27,7 +27,9 @@ import os import re import sys +import logging import warnings +import numpy as np import jade.acepyne as ace import jade.xsdirpyne as xs @@ -36,28 +38,28 @@ from jade.exceptions import fatal_exception # colors -CRED = '\033[91m' -CEND = '\033[0m' +CRED = "\033[91m" +CEND = "\033[0m" -MSG_DEFLIB = ' The Default library {} was used for zaid {}' +MSG_DEFLIB = " The Default library {} was used for zaid {}" class IsotopeDataParser: def __init__(self, isotopes_file: os.PathLike) -> None: # load the natural abundance file abundances = pd.read_csv(isotopes_file, skiprows=2) - abundances['idx'] = abundances['idx'].astype(str) - abundances.set_index('idx', inplace=True) + abundances["idx"] = abundances["idx"].astype(str) + abundances.set_index("idx", inplace=True) self.isotopes = abundances def get_formulazaid(self, formula): match = re.match(r"([a-z]+)([0-9]+)", formula, re.I) parts = match.groups() E, A = parts[0], int(parts[1]) - newiso = self.isotopes[self.isotopes['E'] == E] - Z = newiso['Z'].values[0] - zaid = '{0}{1:0>3}'.format(Z, A) + newiso = self.isotopes[self.isotopes["E"] == E] + Z = newiso["Z"].values[0] + zaid = "{0}{1:0>3}".format(Z, A) return zaid @@ -65,11 +67,15 @@ class LibManager: # def __init__(self, xsdir_file, defaultlib='81c', activationfile=None, # isotopes_file=None): - def __init__(self, lib_df: pd.DataFrame, defaultlib: str = None, - activationfile: os.PathLike = None, - isotopes_file: os.PathLike = None): + def __init__( + self, + lib_df: pd.DataFrame, + defaultlib: str = None, + activationfile: os.PathLike = None, + isotopes_file: os.PathLike = None, + ) -> None: """ - Object dealing with all complex operations that involves nuclear data + Object dealing with all complex operations that involves nuclear data. Parameters ---------- @@ -85,13 +91,32 @@ def __init__(self, lib_df: pd.DataFrame, defaultlib: str = None, path to the isotopes files. If None (default) the file is searched in the current directory. + Attributes + ---------- + isotope_parser : IsotopeDataParser + object dealing with the isotopes data. + isotopes : pd.DataFrame + contains the isotopes data. + defaultlib : str + lib suffix to be used as default in translation operations. + data : dict[str, dict[str, Union[Xsdir, OpenMCXsdir, SerpentXsdir]]] + contains the libraries data. first level keys are the codes, second + level keys are the library suffixes. ultimate value is the xsdir + object. + codes : list + list of codes available. + libraries : dict[str, list[str]] + contains the libraries available for each code. + reactions : dict[str, pd.DataFrame] + contains the reactions data for the different activation libraries. + Returns ------- None. """ if isotopes_file is None: - isotopes_file = os.path.join('resources', 'Isotopes.txt') + isotopes_file = os.path.join("resources", "Isotopes.txt") self.isotope_parser = IsotopeDataParser(isotopes_file) self.isotopes = self.isotope_parser.isotopes @@ -103,56 +128,96 @@ def __init__(self, lib_df: pd.DataFrame, defaultlib: str = None, lib_df.columns = new_columns if defaultlib is None: - self.defaultlib = lib_df[lib_df['default'] == 'yes']['suffix'].values[0] + self.defaultlib = lib_df[lib_df["default"] == "yes"]["suffix"].values[0] else: self.defaultlib = defaultlib self.data = {} self.codes = [] - lib_df.set_index('suffix', inplace=True) + lib_df.set_index("suffix", inplace=True) # Initilize the Xsdir object # self.XS = xs.Xsdir(xsdir_file) + + # this block of code needs to check the availability of the libraries. + # Only libraries specified in the config file are checked, if paths + # for the libraries are left empty, the library is not not checked and + # it is not registered as available. If the path is not empty but + # library is not found, a warning is raised, choice for interrupting the + # session is left to the user. for code in lib_df.columns[2:]: code = code.lower() self.codes.append(code) self.data[code] = {} for library, row in lib_df.iterrows(): path = row[code] - if path != '': - if not os.path.exists(path): - fatal_exception(path + ' does not exist') - - if len(path) != 0 : - - if code == 'mcnp': - self.data[code][library] = Xsdir(path) - - elif code == 'openmc': - self.data[code][library] = OpenMCXsdir(path, self, library) - - elif code == 'serpent': - self.data[code][library] = SerpentXsdir(path) - - elif code == 'd1s': - self.data[code][library] = Xsdir(path) - + # if the path is empty just ignore it + if path is None or path == "": + logging.info("No path for %s library", library) + continue + + # if the path is not empty, check if the file exists + # and if it does not, raise a warning since it may not be the + # intended behaviour by the user + if not os.path.exists(path): + logging.warning( + "Library %s for code %s not found at %s", library, code, path + ) + # fatal_exception(path + " does not exist") + + if code == "mcnp": + xsdir = Xsdir(path) + # verify that the library is actually in the xsdir + available_libs = set(np.array(xsdir.tablenames)[:, 1]) + if library in available_libs: + self.data[code][library] = xsdir + else: + logging.warning( + "Library %s not present in XSDIR file: %s", library, path + ) + + elif code == "openmc": + self.data[code][library] = OpenMCXsdir(path, self, library) + + elif code == "serpent": + self.data[code][library] = SerpentXsdir(path) + + elif code == "d1s": + xsdir = Xsdir(path) + # verify that the library is actually in the xsdir + available_libs = set(np.array(xsdir.tablenames)[:, 1]) + if library in available_libs: + self.data[code][library] = xsdir else: - raise ValueError('{} code not implemented'.format(code)) + logging.warning( + "Library %s not present in XSDIR file: %s", library, path + ) + + else: + raise ValueError(f"{code} code not implemented") # Identify different libraries installed. This is done checking H # libraries = self.check4zaid('1001') # libraries.extend(self.check4zaid('1000')) # photons - """ Legacy library definition changed """ - """ - libraries = [] - for table in self.XS: - lib = table.name.split('.')[1] - if lib not in libraries: - libraries.append(lib) + # """ Legacy library definition changed """ + # """ + # libraries = [] + # for table in self.XS: + # lib = table.name.split('.')[1] + # if lib not in libraries: + # libraries.append(lib) + + # self.libraries = libraries + # """ + + # libraries have now been checked at the source, they may be different + # for each code + libraries = {} + for key, value in self.data.items(): + libraries[key] = [] + for lib, _ in value.items(): + libraries[key].append(lib) self.libraries = libraries - """ - self.libraries = list(lib_df.index) # Load the activation reaction data if available if activationfile is not None: @@ -167,7 +232,7 @@ def __init__(self, lib_df: pd.DataFrame, defaultlib: str = None, self.reactions = reactions - def check4zaid(self, zaid: str, code: str = 'mcnp'): + def check4zaid(self, zaid: str, code: str = "mcnp"): # Needs fixing """ Check which libraries are available for the selected zaid and return it @@ -186,13 +251,13 @@ def check4zaid(self, zaid: str, code: str = 'mcnp'): """ libraries = [] - if code != 'openmc': - for lib in self.libraries: + if code != "openmc": + for lib in self.libraries[code]: xsdir = self.data[code][lib] - if lib in xsdir.find_table(zaid, mode='default-fast'): + if lib in xsdir.find_table(zaid, mode="default-fast"): libraries.append(lib) else: - raise NotImplementedError('{} not implemented yet'.format(code)) + raise NotImplementedError("{} not implemented yet".format(code)) return libraries @@ -205,7 +270,7 @@ def check4zaid(self, zaid: str, code: str = 'mcnp'): # else: # return False - def convertZaid(self, zaid, lib, code: str = 'mcnp'): + def convertZaid(self, zaid: str, lib: str, code: str = "mcnp"): # Needs fixing """ This methods will convert a zaid into the requested library @@ -238,36 +303,43 @@ def convertZaid(self, zaid, lib, code: str = 'mcnp'): """ # Check if library is available in Xsdir - if lib not in self.libraries: - raise ValueError('Library '+lib+' is not available in xsdir file') + if lib not in self.libraries[code]: + raise ValueError("Library " + lib + " is not available in xsdir file") zaidlibs = self.check4zaid(zaid, code) - if code in ['mcnp', 'd1s', 'serpent']: + if code in ["mcnp", "d1s", "serpent"]: XS = self.data[code][lib] # Natural zaid - if zaid[-3:] == '000': + if zaid[-3:] == "000": # Check if zaid has natural info - if XS.find_table(zaid+'.'+lib, mode='exact'): + if XS.find_table(zaid + "." + lib, mode="exact"): translation = {zaid: (lib, 1, 1)} # mass not important else: # Has to be expanded translation = {} - reduced = self.isotopes[self.isotopes['Z'] == int(zaid[:-3])] + reduced = self.isotopes[self.isotopes["Z"] == int(zaid[:-3])] for idx, row in reduced.iterrows(): # zaid availability must be checked - if XS.find_table(idx+'.'+lib, mode='exact'): + if XS.find_table(idx + "." + lib, mode="exact"): newlib = lib - elif self.data[code][self.defaultlib].find_table(idx+'.'+self.defaultlib, - mode='exact'): + elif self.data[code][self.defaultlib].find_table( + idx + "." + self.defaultlib, mode="exact" + ): warnings.warn(MSG_DEFLIB.format(self.defaultlib, zaid)) newlib = self.defaultlib else: - raise ValueError('No available translation for zaid :' + - zaid+'It is needed for natural zaid expansion.') - - translation[idx] = (newlib, row['Mean value'], - row['Atomic Mass']) + raise ValueError( + "No available translation for zaid :" + + zaid + + "It is needed for natural zaid expansion." + ) + + translation[idx] = ( + newlib, + row["Mean value"], + row["Atomic Mass"], + ) # 1to1 elif lib in zaidlibs: translation = {zaid: (lib, 1, 1)} # mass not important @@ -275,22 +347,23 @@ def convertZaid(self, zaid, lib, code: str = 'mcnp'): # No possible correspondence, natural or default lib has to be used else: # Check if the natural zaid is available - natzaid = zaid[:-3]+'000' - if XS.find_table(natzaid+'.'+lib, mode='exact'): + natzaid = zaid[:-3] + "000" + if XS.find_table(natzaid + "." + lib, mode="exact"): translation = {natzaid: (lib, 1, 1)} # mass not important # Check if default lib is available - elif self.data[code][self.defaultlib].find_table(zaid+'.'+self.defaultlib, mode='exact'): + elif self.data[code][self.defaultlib].find_table( + zaid + "." + self.defaultlib, mode="exact" + ): warnings.warn(MSG_DEFLIB.format(self.defaultlib, zaid)) translation = {zaid: (self.defaultlib, 1, 1)} # mass not imp else: - raise ValueError('No available translation for zaid :' + - zaid) + raise ValueError("No available translation for zaid :" + zaid) else: - raise ValueError('Translation not required for code '+code) + raise ValueError("Translation not required for code " + code) return translation - def get_libzaids(self, lib: str, code: str = 'mcnp'): + def get_libzaids(self, lib: str, code: str = "mcnp"): # Needs fixing """ Given a library, returns all zaids available @@ -314,11 +387,11 @@ def get_libzaids(self, lib: str, code: str = 'mcnp'): if isinstance(XS, xs.Xsdir): for table in XS.find_zaids(lib): - zaid = table.name.split('.')[0] + zaid = table.name.split(".")[0] if zaid not in zaids: zaids.append(zaid) else: - raise NotImplementedError('{} code is not yet implemented'.format(code)) + raise NotImplementedError("{} code is not yet implemented".format(code)) return zaids @@ -344,7 +417,7 @@ def get_zaidname(self, zaid): """ if type(zaid) == str: - splitted = zaid.split('.') + splitted = zaid.split(".") elem = splitted[0][:-3] i = int(elem) isotope = splitted[0][-3:] @@ -353,11 +426,11 @@ def get_zaidname(self, zaid): i = int(zaid.element) isotope = zaid.isotope - newiso = self.isotopes.set_index('Z') - newiso = newiso.loc[~newiso.index.duplicated(keep='first')] + newiso = self.isotopes.set_index("Z") + newiso = newiso.loc[~newiso.index.duplicated(keep="first")] - name = newiso['Element'].loc[i] - formula = newiso['E'].loc[i]+'-'+str(int(isotope)) + name = newiso["Element"].loc[i] + formula = newiso["E"].loc[i] + "-" + str(int(isotope)) return name, formula @@ -377,24 +450,24 @@ def get_zaidnum(self, zaidformula): """ # get the table and drop the duplicates - newiso = self.isotopes.set_index(['E']) - newiso = newiso.loc[~newiso.index.duplicated(keep='first')] + newiso = self.isotopes.set_index(["E"]) + newiso = newiso.loc[~newiso.index.duplicated(keep="first")] # split the name - patnum = re.compile(r'\d+') - patname = re.compile(r'[a-zA-Z]+') + patnum = re.compile(r"\d+") + patname = re.compile(r"[a-zA-Z]+") try: num = patnum.search(zaidformula).group() name = patname.search(zaidformula).group() except AttributeError: - raise ValueError('No correspondent zaid found for '+zaidformula) + raise ValueError("No correspondent zaid found for " + zaidformula) - atomnumber = newiso.loc[name, 'Z'] + atomnumber = newiso.loc[name, "Z"] zaidnum = "{}{:03d}".format(atomnumber, int(num)) return zaidnum - def select_lib(self): + def select_lib(self, code: str = "mcnp") -> str: """ Prompt an library input selection with Xsdir availabilty check @@ -402,54 +475,60 @@ def select_lib(self): ------- lib : str Library to assess. + code: str, optional + code for which the library is selected. default is MCNP """ - error = CRED+''' + error = ( + CRED + + """ Error: {} The selected library is not available. - '''+CEND + """ + + CEND + ) # Add a counter to avoid falling in an endless loop i = 0 while True: i += 1 - lib = input(' Select library (e.g. 31c or 99c-31c): ') - if lib in self.libraries: + lib = input(" Select library (e.g. 31c or 99c-31c): ") + if lib in self.libraries[code]: break - elif lib[0] == '{': + elif lib[0] == "{": libs = json.loads(lib) # all libraries should be available tocheck = list(libs.values()) tocheck.extend(list(libs.keys())) flag = True for val in tocheck: - if val not in self.libraries: + if val not in self.libraries[code]: print(error.format(val)) flag = False if flag: break - elif '-' in lib: - libs = lib.split('-') + elif "-" in lib: + libs = lib.split("-") flag = True for val in libs: - if val not in self.libraries: + if val not in self.libraries[code]: print(error.format(val)) flag = False if flag: break - - elif lib == 'back': + + elif lib == "back": break - + elif lib == "exit": - break + break else: print(error.format(lib)) if i > 10: - raise ValueError('Too many wrong inputs') + raise ValueError("Too many wrong inputs") return lib def get_zaid_mass(self, zaid): @@ -468,13 +547,13 @@ def get_zaid_mass(self, zaid): """ try: - m = self.isotopes['Atomic Mass'].loc[zaid.element+zaid.isotope] + m = self.isotopes["Atomic Mass"].loc[zaid.element + zaid.isotope] except KeyError: # It means that it is a natural zaid # For a natural zaid the natural abundance mass is used df = self.isotopes.reset_index() - df['Partial mass'] = df['Atomic Mass']*df['Mean value'] - masked = df.set_index('Z').loc[int(zaid.element)] - m = masked['Partial mass'].sum() + df["Partial mass"] = df["Atomic Mass"] * df["Mean value"] + masked = df.set_index("Z").loc[int(zaid.element)] + m = masked["Partial mass"].sum() return float(m) @@ -497,22 +576,22 @@ def get_reactions(self, lib, parent): """ reactions = [] try: - df = self.reactions[lib].set_index('Parent') + df = self.reactions[lib].set_index("Parent") isotopename, formula = self.get_zaidname(parent) - formulazaid = formula.replace('-', '') # eliminate the '-' + formulazaid = formula.replace("-", "") # eliminate the '-' # collect and provide as tuples subset = df.loc[formulazaid] try: for _, row in subset.iterrows(): - MT = str(int(row['MT'])) - daughter = row['Daughter'] + MT = str(int(row["MT"])) + daughter = row["Daughter"] daughter = self.get_zaidnum(daughter) reactions.append((MT, daughter)) except AttributeError: # then is not a DF but a Series - MT = str(int(subset['MT'])) - daughter = subset['Daughter'] + MT = str(int(subset["MT"])) + daughter = subset["Daughter"] daughter = self.get_zaidnum(daughter) reactions.append((MT, daughter)) diff --git a/jade/main.py b/jade/main.py index b20fe906..f9f949e8 100644 --- a/jade/main.py +++ b/jade/main.py @@ -52,7 +52,7 @@ class Session: - def __init__(self): + def __init__(self) -> None: """ This object represent a JADE session. All "environment" variables are initialized @@ -64,7 +64,7 @@ def __init__(self): """ self.initialize() - def initialize(self): + def initialize(self) -> None: """ Initialize JADE session: - folders structure is created if absent @@ -82,45 +82,54 @@ def initialize(self): jade_root = os.getcwd() if os.path.dirname(code_root) in jade_root: - fatal_exception('Cannot initialise JADE in Code directory') - - self.path_default_settings = os.path.join(code_root, 'default_settings') - self.path_templates = os.path.join(code_root, 'templates') + fatal_exception("Cannot initialise JADE in Code directory") + + self.path_default_settings = os.path.join(code_root, "default_settings") + self.path_templates = os.path.join(code_root, "templates") # --- INITIALIZATION --- # --- Create/memorize JADE folder structure --- # Future implementation - self.path_quality = os.path.join(jade_root, 'Quality') + self.path_quality = os.path.join(jade_root, "Quality") # Test level 1 - self.path_test = os.path.join(jade_root, 'Tests') + self.path_test = os.path.join(jade_root, "Tests") # Test level 2 - self.path_run = os.path.join(self.path_test, 'Simulations') - self.path_pp = os.path.join(self.path_test, 'Post-Processing') + self.path_run = os.path.join(self.path_test, "Simulations") + self.path_pp = os.path.join(self.path_test, "Post-Processing") # Test level 3 - self.path_single = os.path.join(self.path_pp, 'Single_Libraries') - self.path_comparison = os.path.join(self.path_pp, 'Comparisons') + self.path_single = os.path.join(self.path_pp, "Single_Libraries") + self.path_comparison = os.path.join(self.path_pp, "Comparisons") # Utilities - self.path_uti = os.path.join(jade_root, 'Utilities') - self.path_logs = os.path.join(jade_root, 'Utilities', 'Log_Files') - self.path_test_install = os.path.join(jade_root, 'Utilities', - 'Installation_Test') - - keypaths = [self.path_quality, self.path_test, - self.path_run, self.path_pp, self.path_uti, - self.path_single, self.path_comparison, self.path_logs, - self.path_test_install] + self.path_uti = os.path.join(jade_root, "Utilities") + self.path_logs = os.path.join(jade_root, "Utilities", "Log_Files") + self.path_test_install = os.path.join( + jade_root, "Utilities", "Installation_Test" + ) + + keypaths = [ + self.path_quality, + self.path_test, + self.path_run, + self.path_pp, + self.path_uti, + self.path_single, + self.path_comparison, + self.path_logs, + self.path_test_install, + ] for path in keypaths: if not os.path.exists(path): os.mkdir(path) # --This paths must exist or are created at the first initialization-- # Configuration - self.path_cnf = os.path.join(jade_root, 'Configuration', - 'Benchmarks_Configuration') + self.path_cnf = os.path.join( + jade_root, "Configuration", "Benchmarks_Configuration" + ) # Experimental results - self.path_exp_res = os.path.join(jade_root, 'Experimental_Results') + self.path_exp_res = os.path.join(jade_root, "Experimental_Results") # Benchmark inputs - self.path_inputs = os.path.join(jade_root, 'Benchmarks_Inputs') + self.path_inputs = os.path.join(jade_root, "Benchmarks_Inputs") # Copy default settings if it is the first initialization if not os.path.exists(self.path_cnf): @@ -129,32 +138,34 @@ def initialize(self): shutil.copytree(files, os.path.dirname(self.path_cnf)) # Copy files into benchmark inputs folder - files = os.path.join(code_root, 'install_files', 'Benchmarks_Inputs') + files = os.path.join(code_root, "install_files", "Benchmarks_Inputs") shutil.copytree(files, self.path_inputs) # Copy experimental results folder - files = os.path.join(code_root, 'install_files', 'Experimental_Results') + files = os.path.join(code_root, "install_files", "Experimental_Results") shutil.copytree(files, self.path_exp_res) # the application needs to be closed sys.exit() # Read global configuration file. All vital variables are stored here - self.conf = cnf.Configuration(os.path.join(jade_root, 'Configuration', - 'Config.xlsx')) + self.conf = cnf.Configuration( + os.path.join(jade_root, "Configuration", "Config.xlsx") + ) # --- Create the session LOG --- - log = os.path.join(self.path_logs, - 'Log '+time.ctime().replace(':', '-')+'.txt') + log = os.path.join( + self.path_logs, "Log " + time.ctime().replace(":", "-") + ".txt" + ) self.log = cnf.Log(log) # --- Create the library manager --- - #dl = self.conf.default_lib - activationfile = os.path.join(jade_root, 'Configuration', 'Activation.xlsx') - isotopes_file = os.path.join(code_root, 'resources', 'Isotopes.txt') - self.lib_manager = libmanager.LibManager(self.conf.lib, - activationfile=activationfile, - isotopes_file=isotopes_file) + # dl = self.conf.default_lib + activationfile = os.path.join(jade_root, "Configuration", "Activation.xlsx") + isotopes_file = os.path.join(code_root, "resources", "Isotopes.txt") + self.lib_manager = libmanager.LibManager( + self.conf.lib, activationfile=activationfile, isotopes_file=isotopes_file + ) # --- Initialize status --- self.state = status.Status(self) @@ -187,11 +198,11 @@ def check_active_tests(self, action, exp=False): to_perform = [] for idx, row in config.iterrows(): - filename = str(row['Folder Name']) - testname = filename.split('.')[0] + filename = str(row["Folder Name"]) + testname = filename.split(".")[0] pp = row[action] - if pp is True or pp == 'True' or pp == 'true': + if pp is True or pp == "True" or pp == "true": to_perform.append(testname) return to_perform @@ -217,13 +228,12 @@ def restore_default_settings(self): def main(): # Module having problem with log(0) for tick position in graphs - warnings.filterwarnings('ignore', - r'invalid value encountered in double_scalars') - warnings.filterwarnings('ignore', - r'overflow encountered in power') - warnings.filterwarnings('ignore', module=r'plotter') - warnings.filterwarnings('ignore', - message=r'Warning: converting a masked element to nan.') + warnings.filterwarnings("ignore", r"invalid value encountered in double_scalars") + warnings.filterwarnings("ignore", r"overflow encountered in power") + warnings.filterwarnings("ignore", module=r"plotter") + warnings.filterwarnings( + "ignore", message=r"Warning: converting a masked element to nan." + ) session = Session() gui.mainloop(session) diff --git a/jade/status.py b/jade/status.py index efc7c666..006b138d 100644 --- a/jade/status.py +++ b/jade/status.py @@ -21,28 +21,32 @@ You should have received a copy of the GNU General Public License along with JADE. If not, see . """ +from __future__ import annotations import os import re +from jade.main import Session + MULTI_TEST = [ - 'Sphere', - 'Oktavian', - 'SphereSDDR', - 'FNG', - 'Tiara-BC', - 'Tiara-BS', - 'Tiara-FC', - 'FNS-TOF', - 'FNG-BKT', - 'FNG-W', - 'ASPIS-Fe88', - 'TUD-Fe', - 'TUD-W'] -EXP_TAG = 'Exp' - - -class Status(): - def __init__(self, session): + "Sphere", + "Oktavian", + "SphereSDDR", + "FNG", + "Tiara-BC", + "Tiara-BS", + "Tiara-FC", + "FNS-TOF", + "FNG-BKT", + "FNG-W", + "ASPIS-Fe88", + "TUD-Fe", + "TUD-W", +] +EXP_TAG = "Exp" + + +class Status: + def __init__(self, session: Session) -> None: """ Stores the state of the JADE runs and post-processing. @@ -70,14 +74,14 @@ def __init__(self, session): self.comparison_tree, self.single_tree = self.update_pp_status() # Updated by S. Bradnam to include new level, code, between test and zaid. - def update_run_status(self): + def update_run_status(self) -> dict: """ Read/Update the run tree status. All files produced by runs are registered Returns ------- - libraries : dic + libraries : dict dictionary of dictionaries representing the run tree """ @@ -115,17 +119,17 @@ def update_run_status(self): return libraries # Updated by S. Bradnam, UKAEA to include new level, code. - def update_pp_status(self) -> tuple: + def update_pp_status(self) -> tuple[dict, dict]: """ Read/Update the post processing tree status. All files produced by post processing registered Returns ------- - comparison_tree : dic + comparison_tree : dict Dictionary registering all test post processed for each comparison of libraries. - single_tree : dic + single_tree : dict Dictionary registering all test post processed performed for single libraries. @@ -160,7 +164,7 @@ def update_pp_status(self) -> tuple: return comparison_tree, single_tree - def get_path(self, tree: str, itinerary: list) -> str: + def get_path(self, tree: str, itinerary: list[str]) -> str | os.PathLike: """ Get the resulting path of an itinery on one tree @@ -168,7 +172,7 @@ def get_path(self, tree: str, itinerary: list) -> str: ---------- tree : str Either 'comparison', 'single', or 'run'. - itinerary : list + itinerary : list[str] list of strings representing the step to take inside the tree. Raises @@ -178,7 +182,7 @@ def get_path(self, tree: str, itinerary: list) -> str: Returns ------- - cp : str/path + cp : str | os.PathLike path to final step. """ @@ -200,7 +204,9 @@ def get_path(self, tree: str, itinerary: list) -> str: return cp - def get_unfinished_zaids(self, lib) -> tuple: + def get_unfinished_zaids( + self, lib: str + ) -> tuple[dict[str, list], str | os.PathLike] | None: """ Identify zaids to run for rerun or continuation purposes @@ -211,8 +217,10 @@ def get_unfinished_zaids(self, lib) -> tuple: Returns ------- - unfinished : list - zaids/typical materials not run. + unfinished : dict[str, list] + list of zaids/typical materials not run for each code. + motherdir : str | os.PathLike + root of test folder. """ @@ -236,12 +244,12 @@ def get_unfinished_zaids(self, lib) -> tuple: return unfinished, motherdir - def check_test_run(self, files: list, code: str) -> bool: + def check_test_run(self, files: list[str], code: str) -> bool: """Check if a test was run successfully for the specified code. Parameters ---------- - files : list + files : list[str] filenames inside the test folder code : str Transport code @@ -261,13 +269,13 @@ def check_test_run(self, files: list, code: str) -> bool: return flag_run_test - def _check_test_mcnp(self, files: list) -> bool: + def _check_test_mcnp(self, files: list[str]) -> bool: """ Check if a test has been run Parameters ---------- - files : list + files : list[str] file names inside test folder. Returns @@ -285,12 +293,12 @@ def _check_test_mcnp(self, files: list) -> bool: return flag_run_test - def _check_test_serpent(self, files: list) -> bool: + def _check_test_serpent(self, files: list[str]) -> bool: """Checks to see if Serpent simualtion has been run. Parameters ---------- - files : list + files : list[str] file names inside test folder. Returns @@ -306,12 +314,12 @@ def _check_test_serpent(self, files: list) -> bool: flag_run_test = True return flag_run_test - def _check_test_openmc(self, files: list) -> bool: + def _check_test_openmc(self, files: list[str]) -> bool: """Checks to see if OpenMC simualtion has been run. Parameters ---------- - files : list + files : list[str] file names inside test folder. Returns @@ -328,7 +336,7 @@ def _check_test_openmc(self, files: list) -> bool: return flag_run_test # TODO checking for multiple codes - def check_override_run(self, lib, session, exp=False): + def check_override_run(self, lib: str, session: Session, exp: bool = False) -> bool: """ Check status of the requested run. If overridden is required permission is requested to the user @@ -395,12 +403,7 @@ def check_override_run(self, lib, session, exp=False): return ans - def check_lib_run( - self, - lib, - session, - config_option="Run", - exp=False) -> dict: + def check_lib_run(self, lib, session, config_option="Run", exp=False) -> dict: """ Check if a library has been run. To be considered run a meshtally or meshtal have to be produced (for MCNP). Only active benchmarks (specified in @@ -460,8 +463,7 @@ def check_lib_run( exps = self.run_tree[lib][testname] for experiment, codes in exps.items(): for code, files in codes.items(): - flag_run_zaid = self.check_test_run( - files, code) + flag_run_zaid = self.check_test_run(files, code) if flag_run_zaid: flag_test_run = True if flag_test_run: @@ -473,8 +475,7 @@ def check_lib_run( if testname in MULTI_TEST: flag_test_run = False for zaid, files in test.items(): - flag_run_zaid = self.check_test_run( - files, code) + flag_run_zaid = self.check_test_run(files, code) if flag_run_zaid: flag_test_run = True if flag_test_run: @@ -518,9 +519,7 @@ def check_pp_single(self, lib, session, tree="single", exp=False): """ self.update_pp_status() - trees = { - "single": self.single_tree, - "comparison": self.comparison_tree} + trees = {"single": self.single_tree, "comparison": self.comparison_tree} try: library_tests = trees[tree][lib] to_pp = session.check_active_tests("Post-Processing", exp=exp) @@ -582,8 +581,7 @@ def check_override_pp(self, session, exp=False): # Check if libraries have been run flag_not_run = False for lib in libs: - test_run = self.check_lib_run( - lib, session, "Post-Processing", exp=exp) + test_run = self.check_lib_run(lib, session, "Post-Processing", exp=exp) if len(test_run) == 0: # TODO not checking for each benchmark flag_not_run = True lib_not_run = lib @@ -613,8 +611,7 @@ def check_override_pp(self, session, exp=False): You can manage the selection of benchmarks in the Config.xlsx file. """ ) - i = input( - " Would you like to override the results?(y/n) ") + i = input(" Would you like to override the results?(y/n) ") if i == "y": ans = True logtext = ( @@ -656,8 +653,7 @@ def check_override_pp(self, session, exp=False): A comparison for these libraries was already performed. """ ) - i = input( - " Would you like to override the results?(y/n) ") + i = input(" Would you like to override the results?(y/n) ") if i == "y": ans = True logtext = ( diff --git a/tests/libmanager_test.py b/tests/libmanager_test.py index d1119f6a..ab840d70 100644 --- a/tests/libmanager_test.py +++ b/tests/libmanager_test.py @@ -36,10 +36,9 @@ from jade.matreader import Zaid -ACTIVATION_FILE = os.path.join(cp, 'TestFiles', 'libmanager', - 'Activation libs.xlsx') -XSDIR_FILE = os.path.join(cp, 'TestFiles', 'libmanager', 'xsdir') -ISOTOPES_FILE = os.path.join(root, 'jade', 'resources', 'Isotopes.txt') +ACTIVATION_FILE = os.path.join(cp, "TestFiles", "libmanager", "Activation libs.xlsx") +XSDIR_FILE = os.path.join(cp, "TestFiles", "libmanager", "xsdir") +ISOTOPES_FILE = os.path.join(root, "jade", "resources", "Isotopes.txt") class TestLibManger: @@ -47,75 +46,89 @@ class TestLibManger: @pytest.fixture def lm(self): df_rows = [ - ['99c', 'sda', '', XSDIR_FILE], - ['98c', 'acsdc', '', XSDIR_FILE], - ['21c', 'adsadsa', '', XSDIR_FILE], - ['31c', 'adsadas', '', XSDIR_FILE], - ['00c', 'sdas', 'yes', XSDIR_FILE], - ['71c', 'sdasxcx', '', XSDIR_FILE]] + ["99c", "sda", "", XSDIR_FILE], + ["98c", "acsdc", "", XSDIR_FILE], + ["21c", "adsadsa", "", XSDIR_FILE], + ["31c", "adsadas", "", XSDIR_FILE], + ["00c", "sdas", "yes", XSDIR_FILE], + ["71c", "sdasxcx", "", XSDIR_FILE], + ] df_lib = pd.DataFrame(df_rows) - df_lib.columns = ['Suffix', 'Name', 'Default', 'MCNP'] + df_lib.columns = ["Suffix", "Name", "Default", "MCNP"] - return LibManager(df_lib, activationfile=ACTIVATION_FILE, - isotopes_file=ISOTOPES_FILE) + return LibManager( + df_lib, activationfile=ACTIVATION_FILE, isotopes_file=ISOTOPES_FILE + ) + + def test_available_libs(self, lm: LibManager): + """ + Test the ability to recover the available libraries + """ + libs = lm.libraries["mcnp"] + assert "99c" in libs + assert "98c" in libs + assert "21c" in libs + assert "31c" in libs + assert "00c" in libs + assert "71c" in libs def test_reactionfilereading(self, lm): - assert len(lm.reactions['99c']) == 100 - assert len(lm.reactions['98c']) == 34 + assert len(lm.reactions["99c"]) == 100 + assert len(lm.reactions["98c"]) == 34 def test_get_reactions1(self, lm): """ Test ability to recover reactions for parent zaid (one) """ - parent = '9019' - reaction = lm.get_reactions('99c', parent)[0] - assert reaction[0] == '16' - assert reaction[1] == '9018' + parent = "9019" + reaction = lm.get_reactions("99c", parent)[0] + assert reaction[0] == "16" + assert reaction[1] == "9018" def test_get_reactions2(self, lm): """ Test ability to recover reactions for parent zaid (multiple) """ - parent = '11023' - reactions = lm.get_reactions('99c', parent) + parent = "11023" + reactions = lm.get_reactions("99c", parent) print(reactions) reaction1 = reactions[0] reaction2 = reactions[1] - assert reaction1[0] == '16' - assert reaction1[1] == '11022' - assert reaction2[0] == '102' - assert reaction2[1] == '11024' + assert reaction1[0] == "16" + assert reaction1[1] == "11022" + assert reaction2[0] == "102" + assert reaction2[1] == "11024" def test_formula_conversion(self, lm): """ Test the abilty to switch between isotopes formulas and zaid number """ - tests = ['N15', 'Er164', 'Kr83'] - finals = ['N-15', 'Er-164', 'Kr-83'] + tests = ["N15", "Er164", "Kr83"] + finals = ["N-15", "Er-164", "Kr-83"] for test, final in zip(tests, finals): conversion = lm.get_zaidnum(test) name, formula = lm.get_zaidname(conversion) assert final == formula - def test_check4zaid(self, lm): + def test_check4zaid(self, lm: LibManager): """ Correctly checks availability of zaids """ - zaid = '1001' + zaid = "1001" libs = lm.check4zaid(zaid) assert len(libs) > 1 assert len(libs[0]) == 3 - zaid = '1010' + zaid = "1010" assert len(lm.check4zaid(zaid)) == 0 - def test_convertZaid(self, lm): + def test_convertZaid(self, lm: LibManager): # --- Exception if library is not available --- try: - zaid = '1001' - lib = '44c' + zaid = "1001" + lib = "44c" lm.convertZaid(zaid, lib) assert False except ValueError: @@ -123,44 +136,40 @@ def test_convertZaid(self, lm): # --- Natural zaid --- # 1 to 1 - zaid = '12000' - lib = '21c' + zaid = "12000" + lib = "21c" translation = lm.convertZaid(zaid, lib) assert translation == {zaid: (lib, 1, 1)} # expansion - lib = '31c' + lib = "31c" translation = lm.convertZaid(zaid, lib) assert len(translation) == 3 # not available in the requested lib but available in default - # not available - # try: - zaid = '84000' + # available only in the default library + zaid = "84000" translation = lm.convertZaid(zaid, lib) - translation['84209'][0] == '00c' - # assert False - # except ValueError: - # assert True + assert translation["84209"][0] == "00c" # --- 1 to 1 --- - zaid = '1001' + zaid = "1001" translation = lm.convertZaid(zaid, lib) assert translation == {zaid: (lib, 1, 1)} # --- absent --- # Use the natural zaid - zaid = '12024' - lib = '21c' + zaid = "12024" + lib = "21c" translation = lm.convertZaid(zaid, lib) - assert translation == {'12000': (lib, 1, 1)} + assert translation == {"12000": (lib, 1, 1)} # zaid available in default or other library - zaid = '84210' + zaid = "84210" translation = lm.convertZaid(zaid, lib) assert translation[zaid][0] != lib # zaid does not exist or not available in any library - zaid = '84200' + zaid = "84200" try: translation = lm.convertZaid(zaid, lib) assert False @@ -169,46 +178,46 @@ def test_convertZaid(self, lm): def test_get_libzaids(self, lm): - lib = '21c' + lib = "21c" zaids = lm.get_libzaids(lib) assert len(zaids) == 76 - assert zaids[0] == '1001' + assert zaids[0] == "1001" def test_get_zaidname(self, lm): - zaid = '1001' + zaid = "1001" name, formula = lm.get_zaidname(zaid) - assert name == 'hydrogen' - assert formula == 'H-1' + assert name == "hydrogen" + assert formula == "H-1" - zaid = '1000' + zaid = "1000" name, formula = lm.get_zaidname(zaid) - assert name == 'hydrogen' - assert formula == 'H-0' + assert name == "hydrogen" + assert formula == "H-0" def test_get_zaidnum(self, lm): - zaid = '92235' + zaid = "92235" try: zaidnum = lm.get_zaidnum(zaid) assert False except ValueError: assert True - zaid = 'U235' + zaid = "U235" zaidnum = lm.get_zaidnum(zaid) - assert zaidnum == '92235' + assert zaidnum == "92235" def test_select_lib(self, monkeypatch, lm): # monkeypatch the "input" function # Good trials - for lib in ['31c', '{"21c": "31c", "00c": "71c"}', '21c-31c']: - monkeypatch.setattr('builtins.input', lambda _: lib) + for lib in ["31c", '{"21c": "31c", "00c": "71c"}', "21c-31c"]: + monkeypatch.setattr("builtins.input", lambda _: lib) selectedlib = lm.select_lib() assert selectedlib == lib # Not found - for lib in ['44c', '{"21c": "44c", "44c": "71c"}', '21c-44c']: - monkeypatch.setattr('builtins.input', lambda _: lib) + for lib in ["44c", '{"21c": "44c", "44c": "71c"}', "21c-44c"]: + monkeypatch.setattr("builtins.input", lambda _: lib) try: selectedlib = lm.select_lib() print(lib) @@ -218,12 +227,12 @@ def test_select_lib(self, monkeypatch, lm): def test_get_zaid_mass(self, lm): # Normal zaid - zaid = '99235.31c -1' + zaid = "99235.31c -1" zaid = Zaid.from_string(zaid) mass = lm.get_zaid_mass(zaid) assert mass == 252.08298 # Natural zaid - zaid = '8000.21c 1' + zaid = "8000.21c 1" zaid = Zaid.from_string(zaid) mass = lm.get_zaid_mass(zaid) assert mass == 15.99937442590581