示例#1
0
    def __init__(self, setup):
        self.setup = setup

        sigma2 = pow(setup.sigma, 2)
        dx_opt = abs(setup.C_opt / (.5 * sigma2 - setup.r) * setup.l2_opt *
                     sigma2)
        dt_opt = pow(dx_opt, 2) / sigma2 / setup.l2_opt

        # adjusting dt so that nt is integer
        self.dt = setup.T
        self.nt = 0
        while self.dt > dt_opt:
            self.nt += 1
            self.dt = setup.T / self.nt

        # adjusting dx to match requested l^2
        dx = np.sqrt(setup.l2_opt * self.dt) * setup.sigma

        # calculating actual u number and lambda
        self.C = -(.5 * sigma2 - setup.r) * (-self.dt) / dx
        self.l2 = dx * dx / sigma2 / self.dt

        # adjusting nx and setting S_beg, S_end
        S_beg = setup.S_match
        self.nx = 1
        while S_beg > setup.S_min:
            self.nx += 1
            S_beg = np.exp(np.log(setup.S_match) - self.nx * dx)

        self.ix_match = self.nx

        S_end = setup.S_match
        while S_end < setup.S_max:
            self.nx += 1
            S_end = np.exp(np.log(S_beg) + (self.nx - 1) * dx)

        # asset price
        self.S = np.exp(np.log(S_beg) + np.arange(self.nx) * dx)

        self.mu_coeff = (0.5 / self.l2, )
        self.solvers = {}
        self.solvers[1] = Factories.advection_diffusion_1d(
            advectee=setup.payoff(self.S),
            advector=self.C,
            options=Options(n_iters=1, non_zero_mu_coeff=True),
            boundary_conditions=(ExtrapolatedBoundaryCondition(), ))
        self.solvers[2] = Factories.advection_diffusion_1d(
            advectee=setup.payoff(self.S),
            advector=self.C,
            options=Options(**OPTIONS),
            boundary_conditions=(ExtrapolatedBoundaryCondition(), ))
示例#2
0
def test_diffusion_only_2d(data0=np.array([[0, 0, 0], [0, 1., 0], [0, 0, 0]]),
                           mu_coeff=(.1, .1),
                           n_steps=1):
    # Arrange
    options = Options(non_zero_mu_coeff=True)
    boundary_conditions = tuple([Periodic()] * 2)
    advectee = ScalarField(data0, options.n_halo, boundary_conditions)
    advector = VectorField(data=(np.zeros(
        (data0.shape[0] + 1, data0.shape[1])),
                                 np.zeros(
                                     (data0.shape[0], data0.shape[1] + 1))),
                           halo=options.n_halo,
                           boundary_conditions=boundary_conditions)
    solver = Solver(stepper=Stepper(options=options, grid=data0.shape),
                    advector=advector,
                    advectee=advectee)

    # Act
    solver.advance(n_steps=n_steps, mu_coeff=mu_coeff)

    # Assert
    data1 = solver.advectee.get()
    np.testing.assert_almost_equal(actual=np.sum(data1), desired=np.sum(data0))
    assert np.amax(data0) > np.amax(data1)
    assert np.amin(data1) >= 0
    assert np.count_nonzero(data1) == 5
示例#3
0
def test_double_pass_donor_cell(n_iters):
    courant = .5

    options = Options(n_iters=n_iters, DPDC=True, nonoscillatory=True)
    state = np.array([0, 1, 0], dtype=options.dtype)
    boundary_conditions = (Periodic(),)

    mpdata = Solver(
        stepper=Stepper(options=options, n_dims=state.ndim, non_unit_g_factor=False),
        advectee=ScalarField(
            state,
            halo=options.n_halo,
            boundary_conditions=boundary_conditions
        ),
        advector=VectorField(
            (np.full(state.shape[0] + 1, courant, dtype=options.dtype),),
            halo=options.n_halo,
            boundary_conditions=boundary_conditions
        )
    )
    steps = 1

    conserved = np.sum(mpdata.advectee.get())
    mpdata.advance(steps)

    assert np.sum(mpdata.advectee.get()) == conserved
示例#4
0
    def __init__(self, nz, dt, advector_of_t, advectee_of_zZ_at_t0,
                 g_factor_of_zZ, mpdata_settings):
        self.t = 0
        self.dt = dt
        self.advector_of_t = advector_of_t

        grid = (nz, )
        options = Options(n_iters=mpdata_settings['n_iters'],
                          infinite_gauge=mpdata_settings['iga'],
                          flux_corrected_transport=mpdata_settings['fct'],
                          third_order_terms=mpdata_settings['tot'])
        stepper = Stepper(options=options, grid=grid, non_unit_g_factor=True)
        bcs = (ExtrapolatedBoundaryCondition(), )
        g_factor = ScalarField(data=g_factor_of_zZ(
            arakawa_c.z_scalar_coord(grid)),
                               halo=options.n_halo,
                               boundary_conditions=bcs)
        advector = VectorField(data=(np.full(nz + 1, advector_of_t(0)), ),
                               halo=options.n_halo,
                               boundary_conditions=bcs)
        self.advectee = ScalarField(data=advectee_of_zZ_at_t0(
            arakawa_c.z_scalar_coord(grid)),
                                    halo=options.n_halo,
                                    boundary_conditions=bcs)
        self.solver = Solver(stepper=stepper,
                             advectee=self.advectee,
                             advector=advector,
                             g_factor=g_factor)
示例#5
0
    def __init__(self, *, fields,
                 n_iters=2, infinite_gauge=True,
                 flux_corrected_transport=True, third_order_terms=False):
        self.grid = fields.g_factor.shape
        self.asynchronous = False
        self.thread: (Thread, None) = None

        options = Options(
            n_iters=n_iters,
            infinite_gauge=infinite_gauge,
            flux_corrected_transport=flux_corrected_transport,
            third_order_terms=third_order_terms
        )
        disable_threads_if_needed = {}
        if not conf.JIT_FLAGS['parallel']:
            disable_threads_if_needed['n_threads'] = 1

        stepper = Stepper(options=options, grid=self.grid, non_unit_g_factor=True, **disable_threads_if_needed)

        # CFL condition
        for d in range(len(fields.advector)):
            np.testing.assert_array_less(np.abs(fields.advector[d]), 1)

        self.advector = fields.advector
        advector_impl = VectorField(fields.advector, halo=options.n_halo,
                                    boundary_conditions=(PeriodicBoundaryCondition(), PeriodicBoundaryCondition()))

        self.g_factor = fields.g_factor
        g_factor_impl = ScalarField(fields.g_factor.astype(dtype=options.dtype), halo=options.n_halo,
                               boundary_conditions=(PeriodicBoundaryCondition(), PeriodicBoundaryCondition()))
        self.mpdatas = {}
        for k, v in fields.advectees.items():
            advectee = ScalarField(np.full(self.grid, v, dtype=options.dtype), halo=options.n_halo,
                                   boundary_conditions=(PeriodicBoundaryCondition(), PeriodicBoundaryCondition()))
            self.mpdatas[k] = Solver(stepper=stepper, advectee=advectee, advector=advector_impl, g_factor=g_factor_impl)
示例#6
0
    def test_make_upwind(self):
        # Arrange
        psi_data = np.array((0, 1, 0))
        flux_data = np.array((0, 0, 1, 0))

        options = Options()
        halo = options.n_halo
        traversals = Traversals(grid=psi_data.shape,
                                halo=halo,
                                jit_flags={},
                                n_threads=1)
        upwind = make_upwind(options=options,
                             non_unit_g_factor=False,
                             traversals=traversals)

        bc = [PeriodicBoundaryCondition()]
        psi = ScalarField(psi_data, halo, bc)
        psi_impl = psi.impl
        flux_impl = VectorField((flux_data, ), halo, bc).impl
        null_impl = ScalarField.make_null(len(psi_data.shape)).impl

        # Act
        upwind(psi_impl[0], *flux_impl, *null_impl)

        # Assert
        np.testing.assert_array_equal(psi.get(), np.roll(psi_data, 1))
示例#7
0
def analysis(setup, grid_layout, psi_coord, options_dict, GC_max):
    options_str = str(options_dict)
    options = Options(**options_dict)
    simulation = Simulation(setup, grid_layout, psi_coord, options, GC_max)
    result = {
        "n": [],
        "n_analytical": [],
        "error_norm_L2": [],
        "wall_time": []
    }
    last_step = 0
    for n_steps in simulation.out_steps:
        steps = n_steps - last_step
        wall_time = simulation.step(steps) if steps > 0 else 0
        last_step += steps
        result['n'].append(simulation.n_of_r.copy())
        result['wall_time'].append(wall_time)
    result['r'] = simulation.r.copy()
    result['rh'] = simulation.rh.copy()
    result['dx'] = simulation.dx
    return Result(grid_layout_str=grid_layout.__class__.__name__,
                  option_str=options_str,
                  result=result,
                  out_steps=simulation.out_steps,
                  dt=simulation.dt)
示例#8
0
def test_upwind(shape, ij0, out, courant_number):
    value = 44
    scalar_field_init = np.zeros(shape)
    scalar_field_init[ij0] = value

    vector_field_init = (
        np.full((shape[0] + 1, shape[1]), courant_number[0]),
        np.full((shape[0], shape[1] + 1), courant_number[1])
    )
    options = Options(n_iters=1)

    bcs = (Periodic(), Periodic())
    advectee = ScalarField(scalar_field_init, halo=options.n_halo, boundary_conditions=bcs)
    advector = VectorField(vector_field_init, halo=options.n_halo, boundary_conditions=bcs)

    mpdata = Solver(
        stepper=Stepper(options=options, grid=shape, n_threads=1),
        advector=advector,
        advectee=advectee
    )
    mpdata.advance(n_steps=1)

    np.testing.assert_array_equal(
        mpdata.advectee.get(),
        out
    )
示例#9
0
def test_mu_arg_handling(case):
    opt = Options(non_zero_mu_coeff=case['non_zero_mu_coeff'])
    advector = VectorField((np.asarray([1., 2, 3]),), opt.n_halo, BCS)
    advectee = ScalarField(np.asarray([4., 5]), opt.n_halo, BCS)
    stepper = Stepper(options=opt, n_dims=1)
    sut = Solver(stepper, advectee, advector, case['g_factor'])

    sut.advance(1, mu_coeff=case['mu'])
def test_timing_3d(benchmark, options, dtype, static, num_threads):
    numba.set_num_threads(num_threads)

    settings = Settings(n=20, dt=1)
    simulation = Simulation(settings, Options(**options, dtype=dtype), static=static)

    def reset():
        simulation.solver.advectee.get()[:] = settings.advectee

    n_steps = 10
    benchmark.pedantic(simulation.run, (n_steps,), setup=reset, warmup_rounds=1, rounds=1)
示例#11
0
def test_shared_advector():
    n_x = 100
    arr = np.zeros(n_x)
    opt1 = Options(n_iters=2, DPDC=True)
    opt2 = Options(n_iters=2)
    b_c = (Periodic(),)

    halo = opt1.n_halo
    assert opt2.n_halo == halo

    advector = VectorField(data=(np.zeros(n_x + 1),), halo=halo, boundary_conditions=b_c)
    _ = Solver(
        stepper=Stepper(options=opt1, grid=(n_x,)),
        advectee=ScalarField(data=arr, halo=halo, boundary_conditions=b_c),
        advector=advector
    )
    solver = Solver(
        stepper=Stepper(options=opt2, grid=(n_x,)),
        advectee=ScalarField(data=arr, halo=halo, boundary_conditions=b_c),
        advector=advector
    )
    solver.advance(1)
示例#12
0
def make_data(setup, grid, opts):
    options = Options(**opts)
    simulation = Simulation(setup=setup,
                            grid_layout=grid,
                            psi_coord=x_id(),
                            opts=options,
                            GC_max=default_GC_max)
    result = {"wall_time": []}
    last_step = 0
    for n_steps in simulation.out_steps:
        steps = n_steps - last_step
        wall_time_per_timestep = simulation.step(steps)
        last_step += steps
        result['wall_time'].append(wall_time_per_timestep)
    return result
示例#13
0
    def __init__(
        self,
        nz,
        dt,
        advector_of_t,
        advectee_of_zZ_at_t0,
        g_factor_of_zZ,
        mpdata_settings,
    ):
        self.__t = 0
        self.dt = dt
        self.advector_of_t = advector_of_t

        grid = (nz, )
        options = Options(
            n_iters=mpdata_settings["n_iters"],
            infinite_gauge=mpdata_settings["iga"],
            nonoscillatory=mpdata_settings["fct"],
            third_order_terms=mpdata_settings["tot"],
        )
        stepper = Stepper(options=options, grid=grid, non_unit_g_factor=True)
        bcs = (Extrapolated(), )
        zZ_scalar = arakawa_c.z_scalar_coord(grid) / nz
        g_factor = ScalarField(
            data=g_factor_of_zZ(zZ_scalar),
            halo=options.n_halo,
            boundary_conditions=bcs,
        )
        advector = VectorField(
            data=(np.full(nz + 1, advector_of_t(0)), ),
            halo=options.n_halo,
            boundary_conditions=bcs,
        )
        self.advectee = ScalarField(
            data=advectee_of_zZ_at_t0(zZ_scalar),
            halo=options.n_halo,
            boundary_conditions=bcs,
        )
        self.solver = Solver(
            stepper=stepper,
            advectee=self.advectee,
            advector=advector,
            g_factor=g_factor,
        )
def test_formulae_upwind():
    # Arrange
    psi_data = np.array((0, 1, 0))
    flux_data = np.array((0, 0, 1, 0))

    options = Options()
    halo = options.n_halo
    traversals = Traversals(grid=psi_data.shape,
                            halo=halo,
                            jit_flags=options.jit_flags,
                            n_threads=1)
    upwind = make_upwind(options=options,
                         non_unit_g_factor=False,
                         traversals=traversals)

    boundary_conditions = (Periodic(), )

    psi = ScalarField(psi_data, halo, boundary_conditions)
    psi.assemble(traversals)
    psi_impl = psi.impl

    flux = VectorField((flux_data, ), halo, boundary_conditions)
    flux.assemble(traversals)
    flux_impl = flux.impl

    # Act
    with warnings.catch_warnings():
        warnings.simplefilter('ignore',
                              category=NumbaExperimentalFeatureWarning)
        upwind(
            traversals.null_impl,
            _Impl(field=psi_impl[IMPL_META_AND_DATA], bc=psi_impl[IMPL_BC]),
            _Impl(field=flux_impl[IMPL_META_AND_DATA], bc=flux_impl[IMPL_BC]),
            _Impl(field=traversals.null_impl.scalar[IMPL_META_AND_DATA],
                  bc=traversals.null_impl.scalar[IMPL_BC]))

    # Assert
    np.testing.assert_array_equal(psi.get(), np.roll(psi_data, 1))
示例#15
0
def test_upwind_1d():
    state = np.array([0, 1, 0])
    courant = 1

    options = Options(n_iters=1)
    mpdata = Solver(
        stepper=Stepper(options=options, n_dims=len(state.shape), non_unit_g_factor=False),
        advectee=ScalarField(
            state.astype(options.dtype),
            halo=options.n_halo,
            boundary_conditions=(Periodic(),)
        ),
        advector=VectorField(
            (np.full(state.shape[0] + 1, courant, dtype=options.dtype),),
            halo=options.n_halo,
            boundary_conditions=(Periodic(),)
        )
    )
    n_steps = 5

    conserved = np.sum(mpdata.advectee.get())
    mpdata.advance(n_steps)

    assert np.sum(mpdata.advectee.get()) == conserved
示例#16
0
from PyMPDATA.arakawa_c.traversals import Traversals
from PyMPDATA.arakawa_c.meta import META_HALO_VALID
from PyMPDATA import Options, ScalarField, VectorField, ConstantBoundaryCondition
from PyMPDATA.arakawa_c.indexers import indexers
from PyMPDATA.arakawa_c.enumerations import MAX_DIM_NUM, INNER, MID3D, OUTER, IMPL_META_AND_DATA, IMPL_BC, \
    META_AND_DATA_META, ARG_FOCUS, INVALID_INDEX
import pytest
import numba
import numpy as np

jit_flags = Options().jit_flags


@numba.njit(**jit_flags)
def cell_id(i, j, k):
    if i == INVALID_INDEX:
        i = 0
    if j == INVALID_INDEX:
        j = 0
    return 100 * i + 10 * j + k


@numba.njit(**jit_flags)
def _cell_id_scalar(value, arg_1_vec, arg_2_scl, arg_3_scl, arg_4_scl):
    focus = arg_1_vec[ARG_FOCUS]
    if focus != arg_2_scl[ARG_FOCUS]:
        raise Exception()
    if focus != arg_3_scl[ARG_FOCUS]:
        raise Exception()
    if focus != arg_4_scl[ARG_FOCUS]:
        raise Exception()
示例#17
0
def rhod_of_z(arg):
    return 1 - arg * 1e-4


RHOD = np.repeat(rhod_of_z((np.arange(GRID[1]) + 1 / 2) / GRID[1]).reshape(
    (1, GRID[1])),
                 GRID[0],
                 axis=0)

VALUES = {'th': np.full(GRID, 300), 'qv': np.full(GRID, .001)}


@pytest.mark.parametrize(
    "options",
    (Options(n_iters=1), Options(n_iters=2),
     Options(n_iters=2,
             nonoscillatory=True), Options(n_iters=3, nonoscillatory=True),
     Options(n_iters=2, nonoscillatory=True, infinite_gauge=True),
     Options(nonoscillatory=True, infinite_gauge=True, third_order_terms=True),
     Options(nonoscillatory=False, infinite_gauge=True),
     Options(nonoscillatory=False, third_order_terms=True),
     Options(nonoscillatory=False, infinite_gauge=True,
             third_order_terms=True)))
def test_single_timestep(options):
    # Arrange
    stepper = Stepper(options=options, grid=GRID, non_unit_g_factor=True)
    advector = nondivergent_vector_field_2d(GRID, SIZE, TIMESTEP,
                                            stream_function, options.n_halo)
    g_factor = ScalarField(RHOD.astype(dtype=options.dtype),
                           halo=options.n_halo,
示例#18
0
from PyMPDATA_examples.Molenkamp_test_as_in_Jaruga_et_al_2015_Fig_12.simulation import Simulation
from PyMPDATA_examples.Molenkamp_test_as_in_Jaruga_et_al_2015_Fig_12.setup import Setup
from PyMPDATA import Options
from joblib import Parallel, delayed

options = {
    'upwind':
    Options(n_iters=1),
    '2+fct':
    Options(n_iters=2, flux_corrected_transport=True),
    '3+fct+tot':
    Options(n_iters=3, flux_corrected_transport=True, third_order_terms=True),
    '2+fct+iga':
    Options(n_iters=2, flux_corrected_transport=True, infinite_gauge=True)
}


def compute_panel(panel):
    setup = Setup(n_rotations=6)
    simulation = Simulation(setup, options[panel])
    if panel == 'upwind':
        return simulation.state
    simulation.run()
    return simulation.state


def fig_12_data():
    data = Parallel(n_jobs=-2)(
        delayed(compute_panel)(panel)
        for panel in ['upwind', '2+fct', '3+fct+tot', '2+fct+iga'])
    return data
# pylint: disable=missing-module-docstring,missing-class-docstring,missing-function-docstring
import pytest
from PyMPDATA import Options
from PyMPDATA.impl.domain_decomposition import make_subdomain

JIT_FLAGS = Options().jit_flags


@pytest.mark.parametrize(
    "span, rank, size, result",
    [(10, 0, 1, (0, 10)),
     pytest.param(1, 1, 1, (0, 1), marks=pytest.mark.xfail(raises=ValueError)),
     (10, 0, 3, (0, 4)), (10, 1, 3, (4, 8)), (10, 2, 3, (8, 10)),
     (10, 0, 11, (0, 1)), (10, 9, 11, (9, 10))])
def test_subdomain(span, rank, size, result):
    subdomain = make_subdomain(JIT_FLAGS)
    assert subdomain(span, rank, size) == result
示例#20
0
import numpy as np
import pytest
from PyMPDATA import Solver, Stepper, ScalarField, VectorField, Options
from PyMPDATA.boundary_conditions import Periodic

BCS = (Periodic(),)


@pytest.mark.parametrize("case", (
        {'g_factor': None, 'non_zero_mu_coeff': True, 'mu': None},
        {'g_factor': None, 'non_zero_mu_coeff': True, 'mu': (0,)},
        pytest.param({
            'g_factor': None,
            'non_zero_mu_coeff': False,
            'mu': (0,)
        }, marks=pytest.mark.xfail(strict=True)),
        pytest.param({
            'g_factor': ScalarField(np.asarray([1., 1]), Options().n_halo, BCS),
            'non_zero_mu_coeff': True,
            'mu': None
        }, marks=pytest.mark.xfail(strict=True))
))
def test_mu_arg_handling(case):
    opt = Options(non_zero_mu_coeff=case['non_zero_mu_coeff'])
    advector = VectorField((np.asarray([1., 2, 3]),), opt.n_halo, BCS)
    advectee = ScalarField(np.asarray([4., 5]), opt.n_halo, BCS)
    stepper = Stepper(options=opt, n_dims=1)
    sut = Solver(stepper, advectee, advector, case['g_factor'])

    sut.advance(1, mu_coeff=case['mu'])
示例#21
0
def test_mpdata_2d(case_data):
    case = {
        "nx": case_data[0],
        "ny": case_data[1],
        "Cx": case_data[2],
        "Cy": case_data[3],
        "nt": case_data[4],
        "ni": case_data[5],
        "dimsplit": case_data[6],
        "input": case_data[7],
        "output": case_data[8]
    }
    # Arrange
    data = case["input"].reshape((case["nx"], case["ny"]))
    courant = [case["Cx"], case["Cy"]]
    options = Options(n_iters=case["ni"], dimensionally_split=case["dimsplit"])
    grid = data.shape
    advector_data = [
        np.full((grid[0] + 1, grid[1]), courant[0], dtype=options.dtype),
        np.full((grid[0], grid[1] + 1), courant[1], dtype=options.dtype)
    ]
    bcs = (Periodic(), Periodic())
    advector = VectorField(advector_data,
                           halo=options.n_halo,
                           boundary_conditions=bcs)
    advectee = ScalarField(data=data.astype(dtype=options.dtype),
                           halo=options.n_halo,
                           boundary_conditions=bcs)
    stepper = Stepper(options=options, grid=grid, non_unit_g_factor=False)
    mpdata = Solver(stepper=stepper, advectee=advectee, advector=advector)
    sut = mpdata
示例#22
0
    def __init__(self,
                 *,
                 advectees,
                 stream_function,
                 rhod_of_zZ,
                 dt,
                 grid,
                 size,
                 displacement,
                 n_iters=2,
                 infinite_gauge=True,
                 nonoscillatory=True,
                 third_order_terms=False):
        self.grid = grid
        self.size = size
        self.dt = dt
        self.stream_function = stream_function
        self.stream_function_time_dependent = (
            "t" in inspect.signature(stream_function).parameters)
        self.asynchronous = False
        self.thread: (Thread, None) = None
        self.displacement = displacement
        self.t = 0

        options = Options(
            n_iters=n_iters,
            infinite_gauge=infinite_gauge,
            nonoscillatory=nonoscillatory,
            third_order_terms=third_order_terms,
        )
        disable_threads_if_needed = {}
        if not conf.JIT_FLAGS["parallel"]:
            disable_threads_if_needed["n_threads"] = 1

        stepper = Stepper(options=options,
                          grid=self.grid,
                          non_unit_g_factor=True,
                          **disable_threads_if_needed)

        advector_impl = VectorField(
            (
                np.full((grid[0] + 1, grid[1]), np.nan),
                np.full((grid[0], grid[1] + 1), np.nan),
            ),
            halo=options.n_halo,
            boundary_conditions=(Periodic(), Periodic()),
        )

        g_factor = make_rhod(self.grid, rhod_of_zZ)
        g_factor_impl = ScalarField(
            g_factor.astype(dtype=options.dtype),
            halo=options.n_halo,
            boundary_conditions=(Periodic(), Periodic()),
        )

        self.g_factor_vec = (
            rhod_of_zZ(zZ=x_vec_coord(self.grid)[-1]),
            rhod_of_zZ(zZ=z_vec_coord(self.grid)[-1]),
        )
        self.mpdatas = {}
        for k, v in advectees.items():
            advectee_impl = ScalarField(
                np.asarray(v, dtype=options.dtype),
                halo=options.n_halo,
                boundary_conditions=(Periodic(), Periodic()),
            )
            self.mpdatas[k] = Solver(
                stepper=stepper,
                advectee=advectee_impl,
                advector=advector_impl,
                g_factor=g_factor_impl,
            )
    delta_y = (yrange[1] - yrange[0]) / gridsize[1]
    for i in range(gridsize[0]):
        for j in range(gridsize[1]):
            psi[i, j] = pdf(xrange[0] + delta_x * (i + .5),
                            yrange[0] + delta_y * (j + .5))
    coord_x = np.linspace(xrange[0] + delta_x / 2, xrange[1] - delta_x / 2,
                          gridsize[0])
    coord_y = np.linspace(yrange[0] + delta_y / 2, yrange[1] - delta_y / 2,
                          gridsize[1])
    return coord_x, coord_y, psi


@pytest.mark.parametrize(
    "options",
    [
        Options(n_iters=1),
        Options(n_iters=2),
        Options(n_iters=3, infinite_gauge=True),
        Options(n_iters=2, infinite_gauge=True, nonoscillatory=True),
        # Options(n_iters=3, infinite_gauge=False, third_order_terms=True),
        # Options(n_iters=3, infinite_gauge=True, third_order_terms=True, nonoscillatory=True),
    ])
@pytest.mark.parametrize("grid_static_str", ("static", "dynamic"))
# pylint: disable-next=redefined-outer-name
def test_timing_2d(benchmark,
                   options,
                   grid_static_str,
                   num_threads,
                   plot=False):
    if grid_static_str == "static":
        grid_static = True
# pylint: disable=missing-module-docstring,missing-class-docstring,missing-function-docstring
import pytest
import numpy as np
from PyMPDATA_examples.Smolarkiewicz_1984 import Settings, Simulation
from PyMPDATA import Options

# https://github.com/igfuw/libmpdataxx/blob/master/tests/paper_2015_GMD/4_revolving_sphere_3d/...
STATS = {
    # ...refdata/stats_upwind.txt.gz
    Options(n_iters=1): {
        0: {
            'min(solution)': 0.00000000,
            'max(solution)': 4.00000000,
        },
        566: {
            'max(solution)': 1.72131033,
            'min(solution)': 0.00000000,
            'Linf': 3.38441916,
            'L2': 0.00567238,
            'L1': 0.00128141,
        }
    },
    # ...refdata/stats_basic.txt.gz
    Options(n_iters=2): {
        0: {
            'min(solution)': 0.00000000,
            'max(solution)': 4.00000000,
        },
        556: {
            'max(solution)': 4.94170863,
            'min(solution)': 0.00000000,