-
Notifications
You must be signed in to change notification settings - Fork 53
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Pressure Poisson equation giving wrong results #16
Comments
Your usage looks correct; you are creating a matrix that maps the pressure at |
Thanks for the reply.
I believe this is how b.c. are enforced in the examples provided with the repo. Any idea of what I might be doing wrong? Many thanks! |
That all looks good to me. If you set |
Setting I think I'm doing something wrong when assembling the right hand side from the known velocity field. Do you know where I could find a minimal code example of how to use RBF for solving laminar, steady state Navier-Stokes? I'm working on 3D data, but even a 2D basic channel flow example would be great. Thanks again for the help! |
I noticed that the units were not matching up in your rhs. Try creating it as:
|
It's exactly the same, isn't it? I get identical results. :( |
It would be the same if |
And then I compute, for instance, the derivative of u w.r.t. x as Are these correct? Thanks for the help, very much appreciated. |
that looks right. Do the results look like the issue is numerical instability, or just solving the wrong equation? If there are numerical instability issues, maybe try setting |
I tried lots of combinations for |
What are you using for the time derivatives |
I'm working with velocity data from a transient CFD simulation. A curved pipe with one inlet and one outlet. A constant velocity is applied at the inlet, therefore the velocity fields at to consecutive timesteps are almost identical. The time derivatives are in fact close to zero. I tried removing them, but there's no significant difference. |
I am not too familiar with fluid mechanics, but wouldn't you also need some sort of boundary condition like u=0 at the sides of the pipe? |
That's correct, but in my case, I'm not actually solving for velocity. I want to recover the pressure field from the pre-computed velocity field. So I assumed I should not worry about the no slip condition when solving for pressure or when I compute spatial derivatives. |
I see your point that the no slip condition would not be helpful for your problem. Are there any other boundary conditions that would make sense at the sides of the pipe? From the perspective of solving Poisson's equation, my understanding is that you should have some combination of Dirichlet and Neumann boundary conditions around the entire domain in order for the problem to be well-posed. What is the condition number for your LHS? Would it make sense to enforce that dp/dn = 0 at the boundary, where p is pressure and n is the normal vector to the boundary? |
I guess I could try to enforce dp/dn = 0 at the wall. I should use ghost nodes to do that, right? I was able to add one layer of ghost nodes along each wall node's normal, but I'm not sure how distant to the wall I should place them. If I do this, how would I implement the dp/dn = 0 condition? Thanks! |
Yup, that would be the issue. The matrix is very close to singular. You could first try it without ghost nodes. The solution would be much less accurate without the ghost nodes, but you would be able to see if the dp/dn = 0 boundary conditions get you closer to the pressures you are expecting. Implementing the dp/dn = 0 boundary conditions without ghost nodes is easy as long as you have the outward normal vectors at the boundaries. Creating the matrix that maps the pressures at
Once you have that matrix, implementing the dp/dn=0 boundary condition is just like implementing the fixed boundary condition. Ghost nodes require a little more effort to implement. If you look at the second example down here https://rbf.readthedocs.io/en/latest/fd.html, you can see an example of solving Poisson's equation with a mix of fixed and free boundary conditions, where the free boundaries have ghost nodes. When I generate ghost nodes, each ghost node's spacing from the boundary is equal to half the nearest neighbor distance for its corresponding boundary node. However, that was a pretty arbitrary decision. |
I think the use of Thanks |
Thanks for pointing this out. It is due to a bug I recently introduced. Can you try creating the matrix with the argument |
The bug is now fixed in master |
Alright, now I obtain something much closer to the expected solution, although still one order of magnitude larger. I think this is due to the error introduced by the rbf derivative operators, which I noticed being especially large near the walls. I might give ghost nodes a try.
and then before solving:
I also noticed that the error increases if I use higher degree phs or higher order monomials, which is not what I was expecting. Anyway, thanks a lot. |
That looks right. The increased error at the edges with higher order monomials and phs is due to Runge's phenomenon. You may get better results at the edges using |
No luck. |
Are the results at least consistent? Or are you getting significant variations in the results for the different parameter choices? |
Solving the linear systems gives the same results, no matter what solver I use. However, if I use a greater number of stencil knots, I get very different results (worse, compared to the desired solution). Same thing for the choice of |
That is odd and it has me thinking that there may be an issue with your nodes/domain. I have several question: Are your nodes the vertices of the mesh used for the CFD simulation? Can you show me what your domain looks like? Can you show me what some of these incorrect solutions look like? What is the condition number of your LHS matrix? Does your rhs also look significantly different for different parameter choices? |
Thanks! The domain does not look like an issue. It looks like the problem is still ill-conditioned. Are you missing a BC on the left end? I realize it is cheating, but if you fix the left end at 20,000, would that give you the right answer? Does enforcing dp/dn=0 at the left end give you a better solution? |
I don't think I'm missing any conditions. I tried to fix the solution on one node at the left end of my domain; the solution changes only slightly. The distribution you see in the figure was calculated with the dp/dn = 0 condtion. If I don't do that, I obtain much higher values (up to 10^20) and nonsense distributions. |
Would you be able to send me your mesh file and velocities? The files may be small enough to upload here. |
Everything is in the .vtu file.
|
I think I see the problem. The solution is mostly being controlled by the large velocity derivatives at the left end of the pipe. It also looks like the node spacing is too coarse to accurately resolve the velocity derivatives. Consequently, the RHS is very sensitive to the choice of The corresponding pressures are and here is the RHS with phi="phs5" and order=3 the corresponding pressures are You can see that the sign of the pressure solution flips. I am not sure what the best solution would be to mitigate this issue. Do you care about what happens at the left end? or are the large velocity gradients just a boundary artifact to you? Here is the code that I used to generate these results import numpy as np
import meshio
from rbf.pde.geometry import simplex_outward_normals
from rbf.pde.fd import weight_matrix
from rbf.sputils import expand_rows
from scipy.spatial import cKDTree
from scipy.sparse.linalg import spsolve
phi = 'phs5'
order = 3
n = 50
rho = 1060.0
mu = 1.0 # not sure what this should be
data = meshio.read('ts_1_0_0.vtu')
nodes = data.points
N = len(nodes)
u, v, w = data.point_data['velocity'].T
# compute the RHS at `nodes`. Note I have removed the time derivative term
Wx = weight_matrix(nodes, nodes, n, [1, 0, 0], phi=phi, order=order)
Wy = weight_matrix(nodes, nodes, n, [0, 1, 0], phi=phi, order=order)
Wz = weight_matrix(nodes, nodes, n, [0, 0, 1], phi=phi, order=order)
Wxx = weight_matrix(nodes, nodes, n, [2, 0, 0], phi=phi, order=order)
Wyy = weight_matrix(nodes, nodes, n, [0, 2, 0], phi=phi, order=order)
Wzz = weight_matrix(nodes, nodes, n, [0, 0, 2], phi=phi, order=order)
bx = -rho*(u*Wx.dot(u) + v*Wy.dot(u) + w*Wz.dot(u)) + mu*(Wxx.dot(u) + Wyy.dot(u) + Wzz.dot(u))
by = -rho*(u*Wx.dot(v) + v*Wy.dot(v) + w*Wz.dot(v)) + mu*(Wxx.dot(v) + Wyy.dot(v) + Wzz.dot(v))
bz = -rho*(u*Wx.dot(w) + v*Wy.dot(w) + w*Wz.dot(w)) + mu*(Wxx.dot(w) + Wyy.dot(w) + Wzz.dot(w))
rhs = Wx.dot(bx) + Wy.dot(by) + Wz.dot(bz)
# break the tets up into faces. If a face is unique, then it is part of the
# boundary
cells = data.cells[0].data
faces = cells[:, [[0, 1, 2], [0, 1, 3], [0, 2, 3], [1, 2, 3]]]
faces = faces.reshape(-1, 3)
unique_faces, count = np.unique(np.sort(faces, axis=1), axis=0, return_counts=True)
boundary_faces = unique_faces[count == 1]
# break the boundary faces up into faces that get fixed/free BCs
fixed_faces, free_faces = [], []
for face in boundary_faces:
if np.all(np.abs(nodes[face, 0] - 0.1) < 1e-5):
fixed_faces.append(face)
else:
free_faces.append(face)
# get the node indices making up the different boundaries
fixed_ids = np.unique(fixed_faces)
free_ids = np.array([i for i in np.unique(free_faces) if i not in fixed_ids])
interior_ids = np.array([i for i in range(N) if ((i not in fixed_ids) & (i not in free_ids))])
# Get the normal vectors for each free node. The normal at each node will be
# the average of the normals for each face containing the node
free_normals = np.zeros((len(free_ids), 3))
face_normals = simplex_outward_normals(nodes, free_faces)
for idx, node_id in enumerate(free_ids):
for face, nrm in zip(free_faces, face_normals):
if node_id in face:
free_normals[idx] += nrm
free_normals /= np.linalg.norm(free_normals, axis=1)[:, None]
# create and append the ghost nodes. dx is the distance from the free nodes to
# their nearest neighbors
dx = cKDTree(nodes).query(nodes[free_ids], 2)[0][:, 1]
ghost_nodes = nodes[free_ids] + 0.5*dx[:, None]*free_normals
nodes = np.vstack((nodes, ghost_nodes))
ghost_ids = np.arange(N, N + len(ghost_nodes))
N = len(nodes)
# build the LHS
A_interior = weight_matrix(
x=nodes[interior_ids],
p=nodes,
n=n,
diffs=[[2, 0, 0], [0, 2, 0], [0, 0, 2]],
phi=phi,
order=order
)
A_ghost = weight_matrix(
x=nodes[free_ids],
p=nodes,
n=n,
diffs=[[2, 0, 0], [0, 2, 0], [0, 0, 2]],
phi=phi,
order=order
)
A_free = weight_matrix(
x=nodes[free_ids],
p=nodes,
n=n,
diffs=[[1, 0, 0], [0, 1, 0], [0, 0, 1]],
coeffs=free_normals.T,
phi=phi,
order=order
)
A_fixed = weight_matrix(
x=nodes[fixed_ids],
p=nodes,
n=1,
diffs=[0, 0, 0],
phi=phi,
order=0
)
A = expand_rows(A_interior, interior_ids, N)
A += expand_rows(A_ghost, ghost_ids, N)
A += expand_rows(A_free, free_ids, N)
A += expand_rows(A_fixed, fixed_ids, N)
print('condition number: %.2e' % np.linalg.cond(A.A))
d = np.zeros((N,))
d[interior_ids] = rhs[interior_ids]
d[ghost_ids] = rhs[free_ids]
soln = spsolve(A, d)
# remove the solution at ghost nodes
soln = soln[:len(data.points)]
data.point_data['rbf-fd-pressure'] = soln
data.point_data['rhs'] = rhs
data.write('soln.vtu') |
I see. I'll try to run another test with a finer mesh to check if the coarse node spacing is the issue. |
Hello, I'm trying to recover the pressure field from a known fluid velocity field using the pressure Poisson equation.
I'm able to compute spatial derivatives of the velocity vector components. I know these are correct: I have ground truth derivatives.
I have assembled the right hand side of the pressure Poisson equation as:
with subscripts indicating derivatives, rho and mu are fluid density and viscosity.
Then, I assembled the RBF weight matrix as:
Wppe = weight_matrix(nodes[interiorIDs], nodes, n=80, [[2,0,0], [0,2,0], [0,0,2]], phi='phs5', order=5)
, and applied boundary conditions toWppe
andrhs
.However, when I solve the linear system, I obtain wrong pressure fields, both in patterns and values (15 orders of magnitude higher!).
I've tried lots of combinations for assembling the rhs and the coefficient matrix with no success.
Is there something wrong in the way I'm using the weight_matrix function? Or could the problem be related to my rhs?
Any advice or help would be greatly appreciated.
Thanks!
The text was updated successfully, but these errors were encountered: