├── .github └── ISSUE_TEMPLATE.md ├── .gitignore ├── CONFIG ├── autowrf_classlib.py ├── autowrf_consts.py ├── autowrf_namelist_main.py ├── chemlist.txt ├── config ├── metlist.txt ├── namelist.input.template ├── namelist.wps.template └── wrfenvvar ├── LICENSE ├── PREPINPUT ├── bestemissyear.py ├── metgriblist.py ├── mozbc-chemopt-list.txt ├── mozcheck.py ├── postcheck.py ├── precheck ├── prepmegan ├── prepmozbc ├── prepnei_convert ├── prepnei_intermediate ├── prepwps ├── pyncdf.py └── splitwps ├── README.md ├── RUNUTILS └── lastrst ├── Runme ├── Tools ├── datecompare.py ├── gc2moz │ └── python │ │ ├── gc2moz.py │ │ └── gcCH4bins.py ├── met2input ├── pyncdf.py └── splitwps ├── autowrfchem_main ├── cleanall ├── compileall ├── configure ├── prepinpt └── runwrf /.github/ISSUE_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | # Problem summary 2 | 3 | * If you are getting a crash or other error message, include the full text of the 4 | error message and the commands you used that led to the error. If the problem is 5 | a non-fatal bug, describe what you expected to happen and what actually happened 6 | * If you need additional functionality to accomplish your research, describe what 7 | you are trying to do and what functionality is missing 8 | * In general, please describe what you are trying to do and what problem is preventing 9 | you from accomplishing it. 10 | 11 | # Steps to reproduce 12 | If the problem is a bug/crash/etc. please include: 13 | 14 | * What input data you are using (which met data, emissions data, etc.) 15 | * What Python version and package versions are being used 16 | * What compiler was used to compile WRF/WPS 17 | * Version of netCDF C and Fortran libraries used for WRF 18 | * Specific commands to execute to cause the bug/crash 19 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Ignore PyCharm ancillary stuff 2 | .idea 3 | # Ignore compiled Python 4 | *.pyc 5 | # Ignore the configuration files generated by this program 6 | *.cfg 7 | # Ignore the non-template namelists 8 | CONFIG/namelist.input 9 | CONFIG/namelist.wps 10 | CONFIG/namelist_pickle.pkl 11 | CONFIG/NAMELISTS/* 12 | CONFIG/DOMAINS/* 13 | # Ignore logs 14 | COMPLOGS/* 15 | PREPLOGS/* 16 | -------------------------------------------------------------------------------- /CONFIG/autowrf_consts.py: -------------------------------------------------------------------------------- 1 | repo = "https://github.com/CohenBerkeleyLab/AutoWRFChem-Base" 2 | -------------------------------------------------------------------------------- /CONFIG/autowrf_namelist_main.py: -------------------------------------------------------------------------------- 1 | from __future__ import print_function 2 | import sys 3 | import os 4 | import datetime as dt 5 | import autowrf_classlib as WRF 6 | from autowrf_classlib import UI 7 | import pdb 8 | 9 | 10 | if sys.version_info.major == 3: 11 | raw_input = input 12 | 13 | 14 | def MyPath(): 15 | return os.path.dirname(os.path.abspath(__file__)) 16 | 17 | 18 | def NamelistsPath(): 19 | return os.path.join(MyPath(), "NAMELISTS") 20 | 21 | 22 | wrf_namelist_template_file = os.path.join(MyPath(), "namelist.input.template") 23 | wps_namelist_template_file = os.path.join(MyPath(), "namelist.wps.template") 24 | 25 | 26 | def Startup(): 27 | # Check if the NAMELISTS subfolder exists already, 28 | # create it if not 29 | if not os.path.isdir(NamelistsPath()): 30 | os.mkdir(NamelistsPath()) 31 | 32 | 33 | def eprint(*args, **kwargs): 34 | print(*args, file=sys.stderr, **kwargs) 35 | 36 | 37 | def SelectPreexistingNamelists(): 38 | files = os.listdir(NamelistsPath()) 39 | template_name = "Standard template" 40 | wrffiles = [template_name] 41 | wpsfiles = [template_name] 42 | for f in files: 43 | if f.startswith("namelist.input"): 44 | wrffiles.append(f) 45 | elif f.startswith("namelist.wps"): 46 | wpsfiles.append(f) 47 | 48 | # If the namelist container is given a None for the file, it will load the standard template 49 | 50 | print("Select the namelist files to use. If there is a discrepancy in the domain, the WPS file") 51 | print("takes precedence.") 52 | wrffile = UI.UserInputList("Choose the WRF namelist: ", wrffiles) 53 | if wrffile == template_name: 54 | wrffile = wrf_namelist_template_file 55 | elif wrffile is not None: 56 | wrffile = os.path.join(NamelistsPath(), wrffile) 57 | 58 | wpsfile = UI.UserInputList("Choose the WPS namelist: ", wpsfiles) 59 | if wpsfile == template_name: 60 | wpsfile = wps_namelist_template_file 61 | elif wpsfile is not None: 62 | wpsfile = os.path.join(NamelistsPath(), wpsfile) 63 | 64 | return wrffile, wpsfile 65 | 66 | def LoadMenu(): 67 | opts = ["Load standard templates", 68 | "Load existing namelists", 69 | "Modify the current namelists", 70 | "Quit"] 71 | 72 | loadmethod = UI.UserInputList("What namelist would you like to load?", opts, returntype="index") 73 | if loadmethod == 0: 74 | return WRF.NamelistContainer(wrffile=wrf_namelist_template_file, wpsfile=wps_namelist_template_file) 75 | elif loadmethod == 1: 76 | wrffile, wpsfile = SelectPreexistingNamelists() 77 | if wrffile is not None and wpsfile is not None: 78 | nlc = WRF.NamelistContainer(wrffile=wrffile, wpsfile=wpsfile) 79 | nlc.UserSetMet() 80 | return nlc 81 | else: 82 | return None 83 | elif loadmethod == 2: 84 | return WRF.NamelistContainer.LoadPickle() 85 | elif loadmethod == 3: 86 | return None 87 | 88 | def PrintHelp(markstring): 89 | # Find the DOC section in this file and print it 90 | pline = False 91 | with open(__file__, 'r') as f: 92 | for line in f: 93 | if not pline: 94 | if "# BEGIN{0}".format(markstring) in line:# and "#notthis" not in line: 95 | pline = True 96 | else: 97 | if "# END{0}".format(markstring) in line:# and "#notthis" not in line: 98 | pline = False 99 | else: 100 | l = line.strip() 101 | # Remove the leading # 102 | if l.startswith("#"): 103 | l = l[1:] 104 | # Replace $MYDIR with the directory of this file 105 | l = l.replace("$MYDIR", MyPath()) 106 | print(l) 107 | 108 | def SaveMenu(nlc): 109 | # Decide how to save the namelists. Pass it the namelist container 110 | opts = ["Write namelist regularly (namelist files and pickle)", 111 | "Write just the namelist files (not pickle - i.e. temporary namelists)", 112 | "Save namelists to NAMELISTS folder for later", 113 | "Do not save"] 114 | sel = UI.UserInputList("Save the namelists?", opts, returntype="index") 115 | 116 | my_dir = os.path.dirname(__file__) 117 | 118 | if sel == 0: 119 | nlc.WriteNamelists(dir=my_dir) 120 | nlc.SavePickle() 121 | elif sel == 1: 122 | nlc.WriteNamelists(dir=my_dir) 123 | elif sel == 2: 124 | while True: 125 | suffix = UI.UserInputValue("suffix",noempty=True) 126 | if os.path.isfile(os.path.join(NamelistsPath(),"namelist.input.{0}".format(suffix))) or os.path.isfile(os.path.join(NamelistsPath(),"namelist.wps.{0}".format(suffix))): 127 | if UI.UserInputYN("{0} is already used. Overwrite?".format(suffix), default="n"): 128 | break 129 | else: 130 | break 131 | 132 | nlc.WriteNamelists(dir=NamelistsPath(), suffix=suffix) 133 | 134 | userans = raw_input("Do you also write to make these the current namelist? y/[n]: ") 135 | if userans.lower() == "y": 136 | print("Writing out namelists.") 137 | nlc.WriteNamelists(dir=my_dir) 138 | nlc.SavePickle() 139 | else: 140 | print("Not writing out namelists.") 141 | elif sel == 3: 142 | pass 143 | 144 | def StartMenu(): 145 | opts = ["Create or modify namelists", 146 | "Show help", 147 | "Quit"] 148 | while True: 149 | sel = UI.UserInputList("What would you like to do?",opts,returntype="index", emptycancel=False) 150 | if sel == 0: 151 | return LoadMenu() 152 | elif sel == 1: 153 | PrintHelp("HELP") 154 | elif sel == 2: 155 | exit(0) 156 | 157 | def ParseDateTime(dt_in): 158 | if dt_in[0] == "+" or dt_in[0] == "-": 159 | # Convert into timedelta 160 | return ParseTimeDelta(dt_in) 161 | elif "_" not in dt_in and "-" not in dt_in and ":" in dt_in: 162 | # Only a time component given: convert to a datetime.time object 163 | # and use that. 164 | dt_in = dt_in.strip() 165 | if len(dt_in) != 8: 166 | raise RuntimeError("If entering only a time (not date) component, the format must be HH:MM:SS") 167 | # Don't forget that python indices are [inclusive, exclusive] because, well, who knows 168 | hr = int(dt_in[0:2]) 169 | mn = int(dt_in[3:5]) 170 | sec = int(dt_in[6:8]) 171 | 172 | return dt.time(hr, mn, sec) 173 | else: 174 | # Parse it as a WPS type date string: yyyy-mm-dd_HH:MM:SS 175 | return WRF.WpsNamelist.ConvertDate(dt_in) 176 | 177 | def ParseTimeDelta(td_in): 178 | # These must be given in the form [+/-]xxx[s/m/h/d] 179 | if td_in[-1] == "s": 180 | scale = 1 181 | elif td_in[-1] == "m": 182 | scale = 60 183 | elif td_in[-1] == "h": 184 | scale = 3600 185 | elif td_in[-1] == "d": 186 | scale = 3600*24 187 | else: 188 | eprint("If trying to change start or end time by a relative amount (--start-time=+1d, --start-time=-3h), the") 189 | eprint("value MUST end in one of d, h, m, s (days, hours, minutes, seconds, respectively)") 190 | exit(1) 191 | 192 | try: 193 | x = float(td_in[:-1]) 194 | except ValueError: 195 | eprint("Could not parse one of the relative changes to start-date or end-date, or the value of run-time") 196 | exit(1) 197 | 198 | return dt.timedelta(seconds=x*scale) 199 | 200 | def SplitOpt(opt): 201 | opt = opt.strip().split("=") 202 | sopt = [o.strip() for o in opt] 203 | if len(sopt) != 2: 204 | raise RuntimeError("All options must be specified as --optname=value") 205 | else: 206 | return sopt[0], sopt[1] 207 | 208 | def CheckNamelists(wrf_nl, wps_nl): 209 | a_ok = True 210 | if not os.path.isfile(wrf_nl): 211 | eprint("namelist.input does not exist in {0}".format(MyPath())) 212 | a_ok = False 213 | if not os.path.isfile(wps_nl): 214 | eprint("namelist.wps does not exist in {0}".format(MyPath())) 215 | a_ok = False 216 | 217 | if not a_ok: 218 | exit(1) 219 | else: 220 | return 221 | 222 | #### MAIN PROGRAM #### 223 | # Call startup regardless of if we are running as the main program or as a module 224 | Startup() 225 | 226 | if __name__ == "__main__": 227 | 228 | # Namelist generation has 3 modes: 229 | # 1) Allow the user to interactively set the options 230 | # 2) Use specified existing namelists, ensuring that common options are matched 231 | # 3) Make a temporary namelist. This will write out the namelist file, but not overwrite the existing pickle file. 232 | # 233 | # If option 1 is chosen, it will need to load the template namelists and start the interactive menu. Upon finishing, 234 | # the user can choose to save the namelist files in another location to use with option 2 later, in addition to 235 | # saving the actual files to be linked. This can also load a different preexisting 236 | # 237 | # If option 2 is chosen, meteorology will need to be specified on the command line and optionally the namelist 238 | # files. (If the files are not specified, it will use the usual template files). 239 | # 240 | # Option 3 needs the values to be modified temporarily specified on the command line. Start and end dates are 241 | # are special and must be specified using --start-date=yyyy-mm-dd_HH:MM:SS and --end-date=yyyy-mm-dd_HH:MM:SS. All 242 | # other options can be modified by using their name as the flag, e.g. --bio_emiss_opt=3 will set bio_emiss_opt to 3 243 | # in the WRF namelist. 244 | # 245 | # This program will create the NAMELIST folder in its directory the first time it runs. 246 | # NAMELISTS is where you can save WPS/WRF namelist pairs for future use. This can take two forms. First, you could 247 | # just save a WPS namelist there after playing with it in the WPS directory using the plotgrids utility to fine- 248 | # tune your domain, then load that namelist with any WRF namelist (including the standard template) to use the 249 | # domain with any preexisting settings. Alternately, you can save a pair of WPS/WRF namelist from within the program 250 | # and load them later. The domain information in the WPS namelist always takes precedence. 251 | 252 | arg = sys.argv 253 | 254 | if len(arg) == 1: # should have just this function name 255 | # Option 1: interactive 256 | while True: 257 | nlc = StartMenu() 258 | if nlc is not None: 259 | break 260 | 261 | while nlc.UserMenu(): 262 | pass 263 | SaveMenu(nlc) 264 | if len(arg) > 1: 265 | WRF.DEBUG_LEVEL=0 #turn off normal messages from the classlib interface to prevent sending erroneous strings into the shell 266 | if arg[1] == "-h" or arg[1] == "--help": 267 | PrintHelp("DOC") 268 | print("Allowed met types are: ", end="") 269 | for m in WRF.Namelist.mets: 270 | print(m, end=" ") 271 | print("") 272 | elif arg[1] == "load": 273 | argopts = arg[2:] 274 | metopt = None 275 | suffix = None 276 | for opt in argopts: 277 | optname, optval = SplitOpt(opt) 278 | if optname == "--met": 279 | metopt = optval 280 | elif optname == "--suffix": 281 | suffix = optval 282 | 283 | if metopt is None: 284 | eprint("Error: requesting to load an existing namelist requires meteorology to be specified with") 285 | eprint(" --met= in order to ensure that both namelists use the same meteorology") 286 | eprint(" settings. can be one of {0}".format(WRF.Namelist.mets)) 287 | exit(1) 288 | else: 289 | if suffix is not None: 290 | wrffname = WRF.NamelistContainer.wrf_namelist_outfile + "." + suffix 291 | wpsfname = WRF.NamelistContainer.wps_namelist_outfile + "." + suffix 292 | wrffile = os.path.join(NamelistsPath(), wrffname) 293 | wpsfile = os.path.join(NamelistsPath(), wpsfname) 294 | nlc = WRF.NamelistContainer(met=metopt, wrffile=wrffile, wpsfile=wpsfile) 295 | else: 296 | nlc = WRF.NamelistContainer(met=metopt, wrffile=wrf_namelist_template_file, 297 | wpsfile=wps_namelist_template_file) 298 | 299 | nlc.WriteNamelists() 300 | nlc.SavePickle() 301 | 302 | elif arg[1] == "mod" or arg[1] == "modify" or arg[1] == "tempmod": 303 | # So this needs to parse the options looking for a couple things: 304 | # 1) start-date and end-date need to be handled specially, to use the SetTimePeriod method 305 | # 2) run-time also needs to be handled specially as well 306 | # 3) Also add the capability to do relative changes to start and end time with the syntax 307 | # --start-date=+12h 308 | # 4) start-date, end-date and run-time need to be processed at the end once we are sure which ones we 309 | # have 310 | nlc = WRF.NamelistContainer.LoadPickle() 311 | start_date = None 312 | end_date = None 313 | run_time = None 314 | force_wrf_only = False 315 | for a in arg[2:]: 316 | if a == "--force-wrf-only": 317 | force_wrf_only = True 318 | continue 319 | 320 | optname, optval = SplitOpt(a) 321 | if optname == "--start-date": 322 | start_date = ParseDateTime(optval) 323 | elif optname == "--end-date": 324 | end_date = ParseDateTime(optval) 325 | elif optname == "--run-time": 326 | run_time = ParseTimeDelta(optval) 327 | elif optname == "--met": 328 | nlc.SetMet(optval) 329 | else: 330 | optname = optname.replace("-", "") 331 | nlc.CmdSetOtherOpt(optname, optval, forceWrfOnly=force_wrf_only) 332 | 333 | if start_date is not None or end_date is not None: 334 | nlc.SetTimePeriod(start_date, end_date) 335 | 336 | if run_time is not None: 337 | if run_time < dt.timedelta(0): 338 | eprint("Negative values of run time are not permitted") 339 | exit(1) 340 | 341 | # both are returned to ensure it is not stored as a tuple 342 | start_date, end_date = nlc.GetTimePeriod() 343 | end_date = start_date + run_time 344 | nlc.SetTimePeriod(start_date, end_date) 345 | 346 | nlc.WriteNamelists() 347 | # Only write the pickle if the change is not temporary 348 | if arg[1] == "mod" or arg[1] == "modify": 349 | nlc.SavePickle() 350 | 351 | elif "check" in arg[1]: 352 | wrf_namelist = os.path.join(MyPath(), "namelist.input") 353 | wps_namelist = os.path.join(MyPath(), "namelist.wps") 354 | 355 | # Verify that the namelists exist 356 | CheckNamelists(wrf_namelist, wps_namelist) 357 | 358 | nlc = WRF.NamelistContainer(wrffile=wrf_namelist, wpsfile=wps_namelist) 359 | if "wrf" in arg[1]: 360 | namelist = nlc.wrf_namelist 361 | elif "wps" in arg[1]: 362 | namelist = nlc.wps_namelist 363 | else: 364 | eprint("If trying to check an argument, must use check-wrf-opt or check-wps-opt") 365 | eprint("(neither 'wrf' nor 'wps' found in the flag)") 366 | exit(1) 367 | 368 | for a in arg[2:]: 369 | optname, optval = SplitOpt(a) 370 | optname = optname.replace("-", "") 371 | if not namelist.IsOptInNamelist(optname): 372 | eprint("Could not find '{0}' in specified namelist".format(optname)) 373 | exit(2) 374 | nlopt = namelist.GetOptValNoSect(optname, domainnum=1) 375 | optval = optval.split(",") 376 | optbool = [] 377 | # Any option should be in a string format. However, some may include single quotes. 378 | # So we try comparing with and without single quotes 379 | for i in range(len(optval)): 380 | optbool.append(False) 381 | if optval[i] == nlopt: 382 | optbool[i] = True 383 | break 384 | else: 385 | if nlopt.startswith("'"): 386 | nlopt = nlopt[1:] 387 | if nlopt.endswith("'"): 388 | nlopt = nlopt[:-1] 389 | if optval[i] == nlopt: 390 | optbool[i] = True 391 | break 392 | 393 | if all([not b for b in optbool]): 394 | exit(1) 395 | exit(0) 396 | elif "get" in arg[1]: 397 | wrf_namelist = os.path.join(MyPath(), "namelist.input") 398 | wps_namelist = os.path.join(MyPath(), "namelist.wps") 399 | 400 | # Verify that the namelists exist 401 | CheckNamelists(wrf_namelist, wps_namelist) 402 | 403 | nlc = WRF.NamelistContainer(wrffile=wrf_namelist, wpsfile=wps_namelist) 404 | if "wrf" in arg[1]: 405 | namelist = nlc.wrf_namelist 406 | elif "wps" in arg[1]: 407 | namelist = nlc.wps_namelist 408 | else: 409 | eprint("If trying to get an argument, must use get-wrf-opt or get-wps-opt") 410 | eprint("(neither 'wrf' nor 'wps' was found in the flag)") 411 | exit(1) 412 | 413 | if "--no-quotes" in arg: 414 | noquote=True 415 | arg.remove("--no-quotes") 416 | else: 417 | noquote=False 418 | 419 | optname = arg[2].replace("-", "") 420 | if optname == "startdate": 421 | start_date, end_date = nlc.GetTimePeriod() 422 | print(start_date.strftime("%Y-%m-%d_%H:%M:%S")) 423 | elif optname == "enddate": 424 | start_date, end_date = nlc.GetTimePeriod() 425 | print(end_date.strftime("%Y-%m-%d_%H:%M:%S")) 426 | elif not namelist.IsOptInNamelist(optname): 427 | eprint("Could not find '{0}' in specified namelist".format(optname)) 428 | exit(2) 429 | else: 430 | val = namelist.GetOptValNoSect(optname, domainnum=1, noquotes=noquote) 431 | print(val) 432 | else: 433 | eprint("Command '{0}' not recognized".format(arg[1])) 434 | exit(1) 435 | 436 | if len(arg) == 1: 437 | print("Goodbye") 438 | exit(0) 439 | # Documentation section that will be printed as help text from the command line 440 | 441 | # BEGINDOC 442 | # 443 | # autowrf_namelist_main.py has 3 modes of usage: 444 | # python autowrf_namelist_main.py: with no other options, this enters an interactive mode that allows the user to 445 | # interactively set or modify the WRF and WPS namelists. (The main advantage of using this mode over directly 446 | # editing the namelists is that this program keeps common options between WRF and WPS in sync.) 447 | # 448 | # python autowrf_namelist_main.py load --met= --suffix=: this mode will immediately make the 449 | # namelists contained in the NAMELIST folder with the suffix defined by the --suffix option the active namelists. 450 | # A meteorology must be specified with the --met option to be sure that those settings are syncronized between 451 | # the WRF and WPS namelists. If the --suffix option is omitted, then the template files will be used. 452 | # 453 | # python autowrf_namelist_main.py mod: this mode will allow you to modify the current namelist. Most settings in 454 | # either the WPS or WRF namelist can be set directly by specifying their name as the option flag, e.g 455 | # --history_interval=60 will set the history_interval option in the WRF namelist to 60. Note that settings that 456 | # are boolean values must be given the value of .true. or .false. or it will be rejected. Further, at present, 457 | # all domains will be set to this value (there is no way to modify nested domains separately with this version of 458 | # this program). 459 | # Several options are reserved: any options associated with meteorology cannot be changed directly and can 460 | # only be changed by giving the flag --met=, where is one of the allowed meteorologies (see 461 | # below). 462 | # Further, to change the time period, the flags --start-date, --end-date, and --run-time must be used. 463 | # --state-date and --end-date can be one of two forms. First, they can be given as yyyy-mm-dd or 464 | # yyyy-mm-dd_HH:MM:SS to set either as an absolute value. Second, a relative change can be given as 465 | # --start-date=[+/-]nnn[d/h/m/s], that is, the option must start with either a + or - (indicating positive or 466 | # negative change respectively) and end in d, h, m, s (indicating days, hours, minutes, seconds). Thus, 467 | # --start-date=+12h will make the start date 12 hours later, while --end-date=-7d would make the run end 7 days 468 | # sooner. 469 | # Finally, --run-time can be used to set the end time relative to the start time. The syntax is the same as a 470 | # relative change to start or end date except a + sign is not needed at the beginning (so --run-time=12h will 471 | # set the end time to be 12 hours after the start time). Negative values will be rejected. Note that the run time 472 | # option is applied after the start date one, regardless of what order the options are on the command line. 473 | # Running this command without any options restores the namelists to the values stored in the pickle (useful 474 | # in conjunction with the "tempmod" mode below). 475 | # 476 | # python autowrf_namelist_main.py modify: same behavior as "mod" 477 | # 478 | # python autowrf_namelist_main.py tempmod: similar behaviour to "mod," except that in this case the pickle is not 479 | # changed. Since that is what is used to load the current namelist when executing the "mod" option, this means 480 | # that the next time you run this with the mod or similar option, you will be starting from the state BEFORE 481 | # running the "tempmod" command. This is very useful when preparing NEI emissions, as that requires you to 482 | # set a 12 hour run time briefly to prepare the emissions. This way, you could run 483 | # python autowrf_namelist_main.py tempmod --run-time=12h to generate the proper namelist, then 484 | # python autowrf_namelist_main.py mod to restore the current settings. (Of course you'd need to generate the other 485 | # 12 hr NEI file, but this is just an example). 486 | # 487 | # python autowrf_namelist_main.py check-wrf-opt: allows you to pass namelist options using the same flags as the "mod" 488 | # method (except that the special flags --start-date, --end-date, and --run-time cannot be used). If all the 489 | # options specified on the command line are correct as stored in the current permanent namelist (so temporary 490 | # changes made with "tempmod" cannot be checked), this will exit with 0. If any do not match, it exits with 1. If 491 | # you wish to allow the option to be one of several values, separate each with a comma, i.e 492 | # --io_form_auxinput5=2,11 will return 0 if io_form_auxinput5 is 2 or 11. If it cannot find an option, it exits 493 | # with exit code 2 494 | # 495 | # python autowrf_namelist_main.py check-wps-opt: same as check-wrf-opt but for WPS. 496 | # 497 | # python autowrf_namelist_main.py get-wrf-opt: will return the value of the WRF namelist option specified using the 498 | # same syntax as check-wrf-opt. Additionally, the flag --no-quotes will strip any ' characters from the string 499 | # first. Like check-wrf-opt, if it cannot find the option, it exits with exit code 2. 500 | # 501 | # python autowrf_namelist_main.py get-wps-opt: same as get-wrf-opt but for WPS. 502 | # 503 | # ENDDOC 504 | # 505 | # 506 | # Help section printed for the interactive help 507 | # BEGINHELP 508 | # 509 | # This help will focus on the interactive mode of operation. 510 | # For help on the command line options, run "python autowrf_namelist_main.py --help" from the command line. 511 | # 512 | # This program allows you to interactively modify WRF/WPS namelists. The primary advantage of doing it this way rather 513 | # than manually editing them is that this program will ensure any options that should be synchronized between the two 514 | # namelists are kept synchronized (e.g. e_we and e_sn). It also handles settings that relate to the choice of met data 515 | # through your choice of met data. 516 | # 517 | # When loading namelists, you are given three options: 518 | # 1) Load standard templates: this reads the namelist.input.template and namelist.wps.template files in 519 | # $MYDIR. 520 | # These are configured to support NEI, MEGAN, and MOZBC immediately and use RADM2 chemistry. 521 | # 522 | # 2) Load a pair of WPS/WRF namelists: Namelists go in 523 | # $MYDIR/NAMELISTS 524 | # either manually or from within the program. Note that WRF namelists must begin with "namelist.input" and WPS 525 | # namelists must begin with "namelist.wps" to be recognized by the program. The program will 526 | # then ask you to choose, both a WPS and WRF namelist file. You can use this feature to quickly 527 | # switch between chemistry or domain choices. If the domain or time settings in the WPS and WRF namelist differ, 528 | # the WPS namelist takes precedence. 529 | # 530 | # 3) Modify the current namelists: This will load the current namelists without any temporary changes. (More on 531 | # those below). 532 | # 533 | # Once you have loaded your namelists, you will be presented with a list of options to check or change. These are 534 | # fairly self explanatory. One note: whenever you change the start/end date or the domain, it will ask you to 535 | # choose a MOZBC input file. This expects the MOZBC files to be in a very specific place (../../MOZBC/data, relative 536 | # to the autowrf_classlib.py file). If it cannot find any, it will print an error message. It also expects 537 | # wrfbuild.cfg to be present one level above $MYDIR 538 | # as that file will be where the MOZBC file is specified. If you are not using this as part of the larger AutoWRFChem 539 | # program, you can ignore this warnings, so long as you recognize that the MOZBC data file will not be specified 540 | # automatically. 541 | # 542 | # When saving, you are again given four options. To understand these, you should know that this program has the 543 | # ability to temporarily modify the namelists while retaining a copy of the original. It does so by storing the 544 | # current namelists in two ways. First, it writes out the namelist files namelist.input and namelist.wps in its 545 | # directory: 546 | # 547 | # $MYDIR 548 | # 549 | # Second, it saves Python objects representing the namelists in the "namelist_pickle.pkl" file in the same directory. 550 | # (A "pickle" is how Python can save data for it to read again later.) This does NOT contain any temporary changes, 551 | # and is what is loaded when you choose "Modify the current namelists" when loading a file. 552 | # 553 | # Temporary changes can be used when, for instance, you need to set the run time to 12 hours to generate NEI input 554 | # files. You probably don't want to only run WRF for 12 hours when you do the actual run, but you do need it to only 555 | # run that long to generate wrfchemi_00z_d01 and wrfchemi_12z_d01. By setting the 12 hour run time as a temporary 556 | # change, this means that the next time this program is run, it can easily restore your settings for the full run. In 557 | # fact, it does not support loading a namelist such that the temporary changes are maintained currently. (The full 558 | # AutoWRFChem program uses the command line implementation of this to automate preparing NEI emissions.) 559 | # 560 | # Now we can understand the options available for saving the namelists: 561 | # 1) Write namelist regularly: saves the namelists to both the actual namelist files and the pickle. Thus, if you 562 | # ran this program again and chose "Modify current namelists" you would get the namelists you just saved. 563 | # 564 | # 2) Write just the namelist files: does not write the pickle. Thus, if you run this program again and chose 565 | # "Modify current namelists" you would NOT get the namelist you just saved, but rather the last one you saved 566 | # using option 1 or possibly 3. 567 | # 568 | # 3) Save namelits to NAMELISTS folder: this will ask for a suffix to append to the namelist names to identify 569 | # them, then write the namelists to the NAMELIST folder, where it can load them later. It will also ask if you 570 | # want to also set them to be the current namelist, if you answer yes, it saves both the regular files and the 571 | # pickle. 572 | # 573 | # 4) Do not save: Self explanatory. Does not save anything. 574 | # 575 | # 576 | # ENDHELP 577 | -------------------------------------------------------------------------------- /CONFIG/chemlist.txt: -------------------------------------------------------------------------------- 1 | # This file holds the definitions for the various chemistry options that can be automatically defined in 2 | # autowrfchem_classlib.py 3 | # 4 | # To add a new section, it must begin and end with the lines "BEGIN ___" and "END ___" (without the quotes) where ___ 5 | # is replaced with the name you want it to be called in the program. (e.g. BEGIN RADM2). This is case sensitive in that 6 | # BEGIN and END must be capitalized. To mark the mechanism as a KPP mechanism, include the line @ISKPP anywhere between 7 | # the BEGIN and END statements. 8 | # 9 | # List each option to be set using "namelist:section:option_name = val" (without quotes). White space 10 | # does not matter, but DO NOT include more than one value per line at the moment (nested domains are not implemented). 11 | 12 | BEGIN RADM2 13 | wrf:chem:chem_opt = 2 14 | wrf:chem:io_style_emissions = 1 15 | wrf:chem:emiss_inpt_opt = 1 16 | wrf:chem:emiss_opt = 3 17 | wrf:chem:chem_in_opt = 0 18 | wrf:chem:phot_opt = 2 19 | wrf:chem:gas_drydep_opt = 1 20 | wrf:chem:aer_drydep_opt = 1 21 | wrf:chem:dust_opt = 0 22 | wrf:chem:dmsemis_opt = 0 23 | wrf:chem:seas_opt = 0 24 | wrf:chem:have_bcs_chem = .true. 25 | wrf:chem:gas_bc_opt = 16 26 | wrf:chem:gas_ic_opt = 16 27 | wrf:chem:aer_bc_opt = 1 28 | wrf:chem:aer_ic_opt = 1 29 | wrf:chem:gaschem_onoff = 1 30 | wrf:chem:aerchem_onoff = 1 31 | wrf:chem:wetscav_onoff = 0 32 | wrf:chem:cldchem_onoff = 0 33 | wrf:chem:vertmix_onoff = 1 34 | END RADM2 35 | 36 | BEGIN R2SMH 37 | @ISKPP 38 | wrf:chem:chem_opt = 113 39 | wrf:chem:io_style_emissions = 1 40 | wrf:chem:emiss_inpt_opt = 1 41 | wrf:chem:emiss_opt = 11 42 | wrf:chem:chem_in_opt = 0 43 | wrf:chem:phot_opt = 2 44 | wrf:chem:gas_drydep_opt = 1 45 | wrf:chem:aer_drydep_opt = 1 46 | wrf:chem:dust_opt = 0 47 | wrf:chem:dmsemis_opt = 0 48 | wrf:chem:seas_opt = 0 49 | wrf:chem:have_bcs_chem = .true. 50 | wrf:chem:gas_bc_opt = 16 51 | wrf:chem:gas_ic_opt = 16 52 | wrf:chem:aer_bc_opt = 1 53 | wrf:chem:aer_ic_opt = 1 54 | wrf:chem:gaschem_onoff = 1 55 | wrf:chem:aerchem_onoff = 1 56 | wrf:chem:wetscav_onoff = 0 57 | wrf:chem:cldchem_onoff = 0 58 | wrf:chem:vertmix_onoff = 1 59 | END R2SMH 60 | 61 | BEGIN DUST 62 | wrf:chem:chem_opt = 401 63 | wrf:chem:io_style_emissions = 0 64 | wrf:chem:emiss_inpt_opt = 0 65 | wrf:chem:emiss_opt = 0 66 | wrf:chem:bio_emiss_opt = 0 67 | wrf:chem:chem_in_opt = 0 68 | wrf:chem:phot_opt = 0 69 | wrf:chem:gas_drydep_opt = 0 70 | wrf:chem:aer_drydep_opt = 0 71 | wrf:chem:dust_opt = 0 72 | wrf:chem:dmsemis_opt = 0 73 | wrf:chem:seas_opt = 0 74 | wrf:chem:have_bcs_chem = .false. 75 | wrf:chem:gas_bc_opt = 0 76 | wrf:chem:gas_ic_opt = 0 77 | wrf:chem:aer_bc_opt = 0 78 | wrf:chem:aer_ic_opt = 0 79 | wrf:chem:gaschem_onoff = 0 80 | wrf:chem:aerchem_onoff = 0 81 | wrf:chem:wetscav_onoff = 0 82 | wrf:chem:cldchem_onoff = 0 83 | wrf:chem:vertmix_onoff = 1 84 | END DUST 85 | -------------------------------------------------------------------------------- /CONFIG/config: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | cd `dirname $0` 4 | mydir=`pwd -P` 5 | outfile="$mydir/../wrfbuild.cfg" 6 | 7 | # Have the user choose available met 8 | mettypes=("NARR" "Other") 9 | i=0 10 | echo "Choose the meterology type:" 11 | for met in "${mettypes[@]}"; do 12 | i=$((i+1)) 13 | echo " $i: $met" 14 | done 15 | while true; do 16 | echo -n "Enter choice (1-${i}):" 17 | read userin 18 | if [ $userin -ge 1 -a $userin -le $i ]; then 19 | break 20 | fi 21 | done 22 | 23 | rm -f $outfile 24 | 25 | sel=$((userin-1)) 26 | metType=${mettypes[$sel]} 27 | echo "metType=$metType" >> $outfile 28 | 29 | echo "Enter the path where the meteorology data is stored." 30 | echo "You may also skip this and modify $mydir/wrfbuild.cfg later." 31 | echo "Note that the path should not contain any spaces, as this poses a problem for linkgrib.csh" 32 | echo "Making it an absolute path is also HIGHLY recommended, though paths relative to the WPS" 33 | echo "directory *should* work" 34 | read metDir 35 | 36 | if [[ $metDir == *[[:space:]]* ]]; then 37 | echo "WARNING: the path $metDir contains whitespace, this may break linkgrib.csh" 38 | fi 39 | 40 | echo "metDir='$metDir'" >> $outfile 41 | -------------------------------------------------------------------------------- /CONFIG/metlist.txt: -------------------------------------------------------------------------------- 1 | # This file holds the meteorology related options that will be set when 2 | # selecting a meteorology in autowrfchem_classlib.py. 3 | # 4 | # To add a new meteorology, you'll need to add a section delineated by 5 | # "BEGIN ___" and "END ___" where ___ is replaced by the meterology name. 6 | # Do not include quotes. List the options to set using 7 | # 8 | # namelist:namelist_section:option = value 9 | # 10 | # as an example, 11 | # 12 | # wrf:time_control:interval_seconds = 10800 13 | # 14 | # Only include one value per line (nested domains are not implemented). 15 | # Whitespace does not matter. 16 | 17 | BEGIN NARR 18 | wps:share:interval_seconds = 10800 19 | wrf:time_control:interval_seconds = 10800 20 | wrf:domains:p_top_requested = 10000 21 | wrf:domains:e_vert = 30 22 | wrf:domains:num_metgrid_levels = 30 23 | wrf:domains:num_metgrid_soil_levels = 4 24 | wrf:fdda:gfdda_interval_m = 180 25 | END NARR 26 | -------------------------------------------------------------------------------- /CONFIG/namelist.input.template: -------------------------------------------------------------------------------- 1 | &time_control 2 | run_days = 0, 3 | run_hours = 7, 4 | run_minutes = 0, 5 | run_seconds = 0, 6 | start_year = 2013, 2013, 2999, 7 | start_month = 05, 05, 06, 8 | start_day = 27, 27, 21, 9 | start_hour = 00, 00, 12, 10 | start_minute = 00, 00, 00, 11 | start_second = 00, 00, 00, 12 | end_year = 2013, 2013, 2013, 13 | end_month = 06, 09, 07, 14 | end_day = 03, 01, 16, 15 | end_hour = 00, 00, 12, 16 | end_minute = 00, 00, 00, 17 | end_second = 00, 00, 00, 18 | interval_seconds = 10800 19 | input_from_file = .true.,.true.,.true., 20 | history_interval = 30, 30, 60, 21 | frames_per_outfile = 1, 1, 12, 22 | restart = .false., 23 | restart_interval = 10080, 24 | auxinput5_interval_h = 1, 1, 60 25 | io_form_history = 2 26 | io_form_restart = 2 27 | io_form_input = 2 28 | io_form_boundary = 2 29 | io_form_auxinput4 = 0 30 | io_form_auxinput5 = 2 31 | io_form_auxinput6 = 2 32 | io_form_auxinput8 = 0 33 | io_form_auxinput7 = 0 34 | io_form_auxinput12 = 0 35 | io_form_auxinput2 = 2 36 | auxinput12_inname ='wrf_chem_input', 37 | debug_level = 0 38 | / 39 | 40 | &domains 41 | time_step = 72, 42 | time_step_fract_num = 0, 43 | time_step_fract_den = 1, 44 | max_dom = 1, 45 | e_we = 74, 121, 94, 46 | e_sn = 61, 91, 91, 47 | e_vert = 30, 30, 20, 48 | dx = 30000, 4000, 123, 49 | dy = 30000, 4000, 123, 50 | p_top_requested = 10000, 51 | num_metgrid_levels = 30, 52 | num_metgrid_soil_levels = 4, 53 | grid_id = 1, 2, 3, 54 | parent_id = 0, 1, 2, 55 | i_parent_start = 1, 20, 30, 56 | j_parent_start = 1, 20, 30, 57 | parent_grid_ratio = 1, 3, 3, 58 | parent_time_step_ratio = 1, 3, 3, 59 | feedback = 1, 60 | smooth_option = 0 61 | / 62 | 63 | &physics 64 | mp_physics = 2, 2, 2, 65 | progn = 0, 0, 0, 66 | naer = 1e9 67 | ra_lw_physics = 1, 1, 1, 68 | ra_sw_physics = 2, 2, 2, 69 | radt = 30, 30, 10, 70 | sf_sfclay_physics = 1, 1, 1, 71 | sf_surface_physics = 2, 2, 2, 72 | bl_pbl_physics = 1, 1, 1, 73 | bldt = 1, 1, 0, 74 | cu_physics = 5, 5, 0, 75 | cugd_avedx = 1 76 | cudt = 1, 1, 1, 77 | isfflx = 1, 78 | ifsnow = 1, 79 | icloud = 1, 80 | surface_input_source = 1, 81 | num_soil_layers = 4, 82 | sf_urban_physics = 0, 0, 0, 83 | maxiens = 1, 84 | maxens = 3, 85 | maxens2 = 3, 86 | maxens3 = 16, 87 | ensdim = 144, 88 | cu_rad_feedback = .true., 89 | cu_diag = 1, 90 | / 91 | 92 | &fdda 93 | / 94 | 95 | &dynamics 96 | w_damping = 1, 97 | diff_opt = 1, 98 | km_opt = 4, 99 | diff_6th_opt = 0, 0, 0, 100 | diff_6th_factor = 0.12, 0.12, 0.12, 101 | base_temp = 290. 102 | damp_opt = 0, 103 | zdamp = 5000., 5000., 5000., 104 | dampcoef = 0.2, 0.2, 0.2 105 | khdif = 0, 0, 0, 106 | kvdif = 0, 0, 0, 107 | non_hydrostatic = .true., .true., .true., 108 | moist_adv_opt = 2, 1, 1, 109 | scalar_adv_opt = 2, 1, 1, 110 | chem_adv_opt = 2, 1, 1, 111 | / 112 | 113 | &bdy_control 114 | spec_bdy_width = 5, 115 | spec_zone = 1, 116 | relax_zone = 4, 117 | specified = .true., .false.,.false., 118 | nested = .false., .true., .true., 119 | / 120 | 121 | &grib2 122 | / 123 | 124 | &chem 125 | kemit = 19, 126 | chem_opt = 2, 2, 127 | bioemdt = 0, 0, 128 | photdt = 30, 30, 129 | chemdt = 0, 0, 130 | io_style_emissions = 1, 131 | emiss_inpt_opt = 1, 1, 132 | emiss_opt = 3, 3, 133 | chem_in_opt = 0, 0, 134 | phot_opt = 2, 2, 135 | gas_drydep_opt = 1, 1, 136 | aer_drydep_opt = 1, 1, 137 | bio_emiss_opt = 3, 3, 138 | ne_area = 200, 139 | dust_opt = 0, 140 | dmsemis_opt = 0, 141 | seas_opt = 0, 142 | have_bcs_chem = .false.,.false., 143 | gas_bc_opt = 1, 1, 144 | gas_ic_opt = 1, 1, 145 | aer_bc_opt = 1, 1, 146 | aer_ic_opt = 1, 1, 147 | gaschem_onoff = 1, 1, 148 | aerchem_onoff = 1, 1, 149 | wetscav_onoff = 0, 0, 150 | cldchem_onoff = 0, 0, 151 | vertmix_onoff = 1, 1, 152 | biomass_burn_opt = 0, 0, 153 | plumerisefire_frq = 30, 30, 154 | aer_ra_feedback = 0, 0, 155 | chem_conv_tr = 1, 1, 156 | diagnostic_chem = 3, 3, 157 | scale_nei_emis = .true., 158 | scale_closest_year = .false., 159 | / 160 | 161 | &namelist_quilt 162 | nio_tasks_per_group = 0, 163 | nio_groups = 1, 164 | / 165 | -------------------------------------------------------------------------------- /CONFIG/namelist.wps.template: -------------------------------------------------------------------------------- 1 | &share 2 | wrf_core = 'ARW', 3 | max_dom = 1, 4 | start_date = '2013-05-27_00:00:00','2013-05-27_00:00:00', 5 | end_date = '2013-06-03_00:00:00','2013-06-03_00:00:00', 6 | interval_seconds = 10800 7 | io_form_geogrid = 2, 8 | / 9 | 10 | &geogrid 11 | parent_id = 1, 1, 12 | parent_grid_ratio = 1, 3, 13 | i_parent_start = 1, 31, 14 | j_parent_start = 1, 17, 15 | e_we = 74, 112, 16 | e_sn = 61, 91, 17 | geog_data_res = '10m','2m', 18 | dx = 30000, 19 | dy = 30000, 20 | map_proj = 'lambert', 21 | ref_lat = 34.83, 22 | ref_lon = -81.03, 23 | truelat1 = 34.83, 24 | truelat2 = 34.83, 25 | stand_lon = -81.03, 26 | geog_data_path = '/global/scratch/laughner/WRF-All/WPS_GEOG_ALL/' 27 | / 28 | 29 | &ungrib 30 | out_format = 'WPS', 31 | prefix = 'FILE', 32 | / 33 | 34 | &metgrid 35 | fg_name = 'FILE' 36 | io_form_metgrid = 2, 37 | constants_name = '/global/home/groups/co_aiolos/cohen/WPS_FIXED/NARRfixed.wps' 38 | / 39 | -------------------------------------------------------------------------------- /CONFIG/wrfenvvar: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # 3 | # Check that WRF-Chem appropriate environmental variables are 4 | # set. 5 | # The values will be placed in the envvar_wrfchem.cfg file in the 6 | # directory one level up from this to be sourced by other scripts as 7 | # necessary. 8 | # 9 | # First see if any of the core environmental variables are set 10 | 11 | if [ "$1" == "--override" ]; then 12 | override=true 13 | else 14 | override=false 15 | fi 16 | 17 | outfile="$(dirname $0)/../envvar_wrfchem.cfg" 18 | rm -f $outfile 19 | 20 | 21 | if ! $override; then 22 | doexit=false 23 | if [ -z "$EM_CORE" ] || [ "$EM_CORE" -ne 1 ]; then 24 | echo "EM_CORE should be set to 1 for WRF-Chem" 25 | doexit=true 26 | fi 27 | if [ -z "$NMM_CORE" ] || [ "$NMM_CORE" -ne 0 ]; then 28 | echo "NMM_CORE should be set to 0 for WRF-Chem" 29 | doexit=true 30 | fi 31 | if [ -z "$WRF_EM_CORE" ] || [ "$WRF_EM_CORE" -ne 1 ]; then 32 | echo "WRF_EM_CORE should be set to 1 for WRF-Chem" 33 | doexit=true 34 | fi 35 | if [ -z "$WRF_NMM_CORE" ] || [ "$WRF_NMM_CORE" -ne 0 ]; then 36 | echo "WRF_NMM_CORE should be set to 0 for WRF-Chem" 37 | doexit=true 38 | fi 39 | if [ -z "$WRF_CHEM" ] || [ "$WRF_CHEM" -ne 1 ]; then 40 | echo "WRF_CHEM must be set to 1 to compile WRF-Chem" 41 | doexit=true 42 | fi 43 | if [ -z "$WRFIO_NCD_LARGE_FILE_SUPPORT" ] || [ "$WRFIO_NCD_LARGE_FILE_SUPPORT" -ne 1 ]; then 44 | echo "WRFIO_NCD_LARGE_FILE_SUPPORT should be set to 1 for WRF-Chem" 45 | doexit=true 46 | fi 47 | 48 | if $doexit; then 49 | echo "" 50 | echo "**************************************************************************************" 51 | echo "One or more environmental variables for WRF-Chem have been detected as set improperly." 52 | echo "Unset them, or run buildwrfchem with both the 'config' and 'override' options to" 53 | echo "force all environmental variables to be set correctly." 54 | echo "**************************************************************************************" 55 | echo "" 56 | 57 | exit 1 58 | else 59 | echo "export EM_CORE=$EM_CORE" >> $outfile 60 | echo "export WRF_EM_CORE=$WRF_EM_CORE" >> $outfile 61 | echo "export NMM_CORE=$NMM_CORE" >> $outfile 62 | echo "export WRF_NMM_CORE=$WRF_NMM_CORE" >> $outfile 63 | echo "export WRF_CHEM=$WRF_CHEM" >> $outfile 64 | echo "export WRFIO_NCD_LARGE_FILE_SUPPORT=1" >> $outfile 65 | fi 66 | else 67 | echo "export EM_CORE=1" >> $outfile 68 | echo "export WRF_EM_CORE=1" >> $outfile 69 | echo "export NMM_CORE=0" >> $outfile 70 | echo "export WRF_NMM_CORE=0" >> $outfile 71 | echo "export WRF_CHEM=1" >> $outfile 72 | echo "export WRFIO_NCD_LARGE_FILE_SUPPORT=1" >> $outfile 73 | fi 74 | 75 | if [ ! -z "$WRF_KPP" ]; then 76 | if [ "$WRF_KPP" -eq 1 ]; then 77 | usekpp=true 78 | else 79 | usekpp=false 80 | fi 81 | else 82 | echo -n "Use KPP (kinetic preprocessor)? Default is Y. [y/n]: " 83 | read userans 84 | if [ "$userans" == "n" -o "userans" == "N" ]; then 85 | echo "Not using KPP" 86 | usekpp=false 87 | else 88 | echo "Using KPP" 89 | usekpp=true 90 | fi 91 | fi 92 | 93 | if $usekpp; then 94 | # Directories to look for libfl.a in, separated by spaces 95 | libdirs="/usr/lib64 /usr/lib/ $WRF_KPP_LIBS" 96 | 97 | echo "export WRF_KPP=1" >> $outfile 98 | if ! $override; then 99 | doexit=false 100 | if [ -z "$YACC" ]; then 101 | yaccloc=$(which yacc) 102 | echo "export YACC=\"$yaccloc -d\"" >> $outfile 103 | else 104 | yaccarray=($YACC) # usually has the -d flag, which we need to remove before testing existance. 105 | if [ ! -f "${yaccarray[0]}" ]; then 106 | echo "Your value for YACC ($YACC) does not seem to point to a valid file." 107 | echo "Correct this, clear the YACC environmental variable, or use the override mode" 108 | echo "to force autowrfchem to find it." 109 | doexit=true 110 | else 111 | # This keeps the final check happy 112 | yaccloc="${yaccarray[0]}" 113 | fi 114 | fi 115 | if [ -z $FLEX_LIB_DIR ]; then 116 | for p in $libdirs; do 117 | flexloc=$(find $p -name libfl.a) 118 | if [ ! -z $flexloc ]; then 119 | flexloc=$(dirname $flexloc) 120 | echo "export FLEX_LIB_DIR=$flexloc" >> $outfile 121 | break 122 | fi 123 | done 124 | else 125 | if [ ! -f "$FLEX_LIB_DIR/libfl.a" ]; then 126 | echo "Your value for FLEX_LIB_DIR ($FLEX_LIB_DIR) does not seem to point to a directory with libfl.a." 127 | echo "Correct this, clear the FLEX_LIB_DIR environmental variable, or use the override mode" 128 | echo "to force autowrfchem to find it." 129 | doexit=true 130 | else 131 | # This keeps the final check happy 132 | flexloc="$FLEX_LIB_DIR" 133 | fi 134 | fi 135 | if $doexit; then 136 | echo "Aborting configuration" 137 | exit 1 138 | fi 139 | else 140 | yaccloc=$(which yacc) 141 | echo "export YACC=\"$yaccloc -d\"" >> $outfile 142 | 143 | for p in $libdirs; do 144 | flexloc=$(find $p -name libfl.a) 145 | if [ ! -z $flexloc ]; then 146 | flexloc=$(dirname $flexloc) 147 | echo "export FLEX_LIB_DIR=$flexloc" >> $outfile 148 | break 149 | fi 150 | done 151 | fi 152 | 153 | doexit=false 154 | # Check if any of the environmental variables failed to set 155 | if [ -z $yaccloc ]; then 156 | echo "Could not find an executable for YACC on your search path. Set env. var. YACC manually if needed." 157 | doexit=true 158 | fi 159 | if [ -z $flexloc ]; then 160 | echo "Could not find libfl.a in any of: $libdirs. Can add additional search paths (separated by spaces) as the env. var. WRF_KPP_LIBS." 161 | doexit=true 162 | fi 163 | 164 | if $doexit; then 165 | echo "One or more KPP variables could not be set (see previous messages). Configuration aborted." 166 | exit 1 167 | fi 168 | fi 169 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | This is free and unencumbered software released into the public domain. 2 | 3 | Anyone is free to copy, modify, publish, use, compile, sell, or 4 | distribute this software, either in source code form or as a compiled 5 | binary, for any purpose, commercial or non-commercial, and by any 6 | means. 7 | 8 | In jurisdictions that recognize copyright laws, the author or authors 9 | of this software dedicate any and all copyright interest in the 10 | software to the public domain. We make this dedication for the benefit 11 | of the public at large and to the detriment of our heirs and 12 | successors. We intend this dedication to be an overt act of 13 | relinquishment in perpetuity of all present and future rights to this 14 | software under copyright law. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 17 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 18 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 19 | IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR 20 | OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, 21 | ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 22 | OTHER DEALINGS IN THE SOFTWARE. 23 | 24 | For more information, please refer to 25 | -------------------------------------------------------------------------------- /PREPINPUT/bestemissyear.py: -------------------------------------------------------------------------------- 1 | # Given start and end dates for the run, figures out which year 2 | # best represents the emissions for it and prints out a start 3 | # date which can be used by the namelist program to set the 4 | # start date for convert_emiss.F 5 | from __future__ import print_function 6 | import datetime as dt 7 | import argparse 8 | import re 9 | import sys 10 | 11 | def shell_error(msg, exitcode=1): 12 | print('{0}: {1}'.format(__file__, msg), file=sys.stderr) 13 | exit(exitcode) 14 | 15 | def shell_warning(msg): 16 | print('{0}: {1}'.format(__file__, msg), file=sys.stderr) 17 | 18 | def get_args(): 19 | parser = argparse.ArgumentParser(description='Generate start dates for convert_emiss based on run start and end dates') 20 | parser.add_argument('zhr', help='String 00z or 12z, indicates which start time should be returned') 21 | parser.add_argument('startdate', help='Start date for model run in yyyy-mm-dd_HH:MM:SS format') 22 | parser.add_argument('enddate', help='End date for model run in yyyy-mm-dd_HH:MM:SS format') 23 | 24 | inputs = parser.parse_args() 25 | 26 | if inputs.zhr not in ['00z', '12z']: 27 | shell_error('zhr must be 00z or 12z') 28 | 29 | regex = "\d\d\d\d-\d\d-\d\d_\d\d:\d\d:\d\d" 30 | if not re.match(regex, inputs.startdate): 31 | shell_error("startdate '{0}' is not in yyyy-mm-dd_HH:MM:SS format".format(inputs.startdate)) 32 | if not re.match(regex, inputs.enddate): 33 | shell_error("enddate '{0}' is not in yyyy-mm-dd_HH:MM:SS format".format(inputs.enddate)) 34 | 35 | return inputs 36 | 37 | def str2datetime(s): 38 | return dt.datetime.strptime(s, '%Y-%m-%d_%H:%M:%S'); 39 | 40 | def calc_year(startdate, enddate, hr): 41 | sdate = str2datetime(startdate) 42 | edate = str2datetime(enddate) 43 | 44 | if edate < sdate: 45 | shell_error('enddate cannot be before startdate') 46 | 47 | if edate.year == sdate.year: 48 | return dt.datetime(year=sdate.year, month=sdate.month, day=sdate.day, hour=hr) 49 | elif edate.year - sdate.year == 1: 50 | # Choose the year that makes up the majority of the run. 51 | sdays = dt.datetime(sdate.year+1,1,1) - sdate 52 | edays = edate - dt.datetime(edate.year,1,1) 53 | if sdays > edays: 54 | em_date = sdate 55 | else: 56 | em_date = dt.datetime(year=edate.year, month=1, day=1) 57 | 58 | shell_warning('Start and end dates are 1 year apart, calculating emissions for majority year') 59 | return dt.datetime(year=em_date.year, month=em_date.month, day=em_date.day, hour=hr) 60 | else: 61 | tdel = edate - sdate 62 | tdel /= 2 63 | tmpdate = sdate + tdel 64 | yr = tmpdate.year 65 | 66 | shell_warning('Start and end dates are >= 2 years apart, calculating emissions for mean year') 67 | return dt.datetime(year=yr, month=1, day=1, hour=hr) 68 | 69 | def main(): 70 | args = get_args() 71 | hrdict = {'00z':0, '12z':12} 72 | em_date = calc_year(args.startdate, args.enddate, hrdict[args.zhr]) 73 | shell_warning('Emissions will use year {0}'.format(em_date.year)) 74 | print(em_date.strftime('%Y-%m-%d_%H:%M:%S')) 75 | 76 | if __name__ == '__main__': 77 | main() 78 | -------------------------------------------------------------------------------- /PREPINPUT/metgriblist.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | from __future__ import print_function 3 | import argparse 4 | import calendar 5 | import datetime as dt 6 | import re 7 | import sys 8 | 9 | import pdb 10 | __author__ = 'Josh' 11 | 12 | # This program generates a list of expected met GRIB files for a given time period 13 | # The time period should be specified in YYYY-MM-DD format from the command line. 14 | 15 | def print_help(): 16 | pass 17 | 18 | def narr_end_of_range_date(curr_date, ndays): 19 | # NARR date ranges are such that they do not cross months; if a range of days would 20 | # straddle two months, it is cut off at the end of that month. 21 | tdel = dt.timedelta(days=ndays-1) 22 | eor_date = curr_date + tdel 23 | eom_day = calendar.monthrange(curr_date.year, curr_date.month)[1] 24 | #pdb.set_trace() 25 | if eor_date.month != curr_date.month: 26 | # month range returns two values; the second is the number of days 27 | # in the given month 28 | eor_date = dt.date(curr_date.year, curr_date.month, eom_day) 29 | elif eom_day - eor_date.day <= 1 and eor_date.month != 2: 30 | # At the the 3D NARR files have an extra day if it would otherwise be the 31 | # day before the end of the month, except in February. (The algorithm NARR 32 | # seems to use to generate filenames/ranges is very obtuse. It seems like 33 | # they must've hand-specified ranges.) 34 | eor_date = eor_date.replace(day=eom_day) 35 | 36 | return eor_date 37 | 38 | 39 | def narr_next_sfc_date(curr_date): 40 | #pdb.set_trace() 41 | if curr_date.day >= 1 and curr_date.day <= 9: 42 | tdel = dt.timedelta(days=10-curr_date.day) 43 | elif curr_date.day >= 10 and curr_date.day <= 19: 44 | tdel = dt.timedelta(days=20-curr_date.day) 45 | else: 46 | eom_day = calendar.monthrange(curr_date.year, curr_date.month)[1] 47 | tdel = dt.timedelta(days=eom_day+1-curr_date.day) 48 | 49 | return curr_date + tdel 50 | 51 | 52 | def make_narr_grib_list(start_date, end_date): 53 | if (type(start_date) is not dt.datetime and type(start_date) is not dt.date or 54 | type(end_date) is not dt.datetime and type(end_date) is not dt.date): 55 | raise TypeError("start_date and end_date must be datetime or date objects") 56 | 57 | narr_files = [] 58 | narr_files += list_narr_grib_files(start_date, end_date, '3D') 59 | narr_files += list_narr_grib_files(start_date, end_date, 'RS.flx') 60 | narr_files += list_narr_grib_files(start_date, end_date, 'RS.sfc') 61 | 62 | return narr_files 63 | 64 | 65 | def list_narr_grib_files(start_date, end_date, suffix): 66 | # NARR files are every 3 hours, so make sure the start date is at the beginning of a three 67 | # hour block 68 | start_date = start_date.replace(minute=0, second=0, microsecond=0) 69 | start_hour = start_date.hour 70 | if start_hour % 3 != 0: 71 | start_date = start_date.replace(start_hour - (start_hour % 3)) 72 | 73 | file_pattern = 'merged_AWIP32.{date}.{suffix}' 74 | files = [] 75 | curr_date = start_date 76 | while curr_date <= end_date: 77 | files.append(file_pattern.format(date=curr_date.strftime('%Y%m%d%H'), suffix=suffix)) 78 | curr_date += dt.timedelta(hours=3) 79 | 80 | return files 81 | 82 | 83 | def make_narr_tar_list(start_date, end_date): 84 | if (type(start_date) is not dt.datetime and type(start_date) is not dt.date or 85 | type(end_date) is not dt.datetime and type(end_date) is not dt.date): 86 | raise TypeError("start_date and end_date must be datetime or date objects") 87 | 88 | narr_files = [] 89 | list_narr_tar_files(narr_files, start_date, end_date, "NARR3D_", 3) 90 | list_narr_tar_files(narr_files, start_date, end_date, "NARRflx_", 8) 91 | list_narr_sfc_tar_files(narr_files, start_date, end_date) 92 | 93 | return narr_files 94 | 95 | 96 | def list_narr_tar_files(narr_files, start_date, end_date, stem, ndays, extension="tar"): 97 | # NARR files come in multi day groups, the file names are of the format 98 | # NARRxxx_YYYYMM_SSEE.tar, where YYYY is the year, MM the month, SS the first 99 | # day of the three day group and EE the last. xxx varies depending on the file type, 100 | # 101 | # narr_files is the list to append to; start_ and end_ date are (of course) the 102 | # first and last day of the WRF run. stem is the file name up to the year. ndays 103 | # is how many days per group. extension is the file extension, defaults to tar. 104 | 105 | dom = start_date.day 106 | # Day of month must be a multiple of ndays plus 1 107 | while dom % ndays != 1: 108 | dom -= 1 109 | 110 | if stem == "NARR3D_" and dom == 31: 111 | # This kludge fixes a bug that occurs if the time period requested 112 | # starts on the 31st - 31 to 31 fits the rules that work for everything 113 | # else, but NARR3D files are produced for 28 to 31, not 28 to 30 and 31 to 31. 114 | dom = 28 115 | 116 | curr_date = dt.date(start_date.year, start_date.month, dom) 117 | while curr_date <= end_date: 118 | eor_date = narr_end_of_range_date(curr_date, ndays) 119 | fname = "{namestem}{year:04}{month:02}_{sday:02}{eday:02}.{ext}".format( 120 | namestem = stem, year = curr_date.year, month=curr_date.month, sday=curr_date.day, eday=eor_date.day, 121 | ext=extension 122 | ) 123 | narr_files.append(fname) 124 | curr_date = eor_date + dt.timedelta(days=1) 125 | if curr_date.day <= ndays: 126 | curr_date = curr_date.replace(day=1) 127 | 128 | 129 | def list_narr_sfc_tar_files(narr_files, start_date, end_date, extension="tar"): 130 | # The sfc files are annoying because they use the date ranges 1-9, 10-19, and 20-eom, rather than being consistent. 131 | # So of course they need special handling 132 | curr_date = start_date 133 | while curr_date <= end_date: 134 | if curr_date.day >= 1 and curr_date.day <= 9: 135 | drange = "0109" 136 | elif curr_date.day >= 10 and curr_date.day <= 19: 137 | drange = "1019" 138 | else: 139 | eom_day = calendar.monthrange(curr_date.year, curr_date.month)[1] 140 | drange = "20{:02}".format(eom_day) 141 | 142 | fname = "NARRsfc_{year:04}{month:02}_{days}.{ext}".format( 143 | year=curr_date.year, month=curr_date.month, days=drange, ext=extension 144 | ) 145 | narr_files.append(fname) 146 | curr_date = narr_next_sfc_date(curr_date) 147 | 148 | def parse_args(): 149 | allowed_mets = ["narr"] 150 | parser = argparse.ArgumentParser(description='Generate the list of meteorology files expected by WPS for a given ' 151 | 'date range (either the .tar archives or the actual GRIB files)', 152 | formatter_class=argparse.RawTextHelpFormatter) 153 | parser.add_argument("mettype", help="Which type of meteorology to generate file names for. Allowed values (case insensitive): {0}". 154 | format(", ".join(allowed_mets))) 155 | parser.add_argument("startdate", help="The first date of the WRF run, in yyyy-mm-dd_HH:MM:SS format") 156 | parser.add_argument("enddate", help="The last date of the WRF run, in yyyy-mm-dd_HH:MM:SS format") 157 | parser.add_argument("-l", action="store_const", const="\n", default=" ", 158 | help="Print the files one per line (rather than space delimited)") 159 | parser.add_argument("--grib", action="store_true", help="List the GRIB files, rather than the default .tar files") 160 | 161 | inputs = parser.parse_args() 162 | if inputs.mettype.lower() not in allowed_mets: 163 | print("{0} is not one of the allowed meteorologies (allowed mets: {1})".format( 164 | inputs.mettype, ", ".join(allowed_mets)), file=sys.stderr) 165 | exit(1) 166 | 167 | regex = "\d\d\d\d-\d\d-\d\d_\d\d:\d\d:\d\d" 168 | doexit = False 169 | if not re.match(regex, inputs.startdate): 170 | print("startdate '{0}' is not in yyyy-mm-dd_HH:MM:SS format".format(inputs.startdate), file=sys.stderr) 171 | doexit = True 172 | if not re.match(regex, inputs.enddate): 173 | print("enddate '{0}' is not in yyyy-mm-dd_HH:MM:SS format".format(inputs.enddate), file=sys.stderr) 174 | doexit = True 175 | 176 | if doexit: 177 | exit(1) 178 | 179 | start_datetime = dt.datetime.strptime(inputs.startdate, "%Y-%m-%d_%H:%M:%S") 180 | end_datetime = dt.datetime.strptime(inputs.enddate, "%Y-%m-%d_%H:%M:%S") 181 | return inputs.mettype.lower(), start_datetime, end_datetime, inputs.l, inputs.grib 182 | 183 | #### MAIN FUNCTION ##### 184 | if __name__ == "__main__": 185 | met, start_datetime, end_datetime, delim, do_grib = parse_args() 186 | if met == "narr": 187 | if do_grib: 188 | files = make_narr_grib_list(start_datetime, end_datetime) 189 | else: 190 | files = make_narr_tar_list(start_datetime.date(), end_datetime.date()) 191 | print(delim.join(files)) 192 | else: 193 | raise RuntimeError("No action specified for met type {0}".format(met)) 194 | 195 | exit(0) 196 | -------------------------------------------------------------------------------- /PREPINPUT/mozbc-chemopt-list.txt: -------------------------------------------------------------------------------- 1 | # Maps the various .inp files to the corresponding chem_opt value in the namelist 2 | # One mapping per line, so if an .inp file is used for multiple chem opts, each 3 | # needs its own line. Each line should have the format chemopt=file.inp 4 | # I have not tested all of these, in fact, any of them except 2 and 113. 5 | # Josh Laughner 19 Jul 2016 6 | 1=RADM2.inp 7 | 2=RADM2SORG.inp 8 | 5=NONE 9 | 6=NONE 10 | 7=CBMZ-MOSAIC_4bins.inp 11 | 8=CBMZ-MOSAIC_8bins.inp 12 | 9=CBMZ-MOSAIC_8bins.inp 13 | 10=CBMZ-MOSAIC_8bins.inp 14 | 11=RADM2SORG.inp 15 | 12=RACMSORG.inp 16 | 13=NONE 17 | 14=NONE 18 | 15=NONE 19 | 16=NONE 20 | 17=NONE 21 | 31=CBMZ-MOSAIC_4bins.inp 22 | 32=CBMZ-MOSAIC_4bins.inp 23 | 33=CBMZ-MOSAIC_8bins.inp 24 | 34=CBMZ-MOSAIC_8bins.inp 25 | 35=NONE 26 | 41=RADM2SORG.inp 27 | 42=RACMSORG.inp 28 | 43=NONE 29 | 101=RADM2.inp 30 | 102=RACM.inp 31 | 103=RACM.inp 32 | 104=RACM.inp 33 | 105=RACMSORG.inp 34 | 106=RADM2SORG.inp 35 | 107=RACMSORG.inp 36 | 108=NONE 37 | 110=NONE 38 | 111=NONE 39 | 112=MOZCART.inp 40 | 120=NONE 41 | 131=NONE 42 | 132=NONE 43 | 170=NONE 44 | 195=NONE 45 | 198=NONE 46 | 201=NONE 47 | 202=NONE 48 | 203=NONE 49 | 204=NONE 50 | 300=GOCART.inp 51 | 301=NONE 52 | 303=NONE 53 | 400=NONE 54 | 401=NONE 55 | 402=NONE 56 | 403=NONE 57 | 501=NONE 58 | 502=NONE 59 | 503=NONE 60 | 504=NONE 61 | 600=NONE 62 | 601=NONE 63 | 64 | # R2SMH mechanism added by Azimeh Zare using KPP 65 | 113=R2SMH.inp 66 | -------------------------------------------------------------------------------- /PREPINPUT/mozcheck.py: -------------------------------------------------------------------------------- 1 | # check that the given Mozart file covers the time range and the domain plus a 10 degree buffer 2 | from __future__ import print_function 3 | import argparse 4 | import datetime as dt 5 | import os 6 | import re 7 | import sys 8 | from netCDF4 import Dataset as ncdat 9 | 10 | wrf_dt_fmt = '%Y-%m-%d_%H:%M:%S' 11 | 12 | def shell_error(msg, exitcode=1): 13 | print('{0}:\n\t{1}'.format(__file__, msg), file=sys.stderr) 14 | exit(exitcode) 15 | 16 | def shell_warning(msg): 17 | print('{0}:\n\t{1}'.format(__file__, msg), file=sys.stderr) 18 | 19 | def get_args(): 20 | parser = argparse.ArgumentParser(description='Check the chosen MOZBC file satisfies the necessary conditions for the run') 21 | parser.add_argument('moz_file',help='MOZBC netCDF file') 22 | parser.add_argument('wrfin_file',help='wrfinput_dxx file') 23 | parser.add_argument('startdate',help='Start date for the whole WRF run') 24 | parser.add_argument('enddate',help='End date for the whole WRF run') 25 | 26 | args = parser.parse_args() 27 | 28 | 29 | wrfin_re = 'wrfinput_d\d+' 30 | date_re = '\d\d\d\d-\d\d-\d\d_\d\d:\d\d:\d\d' 31 | 32 | if not os.path.isfile(args.wrfin_file): 33 | shell_error('{0} does not exist'.format(args.wrfin_file)) 34 | elif not re.match(wrfin_re, os.path.basename(args.wrfin_file)): 35 | shell_error('{0} does not appear to be a wrfinput file (base name did not match regular expression {1})'.format(wrfin_file, wrfin_re)) 36 | 37 | if not re.match(date_re, args.startdate): 38 | shell_error('Start date is not in yyyy-mm-dd_HH:MM:SS format') 39 | if not re.match(date_re, args.enddate): 40 | shell_error('End date is not in yyyy-mm-dd_HH:MM:SS format') 41 | 42 | return args 43 | 44 | def get_run_info(wrfin, stime_str, etime_str): 45 | in_rgrp = ncdat(wrfin) 46 | xlon = in_rgrp.variables['XLONG'][:] 47 | xlat = in_rgrp.variables['XLAT'][:] 48 | in_rgrp.close() 49 | 50 | sdate = dt.datetime.strptime(stime_str, wrf_dt_fmt) 51 | edate = dt.datetime.strptime(etime_str, wrf_dt_fmt) 52 | 53 | return (xlon.min(), xlon.max()), (xlat.min(), xlat.max()), (sdate, edate) 54 | 55 | def convert_moz_date(dateint, datesec): 56 | # MOZ dates are given as an integer with 8 digits. The first four are year, then two month, then two for day 57 | # Some arithmetic sneakiness to break it apart 58 | yr = dateint/10000 59 | mn = (dateint % 10000)/100 60 | dy = dateint % 100 61 | 62 | hour = datesec / 3600 63 | minute = (datesec % 3600)/60 64 | sec = datesec % 60 65 | return dt.datetime(year=yr, month=mn, day=dy, hour=hour, minute=minute, second=sec) 66 | 67 | def get_moz_info(mozfile): 68 | m_rgrp = ncdat(mozfile) 69 | lon = m_rgrp.variables['lon'][:] 70 | lon[lon>180] -= 360 # MOZBC files give longitude as degrees east (so 200 in MOZ = -160 in WRF) 71 | lat = m_rgrp.variables['lat'][:] 72 | mdate = m_rgrp.variables['date'][:] 73 | mdatesec = m_rgrp.variables['datesec'][:] 74 | 75 | moz_st_dt = convert_moz_date(mdate[0], mdatesec[0]) 76 | moz_end_dt = convert_moz_date(mdate[-1], mdatesec[-1]) 77 | return (lon.min(), lon.max()), (lat.min(), lat.max()), (moz_st_dt, moz_end_dt) 78 | 79 | def moz_validation(mozfile, wrfin, wrf_startdate, wrf_enddate): 80 | latlon_buffer = 10 81 | 82 | wrf_lonlim, wrf_latlim, wrf_timelim = get_run_info(wrfin, wrf_startdate, wrf_enddate) 83 | moz_lonlim, moz_latlim, moz_timelim = get_moz_info(mozfile) 84 | 85 | exitcode = 0 86 | 87 | geo_msg = 'Warning: {0} MOZ border does not have a {1} degree buffer from the nearest WRF border\n\t{2}' 88 | if moz_lonlim[0] - wrf_lonlim[0] > -latlon_buffer: 89 | line2 = '(MOZ min lon = {0}, WRF min lon = {1})'.format(moz_lonlim[0], wrf_lonlim[0]) 90 | shell_warning(geo_msg.format('West', latlon_buffer, line2)) 91 | exitcode = 1 92 | if moz_lonlim[1] - wrf_lonlim[1] < latlon_buffer: 93 | line2 = '(MOZ max lon = {0}, WRF max lon = {1})'.format(moz_lonlim[1], wrf_lonlim[1]) 94 | shell_warning(geo_msg.format('East', latlon_buffer, line2)) 95 | exitcode = 1 96 | if moz_latlim[0] - wrf_latlim[0] > -latlon_buffer: 97 | line2 = '(MOZ min lat = {0}, WRF min lat = {1})'.format(moz_latlim[0], wrf_latlim[0]) 98 | shell_warning(geo_msg.format('South', latlon_buffer, line2)) 99 | exitcode = 1 100 | if moz_latlim[1] - wrf_latlim[1] < latlon_buffer: 101 | line2 = '(MOZ max lat = {0}, WRF max lat = {1})'.format(moz_latlim[1], wrf_latlim[1]) 102 | shell_warning(geo_msg.format('North', latlon_buffer, line2)) 103 | exitcode = 1 104 | if moz_timelim[0] > wrf_timelim[0]: 105 | line2 = '(MOZ first time = {0}, WRF first time = {1})'.format(moz_timelim[0], wrf_timelim[0]) 106 | shell_warning('First time in MOZ file is after start time of WRF run\n\t{0}'.format(line2)) 107 | exitcode = 1 108 | if moz_timelim[1] < wrf_timelim[1]: 109 | line2 = '(MOZ last time = {0}, WRF last time = {1})'.format(moz_timelim[1], wrf_timelim[1]) 110 | shell_warning('Last time in MOZ file is before end time of WRF run\n\t{0}'.format(line2)) 111 | 112 | return exitcode 113 | 114 | def main(): 115 | args = get_args() 116 | ecode = moz_validation(args.moz_file, args.wrfin_file, args.startdate, args.enddate) 117 | exit(ecode) 118 | 119 | if __name__ == '__main__': 120 | main() 121 | -------------------------------------------------------------------------------- /PREPINPUT/postcheck.py: -------------------------------------------------------------------------------- 1 | #!/bin/env python 2 | 3 | # This will spot check the various input files and make sure that 4 | # things seem to be ok. Check that wrfinput_d01 and wrfbdy_d01 have 5 | # non-zero values for no, no2, o3, co. 6 | # 7 | # Should be executed in the WRFV3/run directory 8 | # 9 | # Exit codes: 1 bit = some problem (summary) 10 | # 2 bit = problem with wrfinput 11 | # 4 bit: on = wrfinput missing variables, off = variables below min threshold 12 | # 8 bit = problem with wrfbdy 13 | # 16 bit: on = wrfbdy missing variables, off = variables below min threshold 14 | from __future__ import print_function 15 | import pdb 16 | import argparse 17 | import re 18 | import numpy as np 19 | import os 20 | import sys 21 | from warnings import warn 22 | try: 23 | from netCDF4 import Dataset as ncdat 24 | use_ncdump = False 25 | except ImportError: 26 | import pyncdf as pync 27 | use_ncdump = True 28 | warn('package netCDF4 not found, using pyncdf. Some operations may not be possible or will be limited.') 29 | 30 | min_nonzero_conc = 1e-15 31 | 32 | def __shell_error(msg, exitcode=1): 33 | print(msg, file=sys.stderr) 34 | exit(exitcode) 35 | 36 | def get_var_names(filename): 37 | if use_ncdump: 38 | return pync.call_ncdump_varnames(filename) 39 | else: 40 | rgrp = ncdat(filename) 41 | return rgrp.variables.keys() 42 | rgrp.close() 43 | 44 | def get_var_values(varnames, filename): 45 | if use_ncdump: 46 | var_vals = pync.call_ncdump_vals(varnames, filename) 47 | else: 48 | var_vals = dict() 49 | rgrp = ncdat(filename) 50 | for var in varnames: 51 | var_vals[var] = rgrp.variables[var][:] 52 | rgrp.close() 53 | return var_vals 54 | 55 | 56 | def check_vars(varnames, filename): 57 | print('Checking {0} in {1}'.format(', '.join(varnames), filename)) 58 | filevars = get_var_names(filename) 59 | 60 | ecode = 0 61 | 62 | for var in varnames: 63 | if var not in filevars: 64 | print(' WARNING: {0} not present in {1}'.format(var, filename)) 65 | ecode = 6 66 | 67 | if ecode > 0: 68 | return ecode 69 | vals = get_var_values(varnames, filename) 70 | for var in varnames: 71 | varmean = np.nanmean(vals[var]) 72 | varmed = np.nanmedian(vals[var]) 73 | varmin = np.nanmin(vals[var]) 74 | varmax = np.nanmax(vals[var]) 75 | statstr = 'mean = {0}, median = {1}, min = {2}, max = {3}'.format(varmean, varmed, varmin, varmax) 76 | if abs(varmed) < min_nonzero_conc: 77 | print(' WARNING: |median({0})| < {1} ({2})'.format(var, min_nonzero_conc, statstr)) 78 | ecode = 2 79 | else: 80 | print(' {0}: {1}'.format(var, statstr)) 81 | 82 | return ecode 83 | 84 | def check_input_bdy(wrfin_file, wrfbdy_file, invars): 85 | ecode_in = check_vars(invars, wrfin_file) 86 | 87 | bdyvars = [] 88 | bdy_ncdump_lim = {var: True for var in invars} # used to check just one boundary variable per species if using ncdump 89 | allbdyvars = get_var_names(wrfbdy_file) 90 | restr = '(' + '|'.join(invars) + ')(?=_)' 91 | for v in allbdyvars: 92 | match = re.match(restr,v) 93 | if not use_ncdump and match: 94 | bdyvars.append(v) 95 | elif match and bdy_ncdump_lim[match.group()]: 96 | bdy_ncdump_lim[match.group()] = False 97 | bdyvars.append(v) 98 | 99 | ecode_bdy = check_vars(bdyvars, wrfbdy_file) 100 | 101 | ecode = ecode_in | ecode_bdy << 2 102 | 103 | return ecode 104 | 105 | def get_args(): 106 | parser = argparse.ArgumentParser(description='Check the results of WRF-Chem input preparation') 107 | parser.add_argument('wrfinput_file',help='Path to the wrfinput_dXX file to check') 108 | parser.add_argument('wrfbdy_file',help='Path to the wrfbdy_dXX file to check') 109 | parser.add_argument('--mode',default='chem',help='Mode of operation, can be "met", "chem", or "both". Default is "chem"') 110 | 111 | args = parser.parse_args() 112 | 113 | wrfin_file = args.wrfinput_file 114 | wrfbdy_file = args.wrfbdy_file 115 | 116 | wrfin_re = 'wrfinput_d\d+' 117 | wrfbdy_re = 'wrfbdy_d\d+' 118 | 119 | 120 | if not os.path.isfile(wrfin_file): 121 | __shell_error('{0} does not exist'.format(wrfin_file)) 122 | elif not re.match(wrfin_re, os.path.basename(wrfin_file)): 123 | __shell_error('{0} does not appear to be a wrfinput file (base name did not match regular expression {1})'.format(wrfin_file, wrfin_re)) 124 | 125 | if not os.path.isfile(wrfbdy_file): 126 | __shell_error('{0} does not exist'.format(wrfbdy_file)) 127 | elif not re.match(wrfbdy_re, os.path.basename(wrfbdy_file)): 128 | __shell_error('{0} does not appear to be a wrfbdy file (base name did not match regular expression {1})'.format(wrfbdy_file, wrfbdy_re)) 129 | 130 | return args 131 | 132 | def main(): 133 | args = get_args() 134 | 135 | metvars = ['U','V','T'] 136 | chemvars = ['no','no2','o3','co'] 137 | if args.mode.lower() == 'chem': 138 | checkvars = chemvars 139 | elif args.mode.lower() == 'met': 140 | checkvars = metvars 141 | elif args.mode.lower() == 'both': 142 | checkvars = chemvars + metvars 143 | else: 144 | __shell_error('{0} is not a recognized value for --mode'.format(args.mode)) 145 | ecode = check_input_bdy(args.wrfinput_file, args.wrfbdy_file, checkvars) 146 | exit(ecode) 147 | 148 | if __name__ == '__main__': 149 | main() 150 | -------------------------------------------------------------------------------- /PREPINPUT/precheck: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # Input Parsing 4 | 5 | metonly=false 6 | chemonly=false 7 | 8 | while [[ $# -gt 0 ]]; do 9 | case $1 in 10 | --met-only) 11 | metonly=true 12 | ;; 13 | --chem-only) 14 | chemonly=true 15 | ;; 16 | esac 17 | shift 18 | done 19 | 20 | # End Input parsing 21 | 22 | 23 | # Reference this script 24 | cd "$(dirname $0)" 25 | mydir="$(pwd -P)" 26 | rootdir="$(cd ../..; pwd -P)" 27 | pyprog="$mydir/../CONFIG/autowrf_namelist_main.py" 28 | 29 | # Ensure the PREPLOGS directory exists 30 | if [ ! -d "$mydir/../PREPLOGS" ]; then 31 | mkdir "$mydir/../PREPLOGS" 32 | fi 33 | 34 | # Reset the namelist to default state 35 | python "$pyprog" tempmod 36 | 37 | # Verify that the settings needed for NEI and MEGAN are present in the current namelist 38 | badopts=false 39 | 40 | if ! $metonly; then 41 | # Check various options needed for NEI to work 42 | python $pyprog check-wrf-opt --io_form_auxinput5=2,11 --io_style_emissions=1 --emiss_inpt_opt=1 43 | optchk=$? 44 | if [ $optchk -gt 0 ]; then 45 | echo "" 46 | echo "One or more of the namelist options expected if using NEI are incorrect." 47 | echo "Run 'autowrfchem config namelist' and use the NEI compatibility checker" 48 | echo "on the current namelist." 49 | badopts=true 50 | fi 51 | 52 | python $pyprog check-wps-opt --map_proj=lambert,polar 53 | optchk=$? 54 | if [ $optchk -gt 0 ]; then 55 | echo "" 56 | echo "NEI emissions only work with lambert or polar map projections. If trying to use a different" 57 | echo "map projection with custom emissions, you may run autowrfchem prepinpt with the" 58 | echo "'met-only' flag, which will only produce meteorology. You will need to supply the" 59 | echo "remaining inputs manually." 60 | badopts=true 61 | fi 62 | 63 | # Do the same for MEGAN 64 | python $pyprog check_wrf_opt --io_form_auxinput6=2,11 --bio_emiss_opt=3 65 | optchk=$? 66 | if [ $optchk -ne 0 ]; then 67 | echo "" 68 | echo "To use MEGAN for biogenic emissions, you must have io_form_auxinput6 set" 69 | echo "to 2 or 11 under the &time_control section of the WRF namelist (11 will" 70 | echo "only work if parallel netCDF is installed) and bio_emiss_opt set to 3" 71 | echo "under the &chem section of the namelist. One or both of these is not" 72 | echo "true. Correct them and rerun the input preparation." 73 | badopts=true 74 | fi 75 | fi 76 | 77 | # Make sure that the met directory exists as does the mozbc file and the 78 | # MEGAN and NEI data 79 | if [ -f "$mydir/../wrfbuild.cfg" ]; then 80 | . "$mydir/../wrfbuild.cfg" # source the file with metType, metDir, and mozbcFile in it 81 | else 82 | echo "wrfbuild.cfg does not exist. Run autowrfchem config at least once to generate it." 83 | exit 1 84 | fi 85 | 86 | # We'll need these a couple places 87 | wrfstart="$(python $pyprog get-wrf-opt --start-date)" 88 | wrfend="$(python $pyprog get-wrf-opt --end-date)" 89 | 90 | if ! $chemonly; then 91 | # Check the met directory from WPS since that's where it'll be called from 92 | cd "$rootdir/WPS" 93 | wpsdir="$(pwd -P)" 94 | if [ ! -d "$metDir" ]; then 95 | echo "" 96 | echo "The directory supposedly containing the meteorology data" 97 | echo "(metDir = $metDir)" 98 | echo "does not exist. Fix it in $mydir/../wrfbuild.cfg." 99 | badopts=true 100 | nometdir=true 101 | else 102 | nometdir=false 103 | fi 104 | 105 | # Verify that the raw met files needed are present in the met directory 106 | if ! $nometdir; then 107 | # Check if the met directory has untarred GRIB files or tar archives 108 | if [[ $metType == 'NARR' ]]; then 109 | grib_prefix='merged_AWIP32' 110 | else 111 | echo "Met type '$metType' not recognized" 112 | fi 113 | 114 | ls "${metDir}/${grib_prefix}"* >& /dev/null 115 | if [[ $? != 0 ]]; then 116 | look_for_grib="" 117 | else 118 | look_for_grib="--grib" 119 | fi 120 | 121 | reqmetfiles=$(python "$mydir/metgriblist.py" $look_for_grib "$metType" "$wrfstart" "$wrfend") 122 | cd "$metDir" 123 | missingMetFiles="" 124 | for metfile in $reqmetfiles; do 125 | if [[ ! -f $metfile ]]; then 126 | missingMetFiles="$missingMetFiles $metfile" 127 | fi 128 | done 129 | 130 | if [[ ! -z $missingMetFiles ]]; then 131 | echo "The following expected met files could not be found in" 132 | echo "$metDir" 133 | for f in $missingMetFiles; do 134 | echo " $f" 135 | done 136 | badopts=true 137 | else 138 | echo "Pass: All raw met files found" 139 | fi 140 | fi 141 | 142 | # Need to go back to WPS directory so the relative paths work 143 | cd "$wpsdir" 144 | 145 | # Also check the geog_data_path and constants file 146 | geogpath="`python $pyprog get-wps-opt --no-quotes --geog_data_path`" 147 | if [ $? -ne 0 ]; then 148 | echo "" 149 | echo "Fail: The option 'geog_data_path' is not specified in the WPS namelist, and it should be." 150 | badopts=true 151 | elif [ ! -d "$geogpath" ]; then 152 | echo "" 153 | echo "Fail: The WPS geog_data_path $geogpath is not a directory." 154 | badopts=true 155 | else: 156 | echo "Pass: WPS geog_data_path is a directory" 157 | fi 158 | constfile="`python $pyprog get-wps-opt --no-quotes --constants_name`" 159 | if [ $? -ne 0 ]; then 160 | echo "" 161 | echo "Fail: The option 'constants_name' is not specified in the WPS namelist, and it should be." 162 | badopts=true 163 | elif [ ! -f "$constfile" ]; then 164 | echo "" 165 | echo "Fail: The WPS constants file $constfile does not exist." 166 | badopts=true 167 | else 168 | echo "Pass: WPS constants file found." 169 | fi 170 | fi 171 | 172 | if ! $metonly; then 173 | cd "$rootdir/NEI" 174 | ls 2011/area4k >& /dev/null 175 | areatst=$? 176 | ls 2011/point >& /dev/null 177 | pointtst=$? 178 | if [ $areatst -ne 0 -o $pointtst -ne 0 ]; then 179 | echo "" 180 | echo "Fail: Could not find 2011/area4k and/or 2011/point in NEI." 181 | echo "Is the directory 2011 present or linked there?" 182 | echo "Does it contain area4k and point as immediate subdirectories?" 183 | badopts=true 184 | else 185 | echo "" 186 | echo "Pass: Found NEI/2011/area4k and NEI/2011/point" 187 | fi 188 | # Check 2005 under certain conditions? 189 | 190 | cd "$rootdir/MEGAN" 191 | ls data/lai* >& /dev/null 192 | megtst=$? 193 | if [ $megtst -ne 0 ]; then 194 | echo "" 195 | echo "Fail: Could not find any lai* files under data/ in MEGAN." 196 | echo "Is data a directory in MEGAN that contains the MEGAN data," 197 | echo "or a link to such a directory?" 198 | badopts=true 199 | else 200 | echo "" 201 | echo "Pass: Found lai* files under MEGAN/data." 202 | fi 203 | 204 | # Now check the MOZBC file 205 | cd "$rootdir/MOZBC" 206 | if [ -z $mozbcFile ]; then 207 | echo "" 208 | echo "mozbcFile has not been set. Please run 'autowrfchem config namelist' and use the 'Select MOZBC File' option" 209 | badopts=true 210 | elif [ ! -f "data/$mozbcFile" ]; then 211 | echo "" 212 | echo "Cannot find data/$mozbcFile" 213 | echo "from the MOZBC directory. Is the data directory or link present?" 214 | echo "If so, is the file directly in that directory (not a subdirectory?)" 215 | badopts=true 216 | fi 217 | 218 | echo "" 219 | if [ ! -f "$rootdir/WRFV3/run/wrfinput_d01" ]; then 220 | echo "wrfinput_d01 not created yet. Holding off on checking MOZ file." 221 | else 222 | python "$mydir/mozcheck.py" "$rootdir/MOZBC/data/$mozbcFile" "$rootdir/WRFV3/run/wrfinput_d01" "$wrfstart" "$wrfend" 223 | moztst=$? 224 | if [ $moztst -ne 0 ]; then 225 | echo "" 226 | echo "Fail: See above problems with MOZBC input file." 227 | echo "Choose a different file with autowrfchem config namelist." 228 | badopts=true 229 | else 230 | echo "Pass: MOZBC meets safety check (10 deg buffer around domain, time period checked)" 231 | fi 232 | fi 233 | fi 234 | 235 | if $badopts; then 236 | echo "" 237 | echo "One or more options or settings are incorrect, aborting input preparation" 238 | exit 1 239 | else 240 | exit 0 241 | fi 242 | -------------------------------------------------------------------------------- /PREPINPUT/prepmegan: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # This too will have several steps. First we need to rerun 3 | # real for the whole time, making sure bio_emiss_opt is still 0, 4 | # but that it will be reset to 3 when the regular run is done 5 | # and that io_form_auxinput6 is set to 2 or 11. 6 | 7 | cd "`dirname $0`" 8 | mydir="`pwd -P`" 9 | pyprog="$mydir/../CONFIG/autowrf_namelist_main.py" 10 | 11 | if [[ $FDDA_ON == 1 ]]; then 12 | fdda_args="--grid_fdda=0" 13 | else 14 | # Just being explicit, since an unassigned variable 15 | # would also return an empty string 16 | fdda_args="" 17 | fi 18 | python $pyprog tempmod --bio_emiss_opt=0 $fdda_args 19 | cd ../../WRFV3/run 20 | 21 | if [ -f wrfbiochemi_d01 ]; then 22 | mv wrfbiochemi_d01 wrfbiochemi_d01.autowrf-backup 23 | fi 24 | 25 | # Double check that the namelist is linked to the one in the CONFIG directory. Backup 26 | # only if not already a link to somewhere 27 | if [ -f namelist.input ]; then 28 | lnk=`readlink namelist.input` 29 | if [ ! -z $lnk ]; then 30 | mv namelist.input namelist.input.autowrf-backup 31 | fi 32 | fi 33 | ln -s "$mydir/../CONFIG/namelist.input" 34 | 35 | ./real.exe 36 | realexit=$? 37 | if [ $realexit -ne 0 ]; then 38 | echo "real.exe failed with exit code $realexit when running the full period in preparation for" 39 | echo "MEGAN. You should run it directly (with mpirun if compiled in parallel) and check any" 40 | echo "output to identify the problem" 41 | exit 1 42 | fi 43 | 44 | cd ../../MEGAN/src 45 | ./megan_bio_emiss < namelist.megan >& "$mydir/../PREPLOGS/megan.log" 46 | meganexit=$? 47 | if [ $meganexit -ne 0 ]; then 48 | echo "MEGAN failed with exit code $meganexit. Review megan.log in $mydir/../PREPLOGS to determine" 49 | echo "the cause." 50 | exit $meganexit 51 | fi 52 | 53 | if [ ! -f wrfbiochemi_d01 ]; then 54 | echo "MEGAN failed to produce wrfbiochemi_d01. Review megan.log in $mydir/../PREPLOGS to determine" 55 | echo "the cause." 56 | exit 1 57 | fi 58 | 59 | if [ -f "../../WRFV3/run/wrfbiochemi_d01" ]; then 60 | mv "../../WRFV3/run/wrfbiochemi_d01{,.autowrf-backup}" 61 | fi 62 | 63 | cp wrfbiochemi_d01 ../../WRFV3/run/ 64 | exit 0 65 | -------------------------------------------------------------------------------- /PREPINPUT/prepmozbc: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # Last one: this one needs to restore the full namelist, then 3 | # run real for the entire time period with biogenic emissions. 4 | # The only annoying part will be choosing which input file to 5 | # use. That will need to retrieve the value for chem_opt and 6 | # know which .inp file goes with which mechanism. 7 | # 8 | # For expandability, those mappings are included in a text file 9 | # that this script could parse. If each line was of the format 10 | # number=file, i.e. 2=RADM2SORG.inp, then this could be done 11 | # with i=`expr index "$line" "="` and ${line:i} 12 | 13 | cd `dirname $0` 14 | mydir=`pwd -P` 15 | rootdir=$(cd ../..; pwd -P) 16 | pyprog="$mydir/../CONFIG/autowrf_namelist_main.py" 17 | chemopt=`python $pyprog get-wrf-opt --chem_opt` 18 | 19 | if [ -z "$chemopt" ]; then 20 | echo "Failed to obtain the value of chem_opt. I do not know why that happened." 21 | echo "Aborting MOZBC" 22 | exit 1 23 | fi 24 | 25 | . "$mydir/../wrfbuild.cfg" # this will get the variable mozbcFile 26 | if [ -z $mozbcFile ]; then 27 | echo "No mozbc file set in wrfbuild.cfg! Use autowrfchem config namelist to choose one." 28 | echo "Aborting MOZBC" 29 | exit 1 30 | fi 31 | 32 | # Find out if the file is a GEOS-chem derived file. This ungodly long command prints the global attributes 33 | # for the file, finds the one called 'title', and cuts the string down to its value, trimming spaces at the 34 | # beginning and end. The grep specifically looks for "title" after "Global attribute n:" where n is a 1-4 35 | # digit number to ensure that we are getting the title attribute and not some other attribute line that happens 36 | # to include the string "title" 37 | echo "$mozbcFile" 38 | mozfiletitle=$(ncks -M "$rootdir/MOZBC/data/$mozbcFile" | grep -E 'Global attribute [[:digit:]]{1,4}: title' | cut -f 3 -d '=' | sed -e 's/^[[:space:]]*//' -e 's/[[:space:]]*$//') 39 | 40 | listfile="mozbc-chemopt-list.txt" 41 | while read -r line; do 42 | if [ -z "$line" ]; then 43 | continue 44 | elif [ ${line:0:1} == "#" ]; then 45 | continue 46 | fi 47 | 48 | i=`expr index "$line" "="` 49 | if [ $i -eq 0 ]; then 50 | continue 51 | fi 52 | 53 | lopt=${line:0:$((i-1))} 54 | fname=${line:i} 55 | if [ $lopt -eq $chemopt ]; then 56 | mozfile=$fname 57 | break 58 | fi 59 | done < "$listfile" 60 | 61 | if [ -z "$mozfile" ]; then 62 | echo "No input file specified for chem_opt=$chemopt in $listfile" 63 | echo "You should edit the list file to specify the correct input" 64 | echo "file for your chemistry. If there isn't one, specify the file" 65 | echo "as NONE." 66 | echo "Aborting MOZBC" 67 | exit 1 68 | elif [ "$mozfile" == "NONE" ]; then 69 | echo "No MOZART --> WRF-Chem mapping available for chem_opt=$chemopt" 70 | echo "Skipping MOZBC." 71 | exit 0 72 | fi 73 | 74 | # If the netCDF file is a GEOS-Chem derived file, append -GC to the input file 75 | # name. Perhaps this should instead allow the user to pick the file out of a 76 | # comma separated list in the list file? 77 | if [[ $mozfiletitle == "GEOS-Chem" ]]; then 78 | oldifs=$IFS # storing this to reset may be unecessary, but seems safer 79 | IFS='.', read fstem fext <<< "$mozfile" 80 | IFS=$oldifs 81 | mozfile="${fstem}-GC.${fext}" 82 | echo "netCDF file $mozbcFile looks to be derived from GEOS-Chem with gc2moz.py" 83 | fi 84 | 85 | echo "Using $mozfile as the input" 86 | 87 | # Double check that the input file exists BEFORE we run real (why waste time if 88 | # it's not going to work?) 89 | cd ../../MOZBC/src 90 | if [ ! -f "$mozfile" ]; then 91 | echo "$mozfile (the specified .inp file for chem_opt=$chemopt does not exist." 92 | echo "Aborting MOZBC" 93 | exit 1 94 | fi 95 | 96 | # Modify the input file to have the proper data file 97 | oldline=`grep -- "fn_moz" "$mozfile"` 98 | newline="fn_moz = '$mozbcFile'" 99 | 100 | perl -pi -e "s/$oldline/$newline/g" $mozfile 101 | 102 | cd ../../WRFV3/run 103 | # Ensure that the namelist is back to the regular options 104 | python $pyprog tempmod 105 | # Then double check that the NEI and MEGAN outputs are here 106 | missingfiles=false 107 | if [ ! -f "wrfchemi_00z_d01" -o ! -f "wrfchemi_12z_d01" ]; then 108 | echo "One or both of wrfchemi_00z_d01, wrfchemi_12z_d01 are not present in run directory" 109 | missingfiles=true 110 | fi 111 | if [ ! -f "wrfbiochemi_d01" ]; then 112 | echo "wrfbiochemi_d01 is not present in run directory" 113 | missingfiles=true 114 | fi 115 | if $missingfiles; then 116 | echo "Aborting MOZBC because previous input preparation steps failed." 117 | echo "" 118 | exit 1 119 | fi 120 | 121 | # Also double check the grid_fdda setting. if $FDDA_ON was set in the main prepinpt script, 122 | # then grid_fdda should be on here. If not, something weird is going on. 123 | if [[ $FDDA_ON == 1 ]]; then 124 | python $pyprog check-wrf-opt --grid_fdda=1 125 | fdda_check=$? 126 | if [[ $fdda_check == 1 ]]; then 127 | echo "Problem with prepmozbc: the main prepinpt script found that grid_fdda==1 in namelist.input" 128 | echo "at the beginning of the input prep run and set the env. variable FDDA_ON to 1, but grid_fdda" 129 | echo "did not get returned to 1 in the namelist before running the last instance of real.exe" 130 | echo "Aborting MOZBC" 131 | exit 1 132 | elif [[ $fdda_check == 2 ]]; then 133 | echo "Problem with prepmozbc: the main prepinpt script found that grid_fdda==1 in namelist.input" 134 | echo "at the beginning of the input prep run and set the env. variable FDDA_ON to 1, but grid_fdda" 135 | echo "does not exist in the namelist now." 136 | echo "Aborting MOZBC" 137 | exit 1 138 | elif [[ $fdda_check != 0 ]]; then 139 | echo "Problem with prepmozbc: checking for the value of grid_fdda failed with an exit code of $fdda_check" 140 | echo "Typically, the only exit codes should be 0 (option is correct), 1 (option is wrong), or 2" 141 | echo "(option not found in namelist). Has $pyprog" 142 | echo "been updated since the last time prepmozbc was edited?" 143 | echo "Aborting MOZBC" 144 | exit 1 145 | fi 146 | fi 147 | 148 | ./real.exe 149 | realexit=$? 150 | if [ $realexit -ne 0 ]; then 151 | echo "real.exe failed with exit code $realexit while running the full time period before" 152 | echo "running MOZBC. You should run real.exe manually (with mpirun, if WRF is compiled in" 153 | echo "parallel) and check the output to determine the cause." 154 | echo "Aborting MOZBC" 155 | exit 1 156 | fi 157 | 158 | # Check that the chosen MOZART file satisfies the buffer size around the WRF domain as well as the time period 159 | # needed. This needs to happen after running real.exe to ensure that the results are consistent with the 160 | # final version of wrfinput 161 | wrfstart="$(python $pyprog get-wrf-opt --start-date)" 162 | wrfend="$(python $pyprog get-wrf-opt --end-date)" 163 | python "$mydir/mozcheck.py" "$rootdir/MOZBC/data/$mozbcFile" "$rootdir/WRFV3/run/wrfinput_d01" "$wrfend" "$wrfstart" 164 | moztst=$? 165 | if [ $moztst -ne 0 ]; then 166 | echo "" 167 | echo "prepmozbc: See above problems with MOZBC input file." 168 | echo "Choose a different file with autowrfchem config namelist." 169 | echo "Aborting MOZBC" 170 | exit 1 171 | fi 172 | 173 | cd ../../MOZBC/src 174 | ./mozbc < "$mozfile" >& "$mydir/../PREPLOGS/mozbc.log" 175 | mozexit=$? 176 | if [ $mozexit -ne 0 ]; then 177 | echo "MOZBC failed with exit code ${mozexit}. You should run mozbc manually (with the command" 178 | echo "./mozbc < $mozfile in MOZBC/src) to determine the cause. If the cause is a missing variable" 179 | echo "(WRF or MOZART), it would be a good idea to run mozbc manually until you're sure that" 180 | echo "all missing variables have been dealt with." 181 | exit 1 182 | else 183 | exit 0 184 | fi 185 | -------------------------------------------------------------------------------- /PREPINPUT/prepnei_convert: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # THIS one will be somewhat complicated. It needs to 3 | # 1) reset the WRF namelist to the first 12 hours of the time period and turn off bio emissions 4 | # 2) run real, then convert_emiss.exe 5 | # 3) reset the namelist to the second 12 hours of the time period 6 | # 4) run real then convert_emiss.exe again 7 | # After each, it should verify that the wrfchemi file was generated. 8 | # I think convert_emiss.exe may be smart enough to allow both to be generated simultaneously 9 | # but I have never tried that. 10 | 11 | cd `dirname $0` 12 | mydir=`pwd -P` 13 | 14 | pyprog="$mydir/../CONFIG/autowrf_namelist_main.py" 15 | pyemiyr="$mydir/bestemissyear.py" 16 | # Now temporarily reset both the run time and the bio_emiss_opt 17 | # Need to use 11 h runtime (and make sure that convert_emiss doesn't ACTUALLY 18 | # look at the run time) because that's the best way to get end hour = 11 and 19 | # keep the python program consistent. 20 | # 21 | # Also for a run that spans multiple years we have to be a little tricky about 22 | # how we handle the date, since we automatically scale the emissions by year. 23 | # In 24 | if [[ $FDDA_ON == 1 ]]; then 25 | fdda_args="$pyargs --grid_fdda=0" 26 | else 27 | fdda_args="" 28 | fi 29 | 30 | run_sdate=$(python $pyprog get-wps-opt --no-quotes --start_date) 31 | run_edate=$(python $pyprog get-wps-opt --no-quotes --end_date) 32 | 33 | emi_sdate=$(python $pyemiyr 00z $run_sdate $run_edate) 34 | # Set up the namelist to run the first 12 hour period for convert_emiss.exe. The emi_sdate (emissions start date) 35 | # is set up to be in the year that we want to scale emissions to. --run-time=11 h is necessary to generate the 36 | # first 12 hours; convert_emiss seems to take the start date and run time rather than the start and end dates. 37 | # Since we have no MEGAN data yet, bio_emiss_opt cannot be 3, so set it to 0. Setting restart to .false. deals 38 | # with an unusual case where I was running from a restart file with new boundary conditions, with restart=.true., 39 | # it tried to find a restart file during the second 12-hour span, which obviously didn't exist. fdda_args turns 40 | # off the FDDA nudging for real.exe only if it's been turned on; if the gfdda section of the namelist is missing, 41 | # then trying to set that value gives an error. 42 | python $pyprog tempmod --start-date=${emi_sdate} --run-time=11h --bio_emiss_opt=0 --restart=.false. $fdda_args 43 | # Now we go over to the run directory. Backup any existing wrfchemi files, then 44 | # run real.exe followed by convert_emiss.exe 45 | cd ../../WRFV3/run 46 | if [ -f wrfchemi_00z_d01 ]; then 47 | mv wrfchemi_00z_d01 wrfchemi_00z_d01.autowrf-backup 48 | fi 49 | if [ -f wrfchemi_12z_d01 ]; then 50 | mv wrfchemi_12z_d01 wrfchemi_12z_d01.autowrf-backup 51 | fi 52 | 53 | # Double check that the namelist is linked to the one in the CONFIG directory. Backup 54 | # only if not already a link to somewhere 55 | if [ -f namelist.input ]; then 56 | lnk=`readlink namelist.input` 57 | if [ ! -z $lnk ]; then 58 | mv namelist.input namelist.input.autowrf-backup 59 | fi 60 | fi 61 | ln -s "$mydir/../CONFIG/namelist.input" 62 | 63 | ./real.exe 64 | realexit=$? 65 | if [ $realexit -ne 0 ]; then 66 | echo "real.exe failed with exit code $realexit when running the 00-12z period for NEI emissions" 67 | echo "You should run it directly (with mpirun if compiled in parallel) and check any output to" 68 | echo "identify the problem" 69 | echo "Skipping the remainder of convert_emiss" 70 | exit 1 71 | fi 72 | 73 | ./convert_emiss.exe 74 | convexit=$? 75 | cp -f rsl.error.0000 $mydir/../PREPLOGS/convert_00z.log 76 | if [ $convexit -ne 0 ]; then 77 | echo "convert_emiss.exe failed with exit code $convexit when running the 00-12z period for NEI" 78 | echo "emissions. You should run it directly (with mpirun if compiled in parallel) and check any" 79 | echo "output to identify the problem" 80 | exit 1 81 | fi 82 | if [ ! -f wrfchemi_00z_d01 ]; then 83 | echo "convert_emiss.exe failed to produce wrfchemi_00z_d01. You should run it directly (with" 84 | echo "mpirun if compiled in parallel) and check any output to identify the problem." 85 | exit 1 86 | fi 87 | 88 | 89 | # Now set up for the second 12 hr segment. See the first 12 hr segment for the reasoning behing all the options. 90 | emi_sdate=$(python $pyemiyr 12z $run_sdate $run_edate) 91 | python $pyprog tempmod --start-date=${emi_sdate} --run-time=11h --bio_emiss_opt=0 --restart=.false. $fdda_args 92 | 93 | ./real.exe 94 | realexit=$? 95 | if [ $realexit -ne 0 ]; then 96 | echo "real.exe failed with exit code $realexit when running the 12-24z period for NEI emissions" 97 | echo "You should run it directly (with mpirun if compiled in parallel) and check any output to" 98 | echo "identify the problem" 99 | exit 1 100 | fi 101 | 102 | ./convert_emiss.exe 103 | convexit=$? 104 | cp -f rsl.error.0000 $mydir/../PREPLOGS/convert_12z.log 105 | if [ $convexit -ne 0 ]; then 106 | echo "convert_emiss.exe failed with exit code $convexit when running the 12-24z period for NEI" 107 | echo "emissions. You should run it directly (with mpirun if compiled in parallel) and check any" 108 | echo "output to identify the problem" 109 | exit 1 110 | fi 111 | if [ ! -f wrfchemi_12z_d01 ]; then 112 | echo "convert_emiss.exe failed to produce wrfchemi_12z_d01. You should run it directly (with" 113 | echo "mpirun if compiled in parallel) and check any output to identify the problem." 114 | exit 1 115 | fi 116 | 117 | 118 | # Restore the namelist to its user defined state, minus grid_fdda if it was on 119 | python $pyprog tempmod $fdda_args 120 | 121 | exit 0 122 | -------------------------------------------------------------------------------- /PREPINPUT/prepnei_intermediate: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # Okay, this one will not be too bad. All it needs to do is 3 | # go into the NEI directory and run it. In the future, it may 4 | # need to do both NEI05 or NEI11. 5 | # This will however handle the copying and linking of the wrfem 6 | # files in the run directory 7 | cd `dirname $0` 8 | mydir=`pwd -P` 9 | 10 | cd "$mydir/../../NEI/src/v04" 11 | ./emiss_v04.exe 12 | emissexit=$? 13 | 14 | # Ensure that the WPS namelist is linked to the one in the CONFIG folder 15 | if [ -e namelist.wps ]; then 16 | # Back up only if not a link already 17 | lnk=`readlink namelist.wps` 18 | if [ ! -z $lnk ]; then 19 | mv namelist.wps namelist.wps.autowrf-backup 20 | fi 21 | fi 22 | 23 | ln -s "$mydir/../CONFIG/namelist.wps" 24 | 25 | if [ $emissexit -ne 0 ]; then 26 | echo "emiss_v04.exe failed with exit code $emissexit" 27 | exit 1 28 | fi 29 | 30 | for f in wrfem*; do 31 | if [ -f "../../../WRFV3/run/$f" ]; then 32 | mv "../../../WRFV3/run/${f}" "../../../WRFV3/run/${f}.autowrf-backup" 33 | fi 34 | done 35 | 36 | cp wrfem* ../../../WRFV3/run 37 | cd ../../../WRFV3/run 38 | ln -sf wrfem_00to12Z wrfem_00to12z_d01 39 | ln -sf wrfem_12to24Z wrfem_12to24z_d01 40 | 41 | # Link the output to make it easier to find 42 | cd "$mydir/../PREPLOGS" 43 | ln -sf ../../NEI/src/v04/al2radm2.outp emiss.log 44 | 45 | exit 0 46 | -------------------------------------------------------------------------------- /PREPINPUT/prepwps: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # 3 | # This will be responsible for running all of the 4 | # input data prep (WPS, NEI, MEGAN, MOZBC) 5 | 6 | # Input parsing - can override the default WPS directory to allow 7 | # WPS jobs to be split up over multiple jobs. To do this though we 8 | # need hard copies of modified namelists for it to work. 9 | if [[ -z $1 ]]; then 10 | wpsdir="WPS" 11 | lnknamelist=true 12 | else 13 | wpsdir="$1" 14 | lnknamelist=false 15 | fi 16 | 17 | mydir=$(dirname $0) # always find this directory, which should be PREPINPUT 18 | cd $mydir 19 | mydir=`pwd -P` 20 | pyprog="$mydir/../CONFIG/autowrf_namelist_main.py" #namelist control program 21 | pymet="$mydir/metgriblist.py" 22 | # This must contain definitions of the variables 23 | # metType and metDir. metType must match the ungrib 24 | # Vtable name. Source both. 25 | . "$mydir/../wrfbuild.cfg" 26 | # This ensures all necessary env. vars. are set 27 | . "$mydir/../envvar_wrfchem.cfg" 28 | 29 | # Find the root directory. 30 | rootdir=$(cd ../..; pwd -P) 31 | 32 | # Define the subdirectory where we put to-be-untarred files 33 | tarDir="MetTARs" 34 | 35 | ############# 36 | #### WPS #### 37 | ############# 38 | cd "$rootdir/$wpsdir" 39 | 40 | # Ensure that the WPS namelist is linked to the one in the CONFIG folder 41 | # This is not done if using an alternate WPS folder because the assumption 42 | # is that it is running split met periods, which requires separate WPS namelists 43 | # in each WPS run directory. 44 | if $lnknamelist; then 45 | if [[ -e namelist.wps ]]; then 46 | # Back up only if not a link already 47 | lnk=`readlink namelist.wps` 48 | if [[ ! -z $lnk ]]; then 49 | mv namelist.wps namelist.wps.autowrf-backup 50 | fi 51 | fi 52 | ln -s "$mydir/../CONFIG/namelist.wps" 53 | fi 54 | 55 | # First make sure that the GEOGRID table is the ARW_CHEM table 56 | # and that the ungrib Vtable matches the requested meteorology 57 | # 58 | # Backup the GEOGRID.TBL link first. Need to see if it is a link 59 | cd geogrid 60 | if [[ -f GEOGRID.TBL ]]; then 61 | lnk=$(readlink GEOGRID.TBL) 62 | if [[ -z $lnk ]]; then 63 | cp GEOGRID.TBL GEOGRID.TBL.BACKUP 64 | else 65 | ln -s $lnk GEOGRID.TBL.BACKUP 66 | fi 67 | fi 68 | # Now link the the ARW_CHEM file 69 | echo "Linking GEOGRID.TBL.ARW_CHEM as GEOGRID.TBL." 70 | ln -sf GEOGRID.TBL.ARW_CHEM GEOGRID.TBL 71 | cd .. 72 | 73 | # Now handle the ungrib Vtable link 74 | if [[ -f Vtable ]]; then 75 | lnk=$(readlink Vtable) 76 | if [[ -z $lnk ]]; then 77 | cp Vtable Vtable.BACKUP 78 | else 79 | ln -s $lnk Vtable.BACKUP 80 | fi 81 | fi 82 | 83 | ln -sf ungrib/Variable_Tables/Vtable.${metType} Vtable 84 | 85 | 86 | # Begin by running geogrid 87 | # Log files use the WPS directory to identify WPS instances 88 | # running concurrently 89 | gglogfile="${wpsdir}_geogrid.log" 90 | ./geogrid.exe >& "$mydir/../PREPLOGS/$gglogfile" 91 | ggexit=$? 92 | if [[ $ggexit != 0 ]]; then 93 | echo "In ${wpsdir}: GEOGRID.exe failed with exit code $ggexit" 94 | echo "Aborting the remainer of WPS" 95 | exit 1 96 | fi 97 | 98 | # Then ungrib. Need to link the met data first. We need to link only the 99 | # relevant files because ungrib isn't smart enough to only operate on 100 | # the relevant ones for all steps, meaning it can take unnecessarily 101 | # long if there's a lot of irrelevant met files. We have python code 102 | # to list those files, given start and end dates, but we need to extract 103 | # the start and end dates from the namelist manually to allow for the 104 | # split-met functionality. Also, we want to untar the archive files, if it isn't 105 | # done yet. 106 | regex='[0-9][0-9][0-9][0-9]-[0-9][0-9]-[0-9][0-9]_[0-9][0-9]:[0-9][0-9]:[0-9][0-9]' 107 | startline=$(grep 'start_date' namelist.wps) 108 | endline=$(grep 'end_date' namelist.wps) 109 | linkstop=false 110 | if [[ $startline =~ $regex ]]; then 111 | startdate=${BASH_REMATCH[0]} 112 | else 113 | echo "Could not find start date in namelist.wps" >&2 114 | linkstop=true 115 | fi 116 | if [[ $endline =~ $regex ]]; then 117 | enddate=${BASH_REMATCH[0]} 118 | else 119 | echo "Could not find end date in namelist.wps" >&2 120 | linkstop=true 121 | fi 122 | 123 | # Generate the list of files to link 124 | if [[ $metDir == *[[:space:]]* ]]; then 125 | echo "" 126 | echo "WARNING: The directory with the met files ($metDir)" 127 | echo "contains spaces. This will likely bread link_grib.csh." 128 | echo "" 129 | fi 130 | 131 | # Check if the met data was untarred yet or not 132 | if [[ $metType == 'NARR' ]]; then 133 | grib_prefix='merged_AWIP32' 134 | else 135 | echo "Met type '$metType' not recognized" 136 | fi 137 | 138 | # If untarred files don't exist, we need to untar the necessary ones 139 | ls "${metDir}/${grib_prefix}"* >& /dev/null 140 | if [[ $? != 0 ]]; then 141 | mkdir -p "$tarDir" 142 | cd "$tarDir" 143 | rm -f ${grib_prefix}* *.tar 144 | 145 | tarfiles=$(python "$pymet" "$metType" "$startdate" "$enddate") 146 | for f in $tarfiles; do 147 | # This will untar the pointed to file in the current directory 148 | echo "Untarring ${metDir}/${f}" 149 | cp "${metDir}/${f}" . 150 | tar -xf $f 151 | rm $f 152 | done 153 | 154 | cd .. 155 | linkDir="$tarDir" 156 | else 157 | linkDir="$metDir" 158 | fi 159 | 160 | flist=$(python "$pymet" --grib "$metType" "$startdate" "$enddate") 161 | metfiles="" 162 | for f in $flist; do 163 | metfiles="$metfiles ${linkDir}/${f}" 164 | done 165 | 166 | rm -f GRIBFILE* 167 | ./link_grib.csh $metfiles 168 | 169 | lnkexit=$? 170 | if [[ $lnkexit != 0 ]]; then 171 | echo "In ${wpsdir}: LINK_GRIB.CSH failed with exit code $lnkexit" 172 | echo "Aborting the remainer of WPS" 173 | exit 1 174 | elif [[ ! -e GRIBFILE.AAA ]]; then 175 | echo "In ${wpsdir}: LINK_GRIB.CSH did not generate any valid GRIBFILE links." 176 | echo "Aborting the remainer of WPS" 177 | exit 1 178 | fi 179 | 180 | 181 | # Now run ungrib 182 | uglogfile="${wpsdir}_ungrib.log" 183 | ./ungrib.exe >& "$mydir/../PREPLOGS/${uglogfile}" 184 | ugexit=$? 185 | if [[ $ugexit != 0 ]]; then 186 | echo "In ${wpsdir}: UNGRIB.EXE failed with exit code $ugexit" 187 | echo "Aborting the remainer of WPS" 188 | exit 1 189 | fi 190 | 191 | # Finally metgrid 192 | rm -f met_em* 193 | mglogfile="${wpsdir}_metgrid.log" 194 | ./metgrid.exe >& "$mydir/../PREPLOGS/${mglogfile}" 195 | mgexit=$? 196 | if [[ $mgexit != 0 ]]; then 197 | echo "In ${wpsdir}: METGRID.EXE failed with exit code $mgexit" 198 | echo "Met files will not be linked to the run directory" 199 | exit 1 200 | fi 201 | 202 | # Remove the intermediate files. PFILE seems to be a static 203 | # identifier, that doesn't seem to change with the prefix 204 | rm -f GRIBFILE* 205 | prefix="`python $pyprog get-wps-opt --no-quotes --prefix`" 206 | rm -f "${prefix}"* 207 | rm -f PFILE* 208 | # Now go over to the run directory and link this meteorology 209 | cd ../WRFV3/run 210 | # Only remove if not running in split-met mode 211 | # Handle in splitwps if we are running in that mode 212 | if [[ $wpsdir == WPS ]]; then 213 | rm -f met_em* 214 | fi 215 | p="$rootdir/$wpsdir/met_em*" 216 | for f in $p; do 217 | ln -s $f 218 | done 219 | 220 | # If using lat-lon map projection, then we need to modify the 221 | # WRF namelist permanently to account for the fact that the 222 | # dx dy in the WPS namelist is ONLY IN THAT CASE given in degrees 223 | # but the WRF namelist still expects meters. Further, only allow 224 | # this to edit the namelist if either a single instance of WPS 225 | # is supposed to be run ($wpsdir == WPS) or we're running the first 226 | # instance of WPS when it's been split up into smaller time 227 | # periods ($wpsdir == WPS_00) 228 | mapproj=`python $pyprog get-wps-opt --no-quotes --map_proj` 229 | if [[ "$mapproj" == "lat-lon" ]] && [[ $wpsdir == "WPS" ]] || [[ $wpsdir == "WPS_00" ]]; then 230 | which ncdump > /dev/null 231 | if [[ $? -ne 0 ]]; then 232 | echo "" 233 | echo "***********************************************************************************" 234 | echo "WARNING: You are using a LAT-LON map projection and do not have NCDUMP on your path" 235 | echo " AUTOWRFCHEM uses NCDUMP to convert DX,DY in the WPS namelist into meters for WRF." 236 | echo " Without NCDUMP, you will need to manually edit namelist.input in" 237 | echo " $mydir/../CONFIG" 238 | echo " to get DX and DY right." 239 | echo "***********************************************************************************" 240 | echo "" 241 | else 242 | # These work together to capture the number as, e.g. 243 | # 977.123f --> 977.123 244 | # 977.f --> 977 245 | # 977f --> 977 246 | # Basically we only want the decimal point if followed by numbers 247 | regex1="[0-9]+\.[0-9]+" 248 | regex2="[0-9]+" 249 | metfile=`ls -1 met_em* | head -n 1` 250 | dx=`ncdump -h $metfile | grep ":DX"` 251 | if [[ $dx =~ $regex1 ]] || [[ $dx =~ $regex2 ]]; then 252 | dxNum=${BASH_REMATCH[0]} 253 | fi 254 | dy=`ncdump -h $metfile | grep ":DY"` 255 | if [[ $dy =~ $regex1 ]] || [[ $dy =~ $regex2 ]]; then 256 | dyNum=${BASH_REMATCH[0]} 257 | fi 258 | 259 | if [[ -z "$dxNum" ]] || [[ -z "$dyNum" ]]; then 260 | echo "" 261 | echo "*****************************************************" 262 | echo "Failed to obtain DX or DY from the meteorology files." 263 | echo "You will need to manually edit namelist.input in" 264 | echo "$mydir/../CONFIG" 265 | echo "to get DX and DY right." 266 | echo "*****************************************************" 267 | echo "" 268 | else 269 | echo "" 270 | echo "***************************************************************************" 271 | echo "LAT-LON map proj: Setting DX and DY in namelist.input to match met_em files" 272 | echo "***************************************************************************" 273 | echo "" 274 | python $pyprog mod --force-wrf-only --dx=$dxNum 275 | python $pyprog mod --force-wrf-only --dy=$dyNum 276 | fi 277 | fi 278 | fi 279 | -------------------------------------------------------------------------------- /PREPINPUT/pyncdf.py: -------------------------------------------------------------------------------- 1 | ../Tools/pyncdf.py -------------------------------------------------------------------------------- /PREPINPUT/splitwps: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # 3 | # This allows you to split up WPS jobs into multiple directories 4 | # which can then be submitted separately to deal with large amounts 5 | # of met data to process (i.e. whole US for three years...) 6 | 7 | tmp=$(dirname $0) 8 | mydir=$(cd "$tmp"; pwd -P) 9 | pyprog="$mydir/../CONFIG/autowrf_namelist_main.py" 10 | pydc="$mydir/../Tools/datecompare.py" 11 | rootdir=$(cd "$mydir/../.."; pwd -P) 12 | 13 | # Input parsing 14 | 15 | ndays=30 16 | dosubmit=false 17 | 18 | while [[ $# -gt 0 ]]; do 19 | key=$1 20 | case $key in 21 | --submitfile*) 22 | subfile=${key#*=} 23 | # AUTOCALLDIR is set in the main autowrfchem script 24 | # as the directory that it is called from. So filenames 25 | # given as relative paths should be pointed to properly 26 | if [[ ${subfile:0:1} != "/" ]]; then 27 | subfile="$AUTOCALLDIR/$subfile" 28 | fi 29 | ;; 30 | --ndays*) 31 | ndays=${key#*=} 32 | ;; 33 | esac 34 | shift 35 | done 36 | 37 | if [[ ! -z $subfile ]]; then 38 | dosubmit=true 39 | grep '#AUTOWRFEXEC' "$subfile" >& /dev/null 40 | if [[ $? != 0 ]]; then 41 | echo "File passed with --subfile= must have a line containing '#AUTOWRFEXEC' to be replaced with the necessary commands. Aborting split WPS run." >&2 42 | exit 1 43 | fi 44 | fi 45 | 46 | 47 | # Store the WRF date format for the datecompare program 48 | datefmt='%Y-%m-%d_%H:%M:%S' 49 | 50 | # Test for SLURM scheduler 51 | if $dosubmit; then 52 | which sbatch >& /dev/null 53 | slurm_chk=$? 54 | if [[ $slurm_chk == 0 ]]; then 55 | subcmd="sbatch" 56 | else 57 | echo "Cannot find scheduler, will not autosubmit jobs" >&2 58 | fi 59 | fi 60 | 61 | # Get the actual end date 62 | enddate=$(python "$pyprog" get-wps-opt --end-date) 63 | 64 | # Clean up the WPS directory prior to copying 65 | echo "WARNING: will delete all met files in WPS and old WPS_nn directories in 15 sec" 66 | sleep 15 67 | 68 | ungrib_prefix="$(python $pyprog get-wps-opt --prefix --no-quotes)" 69 | cd "$rootdir" 70 | rm -f WPS/GRIBFILE* WPS/$ungrib_prefix* WPS/PFILE* WPS/met_em* 71 | rm -rf WPS_* 72 | rm -f WRFV3/run/met_em* 73 | 74 | i=0 75 | datechk=1 76 | while [[ $datechk == 1 ]]; do 77 | 78 | # Set the run time temporarily to 30 days, as long as that doesn't exceed 79 | # the requested end date 80 | sdatemod="+$((i*ndays))d" 81 | python "$pyprog" tempmod --run-time=${ndays}d --start-date=$sdatemod 82 | 83 | tmpend=$(python "$pyprog" get-wps-opt --end-date) 84 | python "$pydc" --datefmt=$datefmt "$tmpend" gt "$enddate" 85 | datechk=$? 86 | if [[ $datechk == 0 ]]; then 87 | python "$pyprog" tempmod --start-date=$sdatemod 88 | fi 89 | 90 | newdir="WPS_$(printf '%02d' $i)" 91 | cp -r WPS/ $newdir 92 | rm -rf $newdir/.git $newdir/.gitignore $newdir/namelist.wps 93 | cp -f "$mydir/../CONFIG/namelist.wps" "$newdir/" 94 | 95 | i=$((i+1)) 96 | done 97 | 98 | # Had some trouble in the past with files not being present, so let's 99 | # give it some time to make sure everything's caught up 100 | sleep 10 101 | 102 | # Reset the main namelists to the full time period 103 | python "$pyprog" tempmod 104 | 105 | if $dosubmit; then 106 | # should still be in AutoWRFChem top (root) directory at this point 107 | wps_rundirs=WPS_* 108 | for w in $wps_rundirs; do 109 | prepcmd="$AUTOWRFDIR/autowrfchem_main prepinpt met-only --wpsdir=$w --noreal" 110 | prepcmd=$(echo "$prepcmd" | sed -e 's/\//\\\//g') 111 | sed -e "s/.*#AUTOWRFEXEC.*/$prepcmd/" "$subfile" > "${subfile}_$w" 112 | echo "Submitting ${subfile}_$w with $subcmd" 113 | $subcmd "${subfile}_$w" 114 | done 115 | fi 116 | 117 | exit 0 118 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # AutoWRFChem-Base 2 | This contains the stand-alone code necessary to automatically run WRF-Chem with NEI and MEGAN emissions, and MOZART boundary conditions 3 | 4 | ## Prerequisites 5 | AutoWRFChem is designed to run on Unix/Linux-like systems. It requires the following programs to be installed on your system and available on your `PATH`: 6 | 7 | * Bash (at `/bin/bash`) 8 | * Python (2 or 3, note that 3 compatibility not fully tested) 9 | * Perl 10 | * Git 11 | 12 | ## Setup 13 | AutoWRFChem is intended to be placed alongside directories for WRF-Chem, WPS, the NEI emissions converter tool, MEGAN, 14 | and MOZART boundary condition tool, that is, in a directory structure like this: 15 | 16 | ```bash 17 | AutoWRFChem-Root/ 18 | ├── AutoWRFChem-Base 19 | ├── MEGAN 20 | ├── MOZBC 21 | ├── NEI 22 | ├── WPS 23 | └── WRFV3 24 | └── run 25 | ``` 26 | 27 | AutoWRFChem can clone forks of the WPS, MEGAN, NEI, and MOZBC directories if needed, but you must download the WRFV3 directory yourself. To get the WPS etc. 28 | repos, run the `Runme` script in AutoWRFChem-Base. This will clone them as well as provide a link to the main AutoWRFChem executable in the top directory 29 | (here, `AutoWRFChem-Root`). 30 | 31 | ## Usage 32 | 33 | For a full list of subcommands, call `./autowrfchem_main --help` in the `AutoWRFChem-Base` directory, or `./autowrfchem --help` in the top directory if you 34 | ran `Runme` to generate that link. 35 | 36 | AutoWRFChem usage is generally broken down into four main subcommands: 37 | 38 | * `config` runs all of the component configuration scripts. It requires user input, so do not run as part of a batch job. 39 | - `config namelist` starts an interactive program to edit the WRF-Chem namelists. **Note:** AutoWRFChem modifies the namelists 40 | written in `WRFV3/run` for parts of its functionality, so it is safest to use this interactive tool. 41 | * `compile` compiles all of the components. 42 | * `prepinpt` generates the input files needed to run WRF-Chem with NEI emissions, MEGAN biogenic emissions, and MOZART chemical boundary 43 | conditions. 44 | - There are options to generate just met files, just chemistry files, or just missing files. 45 | * `run` will start WRF-Chem, and has options to automatically resume from the last restart file. 46 | 47 | ## FAQ 48 | 49 | * *What versions of WRF is this compatible with?* Only v3.5 and 3.6 have been tested. Other versions may work, but are not guaranteed. 50 | * *How can I try a newer version?* Download WRF and WPS manually to the correct directory structure. Emissions preparation may not work, 51 | so you may need to handle that manually. 52 | * *Can I use multiple domains?* Not with this version. A 2.0 version is planned that will be more flexible, but I cannot give any sort of timeline 53 | for when that will be ready. 54 | -------------------------------------------------------------------------------- /RUNUTILS/lastrst: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # 3 | # Finds the last restart file in ../../WRFV3/run and echoes it 4 | # Therefore we can't echo anything else from this to stdout, it must 5 | # go to stderr if an error message is required. 6 | 7 | cd `dirname $0` 8 | myname=`basename $0` 9 | mydir=`pwd -P` 10 | pyprog="$mydir/../CONFIG/autowrf_namelist_main.py" 11 | pydc="$mydir/../Tools/datecompare.py" 12 | 13 | # Get the start and end dates from the WRF namelist 14 | wrf_start=`python $pyprog get-wrf-opt --start-date` 15 | wrf_end=`python $pyprog get-wrf-opt --end-date` 16 | 17 | rstfiles="../../WRFV3/run/wrfrst*" 18 | regex="[0-9][0-9][0-9][0-9]-[0-9][0-9]-[0-9][0-9]_[0-9][0-9]:[0-9][0-9]:[0-9][0-9]" 19 | for f in $rstfiles; do 20 | if [[ $f =~ $regex ]]; then 21 | rdate="${BASH_REMATCH[0]}" 22 | 23 | python $pydc --datefmt '%Y-%m-%d_%H:%M:%S' "$rdate" ge "$wrf_start" 24 | start_chk=$? 25 | python $pydc --datefmt '%Y-%m-%d_%H:%M:%S' "$rdate" lt "$wrf_end" 26 | end_chk=$? 27 | 28 | if [[ $start_chk == 0 ]] && [[ $end_chk == 0 ]]; then 29 | last_rst_file="$f" 30 | fi 31 | else 32 | echo "Could not determine date of restart file ($f)" >&2 33 | fi 34 | done 35 | 36 | echo "$last_rst_file" 37 | -------------------------------------------------------------------------------- /Runme: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # Initialization script for autowrfchem. Run this to set up for automatic 3 | # running of WRF-Chem 4 | 5 | cd `dirname $0` 6 | mydir=`pwd -P` 7 | cd .. 8 | 9 | # Link the main executable up one directory to make it obvious which command to call 10 | ln -s "$mydir/autowrfchem_main" autowrfchem 11 | 12 | # Check if all utilities needed are installed 13 | doexit=false 14 | which python >& /dev/null 15 | if [ $? -ne 0 ]; then 16 | echo "python is not on your path. This is necessary to use autowrfchem." 17 | doexit=true 18 | fi 19 | 20 | which perl >& /dev/null 21 | if [ $? -ne 0 ]; then 22 | echo "perl is not on your path. This is necessary to use autowrfchem." 23 | doexit=true 24 | fi 25 | 26 | which git >& /dev/null 27 | if [ $? -ne 0 ]; then 28 | echo "git is not available on this system." 29 | doexit=true 30 | fi 31 | 32 | if $doexit; then 33 | exit 1 34 | fi 35 | 36 | rootrepo="https://github.com/CohenBerkeleyLab" 37 | 38 | # These two arrays will be used in selecting the WPS version. The first is used to print out 39 | # the menu options. The second should correspond to the remote branch name containing that version 40 | # such that running "git checkout origin/${branchname[$sel]}" would checkout that branch. 41 | # These are separated to allow more flexibility in naming the branches (in case there's a 42 | # mistake in the naming) but the branches should REALLY be named with the version number for 43 | # consistency. Note that the order of the two must be the same. 44 | wpsmenu=("v3.5.1" "v3.6") 45 | branchname=("v3.5.1" "v3.6") 46 | 47 | # Determine if it needs to clone anything (WPS, NEI, MEGAN, MOZBC). WPS will need to know 48 | # what version to clone (maintained as different branches in the WPS repo) 49 | if [ ! -e WPS -o ! -e NEI -o ! -e MEGAN -o ! -e MOZBC ]; then 50 | echo "AUTOWRFCHEM expects the directories WPS, NEI, MEGAN, and MOZBC to be siblings to it." 51 | echo "One or more of these is missing; would you like to clone the missing ones automatically?" 52 | read -p "Enter y or n: " userans 53 | if [ "$userans" == "y" -o "$userans" == "Y" ]; then 54 | 55 | if [ ! -e WPS ]; then 56 | git clone ${rootrepo}/WPS.git WPS 57 | # Have the user choose the WPS version 58 | cd WPS 59 | echo "Choose the WPS version:" 60 | i=0 61 | for vers in "${wpsmenu[@]}"; do 62 | i=$((i+1)) 63 | echo " $i: $vers" 64 | done 65 | while true; do 66 | echo -n "Enter choice (1-${i}): " 67 | read userin 68 | if [ $userin -ge 1 -a $userin -le $i ]; then break; fi 69 | done 70 | sel=$((userin-1)) 71 | git checkout -b ${branchname[$sel]} origin/${branchname[$sel]} 72 | cd .. 73 | fi 74 | 75 | if [ ! -e NEI ]; then 76 | git clone ${rootrepo}/NEI.git NEI 77 | fi 78 | 79 | if [ ! -e MEGAN ]; then 80 | git clone ${rootrepo}/MEGAN.git MEGAN 81 | fi 82 | 83 | if [ ! -e MOZBC ]; then 84 | git clone ${rootrepo}/MOZBC.git MOZBC 85 | fi 86 | else 87 | echo "You have chosen not to clone the various components. They must still be provided for autowrfchem to work." 88 | fi 89 | fi 90 | 91 | if [ ! -e WRFV3 ]; then 92 | echo "Remember, WRFV3 must be a directory at this level, and it must contain a run subdirectory." 93 | fi 94 | 95 | exit 0 96 | -------------------------------------------------------------------------------- /Tools/datecompare.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # 3 | # datecompare.py - python script that can be used to compare two dates given as strings 4 | # Gives exit status of 0 if comparison is true, 1 if false, so can be used in shell if 5 | # statements. 6 | from __future__ import print_function 7 | import argparse 8 | import datetime as dt 9 | import re 10 | import sys 11 | 12 | def shell_error(msg, exitcode=2): # use 2 because 1 is used to indicate the comparison was false, not an error 13 | print(msg,file=sys.stderr); 14 | exit(exitcode) 15 | 16 | def parse_args(): 17 | parser = argparse.ArgumentParser(description='compare two dates', formatter_class=argparse.RawTextHelpFormatter) 18 | parser.add_argument('-f', '--datefmt', default='%Y-%m-%d', help='The format that Python\'s datetime module should interprete the dates as.\n' 19 | 'Defaults to %(default)s, i.e. yyyy-mm-dd. Most common symbols are:\n' 20 | ' %%Y - four digit year \n' 21 | ' %%m - two digit month \n' 22 | ' %%d - two digit day of month \n' 23 | ' %%H - 24 hr clock (00-23) hour \n' 24 | ' %%M - minute (00-59) \n' 25 | ' %%S - second (00-59) \n' 26 | 'See the datetime.strptime documentation for the full list.') 27 | parser.add_argument('dates_ops', nargs=argparse.REMAINDER, help='The first date to compare. The format of the string is determined by the --datefmt option.\n' 28 | 'Exactly one comparison operator is require, which can be:\n =, ==, eq (equality) \n !=, ne (not equal)' 29 | '\n <, lt (less than) \n <=, le (less than or equal to) \n >, gt (greater than),' 30 | '\n >=, ge (greater than or equal to) \n' 31 | ' Note: most of the symbolic operations (e.g. <, >) will need to be quoted when presented as arguments \n' 32 | ' to avoid the shell interpreting them as special symbols for, e.g. redirection.' 33 | 'Dates may be modified with + or - operators. Examples:' 34 | '\n +2d (add two days to the preceding date)' 35 | '\n -12h (subtract two hours from the preceding datetime' 36 | '\n +30m (add 30 minutes)' 37 | '\n -40s (subtract 40 seconds)' 38 | '\n +1d12h (add 1 day and 12 hours)') 39 | return parser.parse_args() 40 | 41 | def parse_timedelta(td): 42 | if td[0] == '+': 43 | f = 1 44 | elif td[0] == '-': 45 | f = -1 46 | else: 47 | raise ValueError('The first character of td must be + or -') 48 | 49 | days = 0 50 | hours = 0 51 | minutes = 0 52 | seconds = 0 53 | 54 | match = re.finditer('\d+', td) 55 | for m in match: 56 | val = int(m.group()) 57 | timeseg = td[m.end()] 58 | if timeseg == 'd': 59 | days += f * val 60 | elif timeseg == 'h': 61 | hours += f * val 62 | elif timeseg == 'm': 63 | minutes += f * val 64 | elif timeseg == 's': 65 | seconds += f * val 66 | else: 67 | shell_error('Modification operators only recognize d, h, m, s as valid time segments (days, hours, minutes, seconds') 68 | 69 | return dt.timedelta(days=days, hours=hours, minutes=minutes, seconds=seconds) 70 | 71 | def process_dates_opts(dates_ops, date_fmt): 72 | comp_op = '' 73 | comparison_ops = ['=','==','eq','!=','ne','<','lt','<=','le','>','gt','>=','ge'] 74 | mod_ops = ['+','-'] 75 | dates = [] 76 | for d in dates_ops: 77 | if d in comparison_ops: 78 | if len(comp_op) == 0: 79 | comp_op = d 80 | else: 81 | shell_error('Cannot specify multiply comparison operators ({0})'.format(', '.join(comparison_ops))) 82 | elif d[0] in mod_ops: 83 | if len(dates) == 0: 84 | shell_error('Modification operators (starting with {0}) must come after a date'.format(', '.join(mod_ops))) 85 | else: 86 | # Modify the most recent date to be read 87 | td = parse_timedelta(d) 88 | dates[-1] += td 89 | else: 90 | # Must be a date 91 | if len(dates) < 2: 92 | dates.append(convert_date(d, date_fmt)) 93 | else: 94 | shell_error('Only 2 dates can be input. Already found two: {0}'.format(', '.join(dates))) 95 | 96 | if comp_op == '': 97 | shell_error('No comparison operator given') 98 | elif len(dates) < 2: 99 | shell_error('Two dates must be given for comparison') 100 | 101 | return dates[0], dates[1], comp_op 102 | 103 | def convert_date(datestr,datefmt): 104 | dateval = dt.datetime.strptime(datestr, datefmt) 105 | return dateval 106 | 107 | def compare_dates(d1, d2, op): 108 | if op == '=' or op == '==' or op == 'eq': 109 | return d1 == d2 110 | elif op == '!=' or op == 'ne': 111 | return d1 != d2 112 | elif op == '<' or op == 'lt': 113 | return d1 < d2 114 | elif op == '<=' or op == 'le': 115 | return d1 <= d2 116 | elif op == '>' or op == 'gt': 117 | return d1 > d2 118 | elif op == '>=' or op == 'ge': 119 | return d1 >= d2 120 | else: 121 | exit(2) 122 | 123 | def main(): 124 | args=parse_args() 125 | d1, d2, op = process_dates_opts(args.dates_ops, args.datefmt) 126 | result = compare_dates(d1, d2, op) 127 | if result: 128 | exit(0) 129 | else: 130 | exit(1) 131 | 132 | if __name__ == '__main__': 133 | main() 134 | -------------------------------------------------------------------------------- /Tools/gc2moz/python/gc2moz.py: -------------------------------------------------------------------------------- 1 | from __future__ import print_function 2 | import argparse 3 | from bpch import bpch 4 | import datetime as dt 5 | import netCDF4 as ncdf 6 | import numpy as np 7 | import os 8 | import re 9 | import sys 10 | import pdb 11 | 12 | # Local modules 13 | import gcCH4bins 14 | 15 | __author__ = 'Josh Laughner' 16 | __debug_level__ = 2 17 | 18 | ###################################### 19 | ##### GEOS-Chem Static variables ##### 20 | ###################################### 21 | 22 | fill_val = 0.0; 23 | 24 | gclon = np.arange(-180.0, 180.0, 2.5) 25 | gclat = np.append(np.arange(-90.0, 90.0, 2.0), 89.5) 26 | gclat[0] = -89.5 27 | 28 | # These are given at 29 | # http://wiki.seas.harvard.edu/geos-chem/index.php/GEOS-Chem_vertical_grids#Vertical_grids_for_GEOS-5.2C_GEOS-FP.2C_MERRA.2C_and_MERRA-2 30 | # but need to be flipped b/c GEOS-Chem puts index 1 at the surface, but GMAO (and MOZART) put it at the top of the 31 | # atmosphere 32 | 33 | # A is given in hPa at the above length, we convert to kPa essentially to be consistent with how MOZART defines its A. 34 | # In MOZART, both A and B are considered dimensionless, but A is multiplied by P0 = 100000 Pa, so we can kind of think 35 | # of A as being either in kPa or unitless. 36 | gc_hya = np.flipud(0.001*np.array([0.000000e+00, 4.804826e-02, 6.593752e+00, 1.313480e+01, 1.961311e+01, 2.609201e+01, 37 | 3.257081e+01, 3.898201e+01, 4.533901e+01, 5.169611e+01, 5.805321e+01, 6.436264e+01, 38 | 7.062198e+01, 7.883422e+01, 8.909992e+01, 9.936521e+01, 1.091817e+02, 1.189586e+02, 39 | 1.286959e+02, 1.429100e+02, 1.562600e+02, 1.696090e+02, 1.816190e+02, 1.930970e+02, 40 | 2.032590e+02, 2.121500e+02, 2.187760e+02, 2.238980e+02, 2.243630e+02, 2.168650e+02, 41 | 2.011920e+02, 1.769300e+02, 1.503930e+02, 1.278370e+02, 1.086630e+02, 9.236572e+01, 42 | 7.851231e+01, 5.638791e+01, 4.017541e+01, 2.836781e+01, 1.979160e+01, 9.292942e+00, 43 | 4.076571e+00, 1.650790e+00, 6.167791e-01, 2.113490e-01, 6.600001e-02, 1.000000e-02])) 44 | gc_hyb = np.flipud(np.array([1.000000e+00, 9.849520e-01, 9.634060e-01, 9.418650e-01, 9.203870e-01, 8.989080e-01, 45 | 8.774290e-01, 8.560180e-01, 8.346609e-01, 8.133039e-01, 7.919469e-01, 7.706375e-01, 46 | 7.493782e-01, 7.211660e-01, 6.858999e-01, 6.506349e-01, 6.158184e-01, 5.810415e-01, 47 | 5.463042e-01, 4.945902e-01, 4.437402e-01, 3.928911e-01, 3.433811e-01, 2.944031e-01, 48 | 2.467411e-01, 2.003501e-01, 1.562241e-01, 1.136021e-01, 6.372006e-02, 2.801004e-02, 49 | 6.960025e-03, 8.175413e-09, 0.000000e+00, 0.000000e+00, 0.000000e+00, 0.000000e+00, 50 | 0.000000e+00, 0.000000e+00, 0.000000e+00, 0.000000e+00, 0.000000e+00, 0.000000e+00, 51 | 0.000000e+00, 0.000000e+00, 0.000000e+00, 0.000000e+00, 0.000000e+00, 0.000000e+00])) 52 | gc_hya_mid = 0.5*(gc_hya[:-1] + gc_hya[1:]) 53 | gc_hyb_mid = 0.5*(gc_hyb[:-1] + gc_hyb[1:]) 54 | 55 | gc_base_date = dt.datetime(1985, 1, 1) 56 | 57 | # Possible diagnostic categories that GC tracers could be in. Any GEOS-Chem tracer in one of these categories and not 58 | # listed in gc_ignore_vars will be written to the pseudo-MOZART file. This category will be removed from the variable 59 | # name when saving to the MOZART-like file. 60 | gc_categories = ['IJ-AVG-$_', 'TIME-SER_'] 61 | gc_ignore_vars = ['TIME-SER_AIRDEN'] 62 | 63 | # The suffix to append to each variable from the GEOS-Chem categories given above before saving to the MOZART-like file 64 | moz_suffix = '_VMR_inst' 65 | 66 | def shell_error(msg, exitcode=1): 67 | print(msg, file=sys.stderr) 68 | exit(exitcode) 69 | 70 | 71 | def shell_msg(msg): 72 | print(msg, file=sys.stderr) 73 | 74 | 75 | def format_error(lineno, spcfile, msg): 76 | shell_error('Format error, line {0} in species map file ({1}): {2}'.format(lineno, spcfile, msg)) 77 | 78 | 79 | def bpch_error(bpch_filename, varname, req_for): 80 | if isinstance(req_for, str): 81 | shell_error('Variable {0} not found in {1} (requested by {2})'.format(varname, bpch_filename, req_for)) 82 | 83 | 84 | def flip_dim(M, dim): 85 | if not isinstance(M, np.ndarray): 86 | raise TypeError('M must be an instance of np.ndarray') 87 | elif not isinstance(dim, int): 88 | raise TypeError('dim must be an integer') 89 | elif dim < 0 or dim > M.ndim-1: 90 | raise IndexError('dim must be in the range 0 to M.ndim - 1 ({0} in this case)'.format(M.ndim-1)) 91 | 92 | pvec = np.delete(np.arange(0, M.ndim), dim) 93 | pvec = np.insert(pvec, 0, dim) 94 | M = M.transpose(pvec) 95 | M = M[::-1] 96 | M = M.transpose(np.argsort(pvec)) 97 | return M 98 | 99 | 100 | def geos_to_moz_name(name): 101 | for cat in gc_categories: 102 | name = name.replace(cat, '') 103 | name += moz_suffix 104 | return name 105 | 106 | 107 | def mozdate(date_in): 108 | # MOZART defines time two different ways: 109 | # 1) With the date as a 8-digit integer (yyyymmdd) and then number of seconds since midnight 110 | # 2) As days since 0000-01-01 + seconds since midnight. 111 | # Unfortunately, Python's datetime module does not like year 0. So we need to adjust by using year 1 then adding 112 | # 366 days (because year 0 is technically a leap year - if it exists). 113 | 114 | date_int = 10000 * date_in.year + 100 * date_in.month + date_in.day 115 | date_secs = date_in.hour * 3600 + date_in.minute * 60 + date_in.second 116 | 117 | timedel = date_in - dt.datetime(1,1,1) 118 | return date_int, date_secs, timedel.days+366, timedel.seconds 119 | 120 | 121 | def mozdate_array(dates_in): 122 | date_ints = [] 123 | date_secs = [] 124 | days_since = [] 125 | since_secs = [] 126 | for d in dates_in: 127 | dint, dsec, dsince, ssince = mozdate(d) 128 | date_ints.append(dint) 129 | date_secs.append(dsec) 130 | days_since.append(dsince) 131 | since_secs.append(ssince) 132 | 133 | return np.array(date_ints), np.array(date_secs), np.array(days_since), np.array(since_secs) 134 | 135 | 136 | def unit_conversion(current_values, unit_type, current_unit, new_unit): 137 | """ 138 | Converts given values between the specified units 139 | :param current_values: the current values that you want converted between units. It can be any type, so long as 140 | arithmetic operations with scalars behave appropriately. A numpy array will work; a list will not. 141 | :param unit_type: the type of unit (e.g. mixing ratio, length, mass) as a string 142 | :param current_unit: a string representing the current unit 143 | :param new_unit: a string representing the desired unit 144 | :return: the same type as current_values converted to be the new units 145 | """ 146 | 147 | if not isinstance(unit_type, str): 148 | raise TypeError('unit_type must be a string') 149 | if not isinstance(current_unit, str): 150 | raise TypeError('current_unit must be a string') 151 | if not isinstance(new_unit, str): 152 | raise TypeError('new_unit must be a string') 153 | try: 154 | current_values + 1.0 155 | current_values - 1.0 156 | current_values * 1.0 157 | current_values / 1.0 158 | except: 159 | raise TypeError('Cannot perform one or more arithmetic operations on current_values') 160 | 161 | # Define the conversions here. conv_factors must be a dictionary where the keys are the units and the values are 162 | # the factor that multiplying the a base unit by converts it to the unit in question. 163 | if unit_type.lower() == 'mixing ratio' or unit_type.lower() == 'vmr': 164 | base_unit = 'ppp' 165 | conv_factors = {'ppp': 1.0, 'ppm': 1.0e6, 'ppmv': 1.0e6, 'ppb': 1.0e9, 'ppbv': 1.0e9, 'ppbC': 1.0e9} 166 | else: 167 | raise ValueError('Unit type "{0}" not recognized'.format(unit_type)) 168 | 169 | if current_unit not in conv_factors.keys(): 170 | raise KeyError('{0} unit "{1}" not defined'.format(unit_type, current_unit)) 171 | if new_unit not in conv_factors.keys(): 172 | raise KeyError('{0} unit "{1}" not defined'.format(unit_type, new_unit)) 173 | 174 | return current_values * conv_factors[new_unit] / conv_factors[current_unit] 175 | 176 | 177 | 178 | class FilesAndTimes(object): 179 | def __init__(self, bfiles): 180 | if not isinstance(bfiles, (list, tuple)): 181 | raise TypeError('bfiles must be a list or tuple of strings') 182 | else: 183 | for f in bfiles: 184 | if not isinstance(f, str): 185 | raise TypeError('bfiles must be a list or tuple of strings') 186 | 187 | self.files, self.datetimes = self.populate_times(bfiles) 188 | 189 | def populate_times(self, bfiles): 190 | tmp_name_list = [] 191 | tmp_date_list = [] 192 | 193 | for filename in bfiles: 194 | f = bpch(filename) 195 | times = f.variables['time'] 196 | for t in times: 197 | tmp_date_list.append(gc_base_date + dt.timedelta(hours=t)) 198 | tmp_name_list.append(filename) 199 | 200 | date_list = sorted(tmp_date_list) 201 | name_list = [] 202 | for d in date_list: 203 | i = tmp_date_list.index(d) 204 | name_list.append(tmp_name_list[i]) 205 | 206 | return name_list, date_list 207 | 208 | def iter_times(self): 209 | for i in range(len(self.datetimes)): 210 | yield self.datetimes[i], self.files[i] 211 | 212 | def ntimes(self): 213 | return len(self.datetimes) 214 | 215 | def unique_files(self): 216 | unique = [] 217 | for fname in self.files: 218 | if fname not in unique: 219 | unique.append(fname) 220 | return unique 221 | 222 | 223 | def get_args(): 224 | parser = argparse.ArgumentParser(description='Convert a GEOS-Chem ND49 binary punch file to a netCDF file that MOZBC can use', 225 | formatter_class=argparse.RawTextHelpFormatter, 226 | epilog='This method is intended to read ND49 diagnostic files because generally\n' 227 | 'one needs output multiple times per day, rather than one daily average,\n' 228 | 'to set initial and boundary conditions for WRF with any sort of accuracy.\n' 229 | 'Those ND49 files must contain whatever tracers are referenced in the\n' 230 | 'species input file, plus the PEDGE-$_PSURF diagnostic.') 231 | parser.add_argument('-o', '--output-file', default='geosBC.nc', help='the name (and path if desired) of the output file') 232 | parser.add_argument('bpchfiles', nargs='+', help='All the ND49 bpch files to draw data from') 233 | 234 | args = parser.parse_args() 235 | 236 | argout = {'bpchfiles': args.bpchfiles, 'outfile': args.output_file} 237 | return argout 238 | 239 | 240 | def write_netcdf(outfile, bpchfiles, overwrite=True): 241 | ncfile = ncdf.Dataset(outfile, 'w', clobber=overwrite, format='NETCDF3_CLASSIC') 242 | ncfile.title = 'GEOS-Chem' 243 | 244 | if __debug_level__ > 0: 245 | shell_msg('Reading times from BPCH files') 246 | file_times = FilesAndTimes(bpchfiles) 247 | 248 | if __debug_level__ > 0: 249 | shell_msg('Writing dimensions') 250 | define_dimensions(ncfile, file_times) 251 | 252 | if __debug_level__ > 0: 253 | shell_msg('Writing chemical species') 254 | write_chem_species(ncfile, file_times) 255 | 256 | if __debug_level__ > 0: 257 | shell_msg('Adding CH4 from latitudinal bins') 258 | add_methane(ncfile) 259 | 260 | ncfile.close() 261 | 262 | 263 | def define_dimensions(ncfile, filetimes): 264 | """ 265 | Defines all relevant dimensions in the netCDF file and writes any time invariant variables used to describe those 266 | dimensions. 267 | :param ncfile: an instance of netCDF4.Dataset that is the file to be written to 268 | :param filetimes: a FilesAndTimes instance listing all the BPCH files 269 | :return: 270 | """ 271 | if not isinstance(ncfile, ncdf.Dataset): 272 | raise TypeError('ncfile must be an instance of netCDF4.Dataset') 273 | 274 | if not isinstance(filetimes, FilesAndTimes): 275 | raise TypeError('filetimes must be an instance of FilesAndTimes') 276 | 277 | bfile = bpch(filetimes.files[0]) 278 | 279 | lon = bfile.variables['longitude'] 280 | lon[lon<0] += 360 # MOZART gives longitude W as positive numbers where -179 -> 181 and -1 -> 359 281 | lat = bfile.variables['latitude'] 282 | psurf = bfile.variables['PEDGE-$_PSURF'] 283 | 284 | # Write the dimensions 285 | ncfile.createDimension('lon', size=len(lon)) 286 | ncfile.createDimension('lat', size=len(lat)) 287 | ncfile.createDimension('lev', size=len(gc_hya_mid)) 288 | ncfile.createDimension('ilev', size=len(gc_hya)) 289 | ncfile.createDimension('time', size=0) # unlimited 290 | 291 | # and the variables that define their values 292 | ncfile.createVariable('lon', np.float32, dimensions=('lon')) 293 | ncfile.variables['lon'][:] = lon 294 | ncfile.variables['lon'].long_name = 'longitude' 295 | ncfile.variables['lon'].units = 'degrees_east' 296 | 297 | ncfile.createVariable('lat', np.float32, dimensions=('lat')) 298 | ncfile.variables['lat'][:] = lat 299 | ncfile.variables['lat'].long_name = 'latitude' 300 | ncfile.variables['lat'].units = 'degrees_north' 301 | 302 | # Time dimensions 303 | ncfile.createVariable('time', np.float64, dimensions=('time')) 304 | ncfile.variables['time'].long_name = 'simulation_time' 305 | ncfile.variables['time'].units = 'days since 0000-01-01' 306 | ncfile.variables['time'].calendar = 'gregorian' 307 | 308 | ncfile.createVariable('secs', np.int32, dimensions=('time')) 309 | ncfile.variables['secs'].long_name = 'seconds to complete elapsed days' 310 | ncfile.variables['secs'].units = 's' 311 | 312 | ncfile.createVariable('date', np.int32, dimensions=('time')) 313 | ncfile.variables['date'].long_name = 'current date as 8 digit integer (YYYYMMDD)' 314 | 315 | ncfile.createVariable('datesec', np.int32, dimensions=('time')) 316 | ncfile.variables['datesec'].long_name = 'seconds to complete current date' 317 | ncfile.variables['datesec'].units = 's' 318 | 319 | date_int, date_sec, days_since, sec_since = mozdate_array(filetimes.datetimes) 320 | ncfile.variables['date'][:] = date_int 321 | ncfile.variables['datesec'][:] = date_sec 322 | ncfile.variables['time'][:] = days_since 323 | ncfile.variables['secs'][:] = sec_since 324 | 325 | # GEOS-Chem uses the hybrid sigma-eta coordinate system 326 | # (http://wiki.seas.harvard.edu/geos-chem/index.php/GEOS-Chem_vertical_grids#Vertical_grids_for_GEOS-5.2C_GEOS-FP.2C_MERRA.2C_and_MERRA-2) 327 | # and we usually run on the 47-layer reduced vertical grid. The A and B coefficients are given at the above link 328 | # and the formula for converting to actual pressure levels is given at 329 | # http://wiki.seas.harvard.edu/geos-chem/index.php/GEOS-Chem_vertical_grids#Hybrid_grid_definition 330 | # and is Pedge(I,J,L) = Ap(L) + [ Bp(L) * Psurface(I,J) ], Pcenter(I,J,L) = [ Pedge(I,J,L) + Pedge(I,J,L+1) ]/2 331 | # 332 | # In MOZBC, pressures are computed as ps_mozi(I,J) * hybm + ps0 * hyam; where hybm and hyam are vectors (that are 333 | # the same for every column) of the A and B corefficients at level midpoints, ps_mozi is the MOZART surface pressure 334 | # interpolated to the WRF grid, and ps0 is just a constant, always 100000 Pa. The GEOS-Chem B parameter and the 335 | # MOZART B parameter are both unitless. The GC and MOZART A parameters just differ by a scaling factor, A_moz * P0 = 336 | # A_gc * 100 (GC A is in hPa, moz P0 has units of Pa). 337 | # 338 | # MOZART uses the GMAO standard that the first index in the vertical dimension is the top of the 339 | # atmosphere, whereas GEOS-Chem says the first index is the surface. 340 | 341 | ncfile.createVariable('lev', np.float32, dimensions='lev') 342 | ncfile.variables['lev'][:] = 1000*(gc_hya_mid + gc_hyb_mid) 343 | ncfile.variables['lev'].long_name = 'hybrid level at layer midpoints (1000*(A+B))' 344 | ncfile.variables['lev'].units = 'hybrid_sigma_pressure' 345 | ncfile.variables['lev'].positive = 'down' 346 | ncfile.variables['lev'].A_var = 'hyam' 347 | ncfile.variables['lev'].B_var = 'hybm' 348 | ncfile.variables['lev'].P0_var = 'P0' 349 | ncfile.variables['lev'].PS_var = 'PS' 350 | ncfile.variables['lev'].bounds = 'ilev' 351 | 352 | ncfile.createVariable('ilev', np.float32, dimensions='ilev') 353 | ncfile.variables['ilev'][:] = 1000*(gc_hya + gc_hyb) 354 | ncfile.variables['ilev'].long_name = 'hybrid level at layer interface (1000*(A+B))' 355 | ncfile.variables['ilev'].units = 'hybrid_sigma_pressure' 356 | ncfile.variables['ilev'].positive = 'down' 357 | ncfile.variables['ilev'].A_var = 'hyai' 358 | ncfile.variables['ilev'].B_var = 'hybi' 359 | ncfile.variables['ilev'].P0_var = 'P0' 360 | ncfile.variables['ilev'].PS_var = 'PS' 361 | 362 | ncfile.createVariable('hyam', np.float32, dimensions='lev') 363 | ncfile.variables['hyam'][:] = gc_hya_mid 364 | ncfile.variables['hyam'].long_name = 'hybrid A coefficient at layer midpoints' 365 | 366 | ncfile.createVariable('hybm', np.float32, dimensions='lev') 367 | ncfile.variables['hybm'][:] = gc_hyb_mid 368 | ncfile.variables['hybm'].long_name = 'hybrid B coefficient at layer midpoints' 369 | 370 | ncfile.createVariable('hyai', np.float32, dimensions='ilev') 371 | ncfile.variables['hyai'][:] = gc_hya 372 | ncfile.variables['hyai'].long_name = 'hybrid A coefficient at layer interfaces' 373 | 374 | ncfile.createVariable('hybi', np.float32, dimensions='ilev') 375 | ncfile.variables['hybi'][:] = gc_hyb 376 | ncfile.variables['hybi'].long_name = 'hybrid B coefficient at layer interfaces' 377 | 378 | ncfile.createVariable('P0', np.float32) # scalar 379 | ncfile.variables['P0'][:] = 1e5 380 | ncfile.variables['P0'].long_name = 'reference pressure' 381 | ncfile.variables['P0'].units = 'Pa' 382 | 383 | bfile.close() 384 | 385 | # Surface pressure has a time component, so first we need to concatenate all the values 386 | ps_list = [] 387 | for fname in filetimes.unique_files(): 388 | b = bpch(fname) 389 | ps_list.append(b.variables['PEDGE-$_PSURF'][:,0,:,:].squeeze()*100) # get surface only and convert from hPa to Pa 390 | b.close() 391 | 392 | ncfile.createVariable('PS', np.float32, dimensions=('time', 'lat', 'lon')) 393 | ncfile.variables['PS'][:] = np.concatenate(ps_list, 0) 394 | ncfile.variables['PS'].units = 'Pa' 395 | 396 | 397 | def write_chem_species(ncfile, filetimes): 398 | nlev = ncfile.dimensions['lev'].size 399 | 400 | # Find all GEOS variables in the categories defined as static variables 401 | b = bpch(filetimes.unique_files()[0]) 402 | gc_moz_names = {} # this will be a dictionary with the GC name as the key and the MOZART-like name as the value 403 | for k in b.variables.keys(): 404 | for cat in gc_categories: 405 | if cat in k: 406 | gc_moz_names[k] = geos_to_moz_name(k) 407 | b.close() 408 | 409 | for gc_name, moz_name in gc_moz_names.iteritems(): 410 | if __debug_level__ > 1: 411 | shell_msg(' Writing {0} as {1}'.format(gc_name, moz_name)) 412 | val = [] 413 | for fname in filetimes.unique_files(): 414 | b = bpch(fname) 415 | this_val = b.variables[gc_name] 416 | this_unit = b.variables[gc_name].units 417 | if this_unit == 'molec/cm3': 418 | if __debug_level__ > 1: 419 | shell_msg('Converting molec/cm3 to VMR') 420 | 421 | airden = b.variables['TIME-SER_AIRDEN'] 422 | this_val /= airden 423 | else: 424 | if __debug_level__ > 1: 425 | scale = unit_conversion(1.0, 'vmr', this_unit, 'ppp') 426 | shell_msg(' Scaling by {0}'.format(scale)) 427 | this_val = unit_conversion(this_val, 'vmr', this_unit, 'ppp') 428 | sz = list(this_val.shape) 429 | n_to_add = nlev - sz[1] 430 | if n_to_add > 0: 431 | sz[1] = n_to_add 432 | padding = np.empty(sz) 433 | padding.fill(fill_val) 434 | this_val = np.concatenate([this_val, padding], 1) 435 | val.append(flip_dim(this_val, 1)) # remember, GEOS-Chem defines z=1 as surface; MOZART says that's TOA 436 | b.close() 437 | 438 | ncfile.createVariable(moz_name, np.float32, dimensions=('time','lev','lat','lon')) 439 | ncfile.variables[moz_name][:] = np.concatenate(val, 0) 440 | ncfile.variables[moz_name].units = 'VMR' 441 | 442 | 443 | def add_methane(ncfile): 444 | # Add methane based on the GEOS-Chem methane bins. 445 | # For each time, add the year-specific concentrations. This will cause a slight discontinuity at the end of each 446 | # year, but the percent change is small and I believe this is how GEOS-Chem does it (though that's based off of a 447 | # very quick examination of the code). Also, this really should decrease once you get into the stratosphere, but 448 | # generally WRF-Chem does not simulate the stratosphere (at least as I've used it) and I think GEOS-Chem sets the 449 | # CH4 concentration to be the same in all levels. 450 | 451 | years = ncfile.variables['date'][:]/10000 452 | lat = ncfile.variables['lat'][:] 453 | 454 | # Find a chemistry variable to get the shape of 455 | for k in ncfile.variables.keys(): 456 | if moz_suffix in k: 457 | chem_key = k 458 | break 459 | 460 | ch4 = np.zeros_like(ncfile.variables[chem_key][:]) 461 | 462 | for i in range(len(years)): 463 | ch4_bins = gcCH4bins.get_global_ch4(years[i]) 464 | 465 | xx = (lat >= gcCH4bins.north_lats[0]) 466 | ch4[i, :, xx, :] = ch4_bins['north'] 467 | 468 | xx = (lat >= gcCH4bins.north_trop_lats[0]) & (lat < gcCH4bins.north_trop_lats[1]) 469 | ch4[i, :, xx, :] = ch4_bins['north_trop'] 470 | 471 | xx = (lat >= gcCH4bins.south_trop_lats[0]) & (lat < gcCH4bins.south_trop_lats[1]) 472 | ch4[i, :, xx, :] = ch4_bins['south_trop'] 473 | 474 | xx = (lat < gcCH4bins.south_lats[1]) 475 | ch4[i, :, xx, :] = ch4_bins['south'] 476 | 477 | ch4_varname = 'CH4' + moz_suffix 478 | ncfile.createVariable(ch4_varname, np.float32, dimensions=('time','lev','lat','lon')) 479 | ncfile.variables[ch4_varname][:] = ch4 480 | ncfile.variables[ch4_varname].units = 'VMR' 481 | 482 | 483 | def main(): 484 | args = get_args() 485 | write_netcdf(args['outfile'], args['bpchfiles']) 486 | 487 | 488 | if __name__ == '__main__': 489 | main() 490 | -------------------------------------------------------------------------------- /Tools/gc2moz/python/gcCH4bins.py: -------------------------------------------------------------------------------- 1 | from __future__ import print_function 2 | import sys 3 | __author__ = 'Josh' 4 | 5 | north_lats = (30.0, 90.0) 6 | north_trop_lats = (0.0, 30.0) 7 | south_trop_lats = (-30.0, 0.0) 8 | south_lats = (-90.0, 30.0) 9 | 10 | def shell_error(msg, exitcode=1): 11 | print(msg, file=sys.stderr) 12 | exit(exitcode) 13 | 14 | 15 | def shell_msg(msg): 16 | print(msg, file=sys.stderr) 17 | 18 | 19 | def get_global_ch4(year): 20 | north, north_trop, south_trop, south = __ch4(year) 21 | return {'north': north, 'north_trop': north_trop, 'south_trop': south_trop, 'south': south} 22 | 23 | 24 | def __ch4(year): 25 | # Pretty much directly copied from get_global_CH4.F in GeosCore 26 | # Preindustrial years 27 | if year <= 1750: 28 | south = 700.0 29 | south_trop = 700.0 30 | north_trop = 700.0 31 | north = 700.0 32 | 33 | # Modern-day years ... 34 | elif year == 1983: 35 | south = 1583.48 36 | south_trop = 1598.24 37 | north_trop = 1644.37 38 | north = 1706.48 39 | 40 | elif year == 1984: 41 | south = 1597.77 42 | south_trop = 1606.66 43 | north_trop = 1655.62 44 | north = 1723.63 45 | 46 | elif year == 1985: 47 | south = 1608.08 48 | south_trop = 1620.43 49 | north_trop = 1668.11 50 | north = 1736.78 51 | 52 | elif year == 1986: 53 | south = 1619.91 54 | south_trop = 1632.24 55 | north_trop = 1682.88 56 | north = 1752.71 57 | 58 | elif year == 1987: 59 | south = 1630.54 60 | south_trop = 1640.54 61 | north_trop = 1702.05 62 | north = 1763.03 63 | 64 | elif year == 1988: 65 | south = 1642.08 66 | south_trop = 1651.60 67 | north_trop = 1713.07 68 | north = 1775.66 69 | 70 | elif year == 1989: 71 | south = 1654.03 72 | south_trop = 1666.12 73 | north_trop = 1720.53 74 | north = 1781.83 75 | 76 | elif year == 1990: 77 | south = 1663.21 78 | south_trop = 1672.45 79 | north_trop = 1733.84 80 | north = 1791.92 81 | 82 | elif year == 1991: 83 | south = 1673.52 84 | south_trop = 1683.87 85 | north_trop = 1750.68 86 | north = 1800.90 87 | 88 | elif year == 1992: 89 | south = 1687.97 90 | south_trop = 1692.97 91 | north_trop = 1755.94 92 | north = 1807.16 93 | 94 | elif year == 1993: 95 | south = 1687.83 96 | south_trop = 1696.48 97 | north_trop = 1758.86 98 | north = 1810.99 99 | 100 | elif year == 1994: 101 | south = 1692.00 102 | south_trop = 1701.41 103 | north_trop = 1766.98 104 | north = 1817.12 105 | 106 | elif year == 1995: 107 | south = 1701.04 108 | south_trop = 1709.07 109 | north_trop = 1778.25 110 | north = 1822.04 111 | 112 | elif year == 1996: 113 | south = 1701.87 114 | south_trop = 1711.01 115 | north_trop = 1778.08 116 | north = 1825.23 117 | 118 | elif year == 1997: 119 | south = 1708.01 120 | south_trop = 1713.91 121 | north_trop = 1781.43 122 | north = 1825.15 123 | 124 | elif year == 1998: 125 | south = 1716.55 126 | south_trop = 1724.57 127 | north_trop = 1783.86 128 | north = 1839.72 129 | 130 | elif year == 1999: 131 | south = 1725.70 132 | south_trop = 1734.06 133 | north_trop = 1791.50 134 | north = 1842.59 135 | 136 | elif year == 2000: 137 | south = 1728.13 138 | south_trop = 1737.70 139 | north_trop = 1792.42 140 | north = 1840.83 141 | 142 | elif year == 2001: 143 | south = 1726.92 144 | south_trop = 1730.72 145 | north_trop = 1789.11 146 | north = 1841.85 147 | 148 | elif year == 2002: 149 | south = 1729.75 150 | south_trop = 1735.28 151 | north_trop = 1790.08 152 | north = 1842.36 153 | 154 | elif year == 2003: 155 | south = 1729.64 156 | south_trop = 1735.49 157 | north_trop = 1795.89 158 | north = 1853.97 159 | 160 | elif year == 2004: 161 | south = 1728.72 162 | south_trop = 1738.54 163 | north_trop = 1797.30 164 | north = 1849.58 165 | 166 | elif year == 2005: 167 | south = 1727.10 168 | south_trop = 1734.65 169 | north_trop = 1795.73 170 | north = 1849.79 171 | 172 | elif year == 2006: 173 | south = 1726.53 174 | south_trop = 1735.17 175 | north_trop = 1796.30 176 | north = 1848.20 177 | 178 | elif year >= 2007: 179 | south = 1732.52 180 | south_trop = 1741.68 181 | north_trop = 1801.38 182 | north = 1855.55 183 | 184 | if year > 2007: 185 | shell_msg('Using 2007 CH4 bins, 2007 is last year with reported data in GEOS-Chem v9-02') 186 | 187 | else: 188 | raise ValueError('CH4 not defined for {0}'.format(year)) 189 | 190 | # Convert from ppb to straight VMR 191 | north *= 1e-9 192 | north_trop *= 1e-9 193 | south_trop *= 1e-9 194 | south *= 1e-9 195 | 196 | return north, north_trop, south_trop, south 197 | -------------------------------------------------------------------------------- /Tools/met2input: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # Reference this script 4 | cd `dirname $0` 5 | mydir=`pwd -P` 6 | 7 | pyprog="$mydir/../CONFIG/autowrf_namelist_main.py" 8 | dateprog="$mydir/datecompare.py" 9 | datefmt='%Y-%m-%d_%H:%M:%S' 10 | 11 | 12 | # Input arguments: first and last date to process 13 | firstdate="$1" 14 | lastdate="$2" 15 | 16 | # Change into the WRF/run directory, get a list of the met_em files 17 | cd ../../WRFV3/run 18 | mets=met_em* 19 | 20 | # For each met file, switch the namelist (temporarily) to run just 21 | # that time, generate wrfinput_d01, then move that to be 22 | # wrfinput_d01_yyyy-mm-dd_HH:MM:SS 23 | regex=[0-9][0-9][0-9][0-9]-[0-9][0-9]-[0-9][0-9]_[0-9][0-9]:[0-9][0-9]:[0-9][0-9] 24 | for f in $mets; do 25 | if [[ $f =~ $regex ]]; then 26 | mdate="${BASH_REMATCH[0]}" 27 | echo "Met date = $mdate" 28 | if [[ ! -z $firstdate ]] && $(python "$dateprog" -f "$datefmt" "$mdate" lt "$firstdate"); then 29 | echo "Before first date $firstdate, skipping" 30 | continue 31 | elif [[ ! -z $lastdate ]] && $(python "$dateprog" -f "$datefmt" "$mdate" gt "$lastdate"); then 32 | echo "After last date $lastdate, skipping" 33 | continue 34 | fi 35 | 36 | python $pyprog tempmod --start-date="$mdate" --run-time=0h --io_style_emissions=0 --emiss_inpt_opt=0 --bio_emiss_opt=0 37 | echo "Generating wrfinput for $mdate" 38 | ./real.exe 39 | if [[ $? -ne 0 ]]; then 40 | echo "real.exe failed when working on $mdate" >&2 41 | echo "Check rsl.error.0000 if no other error message printed above" >&2 42 | exit 1 43 | fi 44 | mv wrfinput_d01 "wrfinput_d01_$mdate" 45 | fi 46 | done 47 | 48 | echo "Restoring namelist to original state" 49 | python $pyprog tempmod 50 | exit 0 51 | -------------------------------------------------------------------------------- /Tools/pyncdf.py: -------------------------------------------------------------------------------- 1 | from __future__ import print_function 2 | import numpy as np 3 | import os 4 | import re 5 | import subprocess 6 | import sys 7 | 8 | import pdb 9 | 10 | def shell_error(msg, exitcode=1): 11 | print(msg, file=sys.stderr) 12 | exit(exitcode) 13 | 14 | def which(program): 15 | import os 16 | def is_exe(fpath): 17 | return os.path.isfile(fpath) and os.access(fpath, os.X_OK) 18 | 19 | fpath, fname = os.path.split(program) 20 | if fpath: 21 | if is_exe(program): 22 | return program 23 | else: 24 | for path in os.environ["PATH"].split(os.pathsep): 25 | path = path.strip('"') 26 | exe_file = os.path.join(path, program) 27 | if is_exe(exe_file): 28 | return exe_file 29 | 30 | return None 31 | 32 | def parse_varlines(varlines, varfxns): 33 | vals = dict() 34 | for var in varlines: 35 | varname, fullstr = ''.join(var).split('=') 36 | varname = varname.strip() 37 | fullstr = fullstr.rstrip(';') 38 | valstrs = [s.strip() for s in fullstr.split(',')] 39 | this_var = [] 40 | for val in valstrs: 41 | this_var.append(varfxns[varlines.index(var)](val)) 42 | vals[varname] = this_var 43 | 44 | return vals 45 | 46 | def parse_dims(lines): 47 | do_parse = False 48 | dims = dict() 49 | for l in lines: 50 | if do_parse: 51 | if 'variables:' in l: 52 | return dims 53 | varname, valstrraw = l.split('=') 54 | varname = varname.strip() 55 | valstr = valstrraw.split(';')[0].strip() 56 | #pdb.set_trace() 57 | if valstr == 'UNLIMITED': 58 | # Unlimited dimensions should be followed by (xx currently), which gives the current actual dimension length 59 | valstr = re.search('\d+(?=\s*currently)', valstrraw).group() 60 | 61 | dims[varname] = int(valstr) # dimension lengths should always be integers 62 | elif "dimensions:" in l: 63 | do_parse = True 64 | 65 | 66 | def get_var_dims(lines, dims, varname): 67 | do_parse = False 68 | restr = '\s{0}\('.format(varname) 69 | for l in lines: 70 | if do_parse: 71 | if re.search(restr, l): 72 | dimstrs = re.search('(?<=\().*(?=\))',l).group().split(',') 73 | dimlen = [] 74 | for d in dimstrs: 75 | dimlen.append(dims[d.strip()]) 76 | return dimlen 77 | else: 78 | if 'variables:' in l: 79 | do_parse = True 80 | raise RuntimeError('Variable {0} not found in the ncdump'.format(varname)) 81 | 82 | def get_var_dtype(lines, var): 83 | restr1 = '{0}(?=\()'.format(var) 84 | in_vars = False 85 | for l in lines: 86 | if in_vars: 87 | m = re.search(restr1, l) 88 | if m: 89 | data_type = l.split(' ')[0].strip() 90 | if data_type == 'int': 91 | return int 92 | elif data_type == 'float': 93 | return float 94 | elif data_type == 'char': 95 | return str 96 | else: 97 | raise RuntimeError('Data type {0} not recognized (variable is {1})'.format(data_type, m.group())) 98 | elif "variables:" in l: 99 | in_vars = True 100 | 101 | 102 | def call_ncdump_varnames(filename): 103 | if not os.path.isfile(filename): 104 | shell_error('{0} does not exist'.format(filename)) 105 | 106 | ncout = subprocess.check_output(['ncdump', '-h', filename]) 107 | lines = ncout.splitlines() 108 | in_vars = False 109 | varnames = [] 110 | for l in lines: 111 | if in_vars: 112 | m = re.search('(?<=\w\s)[\w\-]+(?=\()',l) 113 | if m: 114 | varnames.append(m.group()) 115 | elif "variables:" in l: 116 | in_vars = True 117 | 118 | return varnames 119 | 120 | 121 | def call_ncdump_vals(variables, filename): 122 | if not os.path.isfile(filename): 123 | shell_error('{0} does not exist'.format(filename)) 124 | 125 | varstr=','.join(variables) 126 | ncout = subprocess.check_output(['ncdump', '-f', 'c', '-v', varstr, filename]) 127 | lines = ncout.splitlines() 128 | dims_lengths = parse_dims(lines) 129 | 130 | # Variable lines all come after one with "data:" in it, and all start with 131 | # "varname = " followed by values 132 | varfxn = dict() 133 | varvals = dict() 134 | 135 | var_restr = '|'.join(variables) 136 | restr2 = '(?<=\s)({0})(?=\()'.format(var_restr) 137 | 138 | 139 | for var in variables: 140 | varfxn[var] = get_var_dtype(lines, var) 141 | vardims = get_var_dims(lines, dims_lengths, var) 142 | varvals[var] = np.zeros(vardims, dtype=varfxn[var]) 143 | 144 | in_data = False 145 | for l in lines: 146 | if in_data: 147 | m = re.search(restr2, l) 148 | if m: 149 | # Get the coordinates for this value and the value itself 150 | val, comment_str = l.split('//') 151 | val = varfxn[m.group()](val.strip(',; ')) 152 | # Seach for a series of numbers separated by commas within parentheses 153 | coord_str = re.search('(?<=\()(\d+,)*\d+(?=\))',comment_str).group() 154 | coords = tuple([int(x) for x in coord_str.split(',')]) # numpy arrays treat tuples as individual indices 155 | varvals[m.group()][coords] = val 156 | elif "data:" in l: 157 | in_data = True 158 | 159 | return varvals 160 | """ 161 | li = 0 162 | while li < len(lines): 163 | l = lines[li] 164 | if not in_data: 165 | if l == 'data:': 166 | in_data = True 167 | else: 168 | m = re.search(restr1, l) 169 | if m is not None: 170 | vi = variables.index(m.group()) 171 | data_type = l.split(' ')[0].strip() 172 | if data_type == 'int': 173 | varfxn[vi] = int 174 | elif data_type == 'float': 175 | varfxn[vi] = float 176 | elif data_type == 'char': 177 | varfxn[vi] = str 178 | else: 179 | raise RuntimeError('Data type {0} not recognized (variable is {1})'.format(data_type, m.group())) 180 | 181 | else: 182 | m = re.search(restr2, l) 183 | if m is not None: 184 | vi = variables.index(m.group()) 185 | this_var = [l] 186 | while True: 187 | li += 1 188 | this_var.append(lines[li]) 189 | if ';' in lines[li]: 190 | break 191 | 192 | varlines[vi] = this_var 193 | m = None 194 | li += 1 195 | return parse_varlines(varlines, varfxn) 196 | """ 197 | 198 | def init(): 199 | if which('ncdump') is None: 200 | raise OSError('Executable "ncdump" not found on path. ncdump is required for pyncdf to function') 201 | 202 | # Always run init() even when importing 203 | init() 204 | if __name__ == '__main__': 205 | pass 206 | 207 | -------------------------------------------------------------------------------- /Tools/splitwps: -------------------------------------------------------------------------------- 1 | ../PREPINPUT/splitwps -------------------------------------------------------------------------------- /autowrfchem_main: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # 3 | # First parse arguments 4 | 5 | # Used when filenames are given relative to the folder that 6 | # this is called from 7 | export AUTOCALLDIR=`pwd -P` 8 | 9 | myname=`basename $0` 10 | cd `dirname $0` 11 | lnk=`readlink $myname` 12 | if [ ! -z "$lnk" ]; then 13 | cd `dirname $lnk` 14 | fi 15 | mydir=`pwd -P` 16 | export AUTOWRFDIR=`pwd -P` 17 | 18 | doconfig=false 19 | namelistonly=false 20 | envvaroverride=false 21 | docompile=false 22 | doclean=false 23 | cleaninput=false 24 | doprepinpt=false 25 | prepargs="" 26 | docheckonly=false 27 | dometonly=false 28 | dofinishonly=false 29 | dorun=false 30 | runargs="" 31 | while [ $# -gt 0 ]; do 32 | case $1 in 33 | -h|--help) 34 | echo "" 35 | echo "$myname: utility to automate the compilation and running of WRF-Chem" 36 | echo " Modes of operation: " 37 | echo "" 38 | echo " ***************************** CONFIGURATION: *****************************" 39 | echo "" 40 | echo " ./$myname config : will require user input to generate" 41 | echo " the necessary configuration files (so don't run this as" 42 | echo " part of a batch job). It will also set a global namelist" 43 | echo " that will ensure WRF, WPS, and NEI are all set to the same" 44 | echo " domain." 45 | echo "" 46 | echo " ./$myname config override : will include the necessary environmental" 47 | echo " variables to run WRF-Chem (EM_CORE=1, WRF_CHEM=1, etc) regardless" 48 | echo " of how those variables are set in your shell. It will NOT modify" 49 | echo " these variables in your shell except when this program is running." 50 | echo "" 51 | echo " ./$myname config namelist : will only reconfigure the" 52 | echo " namelist. Useful if you just need to change the domain or" 53 | echo " time period." 54 | echo "" 55 | echo "" 56 | echo " ***************************** COMPILATION: *****************************" 57 | echo "" 58 | echo " ./$myname compile : will execute the compilation of all" 59 | echo " the components of WRF-Chem and it's input preparation," 60 | echo " including WRF-Chem, WPS, convert_emiss, emiss_v0x.F," 61 | echo " megan_bio_emiss, and mozbc. It will skip compiling if" 62 | echo " it finds an existing .exe file." 63 | echo "" 64 | echo " ./$myname clean : will remove all compiled files for a clean" 65 | echo " compile." 66 | echo "" 67 | echo " ./$myname clean input : will clean up the input files instead." 68 | echo " It does not clean the code." 69 | echo "" 70 | echo "" 71 | echo " ***************************** INPUT PREPARATION: *****************************" 72 | echo "" 73 | echo " ./$myname prepinpt : will prepare meteorology, NEI emissions," 74 | echo " MEGAN emissions, and MOZBC initial and boundary conditions." 75 | echo "" 76 | echo " ./$myname prepinpt check : will just check that everything required" 77 | echo " for preparing the input data is ready to go. Add met-only as an" 78 | echo " additional argument to only check what is necessary for preparing" 79 | echo " meteorology." 80 | echo "" 81 | echo " ./$myname prepinpt met-only : will only prepare met data, and run" 82 | echo " real.exe after finishing WPS." 83 | echo "" 84 | echo " ./$myname prepinpt met-only --noreal : will not run real at the end" 85 | echo " of preparing the met input." 86 | echo "" 87 | echo " ./$myname prepinpt met-only --wpsdir= : run WPS in a different" 88 | echo " directory, relative to the top directory of AutoWRFChem, i.e. the one that" 89 | echo " contains WRFV3, WPS, etc. This is not really meant to be called manually," 90 | echo " instead, it is a component of the split-met option" 91 | echo "" 92 | echo " ./$myname prepinpt split-met [ --ndays= ] [ --submitfile= ]" 93 | echo " Divides the WPS preprocessing up into time periods of ndays days, which is" 94 | echo " 30 by default. If a file is given via the --submitfile option, it will submit" 95 | echo " jobs to a batch scheduler (e.g. SLURM or QSUB) to carry out all the necessary" 96 | echo " met preprocessing. The file must be a batch submission script with a line" 97 | echo " #AUTOWRFEXEC specified. That will be replaced by the necessary command to" 98 | echo " run all the different WPS subdivisions." 99 | echo "" 100 | echo " ./$myname prepinpt chem-only : run everything but WPS." 101 | echo "" 102 | echo " ./$myname prepinpt finish : will only prepare inputs that are missing." 103 | echo " Note that this is pretty simple minded with respect to met data, and" 104 | echo " will simply assume that WPS succeeded if ANY met_em files are present" 105 | echo " in WRFV3/run." 106 | echo "" 107 | echo "" 108 | echo " ***************************** RUNNING WRF: *****************************" 109 | echo "" 110 | echo " ./$myname run : will run WRF-Chem. This will first check" 111 | echo " that the WRF namelist.input file matches what was configured" 112 | echo " by ./$myname config. If running WRF-Chem in parallel, must pass" 113 | echo " the number of tasks to start (x) as --ntasks=x or use --alt-mpi-cmd" 114 | echo " (see below). If running in serial, must pass the flag --nompi instead" 115 | echo "" 116 | echo " ./$myname run --alt-mpi-cmd : Use the command specified in the environmental" 117 | echo " variable AWC_MPICMD to run WRF rather than mpirun -np \$ntasks. This must" 118 | echo " be EXACTLY the command you would execute in the WRFV3/run directory to" 119 | echo " start WRF. E.g. if you would use 'mpiexec -n 64 wrf.exe', then AWC_MPICMD" 120 | echo " must be that whole command." 121 | echo "" 122 | echo " ./$myname run rst : will try to find the last wrfrst file within the" 123 | echo " time period in the namelist in WRFV3/run and start from there." 124 | echo " If it cannot find a wrfrst file, it will abort." 125 | echo "" 126 | echo " ./$myname run rst --allow-no-file : if no restart files in the proper" 127 | echo " time period found, this will start from the beginning instead of" 128 | echo " aborting." 129 | echo "" 130 | echo " ./$myname run --run-for=x : only run for x, where x is a time period in days," 131 | echo " hours, minutes, and seconds. Works with rst, in fact, is primarily intended" 132 | echo " to work with rst so that you can break a long run up into smaller chunks." 133 | echo " This uses the tempmod function of the python namelist editor, so it retains" 134 | echo " knowledge of what your intended overall end time is and will not go past it." 135 | echo " Example: ./$myname run rst --run-for=28d will run for 28 days from the last" 136 | echo " restart file." 137 | echo "" 138 | echo " ./$myname run --dry-run : do everything normally done for $myname run" 139 | echo " except start WRF; instead, it will print out the command it would use" 140 | echo " This can be used with any of the previous flags to check how $myname" 141 | echo " would modify the namelist, try to execute WRF, etc." 142 | echo "" 143 | echo "" 144 | echo " ***************************** NOTES *****************************" 145 | echo "" 146 | echo " Exit codes for the compile and prepinpt steps will indicate which step" 147 | echo " failed using the numeric value as a binary flag. For compile:" 148 | echo " 1st bit = WRF (compile) or real.exe (prepinpt met-only)" 149 | echo " 2nd bit = WPS" 150 | echo " 3rd bit = convert_emiss" 151 | echo " 4th bit = emiss_v0x" 152 | echo " 5th bit = megan_bio_emiss" 153 | echo " 6th bit = mozbc" 154 | echo " 7th bit = postcheck (prepinpt only)" 155 | echo " So an exit code of 1 means only WRF failed to compile, while 56 (= 000111)" 156 | echo " means emiss_v0x, megan_bio_emiss, and mozbc all failed to compile." 157 | echo " The codes are the same for prepinpt, except that the first bit will only" 158 | echo " be set if you run prepinpt with the 'met-only' option, in which case it" 159 | echo " reflects the success or failure of real.exe after WPS is completed." 160 | echo "" 161 | echo " postcheck is a special case for prepinpt, a failure there means that the postcheck" 162 | echo " utility found something wrong in the wrfinput or wrfbdy file (like a variable with" 163 | echo " an average value near 0)." 164 | echo "" 165 | echo " If your cluster has the concept of 'login' vs. 'compute' nodes, i.e. nodes" 166 | echo " expected to be used only for light tasks (login) and those for heavy computing" 167 | echo " (compute) then 'config', 'clean', and 'prepinpt check' can be safely run on" 168 | echo " login-type nodes, but all other modes (including regular prepinpt) should be" 169 | echo " run on compute-type nodes. ('compile' is a possible exception, if you normally" 170 | echo " do compilation on a login-type node, this should be fine.)" 171 | echo "" 172 | echo " Note that this utility is only set up to handle one domain currently." 173 | echo "" 174 | exit 0 175 | ;; 176 | config) 177 | doconfig=true 178 | ;; 179 | namelist) 180 | namelistonly=true 181 | ;; 182 | override|--override) 183 | envvaroverride=true 184 | runargs="$runargs --override" 185 | ;; 186 | compile) 187 | docompile=true 188 | ;; 189 | clean) 190 | doclean=true 191 | ;; 192 | input) 193 | cleaninput=true 194 | ;; 195 | prepinpt) 196 | doprepinpt=true 197 | ;; 198 | check) 199 | prepargs="$prepargs --check-only" 200 | ;; 201 | met-only) 202 | prepargs="$prepargs --met-only" 203 | ;; 204 | chem-only) 205 | prepargs="$prepargs --chem-only" 206 | ;; 207 | split-met) 208 | prepargs="$prepargs --splitmet" 209 | ;; 210 | finish) 211 | prepargs="$prepargs --finish-only" 212 | ;; 213 | --noreal|--wpsdir*|--ndays*|--submitfile*) 214 | prepargs="$prepargs $1" 215 | ;; 216 | run) 217 | dorun=true 218 | ;; 219 | --ntasks=*) 220 | runargs="$runargs $1" 221 | ;; 222 | --alt-mpi-cmd) 223 | runargs="$runargs $1" 224 | ;; 225 | --nompi) 226 | runargs="$runargs $1" 227 | ;; 228 | rst) 229 | runargs="$runargs --restart" 230 | ;; 231 | --allow-no-file) 232 | runargs="$runargs --allow-no-file" 233 | ;; 234 | --run-for*) 235 | runargs="$runargs $1" 236 | ;; 237 | -D|--dry-run) 238 | runargs="$runargs --dry-run" 239 | ;; 240 | *) 241 | echo "$1 is not a recognized option" 242 | exit 1 243 | ;; 244 | esac 245 | shift 246 | done 247 | 248 | # Check that directory structure is intact 249 | cd .. 250 | missingdir="" 251 | if [ ! -d WRFV3 ]; then missingdir="$missingdir WRFV3"; fi 252 | if [ ! -d WPS ]; then missingdir="$missingdir WPS"; fi 253 | if [ ! -d NEI ]; then missingdir="$missingdir NEI"; fi 254 | if [ ! -d MEGAN ]; then missingdir="$missingdir MEGAN"; fi 255 | if [ ! -d MOZBC ]; then missingdir="$missingdir MOZBC"; fi 256 | 257 | if [ ! -z "$missingdir" ]; then 258 | echo "The directory(ies) $missingdir are not present as child directories." 259 | exit 1 260 | fi 261 | 262 | 263 | cd $mydir 264 | if $doconfig; then 265 | configopts="" 266 | if $namelistonly; then 267 | configopts="$configopts --namelist-only" 268 | fi 269 | if $envvaroverride; then 270 | configopts="$configopts --override" 271 | fi 272 | ./configure $configopts # set env vars and all namelist options. 273 | configexit=$? 274 | 275 | if [ $configexit -eq 0 ]; then 276 | if ! $namelistonly; then 277 | echo "" 278 | echo "*****************************************************************************" 279 | echo "Configuration files generated. Run $myname with 'compile' to begin compilation" 280 | echo "*****************************************************************************" 281 | echo "" 282 | fi 283 | fi 284 | 285 | exit $configexit 286 | fi 287 | if $doclean; then 288 | if $cleaninput; then 289 | $mydir/cleanall --input 290 | else 291 | $mydir/cleanall 292 | fi 293 | exit $? 294 | fi 295 | if $docompile; then 296 | $mydir/compileall 297 | compexit=$? 298 | if [ $compexit -ne 0 ]; then 299 | echo "***********************************************" 300 | echo "One or more compilations failed (exit status $compexit)" 301 | echo "***********************************************" 302 | fi 303 | exit $compexit 304 | fi 305 | if $doprepinpt; then 306 | $mydir/prepinpt $prepargs 307 | exit $? 308 | fi 309 | if $dorun; then 310 | if $dometonly; then 311 | runargs="$runargs --met-only" 312 | fi 313 | 314 | $mydir/runwrf $runargs 315 | exit $? 316 | fi 317 | 318 | echo "No action specified. See help for usage of $myname." 319 | exit 0 320 | 321 | -------------------------------------------------------------------------------- /cleanall: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | mydir=`dirname $0` 4 | cd $mydir 5 | mydir=`pwd -P` # make the path absolute rather than relative 6 | 7 | if [ "$1" == "--input" ]; then 8 | cleaninpt=true 9 | else 10 | cleaninpt=false 11 | fi 12 | 13 | if $cleaninpt; then 14 | cd ../WRFV3/run 15 | rm -f met_em* 16 | rm -f wrfbiochemi* 17 | rm -f wrfchemi* 18 | rm -f wrfem* 19 | rm -f wrfbdy* 20 | rm -f wrfinput* 21 | 22 | cd ../../WPS 23 | rm -f met_em* 24 | 25 | cd ../NEI/src/v04 26 | rm -f wrfem* 27 | 28 | cd ../../../MEGAN/src 29 | rm -f wrfbiochemi* 30 | else 31 | 32 | cd ../WRFV3 33 | ./clean -a 34 | 35 | cd ../WPS 36 | ./clean 37 | 38 | # currently only the version 4 NEI gridding code has a Makefile 39 | cd ../NEI/src/v04 40 | make clean 41 | 42 | cd ../../../MEGAN/src/ 43 | rm megan_bio_emiss 44 | make clean 45 | 46 | cd ../../MOZBC/src 47 | make clean 48 | 49 | fi 50 | -------------------------------------------------------------------------------- /compileall: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # This will compile all the necessary utilities to run WRF-Chem and 3 | # prepare its input. 4 | 5 | mydir=`dirname $0` 6 | cd $mydir 7 | mydir=`pwd -P` 8 | 9 | # Make sure the folder for the compilation logs exists 10 | if [ ! -d "$mydir/COMPLOGS" ]; then 11 | mkdir "$mydir/COMPLOGS" 12 | fi 13 | 14 | # Also make sure to source the environmental variables 15 | . "$mydir/envvar_wrfchem.cfg" 16 | 17 | # The exit status needs to reflect which compilations succeeded 18 | # start as 111111 (= 63) and remove the corresponding bit as each 19 | # one succeeds 20 | exitstat=63 21 | 22 | # First WRF-Chem 23 | cd ../WRFV3 24 | wrfcomp=false 25 | echo "" 26 | echo "Compiling WRF-Chem..." 27 | if [ ! -f configure.wrf ]; then 28 | echo " ERROR: configure.wrf is not present; cannot compile WRF." 29 | echo " Run 'autowrfchem config' to produce the necessary file." 30 | echo " Remember, the clean command removes the configure.wrf file." 31 | else 32 | ./compile em_real >& "$mydir/COMPLOGS/compile_wrf.log" 33 | wrfexit=$? 34 | if [ $wrfexit -eq 0 ]; then 35 | wrfcomp=true 36 | exitstat=$((exitstat - 1)) 37 | fi 38 | fi 39 | 40 | # Next WPS. Only compile if WRF successfully did. 41 | cd ../WPS 42 | echo "" 43 | echo "Compiling WPS..." 44 | if $wrfcomp; then 45 | if [ ! -f configure.wps ]; then 46 | echo "ERROR: configure.wps is not present; cannot compile WPS." 47 | echo "Run 'autowrfchem config' to produce the necessary file." 48 | echo "Remember, the clean command removes the configure.wps file." 49 | else 50 | ./compile >& "$mydir/COMPLOGS/compile_wps.log" 51 | wpsexit=$? 52 | if [ $wpsexit -eq 0 ]; then 53 | # WPS has 3 pieces so double check that all of them were created 54 | wpssecc=true 55 | if [ ! -f geogrid.exe ]; then 56 | echo "geogrid did not compile" 57 | wpssecc=false 58 | fi 59 | if [ ! -f ungrib.exe ]; then 60 | echo "ungrib did not compile" 61 | wpssecc=false 62 | fi 63 | if [ ! -f metgrid.exe ]; then 64 | echo "metgrid did not compile" 65 | wpssecc=false 66 | fi 67 | 68 | if $wpssucc; then 69 | exitstat=$((exitstat - 2)) 70 | fi 71 | fi 72 | fi 73 | else 74 | echo " Cannot compile WPS if WRF did not compile. Skipping for" 75 | echo " now. Check the compile logs in" 76 | echo " $mydir/COMPLOGS" 77 | echo " to see why WRF failed to build and correct that issue before" 78 | echo " rerunning 'autowrfchem compile'" 79 | fi 80 | 81 | # Compile the convert_emiss.exe program 82 | cd ../WRFV3 83 | echo "" 84 | echo "Compiling convert_emiss.exe..." 85 | if $wrfcomp; then 86 | if [ ! -f configure.wrf ]; then 87 | echo " ERROR: configure.wrf is not present; cannot compile WRF." 88 | echo " Run 'autowrfchem config' to produce the necessary file." 89 | echo " Remember, the clean command removes the configure.wrf file." 90 | else 91 | ./compile emi_conv >& "$mydir/COMPLOGS/compile_emi-conv.log" 92 | convexit=$? 93 | if [ $convexit -eq 0 ]; then 94 | exitstat=$((exitstat - 4 )) 95 | cd run 96 | ln -sf ../chem/convert_emiss.exe 97 | cd .. 98 | fi 99 | fi 100 | else 101 | echo " Cannot compile convert_emiss if WRF did not compile." 102 | echo " Skipping for now. Check the compile logs in" 103 | echo " $mydir/COMPLOGS" 104 | echo " to see why WRF failed to build and correct that issue before" 105 | echo " rerunning 'autowrfchem compile'" 106 | fi 107 | # Up next: NEI gridding code. Currently only v04 has a makefile. 108 | cd ../NEI/src/v04 109 | echo "" 110 | echo "Compiling NEI emiss_v04..." 111 | make >& "$mydir/COMPLOGS/compile_emissv04.log" 112 | nei11exit=$? 113 | if [ $nei11exit -eq 0 ]; then 114 | exitstat=$((exitstat - 8)) 115 | fi 116 | 117 | 118 | # Now MEGAN 119 | cd ../../../MEGAN/src 120 | echo "" 121 | echo "Compiling megan_bio_emiss..." 122 | ./make_util megan_bio_emiss >& "$mydir/COMPLOGS/compile_megan.log" 123 | meganexit=$? 124 | if [ $meganexit -eq 0 ]; then 125 | exitstat=$((exitstat - 16)) 126 | fi 127 | 128 | # And MOZBC 129 | cd ../../MOZBC/src 130 | echo "" 131 | echo "Compiling MOZBC..." 132 | ./make_mozbc >& "$mydir/COMPLOGS/compile_mozbc.log" 133 | mozbcexit=$? 134 | if [ $mozbcexit -eq 0 ]; then 135 | exitstat=$((exitstat - 32)) 136 | fi 137 | 138 | exit $exitstat 139 | -------------------------------------------------------------------------------- /configure: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # This program will carry out the configuration of WRF-Chem, including 3 | # 1) configuring the environmental variables 4 | # 2) configuring WRF and WPS 5 | # 3) setting up the namelist 6 | 7 | mydir=`dirname $0` 8 | myname=`basename $0` 9 | cd $mydir 10 | mydir=`pwd -P` # transform the directory from relative to absolute 11 | 12 | namelistonly=false 13 | envvaropts="" 14 | while [ $# -gt 0 ]; do 15 | case $1 in 16 | --namelist-only) 17 | namelistonly=true 18 | ;; 19 | --override) 20 | envvaropts="--override" 21 | ;; 22 | esac 23 | shift 24 | done 25 | 26 | if ! $namelistonly; then 27 | 28 | # Run the environmental variable configuration 29 | CONFIG/wrfenvvar $envvaropts 30 | if [ $? -ne 0 ]; then 31 | exit 1 32 | fi 33 | . $mydir/envvar_wrfchem.cfg # source the environmental variable file 34 | 35 | # Run the WRF and WPS configurations 36 | echo "You will now be asked to configure WRF by choosing your system's architecture and a nesting scheme" 37 | echo "Remember that WRF-Chem requires distributed memory (dmpar) and cannot use shared memory (smpar)." 38 | echo -n "Press ENTER to continue." 39 | read usercont 40 | cd ../WRFV3 41 | ./configure 42 | 43 | echo "You will now be asked to configure WPS by choosing your system's architecture. Remember that WPS" 44 | echo "can run in serial in most cases." 45 | echo -n "Press ENTER to continue." 46 | read usercont 47 | cd ../WPS 48 | ./configure 49 | cd $mydir 50 | 51 | # Run the second configure program. Currently this just sets the meteorology path and the met type. 52 | CONFIG/config 53 | 54 | # Run the namelist program. Only print this message if doing the full configuration (otherwise I think the 55 | # user knows that they are setting the namelists) 56 | echo "Finally you can set up your namelists. The Python program about to be called will allow you to interactively" 57 | echo "set the namelist options. It will ensure that any common options are matched between the WRF and WPS namelists." 58 | echo "The resulting namelists will be placed in $mydir/CONFIG" 59 | echo "and linked to the WRFV3/run, WPS, and NEI/src/v0x directories." 60 | echo -n "Press ENTER to continue." 61 | read usercont 62 | 63 | fi 64 | 65 | python CONFIG/autowrf_namelist_main.py 66 | 67 | # Link the resulting namelists in each place needed (the fancy new NEI program will take 68 | # the WPS namelist as well) 69 | cd ../WRFV3/run 70 | if [ -e namelist.input -o -L namelist.input ]; then 71 | mv namelist.input namelist.input.autowrf-backup 72 | fi 73 | ln -s $mydir/CONFIG/namelist.input 74 | 75 | cd ../../WPS 76 | if [ -e namelist.wps -o -L namelist.wps ]; then 77 | mv namelist.wps namelist.wps.autowrf-backup 78 | fi 79 | ln -s $mydir/CONFIG/namelist.wps 80 | 81 | cd ../NEI/src/v04 82 | if [ -e namelist.wps -o -L namelist.wps ]; then 83 | mv namelist.wps namelist.wps.autowrf-backup 84 | fi 85 | ln -s $mydir/CONFIG/namelist.wps 86 | cd ../v03 87 | if [ -e namelist.wps -o -L namelist.wps ]; then 88 | mv namelist.wps namelist.wps.autowrf-backup 89 | fi 90 | ln -s $mydir/CONFIG/namelist.wps 91 | 92 | echo " Configuration complete! " 93 | 94 | -------------------------------------------------------------------------------- /prepinpt: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | mydir=`dirname $0` 4 | cd "$mydir" 5 | mydir=`pwd -P` 6 | wrfrun="$mydir/../WRFV3/run" 7 | pyprog="$mydir/CONFIG/autowrf_namelist_main.py" 8 | 9 | checkonly=false 10 | finishonly=false 11 | metonly=false 12 | doreal=true # only used if metonly is true 13 | chemonly=false 14 | wpsargs="" 15 | dosplit=false 16 | splitargs="" 17 | 18 | while [ $# -gt 0 ]; do 19 | case "$1" in 20 | --check-only) 21 | checkonly=true 22 | ;; 23 | --finish-only) 24 | finishonly=true 25 | ;; 26 | --met-only) 27 | metonly=true 28 | pcargs="--met-only" 29 | ;; 30 | --chem-only) 31 | chemonly=true 32 | pcargs="--chem-only" 33 | ;; 34 | --noreal) 35 | doreal=false 36 | ;; 37 | --wpsdir*) 38 | wpsdir=${1#*=} 39 | wpsargs="$wpsargs $wpsdir" 40 | ;; 41 | --splitmet) 42 | dosplit=true 43 | pcargs="--met-only" 44 | ;; 45 | --ndays*|--submitfile*) 46 | splitargs="$splitargs $1" 47 | ;; 48 | esac 49 | shift 50 | done 51 | 52 | 53 | # Do a quick check of the most important namelist options 54 | # before starting the prep 55 | PREPINPUT/precheck $pcargs 56 | chkexit=$? 57 | if [ $chkexit -ne 0 ]; then 58 | exit $chkexit 59 | fi 60 | 61 | if $checkonly; then 62 | exit 0 63 | fi 64 | 65 | if $finishonly; then 66 | cd ../WRFV3/run 67 | pwd 68 | ls met_em* >& /dev/null 69 | if [ $? -eq 0 ]; then 70 | # Met files linked in WRFV3/run, no need to redo 71 | dowps=false 72 | else 73 | dowps=true 74 | fi 75 | 76 | if [ -f wrfem_00to12Z -a -f wrfem_12to24Z ]; then 77 | doemiss=false 78 | else 79 | doemiss=true 80 | fi 81 | 82 | if [ -f wrfchemi_00z_d01 -a -f wrfchemi_12z_d01 ]; then 83 | doconv=false 84 | else 85 | doconv=true 86 | fi 87 | 88 | if [ -f wrfbiochemi_d01 ]; then 89 | domegan=false 90 | else 91 | domegan=true 92 | fi 93 | domozbc=true 94 | cd "$mydir" 95 | elif $metonly; then 96 | dowps=true 97 | doemiss=false 98 | doconv=false 99 | domegan=false 100 | domozbc=false 101 | elif $chemonly; then 102 | dowps=false 103 | doemiss=true 104 | doconv=true 105 | domegan=true 106 | domozbc=true 107 | else 108 | dowps=true 109 | doemiss=true 110 | doconv=true 111 | domegan=true 112 | domozbc=true 113 | fi 114 | 115 | # Are we doing FDDA nudging? If so, we'll want to turn that 116 | # off for each run of real.exe EXCEPT the last one 117 | python $pyprog --check-wrf-opt --grid_fdda=1 118 | fdda_check=$? 119 | if [[ $fdda_check == 0 ]]; then 120 | export FDDA_ON=1 121 | fi 122 | 123 | # If doing met splitting, do it now and then stop 124 | if $dosplit; then 125 | $mydir/PREPINPUT/splitwps $splitargs 126 | exit $? 127 | fi 128 | 129 | # Ensure that namelist.input is linked 130 | # Backup only if not already a link to somewhere 131 | cd ../WRFV3/run 132 | if [ -f namelist.input ]; then 133 | lnk=`readlink namelist.input` 134 | if [ ! -z $lnk ]; then 135 | mv namelist.input namelist.input.autowrf-backup 136 | fi 137 | fi 138 | ln -sf "$mydir/CONFIG/namelist.input" 139 | cd "$mydir" 140 | 141 | # The exit status needs to be 0 if everything works, 142 | # otherwise have bits representing which one failed 143 | # start as 011111 and remove 1's as things succeed 144 | # = 2+4+8+16+32 = 62 145 | # 2 = WPS 146 | # 4 = emiss_v0x 147 | # 8 = convert_emiss 148 | # 16 = MEGAN 149 | # 32 = MOZBC 150 | # 64 = postcheck.py - should run for any case 151 | # 1 = real.exe after WPS during met-only 152 | exitstat=0 153 | if $metonly && $doreal; then 154 | exitstat=$((exitstat+1)) 155 | fi 156 | if $dowps; then 157 | exitstat=$((exitstat+2)) 158 | fi 159 | if $doemiss; then 160 | exitstat=$((exitstat+4)) 161 | fi 162 | if $doconv; then 163 | exitstat=$((exitstat+8)) 164 | fi 165 | if $domegan; then 166 | exitstat=$((exitstat+16)) 167 | fi 168 | if $domozbc; then 169 | exitstat=$((exitstat+32)) 170 | fi 171 | 172 | 173 | 174 | if $dowps; then 175 | echo "Running WPS..." 176 | PREPINPUT/prepwps $wpsargs 177 | wpsexit=$? 178 | if [ $wpsexit -eq 0 ]; then 179 | exitstat=$((exitstat-2)) 180 | fi 181 | else 182 | echo "Not running WPS, either met files exist or chem-only requested." 183 | fi 184 | 185 | if $metonly; then 186 | if $doreal; then 187 | if [ $wpsexit -eq 0 ]; then 188 | echo "Running real.exe since only the meterology has been requested." 189 | cd ../WRFV3/run 190 | python $pyprog tempmod --emiss_opt=0 --bio_emiss_opt=0 --restart=.false. 191 | ./real.exe 192 | realexit=$? 193 | if [ $realexit -eq 0 ]; then 194 | exitstat=$((exitstat-1)) 195 | fi 196 | 197 | python "$mydir/PREPINPUT/postcheck.py" --mode=met "$wrfrun/wrfinput_d01" "$wrfrun/wrfbdy_d01" 198 | postexit=$? 199 | if [ $postexit -ne 0 ]; then 200 | exitstat=$((exitstat+64)) 201 | fi 202 | else 203 | echo "WPS failed, not running real.exe." 204 | fi 205 | fi 206 | exit $exitstat 207 | fi 208 | 209 | if $doemiss; then 210 | echo "Running emiss_v04..." 211 | PREPINPUT/prepnei_intermediate 212 | neiexit=$? 213 | if [ $neiexit -eq 0 ]; then 214 | exitstat=$((exitstat-4)) 215 | fi 216 | else 217 | echo "wrfem files found, skipping emiss_v04" 218 | fi 219 | 220 | 221 | if $doconv; then 222 | if [ -f $wrfrun/wrfem_00to12z_d01 -a -f $wrfrun/wrfem_12to24z_d01 ]; then 223 | echo "Running convert_emiss.exe" 224 | PREPINPUT/prepnei_convert 225 | convexit=$? 226 | if [ $convexit -eq 0 ]; then 227 | exitstat=$((exitstat-8)) 228 | fi 229 | else 230 | echo "wrfem_00to12z_d01 and/or wrfem_12to24z_d01 not present in" 231 | echo "$wrfrun, cannot execute convert_emiss.exe" 232 | fi 233 | else 234 | echo "wrfchemi files detected, skipping convert_emiss.exe" 235 | fi 236 | 237 | if $domegan; then 238 | echo "Running MEGAN" 239 | PREPINPUT/prepmegan 240 | meganexit=$? 241 | if [ $meganexit -eq 0 ]; then 242 | exitstat=$((exitstat-16)) 243 | fi 244 | else 245 | echo "wrfbiochemi file found, skipping MEGAN" 246 | fi 247 | 248 | if $domozbc; then 249 | echo "Running MOZBC..." 250 | PREPINPUT/prepmozbc 251 | mozbcexit=$? 252 | if [ $mozbcexit -eq 0 ]; then 253 | exitstat=$((exitstat-32)) 254 | fi 255 | 256 | postchk_mode="both" 257 | else 258 | postchk_mode="met" 259 | fi 260 | 261 | python "$mydir/PREPINPUT/postcheck.py" --mode=$postchk_mode "$wrfrun/wrfinput_d01" "$wrfrun/wrfbdy_d01" 262 | postexit=$? 263 | if [ $postexit -ne 0 ]; then 264 | exitstat=$((exitstat+64)) 265 | fi 266 | 267 | exit $exitstat 268 | -------------------------------------------------------------------------------- /runwrf: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # 3 | # This is it! Time to run WRF. Will do some checking first though. 4 | # 1) Is the namelist.input correctly linked? If not, error because 5 | # the user needs to check that the emissions and meteorology are 6 | # correct. 7 | # 2) The emissions files are present. 8 | # 3) The met files for the start and end date are present 9 | 10 | cd `dirname $0` 11 | myname=`basename $0` 12 | mydir=`pwd -P` 13 | pyprog="$mydir/CONFIG/autowrf_namelist_main.py" 14 | pydate="$mydir/Tools/datecompare.py" 15 | 16 | # See if this was called with the "override" flag 17 | override=false 18 | usempi=true 19 | useenvcmd=false 20 | metonly=false 21 | rst=false 22 | errnorst=true 23 | dryrun=false 24 | while [ $# -gt 0 ]; do 25 | case $1 in 26 | override|--override) 27 | override=true 28 | ;; 29 | --met-only) 30 | metonly=true 31 | ;; 32 | --ntasks*) 33 | i=`expr index "$1" "="` 34 | ntasks=${1:i} 35 | usempi=true 36 | ;; 37 | --alt-mpi-cmd) 38 | useenvcmd=true 39 | usempi=true 40 | ;; 41 | --nompi) 42 | usempi=false 43 | ;; 44 | --restart) 45 | rst=true 46 | ;; 47 | --allow-no-file) 48 | errnorst=false 49 | ;; 50 | --run-for*) 51 | i=`expr index $1 "="` 52 | runfor=${1:i} 53 | ;; 54 | --dry-run) 55 | dryrun=true 56 | ;; 57 | esac 58 | shift 59 | done 60 | 61 | 62 | # INPUT CHECKING 63 | if $usempi && ! $useenvcmd; then 64 | if [ -z "$ntasks" ]; then 65 | echo "Must pass a number of tasks to start with --ntasks= if running WRF in parallel" 66 | echo "or use the --mpicmd= option to specify an alternate command to execute WRF." 67 | echo "(give option --nompi to run using syntax ./wrf.exe)" 68 | exit 1 69 | fi 70 | fi 71 | 72 | # Undo any other temporary changes to the namelist 73 | python "$pyprog" tempmod 74 | 75 | cd ../WRFV3/run 76 | if [ ! "namelist.input" -ef "$mydir/CONFIG/namelist.input" ]; then 77 | echo "WRFV3/run/namelist.input is not a link to" 78 | echo " $mydir/CONFIG/namelist.input" 79 | echo "This should NOT be the case if you have run 'autowrfchem prepinpt'" 80 | if ! $override; then 81 | echo "Aborting run. To force the run, use 'autowrfchem run --override'" 82 | exit 1 83 | fi 84 | fi 85 | 86 | missingfile=false 87 | if ! $metonly; then 88 | if [ ! -f wrfchemi_00z_d01 -o ! -f wrfchemi_12z_d01 ]; then 89 | echo "One or both of wrfchemi_00z_d01, wrfchemi_12z_d01 not found in WRFV3/run" 90 | missingfile=true 91 | fi 92 | 93 | if [ ! -f wrfbiochemi_d01 ]; then 94 | echo "wrfbiochemi_d01 not found in WRFV3/run" 95 | missingfile=true 96 | fi 97 | fi 98 | 99 | 100 | if $missingfile; then 101 | echo "One or more missing files, aborting run." 102 | exit 1 103 | fi 104 | 105 | # If the --run-for flag is given, we'll need to set the run time to match. Do not exceed the 106 | # given end date, so we'll need to test that after figuring out the start time (from the 107 | # restart file if specified). Now we can go ahead and check vs. the specified start time 108 | nl_startdate=$(python "$pyprog" get-wps-opt --no-quotes --start_date) 109 | nl_enddate=$(python "$pyprog" get-wps-opt --no-quotes --end_date) 110 | if [[ ! -z $runfor ]]; then 111 | python "$pydate" --datefmt='%Y-%m-%d_%H:%M:%S' "$nl_startdate" "+$runfor" lt "$nl_enddate" 112 | if [[ $? == 0 ]]; then 113 | runtimearg="--run-time=$runfor" 114 | fi 115 | fi 116 | 117 | # If we need to restart, then find the last restart file and set the date to it 118 | if $rst; then 119 | rstfile=`$mydir/RUNUTILS/lastrst` 120 | if [[ ! -z $rstfile ]]; then 121 | regex="[0-9][0-9][0-9][0-9]-[0-9][0-9]-[0-9][0-9]_[0-9][0-9]:[0-9][0-9]:[0-9][0-9]" 122 | if [[ $rstfile =~ $regex ]]; then 123 | rdate="${BASH_REMATCH[0]}" 124 | python "$pydate" --datefmt='%Y-%m-%d_%H:%M:%S' "$rdate" "+$runfor" "ge" "$nl_enddate" 125 | if [[ $? == 0 ]]; then 126 | # If the specified run time would cause the new end time to be after the one 127 | # given in the namelist, then do not modify the run time 128 | runtimearg="" 129 | fi 130 | python $pyprog tempmod --start-date="$rdate" --restart=".true." $runtimearg 131 | else 132 | echo "$myname: Could not determine date of restart file ($rstfile), aborting run" 133 | exit 1 134 | fi 135 | else 136 | if $errnorst; then 137 | echo "$myname: No restart file in the time period of the namelist found, aborting run" 138 | exit 1 139 | else 140 | echo "No restart file in the time period of the namelist found, starting at beginning" 141 | python $pyprog tempmod --restart=".false." $runtimearg 142 | fi 143 | fi 144 | elif [[ ! -z $runfor ]]; then 145 | python $pyprog tempmod $runtimearg 146 | fi 147 | 148 | # Format the run command 149 | if $useenvcmd; then 150 | if [[ -z $AWC_MPICMD ]]; then 151 | echo "Error trying to use --alt-mpi-cmd: Environmental variable AWC_MPICMD not set, aborting run" 152 | exit 1 153 | fi 154 | runcmd="$AWC_MPICMD" 155 | elif $usempi; then 156 | runcmd="mpirun -np $ntasks wrf.exe" 157 | else 158 | runcmd="./wrf.exe >& runwrf.log" 159 | fi 160 | 161 | # Actually run WRF! 162 | if $dryrun; then 163 | echo "$runcmd in $(pwd)" 164 | wrfexit=0 165 | else 166 | $runcmd 167 | wrfexit=$? 168 | fi 169 | 170 | if [ $wrfexit -ne 0 ]; then 171 | echo "WRF failed (exit code $wrfexit). Check the $errorfile file in WRFV3/run" 172 | echo "to determine the cause." 173 | fi 174 | exit $wrfexit 175 | --------------------------------------------------------------------------------