download
raw
20.4 kB
import json
import csv
import io
from datetime import datetime
import numpy as np
import matplotlib
matplotlib.use('Agg')
import matplotlib.pyplot as plt
from django.db import models
from django.conf import settings
class SimulationMethod(models.Model):
name = models.CharField(max_length=200)
slug = models.SlugField(unique=True)
description = models.TextField()
theory = models.TextField(blank=True)
parameters_schema = models.JSONField(default=dict)
default_parameters = models.JSONField(default=dict)
is_active = models.BooleanField(default=True)
created_at = models.DateTimeField(auto_now_add=True)
updated_at = models.DateTimeField(auto_now=True)
class Meta:
ordering = ['name']
def __str__(self):
return self.name
class SimulationRun(models.Model):
STATUS_CHOICES = [
('PENDING', 'En attente'),
('RUNNING', 'En cours'),
('SUCCESS', 'Terminé'),
('FAILURE', 'Échoué'),
('CANCELLED', 'Annulé'),
]
method = models.ForeignKey(
SimulationMethod,
on_delete=models.CASCADE,
related_name='runs'
)
name = models.CharField(max_length=200, blank=True)
parameters = models.JSONField(default=dict)
status = models.CharField(
max_length=20,
choices=STATUS_CHOICES,
default='PENDING'
)
progress = models.IntegerField(default=0)
logs = models.TextField(blank=True)
result_data = models.JSONField(null=True, blank=True)
error_message = models.TextField(blank=True)
input_file = models.FileField(
upload_to='simulations/inputs/',
null=True,
blank=True
)
output_file = models.FileField(
upload_to='simulations/outputs/',
null=True,
blank=True
)
plot_file = models.FileField(
upload_to='simulations/plots/',
null=True,
blank=True
)
pdf_file = models.FileField(
upload_to='simulations/reports/',
null=True,
blank=True
)
csv_file = models.FileField(
upload_to='simulations/csv/',
null=True,
blank=True
)
created_by = models.ForeignKey(
settings.AUTH_USER_MODEL,
on_delete=models.SET_NULL,
null=True,
blank=True
)
created_at = models.DateTimeField(auto_now_add=True)
started_at = models.DateTimeField(null=True, blank=True)
completed_at = models.DateTimeField(null=True, blank=True)
class Meta:
ordering = ['-created_at']
def __str__(self):
return self.name or f"Run #{self.id} - {self.method.name}"
def add_log(self, message):
from django.utils import timezone
timestamp = timezone.now().strftime('%Y-%m-%d %H:%M:%S')
self.logs += f"[{timestamp}] {message}\n"
self.save(update_fields=['logs'])
def set_running(self):
from django.utils import timezone
self.status = 'RUNNING'
self.started_at = timezone.now()
self.save(update_fields=['status', 'started_at'])
def set_success(self, result_data=None):
from django.utils import timezone
self.status = 'SUCCESS'
self.progress = 100
self.completed_at = timezone.now()
if result_data:
self.result_data = result_data
self.save(update_fields=['status', 'progress', 'completed_at', 'result_data'])
self.generate_outputs()
def set_failure(self, error_message):
from django.utils import timezone
self.status = 'FAILURE'
self.completed_at = timezone.now()
self.error_message = error_message
self.save(update_fields=['status', 'completed_at', 'error_message'])
def set_pending(self):
self.status = 'PENDING'
self.progress = 0
self.started_at = None
self.completed_at = None
self.save(update_fields=['status', 'progress', 'started_at', 'completed_at'])
def generate_plot(self):
"""Génère un graphique selon la méthode."""
if not self.result_data:
return None
fig, ax = plt.subplots(figsize=(10, 6))
method_slug = self.method.slug
if method_slug == 'monte-carlo-pi':
self._plot_monte_carlo(fig, ax)
elif method_slug == 'diffusion-1d':
self._plot_diffusion(fig, ax)
elif method_slug == 'linear-solve':
self._plot_linear(fig, ax)
elif method_slug == 'heat-conduction':
self._plot_heat_conduction(fig, ax)
elif method_slug == 'traffic-flow':
self._plot_traffic_flow(fig, ax)
elif method_slug == 'phugoid':
self._plot_phugoid(fig, ax)
elif method_slug == 'elasticity-dangvan':
self._plot_elasticity(fig, ax)
elif method_slug == 'wave-equation':
self._plot_wave_equation(fig, ax)
else:
ax.text(0.5, 0.5, 'Pas de visualisation disponible',
ha='center', va='center', transform=ax.transAxes)
plt.tight_layout()
buffer = io.BytesIO()
plt.savefig(buffer, format='png', dpi=150)
buffer.seek(0)
plt.close(fig)
return buffer
def _plot_monte_carlo(self, fig, ax):
"""Graphique pour Monte Carlo."""
data = self.result_data
ax.bar(['Estimé', 'Exact'], [data['pi_estimate'], data['exact_pi']],
color=['#3498db', '#27ae60'])
ax.set_ylabel('Valeur de Pi')
ax.set_title(f"Estimation de Pi - {data['n_points']} points")
ax.set_ylim(0, 4)
error_pct = abs(data['error']) / data['exact_pi'] * 100
ax.text(0.5, 0.9, f"Erreur: {error_pct:.4f}%",
transform=ax.transAxes, ha='center')
def _plot_diffusion(self, fig, ax):
"""Graphique pour diffusion 1D."""
data = self.result_data
final_state = data.get('final_state', [])
if final_state:
x = np.linspace(0, len(final_state) - 1, len(final_state))
ax.plot(x, final_state, 'b-', linewidth=2, label='État final')
ax.set_xlabel('Position x')
ax.set_ylabel('Concentration u(x,t)')
ax.set_title('Profil de diffusion 1D - État final')
ax.grid(True, alpha=0.3)
ax.legend()
ax.fill_between(x, final_state, alpha=0.3)
ax.set_xlim(0, len(final_state) if final_state else 100)
def _plot_linear(self, fig, ax):
"""Graphique pour résolution linéaire."""
data = self.result_data
info = f"Taille: {data['size']}\n"
info += f"Norme solution: {data['solution_norm']:.4f}\n"
info += f"Résidu: {data['residual']:.2e}\n"
info += f"Nombre de condition: {data['condition_number']:.2e}"
ax.text(0.5, 0.5, info, transform=ax.transAxes, ha='center', va='center',
fontsize=12, bbox=dict(boxstyle='round', facecolor='wheat', alpha=0.5))
ax.set_title('Résultats - Résolution système linéaire')
def _plot_heat_conduction(self, fig, ax):
"""Graphique pour conduction thermique."""
data = self.result_data
T_final = np.array(data.get('final_T', []))
T_analytical = np.array(data.get('T_analytical', []))
N = len(T_final)
t = np.arange(N) * data.get('dt', 0.001)
ax.plot(t, T_final, 'b-', linewidth=2, label='Numérique')
ax.plot(t, T_analytical, 'r--', linewidth=2, label='Analytique')
ax.set_xlabel('Temps')
ax.set_ylabel('Température')
ax.set_title(f"Conduction thermique - θ={data.get('theta', 0.5)}")
ax.legend()
ax.grid(True, alpha=0.3)
def _plot_traffic_flow(self, fig, ax):
"""Graphique pour flux de trafic."""
data = self.result_data
rho_matrix = np.array(data.get('density_matrix', []))
if rho_matrix.size > 0:
im = ax.imshow(rho_matrix, aspect='auto', cmap='hot', origin='lower')
ax.set_xlabel('Position x')
ax.set_ylabel('Temps t')
ax.set_title('Densité de trafic')
plt.colorbar(im, ax=ax, label='Densité')
else:
ax.text(0.5, 0.5, 'Pas de données', ha='center', va='center')
def _plot_phugoid(self, fig, ax):
"""Graphique pour trajectoire phugoid."""
data = self.result_data
x = np.array(data.get('x', []))
z = np.array(data.get('z', []))
if x.size > 0:
ax.plot(x, -z, 'b-', linewidth=2)
ax.set_xlabel('x')
ax.set_ylabel('z')
ax.set_title(f"Trajectoire de vol - C={data.get('C', 0):.3f}")
ax.grid(True, alpha=0.3)
ax.set_aspect('equal')
def _plot_elasticity(self, fig, ax):
"""Graphique pour élasticité DangVan."""
data = self.result_data
tensor = np.array(data.get('tensor_matrix', [])).reshape(3, 3)
im = ax.imshow(tensor, cmap='coolwarm', aspect='equal')
ax.set_title('Tenseur des contraintes (MPa)')
for i in range(3):
for j in range(3):
ax.text(j, i, f'{tensor[i,j]:.1f}', ha='center', va='center', color='black')
plt.colorbar(im, ax=ax)
def _plot_wave_equation(self, fig, ax):
"""Graphique pour équation d'onde."""
data = self.result_data
u = np.array(data.get('displacement_matrix', []))
if u.size > 0:
im = ax.imshow(u, aspect='auto', cmap='RdBu', origin='lower')
ax.set_xlabel('Position x')
ax.set_ylabel('Temps t')
ax.set_title('Déplacement u(x,t)')
plt.colorbar(im, ax=ax, label='u')
def generate_csv(self):
"""Génère un fichier CSV des résultats."""
if not self.result_data:
return None
buffer = io.StringIO()
writer = csv.writer(buffer)
writer.writerow(['Simulation Run Report'])
writer.writerow(['ID', self.id])
writer.writerow(['Méthode', self.method.name])
writer.writerow(['Statut', self.status])
writer.writerow(['Créé le', self.created_at])
writer.writerow(['Terminé le', self.completed_at])
writer.writerow([])
writer.writerow(['Paramètres'])
for key, value in self.parameters.items():
writer.writerow([key, str(value)])
writer.writerow([])
writer.writerow(['Résultats'])
for key, value in self.result_data.items():
writer.writerow([key, str(value)])
buffer.seek(0)
return io.BytesIO(buffer.getvalue().encode('utf-8'))
def generate_pdf(self):
"""Génère un rapport PDF avec ReportLab."""
from reportlab.lib.pagesizes import A4
from reportlab.lib import colors
from reportlab.lib.units import inch
from reportlab.platypus import SimpleDocTemplate, Paragraph, Spacer, Table, TableStyle, Image
from reportlab.lib.styles import getSampleStyleSheet, ParagraphStyle
from reportlab.lib.enums import TA_CENTER, TA_LEFT
import tempfile
buffer = io.BytesIO()
doc = SimpleDocTemplate(buffer, pagesize=A4,
leftMargin=0.5*inch, rightMargin=0.5*inch,
topMargin=0.5*inch, bottomMargin=0.5*inch)
styles = getSampleStyleSheet()
title_style = ParagraphStyle('Title', parent=styles['Heading1'],
fontSize=18, spaceAfter=20, alignment=TA_CENTER)
heading_style = ParagraphStyle('Heading', parent=styles['Heading2'],
fontSize=14, spaceAfter=10, color=colors.darkblue)
normal_style = styles['Normal']
story = []
story.append(Paragraph(f"Rapport de Simulation: {self.method.name}", title_style))
story.append(Spacer(1, 20))
story.append(Paragraph("Informations Générales", heading_style))
info_data = [
['ID de la simulation', str(self.id)],
['Méthode', self.method.name],
['Statut', self.status],
['Créé le', str(self.created_at)],
['Terminé le', str(self.completed_at)],
]
info_table = Table(info_data, colWidths=[2.5*inch, 3*inch])
info_table.setStyle(TableStyle([
('BACKGROUND', (0, 0), (0, -1), colors.lightgrey),
('TEXTCOLOR', (0, 0), (0, -1), colors.black),
('GRID', (0, 0), (-1, -1), 0.5, colors.grey),
('PADDING', (0, 0), (-1, -1), 6),
]))
story.append(info_table)
story.append(Spacer(1, 20))
story.append(Paragraph("Paramètres de Simulation", heading_style))
params_data = [[str(k), str(v)] for k, v in self.parameters.items()]
if params_data:
params_table = Table(params_data, colWidths=[2.5*inch, 3*inch])
params_table.setStyle(TableStyle([
('BACKGROUND', (0, 0), (0, -1), colors.lightblue),
('GRID', (0, 0), (-1, -1), 0.5, colors.grey),
('PADDING', (0, 0), (-1, -1), 6),
]))
story.append(params_table)
else:
story.append(Paragraph("Aucun paramètre personnalisé", normal_style))
story.append(Spacer(1, 20))
story.append(Paragraph("Résultats", heading_style))
results_data = [[str(k), str(v)] for k, v in self.result_data.items()]
if results_data:
results_table = Table(results_data, colWidths=[2.5*inch, 3*inch])
results_table.setStyle(TableStyle([
('BACKGROUND', (0, 0), (0, -1), colors.lightgreen),
('GRID', (0, 0), (-1, -1), 0.5, colors.grey),
('PADDING', (0, 0), (-1, -1), 6),
]))
story.append(results_table)
story.append(Spacer(1, 20))
story.append(Paragraph("Graphique", heading_style))
import os
if self.plot_file and os.path.exists(self.plot_file.path):
img = Image(self.plot_file.path, width=5*inch, height=3*inch)
img.hAlign = 'CENTER'
story.append(img)
else:
story.append(Paragraph("Graphique non disponible", normal_style))
story.append(Spacer(1, 20))
if self.logs:
story.append(Paragraph("Logs d'Éxecution", heading_style))
log_style = ParagraphStyle('Log', parent=styles['Normal'],
fontSize=8, textColor=colors.darkgrey)
for line in self.logs.strip().split('\n')[-50:]:
story.append(Paragraph(line, log_style))
story.append(Spacer(1, 30))
story.append(Paragraph(f"Généré le {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}",
ParagraphStyle('Date', parent=styles['Normal'],
fontSize=8, alignment=TA_CENTER)))
doc.build(story)
buffer.seek(0)
return buffer
def generate_outputs(self):
"""Génère tous les fichiers de sortie."""
import os
plot_buffer = self.generate_plot()
if plot_buffer:
filename = f"plot_{self.id}.png"
self.plot_file.save(filename, plot_buffer, save=True)
csv_buffer = self.generate_csv()
if csv_buffer:
filename = f"results_{self.id}.csv"
self.csv_file.save(filename, csv_buffer, save=True)
if self.plot_file:
pdf_buffer = self.generate_pdf_with_plot()
if pdf_buffer:
filename = f"report_{self.id}.pdf"
self.pdf_file.save(filename, pdf_buffer, save=True)
def generate_pdf_with_plot(self):
"""Génère un rapport PDF avec le graphique déjà sauvegardé."""
from reportlab.lib.pagesizes import A4
from reportlab.lib import colors
from reportlab.lib.units import inch
from reportlab.platypus import SimpleDocTemplate, Paragraph, Spacer, Table, TableStyle, Image
from reportlab.lib.styles import getSampleStyleSheet, ParagraphStyle
from reportlab.lib.enums import TA_CENTER, TA_LEFT
import os
if not self.plot_file or not os.path.exists(self.plot_file.path):
return None
buffer = io.BytesIO()
doc = SimpleDocTemplate(buffer, pagesize=A4,
leftMargin=0.5*inch, rightMargin=0.5*inch,
topMargin=0.5*inch, bottomMargin=0.5*inch)
styles = getSampleStyleSheet()
title_style = ParagraphStyle('Title', parent=styles['Heading1'],
fontSize=18, spaceAfter=20, alignment=TA_CENTER)
heading_style = ParagraphStyle('Heading', parent=styles['Heading2'],
fontSize=14, spaceAfter=10, color=colors.darkblue)
normal_style = styles['Normal']
story = []
story.append(Paragraph(f"Rapport de Simulation: {self.method.name}", title_style))
story.append(Spacer(1, 20))
story.append(Paragraph("Informations Générales", heading_style))
info_data = [
['ID de la simulation', str(self.id)],
['Méthode', self.method.name],
['Statut', self.status],
['Créé le', str(self.created_at)],
['Terminé le', str(self.completed_at)],
]
info_table = Table(info_data, colWidths=[2.5*inch, 3*inch])
info_table.setStyle(TableStyle([
('BACKGROUND', (0, 0), (0, -1), colors.lightgrey),
('TEXTCOLOR', (0, 0), (0, -1), colors.black),
('GRID', (0, 0), (-1, -1), 0.5, colors.grey),
('PADDING', (0, 0), (-1, -1), 6),
]))
story.append(info_table)
story.append(Spacer(1, 20))
story.append(Paragraph("Paramètres de Simulation", heading_style))
params_data = [[str(k), str(v)] for k, v in self.parameters.items()]
if params_data:
params_table = Table(params_data, colWidths=[2.5*inch, 3*inch])
params_table.setStyle(TableStyle([
('BACKGROUND', (0, 0), (0, -1), colors.lightblue),
('GRID', (0, 0), (-1, -1), 0.5, colors.grey),
('PADDING', (0, 0), (-1, -1), 6),
]))
story.append(params_table)
else:
story.append(Paragraph("Aucun paramètre personnalisé", normal_style))
story.append(Spacer(1, 20))
story.append(Paragraph("Résultats", heading_style))
if self.result_data:
results_data = [[str(k), str(v)] for k, v in self.result_data.items()]
if results_data:
results_table = Table(results_data, colWidths=[2.5*inch, 3*inch])
results_table.setStyle(TableStyle([
('BACKGROUND', (0, 0), (0, -1), colors.lightgreen),
('GRID', (0, 0), (-1, -1), 0.5, colors.grey),
('PADDING', (0, 0), (-1, -1), 6),
]))
story.append(results_table)
story.append(Spacer(1, 20))
story.append(Paragraph("Graphique", heading_style))
img = Image(self.plot_file.path, width=5*inch, height=3*inch)
img.hAlign = 'CENTER'
story.append(img)
story.append(Spacer(1, 20))
if self.logs:
story.append(Paragraph("Logs d'Éxecution", heading_style))
log_style = ParagraphStyle('Log', parent=styles['Normal'],
fontSize=8, textColor=colors.darkgrey)
for line in self.logs.strip().split('\n')[-50:]:
story.append(Paragraph(line, log_style))
story.append(Spacer(1, 30))
story.append(Paragraph(f"Généré le {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}",
ParagraphStyle('Date', parent=styles['Normal'],
fontSize=8, alignment=TA_CENTER)))
doc.build(story)
buffer.seek(0)
return buffer

Xet Storage Details

Size:
20.4 kB
·
Xet hash:
f746217e2bad2cc1a6f853594872f5e4a10539b4336df35bf13d314ec4d9d40a

Xet efficiently stores files, intelligently splitting them into unique chunks and accelerating uploads and downloads. More info.