@@ -2765,7 +2765,9 @@ def getRAFT(self,RAFTDict,pristine=1):
27652765
27662766 if 'ID' in RAFTDict ['array' ]['keys' ]:
27672767 IDindex = np .where (np .array (RAFTDict ['array' ]['keys' ])== 'ID' )[0 ][0 ]
2768+ IDdata = [row [IDindex ] for row in RAFTDict ['array' ]['data' ]]
27682769 RAFTDict ['array' ]['keys' ].pop (IDindex ) # remove key for ID because this doesn't exist in RAFT array table
2770+ reinsert = True
27692771 if 'topsideID' in RAFTDict ['array' ]['keys' ]:
27702772 ts_loc = RAFTDict ['array' ]['keys' ].index ('topsideID' )
27712773 RAFTDict ['array' ]['keys' ][ts_loc ] = 'turbineID'
@@ -2789,7 +2791,8 @@ def getRAFT(self,RAFTDict,pristine=1):
27892791 if turb [tsIDindex ] > ti :
27902792 turb [tsIDindex ] -= 1
27912793
2792-
2794+
2795+
27932796 # create empty mooring dictionary
27942797 RAFTDict ['mooring' ] = {}
27952798 #RAFTDict['mooring']['currentMod']
@@ -2817,6 +2820,12 @@ def getRAFT(self,RAFTDict,pristine=1):
28172820 # connect RAFT fowt to the correct moorpy body
28182821 for i in range (0 ,len (self .platformList )): # do not include substations (these are made last)
28192822 self .array .fowtList [i ].body = self .ms .bodyList [i ]
2823+
2824+ # Reinsert 'ID' key and data back into RAFTDict
2825+ if reinsert :
2826+ RAFTDict ['array' ]['keys' ].insert (IDindex , 'ID' ) # Reinsert 'ID' key at its original position
2827+ for i , row in enumerate (RAFTDict ['array' ]['data' ]):
2828+ row .insert (IDindex , IDdata [i ]) # Reinsert 'ID' data into each row
28202829 else :
28212830 raise Exception ('Platform(s) must be specified in YAML file' )
28222831
@@ -4118,7 +4127,7 @@ def extractFarmInfo(self, cmax=5, fmax=10/6, Cmeander=1.9):
41184127 yaw_init = np .zeros ((1 , len (self .platformList .items ())))
41194128 for _ , pf in self .platformList .items ():
41204129 if pf .entity == 'FOWT' :
4121- x , y , z = pf .body . r6 [0 ], pf .body . r6 [1 ], pf .body . r6 [2 ]
4130+ x , y , z = pf .r [0 ], pf .r [1 ], pf .r [2 ]
41224131 phi_deg = np .degrees (pf .phi ) # float((90 - np.degrees(pf.phi)) % 360) # Converting FAD's rotational convention (0deg N, +ve CW) into FF's rotational convention (0deg E, +ve CCW)
41234132 phi_deg = (phi_deg + 180 ) % 360 - 180 # Shift range to -180 to 180
41244133 for att in pf .attachments .values ():
@@ -4138,7 +4147,7 @@ def extractFarmInfo(self, cmax=5, fmax=10/6, Cmeander=1.9):
41384147
41394148 return wts , yaw_init
41404149
4141- def FFarmCompatibleMDOutput (self , filename , unrotateTurbines = True , renameBody = True , removeBody = True , MDoptionsDict = {}):
4150+ def FFarmCompatibleMDOutput (self , filename , unrotateTurbines = True , renameBody = True , removeBody = True , MDoptionsDict = {}, bathymetryFile = None ):
41424151 '''
41434152 Function to create FFarm-compatible MoorDyn input file:
41444153
@@ -4209,7 +4218,22 @@ def FFarmCompatibleMDOutput(self, filename, unrotateTurbines=True, renameBody=Tr
42094218
42104219 with open (filename , 'w' ) as f :
42114220 f .writelines (newLines )
4212-
4221+
4222+ if bathymetryFile :
4223+ with open (filename , 'r' ) as f :
4224+ lines = f .readlines ()
4225+
4226+ newLines = []
4227+
4228+ for i , line in enumerate (lines ):
4229+ newLines .append (line )
4230+ if '---' in line and 'OPTIONS' in line .upper ():
4231+ newLines .append (f" { bathymetryFile } Seafloor File\n " )
4232+
4233+
4234+ with open (filename , 'w' ) as f :
4235+ f .writelines (newLines )
4236+
42134237 def resetArrayCenter (self , FOWTOnly = True ):
42144238 '''
42154239 Function to reset array center such that the farm origin is the mid-point between all FOWT platforms:
@@ -4251,7 +4275,116 @@ def resetArrayCenter(self, FOWTOnly=True):
42514275
42524276 if 'platforms' in self .RAFTDict or 'platform' in self .RAFTDict :
42534277 self .getRAFT (self .RAFTDict ,pristine = 1 )
4278+ self .getMoorPyArray ()
4279+
4280+ def reorientArray (self , windHeading = None , degrees = False ):
4281+ '''
4282+ Reorients the array based on a given wind heading. The array will be reoriented such that wind faces East (the zero in FFarm).
4283+ Useful to allign the array with the wind direction.
4284+
4285+ Parameters
4286+ ----------
4287+ windHeading, float (optional)
4288+ The heading of the wind [deg or rad] depending on
4289+ degrees parameter. The heading is based on compass convention (North=0deg and +ve CW).
4290+ degrees : bool (optional)
4291+ Determines whether to use degree or radian for heading.
4292+ '''
4293+
4294+ from scipy .interpolate import griddata
4295+
4296+ # Check if windHeading is given
4297+ if windHeading is None :
4298+ raise ValueError ("windHeading is not given. Please provide a valid wind heading." )
4299+
4300+ if degrees :
4301+ windHeading = np .radians (windHeading )
4302+
4303+ # reference wind heading (aligned with x-axis)
4304+ windHeadingRef = np .radians (270 )
4305+ # Calculate the phi angle with which we will rotate the array
4306+ phi = ((np .pi / 2 - windHeading ) + np .pi ) % (2 * np .pi )
4307+
4308+ # Compute rotation matrix for faster computation
4309+ R = np .array ([[np .cos (phi ), np .sin (phi )],
4310+ [- np .sin (phi ), np .cos (phi )]])
4311+
4312+ # Rotate the boundary
4313+ self .boundary = np .dot (R , self .boundary .T ).T
4314+
4315+ # Rotate the bathymetry
4316+ X , Y = np .meshgrid (self .grid_x , self .grid_y )
4317+ coords_flat = np .stack ([X .flatten (), Y .flatten ()], axis = - 1 )
4318+ rotated_coords_flat = np .dot (R , coords_flat .T ).T
4319+ rotated_X_flat , rotated_Y_flat = rotated_coords_flat [:, 0 ], rotated_coords_flat [:, 1 ]
4320+ X_rot = rotated_X_flat .reshape (X .shape )
4321+ Y_rot = rotated_Y_flat .reshape (Y .shape )
4322+
4323+ min_X = np .min (X_rot )
4324+ max_X = np .max (X_rot )
4325+ min_Y = np .min (Y_rot )
4326+ max_Y = np .max (Y_rot )
4327+
4328+ self .grid_x = np .arange (min_X , max_X + np .min (np .diff (self .grid_x )), np .min (np .diff (self .grid_x )))
4329+ self .grid_y = np .arange (min_Y , max_Y + np .min (np .diff (self .grid_y )), np .min (np .diff (self .grid_y )))
4330+ X_rot , Y_rot = np .meshgrid (self .grid_x , self .grid_y )
4331+
4332+ # Interpolate self.grid_depth onto the rotated grid
4333+ depth_flat = self .grid_depth .flatten () # Flatten the depth values
4334+ rotated_depth = griddata (
4335+ points = rotated_coords_flat , # Original grid points
4336+ values = depth_flat , # Original depth values
4337+ xi = (X_rot , Y_rot ), # New rotated grid points
4338+ method = 'linear' # Interpolation method (can also use 'nearest' or 'cubic')
4339+ )
4340+
4341+ if np .isnan (rotated_depth ).any ():
4342+ nan_mask = np .isnan (rotated_depth )
4343+ nearest_depth = griddata (
4344+ points = rotated_coords_flat ,
4345+ values = depth_flat ,
4346+ xi = (X_rot , Y_rot ),
4347+ method = 'nearest'
4348+ )
4349+ rotated_depth [nan_mask ] = nearest_depth [nan_mask ]
4350+
4351+ self .grid_depth = rotated_depth
4352+
4353+ # Rotate the platforms
4354+ for pf in self .platformList .values ():
4355+ pf .r [:2 ] = np .dot (R , pf .r [:2 ].T ).T
4356+ pf .phi = pf .phi + windHeadingRef - windHeading
4357+
4358+
4359+ # Rotate moorings
4360+ for moor in self .mooringList .values ():
4361+ # if not moor.shared:
4362+ mooringHeading = np .radians (moor .heading ) + windHeadingRef - windHeading
4363+ moor .reposition (heading = mooringHeading , project = self )
4364+ # else:
4365+ # mooringHeading = np.radians(obj.heading)
4366+ # moor.reposition(heading=mooringHeading)
4367+
4368+
4369+
4370+ # if isinstance(obj, Cable):
4371+ # cableHeading = [obj.subcomponents[0].headingA + phi, obj.subcomponents[-1].headingB + phi]
4372+ # obj.reposiiton(headings=cableHeading, project=self)
4373+
4374+ # Change RAFTDict if available.
4375+ if self .RAFTDict :
4376+ x_idx = self .RAFTDict ['array' ]['keys' ].index ('x_location' )
4377+ y_idx = self .RAFTDict ['array' ]['keys' ].index ('y_location' )
4378+ p_idx = self .RAFTDict ['array' ]['keys' ].index ('heading_adjust' )
4379+ for i , pf in enumerate (self .platformList .values ()):
4380+ self .RAFTDict ['array' ]['data' ][i ][x_idx ] = pf .r [0 ]
4381+ self .RAFTDict ['array' ]['data' ][i ][y_idx ] = pf .r [1 ]
4382+ self .RAFTDict ['array' ]['data' ][i ][p_idx ] = np .degrees (pf .phi )
4383+
4384+ if 'platforms' in self .RAFTDict or 'platform' in self .RAFTDict :
4385+ self .getRAFT (self .RAFTDict ,pristine = 1 )
42544386
4387+ self .getMoorPyArray ()
42554388
42564389 def updateFailureProbability (self ):
42574390 '''
0 commit comments