SimFerMix_2024 / app.py
YAYA20's picture
Upload app.py
90865b9 verified
raw
history blame
19.7 kB
import numpy as np
from scipy.integrate import odeint
import emcee
import panel as pn
import param
import plotly.graph_objects as go
from plotly.subplots import make_subplots
import plotly.io as pio
# Définition du thème sombre
pio.templates.default = "plotly_dark"
# Initialiser l'extension Panel
pn.extension('katex', 'mathjax', 'plotly')
# Informations personnelles
photo_url = "PPFB.png"
prenom = "Yaya"
nom = "Toure"
email = "[email protected]"
whatsapps_url = "https://wa.me/message/GW7RWRW3GR4WN1"
linkedin_url = "https://www.linkedin.com/in/yaya-toure-8251a4280/"
github_url = "https://github.com/CodingYayaToure"
universite = "UNCHK"
formation = "Licence Analyse Numerique et Modelisation | Master Calcul scientifique et Modelisation"
certificat = "Collecte et Fouille de Donnees (UADB-CNAM Paris)"
TP_Scilab = "https://codingyayatoure.github.io/Equation_de_Transport_en_1D_par_la_Methode_des_Differences_Finis/"
# Créer le template du tableau de bord
template = pn.template.ReactTemplate(title='Progiciel de Simulation des Interactions Microbiennes en Fermentation de Jus de Légumes. | Approche Modélisation mathématique ')
# Classe pour la dynamique Consumer-Resource
class ConsumerResourceApp(param.Parameterized):
# Paramètres du modèle
r = param.Number(10.0, bounds=(0.1, 20.0), step=0.1, doc="Taux d'approvisionnement en ressources")
d = param.Number(0.1, bounds=(0.01, 1.0), step=0.01, doc="Taux de mortalité des consommateurs")
c = param.Number(0.1, bounds=(0.01, 1.0), step=0.01, doc="Coefficient de consommation")
K = param.Number(1.0, bounds=(0.1, 10.0), step=0.1, doc="Concentration à laquelle la croissance est à moitié maximale")
X0 = param.Number(1.0, bounds=(0.1, 10.0), step=0.1, doc="Population initiale des consommateurs")
S0 = param.Number(5.0, bounds=(0.1, 20.0), step=0.1, doc="Concentration initiale de la ressource")
@param.depends('r', 'd', 'c', 'K', 'X0', 'S0')
def view(self):
# Fonction de croissance dépendante de la ressource (Monod)
def mu(S):
return S / (self.K + S)
# Équations différentielles
def model(y, t):
X, S = y
dXdt = mu(S) * X - self.d * X # Dynamique des consommateurs
dSdt = self.r - self.c * mu(S) * X # Dynamique des ressources
return [dXdt, dSdt]
# Conditions initiales
y0 = [self.X0, self.S0]
# Temps pour la simulation (en heures)
t_hours = np.linspace(0, 100, 500)
# Résoudre le système d'équations différentielles
solution = odeint(model, y0, t_hours)
# Créer les graphiques avec Plotly en utilisant des sous-graphiques
fig = make_subplots(
rows=1, cols=3,
specs=[[{'type': 'scatter'}, {'type': 'scatter'}, {'type': 'scatter'}]],
subplot_titles=["Population des Consommateurs (X)", "Concentration de la Ressource (S)", "Population et Ressource sur le même repère"]
)
# Ajouter la trace pour les consommateurs
fig.add_trace(go.Scatter(x=t_hours, y=solution[:, 0], mode='lines', name='Population des Consommateurs (X)', line=dict(color='blue')), row=1, col=1)
# Ajouter la trace pour les ressources
fig.add_trace(go.Scatter(x=t_hours, y=solution[:, 1], mode='lines', name='Concentration de la Ressource (S)', line=dict(color='green')), row=1, col=2)
# Ajouter la trace pour les consommateurs et les ressources sur le même graphique
fig.add_trace(go.Scatter(x=t_hours, y=solution[:, 0], mode='lines', name='Population des Consommateurs (X)', line=dict(color='blue')), row=1, col=3)
fig.add_trace(go.Scatter(x=t_hours, y=solution[:, 1], mode='lines', name='Concentration de la Ressource (S)', line=dict(color='green')), row=1, col=3)
# Mettre à jour la mise en page du graphique
fig.update_layout(
title='Dynamique du Modèle Consumer-Resource',
xaxis_title='Temps',
yaxis_title='Population / Concentration',
legend_title='Légende',
height=400,
width=1500,
font_color="white",
template='plotly_dark'
)
return pn.pane.Plotly(fig)
def panel_view(self):
return pn.Column(
pn.Row(pn.Param(self, width=400), self.formulas()), # Aligner les widgets et les formules sur la même ligne
self.view # Appeler la vue pour qu'elle se mette à jour dynamiquement
)
def formulas(self):
return pn.Column(
pn.pane.LaTeX(r"""
$$
\text{Fonction de croissance : } \mu(S) = \frac{S}{K + S}
$$
""", styles={'font-size': '16pt'}),
pn.pane.LaTeX(r"""
$$
\text{Dynamique des consommateurs : } \frac{dX}{dt} = \mu(S) \times X - d \times X
$$
""", styles={'font-size': '16pt'}),
pn.pane.LaTeX(r"""
$$
\text{Dynamique des ressources : } \frac{dS}{dt} = r - c \times \mu(S) \times X
$$
""", styles={'font-size': '16pt'})
)
# Classe pour la dynamique des populations microbiennes
class MicrobialDynamicsApp(param.Parameterized):
# Paramètres du modèle pour la dynamique des populations microbiennes
r1 = param.Number(0.5, bounds=(0.1, 1.0), step=0.01, doc="Taux de croissance espèce 1")
r2 = param.Number(0.3, bounds=(0.1, 1.0), step=0.01, doc="Taux de croissance espèce 2")
K1 = param.Number(100, bounds=(10, 200), step=1, doc="Capacité de charge espèce 1")
K2 = param.Number(80, bounds=(10, 200), step=1, doc="Capacité de charge espèce 2")
alpha = param.Number(0.01, bounds=(0.001, 0.1), step=0.001, doc="Compétition espèce 2 sur espèce 1")
beta = param.Number(0.01, bounds=(0.001, 0.1), step=0.001, doc="Compétition espèce 1 sur espèce 2")
@param.depends('r1', 'r2', 'K1', 'K2', 'alpha', 'beta')
def view(self):
# Équations différentielles pour les populations microbiennes
def model(y, t):
X1, X2 = y
dX1dt = self.r1 * X1 * (1 - (X1 + self.alpha * X2) / self.K1)
dX2dt = self.r2 * X2 * (1 - (X2 + self.beta * X1) / self.K2)
return [dX1dt, dX2dt]
# Conditions initiales
y0 = [10, 5] # Population initiale des deux espèces
# Temps pour la simulation
t = np.linspace(0, 50, 500)
# Résoudre le système d'équations différentielles
solution = odeint(model, y0, t)
# Créer le graphique avec Plotly
fig = go.Figure()
fig.add_trace(go.Scatter(x=t, y=solution[:, 0], mode='lines', name='Espèce 1', line=dict(color='red')))
fig.add_trace(go.Scatter(x=t, y=solution[:, 1], mode='lines', name='Espèce 2', line=dict(color='purple')))
# Mettre à jour la mise en page du graphique
fig.update_layout(
title='Dynamique des Populations Microbiennes',
xaxis_title='Temps',
yaxis_title='Population',
legend_title='Légende',
height=400,
width=1500,
font_color="white",
template='plotly_dark'
)
return pn.pane.Plotly(fig)
def panel_view(self):
return pn.Column(
pn.Row(pn.Param(self, width=400), self.formulas()), # Aligner les widgets et les formules sur la même ligne
self.view # Appeler la vue pour qu'elle se mette à jour dynamiquement
)
def formulas(self):
return pn.Column(
pn.pane.LaTeX(r"""
$$
\frac{{dX_1}}{{dt}} = r_1 X_1 \left( 1 - \frac{{X_1 + \alpha X_2}}{{K_1}} \right)
$$
""", styles={'font-size': '16pt'}),
pn.pane.LaTeX(r"""
$$
\frac{{dX_2}}{{dt}} = r_2 X_2 \left( 1 - \frac{{X_2 + \beta X_1}}{{K_2}} \right)
$$
""", styles={'font-size': '16pt'})
)
# Classe pour la modélisation de la fermentation
class FermentationModelApp(param.Parameterized):
# Paramètres du modèle de fermentation
mu_max = param.Number(0.8, bounds=(0.1, 2.0), step=0.1, doc="Taux de croissance maximal")
Ks = param.Number(10.0, bounds=(1.0, 50.0), step=1.0, doc="Constante de demi-saturation")
Yxs = param.Number(0.5, bounds=(0.1, 1.0), step=0.1, doc="Rendement de croissance sur substrat")
Sin = param.Number(20.0, bounds=(10.0, 100.0), step=1.0, doc="Concentration initiale en substrat")
X0 = param.Number(0.1, bounds=(0.01, 5.0), step=0.01, doc="Concentration initiale en biomasse")
P0 = param.Number(0.0, bounds=(0.0, 5.0), step=0.1, doc="Concentration initiale en produit")
@param.depends('mu_max', 'Ks', 'Yxs', 'Sin', 'X0', 'P0')
def view(self):
# Équations différentielles pour la fermentation
def model(y, t):
X, S, P = y
mu = self.mu_max * S / (self.Ks + S)
dXdt = mu * X
dSdt = -dXdt / self.Yxs
dPdt = 0.1 * dXdt # Production de produit proportionnelle à la croissance
return [dXdt, dSdt, dPdt]
# Conditions initiales
y0 = [self.X0, self.Sin, self.P0]
# Temps pour la simulation
t = np.linspace(0, 50, 500)
# Résoudre le système d'équations différentielles
solution = odeint(model, y0, t)
# Créer le graphique avec Plotly
fig = go.Figure()
fig.add_trace(go.Scatter(x=t, y=solution[:, 0], mode='lines', name='Biomasse (X)', line=dict(color='blue')))
fig.add_trace(go.Scatter(x=t, y=solution[:, 1], mode='lines', name='Substrat (S)', line=dict(color='green')))
fig.add_trace(go.Scatter(x=t, y=solution[:, 2], mode='lines', name='Produit (P)', line=dict(color='orange')))
# Mettre à jour la mise en page du graphique
fig.update_layout(
title='Modélisation de la Fermentation',
xaxis_title='Temps',
yaxis_title='Concentration',
legend_title='Légende',
height=400,
width=1500,
font_color="white",
template='plotly_dark'
)
return pn.pane.Plotly(fig)
def formulas(self):
return pn.Column(
pn.pane.LaTeX(r"""
$$
\mu = \frac{{\mu_{{\text{{max}}}} \cdot S}}{{K_s + S}}
$$
""", styles={'font-size': '16pt'}),
pn.pane.LaTeX(r"""
$$
\frac{{dX}}{{dt}} = \mu \cdot X
$$
""", styles={'font-size': '16pt'}),
pn.pane.LaTeX(r"""
$$
\frac{{dS}}{{dt}} = -\frac{1}{{Y_{{xs}}}} \cdot \frac{{dX}}{{dt}}
$$
""", styles={'font-size': '16pt'}),
pn.pane.LaTeX(r"""
$$
\frac{{dP}}{{dt}} = 0.1 \cdot \frac{{dX}}{{dt}}
$$
""", styles={'font-size': '16pt'})
)
def panel_view(self):
return pn.Column(
pn.Row(pn.Param(self, width=400), self.formulas()), # Aligner les widgets et les formules sur la même ligne
self.view # Appeler la vue pour qu'elle se mette à jour dynamiquement
)
# Classe pour l'estimation des paramètres par MCMC
class EstimationParametresFermentation(param.Parameterized):
# Paramètres pour l'estimation MCMC
n_marcheurs = param.Integer(32, bounds=(10, 100), doc="Nombre de marcheurs MCMC")
n_etapes = param.Integer(1000, bounds=(100, 5000), doc="Nombre d'étapes MCMC")
niveau_bruit = param.Number(0.1, bounds=(0.01, 0.5), doc="Niveau de bruit dans les données synthétiques")
def __init__(self, **params):
super().__init__(**params)
self.parametres_reels = {
'mu_max': 0.8,
'Ks': 10.0,
'Yxs': 0.5
}
def generer_donnees_synthetiques(self, t):
def modele(y, t, mu_max, Ks, Yxs):
X, S, P = y
mu = mu_max * S / (Ks + S)
dXdt = mu * X
dSdt = -dXdt / Yxs
dPdt = 0.1 * dXdt
return [dXdt, dSdt, dPdt]
y0 = [0.1, 20.0, 0.0]
solution = odeint(modele, y0, t, args=(self.parametres_reels['mu_max'], self.parametres_reels['Ks'], self.parametres_reels['Yxs']))
bruit = np.random.normal(0, self.niveau_bruit, solution.shape)
return solution + bruit
def log_vraisemblance(self, theta, t, donnees):
mu_max, Ks, Yxs = theta
def modele(y, t, mu_max, Ks, Yxs):
X, S, P = y
mu = mu_max * S / (Ks + S)
dXdt = mu * X
dSdt = -dXdt / Yxs
dPdt = 0.1 * dXdt
return [dXdt, dSdt, dPdt]
y0 = [0.1, 20.0, 0.0]
try:
solution = odeint(modele, y0, t, args=(mu_max, Ks, Yxs))
sigma2 = self.niveau_bruit ** 2
return -0.5 * np.sum((donnees - solution) ** 2 / sigma2 + np.log(2 * np.pi * sigma2))
except:
return -np.inf
def log_apriori(self, theta):
mu_max, Ks, Yxs = theta
if 0.1 < mu_max < 2.0 and 1.0 < Ks < 50.0 and 0.1 < Yxs < 1.0:
return 0.0
return -np.inf
def log_probabilite(self, theta, t, donnees):
lp = self.log_apriori(theta)
if not np.isfinite(lp):
return -np.inf
return lp + self.log_vraisemblance(theta, t, donnees)
def lancer_mcmc(self):
t = np.linspace(0, 50, 50)
donnees = self.generer_donnees_synthetiques(t)
ndim = 3
pos = [
[self.parametres_reels['mu_max'], self.parametres_reels['Ks'], self.parametres_reels['Yxs']] + 1e-4 * np.random.randn(ndim)
for i in range(self.n_marcheurs)
]
sampler = emcee.EnsembleSampler(self.n_marcheurs, ndim, self.log_probabilite, args=(t, donnees))
sampler.run_mcmc(pos, self.n_etapes, progress=True)
return sampler, t, donnees
@param.depends('n_marcheurs', 'n_etapes', 'niveau_bruit')
def vue(self):
sampler, t, donnees = self.lancer_mcmc()
fig = make_subplots(rows=2, cols=2,
subplot_titles=('Chaînes MCMC', 'Distribution postérieure', 'Ajustement du modèle', 'Corrélations des paramètres'))
echantillons = sampler.get_chain()
# Tracer les chaînes MCMC
for i in range(3):
fig.add_trace(go.Scatter(y=echantillons[:, 0, i], mode='lines', name=f'Paramètre {i+1}'), row=1, col=1)
echantillons_plats = sampler.get_chain(discard=100, thin=15, flat=True)
# Tracer les distributions postérieures
for i in range(3):
fig.add_trace(go.Histogram(x=echantillons_plats[:, i], name=f'Paramètre {i+1}', nbinsx=30), row=1, col=2)
params_med = np.median(echantillons_plats, axis=0)
y0 = [0.1, 20.0, 0.0]
# Définir une fonction de modèle pour odeint
def modele(y, t):
mu_max, Ks, Yxs = params_med
X, S, P = y
mu = mu_max * S / (Ks + S)
dXdt = mu * X
dSdt = -dXdt / Yxs
dPdt = 0.1 * dXdt
return [dXdt, dSdt, dPdt]
# Utiliser la fonction de modèle dans odeint
solution = odeint(modele, y0, t)
fig.add_trace(go.Scatter(x=t, y=donnees[:, 0], mode='markers', name='Données (X)', marker=dict(color='blue')), row=2, col=1)
fig.add_trace(go.Scatter(x=t, y=solution[:, 0], mode='lines', name='Modèle (X)', line=dict(color='red')), row=2, col=1)
# Tracer les corrélations des paramètres
fig.add_trace(go.Scatter(x=echantillons_plats[:, 0], y=echantillons_plats[:, 1],
mode='markers', marker=dict(size=2),
name='mu_max vs Ks'), row=2, col=2)
fig.update_layout(height=400, width=1200,
showlegend=True,
template='plotly_dark')
return pn.pane.Plotly(fig)
def vue_panneau(self):
return pn.Column(
pn.Row(
pn.Param(self.param,
widgets={
'n_marcheurs': pn.widgets.IntSlider,
'n_etapes': pn.widgets.IntSlider,
'niveau_bruit': pn.widgets.FloatSlider}),
self.description_mcmc()
),
self.vue # Correction pour supprimer '()' et référencer dynamiquement
)
def description_mcmc(self):
return pn.pane.Markdown("""
# Estimation des paramètres par MCMC
## Cette section permet d'estimer les paramètres du modèle de fermentation en utilisant l'algorithme MCMC (Markov Chain Monte Carlo).
- ## **n_marcheurs**: Nombre de chaînes MCMC parallèles.
- ## **n_etapes**: Nombre d'itérations pour chaque chaîne.
- ## **niveau_bruit**: Niveau de bruit dans les données synthétiques.
## Les graphiques montrent:
### 1. L'évolution des chaînes MCMC.
### 2. Les distributions postérieures des paramètres.
### 3. L'ajustement du modèle aux données.
### 4. Les corrélations entre les paramètres.
""")
# Informations personnelles et photo
photo = pn.pane.PNG(photo_url, width=200, height=200)
info = pn.Column(
pn.pane.Markdown(f"<div style='font-size:16px'><strong>Prénom</strong>: {prenom}</div>"),
pn.pane.Markdown(f"<div style='font-size:16px'><strong>Nom</strong>: {nom}</div>"),
pn.pane.Markdown(f"<div style='font-size:16px'><strong>Email</strong>: <a href='mailto:{email}'>{email}</a></div>"),
#pn.pane.Markdown(f"<div style='font-size:16px'><strong>WhatsApp</strong>: <a href='{whatsapps_url}'>WhatsApp</a></div>"),
#pn.pane.Markdown(f"<div style='font-size:16px'><strong>LinkedIn</strong>: <a href='{linkedin_url}'>LinkedIn</a></div>"),
# pn.pane.Markdown(f"<div style='font-size:16px'><strong>GitHub</strong>: <a href='{github_url}'>GitHub</a></div>"),
pn.pane.Markdown(f"<div style='font-size:16px'><strong>Université</strong>: {universite}</div>"),
pn.pane.Markdown(f"<div style='font-size:16px'><strong>Formation</strong>: {formation}</div>"),
pn.pane.Markdown(f"<div style='font-size:16px'><strong>Certificat</strong>: {certificat}</div>"),
pn.pane.Markdown(f"<div style='font-size:16px'><strong>Autre Projet </strong>: <a href='{TP_Scilab}'>Pour comprendre l'équation de Transport en 1D avec Scilab</a></div>"),
sizing_mode='stretch_width'
)
# Créer les applications
consumer_app = ConsumerResourceApp()
microbial_app = MicrobialDynamicsApp()
ferm_app = FermentationModelApp()
# Créer l'application
application_estimation_parametres = EstimationParametresFermentation()
# Afficher l'application Panel
#template = pn.template.ReactTemplate(title='Estimation des paramètres par MCMC')
#template.main[0, 0] = application_estimation_parametres.vue_panneau()
# Créer la mise en page
layout = pn.Row(
pn.Column(photo, info, width=300), # Ajouter la photo et les informations personnelles à gauche
pn.Column(
pn.Tabs(
('Consumer-Resource Dynamics', consumer_app.panel_view()),
('Microbial Dynamics', microbial_app.panel_view()),
(' Fermentation', ferm_app.panel_view()),
('Estimation Parametres', application_estimation_parametres.vue_panneau())
)
)
)
# Ajouter le layout au template principal
template.main[0, 0] = layout
# Exécuter l'application
template.servable()