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.
Files changed (37) hide show
  1. pdesolver-0.1.0/PKG-INFO +99 -0
  2. pdesolver-0.1.0/README.md +70 -0
  3. pdesolver-0.1.0/pdesolver/Auxs/FuncAux.py +24 -0
  4. pdesolver-0.1.0/pdesolver/Auxs/PDESEncoder.py +13 -0
  5. pdesolver-0.1.0/pdesolver/Auxs/Visualize.py +266 -0
  6. pdesolver-0.1.0/pdesolver/Auxs/__init__.py +2 -0
  7. pdesolver-0.1.0/pdesolver/Disc/Disc.py +372 -0
  8. pdesolver-0.1.0/pdesolver/Disc/__init__.py +1 -0
  9. pdesolver-0.1.0/pdesolver/Disc/boundaries/__init__.py +44 -0
  10. pdesolver-0.1.0/pdesolver/Disc/boundaries/boundary_base.py +36 -0
  11. pdesolver-0.1.0/pdesolver/Disc/boundaries/dirichlet.py +83 -0
  12. pdesolver-0.1.0/pdesolver/Disc/boundaries/neumann.py +48 -0
  13. pdesolver-0.1.0/pdesolver/Disc/boundaries/periodic_example.py +30 -0
  14. pdesolver-0.1.0/pdesolver/Disc/boundaries/robin.py +51 -0
  15. pdesolver-0.1.0/pdesolver/PDE.py +29 -0
  16. pdesolver-0.1.0/pdesolver/PDES.py +175 -0
  17. pdesolver-0.1.0/pdesolver/Solvers/CN.py +151 -0
  18. pdesolver-0.1.0/pdesolver/Solvers/RKF.py +285 -0
  19. pdesolver-0.1.0/pdesolver/Solvers/__init__.py +3 -0
  20. pdesolver-0.1.0/pdesolver/Solvers/bdf2.py +177 -0
  21. pdesolver-0.1.0/pdesolver/Solvers/solver_base.py +259 -0
  22. pdesolver-0.1.0/pdesolver/Solvers/solver_base2.py +159 -0
  23. pdesolver-0.1.0/pdesolver/__init__.py +5 -0
  24. pdesolver-0.1.0/pdesolver.egg-info/PKG-INFO +99 -0
  25. pdesolver-0.1.0/pdesolver.egg-info/SOURCES.txt +35 -0
  26. pdesolver-0.1.0/pdesolver.egg-info/dependency_links.txt +1 -0
  27. pdesolver-0.1.0/pdesolver.egg-info/requires.txt +12 -0
  28. pdesolver-0.1.0/pdesolver.egg-info/top_level.txt +1 -0
  29. pdesolver-0.1.0/pyproject.toml +41 -0
  30. pdesolver-0.1.0/setup.cfg +4 -0
  31. pdesolver-0.1.0/tests/test_adveccao.py +73 -0
  32. pdesolver-0.1.0/tests/test_burgers.py +69 -0
  33. pdesolver-0.1.0/tests/test_calor.py +83 -0
  34. pdesolver-0.1.0/tests/test_fitzhugh.py +90 -0
  35. pdesolver-0.1.0/tests/test_load.py +23 -0
  36. pdesolver-0.1.0/tests/test_reacao1d.py +87 -0
  37. pdesolver-0.1.0/tests/tests__init__.py +1 -0
@@ -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()
@@ -0,0 +1,2 @@
1
+ from .FuncAux import symbol_references, repl_symbol, d_dt
2
+ from .Visualize import visualize