planformcreator2 4.3.0__tar.gz → 4.3.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.
Files changed (33) hide show
  1. {planformcreator2-4.3.0 → planformcreator2-4.3.2}/PKG-INFO +3 -3
  2. {planformcreator2-4.3.0 → planformcreator2-4.3.2}/README.md +1 -1
  3. {planformcreator2-4.3.0 → planformcreator2-4.3.2}/doc/README_PyPi.md +1 -1
  4. planformcreator2-4.3.2/icons/AE.ico +0 -0
  5. {planformcreator2-4.3.0 → planformcreator2-4.3.2}/planformcreator2/app.py +6 -1
  6. {planformcreator2-4.3.0 → planformcreator2-4.3.2}/planformcreator2/app_model.py +44 -36
  7. {planformcreator2-4.3.0 → planformcreator2-4.3.2}/planformcreator2/app_modes.py +14 -15
  8. {planformcreator2-4.3.0 → planformcreator2-4.3.2}/planformcreator2/model/VLM_wing.py +92 -41
  9. {planformcreator2-4.3.0 → planformcreator2-4.3.2}/planformcreator2/model/wing.py +123 -55
  10. {planformcreator2-4.3.0 → planformcreator2-4.3.2}/planformcreator2/ui/pc2_artists.py +83 -28
  11. {planformcreator2-4.3.0 → planformcreator2-4.3.2}/planformcreator2/ui/pc2_diagrams.py +67 -58
  12. {planformcreator2-4.3.0 → planformcreator2-4.3.2}/planformcreator2/ui/pc2_dialogs.py +11 -3
  13. {planformcreator2-4.3.0 → planformcreator2-4.3.2}/planformcreator2/ui/pc2_panels.py +17 -2
  14. {planformcreator2-4.3.0 → planformcreator2-4.3.2}/pyproject.toml +1 -1
  15. {planformcreator2-4.3.0 → planformcreator2-4.3.2}/.gitignore +0 -0
  16. {planformcreator2-4.3.0 → planformcreator2-4.3.2}/LICENSE +0 -0
  17. {planformcreator2-4.3.0 → planformcreator2-4.3.2}/icons/PC2.ico +0 -0
  18. {planformcreator2-4.3.0 → planformcreator2-4.3.2}/icons/PC2_ico.pdn +0 -0
  19. {planformcreator2-4.3.0 → planformcreator2-4.3.2}/planformcreator2/__init__.py +0 -0
  20. {planformcreator2-4.3.0 → planformcreator2-4.3.2}/planformcreator2/__main__.py +0 -0
  21. {planformcreator2-4.3.0 → planformcreator2-4.3.2}/planformcreator2/model/VLM.py +0 -0
  22. {planformcreator2-4.3.0 → planformcreator2-4.3.2}/planformcreator2/model/__init__.py +0 -0
  23. {planformcreator2-4.3.0 → planformcreator2-4.3.2}/planformcreator2/model/wing_exports.py +0 -0
  24. {planformcreator2-4.3.0 → planformcreator2-4.3.2}/planformcreator2/resources.py +0 -0
  25. {planformcreator2-4.3.0 → planformcreator2-4.3.2}/planformcreator2/ui/__init__.py +0 -0
  26. {planformcreator2-4.3.0 → planformcreator2-4.3.2}/templates/ASW_17.png +0 -0
  27. {planformcreator2-4.3.0 → planformcreator2-4.3.2}/templates/Amokka.pc2 +0 -0
  28. {planformcreator2-4.3.0 → planformcreator2-4.3.2}/templates/Bow.pc2 +0 -0
  29. {planformcreator2-4.3.0 → planformcreator2-4.3.2}/templates/Elliptical.pc2 +0 -0
  30. {planformcreator2-4.3.0 → planformcreator2-4.3.2}/templates/F3B_F3F.pc2 +0 -0
  31. {planformcreator2-4.3.0 → planformcreator2-4.3.2}/templates/Scale_Glider.pc2 +0 -0
  32. {planformcreator2-4.3.0 → planformcreator2-4.3.2}/templates/Spitfire.png +0 -0
  33. {planformcreator2-4.3.0 → planformcreator2-4.3.2}/templates/Trapezoid.pc2 +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: planformcreator2
3
- Version: 4.3.0
3
+ Version: 4.3.2
4
4
  Summary: Design a wing planform
5
5
  Project-URL: Homepage, https://github.com/jxjo/PlanformCreator2
6
6
  Project-URL: Issues, https://github.com/jxjo/PlanformCreator2/issues
@@ -12,12 +12,12 @@ Classifier: Operating System :: OS Independent
12
12
  Classifier: Programming Language :: Python :: 3.12
13
13
  Classifier: Programming Language :: Python :: 3.13
14
14
  Requires-Python: >=3.12
15
- Requires-Dist: airfoileditor~=4.3.0
15
+ Requires-Dist: airfoileditor~=4.3.2
16
16
  Description-Content-Type: text/markdown
17
17
 
18
18
  ![PC2](https://github.com/jxjo/PlanformCreator2/blob/main/images/PC2_logo.png?raw=true)
19
19
 
20
- # v4.3.0
20
+ # v4.3.2
21
21
 
22
22
  **PlanformCreator2** (PC2) is an app for designing wing planforms, with a focus on model planes.
23
23
 
@@ -2,7 +2,7 @@
2
2
  ![PC2](images/PC2_logo.png "Logo")
3
3
 
4
4
 
5
- # v4.3.0
5
+ # v4.3.2
6
6
 
7
7
  **PlanformCreator2** (PC2) is an app for designing wing planforms, with a focus on model planes.
8
8
 
@@ -1,6 +1,6 @@
1
1
  ![PC2](https://github.com/jxjo/PlanformCreator2/blob/main/images/PC2_logo.png?raw=true)
2
2
 
3
- # v4.3.0
3
+ # v4.3.2
4
4
 
5
5
  **PlanformCreator2** (PC2) is an app for designing wing planforms, with a focus on model planes.
6
6
 
Binary file
@@ -66,7 +66,7 @@ logger = logging.getLogger(__name__)
66
66
  #-------------------------------------------------------------------------------
67
67
 
68
68
  APP_NAME = "PlanformCreator2"
69
- __version__ = "4.3.0" # hatch "version dynamic" reads this version for build
69
+ __version__ = "4.3.2" # hatch "version dynamic" reads this version for build
70
70
 
71
71
 
72
72
  class Main (QMainWindow):
@@ -220,6 +220,11 @@ class Main (QMainWindow):
220
220
  def closeEvent (self, event : QCloseEvent):
221
221
  """ main window is closed """
222
222
 
223
+ current_mode = self._modes_manager.current_mode if self._modes_manager else None
224
+ if current_mode is not None and not current_mode.confirm_close():
225
+ event.ignore()
226
+ return
227
+
223
228
  # terminate polar watchdog thread, clean up working dir
224
229
  self._app_model.close()
225
230
 
@@ -70,9 +70,7 @@ class App_Model (QObject):
70
70
  sig_new_polars = pyqtSignal() # new polars generated
71
71
  sig_paneling_changed = pyqtSignal() # paneling definition changed
72
72
 
73
- sig_vlm_polar_reset = pyqtSignal() # VLM reset - new polar will be generated on demand
74
- sig_vlm_polar_changed = pyqtSignal() # current VLM polar changed
75
- sig_vlm_opPoint_changed = pyqtSignal() # current VLM operating point changed
73
+ sig_vlm_changed = pyqtSignal() # current VLM polar/op-point changed
76
74
 
77
75
 
78
76
  def __init__(self, workingDir_default: str = None):
@@ -113,6 +111,23 @@ class App_Model (QObject):
113
111
  return f"<App_Model {self.wing}>"
114
112
 
115
113
 
114
+ def _set_cur_polar_def_default(self, silent: bool = False):
115
+ """Initialize or repair the current polar definition explicitly."""
116
+
117
+ new_polar_def = self.cur_polar_def
118
+ if new_polar_def == self._cur_polar_def:
119
+ return
120
+
121
+ self._cur_polar_def = new_polar_def
122
+ logger.debug (f"{self} set cur_polar_def to {new_polar_def}")
123
+
124
+ if self.vlm_alpha_fixed_to_max:
125
+ self._cur_vlm_alpha = None
126
+
127
+ if not silent:
128
+ self.sig_vlm_changed.emit()
129
+
130
+
116
131
  def _init_watchdog (self):
117
132
  """ initialize watchdog thread to check for new polars and xo2 state changes """
118
133
 
@@ -272,35 +287,29 @@ class App_Model (QObject):
272
287
  def cur_polar_def (self) -> Polar_Definition:
273
288
  """ selected T1 polar definition of wingSection at root"""
274
289
 
275
- new_polar_def = self._cur_polar_def
290
+ if self._cur_polar_def in self.polar_definitions_T1:
291
+ return self._cur_polar_def
276
292
 
277
- # ensure current polar def is in existing polar definitions
278
- if self._cur_polar_def not in self.polar_definitions_T1:
279
- new_polar_def = None
293
+ if self.polar_definitions_T1:
294
+ return self.polar_definitions_T1[0]
280
295
 
281
- if self._cur_polar_def is None and self.polar_definitions_T1:
282
- new_polar_def = self.polar_definitions_T1[0]
283
-
284
- if new_polar_def != self._cur_polar_def:
285
- self._cur_polar_def = new_polar_def
286
- logger.debug (f"{self} set cur_polar_def to {new_polar_def}")
287
-
288
- # reset current VLM opPoint
289
- self._cur_vlm_alpha = None if self.vlm_alpha_fixed_to_max else 0.0
290
- self.sig_vlm_opPoint_changed.emit()
291
-
292
- return self._cur_polar_def
296
+ return None
293
297
 
294
298
 
295
299
  def set_cur_polar_def (self, polar_def : Polar_Definition):
296
300
  """ set selected polar definition of wingSection at root"""
297
301
 
298
- if polar_def != self._cur_polar_def and polar_def in self.wing.polar_definitions:
299
- self._cur_polar_def = polar_def
300
- logger.debug (f"{self} set cur_polar_def to {polar_def}")
302
+ if polar_def in self.wing.polar_definitions:
303
+ if polar_def != self._cur_polar_def:
304
+ self._cur_polar_def = polar_def
305
+ logger.debug (f"{self} set cur_polar_def to {polar_def}")
301
306
 
302
- # reset current VLM opPoint
303
- self.notify_polar_definitions_changed ()
307
+ if self.vlm_alpha_fixed_to_max:
308
+ self._cur_vlm_alpha = None
309
+
310
+ self.sig_vlm_changed.emit()
311
+ else:
312
+ self.sig_vlm_changed.emit()
304
313
 
305
314
 
306
315
  @property
@@ -311,16 +320,15 @@ class App_Model (QObject):
311
320
 
312
321
  @property
313
322
  def cur_vlm_polar (self) -> VLM_Polar:
314
- """ current VLM polar based on cur polar_def at root chord """
323
+ """ current VLM polar based on the selected root polar definition """
315
324
 
316
325
  if self.wing.vlm_wing and self.cur_polar_def:
317
- v = self.cur_polar_def.calc_v_for_chord(self.wing.planform.chord_root)
318
- new_polar = self.wing.vlm_wing.polar_at (v)
326
+ new_polar = self.wing.vlm_wing.polar_at (self.cur_polar_def)
319
327
  # do change detection for signal
320
328
  if new_polar != self._cur_vlm_polar:
321
329
  self._cur_vlm_polar = new_polar
322
330
  logger.debug (f"{self} set cur_vlm_polar to {new_polar}")
323
- self.sig_vlm_polar_changed.emit()
331
+ self.sig_vlm_changed.emit()
324
332
  return new_polar
325
333
  else:
326
334
  return None
@@ -338,7 +346,7 @@ class App_Model (QObject):
338
346
  aVal = clip (aVal, -20, 20)
339
347
  if self._cur_vlm_alpha != aVal:
340
348
  self._cur_vlm_alpha = aVal
341
- self.sig_vlm_opPoint_changed.emit()
349
+ self.sig_vlm_changed.emit()
342
350
 
343
351
  @property
344
352
  def vlm_alpha_fixed_to_max (self) -> bool:
@@ -352,7 +360,7 @@ class App_Model (QObject):
352
360
  if aBool: # try to set to max now
353
361
  self._cur_vlm_alpha = self._get_vlm_alpha_max ()
354
362
 
355
- self.sig_vlm_opPoint_changed.emit()
363
+ self.sig_vlm_changed.emit()
356
364
 
357
365
  @property
358
366
  def cur_vlm_opPoint (self) -> VLM_OpPoint:
@@ -380,6 +388,7 @@ class App_Model (QObject):
380
388
  self._cur_wingSection = None
381
389
  self._cur_vlm_alpha = None
382
390
  self._cur_polar_def = None
391
+ self._set_cur_polar_def_default(silent=True)
383
392
 
384
393
  self.sig_new_wing.emit()
385
394
 
@@ -404,13 +413,12 @@ class App_Model (QObject):
404
413
  """ notify self that polar definitions have changed """
405
414
 
406
415
  # as polar definitions could have changed, ensure a new initialized polarSet
407
- self.wing.planform.wingSections.refresh_polar_sets (ensure=True)
416
+ self.wing.planform.wingSections.refresh_polar_sets (reset=True)
417
+ self._set_cur_polar_def_default(silent=True)
408
418
  self.sig_polar_set_changed.emit()
409
419
 
410
- # reset alpha - may angle won't be available anymore
411
- self._cur_vlm_alpha = None if self.vlm_alpha_fixed_to_max else 0.0
412
-
413
- QTimer.singleShot(0, self.sig_vlm_polar_reset.emit) # notify after current events processed
420
+
421
+ QTimer.singleShot(0, self.sig_vlm_changed.emit) # notify after current events processed
414
422
 
415
423
 
416
424
  def notify_planform_changed (self):
@@ -446,7 +454,7 @@ class App_Model (QObject):
446
454
  if self.vlm_alpha_fixed_to_max:
447
455
  self._cur_vlm_alpha = None
448
456
 
449
- QTimer.singleShot(0, self.sig_vlm_polar_reset.emit) # notify after current events processed
457
+ QTimer.singleShot(0, self.sig_vlm_changed.emit) # notify after current events processed
450
458
 
451
459
 
452
460
  def notify_fileName_changed (self):
@@ -163,6 +163,12 @@ class Mode_Abstract (QObject):
163
163
  logger.debug(f"Exiting {self.__class__.__name__}")
164
164
 
165
165
 
166
+ def confirm_close(self) -> bool:
167
+ """ Default close confirmation for modes that do not need special handling. """
168
+
169
+ return True
170
+
171
+
166
172
  def prepare_check_enter(self, on_arg=None) -> object:
167
173
  """ Check if the mode can be entered. Prepare and Return initial object. """
168
174
  # to be overridden in subclasses if needed.
@@ -252,10 +258,11 @@ class Mode_Modify (Mode_Abstract):
252
258
  super().on_leave()
253
259
 
254
260
 
255
- def _on_leaving_planform (self) -> bool:
261
+ @override
262
+ def confirm_close (self) -> bool:
256
263
  """
257
- Do checks and handle if user wants to leave current planform.
258
- Returns True if leaving ok, False if cancelled
264
+ Do checks and handle if user wants to close or leave the current planform.
265
+ Returns True if leaving ok, False if cancelled.
259
266
  """
260
267
 
261
268
  leave = True
@@ -280,7 +287,7 @@ class Mode_Modify (Mode_Abstract):
280
287
  leave = False
281
288
 
282
289
  if leave:
283
- self.app_model.cleanup_wing (all = clean_up_all)
290
+ self.app_model.cleanup_wing (all = clean_up_all or self.wing.is_new_wing)
284
291
 
285
292
  return leave
286
293
 
@@ -288,7 +295,7 @@ class Mode_Modify (Mode_Abstract):
288
295
  def open (self):
289
296
  """ open a new wing definition and load it"""
290
297
 
291
- if not self._on_leaving_planform (): # changes made? user cancelled?
298
+ if not self.confirm_close (): # changes made? user cancelled?
292
299
  return
293
300
 
294
301
  filters = "PlanformCreator2 files (*.pc2)"
@@ -341,7 +348,7 @@ class Mode_Modify (Mode_Abstract):
341
348
  def new (self):
342
349
  """ reset - and start with example definition"""
343
350
 
344
- if not self._on_leaving_planform (): # changes made? user cancelled?
351
+ if not self.confirm_close (): # changes made? user cancelled?
345
352
  return
346
353
 
347
354
  # select a new template
@@ -399,14 +406,7 @@ class Mode_Modify (Mode_Abstract):
399
406
  def exit (self):
400
407
  """ User action: leave current mode and app. """
401
408
 
402
- leave = self._on_leaving_planform () # changes made? user cancelled?
403
-
404
- if leave:
405
- # still new wing? - remove temp dir of airfoil strak etc
406
- if self.wing.is_new_wing:
407
- self.app_model.cleanup_wing (all = True)
408
-
409
- self.sig_exit_requested.emit ()
409
+ self.sig_exit_requested.emit ()
410
410
 
411
411
 
412
412
  @property
@@ -631,7 +631,6 @@ class Modes_Manager (QObject):
631
631
 
632
632
  def exit(self):
633
633
  """ exit mode and close app if in view mode """
634
-
635
634
  if self.current_mode is not None:
636
635
  self.current_mode.on_leave()
637
636
 
@@ -19,18 +19,23 @@ Wing build with VLM_Panels based on Planform_Paneled
19
19
  |-- VLM_OpPoint - angle of attack
20
20
  !-- aerogrid - input data structure of PanelAero
21
21
  """
22
-
22
+ from __future__ import annotations # for forward type references in annotations (wingSection, Planform_Paneled)
23
+
23
24
  import numpy as np
25
+ from copy import copy
24
26
  from enum import StrEnum
25
27
  from typing import NamedTuple, TYPE_CHECKING
26
28
  from math import isclose, degrees, radians
27
29
 
28
30
  from .VLM import calc_Qjj
29
- from airfoileditor.model.polar_set import Polar_Set, RE_SCALE_ROUND_TO, re_from_v, Polar
31
+ from airfoileditor.model.polar_set import Polar_Set, RE_SCALE_ROUND_TO, polarType, Polar, Polar_Definition
30
32
 
31
33
  if TYPE_CHECKING:
32
34
  from .wing import Planform_Paneled, WingSection # avoid circular import
33
35
 
36
+
37
+ VLM_Polar_CacheEntry = tuple[Polar_Definition, 'VLM_Polar']
38
+
34
39
  import logging
35
40
  logger = logging.getLogger(__name__)
36
41
  # logger.setLevel(logging.DEBUG)
@@ -227,7 +232,7 @@ class VLM_Wing:
227
232
  self._b_stripes = None # width of stripes
228
233
 
229
234
  self._y_sections = None # y position of wing sections in m
230
- self._polars = {} # dict of polars
235
+ self._polars: list[VLM_Polar_CacheEntry] = [] # list of (root polar def, VLM polar)
231
236
 
232
237
 
233
238
  def __repr__(self) -> str:
@@ -235,7 +240,7 @@ class VLM_Wing:
235
240
  return f"<{type(self).__name__}>"
236
241
 
237
242
  @property
238
- def wingSections(self) -> list :
243
+ def wingSections(self) -> list [WingSection]:
239
244
  """ wing sections of self._planform_paneled"""
240
245
 
241
246
  planform_paneled : Planform_Paneled = self._planform_paneled
@@ -350,34 +355,46 @@ class VLM_Wing:
350
355
 
351
356
 
352
357
 
353
- def polar_at (self, vtas: float) -> 'VLM_Polar':
354
- """ returns polar for air speed vtas"""
358
+ def _get_cached_polar (self, root_polar_def: Polar_Definition) -> 'VLM_Polar | None':
359
+ """ return cached VLM polar for a matching root polar definition """
355
360
 
356
- if not isinstance (vtas, (int, float)) or vtas <= 0.0:
357
- return None
361
+ for cached_root_polar_def, polar in self._polars:
362
+ if cached_root_polar_def.is_equal_to (root_polar_def):
363
+ return polar
364
+ return None
358
365
 
359
- v = round (vtas, 1) # ensure clean key for dict
360
366
 
361
- polar = self._polars.get (v, None) # already exisiting
367
+ def polar_at (self, root_polar_def: Polar_Definition) -> 'VLM_Polar':
368
+ """ returns polar for a root airfoil polar definition """
369
+
370
+ if not isinstance (root_polar_def, Polar_Definition):
371
+ return None
372
+
373
+ polar = self._get_cached_polar (root_polar_def) # already exisiting
362
374
 
363
375
  if not polar:
364
- polar = VLM_Polar (self, v) # calculate new Polar and opPoints
365
- self._polars[v] = polar
376
+ polar_def_copy = copy (root_polar_def) # polar_def in cache must be inmutable
377
+ polar = VLM_Polar (self, polar_def_copy) # calculate new Polar and opPoints
378
+ self._polars.append ((polar_def_copy, polar))
366
379
 
367
380
  return polar
368
381
 
382
+
369
383
  def has_polars (self) -> bool:
370
384
  """ True if at least one polar is existing"""
371
385
  return bool (self._polars)
372
386
 
373
387
 
374
- def remove_polar_at (self, vtas: float):
375
- """ removes polar for air speed vtas - will be calculated new on next request"""
376
- if not isinstance (vtas, (int, float)) or vtas <= 0.0:
388
+ def remove_polar_at (self, root_polar_def: Polar_Definition):
389
+ """ removes polar for a root airfoil polar definition - will be calculated new on next request"""
390
+
391
+ if not isinstance (root_polar_def, Polar_Definition):
377
392
  return
378
393
 
379
- v = round (vtas, 1) # ensure clean key for dict
380
- self._polars.pop(v, None) # remove existing, None if not found
394
+ for i, (cached_root_polar_def, _) in enumerate (self._polars):
395
+ if cached_root_polar_def.is_equal_to (root_polar_def):
396
+ self._polars.pop (i)
397
+ break
381
398
 
382
399
 
383
400
 
@@ -544,13 +561,19 @@ class VLM_Polar:
544
561
  VLM solution for certain velocity
545
562
  """
546
563
 
547
- def __init__ (self, wing: VLM_Wing, vtas: float ):
564
+ def __init__ (self, wing: VLM_Wing, root_polar_def: Polar_Definition):
565
+
566
+ self.vlm_wing = wing # my parent wing
567
+
568
+ # sanity - only T1 polars allowed for VLM calculation
569
+ if not isinstance (root_polar_def, Polar_Definition) or not root_polar_def.type == polarType.T1:
570
+ raise ValueError ("root polar definition must be a T1 polar definition")
548
571
 
549
- self.vlm_wing = wing
550
- self.vtas = vtas # true air speed
572
+ self._root_polar_def = root_polar_def # polar definition of root airfoil (wingSection 0)
551
573
 
552
- self._opPoints = {} # dict of operating points
553
- self._error_reason = [] # list of error messages eg polar couldn't be loaded
574
+ self._vtas = None # true air speed (derived from root polar def)
575
+ self._opPoints = {} # dict of operating points
576
+ self._error_reason = [] # list of error messages eg polar couldn't be loaded
554
577
  self._generating_airfoil_polars = False # airfoil polars are currently generated
555
578
  self._use_viscous_loop = True # in opPoint calculation
556
579
 
@@ -572,6 +595,20 @@ class VLM_Polar:
572
595
  # overwrite to get a nice print string
573
596
  return f"<{type(self).__name__} {self.vtas:.1f}m/s >"
574
597
 
598
+
599
+ @property
600
+ def vtas (self) -> float:
601
+ """ true air speed of self"""
602
+
603
+ if isinstance (self._root_polar_def, Polar_Definition) and self._vtas is None:
604
+
605
+ # calc v from airfoil polar and chord of root section
606
+ root_chord = self.vlm_wing.wingSections[0].c
607
+ self._vtas = self._root_polar_def.calc_v_for_chord(root_chord)
608
+
609
+ return self._vtas
610
+
611
+
575
612
  @property
576
613
  def name (self) -> str:
577
614
  """ name of polar like T1-22.3-VLM"""
@@ -774,6 +811,25 @@ class VLM_Polar:
774
811
  # ---- private ----
775
812
 
776
813
 
814
+ def _get_matching_polar (self, airfoil_polarSet: Polar_Set, section_re: float) -> Polar | None:
815
+ """ return the VLM polar that best matches root polar definition and section Re """
816
+
817
+ polar_def_target = copy (self._root_polar_def)
818
+ polar_def_target.set_re (section_re)
819
+
820
+ matching_polar = None
821
+ best_re_delta = float("inf")
822
+
823
+ for polar in airfoil_polarSet.polars_VLM:
824
+ if polar.is_equal_to (polar_def_target, ignore_active=True, ignore_xtrip=True, re_abs_tolerance=RE_SCALE_ROUND_TO):
825
+ re_delta = abs (polar.re - section_re)
826
+ if re_delta < best_re_delta:
827
+ matching_polar = polar
828
+ best_re_delta = re_delta
829
+
830
+ return matching_polar
831
+
832
+
777
833
  def _get_airfoil_polars (self) -> list [Polar] | None:
778
834
  """ loads for all wingSections polar of airfoil"""
779
835
 
@@ -782,7 +838,7 @@ class VLM_Polar:
782
838
 
783
839
  # get airfoil polars for all wingSections
784
840
  airfoil_polars = []
785
- section : WingSection
841
+
786
842
  for section in self.vlm_wing.wingSections:
787
843
 
788
844
  if not section.airfoil.isLoaded:
@@ -806,26 +862,21 @@ class VLM_Polar:
806
862
 
807
863
  # find polar with matching Re of this wing section
808
864
 
809
- section_re = re_from_v (self.vtas, section.c / 1000, round_to=RE_SCALE_ROUND_TO)
810
-
811
- for polar in airfoil_polarSet.polars_VLM:
812
-
813
- if isclose (polar.re, section_re, abs_tol=RE_SCALE_ROUND_TO):
814
- if polar.isLoaded:
815
-
816
- # there is a polar that fits to Re of wingSection
817
- airfoil_polars.append(polar)
865
+ section_re = self._root_polar_def.re * section.cn
866
+ matching_polar = self._get_matching_polar (airfoil_polarSet, section_re)
818
867
 
868
+ if matching_polar is not None:
869
+ if matching_polar.isLoaded:
870
+ # there is a polar that fits to Re of wingSection
871
+ airfoil_polars.append(matching_polar)
872
+ else:
873
+ if matching_polar.error_occurred:
874
+ self._error_reason.append (matching_polar.error_reason)
819
875
  else:
820
- if polar.error_occurred:
821
- self._error_reason.append (polar.error_reason)
822
- else:
823
- self._generating_airfoil_polars = True
824
- break
825
-
826
- if not isclose (polar.re, section_re, abs_tol=RE_SCALE_ROUND_TO):
827
- polars_re_list = [f"{p.re:.0f}" for p in airfoil_polarSet.polars_VLM]
828
- msg = f"No polar with Re = {section_re:.0f} in Polarset of {section} with polars: {polars_re_list}"
876
+ self._generating_airfoil_polars = True
877
+ else:
878
+ msg = (f"No VLM polar matching root definition within ±{RE_SCALE_ROUND_TO:.0f} Re "
879
+ f"for section {section.id} (target Re={section_re:.0f}) " )
829
880
  logger.error (msg)
830
881
  self._error_reason.append (msg)
831
882