// This code simulates the electromagnetic wave radiation of a half-wave dipole antenna excited at 1 GHz.
// The simulation is performed in 2D (i.e. the fields are constant in the z direction). The goal of this code 
// is to provide a lightweight example that can be used with minimal modifications to simulate actual 3D antennas. 
//
// The radiation pattern of this dipole antenna can be obtained by calling 'interpolate' on the time-averaged power 
// expression for the points of a circle near the domain boundary and centered at the origin. The interpolated
// values can then be saved to .csv using 'writevector' and 'polarplot' could be used in Matlab for visualization. 


#include "sparselizardbase.h"


using namespace mathop;

void sparselizard(void)
{	
    wallclock clk;
    
    // The domain regions as defined in 'dipoleantenna.geo':
    int air = 1, conductor = 2, feed = 3, boundary = 4;
    
    mesh mymesh("dipoleantenna.msh");
    
    // Define the whole non-conducting region for convenience:
    int wholedomain = regionunion({air, feed});
    
    // Edge shape functions 'hcurl' for the electric field E.
    // Because of the propagating EM waves the electric field has an 
    // in-phase and quadrature component (thus harmonics 2 and 3 are used):
    // E = E2 * sin(2*pi*1GHz*t) + E3 * cos(2*pi*1GHz*t).
    field E("hcurl", {2,3});
    field Es = E.harmonic(2), Ec = E.harmonic(3);
    
    // Use interpolation order 2 on the whole domain:
    E.setorder(wholedomain, 2);
    
    // Define the speed of light [m/s] and the working frequency [Hz].
    // The total half-wave dipole antenna length is 15 cm. This corresponds to a 1GHz frequency:
    double c = 3e8, freq = 1e9;
    
    setfundamentalfrequency(freq);    
    
    // The dipole is a perfect conductor. We thus force E to 0 on it:
    E.setconstraint(conductor);
    // We force an electric field of 1 V/m in the y direction on the feed port 
    // for the sin component of E only (the cos component is forced to zero):
    Es.setconstraint(feed, array3x1(0,1,0));
    Ec.setconstraint(feed);
    
    formulation maxwell;
    
    // This is the weak formulation in time for electromagnetic waves:
    maxwell += integral(wholedomain, -curl(dof(E))*curl(tf(E)) - 1/(c*c)*dtdt(dof(E))*tf(E));
    
    // The OUTWARD pointing normal is required for the wave radiation condition.
    // Depending on the 'boundary' lines orientation the normal can be flipped.
    // To confirm the normal direction it is written to disk below:
    expression n = -normal(boundary);
    n.write(boundary, "normal.pos");
    
    // Silver-Mueller radiation condition to force outgoing electromagnetic waves:
    maxwell += integral(boundary, -1/c * crossproduct(crossproduct(n, dt(dof(E))), n) * tf(E)); 
    
    // Generate the algebraic matrices A and right handside b of Ax = b:
    maxwell.generate();
    
    // Get the solution vector x of Ax = b:
    vec solE = solve(maxwell.A(), maxwell.b());
    
    // Transfer the data in the solution vector to field E:
    E.setdata(wholedomain, solE);    
    
    clk.print("Resolution time (before post-processing):");
    
    // Save the electric field E and magnetic field H with an order 2 interpolation.
    // Since from Maxwell -mu0*dt(H) = curl(E) we have that H = 1/(mu0*w^2) * curl(dt(E)).
    double mu0 = 4*getpi()*1e-7;
    
    expression H = 1/(mu0*pow(2*getpi()*freq, 2)) * curl(dt(E));
    
    H.write(wholedomain, "H.pos", 2);
    E.write(wholedomain, "E.pos", 2);
    // Write the electric field at 50 timesteps of a period for a time visualization: 
    E.write(wholedomain, "E.pos", 2, 50);
    
    // Write the Poynting vector E x H.
    expression S = crossproduct(E, H);
    // This involves a product that makes new harmonics appear at 2x1GHz (harmonics 4 and 5) plus a 
    // constant component (harmonic 1). An FFT is thus required (performed on 6 timesteps below):
    S.write(wholedomain, 6, "S.pos", 2);
    // Also save in time at 50 timesteps of a period:
    S.write(wholedomain, "S.pos", 2, 50);
    
    // Code validation line. Can be removed.
    std::cout << (norm(crossproduct(Es,Ec)).max(wholedomain, 5)[0] < 0.00218416 && norm(crossproduct(Es,Ec)).max(wholedomain, 5)[0] > 0.00218413);
}

int main(void)
{	
    SlepcInitialize(0,{},0,0);

    sparselizard();

    SlepcFinalize();

    return 0;
}

