예제 #1
0
def test_init():
    grid = _test_grid("electrostatic_gaussian_sphere", num=50)

    # Cartesian
    source = (0 * u.mm, -10 * u.mm, 0 * u.mm)
    detector = (0 * u.mm, 200 * u.mm, 0 * u.mm)

    sim = cpr.Tracker(grid, source, detector, verbose=False)

    # Test manually setting hdir and vdir
    hdir = np.array([1, 0, 1])
    sim = cpr.Tracker(grid,
                      source,
                      detector,
                      verbose=False,
                      detector_hdir=hdir)

    # Test special case hdir == [0,0,1]
    source = (0 * u.mm, 0 * u.mm, -10 * u.mm)
    detector = (0 * u.mm, 0 * u.mm, 200 * u.mm)
    sim = cpr.Tracker(grid, source, detector, verbose=False)
    assert all(sim.det_hdir == np.array([1, 0, 0]))

    # Test that hdir is calculated correctly if src-det axis is anti-parallel to z
    source = (0 * u.mm, 0 * u.mm, 10 * u.mm)
    detector = (0 * u.mm, 0 * u.mm, -200 * u.mm)
    sim = cpr.Tracker(grid, source, detector, verbose=False)
    assert all(sim.det_hdir == np.array([1, 0, 0]))
예제 #2
0
def test_coordinate_systems():
    """
    Check that specifying the same point in different coordinate systems
    ends up with identical source and detector vectors.
    """

    grid = _test_grid("empty")

    # Cartesian
    source = (-7.07 * u.mm, -7.07 * u.mm, 0 * u.mm)
    detector = (70.07 * u.mm, 70.07 * u.mm, 0 * u.mm)
    sim1 = cpr.Tracker(grid, source, detector, verbose=True)

    # Cylindrical
    source = (-1 * u.cm, 45 * u.deg, 0 * u.mm)
    detector = (10 * u.cm, 45 * u.deg, 0 * u.mm)
    sim2 = cpr.Tracker(grid, source, detector, verbose=False)

    # In spherical
    source = (-0.01 * u.m, 90 * u.deg, 45 * u.deg)
    detector = (0.1 * u.m, 90 * u.deg, 45 * u.deg)
    sim3 = cpr.Tracker(grid, source, detector, verbose=False)

    assert np.allclose(sim1.source, sim2.source, atol=1e-2)
    assert np.allclose(sim2.source, sim3.source, atol=1e-2)
    assert np.allclose(sim1.detector, sim2.detector, atol=1e-2)
    assert np.allclose(sim2.detector, sim3.detector, atol=1e-2)
예제 #3
0
def test_run_options():
    grid = _test_grid("electrostatic_gaussian_sphere", num=50)

    # Cartesian
    source = (0 * u.mm, -10 * u.mm, 0 * u.mm)
    detector = (0 * u.mm, 200 * u.mm, 0 * u.mm)
    sim = cpr.Tracker(grid, source, detector, verbose=True)

    # Test that trying to call run() without creating particles
    # raises an exception
    with pytest.raises(ValueError):
        sim.run()

    sim = cpr.Tracker(grid, source, detector, verbose=True)
    sim.create_particles(1e4, 3 * u.MeV, max_theta=10 * u.deg)

    # Try running with nearest neighbor interpolator
    # Test manually setting a timestep
    sim.run(field_weighting="nearest neighbor", dt=1e-12 * u.s)

    # Test max_deflections
    sim.max_deflection

    # Test way too big of a max_theta
    sim = cpr.Tracker(grid, source, detector, verbose=True)
    sim.create_particles(1e4, 3 * u.MeV, max_theta=89 * u.deg)
    with pytest.warns(RuntimeWarning,
                      match="of "
                      "particles entered the field grid"):
        sim.run(field_weighting="nearest neighbor", dt=1e-12 * u.s)

    # Test extreme deflections -> warns user
    # This requires instantiating a whole new example field with a really
    # big B-field
    grid = _test_grid("constant_bz", num=50, B0=250 * u.T)
    source = (0 * u.mm, -10 * u.mm, 0 * u.mm)
    detector = (0 * u.mm, 200 * u.mm, 0 * u.mm)

    # Expect warnings because these fields aren't well-behaved at the edges
    with pytest.warns(
            RuntimeWarning,
            match="Fields should go to zero at edges of grid to avoid "):
        sim = cpr.Tracker(grid, source, detector, verbose=False)
    sim.create_particles(1e4, 3 * u.MeV, max_theta=0.1 * u.deg)
    with pytest.warns(
            RuntimeWarning,
            match="particles have been "
            "deflected away from the detector plane",
    ):
        sim.run(field_weighting="nearest neighbor", dt=1e-12 * u.s)
    # Calc max deflection: should be between 0 and pi/2
    # Note: that's only true because max_theta is very small
    # More generally, max_deflection can be a bit bigger than pi/2 for
    # particles that begin at an angle then deflect all the way around.
    assert 0 < sim.max_deflection.to(u.rad).value < np.pi / 2
예제 #4
0
def test_load_particles():

    grid = _test_grid("electrostatic_gaussian_sphere", num=50)

    # Cartesian
    source = (0 * u.mm, -10 * u.mm, 0 * u.mm)
    detector = (0 * u.mm, 200 * u.mm, 0 * u.mm)

    sim = cpr.Tracker(grid, source, detector, verbose=False)
    sim.create_particles(1e3,
                         15 * u.MeV,
                         max_theta=0.1 * u.rad,
                         distribution="uniform")

    # Test adding unequal numbers of particles
    x = np.zeros([100, 3]) * u.m
    v = np.ones([150, 3]) * u.m / u.s
    with pytest.raises(ValueError):
        sim.load_particles(x, v)

    # Test creating particles with explicit keywords
    x = sim.x * u.m
    v = sim.v * u.m / u.s

    # Try setting particles going the wrong direction
    with pytest.warns(RuntimeWarning):
        sim.load_particles(x, -v)

    # Try specifying a larger ion (not a proton or electron)
    sim.load_particles(x, v, particle="C-12 +3")

    # Run the tracker to make sure everything works
    sim.run(field_weighting="nearest neighbor")
예제 #5
0
def run_1D_example(name):
    """
    Run a simulation through an example with parameters optimized to
    sum up to a lineout along x. The goal is to run a relatively fast
    sim with a quasi-1D field grid that can then be summed to get good
    enough statistics to use as a test.
    """
    grid = _test_grid(name, L=1 * u.mm, num=50)

    # Cartesian
    source = (0 * u.mm, -10 * u.mm, 0 * u.mm)
    detector = (0 * u.mm, 200 * u.mm, 0 * u.mm)

    # Expect warnings because these fields aren't well-behaved at the edges
    with pytest.warns(
            RuntimeWarning,
            match="Fields should go to zero at edges of grid to avoid "):
        sim = cpr.Tracker(grid, source, detector, verbose=False)
    sim.create_particles(1e4, 3 * u.MeV, max_theta=0.1 * u.deg)
    sim.run()

    size = np.array([[-1, 1], [-1, 1]]) * 10 * u.cm
    bins = [200, 60]
    hax, vax, values = cpr.synthetic_radiograph(sim, size=size, bins=bins)

    values = np.mean(values[:, 20:40], axis=1)

    return hax, values
예제 #6
0
def create_tracker_obj():
    # CREATE A RADIOGRAPH OBJECT
    grid = _test_grid("electrostatic_gaussian_sphere", num=50)
    source = (0 * u.mm, -10 * u.mm, 0 * u.mm)
    detector = (0 * u.mm, 200 * u.mm, 0 * u.mm)

    sim = cpr.Tracker(grid, source, detector, verbose=False)
    sim.create_particles(int(1e4), 3 * u.MeV, max_theta=10 * u.deg)
    return sim
예제 #7
0
def test_create_particles():
    grid = _test_grid("electrostatic_gaussian_sphere", num=50)

    # Cartesian
    source = (0 * u.mm, -10 * u.mm, 0 * u.mm)
    detector = (0 * u.mm, 200 * u.mm, 0 * u.mm)

    sim = cpr.Tracker(grid, source, detector, verbose=False)

    sim.create_particles(
        1e3, 15 * u.MeV, max_theta=0.1 * u.rad, distribution="monte-carlo"
    )

    sim.create_particles(1e3, 15 * u.MeV, max_theta=0.1 * u.rad, distribution="uniform")

    # Test specifying particle
    charge = 3 * const.e.si
    mass = const.m_e.si
    sim.create_particles(1e3, 15 * u.MeV, particle="e")
예제 #8
0
def run_mesh_example(
    location=np.array([0, -2, 0]) * u.mm,
    extent=(2 * u.mm, 1.5 * u.mm),
    nwires=9,
    wire_diameter=20 * u.um,
    mesh_hdir=None,
    mesh_vdir=None,
    nparticles=1e4,
    problem="electrostatic_gaussian_sphere",
):
    """
    Takes all of the add_wire_mesh parameters and runs a standard example problem
    simulation using them.

    Returns the sim object for use in additional tests
    """

    grid = _test_grid(problem, num=100)
    source = (0 * u.mm, -10 * u.mm, 0 * u.mm)
    detector = (0 * u.mm, 200 * u.mm, 0 * u.mm)

    sim = cpr.Tracker(grid, source, detector, verbose=False)

    sim.add_wire_mesh(
        location,
        extent,
        nwires,
        wire_diameter,
        mesh_hdir=mesh_hdir,
        mesh_vdir=mesh_vdir,
    )

    sim.create_particles(nparticles, 3 * u.MeV, max_theta=10 * u.deg)
    sim.run(field_weighting="nearest neighbor")

    return sim
예제 #9
0
def test_gaussian_sphere_analytical_comparison():
    """
    This test runs a known example problem and compares to a theoretical
    model for small deflections.

    Still under construction (comparing the actual form of the radiograph
    is possible but tricky to implement).
    """

    # The Gaussian sphere problem for small deflection potentials
    # is solved in Kugland2012relation, and the equations referenced
    # below are from that paper.
    # https://doi.org/10.1063/1.4750234

    a = (1 * u.mm / 3).to(u.mm).value
    phi0 = 1.4e5
    W = 15e6

    l = 10
    L = 200

    # Define and run the problem
    # Setting b to be much larger than the problem so that the field is not
    # cut off at the edges. This is required to be directly
    # comparable to the theoretical result.
    grid = _test_grid(
        "electrostatic_gaussian_sphere",
        num=100,
        phi0=phi0 * u.V,
        a=a * u.mm,
        b=20 * u.mm,
    )
    source = (0 * u.mm, -l * u.mm, 0 * u.mm)
    detector = (0 * u.mm, L * u.mm, 0 * u.mm)

    with pytest.warns(
            RuntimeWarning,
            match="Fields should go to zero at edges of grid to avoid "):
        sim = cpr.Tracker(grid, source, detector, verbose=False)

    sim.create_particles(1e3, W * u.eV, max_theta=12 * u.deg)
    sim.run()

    size = np.array([[-1, 1], [-1, 1]]) * 4 * u.cm
    bins = [100, 100]
    h, v, i = cpr.synthetic_radiograph(sim, size=size, bins=bins)
    h = h.to(u.mm).value / sim.mag
    v = v.to(u.mm).value / sim.mag
    r0 = h

    # Calculate a lineout across the center of the plane (y=0)
    v0 = np.argmin(np.abs(v))

    line = np.mean(i[:, v0 - 6:v0 + 6], axis=1)
    # Zero the edge of the radiograph
    line += -np.mean(line)
    line *= 1 / np.max(np.abs(line))

    # Calculate the theoretical deflection angles (Eq. 28)
    theory = phi0 / W * np.sqrt(np.pi) * (r0 / a) * np.exp(-((r0 / a)**2))

    max_deflection = np.max(np.abs(theory))
    mu = np.sqrt(np.pi) * (phi0 / W) * (l / a)

    # sim_mu = sim.max_deflection.to(u.rad).value*(l/a)

    # Calculate the theoretical inversion (Eq. 31 )
    theory_deflect = -2 * mu * (1 - (r0 / a)**2) * np.exp(-((r0 / a)**2))
    theory_deflect *= 1 / np.max(np.abs(theory_deflect))

    # Uncomment for debug
    """
    print(f"Theory max deflection: {max_deflection:.6f}")
    print(f"Theory mu: {mu:.3f}")
    print(f"Sim max deflection: {sim.max_deflection.to(u.rad).value:.6f}")
    print(f"Sim mu: {sim_mu:.3f}")

    import matplotlib.pyplot as plt
    print(f"Theory max deflection: {max_deflection:.6f}")
    print(f"Theory mu: {mu:.3f}")
    print(f"Sim max deflection: {sim.max_deflection.to(u.rad).value:.6f}")
    print(f"Sim mu: {sim_mu:.3f}")

    fig, ax = plt.subplots()
    ax.pcolormesh(h, v, i.T, shading='auto', cmap='Blues_r')
    ax.set_aspect('equal')

    fig, ax = plt.subplots()
    ax.plot(h, line )
    ax.plot(h, theory_deflect)
    """

    assert np.isclose(max_deflection,
                      sim.max_deflection.to(u.rad).value,
                      atol=1e-3)
예제 #10
0
def test_input_validation():
    """
    Intentionally raise a number of errors.
    """

    # ************************************************************************
    # During initialization
    # ************************************************************************

    grid = _test_grid("electrostatic_gaussian_sphere")
    source = (-10 * u.mm, 90 * u.deg, 45 * u.deg)
    detector = (100 * u.mm, 90 * u.deg, 45 * u.deg)

    # Check that an error is raised when an input grid has a nan or infty value
    # First check NaN
    Ex = grid["E_x"]
    Ex[0, 0, 0] = np.nan * u.V / u.m
    grid.add_quantities(E_x=Ex)
    with pytest.raises(ValueError):
        sim = cpr.Tracker(grid, source, detector, verbose=False)
    Ex[0, 0, 0] = 0 * u.V / u.m

    Ex[0, 0, 0] = np.inf * u.V / u.m  # Reset element for the rest of the tests
    grid.add_quantities(E_x=Ex)
    with pytest.raises(ValueError):
        sim = cpr.Tracker(grid, source, detector, verbose=False)
    Ex[0, 0, 0] = 0 * u.V / u.m

    # Check what happens if a value is large relative to the rest of the array
    Ex[0, 0, 0] = 0.5 * np.max(Ex)
    grid.add_quantities(E_x=Ex)
    # with pytest.raises(ValueError):
    with pytest.warns(RuntimeWarning):
        sim = cpr.Tracker(grid, source, detector, verbose=False)
    Ex[0, 0, 0] = 0 * u.V / u.m

    # Raise error when source-to-detector vector doesn't pass through the
    # field grid
    source_bad = (10 * u.mm, -10 * u.mm, 0 * u.mm)
    detector_bad = (10 * u.mm, 100 * u.mm, 0 * u.mm)
    with pytest.raises(ValueError):
        sim = cpr.Tracker(grid, source_bad, detector_bad, verbose=False)

    # Test raises warning when one (or more) of the required fields is missing
    grid_bad = CartesianGrid(-1 * u.mm, 1 * u.mm, num=50)
    with pytest.warns(RuntimeWarning,
                      match="is not specified for the provided grid."):
        sim = cpr.Tracker(grid_bad, source, detector, verbose=True)

    # ************************************************************************
    # During create_particles
    # ************************************************************************
    sim = cpr.Tracker(grid, source, detector, verbose=False)
    sim.create_particles(1e3, 15 * u.MeV, max_theta=0.99 * np.pi / 2 * u.rad)

    # ************************************************************************
    # During runtime
    # ************************************************************************

    sim = cpr.Tracker(grid, source, detector, verbose=False)
    sim.create_particles(1e3, 15 * u.MeV)

    # Test an invalid field weighting keyword
    with pytest.raises(ValueError):
        sim.run(field_weighting="not a valid field weighting")

    # ************************************************************************
    # During runtime
    # ************************************************************************
    # SYNTHETIC RADIOGRAPH ERRORS
    sim.run()

    # Choose a very small synthetic radiograph size that misses most of the
    # particles
    with pytest.warns(
            RuntimeWarning,
            match="of the particles are shown on this synthetic radiograph."):
        size = np.array([[-1, 1], [-1, 1]]) * 1 * u.mm
        hax, vax, values = cpr.synthetic_radiograph(sim, size=size)