@@ -156,16 +156,22 @@ def __init__(self, depth, lineProps=None, **kwargs):
156156 Clump weights and buoyancy floats are not specified directly. They are both 'weights' and can have either a postitive or negative value
157157 '''
158158
159- # first set the weight, length, and diameter lists based on the allVars inputs. Don't worry about design variables yet
159+ # first set the weight, length, and diameter lists based on the allVars inputs. Don't worry about design variables yet. Create the units list too.
160+
160161 if self .shared == 1 :
161162 if self .span == 0 : raise Exception ("For shared arrangements, a span must be provided to the Mooring object." )
162163 Ws = self .allVars [0 ::3 ].tolist ()
163164 else :
164165 self .span = self .allVars [0 ]* 10 - self .rBFair [0 ] # in tens of meters
165166 Ws = self .allVars [3 ::3 ].tolist ()
167+
166168 Ls = self .allVars [1 ::3 ].tolist ()
167169 Ds = self .allVars [2 ::3 ].tolist ()
168-
170+
171+ unitPattern = ['t' , 'm' , 'mm' ]
172+ self .allVarsUnits = [unitPattern [i % 3 ] for i in range (len (self .allVars ))]
173+ if self .shared == 0 :
174+ self .allVarsUnits [0 ] = 'm'
169175 # if any of the input lengths are in ratio form, convert them to real value form
170176 # (this can currently only handle 1 ration variable per Mooring)
171177 if len (self .rInds ) > 0 :
@@ -318,7 +324,7 @@ def __init__(self, depth, lineProps=None, **kwargs):
318324
319325 # fill in the X0 value (initial design variable values) based on provided allVars and Xindices (uses first value if a DV has multiple in allVars)
320326 self .X0 = np .array ([self .allVars [self .Xindices .index (i )] for i in range (self .nX )])
321-
327+ self . X0Units = np . array ( self . allVarsUnits )[[ self . Xindices . index ( i ) for i in range ( self . nX )]] # corresponding units
322328 self .X_denorm = np .ones (self .nX ) # normalization factor for design variables
323329 self .obj_denorm = 1.0 # normalization factor for objective function
324330
@@ -340,10 +346,34 @@ def __init__(self, depth, lineProps=None, **kwargs):
340346 "max_sag" : self .con_max_sag , # a maximum for the lowest point's depth at x=x_extr_neg
341347 "max_total_length" : self .con_total_length , # a maximum line length
342348 "min_yaw_stiff" : self .con_yaw_stiffness , # a minimum yaw stiffness for the whole system about the extreme negative position
343- "max_damage" : self .con_damage , # a maximum fatigue damage for a specified mooring line (scales from provided damage from previous iteration)
344- "min_tension" : self .con_min_tension # a minimum line tension
349+ "max_damage" : self .con_damage , # a maximum fatigue damage for a specified mooring line (scales from provided damage from previous iteration)
350+ "min_tension" : self .con_min_tension , # a minimum line tension
351+ "min_angle" : self .con_min_angle , # a minimum inclination angle for the line
352+ "max_angle" : self .con_max_angle # a maximum inclination angle for the line
345353 }
346354
355+ # a hard-coded dictionary of the units associated with the constraints
356+ conUnitsDict = {"min_Kx" : "N/m" ,
357+ "max_offset" : "m" ,
358+ "min_lay_length" : "m" ,
359+ "rope_contact" : "m" ,
360+ "tension_safety_factor" : "-" ,
361+ "min_sag" : "m" ,
362+ "max_sag" : "m" ,
363+ "max_total_length" : "m" ,
364+ "min_yaw_stiff" : "Nm/deg" ,
365+ "max_damage" : "-" ,
366+ "min_tension" : "N" ,
367+ "min_angle" : "deg" ,
368+ "max_angle" : "deg"
369+ }
370+
371+ # add units to the constraints dictionary
372+ for con in self .constraints :
373+ if con ['name' ] in conUnitsDict :
374+ con ['unit' ] = conUnitsDict [con ['name' ]]
375+ else :
376+ raise Exception (f"The constraint name '{ con ['name' ]} ' is not recognized." )
347377 # set up list of active constraint functions
348378 self .conList = []
349379 self .convals = np .zeros (len (self .constraints )) # array to hold constraint values
@@ -1288,30 +1318,59 @@ def plotOptimization(self):
12881318 if len (self .log ['x' ]) == 0 :
12891319 print ("No optimization trajectory saved (log is empty). Nothing to plot." )
12901320 return
1291-
1292- fig , ax = plt .subplots (len (self .X0 )+ 1 + len (self .constraints ),1 , sharex = True , figsize = [6 ,8 ])
1293- fig .subplots_adjust (left = 0.4 )
1321+
12941322 Xs = np .array (self .log ['x' ])
12951323 Fs = np .array (self .log ['f' ])
12961324 Gs = np .array (self .log ['g' ])
12971325
1298- for i in range (len (self .X0 )):
1299- ax [i ].plot (Xs [:,i ])
1300- #ax[i].axhline(self.Xmin[i], color=[0.5,0.5,0.5], dashes=[1,1])
1301- #ax[i].axhline(self.Xmax[i], color=[0.5,0.5,0.5], dashes=[1,1])
1326+ n_dv = len (self .X0 )
1327+ n_con = len (self .constraints )
1328+ n_rows = max (n_dv , n_con , 1 )
13021329
1303- ax [len (self .X0 )].plot (Fs )
1304- ax [len (self .X0 )].set_ylabel ("cost" , rotation = 'horizontal' )
1330+ fig , axes = plt .subplots (n_rows , 3 , sharex = True , figsize = [12 , 3 * n_rows ])
1331+ fig .subplots_adjust (left = 0.1 , right = 0.95 , hspace = 0.5 , wspace = 0.4 )
1332+
1333+ if n_rows == 1 :
1334+ axes = axes [np .newaxis , :]
13051335
1306- for i , con in enumerate (self .constraints ):
1307- j = i + 1 + len (self .X0 )
1308- ax [j ].axhline (0 , color = [0.5 ,0.5 ,0.5 ])
1309- ax [j ].plot (Gs [:,i ])
1310- ax [j ].set_ylabel (f"{ con ['name' ]} ({ con ['threshold' ]} )" ,
1311- rotation = 'horizontal' , labelpad = 80 )
1336+ # --- Column 1: Design variables ---
1337+ for i in range (n_dv ):
1338+ ax = axes [i , 0 ]
1339+ ax .plot (Xs [:, i ], color = 'blue' )
1340+ ax .set_title (f"d.v.{ i + 1 } [{ self .X0Units [i ]} ]" )
1341+
1342+ # turn off unused subplots
1343+ for i in range (n_dv , n_rows ):
1344+ axes [i , 0 ].axis ("off" )
13121345
1313- ax [j ].set_xlabel ("function evaluations" )
1314-
1346+ # --- Column 2: Constraints ---
1347+ for i , con in enumerate (self .constraints ):
1348+ tol = 0.005 * (max (Gs [:, 0 ])- min (Gs [:, 0 ]))
1349+ ax = axes [i , 1 ]
1350+ ax .axhline (0 , color = [0.5 ,0.5 ,0.5 ])
1351+ color = 'green' if Gs [- 1 , i ] >= - tol else 'red'
1352+ ax .plot (Gs [:, i ], color = color )
1353+ ax .set_title (f"{ con ['name' ]} ({ con ['threshold' ]} ) [{ con ['unit' ]} ]" )
1354+
1355+ for i in range (n_con , n_rows ):
1356+ axes [i , 1 ].axis ("off" )
1357+
1358+ # --- Column 3: Cost ---
1359+ ax_cost = axes [0 , 2 ]
1360+ ax_cost .plot (Fs / 1e6 , color = 'black' )
1361+ ax_cost .set_title ('cost [$M]' )
1362+
1363+ for i in range (1 , n_rows ):
1364+ axes [i , 2 ].axis ("off" )
1365+
1366+ # x-label
1367+ for i in range (n_rows ):
1368+ for j in range (3 ):
1369+ if axes [i , j ].has_data ():
1370+ axes [i , j ].set_xlabel ("function evaluations" )
1371+
1372+ plt .tight_layout ()
1373+ plt .savefig ("/Users/ralkarem/Documents/projects/FADesign/scripts/concepts2/LineDesign_optimization_ex.pdf" , dpi = 200 )
13151374 def plotGA (self ):
13161375 '''A function dedicated to plotting relevant GA outputs'''
13171376
@@ -1802,7 +1861,14 @@ def con_max_sag(self, X, index, threshold, display=0):
18021861 a certain maximum depth.'''
18031862 return self .ss .getSag (index ) - threshold
18041863
1864+ # ----- angle constraints -----
1865+ def con_min_angle (self , X , index , threshold , display = 0 ):
1866+ '''Ensure the angle of a line section is above a minimum value.'''
1867+ return self .ss .getAng (index ) - threshold
18051868
1869+ def con_max_angle (self , X , index , threshold , display = 0 ):
1870+ '''Ensure the angle of a line section is below a maximum value.'''
1871+ return threshold - self .ss .getAng (index )
18061872
18071873 # ----- utility functions -----
18081874
0 commit comments