Fix Markov simulation chart display issue

- Fix circular import issue by moving simulation_manager to app/__init__.py
- Enhance get_wealth_evolution to include inequality metrics data
- Add get_inequality_evolution method for complete chart data
- Update API to return top10_shares and capital_shares in evolution data
- Modify onSimulationComplete to fetch and populate charts with complete data
- Fix simulation threading to properly mark completion state
- Add test script to verify chart data generation

The charts now properly display simulation results by fetching complete
evolution data when simulation completes, resolving the empty diagrams issue.
This commit is contained in:
2025-08-24 19:44:24 +02:00
parent bec913a599
commit 27e185c2a6
10 changed files with 106 additions and 9 deletions

View File

@@ -8,10 +8,14 @@ updates of the Markov economics simulation.
from flask import Flask from flask import Flask
from flask_socketio import SocketIO from flask_socketio import SocketIO
from config import config from config import config
from app.models import SimulationManager
# Initialize SocketIO # Initialize SocketIO
socketio = SocketIO(cors_allowed_origins="*") socketio = SocketIO(cors_allowed_origins="*")
# Global simulation manager instance
simulation_manager = SimulationManager()
def create_app(config_name='development'): def create_app(config_name='development'):
""" """

View File

@@ -262,6 +262,23 @@ class EconomicSimulation:
return iterations, total_wealth, gini_coefficients 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]]: def get_agent_wealth_distribution(self) -> Dict[str, List[float]]:
""" """
Get current wealth distribution across all agents. Get current wealth distribution across all agents.

View File

@@ -13,7 +13,7 @@ from typing import Optional
from app import socketio from app import socketio
from app.models import SimulationManager, SimulationParameters from app.models import SimulationManager, SimulationParameters
from app.routes.main import simulation_manager from app import simulation_manager
api_bp = Blueprint('api', __name__) api_bp = Blueprint('api', __name__)
@@ -95,6 +95,9 @@ def start_simulation(simulation_id: str):
if simulation.is_running: if simulation.is_running:
return jsonify({'error': 'Simulation already running'}), 400 return jsonify({'error': 'Simulation already running'}), 400
# Mark simulation as running
simulation.is_running = True
# Start simulation in background thread # Start simulation in background thread
def run_simulation_background(): def run_simulation_background():
try: try:
@@ -125,6 +128,9 @@ def start_simulation(simulation_id: str):
# Small delay to allow real-time visualization # Small delay to allow real-time visualization
time.sleep(0.01) time.sleep(0.01)
# Mark as completed
simulation.is_running = False
# Emit completion # Emit completion
socketio.emit('simulation_complete', { socketio.emit('simulation_complete', {
'simulation_id': simulation_id, 'simulation_id': simulation_id,
@@ -132,6 +138,7 @@ def start_simulation(simulation_id: str):
}, room=f'simulation_{simulation_id}') }, room=f'simulation_{simulation_id}')
except Exception as e: except Exception as e:
simulation.is_running = False
current_app.logger.error(f"Error in simulation thread: {str(e)}") current_app.logger.error(f"Error in simulation thread: {str(e)}")
socketio.emit('simulation_error', { socketio.emit('simulation_error', {
'simulation_id': simulation_id, 'simulation_id': simulation_id,
@@ -254,10 +261,15 @@ def get_simulation_data(simulation_id: str):
# Include evolution data if requested # Include evolution data if requested
if request.args.get('include_evolution', '').lower() == 'true': 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'] = { response_data['evolution'] = {
'iterations': iterations, 'iterations': iterations,
'total_wealth': total_wealth, '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) return jsonify(response_data)

View File

@@ -10,8 +10,8 @@ from app.models import SimulationManager, SimulationParameters
main_bp = Blueprint('main', __name__) main_bp = Blueprint('main', __name__)
# Global simulation manager instance # Import global simulation manager instance
simulation_manager = SimulationManager() from app import simulation_manager
@main_bp.route('/') @main_bp.route('/')

View File

@@ -651,14 +651,44 @@ function updateMetricsDisplay(data) {
/** /**
* Handle simulation completion * Handle simulation completion
*/ */
function onSimulationComplete(data) { async function onSimulationComplete(data) {
currentSimulation.isRunning = false; currentSimulation.isRunning = false;
updateUIState('complete'); updateUIState('complete');
window.MarkovEconomics.utils.showNotification( // Fetch complete simulation data and populate charts
`Simulation completed! ${data.total_snapshots} data points collected.`, try {
'success' 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'
);
}
} }
/** /**

34
test_charts.py Normal file
View File

@@ -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')