pyfoiltools 0.0.1__tar.gz → 0.0.2__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: pyfoiltools
3
- Version: 0.0.1
3
+ Version: 0.0.2
4
4
  Summary: Python Package containing tools for pyfoil
5
5
  Author-email: Xero64 <xero64@gmail.com>
6
6
  License-Expression: MIT
@@ -8,7 +8,7 @@ Project-URL: Homepage, https://github.com/Xero64/pyfoiltools
8
8
  Project-URL: Issues, https://github.com/Xero64/pyfoiltools/issues
9
9
  Classifier: Programming Language :: Python :: 3
10
10
  Classifier: Operating System :: OS Independent
11
- Requires-Python: >=3.10
11
+ Requires-Python: >=3.13
12
12
  Description-Content-Type: text/markdown
13
13
  License-File: LICENSE
14
14
  Requires-Dist: matplotlib>=3.10.5
@@ -1,12 +1,16 @@
1
+ [build-system]
2
+ requires = ["setuptools>=61.0"]
3
+ build-backend = "setuptools.build_meta"
4
+
1
5
  [project]
2
6
  name = "pyfoiltools"
3
- version = "0.0.1"
7
+ version = "0.0.2"
4
8
  authors = [
5
9
  { name="Xero64", email="xero64@gmail.com" },
6
10
  ]
7
11
  description = "Python Package containing tools for pyfoil"
8
12
  readme = "README.md"
9
- requires-python = ">=3.10"
13
+ requires-python = ">=3.13"
10
14
  classifiers = [
11
15
  "Programming Language :: Python :: 3",
12
16
  "Operating System :: OS Independent",
@@ -6,6 +6,7 @@ from numpy.linalg import lstsq, norm
6
6
  from py2md.classes import MDReport
7
7
  from pygeom.geom2d import BSplineCurve2D, Vector2D
8
8
  from pygeom.tools.solvers import solve_clsq
9
+ from pygeom.tools.spacing import full_cosine_spacing
9
10
 
10
11
  from .airfoilpoly import AirfoilPoly, AirfoilPolyFit
11
12
  from .airfoilshape import AirfoilShape, AirfoilShapeFit
@@ -19,25 +20,25 @@ if TYPE_CHECKING:
19
20
  class AirfoilBSpline():
20
21
  name: str = None
21
22
  adef: AirfoilPoly | AirfoilShape = None
22
- bspline_u: 'BSplineCurve2D' = None
23
- bspline_l: 'BSplineCurve2D' = None
23
+ bspline: 'BSplineCurve2D' = None
24
24
  c: float = None
25
25
  num: int = None
26
- _spacing: 'ChordSpacing' = None
26
+ _tu: 'NDArray' = None
27
27
  _xu: 'NDArray' = None
28
28
  _yu: 'NDArray' = None
29
+ _tl: 'NDArray' = None
29
30
  _xl: 'NDArray' = None
30
31
  _yl: 'NDArray' = None
32
+ # _t: 'NDArray' = None
31
33
  _x: 'NDArray' = None
32
34
  _y: 'NDArray' = None
33
35
 
34
36
  def __init__(self, name: str, adef: AirfoilPoly | AirfoilShape,
35
- bspline_u: 'BSplineCurve2D', bspline_l: 'BSplineCurve2D',
37
+ bspline: 'BSplineCurve2D',
36
38
  c: float = 1.0, num: int = 101) -> None:
37
39
  self.name = name
38
40
  self.adef = adef
39
- self.bspline_u = bspline_u
40
- self.bspline_l = bspline_l
41
+ self.bspline = bspline
41
42
  self.c = c
42
43
  self.num = num
43
44
 
@@ -50,53 +51,65 @@ class AirfoilBSpline():
50
51
  self.c = c
51
52
 
52
53
  @property
53
- def spacing(self) -> ChordSpacing:
54
- if self._spacing is None:
55
- self._spacing = ChordSpacing.from_num(self.num)
56
- return self._spacing
57
-
58
- @property
59
- def th(self) -> 'NDArray':
60
- return self.spacing.th
54
+ def tu(self) -> 'NDArray':
55
+ if self._tu is None:
56
+ self._tu = full_cosine_spacing(self.num)*0.5 + 0.5
57
+ return self._tu
61
58
 
62
59
  @property
63
60
  def xu(self) -> 'NDArray':
64
61
  if self._xu is None:
65
- pnts_u = self.bspline_u.evaluate_points_at_t(self.spacing.s)
62
+ pnts_u = self.bspline.evaluate_points_at_t(self.tu)
66
63
  self._xu, self._yu = pnts_u.to_xy()
67
64
  return self._xu
68
65
 
69
66
  @property
70
67
  def yu(self) -> 'NDArray':
71
68
  if self._yu is None:
72
- pnts_u = self.bspline_u.evaluate_points_at_t(self.spacing.s)
69
+ pnts_u = self.bspline.evaluate_points_at_t(self.tu)
73
70
  self._xu, self._yu = pnts_u.to_xy()
74
71
  return self._yu
75
72
 
73
+ @property
74
+ def tl(self) -> 'NDArray':
75
+ if self._tl is None:
76
+ self._tl = (full_cosine_spacing(self.num)*0.5)[::-1]
77
+ return self._tl
78
+
76
79
  @property
77
80
  def xl(self) -> 'NDArray':
78
81
  if self._xl is None:
79
- pnts_l = self.bspline_l.evaluate_points_at_t(self.spacing.s)
82
+ pnts_l = self.bspline.evaluate_points_at_t(self.tl)
80
83
  self._xl, self._yl = pnts_l.to_xy()
81
84
  return self._xl
82
85
 
83
86
  @property
84
87
  def yl(self) -> 'NDArray':
85
88
  if self._yl is None:
86
- pnts_l = self.bspline_l.evaluate_points_at_t(self.spacing.s)
89
+ pnts_l = self.bspline.evaluate_points_at_t(self.tl)
87
90
  self._xl, self._yl = pnts_l.to_xy()
88
91
  return self._yl
89
92
 
93
+ @property
94
+ def t(self) -> 'NDArray':
95
+ if self._t is None:
96
+ self._t = concatenate((self.tl[::-1], self.tu[1:]))
97
+ return self._t
98
+
90
99
  @property
91
100
  def x(self) -> 'NDArray':
92
101
  if self._x is None:
93
102
  self._x = hstack((self.xl[::-1], self.xu[1:]))
103
+ # pnts = self.bspline.evaluate_points_at_t(self.t)
104
+ # self._x, self._y = pnts.to_xy()
94
105
  return self._x
95
106
 
96
107
  @property
97
108
  def y(self) -> 'NDArray':
98
109
  if self._y is None:
99
110
  self._y = hstack((self.yl[::-1], self.yu[1:]))
111
+ # pnts = self.bspline.evaluate_points_at_t(self.t)
112
+ # self._x, self._y = pnts.to_xy()
100
113
  return self._y
101
114
 
102
115
  def plot_shape(self, ax: 'Axes' = None, **kwargs: dict[str, Any]) -> 'Axes':
@@ -123,13 +136,12 @@ class AirfoilBSpline():
123
136
  return self.to_mdobj()._repr_markdown_()
124
137
 
125
138
  def __str__(self) -> str:
126
- return f'AirfoilBSpline: {self.name:s}\nBSpline_u = \n{self.bspline_u}\nBSpline_l = \n{self.bspline_l}'
139
+ return f'AirfoilBSpline: {self.name:s}\nBSpline = \n{self.bspline}\n'
127
140
 
128
141
 
129
142
  class AirfoilBSplineFit():
130
143
  afit: AirfoilPolyFit | AirfoilShapeFit = None
131
- Nu: int = None
132
- Nl: int = None
144
+ Nb: int = None
133
145
  _adef: AirfoilPoly | AirfoilShape = None
134
146
  _spcu: ChordSpacing = None
135
147
  _THu: 'NDArray' = None
@@ -157,10 +169,9 @@ class AirfoilBSplineFit():
157
169
  _lmda: 'NDArray' = None
158
170
 
159
171
  def __init__(self, afit: AirfoilPolyFit | AirfoilShapeFit,
160
- Nu: int, Nl: int) -> None:
172
+ Nb: int) -> None:
161
173
  self.afit = afit
162
- self.Nu = Nu
163
- self.Nl = Nl
174
+ self.Nb = Nb
164
175
 
165
176
  @property
166
177
  def adef(self) -> AirfoilPoly | AirfoilShape:
@@ -176,7 +187,7 @@ class AirfoilBSplineFit():
176
187
  @property
177
188
  def spcu(self) -> ChordSpacing:
178
189
  if self._spcu is None:
179
- self._spcu = ChordSpacing.from_num(self.Nu)
190
+ self._spcu = ChordSpacing.from_num(self.Nb)
180
191
  return self._spcu
181
192
 
182
193
  @property
@@ -220,7 +231,7 @@ class AirfoilBSplineFit():
220
231
  @property
221
232
  def spcl(self) -> ChordSpacing:
222
233
  if self._spcl is None:
223
- self._spcl = ChordSpacing.from_num(self.Nl)
234
+ self._spcl = ChordSpacing.from_num(self.Nb)
224
235
  return self._spcl
225
236
 
226
237
  @property
@@ -265,37 +276,46 @@ class AirfoilBSplineFit():
265
276
  def aspl(self) -> AirfoilBSpline:
266
277
  if self._aspl is None:
267
278
 
268
- ctlpnts_u = Vector2D.zeros(self.Nu + 1)
269
- ctlpnts_u.x[1:] = self.Xcu + self.Ucu*self.Tcu
270
- ctlpnts_u.y[1:] = self.Ycu + self.Vcu*self.Tcu
271
- bspline_u = BSplineCurve2D(ctlpnts_u, degree=self.Nu)
279
+ Xl = self.Xcl + self.Ucl*self.Tcl
280
+ Yl = self.Ycl + self.Vcl*self.Tcl
281
+
282
+ Xu = self.Xcu + self.Ucu*self.Tcu
283
+ Yu = self.Ycu + self.Vcu*self.Tcu
284
+
285
+ Xb = concatenate((Xl[::-1], zeros((1,)), Xu))
286
+ Yb = concatenate((Yl[::-1], zeros((1,)), Yu))
287
+
288
+ ctlpnts = Vector2D(Xb, Yb)
272
289
 
273
- ctlpnts_l = Vector2D.zeros(self.Nl + 1)
274
- ctlpnts_l.x[1:] = self.Xcl + self.Ucl*self.Tcl
275
- ctlpnts_l.y[1:] = self.Ycl + self.Vcl*self.Tcl
276
- bspline_l = BSplineCurve2D(ctlpnts_l, degree=self.Nl)
290
+ bspline = BSplineCurve2D(ctlpnts, degree=self.Nb)
277
291
 
278
292
  self._aspl = AirfoilBSpline(self.adef.name, self.adef,
279
- bspline_u, bspline_l)
293
+ bspline)
280
294
 
281
295
  return self._aspl
282
296
 
283
297
  @property
284
298
  def tu(self) -> 'NDArray':
285
299
  if self._tu is None:
286
- self._tu = self.afit.spcu.s.copy()
287
- self._tu = bspline_lstsq_fit_t(self.aspl.bspline_u, self.afit.xu,
300
+ self._tu = self.afit.spcu.s.copy()*0.5 + 0.5
301
+ self._tu = bspline_lstsq_fit_t(self.aspl.bspline, self.afit.xu,
288
302
  self.afit.yu, self._tu)
289
303
  return self._tu
290
304
 
291
305
  @property
292
306
  def tl(self) -> 'NDArray':
293
307
  if self._tl is None:
294
- self._tl = self.afit.spcl.s.copy()
295
- self._tl = bspline_lstsq_fit_t(self.aspl.bspline_l, self.afit.xl,
308
+ self._tl = (self.afit.spcl.s.copy()*0.5)[::-1]
309
+ self._tl = bspline_lstsq_fit_t(self.aspl.bspline, self.afit.xl,
296
310
  self.afit.yl, self._tl)
297
311
  return self._tl
298
312
 
313
+ @property
314
+ def t(self) -> 'NDArray':
315
+ if self._t is None:
316
+ self._t = concatenate((self.tu, self.tl))
317
+ return self._t
318
+
299
319
  @property
300
320
  def num_Tu(self) -> int:
301
321
  return self.Tcu.size - 1
@@ -373,16 +393,12 @@ class AirfoilBSplineFit():
373
393
  ax = self.adef.plot_shape(ax=ax)
374
394
  ax = self.afit.airfoil.plot(ax=ax)
375
395
 
376
- pnts_u = self.aspl.bspline_u.evaluate_points(100)
377
- pnts_l = self.aspl.bspline_l.evaluate_points(100)
396
+ pnts = self.aspl.bspline.evaluate_points(100)
378
397
 
379
- ctls_u = self.aspl.bspline_u.ctlpnts
380
- ctls_l = self.aspl.bspline_l.ctlpnts
398
+ ctls = self.aspl.bspline.ctlpnts
381
399
 
382
- ax.plot(pnts_u.x, pnts_u.y, label='BSpline Fit Upper')
383
- ax.plot(pnts_l.x, pnts_l.y, label='BSpline Fit Lower')
384
- ax.plot(ctls_u.x, ctls_u.y, '-o', label='Control Points Upper')
385
- ax.plot(ctls_l.x, ctls_l.y, '-o', label='Control Points Lower')
400
+ ax.plot(pnts.x, pnts.y, label='BSpline Fit')
401
+ ax.plot(ctls.x, ctls.y, '-o', label='Control Points')
386
402
  ax.plot(self.afit.xu, self.afit.yu, 'x', label='Target Points Upper')
387
403
  ax.plot(self.afit.xl, self.afit.yl, 'x', label='Target Points Lower')
388
404
  _ = ax.legend()
@@ -390,41 +406,45 @@ class AirfoilBSplineFit():
390
406
 
391
407
  def improved_fit(self, display: bool = False) -> tuple[float, float]:
392
408
 
393
- xu, yu = self.aspl.bspline_u.evaluate_points_at_t(self.tu).to_xy()
409
+ xu, yu = self.aspl.bspline.evaluate_points_at_t(self.tu).to_xy()
394
410
 
395
411
  Dxu = xu - self.afit.xu
396
412
  Dyu = yu - self.afit.yu
397
413
 
398
- xl, yl = self.aspl.bspline_l.evaluate_points_at_t(self.tl).to_xy()
414
+ xl, yl = self.aspl.bspline.evaluate_points_at_t(self.tl).to_xy()
399
415
 
400
416
  Dxl = xl - self.afit.xl
401
417
  Dyl = yl - self.afit.yl
402
418
 
403
419
  f = concatenate((Dxu, Dyu, Dxl, Dyl))
404
420
 
405
- Ntu = self.aspl.bspline_u.basis_functions(self.tu).transpose()
421
+ Nt = self.aspl.bspline.basis_functions(self.tu).transpose()
422
+
423
+ Ntu = Nt[:, self.Nb:]
406
424
 
407
- dDxudXu = Ntu[:, 1:self.Nu]
408
- dDyudYu = Ntu[:, 1:self.Nu]
425
+ dDxudXu = Ntu[:, 1:self.Nb]
426
+ dDyudYu = Ntu[:, 1:self.Nb]
409
427
 
410
- Ntl = self.aspl.bspline_l.basis_functions(self.tl).transpose()
428
+ Nt = self.aspl.bspline.basis_functions(self.tl).transpose()
411
429
 
412
- dDxldXl = Ntl[:, 1:self.Nl]
413
- dDyldYl = Ntl[:, 1:self.Nl]
430
+ Ntl = Nt[:, self.Nb::-1]
431
+
432
+ dDxldXl = Ntl[:, 1:self.Nb]
433
+ dDyldYl = Ntl[:, 1:self.Nb]
414
434
 
415
435
  dDxudTu = dDxudXu@self.dXudTu + dDyudYu@self.dYudTu
416
- dDxudTl = zeros((xu.size, self.Nl - 1))
436
+ dDxudTl = zeros((xu.size, self.Nb - 1))
417
437
 
418
438
  dDyudTu = dDyudYu@self.dYudTu + dDyudYu@self.dYudTu
419
- dDyudTl = zeros((yu.size, self.Nl - 1))
439
+ dDyudTl = zeros((yu.size, self.Nb - 1))
420
440
 
421
441
  dDxldTl = dDxldXl@self.dXldTl + dDyldYl@self.dYldTl
422
- dDxldTu = zeros((xl.size, self.Nu - 1))
442
+ dDxldTu = zeros((xl.size, self.Nb - 1))
423
443
 
424
444
  dDyldTl = dDyldYl@self.dYldTl + dDyldYl@self.dYldTl
425
- dDyldTu = zeros((yl.size, self.Nu - 1))
445
+ dDyldTu = zeros((yl.size, self.Nb - 1))
426
446
 
427
- dDxudtu, dDyudtu = self.aspl.bspline_u.evaluate_first_derivatives_at_t(self.tu).to_xy()
447
+ dDxudtu, dDyudtu = self.aspl.bspline.evaluate_first_derivatives_at_t(self.tu).to_xy()
428
448
 
429
449
  dDxudtu = diag(dDxudtu)
430
450
  dDxudtl = zeros((xu.size, self.tl.size))
@@ -432,7 +452,7 @@ class AirfoilBSplineFit():
432
452
  dDyudtu = diag(dDyudtu)
433
453
  dDyudtl = zeros((yu.size, self.tl.size))
434
454
 
435
- dDxldtl, dDyldtl = self.aspl.bspline_l.evaluate_first_derivatives_at_t(self.tl).to_xy()
455
+ dDxldtl, dDyldtl = self.aspl.bspline.evaluate_first_derivatives_at_t(self.tl).to_xy()
436
456
 
437
457
  dDxldtl = diag(dDxldtl)
438
458
  dDxldtu = zeros((xl.size, self.tu.size))
@@ -441,9 +461,9 @@ class AirfoilBSplineFit():
441
461
  dDyldtu = zeros((yl.size, self.tu.size))
442
462
 
443
463
  dfdTt = block([[dDxudTu, dDxudTl, dDxudtu, dDxudtl],
444
- [dDyudTu, dDyudTl, dDyudtu, dDyudtl],
445
- [dDxldTu, dDxldTl, dDxldtu, dDxldtl],
446
- [dDyldTu, dDyldTl, dDyldtu, dDyldtl]])
464
+ [dDyudTu, dDyudTl, dDyudtu, dDyudtl],
465
+ [dDxldTu, dDxldTl, dDxldtu, dDxldtl],
466
+ [dDyldTu, dDyldTl, dDyldtu, dDyldtl]])
447
467
 
448
468
  dvTt, dlTt = solve_clsq(dfdTt, f, self.carr, self.darr)
449
469
 
@@ -473,11 +493,13 @@ class AirfoilBSplineFit():
473
493
 
474
494
  return normdcon, normdvar
475
495
 
476
- def improved_fit_loop(self, tol_dcon: float = 1e-12, tol_dvar: float = 1e-3,
477
- max_iter: int = 50, display: bool = False) -> tuple[float, float]:
496
+ def improved_fit_loop(self, tol_dcon: float = 1e-12,
497
+ tol_dvar: float = 1e-3, max_iter: int = 50,
498
+ display: bool = False) -> tuple[float, float, bool]:
478
499
 
479
500
  normdcon, normdvar = float('inf'), float('inf')
480
501
 
502
+ conv = True
481
503
  iter = 1
482
504
  while normdcon > tol_dcon or normdvar > tol_dvar:
483
505
  normdcon, normdvar = self.improved_fit()
@@ -487,11 +509,14 @@ class AirfoilBSplineFit():
487
509
  print(f'normdvar = {normdvar:.12f}')
488
510
  print()
489
511
  if iter >= max_iter:
512
+ conv = False
490
513
  err = f'Maximum number of iterations ({max_iter}) reached.'
491
- raise RuntimeError(err)
514
+ print(err)
515
+ break
516
+ # raise RuntimeError(err)
492
517
  iter += 1
493
518
 
494
- return normdcon, normdvar
519
+ return normdcon, normdvar, conv
495
520
 
496
521
  def __repr__(self) -> str:
497
522
  return f'<AirfoilBSplineFit: {self.adef.name:s}>'
@@ -702,16 +702,16 @@ class AirfoilPoly():
702
702
  def to_mdobj(self) -> MDReport:
703
703
  report = MDReport()
704
704
  report.add_heading(f'Airfoil Shape: {self.name:s}', level=1)
705
- table = report.add_table()
706
705
  if self.Cn.size > 0:
706
+ table = report.add_table()
707
707
  table.add_column('Leading Edge Camber %', '.3f', data=[self.yc_le/self.c])
708
708
  table.add_column('Trailing Edge Camber %', '.3f', data=[self.yc_te/self.c])
709
709
  table.add_column('Max Camber %', '.3f', data=[self.max_yc/self.c*100])
710
710
  table.add_column('Max Camber Position %', '.3f', data=[self.max_yc_pc*100])
711
711
  table.add_column('Leading Edge Angle (deg)', '.3f', data=[degrees(self.thy_le)])
712
712
  table.add_column('Trailing Edge Angle (deg)', '.3f', data=[degrees(self.thy_te)])
713
- table = report.add_table()
714
713
  if self.Tn.size > 0:
714
+ table = report.add_table()
715
715
  table.add_column('Trailing Edge Thickness %', '.3f', data=[self.tc_te/self.c])
716
716
  table.add_column('Max Thickness %', '.3f', data=[self.max_tc/self.c*100])
717
717
  table.add_column('Max Thickness Position %', '.3f', data=[self.max_tc_pc*100])
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: pyfoiltools
3
- Version: 0.0.1
3
+ Version: 0.0.2
4
4
  Summary: Python Package containing tools for pyfoil
5
5
  Author-email: Xero64 <xero64@gmail.com>
6
6
  License-Expression: MIT
@@ -8,7 +8,7 @@ Project-URL: Homepage, https://github.com/Xero64/pyfoiltools
8
8
  Project-URL: Issues, https://github.com/Xero64/pyfoiltools/issues
9
9
  Classifier: Programming Language :: Python :: 3
10
10
  Classifier: Operating System :: OS Independent
11
- Requires-Python: >=3.10
11
+ Requires-Python: >=3.13
12
12
  Description-Content-Type: text/markdown
13
13
  License-File: LICENSE
14
14
  Requires-Dist: matplotlib>=3.10.5
File without changes
File without changes
File without changes