File size: 5,168 Bytes
6e8da07
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
# bioprocess_model.py

import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
from scipy.optimize import curve_fit
from sklearn.metrics import mean_squared_error
from sympy import symbols, lambdify, sympify, Function

class BioprocessModel:
    def __init__(self):
        self.params = {}
        self.r2 = {}
        self.rmse = {}
        self.models = {}  # Initialize the models dictionary

    @staticmethod
    def logistic(time, xo, xm, um):
        return (xo * np.exp(um * time)) / (1 - (xo / xm) * (1 - np.exp(um * time)))

    @staticmethod
    def substrate(time, so, p, q, xo, xm, um):
        return so - (p * xo * ((np.exp(um * time)) / (1 - (xo / xm) * (1 - np.exp(um * time))) - 1)) - \
               (q * (xm / um) * np.log(1 - (xo / xm) * (1 - np.exp(um * time))))

    @staticmethod
    def product(time, po, alpha, beta, xo, xm, um):
        return po + (alpha * xo * ((np.exp(um * time) / (1 - (xo / xm) * (1 - np.exp(um * time))) - 1))) + \
               (beta * (xm / um) * np.log(1 - (xo / xm) * (1 - np.exp(um * time))))

    def set_model(self, model_type, equation, params_str):
        """
        Configures the model based on the type, equation, and parameters.

        :param model_type: Type of the model ('biomass', 'substrate', 'product')
        :param equation: The equation as a string
        :param params_str: Comma-separated string of parameter names
        """
        t_symbol = symbols('t')
        X = Function('X')  # Definir 'X(t)' como una funci贸n simb贸lica

        try:
            expr = sympify(equation)
        except Exception as e:
            raise ValueError(f"Error al parsear la ecuaci贸n '{equation}': {e}")

        params = [param.strip() for param in params_str.split(',')]
        params_symbols = symbols(params)

        # Extraer s铆mbolos utilizados en la expresi贸n
        used_symbols = expr.free_symbols
        # Convertir s铆mbolos a strings
        used_params = [str(s) for s in used_symbols if s != t_symbol]

        # Verificar que todos los par谩metros en params_str est茅n usados en la ecuaci贸n
        for param in params:
            if param not in used_params:
                raise ValueError(f"El par谩metro '{param}' no se usa en la ecuaci贸n '{equation}'.")

        if model_type == 'biomass':
            # Biomasa como funci贸n de tiempo y par谩metros
            func_expr = expr
            func = lambdify((t_symbol, *params_symbols), func_expr, 'numpy')
            self.models['biomass'] = {
                'function': func,
                'params': params
            }
        elif model_type in ['substrate', 'product']:
            # Estos modelos dependen de biomasa, que ya deber铆a estar establecida
            if 'biomass' not in self.models:
                raise ValueError("Biomasa debe estar configurada antes de Sustrato o Producto.")
            biomass_func = self.models['biomass']['function']
            # Reemplazar 'X(t)' por la funci贸n de biomasa
            func_expr = expr.subs('X(t)', biomass_func)
            func = lambdify((t_symbol, *params_symbols), func_expr, 'numpy')
            self.models[model_type] = {
                'function': func,
                'params': params
            }
        else:
            raise ValueError(f"Tipo de modelo no soportado: {model_type}")

    def fit_model(self, model_type, time, data, bounds=([-np.inf], [np.inf])):
        """
        Fits the model to the data.

        :param model_type: Type of the model ('biomass', 'substrate', 'product')
        :param time: Time data
        :param data: Observed data to fit
        :param bounds: Bounds for the parameters
        :return: Predicted data from the model
        """
        if model_type not in self.models:
            raise ValueError(f"Model type '{model_type}' is not set. Please use set_model first.")

        func = self.models[model_type]['function']
        params = self.models[model_type]['params']

        # Definir la funci贸n de ajuste
        def fit_func(t, *args):
            try:
                y = func(t, *args)
                return y
            except Exception as e:
                raise RuntimeError(f"Error en fit_func: {e}")

        # Definir una estimaci贸n inicial para los par谩metros
        p0 = [1.0] * len(params)  # Puedes ajustar estos valores seg煤n sea necesario

        try:
            # Definir los l铆mites correctamente
            lower_bounds, upper_bounds = bounds

            # Ajustar el modelo usando curve_fit con p0
            popt, _ = curve_fit(fit_func, time, data, p0=p0, bounds=(lower_bounds, upper_bounds), maxfev=10000)

            # Guardar los par谩metros ajustados en el modelo
            self.params[model_type] = {param: val for param, val in zip(params, popt)}
            y_pred = fit_func(time, *popt)
            self.r2[model_type] = 1 - (np.sum((data - y_pred) ** 2) / np.sum((data - np.mean(data)) ** 2))
            self.rmse[model_type] = np.sqrt(mean_squared_error(data, y_pred))
            return y_pred
        except Exception as e:
            raise RuntimeError(f"Error while fitting {model_type} model: {str(e)}")