Referensi teknis modul PySCnomics untuk pengembang. Dokumentasi struktur kode, alur data, dan API kelas utama.
Modul econ/costs.py dan econ/costs_tools.py mendefinisikan struktur data dan logika untuk mengelola biaya. Seluruh biaya direpresentasikan sebagai objek Cost yang membawa metadata klasifikasi dan nilai moneter.
@dataclass
class Cost:
description: str
cost_type: CostType
fluid_type: FluidType
pis_year: int
cost: float
useful_life: Optional[int] = None
depreciation_factor: Optional[float] = None
investment_credit: Optional[float] = None
tax_portion: Optional[float] = None
Aturan validasi bergantung pada:
def validate_pis_year(cost: Cost, project: BaseProject) -> None:
if project.is_strict:
if cost.cost_type == CostType.PRE_ONSTREAM_COST:
assert cost.pis_year <= project.onstream_year
if project.is_pod_1 and cost.cost_type == CostType.SUNK_COST:
assert cost.pis_year < project.approval_year
def classify_costs_by_pod_status(costs, project):
if project.is_pod_1:
sunk = [c for c in costs if c.pis_year < project.approval_year]
pre_onstream = [c for c in costs if c.pis_year >= project.approval_year and c.pis_year <= project.onstream_year]
else:
sunk = []
pre_onstream = [c for c in costs if c.pis_year <= project.onstream_year]
post_onstream = [c for c in costs if c.pis_year > project.onstream_year]
Modul contracts/costrecovery.py mengimplementasikan logika perhitungan kontrak Cost Recovery (CR). Kelas CostRecovery merupakan turunan dari BaseProject dan menangani mekanisme spesifik CR.
def calculate_cost_recovery_pool(self, year: int) -> float:
pool = 0.0
pool += self.get_undepreciated_capital(year)
pool += self.get_unamortized_intangible(year)
pool += self.get_opex(year)
pool += self.get_asr(year)
pool += self.get_lbt(year)
return min(pool, self.cost_recovery_cap * self.gross_revenue[year])
Modul contracts/grosssplit.py mengimplementasikan logika perhitungan kontrak Gross Split (GS). Kelas GrossSplit menangani mekanisme pembagian langsung tanpa cost recovery.
def calculate_split(self) -> float:
split = self.base_split_oil # atau gas
if self.variable_split_enabled:
split += self.variable_split_oil
split += self.get_coefficient_adjustment()
return split
Modul econ/depreciation.py dan econ/dmo.py mengimplementasikan perhitungan depresiasi aset dan kewajiban Domestic Market Obligation (DMO).
def calculate_depreciation(cost, method, year, useful_life, factor=1.0):
if method == DeprMethod.SL:
return (cost / useful_life) * factor
elif method in (DeprMethod.PSC_DB, DeprMethod.DB):
return book_value * rate * factor
elif method == DeprMethod.UOP:
return (production / total_reserves) * cost * factor
Metode yang didukung: Straight Line (SL), Declining Balance (DB/PSC_DB), dan Unit of Production (UOP).
def calculate_dmo(contractor_share, dmo_config, fluid):
if dmo_config.holiday and year <= dmo_config.holiday_duration:
return 0.0
dmo_volume = contractor_share * dmo_config.percentage
dmo_revenue = dmo_volume * dmo_config.price
dmo_fee = dmo_volume * dmo_config.fee
return dmo_revenue - dmo_fee
DMO mengurangi contractor net cashflow karena volume DMO dijual pada harga yang lebih rendah dari harga pasar. DMO Holiday menunda kewajiban ini pada tahun-tahun awal produksi.
Modul econ/indicator.py mengimplementasikan perhitungan indikator keekonomian.
def npv(cashflows, disc_rate):
return sum(cf / (1 + disc_rate) ** t for t, cf in enumerate(cashflows))
Mode yang didukung:
END_YEAR — cashflow di akhir tahunMID_YEAR — cashflow di tengah tahun (eksponen )def irr(cashflows, guess=0.1, tolerance=1e-6, max_iter=1000):
rate = guess
for _ in range(max_iter):
npv_val = npv(cashflows, rate)
d_npv = npv_derivative(cashflows, rate)
rate -= npv_val / d_npv
if abs(npv_val) < tolerance:
return rate
return rate
Metode Newton-Raphson dengan turunan pertama NPV terhadap discount rate.
Modul econ/selection.py mendefinisikan seluruh enum untuk mengontrol pilihan konfigurasi.
class ContractType(str, Enum):
PROJECT = "Project"
PSC_COST_RECOVERY = "PSC Cost Recovery (CR)"
PSC_GROSS_SPLIT = "PSC Gross Split (GS)"
TRANSITION_CR_CR = "Transition CR - CR"
TRANSITION_CR_GS = "Transition CR - GS"
class DeprMethod(str, Enum):
SL = "Straight Line"
PSC_DB = "Declining Balance"
UOP = "Unit of Production"
class FluidType(str, Enum):
OIL = "Oil"
GAS = "Gas"
SULFUR = "Sulfur"
ELECTRICITY = "Electricity"
CO2 = "CO2"
Transition bukan jenis kontrak mandiri — melainkan kombinasi dua kontrak PSC berurutan. Modul contracts/transition.py mengimplementasikan logika peralihan dari satu rezim PSC ke rezim lainnya.
Kelas TransitionContract menyimpan dua kontrak internal: _contract1 (fase pertama) dan _contract2_transitioned (fase kedua). Kedua kontrak dihitung secara independen, lalu hasilnya digabungkan.
class TransitionContract(BaseProject):
def __init__(self, ...):
self._contract1 = CostRecoveryContract(...) # atau GrossSplitContract
self._contract2_transitioned = CostRecoveryContract(...) # atau GrossSplitContract
Varian transition yang didukung (via ContractType):
def run(self):
contract1_result = self._contract1.run()
contract2_result = self._contract2_transitioned.run()
return consolidate(contract1_result, contract2_result)
Setiap fase memiliki start date dan end date-nya sendiri, dikonfigurasi melalui Project Start, Project End, Project Start 2nd, dan Project End 2nd di halaman General.
Data lifting dialokasikan ke masing-masing fase berdasarkan rentang tahunnya. Untuk gas, alokasi juga mempertimbangkan jumlah GSA yang dikonfigurasi, setiap GSA menghasilkan data lifting terpisah yang didistribusikan ke fase yang sesuai.
Modul optimize/ mengimplementasikan analisis sensitivitas, simulasi Monte Carlo, dan optimisasi sekuensial.
def sensitivity_analysis(project, parameters, variations):
results = {}
for param, base_value in parameters.items():
for var in variations:
modified_project = deepcopy(project)
setattr(modified_project, param, base_value * (1 + var))
modified_project.run()
results[(param, var)] = modified_project.npv
return results
Setiap parameter divariasikan secara independen. Hasil berupa matriks NPV untuk setiap kombinasi (parameter, variasi).
def monte_carlo(project, distributions, n_iterations=10000):
npvs = []
for _ in range(n_iterations):
modified = deepcopy(project)
for param, dist in distributions.items():
value = dist.sample()
setattr(modified, param, value)
modified.run()
npvs.append(modified.npv)
return npvs
Distribusi yang didukung: Uniform, Triangular, dan Normal.