Implemented a draft of the GZ computation tool
This commit is contained in:
parent
a0a2a1012e
commit
63f976e25a
|
@ -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})
|
||||
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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>
|
||||
|
|
143
src/Mod/Ship/shipGZ/Tools.py
Normal file
143
src/Mod/Ship/shipGZ/Tools.py
Normal 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
|
Loading…
Reference in New Issue
Block a user