Skip to content

Commit

Permalink
Merge branch 'develop' into feature/gefs_v13_822_add_atm_fcst
Browse files Browse the repository at this point in the history
  • Loading branch information
XianwuXue-NOAA committed Jan 11, 2023
2 parents 43555a0 + ddc8688 commit dd70d8d
Showing 1 changed file with 107 additions and 0 deletions.
107 changes: 107 additions & 0 deletions ush/compare_f90nml.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
#!/usr/bin/env python3

import json
import f90nml
from typing import Dict
from argparse import ArgumentParser, ArgumentDefaultsHelpFormatter


def get_dict_from_nml(filename: str) -> Dict:
"""
Read a F90 namelist and convert to a dictionary.
This method uses json to convert OrderedDictionary into regular dictionary
Parameters
----------
filename: str
Name of the F90 namelist
Returns
-------
dictionary: Dict
F90 namelist returned as a dictionary
"""
return json.loads(json.dumps(f90nml.read(filename).todict()))


def compare_dicts(dict1: Dict, dict2: Dict, path: str = "") -> None:
"""
Compare 2 dictionaries.
This is done by looping over keys in dictionary 1 and searching for them
in dictionary 2.
If a matching key is found, the values are compared.
If a matching key is not found, it is set to as UNDEFINED.
Note: A reverse match is not performed in this method. For reverse matching, use the -r option in the main driver.
Note: This is a recursive method to handle nested dictionaries.
Parameters
----------
dict1: Dict
First dictionary
dict2: Dict
Second dictionary
path: str (optional)
default: ""
key (if nested dictionary)
Returns
-------
None
"""

result = dict()
for kk in dict1.keys(): # Loop over all keys of first dictionary
if kk in dict2.keys(): # kk is present in dict2
if isinstance(dict1[kk], dict): # nested dictionary, go deeper
compare_dicts(dict1[kk], dict2[kk], path=kk)
else:
if dict1[kk] != dict2[kk]:
if path not in result:
result[path] = dict()
result[path][kk] = [dict1[kk], dict2[kk]]
else: # kk is *not* present in dict2
tt = path if path else kk
if tt not in result:
result[tt] = dict()
result[tt][kk] = [dict1[kk], 'UNDEFINED']

def _print_diffs(diff_dict: Dict) -> None:
"""
Print the differences between the two dictionaries to stdout
Parameters
----------
diff_dict: Dict
Dictionary containing differences
Returns
-------
None
"""
for path in diff_dict.keys():
print(f"{path}:")
max_len = len(max(diff_dict[path], key=len))
for kk in diff_dict[path].keys():
items = diff_dict[path][kk]
print(
f"{kk:>{max_len+2}} : {' | '.join(map(str, diff_dict[path][kk]))}")

_print_diffs(result)


if __name__ == "__main__":

parser = ArgumentParser(
description=("Compare two Fortran namelists and display differences (left_namelist - right_namelist)"),
formatter_class=ArgumentDefaultsHelpFormatter)
parser.add_argument('left_namelist', type=str, help="Left namelist to compare")
parser.add_argument('right_namelist', type=str, help="Right namelist to compare")
parser.add_argument('-r', '--reverse', help='reverse diff (right_namelist - left_namelist)',
action='store_true', required=False)
args = parser.parse_args()

nml1, nml2 = args.left_namelist, args.right_namelist
if args.reverse:
nml2, nml1 = nml1, nml2

dict1 = get_dict_from_nml(nml1)
dict2 = get_dict_from_nml(nml2)

msg = f"comparing: {nml1} | {nml2}"
print(msg)
print("-" * len(msg))
compare_dicts(dict1, dict2)

0 comments on commit dd70d8d

Please sign in to comment.