- Engine: Add ppa_revenue_cr, mcp_revenue_cr, tariff, units to PnLRow - Engine: Split PPA vs MCP revenue in P&L computation - Web: Collapsible rows for PPA/MCP Revenue and Opex - Web: Highlighted rows (Total Revenue, EBITDA, EBIT, PBT, PAT) - Web: Units above Tariff in breakdown, bg-blue-50 highlight - Fix sticky column z-index for horizontal scroll - CLAUDE.md: Add project documentation Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
20 KiB
REmodel Codebase Investigation Report
Date: 2026-05-07
Investigator: Claude (Agentic exploration)
1. Executive Summary
REmodel is a full-stack hybrid renewable energy (Solar + Wind + BESS) project finance modeling platform built in Python with a FastAPI backend and Next.js frontend. The project is designed to replace an Excel-macro workflow used for bid preparation at ReNew Power in India, targeting computation of optimal flat tariff and full 25-year project financials in under 30 seconds per scenario.
Key Architecture
┌─────────────────────────────────────────────────────────────────────────┐
│ packages/engine │
│ Python calculation engine (pip-installable) │
│ • Generation (solar, wind, BESS) │
│ • Capex + IDC calculation │
│ • Financial model (P&L, CFS, BS) │
│ • Debt sizing + scheduling │
│ • Tariff solver (brentq) │
│ • CLI driver (Typer) │
└─────────────────────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────────────────┐
│ packages/api │
│ FastAPI + Arq async workers (Redis-backed) │
│ • REST endpoints (scenarios, templates) │
│ • Background task processing │
│ • SQLite + Parquet storage │
│ • SSE for real-time progress │
│ • Excel export │
└─────────────────────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────────────────┐
│ packages/web │
│ Next.js 14 App Router + shadcn/ui + Tailwind │
│ • Scenario list + wizard │
│ • Results dashboard │
│ • KPI visualizations (Recharts) │
│ • Compare scenarios view │
│ • DataGrid (AG Grid) │
└─────────────────────────────────────────────┘
2. Codebase Structure
2.1 Root Directory
/Users/manohar_air/MyProjects/REModel/
├── PROJECT.md # Master specification document
├── README.md # Quick reference
├── docker-compose.yml # Redis service
├── Makefile # Common commands
├── .pre-commit-config.yaml
├── .github/workflows/ # CI/CD
├── packages/ # Monorepo structure
│ ├── engine/ # Python calculation engine
│ ├── api/ # FastAPI backend
│ └── web/ # Next.js frontend
└── sprints/ # Sprint documentation (SPRINT_00-08)
3. Packages/Engine Analysis
3.1 Module Structure (42 Python files)
packages/engine/src/remodel_engine/
├── __init__.py
├── cli.py # Typer CLI (simulate-gen, compute-idc, solve-tariff)
│
├── schemas/ # Pydantic models (single source of truth)
│ ├── __init__.py
│ ├── scenario.py # ScenarioInput, ScenarioResult, KpiSummary
│ ├── capex.py # CostItem, CapexConfig, PhasingCurve, DrawdownCurve
│ ├── debt.py # DebtConfig, DebtYearRow, IRRMetrics
│ ├── financial.py # CommercialConfig, OpexConfig, TaxConfig, Financials
│ └── generation.py # SolarConfig, WindConfig, BessConfig
│
├── catalog/ # Defaults and profile loaders
│ ├── __init__.py
│ ├── defaults.py # Constants: PROJECT_LIFE=25, HOURS=8760
│ ├── loader.py # Solar/wind profile CSV loader (RJ, KA, GJ)
│ └── profiles/ # Bundled 8760-hour CSV files
│
├── generation/ # 25-year generation simulation
│ ├── __init__.py
│ ├── solar.py # DC→AC→clipping→availability→soiling→degradation
│ ├── wind.py # Power curve lookup→shear→wake→availability
│ └── bess_state.py # Degradation + augmentation capacity model
│
├── dispatch/ # Hybrid RTC dispatch
│ ├── __init__.py
│ ├── hybrid_rtc.py # Per-hour charge/discharge logic
│ └── mcp_settlement.py # Merchant sale at MCP
│
├── capex/ # Capital expenditure
│ ├── __init__.py
│ ├── cost_items.py # CostItem catalog → total capex
│ ├── phasing.py # Construction phasing matrix
│ └── idc.py # Interest During Construction (fixed-point)
│
├── commercial/ # Revenue and DSM
│ ├── __init__.py
│ └── ppa.py # Generation aggregation, receivables/payables
│
├── financial/ # 3-statement model
│ ├── __init__.py
│ ├── pnl.py # Revenue → OpEx → EBITDA → Depr → EBIT → Tax → PAT
│ ├── cfs.py # Cash Flow Statement (CFO/CFI/CFF)
│ ├── bs.py # Balance Sheet (reconciliation)
│ ├── depreciation.py # Book SLM + tax WDV schedules
│ ├── tax.py # 115BAA (India) tax computation
│ └── working_capital.py # Receivables, payables, inventory
│
├── debt/ # Debt financing
│ ├── __init__.py
│ ├── sizing.py # Fixed-point debt sizing (D:E, DSCR constraints)
│ └── schedule.py # Repayment shapes (equal principal, EMI, sculpted, balloon)
│
├── irr/ # Financial metrics
│ └── metrics.py # IRR, NPV, LCOE, DSCR, LLCR, PLCR, payback
│
├── solver/ # Solver logic
│ └── tariff.py # Brentq solver for tariff→IRR target
│
├── scenarios/ # Scenario orchestration
│ ├── __init__.py
│ ├── runner.py # Full pipeline (generation→financial→debt→IRR)
│ └── sweep.py # Sensitivity analysis
│
└── io/ # Data export
└── excel_export.py # .xlsx workbook generation
3.2 Key Schema Definitions
ScenarioInput (Top-level input)
class ScenarioInput(BaseModel):
project: ProjectInfo # name, state, capacities, COD
solar: SolarConfig | None # location, DC/AC, losses, degradation
wind: WindConfig | None # location, MW, hub-height
bess: BessConfig | None # MWh, power, RTE, DoD
rtc: RtcConfig | None # contracted RTC MW, MCP toggle
commercial: CommercialConfig # tariff, losses, working capital
capex: CapexConfig # cost items, phasing curves, drawdowns
opex: OpexConfig # O&M, insurance, land lease
debt: DebtConfig # rate, tenor, DSCR constraints
tax: TaxConfig # 115BAA rates
solver: SolverConfig # solve_tariff or fixed_tariff mode
ScenarioResult (Full output)
class ScenarioResult(BaseModel):
inputs: ScenarioInput
status: Literal["queued","running","success","failed"]
solved_tariff: float | None
kpis: KpiSummary # equity_irr, project_irr, DSCR, capex, LCOE, etc.
financials: Financials # pnl_25y, cfs_25y, bs_25y
debt_schedule: list[DebtYearRow]
irr_metrics: IRRMetrics
warnings: list[str]
runtime_s: float
generation_by_year: list[dict]
idc_phasing: dict
3.3 Financial Model Flow
The 3-statement model computes:
-
P&L (Profit & Loss)
- Revenue: generation_MWh × tariff × (1 - losses) / 10^7 → Cr
- OpEx: O&M + insurance + land_lease + AM_fee + misc
- EBITDA = Revenue - OpEx
- Depreciation: Book SLM by asset class
- EBIT = EBITDA - Depreciation
- Interest: From debt schedule
- PBT = EBIT - Interest
- Tax: 115BAA (25.17%) current + deferred
- PAT = PBT - Tax
-
CFS (Cash Flow Statement)
- CFO = PAT + Depreciation - ΔWC
- CFI = 0 (capex in year 0)
- CFF = Debt drawdown - Debt repayment + Equity injection
- Net = CFO + CFI + CFF
-
BS (Balance Sheet)
- Assets = Net block + Cash + Receivables
- Liabilities = Equity + Reserves + LT Debt + Payables + DTL
- Reconciliation enforced (≤₹0.05 Cr difference)
3.4 Debt Sizing Algorithm
The debt sizing module (debt/sizing.py) implements a fixed-point iteration that satisfies three constraints:
- D:E Ratio Cap:
debt ≤ total_capex × de_ratio / (1 + de_ratio) - Min DSCR Constraint:
debt ≤ max satisfying min(DSCR) ≥ min_dscr - Avg DSCR Constraint:
debt ≤ max satisfying avg(DSCR) ≥ avg_dscr
The binding constraint determines the final debt amount.
3.5 Repayment Schedule Shapes
Five debt schedule shapes are supported (in debt/schedule.py):
- equal_principal: Fixed principal each year in repayment period
- equal_installment: EMI (level annuity)
- dscr_sculpted: Principal sculpted so DSCR = avg_dscr each year
- balloon: Interest-only then principal at end
- custom_pct_vector: Caller supplies repayment % per year
3.6 Tariff Solver
The solver (solver/tariff.py) uses Brent's method (scipy.optimize.brentq) to find the tariff that achieves target equity IRR:
- Bracket: [2.0, 8.0] INR/kWh
- Convergence tolerance: 1e-4
- Max iterations: 50
3.7 Generation Models
Solar (generation/solar.py):
- Input: irradiance × capacity_dc → DC power → DC losses
- Inverter efficiency → AC pre-clip
- Clipping at MW_AC (DC/AC ratio > 1)
- Availability × soiling × degradation
- Output: 25 years × 8760 hours = 219,000 rows
Model chain:
irradiance → DC power → DC losses → inverter → AC losses
→ clipping at MW_AC → availability → soiling → degradation
Wind (generation/wind.py):
- Wind speed at reference → hub-height correction (power law shear)
- Power curve lookup → normalized power (0-1)
- Wake losses × electrical losses × availability × degradation
BESS (generation/bess_state.py):
- Degradation:
SOH = max(eol_soh, 1 - cum_cycles/design_cycles × (1 - eol_soh)) - Usable MWh = (nameplate + augmentation) × SOH
- Augmentation steps add capacity at specified years
3.8 Hybrid RTC Dispatch
The dispatch module (dispatch/hybrid_rtc.py) implements hourly charge/discharge:
For each hour h:
gen = solar[h] + wind[h]
surplus = gen - target (positive = excess, negative = deficit)
If surplus ≥ 0:
- Charge BESS with min(surplus, bess_mw, headroom)
- Curtail excess
Else:
- Discharge BESS with min(deficit, available)
- Shortfall = deficit - discharge
3.9 CLI Commands
# Simulate 25-year solar + wind generation
remodel simulate-gen --input scenario.json --output gen.parquet
# Compute IDC (Interest During Construction)
remodel compute-idc --input capex.json --output idc.json
# Run full scenario pipeline
remodel solve-tariff --input scenario.json --output result.json
4. Packages/API Analysis
4.1 Module Structure
packages/api/src/remodel_api/
├── __init__.py
├── main.py # FastAPI app, CORS, lifespan
├── config.py # Settings (environment config)
├── db/
│ ├── __init__.py
│ ├── session.py # SQLAlchemy async session
│ └── models.py # Scenario ORM model
├── routers/
│ ├── __init__.py
│ ├── scenarios.py # CRUD + run + results + SSE + Excel export
│ └── templates.py # Scenario templates
└── workers/
├── __init__.py
├── main.py # Arq worker setup
└── tasks.py # Background scenario execution
4.2 API Endpoints
| Method | Endpoint | Description |
|---|---|---|
| POST | /api/scenarios |
Create scenario, queue for execution |
| GET | /api/scenarios |
List scenarios |
| GET | /api/scenarios/{id} |
Get scenario details |
| PATCH | /api/scenarios/{id}/inputs |
Update inputs, re-queue |
| DELETE | /api/scenarios/{id} |
Archive scenario |
| GET | /api/scenarios/{id}/kpis |
Get KPI results |
| GET | /api/scenarios/{id}/statements |
Get P&L, CFS, BS |
| GET | /api/scenarios/{id}/export/excel |
Export .xlsx |
| GET | /api/scenarios/{id}/events |
SSE progress events |
4.3 Database Schema
class Scenario(Base):
id: str (UUID, primary key)
name: str
status: str ("queued", "running", "success", "failed")
inputs_json: Text (JSON string of ScenarioInput)
kpis_json: Text (JSON string of KpiSummary)
statements_json: Text (JSON string of pnl/cfs/bs)
debt_schedule_json: Text
timeseries_path: str
error_message: str
runtime_s: float
created_at: datetime
archived_at: datetime | None
4.4 Background Processing
- Uses Arq (async Redis-backed task queue)
- ThreadPoolExecutor runs CPU-bound engine in separate thread
- Progress published via Redis pub/sub
- SSE endpoint polls progress
5. Packages/WEB Analysis
5.1 Module Structure
packages/web/
├── app/
│ ├── page.tsx # Scenario list + wizard
│ ├── layout.tsx # Root layout
│ ├── providers.tsx # React Query, etc.
│ ├── globals.css # Tailwind
│ ├── scenarios/
│ │ └── [id]/
│ │ └── page.tsx # Scenario detail view
│ ├── compare/
│ │ └── page.tsx # Scenario comparison
│ └── api-types/ # Auto-generated OpenAPI types
├── components/
│ ├── ui/ # shadcn/ui components
│ ├── DataGrid/ # AG Grid wrapper
│ ├── ScenarioWizard/ # Input wizard form
│ ├── Charts/ # Recharts visualizations
│ └── KpiCard/ # KPI display card
└── lib/
└── api.ts # TypeScript API client
5.2 Frontend Tech Stack
- Next.js 14 App Router
- TypeScript (strict mode)
- shadcn/ui + Tailwind CSS
- TanStack Query (React Query)
- AG Grid Community (DataGrid)
- Recharts (KPIs)
- Zustand (lightweight state)
6. Domain Context (Indian RE Bidding)
6.1 Key Concepts
- PPA: Power Purchase Agreement at tariff (₹/kWh)
- RTC CUF: Round-the-clock capacity factor developer commits to
- DSM: Deviation Settlement Mechanism penalties
- IDC: Interest During Construction
- 115BAA: Indian tax regime (22% + cess = 25.17%)
- DSCR: Debt Service Coverage Ratio
- D:E: Debt-to-Equity ratio (typically 75:25)
6.2 Currency
- Crore (Cr) = 10 million INR (1 Cr = 1,00,00,000)
- Lakh = 100 thousand INR
- Capex quoted in Cr/MW or INR/Wp
7. Sprint Status
| Sprint | Goal | Status |
|---|---|---|
| S0 | Repo setup, CI, API/Web skeleton | Complete |
| S1 | Solar + Wind + BESS generation | Complete |
| S2 | Capex + IDC calculation | In Progress |
| S3 | 3-statement financial model | Pending |
| S4 | Debt sizing + scheduling | Pending |
| S5 | IRR/tariff solver | Pending |
| S6 | Full scenario runner | Pending |
| S7 | Excel parity gate | Pending |
| S8 | Sensitivity sweeps | Pending |
8. Technical Constraints & Decisions
8.1 Working Agreements (from PROJECT.md)
- Engine is UI-agnostic (pip-installable package)
- CostItem table model (not flat fields)
- Three nested iterations: tariff → debt → IDC
- IDC has independent equity/debt drawdowns
- Two DSCR compliance knobs
- Sync-async hybrid: all runs through Arq queue
- SQLite v0, Parquet for timeseries
- Excel parity is sacred — must match user Excel within 0.1%
- Coverage ≥85%
- mypy strict mode
- One DataGrid component (shared across 5+ places)
8.2 Testing
- Unit tests for every public function
- Integration tests for modules
- Parity gate — tests against user's Excel gold scenarios
- Location:
packages/engine/tests/ - Fixtures:
packages/engine/tests/fixtures/
9. Observations & Potential Issues
9.1 Strong Points
- Clean architecture — engine/API/web separation well-enforced
- Pydantic schemas — single source of truth for types
- Comprehensive financial model — 3-statement + depreciation + tax
- Multiple debt schedule shapes — flexible repayment
- Production-ready — Ruff, mypy, pytest, coverage
- Async backend — Redis queue for scaling
- TypeScript types — auto-generated from OpenAPI
9.2 Potential Concerns
- IDC fixed-point — may need more robust convergence handling
- Parity gate — not yet validated against user Excel
- Multi-user — SQLite v0, needs Postgres for v2
- Test coverage gaps — some modules newly added
- Wind power curve — generic, may need calibration
- Solar profiles — only 3 locations (RJ, KA, GJ)
9.3 Missing/Incomplete (from git status)
The git status shows many new untracked files:
packages/engine/src/remodel_engine/capex/(existing, modified)- New modules for dispatch, commercial, debt, financial, irr, solver, scenarios, io
packages/web/app/compare/(new page)- Various test updates
10. Recommendations for Discussion
-
Validation Priority: Run the Excel parity gate with user-supplied gold scenario to verify model accuracy before proceeding to next sprints
-
Test Coverage: Some newly-added modules (dispatch, commercial, irr) have limited test coverage — prioritize before shipping
-
DataGrid Usage: Single AG Grid component exists but needs verification across all 5+ use cases
-
Database Migration: SQLite works for v0, but PostgreSQL planning would help architecture decisions
-
Wind Profile Calibration: Generic wind power curve may need location-specific calibration for Indian sites
-
Performance: 30-second target may be achievable, but needs benchmarking with realistic scenarios
11. File Locations Reference
Key Source Files
| Component | Path |
|---|---|
| Scenario input schema | packages/engine/src/remodel_engine/schemas/scenario.py |
| Generation simulation | packages/engine/src/remodel_engine/generation/solar.py |
| Wind simulation | packages/engine/src/remodel_engine/generation/wind.py |
| BESS model | packages/engine/src/remodel_engine/generation/bess_state.py |
| RTC dispatch | packages/engine/src/remodel_engine/dispatch/hybrid_rtc.py |
| Capex calculation | packages/engine/src/remodel_engine/capex/cost_items.py |
| IDC calculation | packages/engine/src/remodel_engine/capex/idc.py |
| P&L | packages/engine/src/remodel_engine/financial/pnl.py |
| CFS | packages/engine/src/remodel_engine/financial/cfs.py |
| Balance Sheet | packages/engine/src/remodel_engine/financial/bs.py |
| Depreciation | packages/engine/src/remodel_engine/financial/depreciation.py |
| Tax | packages/engine/src/remodel_engine/financial/tax.py |
| Debt sizing | packages/engine/src/remodel_engine/debt/sizing.py |
| Debt schedule | packages/engine/src/remodel_engine/debt/schedule.py |
| IRR metrics | packages/engine/src/remodel_engine/irr/metrics.py |
| Tariff solver | packages/engine/src/remodel_engine/solver/tariff.py |
| Scenario runner | packages/engine/src/remodel_engine/scenarios/runner.py |
| CLI | packages/engine/src/remodel_engine/cli.py |
| API main | packages/api/src/remodel_api/main.py |
| Scenario endpoints | packages/api/src/remodel_api/routers/scenarios.py |
| Background tasks | packages/api/src/remodel_api/workers/tasks.py |
| Web API client | packages/web/lib/api.ts |
| Main page | packages/web/app/page.tsx |
End of investigation report.