Implemented a draft of the GZ computation tool

This commit is contained in:
Jose Luis Cercos Pita 2016-01-18 18:04:01 +01:00
parent a0a2a1012e
commit 63f976e25a
4 changed files with 166 additions and 53 deletions

View File

@ -90,6 +90,7 @@ SET(ShipGZ_SRCS
shipGZ/PlotAux.py
shipGZ/TaskPanel.py
shipGZ/TaskPanel.ui
shipGZ/Tools.py
)
SOURCE_GROUP("shipgz" FILES ${ShipGZ_SRCS})

View File

@ -27,6 +27,7 @@ import FreeCADGui as Gui
import Units
from PySide import QtGui, QtCore
import PlotAux
import Tools
from shipUtils import Paths
import shipUtils.Units as USys
import shipUtils.Locale as Locale
@ -40,6 +41,26 @@ class TaskPanel:
if self.lc is None:
return False
self.save()
mw = self.getMainWindow()
form = mw.findChild(QtGui.QWidget, "TaskPanel")
form.angle = self.widget(QtGui.QLineEdit, "Angle")
form.n_points = self.widget(QtGui.QSpinBox, "NumPoints")
form.var_trim = self.widget(QtGui.QCheckBox, "VariableTrim")
rolls = []
roll = Units.Quantity(Locale.fromString(
form.angle.text())).getValueAs('deg').Value
n_points = form.n_points.value()
for i in range(n_points):
rolls.append(roll * i / float(n_points - 1))
gz = Tools.solve(self.ship,
self.weights,
self.tanks
rolls,
form.var_trim.isChecked())
return True
def reject(self):
@ -72,7 +93,6 @@ class TaskPanel:
form.angle = self.widget(QtGui.QLineEdit, "Angle")
form.n_points = self.widget(QtGui.QSpinBox, "NumPoints")
form.var_draft = self.widget(QtGui.QCheckBox, "VariableDraft")
form.var_trim = self.widget(QtGui.QCheckBox, "VariableTrim")
self.form = form
if self.initValues():
@ -278,7 +298,6 @@ class TaskPanel:
form = mw.findChild(QtGui.QWidget, "TaskPanel")
form.angle = self.widget(QtGui.QLineEdit, "Angle")
form.n_points = self.widget(QtGui.QSpinBox, "NumPoints")
form.var_draft = self.widget(QtGui.QCheckBox, "VariableDraft")
form.var_trim = self.widget(QtGui.QCheckBox, "VariableTrim")
form.angle.setText(Locale.toString(angle_format.format(90.0)))
# Try to use saved values
@ -295,14 +314,6 @@ class TaskPanel:
form.n_points.setValue(self.ship.GZNumPoints)
except ValueError:
pass
try:
props.index("GZVariableDraft")
if self.ship.GZVariableDraft:
form.var_draft.setCheckState(QtCore.Qt.Checked)
else:
form.var_draft.setCheckState(QtCore.Qt.Unchecked)
except ValueError:
pass
try:
props.index("GZVariableTrim")
if self.ship.GZVariableTrim:
@ -336,20 +347,6 @@ class TaskPanel:
"Number of points",
None,
QtGui.QApplication.UnicodeUTF8))
self.widget(QtGui.QCheckBox, "VariableDraft").setText(
QtGui.QApplication.translate(
"ship_gz",
"Variable draft",
None,
QtGui.QApplication.UnicodeUTF8))
self.widget(QtGui.QCheckBox, "VariableDraft").setToolTip(
QtGui.QApplication.translate(
"ship_gz",
"The ship will be moved to the equilibrium draft for each" + \
" roll angle. It will significantly increase the required" + \
" computing time",
None,
QtGui.QApplication.UnicodeUTF8))
self.widget(QtGui.QCheckBox, "VariableTrim").setText(
QtGui.QApplication.translate(
"ship_gz",
@ -371,13 +368,11 @@ class TaskPanel:
form = mw.findChild(QtGui.QWidget, "TaskPanel")
form.angle = self.widget(QtGui.QLineEdit, "Angle")
form.n_points = self.widget(QtGui.QSpinBox, "NumPoints")
form.var_draft = self.widget(QtGui.QCheckBox, "VariableDraft")
form.var_trim = self.widget(QtGui.QCheckBox, "VariableTrim")
angle = Units.Quantity(Locale.fromString(
form.angle.text())).getValueAs('deg').Value
n_points = form.n_points.value()
var_draft = form.var_draft.isChecked()
var_trim = form.var_trim.isChecked()
props = self.ship.PropertiesList
@ -413,22 +408,6 @@ class TaskPanel:
"Ship",
tooltip)
self.ship.GZNumPoints = n_points
try:
props.index("GZVariableDraft")
except ValueError:
try:
tooltip = str(QtGui.QApplication.translate(
"ship_areas",
"GZ curve tool variable draft selection",
None,
QtGui.QApplication.UnicodeUTF8))
except:
tooltip = "GZ curve tool variable draft selection"
self.ship.addProperty("App::PropertyBool",
"GZVariableDraft",
"Ship",
tooltip)
self.ship.GZVariableDraft = var_draft
try:
props.index("GZVariableTrim")
except ValueError:

View File

@ -33,16 +33,6 @@
</property>
</widget>
</item>
<item row="2" column="0" colspan="2">
<widget class="QCheckBox" name="VariableDraft">
<property name="text">
<string>Variable Draft</string>
</property>
<property name="checked">
<bool>true</bool>
</property>
</widget>
</item>
<item row="0" column="0">
<widget class="QLabel" name="AngleLabel">
<property name="sizePolicy">
@ -59,7 +49,7 @@
<item row="0" column="1">
<widget class="Gui::InputField" name="Angle"/>
</item>
<item row="3" column="0" colspan="2">
<item row="2" column="0" colspan="2">
<widget class="QCheckBox" name="VariableTrim">
<property name="text">
<string>Variable Trim angle</string>

View File

@ -0,0 +1,143 @@
#***************************************************************************
#* *
#* 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
from FreeCAD import Vector, Matrix, Placement
import Part
import Units
import FreeCAD as App
import FreeCADGui as Gui
import Instance as ShipInstance
import WeightInstance
import TankInstance
from shipHydrostatics import Tools as Hydrostatics
G = 9.81
MAX_EQUILIBRIUM_ITERS = 10
DENS = 1.025 # [tons/m3], salt water
def solve(ship, weights, tanks, rolls, var_trim=True):
""" Compute the ship GZ curve.
@param ship Ship instance.
@param weights Considered weights.
@param tanks Considered tanks.
@param rolls List of considered roll angles.
@param var_trim True if the trim angle should be recomputed at each roll
angle, False otherwise.
@return GZ values for each roll angle
"""
# Get the unloaded weight (ignoring the tanks for the moment).
W = 0.0
COG = Vector()
for w in weights:
W += w.Proxy.getMass(w).getValueAs('kg').Value
m = w.Proxy.getMoment(w)
COG.x += m[0].getValueAs('kg*m').Value
COG.y += m[1].getValueAs('kg*m').Value
COG.z += m[2].getValueAs('kg*m').Value
COG = COG.multiply(1.0 / W)
W = W * G
# Get the tanks weight
TW = 0.0
for t in tanks:
# t[0] = tank object
# t[1] = load density
# t[2] = filling level
vol = t[0].Proxy.setFillingLevel(t[0], t[2]).getValueAs('m^3').Value
TW += vol * t[1]
TW = TW.getValueAs('kg').Value * G
gzs = []
for roll in rolls:
gz = solve_point(W, COG, TW, ship, tanks, roll, var_trim)
if gz is None:
return []
gzs.append(solve_point(W, COG, TW, ship, tanks, roll, var_trim))
return gzs
def solve_point(W, COG, TW, ship, tanks, roll, var_trim=True):
""" Compute the ship GZ value.
@param W Empty ship weight.
@param COG Empty ship Center of mass.
@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
"""
gz = 0.0
# Look for the equilibrium draft (and eventually the trim angle too)
max_draft = ship.Shape.BoundBox.ZMax
draft = max_draft
max_disp = ship.Shape.Volume.getValueAs('m^3').Value * DENS * 1000.0 * G
if max_disp < W + TW:
msg = QtGui.QApplication.translate(
"ship_console",
"Too much weight! The ship will never displace water enough",
None,
QtGui.QApplication.UnicodeUTF8)
App.Console.PrintError(msg + ' ({} tons vs. {} tons)\n'.format(
max_disp / 1000.0 / G, (W + TW) / 1000.0 / G))
return None
trim = 0.0
for i in range(MAX_EQUILIBRIUM_ITERS):
# Get the displacement, and the bouyance application point
disp, B, Cb = Hydrostatics.displacement(ship, draft, roll, trim)
disp *= 1000.0 * G
# Get the empty ship weight transformed application point
p = Part.makePoint(COG)
p.translate(Vector(0.0, 0.0, -draft))
m = Matrix()
m.rotateX(math.radians(roll))
m.rotateY(-math.radians(trim))
p.rotate(Placement(m))
# Add the tanks
# TODO
# ---
# Compute the errors
draft_error = abs(disp - W - TW) / max_disp
if not var_trim:
trim_error = 0.0
else:
dx = B.x - p.X
dz = B.z - p.Z
if abs(dx) < 0.001 * ship.Length.getValueAs('m').Value:
trim_error = 0.0
else:
trim_error = math.degrees(math.atan2(dz, dx))
# Check if we can tolerate the errors
if draft_error < 0.01 and trim_error < 1.0:
break
# Get the new draft and trim
draft += draft_error * max_draft
trim += 0.5 * trim_error
return B.y - p.Y