Skip to content

Commit

Permalink
Merge pull request #287 from WISDEM/moorpy_update
Browse files Browse the repository at this point in the history
Moorpy update
  • Loading branch information
ptrbortolotti committed Jun 14, 2021
2 parents c8ea1f4 + b1cb753 commit 9438c25
Show file tree
Hide file tree
Showing 25 changed files with 560 additions and 281 deletions.
10 changes: 2 additions & 8 deletions examples/09_floating/IEA-15-240-RWT_VolturnUS-S.yaml
Expand Up @@ -729,21 +729,15 @@ components:
line_types:
- name: main
diameter: 0.185
mass_density: 685.0
stiffness: 3270e6
breaking_load: 1e8
cost: 100.0
type: chain
transverse_added_mass: 1.0
tangential_added_mass: 0.0
transverse_drag: 1.6
tangential_drag: 0.1

anchor_types:
- name: drag_embedment
mass: 1e3
cost: 1e4
max_vertical_load: 0.0
max_lateral_load: 1e5
type: drag_embedment

airfoils:
- name: circular
Expand Down
10 changes: 2 additions & 8 deletions examples/09_floating/nrel5mw-semi_oc4.yaml
Expand Up @@ -634,21 +634,15 @@ components:
line_types:
- name: main
diameter: 0.0766
mass_density: 113.35
stiffness: 7.536e8
breaking_load: 1e8
cost: 100.0
type: chain
transverse_added_mass: 0.8
tangential_added_mass: 0.25
transverse_drag: 2.0
tangential_drag: 0.4

anchor_types:
- name: drag_embedment
mass: 1e3
cost: 1e4
max_vertical_load: 0.0
max_lateral_load: 1e5
type: drag_embedment

airfoils:
- aerodynamic_center: 0.275
Expand Down
21 changes: 9 additions & 12 deletions examples/09_floating/nrel5mw-spar_oc3.yaml
Expand Up @@ -405,21 +405,15 @@ components:
line_types:
- name: main
diameter: 0.09
mass_density: 77.7066
stiffness: 384.243e6
breaking_load: 1e8
cost: 100.0
type: chain
transverse_added_mass: 1.0
tangential_added_mass: 0.0
transverse_drag: 1.6
tangential_drag: 0.1

anchor_types:
- name: drag_embedment
mass: 1e3
cost: 1e4
max_vertical_load: 0.0
max_lateral_load: 1e5
type: drag_embedment

airfoils:
- aerodynamic_center: 0.275
Expand Down Expand Up @@ -709,11 +703,14 @@ control:
maxOmega: 1.26711
pitch:
PC_zeta: 0.7 # Pitch controller desired damping ratio [-]
PC_omega: 0.5 # Pitch controller desired natural frequency [rad/s]
ps_percent: 0.8 # Percent peak shaving [%, <= 1 ], {default = 80%}
PC_omega: 0.35 # Pitch controller desired natural frequency [rad/s]
ps_percent: 0.9 # Percent peak shaving [%, <= 1 ], {default = 80%}
max_pitch: 1.57 # Maximum pitch angle [rad], {default = 90 degrees}
max_pitch_rate: 0.1745
min_pitch: 0 # Minimum pitch angle [rad], {default = 0 degrees}
fl_feedback:
twr_freq: 100 # Frequency of notch in floating feedback (F_NotchCornerFreq)
ptfm_freq: .2 # Frequency of LPF in floating feedback (F_FlCornerFreq)
torque:
control_type: tsr_tracking
tsr: 7.01754386
Expand All @@ -738,8 +735,8 @@ environment:
water_density: 1025.0
water_dyn_viscosity: 1.3351e-3
water_depth: 320.0
significant_wave_height: 9.14
significant_wave_period: 13.6
significant_wave_height: 6.0
significant_wave_period: 10.0
soil_shear_modulus: 140.e+6
soil_poisson: 0.4

Expand Down
2 changes: 2 additions & 0 deletions examples/09_floating/tlp_example.py
Expand Up @@ -41,6 +41,8 @@
opt["mooring"] = {}
opt["mooring"]["n_attach"] = 3
opt["mooring"]["n_anchors"] = 3
opt["mooring"]["line_anchor"] = ["custom"] * 3
opt["mooring"]["line_material"] = ["custom"] * 3
opt["materials"] = {}
opt["materials"]["n_mat"] = 2

Expand Down
58 changes: 33 additions & 25 deletions wisdem/commonse/frustum.py
Expand Up @@ -34,7 +34,7 @@ def frustum(Db, Dt, H):
return vol, cm


def frustumVol(rb, rt, h, diamFlag=False):
def frustumVol(rb_0, rt_0, h, diamFlag=False):
"""This function returns a frustum's volume with radii or diameter inputs.
INPUTS:
Expand All @@ -51,12 +51,13 @@ def frustumVol(rb, rt, h, diamFlag=False):
"""
if diamFlag:
# Convert diameters to radii
rb *= 0.5
rt *= 0.5
rb, rt = 0.5 * rb_0, 0.5 * rt_0
else:
rb, rt = rb_0, rt_0
return np.pi * (h / 3.0) * (rb * rb + rt * rt + rb * rt)


def frustumCG(rb, rt, h, diamFlag=False):
def frustumCG(rb_0, rt_0, h, diamFlag=False):
"""This function returns a frustum's center of mass/gravity (centroid) with radii or diameter inputs.
NOTE: This is for a SOLID frustum, not a shell
Expand All @@ -74,12 +75,13 @@ def frustumCG(rb, rt, h, diamFlag=False):
"""
if diamFlag:
# Convert diameters to radii
rb *= 0.5
rt *= 0.5
rb, rt = 0.5 * rb_0, 0.5 * rt_0
else:
rb, rt = rb_0, rt_0
return 0.25 * h * (rb ** 2 + 2.0 * rb * rt + 3.0 * rt ** 2) / (rb ** 2 + rb * rt + rt ** 2)


def frustumIzz(rb, rt, h, diamFlag=False):
def frustumIzz(rb_0, rt_0, h, diamFlag=False):
"""This function returns a frustum's mass-moment of inertia (divided by density) about the
central (axial) z-axis with radii or diameter inputs.
NOTE: This is for a SOLID frustum, not a shell
Expand All @@ -98,15 +100,16 @@ def frustumIzz(rb, rt, h, diamFlag=False):
"""
if diamFlag:
# Convert diameters to radii
rb *= 0.5
rt *= 0.5
rb, rt = 0.5 * rb_0, 0.5 * rt_0
else:
rb, rt = rb_0, rt_0
# Integrate 2*pi*r*r^2 dr dz from r=0 to r(z), z=0 to h
# Also equals 0.3*Vol * (rt**5.0 - rb**5.0) / (rt**3.0 - rb**3.0)
# Also equals (0.1*np.pi*h * (rt**5.0 - rb**5.0) / (rt - rb) )
return 0.1 * np.pi * h * (rt ** 4.0 + rb * rt ** 3 + rb ** 2 * rt ** 2 + rb ** 3 * rt + rb ** 4.0)


def frustumIxx(rb, rt, h, diamFlag=False):
def frustumIxx(rb_0, rt_0, h, diamFlag=False):
"""This function returns a frustum's mass-moment of inertia (divided by density) about the
transverse x/y-axis passing through the center of mass with radii or diameter inputs.
NOTE: This is for a SOLID frustum, not a shell
Expand All @@ -125,10 +128,11 @@ def frustumIxx(rb, rt, h, diamFlag=False):
"""
if diamFlag:
# Convert diameters to radii
rb *= 0.5
rt *= 0.5
rb, rt = 0.5 * rb_0, 0.5 * rt_0
else:
rb, rt = rb_0, rt_0
# Integrate pi*r(z)^4/4 + pi*r(z)^2*(z-z_cg)^2 dz from z=0 to h
A = 0.5 * frustumIzz(rb, rt, h)
A = 0.5 * frustumIzz(rb_0, rt_0, h)
B = (
np.pi
* h ** 3
Expand All @@ -141,7 +145,7 @@ def frustumIxx(rb, rt, h, diamFlag=False):
return A + B


def frustumShellVol(rb, rt, t, h, diamFlag=False):
def frustumShellVol(rb_0, rt_0, t, h, diamFlag=False):
"""This function returns a frustum shell's volume (for computing mass with density) with radii or diameter inputs.
NOTE: This is for a frustum SHELL, not a solid
Expand All @@ -160,8 +164,9 @@ def frustumShellVol(rb, rt, t, h, diamFlag=False):
"""
if diamFlag:
# Convert diameters to radii
rb *= 0.5
rt *= 0.5
rb, rt = 0.5 * rb_0, 0.5 * rt_0
else:
rb, rt = rb_0, rt_0
# Integrate 2*pi*r*dr*dz from r=ri(z) to ro(z), z=0 to h
rb_o = rb
rb_i = rb - t
Expand All @@ -171,7 +176,7 @@ def frustumShellVol(rb, rt, t, h, diamFlag=False):
return frustumVol(rb_o, rt_o, h) - frustumVol(rb_i, rt_i, h)


def frustumShellCG(rb, rt, t, h, diamFlag=False):
def frustumShellCG(rb_0, rt_0, t, h, diamFlag=False):
"""This function returns a frustum's center of mass/gravity (centroid) with radii or diameter inputs.
NOTE: This is for a frustum SHELL, not a solid
Expand All @@ -190,8 +195,9 @@ def frustumShellCG(rb, rt, t, h, diamFlag=False):
"""
if diamFlag:
# Convert diameters to radii
rb *= 0.5
rt *= 0.5
rb, rt = 0.5 * rb_0, 0.5 * rt_0
else:
rb, rt = rb_0, rt_0
# Integrate 2*pi*r*z*dr*dz/V from r=ri(z) to ro(z), z=0 to h
rb_o = rb
rb_i = rb - t
Expand All @@ -202,7 +208,7 @@ def frustumShellCG(rb, rt, t, h, diamFlag=False):
return h * A / 4.0 / B


def frustumShellIzz(rb, rt, t, h, diamFlag=False):
def frustumShellIzz(rb_0, rt_0, t, h, diamFlag=False):
"""This function returns a frustum's mass-moment of inertia (divided by density) about the
central (axial) z-axis with radii or diameter inputs.
NOTE: This is for a frustum SHELL, not a solid
Expand All @@ -222,8 +228,9 @@ def frustumShellIzz(rb, rt, t, h, diamFlag=False):
"""
if diamFlag:
# Convert diameters to radii
rb *= 0.5
rt *= 0.5
rb, rt = 0.5 * rb_0, 0.5 * rt_0
else:
rb, rt = rb_0, rt_0
# Integrate 2*pi*r*dr*dz from r=ri(z) to ro(z), z=0 to h
rb_o = rb
rb_i = rb - t
Expand All @@ -232,7 +239,7 @@ def frustumShellIzz(rb, rt, t, h, diamFlag=False):
return frustumIzz(rb_o, rt_o, h) - frustumIzz(rb_i, rt_i, h)


def frustumShellIxx(rb, rt, t, h, diamFlag=False):
def frustumShellIxx(rb_0, rt_0, t, h, diamFlag=False):
"""This function returns a frustum's mass-moment of inertia (divided by density) about the
transverse x/y-axis passing through the center of mass with radii or diameter inputs.
NOTE: This is for a frustum SHELL, not a solid
Expand All @@ -252,8 +259,9 @@ def frustumShellIxx(rb, rt, t, h, diamFlag=False):
"""
if diamFlag:
# Convert diameters to radii
rb *= 0.5
rt *= 0.5
rb, rt = 0.5 * rb_0, 0.5 * rt_0
else:
rb, rt = rb_0, rt_0
# Integrate 2*pi*r*dr*dz from r=ri(z) to ro(z), z=0 to h
rb_o = rb
rb_i = rb - t
Expand Down
5 changes: 0 additions & 5 deletions wisdem/floatingse/floating.py
Expand Up @@ -110,8 +110,3 @@ def setup(self):
for k in range(n_member):
for var in mem_vars:
self.connect(f"member{k}." + var, f"member{k}:" + var)

"""
self.connect("max_offset_restoring_force", "mooring_surge_restoring_force")
self.connect("operational_heel_restoring_force", "mooring_pitch_restoring_force")
"""
40 changes: 35 additions & 5 deletions wisdem/floatingse/floating_frame.py
Expand Up @@ -470,6 +470,7 @@ def setup(self):
self.add_output("system_center_of_mass", np.zeros(3), units="m")
self.add_output("system_mass", 0.0, units="kg")
self.add_output("variable_ballast_mass", 0.0, units="kg")
self.add_output("variable_center_of_mass", val=np.zeros(3), units="m")
self.add_output("constr_variable_margin", val=0.0)
self.add_output("member_variable_volume", val=np.zeros(n_member), units="m**3")
self.add_output("member_variable_height", val=np.zeros(n_member))
Expand Down Expand Up @@ -619,6 +620,7 @@ def compute(self, inputs, outputs):
outputs["member_variable_height"][k] = s_end - spts[0]

cg_variable = np.dot(V_variable_member, cg_variable_member) / V_variable
outputs["variable_center_of_mass"] = cg_variable

# Now find total system mass
outputs["system_mass"] = m_sys + m_variable
Expand All @@ -643,6 +645,9 @@ def setup(self):

self.add_input("platform_mass", 0.0, units="kg")
self.add_input("platform_center_of_mass", np.zeros(3), units="m")
self.add_input("platform_added_mass", np.zeros(6), units="kg")
self.add_input("platform_center_of_buoyancy", np.zeros(3), units="m")
self.add_input("platform_displacement", 0.0, units="m**3")

self.add_input("tower_nodes", NULL * np.ones((MEMMAX, 3)), units="m")
self.add_input("tower_Fnode", NULL * np.ones((MEMMAX, 3)), units="N")
Expand Down Expand Up @@ -702,6 +707,9 @@ def setup(self):
self.add_input("rna_I", np.zeros(6), units="kg*m**2")
self.add_input("mooring_neutral_load", np.zeros((n_attach, 3)), units="N")
self.add_input("mooring_fairlead_joints", np.zeros((n_attach, 3)), units="m")
self.add_input("mooring_stiffness", np.zeros((6, 6)), units="N/m")
self.add_input("variable_ballast_mass", 0.0, units="kg")
self.add_input("variable_center_of_mass", val=np.zeros(3), units="m")

NFREQ2 = int(NFREQ / 2)
self.add_output("tower_freqs", val=np.zeros(NFREQ), units="Hz")
Expand Down Expand Up @@ -737,11 +745,16 @@ def compute(self, inputs, outputs):
n_attach = opt["mooring"]["n_attach"]
m_rna = float(inputs["rna_mass"])
cg_rna = inputs["rna_cg"]
cb = inputs["platform_center_of_buoyancy"]
V_total = inputs["platform_displacement"]
I_rna = inputs["rna_I"]
I_trans = inputs["transition_piece_I"]
m_variable = float(inputs["variable_ballast_mass"])
cg_variable = inputs["variable_center_of_mass"]

fairlead_joints = inputs["mooring_fairlead_joints"]
mooringF = inputs["mooring_neutral_load"]
mooringK = np.abs(np.diag(inputs["mooring_stiffness"]))

# Create frame3dd instance: nodes, elements, reactions, and options
for frame in ["tower", "system"]:
Expand Down Expand Up @@ -776,9 +789,20 @@ def compute(self, inputs, outputs):
ielem = np.arange(nelem) + 1
elem_obj = pyframe3dd.ElementData(ielem, N1 + 1, N2 + 1, A, Asx, Asy, Izz, Ixx, Iyy, E, G, roll, rho)

# TODO: Hydro_K + Mooring_K for tower (system too?)
rid = np.array([itrans]) # np.array([np.argmin(nodes[:, 2])])
Rx = Ry = Rz = Rxx = Ryy = Rzz = np.array([RIGID])
# Use Mooring stiffness (TODO Hydro_K too)
if frame == "tower":
rid = np.array([itrans]) # np.array([np.argmin(nodes[:, 2])])
else:
ind = []
for k in range(n_attach):
ind.append(util.closest_node(nodes, fairlead_joints[k, :]))
rid = np.array([ind]) # np.array([np.argmin(nodes[:, 2])])

Rx = Ry = Rz = Rxx = Ryy = Rzz = RIGID * np.ones(rid.size)
# Rx, Ry, Rz = [mooringK[0]], [mooringK[1]], [mooringK[2]]
# Only this solution works and there isn't much different with fully rigid
# Rx, Ry, Rz = [RIGID], [RIGID], [mooringK[2]]
# Rxx, Ryy, Rzz = [RIGID], [RIGID], [RIGID]
react_obj = pyframe3dd.ReactionData(rid + 1, Rx, Ry, Rz, Rxx, Ryy, Rzz, rigid=RIGID)

frame3dd_opt = opt["WISDEM"]["FloatingSE"]["frame3dd"]
Expand All @@ -789,10 +813,11 @@ def compute(self, inputs, outputs):
# Added mass
m_trans = float(inputs["transition_piece_mass"])
if frame == "tower":
# TODO: Added mass and stiffness
m_trans += float(inputs["platform_mass"])
m_trans += float(inputs["platform_mass"]) + inputs["platform_added_mass"][0] + m_variable
cg_trans = inputs["transition_node"] - inputs["platform_center_of_mass"]
I_trans[:3] += inputs["platform_added_mass"][3:]
else:
m_trans += m_variable
cg_trans = np.zeros(3)
add_gravity = True
mID = np.array([itrans, ihub], dtype=np.int_).flatten()
Expand Down Expand Up @@ -830,6 +855,11 @@ def compute(self, inputs, outputs):
for k in range(n_attach):
ind = util.closest_node(nodes, fairlead_joints[k, :])
Fnode[ind, :] += mooringF[k, :]
else:
# Combine all buoyancy forces into one
ind = util.closest_node(nodes, cb)
Fnode[ind, -1] += V_total * 1025 * gravity

Fnode[ihub, :] += inputs["rna_F"]
Mnode[ihub, :] += inputs["rna_M"]
nF = np.where(np.abs(Fnode).sum(axis=1) > 0.0)[0]
Expand Down
4 changes: 3 additions & 1 deletion wisdem/floatingse/member.py
Expand Up @@ -1651,7 +1651,9 @@ def setup(self):
)

self.add_subsystem(
"gc", GeometricConstraints(nPoints=n_height, diamFlag=True), promotes=["constr_taper", "constr_d_to_t"]
"gc",
GeometricConstraints(nPoints=n_height, diamFlag=True),
promotes=["constr_taper", "constr_d_to_t", "slope"],
)
self.connect("outer_diameter", "gc.d")
self.connect("wall_thickness", "gc.t")
Expand Down

0 comments on commit 9438c25

Please sign in to comment.