symcad.parts.composite.CrossFormAirfoils

  1#!/usr/bin/env python3
  2# Copyright (C) 2022, Will Hedgecock
  3#
  4# This program is free software: you can redistribute it and/or modify
  5# it under the terms of the GNU General Public License as published by
  6# the Free Software Foundation, either version 3 of the License, or
  7# (at your option) any later version.
  8#
  9# This program is distributed in the hope that it will be useful,
 10# but WITHOUT ANY WARRANTY; without even the implied warranty of
 11# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 12# GNU General Public License for more details.
 13#
 14# You should have received a copy of the GNU General Public License
 15# along with this program.  If not, see <http://www.gnu.org/licenses/>.
 16
 17from __future__ import annotations
 18from PyFreeCAD.FreeCAD import FreeCAD, Part
 19from typing import Dict, Optional, Tuple, Union
 20from sympy import Expr, Symbol, sqrt, sin, cos
 21from . import CompositeShape
 22import math
 23
 24class CrossFormAirfoils(CompositeShape):
 25   """Model representing a set of cross-form parameteric airfoils.
 26
 27   By default, the airfoils are oriented in the following configuration:
 28
 29   ![CrossFormAirfoils](https://symbench.github.io/SymCAD/images/CrossFormAirfoils.png)
 30
 31   The `geometry` of this shape includes the following parameters:
 32
 33   - `max_thickness`: Maximum thickness (in `% of length`) of each airfoil
 34   - `chord_length`: Length (in `m`) of the chord of each airfoil
 35   - `span`: Width (in `m`) of each airfoil
 36   - `separation_radius`: Radius (in `m`) of separation between each airfoil
 37   - `curvature_tilt`: Amount of tilt (in `deg`) of each airfoil
 38
 39   Note that the above dimensions should be interpreted as if the airfoils are unrotated. In other
 40   words, any shape rotation takes place *after* the airfoil dimensions have been specified.
 41   """
 42
 43   # Constructor ----------------------------------------------------------------------------------
 44
 45   def __init__(self, identifier: str, material_density_kg_m3: Optional[float] = 1.0) -> None:
 46      """Initializes a cross-form parametric airfoil object.
 47
 48      Parameters
 49      ----------
 50      identifier : `str`
 51         Unique identifying name for the object.
 52      material_density_kg_m3 : `float`, optional, default=1.0
 53         Uniform material density in `kg/m^3` to be used in mass property calculations.
 54      """
 55      super().__init__(identifier, self.__create_cad__, None, material_density_kg_m3)
 56      setattr(self.geometry, 'max_thickness', Symbol(self.name + '_max_thickness'))
 57      setattr(self.geometry, 'chord_length', Symbol(self.name + '_chord_length'))
 58      setattr(self.geometry, 'span', Symbol(self.name + '_span'))
 59      setattr(self.geometry, 'separation_radius', Symbol(self.name + '_separation_radius'))
 60      setattr(self.geometry, 'curvature_tilt', Symbol(self.name + '_curvature_tilt'))
 61
 62
 63   # CAD generation function ----------------------------------------------------------------------
 64
 65   @staticmethod
 66   def z_val(x: float, max_thickness: float, chord_length: float) -> float:
 67      return 5.0 * max_thickness * chord_length * (
 68         (0.2969 * x**0.5) - (0.1260 * x) - (0.3516 * x**2) + (0.2843 * x**3) - (0.1036 * x**4))
 69
 70   @staticmethod
 71   def __create_cad__(params: Dict[str, float], _fully_displace: bool) -> Part.Solid:
 72      """Scripted CAD generation method for `CrossFormAirfoils`."""
 73      doc = FreeCAD.newDocument('Temp')
 74      max_thickness_percent = params['max_thickness']
 75      chord_length_mm = 1000.0 * params['chord_length']
 76      span_mm = 1000.0 * params['span']
 77      separation_radius_mm = 1000.0 * params['separation_radius']
 78      curvature_tilt = params['curvature_tilt']
 79      x, points_upper, points_lower = 0.0, [], []
 80      while x <= 1.009:
 81         points_upper.append(FreeCAD.Vector(x * chord_length_mm,
 82                                            CrossFormAirfoils.z_val(x, max_thickness_percent,
 83                                                                       chord_length_mm)))
 84         points_lower.append(FreeCAD.Vector(x * chord_length_mm,
 85                                            -CrossFormAirfoils.z_val(x, max_thickness_percent,
 86                                                                        chord_length_mm)))
 87         x += 0.01
 88      bodies = []
 89      for i in range(4):
 90         body = doc.addObject('PartDesign::Body','Airfoil' + str(i))
 91         sketch = doc.addObject('Sketcher::SketchObject', 'Sketch' + str(i))
 92         sketch.Support = doc.XZ_Plane
 93         sketch.MapMode = 'FlatFace'
 94         body.addObject(sketch)
 95         sketch.addGeometry(Part.BSplineCurve(points_upper, None, None,
 96                                              False, 3, None, False), False)
 97         sketch.addGeometry(Part.BSplineCurve(points_lower, None, None,
 98                                              False, 3, None, False), False)
 99         pad = doc.addObject('PartDesign::Pad', 'Pad' + str(i))
100         body.addObject(pad)
101         pad.Length = int(span_mm)
102         pad.Profile = sketch
103         placement_vector = FreeCAD.Vector(0, -separation_radius_mm if i == 0 else
104                                              (separation_radius_mm if i == 2 else 0),
105                                              -separation_radius_mm if i == 1 else
106                                              (separation_radius_mm if i == 3 else 0))
107         body.Placement = FreeCAD.Placement(placement_vector,
108                                            FreeCAD.Rotation(-curvature_tilt if i == 2 else
109                                                             (curvature_tilt if i == 0 else 0),
110                                                             -curvature_tilt if i == 1 else
111                                                             (curvature_tilt if i == 3 else 0),
112                                                             i * 90.0))
113         bodies.append(body)
114      fusion = doc.addObject('Part::MultiFuse', 'Fusion')
115      fusion.Shapes = bodies
116      doc.recompute()
117      airfoils = fusion.Shape
118      FreeCAD.closeDocument(doc.Name)
119      return airfoils
120
121
122   # Geometry setter ------------------------------------------------------------------------------
123
124   def set_geometry(self, *, max_thickness_percent: Union[float, None],
125                             chord_length_m: Union[float, None],
126                             span_m: Union[float, None],
127                             separation_radius_m: Union[float, None],
128                             curvature_tilt_deg: Union[float, None]) -> CrossFormAirfoils:
129      """Sets the physical geometry of the current `CrossFormAirfoils` object.
130
131      See the `CrossFormAirfoils` class documentation for a description of each geometric
132      parameter.
133      """
134      self.geometry.set(max_thickness=max_thickness_percent,
135                        chord_length=chord_length_m,
136                        span=span_m,
137                        separation_radius=separation_radius_m,
138                        curvature_tilt=curvature_tilt_deg)
139      return self
140
141   def get_geometric_parameter_bounds(self, parameter: str) -> Tuple[float, float]:
142      parameter_bounds = {
143         'max_thickness': (0.01, 0.90),
144         'chord_length': (0.1, 2.0),
145         'span': (0.0, 2.0),
146         'separation_radius': (0.0, 1.5),
147         'curvature_tilt': (0.0, 45.0)
148      }
149      return parameter_bounds.get(parameter, (0.0, 0.0))
150
151
152   # Geometric properties -------------------------------------------------------------------------
153
154   @property
155   def material_volume(self) -> Union[float, Expr]:
156      return self.displaced_volume
157
158   @property
159   def displaced_volume(self) -> Union[float, Expr]:
160      x, points_upper = 0.0, []
161      while x <= 1.009:
162         points_upper.append((x * self.geometry.chord_length,
163                              CrossFormAirfoils.z_val(x, self.geometry.max_thickness,
164                                                         self.geometry.chord_length)))
165         x += 0.01
166      area = 0.0
167      for i in range(1, len(points_upper) - 1):
168         area += points_upper[i][1]
169      area = points_upper[0][1] + (2.0 * area) + points_upper[len(points_upper)-1][1]
170      h = (points_upper[len(points_upper)-1][0] - points_upper[0][0]) / (len(points_upper) - 1)
171      return 4.0 * h * area * self.geometry.span
172
173   @property
174   def surface_area(self) -> Union[float, Expr]:
175      x, points_upper = 0.0, []
176      while x <= 1.009:
177         points_upper.append((x * self.geometry.chord_length,
178                              CrossFormAirfoils.z_val(x, self.geometry.max_thickness,
179                                                         self.geometry.chord_length)))
180         x += 0.01
181      area = 0.0
182      for i in range(1, len(points_upper)):
183         x_diff = (points_upper[i][0] - points_upper[i-1][0])
184         y_diff = (points_upper[i][1] - points_upper[i-1][1])
185         area += sqrt(x_diff**2 + y_diff**2)
186      area = (2.0 * area) * self.geometry.span
187      return 4.0 * area
188
189   @property
190   def unoriented_center_of_gravity(self) -> Tuple[Union[float, Expr],
191                                                   Union[float, Expr],
192                                                   Union[float, Expr]]:
193      cosine = cos(self.geometry.curvature_tilt * math.pi / 180.0)
194      sine = sin(self.geometry.curvature_tilt * math.pi / 180.0)
195      return ((0.41 * cosine * self.geometry.chord_length) + (0.5 * sine * self.geometry.span),
196              0.5 * self.unoriented_width,
197              0.5 * self.unoriented_height)
198
199   @property
200   def unoriented_center_of_buoyancy(self) -> Tuple[Union[float, Expr],
201                                                    Union[float, Expr],
202                                                    Union[float, Expr]]:
203      return self.unoriented_center_of_gravity
204
205   @property
206   def unoriented_length(self) -> Union[float, Expr]:
207      cosine = cos(self.geometry.curvature_tilt * math.pi / 180.0)
208      sine = sin(self.geometry.curvature_tilt * math.pi / 180.0)
209      return (cosine * self.geometry.chord_length) + (sine * self.geometry.span)
210
211   @property
212   def unoriented_width(self) -> Union[float, Expr]:
213      cosine = cos(self.geometry.curvature_tilt * math.pi / 180.0)
214      return (2.0 * cosine * self.geometry.span) + (2.0 * self.geometry.separation_radius)
215
216   @property
217   def unoriented_height(self) -> Union[float, Expr]:
218      return self.unoriented_width
219
220   @property
221   def oriented_length(self) -> Union[float, Expr]:
222      # TODO: Implement this
223      return 0
224
225   @property
226   def oriented_width(self) -> Union[float, Expr]:
227      # TODO: Implement this
228      return 0
229
230   @property
231   def oriented_height(self) -> Union[float, Expr]:
232      # TODO: Implement this
233      return 0
class CrossFormAirfoils(symcad.parts.composite.CompositeShape):
 25class CrossFormAirfoils(CompositeShape):
 26   """Model representing a set of cross-form parameteric airfoils.
 27
 28   By default, the airfoils are oriented in the following configuration:
 29
 30   ![CrossFormAirfoils](https://symbench.github.io/SymCAD/images/CrossFormAirfoils.png)
 31
 32   The `geometry` of this shape includes the following parameters:
 33
 34   - `max_thickness`: Maximum thickness (in `% of length`) of each airfoil
 35   - `chord_length`: Length (in `m`) of the chord of each airfoil
 36   - `span`: Width (in `m`) of each airfoil
 37   - `separation_radius`: Radius (in `m`) of separation between each airfoil
 38   - `curvature_tilt`: Amount of tilt (in `deg`) of each airfoil
 39
 40   Note that the above dimensions should be interpreted as if the airfoils are unrotated. In other
 41   words, any shape rotation takes place *after* the airfoil dimensions have been specified.
 42   """
 43
 44   # Constructor ----------------------------------------------------------------------------------
 45
 46   def __init__(self, identifier: str, material_density_kg_m3: Optional[float] = 1.0) -> None:
 47      """Initializes a cross-form parametric airfoil object.
 48
 49      Parameters
 50      ----------
 51      identifier : `str`
 52         Unique identifying name for the object.
 53      material_density_kg_m3 : `float`, optional, default=1.0
 54         Uniform material density in `kg/m^3` to be used in mass property calculations.
 55      """
 56      super().__init__(identifier, self.__create_cad__, None, material_density_kg_m3)
 57      setattr(self.geometry, 'max_thickness', Symbol(self.name + '_max_thickness'))
 58      setattr(self.geometry, 'chord_length', Symbol(self.name + '_chord_length'))
 59      setattr(self.geometry, 'span', Symbol(self.name + '_span'))
 60      setattr(self.geometry, 'separation_radius', Symbol(self.name + '_separation_radius'))
 61      setattr(self.geometry, 'curvature_tilt', Symbol(self.name + '_curvature_tilt'))
 62
 63
 64   # CAD generation function ----------------------------------------------------------------------
 65
 66   @staticmethod
 67   def z_val(x: float, max_thickness: float, chord_length: float) -> float:
 68      return 5.0 * max_thickness * chord_length * (
 69         (0.2969 * x**0.5) - (0.1260 * x) - (0.3516 * x**2) + (0.2843 * x**3) - (0.1036 * x**4))
 70
 71   @staticmethod
 72   def __create_cad__(params: Dict[str, float], _fully_displace: bool) -> Part.Solid:
 73      """Scripted CAD generation method for `CrossFormAirfoils`."""
 74      doc = FreeCAD.newDocument('Temp')
 75      max_thickness_percent = params['max_thickness']
 76      chord_length_mm = 1000.0 * params['chord_length']
 77      span_mm = 1000.0 * params['span']
 78      separation_radius_mm = 1000.0 * params['separation_radius']
 79      curvature_tilt = params['curvature_tilt']
 80      x, points_upper, points_lower = 0.0, [], []
 81      while x <= 1.009:
 82         points_upper.append(FreeCAD.Vector(x * chord_length_mm,
 83                                            CrossFormAirfoils.z_val(x, max_thickness_percent,
 84                                                                       chord_length_mm)))
 85         points_lower.append(FreeCAD.Vector(x * chord_length_mm,
 86                                            -CrossFormAirfoils.z_val(x, max_thickness_percent,
 87                                                                        chord_length_mm)))
 88         x += 0.01
 89      bodies = []
 90      for i in range(4):
 91         body = doc.addObject('PartDesign::Body','Airfoil' + str(i))
 92         sketch = doc.addObject('Sketcher::SketchObject', 'Sketch' + str(i))
 93         sketch.Support = doc.XZ_Plane
 94         sketch.MapMode = 'FlatFace'
 95         body.addObject(sketch)
 96         sketch.addGeometry(Part.BSplineCurve(points_upper, None, None,
 97                                              False, 3, None, False), False)
 98         sketch.addGeometry(Part.BSplineCurve(points_lower, None, None,
 99                                              False, 3, None, False), False)
100         pad = doc.addObject('PartDesign::Pad', 'Pad' + str(i))
101         body.addObject(pad)
102         pad.Length = int(span_mm)
103         pad.Profile = sketch
104         placement_vector = FreeCAD.Vector(0, -separation_radius_mm if i == 0 else
105                                              (separation_radius_mm if i == 2 else 0),
106                                              -separation_radius_mm if i == 1 else
107                                              (separation_radius_mm if i == 3 else 0))
108         body.Placement = FreeCAD.Placement(placement_vector,
109                                            FreeCAD.Rotation(-curvature_tilt if i == 2 else
110                                                             (curvature_tilt if i == 0 else 0),
111                                                             -curvature_tilt if i == 1 else
112                                                             (curvature_tilt if i == 3 else 0),
113                                                             i * 90.0))
114         bodies.append(body)
115      fusion = doc.addObject('Part::MultiFuse', 'Fusion')
116      fusion.Shapes = bodies
117      doc.recompute()
118      airfoils = fusion.Shape
119      FreeCAD.closeDocument(doc.Name)
120      return airfoils
121
122
123   # Geometry setter ------------------------------------------------------------------------------
124
125   def set_geometry(self, *, max_thickness_percent: Union[float, None],
126                             chord_length_m: Union[float, None],
127                             span_m: Union[float, None],
128                             separation_radius_m: Union[float, None],
129                             curvature_tilt_deg: Union[float, None]) -> CrossFormAirfoils:
130      """Sets the physical geometry of the current `CrossFormAirfoils` object.
131
132      See the `CrossFormAirfoils` class documentation for a description of each geometric
133      parameter.
134      """
135      self.geometry.set(max_thickness=max_thickness_percent,
136                        chord_length=chord_length_m,
137                        span=span_m,
138                        separation_radius=separation_radius_m,
139                        curvature_tilt=curvature_tilt_deg)
140      return self
141
142   def get_geometric_parameter_bounds(self, parameter: str) -> Tuple[float, float]:
143      parameter_bounds = {
144         'max_thickness': (0.01, 0.90),
145         'chord_length': (0.1, 2.0),
146         'span': (0.0, 2.0),
147         'separation_radius': (0.0, 1.5),
148         'curvature_tilt': (0.0, 45.0)
149      }
150      return parameter_bounds.get(parameter, (0.0, 0.0))
151
152
153   # Geometric properties -------------------------------------------------------------------------
154
155   @property
156   def material_volume(self) -> Union[float, Expr]:
157      return self.displaced_volume
158
159   @property
160   def displaced_volume(self) -> Union[float, Expr]:
161      x, points_upper = 0.0, []
162      while x <= 1.009:
163         points_upper.append((x * self.geometry.chord_length,
164                              CrossFormAirfoils.z_val(x, self.geometry.max_thickness,
165                                                         self.geometry.chord_length)))
166         x += 0.01
167      area = 0.0
168      for i in range(1, len(points_upper) - 1):
169         area += points_upper[i][1]
170      area = points_upper[0][1] + (2.0 * area) + points_upper[len(points_upper)-1][1]
171      h = (points_upper[len(points_upper)-1][0] - points_upper[0][0]) / (len(points_upper) - 1)
172      return 4.0 * h * area * self.geometry.span
173
174   @property
175   def surface_area(self) -> Union[float, Expr]:
176      x, points_upper = 0.0, []
177      while x <= 1.009:
178         points_upper.append((x * self.geometry.chord_length,
179                              CrossFormAirfoils.z_val(x, self.geometry.max_thickness,
180                                                         self.geometry.chord_length)))
181         x += 0.01
182      area = 0.0
183      for i in range(1, len(points_upper)):
184         x_diff = (points_upper[i][0] - points_upper[i-1][0])
185         y_diff = (points_upper[i][1] - points_upper[i-1][1])
186         area += sqrt(x_diff**2 + y_diff**2)
187      area = (2.0 * area) * self.geometry.span
188      return 4.0 * area
189
190   @property
191   def unoriented_center_of_gravity(self) -> Tuple[Union[float, Expr],
192                                                   Union[float, Expr],
193                                                   Union[float, Expr]]:
194      cosine = cos(self.geometry.curvature_tilt * math.pi / 180.0)
195      sine = sin(self.geometry.curvature_tilt * math.pi / 180.0)
196      return ((0.41 * cosine * self.geometry.chord_length) + (0.5 * sine * self.geometry.span),
197              0.5 * self.unoriented_width,
198              0.5 * self.unoriented_height)
199
200   @property
201   def unoriented_center_of_buoyancy(self) -> Tuple[Union[float, Expr],
202                                                    Union[float, Expr],
203                                                    Union[float, Expr]]:
204      return self.unoriented_center_of_gravity
205
206   @property
207   def unoriented_length(self) -> Union[float, Expr]:
208      cosine = cos(self.geometry.curvature_tilt * math.pi / 180.0)
209      sine = sin(self.geometry.curvature_tilt * math.pi / 180.0)
210      return (cosine * self.geometry.chord_length) + (sine * self.geometry.span)
211
212   @property
213   def unoriented_width(self) -> Union[float, Expr]:
214      cosine = cos(self.geometry.curvature_tilt * math.pi / 180.0)
215      return (2.0 * cosine * self.geometry.span) + (2.0 * self.geometry.separation_radius)
216
217   @property
218   def unoriented_height(self) -> Union[float, Expr]:
219      return self.unoriented_width
220
221   @property
222   def oriented_length(self) -> Union[float, Expr]:
223      # TODO: Implement this
224      return 0
225
226   @property
227   def oriented_width(self) -> Union[float, Expr]:
228      # TODO: Implement this
229      return 0
230
231   @property
232   def oriented_height(self) -> Union[float, Expr]:
233      # TODO: Implement this
234      return 0

Model representing a set of cross-form parameteric airfoils.

By default, the airfoils are oriented in the following configuration:

CrossFormAirfoils

The geometry of this shape includes the following parameters:

  • max_thickness: Maximum thickness (in % of length) of each airfoil
  • chord_length: Length (in m) of the chord of each airfoil
  • span: Width (in m) of each airfoil
  • separation_radius: Radius (in m) of separation between each airfoil
  • curvature_tilt: Amount of tilt (in deg) of each airfoil

Note that the above dimensions should be interpreted as if the airfoils are unrotated. In other words, any shape rotation takes place after the airfoil dimensions have been specified.

CrossFormAirfoils(identifier: str, material_density_kg_m3: Optional[float] = 1.0)
46   def __init__(self, identifier: str, material_density_kg_m3: Optional[float] = 1.0) -> None:
47      """Initializes a cross-form parametric airfoil object.
48
49      Parameters
50      ----------
51      identifier : `str`
52         Unique identifying name for the object.
53      material_density_kg_m3 : `float`, optional, default=1.0
54         Uniform material density in `kg/m^3` to be used in mass property calculations.
55      """
56      super().__init__(identifier, self.__create_cad__, None, material_density_kg_m3)
57      setattr(self.geometry, 'max_thickness', Symbol(self.name + '_max_thickness'))
58      setattr(self.geometry, 'chord_length', Symbol(self.name + '_chord_length'))
59      setattr(self.geometry, 'span', Symbol(self.name + '_span'))
60      setattr(self.geometry, 'separation_radius', Symbol(self.name + '_separation_radius'))
61      setattr(self.geometry, 'curvature_tilt', Symbol(self.name + '_curvature_tilt'))

Initializes a cross-form parametric airfoil object.

Parameters
  • identifier (str): Unique identifying name for the object.
  • material_density_kg_m3 (float, optional, default=1.0): Uniform material density in kg/m^3 to be used in mass property calculations.
@staticmethod
def z_val(x: float, max_thickness: float, chord_length: float) -> float:
66   @staticmethod
67   def z_val(x: float, max_thickness: float, chord_length: float) -> float:
68      return 5.0 * max_thickness * chord_length * (
69         (0.2969 * x**0.5) - (0.1260 * x) - (0.3516 * x**2) + (0.2843 * x**3) - (0.1036 * x**4))
def set_geometry( self, *, max_thickness_percent: Optional[float], chord_length_m: Optional[float], span_m: Optional[float], separation_radius_m: Optional[float], curvature_tilt_deg: Optional[float]) -> CrossFormAirfoils:
125   def set_geometry(self, *, max_thickness_percent: Union[float, None],
126                             chord_length_m: Union[float, None],
127                             span_m: Union[float, None],
128                             separation_radius_m: Union[float, None],
129                             curvature_tilt_deg: Union[float, None]) -> CrossFormAirfoils:
130      """Sets the physical geometry of the current `CrossFormAirfoils` object.
131
132      See the `CrossFormAirfoils` class documentation for a description of each geometric
133      parameter.
134      """
135      self.geometry.set(max_thickness=max_thickness_percent,
136                        chord_length=chord_length_m,
137                        span=span_m,
138                        separation_radius=separation_radius_m,
139                        curvature_tilt=curvature_tilt_deg)
140      return self

Sets the physical geometry of the current CrossFormAirfoils object.

See the CrossFormAirfoils class documentation for a description of each geometric parameter.

def get_geometric_parameter_bounds(self, parameter: str) -> Tuple[float, float]:
142   def get_geometric_parameter_bounds(self, parameter: str) -> Tuple[float, float]:
143      parameter_bounds = {
144         'max_thickness': (0.01, 0.90),
145         'chord_length': (0.1, 2.0),
146         'span': (0.0, 2.0),
147         'separation_radius': (0.0, 1.5),
148         'curvature_tilt': (0.0, 45.0)
149      }
150      return parameter_bounds.get(parameter, (0.0, 0.0))

Abstract method that must be overridden by a concrete SymPart class to return the minimum and maximum expected bounds for a given geometric parameter.

Parameters
  • parameter (str): Name of the geometric parameter for which to return the minimum and maximum bounds.
Returns
  • Tuple[float, float]: Minimum and maximum bounds for the specified geometric parameter.
material_volume: Union[float, sympy.core.expr.Expr]

Material volume (in m^3) of the SymPart (read-only).

displaced_volume: Union[float, sympy.core.expr.Expr]

Displaced volume (in m^3) of the SymPart (read-only).

surface_area: Union[float, sympy.core.expr.Expr]

Surface/wetted area (in m^2) of the SymPart (read-only).

unoriented_center_of_gravity: Tuple[Union[float, sympy.core.expr.Expr], Union[float, sympy.core.expr.Expr], Union[float, sympy.core.expr.Expr]]

Center of gravity (in m) of the unoriented SymPart (read-only).

unoriented_center_of_buoyancy: Tuple[Union[float, sympy.core.expr.Expr], Union[float, sympy.core.expr.Expr], Union[float, sympy.core.expr.Expr]]

Center of buoyancy (in m) of the unoriented SymPart (read-only).

unoriented_length: Union[float, sympy.core.expr.Expr]

X-axis length (in m) of the bounding box of the unoriented SymPart (read-only).

unoriented_width: Union[float, sympy.core.expr.Expr]

Y-axis width (in m) of the bounding box of the unoriented SymPart (read-only).

unoriented_height: Union[float, sympy.core.expr.Expr]

Z-axis height (in m) of the bounding box of the unoriented SymPart (read-only).

oriented_length: Union[float, sympy.core.expr.Expr]

X-axis length (in m) of the bounding box of the oriented SymPart (read-only).

oriented_width: Union[float, sympy.core.expr.Expr]

Y-axis length (in m) of the bounding box of the oriented SymPart (read-only).

oriented_height: Union[float, sympy.core.expr.Expr]

Z-axis length (in m) of the bounding box of the oriented SymPart (read-only).