From 63f976e25ae10998dcfba06f3d9db6646a27453b Mon Sep 17 00:00:00 2001 From: Jose Luis Cercos Pita Date: Mon, 18 Jan 2016 18:04:01 +0100 Subject: [PATCH] Implemented a draft of the GZ computation tool --- src/Mod/Ship/CMakeLists.txt | 1 + src/Mod/Ship/shipGZ/TaskPanel.py | 63 +++++--------- src/Mod/Ship/shipGZ/TaskPanel.ui | 12 +-- src/Mod/Ship/shipGZ/Tools.py | 143 +++++++++++++++++++++++++++++++ 4 files changed, 166 insertions(+), 53 deletions(-) create mode 100644 src/Mod/Ship/shipGZ/Tools.py diff --git a/src/Mod/Ship/CMakeLists.txt b/src/Mod/Ship/CMakeLists.txt index 10f63449c..efbc08b58 100644 --- a/src/Mod/Ship/CMakeLists.txt +++ b/src/Mod/Ship/CMakeLists.txt @@ -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}) diff --git a/src/Mod/Ship/shipGZ/TaskPanel.py b/src/Mod/Ship/shipGZ/TaskPanel.py index f54d7d428..ccc87cdfc 100644 --- a/src/Mod/Ship/shipGZ/TaskPanel.py +++ b/src/Mod/Ship/shipGZ/TaskPanel.py @@ -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: diff --git a/src/Mod/Ship/shipGZ/TaskPanel.ui b/src/Mod/Ship/shipGZ/TaskPanel.ui index 36fa605a3..b57157e5f 100644 --- a/src/Mod/Ship/shipGZ/TaskPanel.ui +++ b/src/Mod/Ship/shipGZ/TaskPanel.ui @@ -33,16 +33,6 @@ - - - - Variable Draft - - - true - - - @@ -59,7 +49,7 @@ - + Variable Trim angle diff --git a/src/Mod/Ship/shipGZ/Tools.py b/src/Mod/Ship/shipGZ/Tools.py new file mode 100644 index 000000000..b025a1800 --- /dev/null +++ b/src/Mod/Ship/shipGZ/Tools.py @@ -0,0 +1,143 @@ +#*************************************************************************** +#* * +#* Copyright (c) 2011, 2012 * +#* Jose Luis Cercos Pita * +#* * +#* 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 \ No newline at end of file