def generate_total_potential_report(all_res_names, all_predictions, source_data, pre_history_stats, reserves, output_file='field_potential_report.xlsx'):
"""
Формирует итоговый отчет.
Разделяет Газ Шапки (нефтяные скважины) и Природный Газ (газовые скважины).
"""
res_stats = defaultdict(lambda: {
'fact_gas_cap': 0.0, # ГШ
'fact_nat_gas': 0.0, # СГ
'future_gas_cap': 0.0,
'future_nat_gas': 0.0,
'act_cnt': 0,
'arch_cnt': 0,
'finite_forecast_gas_cap': 0.0,
'finite_forecast_nat_gas': 0.0
})
processed_wells = set()
details = []
# Активные (по которым построился прогноз)
for p in all_predictions:
res = p['reservoir']
well = p['well_name']
well_type = p.get('well_type', 'НЕФ') # Получаем тип из main.py
model_type = p.get('model_type', config.model_type)
processed_wells.add((res, well))
# Данные из предыстории
pre_val_cap = pre_history_stats[res][well]['GAS_CAP_M3']
pre_val_nat = pre_history_stats[res][well]['GAS_NAT_M3']
# Данные периода анализа (Факт)
fact_vec_last = p['fact'][1][-1]
# Определяем, куда записать факт периода
per_val_cap = 0.0
per_val_nat = 0.0
if well_type == 'ГАЗ':
per_val_nat = fact_vec_last
else:
per_val_cap = fact_vec_last
# Будущее (Остаточные)
params = p['params']
q0 = params[0]
raw_a = params[1]
a_val = raw_a
b_val = None
if isinstance(raw_a, tuple):
a_val = raw_a[0]
b_val = raw_a[1]
m0 = p['m0_date']
last_date = p['fact'][0][-1]
fc_Q_vec = p['forecast'][1]
fact_Q_last = p['fact'][1][-1]
forecast_period_val = 0
if fc_Q_vec:
val = fc_Q_vec[-1] - fact_Q_last
if val > 0:
forecast_period_val = val
# Время t на конец факта
from calendar import monthrange
days_in_last = monthrange(last_date.year, last_date.month)[1]
t_end = (last_date - m0).days + days_in_last
# Полный хвост (EUR Future)
# ВАЖНО: передаем model_type и b_val, иначе расчет будет неверен для Hyp/Pow
future_val_total = calculate_eur_future(q0, a_val, t_end, b=b_val, model_type=model_type)
# Распределяем будущее и накопленную
total_hist_cap = pre_val_cap + per_val_cap
total_hist_nat = pre_val_nat + per_val_nat
cur_future_cap = 0.0
cur_future_nat = 0.0
if well_type == 'ГАЗ':
cur_future_nat = future_val_total
res_stats[res]['finite_forecast_nat_gas'] += forecast_period_val
else:
cur_future_cap = future_val_total
res_stats[res]['finite_forecast_gas_cap'] += forecast_period_val
res_stats[res]['fact_gas_cap'] += total_hist_cap
res_stats[res]['fact_nat_gas'] += total_hist_nat
res_stats[res]['future_gas_cap'] += cur_future_cap
res_stats[res]['future_nat_gas'] += cur_future_nat
res_stats[res]['act_cnt'] += 1
# Формируем красивый вывод параметра a
a_str = f"{a_val:.6f}"
if b_val is not None:
a_str += f" (b={b_val:.2f})"
details.append({
'Пласт': res, 'Скважина': well, 'Тип': 'Прогноз', 'Характер': well_type,
'Модель': model_type,
'Факт ГШ': total_hist_cap, 'Факт СГ': total_hist_nat,
'Остаточные ГШ': cur_future_cap, 'Остаточные СГ': cur_future_nat,
'a': a_str
})
# Скважины, которые в прогнозе не участвовали
all_wells = set()
for r in source_data:
for w in source_data[r]:
all_wells.add((r, w))
for r in pre_history_stats:
for w in pre_history_stats[r]:
all_wells.add((r, w))
for res, well in all_wells:
if (res, well) in processed_wells:
continue
# Предыстория
pre_cap = pre_history_stats.get(res, {}).get(well, {}).get('GAS_CAP_M3', 0)
pre_nat = pre_history_stats.get(res, {}).get(well, {}).get('GAS_NAT_M3', 0)
# Период анализа
per_cap = 0
per_nat = 0
well_type_arch = "НЕФ" # По умолчанию
if res in source_data and well in source_data[res]:
mer_list = source_data[res][well]
if mer_list:
# Смотрим последний статус, чтобы понять тип
if mer_list[-1].CHARWORK == 'ГАЗ':
well_type_arch = 'ГАЗ'
per_cap = sum(m.GAS_CAP_M3 for m in mer_list)
per_nat = sum(m.GAS_NAT_M3 for m in mer_list)
hist_cap = pre_cap + per_cap
hist_nat = pre_nat + per_nat
if (hist_cap + hist_nat) > 0:
res_stats[res]['fact_gas_cap'] += hist_cap
res_stats[res]['fact_nat_gas'] += hist_nat
res_stats[res]['arch_cnt'] += 1
details.append({
'Пласт': res, 'Скважина': well, 'Тип': 'Архив', 'Характер': well_type_arch,
'Модель': '-',
'Факт ГШ': hist_cap, 'Факт СГ': hist_nat,
'Всего прогноз ГШ': 0, 'Всего прогноз СГ': 0,
'a': 0
})
# Сводная
summary = []
for res in sorted(res_stats):
s = res_stats[res]
reserves_list = reserves.get(res, {'geo_reserves': 0, 'extract_reserves': 0, 'cum_prod_f6': 0, 'last_year_prod_f6': 0})
total_eur_cap = s['fact_gas_cap'] + s['future_gas_cap']
total_eur_nat = s['fact_nat_gas'] + s['future_nat_gas']
summary.append({
'Пласт': res,
'Активных скважин, шт.': s['act_cnt'],
'Выбывших скважин, шт.': s['arch_cnt'],
# ГШ
'Накопленная добыча ГШ МЭР, млн. м3': s['fact_gas_cap'] / 1000000,
f'Прогноз добычи газа ГШ активным фондом нефтяных скважин на {config.forecast_months} мес., млн. м3': s['finite_forecast_gas_cap'] / 1000000,
'Всего прогнозируемая добыча газа ГШ активным фондом нефтяных скважин, млн. м3': s['future_gas_cap'] / 1000000,
# СГ
'Накопленная добыча природного газа, млн. м3': s['fact_nat_gas'] / 1000000,
f'Прогноз добычи природного газа активным фондом газовых скважин на {config.forecast_months} мес., млн. м3': s['finite_forecast_nat_gas'] / 1000000,
'Всего прогнозируемая добыча природного газа активным фондом газовых скважин, млн. м3': s['future_nat_gas'] / 1000000,
# 6гр
'НГЗ газа ГШ (6гр), млн. м3': reserves_list['geo_reserves'] / 1000000,
'НИЗ газа ГШ (6гр), млн. м3': reserves_list['extract_reserves'] / 1000000,
'Накопленная добыча газа ГШ (6гр), млн. м3': reserves_list['cum_prod_f6'] / 1000000,
'Вовлекаемые запасы, млн. м3': (total_eur_cap + total_eur_nat) / 1000000,
})
with pd.ExcelWriter(output_file) as writer:
pd.DataFrame(summary).to_excel(writer, 'Итоги', index=False)
pd.DataFrame(details).to_excel(writer, 'Детали', index=False)
print(f"Отчет сохранен: {output_file}")