-
Notifications
You must be signed in to change notification settings - Fork 92
/
EDPatchDynamicsMod.F90
3371 lines (2736 loc) · 169 KB
/
EDPatchDynamicsMod.F90
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
module EDPatchDynamicsMod
! ============================================================================
! Controls formation, creation, fusing and termination of patch level processes.
! ============================================================================
use FatesGlobals , only : fates_log
use FatesGlobals , only : FatesWarn,N2S,A2S
use FatesInterfaceTypesMod, only : hlm_freq_day
use FatesInterfaceTypesMod, only : hlm_current_tod
use EDPftvarcon , only : EDPftvarcon_inst
use EDPftvarcon , only : GetDecompyFrac
use PRTParametersMod , only : prt_params
use EDCohortDynamicsMod , only : fuse_cohorts, sort_cohorts, insert_cohort
use EDTypesMod , only : area_site => area
use ChecksBalancesMod , only : PatchMassStock
use FatesLitterMod , only : ncwd
use FatesLitterMod , only : ndcmpy
use FatesLitterMod , only : litter_type
use FatesConstantsMod , only : n_dbh_bins
use FatesLitterMod , only : adjust_SF_CWD_frac
use EDTypesMod , only : homogenize_seed_pfts
use EDTypesMod , only : area
use FatesConstantsMod , only : patchfusion_dbhbin_loweredges
use EDtypesMod , only : force_patchfuse_min_biomass
use EDTypesMod , only : ed_site_type
use FatesPatchMod, only : fates_patch_type
use FatesCohortMod , only : fates_cohort_type
use EDTypesMod , only : site_massbal_type
use EDTypesMod , only : site_fluxdiags_type
use EDTypesMod , only : min_patch_area
use EDTypesMod , only : min_patch_area_forced
use EDParamsMod , only : nclmax
use EDParamsMod , only : regeneration_model
use FatesInterfaceTypesMod, only : numpft
use FatesConstantsMod , only : dtype_ifall
use FatesConstantsMod , only : dtype_ilog
use FatesConstantsMod , only : dtype_ifire
use FatesConstantsMod , only : dtype_ilandusechange
use FatesConstantsMod , only : ican_upper
use PRTGenericMod , only : num_elements
use PRTGenericMod , only : element_list
use FatesLitterMod , only : lg_sf
use FatesLitterMod , only : dl_sf
use FatesConstantsMod , only : N_DIST_TYPES
use EDTypesMod , only : AREA_INV
use FatesConstantsMod , only : rsnbl_math_prec
use FatesConstantsMod , only : fates_tiny
use FatesConstantsMod , only : nocomp_bareground
use FatesInterfaceTypesMod , only : hlm_use_planthydro
use FatesInterfaceTypesMod , only : bc_in_type
use FatesInterfaceTypesMod , only : numpft
use FatesInterfaceTypesMod , only : hlm_stepsize
use FatesInterfaceTypesMod , only : hlm_use_sp
use FatesInterfaceTypesMod , only : hlm_use_nocomp
use FatesInterfaceTypesMod , only : hlm_use_fixed_biogeog
use FatesInterfaceTypesMod , only : hlm_num_lu_harvest_cats
use FatesInterfaceTypesMod , only : hlm_use_luh
use FatesInterfaceTypesMod , only : hlm_num_luh2_states
use FatesInterfaceTypesMod , only : hlm_num_luh2_transitions
use FatesGlobals , only : endrun => fates_endrun
use FatesConstantsMod , only : r8 => fates_r8
use FatesConstantsMod , only : itrue, ifalse
use FatesConstantsMod , only : t_water_freeze_k_1atm
use FatesConstantsMod , only : TRS_regeneration
use FatesPlantHydraulicsMod, only : InitHydrCohort
use FatesPlantHydraulicsMod, only : AccumulateMortalityWaterStorage
use FatesPlantHydraulicsMod, only : DeallocateHydrCohort
use EDLoggingMortalityMod, only : logging_litter_fluxes
use EDLoggingMortalityMod, only : logging_time
use EDLoggingMortalityMod, only : get_harvest_rate_area
use EDLoggingMortalityMod, only : get_harvest_rate_carbon
use EDLoggingMortalityMod, only : get_harvestable_carbon
use EDLoggingMortalityMod, only : get_harvest_debt
use EDParamsMod , only : fates_mortality_disturbance_fraction
use FatesAllometryMod , only : carea_allom
use FatesAllometryMod , only : set_root_fraction
use FatesConstantsMod , only : g_per_kg
use FatesConstantsMod , only : ha_per_m2
use FatesConstantsMod , only : days_per_sec
use FatesConstantsMod , only : years_per_day
use FatesConstantsMod , only : nearzero
use FatesConstantsMod , only : primaryland, secondaryland, pastureland, rangeland, cropland
use FatesConstantsMod , only : n_landuse_cats
use FatesLandUseChangeMod, only : get_landuse_transition_rates
use FatesConstantsMod , only : fates_unset_r8
use FatesConstantsMod , only : fates_unset_int
use FatesConstantsMod , only : hlm_harvest_carbon
use EDCohortDynamicsMod , only : InitPRTObject
use ChecksBalancesMod, only : SiteMassStock
use PRTGenericMod, only : carbon12_element
use PRTGenericMod, only : leaf_organ
use PRTGenericMod, only : fnrt_organ
use PRTGenericMod, only : sapw_organ
use PRTGenericMod, only : store_organ
use PRTGenericMod, only : repro_organ
use PRTGenericMod, only : struct_organ
use PRTLossFluxesMod, only : PRTBurnLosses
use FatesInterfaceTypesMod, only : hlm_parteh_mode
use PRTGenericMod, only : prt_carbon_allom_hyp
use PRTGenericMod, only : prt_cnp_flex_allom_hyp
use SFParamsMod, only : SF_VAL_CWD_FRAC
use EDParamsMod, only : logging_event_code
use EDParamsMod, only : logging_export_frac
use EDParamsMod, only : maxpatches_by_landuse
use FatesRunningMeanMod, only : ema_sdlng_mdd
use FatesRunningMeanMod, only : ema_sdlng_emerg_h2o, ema_sdlng_mort_par, ema_sdlng2sap_par
use FatesRunningMeanMod, only : ema_24hr, fixed_24hr, ema_lpa, ema_longterm
use FatesRadiationMemMod, only : num_swb
! CIME globals
use shr_infnan_mod , only : nan => shr_infnan_nan, assignment(=)
use shr_log_mod , only : errMsg => shr_log_errMsg
!
implicit none
private
public :: spawn_patches
public :: fuse_patches
public :: terminate_patches
public :: patch_pft_size_profile
public :: disturbance_rates
public :: check_patch_area
public :: set_patchno
private:: fuse_2_patches
public :: get_frac_site_primary
character(len=*), parameter, private :: sourcefile = &
__FILE__
logical, parameter :: debug = .false.
! When creating new patches from other patches, we need to send some of the
! litter from the old patch to the new patch. Likewise, when plants die
! from the disturbance, we need to send some amount from the old patch to
! the new patch. If the plant matter falls straight down, then that
! would make a case for all of the litter going to the new patch.
! This would be localization=1
! But if we think some of the plant matter drifts, or a tall tree lands
! outside of its gap, or are afraid of newly formed patches having
! too much burnable material, then we drop the localization from 1 down
! a notch.
! Note that in all cases, a localization of 0, suggests that litter
! is dispensed randomly in space among the area of the new and old
! patch combined. A localization of 1 suggests that
! all litter is sent to the new patch.
real(r8), parameter :: existing_litt_localization = 1.0_r8
real(r8), parameter :: treefall_localization = 0.0_r8
real(r8), parameter :: burn_localization = 0.0_r8
real(r8), parameter :: landusechange_localization = 1.0_r8
integer :: istat ! return status code
character(len=255) :: smsg ! Message string for deallocation errors
character(len=512) :: msg ! Message string for warnings and logging
! 10/30/09: Created by Rosie Fisher
! ============================================================================
contains
! ============================================================================
subroutine disturbance_rates( site_in, bc_in)
!
! !DESCRIPTION:
! Calculates the fire and mortality related disturbance rates for each patch,
! and then determines which is the larger at the patch scale (for now, there an only
! be one disturbance type for each timestep.
! all disturbance rates here are per daily timestep.
! 2016-2017
! Modify to add logging disturbance
! !USES:
use EDMortalityFunctionsMod , only : mortality_rates
use EDMortalityFunctionsMod , only : ExemptTreefallDist
! loging flux
use EDLoggingMortalityMod , only : LoggingMortality_frac
! !ARGUMENTS:
type(ed_site_type) , intent(inout) :: site_in
type(bc_in_type) , intent(in) :: bc_in
!
! !LOCAL VARIABLES:
type (fates_patch_type) , pointer :: currentPatch
type (fates_cohort_type), pointer :: currentCohort
real(r8) :: cmort
real(r8) :: bmort
real(r8) :: hmort
real(r8) :: frmort
real(r8) :: smort
real(r8) :: asmort
real(r8) :: dgmort
real(r8) :: lmort_direct
real(r8) :: lmort_collateral
real(r8) :: lmort_infra
real(r8) :: l_degrad ! fraction of trees that are not killed but suffer from forest
! degradation (i.e. they are moved to newly-anthro-disturbed
! secondary forest patch)
real(r8) :: dist_rate_ldist_notharvested
integer :: threshold_sizeclass
integer :: i_dist
integer :: h_index
real(r8) :: frac_site_primary
real(r8) :: harvest_rate
real(r8) :: tempsum
real(r8) :: mean_temp
real(r8) :: harvestable_forest_c(hlm_num_lu_harvest_cats)
integer :: harvest_tag(hlm_num_lu_harvest_cats)
real(r8) :: landuse_transition_matrix(n_landuse_cats, n_landuse_cats) ! [m2/m2/day]
real(r8) :: current_fates_landuse_state_vector(n_landuse_cats) ! [m2/m2]
!----------------------------------------------------------------------------------------------
! Calculate Mortality Rates (these were previously calculated during growth derivatives)
! And the same rates in understory plants have already been applied to %dndt
!----------------------------------------------------------------------------------------------
! first calculate the fraction of the site that is primary land
call get_frac_site_primary(site_in, frac_site_primary)
! get available biomass for harvest for all patches
call get_harvestable_carbon(site_in, bc_in%site_area, bc_in%hlm_harvest_catnames, harvestable_forest_c)
currentPatch => site_in%oldest_patch
do while (associated(currentPatch))
currentCohort => currentPatch%shortest
do while(associated(currentCohort))
! Mortality for trees in the understorey.
!currentCohort%patchptr => currentPatch
mean_temp = currentPatch%tveg24%GetMean()
call mortality_rates(currentCohort,bc_in,currentPatch%btran_ft, &
mean_temp, cmort,hmort,bmort,frmort,smort,asmort,dgmort)
currentCohort%dmort = cmort+hmort+bmort+frmort+smort+asmort+dgmort
call carea_allom(currentCohort%dbh,currentCohort%n,site_in%spread,currentCohort%pft, &
currentCohort%crowndamage,currentCohort%c_area)
! Initialize diagnostic mortality rates
currentCohort%cmort = cmort
currentCohort%bmort = bmort
currentCohort%hmort = hmort
currentCohort%frmort = frmort
currentCohort%smort = smort
currentCohort%asmort = asmort
currentCohort%dgmort = dgmort
call LoggingMortality_frac(currentCohort%pft, currentCohort%dbh, currentCohort%canopy_layer, &
lmort_direct,lmort_collateral,lmort_infra,l_degrad,&
bc_in%hlm_harvest_rates, &
bc_in%hlm_harvest_catnames, &
bc_in%hlm_harvest_units, &
currentPatch%land_use_label, &
currentPatch%age_since_anthro_disturbance, &
frac_site_primary, &
harvestable_forest_c, &
harvest_tag)
currentCohort%lmort_direct = lmort_direct
currentCohort%lmort_collateral = lmort_collateral
currentCohort%lmort_infra = lmort_infra
currentCohort%l_degrad = l_degrad
currentCohort => currentCohort%taller
end do
currentPatch => currentPatch%younger
end do
call get_harvest_debt(site_in, bc_in, harvest_tag)
if ( hlm_use_luh .eq. itrue ) then
call get_landuse_transition_rates(bc_in, landuse_transition_matrix)
else
landuse_transition_matrix(:,:) = 0._r8
endif
! calculate total area in each landuse category
current_fates_landuse_state_vector(:) = 0._r8
currentPatch => site_in%oldest_patch
do while (associated(currentPatch))
current_fates_landuse_state_vector(currentPatch%land_use_label) = &
current_fates_landuse_state_vector(currentPatch%land_use_label) + &
currentPatch%area/AREA
currentPatch => currentPatch%younger
end do
! ---------------------------------------------------------------------------------------------
! Calculate Disturbance Rates based on the mortality rates just calculated
! ---------------------------------------------------------------------------------------------
! Recalculate total canopy area prior to resolving the disturbance
currentPatch => site_in%oldest_patch
do while (associated(currentPatch))
currentPatch%total_canopy_area = 0._r8
currentCohort => currentPatch%shortest
do while(associated(currentCohort))
if(currentCohort%canopy_layer==1)then
currentPatch%total_canopy_area = currentPatch%total_canopy_area + currentCohort%c_area
end if
currentCohort => currentCohort%taller
end do
currentPatch => currentPatch%younger
end do
currentPatch => site_in%oldest_patch
do while (associated(currentPatch))
currentPatch%disturbance_rates(dtype_ifall) = 0.0_r8
currentPatch%disturbance_rates(dtype_ilog) = 0.0_r8
currentPatch%disturbance_rates(dtype_ifire) = 0.0_r8
dist_rate_ldist_notharvested = 0.0_r8
! Avoid this calculation to avoid NaN due to division by zero result if luh is not used
if (hlm_use_luh .eq. itrue) then
currentPatch%landuse_transition_rates(1:n_landuse_cats) = min(1._r8, &
landuse_transition_matrix(currentPatch%land_use_label,1:n_landuse_cats) / &
current_fates_landuse_state_vector(currentPatch%land_use_label))
else
currentPatch%landuse_transition_rates = 0.0_r8
end if
currentCohort => currentPatch%shortest
do while(associated(currentCohort))
if(currentCohort%canopy_layer == 1)then
! Treefall Disturbance Rate. Only count this for trees, not grasses
if ( .not. ExemptTreefallDist(currentCohort) ) then
currentPatch%disturbance_rates(dtype_ifall) = currentPatch%disturbance_rates(dtype_ifall) + &
fates_mortality_disturbance_fraction * &
min(1.0_r8,currentCohort%dmort)*hlm_freq_day*currentCohort%c_area/currentPatch%area
end if
! Logging Disturbance Rate
currentPatch%disturbance_rates(dtype_ilog) = currentPatch%disturbance_rates(dtype_ilog) + &
min(1.0_r8, currentCohort%lmort_direct + &
currentCohort%lmort_collateral + &
currentCohort%lmort_infra + &
currentCohort%l_degrad ) * &
currentCohort%c_area/currentPatch%area
if(currentPatch%disturbance_rates(dtype_ilog)>1.0) then
write(fates_log(),*) 'See luc mortalities:', currentCohort%lmort_direct, &
currentCohort%lmort_collateral, currentCohort%lmort_infra, currentCohort%l_degrad
end if
! Non-harvested part of the logging disturbance rate
dist_rate_ldist_notharvested = dist_rate_ldist_notharvested + currentCohort%l_degrad * &
currentCohort%c_area/currentPatch%area
endif
currentCohort => currentCohort%taller
enddo !currentCohort
! for non-closed-canopy areas subject to logging, add an additional increment of area disturbed
! equivalent to the fraction logged to account for transfer of interstitial ground area to new secondary lands
if ( logging_time .and. &
(currentPatch%area - currentPatch%total_canopy_area) .gt. fates_tiny ) then
! The canopy is NOT closed.
if(bc_in%hlm_harvest_units == hlm_harvest_carbon) then
call get_harvest_rate_carbon (currentPatch%land_use_label, bc_in%hlm_harvest_catnames, &
bc_in%hlm_harvest_rates, currentPatch%age_since_anthro_disturbance, harvestable_forest_c, &
harvest_rate, harvest_tag)
else
call get_harvest_rate_area (currentPatch%land_use_label, bc_in%hlm_harvest_catnames, &
bc_in%hlm_harvest_rates, frac_site_primary, currentPatch%age_since_anthro_disturbance, harvest_rate)
end if
currentPatch%disturbance_rates(dtype_ilog) = currentPatch%disturbance_rates(dtype_ilog) + &
(currentPatch%area - currentPatch%total_canopy_area) * harvest_rate / currentPatch%area
! Non-harvested part of the logging disturbance rate
dist_rate_ldist_notharvested = dist_rate_ldist_notharvested + &
(currentPatch%area - currentPatch%total_canopy_area) * harvest_rate / currentPatch%area
endif
! For nocomp mode, we need to prevent producing too small patches, which may produce small patches
if ((hlm_use_nocomp .eq. itrue) .and. &
(currentPatch%disturbance_rates(dtype_ilog)*currentPatch%area .lt. min_patch_area_forced)) then
currentPatch%disturbance_rates(dtype_ilog) = 0._r8
end if
! fraction of the logging disturbance rate that is non-harvested
if (currentPatch%disturbance_rates(dtype_ilog) .gt. nearzero) then
currentPatch%fract_ldist_not_harvested = dist_rate_ldist_notharvested / &
currentPatch%disturbance_rates(dtype_ilog)
endif
! Fire Disturbance Rate
currentPatch%disturbance_rates(dtype_ifire) = currentPatch%frac_burnt
! Fires can't burn the whole patch, as this causes /0 errors.
if (currentPatch%disturbance_rates(dtype_ifire) > 0.98_r8)then
msg = 'very high fire areas'//trim(A2S(currentPatch%disturbance_rates(:)))//trim(N2S(currentPatch%frac_burnt))
call FatesWarn(msg,index=2)
endif
! if the sum of all disturbance rates is such that they will exceed total patch area on this day, then reduce them all proportionally.
if ( (sum(currentPatch%disturbance_rates(:)) + sum(currentPatch%landuse_transition_rates(1:n_landuse_cats))) .gt. 1.0_r8 ) then
tempsum = sum(currentPatch%disturbance_rates(:)) + sum(currentPatch%landuse_transition_rates(1:n_landuse_cats))
do i_dist = 1,N_DIST_TYPES
currentPatch%disturbance_rates(i_dist) = currentPatch%disturbance_rates(i_dist) / tempsum
end do
do i_dist = 1,n_landuse_cats
currentPatch%landuse_transition_rates(i_dist) = currentPatch%landuse_transition_rates(i_dist) / tempsum
end do
endif
currentPatch => currentPatch%younger
enddo !patch loop
end subroutine disturbance_rates
! ============================================================================
subroutine spawn_patches( currentSite, bc_in)
!
! !DESCRIPTION:
! In this subroutine, the following happens,
! all of which within a complex loop structure of (from outermost to innermost loop),
! nocomp-PFT, disturbance type, donor patch land use label, and receiver patch land use label:
! 1) the total area disturbed is calculated
! 2) a new patch is created
! 3) properties are averaged
! 4) litter fluxes from fire and mortality are added
! 5) For mortality, plants in existing patch canopy are killed.
! 6) For mortality, Plants in new and existing understorey are killed
! 7) For fire, burned plants are killed, and unburned plants are added to new patch.
! 8) New cohorts are added to new patch and sorted.
! 9) New patch is added into linked list
! 10) Area checked, and patchno recalculated.
!
! !USES:
use EDParamsMod , only : ED_val_understorey_death, logging_coll_under_frac
use EDCohortDynamicsMod , only : terminate_cohorts
use FatesConstantsMod , only : rsnbl_math_prec
use FatesLandUseChangeMod, only : get_landuse_transition_rates
use FatesLandUseChangeMod, only : get_landusechange_rules
!
! !ARGUMENTS:
type (ed_site_type), intent(inout) :: currentSite
type (bc_in_type), intent(in) :: bc_in
!
! !LOCAL VARIABLES:
type (fates_patch_type) , pointer :: newPatch
type (fates_patch_type) , pointer :: currentPatch
type (fates_cohort_type), pointer :: currentCohort
type (fates_cohort_type), pointer :: nc
type (fates_cohort_type), pointer :: storesmallcohort
type (fates_cohort_type), pointer :: storebigcohort
real(r8) :: site_areadis_primary ! total area disturbed (to primary forest) in m2 per site per day
real(r8) :: site_areadis_secondary ! total area disturbed (to secondary forest) in m2 per site per day
real(r8) :: patch_site_areadis ! total area disturbed in m2 per patch per day
real(r8) :: site_areadis ! total site area disturbed in m2 per day
real(r8) :: age ! notional age of this patch in years
integer :: el ! element loop index
integer :: pft ! pft loop index
integer :: tnull ! is there a tallest cohort?
integer :: snull ! is there a shortest cohort?
integer :: levcan ! canopy level
real(r8) :: leaf_c ! leaf carbon [kg]
real(r8) :: fnrt_c ! fineroot carbon [kg]
real(r8) :: sapw_c ! sapwood carbon [kg]
real(r8) :: store_c ! storage carbon [kg]
real(r8) :: struct_c ! structure carbon [kg]
real(r8) :: total_c ! total carbon of plant [kg]
real(r8) :: leaf_burn_frac ! fraction of leaves burned in fire
! for both woody and grass species
real(r8) :: leaf_m ! leaf mass during partial burn calculations
integer :: min_nocomp_pft, max_nocomp_pft, i_nocomp_pft
integer :: i_disturbance_type, i_dist2 ! iterators for looping over disturbance types
integer :: i_landusechange_receiverpatchlabel ! iterator for the land use change types
integer :: i_donorpatch_landuse_type ! iterator for the land use change types donor patch
integer :: start_receiver_lulabel ! starting bound for receiver landuse label type loop
integer :: end_receiver_lulabel ! ending bound for receiver landuse label type loop
real(r8) :: disturbance_rate ! rate of disturbance being resolved [fraction of patch area / day]
real(r8) :: oldarea ! old patch area prior to disturbance
logical :: clearing_matrix(n_landuse_cats,n_landuse_cats) ! do we clear vegetation when transferring from one LU type to another?
!---------------------------------------------------------------------
storesmallcohort => null() ! storage of the smallest cohort for insertion routine
storebigcohort => null() ! storage of the largest cohort for insertion routine
if (hlm_use_nocomp .eq. itrue) then
min_nocomp_pft = 0
max_nocomp_pft = numpft
else
min_nocomp_pft = fates_unset_int
max_nocomp_pft = fates_unset_int
endif
! zero the diagnostic disturbance rate fields
currentSite%disturbance_rates(:,:,:) = 0._r8
! get rules for vegetation clearing during land use change
call get_landusechange_rules(clearing_matrix)
! in the nocomp cases, since every patch has a PFT identity, it can only receive patch area from patches
! that have the same identity. In order to allow this, we have this very high level loop over nocomp PFTs
! and only do the disturbance for any patches that have that nocomp PFT identity.
! If nocomp is not enabled, then this is not much of a loop, it only passes through once.
nocomp_pft_loop: do i_nocomp_pft = min_nocomp_pft,max_nocomp_pft
! we want at the second-outermost loop to go through all disturbance types, because we resolve each of these separately
disturbance_type_loop: do i_disturbance_type = 1,N_DIST_TYPES
! the next loop level is to go through patches that have a specific land-use type. the reason to do this is because the combination of
! disturbance type and donor land-use type uniquly define the land-use type of the receiver patch.
landuse_donortype_loop: do i_donorpatch_landuse_type = 1, n_landuse_cats
! figure out what land use label(s) the receiver patch for disturbance from patches with
! this disturbance label and disturbance of this type will have, and set receiver label loop bounds accordingly.
! for fire and treefall disturbance, receiver land-use type is whatever the donor land-use type is.
! for logging disturbance, receiver land-use type is always secondary lands
! for land-use-change disturbance, we need to loop over all possible transition types for land-use-change from the current land-use type.
select case(i_disturbance_type)
case(dtype_ifire)
start_receiver_lulabel = i_donorpatch_landuse_type
end_receiver_lulabel = i_donorpatch_landuse_type
case(dtype_ifall)
start_receiver_lulabel = i_donorpatch_landuse_type
end_receiver_lulabel = i_donorpatch_landuse_type
case(dtype_ilog)
start_receiver_lulabel = secondaryland
end_receiver_lulabel = secondaryland
case(dtype_ilandusechange)
start_receiver_lulabel = 1 ! this could actually maybe be 2, as primaryland column of matrix should all be zeros, but leave as 1 for now
end_receiver_lulabel = n_landuse_cats
case default
write(fates_log(),*) 'unknown disturbance mode?'
write(fates_log(),*) 'i_disturbance_type: ',i_disturbance_type
call endrun(msg=errMsg(sourcefile, __LINE__))
end select
! next loop level is the set of possible receiver patch land use types.
! for disturbance types other than land use change, this is sort of a dummy loop, per the above logic.
landusechange_receiverpatchlabel_loop: do i_landusechange_receiverpatchlabel = start_receiver_lulabel, end_receiver_lulabel
! now we want to begin resolving all of the disturbance given the above categorical criteria of:
! nocomp-PFT, disturbance type, donor patch land use label, and receiver patch land use label. All of the disturbed area that meets these
! criteria (if any) will be put into a new patch whose area and properties are taken from one or more donor patches.
! calculate area of disturbed land that meets the above criteria, in this timestep, by summing contributions from each existing patch.
currentPatch => currentSite%youngest_patch
! this variable site_areadis holds all the newly disturbed area from all patches for all disturbance being resolved now.
site_areadis = 0.0_r8
! loop over all patches to figure out the total patch area generated as a result of all disturbance being resolved now.
patchloop_areadis: do while(associated(currentPatch))
cp_nocomp_matches_1_if: if ( hlm_use_nocomp .eq. ifalse .or. &
currentPatch%nocomp_pft_label .eq. i_nocomp_pft ) then
patchlabel_matches_lutype_if_areadis: if (currentPatch%land_use_label .eq. i_donorpatch_landuse_type) then
disturbance_rate = 0.0_r8
if ( i_disturbance_type .ne. dtype_ilandusechange) then
disturbance_rate = currentPatch%disturbance_rates(i_disturbance_type)
else
disturbance_rate = currentPatch%landuse_transition_rates(i_landusechange_receiverpatchlabel)
endif
if(disturbance_rate > (1.0_r8 + rsnbl_math_prec)) then
write(fates_log(),*) 'patch disturbance rate > 1 ?',disturbance_rate
call currentPatch%Dump()
call endrun(msg=errMsg(sourcefile, __LINE__))
else if (disturbance_rate > 1.0_r8) then
disturbance_rate = 1.0_r8
end if
! Only create new patches that have non-negligible amount of land
if((currentPatch%area*disturbance_rate) > nearzero ) then
site_areadis = site_areadis + currentPatch%area * disturbance_rate
! track disturbance rates to output to history
currentSite%disturbance_rates(i_disturbance_type,i_donorpatch_landuse_type,i_landusechange_receiverpatchlabel) = &
currentSite%disturbance_rates(i_disturbance_type,i_donorpatch_landuse_type,i_landusechange_receiverpatchlabel) + &
currentPatch%area * disturbance_rate * AREA_INV
end if
end if patchlabel_matches_lutype_if_areadis
end if cp_nocomp_matches_1_if
currentPatch => currentPatch%older
enddo patchloop_areadis! end loop over patches. sum area disturbed for all patches.
! It is possible that no disturbance area was generated
if ( site_areadis > nearzero) then
age = 0.0_r8
! create an empty patch, to absorb newly disturbed area
allocate(newPatch)
call newPatch%Create(age, site_areadis, i_landusechange_receiverpatchlabel, i_nocomp_pft, &
num_swb, numpft, currentSite%nlevsoil, hlm_current_tod, &
regeneration_model)
! Initialize the litter pools to zero, these
! pools will be populated by looping over the existing patches
! and transfering in mass
do el=1,num_elements
call newPatch%litter(el)%InitConditions(init_leaf_fines=0._r8, &
init_root_fines=0._r8, &
init_ag_cwd=0._r8, &
init_bg_cwd=0._r8, &
init_seed=0._r8, &
init_seed_germ=0._r8)
end do
newPatch%tallest => null()
newPatch%shortest => null()
endif
! we now have a new patch and know its area, but it is otherwise empty. Next, we
! loop round all the patches that contribute surviving individuals and litter
! pools to the new patch. We only loop the pre-existing patches, so
! quit the loop if the current patch is null, and ignore the patch if the patch's categorical variables do not
! match those of the outermost set of loops (i.e. the patch's land-use label or nocomp-PFT label
! are not what we are resolving right now).
currentPatch => currentSite%oldest_patch
patchloop: do while(associated(currentPatch))
cp_nocomp_matches_2_if: if ( hlm_use_nocomp .eq. ifalse .or. &
currentPatch%nocomp_pft_label .eq. i_nocomp_pft ) then
patchlabel_matches_lutype_if: if (currentPatch%land_use_label .eq. i_donorpatch_landuse_type) then
! disturbance_rate is the fraction of the patch's area that is disturbed and donated
disturbance_rate = 0.0_r8
if ( i_disturbance_type .ne. dtype_ilandusechange) then
disturbance_rate = currentPatch%disturbance_rates(i_disturbance_type)
else
disturbance_rate = currentPatch%landuse_transition_rates(i_landusechange_receiverpatchlabel)
endif
! patch_site_areadis is the absolute amount of the patch's area that is disturbed and donated
patch_site_areadis = currentPatch%area * disturbance_rate
areadis_gt_zero_if: if ( patch_site_areadis > nearzero ) then
if(.not.associated(newPatch))then
write(fates_log(),*) 'Patch spawning has attempted to point to'
write(fates_log(),*) 'an un-allocated patch'
call endrun(msg=errMsg(sourcefile, __LINE__))
end if
! for the case where the donating patch is not primary, and
! the current disturbance from this patch is non-anthropogenic,
! then we need to average in the time-since-anthropogenic-disturbance
! from the donor patch into that of the receiver patch
if ( currentPatch%land_use_label .gt. primaryland .and. &
(i_disturbance_type .lt. dtype_ilog) ) then
newPatch%age_since_anthro_disturbance = newPatch%age_since_anthro_disturbance + &
currentPatch%age_since_anthro_disturbance * (patch_site_areadis / site_areadis)
endif
! Transfer the litter existing already in the donor patch to the new patch
! This call will only transfer non-burned litter to new patch
! and burned litter to atmosphere. Thus it is important to zero burnt_frac_litter when
! fire is not the current disturbance regime.
if(i_disturbance_type .ne. dtype_ifire) then
currentPatch%burnt_frac_litter(:) = 0._r8
end if
call TransLitterNewPatch( currentSite, currentPatch, newPatch, patch_site_areadis)
! Transfer in litter fluxes from plants in various contexts of death and destruction
select case(i_disturbance_type)
case (dtype_ilog)
call logging_litter_fluxes(currentSite, currentPatch, &
newPatch, patch_site_areadis,bc_in)
case (dtype_ifire)
call fire_litter_fluxes(currentSite, currentPatch, &
newPatch, patch_site_areadis,bc_in)
case (dtype_ifall)
call mortality_litter_fluxes(currentSite, currentPatch, &
newPatch, patch_site_areadis,bc_in)
case (dtype_ilandusechange)
call landusechange_litter_fluxes(currentSite, currentPatch, &
newPatch, patch_site_areadis,bc_in, &
clearing_matrix(i_donorpatch_landuse_type,i_landusechange_receiverpatchlabel))
case default
write(fates_log(),*) 'unknown disturbance mode?'
write(fates_log(),*) 'i_disturbance_type: ',i_disturbance_type
call endrun(msg=errMsg(sourcefile, __LINE__))
end select
! Copy any means or timers from the original patch to the new patch
! These values will inherit all info from the original patch
! --------------------------------------------------------------------------
call newPatch%tveg24%CopyFromDonor(currentPatch%tveg24)
call newPatch%tveg_lpa%CopyFromDonor(currentPatch%tveg_lpa)
call newPatch%tveg_longterm%CopyFromDonor(currentPatch%tveg_longterm)
if ( regeneration_model == TRS_regeneration ) then
call newPatch%seedling_layer_par24%CopyFromDonor(currentPatch%seedling_layer_par24)
call newPatch%sdlng_mort_par%CopyFromDonor(currentPatch%sdlng_mort_par)
call newPatch%sdlng2sap_par%CopyFromDonor(currentPatch%sdlng2sap_par)
do pft = 1,numpft
call newPatch%sdlng_emerg_smp(pft)%p%CopyFromDonor(currentPatch%sdlng_emerg_smp(pft)%p)
call newPatch%sdlng_mdd(pft)%p%CopyFromDonor(currentPatch%sdlng_mdd(pft)%p)
enddo
end if
call newPatch%tveg_longterm%CopyFromDonor(currentPatch%tveg_longterm)
! --------------------------------------------------------------------------
! The newly formed patch from disturbance (newPatch), has now been given
! some litter from dead plants and pre-existing litter from the donor patches.
!
! Next, we loop through the cohorts in the donor patch, copy them with
! area modified number density into the new patch, and apply survivorship.
! -------------------------------------------------------------------------
currentCohort => currentPatch%shortest
cohortloop: do while(associated(currentCohort))
allocate(nc)
if(hlm_use_planthydro.eq.itrue) call InitHydrCohort(CurrentSite,nc)
! Initialize the PARTEH object and point to the
! correct boundary condition fields
nc%prt => null()
call InitPRTObject(nc%prt)
call nc%InitPRTBoundaryConditions()
! (Keeping as an example)
! Allocate running mean functions
!allocate(nc%tveg_lpa)
!call nc%tveg_lpa%InitRMean(ema_lpa,init_value=newPatch%tveg_lpa%GetMean())
call nc%ZeroValues()
! nc is the new cohort that goes in the disturbed patch (newPatch)... currentCohort
! is the curent cohort that stays in the donor patch (currentPatch)
call currentCohort%Copy(nc)
!this is the case as the new patch probably doesn't have a closed canopy, and
! even if it does, that will be sorted out in canopy_structure.
nc%canopy_layer = 1
nc%canopy_layer_yesterday = 1._r8
sapw_c = currentCohort%prt%GetState(sapw_organ, carbon12_element)
struct_c = currentCohort%prt%GetState(struct_organ, carbon12_element)
leaf_c = currentCohort%prt%GetState(leaf_organ, carbon12_element)
fnrt_c = currentCohort%prt%GetState(fnrt_organ, carbon12_element)
store_c = currentCohort%prt%GetState(store_organ, carbon12_element)
total_c = sapw_c + struct_c + leaf_c + fnrt_c + store_c
! survivorship of plants in both the disturbed and undisturbed cohorts depends on what type of disturbance is happening.
disttype_case: select case(i_disturbance_type)
! treefall mortality is the current disturbance
case (dtype_ifall)
in_canopy_if_falldtype: if(currentCohort%canopy_layer == 1)then
! In the donor patch we are left with fewer trees because the area has decreased
! the plant density for large trees does not actually decrease in the donor patch
! because this is the part of the original patch where no trees have actually fallen
! The diagnostic cmort,bmort,hmort, and frmort rates have already been saved
currentCohort%n = currentCohort%n * (1.0_r8 - fates_mortality_disturbance_fraction * &
min(1.0_r8,currentCohort%dmort * hlm_freq_day))
nc%n = 0.0_r8 ! kill all of the trees who caused the disturbance.
nc%cmort = nan ! The mortality diagnostics are set to nan
! because the cohort should dissappear
nc%hmort = nan
nc%bmort = nan
nc%frmort = nan
nc%smort = nan
nc%asmort = nan
nc%dgmort = nan
nc%lmort_direct = nan
nc%lmort_collateral = nan
nc%lmort_infra = nan
nc%l_degrad = nan
else
! understory trees
woody_if_falldtype: if( prt_params%woody(currentCohort%pft) == itrue)then
! Survivorship of undestory woody plants. Two step process.
! Step 1: Reduce current number of plants to reflect the
! change in area.
! The number density per square are doesn't change,
! but since the patch is smaller and cohort counts
! are absolute, reduce this number.
nc%n = currentCohort%n * patch_site_areadis/currentPatch%area
! because the mortality rate due to impact for the cohorts which
! had been in the understory and are now in the newly-
! disturbed patch is very high, passing the imort directly to history
! results in large numerical errors, on account of the sharply
! reduced number densities. so instead pass this info via a
! site-level diagnostic variable before reducing the number density.
currentSite%imort_rate(currentCohort%size_class, currentCohort%pft) = &
currentSite%imort_rate(currentCohort%size_class, currentCohort%pft) + &
nc%n * ED_val_understorey_death / hlm_freq_day
currentSite%imort_carbonflux(currentCohort%pft) = &
currentSite%imort_carbonflux(currentCohort%pft) + &
(nc%n * ED_val_understorey_death / hlm_freq_day ) * &
total_c * g_per_kg * days_per_sec * years_per_day * ha_per_m2
currentSite%imort_abg_flux(currentCohort%size_class, currentCohort%pft) = &
currentSite%imort_abg_flux(currentCohort%size_class, currentCohort%pft) + &
(nc%n * ED_val_understorey_death / hlm_freq_day ) * &
( (sapw_c + struct_c + store_c) * prt_params%allom_agb_frac(currentCohort%pft) + &
leaf_c ) * &
g_per_kg * days_per_sec * years_per_day * ha_per_m2
! Step 2: Apply survivor ship function based on the understory death fraction
! remaining of understory plants of those that are knocked over
! by the overstorey trees dying...
nc%n = nc%n * (1.0_r8 - ED_val_understorey_death)
! since the donor patch split and sent a fraction of its members
! to the new patch and a fraction to be preserved in itself,
! when reporting diagnostic rates, we must carry over the mortality rates from
! the donor that were applied before the patch split. Remember this is only
! for diagnostics. But think of it this way, the rates are weighted by
! number density in EDCLMLink, and the number density of this new patch is donated
! so with the number density must come the effective mortality rates.
nc%cmort = currentCohort%cmort
nc%hmort = currentCohort%hmort
nc%bmort = currentCohort%bmort
nc%frmort = currentCohort%frmort
nc%smort = currentCohort%smort
nc%asmort = currentCohort%asmort
nc%dgmort = currentCohort%dgmort
nc%dmort = currentCohort%dmort
nc%lmort_direct = currentCohort%lmort_direct
nc%lmort_collateral = currentCohort%lmort_collateral
nc%lmort_infra = currentCohort%lmort_infra
! understory trees that might potentially be knocked over in the disturbance.
! The existing (donor) patch should not have any impact mortality, it should
! only lose cohorts due to the decrease in area. This is not mortality.
! Besides, the current and newly created patch sum to unity
currentCohort%n = currentCohort%n * (1._r8 - patch_site_areadis/currentPatch%area)
else
! grass is not killed by mortality disturbance events. Just move it into the new patch area.
! Just split the grass into the existing and new patch structures
nc%n = currentCohort%n * patch_site_areadis/currentPatch%area
! Those remaining in the existing
currentCohort%n = currentCohort%n * (1._r8 - patch_site_areadis/currentPatch%area)
nc%cmort = currentCohort%cmort
nc%hmort = currentCohort%hmort
nc%bmort = currentCohort%bmort
nc%frmort = currentCohort%frmort
nc%smort = currentCohort%smort
nc%asmort = currentCohort%asmort
nc%dgmort = currentCohort%dgmort
nc%dmort = currentCohort%dmort
nc%lmort_direct = currentCohort%lmort_direct
nc%lmort_collateral = currentCohort%lmort_collateral
nc%lmort_infra = currentCohort%lmort_infra
endif woody_if_falldtype
endif in_canopy_if_falldtype
! Fire is the current disturbance
case (dtype_ifire)
! Number of members in the new patch, before we impose fire survivorship
nc%n = currentCohort%n * patch_site_areadis/currentPatch%area
! loss of individuals from source patch due to area shrinking
currentCohort%n = currentCohort%n * (1._r8 - patch_site_areadis/currentPatch%area)
levcan = currentCohort%canopy_layer
if(levcan==ican_upper) then
! before changing number densities, track total rate of trees that died
! due to fire, as well as from each fire mortality term
currentSite%fmort_rate_canopy(currentCohort%size_class, currentCohort%pft) = &
currentSite%fmort_rate_canopy(currentCohort%size_class, currentCohort%pft) + &
nc%n * currentCohort%fire_mort / hlm_freq_day
currentSite%fmort_carbonflux_canopy(currentCohort%pft) = &
currentSite%fmort_carbonflux_canopy(currentCohort%pft) + &
(nc%n * currentCohort%fire_mort) * &
total_c * g_per_kg * days_per_sec * ha_per_m2
else
! understory
currentSite%fmort_rate_ustory(currentCohort%size_class, currentCohort%pft) = &
currentSite%fmort_rate_ustory(currentCohort%size_class, currentCohort%pft) + &
nc%n * currentCohort%fire_mort / hlm_freq_day
currentSite%fmort_carbonflux_ustory(currentCohort%pft) = &
currentSite%fmort_carbonflux_ustory(currentCohort%pft) + &
(nc%n * currentCohort%fire_mort) * &
total_c * g_per_kg * days_per_sec * ha_per_m2
end if
currentSite%fmort_abg_flux(currentCohort%size_class, currentCohort%pft) = &
currentSite%fmort_abg_flux(currentCohort%size_class, currentCohort%pft) + &
(nc%n * currentCohort%fire_mort) * &
( (sapw_c + struct_c + store_c) * prt_params%allom_agb_frac(currentCohort%pft) + &
leaf_c ) * &
g_per_kg * days_per_sec * ha_per_m2
currentSite%fmort_rate_cambial(currentCohort%size_class, currentCohort%pft) = &
currentSite%fmort_rate_cambial(currentCohort%size_class, currentCohort%pft) + &
nc%n * currentCohort%cambial_mort / hlm_freq_day
currentSite%fmort_rate_crown(currentCohort%size_class, currentCohort%pft) = &
currentSite%fmort_rate_crown(currentCohort%size_class, currentCohort%pft) + &
nc%n * currentCohort%crownfire_mort / hlm_freq_day
! loss of individual from fire in new patch.
nc%n = nc%n * (1.0_r8 - currentCohort%fire_mort)
nc%cmort = currentCohort%cmort
nc%hmort = currentCohort%hmort
nc%bmort = currentCohort%bmort
nc%frmort = currentCohort%frmort
nc%smort = currentCohort%smort
nc%asmort = currentCohort%asmort
nc%dgmort = currentCohort%dgmort
nc%dmort = currentCohort%dmort
nc%lmort_direct = currentCohort%lmort_direct
nc%lmort_collateral = currentCohort%lmort_collateral
nc%lmort_infra = currentCohort%lmort_infra
! Some of of the leaf mass from living plants has been
! burned off. Here, we remove that mass, and
! tally it in the flux we sent to the atmosphere
if(prt_params%woody(currentCohort%pft) == itrue)then
leaf_burn_frac = currentCohort%fraction_crown_burned
else
! Grasses determine their fraction of leaves burned here
leaf_burn_frac = currentPatch%burnt_frac_litter(lg_sf)
endif
! Perform a check to make sure that spitfire gave
! us reasonable mortality and burn fraction rates
if( (leaf_burn_frac < 0._r8) .or. &
(leaf_burn_frac > 1._r8) .or. &
(currentCohort%fire_mort < 0._r8) .or. &
(currentCohort%fire_mort > 1._r8)) then
write(fates_log(),*) 'unexpected fire fractions'
write(fates_log(),*) prt_params%woody(currentCohort%pft)
write(fates_log(),*) leaf_burn_frac
write(fates_log(),*) currentCohort%fire_mort
call endrun(msg=errMsg(sourcefile, __LINE__))
end if
do el = 1,num_elements
leaf_m = nc%prt%GetState(leaf_organ, element_list(el))
! for woody plants burn only leaves
if(int(prt_params%woody(currentCohort%pft)) == itrue)then
leaf_m = nc%prt%GetState(leaf_organ, element_list(el))
else
! for grasses burn all aboveground tissues
leaf_m = nc%prt%GetState(leaf_organ, element_list(el)) + &
nc%prt%GetState(sapw_organ, element_list(el)) + &