pdesolver 0.1.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- pdesolver-0.1.0/PKG-INFO +99 -0
- pdesolver-0.1.0/README.md +70 -0
- pdesolver-0.1.0/pdesolver/Auxs/FuncAux.py +24 -0
- pdesolver-0.1.0/pdesolver/Auxs/PDESEncoder.py +13 -0
- pdesolver-0.1.0/pdesolver/Auxs/Visualize.py +266 -0
- pdesolver-0.1.0/pdesolver/Auxs/__init__.py +2 -0
- pdesolver-0.1.0/pdesolver/Disc/Disc.py +372 -0
- pdesolver-0.1.0/pdesolver/Disc/__init__.py +1 -0
- pdesolver-0.1.0/pdesolver/Disc/boundaries/__init__.py +44 -0
- pdesolver-0.1.0/pdesolver/Disc/boundaries/boundary_base.py +36 -0
- pdesolver-0.1.0/pdesolver/Disc/boundaries/dirichlet.py +83 -0
- pdesolver-0.1.0/pdesolver/Disc/boundaries/neumann.py +48 -0
- pdesolver-0.1.0/pdesolver/Disc/boundaries/periodic_example.py +30 -0
- pdesolver-0.1.0/pdesolver/Disc/boundaries/robin.py +51 -0
- pdesolver-0.1.0/pdesolver/PDE.py +29 -0
- pdesolver-0.1.0/pdesolver/PDES.py +175 -0
- pdesolver-0.1.0/pdesolver/Solvers/CN.py +151 -0
- pdesolver-0.1.0/pdesolver/Solvers/RKF.py +285 -0
- pdesolver-0.1.0/pdesolver/Solvers/__init__.py +3 -0
- pdesolver-0.1.0/pdesolver/Solvers/bdf2.py +177 -0
- pdesolver-0.1.0/pdesolver/Solvers/solver_base.py +259 -0
- pdesolver-0.1.0/pdesolver/Solvers/solver_base2.py +159 -0
- pdesolver-0.1.0/pdesolver/__init__.py +5 -0
- pdesolver-0.1.0/pdesolver.egg-info/PKG-INFO +99 -0
- pdesolver-0.1.0/pdesolver.egg-info/SOURCES.txt +35 -0
- pdesolver-0.1.0/pdesolver.egg-info/dependency_links.txt +1 -0
- pdesolver-0.1.0/pdesolver.egg-info/requires.txt +12 -0
- pdesolver-0.1.0/pdesolver.egg-info/top_level.txt +1 -0
- pdesolver-0.1.0/pyproject.toml +41 -0
- pdesolver-0.1.0/setup.cfg +4 -0
- pdesolver-0.1.0/tests/test_adveccao.py +73 -0
- pdesolver-0.1.0/tests/test_burgers.py +69 -0
- pdesolver-0.1.0/tests/test_calor.py +83 -0
- pdesolver-0.1.0/tests/test_fitzhugh.py +90 -0
- pdesolver-0.1.0/tests/test_load.py +23 -0
- pdesolver-0.1.0/tests/test_reacao1d.py +87 -0
- pdesolver-0.1.0/tests/tests__init__.py +1 -0
pdesolver-0.1.0/PKG-INFO
ADDED
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: pdesolver
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Solver simbólico para equações diferenciais parciais (PDEs) com discretização por diferenças finitas
|
|
5
|
+
License: MIT
|
|
6
|
+
Project-URL: Repository, https://github.com/maiocacedo/PDESsolver
|
|
7
|
+
Keywords: pde,differential equations,numerical methods,finite differences,solver
|
|
8
|
+
Classifier: Development Status :: 3 - Alpha
|
|
9
|
+
Classifier: Intended Audience :: Science/Research
|
|
10
|
+
Classifier: Topic :: Scientific/Engineering :: Mathematics
|
|
11
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
12
|
+
Classifier: Programming Language :: Python :: 3
|
|
13
|
+
Classifier: Programming Language :: Python :: 3.9
|
|
14
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
15
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
16
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
17
|
+
Requires-Python: >=3.9
|
|
18
|
+
Description-Content-Type: text/markdown
|
|
19
|
+
Requires-Dist: numpy>=1.24
|
|
20
|
+
Requires-Dist: sympy>=1.12
|
|
21
|
+
Requires-Dist: matplotlib>=3.7
|
|
22
|
+
Requires-Dist: scipy>=1.10
|
|
23
|
+
Provides-Extra: gpu
|
|
24
|
+
Requires-Dist: cupy-cuda12x; extra == "gpu"
|
|
25
|
+
Provides-Extra: dev
|
|
26
|
+
Requires-Dist: pytest>=7; extra == "dev"
|
|
27
|
+
Requires-Dist: build; extra == "dev"
|
|
28
|
+
Requires-Dist: twine; extra == "dev"
|
|
29
|
+
|
|
30
|
+
# pdesolver
|
|
31
|
+
|
|
32
|
+
Solver simbólico para equações diferenciais parciais (PDEs) usando discretização por diferenças finitas.
|
|
33
|
+
|
|
34
|
+
## Instalação
|
|
35
|
+
|
|
36
|
+
```bash
|
|
37
|
+
pip install pdesolver
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
Para suporte a GPU (CUDA 12):
|
|
41
|
+
|
|
42
|
+
```bash
|
|
43
|
+
pip install pdesolver[gpu]
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
## Uso rápido
|
|
47
|
+
|
|
48
|
+
```python
|
|
49
|
+
from pdesolver import PDE, PDES
|
|
50
|
+
|
|
51
|
+
# Equação do calor: du/dt = d2u/dt2
|
|
52
|
+
pde = PDE(
|
|
53
|
+
eq="duxy/dt = d2uxy/dx2",
|
|
54
|
+
func="u",
|
|
55
|
+
sp_var=["x"],
|
|
56
|
+
ivar=["t"],
|
|
57
|
+
ivar_boundary=[(0, 1)],
|
|
58
|
+
expr_ic="sin(pi*x)",
|
|
59
|
+
west_bd="Dirichlet", west_func_bd="0",
|
|
60
|
+
east_bd="Dirichlet", east_func_bd="0",
|
|
61
|
+
)
|
|
62
|
+
|
|
63
|
+
sistema = PDES(pdes=[pde], disc_n=[50])
|
|
64
|
+
sistema.discretize(method='central')
|
|
65
|
+
sistema.solve(method='bdf2', tf=0.1, nt=100)
|
|
66
|
+
sistema.visualize(mode='plot1d_all', tf=0.1)
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
## Métodos disponíveis
|
|
70
|
+
|
|
71
|
+
### Discretização espacial
|
|
72
|
+
- `method='central'` — diferenças centrais (padrão)
|
|
73
|
+
- `method='forward'` — diferenças progressivas
|
|
74
|
+
- `method='backward'` — diferenças regressivas
|
|
75
|
+
|
|
76
|
+
### Integração temporal
|
|
77
|
+
- `method='bdf2'` — BDF-2 implícito (padrão, recomendado)
|
|
78
|
+
- `method='CN'` — Crank-Nicolson
|
|
79
|
+
- `method='RKF'` — Runge-Kutta-Fehlberg 4(5) com CUDA *(requer cupy)*
|
|
80
|
+
|
|
81
|
+
### Condições de contorno
|
|
82
|
+
- `Dirichlet` — valor prescrito
|
|
83
|
+
- `Neumann` — fluxo prescrito
|
|
84
|
+
- `Robin` — combinação linear
|
|
85
|
+
|
|
86
|
+
### Visualização
|
|
87
|
+
- `'plot1d'` / `'plot1d_all'` — perfis 1D
|
|
88
|
+
- `'heatmap1d'` — mapa espaciotemporal 1D
|
|
89
|
+
- `'animation1d'` — animação 1D
|
|
90
|
+
- `'heatmap'` — mapa de calor 2D
|
|
91
|
+
- `'plot3d'` / `'animation3d'` — superfície 3D
|
|
92
|
+
|
|
93
|
+
## Salvar e carregar resultados
|
|
94
|
+
|
|
95
|
+
```python
|
|
96
|
+
sistema.save_to_json("resultado.json")
|
|
97
|
+
|
|
98
|
+
sistema2 = PDES.load_from_json("resultado.json")
|
|
99
|
+
```
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
# pdesolver
|
|
2
|
+
|
|
3
|
+
Solver simbólico para equações diferenciais parciais (PDEs) usando discretização por diferenças finitas.
|
|
4
|
+
|
|
5
|
+
## Instalação
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
pip install pdesolver
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
Para suporte a GPU (CUDA 12):
|
|
12
|
+
|
|
13
|
+
```bash
|
|
14
|
+
pip install pdesolver[gpu]
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
## Uso rápido
|
|
18
|
+
|
|
19
|
+
```python
|
|
20
|
+
from pdesolver import PDE, PDES
|
|
21
|
+
|
|
22
|
+
# Equação do calor: du/dt = d2u/dt2
|
|
23
|
+
pde = PDE(
|
|
24
|
+
eq="duxy/dt = d2uxy/dx2",
|
|
25
|
+
func="u",
|
|
26
|
+
sp_var=["x"],
|
|
27
|
+
ivar=["t"],
|
|
28
|
+
ivar_boundary=[(0, 1)],
|
|
29
|
+
expr_ic="sin(pi*x)",
|
|
30
|
+
west_bd="Dirichlet", west_func_bd="0",
|
|
31
|
+
east_bd="Dirichlet", east_func_bd="0",
|
|
32
|
+
)
|
|
33
|
+
|
|
34
|
+
sistema = PDES(pdes=[pde], disc_n=[50])
|
|
35
|
+
sistema.discretize(method='central')
|
|
36
|
+
sistema.solve(method='bdf2', tf=0.1, nt=100)
|
|
37
|
+
sistema.visualize(mode='plot1d_all', tf=0.1)
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
## Métodos disponíveis
|
|
41
|
+
|
|
42
|
+
### Discretização espacial
|
|
43
|
+
- `method='central'` — diferenças centrais (padrão)
|
|
44
|
+
- `method='forward'` — diferenças progressivas
|
|
45
|
+
- `method='backward'` — diferenças regressivas
|
|
46
|
+
|
|
47
|
+
### Integração temporal
|
|
48
|
+
- `method='bdf2'` — BDF-2 implícito (padrão, recomendado)
|
|
49
|
+
- `method='CN'` — Crank-Nicolson
|
|
50
|
+
- `method='RKF'` — Runge-Kutta-Fehlberg 4(5) com CUDA *(requer cupy)*
|
|
51
|
+
|
|
52
|
+
### Condições de contorno
|
|
53
|
+
- `Dirichlet` — valor prescrito
|
|
54
|
+
- `Neumann` — fluxo prescrito
|
|
55
|
+
- `Robin` — combinação linear
|
|
56
|
+
|
|
57
|
+
### Visualização
|
|
58
|
+
- `'plot1d'` / `'plot1d_all'` — perfis 1D
|
|
59
|
+
- `'heatmap1d'` — mapa espaciotemporal 1D
|
|
60
|
+
- `'animation1d'` — animação 1D
|
|
61
|
+
- `'heatmap'` — mapa de calor 2D
|
|
62
|
+
- `'plot3d'` / `'animation3d'` — superfície 3D
|
|
63
|
+
|
|
64
|
+
## Salvar e carregar resultados
|
|
65
|
+
|
|
66
|
+
```python
|
|
67
|
+
sistema.save_to_json("resultado.json")
|
|
68
|
+
|
|
69
|
+
sistema2 = PDES.load_from_json("resultado.json")
|
|
70
|
+
```
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import re
|
|
2
|
+
|
|
3
|
+
import sympy as sp
|
|
4
|
+
from sympy.parsing.sympy_parser import parse_expr
|
|
5
|
+
|
|
6
|
+
def symbol_references(in_list):
|
|
7
|
+
slist = []
|
|
8
|
+
|
|
9
|
+
for e in in_list:
|
|
10
|
+
globals()[e] = sp.Symbol(e)
|
|
11
|
+
slist.append(e)
|
|
12
|
+
return slist
|
|
13
|
+
|
|
14
|
+
def d_dt(expr_str: str) -> str:
|
|
15
|
+
t = sp.Symbol('t')
|
|
16
|
+
try:
|
|
17
|
+
e = parse_expr(expr_str, evaluate=False)
|
|
18
|
+
return str(sp.diff(e, t))
|
|
19
|
+
except Exception:
|
|
20
|
+
return expr_str
|
|
21
|
+
|
|
22
|
+
def repl_symbol(expr: str, sym: str, repl: str) -> str:
|
|
23
|
+
pattern = rf'(?<![A-Za-z0-9_]){sym}(?![A-Za-z0-9_])'
|
|
24
|
+
return re.sub(pattern, repl, expr)
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import json
|
|
2
|
+
import numpy as np
|
|
3
|
+
import sympy as sp
|
|
4
|
+
|
|
5
|
+
class PDESEncoder(json.JSONEncoder):
|
|
6
|
+
def default(self, obj):
|
|
7
|
+
if isinstance(obj, np.ndarray):
|
|
8
|
+
return obj.tolist()
|
|
9
|
+
if isinstance(obj, sp.Basic):
|
|
10
|
+
return str(obj)
|
|
11
|
+
if hasattr(obj, '__dict__'):
|
|
12
|
+
return obj.__dict__
|
|
13
|
+
return super().default(obj)
|
|
@@ -0,0 +1,266 @@
|
|
|
1
|
+
|
|
2
|
+
import numpy as np
|
|
3
|
+
import matplotlib.pyplot as plt
|
|
4
|
+
from matplotlib.animation import FuncAnimation
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
def visualize(pdes_obj, mode='heatmap', func_idx=0, time_step=-1, **kwargs):
|
|
8
|
+
|
|
9
|
+
if pdes_obj.results is None:
|
|
10
|
+
print("Erro: rode .solve() antes de visualizar.")
|
|
11
|
+
return
|
|
12
|
+
|
|
13
|
+
_, historico = pdes_obj.results
|
|
14
|
+
|
|
15
|
+
if not historico or not historico[func_idx]:
|
|
16
|
+
print("Erro: histórico vazio.")
|
|
17
|
+
return
|
|
18
|
+
|
|
19
|
+
if _is_1d(pdes_obj):
|
|
20
|
+
x = _get_x1d(pdes_obj)
|
|
21
|
+
if mode in ('plot1d', 'plot'):
|
|
22
|
+
plot1d(historico, x, pdes_obj.funcs, func_idx, time_step, **kwargs)
|
|
23
|
+
elif mode == 'plot1d_all':
|
|
24
|
+
plot1d_all(historico, x, pdes_obj.funcs, func_idx, **kwargs)
|
|
25
|
+
elif mode == 'heatmap1d':
|
|
26
|
+
heatmap1d(historico, x, pdes_obj.funcs, pdes_obj.pdes, func_idx, **kwargs)
|
|
27
|
+
elif mode == 'animation1d':
|
|
28
|
+
animate1d(historico, x, pdes_obj.funcs, func_idx, **kwargs)
|
|
29
|
+
else:
|
|
30
|
+
print(f"Modo '{mode}' não reconhecido para 1D. "
|
|
31
|
+
f"Use: 'plot1d', 'plot1d_all', 'heatmap1d', 'animation1d'.")
|
|
32
|
+
return
|
|
33
|
+
|
|
34
|
+
nx, ny = pdes_obj.disc_n
|
|
35
|
+
x = np.linspace(0, 1, nx)
|
|
36
|
+
y = np.linspace(0, 1, ny)
|
|
37
|
+
X, Y = np.meshgrid(x, y, indexing='ij')
|
|
38
|
+
|
|
39
|
+
if mode == 'heatmap':
|
|
40
|
+
plot_heatmap(historico, X, Y, pdes_obj.funcs, pdes_obj.disc_n, func_idx, time_step)
|
|
41
|
+
elif mode == 'animation':
|
|
42
|
+
animate2d(historico, X, Y, pdes_obj.funcs, pdes_obj.disc_n, func_idx, **kwargs)
|
|
43
|
+
elif mode == 'plot3d':
|
|
44
|
+
plot3d(historico, X, Y, pdes_obj.funcs, pdes_obj.disc_n, func_idx, time_step, **kwargs)
|
|
45
|
+
elif mode == 'animation3d':
|
|
46
|
+
animate3d(historico, X, Y, pdes_obj.funcs, pdes_obj.disc_n, func_idx, **kwargs)
|
|
47
|
+
else:
|
|
48
|
+
print(f"Modo '{mode}' desconhecido para 2D. "
|
|
49
|
+
f"Use: 'heatmap', 'animation', 'plot3d', 'animation3d'.")
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
def _is_1d(pdes_obj):
|
|
53
|
+
return len(pdes_obj.disc_n) == 1
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
def _get_x1d(pdes_obj):
|
|
57
|
+
a, b = pdes_obj.pdes[0].ivar_boundary[0]
|
|
58
|
+
return np.linspace(a, b, pdes_obj.disc_n[0])
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
def plot1d(historico, x, funcs, func_idx, time_step, **kwargs):
|
|
62
|
+
color = kwargs.get('color', 'steelblue')
|
|
63
|
+
lw = kwargs.get('lw', 2)
|
|
64
|
+
|
|
65
|
+
data = np.array(historico[func_idx][time_step])
|
|
66
|
+
label = time_step if time_step >= 0 else len(historico[func_idx]) + time_step
|
|
67
|
+
|
|
68
|
+
fig, ax = plt.subplots(figsize=(7, 4))
|
|
69
|
+
ax.plot(x, data, color=color, linewidth=lw, label=f't = passo {label}')
|
|
70
|
+
ax.set_xlabel('x')
|
|
71
|
+
ax.set_ylabel(funcs[func_idx])
|
|
72
|
+
ax.set_title(f"Perfil de {funcs[func_idx]} — passo {label}")
|
|
73
|
+
ax.legend()
|
|
74
|
+
ax.grid(True, alpha=0.3)
|
|
75
|
+
plt.tight_layout()
|
|
76
|
+
plt.show()
|
|
77
|
+
|
|
78
|
+
|
|
79
|
+
def plot1d_all(historico, x, funcs, func_idx, **kwargs):
|
|
80
|
+
n_profiles = kwargs.get('n_profiles', 10)
|
|
81
|
+
cmap = kwargs.get('cmap', 'viridis')
|
|
82
|
+
lw = kwargs.get('lw', 1.5)
|
|
83
|
+
tf_real = kwargs.get('tf', None)
|
|
84
|
+
|
|
85
|
+
n_passos = len(historico[func_idx])
|
|
86
|
+
indices = np.linspace(0, n_passos - 1, n_profiles, dtype=int)
|
|
87
|
+
cm = plt.get_cmap(cmap)
|
|
88
|
+
colors = [cm(i / (n_profiles - 1)) for i in range(n_profiles)]
|
|
89
|
+
|
|
90
|
+
fig, ax = plt.subplots(figsize=(8, 5))
|
|
91
|
+
|
|
92
|
+
for idx_cor, idx_passo in enumerate(indices):
|
|
93
|
+
data = np.array(historico[func_idx][idx_passo])
|
|
94
|
+
if tf_real is not None:
|
|
95
|
+
t_val = tf_real * idx_passo / (n_passos - 1)
|
|
96
|
+
lbl = f't = {t_val:.2f}'
|
|
97
|
+
else:
|
|
98
|
+
lbl = f'passo {idx_passo}'
|
|
99
|
+
ax.plot(x, data, color=colors[idx_cor], linewidth=lw, label=lbl)
|
|
100
|
+
|
|
101
|
+
ax.set_xlabel('x')
|
|
102
|
+
ax.set_ylabel(funcs[func_idx])
|
|
103
|
+
ax.set_title(f"Evolução de {funcs[func_idx]} ao longo do tempo")
|
|
104
|
+
ax.legend(fontsize=8, loc='best')
|
|
105
|
+
ax.grid(True, alpha=0.3)
|
|
106
|
+
|
|
107
|
+
sm = plt.cm.ScalarMappable(cmap=cmap, norm=plt.Normalize(vmin=0, vmax=1))
|
|
108
|
+
sm.set_array([])
|
|
109
|
+
cbar = plt.colorbar(sm, ax=ax, pad=0.02)
|
|
110
|
+
cbar.set_label('t / tf')
|
|
111
|
+
|
|
112
|
+
plt.tight_layout()
|
|
113
|
+
plt.show()
|
|
114
|
+
|
|
115
|
+
|
|
116
|
+
def heatmap1d(historico, x, funcs, pdes, func_idx, **kwargs):
|
|
117
|
+
cmap = kwargs.get('cmap', 'viridis')
|
|
118
|
+
tf_real = kwargs.get('tf', None)
|
|
119
|
+
|
|
120
|
+
matriz = np.array(historico[func_idx])
|
|
121
|
+
n_passos = matriz.shape[0]
|
|
122
|
+
t_max = tf_real if tf_real is not None else n_passos - 1
|
|
123
|
+
a, b = pdes[0].ivar_boundary[0]
|
|
124
|
+
|
|
125
|
+
fig, ax = plt.subplots(figsize=(8, 5))
|
|
126
|
+
im = ax.imshow(
|
|
127
|
+
matriz.T,
|
|
128
|
+
aspect='auto',
|
|
129
|
+
origin='lower',
|
|
130
|
+
extent=[0, t_max, a, b],
|
|
131
|
+
cmap=cmap
|
|
132
|
+
)
|
|
133
|
+
ax.set_xlabel('t')
|
|
134
|
+
ax.set_ylabel('x')
|
|
135
|
+
ax.set_title(f"Evolução espaciotemporal de {funcs[func_idx]}")
|
|
136
|
+
plt.colorbar(im, ax=ax, label=funcs[func_idx])
|
|
137
|
+
plt.tight_layout()
|
|
138
|
+
plt.show()
|
|
139
|
+
|
|
140
|
+
|
|
141
|
+
def animate1d(historico, x, funcs, func_idx, **kwargs):
|
|
142
|
+
frames_step = kwargs.get('frames_step', 1)
|
|
143
|
+
interval = kwargs.get('interval', 50)
|
|
144
|
+
color = kwargs.get('color', 'steelblue')
|
|
145
|
+
lw = kwargs.get('lw', 2)
|
|
146
|
+
tf_real = kwargs.get('tf', None)
|
|
147
|
+
|
|
148
|
+
n_passos = len(historico[func_idx])
|
|
149
|
+
frames = list(range(0, n_passos, frames_step))
|
|
150
|
+
|
|
151
|
+
todos = np.concatenate([historico[func_idx][f] for f in frames])
|
|
152
|
+
y_min, y_max = todos.min(), todos.max()
|
|
153
|
+
margem = (y_max - y_min) * 0.05 or 0.1
|
|
154
|
+
|
|
155
|
+
fig, ax = plt.subplots(figsize=(8, 4))
|
|
156
|
+
linha, = ax.plot(x, historico[func_idx][0], color=color, linewidth=lw)
|
|
157
|
+
ax.set_xlim(x[0], x[-1])
|
|
158
|
+
ax.set_ylim(y_min - margem, y_max + margem)
|
|
159
|
+
ax.set_xlabel('x')
|
|
160
|
+
ax.set_ylabel(funcs[func_idx])
|
|
161
|
+
ax.grid(True, alpha=0.3)
|
|
162
|
+
titulo = ax.set_title('')
|
|
163
|
+
|
|
164
|
+
def update(frame_idx):
|
|
165
|
+
passo = frames[frame_idx]
|
|
166
|
+
linha.set_ydata(np.array(historico[func_idx][passo]))
|
|
167
|
+
if tf_real is not None:
|
|
168
|
+
t_val = tf_real * passo / (n_passos - 1)
|
|
169
|
+
titulo.set_text(f"{funcs[func_idx]} — t = {t_val:.3f}")
|
|
170
|
+
else:
|
|
171
|
+
titulo.set_text(f"{funcs[func_idx]} — passo {passo}")
|
|
172
|
+
return linha, titulo
|
|
173
|
+
|
|
174
|
+
anim = FuncAnimation(fig, update, frames=len(frames),
|
|
175
|
+
interval=interval, blit=True)
|
|
176
|
+
plt.tight_layout()
|
|
177
|
+
plt.show()
|
|
178
|
+
|
|
179
|
+
def plot_heatmap(historico, X, Y, funcs, disc_n, func_idx, time_step):
|
|
180
|
+
data = np.array(historico[func_idx][time_step]).reshape(disc_n)
|
|
181
|
+
plt.figure(figsize=(7, 6))
|
|
182
|
+
contorno = plt.contourf(X, Y, data, levels=30, cmap='hot')
|
|
183
|
+
plt.title(f"Distribuição de {funcs[func_idx]} no passo {time_step}")
|
|
184
|
+
plt.colorbar(contorno)
|
|
185
|
+
plt.show()
|
|
186
|
+
|
|
187
|
+
|
|
188
|
+
def animate2d(historico, X, Y, funcs, disc_n, func_idx, **kwargs):
|
|
189
|
+
frames_step = kwargs.get('frames_step', 1)
|
|
190
|
+
interval = kwargs.get('interval', 50)
|
|
191
|
+
|
|
192
|
+
fig, ax = plt.subplots(figsize=(7, 6))
|
|
193
|
+
Z_ini = np.array(historico[func_idx][0]).reshape(disc_n)
|
|
194
|
+
contorno = ax.contourf(X, Y, Z_ini, levels=30, cmap='hot')
|
|
195
|
+
fig.colorbar(contorno, ax=ax)
|
|
196
|
+
|
|
197
|
+
def update(frame):
|
|
198
|
+
ax.clear()
|
|
199
|
+
Z = np.array(historico[func_idx][frame]).reshape(disc_n)
|
|
200
|
+
cont = ax.contourf(X, Y, Z, levels=30, cmap='hot')
|
|
201
|
+
ax.set_title(f"Evolução de {funcs[func_idx]} - Passo {frame}")
|
|
202
|
+
ax.set_xlabel('x')
|
|
203
|
+
ax.set_ylabel('y')
|
|
204
|
+
return cont,
|
|
205
|
+
|
|
206
|
+
frames_list = range(0, len(historico[func_idx]), frames_step)
|
|
207
|
+
anim = FuncAnimation(fig, update, frames=frames_list,
|
|
208
|
+
interval=interval, blit=False)
|
|
209
|
+
plt.show()
|
|
210
|
+
|
|
211
|
+
|
|
212
|
+
def plot3d(historico, X, Y, funcs, disc_n, func_idx, time_step, **kwargs):
|
|
213
|
+
cmap = kwargs.get('cmap', 'hot')
|
|
214
|
+
alpha = kwargs.get('alpha', 1.0)
|
|
215
|
+
elev = kwargs.get('elev', 30)
|
|
216
|
+
azim = kwargs.get('azim', -60)
|
|
217
|
+
|
|
218
|
+
data = np.array(historico[func_idx][time_step]).reshape(disc_n)
|
|
219
|
+
label = time_step if time_step >= 0 else len(historico[func_idx]) + time_step
|
|
220
|
+
|
|
221
|
+
fig = plt.figure(figsize=(8, 6))
|
|
222
|
+
ax = fig.add_subplot(111, projection='3d')
|
|
223
|
+
ax.plot_surface(X, Y, data, cmap=cmap, alpha=alpha)
|
|
224
|
+
ax.set_title(f"Superfície 3D de {funcs[func_idx]} - Passo {label}")
|
|
225
|
+
ax.set_xlabel('x')
|
|
226
|
+
ax.set_ylabel('y')
|
|
227
|
+
ax.set_zlabel(str(funcs[func_idx]))
|
|
228
|
+
ax.view_init(elev=elev, azim=azim)
|
|
229
|
+
plt.tight_layout()
|
|
230
|
+
plt.show()
|
|
231
|
+
|
|
232
|
+
|
|
233
|
+
def animate3d(historico, X, Y, funcs, disc_n, func_idx, **kwargs):
|
|
234
|
+
frames_step = kwargs.get('frames_step', 1)
|
|
235
|
+
interval = kwargs.get('interval', 50)
|
|
236
|
+
cmap = kwargs.get('cmap', 'hot')
|
|
237
|
+
alpha = kwargs.get('alpha', 1.0)
|
|
238
|
+
elev = kwargs.get('elev', 30)
|
|
239
|
+
azim = kwargs.get('azim', -60)
|
|
240
|
+
|
|
241
|
+
todos_frames = [
|
|
242
|
+
np.array(historico[func_idx][f]).reshape(disc_n)
|
|
243
|
+
for f in range(0, len(historico[func_idx]), frames_step)
|
|
244
|
+
]
|
|
245
|
+
z_min = min(Z.min() for Z in todos_frames)
|
|
246
|
+
z_max = max(Z.max() for Z in todos_frames)
|
|
247
|
+
|
|
248
|
+
fig = plt.figure(figsize=(8, 6))
|
|
249
|
+
ax = fig.add_subplot(111, projection='3d')
|
|
250
|
+
|
|
251
|
+
def update(frame):
|
|
252
|
+
ax.clear()
|
|
253
|
+
Z = todos_frames[frame]
|
|
254
|
+
ax.plot_surface(X, Y, Z, cmap=cmap, alpha=alpha)
|
|
255
|
+
ax.set_title(
|
|
256
|
+
f"Evolução 3D de {funcs[func_idx]} - Passo {frame * frames_step}"
|
|
257
|
+
)
|
|
258
|
+
ax.set_xlabel('x')
|
|
259
|
+
ax.set_ylabel('y')
|
|
260
|
+
ax.set_zlabel(str(funcs[func_idx]))
|
|
261
|
+
ax.set_zlim(z_min, z_max)
|
|
262
|
+
ax.view_init(elev=elev, azim=azim)
|
|
263
|
+
|
|
264
|
+
anim = FuncAnimation(fig, update, frames=len(todos_frames),
|
|
265
|
+
interval=interval, blit=False)
|
|
266
|
+
plt.show()
|