// This code simulates the heating that arises when DC current flows through a tungsten conductor 
// with a thin central part. The DC current flow is calculated with an electrokinetic formulation
// based on applied electric potentials at both ends and the temperature field is calculated 
// using the heat equation formulation, including the Joule heating due to the current flow.
// The dependency of the tungsten resistivity and its thermal conductivity on the temperature is taken into 
// account. Because of this the problem is nonlinear and requires an iterative resolution to converge.


#include "sparselizardbase.h"


using namespace mathop;

mesh createmesh(void);

void sparselizard(void)
{	
    // Region number 
    // - 1 corresponds to the whole volume
    // - 2 to the left face (input, electrically actuated)
    // - 3 to the right face (output, grounded)
    //
    int volume = 1, input = 2, output = 3;
    
    // Create the geometry and the mesh:    
    mesh mymesh = createmesh();
    
    // Write to file for visualization:
    mymesh.write("meshed.msh");

    // Voltage [V] applied between the input and output faces:
    double appliedvoltage = 0.2;
    
    // Create the electric potential field v and temperature field T (both with nodal shape functions h1):
    field v("h1"), T("h1");
    
    // Use an order 2 interpolation for both fields:
    v.setorder(volume,2);
    T.setorder(volume,2);
    
    // Apply the requested voltage to the input and ground the output:
    v.setconstraint(input, appliedvoltage);
    v.setconstraint(output);
    // Assume the temperature is fixed to 293 K on the input and output faces:
    T.setconstraint(input, 293);
    T.setconstraint(output, 293);
    
    // The tungsten resistivity 'rho' [Ohm*m] is a function of the temperature (5.60e-8 at 293 K with a 0.0045 [K^-1] temperature coefficient): 
    parameter rho, k;
    rho|volume = 5.6e-8*(1+0.0045*(T-293));
    // Its thermal conductivity varies from 173 to 118 [W/(m*K)] between 293 and 1000 K: 
    k|volume = 173-(173-118)*(T-293)/(1000-293);
    
    // Expression for the electric field E [V/m] and current density j [A/m^2]:
    expression E = -grad(v);
    expression j = 1/rho * E;

    
    // Define the weak formulation for the static current flow.
    //
    // The strong form is:
    //
    // div(1/rho * grad(v)) = 0
    //	
    // with E = -grad(v)
    //	
    formulation electrokinetics;
    
    electrokinetics += integral(volume, grad(tf(v))*1/rho*grad(dof(v)));
    
    // Define the weak formulation for the heat equation.
    //
    // Can be found at https://en.wikiversity.org/wiki/Nonlinear_finite_elements/Weak_form_of_heat_equation
    //
    // Strong form is r*cp*dT/dt - div(k*grad(T)) = volumic heat sources [W/m^3]
    //
    // where r [kg/m^3] is the density and cp [J/(kg*K)] is the specific heat capacity.
    //
    formulation heatequation;
    
    // Here the time derivative is zero (static simulation):
    // heatequation += integral(volume, r*cp*dt(dof(T))*tf(T));
    heatequation += integral(volume, grad(tf(T))*k*grad(dof(T)));
    // Add the current heating source:
    heatequation += integral(volume, -j*E*tf(T));


    
    // Start with a uniform 293 K temperature everywhere:
    T.setvalue(volume, 293);
    
    // Initial all-zero solution vector for the heat equation:
    vec solheat(heatequation);
    
    double relres = 1;
    while (relres > 1e-7)
    {
        // Compute the static current everywhere:
        electrokinetics.generate();
        // Get A and b to solve Ax = b:
        vec solv = solve(electrokinetics.A(), electrokinetics.b());
        // Transfer the data from the solution vector to the v field to be used for the heat equation below:
        v.setdata(volume, solv);
        
        // Deduce the temperature field everywhere:
        heatequation.generate();
        // Get A and b to solve Ax = b:
        mat Aheat = heatequation.A();
        vec bheat = heatequation.b();
        
        // Compute a relative residual:
        relres = (bheat - Aheat*solheat).norm() / bheat.norm();
        
        // Solve Ax = b:
        solheat = solve(Aheat, bheat);
        
        // Transfer the data from the solution vector to the T field to be used for the electrokinetics above:
        T.setdata(volume, solheat);
        
        std::cout << "Current iteration has relative residual: " << relres << std::endl;
    }

    // Compute the total current flowing trough the input face.
    // Since the computation involves a gradient that has to be 
    // calculated in the volume (and not on the input face) 
    // one can not simply call (normal(input)*j).integrate(input,4)
    // since with this a surface gradient will be calculated.
    // 'on()' is called to force the evaluation in the volume.
    double I = (-normal(input)*on(volume, j)).integrate(input, 4);
    // Compute the electric resistance R between input and output:
    double R = appliedvoltage/I;
    
    std::cout << std::endl << "Resistance is " << R << " Ohm. Current is " << I << " A" << std::endl;
    
    // Compute the peak temperature on the whole volume:
    double peaktemperature = T.max(volume,2)[0];
    std::cout << "Peak temperature is " << peaktemperature << " K" << std::endl << std::endl;
    
    // Write v, j and T:
    v.write(volume, "v.pos");
    j.write(volume, "j.pos");
    T.write(volume, "T.pos");
    
    // Code validation line. Can be removed.
    std::cout << (peaktemperature < 618.446 && peaktemperature > 618.444 && I < 1851.30 && I > 1851.28);
}

mesh createmesh(void)
{
    // Give names to the physical region numbers:
    int volume = 1, input = 2, output = 3;
    
    // Define the x, y and z coordinate fields:
    field x("x"), y("y"), z("z");
    
    double length = 0.2, thickness = 0.01, width = 0.05;
    
    // Define the 2D base to be extruded:
    shape leftquad("quadrangle", -1, {-length/2,-width/2,-thickness/2, -0.5*length/2,-width/2,-thickness/2, -0.5*length/2,width/2,-thickness/2, -length/2,width/2,-thickness/2}, {8,8,8,8});
    shape rightquad("quadrangle", -1, {0.5*length/2,-width/2,-thickness/2, length/2,-width/2,-thickness/2, length/2,width/2,-thickness/2, 0.5*length/2,width/2,-thickness/2}, {8,8,8,8});
    shape centralquad("quadrangle", -1, {-0.5*length/2,-width/2,-thickness/2, 0.5*length/2,-width/2,-thickness/2, 0.5*length/2,width/2,-thickness/2, -0.5*length/2,width/2,-thickness/2}, {16,8,16,8});
    
    // Extrude the 2D base:
    shape leftblock = leftquad.extrude(volume, thickness, 3);
    shape rightblock = rightquad.extrude(volume, thickness, 3);
    shape centralblock = centralquad.extrude(volume, thickness, 3);

    // Make the central block less large in the middle:
    centralblock.deform(0, 15*(abs(x)-0.5*length/2)*y, 0);
    // Make it also a little thinner (in the z direction) in the middle:
    centralblock.deform(0, 0, 15*(abs(x)-0.5*length/2)*z);
    
    // Get the input and output faces:
    shape inputface = leftblock.getsons()[4];
    shape outputface = rightblock.getsons()[2];
    
    // Assign the physical region numbers as detailed above:
    inputface.setphysicalregion(input);
    outputface.setphysicalregion(output);
    
    // Load to the mesh all shapes important for the finite element simulation:
    mesh mymesh({leftblock,rightblock,centralblock,inputface,outputface});
    
    // The mesh can be written at any time to have a feedback while creating the geometry!
    // mymesh.write("meshed.msh");

    return mymesh;
}

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

    sparselizard();

    SlepcFinalize();

    return 0;
}

