// This code simulates the harmonic mechanical deflection of a 3D monocrystalline silicon - PZT microbilayer when 
// the PZT (a piezoelectric material) is sandwiched between two electrically actuated electrodes.
//
// The anisotropic behavior of both silicon and PZT is fully taken into account.
// The silicon and PZT crystals are rotated to align with a selected orientation.


#include "sparselizardbase.h"


using namespace mathop;

void sparselizard(void)
{	
    // The domain regions as defined in 'bilayer.geo':
    int pztlayer = 1, siliconlayer = 2, electrode = 3, ground = 4, clamp = 6, freeside = 7, elecbox = 8, mecabox = 9;
    
    mesh mymesh("bilayer.msh");
    
    int wholedomain = regionunion({pztlayer, siliconlayer});
    
    // Harmonic analysis. Set the fundamental frequency [Hz]:
    setfundamentalfrequency(1e4);
    
    // Nodal shape functions 'h1' for v (the electric potential) and u 
    // (the mechanical displacement). Three components are used for u.
    // Use harmonic 2, i.e. u(x,t) = U(x)*sin(2pi*f0*t) for u and 
    // v(x,t) = V(x)*sin(2pi*f0*t) for v.
    //
    field u("h1xyz",{2}), v("h1",{2});
    
    // Use interpolation order 2 on 'wholedomain' for u and 1 for v in the PZT layer:
    u.setorder(wholedomain, 2);
    v.setorder(pztlayer, 1);
    
    // Clamp on surface 'clamp' (i.e. 0 valued-Dirichlet conditions):
    u.setconstraint(clamp);
    
    v.harmonic(2).setconstraint(electrode, 10);	
    v.setconstraint(ground, 0);	
    
    
    // Silicon and PZT density [kg/m^3]:
    double rhosi = 2330, rhopzt = 7500;
    
    // Diagonal relative permittivity matrix for PZT:
    expression K(3,3,{1704,1704,1433});
    K = K * 8.854e-12;
    
    // Coupling matrix [C/m^2] for PZT (6 rows, 3 columns):
    expression C(6,3,{0,0,-6.62, 0,0,-6.62, 0,0,23.24, 0,17.03,0, 17.03,0,0, 0,0,0});
    
    // Anisotropic elasticity matrix [Pa] for silicon and PZT. Ordering is [exx,eyy,ezz,gyz,gxz,gxy] (Voigt form).
    // Only the lower triangular part (top to bottom and left to right) is provided since it is symmetric.
    expression Hsi(6,6, {194.5e9, 35.7e9,194.5e9, 64.1e9,64.1e9,165.7e9, 0,0,0,79.6e9, 0,0,0,0,79.6e9, 0,0,0,0,0,50.9e9});
    expression Hpzt(6,6, {1.27e11, 8.02e10,1.27e11, 8.46e10,8.46e10,1.17e11, 0,0,0,2.29e10, 0,0,0,0,2.29e10, 0,0,0,0,0,2.34e10});
    
    // Rotate the silicon and PZT crystals first by -30 degrees around y then by 45 degrees around z.
    // Refer to the documentation to make sure you understand how to use 'rotate'!
    Hsi.rotate(0,-30,45,"K","KT"); Hpzt.rotate(0,-30,45,"K","KT"); C.rotate(0,-30,45,"K","RT"); K.rotate(0,-30,45);
    
    
    formulation piezo;
    
    // Orthotropic elasticity in the silicon (not piezoelectric):
    piezo += integral(siliconlayer, predefinedelasticity(dof(u), tf(u), Hsi) );
    // Inertia term:
    piezo += integral(siliconlayer, -rhosi*dtdt(dof(u))*tf(u) );
    
    // The classical weak formulation for piezoelectricity below can be found e.g. in paper:
    //
    // "A new fnite-element formulation for electromechanical boundary value problems"
    
    // Define the mechanical equations of the problem in the piezo.
    // strain(u) returns the strain vector [exx,eyy,ezz,gyz,gxz,gxy] of u.
    piezo += integral(pztlayer, -( Hpzt*strain(dof(u)) )*strain(tf(u)) - ( C*grad(dof(v)) )*strain(tf(u)) );
    // Inertia term for PZT:
    piezo += integral(pztlayer, -rhopzt*dtdt(dof(u))*tf(u) );
    // Define the electrical equations of the problem in the piezo:
    piezo += integral(pztlayer, ( transpose(C)*strain(dof(u)) )*grad(tf(v)) - ( K*grad(dof(v)) )*grad(tf(v)) );
    
    solve(piezo);
    
    // Display the peak displacement:
    double umax = norm(u.harmonic(2)).max(wholedomain, 5)[0];
    std::cout << "Peak deflection is " << umax << " m" << std::endl;
    
    // Write the deflection and electric potential on the volume boundary.
    u.write(mecabox, "u.vtk", 2);
    v.write(elecbox, "v.vtk", 1);
    // Harmonic fields can be saved in time. Use 50 time steps:
    u.write(mecabox, "utime.vtk", 2, 50);
    v.write(elecbox, "vtime.vtk", 1, 50);
    
    // Code validation line. Can be removed.
    std::cout << (umax < 1.7386e-07 && umax > 1.7384e-07);
}

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

    sparselizard();

    SlepcFinalize();

    return 0;
}

