from datetime import datetime, timedelta from pyeto import fao,convert from pyeto.fao import svp_from_t, avp_from_rhmean, delta_svp, psy_const,atm_pressure import pandas as pd #1、计算日参考蒸散量 def calculateET0(tmean, tmax, tmin, rh_mean, wind_speed, solar_rad, elevation, lat, doy): # 计算饱和水汽压和实际水汽压 svp_max = svp_from_t(tmax) svp_min = svp_from_t(tmin) svp_mean =(svp_max+svp_min)/2 avp = avp_from_rhmean(svp_max, svp_min, rh_mean) # 计算净辐射(简化版) # 将纬度从度转换为弧度 lat_rad = convert.deg2rad(lat) sol_dec = fao.sol_dec(doy) sha = fao.sunset_hour_angle(lat_rad, sol_dec) ird = fao.inv_rel_dist_earth_sun(doy) et_rad = fao.et_rad(lat_rad, sol_dec, sha, ird) cs_rad = fao.cs_rad(elevation, et_rad) nisr = fao.net_in_sol_rad(solar_rad) nolr = fao.net_out_lw_rad(tmin, tmax, solar_rad, cs_rad, avp) rn = fao.net_rad(nisr, nolr) deltasvp= delta_svp(tmean) psy = psy_const(atm_pressure(elevation)) # 计算ET₀ et0 = fao.fao56_penman_monteith( net_rad=rn, t=tmean, ws=wind_speed, svp=svp_mean, avp=avp, delta_svp=deltasvp, psy=psy ) return et0 def cal_et0_List(planting_date,monitor_date,date_params): et0_list = [] current_date = planting_date while current_date < monitor_date: result_params=date_params[current_date.strftime("%Y-%m-%d")] et0=calculateET0(result_params['tmean'], result_params['tmax'], result_params['tmin'], result_params['rh_mean'], result_params['wind_speed'], result_params['solar_rad'], result_params['elevation'],result_params['lat'], result_params['doy']) et0_list.append(et0) current_date += timedelta(days=1) # 增加一天 return et0_list #计算实际每天作物蒸散量 # 1. 定义甘草生育阶段与作物系数(参考苜蓿) # growth_stages = { # "initial": {"duration_days": 20, "kc": 0.40}, # 生长初期 # "development": {"duration_days": 40, "kc": 0.90}, #生长快速期 # "mid": {"duration_days": 20, "kc": 1.1}, # 生长缓慢期 # "growth_slowly": {"duration_days": 30, "kc": 0.6}, # 生长缓慢期 # "dormant_period": {"duration_days": 150, "kc": 0.40}, # 营养储备期 # "fanqing_year2":{"duration_days": 30, "kc": 0.35},#返青期 # "development_year2":{"duration_days": 90, "kc": 0.9}, #第二年快速生长期 # "maturity _year2": {"duration_days": 40, "kc": 0.6}, # 第二年成熟期 # } growth_stages_roots1 = { 'initial': { 'root_length': {5: 0.8, 10: 0.2}, # 主根长:80%概率在0-15cm,平均约8cm 'duration_days': 23, # 6月播种后1-2周 'kc': 0.40, # 作物系数 'time_range': '2025-06-17至2025-07-10', 'hight':0.03,#m 'k_rain':0.25 }, 'development': { 'root_length': {20: 0.6, 45: 0.4}, # 主根长:60%概率在20cm,40%概率在45cm,平均约30cm 'duration_days': 40, # 发芽后-8月20日左右(约2个月) 'kc': 0.9, # 作物系数 'time_range': '2025-07-10至2025-08-20', 'hight':0.15, 'k_rain':0.25 }, 'mid': { 'root_length': {30: 0.5, 80: 0.5}, # 主根长:50%概率在30cm,50%概率在80cm,平均约55cm 'duration_days': 30, # 8月20日-9月下旬 'kc': 1.1, # 作物系数 'time_range': '2025-08-21至2025-09-20', 'hight':0.45, 'soil_evaporation_depth': 0.65, 'k_rain':0.4 }, 'growth_slowly': { 'root_length':{40: 0.4,60:0.4, 80: 0.2}, # 根据数据估算平均主根长 'duration_days': 30, # 9月下旬-10月下旬 'kc': 0.6, 'time_range': '2025-09-21至2025-10-20', 'hight':0.6 }, 'dormant_period': { 'root_length': {40: 0.3,60:0.4, 80: 0.2}, # 冬季休眠,主根基本停止生长 'duration_days': 160, # 11月-次年3月 'kc': 0.4, 'time_range': '2025-10-21至2026-03-30', 'hight':0.8, 'k_rain':0.5 }, 'fanqing_year2': { 'root_length': {40: 0.2,60:0.6, 80: 0.2}, # 返青期主根长变化不大 'duration_days': 30, # 3月下旬-4月下旬 'kc': 0.35, 'time_range': '2026-04-01至2026-04-30', 'hight':0.5, 'k_rain':0.5 }, 'development_year2': { 'root_length': {40: 0.1,60:0.7, 80: 0.2}, # 根据数据估算平均主根长 'duration_days': 90, # 4月下旬-7月下旬 'kc': 0.9, 'time_range': '2026-05-01至2026-07-30', 'hight':0.45, 'k_rain':0.65 }, 'maturity_year2': { 'root_length':{40: 0.1,60:0.6, 80: 0.3}, # 根据数据估算平均主根长 'duration_days': 40, # 7月下旬-8月下旬 'kc': 0.6, 'time_range': '2026-08-01至2026-09-10', 'hight':0.65, 'k_rain':0.5 } } # growth_stages_roots = { # 'initial': { # 'root_length': {5: 0.8, 10: 0.2}, # 主根长:80%概率在0-15cm,平均约8cm # 'duration_days': 20, # 6月播种后1-2周 # 'kc': 0.40, # 作物系数 # 'time_range': '2025-07-08至2025-07-28', # 'hight':0.03,#m # 'k_rain':0.25 # }, # 'development': { # 'root_length': {20: 0.6, 45: 0.4}, # 主根长:60%概率在20cm,40%概率在45cm,平均约30cm # 'duration_days': 42, # 发芽后-8月20日左右(约2个月) # 'kc': 0.9, # 作物系数 # 'time_range': '2025-07-29至2025-09-10', # 'hight':0.15, # 'k_rain':0.25 # }, # 'mid': { # 'root_length': {30: 0.5, 80: 0.5}, # 主根长:50%概率在30cm,50%概率在80cm,平均约55cm # 'duration_days': 30, # 8月20日-9月下旬 # 'kc': 1.1, # 作物系数 # 'time_range': '2025-09-11至2025-10-10', # 'hight':0.45, # 'soil_evaporation_depth': 0.65, # 'k_rain':0.4 # }, # 'growth_slowly': { # 'root_length':{40: 0.4,60:0.4, 80: 0.2}, # 根据数据估算平均主根长 # 'duration_days': 31, # 9月下旬-10月下旬 # 'kc': 0.6, # 'time_range': '2025-10-11至2025-11-10', # 'hight':0.6 # }, # 'dormant_period': { # 'root_length': {40: 0.3,60:0.4, 80: 0.2}, # 冬季休眠,主根基本停止生长 # 'duration_days': 140, # 11月-次年3月 # 'kc': 0.4, # 'time_range': '2025-11-11至2026-03-31', # 'hight':0.8, # 'k_rain':0.5 # }, # 'fanqing_year2': { # 'root_length': {40: 0.2,60:0.6, 80: 0.2}, # 返青期主根长变化不大 # 'duration_days': 30, # 3月下旬-4月下旬 # 'kc': 0.35, # 'time_range': '2026-04-01至2026-04-30', # 'hight':0.5, # 'k_rain':0.5 # }, # 'development_year2': { # 'root_length': {40: 0.1,60:0.7, 80: 0.2}, # 根据数据估算平均主根长 # 'duration_days': 90, # 4月下旬-7月下旬 # 'kc': 0.9, # 'time_range': '2026-05-01至2026-07-30', # 'hight':0.45, # 'k_rain':0.65 # }, # 'maturity_year2': { # 'root_length':{40: 0.1,60:0.6, 80: 0.3}, # 根据数据估算平均主根长 # 'duration_days': 40, # 7月下旬-8月下旬 # 'kc': 0.6, # 'time_range': '2026-08-01至2026-09-10', # 'hight':0.65, # 'k_rain':0.5 # } # } # --------------------------- # 2. 计算每日Kc值(根据生育阶段动态分配) # --------------------------- def get_daily_kc(growth_stages, total_days=423): daily_kc = [] for stage in growth_stages.values(): daily_kc.extend([stage["kc"]] * stage["duration_days"]) # 如果总天数超出定义,用末期Kc填充剩余天数 if len(daily_kc) < total_days: daily_kc.extend([growth_stages["maturity_year2"]["kc"]] * (total_days - len(daily_kc))) return daily_kc[:total_days] # 截断至总天数 # --------------------------- # 3. 计算每日实际蒸散量ETc(得到历史列表) # --------------------------- def calculate_etc_List(et0_daily, planting_date,monitor_date, kc_daily): """ 计算每日ETc(实际蒸散量) :param eto_daily: 每日参考蒸散量列表(单位:cm/d) :param planting_date: 播种日期,格式"YYYY-MM-DD" :param kc_daily: 每日Kc值列表 :return: 日期列表、ETc列表 """ start_date = planting_date delta=monitor_date-start_date days = delta.days dates = [start_date + timedelta(days=i) for i in range(days)] date_strings = [date.strftime("%Y-%m-%d") for date in dates] kc_daily = kc_daily[:days] etc = [eto * kc for eto, kc in zip(et0_daily, kc_daily)] return etc,date_strings,kc_daily #基于作物蒸散和土壤蒸发计算实际蒸散量 def calculate_etc_List_new(et0_daily, planting_date,monitor_date, kc_daily,ke_daily): """ 计算每日ETc(实际蒸散量) :param eto_daily: 每日参考蒸散量列表(单位:cm/d) :param planting_date: 播种日期,格式"YYYY-MM-DD" :param kc_daily: 每日Kc值列表 :return: 日期列表、ETc列表 """ start_date = planting_date delta=monitor_date-start_date days = delta.days dates = [start_date + timedelta(days=i) for i in range(days)] date_strings = [date.strftime("%Y-%m-%d") for date in dates] kc_daily = kc_daily[:days] etc = [eto * (kc+ke) for eto, kc,ke in zip(et0_daily, kc_daily,ke_daily)] return etc,date_strings,kc_daily def calculate_etc_dayily(et0_daily, planting_date,monitor_date, kc_daily): """ 计算每日ETc(实际蒸散量) :param eto_daily: 每日参考蒸散量列表(单位:cm/d) :param planting_date: 播种日期,格式"YYYY-MM-DD" :param kc_daily: 每日Kc值列表 :return: 日期列表、ETc列表 """ start_date = planting_date delta=monitor_date-start_date days = delta.days # dates = [start_date + timedelta(days=i) for i in range(len(kc_daily))] kc_daily = kc_daily[days] etc = et0_daily*kc_daily return etc def export_etc_et0_date(et0_list,etc_list,dates,kc_list,ke_daily,soil_data,rain_daily,irrigate_daily,dk_name): df = pd.DataFrame({ "Date": dates, "ET0": [float(f"{x:.3f}") for x in et0_list], "KC": kc_list, "KE":ke_daily, "ETc": [float(f"{x:.3f}") for x in etc_list], "rain_effective":rain_daily, "I_effective":irrigate_daily, "VWC_fc":0.2884, "VWC_wp":0.1086, "dkbm":dk_name }) df['VWC_SUR'] = 0.0 df['VWC_MID'] = 0.0 df['VWC_BOT'] = 0.0 for i in range(len(df)): date=df.at[i, 'Date'] df.at[i, 'VWC_SUR']=round(float(soil_data["SOIL_MOISTURE_SURFACE"][date]),3) df.at[i, 'VWC_MID'] = round(float(soil_data["SOIL_MOISTURE_MIDDLE"][date]),3) df.at[i, 'VWC_BOT'] = round(float(soil_data["SOIL_MOISTURE_BOTTOM"][date]),3) return df def estimate_mad_from_et(max_et): # 根据最大日蒸散量查表确定MAD值 if max_et < 2: mad = 0.68 elif 2 <= max_et < 3: mad = 0.58 elif 3 <= max_et < 4: mad = 0.48 elif 4 <= max_et < 5: mad = 0.4 elif 5 <= max_et < 6: mad = 0.35 elif 6 <= max_et < 7: mad = 0.33 elif 7 <= max_et < 8: mad = 0.28 elif 8 <= max_et < 9: mad = 0.25 else: # max_et >= 8 mad = 0.23 return mad def calculate_weighted_soil_parameters(root_distribution, soil_layers): # 初始化累计变量 sum_fc = 0.0 sum_wp = 0.0 # 处理每个根系分布层 for root_depth, root_ratio in root_distribution['root_length'].items(): # 找到包含该深度的土壤层 for layer in soil_layers: min_depth, max_depth = layer['depth_range'] if min_depth <= root_depth < max_depth: # 计算该层贡献 (简单比例分配) layer_thickness = max_depth - min_depth depth_in_layer = min(root_depth, max_depth) - min_depth contribution_ratio = depth_in_layer / layer_thickness * root_ratio sum_fc += layer['theta_33'] * contribution_ratio sum_wp += layer['theta_1500'] * contribution_ratio break return sum_fc, sum_wp def cal_stagebyDate(monitor_date,zwlx_growth_stages): for stage, details in zwlx_growth_stages.items(): time_range_str = details['time_range'] if type(monitor_date) is str: monitor_date= datetime.strptime(monitor_date, '%Y-%m-%d') if '-' in time_range_str: start_str, end_str = time_range_str.split('至') start_date = datetime.strptime(start_str, '%Y-%m-%d') end_date = datetime.strptime(end_str, '%Y-%m-%d') if start_date <= monitor_date <= end_date: return stage,details['root_length'],details['hight'],details['k_rain'] raise ValueError(f"监测日期 {monitor_date.strftime('%Y-%m-%d')} 不在任何生育阶段范围内") #根据日期计算主根长度 def calculate_average_root_length(root_length_dist): return sum(length * prob for length, prob in root_length_dist.items()) def cal_soil_fc_wp(): # 土壤分层数据 (33kPa和1500kPa对应的含水率) soil_data = [ {'depth_range': (0, 5), 'theta_33': 0.296, 'theta_1500': 0.103}, {'depth_range': (5, 15), 'theta_33': 0.284, 'theta_1500': 0.104}, {'depth_range': (15, 30), 'theta_33': 0.286, 'theta_1500': 0.105}, {'depth_range': (30, 60), 'theta_33': 0.290, 'theta_1500': 0.114}, {'depth_range': (60, 100), 'theta_33': 0.286, 'theta_1500': 0.117}, {'depth_range': (100, 200), 'theta_33': 0.284, 'theta_1500': 0.114} ] # theta_fc, theta_wp = calculate_weighted_soil_parameters( # growth_stages_roots[stage], # soil_data # ) theta_fc=0.2884 theta_wp=0.1086 return theta_fc,theta_wp if __name__ == "__main__": # #cal_et0_List(datetime(2026, 6, 18),datetime(2026, 6, 22)) et0_daily=[23,45,67,89] # etc,dates,kc_daily=calculate_etc_List(et0_daily, datetime(2026, 6, 18),datetime(2026, 6, 22), get_daily_kc(growth_stages_roots)) # calculate_etc_dayily(45, datetime(2026, 6, 18), datetime(2026, 6, 22), get_daily_kc(growth_stages)) # df=export_etc_et0_date(et0_daily,etc,dates,kc_daily) # print(df) # cal_soil_fc_wp('initial') # stage,details = cal_stagebyDate(datetime(2025, 7, 18)) # print(details) # rd=calculate_average_root_length(details) # print(rd)