0

I am trying to translate a Matlab code to python, and it includes a multi-objective solver (using gamultiobj in matlab) for which I chose to use the AGEMOEA algorithm in the pymoo-package. However, my problem is that I can't seem to understand how to format the input correctly, and as a result, the pymoo-solver is not able to reshape the output.

My error is this:

     File "C:\Users\IDABUK\Anaconda3\lib\site-packages\pymoo\core\problem.py", line 288, in _format_dict
    raise Exception(
Exception: ('Problem Error: F can not be set, expected shape (200, 1) but provided (200, 1, 1, 1, 2)', ValueError('cannot reshape array of size 400 into shape (200,1)'))

And here is the code (I tried to minimize it)

Imports and defining variables

import numpy as np
import warnings
from pymoo.core.problem import Problem
from pymoo.algorithms.moo.age import AGEMOEA
from pymoo.optimize import minimize
from pymoo.problems.functional import FunctionalProblem
from pymoo.termination import get_termination
from cmath import nan
import numpy as np
from scipy.optimize import fsolve

rho = 1000
mu = 9 * 10 ** - 4
roughness = 0.2 * 0.001
D_min = 5 * 0.001
D_max = 1000 * 0.001
velocity_boundaries = np.array([0.1, 3])
m_dot_plot = np.linspace(0.1, 1, 10)
D_plot = np.array([15, 20, 30, 40, 50, 65, 80, 100, 125, 150, 200, 250,
                   300, 350, 400]) / 1000

Here is the code

def colebrook(Re=None, k=None):
    if Re <= 0 or k <= 0:
        raise Exception('inputs must be all positive values')
    else:
        if Re < 2300:
            f = 64 / Re
            message = 1
        else:
            def fun(f):
                return 1 / np.sqrt(f) + 2 * np.log10(k / 3.7 + 2.51 / (Re * np.sqrt(f)))
            f, _, flag, msg = fsolve(fun, 0.1, full_output=True)  # removed optimset('Display', 'off') #ERROR! FIXME
            # discuss solutions
            if flag <= 0:
                # refuse solution
                f = nan
                message = - 1
                # algorithm did not converge to a solution.
            else:
                # accept solution
                message = 1
    return f, message


def nlinconstraint(D=None, m_dot=None, rho=None, velocity_boundaries=None):
    v = 4 * m_dot / (np.pi * D ** 2.0 * rho)
    # constraint functions
    if True and not len(velocity_boundaries) == 0:
        if np.isscalar(velocity_boundaries[0]) and not np.isnan(velocity_boundaries[0]): #wrong with () instead of []? XXX
            #c[:, 0] = velocity_boundaries[0] - v
            c1= np.array(velocity_boundaries[0] - v)
        if np.isscalar(velocity_boundaries[1]) and not np.isnan(velocity_boundaries[1]):
            #c[:, 1] = v - velocity_boundaries[1]
            c2 = np.array(v - velocity_boundaries[1])
    #c = np.array([c1, c2])
    c = np.concatenate((c1, c2), axis= 0)
    ceq = np.array([])
    return c, ceq #remove ceq as no equalities as constraints? 


def obj_function(D=None, m_dot=None, rho=None, mu=None, roughness=None):
    # This function contains the objectives to be minimized.
    # The first objective is expressed as the pressure drop per meter of
    # pipe, i.e. [Pa/m]. The second one is the inner pipe diameter in [m]
    # To run this function, the Colebrook implicit solver is required.
    #
    #  pressure drop [Pa/m]
    # delta p     8 * m^2                        4 m         epsilon
    # -------- = ----------------- * colebrook(---------- , --------- )
    #    L        pi^2 * rho * D^5              pi * D * m       D
    #y = np.array([])  # empty array
    
    v = 4 * m_dot / (np.pi * D ** 2.0 * rho)
    Re = (np.multiply(np.multiply(rho, v), D)) / mu
    f = np.array([])
    f = colebrook(Re, roughness/D)[0] # do not include message? 
    
    delta_p_m = np.multiply(np.multiply(np.multiply((1.0/D), f) * 0.5, rho),
                            v**2)
    D = np.array(D)
    y = np.column_stack((delta_p_m, D))
    return y

def topsis(DM=None, weights=None, min_max=None):
    # This function ranks the decision matrix [m x n]
    # with n = attributes (columns)
    # and m = alternatives, configurations (rows) using the TOPSIS method.
    #
    # Inputs: - decision matrix [m x n]
    #         - weights array for the attributes [1 x m]
    #         - minimize/maximize array [1 x m]
    #             for the j-th attribute, with j = 1, 2, ... m
    #             choose +1 if the j-th attribute shall be maximized
    #                    -1 if the j-th attribute shall be minimized
    #     
    # Output: - ranked decision matrix [m x n]
    #           expressed with the "real" units.
    #
    # Pay attention, this function works if each element of the decision matrix
    # is a scalar, not an array!
    m, n = DM.shape  # fix 
    Z = np.multiply(nan, np.ones((m, n)))
    Zw = Z
    a_plus = np.multiply(nan, np.ones((1, n)))
    a_minus = np.multiply(nan, np.ones((1, n)))
    
    for j in np.arange(1, n+1).reshape(-1):  # fix 
        Z[:, j] = DM[:, j]/(np.sum(DM[:, j]**2, 1-1))**0.5
        Zw[:, j] = np.multiply(Z[:, j], weights(j))
        if min_max(j) == -1:  # is this a typo? Array index or function call?
            a_plus[j] = np.amin[Zw[:, j]]
            a_minus[j] = np.amax[Zw[:, j]]
        else:
            if min_max(j) == 1:  # same as above
                a_plus[j] = np.amax[Zw[:, j]]
                a_minus[j] = np.amin[Zw[:, j]]
    # 2-1 for indexing purposes
    Si_minus = np.sum((Zw - a_minus)**2, 2-1)**0.5
    Si_plus = np.sum((Zw - a_plus) ** 2, 2-1)**0.5
    c = Si_minus/(Si_plus + Si_minus)
    new_DM = np.array([DM, c])
    # ranked_DM = sortrows(new_DM,n + 1,'descend') #fix - matlab code
    ranked_DM = new_DM.sort(reverse=True)  # fix - include n+1 from matlab code -use argsort instead (FIXFIX) # noqa: E501
    output_DM = ranked_DM[:, np.arange(1, n+1)]
    return output_DM


def opt_pr_dia(m_dot=None, rho=None, mu=None, roughness=None,
               D_min=None, D_max=None, velocity_boundaries=None):
    # This function solves the optimization problem of the pipe optimal
    # diameter with minimal pressure drop.
    # Inputs: - mass flow rate [kg/s] (scalar)
    #         - fluid density [kg/m^3] (scalar)
    #         - pipe absolute roughness [m] (scalar)
    #         - lower boundary for the search of the pipe D [m] (scalar)
    #         - upper boundary for the search of the pipe D [m] (scalar)
    #         - velocity boundary [v_min, v_max] (1 x 2 array)
    #
    # Outputs: - T table containing all non-dominated solutions
    #          - exit flag (>0 if successful, <=0 if algorithm did not
    #            converge)
    f_array = lambda D = None: [obj_function(D,m_dot,rho,mu,roughness)]
    constr_ieq = [lambda D = None: nlinconstraint(D, m_dot, rho, velocity_boundaries)]
    n_var = 1 #number of variables

    problem = FunctionalProblem(n_var, f_array, constr_ieq=constr_ieq, xl=D_min, xu=D_max ) #BUG: gets shape of problem (200, 1, 1, 2, 1) instead of (200, 1) - ValueError cannot reshape array of size 400 into shape (200,1) - size is twice what it should be
    #problem = Problem(n_var, f_array, n_ieq_constr=constr_ieq, xl=D_min, xu=D_max)
    algorithm = AGEMOEA(pop_size= 200) #choice of algorithm with chosen population size
    termination = get_termination("n_gen", 500) # terminates after 500 generations - could also terminate due to time: ("time", "00:05:00") - 5 minutes
    res = minimize(problem, algorithm, termination, seed=1, verbose=True)

    f_values = algorithm.result() 

    exitflag =  500 -  res.algorithm.n_gen #number of iterations left

    if exitflag < 0:
        # refuse solution
        raise Exception('optimization failed')
    else:
        if exitflag == 0:
            # max number of generations reached
            # FIXME: Implement using Python logging - done(?)
            warnings.warn('max number of generations reached. ' +
                          'Try to relax variable boundaries or constraints')
        else:
            # accept solution
            print('optimization successful')
            w = np.array([0.5, 0.5])
            # rank the objectives according to the TOPSIS method
            obj = topsis(f_values, w, np.array([-1, -1]))
            # build the output table containing the designs in the pareto front
            d = obj[:, 2]
            delta_p_1m = obj[:, 1]
            v = 4 * m_dot/(np.pi * d**2.0 * rho)
            Re = np.multiply(np.multiply(rho, v), d)/mu
            delta_p_mmWC = delta_p_1m/9.81

            T = np.array(np.multiply(m_dot, np.ones((delta_p_1m.shape, # does this work?? 
                                                     delta_p_1m.shape))),
                         np.round(delta_p_1m), np.round(delta_p_mmWC),
                         np.round(d * 1000, 2), np.round(v, 2), np.round(Re))
            #FIXME??

            T.Properties.VariableNames = np.array(['m_dot', 'dp [Pa/m]', 
                                                   'dp [mm H2O/m]', 'd [mm]',
                                                   'v [m/s]', 'Re'])
    return T, exitflag


if __name__ == '__main__':
    # test the function
    m_dot = 0.5
    rho = 1000
    mu = 0.001
    roughness = 0.00005
    D_min = 0.01
    D_max = 0.05
    velocity_boundaries = np.array([0.5, 2])
    T, exitflag = opt_pr_dia(m_dot, rho, mu, roughness, D_min, D_max, velocity_boundaries)

Here is the original code in matlab: https://github.com/giuliots95/Optimize_pipe_diameter_and_pressure_drop

Adriaan
  • 17,741
  • 7
  • 42
  • 75
IdaSB
  • 3
  • 2

0 Answers0