Skip to content

Commit d8010d9

Browse files
committed
adding example for acopf
1 parent 23e7c64 commit d8010d9

3 files changed

Lines changed: 177 additions & 70 deletions

File tree

cvxpy/sandbox/clnlbeam.py

Lines changed: 45 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -1,54 +1,50 @@
1+
import cvxpy as cp
12

3+
# Problem parameters
4+
N = 100
5+
h = 1 / N
6+
alpha = 350
27

3-
import cvxpy as cp
8+
# Define variables with bounds
9+
t = cp.Variable(N+1) # -1 <= t <= 1
10+
x = cp.Variable(N+1) # -0.05 <= x <= 0.05
11+
u = cp.Variable(N+1) # unbounded
412

13+
# Define objective function
14+
# Minimize: sum of 0.5*h*(u[i+1]^2 + u[i]^2) + 0.5*alpha*h*(cos(t[i+1]) + cos(t[i]))
15+
objective_terms = []
16+
for i in range(N): # i from 0 to N-1 (Python 0-indexing)
17+
control_term = 0.5 * h * (u[i+1]**2 + u[i]**2)
18+
# Note: cos() is non-convex, this may cause solver issues
19+
trigonometric_term = 0.5 * alpha * h * (cp.cos(t[i+1]) + cp.cos(t[i]))
20+
objective_terms.append(control_term + trigonometric_term)
521

6-
def example_clnlbeam():
7-
# Problem parameters
8-
N = 1000
9-
h = 1 / N
10-
alpha = 350
11-
12-
# Define variables with bounds
13-
t = cp.Variable(N+1) # -1 <= t <= 1
14-
x = cp.Variable(N+1) # -0.05 <= x <= 0.05
15-
u = cp.Variable(N+1) # unbounded
16-
17-
# Define objective function
18-
# Minimize: sum of 0.5*h*(u[i+1]^2 + u[i]^2) + 0.5*alpha*h*(cos(t[i+1]) + cos(t[i]))
19-
objective_terms = []
20-
for i in range(N): # i from 0 to N-1 (Python 0-indexing)
21-
control_term = 0.5 * h * (u[i+1]**2 + u[i]**2)
22-
# Note: cos() is non-convex, this may cause solver issues
23-
trigonometric_term = 0.5 * alpha * h * (cp.cos(t[i+1]) + cp.cos(t[i]))
24-
objective_terms.append(control_term + trigonometric_term)
25-
26-
objective = cp.Minimize(cp.sum(objective_terms))
27-
28-
# Define constraints
29-
constraints = []
30-
31-
# Variable bounds
32-
constraints.extend([
33-
t >= -1,
34-
t <= 1,
35-
x >= -0.05,
36-
x <= 0.05
37-
])
38-
39-
# Dynamics constraints
40-
for i in range(N): # i from 0 to N-1
41-
# x[i+1] - x[i] - 0.5*h*(sin(t[i+1]) + sin(t[i])) == 0
42-
# Note: sin() is also non-convex
43-
position_constraint = (x[i+1] - x[i] -
44-
0.5 * h * (cp.sin(t[i+1]) + cp.sin(t[i])) == 0)
45-
constraints.append(position_constraint)
46-
47-
# t[i+1] - t[i] - 0.5*h*u[i+1] - 0.5*h*u[i] == 0
48-
angle_constraint = (t[i+1] - t[i] -
49-
0.5 * h * u[i+1] - 0.5 * h * u[i] == 0)
50-
constraints.append(angle_constraint)
22+
objective = cp.Minimize(cp.sum(objective_terms))
23+
24+
# Define constraints
25+
constraints = []
26+
27+
# Variable bounds
28+
constraints.extend([
29+
t >= -1,
30+
t <= 1,
31+
x >= -0.05,
32+
x <= 0.05
33+
])
34+
35+
# Dynamics constraints
36+
for i in range(N): # i from 0 to N-1
37+
# x[i+1] - x[i] - 0.5*h*(sin(t[i+1]) + sin(t[i])) == 0
38+
# Note: sin() is also non-convex
39+
position_constraint = (x[i+1] - x[i] -
40+
0.5 * h * (cp.sin(t[i+1]) + cp.sin(t[i])) == 0)
41+
constraints.append(position_constraint)
5142

52-
# Create and solve the problem
53-
problem = cp.Problem(objective, constraints)
54-
return problem
43+
# t[i+1] - t[i] - 0.5*h*u[i+1] - 0.5*h*u[i] == 0
44+
angle_constraint = (t[i+1] - t[i] -
45+
0.5 * h * u[i+1] - 0.5 * h * u[i] == 0)
46+
constraints.append(angle_constraint)
47+
48+
# Create and solve the problem
49+
problem = cp.Problem(objective, constraints)
50+
problem.solve(solver=cp.IPOPT, nlp=True, verbose=True)

cvxpy/sandbox/control_of_car.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
import cvxpy as cp
55

66

7-
def solve_car_control(x_final, L=0.1, N=5, h=0.1, gamma=10):
7+
def solve_car_control(x_final, L=0.1, N=2, h=0.1, gamma=10):
88
"""
99
Solve the nonlinear optimal control problem for car trajectory planning.
1010

cvxpy/tests/test_nlp_solvers.py

Lines changed: 131 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -36,18 +36,19 @@ def test_min(self):
3636

3737

3838
class TestExamplesIPOPT():
39-
39+
"""
40+
Nonlinear test problems taken from the IPOPT documentation and
41+
the Julia documentation: https://jump.dev/JuMP.jl/stable/tutorials/nonlinear/simple_examples/.
42+
"""
4043
def test_hs071(self):
4144
x = cp.Variable(4, bounds=[0,6])
4245
x.value = np.array([1.0, 5.0, 5.0, 1.0])
4346
objective = cp.Minimize(x[0]*x[3]*(x[0] + x[1] + x[2]) + x[2])
44-
45-
# Constraints
47+
4648
constraints = [
47-
x[0]*x[1]*x[2]*x[3] >= 25, # Product constraint
48-
cp.sum_squares(x) == 40, # Sum of squares constraint
49+
x[0]*x[1]*x[2]*x[3] >= 25,
50+
cp.sum_squares(x) == 40,
4951
]
50-
# Create problem
5152
problem = cp.Problem(objective, constraints)
5253
problem.solve(solver=cp.IPOPT, nlp=True)
5354
assert problem.status == cp.OPTIMAL
@@ -63,19 +64,23 @@ def test_portfolio_opt(self):
6364
3.120, 2.980, 1.900, 1.750, 1.800]
6465
})
6566

66-
# Compute returns
6767
returns = df.pct_change().dropna().values
6868
r = np.mean(returns, axis=0)
6969
Q = np.cov(returns.T)
7070

71-
# Single-objective optimization
72-
x = cp.Variable(3) # Non-negative weights
73-
x.value = np.array([10.0, 10.0, 10.0]) # Initial guess
71+
x = cp.Variable(3)
72+
x.value = np.array([10.0, 10.0, 10.0])
7473
variance = cp.quad_form(x, Q)
7574
expected_return = r @ x
7675

7776
problem = cp.Problem(
78-
cp.Minimize(variance),[cp.sum(x) <= 1000, expected_return >= 50, x >= 0])
77+
cp.Minimize(variance),
78+
[
79+
cp.sum(x) <= 1000,
80+
expected_return >= 50,
81+
x >= 0
82+
]
83+
)
7984
problem.solve(solver=cp.IPOPT, nlp=True)
8085
assert problem.status == cp.OPTIMAL
8186
assert np.allclose(x.value, np.array([4.97045504e+02, -9.89291685e-09, 5.02954496e+02]))
@@ -91,7 +96,6 @@ def test_mle(self):
9196
sigma.value = np.array([1.0])
9297

9398
constraints = [mu == sigma**2, sigma >= 1e-6]
94-
# Sum of squared residuals
9599
residual_sum = cp.sum_squares(data - mu)
96100
log_likelihood = (
97101
(n / 2) * cp.log(1 / (2 * np.pi * (sigma)**2))
@@ -115,28 +119,135 @@ def test_rosenbrock(self):
115119

116120
def test_qcp(self):
117121
x = cp.Variable(1)
118-
y = cp.Variable(1, bounds=[0, np.inf]) # y >= 0
119-
z = cp.Variable(1, bounds=[0, np.inf]) # z >= 0
122+
y = cp.Variable(1, bounds=[0, np.inf])
123+
z = cp.Variable(1, bounds=[0, np.inf])
120124

121125
objective = cp.Maximize(x)
122126

123127
constraints = [
124-
x + y + z == 1, # Linear equality constraint
125-
x**2 + y**2 - z**2 <= 0, # Quadratic constraint: x*x + y*y - z*z <= 0
126-
x**2 - y*z <= 0 # Quadratic constraint: x*x - y*z <= 0
128+
x + y + z == 1,
129+
x**2 + y**2 - z**2 <= 0,
130+
x**2 - y*z <= 0
127131
]
128-
# Create and solve problem
129132
problem = cp.Problem(objective, constraints)
130133
problem.solve(solver=cp.IPOPT, nlp=True)
131134
assert problem.status == cp.OPTIMAL
132135
assert np.allclose(x.value, np.array([0.32699284]))
133136
assert np.allclose(y.value, np.array([0.25706586]))
134137
assert np.allclose(z.value, np.array([0.4159413]))
135138

139+
def test_acopf(self):
140+
N = 4
141+
142+
# Conductance/susceptance components
143+
G = np.array(
144+
[
145+
[1.7647, -0.5882, 0.0, -1.1765],
146+
[-0.5882, 1.5611, -0.3846, -0.5882],
147+
[0.0, -0.3846, 1.5611, -1.1765],
148+
[-1.1765, -0.5882, -1.1765, 2.9412],
149+
]
150+
)
151+
152+
B = np.array(
153+
[
154+
[-7.0588, 2.3529, 0.0, 4.7059],
155+
[2.3529, -6.629, 1.9231, 2.3529],
156+
[0.0, 1.9231, -6.629, 4.7059],
157+
[4.7059, 2.3529, 4.7059, -11.7647],
158+
]
159+
)
160+
161+
# Assign bounds where fixings are needed
162+
v_lb = np.array([1.0, 0.0, 1.0, 0.0])
163+
v_ub = np.array([1.0, 1.5, 1.0, 1.5])
164+
165+
P_lb = np.array([-3.0, -0.3, 0.3, -0.2])
166+
P_ub = np.array([3.0, -0.3, 0.3, -0.2])
167+
168+
Q_lb = np.array([-3.0, -0.2, -3.0, -0.15])
169+
Q_ub = np.array([3.0, -0.2, 3.0, -0.15])
170+
171+
theta_lb = np.array([0.0, -np.pi / 2, -np.pi / 2, -np.pi / 2])
172+
theta_ub = np.array([0.0, np.pi / 2, np.pi / 2, np.pi / 2])
173+
174+
# Create variables with bounds
175+
P = cp.Variable(N, name="P") # Real power for buses
176+
Q = cp.Variable(N, name="Q") # Reactive power for buses
177+
v = cp.Variable(N, name="v") # Voltage magnitude at buses
178+
theta = cp.Variable(N, name="theta") # Voltage angle at buses
179+
180+
# Reshape theta to column vector for broadcasting
181+
theta_col = cp.reshape(theta, (N, 1))
182+
183+
# Create constraints list
184+
constraints = []
185+
186+
# Add bound constraints
187+
constraints += [
188+
P >= P_lb,
189+
P <= P_ub,
190+
Q >= Q_lb,
191+
Q <= Q_ub,
192+
v >= v_lb,
193+
v <= v_ub,
194+
theta >= theta_lb,
195+
theta <= theta_ub
196+
]
197+
P_balance = cp.multiply(v, (G * cp.cos(theta_col - theta_col.T) + B * cp.sin(theta_col - theta_col.T)) @ v)
198+
constraints.append(P == P_balance)
199+
200+
# Reactive power balance
201+
Q_balance = cp.multiply(v, (G * cp.sin(theta_col - theta_col.T) - B * cp.cos(theta_col - theta_col.T)) @ v)
202+
constraints.append(Q == Q_balance)
203+
204+
# Objective: minimize reactive power at buses 1 and 3 (indices 0 and 2)
205+
objective = cp.Minimize(Q[0] + Q[2])
206+
207+
# Create and solve the problem
208+
problem = cp.Problem(objective, constraints)
209+
problem.solve(solver=cp.IPOPT, nlp=True)
210+
assert problem.status == cp.OPTIMAL
211+
136212
class TestNonlinearControl():
137213

138214
def test_control_of_car(self):
139215
pass
140216

141-
def test_clnl_beam(self):
142-
pass
217+
def test_clnlbeam(self):
218+
N = 10
219+
h = 1 / N
220+
alpha = 350
221+
222+
t = cp.Variable(N+1)
223+
x = cp.Variable(N+1)
224+
u = cp.Variable(N+1)
225+
226+
objective_terms = []
227+
for i in range(N):
228+
control_term = 0.5 * h * (u[i+1]**2 + u[i]**2)
229+
trigonometric_term = 0.5 * alpha * h * (cp.cos(t[i+1]) + cp.cos(t[i]))
230+
objective_terms.append(control_term + trigonometric_term)
231+
232+
objective = cp.Minimize(cp.sum(objective_terms))
233+
234+
constraints = [
235+
t >= -1,
236+
t <= 1,
237+
x >= -0.05,
238+
x <= 0.05
239+
]
240+
241+
for i in range(N):
242+
position_constraint = (x[i+1] - x[i] -
243+
0.5 * h * (cp.sin(t[i+1]) + cp.sin(t[i])) == 0)
244+
constraints.append(position_constraint)
245+
246+
angle_constraint = (t[i+1] - t[i] -
247+
0.5 * h * u[i+1] - 0.5 * h * u[i] == 0)
248+
constraints.append(angle_constraint)
249+
250+
problem = cp.Problem(objective, constraints)
251+
problem.solve(solver=cp.IPOPT, nlp=True)
252+
assert problem.status == cp.OPTIMAL
253+
assert problem.value == 3.500e+02

0 commit comments

Comments
 (0)