Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
108 commits
Select commit Hold shift + click to select a range
1bd9aa8
sync
PooyaHekmati Sep 25, 2025
5abd7f0
Create test_economics.py
PooyaHekmati Sep 25, 2025
571d430
partial budget and framework classes
PooyaHekmati Oct 29, 2025
ec6b9da
remove unused EconomicPreprocessor import and related code
PooyaHekmati Oct 29, 2025
903c445
1
PooyaHekmati Oct 29, 2025
0fa4950
Revert "1"
PooyaHekmati Oct 29, 2025
3dad0c4
add classes
PooyaHekmati Oct 29, 2025
33e5b13
Apply Black Formatting
github-actions[bot] Dec 16, 2025
662428e
initial draft econ map
JoeWaddell Jul 22, 2025
6013246
added changelog entry
JoeWaddell Jul 23, 2025
51d6afe
added comments and updated filenames as provided by sustainability sc…
JoeWaddell Jul 23, 2025
dae4f5d
Update formatting
jadamchick Jul 29, 2025
117dec9
Apply Black Formatting
github-actions[bot] Sep 25, 2025
a0bab4e
Create preprocessing.py
PooyaHekmati Sep 25, 2025
3cb89d6
update jsons
PooyaHekmati Sep 25, 2025
8c49703
Apply Black Formatting
github-actions[bot] Sep 25, 2025
681659f
Update badges on README
PooyaHekmati Sep 25, 2025
b8b6bfe
Update default.json
PooyaHekmati Sep 25, 2025
c722ae0
Apply Black Formatting
github-actions[bot] Sep 25, 2025
9566d00
Update preprocessing.py
PooyaHekmati Sep 25, 2025
822a7d0
Apply Black Formatting
github-actions[bot] Sep 25, 2025
3ef11ff
initial economic inputs file - for manual input of some values we don…
JoeWaddell Oct 3, 2025
7bfbe97
starting preprocessing file with FPCM example
JoeWaddell Oct 3, 2025
98e83e1
updated economics_map
JoeWaddell Oct 3, 2025
957b98e
digester production manual input change
JoeWaddell Oct 3, 2025
d5afd1b
minor updates
JoeWaddell Oct 6, 2025
d9eba30
update for econ file based on user input
JoeWaddell Oct 6, 2025
be4abc0
additional nuance
JoeWaddell Oct 6, 2025
4d0bee4
minor change for purchased heifers re: postprocessing
JoeWaddell Oct 9, 2025
e45d5d7
partial budget class
PooyaHekmati Oct 29, 2025
4feeec2
1
PooyaHekmati Oct 29, 2025
f1b755f
Revert "1"
PooyaHekmati Oct 29, 2025
21db864
connect preprocessing and make some logic clearer
PooyaHekmati Oct 29, 2025
210a52a
temp, smoke_test 1
PooyaHekmati Oct 29, 2025
eafdeb2
Refactor DCFRORCalculator to use EconomicEquations and EconomicMetric…
PooyaHekmati Oct 29, 2025
a073c3b
Refactor __init__.py to streamline imports and enhance module clarity
PooyaHekmati Oct 29, 2025
27c378b
Refactor __init__.py to import EconomicFramework and update __all__ d…
PooyaHekmati Oct 29, 2025
6938e22
fix default.json
PooyaHekmati Oct 29, 2025
93bb87a
Add economics_map entry to example freestall dairy metadata
PooyaHekmati Oct 29, 2025
ae35fef
Fix input manager location paths in economics_map.json for consistency
PooyaHekmati Oct 29, 2025
9e78f41
map notes update and metadata tweaks
JoeWaddell Oct 31, 2025
00041b0
properties
PooyaHekmati Nov 17, 2025
15421da
Update example_freestall_dairy_metadata.json
PooyaHekmati Nov 17, 2025
2810f6f
manure_disposal_transport_km
PooyaHekmati Nov 17, 2025
898d736
Apply Black Formatting
github-actions[bot] Dec 17, 2025
d883c66
Apply Black Formatting
github-actions[bot] Dec 18, 2025
a5cf741
Update badges on README
PooyaHekmati Dec 18, 2025
f909318
Merge a5cf7414f14e3da47d13f2d9d2e9dfde65a452f2 into ebcca887ea4e10b0b…
PooyaHekmati Dec 18, 2025
7c2d556
Apply Black Formatting
github-actions[bot] Dec 18, 2025
d9a0970
Update badges on README
PooyaHekmati Dec 18, 2025
b112fac
fix mapping
PooyaHekmati Dec 19, 2025
4b6480b
fixes and docs
PooyaHekmati Dec 19, 2025
b3cc401
Merge branch 'add-support-for-multiple-properties-files' into economics
PooyaHekmati Dec 19, 2025
2381bab
add warnings
PooyaHekmati Dec 19, 2025
b2c92f4
Add economic flowchart diagram in Markdown
PooyaHekmati Jan 1, 2026
9241e87
Refactor economics_map.json to replace 'biophysical_simulation' with …
PooyaHekmati Jan 20, 2026
ff72db5
cached commit
PooyaHekmati Jan 29, 2026
affa263
add preprocessed inputs
PooyaHekmati Feb 3, 2026
d8134cf
rename
PooyaHekmati Feb 3, 2026
45e84dc
Merge pull request #2713 from RuminantFarmSystems/some-fixes
PooyaHekmati Feb 3, 2026
f3a2cde
expose more inputs and add output prefix
PooyaHekmati Feb 23, 2026
b1e1128
Merge branch 'dev' into economics
PooyaHekmati Feb 23, 2026
acfccad
Merge b1e112805eaf8ac4e6024ad6e453989f5ec66e0e into 1bdb84d720227fd2f…
PooyaHekmati Feb 23, 2026
04a449d
Merge branch 'dev' into economics
PooyaHekmati Feb 23, 2026
ac405b6
Apply Black Formatting
github-actions[bot] Feb 23, 2026
51c5989
Merge branch 'economics' of https://github.com/RuminantFarmSystems/Ru…
PooyaHekmati Feb 23, 2026
2e0eb98
Merge 51c598975de08b772a40b7492d7db7e941f59f71 into 1bdb84d720227fd2f…
PooyaHekmati Feb 23, 2026
53cfe5e
Apply Black Formatting
github-actions[bot] Feb 23, 2026
7d2490a
Update badges on README
PooyaHekmati Feb 23, 2026
cf1a0b0
Update EEE_manager.py
PooyaHekmati Feb 26, 2026
bed0c53
Update dcfror.py
PooyaHekmati Feb 27, 2026
21d5ea8
Update preprocessing.py
PooyaHekmati Feb 27, 2026
2044cfa
Adding _default_values_fallback.csv
bradenlimb Mar 5, 2026
f3a9b3e
Updated the definition of dm percentage.
matthew7838 Mar 10, 2026
5b56dc8
Updated changelog.md
matthew7838 Mar 10, 2026
3317332
Merge branch 'dev' into dm-clarify
matthew7838 Mar 10, 2026
8432d58
Merge branch 'dev' into economics
PooyaHekmati Mar 10, 2026
4df4c5e
Merge 8432d586c7917ebba043b3edf3b1aa47ea4961f1 into 009aca1c7efad7331…
PooyaHekmati Mar 10, 2026
08d1270
Apply Black Formatting
github-actions[bot] Mar 10, 2026
0fa6260
Update badges on README
PooyaHekmati Mar 10, 2026
1acf26f
add fallback and fix mapping errors
PooyaHekmati Mar 11, 2026
4144fbe
Merge branch 'economics' of https://github.com/RuminantFarmSystems/Ru…
PooyaHekmati Mar 11, 2026
b5a4fbc
Merge 4144fbedb76cced5bd5f128228a089d589149d67 into 009aca1c7efad7331…
PooyaHekmati Mar 11, 2026
00b84f8
Apply Black Formatting
github-actions[bot] Mar 11, 2026
dfe8617
Update badges on README
PooyaHekmati Mar 11, 2026
407c421
goal seek
PooyaHekmati Mar 11, 2026
af88038
Merge branch 'dev' into dm-clarify
matthew7838 Mar 11, 2026
81112af
Merge pull request #2849 from RuminantFarmSystems/dm-clarify
matthew7838 Mar 11, 2026
f7a0825
Update default.json
PooyaHekmati Mar 11, 2026
ffd029e
fix
PooyaHekmati Mar 11, 2026
b32e2db
Update dcfror.py
PooyaHekmati Mar 11, 2026
b556c62
Merge b32e2db71afab342f5b4dcd6a3a9d97d0ce8863d into 81112aff00f30f568…
PooyaHekmati Mar 11, 2026
2b92130
Apply Black Formatting
github-actions[bot] Mar 11, 2026
206ef93
Update badges on README
PooyaHekmati Mar 11, 2026
ec4d5de
Update framework.py
PooyaHekmati Mar 12, 2026
0c32600
Merge ec4d5de0615e1565d2359e53fa848b146814ce8b into 81112aff00f30f568…
PooyaHekmati Mar 12, 2026
2940407
Apply Black Formatting
github-actions[bot] Mar 12, 2026
47a4fc6
Update mapping.py
PooyaHekmati Mar 12, 2026
83114d0
Merge branch 'economics' of https://github.com/RuminantFarmSystems/Ru…
PooyaHekmati Mar 12, 2026
189cb22
Merge 83114d0806a9e2ef56f244308aeffc117b12b5bc into 81112aff00f30f568…
PooyaHekmati Mar 12, 2026
2662825
Apply Black Formatting
github-actions[bot] Mar 12, 2026
7d91999
Update economic_inputs.json so they will solve with DCFROR goal seek …
bradenlimb Mar 16, 2026
64b4dc0
Update comments to reflect updated economic equations in the design d…
bradenlimb Mar 17, 2026
66fc396
interim
PooyaHekmati Mar 19, 2026
c810f52
revert
PooyaHekmati Mar 19, 2026
87be1d4
Update econ_metadata.json
PooyaHekmati Mar 19, 2026
4cd907e
remove _csv
PooyaHekmati Mar 19, 2026
d6ead56
Update preprocessing.py
PooyaHekmati Mar 19, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 5 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
[![Flake8](https://img.shields.io/badge/Flake8-passed-brightgreen)](https://github.com/RuminantFarmSystems/MASM/actions/workflows/combined_format_lint_test_mypy.yml)
[![Pytest](https://img.shields.io/badge/Pytest-passed-brightgreen)](https://github.com/RuminantFarmSystems/MASM/actions/workflows/combined_format_lint_test_mypy.yml)
[![Coverage](https://img.shields.io/badge/Coverage-99%25-brightgreen)](https://github.com/RuminantFarmSystems/MASM/actions/workflows/combined_format_lint_test_mypy.yml)
[![Mypy](https://img.shields.io/badge/Mypy-1249%20errors-red)](https://github.com/RuminantFarmSystems/MASM/actions/workflows/combined_format_lint_test_mypy.yml)
[![Flake8](https://img.shields.io/badge/Flake8-failed-red)](https://github.com/RuminantFarmSystems/MASM/actions/workflows/combined_format_lint_test_mypy.yml)
[![Pytest](https://img.shields.io/badge/Pytest-failed-red)](https://github.com/RuminantFarmSystems/MASM/actions/workflows/combined_format_lint_test_mypy.yml)
[![Coverage](https://img.shields.io/badge/Coverage-%25-red)](https://github.com/RuminantFarmSystems/MASM/actions/workflows/combined_format_lint_test_mypy.yml)
[![Mypy](https://img.shields.io/badge/Mypy-1401%20errors-red)](https://github.com/RuminantFarmSystems/MASM/actions/workflows/combined_format_lint_test_mypy.yml)



# RuFaS: Ruminant Farm Systems
Expand Down
14 changes: 14 additions & 0 deletions RUFAS/EEE/EEE_manager.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
from RUFAS.input_manager import InputManager
from RUFAS.output_manager import OutputManager

from .emissions import EmissionsEstimator
from .energy import EnergyEstimator
from .economics.framework import EconomicFramework


class EEEManager:
Expand All @@ -23,3 +25,15 @@ def estimate_all() -> None:
om.add_log("Energy Processing", "Starting processing of energy.", info_map)
EnergyEstimator.estimate_all()
om.add_log("Energy Processing", "Completed processing of energy.", info_map)

metadata_loaded = InputManager().load_runtime_metadata("EEE_econ", eager_termination=True)
if metadata_loaded is False:
om.add_error(
"Emissions metadata load failure",
"Failed to load runtime metadata for 'EEE_econ'. Aborting emissions estimation.",
info_map,
)
return
om.add_log("Economics Processing", "Starting processing of economics.", info_map)
EconomicFramework().run_economic_analysis()
om.add_log("Economics Processing", "Completed processing of economics.", info_map)
5 changes: 5 additions & 0 deletions RUFAS/EEE/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
"""Energy, Economics, and Emissions package."""

from .economics import EconomicFramework

__all__ = ["EconomicFramework"]
322 changes: 322 additions & 0 deletions RUFAS/EEE/economics.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,322 @@
from typing import Any, Dict, Tuple
import pandas as pd
import numpy as np
from RUFAS.input_manager import InputManager
from RUFAS.output_manager import OutputManager

# NOTE: ``dcfror.py`` is the maintained implementation described in
# `RuFaS_Economics_Module_Overview_Slides.pdf`. This duplicate remains for
# legacy compatibility and will be removed once users migrate to the dedicated
# module.


class DCFRORCalculator:
"""
A class to compute the Discounted Cash Flow Rate of Return (DCFROR) analysis for RuFaS.
"""

def __init__(self):
self.im = InputManager()
self.om = OutputManager()
self.inputs = self._load_inputs()

def _load_inputs(self) -> Dict[str, Any]:
info_map = {"class": self.__class__.__name__, "function": self._load_inputs.__name__}
try:
inputs: Dict[str, Any] = {
"cost_capital_multiple": self.im.get_data("economic_inputs.capital_costs.capital_cost_breakdown"),
"cost_operational_units": self.im.get_data("economic_inputs.cashflow_inputs.operating_units"),
"cost_operational_unit_cost": self.im.get_data("economic_inputs.cashflow_inputs.operating_unit_costs"),
"units_produced": self.im.get_data("economic_inputs.cashflow_inputs.units_produced"),
"unit_cost": self.im.get_data("economic_inputs.cashflow_inputs.unit_price"),
"loan_interest_rate": self.im.get_data("economic_inputs.cashflow_inputs.loan_interest_rate"),
"loan_term": self.im.get_data("economic_inputs.cashflow_inputs.loan_term"),
"loan_amount": self.im.get_data("economic_inputs.cashflow_inputs.loan_fraction"),
"equity_amount": self.im.get_data("economic_inputs.cashflow_inputs.equity"),
"interest_rate_construction": self.im.get_data("economic_inputs.cashflow_inputs.const_int"),
"construction_term": self.im.get_data("economic_inputs.cashflow_inputs.const_term"),
"construction_finish_pcts": self.im.get_data("economic_inputs.cashflow_inputs.const_rate_i"),
"tax_rate": self.im.get_data("economic_inputs.cashflow_inputs.tax_rate"),
"internal_rate_of_return": self.im.get_data(
"economic_inputs.cashflow_inputs.target_internal_rate_of_return"
),
"project_term": self.im.get_data("economic_inputs.cashflow_inputs.project_term"),
"depreciation_rate": np.array(self.im.get_data("economic_inputs.cashflow_inputs.depreciation_i")),
"enable": self.im.get_data("economic_inputs.cashflow_inputs.enable_dcfror"),
"tax_credit_used": self.im.get_data("economic_inputs.cashflow_inputs.tax_credit_used"),
"tax_credit_revenue": self.im.get_data("economic_inputs.cashflow_inputs.tax_credit_revenue"),
}
inputs["cost_capital_multiple"].set_index("Item", inplace=True)
return inputs
except KeyError as e:
self.om.add_error("MissingInputKey", f"Missing input key: {str(e)}", info_map)
raise

def calculate(self) -> None:
"""
Main function to execute DCFROR calculation and export results.
"""
info_map = {"class": self.__class__.__name__, "function": self.calculate.__name__}

if not self.inputs.get("enable", False):
self.om.add_log("DCFROR calculation skipped due to configuration flag.", info_map)
return

self.om.add_log("Starting DCFROR calculation", info_map)

try:
cost_inputs = self._prepare_costs()
financing = self._prepare_financing()
cash_flows, cash_flow_df = self._compute_cash_flows(**cost_inputs, **financing)
self._export_results(cash_flows, cash_flow_df, financing["internal_rate_of_return"])
except Exception as e:
self.om.add_error("DCFRORCalculationFailed", f"DCFROR calculation failed: {str(e)}", info_map)
raise

def _prepare_costs(self) -> Dict[str, Any]:
"""
Calculate total capital cost including construction interest, and extract revenue and cost arrays.

Returns
-------
dict
Dictionary containing project term, capital cost, revenue array, and operating cost array.
"""
info_map = {"class": self.__class__.__name__, "function": self._prepare_costs.__name__}

cap_breakdown = self.inputs["cost_capital_multiple"]
if cap_breakdown.empty:
self.om.add_warning("EmptyCapitalCostBreakdown", "Capital cost breakdown is empty", info_map)

base_cost = cap_breakdown["Cost"].sum()
if base_cost == 0:
self.om.add_warning("ZeroCapitalCost", "Total capital cost is zero", info_map)

interest_rate = self.inputs["interest_rate_construction"]
construction_years = self.inputs["construction_term"]
finish_percentages = self.inputs["construction_finish_pcts"]

annual_expenditures = [base_cost * finish_percentages[i] for i in range(construction_years)]
interest_charges = [sum(annual_expenditures[: i + 1]) * interest_rate for i in range(construction_years)]
capital_cost = base_cost + sum(interest_charges)

operating_costs = (self.inputs["cost_operational_units"] * self.inputs["cost_operational_unit_cost"]).sum(
axis=0
)
revenue = (self.inputs["units_produced"] * self.inputs["unit_cost"]).sum(axis=0)

project_term = self.inputs["project_term"]
if project_term <= 0:
self.om.add_error("InvalidProjectTerm", "Project term must be positive.", info_map)
raise ValueError("Project term must be positive.")

return {
"project_term": project_term,
"capital_cost": capital_cost,
"revenue": revenue[:project_term],
"operating_costs": operating_costs[:project_term],
}

def _prepare_financing(self) -> Dict[str, Any]:
"""
Extract financing parameters for depreciation, loan, and tax.

Returns
-------
dict
Dictionary of financing parameters.
"""
input_dict = self.inputs
depreciation_rate = input_dict["depreciation_rate"]
depreciation_rate = depreciation_rate[~np.isnan(depreciation_rate)]
return {
"depreciation_rate": depreciation_rate,
"dep_term": len(depreciation_rate),
"loan_interest_rate": input_dict["loan_interest_rate"],
"loan_term": input_dict["loan_term"],
"loan_amount": input_dict["loan_amount"],
"equity_amount": input_dict["equity_amount"],
"tax_rate": input_dict["tax_rate"],
"internal_rate_of_return": input_dict["internal_rate_of_return"],
}

def _compute_cash_flows(
self,
project_term: int,
capital_cost: float,
depreciation_rate: np.ndarray,
dep_term: int,
loan_interest_rate: float,
loan_term: int,
loan_amount: float,
equity_amount: float,
revenue: np.ndarray,
operating_costs: np.ndarray,
tax_rate: float,
) -> Tuple[np.ndarray, pd.DataFrame]:
"""
Compute annual cash flows and build a DataFrame with financial details.

Returns
-------
cash_flows : np.ndarray
Net cash flows over the project term.
cash_flow_df : pd.DataFrame
DataFrame containing detailed breakdown for each year.
"""
operating_expense = np.zeros(project_term)
depreciation = np.zeros(project_term)
debt_payment = np.zeros(project_term)
interest_payment = np.zeros(project_term)
earnings_before_tax = np.zeros(project_term)
tax_paid = np.zeros(project_term)
net_income = np.zeros(project_term)
net_cash_flow = np.zeros(project_term)

annual_depreciation = np.zeros(project_term)
for i in range(min(dep_term, project_term)):
annual_depreciation[i] = depreciation_rate[i] * capital_cost

debt_total = loan_amount * capital_cost
annual_debt_payment = np.pmt(loan_interest_rate, loan_term, -debt_total) if loan_term > 0 else 0

carry_forward_limit = 0.8
losses_forward = 0.0

for t in range(project_term):
op_exp = operating_costs[t] if t < len(operating_costs) else 0
depr = annual_depreciation[t] if t < len(annual_depreciation) else 0
dp = annual_debt_payment if t < loan_term else 0
ip = loan_interest_rate * (debt_total - np.sum(debt_payment[:t])) if t < loan_term else 0

operating_expense[t] = op_exp
depreciation[t] = depr
debt_payment[t] = dp
interest_payment[t] = ip

earnings_before_tax[t] = revenue[t] - op_exp - depr - dp - ip

if earnings_before_tax[t] <= 0:
losses_forward += earnings_before_tax[t]
taxable_income = 0
else:
usable_loss = min(-losses_forward * carry_forward_limit, earnings_before_tax[t])
taxable_income = earnings_before_tax[t] - usable_loss
losses_forward += -usable_loss

tax_paid[t] = tax_rate * taxable_income
net_income[t] = earnings_before_tax[t] - tax_paid[t]
net_cash_flow[t] = net_income[t] + depr

cash_flow_df = pd.DataFrame(
{
"Year": np.arange(1, project_term + 1),
"Revenue": revenue,
"Operating Cost": operating_expense,
"Depreciation": depreciation,
"Debt Payment": debt_payment,
"Interest": interest_payment,
"EBT": earnings_before_tax,
"Taxes": tax_paid,
"Net Income": net_income,
"Cash Flow": net_cash_flow,
}
)

return net_cash_flow, cash_flow_df

def _export_results(self, CF: np.ndarray, cash_flow_df: pd.DataFrame, internal_rate_of_return: float) -> None:
"""
Export results to the OutputManager.

Parameters
----------
CF : np.ndarray
Net cash flows.
cash_flow_df : pd.DataFrame
Detailed cash flow breakdown.
internal_rate_of_return : float
Internal Rate of Return.
"""
info_map = {"class": self.__class__.__name__, "function": self.calculate.__name__}
npv = np.npv(internal_rate_of_return, CF)
self.om.add_variable("econ_dcfror_npv", npv, info_map)
self.om.add_variable("econ_dcfror_cash_flow_summary", cash_flow_df.to_dict(orient="list"), info_map)
self.om.add_log("DCFROR calculation completed successfully.", info_map)

def goal_seek(
self,
variable_name: str,
target_npv: float = 0.0,
bounds: Tuple[float, float] = (0.01, 100.0),
tol: float = 1e-6,
max_iter: int = 100,
) -> float:
"""
Iteratively adjusts the specified input variable to achieve the target NPV using binary search.

Parameters
----------
variable_name : str
The name of the input variable to adjust.
target_npv : float, optional
The desired net present value (NPV), by default 0.0.
bounds : Tuple[float, float], optional
Lower and upper bounds for the multiplier applied to the variable, by default (0.01, 100.0).
tol : float, optional
Tolerance for convergence, by default 1e-6.
max_iter : int, optional
Maximum number of iterations to try, by default 100.

Returns
-------
float
The multiplier that achieves the target NPV within tolerance. Returns NaN if not found.
"""
info_map = {"class": self.__class__.__name__, "function": self.goal_seek.__name__}
low, high = bounds

for _ in range(max_iter):
mid = (low + high) / 2
override_inputs = {variable_name: self.inputs[variable_name] * mid}
self.calculate(override_inputs=override_inputs)
npv_result = self.om.filter_variables_pool(
{
"name": "NPV Retrieval",
"description": "Retrieve the latest DCFROR NPV for goal seek.",
"filters": [f"{self.__class__.__name__}.{self.calculate.__name__}.econ_dcfror_npv"],
}
)
npv = npv_result.get(
f"{self.__class__.__name__}.{self.calculate.__name__}.econ_dcfror_npv",
None,
)

if npv is None:
self.om.add_error(
"NPVNotFound",
"NPV not found after DCFROR calculation. Check calculation logic.",
info_map,
)
return float("nan")

if abs(npv - target_npv) < tol:
return mid
elif npv > target_npv:
high = mid
else:
low = mid

self.om.add_error(
"GoalSeekFailed", "Goal seek did not converge within the maximum number of iterations.", info_map
)
return float("nan")


# Hook: Call DCFROR in EEE_manager.py
if __name__ == "__main__":
try:
dcfror = DCFRORCalculator()
dcfror.calculate()
except Exception as e:
OutputManager().add_error(f"DCFROR main execution failed: {str(e)}", {"script": "EEE_manager.py"})
22 changes: 22 additions & 0 deletions RUFAS/EEE/economics/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
"""Economics subpackage for RuFaS.

This package hosts financial analysis tools such as the
Discounted Cash Flow Rate of Return (DCFROR) calculator
and a Partial Budget Analysis (PBA) routine. These are
orchestrated via a Flexible Economic Framework that
selects the appropriate analysis based on input data.
"""

from .framework import EconomicFramework
from .metrics import EconomicMetrics
from .digester_costs import DigesterCostCalculator
from .equations import EconomicEquations
from .dcfror import DCFRORCalculator

__all__ = [
"EconomicFramework",
"EconomicMetrics",
"DigesterCostCalculator",
"EconomicEquations",
"DCFRORCalculator",
]
Loading