|
|
|
@ -11,21 +11,15 @@ import math
|
|
|
|
import uuid
|
|
|
|
import uuid
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
SIMULATION_ID = os.getenv("PLANT_SIMULATION_ID")
|
|
|
|
|
|
|
|
MASTER_TABLE = "lcc_ms_master_simulations"
|
|
|
|
|
|
|
|
PLANT_TABLE = "lcc_plant_tr_data_simulations"
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def validate_number(n):
|
|
|
|
|
|
|
|
return n if n is not None else 0
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def normalize_db_value(value):
|
|
|
|
def normalize_db_value(value):
|
|
|
|
"""Convert numpy scalars to native Python types for psycopg2."""
|
|
|
|
"""Convert numpy scalars to native Python types for psycopg2."""
|
|
|
|
if isinstance(value, np.generic):
|
|
|
|
if isinstance(value, np.generic):
|
|
|
|
return value.item()
|
|
|
|
return value.item()
|
|
|
|
return value
|
|
|
|
return value
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def validate_number(n):
|
|
|
|
|
|
|
|
return n if n is not None else 0
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def cumulative_npv(values, rate, initial_cf0=0.0):
|
|
|
|
def cumulative_npv(values, rate, initial_cf0=0.0):
|
|
|
|
"""
|
|
|
|
"""
|
|
|
|
@ -76,12 +70,69 @@ def hitung_pv(rate, nper, fv):
|
|
|
|
def hitung_irr(cashflows: list):
|
|
|
|
def hitung_irr(cashflows: list):
|
|
|
|
return npf.irr(cashflows)
|
|
|
|
return npf.irr(cashflows)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def getproyeksilinier(years, values, iterations=30, target_years=None):
|
|
|
|
|
|
|
|
"""
|
|
|
|
|
|
|
|
Jika target_years diberikan (list[int]), fungsi mengembalikan prediksi untuk tahun-tahun tersebut.
|
|
|
|
|
|
|
|
Jika target_years None, perilaku lama tetap: memproyeksikan dari max(years)+1 sepanjang (iterations - len(years)).
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Catatan:
|
|
|
|
|
|
|
|
- Jika data historis < 2 titik, fallback pakai nilai terakhir (atau 0).
|
|
|
|
|
|
|
|
- Jika semua year sama (degenerate), fallback juga.
|
|
|
|
|
|
|
|
"""
|
|
|
|
|
|
|
|
if len(years) != len(values):
|
|
|
|
|
|
|
|
raise ValueError("Panjang years dan values harus sama")
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
# bersihkan pasangan (year, value) yang year-nya None
|
|
|
|
|
|
|
|
pairs = [(int(y), float(v)) for y, v in zip(years, values) if y is not None]
|
|
|
|
|
|
|
|
if not pairs:
|
|
|
|
|
|
|
|
# tidak ada data sama sekali
|
|
|
|
|
|
|
|
if target_years is None:
|
|
|
|
|
|
|
|
return {}
|
|
|
|
|
|
|
|
return {int(y): 0.0 for y in target_years}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
years_clean = [p[0] for p in pairs]
|
|
|
|
|
|
|
|
values_clean = [p[1] for p in pairs]
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
# fallback kalau data tidak cukup untuk regresi
|
|
|
|
|
|
|
|
if len(set(years_clean)) < 2 or len(values_clean) < 2:
|
|
|
|
|
|
|
|
last_val = float(values_clean[-1]) if values_clean else 0.0
|
|
|
|
|
|
|
|
if target_years is None:
|
|
|
|
|
|
|
|
# perilaku lama
|
|
|
|
|
|
|
|
n_hist = len(years_clean)
|
|
|
|
|
|
|
|
if iterations <= n_hist:
|
|
|
|
|
|
|
|
return {}
|
|
|
|
|
|
|
|
start_year = max(years_clean) + 1
|
|
|
|
|
|
|
|
n_projection = iterations - n_hist
|
|
|
|
|
|
|
|
return {start_year + i: last_val for i in range(n_projection)}
|
|
|
|
|
|
|
|
return {int(y): last_val for y in target_years}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
# regresi linier y = a*x + b
|
|
|
|
|
|
|
|
x = np.array(years_clean, dtype=float)
|
|
|
|
|
|
|
|
y = np.array(values_clean, dtype=float)
|
|
|
|
|
|
|
|
a, b = np.polyfit(x, y, 1)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def _predict(yr: int) -> float:
|
|
|
|
|
|
|
|
v = float(a * yr + b)
|
|
|
|
|
|
|
|
# optional: kalau tidak boleh negatif, clamp
|
|
|
|
|
|
|
|
return max(0.0, v)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
# mode target_years: prediksi untuk tahun tertentu
|
|
|
|
|
|
|
|
if target_years is not None:
|
|
|
|
|
|
|
|
return {int(yr): _predict(int(yr)) for yr in target_years}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
# mode lama: generate dari tahun setelah histori
|
|
|
|
|
|
|
|
n_hist = len(years_clean)
|
|
|
|
|
|
|
|
if iterations <= n_hist:
|
|
|
|
|
|
|
|
raise ValueError(
|
|
|
|
|
|
|
|
f"iterations ({iterations}) harus lebih besar dari jumlah data historis ({n_hist})"
|
|
|
|
|
|
|
|
)
|
|
|
|
|
|
|
|
start_year = max(years_clean) + 1
|
|
|
|
|
|
|
|
n_projection = iterations - n_hist
|
|
|
|
|
|
|
|
return {start_year + i: _predict(start_year + i) for i in range(n_projection)}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def main():
|
|
|
|
|
|
|
|
if not SIMULATION_ID:
|
|
|
|
|
|
|
|
print("Environment variable PLANT_SIMULATION_ID is required for simulations.")
|
|
|
|
|
|
|
|
sys.exit(1)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def main():
|
|
|
|
connections = get_connection()
|
|
|
|
connections = get_connection()
|
|
|
|
conn = connections[0] if isinstance(connections, tuple) else connections
|
|
|
|
conn = connections[0] if isinstance(connections, tuple) else connections
|
|
|
|
if conn is None:
|
|
|
|
if conn is None:
|
|
|
|
@ -98,22 +149,18 @@ def main():
|
|
|
|
|
|
|
|
|
|
|
|
cur = conn.cursor()
|
|
|
|
cur = conn.cursor()
|
|
|
|
|
|
|
|
|
|
|
|
# ### LOCKING: kunci tabel simulasi
|
|
|
|
# ### LOCKING: kunci tabel lcc_plant_tr_data
|
|
|
|
# Mode SHARE ROW EXCLUSIVE:
|
|
|
|
# Mode SHARE ROW EXCLUSIVE:
|
|
|
|
# - Menghalangi INSERT/UPDATE/DELETE di tabel ini
|
|
|
|
# - Menghalangi INSERT/UPDATE/DELETE di tabel ini
|
|
|
|
# - Menghalangi lock SHARE ROW EXCLUSIVE lain → script ngantri satu per satu
|
|
|
|
# - Menghalangi lock SHARE ROW EXCLUSIVE lain → script ngantri satu per satu
|
|
|
|
cur.execute(f"LOCK TABLE {PLANT_TABLE} IN SHARE ROW EXCLUSIVE MODE")
|
|
|
|
cur.execute("LOCK TABLE lcc_plant_tr_data IN SHARE ROW EXCLUSIVE MODE")
|
|
|
|
|
|
|
|
|
|
|
|
# 0 Mendapatkan master parameter dari tabel lcc_ms_master
|
|
|
|
# 0 Mendapatkan master parameter dari tabel lcc_ms_master
|
|
|
|
cur.execute(
|
|
|
|
cur.execute("""
|
|
|
|
f"""
|
|
|
|
|
|
|
|
SELECT name,
|
|
|
|
SELECT name,
|
|
|
|
value_num AS value
|
|
|
|
value_num AS value
|
|
|
|
FROM {MASTER_TABLE}
|
|
|
|
FROM lcc_ms_master
|
|
|
|
WHERE simulation_id = %s
|
|
|
|
""")
|
|
|
|
""",
|
|
|
|
|
|
|
|
(SIMULATION_ID,),
|
|
|
|
|
|
|
|
)
|
|
|
|
|
|
|
|
param_rows = cur.fetchall()
|
|
|
|
param_rows = cur.fetchall()
|
|
|
|
param_map = {name: val for (name, val) in param_rows}
|
|
|
|
param_map = {name: val for (name, val) in param_rows}
|
|
|
|
|
|
|
|
|
|
|
|
@ -124,43 +171,29 @@ def main():
|
|
|
|
|
|
|
|
|
|
|
|
# 0-1 Generate New data Projection (is_actual=0) if not exist
|
|
|
|
# 0-1 Generate New data Projection (is_actual=0) if not exist
|
|
|
|
# Hapus data projection lama (is_actual = 0)
|
|
|
|
# Hapus data projection lama (is_actual = 0)
|
|
|
|
cur.execute(
|
|
|
|
cur.execute("""
|
|
|
|
f"""
|
|
|
|
|
|
|
|
DELETE
|
|
|
|
DELETE
|
|
|
|
FROM {PLANT_TABLE}
|
|
|
|
FROM lcc_plant_tr_data
|
|
|
|
WHERE is_actual = 0
|
|
|
|
WHERE is_actual = 0
|
|
|
|
AND simulation_id = %s
|
|
|
|
""")
|
|
|
|
""",
|
|
|
|
|
|
|
|
(SIMULATION_ID,),
|
|
|
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
# Hitung kebutuhan jumlah baris projection baru agar total (actual + projection)
|
|
|
|
# Hitung kebutuhan jumlah baris projection baru agar total (actual + projection)
|
|
|
|
# sama dengan parameter umur_teknis
|
|
|
|
# sama dengan parameter umur_teknis
|
|
|
|
cur.execute(
|
|
|
|
cur.execute("""
|
|
|
|
f"""
|
|
|
|
|
|
|
|
SELECT COALESCE(COUNT(*), 0)
|
|
|
|
SELECT COALESCE(COUNT(*), 0)
|
|
|
|
FROM {PLANT_TABLE}
|
|
|
|
FROM lcc_plant_tr_data
|
|
|
|
WHERE is_actual = 1
|
|
|
|
WHERE is_actual = 1
|
|
|
|
AND simulation_id = %s
|
|
|
|
""")
|
|
|
|
""",
|
|
|
|
|
|
|
|
(SIMULATION_ID,),
|
|
|
|
|
|
|
|
)
|
|
|
|
|
|
|
|
count_actual = cur.fetchone()[0] if cur.rowcount != -1 else 0
|
|
|
|
count_actual = cur.fetchone()[0] if cur.rowcount != -1 else 0
|
|
|
|
|
|
|
|
|
|
|
|
umur_teknis = int(get_param("umur_teknis"))
|
|
|
|
umur_teknis = int(get_param("umur_teknis"))
|
|
|
|
proj_needed = max(0, umur_teknis - int(count_actual))
|
|
|
|
proj_needed = max(0, umur_teknis - int(count_actual))
|
|
|
|
|
|
|
|
|
|
|
|
# Ambil seq dan tahun terakhir sebagai titik awal penomoran berikutnya
|
|
|
|
# Ambil seq dan tahun terakhir sebagai titik awal penomoran berikutnya
|
|
|
|
cur.execute(
|
|
|
|
cur.execute("SELECT COALESCE(MAX(seq), 0) FROM lcc_plant_tr_data")
|
|
|
|
f"SELECT COALESCE(MAX(seq), 0) FROM {PLANT_TABLE} WHERE simulation_id = %s",
|
|
|
|
|
|
|
|
(SIMULATION_ID,),
|
|
|
|
|
|
|
|
)
|
|
|
|
|
|
|
|
last_seq = int(cur.fetchone()[0])
|
|
|
|
last_seq = int(cur.fetchone()[0])
|
|
|
|
|
|
|
|
|
|
|
|
cur.execute(
|
|
|
|
cur.execute("SELECT COALESCE(MAX(tahun), 0) FROM lcc_plant_tr_data")
|
|
|
|
f"SELECT COALESCE(MAX(tahun), 0) FROM {PLANT_TABLE} WHERE simulation_id = %s",
|
|
|
|
|
|
|
|
(SIMULATION_ID,),
|
|
|
|
|
|
|
|
)
|
|
|
|
|
|
|
|
last_year = int(cur.fetchone()[0])
|
|
|
|
last_year = int(cur.fetchone()[0])
|
|
|
|
|
|
|
|
|
|
|
|
# Jika belum ada tahun sama sekali, gunakan tahun_cod-1 sebagai dasar
|
|
|
|
# Jika belum ada tahun sama sekali, gunakan tahun_cod-1 sebagai dasar
|
|
|
|
@ -181,29 +214,62 @@ def main():
|
|
|
|
next_year += 1
|
|
|
|
next_year += 1
|
|
|
|
|
|
|
|
|
|
|
|
insert_sql = (
|
|
|
|
insert_sql = (
|
|
|
|
f"INSERT INTO {PLANT_TABLE} (id, seq, tahun, is_actual, simulation_id, created_at, created_by) "
|
|
|
|
"INSERT INTO lcc_plant_tr_data (id, seq, tahun, is_actual, created_at, created_by) "
|
|
|
|
"VALUES (%s, %s, %s, 0, %s, CURRENT_TIMESTAMP, 'SYS')"
|
|
|
|
"VALUES (%s, %s, %s, 0, CURRENT_TIMESTAMP, 'SYS')"
|
|
|
|
)
|
|
|
|
)
|
|
|
|
sim_values = [(*val, SIMULATION_ID) for val in values]
|
|
|
|
cur.executemany(insert_sql, values)
|
|
|
|
cur.executemany(insert_sql, sim_values)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
# 1. Ambil data awal
|
|
|
|
# 1. Ambil data awal
|
|
|
|
select_sql = f"""
|
|
|
|
select_sql = """
|
|
|
|
SELECT *
|
|
|
|
SELECT *
|
|
|
|
FROM {PLANT_TABLE}
|
|
|
|
FROM lcc_plant_tr_data
|
|
|
|
WHERE simulation_id = %s
|
|
|
|
|
|
|
|
ORDER BY seq \
|
|
|
|
ORDER BY seq \
|
|
|
|
"""
|
|
|
|
"""
|
|
|
|
cur.execute(select_sql, (SIMULATION_ID,))
|
|
|
|
cur.execute(select_sql)
|
|
|
|
|
|
|
|
|
|
|
|
col_names = [desc[0] for desc in cur.description]
|
|
|
|
col_names = [desc[0] for desc in cur.description]
|
|
|
|
rows = cur.fetchall()
|
|
|
|
rows = cur.fetchall()
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
# ============================================================
|
|
|
|
|
|
|
|
# PROYEKSI LINIER untuk COST BD berdasarkan histori (is_actual=1)
|
|
|
|
|
|
|
|
# ============================================================
|
|
|
|
|
|
|
|
hist_years_om, hist_vals_om = [], []
|
|
|
|
|
|
|
|
hist_years_pm, hist_vals_pm = [], []
|
|
|
|
|
|
|
|
hist_years_bd, hist_vals_bd = [], []
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
projection_years = []
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
for r in rows:
|
|
|
|
|
|
|
|
d = dict(zip(col_names, r))
|
|
|
|
|
|
|
|
yr = d.get("tahun")
|
|
|
|
|
|
|
|
if yr is None:
|
|
|
|
|
|
|
|
continue
|
|
|
|
|
|
|
|
yr = int(yr)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if d.get("is_actual") == 1:
|
|
|
|
|
|
|
|
# ambil histori (boleh 0 kalau null)
|
|
|
|
|
|
|
|
hist_years_om.append(yr)
|
|
|
|
|
|
|
|
hist_vals_om.append(validate_number(d.get("cost_bd_om")))
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
hist_years_pm.append(yr)
|
|
|
|
|
|
|
|
hist_vals_pm.append(validate_number(d.get("cost_bd_pm_nonmi")))
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
hist_years_bd.append(yr)
|
|
|
|
|
|
|
|
hist_vals_bd.append(validate_number(d.get("cost_bd_bd")))
|
|
|
|
|
|
|
|
else:
|
|
|
|
|
|
|
|
# tahun-tahun projection yang ingin diprediksi
|
|
|
|
|
|
|
|
projection_years.append(yr)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
# buat mapping prediksi per tahun untuk masing-masing komponen cost BD
|
|
|
|
|
|
|
|
proj_cost_bd_om = getproyeksilinier(hist_years_om, hist_vals_om, target_years=projection_years)
|
|
|
|
|
|
|
|
proj_cost_bd_pm = getproyeksilinier(hist_years_pm, hist_vals_pm, target_years=projection_years)
|
|
|
|
|
|
|
|
proj_cost_bd_bd = getproyeksilinier(hist_years_bd, hist_vals_bd, target_years=projection_years)
|
|
|
|
|
|
|
|
|
|
|
|
print(f"Jumlah baris yang akan di-update: {len(rows)}")
|
|
|
|
print(f"Jumlah baris yang akan di-update: {len(rows)}")
|
|
|
|
|
|
|
|
|
|
|
|
# 2. Siapkan data untuk bulk UPDATE
|
|
|
|
# 2. Siapkan data untuk bulk UPDATE
|
|
|
|
update_sql = f"""
|
|
|
|
update_sql = """
|
|
|
|
UPDATE {PLANT_TABLE}
|
|
|
|
UPDATE lcc_plant_tr_data
|
|
|
|
SET net_capacity_factor = %s,
|
|
|
|
SET net_capacity_factor = %s,
|
|
|
|
eaf = %s,
|
|
|
|
eaf = %s,
|
|
|
|
production_bruto = %s,
|
|
|
|
production_bruto = %s,
|
|
|
|
@ -269,9 +335,9 @@ def main():
|
|
|
|
chart_capex_component_a = %s,
|
|
|
|
chart_capex_component_a = %s,
|
|
|
|
chart_capex_biaya_investasi_tambahan = %s,
|
|
|
|
chart_capex_biaya_investasi_tambahan = %s,
|
|
|
|
chart_capex_acquisition_cost = %s,
|
|
|
|
chart_capex_acquisition_cost = %s,
|
|
|
|
chart_capex_annualized = %s
|
|
|
|
chart_capex_annualized = %s,
|
|
|
|
WHERE seq = %s
|
|
|
|
cost_disposal_cost = %s
|
|
|
|
AND simulation_id = %s \
|
|
|
|
WHERE seq = %s \
|
|
|
|
"""
|
|
|
|
"""
|
|
|
|
|
|
|
|
|
|
|
|
# Ambil parameter dari tabel (fungsi get_param sudah kamu buat sebelumnya)
|
|
|
|
# Ambil parameter dari tabel (fungsi get_param sudah kamu buat sebelumnya)
|
|
|
|
@ -333,32 +399,25 @@ def main():
|
|
|
|
net_capacity_factor_v = 0
|
|
|
|
net_capacity_factor_v = 0
|
|
|
|
eaf_v = 0
|
|
|
|
eaf_v = 0
|
|
|
|
|
|
|
|
|
|
|
|
# Prefetch CF/EAF master data once to avoid repeated queries per row
|
|
|
|
# Prefetch master data CF dan EAF sekali saja di luar loop
|
|
|
|
cur.execute(
|
|
|
|
cur.execute("""
|
|
|
|
"""
|
|
|
|
|
|
|
|
SELECT year as tahun, cf, eaf
|
|
|
|
SELECT year as tahun, cf, eaf
|
|
|
|
FROM lcc_ms_year_data
|
|
|
|
FROM lcc_ms_year_data
|
|
|
|
"""
|
|
|
|
order by year asc
|
|
|
|
)
|
|
|
|
""")
|
|
|
|
year_rows = cur.fetchall()
|
|
|
|
year_rows = cur.fetchall()
|
|
|
|
year_data_map = {
|
|
|
|
year_data_map = {int(t): (validate_number(cf), validate_number(eaf)) for (t, cf, eaf) in year_rows if
|
|
|
|
int(t): (validate_number(cf), validate_number(eaf))
|
|
|
|
t is not None}
|
|
|
|
for (t, cf, eaf) in year_rows
|
|
|
|
|
|
|
|
if t is not None
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
for row in rows:
|
|
|
|
for row in rows:
|
|
|
|
# row adalah tuple sesuai urutan select_sql
|
|
|
|
# row adalah tuple sesuai urutan select_sql
|
|
|
|
data = dict(zip(col_names, row))
|
|
|
|
data = dict(zip(col_names, row))
|
|
|
|
|
|
|
|
|
|
|
|
seq = data["seq"] # primary key / unique key untuk WHERE
|
|
|
|
seq = data["seq"] # primary key / unique key untuk WHERE
|
|
|
|
|
|
|
|
yr = int(data["tahun"]) if data.get("tahun") is not None else None
|
|
|
|
|
|
|
|
|
|
|
|
# Ambil net_capacity_factor dan eaf dari year-data cache berdasarkan tahun
|
|
|
|
# Ambil net_capacity_factor dan eaf dari cache berdasarkan tahun
|
|
|
|
cf_eaf = (
|
|
|
|
cf_eaf = year_data_map.get(int(data["tahun"])) if data.get("tahun") is not None else None
|
|
|
|
year_data_map.get(int(data["tahun"]))
|
|
|
|
|
|
|
|
if data.get("tahun") is not None
|
|
|
|
|
|
|
|
else None
|
|
|
|
|
|
|
|
)
|
|
|
|
|
|
|
|
if cf_eaf:
|
|
|
|
if cf_eaf:
|
|
|
|
net_capacity_factor_v, eaf_v = cf_eaf
|
|
|
|
net_capacity_factor_v, eaf_v = cf_eaf
|
|
|
|
else:
|
|
|
|
else:
|
|
|
|
@ -366,8 +425,6 @@ def main():
|
|
|
|
eaf_v = 0
|
|
|
|
eaf_v = 0
|
|
|
|
|
|
|
|
|
|
|
|
if data["is_actual"] == 1:
|
|
|
|
if data["is_actual"] == 1:
|
|
|
|
net_capacity_factor = validate_number(data["net_capacity_factor"])
|
|
|
|
|
|
|
|
eaf = validate_number(data["eaf"])
|
|
|
|
|
|
|
|
production_bruto = validate_number(data["production_bruto"])
|
|
|
|
production_bruto = validate_number(data["production_bruto"])
|
|
|
|
production_netto = validate_number(data["production_netto"])
|
|
|
|
production_netto = validate_number(data["production_netto"])
|
|
|
|
energy_sales = production_netto
|
|
|
|
energy_sales = production_netto
|
|
|
|
@ -381,8 +438,6 @@ def main():
|
|
|
|
cost_bd_pm_nonmi = validate_number(data["cost_bd_pm_nonmi"])
|
|
|
|
cost_bd_pm_nonmi = validate_number(data["cost_bd_pm_nonmi"])
|
|
|
|
cost_bd_bd = validate_number(data["cost_bd_bd"])
|
|
|
|
cost_bd_bd = validate_number(data["cost_bd_bd"])
|
|
|
|
else:
|
|
|
|
else:
|
|
|
|
net_capacity_factor = net_capacity_factor # last value
|
|
|
|
|
|
|
|
eaf = eaf # last value
|
|
|
|
|
|
|
|
production_netto = net_capacity_factor * 8760 * daya_mampu_netto / 100
|
|
|
|
production_netto = net_capacity_factor * 8760 * daya_mampu_netto / 100
|
|
|
|
production_bruto = production_netto / (100 - (auxiliary + susut_trafo)) * 100
|
|
|
|
production_bruto = production_netto / (100 - (auxiliary + susut_trafo)) * 100
|
|
|
|
energy_sales = production_netto
|
|
|
|
energy_sales = production_netto
|
|
|
|
@ -392,9 +447,15 @@ def main():
|
|
|
|
revenue_c = price_c * production_netto * 1000 / 1000000
|
|
|
|
revenue_c = price_c * production_netto * 1000 / 1000000
|
|
|
|
revenue_d = price_d * production_netto * 1000 / 1000000
|
|
|
|
revenue_d = price_d * production_netto * 1000 / 1000000
|
|
|
|
cost_c_fuel = fuel_consumption * harga_bahan_bakar / 1000000
|
|
|
|
cost_c_fuel = fuel_consumption * harga_bahan_bakar / 1000000
|
|
|
|
cost_bd_om = cost_bd_om # last value
|
|
|
|
# default fallback tetap pakai last value kalau tahun kosong / prediksi tidak ada
|
|
|
|
cost_bd_pm_nonmi = cost_bd_pm_nonmi # last value
|
|
|
|
if yr is not None:
|
|
|
|
cost_bd_bd = cost_bd_bd # last value
|
|
|
|
cost_bd_om = proj_cost_bd_om.get(yr, cost_bd_om)
|
|
|
|
|
|
|
|
cost_bd_pm_nonmi = proj_cost_bd_pm.get(yr, cost_bd_pm_nonmi)
|
|
|
|
|
|
|
|
cost_bd_bd = proj_cost_bd_bd.get(yr, cost_bd_bd)
|
|
|
|
|
|
|
|
else:
|
|
|
|
|
|
|
|
cost_bd_om = cost_bd_om
|
|
|
|
|
|
|
|
cost_bd_pm_nonmi = cost_bd_pm_nonmi
|
|
|
|
|
|
|
|
cost_bd_bd = cost_bd_bd
|
|
|
|
|
|
|
|
|
|
|
|
net_capacity_factor = net_capacity_factor_v
|
|
|
|
net_capacity_factor = net_capacity_factor_v
|
|
|
|
eaf = eaf_v
|
|
|
|
eaf = eaf_v
|
|
|
|
@ -440,6 +501,7 @@ def main():
|
|
|
|
# + cost_a_pinjaman
|
|
|
|
# + cost_a_pinjaman
|
|
|
|
# + cost_a_depreciation
|
|
|
|
# + cost_a_depreciation
|
|
|
|
)
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
|
|
else:
|
|
|
|
else:
|
|
|
|
cost_a_replacement = 0
|
|
|
|
cost_a_replacement = 0
|
|
|
|
cost_a_pm = 0
|
|
|
|
cost_a_pm = 0
|
|
|
|
@ -455,11 +517,15 @@ def main():
|
|
|
|
|
|
|
|
|
|
|
|
chart_capex_component_a = cost_a_acquisition
|
|
|
|
chart_capex_component_a = cost_a_acquisition
|
|
|
|
chart_capex_annualized = cost_a_annualized
|
|
|
|
chart_capex_annualized = cost_a_annualized
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
cost_disposal_cost = -npf.pmt(discount_rate, seq, 0, 0.05 * total_project_cost)
|
|
|
|
else:
|
|
|
|
else:
|
|
|
|
chart_capex_component_a = total_project_cost
|
|
|
|
chart_capex_component_a = total_project_cost
|
|
|
|
chart_capex_annualized = 0
|
|
|
|
chart_capex_annualized = 0
|
|
|
|
cost_a_pv = 0
|
|
|
|
cost_a_pv = 0
|
|
|
|
cost_a_annualized = 0
|
|
|
|
cost_a_annualized = 0
|
|
|
|
|
|
|
|
cost_disposal_cost = 0
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
chart_capex_biaya_investasi_tambahan = 0
|
|
|
|
chart_capex_biaya_investasi_tambahan = 0
|
|
|
|
chart_capex_acquisition_cost = 0
|
|
|
|
chart_capex_acquisition_cost = 0
|
|
|
|
@ -544,6 +610,8 @@ def main():
|
|
|
|
calc4_free_cash_flow_on_equity_array.append(calc4_free_cash_flow_on_equity)
|
|
|
|
calc4_free_cash_flow_on_equity_array.append(calc4_free_cash_flow_on_equity)
|
|
|
|
calc4_discounted_fcf_on_equity = hitung_pv(wacc_on_equity, seq, calc4_free_cash_flow_on_equity)
|
|
|
|
calc4_discounted_fcf_on_equity = hitung_pv(wacc_on_equity, seq, calc4_free_cash_flow_on_equity)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
row_params = (
|
|
|
|
row_params = (
|
|
|
|
net_capacity_factor,
|
|
|
|
net_capacity_factor,
|
|
|
|
eaf,
|
|
|
|
eaf,
|
|
|
|
@ -611,8 +679,8 @@ def main():
|
|
|
|
chart_capex_biaya_investasi_tambahan,
|
|
|
|
chart_capex_biaya_investasi_tambahan,
|
|
|
|
chart_capex_acquisition_cost,
|
|
|
|
chart_capex_acquisition_cost,
|
|
|
|
chart_capex_annualized,
|
|
|
|
chart_capex_annualized,
|
|
|
|
seq,
|
|
|
|
cost_disposal_cost,
|
|
|
|
SIMULATION_ID,
|
|
|
|
seq # <-- penting: ini untuk WHERE
|
|
|
|
)
|
|
|
|
)
|
|
|
|
params.append(tuple(normalize_db_value(v) for v in row_params))
|
|
|
|
params.append(tuple(normalize_db_value(v) for v in row_params))
|
|
|
|
|
|
|
|
|
|
|
|
@ -639,11 +707,10 @@ def main():
|
|
|
|
ROA_TO_L = sum(calc2_earning_after_tax_array_sampai_sekarang) / sum(
|
|
|
|
ROA_TO_L = sum(calc2_earning_after_tax_array_sampai_sekarang) / sum(
|
|
|
|
total_residual_value_array_sampai_sekarang) * 100 # dalam %
|
|
|
|
total_residual_value_array_sampai_sekarang) * 100 # dalam %
|
|
|
|
|
|
|
|
|
|
|
|
update_kpi_sql = f"""
|
|
|
|
update_kpi_sql = """
|
|
|
|
UPDATE {MASTER_TABLE}
|
|
|
|
UPDATE lcc_ms_master
|
|
|
|
SET value_num = %s
|
|
|
|
SET value_num = %s
|
|
|
|
WHERE name = %s
|
|
|
|
WHERE name = %s \
|
|
|
|
AND simulation_id = %s \
|
|
|
|
|
|
|
|
"""
|
|
|
|
"""
|
|
|
|
|
|
|
|
|
|
|
|
kpi_params_raw = [
|
|
|
|
kpi_params_raw = [
|
|
|
|
@ -656,11 +723,7 @@ def main():
|
|
|
|
]
|
|
|
|
]
|
|
|
|
|
|
|
|
|
|
|
|
kpi_params = [
|
|
|
|
kpi_params = [
|
|
|
|
(
|
|
|
|
(None if (value is None or isinstance(value, float) and math.isnan(value)) else value, key)
|
|
|
|
None if (value is None or isinstance(value, float) and math.isnan(value)) else value,
|
|
|
|
|
|
|
|
key,
|
|
|
|
|
|
|
|
SIMULATION_ID,
|
|
|
|
|
|
|
|
)
|
|
|
|
|
|
|
|
for value, key in kpi_params_raw
|
|
|
|
for value, key in kpi_params_raw
|
|
|
|
]
|
|
|
|
]
|
|
|
|
|
|
|
|
|
|
|
|
|