Skip to content

Commit f047068

Browse files
VARENNES RobinVARENNES Robin
authored andcommitted
Add input with proper energ conservation. Modified filtering frequency when time derivative is computed
1 parent bf1d2b1 commit f047068

4 files changed

Lines changed: 166 additions & 3 deletions

File tree

diagnostics/diag_main.ipynb

Lines changed: 156 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -149,6 +149,8 @@
149149
"\n",
150150
"from simulation_diag_handler import Simulation, set_plot_defaults\n",
151151
"\n",
152+
"sim = Simulation(\"/ZONE_TRAVAIL/RV255575/tokam2d_conservation/HW_cons_rst\")\n",
153+
"\n",
152154
"set_plot_defaults()\n",
153155
"\n",
154156
"plt.close('all')\n",
@@ -290,6 +292,160 @@
290292
"\n",
291293
"sim.make_movie(field='density', path=None, filename='mysim', it_slice=None, parallel=True, num_cores=None, for_IA=False, scheme=None, cmap='plasma', vmin=None, vmax=None, fps=30, save_frames=False)"
292294
]
295+
},
296+
{
297+
"cell_type": "markdown",
298+
"id": "869be0f0",
299+
"metadata": {},
300+
"source": [
301+
"## **Conservation**"
302+
]
303+
},
304+
{
305+
"cell_type": "markdown",
306+
"id": "175e983b",
307+
"metadata": {},
308+
"source": [
309+
"### **Compute energy conservation (Hasegawa-Wakatani)**\n",
310+
"\n",
311+
"\\begin{equation}\n",
312+
"\\frac{dE}{dt} = \\Gamma_n - D_\\alpha - D_E\n",
313+
"\\end{equation}\n",
314+
"\n",
315+
"Where \n",
316+
"\n",
317+
"\\begin{equation}\n",
318+
"E = \\frac{1}{2} \\int n^2 + |\\nabla \\phi|^2 \\, d\\mathbf{x}\n",
319+
"\\end{equation}\n",
320+
"\n",
321+
"The time derivative of the energy $E$ can be computed from saved data as\n",
322+
"\n",
323+
"\\begin{equation}\n",
324+
"\\frac{dE}{dt} = \\int \\left( n \\cdot \\frac{dn}{dt} \n",
325+
"+ |\\nabla_x \\phi| \\cdot \\frac{d |\\nabla_x \\phi|}{dt}\n",
326+
"+ |\\nabla_y \\phi| \\cdot \\frac{d |\\nabla_y \\phi|}{dt} \\right)\n",
327+
"\\, d\\mathbf{x}\n",
328+
"\\end{equation}\n",
329+
"\n",
330+
"and \n",
331+
"\n",
332+
"\\begin{equation}\n",
333+
"\\Gamma_n = -\\kappa \\int n \\cdot \\frac{\\partial \\phi}{\\partial y} \\, d\\mathbf{x} \\text{~~(``source'')}\n",
334+
"\\end{equation}\n",
335+
"\n",
336+
"\\begin{equation}\n",
337+
"D_C = C \\int (n - \\phi)^2 \\, d\\mathbf{x} \\text{~~(``coupling'')}\n",
338+
"\\end{equation}\n",
339+
"\n",
340+
"\\begin{equation}\n",
341+
"D_{E,n} = (-1)^{N_n} D_n \\int n \\nabla^{2N_n} n \\, d\\mathbf{x} \\text{~~(``density dissipation'')}\n",
342+
"\\end{equation}\n",
343+
"\n",
344+
"\\begin{equation}\n",
345+
"D_{E,\\Omega} = (-1)^{N_\\phi} D_\\phi \\int n \\nabla^{2N_\\phi+2} \\phi \\, d\\mathbf{x} \\text{~~(``vorticity dissipation'')}\n",
346+
"\\end{equation}\n",
347+
"\n",
348+
"**<span style=\"color:red\">\n",
349+
"/!\\ Some important notes\n",
350+
"</span>**\n",
351+
"\n",
352+
"- Accuracy of time derivatives are very important to compute the conservations. It is advised to switch on the 'compute_time_derivatives' in the simulation inputs (which computes the 8th order central finite difference from the rk4 time steps which are not available in the stored data outputs) and derive the time derivative of energy according to the formula above.\n",
353+
"\n",
354+
"- Semi-spectral code such as Tokam2D usually relies on a 2/3 dealiasing rule for avoid the emergence of spurious (non-physical) modes by truncating a part of high k (small scales) while the simulation is running.\n",
355+
"One consequence is that the energy transferred to these modes is lost.\n",
356+
"While the difference is (or should be) small, test cases for evaluating conservation properties of the code can be performed without this filter by switching of the 'fft_filter' in input file.\n",
357+
"\n",
358+
"- For Hasegawa-Wakatani schemes, noise accumulates during Tokam2D simulation and takes the form of non-zero imaginary part for some modes for the real space fields (which should be real signals). A filter is thus applied frequently during the simulation to remove this noise. Consequence on conservation are not clear.\n",
359+
"\n",
360+
"- Depending on the level of accuracy of conservation one needs, diminishing 'dt_rk4' (or equivalently increasing 'rk4_per_diag') in the input file should improve agreement. Increasing the spatial resolution 'Nx' and 'Ny', however, does not garantee a better conservation"
361+
]
362+
},
363+
{
364+
"cell_type": "code",
365+
"execution_count": null,
366+
"id": "9f70cf8a",
367+
"metadata": {},
368+
"outputs": [],
369+
"source": [
370+
"from simulation_diag_handler import Simulation, set_plot_defaults\n",
371+
"import numpy as np\n",
372+
"import matplotlib.pyplot as plt\n",
373+
"\n",
374+
"set_plot_defaults()\n",
375+
"\n",
376+
"## Fetch simulation data\n",
377+
"sim_path = '/path/to/sim'\n",
378+
"sim = Simulation(sim_path)\n",
379+
"\n",
380+
"## Define proxy function\n",
381+
"def rifft(x): return np.real(np.fft.ifft2(x, axes=(-2, -1)))\n",
382+
"\n",
383+
"## Choose\n",
384+
"it_beg = 0\n",
385+
"it_end = len(sim.time) - 1\n",
386+
"\n",
387+
"## Cast useful quantities\n",
388+
"potential_fft = sim.get_data_slice('potential_fft' , slice(it_beg, it_end))\n",
389+
"density_fft = sim.get_data_slice('density_fft' , slice(it_beg, it_end))\n",
390+
"density = sim.get_data_slice('density' , slice(it_beg, it_end))\n",
391+
"potential = sim.get_data_slice('potential' , slice(it_beg, it_end))\n",
392+
"dt_density = sim.get_data_slice('dt_density' , slice(it_beg, it_end))\n",
393+
"dt_potential = sim.get_data_slice('dt_potential' , slice(it_beg, it_end))\n",
394+
"dt_potential_fft = sim.get_data_slice('dt_potential_fft', slice(it_beg, it_end))\n",
395+
"kappa = sim[\"kappa\"][()]\n",
396+
"C = sim[\"C\"][()]\n",
397+
"D_n = sim[\"Dn\"][()][0]\n",
398+
"D_phi = sim[\"Dphi\"][()][0]\n",
399+
"N_n = 2 # 1: Diffusion, 2: Hyperdiffusion\n",
400+
"N_phi = 2 # 1: Diffusion, 2: Hyperdiffusion\n",
401+
"kx2D = sim.kx2D\n",
402+
"ky2D = sim.ky2D\n",
403+
"k2D = sim.k2D\n",
404+
"\n",
405+
"## Compute energy\n",
406+
"gradx_phi = rifft( (1j * kx2D) * potential_fft)\n",
407+
"grady_phi = rifft( (1j * ky2D) * potential_fft)\n",
408+
"energy = 0.5*np.mean(density**2 + (gradx_phi**2 + grady_phi**2), axis=(-2, -1))\n",
409+
"\n",
410+
"# FIGURE : time evolution of energy\n",
411+
"fig = plt.figure(figsize=(10,7))\n",
412+
"ax = fig.add_subplot(111)\n",
413+
"ax.plot(sim.time[it_beg:it_end], energy)\n",
414+
"ax.set_xlabel('Time')\n",
415+
"ax.set_ylabel('Energy')\n",
416+
"ax.set_title(f'Averaged between {sim.time[it_beg]:.2f} < t < {sim.time[it_end-1]:.2f}')\n",
417+
"\n",
418+
"## Compute terms of energy conservation equation \n",
419+
"# Energy time derivative\n",
420+
"dEdt_n = density * dt_density\n",
421+
"gradx_dt_phi = rifft( (1j * kx2D) * dt_potential_fft)\n",
422+
"grady_dt_phi = rifft( (1j * ky2D) * dt_potential_fft)\n",
423+
"dEdt_phi = gradx_phi * gradx_dt_phi + grady_phi * grady_dt_phi\n",
424+
"dEdt_avg = np.mean(dEdt_n + dEdt_phi, axis=(-2, -1))\n",
425+
"\n",
426+
"# Source term\n",
427+
"Gamma_n = - kappa * np.mean( density * grady_phi , axis=(-2, -1))\n",
428+
"\n",
429+
"# Coupling term\n",
430+
"D_C = C * np.mean( (density - potential)**2 , axis=(-2, -1))\n",
431+
"\n",
432+
"# Density dissipation\n",
433+
"nabla_2N_dens = rifft( (1j * k2D)**(2*N_n) * density_fft)\n",
434+
"DE_n = (-1)**N_n * D_n * np.mean(density * nabla_2N_dens, axis=(-2, -1))\n",
435+
"\n",
436+
"# Vorticity dissipation\n",
437+
"nabla_2N_vort = rifft( (1j * k2D)**(2*N_phi+2) * potential_fft)\n",
438+
"DE_vort = (-1)**N_phi * D_phi * np.mean(potential * nabla_2N_vort, axis=(-2, -1))\n",
439+
"\n",
440+
"# FIGURE : time evolution of energy conservation LHS and RHS\n",
441+
"fig = plt.figure(figsize=(10,7))\n",
442+
"ax = fig.add_subplot(111)\n",
443+
"ax.plot(sim.time[it_beg:it_end], dEdt_avg, ls='-',label=r'LHS: $\\partial_t E$')\n",
444+
"ax.plot(sim.time[it_beg:it_end], Gamma_n - D_C - DE_n + DE_vort, ls='--',label=r'RHS: $\\Gamma_n - D_C - D_{E,n} + D_{E,\\Omega}$')\n",
445+
"ax.set_xlabel('Time')\n",
446+
"ax.set_title(f'Averaged between {sim.time[it_beg]:.2f} < t < {sim.time[it_end-1]:.2f}')\n",
447+
"ax.legend()"
448+
]
293449
}
294450
],
295451
"metadata": {

diagnostics/simulation_diag_handler.py

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -242,7 +242,11 @@ def get_data_slice(self, field, it=None, iy=None, ix=None):
242242
return np.fft.fft2(self["potential"][it_slice], axes=(-2, -1))[..., y_slice, x_slice]
243243
if field == "density_fft":
244244
return np.fft.fft2(self["density"][it_slice], axes=(-2, -1))[..., y_slice, x_slice]
245-
245+
if field == "dt_potential_fft":
246+
return np.fft.fft2(self["dt_potential"][it_slice], axes=(-2, -1))[..., y_slice, x_slice]
247+
if field == "dt_density_fft":
248+
return np.fft.fft2(self["dt_density"][it_slice], axes=(-2, -1))[..., y_slice, x_slice]
249+
246250
# If the name of the field is not in the mapping, raise an error
247251
if field not in self.field_mapping:
248252
raise KeyError(f"Field {field} not found in mapping.")

main.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@
1010
from src.interfaces.output_saver import OutputSaver
1111

1212
import os
13-
os.environ["CUDA_VISIBLE_DEVICES"] = "0"
13+
os.environ["CUDA_VISIBLE_DEVICES"] = "1"
1414

1515
import time
1616

src/simulation/run_simulation.py

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -114,6 +114,8 @@ def run(self, fields):
114114
while self.step_rk4_count < self.Nt_rk4 and not self._stop:
115115

116116
if self.user_inline_operations.get('compute_time_derivatives', False):
117+
for op in self.inline_operations:
118+
fields = op(fields)
117119
self.inline_compute_time_derivative(fields)
118120

119121
if self.step_rk4_count % self.rk4_per_diag == 0:
@@ -128,8 +130,9 @@ def run(self, fields):
128130
# Trigger inline operations (e.g. FFT filtering) at diagnostic time step
129131
for op in self.inline_operations:
130132
fields = op(fields)
131-
133+
132134
self.step_diag_count += 1
135+
133136
fields = step(fields, self.pde, self.dt_rk4, t=self.time)
134137

135138
self.time += self.dt_rk4

0 commit comments

Comments
 (0)