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/PlotAux.py
|
||||||
shipGZ/TaskPanel.py
|
shipGZ/TaskPanel.py
|
||||||
shipGZ/TaskPanel.ui
|
shipGZ/TaskPanel.ui
|
||||||
|
shipGZ/Tools.py
|
||||||
)
|
)
|
||||||
SOURCE_GROUP("shipgz" FILES ${ShipGZ_SRCS})
|
SOURCE_GROUP("shipgz" FILES ${ShipGZ_SRCS})
|
||||||
|
|
||||||
|
|
|
@ -27,6 +27,7 @@ import FreeCADGui as Gui
|
||||||
import Units
|
import Units
|
||||||
from PySide import QtGui, QtCore
|
from PySide import QtGui, QtCore
|
||||||
import PlotAux
|
import PlotAux
|
||||||
|
import Tools
|
||||||
from shipUtils import Paths
|
from shipUtils import Paths
|
||||||
import shipUtils.Units as USys
|
import shipUtils.Units as USys
|
||||||
import shipUtils.Locale as Locale
|
import shipUtils.Locale as Locale
|
||||||
|
@ -40,6 +41,26 @@ class TaskPanel:
|
||||||
if self.lc is None:
|
if self.lc is None:
|
||||||
return False
|
return False
|
||||||
self.save()
|
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
|
return True
|
||||||
|
|
||||||
def reject(self):
|
def reject(self):
|
||||||
|
@ -72,7 +93,6 @@ class TaskPanel:
|
||||||
|
|
||||||
form.angle = self.widget(QtGui.QLineEdit, "Angle")
|
form.angle = self.widget(QtGui.QLineEdit, "Angle")
|
||||||
form.n_points = self.widget(QtGui.QSpinBox, "NumPoints")
|
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.var_trim = self.widget(QtGui.QCheckBox, "VariableTrim")
|
||||||
self.form = form
|
self.form = form
|
||||||
if self.initValues():
|
if self.initValues():
|
||||||
|
@ -278,7 +298,6 @@ class TaskPanel:
|
||||||
form = mw.findChild(QtGui.QWidget, "TaskPanel")
|
form = mw.findChild(QtGui.QWidget, "TaskPanel")
|
||||||
form.angle = self.widget(QtGui.QLineEdit, "Angle")
|
form.angle = self.widget(QtGui.QLineEdit, "Angle")
|
||||||
form.n_points = self.widget(QtGui.QSpinBox, "NumPoints")
|
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.var_trim = self.widget(QtGui.QCheckBox, "VariableTrim")
|
||||||
form.angle.setText(Locale.toString(angle_format.format(90.0)))
|
form.angle.setText(Locale.toString(angle_format.format(90.0)))
|
||||||
# Try to use saved values
|
# Try to use saved values
|
||||||
|
@ -295,14 +314,6 @@ class TaskPanel:
|
||||||
form.n_points.setValue(self.ship.GZNumPoints)
|
form.n_points.setValue(self.ship.GZNumPoints)
|
||||||
except ValueError:
|
except ValueError:
|
||||||
pass
|
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:
|
try:
|
||||||
props.index("GZVariableTrim")
|
props.index("GZVariableTrim")
|
||||||
if self.ship.GZVariableTrim:
|
if self.ship.GZVariableTrim:
|
||||||
|
@ -336,20 +347,6 @@ class TaskPanel:
|
||||||
"Number of points",
|
"Number of points",
|
||||||
None,
|
None,
|
||||||
QtGui.QApplication.UnicodeUTF8))
|
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(
|
self.widget(QtGui.QCheckBox, "VariableTrim").setText(
|
||||||
QtGui.QApplication.translate(
|
QtGui.QApplication.translate(
|
||||||
"ship_gz",
|
"ship_gz",
|
||||||
|
@ -371,13 +368,11 @@ class TaskPanel:
|
||||||
form = mw.findChild(QtGui.QWidget, "TaskPanel")
|
form = mw.findChild(QtGui.QWidget, "TaskPanel")
|
||||||
form.angle = self.widget(QtGui.QLineEdit, "Angle")
|
form.angle = self.widget(QtGui.QLineEdit, "Angle")
|
||||||
form.n_points = self.widget(QtGui.QSpinBox, "NumPoints")
|
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.var_trim = self.widget(QtGui.QCheckBox, "VariableTrim")
|
||||||
|
|
||||||
angle = Units.Quantity(Locale.fromString(
|
angle = Units.Quantity(Locale.fromString(
|
||||||
form.angle.text())).getValueAs('deg').Value
|
form.angle.text())).getValueAs('deg').Value
|
||||||
n_points = form.n_points.value()
|
n_points = form.n_points.value()
|
||||||
var_draft = form.var_draft.isChecked()
|
|
||||||
var_trim = form.var_trim.isChecked()
|
var_trim = form.var_trim.isChecked()
|
||||||
|
|
||||||
props = self.ship.PropertiesList
|
props = self.ship.PropertiesList
|
||||||
|
@ -413,22 +408,6 @@ class TaskPanel:
|
||||||
"Ship",
|
"Ship",
|
||||||
tooltip)
|
tooltip)
|
||||||
self.ship.GZNumPoints = n_points
|
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:
|
try:
|
||||||
props.index("GZVariableTrim")
|
props.index("GZVariableTrim")
|
||||||
except ValueError:
|
except ValueError:
|
||||||
|
|
|
@ -33,16 +33,6 @@
|
||||||
</property>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</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">
|
<item row="0" column="0">
|
||||||
<widget class="QLabel" name="AngleLabel">
|
<widget class="QLabel" name="AngleLabel">
|
||||||
<property name="sizePolicy">
|
<property name="sizePolicy">
|
||||||
|
@ -59,7 +49,7 @@
|
||||||
<item row="0" column="1">
|
<item row="0" column="1">
|
||||||
<widget class="Gui::InputField" name="Angle"/>
|
<widget class="Gui::InputField" name="Angle"/>
|
||||||
</item>
|
</item>
|
||||||
<item row="3" column="0" colspan="2">
|
<item row="2" column="0" colspan="2">
|
||||||
<widget class="QCheckBox" name="VariableTrim">
|
<widget class="QCheckBox" name="VariableTrim">
|
||||||
<property name="text">
|
<property name="text">
|
||||||
<string>Variable Trim angle</string>
|
<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