MFEM v4.10.0
Finite element discretization library
Loading...
Searching...
No Matches
nurbs_naca_cmesh.cpp
Go to the documentation of this file.
1// Copyright (c) 2010-2026, Lawrence Livermore National Security, LLC. Produced
2// at the Lawrence Livermore National Laboratory. All Rights reserved. See files
3// LICENSE and NOTICE for details. LLNL-CODE-806117.
4//
5// This file is part of the MFEM library. For more information and source code
6// availability visit https://mfem.org.
7//
8// MFEM is free software; you can redistribute it and/or modify it under the
9// terms of the BSD-3 license. We welcome feedback and contributions, see file
10// CONTRIBUTING.md for details.
11//
12// Compile with: make nurbs_naca_cmesh
13//
14// Sample run: nurbs_naca_cmesh -ntail 80 -nbnd 80 -ntip 20 -nwake 40 -sw 2.0 -sbnd 2.5 -stip 1.1 -aoa 3
15//
16// Description: This example code demonstrates the use of MFEM to create a
17// C-mesh around a NACA-foil section. The foil section is defined
18// in the class NACA4, which can be easily replaced with any other
19// description of a foil section. To apply an angle of attack, we
20// rotate the domain around the origin.
21//
22// The mesh employs five patches of which two describe the domain
23// behind the foil section (wake). The boundary describing the
24// foil section is divided over three patches. One patch describes
25// the domain adjacent to the boundary which describes the tip /
26// leading edge of the foil section and two patches describe the
27// domain which is adjacent to the two boundaries describing the
28// remainder of the foil section. The aim is to create a mesh with
29// the highest quality close to the boundary of the foil section
30// and the wake.
31//
32// The example returns a VisIt and a GLVis data structure for
33// visualization. Note that one will need to use the option
34// "Operators/Selection/MultiresControl" to inspect the shape of
35// the NURBS in VisIt. This allows for increasing the resolution
36// on which the NURBS curves are evaluated. This is required for
37// correct visualisation of coarse meshes. In the current mesh, it
38// allows to visualize the tip of the foil section.
39//
40// For visualization of the result MFEM 4.6 or higher is required
41// in GlVis or VisIt.
42//
43// Possible improvements:
44// - Implement optimization with TMOP
45// - Streamline GetTipXY() for two options
46
47#include "mfem.hpp"
48#include <iostream>
49#include <cmath>
50
51using namespace std;
52using namespace mfem;
53
54// Object that describes a symmetric NACA foil section
55class NACA4
56{
57protected:
58 // Constants describing the thickness profile
59 real_t A, B, C, D, E;
60 // Thickness of the foil section
61 real_t t;
62 // Chord of the foil section: foil length
63 real_t c;
64 // Maximum number of iterations for Newton solver
65 int iter_max;
66 // Tolerance for Newton solver
67 real_t epsilon;
68public:
69 NACA4(real_t t_, real_t c_);
70 // Returns the coordinate y corresponding to coordinate @a xi
71 real_t y(real_t xi) const;
72 // Returns the derivative of the curve at location coordinate @a xi
73 real_t dydx(real_t xi) const;
74 // Returns the curve length at coordinate @a xi
75 real_t len(real_t xi) const;
76 // Returns the derivative of the curve length at coordinate @a xi
77 real_t dlendx(real_t xi) const;
78 // Returns the coordinate x corresponding to the curve length @a l from
79 // the tip of the foil section
80 real_t xl(real_t l) const;
81 // Get the chord of the foil_section
82 real_t GetChord() const {return c;}
83};
84
85// Function that finds the coordinates of the control points of the tip of the
86// foil section @a xy based on the @a foil_section, knot vector @a kv and tip
87// fraction @a tf. We have two cases, with an odd number of control points and
88// with an even number of control points. These may be streamlined in the
89// future.
90void GetTipXY(const NACA4 &foil_section, const KnotVector &kv, real_t tf,
91 Vector &x, Vector &y);
92
93// Function that returns a uniform knot vector based on the @a order and the
94// number of control points @a ncp.
95unique_ptr<KnotVector> UniformKnotVector(int order, int ncp);
96
97// Function that returns a knot vector that is stretched with stretch @s
98// with the form x^s based on the @a order and the number of control points
99// @a ncp. Special case @a s = 0 will give a uniform knot vector.
100unique_ptr<KnotVector> PowerStretchKnotVector(int order, int ncp,
101 real_t s = 0.0);
102
103// Function that returns a knot vector with a hyperbolic tangent spacing
104// with a cut-off @c using the @a order and the number of control points @a ncp.
105unique_ptr<KnotVector> TanhKnotVector(int order, int ncp, real_t c);
106
107// Function that returns a knot vector with a hyperbolic tangent spacing from
108// both sides of the knot vector with a cut-off @c using the @a order and the
109// number of control points @a ncp.
110unique_ptr<KnotVector> DoubleTanhKnotVector(int order, int ncp, real_t c);
111
112// Function that evaluates a linear function which describes the boundary
113// distance based on the flair angle @a flair, smallest boundary distance @a bd
114// and coordinate @a x. The flair angle is mainly used to be able to enforce
115// inflow on the top and bottom boundary and to create an elegant mesh.
117
118int main(int argc, char *argv[])
119{
120 int mdim = 2;
121 int order = 2;
122
123 // 1. Parse command-line options.
124 OptionsParser args(argc, argv);
125 const char *msh_path = "";
126 const char *msh_filename = "naca-cmesh";
127 args.AddOption(&msh_path, "-p", "--mesh-path",
128 "Path in which the generated mesh is saved.");
129 args.AddOption(&msh_filename, "-m", "--mesh-file",
130 "File where the generated mesh is written to.");
131
132 // Foil section options
133 real_t foil_length = 1.0;
134 real_t foil_thickness = 0.12;
135 real_t aoa = 0.0;
136 args.AddOption(&foil_length, "-l", "--foil-length",
137 "Length of the used foil in the mesh. ");
138 args.AddOption(&foil_thickness, "-t", "--foil-thickness",
139 "Thickness of the foil in the mesh as a fraction of length.");
140 args.AddOption(&aoa, "-aoa", "--angle-of-attack",
141 "Angle of attack of the foil. ");
142
143 // Mesh options
144 real_t boundary_dist = 3.0;
145 real_t wake_length = 3.0;
146 real_t tip_fraction = 0.05;
147 real_t flair = -999;
148 args.AddOption(&boundary_dist, "-b", "--boundary-distance",
149 "Radius of the c-mesh, distance between foil and boundary");
150 args.AddOption(&wake_length, "-w", "--wake_length",
151 "Length of the mesh after the foil");
152 args.AddOption(&tip_fraction, "-tf", "--tip-fraction",
153 "Fraction of the length of foil that will be in tip patch");
154 args.AddOption(&flair, "-f", "--flair-angle",
155 "Flair angle of top and bottom boundary to enforce inflow. If\
156 left at default, the flair angle is determined automatically\
157 to create an elegant mesh.");
158
159 int ncp_tip = 3;
160 int ncp_tail = 3;
161 int ncp_wake = 3;
162 int ncp_bnd = 3;
163 args.AddOption(&ncp_tip, "-ntip", "--ncp-tip",
164 "Number of control points used over the tip of the foil.");
165 args.AddOption(&ncp_tail, "-ntail", "--ncp-tail",
166 "Number of control points used over the tail of the foil.");
167 args.AddOption(&ncp_wake, "-nwake", "--ncp-wake",
168 "Number of control points over the wake behind the foil.");
169 args.AddOption(&ncp_bnd, "-nbnd", "--ncp-circ",
170 "Number of control points between the foil and boundary.");
171
172 real_t str_tip = 1;
173 real_t str_wake = 1;
174 real_t str_bnd = 1;
175 real_t str_tail = 1;
176 args.AddOption(&str_tip, "-stip", "--str-tip",
177 "Stretch of the knot vector of the tip.");
178 args.AddOption(&str_tail, "-stail", "--str-tail",
179 "Stretch of the knot vector of the tail.");
180 args.AddOption(&str_wake, "-sw", "--str-wake",
181 "Stretch of the knot vector of the wake.");
182 args.AddOption(&str_bnd, "-sbnd", "--str-circ",
183 "Stretch of the knot vector of the circle.");
184
185 bool visualization = true;
186 bool visit = true;
187 args.AddOption(&visualization, "-vis", "--visualization", "-no-vis",
188 "--no-visualization",
189 "Enable or disable GLVis visualization.");
190 args.AddOption(&visit, "-visit", "--visit", "-no-visit", "--no-visit",
191 "Enable/disable VisIt visualization in output Naca_cmesh.");
192
193 // Parse and print command line options
194 args.Parse();
195 if (!args.Good())
196 {
197 args.PrintUsage(cout);
198 return 1;
199 }
200 args.PrintOptions(cout);
201
202 // Convert fraction
203 const real_t tail_fraction = 1.0 - tip_fraction;
204
205 // Convert angles to radians
206 constexpr real_t deg2rad = M_PI/180;
207 aoa = aoa*deg2rad;
208
209 // 2. Create knot vectors
210 unique_ptr<KnotVector> kv0 = TanhKnotVector(order, ncp_wake, str_wake);
211 kv0->Flip();
212 unique_ptr<KnotVector> kv4 (new KnotVector(*kv0));
213 kv4->Flip();
214
215 unique_ptr<KnotVector> kv1 = PowerStretchKnotVector(order, ncp_tail,
216 -str_tail);
217 unique_ptr<KnotVector> kv3 = PowerStretchKnotVector(order, ncp_tail, str_tail);
218 unique_ptr<KnotVector> kv2 = DoubleTanhKnotVector(order, ncp_tip, str_tip);
219 unique_ptr<KnotVector> kvr = TanhKnotVector(order, ncp_bnd, str_bnd);
220
221 unique_ptr<KnotVector> kv_o1 = UniformKnotVector(1, 2);
222 unique_ptr<KnotVector> kv_o2 = UniformKnotVector(2, 3);
223
224
225 // Variables required for curve interpolation
226
227 // 3. Create required (variables for) curves: foil_section and flair
228 const NACA4 foil_section(foil_thickness, foil_length);
229
230 // The default flair angle is defined to be the same as the angle of the
231 // curve of the foil section to create an elegant mesh.
232 if (flair == -999)
233 {
234 flair = atan(foil_section.dydx(tip_fraction*foil_length));
235 }
236
237 // 4. We map coordinates in patches, apply refinement and interpolate the
238 // foil section in patches 1, 2 and 3. Note the case of non-unity weights
239 // in patch 2 to create a circular shape: its coordinates are converted to
240 // homogeneous coordinates. This is not needed for other patches as
241 // homogeneous coordinates and Cartesian coordinates are the same
242 // for patches with unity weight.
243
244 // Patch 0: lower wake part behind foil section.
245 NURBSPatch patch0(kv_o1.get(), kv_o1.get(), 3);
246 {
247 for (int i = 0; i < 2; i++)
248 for (int j = 0; j < 2; j++)
249 {
250 patch0(i,j,2) = 1.0;
251 }
252
253 // Define points
254 patch0(0,0,0) = foil_length + wake_length;
255 patch0(0,0,1) = 0.0;
256
257 patch0(1,0,0) = foil_length;
258 patch0(1,0,1) = 0.0;
259
260 patch0(0,1,0) = foil_length + wake_length;
261 patch0(0,1,1) = -FlairBoundDist(flair, boundary_dist, patch0(0,1,0));
262
263 patch0(1,1,0) = foil_length;
264 patch0(1,1,1) = -FlairBoundDist(flair, boundary_dist, patch0(1,1,0));
265
266 // Refine
267 patch0.DegreeElevate(0, order-1);
268 patch0.KnotInsert(0, *kv0);
269 patch0.DegreeElevate(1, order-1);
270 patch0.KnotInsert(1, *kvr);
271 }
272
273 // Patch 1: Lower tail of foil
274 NURBSPatch patch1(kv_o1.get(), kv_o1.get(), 3);
275 {
276 for (int i = 0; i < 2; i++)
277 for (int j = 0; j < 2; j++)
278 {
279 patch1(i,j,2) = 1.0;
280 }
281
282 // Define points
283 patch1(0,0,0) = foil_length;
284 patch1(0,0,1) = 0.0;
285
286 patch1(1,0,0) = tip_fraction*foil_length;
287 patch1(1,0,1) = -foil_section.y(patch1(1,0,0));
288
289 patch1(0,1,0) = foil_length;
290 patch1(0,1,1) = -FlairBoundDist(flair, boundary_dist, patch1(0,1,0));
291
292 patch1(1,1,0) = -boundary_dist*sin(flair) + tip_fraction*foil_length;
293 patch1(1,1,1) = -boundary_dist*cos(flair);
294
295 // Refine
296 patch1.DegreeElevate(0, order-1);
297 patch1.KnotInsert(0, *kv1);
298
299 int ncp = kv1->GetNCP();
300 // We locate the control points at the location of the maxima of the
301 // shapefunctions defined by the knot vectors -- the Botella points.
302 Vector u(ncp),x(ncp),y(ncp),interp(ncp);
303 for (int i = 0; i < ncp; i++)
304 {
305 u[i] = kv1->GetBotella(i);
306 }
307
308 for (int i = 0; i < ncp; i++)
309 {
310 x[i] = foil_length*(1.0 - tail_fraction*u[i]);
311 }
312 kv1->GetInterpolant(x,u,interp);
313 for (int i = 0; i < ncp; i++)
314 {
315 patch1(i,0,0) = interp[i];
316 }
317
318 for (int i = 0; i < ncp; i++)
319 {
320 y[i] = -foil_section.y(x[i]);
321 }
322 kv1->GetInterpolant(y,u,interp);
323 for (int i = 0; i < ncp; i++)
324 {
325 patch1(i,0,1) = interp[i];
326 }
327
328 patch1.DegreeElevate(1, order-1);
329 patch1.KnotInsert(1, *kvr);
330 }
331
332 // Patch 2: Tip of foil section
333 NURBSPatch patch2(kv_o2.get(), kv_o1.get(), 3);
334 {
335 // Define weights
336 for (int i = 0; i < 3; i++)
337 for (int j = 0; j < 2; j++)
338 {
339 patch2(i,j,2) = 1.0;
340 }
341
342 // Define points
343 patch2(2,0,0) = tip_fraction*foil_length;
344 patch2(2,0,1) = foil_section.y(patch2(2,0,0));
345
346 patch2(1,0,0) = 0.0;
347 patch2(1,0,1) = 0.0;
348 patch2(1,0,2) = cos((180*deg2rad-2*flair)/2);
349
350 patch2(0,0,0) = tip_fraction*foil_length;
351 patch2(0,0,1) = -foil_section.y(patch2(0,0,0));
352
353
354 patch2(2,1,0) = -boundary_dist*cos(90*deg2rad-flair)
355 + tip_fraction*foil_length;
356 patch2(2,1,1) = boundary_dist*sin(90*deg2rad-flair);
357
358 patch2(1,1,0) = -boundary_dist/sin(flair);
359 patch2(1,1,1) = 0.0;
360 patch2(1,1,2) = cos((180*deg2rad-2*flair)/2);
361
362 patch2(0,1,0) = -boundary_dist*cos(90*deg2rad-flair)
363 + tip_fraction*foil_length;
364 patch2(0,1,1) = -boundary_dist*sin(90*deg2rad-flair);
365
366 // Deal with non-uniform weight: convert to homogeneous coordinates
367 patch2(1,0,0) *= patch2(1,0,2);
368 patch2(1,0,1) *= patch2(1,0,2);
369 patch2(1,1,0) *= patch2(1,1,2);
370 patch2(1,1,1) *= patch2(1,1,2);
371
372 // Refine
373 patch2.DegreeElevate(0, order-2);
374 patch2.KnotInsert(0, *kv2);
375
376 // Project foil
377 int ncp = kv2->GetNCP();
378 Vector x(ncp), y(ncp);
379
380 GetTipXY(foil_section, *kv2, tip_fraction,x,y);
381
382 Vector u(ncp),interp(ncp);
383 for (int i = 0; i < ncp; i++)
384 {
385 u[i] = kv2->GetBotella(i);
386 }
387 kv2->GetInterpolant(x,u,interp);
388 for (int i = 0; i < ncp; i++)
389 {
390 patch2(i,0,0) = interp[i]*patch2(i,0,2);
391 }
392 kv2->GetInterpolant(y,u,interp);
393 for (int i = 0; i < ncp; i++)
394 {
395 patch2(i,0,1) = interp[i]*patch2(i,0,2);
396 }
397
398 // Project circle
399 patch2.DegreeElevate(1, order-1);
400 patch2.KnotInsert(1, *kvr);
401 }
402
403 // Patch 3: Upper part of trailing part foil section
404 NURBSPatch patch3(kv_o1.get(), kv_o1.get(), 3);
405 {
406 for (int i = 0; i < 2; i++)
407 for (int j = 0; j < 2; j++)
408 {
409 patch3(i,j,2) = 1.0;
410 }
411
412 // Define points
413 patch3(0,0,0) = tip_fraction*foil_length;
414 patch3(0,0,1) = foil_section.y(patch3(0,0,0));
415
416 patch3(1,0,0) = foil_length;
417 patch3(1,0,1) = 0.0;
418
419 patch3(0,1,0) = -boundary_dist*sin(flair) + tip_fraction*foil_length;
420 patch3(0,1,1) = boundary_dist*cos(flair);
421
422 patch3(1,1,0) = foil_length;
423 patch3(1,1,1) = FlairBoundDist(flair, boundary_dist, patch3(1,1,0));
424
425 // Refine
426 patch3.DegreeElevate(0, order-1);
427 patch3.KnotInsert(0, *kv3);
428
429 int ncp = kv3->GetNCP();
430
431 Vector u(ncp),x(ncp),y(ncp),interp(ncp);
432 for (int i = 0; i < ncp; i++)
433 {
434 u[i] = kv3->GetBotella(i);
435 }
436
437 for (int i = 0; i < ncp; i++)
438 {
439 x[i] = foil_length*(tip_fraction + tail_fraction*u[i]);
440 }
441 kv3->GetInterpolant(x,u,interp);
442 for (int i = 0; i < ncp; i++)
443 {
444 patch3(i,0,0) = interp[i];
445 }
446
447 for (int i = 0; i < ncp; i++)
448 {
449 y[i] = foil_section.y(x[i]);
450 }
451 kv3->GetInterpolant(y,u,interp);
452 for (int i = 0; i < ncp; i++)
453 {
454 patch3(i,0,1) = interp[i];
455 }
456
457 patch3.DegreeElevate(1, order-1);
458 patch3.KnotInsert(1, *kvr);
459 }
460
461 // Patch 4: Upper trailing wake part
462 NURBSPatch patch4(kv_o1.get(), kv_o1.get(), 3);
463 {
464 for (int i = 0; i < 2; i++)
465 for (int j = 0; j < 2; j++)
466 {
467 patch4(i,j,2) = 1.0;
468 }
469
470 // Define points
471 patch4(0,0,0) = foil_length;
472 patch4(0,0,1) = 0.0;
473
474 patch4(1,0,0) = foil_length+ wake_length;
475 patch4(1,0,1) = 0.0;
476
477 patch4(0,1,0) = foil_length;
478 patch4(0,1,1) = FlairBoundDist(flair, boundary_dist, patch4(0,1,0));
479
480 patch4(1,1,0) = foil_length+ wake_length;
481 patch4(1,1,1) = FlairBoundDist(flair, boundary_dist, patch4(1,1,0));
482
483 // Refine
484 patch4.DegreeElevate(0, order-1);
485 patch4.KnotInsert(0, *kv4);
486 patch4.DegreeElevate(1, order-1);
487 patch4.KnotInsert(1, *kvr);
488 }
489
490 // Apply angle of attack
491 patch0.Rotate2D(-aoa);
492 patch1.Rotate2D(-aoa);
493 patch2.Rotate2D(-aoa);
494 patch3.Rotate2D(-aoa);
495 patch4.Rotate2D(-aoa);
496
497 // 5. Print mesh to file
498
499 // Open mesh output file
500 string mesh_file;
501 mesh_file.append(msh_path);
502 mesh_file.append(msh_filename);
503 mesh_file.append(".mesh");
504 ofstream output(mesh_file.c_str());
505
506 // File header
507 output<<"MFEM NURBS mesh v1.0"<<endl;
508 output<< endl << "# " << mdim
509 << "D C-mesh around a symmetric NACA foil section"
510 << endl << endl;
511 output<< "dimension"<<endl;
512 output<< mdim <<endl;
513 output<< endl;
514
515 // NURBS patches defined as elements
516 output << "elements"<<endl;
517 output << "5"<<endl;
518 output << "1 3 0 1 5 4" << endl; // Lower wake
519 output << "1 3 1 2 6 5" << endl; // Lower tail
520 output << "1 3 2 3 7 6" << endl; // Tip
521 output << "1 3 3 1 8 7" << endl; // Upper tail
522 output << "1 3 1 0 9 8" << endl; // Upper wake
523 output << endl;
524
525 // Boundaries
526 output << "boundary" <<endl;
527 output << "10" <<endl;
528 output << "1 1 5 4" << endl; // Bottom
529 output << "1 1 6 5" << endl; // Bottom
530 output << "2 1 7 6" << endl; // Inflow
531 output << "3 1 8 7" << endl; // Top
532 output << "3 1 9 8" << endl; // Top
533 output << "4 1 4 0" << endl; // Outflow
534 output << "4 1 0 9" << endl; // Outflow
535 output << "5 1 1 2" << endl; // Foil section
536 output << "5 1 2 3" << endl; // Foil section
537 output << "5 1 3 1" << endl; // Foil section
538 output << endl;
539
540 // Edges
541 output <<"edges"<<endl;
542 output <<"15"<<endl;
543 output << "0 0 1"<<endl;
544 output << "1 1 2"<<endl;
545 output << "2 2 3"<<endl;
546 output << "3 3 1"<<endl;
547
548 output << "0 4 5"<<endl;
549 output << "1 5 6"<<endl;
550 output << "2 6 7"<<endl;
551 output << "3 7 8"<<endl;
552 output << "0 9 8"<<endl;
553
554 output << "4 0 4"<<endl;
555 output << "4 1 5"<<endl;
556 output << "4 2 6"<<endl;
557 output << "4 3 7"<<endl;
558 output << "4 1 8"<<endl;
559 output << "4 0 9"<<endl;
560 output << endl;
561
562 // Vertices
563 output << "vertices" << endl;
564 output << 10 << endl;
565 output << endl;
566
567 // Patches
568 output<<"patches"<<endl;
569 output<<endl;
570
571 output << "# Patch 0 " << endl;
572 patch0.Print(output); output<<endl;
573 output << "# Patch 1 " << endl;
574 patch1.Print(output); output<<endl;
575 output << "# Patch 2 " << endl;
576 patch2.Print(output); output<<endl;
577 output << "# Patch 3 " << endl;
578 patch3.Print(output); output<<endl;
579 output << "# Patch 4 " << endl;
580 patch4.Print(output); output<<endl;
581
582 // Close
583 output.close();
584
585 cout << endl << "Boundary identifiers:" << endl;
586 cout << " 1 Bottom" << endl;
587 cout << " 2 Inflow" << endl;
588 cout << " 3 Top" << endl;
589 cout << " 4 Outflow" << endl;
590 cout << " 5 Foil section" << endl;
591 cout << "=========================================================="<< endl;
592 cout << " "<< mdim <<"D mesh generated: " <<mesh_file.c_str()<< endl ;
593 cout << "=========================================================="<< endl;
594
595 // Print mesh info to screen
596 cout << "=========================================================="<< endl;
597 cout << " Attempting to read mesh: " <<mesh_file.c_str()<< endl ;
598 cout << "=========================================================="<< endl;
599 Mesh mesh(mesh_file.c_str(), 1, 1);
600 mesh.PrintInfo();
601
602 // Print mesh to file for visualization
603 if (visit)
604 {
605 VisItDataCollection dc = VisItDataCollection("mesh", &mesh);
606 dc.SetPrefixPath("Naca_cmesh");
607 dc.SetCycle(0);
608 dc.SetTime(0.0);
609 dc.Save();
610 }
611
612 if (visualization)
613 {
614 // Create glvis output
615 string glvis_file;
616 glvis_file.append(msh_path);
617 glvis_file.append("glvis_");
618 glvis_file.append(msh_filename);
619 glvis_file.append(".mesh");
620 ofstream glvis_output(glvis_file.c_str());
621 mesh.Print(glvis_output);
622 glvis_output.close();
623 }
624
625 return 0;
626}
627
628NACA4::NACA4(real_t t_, real_t c_)
629{
630 t = t_;
631 c = c_;
632 A = 0.2969, B = 0.1260, C = 0.3516, D = 0.2843, E = 0.1036;
633 iter_max = 1000;
634 epsilon = 1e-8;
635}
636
637real_t NACA4::y(real_t x) const
638{
639 real_t y = 5*t*(A*sqrt(x/c) - B*x/c - C*pow(x/c,2)
640 + D*pow(x/c,3) - E*pow(x/c,4));
641 return y*c;
642}
643
644real_t NACA4::dydx(real_t x) const
645{
646 real_t y = 5*t*(0.5 * A/sqrt(x/c) - B - 2*C*x/c
647 + 3* D*pow(x/c,2) - 4* E*pow(x/c,3));
648 return y*c;
649}
650
651real_t NACA4::len(real_t x) const
652{
653 real_t l = 5 * t * (A*sqrt(x/c) - B*x - C*pow(x/c,2)
654 + D*pow(x/c,3) - E * pow(x/c,4)) + x/c;
655 return l*c;
656}
657
658real_t NACA4::dlendx(real_t xi) const
659{
660 return 1 + dydx(xi);
661}
662
663real_t NACA4::xl(real_t l) const
664{
665 real_t x = l; // Initial guess, length should be a good one
666 real_t h;
667 int i = 0;
668 do
669 {
670 x = abs(x); // The function and its derivative do not exist for x < 0
671 // Newton step: x(i+1) = x(i) - f(x) / f'(x)
672 h = (len(x) - l)/dlendx(x);
673 x = x - h;
674 }
675 while (abs(h) >= epsilon && i++ < iter_max);
676
677 if (i >= iter_max) { mfem_error("Did not find root"); }
678 return x;
679}
680
681void GetTipXY(const NACA4 &foil_section, const KnotVector &kv, real_t tf,
682 Vector &x,Vector &y)
683{
684 int ncp = kv.GetNCP();
685 // Length of half the curve: the boundary covers both sides of the tip
686 const real_t l = foil_section.len(tf * foil_section.GetChord());
687
688 // Find location of maxima of knot vector
689 Array<int> i_args(ncp);
690 Vector xi_args(ncp), u_args(ncp);
691 // kv.FindMaxima(i_args,xi_args, u_args);
692 for (int i = 0; i < ncp; i++)
693 {
694 u_args[i] = kv.GetBotella(i);
695 i_args[i] = kv.GetSpan(u_args[i]) - kv.GetOrder();
696 xi_args[i] = kv.GetRefPoint(u_args[i],i_args[i]+kv.GetOrder());
697 }
698
699 // We have two cases: one with an odd number of control points and one
700 // with an even number of control points.
701 const int n = ncp/2;
702 if (ncp % 2)
703 {
704 // Find arc lengths to control points on upperside of foil section
705 // then find x-coordinates.
706 Vector xcp(n+1);
707 for (int i = 0; i < n+1; i++)
708 {
709 real_t u = 2*(u_args[n+i]-0.5);
710 real_t lcp = u * l;
711 xcp[i] = foil_section.xl(lcp);
712 }
713
714 // Find corresponding xy vector
715 x[n] = 0; y[n] = 0; // Foil section tip
716 for (int i = 0; i < n; i++)
717 {
718 // Lower half
719 x[i] = xcp[n-i];
720 y[i] = -foil_section.y(xcp[n-i]);
721
722 // Upper half
723 x[n+1+i] = xcp[i+1];
724 y[n+1+i] = foil_section.y(xcp[i+1]);
725 }
726 }
727 else
728 {
729 // Find arc lengths to control points on upperside of foil section then
730 // find x-coordinates
731 Vector xcp(n);
732 for (int i = 0; i < n; i++)
733 {
734 real_t u = 2*(u_args[n+i]-0.5);
735 real_t lcp = u * l;
736 xcp[i] = foil_section.xl(lcp);
737 }
738 // Find corresponding xy vector
739 for (int i = 0; i < n; i++)
740 {
741 // Lower half
742 x[i] = xcp[n-1-i];
743 y[i] = -foil_section.y(xcp[n-1-i]);
744
745 // Upper half
746 x[n+i] = xcp[i];
747 y[n+i] = foil_section.y(xcp[i]);
748 }
749 }
750}
751
752unique_ptr<KnotVector> UniformKnotVector(int order, int ncp)
753{
754 unique_ptr<KnotVector> kv(new KnotVector(order, ncp));
755
756 for (int i = 0; i < order+1; i++)
757 {
758 (*kv)[i] = 0.0;
759 }
760 for (int i = order+1; i < ncp; i++)
761 {
762 (*kv)[i] = (i-order)/real_t(ncp-order);
763 }
764 for (int i = ncp ; i < ncp + order + 1; i++)
765 {
766 (*kv)[i] = 1.0;
767 }
768 return kv;
769}
770
771unique_ptr<KnotVector> PowerStretchKnotVector(int order, int ncp, real_t s)
772{
773 unique_ptr<KnotVector> kv(new KnotVector(order, ncp));
774
775 for (int i = 0; i < order+1; i++)
776 {
777 (*kv)[i] = 0.0;
778 }
779 for (int i = order+1; i < ncp; i++)
780 {
781 (*kv)[i] = (i-order)/real_t(ncp-order);
782 if (s > 0) { (*kv)[i] = pow((*kv)[i], s); }
783 if (s < 0) { (*kv)[i] = 1.0 - pow(1.0-(*kv)[i], -s); }
784 }
785 for (int i = ncp ; i < ncp + order + 1; i++)
786 {
787 (*kv)[i] = 1.0;
788 }
789 return kv;
790}
791
792unique_ptr<KnotVector> TanhKnotVector(int order, int ncp, real_t c)
793{
794 unique_ptr<KnotVector> kv(new KnotVector(order, ncp));
795
796 for (int i = 0; i < order+1; i++)
797 {
798 (*kv)[i] = 0.0;
799 }
800 for (int i = order+1; i < ncp; i++)
801 {
802 (*kv)[i] = (i-order)/real_t(ncp-order);
803 (*kv)[i] = 1 + tanh(c * ((*kv)[i]-1))/tanh(c);
804 }
805 for (int i = ncp ; i < ncp + order + 1; i++)
806 {
807 (*kv)[i] = 1.0;
808 }
809 return kv;
810}
811
812unique_ptr<KnotVector> DoubleTanhKnotVector(int order, int ncp, real_t c)
813{
814 unique_ptr<KnotVector> kv(UniformKnotVector(order, ncp));
815
816 for (int i = 0; i < order+1; i++)
817 {
818 (*kv)[i] = 0.0;
819 }
820 for (int i = order+1; i < ncp; i++)
821 {
822 if ((*kv)[i] < 0.5)
823 {
824 (*kv)[i] = -1 + 2*( 1 - (i-order)/real_t(ncp-order));
825 (*kv)[i] = 0.5 * abs((tanh(c * ((*kv)[i]-1))/tanh(c)));
826 }
827 else
828 {
829 (*kv)[i] = 2*((i-order)/real_t(ncp-order) - 0.5);
830 (*kv)[i] = 0.5 +(1 + tanh(c * ((*kv)[i]-1))/tanh(c))/2;
831 }
832 }
833 for (int i = ncp ; i < ncp + order + 1; i++)
834 {
835 (*kv)[i] = 1.0;
836 }
837 return kv;
838}
839
841{
842 real_t b = sin(flair);
843 real_t c = bd*cos(flair) + bd * sin(flair) * sin(flair);
844 return b * x + c;
845}
void SetCycle(int c)
Set time cycle (for time-dependent simulations)
void SetTime(real_t t)
Set physical time (for time-dependent simulations)
void SetPrefixPath(const std::string &prefix)
Set the path where the DataCollection will be saved.
A vector of knots in one dimension, with B-spline basis functions of a prescribed order.
Definition nurbs.hpp:38
real_t GetRefPoint(real_t u, int ni) const
Return the reference coordinate in [0,1] for parameter u in the element beginning at knot ni.
Definition nurbs.hpp:143
real_t GetBotella(int i) const
Definition nurbs.cpp:230
int GetOrder() const
Return the order.
Definition nurbs.hpp:114
int GetNCP() const
Return the number of control points.
Definition nurbs.hpp:111
int GetSpan(real_t u) const
Return the index of the knot span containing parameter u.
Definition nurbs.cpp:175
Mesh data type.
Definition mesh.hpp:67
virtual void Print(std::ostream &os=mfem::out, const std::string &comments="") const
Print the mesh to the given stream using the default MFEM mesh format.
Definition mesh.hpp:2610
virtual void PrintInfo(std::ostream &os=mfem::out)
In serial, this method calls PrintCharacteristics(). In parallel, additional information about the pa...
Definition mesh.hpp:2699
A NURBS patch can be 1D, 2D, or 3D, and is defined as a tensor product of KnotVectors.
Definition nurbs.hpp:324
void Rotate2D(real_t angle)
Rotate the NURBSPatch, 2D case.
Definition nurbs.cpp:2404
void KnotInsert(int dir, const KnotVector &knot)
Insert any new knots from knot in direction dir. If the order of knot is higher than the current orde...
Definition nurbs.cpp:1714
void Print(std::ostream &os) const
Writes KnotVectors and data to the stream os.
Definition nurbs.cpp:1484
void DegreeElevate(int dir, int t)
Increase the order in direction dir by t >= 0.
Definition nurbs.cpp:2082
void Parse()
Parse the command-line options. Note that this function expects all the options provided through the ...
void PrintUsage(std::ostream &out) const
Print the usage message.
void PrintOptions(std::ostream &out) const
Print the options.
void AddOption(bool *var, const char *enable_short_name, const char *enable_long_name, const char *disable_short_name, const char *disable_long_name, const char *description, bool required=false)
Add a boolean option and set 'var' to receive the value. Enable/disable tags are used to set the bool...
Definition optparser.hpp:82
bool Good() const
Return true if the command line options were parsed successfully.
Vector data type.
Definition vector.hpp:82
Data collection with VisIt I/O routines.
void Save() override
Save the collection and a VisIt root file.
int main()
real_t b
Definition lissajous.cpp:42
MFEM_HOST_DEVICE dual< value_type, gradient_type > sqrt(dual< value_type, gradient_type > x)
implementation of square root for dual numbers
Definition dual.hpp:288
MFEM_HOST_DEVICE dual< value_type, gradient_type > pow(dual< value_type, gradient_type > a, dual< value_type, gradient_type > b)
implementation of a (dual) raised to the b (dual) power
Definition dual.hpp:374
real_t u(const Vector &xvec)
Definition lor_mms.hpp:22
void mfem_error(const char *msg)
Definition error.cpp:154
float real_t
Definition config.hpp:46
STL namespace.
unique_ptr< KnotVector > DoubleTanhKnotVector(int order, int ncp, real_t c)
void GetTipXY(const NACA4 &foil_section, const KnotVector &kv, real_t tf, Vector &x, Vector &y)
unique_ptr< KnotVector > PowerStretchKnotVector(int order, int ncp, real_t s=0.0)
real_t FlairBoundDist(real_t flair, real_t bd, real_t x)
unique_ptr< KnotVector > TanhKnotVector(int order, int ncp, real_t c)
unique_ptr< KnotVector > UniformKnotVector(int order, int ncp)
MFEM_HOST_DEVICE real_t abs(const Complex &z)