Skip to content

Commit cce5c3a

Browse files
authored
Physics-informed PCE capability
2 parents 1ea42a6 + c9a47c2 commit cce5c3a

File tree

18 files changed

+1635
-78
lines changed

18 files changed

+1635
-78
lines changed

README.rst

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
|AzureDevops| |PyPIdownloads| |PyPI| |CondaPlatforms| |GithubRelease| |Binder| |Docs| |bear-ified|
1+
|AzureDevops| |PyPIdownloads| |PyPI| |CondaPlatforms| |GithubRelease| |Docs| |bear-ified|
22

33
.. |Docs| image:: https://img.shields.io/readthedocs/uqpy?style=plastic :alt: Read the Docs
44
.. |CondaPlatforms| image:: https://img.shields.io/conda/pn/SURG_JHU/uqpy?style=plastic :alt: Conda
Lines changed: 293 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,293 @@
1+
"""
2+
3+
Physics-Informed PCE: Uncertainty Quantification of Euler Beam
4+
======================================================================
5+
6+
In this example, we use Physics-informed Polynomial Chaos Expansion for approximation and UQ of Euler beam equation.
7+
"""
8+
9+
# %% md
10+
#
11+
# Import necessary libraries.
12+
13+
# %%
14+
15+
from UQpy.distributions import Uniform, JointIndependent
16+
from UQpy.surrogates import *
17+
18+
# load PC^2
19+
from UQpy.surrogates.polynomial_chaos.polynomials.TotalDegreeBasis import TotalDegreeBasis
20+
from UQpy.surrogates.polynomial_chaos.physics_informed.ConstrainedPCE import ConstrainedPCE
21+
from UQpy.surrogates.polynomial_chaos.physics_informed.PdeData import PdeData
22+
from UQpy.surrogates.polynomial_chaos.physics_informed.PdePCE import PdePCE
23+
from UQpy.surrogates.polynomial_chaos.physics_informed.Utilities import *
24+
from UQpy.surrogates.polynomial_chaos.physics_informed.ReducedPCE import ReducedPCE
25+
26+
27+
# %% md
28+
#
29+
# We then define functions used for construction of Physically Constrained PCE (PC :math:`^2`)
30+
31+
# %%
32+
33+
# Definition of PDE/ODE
34+
def pde_func(standardized_sample, pce):
35+
der_order = 4
36+
deriv_pce = derivative_basis(standardized_sample, pce, derivative_order=der_order, leading_variable=0) * ((2 / 1) ** der_order)
37+
38+
pde_basis = deriv_pce
39+
40+
return pde_basis
41+
42+
43+
# Definition of the source term
44+
def pde_res(standardized_sample):
45+
load_s = load(standardized_sample)
46+
47+
return -load_s[:, 0]
48+
49+
50+
def load(standardized_sample):
51+
return const_load(standardized_sample)
52+
53+
54+
def const_load(standardized_sample):
55+
l = (1 + (1 + standardized_sample[:, 1]) / 2).reshape(-1, 1)
56+
return l
57+
58+
59+
# Definition of the function for sampling of boundary conditions
60+
def bc_sampling(nsim=1000):
61+
# BC sampling
62+
63+
nsim_half = round(nsim / 2)
64+
sample = np.zeros((nsim, 2))
65+
real_ogrid_1d = ortho_grid(nsim_half, 1, 0.0, 1.0)[:, 0]
66+
67+
sample[:nsim_half, 0] = np.zeros(nsim_half)
68+
sample[:nsim_half, 1] = real_ogrid_1d
69+
70+
sample[nsim_half:, 0] = np.ones(nsim_half)
71+
sample[nsim_half:, 1] = real_ogrid_1d
72+
73+
return sample
74+
75+
76+
# define sampling and evaluation of BC for estimation of error
77+
def bc_res(nsim, pce):
78+
physical_sample= np.zeros((2, 2))
79+
physical_sample[1, 0] = 1
80+
standardized_sample = polynomial_chaos.Polynomials.standardize_sample(physical_sample, pce.polynomial_basis.distributions)
81+
82+
der_order = 2
83+
deriv_pce = np.sum(
84+
derivative_basis(standardized_sample, pce, derivative_order=der_order, leading_variable=0) *
85+
((2 / 1) ** der_order) * np.array(pce.coefficients).T, axis=1)
86+
87+
return deriv_pce
88+
89+
90+
# Definition of the reference solution for an error estimation
91+
def ref_sol(physical_coordinate, q):
92+
return (q + 1) * (-(physical_coordinate ** 4) / 24 + physical_coordinate ** 3 / 12 - physical_coordinate / 24)
93+
94+
95+
# %% md
96+
#
97+
# Beam equation is parametrized by one deterministic input variable (coordinate :math:`x`) and
98+
# random load intensity :math:`q`, both modeled as Uniform random variables.
99+
100+
# number of input variables
101+
102+
nrand = 1
103+
nvar = 1 + nrand
104+
105+
# definition of a joint probability distribution
106+
dist1 = Uniform(loc=0, scale=1)
107+
dist2 = Uniform(loc=0, scale=1)
108+
marg = [dist1, dist2]
109+
joint = JointIndependent(marginals=marg)
110+
111+
# %% md
112+
#
113+
# The physical domain is defined by :math:`x \in [0,1]`
114+
115+
116+
# define geometry of physical space
117+
geometry_xmin = [0]
118+
geometry_xmax = [1]
119+
120+
# %% md
121+
#
122+
# Boundary conditions are prescribed as
123+
#
124+
# .. math:: \frac{d^2 u(0,q)}{d x^2}=0, \qquad \frac{d^2 u(1,q)}{d x^2}=0, \qquad u(0,q)=0, \qquad u(1,q)=0
125+
126+
# number of BC samples
127+
nbc = 2 * 10
128+
129+
# derivation orders of prescribed BCs
130+
der_orders = [0, 2]
131+
# normals associated to precribed BCs
132+
bc_normals = [0, 0]
133+
# sampling of BC points
134+
135+
bc_xtotal = bc_sampling(20)
136+
bc_ytotal = np.zeros(len(bc_xtotal))
137+
138+
bc_x = [bc_xtotal, bc_xtotal]
139+
bc_y = [bc_ytotal, bc_ytotal]
140+
141+
# %% md
142+
#
143+
# Here we construct an object containing general physical information (geometry, boundary conditions)
144+
pde_data = PdeData(geometry_xmax, geometry_xmin, der_orders, bc_normals, bc_x, bc_y)
145+
146+
# %% md
147+
#
148+
# Further we construct an object containing PDE physical data and PC :math:`^2` definitions of PDE
149+
150+
pde_pce = PdePCE(pde_data, pde_func, pde_source=pde_res, boundary_conditions_evaluate=bc_res)
151+
152+
# %% md
153+
#
154+
# Get dirichlet boundary conditions from PDE data object that are further used for construction of initial PCE object.
155+
156+
dirichlet_bc = pde_data.dirichlet
157+
x_train = dirichlet_bc[:, :-1]
158+
y_train = dirichlet_bc[:, -1]
159+
160+
# %% md
161+
#
162+
# Finally, PC :math:`^2` is constructed using Karush-Kuhn-Tucker normal equations solved by ordinary least squares and
163+
# Least Angle Regression.
164+
165+
least_squares = LeastSquareRegression()
166+
p = 9
167+
168+
PCEorder = p
169+
polynomial_basis = TotalDegreeBasis(joint, PCEorder, hyperbolic=1)
170+
171+
# create initial PCE object containing basis, regression method and dirichlet BC
172+
initpce = PolynomialChaosExpansion(polynomial_basis=polynomial_basis, regression_method=least_squares)
173+
initpce.set_data(x_train, y_train)
174+
175+
# construct a PC^2 object combining pde_data, pde_pce and initial PCE objects
176+
pcpc = ConstrainedPCE(pde_data, pde_pce, initpce)
177+
# get coefficients of PC^2 by least angle regression
178+
pcpc.lar()
179+
180+
# get coefficients of PC^2 by ordinary least squares
181+
pcpc.ols()
182+
183+
# evaluate errors of approximations
184+
real_ogrid = ortho_grid(100, nvar, 0.0, 1.0)
185+
yy_val_pce = pcpc.lar_pce.predict(real_ogrid).flatten()
186+
yy_val_pce_ols = pcpc.initial_pce.predict(real_ogrid).flatten()
187+
yy_val_true = ref_sol(real_ogrid[:, 0], real_ogrid[:, 1]).flatten()
188+
189+
err = np.abs(yy_val_pce - yy_val_true)
190+
tot_err = np.sum(err)
191+
print('\nTotal approximation error by PC^2-LAR: ', tot_err)
192+
193+
err_ols = np.abs(yy_val_pce_ols - yy_val_true)
194+
tot_err_ols = np.sum(err_ols)
195+
print('Total approximation error by PC^2-OLS: ', tot_err_ols)
196+
197+
# %% md
198+
#
199+
# Once the PC :math:`^2` is constructed, we use ReducedPce class to filter out influence of deterministic variable in UQ
200+
201+
reduced_pce = ReducedPCE(pcpc.lar_pce, n_deterministic=1)
202+
203+
# %% md
204+
#
205+
# ReducedPce is used for estimation of local statistical moments and quantiles :math:`\pm2\sigma`
206+
coeff_res = []
207+
var_res = []
208+
mean_res = []
209+
vartot_res = []
210+
lower_quantiles_modes = []
211+
upper_quantiles_modes = []
212+
213+
n_derivations = 4
214+
sigma_mult = 2
215+
beam_x = np.arange(0, 101) / 100
216+
217+
for x in beam_x:
218+
mean = np.zeros(n_derivations + 1)
219+
var = np.zeros(n_derivations + 1)
220+
variances = np.zeros((n_derivations + 1, nrand))
221+
lq = np.zeros((1 + n_derivations, nrand))
222+
uq = np.zeros((1 + n_derivations, nrand))
223+
for d in range(1 + n_derivations):
224+
if d == 0:
225+
coeff = (reduced_pce.evaluate_coordinate(np.array(x), return_coefficients=True))
226+
else:
227+
coeff = reduced_pce.derive_coordinate(np.array(x), derivative_order=d, leading_variable=0,
228+
return_coefficients=True,
229+
derivative_multiplier=transformation_multiplier(pde_data, 0, d))
230+
mean[d] = coeff[0]
231+
var[d] = np.sum(coeff[1:] ** 2)
232+
variances[d, :] = reduced_pce.variance_contributions(coeff)
233+
234+
for e in range(nrand):
235+
lq[d, e] = mean[d] + sigma_mult * np.sqrt(np.sum(variances[d, :e + 1]))
236+
uq[d, e] = mean[d] - sigma_mult * np.sqrt(np.sum(variances[d, :e + 1]))
237+
238+
lower_quantiles_modes.append(lq)
239+
upper_quantiles_modes.append(uq)
240+
var_res.append(variances)
241+
mean_res.append(mean)
242+
vartot_res.append(var)
243+
244+
mean_res = np.array(mean_res)
245+
vartot_res = np.array(vartot_res)
246+
var_res = np.array(var_res)
247+
lower_quantiles_modes = np.array(lower_quantiles_modes)
248+
upper_quantiles_modes = np.array(upper_quantiles_modes)
249+
250+
# %% md
251+
#
252+
# PC :math:`^2` and corresponding Reduced PCE can be further used for local UQ. Obtained results are depicted in figure.
253+
254+
# plot results
255+
import matplotlib.pyplot as plt
256+
257+
fig, ax = plt.subplots(1, 4, figsize=(14.5, 3))
258+
colors = ['black', 'blue', 'green', 'red']
259+
260+
for d in range(2, 1 + n_derivations):
261+
ax[d - 1].plot(beam_x, mean_res[:, d], color=colors[d - 1])
262+
ax[d - 1].plot(beam_x, mean_res[:, d] + sigma_mult * np.sqrt(vartot_res[:, d]), color=colors[d - 1])
263+
ax[d - 1].plot(beam_x, mean_res[:, d] - sigma_mult * np.sqrt(vartot_res[:, d]), color=colors[d - 1])
264+
265+
ax[d - 1].plot(beam_x, lower_quantiles_modes[:, d, 0], '--', alpha=0.7, color=colors[d - 1])
266+
ax[d - 1].plot(beam_x, upper_quantiles_modes[:, d, 0], '--', alpha=0.7, color=colors[d - 1])
267+
ax[d - 1].fill_between(beam_x, lower_quantiles_modes[:, d, 0], upper_quantiles_modes[:, d, 0],
268+
facecolor=colors[d - 1], alpha=0.05)
269+
270+
ax[d - 1].plot(beam_x, np.zeros(len(beam_x)), color='black')
271+
272+
ax[0].plot(beam_x, mean_res[:, 0], color=colors[0])
273+
ax[0].plot(beam_x, mean_res[:, 0] + sigma_mult * np.sqrt(vartot_res[:, 0]), color=colors[0])
274+
ax[0].plot(beam_x, mean_res[:, 0] - sigma_mult * np.sqrt(vartot_res[:, 0]), color=colors[0])
275+
276+
ax[0].plot(beam_x, lower_quantiles_modes[:, 0, 0], '--', alpha=0.7, color=colors[0])
277+
ax[0].plot(beam_x, upper_quantiles_modes[:, 0, 0], '--', alpha=0.7, color=colors[0])
278+
ax[0].fill_between(beam_x, lower_quantiles_modes[:, 0, 0], upper_quantiles_modes[:, 0, 0],
279+
facecolor=colors[0], alpha=0.05)
280+
281+
ax[0].plot(beam_x, np.zeros(len(beam_x)), color='black')
282+
283+
ax[3].invert_yaxis()
284+
ax[1].invert_yaxis()
285+
286+
ax[3].set_title(r'Load $\frac{\partial^4 w}{\partial x^4}$', y=1.04)
287+
ax[2].set_title(r'Shear Force $\frac{\partial^3 w}{\partial x^3}$', y=1.04)
288+
ax[1].set_title(r'Bending Moment $\frac{\partial^2 w}{\partial x^2}$', y=1.04)
289+
ax[0].set_title(r'Deflection $w$', y=1.04)
290+
291+
for axi in ax.flatten():
292+
axi.set_xlabel(r'$x$')
293+
plt.show()

docs/code/surrogates/pce/plot_pce_sparsity_lars.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -237,7 +237,7 @@ def ishigami(xx):
237237

238238
# %% md
239239
#
240-
# In case of high-dimensional input and/or high :math:P` it is also beneficial to reduce the TD basis set by hyperbolic
240+
# In case of high-dimensional input and/or high :math:`P` it is also beneficial to reduce the TD basis set by hyperbolic
241241
# truncation. The hyperbolic truncation reduces higher-order interaction terms in dependence to parameter :math:`q` in
242242
# interval :math:`(0,1)`. The set of multi indices :math:`\alpha` is reduced as follows:
243243
#

0 commit comments

Comments
 (0)