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