def solve_B_field(particle, B, final_time, num_pts=1000):
    """
    Solve for the velocity function with respect to time
    """
    assert isinstance(B, np.ndarray) and B.shape[0] == 3 and len(
        B.shape) == 1, "B must be a 3D vector"
    v = particle.velocity[0]
    omega = particle.charge * magnitude(B) / particle.mass
    v_parallel = vector_projection(v, B)
    v_perpendicular = v - v_parallel

    F = cross(v_perpendicular, B) * particle.charge
    radius = (particle.mass *
              dot(v_perpendicular, v_perpendicular)) / magnitude(F)
    centre_of_rotation = F * radius / magnitude(F) + particle.position[0]
    relative_position = particle.position[0] - centre_of_rotation

    def parallel_motion(t):
        return v_parallel * t

    def perpendicular_motion(t):
        angle = -omega * t
        return arbitrary_axis_rotation_3d(relative_position, B, angle)

    times = np.linspace(0.0, final_time, num_pts)
    positions = np.zeros((num_pts, 3))
    for i, t in enumerate(times):
        positions[i, :] = centre_of_rotation + parallel_motion(
            t) + perpendicular_motion(t)

    return times, positions
    def test_electric_field(self):
        """
        Function to test the electric field generated PointField
        class

        :return:
        """
        field = PointField(1.0, 1.0, np.zeros(3))
        radius = field.radius
        rho = field.rho
        total_charge = rho * 4.0 / 3.0 * np.pi * radius**3

        # Sample at random points
        seed = 1
        num_tests = 1000
        np.random.seed(seed)
        sample_point = np.random.uniform(
            low=1.0, high=100.0, size=(3, num_tests)) * radius

        for i in range(sample_point.shape[1]):
            E = field.e_field(sample_point[:, i])
            E_mag = magnitude(E)

            self.assertAlmostEqual(
                total_charge /
                (4.0 * np.pi * magnitude(sample_point[:, i])**2 *
                 PointField.epsilon), E_mag)
    def b_field(self, field_point):
        """
        Calculate the B field at an arbitrary point from the loop
        """
        assert isinstance(field_point, np.ndarray)

        permeability_constant = CurrentLoop.mu_0 / (4 * np.pi)
        d_arc_length = self.__radius * self.d_theta
        integral_constant = permeability_constant * d_arc_length * self.__I

        # Integrate vector contributions
        b_field_contributions = np.zeros((self.theta.shape[0], 3))
        for i, rotation_angle in enumerate(self.theta):
            # Get b field direction
            loop_to_point = self.radial_locations[i, :] - field_point
            b_field_direction = cross(loop_to_point,
                                      self.current_direction[i, :])
            b_unit = b_field_direction / magnitude(b_field_direction)

            loop_distance = magnitude(loop_to_point)
            b_field_contributions[i, :] = b_unit / loop_distance**2

        b_field_contributions *= integral_constant
        b_field = np.sum(b_field_contributions, axis=0)

        return b_field
    def test_interpolated_b_field(self):
        """
        Function to test interpolated B field behaviour is as expected
        :return:
        """
        # Generate Interpolated field
        I = 1e6
        loop_pts = 40
        domain_pts = 50
        dom_size = 0.2
        radius = 0.15
        file_name = "current_loop_{}_{}_{}_{}".format(I * 1e-6, loop_pts,
                                                      domain_pts, dom_size)
        file_path = os.path.join(file_name)
        interp_field = InterpolatedBField(file_path)

        Z = np.linspace(-dom_size, dom_size, 50)
        for i, z in enumerate(Z):
            analytic_z_field = CurrentLoop.mu_0 / 2 * I * radius**2 / (
                (radius**2 + z**2)**(3.0 / 2.0))

            b = interp_field.b_field(np.asarray([[0.0, 0.0, z]]))
            B = magnitude(b[0])

            self.assertAlmostEqual(B / analytic_z_field, 1.0, 2)
예제 #5
0
def compare_fields(dom_size, numerical_pts, b_field, radius, current):
    b_factor = current / radius
    unit_field = load_field(1.0, 1.0)

    min_dom = -dom_size
    max_dom = dom_size
    X = np.linspace(min_dom, max_dom, numerical_pts)
    Y = np.linspace(min_dom, max_dom, numerical_pts)
    Z = np.linspace(min_dom, max_dom, numerical_pts)
    B = np.zeros((numerical_pts, numerical_pts, numerical_pts, 4))
    for i, x in enumerate(X):
        for j, y in enumerate(Y):
            for k, z in enumerate(Z):
                print(i, j, k)
                field_point = np.zeros((1, 3))
                field_point[0, 0] = x
                field_point[0, 1] = y
                field_point[0, 2] = z
                b = np.abs(b_field.b_field(field_point))
                gummersall_field = np.abs(
                    unit_field.b_field(field_point / radius) * b_factor)
                b = gummersall_field - b

                B[i, j, k, 0] = b[0, 0]
                B[i, j, k, 1] = b[0, 1]
                B[i, j, k, 2] = b[0, 2]
                B[i, j, k, 3] = magnitude(b[0])

    fig, ax = plt.subplots(1)
    X_1, Y_1 = np.meshgrid(X, Y, indexing='ij')
    im = ax.contourf(X_1, Y_1, B[:, :, numerical_pts // 2, 3], 100)
    fig.colorbar(im, ax=ax)
    ax.quiver(X_1, Y_1, B[:, :, numerical_pts // 2, 0],
              B[:, :, numerical_pts // 2, 1])
    plt.savefig("magnetic_field_sample_xy")
    plt.show()

    fig, ax = plt.subplots(1)
    X_2, Z_2 = np.meshgrid(X, Z, indexing='ij')
    im = ax.contourf(X_2, Z_2, B[:, numerical_pts // 2, :, 3], 100)
    fig.colorbar(im, ax=ax)
    ax.quiver(X_2, Z_2, B[:, numerical_pts // 2, :, 0],
              B[:, numerical_pts // 2, :, 2])
    plt.savefig("magnetic_field_sample_xz")
    plt.show()

    fig, ax = plt.subplots(1)
    Y_3, Z_3 = np.meshgrid(X, Y, indexing='ij')
    im = ax.contourf(Y_3, Z_3, B[numerical_pts // 2, :, :, 3], 100)
    fig.colorbar(im, ax=ax)
    ax.quiver(Y_3, Z_3, B[numerical_pts // 2, :, :, 1],
              B[numerical_pts // 2, :, :, 2])
    plt.savefig("magnetic_field_sample_yz")
    plt.show()
    def calculate_post_collision_velocities(self, v_1, v_2, dt, idx_1, idx_2):
        """
        Calculate the post collisional velocities of  a given pair of particles
        """
        # Step 1 - Get relative velocity u and perpendicular velocity u_xy
        u_rel = v_1 - v_2
        u = vector_ops.magnitude(u_rel)
        u_xy = np.sqrt(u_rel[0]**2 + u_rel[1]**2)
        u = 1e-16 if u < 1e-16 else u

        # Step 2 - Get scattering angles THETA and PHI
        PHI = np.random.uniform(0.0, 2.0 * np.pi)
        c_phi = np.cos(PHI)
        s_phi = np.sin(PHI)

        delta_squared = self.__q_1**2 * self.__q_2**2 * max(
            self.__n_1, self.__n_2)
        delta_squared *= dt * self.__coulomb_logarithm
        delta_squared /= 8.0 * np.pi * u**3 * self.__m_eff**2 * PhysicalConstants.epsilon_0**2
        assert not np.isnan(delta_squared) and not np.isinf(
            delta_squared), "{}, {}".format(delta_squared, u)

        delta = np.random.normal(0.0, np.sqrt(delta_squared))
        s_theta = 2 * delta / (1 + delta**2)
        one_minus_c_theta = 2 * delta**2 / (1 + delta**2)

        # Step 3 - Calculate du
        du = np.zeros((3, ))
        if u_xy != 0.0:
            du[0] = u_rel[0] / u_xy * u_rel[2] * s_theta * c_phi - \
                u_rel[1] / u_xy * u * s_theta * s_phi - \
                u_rel[0] * one_minus_c_theta
            du[1] = u_rel[1] / u_xy * u_rel[2] * s_theta * c_phi + \
                u_rel[0] / u_xy * u * s_theta * s_phi - \
                u_rel[1] * one_minus_c_theta
            du[2] = -u_xy * s_theta * c_phi - u_rel[2] * one_minus_c_theta
        else:
            du[0] = u * s_theta * c_phi
            du[1] = u * s_theta * s_phi
            du[2] = -u * one_minus_c_theta

        assert not np.any(np.isnan(du)), "{}, {}, {}, {}".format(
            du, delta, s_theta, one_minus_c_theta)

        # Step 4 - Update velocities
        P_1 = 1.0 if np.random.uniform(
            0, 1) <= self.__collision_threshold_1 else 0.0
        P_2 = 1.0 if np.random.uniform(
            0, 1) <= self.__collision_threshold_2 else 0.0
        new_v_1 = v_1 + P_1 * self.__m_eff / self.__m_1 * du
        new_v_2 = v_2 - P_2 * self.__m_eff / self.__m_2 * du

        return new_v_1, new_v_2
def full_b_field():
    I = 1e6
    radius = 2.0
    loop_pts = 20
    loop = CurrentLoop(I, radius, np.asarray([0.0, 0.0, 0.0]),
                       np.asarray([0.0, 0.0, 1.0]), loop_pts)

    numerical_pts = 40
    min_dom = -2.5
    max_dom = 2.5
    X = np.linspace(min_dom, max_dom, numerical_pts)
    Y = np.linspace(min_dom, max_dom, numerical_pts)
    Z = np.linspace(min_dom, max_dom, numerical_pts)
    B = np.zeros((numerical_pts, numerical_pts, numerical_pts, 4))
    for i, x in enumerate(X):
        for j, y in enumerate(Y):
            for k, z in enumerate(Z):
                b = loop.b_field(np.asarray([x, y, z]))
                B[i, j, k, 0] = b[0]
                B[i, j, k, 1] = b[1]
                B[i, j, k, 2] = b[2]
                B[i, j, k, 3] = magnitude(b)

    fig, ax = plt.subplots(3, figsize=(5, 5))
    X_1, Y_1 = np.meshgrid(X, Y, indexing='ij')
    im = ax[0].contourf(X_1, Y_1, B[:, :, numerical_pts // 2, 3], 100)
    fig.colorbar(im, ax=ax[0])
    ax[0].quiver(X_1,
                 Y_1,
                 B[:, :, numerical_pts // 2, 0],
                 B[:, :, numerical_pts // 2, 1],
                 headlength=7)

    X_2, Z_2 = np.meshgrid(X, Z, indexing='ij')
    im = ax[1].contourf(X_2, Z_2, B[:, numerical_pts // 2, :, 3], 100)
    fig.colorbar(im, ax=ax[1])
    ax[1].quiver(X_2,
                 Z_2,
                 B[:, numerical_pts // 2, :, 0],
                 B[:, numerical_pts // 2, :, 2],
                 headlength=7)

    Y_3, Z_3 = np.meshgrid(X, Y, indexing='ij')
    im = ax[2].contourf(Y_3, Z_3, B[numerical_pts // 2, :, :, 3], 100)
    fig.colorbar(im, ax=ax[2])
    ax[2].quiver(Y_3,
                 Z_3,
                 B[numerical_pts // 2, :, :, 1],
                 B[numerical_pts // 2, :, :, 2],
                 headlength=7)

    plt.show()
예제 #8
0
def generate_domain(dom_size, numerical_pts, b_field):
    min_dom = -dom_size
    max_dom = dom_size
    X = np.linspace(min_dom, max_dom, numerical_pts)
    Y = np.linspace(min_dom, max_dom, numerical_pts)
    Z = np.linspace(min_dom, max_dom, numerical_pts)
    B = np.zeros((numerical_pts, numerical_pts, numerical_pts, 4))
    for i, x in enumerate(X):
        for j, y in enumerate(Y):
            for k, z in enumerate(Z):
                print(i, j, k)
                field_point = np.zeros((1, 3))
                field_point[0, 0] = x
                field_point[0, 1] = y
                field_point[0, 2] = z
                b = b_field.b_field(field_point)
                B[i, j, k, 0] = b[0, 0]
                B[i, j, k, 1] = b[0, 1]
                B[i, j, k, 2] = b[0, 2]
                B[i, j, k, 3] = magnitude(b[0])

    fig, ax = plt.subplots(1)
    X_1, Y_1 = np.meshgrid(X, Y, indexing='ij')
    im = ax.contourf(X_1, Y_1, np.log10(B[:, :, numerical_pts // 2, 3]), 100)
    fig.colorbar(im, ax=ax)
    ax.quiver(X_1, Y_1, B[:, :, numerical_pts // 2, 0],
              B[:, :, numerical_pts // 2, 1])
    plt.savefig("magnetic_field_sample_xy")
    plt.show()

    fig, ax = plt.subplots(1)
    X_2, Z_2 = np.meshgrid(X, Z, indexing='ij')
    im = ax.contourf(X_2, Z_2, np.log10(B[:, numerical_pts // 2, :, 3]), 100)
    fig.colorbar(im, ax=ax)
    ax.quiver(X_2, Z_2, B[:, numerical_pts // 2, :, 0],
              B[:, numerical_pts // 2, :, 2])
    plt.savefig("magnetic_field_sample_xz")
    plt.show()

    fig, ax = plt.subplots(1)
    Y_3, Z_3 = np.meshgrid(X, Y, indexing='ij')
    im = ax.contourf(Y_3, Z_3, np.log10(B[numerical_pts // 2, :, :, 3]), 100)
    fig.colorbar(im, ax=ax)
    ax.quiver(Y_3, Z_3, B[numerical_pts // 2, :, :, 1],
              B[numerical_pts // 2, :, :, 2])
    plt.savefig("magnetic_field_sample_yz")
    plt.show()
예제 #9
0
    def v_field(self, field_point):
        """"
        Calculated the potential difference at a given point

        field_point: point at which the field is being calculated
        """
        radial_distance = field_point - self.__centre
        radial_magnitude = magnitude(radial_distance)

        if radial_magnitude < self.__radius:
            V = self.total_charge / (
                8.0 * np.pi * self.__radius * PointField.epsilon) * (
                    3.0 - radial_magnitude**2 / self.__radius**2)
            return V
        else:
            V = self.total_charge / (4.0 * np.pi * radial_magnitude *
                                     PointField.epsilon)
            return V
예제 #10
0
    def e_field(self, field_point):
        """"
        Calculated the electric field at a given point

        field_point: point at which the field is being calculated
        """
        radial_distance = field_point - self.__centre
        radial_magnitude = magnitude(radial_distance.flatten())
        radial_direction = radial_distance / radial_magnitude

        if radial_magnitude < self.__radius:
            E = self.total_charge * radial_magnitude / (
                4.0 * np.pi * self.__radius**3 * PointField.epsilon)
            return E * radial_distance
        else:
            E = self.total_charge / (4.0 * np.pi * radial_magnitude**2 *
                                     PointField.epsilon)
            return E * radial_direction
예제 #11
0
    def __init__(self, I, radius, centre, normal, num_pts):
        """"
        Initialise primary and secondary variables of the class

        I: Current in loop
        radius: Radius of the loop
        normal: Normal to the loop. This also defines the direction of the current, as the normal is in the direction
                of the B field.
        num_pts: Number of points that are used to integrate the biot savart law across the loop
        """
        assert isinstance(I, float)
        assert isinstance(radius, float)
        assert isinstance(centre, np.ndarray)
        assert isinstance(normal, np.ndarray)
        assert isinstance(num_pts, int)

        self.__I = I
        self.__radius = radius
        self.__centre = centre
        self.__normal = normal
        self.__num_pts = num_pts

        # Discretise the loop by angle
        self.d_theta = 2.0 * np.pi / num_pts
        self.theta = np.linspace(0.0, 2 * np.pi - self.d_theta, num_pts)

        # Find an arbitrary parallel unit vector
        unit_vector = np.asarray([1.0, 1.0, 1.0])
        self.r = cross(unit_vector, normal)
        self.r_unit = self.r / magnitude(self.r)
        self.R = self.r_unit * self.__radius

        self.radial_locations = np.zeros((num_pts, 3))
        self.current_direction = np.zeros((num_pts, 3))
        for i, rotation_angle in enumerate(self.theta):
            self.radial_locations[i, :] = arbitrary_axis_rotation_3d(
                self.R, self.__normal, rotation_angle) - self.__centre
            self.current_direction[i, :] = cross(
                self.__normal, self.radial_locations[i, :] - self.__centre)
    def test_b_field(self):
        """
        Function to test B field along axis is as expected from analytic solution
        :return:
        """
        for offset in [1.0, 0.0, -1.0]:
            I = 1e6
            radius = 0.15
            loop_pts = 500
            loop = CurrentLoop(I, radius, np.asarray([0.0, 0.0, offset]),
                               np.asarray([0.0, 0.0, 1.0]), loop_pts)

            Z = np.linspace(-5.0, 5.0, 50)

            for i, z in enumerate(Z):
                analytic_z_field = CurrentLoop.mu_0 / 2 * I * radius**2 / (
                    (radius**2 + (z + offset)**2)**(3.0 / 2.0))

                b = loop.b_field(np.asarray([0.0, 0.0, z]))
                B = magnitude(b)

                self.assertAlmostEqual(analytic_z_field, B, 5)
def generate_current_loop_field():
    I = 1e6
    radius = 0.15
    loop_offset = 0.0
    loop_pts = 40
    loop = CurrentLoop(I, radius, np.asarray([-loop_offset, 0.0, 0.0]), np.asarray([0.0, 0.0, 1.0]), loop_pts)

    # Calculate current loop field at all points
    domain_pts = 50
    dom_size = 0.2
    min_dom = -dom_size
    max_dom = dom_size
    X = np.linspace(min_dom, max_dom, domain_pts)
    Y = np.linspace(min_dom, max_dom, domain_pts)
    Z = np.linspace(min_dom, max_dom, domain_pts)
    B_x = np.zeros((domain_pts, domain_pts, domain_pts))
    B_y = np.zeros((domain_pts, domain_pts, domain_pts))
    B_z = np.zeros((domain_pts, domain_pts, domain_pts))
    B = np.zeros((domain_pts, domain_pts, domain_pts))
    for i, x in enumerate(X):
        for j, y in enumerate(Y):
            for k, z in enumerate(Z):
                print(i, j, k)
                b = loop.b_field(np.asarray([x, y, z]))
                B_x[i, j, k] = b[0]
                B_y[i, j, k] = b[1]
                B_z[i, j, k] = b[2]
                B[i, j, k] = magnitude(b)

    # Write output files
    file_name = "../../../testing/algo/fields/magnetic_fields/current_loop_{}_{}_{}_{}".format(I * 1e-6, loop_pts, domain_pts, dom_size)
    np.savetxt("{}_x".format(file_name), B_x.reshape((domain_pts, domain_pts ** 2)))
    np.savetxt("{}_y".format(file_name), B_y.reshape((domain_pts, domain_pts ** 2)))
    np.savetxt("{}_z".format(file_name), B_z.reshape((domain_pts, domain_pts ** 2)))
    np.savetxt(file_name, B.reshape((domain_pts, domain_pts ** 2)))
    write_vti_file(B, file_name)
예제 #14
0
    num_pts = 200
    X = np.linspace(-5.0, 5.0, num_pts)
    Y = np.linspace(-5.0, 5.0, num_pts)
    E = np.zeros((num_pts, num_pts))
    E_vector = np.zeros((num_pts, num_pts, 2))
    V = np.zeros((num_pts, num_pts))

    for i, x in enumerate(X):
        for j, y in enumerate(Y):
            point = np.asarray([x, y])
            e = field.e_field(point)
            v = field.v_field(point)

            E_vector[i, j, 0] = e[0]
            E_vector[i, j, 1] = e[1]
            E[i, j] = magnitude(e)
            V[i, j] = v

    # Plot results
    X, Y = np.meshgrid(X, Y)
    X = X.transpose()
    Y = Y.transpose()
    num_contours = 100
    fig, axes = plt.subplots(2, figsize=(10, 10))
    im = axes[0].contourf(X, Y, E, num_contours)
    quiv = axes[0].quiver(X, Y, E_vector[:, :, 0], E_vector[:, :, 1])
    fig.colorbar(im, ax=axes[0])
    axes[0].set_title("Electric Field (V/m)")
    im = axes[1].contourf(X, Y, V, num_contours)
    fig.colorbar(im, ax=axes[1])
    axes[1].set_title("Potential Field (V)")
def generate_polywell_fields(current_offset_factor=1.0, plot_fields=False):
    assert 0.0 <= current_offset_factor <= 1.0

    # Generate Polywell field
    I = 1e6
    radius = 0.15
    loop_offset = 0.175
    loop_pts = 20
    comp_loops = list()
    comp_loops.append(CurrentLoop(I, radius, np.asarray([-loop_offset, 0.0, 0.0]), np.asarray([1.0, 0.0, 0.0]), loop_pts))
    comp_loops.append(CurrentLoop(I, radius, np.asarray([loop_offset, 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, 0.0]), np.asarray([0.0, 1.0, 0.0]), loop_pts))
    comp_loops.append(CurrentLoop(I, radius, np.asarray([0.0, loop_offset, 0.0]), np.asarray([0.0, -1.0, 0.0]), loop_pts))
    comp_loops.append(CurrentLoop(current_offset_factor * I, radius, np.asarray([0.0, 0.0, -loop_offset]), np.asarray([0.0, 0.0, 1.0]), loop_pts))
    comp_loops.append(CurrentLoop(I, radius, np.asarray([0.0, 0.0, loop_offset]), np.asarray([0.0, 0.0, -1.0]), loop_pts))
    combined_field = CombinedField(comp_loops)

    # Calculate polywell field at all points
    domain_pts = 20
    dom_size = 0.2
    min_dom = -dom_size
    max_dom = dom_size
    X = np.linspace(min_dom, max_dom, domain_pts)
    Y = np.linspace(min_dom, max_dom, domain_pts)
    Z = np.linspace(min_dom, max_dom, domain_pts)
    B_x = np.zeros((domain_pts, domain_pts, domain_pts))
    B_y = np.zeros((domain_pts, domain_pts, domain_pts))
    B_z = np.zeros((domain_pts, domain_pts, domain_pts))
    B = np.zeros((domain_pts, domain_pts, domain_pts))
    for i, x in enumerate(X):
        for j, y in enumerate(Y):
            for k, z in enumerate(Z):
                print(i, j, k)
                b = combined_field.b_field(np.asarray([x, y, z]))
                B_x[i, j, k] = b[0]
                B_y[i, j, k] = b[1]
                B_z[i, j, k] = b[2]
                B[i, j, k] = magnitude(b)

    # Write output files
    file_name = "b_field_{}_{}_{}_{}_{}".format(I * 1e-6, loop_pts, domain_pts, dom_size, current_offset_factor)
    np.savetxt("{}_x".format(file_name), B_x.reshape((domain_pts, domain_pts ** 2)))
    np.savetxt("{}_y".format(file_name), B_y.reshape((domain_pts, domain_pts ** 2)))
    np.savetxt("{}_z".format(file_name), B_z.reshape((domain_pts, domain_pts ** 2)))
    np.savetxt(file_name, B.reshape((domain_pts, domain_pts ** 2)))
    write_vti_file(B, file_name)

    if plot_fields:
        # Plots overall field
        B = np.log10(B)
        fig, ax = plt.subplots(3, figsize=(15, 15))

        X_1, Y_1 = np.meshgrid(X, Y, indexing='ij')
        im = ax[0].contourf(X_1, Y_1, B[:, :, domain_pts // 2], 100)
        fig.colorbar(im, ax=ax[0])
        ax[0].quiver(X_1, Y_1, B_x[:, :, domain_pts // 2], B_y[:, :, domain_pts // 2], 100)
        ax[0].set_title("XY Plane")

        X_2, Z_2 = np.meshgrid(X, Z, indexing='ij')
        im = ax[1].contourf(X_2, Z_2, B[:, domain_pts // 2, :], 100)
        fig.colorbar(im, ax=ax[1])
        ax[1].quiver(X_2, Z_2, B_x[:, :, domain_pts // 2], B_z[:, :, domain_pts // 2], 100)
        ax[1].set_title("XZ Plane")

        Y_3, Z_3 = np.meshgrid(X, Y, indexing='ij')
        im = ax[2].contourf(Y_3, Z_3, B[domain_pts // 2, :, :], 100)
        fig.colorbar(im, ax=ax[2])
        ax[2].quiver(Y_3, Z_3, B_y[:, :, domain_pts // 2], B_z[:, :, domain_pts // 2], 100)
        ax[2].set_title("YZ Plane")
        plt.show()
def generate_interpolated_fields(current_offset_factor, compare_fields=False):
    # Generate Interpolated field
    I = 1e6
    loop_pts = 20
    domain_pts = 20
    dom_size = 0.2
    file_name = "b_field_{}_{}_{}_{}_{}".format(I * 1e-6, loop_pts, domain_pts, dom_size, current_offset_factor)
    # file_path = os.path.join("mesh_data", file_name)
    interp_field = InterpolatedBField(file_name)

    if compare_fields:
        # Generate Polywell field
        radius = 0.15
        loop_offset = 0.175
        comp_loops = list()
        comp_loops.append(CurrentLoop(I, radius, np.asarray([-loop_offset, 0.0, 0.0]), np.asarray([1.0, 0.0, 0.0]), loop_pts))
        comp_loops.append(CurrentLoop(I, radius, np.asarray([loop_offset, 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, 0.0]), np.asarray([0.0, 1.0, 0.0]), loop_pts))
        comp_loops.append(CurrentLoop(I, radius, np.asarray([0.0, loop_offset, 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]), np.asarray([0.0, 0.0, 1.0]), loop_pts))
        comp_loops.append(CurrentLoop(I, radius, np.asarray([0.0, 0.0, loop_offset]), np.asarray([0.0, 0.0, -1.0]), loop_pts))
        combined_field = CombinedField(comp_loops)

        B_poly = np.zeros((domain_pts, domain_pts, domain_pts))

    X = np.linspace(-dom_size, dom_size, domain_pts)
    Y = np.linspace(-dom_size, dom_size, domain_pts)
    Z = np.linspace(-dom_size, dom_size, domain_pts)
    B_interp = np.zeros((domain_pts, domain_pts, domain_pts))
    for i, x in enumerate(X):
        for j, y in enumerate(Y):
            for k, z in enumerate(Z):
                print(i, j, k)
                b_interp = interp_field.b_field(np.asarray([[x, y, z]]))
                B_interp[i, j, k] = magnitude(b_interp[0])

                if compare_fields:
                    b_poly = combined_field.b_field(np.asarray([x, y, z]))
                    B_poly[i, j, k] = magnitude(b_poly)

    if compare_fields:
        B_comp = B_poly - B_interp
        plot_fields = [B_poly, B_interp, B_comp]
    else:
        plot_fields = [B_interp]

    # Plots overall field
    for B in plot_fields:
        fig, ax = plt.subplots(3, figsize=(5, 5))
        X_1, Y_1 = np.meshgrid(X, Y)
        im = ax[0].contourf(X_1, Y_1, B[:, :, domain_pts // 2], 100)
        fig.colorbar(im, ax=ax[0])
        ax[0].set_title("XY Plane")

        X_2, Z_2 = np.meshgrid(X, Z)
        im = ax[1].contourf(X_2, Z_2, B[:, domain_pts // 2, :], 100)
        fig.colorbar(im, ax=ax[1])
        ax[1].set_title("XZ Plane")

        Y_3, Z_3 = np.meshgrid(X, Y)
        im = ax[2].contourf(Y_3, Z_3, B[domain_pts // 2, :, :], 100)
        fig.colorbar(im, ax=ax[2])
        ax[2].set_title("YZ Plane")
        plt.show()
예제 #17
0
def run_simulation(params):
    b_field, particle, radius, domain_size, I, dI_dt = params
    print_output = False
    plot_sim = False

    # There is no E field in the simulations
    def e_field(x):
        B = b_field.b_field(x)
        dB_dt = B / I * dI_dt
        return -dB_dt

    def b_field_func(x):
        B = b_field.b_field(x / radius)
        B *= I / radius
        return B

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

    # Set timestep according to Gummersall approximation
    max_dt = 1e-9 * radius
    min_dt = 1e-3 * max_dt
    final_time = 1e5 * max_dt
    max_steps = int(1e7)

    times = []
    positions = []
    velocities = []
    t = 0.0
    ts = 0
    times.append(t)
    positions.append(X)
    velocities.append(V)
    # Calculate fields
    while t < final_time and ts < max_steps:
        if print_output:
            print(t / final_time)

        # Get fields
        E = e_field(X)
        B = b_field_func(X)

        # Calculate time step
        dt = 0.2 * particle.mass / (magnitude(B[0]) * particle.charge)
        dt = min(max_dt, dt)
        dt = max(min_dt, dt)

        # Update time step
        ts += 1
        t += dt

        # Move particles
        x, v = boris_solver_internal(E, B, X, V, Q, M, dt)

        if np.any(x[0, :] < -domain_size) or np.any(x[0, :] > domain_size):
            if print_output:
                print("PARTICLE ESCAPED! - {}, {}, {}".format(ts, t, X[0]))

            x = np.asarray(positions)[:, :, 0].flatten()
            y = np.asarray(positions)[:, :, 1].flatten()
            z = np.asarray(positions)[:, :, 2].flatten()
            v_x = np.asarray(velocities)[:, :, 0].flatten()
            v_y = np.asarray(velocities)[:, :, 1].flatten()
            v_z = np.asarray(velocities)[:, :, 2].flatten()

            if plot_sim:
                # Plot 3D motion
                fig = plt.figure(figsize=(20, 10))
                ax = fig.add_subplot('111', projection='3d')
                ax.plot(x, y, z, label='numerical')
                ax.set_xlim([-1.25 * radius, 1.25 * radius])
                ax.set_ylim([-1.25 * radius, 1.25 * radius])
                ax.set_zlim([-1.25 * radius, 1.25 * radius])
                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()

            return times, x, y, z, v_x, v_y, v_z, True

        times.append(t)
        positions.append(x)
        velocities.append(v)
        X = x
        V = v

    # Convert points to x, y and z locations
    x = np.asarray(positions)[:, :, 0].flatten()
    y = np.asarray(positions)[:, :, 1].flatten()
    z = np.asarray(positions)[:, :, 2].flatten()
    v_x = np.asarray(velocities)[:, :, 0].flatten()
    v_y = np.asarray(velocities)[:, :, 1].flatten()
    v_z = np.asarray(velocities)[:, :, 2].flatten()

    if plot_sim:
        # Plot 3D motion
        fig = plt.figure(figsize=(20, 10))
        ax = fig.add_subplot('111', projection='3d')
        ax.plot(x, y, z, label='numerical')
        ax.set_xlim([-1.25 * radius, 1.25 * radius])
        ax.set_ylim([-1.25 * radius, 1.25 * radius])
        ax.set_zlim([-1.25 * radius, 1.25 * radius])
        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()

    return times, x, y, z, v_x, v_y, v_z, False
def generate_polywell_fields(params):
    """
    Generic function to plot a polywell field given geometry and current
    
    I: Current in coils
    radius: radius of coil
    loop_offset: spacing of coils as a ratio of the radius
    loop_pts: Number of loop segments used to solve Biot Savart law
    """
    I, radius, loop_offset, domain_pts, loop_pts = params
    assert loop_offset >= 1.0

    convert_to_kA = 1e-3
    dom_size = 1.1 * loop_offset * radius
    file_dir = os.path.join("data", "radius-{}m".format(radius),
                            "current-{}kA".format(I * convert_to_kA),
                            "domres-{}".format(domain_pts))
    if not os.path.exists(file_dir):
        os.makedirs(file_dir)
    file_name = "b_field_{}_{}_{}_{}_{}_{}".format(I * convert_to_kA, radius,
                                                   loop_offset, domain_pts,
                                                   loop_pts, dom_size)
    print("Starting mesh {}".format(file_name))

    # Generate Polywell field
    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))
    combined_field = CombinedField(comp_loops)

    # Calculate polywell field at all points
    min_dom = -dom_size
    max_dom = dom_size
    X = np.linspace(min_dom, max_dom, domain_pts)
    Y = np.linspace(min_dom, max_dom, domain_pts)
    Z = np.linspace(min_dom, max_dom, domain_pts)
    B_x = np.zeros((domain_pts, domain_pts, domain_pts))
    B_y = np.zeros((domain_pts, domain_pts, domain_pts))
    B_z = np.zeros((domain_pts, domain_pts, domain_pts))
    B = np.zeros((domain_pts, domain_pts, domain_pts))
    for i, x in enumerate(X):
        for j, y in enumerate(Y):
            for k, z in enumerate(Z):
                b = combined_field.b_field(np.asarray([[x, y, z]]))
                B_x[i, j, k] = b[0, 0]
                B_y[i, j, k] = b[0, 1]
                B_z[i, j, k] = b[0, 2]
                B[i, j, k] = magnitude(b[0])

    # Write output files
    np.savetxt(os.path.join(file_dir, "{}_x".format(file_name)),
               B_x.reshape((domain_pts, domain_pts**2)))
    np.savetxt(os.path.join(file_dir, "{}_y".format(file_name)),
               B_y.reshape((domain_pts, domain_pts**2)))
    np.savetxt(os.path.join(file_dir, "{}_z".format(file_name)),
               B_z.reshape((domain_pts, domain_pts**2)))
    np.savetxt(os.path.join(file_dir, file_name),
               B.reshape((domain_pts, domain_pts**2)))
    write_vti_file(B, os.path.join(file_dir, file_name))