FreeCAD/src/Mod/Ship/shipHydrostatics/Tools.py

423 lines
16 KiB
Python

#***************************************************************************
#* *
#* Copyright (c) 2011, 2012 *
#* Jose Luis Cercos Pita <jlcercos@gmail.com> *
#* *
#* This program is free software; you can redistribute it and/or modify *
#* it under the terms of the GNU Lesser General Public License (LGPL) *
#* as published by the Free Software Foundation; either version 2 of *
#* the License, or (at your option) any later version. *
#* for detail see the LICENCE text file. *
#* *
#* This program is distributed in the hope that it will be useful, *
#* but WITHOUT ANY WARRANTY; without even the implied warranty of *
#* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
#* GNU Library General Public License for more details. *
#* *
#* You should have received a copy of the GNU Library General Public *
#* License along with this program; if not, write to the Free Software *
#* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 *
#* USA *
#* *
#***************************************************************************
import math
# FreeCAD modules
from FreeCAD import Vector, Part
import FreeCAD as App
import FreeCADGui as Gui
# Module
import Instance
from shipUtils import Math
def areas(ship, draft, roll=0.0, trim=0.0, yaw=0.0, n=30):
""" Compute ship transversal areas.
@param ship Ship instance.
@param draft Ship draft.
@param roll Ship roll angle.
@param trim Ship trim angle.
@param yaw Ship yaw angle. Ussually you don't want to use this
value.
@param n Number of sections to perform.
@return Transversal areas (every area value is composed by x
coordinate and computed area)
"""
if n < 2:
return []
# We will take a duplicate of ship shape in order to place it
shape = ship.Shape.copy()
shape.translate(Vector(0.0,0.0,-draft))
# Rotations composition is Roll->Trim->Yaw
shape.rotate(Vector(0.0,0.0,0.0), Vector(1.0,0.0,0.0), roll)
shape.rotate(Vector(0.0,0.0,0.0), Vector(0.0,-1.0,0.0), trim)
shape.rotate(Vector(0.0,0.0,0.0), Vector(0.0,0.0,1.0), yaw)
# Now we need to know the x range of values
bbox = shape.BoundBox
xmin = bbox.XMin
xmax = bbox.XMax
dx = (xmax - xmin) / (n-1.0)
# First area is equal to zero.
areas = [[xmin, 0.0]]
# Since we need face entities, in order to compute sections we will
# create boxes with front face at transversal area position,
# compute solid common, divide by faces, and preserve only desired
# ones.
App.Console.PrintMessage("Computing transversal areas...\n")
App.Console.PrintMessage("Some Inventor representation errors can be shown, ignore it please.\n")
for i in range(1,n-1):
App.Console.PrintMessage("%d / %d\n" % (i, n-2))
x = xmin + i*dx
area = 0.0
# Create the box
L = xmax - xmin
B = bbox.YMax - bbox.YMin
p = Vector(-1.5*L, -1.5*B, bbox.ZMin - 1.0)
box = Part.makeBox(1.5*L + x, 3.0*B, - bbox.ZMin + 1.0, p)
# Compute common part with ship
for s in shape.Solids:
# Get solids intersection
try:
common = box.common(s)
except:
continue
if common.Volume == 0.0:
continue
# Recompute object adding it to the scene, when we have
# computed desired data we can remove it.
try:
Part.show(common)
except:
continue
# Divide by faces and compute only section placed ones
faces = common.Faces
for f in faces:
faceBounds = f.BoundBox
# Orientation filter
if faceBounds.XMax - faceBounds.XMin > 0.00001:
continue
# Place filter
if abs(faceBounds.XMax - x) > 0.00001:
continue
# Valid face, compute area
area = area + f.Area
# Destroy last object generated
App.ActiveDocument.removeObject(App.ActiveDocument.Objects[-1].Name)
# Append transversal area
areas.append([x, area])
# Last area is equal to zero
areas.append([xmax, 0.0])
App.Console.PrintMessage("Done!\n")
return areas
def displacement(ship, draft, roll=0.0, trim=0.0, yaw=0.0):
""" Compute ship displacement.
@param ship Ship instance.
@param draft Ship draft.
@param roll Ship roll angle.
@param trim Ship trim angle.
@param yaw Ship yaw angle. Ussually you don't want to use this
value.
@return [disp, B, Cb], \n
disp = Ship displacement [ton].
B = Bouyance center [m].
Cb = Block coefficient.
@note Bouyance center will returned as FreeCAD.Vector class.
@note Returned Bouyance center is in non modified ship coordinates
"""
# We will take a duplicate of ship shape in order to place it
shape = ship.Shape.copy()
shape.translate(Vector(0.0,0.0,-draft))
# Rotations composition is Roll->Trim->Yaw
shape.rotate(Vector(0.0,0.0,0.0), Vector(1.0,0.0,0.0), roll)
shape.rotate(Vector(0.0,0.0,0.0), Vector(0.0,-1.0,0.0), trim)
shape.rotate(Vector(0.0,0.0,0.0), Vector(0.0,0.0,1.0), yaw)
# Now we need to know box dimensions
bbox = shape.BoundBox
xmin = bbox.XMin
xmax = bbox.XMax
# Create the box
L = xmax - xmin
B = bbox.YMax - bbox.YMin
p = Vector(-1.5*L, -1.5*B, bbox.ZMin - 1.0)
box = Part.makeBox(3.0*L, 3.0*B, -bbox.ZMin + 1.0, p)
vol = 0.0
cog = Vector()
for solid in shape.Solids:
# Compute common part with ship
try:
common = box.common(solid)
except:
continue
# Get data
vol = vol + common.Volume
for s in common.Solids:
sCoG = s.CenterOfMass
cog.x = cog.x + sCoG.x*s.Volume
cog.y = cog.y + sCoG.y*s.Volume
cog.z = cog.z + sCoG.z*s.Volume
cog.x = cog.x / vol
cog.y = cog.y / vol
cog.z = cog.z / vol
Vol = L*B*abs(bbox.ZMin)
# Undo transformations
B = Vector()
B.x = cog.x*math.cos(math.radians(-yaw)) - cog.y*math.sin(math.radians(-yaw))
B.y = cog.x*math.sin(math.radians(-yaw)) + cog.y*math.cos(math.radians(-yaw))
B.z = cog.z
cog.x = B.x*math.cos(math.radians(-trim)) - B.z*math.sin(math.radians(-trim))
cog.y = B.y
cog.z = B.x*math.sin(math.radians(-trim)) + B.z*math.cos(math.radians(-trim))
B.x = cog.x
B.y = cog.y*math.cos(math.radians(-roll)) - cog.z*math.sin(math.radians(-roll))
B.z = cog.y*math.sin(math.radians(-roll)) + cog.z*math.cos(math.radians(-roll))
B.z = B.z + draft
# Return data
dens = 1.025 # [tons/m3], salt water
return [dens*vol, B, vol/Vol]
def wettedArea(shape, draft, trim):
""" Calculate wetted ship area.
@param shape Ship external faces instance.
@param draft Draft.
@param trim Trim in degrees.
@return Wetted ship area.
"""
area = 0.0
nObjects = 0
# We will take a duplicate of ship shape in order to place it
shape = shape.copy()
shape.translate(Vector(0.0,0.0,-draft))
shape.rotate(Vector(0.0,0.0,0.0), Vector(0.0,-1.0,0.0), trim)
# Now we need to know the x range of values
bbox = shape.BoundBox
xmin = bbox.XMin
xmax = bbox.XMax
# Create the box
L = xmax - xmin
B = bbox.YMax - bbox.YMin
p = Vector(-1.5*L, -1.5*B, bbox.ZMin - 1.0)
box = Part.makeBox(3.0*L, 3.0*B, - bbox.ZMin + 1.0, p)
# Compute common part with ship
for f in shape.Faces:
# Get solids intersection
try:
common = box.common(f)
except:
continue
area = area + common.Area
return area
def moment(ship, draft, trim, disp, xcb):
""" Calculate triming 1cm ship moment.
@param ship Selected ship instance
@param draft Draft.
@param trim Trim in degrees.
@param disp Displacement at selected draft and trim.
@param xcb Bouyance center at selected draft and trim.
@return Moment to trim ship 1cm (ton m).
@note Moment is positive when produce positive trim.
"""
angle = math.degrees(math.atan2(0.01,0.5*ship.Length))
newTrim = trim + angle
data = displacement(ship,draft,0.0,newTrim,0.0)
mom0 = -disp*xcb
mom1 = -data[0]*data[1].x
return mom1 - mom0
def FloatingArea(ship, draft, trim):
""" Calculate ship floating area.
@param ship Selected ship instance
@param draft Draft.
@param trim Trim in degrees.
@return Ship floating area, and floating coefficient.
"""
area = 0.0
cf = 0.0
maxX = 0.0
minX = 0.0
maxY = 0.0
minY = 0.0
# We will take a duplicate of ship shape in order to place it
shape = ship.Shape.copy()
shape.translate(Vector(0.0,0.0,-draft))
shape.rotate(Vector(0.0,0.0,0.0), Vector(0.0,-1.0,0.0), trim)
# Now we need to know the x range of values
bbox = shape.BoundBox
xmin = bbox.XMin
xmax = bbox.XMax
# Create the box
L = xmax - xmin
B = bbox.YMax - bbox.YMin
p = Vector(-1.5*L, -1.5*B, bbox.ZMin - 1.0)
box = Part.makeBox(3.0*L, 3.0*B, - bbox.ZMin + 1.0, p)
# Compute common part with ship
for s in shape.Solids:
# Get solids intersection
try:
common = box.common(s)
except:
continue
if common.Volume == 0.0:
continue
# Recompute object adding it to the scene, when we have
# computed desired data we can remove it.
try:
Part.show(common)
except:
continue
# Divide by faces and compute only section placed ones
faces = common.Faces
for f in faces:
faceBounds = f.BoundBox
# Orientation filter
if faceBounds.ZMax - faceBounds.ZMin > 0.00001:
continue
# Place filter
if abs(faceBounds.ZMax) > 0.00001:
continue
# Valid face, compute area
area = area + f.Area
maxX = max(maxX, faceBounds.XMax)
minX = max(minX, faceBounds.XMin)
maxY = max(maxY, faceBounds.YMax)
minY = max(minY, faceBounds.YMin)
# Destroy last object generated
App.ActiveDocument.removeObject(App.ActiveDocument.Objects[-1].Name)
dx = maxX - minX
dy = maxY - minY
if dx*dy > 0.0:
cf = area / (dx*dy)
return [area, cf]
def BMT(ship, draft, trim=0.0):
""" Calculate ship Bouyance center transversal distance.
@param ship Ship instance.
@param draft Ship draft.
@param trim Ship trim angle.
@return BM Bouyance to metacenter height [m].
"""
nRoll = 2
maxRoll = 7.0
B0 = displacement(ship,draft,0.0,trim,0.0)[1]
BM = 0.0
for i in range(0,nRoll):
roll = (maxRoll/nRoll)*(i+1)
B1 = displacement(ship,draft,roll,trim,0.0)[1]
# * M
# / \
# / \ BM ==|> BM = (BB/2) / tan(alpha/2)
# / \
# *-------*
# BB
BB = [B1.x - B0.x, B1.y - B0.y]
BB = math.sqrt(BB[0]*BB[0] + BB[1]*BB[1])
BM = BM + 0.5*BB/math.tan(math.radians(0.5*roll)) / nRoll # nRoll is the weight function
return BM
def mainFrameCoeff(ship, draft):
""" Calculate main frame coefficient.
@param ship Selected ship instance
@param draft Draft.
@return Main frame coefficient
"""
cm = 0.0
maxY = 0.0
minY = 0.0
# We will take a duplicate of ship shape in order to place it
shape = ship.Shape.copy()
shape.translate(Vector(0.0,0.0,-draft))
x = 0.0
area = 0.0
# Now we need to know the x range of values
bbox = shape.BoundBox
xmin = bbox.XMin
xmax = bbox.XMax
# Create the box
L = xmax - xmin
B = bbox.YMax - bbox.YMin
p = Vector(-1.5*L, -1.5*B, bbox.ZMin - 1.0)
box = Part.makeBox(1.5*L + x, 3.0*B, - bbox.ZMin + 1.0, p)
# Compute common part with ship
for s in shape.Solids:
# Get solids intersection
try:
common = box.common(s)
except:
continue
if common.Volume == 0.0:
continue
# Recompute object adding it to the scene, when we have
# computed desired data we can remove it.
try:
Part.show(common)
except:
continue
# Divide by faces and compute only section placed ones
faces = common.Faces
for f in faces:
faceBounds = f.BoundBox
# Orientation filter
if faceBounds.XMax - faceBounds.XMin > 0.00001:
continue
# Place filter
if abs(faceBounds.XMax - x) > 0.00001:
continue
# Valid face, compute area
area = area + f.Area
maxY = max(maxY, faceBounds.YMax)
minY = max(minY, faceBounds.YMin)
# Destroy last object generated
App.ActiveDocument.removeObject(App.ActiveDocument.Objects[-1].Name)
dy = maxY - minY
if dy*draft > 0.0:
cm = area / (dy*draft)
return cm
class Point:
""" Hydrostatics point, that conatins: \n
draft Ship draft [m]. \n
trim Ship trim [deg]. \n
disp Ship displacement [ton]. \n
xcb Bouyance center X coordinate [m].
wet Wetted ship area [m2].
mom Triming 1cm ship moment [ton m].
farea Floating area [m2].
KBt Transversal KB height [m].
BMt Transversal BM height [m].
Cb Block coefficient.
Cf Floating coefficient.
Cm Main frame coefficient.
@note Moment is positive when produce positive trim.
"""
def __init__(self, ship, faces, draft, trim):
""" Use all hydrostatics tools to define a hydrostatics
point.
@param ship Selected ship instance
@param faces Ship external faces
@param draft Draft.
@param trim Trim in degrees.
"""
# Hydrostatics computation
dispData = displacement(ship,draft,0.0,trim,0.0)
if not faces:
wet = 0.0
else:
wet = wettedArea(faces,draft,trim)
mom = moment(ship,draft,trim,dispData[0],dispData[1].x)
farea = FloatingArea(ship,draft,trim)
bm = BMT(ship,draft,trim)
cm = mainFrameCoeff(ship,draft)
# Store final data
self.draft = draft
self.trim = trim
self.disp = dispData[0]
self.xcb = dispData[1].x
self.wet = wet
self.farea = farea[0]
self.mom = mom
self.KBt = dispData[1].y
self.BMt = bm
self.Cb = dispData[2]
self.Cf = farea[1]
self.Cm = cm