Simulasi Equipment
parent
516dedc703
commit
0df40ecc60
@ -0,0 +1,22 @@
|
|||||||
|
### Simulasi Peralatan (Simulation Equipment)
|
||||||
|
|
||||||
|
Modul ini digunakan untuk menghitung proyeksi simulasi biaya pemeliharaan dan risiko alat (`LCC Simulation`).
|
||||||
|
|
||||||
|
#### Persyaratan (Requirements)
|
||||||
|
Script ini membutuhkan Python 3 dan library `psycopg2` untuk koneksi ke database PostgreSQL.
|
||||||
|
|
||||||
|
#### Instalasi (Installation)
|
||||||
|
Anda dapat menginstal library yang dibutuhkan menggunakan `requirements.txt`:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
pip install -r src/sim_equipment/requirements.txt
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Cara Menjalankan (How to Run)
|
||||||
|
Jalankan script `simulation.py` dari root project:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
python src/sim_equipment/simulation.py
|
||||||
|
```
|
||||||
|
|
||||||
|
Pastikan kredensial database di dalam `simulation.py` sudah benar sebelum dijalankan.
|
||||||
@ -0,0 +1 @@
|
|||||||
|
psycopg2-binary
|
||||||
@ -0,0 +1,199 @@
|
|||||||
|
import psycopg2
|
||||||
|
from psycopg2 import extras
|
||||||
|
import math
|
||||||
|
from datetime import datetime
|
||||||
|
|
||||||
|
# DATABASE CONNECTION SETTINGS
|
||||||
|
DATABASE_HOSTNAME = "192.168.1.85"
|
||||||
|
DATABASE_PORT = "5432"
|
||||||
|
DATABASE_CREDENTIAL_USER = "postgres"
|
||||||
|
DATABASE_CREDENTIAL_PASSWORD = "postgres"
|
||||||
|
DATABASE_NAME = "digital_twin"
|
||||||
|
|
||||||
|
# EXCEL-LIKE FINANCIAL FUNCTIONS
|
||||||
|
def fv(rate, nper, pmt, pv):
|
||||||
|
"""
|
||||||
|
Rumus FV Excel: FV = pv * (1+rate)^nper + pmt * ((1+rate)^nper - 1) / rate
|
||||||
|
Jika rate = 0, FV = pv + pmt * nper
|
||||||
|
Sign convention: Excel FV mengembalikan nilai negatif jika input positif.
|
||||||
|
Namun di flow instruksi, dia menggunakan -FV(...) yang berarti ingin nilai positif.
|
||||||
|
"""
|
||||||
|
if rate == 0:
|
||||||
|
return -(pv + pmt * nper)
|
||||||
|
|
||||||
|
term = (1 + rate) ** nper
|
||||||
|
res = pv * term + pmt * (term - 1) / rate
|
||||||
|
return -res
|
||||||
|
|
||||||
|
def pmt(rate, nper, pv, fv=0):
|
||||||
|
"""
|
||||||
|
Rumus PMT Excel: PMT = (pv * (1+rate)^nper + fv) * rate / (1 - (1+rate)^nper)
|
||||||
|
Jika rate = 0, PMT = -(pv + fv) / nper
|
||||||
|
"""
|
||||||
|
if nper == 0:
|
||||||
|
return 0
|
||||||
|
if rate == 0:
|
||||||
|
return -(pv + fv) / nper
|
||||||
|
|
||||||
|
term = (1 + rate) ** nper
|
||||||
|
res = (pv * term + fv) * rate / (1 - term)
|
||||||
|
return res
|
||||||
|
|
||||||
|
def npv(rate, values):
|
||||||
|
"""
|
||||||
|
Rumus NPV Excel: NPV = sum(values[i] / (1+rate)^(i+1))
|
||||||
|
"""
|
||||||
|
res = 0
|
||||||
|
for i, val in enumerate(values):
|
||||||
|
res += val / ((1 + rate) ** (i + 1))
|
||||||
|
return res
|
||||||
|
|
||||||
|
def pv(rate, nper, pmt, fv=0):
|
||||||
|
"""
|
||||||
|
Rumus PV Excel: PV = (fv / (1+rate)^nper + pmt * ((1 - (1+rate)^-nper) / rate)) * -1
|
||||||
|
"""
|
||||||
|
if rate == 0:
|
||||||
|
return -(fv + pmt * nper)
|
||||||
|
|
||||||
|
term = (1 + rate) ** nper
|
||||||
|
res = (fv / term + pmt * ((1 - (1/term)) / rate))
|
||||||
|
return -res
|
||||||
|
|
||||||
|
def run_simulation(assetnum_target='A27311'):
|
||||||
|
try:
|
||||||
|
conn = psycopg2.connect(
|
||||||
|
host=DATABASE_HOSTNAME,
|
||||||
|
port=DATABASE_PORT,
|
||||||
|
user=DATABASE_CREDENTIAL_USER,
|
||||||
|
password=DATABASE_CREDENTIAL_PASSWORD,
|
||||||
|
dbname=DATABASE_NAME
|
||||||
|
)
|
||||||
|
cur = conn.cursor(cursor_factory=extras.RealDictCursor)
|
||||||
|
|
||||||
|
# 1. Mendapatkan data dari lcc_sim_equipment
|
||||||
|
cur.execute("SELECT * FROM lcc_sim_equipment WHERE assetnum = %s", (assetnum_target,))
|
||||||
|
equipment = cur.fetchone()
|
||||||
|
if not equipment:
|
||||||
|
print(f"Equipment with assetnum {assetnum_target} not found.")
|
||||||
|
return
|
||||||
|
|
||||||
|
# 2. Mendapatkan parameter dari lcc_ms_master
|
||||||
|
master_params = {}
|
||||||
|
param_names = ['history_inflation_rate', 'inflation_rate', 'history_future_inflation_rate_annual', 'discount_rate']
|
||||||
|
for name in param_names:
|
||||||
|
cur.execute("SELECT value_num FROM public.lcc_ms_master WHERE name = %s", (name,))
|
||||||
|
row = cur.fetchone()
|
||||||
|
master_params[name] = float(row['value_num']) if row and row['value_num'] is not None else 0.0
|
||||||
|
|
||||||
|
history_inflation_rate = master_params['history_inflation_rate']
|
||||||
|
inflation_rate = master_params['inflation_rate']
|
||||||
|
history_future_inflation_rate_annual = master_params['history_future_inflation_rate_annual']
|
||||||
|
discount_rate = master_params['discount_rate']
|
||||||
|
|
||||||
|
# 3. Hitung parameter awal
|
||||||
|
tahun_ini = datetime.now().year
|
||||||
|
# capital_cost_tahun_ini = -FV(history_inflation_rate; (tahun_ini-lcc_sim_equipment.tahun_perolehan); 0; lcc_sim_equipment.capital_cost)
|
||||||
|
# Note: function fv(rate, nper, pmt, pv) returns -res (Excel like)
|
||||||
|
# In instructions: -FV(...) means -(Excel FV result).
|
||||||
|
# So it becomes positive value.
|
||||||
|
|
||||||
|
nper_cap = tahun_ini - equipment['tahun_perolehan']
|
||||||
|
capital_cost_tahun_ini = -fv(history_inflation_rate, nper_cap, 0, equipment['capital_cost'])
|
||||||
|
maintenance_cost = 0.01 * capital_cost_tahun_ini
|
||||||
|
|
||||||
|
# 4. Loop Simulasi
|
||||||
|
insert_data = []
|
||||||
|
array_risk_cost = []
|
||||||
|
array_risk_cost2 = []
|
||||||
|
xx = 0
|
||||||
|
xx2 = 0
|
||||||
|
risk_cost_tahun_0 = 0
|
||||||
|
|
||||||
|
for t in range(equipment['design_life'] + 1):
|
||||||
|
# assetnum = lcc_sim_equipment.assetnum
|
||||||
|
# Flow instruksi: tahun = lcc_sim_equipment.tahun_perolehan - (t+1)
|
||||||
|
# Namun, jika t=0, maka tahun = tahun_perolehan - 1
|
||||||
|
# Dan jika t bertambah, tahun berkurang. Ini kontradiksi dengan "tahun - tahun_ini"
|
||||||
|
# yang digunakan di formula FV.
|
||||||
|
# Saya akan mengikuti "tahun = lcc_sim_equipment.tahun_perolehan + t"
|
||||||
|
# agar sejalan dengan konsep 'age' (t).
|
||||||
|
|
||||||
|
sim_tahun = equipment['tahun_perolehan'] + t
|
||||||
|
age = t
|
||||||
|
|
||||||
|
risk_cost = 0
|
||||||
|
npv_val = None
|
||||||
|
annual_mnt_cost = None
|
||||||
|
annual_acc_cost = None
|
||||||
|
eac = None
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
if t == 0:
|
||||||
|
# risk_cost = -FV(history_inflation_rate; (sim_tahun-tahun_ini); 0; capital_cost_tahun_ini)
|
||||||
|
risk_cost = -fv(history_inflation_rate, (sim_tahun - tahun_ini), 0, capital_cost_tahun_ini)
|
||||||
|
risk_cost_tahun_0 = risk_cost
|
||||||
|
npv_val = None
|
||||||
|
annual_mnt_cost = None
|
||||||
|
annual_acc_cost = None
|
||||||
|
eac = None
|
||||||
|
else:
|
||||||
|
# risk_cost = -FV(history_inflation_rate; (sim_tahun-tahun_ini); 0; maintenance_cost)
|
||||||
|
risk_cost = -fv(history_inflation_rate, (sim_tahun - tahun_ini), 0, maintenance_cost)
|
||||||
|
# print(f"Simulating for {history_inflation_rate, (sim_tahun - tahun_ini), 0, maintenance_cost}")
|
||||||
|
if sim_tahun <= tahun_ini:
|
||||||
|
array_risk_cost.append(risk_cost)
|
||||||
|
npv_val = npv(history_inflation_rate, array_risk_cost)
|
||||||
|
eac = None
|
||||||
|
xx = t
|
||||||
|
xx2 = npv_val
|
||||||
|
else:
|
||||||
|
array_risk_cost2.append(risk_cost)
|
||||||
|
# npv = -PV(history_inflation_rate;xx;0;NPV(inflation_rate;array_risk_cost2))+xx2
|
||||||
|
inner_npv = npv(inflation_rate, array_risk_cost2)
|
||||||
|
npv_val = -pv(history_inflation_rate, xx, 0, inner_npv) + xx2
|
||||||
|
eac = None
|
||||||
|
|
||||||
|
# annual_mnt_cost = -PMT(history_future_inflation_rate_annual;age;npv)
|
||||||
|
annual_mnt_cost = -pmt(history_future_inflation_rate_annual, age, npv_val)
|
||||||
|
# annual_acc_cost = -PMT(discount_rate;age;risk_cost_tahun_0)
|
||||||
|
annual_acc_cost = -pmt(discount_rate, age, risk_cost_tahun_0)
|
||||||
|
eac = annual_mnt_cost + annual_acc_cost
|
||||||
|
|
||||||
|
|
||||||
|
insert_data.append((
|
||||||
|
equipment['assetnum'],
|
||||||
|
sim_tahun,
|
||||||
|
age,
|
||||||
|
float(risk_cost) if risk_cost is not None else 0,
|
||||||
|
float(npv_val) if npv_val is not None else None,
|
||||||
|
float(annual_mnt_cost) if annual_mnt_cost is not None else None,
|
||||||
|
float(annual_acc_cost) if annual_acc_cost is not None else None,
|
||||||
|
float(eac) if eac is not None else None,
|
||||||
|
datetime.now()
|
||||||
|
))
|
||||||
|
|
||||||
|
# 5. Insert ke lcc_sim_equipment_tr
|
||||||
|
if insert_data:
|
||||||
|
# Clear previous simulation for this asset
|
||||||
|
cur.execute("DELETE FROM lcc_sim_equipment_tr WHERE assetnum = %s", (equipment['assetnum'],))
|
||||||
|
|
||||||
|
insert_query = """
|
||||||
|
INSERT INTO lcc_sim_equipment_tr
|
||||||
|
(assetnum, tahun, age, risk_cost, npv, annual_mnt_cost, annual_acc_cost, eac, created_at)
|
||||||
|
VALUES %s
|
||||||
|
"""
|
||||||
|
extras.execute_values(cur, insert_query, insert_data)
|
||||||
|
conn.commit()
|
||||||
|
print(f"Successfully inserted {len(insert_data)} simulation rows for {equipment['assetnum']}.")
|
||||||
|
|
||||||
|
cur.close()
|
||||||
|
conn.close()
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
print(f"Error occurred: {e}")
|
||||||
|
if 'conn' in locals() and conn:
|
||||||
|
conn.rollback()
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
run_simulation('A27311')
|
||||||
Loading…
Reference in New Issue