def test_calibration_fit_from_points(name, args):
    """Test Calibration.fit and from_points methods."""
    # test fit()
    cal1 = make_calibration(name, args)
    cal1.add_points(points_x, points_y)
    cal1.fit()
    # alternate: call fit_points() instead of add_points() and fit()
    cal1.fit_points(points_x, points_y)

    # skip any instances that require a factory method
    if len(args) != 2:
        cal2 = None
    else:
        # test from_points()
        cal2 = Calibration.from_points(args[0], points_x, points_y)
        cal2 = Calibration.from_points(args[0], points_x, points_y, params0=args[1])
        assert cal2 == cal1

    # test fit() with weights
    cal1 = make_calibration(name, args)
    cal1.add_points(points_x, points_y, weights)
    cal1.fit()
    # alternate: call fit_points() with weights
    cal1.fit_points(points_x, points_y, weights)

    # skip any instances that require a factory method
    if len(args) != 2:
        cal2 = None
    else:
        # test from_points() with weights
        cal2 = Calibration.from_points(args[0], points_x, points_y, weights)
        cal2 = Calibration.from_points(
            args[0], points_x, points_y, weights, params0=args[1]
        )
        assert cal2 == cal1

    plt.figure()
    plt.title(cal1.expression)
    x_fine1 = np.linspace(0, 3000, num=500)
    plt.plot(
        x_fine1,
        cal1(x_fine1),
        "b-",
        lw=2,
        alpha=0.5,
        label="fitted function",
    )
    plt.plot(points_x, points_y, "ro", label="calibration points")
    plt.xlabel("x")
    plt.xlabel("y")
    plt.xlim(0)
    plt.ylim(0)
    plt.legend()
    plt.savefig(os.path.join(TEST_OUTPUTS, f"calibration__fit__{name}.png"))

    # Test statistics
    assert len(cal1.fit_y) > 0
    assert cal1.fit_R_squared > 0.8  # note: this is flexible
    assert 0 <= cal1.fit_reduced_chi_squared <= 200  # note: this is flexible
    cal1.plot()
def make_calibration(name, args):
    """Make an instance of the desired Calibration type."""
    attrs = {"comment": "Test of Calibration class", "name": name}
    if name.startswith("lin"):
        cal = Calibration.from_linear(*args, **attrs, domain=domain)
    elif name.startswith("poly"):
        cal = Calibration.from_polynomial(*args, **attrs, domain=domain)
    elif name.startswith("sqrt"):
        cal = Calibration.from_sqrt_polynomial(*args, **attrs, domain=domain)
    elif name.startswith("interp"):
        cal = Calibration.from_interpolation(points_x, points_y, **attrs, domain=domain)
    else:
        cal = Calibration(*args, **attrs, domain=domain)
    return cal
Example #3
0
def test_calibration_read_failures():
    """Test miscellaneous HDF5 reading failures."""
    fname = os.path.join(TEST_OUTPUTS, "calibration__read_failures.h5")
    cal = Calibration.from_linear([2.0, 3.0])
    cal.add_points([0, 1000, 2000], [0, 1000, 2000])

    # remove datasets from the file and show that read raises an error
    for dset_name in ["params", "expression", "domain", "range"]:
        cal.write(fname)
        with h5.open_h5(fname, "r+") as f:
            del f[dset_name]
        with pytest.raises(CalibrationError):
            Calibration.read(fname)

    # remove datasets from the file and show that read does *not* raise error
    for dset_name in ["points_x", "points_y"]:
        cal.write(fname)
        with h5.open_h5(fname, "r+") as f:
            del f[dset_name]
        Calibration.read(fname)

    # add unexpected dataset to the file and show that read raises an error
    cal.write(fname)
    with h5.open_h5(fname, "r+") as f:
        f.create_dataset("unexpected", data=[0, 1, 2])
    with pytest.raises(CalibrationError):
        Calibration.read(fname)
Example #4
0
def test_calibration_set_add_points(name, args):
    """Test Calibration.set_points and add_points methods."""
    fname = os.path.join(TEST_OUTPUTS, f"calibration__add_points__{name}.h5")
    cal = make_calibration(name, args)
    # test set_points
    cal.set_points()
    cal.set_points(1000, 1000)
    cal.set_points((0, 1000), (0, 1000))
    cal.set_points([], [])
    # test add_points
    cal.add_points()  # does nothing
    for px, py in [[(), ()], [1000, 1000], [(0, 1000), (0, 1000)]]:
        cal.add_points(px, py)
    # test __str__() and __repr__()
    str(cal)
    repr(cal)
    # test write()
    cal.write(fname)
    # test read()
    cal2 = Calibration.read(fname)
    # test __eq__()
    assert cal2 == cal
    # points_x is not 1D
    with pytest.raises(CalibrationError):
        points_1d = np.array([0, 1000, 2000])
        points_2d = np.reshape(points_1d, (1, 3))
        cal.add_points(points_2d, points_1d)
    # points_y is not 1D
    with pytest.raises(CalibrationError):
        points_1d = np.array([0, 1000, 2000])
        points_2d = np.reshape(points_1d, (1, 3))
        cal.add_points(points_1d, points_2d)
    # points have different lengths
    with pytest.raises(CalibrationError):
        points_1d = np.array([0, 1000, 2000])
        points_2d = np.reshape(points_1d, (1, 3))
        cal.add_points([0, 1000, 2000], [0, 2000])
    # points_x contains negative values
    with pytest.raises(CalibrationError):
        cal.add_points([0, -2000], [0, 2000])
    # points_y contains negative values
    with pytest.raises(CalibrationError):
        cal.add_points([0, 2000], [0, -2000])
Example #5
0
def test_calibration(name, args):
    """Test the Calibration class."""
    fname = os.path.join(TEST_OUTPUTS, f"calibration__init__{name}.h5")
    # test __init__()
    cal = make_calibration(name, args)
    # test protections on setting parameters
    with pytest.raises(CalibrationError):
        cal.params = None
    with pytest.raises(CalibrationError):
        cal.params = np.ones((1, 2))
    # test write()
    cal.write(fname)
    # test read()
    cal2 = Calibration.read(fname)
    # test __eq__()
    assert cal2 == cal
    # test copy()
    cal3 = cal.copy()
    assert cal3 == cal
    # test __call__()
    cal(100.0)
    str(cal)
    repr(cal)
Example #6
0
def test_calibration_interpolation():
    """Test Calibration.from_interpolation."""
    Calibration.from_interpolation(points_x[:2], points_y[:2])
    with pytest.raises(CalibrationError):
        Calibration.from_interpolation(points_x[:1], points_y[:1])
Example #7
0
def test_calibration_misc():
    """Miscellaneous tests to increase test coverage."""
    cal1 = Calibration.from_linear([2.0, 3.0])
    cal2 = Calibration.from_polynomial([2.0, 3.0, 7.0])
    with pytest.raises(CalibrationError):
        cal1 != 0
    assert cal1 != cal2

    # bad number of arguments
    with pytest.raises(CalibrationError):
        Calibration.from_linear([2.0])
    Calibration.from_linear([2.0, 3.0])
    with pytest.raises(CalibrationError):
        Calibration.from_linear([2.0, 3.0, 4.0])

    # bad number of arguments
    with pytest.raises(CalibrationError):
        Calibration.from_polynomial([2.0])
    Calibration.from_polynomial([2.0, 3.0])
    Calibration.from_polynomial([2.0, 3.0, 4.0])

    # bad number of arguments
    with pytest.raises(CalibrationError):
        Calibration.from_sqrt_polynomial([2.0])
    Calibration.from_sqrt_polynomial([2.0, 3.0])
    Calibration.from_sqrt_polynomial([2.0, 3.0, 4.0])
Example #8
0
def test_calibration_inverse():
    """Test calibrations with and without inverse expression."""
    fname = os.path.join(TEST_OUTPUTS, "calibration__inverse.h5")

    # cal1 has an explicit inverse expression, cal2 does not
    cal1 = Calibration("p[0] + p[1] * x", [5.0, 4.0],
                       inv_expression="(y - p[0]) / p[1]")
    cal2 = Calibration(cal1.expression, [5.0, 4.0])
    assert cal1 == cal2

    # evaluate the inverse for a scalar
    y = 100.0
    x1 = cal1.inverse(y)
    x2 = cal2.inverse(y)
    assert np.isclose(x1, (y - 5.0) / 4.0)
    assert np.isclose(x1, x2)

    # evaluate the inverse for a scalar with initial guess
    x1 = cal1.inverse(y, x0=25.0)
    x2 = cal2.inverse(y, x0=25.0)
    assert np.isclose(x1, (y - 5.0) / 4.0)
    assert np.isclose(x1, x2)

    # evaluate the inverse for an array
    y = np.linspace(20.0, 500.0, num=100)
    x1 = cal1.inverse(y)
    x2 = cal2.inverse(y)
    assert np.allclose(x1, (y - 5.0) / 4.0)
    assert np.allclose(x1, x2)

    # evaluate the inverse for an array with initial guesses
    y = np.linspace(20.0, 500.0, num=100)
    x0 = np.arange(len(y)) / 4.0
    x1 = cal1.inverse(y, x0=x0)
    x2 = cal2.inverse(y, x0=x0)
    assert np.allclose(x1, (y - 5.0) / 4.0)
    assert np.allclose(x1, x2)

    # evaluate inverse for values inside the range with result inside domain
    cal1.domain = [0, 1]
    cal1.range = [-1e6, 1e6]
    x0 = 0.5
    y0 = cal1(x0)
    cal1.range = [y0 - 1, y0 + 1]
    cal1.inverse(y0)

    # evaluate inverse for values inside the range with result outside domain
    cal1.domain = [0, 2]
    cal1.range = [-1e6, 1e6]
    x0 = 2.0
    y0 = cal1(x0)
    cal1.domain = [0, 1]
    cal1.range = [y0 - 1, y0 + 1]
    with pytest.raises(CalibrationError):
        cal1(x0)
    with pytest.raises(CalibrationError):
        cal1.inverse(y0)

    # evaluate the inverse for a value outside the range and the domain
    cal1.domain = [0, 2]
    cal1.range = [-1e6, 1e6]
    x0 = 2.0
    y0 = cal1(x0)
    cal1.domain = [x0 - 2, x0 - 1]
    cal1.range = [y0 - 2, y0 - 1]
    with pytest.raises(CalibrationError):
        cal1(x0)
    with pytest.raises(CalibrationError):
        cal1.inverse(y0)

    # test __str__() and __repr__()
    str(cal1)
    repr(cal1)

    # test write() and read()
    cal1.write(fname)
    cal3 = Calibration.read(fname)
    assert cal3.inv_expression is not None
    assert cal3.inv_expression == cal1.inv_expression
Example #9
0
def test_calibration_domain_range():
    """Test setting the domain and range of a calibration."""
    cal = Calibration("p[0] + p[1] * x", [5.0, 4.0])

    # set the domain and range to defaults
    cal.domain = None
    cal.range = None

    # cannot set domain or range to infinite values
    with pytest.raises(CalibrationError):
        cal.domain = [-np.inf, np.inf]
    with pytest.raises(CalibrationError):
        cal.range = [-np.inf, np.inf]

    # set the domain and range to specific values
    x0 = 1
    cal.domain = [x0 - 1, x0 + 1]
    cal.range = [-1e6, 1e6]

    # evaluate for x inside domain and result inside range
    y0 = cal(x0)

    # evaluate for x outside domain (fails)
    x1 = cal.domain[1] + 3
    with pytest.raises(CalibrationError):
        cal(x1)

    # expand domain and reevaluate
    cal.domain = (cal.domain[0], x1 + 1)
    cal(x1)

    # evaluate for x inside domain and result outside range (warns)
    cal.domain = [x0 - 1, x0 + 1]
    cal.range = [y0 + 10, y0 + 20]
    with pytest.warns(CalibrationWarning):
        y2 = cal(x0)
    # y value is not clipped to the range so should be the same as before
    assert np.isclose(y0, y2)

    # expand range and reevaluate
    cal.range = [y0 - 1, y0 + 1]
    cal(x0)