symcad.parts.endcaps.Hemisphere

  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
 21from . import EndcapShape
 22import math
 23
 24class Hemisphere(EndcapShape):
 25   """Model representing a hollow, parametric, hemispherical endcap.
 26
 27   By default, the endcap is oriented such that its base is perpendicular to the z-axis:
 28
 29   ![Hemisphere](https://symbench.github.io/SymCAD/images/Hemisphere.png)
 30
 31   The `geometry` of this shape includes the following parameters:
 32
 33   - `radius`: Radius (in `m`) of the Hemisphere
 34   - `thickness`: Thickness (in `m`) of the shell of the Hemisphere
 35
 36   Note that the above dimensions should be interpreted as if the Hemisphere is unrotated.
 37   In other words, any shape rotation takes place *after* the Hemisphere dimensions have been
 38   specified.
 39   """
 40
 41   # Constructor ----------------------------------------------------------------------------------
 42
 43   def __init__(self, identifier: str, material_density_kg_m3: Optional[float] = 1.0) -> None:
 44      """Initializes a hollow, parametric, hemispherical endcap object.
 45
 46      Parameters
 47      ----------
 48      identifier : `str`
 49         Unique identifying name for the object.
 50      material_density_kg_m3 : `float`, optional, default=1.0
 51         Uniform material density in `kg/m^3` to be used in mass property calculations.
 52      """
 53      super().__init__(identifier, self.__create_cad__, None, material_density_kg_m3)
 54      setattr(self.geometry, 'radius', Symbol(self.name + '_radius'))
 55      setattr(self.geometry, 'thickness', Symbol(self.name + '_thickness'))
 56
 57
 58   # CAD generation function ----------------------------------------------------------------------
 59
 60   @staticmethod
 61   def __create_cad__(params: Dict[str, float], fully_displace: bool) -> Part.Solid:
 62      """Scripted CAD generation method for a `Hemisphere`."""
 63      thickness_mm = 1000.0 * params['thickness']
 64      outer_radius_mm = 1000.0 * params['radius']
 65      inner_radius_mm = outer_radius_mm - thickness_mm
 66      outer = Part.makeSphere(outer_radius_mm, FreeCAD.Vector(0, 0, 0), FreeCAD.Vector(0, 0, 1), 0, 90, 360)
 67      if not fully_displace:
 68         inner = Part.makeSphere(inner_radius_mm, FreeCAD.Vector(0, 0, 0), FreeCAD.Vector(0, 0, 1), 0, 90, 360)
 69         return outer.cut(inner)
 70      else:
 71         return outer
 72
 73
 74   # Geometry setter ------------------------------------------------------------------------------
 75
 76   def set_geometry(self, *, radius_m: Union[float, None],
 77                             thickness_m: Union[float, None]) -> Hemisphere:
 78      """Sets the physical geometry of the current `Hemisphere` object.
 79
 80      See the `Hemisphere` class documentation for a description of each geometric parameter.
 81      """
 82      self.geometry.set(radius=radius_m, thickness=thickness_m)
 83      return self
 84
 85   def get_geometric_parameter_bounds(self, parameter: str) -> Tuple[float, float]:
 86      parameter_bounds = {
 87         'radius': (0.0, 2.0),
 88         'thickness': (0.0, 0.05)
 89      }
 90      return parameter_bounds.get(parameter, (0.0, 0.0))
 91
 92
 93   # Geometric properties -------------------------------------------------------------------------
 94
 95   @property
 96   def material_volume(self) -> Union[float, Expr]:
 97      volume = self.displaced_volume
 98      volume -= ((2.0 * math.pi / 3.0) * (self.geometry.radius - self.geometry.thickness)**3)
 99      return volume
100
101   @property
102   def displaced_volume(self) -> Union[float, Expr]:
103      return (2.0 * math.pi / 3.0) * self.geometry.radius**3
104
105   @property
106   def surface_area(self) -> Union[float, Expr]:
107      return 2.0 * math.pi * self.geometry.radius**2
108
109   @property
110   def unoriented_center_of_gravity(self) -> Tuple[Union[float, Expr],
111                                                   Union[float, Expr],
112                                                   Union[float, Expr]]:
113      return self.geometry.radius, self.geometry.radius, 0.5 * self.geometry.radius
114
115   @property
116   def unoriented_center_of_buoyancy(self) -> Tuple[Union[float, Expr],
117                                                    Union[float, Expr],
118                                                    Union[float, Expr]]:
119      return self.geometry.radius, self.geometry.radius, 3.0 * self.geometry.radius / 8.0
120
121   @property
122   def unoriented_length(self) -> Union[float, Expr]:
123      return 2.0 * self.geometry.radius
124
125   @property
126   def unoriented_width(self) -> Union[float, Expr]:
127      return self.unoriented_length
128
129   @property
130   def unoriented_height(self) -> Union[float, Expr]:
131      return self.geometry.radius
132
133   @property
134   def oriented_length(self) -> Union[float, Expr]:
135      # TODO: Implement this
136      return 0
137
138   @property
139   def oriented_width(self) -> Union[float, Expr]:
140      # TODO: Implement this
141      return 0
142
143   @property
144   def oriented_height(self) -> Union[float, Expr]:
145      # TODO: Implement this
146      return 0
class Hemisphere(symcad.parts.endcaps.EndcapShape):
 25class Hemisphere(EndcapShape):
 26   """Model representing a hollow, parametric, hemispherical endcap.
 27
 28   By default, the endcap is oriented such that its base is perpendicular to the z-axis:
 29
 30   ![Hemisphere](https://symbench.github.io/SymCAD/images/Hemisphere.png)
 31
 32   The `geometry` of this shape includes the following parameters:
 33
 34   - `radius`: Radius (in `m`) of the Hemisphere
 35   - `thickness`: Thickness (in `m`) of the shell of the Hemisphere
 36
 37   Note that the above dimensions should be interpreted as if the Hemisphere is unrotated.
 38   In other words, any shape rotation takes place *after* the Hemisphere dimensions have been
 39   specified.
 40   """
 41
 42   # Constructor ----------------------------------------------------------------------------------
 43
 44   def __init__(self, identifier: str, material_density_kg_m3: Optional[float] = 1.0) -> None:
 45      """Initializes a hollow, parametric, hemispherical endcap object.
 46
 47      Parameters
 48      ----------
 49      identifier : `str`
 50         Unique identifying name for the object.
 51      material_density_kg_m3 : `float`, optional, default=1.0
 52         Uniform material density in `kg/m^3` to be used in mass property calculations.
 53      """
 54      super().__init__(identifier, self.__create_cad__, None, material_density_kg_m3)
 55      setattr(self.geometry, 'radius', Symbol(self.name + '_radius'))
 56      setattr(self.geometry, 'thickness', Symbol(self.name + '_thickness'))
 57
 58
 59   # CAD generation function ----------------------------------------------------------------------
 60
 61   @staticmethod
 62   def __create_cad__(params: Dict[str, float], fully_displace: bool) -> Part.Solid:
 63      """Scripted CAD generation method for a `Hemisphere`."""
 64      thickness_mm = 1000.0 * params['thickness']
 65      outer_radius_mm = 1000.0 * params['radius']
 66      inner_radius_mm = outer_radius_mm - thickness_mm
 67      outer = Part.makeSphere(outer_radius_mm, FreeCAD.Vector(0, 0, 0), FreeCAD.Vector(0, 0, 1), 0, 90, 360)
 68      if not fully_displace:
 69         inner = Part.makeSphere(inner_radius_mm, FreeCAD.Vector(0, 0, 0), FreeCAD.Vector(0, 0, 1), 0, 90, 360)
 70         return outer.cut(inner)
 71      else:
 72         return outer
 73
 74
 75   # Geometry setter ------------------------------------------------------------------------------
 76
 77   def set_geometry(self, *, radius_m: Union[float, None],
 78                             thickness_m: Union[float, None]) -> Hemisphere:
 79      """Sets the physical geometry of the current `Hemisphere` object.
 80
 81      See the `Hemisphere` class documentation for a description of each geometric parameter.
 82      """
 83      self.geometry.set(radius=radius_m, thickness=thickness_m)
 84      return self
 85
 86   def get_geometric_parameter_bounds(self, parameter: str) -> Tuple[float, float]:
 87      parameter_bounds = {
 88         'radius': (0.0, 2.0),
 89         'thickness': (0.0, 0.05)
 90      }
 91      return parameter_bounds.get(parameter, (0.0, 0.0))
 92
 93
 94   # Geometric properties -------------------------------------------------------------------------
 95
 96   @property
 97   def material_volume(self) -> Union[float, Expr]:
 98      volume = self.displaced_volume
 99      volume -= ((2.0 * math.pi / 3.0) * (self.geometry.radius - self.geometry.thickness)**3)
100      return volume
101
102   @property
103   def displaced_volume(self) -> Union[float, Expr]:
104      return (2.0 * math.pi / 3.0) * self.geometry.radius**3
105
106   @property
107   def surface_area(self) -> Union[float, Expr]:
108      return 2.0 * math.pi * self.geometry.radius**2
109
110   @property
111   def unoriented_center_of_gravity(self) -> Tuple[Union[float, Expr],
112                                                   Union[float, Expr],
113                                                   Union[float, Expr]]:
114      return self.geometry.radius, self.geometry.radius, 0.5 * self.geometry.radius
115
116   @property
117   def unoriented_center_of_buoyancy(self) -> Tuple[Union[float, Expr],
118                                                    Union[float, Expr],
119                                                    Union[float, Expr]]:
120      return self.geometry.radius, self.geometry.radius, 3.0 * self.geometry.radius / 8.0
121
122   @property
123   def unoriented_length(self) -> Union[float, Expr]:
124      return 2.0 * self.geometry.radius
125
126   @property
127   def unoriented_width(self) -> Union[float, Expr]:
128      return self.unoriented_length
129
130   @property
131   def unoriented_height(self) -> Union[float, Expr]:
132      return self.geometry.radius
133
134   @property
135   def oriented_length(self) -> Union[float, Expr]:
136      # TODO: Implement this
137      return 0
138
139   @property
140   def oriented_width(self) -> Union[float, Expr]:
141      # TODO: Implement this
142      return 0
143
144   @property
145   def oriented_height(self) -> Union[float, Expr]:
146      # TODO: Implement this
147      return 0

Model representing a hollow, parametric, hemispherical endcap.

By default, the endcap is oriented such that its base is perpendicular to the z-axis:

Hemisphere

The geometry of this shape includes the following parameters:

  • radius: Radius (in m) of the Hemisphere
  • thickness: Thickness (in m) of the shell of the Hemisphere

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

Hemisphere(identifier: str, material_density_kg_m3: Optional[float] = 1.0)
44   def __init__(self, identifier: str, material_density_kg_m3: Optional[float] = 1.0) -> None:
45      """Initializes a hollow, parametric, hemispherical endcap object.
46
47      Parameters
48      ----------
49      identifier : `str`
50         Unique identifying name for the object.
51      material_density_kg_m3 : `float`, optional, default=1.0
52         Uniform material density in `kg/m^3` to be used in mass property calculations.
53      """
54      super().__init__(identifier, self.__create_cad__, None, material_density_kg_m3)
55      setattr(self.geometry, 'radius', Symbol(self.name + '_radius'))
56      setattr(self.geometry, 'thickness', Symbol(self.name + '_thickness'))

Initializes a hollow, parametric, hemispherical endcap 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.
def set_geometry( self, *, radius_m: Optional[float], thickness_m: Optional[float]) -> Hemisphere:
77   def set_geometry(self, *, radius_m: Union[float, None],
78                             thickness_m: Union[float, None]) -> Hemisphere:
79      """Sets the physical geometry of the current `Hemisphere` object.
80
81      See the `Hemisphere` class documentation for a description of each geometric parameter.
82      """
83      self.geometry.set(radius=radius_m, thickness=thickness_m)
84      return self

Sets the physical geometry of the current Hemisphere object.

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

def get_geometric_parameter_bounds(self, parameter: str) -> Tuple[float, float]:
86   def get_geometric_parameter_bounds(self, parameter: str) -> Tuple[float, float]:
87      parameter_bounds = {
88         'radius': (0.0, 2.0),
89         'thickness': (0.0, 0.05)
90      }
91      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).