I'm currently working on an algorithm that determines the cost of a wind turbine support structure. The algorithm I'm writing needs to optimize the weight of an initial input support structure so that the stress levels will not exceed but be near to the failure criteria of the properties of the materials used. Another requirement is that the natural frequency of the structure needs to be bounded between 2 values. To optimize the structure 4 variables can be altered.
Can I use a function from the Scipy.Optimize library that optimizes the weight of this structure using several design parameters, but keeps into account the natural frequency and the maximum stress value in the support structure?
The function I'm optimizing looks like this:
def func(self, x):
self.properties.D_mp = x[0] # Set a new diameter for the monopile
self.properties.Dtrat_tower = x[1] # Set a new thickness ratio for the tower
self.properties.Dtrat_tp = x[2] # Set a new thickness ratio for the transition piece
self.properties.Dtrat_mud = x[3] # Set a new thickness ratio for the mudline region of the monopile
self.UpdateAll() # Update the support structure based on the changes in variables above
eig = self.GetEigenFrequency() # Get the natural frequency
maxUtil = self.GetMaximumUtilisationFactor() # Get the maximum utilisation ratio on the structure (more than 1 means stress is higher than maximum allowed)
# Natural frequency of 0.25 and utilisation ratio of 1 are ideal
# Create some penalty...
penalty = (100000 * abs((eig - 0.25)))
penalty += (100000 * abs(maxUtil - 1))
return self.GetTotalMass() + penalty
Thanks in advance!
It is probably easiest to make this a single-value optimization problem by folding in the frequency and stress constraints as penalties to the overall fitness, something like
LOW_COST = 10.
MID_COST = 150.
HIGH_COST = 400.
def weight(a, b, c, d):
return "calculated weight of structure"
def frequency(a, b, c, d):
return "calculated resonant frequency"
def freq_penalty(freq):
# Example linear piecewise penalty function -
# increasing cost for frequencies below 205 or above 395
if freq < 205:
return MID_COST * (205 - freq)
elif freq < 395:
return 0.
else:
return MID_COST * (freq - 395)
def stress_fraction(a, b, c, d):
return "calculated stress / failure criteria"
def stress_penalty(stress_frac):
# Example linear piecewise penalty function -
# low extra cost for stress fraction below 0.85,
# high extra cost for stress fraction over 0.98
if stress_frac < 0.85:
return LOW_COST * (0.85 - stress_frac)
elif stress_frac < 0.98:
return 0.
else:
return HIGH_COST * (stress_frac - 0.98)
def overall_fitness(parameter_vector):
a, b, c, d = parameter_vector
return (
# D'oh! it took me a while to get this right -
# we want _minimum_ weight and _minimum_ penalty
# to get _maximum_ fitness.
-weight(a, b, c, d)
- freq_penalty(frequency(a, b, c, d))
- stress_penalty(stress_fraction(a, b, c, d)
)
... you will of course want to find more suitable penalty functions and play with the relative weighting, but this should give you the general idea. Then you can maximize it like
from scipy.optimize import fmin
initial_guess = [29., 45., 8., 0.06]
result = fmin(lambda x: -overall_fitness(x), initial_guess, maxfun=100000, full_output=True)
(using the lambda lets fmin (a minimizer) find a maximum value for overall_fitness).
Alternatively, fmin
allows a callback function which is applied after each iteration; you could use this to put hard limits on frequency IF you know how to tweak a,b,c,d appropriately - something like
def callback(x):
a, b, c, d = x # unpack parameter vector
freq = frequency(a, b, c, d)
if freq < 205:
# apply appropriate correction to put frequency back in bounds
return [a, b, c + 0.2, d]
elif freq < 395:
return x
else:
return [a, b, c - 0.2, d]