LOTS OF PROGRAMS 5


Here is the Maple code for working out the FEM using Harmonic splines:



> #This function creates the basis functions for the harmonic
#finite element method. The basis functions are quite simple:
#They are just 1 at one boundary, 0 at the other boundaries,
#and filled in harmonically the rest of the way. Here, I store
#(1,0,0) in funcno, (0,1,0) in funcno+1, and (0,0,1) in funcno+2,
#where the first number corresponds to the value at [0], the second num
#to the value at [1], and I think you can figure out the last case.
#It should be noted that funcno, funcno+1, and funcno+2 are all used up
#by this function.

calc_harms:=proc(T,m,funcno)

T[[0],funcno]:=1;
T[[1],funcno]:=0;
T[[2],funcno]:=0;
T[[0,1],funcno]:=2/5;
T[[1,2],funcno]:=1/5;
T[[0,2],funcno]:=2/5;
harm(T,m,[0],[0,1],[0,2],funcno);
harm(T,m,[1],[1,2],[0,1],funcno);
harm(T,m,[2],[0,2],[1,2],funcno);


T[[0],funcno+1]:=0;
T[[1],funcno+1]:=1;
T[[2],funcno+1]:=0;
T[[0,1],funcno+1]:=2/5;
T[[1,2],funcno+1]:=2/5;
T[[0,2],funcno+1]:=1/5;
harm(T,m,[0],[0,1],[0,2],funcno+1);
harm(T,m,[1],[1,2],[0,1],funcno+1);
harm(T,m,[2],[0,2],[1,2],funcno+1);


T[[0],funcno+2]:=0;
T[[1],funcno+2]:=0;
T[[2],funcno+2]:=1;
T[[0,1],funcno+2]:=1/5;
T[[1,2],funcno+2]:=2/5;
T[[0,2],funcno+2]:=2/5;
harm(T,m,[0],[0,1],[0,2],funcno+2);
harm(T,m,[1],[1,2],[0,1],funcno+2);
harm(T,m,[2],[0,2],[1,2],funcno+2);

end;

>

>

> #This function computes the inner product of two harmonic basis elements.
#It uses theoretical values for inner products of harmonic functions. It
#then scales down the answer by a factor of 3^m, because the triangles are
#smaller by a factor of 3^m.

inner_prod_h:=proc(S,m,basis_1,basis_2)

local out,temp,basis1,basis2;

basis1:=basis_1;
basis2:=basis_2;

#if they aren't neighbors, we get nothing...
if basis1[1] <> basis2[1] and S[basis1[1],1] <> basis2[1] and S[basis1[1],2] <> basis2[1] and S[basis1[1],3] <> basis2[1] and S[basis1[1],4] <> basis2[1] then RETURN(0) fi;


#If the 2 basis elts are the same, then we get 7/45*2 (the two comes from the 2 neighboring triangles)
#the value 7/45 is theoretical
if basis1[1]=basis2[1] then
out:=7/45 * 2 / (3^m);
fi;

#If the 2 basis elts are neighbors, then we get 4/45 (theoretical value)
if S[basis1[1],1] = basis2[1] or S[basis1[1],2] = basis2[1] or S[basis1[1],3] = basis2[1] or
S[basis1[1],4] = basis2[1]
then
out:=4/45 / (3^m);
fi;

out;
end;

> #This function returns the energy of two basis functions.
#It works just like inner_prod_h, except that it uses a scaling factor of
#(5/3)^m, which is the energy scaling factor from kigami.


energy_h:=proc(S,m,basis_1,basis_2)

local out,temp,basis1,basis2;

basis1:=basis_1;
basis2:=basis_2;

#if they aren't neighbors, we get nothing...
if basis1[1] <> basis2[1] and S[basis1[1],1] <> basis2[1] and S[basis1[1],2] <> basis2[1] and S[basis1[1],3] <> basis2[1] and S[basis1[1],4] <> basis2[1] then RETURN(0) fi;

if basis1[1]=basis2[1] then
out:=2 * 2 * (5/3)^m;
fi;
if S[basis1[1],1] = basis2[1] or S[basis1[1],2] = basis2[1] or S[basis1[1],3] = basis2[1] or
S[basis1[1],4] = basis2[1]
then
out:=-1 * (5/3)^m;
fi;

out;
end;

> #This function takes a basis element and a function, and returns integral(basis*function).
#The nice thing is that the basis element is zero everywhere except for 2 triangles. Thus,
#we can speed computations by just integrating over those particular triangles.
#Note that the integration method used here is simpson's rule out to maximum depth in the SG.

int_vs_basis_h:=proc(T,S,m_basis,m_func,basis,funct,harm_funcno)

local V0,Vother,Vm,fact,triangle1,triangle2,V0sum1,V0sum2,Vothersum1,Vothersum2,Vmsum1,Vmsum2,i,j,Vother_e;

fact:= 1/(6*3^m_func); #This is the factor for simpson's rule...


#get the vertices for V0, Vm, and everything in between...
#Notice that here we are taking m to be m_func-m_basis, because what we want to do is
#consider vertices scaled down by m_basis in funct and multiply by vertices in the basis

V0:=vertices_m(0);
if m_func-m_basis = 1 then Vother:=[] else Vother_e:=vertices(m_func-m_basis-1);Vother:=Vother_e[4..nops(Vother_e)]; fi;
Vm:=vertices_m(m_func-m_basis);

#all basis elements are of type f...
if basis[2]=f then

#So basically, we will first check the orientation of the basis element (i.e, the last two digits).
#Then, we get the triangles associated with the basis element. Then, given a point in Vm_func-m_basis,
#we can use coord_transfer to determine (using the triangle) which is the corresponding coordinate
#in Vm_func, and then multiply that by the corresponding value of the basis function, and sum.

if basis[1][nops(basis[1])-1..nops(basis[1])] = [0,1] then
triangle1:=get_triangle(S,basis,m_basis,1);
triangle2:=get_triangle(S,basis,m_basis,2);

#let's calculate sum's for triangle 1, shall we?
V0sum1:=add(T[coord_transfer(V0[i],triangle1,basis[1],S[basis[1],1],S[basis[1],2]),funct] *(T[V0[i],harm_funcno]),i=1..nops(V0));

Vothersum1:=add(T[coord_transfer(Vother[i],triangle1,basis[1],S[basis[1],1],S[basis[1],2]),funct] *(T[Vother[i],harm_funcno]),i=1..nops(Vother));

Vmsum1:=add(T[coord_transfer(Vm[i],triangle1,basis[1],S[basis[1],1],S[basis[1],2]),funct] *(T[Vm[i],harm_funcno]),i=1..nops(Vm));

#and now on to triangle numero 2!
V0sum2:=add(T[coord_transfer(V0[i],triangle2,S[basis[1],4],basis[1],S[basis[1],3]),funct]*(T[V0[i],harm_funcno+1] ),i=1..nops(V0));

Vothersum2:=add(T[coord_transfer(Vother[i],triangle2,S[basis[1],4],basis[1],S[basis[1],3]),funct] *(T[Vother[i],harm_funcno+1] ),i=1..nops(Vother));

Vmsum2:=add(T[coord_transfer(Vm[i],triangle2,S[basis[1],4],basis[1],S[basis[1],3]),funct]*(T[Vm[i],harm_funcno+1]),i=1..nops(Vm));

fi;

if basis[1][nops(basis[1])-1..nops(basis[1])] = [0,2] then
triangle1:=get_triangle(S,basis,m_basis,1);
triangle2:=get_triangle(S,basis,m_basis,2);

#let's calculate sum's for triangle 1, shall we?
V0sum1:=add(T[coord_transfer(V0[i],triangle1,S[basis[1],1],S[basis[1],2],basis[1]),funct]*(T[V0[i],harm_funcno+2] ),i=1..nops(V0));

Vothersum1:=add(T[coord_transfer(Vother[i],triangle1,S[basis[1],1],S[basis[1],2],basis[1]),funct] *(T[Vother[i],harm_funcno+2]),i=1..nops(Vother));

Vmsum1:=add(T[coord_transfer(Vm[i],triangle1,S[basis[1],1],S[basis[1],2],basis[1]),funct]*(T[Vm[i],harm_funcno+2]),i=1..nops(Vm));

#and now on to triangle numero 2!
V0sum2:=add(T[coord_transfer(V0[i],triangle2,basis[1],S[basis[1],3],S[basis[1],4]),funct]*(T[V0[i],harm_funcno] ),i=1..nops(V0));

Vothersum2:=add(T[coord_transfer(Vother[i],triangle2,basis[1],S[basis[1],3],S[basis[1],4]),funct] *(T[Vother[i],harm_funcno]),i=1..nops(Vother));

Vmsum2:=add(T[coord_transfer(Vm[i],triangle2,basis[1],S[basis[1],3],S[basis[1],4]),funct]*(T[Vm[i],harm_funcno] ),i=1..nops(Vm));

fi;

if basis[1][nops(basis[1])-1..nops(basis[1])] = [1,2] then
triangle1:=get_triangle(S,basis,m_basis,1);
triangle2:=get_triangle(S,basis,m_basis,2);

#let's calculate sum's for triangle 1, shall we?
V0sum1:=add(T[coord_transfer(V0[i],triangle1,S[basis[1],2],basis[1],S[basis[1],1]),funct]*(T[V0[i],harm_funcno+1]),i=1..nops(V0));

Vothersum1:=add(T[coord_transfer(Vother[i],triangle1,S[basis[1],2],basis[1],S[basis[1],1]),funct] *(T[Vother[i],harm_funcno+1]),i=1..nops(Vother));

Vmsum1:=add(T[coord_transfer(Vm[i],triangle1,S[basis[1],2],basis[1],S[basis[1],1]),funct]*(T[Vm[i],harm_funcno+1]),i=1..nops(Vm));

#and now on to triangle numero 2!
V0sum2:=add(T[coord_transfer(V0[i],triangle2,S[basis[1],3],S[basis[1],4],basis[1]),funct]*(T[V0[i],harm_funcno+2]),i=1..nops(V0));

Vothersum2:=add(T[coord_transfer(Vother[i],triangle2,S[basis[1],3],S[basis[1],4],basis[1]),funct] *(T[Vother[i],harm_funcno+2]),i=1..nops(Vother));

Vmsum2:=add(T[coord_transfer(Vm[i],triangle2,S[basis[1],3],S[basis[1],4],basis[1]),funct]*(T[Vm[i],harm_funcno+2]),i=1..nops(Vm));

fi;
fi;


#And then just return the formula for simpson's rule...
RETURN(fact * (5*(Vmsum1+Vmsum2) + 2*(Vothersum1+Vothersum2) + (V0sum1+V0sum2)));


end;



> #Here, we generate the matrix of E(basisi,basisj) out to level m_basis
#The final matrix is a sparse, symmetric matrix, and we take that into
#account here.

gen_energy_matrix_h:=proc(S,m_basis,vm)

local i,j,en_matrix,n_verts,en;

n_verts:=nops(vm)-3; sum(3^i,i=1..m_basis);

en_matrix:=array(sparse,1..n_verts,1..n_verts);

for i from 1 to n_verts do
for j from 1 to n_verts do
en:=energy_h(S,m_basis,get_basis(vm,i),get_basis(vm,j));
if en <> 0 then en_matrix[i,j]:=en; en_matrix[j,i]:=en; fi;
od;
od;

en_matrix;
end;

> #This function returns a basis element given any particular basis element number.
#Notice that this function works for both harmonic and biharmonic splines; if the
#number is greater than the number of f-type vectors, we return a g-type vector,
#but if we are using harmonic splines, we will never get a number that high, and
#so all we get are the f-type vectors.

get_basis:=proc(vm,number)

if number <= nops(vm)-3 then [vm[number+3],f]
else [vm[number-nops(vm)+3],g] fi; end;

> #This is just like gen_energy_matrix_h, except that creates the inner product matrix.

gen_iprod_matrix_h:=proc(S,m_basis,vm)

local i,j,i_matrix,n_verts,ip;

n_verts:=nops(vm)-3; add(3^i,i=1..m_basis);

i_matrix:=array(sparse,1..n_verts,1..n_verts);

for i from 1 to n_verts do
for j from 1 to n_verts do
ip:=inner_prod_h(S,m_basis,get_basis(vm,i),get_basis(vm,j));
if ip <> 0 then i_matrix[i,j]:=ip; i_matrix[j,i]:=ip; fi;
od;
od;

i_matrix;
end;

> #This procedure generates a vector of integral(funcno*basisi).
#It calls int_vs_basis_h, where most of the hard work is actually
#done.


gen_f_vector_h:=proc(T,S,m_basis,m_func,vm,funcno,harm_funcno)

local i,f_vec,n_verts;


n_verts:=add(3^i,i=1..m_basis);

f_vec:=array(1..n_verts);

for i from 1 to n_verts do
f_vec[i]:=int_vs_basis_h(T,S,m_basis,m_func,get_basis(vm,i),funcno,harm_funcno);
od;

f_vec;
end;

> #This function is called by make_pretty_picture_h. It basically goes through not assigning values to
#any vertices until it reaches the end of m_basis, and then it just fills everything in harmonically,
#basically making a piecewise harmonic function with boundary values at the harmonic spline basis
#vertices.

make_h_answer:=proc(T,m_basis,m_func,add0,add1,add2,funcno);

if m_basis = 0 then
# print(add0,add1,add2);
harm(T,m_func,add0,add1,add2,funcno);
else
if add1[nops(add1)-1..nops(add1)] = [0,1] then
make_h_answer(T,m_basis-1,m_func-1,add0,[op(add1[1..nops(add1)-2]),0,0,1],[op(add1[1..nops(add1)-2]),0,0,2],funcno);
make_h_answer(T,m_basis-1,m_func-1,add1,[op(add1[1..nops(add1)-2]),0,1,2],[op(add1[1..nops(add1)-2]),0,0,1],funcno);
make_h_answer(T,m_basis-1,m_func-1,add2,[op(add1[1..nops(add1)-2]),0,0,2],[op(add1[1..nops(add1)-2]),0,1,2],funcno);
fi;
if add1[nops(add1)-1..nops(add1)] = [1,2] then
make_h_answer(T,m_basis-1,m_func-1,add0,[op(add1[1..nops(add1)-2]),1,1,2],[op(add1[1..nops(add1)-2]),1,0,1],funcno);
make_h_answer(T,m_basis-1,m_func-1,add1,[op(add1[1..nops(add1)-2]),1,0,2],[op(add1[1..nops(add1)-2]),1,1,2],funcno);
make_h_answer(T,m_basis-1,m_func-1,add2,[op(add1[1..nops(add1)-2]),1,0,1],[op(add1[1..nops(add1)-2]),1,0,2],funcno);
fi;
if add1[nops(add1)-1..nops(add1)] = [0,2] then
make_h_answer(T,m_basis-1,m_func-1,add0,[op(add1[1..nops(add1)-2]),2,0,2],[op(add1[1..nops(add1)-2]),2,1,2],funcno);
make_h_answer(T,m_basis-1,m_func-1,add1,[op(add1[1..nops(add1)-2]),2,0,1],[op(add1[1..nops(add1)-2]),2,0,2],funcno);
make_h_answer(T,m_basis-1,m_func-1,add2,[op(add1[1..nops(add1)-2]),2,1,2],[op(add1[1..nops(add1)-2]),2,0,1],funcno);
fi;

fi;
end;

> #This function basically copies the values of C (a projective representation of a function
#in the harmonic spline basis) into a function, and then calls make_h_answer to fill in the
#rest of the function. This procedure is far more efficient than the method used in
#make_pretty_picture because there, we have to add in each basis function manually.
#Here, we can use the fact that the harmonic functions are completely defined
#by knowing the values of the function on the boundary and then just using the harmonic
#algorithm to determine the rest.

make_pretty_picture_h:=proc(T,m_basis,m_func,C,funcno)

local vm,j;
vm:=vertices(m_basis);

#first, set the initial values...
T[[0],funcno]:=0 ;T[[1],funcno]:=0; T[[2],funcno]:=0;

for j from 1 to nops([indices(C)]) do
T[vm[j+3],funcno]:=C[j];
od;

#then call make_h_answer to do all the real work...
make_h_answer(T,m_basis-1,m_func-1,[0],[0,1],[0,2],funcno);
make_h_answer(T,m_basis-1,m_func-1,[1],[1,2],[0,1],funcno);
make_h_answer(T,m_basis-1,m_func-1,[2],[0,2],[1,2],funcno);

end;

> #This function returns a list of which harmonic basis functions
#should be used for integrating versus two basis elements.
#It assumes that the basis elements are neighbors, because otherwise
#it's a fairly trivial question. It is called by int_vs_basis12_h.
#Notice that the values depend on orientation as well as neighbors...

which_funcs_h:=proc(S,basis1,basis2)
local out;


if last_digits(basis1[1]) = [0,1] then
if S[basis1[1],1] = basis2[1] then out:=[0,1] fi;
if S[basis1[1],2] = basis2[1] then out:=[0,2] fi;
if S[basis1[1],3] = basis2[1] then out:=[1,2] fi;
if S[basis1[1],4] = basis2[1] then out:=[1,0] fi;
fi;
if last_digits(basis1[1]) = [0,2] then
if S[basis1[1],1] = basis2[1] then out:=[2,0] fi;
if S[basis1[1],2] = basis2[1] then out:=[2,1] fi;
if S[basis1[1],3] = basis2[1] then out:=[0,1] fi;
if S[basis1[1],4] = basis2[1] then out:=[0,2] fi;
fi;
if last_digits(basis1[1]) = [1,2] then
if S[basis1[1],1] = basis2[1] then out:=[1,2] fi;
if S[basis1[1],2] = basis2[1] then out:=[1,0] fi;
if S[basis1[1],3] = basis2[1] then out:=[2,0] fi;
if S[basis1[1],4] = basis2[1] then out:=[2,1] fi;
fi;

out;
end;

> #This function is used to generate the matrix integral(q,basisi,basisj).
#It basically has to go through the logic of inner_prod_h, except that instead
#of just returning a theoretical value, it returns the appropriate integral.

int_vs_basis12_h:=proc(T,S,m_basis,m_func,basis_1,basis_2,funct,harm_funcno)

local out,temp,basis1,basis2,fact,triangle1,triangle2,addresses1,addresses2,j,which_harms;

fact:=1/(3^m_basis); #scaling factor due to m_basis
basis1:=basis_1;
basis2:=basis_2;
# print(basis1,basis2);

#if they aren't neighbors, we get nothing...
if basis1[1] <> basis2[1] and S[basis1[1],1] <> basis2[1] and S[basis1[1],2] <> basis2[1] and S[basis1[1],3] <> basis2[1] and S[basis1[1],4] <> basis2[1] then RETURN(0) fi;

if basis1[1]=basis2[1] then
triangle1:=get_triangle(S,basis1,m_basis,1); #first, let's get the triangles, as well as the addresses of the vertices of those triangles
triangle2:=get_triangle(S,basis1,m_basis,2);
addresses1:=get_addresses(S,basis1,m_basis,1); #this returns the addresses of the coordinates of the triangles
addresses2:=get_addresses(S,basis1,m_basis,2);

#Notice that if basis1 and basis2 are in the same place, then we have to integrate over two triangles
#Another important thing is that the harmonic basis elements that you integrate against dependent solely
#upon the orientation of the basis elements, since they are in the same place.
#Notice that a lot of the work here is done in integrate_basis12, which is in splines.mws.
if last_digits(basis1[1]) = [0,1] then
out:=integrate_basis12(T,m_func-m_basis,triangle1,addresses1[1],addresses1[2],addresses1[3],funct,harm_funcno+0,harm_funcno+0)+
integrate_basis12(T,m_func-m_basis,triangle2,addresses2[1],addresses2[2],addresses2[3],funct,harm_funcno+1,harm_funcno+1); fi;

if last_digits(basis1[1]) = [0,2] then
out:=integrate_basis12(T,m_func-m_basis,triangle1,addresses1[1],addresses1[2],addresses1[3],funct,harm_funcno+2,harm_funcno+2)+
integrate_basis12(T,m_func-m_basis,triangle2,addresses2[1],addresses2[2],addresses2[3],funct,harm_funcno+0,harm_funcno+0); fi;

if last_digits(basis1[1]) = [1,2] then
out:=integrate_basis12(T,m_func-m_basis,triangle1,addresses1[1],addresses1[2],addresses1[3],funct,harm_funcno+1,harm_funcno+1)+
integrate_basis12(T,m_func-m_basis,triangle2,addresses2[1],addresses2[2],addresses2[3],funct,harm_funcno+2,harm_funcno+2); fi;

fi; #END OF CASE WHERE BASIS1 and BASIS2 are in same place

#And so now what if the basis elements are neighbors
if S[basis1[1],1] = basis2[1] or S[basis1[1],2] = basis2[1] or S[basis1[1],3] = basis2[1] or S[basis1[1],4] = basis2[1] then
if S[basis1[1],1] = basis2[1] or S[basis1[1],2] = basis2[1] then
#This case is if the overlapping triangle is the first triangle of basis1 (in the clockwise sense).
triangle1:=get_triangle(S,basis1,m_basis,1); #first, let's get the triangle...
addresses1:=get_addresses(S,basis1,m_basis,1); #and the corresponding address...

which_harms:=which_funcs_h(S,basis1,basis2); #This function does most of the work for determining which basis functions to use.

out:=integrate_basis12(T,m_func-m_basis,triangle1,addresses1[1],addresses1[2],addresses1[3],funct, harm_funcno+which_harms[1],harm_funcno+which_harms[2]);
else

#This case is if the overlapping triangle is the second triangle of basis1 (in the clockwise sense).
#Otherwise, it's just like before.
triangle1:=get_triangle(S,basis1,m_basis,2);
addresses1:=get_addresses(S,basis1,m_basis,2);
which_harms:=which_funcs_h(S,basis1,basis2);
out:=integrate_basis12(T,m_func-m_basis,triangle1,addresses1[1],addresses1[2],addresses1[3],funct, harm_funcno+which_harms[1],harm_funcno+which_harms[2]);
fi;

fi;

out*fact; #Don't forget the scaling factor!
end;


> #This function generates the matrix integral(q*basisi,basisj) which is used for q-nonconstant
#in the finite element method. The matrix is sparse and symmetrical, and both of these properties are
#used here. Most of the work is done in int_vs_basis12_h.


gen_q_matrix_h:=proc(T,S,m_basis,m_func,vm,funcno,harm_funcno)

local i,j,q_matrix,q_val,n_verts;

n_verts:=nops(vm)-3;

q_matrix:=array(sparse,1..n_verts,1..n_verts);


for i from 1 to n_verts do

printf(`%d `,i);
if ((i mod 15)=1) then
printf(`\n`): fi:

for j from 1 to i do
q_val:=int_vs_basis12_h(T,S,m_basis,m_func,get_basis(vm,i),get_basis(vm,j),funcno,harm_funcno);
if q_val <> 0 then q_matrix[i,j]:=q_val; q_matrix[j,i]:=q_val;fi;
od;
od;

q_matrix;
end;


Previous page Next page
Back to Lots of Programs