// This code simulates the magnetic field created by an imposed current density
// in an aluminium wire when a magnetic shield (a steel cylinder) is placed nearby.
//
// This example illustrates the resolution of a general magnetostatic problem with
// current density sources on a 3D geometry. It uses the magnetic vector potential
// based on edge shape functions 'hcurl' and combined with a gauge. The gauge is
// needed because the algebraic system to solve is singular. The effect of the gauge
// is to remove all degrees of freedom associated to higher order gradient type shape 
// functions from the algebraic system. Additionally all degrees of freedom associated
// to lowest order (order 0) edge shape functions are removed for all edges in a
// spanning tree. After having removed these two types of degrees of freedom the 
// matrix becomes non-singular and can be solved with a usual direct solver.
// 
// It is worth noting that the spanning tree growth HAS TO START on the regions
// where the magnetic vector potential field 'a' is constrained. This is to avoid
// issues of forcing the value of the magnetic flux through faces where we do not 
// want it to be constrained.


#include "sparselizardbase.h"


using namespace mathop;

void sparselizard(void)
{	
	// Give names to the region numbers defined in the mesh file:
	int conductor = 1, shield = 2, air = 3, contour = 4;

	// Load the mesh:
	mesh mymesh("magmesh.msh");

	// Define the whole volume region for convenience:
	int wholedomain = regionunion({conductor,shield,air});

	// Define the magnetic permeability mu [H/m] in the air, conductor (aluminium) and magnetic shield (steel):
	double mu0 = 4*getpi()*1e-7;
	
	parameter mu;

	mu|air = mu0;
	mu|conductor = mu0;
	mu|shield = 1000*mu0;

	// Define a spanning tree to gauge the magnetic vector potential (otherwise the matrix is singular).
	// Start growing the tree from the regions with constrained potential vector (here the contour): 
	spanningtree spantree({contour});
	// Write it for illustration:
	spantree.write("spantree.pos");

	// Define the magnetic vector potential 'a' and provide the tree. Use edge shape functions 'hcurl'.
	field a("hcurl", spantree);

	// Gauge the vector potential field on the whole volume:
	a.setgauge(wholedomain);

	// Use higher interpolation orders where needed. Use order 1 (default) everywhere else:
	a.setorder(shield, 2);

	// Put a magnetic wall (i.e. set field 'a' to 0) all around the domain (no magnetic flux can cross it):
	a.setconstraint(contour);

	// Define the magnetostatic formulation:
	formulation magnetostatics;

	// The strong form of the magnetostatic formulation is curl( 1/mu * curl(a) ) = j, with b = curl(a):
	magnetostatics += integral(wholedomain, 1/mu* curl(dof(a)) * curl(tf(a)) );
	// A current density of 1A/m^2 flows in the z direction in the conductor region:
	magnetostatics += integral(conductor, -array3x1(0,0,1)*tf(a));

	// Generate the algebraic matrices A and vector b of the Ax = b problem:
	magnetostatics.generate();

	// Get the x solution vector:
	vec sol = solve(magnetostatics.A(), magnetostatics.b());

	// Update field 'a' with the solution that has just been computed:
	a.setdata(wholedomain, sol);
	
	// Write the magnetic induction field b = curl(a) [T]:
	curl(a).write(wholedomain, "b.pos");
	
	// Code validation line. Can be removed:
	std::cout << (norm(a).max(wholedomain,4)[0] < 1.96437e-08 && norm(a).max(wholedomain,4)[0] > 1.96435e-08);
}

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

    sparselizard();

    SlepcFinalize();

    return 0;
}

