#!/usr/bin/env python3 """ This script is used to test the results of the Galilean PSATD and averaged Galilean PSATD methods in WarpX. It compares the energy of the electric field with a given reference energy. The reference energy is computed by running the same test with: (i) psatd.v_galilean=(0,0,0) for Galilean tests, or (ii) psatd.do_time_averaging=0 for averaged Galilean tests. In both cases, the reference energy corresponds to unstable results due to NCI (suppressed by the Galilean PSATD method, without or with averaging, respectively). """ import os import re import sys import numpy as np import scipy.constants as scc import yt ; yt.funcs.mylog.setLevel(0) sys.path.insert(1, '../../../../warpx/Regression/Checksum/') import checksumAPI filename = sys.argv[1] # Parse some input arguments from output file 'warpx_used_inputs' current_correction = False time_averaging = False periodic_single_box = False warpx_used_inputs = open('./warpx_used_inputs', 'r').read() if re.search('geometry.dims\s*=\s*2', warpx_used_inputs): dims = '2D' elif re.search('geometry.dims\s*=\s*RZ', warpx_used_inputs): dims = 'RZ' elif re.search('geometry.dims\s*=\s*3', warpx_used_inputs): dims = '3D' if re.search('psatd.current_correction\s*=\s*1', warpx_used_inputs): current_correction = True if re.search('psatd.do_time_averaging\s*=\s*1', warpx_used_inputs): time_averaging = True if re.search('psatd.periodic_single_box_fft\s*=\s*1', warpx_used_inputs): periodic_single_box = True ds = yt.load(filename) # yt 4.0+ has rounding issues with our domain data: # RuntimeError: yt attempted to read outside the boundaries # of a non-periodic domain along dimension 0. if 'force_periodicity' in dir(ds): ds.force_periodicity() all_data = ds.covering_grid(level=0, left_edge=ds.domain_left_edge, dims=ds.domain_dimensions) if dims == 'RZ': Ex = all_data['boxlib', 'Er'].squeeze().v Ey = all_data['boxlib', 'Et'].squeeze().v else: Ex = all_data['boxlib', 'Ex'].squeeze().v Ey = all_data['boxlib', 'Ey'].squeeze().v Ez = all_data['boxlib', 'Ez'].squeeze().v # Set reference energy values, and tolerances for numerical stability and charge conservation tol_energy = 1e-8 tol_charge = 1e-9 if dims == '2D': if not current_correction: energy_ref = 35657.41657683263 if current_correction and periodic_single_box: energy_ref = 35024.0275199999 if current_correction and not periodic_single_box: energy_ref = 35675.25563324745 tol_energy = 2e-8 tol_charge = 2e-4 if time_averaging: energy_ref = 26208.04843478073 tol_energy = 1e-6 elif dims == 'RZ': if not current_correction: energy_ref = 191002.6526271543 if current_correction and periodic_single_box: energy_ref = 472779.70801323955 if current_correction and not periodic_single_box: energy_ref = 511671.4108624746 tol_charge = 3e-4 elif dims == '3D': if not current_correction: energy_ref = 661285.098907683 if current_correction and periodic_single_box: energy_ref = 856783.3007547935 if current_correction and not periodic_single_box: energy_ref = 875307.5138913819 tol_charge = 1e-2 if time_averaging: energy_ref = 14.564631643496 tol_energy = 1e-4 # Check numerical stability by comparing electric field energy to reference energy energy = np.sum(scc.epsilon_0/2*(Ex**2+Ey**2+Ez**2)) err_energy = energy / energy_ref print('\nCheck numerical stability:') print(f'err_energy = {err_energy}') print(f'tol_energy = {tol_energy}') assert(err_energy < tol_energy) # Check charge conservation (relative L-infinity norm of error) with current correction if current_correction: divE = all_data['boxlib', 'divE'].squeeze().v rho = all_data['boxlib', 'rho' ].squeeze().v / scc.epsilon_0 err_charge = np.amax(np.abs(divE - rho)) / max(np.amax(divE), np.amax(rho)) print('\nCheck charge conservation:') print(f'err_charge = {err_charge}') print(f'tol_charge = {tol_charge}') assert(err_charge < tol_charge) test_name = os.path.split(os.getcwd())[1] checksumAPI.evaluate_checksum(test_name, filename, rtol=1.e-8)