Skip to content

Commit 0613d30

Browse files
committed
added file calculate_luh2_secondary_agedist.py
1 parent 5b35732 commit 0613d30

File tree

1 file changed

+275
-0
lines changed

1 file changed

+275
-0
lines changed
Lines changed: 275 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,275 @@
1+
#!/usr/bin/env python
2+
3+
import sys
4+
5+
import xarray as xr
6+
import numpy as np
7+
import matplotlib.pyplot as plt
8+
9+
from scipy.optimize import curve_fit
10+
from datetime import date
11+
12+
def piecewise_exponential_cumulative(ages, k1, k2, m=94):
13+
### this function calculates a piecewise exponential function, whose integral equals one
14+
# k1 is the decay rate of first exponential
15+
# k2 is decay rate of secodn exponetial
16+
# m is the boundary between k1 and k2
17+
# this function represents the steady-state cumulative age distribution for patch ages given harvest rates for
18+
# young and mature forests (k1 and k2) where m is the age that distinguishes the two age types
19+
n_age_bins = len(ages)
20+
21+
# calculate the frequency of youngest age bin
22+
a = 1./((1./k1)+np.exp(-k1*m)*((-1/k1)+(1/k2)))
23+
24+
# calculate the first piecewise exponential set
25+
vals = a * np.exp(-k1*ages)
26+
##print(len(vals[m:]))
27+
##print(len(np.arange(n_age_bins-m)))
28+
##print(len(ages.isel(slice(m-n_age_bins, None))))
29+
30+
# calculate the second piecewise exponential set, beginning with the last element ofthe first set
31+
vals[m:] = vals[m]*np.exp(-k2*np.arange(n_age_bins-m))
32+
33+
# rescale last age bin to account for all of the truncated ages beyond
34+
#vals[-1] = vals[-1]/k2
35+
36+
vals_cumulative = vals.cumsum()
37+
return(vals_cumulative)
38+
39+
# this is the year that LUH2 transient data starts
40+
year_luh2_start = 850
41+
42+
if len(sys.argv) < 4:
43+
print("Three command-line arguments needed: ")
44+
print("first is the path to the LUH data file as processed for FATES. ")
45+
print("second is the year for which to calculate steady-state.")
46+
print("third is a grid name (used only for output filename, e.g. ne30 or 4x5)")
47+
sys.exit(4)
48+
49+
#fin_luh2 = xr.open_dataset('LUH2_states_transitions_management.timeseries_4x5_hist_simyr1650-2015_c240216.nc')
50+
fin_luh2 = xr.open_dataset(sys.argv[1])
51+
52+
nages = 300
53+
ntime_total = len(fin_luh2.time)
54+
55+
# calculate the year that you want to calculate a steady-state logging rate for
56+
year_to_calc_steadystate = sys.argv[2]
57+
if (year_to_calc_steadystate < year_luh2_start) or (year_to_calc_steadystate > year_luh2_start + ntime_total):
58+
print("year_to_calc_steadystate must be within the time span of data file")
59+
print(year_to_calc_steadystate)
60+
sys.exit(4)
61+
62+
gridname = sys.argv[3]
63+
64+
65+
print(fin_luh2.data_vars)
66+
if "lon" in fin_luh2.coords or "lsmlon" in fin_luh2.coords:
67+
reg_grid = True
68+
IM = len(fin_luh2.lon)
69+
JM = len(fin_luh2.lat)
70+
if "lsmlon" in fin_luh2.dims:
71+
latname = "lsmlat"
72+
lonname = "lsmlon"
73+
lon_coord = fin_luh2.lsmlon
74+
lat_coord = fin_luh2.lsmlat
75+
else:
76+
latname = "lat"
77+
lonname = "lon"
78+
lon_coord = fin_luh2.lon
79+
lat_coord = fin_luh2.lat
80+
else:
81+
lsmlon = False
82+
reg_grid = False
83+
IM = len(fin_luh2.lndgrid)
84+
85+
ages = xr.DataArray(np.arange(nages), dims=['age'], coords=[np.arange(nages)])
86+
87+
age_mature=94
88+
89+
#print(fin_luh2.dims)
90+
91+
area_prim = fin_luh2.primf + fin_luh2.primn
92+
93+
rate_newsec_creation = fin_luh2.primf_harv + fin_luh2.primn_harv + fin_luh2.primf_to_secdn + fin_luh2.primn_to_secdf + fin_luh2.urban_to_secdf + fin_luh2.urban_to_secdn + fin_luh2.c3ann_to_secdf + fin_luh2.c3ann_to_secdn + fin_luh2.c4ann_to_secdf + fin_luh2.c4ann_to_secdn + fin_luh2.c3per_to_secdf + fin_luh2.c3per_to_secdn + fin_luh2.c4per_to_secdf + fin_luh2.c4per_to_secdn + fin_luh2.c3nfx_to_secdf + fin_luh2.c3nfx_to_secdn + fin_luh2.pastr_to_secdf + fin_luh2.pastr_to_secdn + fin_luh2.range_to_secdf + fin_luh2.range_to_secdn
94+
95+
rate_sec_loss_transitions = fin_luh2.secdf_to_urban + fin_luh2.secdf_to_c3ann + fin_luh2.secdf_to_c4ann + fin_luh2.secdf_to_c3per + fin_luh2.secdf_to_c4per + fin_luh2.secdf_to_c3nfx + fin_luh2.secdf_to_pastr + fin_luh2.secdf_to_range + fin_luh2.secdn_to_urban + fin_luh2.secdn_to_c3ann + fin_luh2.secdn_to_c4ann + fin_luh2.secdn_to_c3per + fin_luh2.secdn_to_c4per + fin_luh2.secdn_to_c3nfx + fin_luh2.secdn_to_pastr + fin_luh2.secdn_to_range
96+
97+
secmf_harv = fin_luh2.secmf_harv
98+
secyf_harv = fin_luh2.secyf_harv
99+
secnf_harv = fin_luh2.secnf_harv
100+
101+
sectot = fin_luh2.secdf + fin_luh2.secdn
102+
#print(sectot)
103+
#sys.exit(4)
104+
105+
if reg_grid:
106+
### initialize the secondary forest age as having all zero age at start of dataset
107+
age_dist = xr.DataArray(np.zeros((ntime_total,nages,JM,IM)), dims=['time','age',latname,lonname], coords=[fin_luh2.time, ages, lat_coord, lon_coord])
108+
age_dist[0,0,:,:] = sectot.isel(time=0)
109+
110+
generic_zeros_array = xr.DataArray(np.zeros((ntime_total,JM,IM)), dims=['time',latname,lonname], coords=[fin_luh2.time, lat_coord, lon_coord])
111+
else:
112+
### initialize the secondary forest age as having all zero age at start of dataset
113+
age_dist = xr.DataArray(np.zeros((ntime_total,nages, IM)), dims=['time','age','lndgrid'], coords=[fin_luh2.time, ages, fin_luh2.lndgrid])
114+
age_dist[0,0,:] = sectot.isel(time=0)
115+
generic_zeros_array = xr.DataArray(np.zeros((ntime_total, IM)), dims=['time','lndgrid'], coords=[fin_luh2.time, fin_luh2.lndgrid])
116+
117+
secy_dist = generic_zeros_array.copy()#xr.DataArray(np.zeros((ntime_total,JM,IM)), dims=['time',latname,lonname], coords=[fin_luh2.time, fin_luh2.lsmlat, fin_luh2.lsmlon])
118+
secm_dist = generic_zeros_array.copy()#xr.DataArray(np.zeros((ntime_total,JM,IM)), dims=['time',latname,lonname], coords=[fin_luh2.time, fin_luh2.lsmlat, fin_luh2.lsmlon])
119+
120+
k_secloss_dist = generic_zeros_array.copy()#xr.DataArray(np.zeros((ntime_total,JM,IM)), dims=['time',latname,lonname], coords=[fin_luh2.time, fin_luh2.lsmlat, fin_luh2.lsmlon])
121+
k_secyhar_dist = generic_zeros_array.copy()#xr.DataArray(np.zeros((ntime_total,JM,IM)), dims=['time',latname,lonname], coords=[fin_luh2.time, fin_luh2.lsmlat, fin_luh2.lsmlon])
122+
k_secthar_dist = generic_zeros_array.copy()#xr.DataArray(np.zeros((ntime_total,JM,IM)), dims=['time',latname,lonname], coords=[fin_luh2.time, fin_luh2.lsmlat, fin_luh2.lsmlon])
123+
k_secmhar_dist = generic_zeros_array.copy()#xr.DataArray(np.zeros((ntime_total,JM,IM)), dims=['time',latname,lonname], coords=[fin_luh2.time, fin_luh2.lsmlat, fin_luh2.lsmlon])
124+
125+
126+
for t in range(ntime_total-1):
127+
if t%50 == 0:
128+
print('starting year ',t)
129+
secy = age_dist.isel(time=t).isel(age=slice(0,age_mature)).sum(dim='age')
130+
secm = age_dist.isel(time=t).isel(age=slice(age_mature,None)).sum(dim='age')
131+
if reg_grid:
132+
age_dist[t+1,0,:,:] = rate_newsec_creation.isel(time=t)
133+
else:
134+
age_dist[t+1,0,:] = rate_newsec_creation.isel(time=t)
135+
k_secloss = np.minimum(1.,np.nan_to_num(rate_sec_loss_transitions.isel(time=t)/sectot.isel(time=t)))
136+
k_secyhar = np.minimum(1.,np.nan_to_num(secyf_harv.isel(time=t)/secy))
137+
k_secthar = np.minimum(1.,np.nan_to_num(secnf_harv.isel(time=t)/sectot.isel(time=t)))
138+
k_secmhar = np.minimum(1.,np.nan_to_num(secmf_harv.isel(time=t)/secm))
139+
140+
for age in range(age_mature):
141+
age_dist[t+1,age+1] = age_dist[t,age] * (1. - np.minimum(1.,(k_secloss + k_secyhar + k_secthar)))
142+
age_dist[t+1,0] = age_dist[t+1,0] + age_dist[t,age] * np.minimum(1.,(k_secyhar + k_secthar))
143+
for age in range(age_mature,nages-2):
144+
age_dist[t+1,age+1] = age_dist[t,age] * (1.- np.minimum(1.,(k_secloss + k_secmhar + k_secthar)))
145+
age_dist[t+1,0] = age_dist[t+1,0] + age_dist[t,age] * np.minimum(1.,(k_secmhar + k_secthar))
146+
age_dist[t+1,nages-1] = (age_dist[t,nages-1] + age_dist[t,nages-2]) * (1.- np.minimum(1.,(k_secloss + k_secmhar + k_secthar)))
147+
age_dist[t+1,0] = age_dist[t+1,0] + (age_dist[t,nages-1] + age_dist[t,nages-2]) * np.minimum(1.,(k_secmhar + k_secthar))
148+
149+
if reg_grid:
150+
secy_dist[t,:,:] = secy
151+
secm_dist[t,:,:] = secm
152+
k_secloss_dist[t,:,:] = k_secloss
153+
k_secyhar_dist[t,:,:] = k_secyhar
154+
k_secthar_dist[t,:,:] = k_secthar
155+
k_secmhar_dist[t,:,:] = k_secmhar
156+
else:
157+
secy_dist[t,:] = secy
158+
secm_dist[t,:] = secm
159+
k_secloss_dist[t,:] = k_secloss
160+
k_secyhar_dist[t,:] = k_secyhar
161+
k_secthar_dist[t,:] = k_secthar
162+
k_secmhar_dist[t,:] = k_secmhar
163+
164+
165+
age_dist_cum = age_dist.cumsum(dim='age')
166+
167+
168+
mean_age = (age_dist * ages).sum(dim='age') / age_dist.sum(dim='age')
169+
170+
171+
age_dist_cum_norm = age_dist_cum / sectot
172+
173+
174+
median_secondary_age = np.abs(age_dist_cum_norm.fillna(0.) - 0.5).argmin(dim='age', skipna=True)
175+
median_secondary_age = (median_secondary_age * sectot / sectot)
176+
177+
178+
if reg_grid:
179+
generic_nan_array = xr.DataArray(np.nan * np.ones([JM,IM]), dims=[latname,lonname], coords=[fin_luh2.lat, fin_luh2.lon])
180+
else:
181+
generic_nan_array = xr.DataArray(np.nan * np.ones([IM]), dims=['lndgrid'], coords=[fin_luh2.lndgrid])
182+
183+
sec_young_harvestrate_rel = generic_nan_array.copy()#xr.DataArray(np.nan * np.ones([JM,IM]), dims=[latname,lonname], coords=[fin_luh2.lat, fin_luh2.lon])
184+
sec_mature_harvestrate_rel = generic_nan_array.copy()#xr.DataArray(np.nan * np.ones([JM,IM]), dims=[latname,lonname], coords=[fin_luh2.lat, fin_luh2.lon])
185+
186+
187+
time = year_to_calc_steadystate - year_luh2_start
188+
n_age_max = 150
189+
190+
x = ages.isel(age=slice(0,n_age_max)).data
191+
fails = generic_nan_array.copy()#xr.DataArray(np.nan * np.ones([JM,IM]), dims=[latname,lonname], coords=[fin_luh2.lat, fin_luh2.lon])
192+
if reg_grid:
193+
for i in range(IM):
194+
for j in range(JM):
195+
if latname == "lsmlat":
196+
if sectot.sel(lsmlat=j,lsmlon=i,time=time) > 0.:
197+
y = age_dist_cum_norm.isel(lsmlat=j,lsmlon=i,time=time,age=slice(0,n_age_max)).data
198+
else:
199+
continue
200+
else:
201+
if sectot.isel(lat=j,lon=i,time=time) > 0.:
202+
y = age_dist_cum_norm.isel(lat=j,lon=i,time=time,age=slice(0,n_age_max)).data
203+
else:
204+
continue
205+
try:
206+
params = curve_fit(piecewise_exponential_cumulative, x, y, p0=[0.01,0.01], bounds=[0,1])
207+
#print(params)
208+
sec_young_harvestrate_rel[j,i] = params[0][0]
209+
sec_mature_harvestrate_rel[j,i] = params[0][1]
210+
except:
211+
fails[j,i] = 1.
212+
sec_young_harvestrate_rel[j,i] = 0.05 ### set to some nominal value
213+
sec_mature_harvestrate_rel[j,i] = 0.05 ### set to some nominal value
214+
else:
215+
for i in range(IM):
216+
if sectot.sel(lndgrid=i,time=time) > 0.:
217+
y = age_dist_cum_norm.isel(lndgrid=i,time=time,age=slice(0,n_age_max)).data
218+
try:
219+
params = curve_fit(piecewise_exponential_cumulative, x, y, p0=[0.01,0.01], bounds=[0,1])
220+
#print(params)
221+
sec_young_harvestrate_rel[i] = params[0][0]
222+
sec_mature_harvestrate_rel[i] = params[0][1]
223+
except:
224+
fails[i] = 1.
225+
sec_young_harvestrate_rel[i] = 0.05 ### set to some nominal value
226+
sec_mature_harvestrate_rel[i] = 0.05 ### set to some nominal value
227+
228+
229+
230+
sec_young_harvestrate_rel.plot()
231+
232+
233+
sec_mature_harvestrate_rel.plot()
234+
235+
236+
fails.plot()
237+
238+
239+
# replace nans with zeros in harvest rates
240+
sec_young_harvestrate_rel = sec_young_harvestrate_rel.fillna(0.)
241+
sec_mature_harvestrate_rel = sec_mature_harvestrate_rel.fillna(0.)
242+
243+
244+
n_ts_out = 500
245+
fout = fin_luh2.sel(time=[time]*n_ts_out)
246+
247+
248+
# first zero all transitions
249+
varlist = list(fout.variables)
250+
matchers = ['_to_','_harv','_bioh']
251+
matching_vars = [s for s in varlist if any(xs in s for xs in matchers)]
252+
#matching_vars
253+
254+
# Loop over variables governing transition rates and zero them
255+
for var in matching_vars:
256+
# Access the variable data
257+
fout[var] = fout[var] * 0.
258+
259+
260+
# now rewrite the secondary harvest rates based on the fits above
261+
if reg_grid:
262+
for i_ts in range(n_ts_out):
263+
fout.secyf_harv[i_ts,:,:] = sec_young_harvestrate_rel * (fout.secdf.isel(time=0) + fout.secdn.isel(time=0)).data
264+
fout.secmf_harv[i_ts,:,:] = sec_mature_harvestrate_rel * (fout.secdf.isel(time=0) + fout.secdn.isel(time=0)).data
265+
else:
266+
for i_ts in range(n_ts_out):
267+
fout.secyf_harv[i_ts,:] = sec_young_harvestrate_rel * (fout.secdf.isel(time=0) + fout.secdn.isel(time=0)).data
268+
fout.secmf_harv[i_ts,:] = sec_mature_harvestrate_rel * (fout.secdf.isel(time=0) + fout.secdn.isel(time=0)).data
269+
270+
fout['time'] = np.arange(n_ts_out)
271+
fout['YEAR'][:] = np.arange(n_ts_out)
272+
273+
fout.to_netcdf('LUH2_states_transitions_management.timeseries_'+gridname+'_hist_steadystate_'+str(year_to_calc_steadystate)+'_'+str(date.today())+'.nc')
274+
275+

0 commit comments

Comments
 (0)