├── model ├── __init__.py ├── pyRichards │ ├── __init__.py │ └── richards.py ├── pyNoahMP │ ├── data │ │ ├── GENPARM.TBL │ │ ├── SOILPARM.TBL │ │ └── URBPARM.TBL │ ├── src │ │ ├── Utility_routines │ │ │ ├── Makefile │ │ │ ├── module_wrf_utilities.F │ │ │ ├── module_ra_gfdleta.F │ │ │ ├── kwm_string_utilities.F │ │ │ ├── module_model_constants.F │ │ │ └── module_date_utilities.F │ │ ├── Noah │ │ │ ├── Makefile │ │ │ ├── LICENSE.txt │ │ │ └── module_sf_noahmp_groundwater.F │ │ └── user_build_options │ └── make.py ├── old │ └── driver.py ├── model.py ├── unittests │ └── unittests.py ├── pyHWU │ └── management_funcs.py └── pyRouting │ └── routing.py ├── preprocessing └── __init__.py ├── setup.py ├── .gitignore ├── postprocessing ├── src │ ├── compile.py │ └── upscaling_tools.f90 ├── postprocessing.py └── upscaling_python.py ├── README.md ├── HB ├── baseline_template.json ├── yml └── HB_linux64.yml └── spec-file.txt /model/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /model/pyRichards/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /preprocessing/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /setup.py: -------------------------------------------------------------------------------- 1 | import os 2 | #model:Noah-MP 3 | cwd = os.getcwd() 4 | os.chdir('model/pyNoahMP') 5 | os.system('python make.py') 6 | os.chdir(cwd) 7 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.pyc 2 | *.pyf 3 | *.mod 4 | *.so 5 | *.o 6 | *.tar.gz 7 | *.dSYM 8 | *.ipynb_checkpoints 9 | model/pyNoahMP/src/Noah/*.f90 10 | model/pyNoahMP/src/Utility_routines/*.f90 11 | *.nbc 12 | *.nbi 13 | -------------------------------------------------------------------------------- /postprocessing/src/compile.py: -------------------------------------------------------------------------------- 1 | import os 2 | cmd = 'f2py upscaling_tools.f90 -h upscaling_tools.pyf -m upscaling_fortran --overwrite-signature' 3 | os.system(cmd) 4 | #Create driver library 5 | cmd = 'f2py upscaling_tools.f90 -lgomp -c upscaling_tools.pyf --fcompiler=gnu95 --f90flags="-w -fopenmp"' 6 | os.system(cmd) 7 | #Move to the previos directory 8 | os.system('mv upscaling_fortran.so ../.') 9 | -------------------------------------------------------------------------------- /postprocessing/src/upscaling_tools.f90: -------------------------------------------------------------------------------- 1 | subroutine time_average(series_in,series_out,nt_out,nt_in) 2 | 3 | integer,intent(in) :: nt_out,nt_in 4 | real*8,intent(inout) :: series_in(nt_in) 5 | real*8,intent(out) :: series_out(nt_out) 6 | integer :: i 7 | integer :: dt 8 | dt = nt_in/nt_out 9 | do i = 1,nt_out 10 | series_out(i) = sum(series_in((1+(i-1)*dt):(i*dt)))/dt 11 | enddo 12 | 13 | endsubroutine 14 | -------------------------------------------------------------------------------- /model/pyNoahMP/data/GENPARM.TBL: -------------------------------------------------------------------------------- 1 | General Parameters 2 | SLOPE_DATA 3 | 9 4 | 0.1 5 | 0.6 6 | 1.0 7 | 0.35 8 | 0.55 9 | 0.8 10 | 0.63 11 | 0.0 12 | 0.0 13 | SBETA_DATA 14 | -2.0 15 | FXEXP_DATA 16 | 2.0 17 | CSOIL_DATA 18 | 2.00E+6 19 | SALP_DATA 20 | 2.6 21 | REFDK_DATA 22 | 2.0E-6 23 | REFKDT_DATA 24 | 3.0 25 | FRZK_DATA 26 | 0.15 27 | ZBOT_DATA 28 | -8.0 29 | CZIL_DATA 30 | 0.1 31 | SMLOW_DATA 32 | 0.5 33 | SMHIGH_DATA 34 | 3.0 35 | LVCOEF_DATA 36 | 0.5 37 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | HydroBlocks 2 | ========== 3 | 4 | The following steps walk through how to install HydroBlocks 5 | 6 | **1. Clone the HydroBlocks repository.** 7 | 8 | ``` 9 | git clone https://github.com/chaneyn/HydroBlocks.git 10 | cd HydroBlocks 11 | ``` 12 | 13 | **2. Create a conda environment named HB from the spec-file. Note that the only current spec-file in the repository is for a linux64 machine.** 14 | 15 | ``` 16 | conda create --name HB --file spec-file.txt 17 | source activate HB 18 | pip install git+https://github.com/chaneyn/geospatialtools@dev_nate 19 | pip install psutil==5.9.4 20 | ``` 21 | 22 | **3. Install HydroBlocks.** 23 | 24 | ``` 25 | python setup.py 26 | ``` 27 | 28 | -------------------------------------------------------------------------------- /model/pyNoahMP/src/Utility_routines/Makefile: -------------------------------------------------------------------------------- 1 | # Makefile 2 | # 3 | .SUFFIXES: 4 | .SUFFIXES: .o .F 5 | 6 | include ../user_build_options 7 | 8 | OBJS = \ 9 | module_date_utilities.o \ 10 | module_model_constants.o \ 11 | module_ra_gfdleta.o \ 12 | module_wrf_utilities.o \ 13 | kwm_string_utilities.o 14 | 15 | CPPHRLDAS = -D_HRLDAS_OFFLINE_ 16 | 17 | 18 | all: $(OBJS) 19 | 20 | .F.o: 21 | @echo "" 22 | $(RM) $(*).f90 23 | $(CPP) $(CPPFLAGS) $(CPPHRLDAS) $(*).F > $(*).f90 24 | $(COMPILERF90) -o $(@) -c $(F90FLAGS) $(FREESOURCE) $(*).f90 25 | @echo "" 26 | 27 | # 28 | # Dependencies: 29 | # 30 | 31 | # 32 | # This command cleans up object (etc.) files: 33 | # 34 | 35 | clean: 36 | $(RM) *.o *.mod *.stb *~ *.f90 37 | 38 | -------------------------------------------------------------------------------- /model/pyNoahMP/src/Noah/Makefile: -------------------------------------------------------------------------------- 1 | # Makefile 2 | # 3 | .SUFFIXES: 4 | .SUFFIXES: .o .F 5 | 6 | include ../user_build_options 7 | 8 | OBJS = \ 9 | module_sf_noahmpdrv.o \ 10 | module_sf_noahmplsm.o \ 11 | module_sf_noahmp_glacier.o \ 12 | module_sf_noahmp_groundwater.o \ 13 | module_sf_gecros.o \ 14 | module_sf_urban.o \ 15 | module_sf_bep.o \ 16 | module_sf_bem.o \ 17 | module_sf_bep_bem.o 18 | 19 | CPPHRLDAS = -D_HRLDAS_OFFLINE_ 20 | 21 | all: $(OBJS) 22 | 23 | .F.o: 24 | @echo "" 25 | $(RM) $(*).f90 26 | $(CPP) $(CPPFLAGS) $(CPPHRLDAS) $(*).F > $(*).f90 27 | $(COMPILERF90) -o $(@) -c -I../Utility_routines $(F90FLAGS) $(FREESOURCE) $(*).f90 28 | @echo "" 29 | 30 | # 31 | # Dependencies: 32 | # 33 | module_sf_noahmplsm.o: module_sf_gecros.o 34 | module_sf_noahmpdrv.o: module_sf_noahmplsm.o module_sf_noahmp_glacier.o module_sf_noahmp_groundwater.o \ 35 | module_sf_urban.o module_sf_bep.o module_sf_bep_bem.o 36 | module_sf_noahmp_groundwater.o: module_sf_noahmplsm.o 37 | module_sf_bep.o: module_sf_urban.o 38 | module_sf_bep_bem.o: module_sf_urban.o module_sf_bem.o 39 | 40 | # 41 | # This command cleans up object (etc) files: 42 | # 43 | 44 | clean: 45 | $(RM) *.o *.mod *.stb *~ *.f90 46 | 47 | -------------------------------------------------------------------------------- /model/pyNoahMP/src/Utility_routines/module_wrf_utilities.F: -------------------------------------------------------------------------------- 1 | !WRF:DRIVER_LAYER:UTIL 2 | ! 3 | 4 | SUBROUTINE wrf_message( str ) 5 | IMPLICIT NONE 6 | 7 | CHARACTER*(*) str 8 | print *,trim(str) 9 | 10 | END SUBROUTINE wrf_message 11 | 12 | SUBROUTINE wrf_error_fatal( str ) 13 | IMPLICIT NONE 14 | CHARACTER*(*) str 15 | CALL wrf_message( '-------------- FATAL CALLED ---------------' ) 16 | CALL wrf_message( str ) 17 | CALL wrf_message( '-------------------------------------------' ) 18 | 19 | CALL wrf_abort 20 | END SUBROUTINE wrf_error_fatal 21 | 22 | SUBROUTINE wrf_abort 23 | STOP 'wrf_abort' 24 | END SUBROUTINE wrf_abort 25 | 26 | SUBROUTINE wrf_debug( level , str ) 27 | IMPLICIT NONE 28 | CHARACTER*(*) str 29 | INTEGER , INTENT (IN) :: level 30 | CALL wrf_message( str ) 31 | RETURN 32 | END SUBROUTINE wrf_debug 33 | 34 | subroutine wrf_dm_bcast_real(rval, ival) 35 | implicit none 36 | real, intent(in) :: rval 37 | integer, intent(in) :: ival 38 | end subroutine wrf_dm_bcast_real 39 | 40 | subroutine wrf_dm_bcast_integer(rval, ival) 41 | implicit none 42 | integer, intent(in) :: rval 43 | integer, intent(in) :: ival 44 | end subroutine wrf_dm_bcast_integer 45 | 46 | subroutine wrf_dm_bcast_string(rval, ival) 47 | implicit none 48 | character(len=*), intent(in) :: rval 49 | integer, intent(in) :: ival 50 | end subroutine wrf_dm_bcast_string 51 | -------------------------------------------------------------------------------- /model/pyNoahMP/src/user_build_options: -------------------------------------------------------------------------------- 1 | 2 | #============================================================================================= 3 | # Options for Linux with gfortran (not fully tested) 4 | #============================================================================================= 5 | 6 | COMPILERF90 = /usr/bin/gfortran 7 | FREESOURCE = -ffree-form -ffree-line-length-none 8 | # F90FLAGS = -g -fconvert=big-endian -fbounds-check -fno-range-check -fno-underscoring 9 | F90FLAGS = -g -fconvert=big-endian -fbounds-check -fno-range-check -fPIC 10 | MODFLAG = -I 11 | LDFLAGS = 12 | CPP = /usr/bin/cpp 13 | CPPFLAGS = -P -traditional -D_GFORTRAN_ 14 | LIBS = 15 | LIBJASPER = -ljpeg -L/home/nc153/soteria/software/miniconda3/envs/hrldas/lib -ljasper 16 | INCJASPER = -I/home/nc153/soteria/software/miniconda3/envs/hrldas/include 17 | NETCDFMOD = -I/stor/soteria/hydro/private/nc153/local/include 18 | NETCDFLIB = -L/stor/soteria/hydro/private/nc153/local/lib -lnetcdf -lnetcdff 19 | BZIP2 = NO 20 | BZIP2_LIB = -lbz2 -L/home/nc153/soteria/software/miniconda3/envs/hrldas/lib 21 | BZIP2_INCLUDE = -I/home/nc153/soteria/software/miniconda3/envs/hrldas/include 22 | RM = rm -f 23 | CC = /usr/bin/gcc 24 | 25 | -------------------------------------------------------------------------------- /HB: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | import sys 3 | import getopt 4 | from mpi4py import MPI 5 | comm = MPI.COMM_WORLD 6 | #size = comm.Get_size() 7 | #rank = comm.Get_rank() 8 | #name = MPI.Get_processor_name() 9 | 10 | def main(argv): 11 | edir = '' 12 | try: 13 | opts, args = getopt.getopt(argv,"hm:t:",["mfile=","mtype="]) 14 | except getopt.GetoptError: 15 | print('HB -m -t ') 16 | sys.exit(2) 17 | for opt, arg in opts: 18 | if opt == '-h': 19 | print('HB -m -t ') 20 | sys.exit() 21 | elif opt in ("-m", "--mfile"): 22 | mfile = arg 23 | elif opt in ("-t", "--mtype"): 24 | mtype = arg 25 | 26 | if mtype == 'preprocess': 27 | print("HydroBlocks: Preprocess",flush=True) 28 | import preprocessing.preprocessing 29 | preprocessing.preprocessing.driver(comm,mfile) 30 | #preprocessing.driver(comm,edir) 31 | elif mtype == 'model': 32 | print("HydroBlocks: Model",flush=True) 33 | import model.model 34 | model.model.run(comm,mfile) 35 | elif mtype == 'postprocess': 36 | print("HydroBlocks: Postprocess",flush=True) 37 | import postprocessing.postprocessing 38 | postprocessing.postprocessing.driver(comm,mfile) 39 | else: 40 | print("The mtype provided is not an option") 41 | 42 | 43 | 44 | if __name__ == "__main__": 45 | main(sys.argv[1:]) 46 | -------------------------------------------------------------------------------- /model/old/driver.py: -------------------------------------------------------------------------------- 1 | import datetime 2 | from dateutil.relativedelta import relativedelta 3 | import HydroBlocks# as HB 4 | import sys 5 | import pickle 6 | 7 | def Read_Metadata_File(file): 8 | 9 | import json 10 | metadata = json.load(open(file))['HydroBlocks'] 11 | 12 | return metadata 13 | 14 | #Read in the metadata file 15 | metadata_file = sys.argv[1] 16 | metadata = Read_Metadata_File(metadata_file) 17 | info = metadata 18 | info["dz"] = [0.05, 0.1, 0.1, 0.1, 0.2, 0.2, 0.25, 0.25, 0.25, 0.5,1.0,2.0] 19 | #info['enddate']['year'] = 2002 20 | #info['enddate']['month'] = 2 21 | #info['enddate']['day'] = 1 22 | #info['subsurface_module'] = 'none' 23 | #info['segment']['years_per_segment'] = 2017-2002+1 24 | 25 | #Define idate and fdate 26 | idate = datetime.datetime(metadata['startdate']['year'],metadata['startdate']['month'],metadata['startdate']['day'],0) 27 | fdate = datetime.datetime(metadata['enddate']['year'],metadata['enddate']['month'],metadata['enddate']['day'],0) + datetime.timedelta(days=1) 28 | 29 | #Run the segments for the model 30 | sidate = idate 31 | sfdate = idate 32 | while sidate < fdate: 33 | sfdate = sidate + relativedelta(years=metadata['segment']['years_per_segment']) 34 | if sfdate > fdate: sfdate = fdate 35 | #Set the parameters 36 | info['idate'] = sidate 37 | info['fdate'] = sfdate 38 | #Run the model 39 | #Initialize 40 | HB = HydroBlocks.initialize(info) 41 | #Run the model 42 | HB.run(info) 43 | #Finalize 44 | HB.finalize() 45 | #Update initial time step 46 | sidate = sfdate 47 | 48 | 49 | 50 | -------------------------------------------------------------------------------- /model/pyNoahMP/src/Utility_routines/module_ra_gfdleta.F: -------------------------------------------------------------------------------- 1 | ! 2 | ! An attempt to mimic the GFDL code without requiring all the unnecessary extras. 3 | 4 | 5 | MODULE MODULE_RA_GFDLETA 6 | 7 | contains 8 | 9 | !--------------------------------------------------------------------- 10 | SUBROUTINE CAL_MON_DAY(JULDAY,julyr,Jmonth,Jday) 11 | !--------------------------------------------------------------------- 12 | IMPLICIT NONE 13 | !----------------------------------------------------------------------- 14 | INTEGER, INTENT(IN) :: JULDAY,julyr 15 | INTEGER, INTENT(OUT) :: Jmonth,Jday 16 | LOGICAL :: LEAP,NOT_FIND_DATE 17 | INTEGER :: MONTH (12),itmpday,itmpmon,i 18 | !----------------------------------------------------------------------- 19 | DATA MONTH/31,28,31,30,31,30,31,31,30,31,30,31/ 20 | !*********************************************************************** 21 | NOT_FIND_DATE = .true. 22 | 23 | itmpday = JULDAY 24 | itmpmon = 1 25 | LEAP=.FALSE. 26 | IF(MOD(julyr,4).EQ.0)THEN 27 | MONTH(2)=29 28 | LEAP=.TRUE. 29 | ENDIF 30 | 31 | i = 1 32 | DO WHILE (NOT_FIND_DATE) 33 | IF(itmpday.GT.MONTH(i))THEN 34 | itmpday=itmpday-MONTH(i) 35 | ELSE 36 | Jday=itmpday 37 | Jmonth=i 38 | NOT_FIND_DATE = .false. 39 | ENDIF 40 | i = i+1 41 | END DO 42 | 43 | END SUBROUTINE CAL_MON_DAY 44 | !!================================================================================ 45 | 46 | END MODULE module_RA_GFDLETA 47 | -------------------------------------------------------------------------------- /model/pyNoahMP/make.py: -------------------------------------------------------------------------------- 1 | import os 2 | #Compile NOAH 3 | os.chdir('src/Utility_routines') 4 | os.system('make clean') 5 | os.system('make') 6 | os.chdir('../Noah') 7 | os.system('make clean') 8 | os.system('make') 9 | os.system('cat pyNoahMP.F > pyNoahMP.f90') 10 | #Create signature file 11 | #cmd = 'f2py pyNoahMP.f90 -h NoahMP.pyf -m NoahMP --overwrite-signature' 12 | #os.system(cmd) 13 | #Define subroutine to import 14 | subroutines = 'initialize' 15 | #Create driver library 16 | #cmd = 'f2py -c NoahMP.pyf' 17 | cmd = 'f2py -c pyNoahMP.f90 *.o ../Utility_routines/*.o -m NoahMP -I../Utility_routines -L/lib64 -L/usr/lib64 --f90flags="-w -fopenmp -g -Werror -fmodule-private -fimplicit-none -fbounds-check -fcheck=array-temps,bounds,do,mem,pointer" only: initialize update :' 18 | #cmd = 'f2py --debug -lgomp -c NoahMP.pyf *.o ../Utility_routines/*.o --fcompiler=gnu95 -L/lib64 -L/usr/lib64 --f90flags="-w -fopenmp -g -Werror -fmodule-private -fimplicit-none -fbounds-check -fcheck=array-temps,bounds,do,mem,pointer"' 19 | #cmd = 'f2py -c only: %s --debug -lgomp -c NoahMP.pyf *.o ../Utility_routines/*.o --fcompiler=gnu95 -L/lib64 -L/usr/lib64 --f90flags="-w -fopenmp -g -Werror -fmodule-private -fimplicit-none -fbounds-check -fcheck=array-temps,bounds,do,mem,pointer"' % subroutines 20 | #cmd = 'f2py -m NoahMP --debug -lgomp -c only: run_model run_model_cell : pyNoahMP.f90 *.o --fcompiler=gnu95 -I../Utility_routines -L/lib64 -L/usr/lib64 --f90flags="-w -fopenmp -g -Werror -fmodule-private -fimplicit-none -fbounds-check -fcheck=array-temps,bounds,do,mem,pointer -ffree-form -ffree-line-length-none"' 21 | #cmd = 'f2py -m NoahMP --debug -lgomp -c pyNoahMP.f90 *.o ../Utility_routines/*.o --fcompiler=gnu95 -I../Utility_routines -L/lib64 -L/usr/lib64 --f90flags="-w -fopenmp -g -Werror -fmodule-private -fimplicit-none -fbounds-check -fcheck=array-temps,bounds,do,mem,pointer -ffree-form -ffree-line-length-none"' 22 | #print(cmd) 23 | #cmd = 'f2py -m NoahMP --debug -lgfortran -lgomp -c NoahMP.pyf *.o ../Utility_routines/*.o only: initialize_parameters run_model : --fcompiler=gnu95 -L/lib64 -L/usr/lib64 --f90flags="-w -fopenmp -g -Werror -fmodule-private -fimplicit-none -fbounds-check -fcheck=array-temps,bounds,do,mem,pointer -ffree-form -ffree-line-length-none"' 24 | #cmd = 'f2py -m NoahMP --debug -lgomp -c NoahMP.pyf *.o ../Utility_routines/*.o only: initialize_parameters run_model : --fcompiler=gnu95 -L/lib64 -L/usr/lib64 --f90flags="-w -fopenmp -g -Werror -fmodule-private -fimplicit-none -fbounds-check -fcheck=array-temps,bounds,do,mem,pointer"' 25 | #exit() 26 | #cmd = 'f2py --debug -c NoahMP.pyf *.o ../Utility_routines/*.o --fcompiler=gnu95 -L/lib64 -L/usr/lib64 --f90flags="-w -g -Werror -fmodule-private -fimplicit-none -fbounds-check -fcheck=array-temps,bounds,do,mem,pointer"' 27 | 28 | os.system(cmd) 29 | #Clean up both directories 30 | #os.system('mv NoahMP.*.so ../../NoahMP.so') 31 | os.system('mv NoahMP*so ../../NoahMP.so') 32 | -------------------------------------------------------------------------------- /baseline_template.json: -------------------------------------------------------------------------------- 1 | { 2 | "rdir": "/stor/tyche/hydro/private/lpt14/projects/HBupdate2023/FD_test", 3 | "experiment": "CMHB_ON_svp_ON", 4 | "startdate": { 5 | "year": 2018, 6 | "month": 1, 7 | "day": 1 8 | }, 9 | "enddate": { 10 | "year": 2018, 11 | "month": 3, 12 | "day": 31 13 | }, 14 | "dt": 3600.0, 15 | "channel_initiation": { 16 | "athrs": 1000000 17 | }, 18 | "connection_matrix_hbands": true, 19 | "soil_vertical_properties": true, 20 | "hmc_parameters": { 21 | "number_of_characteristic_subbasins": 5, 22 | "average_height_difference_between_bands": 80, 23 | "number_of_intraband_clusters": 5, 24 | "subbasin_clustering_covariates": [ 25 | "dem", 26 | "lats", 27 | "lons" 28 | ], 29 | "intraband_clustering_covariates": [ 30 | "lc", 31 | "clay" 32 | ], 33 | "interridge_connectivity": false, 34 | "intervalley_connectivity": false, 35 | "intraband_connectivity": false 36 | }, 37 | "parameter_scaling": { 38 | "bankfull_depth": 1.0, 39 | "channel_manning": 1.0, 40 | "floodplain_manning": 1.0, 41 | "channel_width": 1.0 42 | }, 43 | "noahmp_options": { 44 | "idveg": 3, 45 | "iopt_crs": 2, 46 | "iopt_btr": 1, 47 | "iopt_run": 2, 48 | "iopt_sfc": 1, 49 | "iopt_frz": 2, 50 | "iopt_inf": 2, 51 | "iopt_rad": 1, 52 | "iopt_alb": 2, 53 | "iopt_snf": 3, 54 | "iopt_tbot": 2, 55 | "iopt_stc": 2, 56 | "iopt_gla": 1, 57 | "iopt_rsf": 1, 58 | "iopt_crop": 0, 59 | "sf_urban_physics": 0, 60 | "z_ml": 10.0, 61 | "nsnow": 3 62 | }, 63 | "noahmp_ics": { 64 | "swe": 0.0, 65 | "snowh": 0.0, 66 | "Tsoil": [ 67 | 266.1, 68 | 274.0, 69 | 276.9, 70 | 279.9, 71 | 279.9, 72 | 279.9, 73 | 279.9, 74 | 279.9, 75 | 279.9, 76 | 279.9 77 | ], 78 | "soilM": [ 79 | 0.298, 80 | 0.294, 81 | 0.271, 82 | 0.307, 83 | 0.307, 84 | 0.307, 85 | 0.307, 86 | 0.307, 87 | 0.307, 88 | 0.307 89 | ], 90 | "Tskin": 278.0, 91 | "Tbot": 285.0 92 | }, 93 | "segment": { 94 | "years_per_segment": 1 95 | }, 96 | "dz": [ 97 | 0.05, 98 | 0.1, 99 | 0.1, 100 | 0.1, 101 | 0.2, 102 | 0.2, 103 | 0.25, 104 | 0.25, 105 | 0.25, 106 | 0.5 107 | ], 108 | "routing_module": { 109 | "type": "kinematic", 110 | "surface_coupling": true, 111 | "dt_routing":100 112 | }, 113 | "subsurface_module": "richards", 114 | "restart": { 115 | "flag": false 116 | }, 117 | "output": { 118 | "vars": [ 119 | "smc", 120 | "trad", 121 | "sh", 122 | "lh", 123 | "runoff", 124 | "prcp", 125 | "cm", 126 | "emissi", 127 | "salb" 128 | ], 129 | "routing_vars": [ 130 | "Q", 131 | "A", 132 | "Qc", 133 | "Qf" 134 | ] 135 | }, 136 | "upscaling": { 137 | "res": 0.0166666666667, 138 | "vars": [ 139 | "trad", 140 | "smc1" 141 | ] 142 | } 143 | } 144 | -------------------------------------------------------------------------------- /model/pyNoahMP/src/Utility_routines/kwm_string_utilities.F: -------------------------------------------------------------------------------- 1 | module kwm_string_utilities 2 | 3 | contains 4 | 5 | subroutine strrep(string, rep1, rep2) 6 | ! In string 1, replace expression rep1 with expression rep2. 7 | ! if rep2 is shorter than rep1, fill the end of the string 8 | ! with blanks 9 | 10 | implicit none 11 | character(len=*) :: string, rep1, rep2 12 | integer :: idx, inlen, len1, len2 13 | 14 | do 15 | inlen = len(string) 16 | len1 = len(rep1) 17 | len2 = len(rep2) 18 | idx = index(string, rep1)-1 19 | 20 | if (idx == -1) then 21 | return 22 | else 23 | string = string(1:idx)// rep2 // & 24 | string((idx+len1+1):inlen) // & 25 | " " 26 | endif 27 | enddo 28 | 29 | end subroutine strrep 30 | 31 | 32 | character(len=1024) function unblank(string) result(return_string) 33 | ! Remove blanks (and tabs [char(9)] from string 34 | implicit none 35 | character(len=*), intent(in) :: string 36 | integer :: i, j, lenstr 37 | 38 | return_string = " " 39 | 40 | if ( verify(string," "//char(9)) == 0 ) then 41 | stop 'String is all blanks.' 42 | endif 43 | 44 | j = 0 45 | do i = 1, len(string) 46 | if ((string(i:i).ne.' ').and.(string(i:i).ne.char(9))) then 47 | j = j + 1 48 | return_string(j:j) = string(i:i) 49 | endif 50 | enddo 51 | 52 | end function unblank 53 | 54 | !KWM character function upcase(h) 55 | !KWM implicit none 56 | !KWM character :: h 57 | !KWM 58 | !KWM if ((ichar(h).ge.96) .and. (ichar(h).le.123)) then 59 | !KWM upcase = char(ichar(h)-32) 60 | !KWM else 61 | !KWM upcase = h 62 | !KWM endif 63 | !KWM 64 | !KWM end function upcase 65 | 66 | character(len=256) function upcase(h) result(return_string) 67 | implicit none 68 | character(len=*), intent(in) :: h 69 | integer :: i 70 | 71 | return_string = " " 72 | 73 | do i = 1, len_trim(h) 74 | 75 | if ((ichar(h(i:i)).ge.96) .and. (ichar(h(i:i)).le.123)) then 76 | return_string(i:i) = char(ichar(h(i:i))-32) 77 | else 78 | return_string(i:i) = h(i:i) 79 | endif 80 | enddo 81 | 82 | end function upcase 83 | 84 | !KWM character function downcase(h) 85 | !KWM implicit none 86 | !KWM character h 87 | !KWM 88 | !KWM if ((ichar(h).ge.65) .and. (ichar(h).le.90)) then 89 | !KWM downcase = char(ichar(h)+32) 90 | !KWM else 91 | !KWM downcase = h 92 | !KWM endif 93 | !KWM end function downcase 94 | 95 | character(len=256) function downcase(h) result(return_string) 96 | implicit none 97 | character(len=*), intent(in) :: h 98 | integer :: i 99 | 100 | return_string = " " 101 | 102 | do i = 1, len_trim(h) 103 | 104 | if ((ichar(h(i:i)).ge.65) .and. (ichar(h(i:i)).le.90)) then 105 | return_string(i:i) = char(ichar(h(i:i))+32) 106 | else 107 | return_string(i:i) = h(i:i) 108 | endif 109 | enddo 110 | 111 | end function downcase 112 | 113 | 114 | end module kwm_string_utilities 115 | -------------------------------------------------------------------------------- /model/model.py: -------------------------------------------------------------------------------- 1 | from mpi4py import MPI 2 | import json 3 | import fiona 4 | import glob 5 | import numpy as np 6 | import os 7 | import sys 8 | 9 | def Read_Metadata_File(file): 10 | 11 | metadata = json.load(open(file)) 12 | 13 | return metadata 14 | 15 | def Run_HydroBlocks(metadata,edir,cid,rdir): 16 | #print('Run HB',flush=True) 17 | import datetime 18 | from dateutil.relativedelta import relativedelta 19 | #import sys 20 | #print("%s/model" % hb) 21 | #sys.path.append("%s/model" % hb) 22 | import model.HydroBlocks as HydroBlocks 23 | 24 | #Read in the metadata file 25 | info = metadata 26 | info['ncores'] = 1 27 | info['cid'] = cid 28 | info['cdir'] = '%s/%s' % (edir,cid) 29 | info['Qobs_file'] = '%s/data/obs/obs.pck' % rdir 30 | info['routing_file'] = '%s/%s/octopy.pck' % (edir,cid) 31 | info['input_file'] = '%s/%s/input_file_routing.nc' % (edir,cid) 32 | if metadata["routing_module"]["type"] == 'kinematic': 33 | info['output'] = {"dir":"%s/output_data/%s" % (edir,cid), 34 | "vars":info['output']['vars'], 35 | "routing_vars":info['output']['routing_vars']} 36 | else: 37 | info['output'] = {"dir":"%s/output_data/%s" % (edir,cid), 38 | "vars":info['output']['vars']} 39 | info['restart'] = {"flag":info['restart']['flag'], 40 | "dir":"%s/restart_data/%d" % (edir,cid)} 41 | os.system('mkdir -p %s' % (info['restart']['dir'])) 42 | 43 | #Define idate and fdate 44 | idate = datetime.datetime(metadata['startdate']['year'],metadata['startdate']['month'],metadata['startdate']['day'],0) 45 | fdate = datetime.datetime(metadata['enddate']['year'],metadata['enddate']['month'],metadata['enddate']['day'],0) + datetime.timedelta(days=1) 46 | 47 | #Run the segments for the model 48 | #restart_frequency = metadata['segment']['restart_frequency'] 49 | sidate = idate 50 | sfdate = idate 51 | while sidate < fdate: 52 | sfdate = sidate + relativedelta(years=metadata['segment']['years_per_segment']) 53 | #if restart_frequency == 'daily': 54 | #sfdate = sidate + relativedelta(days=1) 55 | #if restart_frequency == 'monthly': 56 | #sfdate = sidate + relativedelta(months=1) 57 | #if restart_frequency == 'yearly': 58 | #sfdate = sidate + relativedelta(years=1) 59 | if sfdate > fdate: sfdate = fdate 60 | #Set the parameters 61 | info['idate'] = sidate 62 | info['fdate'] = sfdate 63 | info['MPI'] = MPI 64 | #Run the model 65 | print(' Initialize model',flush=True) 66 | HB = HydroBlocks.initialize(info) 67 | print(' Run the model',flush=True) 68 | HB.run(info) 69 | print(' Finalize the model',flush=True) 70 | HB.finalize() 71 | #Update initial time step 72 | sidate = sfdate 73 | 74 | #Delete HB and library 75 | del HB 76 | del HydroBlocks 77 | 78 | return 79 | 80 | def run(comm,metadata_file): 81 | 82 | #Get some general info 83 | metadata = Read_Metadata_File(metadata_file) 84 | rdir = metadata['rdir'] 85 | edir = '%s/experiments/simulations/%s' % (rdir,metadata['experiment']) 86 | size = int(comm.Get_size()) 87 | rank = int(comm.Get_rank()) 88 | 89 | #Get cid configuration 90 | dfile = '%s/data/shp/domain.shp' % rdir 91 | fp = fiona.open(dfile,'r') 92 | cids = np.array(range(1,len(list(fp))+1)) 93 | fp.close() 94 | 95 | #Iterate per catchment 96 | for cid in cids[rank::size]: 97 | #print('cid:',cid,flush=True) 98 | #Create output directory 99 | os.system('mkdir -p %s/output_data/%d' % (edir,cid)) 100 | #os.chdir('%s' % (edir,)) 101 | #mdfile = '%s/%s/metadata.json' % (edir,cid) 102 | Run_HydroBlocks(metadata,edir,cid,rdir) 103 | -------------------------------------------------------------------------------- /model/unittests/unittests.py: -------------------------------------------------------------------------------- 1 | import warnings 2 | warnings.filterwarnings("ignore") 3 | import unittest 4 | import sys 5 | sys.path.append('../') 6 | from pyRichards import richards 7 | #import pyDTopmodel.dynamic_topmodel_tools as dtt 8 | import numpy as np 9 | import scipy 10 | import scipy.sparse 11 | import time 12 | 13 | class Richards(unittest.TestCase): 14 | 15 | def test_soil_moisture_potential(self): 16 | 17 | eps = 0.01 18 | nhru = 100 19 | nsoil = 5 20 | model = richards.richards(nhru,nsoil) 21 | #define parameters 22 | model.theta[:] = np.random.uniform(low=0.05,high=0.5,size=nhru)[:,np.newaxis] 23 | model.thetar[:] = 0.1 24 | model.thetas[:] = 0.5 25 | model.b[:] = np.random.uniform(low=3,high=8,size=nhru) 26 | model.satpsi[:] = 0.1 27 | #choose layer 28 | psi = [] 29 | bpsi = [] 30 | for il in xrange(nsoil): 31 | psi.append(model.calculate_soil_moisture_potential(il)) 32 | tmp = model.theta[:,il] 33 | m = (tmp <= (1+eps)*model.thetar) 34 | tmp[m] = (1+eps)*model.thetar[m] 35 | bpsi.append(model.satpsi*((model.theta[:,il]-model.thetar)/(model.thetas-model.thetar))**(-model.b)) 36 | psi = np.array(psi) 37 | bpsi = np.array(bpsi) 38 | self.assertTrue(np.allclose(psi,bpsi)) 39 | 40 | def test_hydraulic_conductivity(self): 41 | 42 | nhru = 100 43 | nsoil = 5 44 | model = richards.richards(nhru,1) 45 | model.ksat[:] = 10**-3*np.random.uniform(low=10**-6,high=1,size=nhru) #mm/s 46 | model.satpsi[:] = np.random.uniform(low=0.01,high=1.0,size=nhru) 47 | model.b[:] = np.random.uniform(low=3,high=8,size=nhru) 48 | psi = np.random.uniform(low=0.001,high=10**10,size=nhru) 49 | #model 50 | kx = model.calculate_hydraulic_conductivity(psi) 51 | #baseline 52 | bkx = 10*model.ksat*(psi/model.satpsi)**(-2-3/model.b) 53 | self.assertTrue(np.allclose(kx,bkx,rtol=1e-10, atol=1e-50)) 54 | 55 | def test_dense_sparse_comparison(self): 56 | 57 | nhru = 5000 58 | nsoil = 1 59 | model = richards.richards(nhru,nsoil) 60 | #define parameters 61 | model.nhru = nhru 62 | model.theta[:] = np.random.uniform(low=0.1,high=0.5,size=(nhru,nsoil)) 63 | model.thetar[:] = 0.1 64 | model.thetas[:] = 0.5 65 | model.b[:] = np.random.uniform(low=0.1,high=0.5,size=nhru) 66 | model.satpsi[:] = np.random.uniform(low=0.001,high=1.0,size=nhru) 67 | model.ksat[:] = 10**-3*np.random.uniform(low=10**-6,high=1,size=nhru) #mm/s 68 | model.dz[:] = np.random.uniform(low=0.01,high=100.0,size=nsoil) 69 | model.dx = 30.0 70 | model.area[:] = np.random.uniform(low=900,high=90000,size=nhru) 71 | model.dem[:] = np.random.uniform(low=0.0,high=1000.0,size=nhru) 72 | tmp = np.random.randint(low=-1000,high=2,size=(nhru,nhru)) 73 | tmp[tmp <= 0] = 0 74 | tmp[range(nhru),range(nhru)] = 1 75 | tsymm = 30*(tmp + tmp.T)/2 76 | model.width = scipy.sparse.csr_matrix(tsymm) 77 | model.I = model.width.copy() 78 | model.I[model.I != 0] = 1 79 | print '%d connections out of %d possible connections' % (np.sum(model.width != 0),nhru*nhru) 80 | #update (dense) 81 | tic = time.time() 82 | model.update(type='dense') 83 | print 'dense',time.time()-tic 84 | hdiv = np.copy(model.hdiv) 85 | #update (sparse) 86 | tic = time.time() 87 | model.update(type='sparse') 88 | print 'sparse',time.time()-tic 89 | bhdiv = np.copy(model.hdiv) 90 | #compare 91 | self.assertTrue(np.allclose(hdiv,bhdiv,rtol=1e-10, atol=1e-50)) 92 | 93 | suite = unittest.TestLoader().loadTestsFromTestCase(Richards) 94 | unittest.TextTestRunner(verbosity=2).run(suite) 95 | #suite = unittest.TestLoader().loadTestsFromTestCase(DynamicTopmodel) 96 | #unittest.TextTestRunner(verbosity=2).run(suite) 97 | -------------------------------------------------------------------------------- /model/pyHWU/management_funcs.py: -------------------------------------------------------------------------------- 1 | import numpy as np 2 | from skimage.segmentation import find_boundaries, clear_border 3 | import random 4 | 5 | def hrus_centroid_distance(lats,lons): 6 | radius = 6371 # km 7 | 8 | ncells = len(lats) 9 | distance = np.zeros((ncells,ncells)) 10 | for i, lat1, lon1 in zip(range(ncells),lats,lons): 11 | for j, lat2, lon2 in zip(range(ncells),lats,lons): 12 | if j>i: 13 | dlat = np.radians(lat2-lat1) 14 | dlon = np.radians(lon2-lon1) 15 | a = np.sin(dlat/2) * np.sin(dlat/2) + np.cos(np.radians(lat1)) \ 16 | * np.cos(np.radians(lat2)) * np.sin(dlon/2) * np.sin(dlon/2) 17 | c = 2 * np.math.atan2(np.sqrt(a), np.sqrt(1-a)) 18 | distance[i,j] = radius * c 19 | distance[j,i] = radius * c 20 | 21 | return distance 22 | 23 | def hrus_slope(elevation,dist): 24 | ncells = len(elevation) 25 | 26 | slope = np.zeros((ncells,ncells)) 27 | distance = np.array(dist)*1000.0 # convert to m 28 | 29 | for i, h1 in zip(range(ncells),elevation): 30 | for j, h2 in zip(range(ncells),elevation): 31 | if j>i : 32 | if distance[i,j] > 0: 33 | slope[i,j] = (h1-h2)/distance[i,j] 34 | slope[j,i] = -slope[i,j] 35 | else: 36 | slope[i,j] = 0.0 37 | slope[j,i] = 0.0 38 | 39 | return slope 40 | 41 | def calc_calendar(self,ncells): 42 | grow = np.zeros((ncells,12),dtype=int) 43 | st_gscal = np.copy(self.st_gscal)-1 44 | en_gscal = np.copy(self.en_gscal)-1 45 | 46 | leng = en_gscal-st_gscal+1 47 | 48 | m = leng > 0 49 | for i in np.where(m)[0]: 50 | grow[i,st_gscal[i]:en_gscal[i]+1] = 1 51 | 52 | m = leng < 0 53 | for i in np.where(m)[0]: 54 | grow[i,:] = 1 55 | grow[i,en_gscal[i]+1:st_gscal[i]] = 0 56 | 57 | 58 | return grow 59 | 60 | def calculate_min_distance(hsu,nhru,cluster_ids,lats,lons,clats,clons): 61 | radius = 6367.0 62 | # Minimum distance between HRUs 63 | 64 | # Get lat lon from the borders 65 | idx = (cluster_ids == hsu) 66 | idx = clear_border(idx,bgval=False) 67 | idx = find_boundaries(idx, mode='inner') 68 | bd_lats = lats[idx].flatten() 69 | bd_lons = lons[idx].flatten() 70 | 71 | if len(bd_lats) < 1 : 72 | idx = (cluster_ids == hsu) 73 | idx = find_boundaries(idx, mode='inner') 74 | bd_lats = lats[idx].flatten() 75 | bd_lons = lons[idx].flatten() 76 | 77 | # Get unique lat,lon values and sample 50 points 78 | points = set(zip(bd_lats,bd_lons)) 79 | nsamp = 1#30 80 | if len(points) <= nsamp: nsamp = int(len(points)/2.) 81 | if len(points) <= 5: nsamp = len(points) 82 | 83 | points = random.sample(points, nsamp) 84 | bd_lats = np.array(list(zip(*points))[0]) 85 | bd_lons = np.array(list(zip(*points))[1]) 86 | 87 | distance = np.ones(nhru)*10000000. 88 | 89 | #Calculate the distance of a boundary to a centroid of each hru 90 | 91 | for lat, lon in zip(bd_lats,bd_lons): 92 | dlat = np.radians(lat-clats) 93 | dlon = np.radians(lon-clons) 94 | a = np.sin(dlat/2) * np.sin(dlat/2) + np.cos(np.radians(clats)) \ 95 | * np.cos(np.radians(lat)) * np.sin(dlon/2) * np.sin(dlon/2) 96 | c = np.zeros((len(a))) 97 | for count in range(len(a)): 98 | c[count] = 2 * np.math.atan2(np.sqrt(a[count]), np.sqrt(1-a[count])) 99 | dist = radius * c 100 | distance[dist < distance] = dist[dist < distance] 101 | # for hrs in range(nhru): 102 | # if hrs == hsu: 103 | # distance[hrs] = 0.0 104 | # else: 105 | # clat = clats[hrs] 106 | # clon = clons[hrs] 107 | # 108 | # for lat, lon in zip(bd_lats,bd_lons): 109 | # dlat = np.radians(lat-clat) 110 | # dlon = np.radians(lon-clon) 111 | # a = np.sin(dlat/2) * np.sin(dlat/2) + np.cos(np.radians(clat)) \ 112 | # * np.cos(np.radians(lat)) * np.sin(dlon/2) * np.sin(dlon/2) 113 | # c = 2 * np.math.atan2(np.sqrt(a), np.sqrt(1-a)) 114 | # dist = radius * c 115 | # if dist < distance[hrs]: distance[hrs] = dist 116 | # #print hsu, hrs, dist, distance[hrs] 117 | 118 | #print hsu, distance 119 | return distance 120 | # 121 | 122 | 123 | -------------------------------------------------------------------------------- /model/pyNoahMP/data/SOILPARM.TBL: -------------------------------------------------------------------------------- 1 | Soil Parameters 2 | STAS 3 | 19,1 'BB DRYSMC F11 MAXSMC REFSMC SATPSI SATDK SATDW WLTSMC QTZ ' 4 | 1, 2.79, 0.010, -0.472, 0.339, 0.192, 0.069, 4.66E-5, 2.65E-5, 0.010, 0.92, 'SAND' 5 | 2, 4.26, 0.028, -1.044, 0.421, 0.283, 0.036, 1.41E-5, 5.14E-6, 0.028, 0.82, 'LOAMY SAND' 6 | 3, 4.74, 0.047, -0.569, 0.434, 0.312, 0.141, 5.23E-6, 8.05E-6, 0.047, 0.60, 'SANDY LOAM' 7 | 4, 5.33, 0.084, 0.162, 0.476, 0.360, 0.759, 2.81E-6, 2.39E-5, 0.084, 0.25, 'SILT LOAM' 8 | 5, 3.86, 0.061, 0.162, 0.484, 0.347, 0.955, 2.18E-6, 1.66E-5, 0.061, 0.10, 'SILT' 9 | 6, 5.25, 0.066, -0.327, 0.439, 0.329, 0.355, 3.38E-6, 1.43E-5, 0.066, 0.40, 'LOAM' 10 | 7, 6.77, 0.069, -1.491, 0.404, 0.315, 0.135, 4.45E-6, 1.01E-5, 0.069, 0.60, 'SANDY CLAY LOAM' 11 | 8, 8.72, 0.120, -1.118, 0.464, 0.387, 0.617, 2.03E-6, 2.35E-5, 0.120, 0.10, 'SILTY CLAY LOAM' 12 | 9, 8.17, 0.103, -1.297, 0.465, 0.382, 0.263, 2.45E-6, 1.13E-5, 0.103, 0.35, 'CLAY LOAM' 13 | 10, 10.73, 0.100, -3.209, 0.406, 0.338, 0.098, 7.22E-6, 1.87E-5, 0.100, 0.52, 'SANDY CLAY' 14 | 11, 10.39, 0.126, -1.916, 0.468, 0.404, 0.324, 1.34E-6, 9.64E-6, 0.126, 0.10, 'SILTY CLAY' 15 | 12, 11.55, 0.138, -2.138, 0.468, 0.412, 0.468, 9.74E-7, 1.12E-5, 0.138, 0.25, 'CLAY' 16 | 13, 5.25, 0.066, -0.327, 0.439, 0.329, 0.355, 3.38E-6, 1.43E-5, 0.066, 0.05, 'ORGANIC MATERIAL' 17 | 14, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.60, 'WATER' 18 | 15, 2.79, 0.006, -1.111, 0.20, 0.17, 0.069, 1.41E-4, 1.36E-4, 0.006, 0.07, 'BEDROCK' 19 | 16, 4.26, 0.028, -1.044, 0.421, 0.283, 0.036, 1.41E-5, 5.14E-6, 0.028, 0.25, 'OTHER(land-ice)' 20 | 17, 11.55, 0.030, -10.472, 0.468, 0.454, 0.468, 9.74E-7, 1.12E-5, 0.030, 0.60, 'PLAYA' 21 | 18, 2.79, 0.006, -0.472, 0.200, 0.17, 0.069, 1.41E-4, 1.36E-4, 0.006, 0.52, 'LAVA' 22 | 19, 2.79, 0.01, -0.472, 0.339, 0.192, 0.069, 4.66E-5, 2.65E-5, 0.01, 0.92, 'WHITE SAND' 23 | Soil Parameters 24 | STAS-RUC 25 | 19,1 'BB DRYSMC HC MAXSMC REFSMC SATPSI SATDK SATDW WLTSMC QTZ ' 26 | 1, 4.05, 0.002, 1.47, 0.395, 0.174, 0.121, 1.76E-4, 0.608E-6, 0.033, 0.92, 'SAND' 27 | 2, 4.38, 0.035, 1.41, 0.410, 0.179, 0.090, 1.56E-4, 0.514E-5, 0.055, 0.82, 'LOAMY SAND' 28 | 3, 4.90, 0.041, 1.34, 0.435, 0.249, 0.218, 3.47E-5, 0.805E-5, 0.095, 0.60, 'SANDY LOAM' 29 | 4, 5.30, 0.034, 1.27, 0.485, 0.369, 0.786, 7.20E-6, 0.239E-4, 0.143, 0.25, 'SILT LOAM' 30 | 5, 5.30, 0.034, 1.27, 0.485, 0.369, 0.786, 7.20E-6, 0.239E-4, 0.143, 0.10, 'SILT' 31 | 6, 5.39, 0.050, 1.21, 0.451, 0.314, 0.478, 6.95E-6, 0.143E-4, 0.137, 0.40, 'LOAM' 32 | 7, 7.12, 0.068, 1.18, 0.420, 0.299, 0.299, 6.30E-6, 0.990E-5, 0.148, 0.60, 'SANDY CLAY LOAM' 33 | 8, 7.75, 0.060, 1.32, 0.477, 0.357, 0.356, 1.70E-6, 0.237E-4, 0.208, 0.10, 'SILTY CLAY LOAM' 34 | 9, 8.52, 0.085, 1.23, 0.476, 0.391, 0.630, 2.45E-6, 0.113E-4, 0.230, 0.35, 'CLAY LOAM' 35 | 10, 10.40, 0.100, 1.18, 0.426, 0.316, 0.153, 2.17E-6, 0.187E-4, 0.210, 0.52, 'SANDY CLAY' 36 | 11, 10.40, 0.070, 1.15, 0.492, 0.409, 0.490, 1.03E-6, 0.964E-5, 0.250, 0.10, 'SILTY CLAY' 37 | 12, 11.40, 0.068, 1.09, 0.482, 0.400, 0.405, 1.28E-6, 0.112E-4, 0.268, 0.25, 'CLAY' 38 | 13, 5.39, 0.027, 1.21, 0.451, 0.314, 0.478, 6.95E-6, 0.143E-4, 0.117, 0.05, 'ORGANIC MATERIAL' 39 | 14, 0.0, 0.0, 4.18, 1.0, 1.0, 0.0, 0.0, 0.0, 0.0, 0.00, 'WATER' 40 | 15, 4.05, 0.004, 2.03, 0.200, 0.10 , 0.121, 1.41E-4, 0.136E-3, 0.006, 0.60, 'BEDROCK' 41 | 16, 4.90, 0.065, 2.10, 0.435, 0.249, 0.218, 3.47E-5, 0.514E-5, 0.114, 0.05, 'OTHER(land-ice)' 42 | 17, 11.40, 0.030, 1.41, 0.468, 0.454, 0.468, 9.74E-7, 0.112E-4, 0.030, 0.60, 'PLAYA' 43 | 18, 4.05, 0.006, 1.41, 0.200, 0.17, 0.069, 1.41E-4, 0.136E-3, 0.006, 0.52, 'LAVA' 44 | 19, 4.05, 0.01, 1.47, 0.339, 0.236, 0.069, 1.76E-4, 0.608E-6, 0.060, 0.92, 'WHITE SAND' 45 | 46 | -------------------------------------------------------------------------------- /model/pyNoahMP/src/Noah/LICENSE.txt: -------------------------------------------------------------------------------- 1 | USE OF THIS SOFTWARE IS SUBJECT TO THE FOLLOWING TERMS AND CONDITIONS: 2 | 3 | 1. License. Subject to these terms and conditions, University Corporation for Atmospheric Research (UCAR) 4 | grants you a non-exclusive, royalty-free license to use, create derivative works, publish, distribute, 5 | disseminate, transfer, modify, revise and copy the Noah-MP software, in both object and source code 6 | (the "Software"). You shall not sell, license or transfer for a fee the Software, or any work that in any 7 | manner contains the Software. 8 | 9 | 2. Disclaimer of Warranty on Software. Use of the Software is at your sole risk. The Software is provided 10 | "AS IS" and without warranty of any kind and UCAR EXPRESSLY DISCLAIMS ALL WARRANTIES AND/OR CONDITIONS OF 11 | ANY KIND, EXPRESS OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, ANY WARRANTIES OR CONDITIONS OF TITLE, 12 | NON-INFRINGEMENT OF A THIRD PARTY'S INTELLECTUAL PROPERTY, MERCHANTABILITY OR SATISFACTORY QUALITY AND 13 | FITNESS FOR A PARTICULAR PURPOSE. THE PARTIES EXPRESSLY DISCLAIM THAT THE UNIFORM COMPUTER INFORMATION 14 | TRANSACTIONS ACT (UCITA) APPLIES TO OR GOVERNS THIS AGREEMENT. No oral or written information or advice 15 | given by UCAR or a UCAR authorized representative shall create a warranty or in any way increase the scope 16 | of this warranty. Should the Software prove defective, you (and neither UCAR nor any UCAR representative) 17 | assume the cost of all necessary correction. 18 | 19 | 3. Limitation of Liability. UNDER NO CIRCUMSTANCES, INCLUDING NEGLIGENCE, SHALL UCAR BE LIABLE FOR ANY 20 | DIRECT, INCIDENTAL, SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES INCLUDING LOST REVENUE, PROFIT OR DATA, 21 | WHETHER IN AN ACTION IN CONTRACT OR TORT ARISING OUT OF OR RELATING TO THE USE OF OR INABILITY TO USE THE 22 | SOFTWARE, EVEN IF UCAR HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. 23 | 24 | 4. Compliance with Law. All Software and any technical data delivered under this Agreement are subject to 25 | U.S. export control laws and may be subject to export or import regulations in other countries. You agree 26 | to comply strictly with all applicable laws and regulations in connection with use and distribution of the 27 | Software, including export control laws, and you acknowledge that you have responsibility to obtain any 28 | required license to export, re-export, or import as may be required. 29 | 30 | 5. No Endorsement/No Support. The names UCAR/NCAR, National Center for Atmospheric Research and the 31 | University Corporation for Atmospheric Research may not be used in any advertising or publicity to endorse 32 | or promote any products or commercial entity unless specific written permission is obtained from UCAR. The 33 | Software is provided without any support or maintenance, and without any obligation to provide you with 34 | modifications, improvements, enhancements, or updates of the Software. 35 | 36 | 6. Controlling Law and Severability. This Agreement shall be governed by the laws of the United States and the 37 | State of Colorado. If for any reason a court of competent jurisdiction finds any provision, or portion 38 | thereof, to be unenforceable, the remainder of this Agreement shall continue in full force and effect. This 39 | Agreement shall not be governed by the United Nations Convention on Contracts for the International Sale of 40 | Goods, the application of which is hereby expressly excluded. 41 | 42 | 7. Termination. Your rights under this Agreement will terminate automatically without notice from UCAR if you 43 | fail to comply with any term(s) of this Agreement. You may terminate this Agreement at any time by destroying 44 | the Software and any related documentation and any complete or partial copies thereof. Upon termination, all 45 | rights granted under this Agreement shall terminate. The following provisions shall survive termination: 46 | Sections 2, 3, 6 and 9. 47 | 48 | 8. Complete Agreement. This Agreement constitutes the entire agreement between the parties with respect to the 49 | use of the Software and supersedes all prior or contemporaneous understandings regarding such subject matter. 50 | No amendment to or modification of this Agreement will be binding unless in a writing and signed by UCAR. 51 | 52 | 9. Notices and Additional Terms. Copyright in Software is held by UCAR. You must include, with each copy of the 53 | Software and associated documentation, a copy of this Agreement and the following notice: 54 | 55 | "The source of this material is the Research Applications Laboratory at the National Center for Atmospheric 56 | Research, a program of the University Corporation for Atmospheric Research (UCAR) pursuant to a Cooperative 57 | Agreement with the National Science Foundation; ©2007 University Corporation for Atmospheric Research. All 58 | Rights Reserved." 59 | 60 | The following notice shall be displayed on any scholarly works associated with, related to or derived from 61 | the Software: 62 | 63 | "The Noah-MP modeling system was developed at the National Center for Atmospheric Research (NCAR) with collaborations 64 | from university partners. NCAR is sponsored by the United States National Science Foundation." 65 | 66 | BY USING OR DOWNLOADING THIS SOFTWARE, YOU AGREE TO BE BOUND BY THE TERMS AND CONDITIONS OF THIS AGREEMENT. 67 | -------------------------------------------------------------------------------- /yml/HB_linux64.yml: -------------------------------------------------------------------------------- 1 | name: HBnew 2 | channels: 3 | - conda-forge 4 | dependencies: 5 | - _libgcc_mutex=0.1=conda_forge 6 | - _openmp_mutex=4.5=1_gnu 7 | - affine=2.3.0=py_0 8 | - attrs=21.4.0=pyhd8ed1ab_0 9 | - binutils_impl_linux-64=2.40=hf600244_0 10 | - blosc=1.21.0=h9c3ff4c_0 11 | - boost-cpp=1.74.0=h6cacc03_7 12 | - bzip2=1.0.8=h7f98852_4 13 | - c-ares=1.18.1=h7f98852_0 14 | - ca-certificates=2022.12.7=ha878542_0 15 | - cached-property=1.5.2=hd8ed1ab_1 16 | - cached_property=1.5.2=pyha770c72_1 17 | - cairo=1.16.0=ha00ac49_1009 18 | - certifi=2022.12.7=pyhd8ed1ab_0 19 | - cfitsio=4.0.0=h9a35b8e_0 20 | - click-plugins=1.1.1=py_0 21 | - cligj=0.7.2=pyhd8ed1ab_1 22 | - curl=7.81.0=h494985f_0 23 | - expat=2.4.6=h27087fc_0 24 | - font-ttf-dejavu-sans-mono=2.37=hab24e00_0 25 | - font-ttf-inconsolata=3.000=h77eed37_0 26 | - font-ttf-source-code-pro=2.038=h77eed37_0 27 | - font-ttf-ubuntu=0.83=hab24e00_0 28 | - fontconfig=2.13.96=ha180cfb_0 29 | - fonts-conda-ecosystem=1=0 30 | - fonts-conda-forge=1=0 31 | - freetype=2.10.4=h0708190_1 32 | - freexl=1.0.6=h7f98852_0 33 | - gcc=12.2.0=h26027b1_11 34 | - gcc_impl_linux-64=12.2.0=hcc96c02_19 35 | - geos=3.10.2=h9c3ff4c_0 36 | - geotiff=1.7.0=h6593c0a_6 37 | - gettext=0.19.8.1=h73d1719_1008 38 | - gfortran=12.2.0=h8acd90e_11 39 | - gfortran_impl_linux-64=12.2.0=h55be85b_19 40 | - giflib=5.2.1=h36c2ea0_2 41 | - hdf4=4.2.15=h10796ff_3 42 | - hdf5=1.12.1=nompi_h7f166f4_103 43 | - icu=69.1=h9c3ff4c_0 44 | - importlib_metadata=4.11.4=hd8ed1ab_0 45 | - jbig=2.1=h7f98852_2003 46 | - joblib=1.1.0=pyhd8ed1ab_0 47 | - jpeg=9e=h7f98852_0 48 | - json-c=0.15=h98cffda_0 49 | - kealib=1.4.14=h87e4c3c_3 50 | - kernel-headers_linux-64=2.6.32=he073ed8_15 51 | - keyutils=1.6.1=h166bdaf_0 52 | - krb5=1.19.2=h08a2579_4 53 | - lcms2=2.12=hddcbb42_0 54 | - ld_impl_linux-64=2.40=h41732ed_0 55 | - lerc=3.0=h9c3ff4c_0 56 | - libblas=3.9.0=13_linux64_openblas 57 | - libcblas=3.9.0=13_linux64_openblas 58 | - libcurl=7.81.0=h494985f_0 59 | - libdap4=3.20.6=hd7c4107_2 60 | - libdeflate=1.10=h7f98852_0 61 | - libedit=3.1.20191231=he28a2e2_2 62 | - libev=4.33=h516909a_1 63 | - libffi=3.4.2=h7f98852_5 64 | - libgcc-devel_linux-64=12.2.0=h3b97bd3_19 65 | - libgcc-ng=12.2.0=h65d4601_19 66 | - libgdal=3.4.1=hff5c5e8_5 67 | - libgfortran-ng=12.2.0=h69a702a_19 68 | - libgfortran5=12.2.0=h337968e_19 69 | - libglib=2.70.2=h174f98d_4 70 | - libgomp=12.2.0=h65d4601_19 71 | - libiconv=1.16=h516909a_0 72 | - libkml=1.3.0=h238a007_1014 73 | - liblapack=3.9.0=13_linux64_openblas 74 | - libllvm11=11.1.0=hf817b99_3 75 | - libnetcdf=4.8.1=nompi_hb3fd0d9_101 76 | - libnghttp2=1.47.0=he49606f_0 77 | - libnsl=2.0.0=h7f98852_0 78 | - libopenblas=0.3.18=pthreads_h8fe5266_0 79 | - libpng=1.6.37=h21135ba_2 80 | - libpq=14.2=h676c864_0 81 | - librttopo=1.1.0=hf69c175_9 82 | - libsanitizer=12.2.0=h46fd767_19 83 | - libspatialite=5.0.1=h0e567f8_14 84 | - libssh2=1.10.0=ha35d2d1_2 85 | - libstdcxx-ng=12.2.0=h46fd767_19 86 | - libtiff=4.3.0=h542a066_3 87 | - libuuid=2.32.1=h7f98852_1000 88 | - libwebp-base=1.2.2=h7f98852_1 89 | - libxcb=1.13=h7f98852_1004 90 | - libxml2=2.9.12=h885dcf4_1 91 | - libzip=1.8.0=h1c5bbd1_1 92 | - libzlib=1.2.11=h36c2ea0_1013 93 | - lz4-c=1.9.3=h9c3ff4c_1 94 | - munch=2.5.0=py_0 95 | - ncurses=6.3=h9c3ff4c_0 96 | - nspr=4.32=h9c3ff4c_1 97 | - nss=3.74=hb5efdd6_0 98 | - openjpeg=2.4.0=hb52868f_1 99 | - openssl=3.1.0=h0b41bf4_0 100 | - packaging=21.3=pyhd8ed1ab_0 101 | - pcre=8.45=h9c3ff4c_0 102 | - pip=22.0.3=pyhd8ed1ab_0 103 | - pixman=0.40.0=h36c2ea0_0 104 | - poppler=22.01.0=ha39eefc_0 105 | - poppler-data=0.4.11=hd8ed1ab_0 106 | - postgresql=14.2=hce44dc1_0 107 | - proj=8.2.1=h277dcde_0 108 | - pthread-stubs=0.4=h36c2ea0_1001 109 | - pyparsing=3.0.7=pyhd8ed1ab_0 110 | - python=3.10.2=hc74c709_3_cpython 111 | - python-dateutil=2.8.2=pyhd8ed1ab_0 112 | - python_abi=3.10=2_cp310 113 | - readline=8.1=h46c0cb4_0 114 | - six=1.16.0=pyh6c4a22f_0 115 | - snuggs=1.4.7=py_0 116 | - sqlite=3.37.0=h9cd32fc_0 117 | - sysroot_linux-64=2.12=he073ed8_15 118 | - threadpoolctl=3.1.0=pyh8a188c0_0 119 | - tiledb=2.7.0=h3f4058f_0 120 | - tk=8.6.12=h27826a3_0 121 | - typing_extensions=4.3.0=pyha770c72_0 122 | - tzcode=2021e=h7f98852_0 123 | - tzdata=2021e=he74cb21_0 124 | - wheel=0.37.1=pyhd8ed1ab_0 125 | - xarray=2022.3.0=pyhd8ed1ab_0 126 | - xerces-c=3.2.3=h8ce2273_4 127 | - xorg-kbproto=1.0.7=h7f98852_1002 128 | - xorg-libice=1.0.10=h7f98852_0 129 | - xorg-libsm=1.2.3=hd9c2040_1000 130 | - xorg-libx11=1.7.2=h7f98852_0 131 | - xorg-libxau=1.0.9=h7f98852_0 132 | - xorg-libxdmcp=1.1.3=h7f98852_0 133 | - xorg-libxext=1.3.4=h7f98852_1 134 | - xorg-libxrender=0.9.10=h7f98852_1003 135 | - xorg-renderproto=0.11.1=h7f98852_1002 136 | - xorg-xextproto=7.3.0=h7f98852_1002 137 | - xorg-xproto=7.0.31=h7f98852_1007 138 | - xz=5.2.5=h516909a_1 139 | - zipp=3.8.0=pyhd8ed1ab_0 140 | - zlib=1.2.11=h36c2ea0_1013 141 | - zstd=1.5.2=ha95c52a_0 142 | - pip: 143 | - cftime==1.6.0 144 | - click==8.0.4 145 | - fiona==1.8.21 146 | - gdal==3.4.1 147 | - geopandas==0.10.2 148 | #- geospatialtools==0.0.0 149 | - h5py==3.6.0 150 | - importlib-metadata==4.11.4 151 | - llvmlite==0.38.0 152 | - mpi4py==3.1.3 153 | - netcdf4==1.5.8 154 | - numba==0.55.1 155 | - numpy==1.21.5 156 | - pandas==1.4.1 157 | - psutil==5.9.4 158 | - pyproj==3.3.0 159 | - pytz==2021.3 160 | - rasterio==1.2.10 161 | - scikit-learn==1.0.2 162 | - scipy==1.8.0 163 | - setuptools==59.8.0 164 | - shapely==1.8.0 165 | #- "git+https://github.com/chaneyn/geospatialtools.git@dev_nate_2023" 166 | -------------------------------------------------------------------------------- /postprocessing/postprocessing.py: -------------------------------------------------------------------------------- 1 | import warnings 2 | warnings.filterwarnings('ignore') 3 | import postprocessing.upscaling_python as upscaling_python 4 | import geospatialtools.gdal_tools as gdal_tools 5 | import numpy as np 6 | import datetime 7 | import sys 8 | 9 | def Read_Metadata_File(file): 10 | 11 | import json 12 | metadata = json.load(open(file)) 13 | 14 | return metadata 15 | 16 | def driver(comm,metadata_file): 17 | 18 | size = comm.Get_size() 19 | rank = comm.Get_rank() 20 | #Read in the metadata 21 | #metadata_file = '%s/metadata.json' % edir 22 | metadata = Read_Metadata_File(metadata_file) 23 | metadata['idate'] = datetime.datetime(metadata['startdate']['year'], 24 | metadata['startdate']['month'], 25 | metadata['startdate']['day'],0) 26 | metadata['fdate'] = datetime.datetime(metadata['enddate']['year'], 27 | metadata['enddate']['month'], 28 | metadata['enddate']['day'],0) + datetime.timedelta(days=1) - datetime.timedelta(seconds=metadata['dt']) 29 | startdate = metadata['idate'] 30 | enddate = metadata['fdate'] 31 | rdir = metadata['rdir'] 32 | edir = '%s/experiments/simulations/%s' % (rdir,metadata['experiment']) 33 | 34 | #Create the upscale template 35 | if rank == 0: 36 | upscaling_python.Create_Upscale_Template(metadata) #laura, uncommented 37 | comm.Barrier() 38 | 39 | #Determine the sites box 40 | print(rank,"Determing the bounding box",flush=True) 41 | bbox_metadata = Determine_Bounding_Box(metadata,rank,size) 42 | 43 | #Create upscaling mapping 44 | print(rank,"Creating the upscaling mapping",flush=True) 45 | upscaling_python.Create_Upscale_Mapping(metadata,rank,bbox_metadata) #laura, uncommented 46 | 47 | #Map the data 48 | vars = metadata['upscaling']['vars'] 49 | #metadata['nt_in'] = 365*24 50 | #metadata['nt_out'] = 365*24 51 | for year in range(metadata['idate'].year,metadata['fdate'].year+1): 52 | if year == metadata['idate'].year: 53 | startdate = metadata['idate'] 54 | enddate = datetime.datetime(year,12,31,23) 55 | elif year == metadata['fdate'].year: 56 | startdate = datetime.datetime(year,1,1,0) 57 | enddate = metadata['fdate'] 58 | else: 59 | startdate = datetime.datetime(year,1,1,0) 60 | enddate = datetime.datetime(year,12,31,23) 61 | 62 | upscaling_python.Map_Model_Output(metadata,vars,rank,bbox_metadata,startdate,enddate) 63 | #print('upscale_python_map',flush=True) 64 | #Pause until all files have been processed 65 | comm.Barrier() 66 | 67 | #Create files 68 | print(rank,"Creating the output files (%d)" % year,flush=True) 69 | upscaling_python.Create_Output_Files(metadata,rank,size,vars,startdate,enddate) 70 | #Pause until all files have been processed 71 | comm.Barrier() 72 | 73 | return 74 | 75 | def Determine_Bounding_Box(metadata,rank,size): 76 | print(rank,size,flush=True) 77 | rdir = metadata['rdir'] 78 | file_cid = '%s/experiments/simulations/%s/postprocess/cids.vrt' % (rdir,metadata['experiment']) 79 | file_mapping = '%s/experiments/simulations/%s/postprocess/mapping.tif' % (rdir,metadata['experiment']) 80 | #Retrieve metadata for entire region 81 | metadata_upscale = gdal_tools.retrieve_metadata(file_mapping) 82 | res_upscale = metadata_upscale['resx'] 83 | lats_upscale = np.linspace(metadata_upscale['miny']+res_upscale/2,metadata_upscale['maxy']-res_upscale/2,metadata_upscale['ny']) 84 | lons_upscale = np.linspace(metadata_upscale['minx']+res_upscale/2,metadata_upscale['maxx']-res_upscale/2,metadata_upscale['nx']) 85 | lats_upscale_flipped = np.flipud(lats_upscale) 86 | metadata_finescale = gdal_tools.retrieve_metadata(file_cid) 87 | res_finescale = metadata_finescale['resx'] 88 | lats_finescale = np.linspace(metadata_finescale['miny']+res_finescale/2, 89 | metadata_finescale['maxy']-res_finescale/2,metadata_finescale['ny']) 90 | lons_finescale = np.linspace(metadata_finescale['minx']+res_finescale/2, 91 | metadata_finescale['maxx']-res_finescale/2,metadata_finescale['nx']) 92 | 93 | #Determine its bounding region 94 | #comm = MPI.COMM_WORLD 95 | #size = comm.size 96 | #rank = comm.rank 97 | #ibox = int(rank) / int(size**0.5) 98 | #jbox = int(rank % size**0.5) 99 | split_size = int(np.ceil(lons_upscale.size/size)) 100 | ilons_upscale_min = rank*split_size 101 | ilons_upscale_max = (rank+1)*split_size 102 | if rank == size-2: 103 | ilons_upscale_max = ilons_upscale_max - 1 104 | if rank == size-1: 105 | ilons_upscale_min = ilons_upscale_min - 1 106 | if ilons_upscale_max > lons_upscale.size:ilons_upscale_max = lons_upscale.size 107 | 108 | #Determine the lats/lons and ilats/ilons for the bounding box (coarsescale) 109 | lats_upscale_box = lats_upscale#[ibox*nlat_upscale:(ibox+1)*nlat_upscale]#+1] 110 | #lons_upscale_box = lons_upscale[jbox*nlon_upscale:(jbox+1)*nlon_upscale]#+1] 111 | lons_upscale_box = lons_upscale[ilons_upscale_min:ilons_upscale_max+1] 112 | ilats_upscale_box = np.where(np.in1d(lats_upscale,lats_upscale_box))[0] 113 | ilons_upscale_box = np.where(np.in1d(lons_upscale,lons_upscale_box))[0] 114 | ilats_upscale_flipped_box = np.where(np.in1d(lats_upscale_flipped,lats_upscale_box))[0] 115 | 116 | print(rank,np.where((lons_finescale+res_finescale/2 <= lons_upscale_box[-1]+res_upscale/2) & (lons_finescale-res_finescale/2 >= lons_upscale_box[0]-res_upscale/2))[0],flush=True) 117 | 118 | #Determine the lats/lons and ilats/ilons for the bounding box (finescale) 119 | ilats_finescale_box = np.where((lats_finescale+res_finescale/2 <= lats_upscale_box[-1]+res_upscale/2) & 120 | (lats_finescale-res_finescale/2 >= lats_upscale_box[0]-res_upscale/2))[0] 121 | ilons_finescale_box = np.where((lons_finescale+res_finescale/2 <= lons_upscale_box[-1]+res_upscale/2) & 122 | (lons_finescale-res_finescale/2 >= lons_upscale_box[0]-res_upscale/2))[0] 123 | #Skips one? 124 | lats_finescale_box = lats_finescale[ilats_finescale_box] 125 | lons_finescale_box = lons_finescale[ilons_finescale_box] 126 | bbox_metadata = {'lats_upscale':lats_upscale_box,'ilats_upscale':ilats_upscale_box, 127 | 'lons_upscale':lons_upscale_box,'ilons_upscale':ilons_upscale_box, 128 | 'res_upscale':res_upscale,'ilats_upscale_flipped':ilats_upscale_flipped_box, 129 | 'lats_finescale':lats_finescale_box,'ilats_finescale':ilats_finescale_box, 130 | 'lons_finescale':lons_finescale_box,'ilons_finescale':ilons_finescale_box, 131 | 'res_finescale':res_finescale} 132 | 133 | return bbox_metadata 134 | -------------------------------------------------------------------------------- /model/pyNoahMP/src/Utility_routines/module_model_constants.F: -------------------------------------------------------------------------------- 1 | !WRF:MODEL_LAYER:CONSTANTS 2 | ! 3 | 4 | MODULE module_model_constants 5 | 6 | ! 2. Following are constants for use in defining real number bounds. 7 | 8 | ! A really small number. 9 | 10 | REAL , PARAMETER :: epsilon = 1.E-15 11 | 12 | ! 4. Following is information related to the physical constants. 13 | 14 | ! These are the physical constants used within the model. 15 | 16 | ! JM NOTE -- can we name this grav instead? 17 | REAL , PARAMETER :: g = 9.81 ! acceleration due to gravity (m {s}^-2) 18 | 19 | #if ( NMM_CORE == 1 ) 20 | REAL , PARAMETER :: r_d = 287.04 21 | REAL , PARAMETER :: cp = 1004.6 22 | #else 23 | REAL , PARAMETER :: r_d = 287. 24 | REAL , PARAMETER :: cp = 7.*r_d/2. 25 | #endif 26 | 27 | REAL , PARAMETER :: r_v = 461.6 28 | REAL , PARAMETER :: cv = cp-r_d 29 | REAL , PARAMETER :: cpv = 4.*r_v 30 | REAL , PARAMETER :: cvv = cpv-r_v 31 | REAL , PARAMETER :: cvpm = -cv/cp 32 | REAL , PARAMETER :: cliq = 4190. 33 | REAL , PARAMETER :: cice = 2106. 34 | REAL , PARAMETER :: psat = 610.78 35 | REAL , PARAMETER :: rcv = r_d/cv 36 | REAL , PARAMETER :: rcp = r_d/cp 37 | REAL , PARAMETER :: rovg = r_d/g 38 | REAL , PARAMETER :: c2 = cp * rcv 39 | real , parameter :: mwdry = 28.966 ! molecular weight of dry air (g/mole) 40 | 41 | REAL , PARAMETER :: p1000mb = 100000. 42 | REAL , PARAMETER :: t0 = 300. 43 | REAL , PARAMETER :: p0 = p1000mb 44 | REAL , PARAMETER :: cpovcv = cp/(cp-r_d) 45 | REAL , PARAMETER :: cvovcp = 1./cpovcv 46 | REAL , PARAMETER :: rvovrd = r_v/r_d 47 | 48 | REAL , PARAMETER :: reradius = 1./6370.0e03 49 | 50 | REAL , PARAMETER :: asselin = .025 51 | ! REAL , PARAMETER :: asselin = .0 52 | REAL , PARAMETER :: cb = 25. 53 | 54 | REAL , PARAMETER :: XLV0 = 3.15E6 55 | REAL , PARAMETER :: XLV1 = 2370. 56 | REAL , PARAMETER :: XLS0 = 2.905E6 57 | REAL , PARAMETER :: XLS1 = 259.532 58 | 59 | REAL , PARAMETER :: XLS = 2.85E6 60 | REAL , PARAMETER :: XLV = 2.5E6 61 | REAL , PARAMETER :: XLF = 3.50E5 62 | 63 | REAL , PARAMETER :: rhowater = 1000. 64 | REAL , PARAMETER :: rhosnow = 100. 65 | REAL , PARAMETER :: rhoair0 = 1.28 66 | ! 67 | REAL , PARAMETER :: n_ccn0 = 1.0E8 68 | ! 69 | REAL , PARAMETER :: piconst = 3.1415926535897932384626433 70 | REAL , PARAMETER :: DEGRAD = piconst/180. 71 | REAL , PARAMETER :: DPD = 360./365. 72 | 73 | REAL , PARAMETER :: SVP1=0.6112 74 | REAL , PARAMETER :: SVP2=17.67 75 | REAL , PARAMETER :: SVP3=29.65 76 | REAL , PARAMETER :: SVPT0=273.15 77 | REAL , PARAMETER :: EP_1=R_v/R_d-1. 78 | REAL , PARAMETER :: EP_2=R_d/R_v 79 | REAL , PARAMETER :: KARMAN=0.4 80 | REAL , PARAMETER :: EOMEG=7.2921E-5 81 | REAL , PARAMETER :: STBOLT=5.67051E-8 82 | 83 | REAL , PARAMETER :: prandtl = 1./3.0 84 | ! constants for w-damping option 85 | REAL , PARAMETER :: w_alpha = 0.3 ! strength m/s/s 86 | REAL , PARAMETER :: w_beta = 1.0 ! activation cfl number 87 | 88 | REAL , PARAMETER :: pq0=379.90516 89 | REAL , PARAMETER :: epsq2=0.2 90 | REAL , PARAMETER :: a2=17.2693882 91 | REAL , PARAMETER :: a3=273.16 92 | REAL , PARAMETER :: a4=35.86 93 | REAL , PARAMETER :: epsq=1.e-12 94 | REAL , PARAMETER :: p608=rvovrd-1. 95 | !#if ( NMM_CORE == 1 ) 96 | REAL , PARAMETER :: climit=1.e-20 97 | REAL , PARAMETER :: cm1=2937.4 98 | REAL , PARAMETER :: cm2=4.9283 99 | REAL , PARAMETER :: cm3=23.5518 100 | ! REAL , PARAMETER :: defc=8.0 101 | ! REAL , PARAMETER :: defm=32.0 102 | REAL , PARAMETER :: defc=0.0 103 | REAL , PARAMETER :: defm=99999.0 104 | REAL , PARAMETER :: epsfc=1./1.05 105 | REAL , PARAMETER :: epswet=0.0 106 | REAL , PARAMETER :: fcdif=1./3. 107 | #ifdef HWRF 108 | REAL , PARAMETER :: fcm=0.0 109 | #else 110 | REAL , PARAMETER :: fcm=0.00003 111 | #endif 112 | REAL , PARAMETER :: gma=-r_d*(1.-rcp)*0.5 113 | REAL , PARAMETER :: p400=40000.0 114 | REAL , PARAMETER :: phitp=15000.0 115 | REAL , PARAMETER :: pi2=2.*3.1415926, pi1=3.1415926 116 | REAL , PARAMETER :: plbtm=105000.0 117 | REAL , PARAMETER :: plomd=64200.0 118 | REAL , PARAMETER :: pmdhi=35000.0 119 | REAL , PARAMETER :: q2ini=0.50 120 | REAL , PARAMETER :: rfcp=0.25/cp 121 | REAL , PARAMETER :: rhcrit_land=0.75 122 | REAL , PARAMETER :: rhcrit_sea=0.80 123 | REAL , PARAMETER :: rlag=14.8125 124 | REAL , PARAMETER :: rlx=0.90 125 | REAL , PARAMETER :: scq2=50.0 126 | REAL , PARAMETER :: slopht=0.001 127 | REAL , PARAMETER :: tlc=2.*0.703972477 128 | REAL , PARAMETER :: wa=0.15 129 | REAL , PARAMETER :: wght=0.35 130 | REAL , PARAMETER :: wpc=0.075 131 | REAL , PARAMETER :: z0land=0.10 132 | #ifdef HWRF 133 | REAL , PARAMETER :: z0max=0.01 134 | #else 135 | REAL , PARAMETER :: z0max=0.008 136 | #endif 137 | REAL , PARAMETER :: z0sea=0.001 138 | !#endif 139 | 140 | 141 | ! Earth 142 | 143 | ! The value for P2SI *must* be set to 1.0 for Earth 144 | ! Although, now we may not need this declaration here (see above) 145 | !REAL , PARAMETER :: P2SI = 1.0 146 | 147 | ! Orbital constants: 148 | 149 | INTEGER , PARAMETER :: PLANET_YEAR = 365 150 | REAL , PARAMETER :: OBLIQUITY = 23.5 151 | REAL , PARAMETER :: ECCENTRICITY = 0.014 152 | REAL , PARAMETER :: SEMIMAJORAXIS = 1.0 ! In AU 153 | ! Don't know the following values, so we'll fake them for now 154 | REAL , PARAMETER :: zero_date = 0.0 ! Time of perihelion passage 155 | ! Fraction into the year (from perhelion) of the 156 | ! occurrence of the Northern Spring Equinox 157 | REAL , PARAMETER :: EQUINOX_FRACTION= 0.0 158 | 159 | ! 2012103 160 | #if (EM_CORE == 1) 161 | ! for calls to set_tiles 162 | INTEGER, PARAMETER :: ZONE_SOLVE_EM = 1 163 | INTEGER, PARAMETER :: ZONE_SFS = 2 164 | #endif 165 | 166 | CONTAINS 167 | SUBROUTINE init_module_model_constants 168 | END SUBROUTINE init_module_model_constants 169 | END MODULE module_model_constants 170 | -------------------------------------------------------------------------------- /spec-file.txt: -------------------------------------------------------------------------------- 1 | # This file may be used to create an environment using: 2 | # $ conda create --name --file 3 | # platform: linux-64 4 | @EXPLICIT 5 | https://conda.anaconda.org/conda-forge/linux-64/_libgcc_mutex-0.1-conda_forge.tar.bz2 6 | https://conda.anaconda.org/anaconda/linux-64/ca-certificates-2020.10.14-0.tar.bz2 7 | https://conda.anaconda.org/conda-forge/linux-64/ld_impl_linux-64-2.35-h769bd43_9.tar.bz2 8 | https://conda.anaconda.org/conda-forge/linux-64/libgfortran4-7.5.0-hae1eefd_17.tar.bz2 9 | https://conda.anaconda.org/conda-forge/linux-64/libstdcxx-ng-9.3.0-h2ae2ef3_17.tar.bz2 10 | https://repo.anaconda.com/pkgs/main/linux-64/mpi-1.0-mpich.conda 11 | https://conda.anaconda.org/conda-forge/noarch/poppler-data-0.4.9-1.tar.bz2 12 | https://conda.anaconda.org/conda-forge/linux-64/libgfortran-ng-7.5.0-hae1eefd_17.tar.bz2 13 | https://conda.anaconda.org/conda-forge/linux-64/libgomp-9.3.0-h5dbcf3e_17.tar.bz2 14 | https://conda.anaconda.org/conda-forge/linux-64/_openmp_mutex-4.5-1_gnu.tar.bz2 15 | https://conda.anaconda.org/conda-forge/linux-64/libgcc-ng-9.3.0-h5dbcf3e_17.tar.bz2 16 | https://conda.anaconda.org/conda-forge/linux-64/bzip2-1.0.8-h516909a_3.tar.bz2 17 | https://conda.anaconda.org/conda-forge/linux-64/c-ares-1.16.1-h516909a_3.tar.bz2 18 | https://conda.anaconda.org/conda-forge/linux-64/expat-2.2.9-he1b5a44_2.tar.bz2 19 | https://conda.anaconda.org/conda-forge/linux-64/freexl-1.0.5-h516909a_1002.tar.bz2 20 | https://conda.anaconda.org/conda-forge/linux-64/geos-3.8.1-he1b5a44_0.tar.bz2 21 | https://conda.anaconda.org/conda-forge/linux-64/giflib-5.2.1-h516909a_2.tar.bz2 22 | https://conda.anaconda.org/conda-forge/linux-64/icu-67.1-he1b5a44_0.tar.bz2 23 | https://conda.anaconda.org/conda-forge/linux-64/jpeg-9d-h516909a_0.tar.bz2 24 | https://conda.anaconda.org/conda-forge/linux-64/json-c-0.13.1-hbfbb72e_1002.tar.bz2 25 | https://conda.anaconda.org/conda-forge/linux-64/libev-4.33-h516909a_1.tar.bz2 26 | https://conda.anaconda.org/conda-forge/linux-64/libffi-3.2.1-he1b5a44_1007.tar.bz2 27 | https://conda.anaconda.org/conda-forge/linux-64/libiconv-1.16-h516909a_0.tar.bz2 28 | https://conda.anaconda.org/conda-forge/linux-64/libopenblas-0.3.10-pthreads_hb3c22a3_4.tar.bz2 29 | https://conda.anaconda.org/anaconda/linux-64/libsodium-1.0.18-h7b6447c_0.tar.bz2 30 | https://conda.anaconda.org/conda-forge/linux-64/libspatialindex-1.9.3-he1b5a44_3.tar.bz2 31 | https://conda.anaconda.org/conda-forge/linux-64/libuuid-2.32.1-h14c3975_1000.tar.bz2 32 | https://conda.anaconda.org/conda-forge/linux-64/libwebp-base-1.1.0-h516909a_3.tar.bz2 33 | https://conda.anaconda.org/conda-forge/linux-64/lz4-c-1.9.2-he1b5a44_3.tar.bz2 34 | https://repo.anaconda.com/pkgs/main/linux-64/mpich-3.3.2-hc856adb_0.conda 35 | https://conda.anaconda.org/conda-forge/linux-64/ncurses-6.2-he1b5a44_1.tar.bz2 36 | https://conda.anaconda.org/anaconda/linux-64/openssl-1.1.1h-h7b6447c_0.tar.bz2 37 | https://conda.anaconda.org/conda-forge/linux-64/pcre-8.44-he1b5a44_0.tar.bz2 38 | https://conda.anaconda.org/conda-forge/linux-64/pixman-0.38.0-h516909a_1003.tar.bz2 39 | https://conda.anaconda.org/conda-forge/linux-64/pthread-stubs-0.4-h14c3975_1001.tar.bz2 40 | https://conda.anaconda.org/conda-forge/linux-64/tbb-2020.2-hc9558a2_0.tar.bz2 41 | https://conda.anaconda.org/conda-forge/linux-64/tzcode-2020a-h516909a_0.tar.bz2 42 | https://conda.anaconda.org/conda-forge/linux-64/xorg-kbproto-1.0.7-h14c3975_1002.tar.bz2 43 | https://conda.anaconda.org/conda-forge/linux-64/xorg-libice-1.0.10-h516909a_0.tar.bz2 44 | https://conda.anaconda.org/conda-forge/linux-64/xorg-libxau-1.0.9-h14c3975_0.tar.bz2 45 | https://conda.anaconda.org/conda-forge/linux-64/xorg-libxdmcp-1.1.3-h516909a_0.tar.bz2 46 | https://conda.anaconda.org/conda-forge/linux-64/xorg-renderproto-0.11.1-h14c3975_1002.tar.bz2 47 | https://conda.anaconda.org/conda-forge/linux-64/xorg-xextproto-7.3.0-h14c3975_1002.tar.bz2 48 | https://conda.anaconda.org/conda-forge/linux-64/xorg-xproto-7.0.31-h14c3975_1007.tar.bz2 49 | https://conda.anaconda.org/conda-forge/linux-64/xz-5.2.5-h516909a_1.tar.bz2 50 | https://conda.anaconda.org/conda-forge/linux-64/zlib-1.2.11-h516909a_1009.tar.bz2 51 | https://conda.anaconda.org/conda-forge/linux-64/gettext-0.19.8.1-hc5be6a0_1002.tar.bz2 52 | https://conda.anaconda.org/conda-forge/linux-64/hdf4-4.2.13-hf30be14_1003.tar.bz2 53 | https://conda.anaconda.org/conda-forge/linux-64/hdf5-1.10.6-nompi_h3c11f04_101.tar.bz2 54 | https://conda.anaconda.org/conda-forge/linux-64/libblas-3.8.0-17_openblas.tar.bz2 55 | https://conda.anaconda.org/conda-forge/linux-64/libedit-3.1.20191231-he28a2e2_2.tar.bz2 56 | https://conda.anaconda.org/conda-forge/linux-64/libllvm10-10.0.1-he513fc3_3.tar.bz2 57 | https://conda.anaconda.org/conda-forge/linux-64/libnghttp2-1.41.0-h8cfc5f6_2.tar.bz2 58 | https://conda.anaconda.org/conda-forge/linux-64/libpng-1.6.37-hed695b0_2.tar.bz2 59 | https://conda.anaconda.org/conda-forge/linux-64/libssh2-1.9.0-hab1572f_5.tar.bz2 60 | https://conda.anaconda.org/conda-forge/linux-64/libtiff-4.1.0-hc7e4089_6.tar.bz2 61 | https://conda.anaconda.org/conda-forge/linux-64/libxcb-1.13-h14c3975_1002.tar.bz2 62 | https://conda.anaconda.org/conda-forge/linux-64/libxml2-2.9.10-h68273f3_2.tar.bz2 63 | https://conda.anaconda.org/conda-forge/linux-64/readline-8.0-he28a2e2_2.tar.bz2 64 | https://conda.anaconda.org/conda-forge/linux-64/tk-8.6.10-hed695b0_0.tar.bz2 65 | https://conda.anaconda.org/conda-forge/linux-64/xerces-c-3.2.3-hfe33f54_1.tar.bz2 66 | https://conda.anaconda.org/conda-forge/linux-64/xorg-libsm-1.2.3-h84519dc_1000.tar.bz2 67 | https://conda.anaconda.org/anaconda/linux-64/zeromq-4.3.3-he6710b0_3.tar.bz2 68 | https://conda.anaconda.org/conda-forge/linux-64/zstd-1.4.5-h6597ccf_2.tar.bz2 69 | https://conda.anaconda.org/conda-forge/linux-64/boost-cpp-1.74.0-h9359b55_0.tar.bz2 70 | https://conda.anaconda.org/conda-forge/linux-64/freetype-2.10.2-he06d7ca_0.tar.bz2 71 | https://conda.anaconda.org/conda-forge/linux-64/kealib-1.4.13-h33137a7_1.tar.bz2 72 | https://conda.anaconda.org/conda-forge/linux-64/krb5-1.17.1-hfafb76e_3.tar.bz2 73 | https://conda.anaconda.org/conda-forge/linux-64/libcblas-3.8.0-17_openblas.tar.bz2 74 | https://conda.anaconda.org/conda-forge/linux-64/liblapack-3.8.0-17_openblas.tar.bz2 75 | https://conda.anaconda.org/conda-forge/linux-64/openjpeg-2.3.1-h981e76c_3.tar.bz2 76 | https://conda.anaconda.org/conda-forge/linux-64/sqlite-3.33.0-h4cf870e_0.tar.bz2 77 | https://conda.anaconda.org/conda-forge/linux-64/xorg-libx11-1.6.12-h516909a_0.tar.bz2 78 | https://conda.anaconda.org/conda-forge/linux-64/fontconfig-2.13.1-h1056068_1002.tar.bz2 79 | https://conda.anaconda.org/conda-forge/linux-64/libcurl-7.71.1-hcdd3856_8.tar.bz2 80 | https://conda.anaconda.org/conda-forge/linux-64/libkml-1.3.0-h74f7ee3_1012.tar.bz2 81 | https://conda.anaconda.org/conda-forge/linux-64/libpq-12.3-h5513abc_0.tar.bz2 82 | https://conda.anaconda.org/conda-forge/linux-64/python-3.8.5-h1103e12_9_cpython.tar.bz2 83 | https://conda.anaconda.org/conda-forge/linux-64/xorg-libxext-1.3.4-h516909a_0.tar.bz2 84 | https://conda.anaconda.org/conda-forge/linux-64/xorg-libxrender-0.9.10-h516909a_1002.tar.bz2 85 | https://conda.anaconda.org/conda-forge/noarch/affine-2.3.0-py_0.tar.bz2 86 | https://conda.anaconda.org/conda-forge/noarch/attrs-20.2.0-pyh9f0ad1d_0.tar.bz2 87 | https://conda.anaconda.org/anaconda/noarch/backcall-0.2.0-py_0.tar.bz2 88 | https://conda.anaconda.org/anaconda/linux-64/certifi-2020.6.20-py38_0.tar.bz2 89 | https://conda.anaconda.org/conda-forge/linux-64/cfitsio-3.470-hce51eda_6.tar.bz2 90 | https://conda.anaconda.org/conda-forge/noarch/click-7.1.2-pyh9f0ad1d_0.tar.bz2 91 | https://conda.anaconda.org/conda-forge/linux-64/curl-7.71.1-he644dc0_8.tar.bz2 92 | https://conda.anaconda.org/anaconda/noarch/decorator-4.4.2-py_0.tar.bz2 93 | https://conda.anaconda.org/conda-forge/linux-64/glib-2.66.1-h680cd38_0.tar.bz2 94 | https://conda.anaconda.org/anaconda/linux-64/ipython_genutils-0.2.0-py38_0.tar.bz2 95 | https://repo.anaconda.com/pkgs/main/linux-64/mpi4py-3.0.3-py38h028fd6f_0.conda 96 | https://conda.anaconda.org/anaconda/noarch/parso-0.8.0-py_0.tar.bz2 97 | https://conda.anaconda.org/anaconda/linux-64/pickleshare-0.7.5-py38_1000.tar.bz2 98 | https://conda.anaconda.org/conda-forge/linux-64/postgresql-12.3-h8573dbc_0.tar.bz2 99 | https://conda.anaconda.org/conda-forge/linux-64/proj-7.1.0-h966b41f_1.tar.bz2 100 | https://conda.anaconda.org/anaconda/linux-64/ptyprocess-0.6.0-py38_0.tar.bz2 101 | https://conda.anaconda.org/conda-forge/noarch/pyparsing-2.4.7-pyh9f0ad1d_0.tar.bz2 102 | https://conda.anaconda.org/conda-forge/linux-64/python_abi-3.8-1_cp38.tar.bz2 103 | https://conda.anaconda.org/conda-forge/noarch/pytz-2020.1-pyh9f0ad1d_0.tar.bz2 104 | https://conda.anaconda.org/anaconda/linux-64/pyzmq-19.0.2-py38he6710b0_1.tar.bz2 105 | https://conda.anaconda.org/conda-forge/noarch/six-1.15.0-pyh9f0ad1d_0.tar.bz2 106 | https://conda.anaconda.org/conda-forge/noarch/threadpoolctl-2.1.0-pyh5ca1d4c_0.tar.bz2 107 | https://conda.anaconda.org/anaconda/linux-64/tornado-6.0.4-py38h7b6447c_1.tar.bz2 108 | https://conda.anaconda.org/anaconda/noarch/wcwidth-0.2.5-py_0.tar.bz2 109 | https://conda.anaconda.org/conda-forge/noarch/wheel-0.35.1-pyh9f0ad1d_0.tar.bz2 110 | https://conda.anaconda.org/conda-forge/linux-64/cairo-1.16.0-h3fc0475_1005.tar.bz2 111 | https://conda.anaconda.org/conda-forge/noarch/click-plugins-1.1.1-py_0.tar.bz2 112 | https://conda.anaconda.org/conda-forge/noarch/cligj-0.5.0-py_0.tar.bz2 113 | https://conda.anaconda.org/conda-forge/linux-64/geotiff-1.6.0-ha04d9d0_1.tar.bz2 114 | https://repo.anaconda.com/pkgs/main/linux-64/jedi-0.18.1-py38h06a4308_1.conda 115 | https://conda.anaconda.org/conda-forge/linux-64/libdap4-3.20.6-h1d1bd15_1.tar.bz2 116 | https://conda.anaconda.org/conda-forge/linux-64/libnetcdf-4.7.4-nompi_h84807e1_105.tar.bz2 117 | https://conda.anaconda.org/conda-forge/linux-64/libspatialite-4.3.0a-h57f1b35_1039.tar.bz2 118 | https://conda.anaconda.org/conda-forge/linux-64/llvmlite-0.34.0-py38h4f45e52_1.tar.bz2 119 | https://conda.anaconda.org/conda-forge/linux-64/numpy-1.19.1-py38hbc27379_2.tar.bz2 120 | https://conda.anaconda.org/anaconda/linux-64/pexpect-4.8.0-py38_0.tar.bz2 121 | https://conda.anaconda.org/conda-forge/linux-64/pyproj-2.6.1.post1-py38h8e47818_1.tar.bz2 122 | https://conda.anaconda.org/conda-forge/noarch/python-dateutil-2.8.1-py_0.tar.bz2 123 | https://conda.anaconda.org/conda-forge/linux-64/rtree-0.9.4-py38h08f867b_1.tar.bz2 124 | https://conda.anaconda.org/conda-forge/linux-64/setuptools-49.6.0-py38h32f6830_1.tar.bz2 125 | https://conda.anaconda.org/conda-forge/linux-64/tiledb-2.0.8-h3effe38_1.tar.bz2 126 | https://conda.anaconda.org/anaconda/noarch/traitlets-5.0.5-py_0.tar.bz2 127 | https://conda.anaconda.org/conda-forge/linux-64/cftime-1.2.1-py38h8790de6_0.tar.bz2 128 | https://conda.anaconda.org/conda-forge/linux-64/h5py-2.10.0-nompi_py38hfb01d0b_104.tar.bz2 129 | https://conda.anaconda.org/conda-forge/noarch/joblib-0.17.0-py_0.tar.bz2 130 | https://conda.anaconda.org/anaconda/linux-64/jupyter_core-4.6.3-py38_0.tar.bz2 131 | https://conda.anaconda.org/conda-forge/noarch/munch-2.5.0-py_0.tar.bz2 132 | https://conda.anaconda.org/conda-forge/linux-64/numba-0.51.2-py38hc5bc63f_0.tar.bz2 133 | https://conda.anaconda.org/conda-forge/linux-64/pandas-1.1.3-py38h950e882_0.tar.bz2 134 | https://conda.anaconda.org/conda-forge/noarch/pip-20.2.3-py_0.tar.bz2 135 | https://conda.anaconda.org/conda-forge/linux-64/poppler-0.89.0-h4190859_1.tar.bz2 136 | https://conda.anaconda.org/anaconda/noarch/pygments-2.7.1-py_0.tar.bz2 137 | https://conda.anaconda.org/conda-forge/linux-64/scipy-1.5.2-py38h8c5af15_0.tar.bz2 138 | https://conda.anaconda.org/conda-forge/linux-64/shapely-1.7.1-py38hc7361b7_0.tar.bz2 139 | https://conda.anaconda.org/conda-forge/noarch/snuggs-1.4.7-py_0.tar.bz2 140 | https://conda.anaconda.org/anaconda/noarch/jupyter_client-6.1.7-py_0.tar.bz2 141 | https://conda.anaconda.org/conda-forge/linux-64/libgdal-3.1.2-hb2a6f5f_1.tar.bz2 142 | https://conda.anaconda.org/conda-forge/linux-64/netcdf4-1.5.4-nompi_py38hec8b9af_102.tar.bz2 143 | https://conda.anaconda.org/anaconda/noarch/prompt-toolkit-3.0.8-py_0.tar.bz2 144 | https://conda.anaconda.org/conda-forge/linux-64/scikit-learn-0.23.2-py38hee58b96_0.tar.bz2 145 | https://conda.anaconda.org/conda-forge/linux-64/gdal-3.1.2-py38hb61cb63_1.tar.bz2 146 | https://conda.anaconda.org/anaconda/linux-64/ipython-7.18.1-py38h5ca1d4c_0.tar.bz2 147 | https://conda.anaconda.org/conda-forge/linux-64/rasterio-1.1.7-py38h676c6b2_0.tar.bz2 148 | https://conda.anaconda.org/conda-forge/linux-64/fiona-1.8.17-py38h676c6b2_0.tar.bz2 149 | https://conda.anaconda.org/anaconda/linux-64/ipykernel-5.3.4-py38h5ca1d4c_0.tar.bz2 150 | https://conda.anaconda.org/conda-forge/noarch/geopandas-0.8.1-py_0.tar.bz2 151 | -------------------------------------------------------------------------------- /model/pyNoahMP/data/URBPARM.TBL: -------------------------------------------------------------------------------- 1 | # The parameters in this table may vary greatly from city to city. 2 | # The default values are probably not appropriate for any given city. 3 | # Users should adapt these values based on the city they are working 4 | # with. 5 | 6 | # Urban Parameters depending on Urban type 7 | # USGS 8 | 9 | Number of urban categories: 3 10 | 11 | # 12 | # Where there are multiple columns of values, the values refer, in 13 | # order, to: 1) Low density residential, 2) High density residential, 14 | # and 3) Commercial: I.e.: 15 | # 16 | # Index: 1 2 3 17 | # Type: Low-dens Res, Hi-dens Res, Commercial 18 | # 19 | 20 | # 21 | # ZR: Roof level (building height) [ m ] 22 | # (sf_urban_physics=1) 23 | 24 | ZR: 5.0, 7.5, 10.0 25 | 26 | # 27 | # SIGMA_ZED: Standard Deviation of roof height [ m ] 28 | # (sf_urban_physics=1) 29 | 30 | SIGMA_ZED: 1.0, 3.0, 4.0 31 | 32 | # 33 | # ROOF_WIDTH: Roof (i.e., building) width [ m ] 34 | # (sf_urban_physics=1) 35 | 36 | ROOF_WIDTH: 8.3, 9.4, 10.0 37 | 38 | # 39 | # ROAD_WIDTH: road width [ m ] 40 | # (sf_urban_physics=1) 41 | # 42 | 43 | ROAD_WIDTH: 8.3, 9.4, 10.0 44 | 45 | # 46 | # AH: Anthropogenic heat [ W m{-2} ] 47 | # (sf_urban_physics=1) 48 | # 49 | 50 | AH: 20.0, 50.0, 90.0 51 | 52 | 53 | # 54 | # ALH: Anthropogenic latent heat [ W m{-2} ] 55 | # (sf_urban_physics=1) 56 | # 57 | 58 | ALH: 20.0, 25.0, 40.0 59 | 60 | # 61 | # AKANDA_URBAN: Coefficient modifying the Kanda approach to computing 62 | # surface layer exchange coefficients. 63 | # (sf_urban_physics=1) 64 | 65 | AKANDA_URBAN: 1.29 1.29 1.29 66 | 67 | # 68 | # DDZR: Thickness of each roof layer [ m ] 69 | # This is currently NOT a function urban type, but a function 70 | # of the number of layers. Number of layers must be 4, for now. 71 | # (sf_urban_physics=1) 72 | 73 | DDZR: 0.05, 0.05, 0.05, 0.05 74 | 75 | # 76 | # DDZB: Thickness of each building wall layer [ m ] 77 | # This is currently NOT a function urban type, but a function 78 | # of the number of layers. Number of layers must be 4, for now. 79 | # (sf_urban_physics=1) 80 | # 81 | 82 | DDZB: 0.05, 0.05, 0.05, 0.05 83 | 84 | # 85 | # DDZG: Thickness of each ground (road) layer [ m ] 86 | # This is currently NOT a function urban type, but a function 87 | # of the number of layers. Number of layers must be 4, for now. 88 | # (sf_urban_physics=1) 89 | # 90 | 91 | DDZG: 0.05, 0.25, 0.50, 0.75 92 | 93 | # 94 | # BOUNDR: Lower boundary condition for roof layer temperature [ 1: Zero-Flux, 2: T = Constant ] 95 | # (sf_urban_physics=1) 96 | # 97 | 98 | BOUNDR: 1 99 | 100 | # 101 | # BOUNDB: Lower boundary condition for wall layer temperature [ 1: Zero-Flux, 2: T = Constant ] 102 | # (sf_urban_physics=1) 103 | # 104 | 105 | BOUNDB: 1 106 | 107 | # 108 | # BOUNDG: Lower boundary condition for ground (road) layer temperature [ 1: Zero-Flux, 2: T = Constant ] 109 | # (sf_urban_physics=1) 110 | # 111 | 112 | BOUNDG: 1 113 | 114 | # 115 | # Ch of Wall and Road [ 1: M-O Similarity Theory, 2: Empirical Form of Narita et al., 1997 (recommended) ] 116 | # (sf_urban_physics=1) 117 | # 118 | 119 | CH_SCHEME: 2 120 | 121 | # 122 | # Surface and Layer Temperatures [ 1: 4-layer model, 2: Force-Restore method ] 123 | # (sf_urban_physics=1) 124 | # 125 | 126 | TS_SCHEME: 1 127 | 128 | # 129 | # AHOPTION [ 0: No anthropogenic heating, 1: Anthropogenic heating will be added to sensible heat flux term ] 130 | # (sf_urban_physics=1) 131 | # 132 | 133 | AHOPTION: 0 134 | 135 | # 136 | # Anthropogenic Heating diurnal profile. 137 | # Multiplication factor applied to AH (as defined in the table above) 138 | # Hourly values ( 24 of them ), starting at 01 hours Local Time. 139 | # For sub-hourly model time steps, value changes on the hour and is 140 | # held constant until the next hour. 141 | # (sf_urban_physics=1) 142 | # 143 | 144 | AHDIUPRF: 0.16 0.13 0.08 0.07 0.08 0.26 0.67 0.99 0.89 0.79 0.74 0.73 0.75 0.76 0.82 0.90 1.00 0.95 0.68 0.61 0.53 0.35 0.21 0.18 145 | 146 | # 147 | # ALHOPTION [ 0: No anthropogenic latent heat, 1: Anthropogenic heating will be added to latent heat flux term ] 148 | # (sf_urban_physics=1) 149 | # 150 | 151 | ALHOPTION: 0 152 | 153 | # 154 | # Anthropogenic latent heat: seasonal coefficient of daily maximum values 155 | # From left to right in order: Spring (MAM), Summer(JJA), Fall(SON), Winter(DJF) 156 | # (sf_urban_physics=1) 157 | # 158 | 159 | ALHSEASON: 0.43 1.00 0.54 0.40 160 | 161 | # 162 | # Anthropogenic latent heat diurnal profile. 163 | # Multiplication factor applied to seasonal ALH (as defined above) 164 | # Half-hourly values ( 48 of them ), starting at 00:30 hours Local Time. 165 | # (sf_urban_physics=1) 166 | # 167 | 168 | ALHDIUPRF: 0.436 0.421 0.391 0.356 0.311 0.301 0.306 0.295 0.253 0.205 0.177 0.162 0.148 0.121 0.118 0.146 0.210 0.250 0.227 0.162 0.127 0.184 0.306 0.413 0.487 0.559 0.639 0.728 0.754 0.812 0.867 0.969 1.000 0.949 0.840 0.775 0.758 0.756 0.706 0.658 0.637 0.632 0.636 0.633 0.639 0.615 0.553 0.485 169 | 170 | # Oasis effect 171 | # Multiplication factor applied to potential ET of vegetation in urban areas 172 | # Value should be larger than 1 when actived 173 | # (sf_urban_physics=1) 174 | 175 | OASIS: 1.0 176 | 177 | # Evaporation scheme for impervious surfaces (for roof, wall, and road) 178 | # [1: Hypothesized evaporation during large rainfall events (Original) 179 | # [2: Water-holding scheme over impervious surface, Yang et al., 2014 180 | # (sf_urban_physics=1) 181 | 182 | IMP_SCHEME: 1 183 | 184 | # Porosity of pavement materials on impervious surface 185 | # For calculating latent heat flux over impervious surface 186 | # From left to right in order: roof, wall, road 187 | # (sf_urban_physics=1,IMP_SCHEME=2) 188 | # 189 | 190 | PORIMP: 0.45 0.45 0.45 191 | 192 | # Maximum water-holding depth of pavement materials on impervious surface [m] 193 | # For calculating latent heat flux over impervious surface 194 | # From left to right in order: roof, wall, road 195 | # (sf_urban_physics=1,IMP_SCHEME=2) 196 | # 197 | 198 | DENGIMP: 0.001 0.0002 0.001 199 | 200 | # Urban irrigation scheme, for vegetation in urban area and green roof 201 | # [0: No irrigation 202 | # [1: Summertime (May-Sep) irrigation everyday at 9pm 203 | # (sf_urban_physics=1) 204 | 205 | IRI_SCHEME: 0 206 | 207 | # 208 | # GROPTION [ 0: No green roof, 1: Enable green roof simulation] 209 | # (sf_urban_physics=1) 210 | # 211 | 212 | GROPTION: 0 213 | 214 | # Surface fraction of green roof over urban rooftop (0-1) 215 | # (sf_urban_physics=1) 216 | # 217 | 218 | FGR: 0.0 219 | 220 | # 221 | # DZGR: Thickness of each layer on green roof [ m ] 222 | # Green roof structure: 4-layers 223 | # 1: Top Soil layer 2:Soil layer 3: Growing Medium layer 224 | # 4: concrete roof (depth depends on DDZR defined earlier in this table) 225 | # (sf_urban_physics=1) 226 | 227 | DZGR: 0.05 0.10 0.15 0.20 228 | 229 | # 230 | # FRC_URB: Fraction of the urban landscape which does not have natural 231 | # vegetation. [ Fraction ] 232 | # (sf_urban_physics=1,2,3) 233 | # 234 | 235 | FRC_URB: 0.5, 0.9, 0.95 236 | 237 | # 238 | # CAPR: Heat capacity of roof [ J m{-3} K{-1} ] 239 | # (sf_urban_physics=1,2,3) 240 | # 241 | 242 | CAPR: 1.0E6, 1.0E6, 1.0E6, 243 | 244 | # 245 | # CAPB: Heat capacity of building wall [ J m{-3} K{-1} ] 246 | # (sf_urban_physics=1,2,3) 247 | # 248 | 249 | CAPB: 1.0E6, 1.0E6, 1.0E6, 250 | 251 | # 252 | # CAPG: Heat capacity of ground (road) [ J m{-3} K{-1} ] 253 | # (sf_urban_physics=1,2,3) 254 | # 255 | 256 | CAPG: 1.4E6, 1.4E6, 1.4E6, 257 | 258 | # 259 | # AKSR: Thermal conductivity of roof [ J m{-1} s{-1} K{-1} ] 260 | # (sf_urban_physics=1,2,3) 261 | # 262 | 263 | AKSR: 0.67, 0.67, 0.67, 264 | 265 | # 266 | # AKSB: Thermal conductivity of building wall [ J m{-1} s{-1} K{-1} ] 267 | # (sf_urban_physics=1,2,3) 268 | # 269 | 270 | AKSB: 0.67, 0.67, 0.67, 271 | 272 | # 273 | # AKSG: Thermal conductivity of ground (road) [ J m{-1} s{-1} K{-1} ] 274 | # (sf_urban_physics=1,2,3) 275 | # 276 | 277 | AKSG: 0.4004, 0.4004, 0.4004, 278 | 279 | # 280 | # ALBR: Surface albedo of roof [ fraction ] 281 | # (sf_urban_physics=1,2,3) 282 | # 283 | 284 | ALBR: 0.20, 0.20, 0.20 285 | 286 | # 287 | # ALBB: Surface albedo of building wall [ fraction ] 288 | # (sf_urban_physics=1,2,3) 289 | # 290 | 291 | ALBB: 0.20, 0.20, 0.20 292 | 293 | # 294 | # ALBG: Surface albedo of ground (road) [ fraction ] 295 | # (sf_urban_physics=1,2,3) 296 | # 297 | 298 | ALBG: 0.20, 0.20, 0.20 299 | 300 | # 301 | # EPSR: Surface emissivity of roof [ - ] 302 | # (sf_urban_physics=1,2,3) 303 | # 304 | 305 | EPSR: 0.90, 0.90, 0.90 306 | 307 | # 308 | # EPSB: Surface emissivity of building wall [-] 309 | # (sf_urban_physics=1,2,3) 310 | # 311 | 312 | EPSB: 0.90, 0.90, 0.90 313 | 314 | # 315 | # EPSG: Surface emissivity of ground (road) [ - ] 316 | # (sf_urban_physics=1,2,3) 317 | # 318 | 319 | EPSG: 0.95, 0.95, 0.95 320 | 321 | # 322 | # Z0B: Roughness length for momentum, over building wall [ m ] 323 | # Only active for CH_SCHEME == 1 324 | # (sf_urban_physics=1) 325 | # 326 | 327 | Z0B: 0.0001, 0.0001, 0.0001 328 | 329 | # 330 | # Z0G: Roughness length for momentum, over ground (road) [ m ] 331 | # Only active for CH_SCHEME == 1 332 | # (sf_urban_physics=1,2,3) 333 | # 334 | 335 | Z0G: 0.01, 0.01, 0.01 336 | 337 | # 338 | # Z0R: Roughness length for momentum over roof [ m ] 339 | # (sf_urban_physics=2,3) 340 | # 341 | 342 | Z0R: 0.01, 0.01, 0.01 343 | 344 | # 345 | # TRLEND: Lower boundary condition for roof temperature [ K ] 346 | # (sf_urban_physics=1,2,3) 347 | # 348 | 349 | TRLEND: 293.00, 293.00, 293.00 350 | 351 | # 352 | # TBLEND: Lower boundary temperature for building wall temperature [ K ] 353 | # (sf_urban_physics=1,2,3) 354 | # 355 | 356 | TBLEND: 293.00, 293.00, 293.00 357 | 358 | # 359 | # TGLEND: Lower boundary temperature for ground (road) temperature [ K ] 360 | # (sf_urban_physics=1,2,3) 361 | # 362 | 363 | TGLEND: 293.00, 293.00, 293.00 364 | 365 | # 366 | # COP: Coefficient of performance of the A/C systems [ - ] 367 | # (sf_urban_physics=3) 368 | # 369 | 370 | COP: 3.5, 3.5, 3.5 371 | 372 | # 373 | # BLDAC_FRC: fraction of buildings installed with A/C systems [ - ] 374 | # (sf_urban_physics=3) 375 | # 376 | 377 | BLDAC_FRC: 1.0, 1.0, 1.0 378 | 379 | # 380 | # COOLED_FRC: fraction of cooled floor area in buildings [ - ] 381 | # (sf_urban_physics=3) 382 | # 383 | COOLED_FRC: 1.0, 1.0, 1.0 384 | 385 | # 386 | # PWIN: Coverage area fraction of windows in the walls of the building [ - ] 387 | # (sf_urban_physics=3) 388 | # 389 | 390 | PWIN: 0.2, 0.2, 0.2 391 | 392 | # 393 | # BETA: Thermal efficiency of heat exchanger 394 | # (sf_urban_physics=3) 395 | # 396 | 397 | BETA: 0.75, 0.75, 0.75 398 | 399 | # 400 | # SW_COND: Air conditioning switch, 1=ON 401 | # (sf_urban_physics=3) 402 | # 403 | 404 | SW_COND: 1, 1, 1 405 | 406 | # 407 | # TIME_ON: Initial local time of A/C systems, [ h ] 408 | # (sf_urban_physics=3) 409 | # 410 | 411 | TIME_ON: 0., 0., 0. 412 | 413 | # 414 | # TIME_OFF: End local time of A/C systems, [ h ] 415 | # (sf_urban_physics=3) 416 | # 417 | 418 | TIME_OFF: 24., 24., 24. 419 | 420 | # 421 | # TARGTEMP: Target Temperature of the A/C systems, [ K ] 422 | # (sf_urban_physics=3) 423 | # 424 | 425 | TARGTEMP: 298., 298., 297. 426 | 427 | # 428 | # GAPTEMP: Comfort Range of the indoor Temperature, [ K ] 429 | # (sf_urban_physics=3) 430 | # 431 | 432 | GAPTEMP: 0.5, 0.5, 0.5 433 | 434 | # 435 | # TARGHUM: Target humidity of the A/C systems, [ Kg/Kg ] 436 | # (sf_urban_physics=3) 437 | # 438 | 439 | TARGHUM: 0.005, 0.005, 0.005 440 | 441 | # 442 | # GAPHUM: Comfort Range of the specific humidity, [ Kg/Kg ] 443 | # (sf_urban_physics=3) 444 | # 445 | 446 | GAPHUM: 0.005, 0.005, 0.005 447 | 448 | # 449 | # PERFLO: Peak number of occupants per unit floor area, [ person/m^2 ] 450 | # (sf_urban_physics=3) 451 | # 452 | 453 | PERFLO: 0.01, 0.01, 0.02 454 | 455 | # 456 | # HSEQUIP: Diurnal heating profile of heat generated by equipments 457 | # (sf_urban_physics=3) 458 | # 459 | 460 | HSEQUIP: 0.25 0.25 0.25 0.25 0.25 0.25 0.25 0.5 1.0 1.0 1.0 1.0 1.0 1.0 1.0 1.0 1.0 1.0 0.5 0.25 0.25 0.25 0.25 0.25 461 | 462 | # 463 | # HSEQUIP_SCALE_FACTOR: Peak heat generated by equipments, [ W/m^2 ] 464 | # (sf_urban_physics=3) 465 | # 466 | 467 | HSEQUIP_SCALE_FACTOR: 16.00, 20.00, 36.00 468 | 469 | STREET PARAMETERS: 470 | # (sf_urban_physics=2,3) 471 | 472 | # urban street street building 473 | # category direction width width 474 | # [index] [deg from N] [m] [m] 475 | 476 | 1 0.0 30. 13. 477 | 1 90.0 30. 13. 478 | 2 0.0 25. 17. 479 | 2 90.0 25. 17. 480 | 3 0.0 20. 20. 481 | 3 90.0 20. 20. 482 | 483 | END STREET PARAMETERS 484 | 485 | BUILDING HEIGHTS: 1 486 | # (sf_urban_physics=2,3) 487 | 488 | # height Percentage 489 | # [m] [%] 490 | 5.0 15.0 491 | 10.0 70.0 492 | 15.0 15.0 493 | END BUILDING HEIGHTS 494 | 495 | BUILDING HEIGHTS: 2 496 | # (sf_urban_physics=2,3) 497 | 498 | # height Percentage 499 | # [m] [%] 500 | 5.0 0.0 501 | 10.0 20.0 502 | 15.0 60.0 503 | 20.0 20.0 504 | END BUILDING HEIGHTS 505 | 506 | BUILDING HEIGHTS: 3 507 | # (sf_urban_physics=2,3) 508 | 509 | # height Percentage 510 | # [m] [%] 511 | 5.0 0.0 512 | 10.0 0.0 513 | 15.0 10.0 514 | 20.0 25.0 515 | 25.0 40.0 516 | 30.0 25.0 517 | 35.0 0.0 518 | END BUILDING HEIGHTS 519 | -------------------------------------------------------------------------------- /model/pyRouting/routing.py: -------------------------------------------------------------------------------- 1 | import os 2 | import glob 3 | import json 4 | import netCDF4 as nc 5 | import numpy as np 6 | np.seterr(divide='ignore', invalid='ignore') 7 | import scipy.sparse as sparse 8 | import scipy.sparse.linalg 9 | import pickle 10 | import copy 11 | import numba 12 | import time 13 | import sys 14 | import h5py 15 | 16 | class kinematic: 17 | 18 | def __init__(self,MPI,cid,cid_rank_mapping,dt,nhband,nhru,cdir,Qobs_file,dt_routing): 19 | 20 | self.itime = 0 21 | self.comm = MPI.COMM_WORLD 22 | self.size = self.comm.Get_size() 23 | self.rank = self.comm.Get_rank() 24 | self.name = MPI.Get_processor_name() 25 | self.cid_rank_mapping = cid_rank_mapping 26 | 27 | #Read in all the relevant data (this should be moved to the preprocessor) 28 | #self.db = pickle.load(open('/stor/soteria/hydro/private/nc153/projects/Octopy/parallel/tmp/%d.pck' ,'rb')) 29 | self.db = pickle.load(open('%s/octopy.pck' % cdir ,'rb')) 30 | 31 | #Read in the discharge input data 32 | #self.Qobs = pickle.load(open(Qobs_file,'rb')) 33 | #self.Qobs = pickle.load(open('/home/nc153/soteria/projects/hydroblocks_inter_catchment/regions/SGP_OK/obs/GSAL_2004-2019.pck','rb')) 34 | #self.Qobs2 = pickle.load(open('/home/nc153/soteria/projects/hydroblocks_inter_catchment/regions/SGP_OK/obs/chikaskia/chikaskia_2015-2017.pck','rb')) 35 | 36 | #Define the variables 37 | self.dt = dt 38 | self.dt_routing = dt_routing 39 | self.cid = cid 40 | self.uhs = self.db['uhs'][:] #unit hydrographs 41 | self.uh_travel_distance = self.db['uh_travel_time'] #travel distance per hband (travel time is bug) 42 | self.c_length = self.db['c_length'][:] 43 | self.c_n = self.db['c_n'][:] 44 | self.fp_n = self.db['fp_n'][:] 45 | #self.c_n[:] = 0.035#0.05#1#0.05#0.03 46 | #self.fp_n[:] = 0.15#0.15#25#0.15#0.15 47 | m = self.fp_n < self.c_n 48 | self.fp_n[m] = self.c_n[m] 49 | self.c_slope = self.db['c_slope'][:] 50 | self.c_slope[self.c_slope < 10**-3] = 10**-3 51 | self.c_bankfull = self.db['c_bankfull'][:] 52 | self.nchannel = self.c_length.size 53 | self.A0 = 10**-5*np.ones(self.c_length.size) 54 | self.u0 = 10**-5*np.ones(self.c_length.size) 55 | self.A1 = np.zeros(self.c_length.size) 56 | self.bcs = np.zeros(self.c_length.size) 57 | #self.qin = np.zeros(self.c_length.size) 58 | #self.qout = np.zeros(self.c_length.size) 59 | self.qss = np.zeros(self.c_length.size) 60 | self.qss_remainder = np.zeros(self.c_length.size) 61 | self.dA = np.zeros(self.c_length.size) 62 | self.Q0 = np.zeros(self.c_length.size) 63 | self.Q1 = np.zeros(self.c_length.size) 64 | self.reach2hband = np.asarray(self.db['reach2hband'].todense()) 65 | self.reach2hband_inundation = np.copy(self.reach2hband) 66 | self.reach2hband_inundation[:] = 0.0 67 | self.scids_hdw = self.db['scids_hdw'] 68 | self.rcids_hdw = self.db['rcids_hdw'] 69 | self.hdw = self.db['hdw'] 70 | self.hdb = self.db['hdb'] 71 | self.hdb['hband'] = self.hdb['hband'].astype(np.int64) 72 | self.hdb['M'][self.hdb['M'] == 0] = 10**-5 73 | self.c_width = self.hdb['W'][:,0]#self.db['c_width'][:] 74 | self.LHS = self.db['LHS'] 75 | tmp_topology = self.db['LHS'][:] 76 | tmp_topology.setdiag(0) 77 | tmp_topology = tmp_topology.todense() 78 | topology = np.zeros((tmp_topology.shape[0],3),dtype=np.int32) 79 | topology[:] = -9999 80 | for i in range(tmp_topology.shape[0]): 81 | ids = np.where(tmp_topology[i,:] != 0)[1] 82 | topology[i,0:ids.size] = ids[:] 83 | self.topology = topology 84 | self.A0_org = np.copy(self.A0) 85 | self.u0_org = np.copy(self.u0) 86 | self.Ac0 = np.zeros(self.c_length.size) 87 | self.Ac1 = np.zeros(self.c_length.size) 88 | self.dAc0 = np.zeros(self.c_length.size) 89 | self.dAc1 = np.zeros(self.c_length.size) 90 | self.Af0 = np.zeros(self.c_length.size) 91 | self.Af1 = np.zeros(self.c_length.size) 92 | self.dAf0 = np.zeros(self.c_length.size) 93 | self.dAf1 = np.zeros(self.c_length.size) 94 | self.Ac = np.zeros(self.c_length.size) 95 | self.Af = np.zeros(self.c_length.size) 96 | self.Qc = np.zeros(self.c_length.size) 97 | self.Qf = np.zeros(self.c_length.size) 98 | self.hru_inundation = np.zeros(nhru) 99 | self.hru_runoff_inundation = np.zeros(nhru) 100 | self.hband_inundation = np.zeros(nhband) 101 | self.hband_inundation1 = np.zeros(nhband) 102 | self.fct_infiltrate = 0.1 103 | 104 | #Define channel hbands per reach 105 | self.hband_channel = np.zeros(self.c_length.size).astype(np.int32) 106 | m = (self.hdb['hand'] == 0) & (self.hdb['W'] > 0) 107 | self.hband_channel[:] = self.hdb['hband'][m] 108 | 109 | #Pause until all cores have all their data 110 | self.comm.Barrier() 111 | 112 | return 113 | 114 | def update(self,dt): 115 | 116 | #Explicit solution 117 | #self.dt_routing = 100 #seconds 118 | dt_routing = self.dt_routing 119 | nt = int(dt/dt_routing) 120 | for it in range(nt): 121 | #Exchange boundary conditions 122 | self.exchange_bcs() 123 | #Update solution 124 | (self.Q0,self.u0,self.A1) = update_solution_explicit(self.c_slope,self.c_n,self.u0,self.A0,self.topology,self.c_length,self.qss,self.bcs,self.dt_routing,self.fp_n,self.hdb['Ac'],self.hdb['Af'],self.hdb['Pc'],self.hdb['Pf'],self.hdb['W'],self.hdb['M']) 125 | self.A0[:] = self.A1[:] 126 | self.Q1[:] = self.Q0[:] 127 | 128 | #Zero out qss 129 | self.qss[:] = 0.0 130 | #Calculate area-weighted average inundation height per hband 131 | A = self.hdb['Af'] + self.hdb['Ac'] 132 | #Add channel and flood cross sectional area 133 | A1 = self.A0[:] 134 | W = self.hdb['W'] 135 | M = self.hdb['M'] 136 | hand = self.hdb['hand'] 137 | hband = self.hdb['hband'] 138 | (self.hband_inundation[:],self.reach2hband_inundation[:]) = calculate_inundation_height_per_hband(A,A1,W,M,hand,hband,self.reach2hband_inundation,self.reach2hband) 139 | tmp = np.sum(self.reach2hband*self.reach2hband_inundation,axis=1)/self.c_length 140 | tmp1 = np.max(np.abs(A1-tmp)) 141 | if tmp1 > 10**-3: 142 | argmax = np.argmax(np.abs(A1-tmp)) 143 | print(self.itime,np.abs(A1-tmp)[argmax],A1[argmax],tmp[argmax],flush=True) 144 | 145 | #Calculate Qc and Qf 146 | self.Ac[:] = self.reach2hband[range(self.nchannel),self.hband_channel]*self.reach2hband_inundation[range(self.nchannel),self.hband_channel]/self.c_length 147 | self.Af[:] = self.A0 - self.Ac 148 | self.Qc[:] = self.u0*self.Ac 149 | self.Qf[:] = self.u0*self.Af 150 | self.Qc[self.Qc < 0.0] = 0.0 151 | self.Qf[self.Qf < 0.0] = 0.0 152 | 153 | return 154 | 155 | 156 | def exchange_bcs(self,): 157 | 158 | #Send headwater data 159 | crm = self.cid_rank_mapping #Where each cid resides 160 | for ucid in self.scids_hdw: 161 | dest = crm[ucid] 162 | db_ex = {'cid':self.cid,'scid_hdw':ucid, 163 | 'Q0':self.Q0[self.hdw[ucid][self.cid]['outlet']]} 164 | self.comm.send(db_ex,dest=dest,tag=11) 165 | 166 | #Receive headwater data 167 | recv = {} 168 | for ucid in self.rcids_hdw: 169 | if ucid not in recv:recv[ucid] = {} 170 | source = crm[ucid] 171 | db_ex = self.comm.recv(source=source,tag=11) 172 | for var in db_ex: 173 | recv[ucid][var] = db_ex[var] 174 | 175 | #Update the boundary conditions 176 | bcs = self.bcs 177 | self.bcs[:] = 0.0 178 | for ucid in self.rcids_hdw: 179 | for ic in range(len(self.hdw[self.cid][ucid]['inlet'])): 180 | inlet = self.hdw[self.cid][ucid]['inlet'][ic] 181 | self.bcs[inlet] += recv[ucid]['Q0'][ic] 182 | 183 | #Wait until all are done 184 | self.comm.Barrier() 185 | 186 | return 187 | 188 | def update_solution_implicit(self,): 189 | 190 | #Extract info 191 | hdb = self.hdb 192 | A0 = self.A0[:] 193 | c_slope = self.c_slope 194 | c_n = self.c_n 195 | LHS = self.LHS 196 | c_length = self.c_length 197 | A0_org = self.A0_org[:] 198 | qss = self.qss 199 | bcs = self.bcs 200 | maxu = 2.0 201 | minu = 0.1 202 | dt = self.dt 203 | 204 | #Determine velocity 205 | #This effectively linearizes the sub-grid network solver by setting the velocity with the previous time step A (Another way to do this, is converge first locally every change in BCs) 206 | Kvn = calculate_compound_convenyance(hdb['Ac'],hdb['Af'],hdb['Pc'],hdb['Pf'],hdb['W'],hdb['M'],A0_org,self.c_n,self.fp_n) 207 | u1 = np.zeros(Kvn.size) 208 | u1[A0_org > 0.0] = Kvn[A0_org > 0.0]*c_slope[A0_org > 0.0]**0.5/A0_org[A0_org > 0.0] 209 | #Constrain velocity 210 | u1[u1 > maxu] = maxu 211 | u1[u1 < minu] = minu 212 | #Fill non-diagonals 213 | tmp = -dt*u1 214 | LHS = LHS.multiply(tmp) 215 | #Fill diagonal 216 | tmp = c_length + dt*u1 217 | LHS.setdiag(tmp) 218 | #Set right hand side 219 | RHS0 = c_length*A0_org 220 | RHS2 = np.zeros(A0.size) 221 | RHS2[:] = dt*bcs 222 | #Iterate qss values to ensure A1 is above 0 223 | RHS1 = dt*qss*c_length 224 | RHS = (RHS0 + RHS1 + RHS2)#/(c_length + dt*u1) 225 | A1 = scipy.sparse.linalg.spsolve(LHS.tocsr(),RHS,use_umfpack=False) 226 | #Curate A1 (issues with conservation of mass) 227 | A1[A1 < 0] = 0.0 228 | #Calculate difference with previous iteration 229 | dif1 = np.max(np.abs(A0 - A1)) 230 | #Reset A0 231 | A0[:] = A1[:] 232 | #Calculate Q1 233 | Q1 = A0*u1 234 | #Reset Q0 235 | self.Q0[:] = Q1[:] 236 | self.u0[:] = u1[:] 237 | 238 | #Update data in db 239 | self.A0[:] = A0[:] 240 | self.Q1[:] = Q1[:] 241 | self.A1[:] = A1[:] 242 | self.dif0 = dif1 243 | 244 | return 245 | 246 | def update_solution_explicit_original(self,it): 247 | 248 | #Extract info 249 | hdb = self.hdb 250 | A0 = self.A0[:] 251 | c_slope = self.c_slope 252 | c_slope[c_slope < 10**-3] = 10**-3 253 | c_n = self.c_n 254 | LHS = self.LHS 255 | c_length = self.c_length 256 | A0_org = A0[:]#elf.A0_org[:] 257 | qss = self.qss 258 | bcs = self.bcs/c_length 259 | maxu = 10.0 260 | minu = 0.1 261 | dt = self.dt_routing 262 | 263 | #Determine velocity 264 | #This effectively linearizes the sub-grid network solver by setting the velocity with the previous time step A (Another way to do this, is converge first locally every change in BCs) 265 | Kvn = calculate_compound_convenyance(hdb['Ac'],hdb['Af'],hdb['Pc'],hdb['Pf'],hdb['W'],hdb['M'],A0_org,self.c_n,self.fp_n) 266 | u1 = np.zeros(Kvn.size) 267 | u1[A0_org > 0.0] = Kvn[A0_org > 0.0]*c_slope[A0_org > 0.0]**0.5/A0_org[A0_org > 0.0] 268 | #Constrain velocity 269 | u1[u1 > maxu] = maxu 270 | u1[u1 < minu] = minu 271 | #Ammending LHS from the implicit solver for this (HACK) 272 | LHS.setdiag(0) 273 | #LHS = np.array(LHS.todense()) 274 | if (it == 0): 275 | Q0in = LHS*(u1*A0) 276 | if (self.rank == 0):print(Q0in[0:3]) 277 | exit() 278 | #A1 = A0 + source/sink + boundary conditions - Qout + Qin 279 | A1 = A0 + dt*qss + dt*bcs - dt*(u1*A0)/c_length + dt*(LHS*(u1*A0))/c_length 280 | #Curate A1 (issues with conservation of mass) 281 | A1[A1 < 0] = 0.0 282 | #Reset A0 283 | A0[:] = A1[:] 284 | #Calculate Q1 285 | Q1 = A0*u1 286 | #Update data in db 287 | self.Q0[:] = Q1[:] 288 | self.u0[:] = u1[:] 289 | self.A0[:] = A0[:] 290 | self.Q1[:] = Q1[:] 291 | self.A1[:] = A1[:] 292 | 293 | return 294 | 295 | @numba.jit(nopython=True,cache=True) 296 | def update_solution_explicit(c_slope,c_n,u0,A0,topology,c_length,qss,bcs,dt_routing, 297 | fp_n,Ac,Af,Pc,Pf,W,M): 298 | 299 | #Extract info 300 | bcs_c = bcs/c_length 301 | maxu = 10.0 302 | minu = 0.1 303 | dt = dt_routing 304 | 305 | #Determine velocity 306 | #Kvn = calculate_compound_convenyance(hdb['Ac'],hdb['Af'],hdb['Pc'],hdb['Pf'],hdb['W'],hdb['M'],A0,c_n,fp_n) 307 | Kvn = calculate_compound_convenyance(Ac,Af,Pc,Pf,W,M,A0,c_n,fp_n) 308 | u0 = np.zeros(Kvn.size) 309 | u0[A0 > 0.0] = Kvn[A0 > 0.0]*c_slope[A0 > 0.0]**0.5/A0[A0 > 0.0] 310 | 311 | #Constrain velocity 312 | u0[u0 > maxu] = maxu 313 | u0[u0 < minu] = minu 314 | 315 | #Compute Q0in 316 | Q0in = Compute_Q0in(topology,u0,A0) 317 | 318 | #A1 = A0 + source/sink + boundary conditions - Qout + Qin 319 | A1 = A0 + dt*qss + dt*bcs_c - dt*(u0*A0)/c_length + dt*Q0in/c_length 320 | 321 | #Curate A1 (issues with conservation of mass) 322 | A1[A1 < 0] = 0.0 323 | 324 | #Calculate Q0 325 | Q0 = A0*u0 326 | 327 | return (Q0,u0,A1) 328 | 329 | 330 | @numba.jit(nopython=True,cache=True,nogil=True,fastmath=True) 331 | def Compute_Q0in(topology,u0,A0): 332 | 333 | Q0in = np.zeros(A0.size) 334 | for i in range(Q0in.size): 335 | for j in range(topology.shape[1]): 336 | if topology[i,j] == -9999:continue 337 | else: Q0in[i] += u0[topology[i,j]]*A0[topology[i,j]] 338 | 339 | return Q0in 340 | 341 | @numba.jit(nopython=True,cache=True) 342 | def calculate_hydraulic_radius(A,P,W,A1): 343 | 344 | Rh = np.zeros(A.shape[0]) 345 | for i in range(A.shape[0]): 346 | A0 = 0.0 347 | P0 = 0.0 348 | for j in range(A.shape[1]): 349 | W1 = W[i,j] 350 | if A[i,j] > A1[i]:break 351 | if A[i,j+1] == 0.0:break 352 | A0 = A[i,j] 353 | P0 = P[i,j] 354 | #Calculate the height above the segment 355 | h = (A1[i] - A0)/np.sum(W[i,0:j+1]) 356 | #Calculate the wetted perimeter 357 | P1 = P0 + 2*h + W1 358 | #Calculate hydraulic radius 359 | Rh[i] = A1[i]/P1 360 | 361 | return Rh 362 | 363 | @numba.jit(nopython=True,cache=True,nogil=True,fastmath=True) 364 | def calculate_compound_convenyance(Ac,Af,Pc,Pf,W,M,A1,cn,fpn): 365 | 366 | Kvn = np.zeros(Ac.shape[0]) 367 | #Determine the level for which we need to back out info 368 | for i in range(Ac.shape[0]): 369 | for j in range(Ac.shape[1]-1): 370 | if (Af[i,j+1]+Ac[i,j+1]) > A1[i]:break 371 | if (Af[i,j+1]+Ac[i,j+1]) == 0.0:break 372 | if j == 0: 373 | A0 = Ac[i,j] 374 | P0 = Pc[i,j] 375 | #Calculate the height above the segment 376 | h = (A1[i] - A0)/W[i,0] 377 | #Calculate the wetted perimeter 378 | P1 = P0 + 2*h + W[i,0] 379 | #Calculate compound conveyance 380 | Kvn[i] = A1[i]**(5.0/3.0)/P1**(2.0/3.0)/cn[i] 381 | else: 382 | #Calculate the height above the segment (quadratic equation) 383 | c = -(A1[i] - (Af[i,j] + Ac[i,j])) 384 | b = np.sum(W[i,0:j]) 385 | a = 1.0/M[i,j] 386 | h = (-b + (b**2.0 - 4.0*a*c)**0.5)/(2.0*a) 387 | #h2 = (-b - (b**2.0 - 4.0*a*c)**0.5)/(2.0*a) 388 | #print('inside1:',h1,h2,A1[i],(Af[i,j] + Ac[i,j]),a,b,c,M[i,j]) 389 | #Calculate channel cross sectional area 390 | Ac1 = Ac[i,1] + W[i,0]*h 391 | #Calculate floodplain cross sectional area 392 | Af1 = Af[i,1] + np.sum(W[i,1:j])*h + h*h/M[i,j] 393 | #Calculate channel wetted perimeter 394 | Pc1 = Pc[i,1] 395 | #Calculate floodplain wetted perimieter 396 | Pf1 = Pf[i,j] + 2*(h**2 + (h/M[i,j])**2)**0.5 397 | #print(h,M[i,j],Pc1,Pf1,cn[i],fpn[i]) 398 | #Calculate conveyances 399 | Kvnc = (1.0/cn[i])*(Ac1**(5.0/3.0)/Pc1**(2.0/3.0)) 400 | Kvnf = (1.0/fpn[i])*(Af1**(5.0/3.0)/Pf1**(2.0/3.0)) 401 | #Calculate compound conveyance 402 | Kvn[i] = Kvnc + Kvnf 403 | if np.isnan(Kvn[i]) == 1: 404 | print('inside',A1[i],Kvn[i],a,b,c,h,Ac1,Af1,Pc1,Pf1,Kvnc,Kvnf) 405 | 406 | return Kvn 407 | 408 | @numba.jit(nopython=True,cache=True) 409 | def calculate_inundation_height_per_hband(A,A1,W,M,hand,hband,reach2hband_inundation,reach2hband): 410 | 411 | #Zero out reach2hband_inundation 412 | reach2hband_inundation[:] = 0.0 413 | #Determine the inundation height per hand value per basin 414 | for i in range(A.shape[0]): 415 | A0 = 0.0 416 | for j in range(1,A.shape[1]): 417 | if A[i,j] > A1[i]:break 418 | if A[i,j+1] == 0.0:break 419 | A0 = A[i,j] 420 | #Calculate the height above the segment 421 | if j == 1:htop = (A1[i] - A0)/W[i,0] 422 | else: 423 | c = -(A1[i] - A0) 424 | b = np.sum(W[i,0:j-1]) 425 | a = 1.0/M[i,j-1] 426 | htop = (-b + (b**2.0 - 4.0*a*c)**0.5)/(2.0*a) #trapezoidal 427 | #Based on htop calculate the water heights (Wrong) 428 | h = np.zeros(j) 429 | h[0] = hand[i,j-1] + htop 430 | if j > 1:h[j-1] = htop*htop/M[i,j-1]/W[i,j-1] 431 | if j > 2: 432 | h[1:j-1] = hand[i,j-1] - hand[i,1:j-1] - (hand[i,2:j]-hand[i,1:j-1])/2 + htop 433 | #Add to hband sum (times area) 434 | idxs = hband[i,0:j] 435 | #Place the inundation level 436 | for k in range(idxs.size): 437 | idx = idxs[k] 438 | reach2hband_inundation[i,idx] = h[k] 439 | areas = np.sum(reach2hband,axis=0) 440 | hband_inundation = np.zeros(areas.size) 441 | hband_inundation[:] = np.sum(reach2hband*reach2hband_inundation,axis=0)/np.sum(reach2hband,axis=0) 442 | 443 | return (hband_inundation,reach2hband_inundation) 444 | 445 | @numba.jit(nopython=True,cache=True,nogil=True,fastmath=True) 446 | def compute_qss(reach2hband,crunoff,c_length,qss): 447 | for i in range(qss.size): 448 | qss[i] = np.sum(reach2hband[i,:]*crunoff/1000.0)/c_length[i] #m2/s 449 | #qss[:] += reach2hband.dot(crunoff/1000.0)/c_length #m2/s 450 | return qss 451 | -------------------------------------------------------------------------------- /model/pyRichards/richards.py: -------------------------------------------------------------------------------- 1 | import numpy as np 2 | import scipy.sparse as sparse 3 | import time 4 | import numba 5 | 6 | class richards: 7 | 8 | def __init__(self,nhru,nsoil,flag): 9 | 10 | self.theta = np.zeros((nhru,nsoil)) 11 | if flag==True: 12 | self.thetar = np.zeros((nhru,nsoil)) #laura svp 13 | self.thetas = np.zeros((nhru,nsoil)) #laura svp 14 | self.b = np.zeros((nhru,nsoil)) #laura svp 15 | self.satpsi = np.zeros((nhru,nsoil)) #laura svp 16 | self.ksat = np.zeros((nhru,nsoil)) #laura svp 17 | else: 18 | self.thetar = np.zeros(nhru) 19 | self.thetas = np.zeros(nhru) 20 | self.b = np.zeros(nhru) 21 | self.satpsi = np.zeros(nhru) 22 | self.ksat = np.zeros(nhru) 23 | 24 | #self.theta = np.zeros((nhru,nsoil)) 25 | #self.thetar = np.zeros(nhru) 26 | #self.thetas = np.zeros(nhru) 27 | #self.b = np.zeros(nhru) 28 | #self.satpsi = np.zeros(nhru) 29 | #self.ksat = np.zeros(nhru) 30 | 31 | self.dem = np.zeros(nhru) 32 | self.slope = np.zeros(nhru) 33 | #self.hand = np.zeros(nhru) 34 | self.area = np.zeros(nhru) 35 | self.dz = np.zeros((nhru,nsoil)) 36 | self.hdiv = np.zeros((nhru,nsoil)) 37 | self.m = np.zeros(nhru) 38 | 39 | #Initialize the width array 40 | self.width = [] 41 | self.I = [] 42 | 43 | return 44 | 45 | def calculate_soil_moisture_potential(self,il): 46 | 47 | eps = 0.01 48 | theta = self.theta[:,il] 49 | thetar = self.thetar 50 | thetas = self.thetas 51 | b = self.b 52 | satpsi = self.satpsi #meters 53 | m = (theta <= (1+eps)*thetar) 54 | theta[m] = (1+eps)*thetar[m] 55 | #psi = 1000.0*np.ones(theta.shape) #limit 56 | #m = theta > thetar 57 | #psi[m] = satpsi[m]*((theta[m] - thetar[m])/(thetas[m] - thetar[m]))**(-b[m]) 58 | #psi[m] = satpsi[m]*((theta[m] - thetar[m])/(thetas[m] - thetar[m]))**(-b[m]) 59 | 60 | with np.errstate(invalid='ignore'): 61 | psi = satpsi*((theta-thetar)/(thetas-thetar))**-b 62 | 63 | return psi 64 | 65 | def calculate_hydraulic_conductivity(self,psi,il): 66 | 67 | af = 1.0 #safe 68 | #sdz = np.cumsum(self.dz,axis=1)-self.dz/2. 69 | #df = np.exp(-self.m[:,np.newaxis]/sdz)[:,il] 70 | #Ksat_x = af*df*self.ksat[:] #lateral saturated hydraulic conductivity (multiply times anisotropy factor) [m/s] 71 | Ksat_x = af*self.ksat[:] #lateral saturated hydraulic conductivity (multiply times anisotropy factor) [m/s] 72 | with np.errstate(invalid='ignore'): 73 | K_x = Ksat_x*(psi/self.satpsi)**(-2-3/self.b) 74 | 75 | return K_x 76 | 77 | def calculate_transmissivity(self,psi,ztop,zbot): 78 | 79 | #af = 1.0 #safe 80 | af = 2.0 81 | m = np.copy(self.m) 82 | #m[:] = 1000.0 83 | Ksat_x = af*self.ksat[:] #lateral saturated hydraulic conductivity (multiply times anisotropy factor) [m/s] 84 | with np.errstate(invalid='ignore', divide='ignore'): 85 | K_x = Ksat_x*np.true_divide(psi,self.satpsi)**(-2-np.true_divide(3.,self.b)) 86 | K_x[~np.isfinite(K_x)] = np.nan 87 | #Calculate transmissivity at top layer (exponential decay) 88 | Ttop = m*K_x*np.exp(-ztop/m) 89 | #Calculate transmissivity at bottom of layer (exponential decay) 90 | Tbot = m*K_x*np.exp(-zbot/m) 91 | T = Ttop - Tbot 92 | 93 | return T 94 | 95 | def calculate_hydraulic_head(self,psi,depth): 96 | 97 | h = self.dem - depth - psi 98 | 99 | return h 100 | 101 | #def calculate_divergence_dense(self,h,K_x,dz): 102 | #@numba.jit(nopython=True,cache=True) 103 | def calculate_divergence_dense(self,h,T): 104 | 105 | #tic = time.time() 106 | dh = h[:,np.newaxis] - h[np.newaxis,:] 107 | w = self.w 108 | dx = self.dx 109 | area = self.area 110 | That = np.true_divide((2*T[:,np.newaxis]*T[np.newaxis,:]),(T[:,np.newaxis] + T[np.newaxis,:])) 111 | That[~np.isfinite(That)] = np.nan 112 | #[mm/s] = [mm/m]*[m/s]*[m]/[m]*[m]*[m]/[m2] 113 | calc_div = -1000.0*That*np.true_divide(dh,dx)*np.true_divide(w,area) # mm/s 114 | calc_div[~np.isfinite(calc_div)] = np.nan 115 | #print('calc_div',time.time() - tic) 116 | 117 | return calc_div 118 | 119 | #def calculate_divergence_sparse(self,h,K_x,dz): 120 | def calculate_divergence_sparse(self,h,T): 121 | 122 | #Define the boolean matrix (connections or not?) 123 | I = self.I 124 | #Calculate dh 125 | h1 = (I != 0).multiply(sparse.csr_matrix(h)) 126 | dh = h1.T - h1 127 | #Calculate dx 128 | d1 = (I != 0).multiply(sparse.csr_matrix(self.dem)) 129 | dx = d1.T - d1#**2 + self.dx**2) 130 | dx = dx.power(2) 131 | dx.data += self.dx**2 132 | dx = dx.power(0.5) 133 | #dx = (np.abs(d1.T - d1)**2 + self.dx**2)**0.5 134 | #dx = (np.abs(d1.T - d1)**2 + self.dx**2)**0.5 135 | #dx = (np.abs(self.dem[:,np.newaxis] - self.dem[np.newaxis,:])**2 + self.dx**2)**0.5 136 | #Calculate the effective hydraulic conductivity 137 | #k1 = (I != 0).multiply(sparse.csr_matrix(K_x)) 138 | #n = 2*k1.T.multiply(k1) 139 | #d = k1.T+k1 140 | #Khat = n.multiply(d.power(-1)) 141 | t1 = (I != 0).multiply(sparse.csr_matrix(T)) 142 | n = 2*t1.T.multiply(t1) 143 | d = t1.T+t1 144 | That = n.multiply(d.power(-1)).tocsr() 145 | print(That.count_nonzero,self.width.count_nonzero) 146 | #Calculate the flux 147 | #[m/s] = [m/s]*[m]/[m]*[m]*[m]/[m2] 148 | print(That.multiply(dh).shape) 149 | print(That.multiply(dh).multiply(self.width).count_nonzero) 150 | print(1.0/self.area) 151 | print(That.multiply(dh).multiply(self.width).multiply(1.0/self.area).count_nonzero) 152 | print(That.multiply(dh).multiply(self.width).multiply(1.0/self.area).multiply(dx.power(-1)).count_nonzero) 153 | return -That.multiply(dh).multiply(self.width).multiply(1.0/self.area).multiply(dx.power(-1)).multiply(1000) #mm/s 154 | #return -Khat.multiply(dh).multiply(self.width).multiply(dz/self.dx/self.area).multiply(1000) #mm/s 155 | 156 | def update(self): 157 | 158 | #Iterate per layer 159 | for il in range(self.theta.shape[1]): 160 | #Calculate soil moisture potential 161 | psi = self.calculate_soil_moisture_potential(il) 162 | zbot = np.sum(self.dz[:,0:il+1],axis=1) 163 | ztop = zbot - self.dz[:,il] 164 | T = self.calculate_transmissivity(psi,ztop,zbot) 165 | #Calculate hydraulic head 166 | h = self.calculate_hydraulic_head(psi,ztop) 167 | #Calculate the divergence 168 | q = self.calculate_divergence_dense(h,T) 169 | self.hdiv[:,il] = np.sum(q,axis=0) #mm/s 170 | 171 | return 172 | 173 | def update_numba(self,flag): 174 | 175 | theta = self.theta 176 | dz = self.dz 177 | hdiv = self.hdiv 178 | thetar = self.thetar 179 | thetas = self.thetas 180 | b = self.b 181 | satpsi = self.satpsi 182 | m = self.m 183 | ksat = self.ksat 184 | #hand = self.dem 185 | hand = self.dem1 186 | w = self.w 187 | dx = self.dx 188 | area = self.area 189 | if flag==True: #laura svp 190 | self.hdiv[:] = update_workhorse_vsp(theta,dz,hdiv,thetar,thetas,b,satpsi,m,ksat,hand,w,dx,area) #divergence computed with vertical variable soil properties 191 | else: 192 | self.hdiv[:] = update_workhorse(theta,dz,hdiv,thetar,thetas,b,satpsi,m,ksat,hand,w,dx,area) 193 | 194 | return 195 | 196 | class richards_hbands: 197 | 198 | def __init__(self,nhru,nhru2,nsoil,flag): #laura 199 | 200 | self.theta = np.zeros((nhru,nsoil)) 201 | if flag==True: 202 | self.thetar = np.zeros((nhru,nsoil)) #laura svp 203 | self.thetas = np.zeros((nhru,nsoil)) #laura svp 204 | self.b = np.zeros((nhru,nsoil)) #laura svp 205 | self.satpsi = np.zeros((nhru,nsoil)) #laura svp 206 | self.ksat = np.zeros((nhru,nsoil)) #laura svp 207 | else: 208 | self.thetar = np.zeros(nhru) 209 | self.thetas = np.zeros(nhru) 210 | self.b = np.zeros(nhru) 211 | self.satpsi = np.zeros(nhru) 212 | self.ksat = np.zeros(nhru) 213 | 214 | #self.theta = np.zeros((nhru2,nsoil)) 215 | #self.thetar = np.zeros(nhru2) 216 | #self.thetas = np.zeros(nhru2) 217 | #self.b = np.zeros(nhru2) 218 | #self.satpsi = np.zeros(nhru2) 219 | #self.ksat = np.zeros(nhru2) 220 | self.dem = np.zeros(nhru) 221 | self.demhband = np.zeros(nhru2) #laura added 222 | self.dem1hband=np.zeros(nhru2) #laura added 223 | self.slope = np.zeros(nhru2) 224 | #self.hand = np.zeros(nhru) 225 | self.area = np.zeros(nhru2) 226 | self.dz = np.zeros((nhru2,nsoil)) 227 | self.hdiv = np.zeros((nhru2,nsoil)) 228 | self.m = np.zeros(nhru2) 229 | 230 | #Initialize the width array 231 | self.width = {}#[] #laura 232 | self.I = {}#[]#laura 233 | 234 | return 235 | 236 | def calculate_soil_moisture_potential(self,il): 237 | 238 | eps = 0.01 239 | theta = self.theta[:,il] 240 | thetar = self.thetar 241 | thetas = self.thetas 242 | b = self.b 243 | satpsi = self.satpsi #meters 244 | m = (theta <= (1+eps)*thetar) 245 | theta[m] = (1+eps)*thetar[m] 246 | #psi = 1000.0*np.ones(theta.shape) #limit 247 | #m = theta > thetar 248 | #psi[m] = satpsi[m]*((theta[m] - thetar[m])/(thetas[m] - thetar[m]))**(-b[m]) 249 | #psi[m] = satpsi[m]*((theta[m] - thetar[m])/(thetas[m] - thetar[m]))**(-b[m]) 250 | 251 | with np.errstate(invalid='ignore'): 252 | psi = satpsi*((theta-thetar)/(thetas-thetar))**-b 253 | 254 | return psi 255 | 256 | def calculate_hydraulic_conductivity(self,psi,il): 257 | 258 | af = 1.0 #safe 259 | #sdz = np.cumsum(self.dz,axis=1)-self.dz/2. 260 | #df = np.exp(-self.m[:,np.newaxis]/sdz)[:,il] 261 | #Ksat_x = af*df*self.ksat[:] #lateral saturated hydraulic conductivity (multiply times anisotropy factor) [m/s] 262 | Ksat_x = af*self.ksat[:] #lateral saturated hydraulic conductivity (multiply times anisotropy factor) [m/s] 263 | with np.errstate(invalid='ignore'): 264 | K_x = Ksat_x*(psi/self.satpsi)**(-2-3/self.b) 265 | 266 | return K_x 267 | 268 | def calculate_transmissivity(self,psi,ztop,zbot): 269 | 270 | #af = 1.0 #safe 271 | af = 2.0 272 | m = np.copy(self.m) 273 | #m[:] = 1000.0 274 | Ksat_x = af*self.ksat[:] #lateral saturated hydraulic conductivity (multiply times anisotropy factor) [m/s] 275 | with np.errstate(invalid='ignore', divide='ignore'): 276 | K_x = Ksat_x*np.true_divide(psi,self.satpsi)**(-2-np.true_divide(3.,self.b)) 277 | K_x[~np.isfinite(K_x)] = np.nan 278 | #Calculate transmissivity at top layer (exponential decay) 279 | Ttop = m*K_x*np.exp(-ztop/m) 280 | #Calculate transmissivity at bottom of layer (exponential decay) 281 | Tbot = m*K_x*np.exp(-zbot/m) 282 | T = Ttop - Tbot 283 | 284 | return T 285 | 286 | def calculate_hydraulic_head(self,psi,depth): 287 | 288 | h = self.dem - depth - psi 289 | 290 | return h 291 | 292 | #def calculate_divergence_dense(self,h,K_x,dz): 293 | #@numba.jit(nopython=True,cache=True) 294 | def calculate_divergence_dense(self,h,T): 295 | 296 | #tic = time.time() 297 | dh = h[:,np.newaxis] - h[np.newaxis,:] 298 | w = self.w 299 | dx = self.dx 300 | area = self.area 301 | That = np.true_divide((2*T[:,np.newaxis]*T[np.newaxis,:]),(T[:,np.newaxis] + T[np.newaxis,:])) 302 | That[~np.isfinite(That)] = np.nan 303 | #[mm/s] = [mm/m]*[m/s]*[m]/[m]*[m]*[m]/[m2] 304 | calc_div = -1000.0*That*np.true_divide(dh,dx)*np.true_divide(w,area) # mm/s 305 | calc_div[~np.isfinite(calc_div)] = np.nan 306 | #print('calc_div',time.time() - tic) 307 | 308 | return calc_div 309 | 310 | #def calculate_divergence_sparse(self,h,K_x,dz): 311 | def calculate_divergence_sparse(self,h,T): 312 | 313 | #Define the boolean matrix (connections or not?) 314 | I = self.I 315 | #Calculate dh 316 | h1 = (I != 0).multiply(sparse.csr_matrix(h)) 317 | dh = h1.T - h1 318 | #Calculate dx 319 | d1 = (I != 0).multiply(sparse.csr_matrix(self.dem)) 320 | dx = d1.T - d1#**2 + self.dx**2) 321 | dx = dx.power(2) 322 | dx.data += self.dx**2 323 | dx = dx.power(0.5) 324 | #dx = (np.abs(d1.T - d1)**2 + self.dx**2)**0.5 325 | #dx = (np.abs(d1.T - d1)**2 + self.dx**2)**0.5 326 | #dx = (np.abs(self.dem[:,np.newaxis] - self.dem[np.newaxis,:])**2 + self.dx**2)**0.5 327 | #Calculate the effective hydraulic conductivity 328 | #k1 = (I != 0).multiply(sparse.csr_matrix(K_x)) 329 | #n = 2*k1.T.multiply(k1) 330 | #d = k1.T+k1 331 | #Khat = n.multiply(d.power(-1)) 332 | t1 = (I != 0).multiply(sparse.csr_matrix(T)) 333 | n = 2*t1.T.multiply(t1) 334 | d = t1.T+t1 335 | That = n.multiply(d.power(-1)).tocsr() 336 | print(That.count_nonzero,self.width.count_nonzero) 337 | #Calculate the flux 338 | #[m/s] = [m/s]*[m]/[m]*[m]*[m]/[m2] 339 | print(That.multiply(dh).shape) 340 | print(That.multiply(dh).multiply(self.width).count_nonzero) 341 | print(1.0/self.area) 342 | print(That.multiply(dh).multiply(self.width).multiply(1.0/self.area).count_nonzero) 343 | print(That.multiply(dh).multiply(self.width).multiply(1.0/self.area).multiply(dx.power(-1)).count_nonzero) 344 | return -That.multiply(dh).multiply(self.width).multiply(1.0/self.area).multiply(dx.power(-1)).multiply(1000) #mm/s 345 | #return -Khat.multiply(dh).multiply(self.width).multiply(dz/self.dx/self.area).multiply(1000) #mm/s 346 | 347 | def update(self): 348 | 349 | #Iterate per layer 350 | for il in range(self.theta.shape[1]): 351 | #Calculate soil moisture potential 352 | psi = self.calculate_soil_moisture_potential(il) 353 | zbot = np.sum(self.dz[:,0:il+1],axis=1) 354 | ztop = zbot - self.dz[:,il] 355 | T = self.calculate_transmissivity(psi,ztop,zbot) 356 | #Calculate hydraulic head 357 | h = self.calculate_hydraulic_head(psi,ztop) 358 | #Calculate the divergence 359 | q = self.calculate_divergence_dense(h,T) 360 | self.hdiv[:,il] = np.sum(q,axis=0) #mm/s 361 | 362 | return 363 | 364 | def update_numba(self,flag): 365 | 366 | theta = self.theta 367 | dz = self.dz 368 | hdiv = self.hdiv 369 | thetar = self.thetar 370 | thetas = self.thetas 371 | b = self.b 372 | satpsi = self.satpsi 373 | m = self.m 374 | ksat = self.ksat 375 | #hand = self.dem 376 | hand = self.dem1hband 377 | w = self.w 378 | dx = self.dx 379 | area = self.area 380 | ncsbasins=self.ncsbasins #laura, number of characteristic subbasins 381 | #self.hdiv[:] = update_workhorse(theta,dz,hdiv,thetar,thetas,b,satpsi,m,ksat,hand,w,dx,area) 382 | #Compute divergence independently per characteristic subbasin, laura 383 | aux=0 384 | div=np.empty(self.hdiv.shape) 385 | for bas in range(1,ncsbasins+1): 386 | w_bas=w['Basin%s' %bas] 387 | dx_bas=dx['Basin%s' %bas] 388 | init=aux 389 | fin=aux+w_bas.shape[0] 390 | if flag==False: 391 | div[init:fin,:]=update_workhorse(theta[init:fin,:],dz[init:fin,:],hdiv[init:fin,:],thetar[init:fin],thetas[init:fin],b[init:fin],satpsi[init:fin],m[init:fin],ksat[init:fin],hand[init:fin],w_bas,dx_bas,area[init:fin]) 392 | aux=fin 393 | else: 394 | div[init:fin,:]=update_workhorse_vsp(theta[init:fin,:],dz[init:fin,:],hdiv[init:fin,:],thetar[init:fin],thetas[init:fin],b[init:fin],satpsi[init:fin],m[init:fin],ksat[init:fin],hand[init:fin],w_bas,dx_bas,area[init:fin]) 395 | aux=fin #laura, added to fix flerchinger 396 | self.hdiv=div 397 | 398 | return 399 | 400 | @numba.jit(nopython=True,cache=True) 401 | def update_workhorse_vsp(theta,dz,hdiv,thetar,thetas,b,satpsi,m,ksat,hand,w,dx,area): 402 | 403 | #Iterate per layer 404 | for il in range(theta.shape[1]): 405 | #Calculate soil moisture potential 406 | psi = calculate_soil_moisture_potential(il,theta,thetar[:,il],thetas[:,il],b[:,il],satpsi[:,il]) #laura svp 407 | zbot = np.sum(dz[:,0:il+1],axis=1) 408 | ztop = zbot - dz[:,il] 409 | T = calculate_transmissivity(psi,ztop,zbot,m,ksat[:,il],satpsi[:,il],b[:,il])#laura svp 410 | #Calculate hydraulic head 411 | h = calculate_hydraulic_head(hand,psi,ztop) 412 | #Calculate the divergence 413 | q = calculate_divergence(h,T,w,dx,area) 414 | hdiv[:,il] = np.sum(q,axis=0) #mm/s''' 415 | 416 | return hdiv 417 | 418 | @numba.jit(nopython=True,cache=True) 419 | def update_workhorse(theta,dz,hdiv,thetar,thetas,b,satpsi,m,ksat,hand,w,dx,area): 420 | 421 | #Iterate per layer 422 | for il in range(theta.shape[1]): 423 | #Calculate soil moisture potential 424 | psi = calculate_soil_moisture_potential(il,theta,thetar,thetas,b,satpsi) 425 | zbot = np.sum(dz[:,0:il+1],axis=1) 426 | ztop = zbot - dz[:,il] 427 | T = calculate_transmissivity(psi,ztop,zbot,m,ksat,satpsi,b) 428 | #Calculate hydraulic head 429 | h = calculate_hydraulic_head(hand,psi,ztop) 430 | #Calculate the divergence 431 | q = calculate_divergence(h,T,w,dx,area) 432 | hdiv[:,il] = np.sum(q,axis=0) #mm/s''' 433 | 434 | return hdiv 435 | 436 | @numba.jit(nopython=True,cache=True) 437 | def calculate_soil_moisture_potential(il,theta,thetar,thetas,b,satpsi): 438 | 439 | eps = 0.01 440 | theta = theta[:,il] 441 | m = (theta <= (1+eps)*thetar) 442 | theta[m] = (1+eps)*thetar[m] 443 | psi = satpsi*((theta-thetar)/(thetas-thetar))**-b 444 | 445 | return psi 446 | 447 | @numba.jit(nopython=True,cache=True) 448 | def calculate_transmissivity(psi,ztop,zbot,m,ksat,satpsi,b): 449 | 450 | af = 1.0#10.0#2.0 451 | Ksat_x = af*ksat #lateral saturated hydraulic conductivity (multiply times anisotropy factor) [m/s] 452 | K_x = Ksat_x*np.true_divide(psi,satpsi)**(-2-np.true_divide(3.,b)) 453 | #Calculate transmissivity at top layer (exponential decay) 454 | Ttop = m*K_x*np.exp(-ztop/m) 455 | #Calculate transmissivity at bottom of layer (exponential decay) 456 | Tbot = m*K_x*np.exp(-zbot/m) 457 | T = Ttop - Tbot 458 | 459 | return T 460 | 461 | @numba.jit(nopython=True,cache=True) 462 | def calculate_hydraulic_head(hand,psi,depth): 463 | 464 | h = hand - depth - psi 465 | 466 | return h 467 | 468 | @numba.jit(nopython=True,cache=True) 469 | def calculate_divergence(h,T,w,dx,area): 470 | 471 | #Calculate dh 472 | #dh = h[:,np.newaxis] - h[np.newaxis,:] 473 | dh = calculate_dh(h) 474 | #Calculate That 475 | #That = np.true_divide((2*T[:,np.newaxis]*T[np.newaxis,:]),(T[:,np.newaxis] + T[np.newaxis,:])) 476 | That = calculate_That(T) 477 | #That[~np.isfinite(That)] = np.nan 478 | #[mm/s] = [mm/m]*[m/s]*[m]/[m]*[m]*[m]/[m2] 479 | calc_div = -1000.0*That*dh/dx*w/area # mm/s 480 | #calc_div[~np.isfinite(calc_div)] = np.nan 481 | 482 | return calc_div 483 | 484 | @numba.jit(nopython=True,cache=True) 485 | def calculate_dh(h): 486 | 487 | dh = np.zeros((h.size,h.size)) 488 | for i in range(h.size): 489 | for j in range(h.size): 490 | dh[i,j] = h[i] - h[j] 491 | #dh = h[:,np.newaxis] - h[np.newaxis,:] 492 | 493 | return dh 494 | 495 | @numba.jit(nopython=True,cache=True) 496 | def calculate_That(T): 497 | 498 | That = np.zeros((T.size,T.size)) 499 | for i in range(T.size): 500 | for j in range(T.size): 501 | That[i,j] = (2*T[i]*T[j])/(T[i] + T[j]) 502 | #That[i,j] = np.true_divide((2*T[:,np.newaxis]*T[np.newaxis,:]),(T[:,np.newaxis] + T[np.newaxis,:])) 503 | 504 | return That 505 | -------------------------------------------------------------------------------- /postprocessing/upscaling_python.py: -------------------------------------------------------------------------------- 1 | import netCDF4 as nc 2 | import glob 3 | import geospatialtools.netcdf_tools as netcdf_tools 4 | import numpy as np 5 | import os 6 | import geospatialtools.gdal_tools as gdal_tools 7 | import pickle 8 | import datetime 9 | import time 10 | import geospatialtools.upscaling_tools_fortran as upscaling_fortran 11 | import random 12 | import dateutil.relativedelta as relativedelta 13 | import rasterio 14 | 15 | def Create_Output_Files(metadata,rank,size,vars,startdate,enddate): 16 | 17 | #startdate = metadata['idate'] 18 | #enddate = metadata['fdate'] 19 | rdir = metadata['rdir'] 20 | workspace = '%s/experiments/simulations/%s/postprocess/workspace' % (rdir,metadata['experiment']) 21 | output_dir = '%s/experiments/simulations/%s/postprocess/output_dir' % (rdir,metadata['experiment']) 22 | #nstripes = info['nstripes'] 23 | #ncores = info['ncores'] 24 | #output_dir = info['output_dir'] 25 | date = startdate 26 | dt = 1 27 | #nt = (enddate - startdate).days + 1 28 | #nt_out = 24 29 | nt = 24*((enddate - startdate).days + 1) 30 | 31 | #Create dates array 32 | dates = [] 33 | date = startdate 34 | while date <= enddate: 35 | dates.append(date) 36 | date = date + relativedelta.relativedelta(days=1) 37 | dates = np.array(dates) 38 | 39 | #Define the dimensions 40 | file = '%s/experiments/simulations/%s/postprocess/mapping.tif' % (rdir,metadata['experiment']) 41 | metadata = gdal_tools.retrieve_metadata(file) 42 | #nlon_chunk = int(metadata['nx']/size**0.5)/2 43 | #nlat_chunk = int(metadata['ny']/size**0.5)/2 44 | nlon_chunk = int(metadata['nx']/10) 45 | nlat_chunk = int(metadata['ny']/10) 46 | #ntime_chunk = nt_out 47 | dims = {'nlat':metadata['ny'], 48 | 'nlon':metadata['nx'], 49 | 'res':metadata['resx'], 50 | 'minlon':metadata['minx'] + metadata['resx']/2, 51 | 'minlat':metadata['miny'] + metadata['resy']/2, 52 | 'undef':-9999.0, 53 | 'chunksize':(-9999,nlat_chunk,nlon_chunk)} 54 | 55 | for date in dates[rank::size]: 56 | 57 | print("Creating the file for ",date,flush=True) 58 | #Update the number of days 59 | dtt = relativedelta.relativedelta(days=1) 60 | nt_out = 24#((date + dtt) - date).days 61 | dims['chunksize'] = nt_out 62 | 63 | #Define the file 64 | ncfile = '%s/%04d%02d%02d.nc' % (output_dir,date.year,date.month,date.day) 65 | 66 | #Create the netcdf file 67 | #nlat = md['nlat'] 68 | md = {} 69 | md['file'] = ncfile 70 | md['vars'] = vars 71 | md['vars_info'] = vars 72 | md['tstep'] = '%dhr' % dt 73 | md['nt'] = nt_out 74 | md['nlat'] = dims['nlat'] 75 | md['nlon'] = dims['nlon'] 76 | md['res'] = dims['res'] 77 | md['minlon'] = dims['minlon'] 78 | md['minlat'] = dims['minlat'] 79 | md['undef'] = dims['undef'] 80 | md['chunksize'] = dims['chunksize'] 81 | md['tinitial'] = date 82 | md['tinitial_all'] = startdate 83 | #fp = netcdf_tools.Create_NETCDF_File(dims,ncfile,vars,vars,date,'%dday' % dt,nt_out) 84 | fp = netcdf_tools.Create_NETCDF_File(md) 85 | 86 | #Iterate through all the files 87 | files = glob.glob('%s/workspace/%04d%02d%02d/*.pck' % (output_dir,date.year,date.month,date.day)) 88 | for file in files: 89 | output = pickle.load(open(file,'rb')) 90 | ilats = output['coords']['ilats'] 91 | ilons = output['coords']['ilons'] 92 | vars_keys = output['vars'].keys() 93 | for var in vars_keys: 94 | fp.variables[var][:,ilats[0]:ilats[-1]+1,ilons[0]:ilons[-1]+1] = output['vars'][var] 95 | del output 96 | 97 | #Close the file 98 | fp.close() 99 | 100 | #Compress the file 101 | #print("Compressing the file for ",date,flush=True) 102 | #tmp = '%s/workspace/%04d%02d%02d/tmp.nc' % (output_dir,date.year,date.month,date.day) 103 | #os.system('nccopy -k 3 -d 4 %s %s' % (ncfile,tmp)) 104 | #os.system('mv %s %s' % (tmp,ncfile)) 105 | 106 | #Remove the workspace 107 | os.system('rm -rf %s/workspace/%04d%02d%02d' % (output_dir,date.year,date.month,date.day)) 108 | 109 | #Update the time step 110 | #date = date + datetime.timedelta(hours=nt_out) 111 | #date = date + relativedelta.relativedelta(days=nt_out) 112 | 113 | #Create the control file 114 | #if rank == 0: 115 | # tstep = '1dy' 116 | # file_template = '^%s.nc' % ('%y4%m2',) 117 | # ctl_file = '%s/hydrobloks_output.ctl' % output_dir 118 | # netcdf_tools.Update_Control_File('nc',startdate,dims,nt,tstep,file_template,ctl_file) 119 | 120 | return 121 | 122 | def Create_Virtual_Rasters(metadata): 123 | 124 | dir = metadata['dir'] 125 | output_dir = metadata['output_dir'] 126 | ncatch = metadata['ncatch'] 127 | 128 | #Create the virtual raster of the hsus 129 | os.system('rm -f %s/workspace/files.txt' % output_dir) 130 | files = [] 131 | for icatch in xrange(ncatch): 132 | #file = '%s/catch_%d/workspace/hsu_mapping_latlon.tif' % (dir,icatch) 133 | file = '%s/hru/%d.tif' % (dir,icatch) 134 | os.system('echo %s >> %s/files.txt' % (file,dir)) 135 | os.system('gdalbuildvrt -srcnodata -9999 -input_file_list %s/files.txt %s/hsu_map.vrt' % (dir,dir)) 136 | 137 | #Create the virtual raster of the catchment ids 138 | os.system('rm -f %s/workspace/files.txt' % output_dir) 139 | files = [] 140 | for icatch in xrange(ncatch): 141 | #file = '%s/catch_%d/workspace/icatch_latlon.tif' % (dir,icatch) 142 | file = '%s/cid/%d.tif' % (dir,icatch) 143 | os.system('echo %s >> %s/files.txt' % (file,output_dir)) 144 | os.system('gdalbuildvrt -srcnodata -9999 -input_file_list %s/files.txt %s/icatch_map.vrt' % (dir,dir)) 145 | 146 | return 147 | 148 | def Create_Finescale_Maps(metadata): 149 | 150 | dir = metadata['dir'] 151 | bbox = metadata['coverage']['bbox'] 152 | minlat = bbox['minlat'] 153 | maxlat = bbox['maxlat'] 154 | minlon = bbox['minlon'] 155 | maxlon = bbox['maxlon'] 156 | output_dir = metadata['output_dir'] 157 | #hsu map 158 | vrt_in = '%s/hru/hsu_map.vrt' % dir 159 | file_out = '%s/workspace/hsu_map_region.tif' % output_dir 160 | os.system('rm -f %s' % file_out) 161 | os.system('gdalwarp -te %f %f %f %f %s %s' % (minlon,minlat,maxlon,maxlat,vrt_in,file_out)) 162 | metadata['hru_map'] = file_out 163 | #cid map 164 | vrt_in = '%s/cid/icatch_map.vrt' % dir 165 | file_out = '%s/workspace/cid_map_region.tif' % output_dir 166 | os.system('rm -f %s' % file_out) 167 | os.system('gdalwarp -te %f %f %f %f %s %s' % (minlon,minlat,maxlon,maxlat,vrt_in,file_out)) 168 | metadata['cid_map'] = file_out 169 | 170 | return metadata 171 | 172 | def Create_Upscale_Template(metadata): 173 | 174 | res = metadata['upscaling']['res'] 175 | rdir = metadata['rdir'] 176 | file_cid = '%s/experiments/simulations/%s/postprocess/cids.vrt' % (rdir,metadata['experiment']) 177 | file = '%s/experiments/simulations/%s/postprocess/mapping.tif' % (rdir,metadata['experiment']) 178 | os.system('rm -rf %s' % file) 179 | #Create the upscaled grid -> summary per cell info 180 | os.system('gdalwarp %s -srcnodata -9999 -dstnodata -9999 -tr %.16f %.16f %s' % (file_cid,res,res,file)) 181 | 182 | return 183 | 184 | def Create_Upscale_Mapping(metadata,rank,bbox): 185 | 186 | rdir = metadata['rdir'] 187 | file_cid = '%s/experiments/simulations/%s/postprocess/cids.vrt' % (rdir,metadata['experiment']) 188 | file_hrus = '%s/experiments/simulations/%s/postprocess/hrus.vrt' % (rdir,metadata['experiment']) 189 | workspace = '%s/experiments/simulations/%s/postprocess/workspace' % (rdir,metadata['experiment']) 190 | os.system('mkdir -p %s' % workspace) 191 | 192 | 193 | #Read in the upscaled mapping 194 | lats_upscale = bbox['lats_upscale'] 195 | lons_upscale = bbox['lons_upscale'] 196 | ilats_upscale = bbox['ilats_upscale'] 197 | ilons_upscale = bbox['ilons_upscale'] 198 | res_upscale = bbox['res_upscale'] 199 | 200 | #Read in the fine scale catchment map 201 | lats_finescale = bbox['lats_finescale'] 202 | lons_finescale = bbox['lons_finescale'] 203 | ilats_finescale = bbox['ilats_finescale'] 204 | ilons_finescale = bbox['ilons_finescale'] 205 | res_finescale = bbox['res_finescale'] 206 | 207 | #Read in the fine scale data 208 | dims = {'nx':lons_finescale.size,'ny':lats_finescale.size, 209 | 'ixmin':ilons_finescale[0],'iymin':ilats_finescale[0]} 210 | #print(dims,ilons_finescale[0].dtype,ilats_finescale[0].dtype,lons_finescale.size,lats_finescale.size) 211 | #icatch_finescale = gdal_tools.read_raster_subarea(file_cid,dims) 212 | #hrus_finescale = gdal_tools.read_raster_subarea(file_hrus,dims) 213 | #window = rasterio.windows.Window(dims['iymin'],dims['ixmin'],dims['nx'],dims['ny']) 214 | window = rasterio.windows.Window(dims['ixmin'],dims['iymin'],dims['nx'],dims['ny']) 215 | icatch_finescale = rasterio.open(file_cid).read(1,window=window) 216 | hrus_finescale = rasterio.open(file_hrus).read(1,window=window) 217 | icatch_finescale[icatch_finescale < 0] = -9999 218 | hrus_finescale[icatch_finescale < 0] = -9999 219 | 220 | #Iterate through all upscaled cells and find their mappings 221 | lats_upscale = list(lats_upscale) 222 | lons_upscale = list(lons_upscale) 223 | print(rank,"Determining the mapping",flush=True) 224 | Output = {} 225 | icell = 0 226 | for lat in lats_upscale: 227 | ilat = lats_upscale.index(lat) 228 | #print "cell %d" % icell 229 | mask_lats = np.where((lats_finescale >= lats_upscale[ilat]-res_upscale/2) & 230 | (lats_finescale < lats_upscale[ilat]+res_upscale/2))[0] 231 | #for ilon in ilons_upscale[0:-1]: 232 | for lon in lons_upscale: 233 | ilon = lons_upscale.index(lon) 234 | #Determine the fine scale cells that fall into each coarse cell 235 | mask_lons = np.where((lons_finescale >= lons_upscale[ilon]-res_upscale/2) & 236 | (lons_finescale < lons_upscale[ilon]+res_upscale/2))[0] 237 | #Memorize the HRUs and their catchments for this coarse cell 238 | icatchs = icatch_finescale[mask_lats[0]:mask_lats[-1]+1,mask_lons[0]:mask_lons[-1]+1] 239 | hrus = hrus_finescale[mask_lats[0]:mask_lats[-1]+1,mask_lons[0]:mask_lons[-1]+1] 240 | #If the are no suitable candidates then skip the current cell 241 | mask_map = (icatchs >= 0) & (hrus >= 0) 242 | icatchs = icatchs[mask_map] 243 | hrus = hrus[mask_map] 244 | #icatchs = np.unique(icatchs[mask_map]) 245 | if icatchs.size != 0: 246 | #Iterate through the catchments 247 | ncells = icatchs.size 248 | info = {} 249 | for icatch in np.unique(icatchs).astype(np.int): 250 | mask_icatch = icatchs == icatch 251 | info[icatch] = {'hru':[],'pct':[]} 252 | for hru in np.unique(hrus[mask_icatch]).astype(np.int): 253 | mask_hru = hrus[mask_icatch] == hru 254 | pct = float(np.sum(mask_hru))/float(ncells) 255 | info[icatch]['hru'].append(hru) 256 | info[icatch]['pct'].append(pct) 257 | info[icatch]['hru'] = np.array(info[icatch]['hru']) 258 | info[icatch]['pct'] = np.array(info[icatch]['pct']) 259 | Output[icell] = {'coords':{'ilat_local':ilat,'ilon_local':ilon},'info':info} 260 | 261 | #Update the cell id 262 | icell += 1 263 | 264 | #Output the mapping 265 | print(rank,"Writing out the mapping info",flush=True) 266 | pickle.dump(Output,open('%s/mapping_%d.pck' % (workspace,rank),'wb'),pickle.HIGHEST_PROTOCOL) 267 | 268 | return 269 | 270 | def Map_Model_Output_Full(info,var,MPI): 271 | 272 | ncatch = info['ncatch'] 273 | nt_in = info['nt_in'] 274 | startdate = info['startdate'] 275 | enddate = info['enddate'] 276 | dt = info['dt'] 277 | output_dir = info['output_dir'] 278 | dir = info['dir'] 279 | nt_out = info['nt_out'] 280 | rank = MPI.COMM_WORLD.Get_rank() 281 | size = MPI.COMM_WORLD.Get_size() 282 | 283 | #Define the file 284 | file = '%s/%s_%d_%d.nc' % (output_dir,var,startdate.year,enddate.year) 285 | 286 | #Define the variables 287 | vars = [var,] 288 | 289 | #Create the netcdf file 290 | fp = h5py.File(file,'a',driver='mpio',comm=MPI.COMM_WORLD) 291 | 292 | #Open access to all the catchments 293 | fp_in = nc.Dataset('%s/catch_%d/of.nc' % (dir,0)) 294 | 295 | #Read in the mapping 296 | hmll = fp_in.groups['latlon_mapping'].variables['hmll'][:].astype(np.int32) 297 | tmp = np.zeros(hmll.shape) 298 | mask = hmll >= 0 299 | hmll = hmll[mask] 300 | 301 | #Iterate through time steps 302 | tmp[:] = -9999.0 303 | for itime in np.arange(info['nt_out'])[rank::size]: 304 | print(rank,itime) 305 | #Compute the map 306 | data = fp_in.groups['catchment'].variables[var][itime,:] 307 | tmp[mask] = data[hmll] 308 | #Write the data 309 | fp[var][itime,:,:] = np.flipud(tmp[:]) 310 | 311 | #Close the file 312 | fp.close() 313 | 314 | return 315 | 316 | def Map_Model_Output(metadata,vars,rank,bbox,startdate,enddate): 317 | 318 | '''ncatch = info['ncatch'] 319 | nt_in = info['nt_in'] 320 | startdate = info['startdate'] 321 | enddate = info['enddate'] 322 | dt = info['dt'] 323 | output_dir = info['output_dir'] 324 | dir = info['dir'] 325 | nt_out = info['nt_out']''' 326 | #startdate = metadata['idate'] 327 | #enddate = metadata['fdate'] 328 | #nt_in = metadata['nt_in'] 329 | #nt_out = metadata['nt_out'] 330 | rdir = metadata['rdir'] 331 | workspace = '%s/experiments/simulations/%s/postprocess/workspace' % (rdir,metadata['experiment']) 332 | output_dir = '%s/experiments/simulations/%s/postprocess/output_dir' % (rdir,metadata['experiment']) 333 | os.system('mkdir -p %s' % output_dir) 334 | file_cid = '%s/experiments/simulations/%s/postprocess/cids.vrt' % (rdir,metadata['experiment']) 335 | 336 | #Read in the mapping info 337 | print("Reading in the mapping info",flush=True) 338 | mapping = pickle.load(open('%s/mapping_%d.pck' % (workspace,rank),'rb')) 339 | 340 | #Determine the unique catchments in the box 341 | #Read in the fine scale data 342 | dims = {'nx':bbox['lons_finescale'].size,'ny':bbox['lats_finescale'].size, 343 | 'ixmin':bbox['ilons_finescale'][0],'iymin':bbox['ilats_finescale'][0]} 344 | #icatch_finescale = gdal_tools.read_raster_subarea(info['cid_map'],dims) 345 | #window = rasterio.windows.Window(dims['iymin'],dims['ixmin'],dims['ny'],dims['nx']) 346 | window = rasterio.windows.Window(dims['ixmin'],dims['iymin'],dims['nx'],dims['ny']) 347 | icatch_finescale = rasterio.open(file_cid).read(1,window=window) 348 | icatchs = np.unique(icatch_finescale[icatch_finescale >= 0]).astype(np.int) 349 | 350 | nlat = bbox['lats_upscale'].size#-1#metadata_upscale['ny'] 351 | nlon = bbox['lons_upscale'].size#-1#metadata_upscale['nx'] 352 | mask = np.zeros((nlat,nlon)) 353 | 354 | #Extract true location info 355 | ilats_upscale = bbox['ilats_upscale'] 356 | ilons_upscale = bbox['ilons_upscale'] 357 | ilats_upscale_flipped = bbox['ilats_upscale_flipped'] 358 | 359 | #Open access to all the catchments 360 | fps = {} 361 | for cid in icatchs: 362 | #file_output = '%s/catch_%d/output.nc' % (dir,icatch) 363 | file_output = '%s/experiments/simulations/%s/output_data/%d/%04d-%02d-%02d.nc' % (rdir,metadata['experiment'],cid,startdate.year,startdate.month,startdate.day) 364 | fps[cid] = nc.Dataset(file_output) 365 | 366 | #Determine nt_out 367 | nt_out = fps[cid]['data'].variables['trad'].shape[0] 368 | 369 | #Initialize the output 370 | output = {} 371 | for var in vars: 372 | output[var] = np.zeros((nt_out,nlat,nlon)) 373 | 374 | #Iterate through all the cachments 375 | print(rank,"Begin: Reading and preparing the output",time.ctime(),flush=True) 376 | for icatch in icatchs: 377 | #flag_catchment = True 378 | data_catchment = {} 379 | for var in vars: 380 | #try: 381 | #data_catchment[var] = fps[icatch].groups['catchment'].variables[var][:,:] 382 | data_catchment[var] = fps[icatch]['data'].variables['%s' % var][:,:] 383 | #except: 384 | # flag_catchment = False 385 | #if flag_catchment == False:continue 386 | 387 | #Iterate through all the cell ids 388 | #cid = 0 389 | cids = mapping.keys() 390 | for cid in cids: 391 | info = mapping[cid]['info'] 392 | ilat = mapping[cid]['coords']['ilat_local'] 393 | ilon = mapping[cid]['coords']['ilon_local'] 394 | if icatch in info: 395 | hrus = info[icatch]['hru'] 396 | pcts = info[icatch]['pct'] 397 | for var in vars: 398 | #data = np.sum(pcts*fps[icatch].groups['catchment'].variables[var][0:nt_in,hrus],axis=1) 399 | data = np.sum(pcts*data_catchment[var][:,hrus],axis=1) 400 | #Save the output 401 | output[var][:,ilat,ilon] += upscaling_fortran.time_average(data,data.size) 402 | #Add to the mask 403 | mask[ilat,ilon] += 1 404 | print(rank,"End: Reading and preparing the output",time.ctime(),flush=True) 405 | 406 | #Clear up the undefined 407 | for var in vars: 408 | output[var][:,mask == 0] = -9999.0 409 | 410 | #Create the dates array (monthly) 411 | '''dates_monthly = [] 412 | date = datetime.datetime(startdate.year,startdate.month,startdate.day,0,0,0) 413 | fdate = datetime.datetime(enddate.year,enddate.month,enddate.day,23,0,0) 414 | timedelta = relativedelta.relativedelta(months=1) 415 | while date <= fdate: 416 | dates_monthly.append(date) 417 | date = date + timedelta 418 | dates_monthly = np.array(dates_monthly)''' 419 | #Create the dates array (daily) 420 | dates_daily = [] 421 | timedelta = datetime.timedelta(hours=24) 422 | date = startdate 423 | while date <= enddate: 424 | dates_daily.append(date) 425 | date = date + timedelta 426 | dates_daily = np.array(dates_daily) 427 | #Create the dates array (hourly) 428 | dates_hourly = [] 429 | #timedelta = datetime.timedelta(hours=24) 430 | timedelta = datetime.timedelta(hours=1) 431 | date = startdate 432 | while date <= enddate: 433 | dates_hourly.append(date) 434 | date = date + timedelta 435 | dates_hourly = np.array(dates_hourly) 436 | #Write the data 437 | for var in output: 438 | output[var] = np.fliplr(output[var][:]) 439 | print(rank,"Begin: Writing the data",time.ctime()) 440 | #Define the file 441 | random.shuffle(dates_daily) 442 | timedelta = relativedelta.relativedelta(days=1) 443 | for date in dates_daily: 444 | daily_dir = '%s/workspace/%04d%02d%02d' % (output_dir,date.year,date.month,date.day) 445 | os.system('mkdir -p %s' % daily_dir) 446 | file = '%s/%d.pck' % (daily_dir,rank) 447 | #print date,date+timedelta 448 | #mask = (dates_daily >= date) & (dates_daily < date + timedelta) 449 | mask = (dates_hourly >= date) & (dates_hourly < date + timedelta) 450 | #output_daily = {'coords':{'ilats':ilats_upscale_flipped,'ilons':ilons_upscale}, 451 | output_hourly = {'coords':{'ilats':ilats_upscale_flipped,'ilons':ilons_upscale}, 452 | 'vars':{}} 453 | for var in vars: 454 | output_hourly['vars'][var] = output[var][mask,:,:] 455 | 456 | #Save the data 457 | pickle.dump(output_hourly,open(file,'wb'),pickle.HIGHEST_PROTOCOL) 458 | print(rank,"End: Writing the data",time.ctime()) 459 | 460 | return 461 | -------------------------------------------------------------------------------- /model/pyNoahMP/src/Noah/module_sf_noahmp_groundwater.F: -------------------------------------------------------------------------------- 1 | MODULE module_sf_noahmp_groundwater 2 | !=============================================================================== 3 | ! Module to calculate lateral groundwater flow and the flux between groundwater and rivers 4 | ! plus the routine to update soil moisture and water table due to those two fluxes 5 | ! according to the Miguez-Macho & Fan groundwater scheme (Miguez-Macho et al., JGR 2007). 6 | ! Module written by Gonzalo Miguez-Macho , U. de Santiago de Compostela, Galicia, Spain 7 | ! November 2012 8 | !=============================================================================== 9 | 10 | CONTAINS 11 | 12 | SUBROUTINE WTABLE_mmf_noahmp (NSOIL ,XLAND ,XICE ,XICE_THRESHOLD ,ISICE ,& !in 13 | ISLTYP ,SMOISEQ ,DZS ,WTDDT ,& !in 14 | FDEPTH ,AREA ,TOPO ,ISURBAN ,IVGTYP ,& !in 15 | RIVERCOND ,RIVERBED ,EQWTD ,PEXP ,& !in 16 | SMOIS ,SH2OXY ,SMCWTD ,WTD ,QRF ,& !inout 17 | DEEPRECH ,QSPRING ,QSLAT ,QRFS ,QSPRINGS ,RECH ,& !inout 18 | ids,ide, jds,jde, kds,kde, & 19 | ims,ime, jms,jme, kms,kme, & 20 | its,ite, jts,jte, kts,kte ) 21 | 22 | ! ---------------------------------------------------------------------- 23 | USE NOAHMP_TABLES, ONLY: BEXP_TABLE, DKSAT_TABLE, SMCMAX_TABLE,PSISAT_TABLE, SMCWLT_TABLE 24 | ! ---------------------------------------------------------------------- 25 | IMPLICIT NONE 26 | ! ---------------------------------------------------------------------- 27 | ! IN only 28 | 29 | INTEGER, INTENT(IN ) :: ids,ide, jds,jde, kds,kde, & 30 | & ims,ime, jms,jme, kms,kme, & 31 | & its,ite, jts,jte, kts,kte 32 | REAL, INTENT(IN) :: WTDDT 33 | REAL, INTENT(IN) :: XICE_THRESHOLD 34 | INTEGER, INTENT(IN ) :: ISICE 35 | REAL, DIMENSION( ims:ime, jms:jme ) , & 36 | & INTENT(IN ) :: XLAND, & 37 | XICE 38 | INTEGER, DIMENSION( ims:ime, jms:jme ) , & 39 | INTENT(IN ) :: ISLTYP, & 40 | IVGTYP 41 | INTEGER, INTENT(IN) :: nsoil 42 | INTEGER, INTENT(IN) :: ISURBAN 43 | REAL, DIMENSION( ims:ime , 1:nsoil, jms:jme ), & 44 | & INTENT(IN) :: SMOISEQ 45 | REAL, DIMENSION(1:nsoil), INTENT(IN) :: DZS 46 | REAL, DIMENSION( ims:ime, jms:jme ) , & 47 | & INTENT(IN) :: FDEPTH, & 48 | AREA, & 49 | TOPO, & 50 | EQWTD, & 51 | PEXP, & 52 | RIVERBED, & 53 | RIVERCOND 54 | 55 | ! IN and OUT 56 | 57 | REAL, DIMENSION( ims:ime , 1:nsoil, jms:jme ), & 58 | & INTENT(INOUT) :: SMOIS, & 59 | & SH2OXY 60 | 61 | 62 | REAL, DIMENSION( ims:ime, jms:jme ) , & 63 | & INTENT(INOUT) :: WTD, & 64 | SMCWTD, & 65 | DEEPRECH, & 66 | QSLAT, & 67 | QRFS, & 68 | QSPRINGS, & 69 | RECH 70 | 71 | !OUT 72 | 73 | REAL, DIMENSION( ims:ime, jms:jme ) , & 74 | & INTENT(OUT) :: QRF, & !groundwater - river water flux 75 | QSPRING !water springing at the surface from groundwater convergence in the column 76 | 77 | !LOCAL 78 | 79 | INTEGER :: I,J,K 80 | REAL, DIMENSION( 0:NSOIL) :: ZSOIL !depth of soil layer-bottom [m] 81 | REAL, DIMENSION( 1:NSOIL) :: SMCEQ !equilibrium soil water content [m3/m3] 82 | REAL, DIMENSION( 1:NSOIL) :: SMC,SH2O 83 | REAL :: DELTAT,RCOND,TOTWATER,PSI & 84 | ,WFLUXDEEP,WCNDDEEP,DDZ,SMCWTDMID & 85 | ,WPLUS,WMINUS 86 | REAL, DIMENSION( ims:ime, jms:jme ) :: QLAT 87 | INTEGER, DIMENSION( ims:ime, jms:jme ) :: LANDMASK !-1 for water (ice or no ice) and glacial areas, 1 for land where the LSM does its soil moisture calculations. 88 | 89 | REAL :: BEXP,DKSAT,PSISAT,SMCMAX,SMCWLT 90 | 91 | DELTAT = WTDDT * 60. !timestep in seconds for this calculation 92 | 93 | ZSOIL(0) = 0. 94 | ZSOIL(1) = -DZS(1) 95 | DO K = 2, NSOIL 96 | ZSOIL(K) = -DZS(K) + ZSOIL(K-1) 97 | END DO 98 | 99 | WHERE(XLAND-1.5.LT.0..AND.XICE.LT. XICE_THRESHOLD.AND.IVGTYP.NE.ISICE) 100 | LANDMASK=1 101 | ELSEWHERE 102 | LANDMASK=-1 103 | ENDWHERE 104 | 105 | !Calculate lateral flow 106 | 107 | QLAT = 0. 108 | CALL LATERALFLOW(ISLTYP,WTD,QLAT,FDEPTH,TOPO,LANDMASK,DELTAT,AREA & 109 | ,ids,ide,jds,jde,kds,kde & 110 | ,ims,ime,jms,jme,kms,kme & 111 | ,its,ite,jts,jte,kts,kte ) 112 | 113 | 114 | !compute flux from grounwater to rivers in the cell 115 | 116 | DO J=jts,jte 117 | DO I=its,ite 118 | IF(LANDMASK(I,J).GT.0)THEN 119 | IF(WTD(I,J) .GT. RIVERBED(I,J) .AND. EQWTD(I,J) .GT. RIVERBED(I,J)) THEN 120 | RCOND = RIVERCOND(I,J) * EXP(PEXP(I,J)*(WTD(I,J)-EQWTD(I,J))) 121 | ELSE 122 | RCOND = RIVERCOND(I,J) 123 | ENDIF 124 | QRF(I,J) = RCOND * (WTD(I,J)-RIVERBED(I,J)) * DELTAT/AREA(I,J) 125 | !for now, dont allow it to go from river to groundwater 126 | QRF(I,J) = MAX(QRF(I,J),0.) 127 | ELSE 128 | QRF(I,J) = 0. 129 | ENDIF 130 | ENDDO 131 | ENDDO 132 | 133 | 134 | DO J=jts,jte 135 | DO I=its,ite 136 | IF(LANDMASK(I,J).GT.0)THEN 137 | 138 | BEXP = BEXP_TABLE (ISLTYP(I,J)) 139 | DKSAT = DKSAT_TABLE (ISLTYP(I,J)) 140 | PSISAT = -1.0*PSISAT_TABLE (ISLTYP(I,J)) 141 | SMCMAX = SMCMAX_TABLE (ISLTYP(I,J)) 142 | SMCWLT = SMCWLT_TABLE (ISLTYP(I,J)) 143 | 144 | IF(IVGTYP(I,J)==ISURBAN)THEN 145 | SMCMAX = 0.45 146 | SMCWLT = 0.40 147 | ENDIF 148 | 149 | !for deep water table calculate recharge 150 | IF(WTD(I,J) < ZSOIL(NSOIL)-DZS(NSOIL))THEN 151 | !assume all liquid if the wtd is deep 152 | DDZ = ZSOIL(NSOIL)-WTD(I,J) 153 | SMCWTDMID = 0.5 * (SMCWTD(I,J) + SMCMAX ) 154 | PSI = PSISAT * ( SMCMAX / SMCWTD(I,J) ) ** BEXP 155 | WCNDDEEP = DKSAT * ( SMCWTDMID / SMCMAX ) ** (2.0*BEXP + 3.0) 156 | WFLUXDEEP = - DELTAT * WCNDDEEP * ( (PSISAT-PSI) / DDZ - 1.) 157 | !update deep soil moisture 158 | SMCWTD(I,J) = SMCWTD(I,J) + (DEEPRECH(I,J) - WFLUXDEEP) / DDZ 159 | WPLUS = MAX((SMCWTD(I,J)-SMCMAX), 0.0) * DDZ 160 | WMINUS = MAX((1.E-4-SMCWTD(I,J)), 0.0) * DDZ 161 | SMCWTD(I,J) = MAX( MIN(SMCWTD(I,J),SMCMAX) , 1.E-4) 162 | WFLUXDEEP = WFLUXDEEP + WPLUS - WMINUS 163 | DEEPRECH(I,J) = WFLUXDEEP 164 | ENDIF 165 | 166 | 167 | !Total water flux to or from groundwater in the cell 168 | TOTWATER = QLAT(I,J) - QRF(I,J) + DEEPRECH(I,J) 169 | 170 | SMC(1:NSOIL) = SMOIS(I,1:NSOIL,J) 171 | SH2O(1:NSOIL) = SH2OXY(I,1:NSOIL,J) 172 | SMCEQ(1:NSOIL) = SMOISEQ(I,1:NSOIL,J) 173 | 174 | !Update the water table depth and soil moisture 175 | CALL UPDATEWTD ( NSOIL, DZS , ZSOIL, SMCEQ, SMCMAX, SMCWLT, PSISAT, BEXP ,I , J , &!in 176 | TOTWATER, WTD(I,J), SMC, SH2O, SMCWTD(I,J) , &!inout 177 | QSPRING(I,J) ) !out 178 | 179 | !now update soil moisture 180 | SMOIS(I,1:NSOIL,J) = SMC(1:NSOIL) 181 | SH2OXY(I,1:NSOIL,J) = SH2O(1:NSOIL) 182 | 183 | ENDIF 184 | ENDDO 185 | ENDDO 186 | 187 | !accumulate fluxes for output 188 | 189 | DO J=jts,jte 190 | DO I=its,ite 191 | QSLAT(I,J) = QSLAT(I,J) + QLAT(I,J)*1.E3 192 | QRFS(I,J) = QRFS(I,J) + QRF(I,J)*1.E3 193 | QSPRINGS(I,J) = QSPRINGS(I,J) + QSPRING(I,J)*1.E3 194 | RECH(I,J) = RECH(I,J) + DEEPRECH(I,J)*1.E3 195 | !zero out DEEPRECH 196 | DEEPRECH(I,J) =0. 197 | ENDDO 198 | ENDDO 199 | 200 | 201 | END SUBROUTINE WTABLE_mmf_noahmp 202 | ! ================================================================================================== 203 | ! ---------------------------------------------------------------------- 204 | SUBROUTINE LATERALFLOW (ISLTYP,WTD,QLAT,FDEPTH,TOPO,LANDMASK,DELTAT,AREA & 205 | ,ids,ide,jds,jde,kds,kde & 206 | ,ims,ime,jms,jme,kms,kme & 207 | ,its,ite,jts,jte,kts,kte ) 208 | ! ---------------------------------------------------------------------- 209 | USE NOAHMP_TABLES, ONLY : DKSAT_TABLE 210 | ! ---------------------------------------------------------------------- 211 | IMPLICIT NONE 212 | ! ---------------------------------------------------------------------- 213 | ! input 214 | INTEGER, INTENT(IN ) :: ids,ide, jds,jde, kds,kde, & 215 | & ims,ime, jms,jme, kms,kme, & 216 | & its,ite, jts,jte, kts,kte 217 | REAL , INTENT(IN) :: DELTAT 218 | INTEGER, DIMENSION( ims:ime, jms:jme ), INTENT(IN) :: ISLTYP, LANDMASK 219 | REAL, DIMENSION( ims:ime, jms:jme ), INTENT(IN) :: FDEPTH,WTD,TOPO,AREA 220 | 221 | !output 222 | REAL, DIMENSION( ims:ime , jms:jme ), INTENT(OUT) :: QLAT 223 | 224 | !local 225 | INTEGER :: I, J, itsh,iteh,jtsh,jteh 226 | REAL :: Q,KLAT 227 | REAL, DIMENSION( ims:ime , jms:jme ) :: KCELL, HEAD 228 | 229 | REAL, DIMENSION(19) :: KLATFACTOR 230 | DATA KLATFACTOR /2.,3.,4.,10.,10.,12.,14.,20.,24.,28.,40.,48.,2.,0.,10.,0.,20.,2.,2./ 231 | 232 | REAL, PARAMETER :: PI = 3.14159265 233 | REAL, PARAMETER :: FANGLE = 0.22754493 ! = 0.5*sqrt(0.5*tan(pi/8)) 234 | 235 | itsh=max(its-1,ids) 236 | iteh=min(ite+1,ide-1) 237 | jtsh=max(jts-1,jds) 238 | jteh=min(jte+1,jde-1) 239 | 240 | 241 | DO J=jtsh,jteh 242 | DO I=itsh,iteh 243 | IF(FDEPTH(I,J).GT.0.)THEN 244 | KLAT = DKSAT_TABLE(ISLTYP(I,J)) * KLATFACTOR(ISLTYP(I,J)) 245 | IF(WTD(I,J) < -1.5)THEN 246 | KCELL(I,J) = FDEPTH(I,J) * KLAT * EXP( (WTD(I,J) + 1.5) / FDEPTH(I,J) ) 247 | ELSE 248 | KCELL(I,J) = KLAT * ( WTD(I,J) + 1.5 + FDEPTH(I,J) ) 249 | ENDIF 250 | ELSE 251 | KCELL(i,J) = 0. 252 | ENDIF 253 | 254 | HEAD(I,J) = TOPO(I,J) + WTD(I,J) 255 | ENDDO 256 | ENDDO 257 | 258 | itsh=max(its,ids+1) 259 | iteh=min(ite,ide-2) 260 | jtsh=max(jts,jds+1) 261 | jteh=min(jte,jde-2) 262 | 263 | DO J=jtsh,jteh 264 | DO I=itsh,iteh 265 | IF(LANDMASK(I,J).GT.0)THEN 266 | Q=0. 267 | 268 | Q = Q + (KCELL(I-1,J+1)+KCELL(I,J)) & 269 | * (HEAD(I-1,J+1)-HEAD(I,J))/SQRT(2.) 270 | 271 | Q = Q + (KCELL(I-1,J)+KCELL(I,J)) & 272 | * (HEAD(I-1,J)-HEAD(I,J)) 273 | 274 | Q = Q + (KCELL(I-1,J-1)+KCELL(I,J)) & 275 | * (HEAD(I-1,J-1)-HEAD(I,J))/SQRT(2.) 276 | 277 | Q = Q + (KCELL(I,J+1)+KCELL(I,J)) & 278 | * (HEAD(I,J+1)-HEAD(I,J)) 279 | 280 | Q = Q + (KCELL(I,J-1)+KCELL(I,J)) & 281 | * (HEAD(I,J-1)-HEAD(I,J)) 282 | 283 | Q = Q + (KCELL(I+1,J+1)+KCELL(I,J)) & 284 | * (HEAD(I+1,J+1)-HEAD(I,J))/SQRT(2.) 285 | 286 | Q = Q + (KCELL(I+1,J)+KCELL(I,J)) & 287 | * (HEAD(I+1,J)-HEAD(I,J)) 288 | 289 | Q = Q + (KCELL(I+1,J-1)+KCELL(I,J)) & 290 | * (HEAD(I+1,J-1)-HEAD(I,J))/SQRT(2.) 291 | 292 | 293 | QLAT(I,J) = FANGLE* Q * DELTAT / AREA(I,J) 294 | ENDIF 295 | ENDDO 296 | ENDDO 297 | 298 | 299 | END SUBROUTINE LATERALFLOW 300 | ! ================================================================================================== 301 | ! ---------------------------------------------------------------------- 302 | SUBROUTINE UPDATEWTD (NSOIL, DZS, ZSOIL ,SMCEQ ,& !in 303 | SMCMAX, SMCWLT, PSISAT, BEXP ,ILOC ,JLOC ,& !in 304 | TOTWATER, WTD ,SMC, SH2O ,SMCWTD ,& !inout 305 | QSPRING ) !out 306 | ! ---------------------------------------------------------------------- 307 | IMPLICIT NONE 308 | ! ---------------------------------------------------------------------- 309 | ! input 310 | INTEGER, INTENT(IN) :: NSOIL !no. of soil layers 311 | INTEGER, INTENT(IN) :: ILOC, JLOC 312 | REAL, INTENT(IN) :: SMCMAX 313 | REAL, INTENT(IN) :: SMCWLT 314 | REAL, INTENT(IN) :: PSISAT 315 | REAL, INTENT(IN) :: BEXP 316 | REAL, DIMENSION( 0:NSOIL), INTENT(IN) :: ZSOIL !depth of soil layer-bottom [m] 317 | REAL, DIMENSION( 1:NSOIL), INTENT(IN) :: SMCEQ !equilibrium soil water content [m3/m3] 318 | REAL, DIMENSION( 1:NSOIL), INTENT(IN) :: DZS ! soil layer thickness [m] 319 | ! input-output 320 | REAL , INTENT(INOUT) :: TOTWATER 321 | REAL , INTENT(INOUT) :: WTD 322 | REAL , INTENT(INOUT) :: SMCWTD 323 | REAL, DIMENSION( 1:NSOIL), INTENT(INOUT) :: SMC 324 | REAL, DIMENSION( 1:NSOIL), INTENT(INOUT) :: SH2O 325 | ! output 326 | REAL , INTENT(OUT) :: QSPRING 327 | !local 328 | INTEGER :: K 329 | INTEGER :: K1 330 | INTEGER :: IWTD 331 | INTEGER :: KWTD 332 | REAL :: MAXWATUP, MAXWATDW ,WTDOLD 333 | REAL :: WGPMID 334 | REAL :: SYIELDDW 335 | REAL :: DZUP 336 | REAL :: SMCEQDEEP 337 | REAL, DIMENSION( 1:NSOIL) :: SICE 338 | ! ------------------------------------------------------------- 339 | 340 | 341 | 342 | QSPRING=0. 343 | 344 | SICE = SMC - SH2O 345 | 346 | iwtd=1 347 | 348 | !case 1: totwater > 0 (water table going up): 349 | IF(totwater.gt.0.)then 350 | 351 | 352 | if(wtd.ge.zsoil(nsoil))then 353 | 354 | do k=nsoil-1,1,-1 355 | if(wtd.lt.zsoil(k))exit 356 | enddo 357 | iwtd=k 358 | kwtd=iwtd+1 359 | 360 | !max water that fits in the layer 361 | maxwatup=dzs(kwtd)*(smcmax-smc(kwtd)) 362 | 363 | if(totwater.le.maxwatup)then 364 | smc(kwtd) = smc(kwtd) + totwater / dzs(kwtd) 365 | smc(kwtd) = min(smc(kwtd),smcmax) 366 | if(smc(kwtd).gt.smceq(kwtd))wtd = min ( ( smc(kwtd)*dzs(kwtd) & 367 | - smceq(kwtd)*zsoil(iwtd) + smcmax*zsoil(kwtd) ) / & 368 | ( smcmax-smceq(kwtd) ) , zsoil(iwtd) ) 369 | totwater=0. 370 | else !water enough to saturate the layer 371 | smc(kwtd) = smcmax 372 | totwater=totwater-maxwatup 373 | k1=iwtd 374 | do k=k1,0,-1 375 | wtd = zsoil(k) 376 | iwtd=k-1 377 | if(k.eq.0)exit 378 | maxwatup=dzs(k)*(smcmax-smc(k)) 379 | if(totwater.le.maxwatup)then 380 | smc(k) = smc(k) + totwater / dzs(k) 381 | smc(k) = min(smc(k),smcmax) 382 | if(smc(k).gt.smceq(k))wtd = min ( ( smc(k)*dzs(k) & 383 | - smceq(k)*zsoil(iwtd) + smcmax*zsoil(k) ) / & 384 | ( smcmax-smceq(k) ) , zsoil(iwtd) ) 385 | totwater=0. 386 | exit 387 | else 388 | smc(k) = smcmax 389 | totwater=totwater-maxwatup 390 | endif 391 | 392 | enddo 393 | 394 | endif 395 | 396 | elseif(wtd.ge.zsoil(nsoil)-dzs(nsoil))then ! wtd below bottom of soil model 397 | 398 | !gmmequilibrium soil moisture content 399 | smceqdeep = smcmax * ( psisat / & 400 | (psisat - dzs(nsoil)) ) ** (1./bexp) 401 | ! smceqdeep = max(smceqdeep,smcwlt) 402 | smceqdeep = max(smceqdeep,1.E-4) 403 | 404 | maxwatup=(smcmax-smcwtd)*dzs(nsoil) 405 | 406 | if(totwater.le.maxwatup)then 407 | smcwtd = smcwtd + totwater / dzs(nsoil) 408 | smcwtd = min(smcwtd,smcmax) 409 | if(smcwtd.gt.smceqdeep)wtd = min( ( smcwtd*dzs(nsoil) & 410 | - smceqdeep*zsoil(nsoil) + smcmax*(zsoil(nsoil)-dzs(nsoil)) ) / & 411 | ( smcmax-smceqdeep ) , zsoil(nsoil) ) 412 | totwater=0. 413 | else 414 | smcwtd=smcmax 415 | totwater=totwater-maxwatup 416 | do k=nsoil,0,-1 417 | wtd=zsoil(k) 418 | iwtd=k-1 419 | if(k.eq.0)exit 420 | maxwatup=dzs(k)*(smcmax-smc(k)) 421 | if(totwater.le.maxwatup)then 422 | smc(k) = min(smc(k) + totwater / dzs(k),smcmax) 423 | if(smc(k).gt.smceq(k))wtd = min ( ( smc(k)*dzs(k) & 424 | - smceq(k)*zsoil(iwtd) + smcmax*zsoil(k) ) / & 425 | ( smcmax-smceq(k) ) , zsoil(iwtd) ) 426 | totwater=0. 427 | exit 428 | else 429 | smc(k) = smcmax 430 | totwater=totwater-maxwatup 431 | endif 432 | enddo 433 | endif 434 | 435 | !deep water table 436 | else 437 | 438 | maxwatup=(smcmax-smcwtd)*(zsoil(nsoil)-dzs(nsoil)-wtd) 439 | if(totwater.le.maxwatup)then 440 | wtd = wtd + totwater/(smcmax-smcwtd) 441 | totwater=0. 442 | else 443 | totwater=totwater-maxwatup 444 | wtd=zsoil(nsoil)-dzs(nsoil) 445 | maxwatup=(smcmax-smcwtd)*dzs(nsoil) 446 | if(totwater.le.maxwatup)then 447 | 448 | !gmmequilibrium soil moisture content 449 | smceqdeep = smcmax * ( psisat / & 450 | (psisat - dzs(nsoil)) ) ** (1./bexp) 451 | ! smceqdeep = max(smceqdeep,smcwlt) 452 | smceqdeep = max(smceqdeep,1.E-4) 453 | 454 | smcwtd = smcwtd + totwater / dzs(nsoil) 455 | smcwtd = min(smcwtd,smcmax) 456 | wtd = ( smcwtd*dzs(nsoil) & 457 | - smceqdeep*zsoil(nsoil) + smcmax*(zsoil(nsoil)-dzs(nsoil)) ) / & 458 | ( smcmax-smceqdeep ) 459 | totwater=0. 460 | else 461 | smcwtd=smcmax 462 | totwater=totwater-maxwatup 463 | do k=nsoil,0,-1 464 | wtd=zsoil(k) 465 | iwtd=k-1 466 | if(k.eq.0)exit 467 | maxwatup=dzs(k)*(smcmax-smc(k)) 468 | 469 | if(totwater.le.maxwatup)then 470 | smc(k) = smc(k) + totwater / dzs(k) 471 | smc(k) = min(smc(k),smcmax) 472 | if(smc(k).gt.smceq(k))wtd = ( smc(k)*dzs(k) & 473 | - smceq(k)*zsoil(iwtd) + smcmax*zsoil(k) ) / & 474 | ( smcmax-smceq(k) ) 475 | totwater=0. 476 | exit 477 | else 478 | smc(k) = smcmax 479 | totwater=totwater-maxwatup 480 | endif 481 | enddo 482 | endif 483 | endif 484 | endif 485 | 486 | !water springing at the surface 487 | qspring=totwater 488 | 489 | !case 2: totwater < 0 (water table going down): 490 | ELSEIF(totwater.lt.0.)then 491 | 492 | 493 | if(wtd.ge.zsoil(nsoil))then !wtd in the resolved layers 494 | 495 | do k=nsoil-1,1,-1 496 | if(wtd.lt.zsoil(k))exit 497 | enddo 498 | iwtd=k 499 | 500 | k1=iwtd+1 501 | do kwtd=k1,nsoil 502 | 503 | !max water that the layer can yield 504 | maxwatdw=dzs(kwtd)*(smc(kwtd)-max(smceq(kwtd),sice(kwtd))) 505 | 506 | if(-totwater.le.maxwatdw)then 507 | smc(kwtd) = smc(kwtd) + totwater / dzs(kwtd) 508 | if(smc(kwtd).gt.smceq(kwtd))then 509 | wtd = ( smc(kwtd)*dzs(kwtd) & 510 | - smceq(kwtd)*zsoil(iwtd) + smcmax*zsoil(kwtd) ) / & 511 | ( smcmax-smceq(kwtd) ) 512 | else 513 | wtd=zsoil(kwtd) 514 | iwtd=iwtd+1 515 | endif 516 | totwater=0. 517 | exit 518 | else 519 | wtd = zsoil(kwtd) 520 | iwtd=iwtd+1 521 | if(maxwatdw.ge.0.)then 522 | smc(kwtd) = smc(kwtd) + maxwatdw / dzs(kwtd) 523 | totwater = totwater + maxwatdw 524 | endif 525 | endif 526 | 527 | enddo 528 | 529 | if(iwtd.eq.nsoil.and.totwater.lt.0.)then 530 | !gmmequilibrium soil moisture content 531 | smceqdeep = smcmax * ( psisat / & 532 | (psisat - dzs(nsoil)) ) ** (1./bexp) 533 | ! smceqdeep = max(smceqdeep,smcwlt) 534 | smceqdeep = max(smceqdeep,1.E-4) 535 | 536 | maxwatdw=dzs(nsoil)*(smcwtd-smceqdeep) 537 | 538 | if(-totwater.le.maxwatdw)then 539 | 540 | smcwtd = smcwtd + totwater / dzs(nsoil) 541 | wtd = max( ( smcwtd*dzs(nsoil) & 542 | - smceqdeep*zsoil(nsoil) + smcmax*(zsoil(nsoil)-dzs(nsoil)) ) / & 543 | ( smcmax-smceqdeep ) , zsoil(nsoil)-dzs(nsoil) ) 544 | 545 | else 546 | 547 | wtd=zsoil(nsoil)-dzs(nsoil) 548 | smcwtd = smcwtd + totwater / dzs(nsoil) 549 | !and now even further down 550 | dzup=(smceqdeep-smcwtd)*dzs(nsoil)/(smcmax-smceqdeep) 551 | wtd=wtd-dzup 552 | smcwtd=smceqdeep 553 | 554 | endif 555 | 556 | endif 557 | 558 | 559 | 560 | elseif(wtd.ge.zsoil(nsoil)-dzs(nsoil))then 561 | 562 | !if wtd was already below the bottom of the resolved soil crust 563 | !gmmequilibrium soil moisture content 564 | smceqdeep = smcmax * ( psisat / & 565 | (psisat - dzs(nsoil)) ) ** (1./bexp) 566 | ! smceqdeep = max(smceqdeep,smcwlt) 567 | smceqdeep = max(smceqdeep,1.E-4) 568 | 569 | maxwatdw=dzs(nsoil)*(smcwtd-smceqdeep) 570 | 571 | if(-totwater.le.maxwatdw)then 572 | 573 | smcwtd = smcwtd + totwater / dzs(nsoil) 574 | wtd = max( ( smcwtd*dzs(nsoil) & 575 | - smceqdeep*zsoil(nsoil) + smcmax*(zsoil(nsoil)-dzs(nsoil)) ) / & 576 | ( smcmax-smceqdeep ) , zsoil(nsoil)-dzs(nsoil) ) 577 | 578 | else 579 | 580 | wtd=zsoil(nsoil)-dzs(nsoil) 581 | smcwtd = smcwtd + totwater / dzs(nsoil) 582 | !and now even further down 583 | dzup=(smceqdeep-smcwtd)*dzs(nsoil)/(smcmax-smceqdeep) 584 | wtd=wtd-dzup 585 | smcwtd=smceqdeep 586 | 587 | endif 588 | 589 | else 590 | !gmmequilibrium soil moisture content 591 | wgpmid = smcmax * ( psisat / & 592 | (psisat - (zsoil(nsoil)-wtd)) ) ** (1./bexp) 593 | ! wgpmid=max(wgpmid,smcwlt) 594 | wgpmid=max(wgpmid,1.E-4) 595 | syielddw=smcmax-wgpmid 596 | wtdold=wtd 597 | wtd = wtdold + totwater/syielddw 598 | !update wtdwgp 599 | smcwtd = (smcwtd*(zsoil(nsoil)-wtdold)+wgpmid*(wtdold-wtd) ) / (zsoil(nsoil)-wtd) 600 | 601 | endif 602 | 603 | qspring=0. 604 | 605 | ENDIF 606 | 607 | SH2O = SMC - SICE 608 | 609 | 610 | END SUBROUTINE UPDATEWTD 611 | 612 | ! ---------------------------------------------------------------------- 613 | 614 | END MODULE module_sf_noahmp_groundwater 615 | -------------------------------------------------------------------------------- /model/pyNoahMP/src/Utility_routines/module_date_utilities.F: -------------------------------------------------------------------------------- 1 | module Module_Date_utilities 2 | contains 3 | subroutine geth_newdate (ndate, odate, idt) 4 | implicit none 5 | 6 | ! From old date ("YYYY-MM-DD HH:MM:SS.ffff" or "YYYYMMDDHHMMSSffff") and 7 | ! delta-time, compute the new date. 8 | 9 | ! on entry - odate - the old hdate. 10 | ! idt - the change in time 11 | 12 | ! on exit - ndate - the new hdate. 13 | 14 | integer, intent(in) :: idt 15 | character (len=*), intent(out) :: ndate 16 | character (len=*), intent(in) :: odate 17 | 18 | ! Local Variables 19 | 20 | ! yrold - indicates the year associated with "odate" 21 | ! moold - indicates the month associated with "odate" 22 | ! dyold - indicates the day associated with "odate" 23 | ! hrold - indicates the hour associated with "odate" 24 | ! miold - indicates the minute associated with "odate" 25 | ! scold - indicates the second associated with "odate" 26 | 27 | ! yrnew - indicates the year associated with "ndate" 28 | ! monew - indicates the month associated with "ndate" 29 | ! dynew - indicates the day associated with "ndate" 30 | ! hrnew - indicates the hour associated with "ndate" 31 | ! minew - indicates the minute associated with "ndate" 32 | ! scnew - indicates the second associated with "ndate" 33 | 34 | ! mday - a list assigning the number of days in each month 35 | 36 | ! i - loop counter 37 | ! nday - the integer number of days represented by "idt" 38 | ! nhour - the integer number of hours in "idt" after taking out 39 | ! all the whole days 40 | ! nmin - the integer number of minutes in "idt" after taking out 41 | ! all the whole days and whole hours. 42 | ! nsec - the integer number of minutes in "idt" after taking out 43 | ! all the whole days, whole hours, and whole minutes. 44 | 45 | integer :: newlen, oldlen 46 | integer :: yrnew, monew, dynew, hrnew, minew, scnew, frnew 47 | integer :: yrold, moold, dyold, hrold, miold, scold, frold 48 | integer :: nday, nhour, nmin, nsec, nfrac, i, ifrc 49 | logical :: opass 50 | character (len=10) :: hfrc 51 | character (len=1) :: sp 52 | logical :: punct 53 | integer :: yrstart, yrend, mostart, moend, dystart, dyend 54 | integer :: hrstart, hrend, mistart, miend, scstart, scend, frstart 55 | integer :: units 56 | integer, dimension(12) :: mday = (/31,28,31,30,31,30,31,31,30,31,30,31/) 57 | 58 | ! Determine if odate is "YYYY-MM-DD_HH ... " or "YYYYMMDDHH...." 59 | if (odate(5:5) == "-") then 60 | punct = .TRUE. 61 | else 62 | punct = .FALSE. 63 | endif 64 | 65 | ! Break down old hdate into parts 66 | 67 | hrold = 0 68 | miold = 0 69 | scold = 0 70 | frold = 0 71 | oldlen = LEN(odate) 72 | if (punct) then 73 | yrstart = 1 74 | yrend = 4 75 | mostart = 6 76 | moend = 7 77 | dystart = 9 78 | dyend = 10 79 | hrstart = 12 80 | hrend = 13 81 | mistart = 15 82 | miend = 16 83 | scstart = 18 84 | scend = 19 85 | frstart = 21 86 | select case (oldlen) 87 | case (10) 88 | ! Days 89 | units = 1 90 | case (13) 91 | ! Hours 92 | units = 2 93 | case (16) 94 | ! Minutes 95 | units = 3 96 | case (19) 97 | ! Seconds 98 | units = 4 99 | case (21) 100 | ! Tenths 101 | units = 5 102 | case (22) 103 | ! Hundredths 104 | units = 6 105 | case (23) 106 | ! Thousandths 107 | units = 7 108 | case (24) 109 | ! Ten thousandths 110 | units = 8 111 | case default 112 | write(*,*) 'ERROR: geth_newdate: odd length: #'//trim(odate)//'#' 113 | stop 114 | end select 115 | 116 | if (oldlen.ge.11) then 117 | sp = odate(11:11) 118 | else 119 | sp = ' ' 120 | end if 121 | 122 | else 123 | 124 | yrstart = 1 125 | yrend = 4 126 | mostart = 5 127 | moend = 6 128 | dystart = 7 129 | dyend = 8 130 | hrstart = 9 131 | hrend = 10 132 | mistart = 11 133 | miend = 12 134 | scstart = 13 135 | scend = 14 136 | frstart = 15 137 | 138 | select case (oldlen) 139 | case (8) 140 | ! Days 141 | units = 1 142 | case (10) 143 | ! Hours 144 | units = 2 145 | case (12) 146 | ! Minutes 147 | units = 3 148 | case (14) 149 | ! Seconds 150 | units = 4 151 | case (15) 152 | ! Tenths 153 | units = 5 154 | case (16) 155 | ! Hundredths 156 | units = 6 157 | case (17) 158 | ! Thousandths 159 | units = 7 160 | case (18) 161 | ! Ten thousandths 162 | units = 8 163 | case default 164 | write(*,*) 'ERROR: geth_newdate: odd length: #'//trim(odate)//'#' 165 | stop 166 | end select 167 | endif 168 | 169 | ! Use internal READ statements to convert the CHARACTER string 170 | ! date into INTEGER components. 171 | 172 | read(odate(yrstart:yrend), '(i4)') yrold 173 | read(odate(mostart:moend), '(i2)') moold 174 | read(odate(dystart:dyend), '(i2)') dyold 175 | if (units.ge.2) then 176 | read(odate(hrstart:hrend),'(i2)') hrold 177 | if (units.ge.3) then 178 | read(odate(mistart:miend),'(i2)') miold 179 | if (units.ge.4) then 180 | read(odate(scstart:scend),'(i2)') scold 181 | if (units.ge.5) then 182 | read(odate(frstart:oldlen),*) frold 183 | end if 184 | end if 185 | end if 186 | end if 187 | 188 | ! Set the number of days in February for that year. 189 | 190 | mday(2) = nfeb(yrold) 191 | 192 | ! Check that ODATE makes sense. 193 | 194 | opass = .TRUE. 195 | 196 | ! Check that the month of ODATE makes sense. 197 | 198 | if ((moold.gt.12).or.(moold.lt.1)) then 199 | write(*,*) 'GETH_NEWDATE: Month of ODATE = ', moold 200 | opass = .FALSE. 201 | end if 202 | 203 | ! Check that the day of ODATE makes sense. 204 | 205 | if ((dyold.gt.mday(moold)).or.(dyold.lt.1)) then 206 | write(*,*) 'GETH_NEWDATE: Day of ODATE = ', dyold 207 | opass = .FALSE. 208 | end if 209 | 210 | ! Check that the hour of ODATE makes sense. 211 | 212 | if ((hrold.gt.23).or.(hrold.lt.0)) then 213 | write(*,*) 'GETH_NEWDATE: Hour of ODATE = ', hrold 214 | opass = .FALSE. 215 | end if 216 | 217 | ! Check that the minute of ODATE makes sense. 218 | 219 | if ((miold.gt.59).or.(miold.lt.0)) then 220 | write(*,*) 'GETH_NEWDATE: Minute of ODATE = ', miold 221 | opass = .FALSE. 222 | end if 223 | 224 | ! Check that the second of ODATE makes sense. 225 | 226 | if ((scold.gt.59).or.(scold.lt.0)) then 227 | write(*,*) 'GETH_NEWDATE: Second of ODATE = ', scold 228 | opass = .FALSE. 229 | end if 230 | 231 | ! Check that the fractional part of ODATE makes sense. 232 | 233 | !KWM IF ((scold.GT.59).or.(scold.LT.0)) THEN 234 | !KWM WRITE(*,*) 'GETH_NEWDATE: Second of ODATE = ', scold 235 | !KWM opass = .FALSE. 236 | !KWM END IF 237 | 238 | if (.not.opass) then 239 | write(*,*) 'Crazy ODATE: ', odate(1:oldlen), oldlen 240 | stop 241 | end if 242 | 243 | ! Date Checks are completed. Continue. 244 | 245 | 246 | ! Compute the number of days, hours, minutes, and seconds in idt 247 | 248 | if (units.ge.5) then !idt should be in fractions of seconds 249 | ifrc = oldlen-(frstart)+1 250 | ifrc = 10**ifrc 251 | nday = abs(idt)/(86400*ifrc) 252 | nhour = mod(abs(idt),86400*ifrc)/(3600*ifrc) 253 | nmin = mod(abs(idt),3600*ifrc)/(60*ifrc) 254 | nsec = mod(abs(idt),60*ifrc)/(ifrc) 255 | nfrac = mod(abs(idt), ifrc) 256 | else if (units.eq.4) then !idt should be in seconds 257 | ifrc = 1 258 | nday = abs(idt)/86400 ! integer number of days in delta-time 259 | nhour = mod(abs(idt),86400)/3600 260 | nmin = mod(abs(idt),3600)/60 261 | nsec = mod(abs(idt),60) 262 | nfrac = 0 263 | else if (units.eq.3) then !idt should be in minutes 264 | ifrc = 1 265 | nday = abs(idt)/1440 ! integer number of days in delta-time 266 | nhour = mod(abs(idt),1440)/60 267 | nmin = mod(abs(idt),60) 268 | nsec = 0 269 | nfrac = 0 270 | else if (units.eq.2) then !idt should be in hours 271 | ifrc = 1 272 | nday = abs(idt)/24 ! integer number of days in delta-time 273 | nhour = mod(abs(idt),24) 274 | nmin = 0 275 | nsec = 0 276 | nfrac = 0 277 | else if (units.eq.1) then !idt should be in days 278 | ifrc = 1 279 | nday = abs(idt) ! integer number of days in delta-time 280 | nhour = 0 281 | nmin = 0 282 | nsec = 0 283 | nfrac = 0 284 | else 285 | write(*,'(''GETH_NEWDATE: Strange length for ODATE: '', i3)') & 286 | oldlen 287 | write(*,*) '#'//odate(1:oldlen)//'#' 288 | stop 289 | end if 290 | 291 | if (idt.ge.0) then 292 | 293 | frnew = frold + nfrac 294 | if (frnew.ge.ifrc) then 295 | frnew = frnew - ifrc 296 | nsec = nsec + 1 297 | end if 298 | 299 | scnew = scold + nsec 300 | if (scnew .ge. 60) then 301 | scnew = scnew - 60 302 | nmin = nmin + 1 303 | end if 304 | 305 | minew = miold + nmin 306 | if (minew .ge. 60) then 307 | minew = minew - 60 308 | nhour = nhour + 1 309 | end if 310 | 311 | hrnew = hrold + nhour 312 | if (hrnew .ge. 24) then 313 | hrnew = hrnew - 24 314 | nday = nday + 1 315 | end if 316 | 317 | dynew = dyold 318 | monew = moold 319 | yrnew = yrold 320 | do i = 1, nday 321 | dynew = dynew + 1 322 | if (dynew.gt.mday(monew)) then 323 | dynew = dynew - mday(monew) 324 | monew = monew + 1 325 | if (monew .gt. 12) then 326 | monew = 1 327 | yrnew = yrnew + 1 328 | ! If the year changes, recompute the number of days in February 329 | mday(2) = nfeb(yrnew) 330 | end if 331 | end if 332 | end do 333 | 334 | else if (idt.lt.0) then 335 | 336 | frnew = frold - nfrac 337 | if (frnew .lt. 0) then 338 | frnew = frnew + ifrc 339 | nsec = nsec + 1 340 | end if 341 | 342 | scnew = scold - nsec 343 | if (scnew .lt. 00) then 344 | scnew = scnew + 60 345 | nmin = nmin + 1 346 | end if 347 | 348 | minew = miold - nmin 349 | if (minew .lt. 00) then 350 | minew = minew + 60 351 | nhour = nhour + 1 352 | end if 353 | 354 | hrnew = hrold - nhour 355 | if (hrnew .lt. 00) then 356 | hrnew = hrnew + 24 357 | nday = nday + 1 358 | end if 359 | 360 | dynew = dyold 361 | monew = moold 362 | yrnew = yrold 363 | do i = 1, nday 364 | dynew = dynew - 1 365 | if (dynew.eq.0) then 366 | monew = monew - 1 367 | if (monew.eq.0) then 368 | monew = 12 369 | yrnew = yrnew - 1 370 | ! If the year changes, recompute the number of days in February 371 | mday(2) = nfeb(yrnew) 372 | end if 373 | dynew = mday(monew) 374 | end if 375 | end do 376 | end if 377 | 378 | ! Now construct the new mdate 379 | 380 | newlen = LEN(ndate) 381 | 382 | if (punct) then 383 | 384 | if (newlen.gt.frstart) then 385 | write(ndate(1:scend),19) yrnew, monew, dynew, hrnew, minew, scnew 386 | write(hfrc,'(i10)') frnew+1000000000 387 | ndate = ndate(1:scend)//'.'//hfrc(31-newlen:10) 388 | 389 | else if (newlen.eq.scend) then 390 | write(ndate(1:scend),19) yrnew, monew, dynew, hrnew, minew, scnew 391 | 19 format(i4,'-',i2.2,'-',i2.2,'_',i2.2,':',i2.2,':',i2.2) 392 | 393 | else if (newlen.eq.miend) then 394 | write(ndate,16) yrnew, monew, dynew, hrnew, minew 395 | 16 format(i4,'-',i2.2,'-',i2.2,'_',i2.2,':',i2.2) 396 | 397 | else if (newlen.eq.hrend) then 398 | write(ndate,13) yrnew, monew, dynew, hrnew 399 | 13 format(i4,'-',i2.2,'-',i2.2,'_',i2.2) 400 | 401 | else if (newlen.eq.dyend) then 402 | write(ndate,10) yrnew, monew, dynew 403 | 10 format(i4,'-',i2.2,'-',i2.2) 404 | 405 | end if 406 | 407 | else 408 | 409 | if (newlen.gt.frstart) then 410 | write(ndate(1:scend),119) yrnew, monew, dynew, hrnew, minew, scnew 411 | write(hfrc,'(i10)') frnew+1000000000 412 | ndate = ndate(1:scend)//'.'//hfrc(31-newlen:10) 413 | 414 | else if (newlen.eq.scend) then 415 | write(ndate(1:scend),119) yrnew, monew, dynew, hrnew, minew, scnew 416 | 119 format(i4,i2.2,i2.2,i2.2,i2.2,i2.2) 417 | 418 | else if (newlen.eq.miend) then 419 | write(ndate,116) yrnew, monew, dynew, hrnew, minew 420 | 116 format(i4,i2.2,i2.2,i2.2,i2.2) 421 | 422 | else if (newlen.eq.hrend) then 423 | write(ndate,113) yrnew, monew, dynew, hrnew 424 | 113 format(i4,i2.2,i2.2,i2.2) 425 | 426 | else if (newlen.eq.dyend) then 427 | write(ndate,110) yrnew, monew, dynew 428 | 110 format(i4,i2.2,i2.2) 429 | 430 | end if 431 | 432 | endif 433 | 434 | if (punct .and. (oldlen.ge.11) .and. (newlen.ge.11)) ndate(11:11) = sp 435 | 436 | end subroutine geth_newdate 437 | 438 | subroutine geth_idts (newdate, olddate, idt) 439 | 440 | implicit none 441 | 442 | ! From 2 input mdates ('YYYY-MM-DD HH:MM:SS.ffff'), 443 | ! compute the time difference. 444 | 445 | ! on entry - newdate - the new hdate. 446 | ! olddate - the old hdate. 447 | 448 | ! on exit - idt - the change in time. 449 | ! Units depend on length of date strings. 450 | 451 | character (len=*) , intent(in) :: newdate, olddate 452 | integer , intent(out) :: idt 453 | 454 | 455 | ! Local Variables 456 | 457 | ! yrnew - indicates the year associated with "ndate" 458 | ! yrold - indicates the year associated with "odate" 459 | ! monew - indicates the month associated with "ndate" 460 | ! moold - indicates the month associated with "odate" 461 | ! dynew - indicates the day associated with "ndate" 462 | ! dyold - indicates the day associated with "odate" 463 | ! hrnew - indicates the hour associated with "ndate" 464 | ! hrold - indicates the hour associated with "odate" 465 | ! minew - indicates the minute associated with "ndate" 466 | ! miold - indicates the minute associated with "odate" 467 | ! scnew - indicates the second associated with "ndate" 468 | ! scold - indicates the second associated with "odate" 469 | ! i - loop counter 470 | ! mday - a list assigning the number of days in each month 471 | 472 | ! ndate, odate: local values of newdate and olddate 473 | character(len=24) :: ndate, odate 474 | 475 | integer :: oldlen, newlen 476 | integer :: yrnew, monew, dynew, hrnew, minew, scnew, frnew 477 | integer :: yrold, moold, dyold, hrold, miold, scold, frold 478 | integer :: i, newdys, olddys 479 | logical :: npass, opass 480 | integer :: timesign 481 | integer :: ifrc 482 | integer, dimension(12) :: mday = (/31,28,31,30,31,30,31,31,30,31,30,31/) 483 | logical :: punct 484 | integer :: yrstart, yrend, mostart, moend, dystart, dyend 485 | integer :: hrstart, hrend, mistart, miend, scstart, scend, frstart 486 | integer :: units 487 | 488 | oldlen = len(olddate) 489 | newlen = len(newdate) 490 | if (newlen.ne.oldlen) then 491 | write(*,'("GETH_IDTS: NEWLEN /= OLDLEN: ", A, 3x, A)') newdate(1:newlen), olddate(1:oldlen) 492 | stop 493 | endif 494 | 495 | if (olddate.gt.newdate) then 496 | timesign = -1 497 | 498 | ifrc = oldlen 499 | oldlen = newlen 500 | newlen = ifrc 501 | 502 | ndate = olddate 503 | odate = newdate 504 | else 505 | timesign = 1 506 | ndate = newdate 507 | odate = olddate 508 | end if 509 | 510 | ! Break down old hdate into parts 511 | 512 | ! Determine if olddate is punctuated or not 513 | if (odate(5:5) == "-") then 514 | punct = .TRUE. 515 | if (ndate(5:5) /= "-") then 516 | write(*,'("GETH_IDTS: Dates appear to be different formats: ", A, 3x, A)') & 517 | ndate(1:newlen), odate(1:oldlen) 518 | stop 519 | endif 520 | else 521 | punct = .FALSE. 522 | if (ndate(5:5) == "-") then 523 | write(*,'("GETH_IDTS: Dates appear to be different formats: ", A, 3x, A)') & 524 | ndate(1:newlen), odate(1:oldlen) 525 | stop 526 | endif 527 | endif 528 | 529 | if (punct) then 530 | yrstart = 1 531 | yrend = 4 532 | mostart = 6 533 | moend = 7 534 | dystart = 9 535 | dyend = 10 536 | hrstart = 12 537 | hrend = 13 538 | mistart = 15 539 | miend = 16 540 | scstart = 18 541 | scend = 19 542 | frstart = 21 543 | select case (oldlen) 544 | case (10) 545 | ! Days 546 | units = 1 547 | case (13) 548 | ! Hours 549 | units = 2 550 | case (16) 551 | ! Minutes 552 | units = 3 553 | case (19) 554 | ! Seconds 555 | units = 4 556 | case (21) 557 | ! Tenths 558 | units = 5 559 | case (22) 560 | ! Hundredths 561 | units = 6 562 | case (23) 563 | ! Thousandths 564 | units = 7 565 | case (24) 566 | ! Ten thousandths 567 | units = 8 568 | case default 569 | write(*,*) 'ERROR: geth_idts: odd length: #'//trim(odate)//'#' 570 | stop 571 | end select 572 | else 573 | 574 | yrstart = 1 575 | yrend = 4 576 | mostart = 5 577 | moend = 6 578 | dystart = 7 579 | dyend = 8 580 | hrstart = 9 581 | hrend = 10 582 | mistart = 11 583 | miend = 12 584 | scstart = 13 585 | scend = 14 586 | frstart = 15 587 | 588 | select case (oldlen) 589 | case (8) 590 | ! Days 591 | units = 1 592 | case (10) 593 | ! Hours 594 | units = 2 595 | case (12) 596 | ! Minutes 597 | units = 3 598 | case (14) 599 | ! Seconds 600 | units = 4 601 | case (15) 602 | ! Tenths 603 | units = 5 604 | case (16) 605 | ! Hundredths 606 | units = 6 607 | case (17) 608 | ! Thousandths 609 | units = 7 610 | case (18) 611 | ! Ten thousandths 612 | units = 8 613 | case default 614 | write(*,*) 'ERROR: geth_idts: odd length: #'//trim(odate)//'#' 615 | stop 616 | end select 617 | endif 618 | 619 | 620 | hrold = 0 621 | miold = 0 622 | scold = 0 623 | frold = 0 624 | 625 | read(odate(yrstart:yrend), '(i4)') yrold 626 | read(odate(mostart:moend), '(i2)') moold 627 | read(odate(dystart:dyend), '(i2)') dyold 628 | if (units.ge.2) then 629 | read(odate(hrstart:hrend),'(i2)') hrold 630 | if (units.ge.3) then 631 | read(odate(mistart:miend),'(i2)') miold 632 | if (units.ge.4) then 633 | read(odate(scstart:scend),'(i2)') scold 634 | if (units.ge.5) then 635 | read(odate(frstart:oldlen),*) frold 636 | end if 637 | end if 638 | end if 639 | end if 640 | 641 | ! Break down new hdate into parts 642 | 643 | hrnew = 0 644 | minew = 0 645 | scnew = 0 646 | frnew = 0 647 | 648 | read(ndate(yrstart:yrend), '(i4)') yrnew 649 | read(ndate(mostart:moend), '(i2)') monew 650 | read(ndate(dystart:dyend), '(i2)') dynew 651 | if (units.ge.2) then 652 | read(ndate(hrstart:hrend),'(i2)') hrnew 653 | if (units.ge.3) then 654 | read(ndate(mistart:miend),'(i2)') minew 655 | if (units.ge.4) then 656 | read(ndate(scstart:scend),'(i2)') scnew 657 | if (units.ge.5) then 658 | read(ndate(frstart:newlen),*) frnew 659 | end if 660 | end if 661 | end if 662 | end if 663 | 664 | ! Check that the dates make sense. 665 | 666 | npass = .true. 667 | opass = .true. 668 | 669 | ! Check that the month of NDATE makes sense. 670 | 671 | if ((monew.gt.12).or.(monew.lt.1)) then 672 | write(*,*) 'GETH_IDTS: Month of NDATE = ', monew 673 | npass = .false. 674 | end if 675 | 676 | ! Check that the month of ODATE makes sense. 677 | 678 | if ((moold.gt.12).or.(moold.lt.1)) then 679 | print*, 'GETH_IDTS: Month of ODATE = ', moold 680 | opass = .false. 681 | end if 682 | 683 | ! Check that the day of NDATE makes sense. 684 | 685 | if (monew.ne.2) then 686 | ! ...... For all months but February 687 | if ((dynew.gt.mday(monew)).or.(dynew.lt.1)) then 688 | print*, 'GETH_IDTS: Day of NDATE = ', dynew 689 | npass = .false. 690 | end if 691 | else if (monew.eq.2) then 692 | ! ...... For February 693 | if ((dynew > nfeb(yrnew)).or.(dynew < 1)) then 694 | print*, 'GETH_IDTS: Day of NDATE = ', dynew 695 | npass = .false. 696 | end if 697 | endif 698 | 699 | ! Check that the day of ODATE makes sense. 700 | 701 | if (moold.ne.2) then 702 | ! ...... For all months but February 703 | if ((dyold.gt.mday(moold)).or.(dyold.lt.1)) then 704 | print*, 'GETH_IDTS: Day of ODATE = ', dyold 705 | opass = .false. 706 | end if 707 | else if (moold.eq.2) then 708 | ! ....... For February 709 | if ((dyold > nfeb(yrold)).or.(dyold < 1)) then 710 | print*, 'GETH_IDTS: Day of ODATE = ', dyold 711 | opass = .false. 712 | end if 713 | end if 714 | 715 | ! Check that the hour of NDATE makes sense. 716 | 717 | if ((hrnew.gt.23).or.(hrnew.lt.0)) then 718 | print*, 'GETH_IDTS: Hour of NDATE = ', hrnew 719 | npass = .false. 720 | end if 721 | 722 | ! Check that the hour of ODATE makes sense. 723 | 724 | if ((hrold.gt.23).or.(hrold.lt.0)) then 725 | print*, 'GETH_IDTS: Hour of ODATE = ', hrold 726 | opass = .false. 727 | end if 728 | 729 | ! Check that the minute of NDATE makes sense. 730 | 731 | if ((minew.gt.59).or.(minew.lt.0)) then 732 | print*, 'GETH_IDTS: Minute of NDATE = ', minew 733 | npass = .false. 734 | end if 735 | 736 | ! Check that the minute of ODATE makes sense. 737 | 738 | if ((miold.gt.59).or.(miold.lt.0)) then 739 | print*, 'GETH_IDTS: Minute of ODATE = ', miold 740 | opass = .false. 741 | end if 742 | 743 | ! Check that the second of NDATE makes sense. 744 | 745 | if ((scnew.gt.59).or.(scnew.lt.0)) then 746 | print*, 'GETH_IDTS: SECOND of NDATE = ', scnew 747 | npass = .false. 748 | end if 749 | 750 | ! Check that the second of ODATE makes sense. 751 | 752 | if ((scold.gt.59).or.(scold.lt.0)) then 753 | print*, 'GETH_IDTS: Second of ODATE = ', scold 754 | opass = .false. 755 | end if 756 | 757 | if (.not. npass) then 758 | print*, 'Screwy NDATE: ', ndate(1:newlen) 759 | stop 760 | end if 761 | 762 | if (.not. opass) then 763 | print*, 'Screwy ODATE: ', odate(1:oldlen) 764 | stop 765 | end if 766 | 767 | ! Date Checks are completed. Continue. 768 | 769 | ! Compute number of days from 1 January ODATE, 00:00:00 until ndate 770 | ! Compute number of hours from 1 January ODATE, 00:00:00 until ndate 771 | ! Compute number of minutes from 1 January ODATE, 00:00:00 until ndate 772 | 773 | newdys = 0 774 | do i = yrold, yrnew - 1 775 | newdys = newdys + 337 + nfeb(i) 776 | end do 777 | 778 | if (monew .gt. 1) then 779 | mday(2) = nfeb(yrnew) 780 | do i = 1, monew - 1 781 | newdys = newdys + mday(i) 782 | end do 783 | mday(2) = 28 784 | end if 785 | 786 | newdys = newdys + dynew - 1 787 | 788 | ! Compute number of hours from 1 January ODATE, 00:00:00 until odate 789 | ! Compute number of minutes from 1 January ODATE, 00:00:00 until odate 790 | 791 | olddys = 0 792 | 793 | if (moold .gt. 1) then 794 | mday(2) = nfeb(yrold) 795 | do i = 1, moold - 1 796 | olddys = olddys + mday(i) 797 | end do 798 | mday(2) = 28 799 | end if 800 | 801 | olddys = olddys + dyold -1 802 | 803 | ! Determine the time difference 804 | 805 | idt = (newdys - olddys) 806 | if (units.ge.2) then 807 | idt = idt*24 + (hrnew - hrold) 808 | if (units.ge.3) then 809 | idt = idt*60 + (minew - miold) 810 | if (units.ge.4) then 811 | idt = idt*60 + (scnew - scold) 812 | if (units.ge.5) then 813 | ifrc = oldlen-(frstart-1) 814 | ifrc = 10**ifrc 815 | idt = idt * ifrc + (frnew-frold) 816 | endif 817 | endif 818 | endif 819 | endif 820 | 821 | if (timesign .eq. -1) then 822 | idt = idt * timesign 823 | end if 824 | 825 | end subroutine geth_idts 826 | 827 | 828 | integer function nfeb(year) 829 | ! 830 | ! Compute the number of days in February for the given year. 831 | ! 832 | implicit none 833 | integer, intent(in) :: year ! Four-digit year 834 | 835 | nfeb = 28 ! By default, February has 28 days ... 836 | if (mod(year,4).eq.0) then 837 | nfeb = 29 ! But every four years, it has 29 days ... 838 | if (mod(year,100).eq.0) then 839 | nfeb = 28 ! Except every 100 years, when it has 28 days ... 840 | if (mod(year,400).eq.0) then 841 | nfeb = 29 ! Except every 400 years, when it has 29 days ... 842 | if (mod(year,3600).eq.0) then 843 | nfeb = 28 ! Except every 3600 years, when it has 28 days. 844 | endif 845 | endif 846 | endif 847 | endif 848 | end function nfeb 849 | 850 | integer function nmdays(hdate) 851 | ! 852 | ! Compute the number of days in the month of given date hdate. 853 | ! 854 | implicit none 855 | character(len=*), intent(in) :: hdate 856 | 857 | integer :: year, month 858 | integer, dimension(12), parameter :: ndays = (/ 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 /) 859 | 860 | read(hdate(1:7), '(I4,1x,I2)') year, month 861 | 862 | if (month == 2) then 863 | nmdays = nfeb(year) 864 | else 865 | nmdays = ndays(month) 866 | endif 867 | end function nmdays 868 | 869 | function monthabbr_to_mm(mon) result(mm) 870 | implicit none 871 | 872 | character(len=3), intent(in) :: mon 873 | 874 | integer :: mm 875 | 876 | if (mon == "Jan") then 877 | mm = 1 878 | elseif (mon == "Feb") then 879 | mm = 2 880 | elseif (mon == "Mar") then 881 | mm = 3 882 | elseif (mon == "Apr") then 883 | mm = 4 884 | elseif (mon == "May") then 885 | mm = 5 886 | elseif (mon == "Jun") then 887 | mm = 6 888 | elseif (mon == "Jul") then 889 | mm = 7 890 | elseif (mon == "Aug") then 891 | mm = 8 892 | elseif (mon == "Sep") then 893 | mm = 9 894 | elseif (mon == "Oct") then 895 | mm = 10 896 | elseif (mon == "Nov") then 897 | mm = 11 898 | elseif (mon == "Dec") then 899 | mm = 12 900 | else 901 | write(*, '("Function monthabbr_to_mm: mon = <",A,">")') mon 902 | stop "Function monthabbr_to_mm: Unrecognized mon" 903 | endif 904 | end function monthabbr_to_mm 905 | 906 | subroutine swap_date_format(indate, outdate) 907 | implicit none 908 | character(len=*), intent(in) :: indate 909 | character(len=*), intent(out) :: outdate 910 | integer :: inlen 911 | 912 | inlen = len(indate) 913 | if (indate(5:5) == "-") then 914 | select case (inlen) 915 | case (10) 916 | ! YYYY-MM-DD 917 | outdate = indate(1:4)//indate(6:7)//indate(9:10) 918 | case (13) 919 | ! YYYY-MM-DD_HH 920 | outdate = indate(1:4)//indate(6:7)//indate(9:10)//indate(12:13) 921 | case (16) 922 | ! YYYY-MM-DD_HH:mm 923 | outdate = indate(1:4)//indate(6:7)//indate(9:10)//indate(12:13)//indate(15:16) 924 | case (19) 925 | ! YYYY-MM-DD_HH:mm:ss 926 | outdate = indate(1:4)//indate(6:7)//indate(9:10)//indate(12:13)//indate(15:16)//& 927 | indate(18:19) 928 | case (21,22,23,24) 929 | ! YYYY-MM-DD_HH:mm:ss.f[f[f[f]]] 930 | outdate = indate(1:4)//indate(6:7)//indate(9:10)//indate(12:13)//indate(15:16)//& 931 | indate(18:19)//indate(21:inlen) 932 | case default 933 | write(*,'("Unrecognized length: <", A,">")') indate 934 | stop 935 | end select 936 | else 937 | select case (inlen) 938 | case (8) 939 | ! YYYYMMDD 940 | outdate = indate(1:4)//"-"//indate(5:6)//"-"//indate(7:8) 941 | case (10) 942 | ! YYYYMMDDHH 943 | outdate = indate(1:4)//"-"//indate(5:6)//"-"//indate(7:8)//"_"//& 944 | indate(9:10) 945 | case (12) 946 | ! YYYYMMDDHHmm 947 | outdate = indate(1:4)//"-"//indate(5:6)//"-"//indate(7:8)//"_"//& 948 | indate(9:10)//":"//indate(11:12) 949 | case (14) 950 | ! YYYYMMDDHHmmss 951 | outdate = indate(1:4)//"-"//indate(5:6)//"-"//indate(7:8)//"_"//& 952 | indate(9:10)//":"//indate(11:12)//":"//indate(13:14) 953 | case (15,16,17,18) 954 | ! YYYYMMDDHHmmssf[f[f[f]]] 955 | outdate = indate(1:4)//"-"//indate(5:6)//"-"//indate(7:8)//"_"//& 956 | indate(9:10)//":"//indate(11:12)//":"//indate(13:14)//"."//indate(15:inlen) 957 | case default 958 | write(*,'("Unrecognized length: <", A,">")') indate 959 | stop 960 | end select 961 | endif 962 | 963 | end subroutine swap_date_format 964 | 965 | character(len=3) function mm_to_monthabbr(ii) result(mon) 966 | implicit none 967 | integer, intent(in) :: ii 968 | character(len=3), parameter, dimension(12) :: month = (/ & 969 | "Jan", "Feb", "Mar", "Apr", "May", "Jun", & 970 | "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" /) 971 | if (ii > 0 .and. ii < 13 ) then 972 | mon = month(ii) 973 | else 974 | stop "mm_to_monthabbr" 975 | endif 976 | end function mm_to_monthabbr 977 | 978 | end module Module_Date_utilities 979 | --------------------------------------------------------------------------------