fargopy 0.3.12__py3-none-any.whl → 0.3.14__py3-none-any.whl
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.
- fargopy/__init__.py +1 -0
- fargopy/fsimulation.py +603 -0
- fargopy/version.py +1 -1
- {fargopy-0.3.12.dist-info → fargopy-0.3.14.dist-info}/METADATA +19 -4
- fargopy-0.3.14.dist-info/RECORD +16 -0
- {fargopy-0.3.12.dist-info → fargopy-0.3.14.dist-info}/WHEEL +1 -1
- fargopy-0.3.12.dist-info/RECORD +0 -15
- {fargopy-0.3.12.data → fargopy-0.3.14.data}/scripts/ifargopy +0 -0
- {fargopy-0.3.12.dist-info → fargopy-0.3.14.dist-info}/entry_points.txt +0 -0
- {fargopy-0.3.12.dist-info → fargopy-0.3.14.dist-info/licenses}/LICENSE +0 -0
- {fargopy-0.3.12.dist-info → fargopy-0.3.14.dist-info}/top_level.txt +0 -0
fargopy/__init__.py
CHANGED
fargopy/fsimulation.py
ADDED
|
@@ -0,0 +1,603 @@
|
|
|
1
|
+
import pandas as pd
|
|
2
|
+
import numpy as np
|
|
3
|
+
import fargopy as fp
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
import matplotlib.pyplot as plt
|
|
7
|
+
import plotly.figure_factory as ff
|
|
8
|
+
from plotly.subplots import make_subplots
|
|
9
|
+
import plotly.graph_objects as go
|
|
10
|
+
from matplotlib.animation import FFMpegWriter
|
|
11
|
+
|
|
12
|
+
from ipywidgets import interact, FloatSlider,IntSlider
|
|
13
|
+
from celluloid import Camera
|
|
14
|
+
from IPython.display import HTML
|
|
15
|
+
from IPython.display import Video
|
|
16
|
+
|
|
17
|
+
from scipy.interpolate import griddata
|
|
18
|
+
from scipy.integrate import solve_ivp
|
|
19
|
+
from tqdm import tqdm
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
class DataHandler:
|
|
25
|
+
def __init__(self, sim):
|
|
26
|
+
self.sim = sim
|
|
27
|
+
self.df = None
|
|
28
|
+
self.plane = None
|
|
29
|
+
|
|
30
|
+
def load_data(self, plane, angle, num_snapshots):
|
|
31
|
+
self.plane = plane
|
|
32
|
+
snapshots = np.arange(1, num_snapshots + 1)
|
|
33
|
+
time_values = snapshots / num_snapshots
|
|
34
|
+
|
|
35
|
+
df_snapshots = pd.DataFrame(columns=["snapshot", "time", "vel1", "vel2", "gasdens", "coord1", "coord2"])
|
|
36
|
+
|
|
37
|
+
for i, snap in enumerate(snapshots):
|
|
38
|
+
gasv = self.sim.load_field('gasv', snapshot=snap, type='vector')
|
|
39
|
+
gasvx, gasvy, gasvz = gasv.to_cartesian()
|
|
40
|
+
gasd = self.sim.load_field('gasdens', snapshot=snap, type='scalar')
|
|
41
|
+
|
|
42
|
+
if plane == 'XZ':
|
|
43
|
+
vel1_slice, mesh = gasvx.meshslice(slice=angle)
|
|
44
|
+
vel2_slice, _ = gasvz.meshslice(slice=angle)
|
|
45
|
+
coord1, coord2 = mesh.x, mesh.z
|
|
46
|
+
elif plane == 'XY':
|
|
47
|
+
vel1_slice, mesh = gasvx.meshslice(slice=angle)
|
|
48
|
+
vel2_slice, _ = gasvy.meshslice(slice=angle)
|
|
49
|
+
coord1, coord2 = mesh.x, mesh.y
|
|
50
|
+
elif plane == 'YZ':
|
|
51
|
+
vel1_slice, mesh = gasvy.meshslice(slice='r=1.0')
|
|
52
|
+
vel2_slice, _ = gasvz.meshslice(slice='r=1.0')
|
|
53
|
+
coord1, coord2 = mesh.y, mesh.z
|
|
54
|
+
|
|
55
|
+
gasd_slice, _ = gasd.meshslice(slice=angle)
|
|
56
|
+
df_snapshots.loc[i] = [snap, time_values[i], vel1_slice, vel2_slice, gasd_slice, coord1, coord2]
|
|
57
|
+
|
|
58
|
+
self.df = df_snapshots
|
|
59
|
+
return df_snapshots
|
|
60
|
+
|
|
61
|
+
def interpolate_field(self, time, var1, var2, field_name):
|
|
62
|
+
df_sorted = self.df.sort_values("time")
|
|
63
|
+
idx = df_sorted["time"].searchsorted(time) - 1
|
|
64
|
+
if idx == -1:
|
|
65
|
+
idx = 0
|
|
66
|
+
idx_after = min(idx + 1, len(df_sorted) - 1)
|
|
67
|
+
|
|
68
|
+
t0, t1 = df_sorted.iloc[idx]["time"], df_sorted.iloc[idx_after]["time"]
|
|
69
|
+
factor = (time - t0) / (t1 - t0) if t1 > t0 else 0
|
|
70
|
+
if factor < 0:
|
|
71
|
+
factor = 0
|
|
72
|
+
|
|
73
|
+
def interp(idx):
|
|
74
|
+
coord1, coord2 = df_sorted.iloc[idx]["coord1"], df_sorted.iloc[idx]["coord2"]
|
|
75
|
+
points = np.column_stack((coord1.ravel(), coord2.ravel()))
|
|
76
|
+
data = df_sorted.iloc[idx][field_name].ravel()
|
|
77
|
+
return griddata(points, data, (var1, var2), method='linear', fill_value=0.0)
|
|
78
|
+
|
|
79
|
+
result = (1 - factor) * interp(idx) + factor * interp(idx_after)
|
|
80
|
+
return result
|
|
81
|
+
|
|
82
|
+
def interpolate_velocity(self, time, var1, var2):
|
|
83
|
+
v1 = self.interpolate_field(time, var1, var2, "vel1")
|
|
84
|
+
v2 = self.interpolate_field(time, var1, var2, "vel2")
|
|
85
|
+
return v1, v2
|
|
86
|
+
|
|
87
|
+
def interpolate_density(self, time, var1, var2):
|
|
88
|
+
return self.interpolate_field(time, var1, var2, "gasdens")
|
|
89
|
+
|
|
90
|
+
|
|
91
|
+
class Simulation:
|
|
92
|
+
def __init__(self, plane, angle, num_snapshots, dir_path):
|
|
93
|
+
self.sim = fp.Simulation(output_dir=dir_path)
|
|
94
|
+
self.data_handler = DataHandler(self.sim)
|
|
95
|
+
self.data_handler.load_data(plane, angle, num_snapshots)
|
|
96
|
+
|
|
97
|
+
def velocity_field(self, t, y):
|
|
98
|
+
var1, var2 = y
|
|
99
|
+
v1, v2 = self.data_handler.interpolate_velocity(t, np.array([var1]), np.array([var2]))
|
|
100
|
+
return np.array([v1[0], v2[0]])
|
|
101
|
+
|
|
102
|
+
def integrate_particles(self, particle_pos, time, dt=0.01):
|
|
103
|
+
""" Integra todas las partículas con un paso explícito de Euler."""
|
|
104
|
+
if len(particle_pos) == 0:
|
|
105
|
+
return np.array([])
|
|
106
|
+
|
|
107
|
+
v1, v2 = self.data_handler.interpolate_velocity(time, particle_pos[:, 0], particle_pos[:, 1])
|
|
108
|
+
|
|
109
|
+
# Paso de Euler: x_{n+1} = x_n + v * dt
|
|
110
|
+
particle_pos[:, 0] += v1 * dt
|
|
111
|
+
particle_pos[:, 1] += v2 * dt
|
|
112
|
+
|
|
113
|
+
return particle_pos
|
|
114
|
+
|
|
115
|
+
def generate_uniform_particles(self, var1_min, var1_max, var2_min, var2_max, num_particles):
|
|
116
|
+
grid_size = int(np.sqrt(num_particles))
|
|
117
|
+
var1_candidates = np.linspace(var1_min + 0.01, var1_max - 0.01, grid_size)
|
|
118
|
+
var2_candidates = np.linspace(var2_min + 0.001, var2_max - 0.001, grid_size)
|
|
119
|
+
VAR1_grid, VAR2_grid = np.meshgrid(var1_candidates, var2_candidates, indexing='ij')
|
|
120
|
+
|
|
121
|
+
density_values = self.data_handler.interpolate_density(0, VAR1_grid, VAR2_grid)
|
|
122
|
+
valid_mask = density_values > 0
|
|
123
|
+
|
|
124
|
+
valid_var1 = VAR1_grid[valid_mask]
|
|
125
|
+
valid_var2 = VAR2_grid[valid_mask]
|
|
126
|
+
|
|
127
|
+
if len(valid_var1) == 0:
|
|
128
|
+
return []
|
|
129
|
+
|
|
130
|
+
num_valid_points = min(num_particles, len(valid_var1))
|
|
131
|
+
new_particles = np.column_stack((valid_var1[:num_valid_points], valid_var2[:num_valid_points]))
|
|
132
|
+
|
|
133
|
+
return new_particles
|
|
134
|
+
|
|
135
|
+
def run_simulation(self, res, var1_min, var1_max, var2_min, var2_max, ts, npi, max_lifetime, generation_interval):
|
|
136
|
+
var1_reg, var2_reg = np.linspace(var1_min, var1_max, res), np.linspace(var2_min, var2_max, res)
|
|
137
|
+
VAR1_reg, VAR2_reg = np.meshgrid(var1_reg, var2_reg, indexing='ij')
|
|
138
|
+
|
|
139
|
+
t_span = (0, 1)
|
|
140
|
+
t_eval = np.linspace(t_span[0], t_span[1], ts)
|
|
141
|
+
|
|
142
|
+
particle_pos = np.empty((0, 2))
|
|
143
|
+
lifetimes = np.empty(0)
|
|
144
|
+
new_particles = self.generate_uniform_particles(var1_min, var1_max, var2_min, var2_max, npi)
|
|
145
|
+
|
|
146
|
+
# Determinar el label del eje y en función del plano
|
|
147
|
+
plane = self.data_handler.plane
|
|
148
|
+
y_label = "Z [AU]" if plane == "XZ" else r"$\phi$ [rad]"
|
|
149
|
+
|
|
150
|
+
fig, ax = plt.subplots(figsize=(8, 8))
|
|
151
|
+
camera = Camera(fig)
|
|
152
|
+
|
|
153
|
+
with tqdm(total=len(t_eval), desc="Generando animación", unit="frame") as pbar:
|
|
154
|
+
for frame in range(len(t_eval)):
|
|
155
|
+
time = t_eval[frame]
|
|
156
|
+
if frame % generation_interval == 0:
|
|
157
|
+
particle_pos = np.vstack([particle_pos, new_particles])
|
|
158
|
+
lifetimes = np.concatenate([lifetimes, np.full(len(new_particles), max_lifetime)])
|
|
159
|
+
|
|
160
|
+
updated_pos = self.integrate_particles(particle_pos, time, dt=0.01)
|
|
161
|
+
updated_pos = np.array([pos for pos in updated_pos if pos is not None])
|
|
162
|
+
updated_lifetimes = lifetimes - 1
|
|
163
|
+
|
|
164
|
+
valid_indices = updated_lifetimes > 0
|
|
165
|
+
particle_pos = updated_pos[valid_indices]
|
|
166
|
+
lifetimes = updated_lifetimes[valid_indices]
|
|
167
|
+
|
|
168
|
+
lifetimes_normalized = lifetimes / max_lifetime
|
|
169
|
+
|
|
170
|
+
# Agregar interpolación de densidad como fondo
|
|
171
|
+
gasd_interpolated = self.data_handler.interpolate_density(time, VAR1_reg, VAR2_reg)
|
|
172
|
+
c = ax.pcolormesh(VAR1_reg, VAR2_reg, np.log10(gasd_interpolated * self.sim.URHO * 1e3),
|
|
173
|
+
cmap="viridis", shading='auto')
|
|
174
|
+
|
|
175
|
+
# Graficar partículas
|
|
176
|
+
if len(particle_pos) > 0:
|
|
177
|
+
ax.scatter(particle_pos[:, 0], particle_pos[:, 1], c='lightgray', alpha=lifetimes_normalized, s=1.0)
|
|
178
|
+
|
|
179
|
+
ax.set_xlim(var1_min, var1_max)
|
|
180
|
+
ax.set_ylim(var2_min, var2_max)
|
|
181
|
+
ax.set_xlabel(r"$r \ [AU]$",size=12)
|
|
182
|
+
ax.set_ylabel(y_label,size=12)
|
|
183
|
+
camera.snap()
|
|
184
|
+
|
|
185
|
+
pbar.update(1)
|
|
186
|
+
|
|
187
|
+
# Agregar barra de color
|
|
188
|
+
fig.colorbar(c, ax=ax, label=r'$\log_{10}(\rho)$ [kg/m$^3$]')
|
|
189
|
+
plt.close(fig)
|
|
190
|
+
animation = camera.animate()
|
|
191
|
+
video_filename = 'figures/streaklines.mp4'
|
|
192
|
+
animation.save(video_filename, writer=FFMpegWriter(fps=10, codec='libx264', bitrate=5000))
|
|
193
|
+
|
|
194
|
+
# Mostrar el video en el entorno interactivo
|
|
195
|
+
return Video(video_filename, embed=True)
|
|
196
|
+
|
|
197
|
+
|
|
198
|
+
class Visualize:
|
|
199
|
+
def __init__(self, data_handler):
|
|
200
|
+
"""
|
|
201
|
+
Inicializa la clase Visualize con una instancia de DataHandler.
|
|
202
|
+
"""
|
|
203
|
+
self.data_handler = data_handler
|
|
204
|
+
|
|
205
|
+
def density(self, var1_min, var1_max, var2_min, var2_max, res, time):
|
|
206
|
+
"""
|
|
207
|
+
Grafica un mapa de contornos de densidad interpolada en un tiempo dado.
|
|
208
|
+
"""
|
|
209
|
+
# Crear una cuadrícula regular para las coordenadas
|
|
210
|
+
var1 = np.linspace(var1_min, var1_max, res)
|
|
211
|
+
var2 = np.linspace(var2_min, var2_max, res)
|
|
212
|
+
VAR1, VAR2 = np.meshgrid(var1, var2, indexing='ij')
|
|
213
|
+
|
|
214
|
+
# Interpolar la densidad en el tiempo dado
|
|
215
|
+
density = self.data_handler.interpolate_density(time, VAR1, VAR2).T
|
|
216
|
+
|
|
217
|
+
# Determinar el label del eje y en función del plano
|
|
218
|
+
plane = self.data_handler.plane
|
|
219
|
+
y_label = "Z [AU]" if plane == "XZ" else r"$\phi$ [rad]"
|
|
220
|
+
|
|
221
|
+
# Crear el gráfico de contorno
|
|
222
|
+
fig = go.Figure(
|
|
223
|
+
data=go.Contour(
|
|
224
|
+
z=np.log10(density * self.data_handler.sim.URHO * 1e3),
|
|
225
|
+
x=var1,
|
|
226
|
+
y=var2,
|
|
227
|
+
colorscale="Spectral_r",
|
|
228
|
+
contours=dict(coloring="heatmap"),
|
|
229
|
+
colorbar=dict(title="log10(ρ) [kg/m³]")
|
|
230
|
+
)
|
|
231
|
+
)
|
|
232
|
+
|
|
233
|
+
# Configurar el diseño del gráfico
|
|
234
|
+
fig.update_layout(
|
|
235
|
+
title=f"Mapa de Contornos de Densidad (t = {time:.2f})",
|
|
236
|
+
xaxis_title="r [AU]",
|
|
237
|
+
yaxis_title=y_label,
|
|
238
|
+
width=600,
|
|
239
|
+
height=600
|
|
240
|
+
)
|
|
241
|
+
|
|
242
|
+
# Mostrar el gráfico
|
|
243
|
+
fig.show()
|
|
244
|
+
|
|
245
|
+
|
|
246
|
+
|
|
247
|
+
|
|
248
|
+
def animate_density(self, var1_min, var1_max, var2_min, var2_max, res, time_array):
|
|
249
|
+
|
|
250
|
+
# Crear una cuadrícula regular para las coordenadas
|
|
251
|
+
var1 = np.linspace(var1_min, var1_max, res)
|
|
252
|
+
var2 = np.linspace(var2_min, var2_max, res)
|
|
253
|
+
VAR1, VAR2 = np.meshgrid(var1, var2, indexing='ij')
|
|
254
|
+
|
|
255
|
+
# Precalcular el campo de densidad para todos los tiempos
|
|
256
|
+
precalculated_density = [
|
|
257
|
+
self.data_handler.interpolate_density(time, VAR1, VAR2).T
|
|
258
|
+
for time in time_array
|
|
259
|
+
]
|
|
260
|
+
# Determinar el label del eje y en función del plano
|
|
261
|
+
plane = self.data_handler.plane
|
|
262
|
+
y_label = "Z [AU]" if plane == "XZ" else r"$\phi$ [rad]"
|
|
263
|
+
|
|
264
|
+
# Crear la figura inicial
|
|
265
|
+
initial_density = precalculated_density[0]
|
|
266
|
+
fig = go.Figure(
|
|
267
|
+
data=go.Contour(
|
|
268
|
+
z=np.log10(initial_density * self.data_handler.sim.URHO * 1e3),
|
|
269
|
+
x=var1,
|
|
270
|
+
y=var2,
|
|
271
|
+
colorscale="Spectral_r",
|
|
272
|
+
contours=dict(coloring="heatmap"),
|
|
273
|
+
colorbar=dict(title="log10(ρ) [kg/m³]")
|
|
274
|
+
)
|
|
275
|
+
)
|
|
276
|
+
|
|
277
|
+
# Crear los frames para la animación
|
|
278
|
+
frames = []
|
|
279
|
+
for i, time in enumerate(time_array):
|
|
280
|
+
density = precalculated_density[i]
|
|
281
|
+
frames.append(go.Frame(
|
|
282
|
+
data=go.Contour(
|
|
283
|
+
z=np.log10(density * self.data_handler.sim.URHO * 1e3),
|
|
284
|
+
x=var1,
|
|
285
|
+
y=var2,
|
|
286
|
+
colorscale="Spectral_r", # Este colormap será actualizado dinámicamente
|
|
287
|
+
contours=dict(coloring="heatmap")
|
|
288
|
+
),
|
|
289
|
+
name=f"{time:.2f}"
|
|
290
|
+
))
|
|
291
|
+
|
|
292
|
+
fig.frames = frames
|
|
293
|
+
|
|
294
|
+
# Configurar el slider
|
|
295
|
+
sliders = [
|
|
296
|
+
dict(
|
|
297
|
+
steps=[
|
|
298
|
+
dict(
|
|
299
|
+
method="animate",
|
|
300
|
+
args=[[f"{time:.2f}"], dict(mode="immediate", frame=dict(duration=100, redraw=True), transition=dict(duration=0))],
|
|
301
|
+
label=f"{time:.2f}"
|
|
302
|
+
)
|
|
303
|
+
for time in time_array
|
|
304
|
+
],
|
|
305
|
+
transition=dict(duration=0),
|
|
306
|
+
currentvalue=dict(font=dict(size=16), prefix="Tiempo: ", visible=True),
|
|
307
|
+
len=0.9
|
|
308
|
+
)
|
|
309
|
+
]
|
|
310
|
+
|
|
311
|
+
# Configurar los botones de reproducción y pausa
|
|
312
|
+
updatemenus = [
|
|
313
|
+
dict(
|
|
314
|
+
type="buttons",
|
|
315
|
+
showactive=False,
|
|
316
|
+
buttons=[
|
|
317
|
+
dict(label="Play",
|
|
318
|
+
method="animate",
|
|
319
|
+
args=[None, dict(frame=dict(duration=100, redraw=True), fromcurrent=True)]),
|
|
320
|
+
dict(label="Pause",
|
|
321
|
+
method="animate",
|
|
322
|
+
args=[[None], dict(frame=dict(duration=0, redraw=False), mode="immediate")])
|
|
323
|
+
]
|
|
324
|
+
)
|
|
325
|
+
]
|
|
326
|
+
|
|
327
|
+
# Actualizar el diseño de la figura
|
|
328
|
+
fig.update_layout(
|
|
329
|
+
title="Evolución del Campo de Densidad",
|
|
330
|
+
xaxis_title='r [AU]',
|
|
331
|
+
yaxis_title=y_label,
|
|
332
|
+
width=600,
|
|
333
|
+
height=600,
|
|
334
|
+
sliders=sliders,
|
|
335
|
+
updatemenus=updatemenus
|
|
336
|
+
)
|
|
337
|
+
|
|
338
|
+
# Mostrar la animación
|
|
339
|
+
fig.show()
|
|
340
|
+
|
|
341
|
+
def velocity(self, var1_min, var1_max, var2_min, var2_max, res, time):
|
|
342
|
+
"""
|
|
343
|
+
Grafica un mapa de magnitud de velocidad interpolada en un tiempo dado.
|
|
344
|
+
Las zonas donde la interpolación da 0 no serán coloreadas.
|
|
345
|
+
"""
|
|
346
|
+
# Crear una cuadrícula regular para las coordenadas
|
|
347
|
+
var1 = np.linspace(var1_min, var1_max, res)
|
|
348
|
+
var2 = np.linspace(var2_min, var2_max, res)
|
|
349
|
+
VAR1, VAR2 = np.meshgrid(var1, var2, indexing='ij')
|
|
350
|
+
|
|
351
|
+
# Interpolar el campo de velocidad en el tiempo dado
|
|
352
|
+
velocity_x, velocity_y = self.data_handler.interpolate_velocity(time, VAR1, VAR2)
|
|
353
|
+
|
|
354
|
+
# Calcular la magnitud de la velocidad
|
|
355
|
+
velocity_magnitude = np.sqrt(velocity_x**2 + velocity_y**2).T * self.data_handler.sim.UV / (1e5) # Convertir a km/s
|
|
356
|
+
|
|
357
|
+
# Establecer las zonas donde velocity_x y velocity_y son 0 como None
|
|
358
|
+
velocity_magnitude[velocity_magnitude==0] = None
|
|
359
|
+
|
|
360
|
+
# Determinar el label del eje y en función del plano
|
|
361
|
+
plane = self.data_handler.plane
|
|
362
|
+
y_label = "Z [AU]" if plane == "XZ" else r"$\phi$ [rad]"
|
|
363
|
+
|
|
364
|
+
# Crear el gráfico de contorno
|
|
365
|
+
fig = go.Figure(
|
|
366
|
+
data=go.Contour(
|
|
367
|
+
z=velocity_magnitude,
|
|
368
|
+
x=var1,
|
|
369
|
+
y=var2,
|
|
370
|
+
colorscale="Viridis",
|
|
371
|
+
contours=dict(coloring="heatmap"),
|
|
372
|
+
colorbar=dict(title="|v| [km/s]"),
|
|
373
|
+
zmin=np.nanmin(velocity_magnitude),
|
|
374
|
+
zmax=np.nanmax(velocity_magnitude),
|
|
375
|
+
showscale=True
|
|
376
|
+
)
|
|
377
|
+
)
|
|
378
|
+
|
|
379
|
+
# Configurar el diseño del gráfico
|
|
380
|
+
fig.update_layout(
|
|
381
|
+
title=f"Mapa de Velocidad (t = {time:.2f})",
|
|
382
|
+
xaxis_title="r [AU]",
|
|
383
|
+
yaxis_title=y_label,
|
|
384
|
+
width=600,
|
|
385
|
+
height=600,
|
|
386
|
+
plot_bgcolor="white" # Fondo blanco para que las zonas no coloreadas sean visibles
|
|
387
|
+
)
|
|
388
|
+
|
|
389
|
+
# Mostrar el gráfico
|
|
390
|
+
fig.show()
|
|
391
|
+
|
|
392
|
+
def animate_velocity(self, var1_min, var1_max, var2_min, var2_max, res, time_array):
|
|
393
|
+
"""
|
|
394
|
+
Crea una animación del mapa de magnitud de velocidad interpolada en función del tiempo.
|
|
395
|
+
Las zonas donde la magnitud de la velocidad es 0 no serán coloreadas.
|
|
396
|
+
"""
|
|
397
|
+
# Crear una cuadrícula regular para las coordenadas
|
|
398
|
+
var1 = np.linspace(var1_min, var1_max, res)
|
|
399
|
+
var2 = np.linspace(var2_min, var2_max, res)
|
|
400
|
+
VAR1, VAR2 = np.meshgrid(var1, var2, indexing='ij')
|
|
401
|
+
|
|
402
|
+
# Determinar el label del eje y en función del plano
|
|
403
|
+
plane = self.data_handler.plane
|
|
404
|
+
y_label = "Z [AU]" if plane == "XZ" else r"$\phi$ [rad]"
|
|
405
|
+
|
|
406
|
+
# Precalcular la magnitud del campo de velocidad para todos los tiempos
|
|
407
|
+
precalculated_velocity_magnitude = []
|
|
408
|
+
for time in time_array:
|
|
409
|
+
velocity_x, velocity_y = self.data_handler.interpolate_velocity(time, VAR1, VAR2)
|
|
410
|
+
velocity_magnitude = np.sqrt(velocity_x**2 + velocity_y**2).T * self.data_handler.sim.UV / (1e5) # Convertir a km/s
|
|
411
|
+
velocity_magnitude[velocity_magnitude == 0] = None # Reemplazar las zonas con magnitud 0 por None
|
|
412
|
+
precalculated_velocity_magnitude.append(velocity_magnitude)
|
|
413
|
+
|
|
414
|
+
# Crear la figura inicial
|
|
415
|
+
initial_velocity_magnitude = precalculated_velocity_magnitude[0]
|
|
416
|
+
fig = go.Figure(
|
|
417
|
+
data=go.Contour(
|
|
418
|
+
z=initial_velocity_magnitude,
|
|
419
|
+
x=var1,
|
|
420
|
+
y=var2,
|
|
421
|
+
colorscale="Viridis",
|
|
422
|
+
contours=dict(coloring="heatmap"),
|
|
423
|
+
colorbar=dict(title="|v| [km/s]")
|
|
424
|
+
)
|
|
425
|
+
)
|
|
426
|
+
|
|
427
|
+
# Crear los frames para la animación
|
|
428
|
+
frames = []
|
|
429
|
+
for i, time in enumerate(time_array):
|
|
430
|
+
velocity_magnitude = precalculated_velocity_magnitude[i]
|
|
431
|
+
frames.append(go.Frame(
|
|
432
|
+
data=go.Contour(
|
|
433
|
+
z=velocity_magnitude,
|
|
434
|
+
x=var1,
|
|
435
|
+
y=var2,
|
|
436
|
+
colorscale="Viridis",
|
|
437
|
+
contours=dict(coloring="heatmap")
|
|
438
|
+
),
|
|
439
|
+
name=f"{time:.2f}"
|
|
440
|
+
))
|
|
441
|
+
|
|
442
|
+
fig.frames = frames
|
|
443
|
+
|
|
444
|
+
# Configurar el slider
|
|
445
|
+
sliders = [
|
|
446
|
+
dict(
|
|
447
|
+
steps=[
|
|
448
|
+
dict(
|
|
449
|
+
method="animate",
|
|
450
|
+
args=[[f"{time:.2f}"], dict(mode="immediate", frame=dict(duration=150, redraw=True), transition=dict(duration=0))],
|
|
451
|
+
label=f"{time:.2f}"
|
|
452
|
+
)
|
|
453
|
+
for time in time_array
|
|
454
|
+
],
|
|
455
|
+
transition=dict(duration=0),
|
|
456
|
+
currentvalue=dict(font=dict(size=16), prefix="Tiempo: ", visible=True),
|
|
457
|
+
len=0.9
|
|
458
|
+
)
|
|
459
|
+
]
|
|
460
|
+
|
|
461
|
+
# Configurar los botones de reproducción y pausa
|
|
462
|
+
updatemenus = [
|
|
463
|
+
dict(
|
|
464
|
+
type="buttons",
|
|
465
|
+
showactive=False,
|
|
466
|
+
buttons=[
|
|
467
|
+
dict(label="Play",
|
|
468
|
+
method="animate",
|
|
469
|
+
args=[None, dict(frame=dict(duration=100, redraw=True), fromcurrent=True)]),
|
|
470
|
+
dict(label="Pause",
|
|
471
|
+
method="animate",
|
|
472
|
+
args=[[None], dict(frame=dict(duration=0, redraw=False), mode="immediate")])
|
|
473
|
+
]
|
|
474
|
+
)
|
|
475
|
+
]
|
|
476
|
+
|
|
477
|
+
# Actualizar el diseño de la figura
|
|
478
|
+
fig.update_layout(
|
|
479
|
+
title="Evolución del Campo de Velocidad",
|
|
480
|
+
xaxis_title='r [AU]',
|
|
481
|
+
yaxis_title=y_label,
|
|
482
|
+
width=600,
|
|
483
|
+
height=600,
|
|
484
|
+
sliders=sliders,
|
|
485
|
+
updatemenus=updatemenus
|
|
486
|
+
)
|
|
487
|
+
|
|
488
|
+
# Mostrar la animación
|
|
489
|
+
fig.show()
|
|
490
|
+
|
|
491
|
+
def vel_streamlines(self, var1_min, var1_max, var2_min, var2_max, res, time):
|
|
492
|
+
"""
|
|
493
|
+
Grafica las streamlines del campo de velocidad con la densidad de fondo para un tiempo dado.
|
|
494
|
+
"""
|
|
495
|
+
# Crear una cuadrícula regular para las coordenadas
|
|
496
|
+
var1 = np.linspace(var1_min, var1_max, res)
|
|
497
|
+
var2 = np.linspace(var2_min, var2_max, res)
|
|
498
|
+
VAR1, VAR2 = np.meshgrid(var1, var2, indexing='ij')
|
|
499
|
+
|
|
500
|
+
# Interpolar el campo de densidad y velocidad en el tiempo dado
|
|
501
|
+
density = self.data_handler.interpolate_density(time, VAR1, VAR2)
|
|
502
|
+
velocity_x, velocity_y = self.data_handler.interpolate_velocity(time, VAR1, VAR2)
|
|
503
|
+
|
|
504
|
+
# Calcular la magnitud de la velocidad
|
|
505
|
+
v_mag = np.sqrt(velocity_x**2 + velocity_y**2).T * self.data_handler.sim.UV / (1e5) # Convertir a km/s
|
|
506
|
+
|
|
507
|
+
# Determinar el label del eje y en función del plano
|
|
508
|
+
plane = self.data_handler.plane
|
|
509
|
+
y_label = "Z [AU]" if plane == "XZ" else r"$\phi$ [rad]"
|
|
510
|
+
|
|
511
|
+
# Crear el gráfico
|
|
512
|
+
plt.figure(figsize=(6, 6))
|
|
513
|
+
plt.pcolormesh(var1, var2, np.log10(density * self.data_handler.sim.URHO * 1e3).T, cmap="Spectral_r", shading='auto')
|
|
514
|
+
plt.streamplot(var1, var2, velocity_x.T, velocity_y.T, color=v_mag, linewidth=0.7, density=3.0, cmap='viridis')
|
|
515
|
+
plt.colorbar(label="|v| [km/s]")
|
|
516
|
+
plt.title(f"Streamlines (t = {time:.2f})")
|
|
517
|
+
plt.xlabel("r [AU]")
|
|
518
|
+
plt.ylabel(y_label)
|
|
519
|
+
plt.show()
|
|
520
|
+
|
|
521
|
+
def vel_streamlines_slide(self, var1_min, var1_max, var2_min, var2_max, res, time_array):
|
|
522
|
+
|
|
523
|
+
# Crear una cuadrícula regular para las coordenadas
|
|
524
|
+
var1 = np.linspace(var1_min, var1_max, res)
|
|
525
|
+
var2 = np.linspace(var2_min, var2_max, res)
|
|
526
|
+
VAR1, VAR2 = np.meshgrid(var1, var2, indexing='ij')
|
|
527
|
+
# Determinar el label del eje y en función del plano
|
|
528
|
+
plane = self.data_handler.plane
|
|
529
|
+
y_label = "Z [AU]" if plane == "XZ" else r"$\phi$ [rad]"
|
|
530
|
+
|
|
531
|
+
# Precalcular densidad y velocidades para todos los tiempos
|
|
532
|
+
precalculated_data = [
|
|
533
|
+
{
|
|
534
|
+
"density": self.data_handler.interpolate_density(time, VAR1, VAR2),
|
|
535
|
+
"velocity": self.data_handler.interpolate_velocity(time, VAR1, VAR2)
|
|
536
|
+
}
|
|
537
|
+
for time in time_array
|
|
538
|
+
]
|
|
539
|
+
|
|
540
|
+
def update_plot(time_index):
|
|
541
|
+
"""
|
|
542
|
+
Actualiza el gráfico para un índice de tiempo dado.
|
|
543
|
+
"""
|
|
544
|
+
data = precalculated_data[time_index]
|
|
545
|
+
density = data["density"]
|
|
546
|
+
velocity_x = data["velocity"][0]
|
|
547
|
+
velocity_y = data["velocity"][1]
|
|
548
|
+
v_mag = np.sqrt(velocity_x**2 + velocity_y**2).T*self.data_handler.sim.UV/(1e5)
|
|
549
|
+
# Crear el gráfico
|
|
550
|
+
plt.figure(figsize=(6, 6))
|
|
551
|
+
plt.pcolormesh(var1, var2, np.log10(density * self.data_handler.sim.URHO * 1e3).T, cmap="Spectral_r", shading='auto')
|
|
552
|
+
plt.streamplot(var1, var2, velocity_x.T, velocity_y.T, color=v_mag, linewidth=0.7, density=3.0,cmap='viridis')
|
|
553
|
+
plt.colorbar(label="|v| [km/s]")
|
|
554
|
+
plt.title(f"Streamlines (t = {time_array[time_index]:.2f})")
|
|
555
|
+
plt.xlabel("r [AU]")
|
|
556
|
+
plt.ylabel(y_label)
|
|
557
|
+
plt.show()
|
|
558
|
+
|
|
559
|
+
# Crear un slider interactivo para el tiempo (con índices enteros)
|
|
560
|
+
interact(update_plot, time_index=IntSlider(value=0, min=0, max=len(time_array) - 1, step=1))
|
|
561
|
+
|
|
562
|
+
|
|
563
|
+
def vel_streamlines_vid(self, var1_min, var1_max, var2_min, var2_max, res, time_array,output_file="streamlines_animation.mp4"):
|
|
564
|
+
|
|
565
|
+
# Crear una cuadrícula regular para las coordenadas
|
|
566
|
+
var1 = np.linspace(var1_min, var1_max, res)
|
|
567
|
+
var2 = np.linspace(var2_min, var2_max, res)
|
|
568
|
+
VAR1, VAR2 = np.meshgrid(var1, var2, indexing='ij')
|
|
569
|
+
# Determinar el label del eje y en función del plano
|
|
570
|
+
plane = self.data_handler.plane
|
|
571
|
+
y_label = "Z [AU]" if plane == "XZ" else r"$\phi$ [rad]"
|
|
572
|
+
|
|
573
|
+
# Configurar la figura y la cámara para la animación
|
|
574
|
+
fig, ax = plt.subplots(figsize=(6, 6))
|
|
575
|
+
camera = Camera(fig)
|
|
576
|
+
|
|
577
|
+
# Generar los frames de la animación
|
|
578
|
+
for time in time_array:
|
|
579
|
+
# Interpolar el campo de densidad y velocidad
|
|
580
|
+
density = self.data_handler.interpolate_density(time, VAR1, VAR2)
|
|
581
|
+
velocity_x, velocity_y = self.data_handler.interpolate_velocity(time, VAR1, VAR2)
|
|
582
|
+
v_mag = np.sqrt(velocity_x**2 + velocity_y**2).T*self.data_handler.sim.UV/(1e5)
|
|
583
|
+
|
|
584
|
+
# Crear el gráfico para el frame actual
|
|
585
|
+
c = ax.pcolormesh(var1, var2, np.log10(density * self.data_handler.sim.URHO * 1e3).T, cmap="Spectral_r", shading='auto')
|
|
586
|
+
strm=ax.streamplot(var1, var2, velocity_x.T, velocity_y.T, color=v_mag, linewidth=0.7, density=3.0,cmap='viridis')
|
|
587
|
+
ax.set_xlabel("r [AU]")
|
|
588
|
+
ax.set_ylabel(y_label)
|
|
589
|
+
camera.snap()
|
|
590
|
+
|
|
591
|
+
|
|
592
|
+
# Crear la animación
|
|
593
|
+
fig.colorbar(strm.lines, ax=ax, label="|v| [km/s]")
|
|
594
|
+
plt.close(fig)
|
|
595
|
+
animation = camera.animate()
|
|
596
|
+
|
|
597
|
+
|
|
598
|
+
# Guardar la animación en un archivo
|
|
599
|
+
animation.save(output_file, writer="ffmpeg", fps=10)
|
|
600
|
+
|
|
601
|
+
plt.close(fig)
|
|
602
|
+
return Video(output_file, embed=True)
|
|
603
|
+
|
fargopy/version.py
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
version='0.3.
|
|
1
|
+
version='0.3.14'
|
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
Metadata-Version: 2.
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
2
|
Name: fargopy
|
|
3
|
-
Version: 0.3.
|
|
3
|
+
Version: 0.3.14
|
|
4
4
|
Summary: FARGO3D Wrapping
|
|
5
5
|
Home-page: https://pypi.org/project/fargopy
|
|
6
|
-
Author: Jorge Zuluaga, Matias Montesinos
|
|
6
|
+
Author: Jorge Zuluaga, Alejandro González, Matias Montesinos
|
|
7
7
|
Author-email: jorge.zuluaga@gmail.com
|
|
8
8
|
License: MIT
|
|
9
9
|
Keywords: astronomy MHD CFD
|
|
@@ -20,6 +20,21 @@ Requires-Dist: ipython
|
|
|
20
20
|
Requires-Dist: celluloid
|
|
21
21
|
Requires-Dist: psutil
|
|
22
22
|
Requires-Dist: gdown
|
|
23
|
+
Requires-Dist: pandas
|
|
24
|
+
Requires-Dist: plotly
|
|
25
|
+
Requires-Dist: ipywidgets
|
|
26
|
+
Requires-Dist: nbformat
|
|
27
|
+
Dynamic: author
|
|
28
|
+
Dynamic: author-email
|
|
29
|
+
Dynamic: classifier
|
|
30
|
+
Dynamic: description
|
|
31
|
+
Dynamic: description-content-type
|
|
32
|
+
Dynamic: home-page
|
|
33
|
+
Dynamic: keywords
|
|
34
|
+
Dynamic: license
|
|
35
|
+
Dynamic: license-file
|
|
36
|
+
Dynamic: requires-dist
|
|
37
|
+
Dynamic: summary
|
|
23
38
|
|
|
24
39
|
# FARGOpy
|
|
25
40
|
## Wrapping FRAGO3D
|
|
@@ -470,5 +485,5 @@ Version 0.0.*:
|
|
|
470
485
|
|
|
471
486
|
------------
|
|
472
487
|
|
|
473
|
-
This package has been designed and written mostly by Jorge I. Zuluaga with advising and contributions by Matías Montesinos (C) 2023
|
|
488
|
+
This package has been designed and written mostly by Jorge I. Zuluaga and Alejandro González with advising and contributions by Matías Montesinos (C) 2023, 2024, 2025
|
|
474
489
|
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
fargopy/__init__.py,sha256=Ua0ORnNZRgXeU1A-SlT5p2YBBs8x-O30Vwu2xh8b3mg,12490
|
|
2
|
+
fargopy/fields.py,sha256=--TrrAaFO9WmsrecRfdmEV1FRfp6JQRlIqyqGn11p2g,10128
|
|
3
|
+
fargopy/fsimulation.py,sha256=jfaLxgNePUx1seUv4uP5BHhxzU02wAmKbpE25wYnR1Y,23912
|
|
4
|
+
fargopy/plot.py,sha256=T2xhVQnBU9NVebxgMgegcuBc0YZSei12VC6OzD5JihI,1583
|
|
5
|
+
fargopy/simulation.py,sha256=MiVkFsm9irt90xFgAcS_2oz467L-Ox5z2eCtIichuZs,41976
|
|
6
|
+
fargopy/sys.py,sha256=O53WNHU3EL3GIrpxuK4mi2er929f6Ztdf5Fej62DDs8,5055
|
|
7
|
+
fargopy/util.py,sha256=Bbll0TwJjFp5hv2luoBu9ogNiepp4oErPFcuty7YqdY,672
|
|
8
|
+
fargopy/version.py,sha256=GtupLqvph4hqrSU2vHCxLLbi0bvHODyy9-lCZqlLOYg,17
|
|
9
|
+
fargopy/tests/test___init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
10
|
+
fargopy-0.3.14.data/scripts/ifargopy,sha256=t5E07jKzOLtNnlNyElQZbzjCW6wnR0bc8uCaZpw9yS8,382
|
|
11
|
+
fargopy-0.3.14.dist-info/licenses/LICENSE,sha256=aIckKnNVrkXQMqEKlzGn_REfTwc82TF35BHMCRwPXCI,1097
|
|
12
|
+
fargopy-0.3.14.dist-info/METADATA,sha256=Em5359gaRwQpEMItJ3bIv9wz820zkiVQJ7u2VDLFVnQ,17746
|
|
13
|
+
fargopy-0.3.14.dist-info/WHEEL,sha256=CmyFI0kx5cdEMTLiONQRbGQwjIoR1aIYB7eCAQ4KPJ0,91
|
|
14
|
+
fargopy-0.3.14.dist-info/entry_points.txt,sha256=555NPKYbLCN0fgJbdW4b4azZ5sqjhqVqTUxujBBYEJY,49
|
|
15
|
+
fargopy-0.3.14.dist-info/top_level.txt,sha256=_r66v1_-9T7dB5sQa12AdxaAdsv9-OTwtR-vft4OPBU,8
|
|
16
|
+
fargopy-0.3.14.dist-info/RECORD,,
|
fargopy-0.3.12.dist-info/RECORD
DELETED
|
@@ -1,15 +0,0 @@
|
|
|
1
|
-
fargopy/__init__.py,sha256=TgVTZSO5vMpUXX5SB4Gx56vak6lcG3o0JZkt43ZZB9g,12464
|
|
2
|
-
fargopy/fields.py,sha256=--TrrAaFO9WmsrecRfdmEV1FRfp6JQRlIqyqGn11p2g,10128
|
|
3
|
-
fargopy/plot.py,sha256=T2xhVQnBU9NVebxgMgegcuBc0YZSei12VC6OzD5JihI,1583
|
|
4
|
-
fargopy/simulation.py,sha256=MiVkFsm9irt90xFgAcS_2oz467L-Ox5z2eCtIichuZs,41976
|
|
5
|
-
fargopy/sys.py,sha256=O53WNHU3EL3GIrpxuK4mi2er929f6Ztdf5Fej62DDs8,5055
|
|
6
|
-
fargopy/util.py,sha256=Bbll0TwJjFp5hv2luoBu9ogNiepp4oErPFcuty7YqdY,672
|
|
7
|
-
fargopy/version.py,sha256=tiMAX-dMt5Ucu3eVWzPjgr1TJ7j--bdpcgEgW46n8Nk,17
|
|
8
|
-
fargopy/tests/test___init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
9
|
-
fargopy-0.3.12.data/scripts/ifargopy,sha256=t5E07jKzOLtNnlNyElQZbzjCW6wnR0bc8uCaZpw9yS8,382
|
|
10
|
-
fargopy-0.3.12.dist-info/LICENSE,sha256=aIckKnNVrkXQMqEKlzGn_REfTwc82TF35BHMCRwPXCI,1097
|
|
11
|
-
fargopy-0.3.12.dist-info/METADATA,sha256=_6dtQR2vmvLifUj_SCmWhH9HpEEQlzsZMQBUIu63z1Y,17366
|
|
12
|
-
fargopy-0.3.12.dist-info/WHEEL,sha256=oiQVh_5PnQM0E3gPdiz09WCNmwiHDMaGer_elqB3coM,92
|
|
13
|
-
fargopy-0.3.12.dist-info/entry_points.txt,sha256=555NPKYbLCN0fgJbdW4b4azZ5sqjhqVqTUxujBBYEJY,49
|
|
14
|
-
fargopy-0.3.12.dist-info/top_level.txt,sha256=_r66v1_-9T7dB5sQa12AdxaAdsv9-OTwtR-vft4OPBU,8
|
|
15
|
-
fargopy-0.3.12.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|