NumOpt 0.0.4__tar.gz → 0.0.6__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {numopt-0.0.4 → numopt-0.0.6}/NumOpt/__init__.py +1 -1
- numopt-0.0.6/NumOpt/airfoil/bspline.py +201 -0
- numopt-0.0.6/NumOpt/airfoil/export_cst2nx.py +92 -0
- numopt-0.0.6/NumOpt/airfoil/kulfan.py +163 -0
- numopt-0.0.6/NumOpt/casadi_callback/surrogate.py +181 -0
- numopt-0.0.6/NumOpt/casadi_callback/surrogate_with_hess.py +169 -0
- {numopt-0.0.4 → numopt-0.0.6}/NumOpt/cprint.py +11 -1
- numopt-0.0.6/NumOpt/nn/bnn.py +135 -0
- numopt-0.0.6/NumOpt/nx/nxcst.py +99 -0
- {numopt-0.0.4 → numopt-0.0.6}/NumOpt/nx/tools.py +2 -3
- {numopt-0.0.4 → numopt-0.0.6}/NumOpt/opti.py +8 -4
- numopt-0.0.6/NumOpt/optimize/v1.py +231 -0
- numopt-0.0.6/NumOpt/preprocess/scaler.py +82 -0
- {numopt-0.0.4 → numopt-0.0.6}/NumOpt.egg-info/PKG-INFO +1 -1
- {numopt-0.0.4 → numopt-0.0.6}/NumOpt.egg-info/SOURCES.txt +8 -0
- {numopt-0.0.4 → numopt-0.0.6}/PKG-INFO +1 -1
- numopt-0.0.6/test/test01.py +920 -0
- numopt-0.0.4/NumOpt/preprocess/scaler.py +0 -31
- numopt-0.0.4/test/test01.py +0 -146
- {numopt-0.0.4 → numopt-0.0.6}/LICENSE.txt +0 -0
- {numopt-0.0.4 → numopt-0.0.6}/NumOpt/FSI/__init__.py +0 -0
- {numopt-0.0.4 → numopt-0.0.6}/NumOpt/FSI/tools.py +0 -0
- {numopt-0.0.4 → numopt-0.0.6}/NumOpt/airfoil/bezier.py +0 -0
- {numopt-0.0.4 → numopt-0.0.6}/NumOpt/nx/__init__.py +0 -0
- {numopt-0.0.4 → numopt-0.0.6}/NumOpt/optimize/__init__.py +0 -0
- {numopt-0.0.4 → numopt-0.0.6}/NumOpt/optimize/hxy_optimization.py +0 -0
- {numopt-0.0.4 → numopt-0.0.6}/NumOpt.egg-info/dependency_links.txt +0 -0
- {numopt-0.0.4 → numopt-0.0.6}/NumOpt.egg-info/requires.txt +0 -0
- {numopt-0.0.4 → numopt-0.0.6}/NumOpt.egg-info/top_level.txt +0 -0
- {numopt-0.0.4 → numopt-0.0.6}/README.md +0 -0
- {numopt-0.0.4 → numopt-0.0.6}/pyproject.toml +0 -0
- {numopt-0.0.4 → numopt-0.0.6}/setup.cfg +0 -0
- {numopt-0.0.4 → numopt-0.0.6}/setup.py +0 -0
|
@@ -0,0 +1,201 @@
|
|
|
1
|
+
from ..opti import asb, anp, cas, np
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
class Bspline:
|
|
5
|
+
def __init__(self, ctrlpts, degree=3):
|
|
6
|
+
self.ctrlpts = ctrlpts
|
|
7
|
+
self.n = self.ctrlpts.shape[0] - 1
|
|
8
|
+
self.degree = degree
|
|
9
|
+
self.order = self.degree + 1
|
|
10
|
+
self.segments = self.n - self.degree + 1
|
|
11
|
+
self.knots = self.quasi_uniform_knots()
|
|
12
|
+
|
|
13
|
+
def quasi_uniform_knots(self):
|
|
14
|
+
middle = np.linspace(0, 1, self.segments + 1)
|
|
15
|
+
start = np.zeros(self.degree, dtype="f8")
|
|
16
|
+
end = np.ones(self.degree, dtype="f8")
|
|
17
|
+
knots = np.hstack([start, middle, end])
|
|
18
|
+
return knots
|
|
19
|
+
|
|
20
|
+
@staticmethod
|
|
21
|
+
def __deBoor(t, i, k, knots):
|
|
22
|
+
if k == 1:
|
|
23
|
+
return cas.if_else(cas.logic_and(t >= knots[i], t < knots[i + 1]), 1.0, 0.0)
|
|
24
|
+
else:
|
|
25
|
+
term1 = term2 = 0.0
|
|
26
|
+
delta_t1 = knots[i + k - 1] - knots[i]
|
|
27
|
+
delta_t2 = knots[i + k] - knots[i + 1]
|
|
28
|
+
|
|
29
|
+
if delta_t1 != 0.0:
|
|
30
|
+
term1 = (t - knots[i]) / (delta_t1) * Bspline.__deBoor(t, i, k - 1, knots)
|
|
31
|
+
|
|
32
|
+
if delta_t2 != 0.0:
|
|
33
|
+
term2 = (knots[i + k] - t) / (delta_t2) * Bspline.__deBoor(t, i + 1, k - 1, knots)
|
|
34
|
+
|
|
35
|
+
return term1 + term2
|
|
36
|
+
|
|
37
|
+
def N_coef(self, t, i):
|
|
38
|
+
return Bspline.__deBoor(t, i, self.order, self.knots)
|
|
39
|
+
|
|
40
|
+
def __call__(self, t):
|
|
41
|
+
def func():
|
|
42
|
+
tmp = 0.0
|
|
43
|
+
for j in range(self.n + 1):
|
|
44
|
+
N_coef = self.N_coef(u, j)
|
|
45
|
+
tmp = tmp + N_coef * self.ctrlpts[j : j + 1, :]
|
|
46
|
+
return tmp
|
|
47
|
+
|
|
48
|
+
nts = t.shape[0]
|
|
49
|
+
pts = [0.0] * nts
|
|
50
|
+
|
|
51
|
+
for i in range(nts):
|
|
52
|
+
u = t[i]
|
|
53
|
+
pts[i] = cas.if_else(u == 1.0, self.ctrlpts[-1:, :], func())
|
|
54
|
+
# if u == 1.0:
|
|
55
|
+
# pts[i] = self.ctrlpts[-1:, :]
|
|
56
|
+
# else:
|
|
57
|
+
# for j in range(self.n + 1):
|
|
58
|
+
# N_coef = self.N_coef(u, j)
|
|
59
|
+
# pts[i] = pts[i] + N_coef * self.ctrlpts[j : j + 1, :]
|
|
60
|
+
pts = cas.vcat(pts)
|
|
61
|
+
return pts
|
|
62
|
+
|
|
63
|
+
|
|
64
|
+
class BsplineAirfoil:
|
|
65
|
+
def __init__(self, ctu=None, ctl=None, degree=3):
|
|
66
|
+
self.ctu = ctu
|
|
67
|
+
self.ctl = ctl
|
|
68
|
+
|
|
69
|
+
self.bspline_upper = Bspline(ctrlpts=self.ctu, degree=degree)
|
|
70
|
+
self.bspline_lower = Bspline(ctrlpts=self.ctl, degree=degree)
|
|
71
|
+
|
|
72
|
+
self.__te = self.ctu[-1, 1] - self.ctl[-1, 1]
|
|
73
|
+
self.__symmetry = False
|
|
74
|
+
|
|
75
|
+
def upper_coordinates(self, t):
|
|
76
|
+
pts = self.bspline_upper(t)[::-1, :]
|
|
77
|
+
return pts
|
|
78
|
+
|
|
79
|
+
def lower_coordinates(self, t):
|
|
80
|
+
pts = self.bspline_lower(t)
|
|
81
|
+
return pts
|
|
82
|
+
|
|
83
|
+
def coordinates(self, t):
|
|
84
|
+
pts_upper = self.upper_coordinates(t)
|
|
85
|
+
pts_lower = self.lower_coordinates(t)
|
|
86
|
+
|
|
87
|
+
pts = cas.vcat([pts_upper[:-1, :], pts_lower])
|
|
88
|
+
return pts
|
|
89
|
+
|
|
90
|
+
@property
|
|
91
|
+
def te(self):
|
|
92
|
+
return self.__te
|
|
93
|
+
|
|
94
|
+
@property
|
|
95
|
+
def symmetry(self):
|
|
96
|
+
return self.__symmetry
|
|
97
|
+
|
|
98
|
+
@staticmethod
|
|
99
|
+
def fit(upper_coordinates, lower_coordinates, nctu=9, nctl=9, symmetry=False):
|
|
100
|
+
default_options = {
|
|
101
|
+
"ipopt.sb": "yes",
|
|
102
|
+
"ipopt.max_iter": 1000,
|
|
103
|
+
"ipopt.max_cpu_time": 1e20,
|
|
104
|
+
"ipopt.mu_strategy": "adaptive",
|
|
105
|
+
"ipopt.fast_step_computation": "yes",
|
|
106
|
+
"detect_simple_bounds": False,
|
|
107
|
+
"expand": True,
|
|
108
|
+
"print_time": False,
|
|
109
|
+
"ipopt.print_level": 0,
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
# ============================ upper ==========================
|
|
113
|
+
opti = cas.Opti()
|
|
114
|
+
|
|
115
|
+
ctu = opti.variable(nctu, 2)
|
|
116
|
+
ctu_init = np.zeros(ctu.shape)
|
|
117
|
+
ctu_init[1:, 0] = np.linspace(0, 1, nctu - 1)
|
|
118
|
+
ctu_init[1:-1, 1] = 0.5
|
|
119
|
+
ctu_init[-1, 1] = upper_coordinates[0, 1]
|
|
120
|
+
|
|
121
|
+
ctl = opti.variable(nctl, 2)
|
|
122
|
+
|
|
123
|
+
t = opti.variable(upper_coordinates.shape[0])
|
|
124
|
+
t_init = np.linspace(0, 1, upper_coordinates.shape[0], dtype="f8")
|
|
125
|
+
|
|
126
|
+
af = BsplineAirfoil(ctu=ctu, ctl=ctl)
|
|
127
|
+
coords = af.upper_coordinates(t)
|
|
128
|
+
|
|
129
|
+
dist = coords - upper_coordinates
|
|
130
|
+
residual = cas.sum(cas.dot(dist, dist))
|
|
131
|
+
|
|
132
|
+
opti.subject_to(
|
|
133
|
+
[
|
|
134
|
+
opti.bounded(0.0, t, 1.0),
|
|
135
|
+
opti.bounded(0.0, ctu[:, 0], 1.0),
|
|
136
|
+
cas.diff(t) > 0.0,
|
|
137
|
+
ctu[:, 0] == ctu_init[:, 0],
|
|
138
|
+
ctu[0, 1] == ctu_init[0, 1],
|
|
139
|
+
ctu[-1, 1] == ctu_init[-1, 1],
|
|
140
|
+
t[0] == 0.0,
|
|
141
|
+
t[-1] == 1.0,
|
|
142
|
+
]
|
|
143
|
+
)
|
|
144
|
+
|
|
145
|
+
opti.minimize(residual)
|
|
146
|
+
opti.solver("ipopt", default_options)
|
|
147
|
+
opti.set_initial(ctu, ctu_init)
|
|
148
|
+
opti.set_initial(t, t_init)
|
|
149
|
+
|
|
150
|
+
sol = opti.solve()
|
|
151
|
+
|
|
152
|
+
ctu_sol = sol.value(ctu)
|
|
153
|
+
|
|
154
|
+
# ============================ lower ==========================
|
|
155
|
+
if symmetry:
|
|
156
|
+
ctl_sol = np.array(ctu_sol)
|
|
157
|
+
ctl_sol[:, 1] = -ctu_sol[:, 1]
|
|
158
|
+
else:
|
|
159
|
+
opti = cas.Opti()
|
|
160
|
+
|
|
161
|
+
ctu = opti.variable(nctu, 2)
|
|
162
|
+
|
|
163
|
+
ctl = opti.variable(nctl, 2)
|
|
164
|
+
ctl_init = np.zeros(ctl.shape)
|
|
165
|
+
ctl_init[1:, 0] = np.linspace(0, 1, nctl - 1)
|
|
166
|
+
ctl_init[1:-1, 1] = -0.5
|
|
167
|
+
ctl_init[-1, 1] = lower_coordinates[-1, 1]
|
|
168
|
+
|
|
169
|
+
t = opti.variable(lower_coordinates.shape[0])
|
|
170
|
+
t_init = np.linspace(0, 1, lower_coordinates.shape[0], dtype="f8")
|
|
171
|
+
|
|
172
|
+
af = BsplineAirfoil(ctu=ctu, ctl=ctl)
|
|
173
|
+
coords = af.lower_coordinates(t)
|
|
174
|
+
|
|
175
|
+
dist = coords - lower_coordinates
|
|
176
|
+
residual = cas.sum(cas.dot(dist, dist))
|
|
177
|
+
|
|
178
|
+
opti.subject_to(
|
|
179
|
+
[
|
|
180
|
+
opti.bounded(0.0, t, 1.0),
|
|
181
|
+
opti.bounded(0.0, ctl[:, 0], 1.0),
|
|
182
|
+
cas.diff(t) > 0.0,
|
|
183
|
+
ctl[:, 0] == ctl_init[:, 0],
|
|
184
|
+
ctl[0, 1] == ctl_init[0, 1],
|
|
185
|
+
ctl[-1, 1] == ctl_init[-1, 1],
|
|
186
|
+
t[0] == 0.0,
|
|
187
|
+
t[-1] == 1.0,
|
|
188
|
+
]
|
|
189
|
+
)
|
|
190
|
+
|
|
191
|
+
opti.minimize(residual)
|
|
192
|
+
opti.solver("ipopt", default_options)
|
|
193
|
+
opti.set_initial(ctl, ctl_init)
|
|
194
|
+
opti.set_initial(t, t_init)
|
|
195
|
+
|
|
196
|
+
sol = opti.solve()
|
|
197
|
+
|
|
198
|
+
ctl_sol = sol.value(ctl)
|
|
199
|
+
|
|
200
|
+
af_fit = BsplineAirfoil(ctu=ctu_sol, ctl=ctl_sol)
|
|
201
|
+
return af_fit
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
import sympy as sp
|
|
2
|
+
from sympy.printing.str import StrPrinter
|
|
3
|
+
import aerosandbox as asb
|
|
4
|
+
from scipy.special import comb
|
|
5
|
+
import aerosandbox.numpy as anp
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
class CustomerPrinter(StrPrinter):
|
|
9
|
+
def _print_Pow(self, expr):
|
|
10
|
+
return f"{expr.base}^{expr.exp}"
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
# def comb(N, i):
|
|
14
|
+
# return (sp.factorial(N)) / (sp.factorial(i) * sp.factorial(N - i))
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
def C_func(t, N1, N2):
|
|
18
|
+
return t**N1 * (1 - t) ** N2
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
def S_func(t, A):
|
|
22
|
+
N = len(A) - 1
|
|
23
|
+
S = 0.0
|
|
24
|
+
for i, ai in enumerate(A):
|
|
25
|
+
b = comb(N, i)
|
|
26
|
+
si = b * t**i * (1 - t) ** (N - i)
|
|
27
|
+
S += si * ai
|
|
28
|
+
return S
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
def write_exp(file, N1, N2, Au, Al, Thickness,Le, yu, yl,prefix):
|
|
32
|
+
with open(file, "w") as fout:
|
|
33
|
+
fout.write(f"{prefix}N1={N1}\n")
|
|
34
|
+
fout.write(f"{prefix}N2={N2}\n")
|
|
35
|
+
for idx, a in enumerate(Au):
|
|
36
|
+
fout.write(f"{prefix}Au{idx}={a:.6f}\n")
|
|
37
|
+
for idx, a in enumerate(Al):
|
|
38
|
+
fout.write(f"{prefix}Al{idx}={a:.6f}\n")
|
|
39
|
+
fout.write(f"{prefix}Te={Thickness:.6f}\n")
|
|
40
|
+
fout.write(f"{prefix}Le={Le:.6f}\n")
|
|
41
|
+
fout.write(f"t=1.0\n")
|
|
42
|
+
fout.write(f"{prefix}scaler=1000.0\n")
|
|
43
|
+
fout.write(f"[MilliMeter]{prefix}x={prefix}scaler*t\n")
|
|
44
|
+
fout.write(f"[MilliMeter]{prefix}yu={prefix}scaler*({yu})\n")
|
|
45
|
+
fout.write(f"[MilliMeter]{prefix}yl={prefix}scaler*({yl})\n")
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
def cst2nx():
|
|
49
|
+
order = 8
|
|
50
|
+
prefix="af0_"
|
|
51
|
+
Au = sp.symbols(f"{prefix}Au:{order}")
|
|
52
|
+
Al = sp.symbols(f"{prefix}Al:{order}")
|
|
53
|
+
N1 = sp.symbols(f"{prefix}N1")
|
|
54
|
+
N2 = sp.symbols(f"{prefix}N2")
|
|
55
|
+
Te = sp.symbols(f"{prefix}Te")
|
|
56
|
+
Le = sp.symbols(f"{prefix}Le")
|
|
57
|
+
# expr=x**2
|
|
58
|
+
|
|
59
|
+
t = sp.symbols("t")
|
|
60
|
+
|
|
61
|
+
C = C_func(t,N1,N2)
|
|
62
|
+
|
|
63
|
+
Su = S_func(t, Au)+Le*t**0.5*(1-t)**(order-0.5)
|
|
64
|
+
yu = C * Su + t * Te / 2.0
|
|
65
|
+
|
|
66
|
+
Sl = S_func(t, Al)+Le*t**0.5*(1-t)**(order-0.5)
|
|
67
|
+
yl = C * Sl - t * Te / 2.0
|
|
68
|
+
|
|
69
|
+
# print(yu.subs(t,1))
|
|
70
|
+
|
|
71
|
+
# print(yu)
|
|
72
|
+
yu_expr = str(yu).replace("**","^")
|
|
73
|
+
yl_expr = str(yl).replace("**","^")
|
|
74
|
+
|
|
75
|
+
print(yu_expr)
|
|
76
|
+
print(yl_expr)
|
|
77
|
+
|
|
78
|
+
af = asb.Airfoil("n63415").normalize().set_TE_thickness(0.0).to_kulfan_airfoil(n_weights_per_side=order)
|
|
79
|
+
# af.leading_edge_weight=1.0
|
|
80
|
+
# print(af.lower_coordinates(anp.cosspace(0,1,100)))
|
|
81
|
+
write_exp(
|
|
82
|
+
"C:/Users/Zcaic/Desktop/cst.exp",
|
|
83
|
+
N1=af.N1,
|
|
84
|
+
N2=af.N2,
|
|
85
|
+
Au=af.upper_weights,
|
|
86
|
+
Al=af.lower_weights,
|
|
87
|
+
Thickness=af.TE_thickness,
|
|
88
|
+
Le=af.leading_edge_weight,
|
|
89
|
+
yu=yu_expr,
|
|
90
|
+
yl=yl_expr,
|
|
91
|
+
prefix=prefix
|
|
92
|
+
)
|
|
@@ -0,0 +1,163 @@
|
|
|
1
|
+
import aerosandbox as asb
|
|
2
|
+
import aerosandbox.numpy as anp
|
|
3
|
+
from ..opti import cas
|
|
4
|
+
import numpy as np
|
|
5
|
+
from scipy.special import comb
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
class KulfanAirfoil:
|
|
9
|
+
def __init__(self, Au, Al, N1=0.5, N2=1.0, Le=0.0, Te=0.0):
|
|
10
|
+
self.Au = Au
|
|
11
|
+
self.Al = Al
|
|
12
|
+
self.N1 = N1
|
|
13
|
+
self.N2 = N2
|
|
14
|
+
self.Le = Le
|
|
15
|
+
self.Te = Te
|
|
16
|
+
|
|
17
|
+
def upper_coordinates(self, x):
|
|
18
|
+
C = KulfanAirfoil.class_function(x, self.N1, self.N2)
|
|
19
|
+
S = KulfanAirfoil.shape_function(x, self.Au)
|
|
20
|
+
y = C * (S + self.Le * x**0.5 * (1 - x) ** (self.Au.shape[0] - 1.5)) + x * self.Te / 2.0
|
|
21
|
+
|
|
22
|
+
coords = cas.hcat([x, y])
|
|
23
|
+
return coords[::-1, :]
|
|
24
|
+
|
|
25
|
+
def lower_coordinates(self, x):
|
|
26
|
+
C = KulfanAirfoil.class_function(x, self.N1, self.N2)
|
|
27
|
+
S = KulfanAirfoil.shape_function(x, self.Al)
|
|
28
|
+
y = C * (S + self.Le * x**0.5 * (1 - x) ** (self.Al.shape[0] - 1.5)) - x * self.Te / 2.0
|
|
29
|
+
|
|
30
|
+
coords = cas.hcat([x, y])
|
|
31
|
+
return coords
|
|
32
|
+
|
|
33
|
+
def coordinates(self, x):
|
|
34
|
+
coordinates_upper = self.upper_coordinates(x)
|
|
35
|
+
coordinates_lower = self.lower_coordinates(x)
|
|
36
|
+
|
|
37
|
+
coordinates = cas.vcat([coordinates_upper[:-1, :], coordinates_lower])
|
|
38
|
+
return coordinates
|
|
39
|
+
|
|
40
|
+
@staticmethod
|
|
41
|
+
def class_function(x, N1, N2):
|
|
42
|
+
C = (x) ** N1 * (1 - x) ** N2
|
|
43
|
+
return C
|
|
44
|
+
|
|
45
|
+
@staticmethod
|
|
46
|
+
def shape_function(x, w):
|
|
47
|
+
N = w.shape[0] - 1 # Order of Bernstein polynomials
|
|
48
|
+
|
|
49
|
+
K = comb(N, np.arange(N + 1)) # Bernstein polynomial coefficients
|
|
50
|
+
|
|
51
|
+
B = []
|
|
52
|
+
for i in range(w.shape[0]):
|
|
53
|
+
tmp = K[i] * (x) ** i * (1 - x) ** (N - i)
|
|
54
|
+
B.append(tmp)
|
|
55
|
+
B = cas.hcat(B)
|
|
56
|
+
S = cas.mtimes(B, w)
|
|
57
|
+
|
|
58
|
+
return S
|
|
59
|
+
|
|
60
|
+
@staticmethod
|
|
61
|
+
def fit(upper_coordinates, lower_coordinates, nAu=8, nAl=8, N1=0.5, N2=1.0, symmetry=False):
|
|
62
|
+
if not symmetry:
|
|
63
|
+
opti = cas.Opti()
|
|
64
|
+
Al = opti.variable(nAl)
|
|
65
|
+
Au = opti.variable(nAu)
|
|
66
|
+
leading_edge_weight = opti.variable(1)
|
|
67
|
+
# N1 = opti.variable(1)
|
|
68
|
+
# N2 = opti.variable(1)
|
|
69
|
+
N1 = N1
|
|
70
|
+
N2 = N2
|
|
71
|
+
te = upper_coordinates[-1, 1] - lower_coordinates[-1, 1]
|
|
72
|
+
|
|
73
|
+
xu = upper_coordinates[::-1, 0]
|
|
74
|
+
yu = upper_coordinates[:, 1]
|
|
75
|
+
xl = lower_coordinates[:, 0]
|
|
76
|
+
yl = lower_coordinates[:, 1]
|
|
77
|
+
|
|
78
|
+
af = KulfanAirfoil(Au=Au, Al=Al, N1=N1, N2=N2, Le=leading_edge_weight, Te=te)
|
|
79
|
+
yu_ = af.upper_coordinates(xu)
|
|
80
|
+
yl_ = af.lower_coordinates(xl)
|
|
81
|
+
|
|
82
|
+
residual = cas.sum((yu_ - yu) ** 2) + cas.sum((yl_ - yl) ** 2)
|
|
83
|
+
|
|
84
|
+
default_options = {
|
|
85
|
+
"ipopt.sb": "yes",
|
|
86
|
+
"ipopt.max_iter": 1000,
|
|
87
|
+
"ipopt.max_cpu_time": 1e20,
|
|
88
|
+
"ipopt.mu_strategy": "adaptive",
|
|
89
|
+
"ipopt.fast_step_computation": "yes",
|
|
90
|
+
"detect_simple_bounds": False,
|
|
91
|
+
"expand": True,
|
|
92
|
+
"print_time": False,
|
|
93
|
+
"ipopt.print_level": 0,
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
opti.minimize(residual)
|
|
97
|
+
opti.subject_to(
|
|
98
|
+
[
|
|
99
|
+
opti.bounded(-1.0, leading_edge_weight, 1.0),
|
|
100
|
+
]
|
|
101
|
+
)
|
|
102
|
+
|
|
103
|
+
opti.solver("ipopt", default_options)
|
|
104
|
+
opti.set_initial(Au, 0.05)
|
|
105
|
+
opti.set_initial(Al, -0.05)
|
|
106
|
+
opti.set_initial(leading_edge_weight, 0.0)
|
|
107
|
+
# opti.set_value(N1,0.5)
|
|
108
|
+
# opti.set_value(N2,1.0)
|
|
109
|
+
# opti.set_initial(N1, 0.5)
|
|
110
|
+
# opti.set_initial(N2, 1.0)
|
|
111
|
+
sol = opti.solve()
|
|
112
|
+
|
|
113
|
+
Au_sol = sol.value(Au)
|
|
114
|
+
Al_sol = sol.value(Al)
|
|
115
|
+
le_sol = sol.value(leading_edge_weight)
|
|
116
|
+
|
|
117
|
+
else:
|
|
118
|
+
opti = cas.Opti()
|
|
119
|
+
Al = opti.variable(nAl)
|
|
120
|
+
Au = opti.variable(nAu)
|
|
121
|
+
leading_edge_weight = 0.0
|
|
122
|
+
# N1 = opti.variable(1)
|
|
123
|
+
# N2 = opti.variable(1)
|
|
124
|
+
N1 = N1
|
|
125
|
+
N2 = N2
|
|
126
|
+
te = upper_coordinates[-1, 1] - lower_coordinates[-1, 1]
|
|
127
|
+
|
|
128
|
+
xu = upper_coordinates[::-1, 0]
|
|
129
|
+
yu = upper_coordinates[:, 1]
|
|
130
|
+
xl = lower_coordinates[:, 0]
|
|
131
|
+
yl = lower_coordinates[:, 1]
|
|
132
|
+
|
|
133
|
+
af = KulfanAirfoil(Au=Au, Al=Al, N1=N1, N2=N2, Le=leading_edge_weight, Te=te)
|
|
134
|
+
yu_ = af.upper_coordinates(xu)
|
|
135
|
+
yl_ = af.lower_coordinates(xl)
|
|
136
|
+
|
|
137
|
+
residual = cas.sum((yu_ - yu) ** 2)
|
|
138
|
+
|
|
139
|
+
default_options = {
|
|
140
|
+
"ipopt.sb": "yes",
|
|
141
|
+
"ipopt.max_iter": 1000,
|
|
142
|
+
"ipopt.max_cpu_time": 1e20,
|
|
143
|
+
"ipopt.mu_strategy": "adaptive",
|
|
144
|
+
"ipopt.fast_step_computation": "yes",
|
|
145
|
+
"detect_simple_bounds": False,
|
|
146
|
+
"expand": True,
|
|
147
|
+
"print_time": False,
|
|
148
|
+
"ipopt.print_level": 0,
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
opti.minimize(residual)
|
|
152
|
+
|
|
153
|
+
opti.solver("ipopt", default_options)
|
|
154
|
+
opti.set_initial(Au, 0.05)
|
|
155
|
+
opti.set_initial(Al, -0.05)
|
|
156
|
+
|
|
157
|
+
sol = opti.solve()
|
|
158
|
+
|
|
159
|
+
Au_sol = sol.value(Au)
|
|
160
|
+
Al_sol = -Au_sol
|
|
161
|
+
le_sol = leading_edge_weight
|
|
162
|
+
|
|
163
|
+
return KulfanAirfoil(Au=Au_sol, Al=Al_sol, N1=N1, N2=N2, Le=le_sol, Te=te)
|
|
@@ -0,0 +1,181 @@
|
|
|
1
|
+
import casadi as ca
|
|
2
|
+
import aerosandbox.numpy as anp
|
|
3
|
+
from typing import Literal
|
|
4
|
+
from smt.surrogate_models.krg import KRG
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
class Surrogate:
|
|
8
|
+
def __init__(self, sm: KRG):
|
|
9
|
+
self.sm = sm
|
|
10
|
+
|
|
11
|
+
@property
|
|
12
|
+
def nx(self):
|
|
13
|
+
return self.sm.nx
|
|
14
|
+
|
|
15
|
+
def predict(self, x):
|
|
16
|
+
return self.sm.predict_values(x)
|
|
17
|
+
|
|
18
|
+
def predict_derivate(self, x):
|
|
19
|
+
gradient = anp.array([self.sm.predict_derivatives(x, kx) for kx in range(self.nx)])
|
|
20
|
+
return gradient
|
|
21
|
+
|
|
22
|
+
def predict_variances(self, x):
|
|
23
|
+
return self.sm.predict_variances(x)
|
|
24
|
+
|
|
25
|
+
def predict_variance_derivatives(self, x):
|
|
26
|
+
gradient = anp.array([self.sm.predict_variance_derivatives(x, kx) for kx in range(self.nx)])
|
|
27
|
+
return gradient
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
class Surrogate2Callback(ca.Callback):
|
|
31
|
+
def __init__(self, name, surrogate: Surrogate,nsamples=1, output_kind: Literal["predict", "predict_variances"] = "predict", opts={}):
|
|
32
|
+
ca.Callback.__init__(self)
|
|
33
|
+
self.surrogate = surrogate
|
|
34
|
+
self.nsamples=nsamples
|
|
35
|
+
if output_kind == "predict":
|
|
36
|
+
self.output = self.surrogate.predict
|
|
37
|
+
self.output_der = self.surrogate.predict_derivate
|
|
38
|
+
else:
|
|
39
|
+
self.output = self.surrogate.predict_variances
|
|
40
|
+
self.output_der = self.surrogate.predict_variance_derivatives
|
|
41
|
+
self.construct(name, opts)
|
|
42
|
+
self.jac_callback = self._jac(name)
|
|
43
|
+
|
|
44
|
+
def get_n_in(self):
|
|
45
|
+
return 1
|
|
46
|
+
|
|
47
|
+
def get_sparsity_in(self, i):
|
|
48
|
+
# return ca.Sparsity.dense(self.surrogate.nx, 1)
|
|
49
|
+
return ca.Sparsity.dense(self.nsamples,self.surrogate.nx)
|
|
50
|
+
|
|
51
|
+
def get_n_out(self):
|
|
52
|
+
return 1
|
|
53
|
+
|
|
54
|
+
def get_sparsity_out(self, i):
|
|
55
|
+
# return ca.Sparsity.dense(1, 1)
|
|
56
|
+
return ca.Sparsity.dense(self.nsamples, 1)
|
|
57
|
+
|
|
58
|
+
def eval(self, args):
|
|
59
|
+
x = args[0]
|
|
60
|
+
x = x.toarray()
|
|
61
|
+
y = self.output(x)
|
|
62
|
+
return [y]
|
|
63
|
+
|
|
64
|
+
def has_jacobian(self):
|
|
65
|
+
return True
|
|
66
|
+
|
|
67
|
+
def _jac(self,name):
|
|
68
|
+
class JacFun(ca.Callback):
|
|
69
|
+
def __init__(self_jac, opts={"enable_fd": False}):
|
|
70
|
+
ca.Callback.__init__(self_jac)
|
|
71
|
+
self_jac.construct(f"jac_{name}", opts)
|
|
72
|
+
|
|
73
|
+
def get_n_in(self_jac):
|
|
74
|
+
return 2
|
|
75
|
+
|
|
76
|
+
def get_n_out(self_jac):
|
|
77
|
+
return 1
|
|
78
|
+
|
|
79
|
+
def get_sparsity_in(self_jac, i):
|
|
80
|
+
if i == 0:
|
|
81
|
+
return ca.Sparsity.dense(self.surrogate.nx, 1)
|
|
82
|
+
elif i == 1:
|
|
83
|
+
return ca.Sparsity.dense(1, 1)
|
|
84
|
+
|
|
85
|
+
def get_sparsity_out(self_jac, i):
|
|
86
|
+
return ca.Sparsity.dense(1, self.surrogate.nx)
|
|
87
|
+
|
|
88
|
+
def eval(self_jac, args):
|
|
89
|
+
x = args[0]
|
|
90
|
+
x = x.toarray()
|
|
91
|
+
y_jac = self.output_der(x)
|
|
92
|
+
return [y_jac]
|
|
93
|
+
return JacFun()
|
|
94
|
+
|
|
95
|
+
def get_jacobian(self, name, inames, onames, opts):
|
|
96
|
+
# class JacFun(ca.Callback):
|
|
97
|
+
# def __init__(self_jac, opts={"enable_fd": False}):
|
|
98
|
+
# ca.Callback.__init__(self_jac)
|
|
99
|
+
# self_jac.construct(name, opts)
|
|
100
|
+
|
|
101
|
+
# def get_n_in(self_jac):
|
|
102
|
+
# return 2
|
|
103
|
+
|
|
104
|
+
# def get_n_out(self_jac):
|
|
105
|
+
# return 1
|
|
106
|
+
|
|
107
|
+
# def get_sparsity_in(self_jac, i):
|
|
108
|
+
# if i == 0:
|
|
109
|
+
# return ca.Sparsity.dense(self.surrogate.nx, 1)
|
|
110
|
+
# elif i == 1:
|
|
111
|
+
# return ca.Sparsity.dense(1, 1)
|
|
112
|
+
|
|
113
|
+
# def get_sparsity_out(self_jac, i):
|
|
114
|
+
# return ca.Sparsity.dense(1, self.surrogate.nx)
|
|
115
|
+
|
|
116
|
+
# def eval(self_jac, args):
|
|
117
|
+
# x = args[0]
|
|
118
|
+
# x = x.toarray()
|
|
119
|
+
# y_jac = self.output_der(x)
|
|
120
|
+
# return [y_jac]
|
|
121
|
+
|
|
122
|
+
# self.jac_callback = JacFun()
|
|
123
|
+
return self.jac_callback
|
|
124
|
+
|
|
125
|
+
|
|
126
|
+
if __name__ == "__main__":
|
|
127
|
+
import plotly.graph_objects as go
|
|
128
|
+
from plotly.subplots import make_subplots
|
|
129
|
+
import aerosandbox as asb
|
|
130
|
+
import time
|
|
131
|
+
|
|
132
|
+
xt = anp.array([0.0, 1.0, 2.0, 3.0, 4.0])
|
|
133
|
+
yt = anp.array([0.0, 1.0, 1.5, 0.9, 1.0])
|
|
134
|
+
|
|
135
|
+
sm = KRG(theta0=[1e-2], print_global=False)
|
|
136
|
+
sm.set_training_values(xt, yt)
|
|
137
|
+
sm.train()
|
|
138
|
+
|
|
139
|
+
N=10000
|
|
140
|
+
|
|
141
|
+
sm = Surrogate(sm)
|
|
142
|
+
sm_predict = Surrogate2Callback("sm", surrogate=sm,nsamples=1)
|
|
143
|
+
# dumpy=ca.GenSX_sym("dumpy",1,1)
|
|
144
|
+
# sm_predict_mpi=ca.Function("mpi",[dumpy],[sm_predict(dumpy)]).map(N,"thread")
|
|
145
|
+
# print(sm.predict_derivate(0.0))
|
|
146
|
+
|
|
147
|
+
|
|
148
|
+
# X=anp.linspace(0,4,N).reshape((-1,1))
|
|
149
|
+
# time_s=time.time()
|
|
150
|
+
# y=sm.predict(X)
|
|
151
|
+
# print(time.time()-time_s,"s")
|
|
152
|
+
# # print(y)
|
|
153
|
+
|
|
154
|
+
# time_s=time.time()
|
|
155
|
+
# y=sm_predict_mpi(X.T)
|
|
156
|
+
# print(time.time()-time_s,"s")
|
|
157
|
+
|
|
158
|
+
# time_s=time.time()
|
|
159
|
+
# y=sm_predict(X)
|
|
160
|
+
# print(time.time()-time_s,"s")
|
|
161
|
+
|
|
162
|
+
opti = asb.Opti()
|
|
163
|
+
x = opti.variable(init_guess=1.0, lower_bound=0.0, upper_bound=4.0)
|
|
164
|
+
obj = sm_predict(x)
|
|
165
|
+
opti.maximize(obj)
|
|
166
|
+
sol = opti.solve(options={"ipopt.hessian_approximation": "limited-memory"})
|
|
167
|
+
xopt = sol(x)
|
|
168
|
+
yopt = sol(obj)
|
|
169
|
+
print(sol(x), sol(obj))
|
|
170
|
+
|
|
171
|
+
# num = 100
|
|
172
|
+
# x = anp.linspace(0.0, 4.0, num)
|
|
173
|
+
# y=sm_predict(x).toarray()
|
|
174
|
+
# print(y)
|
|
175
|
+
|
|
176
|
+
# fig=make_subplots(1,1)
|
|
177
|
+
# fig.add_scatter(x=x,y=y[:,0],mode="lines+markers",row=1,col=1,line={"shape":"spline","width":2})
|
|
178
|
+
# fig.add_scatter(x=[xopt],y=[yopt],mode="markers",row=1,col=1,marker={"symbol":"star","size":20})
|
|
179
|
+
# fig.update_layout()
|
|
180
|
+
# fig.show(config={"scrollZoom":True})
|
|
181
|
+
# fig.write_html("./fig.html",config={"scrollZoom":True},auto_open=True)
|