Skip to content

Feature/og clews converging pipeline#24

Open
NamanmeetSingh wants to merge 7 commits intoEAPD-DRB:mainfrom
NamanmeetSingh:feature/og-clews-converging-pipeline
Open

Feature/og clews converging pipeline#24
NamanmeetSingh wants to merge 7 commits intoEAPD-DRB:mainfrom
NamanmeetSingh:feature/og-clews-converging-pipeline

Conversation

@NamanmeetSingh
Copy link

@NamanmeetSingh NamanmeetSingh commented Feb 24, 2026

Summary

  • What changed: - Introduced the API/og_clews_integration/ package to serve as the backend foundation for the coupled models.
    • Added schemas.py using pydantic to establish strict data contracts for ClewsOutputSchema and OgCoreInputSchema.
    • Added transformer.py to isolate the Extract-Transform-Load (ETL) data wrangling logic.
    • Added converger.py which implements an iterative while loop with a mathematical dampening algorithm ($V_{new} = \alpha \cdot V_{calculated} + (1 - \alpha) \cdot V_{old}$) to enforce stabilization between the macroeconomic and energy models.
    • Added an automated test suite in API/tests/test_pipeline.py.
    • Appended pydantic and pytest to requirements.txt.
  • Why: - To fulfill the Track 1 requirement of building a robust, converging simulation module. Simply passing raw data back and forth between heterogeneous models (like OG-Core and CLEWS) often results in infinite oscillation or solver crashes. This architecture enforces data integrity via strict schemas and ensures mathematical convergence via dampening factors, creating a scalable, production-ready foundation for the full integration.

Related issues

Validation

  • Tests added/updated (or not applicable)
  • Validation steps documented
    • Step 1: Run pip install -r requirements.txt to fetch pydantic and pytest.
    • Step 2: Run pytest API/tests/ to validate that the schema catches invalid negative energy prices and that the transformer math executes correctly.
    • Step 3: Run python API/og_clews_integration/converger.py to execute the mock simulation loop and view the dampening logic in the terminal.

Documentation

  • Docs updated in this PR (or not applicable)
    (Extensive docstrings added to all new classes and methods. Formal markdown docs will be added once the variables are finalized.)

  • Any setup/workflow changes reflected in repo docs
    (Dependencies added to requirements.txt.)

Scope Check

  • No unrelated refactors
  • Implemented from a feature branch
  • Change is deliverable without upstream OSeMOSYS/MUIO dependency
  • Base repo/branch is EAPD-DRB/MUIOGO:main (not upstream)

Questions for Maintainers (@SeaCelo , @autibet)

This is currently a conceptual Draft/PoC using a mocked macroeconomic variable to trigger loop termination. Before expanding this into the full pandas time-series integration:

  1. What specific macroeconomic variables (e.g., specific GDP indices or interest rates) and energy variables do you typically monitor to declare historical convergence between these models?

  2. Do you have preferred default thresholds ($\epsilon$) and dampening factors ($\alpha$) that you rely on to prevent divergent runs in production?

@NamanmeetSingh
Copy link
Author

Architecture Update: Hardening the Execution Engine

While waiting for feedback on the initial PoC schemas, I've pushed a significant architectural upgrade to this branch to ensure the backend is production-ready and server-safe.

Key Upgrades in this Commit:

  1. OOM (Out of Memory) Prevention: OSeMOSYS solvers (GLPK/CBC) generate massive log files. Using standard subprocess.communicate() buffers this entirely into RAM, which will crash the Flask server. I've implemented an asynchronous ModelRunner that streams stdout/stderr directly to disk in real-time.
  2. Directory Sandboxing: Hardcoded the runner to strictly utilize the WebAPP/DataStorage and WebAPP/SOLVERs directories. Using shlex.quote and isolated cwd execution, this guarantees protection against path traversal/shell injection vulnerabilities during model runs.
  3. N-Dimensional Convergence: A 1-dimensional check (e.g., just GDP) is mathematically insufficient for an economy-wide general equilibrium model. The converger.py now utilizes numpy to evaluate the $L_\infty$ norm (maximum absolute percentage error) across an n-dimensional vector of outputs to accurately declare system convergence.
  4. Native Vocabulary: Updated the pydantic schemas to natively ingest exact OSeMOSYS parameters (e.g., AnnualEmissions, TotalDiscountedCostByTechnology from VARIABLES_C).

I will begin drafting the pandas ETL logic to bridge these new robust schemas next!

@NamanmeetSingh
Copy link
Author

Architecture Update: Solving Dimensionality & Infinite Oscillation

Following up on the execution engine updates, I have pushed the Phase 2 implementation of the ETL pipeline to mathematically bridge the two models. I have implemented a proposed mathematical solution to handle the dimensionality mismatch and the risk of divergence.

Key Decisions & Implementations:

The Dimensionality Bridge (Pandas ETL):

CLEWS computes in physical dimensions (Region, Technology, Emission, Year), while OG-Core computes over a 1D macroeconomic Time Path ($T$). The DimensionalityBridge now uses vectorized pandas.groupby operations to collapse the high-dimensional CLEWS outputs (e.g., AnnualEmissions, CapitalInvestment) into 1D time-series arrays that cleanly map to the OgCoreInputSchema.

Stateful Dampening (Preventing Oscillation):

Directly passing raw output multipliers between sectoral and macro models almost guarantees infinite oscillation (e.g., high energy costs crash GDP, which crashes energy demand, which makes energy cheap, which spikes GDP).

I implemented a stateful ConvergingOrchestrator that tracks iteration history and applies a dampening factor ($\alpha$) to smoothly glide the models toward dynamic equilibrium:
Scale_n = Scale_{n-1} + alpha * (Scale_calculated - Scale_{n-1})

C-Optimized Vectorization:

Eliminated $O(N)$ row-by-row iteration in the reverse pass. Scaling factors from the macroeconomic shifts are now applied back to the CLEWS SpecifiedDemandProfile using native Pandas .map() vectorization for high-performance scenario runs.

Included test_convergence.py:

Added a standalone local test script that mocks both model outputs and successfully demonstrates the $L_\infty$ norm convergence logic shrinking the delta to < 1e-4.

test_convergence.py Execution (Terminal Output)

test_screenshot_convergence

@NamanmeetSingh
Copy link
Author

Quick follow-up
Looks like my Git client only staged the test script in the previous commit. The actual core files (converger.py, schemas.py, and transformer.py) containing the ETL and dampening logic are now pushed in the latest commit above.)

@NamanmeetSingh
Copy link
Author

Phase 3 Architecture: The API Gateway & Async Task Manager

To bridge the gap between the ConvergingOrchestrator engine and the Track 2 frontend UI, I've pushed a PoC layer: a fully non-blocking API Gateway (ConvergingRoute.py).

What Was Done & Why

A macroeconomic convergence loop can take minutes to execute. Looking at DataFileRoute.py, current solver executions often rely on blocking synchronous loops or a CustomThread implementation that silently discards exceptions. To prevent the Flask server from hanging and dropping browser connections, an asynchronous task manager is built.

  • POST /api/run/converge: Spawns the orchestrator in a background thread and immediately returns a 202 Accepted with a task_id.
  • GET /api/run/status/<task_id>: Allows the frontend to poll for live iteration updates (e.g., "Iteration 3 completed").
  • OpenAPI Contracts: Documented the exact JSON request/response payloads in the route docstrings so UI contributors have a strict data contract to build their loading dashboards against.

Proof of Concept vs. Production Grade

While this architecture works perfectly for a PoC (see passing tests below), the current background implementation uses Python's threading.Thread and an in-memory TASK_STORE dictionary.

  • The Production Upgrade: For the final GSoC implementation, this in-memory store must be replaced. To survive server restarts and handle concurrent users in a deployed environment, we will need to upgrade this to a robust message broker/worker queue (e.g., Celery + Redis) or at least back the task states into a persistent SQLite database.

Tasks Passed in this Commit

  • Implemented absolute imports to resolve PYTHONPATH module errors.
  • Built ConvergingRoute.py Flask Blueprint with non-blocking execution.
  • Added test_api_routes.py proving the background thread successfully mutates state while the main Flask thread returns instantly.
test_pipeline_and_routes

Next Technical Steps

The next technical step would be to map the mocked DATA_STORAGE reads/writes in the orchestrator directly to the CLI executions of GLPK/CBC and the OG-Core python package.

@NamanmeetSingh
Copy link
Author

Using Demo Data Phase 1 Update: Official CLEWs.Demo Benchmarking & Architecture Audit

Following the addition of the in-repo CLEWs.Demo package (Issue #54, PR #56), I have completely removed the dummy data generators from the Track 1 integration tests.

What works (The PoC):

  • Hooked the DimensionalityBridge directly into the extracted CLEWs.Demo/res/REF/csv/ results.
  • Successfully verified that the pandas ETL pipeline natively ingests the official UN baseline CSVs (AnnualTechnologyEmission.csv, CapitalInvestment.csv), collapses the multi-dimensional physical metrics ($r, t, e$), and outputs the strict 1D time-series vectors ($T$) required for macroeconomic analysis.

Architecture Audit (Preparing for Production):
While this PoC proves the non-blocking execution and vector math work perfectly, a production-grade GSoC implementation will require addressing the following before the final merge:

  1. Macroeconomic Data Completeness: Currently calculating the energy_cost_index solely from Capital Investment. The ETL pipeline needs to be expanded to ingest AnnualFixedOperatingCost.csv, AnnualVariableOperatingCost.csv, and fuel import costs (Trade.csv) for true OG-Core accuracy.
  2. Reverse Pass Elasticity: Applying a flat GDP multiplier to all energy demand is an oversimplification. The pipeline will need to map granular demand elasticities to specific sectors (e.g., Industrial vs. Residential).
  3. WSGI/Concurrency Limits: The current async API uses Python threading and an in-memory TASK_STORE. To survive server restarts and multi-worker environments (e.g., Gunicorn), this must be upgraded to a robust message broker (like Redis/Celery) or backed by a persistent SQLite state table.

The async pipeline is now officially benchmarking against real OSeMOSYS production data.
test_convergence_demo_data

@NamanmeetSingh
Copy link
Author

Just cross-referencing some parallel work happening in the repo. As I noted in my commits 4 days ago on this PR, I originally had to build an asynchronous ModelRunner and a non-blocking /api/run/converge route here to prevent the Track 1 macroeconomic loops from crashing the Flask server.

I was actually working locally this weekend to extract that exact async queue out of this Track 1 branch into a generic, standalone TaskManager for the whole repository (which sparked Issue #114). However, @brightyorcerf has graciously volunteered to take on that generic extraction in PR #118.

Architecture Plan moving forward:
This is a perfect division of labor. I will let the Track 2 team finalize the generic async queue and the frontend UI polling in #118. Once that is merged, I will refactor this branch to strip out my custom ModelRunner and simply submit the ConvergingOrchestrator's mathematical loops directly into the unified queue.

In the meantime, I am keeping this branch 100% focused on the Track 1 science: expanding the DimensionalityBridge Pandas ETL to ingest the remaining macroeconomic variables (O&M, Trade) and fine-tuning the $\alpha$ dampening factor.

cc: @SeaCelo (Just keeping the mentors in the loop on how the Track 1 / Track 2 architecture is cleanly separating)

@SeaCelo
Copy link
Collaborator

SeaCelo commented Mar 2, 2026

Please update the PR body to include a real linked issue for this PR’s scope before we continue review. It currently has Closes # left blank and only a loose Related #23 reference. If there is not already a dedicated issue for this work, please open one first and then update this PR with a real Closes #... or Related #... reference.

@NamanmeetSingh
Copy link
Author

Thanks for the heads-up on the procedural requirement!

I have updated the PR description to formally link and Closes #23 (the main Track 1 Architecture Proposal issue). Since this PR provides the foundational ETL schemas and the converging loop math outlined in that proposal, it fully satisfies the scope of that issue.

Let me know if you'd prefer me to break the subsequent Pandas/Macroeconomic integration (currently in local dev) into a separate implementation issue moving forward to keep the trackers clean. Ready for the technical review whenever you have the bandwidth.

GaneshPatil7517 added a commit to GaneshPatil7517/MUIOGO that referenced this pull request Mar 3, 2026
…/ Issue EAPD-DRB#122)

- Label: 'OG-Core' -> 'OG-Core Macroeconomy'
- sidebarGroups: removed 'industry', renamed 'fiscal' -> 'fiscal_policy'
- Routes: PascalCase controllers (Demographics, FiscalPolicy, Calibration)
- Aligns with OG-Core backend integration structure
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Architecture/Track 1] Proposal: OG-CLEWS Standardized Data Exchange & Converging Simulation Loop

2 participants