Breakwater blocks#
Click –> Live Code to activate live coding on this page!
Problem#
It is desired to design the cheapest breakwater block (box-shaped). The price of the brick will depend only on the amount of used material. It is required that each face has a minimum surface of \(0.8\) \(\text{m}^2\). Also, for stability reasons, the block weight has to be larger than \(3000\) \(\text{kg}\). Let’s assume concrete density of \(2500\) \(\text{kg}/{m}^2\).
Model#
We need to define our model in the form of a nonlinear constrained optimization model (3.1) to apply scipy.optimize.minimize
.
We’ll define the model as follows:
Design variables: width, height and depth of a block
Objective function: minimum volume of the block
Inequality constraint functions: minimum surface area of each face of \(0.8\) \(\text{m}^2\) and maximum weight of \(3000\) \(\text{kg}\)
Equality constraint functions: none
Bounds: positive dimensions
Design variables#
Let’s start with our design variables. In this case a logical choice could be the width, height and depth of our block
Objective function#
Now we can define the objective function as the product of the dimension to represent \(\mathop {\min }\limits_x f\left(x\right) \) in (3.1):
Inequality constraints#
Let’s continue with the inequality constraints, which should deal with the required positive dimensions, minimum surface area of each face of \(0.8\) \(\text{m}^2\) and maximum weight of \(3000\) \(\text{kg}\). These can be defined in the form \({{g}}\left(x_{ij}\right) \le 0\) as:
Bounds#
The dimensions of the block cannot be negative. Therefore, the bounds can be defined as:
Find best solution manually#
Try and adjust the values for \(x_1\), \(x_2\) and \(x_3\). Can you find the optimal solution?
Method#
Now let’s solve this problem using an optimization method.
Import libraries#
import scipy as sp
import numpy as np
import scipy as sp
import numpy as np
Define variables#
As before, we don’t need to specify our variable \(x\) itself as defined in (3.2). However, this optimization method requires an initial guess. An arbitrary value is chosen here:
x0 = np.array([5,0,1])
Define objective function#
The objective function was defined in (3.3), which gives:
def func(x):
vol = x[0]*x[1]*x[2]
return vol
Define constrain functions#
The constraint functions were defined in (3.4). We had no equality constraints. Unlike before with the method to solve linear constrained problem, we need an object which defines the upper and lower bounds. As this problem has only an upper bound of \(0\), the lower bound is set to \(\infty\) which is np.inf
in python. Note that a single constraint object can include multiple constraints.
def nonlinconfun(x):
c1 = 0.8 - x[0]*x[1]
c2 = 0.8 - x[0]*x[2]
c3 = 0.8 - x[1]*x[2]
c4 = 3000 - 2500 * x[0] * x[1] * x[2]
return np.array([c1,c2,c3,c4])
cons = sp.optimize.NonlinearConstraint(nonlinconfun, np.array([-np.inf,-np.inf,-np.inf,-np.inf]), np.array([0,0,0,0]))
Define bounds#
The bounds were defined in (3.5) and result in:
bounds = [[0, None],
[0, None],
[0, None]]
Solve the problem#
Now let’s solve the problem. The cons
object can be added directly, in the case of equality constraints as well you can define a list of constrainer objects as an input.
result = sp.optimize.minimize(fun = func,x0 = x0,bounds = bounds,constraints=cons)
print(result)
message: Optimization terminated successfully
success: True
status: 0
fun: 1.2000000000024624
x: [ 1.353e+00 1.488e+00 5.962e-01]
nit: 10
jac: [ 8.871e-01 8.065e-01 2.013e+00]
nfev: 41
njev: 10
Exercise#
Click –> Live Code to activate live coding on this page.