def read(filename, nGhost=0): """ Read the parthenon hdf file """ from phdf import phdf f = phdf(filename) return f
def Analyse(self, parameters): sys.path.insert( 1, parameters.parthenon_path + "/scripts/python/packages/parthenon_tools/parthenon_tools", ) try: import phdf from phdf_diff import compare except ModuleNotFoundError: print("Couldn't find modules to load Parthenon hdf5.") return False all_pass = True # Reflection: First comparing initial condition to final state res = compare( [ "advection.reflecting.00000.phdf", "advection.reflecting.00004.phdf" ], check_metadata=False, quiet=True, ) if res != 0: print("Double reflection test failed: output start != end ") all_pass = False # Compare that data in between is actually different. res = compare( [ "advection.reflecting.00000.phdf", "advection.reflecting.00002.phdf" ], check_metadata=False, quiet=True, ) if res == 0: print("Double reflection test failed: data identical during sim.") all_pass = False # Periodic: First comparing initial condition to final state res = compare( ["advection.periodic.00000.phdf", "advection.periodic.00004.phdf"], check_metadata=False, quiet=True, ) if res != 0: print("Fully periodic test failed: output start != end ") all_pass = False # Compare that data in between is actually different. res = compare( ["advection.periodic.00000.phdf", "advection.periodic.00001.phdf"], check_metadata=False, quiet=True, ) if res == 0: print("Fully periodic test failed: data identical during sim.") all_pass = False # Outflow: Check there's sth in the beginning... outflow_data_initial = phdf.phdf("advection.outflow.00000.phdf") advected_initial = outflow_data_initial.Get("advected") if np.sum(advected_initial) != 60.0: print("Unexpected initial data for outflow test.") all_pass = False # ... and nothing at the end outflow_data_final = phdf.phdf("advection.outflow.00004.phdf") advected_final = outflow_data_final.Get("advected") if np.sum(advected_final) != 0.0: print("Some 'advected' did not leave the box in outflow test.") all_pass = False return all_pass
def compare(files, all=False, brief=True, quiet=False, one=False, tol=1.0e-12): """ compares two hdf files """ #************** # import Reader #************** from phdf import phdf #************** # Reader Help #************** # for help on phdf uncomment following line # print(help(phdf)) # Load first file and print info try: f0 = phdf(files[0]) if not quiet: print(f0) except: print(""" *** ERROR: Unable to open %s as phdf file """%files[0]) return(1) # Load second file and print info try: f1 = phdf(files[1]) if not quiet: print(f1) except: print(""" *** ERROR: Unable to open %s as phdf file """%files[1]) return(2) # rudimentary checks if f0.TotalCellsReal != f1.TotalCellsReal: # do both simulations have same number of cells? print(""" These simulations have different number of cells. Clearly they are different. Quitting... """) return(3) # Now go through all variables in first file # and hunt for them in second file. # # Note that indices don't match when blocks # are different no_diffs = True if not brief and not quiet: print('____Comparing on a per variable basis with tolerance %.16g'%tol) breakOut = False oneTenth = f0.TotalCells//10 if not quiet: print('Mapping indices:') print('Tolerance = %g' % tol) otherLocations = [None]*f0.TotalCells for idx in range(f0.TotalCells): if not quiet: if idx%oneTenth == 0: print(' Mapping %8d (of %d) '%(idx,f0.TotalCells)) if f0.isGhost[idx%f0.CellsPerBlock]: # don't map ghost cells continue otherLocations[idx] = f0.findIndexInOther(f1,idx) if not quiet: print(f0.TotalCells,'cells mapped') for var in f0.Variables: if var == 'Locations' or var == 'Info': continue #initialize info values same = True errMax = -1.0 maxPos=[0,0,0] # Get values from file val0 = f0.Get(var) val1 = f1.Get(var) isVec = np.prod(val0.shape) != f0.TotalCells for idx,v in enumerate(val0): if f0.isGhost[idx%f0.CellsPerBlock]: # only consider real cells continue [ib,bidx,iz,iy,ix] = f0.ToLocation(idx) # find location in other file [idx1, ib1, bidx1, iz1, iy1, ix1] = otherLocations[idx] # compute error errVal = np.abs(v - val1[idx1]) errMag = np.linalg.norm(errVal) # Note that we use norm / magnitude to compute error if errMag > errMax: errMax = errMag errMaxPos = [f0.x[ib,ix], f0.y[ib,iy], f0.z[ib,iz]] if np.linalg.norm(errVal) > tol: same = False no_diffs = False if brief or quiet: breakOut=True break if isVec: s='[' for xd in errVal: if xd == 0.: s += ' 0.0' else: s += ' %10.4g'%xd s+= ']' else: s = '%10.4g'%errVal if all: print(' %20s: %6d: diff='%(var,idx),s.strip(), 'at:f0:%d:%.4f,%.4f,%.4f'%(idx, f0.x[ib,ix], f0.y[ib,iy], f0.z[ib,iz]), ':f1:%d:%.4f,%.4f,%.4f'%(idx1,f1.x[ib1,ix1],f1.y[ib1,iy1],f1.z[ib1,iz1])) if breakOut: if not quiet: print("") print('Files %s and %s are different'%(f0.file, f1.file)) if not quiet: print("") break if not quiet: if same: print(' %20s: no diffs'%var) else: print('____%26s: MaxDiff=%10.4g at'%(var,errMax),errMaxPos) if one and not same: break if no_diffs: return(0) else: return(4)
all = input.a files = input.files if input.tol is not None: tol = float(input.tol) else: tol = 1.0e-12 if len(files) != 2: Usage() exit(1) # Load first file and print info try: f0 = phdf(files[0]) if not quiet: print(f0) except: exit(1) # Load second file and print info try: f1 = phdf(files[1]) if not quiet: print(f1) except: print(""" *** ERROR: Unable to open %s as phdf file """%files[1]) exit(2) # rudimentary checks
def compare( files, brief=False, quiet=False, one=False, tol=1.0e-12, check_metadata=True, check_input=False, relative=False, ): """compares two hdf files. Returns 0 if the files are equivalent. Error codes: 1 : Can't open file 0 2 : Can't open file 1 3 : Total number of cells differ 4 : Variable data in files differ Metadata Error codes: 10 : Times in hdf files differ 11 : Attribute names in Info of hdf files differ 12 : Values of attributes in Info of hdf files differ 13 : Attribute names or values in Input of hdf files differ 14 : Attribute names or values in Params of hdf files differ 15 : Meta variables (Locations, VolumeLocations, LogicalLocations, Levels) differ """ ERROR_NO_OPEN_F0 = 1 ERROR_NO_OPEN_F1 = 2 ERROR_CELLS_DIFFER = 3 ERROR_DATA_DIFFER = 4 # ************** # import Reader # ************** from phdf import phdf # ************** # Reader Help # ************** # for help on phdf uncomment following line # print(help(phdf)) # Load first file and print info f0 = phdf(files[0]) try: f0 = phdf(files[0]) if not quiet: print(f0) except: print(""" *** ERROR: Unable to open %s as phdf file """ % files[0]) return ERROR_NO_OPEN_F0 # Load second file and print info try: f1 = phdf(files[1]) if not quiet: print(f1) except: print(""" *** ERROR: Unable to open %s as phdf file """ % files[1]) return ERROR_NO_OPEN_F1 # rudimentary checks if f0.TotalCellsReal != f1.TotalCellsReal: # do both simulations have same number of cells? print(""" These simulations have different number of cells. Clearly they are different. Quitting... """) return ERROR_CELLS_DIFFER no_diffs = True if check_metadata: if not quiet: print("Checking metadata") metadata_status = compare_metadata(f0, f1, quiet, one, check_input) if metadata_status != 0: if one: return metadata_status else: no_diffs = False else: if not quiet: print("Metadata matches") else: if not quiet: print("Ignoring metadata") # Now go through all variables in first file # and hunt for them in second file. # # Note that indices don't match when blocks # are different if not brief and not quiet: print("____Comparing on a per variable basis with tolerance %.16g" % tol) oneTenth = f0.TotalCells // 10 print("Tolerance = %g" % tol) # Make loc array of locations matching the shape of val0,val1 loc = f0.GetVolumeLocations(flatten=False) for var in set(f0.Variables + f1.Variables): var_no_diffs = True if var in [ "Locations", "VolumeLocations", "LogicalLocations", "Levels", "Info", "Params", "SparseInfo", "Input", "Blocks", ]: continue # Get values from file val0 = f0.Get(var, flatten=False) val1 = f1.Get(var, flatten=False) is_vec = np.prod(val0.shape) != f0.TotalCells # Determine arrangement of mesh blocks of f1 in terms of ordering in f0 otherBlockIdx = list( f0.findBlockIdxInOther(f1, i) for i in range(f0.NumBlocks)) # Rearrange val1 to match ordering of meshblocks in val0 val1 = val1[otherBlockIdx] # compute error at every point if relative: denom = 0.5 * (np.abs(val0) + np.abs(val1)) # When val0==0 but val1!=0 or vice versa, use the mean of the # entire data set as denom to avoid giving these points an # err_val=2.0 denom[np.logical_or( val0 == 0, val1 == 0)] = 0.5 * np.mean(np.abs(val0) + np.abs(val1)) err_val = np.abs(val0 - val1) / denom # Set error values where denom==0 to 0 # Numpy masked arrays would be more robust here, but they are very slow err_val[denom == 0] = 0 else: err_val = np.abs(val0 - val1) # Compute magnitude of error at every point if is_vec: # Norm every vector err_mag = np.linalg.norm(err_val, axis=-1) else: # Just plain error for scalars err_mag = err_val err_max = err_mag.max() # Check if the error of any block exceeds the tolerance if err_max > tol: no_diffs = False var_no_diffs = False if quiet: continue # Skip reporting the error if one: # Print the maximum difference only bad_idx = np.argmax(err_mag) bad_idx = np.array(np.unravel_index(bad_idx, err_mag.shape)) # Reshape for printing step bad_idxs = bad_idx.reshape((1, *bad_idx.shape)) else: # Print all differences exceeding maximum bad_idxs = np.argwhere(err_mag > tol) for bad_idx in bad_idxs: bad_idx = tuple(bad_idx) # Find the bad location bad_loc = np.array(loc)[:, bad_idx[0], bad_idx[1], bad_idx[2], bad_idx[3]] # TODO(forrestglines): Check that the bkji and zyx reported are the correct order print(f"Diff in {var:20s}") print( f" bkji: ({bad_idx[0]:4d},{bad_idx[1]:4d},{bad_idx[2]:4d},{bad_idx[3]:4d})" ) print( f" zyx: ({bad_loc[0]:4f},{bad_loc[1]:4f},{bad_loc[2]:4f})" ) print(f" err_mag: {err_mag[bad_idx]:4f}") if is_vec: print(f" f0: " + " ".join(f"{u:.4e}" for u in val0[bad_idx])) print(f" f1: " + " ".join(f"{u:.4e}" for u in val1[bad_idx])) print(f" err: " + " ".join(f"{u:.4e}" for u in err_val[bad_idx])) else: print(f" f0: {val0[bad_idx]:.4e}") print(f" f1: {val1[bad_idx]:.4e}") if not quiet: if var_no_diffs: print(f" {var:20s}: no diffs") else: print(f" {var:20s}: differs") if no_diffs: return 0 else: return ERROR_DATA_DIFFER
def compare_analytic(filename, analytic_components, err_func=norm_err_func, tol=1e-12, quiet=False): """Compares data in filename to analytic gold data in analytic_components. Arguments: filename: string Filename of ".phdf" file to compare data analytic_components: dictionary Dictionary keying component names to analytic functions. Each analytic function in the dictionary takes the arguments: def analytic_func(Z,Y,X,t) where Z,Y,X comprise arrays of z,y,x coords to compute the analytic solution for a component at time t Keyword Arguments: err_func=norm_err_func: function(gold,test) Error function that accepts the analytic solution and data and returns an error metric. The default is the L2 norm of the difference between the gold and test data. Other order norms and relative error functions can be constructed from norm_err_func or can be created from scratch tol=1e-12: float Tolerance of error metric. quiet=False: boolean Set to true to supress printing errors exceeding tol Returns True if the error of all components is under the tolerance, otherwise returns False. """ try: import phdf except ModuleNotFoundError: print("Couldn't find module to read Parthenon hdf5 files.") return False datafile = phdf.phdf(filename) # Dictionary of component_name:component[grid_idx,k,j,i] file_components = datafile.GetComponents(analytic_components.keys(), flatten=False) # Generate location arrays for each grid Z, Y, X = datafile.GetVolumeLocations() # Check all components for which an analytic version exists all_ok = True for component in analytic_components.keys(): # Compute the analytic component at Z,Y,X analytic_component = analytic_components[component](Z, Y, X, datafile.Time) # Compute the error between the file and analytic component err = err_func(analytic_component, file_components[component].ravel()) if err > tol: if not quiet: print( f"Component {component} in {filename} error {err} exceeds tolerance {tol}" ) all_ok = False return all_ok