Skip to content

Commit f079069

Browse files
authored
Merge pull request #314 from rgknox/rgknox-tools-pftindexswapper
Add python script for manipulating PFT parameter files
2 parents 426f492 + 6683715 commit f079069

File tree

2 files changed

+327
-0
lines changed

2 files changed

+327
-0
lines changed

tools/FatesPFTIndexSwapper.py

Lines changed: 225 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,225 @@
1+
#!/usr/bin/env python
2+
3+
# =======================================================================================
4+
#
5+
# This python script will open an input FATES parameter file, and given a list of PFT
6+
# indices supplied by the user, will create a new parameter file with PFTs entries cloned
7+
# from the original file as-per the list of indices supplied by the user.
8+
#
9+
# First Added, Ryan Knox: Thu Jan 11 13:36:14 PST 2018
10+
# =======================================================================================
11+
12+
import numpy as np
13+
import sys
14+
import getopt
15+
import code # For development: code.interact(local=locals())
16+
from datetime import datetime
17+
from scipy.io import netcdf
18+
import matplotlib.pyplot as plt
19+
20+
# =======================================================================================
21+
# Parameters
22+
# =======================================================================================
23+
24+
pft_dim_name = 'fates_pft'
25+
26+
27+
class timetype:
28+
29+
# This is time, like the thing that always goes forward and cant be seen
30+
# or touched, insert creative riddle here
31+
32+
def __init__(self,ntimes):
33+
34+
self.year = -9*np.ones((ntimes))
35+
self.month = -9*np.ones((ntimes))
36+
# This is a floating point decimal day
37+
self.day = -9.0*np.ones((ntimes))
38+
39+
# This is a decimal datenumber
40+
self.datenum = -9.0*np.ones((ntimes))
41+
42+
43+
def usage():
44+
print('')
45+
print('=======================================================================')
46+
print('')
47+
print(' python FatesPFTIndexSwapper.py -h --pft-indices <integer position> ')
48+
print(' --fin <netcdf-file-in> ')
49+
print(' --fout <netcdf-file-out>')
50+
print('')
51+
print('')
52+
print(' -h --help ')
53+
print(' print this help message')
54+
print('')
55+
print('')
56+
print(' --pft-indices <integer positions ie 1,1,2,3,5,7>')
57+
print(' This is a comma delimited list of integer positions of the PFTs')
58+
print(' to be copied into the new file. Note that first pft position')
59+
print(' is treated as 1 (not C or python like), and any order or multiples')
60+
print(' of indices can be chosen')
61+
print('')
62+
print('')
63+
print(' --fin <netcdf-file-in>')
64+
print(' This is the full path to the netcdf file you are basing off of')
65+
print('')
66+
print('')
67+
print(' --fout <netcdf-file-out>')
68+
print(' This is the full path to the netcdf file you are writing to.')
69+
print('')
70+
print('')
71+
print('=======================================================================')
72+
73+
74+
def interp_args(argv):
75+
76+
argv.pop(0) # The script itself is the first argument, forget it
77+
78+
# Name of the conversion file
79+
80+
input_fname = "none"
81+
output_fname = "none"
82+
donor_pft_indices = -9
83+
donot_pft_indices_str = ''
84+
try:
85+
opts, args = getopt.getopt(argv, 'h',["fin=","fout=","pft-indices="])
86+
except getopt.GetoptError as err:
87+
print('Argument error, see usage')
88+
usage()
89+
sys.exit(2)
90+
91+
for o, a in opts:
92+
if o in ("-h", "--help"):
93+
usage()
94+
sys.exit(0)
95+
elif o in ("--fin"):
96+
input_fname = a
97+
elif o in ("--fout"):
98+
output_fname = a
99+
elif o in ("--pft-indices"):
100+
donor_pft_indices_str = a.strip()
101+
else:
102+
assert False, "unhandled option"
103+
104+
105+
if (input_fname == "none"):
106+
print("You must specify an input file:\n\n")
107+
usage()
108+
sys.exit(2)
109+
110+
if (output_fname == "none"):
111+
print("You must specify an output file:\n\n")
112+
usage()
113+
sys.exit(2)
114+
115+
if (donor_pft_indices_str == ''):
116+
print("You must specify at least one donor pft index!\n\n")
117+
usage()
118+
sys.exit(2)
119+
else:
120+
donor_pft_indices = []
121+
for strpft in donor_pft_indices_str.split(','):
122+
donor_pft_indices.append(int(strpft))
123+
124+
125+
return (input_fname,output_fname,donor_pft_indices)
126+
127+
128+
# ========================================================================================
129+
# ========================================================================================
130+
# Main
131+
# ========================================================================================
132+
# ========================================================================================
133+
134+
def main(argv):
135+
136+
# Interpret the arguments to the script
137+
[input_fname,output_fname,donor_pft_indices] = interp_args(argv)
138+
139+
num_pft_out = len(donor_pft_indices)
140+
141+
# Open the netcdf files
142+
fp_out = netcdf.netcdf_file(output_fname, 'w')
143+
144+
fp_in = netcdf.netcdf_file(input_fname, 'r')
145+
146+
for key, value in sorted(fp_in.dimensions.iteritems()):
147+
if(key==pft_dim_name):
148+
fp_out.createDimension(key,int(num_pft_out))
149+
print('Creating Dimension: {}={}'.format(key,num_pft_out))
150+
else:
151+
fp_out.createDimension(key,int(value))
152+
print('Creating Dimension: {}={}'.format(key,value))
153+
154+
for key, value in sorted(fp_in.variables.iteritems()):
155+
print('Creating Variable: ',key)
156+
# code.interact(local=locals())
157+
158+
out_var = fp_out.createVariable(key,'f',(fp_in.variables.get(key).dimensions))
159+
in_var = fp_in.variables.get(key)
160+
out_var.units = in_var.units
161+
out_var.long_name = in_var.long_name
162+
163+
# Idenfity if this variable has pft dimension
164+
pft_dim_found = -1
165+
pft_dim_len = len(fp_in.variables.get(key).dimensions)
166+
167+
for idim, name in enumerate(fp_in.variables.get(key).dimensions):
168+
# Manipulate data
169+
if(name==pft_dim_name):
170+
pft_dim_found = idim
171+
172+
# Copy over the input data
173+
# Tedious, but I have to permute through all combinations of dimension position
174+
if( pft_dim_len == 0 ):
175+
out_var.assignValue(float(fp_in.variables.get(key).data))
176+
elif(pft_dim_found==-1):
177+
out_var[:] = in_var[:]
178+
elif( (pft_dim_found==0) & (pft_dim_len==1) ): # 1D fates_pft
179+
tmp_out = np.zeros([num_pft_out])
180+
for id,ipft in enumerate(donor_pft_indices):
181+
tmp_out[id] = fp_in.variables.get(key).data[ipft-1]
182+
out_var[:] = tmp_out
183+
184+
185+
elif( (pft_dim_found==1) & (pft_dim_len==2) ): # 2D hdyro_organ - fate_pft
186+
dim2_len = fp_in.dimensions.get(fp_in.variables.get(key).dimensions[0])
187+
tmp_out = np.zeros([dim2_len,num_pft_out])
188+
for id,ipft in enumerate(donor_pft_indices):
189+
for idim in range(0,dim2_len):
190+
tmp_out[idim,id] = fp_in.variables.get(key).data[idim,ipft-1]
191+
out_var[:] = tmp_out
192+
else:
193+
print('This variable has a dimensioning that we have not considered yet.')
194+
print('Please add this condition to the logic above this statement.')
195+
print('Aborting')
196+
exit(2)
197+
198+
fp_out.history = "This file was made from FatesPFTIndexSwapper.py \n Input File = {} \n Indices = {}"\
199+
.format(input_fname,donor_pft_indices)
200+
201+
#var_out.mode = var.mode
202+
#fp.flush()
203+
204+
fp_in.close()
205+
fp_out.close()
206+
207+
print('Cloneing complete!')
208+
exit(0)
209+
210+
211+
212+
213+
# =======================================================================================
214+
# This is the actual call to main
215+
216+
if __name__ == "__main__":
217+
main(sys.argv)
218+
219+
220+
221+
222+
223+
224+
225+

tools/modify_fates_paramfile.py

Lines changed: 102 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,102 @@
1+
#!/usr/bin/env python
2+
3+
#### this script modifies a FATES parameter file. It accepts the following flags
4+
# --var or --variable: variable.
5+
# --pft or --PFT: PFT number. If this is missing, script will assume that its a global variable that is being modified.
6+
# --input or --fin: input filename.
7+
# --output or --fout: output filename. If missing, will assume its directly modifying the input file, and will prompt unless -O is specified
8+
# --O or --overwrite: overwrite output file without asking.
9+
# --value or --val: value to put in variable
10+
# --s or --silent: don't write anything on successful execution.
11+
####
12+
13+
14+
# =======================================================================================
15+
# =======================================================================================
16+
17+
import numpy as np
18+
import os
19+
from scipy.io import netcdf as nc
20+
import argparse
21+
import shutil
22+
23+
# ========================================================================================
24+
# ========================================================================================
25+
# Main
26+
# ========================================================================================
27+
# ========================================================================================
28+
29+
def main():
30+
parser = argparse.ArgumentParser(description='Parse command line arguments to this script.')
31+
#
32+
parser.add_argument('--var','--variable', dest='varname', type=str, help="What variable to modify? Required.", required=True)
33+
parser.add_argument('--pft','--PFT', dest='pftnum', type=int, help="PFT number to modify. If this is missing, will assume a global variable.")
34+
parser.add_argument('--fin', '--input', dest='inputfname', type=str, help="Input filename. Required.", required=True)
35+
parser.add_argument('--fout','--output', dest='outputfname', type=str, help="Output filename. Required.", required=True)
36+
parser.add_argument('--val', '--value', dest='val', type=float, help="New value of PFT variable. Required.", required=True)
37+
parser.add_argument('--O','--overwrite', dest='overwrite', help="If present, automatically overwrite the output file.", action="store_true")
38+
parser.add_argument('--silent', '--s', dest='silent', help="prevent writing of output.", action="store_true")
39+
#
40+
args = parser.parse_args()
41+
# print(args.varname, args.pftnum, args.inputfname, args.outputfname, args.val, args.overwrite)
42+
#
43+
# check to see if output file exists
44+
if os.path.isfile(args.outputfname):
45+
if args.overwrite:
46+
if not args.silent:
47+
print('replacing file: '+args.outputfname)
48+
os.remove(args.outputfname)
49+
else:
50+
raise ValueError('Output file already exists and overwrite flag not specified for filename: '+args.outputfname)
51+
#
52+
shutil.copyfile(args.inputfname, args.outputfname)
53+
#
54+
ncfile = nc.netcdf_file(args.outputfname, 'a')
55+
#
56+
var = ncfile.variables[args.varname]
57+
#
58+
### check to make sure that, if a PFT is specified, the variable has a PFT dimension, and if not, then it doesn't. and also that shape is reasonable.
59+
ndim_file = len(var.dimensions)
60+
ispftvar = False
61+
for i in range(ndim_file):
62+
if var.dimensions[i] == 'fates_pft':
63+
ispftvar = True
64+
npft_file = var.shape[i]
65+
pftdim = 0
66+
else:
67+
npft_file = None
68+
pftdim = None
69+
if args.pftnum == None and ispftvar:
70+
raise ValueError('pft value is missing but variable has pft dimension.')
71+
if args.pftnum != None and not ispftvar:
72+
raise ValueError('pft value is present but variable does not have pft dimension.')
73+
if ndim_file > 1:
74+
raise ValueError('variable dimensionality is too high for this script')
75+
if ndim_file == 1 and not ispftvar:
76+
raise ValueError('variable dimensionality is too high for this script')
77+
if args.pftnum != None and ispftvar:
78+
if args.pftnum > npft_file:
79+
raise ValueError('PFT specified ('+str(args.pftnum)+') is larger than the number of PFTs in the file ('+str(npft_file)+').')
80+
if pftdim == 0:
81+
if not args.silent:
82+
print('replacing prior value of variable '+args.varname+', for PFT '+str(args.pftnum)+', which was '+str(var[args.pftnum-1])+', with new value of '+str(args.val))
83+
var[args.pftnum-1] = args.val
84+
elif args.pftnum == None and not ispftvar:
85+
if not args.silent:
86+
print('replacing prior value of variable '+args.varname+', which was '+str(var[:])+', with new value of '+str(args.val))
87+
var[:] = args.val
88+
else:
89+
raise ValueError('Nothing happened somehow.')
90+
#
91+
#
92+
ncfile.close()
93+
94+
95+
96+
97+
# =======================================================================================
98+
# This is the actual call to main
99+
100+
if __name__ == "__main__":
101+
main()
102+

0 commit comments

Comments
 (0)