1

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!

hpaulj
  • 221,503
  • 14
  • 230
  • 353
Boris Mulder
  • 167
  • 1
  • 10
  • Possible duplicate of [How does Python return multiple values from a function?](https://stackoverflow.com/questions/39345995/how-does-python-return-multiple-values-from-a-function) – Beck Yang Jun 05 '17 at 13:01
  • You should include more of the `scipy.optimize` call that you intend to you use. And maybe change the subject line so people don't think it's about simply returning a tuple of values. – hpaulj Jun 05 '17 at 18:55

2 Answers2

0

You can use the leastsq function of spicy.optimize.

In my case it was to fit an exponential function with two variables :

def func_exp(p, x, z):                                     
# exponential function with multiple parameters  
    a, b, c, d, t, t2 = p[0], p[1], p[2], p[3], p[4], p[5]
    return a*np.exp(b + pow(x,c)*t + pow(z,d)*t2)

But to use the leastsq function you need to create an error function, this this one you'll optimize.

def err(p, x,z, y):                                         
# error function compare the previous to the estimate to
    return func_exp(p, x,z) - y                            
# minimise the residuals 

To use it :

p0=[1e4,-1e-3,1,1,-1e-2, -1e-6]                           
# First parameters 
pfit_fin, pcov, infodict, errmsg, success = leastsq(err, p0, args=(X,Y,Z), full_output=1, epsfcn=0.000001)

So that's return the best parameters, to generate the results :

Y_2= func_exp(pfit_fin, X,Y)        

I hope that will help you,

Chris.

Chris PERE
  • 722
  • 7
  • 13
0

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]
Hugh Bothwell
  • 55,315
  • 8
  • 84
  • 99
  • Thanks! This worked very nice. I used the fmin_l_bfgs_b function from scipy.optimize to do the optimization as I needed to set boundaries on the 4 variables. – Boris Mulder Jun 06 '17 at 06:19