Module DAVE.marine
Helper functions for Marine analysis
Main functionality:
- linearize_buoyancy
- calculate_linearized_buoyancy_props
-
carene_table
-
GZcurve_DisplacementDriven
- GZcurve_MomentDriven [TODO]
Expand source code
"""
Helper functions for Marine analysis
Main functionality:
- linearize_buoyancy
- calculate_linearized_buoyancy_props
- carene_table
- GZcurve_DisplacementDriven
- GZcurve_MomentDriven [TODO]
"""
"""
This Source Code Form is subject to the terms of the Mozilla Public
License, v. 2.0. If a copy of the MPL was not distributed with this
file, You can obtain one at http://mozilla.org/MPL/2.0/.
Ruben de Bruin - 2019
"""
from DAVE.scene import *
import matplotlib.pyplot as plt
from warnings import warn
def linearize_buoyancy(scene : Scene, node : Buoyancy = None, delta_draft=1e-3, delta_roll = 1, delta_pitch = 0.3):
"""Replaces the given Buoyancy node with a HydSpring node using even-keel (rotation = (0,0,0) ) as reference.
Args:
scene : scene
node : node to be converted, if omitted then all nodes will be converted
delta_draft: draft difference used in linearization [m]
delta_roll: roll difference used in linearization [deg]
delta_pitch pitch difference used in linearization [deg]
See Also: calculate_linearized_buoyancy_props
Returns a reference to the newly created HydSpring node. If node is not specified and multiple buoyancy nodes are
present then only a reference to the last converted node is returned
"""
if node is None:
r = None
for node in scene.nodes_of_type(Buoyancy):
r = linearize_buoyancy(scene, node, delta_draft,delta_roll,delta_pitch)
return r
props = calculate_linearized_buoyancy_props(scene, node,
delta_draft=delta_draft, delta_roll = delta_roll, delta_pitch = delta_pitch)
parent = node.parent
name = node.name
scene.delete(node) # remove buoyancy node
return scene.new_hydspring(name = name,
parent = parent,
cob = props['cob'],
BMT = props['BMT'],
BML = props['BML'],
COFX = props['COFX'] - props['cob'][0], # relative to CoB!
COFY = props['COFY'] - props['cob'][1],
kHeave=props['kHeave'],
waterline=props['waterline'],
displacement_kN=props['displacement_kN'])
def calculate_linearized_buoyancy_props(scene : Scene, node : Buoyancy, delta_draft=1e-3, delta_roll = 1, delta_pitch = 0.3):
"""Obtains the linearized buoyancy properties of a buoyant shape. The properties are derived for the current
vertical position of the parent body (draft at origin) and even-keel.
COFX, COFY and kHeave are evaluated by increasing the draft by delta_draft.
BMT, BML are evaluated by increasing the roll/pitch by delta_roll/delta_pitch [deg]. So roll to sb, pitch to bow.
Note: negative values are allowed. Zero is not allowed and will result in division by zero.
cob,"Center of buoyancy in parent axis system (m,m,m)"
BMT,Vertical distance between cob and metacenter for roll [m]
BML,Vertical distance between cob and metacenter for pitch [m]
COFX,"Horizontal x-position Center of Floatation (center of waterplane area), parent axis system [m]"
COFY,"Horizontal y-position Center of Floatation (center of waterplane area), parent axis system [m]"
kHeave,Heave stiffness in [kN/m]
waterline,Waterline-elevation relative to cob for un-stretched heave-spring. Positive if cob is below the waterline (which is where is normally is) [m]
displacement_kN,False,Displacement in [kN] when waterline is at waterline-elevation
"""
if not isinstance(node, Buoyancy):
raise ValueError(f"Node {node.name} should be a 'buoyancy' type of node but is a {type(node)}.")
s = Scene()
s.resources_paths = scene.resources_paths
a = s.new_axis(node.parent.name, fixed=True)
a.z = node.parent.global_position[2]
exec(node.give_python_code()) # place a copy of the buoyancy node in scene "s"
node = s[node.name]
# get buoyancy
s.update()
displacement_m3 = node.displacement
cob = node.cob_local
cob_global = node.cob
# increase draft
old_z = a.z
a.z = a.z - delta_draft
s.update()
new_disp = node.displacement
delta_disp = new_disp - displacement_m3
if abs(delta_disp) < 1e-6:
warn(f'Zero displacement change detected for vertical position of {a.z}m')
return None
Awl = delta_disp / delta_draft
kHeave = Awl * 9.81 * node.density
# calcualte cofx and cofy from change of cob position
# x_old * x_disp + cofx * dis_change = x_new * dis_new
COFX = (node.cob_local[0] * new_disp - cob[0] * displacement_m3) / delta_disp
COFY = (node.cob_local[1] * new_disp - cob[1] * displacement_m3) / delta_disp
a.z = old_z # restore draft
# calculate BMT and BML by rotating about cob
rot = s.new_axis('rot', position = cob_global)
a.change_parent_to(rot)
rot.rotation = (delta_roll, 0,0) # impose heel
s.update()
delta_MT = node.cob[1] - cob_global[1]
rot.rotation = (0, delta_pitch, 0) # impose trim
s.update()
delta_ML = node.cob[0] - cob_global[0]
BMT = -delta_MT / np.sin(np.deg2rad(delta_roll))
BML = delta_ML / np.sin(np.deg2rad(delta_pitch))
# assemble resuls in a dict
results = {'cob': cob,
'BMT': BMT,
'BML': BML,
'COFX' : COFX,
'COFY' : COFY,
'kHeave': kHeave,
'Awl': Awl,
'displacement_kN': displacement_m3 * 9.81 * node.density,
'displacement' : displacement_m3 ,
'waterline': -cob_global[2]}
del s
return results
def carene_table(scene, buoyancy_node, draft_min, draft_max,
stepsize=0.25,
delta_draft=1e-3, delta_roll = 1, delta_pitch = 0.3):
"""Creates a carene table for buoyancy node.
DRAFT refers the origin of the parent of the buoyancy node.
Args:
scene: reference to Scene object
buoyancy_node: reference to Buoyancy node in scene
draft_min,draft_max: Draft range
stepsize: Stepsize for drafts
delta_draft, delta_roll, delta_pitch: deltas used for derivation of linearized properties (see calculate_linearized_buoyancy_props)
Returns:
Pandas dataframe
"""
zs = np.arange(draft_min, draft_max, stepsize )
if max(zs) < draft_max:
zs = np.append(zs,draft_max)
import pandas as pd
a = []
for z in zs:
buoyancy_node.parent.z = -z
r = calculate_linearized_buoyancy_props(scene, buoyancy_node, delta_roll=delta_roll, delta_pitch=delta_pitch, delta_draft=delta_draft)
if r is None:
break
r['CoB x [m]'] = r['cob'][0]
r['CoB y [m]'] = r['cob'][1]
r['CoB z [m]'] = r['cob'][2]
del r['cob']
r['draft'] = z
a.append(r)
df = pd.DataFrame(a)
df = df.rename(columns={'draft':'Draft [m]',
'BMT': 'BM T [m]',
'BML': 'BM L [m]',
'COFX': 'CoF x [m]',
'COFY': 'CoF y [m]',
'displacement': 'Displacement [m3]',
'Awl': 'Awl [m2]'})
df = df.set_index('Draft [m]')
return df.drop(columns=['waterline', 'displacement_kN','kHeave'])
def GZcurve_DisplacementDriven(scene, vessel_node, displacement_kN=None, minimum_heel= 0, maximum_heel=90, steps=180,
teardown=True,
allow_surge=False, allow_sway=False, allow_yaw=False, allow_trim=True,
noplot = False, noshow = False,
fig = None):
"""This works for vessels without a parent.
The vessels slaved to an axis system and its heel angle is fixed and enforced. After solving statics the GZ
curve is derived from the resulting moment on the connection.
The vessels heel and yaw are fixed.
The vessel is free to heave, trim, and
Only the vessels heel angle is fixed. The vessel is free to heave and trim.
Notes:
The reported heel is relative to the initial equilibrium position of the vessel. So if the initial heel
of the vessel is not zero (vessel not even keel) then the reported heel is the heel relative to that
initial heel.
Args:
scene: the scene
vessel_node: the vessel node or vessel node name
displacement_kN: displacement to be used (default value of 1 results in moment in kN*m instead of arm in m)
minimum_heel minimum heel, begin of the curve (0)
maximum_heel maximum heel, end of the curve (90)
steps: number of steps (use un-even number to capture 0)
teardown: remove the helper elements after the calculation
[disabled] enforce_even_keel (True) Run the analysis relative to even-keel position; otherwise run relative to equilibrium position
allow_surge: (False)
allow_sway: (False)
allow_yaw: (False)
allow_trim: (True)
noplot: Do not plot results [False]
noshow: Do plot but do not do plt.show() [False]
axes: Axes instance to plot in
Returns:
dictionary with heel, moment, and GM
Also, it teardown is not selected, the DOFs for each of the displacements are stored in scene._gui_stability_dofs
This is used by the Gui to make a movie of the stability calculation
"""
# --------- verify input -----------
s = scene # lazy
doflog = []
if minimum_heel > maximum_heel:
raise ValueError('Minimum heel should be smaller than maximum heel')
# vessel_node should not have a parent
vessel = s._node_from_node_or_str(vessel_node)
if vessel.parent is not None:
raise ValueError("Vessel should not have a parent. Got {} as vessel which has parent {}.".format(vessel.name, vessel.parent.name))
# make sure that we are in equilibrium before we start
s.solve_statics()
initial_heel = vessel.heel
initial_trim = vessel.trim
no_displacement = False
if displacement_kN is None: # default value
displacement_kN = 1
if displacement_kN == 1:
no_displacement = True
if minimum_heel == maximum_heel and steps>1:
raise ValueError("Can not take multiple steps of min and max value are identical")
# --------------- store current state -------
# store current vessel props
_position = vessel.position
_rotation = vessel.rotation
_fixed = vessel.fixed
_verbose = s.verbose
s.verbose = False
# --------- construct system to impose heel ---------
# construct axis system at vessel origin
name = s.available_name_like(vessel.name + "_global_motion")
global_motion = s.new_axis(name, parent=vessel) # construct at vessel origin
global_motion.change_parent_to(None)
global_motion.fixed = (not allow_surge,
not allow_sway,
False, # heave allowed
True, True,
not allow_yaw)
trim_motion = s.new_axis(s.available_name_like(vessel.name + "_trim_motion"), parent=global_motion)
if allow_trim:
trim_motion.fixed = (True,True,True,
True,False,True) # allow for trim (rotation about y)
else:
trim_motion.set_fixed()
name = s.available_name_like(vessel.name + "_heel")
heel_node = s.new_axis(name, parent=trim_motion)
heel_node.set_fixed()
vessel.change_parent_to(heel_node)
vessel.set_fixed()
# # check initial heel
#
# if enforce_even_keel:
#
# # equilibrium_rotation = global_motion.rotation
#
# # force global motion trim and heel to be zero but maintain heading
# dx = global_motion.to_glob_direction((1,0,0))
# yaw = np.arctan2(dx[1],dx[0])
# global_motion.rotation = (0,0, np.rad2deg(yaw))
initial_rotation = global_motion.rotation
# record dofs
s._vfc.state_update()
D0 = s._vfc.get_dofs()
# ----------------- do the calcs ---------------
heel = np.linspace(minimum_heel,maximum_heel,num=steps)
moment = list()
trim = list()
for x in heel:
s._vfc.set_dofs(D0)
heel_node.rx = x
s.solve_statics(silent=True)
# for movie replay
heel_node.fixed = (True,True,True,False,True,True)
s._vfc.state_update()
dofs = s._vfc.get_dofs()
doflog.append(dofs)
heel_node.set_fixed()
moment.append(-heel_node.applied_force[3])
trim.append(trim_motion.ry)
if no_displacement:
GM = np.nan
else:
GZ = np.array(moment, dtype=float) / displacement_kN
# calculate GM, but only if zero is part of the heel curve and at least two points
if (np.max(heel)>=0 and np.min(heel)<=0 and len(heel)>1):
GMs = np.diff(GZ) / np.diff(np.deg2rad(heel))
heels = 0.5*(heel[:-1] + heel[1:])
GM = np.interp(0,heels, GMs)
else:
GM = np.nan
# restore dofs
s._vfc.set_dofs(D0)
# ----------- plot the results -----------
if not noplot:
# if np.max(np.abs(initial_rotation)) > 0.01:
# lbl = "Equilibrium heel = {:.2f} deg, trim = {:.2f} deg".format(initial_heel, initial_trim)
# else:
# lbl = None
if fig is None:
fig = plt.figure()
else:
fig.clear()
if allow_trim:
ax_gz = fig.add_subplot(2,1,1,label='gz')
ax_trim = fig.add_subplot(2,1,2,label='trim')
else:
ax_gz = fig.add_subplot(1,1,1, label='gz')
if allow_trim:
ax_trim.plot(heel + initial_heel, trim + initial_trim, color='black', marker='+')
ax_trim.set_xlabel('Heel angle [deg]')
ax_trim.set_ylabel('Solved trim angle [deg]\n(including initial trim of {:.2f} [deg])'.format(initial_trim))
ax_trim.set_title('Trim resulting from imposed heel')
ax_trim.grid()
ax_gz.set_xlabel('Heel angle [deg] including initial heel of {:.2f} deg'.format(initial_heel))
what = 'moment'
if no_displacement:
ax_gz.plot(heel + initial_heel, moment, color='black', marker='+')
ax_gz.set_ylabel('Restoring moment [kN*m]')
else:
ax_gz.plot(heel + initial_heel, GZ, color='black', marker='+')
what = 'arm'
ax_gz.set_ylabel('GZ [m]')
# plot the GM line
yy = ax_gz.get_ylim()
xmax = np.rad2deg(yy[1] / GM)
xmin = np.rad2deg(yy[0] / GM)
xmin = np.max([xmin, np.min(heel)])
xmax = np.min([xmax, np.max(heel)])
ax_gz.plot([xmin + initial_heel, xmax+ initial_heel], [np.deg2rad(xmin)*GM, np.deg2rad(xmax)*GM])
box_props = dict(boxstyle='round', facecolor='gold', alpha=1)
ax_gz.text(xmax,np.deg2rad(xmax)*GM,'GM = {:.2f}'.format(GM),horizontalalignment='left',bbox=box_props)
ax_gz.set_title('Restoring {} curve for {};\n Displacement = {} [kN]'.format(what, vessel.name, round(100*displacement_kN)/100))
ax_gz.grid('on')
# if lbl:
# plt.legend(facecolor='gold')
if not noshow:
plt.show()
# -------- clean up -----------
if teardown:
vessel.change_parent_to(None)
s.delete(global_motion)
# restore current vessel props
vessel.position = _position
vessel.rotation = _rotation
vessel.fixed = _fixed
s.solve_statics()
else:
heel_node.fixed = (True, True, True, False, True, True)
s._gui_stability_dofs = doflog
s.verbose = _verbose
# --------- collect return values --------
r = dict()
r['heel'] = heel
if allow_trim:
r['trim'] = trim
if not no_displacement:
r['GM'] = GM
r['GZ'] = moment
else:
r['moment'] = moment
return r
def GZcurve_MomentDriven():
"""Calculates the GZ curve by applying a heeling moment and calculating the resulting heel angle. This method allows for
accurate calculation of the curve up till approximately the maximum of the curve.
- Where to apply moment (poi / body)
- Where to obtain roll (body)
- Where to get displacement from
Returns:
"""
# Store the current DOFs of the model
# start with a moment (can not be obtained from the GM as more than one buoyancy object may be present)
#
Functions
def GZcurve_DisplacementDriven(scene, vessel_node, displacement_kN=None, minimum_heel=0, maximum_heel=90, steps=180, teardown=True, allow_surge=False, allow_sway=False, allow_yaw=False, allow_trim=True, noplot=False, noshow=False, fig=None)
-
This works for vessels without a parent.
The vessels slaved to an axis system and its heel angle is fixed and enforced. After solving statics the GZ curve is derived from the resulting moment on the connection.
The vessels heel and yaw are fixed. The vessel is free to heave, trim, and Only the vessels heel angle is fixed. The vessel is free to heave and trim.
Notes
The reported heel is relative to the initial equilibrium position of the vessel. So if the initial heel of the vessel is not zero (vessel not even keel) then the reported heel is the heel relative to that initial heel.
Args
scene
-
the scene
vessel_node
- the vessel node or vessel node name
displacement_kN
- displacement to be used (default value of 1 results in moment in kN*m instead of arm in m)
- minimum_heel minimum heel, begin of the curve (0)
- maximum_heel maximum heel, end of the curve (90)
steps
-
number of steps (use un-even number to capture 0)
teardown
-
remove the helper elements after the calculation
- [disabled] enforce_even_keel (True) Run the analysis relative to even-keel position; otherwise run relative to equilibrium position
allow_surge
- (False)
allow_sway
-
(False)
allow_yaw
-
(False)
allow_trim
-
(True)
noplot
-
Do not plot results [False]
noshow
-
Do plot but do not do plt.show() [False]
axes
-
Axes instance to plot in
Returns
dictionary with heel, moment, and GM
Also, it teardown is not selected, the DOFs for each
ofthe displacements are stored in scene._gui_stability_dofs
This is used by the Gui to make a movie
ofthe stability calculation
Expand source code
def GZcurve_DisplacementDriven(scene, vessel_node, displacement_kN=None, minimum_heel= 0, maximum_heel=90, steps=180, teardown=True, allow_surge=False, allow_sway=False, allow_yaw=False, allow_trim=True, noplot = False, noshow = False, fig = None): """This works for vessels without a parent. The vessels slaved to an axis system and its heel angle is fixed and enforced. After solving statics the GZ curve is derived from the resulting moment on the connection. The vessels heel and yaw are fixed. The vessel is free to heave, trim, and Only the vessels heel angle is fixed. The vessel is free to heave and trim. Notes: The reported heel is relative to the initial equilibrium position of the vessel. So if the initial heel of the vessel is not zero (vessel not even keel) then the reported heel is the heel relative to that initial heel. Args: scene: the scene vessel_node: the vessel node or vessel node name displacement_kN: displacement to be used (default value of 1 results in moment in kN*m instead of arm in m) minimum_heel minimum heel, begin of the curve (0) maximum_heel maximum heel, end of the curve (90) steps: number of steps (use un-even number to capture 0) teardown: remove the helper elements after the calculation [disabled] enforce_even_keel (True) Run the analysis relative to even-keel position; otherwise run relative to equilibrium position allow_surge: (False) allow_sway: (False) allow_yaw: (False) allow_trim: (True) noplot: Do not plot results [False] noshow: Do plot but do not do plt.show() [False] axes: Axes instance to plot in Returns: dictionary with heel, moment, and GM Also, it teardown is not selected, the DOFs for each of the displacements are stored in scene._gui_stability_dofs This is used by the Gui to make a movie of the stability calculation """ # --------- verify input ----------- s = scene # lazy doflog = [] if minimum_heel > maximum_heel: raise ValueError('Minimum heel should be smaller than maximum heel') # vessel_node should not have a parent vessel = s._node_from_node_or_str(vessel_node) if vessel.parent is not None: raise ValueError("Vessel should not have a parent. Got {} as vessel which has parent {}.".format(vessel.name, vessel.parent.name)) # make sure that we are in equilibrium before we start s.solve_statics() initial_heel = vessel.heel initial_trim = vessel.trim no_displacement = False if displacement_kN is None: # default value displacement_kN = 1 if displacement_kN == 1: no_displacement = True if minimum_heel == maximum_heel and steps>1: raise ValueError("Can not take multiple steps of min and max value are identical") # --------------- store current state ------- # store current vessel props _position = vessel.position _rotation = vessel.rotation _fixed = vessel.fixed _verbose = s.verbose s.verbose = False # --------- construct system to impose heel --------- # construct axis system at vessel origin name = s.available_name_like(vessel.name + "_global_motion") global_motion = s.new_axis(name, parent=vessel) # construct at vessel origin global_motion.change_parent_to(None) global_motion.fixed = (not allow_surge, not allow_sway, False, # heave allowed True, True, not allow_yaw) trim_motion = s.new_axis(s.available_name_like(vessel.name + "_trim_motion"), parent=global_motion) if allow_trim: trim_motion.fixed = (True,True,True, True,False,True) # allow for trim (rotation about y) else: trim_motion.set_fixed() name = s.available_name_like(vessel.name + "_heel") heel_node = s.new_axis(name, parent=trim_motion) heel_node.set_fixed() vessel.change_parent_to(heel_node) vessel.set_fixed() # # check initial heel # # if enforce_even_keel: # # # equilibrium_rotation = global_motion.rotation # # # force global motion trim and heel to be zero but maintain heading # dx = global_motion.to_glob_direction((1,0,0)) # yaw = np.arctan2(dx[1],dx[0]) # global_motion.rotation = (0,0, np.rad2deg(yaw)) initial_rotation = global_motion.rotation # record dofs s._vfc.state_update() D0 = s._vfc.get_dofs() # ----------------- do the calcs --------------- heel = np.linspace(minimum_heel,maximum_heel,num=steps) moment = list() trim = list() for x in heel: s._vfc.set_dofs(D0) heel_node.rx = x s.solve_statics(silent=True) # for movie replay heel_node.fixed = (True,True,True,False,True,True) s._vfc.state_update() dofs = s._vfc.get_dofs() doflog.append(dofs) heel_node.set_fixed() moment.append(-heel_node.applied_force[3]) trim.append(trim_motion.ry) if no_displacement: GM = np.nan else: GZ = np.array(moment, dtype=float) / displacement_kN # calculate GM, but only if zero is part of the heel curve and at least two points if (np.max(heel)>=0 and np.min(heel)<=0 and len(heel)>1): GMs = np.diff(GZ) / np.diff(np.deg2rad(heel)) heels = 0.5*(heel[:-1] + heel[1:]) GM = np.interp(0,heels, GMs) else: GM = np.nan # restore dofs s._vfc.set_dofs(D0) # ----------- plot the results ----------- if not noplot: # if np.max(np.abs(initial_rotation)) > 0.01: # lbl = "Equilibrium heel = {:.2f} deg, trim = {:.2f} deg".format(initial_heel, initial_trim) # else: # lbl = None if fig is None: fig = plt.figure() else: fig.clear() if allow_trim: ax_gz = fig.add_subplot(2,1,1,label='gz') ax_trim = fig.add_subplot(2,1,2,label='trim') else: ax_gz = fig.add_subplot(1,1,1, label='gz') if allow_trim: ax_trim.plot(heel + initial_heel, trim + initial_trim, color='black', marker='+') ax_trim.set_xlabel('Heel angle [deg]') ax_trim.set_ylabel('Solved trim angle [deg]\n(including initial trim of {:.2f} [deg])'.format(initial_trim)) ax_trim.set_title('Trim resulting from imposed heel') ax_trim.grid() ax_gz.set_xlabel('Heel angle [deg] including initial heel of {:.2f} deg'.format(initial_heel)) what = 'moment' if no_displacement: ax_gz.plot(heel + initial_heel, moment, color='black', marker='+') ax_gz.set_ylabel('Restoring moment [kN*m]') else: ax_gz.plot(heel + initial_heel, GZ, color='black', marker='+') what = 'arm' ax_gz.set_ylabel('GZ [m]') # plot the GM line yy = ax_gz.get_ylim() xmax = np.rad2deg(yy[1] / GM) xmin = np.rad2deg(yy[0] / GM) xmin = np.max([xmin, np.min(heel)]) xmax = np.min([xmax, np.max(heel)]) ax_gz.plot([xmin + initial_heel, xmax+ initial_heel], [np.deg2rad(xmin)*GM, np.deg2rad(xmax)*GM]) box_props = dict(boxstyle='round', facecolor='gold', alpha=1) ax_gz.text(xmax,np.deg2rad(xmax)*GM,'GM = {:.2f}'.format(GM),horizontalalignment='left',bbox=box_props) ax_gz.set_title('Restoring {} curve for {};\n Displacement = {} [kN]'.format(what, vessel.name, round(100*displacement_kN)/100)) ax_gz.grid('on') # if lbl: # plt.legend(facecolor='gold') if not noshow: plt.show() # -------- clean up ----------- if teardown: vessel.change_parent_to(None) s.delete(global_motion) # restore current vessel props vessel.position = _position vessel.rotation = _rotation vessel.fixed = _fixed s.solve_statics() else: heel_node.fixed = (True, True, True, False, True, True) s._gui_stability_dofs = doflog s.verbose = _verbose # --------- collect return values -------- r = dict() r['heel'] = heel if allow_trim: r['trim'] = trim if not no_displacement: r['GM'] = GM r['GZ'] = moment else: r['moment'] = moment return r
def GZcurve_MomentDriven()
-
Calculates the GZ curve by applying a heeling moment and calculating the resulting heel angle. This method allows for accurate calculation of the curve up till approximately the maximum of the curve.
- Where to apply moment (poi / body)
- Where to obtain roll (body)
- Where to get displacement from
Returns:
Expand source code
def GZcurve_MomentDriven(): """Calculates the GZ curve by applying a heeling moment and calculating the resulting heel angle. This method allows for accurate calculation of the curve up till approximately the maximum of the curve. - Where to apply moment (poi / body) - Where to obtain roll (body) - Where to get displacement from Returns: """ # Store the current DOFs of the model # start with a moment (can not be obtained from the GM as more than one buoyancy object may be present) #
def calculate_linearized_buoyancy_props(scene: Scene, node: Buoyancy, delta_draft=0.001, delta_roll=1, delta_pitch=0.3)
-
Obtains the linearized buoyancy properties of a buoyant shape. The properties are derived for the current vertical position of the parent body (draft at origin) and even-keel.
COFX, COFY and kHeave are evaluated by increasing the draft by delta_draft. BMT, BML are evaluated by increasing the roll/pitch by delta_roll/delta_pitch [deg]. So roll to sb, pitch to bow. Note: negative values are allowed. Zero is not allowed and will result in division by zero.
cob,"Center of buoyancy in parent axis system (m,m,m)" BMT,Vertical distance between cob and metacenter for roll [m] BML,Vertical distance between cob and metacenter for pitch [m] COFX,"Horizontal x-position Center of Floatation (center of waterplane area), parent axis system [m]" COFY,"Horizontal y-position Center of Floatation (center of waterplane area), parent axis system [m]" kHeave,Heave stiffness in [kN/m] waterline,Waterline-elevation relative to cob for un-stretched heave-spring. Positive if cob is below the waterline (which is where is normally is) [m] displacement_kN,False,Displacement in [kN] when waterline is at waterline-elevation
Expand source code
def calculate_linearized_buoyancy_props(scene : Scene, node : Buoyancy, delta_draft=1e-3, delta_roll = 1, delta_pitch = 0.3): """Obtains the linearized buoyancy properties of a buoyant shape. The properties are derived for the current vertical position of the parent body (draft at origin) and even-keel. COFX, COFY and kHeave are evaluated by increasing the draft by delta_draft. BMT, BML are evaluated by increasing the roll/pitch by delta_roll/delta_pitch [deg]. So roll to sb, pitch to bow. Note: negative values are allowed. Zero is not allowed and will result in division by zero. cob,"Center of buoyancy in parent axis system (m,m,m)" BMT,Vertical distance between cob and metacenter for roll [m] BML,Vertical distance between cob and metacenter for pitch [m] COFX,"Horizontal x-position Center of Floatation (center of waterplane area), parent axis system [m]" COFY,"Horizontal y-position Center of Floatation (center of waterplane area), parent axis system [m]" kHeave,Heave stiffness in [kN/m] waterline,Waterline-elevation relative to cob for un-stretched heave-spring. Positive if cob is below the waterline (which is where is normally is) [m] displacement_kN,False,Displacement in [kN] when waterline is at waterline-elevation """ if not isinstance(node, Buoyancy): raise ValueError(f"Node {node.name} should be a 'buoyancy' type of node but is a {type(node)}.") s = Scene() s.resources_paths = scene.resources_paths a = s.new_axis(node.parent.name, fixed=True) a.z = node.parent.global_position[2] exec(node.give_python_code()) # place a copy of the buoyancy node in scene "s" node = s[node.name] # get buoyancy s.update() displacement_m3 = node.displacement cob = node.cob_local cob_global = node.cob # increase draft old_z = a.z a.z = a.z - delta_draft s.update() new_disp = node.displacement delta_disp = new_disp - displacement_m3 if abs(delta_disp) < 1e-6: warn(f'Zero displacement change detected for vertical position of {a.z}m') return None Awl = delta_disp / delta_draft kHeave = Awl * 9.81 * node.density # calcualte cofx and cofy from change of cob position # x_old * x_disp + cofx * dis_change = x_new * dis_new COFX = (node.cob_local[0] * new_disp - cob[0] * displacement_m3) / delta_disp COFY = (node.cob_local[1] * new_disp - cob[1] * displacement_m3) / delta_disp a.z = old_z # restore draft # calculate BMT and BML by rotating about cob rot = s.new_axis('rot', position = cob_global) a.change_parent_to(rot) rot.rotation = (delta_roll, 0,0) # impose heel s.update() delta_MT = node.cob[1] - cob_global[1] rot.rotation = (0, delta_pitch, 0) # impose trim s.update() delta_ML = node.cob[0] - cob_global[0] BMT = -delta_MT / np.sin(np.deg2rad(delta_roll)) BML = delta_ML / np.sin(np.deg2rad(delta_pitch)) # assemble resuls in a dict results = {'cob': cob, 'BMT': BMT, 'BML': BML, 'COFX' : COFX, 'COFY' : COFY, 'kHeave': kHeave, 'Awl': Awl, 'displacement_kN': displacement_m3 * 9.81 * node.density, 'displacement' : displacement_m3 , 'waterline': -cob_global[2]} del s return results
def carene_table(scene, buoyancy_node, draft_min, draft_max, stepsize=0.25, delta_draft=0.001, delta_roll=1, delta_pitch=0.3)
-
Creates a carene table for buoyancy node.
DRAFT refers the origin of the parent of the buoyancy node.
Args
scene
- reference to Scene object
buoyancy_node
- reference to Buoyancy node in scene
- draft_min,draft_max: Draft range
stepsize
- Stepsize for drafts
delta_draft
,delta_roll
,delta_pitch
:deltas used for derivation
oflinearized properties (see calculate_linearized_buoyancy_props())
Returns
Pandas dataframe
Expand source code
def carene_table(scene, buoyancy_node, draft_min, draft_max, stepsize=0.25, delta_draft=1e-3, delta_roll = 1, delta_pitch = 0.3): """Creates a carene table for buoyancy node. DRAFT refers the origin of the parent of the buoyancy node. Args: scene: reference to Scene object buoyancy_node: reference to Buoyancy node in scene draft_min,draft_max: Draft range stepsize: Stepsize for drafts delta_draft, delta_roll, delta_pitch: deltas used for derivation of linearized properties (see calculate_linearized_buoyancy_props) Returns: Pandas dataframe """ zs = np.arange(draft_min, draft_max, stepsize ) if max(zs) < draft_max: zs = np.append(zs,draft_max) import pandas as pd a = [] for z in zs: buoyancy_node.parent.z = -z r = calculate_linearized_buoyancy_props(scene, buoyancy_node, delta_roll=delta_roll, delta_pitch=delta_pitch, delta_draft=delta_draft) if r is None: break r['CoB x [m]'] = r['cob'][0] r['CoB y [m]'] = r['cob'][1] r['CoB z [m]'] = r['cob'][2] del r['cob'] r['draft'] = z a.append(r) df = pd.DataFrame(a) df = df.rename(columns={'draft':'Draft [m]', 'BMT': 'BM T [m]', 'BML': 'BM L [m]', 'COFX': 'CoF x [m]', 'COFY': 'CoF y [m]', 'displacement': 'Displacement [m3]', 'Awl': 'Awl [m2]'}) df = df.set_index('Draft [m]') return df.drop(columns=['waterline', 'displacement_kN','kHeave'])
def linearize_buoyancy(scene: Scene, node: Buoyancy = None, delta_draft=0.001, delta_roll=1, delta_pitch=0.3)
-
Replaces the given Buoyancy node with a HydSpring node using even-keel (rotation = (0,0,0) ) as reference.
Args
scene
:scene
node
:node to be converted, if omitted then all nodes will be converted
delta_draft
- draft difference used in linearization [m]
delta_roll
- roll difference used in linearization [deg]
delta_pitch pitch difference used in linearization [deg] See Also: calculate_linearized_buoyancy_props
Returns a reference to the newly created HydSpring node. If node is not specified and multiple buoyancy nodes are present then only a reference to the last converted node is returned
Expand source code
def linearize_buoyancy(scene : Scene, node : Buoyancy = None, delta_draft=1e-3, delta_roll = 1, delta_pitch = 0.3): """Replaces the given Buoyancy node with a HydSpring node using even-keel (rotation = (0,0,0) ) as reference. Args: scene : scene node : node to be converted, if omitted then all nodes will be converted delta_draft: draft difference used in linearization [m] delta_roll: roll difference used in linearization [deg] delta_pitch pitch difference used in linearization [deg] See Also: calculate_linearized_buoyancy_props Returns a reference to the newly created HydSpring node. If node is not specified and multiple buoyancy nodes are present then only a reference to the last converted node is returned """ if node is None: r = None for node in scene.nodes_of_type(Buoyancy): r = linearize_buoyancy(scene, node, delta_draft,delta_roll,delta_pitch) return r props = calculate_linearized_buoyancy_props(scene, node, delta_draft=delta_draft, delta_roll = delta_roll, delta_pitch = delta_pitch) parent = node.parent name = node.name scene.delete(node) # remove buoyancy node return scene.new_hydspring(name = name, parent = parent, cob = props['cob'], BMT = props['BMT'], BML = props['BML'], COFX = props['COFX'] - props['cob'][0], # relative to CoB! COFY = props['COFY'] - props['cob'][1], kHeave=props['kHeave'], waterline=props['waterline'], displacement_kN=props['displacement_kN'])