From 8fc96c13abb52f497755815677168790a7dfc72f Mon Sep 17 00:00:00 2001 From: Jan Petykiewicz Date: Sun, 27 Oct 2019 16:12:30 -0700 Subject: [PATCH] test updates - move generalizable fixtures out into conftest.py - move some other functions out to utils - fix test_poynting_planes() for fdtd --- meanas/test/conftest.py | 92 +++++++++++++++++++++++++ meanas/test/test_fdtd.py | 145 ++++++++------------------------------- meanas/test/utils.py | 11 +++ 3 files changed, 131 insertions(+), 117 deletions(-) create mode 100644 meanas/test/conftest.py create mode 100644 meanas/test/utils.py diff --git a/meanas/test/conftest.py b/meanas/test/conftest.py new file mode 100644 index 0000000..c2adeb9 --- /dev/null +++ b/meanas/test/conftest.py @@ -0,0 +1,92 @@ +from typing import List, Tuple +import numpy +import pytest + + +PRNG = numpy.random.RandomState(12345) + + +##################################### +# Test fixtures +##################################### + +@pytest.fixture(scope='module', + params=[(5, 5, 1), + (5, 1, 5), + (5, 5, 5), + #(7, 7, 7), + ]) +def shape(request): + yield (3, *request.param) + + +@pytest.fixture(scope='module', params=[1.0, 1.5]) +def epsilon_bg(request): + yield request.param + + +@pytest.fixture(scope='module', params=[1.0, 2.5]) +def epsilon_fg(request): + yield request.param + + +@pytest.fixture(scope='module', params=['center', '000', 'random']) +def epsilon(request, shape, epsilon_bg, epsilon_fg): + is3d = (numpy.array(shape) == 1).sum() == 0 + if is3d: + if request.param == '000': + pytest.skip('Skipping 000 epsilon because test is 3D (for speed)') + if epsilon_bg != 1: + pytest.skip('Skipping epsilon_bg != 1 because test is 3D (for speed)') + if epsilon_fg not in (1.0, 2.0): + pytest.skip('Skipping epsilon_fg not in (1, 2) because test is 3D (for speed)') + + epsilon = numpy.full(shape, epsilon_bg, dtype=float) + if request.param == 'center': + epsilon[:, shape[1]//2, shape[2]//2, shape[3]//2] = epsilon_fg + elif request.param == '000': + epsilon[:, 0, 0, 0] = epsilon_fg + elif request.param == 'random': + epsilon[:] = PRNG.uniform(low=min(epsilon_bg, epsilon_fg), + high=max(epsilon_bg, epsilon_fg), + size=shape) + + yield epsilon + + +@pytest.fixture(scope='module', params=[1.0])#, 1.5]) +def j_mag(request): + yield request.param + + +@pytest.fixture(scope='module', params=['center', 'random']) +def j_distribution(request, shape, j_mag): + j = numpy.zeros(shape) + if request.param == 'center': + j[:, shape[1]//2, shape[2]//2, shape[3]//2] = j_mag + elif request.param == '000': + j[:, 0, 0, 0] = j_mag + elif request.param == 'random': + j[:] = PRNG.uniform(low=-j_mag, high=j_mag, size=shape) + yield j + + +@pytest.fixture(scope='module', params=[1.0, 1.5]) +def dx(request): + yield request.param + + +@pytest.fixture(scope='module', params=['uniform']) +def dxes(request, shape, dx): + if request.param == 'uniform': + dxes = [[numpy.full(s, dx) for s in shape[1:]] for _ in range(2)] + yield dxes + + +@pytest.fixture(scope='module', + params=[(0, 4, 8), + #(0,), + ] + ) +def j_steps(request): + yield request.param diff --git a/meanas/test/test_fdtd.py b/meanas/test/test_fdtd.py index 1b185f5..2fdcbea 100644 --- a/meanas/test/test_fdtd.py +++ b/meanas/test/test_fdtd.py @@ -1,21 +1,11 @@ -import numpy -import pytest -import dataclasses from typing import List, Tuple +import dataclasses +import pytest +import numpy from numpy.testing import assert_allclose, assert_array_equal -from meanas import fdtd - - -prng = numpy.random.RandomState(12345) - - -def assert_fields_close(a, b, *args, **kwargs): - numpy.testing.assert_allclose(a, b, verbose=False, err_msg='Fields did not match:\n{}\n{}'.format(numpy.rollaxis(a, -1), - numpy.rollaxis(b, -1)), *args, **kwargs) - -def assert_close(a, b, *args, **kwargs): - numpy.testing.assert_allclose(a, b, *args, **kwargs) +from .. import fdtd +from .utils import assert_close, assert_fields_close def test_initial_fields(sim): @@ -101,40 +91,43 @@ def test_poynting_divergence(sim): def test_poynting_planes(sim): - mask = (sim.js[0] != 0) + mask = (sim.js[0] != 0).any(axis=0) if mask.sum() > 1: - pytest.skip('test_poynting_planes can only test single point sources') + pytest.skip('test_poynting_planes can only test single point sources, got {}'.format(mask.sum())) args = {'dxes': sim.dxes, 'epsilon': sim.epsilon} - dV = numpy.prod(numpy.meshgrid(*sim.dxes[0], indexing='ij'), axis=0) - mx = numpy.roll(mask, (-1, -1), axis=(0, 1)) - my = numpy.roll(mask, -1, axis=2) - mz = numpy.roll(mask, (+1, -1), axis=(0, 3)) - px = numpy.roll(mask, -1, axis=0) - py = mask.copy() - pz = numpy.roll(mask, +1, axis=0) + mx = numpy.roll(mask, -1, axis=0) + my = numpy.roll(mask, -1, axis=1) + mz = numpy.roll(mask, -1, axis=2) u_eprev = None for ii in range(1, 8): u_hstep = fdtd.energy_hstep(e0=sim.es[ii-1], h1=sim.hs[ii], e2=sim.es[ii], **args) u_estep = fdtd.energy_estep(h0=sim.hs[ii], e1=sim.es[ii], h2=sim.hs[ii + 1], **args) + delta_j_B = fdtd.delta_energy_j(j0=sim.js[ii], e1=sim.es[ii], dxes=sim.dxes) + du_half_h2e = u_estep - u_hstep - delta_j_B s_h2e = -fdtd.poynting(e=sim.es[ii], h=sim.hs[ii], dxes=sim.dxes) * sim.dt - planes = [s_h2e[px].sum(), -s_h2e[mx].sum(), - s_h2e[py].sum(), -s_h2e[my].sum(), - s_h2e[pz].sum(), -s_h2e[mz].sum()] - assert_close(sum(planes), (u_estep - u_hstep).sum()) + planes = [s_h2e[0, mask].sum(), -s_h2e[0, mx].sum(), + s_h2e[1, mask].sum(), -s_h2e[1, my].sum(), + s_h2e[2, mask].sum(), -s_h2e[2, mz].sum()] + + assert_close(sum(planes), du_half_h2e[mask]) + if u_eprev is None: u_eprev = u_estep continue + delta_j_A = fdtd.delta_energy_j(j0=sim.js[ii], e1=sim.es[ii-1], dxes=sim.dxes) + du_half_e2h = u_hstep - u_eprev - delta_j_A + s_e2h = -fdtd.poynting(e=sim.es[ii - 1], h=sim.hs[ii], dxes=sim.dxes) * sim.dt - planes = [s_e2h[px].sum(), -s_e2h[mx].sum(), - s_e2h[py].sum(), -s_e2h[my].sum(), - s_e2h[pz].sum(), -s_e2h[mz].sum()] - assert_close(sum(planes), (u_hstep - u_eprev).sum()) + planes = [s_e2h[0, mask].sum(), -s_e2h[0, mx].sum(), + s_e2h[1, mask].sum(), -s_e2h[1, my].sum(), + s_e2h[2, mask].sum(), -s_e2h[2, mz].sum()] + assert_close(sum(planes), du_half_e2h[mask]) # previous half-step u_eprev = u_estep @@ -143,94 +136,14 @@ def test_poynting_planes(sim): ##################################### # Test fixtures ##################################### - -@pytest.fixture(scope='module', - params=[(5, 5, 1), - (5, 1, 5), - (5, 5, 5), -# (7, 7, 7), - ]) -def shape(request): - yield (3, *request.param) +# Also see conftest.py -@pytest.fixture(scope='module', params=[0.3]) +@pytest.fixture(params=[0.3]) def dt(request): yield request.param -@pytest.fixture(scope='module', params=[1.0, 1.5]) -def epsilon_bg(request): - yield request.param - - -@pytest.fixture(scope='module', params=[1.0, 2.5]) -def epsilon_fg(request): - yield request.param - - -@pytest.fixture(scope='module', params=['center', '000', 'random']) -def epsilon(request, shape, epsilon_bg, epsilon_fg): - is3d = (numpy.array(shape) == 1).sum() == 0 - if is3d: - if request.param == '000': - pytest.skip('Skipping 000 epsilon because test is 3D (for speed)') - if epsilon_bg != 1: - pytest.skip('Skipping epsilon_bg != 1 because test is 3D (for speed)') - if epsilon_fg not in (1.0, 2.0): - pytest.skip('Skipping epsilon_fg not in (1, 2) because test is 3D (for speed)') - - epsilon = numpy.full(shape, epsilon_bg, dtype=float) - if request.param == 'center': - epsilon[:, shape[1]//2, shape[2]//2, shape[3]//2] = epsilon_fg - elif request.param == '000': - epsilon[:, 0, 0, 0] = epsilon_fg - elif request.param == 'random': - epsilon[:] = prng.uniform(low=min(epsilon_bg, epsilon_fg), - high=max(epsilon_bg, epsilon_fg), - size=shape) - - yield epsilon - - -@pytest.fixture(scope='module', params=[1.0])#, 1.5]) -def j_mag(request): - yield request.param - - -@pytest.fixture(scope='module', params=['center', 'random']) -def j_distribution(request, shape, j_mag): - j = numpy.zeros(shape) - if request.param == 'center': - j[:, shape[1]//2, shape[2]//2, shape[3]//2] = j_mag - elif request.param == '000': - j[:, 0, 0, 0] = j_mag - elif request.param == 'random': - j[:] = prng.uniform(low=-j_mag, high=j_mag, size=shape) - yield j - - -@pytest.fixture(scope='module', params=[1.0, 1.5]) -def dx(request): - yield request.param - - -@pytest.fixture(scope='module', params=['uniform']) -def dxes(request, shape, dx): - if request.param == 'uniform': - dxes = [[numpy.full(s, dx) for s in shape[1:]] for _ in range(2)] - yield dxes - - -@pytest.fixture(scope='module', - params=[(0,), - (0, 4, 8), - ] - ) -def j_steps(request): - yield request.param - - @dataclasses.dataclass() class SimResult: shape: Tuple[int] @@ -244,7 +157,7 @@ class SimResult: js: List[numpy.ndarray] = dataclasses.field(default_factory=list) -@pytest.fixture(scope='module') +@pytest.fixture() def sim(request, shape, epsilon, dxes, dt, j_distribution, j_steps): is3d = (numpy.array(shape) == 1).sum() == 0 if is3d: @@ -281,5 +194,3 @@ def sim(request, shape, epsilon, dxes, dt, j_distribution, j_steps): sim.es.append(e) sim.hs.append(h) return sim - - diff --git a/meanas/test/utils.py b/meanas/test/utils.py new file mode 100644 index 0000000..53c25e6 --- /dev/null +++ b/meanas/test/utils.py @@ -0,0 +1,11 @@ +import numpy + + +def assert_fields_close(x, y, *args, **kwargs): + numpy.testing.assert_allclose(x, y, verbose=False, + err_msg='Fields did not match:\n{}\n{}'.format(numpy.rollaxis(x, -1), + numpy.rollaxis(y, -1)), *args, **kwargs) + +def assert_close(x, y, *args, **kwargs): + numpy.testing.assert_allclose(x, y, *args, **kwargs) +