I made some progress.
Using md.matrix_term() instead of md.tangent_matrix() I could retrieve all the matrix components
C = mass matrix
K = conductivity matrix + convection matrix
and together with the RHS term from the model
RHS = md.rhs()
I was able to construct my rhs named Q
Q^n =RHS^n-K \,(T^{n-1}-dt\,(1-\theta)\,\dot T^{n-1})
and build my \theta-method for linear problems, that has given the same results as the standard md.Solve(), by solving the following quation for \dot T^n:
[C+K*dt*\theta] * \dot T^n = Q^n
and then calculating
T^n = T^{n-1}+dt*(\theta\, \dot T^n+(1-\theta)\,\dot T^{n-1})
I implemented also the following method, giving directly T as a result without needing \dot T, valid also for explicit problems.
Q^n = RHS^n + (C/dt-(1-\theta)K)T^{n-1}
[C/dt+K\,\theta] T^n = Q^n
What I donāt still understand, and I hope you can help me in this connection, is why the RHS is assembled differently if I run md.Solve() before running my own solver, as shown running the following code snippet.
import getfem as gf
linear_length = 0.1
linear_elements = 1
X = [round(x*linear_length/linear_elements, 3) for x in range(linear_elements+1)]
mesh = gf.Mesh("cartesian", X, X)
BRG_all = 1
mesh.set_region(BRG_all, mesh.outer_faces())
#print(mesh)
mft = gf.MeshFem(mesh, 1) # scalar
mft.set_classical_fem(1) # order 1
mim = gf.MeshIm(mesh, 3) # degree 3
md = gf.Model("real")
md.add_fem_variable("T", mft)
#md.variable_list()
conductivity = md.add_linear_term(mim, '-25*Grad_T.Grad_Test_T')
convection = md.add_linear_term(mim, '-25*(T-15)*Test_T', BRG_all)
theta = 1
md.add_theta_method_for_first_order('T', theta)
mass = md.add_linear_term(mim, "-7850*580*Dot_T*Test_T")
dt=0.5
md.set_time_step(dt)
# implicit theta method similar to the standard Solve()
print("\n\n RUNNING MY SOLVER")
md.set_variable('T', [900 for x in range(md.nbdof())])
md.set_variable('Previous_T', [900 for x in range(md.nbdof())])
md.set_variable('Dot_T', [0 for x in range(md.nbdof())])
md.set_variable('Previous_Dot_T', [0 for x in range(md.nbdof())])
md.assembly('build_all')
C = md.matrix_term(mass, 0)
K = md.matrix_term(conductivity, 0)+md.matrix_term(convection, 0)
RHS = md.rhs()
for i in range(3):
md.assembly('build_all')
M=C+theta*K*dt
Q=RHS-K*(md.variable("Previous_T")-dt*(1-theta)*md.variable("Previous_Dot_T"))
md.set_variable('Dot_T', gf.linsolve_mumps(M, Q).flatten())
md.set_variable("T", md.variable("Previous_T")+dt*(theta*md.variable("Dot_T")+(1-theta)*md.variable("Previous_Dot_T")))
print("T = ",md.variable("T"))
md.shift_variables_for_time_integration()
# implicit or explitic theta method
print("\n\n RUNNING MY SECOND VERSION OF THE SOLVER")
md.set_variable('T', [900 for x in range(md.nbdof())])
md.set_variable('Previous_T', [900 for x in range(md.nbdof())])
md.set_variable('Dot_T', [0 for x in range(md.nbdof())])
md.set_variable('Previous_Dot_T', [0 for x in range(md.nbdof())])
md.assembly('build_all')
C = md.matrix_term(mass, 0)*(1/dt)
K = md.matrix_term(conductivity, 0)+md.matrix_term(convection, 0)
RHS = md.rhs()
theta=0.5
for i in range(3):
md.assembly('build_all')
M=C+theta*K
Q=RHS+(C+(theta-1)*K)*md.variable("T")
md.set_variable('T', gf.linsolve_mumps(M, Q).flatten())
print("T = ",md.variable("T"))
# md.Solve()
print("\n\n RUNNING STANDARD SOLVE")
md.set_variable('T', [900 for x in range(md.nbdof())])
md.set_variable('Previous_T', [900 for x in range(md.nbdof())])
md.set_variable('Dot_T', [0 for x in range(md.nbdof())])
md.set_variable('Previous_Dot_T', [0 for x in range(md.nbdof())])
for i in range(3):
nit, conv = md.solve()
print("T = ",md.variable("T"))
md.shift_variables_for_time_integration()
# test after having run md.Solve()
print("\n\n RUNNING AGAIN MY SOLVER")
md.set_variable('T', [900 for x in range(md.nbdof())])
md.set_variable('Previous_T', [900 for x in range(md.nbdof())])
md.set_variable('Dot_T', [0 for x in range(md.nbdof())])
md.set_variable('Previous_Dot_T', [0 for x in range(md.nbdof())])
md.assembly('build_all')
C = md.matrix_term(mass, 0)
K = md.matrix_term(conductivity, 0)+md.matrix_term(convection, 0)
RHS = md.rhs()
for i in range(3):
md.assembly('build_all')
M=C+theta*K*dt
Q=RHS-K*(md.variable("Previous_T")-dt*(1-theta)*md.variable("Previous_Dot_T"))
md.set_variable('Dot_T', gf.linsolve_mumps(M, Q).flatten())
md.set_variable("T", md.variable("Previous_T")+dt*(theta*md.variable("Dot_T")+(1-theta)*md.variable("Previous_Dot_T")))
print("T = ",md.variable("T"))
md.shift_variables_for_time_integration()
print("\n\n RUNNING AGAIN MY SECOND SOLVER")
md.set_variable('T', [900 for x in range(md.nbdof())])
md.set_variable('Previous_T', [900 for x in range(md.nbdof())])
md.set_variable('Dot_T', [0 for x in range(md.nbdof())])
md.set_variable('Previous_Dot_T', [0 for x in range(md.nbdof())])
md.assembly('build_all')
C = md.matrix_term(mass, 0)*(1/dt)
K = md.matrix_term(conductivity, 0)+md.matrix_term(convection, 0)
RHS = md.rhs()
theta=0.5
for i in range(3):
md.assembly('build_all')
M=C+theta*K
Q=RHS+(C+(theta-1)*K)*md.variable("T")
md.set_variable('T', gf.linsolve_mumps(M, Q).flatten())
print("T = ",md.variable("T"))
It seems to me that the md.Solve() procedure replaces the initial RHS with Q^n =RHS^n-K \,(T^{n-1}-dt\,(1-\theta)\,\dot T^{n-1})
and as a consequence Iām no more able to access the original RHS after running md.Assembly().
Is there a way to reset the RHS calculation procedure or have you got any suggestion how should I proceed?
Thank you