LandscapeDNDC 1.37.0
Loading...
Searching...
No Matches
ldndc::WatercycleDNDC Class Reference

Watercycle model WatercycleDNDC. More...

#include <models/watercycle/dndc/watercycle-dndc.h>

Public Member Functions

lerr_t solve ()
 Kicks off computation for one time step.
 

Private Member Functions

lerr_t event_irrigate ()
 considers water input due to irrigation and/or liquid manure evetns
 
lerr_t event_flood ()
 sets hydrologic conditions during flooding events, e.g.,
 
double balance_check (double *)
 checks each time step for correct water balance
 
void CalcRaining (unsigned int)
 apportion daily rainfall to subdaily rainfall
 
double rainfall_intensity ()
 
void CalcIrrigation (unsigned int)
 apportion daily irrigation to subdaily irrigation
 
lerr_t CalcIceContent ()
 Call of ice-content related processes.
 
lerr_t CalcPotEvapoTranspiration (size_t)
 Potential evaporation.
 
void CalcInterception ()
 Calculates water quantity that is retained on leaf and stem bark surfaces.
 
double get_leaf_water ()
 Collects leaf water from all canopy layers.
 
double get_impedance_factor (size_t)
 Reduces water fluxes out of a layer if ice lenses have formed.
 
lerr_t CalcTranspiration ()
 
lerr_t CalcTranspirationCouvreur (double const &hr_potTransinm)
 
void CalcSnowEvaporation ()
 
void CalcSurfaceWaterEvaporation ()
 
void CalcSurfaceFlux ()
 
void WatercycleDNDC_preferential_flow ()
 
void CalcSoilEvapoPercolation ()
 
void SoilWaterEvaporation (size_t)
 
double WaterFlowCapillaryRise (size_t, double const &)
 Similar to WaterFlowBelowFieldCapacity(...) but restricts water flow by water availability from soil layer below.
 
double WaterFlowAboveFieldCapacity (size_t, double const &)
 
double get_fsl (size_t)
 Returns factor accounting for soil layer depth. Originally, a constant soil layer depth of 0.02 m has been assumed. Due to flexible layer depth, an additional factor has been introduced that accounts for a higher resistance of larger layers.
 
double substep_day_fraction ()
 Length of a substep as fraction of a day.
 
double get_clay_limitation (size_t _sl, double _water_avail, double _water_ref)
 Restrains water availability due to clay content.
 
double hours_per_time_step ()
 

Private Attributes

double thornthwaite_heat_index
 heat index for potential evaporation using approach after Thornthwaite
 
double rainfall_intensity_cache
 

Detailed Description

Watercycle model WatercycleDNDC.

Member Function Documentation

◆ balance_check()

double ldndc::WatercycleDNDC::balance_check ( double * _balance)
private

checks each time step for correct water balance

Balance check of water.

2736{
2737 double balance = get_leaf_water() + wc_.surface_ice + wc_.surface_water;
2738 for ( size_t sl = 0; sl < m_soillayers->soil_layer_cnt(); ++sl)
2739 {
2740 balance += (wc_.wc_sl[sl] + wc_.ice_sl[sl]) * sc_.h_sl[sl];
2741 }
2742 balance += ( - m_wc.accumulated_irrigation_event_executed
2743 - wc_.accumulated_irrigation_automatic
2744 - wc_.accumulated_irrigation_ggcmi
2745 - wc_.accumulated_irrigation_reservoir_withdrawal
2746 - wc_.accumulated_precipitation
2747 - wc_.accumulated_groundwater_access
2748 + wc_.accumulated_percolation
2749 + wc_.accumulated_runoff
2750 + wc_.accumulated_wateruptake_sl.sum()
2751 + wc_.accumulated_interceptionevaporation
2752 + wc_.accumulated_soilevaporation
2753 + wc_.accumulated_surfacewaterevaporation);
2754
2755 if ( _balance)
2756 {
2757 double const balance_delta( std::abs( *_balance - balance));
2758 if ( balance_delta > cbm::DIFFMAX)
2759 {
2760 KLOGWARN( "Water leakage: difference is ", balance - *_balance);
2761 }
2762 }
2763
2764 for ( size_t sl = 0; sl < m_soillayers->soil_layer_cnt(); ++sl)
2765 {
2766 if ( cbm::flt_less( wc_.wc_sl[sl], 0.0))
2767 {
2768 KLOGWARN( "Negative water content of: ", wc_.wc_sl[sl], " in layer: ", sl, ". Water content set to zero.");
2769 wc_.wc_sl[sl] = 0.0;
2770 }
2771 if ( cbm::flt_less( wc_.ice_sl[sl], 0.0))
2772 {
2773 KLOGWARN( "Negative ice content of: ", wc_.ice_sl[sl], " in layer: ", sl, ". Ice content set to zero.");
2774 wc_.ice_sl[sl] = 0.0;
2775 }
2776 }
2777
2778 return balance;
2779}
double get_leaf_water()
Collects leaf water from all canopy layers.
Definition watercycle-dndc.cpp:1163

References balance_check(), and get_leaf_water().

Referenced by balance_check(), and solve().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ CalcInterception()

void ldndc::WatercycleDNDC::CalcInterception ( )
private

Calculates water quantity that is retained on leaf and stem bark surfaces.

  • Rainfall and interception are assumed to happen at the end of the timestep and evaporation is realised at once at the start of the timestep.
  • The model assumes a homogeneous horizontal distribution of LAI although a non-linear dependency to crown cover (relative foliage clumping) has been reported (e.g. Teklehaimanot et al. 1991).
  • The default water-interception for foliage almost equal to the largest species specific value

References get_impedance_factor(), and get_leaf_water().

Referenced by solve().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ CalcIrrigation()

void ldndc::WatercycleDNDC::CalcIrrigation ( unsigned int _substep)
private

apportion daily irrigation to subdaily irrigation

Automatic irrigation to prevent water stress

GGCMI style irrigation

  • fill up the water content of each soil layer to field capacity at the start of each day
  • water supplied just injected into soil layer
  • no interception, no run off, no surface-water
  • only irrigate if a crop is present

Irrigation due to flooding

irrigation triggered by flooding management

Irrigation due to explicit irrigation event

855{
856 //reset irrigation
857 m_wc.irrigation = 0.0;
858
862 if (cbm::flt_in_range_lu(0.0, m_cfg.automaticirrigation, 1.0))
863 {
864 if (m_veg->size() > 0) //check crop is present
865 {
866 if (cbm::flt_greater(mc_.nd_airtemperature,10.0)) // only add water if temperature > 10C)
867 {
868 for (size_t l = 0; l < m_soillayers->soil_layer_cnt(); ++l)
869 {
870 if (cbm::flt_greater_zero(m_veg->mfrt_sl(l))) //only count water need for soil layers that contain roots
871 {
872 // trigger irrigation events as soon as drought stress appears
873 double const wc_target(wc_.wc_wp_sl[l] + m_cfg.automaticirrigation * (wc_.wc_fc_sl[l] - wc_.wc_wp_sl[l]));
874 if (cbm::flt_less(wc_.wc_sl[l], wc_target) &&
875 cbm::flt_equal_zero(wc_.ice_sl[l])) //only add water if no ice is present
876 {
877 // irrigation assumed until target water content
878 double const irrigate_layer((wc_target - wc_.wc_sl[l]) * sc_.h_sl[l]);
879 m_wc.irrigation += irrigate_layer;
880 wc_.accumulated_irrigation_automatic += irrigate_layer;
881 }
882 }
883 }
884
885 }
886 else
887 {
888 }
889
890 }
891 }
892
893 if ( cbm::flt_in_range_lu( 0.0, m_eventflood.get_saturation_level(), 1.0) &&
894 cbm::is_valid( m_eventflood.get_irrigation_height()) &&
895 cbm::is_valid( m_eventflood.get_soil_depth()))
896 {
897 double wc_sum( 0.0);
898 double wc_fc_sum( 0.0);
899 double wc_wp_sum( 0.0);
900 for ( size_t l = 0; l < m_soillayers->soil_layer_cnt(); ++l)
901 {
902 wc_sum += wc_.wc_sl[l] * sc_.h_sl[l];
903 wc_fc_sum += wc_.wc_fc_sl[l] * sc_.h_sl[l];
904 wc_wp_sum += wc_.wc_wp_sl[l] * sc_.h_sl[l];
905
906 if ( cbm::flt_greater_equal( sc_.depth_sl[l], m_eventflood.get_soil_depth()))
907 {
908 break;
909 }
910 }
911
912 // trigger irrigation events as soon as drought stress appears
913 double const wc_target( wc_wp_sum + m_eventflood.get_saturation_level() * (wc_fc_sum - wc_wp_sum));
914 if ( cbm::flt_less( wc_sum, wc_target))
915 {
916 m_wc.irrigation += m_eventflood.get_irrigation_height();
917 wc_.accumulated_irrigation_automatic += m_eventflood.get_irrigation_height();
918 }
919 }
920
928 if ( m_cfg.ggcmi_irrigation)
929 {
930 if( (lclock()->subday() == 1) && (_substep == 0))
931 {
932 if( m_veg->size() > 0 ) //check crop is present
933 {
934 double water_supply( 0.0);
935 for ( size_t l = 0; l < m_soillayers->soil_layer_cnt(); ++l)
936 {
937 if ( cbm::flt_greater_zero( m_veg->mfrt_sl( l))) //only irrigate soil layers that contain roots
938 {
939 if( cbm::flt_less( wc_.wc_sl[l], wc_.wc_fc_sl[l]) &&
940 cbm::flt_equal_zero( wc_.ice_sl[l])) //only add water if no ice is present
941 {
942 water_supply += (wc_.wc_fc_sl[l] - wc_.wc_sl[l]) * sc_.h_sl[l]; //amount of water added (m)
943 wc_.wc_sl[l] = wc_.wc_fc_sl[l];
944 }
945 }
946 }
947 wc_.accumulated_irrigation_ggcmi += water_supply;
948 }
949 }
950 }
951
955 if ( cbm::is_valid( m_eventflood.get_water_table()))
956 {
957 /* Dynamic flooding */
958 if ( cbm::is_valid( m_eventflood.get_irrigation_height()))
959 {
960 bool irrigate( false);
961
962 /* Irrigate after surface water table drop */
963 if ( cbm::flt_greater_equal_zero( m_eventflood.get_water_table()))
964 {
965 if ( cbm::flt_greater( m_eventflood.get_water_table(), wc_.surface_water))
966 {
967 irrigate = true;
968 }
969 }
970 else
971 {
972 if ( !cbm::flt_greater_zero( wc_.surface_water))
973 {
974 double water_tot_sum( 0.0);
975 double water_fc_sum( 0.0);
976 for ( size_t sl = 0; sl < m_soillayers->soil_layer_cnt(); ++sl)
977 {
978 water_tot_sum += wc_.wc_sl[sl] * sc_.h_sl[sl];
979 water_fc_sum += wc_.wc_fc_sl[sl] * sc_.h_sl[sl];
980 if ( cbm::flt_greater_equal( sc_.depth_sl[sl], -m_eventflood.get_water_table()))
981 {
982 break;
983 }
984 }
985 // Note: The tipping bucket approach may block percolation into deeper soil layers
986 // if the upper layers have higher water content. This can lead to water reduction
987 // from evapotranspiration in deeper layers without replenishment from above.
988 // Workaround: AWD is triggered when the integrated topsoil water drops below
989 // 90% of the integrated topsoil field capacity.
990 if ( cbm::flt_less( water_tot_sum, 0.9 * water_fc_sum))
991 {
992 irrigate = true;
993 }
994 }
995 }
996 // trigger AWD irrigation event
997 if ( irrigate)
998 {
999 double water_supply( cbm::bound_min( 0.0, m_eventflood.get_irrigation_height() - wc_.surface_water));
1000 for ( size_t sl = 0; sl < m_soillayers->soil_layer_cnt(); ++sl)
1001 {
1002 // set soil fully water saturated
1003 if ( cbm::flt_greater( sc_.poro_sl[sl], wc_.wc_sl[sl]))
1004 {
1005 water_supply += ((sc_.poro_sl[sl] - wc_.wc_sl[sl]) * sc_.h_sl[sl]);
1006 }
1007 }
1008
1009 //remove rainfall (less irrigation water is supplied if it if raining)
1010 water_supply = cbm::bound_min( 0.0, water_supply - m_mc.precipitation_ts);
1011
1012 if ( m_eventflood.have_unlimited_water())
1013 {
1014 wc_.accumulated_irrigation_automatic += water_supply;
1015 }
1016 else
1017 {
1018 if ( cbm::flt_greater( wc_.irrigation_reservoir, water_supply))
1019 {
1020 wc_.irrigation_reservoir -= water_supply;
1021 }
1022 else
1023 {
1024 water_supply = wc_.irrigation_reservoir;
1025 wc_.irrigation_reservoir = 0.0;
1026 }
1027 wc_.accumulated_irrigation_reservoir_withdrawal += water_supply;
1028 }
1029 m_wc.irrigation += water_supply;
1030 }
1031 }
1032 /* Static flooding */
1033 else
1034 {
1035 if ( !cbm::flt_greater_zero( m_eventflood.get_water_table()))
1036 {
1037 double water_supply( 0.0);
1038 for ( size_t sl = 0; sl < m_soillayers->soil_layer_cnt(); ++sl)
1039 {
1040 if ( cbm::flt_greater( sc_.depth_sl[sl], -m_eventflood.get_water_table()) &&
1041 cbm::flt_greater( sc_.poro_sl[sl], wc_.wc_sl[sl]))
1042 {
1043 double const scale( cbm::flt_less( sc_.depth_sl[sl] - sc_.h_sl[sl], -m_eventflood.get_water_table()) ?
1044 (-m_eventflood.get_water_table() - (sc_.depth_sl[sl] - sc_.h_sl[sl])) / sc_.h_sl[sl] :
1045 1.0);
1046
1047 water_supply += scale * (sc_.poro_sl[sl] - wc_.wc_sl[sl]) * sc_.h_sl[sl];
1048 }
1049 }
1050
1051 //remove rainfall (less irrigation water is supplied if it is raining)
1052 //and limit by saturated water flow into the first soil layer during the substep
1053 water_supply = cbm::bound( 0.0, water_supply - m_mc.precipitation_ts,
1054 WaterFlowSaturated( 0, get_impedance_factor( 0)));
1055
1056 /* Surface water will be actively drained */
1057 if ( cbm::flt_greater_equal( wc_.surface_water, water_supply))
1058 {
1059 wc_.accumulated_runoff += wc_.surface_water - water_supply;
1060 wc_.surface_water = water_supply;
1061 water_supply = 0.0;
1062 }
1063 else if ( cbm::flt_greater_zero( wc_.surface_water))
1064 {
1065 water_supply -= wc_.surface_water;
1066 }
1067
1068 if ( m_eventflood.have_unlimited_water())
1069 {
1070 wc_.accumulated_irrigation_automatic += water_supply;
1071 }
1072 else
1073 {
1074 if ( cbm::flt_greater( wc_.irrigation_reservoir, water_supply))
1075 {
1076 wc_.irrigation_reservoir -= water_supply;
1077 }
1078 else
1079 {
1080 water_supply = wc_.irrigation_reservoir;
1081 wc_.irrigation_reservoir = 0.0;
1082 }
1083 wc_.accumulated_irrigation_reservoir_withdrawal += water_supply;
1084 }
1085
1086 m_wc.irrigation += water_supply;
1087 }
1091 else if ( cbm::flt_greater( m_eventflood.get_water_table(), wc_.surface_water))
1092 {
1093 //adjust surface and soil water in case of flooding event
1094 //water required to fill up surface water
1095 double water_supply( m_eventflood.get_water_table() - wc_.surface_water);
1096
1097 //add water required to fill top x layers up to saturation
1098 unsigned long no_layers(3);
1099 if( no_layers > m_soillayers->soil_layer_cnt())
1100 {
1101 no_layers = m_soillayers->soil_layer_cnt();
1102 }
1103
1104 for ( size_t sl = 0; sl < no_layers; ++sl)
1105 {
1106 water_supply += ((sc_.poro_sl[sl] - wc_.wc_sl[sl]) * sc_.h_sl[sl]);
1107 }
1108
1109 //remove rainfall (less irrigation water is supplied if it if raining)
1110 water_supply = cbm::bound_min( 0.0, water_supply - m_mc.precipitation_ts);
1111
1112 if ( m_eventflood.have_unlimited_water())
1113 {
1114 wc_.accumulated_irrigation_automatic += water_supply;
1115 }
1116 else
1117 {
1118 if ( cbm::flt_greater( wc_.irrigation_reservoir, water_supply))
1119 {
1120 wc_.irrigation_reservoir -= water_supply;
1121 }
1122 else
1123 {
1124 water_supply = wc_.irrigation_reservoir;
1125 wc_.irrigation_reservoir = 0.0;
1126 }
1127 wc_.accumulated_irrigation_reservoir_withdrawal += water_supply;
1128 }
1129 m_wc.irrigation += water_supply;
1130 }
1131 }
1132 }
1133
1137 double const irrigation_scheduled( cbm::bound_min( 0.0, wc_.accumulated_irrigation - m_wc.accumulated_irrigation_event_executed));
1138
1139 // note: amounts are given per substep (substep_hours hours)
1140 if ( cbm::flt_greater( irrigation_scheduled, 24.0 * rainfall_intensity()))
1141 {
1142 m_wc.irrigation += irrigation_scheduled * substep_hours / 24.0;
1143 m_wc.accumulated_irrigation_event_executed += irrigation_scheduled * substep_hours / 24.0;
1144 }
1145 else if ( cbm::flt_greater( irrigation_scheduled, substep_hours * rainfall_intensity()))
1146 {
1147 m_wc.irrigation += substep_hours * rainfall_intensity();
1148 m_wc.accumulated_irrigation_event_executed += substep_hours * rainfall_intensity();
1149 }
1150 else
1151 {
1152 m_wc.irrigation += irrigation_scheduled;
1153 m_wc.accumulated_irrigation_event_executed += irrigation_scheduled;
1154 }
1155}
double rainfall_intensity()
Definition watercycle-dndc.cpp:100
double get_impedance_factor(size_t)
Reduces water fluxes out of a layer if ice lenses have formed.
Definition watercycle-dndc.cpp:2599

References CalcIrrigation(), get_impedance_factor(), and rainfall_intensity().

Referenced by CalcIrrigation(), and solve().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ CalcPotEvapoTranspiration()

lerr_t ldndc::WatercycleDNDC::CalcPotEvapoTranspiration ( size_t _substep)
private

Potential evaporation.


there are two methods of calculating potential evaporation (choose via macro POTENTIAL_EVAPORATION above)

  • [0] Procedure according to Thornthwaite 1948 with modifications by Camargo et al. 1999 and Peireira and Pruitt 2004 (implemented from Pereira and Pruitt 2004) to better account for dry environments and daylength.
    NOTE:
    • monthly temperatures are assumed to be equal to daily average temperature of the middle of the month, calculated from continous equations equal to those used in the weather generator.
    • the model uses always daily average temperature as input even if it is run in sub-daily mode
    • the original PnET-N-DNDC code which contains modification that could not be found in the literature has been outcommented.
  • [1] uses the Priestley Taylor equation (Priestly and Taylor 1972) to calculate daily and hourly potential evapotranspiration (implemented from internet) vps calculation according to Allen et al. 1998, cit. in Cai et al. 2007
575{
576 // Daily evaportation demand is derived from the Thornthwaite, the Penman, or the Priestley-Taylor method (standard is 'thornthwaite).
577 day_potevapo = 0.0;
578 if ( m_cfg.potentialevapotranspiration_method == "thornthwaite")
579 {
580 if ( lclock()->is_position( TMODE_PRE_YEARLY))
581 {
583 lclock()->days_in_year(),
584 m_climate->annual_temperature_average(),
585 m_climate->annual_temperature_amplitude());
586 }
587
588 double const daylength(ldndc::meteo::daylength(m_setup->latitude(), lclock()->yearday()));
589 double const avg_dayl(12.0);
590
591 day_potevapo = m_sipar->WCDNDC_INCREASE_POT_EVAPOTRANS()
593 mc_.nd_airtemperature,
594 daylength, avg_dayl, lclock()->days_in_month(),
596 }
597 else if ( (m_cfg.potentialevapotranspiration_method == "penman") ||
598 (m_cfg.potentialevapotranspiration_method == "priestleytaylor"))
599 {
600 cbm::sclock_t const & clk( lclock_ref());
601
602 /* vapour pressure [10^3:Pa] */
603 double vps( 0.0);
604 ldndc::meteo::vps( mc_.nd_airtemperature, &vps);
605 double vp( vps - m_climate->vpd_day( clk));
606
607 if( m_cfg.potentialevapotranspiration_method == "priestleytaylor")
608 {
609 day_potevapo = m_sipar->WCDNDC_INCREASE_POT_EVAPOTRANS()
610 * ldndc::priestleytaylor( clk.yearday(),
611 clk.days_in_year(),
612 mc_.albedo,
613 m_setup->latitude(),
614 mc_.nd_airtemperature,
615 mc_.nd_shortwaveradiation_in,
616 vp,
617 m_sipar->PT_ALPHA());
618 }
619 else if( m_cfg.potentialevapotranspiration_method == "penman")
620 {
621 /* leaf area index */
622 double const lai( m_veg->lai());
623 ldndc::surface_type surface( cbm::flt_greater_zero( lai) ? short_grass : bare_soil);
624
625 day_potevapo = m_sipar->WCDNDC_INCREASE_POT_EVAPOTRANS()
626 * ldndc::penman( surface,
627 clk.yearday(),
628 clk.days_in_year(),
629 mc_.albedo,
630 m_setup->latitude(),
631 mc_.nd_shortwaveradiation_in * cbm::SEC_IN_DAY,
632 mc_.nd_airtemperature,
633 mc_.nd_windspeed,
634 vp);
635 }
636 }
637 else
638 {
639 KLOGERROR("[BUG] huh : it seems that this potentialevapotranspiration method is not registered",
640 "tell the maintainers refering to this error message");
641 return LDNDC_ERR_FAIL;
642 }
643
644 // The daily evaporation demand is distributed throughout the day (standard is 'previouspattern)
645 // TODO: design a routine that distributes evaporation demand according to temperature or humidity
646 if ( m_cfg.evapotranspiration_method == "uniform")
647 {
648 // hourly evaporation demand is calculated from daily demand equally throughout the day
649 hr_pot_evapotrans = day_potevapo * substep_hours / cbm::HR_IN_DAY;
650 }
651 else if ( m_cfg.evapotranspiration_method == "previouspattern")
652 {
653 // evaporation demand of the time step is derived from the weighted expression of previous values
654 // and equally distributed over the substeps of the time step
655 hr_pot_evapotrans = day_potevapo * pot_evapotranspiration_ts[lclock()->subday()-1] / substeps_per_timestep;
656 }
657 else if (m_cfg.evapotranspiration_method == "daytimeonly")
658 {
659 // hourly evaporation demand is distributed throughout the sunlight hours only (similar as radiation)
660 // hour of the day at the end of the current substep
661 double const hour( double(lclock()->subday() - 1) * substep_hours * substeps_per_timestep + double(_substep + 1) * substep_hours);
662 double const day( lclock()->yearday());
663 double const hsun( 0.833 * cbm::PI / 180.0);
664 double const lat( m_setup->latitude() * cbm::PI / 180.0);
665 double const dec( -23.44 * (cbm::PI / 180.0) * std::cos(2.0 * cbm::PI * (day - 172.0) / lclock()->days_in_year()));
666 double const help( (std::sin(hsun) - std::sin(lat) * std::sin(dec)) / (std::cos(lat) * std::cos(dec)));
667 double dayl(0.0);
668 if (help < -0.999) dayl = 0.0;
669 else if (help > 0.999) dayl = 24.0;
670 else dayl = 24.0 - 24.0 / cbm::PI * std::acos((std::sin(hsun) - std::sin(lat) * std::sin(dec)) / (std::cos(lat) * std::cos(dec)));
671 if (dayl < 1.0) dayl = 0.0;
672 double const rise(12.0 - dayl * 0.5);
673
674 double factor(0.0);
675 if (dayl > 0.0) factor = (-std::cos(2.0 * cbm::PI * (hour - rise) / (cbm::HR_IN_DAY - 2.0 * rise)) + 1.0) / (cbm::HR_IN_DAY - 2.0 * rise);
676
677 double fday(0.0);
678 if (hour > rise && hour < (24.0 - rise)) fday = factor;
679
680 // fday is given per hour
681 hr_pot_evapotrans = day_potevapo * fday * substep_hours;
682 }
683 else
684 {
685 KLOGERROR("[BUG] huh : it seems that this evapotranspiration method option is not registered ",
686 "tell the maintainers refering to this error message");
687 return LDNDC_ERR_FAIL;
688 }
689
690 // the evaporation limit for all timesteps is the daily demand
691 wc_.accumulated_potentialevaporation += hr_pot_evapotrans;
692
693 return LDNDC_ERR_OK;
694}
double thornthwaite_heat_index
heat index for potential evaporation using approach after Thornthwaite
Definition watercycle-dndc.h:137
double LDNDC_API thornthwaite_heat_index(double, size_t, double, double)
Thornthwaite heat index.
Definition ld_thornthwaite.cpp:100
double LDNDC_API thornthwaite(double, double, double, double, double)
Potential evapotranspiration after thornthwaite:1948a.
Definition ld_thornthwaite.cpp:41

References CalcPotEvapoTranspiration(), ldndc::thornthwaite(), ldndc::thornthwaite_heat_index(), and thornthwaite_heat_index.

Referenced by CalcPotEvapoTranspiration(), and solve().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ CalcSnowEvaporation()

void ldndc::WatercycleDNDC::CalcSnowEvaporation ( )
private
Parameters
[in]None
[out]None
Returns
None
2095{
2096 //Average limit of snowEvaporation is 0.00013m day-1 (Granger and Male 1978)
2097 double hr_potESnow( cbm::bound(0.0, hr_pot_evapotrans, 0.00013 * substep_hours/24.0));
2098
2099 if ( cbm::flt_greater_zero( wc_.surface_ice) &&
2100 cbm::flt_greater_zero( hr_potESnow))
2101 {
2102 //If there is a snowpack and there is a potential to evaporate
2103 if ( wc_.surface_ice > hr_potESnow)
2104 {
2105 //Partial evaporation from the snow
2106 //note: snow evaporation is booked as surface water evaporation (no separate state variable)
2107 hr_pot_evapotrans = cbm::bound_min( 0.0, hr_pot_evapotrans - hr_potESnow);
2108 wc_.accumulated_surfacewaterevaporation += hr_potESnow;
2109 wc_.surface_ice -= hr_potESnow;
2110 }
2111 else
2112 {
2113 //Total evaporation of the snowpack
2114 hr_pot_evapotrans = cbm::bound_min( 0.0, hr_pot_evapotrans - wc_.surface_ice);;
2115 wc_.accumulated_surfacewaterevaporation += wc_.surface_ice;
2116 wc_.surface_ice = 0.0;
2117 }
2118 }
2119}

References CalcSnowEvaporation().

Referenced by CalcSnowEvaporation(), CalcTranspiration(), and solve().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ CalcSoilEvapoPercolation()

void ldndc::WatercycleDNDC::CalcSoilEvapoPercolation ( )
private
Parameters
[in]None
[out]None
Returns
None
2227{
2228 // reset all water fluxes
2229 for (size_t sl = 0; sl < m_soillayers->soil_layer_cnt(); ++sl)
2230 {
2231 m_wc.percolation_sl[sl] = 0.0;
2232 }
2233
2234 // fill groundwater layer with water
2235 for (int sl = m_soillayers->soil_layer_cnt() - 1; sl >= 0; --sl)
2236 {
2237 if (cbm::flt_greater(sc_.depth_sl[sl], ground_water_table))
2238 {
2239 // ice volume is excepted from groundwater
2240 double const ice_vol(wc_.ice_sl[sl] / cbm::DICE);
2241 if (cbm::flt_greater(sc_.poro_sl[sl], ice_vol))
2242 {
2243 // new water in the layer is generated from groundwater
2244 double const gw_access(cbm::bound_min(0.0, (sc_.poro_sl[sl] - ice_vol - wc_.wc_sl[sl]) * sc_.h_sl[sl] ));
2245 wc_.wc_sl[sl] += gw_access / sc_.h_sl[sl];
2246 wc_.accumulated_groundwater_access += gw_access;
2247 }
2248 }
2249 else
2250 {
2251 break;
2252 }
2253 }
2254
2255 for (size_t sl = 0; sl < m_soillayers->soil_layer_cnt(); ++sl)
2256 {
2257 // the first soil (litter) layer is filled with surface water up to its capacity
2258 if (sl == 0)
2259 {
2260 double delta_wc(0.0);
2261 double const wc_old(wc_.wc_sl[sl]);
2262 double const wc_cap( (sc_.poro_sl[sl] - wc_.wc_sl[sl]) * sc_.h_sl[sl]); // uptake capacity in first soil layer [mm]
2263
2264 if (cbm::flt_greater(wc_.surface_water, wc_cap))
2265 {
2266 wc_.wc_sl[sl] = sc_.poro_sl[sl];
2267 delta_wc = cbm::bound_max(wc_.surface_water, (cbm::bound_min(0.0, wc_.wc_sl[sl] - wc_old) * sc_.h_sl[sl]));
2268 wc_.surface_water -= delta_wc;
2269 }
2270 else
2271 {
2272 wc_.wc_sl[sl] = (wc_.wc_sl[sl] * sc_.h_sl[sl] + wc_.surface_water) / sc_.h_sl[sl];
2273 delta_wc = cbm::bound_max(wc_.surface_water, (cbm::bound_min(0.0, wc_.wc_sl[sl] - wc_old) * sc_.h_sl[sl]));
2274 wc_.surface_water = 0.0;
2275 }
2276
2277 wc_.accumulated_infiltration += delta_wc;
2278 }
2279 // other soil layers get updated by percolation rates from the layer above
2280 else
2281 {
2282 wc_.wc_sl[sl] += m_wc.percolation_sl[sl - 1] / sc_.h_sl[sl];
2283 }
2284
2285 // percolation for all layers below groundwater
2286 if (cbm::flt_greater(sc_.depth_sl[sl], ground_water_table))
2287 {
2288
2289 // in case groundwater reaches the soil surface, the first outflux is estimated by saturated water flow
2290 if (sl == 0)
2291 {
2292 m_wc.percolation_sl[sl] = WaterFlowSaturated(sl, get_impedance_factor(sl));
2293 }
2294 // other soil layers are assumed to percolate by the same rate as the last non-groundwater layer
2295 else
2296 {
2297 m_wc.percolation_sl[sl] = m_wc.percolation_sl[sl - 1];
2298 }
2299
2300 }
2301 // in all other cases percolation
2302 else if (sl + 1 == m_soillayers->soil_layer_cnt())
2303 {
2304 m_wc.percolation_sl[sl] = 0.0;
2305 if (cbm::flt_greater_zero(wc_.sks_sl[sl]))
2306 {
2307 if (cbm::flt_greater(wc_.wc_sl[sl], sc_.poro_sl[sl]) &&
2308 cbm::flt_greater(wc_.wc_sl[sl], wc_.wc_fc_sl[sl])) //security, should be always true if first condition is met
2309 {
2310 //maximum allowed flow until field capacity
2311 double const max_flow((wc_.wc_sl[sl] - wc_.wc_fc_sl[sl]) * sc_.h_sl[sl]);
2312 m_wc.percolation_sl[sl] = cbm::bound_max(WaterFlowSaturated(sl, get_impedance_factor(sl)), max_flow);
2313 }
2314
2315 //water flux above field capacity
2316 else if (cbm::flt_greater_equal(wc_.wc_sl[sl] + wc_.ice_sl[sl], wc_.wc_fc_sl[sl]))
2317 {
2318 m_wc.percolation_sl[sl] = WaterFlowAboveFieldCapacity(sl, get_impedance_factor(sl));
2319 }
2320
2321 //water flux below field capacity and above wilting point
2322 else if ((sl > 0) && cbm::flt_greater_zero(m_wc.percolation_sl[sl - 1]) &&
2323 cbm::flt_greater(wc_.wc_sl[sl], wc_.wc_wp_sl[sl]))
2324 {
2325 m_wc.percolation_sl[sl] = m_sipar->FPERCOL() * m_wc.percolation_sl[sl - 1];
2326 }
2327
2328 //bound water flux by defined maximal percolation rate (e.g., during flooding events)
2329 m_wc.percolation_sl[sl] = cbm::bound_max(m_wc.percolation_sl[sl], max_percolation);
2330 }
2331 }
2332
2333 //infiltrating water in current time step is exceeding available pore space of last time step
2334 //second condition: security, should be always true if first condition is met
2335 else if (cbm::flt_greater(wc_.wc_sl[sl], sc_.poro_sl[sl]) &&
2336 cbm::flt_greater(wc_.wc_sl[sl], wc_.wc_fc_sl[sl]))
2337 {
2338 //maximum allowed flow until field capacity
2339 double const max_flow((wc_.wc_sl[sl] - wc_.wc_fc_sl[sl]) * sc_.h_sl[sl]);
2340 m_wc.percolation_sl[sl] = cbm::bound_max(WaterFlowSaturated(sl, get_impedance_factor(sl)),
2341 max_flow);
2342 }
2343 else if (cbm::flt_greater_equal(wc_.wc_sl[sl] + wc_.ice_sl[sl], wc_.wc_fc_sl[sl]))
2344 {
2345 m_wc.percolation_sl[sl] = WaterFlowAboveFieldCapacity(sl, get_impedance_factor(sl));
2346 }
2347 else if (cbm::flt_greater(wc_.wc_sl[sl], wc_.wc_wp_sl[sl]))
2348 {
2349 m_wc.percolation_sl[sl] = WaterFlowCapillaryRise(sl, get_impedance_factor(sl));
2350 }
2351 else
2352 {
2353 m_wc.percolation_sl[sl] = 0.0;
2354 }
2355
2356
2357 // water content must be greater or equal wilting point
2358 if (cbm::flt_less((wc_.wc_sl[sl] + wc_.ice_sl[sl]) * sc_.h_sl[sl] - m_wc.percolation_sl[sl], wc_.wc_wp_sl[sl] * sc_.h_sl[sl]) &&
2359 cbm::flt_greater_zero(m_wc.percolation_sl[sl]))
2360 {
2361 m_wc.percolation_sl[sl] = cbm::bound(0.0,
2362 (wc_.wc_sl[sl] + wc_.ice_sl[sl] - wc_.wc_wp_sl[sl]) * sc_.h_sl[sl],
2363 m_wc.percolation_sl[sl]);
2364 }
2365
2366 // Update of the current soil layer water content
2367 wc_.wc_sl[sl] -= m_wc.percolation_sl[sl] / sc_.h_sl[sl];
2368
2370 }
2371
2372 /* Correct water content and percolation if water content is above porosity
2373 * If the layer below cannot take up all of the water assigned for percolation,
2374 * so if the air filled pore space is not sufficient,
2375 * the water remains in the upper layer.
2376 */
2377 for (size_t sl = m_soillayers->soil_layer_cnt() - 1; sl > 0; sl--)
2378 {
2379 double const ice_vol(wc_.ice_sl[sl] / cbm::DICE);
2380 if (cbm::flt_greater(wc_.wc_sl[sl] + ice_vol, sc_.poro_sl[sl]))
2381 {
2382 double delta_wc(wc_.wc_sl[sl] + ice_vol - sc_.poro_sl[sl]);
2383 if (cbm::flt_greater_equal(ice_vol, sc_.poro_sl[sl]))
2384 {
2385 delta_wc = wc_.wc_sl[sl];
2386 wc_.wc_sl[sl] = 0.0;
2387 }
2388 else
2389 {
2390 wc_.wc_sl[sl] = sc_.poro_sl[sl] - ice_vol;
2391 }
2392 m_wc.percolation_sl[sl - 1] -= delta_wc * sc_.h_sl[sl];
2393 wc_.wc_sl[sl - 1] += delta_wc * sc_.h_sl[sl] / sc_.h_sl[sl - 1];
2394 }
2395 }
2396
2397 double const ice_vol(wc_.ice_sl[0] / cbm::DICE);
2398 if (cbm::flt_greater(wc_.wc_sl[0] + ice_vol, sc_.poro_sl[0]))
2399 {
2400
2401 double delta_wc(wc_.wc_sl[0] + ice_vol - sc_.poro_sl[0]);
2402 if (cbm::flt_greater_equal(ice_vol, sc_.poro_sl[0]))
2403 {
2404 delta_wc = wc_.wc_sl[0];
2405 wc_.wc_sl[0] = 0.0;
2406 }
2407 else
2408 {
2409 wc_.wc_sl[0] = sc_.poro_sl[0] - ice_vol;
2410 }
2411 wc_.accumulated_infiltration -= delta_wc * sc_.h_sl[0];
2412 wc_.surface_water += delta_wc * sc_.h_sl[0];
2413 }
2414
2415 if (cbm::flt_greater_zero(wc_.surface_water))
2416 {
2418 }
2419
2420 wc_.accumulated_waterflux_sl += m_wc.percolation_sl;
2421 wc_.accumulated_percolation += m_wc.percolation_sl[ m_soillayers->soil_layer_cnt()-1];
2422}
double WaterFlowAboveFieldCapacity(size_t, double const &)
Definition watercycle-dndc.cpp:2442
double WaterFlowCapillaryRise(size_t, double const &)
Similar to WaterFlowBelowFieldCapacity(...) but restricts water flow by water availability from soil ...
Definition watercycle-dndc.cpp:2493
void CalcSurfaceFlux()
Definition watercycle-dndc.cpp:2648
void SoilWaterEvaporation(size_t)
Definition watercycle-dndc.cpp:2155

References CalcSoilEvapoPercolation(), CalcSurfaceFlux(), get_impedance_factor(), SoilWaterEvaporation(), WaterFlowAboveFieldCapacity(), and WaterFlowCapillaryRise().

Referenced by CalcSoilEvapoPercolation(), CalcTranspiration(), and solve().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ CalcSurfaceFlux()

void ldndc::WatercycleDNDC::CalcSurfaceFlux ( )
private
Parameters
[in]None
[out]None
Returns
None
2649{
2650 if ( cbm::flt_greater_zero( m_eventflood.get_bund_height()))
2651 {
2652 if ( cbm::flt_greater( wc_.surface_water, m_eventflood.get_bund_height()))
2653 {
2654 wc_.accumulated_runoff += wc_.surface_water - m_eventflood.get_bund_height();
2655 wc_.surface_water = m_eventflood.get_bund_height();
2656 }
2657 }
2658 else
2659 {
2660 /* runoff fraction per time step equals RO fraction per hour * length of a time step */
2661 double const runoff_fraction(m_sipar->FRUNOFF() * substep_day_fraction());
2662
2663 // - not all surface water is removed within one timestep
2664 if ( cbm::flt_less( runoff_fraction, 1.0))
2665 {
2666 double const runoff( runoff_fraction * wc_.surface_water);
2667 wc_.surface_water -= runoff;
2668 wc_.accumulated_runoff += runoff;
2669 }
2670 // - all surface water is immediatly removed
2671 else
2672 {
2673 wc_.accumulated_runoff += wc_.surface_water;
2674 wc_.surface_water = 0.0;
2675 }
2676 }
2677}
double substep_day_fraction()
Length of a substep as fraction of a day.
Definition watercycle-dndc.cpp:2724

References CalcSurfaceFlux(), and substep_day_fraction().

Referenced by CalcSoilEvapoPercolation(), CalcSurfaceFlux(), and CalcTranspiration().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ CalcSurfaceWaterEvaporation()

void ldndc::WatercycleDNDC::CalcSurfaceWaterEvaporation ( )
private
Parameters
[in]None
[out]None
Returns
None
2062{
2063 if ( cbm::flt_greater_zero( wc_.surface_water) &&
2064 cbm::flt_greater_zero( hr_pot_evapotrans))
2065 {
2066 if ( hr_pot_evapotrans > wc_.surface_water)
2067 {
2068 wc_.accumulated_surfacewaterevaporation += wc_.surface_water;
2069 hr_pot_evapotrans -= wc_.surface_water;
2070 wc_.surface_water = 0.0;
2071 }
2072 else
2073 {
2074 wc_.surface_water -= hr_pot_evapotrans;
2075 wc_.accumulated_surfacewaterevaporation += hr_pot_evapotrans;
2076 hr_pot_evapotrans = 0.0;
2077 }
2078 }
2079}

References CalcSurfaceWaterEvaporation().

Referenced by CalcSurfaceWaterEvaporation(), CalcTranspiration(), and solve().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ CalcTranspiration()

lerr_t ldndc::WatercycleDNDC::CalcTranspiration ( )
private

◆ CalcTranspirationCouvreur()

lerr_t ldndc::WatercycleDNDC::CalcTranspirationCouvreur ( double const & hr_potTransinm)
private
Parameters
[in]None
[out]None
Returns
None
1919{
1920 // hourly water uptake
1921 double hr_uptake( 0.0);
1922
1923 // Calculate the root water uptake and hence the transpiration for every plant individually
1924 for( PlantIterator vt = m_veg->begin(); vt != m_veg->end(); ++vt)
1925 {
1926 MoBiLE_Plant *p = *vt;
1927
1928 // split up the potential transpiration equally to the various plants
1929 double const transpiration_possible_vt( _transpiration_possible * p->mFrt / m_veg->dw_frt() * cbm::CM_IN_M); // in cm
1930
1931 // If some soil layers are frozen and contain only ice, no fluid water is present and the water potential diverges.
1932 // The uptake from these layers is prevented by considering only roots in layers with more water than ice.
1933 // In this case the root distribution needs to be rescaled (otherwise the non-contributing layers would effectively contribute a potential = 0).
1934 double unfrozen_root_fraction( 1.0);
1935
1936 /* TODO: include ice_water_ratio_critical as proper parameter */
1937 double const ice_water_ratio_critical( 0.1);
1938 double eff_soil_water_potential( 0.0);
1939 for( size_t sl = 0; sl < m_soillayers->soil_layer_cnt(); ++sl)
1940 {
1941 // fine root conditions to prevent transpiration without uptake organs, wateruptake from frozen ground
1942 if( cbm::flt_greater_zero( p->fFrt_sl[sl] * p->mFrt))
1943 {
1944 if( cbm::flt_greater_zero( wc_.wc_sl[sl]) &&
1945 cbm::flt_less( wc_.ice_sl[sl] / wc_.wc_sl[sl], ice_water_ratio_critical))
1946 {
1947 // overall water potential in this layer
1948 eff_soil_water_potential += Soillayerwaterhead( sl) * p->fFrt_sl[sl]; // negative
1949 }
1950 else
1951 {
1952 unfrozen_root_fraction -= p->fFrt_sl[sl];
1953 }
1954 }
1955 else
1956 {
1957 // roots are not wireless ;-)
1958 break;
1959 }
1960 }
1961 // normalize the root distribution in the potential, if some soil is frozen
1962 if( cbm::flt_greater_zero( unfrozen_root_fraction))
1963 {
1964 eff_soil_water_potential /= unfrozen_root_fraction;
1965 }
1966
1967 // hydraulic conductivities of plant system
1968 double const k_rs( vt->KRSINIT() * p->mFrt / vt->FRTMASSINIT() * substep_hours); // cm(water)/cm(from pressure)/timestep
1969 double const k_comp( vt->KCOMPINIT() * p->mFrt / vt->FRTMASSINIT() * substep_hours); // cm(water)/cm(from pressure)/timestep
1970
1971 // water potential in the leafs, depending on the potential transpiration
1972 double const leaf_water_potential( cbm::bound_min( vt->HLEAFCRIT(),
1973 eff_soil_water_potential - (transpiration_possible_vt / k_rs))); // cm
1974
1975 if( cbm::flt_greater_zero( leaf_water_potential))
1976 {
1977 KLOGERROR( "Failed to determine transpiration: Non-negative leaf water potential!");
1978 return LDNDC_ERR_FAIL;
1979 }
1980 if( !cbm::flt_greater_zero( eff_soil_water_potential - leaf_water_potential))
1981 {
1982 KLOGERROR( "Failed to determine transpiration!");
1983 return LDNDC_ERR_FAIL;
1984 }
1985
1986 double const effuptake( k_rs * (eff_soil_water_potential - leaf_water_potential)); // cm/timestep
1987 double uptake_tot( 0.0);
1988 double uptakecomp_tot( 0.0);
1989 //double uptakecompabs_tot( 0.0);
1990
1991 for( size_t sl = 0; sl < m_soillayers->soil_layer_cnt(); ++sl)
1992 {
1993 // fine root condition to prevent transpiration without uptake organs
1994 if( cbm::flt_greater_zero( p->fFrt_sl[sl] * p->mFrt))
1995 {
1996 if(cbm::flt_less( wc_.ice_sl[sl]/wc_.wc_sl[sl], ice_water_ratio_critical))
1997 {
1998 double const soil_water_potential( Soillayerwaterhead( sl));
1999 double const compuptake( k_comp * (soil_water_potential - eff_soil_water_potential));
2000 double uptake_layer( (effuptake + compuptake) * p->fFrt_sl[sl]); // absolute value in cm
2001
2002 wc_.accumulated_wateruptake_sl[sl] += uptake_layer * cbm::M_IN_CM;
2003 uptake_tot += uptake_layer; // absolute value in cm
2004 uptakecomp_tot += compuptake * p->fFrt_sl[sl]; // absolute value in cm
2005 //uptakecompabs_tot += fabs(compuptake * p->fFrt_sl[sl]); // absolute value in cm
2006
2007 wc_.wc_sl[sl] -= uptake_layer * cbm::M_IN_CM /sc_.h_sl[sl]; // fraction of water in this layer, in m
2008
2009 if( !cbm::flt_greater_zero( wc_.wc_sl[sl]))
2010 {
2011 KLOGERROR( "In soil layer ", sl, " the water content is negative: ", wc_.wc_sl[sl]);
2012 return LDNDC_ERR_FAIL;
2013 }
2014 }
2015 }
2016 else
2017 {
2018 // roots are not wireless ;-)
2019 break;
2020 }
2021 }
2022
2023 hr_uptake += uptake_tot * cbm::M_IN_CM; // absolute value in m for all species
2024
2025 if( cbm::flt_greater_zero( fabs(uptakecomp_tot))){
2026 KLOGWARN( "Compensatory water uptake ", uptakecomp_tot, " > 0.");
2027 }
2028 if( cbm::flt_greater( uptake_tot, transpiration_possible_vt, 0.0000000001))
2029 {
2030 KLOGERROR( "Total water uptake ", uptake_tot, " larger than potential transpiration ", transpiration_possible_vt);
2031 }
2032 if(cbm::flt_equal_eps(unfrozen_root_fraction, 1.0, 0.0000000001) && !cbm::flt_equal_eps( uptake_tot, transpiration_possible_vt, 0.0000000001) && !cbm::flt_equal_eps( leaf_water_potential, vt->HLEAFCRIT(), 0.0000000001))
2033 {
2034 KLOGERROR( "T ", uptake_tot, "cm < Tpot ", transpiration_possible_vt, "cm even though leaf_water_potential ", leaf_water_potential, " > HLEAFCRIT", vt->HLEAFCRIT());
2035 }
2036 }
2037
2038 // total evaporation demand reduced to apply on other evaporating sources (ground, snow, surface water)
2039 hr_pot_evapotrans = cbm::bound_min(0.0, hr_pot_evapotrans - hr_uptake);
2040
2041 return LDNDC_ERR_OK;
2042}

References CalcTranspirationCouvreur().

Referenced by CalcTranspiration(), and CalcTranspirationCouvreur().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ event_flood()

lerr_t ldndc::WatercycleDNDC::event_flood ( )
private

sets hydrologic conditions during flooding events, e.g.,

  • surface water table
  • bund height
825{
826 lerr_t rc = m_eventflood.solve();
827 if ( rc != LDNDC_ERR_OK)
828 {
829 KLOGERROR("Flooding event handler not successful!");
830 return rc;
831 }
832
833 /* max_percolation is gradually decreased after end of flooding event to inital value */
834 double const maximum_percolation = m_eventflood.get_maximum_percolation();
835 if ( cbm::is_valid( maximum_percolation))
836 {
837 if ( cbm::flt_greater_zero( maximum_percolation))
838 {
839 max_percolation = maximum_percolation * substep_hours / 24.0;
840 }
841 }
842 else
843 {
844 /* time rate for gradual recovery of max_percolation (called once per time step, i.e., substeps_per_timestep substeps) */
845 double const time_rate( substep_day_fraction() * double( substeps_per_timestep) * 0.1);
846 max_percolation -= (max_percolation - WaterFlowSaturated( m_soillayers->soil_layer_cnt()-1, 1.0)) * time_rate;
847 }
848
849 return LDNDC_ERR_OK;
850}

References event_flood(), and substep_day_fraction().

Referenced by event_flood(), and solve().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ get_leaf_water()

double ldndc::WatercycleDNDC::get_leaf_water ( )
private

Collects leaf water from all canopy layers.

Parameters
[in]None
[out]None
Returns
Leaf water
1164{
1165 if ( m_veg->size() > 0u)
1166 {
1167 return wc_.wc_fl.sum();
1168 }
1169 return 0.0;
1170}

References get_leaf_water().

Referenced by balance_check(), CalcInterception(), and get_leaf_water().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ hours_per_time_step()

double ldndc::WatercycleDNDC::hours_per_time_step ( )
private

number of hours per time step

2784{
2785 return substep_hours * substeps_per_timestep;
2786}

References hours_per_time_step().

Referenced by CalcRaining(), CalcTranspiration(), and hours_per_time_step().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ rainfall_intensity()

double ldndc::WatercycleDNDC::rainfall_intensity ( )
private

rainfall intensity, i.e., maximum rainfall/irrigation per hour [m h-1]

101{
102 if ( rainfall_intensity_cache < 0.0)
103 {
104 if ( m_iokcomm->get_input_class< input_class_climate_t >())
105 {
106 rainfall_intensity_cache = m_iokcomm->get_input_class< climate::input_class_climate_t >()->station_info()->rainfall_intensity * cbm::M_IN_MM;
107 }
108 else
109 {
110 rainfall_intensity_cache = ldndc::climate::climate_info_defaults.rainfall_intensity * cbm::M_IN_MM;
111 }
112 }
114}
double rainfall_intensity_cache
Definition watercycle-dndc.h:171
double rainfall_intensity
Definition climatetypes.h:47

References rainfall_intensity(), and rainfall_intensity_cache.

Referenced by CalcIrrigation(), CalcRaining(), and rainfall_intensity().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ SoilWaterEvaporation()

void ldndc::WatercycleDNDC::SoilWaterEvaporation ( size_t _sl)
private
Parameters
[in]_slSoil layer
[out]None
Returns
None
2157{
2158 //percolation and direct evaporation from the (upper) soil
2159 //if there is snow cover, evaporation from soil will be set to 0, and only sublimation is allowed.
2160 double const wc_min_evaporation(m_sipar->WCDNDC_EVALIM_FRAC_WCMIN() * wc_.wc_wp_sl[_sl]);
2161 if ( cbm::flt_less( sc_.depth_sl[_sl] - sc_.h_sl[_sl], m_sipar->EVALIM()) &&
2162 cbm::flt_greater( wc_.wc_sl[_sl], wc_min_evaporation) &&
2163 cbm::flt_greater_zero( hr_pot_evapotrans))
2164 {
2165 //limiting factor for soil water infiltration
2166 double const f_clay( 1.0 + m_sipar->SLOPE_CLAYF() * std::min(1.0 - sc_.fcorg_sl[_sl] / cbm::CCORG, sc_.clay_sl[_sl]));
2167 double const f_water( cbm::bound( 0.0,
2168 pow( (wc_.wc_sl[_sl] - wc_min_evaporation) / wc_.wc_fc_sl[_sl], f_clay),
2169 1.0));
2170 double const h_rest( std::min( sc_.h_sl[_sl], m_sipar->EVALIM() - ( sc_.depth_sl[_sl] - sc_.h_sl[_sl])));
2171 double const f_depth( h_rest / m_sipar->EVALIM() * std::max(0.0,
2172 1.0 - std::min((double)m_sipar->EVALIM(),
2173 sc_.depth_sl[_sl] - ( 0.5 * sc_.h_sl[_sl]))
2174 / m_sipar->EVALIM()));
2175 double const soilevaporation_max( cbm::bound_max( f_water * wc_.wc_sl[_sl] * sc_.h_sl[_sl], hr_pot_evapotrans));
2176
2177 double const soilevaporation( cbm::bound( 0.0,
2178 hr_pot_evapotrans * f_depth * f_water,
2179 soilevaporation_max));
2180
2181 //update of the current soil layer water content
2182 wc_.wc_sl[_sl] -= soilevaporation / sc_.h_sl[_sl];
2183 wc_.accumulated_soilevaporation += soilevaporation;
2184 hr_pot_evapotrans = cbm::bound_min( 0.0, hr_pot_evapotrans - soilevaporation);
2185 }
2186}

References SoilWaterEvaporation().

Referenced by CalcSoilEvapoPercolation(), CalcTranspiration(), and SoilWaterEvaporation().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ substep_day_fraction()

double ldndc::WatercycleDNDC::substep_day_fraction ( )
private

Length of a substep as fraction of a day.

Length of a substep as fraction of a day (used to convert daily rates to rates per substep)

2725{
2726 return (substep_hours / 24.0);
2727}

References substep_day_fraction().

Referenced by CalcSurfaceFlux(), CalcTranspiration(), event_flood(), substep_day_fraction(), WatercycleDNDC_preferential_flow(), and WaterFlowAboveFieldCapacity().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ WatercycleDNDC_preferential_flow()

void ldndc::WatercycleDNDC::WatercycleDNDC_preferential_flow ( )
private
Parameters
[in]None
[out]None
Returns
None
2538{
2539 //BY_PASSF is defined as the fraction of surface water that passes per hour (0.0 by default)
2540 if ( cbm::flt_greater_zero(m_sipar->BY_PASSF()) &&
2541 cbm::flt_greater_zero( wc_.surface_water))
2542 {
2543
2544 double water_def_total( 0.0);
2545 for ( size_t sl = 0; sl < m_soillayers->soil_layer_cnt(); ++sl)
2546 {
2547 //water flux that flow through macro pores can maximally store
2548 water_def_total += cbm::bound_min( 0.0, (wc_.wc_fc_sl[sl] - wc_.wc_sl[sl] - wc_.ice_sl[sl]) * sc_.h_sl[sl]);
2549 }
2550
2551 //duration of timestep (hr-1)
2552 double const fhr( substep_day_fraction());
2553
2554
2555 //fraction of surface water that is put into bypass flow (m timestep-1)
2556 double const bypass_fraction( cbm::bound_max(m_sipar->BY_PASSF() * fhr, 0.99));
2557 double const bypass_water( wc_.surface_water * bypass_fraction);
2558 wc_.surface_water -= bypass_water;
2559 wc_.accumulated_infiltration += bypass_water;
2560
2561 if ( cbm::flt_greater_equal( water_def_total, bypass_water))
2562 {
2563
2564 for ( size_t sl = 0; sl < m_soillayers->soil_layer_cnt(); ++sl)
2565 {
2566 double const water_def( cbm::bound_min( 0.0, (wc_.wc_fc_sl[sl] - wc_.wc_sl[sl] - wc_.ice_sl[sl]) * sc_.h_sl[sl]));
2567 double const water_add( water_def / water_def_total * bypass_water);
2568
2569 wc_.wc_sl[sl] += water_add / sc_.h_sl[sl];
2570 }
2571 }
2572 else
2573 {
2574 for ( size_t sl = 0; sl < m_soillayers->soil_layer_cnt(); ++sl)
2575 {
2576 double const water_def( cbm::bound_min( 0.0, (wc_.wc_fc_sl[sl] - wc_.wc_sl[sl] - wc_.ice_sl[sl]) * sc_.h_sl[sl]));
2577 wc_.wc_sl[sl] += water_def / sc_.h_sl[sl];
2578 }
2579 /* add surplus to percolation out of last soil layer */
2580 wc_.accumulated_percolation += bypass_water - water_def_total;
2581 wc_.accumulated_waterflux_sl[m_soillayers->soil_layer_cnt()-1] += bypass_water - water_def_total;
2582 }
2583 }
2584}

References substep_day_fraction(), and WatercycleDNDC_preferential_flow().

Referenced by CalcTranspiration(), solve(), and WatercycleDNDC_preferential_flow().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ WaterFlowAboveFieldCapacity()

double ldndc::WatercycleDNDC::WaterFlowAboveFieldCapacity ( size_t _sl,
double const & _fact_impedance )
private

factor accounting for different water travelling characteristics in litter and mineral soil

2445{
2446 double const sks( WaterFlowSaturated( _sl, _fact_impedance));
2447 double const fsl( get_fsl( _sl));
2448 double const fhr( substep_day_fraction());
2449
2451 double slope(( _sl < m_soillayers->soil_layers_in_litter_cnt()) ? m_sipar->SLOPE_FF() : m_sipar->SLOPE_MS());
2452
2453 double const travel_time( 1.0 - log10( wc_.sks_sl[_sl] * cbm::M_IN_CM * cbm::MIN_IN_DAY * fhr));
2454 double const travel_time_factor( cbm::flt_greater_zero( travel_time) ? (1.0 - exp(-1.0 / travel_time)) : 1.0);
2455 return cbm::bound_max( cbm::sqr(1.0 - (wc_.wc_fc_sl[_sl] / ((wc_.wc_sl[_sl] + wc_.ice_sl[_sl]) * slope)))
2456 * wc_.wc_sl[_sl] * sc_.h_sl[_sl] * fsl * travel_time_factor * _fact_impedance,
2457 sks);
2458}
double get_fsl(size_t)
Returns factor accounting for soil layer depth. Originally, a constant soil layer depth of 0....
Definition watercycle-dndc.cpp:2620

References get_fsl(), substep_day_fraction(), and WaterFlowAboveFieldCapacity().

Referenced by CalcSoilEvapoPercolation(), CalcTranspiration(), and WaterFlowAboveFieldCapacity().

Here is the call graph for this function:
Here is the caller graph for this function:

Member Data Documentation

◆ rainfall_intensity_cache

double ldndc::WatercycleDNDC::rainfall_intensity_cache
private

rainfall intensity (constant during a run), cached on first use (negative: not yet read) [m h-1]

Referenced by rainfall_intensity().