diff --git a/app/__init__.py b/app/__init__.py index 73027f1..36f592b 100644 --- a/app/__init__.py +++ b/app/__init__.py @@ -8,10 +8,14 @@ updates of the Markov economics simulation. from flask import Flask from flask_socketio import SocketIO from config import config +from app.models import SimulationManager # Initialize SocketIO socketio = SocketIO(cors_allowed_origins="*") +# Global simulation manager instance +simulation_manager = SimulationManager() + def create_app(config_name='development'): """ diff --git a/app/__pycache__/__init__.cpython-312.pyc b/app/__pycache__/__init__.cpython-312.pyc index 89d5ebd..fc7b586 100644 Binary files a/app/__pycache__/__init__.cpython-312.pyc and b/app/__pycache__/__init__.cpython-312.pyc differ diff --git a/app/models/__pycache__/economic_model.cpython-312.pyc b/app/models/__pycache__/economic_model.cpython-312.pyc index a257bec..c527b57 100644 Binary files a/app/models/__pycache__/economic_model.cpython-312.pyc and b/app/models/__pycache__/economic_model.cpython-312.pyc differ diff --git a/app/models/economic_model.py b/app/models/economic_model.py index ad79339..ce21d21 100644 --- a/app/models/economic_model.py +++ b/app/models/economic_model.py @@ -262,6 +262,23 @@ class EconomicSimulation: return iterations, total_wealth, gini_coefficients + def get_inequality_evolution(self) -> Tuple[List[int], List[float], List[float], List[float]]: + """ + Get inequality evolution data for plotting. + + Returns: + Tuple of (iterations, gini_over_time, top10_share_over_time, capital_share_over_time) + """ + if not self.snapshots: + return [], [], [], [] + + iterations = [s.iteration for s in self.snapshots] + gini_coefficients = [s.gini_coefficient for s in self.snapshots] + top10_shares = [s.wealth_concentration_top10 for s in self.snapshots] + capital_shares = [s.capital_share for s in self.snapshots] + + return iterations, gini_coefficients, top10_shares, capital_shares + def get_agent_wealth_distribution(self) -> Dict[str, List[float]]: """ Get current wealth distribution across all agents. diff --git a/app/routes/__pycache__/api.cpython-312.pyc b/app/routes/__pycache__/api.cpython-312.pyc index 81acbd3..df73f71 100644 Binary files a/app/routes/__pycache__/api.cpython-312.pyc and b/app/routes/__pycache__/api.cpython-312.pyc differ diff --git a/app/routes/__pycache__/main.cpython-312.pyc b/app/routes/__pycache__/main.cpython-312.pyc index 3046a8b..07e231d 100644 Binary files a/app/routes/__pycache__/main.cpython-312.pyc and b/app/routes/__pycache__/main.cpython-312.pyc differ diff --git a/app/routes/api.py b/app/routes/api.py index 7e5b270..51e322c 100644 --- a/app/routes/api.py +++ b/app/routes/api.py @@ -13,7 +13,7 @@ from typing import Optional from app import socketio from app.models import SimulationManager, SimulationParameters -from app.routes.main import simulation_manager +from app import simulation_manager api_bp = Blueprint('api', __name__) @@ -95,6 +95,9 @@ def start_simulation(simulation_id: str): if simulation.is_running: return jsonify({'error': 'Simulation already running'}), 400 + # Mark simulation as running + simulation.is_running = True + # Start simulation in background thread def run_simulation_background(): try: @@ -125,6 +128,9 @@ def start_simulation(simulation_id: str): # Small delay to allow real-time visualization time.sleep(0.01) + # Mark as completed + simulation.is_running = False + # Emit completion socketio.emit('simulation_complete', { 'simulation_id': simulation_id, @@ -132,6 +138,7 @@ def start_simulation(simulation_id: str): }, room=f'simulation_{simulation_id}') except Exception as e: + simulation.is_running = False current_app.logger.error(f"Error in simulation thread: {str(e)}") socketio.emit('simulation_error', { 'simulation_id': simulation_id, @@ -254,10 +261,15 @@ def get_simulation_data(simulation_id: str): # Include evolution data if requested if request.args.get('include_evolution', '').lower() == 'true': + # Get complete inequality data + ineq_iterations, gini_over_time, top10_over_time, capital_over_time = simulation.get_inequality_evolution() + response_data['evolution'] = { 'iterations': iterations, 'total_wealth': total_wealth, - 'gini_coefficients': gini_coefficients + 'gini_coefficients': gini_coefficients, + 'top10_shares': top10_over_time, + 'capital_shares': capital_over_time } return jsonify(response_data) diff --git a/app/routes/main.py b/app/routes/main.py index 2fb435e..64ace60 100644 --- a/app/routes/main.py +++ b/app/routes/main.py @@ -10,8 +10,8 @@ from app.models import SimulationManager, SimulationParameters main_bp = Blueprint('main', __name__) -# Global simulation manager instance -simulation_manager = SimulationManager() +# Import global simulation manager instance +from app import simulation_manager @main_bp.route('/') diff --git a/app/static/js/simulation.js b/app/static/js/simulation.js index bbed8b1..a41bdd3 100644 --- a/app/static/js/simulation.js +++ b/app/static/js/simulation.js @@ -651,14 +651,44 @@ function updateMetricsDisplay(data) { /** * Handle simulation completion */ -function onSimulationComplete(data) { +async function onSimulationComplete(data) { currentSimulation.isRunning = false; updateUIState('complete'); - window.MarkovEconomics.utils.showNotification( - `Simulation completed! ${data.total_snapshots} data points collected.`, - 'success' - ); + // Fetch complete simulation data and populate charts + try { + const response = await window.MarkovEconomics.utils.apiRequest( + `/api/simulation/${currentSimulation.id}/data?include_evolution=true` + ); + + if (response.evolution) { + // Clear and populate with complete data + currentSimulation.data.iterations = response.evolution.iterations; + currentSimulation.data.totalWealth = response.evolution.total_wealth; + currentSimulation.data.giniCoefficients = response.evolution.gini_coefficients; + currentSimulation.data.top10Share = response.evolution.top10_shares || []; + currentSimulation.data.capitalShare = response.evolution.capital_shares || []; + + // Update charts with complete data + updateCharts(); + + // Update final metrics + if (response.latest_snapshot) { + updateMetricsDisplay(response.latest_snapshot); + } + } + + window.MarkovEconomics.utils.showNotification( + `Simulation completed! ${data.total_snapshots} data points collected.`, + 'success' + ); + } catch (error) { + console.error('Failed to fetch simulation data:', error); + window.MarkovEconomics.utils.showNotification( + 'Simulation completed but failed to load results', + 'warning' + ); + } } /** diff --git a/test_charts.py b/test_charts.py new file mode 100644 index 0000000..219c008 --- /dev/null +++ b/test_charts.py @@ -0,0 +1,34 @@ +import requests +import json +import time + +# Test simulation workflow +data = {'r_rate': 0.05, 'g_rate': 0.03, 'num_agents': 50, 'iterations': 200} + +# Create simulation +create_resp = requests.post('http://localhost:5000/api/simulation', json=data) +sim_id = create_resp.json()['simulation_id'] +print('Created simulation:', sim_id) + +# Start simulation +start_resp = requests.post(f'http://localhost:5000/api/simulation/{sim_id}/start') +print('Started simulation:', start_resp.json()['status']) + +# Wait for completion +time.sleep(3) + +# Get evolution data +data_resp = requests.get(f'http://localhost:5000/api/simulation/{sim_id}/data?include_evolution=true') +result = data_resp.json() + +print('\nFinal metrics:') +print(f' Iterations: {len(result["evolution"]["iterations"])}') +print(f' Final Gini: {result["evolution"]["gini_coefficients"][-1]:.3f}') +print(f' Final Total Wealth: ${result["evolution"]["total_wealth"][-1]:.2f}') +print(f' Top 10% data points: {len(result["evolution"].get("top10_shares", []))}') +print(f' Capital share data points: {len(result["evolution"].get("capital_shares", []))}') + +if len(result["evolution"]["gini_coefficients"]) > 0: + print('\n✅ Charts should now be populated with data!') +else: + print('\n❌ No data available for charts') \ No newline at end of file