MFEM v4.9.0
Finite element discretization library
Loading...
Searching...
No Matches
nurbs_naca_cmesh.cpp
Go to the documentation of this file.
1// Copyright (c) 2010-2025, 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 Array<Vector*> &xy);
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 // Variables required for curve interpolation
225 Vector xi_args, u_args;
226 Array<int> i_args;
227 Array<Vector*> xyf(2);
228 xyf[0] = new Vector();
229 xyf[1] = new Vector();
230
231 // 3. Create required (variables for) curves: foil_section and flair
232 const NACA4 foil_section(foil_thickness, foil_length);
233
234 // The default flair angle is defined to be the same as the angle of the
235 // curve of the foil section to create an elegant mesh.
236 if (flair == -999)
237 {
238 flair = atan(foil_section.dydx(tip_fraction*foil_length));
239 }
240
241 // 4. We map coordinates in patches, apply refinement and interpolate the
242 // foil section in patches 1, 2 and 3. Note the case of non-unity weights
243 // in patch 2 to create a circular shape: its coordinates are converted to
244 // homogeneous coordinates. This is not needed for other patches as
245 // homogeneous coordinates and Cartesian coordinates are the same
246 // for patches with unity weight.
247
248 // Patch 0: lower wake part behind foil section.
249 NURBSPatch patch0(kv_o1.get(), kv_o1.get(), 3);
250 {
251 for (int i = 0; i < 2; i++)
252 for (int j = 0; j < 2; j++)
253 {
254 patch0(i,j,2) = 1.0;
255 }
256
257 // Define points
258 patch0(0,0,0) = foil_length + wake_length;
259 patch0(0,0,1) = 0.0;
260
261 patch0(1,0,0) = foil_length;
262 patch0(1,0,1) = 0.0;
263
264 patch0(0,1,0) = foil_length + wake_length;
265 patch0(0,1,1) = -FlairBoundDist(flair, boundary_dist, patch0(0,1,0));
266
267 patch0(1,1,0) = foil_length;
268 patch0(1,1,1) = -FlairBoundDist(flair, boundary_dist, patch0(1,1,0));
269
270 // Refine
271 patch0.DegreeElevate(0, order-1);
272 patch0.KnotInsert(0, *kv0);
273 patch0.DegreeElevate(1, order-1);
274 patch0.KnotInsert(1, *kvr);
275 }
276
277 // Patch 1: Lower tail of foil
278 NURBSPatch patch1(kv_o1.get(), kv_o1.get(), 3);
279 {
280 for (int i = 0; i < 2; i++)
281 for (int j = 0; j < 2; j++)
282 {
283 patch1(i,j,2) = 1.0;
284 }
285
286 // Define points
287 patch1(0,0,0) = foil_length;
288 patch1(0,0,1) = 0.0;
289
290 patch1(1,0,0) = tip_fraction*foil_length;
291 patch1(1,0,1) = -foil_section.y(patch1(1,0,0));
292
293 patch1(0,1,0) = foil_length;
294 patch1(0,1,1) = -FlairBoundDist(flair, boundary_dist, patch1(0,1,0));
295
296 patch1(1,1,0) = -boundary_dist*sin(flair) + tip_fraction*foil_length;
297 patch1(1,1,1) = -boundary_dist*cos(flair);
298
299 // Refine
300 patch1.DegreeElevate(0, order-1);
301 patch1.KnotInsert(0, *kv1);
302
303 int ncp = kv1->GetNCP();
304 xyf[0]->SetSize(ncp); xyf[1]->SetSize(ncp);
305
306 // Project foil
307 kv1->FindMaxima(i_args,xi_args, u_args);
308 for (int i = 0; i < ncp; i++)
309 {
310 (*xyf[0])[i] = foil_length*(1.0 - tail_fraction*u_args[i]);
311 (*xyf[1])[i] = -foil_section.y((*xyf[0])[i]);
312 }
313
314 kv1->FindInterpolant(xyf);
315 for (int i = 0; i < ncp; i++)
316 {
317 patch1(i,0,0) = (*xyf[0])[i];
318 patch1(i,0,1) = (*xyf[1])[i];
319 }
320
321 patch1.DegreeElevate(1, order-1);
322 patch1.KnotInsert(1, *kvr);
323 }
324
325 // Patch 2: Tip of foil section
326 NURBSPatch patch2(kv_o2.get(), kv_o1.get(), 3);
327 {
328 // Define weights
329 for (int i = 0; i < 3; i++)
330 for (int j = 0; j < 2; j++)
331 {
332 patch2(i,j,2) = 1.0;
333 }
334
335 // Define points
336 patch2(2,0,0) = tip_fraction*foil_length;
337 patch2(2,0,1) = foil_section.y(patch2(2,0,0));
338
339 patch2(1,0,0) = 0.0;
340 patch2(1,0,1) = 0.0;
341 patch2(1,0,2) = cos((180*deg2rad-2*flair)/2);
342
343 patch2(0,0,0) = tip_fraction*foil_length;
344 patch2(0,0,1) = -foil_section.y(patch2(0,0,0));
345
346
347 patch2(2,1,0) = -boundary_dist*cos(90*deg2rad-flair)
348 + tip_fraction*foil_length;
349 patch2(2,1,1) = boundary_dist*sin(90*deg2rad-flair);
350
351 patch2(1,1,0) = -boundary_dist/sin(flair);
352 patch2(1,1,1) = 0.0;
353 patch2(1,1,2) = cos((180*deg2rad-2*flair)/2);
354
355 patch2(0,1,0) = -boundary_dist*cos(90*deg2rad-flair)
356 + tip_fraction*foil_length;
357 patch2(0,1,1) = -boundary_dist*sin(90*deg2rad-flair);
358
359 // Deal with non-uniform weight: convert to homogeneous coordinates
360 patch2(1,0,0) *= patch2(1,0,2);
361 patch2(1,0,1) *= patch2(1,0,2);
362 patch2(1,1,0) *= patch2(1,1,2);
363 patch2(1,1,1) *= patch2(1,1,2);
364
365 // Refine
366 patch2.DegreeElevate(0, order-2);
367 patch2.KnotInsert(0, *kv2);
368
369 // Project foil
370 int ncp = kv2->GetNCP();
371 xyf[0]->SetSize(ncp); xyf[1]->SetSize(ncp);
372
373 GetTipXY(foil_section, *kv2, tip_fraction, xyf);
374
375 kv2->FindInterpolant(xyf);
376 for (int i = 0; i < ncp; i++)
377 {
378 // Also deal with non-uniform weights here: convert to homogeneous
379 // coordinates
380 patch2(i,0,0) = (*xyf[0])[i]*patch2(i,0,2);
381 patch2(i,0,1) = (*xyf[1])[i]*patch2(i,0,2);
382 }
383
384 // Project circle
385 patch2.DegreeElevate(1, order-1);
386 patch2.KnotInsert(1, *kvr);
387 }
388
389 // Patch 3: Upper part of trailing part foil section
390 NURBSPatch patch3(kv_o1.get(), kv_o1.get(), 3);
391 {
392 for (int i = 0; i < 2; i++)
393 for (int j = 0; j < 2; j++)
394 {
395 patch3(i,j,2) = 1.0;
396 }
397
398 // Define points
399 patch3(0,0,0) = tip_fraction*foil_length;
400 patch3(0,0,1) = foil_section.y(patch3(0,0,0));
401
402 patch3(1,0,0) = foil_length;
403 patch3(1,0,1) = 0.0;
404
405 patch3(0,1,0) = -boundary_dist*sin(flair) + tip_fraction*foil_length;
406 patch3(0,1,1) = boundary_dist*cos(flair);
407
408 patch3(1,1,0) = foil_length;
409 patch3(1,1,1) = FlairBoundDist(flair, boundary_dist, patch3(1,1,0));
410
411 // Refine
412 patch3.DegreeElevate(0, order-1);
413 patch3.KnotInsert(0, *kv3);
414
415 int ncp = kv3->GetNCP();
416 xyf[0]->SetSize(ncp); xyf[1]->SetSize(ncp);
417
418 // Project foil
419 kv3->FindMaxima(i_args,xi_args, u_args);
420 for (int i = 0; i < ncp; i++)
421 {
422 (*xyf[0])[i] = foil_length*(tip_fraction + tail_fraction*u_args[i]);
423 (*xyf[1])[i] = foil_section.y((*xyf[0])[i]);
424 }
425
426 kv3->FindInterpolant(xyf);
427 for (int i = 0; i < ncp; i++)
428 {
429 patch3(i,0,0) = (*xyf[0])[i];
430 patch3(i,0,1) = (*xyf[1])[i];
431 }
432
433 patch3.DegreeElevate(1, order-1);
434 patch3.KnotInsert(1, *kvr);
435 }
436
437 // Patch 4: Upper trailing wake part
438 NURBSPatch patch4(kv_o1.get(), kv_o1.get(), 3);
439 {
440 for (int i = 0; i < 2; i++)
441 for (int j = 0; j < 2; j++)
442 {
443 patch4(i,j,2) = 1.0;
444 }
445
446 // Define points
447 patch4(0,0,0) = foil_length;
448 patch4(0,0,1) = 0.0;
449
450 patch4(1,0,0) = foil_length+ wake_length;
451 patch4(1,0,1) = 0.0;
452
453 patch4(0,1,0) = foil_length;
454 patch4(0,1,1) = FlairBoundDist(flair, boundary_dist, patch4(0,1,0));
455
456 patch4(1,1,0) = foil_length+ wake_length;
457 patch4(1,1,1) = FlairBoundDist(flair, boundary_dist, patch4(1,1,0));
458
459 // Refine
460 patch4.DegreeElevate(0, order-1);
461 patch4.KnotInsert(0, *kv4);
462 patch4.DegreeElevate(1, order-1);
463 patch4.KnotInsert(1, *kvr);
464 }
465
466 // Apply angle of attack
467 patch0.Rotate2D(-aoa);
468 patch1.Rotate2D(-aoa);
469 patch2.Rotate2D(-aoa);
470 patch3.Rotate2D(-aoa);
471 patch4.Rotate2D(-aoa);
472
473 // 5. Print mesh to file
474
475 // Open mesh output file
476 string mesh_file;
477 mesh_file.append(msh_path);
478 mesh_file.append(msh_filename);
479 mesh_file.append(".mesh");
480 ofstream output(mesh_file.c_str());
481
482 // File header
483 output<<"MFEM NURBS mesh v1.0"<<endl;
484 output<< endl << "# " << mdim
485 << "D C-mesh around a symmetric NACA foil section"
486 << endl << endl;
487 output<< "dimension"<<endl;
488 output<< mdim <<endl;
489 output<< endl;
490
491 // NURBS patches defined as elements
492 output << "elements"<<endl;
493 output << "5"<<endl;
494 output << "1 3 0 1 5 4" << endl; // Lower wake
495 output << "1 3 1 2 6 5" << endl; // Lower tail
496 output << "1 3 2 3 7 6" << endl; // Tip
497 output << "1 3 3 1 8 7" << endl; // Upper tail
498 output << "1 3 1 0 9 8" << endl; // Upper wake
499 output << endl;
500
501 // Boundaries
502 output << "boundary" <<endl;
503 output << "10" <<endl;
504 output << "1 1 5 4" << endl; // Bottom
505 output << "1 1 6 5" << endl; // Bottom
506 output << "2 1 7 6" << endl; // Inflow
507 output << "3 1 8 7" << endl; // Top
508 output << "3 1 9 8" << endl; // Top
509 output << "4 1 4 0" << endl; // Outflow
510 output << "4 1 0 9" << endl; // Outflow
511 output << "5 1 1 2" << endl; // Foil section
512 output << "5 1 2 3" << endl; // Foil section
513 output << "5 1 3 1" << endl; // Foil section
514 output << endl;
515
516 // Edges
517 output <<"edges"<<endl;
518 output <<"15"<<endl;
519 output << "0 0 1"<<endl;
520 output << "1 1 2"<<endl;
521 output << "2 2 3"<<endl;
522 output << "3 3 1"<<endl;
523
524 output << "0 4 5"<<endl;
525 output << "1 5 6"<<endl;
526 output << "2 6 7"<<endl;
527 output << "3 7 8"<<endl;
528 output << "0 9 8"<<endl;
529
530 output << "4 0 4"<<endl;
531 output << "4 1 5"<<endl;
532 output << "4 2 6"<<endl;
533 output << "4 3 7"<<endl;
534 output << "4 1 8"<<endl;
535 output << "4 0 9"<<endl;
536 output << endl;
537
538 // Vertices
539 output << "vertices" << endl;
540 output << 10 << endl;
541 output << endl;
542
543 // Patches
544 output<<"patches"<<endl;
545 output<<endl;
546
547 output << "# Patch 0 " << endl;
548 patch0.Print(output); output<<endl;
549 output << "# Patch 1 " << endl;
550 patch1.Print(output); output<<endl;
551 output << "# Patch 2 " << endl;
552 patch2.Print(output); output<<endl;
553 output << "# Patch 3 " << endl;
554 patch3.Print(output); output<<endl;
555 output << "# Patch 4 " << endl;
556 patch4.Print(output); output<<endl;
557
558 // Close
559 output.close();
560 delete xyf[0];
561 delete xyf[1];
562
563 cout << endl << "Boundary identifiers:" << endl;
564 cout << " 1 Bottom" << endl;
565 cout << " 2 Inflow" << endl;
566 cout << " 3 Top" << endl;
567 cout << " 4 Outflow" << endl;
568 cout << " 5 Foil section" << endl;
569 cout << "=========================================================="<< endl;
570 cout << " "<< mdim <<"D mesh generated: " <<mesh_file.c_str()<< endl ;
571 cout << "=========================================================="<< endl;
572
573 // Print mesh info to screen
574 cout << "=========================================================="<< endl;
575 cout << " Attempting to read mesh: " <<mesh_file.c_str()<< endl ;
576 cout << "=========================================================="<< endl;
577 Mesh mesh(mesh_file.c_str(), 1, 1);
578 mesh.PrintInfo();
579
580 // Print mesh to file for visualization
581 if (visit)
582 {
583 VisItDataCollection dc = VisItDataCollection("mesh", &mesh);
584 dc.SetPrefixPath("Naca_cmesh");
585 dc.SetCycle(0);
586 dc.SetTime(0.0);
587 dc.Save();
588 }
589
590 if (visualization)
591 {
592 // Create glvis output
593 string glvis_file;
594 glvis_file.append(msh_path);
595 glvis_file.append("glvis_");
596 glvis_file.append(msh_filename);
597 glvis_file.append(".mesh");
598 ofstream glvis_output(glvis_file.c_str());
599 mesh.Print(glvis_output);
600 glvis_output.close();
601 }
602
603 return 0;
604}
605
606NACA4::NACA4(real_t t_, real_t c_)
607{
608 t = t_;
609 c = c_;
610 A = 0.2969, B = 0.1260, C = 0.3516, D = 0.2843, E = 0.1036;
611 iter_max = 1000;
612 epsilon = 1e-8;
613}
614
615real_t NACA4::y(real_t x) const
616{
617 real_t y = 5*t*(A*sqrt(x/c) - B*x/c - C*pow(x/c,2)
618 + D*pow(x/c,3) - E*pow(x/c,4));
619 return y*c;
620}
621
622real_t NACA4::dydx(real_t x) const
623{
624 real_t y = 5*t*(0.5 * A/sqrt(x/c) - B - 2*C*x/c
625 + 3* D*pow(x/c,2) - 4* E*pow(x/c,3));
626 return y*c;
627}
628
629real_t NACA4::len(real_t x) const
630{
631 real_t l = 5 * t * (A*sqrt(x/c) - B*x - C*pow(x/c,2)
632 + D*pow(x/c,3) - E * pow(x/c,4)) + x/c;
633 return l*c;
634}
635
636real_t NACA4::dlendx(real_t xi) const
637{
638 return 1 + dydx(xi);
639}
640
641real_t NACA4::xl(real_t l) const
642{
643 real_t x = l; // Initial guess, length should be a good one
644 real_t h;
645 int i = 0;
646 do
647 {
648 x = abs(x); // The function and its derivative do not exist for x < 0
649 // Newton step: x(i+1) = x(i) - f(x) / f'(x)
650 h = (len(x) - l)/dlendx(x);
651 x = x - h;
652 }
653 while (abs(h) >= epsilon && i++ < iter_max);
654
655 if (i >= iter_max) { mfem_error("Did not find root"); }
656 return x;
657}
658
659void GetTipXY(const NACA4 &foil_section, const KnotVector &kv, real_t tf,
660 Array<Vector*> &xy)
661{
662 int ncp = kv.GetNCP();
663 // Length of half the curve: the boundary covers both sides of the tip
664 const real_t l = foil_section.len(tf * foil_section.GetChord());
665
666 // Find location of maxima of knot vector
667 Array<int> i_args;
668 Vector xi_args, u_args;
669 kv.FindMaxima(i_args,xi_args, u_args);
670
671 // We have two cases: one with an odd number of control points and one
672 // with an even number of control points.
673 const int n = ncp/2;
674 if (ncp % 2)
675 {
676 // Find arc lengths to control points on upperside of foil section
677 // then find x-coordinates.
678 Vector xcp(n+1);
679 for (int i = 0; i < n+1; i++)
680 {
681 real_t u = 2*(u_args[n+i]-0.5);
682 real_t lcp = u * l;
683 xcp[i] = foil_section.xl(lcp);
684 }
685
686 // Find corresponding xy vector
687 xy[0]->SetSize(2*n+1); xy[1]->SetSize(2*n+1);
688 xy[0]->Elem(n) = 0; xy[1]->Elem(n) = 0; // Foil section tip
689 for (int i = 0; i < n; i++)
690 {
691 // Lower half
692 xy[0]->Elem(i) = xcp[n-i];
693 xy[1]->Elem(i) = -foil_section.y(xcp[n-i]);
694
695 // Upper half
696 xy[0]->Elem(n+1+i) = xcp[i+1];
697 xy[1]->Elem(n+1+i) = foil_section.y(xcp[i+1]);
698 }
699 }
700 else
701 {
702 // Find arc lengths to control points on upperside of foil section then
703 // find x-coordinates
704 Vector xcp(n);
705 for (int i = 0; i < n; i++)
706 {
707 real_t u = 2*(u_args[n+i]-0.5);
708 real_t lcp = u * l;
709 xcp[i] = foil_section.xl(lcp);
710 }
711
712 // Find corresponding xy vector
713 xy[0]->SetSize(2*n); xy[1]->SetSize(2*n);
714 for (int i = 0; i < n; i++)
715 {
716 // Lower half
717 xy[0]->Elem(i) = xcp[n-1-i];
718 xy[1]->Elem(i) = -foil_section.y(xcp[n-1-i]);
719
720 // Upper half
721 xy[0]->Elem(n+i) = xcp[i];
722 xy[1]->Elem(n+i) = foil_section.y(xcp[i]);
723 }
724 }
725}
726
727unique_ptr<KnotVector> UniformKnotVector(int order, int ncp)
728{
729 unique_ptr<KnotVector> kv(new KnotVector(order, ncp));
730
731 for (int i = 0; i < order+1; i++)
732 {
733 (*kv)[i] = 0.0;
734 }
735 for (int i = order+1; i < ncp; i++)
736 {
737 (*kv)[i] = (i-order)/real_t(ncp-order);
738 }
739 for (int i = ncp ; i < ncp + order + 1; i++)
740 {
741 (*kv)[i] = 1.0;
742 }
743 return kv;
744}
745
746unique_ptr<KnotVector> PowerStretchKnotVector(int order, int ncp, real_t s)
747{
748 unique_ptr<KnotVector> kv(new KnotVector(order, ncp));
749
750 for (int i = 0; i < order+1; i++)
751 {
752 (*kv)[i] = 0.0;
753 }
754 for (int i = order+1; i < ncp; i++)
755 {
756 (*kv)[i] = (i-order)/real_t(ncp-order);
757 if (s > 0) { (*kv)[i] = pow((*kv)[i], s); }
758 if (s < 0) { (*kv)[i] = 1.0 - pow(1.0-(*kv)[i], -s); }
759 }
760 for (int i = ncp ; i < ncp + order + 1; i++)
761 {
762 (*kv)[i] = 1.0;
763 }
764 return kv;
765}
766
767unique_ptr<KnotVector> TanhKnotVector(int order, int ncp, real_t c)
768{
769 unique_ptr<KnotVector> kv(new KnotVector(order, ncp));
770
771 for (int i = 0; i < order+1; i++)
772 {
773 (*kv)[i] = 0.0;
774 }
775 for (int i = order+1; i < ncp; i++)
776 {
777 (*kv)[i] = (i-order)/real_t(ncp-order);
778 (*kv)[i] = 1 + tanh(c * ((*kv)[i]-1))/tanh(c);
779 }
780 for (int i = ncp ; i < ncp + order + 1; i++)
781 {
782 (*kv)[i] = 1.0;
783 }
784 return kv;
785}
786
787unique_ptr<KnotVector> DoubleTanhKnotVector(int order, int ncp, real_t c)
788{
789 unique_ptr<KnotVector> kv(UniformKnotVector(order, ncp));
790
791 for (int i = 0; i < order+1; i++)
792 {
793 (*kv)[i] = 0.0;
794 }
795 for (int i = order+1; i < ncp; i++)
796 {
797 if ((*kv)[i] < 0.5)
798 {
799 (*kv)[i] = -1 + 2*( 1 - (i-order)/real_t(ncp-order));
800 (*kv)[i] = 0.5 * abs((tanh(c * ((*kv)[i]-1))/tanh(c)));
801 }
802 else
803 {
804 (*kv)[i] = 2*((i-order)/real_t(ncp-order) - 0.5);
805 (*kv)[i] = 0.5 +(1 + tanh(c * ((*kv)[i]-1))/tanh(c))/2;
806 }
807 }
808 for (int i = ncp ; i < ncp + order + 1; i++)
809 {
810 (*kv)[i] = 1.0;
811 }
812 return kv;
813}
814
816{
817 real_t b = sin(flair);
818 real_t c = bd*cos(flair) + bd * sin(flair) * sin(flair);
819 return b * x + c;
820}
void SetSize(int nsize)
Change the logical size of the array, keep existing entries.
Definition array.hpp:840
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
void FindMaxima(Array< int > &ks, Vector &xi, Vector &u) const
Gives the locations of the maxima of the KnotVector in reference space. The function gives the knot s...
Definition nurbs.cpp:569
int GetNCP() const
Return the number of control points.
Definition nurbs.hpp:86
Mesh data type.
Definition mesh.hpp:65
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:2567
virtual void PrintInfo(std::ostream &os=mfem::out)
In serial, this method calls PrintCharacteristics(). In parallel, additional information about the pa...
Definition mesh.hpp:2656
A NURBS patch can be 1D, 2D, or 3D, and is defined as a tensor product of KnotVectors.
Definition nurbs.hpp:226
void Rotate2D(real_t angle)
Rotate the NURBSPatch, 2D case.
Definition nurbs.cpp:1933
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:1243
void Print(std::ostream &os) const
Writes KnotVectors and data to the stream os.
Definition nurbs.cpp:1013
void DegreeElevate(int dir, int t)
Increase the order in direction dir by t >= 0.
Definition nurbs.cpp:1611
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
unique_ptr< KnotVector > DoubleTanhKnotVector(int order, int ncp, real_t c)
void GetTipXY(const NACA4 &foil_section, const KnotVector &kv, real_t tf, Array< Vector * > &xy)
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)