From b23fda610c8024a34b78786e8a01e47fd3cf8488 Mon Sep 17 00:00:00 2001 From: Yuksel-Rudy Date: Thu, 20 Feb 2025 19:55:18 -0700 Subject: [PATCH 1/6] Hybrid Systems in FAModel: 1- Upgrading Project class to include platform-type buoys that act as midconnectors in hybrid system. 2- Upgrading Mooring class to get the rFair and zFair from the objects/platform(s) they are attached to instead of relying on the values store in mooring. --- famodel/mooring/mooring.py | 13 +++++++++---- famodel/project.py | 7 ++++++- 2 files changed, 15 insertions(+), 5 deletions(-) diff --git a/famodel/mooring/mooring.py b/famodel/mooring/mooring.py index 6d0cb722..35331fbd 100644 --- a/famodel/mooring/mooring.py +++ b/famodel/mooring/mooring.py @@ -187,7 +187,7 @@ def setSectionType(self, lineType, i): def reposition(self, r_center=None, heading=None, project=None, - degrees=False, rad_fair=[], **kwargs): + degrees=False, rad_fair=[], z_fair=[], **kwargs): '''Adjusts mooring position based on changed platform location or heading. It can call a custom "adjuster" function if one is provided. Otherwise it will just update the end positions. @@ -208,6 +208,10 @@ def reposition(self, r_center=None, heading=None, project=None, fairlead radius of node connected on each end of the mooring line (list should be length 2) If not provided, the fairlead radius will be determined from the attached nodes' listed fairlead radius (or, if it's an anchor, 0) + z_fair : list, optional + fairlead depth of node connected on each end of the mooring line (list should be length 2) + If not provided, the fairlead depth will be determined from the attached nodes' listed + fairlead depths (or, if it's an anchor, 0) **kwargs : dict Additional arguments passed through to the designer function. ''' @@ -232,9 +236,10 @@ def reposition(self, r_center=None, heading=None, project=None, # create fairlead radius list for end A and end B if needed if not rad_fair: rad_fair = [self.attached_to[x].rFair if (hasattr(self.attached_to[x],'rFair') and self.attached_to[x].rFair) else 0 for x in range(2)] - + if not z_fair: + z_fair = [self.attached_to[x].zFair if (hasattr(self.attached_to[x],'zFair') and self.attached_to[x].zFair) else 0 for x in range(2)] # Set the updated end B location - self.setEndPosition(np.hstack([r_centerB + rad_fair[1]*u, self.z_fair]), 'b') + self.setEndPosition(np.hstack([r_centerB + rad_fair[1]*u, z_fair[1]]), 'b') # Run custom function to update the mooring design (and anchor position) # this would also szie the anchor maybe? @@ -242,7 +247,7 @@ def reposition(self, r_center=None, heading=None, project=None, self.adjuster(self, r_centerB, u, project=project, **kwargs) elif self.shared == 1: # set position of end A at platform end A - self.setEndPosition(np.hstack([r_centerA - rad_fair[0]*u, self.z_fair]),'a') + self.setEndPosition(np.hstack([r_centerA - rad_fair[0]*u, z_fair[0]]),'a') else: # otherwise just set the anchor position based on a set spacing (NEED TO UPDATE THE ANCHOR DEPTH AFTER!) xy_loc = r_centerB + self.rad_anch*u diff --git a/famodel/project.py b/famodel/project.py index cdd368a5..1572f47e 100644 --- a/famodel/project.py +++ b/famodel/project.py @@ -600,7 +600,8 @@ def getAnchors(lineAnch, mc=None,aNum=0): # attach turbine to platform self.platformList[arrayInfo[i]['ID']].attach(self.turbineList[turb_name]) self.platformList[arrayInfo[i]['ID']].entity = 'FOWT' - else: + elif arrayInfo[i]['turbineID'] == 0: # no TopSide (buoy) + self.platformList[arrayInfo[i]['ID']].entity = 'buoy' # for now, assume it's an OSS (To be changed!!) self.platformList[arrayInfo[i]['ID']].entity = 'OSS' @@ -747,6 +748,7 @@ def getAnchors(lineAnch, mc=None,aNum=0): ad = getAnchors(lineAnch,aNum=aNum) # call method to create dictionary #mc.z_anch = -zAnew # create anchor object + zAnew = self.getDepthAtLocation(aloc[0], aloc[1]) self.anchorList[arrayAnchor[aNum]['ID']] = Anchor(dd=ad, r=[aloc[0],aloc[1],-zAnew], aNum=aNum,id=arrayAnchor[aNum]['ID']) # attach mooring object to anchor mc.attachTo(self.anchorList[(arrayAnchor[aNum]['ID'])],end='A') @@ -2560,11 +2562,14 @@ def getMoorPyArray(self,bodyInfo=None,plt=0, pristineLines=True,cables=0): wflag = 0 # warning flag has not yet been printed (prevent multiple printings of same hydrostatics warning) for i,body in enumerate(self.platformList): # make all the bodies up front - i is index in dictionary, body is key (name of platform) PF = self.platformList[body] + dd = getattr(PF, 'dd', None) # Check if there is a design dictionary for this body # add a moorpy body at the correct location r6 = [PF.r[0],PF.r[1],0,0,0,0] # use bodyInfo dictionary to create moorpy body if given if bodyInfo: self.ms.addBody(-1,r6,m=bodyInfo[body]['m'],v=bodyInfo[body]['v'],rCG=np.array(bodyInfo[body]['rCG']),rM=np.array(bodyInfo[body]['rM']),AWP=bodyInfo[body]['AWP']) + elif dd: + self.ms.addBody(-1, r6,m=PF.dd['m'],v=PF.dd['v']) elif not bodyInfo and wflag == 0: # default to UMaine VolturnUS-S design hydrostatics info print('No hydrostatics information given, so default body hydrostatics from UMaine VolturnUS-S will be used.') wflag = 1 From 06fbdcce1ab648ba3fdbe8b90fe3ed40b12ad1c9 Mon Sep 17 00:00:00 2001 From: Yuksel-Rudy Date: Fri, 21 Feb 2025 10:48:05 -0700 Subject: [PATCH 2/6] Hybrid Systems Integration (Updated): 1- correcting the entities for buoy and SSO 2- making sure mass and volume when included in the dd to be imported properly into the moorpy system. --- famodel/project.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/famodel/project.py b/famodel/project.py index 1572f47e..bc713c4c 100644 --- a/famodel/project.py +++ b/famodel/project.py @@ -602,6 +602,7 @@ def getAnchors(lineAnch, mc=None,aNum=0): self.platformList[arrayInfo[i]['ID']].entity = 'FOWT' elif arrayInfo[i]['turbineID'] == 0: # no TopSide (buoy) self.platformList[arrayInfo[i]['ID']].entity = 'buoy' + else: # for now, assume it's an OSS (To be changed!!) self.platformList[arrayInfo[i]['ID']].entity = 'OSS' @@ -2568,7 +2569,7 @@ def getMoorPyArray(self,bodyInfo=None,plt=0, pristineLines=True,cables=0): # use bodyInfo dictionary to create moorpy body if given if bodyInfo: self.ms.addBody(-1,r6,m=bodyInfo[body]['m'],v=bodyInfo[body]['v'],rCG=np.array(bodyInfo[body]['rCG']),rM=np.array(bodyInfo[body]['rM']),AWP=bodyInfo[body]['AWP']) - elif dd: + elif 'm' in dd and 'v' in dd: self.ms.addBody(-1, r6,m=PF.dd['m'],v=PF.dd['v']) elif not bodyInfo and wflag == 0: # default to UMaine VolturnUS-S design hydrostatics info print('No hydrostatics information given, so default body hydrostatics from UMaine VolturnUS-S will be used.') From ac64077f8910f090d15d850598a11ffe3bec0475 Mon Sep 17 00:00:00 2001 From: Yuksel-Rudy Date: Fri, 21 Feb 2025 11:05:45 -0700 Subject: [PATCH 3/6] Hybrid Systems (Update 2): - turbineID < 0 for now for buoy. --- famodel/project.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/famodel/project.py b/famodel/project.py index bc713c4c..c9d94bf5 100644 --- a/famodel/project.py +++ b/famodel/project.py @@ -600,7 +600,7 @@ def getAnchors(lineAnch, mc=None,aNum=0): # attach turbine to platform self.platformList[arrayInfo[i]['ID']].attach(self.turbineList[turb_name]) self.platformList[arrayInfo[i]['ID']].entity = 'FOWT' - elif arrayInfo[i]['turbineID'] == 0: # no TopSide (buoy) + elif arrayInfo[i]['turbineID'] < 0: # (buoy) self.platformList[arrayInfo[i]['ID']].entity = 'buoy' else: # for now, assume it's an OSS (To be changed!!) From 52ed7e11fb94b63bb0f46e5bcb7752c77b30f856 Mon Sep 17 00:00:00 2001 From: Yuksel-Rudy Date: Wed, 26 Feb 2025 15:12:26 -0700 Subject: [PATCH 4/6] fixing anchor location: uncommenting mc.z_anch in loadDesign. --- famodel/project.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/famodel/project.py b/famodel/project.py index c9d94bf5..90eef46e 100644 --- a/famodel/project.py +++ b/famodel/project.py @@ -747,9 +747,9 @@ def getAnchors(lineAnch, mc=None,aNum=0): # set line anchor type and get dictionary of anchor information lineAnch = arrayAnchor[k]['type'] ad = getAnchors(lineAnch,aNum=aNum) # call method to create dictionary - #mc.z_anch = -zAnew # create anchor object zAnew = self.getDepthAtLocation(aloc[0], aloc[1]) + mc.z_anch = -zAnew self.anchorList[arrayAnchor[aNum]['ID']] = Anchor(dd=ad, r=[aloc[0],aloc[1],-zAnew], aNum=aNum,id=arrayAnchor[aNum]['ID']) # attach mooring object to anchor mc.attachTo(self.anchorList[(arrayAnchor[aNum]['ID'])],end='A') From 168c3c7f79b8e903ed46f51d364ee5e26c07586f Mon Sep 17 00:00:00 2001 From: Yuksel-Rudy Date: Wed, 26 Feb 2025 16:14:26 -0700 Subject: [PATCH 5/6] connector Type Issue: Connector type name has to be increased by 1 since the length of the connTypes is 1 lower before creating the new connector type --- famodel/project.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/famodel/project.py b/famodel/project.py index 90eef46e..3be44661 100644 --- a/famodel/project.py +++ b/famodel/project.py @@ -4100,7 +4100,7 @@ def gothroughlist(dat): # this is not an empty connector if not 'type' in conf['connectors'][i]: # make a new connector type - connTypes[str(int(len(connTypes)))] = dict(conf['connectors'][i]) + connTypes[str(int(len(connTypes))+1)] = dict(conf['connectors'][i]) ctn = str(int(len(connTypes))) # connector type name else: ctn = str(conf['connectors'][i]['type']) From 2c97e8cd577ac1266bb9fd0aa1e0d4b843399986 Mon Sep 17 00:00:00 2001 From: Yuksel-Rudy Date: Thu, 6 Mar 2025 16:26:19 -0700 Subject: [PATCH 6/6] get MoorPyArray for non-FOWT bodies: - make the buoy free to move and have a z value --- famodel/project.py | 22 +++++++++++++++------- 1 file changed, 15 insertions(+), 7 deletions(-) diff --git a/famodel/project.py b/famodel/project.py index 3be44661..c9fd08a1 100644 --- a/famodel/project.py +++ b/famodel/project.py @@ -2568,7 +2568,11 @@ def getMoorPyArray(self,bodyInfo=None,plt=0, pristineLines=True,cables=0): r6 = [PF.r[0],PF.r[1],0,0,0,0] # use bodyInfo dictionary to create moorpy body if given if bodyInfo: - self.ms.addBody(-1,r6,m=bodyInfo[body]['m'],v=bodyInfo[body]['v'],rCG=np.array(bodyInfo[body]['rCG']),rM=np.array(bodyInfo[body]['rM']),AWP=bodyInfo[body]['AWP']) + if not PF.entity=="FOWT": + r6[2] = bodyInfo[body]['rCG'][-1] + self.ms.addBody(-1,r6,m=bodyInfo[body]['m'],v=bodyInfo[body]['v'],rCG=np.array(bodyInfo[body]['rCG']),rM=np.array(bodyInfo[body]['rM']),AWP=bodyInfo[body]['AWP']) + else: + self.ms.addBody(-1,r6,m=bodyInfo[body]['m'],v=bodyInfo[body]['v'],rCG=np.array(bodyInfo[body]['rCG']),rM=np.array(bodyInfo[body]['rM']),AWP=bodyInfo[body]['AWP']) elif 'm' in dd and 'v' in dd: self.ms.addBody(-1, r6,m=PF.dd['m'],v=PF.dd['v']) elif not bodyInfo and wflag == 0: # default to UMaine VolturnUS-S design hydrostatics info @@ -3712,19 +3716,23 @@ def arrayWatchCircle(self,plot=False, ang_spacing=45, RNAheight=150, if not self.ms: self.getMoorPyArray() - + + # apply thrust force to platforms at specified angle intervals for i,ang in enumerate(angs): print('Analyzing platform offsets at angle ',ang) fx = thrust*np.cos(np.radians(ang)) fy = thrust*np.sin(np.radians(ang)) - + # add thrust force and moment to the body - for body in self.ms.bodyList: - body.f6Ext = np.array([fx, fy, 0, fy*RNAheight, fx*RNAheight, 0]) # apply an external force on the body [N] + for pf in self.platformList.values(): + if pf.entity.upper() == 'FOWT': + pf.body.f6Ext = np.array([fx, fy, 0, fy*RNAheight, fx*RNAheight, 0]) # apply an external force on the body [N] + # solve equilibrium self.ms.solveEquilibrium3(DOFtype='both') - + self.ms.plot(draw_seabed=False) + plt.show() # save info if requested if SFs: # get loads on anchors (may be shared) @@ -4129,7 +4137,7 @@ def gothroughlist(dat): if not 'type' in conf['connectors'][i+1]: # make a new connector type #conf['connectors'][i+1] = cleanDataTypes(conf['connectors'][i+1]) - connTypes[str(len(connTypes))] = conf['connectors'][i+1] + connTypes[str(len(connTypes))] = dict(conf['connectors'][i+1]) ctn = str(int(len(connTypes))) else: ctn = conf['connectors'][i+1]['type']