diff --git a/.github/ISSUE_TEMPLATE/benchmark_request.md b/.github/ISSUE_TEMPLATE/benchmark_request.md
new file mode 100644
index 0000000..be36f65
--- /dev/null
+++ b/.github/ISSUE_TEMPLATE/benchmark_request.md
@@ -0,0 +1,14 @@
+---
+name: Benchmark request
+about: Suggest a benchmark for validation
+title: "[benchmark]"
+labels: benchmark
+assignees: ''
+
+---
+
+**Description**
+A clear and concise description of the requested benchmark.
+
+**Source**
+Source of data for benchmark; a paper, code, or database with sputtering yields, reflection coefficients, etc.
diff --git a/src/bca.rs b/src/bca.rs
index 4f911bd..7d00820 100644
--- a/src/bca.rs
+++ b/src/bca.rs
@@ -75,9 +75,6 @@ pub fn single_ion_bca<T: Geometry>(particle: particle::Particle, material: &mate
 
         //Remove particle from top of vector as particle_1
         let mut particle_1 = particles.pop().unwrap();
-
-        //println!("Particle start Z: {} E: {} ({}, {}, {})", particle_1.Z, particle_1.E/Q, particle_1.pos.x/ANGSTROM, particle_1.pos.y/ANGSTROM, particle_1.pos.z/ANGSTROM);
-
         //BCA loop
         while !particle_1.stopped & !particle_1.left {
 
@@ -114,8 +111,6 @@ pub fn single_ion_bca<T: Geometry>(particle: particle::Particle, material: &mate
                             particle_1.pos.x, particle_2.pos.x, &binary_collision_geometry))
                         .unwrap();
 
-                    //println!("{}", binary_collision_result);
-
                     //Only use 0th order collision for local electronic stopping
                     if k == 0 {
                         normalized_distance_of_closest_approach = binary_collision_result.normalized_distance_of_closest_approach;
@@ -127,17 +122,14 @@ pub fn single_ion_bca<T: Geometry>(particle: particle::Particle, material: &mate
                     particle_2.E = binary_collision_result.recoil_energy - material.average_bulk_binding_energy(particle_2.pos.x, particle_2.pos.y, particle_2.pos.z);
                     particle_2.energy_origin = particle_2.E;
 
-                    //Accumulate asymptotic deflections for primary particle
+                    //Accumulate energy losses and asymptotic deflections for primary particle
                     total_energy_loss += binary_collision_result.recoil_energy;
-
-                    //total_deflection_angle += psi;
                     total_asymptotic_deflection += binary_collision_result.asymptotic_deflection;
 
-                    //Rotate particle 1, 2 by lab frame scattering angles
-                    particle::rotate_particle(&mut particle_1, binary_collision_result.psi,
+                    particle_1.rotate(binary_collision_result.psi,
                         binary_collision_geometry.phi_azimuthal);
 
-                    particle::rotate_particle(&mut particle_2, -binary_collision_result.psi_recoil,
+                    particle_2.rotate(-binary_collision_result.psi_recoil,
                         binary_collision_geometry.phi_azimuthal);
 
                     particle_2.dir_old.x = particle_2.dir.x;
@@ -184,20 +176,17 @@ pub fn single_ion_bca<T: Geometry>(particle: particle::Particle, material: &mate
 
             //Advance particle in space and track total distance traveled
             #[cfg(not(feature = "accelerated_ions"))]
-            let distance_traveled = particle::particle_advance(&mut particle_1,
+            let distance_traveled = particle_1.advance(
                 binary_collision_geometries[0].mfp, total_asymptotic_deflection);
 
             #[cfg(feature = "accelerated_ions")]
-            let distance_traveled = particle::particle_advance(&mut particle_1,
+            let distance_traveled = particle_1.advance(
                 binary_collision_geometries[0].mfp + distance_to_target - material.geometry.get_energy_barrier_thickness(), total_asymptotic_deflection);
 
             //Subtract total energy from all simultaneous collisions and electronic stopping
             bca::update_particle_energy(&mut particle_1, &material, distance_traveled,
                 total_energy_loss, normalized_distance_of_closest_approach, strong_collision_Z,
                 strong_collision_index, &options);
-            //println!("Particle finished collision loop Z: {} E: {} ({}, {}, {})", particle_1.Z, particle_1.E/Q, particle_1.pos.x/ANGSTROM, particle_1.pos.y/ANGSTROM, particle_1.pos.z/ANGSTROM);
-
-            //println!("{} {} {}", energy_0/EV, energy_1/EV, (energy_1 - energy_0)/EV);
 
             //Check boundary conditions on leaving and stopping
             material::boundary_condition_planar(&mut particle_1, &material);
@@ -205,7 +194,6 @@ pub fn single_ion_bca<T: Geometry>(particle: particle::Particle, material: &mate
             //Set particle index to topmost particle
             particle_index = particles.len();
         }
-        //println!("Particle stopped or left Z: {} E: {} ({}, {}, {})", particle_1.Z, particle_1.E/Q, particle_1.pos.x/ANGSTROM, particle_1.pos.y/ANGSTROM, particle_1.pos.z/ANGSTROM);
         particle_output.push(particle_1);
     }
     particle_output
diff --git a/src/geometry.rs b/src/geometry.rs
index 462bad5..3942742 100644
--- a/src/geometry.rs
+++ b/src/geometry.rs
@@ -97,7 +97,6 @@ impl Geometry for Mesh0D {
     }
 
     fn inside_simulation_boundary(&self, x: f64, y: f64, z: f64) -> bool {
-        //println!("x: {} energy_barrier_thickness: {}", x/ANGSTROM, self.energy_barrier_thickness/ANGSTROM);
         x > -10.*self.energy_barrier_thickness
     }
 
diff --git a/src/interactions.rs b/src/interactions.rs
index 3c81cb7..db965a9 100644
--- a/src/interactions.rs
+++ b/src/interactions.rs
@@ -8,7 +8,6 @@ pub fn crossing_point_doca(interaction_potential: InteractionPotential) -> f64 {
         InteractionPotential::MORSE{D, alpha, r0} => (alpha*r0 - (2.0_f64).ln())/alpha,
         InteractionPotential::WW => 50.*ANGSTROM,
         _ => 10.*ANGSTROM,
-        //_ => panic!("Input error: potential never crosses zero for r > 0. Consider using the Newton rootfinder.")
     }
 
 }
diff --git a/src/lib.rs b/src/lib.rs
index 0fc1128..a185891 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -80,6 +80,7 @@ pub use crate::parry::{ParryBall, ParryBallInput, InputParryBall, ParryTriMesh,
 pub fn pybca(py: Python, m: &PyModule) -> PyResult<()> {
     m.add_function(wrap_pyfunction!(simple_bca_py, m)?)?;
     m.add_function(wrap_pyfunction!(simple_bca_list_py, m)?)?;
+    m.add_function(wrap_pyfunction!(compound_bca_list_py, m)?)?;
     Ok(())
 }
 
@@ -980,6 +981,198 @@ pub extern "C" fn simple_bca_c(x: f64, y: f64, z: f64, ux: f64, uy: f64, uz: f64
     }
 }
 
+#[cfg(feature = "python")]
+///compound_tagged_bca_list_py(ux, uy,  uz, energy, Z1, m1, Ec1, Es1, Z2, m2, Ec2, Es2, n2, Eb2)
+/// runs a BCA simulation for a list of particles and outputs a list of sputtered, reflected, and implanted particles.
+/// Args:
+///    energies (list(f64)): initial ion energies in eV.
+///    ux (list(f64)): initial ion directions x. ux != 0.0 to avoid gimbal lock
+///    uy (list(f64)): initial ion directions y.
+///    uz (list(f64)): initial ion directions z.
+///    Z1 (list(f64)): initial ion atomic numbers.
+///    m1 (list(f64)): initial ion masses in amu.
+///    Ec1 (list(f64)): ion cutoff energies in eV. If ion energy < Ec1, it stops in the material.
+///    Es1 (list(f64)): ion surface binding energies. Assumed planar.
+///    Z2 (list(f64)): target material species atomic numbers.
+///    m2 (list(f64)): target material species masses in amu.
+///    Ec2 (list(f64)): target material species cutoff energies in eV. If recoil energy < Ec2, it stops in the material.
+///    Es2 (list(f64)): target species surface binding energies. Assumed planar.
+///    n2 (list(f64)): target material species atomic number densities in inverse cubic Angstroms.
+///    Eb2 (list(f64)): target material species bulk binding energies in eV.
+/// Returns:
+///    output (NX9 list of f64): each row in the list represents an output particle (implanted,
+///    sputtered, or reflected). Each row consists of:
+///      [Z, m (amu), E (eV), x, y, z, (angstrom), ux, uy, uz]
+///    incident (list(bool)): whether each row of output was an incident ion or originated in the target
+#[pyfunction]
+pub fn compound_bca_list_py(energies: Vec<f64>, ux: Vec<f64>, uy: Vec<f64>, uz: Vec<f64>, Z1: Vec<f64>, m1: Vec<f64>, Ec1: Vec<f64>, Es1: Vec<f64>, Z2: Vec<f64>, m2: Vec<f64>, Ec2: Vec<f64>, Es2: Vec<f64>, n2: Vec<f64>, Eb2: Vec<f64>) -> (Vec<[f64; 9]>, Vec<bool>) {
+    let mut total_output = vec![];
+    let mut incident = vec![];
+    let num_species_target = Z2.len();
+    let num_incident_ions = energies.len();
+
+    assert_eq!(ux.len(), num_incident_ions, "Input error: list of x-directions is not the same length as list of incident energies.");
+    assert_eq!(uy.len(), num_incident_ions, "Input error: list of y-directions is not the same length as list of incident energies.");
+    assert_eq!(uz.len(), num_incident_ions, "Input error: list of z-directions is not the same length as list of incident energies.");
+    assert_eq!(Z1.len(), num_incident_ions, "Input error: list of incident atomic numbers is not the same length as list of incident energies.");
+    assert_eq!(m1.len(), num_incident_ions, "Input error: list of incident atomic masses is not the same length as list of incident energies.");
+    assert_eq!(Es1.len(), num_incident_ions, "Input error: list of incident surface binding energies is not the same length as list of incident energies.");
+    assert_eq!(Ec1.len(), num_incident_ions, "Input error: list of incident cutoff energies is not the same length as list of incident energies.");
+
+    assert_eq!(m2.len(), num_species_target, "Input error: list of target atomic masses is not the same length as atomic numbers.");
+    assert_eq!(Ec2.len(), num_species_target, "Input error: list of target cutoff energies is not the same length as atomic numbers.");
+    assert_eq!(Es2.len(), num_species_target, "Input error: list of target surface binding energies is not the same length as atomic numbers.");
+    assert_eq!(Eb2.len(), num_species_target, "Input error: list of target bulk binding energies is not the same length as atomic numbers.");
+    assert_eq!(n2.len(), num_species_target, "Input error: list of target number densities is not the same length as atomic numbers.");
+
+    #[cfg(feature = "distributions")]
+    let options = Options {
+        name: "test".to_string(),
+        track_trajectories: false,
+        track_recoils: true,
+        track_recoil_trajectories: false,
+        write_buffer_size: 8000,
+        weak_collision_order: 3,
+        suppress_deep_recoils: true,
+        high_energy_free_flight_paths: true,
+        electronic_stopping_mode: ElectronicStoppingMode::LOW_ENERGY_NONLOCAL,
+        mean_free_path_model: MeanFreePathModel::LIQUID,
+        interaction_potential: vec![vec![InteractionPotential::KR_C]],
+        scattering_integral: vec![vec![ScatteringIntegral::MENDENHALL_WELLER]],
+        num_threads: 1,
+        num_chunks: 1,
+        use_hdf5: false,
+        root_finder: vec![vec![Rootfinder::DEFAULTNEWTON]],
+        track_displacements: false,
+        track_energy_losses: false,
+        energy_min: 0.0,
+        energy_max: 10.0,
+        energy_num: 11,
+        angle_min: 0.0,
+        angle_max: 90.0,
+        angle_num: 11,
+        x_min: 0.0,
+        y_min: -10.0,
+        z_min: -10.0,
+        x_max: 10.0,
+        y_max: 10.0,
+        z_max: 10.0,
+        x_num: 11,
+        y_num: 11,
+        z_num: 11,
+    };
+
+    #[cfg(not(feature = "distributions"))]
+    let options = Options {
+        name: "test".to_string(),
+        track_trajectories: false,
+        track_recoils: true,
+        track_recoil_trajectories: false,
+        write_buffer_size: 8000,
+        weak_collision_order: 3,
+        suppress_deep_recoils: true,
+        high_energy_free_flight_paths: false,
+        electronic_stopping_mode: ElectronicStoppingMode::LOW_ENERGY_NONLOCAL,
+        mean_free_path_model: MeanFreePathModel::LIQUID,
+        interaction_potential: vec![vec![InteractionPotential::KR_C]],
+        scattering_integral: vec![vec![ScatteringIntegral::MENDENHALL_WELLER]],
+        num_threads: 1,
+        num_chunks: 1,
+        use_hdf5: false,
+        root_finder: vec![vec![Rootfinder::DEFAULTNEWTON]],
+        track_displacements: false,
+        track_energy_losses: false,
+    };
+
+    let x = -2.*(n2.iter().sum::<f64>()*10E30).powf(-1./3.);
+    let y = 0.0;
+    let z = 0.0;
+
+    let material_parameters = material::MaterialParameters {
+        energy_unit: "EV".to_string(),
+        mass_unit: "AMU".to_string(),
+        Eb: Eb2,
+        Es: Es2,
+        Ec: Ec2,
+        Z: Z2,
+        m: m2,
+        interaction_index: vec![0; num_species_target],
+        surface_binding_model: SurfaceBindingModel::INDIVIDUAL,
+        bulk_binding_model: BulkBindingModel::INDIVIDUAL,
+    };
+
+    let geometry_input = geometry::Mesh0DInput {
+        length_unit: "ANGSTROM".to_string(),
+        densities: n2,
+        electronic_stopping_correction_factor: 1.0
+    };
+
+    let m = material::Material::<Mesh0D>::new(&material_parameters, &geometry_input);
+
+    let mut index: usize = 0;
+    for (((((((E1_, ux_), uy_), uz_), Z1_), Ec1_), Es1_), m1_) in energies.iter().zip(ux).zip(uy).zip(uz).zip(Z1).zip(Ec1).zip(Es1).zip(m1) {
+
+        let mut energy_out;
+        let p = particle::Particle {
+            m: m1_*AMU,
+            Z: Z1_,
+            E: E1_*EV,
+            Ec: Ec1_*EV,
+            Es: Es1_*EV,
+            pos: Vector::new(x, y, z),
+            dir: Vector::new(ux_, uy_, uz_),
+            pos_origin: Vector::new(x, y, z),
+            pos_old: Vector::new(x, y, z),
+            dir_old: Vector::new(ux_, uy_, uz_),
+            energy_origin: E1_*EV,
+            asymptotic_deflection: 0.0,
+            stopped: false,
+            left: false,
+            incident: true,
+            first_step: true,
+            trajectory: vec![],
+            energies: vec![],
+            track_trajectories: false,
+            number_collision_events: 0,
+            backreflected: false,
+            interaction_index : 0,
+            weight: 0.0,
+            tag: 0,
+            tracked_vector: Vector::new(0., 0., 0.),
+        };
+
+        let output = bca::single_ion_bca(p, &m, &options);
+
+        for particle in output {
+            if (particle.left) | (particle.incident) {
+
+                incident.push(particle.incident);
+
+                if particle.stopped {
+                    energy_out = 0.
+                } else {
+                    energy_out = particle.E/EV
+                }
+                total_output.push(
+                    [
+                        particle.Z,
+                        particle.m/AMU,
+                        energy_out,
+                        particle.pos.x,
+                        particle.pos.y,
+                        particle.pos.z,
+                        particle.dir.x,
+                        particle.dir.y,
+                        particle.dir.z,
+                    ]
+                );
+            }
+        }
+        index += 1;
+    }
+    (total_output, incident)
+}
+
 #[cfg(feature = "python")]
 /// simple_bca_py( x, y, z, ux, uy, uz, energy, Z1, m1, Ec1, Es1, Z2, m2, Ec2, Es2, n2, Eb2)
 /// --
diff --git a/src/material.rs b/src/material.rs
index 8be2149..9b3eb86 100644
--- a/src/material.rs
+++ b/src/material.rs
@@ -323,7 +323,6 @@ pub fn surface_binding_energy<T: Geometry>(particle_1: &mut particle::Particle,
 
     //Actual surface binding energies
     let Es = material.actual_surface_binding_energy(particle_1, x_old, y_old, z_old);
-    //println!("Actual Es: {}", Es);
     let Ec = particle_1.Ec;
 
     let inside_now = material.inside_energy_barrier(x, y, z);
@@ -380,7 +379,6 @@ pub fn surface_binding_energy<T: Geometry>(particle_1: &mut particle::Particle,
                 particle_1.dir.x = -2.*(costheta)*dx/mag + cosx;
                 particle_1.dir.y = -2.*(costheta)*dy/mag + cosy;
                 particle_1.dir.z = -2.*(costheta)*dz/mag + cosz;
-
                 particle_1.backreflected = true;
             }
         }
diff --git a/src/particle.rs b/src/particle.rs
index 6aff9f6..e82412b 100644
--- a/src/particle.rs
+++ b/src/particle.rs
@@ -83,7 +83,7 @@ pub struct Particle {
     pub left: bool,
     pub incident: bool,
     pub first_step: bool,
-    pub trajectory: Vec<Vector4>,
+    pub trajectory: Vec<TrajectoryElement>,
     pub energies: Vec<EnergyLoss>,
     pub track_trajectories: bool,
     pub number_collision_events: usize,
@@ -122,7 +122,7 @@ impl Particle {
             left: false,
             incident: true,
             first_step: true,
-            trajectory: vec![Vector4::new(input.E, input.x, input.y, input.z)],
+            trajectory: vec![TrajectoryElement::new(input.E, input.x, input.y, input.z)],
             energies: vec![],
             track_trajectories: options.track_trajectories,
             number_collision_events: 0,
@@ -170,7 +170,7 @@ impl Particle {
     /// If `track_trajectories`, add the current (E, x, y, z) to the trajectory.
     pub fn add_trajectory(&mut self) {
         if self.track_trajectories {
-            self.trajectory.push(Vector4 {E: self.E, x: self.pos.x, y: self.pos.y, z: self.pos.z});
+            self.trajectory.push(TrajectoryElement {E: self.E, x: self.pos.x, y: self.pos.y, z: self.pos.z});
         }
     }
 
@@ -190,58 +190,57 @@ impl Particle {
             self.m*speed*self.dir.z,
         )
     }
-}
 
-/// Rotate a particle by a deflection psi at an azimuthal angle phi.
-pub fn rotate_particle(particle_1: &mut particle::Particle, psi: f64, phi: f64) {
-    let cosx: f64 = particle_1.dir.x;
-    let cosy: f64 = particle_1.dir.y;
-    let cosz: f64 = particle_1.dir.z;
-    let cphi: f64 = phi.cos();
-    let sphi: f64 = phi.sin();
-    let sa = (1. - cosx*cosx).sqrt();
+    /// Rotate a particle by a deflection psi at an azimuthal angle phi.
+    pub fn rotate(&mut self, psi: f64, phi: f64) {
+        let cosx: f64 = self.dir.x;
+        let cosy: f64 = self.dir.y;
+        let cosz: f64 = self.dir.z;
+        let cphi: f64 = phi.cos();
+        let sphi: f64 = phi.sin();
+        let sa = (1. - cosx*cosx).sqrt();
 
-    //Particle direction update formulas from original TRIDYN paper, see Moeller and Eckstein 1988
-    let cpsi: f64 = psi.cos();
-    let spsi: f64 = psi.sin();
-    let cosx_new: f64 = cpsi*cosx + spsi*cphi*sa;
-    let cosy_new: f64 = cpsi*cosy - spsi/sa*(cphi*cosx*cosy - sphi*cosz);
-    let cosz_new: f64 = cpsi*cosz - spsi/sa*(cphi*cosx*cosz + sphi*cosy);
+        //Particle direction update formulas from original TRIDYN paper, see Moeller and Eckstein 1988
+        let cpsi: f64 = psi.cos();
+        let spsi: f64 = psi.sin();
+        let cosx_new: f64 = cpsi*cosx + spsi*cphi*sa;
+        let cosy_new: f64 = cpsi*cosy - spsi/sa*(cphi*cosx*cosy - sphi*cosz);
+        let cosz_new: f64 = cpsi*cosz - spsi/sa*(cphi*cosx*cosz + sphi*cosy);
 
-    let dir_new = Vector {x: cosx_new, y: cosy_new, z: cosz_new};
+        let dir_new = Vector {x: cosx_new, y: cosy_new, z: cosz_new};
 
-    particle_1.dir.assign(&dir_new);
-    particle_1.dir.normalize();
-}
+        self.dir.assign(&dir_new);
+        self.dir.normalize();
+    }
 
-/// Push particle in space according to previous direction and return the distance traveled.
-pub fn particle_advance(particle_1: &mut particle::Particle, mfp: f64, asymptotic_deflection: f64) -> f64 {
+    /// Push particle in space according to previous direction and return the distance traveled.
+    pub fn advance(&mut self, mfp: f64, asymptotic_deflection: f64) -> f64 {
 
-    if particle_1.E > particle_1.Ec {
-        particle_1.add_trajectory();
-    }
+        if self.E > self.Ec {
+            self.add_trajectory();
+        }
 
-    //Update previous position
-    particle_1.pos_old.x = particle_1.pos.x;
-    particle_1.pos_old.y = particle_1.pos.y;
-    particle_1.pos_old.z = particle_1.pos.z;
+        //Update previous position
+        self.pos_old.x = self.pos.x;
+        self.pos_old.y = self.pos.y;
+        self.pos_old.z = self.pos.z;
 
-    //In order to keep average denisty constant, must add back previous asymptotic deflection
-    let distance_traveled = mfp + particle_1.asymptotic_deflection - asymptotic_deflection;
-    //let distance_traveled = mfp - asymptotic_deflection;
+        //In order to keep average denisty constant, must add back previous asymptotic deflection
+        let distance_traveled = mfp + self.asymptotic_deflection - asymptotic_deflection;
 
-    //dir has been updated, so use previous direction to advance in space
-    particle_1.pos.x += particle_1.dir_old.x*distance_traveled;
-    particle_1.pos.y += particle_1.dir_old.y*distance_traveled;
-    particle_1.pos.z += particle_1.dir_old.z*distance_traveled;
-    particle_1.asymptotic_deflection = asymptotic_deflection;
+        //dir has been updated, so use previous direction to advance in space
+        self.pos.x += self.dir_old.x*distance_traveled;
+        self.pos.y += self.dir_old.y*distance_traveled;
+        self.pos.z += self.dir_old.z*distance_traveled;
+        self.asymptotic_deflection = asymptotic_deflection;
 
-    //Update previous direction
-    particle_1.dir_old.x = particle_1.dir.x;
-    particle_1.dir_old.y = particle_1.dir.y;
-    particle_1.dir_old.z = particle_1.dir.z;
+        //Update previous direction
+        self.dir_old.x = self.dir.x;
+        self.dir_old.y = self.dir.y;
+        self.dir_old.z = self.dir.z;
 
-    return distance_traveled;
+        return distance_traveled;
+    }
 }
 
 pub fn surface_refraction(particle: &mut Particle, normal: Vector, Es: f64) {
@@ -249,10 +248,6 @@ pub fn surface_refraction(particle: &mut Particle, normal: Vector, Es: f64) {
 
     let costheta = particle.dir.dot(&normal);
 
-    let a = (E/(E + Es)).sqrt();
-    let b = -(E).sqrt()*costheta;
-    let c = (E*costheta.powi(2) + Es).sqrt();
-
     let u1x = (E/(E + Es)).sqrt()*particle.dir.x + ((-(E).sqrt()*costheta + (E*costheta.powi(2) + Es).sqrt())/(E + Es).sqrt())*normal.x;
     let u1y = (E/(E + Es)).sqrt()*particle.dir.y + ((-(E).sqrt()*costheta + (E*costheta.powi(2) + Es).sqrt())/(E + Es).sqrt())*normal.y;
     let u1z = (E/(E + Es)).sqrt()*particle.dir.z + ((-(E).sqrt()*costheta + (E*costheta.powi(2) + Es).sqrt())/(E + Es).sqrt())*normal.z;
diff --git a/src/structs.rs b/src/structs.rs
index 2b9eb90..47f20a0 100644
--- a/src/structs.rs
+++ b/src/structs.rs
@@ -44,18 +44,18 @@ impl Vector {
     }
 }
 
-/// Vector4 is a trajectory-tracking object that includes x, y, z, and the current energy.
+/// TrajectoryElement is a trajectory-tracking object that includes x, y, z, and the current energy.
 #[derive(Clone)]
-pub struct Vector4 {
+pub struct TrajectoryElement {
     pub E: f64,
     pub x: f64,
     pub y: f64,
     pub z: f64,
 }
 
-impl Vector4 {
-    pub fn new(E: f64, x: f64, y: f64, z: f64) -> Vector4 {
-        Vector4 {
+impl TrajectoryElement {
+    pub fn new(E: f64, x: f64, y: f64, z: f64) -> TrajectoryElement {
+        TrajectoryElement {
             E,
             x,
             y,
diff --git a/src/tests.rs b/src/tests.rs
index ea34548..a1f4a8c 100644
--- a/src/tests.rs
+++ b/src/tests.rs
@@ -660,9 +660,6 @@ fn test_surface_refraction() {
     let cosy_new = cosy_new/dir_mag;
     let cosz_new = cosz_new/dir_mag;
 
-    //let delta_theta = particle::refraction_angle(cosx, E, E + Es);
-    //particle::rotate_particle(&mut particle_1, delta_theta, 0.);
-
     let normal = Vector::new(1.0, 0.0, 0.0);
     particle::surface_refraction(&mut particle_1, normal, Es);
 
@@ -690,9 +687,6 @@ fn test_surface_refraction() {
         println!("{} {} {}", particle_1.dir.x, particle_1.dir.y, particle_1.dir.z);
     }
 
-    //let delta_theta = particle::refraction_angle(particle_1.dir.x, particle_1.E, particle_1.E - Es);
-    //particle::rotate_particle(&mut particle_1, delta_theta, 0.);
-
     let normal = Vector::new(1.0, 0.0, 0.0);
     particle::surface_refraction(&mut particle_1, normal, -Es);
 
@@ -856,10 +850,10 @@ fn test_momentum_conservation() {
                         particle_2.E = binary_collision_result.recoil_energy - material_1.average_bulk_binding_energy(particle_2.pos.x, particle_2.pos.y, particle_2.pos.z);
 
                         //Rotate particle 1, 2 by lab frame scattering angles
-                        particle::rotate_particle(&mut particle_1, binary_collision_result.psi,
+                        particle_1.rotate(binary_collision_result.psi,
                             binary_collision_geometries[0].phi_azimuthal);
 
-                        particle::rotate_particle(&mut particle_2, -binary_collision_result.psi_recoil,
+                        particle_2.rotate(-binary_collision_result.psi_recoil,
                             binary_collision_geometries[0].phi_azimuthal);
 
                         //Subtract total energy from all simultaneous collisions and electronic stopping
@@ -894,7 +888,7 @@ fn test_momentum_conservation() {
 }
 
 #[test]
-fn test_rotate_particle() {
+fn test_rotate() {
     let mass = 1.;
     let Z = 1.;
     let E = 1.;
@@ -912,18 +906,18 @@ fn test_rotate_particle() {
     let mut particle = particle::Particle::new(mass, Z, E, Ec, Es, x, y, z, cosx, cosy, cosz, false, false, 0);
 
     //Check that rotation in 2D works
-    particle::rotate_particle(&mut particle, psi, phi);
+    particle.rotate(psi, phi);
     assert!(approx_eq!(f64, particle.dir.x, 0., epsilon = 1E-12), "particle.dir.x: {} Should be ~0.", particle.dir.x);
     assert!(approx_eq!(f64, particle.dir.y, 1., epsilon = 1E-12), "particle.dir.y: {} Should be ~1.", particle.dir.y);
 
     //Check that rotating back by negative psi returns to the previous values
-    particle::rotate_particle(&mut particle, -psi, phi);
+    particle.rotate(-psi, phi);
     assert!(approx_eq!(f64, particle.dir.x, cosx, epsilon = 1E-12), "particle.dir.x: {} Should be ~{}", particle.dir.x, cosx);
     assert!(approx_eq!(f64, particle.dir.y, cosy, epsilon = 1E-12), "particle.dir.y: {} Should be ~{}", particle.dir.y, cosy);
 
     //Check that azimuthal rotation by 180 degrees works correctly
     let phi = PI;
-    particle::rotate_particle(&mut particle, psi, phi);
+    particle.rotate(psi, phi);
     assert!(approx_eq!(f64, particle.dir.x, 1., epsilon = 1E-12), "particle.dir.x: {} Should be ~1.", particle.dir.x);
     assert!(approx_eq!(f64, particle.dir.y, 0., epsilon = 1E-12), "particle.dir.y: {} Should be ~0.", particle.dir.y);
 
@@ -950,7 +944,7 @@ fn test_particle_advance() {
 
     let mut particle = particle::Particle::new(mass, Z, E, Ec, Es, x, y, z, cosx, cosy, cosz, false, false, 0);
 
-    let distance_traveled = particle::particle_advance(&mut particle, mfp, asymptotic_deflection);
+    let distance_traveled = particle.advance(mfp, asymptotic_deflection);
 
     assert_eq!(particle.pos.x, (1. - 0.5)*cosx);
     assert_eq!(particle.pos.y, (1. - 0.5)*cosy);