diff --git a/collective_effects/resonator.py b/collective_effects/resonator.py
new file mode 100644
index 0000000000000000000000000000000000000000..410a287a18d3a526b05b477429e9fbf385f73049
--- /dev/null
+++ b/collective_effects/resonator.py
@@ -0,0 +1,120 @@
+# -*- coding: utf-8 -*-
+"""
+This module defines the impedances and wake functions from the resonator model 
+based on the Wakefield class.
+
+@author: Alexis Gamelin, Watanyu Foosang
+@date: 25/09/2020
+"""
+
+import numpy as np
+from mbtrack2.collective_effects.wakefield import (Wakefield, Impedance, 
+                                                   WakeFunction)
+
+class Resonator(Wakefield):
+    def __init__(self, Rs, fr, Q, plane, n_wake=1e4, n_imp=1e4, imp_freq_lim=50e9):
+        """
+        Resonator model Wakefield element which computes the impedance and the 
+        wake function in both longitudinal and transverse case.
+
+        Parameters
+        ----------
+        Rs : float
+            Shunt impedance in [ohm].
+        fr : float
+            Resonance frequency in [Hz].
+        Q : float
+            Quality factor.
+        plane : str
+            Plane on which the resonator is used: "long", "x" or "y".
+        n_wake : int or float, optional
+            Number of points used in the wake function. The default is 1e4.
+        n_imp : int or float, optional
+            Number of points used in the impedance. The default is 1e4.
+        imp_freq_lim : float, optional
+            Maximum frequency used in the impedance. The default is 50e9.
+            
+        References
+        ----------
+        [1]  B. W. Zotter and S. A. Kheifets, "Impedances and Wakes in High-Energy 
+        Particle Ac-celerators", Eq. (3.10) and (3.15), pp.51-53.
+
+        """
+        super().__init__()
+        self.Rs = Rs
+        self.fr = fr
+        self.wr = 2 * np.pi * self.fr
+        self.Q = Q
+        self.n_wake = int(n_wake)
+        self.n_imp = int(n_imp)
+        self.imp_freq_lim = imp_freq_lim
+        self.plane = plane
+
+        self.timestop = round(np.log(1000)/self.wr*2*self.Q, 12)
+        
+        if self.Q >= 0.5:
+            self.Q_p = np.sqrt(self.Q**2 - 0.25)
+        else:
+            self.Q_p = np.sqrt(0.25 - self.Q**2)
+            
+        self.wr_p = (self.wr*self.Q_p)/self.Q
+        
+        if self.plane == "long":
+            
+            freq = np.linspace(start=1, stop=self.imp_freq_lim, num=self.n_imp)
+            imp = Impedance(variable=freq, 
+                            function=self.long_impedance(freq),
+                            impedance_type="long")
+            super().append_to_model(imp)
+            
+            time = np.linspace(start=0, stop=self.timestop, num=self.n_wake)
+            wake = WakeFunction(variable=time,
+                                function=self.long_wake_function(time),
+                                wake_type="long")
+            super().append_to_model(wake)
+            
+        elif self.plane == "x" or self.plane == "y" :
+            
+            freq = np.linspace(start=1, stop=self.imp_freq_lim, num=self.n_imp)
+            imp = Impedance(variable=freq, 
+                            function=self.transverse_impedance(freq),
+                            impedance_type=self.plane + "dip")
+            super().append_to_model(imp)
+            
+            time = np.linspace(start=0, stop=self.timestop, num=self.n_wake)
+            wake = WakeFunction(variable=time,
+                                function=self.transverse_wake_function(time),
+                                wake_type=self.plane + "dip")
+            super().append_to_model(wake)
+        else:
+            raise ValueError("Plane must be: long, x or y")
+        
+    def long_wake_function(self, t):
+        if self.Q >= 0.5:
+            return ( (self.wr * self.Rs / self.Q) * 
+                    np.exp(-1* self.wr * t / (2 * self.Q) ) *
+                     (np.cos(self.wr_p * t) - 
+                      np.sin(self.wr_p * t) / (2 * self.Q_p) ) )
+                        
+        elif self.Q < 0.5:
+            return ( (self.wr * self.Rs / self.Q) * 
+                    np.exp(-1* self.wr * t / (2 * self.Q) ) *
+                     (np.cosh(self.wr_p * t) - 
+                      np.sinh(self.wr_p * t) / (2 * self.Q_p) ) )
+                            
+    def long_impedance(self, f):
+        return self.Rs / (1 + 1j * self.Q * (f/self.fr - self.fr/f))
+    
+    def transverse_impedance(self, f):
+        return self.Rs * self.fr / f / (
+            1 + 1j * self.Q * (f / self.fr - self.fr / f) )
+    
+    def transverse_wake_function(self, t):
+        if self.Q >= 0.5:
+            return (self.wr * self.Rs / self.Q_p * 
+                    np.exp(-1 * t * self.wr / 2 / self.Q_p) *
+                    np.sin(self.wr_p * t) )
+        else:
+            return (self.wr * self.Rs / self.Q_p * 
+                    np.exp(-1 * t * self.wr / 2 / self.Q_p) *
+                    np.sinh(self.wr_p * t) )
\ No newline at end of file
diff --git a/collective_effects/wakefield.py b/collective_effects/wakefield.py
index 89b6cb3d48d0518eda37cc4ac4174f4cc89074b0..7854a48b66a4d130476650d607763c1ff2b1d4af 100644
--- a/collective_effects/wakefield.py
+++ b/collective_effects/wakefield.py
@@ -1,10 +1,10 @@
 # -*- coding: utf-8 -*-
 """
 This module defines general classes to describes wakefields, impedances and 
-wake functions. Based on impedance library by David Amorim.
+wake functions.
 
-@author: David Amorin, Alexis Gamelin
-@date: 14/01/2020
+@author: David Amorin, Alexis Gamelin, Watanyu Foosang
+@date: 24/09/2020
 """
 
 import warnings
@@ -12,14 +12,13 @@ import re
 import pandas as pd
 import numpy as np
 import matplotlib.pyplot as plt
-from scipy.interpolate import interp1d
-from mbtrack2.tracking.element import Element
+import scipy as sc
 from copy import deepcopy
-from scipy.integrate import trapz
 from scipy.interpolate import interp1d
+from scipy.integrate import trapz
 from scipy.constants import c
-import scipy as sc
 from mpl_toolkits.axes_grid1.inset_locator import inset_axes
+from mbtrack2.tracking.element import Element
 
 class ComplexData:
     """
@@ -260,7 +259,7 @@ class WakeFunction(ComplexData):
     def wake_type(self, value):
         self._wake_type = value
         
-    def wakefunction_from_imp(self, imp_obj, nout=None, trim_zero=True):
+    def wakefunction_from_imp(self, imp_obj, nout=None, trim=False):
         """
         Compute a wake function from an Impedance object and load it to 
         the WakeFunction object.
@@ -273,13 +272,20 @@ class WakeFunction(ComplexData):
             to be 2*(n-1) where n is the length of the input. If nout > n, the
             input is padded with zeros. If nout < n, the inpu it cropped.
             Note that, for any nout value, nout//2+1 input points are required.
-        trim_zero : bool, optional
+        trim : bool or float, optional
             If True, the pseudo wake function is trimmed at time=0 and is forced 
-            to zero where time<0. If False, the original result is preserved.
+            to zero where time<0. 
+            If False, the original result is preserved.
+            If a float is given, the pseudo wake function is trimmed from 
+            time <= trim to 0. 
         """
         
         Z0 = (imp_obj.data['real'] + imp_obj.data['imag']*1j)
         Z = Z0[~np.isnan(Z0)]
+        
+        if imp_obj.impedance_type != "long":
+            Z = Z / 1j
+        
         freq = Z.index
         fs = ( freq[-1] - freq[0] ) / len(freq)
         sampling = freq[1] - freq[0]
@@ -293,15 +299,15 @@ class WakeFunction(ComplexData):
         time = sc.fft.fftshift(time_array)
         Wlong = sc.fft.fftshift(Wlong_raw)
         
-        if trim_zero is True:
-            i_neg = np.where(time<0)[0]
+        if trim is not False:
+            i_neg = np.where(time<trim)[0]
             Wlong[i_neg] = 0
-        
+                    
         super().__init__(variable=time, function=Wlong)
         self.data.index.name = "time [s]"        
         
-    def long_wakefunction(self, Wp_filename, bunch_length=1e-3, 
-                          bunch_charge = 1.44e-9, nout=None, freq_lim=200e9):
+    def long_wakefunction(self, Wp_filename, bunch_length, bunch_charge, 
+                          nout=None, freq_lim=200e9, trim=False):
         """
         Compute wake function from wake potential and load it to the WakeFunction
         object.
@@ -310,8 +316,10 @@ class WakeFunction(ComplexData):
         ----------
         Wp_filename : str
             Text file that contains wake potential data.
-        bunch_length : float, optional
-            Spatial bunch length [m].
+        bunch_length : float
+            Spatial bunch length in [m].
+        bunch_charge : float
+            Bunch charge in [C].
         nout : int, optional
             Length of the transformed axis of the output. If None, it is taken
             to be 2*(n-1) where n is the length of the input. If nout > n, the
@@ -327,7 +335,7 @@ class WakeFunction(ComplexData):
                                    freq_lim=freq_lim,
                                    bunch_length=bunch_length, 
                                    bunch_charge=bunch_charge)
-        self.wakefunction_from_imp(imp, nout=nout)
+        self.wakefunction_from_imp(imp, nout=nout, trim=trim)
         
 class Impedance(ComplexData):
     """
@@ -634,6 +642,13 @@ class Wakefield:
         """
         return np.array([comp for comp in dir(self) if re.match(r'[Z]', comp)])
     
+    @property
+    def wake_components(self):
+        """
+        Return an array of the wake function component names for the element.
+        """
+        return np.array([comp for comp in dir(self) if re.match(r'[W]', comp)])
+    
     @staticmethod
     def add_wakefields(wake1, beta1, wake2, beta2):
         """
@@ -705,129 +720,7 @@ class Wakefield:
             return wake_sum
         else:
             raise ValueError("Error in input.")
-    
-class Resonator(Wakefield):
-    """
-    Compute an analytical wake function and an analytical impedance of a 
-    resonator [1] and load it to the Resonator object.
-    
-    Parameter
-    ---------
-    ring : Synchrotron object.
-    Q_factor : float, optional
-        Resonator quality factor. The default value is 1.
-    f_res : float, optional
-        Resonator resonance frequency in [Hz]. The default value is 10e9 Hz.
-    R_shunt : float, optional
-        Resonator shunt impedance in [Ohm]. The default value is 100 Ohm.
-    n_wake : int, optional
-        Number of points for wake function calcuation.
-    n_imp : int, optional 
-        Number of points for impedance calculation.
-    imp_freq_lim : float, optional
-        The frequency limit for impedance calculation [Hz].
-        
-    References
-    ----------
-    [1]  B. W. Zotter and S. A. Kheifets, "Impedances and Wakes in High-Energy 
-    Particle Ac-celerators", Eq. (3.10) and (3.15), pp.51-53.
-    
-    """
-    
-    def __init__(self, ring, Q_factor=1, f_res=10e9, R_shunt=100,
-                 n_wake=1000, n_imp=1000, imp_freq_lim=50e9):   
-        super().__init__()
-        self.ring = ring
-        self.Q = Q_factor
-        self.omega_res = 2*np.pi*f_res
-        self.R = R_shunt
-        self.n_wake = n_wake
-        self.n_imp = n_imp
-        self.f_stop = imp_freq_lim
-        
-        if Q_factor >= 0.5:
-            self.Q_p = np.sqrt(self.Q**2 - 0.25)
-        else:
-            self.Q_p = np.sqrt(0.25 - self.Q**2)
-            
-        self.omega_res_p = (self.omega_res*self.Q_p)/self.Q
-            
-        self.get_wakefunction()
-        self.long_impedance()
-    
-    def init_timestop(self):
-        """
-        Calculate an approximate position where the wake field depletes. 
-
-        """
-        self.timestop = round(np.log(1000)/self.omega_res*2*self.Q, 12)
-            
-    def time_array(self): 
-        self.tau = np.linspace(start=0, stop=self.timestop, num=self.n_wake)
         
-    def long_wakefunction(self):
-        """
-        The equations of resonator longitudinal wake function.
-
-        """
-        if self.Q >= 0.5:
-            self.wlong = (self.omega_res*self.R/self.Q)*\
-                    np.exp(-self.omega_res*self.tau/(2*self.Q))*\
-                        (np.cos(self.omega_res_p*self.tau)-\
-                         np.sin(self.omega_res_p*self.tau)/(2*self.Q_p))
-                        
-        elif self.Q < 0.5:
-            self.wlong = (self.omega_res*self.R/self.Q)*\
-                    np.exp(-self.omega_res*self.tau/(2*self.Q))*\
-                        (np.cosh(self.omega_res_p*self.tau)-\
-                         np.sinh(self.omega_res_p*self.tau)/(2*self.Q_p))
-    
-    def check_wake_tail(self):
-        """
-        Checking whether the full wake function is obtained by the calculated
-        initial timestop. If not, the timestop is extended by 50 ps.
-
-        """
-        
-        ratio = np.abs(max(self.wlong)) / np.abs(self.wlong[-6:-1])
-        while any(ratio < 1000):
-            self.timestop += 50e-12      
-            self.time_array()
-            self.long_wakefunction()
-            ratio = np.abs(max(self.wlong)) / np.abs(self.wlong[-6:-1])
-                        
-    def get_wakefunction(self):
-        """
-        Summary of all commands for wake function calculation.
-
-        """
-        self.init_timestop()
-        self.time_array()
-        self.long_wakefunction()
-        self.check_wake_tail()
-        
-        wake_func = WakeFunction(variable=self.tau, function=self.wlong)
-        super().append_to_model(wake_func)
-        
-    def long_impedance(self):
-        """
-        Calculate the analytical resonator longitudinal impedance.
-
-        """
-        
-        freq = np.linspace(start=0, stop=self.f_stop, num=self.n_imp)
-        omega = 2*np.pi*freq
-        
-        Re_Z = self.R * \
-            1/(1 + self.Q**2 * (omega**2-self.omega_res**2)**2 / (omega*self.omega_res)**2 )
-        Im_Z = -1*self.R*self.Q * (omega**2-self.omega_res**2) * \
-            1/(omega*self.omega_res) * \
-            1/(1 + self.Q**2 * (omega**2-self.omega_res**2)**2 / (omega*self.omega_res)**2)
-            
-        Z = Re_Z + 1j*Im_Z
-        imp = Impedance(variable=freq, function=Z)
-        super().append_to_model(imp)
-    
 class ImpedanceModel(Element):
     """
     Define the impedance model of the machine.
diff --git a/tracking/element.py b/tracking/element.py
index bcc1940b11038a3c5fa0c405d1c2ea923bfb6f1b..9123e901224ce2d8e399ffa9ba9fbae603f2e9fe 100644
--- a/tracking/element.py
+++ b/tracking/element.py
@@ -9,13 +9,9 @@ included in the tracking.
 """
 
 import numpy as np
-import pandas as pd
 from abc import ABCMeta, abstractmethod
 from functools import wraps
 from mbtrack2.tracking.particles import Beam
-from scipy import signal
-import matplotlib.pyplot as plt
-from scipy.interpolate import interp1d
 
 class Element(metaclass=ABCMeta):
     """
@@ -98,334 +94,7 @@ class LongitudinalMap(Element):
         """
         bunch["delta"] -= self.ring.U0 / self.ring.E0
         bunch["tau"] -= self.ring.ac * self.ring.T0 * bunch["delta"]
-        
-class WakePotential(Element):
-    """
-    Resonator model based wake potential calculation for one turn.
-    
-    Parameters
-    ----------
-    ring : Synchrotron object.
-    Q_factor : float, optional
-        Resonator quality factor. The default value is 1.
-    f_res : float, optional
-        Resonator resonance frequency in [Hz]. The default value is 10e9 Hz.
-    R_shunt : float, optional
-        Resonator shunt impedance in [Ohm]. The default value is 100 Ohm.
-    n_bin : int, optional
-        Number of bins for constructing the longitudinal bunch profile.
-        The default is 65.
-        
-    Attributes
-    ----------
-    rho : array of shape (n_bin, )
-        Bunch charge density profile.
-    tau : array of shape (n_bin + time_extra, )
-        Time array starting from the head of the bunch until the wake tail 
-        called timestop.
-        
-        The length of time_extra is determined by the last position of the
-        bunch time_bunch[-1], timestop, and the mean bin width of the bunch
-        profile mean_bin_size as
-            len(time_extra) = (timestop - time_bunch[-1]) / mean_bin_size
-    W_long : array of shape (n_bin + time_extra, )
-        Wakefunction profile.
-    W_p : array of shape (n_bin + time_extra, )
-        Wake potential profile.
-    wp : array of shape (mp_number, )
-        Wake potential exerted on each macro-particle.
-    
-    Methods
-    -------
-    charge_density(bunch, n_bin=65)
-        Calculate bunch charge density.
-    plot(self, var, plot_rho=True)
-        Plotting wakefunction or wake potential.
-    track(bunch)
-        Tracking method for the element.
-        
-    """
-    
-    def __init__(self, ring, Q_factor=1, f_res=10e9, R_shunt=100, n_bin=65):       
-        self.ring = ring
-        self.n_bin = n_bin
-        
-        self.Q_factor = Q_factor
-        self.omega_res = 2*np.pi*f_res
-        self.R_shunt = R_shunt
-        
-        if Q_factor >= 0.5:
-            self.Q_factor_p = np.sqrt(self.Q_factor**2 - 0.25)
-            self.omega_res_p = (self.omega_res*self.Q_factor_p)/self.Q_factor
-        else:
-            self.Q_factor_pp = np.sqrt(0.25 - self.Q_factor**2)
-            self.omega_res_p = (self.omega_res*self.Q_factor_pp)/self.Q_factor
-            
-    def charge_density(self, bunch, n_bin):
-        self.bins = bunch.binning(n_bin=self.n_bin)
-        self.bin_data = self.bins[2]
-        self.bin_size = self.bins[0].length
-        
-        self.rho = bunch.charge_per_mp*self.bin_data/ \
-            (self.bin_size*bunch.charge)
-    
-    def init_timestop(self):
-        self.timestop = round(np.log(1000)/self.omega_res*2*self.Q_factor, 12)
-    
-    def time_array(self):        
-        time_bunch = self.bins[0].mid
-        mean_bin_size = np.mean(self.bin_size)
-        time_extra = np.arange(start = time_bunch[-1]+mean_bin_size, 
-                               stop = self.timestop, step = mean_bin_size)
-        
-        self.tau = np.concatenate((time_bunch,time_extra))
-     
-    def long_wakefunction(self):
-        w_list = []
-        if self.Q_factor >= 0.5:
-            for t in self.tau: 
-                if t >= 0:
-                    w_long = -(self.omega_res*self.R_shunt/self.Q_factor)*\
-                        np.exp(-self.omega_res*t/(2*self.Q_factor))*\
-                            (np.cos(self.omega_res_p*t)-\
-                             np.sin(self.omega_res_p*t)/(2*self.Q_factor_p))
-                else: 
-                    w_long = 0
-                    
-                w_list.append(w_long)
-                        
-        elif self.Q_factor < 0.5:
-            for t in self.tau: 
-                if t >= 0:
-                    w_long = -(self.omega_res*self.R_shunt/self.Q_factor)*\
-                        np.exp(-self.omega_res*t/(2*self.Q_factor))*\
-                            (np.cosh(self.omega_res_p*t)-\
-                             np.sinh(self.omega_res_p*t)/(2*self.Q_factor_pp))
-                else:
-                    w_long = 0
-                    
-                w_list.append(w_long)
-                
-        self.W_long = np.array(w_list)
-                
-    def wake_potential(self):
-        dtau = (self.tau[-1]-self.tau[0]) / len(self.tau)
-        self.W_p = signal.convolve(self.W_long, self.rho, mode="same") * dtau
-    
-    def plot(self, var, plot_rho=True):
-        """
-        Plotting wakefunction or wake potential.
-
-        Parameters
-        ----------
-        var : {'W_p', 'W_long' }
-            If 'W_p', the wake potential is plotted. 
-            If 'W_long', the wakefunction is plotted.            
-        plot_rho : bool, optional
-            Overlay the bunch charge density profile on the plot.
-            The default is True.
 
-        Returns
-        -------
-        fig : Figure
-            Figure object with the plot on it.
-
-        """
-        
-        fig, ax = plt.subplots()
-            
-        if var == "W_p":
-            ax.plot(self.tau*1e12, self.W_p*1e-12)
-            
-            ax.set_xlabel("$\\tau$ (ps)")
-            ax.set_ylabel("W$_p$ (V/pC)")
-    
-        elif var == "W_long":
-            ax.plot(self.tau*1e12, self.W_long*1e-12)
-            ax.set_xlabel("$\\tau$ (ps)")
-            ax.set_ylabel("W$_{||}$ (V/ps)")
-            
-        if plot_rho is True:
-            rho_array = np.array(self.rho)
-            rho_rescaled = rho_array/max(rho_array)*max(self.W_p)
-            
-            ax.plot(self.bins[0].mid*1e12, rho_rescaled*1e-12)
-            
-        else:
-            pass
-            
-        return fig
-    
-    def check_wake_tail(self):
-        """
-        Checking whether the full wakefunction is obtained by the calculated
-        initial timestop.
-
-        """
-        
-        ratio = np.abs(min(self.W_long) / self.W_long[-6:-1])
-        while any(ratio < 1000):
-            # Initial timestop is too short. 
-            # Extending timestop by 50 ps and recalculating."
-            self.timestop += 50e-12      
-            self.time_array()
-            self.long_wakefunction()
-            ratio = np.abs(min(self.W_long) / self.W_long[-6:-1])
-        
-    @Element.parallel
-    def track(self, bunch):
-        """
-        Tracking method for the element.
-        No bunch to bunch interaction, so written for Bunch objects and
-        @Element.parallel is used to handle Beam objects.
-
-        Parameters
-        ----------
-        bunch : Bunch or Beam object.
-        
-        """
-
-        self.charge_density(bunch, n_bin = self.n_bin)
-        self.init_timestop()
-        self.time_array()
-        self.long_wakefunction()
-        self.check_wake_tail()
-        self.wake_potential()
-        
-        f = interp1d(self.tau, self.W_p, fill_value = 0, bounds_error = False)
-        self.wp = f(bunch["tau"])
-        
-        bunch["delta"] += self.wp * bunch.charge / self.ring.E0
-        
-class WakePotential2(Element):
-    """
-    Compute a wake potential from a wake function by performing a convolution 
-    with a bunch charge profile.
-    
-    Parameters
-    ----------
-    wake_obj : WakeFunction object
-    ring : Synchrotron object
-    n_bin : int, optional
-        Number of bins for constructing the longitudinal bunch profile.
-    bunch : Bunch or Beam object
-        
-    Attributes
-    ----------
-    rho : array of shape (n_bin, )
-        Bunch charge density profile in the unit [1/s].
-    Wlong : array
-        Wake function profile.
-    Wp : array 
-        Wake potential profile.
-    Wp_interp : array of shape (mp_number, )
-        Wake potential, obtained from interpolating Wp, exerted on each macro-particle.
-    
-    Methods
-    -------
-    charge_density(bunch)
-        Calculate bunch charge density [1/s].
-    get_wakepotential
-        Compute a wake potential by convolving the bunch profile and the wake
-        function.
-    plot(self, var, plot_rho=True)
-        Plotting wakefunction or wake potential. If plot_rho=True, the bunch
-        charge density is overlayed.
-    track(bunch)
-        Tracking method for the element.
-        
-    """
-    
-    def __init__(self, ring, wake_obj, n_bin=65):
-        self.wake_obj = wake_obj
-        self.tau0 = self.wake_obj.data.index
-        self.Wlong0 = self.wake_obj.data['real']
-        self.ring = ring
-        self.n_bin = n_bin
-            
-    def charge_density(self, bunch):
-        self.bins = bunch.binning(n_bin=self.n_bin)
-        self.bin_data = self.bins[2]
-        self.bin_size = self.bins[0].length
-        
-        self.rho = bunch.charge_per_mp*self.bin_data/(self.bin_size*bunch.charge)
-                
-    def get_wakepotential(self):
-        dtau = (self.tau0[-1]-self.tau0[0]) / len(self.tau0)
-        first_tau = self.bins[0].mid[0]
-        last_tau = self.tau0[-1]
-        self.tau = np.arange(first_tau, last_tau+dtau, step=dtau)
-        
-        self.tau_rho = np.arange(first_tau, self.bins[0].mid[-1]+dtau, step=dtau)
-        interp_func_rho = interp1d(self.bins[0].mid, self.rho, fill_value=0,
-                                    bounds_error=False)
-        self.rho_interp = interp_func_rho(self.tau_rho)
-        
-        interp_func_wlong = interp1d(self.tau0, self.Wlong0, fill_value=0,
-                                     bounds_error=False)
-        self.Wlong = interp_func_wlong(self.tau)
-        self.Wp = signal.convolve(self.Wlong*-1, self.rho_interp, mode='same')*dtau
-    
-    def plot(self, var, plot_rho=True):
-        """
-        Plotting wakefunction or wake potential.
-
-        Parameters
-        ----------
-        var : {'Wp', 'Wlong'}
-            If 'Wp', the wake potential is plotted. 
-            If 'Wlong', the wakefunction is plotted.            
-        plot_rho : bool, optional
-            Overlay the bunch charge density profile on the plot.
-
-        Returns
-        -------
-        fig : Figure
-            Figure object with the plot on it.
-
-        """
-        
-        fig, ax = plt.subplots()
-            
-        if var == "Wp":
-            ax.plot(self.tau*1e12, self.Wp*1e-12)
-            ax.set_xlabel("$\\tau$ (ps)")
-            ax.set_ylabel("W$_p$ (V/pC)")
-    
-        elif var == "Wlong":
-            ax.plot(self.tau*1e12, self.Wlong*1e-12)
-            ax.set_xlabel("$\\tau$ (ps)")
-            ax.set_ylabel("W$_{||}$ (V/ps)")
-            
-        if plot_rho is True:
-            rho_array = np.array(self.rho)
-            dict_plot_rho = {"Wp":self.Wp, "Wlong":self.Wlong}
-            rho_rescaled = rho_array/max(rho_array)*max(dict_plot_rho[var])
-            ax.plot(self.bins[0].mid*1e12, rho_rescaled*1e-12)
-            
-        return fig
-    
-    @Element.parallel
-    def track(self, bunch):
-        """
-        Tracking method for the element.
-        No bunch to bunch interaction, so written for Bunch objects and
-        @Element.parallel is used to handle Beam objects.
-
-        Parameters
-        ----------
-        bunch : Bunch or Beam object.
-        
-        """
-
-        self.charge_density(bunch)
-        self.get_wakepotential()
-        
-        f = interp1d(self.tau, self.Wp, fill_value = 0, bounds_error = False)
-        self.Wp_interp = f(bunch["tau"])
-        
-        bunch["delta"] += self.Wp_interp * bunch.charge / self.ring.E0
-         
 class SynchrotronRadiation(Element):
     """
     Element to handle synchrotron radiation, radiation damping and quantum 
diff --git a/tracking/wakepotential.py b/tracking/wakepotential.py
new file mode 100644
index 0000000000000000000000000000000000000000..02dee0325a3603c3405cfdea53b6168a9825322d
--- /dev/null
+++ b/tracking/wakepotential.py
@@ -0,0 +1,253 @@
+# -*- coding: utf-8 -*-
+"""
+This module defines the WakePotential class which deals with the single bunch 
+effects, both longitudinal and transverse.
+
+@author: Alexis Gamelin, Watanyu Foosang
+@date: 25/09/2020
+"""
+
+import numpy as np
+import matplotlib.pyplot as plt
+from scipy import signal
+from scipy.interpolate import interp1d
+from mbtrack2.tracking.element import Element
+
+class WakePotential(Element):
+    """
+    Compute a wake potential from wake functions by performing a convolution 
+    with a bunch charge profile.
+    
+    Parameters
+    ----------
+    ring : Synchrotron object
+    wakefield : Wakefield object
+        Wakefield object which contains the wake functions to be used.
+    n_bin : int, optional
+        Number of bins for constructing the longitudinal bunch profile.
+        
+    Attributes
+    ----------
+    rho : array of shape (n_bin, )
+        Bunch charge density profile in the unit [1/s].
+    Wp : array 
+        Wake potential profile.
+    Wp_interp : array of shape (mp_number, )
+        Wake potential, obtained from interpolating Wp, exerted on each macro-particle.
+    
+    Methods
+    -------
+    charge_density(bunch)
+        Compute bunch charge density profile in [1/s].
+    dipole_moment(bunch, plane)
+        Return the dipole moment of the bunch computed on the same time array 
+        as the bunch profile.
+    get_wakepotential(bunch, wake_type)
+        Return the wake potential computed on the same time array as the bunch 
+        profile.
+    track(bunch)
+        Tracking method for the element.
+    plot_last_wake(wake_type, plot_rho=True, plot_dipole=False)
+        Plot the last wake potential of a given type computed during the last
+        call of the track method.
+        
+    """
+    
+    def __init__(self, ring, wakefield, n_bin=65):
+        self.wakefield = wakefield
+        self.types = self.wakefield.wake_components
+        self.n_types = len(self.wakefield.wake_components)
+        self.ring = ring
+        self.n_bin = n_bin
+            
+    def charge_density(self, bunch):
+        """
+        Compute bunch charge density profile in [1/s].
+
+        Parameters
+        ----------
+        bunch : Bunch object
+
+        """
+        
+        # Get binning data
+        a, b, c = bunch.binning(n_bin=self.n_bin)
+        self.bins = a
+        self.sorted_index = b
+        self.profile = c
+        self.bin_size = self.bins.length
+        
+        # Compute charge density
+        self.rho = bunch.charge_per_mp*self.profile/(self.bin_size*bunch.charge)
+        self.rho = np.array(self.rho)
+        
+        # Compute time array
+        self.tau = np.array(self.bins.mid)
+        self.dtau = self.tau[1] - self.tau[0]
+        
+        # Add N values before and after rho and tau
+        if self.n_bin % 2 == 0:
+            N = int(self.n_bin/2)
+            self.tau = np.arange(self.tau[0] - self.dtau*N, 
+                                 self.tau[-1] + self.dtau*N,
+                                 self.dtau)
+            self.rho = np.append(self.rho, np.zeros(N))
+            self.rho = np.insert(self.rho, 0, np.zeros(N))
+        else:
+            N = int(np.floor(self.n_bin/2))
+            self.tau = np.arange(self.tau[0] - self.dtau*N, 
+                                 self.tau[-1] + self.dtau*(N+1),
+                                 self.dtau)
+            self.rho = np.append(self.rho, np.zeros(N))
+            self.rho = np.insert(self.rho, 0, np.zeros(N+1))
+            
+        if len(self.tau) != len(self.rho):
+            self.tau = np.append(self.tau, self.tau[-1] + self.dtau)
+            
+    def dipole_moment(self, bunch, plane):
+        """
+        Return the dipole moment of the bunch computed on the same time array 
+        as the bunch profile.
+
+        Parameters
+        ----------
+        bunch : Bunch object
+        plane : str
+            Plane on which the dipole moment is computed, "x" or "y".
+
+        Returns
+        -------
+        dipole : array
+            Dipole moment of the bunch.
+
+        """
+        dipole = np.zeros((self.n_bin,))
+        for i in range(self.n_bin):
+            dipole[i] = bunch[plane][self.sorted_index == self.bins[i]].sum()
+        dipole = dipole/self.profile
+        dipole[np.isnan(dipole)] = 0
+        
+        # Add N values to get same size as tau/rho
+        if self.n_bin % 2 == 0:
+            N = int(self.n_bin/2)
+            dipole = np.append(dipole, np.zeros(N))
+            dipole = np.insert(dipole, 0, np.zeros(N))
+        else:
+            N = int(np.floor(self.n_bin/2))
+            dipole = np.append(dipole, np.zeros(N))
+            dipole = np.insert(dipole, 0, np.zeros(N+1))
+            
+        setattr(self, "dipole_" + plane, dipole)
+        return dipole
+        
+    def get_wakepotential(self, bunch, wake_type):
+        """
+        Return the wake potential computed on the same time array as the bunch 
+        profile.
+
+        Parameters
+        ----------
+        bunch : Bunch object
+        wake_type : str
+            Wake function type: "Wlong", "Wxdip", ...
+
+        Returns
+        -------
+        Wp : array
+            Wake potential.
+
+        """
+        
+        tau0 = getattr(self.wakefield, wake_type).data.index
+        W0 = getattr(self.wakefield, wake_type).data["real"]
+        interp_func_W0 = interp1d(tau0, W0, fill_value=0, 
+                                     bounds_error=False)
+        W = interp_func_W0(self.tau)
+        if wake_type == "Wlong" or wake_type == "Wxquad" or wake_type == "Wyquad":
+            Wp = signal.convolve(self.rho, W*-1, mode='same')*self.dtau
+        elif wake_type == "Wxdip":
+            dipole = self.dipole_moment(bunch, "x")
+            Wp = signal.convolve(self.rho*dipole, W*-1, mode='same')*self.dtau
+        elif wake_type == "Wydip":
+            dipole = self.dipole_moment(bunch, "y")
+            Wp = signal.convolve(self.rho*dipole, W*-1, mode='same')*self.dtau
+        else:
+            raise ValueError("This type of wake is not taken into account.")
+        
+        setattr(self, wake_type, Wp)
+        return Wp
+    
+    @Element.parallel
+    def track(self, bunch):
+        """
+        Tracking method for the element.
+        No bunch to bunch interaction, so written for Bunch objects and
+        @Element.parallel is used to handle Beam objects.
+
+        Parameters
+        ----------
+        bunch : Bunch or Beam object.
+        
+        """
+
+        self.charge_density(bunch)
+        for wake_type in self.types:
+            Wp = self.get_wakepotential(bunch, wake_type)
+            f = interp1d(self.tau, Wp, fill_value = 0, bounds_error = False)
+            Wp_interp = f(bunch["tau"])
+            if wake_type == "Wlong":
+                bunch["delta"] += Wp_interp * bunch.charge / self.ring.E0
+            elif wake_type == "Wxdip":
+                bunch["xp"] += Wp_interp * bunch.charge / self.ring.E0
+            elif wake_type == "Wydip":
+                bunch["yp"] += Wp_interp * bunch.charge / self.ring.E0
+            elif wake_type == "Wxquad":
+                bunch["xp"] += (bunch["x"] * Wp_interp * bunch.charge 
+                                / self.ring.E0)
+            elif wake_type == "Wyquad":
+                bunch["yp"] += (bunch["y"] * Wp_interp * bunch.charge 
+                                / self.ring.E0)
+                
+    def plot_last_wake(self, wake_type, plot_rho=True, plot_dipole=False):
+        """
+        Plot the last wake potential of a given type computed during the last
+        call of the track method.
+
+        Parameters
+        ----------
+        wake_type : str
+            Type of the wake to plot: "Wlong", "Wxdip", ...
+        plot_rho : bool, optional
+            Plot the normalised bunch profile. The default is True.
+        plot_dipole : bool, optional
+            Plot the normalised dipole moment. The default is False.
+
+        Returns
+        -------
+        fig : figure
+
+        """
+        
+        labels = {"Wlong" : r"$W_{p,long}$ (V/pC)", 
+                  "Wxdip" : r"$W_{p,x}^{D} (V/pC)$",
+                  "Wydip" : r"$W_{p,y}^{D} (V/pC)$",
+                  "Wxquad" : r"$W_{p,x}^{Q} (V/pC/m)$",
+                  "Wyquad" : r"$W_{p,y}^{Q} (V/pC/m)$"}
+        fig, ax = plt.subplots()
+        Wp = getattr(self, wake_type)
+        ax.plot(self.tau*1e12, Wp*1e-12, label=labels[wake_type])
+        ax.set_xlabel("$\\tau$ (ps)")
+        ax.set_ylabel(labels[wake_type])
+
+        if plot_rho is True:
+            rho_rescaled = self.rho/max(self.rho)*max(np.abs(Wp))
+            ax.plot(self.tau*1e12, rho_rescaled*1e-12, label=r"$\rho$ (a.u.)")
+            plt.legend()
+            
+        if plot_dipole is True:
+            dipole = getattr(self, "dipole_" + wake_type[1])
+            dipole_rescaled = dipole/max(dipole)*max(np.abs(Wp))
+            ax.plot(self.tau*1e12, dipole_rescaled*1e-12, label=r"Dipole moment (a.u.)")
+            plt.legend()
+            
+        return fig
\ No newline at end of file