diff --git a/examples/greedy/squareBubbleHomog.py b/examples/greedy/squareBubbleHomog.py
index b04fe75..7150daa 100644
--- a/examples/greedy/squareBubbleHomog.py
+++ b/examples/greedy/squareBubbleHomog.py
@@ -1,107 +1,104 @@
import numpy as np
from rrompy.hfengines.scipy import HelmholtzSquareBubbleProblemEngine as HSBPE
from rrompy.reduction_methods.lagrange_greedy import \
ApproximantLagrangePadeGreedy as Pade
-from rrompy.reduction_methods.lagrange_greedy import \
- ApproximantLagrangePadeOrthogonalGreedy as PadeOrtho
from rrompy.reduction_methods.lagrange_greedy import \
ApproximantLagrangeRBGreedy as RB
from rrompy.utilities.base import squareResonances
verb = 2
timed = False
algo = "Pade"
-algo = "PadeOrtho"
#algo = "RB"
+polyBasis = "LEGENDRE"
+#polyBasis = "CHEBYSHEV"
+polyBasis = "MONOMIAL"
k0s = np.power(np.linspace(95, 149, 200), .5)
+k0s = np.power(np.linspace(95, 129, 200), .5)
k0 = np.mean(np.power(k0s, 2.)) ** .5
kl, kr = min(k0s), max(k0s)
polesexact = np.unique(np.power(squareResonances(kl**2., kr**2., False), .5))
rescaling = lambda x: np.power(x, 2.)
rescalingInv = lambda x: np.power(x, .5)
params = {'muBounds':[kl, kr], 'nTrainingPoints':500, 'Delta':0,
- 'greedyTol':1e-2, 'nTestPoints':2, 'basis':"LEGENDRE"}
-# 'greedyTol':1e-2, 'nTestPoints':2, 'basis':"CHEBYSHEV"}
+ 'greedyTol':1e-2, 'nTestPoints':2, 'basis':polyBasis}
solver = HSBPE(kappa = 12 ** .5, theta = np.pi / 3, n = 30,
verbosity = verb)
solver.omega = np.real(k0)
if algo == "Pade":
approx = Pade(solver, mu0 = k0, approxParameters = params,
verbosity = verb)
-elif algo == "PadeOrtho":
- approx = PadeOrtho(solver, mu0 = k0, approxParameters = params,
- verbosity = verb)
else:
approx = RB(solver, mu0 = k0, approxParameters = params, verbosity = verb)
if timed:
from time import clock
start_time = clock()
approx.greedy()
print("Time: ", clock() - start_time)
else:
approx.greedy(True)
approx.samplingEngine.verbosity = 0
approx.verbosity = 0
from matplotlib import pyplot as plt
normApp = np.zeros_like(k0s)
norm = np.zeros_like(k0s)
res = np.zeros_like(k0s)
err = np.zeros_like(k0s)
for j in range(len(k0s)):
normApp[j] = approx.normApp(k0s[j])
norm[j] = approx.normHF(k0s[j])
res[j] = (approx.estNormer.norm(approx.getRes(k0s[j]))
/ approx.estNormer.norm(approx.getRHS(k0s[j])))
err[j] = approx.normErr(k0s[j]) / approx.normHF(k0s[j])
resApp = approx.errorEstimator(k0s)
plt.figure()
plt.semilogy(k0s, norm)
plt.semilogy(k0s, normApp, '--')
plt.semilogy(polesexact,
2.*np.max(norm)*np.ones_like(polesexact, dtype = float), 'm.')
plt.semilogy(np.real(approx.mus),
4.*np.max(norm)*np.ones_like(approx.mus, dtype = float), 'rx')
plt.xlim([kl, kr])
plt.grid()
plt.show()
plt.close()
plt.figure()
plt.semilogy(k0s, res)
plt.semilogy(k0s, resApp, '--')
plt.semilogy(polesexact,
2.*np.max(resApp)*np.ones_like(polesexact, dtype = float), 'm.')
plt.semilogy(np.real(approx.mus),
4.*np.max(resApp)*np.ones_like(approx.mus, dtype = float), 'rx')
plt.xlim([kl, kr])
plt.grid()
plt.show()
plt.close()
plt.figure()
plt.semilogy(k0s, err)
plt.semilogy(polesexact,
2.*np.max(err)*np.ones_like(polesexact, dtype = float), 'm.')
plt.xlim([kl, kr])
plt.grid()
plt.show()
plt.close()
polesApp = approx.getPoles()
mask = (np.real(polesApp) < kl) | (np.real(polesApp) > kr)
print("Outliers:", polesApp[mask])
polesApp = polesApp[~mask]
plt.figure()
plt.plot(np.real(polesApp), np.imag(polesApp), 'kx')
plt.plot(np.real(polesexact), np.imag(polesexact), 'm.')
plt.axis('equal')
plt.grid()
plt.show()
plt.close()
diff --git a/examples/greedy/squareScatteringAirfoil.py b/examples/greedy/squareScatteringAirfoil.py
index 5d15d84..17fb055 100644
--- a/examples/greedy/squareScatteringAirfoil.py
+++ b/examples/greedy/squareScatteringAirfoil.py
@@ -1,148 +1,145 @@
import numpy as np
import fenics as fen
import ufl
from rrompy.hfengines.scipy import HelmholtzBoxScatteringProblemEngine as HSP
from rrompy.reduction_methods.lagrange_greedy import \
ApproximantLagrangePadeGreedy as Pade
-from rrompy.reduction_methods.lagrange_greedy import \
- ApproximantLagrangePadeOrthogonalGreedy as PadeOrtho
from rrompy.reduction_methods.lagrange_greedy import \
ApproximantLagrangeRBGreedy as RB
from rrompy.utilities.base.fenics import fenONE
verb = 2
timed = False
algo = "Pade"
-algo = "PadeOrtho"
#algo = "RB"
+polyBasis = "LEGENDRE"
+#polyBasis = "CHEBYSHEV"
+polyBasis = "MONOMIAL"
homog = True
homog = False
k0s = np.linspace(5, 20, 50)
k0 = np.mean(k0s)
kl, kr = min(k0s), max(k0s)
params = {'muBounds':[kl, kr], 'nTrainingPoints':500, 'Delta':0,
- 'greedyTol':1e-2, 'nTestPoints':2, 'basis':"LEGENDRE"}
+ 'greedyTol':1e-2, 'nTestPoints':2, 'basis':polyBasis}
#########
PI = np.pi
R = 2
def Dboundary(x, on_boundary):
return on_boundary and (x[0]**2+x[1]**2)**.5 < .95 * R
kappa = 10
theta = PI * - 45 / 180.
mu = 1.1
epsilon = .1
mesh = fen.Mesh('../data/mesh/airfoil.xml')
c, s = np.cos(theta), np.sin(theta)
x, y = fen.SpatialCoordinate(mesh)[:]
u0R = - fen.cos(kappa * (c * x + s * y))
u0I = - fen.sin(kappa * (c * x + s * y))
checkReal = x**2-x+y**2
rhop5 = ((x**2+y**2)/((x-1)**2+y**2))**.25
phiroot1 = fen.atan(-y/(x**2-x+y**2)) / 2
phiroot2 = fen.atan(-y/(x**2-x+y**2)) / 2 - PI * ufl.sign(-y/(x**2-x+y**2)) / 2
kappam1 = (((rhop5*fen.cos(phiroot1)+.5)**2.+(rhop5*fen.sin(phiroot1))**2.)/
((rhop5*fen.cos(phiroot1)-1.)**2.+(rhop5*fen.sin(phiroot1))**2.)
)**.5 - mu
kappam2 = (((rhop5*fen.cos(phiroot2)+.5)**2.+(rhop5*fen.sin(phiroot2))**2.)/
((rhop5*fen.cos(phiroot2)-1.)**2.+(rhop5*fen.sin(phiroot2))**2.)
)**.5 - mu
Heps1 = .9 * .5 * (1 + kappam1/epsilon + fen.sin(PI*kappam1/epsilon) / PI) + .1
Heps2 = .9 * .5 * (1 + kappam2/epsilon + fen.sin(PI*kappam2/epsilon) / PI) + .1
cTT = ufl.conditional(ufl.le(kappam1, epsilon), Heps1, fenONE)
c_F = fen.Constant(.1)
cFT = ufl.conditional(ufl.le(kappam2, epsilon), Heps2, fenONE)
c_F = fen.Constant(.1)
cT = ufl.conditional(ufl.ge(kappam1, - epsilon), cTT, c_F)
cF = ufl.conditional(ufl.ge(kappam2, - epsilon), cFT, c_F)
a = ufl.conditional(ufl.ge(checkReal, 0.), cT, cF)
solver = HSP(R, np.real(k0), theta, n = 1, verbosity = verb,
degree_threshold = 8)
solver.omega = np.real(k0)
solver.V = fen.FunctionSpace(mesh, "P", 3)
solver.diffusivity = a
solver.DirichletBoundary = Dboundary
solver.RobinBoundary = "REST"
solver.DirichletDatum = [u0R, u0I]
#########
if algo == "Pade":
approx = Pade(solver, mu0 = k0, approxParameters = params,
verbosity = verb, homogeneized = homog)
-elif algo == "PadeOrtho":
- approx = PadeOrtho(solver, mu0 = k0, approxParameters = params,
- verbosity = verb, homogeneized = homog)
else:
approx = RB(solver, mu0 = k0, approxParameters = params, verbosity = verb,
homogeneized = homog)
if timed:
from time import clock
start_time = clock()
approx.greedy()
print("Time: ", clock() - start_time)
else:
approx.greedy(True)
approx.samplingEngine.verbosity = 0
approx.verbosity = 0
kl, kr = np.real(kl), np.real(kr)
from matplotlib import pyplot as plt
normApp = np.zeros(len(k0s))
norm = np.zeros_like(normApp)
res = np.zeros_like(normApp)
err = np.zeros_like(normApp)
for j in range(len(k0s)):
normApp[j] = approx.normApp(k0s[j])
norm[j] = approx.normHF(k0s[j])
res[j] = (approx.estNormer.norm(approx.getRes(k0s[j]))
/ approx.estNormer.norm(approx.getRHS(k0s[j])))
err[j] = approx.normErr(k0s[j]) / approx.normHF(k0s[j])
resApp = approx.errorEstimator(k0s)
plt.figure()
plt.plot(k0s, norm)
plt.plot(k0s, normApp, '--')
plt.plot(np.real(approx.mus),
1.05*np.max(norm)*np.ones_like(approx.mus, dtype = float), 'rx')
plt.xlim([kl, kr])
plt.grid()
plt.show()
plt.close()
plt.figure()
plt.semilogy(k0s, res)
plt.semilogy(k0s, resApp, '--')
plt.semilogy(np.real(approx.mus),
4.*np.max(resApp)*np.ones_like(approx.mus, dtype = float), 'rx')
plt.xlim([kl, kr])
plt.grid()
plt.show()
plt.close()
plt.figure()
plt.semilogy(k0s, err)
plt.xlim([kl, kr])
plt.grid()
plt.show()
plt.close()
polesApp = approx.getPoles()
mask = (np.real(polesApp) < kl) | (np.real(polesApp) > kr)
print("Outliers:", polesApp[mask])
polesApp = polesApp[~mask]
plt.figure()
plt.plot(np.real(polesApp), np.imag(polesApp), 'kx')
plt.axis('equal')
plt.grid()
plt.show()
plt.close()
diff --git a/examples/greedy/squareScatteringHomog.py b/examples/greedy/squareScatteringHomog.py
index ca96290..8c9270a 100644
--- a/examples/greedy/squareScatteringHomog.py
+++ b/examples/greedy/squareScatteringHomog.py
@@ -1,94 +1,91 @@
import numpy as np
from rrompy.hfengines.scipy import HelmholtzCavityScatteringProblemEngine as \
HCSPE
from rrompy.reduction_methods.lagrange_greedy import \
ApproximantLagrangePadeGreedy as Pade
-from rrompy.reduction_methods.lagrange_greedy import \
- ApproximantLagrangePadeOrthogonalGreedy as PadeOrtho
from rrompy.reduction_methods.lagrange_greedy import \
ApproximantLagrangeRBGreedy as RB
verb = 2
timed = False
algo = "Pade"
-algo = "PadeOrtho"
#algo = "RB"
+polyBasis = "LEGENDRE"
+#polyBasis = "CHEBYSHEV"
+polyBasis = "MONOMIAL"
k0s = np.linspace(5, 12, 100)
k0 = np.mean(k0s)
kl, kr = min(k0s), max(k0s)
params = {'muBounds':[kl, kr], 'nTrainingPoints':500, 'Delta':0,
- 'greedyTol':1e-2, 'nTestPoints':2, 'basis':"LEGENDRE"}
+ 'greedyTol':1e-2, 'nTestPoints':2, 'basis':polyBasis}
solver = HCSPE(kappa = 5, n = 10, verbosity = verb)
solver.omega = np.real(k0)
if algo == "Pade":
approx = Pade(solver, mu0 = k0, approxParameters = params,
verbosity = verb)
-elif algo == "PadeOrtho":
- approx = PadeOrtho(solver, mu0 = k0, approxParameters = params,
- verbosity = verb)
else:
approx = RB(solver, mu0 = k0, approxParameters = params, verbosity = verb)
if timed:
from time import clock
start_time = clock()
approx.greedy()
print("Time: ", clock() - start_time)
else:
approx.greedy(True)
approx.samplingEngine.verbosity = 0
approx.verbosity = 0
from matplotlib import pyplot as plt
normApp = np.zeros(len(k0s))
norm = np.zeros_like(normApp)
res = np.zeros_like(normApp)
err = np.zeros_like(normApp)
for j in range(len(k0s)):
normApp[j] = approx.normApp(k0s[j])
norm[j] = approx.normHF(k0s[j])
res[j] = (approx.estNormer.norm(approx.getRes(k0s[j]))
/ approx.estNormer.norm(approx.getRHS(k0s[j])))
err[j] = approx.normErr(k0s[j]) / approx.normHF(k0s[j])
resApp = approx.errorEstimator(k0s)
plt.figure()
plt.plot(k0s, norm)
plt.plot(k0s, normApp, '--')
plt.plot(np.real(approx.mus),
1.25*np.max(norm)*np.ones_like(approx.mus, dtype = float), 'rx')
plt.xlim([kl, kr])
plt.grid()
plt.show()
plt.close()
plt.figure()
plt.semilogy(k0s, res)
plt.semilogy(k0s, resApp, '--')
plt.semilogy(np.real(approx.mus),
4.*np.max(resApp)*np.ones_like(approx.mus, dtype = float), 'rx')
plt.xlim([kl, kr])
plt.grid()
plt.show()
plt.close()
plt.figure()
plt.semilogy(k0s, err)
plt.xlim([kl, kr])
plt.grid()
plt.show()
plt.close()
polesApp = approx.getPoles()
mask = (np.real(polesApp) < kl) | (np.real(polesApp) > kr)
print("Outliers:", polesApp[mask])
polesApp = polesApp[~mask]
plt.figure()
plt.plot(np.real(polesApp), np.imag(polesApp), 'kx')
plt.axis('equal')
plt.grid()
plt.show()
plt.close()
diff --git a/examples/greedy/squareTransmissionNonHomog.py b/examples/greedy/squareTransmissionNonHomog.py
index 8cbf907..e3d153a 100644
--- a/examples/greedy/squareTransmissionNonHomog.py
+++ b/examples/greedy/squareTransmissionNonHomog.py
@@ -1,108 +1,105 @@
import numpy as np
from rrompy.hfengines.scipy import HelmholtzSquareTransmissionProblemEngine \
as HSTPE
from rrompy.reduction_methods.lagrange_greedy import \
ApproximantLagrangeRBGreedy as RB
-from rrompy.reduction_methods.lagrange_greedy import \
- ApproximantLagrangePadeOrthogonalGreedy as PadeOrtho
from rrompy.reduction_methods.lagrange_greedy import \
ApproximantLagrangePadeGreedy as Pade
timed = False
verb = 2
algo = "Pade"
-#algo = "PadeOrtho"
#algo = "RB"
+polyBasis = "LEGENDRE"
+polyBasis = "CHEBYSHEV"
+polyBasis = "MONOMIAL"
homog = True
-#homog = False
+homog = False
k0s = np.power(np.linspace(4, 15, 100), .5)
k0 = np.mean(np.power(k0s, 2.)) ** .5
kl, kr = min(k0s), max(k0s)
rescaling = lambda x: np.power(x, 2.)
rescalingInv = lambda x: np.power(x, .5)
params = {'muBounds':[kl, kr], 'nTrainingPoints':500, 'Delta':0,
- 'greedyTol':1e-2, 'nTestPoints':5, 'basis':"LEGENDRE"}
+ 'greedyTol':1e-2, 'nTestPoints':5, 'basis':polyBasis}
solver = HSTPE(nT = 1, nB = 2, theta = np.pi * 20 / 180, kappa = 4.,
n = 20, verbosity = verb)
solver.omega = np.real(k0)
if algo == "Pade":
approx = Pade(solver, mu0 = k0, approxParameters = params,
verbosity = verb, homogeneized = homog)
-elif algo == "PadeOrtho":
- approx = PadeOrtho(solver, mu0 = k0, approxParameters = params,
- verbosity = verb, homogeneized = homog)
else:
approx = RB(solver, mu0 = k0, approxParameters = params, verbosity = verb,
homogeneized = homog)
if timed:
from time import clock
start_time = clock()
approx.greedy()
print("Time: ", clock() - start_time)
else:
approx.greedy(True)
approx.samplingEngine.verbosity = 0
approx.verbosity = 0
from matplotlib import pyplot as plt
normApp = np.zeros(len(k0s))
norm = np.zeros_like(normApp)
res = np.zeros_like(normApp)
err = np.zeros_like(normApp)
for j in range(len(k0s)):
normApp[j] = approx.normApp(k0s[j])
norm[j] = approx.normHF(k0s[j])
res[j] = (approx.estNormer.norm(approx.getRes(k0s[j]))
/ approx.estNormer.norm(approx.getRHS(k0s[j])))
err[j] = approx.normErr(k0s[j]) / approx.normHF(k0s[j])
resApp = approx.errorEstimator(k0s)
polesApp = approx.getPoles()
polesApp = polesApp[np.abs(np.imag(polesApp)) < 1e-3]
plt.figure()
plt.semilogy(k0s, norm)
plt.semilogy(k0s, normApp, '--')
plt.semilogy(np.real(approx.mus),
4.*np.max(norm)*np.ones_like(approx.mus, dtype = float), 'rx')
plt.semilogy(np.real(polesApp),
2.*np.max(norm)*np.ones_like(polesApp, dtype = float), 'k.')
plt.xlim([kl, kr])
plt.grid()
plt.show()
plt.close()
plt.figure()
plt.semilogy(k0s, res)
plt.semilogy(k0s, resApp, '--')
plt.semilogy(np.real(approx.mus),
4.*np.max(resApp)*np.ones_like(approx.mus, dtype = float), 'rx')
plt.semilogy(np.real(polesApp),
2.*np.max(resApp)*np.ones_like(polesApp, dtype = float), 'k.')
plt.xlim([kl, kr])
plt.grid()
plt.show()
plt.close()
plt.figure()
plt.semilogy(k0s, err)
plt.semilogy(np.real(polesApp),
2.*np.max(err)*np.ones_like(polesApp, dtype = float), 'k.')
plt.xlim([kl, kr])
plt.grid()
plt.show()
plt.close()
polesApp = approx.getPoles()
mask = (np.real(polesApp) < kl) | (np.real(polesApp) > kr)
print("Outliers:", polesApp[mask])
polesApp = polesApp[~mask]
plt.figure()
plt.plot(np.real(polesApp), np.imag(polesApp), 'kx')
plt.axis('equal')
plt.grid()
plt.show()
plt.close()
diff --git a/examples/pod/LagrangePoles.py b/examples/pod/LagrangePoles.py
index 141a5f6..bbda022 100644
--- a/examples/pod/LagrangePoles.py
+++ b/examples/pod/LagrangePoles.py
@@ -1,48 +1,46 @@
from matplotlib import pyplot as plt
import numpy as np
from rrompy.hfengines.scipy import HelmholtzSquareBubbleProblemEngine as HSBPE
from rrompy.reduction_methods.lagrange import ApproximantLagrangePade as Pade
from rrompy.utilities.parameter_sampling import QuadratureSampler as QS
from rrompy.utilities.base import squareResonances
verb = 0
-sampling = "Uniform"
ks = [1, 46 ** .5]
solver = HSBPE(kappa = 12 ** .5, theta = np.pi / 3, n = 20,
verbosity = verb)
k0 = np.mean(np.power(ks, 2.)) ** .5
k0 = 3.46104724
solver.omega = np.real(k0)
rescaling = lambda x: np.power(x, 2.)
rescalingInv = lambda x: np.power(x, .5)
-if sampling == "Uniform":
- sampler = QS(ks, "UNIFORM", rescaling, rescalingInv)
+sampler = QS(ks, "UNIFORM", rescaling, rescalingInv)
nsets = 15
-paramsPade = {'S':2, 'POD':True, 'sampler':sampler}
+paramsPade = {'S':2, 'POD':True, 'basis':"LEGENDRE", 'sampler':sampler}
approx = Pade(solver, mu0 = k0, approxParameters = paramsPade,
verbosity = verb)
poles = [None] * (nsets - 1)
polesexact = np.unique(np.power(squareResonances(ks[0]**2., ks[1]**2., False),
.5))
for i in range(1, nsets):
print("N = {}".format(4 * i))
approx.approxParameters = {'N': 4 * i, 'M': 4 * i, 'S': 4 * i + 1}
approx.setupApprox()
poles[i - 1] = approx.getPoles()
for i in range(1, nsets):
plt.figure()
plt.plot(np.real(poles[i - 1]), np.imag(poles[i - 1]), 'kx')
plt.plot(polesexact, np.zeros_like(polesexact), 'm.')
plt.plot(k0, 0, 'r*')
plt.xlim(ks)
plt.ylim((ks[0] - ks[1]) / 2., (ks[1] - ks[0]) / 2.)
plt.title("N = {}, Neff = {}".format(4 * i, len(poles[i - 1])))
plt.grid()
plt.show()
plt.close()
\ No newline at end of file
diff --git a/examples/pod/LagrangeSweep.py b/examples/pod/LagrangeSweep.py
index 45be186..c291cd0 100644
--- a/examples/pod/LagrangeSweep.py
+++ b/examples/pod/LagrangeSweep.py
@@ -1,102 +1,108 @@
from copy import copy
import numpy as np
-#from rrompy.hfengines.scipy import HelmholtzBoxScatteringProblemEngine as HBSPE
-from rrompy.hfengines.scipy import HelmholtzCavityScatteringProblemEngine as HBSPE
+#from rrompy.hfengines.scipy import HelmholtzBoxScatteringProblemEngine as \
+# HBSPE
+from rrompy.hfengines.scipy import HelmholtzCavityScatteringProblemEngine as \
+ HBSPE
from rrompy.reduction_methods.lagrange import ApproximantLagrangePade as Pade
from rrompy.reduction_methods.lagrange import ApproximantLagrangeRB as RB
from rrompy.utilities.parameter_sweeper import ParameterSweeper as Sweeper
from rrompy.utilities.parameter_sampling import QuadratureSampler as QS
from rrompy.utilities.parameter_sampling import WarpingFunction as WF
from rrompy.utilities.parameter_sampling import WarpingSampler as WS
verb = 0
npoints = 100
homog = True
homog = False
LSratio = 2. / 3.
sampling = "Uniform"
#sampling = "Cheby"
#sampling = "SinC"
assert LSratio <= 1. + np.finfo(float).eps
ks = [10 + 0.j, 14 + 0.j]
solver = HBSPE(kappa = 3, n = 15)
solver.omega = np.real(np.mean(ks))
mutars = np.linspace(9, 15, npoints)
homogMSG = "Homog"
if not homog: homogMSG = "Non" + homogMSG
filenamebase = '../data/output/ScatteringSquareLSweep'
#filenamebase = '../data/plots/LagrangeScatteringSquare1p5'
#filenamebase = filenamebase + sampling + "/HelmholtzBoxLSweep" + homogMSG
k0 = np.mean(ks)
shift = 7
shift = np.int(8 / LSratio - 1)
nsets = 3
stride = np.int(8 / LSratio)
Smax = stride * (nsets - 1) + shift + 2
rescaling = lambda x: np.power(x, 2.)
rescalingInv = lambda x: np.power(x, .5)
if sampling == "Uniform":
+ polyBasis = "MONOMIAL"
sampler = QS(ks, "UNIFORM", rescaling, rescalingInv)
if sampling == "Cheby":
+ polyBasis = "CHEBYSHEV"
sampler = QS(ks, "CHEBYSHEV", rescaling, rescalingInv)
if sampling == "SinC":
- warping = WF(call = lambda x: x - 2. * (1. - LSratio) / np.pi * np.sin(np.pi * x),
+ polyBasis = "MONOMIAL"
+ warping = WF(call = lambda x: (x - 2. * (1. - LSratio) / np.pi
+ * np.sin(np.pi * x)),
repr = "x-{}*sin(pi*x)".format(2. * (1. - LSratio) / np.pi))
sampler = WS(ks, warping, rescaling, rescalingInv)
-paramsPade = {'S':Smax, 'POD':True, 'sampler':sampler}
+paramsPade = {'S':Smax, 'POD':True, 'basis':polyBasis, 'sampler':sampler}
paramsRB = copy(paramsPade)
paramsPoly = copy(paramsPade)
paramsSetsPade = [None] * nsets
paramsSetsRB = [None] * nsets
paramsSetsPoly = [None] * nsets
for i in range(nsets):
paramsSetsPade[i] = {'N': np.int(LSratio * (stride * i + shift + 1)),
'M': np.int(LSratio * (stride * i + shift + 1)),
'S': stride * i + shift + 2}
paramsSetsRB[i] = {'R': np.int(LSratio * (stride * i + shift + 1)),
'S': stride * i + shift + 2}
paramsSetsPoly[i] = {'N': 0,
'M': np.int(LSratio * (stride * i + shift + 1)),
'S': stride * i + shift + 2}
appPade = Pade(solver, mu0 = k0, approxParameters = paramsPade,
verbosity = verb, homogeneized = homog)
appRB = RB(solver, mu0 = k0, approxParameters = paramsRB,
verbosity = verb, homogeneized = homog)
appPoly = Pade(solver, mu0 = k0, approxParameters = paramsPoly,
verbosity = verb, homogeneized = homog)
sweeper = Sweeper(mutars = mutars, mostExpensive = 'Approx')
sweeper.ROMEngine = appPade
sweeper.params = paramsSetsPade
filenamePade = sweeper.sweep(filenamebase + 'Pade.dat')
sweeper.ROMEngine = appRB
sweeper.params = paramsSetsRB
filenameRB = sweeper.sweep(filenamebase + 'RB.dat')
sweeper.ROMEngine = appPoly
sweeper.params = paramsSetsPoly
filenamePoly = sweeper.sweep(filenamebase + 'Poly.dat')
sweeper.plotCompare([filenamePade, filenameRB, filenamePoly], ['muRe'],
['normHF', 'normApp'], ['S'], onePlot = True,
save = filenamebase + 'Norm', ylims = {'top' : 1e1},
saveFormat = "png", labels = ["Pade'", "RB", "Poly"],
# figsize = (5, 3.75))
figsize = (10, 7.5))
sweeper.plotCompare([filenamePade, filenameRB, filenamePoly], ['muRe'],
['normResRel'], ['S'], save = filenamebase + 'Res',
ylims = {'top' : 1e1}, saveFormat = "png",
labels = ["Pade'", "RB", "Poly"],
# figsize = (5, 3.75))
figsize = (10, 7.5))
sweeper.plotCompare([filenamePade, filenameRB, filenamePoly], ['muRe'],
['normErrRel'], ['S'], save = filenamebase + 'Err',
ylims = {'top' : 1e1}, saveFormat = "png",
labels = ["Pade'", "RB", "Poly"],
# figsize = (5, 3.75))
figsize = (10, 7.5))
diff --git a/examples/pod/LagrangeSweepUnstable.py b/examples/pod/LagrangeSweepUnstable.py
index 242b57d..27836aa 100644
--- a/examples/pod/LagrangeSweepUnstable.py
+++ b/examples/pod/LagrangeSweepUnstable.py
@@ -1,75 +1,75 @@
from copy import copy
import numpy as np
from rrompy.hfengines.scipy import HelmholtzSquareBubbleProblemEngine as HSBPE
from rrompy.reduction_methods.lagrange import ApproximantLagrangePade as Pade
from rrompy.reduction_methods.lagrange import ApproximantLagrangeRB as RB
from rrompy.utilities.parameter_sweeper import ParameterSweeper as Sweeper
from rrompy.utilities.parameter_sampling import ManualSampler as MS
npoints = 50
solver = HSBPE(kappa = 12 ** .5, theta = np.pi / 3, n = 30, verbosity = 0)
mutars = np.linspace(6**.5, 16**.5, npoints)
filenamebase = '../data/output/HelmholtzBubbleLagrange'
k0 = np.mean(mutars)
rescaling = lambda x: np.power(x, 2.)
rescalingInv = lambda x: np.power(x, .5)
paramsChoices = {
'Stable':
- {'S':4, 'POD':True,
+ {'S':4, 'POD':True, 'basis':"MONOMIAL",
'sampler':MS([6**.5, 16**.5], [[14**.5], [12**.5], [9**.5], [11**.5]])},
'Unstable':
- {'S':4, 'POD':True,
+ {'S':4, 'POD':True, 'basis':"MONOMIAL",
'sampler':MS([6**.5, 16**.5], [[8**.5], [10**.5], [13**.5], [11**.5]])}
}
j = 0
filenamePade = [None] * len(paramsChoices.keys())
filenameRB = [None] * len(paramsChoices.keys())
for typeP in paramsChoices.keys():
paramsPade = paramsChoices[typeP]
paramsRB = copy(paramsPade)
paramsSetsPade = [{'N': 3, 'M': 3}]
paramsSetsRB = [{'R': 4}]
appPade = Pade(solver, mu0 = k0, approxParameters = paramsPade,
verbosity = 0)
appRB = RB(solver, mu0 = k0, approxParameters = paramsRB,
verbosity = 0)
sweeper = Sweeper(mutars = mutars, mostExpensive = 'Approx')
sweeper.ROMEngine = appPade
sweeper.params = paramsSetsPade
filenamePade[j] = sweeper.sweep(filenamebase + 'Pade{}.dat'.format(typeP),
verbose = 0)
sweeper.ROMEngine = appRB
sweeper.params = paramsSetsRB
filenameRB[j] = sweeper.sweep(filenamebase + 'RB{}.dat'.format(typeP),
verbose = 0)
j += 1
print("Pade'")
sweeper.plotCompare(filenamePade, ['muRe'],
['normHF', 'normApp'], ['S'], onePlot = True,
save = filenamebase + 'PadeNorm', saveFormat = "png",
labels = list(paramsChoices.keys()))
sweeper.plotCompare(filenamePade, ['muRe'], ['normResRel'],
['S'], save = filenamebase + 'PadeRes', saveFormat = "png",
labels = list(paramsChoices.keys()))
sweeper.plotCompare(filenamePade, ['muRe'], ['normErrRel'],
['S'], save = filenamebase + 'PadeErr', saveFormat = "png",
labels = list(paramsChoices.keys()))
print("RB")
sweeper.plotCompare(filenameRB, ['muRe'],
['normHF', 'normApp'], ['S'], onePlot = True,
save = filenamebase + 'RBNorm', saveFormat = "png",
labels = list(paramsChoices.keys()))
sweeper.plotCompare(filenameRB, ['muRe'], ['normResRel'],
['S'], save = filenamebase + 'RBRes', saveFormat = "png",
labels = list(paramsChoices.keys()))
sweeper.plotCompare(filenameRB, ['muRe'], ['normErrRel'],
['S'], save = filenamebase + 'RBErr', saveFormat = "png",
labels = list(paramsChoices.keys()))
diff --git a/examples/pod/PadeLagrange.py b/examples/pod/PadeLagrange.py
index df442a8..f4eb9c2 100644
--- a/examples/pod/PadeLagrange.py
+++ b/examples/pod/PadeLagrange.py
@@ -1,107 +1,111 @@
import numpy as np
from rrompy.hfengines.scipy import HelmholtzSquareBubbleProblemEngine as HSBPE
-from rrompy.hfengines.scipy import HelmholtzSquareTransmissionProblemEngine as HSTPE
+from rrompy.hfengines.scipy import HelmholtzSquareTransmissionProblemEngine \
+ as HSTPE
from rrompy.hfengines.scipy import HelmholtzBoxScatteringProblemEngine as HBSPE
from rrompy.reduction_methods.lagrange import ApproximantLagrangePade as Pade
from rrompy.utilities.parameter_sampling import QuadratureSampler as QS
-testNo = 2
+testNo = 1
verb = 5
+polyBasis = "CHEBYSHEV"
+polyBasis = "LEGENDRE"
+#polyBasis = "MONOMIAL"
homog = True
-#homog = False
+homog = False
if testNo == 1:
k0s = np.power([10 + 0.j, 14 + 0.j], .5)
k0 = np.mean(k0s)
ktar = (11 + 0.j) ** .5
rescaling = lambda x: np.power(x, 2.)
rescalingInv = lambda x: np.power(x, .5)
- params = {'N':4, 'M':3, 'S':5, 'POD':True,
+ params = {'N':4, 'M':3, 'S':5, 'POD':True, 'basis':polyBasis,
'sampler':QS(k0s, "CHEBYSHEV", rescaling, rescalingInv)}
solver = HSBPE(kappa = 12 ** .5, theta = np.pi / 3, n = 40,
verbosity = verb)
solver.omega = np.real(k0)
approx = Pade(solver, mu0 = k0, approxParameters = params,
verbosity = verb)
approx.setupApprox()
# approx.plotSamples()
approx.plotApp(ktar, name = 'u_Pade''')
approx.plotHF(ktar, name = 'u_HF')
approx.plotErr(ktar, name = 'err')
approx.plotRes(ktar, name = 'res')
appErr, solNorm = approx.normErr(ktar), approx.normHF(ktar)
resNorm, RHSNorm = approx.normRes(ktar), approx.normRHS(ktar)
print(('SolNorm:\t{}\nErr:\t{}\nErrRel:\t{}').format(solNorm, appErr,
np.divide(appErr, solNorm)))
print(('RHSNorm:\t{}\nRes:\t{}\nResRel:\t{}').format(RHSNorm, resNorm,
np.divide(resNorm, RHSNorm)))
print('\nPoles Pade'':')
print(approx.getPoles())
############
elif testNo == 2:
k0s = [3.85 + 0.j, 4.15 + 0.j]
k0 = np.mean(k0s)
ktar = 4 + 0.j
rescaling = lambda x: np.power(x, 2.)
rescalingInv = lambda x: np.power(x, .5)
- params = {'N':8, 'M':9, 'S':10, 'POD':True,
+ params = {'N':8, 'M':9, 'S':10, 'POD':True, 'basis':polyBasis,
'sampler':QS(k0s, "CHEBYSHEV", rescaling, rescalingInv)}
solver = HSTPE(nT = 2, nB = 1, theta = np.pi * 45/180, kappa = 4., n = 50,
verbosity = verb)
solver.omega = np.real(k0)
approx = Pade(solver, mu0 = k0, approxParameters = params,
verbosity = verb, homogeneized = homog)
approx.setupApprox()
# approx.plotSamples()
approx.plotApp(ktar, name = 'u_Pade''')
approx.plotHF(ktar, name = 'u_HF')
approx.plotErr(ktar, name = 'err')
approx.plotRes(ktar, name = 'res')
appErr, solNorm = approx.normErr(ktar), approx.normHF(ktar)
resNorm, RHSNorm = approx.normRes(ktar), approx.normRHS(ktar)
print(('SolNorm:\t{}\nErr:\t{}\nErrRel:\t{}').format(solNorm, appErr,
np.divide(appErr, solNorm)))
print(('RHSNorm:\t{}\nRes:\t{}\nResRel:\t{}').format(RHSNorm, resNorm,
np.divide(resNorm, RHSNorm)))
print('\nPoles Pade'':')
print(approx.getPoles())
############
elif testNo == 3:
k0s = [2, 5]
k0 = np.mean(k0s)
ktar = 4.5 - .1j
- params = {'N':10, 'M':10, 'S':11, 'POD':True,
+ params = {'N':10, 'M':10, 'S':11, 'POD':True, 'basis':polyBasis,
'sampler':QS(k0s, "CHEBYSHEV")}
solver = HBSPE(R = 7, kappa = 3, theta = - np.pi * 75 / 180, n = 40,
verbosity = verb)
solver.omega = np.real(k0)
approx = Pade(solver, mu0 = k0, approxParameters = params,
verbosity = verb, homogeneized = homog)
approx.setupApprox()
approx.plotSamples()
approx.plotApp(ktar, name = 'u_Pade''')
approx.plotHF(ktar, name = 'u_HF')
approx.plotErr(ktar, name = 'err')
approx.plotRes(ktar, name = 'res')
appErr, solNorm = approx.normErr(ktar), approx.normHF(ktar)
resNorm, RHSNorm = approx.normRes(ktar), approx.normRHS(ktar)
print(('SolNorm:\t{}\nErr:\t{}\nErrRel:\t{}').format(solNorm, appErr,
np.divide(appErr, solNorm)))
print(('RHSNorm:\t{}\nRes:\t{}\nResRel:\t{}').format(RHSNorm, resNorm,
np.divide(resNorm, RHSNorm)))
print('\nPoles Pade'':')
print(approx.getPoles())
diff --git a/examples/pod/PadeOrthogonalLagrange.py b/examples/pod/PadeOrthogonalLagrange.py
deleted file mode 100644
index 254e04f..0000000
--- a/examples/pod/PadeOrthogonalLagrange.py
+++ /dev/null
@@ -1,107 +0,0 @@
-import numpy as np
-from rrompy.hfengines.scipy import HelmholtzSquareBubbleProblemEngine as HSBPE
-from rrompy.hfengines.scipy import HelmholtzSquareTransmissionProblemEngine as HSTPE
-from rrompy.hfengines.scipy import HelmholtzBoxScatteringProblemEngine as HBSPE
-from rrompy.reduction_methods.lagrange import ApproximantLagrangePadeOrthogonal as Pade
-from rrompy.utilities.parameter_sampling import QuadratureSampler as QS
-
-testNo = 2
-verb = 5
-homog = True
-#homog = False
-
-if testNo == 1:
- k0s = np.power([10 + 0.j, 14 + 0.j], .5)
- k0 = np.mean(k0s)
- ktar = (11 + 0.j) ** .5
-
- rescaling = lambda x: np.power(x, 2.)
- rescalingInv = lambda x: np.power(x, .5)
- params = {'N':4, 'M':3, 'S':5, 'POD':True,
- 'sampler':QS(k0s, "CHEBYSHEV", rescaling, rescalingInv)}
-
- solver = HSBPE(kappa = 12 ** .5, theta = np.pi / 3, n = 40,
- verbosity = verb)
- solver.omega = np.real(k0)
- approx = Pade(solver, mu0 = k0, approxParameters = params,
- verbosity = verb)
-
- approx.setupApprox()
-# approx.plotSamples()
- approx.plotApp(ktar, name = 'u_Pade''')
- approx.plotHF(ktar, name = 'u_HF')
- approx.plotErr(ktar, name = 'err')
- approx.plotRes(ktar, name = 'res')
-
- appErr, solNorm = approx.normErr(ktar), approx.normHF(ktar)
- resNorm, RHSNorm = approx.normRes(ktar), approx.normRHS(ktar)
- print(('SolNorm:\t{}\nErr:\t{}\nErrRel:\t{}').format(solNorm, appErr,
- np.divide(appErr, solNorm)))
- print(('RHSNorm:\t{}\nRes:\t{}\nResRel:\t{}').format(RHSNorm, resNorm,
- np.divide(resNorm, RHSNorm)))
- print('\nPoles Pade'':')
- print(approx.getPoles())
-
-############
-elif testNo == 2:
- k0s = [3.85 + 0.j, 4.15 + 0.j]
- k0 = np.mean(k0s)
- ktar = 4 + 0.j
-
- rescaling = lambda x: np.power(x, 2.)
- rescalingInv = lambda x: np.power(x, .5)
- params = {'N':8, 'M':9, 'S':10, 'POD':True,
- 'sampler':QS(k0s, "CHEBYSHEV", rescaling, rescalingInv)}
-
- solver = HSTPE(nT = 2, nB = 1, theta = np.pi * 45/180, kappa = 4., n = 50,
- verbosity = verb)
- solver.omega = np.real(k0)
- approx = Pade(solver, mu0 = k0, approxParameters = params,
- verbosity = verb, homogeneized = homog)
-
- approx.setupApprox()
-# approx.plotSamples()
- approx.plotApp(ktar, name = 'u_Pade''')
- approx.plotHF(ktar, name = 'u_HF')
- approx.plotErr(ktar, name = 'err')
- approx.plotRes(ktar, name = 'res')
-
- appErr, solNorm = approx.normErr(ktar), approx.normHF(ktar)
- resNorm, RHSNorm = approx.normRes(ktar), approx.normRHS(ktar)
- print(('SolNorm:\t{}\nErr:\t{}\nErrRel:\t{}').format(solNorm, appErr,
- np.divide(appErr, solNorm)))
- print(('RHSNorm:\t{}\nRes:\t{}\nResRel:\t{}').format(RHSNorm, resNorm,
- np.divide(resNorm, RHSNorm)))
- print('\nPoles Pade'':')
- print(approx.getPoles())
-
-############
-elif testNo == 3:
- k0s = [2, 5]
- k0 = np.mean(k0s)
- ktar = 4.5 - .1j
- params = {'N':10, 'M':10, 'S':11, 'POD':True,
- 'sampler':QS(k0s, "CHEBYSHEV")}
-
- solver = HBSPE(R = 7, kappa = 3, theta = - np.pi * 75 / 180, n = 40,
- verbosity = verb)
- solver.omega = np.real(k0)
- approx = Pade(solver, mu0 = k0, approxParameters = params,
- verbosity = verb, homogeneized = homog)
-
- approx.setupApprox()
- approx.plotSamples()
- approx.plotApp(ktar, name = 'u_Pade''')
- approx.plotHF(ktar, name = 'u_HF')
- approx.plotErr(ktar, name = 'err')
- approx.plotRes(ktar, name = 'res')
-
- appErr, solNorm = approx.normErr(ktar), approx.normHF(ktar)
- resNorm, RHSNorm = approx.normRes(ktar), approx.normRHS(ktar)
- print(('SolNorm:\t{}\nErr:\t{}\nErrRel:\t{}').format(solNorm, appErr,
- np.divide(appErr, solNorm)))
- print(('RHSNorm:\t{}\nRes:\t{}\nResRel:\t{}').format(RHSNorm, resNorm,
- np.divide(resNorm, RHSNorm)))
- print('\nPoles Pade'':')
- print(approx.getPoles())
-
diff --git a/examples/pod/PadeTaylor.py b/examples/pod/PadeTaylor.py
index 5461db0..ed114a5 100644
--- a/examples/pod/PadeTaylor.py
+++ b/examples/pod/PadeTaylor.py
@@ -1,97 +1,97 @@
import numpy as np
from rrompy.hfengines.scipy import HelmholtzSquareBubbleProblemEngine as HSBPE
from rrompy.hfengines.scipy import (
HelmholtzSquareTransmissionProblemEngine as HSTPE)
from rrompy.hfengines.scipy import HelmholtzBoxScatteringProblemEngine as HBSPE
from rrompy.reduction_methods.taylor import ApproximantTaylorPade as Pade
-testNo = 2
-verb = 0
+testNo = 1
+verb = 5
homog = True
#homog = False
if testNo == 1:
params = {'N':4, 'M':3, 'E':4, 'sampleType':'Arnoldi', 'POD':True}
k0 = 12 ** .5
ktar = 10.5 ** .5
solver = HSBPE(kappa = 12 ** .5, theta = np.pi / 3, n = 40,
verbosity = verb)
solver.omega = np.real(k0)
approx = Pade(solver, mu0 = k0, approxParameters = params,
verbosity = verb)
approx.setupApprox()
# approx.plotSamples()
approx.plotApp(ktar, name = 'u_Pade''')
approx.plotHF(ktar, name = 'u_HF')
approx.plotErr(ktar, name = 'err')
approx.plotRes(ktar, name = 'res')
appErr, solNorm = approx.normErr(ktar), approx.normHF(ktar)
resNorm, RHSNorm = approx.normRes(ktar), approx.normRHS(ktar)
print(('SolNorm:\t{}\nErr:\t{}\nErrRel:\t{}').format(solNorm, appErr,
np.divide(appErr, solNorm)))
print(('RHSNorm:\t{}\nRes:\t{}\nResRel:\t{}').format(RHSNorm, resNorm,
np.divide(resNorm, RHSNorm)))
print('\nPoles Pade'':')
print(approx.getPoles())
############
elif testNo == 2:
params = {'N':6, 'M':7, 'E':7, 'sampleType':'Arnoldi', 'POD':True}
k0 = 16 ** .5
ktar = 15 ** .5
solver = HSTPE(nT = 2, nB = 1, theta = np.pi * 45/180, kappa = 4., n = 50,
verbosity = verb)
solver.omega = np.real(k0)
approx = Pade(solver, mu0 = k0, approxParameters = params,
verbosity = verb, homogeneized = homog)
approx.setupApprox()
# approx.plotSamples()
approx.plotApp(ktar, name = 'u_Pade''')
approx.plotHF(ktar, name = 'u_HF')
approx.plotErr(ktar, name = 'err')
approx.plotRes(ktar, name = 'res')
appErr, solNorm = approx.normErr(ktar), approx.normHF(ktar)
resNorm, RHSNorm = approx.normRes(ktar), approx.normRHS(ktar)
print(('SolNorm:\t{}\nErr:\t{}\nErrRel:\t{}').format(solNorm, appErr,
np.divide(appErr, solNorm)))
print(('RHSNorm:\t{}\nRes:\t{}\nResRel:\t{}').format(RHSNorm, resNorm,
np.divide(resNorm, RHSNorm)))
print('\nPoles Pade'':')
print(approx.getPoles())
############
elif testNo in [3, 4]:
if testNo == 3:
params = {'N':7, 'M':8, 'E':8, 'sampleType':'Krylov', 'POD':True}
else:
params = {'N':7, 'M':8, 'E':8, 'sampleType':'Arnoldi', 'POD':True}
k0 = 3
ktar = 4.+0.j
solver = HBSPE(R = 5, kappa = 3, theta = - np.pi * 75 / 180, n = 30,
verbosity = verb)
solver.omega = np.real(k0)
approx = Pade(solver, mu0 = k0, approxParameters = params,
verbosity = verb, homogeneized = homog)
approx.setupApprox()
approx.plotSamples()
approx.plotApp(ktar, name = 'u_Pade''')
approx.plotHF(ktar, name = 'u_HF')
approx.plotErr(ktar, name = 'err')
approx.plotRes(ktar, name = 'res')
appErr, solNorm = approx.normErr(ktar), approx.normHF(ktar)
resNorm, RHSNorm = approx.normRes(ktar), approx.normRHS(ktar)
print(('SolNorm:\t{}\nErr:\t{}\nErrRel:\t{}').format(solNorm, appErr,
np.divide(appErr, solNorm)))
print(('RHSNorm:\t{}\nRes:\t{}\nResRel:\t{}').format(RHSNorm, resNorm,
np.divide(resNorm, RHSNorm)))
print('\nPoles Pade'':')
print(approx.getPoles())
diff --git a/examples/pod/airfoil.py b/examples/pod/airfoil.py
index 2121fff..2b6169e 100644
--- a/examples/pod/airfoil.py
+++ b/examples/pod/airfoil.py
@@ -1,212 +1,212 @@
from copy import deepcopy as copy
import numpy as np
import fenics as fen
import ufl
from rrompy.hfengines.scipy import HelmholtzBoxScatteringProblemEngine as HSP
from rrompy.reduction_methods.taylor import ApproximantTaylorPade as TP
from rrompy.reduction_methods.taylor import ApproximantTaylorRB as TRB
from rrompy.reduction_methods.lagrange import ApproximantLagrangePade as LP
from rrompy.reduction_methods.lagrange import ApproximantLagrangeRB as LRB
from rrompy.utilities.parameter_sweeper import ParameterSweeper as Sweeper
from rrompy.utilities.parameter_sampling import QuadratureSampler as QS
from rrompy.utilities.base.fenics import fenONE
from operator import itemgetter
def subdict(d, ks):
return dict(zip(ks, itemgetter(*ks)(d)))
verb = 0
####################
homog = True
#homog = False
####################
test = "solve"
test = "Taylor"
test = "Lagrange"
test = "TaylorSweep"
test = "LagrangeSweep"
plotSamples = True
k0 = 10
kLeft, kRight = 8 + 0.j, 12 + 0.j
ktar = 11
ktars = np.linspace(8, 12, 21) + 0.j
PI = np.pi
R = 2
def Dboundary(x, on_boundary):
return on_boundary and (x[0]**2+x[1]**2)**.5 < .95 * R
kappa = 10
theta = PI * - 45 / 180.
mu = 1.1
epsilon = .1
mesh = fen.Mesh('../data/mesh/airfoil.xml')
c, s = np.cos(theta), np.sin(theta)
x, y = fen.SpatialCoordinate(mesh)[:]
u0R = - fen.cos(kappa * (c * x + s * y))
u0I = - fen.sin(kappa * (c * x + s * y))
checkReal = x**2-x+y**2
rhop5 = ((x**2+y**2)/((x-1)**2+y**2))**.25
phiroot1 = fen.atan(-y/(x**2-x+y**2)) / 2
phiroot2 = fen.atan(-y/(x**2-x+y**2)) / 2 - PI * ufl.sign(-y/(x**2-x+y**2)) / 2
kappam1 = (((rhop5*fen.cos(phiroot1)+.5)**2.+(rhop5*fen.sin(phiroot1))**2.)/
((rhop5*fen.cos(phiroot1)-1.)**2.+(rhop5*fen.sin(phiroot1))**2.)
)**.5 - mu
kappam2 = (((rhop5*fen.cos(phiroot2)+.5)**2.+(rhop5*fen.sin(phiroot2))**2.)/
((rhop5*fen.cos(phiroot2)-1.)**2.+(rhop5*fen.sin(phiroot2))**2.)
)**.5 - mu
Heps1 = .9 * .5 * (1 + kappam1/epsilon + fen.sin(PI*kappam1/epsilon) / PI) + .1
Heps2 = .9 * .5 * (1 + kappam2/epsilon + fen.sin(PI*kappam2/epsilon) / PI) + .1
cTT = ufl.conditional(ufl.le(kappam1, epsilon), Heps1, fenONE)
c_F = fen.Constant(.1)
cFT = ufl.conditional(ufl.le(kappam2, epsilon), Heps2, fenONE)
c_F = fen.Constant(.1)
cT = ufl.conditional(ufl.ge(kappam1, - epsilon), cTT, c_F)
cF = ufl.conditional(ufl.ge(kappam2, - epsilon), cFT, c_F)
a = ufl.conditional(ufl.ge(checkReal, 0.), cT, cF)
###
solver = HSP(R, np.abs(k0), theta, n = 1, verbosity = verb,
degree_threshold = 8)
solver.V = fen.FunctionSpace(mesh, "P", 3)
solver.diffusivity = a
solver.DirichletBoundary = Dboundary
solver.RobinBoundary = "REST"
solver.DirichletDatum = [u0R, u0I]
###
if test == "solve":
uinc = solver.liftDirichletData(k0)
if homog:
uhtot = solver.solve(k0, homogeneized = homog)
uh = uhtot + uinc
else:
uh = solver.solve(k0, homogeneized = homog)
uhtot = uh - uinc
print(solver.norm(uh))
print(solver.norm(uhtot))
solver.plot(fen.project(a, solver.V).vector(), what = 'Real',
name = 'a')
solver.plot(uinc, what = 'Real', name = 'u_inc')
solver.plot(uh, what = 'ABS')
solver.plot(uhtot, what = 'ABS', name = 'u + u_inc')
elif test in ["Taylor", "Lagrange"]:
if test == "Taylor":
params = {'N':8, 'M':8, 'R':8, 'E':8,
'sampleType':'Arnoldi', 'POD':True}
parPade = subdict(params, ['N', 'M', 'E', 'sampleType', 'POD'])
parRB = subdict(params, ['R', 'E', 'sampleType', 'POD'])
approxPade = TP(solver, mu0 = k0, approxParameters = parPade,
verbosity = verb, homogeneized = homog)
approxRB = TRB(solver, mu0 = k0, approxParameters = parRB,
verbosity = verb, homogeneized = homog)
else:
- params = {'N':8, 'M':8, 'R':9, 'S':9, 'POD':True,
+ params = {'N':8, 'M':8, 'R':9, 'S':9, 'POD':True, 'basis':"CHEBYSHEV",
'sampler':QS([kLeft, kRight], "CHEBYSHEV")}
- parPade = subdict(params, ['N', 'M', 'S', 'POD', 'sampler'])
+ parPade = subdict(params, ['N', 'M', 'S', 'POD', 'sampler', 'basis'])
parRB = subdict(params, ['R', 'S', 'POD', 'sampler'])
approxPade = LP(solver, mu0 = np.mean([kLeft, kRight]),
approxParameters = parPade, verbosity = verb,
homogeneized = homog)
approxRB = LRB(solver, mu0 = np.mean([kLeft, kRight]),
approxParameters = parRB, verbosity = verb,
homogeneized = homog)
approxPade.setupApprox()
approxRB.setupApprox()
if plotSamples:
approxPade.plotSamples()
approxPade.plotHF(ktar, name = 'u_HF')
approxPade.plotApp(ktar, name = 'u_Pade''')
approxPade.plotErr(ktar, name = 'err_Pade''')
approxPade.plotRes(ktar, name = 'res_Pade''')
approxRB.plotApp(ktar, name = 'u_RB')
approxRB.plotErr(ktar, name = 'err_RB')
approxRB.plotRes(ktar, name = 'res_RB')
HFNorm, RHSNorm = approxPade.normHF(ktar), approxPade.normRHS(ktar)
PadeRes, PadeErr = approxPade.normRes(ktar), approxPade.normErr(ktar)
RBRes, RBErr = approxRB.normRes(ktar), approxRB.normErr(ktar)
print('HFNorm:\t{}\nRHSNorm:\t{}'.format(HFNorm, RHSNorm))
print('PadeRes:\t{}\nPadeErr:\t{}'.format(PadeRes, PadeErr))
print('RBRes:\t{}\nRBErr:\t{}'.format(RBRes, RBErr))
print('\nPoles Pade'':')
print(approxPade.getPoles())
elif test in ["TaylorSweep", "LagrangeSweep"]:
if test == "TaylorSweep":
shift = 10
nsets = 2
stride = 3
Emax = stride * (nsets - 1) + shift + 1
params = {'Emax':Emax, 'sampleType':'Arnoldi', 'POD':True}
paramsSetsPade = [None] * nsets
paramsSetsRB = [None] * nsets
for i in range(nsets):
paramsSetsPade[i] = {'N':stride*i+shift + 1, 'M':stride*i+shift+1,
'E':stride*i+shift + 1}
paramsSetsRB[i] = {'E':stride*i+shift + 1,'R':stride*i+shift + 2}
approxPade = TP(solver, mu0 = kappa,approxParameters = params,
verbosity = verb, homogeneized = homog)
approxRB = TRB(solver, mu0 = kappa, approxParameters = params,
verbosity = verb, homogeneized = homog)
else:
shift = 10
nsets = 2
stride = 3
Smax = stride * (nsets - 1) + shift + 2
- paramsPade = {'S':Smax, 'POD':True,
+ paramsPade = {'S':Smax, 'POD':True, 'basis':"CHEBYSHEV",
'sampler':QS([kLeft, kRight], "CHEBYSHEV")}
paramsRB = copy(paramsPade)
paramsSetsPade = [None] * nsets
paramsSetsRB = [None] * nsets
for i in range(nsets):
paramsSetsPade[i] = {'N': stride*i+shift+1, 'M': stride*i+shift+1,
'S': stride*i+shift+2}
paramsSetsRB[i] = {'R': stride*i+shift+2, 'S': stride*i+shift+2}
approxPade = LP(solver, mu0 = np.mean([kLeft, kRight]),
approxParameters = paramsPade, verbosity = verb,
homogeneized = homog)
approxRB = LRB(solver, mu0 = np.mean([kLeft, kRight]),
approxParameters = paramsRB, verbosity = verb,
homogeneized = homog)
sweeper = Sweeper(mutars = ktars, mostExpensive = 'Approx')
homogMSG = ""
if not homog: homogMSG = "Non"
filenamebase = '../data/output/airfoil' + test[:-5] + homogMSG + "Homog"
sweeper.ROMEngine = approxPade
sweeper.params = paramsSetsPade
filenamePade = sweeper.sweep(filenamebase +'Pade.dat')
sweeper.ROMEngine = approxRB
sweeper.params = paramsSetsRB
filenameRB = sweeper.sweep(filenamebase +'RB.dat')
if test == "TaylorSweep":
constr = ['E']
else:
constr = ['S']
sweeper.plotCompare([filenamePade, filenameRB], ['muRe'],
['normHF', 'normApp'], constr, onePlot = True,
save = filenamebase + 'Norm',
saveFormat = "png", labels = ["Pade'", "RB"])
sweeper.plotCompare([filenamePade, filenameRB], ['muRe'], ['normResRel'],
constr, save = filenamebase + 'Res',
saveFormat = "png", labels = ["Pade'", "RB"])
sweeper.plotCompare([filenamePade, filenameRB], ['muRe'], ['normErrRel'],
constr, save = filenamebase + 'Err',
saveFormat = "png", labels = ["Pade'", "RB"])
diff --git a/examples/pod/scatteringSquare.py b/examples/pod/scatteringSquare.py
index ffb92c7..5832d25 100644
--- a/examples/pod/scatteringSquare.py
+++ b/examples/pod/scatteringSquare.py
@@ -1,166 +1,172 @@
from copy import copy
import numpy as np
from rrompy.hfengines.scipy import (
HelmholtzCavityScatteringProblemEngine as CSPE)
from rrompy.reduction_methods.taylor import ApproximantTaylorPade as TP
from rrompy.reduction_methods.lagrange import ApproximantLagrangePade as LP
from rrompy.reduction_methods.taylor import ApproximantTaylorRB as TRB
from rrompy.reduction_methods.lagrange import ApproximantLagrangeRB as LRB
from rrompy.utilities.parameter_sweeper import ParameterSweeper as Sweeper
from rrompy.utilities.parameter_sampling import QuadratureSampler as QS
from operator import itemgetter
def subdict(d, ks):
return dict(zip(ks, itemgetter(*ks)(d)))
verb = 0
####################
test = "solve"
test = "Taylor"
test = "Lagrange"
test = "TaylorSweep"
#test = "LagrangeSweep"
plotSamples = True
k0 = 10
kLeft, kRight = 9, 11
ktar = 9.5
ktars = np.linspace(8.5, 11.5, 125)
#ktars = np.array([k0])
kappa = 5
n = 50
solver = CSPE(kappa = kappa, n = n, verbosity = verb)
solver.omega = k0
if test == "solve":
uh = solver.solve(k0)
print(solver.norm(uh))
solver.plot(uh, what = ['ABS', 'REAL'])
elif test in ["Taylor", "Lagrange"]:
if test == "Taylor":
- params = {'N':8, 'M':7, 'R':8, 'E':8, 'sampleType':'Krylov', 'POD':True}
- params = {'N':8, 'M':7, 'R':8, 'E':8, 'sampleType':'Arnoldi', 'POD':True}
+ params = {'N':8, 'M':7, 'R':8, 'E':8, 'sampleType':'Krylov',
+ 'POD':True}
+ params = {'N':8, 'M':7, 'R':8, 'E':8, 'sampleType':'Arnoldi',
+ 'POD':True}
parPade = subdict(params, ['N', 'M', 'E', 'sampleType', 'POD'])
parRB = subdict(params, ['R', 'E', 'sampleType', 'POD'])
approxPade = TP(solver, mu0 = k0, approxParameters = parPade,
verbosity = verb)
approxRB = TRB(solver, mu0 = k0, approxParameters = parRB,
verbosity = verb)
else:
- params = {'N':8, 'M':8, 'R':9, 'S':9, 'POD':True,
+ params = {'N':8, 'M':8, 'R':9, 'S':9, 'POD':True, 'basis':"MONOMIAL",
'sampler':QS([kLeft, kRight], "CHEBYSHEV")}
- parPade = subdict(params, ['N', 'M', 'S', 'POD', 'sampler'])
+ params = {'N':8, 'M':8, 'R':9, 'S':9, 'POD':True, 'basis':"CHEBYSHEV",
+ 'sampler':QS([kLeft, kRight], "CHEBYSHEV")}
+ parPade = subdict(params, ['N', 'M', 'S', 'POD', 'sampler', 'basis'])
parRB = subdict(params, ['R', 'S', 'POD', 'sampler'])
approxPade = LP(solver, mu0 = np.mean([kLeft, kRight]),
approxParameters = parPade,
verbosity = verb)
approxRB = LRB(solver, mu0 = np.mean([kLeft, kRight]),
approxParameters = parRB,
verbosity = verb)
approxPade.setupApprox()
approxRB.setupApprox()
if plotSamples:
approxPade.plotSamples()
PadeErr, solNorm = approxPade.normErr(ktar), approxPade.normHF(ktar)
RBErr = approxRB.normErr(ktar)
print(('SolNorm:\t{}\nErrPade:\t{}\nErrRelPade:\t{}\nErrRB:\t\t{}'
'\nErrRelRB:\t{}').format(solNorm, PadeErr,
np.divide(PadeErr, solNorm), RBErr,
np.divide(RBErr, solNorm)))
print('\nPoles Pade'':')
print(approxPade.getPoles())
print('\nPoles RB:')
print(approxRB.getPoles())
approxPade.plotHF(ktar, name = 'u_ex')
approxPade.plotApp(ktar, name = 'u_Pade''')
approxRB.plotApp(ktar, name = 'u_RB')
approxPade.plotErr(ktar, name = 'errPade''')
approxRB.plotErr(ktar, name = 'errRB')
elif test in ["TaylorSweep", "LagrangeSweep"]:
if test == "TaylorSweep":
shift = 5
nsets = 4
stride = 3
Emax = stride * (nsets - 1) + shift + 1
params = {'Emax':Emax, 'sampleType':'Krylov', 'POD':True}
params = {'Emax':Emax, 'sampleType':'Arnoldi', 'POD':True}
paramsSetsPade = [None] * nsets
paramsSetsRB = [None] * nsets
for i in range(nsets):
paramsSetsPade[i] = {'N': stride*i+shift+1,
'M': stride*i+shift,#+1,
'E': stride*i+shift+1}
paramsSetsRB[i] = {'R': stride*i+shift+1,#+1,
'E': stride*i+shift+1}
approxPade = TP(solver, mu0 = k0,approxParameters = params,
verbosity = verb)
approxRB = TRB(solver, mu0 = k0, approxParameters = params,
verbosity = verb)
else:
shift = 7
nsets = 4
stride = 3
Smax = stride * (nsets - 1) + shift + 2
- paramsPade = {'S':Smax, 'POD':True,
+ paramsPade = {'S':Smax, 'POD':True, 'basis':"MONOMIAL",
+ 'sampler':QS([kLeft, kRight], "CHEBYSHEV")}
+ paramsPade = {'S':Smax, 'POD':True, 'basis':"CHEBYSHEV",
'sampler':QS([kLeft, kRight], "CHEBYSHEV")}
paramsRB = copy(paramsPade)
paramsPoly = copy(paramsPade)
paramsSetsPade = [None] * nsets
paramsSetsRB = [None] * nsets
paramsSetsPoly = [None] * nsets
for i in range(nsets):
paramsSetsPade[i] = {'N': stride*i+shift+1,
'M': stride*i+shift+1,
'S': stride*i+shift+2}
paramsSetsRB[i] = {'R': stride*i+shift+2,
'S': stride*i+shift+2}
paramsSetsPoly[i] = {'N': 0,
'M': stride*i+shift+1,
'S': stride*i+shift+2}
approxPade = LP(solver, mu0 = np.mean([kLeft, kRight]),
approxParameters = paramsPade,
verbosity = verb)
approxRB = LRB(solver, mu0 = np.mean([kLeft, kRight]),
approxParameters = paramsRB,
verbosity = verb)
approxPoly = LP(solver, mu0 = np.mean([kLeft, kRight]),
approxParameters = paramsPoly,
verbosity = verb)
filenamebase = '../data/output/scatSquare' + test[:-5]
sweeper = Sweeper(mutars = ktars, mostExpensive = 'Approx')
sweeper.ROMEngine = approxPade
sweeper.params = paramsSetsPade
filenamePade = sweeper.sweep(filenamebase + 'Pade.dat')
sweeper.ROMEngine = approxRB
sweeper.params = paramsSetsRB
filenameRB = sweeper.sweep(filenamebase + 'RB.dat')
sweeper.ROMEngine = approxPoly
sweeper.params = paramsSetsPoly
filenamePoly = sweeper.sweep(filenamebase + 'Poly.dat')
if test == "TaylorSweep":
constr = ['E']
else:
constr = ['S']
sweeper.plotCompare([filenamePade, filenameRB, filenamePoly], ['muRe'],
['normHF', 'normApp'], constr, onePlot = True,
save = filenamebase + 'Norm',
saveFormat = "png", labels = ["Pade'", "RB", "Poly"])
sweeper.plotCompare([filenamePade, filenameRB, filenamePoly], ['muRe'],
['normResRel'], constr, save = filenamebase + 'Res',
saveFormat = "png", labels = ["Pade'", "RB", "Poly"])
sweeper.plotCompare([filenamePade, filenameRB, filenamePoly], ['muRe'],
['normErrRel'], constr, save = filenamebase + 'Err',
saveFormat = "png", labels = ["Pade'", "RB", "Poly"])
diff --git a/rrompy/hfengines/base/boundary_conditions.py b/rrompy/hfengines/base/boundary_conditions.py
index f92ae87..476510f 100644
--- a/rrompy/hfengines/base/boundary_conditions.py
+++ b/rrompy/hfengines/base/boundary_conditions.py
@@ -1,113 +1,120 @@
# Copyright (C) 2018 by the RROMPy authors
#
# This file is part of RROMPy.
#
# RROMPy is free software: you can redistribute it and/or modify
# it under the terms of the GNU Lesser General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# RROMPy is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public License
# along with RROMPy. If not, see .
#
from rrompy.utilities.base.types import GenExpr
from rrompy.utilities.base.fenics import bdrTrue, bdrFalse
__all__ = ['BoundaryConditions']
class BoundaryConditions:
- """Boundary conditions manager."""
+ """
+ Boundary conditions manager.
+
+ Attributes:
+ DirichletBoundary: Callable returning True when on Dirichlet boundary.
+ NeumannBoundary: Callable returning True when on Neumann boundary.
+ RobinBoundary: Callable returning True when on Robin boundary.
+ """
allowedKinds = ["Dirichlet", "Neumann", "Robin"]
def __init__(self, kind : str = None):
if kind is None: return
kind = kind[0].upper() + kind[1:].lower()
if kind in self.allowedKinds:
getattr(self.__class__, kind + "Boundary", None).fset(self, "ALL")
else:
raise Exception("BC kind not recognized.")
def name(self) -> str:
return self.__class__.__name__
def __str__(self) -> str:
return self.name()
def __repr__(self) -> str:
return self.__str__() + " at " + hex(id(self))
def _generalManagement(self, kind:str, value:GenExpr):
if isinstance(value, (str,)):
value = value.upper()
if value.upper() == "ALL":
self._complementaryManagementAll(kind)
elif value.upper() == "REST":
self._complementaryManagementRest(kind)
else:
raise Exception("Wildcard not recognized.")
elif callable(value):
self._standardManagement(kind, value)
else:
raise Exception(kind + "Boundary type not recognized.")
def _complementaryManagementAll(self, kind:str):
if kind not in self.allowedKinds:
raise Exception("BC kind not recognized.")
for k in self.allowedKinds:
if k != kind:
getattr(self.__class__, k + "Boundary").fset(self, bdrFalse)
super().__setattr__("_" + kind + "Boundary", bdrTrue)
if hasattr(self, "_" + kind + "Rest"):
super().__delattr__("_" + kind + "Rest")
def _complementaryManagementRest(self, kind:str):
if kind not in self.allowedKinds:
raise Exception("BC kind not recognized.")
otherBCs = []
for k in self.allowedKinds:
if k != kind:
if hasattr(self, "_" + k + "Rest"):
raise Exception("Only 1 'REST' wildcard can be specified.")
otherBCs += [getattr(self, k + "Boundary")]
def restCall(x, on_boundary):
return (on_boundary
and not any([bc(x, on_boundary) for bc in otherBCs]))
super().__setattr__("_" + kind + "Boundary", restCall)
super().__setattr__("_" + kind + "Rest", 1)
def _standardManagement(self, kind:str, bc:callable):
super().__setattr__("_" + kind + "Boundary", bc)
if hasattr(self, "_" + kind + "Rest"):
super().__delattr__("_" + kind + "Rest")
@property
def DirichletBoundary(self):
"""Function handle to DirichletBoundary."""
return self._DirichletBoundary
@DirichletBoundary.setter
def DirichletBoundary(self, DirichletBoundary):
self._generalManagement("Dirichlet", DirichletBoundary)
@property
def NeumannBoundary(self):
"""Function handle to NeumannBoundary."""
return self._NeumannBoundary
@NeumannBoundary.setter
def NeumannBoundary(self, NeumannBoundary):
self._generalManagement("Neumann", NeumannBoundary)
@property
def RobinBoundary(self):
"""Function handle to RobinBoundary."""
return self._RobinBoundary
@RobinBoundary.setter
def RobinBoundary(self, RobinBoundary):
self._generalManagement("Robin", RobinBoundary)
diff --git a/rrompy/hfengines/base/helmholtz_problem_engine.py b/rrompy/hfengines/base/helmholtz_problem_engine.py
index 33a0b8d..9775a24 100644
--- a/rrompy/hfengines/base/helmholtz_problem_engine.py
+++ b/rrompy/hfengines/base/helmholtz_problem_engine.py
@@ -1,154 +1,164 @@
# Copyright (C) 2018 by the RROMPy authors
#
# This file is part of RROMPy.
#
# RROMPy is free software: you can redistribute it and/or modify
# it under the terms of the GNU Lesser General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# RROMPy is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public License
# along with RROMPy. If not, see .
#
import numpy as np
import scipy.sparse as scsp
import fenics as fen
from .laplace_base_problem_engine import LaplaceBaseProblemEngine
from rrompy.utilities.base.types import Np1D, ScOp
from rrompy.utilities.base.fenics import fenZERO, fenONE
from rrompy.utilities.base import verbosityDepth
__all__ = ['HelmholtzProblemEngine']
class HelmholtzProblemEngine(LaplaceBaseProblemEngine):
"""
Solver for generic Helmholtz problems with parametric wavenumber.
- \nabla \cdot (a \nabla u) - omega^2 * n**2 * u = f in \Omega
u = u0 on \Gamma_D
\partial_nu = g1 on \Gamma_N
\partial_nu + h u = g2 on \Gamma_R
Attributes:
+ verbosity: Verbosity level.
+ BCManager: Boundary condition manager.
+ V: Real FE space.
+ u: Generic trial functions for variational form evaluation.
+ v: Generic test functions for variational form evaluation.
+ As: Scipy sparse array representation (in CSC format) of As.
+ bs: Numpy array representation of bs.
+ energyNormMatrix: Scipy sparse matrix representing inner product over
+ V.
+ bsmu: Mu value of last bs evaluation.
+ liftDirichletDatamu: Mu value of last Dirichlet datum evaluation.
+ liftedDirichletDatum: Dofs of Dirichlet datum lifting.
+ mu0BC: Mu value of last Dirichlet datum lifting.
+ degree_threshold: Threshold for ufl expression interpolation degree.
omega: Value of omega.
diffusivity: Value of a.
refractionIndex: Value of n.
forcingTerm: Value of f.
DirichletDatum: Value of u0.
NeumannDatum: Value of g1.
RobinDatumG: Value of g2.
RobinDatumH: Value of h.
DirichletBoundary: Function handle to \Gamma_D.
NeumannBoundary: Function handle to \Gamma_N.
RobinBoundary: Function handle to \Gamma_R.
- V: Real FE space.
- u: Generic trial functions for variational form evaluation.
- v: Generic test functions for variational form evaluation.
ds: Boundary measure 2-tuple (resp. for Neumann and Robin boundaries).
A0: Scipy sparse array representation (in CSC format) of A0.
A1: Scipy sparse array representation (in CSC format) of A1.
b0: Numpy array representation of b0.
- As: Scipy sparse array representation (in CSC format) of As.
- bs: Numpy array representation of bs.
+ dsToBeSet: Whether ds needs to be set.
"""
nAs = 2
def __init__(self, degree_threshold : int = np.inf, verbosity : int = 10):
super().__init__(degree_threshold = degree_threshold,
verbosity = verbosity)
self.omega = 1.
self.refractionIndex = fenONE
@property
def refractionIndex(self):
"""Value of n."""
return self._refractionIndex
@refractionIndex.setter
def refractionIndex(self, refractionIndex):
self.resetAs()
if not isinstance(refractionIndex, (list, tuple,)):
refractionIndex = [refractionIndex, fenZERO]
self._refractionIndex = refractionIndex
def rescaling(self, x:Np1D) -> Np1D:
"""Rescaling in parameter dependence."""
return np.power(x, 2.)
def rescalingInv(self, x:Np1D) -> Np1D:
"""Inverse rescaling in parameter dependence."""
return np.power(x, .5)
def A(self, mu:complex, der : int = 0) -> ScOp:
"""Assemble (derivative of) operator of linear system."""
Anull = self.checkAInBounds(der)
if Anull is not None: return Anull
self.autoSetDS()
if der <= 0 and self.As[0] is None:
if self.verbosity >= 20:
verbosityDepth("INIT", "Assembling operator term A0.")
DirichletBC0 = fen.DirichletBC(self.V, fenZERO,
self.DirichletBoundary)
aRe, aIm = self.diffusivity
hRe, hIm = self.RobinDatumH
termNames = ["diffusivity", "RobinDatumH"]
parsRe = self.iterReduceQuadratureDegree(zip(
[aRe, hRe],
[x + "Real" for x in termNames]))
parsIm = self.iterReduceQuadratureDegree(zip(
[aIm, hIm],
[x + "Imag" for x in termNames]))
a0Re = (aRe * fen.dot(fen.grad(self.u), fen.grad(self.v)) * fen.dx
+ hRe * fen.dot(self.u, self.v) * self.ds(1))
a0Im = (aIm * fen.dot(fen.grad(self.u), fen.grad(self.v)) * fen.dx
+ hIm * fen.dot(self.u, self.v) * self.ds(1))
A0Re = fen.assemble(a0Re, form_compiler_parameters = parsRe)
A0Im = fen.assemble(a0Im, form_compiler_parameters = parsIm)
DirichletBC0.apply(A0Re)
DirichletBC0.zero(A0Im)
A0ReMat = fen.as_backend_type(A0Re).mat()
A0ImMat = fen.as_backend_type(A0Im).mat()
A0Rer, A0Rec, A0Rev = A0ReMat.getValuesCSR()
A0Imr, A0Imc, A0Imv = A0ImMat.getValuesCSR()
self.As[0] = (scsp.csr_matrix((A0Rev, A0Rec, A0Rer),
shape = A0ReMat.size)
+ 1.j * scsp.csr_matrix((A0Imv, A0Imc, A0Imr),
shape = A0ImMat.size))
if self.verbosity >= 20:
verbosityDepth("DEL", "Done assembling operator term.")
if der <= 1 and self.As[1] is None:
if self.verbosity >= 20:
verbosityDepth("INIT", "Assembling operator term A1.")
DirichletBC0 = fen.DirichletBC(self.V, fenZERO,
self.DirichletBoundary)
nRe, nIm = self.refractionIndex
n2Re, n2Im = nRe * nRe - nIm * nIm, 2 * nRe * nIm
parsRe = self.iterReduceQuadratureDegree(zip([n2Re],
["refractionIndexSquaredReal"]))
parsIm = self.iterReduceQuadratureDegree(zip([n2Im],
["refractionIndexSquaredImag"]))
a1Re = - n2Re * fen.dot(self.u, self.v) * fen.dx
a1Im = - n2Im * fen.dot(self.u, self.v) * fen.dx
A1Re = fen.assemble(a1Re, form_compiler_parameters = parsRe)
A1Im = fen.assemble(a1Im, form_compiler_parameters = parsIm)
DirichletBC0.zero(A1Re)
DirichletBC0.zero(A1Im)
A1ReMat = fen.as_backend_type(A1Re).mat()
A1ImMat = fen.as_backend_type(A1Im).mat()
A1Rer, A1Rec, A1Rev = A1ReMat.getValuesCSR()
A1Imr, A1Imc, A1Imv = A1ImMat.getValuesCSR()
self.As[1] = (scsp.csr_matrix((A1Rev, A1Rec, A1Rer),
shape = A1ReMat.size)
+ 1.j * scsp.csr_matrix((A1Imv, A1Imc, A1Imr),
shape = A1ImMat.size))
if self.verbosity >= 20:
verbosityDepth("DEL", "Done assembling operator term.")
if der == 0:
return self.As[0] + mu**2 * self.As[1]
return self.As[1]
diff --git a/rrompy/hfengines/base/laplace_base_problem_engine.py b/rrompy/hfengines/base/laplace_base_problem_engine.py
index 76b646f..dab145b 100644
--- a/rrompy/hfengines/base/laplace_base_problem_engine.py
+++ b/rrompy/hfengines/base/laplace_base_problem_engine.py
@@ -1,310 +1,320 @@
# Copyright (C) 2018 by the RROMPy authors
#
# This file is part of RROMPy.
#
# RROMPy is free software: you can redistribute it and/or modify
# it under the terms of the GNU Lesser General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# RROMPy is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public License
# along with RROMPy. If not, see .
#
import numpy as np
import scipy.sparse as scsp
import fenics as fen
from .problem_engine_base import ProblemEngineBase
from rrompy.utilities.base.types import Np1D, ScOp
from rrompy.utilities.base.fenics import fenZERO, fenONE
from rrompy.utilities.base import verbosityDepth
__all__ = ['LaplaceBaseProblemEngine']
class LaplaceBaseProblemEngine(ProblemEngineBase):
"""
Solver for generic Laplace problems.
- \nabla \cdot (a \nabla u) = f in \Omega
u = u0 on \Gamma_D
\partial_nu = g1 on \Gamma_N
\partial_nu + h u = g2 on \Gamma_R
Attributes:
+ verbosity: Verbosity level.
+ BCManager: Boundary condition manager.
+ V: Real FE space.
+ u: Generic trial functions for variational form evaluation.
+ v: Generic test functions for variational form evaluation.
+ As: Scipy sparse array representation (in CSC format) of As.
+ bs: Numpy array representation of bs.
+ energyNormMatrix: Scipy sparse matrix representing inner product over
+ V.
+ bsmu: Mu value of last bs evaluation.
+ liftDirichletDatamu: Mu value of last Dirichlet datum evaluation.
+ liftedDirichletDatum: Dofs of Dirichlet datum lifting.
+ mu0BC: Mu value of last Dirichlet datum lifting.
+ degree_threshold: Threshold for ufl expression interpolation degree.
omega: Value of omega.
diffusivity: Value of a.
forcingTerm: Value of f.
DirichletDatum: Value of u0.
NeumannDatum: Value of g1.
RobinDatumG: Value of g2.
RobinDatumH: Value of h.
DirichletBoundary: Function handle to \Gamma_D.
NeumannBoundary: Function handle to \Gamma_N.
RobinBoundary: Function handle to \Gamma_R.
- V: Real FE space.
- u: Generic trial functions for variational form evaluation.
- v: Generic test functions for variational form evaluation.
ds: Boundary measure 2-tuple (resp. for Neumann and Robin boundaries).
A0: Scipy sparse array representation (in CSC format) of A0.
b0: Numpy array representation of b0.
- As: Scipy sparse array representation (in CSC format) of As.
- bs: Numpy array representation of bs.
+ dsToBeSet: Whether ds needs to be set.
"""
def __init__(self, degree_threshold : int = np.inf, verbosity : int = 10):
super().__init__(degree_threshold = degree_threshold,
verbosity = verbosity)
self.omega = 0.
self.diffusivity = fenONE
self.forcingTerm = fenZERO
self.DirichletDatum = fenZERO
self.NeumannDatum = fenZERO
self.RobinDatumG = fenZERO
self.RobinDatumH = fenZERO
@property
def V(self):
"""Value of V."""
return self._V
@V.setter
def V(self, V):
ProblemEngineBase.V.fset(self, V)
self.dsToBeSet = True
@property
def diffusivity(self):
"""Value of a."""
return self._diffusivity
@diffusivity.setter
def diffusivity(self, diffusivity):
self.resetAs()
if not isinstance(diffusivity, (list, tuple,)):
diffusivity = [diffusivity, fenZERO]
self._diffusivity = diffusivity
@property
def forcingTerm(self):
"""Value of f."""
return self._forcingTerm
@forcingTerm.setter
def forcingTerm(self, forcingTerm):
self.resetbs()
if not isinstance(forcingTerm, (list, tuple,)):
forcingTerm = [forcingTerm, fenZERO]
self._forcingTerm = forcingTerm
@property
def DirichletDatum(self):
"""Value of u0."""
return self._DirichletDatum
@DirichletDatum.setter
def DirichletDatum(self, DirichletDatum):
self.resetbs()
if not isinstance(DirichletDatum, (list, tuple,)):
DirichletDatum = [DirichletDatum, fenZERO]
self._DirichletDatum = DirichletDatum
@property
def NeumannDatum(self):
"""Value of g1."""
return self._NeumannDatum
@NeumannDatum.setter
def NeumannDatum(self, NeumannDatum):
self.resetbs()
if not isinstance(NeumannDatum, (list, tuple,)):
NeumannDatum = [NeumannDatum, fenZERO]
self._NeumannDatum = NeumannDatum
@property
def RobinDatumG(self):
"""Value of g2."""
return self._RobinDatumG
@RobinDatumG.setter
def RobinDatumG(self, RobinDatumG):
self.resetbs()
if not isinstance(RobinDatumG, (list, tuple,)):
RobinDatumG = [RobinDatumG, fenZERO]
self._RobinDatumG = RobinDatumG
@property
def RobinDatumH(self):
"""Value of h."""
return self._RobinDatumH
@RobinDatumH.setter
def RobinDatumH(self, RobinDatumH):
self.resetAs()
if not isinstance(RobinDatumH, (list, tuple,)):
RobinDatumH = [RobinDatumH, fenZERO]
self._RobinDatumH = RobinDatumH
@property
def DirichletBoundary(self):
"""Function handle to DirichletBoundary."""
return self.BCManager.DirichletBoundary
@DirichletBoundary.setter
def DirichletBoundary(self, DirichletBoundary):
self.resetAs()
self.resetbs()
self.BCManager.DirichletBoundary = DirichletBoundary
@property
def NeumannBoundary(self):
"""Function handle to NeumannBoundary."""
return self.BCManager.NeumannBoundary
@NeumannBoundary.setter
def NeumannBoundary(self, NeumannBoundary):
self.resetAs()
self.resetbs()
self.dsToBeSet = True
self.BCManager.NeumannBoundary = NeumannBoundary
@property
def RobinBoundary(self):
"""Function handle to RobinBoundary."""
return self.BCManager.RobinBoundary
@RobinBoundary.setter
def RobinBoundary(self, RobinBoundary):
self.resetAs()
self.resetbs()
self.dsToBeSet = True
self.BCManager.RobinBoundary = RobinBoundary
def autoSetDS(self):
"""Set FEniCS boundary measure based on boundary function handles."""
if self.dsToBeSet:
if self.verbosity >= 20:
verbosityDepth("INIT", "Initializing boundary measures.",
end = "")
NB = self.NeumannBoundary
RB = self.RobinBoundary
class NBoundary(fen.SubDomain):
def inside(self, x, on_boundary):
return NB(x, on_boundary)
class RBoundary(fen.SubDomain):
def inside(self, x, on_boundary):
return RB(x, on_boundary)
boundary_markers = fen.MeshFunction("size_t", self.V.mesh(),
self.V.mesh().topology().dim() - 1)
NBoundary().mark(boundary_markers, 0)
RBoundary().mark(boundary_markers, 1)
self.ds = fen.Measure("ds", domain = self.V.mesh(),
subdomain_data = boundary_markers)
self.dsToBeSet = False
if self.verbosity >= 20:
verbosityDepth("DEL", " Done.", inline = True)
def buildEnergyNormForm(self):
"""
Build sparse matrix (in CSR format) representative of scalar product.
"""
if self.verbosity >= 20:
verbosityDepth("INIT", "Assembling energy matrix.", end = "")
normMatFen = fen.assemble((fen.dot(fen.grad(self.u), fen.grad(self.v))
+ np.abs(self.omega)**2 * fen.dot(self.u, self.v)) *fen.dx)
normMat = fen.as_backend_type(normMatFen).mat()
self.energyNormMatrix = scsp.csr_matrix(normMat.getValuesCSR()[::-1],
shape = normMat.size)
if self.verbosity >= 20:
verbosityDepth("DEL", " Done.", inline = True)
def A(self, mu:complex, der : int = 0) -> ScOp:
"""Assemble (derivative of) operator of linear system."""
Anull = self.checkAInBounds(der)
if Anull is not None: return Anull
self.autoSetDS()
if self.As[0] is None:
if self.verbosity >= 20:
verbosityDepth("INIT", "Assembling operator term A0.")
DirichletBC0 = fen.DirichletBC(self.V, fenZERO,
self.DirichletBoundary)
aRe, aIm = self.diffusivity
hRe, hIm = self.RobinDatumH
termNames = ["diffusivity", "RobinDatumH"]
parsRe = self.iterReduceQuadratureDegree(zip(
[aRe, hRe],
[x + "Real" for x in termNames]))
parsIm = self.iterReduceQuadratureDegree(zip(
[aIm, hIm],
[x + "Imag" for x in termNames]))
a0Re = (aRe * fen.dot(fen.grad(self.u), fen.grad(self.v)) * fen.dx
+ hRe * fen.dot(self.u, self.v) * self.ds(1))
a0Im = (aIm * fen.dot(fen.grad(self.u), fen.grad(self.v)) * fen.dx
+ hIm * fen.dot(self.u, self.v) * self.ds(1))
A0Re = fen.assemble(a0Re, form_compiler_parameters = parsRe)
A0Im = fen.assemble(a0Im, form_compiler_parameters = parsIm)
DirichletBC0.apply(A0Re)
DirichletBC0.zero(A0Im)
A0ReMat = fen.as_backend_type(A0Re).mat()
A0ImMat = fen.as_backend_type(A0Im).mat()
A0Rer, A0Rec, A0Rev = A0ReMat.getValuesCSR()
A0Imr, A0Imc, A0Imv = A0ImMat.getValuesCSR()
self.As[0] = (scsp.csr_matrix((A0Rev, A0Rec, A0Rer),
shape = A0ReMat.size)
+ 1.j * scsp.csr_matrix((A0Imv, A0Imc, A0Imr),
shape = A0ImMat.size))
if self.verbosity >= 20:
verbosityDepth("DEL", "Done assembling operator term.")
return self.As[0]
def b(self, mu:complex, der : int = 0,
homogeneized : bool = False) -> Np1D:
"""Assemble (derivative of) RHS of linear system."""
bnull = self.checkbInBounds(der, homogeneized)
if bnull is not None: return bnull
if homogeneized and not np.isclose(self.mu0BC, mu):
self.u0BC = self.liftDirichletData(mu)
if (max(self.nbs, self.nAs * homogeneized) > 1
and not np.isclose(self.bsmu, mu)):
self.bsmu = mu
self.resetbs()
if self.bs[homogeneized][der] is None:
self.autoSetDS()
if self.verbosity >= 20:
verbosityDepth("INIT", "Assembling forcing term b{}.".format(
der))
if der < self.nbs:
fRe, fIm = self.forcingTerm
g1Re, g1Im = self.NeumannDatum
g2Re, g2Im = self.RobinDatumG
else:
fRe, fIm = fenZERO, fenZERO
g1Re, g1Im = fenZERO, fenZERO
g2Re, g2Im = fenZERO, fenZERO
termNames = ["forcingTerm", "NeumannDatum", "RobinDatumG"]
parsRe = self.iterReduceQuadratureDegree(zip(
[fRe, g1Re, g2Re],
[x + "Real" for x in termNames]))
parsIm = self.iterReduceQuadratureDegree(zip(
[fIm, g1Im, g2Im],
[x + "Imag" for x in termNames]))
L0Re = (fen.dot(fRe, self.v) * fen.dx
+ fen.dot(g1Re, self.v) * self.ds(0)
+ fen.dot(g2Re, self.v) * self.ds(1))
L0Im = (fen.dot(fIm, self.v) * fen.dx
+ fen.dot(g1Im, self.v) * self.ds(0)
+ fen.dot(g2Im, self.v) * self.ds(1))
b0Re = fen.assemble(L0Re, form_compiler_parameters = parsRe)
b0Im = fen.assemble(L0Im, form_compiler_parameters = parsIm)
if homogeneized:
Ader = self.A(mu, der)
b0Re[:] -= np.real(Ader.dot(self.u0BC))
b0Im[:] -= np.imag(Ader.dot(self.u0BC))
DBCR = fen.DirichletBC(self.V, fenZERO, self.DirichletBoundary)
DBCI = fen.DirichletBC(self.V, fenZERO, self.DirichletBoundary)
else:
DBCR = fen.DirichletBC(self.V, self.DirichletDatum[0],
self.DirichletBoundary)
DBCI = fen.DirichletBC(self.V, self.DirichletDatum[1],
self.DirichletBoundary)
DBCR.apply(b0Re)
DBCI.apply(b0Im)
self.bs[homogeneized][der] = np.array(b0Re[:]
+ 1.j * b0Im[:], dtype = np.complex)
if self.verbosity >= 20:
verbosityDepth("DEL", "Done assembling forcing term.")
return self.bs[homogeneized][der]
diff --git a/rrompy/hfengines/base/problem_engine_base.py b/rrompy/hfengines/base/problem_engine_base.py
index ec7a9fb..5d01f7d 100644
--- a/rrompy/hfengines/base/problem_engine_base.py
+++ b/rrompy/hfengines/base/problem_engine_base.py
@@ -1,350 +1,366 @@
# Copyright (C) 2018 by the RROMPy authors
#
# This file is part of RROMPy.
#
# RROMPy is free software: you can redistribute it and/or modify
# it under the terms of the GNU Lesser General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# RROMPy is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public License
# along with RROMPy. If not, see .
#
from abc import abstractmethod
import fenics as fen
import numpy as np
from scipy.sparse import csr_matrix
import scipy.sparse as scsp
import scipy.sparse.linalg as scspla
from matplotlib import pyplot as plt
from rrompy.utilities.base.types import (Np1D, ScOp, strLst, FenFunc,
Tuple, List)
from rrompy.utilities.base import purgeList, getNewFilename, verbosityDepth
from .boundary_conditions import BoundaryConditions
__all__ = ['ProblemEngineBase']
class ProblemEngineBase:
"""
Generic solver for parametric problems.
+
+ Attributes:
+ verbosity: Verbosity level.
+ BCManager: Boundary condition manager.
+ V: Real FE space.
+ u: Generic trial functions for variational form evaluation.
+ v: Generic test functions for variational form evaluation.
+ As: Scipy sparse array representation (in CSC format) of As.
+ bs: Numpy array representation of bs.
+ energyNormMatrix: Scipy sparse matrix representing inner product over
+ V.
+ bsmu: Mu value of last bs evaluation.
+ liftDirichletDatamu: Mu value of last Dirichlet datum evaluation.
+ liftedDirichletDatum: Dofs of Dirichlet datum lifting.
+ mu0BC: Mu value of last Dirichlet datum lifting.
+ degree_threshold: Threshold for ufl expression interpolation degree.
"""
nAs, nbs = 1, 1
functional = lambda self, u: 0.
def __init__(self, degree_threshold : int = np.inf, verbosity : int = 10):
self.BCManager = BoundaryConditions("Dirichlet")
self.V = fen.FunctionSpace(fen.UnitSquareMesh(10, 10), "P", 1)
self.verbosity = verbosity
self.resetAs()
self.resetbs()
self.bsmu = np.nan
self.liftDirichletDatamu = np.nan
self.mu0BC = np.nan
self.degree_threshold = degree_threshold
def name(self) -> str:
return self.__class__.__name__
def __str__(self) -> str:
return self.name()
def __repr__(self) -> str:
return self.__str__() + " at " + hex(id(self))
def __dir_base__(self):
return [x for x in self.__dir__() if x[:2] != "__"]
@property
def V(self):
"""Value of V."""
return self._V
@V.setter
def V(self, V):
self.resetAs()
self.resetbs()
if not type(V).__name__ == 'FunctionSpace':
raise Exception("V type not recognized.")
self._V = V
self.u = fen.TrialFunction(V)
self.v = fen.TestFunction(V)
def innerProduct(self, u:Np1D, v:Np1D) -> float:
"""Hilbert space scalar product."""
if not hasattr(self, "energyNormMatrix"):
self.buildEnergyNormForm()
return v.conj().T.dot(self.energyNormMatrix.dot(u))
def buildEnergyNormForm(self):
"""
Build sparse matrix (in CSR format) representative of scalar product.
"""
if self.verbosity >= 20:
verbosityDepth("INIT", "Assembling energy matrix.", end = "")
normMatFen = fen.assemble(fen.dot(self.u, self.v) * fen.dx)
normMat = fen.as_backend_type(normMatFen).mat()
self.energyNormMatrix = csr_matrix(normMat.getValuesCSR()[::-1],
shape = normMat.size)
if self.verbosity >= 20:
verbosityDepth("DEL", " Done.", inline = True)
def norm(self, u:Np1D) -> float:
return np.abs(self.innerProduct(u, u)) ** .5
def rescaling(self, x:Np1D) -> Np1D:
"""Rescaling in parameter dependence."""
return x
def rescalingInv(self, x:Np1D) -> Np1D:
"""Inverse rescaling in parameter dependence."""
return x
def checkAInBounds(self, der : int = 0):
"""Check if derivative index is oob for operator of linear system."""
if der < 0 or der >= self.nAs:
d = self.V.dim()
return scsp.csr_matrix((np.zeros(0), np.zeros(0), np.zeros(d + 1)),
shape = (d, d), dtype = np.complex)
def checkbInBounds(self, der : int = 0, homogeneized : bool = False):
"""Check if derivative index is oob for RHS of linear system."""
if der < 0 or der >= max(self.nbs, self.nAs * homogeneized):
return np.zeros(self.V.dim(), dtype = np.complex)
def setDirichletDatum(self, mu:complex):
"""Set Dirichlet datum if parametric."""
if hasattr(self, "liftedDirichletDatum"):
self.liftDirichletDatamu = mu
def liftDirichletData(self, mu:complex) -> Np1D:
"""Lift Dirichlet datum."""
self.setDirichletDatum(mu)
if not np.isclose(self.liftDirichletDatamu, mu):
try:
liftRe = fen.interpolate(self.DirichletDatum[0], self.V)
except:
liftRe = fen.project(self.DirichletDatum[0], self.V)
try:
liftIm = fen.interpolate(self.DirichletDatum[1], self.V)
except:
liftIm = fen.project(self.DirichletDatum[1], self.V)
self.liftedDirichletDatum = (np.array(liftRe.vector())
+ 1.j * np.array(liftIm.vector()))
return self.liftedDirichletDatum
def resetAs(self):
"""Reset (derivatives of) operator of linear system."""
self.As = [None] * self.nAs
def resetbs(self):
"""Reset (derivatives of) RHS of linear system."""
self.bs = {True: [None] * max(self.nbs, self.nAs),
False: [None] * self.nbs}
def reduceQuadratureDegree(self, fun:FenFunc, name:str):
"""Check whether to reduce compiler parameters to degree threshold."""
if not np.isinf(self.degree_threshold):
from ufl.algorithms.estimate_degrees import (
estimate_total_polynomial_degree as ETPD)
try:
deg = ETPD(fun)
except:
return False
if deg > self.degree_threshold:
if self.verbosity >= 15:
verbosityDepth("MAIN", ("Reducing quadrature degree from "
"{} to {} for {}.").format(
deg,
self.degree_threshold,
name))
return True
return False
def iterReduceQuadratureDegree(self, funsNames:List[Tuple[FenFunc, str]]):
"""
Iterate reduceQuadratureDegree over list and define reduce compiler
parameters.
"""
if funsNames is not None:
for fun, name in funsNames:
if self.reduceQuadratureDegree(fun, name):
return {"quadrature_degree" : self.degree_threshold}
return {}
@abstractmethod
def A(self, mu:complex, der : int = 0) -> ScOp:
"""Assemble (derivative of) operator of linear system."""
Anull = self.checkAInBounds(der)
if Anull is not None: return Anull
if self.As[der] is None:
self.As[der] = 0.
return self.As[der]
@abstractmethod
def b(self, mu:complex, der : int = 0,
homogeneized : bool = False) -> Np1D:
"""Assemble (derivative of) RHS of linear system."""
bnull = self.checkbInBounds(der, homogeneized)
if bnull is not None: return bnull
if self.bs[homogeneized][der] is None:
self.bs[homogeneized][der] = 0.
return self.bs[homogeneized][der]
def affineBlocksA(self, mu : complex = 0.) -> Tuple[List[Np1D], callable]:
"""Assemble affine blocks of operator of linear system."""
def lambdas(x, j):
if j == 0: return np.ones(np.size(x))
if j in range(1, self.nAs): return np.power(self.rescaling(x)
- self.rescaling(mu), j)
raise Exception("Wrong j value.")
As = [None] * self.nAs
for j in range(self.nAs):
As[j] = self.A(mu, j)
return As, lambdas
def affineBlocksb(self, mu : complex = 0., homogeneized : bool = False)\
-> Tuple[List[Np1D], callable]:
"""Assemble affine blocks of RHS of linear system."""
def lambdas(x, j):
if j == 0: return np.ones(np.size(x))
if j in range(1, self.nbsEff): return np.power(self.rescaling(x)
- self.rescaling(mu),
j)
raise Exception("Wrong j value.")
if homogeneized:
self.nbsEff = max(self.nAs, self.nbs)
else:
self.nbsEff = self.nbs
bs = [None] * self.nbsEff
for j in range(self.nbsEff):
bs[j] = self.b(mu, j, homogeneized)
return bs, lambdas
def solve(self, mu:complex, RHS : Np1D = None,
homogeneized : bool = False) -> Np1D:
"""
Find solution of linear system.
Args:
mu: parameter value.
RHS: RHS of linear system. If None, defaults to that of parametric
system. Defaults to None.
"""
A = self.A(mu)
if RHS is None: RHS = self.b(mu, 0, homogeneized)
return scspla.spsolve(A, RHS)
def residual(self, u:Np1D, mu:complex,
homogeneized : bool = False) -> Np1D:
"""
Find residual of linear system for given approximate solution.
Args:
u: numpy complex array with function dofs. If None, set to 0.
mu: parameter value.
"""
A = self.A(mu)
RHS = self.b(mu, 0, homogeneized)
if u is None: return RHS
return RHS - A.dot(u)
def plot(self, u:Np1D, name : str = "u", save : str = None,
what : strLst = 'all', saveFormat : str = "eps",
saveDPI : int = 100, **figspecs):
"""
Do some nice plots of the complex-valued function with given dofs.
Args:
u: numpy complex array with function dofs.
name(optional): Name to be shown as title of the plots. Defaults to
'u'.
save(optional): Where to save plot(s). Defaults to None, i.e. no
saving.
what(optional): Which plots to do. If list, can contain 'ABS',
'PHASE', 'REAL', 'IMAG'. If str, same plus wildcard 'ALL'.
Defaults to 'ALL'.
saveFormat(optional): Format for saved plot(s). Defaults to "eps".
saveDPI(optional): DPI for saved plot(s). Defaults to 100.
figspecs(optional key args): Optional arguments for matplotlib
figure creation.
"""
if isinstance(what, (str,)):
if what.upper() == 'ALL':
what = ['ABS', 'PHASE', 'REAL', 'IMAG']
else:
what = [what]
what = purgeList(what, ['ABS', 'PHASE', 'REAL', 'IMAG'],
listname = self.name() + ".what", baselevel = 1)
if len(what) == 0: return
if 'figsize' not in figspecs.keys():
figspecs['figsize'] = (13. * len(what) / 4, 3)
subplotcode = 100 + len(what) * 10
plt.figure(**figspecs)
plt.jet()
if 'ABS' in what:
uAb = fen.Function(self.V)
uAb.vector()[:] = np.array(np.abs(u), dtype = float)
subplotcode = subplotcode + 1
plt.subplot(subplotcode)
p = fen.plot(uAb, title = "|{0}|".format(name))
plt.colorbar(p)
if 'PHASE' in what:
uPh = fen.Function(self.V)
uPh.vector()[:] = np.array(np.angle(u), dtype = float)
subplotcode = subplotcode + 1
plt.subplot(subplotcode)
p = fen.plot(uPh, title = "phase({0})".format(name))
plt.colorbar(p)
if 'REAL' in what:
uRe = fen.Function(self.V)
uRe.vector()[:] = np.array(np.real(u), dtype = float)
subplotcode = subplotcode + 1
plt.subplot(subplotcode)
p = fen.plot(uRe, title = "Re({0})".format(name))
plt.colorbar(p)
if 'IMAG' in what:
uIm = fen.Function(self.V)
uIm.vector()[:] = np.array(np.imag(u), dtype = float)
subplotcode = subplotcode + 1
plt.subplot(subplotcode)
p = fen.plot(uIm, title = "Im({0})".format(name))
plt.colorbar(p)
if save is not None:
save = save.strip()
plt.savefig(getNewFilename("{}_fig_".format(save), saveFormat),
format = saveFormat, dpi = saveDPI)
plt.show()
plt.close()
def plotmesh(self, name : str = "Mesh", save : str = None,
saveFormat : str = "eps", saveDPI : int = 100, **figspecs):
"""
Do a nice plot of the mesh.
Args:
u: numpy complex array with function dofs.
name(optional): Name to be shown as title of the plots. Defaults to
'u'.
save(optional): Where to save plot(s). Defaults to None, i.e. no
saving.
saveFormat(optional): Format for saved plot(s). Defaults to "eps".
saveDPI(optional): DPI for saved plot(s). Defaults to 100.
figspecs(optional key args): Optional arguments for matplotlib
figure creation.
"""
plt.figure(**figspecs)
fen.plot(self.V.mesh())
if save is not None:
save = save.strip()
plt.savefig(getNewFilename("{}_msh_".format(save), saveFormat),
format = saveFormat, dpi = saveDPI)
plt.show()
plt.close()
diff --git a/rrompy/hfengines/base/scattering_problem_engine.py b/rrompy/hfengines/base/scattering_problem_engine.py
index 2b51334..7b32f88 100644
--- a/rrompy/hfengines/base/scattering_problem_engine.py
+++ b/rrompy/hfengines/base/scattering_problem_engine.py
@@ -1,167 +1,176 @@
# Copyright (C) 2018 by the RROMPy authors
#
# This file is part of RROMPy.
#
# RROMPy is free software: you can redistribute it and/or modify
# it under the terms of the GNU Lesser General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# RROMPy is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public License
# along with RROMPy. If not, see .
#
from numpy import inf
import scipy.sparse as scsp
import fenics as fen
from rrompy.utilities.base.types import Np1D, ScOp
from rrompy.utilities.base.fenics import fenZERO
from rrompy.utilities.base import verbosityDepth
from .helmholtz_problem_engine import HelmholtzProblemEngine
from rrompy.utilities.warning_manager import warn
__all__ = ['ScatteringProblemEngine']
class ScatteringProblemEngine(HelmholtzProblemEngine):
"""
Solver for scattering problems with parametric wavenumber.
- \nabla \cdot (a \nabla u) - omega^2 * n**2 * u = f in \Omega
u = u0 on \Gamma_D
\partial_nu = g1 on \Gamma_N
\partial_nu +- i omega u = g2 on \Gamma_R
Attributes:
+ verbosity: Verbosity level.
+ BCManager: Boundary condition manager.
+ V: Real FE space.
+ u: Generic trial functions for variational form evaluation.
+ v: Generic test functions for variational form evaluation.
+ As: Scipy sparse array representation (in CSC format) of As.
+ bs: Numpy array representation of bs.
+ energyNormMatrix: Scipy sparse matrix representing inner product over
+ V.
+ bsmu: Mu value of last bs evaluation.
+ liftDirichletDatamu: Mu value of last Dirichlet datum evaluation.
+ liftedDirichletDatum: Dofs of Dirichlet datum lifting.
+ mu0BC: Mu value of last Dirichlet datum lifting.
+ degree_threshold: Threshold for ufl expression interpolation degree.
signR: Sign in ABC.
omega: Value of omega.
diffusivity: Value of a.
- refractionIndex: Value of n.
forcingTerm: Value of f.
DirichletDatum: Value of u0.
NeumannDatum: Value of g1.
RobinDatumG: Value of g2.
DirichletBoundary: Function handle to \Gamma_D.
NeumannBoundary: Function handle to \Gamma_N.
RobinBoundary: Function handle to \Gamma_R.
- V: Real FE space.
- u: Generic trial functions for variational form evaluation.
- v: Generic test functions for variational form evaluation.
ds: Boundary measure 2-tuple (resp. for Neumann and Robin boundaries).
A0: Scipy sparse array representation (in CSC format) of A0.
A1: Scipy sparse array representation (in CSC format) of A1.
- A2: Scipy sparse array representation (in CSC format) of A1.
+ A2: Scipy sparse array representation (in CSC format) of A2.
b0: Numpy array representation of b0.
- As: Scipy sparse array representation (in CSC format) of As.
- bs: Numpy array representation of bs.
+ dsToBeSet: Whether ds needs to be set.
"""
nAs = 3
signR = - 1.
def __init__(self, degree_threshold : int = inf, verbosity : int = 10):
self.silenceWarnings = True
super().__init__(degree_threshold = degree_threshold,
verbosity = verbosity)
del self.silenceWarnings
def rescaling(self, x:Np1D) -> Np1D:
"""Rescaling in parameter dependence."""
return x
def rescalingInv(self, x:Np1D) -> Np1D:
"""Inverse rescaling in parameter dependence."""
return x
@property
def RobinDatumH(self):
"""Value of h."""
return self.signR * self.omega
@RobinDatumH.setter
def RobinDatumH(self, RobinDatumH):
if not hasattr(self, "silenceWarnings"):
warn(("Scattering problems do not allow changes of h. Ignoring "
"assignment."))
return
def A(self, mu:complex, der : int = 0) -> ScOp:
"""Assemble (derivative of) operator of linear system."""
Anull = self.checkAInBounds(der)
if Anull is not None: return Anull
self.autoSetDS()
if der <= 0 and self.As[0] is None:
if self.verbosity >= 20:
verbosityDepth("INIT", "Assembling operator term A0.")
DirichletBC0 = fen.DirichletBC(self.V, fenZERO,
self.DirichletBoundary)
aRe, aIm = self.diffusivity
parsRe = self.iterReduceQuadratureDegree(zip([aRe],
["diffusivityReal"]))
parsIm = self.iterReduceQuadratureDegree(zip([aIm],
["diffusivityImag"]))
a0Re = aRe * fen.dot(fen.grad(self.u), fen.grad(self.v)) * fen.dx
a0Im = aIm * fen.dot(fen.grad(self.u), fen.grad(self.v)) * fen.dx
A0Re = fen.assemble(a0Re, form_compiler_parameters = parsRe)
A0Im = fen.assemble(a0Im, form_compiler_parameters = parsIm)
DirichletBC0.apply(A0Re)
DirichletBC0.zero(A0Im)
A0ReMat = fen.as_backend_type(A0Re).mat()
A0ImMat = fen.as_backend_type(A0Im).mat()
A0Rer, A0Rec, A0Rev = A0ReMat.getValuesCSR()
A0Imr, A0Imc, A0Imv = A0ImMat.getValuesCSR()
self.As[0] = (scsp.csr_matrix((A0Rev, A0Rec, A0Rer),
shape = A0ReMat.size)
+ 1.j * scsp.csr_matrix((A0Imv, A0Imc, A0Imr),
shape = A0ImMat.size))
if self.verbosity >= 20:
verbosityDepth("DEL", "Done assembling operator term.")
if der <= 1 and self.As[1] is None:
if self.verbosity >= 20:
verbosityDepth("INIT", "Assembling operator term A1.")
DirichletBC0 = fen.DirichletBC(self.V, fenZERO,
self.DirichletBoundary)
a1 = fen.dot(self.u, self.v) * self.ds(1)
A1 = fen.assemble(a1)
DirichletBC0.zero(A1)
A1Mat = fen.as_backend_type(A1).mat()
A1r, A1c, A1v = A1Mat.getValuesCSR()
self.As[1] = self.signR * 1.j * scsp.csr_matrix((A1v, A1c, A1r),
shape = A1Mat.size)
if self.verbosity >= 20:
verbosityDepth("DEL", "Done assembling operator term.")
if der <= 2 and self.As[2] is None:
if self.verbosity >= 20:
verbosityDepth("INIT", "Assembling operator term A2.")
DirichletBC0 = fen.DirichletBC(self.V, fenZERO,
self.DirichletBoundary)
nRe, nIm = self.refractionIndex
n2Re, n2Im = nRe * nRe - nIm * nIm, 2 * nRe * nIm
parsRe = self.iterReduceQuadratureDegree(zip([n2Re],
["refractionIndexSquaredReal"]))
parsIm = self.iterReduceQuadratureDegree(zip([n2Im],
["refractionIndexSquaredImag"]))
a2Re = - n2Re * fen.dot(self.u, self.v) * fen.dx
a2Im = - n2Im * fen.dot(self.u, self.v) * fen.dx
A2Re = fen.assemble(a2Re, form_compiler_parameters = parsRe)
A2Im = fen.assemble(a2Im, form_compiler_parameters = parsIm)
DirichletBC0.zero(A2Re)
DirichletBC0.zero(A2Im)
A2ReMat = fen.as_backend_type(A2Re).mat()
A2ImMat = fen.as_backend_type(A2Im).mat()
A2Rer, A2Rec, A2Rev = A2ReMat.getValuesCSR()
A2Imr, A2Imc, A2Imv = A2ImMat.getValuesCSR()
self.As[2] = (scsp.csr_matrix((A2Rev, A2Rec, A2Rer),
shape = A2ReMat.size)
+ 1.j * scsp.csr_matrix((A2Imv, A2Imc, A2Imr),
shape = A2ImMat.size))
if self.verbosity >= 20:
verbosityDepth("DEL", "Done assembling operator term.")
if der == 0:
return self.As[0] + mu * self.As[1] + mu**2. * self.As[2]
if der == 1:
return self.As[1] + 2 * mu * self.As[2]
return self.As[2]
diff --git a/rrompy/hfengines/scipy/__init__.py b/rrompy/hfengines/scipy/__init__.py
index 7bd5728..054ddc1 100644
--- a/rrompy/hfengines/scipy/__init__.py
+++ b/rrompy/hfengines/scipy/__init__.py
@@ -1,42 +1,41 @@
# Copyright (C) 2018 by the RROMPy authors
#
# This file is part of RROMPy.
#
# RROMPy is free software: you can redistribute it and/or modify
# it under the terms of the GNU Lesser General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# RROMPy is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public License
# along with RROMPy. If not, see .
#
-from .helmholtz_box_scattering_problem_engine import (
- HelmholtzBoxScatteringProblemEngine)
-from .helmholtz_cavity_scattering_problem_engine import (
- HelmholtzCavityScatteringProblemEngine)
-from .helmholtz_square_bubble_problem_engine import (
- HelmholtzSquareBubbleProblemEngine)
-from .helmholtz_square_bubble_domain_problem_engine import (
- HelmholtzSquareBubbleDomainProblemEngine)
-from .helmholtz_square_transmission_problem_engine import (
- HelmholtzSquareTransmissionProblemEngine)
-from .laplace_disk_gaussian import (
- LaplaceDiskGaussian)
+from .helmholtz_box_scattering_problem_engine import \
+ HelmholtzBoxScatteringProblemEngine
+from .helmholtz_cavity_scattering_problem_engine import \
+ HelmholtzCavityScatteringProblemEngine
+from .helmholtz_square_bubble_problem_engine import \
+ HelmholtzSquareBubbleProblemEngine
+from .helmholtz_square_bubble_domain_problem_engine import \
+ HelmholtzSquareBubbleDomainProblemEngine
+from .helmholtz_square_transmission_problem_engine import \
+ HelmholtzSquareTransmissionProblemEngine
+from .laplace_disk_gaussian import LaplaceDiskGaussian
__all__ = [
'HelmholtzBoxScatteringProblemEngine',
'HelmholtzCavityScatteringProblemEngine',
'HelmholtzSquareBubbleProblemEngine',
'HelmholtzSquareBubbleDomainProblemEngine',
'HelmholtzSquareTransmissionProblemEngine',
'LaplaceDiskGaussian'
]
diff --git a/rrompy/hfengines/scipy/helmholtz_box_scattering_problem_engine.py b/rrompy/hfengines/scipy/helmholtz_box_scattering_problem_engine.py
index 3bb09c2..3c249c8 100644
--- a/rrompy/hfengines/scipy/helmholtz_box_scattering_problem_engine.py
+++ b/rrompy/hfengines/scipy/helmholtz_box_scattering_problem_engine.py
@@ -1,58 +1,58 @@
# Copyright (C) 2018 by the RROMPy authors
#
# This file is part of RROMPy.
#
# RROMPy is free software: you can redistribute it and/or modify
# it under the terms of the GNU Lesser General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# RROMPy is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public License
# along with RROMPy. If not, see .
#
import numpy as np
import fenics as fen
-from rrompy.hfengines.base.scattering_problem_engine import (
- ScatteringProblemEngine)
+from rrompy.hfengines.base.scattering_problem_engine import \
+ ScatteringProblemEngine
__all__ = ['HelmholtzBoxScatteringProblemEngine']
class HelmholtzBoxScatteringProblemEngine(ScatteringProblemEngine):
"""
Solver for scattering problem outside a box with parametric wavenumber.
- \Delta u - omega^2 * n^2 * u = 0 in \Omega
u = 0 on \Gamma_D
\partial_nu - i k u = 0 on \Gamma_R
with exact solution a transmitted plane wave.
"""
def __init__(self, R:float, kappa:float, theta:float, n:int,
degree_threshold : int = np.inf, verbosity : int = 10):
super().__init__(degree_threshold = degree_threshold,
verbosity = verbosity)
self.omega = kappa
import mshr
scatterer = mshr.Polygon([fen.Point(-1, -.5), fen.Point(1, -.5),
fen.Point(1, .5), fen.Point(.8, .5),
fen.Point(.8, -.3), fen.Point(-.8, -.3),
fen.Point(-.8, .5), fen.Point(-1, .5),])
mesh = mshr.generate_mesh(mshr.Circle(fen.Point(0, 0), R)-scatterer, n)
self.V = fen.FunctionSpace(mesh, "P", 3)
self.DirichletBoundary = (lambda x, on_boundary:
on_boundary and (x[0]**2+x[1]**2)**.5 < .95 * R)
self.RobinBoundary = "REST"
c, s = np.cos(theta), np.sin(theta)
x, y = fen.SpatialCoordinate(mesh)[:]
u0R = - fen.cos(kappa * (c * x + s * y))
u0I = - fen.sin(kappa * (c * x + s * y))
self.DirichletDatum = [u0R, u0I]
diff --git a/rrompy/hfengines/scipy/helmholtz_cavity_scattering_problem_engine.py b/rrompy/hfengines/scipy/helmholtz_cavity_scattering_problem_engine.py
index f9263b0..d3d11c5 100644
--- a/rrompy/hfengines/scipy/helmholtz_cavity_scattering_problem_engine.py
+++ b/rrompy/hfengines/scipy/helmholtz_cavity_scattering_problem_engine.py
@@ -1,60 +1,60 @@
# Copyright (C) 2018 by the RROMPy authors
#
# This file is part of RROMPy.
#
# RROMPy is free software: you can redistribute it and/or modify
# it under the terms of the GNU Lesser General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# RROMPy is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public License
# along with RROMPy. If not, see .
#
import numpy as np
import fenics as fen
-from rrompy.hfengines.base.scattering_problem_engine import (
- ScatteringProblemEngine)
+from rrompy.hfengines.base.scattering_problem_engine import \
+ ScatteringProblemEngine
__all__ = ['HelmholtzCavityScatteringProblemEngine']
class HelmholtzCavityScatteringProblemEngine(ScatteringProblemEngine):
"""
Solver for scattering problem inside a cavity with parametric wavenumber.
- \Delta u - omega^2 * n^2 * u = 0 in \Omega
u = 0 on \Gamma_D
\partial_nu - i k u = 0 on \Gamma_R
with exact solution a transmitted plane wave.
"""
def __init__(self, kappa:float, n:int, gamma : float = 0.,
signR : int = -1, degree_threshold : int = np.inf,
verbosity : int = 10):
super().__init__(degree_threshold = degree_threshold,
verbosity = verbosity)
self.signR = signR
self.omega = kappa
pi = np.pi
mesh = fen.RectangleMesh(fen.Point(0, 0), fen.Point(pi, pi), n, n)
self.V = fen.FunctionSpace(mesh, "P", 3)
self.RobinBoundary = (lambda x, on_boundary: on_boundary
and np.isclose(x[1], np.pi))
self.DirichletBoundary = "REST"
x, y = fen.SpatialCoordinate(mesh)[:]
C = 4. / pi ** 4.
bR = C * (2 * (x * (pi - x) + y * (2 * pi - y))
+ (kappa * gamma) ** 2. * x * (pi - x) * y * (2 * pi - y))
bI = C * signR * 2 * kappa * (gamma * (pi - 2 * x) * y * (pi - y)
+ 2 * x * (pi - x) * (pi - y))
wR = fen.cos(kappa * signR * (gamma * x + y))
wI = fen.sin(kappa * signR * (gamma * x + y))
self.forcingTerm = [bR * wR + bI * wI, bI * wR - bR * wI]
diff --git a/rrompy/hfengines/scipy/helmholtz_square_bubble_domain_problem_engine.py b/rrompy/hfengines/scipy/helmholtz_square_bubble_domain_problem_engine.py
index 3a54b7b..d5dd759 100644
--- a/rrompy/hfengines/scipy/helmholtz_square_bubble_domain_problem_engine.py
+++ b/rrompy/hfengines/scipy/helmholtz_square_bubble_domain_problem_engine.py
@@ -1,243 +1,243 @@
# Copyright (C) 2018 by the RROMPy authors
#
# This file is part of RROMPy.
#
# RROMPy is free software: you can redistribute it and/or modify
# it under the terms of the GNU Lesser General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# RROMPy is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public License
# along with RROMPy. If not, see .
#
import numpy as np
import scipy.sparse as scsp
import fenics as fen
-from rrompy.utilities.base.types import Np1D, ScOp, Tuple, List, FenExpr
+from rrompy.utilities.base.types import Np1D, ScOp, Tuple, FenExpr
from rrompy.utilities.base.fenics import fenZERO
-from rrompy.hfengines.base.helmholtz_problem_engine import (
- HelmholtzProblemEngine)
+from rrompy.hfengines.base.helmholtz_problem_engine import \
+ HelmholtzProblemEngine
from rrompy.utilities.base import verbosityDepth
__all__ = ['HelmholtzSquareBubbleDomainProblemEngine']
class HelmholtzSquareBubbleDomainProblemEngine(HelmholtzProblemEngine):
"""
Solver for square bubble Helmholtz problems with parametric domain heigth.
- \Delta u - kappa^2 * u = f in \Omega_mu = [0,\pi] x [0,\mu\pi]
u = 0 on \Gamma_mu = \partial\Omega_mu
with exact solution square bubble times plane wave.
"""
nAs, nbs = 3, 20
def __init__(self, kappa:float, theta:float, n:int, mu0 : np.complex = 1.,
degree_threshold : int = np.inf, verbosity : int = 10):
super().__init__(degree_threshold = degree_threshold,
verbosity = verbosity)
self.omega = kappa
self.kappa = kappa
self.theta = theta
self.mu0 = mu0
self.forcingTermMu = np.nan
mesh = fen.RectangleMesh(fen.Point(0,0), fen.Point(np.pi,np.pi), n, n)
self.V = fen.FunctionSpace(mesh, "P", 3)
def buildEnergyNormForm(self):
"""
Build sparse matrix (in CSR format) representative of scalar product.
"""
if self.verbosity >= 20:
verbosityDepth("INIT", "Assembling energy matrix.", end = "")
mudx = np.abs(self.mu0) * fen.dot(self.u.dx(0), self.v.dx(0)) * fen.dx
muM = np.abs(self.mu0) * fen.dot(self.u, self.v) * fen.dx
imudy = 1. / np.abs(self.mu0) * (fen.dot(self.u.dx(1), self.v.dx(1))
* fen.dx)
normMatFen = fen.assemble(mudx + imudy + muM)
normMat = fen.as_backend_type(normMatFen).mat()
self.energyNormMatrix = scsp.csr_matrix(normMat.getValuesCSR()[::-1],
shape = normMat.size)
if self.verbosity >= 20:
verbosityDepth("DEL", " Done.", inline = True)
def getForcingTerm(self, mu:complex) -> Tuple[FenExpr, FenExpr]:
"""Compute forcing term."""
if not np.isclose(mu, self.forcingTermMu):
if self.verbosity >= 25:
verbosityDepth("INIT",
"Assembling base expression for forcing term.",
end = "")
pi = np.pi
c, s = np.cos(self.theta), np.sin(self.theta)
x, y = fen.SpatialCoordinate(self.V.mesh())[:]
muR, muI = np.real(mu), np.imag(mu)
mu2R, mu2I = np.real(mu ** 2.), np.imag(mu ** 2.)
C = 16. / pi ** 4.
bR = C * (2 * (x * (pi - x) + y * (pi - y))
+ (self.kappa * s) ** 2. * (mu2R - 1.)
* x * (pi - x) * y * (pi - y))
bI = C * (2 * self.kappa * (c * (pi - 2 * x) * y * (pi - y)
+ s * x * (pi - x) * (pi - 2 * y))
+ (self.kappa * s) ** 2. * mu2I
* x * (pi - x) * y * (pi - y))
wR = (fen.cos(self.kappa * (c * x + s * muR * y))
* fen.exp(self.kappa * s * muI * y))
wI = (fen.sin(self.kappa * (c * x + s * muR * y))
* fen.exp(self.kappa * s * muI * y))
self.forcingTerm = [bR * wR + bI * wI, bI * wR - bR * wI]
self.forcingTermMu = mu
if self.verbosity >= 25:
verbosityDepth("DEL", " Done.", inline = True)
return self.forcingTerm
def getExtraFactorB(self, mu:complex, der:int) -> Tuple[FenExpr, FenExpr]:
"""Compute extra expression in RHS."""
def getPowMinusj(x, power):
powR = x ** power
powI = fenZERO
if power % 2 == 1:
powR, powI = powI, powR
if (power + 3) % 4 < 2:
powR, powI = - powR, - powI
return powR, powI
if self.verbosity >= 25:
verbosityDepth("INIT", ("Assembling auxiliary expression for "
"forcing term derivative."), end = "")
from math import factorial as fact
y = fen.SpatialCoordinate(self.V.mesh())[1]
powR, powI = [(self.kappa * np.sin(self.theta)) ** der * k\
for k in getPowMinusj(y, der)]
mu2R, mu2I = np.real(mu ** 2.), np.imag(mu ** 2.)
exprR = mu2R * powR - mu2I * powI
exprI = mu2I * powR + mu2R * powI
if der >= 1:
muR, muI = np.real(2. * mu), np.imag(2. * mu)
powR, powI = [(self.kappa * np.sin(self.theta)) ** (der - 1) * k\
* der for k in getPowMinusj(y, der - 1)]
exprR += muR * powR - muI * powI
exprI += muI * powR + muR * powI
if der >= 2:
powR, powI = [(self.kappa * np.sin(self.theta)) ** (der - 2) * k\
* der * (der - 1) for k in getPowMinusj(y, der - 2)]
exprR += powR
exprI += powI
fac = fact(der)
if self.verbosity >= 25:
verbosityDepth("DEL", " Done.", inline = True)
return [exprR / fac, exprI / fac]
def rescaling(self, x:Np1D) -> Np1D:
"""Rescaling in parameter dependence."""
return x
def rescalingInv(self, x:Np1D) -> Np1D:
"""Inverse rescaling in parameter dependence."""
return x
def A(self, mu:complex, der : int = 0) -> ScOp:
"""Assemble (derivative of) operator of linear system."""
Anull = self.checkAInBounds(der)
if Anull is not None: return Anull
self.autoSetDS()
if der <= 0 and self.As[0] is None:
if self.verbosity >= 20:
verbosityDepth("INIT", "Assembling operator term A0.")
DirichletBC0 = fen.DirichletBC(self.V, fenZERO,
self.DirichletBoundary)
a0Re = fen.dot(self.u.dx(1), self.v.dx(1)) * fen.dx
A0Re = fen.assemble(a0Re)
DirichletBC0.apply(A0Re)
A0ReMat = fen.as_backend_type(A0Re).mat()
A0Rer, A0Rec, A0Rev = A0ReMat.getValuesCSR()
self.As[0] = scsp.csr_matrix((A0Rev, A0Rec, A0Rer),
shape = A0ReMat.size,
dtype = np.complex)
if self.verbosity >= 20:
verbosityDepth("DEL", "Done assembling operator term.")
if der <= 2 and self.As[2] is None:
if self.verbosity >= 20:
verbosityDepth("INIT", "Assembling operator term A2.")
DirichletBC0 = fen.DirichletBC(self.V, fenZERO,
self.DirichletBoundary)
nRe, nIm = self.refractionIndex
n2Re, n2Im = nRe * nRe - nIm * nIm, 2 * nRe * nIm
k2Re, k2Im = np.real(self.omega ** 2), np.imag(self.omega ** 2)
k2n2Re = k2Re * n2Re - k2Im * n2Im
k2n2Im = k2Re * n2Im + k2Im * n2Re
parsRe = self.iterReduceQuadratureDegree(zip([k2n2Re],
["kappaSquaredRefractionIndexSquaredReal"]))
parsIm = self.iterReduceQuadratureDegree(zip([k2n2Im],
["kappaSquaredRefractionIndexSquaredImag"]))
a2Re = (fen.dot(self.u.dx(0), self.v.dx(0))
- k2n2Re * fen.dot(self.u, self.v)) * fen.dx
a2Im = - k2n2Im * fen.dot(self.u, self.v) * fen.dx
A2Re = fen.assemble(a2Re, form_compiler_parameters = parsRe)
A2Im = fen.assemble(a2Im, form_compiler_parameters = parsIm)
DirichletBC0.zero(A2Re)
DirichletBC0.zero(A2Im)
A2ReMat = fen.as_backend_type(A2Re).mat()
A2ImMat = fen.as_backend_type(A2Im).mat()
A2Rer, A2Rec, A2Rev = A2ReMat.getValuesCSR()
A2Imr, A2Imc, A2Imv = A2ImMat.getValuesCSR()
self.As[2] = (scsp.csr_matrix((A2Rev, A2Rec, A2Rer),
shape = A2ReMat.size)
+ 1.j * scsp.csr_matrix((A2Imv, A2Imc, A2Imr),
shape = A2ImMat.size))
if self.verbosity >= 20:
verbosityDepth("DEL", "Done assembling operator term.")
if der == 0:
return self.As[0] + mu ** 2 * self.As[2]
if der == 1:
return 2. * mu * self.As[2]
return self.As[2]
def b(self, mu:complex, der : int = 0,
homogeneized : bool = False) -> Np1D:
"""Assemble (derivative of) RHS of linear system."""
bnull = self.checkbInBounds(der, homogeneized)
if bnull is not None: return bnull
if homogeneized and not np.isclose(self.mu0BC, mu):
self.u0BC = self.liftDirichletData(mu)
if not np.isclose(self.bsmu, mu):
self.bsmu = mu
self.resetbs()
if self.bs[homogeneized][der] is None:
if self.verbosity >= 20:
verbosityDepth("INIT",
"Assembling forcing term b{}.".format(der))
if der < self.nbs:
fRe, fIm = self.getForcingTerm(mu)
cRe, cIm = self.getExtraFactorB(mu, der)
cfRe = cRe * fRe - cIm * fIm
cfIm = cRe * fIm + cIm * fRe
else:
cfRe, cfIm = fenZERO, fenZERO
parsRe = self.iterReduceQuadratureDegree(zip([cfRe],
["forcingTermDer{}Real".format(der)]))
parsIm = self.iterReduceQuadratureDegree(zip([cfIm],
["forcingTermDer{}Imag".format(der)]))
L0Re = fen.dot(cfRe, self.v) * fen.dx
L0Im = fen.dot(cfIm, self.v) * fen.dx
b0Re = fen.assemble(L0Re, form_compiler_parameters = parsRe)
b0Im = fen.assemble(L0Im, form_compiler_parameters = parsIm)
if homogeneized:
Ader = self.A(mu, der)
b0Re[:] -= np.real(Ader.dot(self.u0BC))
b0Im[:] -= np.imag(Ader.dot(self.u0BC))
DirichletBC0 = fen.DirichletBC(self.V, fenZERO,
self.DirichletBoundary)
DirichletBC0.apply(b0Re)
DirichletBC0.apply(b0Im)
self.bs[homogeneized][der] = np.array(b0Re[:]
+ 1.j * b0Im[:], dtype = np.complex)
if self.verbosity >= 20:
verbosityDepth("DEL", "Done assembling forcing term.")
return self.bs[homogeneized][der]
diff --git a/rrompy/hfengines/scipy/helmholtz_square_bubble_problem_engine.py b/rrompy/hfengines/scipy/helmholtz_square_bubble_problem_engine.py
index 7e6035f..1b8a05c 100644
--- a/rrompy/hfengines/scipy/helmholtz_square_bubble_problem_engine.py
+++ b/rrompy/hfengines/scipy/helmholtz_square_bubble_problem_engine.py
@@ -1,53 +1,53 @@
# Copyright (C) 2018 by the RROMPy authors
#
# This file is part of RROMPy.
#
# RROMPy is free software: you can redistribute it and/or modify
# it under the terms of the GNU Lesser General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# RROMPy is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public License
# along with RROMPy. If not, see .
#
import numpy as np
import fenics as fen
-from rrompy.hfengines.base.helmholtz_problem_engine import (
- HelmholtzProblemEngine)
+from rrompy.hfengines.base.helmholtz_problem_engine import \
+ HelmholtzProblemEngine
__all__ = ['HelmholtzSquareBubbleProblemEngine']
class HelmholtzSquareBubbleProblemEngine(HelmholtzProblemEngine):
"""
Solver for square bubble Helmholtz problems with parametric wavenumber.
- \Delta u - omega^2 * u = f in \Omega
u = 0 on \Gamma_D
with exact solution square bubble times plane wave.
"""
def __init__(self, kappa:float, theta:float, n:int,
degree_threshold : int = np.inf, verbosity : int = 10):
super().__init__(degree_threshold = degree_threshold,
verbosity = verbosity)
self.omega = kappa
pi = np.pi
mesh = fen.RectangleMesh(fen.Point(0, 0), fen.Point(pi, pi), n, n)
self.V = fen.FunctionSpace(mesh, "P", 3)
c, s = np.cos(theta), np.sin(theta)
x, y = fen.SpatialCoordinate(mesh)[:]
C = 16. / pi ** 4.
bR = C * 2 * (x * (pi - x) + y * (pi - y))
bI = C * 2 * kappa * (c * (pi - 2 * x) * y * (pi - y)
+ s * x * (pi - x) * (pi - 2 * y))
wR = fen.cos(kappa * (c * x + s * y))
wI = fen.sin(kappa * (c * x + s * y))
self.forcingTerm = [bR * wR + bI * wI, bI * wR - bR * wI]
diff --git a/rrompy/hfengines/scipy/helmholtz_square_transmission_problem_engine.py b/rrompy/hfengines/scipy/helmholtz_square_transmission_problem_engine.py
index dc5cce5..277c9a2 100644
--- a/rrompy/hfengines/scipy/helmholtz_square_transmission_problem_engine.py
+++ b/rrompy/hfengines/scipy/helmholtz_square_transmission_problem_engine.py
@@ -1,78 +1,78 @@
# Copyright (C) 2018 by the RROMPy authors
#
# This file is part of RROMPy.
#
# RROMPy is free software: you can redistribute it and/or modify
# it under the terms of the GNU Lesser General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# RROMPy is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public License
# along with RROMPy. If not, see .
#
import numpy as np
import fenics as fen
import ufl
-from rrompy.hfengines.base.helmholtz_problem_engine import (
- HelmholtzProblemEngine)
+from rrompy.hfengines.base.helmholtz_problem_engine import \
+ HelmholtzProblemEngine
__all__ = ['HelmholtzSquareTransmissionProblemEngine']
class HelmholtzSquareTransmissionProblemEngine(HelmholtzProblemEngine):
"""
Solver for square transmission Helmholtz problems with parametric
wavenumber.
- \Delta u - omega^2 * n^2 * u = 0 in \Omega
u = 0 on \Gamma_D
with exact solution a transmitted plane wave.
"""
def __init__(self, nT:float, nB:float, kappa:float, theta:float, n:int,
degree_threshold : int = np.inf, verbosity : int = 10):
super().__init__(degree_threshold = degree_threshold,
verbosity = verbosity)
self.omega = kappa
mesh = fen.RectangleMesh(fen.Point(-np.pi/2, -np.pi/2),
fen.Point(np.pi/2, np.pi/2), n, n)
self.V = fen.FunctionSpace(mesh, "P", 3)
dx, dy = np.cos(theta), np.sin(theta)
Kx = kappa * nB * dx
Ky = kappa * (nT**2. - (nB * dx)**2. + 0.j)**.5
T = 2 * kappa * nB * dy / (Ky + kappa * nB * dy)
x, y = fen.SpatialCoordinate(mesh)[:]
TR, TI = np.real(T), np.imag(T)
if np.isclose(np.imag(Ky), 0.):
u0RT = (TR * fen.cos(Kx * x + np.real(Ky) * y)
- TI * fen.sin(Kx * x + np.real(Ky) * y))
u0IT = (TR * fen.sin(Kx * x + np.real(Ky) * y)
+ TI * fen.cos(Kx * x + np.real(Ky) * y))
else:
u0RT = fen.exp(- np.imag(Ky) * y) * (TR * fen.cos(Kx * x)
- TI * fen.sin(Kx * x))
u0IT = fen.exp(- np.imag(Ky) * y) * (TR * fen.sin(Kx * x)
+ TI * fen.cos(Kx * x))
u0RB = (fen.cos(kappa * nB * (dx * x + dy * y))
+ (TR - 1) * fen.cos(kappa * nB * (dx*x - dy*y))
- TI * fen.sin(kappa * nB * (dx*x - dy*y)))
u0IB = (fen.sin(kappa * nB * (dx * x + dy * y))
+ (TR - 1) * fen.sin(kappa * nB * (dx*x - dy*y))
+ TI * fen.cos(kappa * nB * (dx*x - dy*y)))
u0R = ufl.conditional(ufl.ge(y, 0.), u0RT, u0RB)
u0I = ufl.conditional(ufl.ge(y, 0.), u0IT, u0IB)
self.refractionIndex = ufl.conditional(ufl.ge(y, 0.),
fen.Constant(nT),
fen.Constant(nB))
self.DirichletDatum = [u0R, u0I]
diff --git a/rrompy/hfengines/scipy/laplace_disk_gaussian.py b/rrompy/hfengines/scipy/laplace_disk_gaussian.py
index 277fe8d..245eab1 100644
--- a/rrompy/hfengines/scipy/laplace_disk_gaussian.py
+++ b/rrompy/hfengines/scipy/laplace_disk_gaussian.py
@@ -1,150 +1,150 @@
# Copyright (C) 2018 by the RROMPy authors
#
# This file is part of RROMPy.
#
# RROMPy is free software: you can redistribute it and/or modify
# it under the terms of the GNU Lesser General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# RROMPy is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public License
# along with RROMPy. If not, see .
#
import numpy as np
import fenics as fen
-from rrompy.utilities.base.types import Np1D, Tuple, List, FenExpr
+from rrompy.utilities.base.types import Np1D, Tuple, FenExpr
from rrompy.hfengines.base.laplace_base_problem_engine import (
LaplaceBaseProblemEngine)
from rrompy.utilities.base.fenics import fenZERO, fenONE
from rrompy.utilities.base import verbosityDepth
__all__ = ['LaplaceDiskGaussian']
class LaplaceDiskGaussian(LaplaceBaseProblemEngine):
"""
Solver for disk Laplace problems with parametric forcing term center.
- \Delta u = C exp(-.5 * ||\cdot - (mu, 0)||^2) in \Omega = B(0, 5)
u = 0 on \partial\Omega.
"""
nbs = 20
def __init__(self, n:int, degree_threshold : int = np.inf,
verbosity : int = 10):
super().__init__(degree_threshold = degree_threshold,
verbosity = verbosity)
self.computebsFactors()
self.forcingTermMu = np.nan
import mshr
mesh = mshr.generate_mesh(mshr.Circle(fen.Point(0., 0.), 5.), n)
self.V = fen.FunctionSpace(mesh, "P", 3)
def getForcingTerm(self, mu:complex) -> Tuple[FenExpr, FenExpr]:
"""Compute forcing term."""
if not np.isclose(mu, self.forcingTermMu):
if self.verbosity >= 25:
verbosityDepth("INIT",
"Assembling base expression for forcing term.",
end = "")
x, y = fen.SpatialCoordinate(self.V.mesh())[:]
C = np.exp(-.5 * mu ** 2.)
CR, CI = np.real(C), np.imag(C)
f0 = (2 * np.pi) ** -.5 * fen.exp(-.5 * (x ** 2. + y ** 2.))
muR, muI = np.real(mu), np.imag(mu)
f1R = fen.exp(muR * x) * fen.cos(muI * x)
f1I = fen.exp(muR * x) * fen.sin(muI * x)
self.forcingTerm = [f0 * (CR * f1R - CI * f1I),
f0 * (CR * f1I + CI * f1R)]
self.forcingTermMu = mu
if self.verbosity >= 25:
verbosityDepth("DEL", " Done.", inline = True)
return self.forcingTerm
def computebsFactors(self):
self.bsFactors = np.zeros((self.nbs, self.nbs), dtype = float)
self.bsFactors[0, 0] = 1.
self.bsFactors[1, 1] = 1.
for j in range(2, self.nbs):
l = (j + 1) % 2 + 1
J = np.arange(l, j + 1, 2)
self.bsFactors[j, J] = self.bsFactors[j - 1, J - 1]
if l == 2:
l = 0
J = np.arange(l, j, 2)
self.bsFactors[j, J] += np.multiply(- 1 - J,
self.bsFactors[j - 1, J + 1])
self.bsFactors[j, l : j + 2 : 2] /= j
def getExtraFactorB(self, mu:complex, der:int) -> Tuple[FenExpr, FenExpr]:
"""Compute extra expression in RHS."""
if self.verbosity >= 25:
verbosityDepth("INIT", ("Assembling auxiliary expression for "
"forcing term derivative."), end = "")
muR, muI = np.real(mu), np.imag(mu)
x = fen.SpatialCoordinate(self.V.mesh())[0]
l = der % 2
if l == 0:
powR, powI = fenONE, fenZERO
else:
powR, powI = x - muR, fen.Constant(muI)
exprR, exprI = [self.bsFactors[der, l] * k for k in [powR, powI]]
for j in range(l + 2, der + 1, 2):
for _ in range(2):
powR, powI = (powR * (x - muR) - powI * muI,
powR * muI + powI * (x - muR))
exprR += self.bsFactors[der, j] * powR
exprI += self.bsFactors[der, j] * powI
if self.verbosity >= 25:
verbosityDepth("DEL", " Done.", inline = True)
return[exprR, exprI]
def b(self, mu:complex, der : int = 0,
homogeneized : bool = False) -> Np1D:
"""Assemble (derivative of) RHS of linear system."""
bnull = self.checkbInBounds(der, homogeneized)
if bnull is not None: return bnull
if homogeneized and not np.isclose(self.mu0BC, mu):
self.u0BC = self.liftDirichletData(mu)
if not np.isclose(self.bsmu, mu):
self.bsmu = mu
self.resetbs()
if self.bs[homogeneized][der] is None:
if self.verbosity >= 20:
verbosityDepth("INIT",
"Assembling forcing term b{}.".format(der))
if der < self.nbs:
fRe, fIm = self.getForcingTerm(mu)
cRe, cIm = self.getExtraFactorB(mu, der)
cfRe = cRe * fRe - cIm * fIm
cfIm = cRe * fIm + cIm * fRe
else:
cfRe, cfIm = fenZERO, fenZERO
parsRe = self.iterReduceQuadratureDegree(zip([cfRe],
["forcingTermDer{}Real".format(der)]))
parsIm = self.iterReduceQuadratureDegree(zip([cfIm],
["forcingTermDer{}Imag".format(der)]))
L0Re = fen.dot(cfRe, self.v) * fen.dx
L0Im = fen.dot(cfIm, self.v) * fen.dx
b0Re = fen.assemble(L0Re, form_compiler_parameters = parsRe)
b0Im = fen.assemble(L0Im, form_compiler_parameters = parsIm)
if homogeneized:
Ader = self.A(mu, der)
b0Re[:] -= np.real(Ader.dot(self.u0BC))
b0Im[:] -= np.imag(Ader.dot(self.u0BC))
DirichletBC0 = fen.DirichletBC(self.V, fenZERO,
self.DirichletBoundary)
DirichletBC0.apply(b0Re)
DirichletBC0.apply(b0Im)
self.bs[homogeneized][der] = np.array(b0Re[:]
+ 1.j * b0Im[:], dtype = np.complex)
if self.verbosity >= 20:
verbosityDepth("DEL", "Done assembling forcing term.")
return self.bs[homogeneized][der]
diff --git a/rrompy/reduction_methods/base/__init__.py b/rrompy/reduction_methods/base/__init__.py
index b662e98..853116c 100644
--- a/rrompy/reduction_methods/base/__init__.py
+++ b/rrompy/reduction_methods/base/__init__.py
@@ -1,27 +1,29 @@
# Copyright (C) 2018 by the RROMPy authors
#
# This file is part of RROMPy.
#
# RROMPy is free software: you can redistribute it and/or modify
# it under the terms of the GNU Lesser General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# RROMPy is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public License
# along with RROMPy. If not, see .
#
from .generic_approximant import GenericApproximant
+from .fit_utils import setupFitCallables
from .pade_utils import checkRobustTolerance
__all__ = [
'GenericApproximant',
+ 'setupFitCallables',
'checkRobustTolerance'
]
diff --git a/rrompy/reduction_methods/base/fit_utils.py b/rrompy/reduction_methods/base/fit_utils.py
new file mode 100644
index 0000000..9594a40
--- /dev/null
+++ b/rrompy/reduction_methods/base/fit_utils.py
@@ -0,0 +1,52 @@
+# Copyright (C) 2018 by the RROMPy authors
+#
+# This file is part of RROMPy.
+#
+# RROMPy is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Lesser General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# RROMPy is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public License
+# along with RROMPy. If not, see .
+#
+
+from numpy import pi, polynomial as po
+from rrompy.utilities.base.types import DictAny
+from rrompy.utilities.warning_manager import warn
+
+__all__ = ['setupFitCallables']
+
+def setupFitCallables(kind:str) -> DictAny:
+ """Setup main callables and name for data fitting."""
+ kind = kind.upper()
+ if kind == "CHEBYSHEV":
+ val = po.chebyshev.chebval
+ vander = po.chebyshev.chebvander
+ fitname = "chebfit"
+ roots = po.chebyshev.chebroots
+ domcoeff = lambda n: 2. ** (n - 1) if n > 0 else 1.
+ elif kind == "LEGENDRE":
+ val = po.legendre.legval
+ vander = po.legendre.legvander
+ fitname = "legfit"
+ roots = po.legendre.legroots
+ from scipy.special import binom
+ domcoeff = lambda n: (2. ** n * (pi * n) ** -.5 if n > 10
+ else .5 ** n * binom(2 * n, n))
+ else:
+ if kind != "MONOMIAL":
+ warn("Fitting basis not recognized. Overriding to 'MONOMIAL'.")
+ val = po.polynomial.polyval
+ vander = po.polynomial.polyvander
+ fitname = "polyfit"
+ roots = po.polynomial.polyroots
+ domcoeff = lambda n: 1.
+ return {"val":val, "vander":vander, "fitname":fitname,
+ "roots":roots, "domcoeff":domcoeff}
+
diff --git a/rrompy/reduction_methods/base/generic_approximant.py b/rrompy/reduction_methods/base/generic_approximant.py
index 52ec3ac..614f640 100644
--- a/rrompy/reduction_methods/base/generic_approximant.py
+++ b/rrompy/reduction_methods/base/generic_approximant.py
@@ -1,532 +1,533 @@
# Copyright (C) 2018 by the RROMPy authors
#
# This file is part of RROMPy.
#
# RROMPy is free software: you can redistribute it and/or modify
# it under the terms of the GNU Lesser General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# RROMPy is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public License
# along with RROMPy. If not, see .
#
from abc import abstractmethod
import numpy as np
from copy import copy
from rrompy.sampling.base.sampling_engine_base import SamplingEngineBase
from rrompy.utilities.base.types import Np1D, DictAny, HFEng, sampleEng, strLst
from rrompy.utilities.base import purgeDict, verbosityDepth
__all__ = ['GenericApproximant']
class GenericApproximant:
"""
ABSTRACT
ROM approximant computation for parametric problems.
Args:
HFEngine: HF problem solver.
mu0(optional): Default parameter. Defaults to 0.
approxParameters(optional): Dictionary containing values for main
parameters of approximant. Recognized keys are:
- 'POD': whether to compute POD of snapshots; defaults to True.
Defaults to empty dict.
+ homogeneized: Whether to homogeneize Dirichlet BCs. Defaults to False.
verbosity(optional): Verbosity level. Defaults to 10.
Attributes:
HFEngine: HF problem solver.
mu0: Default parameter.
- w: Weight for norm computation.
+ homogeneized: Whether to homogeneize Dirichlet BCs.
approxParameters: Dictionary containing values for main parameters of
approximant. Recognized keys are in parameterList.
parameterList: Recognized keys of approximant parameters:
- 'POD': whether to compute POD of snapshots.
verbosity: Verbosity level.
POD: Whether to compute POD of snapshots.
samplingEngine: Sampling engine.
uHF: High fidelity solution with wavenumber lastSolvedHF as numpy
complex vector.
lastSolvedHF: Wavenumber corresponding to last computed high fidelity
solution.
uApp: Last evaluated approximant as numpy complex vector.
lastApproxParameters: List of parameters corresponding to last
computed approximant.
"""
def __init__(self, HFEngine:HFEng, mu0 : complex = 0,
approxParameters : DictAny = {}, homogeneized : bool = False,
verbosity : int = 10):
self._preInit()
self.verbosity = verbosity
if self.verbosity >= 10:
verbosityDepth("INIT", ("Initializing approximant engine of "
"type {}.").format(self.name()))
self.HFEngine = HFEngine
self._HFEngine0 = copy(HFEngine)
self._addParametersToList(["POD"])
self.mu0 = mu0
self.homogeneized = homogeneized
self.approxParameters = approxParameters
self._postInit()
def _preInit(self):
if not hasattr(self, "depth"): self.depth = 0
else: self.depth += 1
def _addParametersToList(self, what:strLst):
if not hasattr(self, "parameterList"):
self.parameterList = []
self.parameterList += what
def _postInit(self):
if self.depth == 0:
if self.verbosity >= 10:
verbosityDepth("DEL", "Done initializing.\n")
del self.depth
else: self.depth -= 1
def name(self) -> str:
return self.__class__.__name__
def __str__(self) -> str:
return self.name()
def __repr__(self) -> str:
return self.__str__() + " at " + hex(id(self))
def setupSampling(self, SamplingEngine : sampleEng = SamplingEngineBase):
"""Setup sampling engine."""
self.samplingEngine = SamplingEngine(self.HFEngine,
verbosity = self.verbosity)
@property
def approxParameters(self):
"""Value of approximant parameters."""
return self._approxParameters
@approxParameters.setter
def approxParameters(self, approxParams):
if not hasattr(self, "approxParameters"):
self._approxParameters = {}
approxParameters = purgeDict(approxParams, self.parameterList,
dictname = self.name() + ".approxParameters",
baselevel = 1)
keyList = list(approxParameters.keys())
if "POD" in keyList:
self.POD = approxParameters["POD"]
elif hasattr(self, "POD"):
self.POD = self.POD
else:
self.POD = True
@property
def POD(self):
"""Value of POD."""
return self._POD
@POD.setter
def POD(self, POD):
if hasattr(self, "POD"): PODold = self.POD
else: PODold = -1
self._POD = POD
self._approxParameters["POD"] = self.POD
if PODold != self.POD:
self.setupSampling()
self.resetSamples()
@property
def homogeneized(self):
"""Value of homogeneized."""
return self._homogeneized
@homogeneized.setter
def homogeneized(self, homogeneized):
if not hasattr(self, "_homogeneized"):
self._homogeneized = None
if homogeneized != self.homogeneized:
self._homogeneized = homogeneized
self.resetSamples()
def solveHF(self, mu : complex = None):
"""
Find high fidelity solution with original parameters and arbitrary
parameter.
Args:
mu: Target parameter.
"""
if mu is None: mu = self.mu0
if (not hasattr(self, "lastSolvedHF")
or not np.isclose(self.lastSolvedHF, mu)):
self.uHF = self.samplingEngine.solveLS(mu,
homogeneized = self.homogeneized)
self.lastSolvedHF = mu
def resetSamples(self):
"""Reset samples."""
if hasattr(self, "samplingEngine"):
self.samplingEngine.resetHistory()
else:
self.setupSampling()
def plotSamples(self, name : str = "u", save : str = None,
what : strLst = 'all', saveFormat : str = "eps",
saveDPI : int = 100, **figspecs):
"""
Do some nice plots of the samples.
Args:
name(optional): Name to be shown as title of the plots. Defaults to
'u'.
what(optional): Which plots to do. If list, can contain 'ABS',
'PHASE', 'REAL', 'IMAG'. If str, same plus wildcard 'ALL'.
Defaults to 'ALL'.
save(optional): Where to save plot(s). Defaults to None, i.e. no
saving.
saveFormat(optional): Format for saved plot(s). Defaults to "eps".
saveDPI(optional): DPI for saved plot(s). Defaults to 100.
figspecs(optional key args): Optional arguments for matplotlib
figure creation.
"""
self.samplingEngine.plotSamples(name = name, save = save, what = what,
saveFormat = saveFormat,
saveDPI = saveDPI,
**figspecs)
@abstractmethod
def setupApprox(self):
"""
Setup approximant. (ABSTRACT)
Any specialization should include something like
self.computeDerivatives()
if not self.checkComputedApprox():
...
self.lastApproxParameters = copy(self.approxParameters)
"""
pass
def checkComputedApprox(self) -> bool:
"""
Check if setup of new approximant is not needed.
Returns:
True if new setup is not needed. False otherwise.
"""
return (hasattr(self, "lastApproxParameters")
and self.approxParameters == self.lastApproxParameters)
@abstractmethod
def evalApproxReduced(self, mu:complex):
"""
Evaluate reduced representation of approximant at arbitrary parameter.
(ABSTRACT)
Any specialization should include something like
self.setupApprox()
self.uAppReduced = ...
Args:
mu: Target parameter.
"""
pass
@abstractmethod
def evalApprox(self, mu:complex):
"""
Evaluate approximant at arbitrary parameter. (ABSTRACT)
Any specialization should include something like
self.evalApproxReduced(mu)
self.uApp = ...
Args:
mu: Target parameter.
"""
pass
def getHF(self, mu:complex, homogeneized : bool = False) -> Np1D:
"""
Get HF solution at arbitrary parameter.
Args:
mu: Target parameter.
homogeneized(optional): Whether to remove Dirichlet BC. Defaults to
False.
Returns:
HFsolution.
"""
self.solveHF(mu)
if self.homogeneized and not homogeneized:
return self.uHF + self.HFEngine.liftDirichletData(mu)
if not self.homogeneized and homogeneized:
return self.uHF - self.HFEngine.liftDirichletData(mu)
return self.uHF
def getRHS(self, mu:complex, homogeneized : bool = False) -> Np1D:
"""
Get linear system RHS at arbitrary parameter.
Args:
mu: Target parameter.
homogeneized(optional): Whether to remove Dirichlet BC. Defaults to
False.
Returns:
Linear system RHS.
"""
return self.HFEngine.residual(None, mu, homogeneized = homogeneized)
def getAppReduced(self, mu:complex) -> Np1D:
"""
Get approximant at arbitrary parameter.
Args:
mu: Target parameter.
Returns:
Reduced approximant.
"""
self.evalApproxReduced(mu)
return self.uAppReduced
def getApp(self, mu:complex, homogeneized : bool = False) -> Np1D:
"""
Get approximant at arbitrary parameter.
Args:
mu: Target parameter.
homogeneized(optional): Whether to remove Dirichlet BC. Defaults to
False.
Returns:
Approximant.
"""
self.evalApprox(mu)
if self.homogeneized and not homogeneized:
return self.uApp + self.HFEngine.liftDirichletData(mu)
if not self.homogeneized and homogeneized:
return self.uApp - self.HFEngine.liftDirichletData(mu)
return self.uApp
def getRes(self, mu:complex, homogeneized : bool = False) -> Np1D:
"""
Get residual at arbitrary parameter.
Args:
mu: Target parameter.
homogeneized(optional): Whether to remove Dirichlet BC. Defaults to
False.
Returns:
Approximant residual.
"""
return self.HFEngine.residual(self.getApp(mu, homogeneized), mu,
homogeneized = homogeneized)
def getErr(self, mu:complex, homogeneized : bool = False) -> Np1D:
"""
Get error at arbitrary parameter.
Args:
mu: Target parameter.
homogeneized(optional): Whether to remove Dirichlet BC. Defaults to
False.
Returns:
Approximant error.
"""
return self.getApp(mu, homogeneized) - self.getHF(mu, homogeneized)
@abstractmethod
def getPoles(self) -> Np1D:
"""
Obtain approximant poles.
Returns:
Numpy complex vector of poles.
"""
pass
def normHF(self, mu:complex, homogeneized : bool = False) -> float:
"""
Compute norm of HF solution at arbitrary parameter.
Args:
mu: Target parameter.
homogeneized(optional): Whether to remove Dirichlet BC. Defaults to
False.
Returns:
Target norm of HFsolution.
"""
return self.HFEngine.norm(self.getHF(mu, homogeneized))
def normRHS(self, mu:complex, homogeneized : bool = False) -> Np1D:
"""
Compute norm of linear system RHS at arbitrary parameter.
Args:
mu: Target parameter.
homogeneized(optional): Whether to remove Dirichlet BC. Defaults to
False.
Returns:
Norm of linear system RHS.
"""
return self.HFEngine.norm(self.getRHS(mu, homogeneized))
def normApp(self, mu:complex, homogeneized : bool = False) -> float:
"""
Compute norm of approximant at arbitrary parameter.
Args:
mu: Target parameter.
homogeneized(optional): Whether to remove Dirichlet BC. Defaults to
False.
Returns:
Target norm of approximant.
"""
return self.HFEngine.norm(self.getApp(mu, homogeneized))
def normRes(self, mu:complex, homogeneized : bool = False) -> float:
"""
Compute norm of approximant residual at arbitrary parameter.
Args:
mu: Target parameter.
homogeneized(optional): Whether to remove Dirichlet BC. Defaults to
False.
Returns:
Target norm of (A(mu)app(mu) - f(mu)).
"""
return self.HFEngine.norm(self.getRes(mu, homogeneized))
def normErr(self, mu:complex, homogeneized : bool = False) -> float:
"""
Compute norm of approximant error at arbitrary parameter.
Args:
mu: Target parameter.
homogeneized(optional): Whether to remove Dirichlet BC. Defaults to
False.
Returns:
Target norm of (approximant - HFsolution).
"""
return self.HFEngine.norm(self.getErr(mu, homogeneized))
def plotHF(self, mu:complex, name : str = "uHF", save : str = None,
what : strLst = 'all', saveFormat : str = "eps",
saveDPI : int = 100, homogeneized : bool = False, **figspecs):
"""
Do some nice plots of the HF solution at arbitrary parameter.
Args:
mu: Target parameter.
name(optional): Name to be shown as title of the plots. Defaults to
'u'.
what(optional): Which plots to do. If list, can contain 'ABS',
'PHASE', 'REAL', 'IMAG'. If str, same plus wildcard 'ALL'.
Defaults to 'ALL'.
save(optional): Where to save plot(s). Defaults to None, i.e. no
saving.
saveFormat(optional): Format for saved plot(s). Defaults to "eps".
saveDPI(optional): DPI for saved plot(s). Defaults to 100.
homogeneized(optional): Whether to remove Dirichlet BC. Defaults to
False.
figspecs(optional key args): Optional arguments for matplotlib
figure creation.
"""
uHF = self.getHF(mu, homogeneized)
self.HFEngine.plot(uHF, name = name, save = save, what = what,
saveFormat = saveFormat, saveDPI = saveDPI,
**figspecs)
def plotApp(self, mu:complex, name : str = "uApp", save : str = None,
what : strLst = 'all', saveFormat : str = "eps",
saveDPI : int = 100, homogeneized : bool = False, **figspecs):
"""
Do some nice plots of approximant at arbitrary parameter.
Args:
mu: Target parameter.
name(optional): Name to be shown as title of the plots. Defaults to
'u'.
what(optional): Which plots to do. If list, can contain 'ABS',
'PHASE', 'REAL', 'IMAG'. If str, same plus wildcard 'ALL'.
Defaults to 'ALL'.
save(optional): Where to save plot(s). Defaults to None, i.e. no
saving.
saveFormat(optional): Format for saved plot(s). Defaults to "eps".
saveDPI(optional): DPI for saved plot(s). Defaults to 100.
homogeneized(optional): Whether to remove Dirichlet BC. Defaults to
False.
figspecs(optional key args): Optional arguments for matplotlib
figure creation.
"""
uApp = self.getApp(mu, homogeneized)
self.HFEngine.plot(uApp, name = name, save = save, what = what,
saveFormat = saveFormat, saveDPI = saveDPI,
**figspecs)
def plotRes(self, mu:complex, name : str = "res", save : str = None,
what : strLst = 'all', saveFormat : str = "eps",
saveDPI : int = 100, homogeneized : bool = False, **figspecs):
"""
Do some nice plots of approximation residual at arbitrary parameter.
Args:
mu: Target parameter.
name(optional): Name to be shown as title of the plots. Defaults to
'u'.
what(optional): Which plots to do. If list, can contain 'ABS',
'PHASE', 'REAL', 'IMAG'. If str, same plus wildcard 'ALL'.
Defaults to 'ALL'.
save(optional): Where to save plot(s). Defaults to None, i.e. no
saving.
saveFormat(optional): Format for saved plot(s). Defaults to "eps".
saveDPI(optional): DPI for saved plot(s). Defaults to 100.
homogeneized(optional): Whether to remove Dirichlet BC. Defaults to
False.
figspecs(optional key args): Optional arguments for matplotlib
figure creation.
"""
uRes = self.getRes(mu, homogeneized)
self.HFEngine.plot(uRes, name = name, save = save, what = what,
saveFormat = saveFormat, saveDPI = saveDPI,
**figspecs)
def plotErr(self, mu:complex, name : str = "err", save : str = None,
what : strLst = 'all', saveFormat : str = "eps",
saveDPI : int = 100, homogeneized : bool = False, **figspecs):
"""
Do some nice plots of approximation error at arbitrary parameter.
Args:
mu: Target parameter.
name(optional): Name to be shown as title of the plots. Defaults to
'u'.
what(optional): Which plots to do. If list, can contain 'ABS',
'PHASE', 'REAL', 'IMAG'. If str, same plus wildcard 'ALL'.
Defaults to 'ALL'.
save(optional): Where to save plot(s). Defaults to None, i.e. no
saving.
saveFormat(optional): Format for saved plot(s). Defaults to "eps".
saveDPI(optional): DPI for saved plot(s). Defaults to 100.
homogeneized(optional): Whether to remove Dirichlet BC. Defaults to
False.
figspecs(optional key args): Optional arguments for matplotlib
figure creation.
"""
uErr = self.getErr(mu, homogeneized)
self.HFEngine.plot(uErr, name = name, save = save, what = what,
saveFormat = saveFormat, saveDPI = saveDPI,
**figspecs)
diff --git a/rrompy/reduction_methods/base/pade_utils.py b/rrompy/reduction_methods/base/pade_utils.py
index 5dbee93..958d928 100644
--- a/rrompy/reduction_methods/base/pade_utils.py
+++ b/rrompy/reduction_methods/base/pade_utils.py
@@ -1,38 +1,44 @@
# Copyright (C) 2018 by the RROMPy authors
#
# This file is part of RROMPy.
#
# RROMPy is free software: you can redistribute it and/or modify
# it under the terms of the GNU Lesser General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# RROMPy is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public License
# along with RROMPy. If not, see .
#
import numpy as np
-from rrompy.utilities.base.types import Np1D, Np2D, Tuple
+from rrompy.utilities.base.types import Np1D
from rrompy.utilities.warning_manager import warn
+__all__ = ['checkRobustTolerance']
+
def checkRobustTolerance(ev:Np1D, E:int, tol:float) -> dict:
+ """
+ Perform robustness check on eigen-/singular values and return reduced
+ parameters with warning.
+ """
N = len(ev) - 1
ts = tol * np.linalg.norm(ev)
diff = N - np.sum(np.abs(ev) >= ts)
if diff <= 0: return {}
Enew = E - diff
Nnew = min(N, Enew)
if Nnew == N:
strN = ""
else:
strN = "N from {} to {} and ".format(N, Nnew)
warn(("Smallest {} eigenvalues below tolerance.\nReducing {}E from {} to "
"{}.").format(diff + 1, strN, E, Enew))
newParameters = {"N" : Nnew, "E" : Enew}
return newParameters
diff --git a/rrompy/reduction_methods/lagrange/__init__.py b/rrompy/reduction_methods/lagrange/__init__.py
index be53e5b..822bd2a 100644
--- a/rrompy/reduction_methods/lagrange/__init__.py
+++ b/rrompy/reduction_methods/lagrange/__init__.py
@@ -1,32 +1,29 @@
# Copyright (C) 2018 by the RROMPy authors
#
# This file is part of RROMPy.
#
# RROMPy is free software: you can redistribute it and/or modify
# it under the terms of the GNU Lesser General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# RROMPy is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public License
# along with RROMPy. If not, see .
#
from .generic_approximant_lagrange import GenericApproximantLagrange
from .approximant_lagrange_pade import ApproximantLagrangePade
-from .approximant_lagrange_pade_orthogonal import (
- ApproximantLagrangePadeOrthogonal)
from .approximant_lagrange_rb import ApproximantLagrangeRB
__all__ = [
'GenericApproximantLagrange',
'ApproximantLagrangePade',
- 'ApproximantLagrangePadeOrthogonal',
'ApproximantLagrangeRB'
]
diff --git a/rrompy/reduction_methods/lagrange/approximant_lagrange_pade.py b/rrompy/reduction_methods/lagrange/approximant_lagrange_pade.py
index e542b1f..11f1846 100644
--- a/rrompy/reduction_methods/lagrange/approximant_lagrange_pade.py
+++ b/rrompy/reduction_methods/lagrange/approximant_lagrange_pade.py
@@ -1,448 +1,479 @@
# Copyright (C) 2018 by the RROMPy authors
#
# This file is part of RROMPy.
#
# RROMPy is free software: you can redistribute it and/or modify
# it under the terms of the GNU Lesser General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# RROMPy is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public License
# along with RROMPy. If not, see .
#
from copy import copy
import numpy as np
-from rrompy.reduction_methods.base import checkRobustTolerance
+from rrompy.reduction_methods.base import (checkRobustTolerance,
+ setupFitCallables)
from .generic_approximant_lagrange import GenericApproximantLagrange
-from rrompy.utilities.base.types import Np1D, DictAny, List, HFEng
-from rrompy.utilities.base import purgeDict, verbosityDepth
+from rrompy.utilities.base.types import Np1D, List, HFEng, DictAny
+from rrompy.utilities.base import verbosityDepth, purgeDict, customFit
from rrompy.utilities.warning_manager import warn
__all__ = ['ApproximantLagrangePade']
class ApproximantLagrangePade(GenericApproximantLagrange):
"""
ROM Lagrange Pade' interpolant computation for parametric problems.
Args:
HFEngine: HF problem solver.
mu0(optional): Default parameter. Defaults to 0.
approxParameters(optional): Dictionary containing values for main
parameters of approximant. Recognized keys are:
- 'POD': whether to compute POD of snapshots; defaults to True;
- 'S': total number of samples current approximant relies upon;
defaults to 2;
- 'sampler': sample point generator; defaults to uniform sampler on
[0, 1];
+ - 'basis': type of basis for interpolation; allowed values include
+ 'MONOMIAL', 'CHEBYSHEV' and 'LEGENDRE'; defaults to 'MONOMIAL';
- 'E': coefficient of interpolant to be minimized; defaults to
min(S, M + 1);
- 'M': degree of Pade' interpolant numerator; defaults to 0;
- 'N': degree of Pade' interpolant denominator; defaults to 0;
- 'interpRcond': tolerance for interpolation via numpy.polyfit;
defaults to None;
- 'robustTol': tolerance for robust Pade' denominator management;
defaults to 0.
Defaults to empty dict.
+ homogeneized: Whether to homogeneize Dirichlet BCs. Defaults to False.
+ verbosity(optional): Verbosity level. Defaults to 10.
Attributes:
HFEngine: HF problem solver.
mu0: Default parameter.
mus: Array of snapshot parameters.
ws: Array of snapshot weigths.
+ homogeneized: Whether to homogeneize Dirichlet BCs.
approxParameters: Dictionary containing values for main parameters of
approximant. Recognized keys are in parameterList.
parameterList: Recognized keys of approximant parameters:
- 'POD': whether to compute POD of snapshots;
- 'S': total number of samples current approximant relies upon;
- 'sampler': sample point generator;
+ - 'basis': type of basis for interpolation;
- 'E': coefficient of interpolant to be minimized;
- 'M': degree of Pade' interpolant numerator;
- 'N': degree of Pade' interpolant denominator;
- 'interpRcond': tolerance for interpolation via numpy.polyfit;
- 'robustTol': tolerance for robust Pade' denominator management.
extraApproxParameters: List of approxParameters keys in addition to
mother class's.
S: Number of solution snapshots over which current approximant is
based upon.
sampler: Sample point generator.
M: Numerator degree of approximant.
N: Denominator degree of approximant.
POD: Whether to compute POD of snapshots.
interpRcond: Tolerance for interpolation via numpy.polyfit.
robustTol: Tolerance for robust Pade' denominator management.
samplingEngine: Sampling engine.
uHF: High fidelity solution with wavenumber lastSolvedHF as numpy
complex vector.
lastSolvedHF: Wavenumber corresponding to last computed high fidelity
solution.
Q: Numpy 1D vector containing complex coefficients of approximant
denominator.
P: Numpy 2D vector whose columns are FE dofs of coefficients of
approximant numerator.
uApp: Last evaluated approximant as numpy complex vector.
lastApproxParameters: List of parameters corresponding to last
computed approximant.
"""
def __init__(self, HFEngine:HFEng, mu0 : complex = 0.,
approxParameters : DictAny = {}, homogeneized : bool = False,
verbosity : int = 10):
self._preInit()
- self._addParametersToList(["E", "M", "N", "interpRcond", "robustTol"])
+ self._addParametersToList(["basis", "E", "M", "N",
+ "interpRcond", "robustTol"])
super().__init__(HFEngine = HFEngine, mu0 = mu0,
approxParameters = approxParameters,
homogeneized = homogeneized,
verbosity = verbosity)
self._postInit()
@property
def approxParameters(self):
"""
- Value of approximant parameters. Its assignment may change E, M, N,
- robustTol and S.
+ Value of approximant parameters.
"""
return self._approxParameters
@approxParameters.setter
def approxParameters(self, approxParams):
approxParameters = purgeDict(approxParams, self.parameterList,
dictname = self.name() + ".approxParameters",
baselevel = 1)
- approxParametersCopy = purgeDict(approxParameters, ["E", "M", "N",
+ approxParametersCopy = purgeDict(approxParameters, ["basis",
+ "E", "M", "N",
"interpRcond",
"robustTol"],
True, True, baselevel = 1)
if hasattr(self, "M"):
Mold = self.M
self._M = 0
if hasattr(self, "N"):
Nold = self.N
self._N = 0
if hasattr(self, "E"):
self._E = 0
GenericApproximantLagrange.approxParameters.fset(self,
approxParametersCopy)
keyList = list(approxParameters.keys())
+ if "basis" in keyList or not hasattr(self, "_val"):
+ if "basis" in keyList:
+ kind = approxParameters["basis"]
+ else:
+ kind = "MONOMIAL"
+ setupFit = setupFitCallables(kind)
+ for x in setupFit:
+ super().__setattr__("_" + x, setupFit[x])
if "interpRcond" in keyList:
self.interpRcond = approxParameters["interpRcond"]
elif hasattr(self, "interpRcond"):
self.interpRcond = self.interpRcond
else:
self.interpRcond = None
if "robustTol" in keyList:
self.robustTol = approxParameters["robustTol"]
elif hasattr(self, "robustTol"):
self.robustTol = self.robustTol
else:
self.robustTol = 0
if "M" in keyList:
self.M = approxParameters["M"]
elif hasattr(self, "M"):
self.M = Mold
else:
self.M = 0
if "N" in keyList:
self.N = approxParameters["N"]
elif hasattr(self, "N"):
self.N = Nold
else:
self.N = 0
if "E" in keyList:
self.E = approxParameters["E"]
else:
self.E = min(self.S - 1, self.M + 1)
@property
def M(self):
"""Value of M. Its assignment may change S."""
return self._M
@M.setter
def M(self, M):
if M < 0: raise ArithmeticError("M must be non-negative.")
self._M = M
self._approxParameters["M"] = self.M
if hasattr(self, "S") and self.S < self.M + 1:
warn("Prescribed S is too small. Updating S to M + 1.")
self.S = self.M + 1
@property
def N(self):
"""Value of N. Its assignment may change S."""
return self._N
@N.setter
def N(self, N):
if N < 0: raise ArithmeticError("N must be non-negative.")
self._N = N
self._approxParameters["N"] = self.N
if hasattr(self, "S") and self.S < self.N + 1:
warn("Prescribed S is too small. Updating S to N + 1.")
self.S = self.N + 1
@property
def E(self):
"""Value of E. Its assignment may change S."""
return self._E
@E.setter
def E(self, E):
if E < 0: raise ArithmeticError("E must be non-negative.")
self._E = E
self._approxParameters["E"] = self.E
if hasattr(self, "S") and self.S < self.E + 1:
warn("Prescribed S is too small. Updating S to E + 1.")
self.S = self.E + 1
@property
def robustTol(self):
"""Value of tolerance for robust Pade' denominator management."""
return self._robustTol
@robustTol.setter
def robustTol(self, robustTol):
if robustTol < 0.:
warn("Overriding prescribed negative robustness tolerance to 0.")
robustTol = 0.
self._robustTol = robustTol
self._approxParameters["robustTol"] = self.robustTol
@property
def S(self):
"""Value of S."""
return self._S
@S.setter
def S(self, S):
if S <= 0: raise ArithmeticError("S must be positive.")
if hasattr(self, "S"): Sold = self.S
else: Sold = -1
vals, label = [0] * 3, {0:"M", 1:"N", 2:"E"}
if hasattr(self, "M"): vals[0] = self.M
if hasattr(self, "N"): vals[1] = self.N
if hasattr(self, "E"): vals[2] = self.E
idxmax = np.argmax(vals)
if vals[idxmax] + 1 > S:
warn("Prescribed S is too small. Updating S to {} + 1."\
.format(label[idxmax]))
self.S = vals[idxmax] + 1
else:
self._S = S
self._approxParameters["S"] = self.S
if Sold != self.S:
self.resetSamples()
def setupApprox(self):
"""
Compute Pade' interpolant.
SVD-based robust eigenvalue management.
"""
if not self.checkComputedApprox():
if self.verbosity >= 5:
verbosityDepth("INIT", "Setting up {}.". format(self.name()))
- self.computeRescaleParameter()
+ self.computeScaleFactor()
self.computeSnapshots()
if self.N > 0:
if self.verbosity >= 7:
verbosityDepth("INIT", ("Starting computation of "
"denominator."))
- TNE = np.vander(self.radiusPade(self.mus), N = self.N + 1)
+ TE = self._vander(self.radiusPade(self.mus), self.E)
while self.N > 0:
- TN = TNE[:, TNE.shape[1] - self.N - 1 :]
- if self.POD:
- data = self.samplingEngine.RPOD
- else:
- data = self.samplingEngine.samples
- RHSFull = np.empty((self.S, data.shape[0] * (self.N + 1)),
- dtype = np.complex)
- for j in range(self.S):
- RHSFull[j, :] = np.kron(data[:, j], TN[j, :])
- fitOut = np.polyfit(self.radiusPade(self.mus), RHSFull,
- self.E, w = self.ws, full = True,
- rcond = self.interpRcond)
+ RHS = np.zeros(self.E + 1)
+ RHS[-1] = 1.
+ fitOut = customFit(TE.T, RHS, w = self.ws, full = True,
+ rcond = self.interpRcond)
if self.verbosity >= 5:
verbosityDepth("MAIN", ("Fitting {} samples with "
"degree {} through {}... "
"Conditioning of LS system: "
"{:.4e}.").format(
- self.S, self.E, "polyfit",
- fitOut[3][0] / fitOut[3][-1]))
- if fitOut[2] < self.E + 1:
- Enew = fitOut[2] - 1
+ self.S, self.E, self._fitname,
+ fitOut[1][2][0] / fitOut[1][2][-1]))
+ if fitOut[1][1] < self.E + 1:
+ Enew = fitOut[1][1] - 1
Nnew = min(self.N, Enew)
Mnew = min(self.M, Enew)
if Nnew == self.N:
strN = ""
else:
strN = "N from {} to {} and ".format(self.N, Nnew)
if Mnew == self.M:
strM = ""
else:
strM = "M from {} to {} and ".format(self.M, Mnew)
warn(("Polyfit is poorly conditioned.\nReducing {}{}E "
"from {} to {}.").format(strN, strM,
self.E, Enew))
newParameters = {"N" : Nnew, "M" : Mnew, "E" : Enew}
self.approxParameters = newParameters
continue
- G = fitOut[0][0, :].reshape((self.N + 1, data.shape[0]))
+ G = (TE[:, : self.N + 1].T * fitOut[0]).T
if self.POD:
if self.verbosity >= 7:
verbosityDepth("INIT", ("Solving svd for square "
"root of gramian matrix."),
end = "")
- _, ev, eV = np.linalg.svd(G, full_matrices = False)
- ev = ev[::-1]
+ G = self.samplingEngine.RPOD.dot(G)
+ _, s, eV = np.linalg.svd(G, full_matrices = False)
+ ev = s[::-1]
eV = eV[::-1, :].conj().T
+ if self.verbosity >= 5:
+ try: condev = s[0] / s[-1]
+ except: condev = np.inf
+ verbosityDepth("MAIN", ("Solved svd problem of "
+ "size {} x {} with "
+ "condition number "
+ "{:.4e}.").format(
+ self.S, self.N + 1, condev))
else:
if self.verbosity >= 10:
verbosityDepth("INIT", "Building gramian matrix.",
end = "")
+ G = self.samplingEngine.samples.dot(G)
G2 = self.HFEngine.innerProduct(G, G)
if self.verbosity >= 10:
verbosityDepth("DEL", "Done building gramian.",
inline = True)
if self.verbosity >= 7:
verbosityDepth("INIT", ("Solving eigenvalue "
"problem for gramian "
"matrix."), end = "")
ev, eV = np.linalg.eigh(G2)
+ if self.verbosity >= 5:
+ try: condev = ev[-1] / ev[0]
+ except: condev = np.inf
+ verbosityDepth("MAIN", ("Solved eigenvalue "
+ "problem of size {} with "
+ "condition number "
+ "{:.4e}.").format(
+ self.N + 1, condev))
if self.verbosity >= 7:
verbosityDepth("DEL", " Done.", inline = True)
newParameters = checkRobustTolerance(ev, self.E,
self.robustTol)
if not newParameters:
break
self.approxParameters = newParameters
if self.N <= 0:
+ self._N = 0
eV = np.ones((1, 1))
- self.Q = np.poly1d(eV[:, 0])
+ self.Q = eV[:, 0]
if self.verbosity >= 7:
verbosityDepth("DEL", "Done computing denominator.")
else:
- self.Q = np.poly1d([1])
+ self.Q = np.ones(1, dtype = np.complex)
if self.verbosity >= 7:
verbosityDepth("INIT", "Starting computation of numerator.")
self.lastApproxParameters = copy(self.approxParameters)
Qevaldiag = np.diag(self.getQVal(self.mus))
while self.M >= 0:
- fitOut = np.polyfit(self.radiusPade(self.mus), Qevaldiag,
- self.M, w = self.ws, full = True,
- rcond = self.interpRcond)
+ fitVander = self._vander(self.radiusPade(self.mus), self.M)
+ fitOut = customFit(fitVander, Qevaldiag, w = self.ws,
+ full = True, rcond = self.interpRcond)
if self.verbosity >= 5:
verbosityDepth("MAIN", ("Fitting {} samples with degree "
"{} through {}... Conditioning of "
"LS system: {:.4e}.").format(
- self.S, self.M, "polyfit",
- fitOut[3][0] / fitOut[3][-1]))
- if fitOut[2] == self.M + 1:
+ self.S, self.M, self._fitname,
+ fitOut[1][2][0] / fitOut[1][2][-1]))
+ if fitOut[1][1] == self.M + 1:
P = fitOut[0].T
break
warn(("Polyfit is poorly conditioned. Reducing M from {} to "
"{}. Exact snapshot interpolation not guaranteed.")\
- .format(self.M, fitOut[2] - 1))
- self.M = fitOut[2] - 1
+ .format(self.M, fitOut[1][1] - 1))
+ self.M = fitOut[1][1] - 1
+ if self.M <= 0:
+ raise Exception(("Instability in computation of numerator. "
+ "Aborting."))
self.P = np.atleast_2d(P)
if self.POD:
self.P = self.samplingEngine.RPOD.dot(self.P)
if self.verbosity >= 7:
verbosityDepth("DEL", "Done computing numerator.")
self.lastApproxParameters = copy(self.approxParameters)
if hasattr(self, "lastSolvedApp"): del self.lastSolvedApp
if self.verbosity >= 5:
verbosityDepth("DEL", "Done setting up approximant.\n")
def radiusPade(self, mu:Np1D, mu0 : float = None) -> float:
"""
Compute translated radius to be plugged into Pade' approximant.
Args:
mu: Parameter(s) 1.
mu0: Parameter(s) 2. If None, set to self.mu0.
Returns:
Translated radius to be plugged into Pade' approximant.
"""
if mu0 is None: mu0 = self.mu0
return ((self.HFEngine.rescaling(mu) - self.HFEngine.rescaling(mu0))
/ self.scaleFactor)
def getPVal(self, mu:List[complex]):
"""
Evaluate Pade' numerator at arbitrary parameter.
Args:
mu: Target parameter.
"""
self.setupApprox()
if self.verbosity >= 10:
verbosityDepth("INIT",
"Evaluating numerator at mu = {}.".format(mu),
end = "")
try:
len(mu)
except:
mu = [mu]
- powerlist = np.vander(self.radiusPade(mu), self.P.shape[1])
- p = self.P.dot(powerlist.T)
+ p = self._val(self.radiusPade(mu), self.P.T)
if len(mu) == 1:
p = p.flatten()
if self.verbosity >= 10:
verbosityDepth("DEL", " Done.", inline = True)
return p
def getQVal(self, mu:List[complex]):
"""
Evaluate Pade' denominator at arbitrary parameter.
Args:
mu: Target parameter.
"""
self.setupApprox()
if self.verbosity >= 10:
verbosityDepth("INIT",
"Evaluating denominator at mu = {}.".format(mu),
end = "")
- q = self.Q(self.radiusPade(mu))
+ q = self._val(self.radiusPade(mu), self.Q)
if self.verbosity >= 10:
verbosityDepth("DEL", " Done.", inline = True)
return q
def evalApproxReduced(self, mu:complex):
"""
Evaluate Pade' approximant at arbitrary parameter.
Args:
mu: Target parameter.
"""
self.setupApprox()
if (not hasattr(self, "lastSolvedApp")
or not np.isclose(self.lastSolvedApp, mu)):
if self.verbosity >= 5:
verbosityDepth("INIT",
"Evaluating approximant at mu = {}.".format(mu))
self.uAppReduced = self.getPVal(mu) / self.getQVal(mu)
self.lastSolvedApp = mu
if self.verbosity >= 5:
verbosityDepth("DEL", "Done evaluating approximant.")
def evalApprox(self, mu:complex):
"""
Evaluate approximant at arbitrary parameter.
Args:
mu: Target parameter.
"""
self.evalApproxReduced(mu)
self.uApp = self.samplingEngine.samples.dot(self.uAppReduced)
def getPoles(self) -> Np1D:
"""
Obtain approximant poles.
Returns:
Numpy complex vector of poles.
"""
self.setupApprox()
- return self.HFEngine.rescalingInv(self.scaleFactor * self.Q.r
- + self.HFEngine.rescaling(self.mu0))
+ return self.HFEngine.rescalingInv(
+ self.scaleFactor * self._roots(self.Q)
+ + self.HFEngine.rescaling(self.mu0))
+
diff --git a/rrompy/reduction_methods/lagrange/approximant_lagrange_pade_orthogonal.py b/rrompy/reduction_methods/lagrange/approximant_lagrange_pade_orthogonal.py
deleted file mode 100644
index c972a5a..0000000
--- a/rrompy/reduction_methods/lagrange/approximant_lagrange_pade_orthogonal.py
+++ /dev/null
@@ -1,299 +0,0 @@
-# Copyright (C) 2018 by the RROMPy authors
-#
-# This file is part of RROMPy.
-#
-# RROMPy is free software: you can redistribute it and/or modify
-# it under the terms of the GNU Lesser General Public License as published by
-# the Free Software Foundation, either version 3 of the License, or
-# (at your option) any later version.
-#
-# RROMPy is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-# GNU Lesser General Public License for more details.
-#
-# You should have received a copy of the GNU Lesser General Public License
-# along with RROMPy. If not, see .
-#
-
-from copy import copy
-import numpy as np
-from rrompy.reduction_methods.base import checkRobustTolerance
-from .approximant_lagrange_pade import ApproximantLagrangePade
-from rrompy.utilities.base.types import Np1D, List
-from rrompy.utilities.base import verbosityDepth
-from rrompy.utilities.warning_manager import warn
-
-__all__ = ['ApproximantLagrangePadeOrthogonal']
-
-class ApproximantLagrangePadeOrthogonal(ApproximantLagrangePade):
- """
- ROM Lagrange Pade' interpolant computation for parametric problems with
- rational-type interpolation basis functions.
-
- Args:
- HFEngine: HF problem solver.
- mu0(optional): Default parameter. Defaults to 0.
- approxParameters(optional): Dictionary containing values for main
- parameters of approximant. Recognized keys are:
- - 'POD': whether to compute POD of snapshots; defaults to True;
- - 'S': total number of samples current approximant relies upon;
- defaults to 2;
- - 'sampler': sample point generator; defaults to uniform sampler on
- [0, 1];
- - 'E': coefficient of interpolant to be minimized; defaults to
- min(S, M + 1);
- - 'M': degree of Pade' interpolant numerator; defaults to 0;
- - 'N': degree of Pade' interpolant denominator; defaults to 0;
- - 'interpRcond': tolerance for interpolation via numpy.polyfit;
- defaults to None;
- - 'robustTol': tolerance for robust Pade' denominator management;
- defaults to 0.
- Defaults to empty dict.
-
- Attributes:
- HFEngine: HF problem solver.
- mu0: Default parameter.
- mus: Array of snapshot parameters.
- ws: Array of snapshot weigths.
- approxParameters: Dictionary containing values for main parameters of
- approximant. Recognized keys are in parameterList.
- parameterList: Recognized keys of approximant parameters:
- - 'POD': whether to compute POD of snapshots;
- - 'S': total number of samples current approximant relies upon;
- - 'sampler': sample point generator;
- - 'E': coefficient of interpolant to be minimized;
- - 'M': degree of Pade' interpolant numerator;
- - 'N': degree of Pade' interpolant denominator;
- - 'interpRcond': tolerance for interpolation via numpy.polyfit;
- - 'robustTol': tolerance for robust Pade' denominator management.
- extraApproxParameters: List of approxParameters keys in addition to
- mother class's.
- S: Number of solution snapshots over which current approximant is
- based upon.
- sampler: Sample point generator.
- M: Numerator degree of approximant.
- N: Denominator degree of approximant.
- POD: Whether to compute POD of snapshots.
- interpRcond: Tolerance for interpolation via numpy.polyfit.
- robustTol: Tolerance for robust Pade' denominator management.
- samplingEngine: Sampling engine.
- uHF: High fidelity solution with wavenumber lastSolvedHF as numpy
- complex vector.
- lastSolvedHF: Wavenumber corresponding to last computed high fidelity
- solution.
- Q: Numpy 1D vector containing complex coefficients of approximant
- denominator.
- P: Numpy 2D vector whose columns are FE dofs of coefficients of
- approximant numerator.
- uApp: Last evaluated approximant as numpy complex vector.
- lastApproxParameters: List of parameters corresponding to last
- computed approximant.
- """
-
- @property
- def sampler(self):
- """Value of sampler."""
- return self._sampler
- @sampler.setter
- def sampler(self, sampler):
- if 'generatePoints' not in dir(sampler):
- raise Exception("Sampler type not recognized.")
- if hasattr(self, '_sampler'):
- samplerOld = self.sampler
- self._sampler = sampler
- self._approxParameters["sampler"] = self.sampler
- if not 'samplerOld' in locals() or samplerOld != self.sampler:
- if hasattr(self.sampler, "kind"):
- if self.sampler.kind == "CHEBYSHEV":
- self._val = np.polynomial.chebyshev.chebval
- self._vander = np.polynomial.chebyshev.chebvander
- self._fit = np.polynomial.chebyshev.chebfit
- self._roots = np.polynomial.chebyshev.chebroots
- self._domcoeff = lambda n: 2. ** max(0, n - 1)
- else:#if self.sampler.kind in ["UNIFORM", "GAUSSLEGENDRE"]:
- self._val = np.polynomial.legendre.legval
- self._vander = np.polynomial.legendre.legvander
- self._fit = np.polynomial.legendre.legfit
- self._roots = np.polynomial.legendre.legroots
- from scipy.special import binom
- self._domcoeff = lambda n: binom(2 * n, n)
- else:
- self._val = np.polynomial.polynomial.polyval
- self._vander = np.polynomial.polynomial.polyvander
- self._fit = np.polynomial.polynomial.polyfit
- self._roots = np.polynomial.polynomial.polyroots
- self._domcoeff = lambda n: 1.
- self.resetSamples()
-
- def setupApprox(self):
- """
- Compute Pade' interpolant.
- SVD-based robust eigenvalue management.
- """
- if not self.checkComputedApprox():
- if self.verbosity >= 5:
- verbosityDepth("INIT", "Setting up {}.". format(self.name()))
- self.computeRescaleParameter()
- self.computeSnapshots()
- if self.N > 0:
- if self.verbosity >= 7:
- verbosityDepth("INIT", ("Starting computation of "
- "denominator."))
- TN = self._vander(self.radiusPade(self.mus), self.N)
- while self.N > 0:
- TN = TN[:, : self.N + 1]
- if self.POD:
- data = self.samplingEngine.RPOD
- else:
- data = self.samplingEngine.samples
- RHSFull = np.empty((self.S, data.shape[0] * (self.N + 1)),
- dtype = np.complex)
- for j in range(self.S):
- RHSFull[j, :] = np.kron(data[:, j], TN[j, :])
- fitOut = self._fit(self.radiusPade(self.mus), RHSFull,
- self.E, w = self.ws, full = True,
- rcond = self.interpRcond)
- if self.verbosity >= 5:
- verbosityDepth("MAIN", ("Fitting {} samples with "
- "degree {} through {}... "
- "Conditioning of LS system: "
- "{:.4e}.").format(
- self.S, self.E, self._fit.__name__,
- fitOut[1][2][0] / fitOut[1][2][-1]))
- if fitOut[1][1] < self.E + 1:
- Enew = fitOut[1][1] - 1
- Nnew = min(self.N, Enew)
- Mnew = min(self.M, Enew)
- if Nnew == self.N:
- strN = ""
- else:
- strN = "N from {} to {} and ".format(self.N, Nnew)
- if Mnew == self.M:
- strM = ""
- else:
- strM = "M from {} to {} and ".format(self.M, Mnew)
- warn(("Polyfit is poorly conditioned.\nReducing {}{}E "
- "from {} to {}.").format(strN, strM,
- self.E, Enew))
- newParameters = {"N" : Nnew, "M" : Mnew, "E" : Enew}
- self.approxParameters = newParameters
- continue
- G = fitOut[0][-1, :].reshape((self.N + 1, data.shape[0]))
- if self.POD:
- if self.verbosity >= 7:
- verbosityDepth("INIT", ("Solving svd for square "
- "root of gramian matrix."),
- end = "")
- _, ev, eV = np.linalg.svd(G, full_matrices = False)
- ev = ev[::-1]
- eV = eV[::-1, :].conj().T
- else:
- if self.verbosity >= 10:
- verbosityDepth("INIT", "Building gramian matrix.",
- end = "")
- G2 = self.HFEngine.innerProduct(G, G)
- if self.verbosity >= 10:
- verbosityDepth("DEL", "Done building gramian.",
- inline = True)
- if self.verbosity >= 7:
- verbosityDepth("INIT", ("Solving eigenvalue "
- "problem for gramian "
- "matrix."), end = "")
- ev, eV = np.linalg.eigh(G2)
- if self.verbosity >= 7:
- verbosityDepth("DEL", " Done.", inline = True)
- newParameters = checkRobustTolerance(ev, self.E,
- self.robustTol)
- if not newParameters:
- break
- self.approxParameters = newParameters
- if self.N <= 0:
- eV = np.ones((1, 1))
- self.Q = eV[:, 0]
- if self.verbosity >= 7:
- verbosityDepth("DEL", "Done computing denominator.")
- else:
- self.Q = np.ones(1, dtype = np.complex)
- if self.verbosity >= 7:
- verbosityDepth("INIT", "Starting computation of numerator.")
- self.lastApproxParameters = copy(self.approxParameters)
- Qevaldiag = np.diag(self.getQVal(self.mus))
- while self.M >= 0:
- fitOut = self._fit(self.radiusPade(self.mus), Qevaldiag,
- self.M, w = self.ws, full = True,
- rcond = self.interpRcond)
- if self.verbosity >= 5:
- verbosityDepth("MAIN", ("Fitting {} samples with degree "
- "{} through {}... Conditioning of "
- "LS system: {:.4e}.").format(
- self.S, self.M, self._fit.__name__,
- fitOut[1][2][0] / fitOut[1][2][-1]))
- if fitOut[1][1] == self.M + 1:
- P = fitOut[0].T
- break
- warn(("Polyfit is poorly conditioned. Reducing M from {} to "
- "{}. Exact snapshot interpolation not guaranteed.")\
- .format(self.M, fitOut[1][1] - 1))
- self.M = fitOut[1][1] - 1
- self.P = np.atleast_2d(P)
- if self.POD:
- self.P = self.samplingEngine.RPOD.dot(self.P)
- if self.verbosity >= 7:
- verbosityDepth("DEL", "Done computing numerator.")
- self.lastApproxParameters = copy(self.approxParameters)
- if hasattr(self, "lastSolvedApp"): del self.lastSolvedApp
- if self.verbosity >= 5:
- verbosityDepth("DEL", "Done setting up approximant.\n")
-
- def getPVal(self, mu:List[complex]):
- """
- Evaluate Pade' numerator at arbitrary parameter.
-
- Args:
- mu: Target parameter.
- """
- self.setupApprox()
- if self.verbosity >= 10:
- verbosityDepth("INIT",
- "Evaluating numerator at mu = {}.".format(mu),
- end = "")
- try:
- len(mu)
- except:
- mu = [mu]
- p = self._val(self.radiusPade(mu), self.P.T)
- if len(mu) == 1:
- p = p.flatten()
- if self.verbosity >= 10:
- verbosityDepth("DEL", " Done.", inline = True)
- return p
-
- def getQVal(self, mu:List[complex]):
- """
- Evaluate Pade' denominator at arbitrary parameter.
-
- Args:
- mu: Target parameter.
- """
- self.setupApprox()
- if self.verbosity >= 10:
- verbosityDepth("INIT",
- "Evaluating denominator at mu = {}.".format(mu),
- end = "")
- q = self._val(self.radiusPade(mu), self.Q)
- if self.verbosity >= 10:
- verbosityDepth("DEL", " Done.", inline = True)
- return q
-
- def getPoles(self) -> Np1D:
- """
- Obtain approximant poles.
-
- Returns:
- Numpy complex vector of poles.
- """
- self.setupApprox()
- return self.HFEngine.rescalingInv(
- self.scaleFactor * self._roots(self.Q)
- + self.HFEngine.rescaling(self.mu0))
diff --git a/rrompy/reduction_methods/lagrange/approximant_lagrange_rb.py b/rrompy/reduction_methods/lagrange/approximant_lagrange_rb.py
index 1b60785..f74db9b 100644
--- a/rrompy/reduction_methods/lagrange/approximant_lagrange_rb.py
+++ b/rrompy/reduction_methods/lagrange/approximant_lagrange_rb.py
@@ -1,289 +1,292 @@
# Copyright (C) 2018 by the RROMPy authors
#
# This file is part of RROMPy.
#
# RROMPy is free software: you can redistribute it and/or modify
# it under the terms of the GNU Lesser General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# RROMPy is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public License
# along with RROMPy. If not, see .
#
from copy import copy
import numpy as np
import scipy as sp
from .generic_approximant_lagrange import GenericApproximantLagrange
from rrompy.utilities.base.types import Np1D, DictAny, HFEng
from rrompy.utilities.base import purgeDict, verbosityDepth
from rrompy.utilities.warning_manager import warn
__all__ = ['ApproximantLagrangeRB']
class ApproximantLagrangeRB(GenericApproximantLagrange):
"""
ROM RB approximant computation for parametric problems.
Args:
HFEngine: HF problem solver.
mu0(optional): Default parameter. Defaults to 0.
approxParameters(optional): Dictionary containing values for main
parameters of approximant. Recognized keys are:
- 'POD': whether to compute POD of snapshots; defaults to True;
- 'S': total number of samples current approximant relies upon;
defaults to 2;
- 'sampler': sample point generator; defaults to uniform sampler on
[0, 1];
- 'R': rank for Galerkin projection; defaults to S.
Defaults to empty dict.
+ homogeneized: Whether to homogeneize Dirichlet BCs. Defaults to False.
+ verbosity(optional): Verbosity level. Defaults to 10.
Attributes:
HFEngine: HF problem solver.
mu0: Default parameter.
mus: Array of snapshot parameters.
ws: Array of snapshot weigths (unused).
+ homogeneized: Whether to homogeneize Dirichlet BCs.
approxRadius: Dummy radius of approximant (i.e. distance from mu0 to
farthest sample point).
approxParameters: Dictionary containing values for main parameters of
approximant. Recognized keys are in parameterList.
parameterList: Recognized keys of approximant parameters:
- 'POD': whether to compute POD of snapshots;
- 'S': total number of samples current approximant relies upon;
- 'sampler': sample point generator;
- 'R': rank for Galerkin projection.
extraApproxParameters: List of approxParameters keys in addition to
mother class's.
S: Number of solution snapshots over which current approximant is
based upon.
sampler: Sample point generator.
R: Rank for Galerkin projection.
POD: Whether to compute POD of snapshots.
samplingEngine: Sampling engine.
projMat: Projection matrix for RB system assembly.
uHF: High fidelity solution with wavenumber lastSolvedHF as numpy
complex vector.
lastSolvedHF: Wavenumber corresponding to last computed high fidelity
solution.
uApp: Last evaluated approximant as numpy complex vector.
lastApproxParameters: List of parameters corresponding to last
computed approximant.
As: List of sparse matrices (in CSC format) representing coefficients
of linear system matrix wrt theta(mu).
bs: List of numpy vectors representing coefficients of linear system
RHS wrt theta(mu).
thetaAs: List of callables representing coefficients of linear system
matrix wrt mu.
thetabs: List of callables representing coefficients of linear system
RHS wrt mu.
ARBs: List of sparse matrices (in CSC format) representing coefficients
of compressed linear system matrix wrt theta(mu).
bRBs: List of numpy vectors representing coefficients of compressed
linear system RHS wrt theta(mu).
"""
def __init__(self, HFEngine:HFEng, mu0 : complex = 0.,
approxParameters : DictAny = {}, homogeneized : bool = False,
verbosity : int = 10):
self._preInit()
self._addParametersToList(["R"])
super().__init__(HFEngine = HFEngine, mu0 = mu0,
approxParameters = approxParameters,
homogeneized = homogeneized,
verbosity = verbosity)
if self.verbosity >= 10:
verbosityDepth("INIT", "Computing affine blocks of system.")
self.As, self.thetaAs = self.HFEngine.affineBlocksA(self.mu0)
self.bs, self.thetabs = self.HFEngine.affineBlocksb(self.mu0,
self.homogeneized)
if self.verbosity >= 10:
verbosityDepth("DEL", "Done computing affine blocks.")
self._postInit()
def resetSamples(self):
"""Reset samples."""
super().resetSamples()
self.projMat = None
@property
def approxParameters(self):
"""
Value of approximant parameters. Its assignment may change M, N and S.
"""
return self._approxParameters
@approxParameters.setter
def approxParameters(self, approxParams):
approxParameters = purgeDict(approxParams, self.parameterList,
dictname = self.name() + ".approxParameters",
baselevel = 1)
approxParametersCopy = purgeDict(approxParameters, ["R"], True, True,
baselevel = 1)
GenericApproximantLagrange.approxParameters.fset(self,
approxParametersCopy)
keyList = list(approxParameters.keys())
if "R" in keyList:
self.R = approxParameters["R"]
elif hasattr(self, "R"):
self.R = self.R
else:
self.R = self.S
@property
def R(self):
"""Value of R. Its assignment may change S."""
return self._R
@R.setter
def R(self, R):
if R < 0: raise ArithmeticError("R must be non-negative.")
self._R = R
self._approxParameters["R"] = self.R
if hasattr(self, "S") and self.S < self.R:
warn("Prescribed S is too small. Updating S to R.")
self.S = self.R
def checkComputedApprox(self) -> bool:
"""
Check if setup of new approximant is not needed.
Returns:
True if new setup is not needed. False otherwise.
"""
return (self.projMat is not None and super().checkComputedApprox())
def setupApprox(self):
"""Compute RB projection matrix."""
if not self.checkComputedApprox():
if self.verbosity >= 5:
verbosityDepth("INIT", "Setting up {}.". format(self.name()))
self.computeSnapshots()
if self.verbosity >= 7:
verbosityDepth("INIT", "Computing projection matrix.",
end = "")
if self.POD:
U, _, _ = np.linalg.svd(self.samplingEngine.RPOD,
full_matrices = False)
self.projMat = self.samplingEngine.samples.dot(U[:, : self.R])
else:
self.projMat = self.samplingEngine.samples[:, : self.R]
if self.verbosity >= 7:
verbosityDepth("DEL", " Done.", inline = True)
self.lastApproxParameters = copy(self.approxParameters)
if hasattr(self, "lastSolvedApp"): del self.lastSolvedApp
if self.verbosity >= 5:
verbosityDepth("DEL", "Done setting up approximant.\n")
def assembleReducedSystem(self):
"""Build affine blocks of RB linear system through projections."""
self.setupApprox()
if self.verbosity >= 10:
verbosityDepth("INIT", "Projecting affine terms of HF model.",
end = "")
projMatH = self.projMat.T.conj()
self.ARBs = [None] * len(self.As)
self.bRBs = [None] * len(self.bs)
if self.verbosity >= 10:
verbosityDepth("MAIN", ".", end = "", inline = True)
for j in range(len(self.As)):
self.ARBs[j] = projMatH.dot(self.As[j].dot(self.projMat))
if self.verbosity >= 10:
verbosityDepth("MAIN", ".", end = "", inline = True)
for j in range(len(self.bs)):
self.bRBs[j] = projMatH.dot(self.bs[j])
if self.verbosity >= 10:
verbosityDepth("DEL", "Done.", inline = True)
def solveReducedSystem(self, mu:complex) -> Np1D:
"""
Solve RB linear system.
Args:
mu: Target parameter.
Returns:
Solution of RB linear system.
"""
self.assembleReducedSystem()
if self.verbosity >= 10:
verbosityDepth("INIT",
"Assembling reduced model for mu = {}.".format(mu),
end = "")
ARBmu = self.thetaAs(mu, 0) * self.ARBs[0][:self.R,:self.R]
bRBmu = self.thetabs(mu, 0) * self.bRBs[0][:self.R]
if self.verbosity >= 10:
verbosityDepth("MAIN", ".", end = "", inline = True)
for j in range(1, len(self.ARBs)):
ARBmu += self.thetaAs(mu, j) * self.ARBs[j][:self.R, :self.R]
if self.verbosity >= 10:
verbosityDepth("MAIN", ".", end = "", inline = True)
for j in range(1, len(self.bRBs)):
bRBmu += self.thetabs(mu, j) * self.bRBs[j][:self.R]
if self.verbosity >= 10:
verbosityDepth("DEL", "Done.", inline = True)
if self.verbosity >= 5:
verbosityDepth("INIT",
"Solving reduced model for mu = {}.".format(mu),
end = "")
uRB = np.linalg.solve(ARBmu, bRBmu)
if self.verbosity >= 5:
verbosityDepth("DEL", " Done.", inline = True)
return uRB
def evalApproxReduced(self, mu:complex):
"""
Evaluate RB approximant at arbitrary wavenumber.
Args:
mu: Target parameter.
"""
self.assembleReducedSystem()
if (not hasattr(self, "lastSolvedApp")
or not np.isclose(self.lastSolvedApp, mu)):
if self.verbosity >= 5:
verbosityDepth("INIT",
"Computing RB solution at mu = {}.".format(mu))
self.uAppReduced = self.solveReducedSystem(mu)
self.lastSolvedApp = mu
if self.verbosity >= 5:
verbosityDepth("DEL", "Done computing RB solution.")
def evalApprox(self, mu:complex):
"""
Evaluate approximant at arbitrary parameter.
Args:
mu: Target parameter.
"""
self.evalApproxReduced(mu)
self.uApp = self.projMat[:, :self.R].dot(self.uAppReduced)
def getPoles(self) -> Np1D:
"""
Obtain approximant poles.
Returns:
Numpy complex vector of poles.
"""
warn(("Impossible to compute poles in general affine parameter "
"dependence. Results subject to interpretation/rescaling, or "
"possibly completely wrong."))
self.assembleReducedSystem()
if len(self.ARBs) < 2:
return
A = np.eye(self.ARBs[0].shape[0] * (len(self.ARBs) - 1),
dtype = np.complex)
B = np.zeros_like(A)
A[: self.ARBs[0].shape[0], : self.ARBs[0].shape[0]] = - self.ARBs[0]
for j in range(len(self.ARBs) - 1):
Aj = self.ARBs[j + 1]
B[: Aj.shape[0], j * Aj.shape[0] : (j + 1) * Aj.shape[0]] = Aj
II = np.arange(self.ARBs[0].shape[0],
self.ARBs[0].shape[0] * (len(self.ARBs) - 1))
B[II, II - self.ARBs[0].shape[0]] = 1.
return self.HFEngine.rescalingInv(sp.linalg.eigvals(A, B)
+ self.HFEngine.rescaling(self.mu0))
diff --git a/rrompy/reduction_methods/lagrange/generic_approximant_lagrange.py b/rrompy/reduction_methods/lagrange/generic_approximant_lagrange.py
index 96f7cca..7b783e0 100644
--- a/rrompy/reduction_methods/lagrange/generic_approximant_lagrange.py
+++ b/rrompy/reduction_methods/lagrange/generic_approximant_lagrange.py
@@ -1,206 +1,209 @@
# Copyright (C) 2018 by the RROMPy authors
#
# This file is part of RROMPy.
#
# RROMPy is free software: you can redistribute it and/or modify
# it under the terms of the GNU Lesser General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# RROMPy is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public License
# along with RROMPy. If not, see .
#
import numpy as np
from rrompy.reduction_methods.base.generic_approximant import (
GenericApproximant)
from rrompy.utilities.base.types import DictAny, HFEng
from rrompy.utilities.base import purgeDict, verbosityDepth
__all__ = ['GenericApproximantLagrange']
class GenericApproximantLagrange(GenericApproximant):
"""
ROM Lagrange interpolant computation for parametric problems (ABSTRACT).
Args:
HFEngine: HF problem solver.
mu0(optional): Default parameter. Defaults to 0.
approxParameters(optional): Dictionary containing values for main
parameters of approximant. Recognized keys are:
- 'POD': whether to compute POD of snapshots; defaults to True;
- 'S': total number of samples current approximant relies upon;
- 'sampler': sample point generator; defaults to uniform sampler on
[0, 1].
Defaults to empty dict.
+ homogeneized: Whether to homogeneize Dirichlet BCs. Defaults to False.
+ verbosity(optional): Verbosity level. Defaults to 10.
Attributes:
HFEngine: HF problem solver.
mu0: Default parameter.
mus: Array of snapshot parameters.
ws: Array of snapshot weigths.
+ homogeneized: Whether to homogeneize Dirichlet BCs.
approxParameters: Dictionary containing values for main parameters of
approximant. Recognized keys are in parameterList.
parameterList: Recognized keys of approximant parameters:
- 'POD': whether to compute POD of snapshots;
- 'S': total number of snapshots current approximant relies upon;
- 'sampler': sample point generator.
extraApproxParameters: List of approxParameters keys in addition to
mother class's.
S: Number of solution snapshots over which current approximant is
based upon.
sampler: Sample point generator.
POD: Whether to compute POD of snapshots.
samplingEngine: Sampling engine.
uHF: High fidelity solution with wavenumber lastSolvedHF as numpy
complex vector.
lastSolvedHF: Wavenumber corresponding to last computed high fidelity
solution.
uApp: Last evaluated approximant as numpy complex vector.
lastApproxParameters: List of parameters corresponding to last
computed approximant.
"""
def __init__(self, HFEngine:HFEng, mu0 : complex = 0.,
approxParameters : DictAny = {}, homogeneized : bool = False,
verbosity : int = 10):
self._preInit()
self._addParametersToList(["S", "sampler"])
super().__init__(HFEngine = HFEngine, mu0 = mu0,
approxParameters = approxParameters,
homogeneized = homogeneized,
verbosity = verbosity)
self._postInit()
def setupSampling(self):
"""Setup sampling engine."""
if not hasattr(self, "POD"): return
if self.POD:
from rrompy.sampling.scipy.sampling_engine_lagrange_pod import (
SamplingEngineLagrangePOD)
super().setupSampling(SamplingEngineLagrangePOD)
else:
from rrompy.sampling.scipy.sampling_engine_lagrange import (
SamplingEngineLagrange)
super().setupSampling(SamplingEngineLagrange)
@property
def mus(self):
"""Value of mus. Its assignment may reset snapshots."""
return self._mus
@mus.setter
def mus(self, mus):
musOld = self.mus if hasattr(self, 'mus') else None
self._mus = np.array(mus)
_, musCounts = np.unique(self._mus, return_counts = True)
if len(np.where(musCounts > 1)[0]) > 0:
raise Exception("Repeated sample points not allowed.")
if (musOld is None or len(self.mus) != len(musOld)
or not np.allclose(self.mus, musOld, 1e-14)):
self.resetSamples()
self.autoNode = None
@property
def approxParameters(self):
"""Value of approximant parameters. Its assignment may change S."""
return self._approxParameters
@approxParameters.setter
def approxParameters(self, approxParams):
approxParameters = purgeDict(approxParams, self.parameterList,
dictname = self.name() + ".approxParameters",
baselevel = 1)
approxParametersCopy = purgeDict(approxParameters, ["S", "sampler"],
True, True, baselevel = 1)
GenericApproximant.approxParameters.fset(self, approxParametersCopy)
keyList = list(approxParameters.keys())
if "S" in keyList:
self.S = approxParameters["S"]
elif hasattr(self, "S"):
self.S = self.S
else:
self.S = 2
if "sampler" in keyList:
self.sampler = approxParameters["sampler"]
elif not hasattr(self, "S"):
from rrompy.utilities.parameter_sampling import QuadratureSampler
self.sampler = QuadratureSampler([0., 1.], "UNIFORM")
del QuadratureSampler
@property
def S(self):
"""Value of S."""
return self._S
@S.setter
def S(self, S):
if S <= 0: raise ArithmeticError("S must be positive.")
if hasattr(self, "S"): Sold = self.S
else: Sold = -1
self._S = S
self._approxParameters["S"] = self.S
if Sold != self.S:
self.resetSamples()
@property
def sampler(self):
"""Value of sampler."""
return self._sampler
@sampler.setter
def sampler(self, sampler):
if 'generatePoints' not in dir(sampler):
raise Exception("Sampler type not recognized.")
if hasattr(self, '_sampler'):
samplerOld = self.sampler
self._sampler = sampler
self._approxParameters["sampler"] = self.sampler
if not 'samplerOld' in locals() or samplerOld != self.sampler:
self.resetSamples()
def computeSnapshots(self):
"""Compute snapshots of solution map."""
if self.samplingEngine.samples is None:
if self.verbosity >= 5:
verbosityDepth("INIT", "Starting computation of snapshots.")
self.mus, self.ws = self.sampler.generatePoints(self.S)
self.mus = np.array([x[0] for x in self.mus])
self.samplingEngine.iterSample(self.mus,
homogeneized = self.homogeneized)
if self.verbosity >= 5:
verbosityDepth("DEL", "Done computing snapshots.")
def checkComputedApprox(self) -> bool:
"""
Check if setup of new approximant is not needed.
Returns:
True if new setup is not needed. False otherwise.
"""
return (self.samplingEngine.samples is not None
and super().checkComputedApprox())
def normApp(self, mu:complex, homogeneized : bool = False) -> float:
"""
Compute norm of approximant at arbitrary parameter.
Args:
mu: Target parameter.
homogeneized(optional): Whether to remove Dirichlet BC. Defaults to
False.
Returns:
Target norm of approximant.
"""
if not self.POD or self.homogeneized != homogeneized:
return super().normApp(mu, homogeneized)
return np.linalg.norm(self.getAppReduced(mu))
- def computeRescaleParameter(self):
+ def computeScaleFactor(self):
"""Compute parameter rescaling factor."""
self.scaleFactor = .5 * np.abs(self.HFEngine.rescaling(
self.sampler.lims[0][0])
- self.HFEngine.rescaling(
self.sampler.lims[1][0]))
diff --git a/rrompy/reduction_methods/lagrange_greedy/__init__.py b/rrompy/reduction_methods/lagrange_greedy/__init__.py
index 516bfc9..f926ff6 100644
--- a/rrompy/reduction_methods/lagrange_greedy/__init__.py
+++ b/rrompy/reduction_methods/lagrange_greedy/__init__.py
@@ -1,33 +1,30 @@
# Copyright (C) 2018 by the RROMPy authors
#
# This file is part of RROMPy.
#
# RROMPy is free software: you can redistribute it and/or modify
# it under the terms of the GNU Lesser General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# RROMPy is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public License
# along with RROMPy. If not, see .
#
from .generic_approximant_lagrange_greedy import (
GenericApproximantLagrangeGreedy)
from .approximant_lagrange_greedy_pade import ApproximantLagrangePadeGreedy
-from .approximant_lagrange_greedy_pade_orthogonal import (
- ApproximantLagrangePadeOrthogonalGreedy)
from .approximant_lagrange_greedy_rb import ApproximantLagrangeRBGreedy
__all__ = [
'GenericApproximantLagrangeGreedy',
'ApproximantLagrangePadeGreedy',
- 'ApproximantLagrangePadeOrthogonalGreedy',
'ApproximantLagrangeRBGreedy'
]
diff --git a/rrompy/reduction_methods/lagrange_greedy/approximant_lagrange_greedy_pade.py b/rrompy/reduction_methods/lagrange_greedy/approximant_lagrange_greedy_pade.py
index 87f5546..0b11da6 100644
--- a/rrompy/reduction_methods/lagrange_greedy/approximant_lagrange_greedy_pade.py
+++ b/rrompy/reduction_methods/lagrange_greedy/approximant_lagrange_greedy_pade.py
@@ -1,363 +1,402 @@
# Copyright (C) 2018 by the RROMPy authors
#
# This file is part of RROMPy.
#
# RROMPy is free software: you can redistribute it and/or modify
# it under the terms of the GNU Lesser General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# RROMPy is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public License
# along with RROMPy. If not, see .
#
from copy import copy
import numpy as np
-from rrompy.reduction_methods.base import checkRobustTolerance
+from rrompy.reduction_methods.base import (checkRobustTolerance,
+ setupFitCallables)
from .generic_approximant_lagrange_greedy import (
GenericApproximantLagrangeGreedy)
from rrompy.reduction_methods.lagrange import ApproximantLagrangePade
from rrompy.utilities.base.types import DictAny, List, HFEng
-from rrompy.utilities.base import purgeDict, verbosityDepth
+from rrompy.utilities.base import purgeDict, verbosityDepth, customFit
from rrompy.utilities.warning_manager import warn
__all__ = ['ApproximantLagrangePadeGreedy']
class ApproximantLagrangePadeGreedy(GenericApproximantLagrangeGreedy,
ApproximantLagrangePade):
"""
- ROM greedy Lagrange Pade' interpolant computation for parametric problems.
+ ROM greedy Pade' interpolant computation for parametric problems.
Args:
HFEngine: HF problem solver.
mu0(optional): Default parameter. Defaults to 0.
approxParameters(optional): Dictionary containing values for main
parameters of approximant. Recognized keys are:
- 'POD': whether to compute POD of snapshots; defaults to True;
- 'muBounds': list of bounds for parameter values; defaults to
[[0], [1]];
+ - 'basis': type of basis for interpolation; allowed values include
+ 'MONOMIAL', 'CHEBYSHEV' and 'LEGENDRE'; defaults to 'MONOMIAL';
- 'Delta': difference between M and N in rational approximant;
defaults to 0;
- 'greedyTol': uniform error tolerance for greedy algorithm;
defaults to 1e-2;
- 'maxIter': maximum number of greedy steps; defaults to 1e2;
- 'refinementRatio': ratio of training points to be exhausted
before training set refinement; defaults to 0.2;
- 'nTrainingPoints': number of training points; defaults to
maxIter / refinementRatio;
- 'nTestPoints': number of starting test points; defaults to 1;
- 'trainingSetGenerator': training sample points generator;
defaults to uniform sampler within muBounds;
- 'interpRcond': tolerance for interpolation via numpy.polyfit;
defaults to None;
- 'robustTol': tolerance for robust Pade' denominator management;
defaults to 0.
Defaults to empty dict.
+ homogeneized: Whether to homogeneize Dirichlet BCs. Defaults to False.
+ verbosity(optional): Verbosity level. Defaults to 10.
Attributes:
HFEngine: HF problem solver.
mu0: Default parameter.
mus: Array of snapshot parameters.
+ homogeneized: Whether to homogeneize Dirichlet BCs.
approxParameters: Dictionary containing values for main parameters of
approximant. Recognized keys are in parameterList.
parameterList: Recognized keys of approximant parameters:
- 'POD': whether to compute POD of snapshots;
- 'muBounds': list of bounds for parameter values;
+ - 'basis': type of basis for interpolation;
- 'Delta': difference between M and N in rational approximant;
- 'greedyTol': uniform error tolerance for greedy algorithm;
- 'maxIter': maximum number of greedy steps;
- 'refinementRatio': ratio of training points to be exhausted
before training set refinement;
- 'nTrainingPoints': number of training points;
- 'nTestPoints': number of starting test points;
- 'trainingSetGenerator': training sample points generator;
- 'interpRcond': tolerance for interpolation via numpy.polyfit;
- 'robustTol': tolerance for robust Pade' denominator management.
extraApproxParameters: List of approxParameters keys in addition to
mother class's.
POD: whether to compute POD of snapshots.
muBounds: list of bounds for parameter values.
greedyTol: uniform error tolerance for greedy algorithm.
maxIter: maximum number of greedy steps.
refinementRatio: ratio of training points to be exhausted before
training set refinement.
nTrainingPoints: number of training points.
nTestPoints: number of starting test points.
trainingSetGenerator: training sample points generator.
interpRcond: Tolerance for interpolation via numpy.polyfit.
robustTol: Tolerance for robust Pade' denominator management.
samplingEngine: Sampling engine.
uHF: High fidelity solution with wavenumber lastSolvedHF as numpy
complex vector.
lastSolvedHF: Wavenumber corresponding to last computed high fidelity
solution.
Q: Numpy 1D vector containing complex coefficients of approximant
denominator.
P: Numpy 2D vector whose columns are FE dofs of coefficients of
approximant numerator.
uApp: Last evaluated approximant as numpy complex vector.
lastApproxParameters: List of parameters corresponding to last
computed approximant.
"""
def __init__(self, HFEngine:HFEng, mu0 : complex = 0.,
approxParameters : DictAny = {}, homogeneized : bool = False,
verbosity : int = 10):
self._preInit()
- self._addParametersToList(["Delta", "interpRcond", "robustTol"])
+ self._addParametersToList(["basis", "Delta", "interpRcond",
+ "robustTol"])
super().__init__(HFEngine = HFEngine, mu0 = mu0,
approxParameters = approxParameters,
homogeneized = homogeneized,
verbosity = verbosity)
self._postInit()
@property
def approxParameters(self):
"""
Value of approximant parameters. Its assignment may change robustTol.
"""
return self._approxParameters
@approxParameters.setter
def approxParameters(self, approxParams):
approxParameters = purgeDict(approxParams, self.parameterList,
dictname = self.name() + ".approxParameters",
baselevel = 1)
- approxParametersCopy = purgeDict(approxParameters, ["Delta",
+ approxParametersCopy = purgeDict(approxParameters, ["basis",
+ "Delta",
"interpRcond",
"robustTol"],
True, True, baselevel = 1)
if "Delta" in list(approxParameters.keys()):
self._Delta = approxParameters["Delta"]
elif hasattr(self, "Delta"):
self._Delta = self.Delta
else:
self._Delta = 0
GenericApproximantLagrangeGreedy.approxParameters.fset(self,
approxParametersCopy)
keyList = list(approxParameters.keys())
self.Delta = self.Delta
+ if "basis" in keyList or not hasattr(self, "_val"):
+ if "basis" in keyList:
+ kind = approxParameters["basis"]
+ else:
+ kind = "MONOMIAL"
+ setupFit = setupFitCallables(kind)
+ for x in setupFit:
+ super().__setattr__("_" + x, setupFit[x])
if "interpRcond" in keyList:
self.interpRcond = approxParameters["interpRcond"]
elif hasattr(self, "interpRcond"):
self.interpRcond = self.interpRcond
else:
self.interpRcond = None
if "robustTol" in keyList:
self.robustTol = approxParameters["robustTol"]
elif hasattr(self, "robustTol"):
self.robustTol = self.robustTol
else:
self.robustTol = 0
@property
def Delta(self):
"""Value of Delta."""
return self._Delta
@Delta.setter
def Delta(self, Delta):
if not np.isclose(Delta, np.floor(Delta)):
raise ArithmeticError("Delta must be an integer.")
if Delta < 0:
warn(("Error estimator unreliable for Delta < 0. Overloading of "
"errorEstimator is suggested."))
else:
Deltamin = (max(self.HFEngine.nbs,
self.HFEngine.nAs * self.homogeneized)
- 1 - 1 * (self.HFEngine.nAs > 1))
if Delta < Deltamin:
warn(("Method unreliable for selected Delta. Suggested "
"minimal value of Delta: {}.").format(Deltamin))
self._Delta = Delta
self._approxParameters["Delta"] = self.Delta
@property
def nTestPoints(self):
"""Value of nTestPoints."""
return self._nTestPoints
@nTestPoints.setter
def nTestPoints(self, nTestPoints):
if nTestPoints <= np.abs(self.Delta):
warn(("nTestPoints must be at least abs(Delta) + 1. Increasing "
"value to abs(Delta) + 1."))
nTestPoints = np.abs(self.Delta) + 1
if not np.isclose(nTestPoints, np.int(nTestPoints)):
raise ArithmeticError("nTestPoints must be an integer.")
nTestPoints = np.int(nTestPoints)
if hasattr(self, "nTestPoints"):
nTestPointsold = self.nTestPoints
else: nTestPointsold = -1
self._nTestPoints = nTestPoints
self._approxParameters["nTestPoints"] = self.nTestPoints
if nTestPointsold != self.nTestPoints:
self.resetSamples()
def errorEstimator(self, mus:List[np.complex]) -> List[np.complex]:
"""
Standard residual-based error estimator. Unreliable for unstable
problems.
"""
self.setupApprox()
self.initEstNormer()
nmus = len(mus)
- if self.Q.order + 1 == self.S:
- QSf = self.Q.c[0] * self.HFEngine.b(self.mu0, 1,
- homogeneized = self.homogeneized)
+ if self.N + 1 == self.S:
+ QSf = self._domcoeff(self.N) * self.Q[-1] * self.HFEngine.b(
+ self.mu0, 1, homogeneized = self.homogeneized)
else:
QSf = 0
- L1P = self.HFEngine.A(self.mu0, 1).dot(self.samplingEngine.samples.dot(
- self.P[:, 0]))
+ L1P = self._domcoeff(self.M) * self.HFEngine.A(self.mu0, 1).dot(
+ self.samplingEngine.samples.dot(self.P[:, -1]))
jOpt = self.estNormer.norm(QSf - L1P)
if np.isnan(jOpt) or np.isinf(jOpt):
err = np.empty(nmus)
err[:] = np.inf
return err
musTile = np.tile(self.HFEngine.rescaling(mus).reshape(-1, 1),
[1, len(self.mus)])
smusCol = self.HFEngine.rescaling(self.mus).reshape(1, -1)
mussmus = np.abs(musTile - smusCol)
num = np.prod(mussmus, axis = 1)
den = np.abs(self.getQVal(mus))
RHSnorms = np.empty(nmus)
if self.HFEngine.nbs == 1:
RHS = self.getRHS(mus[0], homogeneized = self.homogeneized)
RHSnorms[:] = self.estNormer.norm(RHS)
else:
for j in range(nmus):
RHS = self.getRHS(mus[j], homogeneized = self.homogeneized)
RHSnorms[j] = self.estNormer.norm(RHS)
return jOpt * num / den / RHSnorms / self.scaleFactor ** (self.S - 1)
def setupApprox(self):
"""
Compute Pade' interpolant.
SVD-based robust eigenvalue management.
"""
if not self.checkComputedApprox():
if self.verbosity >= 5:
verbosityDepth("INIT", "Setting up {}.". format(self.name()))
- self.computeRescaleParameter()
+ self.computeScaleFactor()
self.S = len(self.mus)
- M = self.S - 1
- N = self.S - 1
+ self._M = self.S - 1
+ self._N = self.S - 1
if self.Delta < 0:
- M += self.Delta
+ self._M += self.Delta
else:
- N -= self.Delta
- if min(M, N) < 0:
+ self._N -= self.Delta
+ if min(self.M, self.N) < 0:
if self.verbosity >= 5:
verbosityDepth("MAIN", "Minimal sample size not achieved.")
- self.Q = np.poly1d([1.])
- self.P = np.diag([np.nan] * len(self.mus))
+ self.Q = np.empty(max(self.N, 0) + 1, dtype = np.complex)
+ self.P = np.empty((len(self.mus), max(self.M, 0) + 1),
+ dtype = np.complex)
+ self.Q[:] = np.nan
+ self.P[:] = np.nan
self.lastApproxParameters = copy(self.approxParameters)
if self.verbosity >= 5:
verbosityDepth("DEL", ("Aborting computation of "
"approximant.\n"))
return
self.greedy()
- if N > 0:
+ if self.N > 0:
if self.verbosity >= 7:
verbosityDepth("INIT", ("Starting computation of "
"denominator."))
- while N > 0:
- TN = np.vander(self.radiusPade(self.mus), N = N + 1)
- if self.POD:
- data = self.samplingEngine.RPOD
- else:
- data = self.samplingEngine.samples
- RHSFull = np.empty((self.S, data.shape[0] * (N + 1)),
- dtype = np.complex)
- for j in range(self.S):
- RHSFull[j, :] = np.kron(data[:, j], TN[j, :])
- fitOut = np.polyfit(self.radiusPade(self.mus), RHSFull,
- self.S - 1, full = True,
- rcond = self.interpRcond)
+ TS = self._vander(self.radiusPade(self.mus), self.S - 1)
+ while self.N > 0:
+ RHS = np.zeros(self.S)
+ RHS[-1] = 1.
+ fitOut = customFit(TS.T, RHS, full = True,
+ rcond = self.interpRcond)
if self.verbosity >= 2:
verbosityDepth("MAIN", ("Fitting {} samples with "
"degree {} through {}... "
"Conditioning of system: "
- "{:.4e}.").format(
- self.S, self.S - 1, "polyfit",
- fitOut[3][0] / fitOut[3][-1]))
- if fitOut[2] < self.S:
+ "{:.4e}.").format(self.S,
+ self.S - 1, self._fitname,
+ fitOut[1][2][0] / fitOut[1][2][-1]))
+ if fitOut[1][1] < self.S:
warn(("Polyfit is poorly conditioned. Starting "
"preemptive termination of computation of "
"approximant."))
- self.Q = np.poly1d([1.])
- self.P = np.diag([np.nan] * len(self.mus))
+ self.Q = np.empty(max(self.N, 0) + 1,
+ dtype = np.complex)
+ self.P = np.empty((len(self.mus), max(self.M, 0) + 1),
+ dtype = np.complex)
+ self.Q[:] = np.nan
+ self.P[:] = np.nan
self.lastApproxParameters = copy(self.approxParameters)
if hasattr(self, "lastSolvedApp"):
del self.lastSolvedApp
if self.verbosity >= 7:
verbosityDepth("DEL", ("Aborting computation of "
"denominator."))
if self.verbosity >= 5:
verbosityDepth("DEL", ("Aborting computation of "
"approximant.\n"))
return
- G = fitOut[0][0, :].reshape((data.shape[0], N + 1))
+ G = (TS[:, : self.N + 1].T * fitOut[0]).T
if self.POD:
if self.verbosity >= 7:
verbosityDepth("INIT", ("Solving svd for square "
"root of gramian matrix."),
end = "")
- _, ev, eV = np.linalg.svd(G, full_matrices = False)
- ev = ev[::-1]
+ G = self.samplingEngine.RPOD.dot(G)
+ _, s, eV = np.linalg.svd(G, full_matrices = False)
+ ev = s[::-1]
eV = eV[::-1, :].conj().T
+ if self.verbosity >= 2:
+ try: condev = s[0] / s[-1]
+ except: condev = np.inf
+ verbosityDepth("MAIN", ("Solved svd problem of "
+ "size {} x {} with "
+ "condition number "
+ "{:.4e}.").format(
+ self.S, self.N + 1, condev))
else:
if self.verbosity >= 10:
verbosityDepth("INIT", "Building gramian matrix.",
end = "")
+ G = self.samplingEngine.samples.dot(G)
G2 = self.HFEngine.innerProduct(G, G)
if self.verbosity >= 10:
verbosityDepth("DEL", "Done building gramian.",
inline = True)
if self.verbosity >= 7:
verbosityDepth("INIT", ("Solving eigenvalue "
"problem for gramian "
"matrix."), end = "")
ev, eV = np.linalg.eigh(G2)
+ if self.verbosity >= 2:
+ try: condev = ev[-1] / ev[0]
+ except: condev = np.inf
+ verbosityDepth("MAIN", ("Solved eigenvalue "
+ "problem of size {} with "
+ "condition number "
+ "{:.4e}.").format(
+ self.N + 1, condev))
if self.verbosity >= 7:
verbosityDepth("DEL", " Done.", inline = True)
- newParameters = checkRobustTolerance(ev, M, self.robustTol)
+ newParameters = checkRobustTolerance(ev, self.M,
+ self.robustTol)
if not newParameters:
break
- N = newParameters["N"]
- M = newParameters["E"]
- if N <= 0:
+ self._N = newParameters["N"]
+ self._M = newParameters["E"]
+ if self.N <= 0:
+ self._N = 0
eV = np.ones((1, 1))
- self.Q = np.poly1d(eV[:, 0])
+ self.Q = eV[:, 0]
if self.verbosity >= 7:
verbosityDepth("DEL", "Done computing denominator.")
else:
- self.Q = np.poly1d([1])
+ self.Q = np.ones(1, dtype = np.complex)
if self.verbosity >= 7:
verbosityDepth("INIT", "Starting computation of numerator.")
self.lastApproxParameters = copy(self.approxParameters)
Qevaldiag = np.diag(self.getQVal(self.mus))
- while M >= 0:
- fitOut = np.polyfit(self.radiusPade(self.mus), Qevaldiag,
- M, full = True, rcond = self.interpRcond)
- if fitOut[2] == M + 1:
+ while self.M >= 0:
+ fitVander = self._vander(self.radiusPade(self.mus), self.M)
+ fitOut = customFit(fitVander, Qevaldiag, full = True,
+ rcond = self.interpRcond)
+ if fitOut[1][1] == self.M + 1:
P = fitOut[0].T
break
warn(("Polyfit is poorly conditioned. Reducing M from {} to "
"{}. Exact snapshot interpolation not guaranteed.")\
- .format(M, fitOut[2] - 1))
- M = fitOut[2] - 1
- if M <= 0:
+ .format(self.M, fitOut[1][1] - 1))
+ self._M = fitOut[1][1] - 1
+ if self.M <= 0:
raise Exception(("Instability in computation of numerator. "
"Aborting."))
self.P = np.atleast_2d(P)
if self.POD:
self.P = self.samplingEngine.RPOD.dot(self.P)
if self.verbosity >= 7:
verbosityDepth("DEL", "Done computing numerator.")
self.lastApproxParameters = copy(self.approxParameters)
if hasattr(self, "lastSolvedApp"): del self.lastSolvedApp
if self.verbosity >= 5:
verbosityDepth("DEL", "Done setting up approximant.\n")
+
diff --git a/rrompy/reduction_methods/lagrange_greedy/approximant_lagrange_greedy_pade_orthogonal.py b/rrompy/reduction_methods/lagrange_greedy/approximant_lagrange_greedy_pade_orthogonal.py
deleted file mode 100644
index 1e03db3..0000000
--- a/rrompy/reduction_methods/lagrange_greedy/approximant_lagrange_greedy_pade_orthogonal.py
+++ /dev/null
@@ -1,400 +0,0 @@
-# Copyright (C) 2018 by the RROMPy authors
-#
-# This file is part of RROMPy.
-#
-# RROMPy is free software: you can redistribute it and/or modify
-# it under the terms of the GNU Lesser General Public License as published by
-# the Free Software Foundation, either version 3 of the License, or
-# (at your option) any later version.
-#
-# RROMPy is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-# GNU Lesser General Public License for more details.
-#
-# You should have received a copy of the GNU Lesser General Public License
-# along with RROMPy. If not, see .
-#
-
-from copy import copy
-import numpy as np
-from rrompy.reduction_methods.base import checkRobustTolerance
-from .generic_approximant_lagrange_greedy import (
- GenericApproximantLagrangeGreedy)
-from rrompy.reduction_methods.lagrange import ApproximantLagrangePadeOrthogonal
-from rrompy.utilities.base.types import DictAny, List, HFEng
-from rrompy.utilities.base import purgeDict, verbosityDepth
-from rrompy.utilities.warning_manager import warn
-
-__all__ = ['ApproximantLagrangePadeOrthogonalGreedy']
-
-class ApproximantLagrangePadeOrthogonalGreedy(GenericApproximantLagrangeGreedy,
- ApproximantLagrangePadeOrthogonal):
- """
- ROM greedy Lagrange Pade' interpolant computation for parametric problems.
-
- Args:
- HFEngine: HF problem solver.
- mu0(optional): Default parameter. Defaults to 0.
- approxParameters(optional): Dictionary containing values for main
- parameters of approximant. Recognized keys are:
- - 'POD': whether to compute POD of snapshots; defaults to True;
- - 'muBounds': list of bounds for parameter values; defaults to
- [[0], [1]];
- - 'basis': type of basis for interpolation; allowed values include
- 'MONOMIAL', 'CHEBYSHEV' and 'LEGENDRE'; defaults to 'MONOMIAL';
- - 'Delta': difference between M and N in rational approximant;
- defaults to 0;
- - 'greedyTol': uniform error tolerance for greedy algorithm;
- defaults to 1e-2;
- - 'maxIter': maximum number of greedy steps; defaults to 1e2;
- - 'refinementRatio': ratio of training points to be exhausted
- before training set refinement; defaults to 0.2;
- - 'nTrainingPoints': number of training points; defaults to
- maxIter / refinementRatio;
- - 'nTestPoints': number of starting test points; defaults to 1;
- - 'trainingSetGenerator': training sample points generator;
- defaults to uniform sampler within muBounds;
- - 'interpRcond': tolerance for interpolation via numpy.polyfit;
- defaults to None;
- - 'robustTol': tolerance for robust Pade' denominator management;
- defaults to 0.
- Defaults to empty dict.
-
- Attributes:
- HFEngine: HF problem solver.
- mu0: Default parameter.
- mus: Array of snapshot parameters.
- approxParameters: Dictionary containing values for main parameters of
- approximant. Recognized keys are in parameterList.
- parameterList: Recognized keys of approximant parameters:
- - 'POD': whether to compute POD of snapshots;
- - 'muBounds': list of bounds for parameter values;
- - 'basis': type of basis for interpolation;
- - 'Delta': difference between M and N in rational approximant;
- - 'greedyTol': uniform error tolerance for greedy algorithm;
- - 'maxIter': maximum number of greedy steps;
- - 'refinementRatio': ratio of training points to be exhausted
- before training set refinement;
- - 'nTrainingPoints': number of training points;
- - 'nTestPoints': number of starting test points;
- - 'trainingSetGenerator': training sample points generator;
- - 'interpRcond': tolerance for interpolation via numpy.polyfit;
- - 'robustTol': tolerance for robust Pade' denominator management.
- extraApproxParameters: List of approxParameters keys in addition to
- mother class's.
- POD: whether to compute POD of snapshots.
- muBounds: list of bounds for parameter values.
- greedyTol: uniform error tolerance for greedy algorithm.
- maxIter: maximum number of greedy steps.
- refinementRatio: ratio of training points to be exhausted before
- training set refinement.
- nTrainingPoints: number of training points.
- nTestPoints: number of starting test points.
- trainingSetGenerator: training sample points generator.
- interpRcond: Tolerance for interpolation via numpy.polyfit.
- robustTol: Tolerance for robust Pade' denominator management.
- samplingEngine: Sampling engine.
- uHF: High fidelity solution with wavenumber lastSolvedHF as numpy
- complex vector.
- lastSolvedHF: Wavenumber corresponding to last computed high fidelity
- solution.
- Q: Numpy 1D vector containing complex coefficients of approximant
- denominator.
- P: Numpy 2D vector whose columns are FE dofs of coefficients of
- approximant numerator.
- uApp: Last evaluated approximant as numpy complex vector.
- lastApproxParameters: List of parameters corresponding to last
- computed approximant.
- """
-
- def __init__(self, HFEngine:HFEng, mu0 : complex = 0.,
- approxParameters : DictAny = {}, homogeneized : bool = False,
- verbosity : int = 10):
- self._preInit()
- self._addParametersToList(["basis", "Delta", "interpRcond",
- "robustTol"])
- super().__init__(HFEngine = HFEngine, mu0 = mu0,
- approxParameters = approxParameters,
- homogeneized = homogeneized,
- verbosity = verbosity)
- self._postInit()
-
- @property
- def approxParameters(self):
- """
- Value of approximant parameters. Its assignment may change robustTol.
- """
- return self._approxParameters
- @approxParameters.setter
- def approxParameters(self, approxParams):
- approxParameters = purgeDict(approxParams, self.parameterList,
- dictname = self.name() + ".approxParameters",
- baselevel = 1)
- approxParametersCopy = purgeDict(approxParameters, ["basis",
- "Delta",
- "interpRcond",
- "robustTol"],
- True, True, baselevel = 1)
- if "Delta" in list(approxParameters.keys()):
- self._Delta = approxParameters["Delta"]
- elif hasattr(self, "Delta"):
- self._Delta = self.Delta
- else:
- self._Delta = 0
- GenericApproximantLagrangeGreedy.approxParameters.fset(self,
- approxParametersCopy)
- keyList = list(approxParameters.keys())
- self.Delta = self.Delta
- if "basis" in keyList or not hasattr(self, "_val"):
- if ("basis" in keyList
- and approxParameters["basis"].upper() == "CHEBYSHEV"):
- self.basis = "CHEBYSHEV"
- self._val = np.polynomial.chebyshev.chebval
- self._vander = np.polynomial.chebyshev.chebvander
- self._fit = np.polynomial.chebyshev.chebfit
- self._roots = np.polynomial.chebyshev.chebroots
- self._domcoeff = lambda n: 2. ** (n - 1) if n > 0 else 1.
- elif ("basis" in keyList
- and approxParameters["basis"].upper() == "LEGENDRE"):
- self.basis = "LEGENDRE"
- self._val = np.polynomial.legendre.legval
- self._vander = np.polynomial.legendre.legvander
- self._fit = np.polynomial.legendre.legfit
- self._roots = np.polynomial.legendre.legroots
- from scipy.special import binom
- self._domcoeff = lambda n: (2. ** n * (np.pi * n) ** -.5
- if n > 10 else
- .5 ** n * binom(2 * n, n))
- else:
- self.basis = "UNIFORM"
- self._val = np.polynomial.polynomial.polyval
- self._vander = np.polynomial.polynomial.polyvander
- self._fit = np.polynomial.polynomial.polyfit
- self._roots = np.polynomial.polynomial.polyroots
- self._domcoeff = lambda n: 1.
- if "interpRcond" in keyList:
- self.interpRcond = approxParameters["interpRcond"]
- elif hasattr(self, "interpRcond"):
- self.interpRcond = self.interpRcond
- else:
- self.interpRcond = None
- if "robustTol" in keyList:
- self.robustTol = approxParameters["robustTol"]
- elif hasattr(self, "robustTol"):
- self.robustTol = self.robustTol
- else:
- self.robustTol = 0
-
- @property
- def Delta(self):
- """Value of Delta."""
- return self._Delta
- @Delta.setter
- def Delta(self, Delta):
- if not np.isclose(Delta, np.floor(Delta)):
- raise ArithmeticError("Delta must be an integer.")
- if Delta < 0:
- warn(("Error estimator unreliable for Delta < 0. Overloading of "
- "errorEstimator is suggested."))
- else:
- Deltamin = (max(self.HFEngine.nbs,
- self.HFEngine.nAs * self.homogeneized)
- - 1 - 1 * (self.HFEngine.nAs > 1))
- if Delta < Deltamin:
- warn(("Method unreliable for selected Delta. Suggested "
- "minimal value of Delta: {}.").format(Deltamin))
- self._Delta = Delta
- self._approxParameters["Delta"] = self.Delta
-
- @property
- def nTestPoints(self):
- """Value of nTestPoints."""
- return self._nTestPoints
- @nTestPoints.setter
- def nTestPoints(self, nTestPoints):
- if nTestPoints <= np.abs(self.Delta):
- warn(("nTestPoints must be at least abs(Delta) + 1. Increasing "
- "value to abs(Delta) + 1."))
- nTestPoints = np.abs(self.Delta) + 1
- if not np.isclose(nTestPoints, np.int(nTestPoints)):
- raise ArithmeticError("nTestPoints must be an integer.")
- nTestPoints = np.int(nTestPoints)
- if hasattr(self, "nTestPoints"):
- nTestPointsold = self.nTestPoints
- else: nTestPointsold = -1
- self._nTestPoints = nTestPoints
- self._approxParameters["nTestPoints"] = self.nTestPoints
- if nTestPointsold != self.nTestPoints:
- self.resetSamples()
-
- def resetSamples(self):
- """Reset samples."""
- super().resetSamples()
- self.qs = np.empty(0, dtype = np.complex)
-
- def errorEstimator(self, mus:List[np.complex]) -> List[np.complex]:
- """
- Standard residual-based error estimator. Unreliable for unstable
- problems.
- """
- self.setupApprox()
- self.initEstNormer()
- nmus = len(mus)
- if self.N + 1 == self.S:
- QSf = self._domcoeff(self.N) * self.Q[-1] * self.HFEngine.b(
- self.mu0, 1, homogeneized = self.homogeneized)
- else:
- QSf = 0
- L1P = self._domcoeff(self.M) * self.HFEngine.A(self.mu0, 1).dot(
- self.samplingEngine.samples.dot(self.P[:, -1]))
- jOpt = self.estNormer.norm(QSf - L1P)
- if np.isnan(jOpt) or np.isinf(jOpt):
- err = np.empty(nmus)
- err[:] = np.inf
- return err
- musTile = np.tile(self.HFEngine.rescaling(mus).reshape(-1, 1),
- [1, len(self.mus)])
- smusCol = self.HFEngine.rescaling(self.mus).reshape(1, -1)
- mussmus = np.abs(musTile - smusCol)
- num = np.prod(mussmus, axis = 1)
- den = np.abs(self.getQVal(mus))
- RHSnorms = np.empty(nmus)
- if self.HFEngine.nbs == 1:
- RHS = self.getRHS(mus[0], homogeneized = self.homogeneized)
- RHSnorms[:] = self.estNormer.norm(RHS)
- else:
- for j in range(nmus):
- RHS = self.getRHS(mus[j], homogeneized = self.homogeneized)
- RHSnorms[j] = self.estNormer.norm(RHS)
- return jOpt * num / den / RHSnorms / self.scaleFactor ** (self.S - 1)
-
- def setupApprox(self):
- """
- Compute Pade' interpolant.
- SVD-based robust eigenvalue management.
- """
- if not self.checkComputedApprox():
- if self.verbosity >= 5:
- verbosityDepth("INIT", "Setting up {}.". format(self.name()))
- self.computeRescaleParameter()
- self.S = len(self.mus)
- self._M = self.S - 1
- self._N = self.S - 1
- if self.Delta < 0:
- self._M += self.Delta
- else:
- self._N -= self.Delta
- if min(self.M, self.N) < 0:
- if self.verbosity >= 5:
- verbosityDepth("MAIN", "Minimal sample size not achieved.")
- self.Q = np.ones(1, dtype = np.complex)
- self.P = np.diag([np.nan] * len(self.mus))
- self.lastApproxParameters = copy(self.approxParameters)
- if self.verbosity >= 5:
- verbosityDepth("DEL", ("Aborting computation of "
- "approximant.\n"))
- return
- self.greedy()
- if self.N > 0:
- if self.verbosity >= 7:
- verbosityDepth("INIT", ("Starting computation of "
- "denominator."))
- TN = self._vander(self.radiusPade(self.mus), self.N)
- while self.N > 0:
- TN = TN[:, : self.N + 1]
- if self.POD:
- data = self.samplingEngine.RPOD
- else:
- data = self.samplingEngine.samples
- RHSFull = np.empty((self.S, data.shape[0] * (self.N + 1)),
- dtype = np.complex)
- for j in range(self.S):
- RHSFull[j, :] = np.kron(data[:, j], TN[j, :])
- fitOut = self._fit(self.radiusPade(self.mus), RHSFull,
- self.S - 1, full = True,
- rcond = self.interpRcond)
- if self.verbosity >= 2:
- verbosityDepth("MAIN", ("Fitting {} samples with "
- "degree {} through {}... "
- "Conditioning of system: "
- "{:.4e}.").format(self.S,
- self.S - 1, self._fit.__name__,
- fitOut[1][2][0] / fitOut[1][2][-1]))
- if fitOut[1][1] < self.S:
- warn(("Polyfit is poorly conditioned. Starting "
- "preemptive termination of computation of "
- "approximant."))
- self.Q = np.ones(1, dtype = np.complex)
- self.P = np.diag([np.nan] * len(self.mus))
- self.lastApproxParameters = copy(self.approxParameters)
- if hasattr(self, "lastSolvedApp"):
- del self.lastSolvedApp
- if self.verbosity >= 7:
- verbosityDepth("DEL", ("Aborting computation of "
- "denominator."))
- if self.verbosity >= 5:
- verbosityDepth("DEL", ("Aborting computation of "
- "approximant.\n"))
- return
- G = fitOut[0][-1, :].reshape((data.shape[0], self.N + 1))
- if self.POD:
- if self.verbosity >= 7:
- verbosityDepth("INIT", ("Solving svd for square "
- "root of gramian matrix."),
- end = "")
- _, ev, eV = np.linalg.svd(G, full_matrices = False)
- ev = ev[::-1]
- eV = eV[::-1, :].conj().T
- else:
- if self.verbosity >= 10:
- verbosityDepth("INIT", "Building gramian matrix.",
- end = "")
- G2 = self.HFEngine.innerProduct(G, G)
- if self.verbosity >= 10:
- verbosityDepth("DEL", "Done building gramian.",
- inline = True)
- if self.verbosity >= 7:
- verbosityDepth("INIT", ("Solving eigenvalue "
- "problem for gramian "
- "matrix."), end = "")
- ev, eV = np.linalg.eigh(G2)
- if self.verbosity >= 7:
- verbosityDepth("DEL", " Done.", inline = True)
- newParameters = checkRobustTolerance(ev, self.M,
- self.robustTol)
- if not newParameters:
- break
- self._N = newParameters["N"]
- self._M = newParameters["E"]
- if self.N <= 0:
- eV = np.ones((1, 1))
- self.Q = eV[:, 0]
- if self.verbosity >= 7:
- verbosityDepth("DEL", "Done computing denominator.")
- else:
- self.Q = np.ones(1, dtype = np.complex)
- if self.verbosity >= 7:
- verbosityDepth("INIT", "Starting computation of numerator.")
- self.lastApproxParameters = copy(self.approxParameters)
- Qevaldiag = np.diag(self.getQVal(self.mus))
- while self.M >= 0:
- fitOut = self._fit(self.radiusPade(self.mus), Qevaldiag,
- self.M, full = True,
- rcond = self.interpRcond)
- if fitOut[1][1] == self.M + 1:
- P = fitOut[0].T
- break
- warn(("Polyfit is poorly conditioned. Reducing M from {} to "
- "{}. Exact snapshot interpolation not guaranteed.")\
- .format(self.M, fitOut[1][1] - 1))
- self._M = fitOut[1][1] - 1
- self.P = np.atleast_2d(P)
- if self.POD:
- self.P = self.samplingEngine.RPOD.dot(self.P)
- if self.verbosity >= 7:
- verbosityDepth("DEL", "Done computing numerator.")
- self.lastApproxParameters = copy(self.approxParameters)
- if hasattr(self, "lastSolvedApp"): del self.lastSolvedApp
- if self.verbosity >= 5:
- verbosityDepth("DEL", "Done setting up approximant.\n")
diff --git a/rrompy/reduction_methods/lagrange_greedy/approximant_lagrange_greedy_rb.py b/rrompy/reduction_methods/lagrange_greedy/approximant_lagrange_greedy_rb.py
index 515d054..50c46d6 100644
--- a/rrompy/reduction_methods/lagrange_greedy/approximant_lagrange_greedy_rb.py
+++ b/rrompy/reduction_methods/lagrange_greedy/approximant_lagrange_greedy_rb.py
@@ -1,300 +1,303 @@
# Copyright (C) 2018 by the RROMPy authors
#
# This file is part of RROMPy.
#
# RROMPy is free software: you can redistribute it and/or modify
# it under the terms of the GNU Lesser General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# RROMPy is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public License
# along with RROMPy. If not, see .
#
import numpy as np
from copy import copy
from .generic_approximant_lagrange_greedy import (
GenericApproximantLagrangeGreedy)
from rrompy.reduction_methods.lagrange import ApproximantLagrangeRB
from rrompy.utilities.base.types import DictAny, HFEng, List
from rrompy.utilities.base import verbosityDepth
from rrompy.utilities.warning_manager import warn
__all__ = ['ApproximantLagrangeRBGreedy']
class ApproximantLagrangeRBGreedy(GenericApproximantLagrangeGreedy,
ApproximantLagrangeRB):
"""
ROM greedy RB approximant computation for parametric problems.
Args:
HFEngine: HF problem solver.
mu0(optional): Default parameter. Defaults to 0.
approxParameters(optional): Dictionary containing values for main
parameters of approximant. Recognized keys are:
- 'POD': whether to compute POD of snapshots; defaults to True;
- 'muBounds': list of bounds for parameter values; defaults to
[[0], [1]];
- 'greedyTol': uniform error tolerance for greedy algorithm;
defaults to 1e-2;
- 'maxIter': maximum number of greedy steps; defaults to 1e2;
- 'refinementRatio': ratio of training points to be exhausted
before training set refinement; defaults to 0.2;
- 'nTrainingPoints': number of training points; defaults to
maxIter / refinementRatio;
- 'nTestPoints': number of starting test points; defaults to 1;
- 'trainingSetGenerator': training sample points generator;
defaults to uniform sampler within muBounds.
Defaults to empty dict.
+ homogeneized: Whether to homogeneize Dirichlet BCs. Defaults to False.
+ verbosity(optional): Verbosity level. Defaults to 10.
Attributes:
HFEngine: HF problem solver.
mu0: Default parameter.
mus: Array of snapshot parameters.
+ homogeneized: Whether to homogeneize Dirichlet BCs.
approxParameters: Dictionary containing values for main parameters of
approximant. Recognized keys are in parameterList.
parameterList: Recognized keys of approximant parameters:
- 'POD': whether to compute POD of snapshots;
- 'muBounds': list of bounds for parameter values;
- 'greedyTol': uniform error tolerance for greedy algorithm;
- 'maxIter': maximum number of greedy steps;
- 'refinementRatio': ratio of training points to be exhausted
before training set refinement;
- 'nTrainingPoints': number of training points;
- 'nTestPoints': number of starting test points;
- 'trainingSetGenerator': training sample points generator;
- 'robustTol': tolerance for robust Pade' denominator management.
extraApproxParameters: List of approxParameters keys in addition to
mother class's.
POD: whether to compute POD of snapshots.
muBounds: list of bounds for parameter values.
greedyTol: uniform error tolerance for greedy algorithm.
maxIter: maximum number of greedy steps.
refinementRatio: ratio of training points to be exhausted before
training set refinement.
nTrainingPoints: number of training points.
nTestPoints: number of starting test points.
trainingSetGenerator: training sample points generator.
samplingEngine: Sampling engine.
projMat: Projection matrix for RB system assembly.
uHF: High fidelity solution with wavenumber lastSolvedHF as numpy
complex vector.
lastSolvedHF: Wavenumber corresponding to last computed high fidelity
solution.
uApp: Last evaluated approximant as numpy complex vector.
lastApproxParameters: List of parameters corresponding to last
computed approximant.
As: List of sparse matrices (in CSC format) representing coefficients
of linear system matrix wrt theta(mu).
bs: List of numpy vectors representing coefficients of linear system
RHS wrt theta(mu).
thetaAs: List of callables representing coefficients of linear system
matrix wrt mu.
thetabs: List of callables representing coefficients of linear system
RHS wrt mu.
ARBs: List of sparse matrices (in CSC format) representing coefficients
of compressed linear system matrix wrt theta(mu).
bRBs: List of numpy vectors representing coefficients of compressed
linear system RHS wrt theta(mu).
"""
def __init__(self, HFEngine:HFEng, mu0 : complex = 0.,
approxParameters : DictAny = {}, homogeneized : bool = False,
verbosity : int = 10):
self._preInit()
super().__init__(HFEngine = HFEngine, mu0 = mu0,
approxParameters = approxParameters,
homogeneized = homogeneized,
verbosity = verbosity)
if self.verbosity >= 10:
verbosityDepth("INIT", "Computing affine blocks of system.")
self.As, self.thetaAs = self.HFEngine.affineBlocksA(self.mu0)
self.bs, self.thetabs = self.HFEngine.affineBlocksb(self.mu0,
self.homogeneized)
if self.verbosity >= 10:
verbosityDepth("DEL", "Done computing affine blocks.")
self._postInit()
@property
def S(self):
"""Value of S."""
return self._S
@S.setter
def S(self, S):
self._S = S
@property
def R(self):
"""Value of R."""
return self._S
@R.setter
def R(self, R):
warn(("R is used just to simplify inheritance, and its value cannot "
"be changed from that of S."))
def resetSamples(self):
"""Reset samples."""
super().resetSamples()
self.projMat = None
self.resbb = None
self.resAb = None
self.resAA = None
def checkComputedApprox(self) -> bool:
"""
Check if setup of new approximant is not needed.
Returns:
True if new setup is not needed. False otherwise.
"""
return (self.projMat is not None and super().checkComputedApprox())
def errorEstimator(self, mus:List[np.complex]) -> List[np.complex]:
"""
Standard residual-based error estimator. Unreliable for unstable
problems (inf-sup constant is missing).
"""
nmus = len(mus)
err = np.empty(nmus)
self.assembleReducedSystem()
self.assembleReducedResidualBlocks()
nAs = len(self.As)
nbs = len(self.bs)
for j in range(nmus):
mu = mus[j]
uAppReduced = self.getAppReduced(mu)
prodbb = 0.
prodAb = 0.
prodAA = 0.
for i1 in range(nbs):
rhobi1 = self.thetabs(mu, i1)
for i2 in range(nbs):
rhobi2 = self.thetabs(mu, i2).conj()
prodbb += rhobi1 * rhobi2 * self.resbb[i1][i2]
for i1 in range(nAs):
rhoAi1 = self.thetaAs(mu, i1)
for i2 in range(nbs):
rhobi2 = self.thetabs(mu, i2).conj()
prodAb += rhoAi1 * rhobi2 * self.resAb[i1][i2]
for i1 in range(nAs):
rhoAi1 = self.thetaAs(mu, i1)
for i2 in range(nAs):
rhoAi2 = self.thetaAs(mu, i2).conj()
prodAA += rhoAi1 * rhoAi2 * self.resAA[i1][i2]
err[j] = np.abs(((uAppReduced.T.conj().dot(prodAA.dot(uAppReduced))
- 2. * np.real(prodAb.dot(uAppReduced))) / prodbb
+ 1.)[0]) ** .5
return err
def setupApprox(self):
"""Compute RB projection matrix."""
if not self.checkComputedApprox():
if self.verbosity >= 5:
verbosityDepth("INIT", "Setting up {}.". format(self.name()))
self.greedy()
self.S = len(self.mus)
if self.verbosity >= 7:
verbosityDepth("INIT", "Computing projection matrix.",
end = "")
self.projMat = self.samplingEngine.samples
if self.verbosity >= 7:
verbosityDepth("DEL", " Done.", inline = True)
self.lastApproxParameters = copy(self.approxParameters)
if hasattr(self, "lastSolvedApp"): del self.lastSolvedApp
if self.verbosity >= 5:
verbosityDepth("DEL", "Done setting up approximant.\n")
def assembleReducedResidualBlocks(self):
"""Build affine blocks of RB linear system through projections."""
self.initEstNormer()
self.assembleReducedSystem()
computeResbb = self.resbb is None
computeResAb = (self.resAb is None
or self.resAb[0][0].shape[0] != self.S)
computeResAA = (self.resAA is None
or self.resAA[0][0].shape[0] != self.S)
if computeResbb or computeResAb or computeResAA:
if self.verbosity >= 7:
verbosityDepth("INIT", "Projecting affine terms of residual.",
end = "")
nAs = len(self.As)
nbs = len(self.bs)
if computeResbb:
self.resbb = []
for i in range(nbs):
resbbi = []
Mbi = self.bs[i]
for j in range(i):
Mbj = self.bs[j]
resbbi += [self.estNormer.innerProduct(Mbi, Mbj)]
resbbi += [self.estNormer.innerProduct(Mbi, Mbi)]
self.resbb += [resbbi]
for i in range(nbs):
for j in range(i + 1, nbs):
self.resbb[i] += [self.resbb[j][i].conj()]
if computeResAb:
if self.resAb is None:
self.resAb = []
for i in range(nAs):
resAbi = []
MAi = self.As[i].dot(self.projMat)
for j in range(nbs):
Mbj = self.bs[j]
resAbi += [self.estNormer.innerProduct(MAi, Mbj)]
self.resAb += [resAbi]
else:
Sold = self.resAb[0][0].shape[0]
for i in range(nAs):
MAi = self.As[i].dot(self.projMat[:, Sold :])
for j in range(nbs):
Mbj = self.bs[j]
self.resAb[i][j] = np.append(self.resAb[i][j],
self.estNormer.innerProduct(MAi, Mbj))
if computeResAA:
if self.resAA is None:
self.resAA = []
for i in range(nAs):
resAAi = []
MAi = self.As[i].dot(self.projMat)
for j in range(i):
MAj = self.As[j].dot(self.projMat)
resAAi += [self.estNormer.innerProduct(MAi, MAj)]
resAAi += [self.estNormer.innerProduct(MAi, MAi)]
self.resAA += [resAAi]
for i in range(nAs):
for j in range(i + 1, nAs):
self.resAA[i] += [self.resAA[j][i].conj()]
else:
Sold = self.resAA[0][0].shape[0]
for i in range(nAs):
MAi = self.As[i].dot(self.projMat)
for j in range(i):
MAj = self.As[j].dot(self.projMat)
resAAcross1 = self.estNormer.innerProduct(
MAi[:, Sold :], MAj[:, : Sold])
resAAcross2 = self.estNormer.innerProduct(
MAi[:, : Sold], MAj[:, Sold :])
resAAdiag = self.estNormer.innerProduct(
MAi[:, Sold :], MAj[:, Sold :])
self.resAA[i][j] = np.block(
[[self.resAA[i][j], resAAcross1],
[ resAAcross2, resAAdiag]])
resAAcross = self.estNormer.innerProduct(
MAi[:, Sold :],
MAi[:, : Sold])
resAAdiag = self.estNormer.innerProduct(MAi[:, Sold :],
MAi[:, Sold :])
self.resAA[i][i] = np.block(
[[ self.resAA[i][i], resAAcross],
[resAAcross.T.conj(), resAAdiag]])
for i in range(nAs):
for j in range(i + 1, nAs):
self.resAA[i][j] = self.resAA[j][i].conj()
if self.verbosity >= 7:
verbosityDepth("DEL", ("Done setting up affine "
"decomposition of residual."))
diff --git a/rrompy/reduction_methods/lagrange_greedy/generic_approximant_lagrange_greedy.py b/rrompy/reduction_methods/lagrange_greedy/generic_approximant_lagrange_greedy.py
index e0bb2cd..9ed10f3 100644
--- a/rrompy/reduction_methods/lagrange_greedy/generic_approximant_lagrange_greedy.py
+++ b/rrompy/reduction_methods/lagrange_greedy/generic_approximant_lagrange_greedy.py
@@ -1,485 +1,488 @@
# Copyright (C) 2018 by the RROMPy authors
#
# This file is part of RROMPy.
#
# RROMPy is free software: you can redistribute it and/or modify
# it under the terms of the GNU Lesser General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# RROMPy is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public License
# along with RROMPy. If not, see .
#
import numpy as np
from rrompy.reduction_methods.base.generic_approximant import (
GenericApproximant)
from rrompy.reduction_methods.lagrange.generic_approximant_lagrange import (
GenericApproximantLagrange)
from rrompy.utilities.base.types import DictAny, HFEng, Tuple, List
from rrompy.utilities.base import purgeDict, verbosityDepth
from rrompy.utilities.warning_manager import warn
__all__ = ['GenericApproximantLagrangeGreedy']
class GenericApproximantLagrangeGreedy(GenericApproximantLagrange):
"""
ROM greedy Lagrange interpolant computation for parametric problems
(ABSTRACT).
Args:
HFEngine: HF problem solver.
mu0(optional): Default parameter. Defaults to 0.
approxParameters(optional): Dictionary containing values for main
parameters of approximant. Recognized keys are:
- 'POD': whether to compute POD of snapshots; defaults to True;
- 'muBounds': list of bounds for parameter values; defaults to
[[0], [1]];
- 'greedyTol': uniform error tolerance for greedy algorithm;
defaults to 1e-2;
- 'maxIter': maximum number of greedy steps; defaults to 1e2;
- 'refinementRatio': ratio of training points to be exhausted
before training set refinement; defaults to 0.2;
- 'nTrainingPoints': number of training points; defaults to
maxIter / refinementRatio;
- 'nTestPoints': number of starting test points; defaults to 1;
- 'trainingSetGenerator': training sample points generator;
defaults to uniform sampler within muBounds;
- 'robustTol': tolerance for robust Pade' denominator management;
defaults to 0.
Defaults to empty dict.
+ homogeneized: Whether to homogeneize Dirichlet BCs. Defaults to False.
+ verbosity(optional): Verbosity level. Defaults to 10.
Attributes:
HFEngine: HF problem solver.
mu0: Default parameter.
mus: Array of snapshot parameters.
+ homogeneized: Whether to homogeneize Dirichlet BCs.
approxParameters: Dictionary containing values for main parameters of
approximant. Recognized keys are in parameterList.
parameterList: Recognized keys of approximant parameters:
- 'POD': whether to compute POD of snapshots;
- 'muBounds': list of bounds for parameter values;
- 'greedyTol': uniform error tolerance for greedy algorithm;
- 'maxIter': maximum number of greedy steps;
- 'refinementRatio': ratio of training points to be exhausted
before training set refinement;
- 'nTrainingPoints': number of training points;
- 'nTestPoints': number of starting test points;
- 'trainingSetGenerator': training sample points generator.
- 'robustTol': tolerance for robust Pade' denominator management.
extraApproxParameters: List of approxParameters keys in addition to
mother class's.
POD: whether to compute POD of snapshots.
muBounds: list of bounds for parameter values.
greedyTol: uniform error tolerance for greedy algorithm.
maxIter: maximum number of greedy steps.
refinementRatio: ratio of training points to be exhausted before
training set refinement.
nTrainingPoints: number of training points.
nTestPoints: number of starting test points.
trainingSetGenerator: training sample points generator.
robustTol: tolerance for robust Pade' denominator management.
samplingEngine: Sampling engine.
uHF: High fidelity solution with wavenumber lastSolvedHF as numpy
complex vector.
lastSolvedHF: Wavenumber corresponding to last computed high fidelity
solution.
uApp: Last evaluated approximant as numpy complex vector.
lastApproxParameters: List of parameters corresponding to last
computed approximant.
"""
TOL_INSTABILITY = 1e-6
def __init__(self, HFEngine:HFEng, mu0 : complex = 0.,
approxParameters : DictAny = {}, homogeneized : bool = False,
verbosity : int = 10):
self._preInit()
self._addParametersToList(["muBounds","greedyTol", "maxIter",
"refinementRatio", "nTrainingPoints",
"nTestPoints", "trainingSetGenerator"])
super(GenericApproximantLagrange, self).__init__(
HFEngine = HFEngine, mu0 = mu0,
approxParameters = approxParameters,
homogeneized = homogeneized,
verbosity = verbosity)
self._postInit()
@property
def approxParameters(self):
"""Value of approximant parameters. Its assignment may change S."""
return self._approxParameters
@approxParameters.setter
def approxParameters(self, approxParams):
approxParameters = purgeDict(approxParams, self.parameterList,
dictname = self.name() + ".approxParameters",
baselevel = 1)
approxParametersCopy = purgeDict(approxParameters,
["muBounds","greedyTol", "maxIter",
"refinementRatio", "nTrainingPoints",
"nTestPoints",
"trainingSetGenerator"],
True, True, baselevel = 1)
GenericApproximant.approxParameters.fset(self, approxParametersCopy)
keyList = list(approxParameters.keys())
if "muBounds" in keyList:
self.muBounds = approxParameters["muBounds"]
elif hasattr(self, "muBounds"):
self.muBounds = self.muBounds
else:
self.muBounds = [[0.], [1.]]
if "greedyTol" in keyList:
self.greedyTol = approxParameters["greedyTol"]
elif hasattr(self, "greedyTol"):
self.greedyTol = self.greedyTol
else:
self.greedyTol = 1e-2
if "maxIter" in keyList:
self.maxIter = approxParameters["maxIter"]
elif hasattr(self, "maxIter"):
self.maxIter = self.maxIter
else:
self.maxIter = 1e2
if "refinementRatio" in keyList:
self.refinementRatio = approxParameters["refinementRatio"]
elif hasattr(self, "refinementRatio"):
self.refinementRatio = self.refinementRatio
else:
self.refinementRatio = 0.2
if "nTrainingPoints" in keyList:
self.nTrainingPoints = approxParameters["nTrainingPoints"]
elif hasattr(self, "nTrainingPoints"):
self.nTrainingPoints = self.nTrainingPoints
else:
self.nTrainingPoints = np.int(np.ceil(self.maxIter
/ self.refinementRatio))
if "nTestPoints" in keyList:
self.nTestPoints = approxParameters["nTestPoints"]
elif hasattr(self, "nTestPoints"):
self.nTestPoints = self.nTestPoints
else:
self.nTestPoints = 1
if "trainingSetGenerator" in keyList:
self.trainingSetGenerator = (
approxParameters["trainingSetGenerator"])
elif hasattr(self, "trainingSetGenerator"):
self.trainingSetGenerator = self.trainingSetGenerator
else:
from rrompy.utilities.parameter_sampling import QuadratureSampler
self.trainingSetGenerator = QuadratureSampler(self.muBounds,
"UNIFORM")
del QuadratureSampler
@property
def S(self):
"""Value of S."""
return self._S
@S.setter
def S(self, S):
self._S = S
@property
def mus(self):
"""Value of mus."""
return self._mus
@mus.setter
def mus(self, mus):
self._mus = np.array(mus, dtype = np.complex)
@property
def muBounds(self):
"""Value of muBounds."""
return self._muBounds
@muBounds.setter
def muBounds(self, muBounds):
if len(muBounds) != 2:
raise Exception("2 limits must be specified.")
try:
muBounds = muBounds.tolist()
except:
muBounds = list(muBounds)
for j in range(2):
try:
len(muBounds[j])
except:
muBounds[j] = np.array([muBounds[j]])
if len(muBounds[0]) != len(muBounds[1]):
raise Exception("The bounds must have the same length.")
self._muBounds = muBounds
@property
def greedyTol(self):
"""Value of greedyTol."""
return self._greedyTol
@greedyTol.setter
def greedyTol(self, greedyTol):
if greedyTol < 0:
raise ArithmeticError("greedyTol must be non-negative.")
if hasattr(self, "greedyTol"): greedyTolold = self.greedyTol
else: greedyTolold = -1
self._greedyTol = greedyTol
self._approxParameters["greedyTol"] = self.greedyTol
if greedyTolold != self.greedyTol:
self.resetSamples()
@property
def maxIter(self):
"""Value of maxIter."""
return self._maxIter
@maxIter.setter
def maxIter(self, maxIter):
if maxIter <= 0: raise ArithmeticError("maxIter must be positive.")
if hasattr(self, "maxIter"): maxIterold = self.maxIter
else: maxIterold = -1
self._maxIter = maxIter
self._approxParameters["maxIter"] = self.maxIter
if maxIterold != self.maxIter:
self.resetSamples()
@property
def refinementRatio(self):
"""Value of refinementRatio."""
return self._refinementRatio
@refinementRatio.setter
def refinementRatio(self, refinementRatio):
if refinementRatio <= 0. or refinementRatio > 1.:
raise ArithmeticError(("refinementRatio must be between 0 "
"(excluded) and 1."))
if hasattr(self, "refinementRatio"):
refinementRatioold = self.refinementRatio
else: refinementRatioold = -1
self._refinementRatio = refinementRatio
self._approxParameters["refinementRatio"] = self.refinementRatio
if refinementRatioold != self.refinementRatio:
self.resetSamples()
@property
def nTrainingPoints(self):
"""Value of nTrainingPoints."""
return self._nTrainingPoints
@nTrainingPoints.setter
def nTrainingPoints(self, nTrainingPoints):
if nTrainingPoints <= 1:
raise ArithmeticError("nTrainingPoints must be greater than 1.")
if not np.isclose(nTrainingPoints, np.int(nTrainingPoints)):
raise ArithmeticError("nTrainingPoints must be an integer.")
nTrainingPoints = np.int(nTrainingPoints)
if hasattr(self, "nTrainingPoints"):
nTrainingPointsold = self.nTrainingPoints
else: nTrainingPointsold = -1
self._nTrainingPoints = nTrainingPoints
self._approxParameters["nTrainingPoints"] = self.nTrainingPoints
if nTrainingPointsold != self.nTrainingPoints:
self.resetSamples()
@property
def nTestPoints(self):
"""Value of nTestPoints."""
return self._nTestPoints
@nTestPoints.setter
def nTestPoints(self, nTestPoints):
if nTestPoints <= 0:
raise ArithmeticError("nTestPoints must be positive.")
if not np.isclose(nTestPoints, np.int(nTestPoints)):
raise ArithmeticError("nTestPoints must be an integer.")
nTestPoints = np.int(nTestPoints)
if hasattr(self, "nTestPoints"):
nTestPointsold = self.nTestPoints
else: nTestPointsold = -1
self._nTestPoints = nTestPoints
self._approxParameters["nTestPoints"] = self.nTestPoints
if nTestPointsold != self.nTestPoints:
self.resetSamples()
@property
def trainingSetGenerator(self):
"""Value of trainingSetGenerator."""
return self._trainingSetGenerator
@trainingSetGenerator.setter
def trainingSetGenerator(self, trainingSetGenerator):
if 'generatePoints' not in dir(trainingSetGenerator):
raise Exception("trainingSetGenerator type not recognized.")
if hasattr(self, '_trainingSetGenerator'):
trainingSetGeneratorOld = self.trainingSetGenerator
self._trainingSetGenerator = trainingSetGenerator
self._approxParameters["trainingSetGenerator"] = (
self.trainingSetGenerator)
if (not 'trainingSetGeneratorOld' in locals()
or trainingSetGeneratorOld != self.trainingSetGenerator):
self.resetSamples()
def resetSamples(self):
"""Reset samples."""
super().resetSamples()
self._mus = []
def initEstNormer(self):
"""Initialize estimator norm engine."""
if not hasattr(self, "estNormer"):
from rrompy.hfengines.base import ProblemEngineBase as PEB
# class L2InverseNormer(PEB):
# def innerProduct(self, u:Np1D, v:Np1D) -> float:
# if not hasattr(self, "energyNormMatrix"):
# self.buildEnergyNormForm()
# return v.conj().T.dot(self.energyNormMatrix.solve(u))
# def buildEnergyNormForm(self):
# super().buildEnergyNormForm()
# from scipy.sparse import csc_matrix, linalg as sla
# Mass = csc_matrix(self.energyNormMatrix,
# dtype = np.complex)
# self.energyNormMatrix = sla.spilu(Mass)
# self.estNormer = L2InverseNormer() # inverse L2 norm
self.estNormer = PEB() # L2 norm
self.estNormer.V = self.HFEngine.V
self.estNormer.buildEnergyNormForm()
def errorEstimator(self, mus:List[np.complex]) -> List[np.complex]:
"""
Standard residual-based error estimator with explicit residual
computation.
"""
self.setupApprox()
nmus = len(mus)
err = np.empty(nmus)
if self.HFEngine.nbs == 1:
RHS = self.getRHS(mus[0], homogeneized = self.homogeneized)
RHSNorm = self.estNormer.norm(RHS)
for j in range(nmus):
res = self.getRes(mus[j], homogeneized = self.homogeneized)
err[j] = self.estNormer.norm(res) / RHSNorm
else:
for j in range(nmus):
res = self.getRes(mus[j], homogeneized = self.homogeneized)
RHS = self.getRHS(mus[j], homogeneized = self.homogeneized)
err[j] = self.estNormer.norm(res) / self.estNormer.norm(RHS)
return np.abs(err)
def getMaxErrorEstimator(self) -> Tuple[float, int]:
"""
Compute maximum of (and index of maximum of) error estimator over
training set.
"""
errorEstTrain = self.errorEstimator(self.muTrain)
idxMaxEst = np.argmax(errorEstTrain)
maxEst = errorEstTrain[idxMaxEst]
return maxEst, idxMaxEst
def greedyNextSample(self, muidx:int, plotEst : bool = False):
"""Compute next greedy snapshot of solution map."""
mu = self.muTrain[muidx]
if self.verbosity >= 2:
verbosityDepth("MAIN", ("Adding {}-th sample point at {} to test "
"set.").format(
self.samplingEngine.nsamples + 1, mu))
self.mus = np.append(self.mus, mu)
idxs = np.arange(len(self.muTrain))
mask = np.ones_like(idxs, dtype = bool)
mask[muidx] = False
idxs = idxs[mask]
self.muTrain = self.muTrain[idxs]
self.samplingEngine.nextSample(mu,
homogeneized = self.homogeneized)
errorEstTrain = self.errorEstimator(self.muTrain)
muidx = np.argmax(errorEstTrain)
maxErrorEst = errorEstTrain[muidx]
mu = self.muTrain[muidx]
if plotEst and not np.all(np.isinf(errorEstTrain)):
from matplotlib import pyplot as plt
plt.figure()
plt.semilogy(np.real(self.muTrain), errorEstTrain, 'k')
plt.semilogy(np.real(self.muTrain),
self.greedyTol * np.ones(len(self.muTrain)), 'r--')
plt.semilogy(np.real(self.mus),
2. * self.greedyTol * np.ones(len(self.mus)), '*m')
plt.semilogy(np.real(mu), maxErrorEst, 'xr')
plt.grid()
plt.show()
plt.close()
return errorEstTrain, muidx, maxErrorEst, mu
def greedy(self, plotEst : bool = False):
"""Compute greedy snapshots of solution map."""
if self.samplingEngine.samples is None:
if self.verbosity >= 2:
verbosityDepth("INIT", "Starting computation of snapshots.")
self.resetSamples()
self.mus, _ = self.trainingSetGenerator.generatePoints(
self.nTestPoints)
self.mus = np.array([x[0] for x in self.mus], dtype = np.complex)
nTrain = self.nTrainingPoints
muTrainBase, _ = self.trainingSetGenerator.generatePoints(nTrain)
self.muTrain = np.empty(len(muTrainBase) + 1, dtype = np.complex)
j = 0
for mu in muTrainBase:
if not np.any(np.isclose(self.mus, mu)):
self.muTrain[j] = mu[0]
j += 1
self.muTrain[j] = self.mus[-1]
self.muTrain = self.muTrain[: j + 1]
self.mus = self.mus[:-1]
for j in range(len(self.mus)):
if self.verbosity >= 2:
verbosityDepth("MAIN", ("Adding {}-th sample point at {} "
"to test set.").format(
self.samplingEngine.nsamples + 1,
self.mus[j]))
self.samplingEngine.nextSample(self.mus[j],
homogeneized = self.homogeneized)
errorEstTrain, muidx, maxErrorEst, mu = self.greedyNextSample(-1,
plotEst)
if self.verbosity >= 2:
verbosityDepth("MAIN", ("Uniform error estimate {:.4e}.")\
.format(maxErrorEst))
while (self.samplingEngine.nsamples < self.maxIter
and (maxErrorEst > self.greedyTol
or self.samplingEngine.nsamples < self.nTestPoints)):
if (1. - self.refinementRatio) * nTrain > len(self.muTrain):
muTrainExtra, _ = self.trainingSetGenerator.refine(nTrain)
self.muTrain = np.sort(np.append(self.muTrain,
muTrainExtra))
nTrain += len(muTrainExtra)
if self.verbosity >= 5:
verbosityDepth("MAIN", ("Enriching training set by {} "
"elements.").format(
len(muTrainExtra)))
muTrainOld, maxErrorEstOld = self.muTrain, maxErrorEst
errorEstTrain, muidx, maxErrorEst, mu = self.greedyNextSample(
muidx, plotEst)
if self.verbosity >= 2:
verbosityDepth("MAIN", ("Uniform error estimate {:.4e}.")\
.format(maxErrorEst))
if maxErrorEstOld < maxErrorEst * self.TOL_INSTABILITY:
warn(("Instability in a posteriori estimator. Starting "
"preemptive greedy loop termination."))
maxErrorEst = maxErrorEstOld
self.muTrain = muTrainOld
self.mus = self.mus[:-1]
self.samplingEngine.popSample()
self.setupApprox()
break
if self.verbosity >= 2:
verbosityDepth("DEL", ("Done computing snapshots (final "
"snapshot count: {}).").format(
self.samplingEngine.nsamples))
def checkComputedApprox(self) -> bool:
"""
Check if setup of new approximant is not needed.
Returns:
True if new setup is not needed. False otherwise.
"""
return (hasattr(self, "_S") and self.S == len(self.mus)
and super().checkComputedApprox())
- def computeRescaleParameter(self):
+ def computeScaleFactor(self):
"""Compute parameter rescaling factor."""
self.scaleFactor = .5 * np.abs(self.HFEngine.rescaling(
self.trainingSetGenerator.lims[0][0])
- self.HFEngine.rescaling(
self.trainingSetGenerator.lims[1][0]))
diff --git a/rrompy/reduction_methods/taylor/approximant_taylor_pade.py b/rrompy/reduction_methods/taylor/approximant_taylor_pade.py
index cce0740..7a2a162 100644
--- a/rrompy/reduction_methods/taylor/approximant_taylor_pade.py
+++ b/rrompy/reduction_methods/taylor/approximant_taylor_pade.py
@@ -1,529 +1,546 @@
# Copyright (C) 2018 by the RROMPy authors
#
# This file is part of RROMPy.
#
# RROMPy is free software: you can redistribute it and/or modify
# it under the terms of the GNU Lesser General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# RROMPy is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public License
# along with RROMPy. If not, see .
#
from copy import copy
import numpy as np
from rrompy.reduction_methods.base import checkRobustTolerance
from .generic_approximant_taylor import GenericApproximantTaylor
from rrompy.sampling.base.pod_engine import PODEngine
from rrompy.utilities.base.types import Np1D, Np2D, List, Tuple, DictAny
from rrompy.utilities.base.types import HFEng
from rrompy.utilities.base import purgeDict, verbosityDepth
from rrompy.utilities.warning_manager import warn
__all__ = ['ApproximantTaylorPade']
class ApproximantTaylorPade(GenericApproximantTaylor):
"""
ROM single-point fast Pade' approximant computation for parametric
problems.
Args:
HFEngine: HF problem solver.
mu0(optional): Default parameter. Defaults to 0.
approxParameters(optional): Dictionary containing values for main
parameters of approximant. Recognized keys are:
- 'POD': whether to compute POD of snapshots; defaults to True;
- 'rho': weight for computation of original Pade' approximant;
defaults to np.inf, i.e. fast approximant;
- 'M': degree of Pade' approximant numerator; defaults to 0;
- 'N': degree of Pade' approximant denominator; defaults to 0;
- 'E': total number of derivatives current approximant relies upon;
defaults to Emax;
- 'Emax': total number of derivatives of solution map to be
computed; defaults to E;
- 'robustTol': tolerance for robust Pade' denominator management;
defaults to 0;
- 'sampleType': label of sampling type; available values are:
- 'ARNOLDI': orthogonalization of solution derivatives through
Arnoldi algorithm;
- 'KRYLOV': standard computation of solution derivatives.
Defaults to 'KRYLOV'.
Defaults to empty dict.
+ homogeneized: Whether to homogeneize Dirichlet BCs. Defaults to False.
+ verbosity(optional): Verbosity level. Defaults to 10.
Attributes:
HFEngine: HF problem solver.
mu0: Default parameter.
+ homogeneized: Whether to homogeneize Dirichlet BCs.
approxParameters: Dictionary containing values for main parameters of
approximant. Recognized keys are in parameterList.
parameterList: Recognized keys of approximant parameters:
- 'POD': whether to compute POD of snapshots;
- 'rho': weight for computation of original Pade' approximant;
- 'M': degree of Pade' approximant numerator;
- 'N': degree of Pade' approximant denominator;
- 'E': total number of derivatives current approximant relies upon;
- 'Emax': total number of derivatives of solution map to be
computed;
- 'robustTol': tolerance for robust Pade' denominator management;
- 'sampleType': label of sampling type.
POD: Whether to compute QR factorization of derivatives.
rho: Weight of approximant.
M: Numerator degree of approximant.
N: Denominator degree of approximant.
E: Number of solution derivatives over which current approximant is
based upon.
Emax: Total number of solution derivatives to be computed.
robustTol: Tolerance for robust Pade' denominator management.
sampleType: Label of sampling type.
initialHFData: HF problem initial data.
uHF: High fidelity solution with wavenumber lastSolvedHF as numpy
complex vector.
lastSolvedHF: Wavenumber corresponding to last computed high fidelity
solution.
G: Square Numpy 2D vector of size (N+1) corresponding to Pade'
denominator matrix (see paper).
Q: Numpy 1D vector containing complex coefficients of approximant
denominator.
P: Numpy 2D vector whose columns are FE dofs of coefficients of
approximant numerator.
uApp: Last evaluated approximant as numpy complex vector.
lastApproxParameters: List of parameters corresponding to last
computed approximant.
"""
def __init__(self, HFEngine:HFEng, mu0 : complex = 0,
approxParameters : DictAny = {}, homogeneized : bool = False,
verbosity : int = 10):
self._preInit()
self._addParametersToList(["M", "N", "robustTol", "rho"])
super().__init__(HFEngine = HFEngine, mu0 = mu0,
approxParameters = approxParameters,
homogeneized = homogeneized,
verbosity = verbosity)
self._postInit()
@property
def approxParameters(self):
"""Value of approximant parameters."""
return self._approxParameters
@approxParameters.setter
def approxParameters(self, approxParams):
approxParameters = purgeDict(approxParams, self.parameterList,
dictname = self.name() + ".approxParameters",
baselevel = 1)
approxParametersCopy = purgeDict(approxParameters,
["M", "N", "robustTol", "rho"],
True, True, baselevel = 1)
keyList = list(approxParameters.keys())
if "rho" in keyList:
self._rho = approxParameters["rho"]
elif hasattr(self, "rho"):
self._rho = self.rho
else:
self._rho = np.inf
GenericApproximantTaylor.approxParameters.fset(self,
approxParametersCopy)
self.rho = self._rho
if "robustTol" in keyList:
self.robustTol = approxParameters["robustTol"]
elif hasattr(self, "robustTol"):
self.robustTol = self.robustTol
else:
self.robustTol = 0
self._ignoreParWarnings = True
if "M" in keyList:
self.M = approxParameters["M"]
elif hasattr(self, "M"):
self.M = self.M
else:
self.M = 0
del self._ignoreParWarnings
if "N" in keyList:
self.N = approxParameters["N"]
elif hasattr(self, "N"):
self.N = self.N
else:
self.N = 0
@property
def rho(self):
"""Value of rho."""
return self._rho
@rho.setter
def rho(self, rho):
self._rho = np.abs(rho)
self._approxParameters["rho"] = self.rho
@property
def M(self):
"""Value of M. Its assignment may change Emax and E."""
return self._M
@M.setter
def M(self, M):
if M < 0: raise ArithmeticError("M must be non-negative.")
self._M = M
self._approxParameters["M"] = self.M
if not hasattr(self, "_ignoreParWarnings"):
self.checkMNEEmax()
@property
def N(self):
"""Value of N. Its assignment may change Emax."""
return self._N
@N.setter
def N(self, N):
if N < 0: raise ArithmeticError("N must be non-negative.")
self._N = N
self._approxParameters["N"] = self.N
self.checkMNEEmax()
def checkMNEEmax(self):
"""Check consistency of M, N, E, and Emax."""
M = self.M if hasattr(self, "_M") else 0
N = self.N if hasattr(self, "_N") else 0
E = self.E if hasattr(self, "_E") else M + N
Emax = self.Emax if hasattr(self, "_Emax") else M + N
if self.rho == np.inf:
if Emax < max(N, M):
warn(("Prescribed Emax is too small. Updating Emax to "
"max(M, N)."))
self.Emax = max(N, M)
if E < max(N, M):
warn("Prescribed E is too small. Updating E to max(M, N).")
self.E = max(N, M)
else:
if Emax < N + M:
warn("Prescribed Emax is too small. Updating Emax to M + N.")
self.Emax = self.N + M
if E < N + M:
warn("Prescribed E is too small. Updating E to M + N.")
self.E = self.N + M
@property
def robustTol(self):
"""Value of tolerance for robust Pade' denominator management."""
return self._robustTol
@robustTol.setter
def robustTol(self, robustTol):
if robustTol < 0.:
warn("Overriding prescribed negative robustness tolerance to 0.")
robustTol = 0.
self._robustTol = robustTol
self._approxParameters["robustTol"] = self.robustTol
@property
def E(self):
"""Value of E. Its assignment may change Emax."""
return self._E
@E.setter
def E(self, E):
if E < 0: raise ArithmeticError("E must be non-negative.")
self._E = E
self.checkMNEEmax()
self._approxParameters["E"] = self.E
if hasattr(self, "Emax") and self.Emax < self.E:
warn("Prescribed Emax is too small. Updating Emax to E.")
self.Emax = self.E
@property
def Emax(self):
"""Value of Emax. Its assignment may reset computed derivatives."""
return self._Emax
@Emax.setter
def Emax(self, Emax):
if Emax < 0: raise ArithmeticError("Emax must be non-negative.")
if hasattr(self, "Emax"): EmaxOld = self.Emax
else: EmaxOld = -1
if hasattr(self, "_N"): N = self.N
else: N = 0
if hasattr(self, "_M"): M = self.M
else: M = 0
if hasattr(self, "_E"): E = self.E
else: E = 0
if self.rho == np.inf:
if max(N, M, E) > Emax:
warn(("Prescribed Emax is too small. Updating Emax to "
"max(N, M, E)."))
Emax = max(N, M, E)
else:
if max(N + M, E) > Emax:
warn(("Prescribed Emax is too small. Updating Emax to "
"max(N + M, E)."))
Emax = max(N + M, E)
self._Emax = Emax
self._approxParameters["Emax"] = Emax
if EmaxOld >= self.Emax and self.samplingEngine.samples is not None:
self.samplingEngine.samples = self.samplingEngine.samples[:,
: self.Emax + 1]
if (self.sampleType == "ARNOLDI"
and self.samplingEngine.HArnoldi is not None):
self.samplingEngine.HArnoldi = self.samplingEngine.HArnoldi[
: self.Emax + 1,
: self.Emax + 1]
self.samplingEngine.RArnoldi = self.samplingEngine.RArnoldi[
: self.Emax + 1,
: self.Emax + 1]
def setupApprox(self):
"""
Compute Pade' approximant. SVD-based robust eigenvalue management.
"""
if not self.checkComputedApprox():
if self.verbosity >= 5:
verbosityDepth("INIT", "Setting up {}.". format(self.name()))
self.computeDerivatives()
if self.N > 0:
if self.verbosity >= 7:
verbosityDepth("INIT", ("Starting computation of "
"denominator."))
while self.N > 0:
if self.POD:
ev, eV = self.findeveVGQR()
else:
ev, eV = self.findeveVGExplicit()
newParameters = checkRobustTolerance(ev, self.E,
self.robustTol)
if not newParameters:
break
self.approxParameters = newParameters
if self.N <= 0:
eV = np.ones((1, 1))
self.Q = np.poly1d(eV[:, 0])
if self.verbosity >= 7:
verbosityDepth("DEL", "Done computing denominator.")
else:
self.Q = np.poly1d([1])
if self.verbosity >= 7:
verbosityDepth("INIT", "Starting computation of numerator.")
self.P = np.zeros((self.Emax + 1, self.M + 1), dtype = np.complex)
for i in range(self.Q.order):
rng = np.arange(self.M + 1 - i)
self.P[rng, - 1 - rng - i] = self.Q[i]
if self.sampleType == "ARNOLDI":
self.P = self.samplingEngine.RArnoldi.dot(self.P)
if self.verbosity >= 7:
verbosityDepth("DEL", "Done computing numerator.")
self.lastApproxParameters = copy(self.approxParameters)
if hasattr(self, "lastSolvedApp"): del self.lastSolvedApp
if self.verbosity >= 5:
verbosityDepth("DEL", "Done setting up approximant.\n")
def rescaleParameter(self, R:Np2D, A : Np2D = None,
exponent : float = 1.) -> Np2D:
"""
Prepare parameter rescaling.
Args:
R: Matrix whose columns need rescaling.
A(optional): Matrix whose diagonal defines scaling factor. If None,
previous value of scaleFactor is used. Defaults to None.
exponent(optional): Exponent of scaling factor in matrix diagonal.
Defaults to 1.
Returns:
Rescaled matrix.
"""
if A is not None:
aDiag = np.diag(A)
scaleCoeffs = np.polyfit(np.arange(A.shape[1]),
np.log(aDiag), 1)
self.scaleFactor = np.exp(- scaleCoeffs[0] / exponent)
return np.multiply(R, np.power(self.scaleFactor,np.arange(R.shape[1])))
def buildG(self):
"""Assemble Pade' denominator matrix."""
self.computeDerivatives()
if self.rho == np.inf:
Nmin = self.E - self.N
else:
Nmin = self.M - self.N + 1
if self.sampleType == "KRYLOV":
DerE = self.samplingEngine.samples[:, Nmin : self.E + 1]
G = self.HFEngine.innerProduct(DerE, DerE)
DerE = self.rescaleParameter(DerE, G, 2.)
G = self.HFEngine.innerProduct(DerE, DerE)
else:
RArnE = self.samplingEngine.RArnoldi[: self.E + 1,
Nmin : self.E + 1]
RArnE = self.rescaleParameter(RArnE, RArnE[Nmin :, :])
G = RArnE.conj().T.dot(RArnE)
if self.rho == np.inf:
self.G = G
else:
Gbig = G
self.G = np.zeros((self.N + 1, self.N + 1), dtype = np.complex)
for k in range(self.E - self.M):
self.G += self.rho ** (2 * k) * Gbig[k : k + self.N + 1,
k : k + self.N + 1]
def findeveVGExplicit(self) -> Tuple[Np1D, Np2D]:
"""
Compute explicitly eigenvalues and eigenvectors of Pade' denominator
matrix.
"""
if self.verbosity >= 10:
verbosityDepth("INIT", "Building gramian matrix.")
self.buildG()
if self.verbosity >= 10:
verbosityDepth("DEL", "Done building gramian.")
if self.verbosity >= 7:
verbosityDepth("INIT",
"Solving eigenvalue problem for gramian matrix.")
ev, eV = np.linalg.eigh(self.G)
eV = self.rescaleParameter(eV.T).T
+ if self.verbosity >= 5:
+ try: condev = ev[-1] / ev[0]
+ except: condev = np.inf
+ verbosityDepth("MAIN", ("Solved eigenvalue problem of size {} "
+ "with condition number {:.4e}.").format(
+ self.N + 1, condev))
if self.verbosity >= 7:
verbosityDepth("DEL", "Done solving eigenvalue problem.")
return ev, eV
def findeveVGQR(self) -> Tuple[Np1D, Np2D]:
"""
Compute eigenvalues and eigenvectors of Pade' denominator matrix
through SVD of R factor. See ``Householder triangularization of a
quasimatrix'', L.Trefethen, 2008 for QR algorithm.
Returns:
Eigenvalues in ascending order and corresponding eigenvector
matrix.
"""
self.computeDerivatives()
if self.rho == np.inf:
Nmin = self.E - self.N
else:
Nmin = self.M - self.N + 1
if self.sampleType == "KRYLOV":
A = copy(self.samplingEngine.samples[:, Nmin : self.E + 1])
self.PODEngine = PODEngine(self.HFEngine)
if self.verbosity >= 10:
verbosityDepth("INIT", "Orthogonalizing samples.", end = "")
R = self.PODEngine.QRHouseholder(A, only_R = True)
if self.verbosity >= 10:
verbosityDepth("DEL", " Done.", inline = True)
else:
R = self.samplingEngine.RArnoldi[: self.E + 1, Nmin : self.E + 1]
R = self.rescaleParameter(R, R[Nmin :, :])
if self.rho == np.inf:
if self.verbosity >= 10:
verbosityDepth("INIT", ("Solving svd for square root of "
"gramian matrix."), end = "")
+ sizeI = R.shape[0]
_, s, V = np.linalg.svd(R, full_matrices = False)
else:
if self.verbosity >= 10:
verbosityDepth("INIT", ("Building matrix stack for square "
"root of gramian."), end = "")
Rtower = np.zeros((R.shape[0] * (self.E - self.M), self.N + 1),
dtype = np.complex)
for k in range(self.E - self.M):
Rtower[k * R.shape[0] : (k + 1) * R.shape[0], :] = (
self.rho ** k
* R[:, self.M - self.N + 1 + k : self.M + 1 + k])
if self.verbosity >= 10:
verbosityDepth("DEL", " Done.", inline = True)
if self.verbosity >= 7:
verbosityDepth("INIT", ("Solving svd for square root of "
"gramian matrix."), end = "")
+ sizeI = Rtower.shape[0]
_, s, V = np.linalg.svd(Rtower, full_matrices = False)
eV = V.conj().T[:, ::-1]
eV = self.rescaleParameter(eV.T).T
+ if self.verbosity >= 5:
+ try: condev = s[0] / s[-1]
+ except: condev = np.inf
+ verbosityDepth("MAIN", ("Solved svd problem of size {} x {} with "
+ "condition number {:.4e}.").format(
+ sizeI, self.N + 1, condev))
if self.verbosity >= 7:
verbosityDepth("DEL", " Done.", inline = True)
return s[::-1], eV
def radiusPade(self, mu:Np1D, mu0 : float = None) -> float:
"""
Compute translated radius to be plugged into Pade' approximant.
Args:
mu: Parameter(s) 1.
mu0: Parameter(s) 2. If None, set to self.mu0.
Returns:
Translated radius to be plugged into Pade' approximant.
"""
if mu0 is None: mu0 = self.mu0
return self.HFEngine.rescaling(mu) - self.HFEngine.rescaling(mu0)
def getPVal(self, mu:List[complex]):
"""
Evaluate Pade' numerator at arbitrary parameter.
Args:
mu: Target parameter.
"""
self.setupApprox()
if self.verbosity >= 10:
verbosityDepth("INIT",
"Evaluating numerator at mu = {}.".format(mu),
end = "")
try:
len(mu)
except:
mu = [mu]
powerlist = np.vander(self.radiusPade(mu), self.M + 1).T
p = self.P.dot(powerlist)
if len(mu) == 1:
p = p.flatten()
if self.verbosity >= 10:
verbosityDepth("DEL", " Done.", inline = True)
return p
def getQVal(self, mu:List[complex]):
"""
Evaluate Pade' denominator at arbitrary parameter.
Args:
mu: Target parameter.
"""
self.setupApprox()
if self.verbosity >= 10:
verbosityDepth("INIT",
"Evaluating denominator at mu = {}.".format(mu),
end = "")
q = self.Q(self.radiusPade(mu))
if self.verbosity >= 10:
verbosityDepth("DEL", " Done.", inline = True)
return q
def evalApproxReduced(self, mu:complex):
"""
Evaluate Pade' approximant at arbitrary parameter.
Args:
mu: Target parameter.
"""
self.setupApprox()
if (not hasattr(self, "lastSolvedApp")
or not np.isclose(self.lastSolvedApp, mu)):
if self.verbosity >= 5:
verbosityDepth("INIT",
"Evaluating approximant at mu = {}.".format(mu))
self.uAppReduced = self.getPVal(mu) / self.getQVal(mu)
self.lastSolvedApp = mu
if self.verbosity >= 5:
verbosityDepth("DEL", "Done evaluating approximant.")
def evalApprox(self, mu:complex):
"""
Evaluate approximant at arbitrary parameter.
Args:
mu: Target parameter.
"""
self.evalApproxReduced(mu)
self.uApp = self.samplingEngine.samples.dot(self.uAppReduced)
def getPoles(self) -> Np1D:
"""
Obtain approximant poles.
Returns:
Numpy complex vector of poles.
"""
self.setupApprox()
return self.HFEngine.rescalingInv(self.Q.r
+ self.HFEngine.rescaling(self.mu0))
diff --git a/rrompy/reduction_methods/taylor/approximant_taylor_rb.py b/rrompy/reduction_methods/taylor/approximant_taylor_rb.py
index 7df57a1..62b3f92 100644
--- a/rrompy/reduction_methods/taylor/approximant_taylor_rb.py
+++ b/rrompy/reduction_methods/taylor/approximant_taylor_rb.py
@@ -1,311 +1,314 @@
# Copyright (C) 2018 by the RROMPy authors
#
# This file is part of RROMPy.
#
# RROMPy is free software: you can redistribute it and/or modify
# it under the terms of the GNU Lesser General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# RROMPy is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public License
# along with RROMPy. If not, see .
#
from copy import copy
import numpy as np
import scipy as sp
from .generic_approximant_taylor import GenericApproximantTaylor
from rrompy.sampling.base.pod_engine import PODEngine
from rrompy.utilities.base.types import Np1D, DictAny, HFEng
from rrompy.utilities.base import purgeDict, verbosityDepth
from rrompy.utilities.warning_manager import warn
__all__ = ['ApproximantTaylorRB']
class ApproximantTaylorRB(GenericApproximantTaylor):
"""
ROM single-point fast RB approximant computation for parametric problems
with polynomial dependence up to degree 2.
Args:
HFEngine: HF problem solver.
mu0(optional): Default parameter. Defaults to 0.
approxParameters(optional): Dictionary containing values for main
parameters of approximant. Recognized keys are:
- 'POD': whether to compute POD of snapshots; defaults to True;
- 'R': rank for Galerkin projection; defaults to E + 1;
- 'E': total number of derivatives current approximant relies upon;
defaults to Emax;
- 'Emax': total number of derivatives of solution map to be
computed; defaults to E;
- 'sampleType': label of sampling type; available values are:
- 'ARNOLDI': orthogonalization of solution derivatives through
Arnoldi algorithm;
- 'KRYLOV': standard computation of solution derivatives.
Defaults to 'KRYLOV'.
Defaults to empty dict.
+ homogeneized: Whether to homogeneize Dirichlet BCs. Defaults to False.
+ verbosity(optional): Verbosity level. Defaults to 10.
Attributes:
HFEngine: HF problem solver.
mu0: Default parameter.
+ homogeneized: Whether to homogeneize Dirichlet BCs.
approxParameters: Dictionary containing values for main parameters of
approximant. Recognized keys are in parameterList.
parameterList: Recognized keys of approximant parameters:
- 'POD': whether to compute POD of snapshots;
- 'R': rank for Galerkin projection;
- 'E': total number of derivatives current approximant relies upon;
- 'Emax': total number of derivatives of solution map to be
computed;
- 'sampleType': label of sampling type.
POD: Whether to compute QR factorization of derivatives.
R: Rank for Galerkin projection.
E: Number of solution derivatives over which current approximant is
based upon.
Emax: Total number of solution derivatives to be computed.
sampleType: Label of sampling type, i.e. 'KRYLOV'.
uHF: High fidelity solution with wavenumber lastSolvedHF as numpy
complex vector.
lastSolvedHF: Wavenumber corresponding to last computed high fidelity
solution.
uApp: Last evaluated approximant as numpy complex vector.
lastApproxParameters: List of parameters corresponding to last
computed approximant.
projMat: Numpy matrix representing projection onto RB space.
projMat: Numpy matrix representing projection onto RB space.
As: List of sparse matrices (in CSC format) representing coefficients
of linear system matrix wrt mu.
bs: List of numpy vectors representing coefficients of linear system
RHS wrt mu.
ARBs: List of sparse matrices (in CSC format) representing RB
coefficients of linear system matrix wrt mu.
bRBs: List of numpy vectors representing RB coefficients of linear
system RHS wrt mu.
"""
def __init__(self, HFEngine:HFEng, mu0 : complex = 0,
approxParameters : DictAny = {}, homogeneized : bool = False,
verbosity : int = 10):
self._preInit()
self._addParametersToList(["R"])
super().__init__(HFEngine = HFEngine, mu0 = mu0,
approxParameters = approxParameters,
homogeneized = homogeneized,
verbosity = verbosity)
if self.verbosity >= 10:
verbosityDepth("INIT", "Computing affine blocks of system.")
self.As, self.thetaAs = self.HFEngine.affineBlocksA(self.mu0)
self.bs, self.thetabs = self.HFEngine.affineBlocksb(self.mu0,
self.homogeneized)
if self.verbosity >= 10:
verbosityDepth("DEL", "Done computing affine blocks.")
self._postInit()
def resetSamples(self):
"""Reset samples."""
super().resetSamples()
self.projMat = None
@property
def approxParameters(self):
"""
Value of approximant parameters. Its assignment may change M, N and S.
"""
return self._approxParameters
@approxParameters.setter
def approxParameters(self, approxParams):
approxParameters = purgeDict(approxParams, self.parameterList,
dictname = self.name() + ".approxParameters",
baselevel = 1)
approxParametersCopy = purgeDict(approxParameters, ["R"],
True, True, baselevel = 1)
GenericApproximantTaylor.approxParameters.fset(self,
approxParametersCopy)
keyList = list(approxParameters.keys())
if "R" in keyList:
self.R = approxParameters["R"]
else:
self.R = self.E + 1
@property
def POD(self):
"""Value of POD."""
return self._POD
@POD.setter
def POD(self, POD):
GenericApproximantTaylor.POD.fset(self, POD)
if (hasattr(self, "sampleType") and self.sampleType == "ARNOLDI"
and not self.POD):
warn(("Arnoldi sampling implicitly forces POD-type derivative "
"management."))
@property
def sampleType(self):
"""Value of sampleType."""
return self._sampleType
@sampleType.setter
def sampleType(self, sampleType):
GenericApproximantTaylor.sampleType.fset(self, sampleType)
if (hasattr(self, "POD") and not self.POD
and self.sampleType == "ARNOLDI"):
warn(("Arnoldi sampling implicitly forces POD-type derivative "
"management."))
@property
def R(self):
"""Value of R. Its assignment may change S."""
return self._R
@R.setter
def R(self, R):
if R < 0: raise ArithmeticError("R must be non-negative.")
self._R = R
self._approxParameters["R"] = self.R
if hasattr(self, "E") and self.E + 1 < self.R:
warn("Prescribed E is too small. Updating E to R - 1.")
self.E = self.R - 1
def setupApprox(self):
"""Setup RB system."""
if not self.checkComputedApprox():
if self.verbosity >= 5:
verbosityDepth("INIT", "Setting up {}.". format(self.name()))
self.computeDerivatives()
if self.verbosity >= 7:
verbosityDepth("INIT", "Computing projection matrix.",
end = "")
if self.POD and not self.sampleType == "ARNOLDI":
self.PODEngine = PODEngine(self.HFEngine)
self.projMatQ, self.projMatR = self.PODEngine.QRHouseholder(
self.samplingEngine.samples)
if self.POD:
if self.sampleType == "ARNOLDI":
self.projMatR = self.samplingEngine.RArnoldi
self.projMatQ = self.samplingEngine.samples
U, _, _ = np.linalg.svd(self.projMatR[: self.E + 1,
: self.E + 1])
self.projMat = self.projMatQ[:, : self.E + 1].dot(U[:,
: self.R])
else:
self.projMat = self.samplingEngine.samples[:, : self.R + 1]
if self.verbosity >= 7:
verbosityDepth("DEL", " Done.", inline = True)
self.lastApproxParameters = copy(self.approxParameters)
if hasattr(self, "lastSolvedApp"): del self.lastSolvedApp
self.assembleReducedSystem()
if self.verbosity >= 5:
verbosityDepth("DEL", "Done setting up approximant.\n")
def assembleReducedSystem(self):
"""Build affine blocks of RB linear system through projections."""
if not self.checkComputedApprox():
self.setupApprox()
if self.verbosity >= 10:
verbosityDepth("INIT", "Projecting affine terms of HF model.",
end = "")
projMatH = self.projMat.T.conj()
self.ARBs = [None] * len(self.As)
self.bRBs = [None] * len(self.bs)
if self.verbosity >= 10:
verbosityDepth("MAIN", ".", end = "", inline = True)
for j in range(len(self.As)):
self.ARBs[j] = projMatH.dot(self.As[j].dot(self.projMat))
if self.verbosity >= 10:
verbosityDepth("MAIN", ".", end = "", inline = True)
for j in range(len(self.bs)):
self.bRBs[j] = projMatH.dot(self.bs[j])
if self.verbosity >= 10:
verbosityDepth("DEL", "Done.", inline = True)
def solveReducedSystem(self, mu:complex) -> Np1D:
"""
Solve RB linear system.
Args:
mu: Target parameter.
Returns:
Solution of RB linear system.
"""
self.setupApprox()
if self.verbosity >= 10:
verbosityDepth("INIT",
"Assembling reduced model for mu = {}.".format(mu),
end = "")
ARBmu = self.thetaAs(mu, 0) * self.ARBs[0][:self.R,:self.R]
bRBmu = self.thetabs(mu, 0) * self.bRBs[0][:self.R]
if self.verbosity >= 10:
verbosityDepth("MAIN", ".", end = "", inline = True)
for j in range(1, len(self.ARBs)):
ARBmu += self.thetaAs(mu, j) * self.ARBs[j][:self.R, :self.R]
if self.verbosity >= 10:
verbosityDepth("MAIN", ".", end = "", inline = True)
for j in range(1, len(self.bRBs)):
bRBmu += self.thetabs(mu, j) * self.bRBs[j][:self.R]
if self.verbosity >= 10:
verbosityDepth("DEL", "Done.", inline = True)
if self.verbosity >= 5:
verbosityDepth("INIT",
"Solving reduced model for mu = {}.".format(mu),
end = "")
uRB = np.linalg.solve(ARBmu, bRBmu)
if self.verbosity >= 5:
verbosityDepth("DEL", " Done.", inline = True)
return uRB
def evalApproxReduced(self, mu:complex):
"""
Evaluate RB approximant at arbitrary wavenumber.
Args:
mu: Target parameter.
"""
self.setupApprox()
if (not hasattr(self, "lastSolvedApp")
or not np.isclose(self.lastSolvedApp, mu)):
if self.verbosity >= 5:
verbosityDepth("INIT",
"Computing RB solution at mu = {}.".format(mu))
self.uAppReduced = self.solveReducedSystem(mu)
self.lastSolvedApp = mu
if self.verbosity >= 5:
verbosityDepth("DEL", "Done computing RB solution.")
def evalApprox(self, mu:complex):
"""
Evaluate approximant at arbitrary parameter.
Args:
mu: Target parameter.
"""
self.evalApproxReduced(mu)
self.uApp = self.projMat[:, : self.R].dot(self.uAppReduced)
def getPoles(self) -> Np1D:
"""
Obtain approximant poles.
Returns:
Numpy complex vector of poles.
"""
warn(("Impossible to compute poles in general affine parameter "
"dependence. Results subject to interpretation/rescaling, or "
"possibly completely wrong."))
self.setupApprox()
if len(self.ARBs) < 2:
return
A = np.eye(self.ARBs[0].shape[0] * (len(self.ARBs) - 1),
dtype = np.complex)
B = np.zeros_like(A)
A[: self.ARBs[0].shape[0], : self.ARBs[0].shape[0]] = - self.ARBs[0]
for j in range(len(self.ARBs) - 1):
Aj = self.ARBs[j + 1]
B[: Aj.shape[0], j * Aj.shape[0] : (j + 1) * Aj.shape[0]] = Aj
II = np.arange(self.ARBs[0].shape[0],
self.ARBs[0].shape[0] * (len(self.ARBs) - 1))
B[II, II - self.ARBs[0].shape[0]] = 1.
return self.HFEngine.rescalingInv(sp.linalg.eigvals(A, B)
+ self.HFEngine.rescaling(self.mu0))
diff --git a/rrompy/reduction_methods/taylor/generic_approximant_taylor.py b/rrompy/reduction_methods/taylor/generic_approximant_taylor.py
index 557bd82..fc7cac1 100644
--- a/rrompy/reduction_methods/taylor/generic_approximant_taylor.py
+++ b/rrompy/reduction_methods/taylor/generic_approximant_taylor.py
@@ -1,241 +1,244 @@
# Copyright (C) 2018 by the RROMPy authors
#
# This file is part of RROMPy.
#
# RROMPy is free software: you can redistribute it and/or modify
# it under the terms of the GNU Lesser General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# RROMPy is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public License
# along with RROMPy. If not, see .
#
import numpy as np
from rrompy.reduction_methods.base.generic_approximant import (
GenericApproximant)
from rrompy.utilities.base.types import DictAny, HFEng
from rrompy.utilities.base import purgeDict, verbosityDepth
from rrompy.utilities.warning_manager import warn
__all__ = ['GenericApproximantTaylor']
class GenericApproximantTaylor(GenericApproximant):
"""
ROM single-point approximant computation for parametric problems
(ABSTRACT).
Args:
HFEngine: HF problem solver.
mu0(optional): Default parameter. Defaults to 0.
approxParameters(optional): Dictionary containing values for main
parameters of approximant. Recognized keys are:
- 'POD': whether to compute POD of snapshots; defaults to True;
- 'E': total number of derivatives current approximant relies upon;
defaults to Emax;
- 'Emax': total number of derivatives of solution map to be
computed; defaults to E;
- 'sampleType': label of sampling type; available values are:
- 'ARNOLDI': orthogonalization of solution derivatives through
Arnoldi algorithm;
- 'KRYLOV': standard computation of solution derivatives.
Defaults to 'KRYLOV'.
Defaults to empty dict.
+ homogeneized: Whether to homogeneize Dirichlet BCs. Defaults to False.
+ verbosity(optional): Verbosity level. Defaults to 10.
Attributes:
HFEngine: HF problem solver.
mu0: Default parameter.
+ homogeneized: Whether to homogeneize Dirichlet BCs.
approxParameters: Dictionary containing values for main parameters of
approximant. Recognized keys are in parameterList.
parameterList: Recognized keys of approximant parameters:
- 'POD': whether to compute POD of snapshots;
- 'E': total number of derivatives current approximant relies upon;
- 'Emax': total number of derivatives of solution map to be
computed;
- 'sampleType': label of sampling type.
POD: Whether to compute QR factorization of derivatives.
E: Number of solution derivatives over which current approximant is
based upon.
Emax: Total number of solution derivatives to be computed.
sampleType: Label of sampling type.
initialHFData: HF problem initial data.
samplingEngine: Sampling engine.
uHF: High fidelity solution with wavenumber lastSolvedHF as numpy
complex vector.
lastSolvedHF: Wavenumber corresponding to last computed high fidelity
solution.
uApp: Last evaluated approximant as numpy complex vector.
lastApproxParameters: List of parameters corresponding to last
computed approximant.
"""
def __init__(self, HFEngine:HFEng, mu0 : complex = 0,
approxParameters : DictAny = {}, homogeneized : bool = False,
verbosity : int = 10):
self._preInit()
self._addParametersToList(["E", "Emax", "sampleType"])
super().__init__(HFEngine = HFEngine, mu0 = mu0,
approxParameters = approxParameters,
homogeneized = homogeneized,
verbosity = verbosity)
self._postInit()
def setupSampling(self):
"""Setup sampling engine."""
if not hasattr(self, "sampleType"): return
if self.sampleType == "ARNOLDI":
from rrompy.sampling.scipy.sampling_engine_arnoldi import (
SamplingEngineArnoldi)
super().setupSampling(SamplingEngineArnoldi)
elif self.sampleType == "KRYLOV":
from rrompy.sampling.scipy.sampling_engine_krylov import (
SamplingEngineKrylov)
super().setupSampling(SamplingEngineKrylov)
else:
raise Exception("Sample type not recognized.")
@property
def approxParameters(self):
"""
Value of approximant parameters. Its assignment may change E and Emax.
"""
return self._approxParameters
@approxParameters.setter
def approxParameters(self, approxParams):
approxParameters = purgeDict(approxParams, self.parameterList,
dictname = self.name() + ".approxParameters",
baselevel = 1)
approxParametersCopy = purgeDict(approxParameters,
["E", "Emax", "sampleType"],
True, True, baselevel = 1)
GenericApproximant.approxParameters.fset(self, approxParametersCopy)
keyList = list(approxParameters.keys())
if "E" in keyList:
self._E = approxParameters["E"]
self._approxParameters["E"] = self.E
if "Emax" in keyList:
self.Emax = approxParameters["Emax"]
else:
if not hasattr(self, "Emax"):
self.Emax = self.E
else:
self.Emax = self.Emax
else:
if "Emax" in keyList:
self._E = approxParameters["Emax"]
self._approxParameters["E"] = self.E
self.Emax = self.E
else:
if not (hasattr(self, "Emax") and hasattr(self, "E")):
raise Exception("At least one of E and Emax must be set.")
if "sampleType" in keyList:
self.sampleType = approxParameters["sampleType"]
elif hasattr(self, "sampleType"):
self.sampleType = self.sampleType
else:
self.sampleType = "KRYLOV"
@property
def E(self):
"""Value of E. Its assignment may change Emax."""
return self._E
@E.setter
def E(self, E):
if E < 0: raise ArithmeticError("E must be non-negative.")
self._E = E
self._approxParameters["E"] = self.E
if hasattr(self, "Emax") and self.Emax < self.E:
warn("Prescribed E is too large. Updating Emax to E.")
self.Emax = self.E
@property
def Emax(self):
"""Value of Emax. Its assignment may reset computed derivatives."""
return self._Emax
@Emax.setter
def Emax(self, Emax):
if Emax < 0: raise ArithmeticError("Emax must be non-negative.")
if hasattr(self, "Emax"): EmaxOld = self.Emax
else: EmaxOld = -1
self._Emax = Emax
if hasattr(self, "E") and self.Emax < self.E:
warn("Prescribed Emax is too small. Updating Emax to E.")
self.Emax = self.E
else:
self._approxParameters["Emax"] = self.Emax
if (EmaxOld >= self.Emax
and self.samplingEngine.samples is not None):
self.samplingEngine.samples = self.samplingEngine.samples[:,
: self.Emax + 1]
if (self.sampleType == "ARNOLDI"
and self.samplingEngine.HArnoldi is not None):
self.samplingEngine.HArnoldi= self.samplingEngine.HArnoldi[
: self.Emax + 1,
: self.Emax + 1]
self.samplingEngine.RArnoldi= self.samplingEngine.RArnoldi[
: self.Emax + 1,
: self.Emax + 1]
else:
self.resetSamples()
@property
def sampleType(self):
"""Value of sampleType."""
return self._sampleType
@sampleType.setter
def sampleType(self, sampleType):
if hasattr(self, "sampleType"): sampleTypeOld = self.sampleType
else: sampleTypeOld = -1
try:
sampleType = sampleType.upper().strip().replace(" ","")
if sampleType not in ["ARNOLDI", "KRYLOV"]:
raise Exception("Sample type not recognized.")
self._sampleType = sampleType
except:
warn(("Prescribed sampleType not recognized. Overriding to "
"'KRYLOV'."))
self._sampleType = "KRYLOV"
self._approxParameters["sampleType"] = self.sampleType
if sampleTypeOld != self.sampleType:
self.resetSamples()
def computeDerivatives(self):
"""Compute derivatives of solution map starting from order 0."""
if self.samplingEngine.samples is None:
if self.verbosity >= 5:
verbosityDepth("INIT", "Starting computation of derivatives.")
self.samplingEngine.iterSample(self.mu0, self.Emax + 1,
homogeneized = self.homogeneized)
if self.verbosity >= 5:
verbosityDepth("DEL", "Done computing derivatives.")
def checkComputedApprox(self) -> bool:
"""
Check if setup of new approximant is not needed.
Returns:
True if new setup is not needed. False otherwise.
"""
return (self.samplingEngine.samples is not None
and super().checkComputedApprox())
def normApp(self, mu:complex, homogeneized : bool = False) -> float:
"""
Compute norm of approximant at arbitrary parameter.
Args:
mu: Target parameter.
homogeneized(optional): Whether to remove Dirichlet BC. Defaults to
False.
Returns:
Target norm of approximant.
"""
if self.sampleType != "ARNOLDI" or self.homogeneized != homogeneized:
return super().normApp(mu, homogeneized)
return np.linalg.norm(self.getAppReduced(mu, homogeneized))
diff --git a/rrompy/utilities/base/__init__.py b/rrompy/utilities/base/__init__.py
index 916595f..90a85f8 100644
--- a/rrompy/utilities/base/__init__.py
+++ b/rrompy/utilities/base/__init__.py
@@ -1,44 +1,46 @@
# Copyright (C) 2018 by the RROMPy authors
#
# This file is part of RROMPy.
#
# RROMPy is free software: you can redistribute it and/or modify
# it under the terms of the GNU Lesser General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# RROMPy is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public License
# along with RROMPy. If not, see .
#
from .find_dict_str_key import findDictStrKey
from .get_new_filename import getNewFilename
from .purge_dict import purgeDict
from .purge_list import purgeList
from .number_theory import (squareResonances, primeFactorize,
getLowestPrimeFactor)
from .sobol import sobolGenerate
from . import types as Types
from . import fenics as Fenics
from .verbosity_depth import verbosityDepth
+from .custom_fit import customFit
__all__ = [
'findDictStrKey',
'getNewFilename',
'purgeDict',
'purgeList',
'squareResonances',
'primeFactorize',
'getLowestPrimeFactor',
'sobolGenerate',
'Types',
'Fenics',
- 'verbosityDepth'
+ 'verbosityDepth',
+ 'customFit'
]
diff --git a/rrompy/utilities/base/custom_fit.py b/rrompy/utilities/base/custom_fit.py
new file mode 100644
index 0000000..1d13d0f
--- /dev/null
+++ b/rrompy/utilities/base/custom_fit.py
@@ -0,0 +1,137 @@
+# Copyright (C) 2018 by the RROMPy authors
+#
+# This file is part of RROMPy.
+#
+# RROMPy is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Lesser General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# RROMPy is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public License
+# along with RROMPy. If not, see .
+#
+
+import numpy as np
+import numpy.linalg as la
+from warnings import warn
+
+__all__ = ["customFit"]
+
+def customFit(van, y, rcond=None, full=False, w=None):
+ """
+ Least-squares fit of a polynomial to data. Copied from
+ numpy.polynomial.polynomial.
+
+ Parameters
+ ----------
+ va : array_like, shape (`M`,`deg` + 1)
+ Vandermonde-like matrix.
+ y : array_like, shape (`M`,) or (`M`, `K`)
+ y-coordinates of the sample points. Several sets of sample points
+ sharing the same x-coordinates can be (independently) fit with one
+ call to `polyfit` by passing in for `y` a 2-D array that contains
+ one data set per column.
+ rcond : float, optional
+ Relative condition number of the fit. Singular values smaller
+ than `rcond`, relative to the largest singular value, will be
+ ignored. The default value is ``len(van)*eps``, where `eps` is the
+ relative precision of the platform's float type, about 2e-16 in
+ most cases.
+ full : bool, optional
+ Switch determining the nature of the return value. When ``False``
+ (the default) just the coefficients are returned; when ``True``,
+ diagnostic information from the singular value decomposition (used
+ to solve the fit's matrix equation) is also returned.
+ w : array_like, shape (`M`,), optional
+ Weights. If not None, the contribution of each point
+ ``(x[i],y[i])`` to the fit is weighted by `w[i]`. Ideally the
+ weights are chosen so that the errors of the products ``w[i]*y[i]``
+ all have the same variance. The default value is None.
+
+ Returns
+ -------
+ coef : ndarray, shape (`deg` + 1,) or (`deg` + 1, `K`)
+ Polynomial coefficients ordered from low to high. If `y` was 2-D,
+ the coefficients in column `k` of `coef` represent the polynomial
+ fit to the data in `y`'s `k`-th column.
+
+ [residuals, rank, singular_values, rcond] : list
+ These values are only returned if `full` = True
+
+ resid -- sum of squared residuals of the least squares fit
+ rank -- the numerical rank of the scaled Vandermonde matrix
+ sv -- singular values of the scaled Vandermonde matrix
+ rcond -- value of `rcond`.
+
+ For more details, see `linalg.lstsq`.
+
+ Raises
+ ------
+ RankWarning
+ Raised if the matrix in the least-squares fit is rank deficient.
+ The warning is only raised if `full` == False. The warnings can
+ be turned off by:
+
+ >>> import warnings
+ >>> warnings.simplefilter('ignore', RankWarning)
+ """
+ van = np.asarray(van) + 0.0
+ y = np.asarray(y) + 0.0
+
+ # check arguments.
+ if van.ndim != 2:
+ raise TypeError("expected 2D vector for van")
+ if van.size == 0:
+ raise TypeError("expected non-empty vector for van")
+ if y.ndim < 1 or y.ndim > 2:
+ raise TypeError("expected 1D or 2D array for y")
+ if len(van) != len(y):
+ raise TypeError("expected van and y to have same length")
+
+ order = van.shape[1]
+
+ # set up the least squares matrices in transposed form
+ lhs = van.T
+ rhs = y.T
+ if w is not None:
+ w = np.asarray(w) + 0.0
+ if w.ndim != 1:
+ raise TypeError("expected 1D vector for w")
+ if len(van) != len(w):
+ raise TypeError("expected van and w to have same length")
+ # apply weights. Don't use inplace operations as they
+ # can cause problems with NA.
+ lhs = lhs * w
+ rhs = rhs * w
+
+ # set rcond
+ if rcond is None:
+ rcond = len(van)*np.finfo(van.dtype).eps
+
+ # Determine the norms of the design matrix columns.
+ if issubclass(lhs.dtype.type, np.complexfloating):
+ scl = np.sqrt((np.square(lhs.real) + np.square(lhs.imag)).sum(1))
+ else:
+ scl = np.sqrt(np.square(lhs).sum(1))
+ scl[scl == 0] = 1
+
+ # Solve the least squares problem.
+ c, resids, rank, s = la.lstsq(lhs.T/scl, rhs.T, rcond)
+ c = (c.T/scl).T
+
+ # warn on rank reduction
+ if rank != order and not full:
+ msg = "The fit may be poorly conditioned"
+ warn(msg, np.polynomial.polyutils.RankWarning, stacklevel = 2)
+
+ if full:
+ return c, [resids, rank, s, rcond]
+ else:
+ return c
+
+