Group Abstract Group Abstract

Message Boards Message Boards

Modeling airflow past a car

Posted 22 days ago

POSTED BY: David Keith
5 Replies
Posted 20 days ago

Thank you, Alessandro. I appreciate the help.

The first step is to obtain a better mesh, as you point out. (I should have seen that!) I have tried to refine the mesh by using

mesh = ToElementMesh[\[CapitalOmega], MaxCellMeasure -> .1, 
   "MaxBoundaryCellMeasure" -> .001];
mesh["Wireframe"]

This works for the example in the documentation using a Disk, but for my RegionDifference (which has Head Polygon) the MaxBoundaryCell option has no effect. I have also tried first creating a BoundaryMesh of Omega, then handing that to ToElementMesh. That also has no effect. I could just use a smaller MaxCellMeasure, but that creates smaller cells even where they are likely not needed. Perhaps you could say more about the process of applying a finer discretization to the car rectangle. I have read all the guide on meshing, but still can't figure out how to control a mesh in that manner.

In another tool I have used there is a tree structure that allows a mesh to be built up under user control, including the separate construction of meshes on boundaries. It could produce a mesh like that below. Is there a capability in Mathematica that can get similar results? enter image description here

POSTED BY: David Keith

I realize this isn’t a trivial issue, mainly due to how ResourceFunction["RoundedPolygon"] works.

While with the Rectangle[] you can use "MaxBoundaryCellMeausure" to set you discretization:

bMeshRec = ToBoundaryMesh[rec, "MaxBoundaryCellMeasure" -> carLength/2];
Length[bMeshRec["Coordinates"]]
52

or by dividing carLength by 10:

bMeshRec = ToBoundaryMesh[rec, "MaxBoundaryCellMeasure" -> carLength/10];
Length[bMeshRec["Coordinates"]]
260

The issue is that ResourceFunction["RoundedPolygon"] returns a predefined, discretized geometry, so ToBoundaryMesh ends up using those fixed coordinates. That’s why it appears not to work properly with ToElementMesh. However, here’s a workaround I found, it’s a bit tricky, but it not only solves the problem, it also gives better insight into how these functions behave.

Solution A is to build the car geometry using a combination of pure Region functions, such as Disk[] and Rectangle[], which gives you more control over the cells size once you pass it to ToElementMesh.

Solution B is to modify the existing boundary mesh to match your desired resolution. For example, you can create two separate BoundaryElementMesh objects with your custom discretization, then join them together and fill in the interior elements afterward.

External rectangle: this is straightforward, as shown earlier, you can set your desired "MaxBoundaryCellMeasure" to control the discretization. Note that you can (and will need to for the next step) extract both the coordinates and the connectivity. Since this is a boundary mesh, the connectivity simply defines line elements between consecutive points.

rec = Rectangle[{0, 0}, {recLength, recHeight}];
bMeshRec = ToBoundaryMesh[rec, "MaxBoundaryCellMeasure" -> carLength/10];
coordsRec = bMeshRec["Coordinates"];
(* [[1,1]] take the first, in this case the singile one, from the list, and then all the connectivity inside LineElement *)
connRec = bMeshRec["BoundaryElements"][[1, 1]];

You can apply the same approach to the car geometry, but keep in mind that "MaxBoundaryCellMeasure" has no effect in this case. My solution is to manually insert additional points between each pair of existing points, so that each segment is discretized according to your desired resolution. Note that the spacing between points is not uniform, corner segments are shorter than the longer sides (see figure).

bMeshCar = ToBoundaryMesh[car, "MaxBoundaryCellMeasure" -> 1];
coordsCar = bMeshCar["Coordinates"];
connCar = bMeshCar["BoundaryElements"][[1, 1]];

To visualize the points distance:

Graphics[{Point[coordsCar],
  {If[Norm[Differences[coordsCar[[#]]]] < carHeight, Red, Blue], 
     Line[coordsCar[[#]]]} & /@ connCar
  }, PlotRange -> {{carx, carx + carLength/2}, {cary + carHeight/2, cary + carHeight}}]

enter image description here

So you’ll need to compute the ideal number of subdivisions for each segment individually. Note that I used Max[ xxx , 2] in the computation to avoid division by zero during points placement, for smaller spacing you won't need it.

Once you understand the logic, you can easily tweak it to suit your needs. And you can do it with:

finerCoords = {};
spacing = carLength/50;
sortingIdx = FindShortestTour[coordsCar][[2]];
sortedCoords = coordsCar[[sortingIdx]];
Do[
  pt0 = sortedCoords[[i]];
  pt1 = sortedCoords[[i + 1]];
  n = Max[Ceiling[Norm[pt1 - pt0]/spacing], 2];
  pts = Table[pt0 + k ( pt1 - pt0)/(n - 1), {k, 0, n - 1}];
  AppendTo[finerCoords, #] & /@ pts;
  , {i, 1, Length[sortedCoords] - 1}];
finerCoordsLen = Length[finerCoords];
newConnectivity = Join[Table[{i, i + 1}, {i, finerCoordsLen - 1}], {{finerCoordsLen, 1}}];

And then you need to join the two boundary mesh information togheter:

updatedConnectivity = newConnectivity + Length[coordsRec];
bmesh = ToBoundaryMesh["Coordinates" -> Join[coordsRec,  "BoundaryElements" ->{LineElement[connRec], LineElement[updatedConnectivity]}];
bmesh["Wireframe"]

enter image description here

At this point, you can mesh the interior region and specify "MaxCellMeasure" to control the element size inside. Just keep in mind that this setting will not affect the boundary discretization. You need to specifiy also the Hole coordinates, you can simply use the center of the car.

mesh = ToElementMesh[bmesh, "RegionHoles" -> Mean[coordsCar], "MaxCellMeasure" -> .1];
mesh["Wireframe"];

enter image description here

Note that although the result may look quite similar at first glance, if you zoom in on the bottom side of the car, you’ll see that the discretization is now much finer.

Show[mesh["Wireframe"], PlotRange -> {{0.9 carx, 1.1 carx + carLength}, {0, cary + carHeight}}]

enter image description here

You can adjust the spacing variable and the boundary discretization as needed to control the resolution more precisely.

Posted 18 days ago

Thank you very much for your help, Alessandro. I will study what you have written. And I will be looking into turbulence models. Especially the k-epsilon model. That should keep me busy.

I really do appreciate your help. I followed the link to your profile. It looks like you do interesting work!

POSTED BY: David Keith

Hi,

Alessandro from the numerics group here. I believe the issue is related to the mesh. If you zoom in on the bottom part of the car, you’ll notice that there are very few elements in that region, yet you’re trying to enforce a velocity of 26 m/s on the road and 0 m/s (no-slip) on the car surface. That’s a large velocity gradient, and resolving it properly requires several mesh elements across the boundary layer.

Show[mesh["Wireframe"], 
 PlotRange -> {{0.9 carx, 1.1 carx + carLength}, {0, 5*cary}}]

enter image description here

You can try reducing the MaxCellMeasure or applying a finer discretization specifically to the car’s rectangle (trasform it into lines) to ensure that the mesh better follows its boundaries with the elements size.

I also recommend applying the gradual ramping to the boundary conditions rather than to the material parameters. For example, you can use:

inlet = DirichletCondition[{u[x, y] == k speed, v[x, y] == 0}, x == 0];

And then set a parametric NDSolve:

pfun = ParametricNDSolveValue[{FluidFlowPDEComponent[vars, 
     pars] == {0, 0, 0}, bcs}, vars[[1]], {x, y} \[Element] mesh, k]

where k will be increase as follows:

nsteps = 100; 
sol = {0, 0, 0};
AbsoluteTiming[Monitor[Do[
    kNew = step*1/nsteps;
    sol = pfun[kNew, "InitialSeeding" -> Thread[Equal[vars[[1]], sol]]];
    , {step, 1, nsteps, 10}], step];]

This should help. Gradually ramping the boundary conditions, such as inlet velocity, is both physically justified and numerically helpful, as it mimics a realistic startup and avoids introducing abrupt changes that can destabilize the solver. In contrast, modifying material properties like viscosity or density during the simulation changes the actual physics of the problem, affecting quantities like the Reynolds number, boundary layer development, and overall flow behavior. For this reason, adjusting boundary conditions is a safer and more physically consistent strategy.

I hope this is helpful, feel free to contact me if you need any further information.

Posted 20 days ago

Cross posted to StackExchange here

POSTED BY: David Keith
Reply to this discussion
Community posts can be styled and formatted using the Markdown syntax.
Reply Preview
Attachments
Remove
or Discard