Files
create/src/Mod/Ship/shipGZ/Tools.py
2018-04-14 10:40:31 +02:00

308 lines
12 KiB
Python

#***************************************************************************
#* *
#* Copyright (c) 2011, 2016 *
#* 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
import FreeCAD as App
import FreeCADGui as Gui
from FreeCAD import Vector, Matrix, Placement
import Part
from FreeCAD import Units
import Instance as ShipInstance
import WeightInstance
import TankInstance
from shipHydrostatics import Tools as Hydrostatics
G = Units.parseQuantity("9.81 m/s^2")
MAX_EQUILIBRIUM_ITERS = 10
DENS = Units.parseQuantity("1025 kg/m^3")
TRIM_RELAX_FACTOR = 10.0
def solve(ship, weights, tanks, rolls, var_trim=True):
"""Compute the ship GZ stability curve
Position arguments:
ship -- Ship object
weights -- List of weights to consider
tanks -- List of tanks to consider (each one should be a tuple with the
tank instance, the density of the fluid inside, and the filling level ratio)
rolls -- List of roll angles
Keyword arguments:
var_trim -- True if the equilibrium trim should be computed for each roll
angle, False if null trim angle can be used instead.
Returned value:
List of GZ curve points. Each point contains the GZ stability length, the
equilibrium draft, and the equilibrium trim angle (0 deg if var_trim is
False)
"""
# Get the unloaded weight (ignoring the tanks for the moment).
W = Units.parseQuantity("0 kg")
mom_x = Units.parseQuantity("0 kg*m")
mom_y = Units.parseQuantity("0 kg*m")
mom_z = Units.parseQuantity("0 kg*m")
for w in weights:
W += w.Proxy.getMass(w)
m = w.Proxy.getMoment(w)
mom_x += m[0]
mom_y += m[1]
mom_z += m[2]
COG = Vector(mom_x / W, mom_y / W, mom_z / W)
W = W * G
# Get the tanks weight
TW = Units.parseQuantity("0 kg")
VOLS = []
for t in tanks:
# t[0] = tank object
# t[1] = load density
# t[2] = filling level
vol = t[0].Proxy.getVolume(t[0], t[2])
VOLS.append(vol)
TW += vol * t[1]
TW = TW * G
points = []
for i,roll in enumerate(rolls):
App.Console.PrintMessage("{0} / {1}\n".format(i + 1, len(rolls)))
point = solve_point(W, COG, TW, VOLS,
ship, tanks, roll, var_trim)
if point is None:
return []
points.append(point)
return points
def solve_point(W, COG, TW, VOLS, ship, tanks, roll, var_trim=True):
""" Compute the ship GZ value.
@param W Empty ship weight.
@param COG Empty ship Center of mass.
@param TW Tanks weights.
@param VOLS List of tank volumes.
@param tanks Considered tanks.
@param roll Roll angle.
@param var_trim True if the trim angle should be recomputed at each roll
angle, False otherwise.
@return GZ value, equilibrium draft, and equilibrium trim angle (0 if
variable trim has not been requested)
"""
# Look for the equilibrium draft (and eventually the trim angle too)
max_draft = Units.Quantity(ship.Shape.BoundBox.ZMax, Units.Length)
draft = ship.Draft
max_disp = Units.Quantity(ship.Shape.Volume, Units.Volume) * DENS * G
if max_disp < W + TW:
msg = QtGui.QApplication.translate(
"ship_console",
"Too much weight! The ship will never displace water enough",
None)
App.Console.PrintError(msg + ' ({} vs. {})\n'.format(
(max_disp / G).UserString, ((W + TW) / G).UserString))
return None
trim = Units.parseQuantity("0 deg")
for i in range(MAX_EQUILIBRIUM_ITERS):
# Get the displacement, and the bouyance application point
disp, B, _ = Hydrostatics.displacement(ship,
draft,
roll,
trim)
disp *= G
# Add the tanks effect on the center of gravity
mom_x = Units.Quantity(COG.x, Units.Length) * W
mom_y = Units.Quantity(COG.y, Units.Length) * W
mom_z = Units.Quantity(COG.z, Units.Length) * W
for i,t in enumerate(tanks):
tank_weight = VOLS[i] * t[1] * G
tank_cog = t[0].Proxy.getCoG(t[0], VOLS[i], roll, trim)
mom_x += Units.Quantity(tank_cog.x, Units.Length) * tank_weight
mom_y += Units.Quantity(tank_cog.y, Units.Length) * tank_weight
mom_z += Units.Quantity(tank_cog.z, Units.Length) * tank_weight
cog_x = mom_x / (W + TW)
cog_y = mom_y / (W + TW)
cog_z = mom_z / (W + TW)
# Compute the errors
draft_error = -((disp - W - TW) / max_disp).Value
R_x = cog_x - Units.Quantity(B.x, Units.Length)
R_y = cog_y - Units.Quantity(B.y, Units.Length)
R_z = cog_z - Units.Quantity(B.z, Units.Length)
if not var_trim:
trim_error = 0.0
else:
trim_error = -TRIM_RELAX_FACTOR * R_x / ship.Length
# Check if we can tolerate the errors
if abs(draft_error) < 0.01 and abs(trim_error) < 0.1:
break
# Get the new draft and trim
draft += draft_error * max_draft
trim += trim_error * Units.Degree
# GZ should be provided in the Free surface oriented frame of reference
c = math.cos(roll.getValueAs('rad'))
s = math.sin(roll.getValueAs('rad'))
return c * R_y - s * R_z, draft, trim
def gz(lc, rolls, var_trim=True):
"""Compute the ship GZ stability curve
Position arguments:
lc -- Load condition spreadsheet
rolls -- List of roll angles to compute
Keyword arguments:
var_trim -- True if the equilibrium trim should be computed for each roll
angle, False if null trim angle can be used instead.
Returned value:
List of GZ curve points. Each point contains the GZ stability length, the
equilibrium draft, and the equilibrium trim angle (0 deg if var_trim is
False)
"""
# B1 cell must be a ship
# B2 cell must be the loading condition itself
doc = lc.Document
try:
if lc not in doc.getObjectsByLabel(lc.get('B2')):
return[]
ships = doc.getObjectsByLabel(lc.get('B1'))
if len(ships) != 1:
if len(ships) == 0:
msg = QtGui.QApplication.translate(
"ship_console",
"Wrong Ship label! (no instances labeled as"
"'{}' found)",
None)
App.Console.PrintError(msg + '\n'.format(
lc.get('B1')))
else:
msg = QtGui.QApplication.translate(
"ship_console",
"Ambiguous Ship label! ({} instances labeled as"
"'{}' found)",
None)
App.Console.PrintError(msg + '\n'.format(
len(ships),
lc.get('B1')))
return[]
ship = ships[0]
if ship is None or not ship.PropertiesList.index("IsShip"):
return[]
except ValueError:
return[]
# Extract the weights and the tanks
weights = []
index = 6
while True:
try:
ws = doc.getObjectsByLabel(lc.get('A{}'.format(index)))
except ValueError:
break
index += 1
if len(ws) != 1:
if len(ws) == 0:
msg = QtGui.QApplication.translate(
"ship_console",
"Wrong Weight label! (no instances labeled as"
"'{}' found)",
None)
App.Console.PrintError(msg + '\n'.format(
lc.get('A{}'.format(index - 1))))
else:
msg = QtGui.QApplication.translate(
"ship_console",
"Ambiguous Weight label! ({} instances labeled as"
"'{}' found)",
None)
App.Console.PrintError(msg + '\n'.format(
len(ws),
lc.get('A{}'.format(index - 1))))
continue
w = ws[0]
try:
if w is None or not w.PropertiesList.index("IsWeight"):
msg = QtGui.QApplication.translate(
"ship_console",
"Invalid Weight! (the object labeled as"
"'{}' is not a weight)",
None)
App.Console.PrintError(msg + '\n'.format(
len(ws),
lc.get('A{}'.format(index - 1))))
continue
except ValueError:
continue
weights.append(w)
tanks = []
index = 6
while True:
try:
ts = doc.getObjectsByLabel(lc.get('C{}'.format(index)))
dens = float(lc.get('D{}'.format(index)))
level = float(lc.get('E{}'.format(index)))
dens = Units.parseQuantity("{} kg/m^3".format(dens))
except ValueError:
break
index += 1
if len(ts) != 1:
if len(ts) == 0:
msg = QtGui.QApplication.translate(
"ship_console",
"Wrong Tank label! (no instances labeled as"
"'{}' found)",
None)
App.Console.PrintError(msg + '\n'.format(
lc.get('C{}'.format(index - 1))))
else:
msg = QtGui.QApplication.translate(
"ship_console",
"Ambiguous Tank label! ({} instances labeled as"
"'{}' found)",
None)
App.Console.PrintError(msg + '\n'.format(
len(ts),
lc.get('C{}'.format(index - 1))))
continue
t = ts[0]
try:
if t is None or not t.PropertiesList.index("IsTank"):
msg = QtGui.QApplication.translate(
"ship_console",
"Invalid Tank! (the object labeled as"
"'{}' is not a tank)",
None)
App.Console.PrintError(msg + '\n'.format(
len(ws),
lc.get('C{}'.format(index - 1))))
continue
except ValueError:
continue
tanks.append((t, dens, level))
return solve(ship, weights, tanks, rolls, var_trim)