How to get normals of a 2D line and to integrate along it

I’m trying to calculate lift coefficient of an airfoil, the domain is 2D and I’m using Paraview 5.8 . I extract only the blocks belonging to the airfoil’s walls and with the “merge blocks” filter I get a 2D line over which I want to get normal vectors . But when applying “extract surfaces” and “generate surface normals” there is no output (I applied also a pointDataTocellData filter and checked generate cell normal option). I think because of the bidimensional nature of the mesh, so the third coordinate is always zero and normals are not well defined on the 2D airfoil’s walls. I managed to calculate for every point of such a 2D line the length coordinate (for easy reordering), so to export data in a csv file and calculate normals and perform integration with matlab. Are there some more elegant techniques to accomplish such a task without leaving Paraview environment ?
Thanks a lot in advance

Please share data and precise steps to reproduce.

After some tweaking it seems the problem is primarily due to the extract surface filter. I have a similar problem with a non multi-block unstructured dataset. I cannot extract the surface of the airfoil with the extract surface filter, simply nothing happens after I apply it.
This is the dataset:
airfoil.vtk (727.5 KB)
The steps to reproduce this issue are :

  1. Load data set, no need to select blocks or data
  2. Clip filter with sphere center in (-0.025, 0 , 0) and radius = 0.6 , Invert option checked, crinckle clip unchecked
  3. Extract surface filter, but I cannot get boundaries on which I can calculate normals, it seems nothing happens.


I think the problem here is that what ParaView things is a “surface” is not the same think that you expect as a “surface,” and thus the operations are not doing what you expected.

ParaView considers a surface as a mesh of 2D polygon types (triangles, quads, etc.). A surface is a 2D dimensional structure (usually considered in a 3D space). Your mesh is strictly 2D. Thus, ParaView thinks that your whole mesh is a surface. What you are thinking of as a surface is what ParaView thinks of as an edge.

So first, let’s talk about getting the surface of your 2D mesh. I think the following operations will get what you want.

  1. Load the data set.
  2. Run the Extract Surface filter. (This will not make any visible difference, but will make some data transforms that are necessary for technical reasons that I don’t want to get into.)
  3. Run the Feature Edges filter. This will extract the boundary edges that you consider the “surface.”
  4. Add the Clip filter using the same parameters you suggest above (Clip Type: Sphere, Center: (-0.025, 0, 0), Radius: 0.6). This will remove the exterior mesh boundary and leave only the airfoil boundary.

Now that you have the “surface” you want, there is another problem. The problem is that ParaView will not create the surface normals you want. When ParaView generates normals, it finds the normal of 2D polygons. You have 1D lines. To get the normals you want, you can use the Programmable Filter with the following Script:

from vtk.numpy_interface import algorithms as algs
import numpy as np

input0 = inputs[0]

numCells = input0.GetNumberOfCells()

normx = np.empty(numCells, dtype=np.float64)
normy = np.empty(numCells, dtype=np.float64)
normz = np.zeros(numCells, dtype=np.float64)
for i in range(numCells):
  cell = input0.GetCell(i)
  p1 = input0.GetPoint(cell.GetPointId(0))
  p2 = input0.GetPoint(cell.GetPointId(1))
  normx[i] = p2[1] - p1[1]
  normy[i] = -(p2[0] - p1[0])

norm = algs.make_vector(normx, normy, normz)
output.CellData.append(norm, "normals")

Dear @Kenneth_Moreland ,
thanks a lot for taking the time to write such a didactic and complete answer.
But if I may ask, what are criteria for return cell end points with GetPointId?
For I can think of such a solution

 for i in range(numCells):
  cell = input0.GetCell(i)
  p1 = input0.GetPoint(cell.GetPointId(0))
  p2 = input0.GetPoint(cell.GetPointId(1))
  dx= p2[0] - p1[0]
  dy= p2[1] - p1[1]
  normx[i] = dx
  normy[i] = dy

But if the grid is unstructured how can I be sure, traversing the airfoils’s wall in the for cycle, to get p1(i), p2(i) and p1(i+1) and p2(i+1) to be the ordered points that I would got traversing the airfoil’s wall in a choerent clockwise or counter-clockwise manner ?
Again, thank you for your answer.

The script in Programmable Filter is making some assumptions about the data (since we are custom writing it). The assumption is that each cell is a line cell, which connects 2 points. So cell.GetPointId(0) retrieves the index of one point and cell.GetPointId(1) retrieves the index of the other. That is about all that is guaranteed.

The order the lines are traversed in the for loop is pretty arbitrary. I would expect it to follow the same order in the original mesh in your input file.

Even the orientation of the lines is not guaranteed to be consistent. (That is, lines could be pointing in different directions.) However, as long as the polygons in your input have consistent winding (e.g. all counter-clockwise), then the lines in the output of Feature Edges should also point in consistent directions. That seems to be the case in your data.

Thank you for this deepening.
I don’t think this method is robust, practically it depends heavily on how the surface of the airfoil was entered to create the mesh.
As a Paraview beginner I can think of 2 methods to reliably calculate line normals :

  1. If memory permits, I can extrude data-set in z direction and the final data-set will contain 2 identical parallel “flat” data-set, each orthogonal to z axis and distant 1 unit apart from each other. In this manner I can use embedded Paraview tools, like generate surface normals.
  2. Once I extracted the surface, instead of extract edges, I can integrate it. It will be produced a quantity call length, which is, I think, an arc length coordinate. It can be used as a discriminant to reorder the cells of the airfoil’s wall. With this method the question is how robust is the tool to integrate along the boundaries and extrapolate an arc length coordinate to get an identifier for every cell.

What do you think about it? Simply I cannot depend on how the mesh was created, I will make no assumptions other than to have to deal with an unstructured data-set.
Again, thank you.

Yes, you can use the Linear Extrusion filter to convert your line surface to a polygon surface, and at that point ParaView can compute normals on it. I was going to suggest this first, but you said you want to integrate some value based on this, so I wasn’t sure if changing the mesh structure is going to affect whatever this integration operation you are planning on doing.

I’m not sure why you are worried about traversing the cells in the order of the airfoil’s wall. An integration (on a discrete mesh like this) generally means you are summing something up, and the order you sum things up does not matter. The direction the normals point should be consistent unless there is something weird with your input mesh. I think it would be a safe assumption that the order would be correct.

I’m not worried traversing the cells in a random order but I’m worried about getting consistent direction of normals. I just want to adopt a precautionary approach because I’ll have to use this script with different cases.
I can think of a simple common case where the input mesh may be weird. Let’s say the surface of the airfoil is input with two different set of points (blocks or zones): the lower surface and the upper surface sets. The first is entered clockwise and the second is entered counter-clockwise. What do you think about it ? As I am writing I realize this may seems an exaggerated claim of robustness, but for publication-quality result robustness is the minimum.
Thank you