2024-05-16 17:46:21 +02:00
|
|
|
|
'''
|
|
|
|
|
Combine EC profiles from three independent beams
|
|
|
|
|
'''
|
|
|
|
|
|
|
|
|
|
from .. import TestCase, options, load_table, run_gray
|
|
|
|
|
|
|
|
|
|
from pathlib import Path
|
|
|
|
|
|
|
|
|
|
import unittest
|
|
|
|
|
import shutil
|
|
|
|
|
import tempfile
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class Test(TestCase):
|
|
|
|
|
inputs: Path # directory of the input files
|
|
|
|
|
reference: Path # directory of the reference outputs
|
|
|
|
|
candidate: Path # directory of the candidate outputs
|
|
|
|
|
|
|
|
|
|
@classmethod
|
|
|
|
|
def setUpClass(cls):
|
|
|
|
|
'''
|
|
|
|
|
Sets up the test case
|
|
|
|
|
'''
|
|
|
|
|
# directory of the test case
|
|
|
|
|
base = Path().joinpath(*cls.__module__.split('.'))
|
|
|
|
|
cls.inputs = base / 'inputs'
|
|
|
|
|
cls.reference = base / 'outputs'
|
|
|
|
|
|
|
|
|
|
# temporary directory holding the candidate outputs
|
|
|
|
|
cls._tempdir = tempfile.mkdtemp(prefix=f'gray-test-{base.name}.')
|
|
|
|
|
cls.candidate = Path(cls._tempdir)
|
|
|
|
|
|
|
|
|
|
# replace reference with candidate
|
|
|
|
|
if options.update:
|
|
|
|
|
print()
|
|
|
|
|
print('Setting new reference for ' + cls.__module__)
|
|
|
|
|
cls.candidate = cls.reference
|
|
|
|
|
|
|
|
|
|
# run gray to generate the candidate outputs
|
|
|
|
|
proc = run_gray(cls.inputs, cls.candidate, binary=options.binary,
|
|
|
|
|
options=['-s', cls.inputs / 'filelist.txt'])
|
|
|
|
|
assert proc.returncode == 0, \
|
|
|
|
|
f"gray failed with exit code {proc.returncode}"
|
|
|
|
|
|
|
|
|
|
# store the stderr for manual inspection
|
|
|
|
|
with open(str(cls.candidate / 'log'), 'w') as log:
|
|
|
|
|
log.write(proc.stderr)
|
|
|
|
|
|
|
|
|
|
@classmethod
|
|
|
|
|
def tearDownClass(cls):
|
|
|
|
|
'''
|
|
|
|
|
Clean up after all tests
|
|
|
|
|
'''
|
|
|
|
|
# remove temporary directory
|
|
|
|
|
if cls._passed or not options.keep_failed:
|
|
|
|
|
shutil.rmtree(cls._tempdir)
|
|
|
|
|
else:
|
|
|
|
|
print()
|
|
|
|
|
print('Some tests failed: preserving outputs in', cls._tempdir)
|
|
|
|
|
|
|
|
|
|
def run(self, result: unittest.runner.TextTestResult):
|
|
|
|
|
'''
|
|
|
|
|
Override to store the test results for tearDownClass
|
|
|
|
|
'''
|
|
|
|
|
TestCase.run(self, result)
|
|
|
|
|
self.__class__._passed = result.failures == []
|
|
|
|
|
|
|
|
|
|
def test_eccd_values(self):
|
|
|
|
|
'''
|
|
|
|
|
Comparing the ECCD values
|
|
|
|
|
'''
|
|
|
|
|
from collections import defaultdict
|
|
|
|
|
|
|
|
|
|
ref = load_table(self.reference / 'sum-summary.txt')
|
|
|
|
|
cand = load_table(self.candidate / 'sum-summary.txt')
|
|
|
|
|
|
|
|
|
|
# precision as number of decimal places
|
|
|
|
|
prec = defaultdict(lambda: 3, [
|
|
|
|
|
('dPdV_peak', -2), ('dPdV_max', -2),
|
|
|
|
|
('J_φ_peak', -2), ('J_φ_max', -2),
|
|
|
|
|
])
|
|
|
|
|
|
|
|
|
|
for val in ref.dtype.names:
|
|
|
|
|
with self.subTest(value=val):
|
2024-10-18 15:56:17 +02:00
|
|
|
|
self.assertAlmostEqual(ref[val][0], cand[val][0],
|
|
|
|
|
prec[val], msg=f"{val} changed)")
|
2024-05-16 17:46:21 +02:00
|
|
|
|
|
2024-10-18 15:56:17 +02:00
|
|
|
|
def test_eccd_profiles(self):
|
2024-05-16 17:46:21 +02:00
|
|
|
|
'''
|
2024-10-18 15:56:17 +02:00
|
|
|
|
Comparing the ECCD radial profiles
|
2024-05-16 17:46:21 +02:00
|
|
|
|
'''
|
2024-10-18 15:56:17 +02:00
|
|
|
|
from scipy.stats import wasserstein_distance as emd
|
|
|
|
|
import numpy as np
|
2024-05-16 17:46:21 +02:00
|
|
|
|
|
|
|
|
|
ref = load_table(self.reference / 'sum-ec-profiles.txt')
|
|
|
|
|
cand = load_table(self.candidate / 'sum-ec-profiles.txt')
|
|
|
|
|
|
2024-10-18 15:56:17 +02:00
|
|
|
|
if options.visual:
|
|
|
|
|
import matplotlib.pyplot as plt
|
|
|
|
|
|
|
|
|
|
fig, axes = plt.subplots(3, 1, sharex=True)
|
|
|
|
|
plt.suptitle(self.__module__ + '.test_ec_profiles')
|
|
|
|
|
|
|
|
|
|
axes[0].set_ylabel('$J_\\text{cd}$')
|
|
|
|
|
axes[0].plot(ref['ρ_t'], ref['J_cd'], c='xkcd:red',
|
|
|
|
|
label='reference')
|
|
|
|
|
axes[0].plot(cand['ρ_t'], cand['J_cd'], c='xkcd:green',
|
|
|
|
|
ls='-.', label='candidate')
|
|
|
|
|
axes[0].legend()
|
|
|
|
|
|
|
|
|
|
axes[1].set_ylabel('$dP/dV$')
|
|
|
|
|
axes[1].plot(ref['ρ_t'], ref['dPdV'], c='xkcd:red')
|
|
|
|
|
axes[1].plot(cand['ρ_t'], cand['dPdV'], c='xkcd:green', ls='-.')
|
|
|
|
|
|
|
|
|
|
axes[2].set_xlabel('$ρ_t$')
|
|
|
|
|
axes[2].set_ylabel('$J_φ$')
|
|
|
|
|
axes[2].plot(ref['ρ_t'], ref['J_φ'], c='xkcd:red')
|
|
|
|
|
axes[2].plot(cand['ρ_t'], cand['J_φ'], c='xkcd:green', ls='-.')
|
|
|
|
|
plt.show()
|
|
|
|
|
|
|
|
|
|
for val in ['J_cd', 'dPdV', 'J_φ']:
|
|
|
|
|
with self.subTest(profile=val):
|
|
|
|
|
y1 = abs(ref[val]) / np.sum(abs(ref[val]))
|
|
|
|
|
y2 = abs(cand[val]) / np.sum(abs(cand[val]))
|
|
|
|
|
dist = emd(ref['ρ_t'], cand['ρ_t'], y1, y2)
|
|
|
|
|
self.assertLess(dist, 0.01, f'{val} profile changed')
|