def run_sims():
    radius = 0.1
    electron_energy = 1000.0
    I = 1e4
    batch_num = 1
    dI_dt = 0.0
    to_kA = 1e-3

    # Generate Polywell field
    loop_pts = 200
    domain_pts = 130
    loop_offset = 1.25
    dom_size = 1.1 * loop_offset * radius
    file_name = "b_field_{}_{}_{}_{}_{}_{}".format(I * to_kA, radius,
                                                   loop_offset, domain_pts,
                                                   loop_pts, dom_size)
    file_path = os.path.join("..", "mesh_generation", "data",
                             "radius-{}m".format(radius),
                             "current-{}kA".format(I * to_kA),
                             "domres-{}".format(domain_pts), file_name)
    b_field = InterpolatedBField(file_path, dom_pts_idx=6, dom_size_idx=8)

    seed = batch_num
    np.random.seed(seed)

    # Run simulations
    vel = np.sqrt(2.0 * electron_energy * PhysicalConstants.electron_charge /
                  PhysicalConstants.electron_mass)
    num_sims = 1
    for i in range(num_sims):
        # Define particle velocity
        z_unit = np.random.uniform(-1.0, 1.0)
        xy_plane = np.sqrt(1 - z_unit**2)
        phi = np.random.uniform(0.0, 2 * np.pi)
        velocity = np.asarray(
            [xy_plane * np.cos(phi), xy_plane * np.sin(phi), z_unit]) * vel

        # Generate particle position
        z_unit = np.random.uniform(-1.0, 1.0)
        xy_plane = np.sqrt(1 - z_unit**2)
        phi = np.random.uniform(0.0, 2 * np.pi)
        position = np.asarray([
            xy_plane * np.cos(phi), xy_plane * np.sin(phi), z_unit
        ]) * np.random.uniform(0.0, 1.0 * radius / 16.0)

        # Generate particle
        particle = PICParticle(9.1e-31, 1.6e-19, position, velocity)

        t, x, y, z, v_x, v_y, v_z, final_idx = run_simulation(
            (b_field, particle, radius, loop_offset * radius, I, dI_dt))
def E_field_example():
    """
    Example E field solution

    :return:
    """
    particle = PICParticle(1.6e-27, 1.6e-19, np.asarray([0.1, 0.0, 0.0]),
                           np.asarray([0.0, 0.1, 0.0]))
    field = PointField(-1.6e-19, 0.1, np.zeros(3))

    def B_field(x):
        B = np.zeros(x.shape)
        return B

    X = particle.position
    V = particle.velocity
    Q = np.asarray([particle.charge])
    M = np.asarray([particle.mass])

    times = np.linspace(0.0, 1, 10000)
    positions = np.zeros((times.shape[0], X.shape[0], X.shape[1]))
    velocities = np.zeros((times.shape[0], X.shape[0], X.shape[1]))
    for i, t in enumerate(times):
        if i == 0:
            positions[i, :, :] = X
            velocities[i, :, :] = V
            continue

        dt = times[i] - times[i - 1]

        x, v = boris_solver(field.e_field, B_field, X, V, Q, M, dt)

        positions[i, :, :] = x
        velocities[i, :, :] = v
        X = x
        V = v

    x = positions[:, :, 0].flatten()
    y = positions[:, :, 1].flatten()
    z = positions[:, :, 2].flatten()
    fig = plt.figure(figsize=(20, 10))
    ax = fig.add_subplot('111', projection='3d')
    ax.plot(x, y, z, label='numerical')
    ax.set_xlabel('X')
    ax.set_ylabel('Y')
    ax.set_zlabel('Z')
    ax.legend(loc='best')
    ax.set_title("Analytic and Numerical Particle Motion")
    plt.show()
def E_field_example():
    """
    Example E field solution
    :return:
    """
    particle = PICParticle(1.0, 2.0, np.asarray([1.0, 0.0, 0.0]),
                           np.asarray([0.0, 1.0, 0.0]))
    E = np.asarray([0.0, 0.0, 3.0])

    times, positions = solve_E_field(particle, E, 3.0)

    fig = plt.figure()
    ax = fig.add_subplot('111', projection='3d')
    ax.plot(positions[:, 0], positions[:, 1], positions[:, 2])
    plt.show()
def aligned_fields_examples():
    """
    Example of parallel fields solution
    :return:
    """
    particle = PICParticle(1.0, 2.0, np.asarray([1.0, 0.0, 0.0]),
                           np.asarray([0.0, 1.0, 0.0]))
    E = np.asarray([0.0, 0.0, 3.0])
    B = np.asarray([0.0, 0.0, 3.0])

    times, positions = solve_aligned_fields(particle, E, B, 3.0)

    fig = plt.figure()
    ax = fig.add_subplot('111', projection='3d')
    ax.plot(positions[:, 0], positions[:, 1], positions[:, 2])
    plt.show()
    t_results = []
    x_results = []
    y_results = []
    z_results = []
    dt_factors = [100.0, 10.0, 1.0, 0.1, 0.01]
    electron_energy = 1000.0
    max_vel = np.sqrt(2.0 * electron_energy *
                      PhysicalConstants.electron_charge /
                      PhysicalConstants.electron_mass)
    for dt in dt_factors:
        seed = 1
        np.random.seed(seed)
        # Define charge particle
        vel = np.random.uniform(low=-1.0, high=1.0, size=(3, )) * max_vel
        particle = PICParticle(9.1e-31, 1.6e-19, np.asarray([0.0, 0.0, 0.0]),
                               vel)

        t, x, y, z = run_sim(b_field, particle, dt)
        t_results.append(t)
        x_results.append(x)
        y_results.append(y)
        z_results.append(z)

    fig, ax = plt.subplots(4)

    x_interp = scipy.interpolate.interp1d(t_results[-1], x_results[-1])
    y_interp = scipy.interpolate.interp1d(t_results[-1], y_results[-1])
    z_interp = scipy.interpolate.interp1d(t_results[-1], z_results[-1])
    for i in range(len(dt_factors) - 1):
        dx = np.abs(x_interp(t_results[i]) - x_results[i])
        dy = np.abs(y_interp(t_results[i]) - y_results[i])
def run_1d_electrostatic_well(radius, num_particles=int(1e4)):
    """
    Function to run simulation - for this simulation, in a spherically symmetric potential well

    radius: size of potential well
    """
    # Define particles
    number_density = 1e12
    charge = 1.602e-19
    weight = int(number_density / num_particles)
    pic_particle = PICParticle(3.344496935079999e-27 * weight, charge * weight,
                               np.asarray([radius, 0.0, 0.0]),
                               np.asarray([0.0, 0.0, 0.0]))
    collision_particle = ChargedParticle(3.344496935079999e-27 * weight,
                                         charge * weight)

    # Define fields
    electron_charge_density = 1e20 * PhysicalConstants.electron_charge

    def e_field(x):
        E_field = np.zeros(x.shape)
        for i in range(x.shape[0]):
            r = magnitude(x[i])
            if r <= radius:
                e_field = electron_charge_density * r / (
                    3.0 * PhysicalConstants.epsilon_0)
            else:
                e_field = electron_charge_density * radius**3 / (
                    3.0 * PhysicalConstants.epsilon_0 * r**2)

            e_field *= -normalise(x[i])
            E_field[i, :] = e_field

        return E_field

    # Define time step and final time
    total_V = radius**3 * electron_charge_density
    total_V /= 2.0 * PhysicalConstants.epsilon_0 * radius
    energy = total_V * pic_particle.charge
    velocity = np.sqrt(2 * energy / pic_particle.mass)
    dt = 0.01 * radius / velocity
    final_time = 50.0 * radius / velocity
    num_steps = int(final_time / dt)

    # Define collision model
    temperature = 298.3
    thermal_velocity = np.sqrt(PhysicalConstants.boltzmann_constant *
                               temperature / pic_particle.mass)
    c = CoulombCollision(collision_particle, collision_particle, 1.0, velocity)
    debye_length = PhysicalConstants.epsilon_0 * temperature
    debye_length /= number_density * PhysicalConstants.electron_charge**2
    debye_length = np.sqrt(debye_length)
    coulomb_logarithm = np.log(debye_length / c.b_90)
    r = RelaxationProcess(c)
    v_K = r.kinetic_loss_stationary_frequency(number_density, temperature,
                                              velocity)
    print("Thermal velocity: {}".format(thermal_velocity / 3e8))
    print("Peak velocity: {}".format(velocity / 3e8))
    print("Debye Length: {}".format(debye_length))
    print("Coulomb Logarithm: {}".format(coulomb_logarithm))
    print("Kinetic Loss Time: {}".format(1.0 / v_K))
    print("Simulation Time Step: {}".format(dt))
    assert dt < 0.01 * 1.0 / v_K
    collision_model = AbeCoulombCollisionModel(
        num_particles,
        collision_particle,
        weight,
        coulomb_logarithm=coulomb_logarithm)

    # Set up initial conditions
    np.random.seed(1)
    X = np.zeros((num_particles, 3))
    for i in range(num_particles):
        x_theta = np.random.uniform(0.0, 2 * np.pi)
        y_theta = np.random.uniform(0.0, 2 * np.pi)
        z_theta = np.random.uniform(0.0, 2 * np.pi)

        X[i, :] = rotate_3d(np.asarray([1.0, 0.0, 0.0]),
                            np.asarray([x_theta, y_theta, z_theta]))
    V = np.random.normal(0.0, thermal_velocity, size=X.shape)
    V = np.zeros(X.shape)

    # Run simulation
    times = np.linspace(0.0, dt * num_steps, num_steps)
    positions = np.zeros((times.shape[0], X.shape[0], X.shape[1]))
    velocities = np.zeros((times.shape[0], X.shape[0], X.shape[1]))
    for i, t in enumerate(times):
        # Set first position and velocity
        if i == 0:
            positions[i, :, :] = X
            velocities[i, :, :] = V
            continue

        # Update position and velocity due to field
        dt = times[i] - times[i - 1]
        x = X + V + 0.5 * dt if i == 1 else X + V * dt
        E = e_field(x)
        v = V + E * pic_particle.charge / pic_particle.mass * dt

        # Update velocity due to collisions
        new_v = collision_model.single_time_step(v, dt)

        positions[i, :, :] = x
        velocities[i, :, :] = new_v
        X = x
        V = new_v

        print("Timestep: {}".format(i))

    return times, positions, velocities
def run_parallel_sims(params):
    radius, electron_energy, I, n, batch_num = params
    to_kA = 1e-3
    use_cartesian_reference_frame = False

    # Get output directory
    res_dir = "results"
    if not os.path.exists(res_dir):
        os.makedirs(res_dir)
    output_dir = os.path.join(res_dir, "radius-{}m".format(radius))
    if not os.path.exists(output_dir):
        os.makedirs(output_dir)
    output_dir = os.path.join(output_dir, "current-{}kA".format(I * to_kA))
    if not os.path.exists(output_dir):
        os.makedirs(output_dir)

    # Get process name
    process_name = "current-{}-radius-{}-energy-{}-n-{:.2E}-batch-{}".format(
        I, radius, electron_energy, n, batch_num)
    print("Starting process: {}".format(process_name))

    # Generate Polywell field
    loop_pts = 200
    domain_pts = 130
    loop_offset = 1.25
    dom_size = 1.1 * loop_offset * 1.0
    file_name = "b_field_{}_{}_{}_{}_{}_{}".format(1.0 * to_kA, 1.0,
                                                   loop_offset, domain_pts,
                                                   loop_pts, dom_size)
    file_path = os.path.join("..", "mesh_generation", "data", "radius-1.0m",
                             "current-0.001kA", "domres-{}".format(domain_pts),
                             file_name)
    b_field = InterpolatedBField(file_path, dom_pts_idx=6, dom_size_idx=8)

    seed = batch_num
    np.random.seed(seed)

    # Run simulations
    num_radial_bins = 200
    num_velocity_bins = 250
    total_particle_position_count = np.zeros((num_radial_bins, ))
    total_particle_velocity_count_x = np.zeros(
        (num_radial_bins, num_velocity_bins - 1))
    total_particle_velocity_count_y = np.zeros(
        (num_radial_bins, num_velocity_bins - 1))
    total_particle_velocity_count_z = np.zeros(
        (num_radial_bins, num_velocity_bins - 1))
    radial_bins = np.linspace(0.0,
                              np.sqrt(3) * loop_offset * radius,
                              num_radial_bins)
    vel = np.sqrt(2.0 * electron_energy * PhysicalConstants.electron_charge /
                  PhysicalConstants.electron_mass)
    velocity_bins = np.linspace(-vel, vel, num_velocity_bins)
    num_sims = 400
    final_positions = []
    for i in range(num_sims):
        # Define particle velocity and 100eV charge particle
        z_unit = np.random.uniform(-1.0, 1.0)
        xy_plane = np.sqrt(1 - z_unit**2)
        phi = np.random.uniform(0.0, 2 * np.pi)
        velocity = np.asarray(
            [xy_plane * np.cos(phi), xy_plane * np.sin(phi), z_unit]) * vel
        particle = PICParticle(
            9.1e-31, 1.6e-19,
            np.random.uniform(-3.0 * radius / 16.0,
                              3.0 * radius / 16.0,
                              size=(3, )), velocity)

        t, x, y, z, v_x, v_y, v_z, final_idx = run_sim(
            (b_field, particle, radius, loop_offset * radius, I, n))

        # Add results_remote_run_15_08 to list
        escaped = False if final_idx is None else True
        final_idx = final_idx if escaped else -1

        # Save final position output
        final_positions.append(
            [t[final_idx], x[final_idx], y[final_idx], z[final_idx], escaped])

        # Change coordinate system
        radial_position = np.sqrt(x[:final_idx]**2 + y[:final_idx]**2 +
                                  z[:final_idx]**2)
        if use_cartesian_reference_frame:
            particle_position_count, particle_velocity_count = get_particle_count(
                radial_bins, velocity_bins, radial_position, v_x, v_y, v_z)
        else:
            r_unit = np.zeros((3, x[:final_idx].shape[0]))
            r_unit[0, :] = x[:final_idx]
            r_unit[1, :] = y[:final_idx]
            r_unit[2, :] = z[:final_idx]
            r_unit /= np.sqrt(x[:final_idx]**2 + y[:final_idx]**2 +
                              z[:final_idx]**2)

            xy_unit = np.zeros((3, x[:final_idx].shape[0]))
            xy_unit[0, :] = x[:final_idx]
            xy_unit[1, :] = y[:final_idx]
            xy_unit /= np.sqrt(np.sum(xy_unit**2, axis=0))

            latitude_unit = np.zeros(xy_unit.shape)
            latitude_unit[0] = xy_unit[1, :]
            latitude_unit[1] = -xy_unit[0, :]
            latitude_unit[2] = 0.0

            longitude_unit = np.zeros((3, x[:final_idx].shape[0]))
            longitude_unit[0, :] = r_unit[1, :] * latitude_unit[
                2, :] - r_unit[2] * latitude_unit[1]
            longitude_unit[1, :] = r_unit[2, :] * latitude_unit[
                0, :] - r_unit[0] * latitude_unit[2]
            longitude_unit[2, :] = r_unit[0, :] * latitude_unit[
                1, :] - r_unit[1] * latitude_unit[0]

            v_r = v_x[:final_idx] * r_unit[0, :] + v_y[:final_idx] * r_unit[
                1, :] + v_z[:final_idx] * r_unit[2, :]
            v_lat = v_x[:final_idx] * latitude_unit[
                0, :] + v_y[:final_idx] * latitude_unit[
                    1, :] + v_z[:final_idx] * latitude_unit[2, :]
            v_long = v_x[:final_idx] * longitude_unit[
                0, :] + v_y[:final_idx] * longitude_unit[
                    1, :] + v_z[:final_idx] * longitude_unit[2, :]

            particle_position_count, particle_velocity_count = get_particle_count(
                radial_bins, velocity_bins, radial_position, v_r, v_lat,
                v_long)

        # Get probability of electron in radial spacings in sim
        total_particle_position_count += particle_position_count
        total_particle_velocity_count_x += particle_velocity_count[0, :, :]
        total_particle_velocity_count_y += particle_velocity_count[1, :, :]
        total_particle_velocity_count_z += particle_velocity_count[2, :, :]

    # Save results_remote_run_15_08 to file
    position_output_path = os.path.join(
        output_dir, "radial_distribution-{}.txt".format(process_name))
    velocity_output_path = os.path.join(
        output_dir, "velocity_distribution-{}".format(process_name))
    final_state_output_path = os.path.join(
        output_dir, "final_state-current-{}.txt".format(process_name))
    np.savetxt(position_output_path,
               np.stack((radial_bins, total_particle_position_count)))
    np.savetxt("{}_x.txt".format(velocity_output_path),
               total_particle_velocity_count_x)
    np.savetxt("{}_y.txt".format(velocity_output_path),
               total_particle_velocity_count_y)
    np.savetxt("{}_z.txt".format(velocity_output_path),
               total_particle_velocity_count_z)
    np.savetxt(final_state_output_path, np.asarray(final_positions))

    print("Finished process: {}".format(process_name))
Beispiel #8
0
    def test_uniform_e_field(self):
        """
        This test considers a particle moving in a uniform electric field
        :return:
        """
        seed = 1
        num_tests = 100
        np.random.seed(seed)

        Es = np.random.uniform(low=0.0, high=1.0, size=(3, num_tests))
        X_0s = np.random.uniform(low=-1.0, high=1.0, size=(3, num_tests))
        V_0s = np.random.uniform(low=-1.0, high=1.0, size=(3, num_tests))

        for idx in range(num_tests):
            e_field = Es[:, idx]
            X_0 = X_0s[:, idx]
            V_0 = V_0s[:, idx]

            def B_field(x):
                B = np.zeros(x.shape)
                return B

            def E_field(x):
                E = np.zeros(x.shape)
                for i, b in enumerate(E):
                    E[i, :] = e_field
                return E

            X = X_0.reshape((1, 3))
            V = V_0.reshape((1, 3))
            Q = np.asarray([1.0])
            M = np.asarray([1.0])

            final_time = 4.0
            num_pts = 1000
            times = np.linspace(0.0, final_time, num_pts)
            positions = np.zeros((times.shape[0], X.shape[0], X.shape[1]))
            velocities = np.zeros((times.shape[0], X.shape[0], X.shape[1]))
            for i, t in enumerate(times):
                if i == 0:
                    positions[i, :, :] = X
                    velocities[i, :, :] = V
                    continue

                dt = times[i] - times[i - 1]

                x, v = boris_solver(E_field, B_field, X, V, Q, M, dt)

                positions[i, :, :] = x
                velocities[i, :, :] = v
                X = x
                V = v

            particle = PICParticle(1.0, Q[0], X_0, V_0)
            E = e_field
            analytic_times, analytic_positions = solve_E_field(particle,
                                                               E,
                                                               final_time,
                                                               num_pts=num_pts)

            x = positions[:, :, 0].flatten()
            y = positions[:, :, 1].flatten()
            z = positions[:, :, 2].flatten()

            self.assertLess(np.absolute(
                np.average(x - analytic_positions[:, 0])),
                            0.01,
                            msg="{}, {}, {}".format(X_0, V_0, e_field))
            self.assertLess(np.absolute(
                np.average(y - analytic_positions[:, 1])),
                            0.01,
                            msg="{}, {}, {}".format(X_0, V_0, e_field))
            self.assertLess(np.absolute(
                np.average(z - analytic_positions[:, 2])),
                            0.01,
                            msg="{}, {}, {}".format(X_0, V_0, e_field))
Beispiel #9
0
    def test_uniform_B_field(self):
        """
        This test considers a particle in a uniform B field, and aims to compare analytic and numerical solution. The
        particle can have an arbitrary velocity, and the B field is relatively in an arbitrary direction
        :return:
        """
        seed = 1
        num_tests = 100
        np.random.seed(seed)

        Bs = np.random.uniform(low=0.0, high=10.0, size=(3, num_tests))
        X_0s = np.random.uniform(low=-1.0, high=1.0, size=(3, num_tests))
        V_0s = np.random.uniform(low=-1.0, high=1.0, size=(3, num_tests))

        for idx in range(num_tests):
            b_field = Bs[:, idx]
            X_0 = X_0s[:, idx]
            V_0 = V_0s[:, idx]

            def B_field(x):
                B = np.zeros(x.shape)
                for i, b in enumerate(B):
                    B[i, :] = b_field
                return B

            def E_field(x):
                E = np.zeros(x.shape)
                return E

            X = X_0.reshape((1, 3))
            V = V_0.reshape((1, 3))
            Q = np.asarray([1.0])
            M = np.asarray([1.0])

            final_time = 4.0
            num_pts = 500
            times = np.linspace(0.0, final_time, num_pts)
            positions = np.zeros((times.shape[0], X.shape[0], X.shape[1]))
            velocities = np.zeros((times.shape[0], X.shape[0], X.shape[1]))
            for i, t in enumerate(times):
                if i == 0:
                    positions[i, :, :] = X
                    velocities[i, :, :] = V
                    continue

                dt = times[i] - times[i - 1]

                x, v = boris_solver(E_field, B_field, X, V, Q, M, dt)

                positions[i, :, :] = x
                velocities[i, :, :] = v
                X = x
                V = v

            particle = PICParticle(1.0, Q[0], X_0, V_0)
            B = b_field
            analytic_times, analytic_positions = solve_B_field(particle,
                                                               B,
                                                               final_time,
                                                               num_pts=num_pts)

            x = positions[:, :, 0].flatten()
            y = positions[:, :, 1].flatten()
            z = positions[:, :, 2].flatten()

            self.assertLess(
                np.absolute(np.average(x - analytic_positions[:, 0])), 0.01)
            self.assertLess(
                np.absolute(np.average(y - analytic_positions[:, 1])), 0.01)
            self.assertLess(
                np.absolute(np.average(z - analytic_positions[:, 2])), 0.01)
Beispiel #10
0
    def test_aligned_fields(self):
        """
        This test considers a particle moving in a uniform orthogonal electric and magnetic field
        :return:
        """
        seed = 1
        np.random.seed(seed)
        for direction in [
                np.asarray([1.0, 0.0, 0.0]),
                np.asarray([0.0, 1.0, 0.0]),
                np.asarray([0.0, 0.0, 1.0])
        ]:
            for sign in [-1, 1]:
                # randomise initial conditions
                B_mag = np.random.uniform(low=0.0, high=1.0)
                E_mag = np.random.uniform(low=0.0, high=1.0)
                X_0 = np.random.uniform(low=-1.0, high=1.0, size=(1, 3))
                V_0 = np.random.uniform(low=-1.0, high=1.0, size=(1, 3))

                def B_field(x):
                    B = np.zeros(x.shape)
                    for i, b in enumerate(B):
                        B[i, :] = B_mag * direction * sign
                    return B

                def E_field(x):
                    E = np.zeros(x.shape)
                    for i, b in enumerate(E):
                        E[i, :] = E_mag * direction * sign
                    return E

                X = X_0.reshape((1, 3))
                V = V_0.reshape((1, 3))
                Q = np.asarray([1.0])
                M = np.asarray([1.0])

                final_time = 4.0
                num_pts = 1000
                times = np.linspace(0.0, final_time, num_pts)
                positions = np.zeros((times.shape[0], X.shape[0], X.shape[1]))
                velocities = np.zeros((times.shape[0], X.shape[0], X.shape[1]))
                for i, t in enumerate(times):
                    if i == 0:
                        positions[i, :, :] = X
                        velocities[i, :, :] = V
                        continue

                    dt = times[i] - times[i - 1]

                    x, v = boris_solver(E_field, B_field, X, V, Q, M, dt)

                    positions[i, :, :] = x
                    velocities[i, :, :] = v
                    X = x
                    V = v

                particle = PICParticle(1.0, Q[0], X_0[0], V_0[0])
                E = E_mag * direction * sign
                B = B_mag * direction * sign
                analytic_times, analytic_positions = solve_aligned_fields(
                    particle, E, B, final_time, num_pts=num_pts)

                x = positions[:, :, 0].flatten()
                y = positions[:, :, 1].flatten()
                z = positions[:, :, 2].flatten()

                self.assertLess(np.absolute(
                    np.average(x - analytic_positions[:, 0])),
                                0.01,
                                msg="{}, {}, {}".format(X_0, V_0, E, B))
                self.assertLess(np.absolute(
                    np.average(y - analytic_positions[:, 1])),
                                0.01,
                                msg="{}, {}, {}".format(X_0, V_0, E, B))
                self.assertLess(np.absolute(
                    np.average(z - analytic_positions[:, 2])),
                                0.01,
                                msg="{}, {}, {}".format(X_0, V_0, E, B))
def run_parallel_sims(params):
    electron_energy_eV = params[0]
    use_interpolation = True
    dI_dt = 0.0

    print("Starting process: electron energy {}eV".format(electron_energy_eV))

    # Generate Polywell field
    I = 1e4
    radius = 1.0
    to_kA = 1e-3
    loop_pts = 200
    domain_pts = 130
    loop_offset = 1.25
    dom_size = 1.1 * loop_offset * radius
    if use_interpolation:
        file_name = "b_field_{}_{}_{}_{}_{}_{}".format(I * to_kA, radius,
                                                       loop_offset, domain_pts,
                                                       loop_pts, dom_size)
        file_path = os.path.join("..", "mesh_generation", "data",
                                 "radius-{}m".format(radius),
                                 "current-{}kA".format(I * to_kA),
                                 "domres-{}".format(domain_pts), file_name)
        b_field = InterpolatedBField(file_path, dom_pts_idx=6, dom_size_idx=8)
    else:
        comp_loops = list()
        comp_loops.append(
            CurrentLoop(I, radius,
                        np.asarray([-loop_offset * radius, 0.0, 0.0]),
                        np.asarray([1.0, 0.0, 0.0]), loop_pts))
        comp_loops.append(
            CurrentLoop(I, radius, np.asarray([loop_offset * radius, 0.0,
                                               0.0]),
                        np.asarray([-1.0, 0.0, 0.0]), loop_pts))
        comp_loops.append(
            CurrentLoop(I, radius,
                        np.asarray([0.0, -loop_offset * radius, 0.0]),
                        np.asarray([0.0, 1.0, 0.0]), loop_pts))
        comp_loops.append(
            CurrentLoop(I, radius, np.asarray([0.0, loop_offset * radius,
                                               0.0]),
                        np.asarray([0.0, -1.0, 0.0]), loop_pts))
        comp_loops.append(
            CurrentLoop(I, radius,
                        np.asarray([0.0, 0.0, -loop_offset * radius]),
                        np.asarray([0.0, 0.0, 1.0]), loop_pts))
        comp_loops.append(
            CurrentLoop(I, radius, np.asarray([0.0, 0.0,
                                               loop_offset * radius]),
                        np.asarray([0.0, 0.0, -1.0]), loop_pts))
        b_field = CombinedField(comp_loops, domain_size=dom_size)

    seed = 1
    np.random.seed(seed)

    # Run simulations
    num_sims = 500
    final_positions = []
    vel = np.sqrt(2.0 * electron_energy_eV *
                  PhysicalConstants.electron_charge /
                  PhysicalConstants.electron_mass)
    for i in range(num_sims):
        # Define particle velocity and 100eV charge particle
        z_unit = np.random.uniform(-1.0, 1.0)
        xy_plane = np.sqrt(1 - z_unit**2)
        phi = np.random.uniform(0.0, 2 * np.pi)
        velocity = np.asarray(
            [xy_plane * np.cos(phi), xy_plane * np.sin(phi), z_unit]) * vel
        particle = PICParticle(
            9.1e-31, 1.6e-19,
            np.random.uniform(-3.0 * radius / 16.0,
                              3.0 * radius / 16.0,
                              size=(3, )), velocity)

        t, x, y, z, final_idx = run_sim(
            (b_field, particle, radius, loop_offset * radius, I, dI_dt))

        # Add results to list
        escaped = False if final_idx is None else True
        final_idx = final_idx if escaped else -1
        final_positions.append(
            [t[final_idx], x[final_idx], y[final_idx], z[final_idx], escaped])

    # Save to file
    if not os.path.exists("results"):
        os.makedirs("results")
    output_path = os.path.join(
        "results",
        "mean_confinement-10kA-1.0m-{}.txt".format(electron_energy_eV))
    np.savetxt(output_path, np.asarray(final_positions))

    print("Finished process: electron energy {}eV".format(electron_energy_eV))
def run_parallel_sims(params):
    electron_energy, I, batch_num = params
    dI_dt = 0.0
    to_kA = 1e-3

    process_name = "electron_energy-{}eV-current-{}kA-batch-{}".format(
        electron_energy, I * to_kA, batch_num)
    print("Starting process: {}".format(process_name))

    # Generate Polywell field
    radius = 1.0
    loop_pts = 200
    domain_pts = 130
    loop_offset = 1.25
    dom_size = 1.1 * loop_offset * radius
    file_name = "b_field_{}_{}_{}_{}_{}_{}".format(I * to_kA, radius,
                                                   loop_offset, domain_pts,
                                                   loop_pts, dom_size)
    file_path = os.path.join("..", "mesh_generation", "data",
                             "radius-{}m".format(radius),
                             "current-{}kA".format(I * to_kA),
                             "domres-{}".format(domain_pts), file_name)
    b_field = InterpolatedBField(file_path, dom_pts_idx=6, dom_size_idx=8)

    seed = batch_num
    np.random.seed(seed)

    # Run simulations
    num_bins = 100
    particle_distribution_count = np.zeros((num_bins, ))
    radial_bins = np.linspace(0.0, np.sqrt(3) * loop_offset * radius, num_bins)
    num_sims = 200
    final_positions = []
    vel = np.sqrt(2.0 * electron_energy * PhysicalConstants.electron_charge /
                  PhysicalConstants.electron_mass)
    for i in range(num_sims):
        # Define particle velocity and 100eV charge particle
        z_unit = np.random.uniform(-1.0, 1.0)
        xy_plane = np.sqrt(1 - z_unit**2)
        phi = np.random.uniform(0.0, 2 * np.pi)
        velocity = np.asarray(
            [xy_plane * np.cos(phi), xy_plane * np.sin(phi), z_unit]) * vel
        particle = PICParticle(
            9.1e-31, 1.6e-19,
            np.random.uniform(-3.0 * radius / 16.0,
                              3.0 * radius / 16.0,
                              size=(3, )), velocity)

        t, x, y, z, final_idx = run_sim(
            (b_field, particle, radius, loop_offset * radius, I, dI_dt))

        # Add results to list
        escaped = False if final_idx is None else True
        final_idx = final_idx if escaped else -1

        # Get probability of electron in 50 radial spacings in sim
        radial_position = np.sqrt(x[:final_idx]**2 + y[:final_idx]**2 +
                                  z[:final_idx]**2)
        for i, r in enumerate(radial_bins):
            if i == 0.0:
                continue

            points_in_range = np.where(
                np.logical_and(radial_position >= radial_bins[i - 1],
                               radial_position <= r))
            particle_distribution_count[i - 1] += points_in_range[0].shape[0]

    # Save to file
    if not os.path.exists("results"):
        os.makedirs("results")
    output_dir = os.path.join("results", "radius-{}m".format(radius))
    if not os.path.exists(output_dir):
        os.makedirs(output_dir)
    output_dir = os.path.join(output_dir, "current-{}kA".format(I * to_kA))
    if not os.path.exists(output_dir):
        os.makedirs(output_dir)

    output_path = os.path.join(
        output_dir,
        "radial_distribution-current-{}-radius-{}-energy-{}-batch-{}.txt".
        format(I, radius, electron_energy, batch_num))
    np.savetxt(output_path, np.stack(
        (radial_bins, particle_distribution_count)))

    print("Finished process: {}".format(process_name))
Beispiel #13
0
def test_aligned_fields():
    """
    This test considers a particle moving in a uniform orthogonal electric and magnetic field
    :return:
    """
    seed = 1
    np.random.seed(seed)
    for direction in [
            np.asarray([1.0, 0.0, 0.0]),
            np.asarray([0.0, 1.0, 0.0]),
            np.asarray([0.0, 0.0, 1.0])
    ]:
        for sign in [-1, 1]:
            # randomise initial conditions
            B_mag = np.random.uniform(low=0.0, high=1.0)
            E_mag = np.random.uniform(low=0.0, high=1.0)
            X_0 = np.random.uniform(low=-1.0, high=1.0, size=(1, 3))
            V_0 = np.random.uniform(low=-1.0, high=1.0, size=(1, 3))

            def B_field(x):
                B = np.zeros(x.shape)
                for i, b in enumerate(B):
                    B[i, :] = B_mag * direction * sign
                return B

            def E_field(x):
                E = np.zeros(x.shape)
                for i, b in enumerate(E):
                    E[i, :] = E_mag * direction * sign
                return E

            X = X_0.reshape((1, 3))
            V = V_0.reshape((1, 3))
            Q = np.asarray([1.0])
            M = np.asarray([1.0])

            final_time = 4.0
            num_pts = 1000
            times = np.linspace(0.0, final_time, num_pts)
            positions = np.zeros((times.shape[0], X.shape[0], X.shape[1]))
            velocities = np.zeros((times.shape[0], X.shape[0], X.shape[1]))
            for i, t in enumerate(times):
                if i == 0:
                    positions[i, :, :] = X
                    velocities[i, :, :] = V
                    continue

                dt = times[i] - times[i - 1]

                x, v = boris_solver(E_field, B_field, X, V, Q, M, dt)

                positions[i, :, :] = x
                velocities[i, :, :] = v
                X = x
                V = v

            particle = PICParticle(1.0, Q[0], X_0[0], V_0[0])
            E = E_mag * direction * sign
            B = B_mag * direction * sign
            analytic_times, analytic_positions = solve_aligned_fields(
                particle, E, B, final_time, num_pts=num_pts)

            x = positions[:, :, 0].flatten()
            y = positions[:, :, 1].flatten()
            z = positions[:, :, 2].flatten()

            fig = plt.figure(figsize=(20, 10))
            ax = fig.add_subplot('111', projection='3d')
            ax.plot(x, y, z, label='numerical')
            ax.plot(analytic_positions[:, 0],
                    analytic_positions[:, 1],
                    analytic_positions[:, 2],
                    label='analytic')
            ax.set_xlabel('X')
            ax.set_ylabel('Y')
            ax.set_zlabel('Z')
            ax.legend(loc='best')
            ax.set_title("Analytic and Numerical Particle Motion")
            plt.show()
def single_particle_example(b_field=np.asarray([0.0, 0.0, 1.0]),
                            X_0=np.asarray([[0.0, 1.0, 0.0]]),
                            V_0=np.asarray([[-2.0, 0.0, 1.0]])):
    """
    Example solution to simple magnetic field case
    :return:
    """
    def B_field(x):
        B = np.zeros(x.shape)
        for i, b in enumerate(B):
            B[i, :] = b_field
        return B

    def E_field(x):
        E = np.zeros(x.shape)
        return E

    X = X_0
    V = V_0
    Q = np.asarray([1.0])
    M = np.asarray([1.0])

    times = np.linspace(0.0, 4.0, 1000)
    positions = np.zeros((times.shape[0], X.shape[0], X.shape[1]))
    velocities = np.zeros((times.shape[0], X.shape[0], X.shape[1]))
    for i, t in enumerate(times):
        if i == 0:
            positions[i, :, :] = X
            velocities[i, :, :] = V
            continue

        dt = times[i] - times[i - 1]

        x, v = boris_solver(E_field, B_field, X, V, Q, M, dt)

        positions[i, :, :] = x
        velocities[i, :, :] = v
        X = x
        V = v

    particle = PICParticle(1.0, Q[0], X_0[0], V_0[0])
    B = b_field
    analytic_times, analytic_positions = solve_B_field(particle, B, 4.0)

    x = positions[:, :, 0].flatten()
    y = positions[:, :, 1].flatten()
    z = positions[:, :, 2].flatten()
    fig = plt.figure(figsize=(20, 10))
    ax = fig.add_subplot('111', projection='3d')
    ax.plot(x, y, z, label='numerical')
    ax.plot(analytic_positions[:, 0],
            analytic_positions[:, 1],
            analytic_positions[:, 2],
            label='analytic')
    ax.set_xlabel('X')
    ax.set_ylabel('Y')
    ax.set_zlabel('Z')
    ax.legend(loc='best')
    ax.set_title("Analytic and Numerical Particle Motion")
    plt.show()

    fig, axes = plt.subplots(3, figsize=(20, 10))
    axes[0].plot(x)
    axes[1].plot(y)
    axes[2].plot(z)
    fig.suptitle("Numerical Solution")
    plt.show()

    fig, axes = plt.subplots(3, figsize=(20, 10))
    axes[0].plot(x - analytic_positions[:, 0])
    axes[1].plot(y - analytic_positions[:, 1])
    axes[2].plot(z - analytic_positions[:, 2])
    fig.suptitle("Deviation of Numerical Solution from the Analytic")
    plt.show()