thanks for sharing "Sherwin and Karniadakis, hp/spectral element book". i was completely unaware of this element. the co-incident is that, i made complete solver without any mesh software in python which is resemble this concept. of course, "statue of liberty" is also possible! but i have to take help of Blender (modeling software, it is also made in python) for generation of node coordinates. one question i would like to ask, as you mentioned in post "If the angle becomes smaller, your error will increase". but, i just displacing nodes to make small angle in element and which is completely in accordance (obeying geometry rules) with the geometry, then why should be there error? i am not discretizing any thing. for example, i have solved Laplace equation with Dirichlet and Neumann boundary condition,
the domain and solution are shown in above figure. does it has error because of due to small angles?
i am giving snippet of codes to show how i put geometrical data and processed in solver. it is my own code except linear algebra pack.
import math
import mesh
import solver
import time
import numpy as np
from scipy.linalg import eigh
#if f and rho are time dependent then you have to pass time and store all element matrices k,f according to time. M is not time dependent
## setting functional
p=2#order of approximation. (p+1)*(p+1)=nodes, here you can increase any order basis function to compute the solution.
# relation between gauss points and degree of polynomial is: p=2n−1 where p=degree of polynomial and n=gauss points. therefore=(p-1)/2(observation: put same p=n)
solver.GP=p+1 #warning increase the gauss points according to polynomial degree otherwise, solution fluctuate
solver.F=f"0"# you can use any f(x,y) here, it is multiped with shape function in solver.
solver.RHO=f"1.0"
solver.setMassTime(massMatrix=False,time=False)# if you want to mass matrix set this true
# solver.FUNCTIONAL=f"delfx('{shape
}')*delfx('{shape[j]}')+delfy('{shape}')*delfy('{shape[j]}')"
# poissons weak form is int(dNi/dx *dNj/dx + dNi/dy* dNj/dy)dxdy=int(fNi) dxdy+ int(qNi)dl
#importing mesh data
totalElements,totalNodes,npe,cords,lg,dirich,neum,robin=mesh.data('laplace_dirich.txt')
# right hand side functions f and rho are set in solver getElementMatrices()
solver.setFunctional(f"delfx($i)*delfx($j)+delfy($i)*delfy($j)",p)#$i and $j are shape functions
# if you want to use 9 element node and don't want to make higher order then use
# transEq=sh.getTransEq(cords,p)
# npe=nodes per element.
#elementsCords[elNo][localNo]=(x,y)
original_order=int(math.sqrt(npe)-1) # original nodes per element
elementsCords, npe=solver.getHigherNodes(cords,p,original_order)#[[(),(),()],[(),(),()]]
stiffMatrices,massMatrices,loads=solver.getElementMatrices(elementsCords,p)# each element cordinates, order
load=solver.setNeumannBC(elementsCords,loads,neum,p)
# stiffMatrices=solver.setRobinBC(elementsCords,stiffMatrices,robin,p)
globalNumbers,totalNodes=solver.getGlobalNumbers(elementsCords)
#you can give condition here whether you want mass matrix or not...
globalStiff,globalMass,globalLoad=solver.assemble(stiffMatrices,massMatrices,load,globalNumbers,totalNodes,p)
GK,GM,GL=solver.setDirichletBC(elementsCords,globalStiff,globalMass,globalLoad,globalNumbers,dirich,p)
##############Solving eigenvalue problem separation of variable method#############
############## Mx=lambda*Kx
# eigvals, eigvecs = eigh(GK, GM)
# cutoffWavenumbers=np.array([math.sqrt(i) for i in eigvals])
# print(cutoffWavenumbers)
# print(cutoffWavenumbers*((3*10**8)/(2*np.pi))*100)
###################################################################################
###########################solving Eliptical problem, KU=F#######################################
U=np.linalg.solve(GK,GL)
print(np.array(U).reshape((p+1),(p+1)))
####################Generating intermediate solution.
division=10
# Z,XY=solver.approximateSolution(U,elementsCords,globalNumbers,p,division)# value of division must be as like order p, and it must greater than p
Z,XY=solver.approximateSolution(U,elementsCords,globalNumbers,p,division)# value of division must be as like order p, and it must greater than p
# time to plot
x=[x for x,y in XY]
y=[y for x,y in XY]
import matplotlib.pyplot as plt
fig=plt.figure()
ax=fig.add_subplot(1,2,1,projection='3d')
# ax.scatter(x,y,Z)# all one dimensional array
ax2=fig.add_subplot(1,2,2)
ax2.scatter(x,y,color="black",s=3)
x=np.array(x).reshape(division,division)
y=np.array(y).reshape(division,division)
Z=np.array(Z).reshape(division,division)
# ax.contourf(x,y,Z)
ax.plot_surface(x,y,Z)
# ax.plot_wireframe(x,y,Z)
plt.show()
#------------------Geometrical data:
#sovled and checked...
#total element, total nodes,nodes per element
$te,1
$tn,4
$npe,4
#elementNum,localNo,x,y nodes coordinates..
$cordStart
1,1,-0.9999999776482582,-0.9999999776482582
1,2,0.980785209685564,0.19509041449055076
1,3,0.19509023986756802,0.9807853028178215
1,4,2.4243127554655075,1.9495416432619095
$cordEnd
#elementNo,localNo,globalNo: numbering # no need to give local global numbering, solver will automatically detect near by elements and gives numbering.
#max of global number is total nodes
$lgStart
$lgEnd
#elementNo,edgeNo,value
$dirichStart
1,1,0
1,3,200*x
1,4,0
1,2,200*y
$dirichEnd
#elementNo,edgeNo, value, you can wirte value as f(x,y) without quoat
$neumStart
1,3,0
1,1,0
$neumEnd
$robinStart
$robinEnd