Skip to content

Commit 0526c8a

Browse files
committed
Adding new capability to rotate array:
1) arrayReorientation: reorients the array such that the wind direction is always aligned with the global x-axis to facilitate extraction to FAST.Farm. 2) getRAFT function updated in Project to reinsert IDs after extracting info so that when called again, an error doesn't occur because of absence of ID key. 3) Platform setPosition function updated to only reposition/reorient anchored lines.
1 parent 3dddac7 commit 0526c8a

File tree

2 files changed

+121
-3
lines changed

2 files changed

+121
-3
lines changed

famodel/platform/platform.py

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -107,8 +107,9 @@ def setPosition(self, r, heading=None, degrees=False,project=None):
107107

108108
# Heading of the mooring line
109109
heading_i = self.mooring_headings[count] + self.phi
110-
# Reposition the whole Mooring
111-
self.attachments[att]['obj'].reposition(r_center=self.r, heading=heading_i,project=project)
110+
# Reposition the whole Mooring if it is an anchored line
111+
if not self.attachments[att]['obj'].shared:
112+
self.attachments[att]['obj'].reposition(r_center=self.r, heading=heading_i,project=project)
112113

113114
count += 1
114115

famodel/project.py

Lines changed: 118 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -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

@@ -4251,8 +4260,116 @@ def resetArrayCenter(self, FOWTOnly=True):
42514260

42524261
if 'platforms' in self.RAFTDict or 'platform' in self.RAFTDict:
42534262
self.getRAFT(self.RAFTDict,pristine=1)
4263+
self.getMoorPyArray()
4264+
4265+
def arrayReorientation(self, windHeading=None, degrees=False):
4266+
'''
4267+
Reorients the array based on a given wind heading. The array will be reoriented such that wind faces East (the zero in FFarm).
4268+
Useful to allign the array with the wind direction.
4269+
4270+
Parameters
4271+
----------
4272+
windHeading, float (optional)
4273+
The heading of the wind [deg or rad] depending on
4274+
degrees parameter. The heading is based on compass convention (North=0deg and +ve CW).
4275+
degrees : bool (optional)
4276+
Determines whether to use degree or radian for heading.
4277+
'''
4278+
4279+
from scipy.interpolate import griddata
4280+
4281+
# Check if windHeading is given
4282+
if windHeading is None:
4283+
raise ValueError("windHeading is not given. Please provide a valid wind heading.")
4284+
4285+
if degrees:
4286+
windHeading = np.radians(windHeading)
4287+
4288+
# reference wind heading (aligned with x-axis)
4289+
windHeadingRef = np.radians(270)
4290+
# Calculate the phi angle with which we will rotate the array
4291+
phi = ((np.pi/2 - windHeading) + np.pi) % (2*np.pi)
4292+
4293+
# Compute rotation matrix for faster computation
4294+
R = np.array([[np.cos(phi), np.sin(phi)],
4295+
[-np.sin(phi), np.cos(phi)]])
4296+
4297+
# Rotate the boundary
4298+
self.boundary = np.dot(R, self.boundary.T).T
4299+
4300+
# Rotate the bathymetry
4301+
X, Y = np.meshgrid(self.grid_x, self.grid_y)
4302+
coords_flat = np.stack([X.flatten(), Y.flatten()], axis=-1)
4303+
rotated_coords_flat = np.dot(R, coords_flat.T).T
4304+
rotated_X_flat, rotated_Y_flat = rotated_coords_flat[:, 0], rotated_coords_flat[:, 1]
4305+
X_rot = rotated_X_flat.reshape(X.shape)
4306+
Y_rot = rotated_Y_flat.reshape(Y.shape)
4307+
4308+
min_X = np.min(X_rot)
4309+
max_X = np.max(X_rot)
4310+
min_Y = np.min(Y_rot)
4311+
max_Y = np.max(Y_rot)
4312+
4313+
self.grid_x = np.arange(min_X, max_X, np.min(np.diff(self.grid_x)))
4314+
self.grid_y = np.arange(min_Y, max_Y, np.min(np.diff(self.grid_y)))
4315+
X_rot, Y_rot = np.meshgrid(self.grid_x, self.grid_y)
4316+
4317+
# Interpolate self.grid_depth onto the rotated grid
4318+
depth_flat = self.grid_depth.flatten() # Flatten the depth values
4319+
rotated_depth = griddata(
4320+
points=rotated_coords_flat, # Original grid points
4321+
values=depth_flat, # Original depth values
4322+
xi=(X_rot, Y_rot), # New rotated grid points
4323+
method='linear' # Interpolation method (can also use 'nearest' or 'cubic')
4324+
)
4325+
4326+
if np.isnan(rotated_depth).any():
4327+
nan_mask = np.isnan(rotated_depth)
4328+
nearest_depth = griddata(
4329+
points=rotated_coords_flat,
4330+
values=depth_flat,
4331+
xi=(X_rot, Y_rot),
4332+
method='nearest'
4333+
)
4334+
rotated_depth[nan_mask] = nearest_depth[nan_mask]
4335+
4336+
self.grid_depth = rotated_depth
4337+
4338+
# Rotate the platforms
4339+
for pf in self.platformList.values():
4340+
pf.r[:2] = np.dot(R, pf.r[:2].T).T
4341+
pf.phi = pf.phi + windHeadingRef - windHeading
4342+
4343+
4344+
# Rotate moorings
4345+
for moor in self.mooringList.values():
4346+
# if not moor.shared:
4347+
mooringHeading = np.radians(moor.heading) + windHeadingRef - windHeading
4348+
moor.reposition(heading=mooringHeading, project=self)
4349+
# else:
4350+
# mooringHeading = np.radians(obj.heading)
4351+
# moor.reposition(heading=mooringHeading)
4352+
4353+
4354+
4355+
# if isinstance(obj, Cable):
4356+
# cableHeading = [obj.subcomponents[0].headingA + phi, obj.subcomponents[-1].headingB + phi]
4357+
# obj.reposiiton(headings=cableHeading, project=self)
42544358

4359+
# Change RAFTDict if available.
4360+
if self.RAFTDict:
4361+
x_idx = self.RAFTDict['array']['keys'].index('x_location')
4362+
y_idx = self.RAFTDict['array']['keys'].index('y_location')
4363+
p_idx = self.RAFTDict['array']['keys'].index('heading_adjust')
4364+
for i, pf in enumerate(self.platformList.values()):
4365+
self.RAFTDict['array']['data'][i][x_idx] = pf.r[0]
4366+
self.RAFTDict['array']['data'][i][y_idx] = pf.r[1]
4367+
self.RAFTDict['array']['data'][i][p_idx] = np.degrees(pf.phi)
42554368

4369+
if 'platforms' in self.RAFTDict or 'platform' in self.RAFTDict:
4370+
self.getRAFT(self.RAFTDict,pristine=1)
4371+
4372+
self.getMoorPyArray()
42564373

42574374
def updateFailureProbability(self):
42584375
'''

0 commit comments

Comments
 (0)