diff --git a/examples/diapason/diapason.geo b/examples/data/mesh/diapason.geo
similarity index 100%
rename from examples/diapason/diapason.geo
rename to examples/data/mesh/diapason.geo
diff --git a/examples/diapason/diapason_1.xml b/examples/data/mesh/diapason_1.xml
similarity index 100%
rename from examples/diapason/diapason_1.xml
rename to examples/data/mesh/diapason_1.xml
diff --git a/examples/diapason/diapason_1_physical_region.xml b/examples/data/mesh/diapason_1_physical_region.xml
similarity index 100%
rename from examples/diapason/diapason_1_physical_region.xml
rename to examples/data/mesh/diapason_1_physical_region.xml
diff --git a/examples/diapason/diapason_2.xml b/examples/data/mesh/diapason_2.xml
similarity index 100%
rename from examples/diapason/diapason_2.xml
rename to examples/data/mesh/diapason_2.xml
diff --git a/examples/diapason/diapason_2_physical_region.xml b/examples/data/mesh/diapason_2_physical_region.xml
similarity index 100%
rename from examples/diapason/diapason_2_physical_region.xml
rename to examples/data/mesh/diapason_2_physical_region.xml
diff --git a/examples/diapason/diapason_3.xml b/examples/data/mesh/diapason_3.xml
similarity index 100%
rename from examples/diapason/diapason_3.xml
rename to examples/data/mesh/diapason_3.xml
diff --git a/examples/diapason/diapason_3_physical_region.xml b/examples/data/mesh/diapason_3_physical_region.xml
similarity index 100%
rename from examples/diapason/diapason_3_physical_region.xml
rename to examples/data/mesh/diapason_3_physical_region.xml
diff --git a/examples/diapason/greedy.py b/examples/diapason/greedy.py
index 81d767e..9cc8a52 100644
--- a/examples/diapason/greedy.py
+++ b/examples/diapason/greedy.py
@@ -1,171 +1,177 @@
import numpy as np
import fenics as fen
import ufl
from rrompy.hfengines.vector_linear_problem import \
LinearElasticityHelmholtzProblemEngine as LEHPE
from rrompy.hfengines.vector_linear_problem import \
LinearElasticityHelmholtzProblemEngineDamped as LEHPED
from rrompy.reduction_methods.lagrange_greedy import \
ApproximantLagrangePadeGreedy as Pade
from rrompy.reduction_methods.lagrange_greedy import \
ApproximantLagrangeRBGreedy as RB
verb = 2
timed = False
algo = "Pade"
#algo = "RB"
polyBasis = "LEGENDRE"
polyBasis = "CHEBYSHEV"
#polyBasis = "MONOMIAL"
if timed: verb = 0
dampingEta = 0 * 1e4 / 2. / np.pi
-k0s = np.linspace(2.5e2, 1.0e4, 100)
-#k0s = np.linspace(2.5e3, 1.5e4, 100)
-#k0s = np.linspace(5.0e4, 1.0e5, 100)
+k0s = np.linspace(2.5e2, 7.5e3, 100)
+k0s = np.linspace(2.5e3, 1.5e4, 100)
+k0s = np.linspace(5.0e4, 1.0e5, 100)
k0s = np.linspace(2.0e5, 2.5e5, 100)
k0 = np.mean(np.power(k0s, 2.)) ** .5 # [Hz]
kl, kr = min(k0s), max(k0s)
params = {'muBounds':[kl, kr], 'nTrainingPoints':5e2, 'Delta':0,
- 'greedyTol':1e-3, 'nTestPoints':2, 'basis':polyBasis,
+ 'greedyTol':1e-2, 'nTestPoints':2, 'polybasis':polyBasis,
'robustTol':2e-16, 'interpRcond':None, 'errorEstimatorKind':'EXACT'}
theta = 20. * np.pi / 180.
phi = 10. * np.pi / 180.
-mesh = fen.Mesh("./diapason_3.xml")
+mesh = fen.Mesh("../data/mesh/diapason_1.xml")
subdomains = fen.MeshFunction("size_t", mesh,
- "./diapason_3_physical_region.xml")
+ "../data/mesh/diapason_1_physical_region.xml")
meshBall = fen.SubMesh(mesh, subdomains, 2)
meshFork = fen.SubMesh(mesh, subdomains, 1)
Hball = np.max(meshBall.coordinates()[:, 1]) #.00257
Ltot = np.max(mesh.coordinates()[:, 1]) #.1022
Lhandle = np.max(meshFork.coordinates()[:, 1]) #.026
Lrod = Ltot - Lhandle #.0762
L2var = (Lrod / 4.) ** 2.
Ehandle_ratio = 3.
rhohandle_ratio = 1.5
c = 3.e2
rho = 8e3 * (2. * np.pi) ** 2.
E = 1.93e11
nu = .3
T = 1e6
lambda_ = E * nu / (1. + nu) / (1. - 2. * nu)
mu_ = E / (1. + nu)
kWave = (np.cos(theta) * np.cos(phi), np.sin(phi), np.sin(theta) * np.cos(phi))
x, y, z = fen.SpatialCoordinate(mesh)[:]
yCorr = y - Ltot
compPlane = kWave[0] * x + kWave[1] * y + kWave[2] * z
xPlane, yPlane, zPlane = (xx - compPlane * xx for xx in (x, y, z))
xOrtho, yOrtho, zOrtho = (compPlane * xx for xx in (x, y, z))
forcingBase = (T / (2. * np.pi * L2var)**.5
* fen.exp(- (xPlane**2. + yPlane**2. + zPlane**2.) / (2.*L2var)))
forcingWeight = np.real(k0) / c * (xOrtho + yOrtho + zOrtho)
neumannDatum = [ufl.as_vector(
tuple(forcingBase * fen.cos(forcingWeight) * kWavedir for kWavedir in kWave)),
ufl.as_vector(
tuple(forcingBase * fen.sin(forcingWeight) * kWavedir for kWavedir in kWave))]
lambda_eff = ufl.conditional(ufl.ge(y, Lhandle), fen.Constant(lambda_),
fen.Constant(Ehandle_ratio * lambda_))
mu_eff = ufl.conditional(ufl.ge(y, Lhandle), fen.Constant(mu_),
fen.Constant(Ehandle_ratio * mu_))
rho_eff = ufl.conditional(ufl.ge(y, Lhandle), fen.Constant(rho),
fen.Constant(rhohandle_ratio * rho))
###
if dampingEta > 0:
solver = LEHPED(degree_threshold = 8, verbosity = 0)
solver.eta = dampingEta
else:
solver = LEHPE(degree_threshold = 8, verbosity = 0)
solver.omega = np.real(k0)
solver.lambda_ = lambda_eff
solver.mu_ = mu_eff
solver.rho_ = rho_eff
solver.V = fen.VectorFunctionSpace(mesh, "P", 1)
solver.DirichletBoundary = lambda x, on_b: on_b and x[1] < Hball
solver.NeumannBoundary = "REST"
solver.forcingTerm = neumannDatum
if algo == "Pade":
approx = Pade(solver, mu0 = k0, approxParameters = params,
verbosity = verb)
else:
+ params.pop("Delta")
+ params.pop("polybasis")
+ params.pop("robustTol")
+ params.pop("interpRcond")
+ params.pop("errorEstimatorKind")
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)
-print("Poles:\n", approx.getPoles())
+polesApp = approx.getPoles()
+print("Poles:\n", polesApp)
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.normApprox(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]) / norm[j]
resApp = approx.errorEstimator(k0s)
res[res < 1e-5 * approx.greedyTol] = np.nan
resApp[resApp < 1e-5 * approx.greedyTol] = np.nan
err[err < 1e-8 * approx.greedyTol] = np.nan
plt.figure()
plt.semilogy(k0s, norm)
plt.semilogy(k0s, normApp, '--')
plt.semilogy(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),
+ approx.greedyTol*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]
+polesAppEff = polesApp[~mask]
plt.figure()
-plt.plot(np.real(polesApp), np.imag(polesApp), 'kx')
+plt.plot(np.real(polesAppEff), np.imag(polesAppEff), 'kx')
plt.axis('equal')
plt.grid()
plt.show()
plt.close()
+
diff --git a/examples/diapason/pod.py b/examples/diapason/pod.py
index 25a2dad..2eb8572 100644
--- a/examples/diapason/pod.py
+++ b/examples/diapason/pod.py
@@ -1,180 +1,181 @@
import numpy as np
import fenics as fen
import ufl
from rrompy.hfengines.vector_linear_problem import \
LinearElasticityHelmholtzProblemEngine as LEHPE
from rrompy.hfengines.vector_linear_problem import \
LinearElasticityHelmholtzProblemEngineDamped as LEHPED
from rrompy.reduction_methods.lagrange import ApproximantLagrangePade as Pade
from rrompy.reduction_methods.lagrange import ApproximantLagrangeRB as RB
from rrompy.utilities.parameter_sampling import QuadratureSampler as QS
-verb = 2
+verb = 100
sol = "single"
-sol = "sweep"
+#sol = "sweep"
algo = "Pade"
#algo = "RB"
polyBasis = "LEGENDRE"
polyBasis = "CHEBYSHEV"
#polyBasis = "MONOMIAL"
dampingEta = 0 * 1e4 / 2. / np.pi
-ktar = 4e2 # [Hz]
+ktar = 1.e4 # [Hz]
k0s = np.array([2.5e2, 1.0e4])
-#k0s = np.array([2.5e3, 1.5e4])
+k0s = np.array([2.5e3, 1.5e4])
#k0s = np.array([5.0e4, 1.0e5])
#k0s = np.array([2.0e5, 3.0e5])
k0 = np.mean(np.power(k0s, 2.)) ** .5
###
if dampingEta > 0:
rescaling = lambda x: x
rescalingInv = lambda x: x
else:
rescaling = lambda x: np.power(x, 2.)
rescalingInv = lambda x: np.power(x, .5)
-params = {'N':15, 'M':14, 'S':25, 'POD':True, 'basis':polyBasis,
- 'sampler':QS(k0s, "CHEBYSHEV", rescaling, rescalingInv)}
+params = {'N':15, 'M':14, 'S':25, 'POD':True, 'polybasis':polyBasis}
theta = 20. * np.pi / 180.
phi = 10. * np.pi / 180.
-mesh = fen.Mesh("./diapason_1.xml")
+mesh = fen.Mesh("../data/mesh/diapason_1.xml")
subdomains = fen.MeshFunction("size_t", mesh,
- "./diapason_1_physical_region.xml")
+ "../data/mesh/diapason_1_physical_region.xml")
meshBall = fen.SubMesh(mesh, subdomains, 2)
meshFork = fen.SubMesh(mesh, subdomains, 1)
Hball = np.max(meshBall.coordinates()[:, 1]) #.00257
Ltot = np.max(mesh.coordinates()[:, 1]) #.1022
Lhandle = np.max(meshFork.coordinates()[:, 1]) #.026
Lrod = Ltot - Lhandle #.0762
L2var = (Lrod / 4.) ** 2.
Ehandle_ratio = 3.
rhohandle_ratio = 1.5
c = 3.e2
rho = 8e3 * (2. * np.pi) ** 2.
E = 1.93e11
nu = .3
T = 1e6
lambda_ = E * nu / (1. + nu) / (1. - 2. * nu)
mu_ = E / (1. + nu)
kWave = (np.cos(theta) * np.cos(phi), np.sin(phi), np.sin(theta) * np.cos(phi))
x, y, z = fen.SpatialCoordinate(mesh)[:]
yCorr = y - Ltot
compPlane = kWave[0] * x + kWave[1] * y + kWave[2] * z
xPlane, yPlane, zPlane = (xx - compPlane * xx for xx in (x, y, z))
xOrtho, yOrtho, zOrtho = (compPlane * xx for xx in (x, y, z))
forcingBase = (T / (2. * np.pi * L2var)**.5
* fen.exp(- (xPlane**2. + yPlane**2. + zPlane**2.) / (2.*L2var)))
forcingWeight = np.real(k0) / c * (xOrtho + yOrtho + zOrtho)
neumannDatum = [ufl.as_vector(
tuple(forcingBase * fen.cos(forcingWeight) * kWavedir for kWavedir in kWave)),
ufl.as_vector(
tuple(forcingBase * fen.sin(forcingWeight) * kWavedir for kWavedir in kWave))]
lambda_eff = ufl.conditional(ufl.ge(y, Lhandle), fen.Constant(lambda_),
fen.Constant(Ehandle_ratio * lambda_))
mu_eff = ufl.conditional(ufl.ge(y, Lhandle), fen.Constant(mu_),
fen.Constant(Ehandle_ratio * mu_))
rho_eff = ufl.conditional(ufl.ge(y, Lhandle), fen.Constant(rho),
fen.Constant(rhohandle_ratio * rho))
###
if dampingEta > 0:
solver = LEHPED(degree_threshold = 8, verbosity = 0)
solver.eta = dampingEta
else:
solver = LEHPE(degree_threshold = 8, verbosity = 0)
solver.omega = np.real(k0)
solver.lambda_ = lambda_eff
solver.mu_ = mu_eff
solver.rho_ = rho_eff
solver.V = fen.VectorFunctionSpace(mesh, "P", 1)
solver.DirichletBoundary = lambda x, on_b: on_b and x[1] < Hball
solver.NeumannBoundary = "REST"
solver.forcingTerm = neumannDatum
if algo == "Pade":
approx = Pade(solver, mu0 = k0, approxParameters = params,
verbosity = verb)
else:
approx = RB(solver, mu0 = k0, approxParameters = params,
verbosity = verb)
+approx.sampler = QS(k0s, "CHEBYSHEV", rescaling, rescalingInv)
approx.setupApprox()
if sol == "single":
approx.outParaviewTimeDomainSamples(
- filename = "out/outSamples{}_".format(dampingEta),
- forceNewFile = False)
+ filename = "out/outSamples{}".format(dampingEta),
+ forceNewFile = False, folders = True)
+ nameBase = "{}_{}".format(ktar, dampingEta)
approx.outParaviewTimeDomainApprox(ktar, omega = 2. * np.pi * ktar,
- filename = "out/outTApp{}_{}_".format(ktar, dampingEta),
- forceNewFile = False)
+ filename = "out/outTApp{}".format(nameBase),
+ forceNewFile = False, folder = True)
approx.outParaviewTimeDomainHF(ktar, omega = 2. * np.pi * ktar,
- filename = "out/outTHF{}_{}_".format(ktar, dampingEta),
- forceNewFile = False)
+ filename = "out/outTHF{}".format(nameBase),
+ forceNewFile = False, folder = True)
approx.outParaviewTimeDomainErr(ktar, omega = 2. * np.pi * ktar,
- filename = "out/outTErr{}_{}_".format(ktar, dampingEta),
- forceNewFile = False)
+ filename = "out/outTErr{}".format(nameBase),
+ forceNewFile = False, folder = True)
approx.outParaviewTimeDomainRes(ktar, omega = 2. * np.pi * ktar,
- filename = "out/outTRes{}_{}_".format(ktar, dampingEta),
- forceNewFile = False)
+ filename = "out/outTRes{}".format(nameBase),
+ forceNewFile = False, folder = True)
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:')
print(approx.getPoles())
if sol == "sweep":
k0s = np.linspace(k0s[0], k0s[1], 100)
kl, kr = min(k0s), max(k0s)
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)
err = np.zeros_like(normApp)
for j in range(len(k0s)):
normApp[j] = approx.normApprox(k0s[j])
norm[j] = approx.normHF(k0s[j])
err[j] = approx.normErr(k0s[j]) / norm[j]
plt.figure()
plt.semilogy(k0s, norm)
plt.semilogy(k0s, normApp, '--')
plt.semilogy(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, 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/diapason/solver.py b/examples/diapason/solver.py
index d77570c..10c0826 100644
--- a/examples/diapason/solver.py
+++ b/examples/diapason/solver.py
@@ -1,81 +1,81 @@
import numpy as np
import fenics as fen
import ufl
from rrompy.hfengines.vector_linear_problem import \
LinearElasticityHelmholtzProblemEngine as LEHPE
from rrompy.hfengines.vector_linear_problem import \
LinearElasticityHelmholtzProblemEngineDamped as LEHPED
verb = 2
dampingEta = 0 * 1e4 / 2. / np.pi
k = 7773.051993943557 # [Hz]
theta = 20. * np.pi / 180.
phi = 10. * np.pi / 180.
-mesh = fen.Mesh("./diapason_1.xml")
+mesh = fen.Mesh("../data/mesh/diapason_1.xml")
subdomains = fen.MeshFunction("size_t", mesh,
- "./diapason_1_physical_region.xml")
+ "../data/mesh/diapason_1_physical_region.xml")
meshBall = fen.SubMesh(mesh, subdomains, 2)
meshFork = fen.SubMesh(mesh, subdomains, 1)
Hball = np.max(meshBall.coordinates()[:, 1]) #.00257
Ltot = np.max(mesh.coordinates()[:, 1]) #.1022
Lhandle = np.max(meshFork.coordinates()[:, 1]) #.026
Lrod = Ltot - Lhandle #.0762
L2var = (Lrod / 4.) ** 2.
Ehandle_ratio = 3.
rhohandle_ratio = 1.5
c = 3.e2
rho = 8e3 * (2. * np.pi) ** 2.
E = 1.93e11
nu = .3
T = 1e6
lambda_ = E * nu / (1. + nu) / (1. - 2. * nu)
mu_ = E / (1. + nu)
kWave = (np.cos(theta) * np.cos(phi), np.sin(phi), np.sin(theta) * np.cos(phi))
x, y, z = fen.SpatialCoordinate(mesh)[:]
yCorr = y - Ltot
compPlane = kWave[0] * x + kWave[1] * y + kWave[2] * z
xPlane, yPlane, zPlane = (xx - compPlane * xx for xx in (x, y, z))
xOrtho, yOrtho, zOrtho = (compPlane * xx for xx in (x, y, z))
forcingBase = (T / (2. * np.pi * L2var)**.5
* fen.exp(- (xPlane**2. + yPlane**2. + zPlane**2.) / (2.*L2var)))
forcingWeight = np.real(k) / c * (xOrtho + yOrtho + zOrtho)
neumannDatum = [ufl.as_vector(
tuple(forcingBase * fen.cos(forcingWeight) * kWavedir for kWavedir in kWave)),
ufl.as_vector(
tuple(forcingBase * fen.sin(forcingWeight) * kWavedir for kWavedir in kWave))]
lambda_eff = ufl.conditional(ufl.ge(y, Lhandle), fen.Constant(lambda_),
fen.Constant(Ehandle_ratio * lambda_))
mu_eff = ufl.conditional(ufl.ge(y, Lhandle), fen.Constant(mu_),
fen.Constant(Ehandle_ratio * mu_))
rho_eff = ufl.conditional(ufl.ge(y, Lhandle), fen.Constant(rho),
fen.Constant(rhohandle_ratio * rho))
###
if dampingEta > 0:
solver = LEHPED(degree_threshold = 8, verbosity = verb)
solver.eta = dampingEta
else:
solver = LEHPE(degree_threshold = 8, verbosity = verb)
solver.omega = np.real(k)
solver.lambda_ = lambda_eff
solver.mu_ = mu_eff
solver.rho_ = rho_eff
solver.V = fen.VectorFunctionSpace(mesh, "P", 1)
solver.DirichletBoundary = lambda x, on_b: on_b and x[1] < Hball
solver.NeumannBoundary = "REST"
solver.forcingTerm = neumannDatum
uh = solver.solve(k)
solver.outParaviewTimeDomain(uh, omega = 2. * np.pi * k,
filename = "out/outT{}_{}_".format(k, dampingEta),
forceNewFile = False)
diff --git a/examples/greedy/squareScatteringHomog.py b/examples/elasticity (arch) greedy.py
similarity index 80%
copy from examples/greedy/squareScatteringHomog.py
copy to examples/elasticity (arch) greedy.py
index fb483d0..1e8c0ec 100644
--- a/examples/greedy/squareScatteringHomog.py
+++ b/examples/elasticity (arch) greedy.py
@@ -1,97 +1,102 @@
import numpy as np
-from rrompy.hfengines.linear_problem import \
- HelmholtzCavityScatteringProblemEngine as HCSPE
+from rrompy.hfengines.vector_linear_problem import \
+ LinearElasticityHelmholtzArchwayFrequency as LEHAF
from rrompy.reduction_methods.lagrange_greedy import \
ApproximantLagrangePadeGreedy as Pade
from rrompy.reduction_methods.lagrange_greedy import \
ApproximantLagrangeRBGreedy as RB
verb = 2
timed = True
algo = "Pade"
#algo = "RB"
polyBasis = "LEGENDRE"
#polyBasis = "CHEBYSHEV"
#polyBasis = "MONOMIAL"
errorEstimatorKind = "SIMPLIFIED"
errorEstimatorKind = "EXACT"
-k0s = np.linspace(5, 12, 100)
+k0s = np.power(np.linspace(1e5, 3e5, 150), .5)
k0 = np.mean(k0s)
kl, kr = min(k0s), max(k0s)
params = {'muBounds':[kl, kr], 'nTrainingPoints':500, 'Delta':0,
'greedyTol':1e-2, 'nTestPoints':2, 'basis':polyBasis,
'errorEstimatorKind':errorEstimatorKind}
if timed:
verb = 0
-solver = HCSPE(kappa = 5, n = 10, verbosity = verb)
+E = 1e6
+nu = .47
+lambda_ = E * nu / (1. + nu) / (1. - 2. * nu)
+mu_ = E / (1. + nu)
+solver = LEHAF(kappa = k0, n = 200, rho_ = 1.5e3, T = 1e4, lambda_ = lambda_,
+ mu_ = mu_, R = 1., r = .85, verbosity = verb)
solver.omega = np.real(k0)
if algo == "Pade":
approx = Pade(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.normApprox(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.semilogy(k0s, norm)
+plt.semilogy(k0s, normApp, '--')
+plt.semilogy(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/elasticity (arch).py b/examples/elasticity (arch).py
new file mode 100644
index 0000000..399bddc
--- /dev/null
+++ b/examples/elasticity (arch).py
@@ -0,0 +1,37 @@
+import numpy as np
+from rrompy.hfengines.vector_linear_problem import \
+ LinearElasticityHelmholtzArchwayFrequency as LEHAF
+import os
+
+if not os.path.isdir("./ArchOut"):
+ os.makedirs("./ArchOut")
+
+scaling = 1.
+
+omegas = np.linspace(17, 18.8, 10)
+omegas = [18.8]
+for j in range(len(omegas)):
+ omega = omegas[j]
+
+ R = 1.
+ r = .85
+ T = 1e4 / scaling
+ E = 1e6
+ nu = .47
+ rho = 1.5e3
+ lambda_ = E * nu / (1. + nu) / (1. - 2. * nu)
+ mu_ = E / (1. + nu)
+ n = 50
+
+ # Create mesh and define function space
+ solver = LEHAF(kappa = omega, n = n, rho_ = rho, T = T, lambda_ = lambda_,
+ mu_ = mu_, R = R, r = r)
+
+ uh = solver.solve(omega)
+ solver.plot(uh, "u_{}".format(omega), what = [], figsize = (10, 5))
+# if j == 0:
+# fPW = solver.outParaview(uh, filename = "ArchOut/Arch", what = 'REAL',
+# time = omega, forceNewFile = False)
+# else:
+# fPW = solver.outParaview(uh, filePW = fPW, what = 'REAL', time = omega)
+solver.outParaviewTimeDomain(uh, omega, 3., filename = "ArchOut/ArchTime")
\ No newline at end of file
diff --git a/examples/elasticity (beam).py b/examples/elasticity (beam).py
new file mode 100644
index 0000000..cc81bdb
--- /dev/null
+++ b/examples/elasticity (beam).py
@@ -0,0 +1,20 @@
+from rrompy.hfengines.vector_linear_problem import \
+ LinearElasticityBeamPoissonRatio as LEBPR
+
+# Scaled variables
+scaling = 10
+
+L = 5.
+g = 9.8 / scaling
+E = 1e6
+nu = .47
+rho = 1.5e3
+lambda_ = E * nu / (1. + nu) / (1. - 2. * nu)
+mu_ = E / (1. + nu)
+n = 50
+
+# Create mesh and define function space
+solver = LEBPR(n = n, rho_ = rho, g = g, E = E, nu0 = nu, length = L)
+
+uh = solver.solve(nu)
+solver.plot(uh, what = ["REAL"], figsize = (6, 3))
diff --git a/examples/from_papers/pod_scatteringAirfoil.py b/examples/from_papers/pod_scatteringAirfoil.py
index 8162eb3..9696ce5 100644
--- a/examples/from_papers/pod_scatteringAirfoil.py
+++ b/examples/from_papers/pod_scatteringAirfoil.py
@@ -1,213 +1,215 @@
from copy import deepcopy as copy
import numpy as np
import fenics as fen
import ufl
from rrompy.hfengines.linear_problem 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, 'basis':"CHEBYSHEV",
- 'sampler':QS([kLeft, kRight], "CHEBYSHEV")}
- parPade = subdict(params, ['N', 'M', 'S', 'POD', 'sampler', 'basis'])
- parRB = subdict(params, ['R', 'S', 'POD', 'sampler'])
+ params = {'N':8, 'M':8, 'R':9, 'S':9, 'POD':True, 'basis':"CHEBYSHEV"}
+ parPade = subdict(params, ['N', 'M', 'S', 'POD', 'basis'])
+ parRB = subdict(params, ['R', 'S', 'POD'])
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.sampler = QS([kLeft, kRight], "CHEBYSHEV")
+ approxRB.sampler = QS([kLeft, kRight], "CHEBYSHEV")
approxPade.setupApprox()
approxRB.setupApprox()
if plotSamples:
approxPade.plotSamples()
approxPade.plotHF(ktar, name = 'u_HF')
approxPade.plotApprox(ktar, name = 'u_Pade''')
approxPade.plotErr(ktar, name = 'err_Pade''')
approxPade.plotRes(ktar, name = 'res_Pade''')
approxRB.plotApprox(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, 'basis':"CHEBYSHEV",
- 'sampler':QS([kLeft, kRight], "CHEBYSHEV")}
+ paramsPade = {'S':Smax, 'POD':True, 'basis':"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)
+ approxPade.sampler = QS([kLeft, kRight], "CHEBYSHEV")
approxRB = LRB(solver, mu0 = np.mean([kLeft, kRight]),
approxParameters = paramsRB, verbosity = verb,
homogeneized = homog)
+ approxRB.sampler = QS([kLeft, kRight], "CHEBYSHEV")
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/greedy/squareBubbleHomog.py b/examples/greedy/squareBubbleHomog.py
index 1177e8d..52e8a6d 100644
--- a/examples/greedy/squareBubbleHomog.py
+++ b/examples/greedy/squareBubbleHomog.py
@@ -1,109 +1,110 @@
import numpy as np
from rrompy.hfengines.linear_problem import \
HelmholtzSquareBubbleProblemEngine as HSBPE
from rrompy.reduction_methods.lagrange_greedy import \
ApproximantLagrangePadeGreedy as Pade
from rrompy.reduction_methods.lagrange_greedy import \
ApproximantLagrangeRBGreedy as RB
from rrompy.utilities.base import squareResonances
-verb = 2
-timed = True
+verb = 20
+timed = False
algo = "Pade"
-#algo = "RB"
+algo = "RB"
polyBasis = "LEGENDRE"
#polyBasis = "CHEBYSHEV"
#polyBasis = "MONOMIAL"
errorEstimatorKind = "SIMPLIFIED"
errorEstimatorKind = "EXACT"
-k0s = np.power(np.linspace(95, 149, 500), .5)
-k0s = np.power(np.linspace(95, 129, 20), .5)
+k0s = np.power(np.linspace(95, 149, 250), .5)
+#k0s = np.power(np.linspace(95, 129, 100), .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))
params = {'muBounds':[kl, kr], 'nTrainingPoints':500, 'Delta':0,
- 'greedyTol':1e-2, 'nTestPoints':2, 'basis':polyBasis,
+ 'greedyTol':1e-2, 'nTestPoints':2, 'polybasis':polyBasis,
'errorEstimatorKind':errorEstimatorKind}
if timed:
verb = 0
-solver = HSBPE(kappa = 12 ** .5, theta = np.pi / 3, n = 30,
+solver = HSBPE(kappa = 12 ** .5, theta = np.pi / 3, n = 20,
verbosity = verb)
solver.omega = np.real(k0)
if algo == "Pade":
approx = Pade(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.trainedModel.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.normApprox(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/squareScatteringHomog.py b/examples/greedy/squareScatteringHomog.py
index fb483d0..89d2db6 100644
--- a/examples/greedy/squareScatteringHomog.py
+++ b/examples/greedy/squareScatteringHomog.py
@@ -1,97 +1,101 @@
import numpy as np
from rrompy.hfengines.linear_problem import \
HelmholtzCavityScatteringProblemEngine as HCSPE
from rrompy.reduction_methods.lagrange_greedy import \
ApproximantLagrangePadeGreedy as Pade
from rrompy.reduction_methods.lagrange_greedy import \
ApproximantLagrangeRBGreedy as RB
-verb = 2
-timed = True
+verb = 20
+timed = False
algo = "Pade"
-#algo = "RB"
+algo = "RB"
polyBasis = "LEGENDRE"
-#polyBasis = "CHEBYSHEV"
+polyBasis = "CHEBYSHEV"
#polyBasis = "MONOMIAL"
errorEstimatorKind = "SIMPLIFIED"
errorEstimatorKind = "EXACT"
-k0s = np.linspace(5, 12, 100)
+k0s = np.linspace(10, 17, 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':polyBasis,
+ 'greedyTol':1e-2, 'nTestPoints':2, 'polybasis':polyBasis,
'errorEstimatorKind':errorEstimatorKind}
if timed:
verb = 0
-solver = HCSPE(kappa = 5, n = 10, verbosity = verb)
+solver = HCSPE(kappa = 5, n = 20, verbosity = verb)
solver.omega = np.real(k0)
if algo == "Pade":
approx = Pade(solver, mu0 = k0, approxParameters = params,
verbosity = verb)
else:
+ params.pop('Delta')
+ params.pop('polybasis')
+ params.pop('errorEstimatorKind')
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.trainedModel.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.normApprox(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/helmholtz_resonator.py b/examples/helmholtz_resonator.py
new file mode 100644
index 0000000..a1a7a80
--- /dev/null
+++ b/examples/helmholtz_resonator.py
@@ -0,0 +1,72 @@
+from matplotlib import pyplot as plt
+import fenics as fen
+import mshr
+import ufl
+from rrompy.hfengines.linear_problem import HelmholtzProblemEngine as HPE
+
+p = plt.jet()
+
+n = 50
+
+boundary = mshr.Polygon([fen.Point(0, 0), fen.Point(6, 0), fen.Point(6, 1),
+ fen.Point(1.3, 1), fen.Point(1.3, 1.2),
+ fen.Point(1.65, 1.2), fen.Point(1.65, 2.2),
+ fen.Point(.65, 2.2), fen.Point(.65, 1.2),
+ fen.Point(1, 1.2), fen.Point(1, 1), fen.Point(0, 1)])
+mesh = mshr.generate_mesh(boundary, n)
+
+class Resonator(fen.SubDomain):
+ def inside(self, x, on_boundary):
+ return fen.between(x[1], (1.25, 2.25))
+class Noslip(fen.SubDomain):
+ def inside(self, x, on_boundary):
+ return on_boundary
+class Inlet(fen.SubDomain):
+ def inside(self, x, on_boundary):
+ return on_boundary and fen.near(x[0], 0.)
+class Outlet(fen.SubDomain):
+ def inside(self, x, on_boundary):
+ return on_boundary and fen.near(x[0], 6.)
+class Liner(fen.SubDomain):
+ def inside(self, x, on_boundary):
+ return on_boundary and fen.near(x[1], 2.25)
+resonator = Resonator()
+noslip = Noslip()
+inlet = Inlet()
+outlet = Outlet()
+liner = Liner()
+
+sub_domains = fen.MeshFunction("size_t", mesh, mesh.topology().dim())
+sub_domains.set_all(0)
+resonator.mark(sub_domains, 1)
+
+boundaries = fen.MeshFunction("size_t", mesh, mesh.topology().dim() - 1)
+noslip.mark(boundaries, 0)
+inlet.mark(boundaries, 1)
+outlet.mark(boundaries, 2)
+liner.mark(boundaries, 3)
+
+for k in range(10):
+ kappa = .25 + .05 * k
+ ZR = 10.
+ x, y = fen.SpatialCoordinate(mesh)[:]
+
+ solver = HPE()
+ solver.V = fen.FunctionSpace(mesh, "P", 3)
+ solver.omega = kappa
+ solver.RobinBoundary = lambda x, on_b: on_b and (fen.near(x[0], 6.)
+ or fen.near(x[1], 2.25))
+ solver.NeumannBoundary = "REST"
+ solver.signR = + 1.
+ solver.NeumannDatum = [fen.Constant(0.),
+ ufl.conditional(ufl.And(ufl.gt(y, 0.), ufl.lt(y, 1.)),
+ fen.Constant(kappa), fen.Constant(0.))]
+ solver.RobinDatumH = [fen.Constant(0.),
+ ufl.conditional(ufl.gt(y, 1.25),
+ fen.Constant(kappa / ZR),
+ fen.Constant(kappa))]
+
+ uh = solver.solve(kappa)
+ solver.plot(uh, name = "k={}".format(kappa))
+ print(solver.norm(uh))
+
\ No newline at end of file
diff --git a/examples/mixed_poisson.py b/examples/mixed_poisson.py
new file mode 100644
index 0000000..42da58b
--- /dev/null
+++ b/examples/mixed_poisson.py
@@ -0,0 +1,32 @@
+from matplotlib import pyplot as plt
+plt.jet()
+import fenics as fen
+
+# Create mesh and define function space
+mesh = fen.UnitSquareMesh(10, 10)
+CG1 = fen.FiniteElement("P", fen.triangle, 1)
+R0 = fen.FiniteElement("R", fen.triangle, 0)
+element = fen.MixedElement([CG1, R0])
+W = fen.FunctionSpace(mesh, element)
+
+# Define variational problem
+(u, c) = fen.TrialFunction(W)
+(v, d) = fen.TestFunctions(W)
+f = fen.Expression("10*exp(-(pow(x[0] - 0.5, 2) + pow(x[1] - 0.5, 2)) / 0.02)",
+ element = CG1)
+g = fen.Expression("-sin(5*x[0])", element = CG1)
+a = (fen.inner(fen.grad(u), fen.grad(v)) + c*v + u*d)*fen.dx
+L = f*v*fen.dx + g*v*fen.ds
+
+# Compute solution
+w = fen.Function(W)
+fen.solve(a == L, w)
+(u, c) = w.split()
+
+# Plot solution
+plt.figure()
+p = fen.plot(u)
+plt.colorbar(p)
+plt.show(), plt.close()
+
+
diff --git a/examples/pod/LagrangePoles.py b/examples/pod/LagrangePoles.py
index b3af094..799c3cc 100644
--- a/examples/pod/LagrangePoles.py
+++ b/examples/pod/LagrangePoles.py
@@ -1,47 +1,47 @@
from matplotlib import pyplot as plt
import numpy as np
from rrompy.hfengines.linear_problem 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
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)
-sampler = QS(ks, "UNIFORM", rescaling, rescalingInv)
nsets = 15
-paramsPade = {'S':2, 'POD':True, 'basis':"LEGENDRE", 'sampler':sampler}
+paramsPade = {'S':2, 'POD':True, 'basis':"LEGENDRE"}
approx = Pade(solver, mu0 = k0, approxParameters = paramsPade,
verbosity = verb)
+approx.sampler = QS(ks, "UNIFORM", rescaling, rescalingInv)
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
+ plt.close()
diff --git a/examples/pod/LagrangeSweep.py b/examples/pod/LagrangeSweep.py
index 9025425..43b6c6c 100644
--- a/examples/pod/LagrangeSweep.py
+++ b/examples/pod/LagrangeSweep.py
@@ -1,108 +1,110 @@
from copy import copy
import numpy as np
#from rrompy.hfengines.linear_problem import \
# HelmholtzBoxScatteringProblemEngine as HBSPE
from rrompy.hfengines.linear_problem import \
- HelmholtzCavityScatteringProblemEngine as HBSPE
+ HelmholtzCavityScatteringProblemEngine as HCSPE
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 = "Cheby"
#sampling = "SinC"
assert LSratio <= 1. + np.finfo(float).eps
ks = [10 + 0.j, 14 + 0.j]
-solver = HBSPE(kappa = 3, n = 15)
+solver = HCSPE(kappa = 5, n = 20)
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":
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, 'basis':polyBasis, 'sampler':sampler}
+paramsPade = {'S':Smax, 'POD':True, 'polybasis':polyBasis}
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)
+appPade.sampler = sampler
appRB = RB(solver, mu0 = k0, approxParameters = paramsRB,
verbosity = verb, homogeneized = homog)
+appRB.sampler = sampler
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,
+ ['normHF', 'normApprox'], ['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 e05297e..cb2379a 100644
--- a/examples/pod/LagrangeSweepUnstable.py
+++ b/examples/pod/LagrangeSweepUnstable.py
@@ -1,76 +1,79 @@
from copy import copy
import numpy as np
from rrompy.hfengines.linear_problem 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, 'basis':"MONOMIAL",
'sampler':MS([6**.5, 16**.5], [[14**.5], [12**.5], [9**.5], [11**.5]])},
'Unstable':
{'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]
+ sampler = paramsPade.pop("sampler")
paramsRB = copy(paramsPade)
paramsSetsPade = [{'N': 3, 'M': 3}]
paramsSetsRB = [{'R': 4}]
appPade = Pade(solver, mu0 = k0, approxParameters = paramsPade,
verbosity = 0)
+ appPade.sampler = sampler
appRB = RB(solver, mu0 = k0, approxParameters = paramsRB,
verbosity = 0)
+ appRB.sampler = sampler
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 40c228d..03c5992 100644
--- a/examples/pod/PadeLagrange.py
+++ b/examples/pod/PadeLagrange.py
@@ -1,113 +1,122 @@
import numpy as np
from rrompy.hfengines.linear_problem import \
HelmholtzSquareBubbleProblemEngine as HSBPE
from rrompy.hfengines.linear_problem import \
HelmholtzSquareTransmissionProblemEngine as HSTPE
from rrompy.hfengines.linear_problem import \
HelmholtzBoxScatteringProblemEngine as HBSPE
from rrompy.reduction_methods.lagrange import ApproximantLagrangePade as Pade
from rrompy.utilities.parameter_sampling import QuadratureSampler as QS
-testNo = 1
-verb = 5
+testNo = -1
+verb = 100
polyBasis = "CHEBYSHEV"
polyBasis = "LEGENDRE"
#polyBasis = "MONOMIAL"
homog = True
-homog = False
+#homog = False
+loadName = "PadeLagrangeModel.pkl"
-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, 'basis':polyBasis,
- 'sampler':QS(k0s, "CHEBYSHEV", rescaling, rescalingInv)}
+if testNo in [1, -1]:
+ if testNo > 0:
+ k0s = np.power([10 + 0.j, 14 + 0.j], .5)
+ k0 = np.mean(np.power(k0s, 2.)) ** .5
+ rescaling = lambda x: np.power(x, 2.)
+ rescalingInv = lambda x: np.power(x, .5)
+ params = {'N':4, 'M':3, 'S':5, 'POD':True, 'polybasis':polyBasis}
+ ktar = (11 + .5j) ** .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()
+ if testNo > 0:
+ solver.omega = np.real(k0)
+ approx = Pade(solver, mu0 = k0, approxParameters = params,
+ verbosity = verb)
+ approx.sampler = QS(k0s, "CHEBYSHEV", rescaling, rescalingInv)
+ approx.setupApprox()
+ # approx.plotSamples()
+ else:
+ approx = Pade(solver, mu0 = 0, verbosity = verb)
+ approx.loadTrainedModel(loadName)
+
approx.plotApprox(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())
+ if testNo > 0:
+ approx.storeTrainedModel("PadeLagrangeModel", forceNewFile = False)
+ print(approx.trainedModel.data.__dict__)
+
############
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, 'basis':polyBasis,
- 'sampler':QS(k0s, "CHEBYSHEV", rescaling, rescalingInv)}
+ params = {'N':8, 'M':9, 'S':10, 'POD':True, 'polybasis':polyBasis},
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.sampler = QS(k0s, "CHEBYSHEV", rescaling, rescalingInv)
approx.setupApprox()
# approx.plotSamples()
approx.plotApprox(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, 'basis':polyBasis,
- 'sampler':QS(k0s, "CHEBYSHEV")}
+ params = {'N':10, 'M':10, 'S':11, 'POD':True, 'polybasis':polyBasis}
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.sampler = QS(k0s, "CHEBYSHEV")
approx.setupApprox()
- approx.plotSamples()
+# approx.plotSamples()
approx.plotApprox(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 5e4ffdc..1e23e98 100644
--- a/examples/pod/PadeTaylor.py
+++ b/examples/pod/PadeTaylor.py
@@ -1,99 +1,108 @@
import numpy as np
from rrompy.hfengines.linear_problem import \
HelmholtzSquareBubbleProblemEngine as HSBPE
from rrompy.hfengines.linear_problem import \
HelmholtzSquareTransmissionProblemEngine as HSTPE
from rrompy.hfengines.linear_problem import \
HelmholtzBoxScatteringProblemEngine as HBSPE
from rrompy.reduction_methods.taylor import ApproximantTaylorPade as Pade
-testNo = 1
-verb = 5
+testNo = -1
+verb = 100
homog = True
#homog = False
+loadName = "PadeTaylorModel.pkl"
-if testNo == 1:
- params = {'N':4, 'M':3, 'E':4, 'sampleType':'Arnoldi', 'POD':True}
- k0 = 12 ** .5
+if testNo in [1, -1]:
+ if testNo > 0:
+ 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)
+ if testNo > 0:
+ approx = Pade(solver, mu0 = k0, approxParameters = params,
+ verbosity = verb)
+ approx.setupApprox()
+ # approx.plotSamples()
+ else:
+ approx = Pade(solver, mu0 = 0, verbosity = verb)
+ approx.loadTrainedModel(loadName)
- approx.setupApprox()
-# approx.plotSamples()
approx.plotApprox(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())
+ if testNo > 0:
+ approx.storeTrainedModel("PadeTaylorModel", forceNewFile = False)
+ print(approx.trainedModel.data.__dict__)
+
############
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.plotApprox(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.plotApprox(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/RBLagrange.py b/examples/pod/RBLagrange.py
index b8bf6fd..b684858 100644
--- a/examples/pod/RBLagrange.py
+++ b/examples/pod/RBLagrange.py
@@ -1,102 +1,111 @@
import numpy as np
from rrompy.hfengines.linear_problem import \
HelmholtzSquareBubbleProblemEngine as HSBPE
from rrompy.hfengines.linear_problem import \
HelmholtzSquareTransmissionProblemEngine as HSTPE
from rrompy.hfengines.linear_problem import \
HelmholtzBoxScatteringProblemEngine as HBSPE
from rrompy.reduction_methods.lagrange import ApproximantLagrangeRB as RB
from rrompy.utilities.parameter_sampling import QuadratureSampler as QS
-testNo = 3
-verb = 0
+testNo = -1
+verb = 100
homog = True
-homog = False
+#homog = False
+loadName = "RBLagrangeModel.pkl"
-if testNo == 1:
- k0s = np.power([10 + 0.j, 14 + 0.j], .5)
- k0 = np.mean(k0s)
+if testNo in [1, -1]:
+ if testNo > 0:
+ k0s = np.power([10 + 0.j, 14 + 0.j], .5)
+ k0 = np.mean(np.power(k0s, 2.)) ** .5
+ rescaling = lambda x: np.power(x, 2.)
+ rescalingInv = lambda x: np.power(x, .5)
+ params = {'S':5, 'R':4, 'POD':True}
ktar = (11 + .5j) ** .5
-
- rescaling = lambda x: np.power(x, 2.)
- rescalingInv = lambda x: np.power(x, .5)
- params = {'S':5, 'R':4, '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 = RB(solver, mu0 = k0, approxParameters = params,
- verbosity = verb)
-
- approx.setupApprox()
-# approx.plotSamples()
+ if testNo > 0:
+ approx = RB(solver, mu0 = k0, approxParameters = params,
+ verbosity = verb)
+ approx.sampler = QS(k0s, "CHEBYSHEV", rescaling, rescalingInv)
+ approx.setupApprox()
+ # approx.plotSamples()
+ else:
+ approx = RB(solver, mu0 = 0, verbosity = verb)
+ approx.loadTrainedModel(loadName)
+
approx.plotApprox(ktar, name = 'u_RB')
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)))
+ if testNo > 0:
+ approx.storeTrainedModel("RBLagrangeModel", forceNewFile = False)
+ print(approx.trainedModel.data.__dict__)
+
############
elif testNo == 2:
k0s = [3.85 + 0.j, 4.15 + 0.j]
k0 = np.mean(k0s)
ktar = 4 + .15j
rescaling = lambda x: np.power(x, 2.)
rescalingInv = lambda x: np.power(x, .5)
- params = {'S':10, 'R':9, 'POD':True,
- 'sampler':QS(k0s, "CHEBYSHEV", rescaling, rescalingInv)}
+ params = {'S':10, 'R':9, 'POD':True}
solver = HSTPE(nT = 2, nB = 1, theta = np.pi * 45/180, kappa = 4., n = 50,
verbosity = verb)
solver.omega = np.real(k0)
approx = RB(solver, mu0 = k0, approxParameters = params,
verbosity = verb, homogeneized = homog)
-
+ approx.sampler = QS(k0s, "CHEBYSHEV", rescaling, rescalingInv)
+
approx.setupApprox()
# approx.plotSamples()
approx.plotApprox(ktar, name = 'u_RB')
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)))
############
elif testNo == 3:
k0s = [2, 5]
k0 = np.mean(k0s)
ktar = 4.5 - 0.j
- params = {'S':15, 'R':10, 'POD':True, 'sampler':QS(k0s, "CHEBYSHEV")}
+ params = {'S':15, 'R':10, 'POD':True}
solver = HBSPE(R = 7, kappa = 3, theta = - np.pi * 75 / 180, n = 40,
verbosity = verb)
solver.omega = np.real(k0)
approx = RB(solver, mu0 = k0, approxParameters = params,
verbosity = verb, homogeneized = homog)
+ approx.sampler = QS(k0s, "CHEBYSHEV")
approx.setupApprox()
# approx.plotSamples()
approx.plotApprox(ktar, name = 'u_RB')
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)))
diff --git a/examples/pod/RBTaylor.py b/examples/pod/RBTaylor.py
index 81353e9..0eb0019 100644
--- a/examples/pod/RBTaylor.py
+++ b/examples/pod/RBTaylor.py
@@ -1,93 +1,102 @@
import numpy as np
from rrompy.hfengines.linear_problem import \
HelmholtzSquareBubbleProblemEngine as HSBPE
from rrompy.hfengines.linear_problem import \
HelmholtzSquareTransmissionProblemEngine as HSTPE
from rrompy.hfengines.linear_problem import \
HelmholtzBoxScatteringProblemEngine as HBSPE
from rrompy.reduction_methods.taylor import ApproximantTaylorRB as RB
-testNo = 4
-verb = 0
+testNo = -1
+verb = 100
homog = True
#homog = False
+loadName = "RBTaylorModel.pkl"
-if testNo == 1:
- params = {'E':4, 'R':4, 'sampleType':'Arnoldi', 'POD':True}
- k0 = 12 ** .5
+if testNo in [1, -1]:
+ if testNo > 0:
+ params = {'E':4, 'R':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 = RB(solver, mu0 = k0, approxParameters = params,
- verbosity = verb)
-
- approx.setupApprox()
-# approx.plotSamples()
+ if testNo > 0:
+ approx = RB(solver, mu0 = k0, approxParameters = params,
+ verbosity = verb)
+ approx.setupApprox()
+ # approx.plotSamples()
+ else:
+ approx = RB(solver, mu0 = 0, verbosity = verb)
+ approx.loadTrainedModel(loadName)
+
approx.plotApprox(ktar, name = 'u_RB')
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)))
+ if testNo > 0:
+ approx.storeTrainedModel("RBTaylorModel", forceNewFile = False)
+ print(approx.trainedModel.data.__dict__)
+
############
elif testNo == 2:
params = {'E':7, 'R':7, 'sampleType':'Arnoldi', 'POD':True}
k0 = 16**.5
ktar = 15**.5
solver = HSTPE(nT = 2, nB = 1, theta = np.pi * 45/180, kappa = 3.,
n = 50, verbosity = verb)
solver.omega = np.real(k0)
approx = RB(solver, mu0 = k0, approxParameters = params,
verbosity = verb, homogeneized = homog)
approx.setupApprox()
# approx.plotSamples()
approx.plotApprox(ktar, name = 'u_RB')
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)))
############
elif testNo in [3, 4]:
if testNo == 3:
params = {'E':8, 'sampleType':'Krylov', 'POD':True}
else:
params = {'E':8, 'sampleType':'Arnoldi', 'POD':True}
k0 = 3
ktar = 4.25+.5j
solver = HBSPE(R = 5, kappa = 3, theta = - np.pi * 75 / 180,
n = 30, verbosity = verb)
solver.omega = np.real(k0)
approx = RB(solver, mu0 = k0, approxParameters = params,
verbosity = verb, homogeneized = homog)
approx.setupApprox()
# approx.plotSamples()
approx.plotApprox(ktar, name = 'u_RB')
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)))
diff --git a/examples/pod/scatteringSquare.py b/examples/pod/scatteringSquare.py
index 95b908f..cfc7ece 100644
--- a/examples/pod/scatteringSquare.py
+++ b/examples/pod/scatteringSquare.py
@@ -1,172 +1,172 @@
from copy import copy
import numpy as np
from rrompy.hfengines.linear_problem 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}
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, 'basis':"MONOMIAL",
- 'sampler':QS([kLeft, kRight], "CHEBYSHEV")}
- 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'])
+ params = {'N':8, 'M':8, 'R':9, 'S':9, 'POD':True, 'basis':"MONOMIAL"}
+ params = {'N':8, 'M':8, 'R':9, 'S':9, 'POD':True, 'basis':"CHEBYSHEV"}
+ parPade = subdict(params, ['N', 'M', 'S', 'POD', 'basis'])
+ parRB = subdict(params, ['R', 'S', 'POD'])
approxPade = LP(solver, mu0 = np.mean([kLeft, kRight]),
approxParameters = parPade,
verbosity = verb)
+ approxPade.sampler = QS([kLeft, kRight], "CHEBYSHEV")
approxRB = LRB(solver, mu0 = np.mean([kLeft, kRight]),
approxParameters = parRB,
verbosity = verb)
+ approxRB.sampler = QS([kLeft, kRight], "CHEBYSHEV")
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.plotApprox(ktar, name = 'u_Pade''')
approxRB.plotApprox(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, 'basis':"MONOMIAL",
- 'sampler':QS([kLeft, kRight], "CHEBYSHEV")}
- paramsPade = {'S':Smax, 'POD':True, 'basis':"CHEBYSHEV",
- 'sampler':QS([kLeft, kRight], "CHEBYSHEV")}
+ paramsPade = {'S':Smax, 'POD':True, 'basis':"MONOMIAL"}
+ paramsPade = {'S':Smax, 'POD':True, 'basis':"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)
+ approxPade.sampler = QS([kLeft, kRight], "CHEBYSHEV")
approxRB = LRB(solver, mu0 = np.mean([kLeft, kRight]),
approxParameters = paramsRB,
verbosity = verb)
+ approxRB.sampler = QS([kLeft, kRight], "CHEBYSHEV")
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 601422e..fffe262 100644
--- a/rrompy/hfengines/base/boundary_conditions.py
+++ b/rrompy/hfengines/base/boundary_conditions.py
@@ -1,125 +1,126 @@
# 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 fenics import SubDomain, AutoSubDomain
from rrompy.utilities.base.types import GenExpr
from rrompy.utilities.base.fenics import bdrFalse
+from rrompy.utilities.exception_manager import RROMPyException
__all__ = ['BoundaryConditions']
class BoundaryConditions:
"""
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.")
+ raise RROMPyException("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.")
+ raise RROMPyException("Wildcard not recognized.")
elif callable(value):
self._standardManagementCallable(kind, value)
elif isinstance(value, (SubDomain,)):
self._standardManagement(kind, value)
else:
- raise Exception(kind + "Boundary type not recognized.")
+ raise RROMPyException(kind + "Boundary type not recognized.")
def _complementaryManagementAll(self, kind:str):
if kind not in self.allowedKinds:
- raise Exception("BC kind not recognized.")
+ raise RROMPyException("BC kind not recognized.")
for k in self.allowedKinds:
if k != kind:
self._standardManagementCallable(k, bdrFalse)
self._complementaryManagementRest(kind)
def _complementaryManagementRest(self, kind:str):
if kind not in self.allowedKinds:
- raise Exception("BC kind not recognized.")
+ raise RROMPyException("BC kind not recognized.")
otherBCs = []
for k in self.allowedKinds:
if k != kind:
if hasattr(self, "_" + k + "Rest"):
self._standardManagement(k, bdrFalse)
otherBCs += [getattr(self, k + "Boundary")]
def restCall(x, on_boundary):
return (on_boundary
and not any([bc.inside(x, on_boundary) for bc in otherBCs]))
self._standardManagementCallable(kind, restCall)
super().__setattr__("_" + kind + "Rest", 1)
def _standardManagementCallable(self, kind:str, bc:callable):
bcSD = AutoSubDomain(bc)
self._standardManagement(kind, bcSD)
def _standardManagement(self, kind:str, bc:SubDomain):
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/problem_engine_base.py b/rrompy/hfengines/base/problem_engine_base.py
index 8fbc709..16c897d 100644
--- a/rrompy/hfengines/base/problem_engine_base.py
+++ b/rrompy/hfengines/base/problem_engine_base.py
@@ -1,451 +1,499 @@
# 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
+from os import path, mkdir
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, Np2D, ScOp, strLst, FenFunc,
Tuple, List)
from rrompy.utilities.base import purgeList, getNewFilename, verbosityDepth
from .boundary_conditions import BoundaryConditions
+from rrompy.utilities.exception_manager import RROMPyException
__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
+ rescalingExp = 1.
functional = lambda self, u: 0.
- def __init__(self, degree_threshold : int = np.inf, verbosity : int = 10):
+ def __init__(self, degree_threshold : int = np.inf, verbosity : int = 10,
+ timestamp : bool = True):
self.BCManager = BoundaryConditions("Dirichlet")
self.V = fen.FunctionSpace(fen.UnitSquareMesh(10, 10), "P", 1)
self.verbosity = verbosity
+ self.timestamp = timestamp
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.")
+ raise RROMPyException("V type not recognized.")
self._V = V
self.u = fen.TrialFunction(V)
self.v = fen.TestFunction(V)
def innerProduct(self, u:Np2D, v:Np2D, onlyDiag : bool = False) -> Np2D:
"""Hilbert space scalar product."""
if not hasattr(self, "energyNormMatrix"):
self.buildEnergyNormForm()
if onlyDiag:
return np.sum(self.energyNormMatrix.dot(u) * v.conj(), axis = 0)
- return v.conj().T.dot(self.energyNormMatrix.dot(u))
+ return v.T.conj().dot(self.energyNormMatrix.dot(u))
def buildEnergyNormForm(self): # L2
"""
Build sparse matrix (in CSR format) representative of scalar product.
"""
if self.verbosity >= 20:
- verbosityDepth("INIT", "Assembling energy matrix.", end = "")
+ verbosityDepth("INIT", "Assembling energy matrix.",
+ timestamp = self.timestamp)
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)
+ verbosityDepth("DEL", "Done assembling energy matrix.",
+ timestamp = self.timestamp)
def norm(self, u:Np2D) -> Np1D:
return np.abs(self.innerProduct(u, u, onlyDiag = True)) ** .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))
+ name),
+ timestamp = self.timestamp)
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.")
+ def affineLinearSystemA(self, mu : complex = 0.) -> List[Np2D]:
+ """
+ Assemble affine blocks of operator of linear system (just linear
+ blocks).
+ """
As = [None] * self.nAs
for j in range(self.nAs):
As[j] = self.A(mu, j)
- return As, lambdas
+ return As
- 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
+ def affineWeightsA(self, mu : complex = 0.) -> callable:
+ """
+ Assemble affine blocks of operator of linear system (just affine
+ weights). Stored as strings for the sake of pickling.
+ """
+ lambdasA = ["np.ones_like(mu)"]
+ mu0Eff = np.power(mu, self.rescalingExp)
+ for j in range(1, self.nAs):
+ lambdasA += ["np.power(np.power(mu, {1}) - {2}, {0})".format(j,
+ self.rescalingExp,
+ mu0Eff)]
+ return lambdasA
+
+ def affineBlocksA(self, mu : complex = 0.) -> Tuple[List[Np2D], callable]:
+ """Assemble affine blocks of operator of linear system."""
+ return self.affineLinearSystemA(mu), self.affineWeightsA(mu)
+
+ def setnbsEff(self, homogeneized : bool = False):
+ """Compute effective number of b terms."""
+ self.nbsEff = max(homogeneized * self.nAs, self.nbs)
+
+ def affineLinearSystemb(self, mu : complex = 0.,
+ homogeneized : bool = False) -> List[Np1D]:
+ """
+ Assemble affine blocks of RHS of linear system (just linear blocks).
+ """
+ self.setnbsEff(homogeneized)
bs = [None] * self.nbsEff
for j in range(self.nbsEff):
bs[j] = self.b(mu, j, homogeneized)
- return bs, lambdas
+ return bs
+
+ def affineWeightsb(self, mu : complex = 0., homogeneized : bool = False)\
+ -> callable:
+ """
+ Assemble affine blocks of RHS of linear system (just affine weights).
+ Stored as strings for the sake of pickling.
+ """
+ self.setnbsEff(homogeneized)
+ lambdasb = ["np.ones_like(mu)"]
+ mu0Eff = np.power(mu, self.rescalingExp)
+ for j in range(1, self.nbsEff):
+ lambdasb += ["np.power(np.power(mu, {1}) - {2}, {0})".format(j,
+ self.rescalingExp,
+ mu0Eff)]
+ return lambdasb
+
+ def affineBlocksb(self, mu : complex = 0., homogeneized : bool = False)\
+ -> Tuple[List[Np1D], callable]:
+ """Assemble affine blocks of RHS of linear system."""
+ return (self.affineLinearSystemb(mu, homogeneized),
+ self.affineWeightsb(mu, homogeneized))
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().set_local(np.abs(u))
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().set_local(np.angle(u))
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().set_local(np.real(u))
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().set_local(np.imag(u))
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()
def outParaview(self, u:Np1D, name : str = "u", filename : str = "out",
time : float = 0., what : strLst = 'all',
- forceNewFile : bool = True, filePW = None):
+ forceNewFile : bool = True, folder : bool = False,
+ filePW = None):
"""
Output complex-valued function with given dofs to ParaView file.
Args:
u: numpy complex array with function dofs.
name(optional): Base name to be used for data output.
filename(optional): Name of output file.
time(optional): Timestamp.
what(optional): Which plots to do. If list, can contain 'MESH',
'ABS', 'PHASE', 'REAL', 'IMAG'. If str, same plus wildcard
'ALL'. Defaults to 'ALL'.
forceNewFile(optional): Whether to create new output file.
+ folder(optional): Whether to create an additional folder layer.
filePW(optional): Fenics File entity (for time series).
"""
if isinstance(what, (str,)):
if what.upper() == 'ALL':
what = ['MESH', 'ABS', 'PHASE', 'REAL', 'IMAG']
else:
what = [what]
what = purgeList(what, ['MESH', 'ABS', 'PHASE', 'REAL', 'IMAG'],
listname = self.name() + ".what", baselevel = 1)
if len(what) == 0: return
- if not filePW:
+ if filePW is None:
+ if folder:
+ if not path.exists(filename + "/"):
+ mkdir(filename)
+ idxpath = filename.rfind("/")
+ filename += "/" + filename[idxpath + 1 :]
if forceNewFile:
filePW = fen.File(getNewFilename(filename, "pvd"))
else:
filePW = fen.File("{}.pvd".format(filename))
if what == ['MESH']:
filePW << (self.V.mesh(), time)
if 'ABS' in what:
uAb = fen.Function(self.V, name = "{}_ABS".format(name))
uAb.vector().set_local(np.abs(u))
filePW << (uAb, time)
if 'PHASE' in what:
uPh = fen.Function(self.V, name = "{}_PHASE".format(name))
uPh.vector().set_local(np.angle(u))
filePW << (uPh, time)
if 'REAL' in what:
uRe = fen.Function(self.V, name = "{}_REAL".format(name))
uRe.vector().set_local(np.real(u))
filePW << (uRe, time)
if 'IMAG' in what:
uIm = fen.Function(self.V, name = "{}_IMAG".format(name))
uIm.vector().set_local(np.imag(u))
filePW << (uIm, time)
return filePW
def outParaviewTimeDomain(self, u:Np1D, omega:float,
timeFinal : float = None,
periodResolution : int = 20, name : str = "u",
filename : str = "out",
- forceNewFile : bool = True):
+ forceNewFile : bool = True,
+ folder : bool = False):
"""
Output complex-valued function with given dofs to ParaView file,
converted to time domain.
Args:
u: numpy complex array with function dofs.
omega: frequency.
timeFinal(optional): final time of simulation.
periodResolution(optional): number of time steps per period.
name(optional): Base name to be used for data output.
filename(optional): Name of output file.
forceNewFile(optional): Whether to create new output file.
+ folder(optional): Whether to create an additional folder layer.
"""
+ if folder:
+ if not path.exists(filename + "/"):
+ mkdir(filename)
+ idxpath = filename.rfind("/")
+ filename += "/" + filename[idxpath + 1 :]
if forceNewFile:
filePW = fen.File(getNewFilename(filename, "pvd"))
else:
filePW = fen.File("{}.pvd".format(filename))
t = 0.
dt = 2. * np.pi / omega / periodResolution
if timeFinal is None: timeFinal = 2. * np.pi / omega - dt
for j in range(int(timeFinal / dt) + 1):
ut = fen.Function(self.V, name = name)
ut.vector().set_local(np.real(u) * np.cos(omega * t)
+ np.imag(u) * np.sin(omega * t))
filePW << (ut, t)
t += dt
return filePW
+
diff --git a/rrompy/hfengines/base/vector_problem_engine_base.py b/rrompy/hfengines/base/vector_problem_engine_base.py
index ceecc28..09a981b 100644
--- a/rrompy/hfengines/base/vector_problem_engine_base.py
+++ b/rrompy/hfengines/base/vector_problem_engine_base.py
@@ -1,194 +1,195 @@
# 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 fenics as fen
import numpy as np
from matplotlib import pyplot as plt
from rrompy.utilities.base.types import Np1D, strLst
from rrompy.utilities.base import purgeList, getNewFilename
from .problem_engine_base import ProblemEngineBase
__all__ = ['VectorProblemEngineBase']
class VectorProblemEngineBase(ProblemEngineBase):
"""
Generic solver for parametric vector 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):
+ def __init__(self, degree_threshold : int = np.inf, verbosity : int = 10,
+ timestamp : bool = True):
super().__init__(degree_threshold = degree_threshold,
- verbosity = verbosity)
+ verbosity = verbosity, timestamp = timestamp)
self.V = fen.VectorFunctionSpace(fen.UnitSquareMesh(10, 10), "P", 1)
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.
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 'figsize' not in figspecs.keys():
figspecs['figsize'] = (13. * max(len(what), 1) / 4, 3)
if len(what) > 0:
for j in range(self.V.num_sub_spaces()):
subplotcode = 100 + len(what) * 10
II = self.V.sub(j).dofmap().dofs()
Vj = self.V.sub(j).collapse()
plt.figure(**figspecs)
plt.jet()
if 'ABS' in what:
uAb = fen.Function(Vj)
uAb.vector().set_local(np.abs(u[II]))
subplotcode = subplotcode + 1
plt.subplot(subplotcode)
p = fen.plot(uAb, title = "|{}_comp{}|".format(name, j))
plt.colorbar(p)
if 'PHASE' in what:
uPh = fen.Function(Vj)
uPh.vector().set_local(np.angle(u[II]))
subplotcode = subplotcode + 1
plt.subplot(subplotcode)
p = fen.plot(uPh, title = "phase({}_comp{})".format(name,
j))
plt.colorbar(p)
if 'REAL' in what:
uRe = fen.Function(Vj)
uRe.vector().set_local(np.real(u[II]))
subplotcode = subplotcode + 1
plt.subplot(subplotcode)
p = fen.plot(uRe, title = "Re({}_comp{})".format(name, j))
plt.colorbar(p)
if 'IMAG' in what:
uIm = fen.Function(Vj)
uIm.vector().set_local(np.imag(u[II]))
subplotcode = subplotcode + 1
plt.subplot(subplotcode)
p = fen.plot(uIm, title = "Im({}_comp{})".format(name, j))
plt.colorbar(p)
if save is not None:
save = save.strip()
plt.savefig(getNewFilename("{}_comp{}_fig_".format(save, j),
saveFormat),
format = saveFormat, dpi = saveDPI)
plt.show()
plt.close()
try:
if len(what) > 1:
figspecs['figsize'] = (2. / len(what) * figspecs['figsize'][0],
figspecs['figsize'][1])
elif len(what) == 0:
figspecs['figsize'] = (2. * figspecs['figsize'][0],
figspecs['figsize'][1])
if len(what) == 0 or 'ABS' in what or 'REAL' in what:
uVRe = fen.Function(self.V)
uVRe.vector().set_local(np.real(u))
plt.figure(**figspecs)
plt.jet()
p = fen.plot(uVRe, title = "{}_Re".format(name),
mode = "displacement")
plt.colorbar(p)
if save is not None:
save = save.strip()
plt.savefig(getNewFilename("{}_disp_Re_fig_".format(save),
saveFormat),
format = saveFormat, dpi = saveDPI)
plt.show()
plt.close()
if 'ABS' in what or 'IMAG' in what:
uVIm = fen.Function(self.V)
uVIm.vector().set_local(np.imag(u))
plt.figure(**figspecs)
plt.jet()
p = fen.plot(uVIm, title = "{}_Im".format(name),
mode = "displacement")
plt.colorbar(p)
if save is not None:
save = save.strip()
plt.savefig(getNewFilename("{}_disp_Im_fig_".format(save, j),
saveFormat),
format = saveFormat, dpi = saveDPI)
plt.show()
plt.close()
except:
pass
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/linear_problem/helmholtz_box_scattering_problem_engine.py b/rrompy/hfengines/linear_problem/helmholtz_box_scattering_problem_engine.py
index eedd5a4..1867b95 100644
--- a/rrompy/hfengines/linear_problem/helmholtz_box_scattering_problem_engine.py
+++ b/rrompy/hfengines/linear_problem/helmholtz_box_scattering_problem_engine.py
@@ -1,57 +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 .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):
+ degree_threshold : int = np.inf, verbosity : int = 10,
+ timestamp : bool = True):
super().__init__(degree_threshold = degree_threshold,
- verbosity = verbosity)
+ verbosity = verbosity, timestamp = timestamp)
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/linear_problem/helmholtz_cavity_scattering_problem_engine.py b/rrompy/hfengines/linear_problem/helmholtz_cavity_scattering_problem_engine.py
index ba217ab..6349830 100644
--- a/rrompy/hfengines/linear_problem/helmholtz_cavity_scattering_problem_engine.py
+++ b/rrompy/hfengines/linear_problem/helmholtz_cavity_scattering_problem_engine.py
@@ -1,59 +1,59 @@
# 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 .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):
+ verbosity : int = 10, timestamp : bool = True):
super().__init__(degree_threshold = degree_threshold,
- verbosity = verbosity)
+ verbosity = verbosity, timestamp = timestamp)
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/linear_problem/helmholtz_problem_engine.py b/rrompy/hfengines/linear_problem/helmholtz_problem_engine.py
index 9775a24..2f3a65c 100644
--- a/rrompy/hfengines/linear_problem/helmholtz_problem_engine.py
+++ b/rrompy/hfengines/linear_problem/helmholtz_problem_engine.py
@@ -1,164 +1,162 @@
# 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.types import 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.
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.
dsToBeSet: Whether ds needs to be set.
"""
nAs = 2
+ rescalingExp = 2.
- def __init__(self, degree_threshold : int = np.inf, verbosity : int = 10):
+ def __init__(self, degree_threshold : int = np.inf, verbosity : int = 10,
+ timestamp : bool = True):
super().__init__(degree_threshold = degree_threshold,
- verbosity = verbosity)
+ verbosity = verbosity, timestamp = timestamp)
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.")
+ verbosityDepth("INIT", "Assembling operator term A0.",
+ timestamp = self.timestamp)
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.")
+ verbosityDepth("DEL", "Done assembling operator term.",
+ timestamp = self.timestamp)
if der <= 1 and self.As[1] is None:
if self.verbosity >= 20:
- verbosityDepth("INIT", "Assembling operator term A1.")
+ verbosityDepth("INIT", "Assembling operator term A1.",
+ timestamp = self.timestamp)
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.")
+ verbosityDepth("DEL", "Done assembling operator term.",
+ timestamp = self.timestamp)
if der == 0:
return self.As[0] + mu**2 * self.As[1]
return self.As[1]
diff --git a/rrompy/hfengines/linear_problem/helmholtz_square_bubble_domain_problem_engine.py b/rrompy/hfengines/linear_problem/helmholtz_square_bubble_domain_problem_engine.py
index c63ac70..5055a01 100644
--- a/rrompy/hfengines/linear_problem/helmholtz_square_bubble_domain_problem_engine.py
+++ b/rrompy/hfengines/linear_problem/helmholtz_square_bubble_domain_problem_engine.py
@@ -1,242 +1,247 @@
# 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, FenExpr
from rrompy.utilities.base.fenics import fenZERO
from .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
+ rescalingExp = 1.
def __init__(self, kappa:float, theta:float, n:int, mu0 : np.complex = 1.,
- degree_threshold : int = np.inf, verbosity : int = 10):
+ degree_threshold : int = np.inf, verbosity : int = 10,
+ timestamp : bool = True):
super().__init__(degree_threshold = degree_threshold,
- verbosity = verbosity)
+ verbosity = verbosity, timestamp = timestamp)
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): # H1
"""
Build sparse matrix (in CSR format) representative of scalar product.
"""
if self.verbosity >= 20:
- verbosityDepth("INIT", "Assembling energy matrix.", end = "")
+ verbosityDepth("INIT", "Assembling energy matrix.",
+ timestamp = self.timestamp)
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)
+ verbosityDepth("DEL", "Done assembling energy matrix.",
+ timestamp = self.timestamp)
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 = "")
+ verbosityDepth("INIT", ("Assembling base expression for "
+ "forcing term."),
+ timestamp = self.timestamp)
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)
+ verbosityDepth("DEL", "Done assembling base expression.",
+ timestamp = self.timestamp)
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 = "")
+ "forcing term derivative."),
+ timestamp = self.timestamp)
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)
+ verbosityDepth("DEL", "Done assembling auxiliary expression.",
+ timestamp = self.timestamp)
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.")
+ verbosityDepth("INIT", "Assembling operator term A0.",
+ timestamp = self.timestamp)
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.")
+ verbosityDepth("DEL", "Done assembling operator term.",
+ timestamp = self.timestamp)
if der <= 2 and self.As[2] is None:
if self.verbosity >= 20:
- verbosityDepth("INIT", "Assembling operator term A2.")
+ verbosityDepth("INIT", "Assembling operator term A2.",
+ timestamp = self.timestamp)
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.")
+ verbosityDepth("DEL", "Done assembling operator term.",
+ timestamp = self.timestamp)
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))
+ verbosityDepth("INIT", ("Assembling forcing term "
+ "b{}.").format(der),
+ timestamp = self.timestamp)
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.")
+ verbosityDepth("DEL", "Done assembling forcing term.",
+ timestamp = self.timestamp)
return self.bs[homogeneized][der]
diff --git a/rrompy/hfengines/linear_problem/helmholtz_square_bubble_problem_engine.py b/rrompy/hfengines/linear_problem/helmholtz_square_bubble_problem_engine.py
index aab7986..e80f733 100644
--- a/rrompy/hfengines/linear_problem/helmholtz_square_bubble_problem_engine.py
+++ b/rrompy/hfengines/linear_problem/helmholtz_square_bubble_problem_engine.py
@@ -1,52 +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 .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):
+ degree_threshold : int = np.inf, verbosity : int = 10,
+ timestamp : bool = True):
super().__init__(degree_threshold = degree_threshold,
- verbosity = verbosity)
+ verbosity = verbosity, timestamp = timestamp)
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/linear_problem/helmholtz_square_transmission_problem_engine.py b/rrompy/hfengines/linear_problem/helmholtz_square_transmission_problem_engine.py
index 1d2d9b7..da8c087 100644
--- a/rrompy/hfengines/linear_problem/helmholtz_square_transmission_problem_engine.py
+++ b/rrompy/hfengines/linear_problem/helmholtz_square_transmission_problem_engine.py
@@ -1,77 +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 .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):
+ degree_threshold : int = np.inf, verbosity : int = 10,
+ timestamp : bool = True):
super().__init__(degree_threshold = degree_threshold,
- verbosity = verbosity)
+ verbosity = verbosity, timestamp = timestamp)
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/linear_problem/laplace_base_problem_engine.py b/rrompy/hfengines/linear_problem/laplace_base_problem_engine.py
index 2336bbb..fe3ba28 100644
--- a/rrompy/hfengines/linear_problem/laplace_base_problem_engine.py
+++ b/rrompy/hfengines/linear_problem/laplace_base_problem_engine.py
@@ -1,315 +1,323 @@
# 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.hfengines.base.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.
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.
dsToBeSet: Whether ds needs to be set.
"""
- def __init__(self, degree_threshold : int = np.inf, verbosity : int = 10):
+ def __init__(self, degree_threshold : int = np.inf, verbosity : int = 10,
+ timestamp : bool = True):
super().__init__(degree_threshold = degree_threshold,
- verbosity = verbosity)
+ verbosity = verbosity, timestamp = timestamp)
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 = "")
+ timestamp = self.timestamp)
mesh = self.V.mesh()
NB = self.NeumannBoundary
RB = self.RobinBoundary
boundary_markers = fen.MeshFunction("size_t", mesh,
mesh.topology().dim() - 1)
NB.mark(boundary_markers, 0)
RB.mark(boundary_markers, 1)
self.ds = fen.Measure("ds", domain = mesh,
subdomain_data = boundary_markers)
self.dsToBeSet = False
if self.verbosity >= 20:
- verbosityDepth("DEL", " Done.", inline = True)
+ verbosityDepth("DEL", "Done initializing boundary measures.",
+ timestamp = self.timestamp)
def buildEnergyNormForm(self): # H1_omega
"""
Build sparse matrix (in CSR format) representative of scalar product.
"""
if self.verbosity >= 20:
- verbosityDepth("INIT", "Assembling energy matrix.", end = "")
+ verbosityDepth("INIT", "Assembling energy matrix.",
+ timestamp = self.timestamp)
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)
+ verbosityDepth("DEL", "Done assembling energy matrix.",
+ timestamp = self.timestamp)
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.")
+ verbosityDepth("INIT", "Assembling operator term A0.",
+ timestamp = self.timestamp)
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.")
+ verbosityDepth("DEL", "Done assembling operator term.",
+ timestamp = self.timestamp)
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))
+ verbosityDepth("INIT", ("Assembling forcing term "
+ "b{}.").format(der),
+ timestamp = self.timestamp)
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.")
+ verbosityDepth("DEL", "Done assembling forcing term.",
+ timestamp = self.timestamp)
return self.bs[homogeneized][der]
diff --git a/rrompy/hfengines/linear_problem/laplace_disk_gaussian.py b/rrompy/hfengines/linear_problem/laplace_disk_gaussian.py
index b6076cb..5fc6234 100644
--- a/rrompy/hfengines/linear_problem/laplace_disk_gaussian.py
+++ b/rrompy/hfengines/linear_problem/laplace_disk_gaussian.py
@@ -1,149 +1,154 @@
# 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, FenExpr
from .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):
+ verbosity : int = 10, timestamp : bool = True):
super().__init__(degree_threshold = degree_threshold,
- verbosity = verbosity)
+ verbosity = verbosity, timestamp = timestamp)
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 = "")
+ verbosityDepth("INIT", ("Assembling base expression for "
+ "forcing term."),
+ timestamp = self.timestamp)
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)
+ verbosityDepth("DEL", "Done assembling base expression.",
+ timestamp = self.timestamp)
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 = "")
+ "forcing term derivative."),
+ timestamp = self.timestamp)
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)
+ verbosityDepth("DEL", "Done assembling auxiliary expression.",
+ timestamp = self.timestamp)
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))
+ verbosityDepth("INIT", "Assembling forcing term b{}.".format(
+ der),
+ timestamp = self.timestamp)
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.")
+ verbosityDepth("DEL", "Done assembling forcing term.",
+ timestamp = self.timestamp)
return self.bs[homogeneized][der]
diff --git a/rrompy/hfengines/linear_problem/scattering_problem_engine.py b/rrompy/hfengines/linear_problem/scattering_problem_engine.py
index 7b32f88..eb6c6dc 100644
--- a/rrompy/hfengines/linear_problem/scattering_problem_engine.py
+++ b/rrompy/hfengines/linear_problem/scattering_problem_engine.py
@@ -1,176 +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.types import 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
+from rrompy.utilities.exception_manager import RROMPyWarning
__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.
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.
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 A2.
b0: Numpy array representation of b0.
dsToBeSet: Whether ds needs to be set.
"""
nAs = 3
+ rescalingExp = 1.
signR = - 1.
- def __init__(self, degree_threshold : int = inf, verbosity : int = 10):
+ def __init__(self, degree_threshold : int = inf, verbosity : int = 10,
+ timestamp : bool = True):
self.silenceWarnings = True
super().__init__(degree_threshold = degree_threshold,
- verbosity = verbosity)
+ verbosity = verbosity, timestamp = timestamp)
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."))
+ RROMPyWarning(("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.")
+ verbosityDepth("INIT", "Assembling operator term A0.",
+ timestamp = self.timestamp)
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.")
+ verbosityDepth("DEL", "Done assembling operator term.",
+ timestamp = self.timestamp)
if der <= 1 and self.As[1] is None:
if self.verbosity >= 20:
- verbosityDepth("INIT", "Assembling operator term A1.")
+ verbosityDepth("INIT", "Assembling operator term A1.",
+ timestamp = self.timestamp)
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.")
+ verbosityDepth("DEL", "Done assembling operator term.",
+ timestamp = self.timestamp)
if der <= 2 and self.As[2] is None:
if self.verbosity >= 20:
- verbosityDepth("INIT", "Assembling operator term A2.")
+ verbosityDepth("INIT", "Assembling operator term A2.",
+ timestamp = self.timestamp)
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.")
+ verbosityDepth("DEL", "Done assembling operator term.",
+ timestamp = self.timestamp)
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/vector_linear_problem/linear_elasticity_beam_poisson_ratio.py b/rrompy/hfengines/vector_linear_problem/linear_elasticity_beam_poisson_ratio.py
index 556c0fe..380007e 100644
--- a/rrompy/hfengines/vector_linear_problem/linear_elasticity_beam_poisson_ratio.py
+++ b/rrompy/hfengines/vector_linear_problem/linear_elasticity_beam_poisson_ratio.py
@@ -1,140 +1,146 @@
# 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 .linear_elasticity_problem_engine import LinearElasticityProblemEngine
from rrompy.utilities.base.fenics import fenZEROS
from rrompy.utilities.base.types import Np1D, ScOp
from rrompy.utilities.base import verbosityDepth
__all__ = ['LinearElasticityBeamPoissonRatio']
class LinearElasticityBeamPoissonRatio(LinearElasticityProblemEngine):
"""
Solver for linear elasticity problem of a beam subject to its own weight,
with parametric Poisson's ratio.
- div(lambda_ * div(u) * I + 2 * mu_ * epsilon(u)) = rho_ * g in \Omega
u = 0 on \Gamma_D
\partial_nu = 0 on \Gamma_N
"""
nAs = 2
nbs = 3
def __init__(self, n:int, rho_:float, g:float, E:float, nu0:float,
length:float, degree_threshold : int = np.inf,
- verbosity : int = 10):
+ verbosity : int = 10, timestamp : bool = True):
super().__init__(degree_threshold = degree_threshold,
- verbosity = verbosity)
+ verbosity = verbosity, timestamp = timestamp)
self.lambda_ = E * nu0 / (1. + nu0) / (1. - 2 * nu0)
self.mu_ = E / (1. + nu0)
mesh = fen.RectangleMesh(fen.Point(0., 0.), fen.Point(length, 1),
n, max(int(n / length), 1))
self.V = fen.VectorFunctionSpace(mesh, "P", 1)
self.forcingTerm = [fen.Constant((0., - rho_ * g / E)), fenZEROS(2)]
self.DirichletBoundary = lambda x, on_b: on_b and fen.near(x[0], 0.)
self.NeumannBoundary = "REST"
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 <= 1 and self.As[0] is None:
if self.verbosity >= 20:
- verbosityDepth("INIT", "Assembling operator term A0.")
+ verbosityDepth("INIT", "Assembling operator term A0.",
+ timestamp = self.timestamp)
DirichletBC0 = fen.DirichletBC(self.V, fenZEROS(2),
self.DirichletBoundary)
epsilon = lambda u: .5 * (fen.grad(u) + fen.nabla_grad(u))
a0Re = 2 * fen.inner(epsilon(self.u), epsilon(self.v)) * 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.")
+ verbosityDepth("DEL", "Done assembling operator term.",
+ timestamp = self.timestamp)
if der <= 1 and self.As[1] is None:
if self.verbosity >= 20:
- verbosityDepth("INIT", "Assembling operator term A1.")
+ verbosityDepth("INIT", "Assembling operator term A1.",
+ timestamp = self.timestamp)
DirichletBC0 = fen.DirichletBC(self.V, fenZEROS(2),
self.DirichletBoundary)
a1Re = fen.div(self.u) * fen.div(self.v) * fen.dx
A1Re = fen.assemble(a1Re)
DirichletBC0.apply(A1Re)
A1ReMat = fen.as_backend_type(A1Re).mat()
A1Rer, A1Rec, A1Rev = A1ReMat.getValuesCSR()
self.As[1] = (scsp.csr_matrix((A1Rev, A1Rec, A1Rer),
shape = A1ReMat.size,
dtype = np.complex)
- 2. * self.As[0])
if self.verbosity >= 20:
- verbosityDepth("DEL", "Done assembling operator term.")
+ verbosityDepth("DEL", "Done assembling operator term.",
+ timestamp = self.timestamp)
if der == 0:
return self.As[0] + mu * self.As[1]
return self.As[1]
def b(self, mu:complex, der : int = 0,
homogeneized : bool = False) -> Np1D:
"""Assemble (derivative of) RHS of linear system."""
homogeneized = False
bnull = self.checkbInBounds(der)
if bnull is not None: return bnull
if (self.nbs > 1 and not np.isclose(self.bsmu, mu)):
self.bsmu = mu
self.resetbs()
if self.bs[homogeneized][der] is None:
self.autoSetDS()
if self.bs[homogeneized][0] is None and der > 0: self.b(mu, 0)
if self.verbosity >= 20:
- verbosityDepth("INIT", "Assembling forcing term b{}.".format(
- der))
+ verbosityDepth("INIT", ("Assembling forcing term "
+ "b{}.").format(der),
+ timestamp = self.timestamp)
if der == 0:
fRe, fIm = self.forcingTerm
parsRe = self.iterReduceQuadratureDegree(zip(
[fRe],
["forcingTermReal"]))
parsIm = self.iterReduceQuadratureDegree(zip(
[fIm],
["forcingTermImag"]))
L0Re = fen.inner(fRe, self.v) * fen.dx
L0Im = fen.inner(fIm, self.v) * fen.dx
b0Re = fen.assemble(L0Re, form_compiler_parameters = parsRe)
b0Im = fen.assemble(L0Im, form_compiler_parameters = parsIm)
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.bsBase = np.array(b0Re[:] + 1.j * b0Im[:],
dtype = np.complex)
self.bs[homogeneized][0] = (1 - mu - 2 * mu ** 2) * self.bsBase
elif der == 1:
self.bs[homogeneized][der] = (- 1 - 4 * mu) * self.bsBase
elif der == 2:
self.bs[homogeneized][der] = - 2. * self.bsBase
if self.verbosity >= 20:
- verbosityDepth("DEL", "Done assembling forcing term.")
+ verbosityDepth("DEL", "Done assembling forcing term.",
+ timestamp = self.timestamp)
return self.bs[homogeneized][der]
diff --git a/rrompy/hfengines/vector_linear_problem/linear_elasticity_helmholtz_archway_frequency.py b/rrompy/hfengines/vector_linear_problem/linear_elasticity_helmholtz_archway_frequency.py
index 2eb31ba..1fc4ccb 100644
--- a/rrompy/hfengines/vector_linear_problem/linear_elasticity_helmholtz_archway_frequency.py
+++ b/rrompy/hfengines/vector_linear_problem/linear_elasticity_helmholtz_archway_frequency.py
@@ -1,66 +1,66 @@
# 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 .linear_elasticity_helmholtz_problem_engine import \
LinearElasticityHelmholtzProblemEngine
from rrompy.utilities.base.fenics import fenZEROS
__all__ = ['LinearElasticityHelmholtzArchwayFrequency']
class LinearElasticityHelmholtzArchwayFrequency(
LinearElasticityHelmholtzProblemEngine):
"""
Solver for archway linear elasticity Helmholtz problem with parametric
wavenumber.
- div(lambda_ * div(u) * I + 2 * mu_ * epsilon(u))
- rho_ * omega^2 * u = rho_ * g / omega in \Omega
u = 0 on \Gamma_D
\partial_nu = 0 on \Gamma_N
"""
def __init__(self, kappa:float, n:int, rho_:float, T:float, lambda_:float,
mu_:float, R:float, r:float, degree_threshold : int = np.inf,
- verbosity : int = 10):
+ verbosity : int = 10, timestamp : bool = True):
super().__init__(degree_threshold = degree_threshold,
- verbosity = verbosity)
+ verbosity = verbosity, timestamp = timestamp)
self.omega = kappa
self.lambda_ = lambda_
self.mu_ = mu_
self.rho_ = rho_
import mshr
domain = (mshr.Circle(fen.Point(0, 0), R)
- mshr.Circle(fen.Point(0, 0), r)
- mshr.Rectangle(fen.Point(-1.05*R, -1.05*R),
fen.Point(1.05*R, 0)))
mesh = mshr.generate_mesh(domain, n)
self.V = fen.VectorFunctionSpace(mesh, "P", 1)
import ufl
x, y = fen.SpatialCoordinate(mesh)[:]
NeumannNonZero = ufl.And(ufl.gt(y, r),
ufl.And(ufl.ge(x, -.25 * R), ufl.le(x, .25 * R)))
self.NeumannDatum = [ufl.as_vector((0.,
ufl.conditional(NeumannNonZero,
fen.Constant(T),
0.))),
fenZEROS(2)]
self.DirichletBoundary = lambda x, on_b: on_b and fen.near(x[1], 0.)
self.NeumannBoundary = "REST"
diff --git a/rrompy/hfengines/vector_linear_problem/linear_elasticity_helmholtz_archway_frequency_3d.py b/rrompy/hfengines/vector_linear_problem/linear_elasticity_helmholtz_archway_frequency_3d.py
index 99e2b77..78093a0 100644
--- a/rrompy/hfengines/vector_linear_problem/linear_elasticity_helmholtz_archway_frequency_3d.py
+++ b/rrompy/hfengines/vector_linear_problem/linear_elasticity_helmholtz_archway_frequency_3d.py
@@ -1,71 +1,71 @@
# 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 .linear_elasticity_helmholtz_problem_engine import \
LinearElasticityHelmholtzProblemEngine
from rrompy.utilities.base.fenics import fenZEROS
__all__ = ['LinearElasticityHelmholtzArchwayFrequency']
class LinearElasticityHelmholtzArchwayFrequency(
LinearElasticityHelmholtzProblemEngine):
"""
Solver for archway linear elasticity Helmholtz problem with parametric
wavenumber.
- div(lambda_ * div(u) * I + 2 * mu_ * epsilon(u))
- rho_ * omega^2 * u = rho_ * g / omega in \Omega
u = 0 on \Gamma_D
\partial_nu = 0 on \Gamma_N
"""
def __init__(self, kappa:float, n:int, rho_:float, T:float, lambda_:float,
mu_:float, R:float, r:float, degree_threshold : int = np.inf,
- verbosity : int = 10):
+ verbosity : int = 10, timestamp : bool = True):
super().__init__(degree_threshold = degree_threshold,
- verbosity = verbosity)
+ verbosity = verbosity, timestamp = timestamp)
self.omega = kappa
self.lambda_ = lambda_
self.mu_ = mu_
self.rho_ = rho_
import mshr
domain = (mshr.Sphere(fen.Point(0, 0, 0), R)
- mshr.Sphere(fen.Point(0, 0, 0), r)
- mshr.Box(fen.Point(-1.05*R, -1.05*R, -1.05*R),
fen.Point(1.05*R, 1.05*R, 0))
- mshr.Box(fen.Point(-1.05*R, -1.05*R, -1.05*R),
fen.Point(1.05*R, -.05*R, 1.05*R))
- mshr.Box(fen.Point(1.05*R, 1.05*R, 1.05*R),
fen.Point(-1.05*R, .05*R, -1.05*R)))
mesh = mshr.generate_mesh(domain, n)
self.V = fen.VectorFunctionSpace(mesh, "P", 1)
import ufl
x, y, z = fen.SpatialCoordinate(mesh)[:]
NeumannNonZero = ufl.And(ufl.gt(z, r),
ufl.And(ufl.ge(x, -.25 * R), ufl.le(x, .25 * R)))
self.NeumannDatum = [ufl.as_vector((0., 0.,fen.Constant(T))),
# ufl.conditional(NeumannNonZero,
# fen.Constant(T),
# 0.))),
fenZEROS(3)]
self.DirichletBoundary = lambda x, on_b: on_b and fen.near(x[2], 0.)
self.NeumannBoundary = "REST"
diff --git a/rrompy/hfengines/vector_linear_problem/linear_elasticity_helmholtz_archway_frequency_broken.py b/rrompy/hfengines/vector_linear_problem/linear_elasticity_helmholtz_archway_frequency_broken.py
index b125f0b..1717e53 100644
--- a/rrompy/hfengines/vector_linear_problem/linear_elasticity_helmholtz_archway_frequency_broken.py
+++ b/rrompy/hfengines/vector_linear_problem/linear_elasticity_helmholtz_archway_frequency_broken.py
@@ -1,110 +1,112 @@
# 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 .linear_elasticity_helmholtz_problem_engine import \
LinearElasticityHelmholtzProblemEngine
from rrompy.utilities.base.fenics import fenZEROS
from rrompy.utilities.base.types import Np1D
from rrompy.utilities.base import verbosityDepth
__all__ = ['LinearElasticityHelmholtzArchwayFrequency']
class LinearElasticityHelmholtzArchwayFrequency(
LinearElasticityHelmholtzProblemEngine):
"""
Solver for archway linear elasticity Helmholtz problem with parametric
wavenumber.
- div(lambda_ * div(u) * I + 2 * mu_ * epsilon(u))
- rho_ * omega^2 * u = rho_ * g / omega in \Omega
u = 0 on \Gamma_D
\partial_nu = 0 on \Gamma_N
"""
nAs = 2
nbs = 20
def __init__(self, kappa:float, n:int, rho_:float, g:float, lambda_:float,
mu_:float, R:float, r:float, degree_threshold : int = np.inf,
- verbosity : int = 10):
+ verbosity : int = 10, timestamp : bool = True):
super().__init__(degree_threshold = degree_threshold,
- verbosity = verbosity)
+ verbosity = verbosity, timestamp = timestamp)
self.omega = kappa
self.lambda_ = lambda_
self.mu_ = mu_
self.rho_ = rho_
import mshr
domain = (mshr.Circle(fen.Point(0, 0), R)
- mshr.Circle(fen.Point(0, 0), r)
- mshr.Rectangle(fen.Point(-1.05*R, -1.05*R),
fen.Point(1.05*R, 0)))
mesh = mshr.generate_mesh(domain, n)
self.V = fen.VectorFunctionSpace(mesh, "P", 1)
self.forcingTerm = [fen.Constant((0., - rho_ * g)), fenZEROS(2)]
self.DirichletBoundary = lambda x, on_b: on_b and fen.near(x[1], 0.)
self.NeumannBoundary = "REST"
def b(self, mu:complex, der : int = 0,
homogeneized : bool = False) -> Np1D:
"""Assemble (derivative of) RHS of linear system."""
homogeneized = False
bnull = self.checkbInBounds(der)
if bnull is not None: return bnull
if (self.nbs > 1 and not np.isclose(self.bsmu, mu)):
self.bsmu = mu
self.resetbs()
if self.bs[homogeneized][der] is None:
self.autoSetDS()
if self.bs[homogeneized][0] is None and der > 0: self.b(mu, 0)
if self.verbosity >= 20:
- verbosityDepth("INIT", "Assembling forcing term b{}.".format(
- der))
+ verbosityDepth("INIT", ("Assembling forcing term "
+ "b{}.").format(der),
+ timestamp = self.timestamp)
if der == 0:
fRe, fIm = self.forcingTerm
parsRe = self.iterReduceQuadratureDegree(zip(
[fRe],
["forcingTermReal"]))
parsIm = self.iterReduceQuadratureDegree(zip(
[fIm],
["forcingTermImag"]))
L0Re = fen.inner(fRe, self.v) * fen.dx
L0Im = fen.inner(fIm, self.v) * fen.dx
b0Re = fen.assemble(L0Re, form_compiler_parameters = parsRe)
b0Im = fen.assemble(L0Im, form_compiler_parameters = parsIm)
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.bsBase = np.array(b0Re[:] + 1.j * b0Im[:],
dtype = np.complex)
if np.isclose(mu, 0.):
if der == 0:
self.bs[homogeneized][der] = self.bsBase
else:
self.bs[homogeneized][der] = np.zeros_like(self.bsBase)
else:
self.bs[homogeneized][der] = (mu * np.power(- mu, - der)
* self.bsBase)
if self.verbosity >= 20:
- verbosityDepth("DEL", "Done assembling forcing term.")
+ verbosityDepth("DEL", "Done assembling forcing term.",
+ timestamp = self.timestamp)
return self.bs[homogeneized][der]
diff --git a/rrompy/hfengines/vector_linear_problem/linear_elasticity_helmholtz_problem_engine.py b/rrompy/hfengines/vector_linear_problem/linear_elasticity_helmholtz_problem_engine.py
index 07c1ff5..87bb44e 100644
--- a/rrompy/hfengines/vector_linear_problem/linear_elasticity_helmholtz_problem_engine.py
+++ b/rrompy/hfengines/vector_linear_problem/linear_elasticity_helmholtz_problem_engine.py
@@ -1,201 +1,201 @@
# 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 scipy.sparse import csr_matrix
import fenics as fen
from .linear_elasticity_problem_engine import LinearElasticityProblemEngine
-from rrompy.utilities.base.types import Np1D, ScOp
+from rrompy.utilities.base.types import ScOp
from rrompy.utilities.base.fenics import fenZERO, fenZEROS, fenONE
from rrompy.utilities.base import verbosityDepth
__all__ = ['LinearElasticityHelmholtzProblemEngine']
class LinearElasticityHelmholtzProblemEngine(LinearElasticityProblemEngine):
"""
Solver for generic linear elasticity Helmholtz problems with parametric
wavenumber.
- div(lambda_ * div(u) * I + 2 * mu_ * epsilon(u))
- rho_ * mu^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 vector FE space.
u: Generic vector trial functions for variational form evaluation.
v: Generic vector 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.
lambda_: Value of lambda_.
mu_: Value of mu_.
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.
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.
dsToBeSet: Whether ds needs to be set.
"""
nAs = 2
+ rescalingExp = 2.
- def __init__(self, degree_threshold : int = np.inf, verbosity : int = 10):
+ def __init__(self, degree_threshold : int = np.inf, verbosity : int = 10,
+ timestamp : bool = True):
super().__init__(degree_threshold = degree_threshold,
- verbosity = verbosity)
+ verbosity = verbosity, timestamp = timestamp)
self.omega = 1.
self.rho_ = fenONE
@property
def rho_(self):
"""Value of rho_."""
return self._rho_
@rho_.setter
def rho_(self, rho_):
self.resetAs()
if not isinstance(rho_, (list, tuple,)):
rho_ = [rho_, fenZERO]
self._rho_ = rho_
def buildEnergyNormForm(self): # energy + omega norm
"""
Build sparse matrix (in CSR format) representative of scalar product.
"""
if self.verbosity >= 20:
- verbosityDepth("INIT", "Assembling energy matrix.", end = "")
+ verbosityDepth("INIT", "Assembling energy matrix.",
+ timestamp = self.timestamp)
l_Re, _ = self.lambda_
m_Re, _ = self.mu_
r_Re, _ = self.rho_
termNames = ["lambda_", "mu_", "rho_"]
pars = self.iterReduceQuadratureDegree(zip(
[l_Re, m_Re, r_Re],
[x + "Real" for x in termNames]))
epsilon = lambda u: 0.5 * (fen.grad(u) + fen.nabla_grad(u))
sigma = lambda u, l_, m_: (
l_ * fen.div(u) * fen.Identity(u.geometric_dimension())
+ 2. * m_ * epsilon(u))
normMatFen = (fen.inner(sigma(self.u, l_Re, m_Re), epsilon(self.v))
+ np.abs(self.omega)**2 * r_Re * fen.inner(self.u, self.v)
) * fen.dx
normMatFen = fen.assemble(normMatFen, form_compiler_parameters = pars)
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 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)
+ verbosityDepth("DEL", "Done assembling energy matrix.",
+ timestamp = self.timestamp)
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.")
+ verbosityDepth("INIT", "Assembling operator term A0.",
+ timestamp = self.timestamp)
DirichletBC0 = fen.DirichletBC(self.V,
fenZEROS(self.V.mesh().topology().dim()),
self.DirichletBoundary)
lambda_Re, lambda_Im = self.lambda_
mu_Re, mu_Im = self.mu_
hRe, hIm = self.RobinDatumH
termNames = ["lambda_", "mu_", "RobinDatumH"]
parsRe = self.iterReduceQuadratureDegree(zip(
[lambda_Re, mu_Re, hRe],
[x + "Real" for x in termNames]))
parsIm = self.iterReduceQuadratureDegree(zip(
[lambda_Im, mu_Re, hIm],
[x + "Imag" for x in termNames]))
epsilon = lambda u: 0.5 * (fen.grad(u) + fen.nabla_grad(u))
sigma = lambda u, l_, m_: (
l_ * fen.div(u) * fen.Identity(u.geometric_dimension())
+ 2. * m_ * epsilon(u))
a0Re = (fen.inner(sigma(self.u, lambda_Re, mu_Re),
epsilon(self.v)) * fen.dx
+ hRe * fen.inner(self.u, self.v) * self.ds(1))
a0Im = (fen.inner(sigma(self.u, lambda_Im, mu_Im),
epsilon(self.v)) * fen.dx
+ hIm * fen.inner(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] = (csr_matrix((A0Rev, A0Rec, A0Rer),
shape = A0ReMat.size)
+ 1.j * csr_matrix((A0Imv, A0Imc, A0Imr),
shape = A0ImMat.size))
if self.verbosity >= 20:
- verbosityDepth("DEL", "Done assembling operator term.")
+ verbosityDepth("DEL", "Done assembling operator term.",
+ timestamp = self.timestamp)
if der <= 1 and self.As[1] is None:
if self.verbosity >= 20:
- verbosityDepth("INIT", "Assembling operator term A1.")
+ verbosityDepth("INIT", "Assembling operator term A1.",
+ timestamp = self.timestamp)
DirichletBC0 = fen.DirichletBC(self.V,
fenZEROS(self.V.mesh().topology().dim()),
self.DirichletBoundary)
rho_Re, rho_Im = self.rho_
parsRe = self.iterReduceQuadratureDegree(zip([rho_Re],
["rho_Real"]))
parsIm = self.iterReduceQuadratureDegree(zip([rho_Im],
["rho_Imag"]))
a1Re = - rho_Re * fen.inner(self.u, self.v) * fen.dx
a1Im = - rho_Im * fen.inner(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] = (csr_matrix((A1Rev, A1Rec, A1Rer),
shape = A1ReMat.size)
+ 1.j * csr_matrix((A1Imv, A1Imc, A1Imr),
shape = A1ImMat.size))
if self.verbosity >= 20:
- verbosityDepth("DEL", "Done assembling operator term.")
+ verbosityDepth("DEL", "Done assembling operator term.",
+ timestamp = self.timestamp)
if der == 0:
return self.As[0] + mu**2 * self.As[1]
return self.As[1]
diff --git a/rrompy/hfengines/vector_linear_problem/linear_elasticity_helmholtz_problem_engine_damped.py b/rrompy/hfengines/vector_linear_problem/linear_elasticity_helmholtz_problem_engine_damped.py
index a62049a..8c1b07d 100644
--- a/rrompy/hfengines/vector_linear_problem/linear_elasticity_helmholtz_problem_engine_damped.py
+++ b/rrompy/hfengines/vector_linear_problem/linear_elasticity_helmholtz_problem_engine_damped.py
@@ -1,209 +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 scipy.sparse import csr_matrix
import fenics as fen
from .linear_elasticity_helmholtz_problem_engine import \
LinearElasticityHelmholtzProblemEngine
-from rrompy.utilities.base.types import Np1D, ScOp
-from rrompy.utilities.base.fenics import fenZERO, fenZEROS, fenONE
+from rrompy.utilities.base.types import ScOp
+from rrompy.utilities.base.fenics import fenZERO, fenZEROS
from rrompy.utilities.base import verbosityDepth
__all__ = ['LinearElasticityHelmholtzProblemEngineDamped']
class LinearElasticityHelmholtzProblemEngineDamped(
LinearElasticityHelmholtzProblemEngine):
"""
Solver for generic linear elasticity Helmholtz problems with parametric
wavenumber.
- div(lambda_ * div(u) * I + 2 * mu_ * epsilon(u))
- rho_ * (mu^2 - i * eta * mu) * 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 vector FE space.
u: Generic vector trial functions for variational form evaluation.
v: Generic vector 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.
lambda_: Value of lambda_.
mu_: Value of mu_.
eta: Value of eta.
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.
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.
dsToBeSet: Whether ds needs to be set.
"""
nAs = 3
+ rescalingExp = 1.
- def __init__(self, degree_threshold : int = np.inf, verbosity : int = 10):
+ def __init__(self, degree_threshold : int = np.inf, verbosity : int = 10,
+ timestamp : bool = True):
super().__init__(degree_threshold = degree_threshold,
- verbosity = verbosity)
+ verbosity = verbosity, timestamp = timestamp)
self.eta = fenZERO
@property
def eta(self):
"""Value of eta."""
return self._eta
@eta.setter
def eta(self, eta):
self.resetAs()
if not isinstance(eta, (list, tuple,)):
eta = [eta, fenZERO]
self._eta = eta
- 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.")
+ verbosityDepth("INIT", "Assembling operator term A0.",
+ timestamp = self.timestamp)
DirichletBC0 = fen.DirichletBC(self.V,
fenZEROS(self.V.mesh().topology().dim()),
self.DirichletBoundary)
lambda_Re, lambda_Im = self.lambda_
mu_Re, mu_Im = self.mu_
hRe, hIm = self.RobinDatumH
termNames = ["lambda_", "mu_", "RobinDatumH"]
parsRe = self.iterReduceQuadratureDegree(zip(
[lambda_Re, mu_Re, hRe],
[x + "Real" for x in termNames]))
parsIm = self.iterReduceQuadratureDegree(zip(
[lambda_Im, mu_Re, hIm],
[x + "Imag" for x in termNames]))
epsilon = lambda u: 0.5 * (fen.grad(u) + fen.nabla_grad(u))
sigma = lambda u, l_, m_: (
l_ * fen.div(u) * fen.Identity(u.geometric_dimension())
+ 2. * m_ * epsilon(u))
a0Re = (fen.inner(sigma(self.u, lambda_Re, mu_Re),
epsilon(self.v)) * fen.dx
+ hRe * fen.inner(self.u, self.v) * self.ds(1))
a0Im = (fen.inner(sigma(self.u, lambda_Im, mu_Im),
epsilon(self.v)) * fen.dx
+ hIm * fen.inner(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] = (csr_matrix((A0Rev, A0Rec, A0Rer),
shape = A0ReMat.size)
+ 1.j * csr_matrix((A0Imv, A0Imc, A0Imr),
shape = A0ImMat.size))
if self.verbosity >= 20:
- verbosityDepth("DEL", "Done assembling operator term.")
+ verbosityDepth("DEL", "Done assembling operator term.",
+ timestamp = self.timestamp)
if der <= 1 and self.As[1] is None:
if self.verbosity >= 20:
- verbosityDepth("INIT", "Assembling operator term A1.")
+ verbosityDepth("INIT", "Assembling operator term A1.",
+ timestamp = self.timestamp)
DirichletBC0 = fen.DirichletBC(self.V,
fenZEROS(self.V.mesh().topology().dim()),
self.DirichletBoundary)
rho_Re, rho_Im = self.rho_
eta_Re, eta_Im = self.eta
termNames = ["rho_", "eta"]
parsRe = self.iterReduceQuadratureDegree(zip([rho_Re, eta_Re],
[x + "Real" for x in termNames]))
parsIm = self.iterReduceQuadratureDegree(zip([rho_Im, eta_Im],
[x + "Imag" for x in termNames]))
a1Re = - ((eta_Re * rho_Im + eta_Im * rho_Re)
* fen.inner(self.u, self.v)) * fen.dx
a1Im = ((eta_Re * rho_Re - eta_Im * rho_Im)
* fen.inner(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] = (csr_matrix((A1Rev, A1Rec, A1Rer),
shape = A1ReMat.size)
+ 1.j * csr_matrix((A1Imv, A1Imc, A1Imr),
shape = A1ImMat.size))
if self.verbosity >= 20:
- verbosityDepth("DEL", "Done assembling operator term.")
+ verbosityDepth("DEL", "Done assembling operator term.",
+ timestamp = self.timestamp)
if der <= 2 and self.As[2] is None:
if self.verbosity >= 20:
- verbosityDepth("INIT", "Assembling operator term A2.")
+ verbosityDepth("INIT", "Assembling operator term A2.",
+ timestamp = self.timestamp)
DirichletBC0 = fen.DirichletBC(self.V,
fenZEROS(self.V.mesh().topology().dim()),
self.DirichletBoundary)
rho_Re, rho_Im = self.rho_
parsRe = self.iterReduceQuadratureDegree(zip([rho_Re],
["rho_Real"]))
parsIm = self.iterReduceQuadratureDegree(zip([rho_Im],
["rho_Imag"]))
a2Re = - rho_Re * fen.inner(self.u, self.v) * fen.dx
a2Im = - rho_Im * fen.inner(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] = (csr_matrix((A2Rev, A2Rec, A2Rer),
shape = A2ReMat.size)
+ 1.j * csr_matrix((A2Imv, A2Imc, A2Imr),
shape = A2ImMat.size))
if self.verbosity >= 20:
- verbosityDepth("DEL", "Done assembling operator term.")
+ verbosityDepth("DEL", "Done assembling operator term.",
+ timestamp = self.timestamp)
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/vector_linear_problem/linear_elasticity_problem_engine.py b/rrompy/hfengines/vector_linear_problem/linear_elasticity_problem_engine.py
index 46131c4..f141e39 100644
--- a/rrompy/hfengines/vector_linear_problem/linear_elasticity_problem_engine.py
+++ b/rrompy/hfengines/vector_linear_problem/linear_elasticity_problem_engine.py
@@ -1,361 +1,369 @@
# 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 scipy.sparse import csr_matrix
import fenics as fen
from rrompy.hfengines.base.vector_problem_engine_base import \
VectorProblemEngineBase
from rrompy.utilities.base.types import Np1D, ScOp
from rrompy.utilities.base.fenics import fenZERO, fenZEROS, fenONE
from rrompy.utilities.base import verbosityDepth
__all__ = ['LinearElasticityProblemEngine']
class LinearElasticityProblemEngine(VectorProblemEngineBase):
"""
Solver for generic linear elasticity problems.
- div(lambda_ * div(u) * I + 2 * mu_ * epsilon(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 vector FE space.
u: Generic vector trial functions for variational form evaluation.
v: Generic vector 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.
lambda_: Value of lambda_.
mu_: Value of mu_.
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.
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.
dsToBeSet: Whether ds needs to be set.
"""
- def __init__(self, degree_threshold : int = np.inf, verbosity : int = 10):
+ def __init__(self, degree_threshold : int = np.inf, verbosity : int = 10,
+ timestamp : bool = True):
super().__init__(degree_threshold = degree_threshold,
- verbosity = verbosity)
+ verbosity = verbosity, timestamp = timestamp)
self.lambda_ = fenONE
self.mu_ = fenONE
self.forcingTerm = fenZEROS(self.V.mesh().topology().dim())
self.DirichletDatum = fenZEROS(self.V.mesh().topology().dim())
self.NeumannDatum = fenZEROS(self.V.mesh().topology().dim())
self.RobinDatumG = fenZEROS(self.V.mesh().topology().dim())
self.RobinDatumH = fenZERO
@property
def V(self):
"""Value of V."""
return self._V
@V.setter
def V(self, V):
VectorProblemEngineBase.V.fset(self, V)
self.forcingTerm = fenZEROS(self.V.mesh().topology().dim())
self.DirichletDatum = fenZEROS(self.V.mesh().topology().dim())
self.NeumannDatum = fenZEROS(self.V.mesh().topology().dim())
self.RobinDatumG = fenZEROS(self.V.mesh().topology().dim())
self.dsToBeSet = True
@property
def lambda_(self):
"""Value of lambda_."""
return self._lambda_
@lambda_.setter
def lambda_(self, lambda_):
self.resetAs()
if not isinstance(lambda_, (list, tuple,)):
lambda_ = [lambda_, fenZERO]
self._lambda_ = lambda_
@property
def mu_(self):
"""Value of mu_."""
return self._mu_
@mu_.setter
def mu_(self, mu_):
self.resetAs()
if not isinstance(mu_, (list, tuple,)):
mu_ = [mu_, fenZERO]
self._mu_ = mu_
@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,
fenZEROS(self.V.mesh().topology().dim())]
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,
fenZEROS(self.V.mesh().topology().dim())]
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,
fenZEROS(self.V.mesh().topology().dim())]
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,
fenZEROS(self.V.mesh().topology().dim())]
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 = "")
+ timestamp = self.timestamp)
NB = self.NeumannBoundary
RB = self.RobinBoundary
boundary_markers = fen.MeshFunction("size_t", self.V.mesh(),
self.V.mesh().topology().dim() - 1)
NB.mark(boundary_markers, 0)
RB.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)
+ verbosityDepth("DEL", "Done initializing boundary measures.",
+ timestamp = self.timestamp)
def buildEnergyNormForm(self): # energy norm
"""
Build sparse matrix (in CSR format) representative of scalar product.
"""
if self.verbosity >= 20:
- verbosityDepth("INIT", "Assembling energy matrix.", end = "")
+ verbosityDepth("INIT", "Assembling energy matrix.",
+ timestamp = self.timestamp)
lambda_Re, _ = self.lambda_
mu_Re, _ = self.mu_
termNames = ["lambda_", "mu_"]
pars = self.iterReduceQuadratureDegree(zip(
[lambda_Re, mu_Re],
[x + "Real" for x in termNames]))
epsilon = lambda u: 0.5 * (fen.grad(u) + fen.nabla_grad(u))
sigma = lambda u, l_, m_: (
l_ * fen.div(u) * fen.Identity(u.geometric_dimension())
+ 2. * m_ * epsilon(u))
normMatFen = fen.inner(sigma(self.u, lambda_Re, mu_Re),
epsilon(self.v)) * fen.dx
normMatFen = fen.assemble(fen.dot(self.u, self.v) * fen.dx,
form_compiler_parameters = pars)
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)
+ verbosityDepth("DEL", "Done assembling energy matrix.",
+ timestamp = self.timestamp)
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.")
+ verbosityDepth("INIT", "Assembling operator term A0.",
+ timestamp = self.timestamp)
DirichletBC0 = fen.DirichletBC(self.V,
fenZEROS(self.V.mesh().topology().dim()),
self.DirichletBoundary)
lambda_Re, lambda_Im = self.lambda_
mu_Re, mu_Im = self.mu_
hRe, hIm = self.RobinDatumH
termNames = ["lambda_", "mu_", "RobinDatumH"]
parsRe = self.iterReduceQuadratureDegree(zip(
[lambda_Re, mu_Re, hRe],
[x + "Real" for x in termNames]))
parsIm = self.iterReduceQuadratureDegree(zip(
[lambda_Im, mu_Re, hIm],
[x + "Imag" for x in termNames]))
epsilon = lambda u: 0.5 * (fen.grad(u) + fen.nabla_grad(u))
sigma = lambda u, l_, m_: (
l_ * fen.div(u) * fen.Identity(u.geometric_dimension())
+ 2. * m_ * epsilon(u))
a0Re = (fen.inner(sigma(self.u, lambda_Re, mu_Re),
epsilon(self.v)) * fen.dx
+ hRe * fen.inner(self.u, self.v) * self.ds(1))
a0Im = (fen.inner(sigma(self.u, lambda_Im, mu_Im),
epsilon(self.v)) * fen.dx
+ hIm * fen.inner(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] = (csr_matrix((A0Rev, A0Rec, A0Rer),
shape = A0ReMat.size)
+ 1.j * csr_matrix((A0Imv, A0Imc, A0Imr),
shape = A0ImMat.size))
if self.verbosity >= 20:
- verbosityDepth("DEL", "Done assembling operator term.")
+ verbosityDepth("DEL", "Done assembling operator term.",
+ timestamp = self.timestamp)
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))
+ verbosityDepth("INIT", ("Assembling forcing term "
+ "b{}.").format(der),
+ timestamp = self.timestamp)
if der < self.nbs:
fRe, fIm = self.forcingTerm
g1Re, g1Im = self.NeumannDatum
g2Re, g2Im = self.RobinDatumG
else:
fRe = fenZEROS(self.V.mesh().topology().dim())
fIm = fenZEROS(self.V.mesh().topology().dim())
g1Re = fenZEROS(self.V.mesh().topology().dim())
g1Im = fenZEROS(self.V.mesh().topology().dim())
g2Re = fenZEROS(self.V.mesh().topology().dim())
g2Im = fenZEROS(self.V.mesh().topology().dim())
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.inner(fRe, self.v) * fen.dx
+ fen.inner(g1Re, self.v) * self.ds(0)
+ fen.inner(g2Re, self.v) * self.ds(1))
L0Im = (fen.inner(fIm, self.v) * fen.dx
+ fen.inner(g1Im, self.v) * self.ds(0)
+ fen.inner(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,
fenZEROS(self.V.mesh().topology().dim()),
self.DirichletBoundary)
DBCI = fen.DirichletBC(self.V,
fenZEROS(self.V.mesh().topology().dim()),
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.")
+ verbosityDepth("DEL", "Done assembling forcing term.",
+ timestamp = self.timestamp)
return self.bs[homogeneized][der]
diff --git a/rrompy/reduction_methods/base/__init__.py b/rrompy/reduction_methods/base/__init__.py
index 853116c..b7e7f8e 100644
--- a/rrompy/reduction_methods/base/__init__.py
+++ b/rrompy/reduction_methods/base/__init__.py
@@ -1,29 +1,38 @@
# 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 .fit_utils import (polybases, polyval, polyvalder, polyvander,
+ polyfitname, polyroots, polydomcoeff)
from .pade_utils import checkRobustTolerance
+from .rb_utils import projectAffineDecomposition
__all__ = [
'GenericApproximant',
- 'setupFitCallables',
- 'checkRobustTolerance'
+ 'polybases',
+ 'polyval',
+ 'polyvalder',
+ 'polyvander',
+ 'polyfitname',
+ 'polyroots',
+ 'polydomcoeff',
+ 'checkRobustTolerance',
+ 'projectAffineDecomposition'
]
diff --git a/rrompy/reduction_methods/base/fit_utils.py b/rrompy/reduction_methods/base/fit_utils.py
index b593035..cccfc4a 100644
--- a/rrompy/reduction_methods/base/fit_utils.py
+++ b/rrompy/reduction_methods/base/fit_utils.py
@@ -1,55 +1,45 @@
# 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
+from scipy.special import binom
-__all__ = ['setupFitCallables']
+__all__ = ['polybases', 'polyval', 'polyvalder', 'polyvander', 'polyfitname',
+ 'polyroots', 'polydomcoeff']
-def setupFitCallables(kind:str) -> DictAny:
- """Setup main callables and name for data fitting."""
- kind = kind.upper()
- if kind == "CHEBYSHEV":
- val = po.chebyshev.chebval
- valder = lambda x, c: po.chebyshev.chebval(x, po.chebyshev.chebder(c))
- 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
- valder = lambda x, c: po.legendre.legval(x, po.legendre.legder(c))
- 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
- valder = lambda x, c: po.polynomial.polyval(x,po.polynomial.polyder(c))
- vander = po.polynomial.polyvander
- fitname = "polyfit"
- roots = po.polynomial.polyroots
- domcoeff = lambda n: 1.
- return {"val":val, "valder":valder, "vander":vander, "fitname":fitname,
- "roots":roots, "domcoeff":domcoeff}
+polybases = ["CHEBYSHEV", "LEGENDRE", "MONOMIAL"]
+
+polyval = {"CHEBYSHEV" : po.chebyshev.chebval, "LEGENDRE" : po.legendre.legval,
+ "MONOMIAL" : po.polynomial.polyval}
+polyvalder = {
+ "CHEBYSHEV" : lambda x, c: po.chebyshev.chebval(x, po.chebyshev.chebder(c)),
+ "LEGENDRE" : lambda x, c: po.legendre.legval(x, po.legendre.legder(c)),
+ "MONOMIAL" : lambda x, c: po.polynomial.polyval(x, po.polynomial.polyder(c))}
+polyvander = {"CHEBYSHEV" : po.chebyshev.chebvander,
+ "LEGENDRE" : po.legendre.legvander,
+ "MONOMIAL" : po.polynomial.polyvander}
+polyfitname = {"CHEBYSHEV" : "chebfit", "LEGENDRE" : "legfit",
+ "MONOMIAL" : "polyfit"}
+polyroots = {"CHEBYSHEV" : po.chebyshev.chebroots,
+ "LEGENDRE" : po.legendre.legroots,
+ "MONOMIAL" : po.polynomial.polyroots}
+polydomcoeff = {"CHEBYSHEV" : lambda n: 2. ** (n - 1) if n > 0 else 1.,
+ "LEGENDRE" : lambda n: (2. ** n * (pi * n) ** -.5 if n > 10
+ else .5 ** n * binom(2 * n, n)),
+ "MONOMIAL" : lambda n: 1.}
diff --git a/rrompy/reduction_methods/base/generic_approximant.py b/rrompy/reduction_methods/base/generic_approximant.py
index 417858e..f999be6 100644
--- a/rrompy/reduction_methods/base/generic_approximant.py
+++ b/rrompy/reduction_methods/base/generic_approximant.py
@@ -1,515 +1,608 @@
# 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 pickle
import numpy as np
-from copy import copy
+from copy import deepcopy as 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
+from rrompy.utilities.base import purgeDict, verbosityDepth, getNewFilename
+from rrompy.utilities.exception_manager import (RROMPyException, modeAssert,
+ RROMPy_READY, RROMPy_FRAGILE)
__all__ = ['GenericApproximant']
def addNormFieldToClass(self, fieldName):
def objFunc(self, mu:complex, homogeneized : bool = False) -> float:
getObj = getattr(self.__class__, "get" + fieldName)
return self.HFEngine.norm(getObj(self, mu, homogeneized))
setattr(self.__class__, "norm" + fieldName, objFunc)
def addPlotFieldToClass(self, fieldName):
def objFunc(self, mu:complex, name : str = fieldName, save : str = None,
what : strLst = 'all', saveFormat : str = "eps",
saveDPI : int = 100, homogeneized : bool = False, **figspecs):
uV = getattr(self.__class__, "get" + fieldName)(self, mu, homogeneized)
self.HFEngine.plot(uV, name = name, save = save, what = what,
saveFormat = saveFormat, saveDPI = saveDPI,
**figspecs)
setattr(self.__class__, "plot" + fieldName, objFunc)
def addOutParaviewFieldToClass(self, fieldName):
def objFunc(self, mu:complex, name : str = fieldName,
filename : str = "out", time : float = 0.,
what : strLst = 'all', forceNewFile : bool = True,
- filePW = None, homogeneized : bool = False):
+ folder : bool = False, filePW = None,
+ homogeneized : bool = False):
uV = getattr(self.__class__, "get" + fieldName)(self, mu, homogeneized)
self.HFEngine.outParaview(uV, name = name, filename = filename,
time = time, what = what,
- forceNewFile = forceNewFile, filePW = filePW)
+ forceNewFile = forceNewFile,
+ folder = folder, filePW = filePW)
setattr(self.__class__, "outParaview" + fieldName, objFunc)
def addOutParaviewTimeDomainFieldToClass(self, fieldName):
def objFunc(self, mu:complex, omega : float = None,
timeFinal : float = None, periodResolution : int = 20,
name : str = fieldName, filename : str = "out",
- forceNewFile : bool = True, homogeneized : bool = False):
+ forceNewFile : bool = True, folder : bool = False,
+ homogeneized : bool = False):
uV = getattr(self.__class__, "get" + fieldName)(self, mu, homogeneized)
if omega is None: omega = np.real(mu)
self.HFEngine.outParaviewTimeDomain(uV, omega = omega,
timeFinal = timeFinal,
periodResolution = periodResolution,
name = name, filename = filename,
- forceNewFile = forceNewFile)
+ forceNewFile = forceNewFile,
+ folder = folder)
setattr(self.__class__, "outParaviewTimeDomain" + fieldName, objFunc)
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.
+ trainedModel: Trained model evaluator.
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.
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):
+ verbosity : int = 10, timestamp : bool = True):
self._preInit()
+ self._mode = RROMPy_READY
self.verbosity = verbosity
+ self.timestamp = timestamp
if self.verbosity >= 10:
verbosityDepth("INIT", ("Initializing approximant engine of "
- "type {}.").format(self.name()))
+ "type {}.").format(self.name()),
+ timestamp = self.timestamp)
self.HFEngine = HFEngine
- self._HFEngine0 = copy(HFEngine)
self._addParametersToList(["POD"])
self.mu0 = mu0
self.homogeneized = homogeneized
self.approxParameters = approxParameters
self._postInit()
### add norm{HF,RHS,Approx,Res,Err} methods
"""
Compute norm of * at arbitrary parameter.
Args:
mu: Target parameter.
homogeneized(optional): Whether to remove Dirichlet BC. Defaults to
False.
Returns:
Target norm of *.
"""
for objName in ["HF", "RHS", "Approx", "Res", "Err"]:
addNormFieldToClass(self, objName)
### add plot{HF,RHS,Approx,Res,Err} methods
"""
Do some nice plots of * 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.
"""
for objName in ["HF", "RHS", "Approx", "Res", "Err"]:
addPlotFieldToClass(self, objName)
### add outParaview{HF,RHS,Approx,Res,Err} methods
"""
Output * to ParaView file.
Args:
mu: Target parameter.
name(optional): Base name to be used for data output.
filename(optional): Name of output file.
time(optional): Timestamp.
what(optional): Which plots to do. If list, can contain 'MESH',
'ABS', 'PHASE', 'REAL', 'IMAG'. If str, same plus wildcard
'ALL'. Defaults to 'ALL'.
forceNewFile(optional): Whether to create new output file.
filePW(optional): Fenics File entity (for time series).
homogeneized(optional): Whether to remove Dirichlet BC. Defaults to
False.
"""
for objName in ["HF", "RHS", "Approx", "Res", "Err"]:
addOutParaviewFieldToClass(self, objName)
### add outParaviewTimeDomain{HF,RHS,Approx,Res,Err} methods
"""
Output * to ParaView file, converted to time domain.
Args:
mu: Target parameter.
omega(optional): frequency.
timeFinal(optional): final time of simulation.
periodResolution(optional): number of time steps per period.
name(optional): Base name to be used for data output.
filename(optional): Name of output file.
forceNewFile(optional): Whether to create new output file.
homogeneized(optional): Whether to remove Dirichlet BC. Defaults to
False.
"""
for objName in ["HF", "RHS", "Approx", "Res", "Err"]:
addOutParaviewTimeDomainFieldToClass(self, objName)
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")
+ verbosityDepth("DEL", "Done initializing.",
+ timestamp = self.timestamp)
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."""
+ modeAssert(self._mode, message = "Cannot setup sampling engine.")
self.samplingEngine = SamplingEngine(self.HFEngine,
verbosity = self.verbosity)
+ @property
+ def mu0(self):
+ """Value of mu0."""
+ return self._mu0
+ @mu0.setter
+ def mu0(self, mu0):
+ if not (hasattr(self, "_mu0") and np.isclose(mu0, self.mu0)):
+ self.resetSamples()
+ self._mu0 = mu0
+
@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:
+ elif not hasattr(self, "_POD") or self._POD is None:
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
+ 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.samplingEngine = None
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"):
+ if hasattr(self, "samplingEngine") and self.samplingEngine is not None:
self.samplingEngine.resetHistory()
else:
self.setupSampling()
+ self.trainedModel = None
+ self._mode = RROMPy_READY
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.
"""
+ modeAssert(self._mode, message = "Cannot plot samples.")
self.samplingEngine.plotSamples(name = name, save = save, what = what,
saveFormat = saveFormat,
saveDPI = saveDPI,
**figspecs)
def outParaviewSamples(self, name : str = "u", filename : str = "out",
times : Np1D = None, what : strLst = 'all',
- forceNewFile : bool = True, filePW = None):
+ forceNewFile : bool = True, folders : bool = False,
+ filePW = None):
"""
Output samples to ParaView file.
Args:
name(optional): Base name to be used for data output.
filename(optional): Name of output file.
times(optional): Timestamps.
what(optional): Which plots to do. If list, can contain 'MESH',
'ABS', 'PHASE', 'REAL', 'IMAG'. If str, same plus wildcard
'ALL'. Defaults to 'ALL'.
forceNewFile(optional): Whether to create new output file.
+ folders(optional): Whether to split output in folders.
filePW(optional): Fenics File entity (for time series).
"""
+ modeAssert(self._mode, message = "Cannot output samples.")
self.samplingEngine.outParaviewSamples(name = name,
filename = filename,
times = times, what = what,
forceNewFile = forceNewFile,
+ folders = folders,
filePW = filePW)
def outParaviewTimeDomainSamples(self, omegas : Np1D = None,
timeFinal : Np1D = None,
periodResolution : int = 20,
name : str = "u",
filename : str = "out",
- forceNewFile : bool = True):
+ forceNewFile : bool = True,
+ folders : bool = False):
"""
Output samples to ParaView file, converted to time domain.
Args:
omegas(optional): frequencies.
timeFinal(optional): final time of simulation.
periodResolution(optional): number of time steps per period.
name(optional): Base name to be used for data output.
filename(optional): Name of output file.
forceNewFile(optional): Whether to create new output file.
+ folders(optional): Whether to split output in folders.
"""
+ modeAssert(self._mode, message = "Cannot output samples.")
self.samplingEngine.outParaviewTimeDomainSamples(omegas = omegas,
timeFinal = timeFinal,
periodResolution = periodResolution,
name = name, filename = filename,
- forceNewFile = forceNewFile)
+ forceNewFile = forceNewFile,
+ folders = folders)
@abstractmethod
def setupApprox(self):
"""
Setup approximant. (ABSTRACT)
Any specialization should include something like
- self.computeDerivatives()
- if not self.checkComputedApprox():
- ...
- self.lastApproxParameters = copy(self.approxParameters)
+ if self.checkComputedApprox():
+ return
+ modeAssert(self._mode, message = "Cannot setup approximant.")
+ ...
+ self.trainedModel = ...
+ self.trainedModel.data = ...
+ self.trainedModel.data.approxParameters = 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)
+ return self._mode == RROMPy_FRAGILE or (self.trainedModel is not None
+ and self.trainedModel.data.approxParameters == self.approxParameters)
- @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
+ self.setupApprox()
+ self.uAppReduced = self.trainedModel.getApproxReduced(mu)
- @abstractmethod
def evalApprox(self, mu:complex):
"""
- Evaluate approximant at arbitrary parameter. (ABSTRACT)
-
- Any specialization should include something like
- self.evalApproxReduced(mu)
- self.uApp = ...
+ Evaluate approximant at arbitrary parameter.
Args:
mu: Target parameter.
"""
- pass
+ self.setupApprox()
+ self.uApp = self.trainedModel.getApprox(mu)
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 getApproxReduced(self, mu:complex) -> Np1D:
"""
Get approximant at arbitrary parameter.
Args:
mu: Target parameter.
Returns:
Reduced approximant.
"""
self.evalApproxReduced(mu)
return self.uAppReduced
def getApprox(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.getApprox(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.getApprox(mu, homogeneized) - self.getHF(mu, homogeneized)
- @abstractmethod
def getPoles(self) -> Np1D:
"""
Obtain approximant poles.
Returns:
Numpy complex vector of poles.
"""
- pass
+ self.setupApprox()
+ if self.verbosity >= 20:
+ verbosityDepth("INIT", "Computing poles of model.",
+ timestamp = self.timestamp)
+ poles = self.trainedModel.getPoles()
+ if self.verbosity >= 20:
+ verbosityDepth("DEL", "Done computing poles.",
+ timestamp = self.timestamp)
+ return poles
+
+ def storeTrainedModel(self, filenameBase : str = "trained_model",
+ forceNewFile : bool = True):
+ """Store trained reduced model to file."""
+ self.setupApprox()
+ if self.verbosity >= 20:
+ verbosityDepth("INIT", "Storing trained model to file.",
+ timestamp = self.timestamp)
+ if forceNewFile:
+ filename = getNewFilename(filenameBase, "pkl")
+ else:
+ filename = "{}.pkl".format(filenameBase)
+ with open(filename, "wb") as fileOut:
+ pickle.dump(self.trainedModel.data.__dict__, fileOut)
+ if self.verbosity >= 20:
+ verbosityDepth("DEL", "Done storing trained model.",
+ timestamp = self.timestamp)
+ return filename
+
+ def loadTrainedModel(self, filename:str):
+ """Store trained reduced model to file."""
+ if self.verbosity >= 20:
+ verbosityDepth("INIT", "Loading pre-trained model from file.",
+ timestamp = self.timestamp)
+ with open(filename, "rb") as fileIn:
+ datadict = pickle.load(fileIn)
+ name = datadict.pop("name")
+ if name == "TrainedModelPade":
+ from rrompy.reduction_methods.trained_model import \
+ TrainedModelPade as tModel
+ elif name == "TrainedModelRB":
+ from rrompy.reduction_methods.trained_model import \
+ TrainedModelRB as tModel
+ else:
+ raise RROMPyException(("Trained model name not recognized. "
+ "Loading failed."))
+ self.mu0 = datadict.pop("mu0")
+ from rrompy.reduction_methods.trained_model import TrainedModelData
+ trainedModel = tModel()
+ trainedModel.verbosity = self.verbosity
+ trainedModel.timestamp = self.timestamp
+ data = TrainedModelData(name, self.mu0, datadict.pop("projMat"),
+ datadict.pop("rescalingExp"))
+ if "mus" in datadict:
+ self.mus = datadict.pop("mus")
+ data.mus = np.copy(self.mus)
+ approxParameters = datadict.pop("approxParameters")
+ data.approxParameters = copy(approxParameters)
+ if "sampler" in approxParameters:
+ self._approxParameters["sampler"] = approxParameters.pop("sampler")
+ self.approxParameters = copy(approxParameters)
+ if name == "TrainedModelPade":
+ self.scaleFactor = datadict.pop("scaleFactor")
+ data.scaleFactor = self.scaleFactor
+ for key in datadict:
+ setattr(data, key, datadict[key])
+ trainedModel.data = data
+ self.trainedModel = trainedModel
+ self._mode = RROMPy_FRAGILE
+ if self.verbosity >= 20:
+ verbosityDepth("DEL", "Done loading pre-trained model.",
+ timestamp = self.timestamp)
diff --git a/rrompy/reduction_methods/base/pade_utils.py b/rrompy/reduction_methods/base/pade_utils.py
index 127ca65..5a1a801 100644
--- a/rrompy/reduction_methods/base/pade_utils.py
+++ b/rrompy/reduction_methods/base/pade_utils.py
@@ -1,44 +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
-from rrompy.utilities.warning_manager import warn
+from rrompy.utilities.exception_manager import RROMPyWarning
__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(np.sum(np.abs(ev) >= ts), Enew)
if Nnew == N:
strN = ""
else:
strN = "N from {} to {} and ".format(N, Nnew)
- warn(("Smallest {} eigenvalues below tolerance. Reducing {}E from {} "
- "to {}.").format(diff + 1, strN, E, Enew))
+ RROMPyWarning(("Smallest {} eigenvalues below tolerance. Reducing {}E "
+ "from {} to {}.").format(diff + 1, strN, E, Enew))
newParameters = {"N" : Nnew, "E" : Enew}
return newParameters
diff --git a/rrompy/reduction_methods/base/rb_utils.py b/rrompy/reduction_methods/base/rb_utils.py
new file mode 100644
index 0000000..e7b8691
--- /dev/null
+++ b/rrompy/reduction_methods/base/rb_utils.py
@@ -0,0 +1,56 @@
+# 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, List
+
+__all__ = ['projectAffineDecomposition']
+
+def projectAffineDecomposition(As:List[Np2D], bs:List[Np1D], pMat:Np2D,
+ ARBsOld : List[Np2D] = None,
+ bRBsOld : List[Np1D] = None,
+ pMatOld : Np2D = None)\
+ -> Tuple[List[Np2D], List[Np1D]]:
+ """Project affine decomposition of linear system onto basis."""
+ assert((ARBsOld is None) == (pMatOld is None)
+ and (bRBsOld is None) == (pMatOld is None))
+ pMatH = pMat.T.conj()
+ ARBs = [None] * len(As)
+ bRBs = [None] * len(bs)
+ if pMatOld is None:
+ for j in range(len(As)):
+ ARBs[j] = pMatH.dot(As[j].dot(pMat))
+ for j in range(len(bs)):
+ bRBs[j] = pMatH.dot(bs[j])
+ else:
+ assert(len(ARBsOld) == len(As) and len(bRBsOld) == len(bs))
+ pMatOldH = pMatOld.T.conj()
+ Sold = pMatOld.shape[1]
+ Snew = pMat.shape[1]
+ for j in range(len(As)):
+ ARBs[j] = np.empty((Sold + Snew, Sold + Snew), dtype = np.complex)
+ ARBs[j][: Sold, : Sold] = ARBsOld[j]
+ ARBs[j][: Sold, Sold :] = pMatOldH.dot(As[j].dot(pMat))
+ ARBs[j][Sold :, : Sold] = pMatH.dot(As[j].dot(pMatOld))
+ ARBs[j][Sold :, Sold :] = pMatH.dot(As[j].dot(pMat))
+ for j in range(len(bs)):
+ bRBs[j] = np.empty((Sold + Snew), dtype = np.complex)
+ bRBs[j][: Sold] = np.copy(bRBsOld[j])
+ bRBs[j][Sold :] = pMatH.dot(bs[j])
+ return ARBs, bRBs
+
diff --git a/rrompy/reduction_methods/lagrange/approximant_lagrange_pade.py b/rrompy/reduction_methods/lagrange/approximant_lagrange_pade.py
index 7593fc9..2089479 100644
--- a/rrompy/reduction_methods/lagrange/approximant_lagrange_pade.py
+++ b/rrompy/reduction_methods/lagrange/approximant_lagrange_pade.py
@@ -1,493 +1,486 @@
# 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,
- setupFitCallables)
+from rrompy.reduction_methods.base import checkRobustTolerance
from .generic_approximant_lagrange import GenericApproximantLagrange
-from rrompy.utilities.base.types import Np1D, List, HFEng, DictAny
+from rrompy.reduction_methods.base.fit_utils import (polybases, polyvander,
+ polyvalder, polyfitname)
+from rrompy.reduction_methods.trained_model import TrainedModelPade as tModel
+from rrompy.reduction_methods.trained_model import TrainedModelData
+from rrompy.utilities.base.types import Np1D, Np2D, HFEng, DictAny, Tuple
from rrompy.utilities.base import verbosityDepth, purgeDict, customFit
-from rrompy.utilities.warning_manager import warn
+from rrompy.utilities.exception_manager import (RROMPyException, modeAssert,
+ RROMPyWarning)
__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';
+ - 'polybasis': type of polynomial 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;
+ - 'polybasis': type of polynomial 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):
+ verbosity : int = 10, timestamp : bool = True):
self._preInit()
- self._addParametersToList(["basis", "E", "M", "N",
+ self._addParametersToList(["polybasis", "E", "M", "N",
"interpRcond", "robustTol"])
super().__init__(HFEngine = HFEngine, mu0 = mu0,
approxParameters = approxParameters,
homogeneized = homogeneized,
- verbosity = verbosity)
+ verbosity = verbosity, timestamp = timestamp)
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, ["basis",
+ approxParametersCopy = purgeDict(approxParameters, ["polybasis",
"E", "M", "N",
"interpRcond",
"robustTol"],
True, True, baselevel = 1)
- if hasattr(self, "M"):
+ if hasattr(self, "_M") and self.M is not None:
Mold = self.M
self._M = 0
- if hasattr(self, "N"):
+ if hasattr(self, "_N") and self.N is not None:
Nold = self.N
self._N = 0
- if hasattr(self, "E"):
+ if hasattr(self, "_E") and self.E is not None:
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 "polybasis" in keyList:
+ self.polybasis = approxParameters["polybasis"]
+ elif not hasattr(self, "_polybasis") or self._polybasis is None:
+ self.polybasis = "MONOMIAL"
if "interpRcond" in keyList:
self.interpRcond = approxParameters["interpRcond"]
- elif hasattr(self, "interpRcond"):
- self.interpRcond = self.interpRcond
- else:
+ elif not hasattr(self, "interpRcond") or self.interpRcond is None:
self.interpRcond = None
if "robustTol" in keyList:
self.robustTol = approxParameters["robustTol"]
- elif hasattr(self, "robustTol"):
- self.robustTol = self.robustTol
- else:
+ elif not hasattr(self, "_robustTol") or self._robustTol is None:
self.robustTol = 0
if "M" in keyList:
self.M = approxParameters["M"]
- elif hasattr(self, "M"):
+ elif hasattr(self, "_M") and self.M is not None:
self.M = Mold
else:
self.M = 0
if "N" in keyList:
self.N = approxParameters["N"]
- elif hasattr(self, "N"):
+ elif hasattr(self, "_N") and self.N is not None:
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 polybasis(self):
+ """Value of polybasis."""
+ return self._polybasis
+ @polybasis.setter
+ def polybasis(self, polybasis):
+ try:
+ polybasis = polybasis.upper().strip().replace(" ","")
+ if polybasis not in polybases:
+ raise RROMPyException("Prescribed polybasis not recognized.")
+ self._polybasis = polybasis
+ except:
+ RROMPyWarning(("Prescribed polybasis not recognized. Overriding "
+ "to 'MONOMIAL'."))
+ self._sampleType = "MONOMIAL"
+ self._approxParameters["polybasis"] = self.polybasis
+
+ @property
+ def interpRcond(self):
+ """Value of interpRcond."""
+ return self._interpRcond
+ @interpRcond.setter
+ def interpRcond(self, interpRcond):
+ self._interpRcond = interpRcond
+ self._approxParameters["interpRcond"] = self.interpRcond
+
@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.")
+ if M < 0: raise RROMPyException("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.")
+ if hasattr(self, "_S") and self.S < self.M + 1:
+ RROMPyWarning("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.")
+ if N < 0: raise RROMPyException("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.")
+ if hasattr(self, "_S") and self.S < self.N + 1:
+ RROMPyWarning("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.")
+ if E < 0: raise RROMPyException("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.")
+ if hasattr(self, "_S") and self.S < self.E + 1:
+ RROMPyWarning("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.")
+ RROMPyWarning(("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
+ if S <= 0: raise RROMPyException("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
+ if hasattr(self, "_M") and self._M is not None: vals[0] = self.M
+ if hasattr(self, "_N") and self._N is not None: vals[1] = self.N
+ if hasattr(self, "_E") and self._E is not None: 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]))
+ RROMPyWarning(("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.computeScaleFactor()
- self.computeSnapshots()
- if self.N > 0:
- if self.verbosity >= 7:
- verbosityDepth("INIT", ("Starting computation of "
- "denominator."))
- TE = self._vander(self.radiusPade(self.mus), self.E)
- TE = (TE.T * self.ws).T
- while self.N > 0:
- RHS = np.zeros(self.E + 1)
- RHS[-1] = 1.
- fitOut = customFit(TE.T, RHS, 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._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
- TE = TE[:, : self.E + 1]
- continue
- 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."))
- 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."))
- 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 solving eigenvalue "
- "problem."))
- 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 = eV[:, 0]
- if self.verbosity >= 7:
- verbosityDepth("DEL", "Done computing denominator.")
- else:
- self.Q = np.ones(1, dtype = np.complex)
+ if self.checkComputedApprox():
+ return
+ modeAssert(self._mode, message = "Cannot setup approximant.")
+ if self.verbosity >= 5:
+ verbosityDepth("INIT", "Setting up {}.". format(self.name()),
+ timestamp = self.timestamp)
+ self.computeScaleFactor()
+ self.computeSnapshots()
+ if self.trainedModel is None:
+ self.trainedModel = tModel()
+ self.trainedModel.verbosity = self.verbosity
+ self.trainedModel.timestamp = self.timestamp
+ data = TrainedModelData(self.trainedModel.name(), self.mu0,
+ np.copy(self.samplingEngine.samples),
+ self.HFEngine.rescalingExp)
+ data.polytype = self.polybasis
+ data.scaleFactor = self.scaleFactor
+ data.mus = np.copy(self.mus)
+ self.trainedModel.data = data
+ else:
+ self.trainedModel.data.projMat = np.copy(
+ self.samplingEngine.samples)
+ if self.N > 0:
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:
- fitVander = self._vander(self.radiusPade(self.mus), self.M)
- fitOut = customFit(fitVander, Qevaldiag, w = self.ws,
- full = True, rcond = self.interpRcond)
+ verbosityDepth("INIT", "Starting computation of denominator.",
+ timestamp = self.timestamp)
+ TE = polyvander[self.polybasis](self.radiusPade(self.mus), self.E)
+ TE = (TE.T * self.ws).T
+ while self.N > 0:
+ RHS = np.zeros(self.E + 1)
+ RHS[-1] = 1.
+ fitOut = customFit(TE.T, RHS, full = True,
+ rcond = self.interpRcond)
if self.verbosity >= 5:
+ condfit = fitOut[1][2][0] / fitOut[1][2][-1]
verbosityDepth("MAIN", ("Fitting {} samples with degree "
"{} through {}... Conditioning of "
"LS system: {:.4e}.").format(
- 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
+ self.S, self.E,
+ polyfitname[self.polybasis],
+ condfit),
+ timestamp = self.timestamp)
+ 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)
+ RROMPyWarning(("Polyfit is poorly conditioned.\nReducing "
+ "{}{}E from {} to {}.").format(strN, strM,
+ self.E, Enew))
+ newParams = {"N" : Nnew, "M" : Mnew, "E" : Enew}
+ self.approxParameters = newParams
+ TE = TE[:, : self.E + 1]
+ continue
+ Ghalf = (TE[:, : self.N + 1].T * fitOut[0]).T
+ if self.POD:
+ self.Ghalf = self.samplingEngine.RPOD.dot(Ghalf)
+ ev, eV = self.findeveVGQR()
+ else:
+ self.Ghalf = self.samplingEngine.samples.dot(Ghalf)
+ ev, eV = self.findeveVGExplicit()
+ newParams = checkRobustTolerance(ev, self.E, self.robustTol)
+ if not newParams:
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
- 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)
+ self.approxParameters = newParams
+ if self.N <= 0:
+ self._N = 0
+ eV = np.ones((1, 1))
+ Q = eV[:, 0]
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.
+ verbosityDepth("DEL", "Done computing denominator.",
+ timestamp = self.timestamp)
+ else:
+ Q = np.ones(1, dtype = np.complex)
+ self.trainedModel.data.Q = np.copy(Q)
+ if self.verbosity >= 7:
+ verbosityDepth("INIT", "Starting computation of numerator.",
+ timestamp = self.timestamp)
- 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)
+ Qevaldiag = np.diag(self.trainedModel.getQVal(self.mus))
+ while self.M >= 0:
+ fitVander = polyvander[self.polybasis](self.radiusPade(self.mus),
+ self.M)
+ fitOut = customFit(fitVander, Qevaldiag, w = self.ws, full = True,
+ rcond = self.interpRcond)
+ if self.verbosity >= 5:
+ condfit = fitOut[1][2][0] / fitOut[1][2][-1]
+ verbosityDepth("MAIN", ("Fitting {} samples with degree {} "
+ "through {}... Conditioning of LS "
+ "system: {:.4e}.").format(
+ self.S, self.M,
+ polyfitname[self.polybasis],
+ condfit),
+ timestamp = self.timestamp)
+ if fitOut[1][1] == self.M + 1:
+ P = fitOut[0].T
+ break
+ RROMPyWarning(("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
+ if self.M <= 0:
+ raise RROMPyException(("Instability in computation of numerator. "
+ "Aborting."))
+ P = np.atleast_2d(P)
+ if self.POD:
+ P = self.samplingEngine.RPOD.dot(P)
+ self.trainedModel.data.P = np.copy(P)
+ if self.verbosity >= 7:
+ verbosityDepth("DEL", "Done computing numerator.",
+ timestamp = self.timestamp)
+ self.trainedModel.data.approxParameters = copy(self.approxParameters)
+ if self.verbosity >= 5:
+ verbosityDepth("DEL", "Done setting up approximant.",
+ timestamp = self.timestamp)
- def getPVal(self, mu:List[complex]):
+ def findeveVGExplicit(self, verbOutput : int = 5) -> Tuple[Np1D, Np2D]:
"""
- Evaluate Pade' numerator at arbitrary parameter.
-
- Args:
- mu: Target parameter.
+ Compute explicitly eigenvalues and eigenvectors of Pade' denominator
+ matrix.
"""
- self.setupApprox()
+ modeAssert(self._mode, message = "Cannot solve eigenvalue problem.")
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()
+ verbosityDepth("INIT", "Building gramian matrix.",
+ timestamp = self.timestamp)
+ self.G = self.HFEngine.innerProduct(self.Ghalf, self.Ghalf)
if self.verbosity >= 10:
- verbosityDepth("DEL", " Done.", inline = True)
- return p
+ verbosityDepth("DEL", "Done building gramian.",
+ timestamp = self.timestamp)
+ if self.verbosity >= 7:
+ verbosityDepth("INIT", ("Solving eigenvalue problem for gramian "
+ "matrix."), timestamp = self.timestamp)
+ ev, eV = np.linalg.eigh(self.G)
+ if self.verbosity >= verbOutput:
+ 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),
+ timestamp = self.timestamp)
+ if self.verbosity >= 7:
+ verbosityDepth("DEL", "Done solving eigenvalue problem.",
+ timestamp = self.timestamp)
+ return ev, eV
- def getQVal(self, mu:List[complex]):
+ def findeveVGQR(self, verbOutput : int = 5) -> Tuple[Np1D, Np2D]:
"""
- Evaluate Pade' denominator at arbitrary parameter.
-
- Args:
- mu: Target parameter.
+ Compute eigenvalues and eigenvectors of Pade' denominator matrix
+ through SVD of R factor.
"""
- 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
+ if self.verbosity >= 7:
+ verbosityDepth("INIT", ("Solving svd for square root of gramian "
+ "matrix."), timestamp = self.timestamp)
+ _, s, eV = np.linalg.svd(self.Ghalf, full_matrices = False)
+ ev = s[::-1]
+ eV = eV[::-1, :].T.conj()
+ if self.verbosity >= verbOutput:
+ 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),
+ timestamp = self.timestamp)
+ if self.verbosity >= 7:
+ verbosityDepth("DEL", "Done solving eigenvalue problem.",
+ timestamp = self.timestamp)
+ return ev, eV
- def evalApproxReduced(self, mu:complex):
+ def radiusPade(self, mu:Np1D, mu0 : float = None) -> float:
"""
- Evaluate Pade' approximant at arbitrary parameter.
+ Compute translated radius to be plugged into Pade' approximant.
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.
+ mu: Parameter(s) 1.
+ mu0: Parameter(s) 2. If None, set to self.mu0.
- Args:
- mu: Target parameter.
+ Returns:
+ Translated radius to be plugged into Pade' approximant.
"""
- self.evalApproxReduced(mu)
- self.uApp = self.samplingEngine.samples.dot(self.uAppReduced)
+ return self.trainedModel.radiusPade(mu, mu0)
def getResidues(self) -> Np1D:
"""
- Obtain norm of approximant residues.
+ Obtain approximant residues.
Returns:
- Numpy vector of norms of residues.
- """
- poles = self.getPoles()
- Pvals = self.samplingEngine.samples.dot(self.getPVal(poles))
- Qder = self._valder(self.radiusPade(poles), self.Q)
- return Pvals / Qder
-
- def getPoles(self) -> Np1D:
- """
- Obtain approximant poles.
-
- Returns:
- Numpy complex vector of poles.
+ Matrix with residues as columns.
"""
self.setupApprox()
- return self.HFEngine.rescalingInv(
- self.scaleFactor * self._roots(self.Q)
- + self.HFEngine.rescaling(self.mu0))
+ if self.verbosity >= 20:
+ verbosityDepth("INIT", "Computing residues of model.",
+ timestamp = self.timestamp)
+ poles = self.getPoles()
+ Pvals = self.trainedModel.data.projMat.dot(self.getPVal(poles))
+ Qder = polyvalder[self.polybasis](self.radiusPade(poles),
+ self.trainedModel.data.Q)
+ residues = Pvals / Qder
+ if self.verbosity >= 20:
+ verbosityDepth("DEL", "Done computing residues.",
+ timestamp = self.timestamp)
+ return residues
diff --git a/rrompy/reduction_methods/lagrange/approximant_lagrange_rb.py b/rrompy/reduction_methods/lagrange/approximant_lagrange_rb.py
index f74db9b..e96c8d7 100644
--- a/rrompy/reduction_methods/lagrange/approximant_lagrange_rb.py
+++ b/rrompy/reduction_methods/lagrange/approximant_lagrange_rb.py
@@ -1,292 +1,212 @@
# 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.reduction_methods.trained_model import TrainedModelRB as tModel
+from rrompy.reduction_methods.trained_model import TrainedModelData
+from rrompy.reduction_methods.base.rb_utils import projectAffineDecomposition
+from rrompy.utilities.base.types import Np1D, Np2D, List, Tuple, DictAny, HFEng
from rrompy.utilities.base import purgeDict, verbosityDepth
-from rrompy.utilities.warning_manager import warn
+from rrompy.utilities.exception_manager import RROMPyWarning
__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):
+ verbosity : int = 10, timestamp : bool = True):
self._preInit()
self._addParametersToList(["R"])
super().__init__(HFEngine = HFEngine, mu0 = mu0,
approxParameters = approxParameters,
homogeneized = homogeneized,
- verbosity = verbosity)
+ verbosity = verbosity, timestamp = timestamp)
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)
+ verbosityDepth("INIT", "Computing affine blocks of system.",
+ timestamp = self.timestamp)
+ self.As = self.HFEngine.affineLinearSystemA(self.mu0)
+ self.bs = self.HFEngine.affineLinearSystemb(self.mu0,
+ self.homogeneized)
if self.verbosity >= 10:
- verbosityDepth("DEL", "Done computing affine blocks.")
+ verbosityDepth("DEL", "Done computing affine blocks.",
+ timestamp = self.timestamp)
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"):
+ elif hasattr(self, "_R") and self._R is not None:
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.")
+ if R < 0: raise RROMPyException("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.")
+ if hasattr(self, "_S") and self.S < self.R:
+ RROMPyWarning("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.checkComputedApprox():
+ return
if self.verbosity >= 5:
- verbosityDepth("INIT",
- "Solving reduced model for mu = {}.".format(mu),
- end = "")
- uRB = np.linalg.solve(ARBmu, bRBmu)
+ verbosityDepth("INIT", "Setting up {}.". format(self.name()),
+ timestamp = self.timestamp)
+ self.computeSnapshots()
+ if self.verbosity >= 7:
+ verbosityDepth("INIT", "Computing projection matrix.",
+ timestamp = self.timestamp)
+ if self.POD:
+ U, _, _ = np.linalg.svd(self.samplingEngine.RPOD,
+ full_matrices = False)
+ pMat = self.samplingEngine.samples.dot(U[:, : self.R])
+ else:
+ pMat = self.samplingEngine.samples[:, : self.R]
+
+ if self.trainedModel is None:
+ self.trainedModel = tModel()
+ self.trainedModel.verbosity = self.verbosity
+ self.trainedModel.timestamp = self.timestamp
+ data = TrainedModelData(self.trainedModel.name(), self.mu0,
+ np.copy(pMat), self.HFEngine.rescalingExp)
+ data.thetaAs = self.HFEngine.affineWeightsA(self.mu0)
+ data.thetabs = self.HFEngine.affineWeightsb(self.mu0,
+ self.homogeneized)
+ data.ARBs, data.bRBs = self.assembleReducedSystem(pMat)
+ data.mus = np.copy(self.mus)
+ self.trainedModel.data = data
+ else:
+ pMatOld = self.trainedModel.data.projMat
+ Sold = pMatOld.shape[1]
+ ARBs, bRBs = self.assembleReducedSystem(pMat[:, Sold :], pMatOld)
+ self.trainedModel.data.ARBs = ARBs
+ self.trainedModel.data.bRBs = bRBs
+ self.trainedModel.data.projMat = np.copy(pMat)
+ if self.verbosity >= 7:
+ verbosityDepth("DEL", "Done computing projection matrix.",
+ timestamp = self.timestamp)
+ self.trainedModel.data.approxParameters = copy(self.approxParameters)
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.
+ verbosityDepth("DEL", "Done setting up approximant.",
+ timestamp = self.timestamp)
- 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))
+ def assembleReducedSystem(self, pMat : Np2D = None, pMatOld : Np2D = None)\
+ -> Tuple[List[Np2D], List[Np1D]]:
+ """Build affine blocks of RB linear system through projections."""
+ if pMat is None:
+ self.setupApprox()
+ ARBs = self.trainedModel.data.ARBs
+ bRBs = self.trainedModel.data.bRBs
+ else:
+ if self.verbosity >= 10:
+ verbosityDepth("INIT", "Projecting affine terms of HF model.",
+ timestamp = self.timestamp)
+ ARBsOld = None if pMatOld is None else self.trainedModel.data.ARBs
+ bRBsOld = None if pMatOld is None else self.trainedModel.data.bRBs
+ ARBs, bRBs = projectAffineDecomposition(self.As, self.bs, pMat,
+ ARBsOld, bRBsOld, pMatOld)
+ if self.verbosity >= 10:
+ verbosityDepth("DEL", "Done projecting affine terms.",
+ timestamp = self.timestamp)
+ return ARBs, bRBs
diff --git a/rrompy/reduction_methods/lagrange/generic_approximant_lagrange.py b/rrompy/reduction_methods/lagrange/generic_approximant_lagrange.py
index 9503fff..b443b62 100644
--- a/rrompy/reduction_methods/lagrange/generic_approximant_lagrange.py
+++ b/rrompy/reduction_methods/lagrange/generic_approximant_lagrange.py
@@ -1,209 +1,196 @@
# 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.exception_manager import RROMPyException, modeAssert
__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].
+ - 'S': total number of samples current approximant relies upon.
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.
+ - 'S': total number of snapshots current approximant relies upon.
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):
+ verbosity : int = 10, timestamp : bool = True):
self._preInit()
- self._addParametersToList(["S", "sampler"])
+ self._addParametersToList(["S"])
super().__init__(HFEngine = HFEngine, mu0 = mu0,
approxParameters = approxParameters,
homogeneized = homogeneized,
- verbosity = verbosity)
+ verbosity = verbosity, timestamp = timestamp)
+ from rrompy.utilities.parameter_sampling import QuadratureSampler
+ self.sampler = QuadratureSampler([0., 1.], "UNIFORM")
+ del QuadratureSampler
self._postInit()
def setupSampling(self):
"""Setup sampling engine."""
- if not hasattr(self, "POD"): return
+ modeAssert(self._mode, message = "Cannot setup sampling engine.")
+ if not hasattr(self, "_POD") or self._POD is None: return
if self.POD:
from rrompy.sampling.linear_problem.sampling_engine_lagrange_pod \
import SamplingEngineLagrangePOD
super().setupSampling(SamplingEngineLagrangePOD)
else:
from rrompy.sampling.linear_problem.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
+ 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.")
+ raise RROMPyException("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"],
+ approxParametersCopy = purgeDict(approxParameters, ["S"],
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:
+ elif not hasattr(self, "_S") or self._S is None:
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
+ if S <= 0: raise RROMPyException("S must be positive.")
+ if hasattr(self, "_S") and self._S is not None: 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'):
+ raise RROMPyException("Sampler type not recognized.")
+ if hasattr(self, '_sampler') and self._sampler is not None:
samplerOld = self.sampler
self._sampler = sampler
- self._approxParameters["sampler"] = self.sampler
+ self._approxParameters["sampler"] = self.sampler.__str__()
if not 'samplerOld' in locals() or samplerOld != self.sampler:
self.resetSamples()
def computeSnapshots(self):
"""Compute snapshots of solution map."""
+ modeAssert(self._mode, message = "Cannot start snapshot computation.")
if self.samplingEngine.samples is None:
if self.verbosity >= 5:
- verbosityDepth("INIT", "Starting computation of snapshots.")
+ verbosityDepth("INIT", "Starting computation of snapshots.",
+ timestamp = self.timestamp)
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())
+ verbosityDepth("DEL", "Done computing snapshots.",
+ timestamp = self.timestamp)
def normApprox(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().normApprox(mu, homogeneized)
return np.linalg.norm(self.getApproxReduced(mu))
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]))
+ modeAssert(self._mode, message = "Cannot compute rescaling factor.")
+ self.scaleFactor = .5 * np.abs(np.power(self.sampler.lims[0][0],
+ self.HFEngine.rescalingExp)
+ - np.power(self.sampler.lims[1][0],
+ self.HFEngine.rescalingExp))
+
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 3767238..506f500 100644
--- a/rrompy/reduction_methods/lagrange_greedy/approximant_lagrange_greedy_pade.py
+++ b/rrompy/reduction_methods/lagrange_greedy/approximant_lagrange_greedy_pade.py
@@ -1,606 +1,514 @@
# 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,
- setupFitCallables)
from .generic_approximant_lagrange_greedy import (
GenericApproximantLagrangeGreedy)
+from rrompy.reduction_methods.base.fit_utils import (polybases, polyvander,
+ polydomcoeff, polyfitname)
from rrompy.reduction_methods.lagrange import ApproximantLagrangePade
+from rrompy.reduction_methods.trained_model import TrainedModelPade as tModel
+from rrompy.reduction_methods.trained_model import TrainedModelData
from rrompy.utilities.base.types import DictAny, List, HFEng
from rrompy.utilities.base import purgeDict, verbosityDepth, customFit
-from rrompy.utilities.warning_manager import warn
+from rrompy.utilities.exception_manager import RROMPyWarning
+from rrompy.utilities.exception_manager import RROMPyException
__all__ = ['ApproximantLagrangePadeGreedy']
class ApproximantLagrangePadeGreedy(GenericApproximantLagrangeGreedy,
ApproximantLagrangePade):
"""
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;
- 'errorEstimatorKind': kind of error estimator; available values
include 'EXACT' and 'SIMPLIFIED'; defaults to 'SIMPLIFIED';
- '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;
- 'errorEstimatorKind': kind of error estimator;
- '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.
errorEstimatorKind: kind of error estimator.
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):
+ verbosity : int = 10, timestamp : bool = True):
self._preInit()
- self._addParametersToList(["basis", "Delta", "errorEstimatorKind",
+ self._addParametersToList(["polybasis", "Delta", "errorEstimatorKind",
"interpRcond", "robustTol"])
super().__init__(HFEngine = HFEngine, mu0 = mu0,
approxParameters = approxParameters,
homogeneized = homogeneized,
- verbosity = verbosity)
+ verbosity = verbosity, timestamp = timestamp)
+ if self.verbosity >= 7:
+ verbosityDepth("INIT", "Computing Taylor blocks of system.",
+ timestamp = self.timestamp)
+ nAs = self.HFEngine.nAs - 1
+ nbs = max(self.HFEngine.nbs, (nAs + 1) * self.homogeneized)
+ self.As = [self.HFEngine.A(self.mu0, j + 1) for j in range(nAs)]
+ self.bs = [self.HFEngine.b(self.mu0, j, self.homogeneized)
+ for j in range(nbs)]
+ if self.verbosity >= 7:
+ verbosityDepth("DEL", "Done computing Taylor blocks.",
+ timestamp = self.timestamp)
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",
+ approxParametersCopy = purgeDict(approxParameters, ["polybasis",
+ "Delta",
"errorEstimatorKind",
"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:
+ elif not hasattr(self, "_Delta") or self._Delta is None:
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 "polybasis" in keyList:
+ self.polybasis = approxParameters["polybasis"]
+ elif not hasattr(self, "_polybasis") or self._polybasis is None:
+ self.polybasis = "MONOMIAL"
if "errorEstimatorKind" in keyList:
self.errorEstimatorKind = approxParameters["errorEstimatorKind"]
- elif hasattr(self, "errorEstimatorKind"):
- self.errorEstimatorKind = self.errorEstimatorKind
- else:
+ elif (not hasattr(self, "_errorEstimatorKind")
+ or self.errorEstimatorKind is None):
self.errorEstimatorKind = "SIMPLIFIED"
if "interpRcond" in keyList:
self.interpRcond = approxParameters["interpRcond"]
- elif hasattr(self, "interpRcond"):
- self.interpRcond = self.interpRcond
- else:
+ elif not hasattr(self, "interpRcond") or self.interpRcond is None:
self.interpRcond = None
if "robustTol" in keyList:
self.robustTol = approxParameters["robustTol"]
- elif hasattr(self, "robustTol"):
- self.robustTol = self.robustTol
- else:
+ elif not hasattr(self, "_robustTol") or self._robustTol is None:
self.robustTol = 0
+ @property
+ def polybasis(self):
+ """Value of polybasis."""
+ return self._polybasis
+ @polybasis.setter
+ def polybasis(self, polybasis):
+ try:
+ polybasis = polybasis.upper().strip().replace(" ","")
+ if polybasis not in polybases:
+ raise RROMPyException("Sample type not recognized.")
+ self._polybasis = polybasis
+ except:
+ RROMPyWarning(("Prescribed polybasis not recognized. Overriding "
+ "to 'MONOMIAL'."))
+ self._polybasis = "MONOMIAL"
+ self._approxParameters["polybasis"] = self.polybasis
+
@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.")
+ raise RROMPyException("Delta must be an integer.")
if Delta < 0:
- warn(("Error estimator unreliable for Delta < 0. Overloading of "
- "errorEstimator is suggested."))
+ RROMPyWarning(("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 may be unreliable for selected Delta. Suggested "
- "minimal value of Delta: {}.").format(Deltamin))
+ RROMPyWarning(("Method may be unreliable for selected Delta. "
+ "Suggested minimal value of Delta: {}.").format(
+ Deltamin))
self._Delta = Delta
self._approxParameters["Delta"] = self.Delta
@property
def errorEstimatorKind(self):
"""Value of errorEstimatorKind."""
return self._errorEstimatorKind
@errorEstimatorKind.setter
def errorEstimatorKind(self, errorEstimatorKind):
errorEstimatorKind = errorEstimatorKind.upper()
if errorEstimatorKind not in ["EXACT", "SIMPLIFIED"]:
- warn(("Error estimator kind not recognized. Overriding to "
- "'SIMPLIFIED'."))
+ RROMPyWarning(("Error estimator kind not recognized. Overriding "
+ "to 'SIMPLIFIED'."))
errorEstimatorKind = "SIMPLIFIED"
self._errorEstimatorKind = errorEstimatorKind
self._approxParameters["errorEstimatorKind"] = self.errorEstimatorKind
@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."))
+ RROMPyWarning(("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.")
+ raise RROMPyException("nTestPoints must be an integer.")
nTestPoints = np.int(nTestPoints)
- if hasattr(self, "nTestPoints"):
+ if hasattr(self, "_nTestPoints") and self.nTestPoints is not None:
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.resbb = None
- self.resAb = None
- self.resAA = None
- self.As = None
- self.bs = None
-
def errorEstimator(self, mus:List[np.complex]) -> List[np.complex]:
"""Standard residual-based error estimator."""
self.setupApprox()
- self.initEstNormer()
- PM = self.P[:, -1]
+ PM = self.trainedModel.data.P[:, -1]
if np.any(np.isnan(PM)) or np.any(np.isinf(PM)):
err = np.empty(len(mus))
err[:] = np.inf
return err
nAs = self.HFEngine.nAs - 1
nbs = max(self.HFEngine.nbs - 1, nAs * self.homogeneized)
radiusmus = self.radiusPade(mus)
radiussmus = self.radiusPade(self.mus)
musTile = np.tile(radiusmus.reshape(-1, 1), [1, self.S])
smusCol = radiussmus.reshape(1, -1)
num = np.prod(musTile[:, : self.S] - smusCol, axis = 1)
- den = self.getQVal(mus)
+ den = self.trainedModel.getQVal(mus)
self.assembleReducedResidualBlocks()
vanderBase = np.polynomial.polynomial.polyvander(radiusmus,
max(nAs, nbs)).T
radiusb0 = vanderBase[: nbs + 1, :]
# 'ij,jk,ik->k', resbb, radiusb0, radiusb0.conj()
b0resb0 = np.sum(self.resbb.dot(radiusb0) * radiusb0.conj(), axis = 0)
RHSnorms = np.power(np.abs(b0resb0), .5)
vanderBase = vanderBase[: -1, :]
delta = self.S - self.N - 1
nbsEff = max(0, nbs - delta)
if self.errorEstimatorKind == "SIMPLIFIED":
radiusA = np.tensordot(PM, vanderBase[: nAs, :], 0)
if delta == 0:
- radiusb = np.abs(self.Q[-1]) * radiusb0[: -1, :]
+ radiusb = (np.abs(self.trainedModel.data.Q[-1])
+ * radiusb0[: -1, :])
else: #if self.errorEstimatorKind == "EXACT":
momentQ = np.zeros(nbsEff, dtype = np.complex)
momentQu = np.zeros((self.S, nAs), dtype = np.complex)
radiusbTen = np.zeros((nbsEff, nbsEff, len(mus)),
dtype = np.complex)
radiusATen = np.zeros((nAs, nAs, len(mus)), dtype = np.complex)
if nbsEff > 0:
- momentQ[0] = self.Q[-1]
+ momentQ[0] = self.trainedModel.data.Q[-1]
radiusbTen[0, :, :] = vanderBase[: nbsEff, :]
- momentQu[:, 0] = self.P[:, -1]
+ momentQu[:, 0] = self.trainedModel.data.P[:, -1]
radiusATen[0, :, :] = vanderBase[: nAs, :]
- Qvals = self.getQVal(self.mus)
+ Qvals = self.trainedModel.getQVal(self.mus)
for k in range(1, max(nAs, nbs * (nbsEff > 0))):
Qvals = Qvals * radiussmus
if k > delta and k < nbs:
momentQ[k - delta] = self._fitinv.dot(Qvals)
radiusbTen[k - delta, k :, :] = (
radiusbTen[0, : delta - k, :])
if k < nAs:
momentQu[:, k] = Qvals * self._fitinv
radiusATen[k, k :, :] = radiusATen[0, : - k, :]
if self.POD and nAs > 1:
momentQu[:, 1 :] = self.samplingEngine.RPOD.dot(
momentQu[:, 1 :])
radiusA = np.tensordot(momentQu, radiusATen, 1)
if nbsEff > 0:
radiusb = np.tensordot(momentQ, radiusbTen, 1)
if ((self.errorEstimatorKind == "SIMPLIFIED" and delta == 0)
or (self.errorEstimatorKind == "EXACT" and nbsEff > 0)):
# 'ij,jk,ik->k', resbb, radiusb, radiusb.conj()
ff = np.sum(self.resbb[delta + 1 :, delta + 1 :].dot(radiusb)
* radiusb.conj(), axis = 0)
# 'ijk,jkl,il->l', resAb, radiusA, radiusb.conj()
Lf = np.sum(np.tensordot(self.resAb[delta :, :, :], radiusA, 2)
* radiusb.conj(), axis = 0)
else:
ff, Lf = 0., 0.
# 'ijkl,klt,ijt->t', resAA, radiusA, radiusA.conj()
LL = np.sum(np.tensordot(self.resAA, radiusA, 2) * radiusA.conj(),
axis = (0, 1))
jOpt = np.power(np.abs(ff - 2. * np.real(Lf) + LL), .5)
- return self._domcoeff(self.S - 1) * jOpt * np.abs(num / den) / RHSnorms
+ return (polydomcoeff[self.polybasis](self.S - 1) * jOpt
+ * np.abs(num / den) / RHSnorms)
def setupApprox(self):
"""
Compute Pade' interpolant.
SVD-based robust eigenvalue management.
"""
- if not self.checkComputedApprox():
+ if self.checkComputedApprox():
+ return
+ if self.verbosity >= 5:
+ verbosityDepth("INIT", "Setting up {}.". format(self.name()),
+ timestamp = self.timestamp)
+ self.computeScaleFactor()
+ self.greedy()
+ self._M = self.S - 1
+ self._N = self.S - 1
+ if self.Delta < 0:
+ self._M += self.Delta
+ else:
+ self._N -= self.Delta
+
+ if self.trainedModel is None:
+ self.trainedModel = tModel()
+ self.trainedModel.verbosity = self.verbosity
+ self.trainedModel.timestamp = self.timestamp
+ data = TrainedModelData(self.trainedModel.name(), self.mu0,
+ np.copy(self.samplingEngine.samples),
+ self.HFEngine.rescalingExp)
+ data.polytype = self.polybasis
+ data.scaleFactor = self.scaleFactor
+ data.mus = np.copy(self.mus)
+ self.trainedModel.data = data
+ else:
+ self.trainedModel.data.projMat = np.copy(
+ self.samplingEngine.samples)
+ self.trainedModel.data.mus = np.copy(self.mus)
+ if min(self.M, self.N) < 0:
if self.verbosity >= 5:
- verbosityDepth("INIT", "Setting up {}.". format(self.name()))
- self.computeScaleFactor()
- 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.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)
+ verbosityDepth("MAIN", "Minimal sample size not achieved.",
+ timestamp = self.timestamp)
+ Q = np.empty(max(self.N, 0) + 1, dtype = np.complex)
+ P = np.empty((len(self.mus), max(self.M, 0) + 1),
+ dtype = np.complex)
+ Q[:] = np.nan
+ P[:] = np.nan
+ self.trainedModel.data.Q = np.copy(Q)
+ self.trainedModel.data.P = np.copy(P)
+ self.trainedModel.data.approxParameters = copy(
+ self.approxParameters)
+ if self.verbosity >= 5:
+ verbosityDepth("DEL", "Aborting computation of approximant.",
+ timestamp = self.timestamp)
+ return
+ if self.N > 0:
+ if self.verbosity >= 7:
+ verbosityDepth("INIT", "Starting computation of denominator.",
+ timestamp = self.timestamp)
+ TS = polyvander[self.polybasis](self.radiusPade(self.mus),
+ self.S - 1).T
+ RHS = np.zeros(self.S)
+ RHS[-1] = 1.
+ fitOut = customFit(TS, RHS, full = True, rcond = self.interpRcond)
+ if self.verbosity >= 2:
+ condfit = fitOut[1][2][0] / fitOut[1][2][-1]
+ verbosityDepth("MAIN", ("Fitting {} samples with degree {} "
+ "through {}... Conditioning of "
+ "system: {:.4e}.").format(
+ self.S, self.S - 1,
+ polyfitname[self.polybasis],
+ condfit),
+ timestamp = self.timestamp)
+ if fitOut[1][1] < self.S:
+ RROMPyWarning(("Polyfit is poorly conditioned. Starting "
+ "preemptive termination of computation of "
+ "approximant."))
+ Q = np.empty(max(self.N, 0) + 1, dtype = np.complex)
+ P = np.empty((len(self.mus), max(self.M, 0) + 1),
+ dtype = np.complex)
+ Q[:] = np.nan
+ P[:] = np.nan
+ self.trainedModel.data.Q = np.copy(Q)
+ self.trainedModel.data.P = np.copy(P)
+ self.trainedModel.data.approxParameters = copy(
+ self.approxParameters)
+ if self.verbosity >= 7:
+ verbosityDepth("DEL",
+ "Aborting computation of denominator.",
+ timestamp = self.timestamp)
if self.verbosity >= 5:
- verbosityDepth("DEL", ("Aborting computation of "
- "approximant.\n"))
+ verbosityDepth("DEL",
+ "Aborting computation of approximant.",
+ timestamp = self.timestamp)
return
- self.greedy()
- if self.N > 0:
- if self.verbosity >= 7:
- verbosityDepth("INIT", ("Starting computation of "
- "denominator."))
- TS = self._vander(self.radiusPade(self.mus), self.S - 1)
- 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, 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.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
- self._fitinv = fitOut[0]
- while self.N > 0:
- G = (TS[:, : self.N + 1].T * self._fitinv).T
- if self.POD:
- if self.verbosity >= 7:
- verbosityDepth("INIT", ("Solving svd for square "
- "root of gramian matrix."))
- 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."))
- 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 solving eigenvalue "
- "problem."))
- Nstable = np.sum(np.abs(ev) >= self.robustTol
- * np.linalg.norm(ev))
- if self.N <= Nstable: break
- if self.verbosity >= 2:
- verbosityDepth("MAIN", ("Smallest {} eigenvalues "
- "below tolerance. Reducing N "
- "to {}.").format(
- self.N - Nstable + 1,
- Nstable))
- self._N = Nstable
- if self.N <= 0:
- 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:
- fitVander = self._vander(self.radiusPade(self.mus), self.M)
- w = None
- if self.M == self.S - 1: w = "AUTO"
- fitOut = customFit(fitVander, Qevaldiag, full = True, w = w,
- rcond = self.interpRcond)
+ self._fitinv = fitOut[0]
+ while self.N > 0:
+ Ghalf = (TS[: self.N + 1, :] * self._fitinv).T
+ if self.POD:
+ self.Ghalf = self.samplingEngine.RPOD.dot(Ghalf)
+ ev, eV = self.findeveVGQR(2)
+ else:
+ self.Ghalf = self.samplingEngine.samples.dot(Ghalf)
+ ev, eV = self.findeveVGQR(2)
+ Nstable = np.sum(np.abs(ev) >=
+ self.robustTol * np.linalg.norm(ev))
+ if self.N <= Nstable: break
if self.verbosity >= 2:
- verbosityDepth("MAIN", ("Fitting {} samples with "
- "degree {} through {}... "
- "Conditioning of system: "
- "{:.4e}.").format(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[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)
+ verbosityDepth("MAIN", ("Smallest {} eigenvalues below "
+ "tolerance. Reducing N to {}.")\
+ .format(self.N - Nstable + 1, Nstable),
+ timestamp = self.timestamp)
+ self._N = Nstable
+ if self.N <= 0:
+ self._N = 0
+ eV = np.ones((1, 1))
+ Q = eV[:, 0]
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")
+ verbosityDepth("DEL", "Done computing denominator.",
+ timestamp = self.timestamp)
+ else:
+ Q = np.ones((1,), dtype = np.complex)
+ self.trainedModel.data.Q = np.copy(Q)
+ if self.verbosity >= 7:
+ verbosityDepth("INIT", "Starting computation of numerator.",
+ timestamp = self.timestamp)
+ Qevaldiag = np.diag(self.trainedModel.getQVal(self.mus))
+ while self.M >= 0:
+ fitVander = polyvander[self.polybasis](self.radiusPade(self.mus),
+ self.M)
+ w = None
+ if self.M == self.S - 1: w = "AUTO"
+ fitOut = customFit(fitVander, Qevaldiag, full = True, w = w,
+ rcond = self.interpRcond)
+ if self.verbosity >= 2:
+ condfit = fitOut[1][2][0] / fitOut[1][2][-1]
+ verbosityDepth("MAIN", ("Fitting {} samples with degree {} "
+ "through {}... Conditioning of "
+ "system: {:.4e}.").format(
+ self.S, self.M,
+ polyfitname[self.polybasis],
+ condfit),
+ timestamp = self.timestamp)
+ if fitOut[1][1] == self.M + 1:
+ P = fitOut[0].T
+ break
+ RROMPyWarning(("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
+ if self.M < 0:
+ raise RROMPyException(("Instability in computation of numerator. "
+ "Aborting."))
+ P = np.atleast_2d(P)
+ if self.POD:
+ P = self.samplingEngine.RPOD.dot(P)
+ self.trainedModel.data.P = np.copy(P)
+ if self.verbosity >= 7:
+ verbosityDepth("DEL", "Done computing numerator.",
+ timestamp = self.timestamp)
+ self.trainedModel.data.approxParameters = copy(self.approxParameters)
+ if self.verbosity >= 5:
+ verbosityDepth("DEL", "Done setting up approximant.",
+ timestamp = self.timestamp)
def assembleReducedResidualBlocks(self):
"""Build affine blocks of reduced linear system through projections."""
- self.initEstNormer()
- if self.As is None or self.bs is None:
- if self.verbosity >= 7:
- verbosityDepth("INIT", "Computing Taylor blocks of system.")
- nAs = self.HFEngine.nAs - 1
- nbs = max(self.HFEngine.nbs, (nAs + 1) * self.homogeneized)
- self.As = [self.HFEngine.A(self.mu0, j + 1) for j in range(nAs)]
- self.bs = [self.HFEngine.b(self.mu0, j, self.homogeneized)
- for j in range(nbs)]
- if self.verbosity >= 7:
- verbosityDepth("DEL", "Done computing Taylor blocks.")
computeResbb = self.resbb is None
computeResAb = self.resAb is None or self.resAb.shape[1] != self.S
computeResAA = self.resAA is None or self.resAA.shape[0] != self.S
- samples = self.samplingEngine.samples
+ pMat = self.trainedModel.data.projMat
+ scaling = self.trainedModel.data.scaleFactor
if computeResbb or computeResAb or computeResAA:
if self.verbosity >= 7:
- verbosityDepth("INIT", "Projecting Taylor terms of residual.")
- nAs = len(self.As)
- nbs = len(self.bs) - 1
+ verbosityDepth("INIT", "Projecting Taylor terms of residual.",
+ timestamp = self.timestamp)
if computeResbb:
- self.resbb = np.empty((nbs + 1, nbs + 1), dtype = np.complex)
- for i in range(nbs + 1):
- Mbi = self.scaleFactor ** i * self.bs[i]
- for j in range(i):
- Mbj = self.scaleFactor ** j * self.bs[j]
- self.resbb[i, j] = self.estNormer.innerProduct(Mbj,
- Mbi)
- self.resbb[i, i] = self.estNormer.innerProduct(Mbi, Mbi)
- for i in range(nbs + 1):
- for j in range(i + 1, nbs + 1):
- self.resbb[i, j] = self.resbb[j][i].conj()
+ self.assembleReducedResidualBlocksbb(self.bs, pMat, scaling)
if computeResAb:
- if self.resAb is None:
- self.resAb = np.empty((nbs, self.S, nAs),
- dtype = np.complex)
- for i in range(nbs):
- Mbi = self.scaleFactor ** (i + 1) * self.bs[i + 1]
- for j in range(nAs):
- MAj = (self.scaleFactor ** (j + 1)
- * self.As[j].dot(samples))
- self.resAb[i, :, j] = self.estNormer.innerProduct(
- MAj, Mbi)
- else:
- Sold = self.resAb.shape[1]
- if Sold > self.S:
- self.resAb = self.resAb[:, : self.S, :]
- else:
- resAbNew = np.empty((nbs, self.S, nAs),
- dtype = np.complex)
- resAbNew[:, : Sold, :] = self.resAb
- self.resAb = resAbNew
- for i in range(nbs):
- Mbi = self.scaleFactor ** (i + 1) * self.bs[i + 1]
- for j in range(nAs):
- MAj = (self.scaleFactor ** (j + 1)
- * self.As[j].dot(samples[:, Sold :]))
- self.resAb[i, Sold :, j] = (
- self.estNormer.innerProduct(MAj, Mbi))
+ self.assembleReducedResidualBlocksAb(self.As, self.bs[1 :],
+ pMat, scaling)
if computeResAA:
- if self.resAA is None:
- self.resAA = np.empty((self.S, nAs, self.S, nAs),
- dtype = np.complex)
- for i in range(nAs):
- MAi = (self.scaleFactor ** (i + 1)
- * self.As[i].dot(samples))
- for j in range(i):
- MAj = (self.scaleFactor ** (j + 1)
- * self.As[j].dot(samples))
- self.resAA[:, i, :, j] = (
- self.estNormer.innerProduct(MAj, MAi))
- self.resAA[:, i, :, i] = self.estNormer.innerProduct(
- MAi, MAi)
- for i in range(nAs):
- for j in range(i + 1, nAs):
- self.resAA[:, i, :, j] = (
- self.resAA[:, j, :, i].conj())
- else:
- Sold = self.resAA.shape[0]
- if Sold > self.S:
- self.resAA = self.resAA[: self.S, :, : self.S, :]
- else:
- resAANew = np.empty((self.S, nAs, self.S, nAs),
- dtype = np.complex)
- resAANew[: Sold, :, : Sold, :] = self.resAA
- self.resAA = resAANew
- for i in range(nAs):
- MAi = (self.scaleFactor ** (i + 1)
- * self.As[i].dot(samples))
- for j in range(i):
- MAj = (self.scaleFactor ** (j + 1)
- * self.As[j].dot(samples))
- self.resAA[: Sold, i, Sold :, j] = (
- self.estNormer.innerProduct(MAj[:, Sold :],
- MAi[:, : Sold]))
- self.resAA[Sold :, i, : Sold, j] = (
- self.estNormer.innerProduct(MAj[:, : Sold],
- MAi[:, Sold :]))
- self.resAA[Sold :, i, Sold :, j] = (
- self.estNormer.innerProduct(MAj[:, Sold :],
- MAi[:, Sold :]))
- self.resAA[: Sold, i, Sold :, i] = (
- self.estNormer.innerProduct(MAi[:, Sold :],
- MAi[:, : Sold]))
- self.resAA[Sold :, i, : Sold, i] = (
- self.resAA[: Sold, i, Sold :, i].conj().T)
- self.resAA[Sold :, i, Sold :, i] = (
- self.estNormer.innerProduct(MAi[:, Sold :],
- MAi[:, Sold :]))
- for i in range(nAs):
- for j in range(i + 1, nAs):
- self.resAA[:, i, :, j] = (
- self.resAA[:, j, :, i].conj())
+ self.assembleReducedResidualBlocksAA(self.As, pMat, scaling)
if self.verbosity >= 7:
- verbosityDepth("DEL", ("Done setting up Taylor "
- "decomposition of residual."))
+ verbosityDepth("DEL", ("Done setting up Taylor "
+ "decomposition of residual."),
+ timestamp = self.timestamp)
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 0fe2e6f..0d24112 100644
--- a/rrompy/reduction_methods/lagrange_greedy/approximant_lagrange_greedy_rb.py
+++ b/rrompy/reduction_methods/lagrange_greedy/approximant_lagrange_greedy_rb.py
@@ -1,311 +1,241 @@
# 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.reduction_methods.trained_model import TrainedModelRB as tModel
+from rrompy.reduction_methods.trained_model import TrainedModelData
from rrompy.utilities.base.types import DictAny, HFEng, List
from rrompy.utilities.base import verbosityDepth
-from rrompy.utilities.warning_manager import warn
+from rrompy.utilities.exception_manager import RROMPyException
__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):
+ verbosity : int = 10, timestamp : bool = True):
self._preInit()
super().__init__(HFEngine = HFEngine, mu0 = mu0,
approxParameters = approxParameters,
homogeneized = homogeneized,
- verbosity = verbosity)
+ verbosity = verbosity, timestamp = timestamp)
+ if self.verbosity >= 10:
+ verbosityDepth("INIT", "Computing affine blocks of system.",
+ timestamp = self.timestamp)
+ self.As = self.HFEngine.affineLinearSystemA(self.mu0)
+ self.bs = self.HFEngine.affineLinearSystemb(self.mu0,
+ self.homogeneized)
+ if self.verbosity >= 10:
+ verbosityDepth("DEL", "Done computing affine blocks.",
+ timestamp = self.timestamp)
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."))
+ raise RROMPyException(("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
- 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.")
-
- 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.setupApprox()
self.assembleReducedResidualBlocks()
- nAs = self.resAA.shape[0]
+ nmus = len(mus)
+ nAs = self.resAA.shape[1]
nbs = self.resbb.shape[0]
+ thetaAs = self.trainedModel.data.thetaAs
+ thetabs = self.trainedModel.data.thetabs
+ radiusA = np.empty((self.S, nAs, nmus), dtype = np.complex)
+ radiusb = np.empty((nbs, nmus), dtype = np.complex)
+ verb = self.trainedModel.verbosity
+ self.trainedModel.verbosity = 0
+ if verb >= 5:
+ mustr = mus
+ if nmus > 2:
+ mustr = "[{} ..({}).. {}]".format(mus[0], nmus - 2,
+ mus[-1])
+ verbosityDepth("INIT", ("Computing RB solution at mu = "
+ "{}.").format(mustr),
+ timestamp = self.timestamp)
for j in range(nmus):
mu = mus[j]
- uAppReduced = self.getApproxReduced(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[i2, i1]
- 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[i2, i1, :]
- 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[i2, i1, :, :]
- err[j] = np.abs(((uAppReduced.T.conj().dot(prodAA.dot(uAppReduced))
- - 2. * np.real(prodAb.dot(uAppReduced))) / prodbb
- + 1.)[0]) ** .5
- return err
+ for i in range(nAs):
+ radiusA[:, i, j] = eval(thetaAs[i]) * self.getApproxReduced(mu)
+ for i in range(nbs):
+ radiusb[i, j] = eval(thetabs[i])
+ if verb >= 5:
+ verbosityDepth("DEL", "Done computing RB solution.",
+ timestamp = self.timestamp)
+ self.trainedModel.verbosity = verb
+ # 'ij,jk,ik->k', resbb, radiusb, radiusb.conj()
+ ff = np.sum(self.resbb.dot(radiusb) * radiusb.conj(), axis = 0)
+ # 'ijk,jkl,il->l', resAb, radiusA, radiusb.conj()
+ Lf = np.sum(np.tensordot(self.resAb, radiusA, 2) * radiusb.conj(),
+ axis = 0)
+ # 'ijkl,klt,ijt->t', resAA, radiusA, radiusA.conj()
+ LL = np.sum(np.tensordot(self.resAA, radiusA, 2) * radiusA.conj(),
+ axis = (0, 1))
+ return np.abs((LL - 2. * np.real(Lf) + ff) / ff) ** .5
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")
+ if self.checkComputedApprox():
+ return
+ if self.verbosity >= 5:
+ verbosityDepth("INIT", "Setting up {}.". format(self.name()),
+ timestamp = self.timestamp)
+ self.greedy()
+ if self.verbosity >= 7:
+ verbosityDepth("INIT", "Computing projection matrix.",
+ timestamp = self.timestamp)
+ pMat = self.samplingEngine.samples
+
+ if self.trainedModel is None:
+ self.trainedModel = tModel()
+ self.trainedModel.verbosity = self.verbosity
+ self.trainedModel.timestamp = self.timestamp
+ data = TrainedModelData(self.trainedModel.name(), self.mu0,
+ np.copy(pMat), self.HFEngine.rescalingExp)
+ data.thetaAs = self.HFEngine.affineWeightsA(self.mu0)
+ data.thetabs = self.HFEngine.affineWeightsb(self.mu0,
+ self.homogeneized)
+ data.ARBs, data.bRBs = self.assembleReducedSystem(pMat)
+ data.mus = np.copy(self.mus)
+ self.trainedModel.data = data
+ else:
+ pMatOld = self.trainedModel.data.projMat
+ Sold = pMatOld.shape[1]
+ ARBs, bRBs = self.assembleReducedSystem(pMat[:, Sold :], pMatOld)
+ self.trainedModel.data.ARBs = ARBs
+ self.trainedModel.data.bRBs = bRBs
+ self.trainedModel.data.projMat = np.copy(pMat)
+ self.trainedModel.data.mus = np.copy(self.mus)
+ if self.verbosity >= 7:
+ verbosityDepth("DEL", "Done computing projection matrix.",
+ timestamp = self.timestamp)
+ self.trainedModel.data.approxParameters = copy(self.approxParameters)
+ if self.verbosity >= 5:
+ verbosityDepth("DEL", "Done setting up approximant.",
+ timestamp = self.timestamp)
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.shape[2] != self.S
- computeResAA = self.resAA is None or self.resAA.shape[2] != self.S
- samples = self.projMat
+ computeResAb = self.resAb is None or self.resAb.shape[1] != self.S
+ computeResAA = self.resAA is None or self.resAA.shape[0] != self.S
if computeResbb or computeResAb or computeResAA:
+ pMat = self.trainedModel.data.projMat
if self.verbosity >= 7:
verbosityDepth("INIT", "Projecting affine terms of residual.",
- end = "")
- nAs = len(self.As)
- nbs = max(len(self.bs), nAs * self.homogeneized)
+ timestamp = self.timestamp)
if computeResbb:
- self.resbb = np.empty((nbs, nbs), dtype = np.complex)
- for i in range(nbs):
- Mbi = self.bs[i]
- for j in range(i):
- Mbj = self.bs[j]
- self.resbb[i, j] = self.estNormer.innerProduct(Mbj,
- Mbi)
- self.resbb[i, i] = self.estNormer.innerProduct(Mbi, Mbi)
- for i in range(nbs):
- for j in range(i + 1, nbs):
- self.resbb[i, j] = self.resbb[j][i].conj()
+ self.assembleReducedResidualBlocksbb(self.bs, pMat)
if computeResAb:
- if self.resAb is None:
- self.resAb = np.empty((nbs, nAs, self.S),
- dtype = np.complex)
- for i in range(nbs):
- Mbi = self.bs[i]
- for j in range(nAs):
- MAj = self.As[j].dot(samples)
- self.resAb[i, j, :] = self.estNormer.innerProduct(
- MAj, Mbi)
- else:
- resAbNew = np.empty((nbs, nAs, self.S), dtype = np.complex)
- Sold = self.resAb.shape[2]
- resAbNew[:, :, : Sold] = self.resAb
- self.resAb = resAbNew
- for i in range(nbs):
- Mbi = self.bs[i]
- for j in range(nAs):
- MAj = self.As[j].dot(samples[:, Sold :])
- self.resAb[i, j, Sold :] = (
- self.estNormer.innerProduct(MAj, Mbi))
+ self.assembleReducedResidualBlocksAb(self.As, self.bs, pMat)
if computeResAA:
- if self.resAA is None:
- self.resAA = np.empty((nAs, nAs, self.S, self.S),
- dtype = np.complex)
- for i in range(nAs):
- MAi = self.As[i].dot(samples)
- for j in range(i):
- MAj = self.As[j].dot(samples)
- self.resAA[i, j, :, :] = (
- self.estNormer.innerProduct(MAj, MAi))
- self.resAA[i, i, :, :] = self.estNormer.innerProduct(
- MAi, MAi)
- for i in range(nAs):
- for j in range(i + 1, nAs):
- self.resAA[i, j, :, :] = (
- self.resAA[j, i, :, :].conj())
- else:
- resAANew = np.empty((nAs, nAs, self.S, self.S),
- dtype = np.complex)
- Sold = self.resAA.shape[2]
- resAANew[:, :, : Sold, : Sold] = self.resAA
- self.resAA = resAANew
- for i in range(nAs):
- MAi = self.As[i].dot(samples)
- for j in range(i):
- MAj = self.As[j].dot(samples)
- self.resAA[i, j, : Sold, Sold :] = (
- self.estNormer.innerProduct(MAj[:, Sold :],
- MAi[:, : Sold]))
- self.resAA[i, j, Sold :, : Sold] = (
- self.estNormer.innerProduct(MAj[:, : Sold],
- MAi[:, Sold :]))
- self.resAA[i, j, Sold :, Sold :] = (
- self.estNormer.innerProduct(MAj[:, Sold :],
- MAi[:, Sold :]))
- self.resAA[i, i, : Sold, Sold :] = (
- self.estNormer.innerProduct(MAi[:, Sold :],
- MAi[:, : Sold]))
- self.resAA[i, i, Sold :, : Sold] = (
- self.resAA[i, i, : Sold, Sold :].conj().T)
- self.resAA[i, i, Sold :, Sold :] = (
- self.estNormer.innerProduct(MAi[:, Sold :],
- MAi[:, Sold :]))
- for i in range(nAs):
- for j in range(i + 1, nAs):
- self.resAA[i, j, :, :] = (
- self.resAA[j, i, :, :].conj())
+ self.assembleReducedResidualBlocksAA(self.As, pMat)
if self.verbosity >= 7:
- verbosityDepth("DEL", ("Done setting up affine "
- "decomposition of residual."))
+ verbosityDepth("DEL", ("Done setting up affine decomposition "
+ "of residual."),
+ timestamp = self.timestamp)
+
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 f478f68..d2ac3a5 100644
--- a/rrompy/reduction_methods/lagrange_greedy/generic_approximant_lagrange_greedy.py
+++ b/rrompy/reduction_methods/lagrange_greedy/generic_approximant_lagrange_greedy.py
@@ -1,504 +1,627 @@
# 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 deepcopy as copy
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.types import Np1D, Np2D, DictAny, HFEng, Tuple, List
from rrompy.utilities.base import purgeDict, verbosityDepth
-from rrompy.utilities.warning_manager import warn
+from rrompy.utilities.exception_manager import (RROMPyException, modeAssert,
+ RROMPyWarning)
__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;
- 'interactive': whether to interactively terminate greedy
algorithm; defaults to False;
- '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):
+ verbosity : int = 10, timestamp : bool = True):
self._preInit()
- self._addParametersToList(["muBounds","greedyTol", "interactive",
+ self._addParametersToList(["muBounds", "greedyTol", "interactive",
"maxIter", "refinementRatio",
"nTrainingPoints", "nTestPoints",
"trainingSetGenerator"])
super(GenericApproximantLagrange, self).__init__(
HFEngine = HFEngine, mu0 = mu0,
approxParameters = approxParameters,
homogeneized = homogeneized,
- verbosity = verbosity)
+ verbosity = verbosity,
+ timestamp = timestamp)
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",
+ ["muBounds", "greedyTol",
"interactive", "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:
+ elif not hasattr(self, "_muBounds") or self.muBounds is None:
self.muBounds = [[0.], [1.]]
if "greedyTol" in keyList:
self.greedyTol = approxParameters["greedyTol"]
- elif hasattr(self, "greedyTol"):
- self.greedyTol = self.greedyTol
- else:
+ elif not hasattr(self, "_greedyTol") or self.greedyTol is None:
self.greedyTol = 1e-2
if "interactive" in keyList:
self.interactive = approxParameters["interactive"]
- elif hasattr(self, "interactive"):
- self.interactive = self.interactive
- else:
+ elif not hasattr(self, "interactive") or self.interactive is None:
self.interactive = False
if "maxIter" in keyList:
self.maxIter = approxParameters["maxIter"]
- elif hasattr(self, "maxIter"):
- self.maxIter = self.maxIter
- else:
+ elif not hasattr(self, "_maxIter") or self.maxIter is None:
self.maxIter = 1e2
if "refinementRatio" in keyList:
self.refinementRatio = approxParameters["refinementRatio"]
- elif hasattr(self, "refinementRatio"):
- self.refinementRatio = self.refinementRatio
- else:
+ elif (not hasattr(self, "_refinementRatio")
+ or self.refinementRatio is None):
self.refinementRatio = 0.2
if "nTrainingPoints" in keyList:
self.nTrainingPoints = approxParameters["nTrainingPoints"]
- elif hasattr(self, "nTrainingPoints"):
- self.nTrainingPoints = self.nTrainingPoints
- else:
+ elif (not hasattr(self, "_nTrainingPoints")
+ or self.nTrainingPoints is None):
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:
+ elif not hasattr(self, "_nTestPoints") or self.nTestPoints is None:
self.nTestPoints = 1
if "trainingSetGenerator" in keyList:
self.trainingSetGenerator = (
approxParameters["trainingSetGenerator"])
- elif hasattr(self, "trainingSetGenerator"):
- self.trainingSetGenerator = self.trainingSetGenerator
- else:
+ elif (not hasattr(self, "_trainingSetGenerator")
+ or self.trainingSetGenerator is None):
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
+ if not hasattr(self, "_mus") or self.mus is None: return 0
+ return len(self.mus)
@S.setter
def S(self, S):
- self._S = S
+ raise RROMPyException(("S is used just to simplify inheritance, and "
+ "its value cannot be changed."))
@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.")
+ raise RROMPyException("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.")
+ raise RROMPyException("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
+ raise RROMPyException("greedyTol must be non-negative.")
+ if hasattr(self, "_greedyTol") and self.greedyTol is not None:
+ 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
+ if maxIter <= 0: raise RROMPyException("maxIter must be positive.")
+ if hasattr(self, "_maxIter") and self.maxIter is not None:
+ 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 "
+ raise RROMPyException(("refinementRatio must be between 0 "
"(excluded) and 1."))
- if hasattr(self, "refinementRatio"):
+ if (hasattr(self, "_refinementRatio")
+ and self.refinementRatio is not None):
refinementRatioold = self.refinementRatio
- else: refinementRatioold = -1
+ 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.")
+ raise RROMPyException("nTrainingPoints must be greater than 1.")
if not np.isclose(nTrainingPoints, np.int(nTrainingPoints)):
- raise ArithmeticError("nTrainingPoints must be an integer.")
+ raise RROMPyException("nTrainingPoints must be an integer.")
nTrainingPoints = np.int(nTrainingPoints)
- if hasattr(self, "nTrainingPoints"):
+ if (hasattr(self, "_nTrainingPoints")
+ and self.nTrainingPoints is not None):
nTrainingPointsold = self.nTrainingPoints
- else: nTrainingPointsold = -1
+ 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.")
+ raise RROMPyException("nTestPoints must be positive.")
if not np.isclose(nTestPoints, np.int(nTestPoints)):
- raise ArithmeticError("nTestPoints must be an integer.")
+ raise RROMPyException("nTestPoints must be an integer.")
nTestPoints = np.int(nTestPoints)
- if hasattr(self, "nTestPoints"):
+ if hasattr(self, "_nTestPoints") and self.nTestPoints is not None:
nTestPointsold = self.nTestPoints
- else: nTestPointsold = -1
+ 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'):
+ raise RROMPyException("trainingSetGenerator type not recognized.")
+ if (hasattr(self, '_trainingSetGenerator')
+ and self.trainingSetGenerator is not None):
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.resbb = None
+ self.resAb = None
+ self.resAA = None
self._mus = []
def initEstNormer(self):
"""Initialize estimator norm engine."""
if not hasattr(self, "estNormer"):
from rrompy.hfengines.base import ProblemEngineBase as PEB
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]:
+ def getMaxErrorEstimator(self, mus, plot : bool = False)\
+ -> Tuple[Np1D, int, float]:
"""
- Compute maximum of (and index of maximum of) error estimator over
- training set.
+ Compute maximum of (and index of maximum of) error estimator over given
+ parameters.
"""
- errorEstTrain = self.errorEstimator(self.muTrain)
+ errorEstTrain = self.errorEstimator(mus)
idxMaxEst = np.argmax(errorEstTrain)
maxEst = errorEstTrain[idxMaxEst]
- return maxEst, idxMaxEst
+ if plot and not np.all(np.isinf(errorEstTrain)):
+ from matplotlib import pyplot as plt
+ onemus = np.ones(self.S)
+ plt.figure()
+ plt.semilogy(np.real(mus), errorEstTrain, 'k')
+ plt.semilogy(np.real(mus[[0, -1]]), [self.greedyTol] * 2, 'r--')
+ plt.semilogy(np.real(self.mus), 2. * self.greedyTol * onemus, '*m')
+ plt.semilogy(np.real(mus[idxMaxEst]), maxEst, 'xr')
+ plt.grid()
+ plt.show()
+ plt.close()
+ return errorEstTrain, idxMaxEst, maxEst
- def greedyNextSample(self, muidx:int, plotEst : bool = False):
+ def greedyNextSample(self, muidx:int, plotEst : bool = False)\
+ -> Tuple[Np1D, int, float, complex]:
"""Compute next greedy snapshot of solution map."""
+ modeAssert(self._mode, message = "Cannot add greedy sample.")
mu = self.muTrain[muidx]
if self.verbosity >= 2:
verbosityDepth("MAIN", ("Adding {}-th sample point at {} to "
"training set.").format(
- self.samplingEngine.nsamples + 1, mu))
+ self.samplingEngine.nsamples + 1, mu),
+ timestamp = self.timestamp)
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
+ self.samplingEngine.nextSample(mu, homogeneized = self.homogeneized)
+ errorEstTrain, muidx, maxErrorEst = self.getMaxErrorEstimator(
+ self.muTrain, plotEst)
+ return errorEstTrain, muidx, maxErrorEst, self.muTrain[muidx]
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(
+ modeAssert(self._mode, message = "Cannot start greedy algorithm.")
+ if self.samplingEngine.samples is not None:
+ return
+ if self.verbosity >= 2:
+ verbosityDepth("INIT", "Starting computation of snapshots.",
+ timestamp = self.timestamp)
+ 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 training 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)
+ 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", ("Uniform error estimate {:.4e}.")\
- .format(maxErrorEst))
- while (self.samplingEngine.nsamples < self.maxIter
- and maxErrorEst > self.greedyTol):
- 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(
+ verbosityDepth("MAIN", ("Adding {}-th sample point at {} to "
+ "training set.").format(
+ self.samplingEngine.nsamples + 1,
+ self.mus[j]),
+ timestamp = self.timestamp)
+ 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),
+ timestamp = self.timestamp)
+ trainedModelOld = copy(self.trainedModel)
+ while (self.samplingEngine.nsamples < self.maxIter
+ and maxErrorEst > self.greedyTol):
+ 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)),
+ timestamp = self.timestamp)
+ 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 (np.isnan(maxErrorEst) or np.isinf(maxErrorEst)
- or 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.interactive
- and self.samplingEngine.nsamples >= self.maxIter):
- verbosityDepth("MAIN", ("Maximum number of iterations {} "
- "reached. Want to increase "
- "maxIter and continue? Y/N")\
- .format(self.maxIter))
- increasemaxIter = input()
- if increasemaxIter.upper() == "Y":
- verbosityDepth("MAIN", "Doubling value of maxIter...")
- self.maxIter *= 2
- if (self.interactive and maxErrorEst <= self.greedyTol):
- verbosityDepth("MAIN", ("Required tolerance {} achieved. "
- "Want to decrease greedyTol and "
- "continue? Y/N").format(
- self.greedyTol))
- increasemaxIter = input()
- if increasemaxIter.upper() == "Y":
- verbosityDepth("MAIN", "Halving value of greedyTol...")
- self.greedyTol *= .5
if self.verbosity >= 2:
- verbosityDepth("DEL", ("Done computing snapshots (final "
- "snapshot count: {}).").format(
- self.samplingEngine.nsamples))
+ verbosityDepth("MAIN", "Uniform error estimate {:.4e}.".format(
+ maxErrorEst),
+ timestamp = self.timestamp)
+ if (np.isnan(maxErrorEst) or np.isinf(maxErrorEst)
+ or maxErrorEstOld < maxErrorEst * self.TOL_INSTABILITY):
+ RROMPyWarning(("Instability in a posteriori estimator. "
+ "Starting preemptive greedy loop termination."))
+ maxErrorEst = maxErrorEstOld
+ self.muTrain = muTrainOld
+ self.mus = self.mus[:-1]
+ self.samplingEngine.popSample()
+ self.trainedModel.data = copy(trainedModelOld.data)
+ break
+ trainedModelOld.data = copy(self.trainedModel.data)
+ if (self.interactive
+ and self.samplingEngine.nsamples >= self.maxIter):
+ verbosityDepth("MAIN", ("Maximum number of iterations {} "
+ "reached. Want to increase maxIter "
+ "and continue? Y/N").format(
+ self.maxIter),
+ timestamp = self.timestamp, end = "")
+ increasemaxIter = input()
+ if increasemaxIter.upper() == "Y":
+ verbosityDepth("MAIN", "Doubling value of maxIter...",
+ timestamp = self.timestamp)
+ self._maxIter *= 2
+ if (self.interactive and maxErrorEst <= self.greedyTol):
+ verbosityDepth("MAIN", ("Required tolerance {} achieved. Want "
+ "to decrease greedyTol and continue? "
+ "Y/N").format(self.greedyTol),
+ timestamp = self.timestamp, end = "")
+ increasemaxIter = input()
+ if increasemaxIter.upper() == "Y":
+ verbosityDepth("MAIN", "Halving value of greedyTol...",
+ timestamp = self.timestamp)
+ self._greedyTol *= .5
+ if self.verbosity >= 2:
+ verbosityDepth("DEL", ("Done computing snapshots (final snapshot "
+ "count: {}).").format(
+ self.samplingEngine.nsamples),
+ timestamp = self.timestamp)
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())
+ return (super().checkComputedApprox()
+ and self.S == self.trainedModel.data.projMat.shape[1])
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]))
+ modeAssert(self._mode, message = "Cannot compute rescaling factor.")
+ self.scaleFactor= np.abs(np.power(self.trainingSetGenerator.lims[0][0],
+ self.HFEngine.rescalingExp)
+ - np.power(self.trainingSetGenerator.lims[1][0],
+ self.HFEngine.rescalingExp)) / 2.
+
+ def assembleReducedResidualBlocksbb(self, bs:List[Np1D], pMat:Np2D,
+ scaling : float = 1.):
+ """
+ Build blocks (of type bb) of reduced linear system through projections.
+ """
+ self.initEstNormer()
+ nbs = len(bs)
+ self.resbb = np.empty((nbs, nbs), dtype = np.complex)
+ for i in range(nbs):
+ Mbi = scaling ** i * bs[i]
+ self.resbb[i, i] = self.estNormer.innerProduct(Mbi, Mbi)
+ for j in range(i):
+ Mbj = scaling ** j * bs[j]
+ self.resbb[i, j] = self.estNormer.innerProduct(Mbj, Mbi)
+ for i in range(nbs):
+ for j in range(i + 1, nbs):
+ self.resbb[i, j] = self.resbb[j, i].conj()
+
+ def assembleReducedResidualBlocksAb(self, As:List[Np2D], bs:List[Np1D],
+ pMat:Np2D, scaling : float = 1.):
+ """
+ Build blocks (of type Ab) of reduced linear system through projections.
+ """
+ self.initEstNormer()
+ nAs = len(As)
+ nbs = len(bs)
+ if not hasattr(self, "resAb") or self.resAb is None:
+ self.resAb = np.empty((nbs, self.S, nAs), dtype = np.complex)
+ for j in range(nAs):
+ MAj = scaling ** (j + 1) * As[j].dot(pMat)
+ for i in range(nbs):
+ Mbi = scaling ** (i + 1) * bs[i]
+ self.resAb[i, :, j] = self.estNormer.innerProduct(MAj, Mbi)
+ else:
+ Sold = self.resAb.shape[1]
+ if Sold > self.S:
+ self.resAb = self.resAb[:, : self.S, :]
+ else:
+ resAbNew = np.empty((nbs, self.S, nAs), dtype = np.complex)
+ resAbNew[:, : Sold, :] = self.resAb
+ self.resAb = resAbNew
+ for j in range(nAs):
+ MAj = scaling ** (j + 1) * As[j].dot(pMat[:, Sold :])
+ for i in range(nbs):
+ Mbi = scaling ** (i + 1) * bs[i]
+ self.resAb[i, Sold :, j] = self.estNormer.innerProduct(
+ MAj, Mbi)
+
+ def assembleReducedResidualBlocksAA(self, As:List[Np2D], pMat:Np2D,
+ scaling : float = 1.):
+ """
+ Build blocks (of type AA) of reduced linear system through projections.
+ """
+ self.initEstNormer()
+ nAs = len(As)
+ if not hasattr(self, "resAA") or self.resAA is None:
+ self.resAA = np.empty((self.S, nAs, self.S, nAs),
+ dtype = np.complex)
+ for i in range(nAs):
+ MAi = scaling ** (i + 1) * As[i].dot(pMat)
+ self.resAA[:, i, :, i] = self.estNormer.innerProduct(MAi, MAi)
+ for j in range(i):
+ MAj = scaling ** (j + 1) * As[j].dot(pMat)
+ self.resAA[:, i, :, j] = self.estNormer.innerProduct(MAj,
+ MAi)
+ for i in range(nAs):
+ for j in range(i + 1, nAs):
+ self.resAA[:, i, :, j] = self.resAA[:, j, :, i].T.conj()
+ else:
+ Sold = self.resAA.shape[0]
+ if Sold > self.S:
+ self.resAA = self.resAA[: self.S, :, : self.S, :]
+ else:
+ resAANew = np.empty((self.S, nAs, self.S, nAs),
+ dtype = np.complex)
+ resAANew[: Sold, :, : Sold, :] = self.resAA
+ self.resAA = resAANew
+ for i in range(nAs):
+ MAi = scaling ** (i + 1) * As[i].dot(pMat)
+ self.resAA[: Sold, i, Sold :, i] = (
+ self.estNormer.innerProduct(MAi[:, Sold :],
+ MAi[:, : Sold]))
+ self.resAA[Sold :, i, : Sold, i] = (
+ self.resAA[: Sold, i, Sold :, i].T.conj())
+ self.resAA[Sold :, i, Sold :, i] = (
+ self.estNormer.innerProduct(MAi[:, Sold :],
+ MAi[:, Sold :]))
+ for j in range(i):
+ MAj = scaling ** (j + 1) * As[j].dot(pMat)
+ self.resAA[: Sold, i, Sold :, j] = (
+ self.estNormer.innerProduct(MAj[:, Sold :],
+ MAi[:, : Sold]))
+ self.resAA[Sold :, i, : Sold, j] = (
+ self.estNormer.innerProduct(MAj[:, : Sold],
+ MAi[:, Sold :]))
+ self.resAA[Sold :, i, Sold :, j] = (
+ self.estNormer.innerProduct(MAj[:, Sold :],
+ MAi[:, Sold :]))
+ for i in range(nAs):
+ for j in range(i + 1, nAs):
+ self.resAA[: Sold, i, Sold :, j] = (
+ self.resAA[Sold :, j, : Sold, i].T.conj())
+ self.resAA[Sold :, i, : Sold, j] = (
+ self.resAA[: Sold, j, Sold :, i].T.conj())
+ self.resAA[Sold :, i, Sold :, j] = (
+ self.resAA[Sold :, j, Sold :, i].T.conj())
diff --git a/rrompy/reduction_methods/taylor/approximant_taylor_pade.py b/rrompy/reduction_methods/taylor/approximant_taylor_pade.py
index 54f9e09..7fc4482 100644
--- a/rrompy/reduction_methods/taylor/approximant_taylor_pade.py
+++ b/rrompy/reduction_methods/taylor/approximant_taylor_pade.py
@@ -1,558 +1,461 @@
# 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.trained_model import (TrainedModelData,
+ TrainedModelPade as tModel)
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
+from rrompy.utilities.base.types import Np1D, Np2D, Tuple, DictAny, HFEng
+from rrompy.utilities.base import verbosityDepth, purgeDict
+from rrompy.utilities.exception_manager import (RROMPyException, modeAssert,
+ RROMPyWarning)
__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;
+ defaults to 1;
- '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):
+ verbosity : int = 10, timestamp : bool = True):
self._preInit()
self._addParametersToList(["M", "N", "robustTol", "rho"])
super().__init__(HFEngine = HFEngine, mu0 = mu0,
approxParameters = approxParameters,
homogeneized = homogeneized,
- verbosity = verbosity)
+ verbosity = verbosity, timestamp = timestamp)
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:
+ elif not hasattr(self, "_rho") or self.rho is None:
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:
+ elif not hasattr(self, "_robustTol") or self._robustTol is None:
self.robustTol = 0
self._ignoreParWarnings = True
if "M" in keyList:
self.M = approxParameters["M"]
- elif hasattr(self, "M"):
+ elif hasattr(self, "_M") and self._M is not None:
self.M = self.M
else:
self.M = 0
del self._ignoreParWarnings
if "N" in keyList:
self.N = approxParameters["N"]
- elif hasattr(self, "N"):
+ elif hasattr(self, "_N") and self._N is not None:
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."""
+ """Value of M. Its assignment may change E."""
return self._M
@M.setter
def M(self, M):
- if M < 0: raise ArithmeticError("M must be non-negative.")
+ if M < 0: raise RROMPyException("M must be non-negative.")
self._M = M
self._approxParameters["M"] = self.M
if not hasattr(self, "_ignoreParWarnings"):
- self.checkMNEEmax()
+ self.checkMNE()
@property
def N(self):
- """Value of N. Its assignment may change Emax."""
+ """Value of N. Its assignment may change E."""
return self._N
@N.setter
def N(self, N):
- if N < 0: raise ArithmeticError("N must be non-negative.")
+ if N < 0: raise RROMPyException("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
+ if not hasattr(self, "_ignoreParWarnings"):
+ self.checkMNE()
+
+ def checkMNE(self):
+ """Check consistency of M, N, and E."""
+ if not hasattr(self, "_E") or self.E is None: return
+ M = self.M if (hasattr(self, "_M") and self.M is not None) else 0
+ N = self.N if (hasattr(self, "_N") and self.N is not None) else 0
+ msg = "max(M, N)" if self.rho == np.inf else "M + N"
+ bound = eval(msg)
+ if self.E < bound:
+ RROMPyWarning(("Prescribed E is too small. Updating E to "
+ "{}.").format(msg))
+ self.E = bound
+ del M, N
@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.")
+ RROMPyWarning(("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."""
+ """Value of E."""
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]
+ GenericApproximantTaylor.E.fset(self, E)
+ self.checkMNE()
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.checkComputedApprox():
+ return
+ if self.verbosity >= 5:
+ verbosityDepth("INIT", "Setting up {}.". format(self.name()),
+ timestamp = self.timestamp)
+ self.computeDerivatives()
+ if self.N > 0:
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)
+ verbosityDepth("INIT", "Starting computation of denominator.",
+ timestamp = self.timestamp)
+ 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))
+ Q = eV[::-1, 0]
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")
+ verbosityDepth("DEL", "Done computing denominator.",
+ timestamp = self.timestamp)
+ else:
+ Q = np.ones(1, dtype = np.complex)
+ if self.verbosity >= 7:
+ verbosityDepth("INIT", "Starting computation of numerator.",
+ timestamp = self.timestamp)
+ P = np.zeros((self.E + 1, self.M + 1), dtype = np.complex)
+ for i in range(self.E + 1):
+ l = min(self.M + 1, i + self.N + 1)
+ P[i, i : l] = Q[: l - i]
+ P = self.rescaleParameter(P.T).T
+ if self.sampleType == "ARNOLDI":
+ P = self.samplingEngine.RArnoldi.dot(P)
+ if self.verbosity >= 7:
+ verbosityDepth("DEL", "Done computing numerator.",
+ timestamp = self.timestamp)
+
+ if self.trainedModel is None:
+ self.trainedModel = tModel()
+ self.trainedModel.verbosity = self.verbosity
+ self.trainedModel.timestamp = self.timestamp
+ data = TrainedModelData(self.trainedModel.name(), self.mu0,
+ self.samplingEngine.samples[:,:self.E + 1],
+ self.HFEngine.rescalingExp)
+ data.P, data.Q = np.copy(P), np.copy(Q)
+ data.polytype = "MONOMIAL"
+ data.scaleFactor = self.scaleFactor
+ self.trainedModel.data = data
+ else:
+ self.trainedModel.data.P = np.copy(P)
+ self.trainedModel.data.Q = np.copy(Q)
+ self.trainedModel.data.projMat = (
+ self.samplingEngine.samples[:, : self.E + 1])
+ self.trainedModel.data.approxParameters = copy(self.approxParameters)
+ if self.verbosity >= 5:
+ verbosityDepth("DEL", "Done setting up approximant.",
+ timestamp = self.timestamp)
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.
"""
+ modeAssert(self._mode, message = "Cannot compute rescaling factor.")
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."""
+ modeAssert(self._mode, message = "Cannot compute G matrix.")
self.computeDerivatives()
+ if self.verbosity >= 10:
+ verbosityDepth("INIT", "Building gramian matrix.",
+ timestamp = self.timestamp)
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)
+ G = RArnE.T.conj().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]
+ if self.verbosity >= 10:
+ verbosityDepth("DEL", "Done building gramian.",
+ timestamp = self.timestamp)
def findeveVGExplicit(self) -> Tuple[Np1D, Np2D]:
"""
Compute explicitly eigenvalues and eigenvectors of Pade' denominator
matrix.
"""
- if self.verbosity >= 10:
- verbosityDepth("INIT", "Building gramian matrix.")
+ modeAssert(self._mode, message = "Cannot solve eigenvalue problem.")
self.buildG()
- if self.verbosity >= 10:
- verbosityDepth("DEL", "Done building gramian.")
if self.verbosity >= 7:
verbosityDepth("INIT",
- "Solving eigenvalue problem for gramian matrix.")
+ "Solving eigenvalue problem for gramian matrix.",
+ timestamp = self.timestamp)
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))
+ self.N + 1,
+ condev),
+ timestamp = self.timestamp)
if self.verbosity >= 7:
- verbosityDepth("DEL", "Done solving eigenvalue problem.")
+ verbosityDepth("DEL", "Done solving eigenvalue problem.",
+ timestamp = self.timestamp)
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.
"""
+ modeAssert(self._mode, message = "Cannot solve eigenvalue problem.")
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 = "")
+ verbosityDepth("INIT", "Orthogonalizing samples.",
+ timestamp = self.timestamp)
R = self.PODEngine.QRHouseholder(A, only_R = True)
if self.verbosity >= 10:
- verbosityDepth("DEL", " Done.", inline = True)
+ verbosityDepth("DEL", "Done orthogonalizing samples.",
+ timestamp = self.timestamp)
else:
R = self.samplingEngine.RArnoldi[: self.E + 1, Nmin : self.E + 1]
- R = self.rescaleParameter(R, R[Nmin :, :])
+ R = self.rescaleParameter(R, R[R.shape[0] - R.shape[1] :, :])
if self.rho == np.inf:
if self.verbosity >= 7:
verbosityDepth("INIT", ("Solving svd for square root of "
- "gramian matrix."))
+ "gramian matrix."),
+ timestamp = self.timestamp)
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 = "")
+ "root of gramian."),
+ timestamp = self.timestamp)
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)
+ verbosityDepth("DEL", "Done building matrix stack.",
+ timestamp = self.timestamp)
if self.verbosity >= 7:
verbosityDepth("INIT", ("Solving svd for square root of "
- "gramian matrix."))
+ "gramian matrix."),
+ timestamp = self.timestamp)
sizeI = Rtower.shape[0]
_, s, V = np.linalg.svd(Rtower, full_matrices = False)
- eV = V.conj().T[:, ::-1]
- eV = self.rescaleParameter(eV.T).T
+ eV = V[::-1, :].T.conj()
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))
+ "condition number {:.4e}.").format(sizeI,
+ self.N + 1,
+ condev),
+ timestamp = self.timestamp)
if self.verbosity >= 7:
- verbosityDepth("DEL", "Done solving eigenvalue problem.")
+ verbosityDepth("DEL", "Done solving eigenvalue problem.",
+ timestamp = self.timestamp)
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)
+ return self.trainedModel.radiusPade(mu, mu0)
def getResidues(self) -> Np1D:
"""
Obtain norm of approximant residues.
Returns:
Numpy vector of norms of residues.
"""
- poles = self.getPoles()
- Pvals = self.samplingEngine.samples.dot(self.getPVal(poles))
- Qder = - self.Q.deriv(1)(self.radiusPade(poles))
- return Pvals / Qder
-
- 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))
+ if self.verbosity >= 20:
+ verbosityDepth("INIT", "Computing residues of model.",
+ timestamp = self.timestamp)
+ poles = self.getPoles()
+ Pvals = self.trainedModel.data.projMat.dot(self.getPVal(poles))
+ Qder = self._valder(self.radiusPade(poles), self.trainedModel.data.Q)
+ residues = Pvals / Qder
+ if self.verbosity >= 20:
+ verbosityDepth("DEL", "Done computing residues.",
+ timestamp = self.timestamp)
+ return residues
diff --git a/rrompy/reduction_methods/taylor/approximant_taylor_rb.py b/rrompy/reduction_methods/taylor/approximant_taylor_rb.py
index 62b3f92..2d8a7e8 100644
--- a/rrompy/reduction_methods/taylor/approximant_taylor_rb.py
+++ b/rrompy/reduction_methods/taylor/approximant_taylor_rb.py
@@ -1,314 +1,229 @@
# 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.reduction_methods.trained_model import TrainedModelRB as tModel
+from rrompy.reduction_methods.trained_model import TrainedModelData
+from rrompy.reduction_methods.base.rb_utils import projectAffineDecomposition
from rrompy.sampling.base.pod_engine import PODEngine
-from rrompy.utilities.base.types import Np1D, DictAny, HFEng
+from rrompy.utilities.base.types import Np1D, Np2D, Tuple, List, DictAny, HFEng
from rrompy.utilities.base import purgeDict, verbosityDepth
-from rrompy.utilities.warning_manager import warn
+from rrompy.utilities.exception_manager import RROMPyWarning
__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;
+ defaults to 1;
- '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):
+ verbosity : int = 10, timestamp : bool = True):
self._preInit()
self._addParametersToList(["R"])
super().__init__(HFEngine = HFEngine, mu0 = mu0,
approxParameters = approxParameters,
homogeneized = homogeneized,
- verbosity = verbosity)
+ verbosity = verbosity, timestamp = timestamp)
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)
+ verbosityDepth("INIT", "Computing affine blocks of system.",
+ timestamp = self.timestamp)
if self.verbosity >= 10:
- verbosityDepth("DEL", "Done computing affine blocks.")
+ verbosityDepth("DEL", "Done computing affine blocks.",
+ timestamp = self.timestamp)
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."))
+ if (hasattr(self, "_sampleType") and self.sampleType == "ARNOLDI"
+ and not self.POD):
+ RROMPyWarning(("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."))
+ if (hasattr(self, "_POD") and not self.POD
+ and self.sampleType == "ARNOLDI"):
+ RROMPyWarning(("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.")
+ if R < 0: raise RROMPyException("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.")
+ if hasattr(self, "_E") and self.E + 1 < self.R:
+ RROMPyWarning("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(
+ if self.checkComputedApprox():
+ return
+ if self.verbosity >= 5:
+ verbosityDepth("INIT", "Setting up {}.". format(self.name()),
+ timestamp = self.timestamp)
+ self.computeDerivatives()
+ if self.verbosity >= 7:
+ verbosityDepth("INIT", "Computing projection matrix.",
+ timestamp = self.timestamp)
+ if self.POD and not self.sampleType == "ARNOLDI":
+ self.PODEngine = PODEngine(self.HFEngine)
+ pMatQ, pMatR = 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")
+ if self.POD:
+ if self.sampleType == "ARNOLDI":
+ pMatR = self.samplingEngine.RArnoldi
+ pMatQ = self.samplingEngine.samples
+ U, _, _ = np.linalg.svd(pMatR[: self.E + 1, : self.E + 1])
+ pMat = pMatQ[:, : self.E + 1].dot(U[:, : self.R])
+ else:
+ pMat = self.samplingEngine.samples[:, : self.R]
+
+ if self.trainedModel is None:
+ self.trainedModel = tModel()
+ self.trainedModel.verbosity = self.verbosity
+ self.trainedModel.timestamp = self.timestamp
+ data = TrainedModelData(self.trainedModel.name(), self.mu0,
+ np.copy(pMat), self.HFEngine.rescalingExp)
+ data.thetaAs = self.HFEngine.affineWeightsA(self.mu0)
+ data.thetabs = self.HFEngine.affineWeightsb(self.mu0,
+ self.homogeneized)
+ data.ARBs, data.bRBs = self.assembleReducedSystem(pMat)
+ self.trainedModel.data = data
+ else:
+ pMatOld = self.trainedModel.data.projMat
+ Sold = pMatOld.shape[1]
+ ARBs, bRBs = self.assembleReducedSystem(pMat[:, Sold :], pMatOld)
+ self.trainedModel.data.ARBs = ARBs
+ self.trainedModel.data.bRBs = bRBs
+ self.trainedModel.data.projMat = np.copy(pMat)
+
+ if self.verbosity >= 7:
+ verbosityDepth("DEL", "Done computing projection matrix.",
+ timestamp = self.timestamp)
+ self.trainedModel.data.approxParameters = copy(self.approxParameters)
+ if self.verbosity >= 5:
+ verbosityDepth("DEL", "Done setting up approximant.",
+ timestamp = self.timestamp)
- def assembleReducedSystem(self):
+ def assembleReducedSystem(self, pMat : Np2D = None, pMatOld : Np2D = None)\
+ -> Tuple[List[Np2D], List[Np1D]]:
"""Build affine blocks of RB linear system through projections."""
- if not self.checkComputedApprox():
+ if pMat is None:
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.
+ ARBs = self.trainedModel.data.ARBs
+ bRBs = self.trainedModel.data.bRBs
+ else:
+ if self.verbosity >= 10:
+ verbosityDepth("INIT", "Projecting affine terms of HF model.",
+ timestamp = self.timestamp)
+ As = self.HFEngine.affineLinearSystemA(self.mu0)
+ bs = self.HFEngine.affineLinearSystemb(self.mu0, self.homogeneized)
+ ARBsOld = None if pMatOld is None else self.trainedModel.data.ARBs
+ bRBsOld = None if pMatOld is None else self.trainedModel.data.bRBs
+ ARBs, bRBs = projectAffineDecomposition(As, bs, pMat, ARBsOld,
+ bRBsOld, pMatOld)
+ if self.verbosity >= 10:
+ verbosityDepth("DEL", "Done projecting affine terms.",
+ timestamp = self.timestamp)
+ return ARBs, bRBs
- 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 913703f..7d848d9 100644
--- a/rrompy/reduction_methods/taylor/generic_approximant_taylor.py
+++ b/rrompy/reduction_methods/taylor/generic_approximant_taylor.py
@@ -1,245 +1,185 @@
# 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
+from rrompy.utilities.exception_manager import (RROMPyException, modeAssert,
+ RROMPyWarning)
__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;
+ defaults to 1;
- '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):
+ verbosity : int = 10, timestamp : bool = True):
self._preInit()
- self._addParametersToList(["E", "Emax", "sampleType"])
+ self._addParametersToList(["E", "sampleType"])
super().__init__(HFEngine = HFEngine, mu0 = mu0,
approxParameters = approxParameters,
homogeneized = homogeneized,
- verbosity = verbosity)
+ verbosity = verbosity, timestamp = timestamp)
self._postInit()
def setupSampling(self):
"""Setup sampling engine."""
- if not hasattr(self, "sampleType"): return
+ modeAssert(self._mode, message = "Cannot setup sampling engine.")
+ if not hasattr(self, "_sampleType"): return
if self.sampleType == "ARNOLDI":
from rrompy.sampling.linear_problem.sampling_engine_arnoldi \
import SamplingEngineArnoldi
super().setupSampling(SamplingEngineArnoldi)
elif self.sampleType == "KRYLOV":
from rrompy.sampling.linear_problem.sampling_engine_krylov \
import SamplingEngineKrylov
super().setupSampling(SamplingEngineKrylov)
else:
- raise Exception("Sample type not recognized.")
+ raise RROMPyException("Sample type not recognized.")
@property
def approxParameters(self):
- """
- Value of approximant parameters. Its assignment may change E and Emax.
- """
+ """Value of approximant parameters. Its assignment may change E."""
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"],
+ approxParametersCopy = purgeDict(approxParameters, ["E", "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
+ self.E = approxParameters["E"]
+ elif hasattr(self, "_E") and self._E is not None:
+ self.E = self.E
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.")
+ self.E = 1
if "sampleType" in keyList:
self.sampleType = approxParameters["sampleType"]
- elif hasattr(self, "sampleType"):
- self.sampleType = self.sampleType
- else:
+ elif not hasattr(self, "_sampleType") or self._sampleType is None:
self.sampleType = "KRYLOV"
@property
def E(self):
- """Value of E. Its assignment may change Emax."""
+ """Value of E."""
return self._E
@E.setter
def E(self, E):
- if E < 0: raise ArithmeticError("E must be non-negative.")
+ if E < 0: raise RROMPyException("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
+ if hasattr(self, "_sampleType") and self._sampleType is not None:
+ sampleTypeOld = self.sampleType
else: sampleTypeOld = -1
try:
sampleType = sampleType.upper().strip().replace(" ","")
if sampleType not in ["ARNOLDI", "KRYLOV"]:
- raise Exception("Sample type not recognized.")
+ raise RROMPyException("Sample type not recognized.")
self._sampleType = sampleType
except:
- warn(("Prescribed sampleType not recognized. Overriding to "
- "'KRYLOV'."))
+ RROMPyWarning(("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."""
+ modeAssert(self._mode,
+ message = "Cannot start derivative computation.")
if self.samplingEngine.samples is None:
if self.verbosity >= 5:
- verbosityDepth("INIT", "Starting computation of derivatives.")
- self.samplingEngine.iterSample(self.mu0, self.Emax + 1,
+ verbosityDepth("INIT", "Starting computation of derivatives.",
+ timestamp = self.timestamp)
+ self.samplingEngine.iterSample(self.mu0, self.E + 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())
+ verbosityDepth("DEL", "Done computing derivatives.",
+ timestamp = self.timestamp)
def normApprox(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().normApprox(mu, homogeneized)
return np.linalg.norm(self.getApproxReduced(mu, homogeneized))
diff --git a/rrompy/reduction_methods/base/__init__.py b/rrompy/reduction_methods/trained_model/__init__.py
similarity index 72%
copy from rrompy/reduction_methods/base/__init__.py
copy to rrompy/reduction_methods/trained_model/__init__.py
index 853116c..35dfee6 100644
--- a/rrompy/reduction_methods/base/__init__.py
+++ b/rrompy/reduction_methods/trained_model/__init__.py
@@ -1,29 +1,31 @@
# 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
+from .trained_model import TrainedModel
+from .trained_model_data import TrainedModelData
+from .trained_model_pade import TrainedModelPade
+from .trained_model_rb import TrainedModelRB
__all__ = [
- 'GenericApproximant',
- 'setupFitCallables',
- 'checkRobustTolerance'
+ 'TrainedModel',
+ 'TrainedModelData',
+ 'TrainedModelPade',
+ 'TrainedModelRB'
]
diff --git a/rrompy/reduction_methods/trained_model/trained_model.py b/rrompy/reduction_methods/trained_model/trained_model.py
new file mode 100644
index 0000000..5399f65
--- /dev/null
+++ b/rrompy/reduction_methods/trained_model/trained_model.py
@@ -0,0 +1,82 @@
+# 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 rrompy.utilities.base.types import Np1D
+
+__all__ = ['TrainedModel']
+
+class TrainedModel:
+ """
+ ABSTRACT
+ ROM approximant evaluation.
+
+ Attributes:
+ Data: dictionary with all that can be pickled.
+ """
+
+ 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))
+
+ @abstractmethod
+ def getApproxReduced(self, mu:complex):
+ """
+ Evaluate reduced representation of approximant at arbitrary parameter.
+ (ABSTRACT)
+
+ Args:
+ mu: Target parameter.
+ """
+ pass
+
+ def getApprox(self, mu:complex):
+ """
+ Evaluate approximant at arbitrary parameter.
+
+ Args:
+ mu: Target parameter.
+ """
+ if (not hasattr(self, "lastSolvedApp")
+ or not np.isclose(self.lastSolvedApp, mu)):
+ uAppRed = self.getApproxReduced(mu)
+ if isinstance(self.data.projMat, (list,)):
+ self.uApp = uAppRed[0] * self.data.projMat[:, 0]
+ for j in range(1, len(uAppRed)):
+ self.uApp += uAppRed[j] * self.data.projMat[:, j]
+ else:
+ self.uApp = self.data.projMat.dot(uAppRed)
+ self.lastSolvedApp = mu
+ return self.uApp
+
+ @abstractmethod
+ def getPoles(self) -> Np1D:
+ """
+ Obtain approximant poles.
+
+ Returns:
+ Numpy complex vector of poles.
+ """
+ pass
+
diff --git a/rrompy/utilities/warning_manager/__init__.py b/rrompy/reduction_methods/trained_model/trained_model_data.py
similarity index 64%
copy from rrompy/utilities/warning_manager/__init__.py
copy to rrompy/reduction_methods/trained_model/trained_model_data.py
index 7138de5..82cd8fd 100644
--- a/rrompy/utilities/warning_manager/__init__.py
+++ b/rrompy/reduction_methods/trained_model/trained_model_data.py
@@ -1,25 +1,31 @@
# 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 .warning_manager import warn
+from rrompy.utilities.base.types import Np2D
-__all__ = [
- 'warn'
- ]
+__all__ = ['TrainedModelData']
+class TrainedModelData:
+ """ROM approximant evaluation data (must be pickle-able)."""
+ def __init__(self, name:str, mu0:complex, projMat:Np2D,
+ rescalingExp : float = 1.):
+ self.name = name
+ self.mu0 = mu0
+ self.projMat = projMat
+ self.rescalingExp = rescalingExp
diff --git a/rrompy/reduction_methods/trained_model/trained_model_pade.py b/rrompy/reduction_methods/trained_model/trained_model_pade.py
new file mode 100644
index 0000000..c9fcbf5
--- /dev/null
+++ b/rrompy/reduction_methods/trained_model/trained_model_pade.py
@@ -0,0 +1,140 @@
+# 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 . import TrainedModel
+from rrompy.utilities.base.types import Np1D
+from rrompy.utilities.base import verbosityDepth
+from rrompy.reduction_methods.base.fit_utils import polyval, polyroots
+
+__all__ = ['TrainedModelPade']
+
+class TrainedModelPade(TrainedModel):
+ """
+ ROM approximant evaluation for Pade' approximant.
+
+ Attributes:
+ Data: dictionary with all that can be pickled.
+ """
+
+ 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.data.mu0.
+
+ Returns:
+ Translated radius to be plugged into Pade' approximant.
+ """
+ if mu0 is None: mu0 = self.data.mu0
+ return (np.power(mu, self.data.rescalingExp)
+ - np.power(mu0, self.data.rescalingExp)) / self.data.scaleFactor
+
+ def getPVal(self, mu:Np1D):
+ """
+ Evaluate Pade' numerator at arbitrary parameter.
+
+ Args:
+ mu: Target parameter.
+ """
+ if self.verbosity >= 10:
+ mustr = mu
+ try:
+ nmu = len(mu)
+ if nmu > 2:
+ mustr = "[{} ..({}).. {}]".format(mu[0], nmu - 2, mu[-1])
+ except: pass
+ verbosityDepth("INIT", ("Evaluating numerator at mu = "
+ "{}.").format(mustr),
+ timestamp = self.timestamp)
+ try:
+ len(mu)
+ except:
+ mu = [mu]
+ p = polyval[self.data.polytype](self.radiusPade(mu), self.data.P.T)
+ if len(mu) == 1:
+ p = p.flatten()
+ if self.verbosity >= 10:
+ verbosityDepth("DEL", "Done evaluating numerator.",
+ timestamp = self.timestamp)
+ return p
+
+ def getQVal(self, mu:Np1D):
+ """
+ Evaluate Pade' denominator at arbitrary parameter.
+
+ Args:
+ mu: Target parameter.
+ """
+ if self.verbosity >= 10:
+ mustr = mu
+ try:
+ nmu = len(mu)
+ if nmu > 2:
+ mustr = "[{} ..({}).. {}]".format(mu[0], nmu - 2, mu[-1])
+ except: pass
+ verbosityDepth("INIT", ("Evaluating denominator at mu = "
+ "{}.").format(mustr),
+ timestamp = self.timestamp)
+ q = polyval[self.data.polytype](self.radiusPade(mu), self.data.Q)
+ if self.verbosity >= 10:
+ verbosityDepth("DEL", "Done evaluating denominator.",
+ timestamp = self.timestamp)
+ return q
+
+ def getApproxReduced(self, mu:complex):
+ """
+ Evaluate reduced representation of approximant at arbitrary parameter.
+
+ Args:
+ mu: Target parameter.
+ """
+ if (not hasattr(self, "lastSolvedAppReduced")
+ or not np.isclose(self.lastSolvedAppReduced, mu)):
+ if self.verbosity >= 5:
+ mustr = mu
+ try:
+ nmu = len(mu)
+ if nmu > 2:
+ mustr = "[{} ..({}).. {}]".format(mu[0], nmu - 2,
+ mu[-1])
+ except: pass
+ verbosityDepth("INIT", ("Evaluating approximant at mu = "
+ "{}.").format(mustr),
+ timestamp = self.timestamp)
+ self.uAppReduced = self.getPVal(mu) / self.getQVal(mu)
+ if self.verbosity >= 5:
+ verbosityDepth("DEL", "Done evaluating approximant.",
+ timestamp = self.timestamp)
+ self.lastSolvedAppReduced = mu
+ return self.uAppReduced
+
+ def getPoles(self) -> Np1D:
+ """
+ Obtain approximant poles.
+
+ Returns:
+ Numpy complex vector of poles.
+ """
+ return np.power(np.power(self.data.mu0, self.data.rescalingExp)
+ + self.data.scaleFactor
+ * polyroots[self.data.polytype](self.data.Q),
+ 1. / self.data.rescalingExp)
+
diff --git a/rrompy/reduction_methods/trained_model/trained_model_rb.py b/rrompy/reduction_methods/trained_model/trained_model_rb.py
new file mode 100644
index 0000000..151e058
--- /dev/null
+++ b/rrompy/reduction_methods/trained_model/trained_model_rb.py
@@ -0,0 +1,111 @@
+# 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 scipy.linalg import eigvals
+from . import TrainedModel
+from rrompy.utilities.base.types import Np1D
+from rrompy.utilities.base import verbosityDepth
+from rrompy.utilities.exception_manager import RROMPyWarning
+
+__all__ = ['TrainedModelRB']
+
+class TrainedModelRB(TrainedModel):
+ """
+ ROM approximant evaluation for RB approximant.
+
+ Attributes:
+ Data: dictionary with all that can be pickled.
+ """
+
+ def getApproxReduced(self, mu:complex):
+ """
+ Evaluate reduced representation of approximant at arbitrary parameter.
+
+ Args:
+ mu: Target parameter.
+ """
+ if (not hasattr(self, "lastSolvedAppReduced")
+ or not np.isclose(self.lastSolvedAppReduced, mu)):
+ if self.verbosity >= 5:
+ mustr = mu
+ try:
+ nmu = len(mu)
+ if nmu > 2:
+ mustr = "[{} ..({}).. {}]".format(mu[0], nmu - 2,
+ mu[-1])
+ except: pass
+ verbosityDepth("INIT", ("Computing RB solution at mu = "
+ "{}.").format(mustr),
+ timestamp = self.timestamp)
+ thetaAs, thetabs = self.data.thetaAs, self.data.thetabs
+ ARBs, bRBs = self.data.ARBs, self.data.bRBs
+ if self.verbosity >= 10:
+ verbosityDepth("INIT", ("Assembling reduced model for mu = "
+ "{}.").format(mustr),
+ timestamp = self.timestamp)
+ ARBmu = eval(thetaAs[0]) * ARBs[0]
+ bRBmu = eval(thetabs[0]) * bRBs[0]
+ for j in range(1, len(ARBs)):
+ ARBmu += eval(thetaAs[j]) * ARBs[j]
+ for j in range(1, len(bRBs)):
+ bRBmu += eval(thetabs[j]) * bRBs[j]
+ if self.verbosity >= 10:
+ verbosityDepth("DEL", "Done assembling reduced model.",
+ timestamp = self.timestamp)
+ if self.verbosity >= 5:
+ verbosityDepth("INIT", ("Solving reduced model for mu = "
+ "{}.").format(mustr),
+ timestamp = self.timestamp)
+ self.uAppReduced = np.linalg.solve(ARBmu, bRBmu)
+ if self.verbosity >= 5:
+ verbosityDepth("DEL", "Done solving reduced model.",
+ timestamp = self.timestamp)
+ if self.verbosity >= 5:
+ verbosityDepth("DEL", "Done computing RB solution.",
+ timestamp = self.timestamp)
+ self.lastSolvedAppReduced = mu
+ return self.uAppReduced
+
+ def getPoles(self) -> Np1D:
+ """
+ Obtain approximant poles.
+
+ Returns:
+ Numpy complex vector of poles.
+ """
+ RROMPyWarning(("Impossible to compute poles in general affine "
+ "parameter dependence. Results subject to "
+ "interpretation/rescaling, or possibly completely "
+ "wrong."))
+ ARBs = self.data.ARBs
+ R = ARBs[0].shape[0]
+ if len(ARBs) < 2:
+ return
+ A = np.eye(R * (len(ARBs) - 1), dtype = np.complex)
+ B = np.zeros_like(A)
+ A[: R, : R] = - ARBs[0]
+ for j in range(len(ARBs) - 1):
+ Aj = ARBs[j + 1]
+ B[: R, j * R : (j + 1) * R] = Aj
+ II = np.arange(R, R * (len(ARBs) - 1))
+ B[II, II - R] = 1.
+ return np.power(eigvals(A, B)
+ + np.power(self.data.mu0, self.data.rescalingExp),
+ 1. / self.data.rescalingExp)
+
diff --git a/rrompy/sampling/base/pod_engine.py b/rrompy/sampling/base/pod_engine.py
index da00bf1..ece593a 100644
--- a/rrompy/sampling/base/pod_engine.py
+++ b/rrompy/sampling/base/pod_engine.py
@@ -1,150 +1,151 @@
# 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 rrompy.utilities.base.types import Np1D, Np2D, Tuple, HFEng
+from rrompy.utilities.exception_manager import RROMPyException
__all__ = ['PODEngine']
class PODEngine:
"""
POD engine for general matrix orthogonalization.
"""
def __init__(self, HFEngine:HFEng):
self.HFEngine = HFEngine
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 norm(self, a:Np1D) -> float:
"""Compute norm of a Hilbert space object."""
pass
def GS(self, a:Np1D, Q:Np2D, n : int = None,
aA:Np1D = None, QA:Np2D = None) -> Tuple[Np1D, Np1D, Np1D]:
"""
Compute 1 Gram-Schmidt step with given projector.
Args:
a: vector to be projected;
Q: orthogonal projection matrix;
n: number of columns of Q to be considered;
aA: augmented components of vector to be projected;
QA: augmented components of projection matrix.
Returns:
Resulting normalized vector, coefficients of a wrt the updated
basis.
"""
if n is None:
n = Q.shape[1]
if aA is None != QA is None:
- raise Exception(("Either both or none of augmented components "
- "must be provided."))
+ raise RROMPyException(("Either both or none of augmented "
+ "components must be provided."))
r = np.zeros((n + 1,), dtype = a.dtype)
if n > 0:
Q = Q[:, : n]
for j in range(2): # twice is enough!
nu = self.HFEngine.innerProduct(a, Q)
a = a - Q.dot(nu)
if aA is not None:
aA = aA - QA.dot(nu)
r[:-1] = r[:-1] + nu
r[-1] = self.HFEngine.norm(a)
if np.isclose(np.abs(r[-1]), 0.):
r[-1] = 1.
a = a / r[-1]
if aA is not None:
aA = aA / r[-1]
return a, r, aA
def QRGramSchmidt(self, A:Np2D,
only_R : bool = False) -> Tuple[Np1D, Np1D]:
"""
Compute QR decomposition of a matrix through Gram-Schmidt method.
Args:
A: matrix to be decomposed;
only_R(optional): whether to skip reconstruction of Q; defaults to
False.
Returns:
Resulting orthogonal and upper-triangular factors.
"""
N = A.shape[1]
Q = np.zeros_like(A, dtype = A.dtype)
R = np.zeros((N, N), dtype = A.dtype)
for k in range(N):
Q[:, k], R[: k + 1, k], _ = self.GS(A[:, k], Q, k)
if only_R:
return R
return Q, R
def QRHouseholder(self, A:Np2D, Q0 : Np2D = None,
only_R : bool = False) -> Tuple[Np1D, Np1D]:
"""
Compute QR decomposition of a matrix through Householder method.
Args:
A: matrix to be decomposed;
Q0(optional): initial orthogonal guess for Q; defaults to random;
only_R(optional): whether to skip reconstruction of Q; defaults to
False.
Returns:
Resulting (orthogonal and )upper-triangular factor(s).
"""
B = copy(A)
N = B.shape[1]
V = np.zeros_like(B, dtype = B.dtype)
R = np.zeros((N, N), dtype = B.dtype)
if Q0 is None:
Q = np.zeros_like(B, dtype = B.dtype) + np.random.randn(*(B.shape))
else:
Q = copy(Q0)
for k in range(N):
if Q0 is None:
Q[:, k], _, _ = self.GS(Q[:, k], Q, k)
a = B[:, k]
R[k, k] = self.HFEngine.norm(a)
alpha = self.HFEngine.innerProduct(a, Q[:, k])
if np.isclose(np.abs(alpha), 0.): s = 1.
else: s = - alpha / np.abs(alpha)
Q[:, k] = s * Q[:, k]
V[:, k], _, _ = self.GS(R[k, k] * Q[:, k] - a, Q, k)
J = np.arange(k + 1, N)
vtB = self.HFEngine.innerProduct(B[:, J], V[:, k])
B[:, J] = B[:, J] - 2 * np.outer(V[:, k], vtB)
R[k, J] = self.HFEngine.innerProduct(B[:, J], Q[:, k])
B[:, J] = B[:, J] - np.outer(Q[:, k], R[k, J])
if only_R:
return R
for k in range(N - 1, -1, -1):
J = np.arange(k, N)
vtQ = self.HFEngine.innerProduct(Q[:, J], V[:, k])
Q[:, J] = Q[:, J] - 2 * np.outer(V[:, k], vtQ)
return Q, R
diff --git a/rrompy/sampling/base/sampling_engine_base.py b/rrompy/sampling/base/sampling_engine_base.py
index 7ffae7d..3af9477 100644
--- a/rrompy/sampling/base/sampling_engine_base.py
+++ b/rrompy/sampling/base/sampling_engine_base.py
@@ -1,178 +1,191 @@
# 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, HFEng, strLst
from rrompy.utilities.base import verbosityDepth
-from rrompy.utilities.warning_manager import warn
+from rrompy.utilities.exception_manager import RROMPyWarning
__all__ = ['SamplingEngineBase']
class SamplingEngineBase:
"""HERE"""
nameBase = 0
- def __init__(self, HFEngine:HFEng, verbosity : int = 10):
+ def __init__(self, HFEngine:HFEng, verbosity : int = 10,
+ timestamp : bool = True):
self.verbosity = verbosity
+ self.timestamp = timestamp
if self.verbosity >= 10:
verbosityDepth("INIT",
"Initializing sampling engine of type {}.".format(
self.name()),
- end = "")
+ timestamp = self.timestamp)
self.HFEngine = HFEngine
if self.verbosity >= 10:
- verbosityDepth("DEL", " Done.", inline = True)
+ verbosityDepth("DEL", "Done initializing sampling engine.",
+ timestamp = self.timestamp)
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 resetHistory(self):
self.samples = None
self.nsamples = 0
self.mus = []
def popSample(self):
if hasattr(self, "nsamples") and self.nsamples > 1:
if self.samples.shape[1] > self.nsamples:
- warn(("More than 'nsamples' memory allocated for samples. "
- "Popping empty sample column."))
+ RROMPyWarning(("More than 'nsamples' memory allocated for "
+ "samples. Popping empty sample column."))
self.nsamples += 1
self.samples = self.samples[:, : -1]
self.nsamples -= 1
self.mus = self.mus[: -1]
else:
self.resetHistory()
def preallocateSamples(self, u:Np1D, mu:np.complex, n:int):
self.samples = np.empty((u.size, n), dtype = u.dtype)
self.samples[:, 0] = u
self.mus = np.empty((n,), dtype = np.complex)
self.mus[0] = mu
@property
def HFEngine(self):
"""Value of HFEngine. Its assignment resets history."""
return self._HFEngine
@HFEngine.setter
def HFEngine(self, HFEngine):
self._HFEngine = HFEngine
self.resetHistory()
def solveLS(self, mu:complex, RHS : Np1D = None,
homogeneized : bool = False) -> Np1D:
"""
Solve linear system.
Args:
mu: Parameter value.
Returns:
Solution of system.
"""
if self.verbosity >= 5:
- verbosityDepth("INIT", "Solving HF model for mu = {}.".format(mu))
+ verbosityDepth("INIT", "Solving HF model for mu = {}.".format(mu),
+ timestamp = self.timestamp)
u = self.HFEngine.solve(mu, RHS, homogeneized)
if self.verbosity >= 5:
- verbosityDepth("DEL", "Done solving HF model.")
+ verbosityDepth("DEL", "Done solving HF model.",
+ timestamp = self.timestamp)
return u
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'.
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.
"""
for j in range(self.nsamples):
self.HFEngine.plot(self.samples[:, j],
name = "{}_{}".format(name, j + self.nameBase),
save = save, what = what,
saveFormat = saveFormat, saveDPI = saveDPI,
**figspecs)
- def outParaviewSamples(self, name : str = "u", filename : str = "out",
- times : Np1D = None, what : strLst = 'all',
- forceNewFile : bool = True, filePW = None):
+ def outParaviewSamples(self, name : str = "u", folders : bool = True,
+ filename : str = "out", times : Np1D = None,
+ what : strLst = 'all', forceNewFile : bool = True,
+ filePW = None):
"""
Output samples to ParaView file.
Args:
name(optional): Base name to be used for data output.
+ folders(optional): Whether to split output in folders.
filename(optional): Name of output file.
times(optional): Timestamps.
what(optional): Which plots to do. If list, can contain 'MESH',
'ABS', 'PHASE', 'REAL', 'IMAG'. If str, same plus wildcard
'ALL'. Defaults to 'ALL'.
forceNewFile(optional): Whether to create new output file.
filePW(optional): Fenics File entity (for time series).
"""
if times is None: times = [0.] * self.nsamples
for j in range(self.nsamples):
self.HFEngine.outParaview(self.samples[:, j],
name = "{}_{}".format(name, j + self.nameBase),
- filename = "{}_{}_".format(filename, j),
+ filename = "{}_{}".format(filename, j),
time = times[j], what = what,
- forceNewFile = forceNewFile, filePW = filePW)
+ forceNewFile = forceNewFile, folder = folders,
+ filePW = filePW)
def outParaviewTimeDomainSamples(self, omegas : Np1D = None,
timeFinal : Np1D = None,
periodResolution : int = 20,
- name : str = "u",
+ name : str = "u", folders : bool = True,
filename : str = "out",
forceNewFile : bool = True):
"""
Output samples to ParaView file, converted to time domain.
Args:
omegas(optional): frequencies.
timeFinal(optional): final time of simulation.
periodResolution(optional): number of time steps per period.
name(optional): Base name to be used for data output.
+ folders(optional): Whether to split output in folders.
filename(optional): Name of output file.
forceNewFile(optional): Whether to create new output file.
"""
if omegas is None: omegas = np.real(self.mus)
if not isinstance(timeFinal, (list, tuple,)):
timeFinal = [timeFinal] * self.nsamples
+ idxpath = filename.rfind("/")
+ filenameStart = filename[: idxpath + 1]
+ filenameEnd = filename[idxpath + 1 :]
for j in range(self.nsamples):
self.HFEngine.outParaviewTimeDomain(self.samples[:, j],
omega = omegas[j], timeFinal = timeFinal[j],
periodResolution = periodResolution,
name = "{}_{}".format(name, j + self.nameBase),
- filename = "{}_{}_".format(filename, j),
- forceNewFile = forceNewFile)
+ filename = "{}_{}".format(filename, j),
+ forceNewFile = forceNewFile,
+ folder = folders)
diff --git a/rrompy/sampling/linear_problem/sampling_engine_arnoldi.py b/rrompy/sampling/linear_problem/sampling_engine_arnoldi.py
index bed70fc..046f722 100644
--- a/rrompy/sampling/linear_problem/sampling_engine_arnoldi.py
+++ b/rrompy/sampling/linear_problem/sampling_engine_arnoldi.py
@@ -1,140 +1,142 @@
# 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.sampling.base.pod_engine import PODEngine
from .sampling_engine_krylov import SamplingEngineKrylov
from rrompy.utilities.base.types import Np1D
from rrompy.utilities.base import verbosityDepth
__all__ = ['SamplingEngineArnoldi']
class SamplingEngineArnoldi(SamplingEngineKrylov):
"""HERE"""
def resetHistory(self):
super().resetHistory()
self.HArnoldi = None
self.RArnoldi = None
self.RHSs = None
self.samplesAug = None
def popSample(self):
if hasattr(self, "nsamples") and self.nsamples > 1:
self.HArnoldi = self.HArnoldi[: -1, : -1]
self.RArnoldi = self.RArnoldi[: -1, : -1]
if self.nsamples > 2:
self.RHSs = self.RHSs[:, : -1]
else:
self.RHSs = None
self.samplesAug = self.RHSs[self.HFEngine.V.dim() :, : -1]
super().popSample()
@property
def HFEngine(self):
"""Value of HFEngine. Its assignment resets history."""
return self._HFEngine
@HFEngine.setter
def HFEngine(self, HFEngine):
self._HFEngine = HFEngine
self.resetHistory()
self.PODEngine = PODEngine(self._HFEngine)
def preprocesssamples(self):
ns = self.nsamples
if ns <= 0: return
return self.samplesAug[:, ns - 1].reshape((-1,self.HFEngine.V.dim())).T
def preprocessb(self, mu:complex, overwrite : bool = False,
homogeneized : bool = False):
ns = self.nsamples
r = super().preprocessb(mu, overwrite, homogeneized)
if ns == 0:
return r
elif ns == 1:
r = r / self.RArnoldi[0, 0]
else:
r = ((r - self.RHSs[:, :ns-1].dot(self.RArnoldi[:ns-1, ns-1]))
/ self.RArnoldi[ns-1, ns-1])
if overwrite:
self.RHSs[:, ns - 1] = r
else:
if ns == 1:
self.RHSs = r.reshape((- 1, 1))
else:
self.RHSs = np.hstack((self.RHSs, r[:, None]))
return r
def postprocessu(self, u:Np1D, overwrite : bool = False):
if self.verbosity >= 10:
- verbosityDepth("INIT", "Starting orthogonalization.")
+ verbosityDepth("INIT", "Starting orthogonalization.",
+ timestamp = self.timestamp)
ns = self.nsamples
nsAug = (ns + 1) * self.HFEngine.V.dim()
if ns == 0:
u, h, _ = self.PODEngine.GS(u, np.empty((0, 0)))
r = h[0]
uAug = copy(u)
else:
uAug = np.concatenate((self.samplesAug[self.HFEngine.V.dim()
- nsAug :, ns - 1],
u), axis = None)
u, h, uAug = self.PODEngine.GS(u, self.samples[:, : ns], ns, uAug,
self.samplesAug[- nsAug :, : ns])
if overwrite:
self.HArnoldi[: ns + 1, ns] = h
if ns > 0:
r = self.HArnoldi[: ns + 1, 1 : ns + 1].dot(
self.RArnoldi[: ns, ns - 1])
self.RArnoldi[: ns + 1, ns] = r
self.samplesAug[- nsAug :, ns] = uAug
else:
if ns == 0:
self.HArnoldi = h.reshape((1, 1))
self.RArnoldi = r.reshape((1, 1))
self.samplesAug = uAug.reshape((-1, 1))
else:
self.HArnoldi=np.block([[ self.HArnoldi, h[:-1, None]],
[np.zeros((1, ns)), h[-1]]])
if ns > 0:
r = self.HArnoldi[: ns + 1, 1 : ns + 1].dot(
self.RArnoldi[: ns, ns - 1])
self.RArnoldi=np.block([[ self.RArnoldi, r[:-1, None]],
[np.zeros((1, ns)), r[-1]]])
self.samplesAug=np.vstack((np.zeros((self.HFEngine.V.dim(),
ns)),
self.samplesAug))
self.samplesAug = np.hstack((self.samplesAug, uAug[:, None]))
if self.verbosity >= 10:
- verbosityDepth("DEL", "Done orthogonalizing.")
+ verbosityDepth("DEL", "Done orthogonalizing.",
+ timestamp = self.timestamp)
return u
def preallocateSamples(self, u:Np1D, mu:np.complex, n:int):
super().preallocateSamples(u, mu, n)
h = self.HArnoldi
r = self.RArnoldi
saug = self.samplesAug
self.HArnoldi = np.zeros((n, n), dtype = u.dtype)
self.HArnoldi[0, 0] = h[0, 0]
self.RArnoldi = np.zeros((n, n), dtype = u.dtype)
self.RArnoldi[0, 0] = r[0, 0]
self.RHSs = np.empty((u.size, n - 1), dtype = u.dtype)
self.samplesAug = np.zeros((self.HFEngine.V.dim() * (n + 1), n),
dtype = u.dtype)
self.samplesAug[- self.HFEngine.V.dim() :, 0] = saug[:, 0]
diff --git a/rrompy/sampling/linear_problem/sampling_engine_krylov.py b/rrompy/sampling/linear_problem/sampling_engine_krylov.py
index 516529e..d2e4d98 100644
--- a/rrompy/sampling/linear_problem/sampling_engine_krylov.py
+++ b/rrompy/sampling/linear_problem/sampling_engine_krylov.py
@@ -1,85 +1,91 @@
# 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.sampling.base.sampling_engine_base import SamplingEngineBase
from rrompy.utilities.base.types import Np1D, Np2D
from rrompy.utilities.base import verbosityDepth
+from rrompy.utilities.exception_manager import RROMPyException
__all__ = ['SamplingEngineKrylov']
class SamplingEngineKrylov(SamplingEngineBase):
"""HERE"""
def preprocesssamples(self):
if self.samples is None: return
return self.samples[:, : self.nsamples]
def preprocessb(self, mu:complex, overwrite : bool = False,
homogeneized : bool = False):
return self.HFEngine.b(mu, self.nsamples, homogeneized = homogeneized)
def postprocessu(self, u:Np1D, overwrite : bool = False):
return u
def nextSample(self, mu:complex, overwrite : bool = False,
homogeneized : bool = False) -> Np1D:
ns = self.nsamples
if self.verbosity >= 10:
verbosityDepth("INIT", ("Setting up computation of {}-th Taylor "
- "coefficient.").format(ns))
+ "coefficient.").format(ns),
+ timestamp = self.timestamp)
samplesOld = self.preprocesssamples()
RHS = self.preprocessb(mu, overwrite = overwrite,
homogeneized = homogeneized)
for i in range(1, ns + 1):
RHS -= self.HFEngine.A(mu, i).dot(samplesOld[:, - i])
if self.verbosity >= 10:
- verbosityDepth("DEL", "Done setting up for Taylor coefficient.")
+ verbosityDepth("DEL", "Done setting up for Taylor coefficient.",
+ timestamp = self.timestamp)
u = self.postprocessu(self.solveLS(mu, RHS = RHS,
homogeneized = homogeneized),
overwrite = overwrite)
if overwrite:
self.samples[:, ns] = u
self.mus[ns] = mu
else:
if ns == 0:
self.samples = u[:, None]
else:
self.samples = np.hstack((self.samples, u[:, None]))
self.mus = self.mus + [mu]
self.nsamples += 1
return u
def iterSample(self, mu:complex, n:int,
homogeneized : bool = False) -> Np2D:
if self.verbosity >= 5:
- verbosityDepth("INIT", "Starting sampling iterations at mu = {}."\
- .format(mu))
+ verbosityDepth("INIT", ("Starting sampling iterations at mu = "
+ "{}.").format(mu),
+ timestamp = self.timestamp)
if n <= 0:
- raise Exception(("Number of Krylov iterations must be positive."))
+ raise RROMPyException(("Number of Krylov iterations must be "
+ "positive."))
self.resetHistory()
u = self.nextSample(mu, homogeneized = homogeneized)
if n > 1:
self.preallocateSamples(u, mu, n)
for _ in range(1, n):
self.nextSample(mu, overwrite = True,
homogeneized = homogeneized)
if self.verbosity >= 5:
- verbosityDepth("DEL", "Finished sampling iterations.")
+ verbosityDepth("DEL", "Finished sampling iterations.",
+ timestamp = self.timestamp)
return self.samples
diff --git a/rrompy/sampling/linear_problem/sampling_engine_lagrange.py b/rrompy/sampling/linear_problem/sampling_engine_lagrange.py
index 5fca1f7..4939b77 100644
--- a/rrompy/sampling/linear_problem/sampling_engine_lagrange.py
+++ b/rrompy/sampling/linear_problem/sampling_engine_lagrange.py
@@ -1,67 +1,70 @@
# 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.sampling.base.sampling_engine_base import SamplingEngineBase
from rrompy.utilities.base.types import Np1D, Np2D
from rrompy.utilities.base import verbosityDepth
+from rrompy.utilities.exception_manager import RROMPyException
__all__ = ['SamplingEngineLagrange']
class SamplingEngineLagrange(SamplingEngineBase):
"""HERE"""
nameBase = 1
def postprocessu(self, u:Np1D, overwrite : bool = False):
return u
def nextSample(self, mu:complex, overwrite : bool = False,
homogeneized : bool = False) -> Np1D:
ns = self.nsamples
u = self.postprocessu(self.solveLS(mu, homogeneized = homogeneized),
overwrite = overwrite)
if overwrite:
self.samples[:, ns] = u
self.mus[ns] = mu
else:
if ns == 0:
self.samples = u[:, None]
else:
self.samples = np.hstack((self.samples, u[:, None]))
self.mus = self.mus + [mu]
self.nsamples += 1
return u
def iterSample(self, mus:Np1D, homogeneized : bool = False) -> Np2D:
if self.verbosity >= 5:
- verbosityDepth("INIT", "Starting sampling iterations.")
+ verbosityDepth("INIT", "Starting sampling iterations.",
+ timestamp = self.timestamp)
n = mus.size
if n <= 0:
- raise Exception(("Number of samples must be positive."))
+ raise RROMPyException(("Number of samples must be positive."))
self.resetHistory()
u = self.nextSample(mus[0], homogeneized = homogeneized)
if n > 1:
self.preallocateSamples(u, mus[0], n)
for j in range(1, n):
self.nextSample(mus[j], overwrite = True,
homogeneized = homogeneized)
if self.verbosity >= 5:
- verbosityDepth("DEL", "Finished sampling iterations.")
+ verbosityDepth("DEL", "Finished sampling iterations.",
+ timestamp = self.timestamp)
return self.samples
diff --git a/rrompy/sampling/linear_problem/sampling_engine_lagrange_pod.py b/rrompy/sampling/linear_problem/sampling_engine_lagrange_pod.py
index 82faacb..60be1d8 100644
--- a/rrompy/sampling/linear_problem/sampling_engine_lagrange_pod.py
+++ b/rrompy/sampling/linear_problem/sampling_engine_lagrange_pod.py
@@ -1,75 +1,77 @@
# 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.sampling.base.pod_engine import PODEngine
from .sampling_engine_lagrange import SamplingEngineLagrange
from rrompy.utilities.base.types import Np1D
from rrompy.utilities.base import verbosityDepth
__all__ = ['SamplingEngineLagrangePOD']
class SamplingEngineLagrangePOD(SamplingEngineLagrange):
"""HERE"""
def resetHistory(self):
super().resetHistory()
self.RPOD = None
def popSample(self):
if hasattr(self, "nsamples") and self.nsamples > 1:
self.RPOD = self.RPOD[: -1, : -1]
super().popSample()
@property
def HFEngine(self):
"""Value of HFEngine. Its assignment resets history."""
return self._HFEngine
@HFEngine.setter
def HFEngine(self, HFEngine):
self._HFEngine = HFEngine
self.resetHistory()
self.PODEngine = PODEngine(self._HFEngine)
def postprocessu(self, u:Np1D, overwrite : bool = False):
if self.verbosity >= 10:
- verbosityDepth("INIT", "Starting orthogonalization.")
+ verbosityDepth("INIT", "Starting orthogonalization.",
+ timestamp = self.timestamp)
ns = self.nsamples
if ns == 0:
u, r, _ = self.PODEngine.GS(u, np.empty((0, 0)))
r = r[0]
else:
u, r, _ = self.PODEngine.GS(u, self.samples[:, : ns], ns)
if overwrite:
self.RPOD[: ns + 1, ns] = r
else:
if ns == 0:
self.RPOD = r.reshape((1, 1))
else:
self.RPOD=np.block([[ self.RPOD, r[:-1, None]],
[np.zeros((1, ns)), r[-1]]])
if self.verbosity >= 10:
- verbosityDepth("DEL", "Done orthogonalizing.")
+ verbosityDepth("DEL", "Done orthogonalizing.",
+ timestamp = self.timestamp)
return u
def preallocateSamples(self, u:Np1D, mu:np.complex, n:int):
super().preallocateSamples(u, mu, n)
r = self.RPOD
self.RPOD = np.zeros((n, n), dtype = u.dtype)
self.RPOD[0, 0] = r[0, 0]
diff --git a/rrompy/utilities/base/custom_fit.py b/rrompy/utilities/base/custom_fit.py
index 52a8dd1..a64d896 100644
--- a/rrompy/utilities/base/custom_fit.py
+++ b/rrompy/utilities/base/custom_fit.py
@@ -1,146 +1,146 @@
# 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")
+ raise RROMPyException("expected 2D vector for van")
if van.size == 0:
- raise TypeError("expected non-empty vector for van")
+ raise RROMPyException("expected non-empty vector for van")
if y.ndim < 1 or y.ndim > 2:
- raise TypeError("expected 1D or 2D array for y")
+ raise RROMPyException("expected 1D or 2D array for y")
if len(van) != len(y):
- raise TypeError("expected van and y to have same length")
+ raise RROMPyException("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 isinstance(w, (str, )) and w.upper() == "AUTO":
# Determine the norms of the design matrix rows.
if issubclass(van.dtype.type, np.complexfloating):
w = np.sqrt((np.square(van.real) + np.square(van.imag)).sum(1))
else:
w = np.sqrt(np.square(van).sum(1))
w[w == 0] = 1
w = np.power(w, -1.)
if w is not None:
w = np.asarray(w) + 0.0
if w.ndim != 1:
- raise TypeError("expected 1D vector for w")
+ raise RROMPyException("expected 1D vector for w")
if len(van) != len(w):
- raise TypeError("expected van and w to have same length")
+ raise RROMPyException("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)
+ RROMPyWarning(msg, np.polynomial.polyutils.RankWarning, stacklevel = 2)
if full:
return c, [resids, rank, s, rcond]
else:
return c
diff --git a/rrompy/utilities/base/purge_dict.py b/rrompy/utilities/base/purge_dict.py
index dcb2108..3ace6e4 100644
--- a/rrompy/utilities/base/purge_dict.py
+++ b/rrompy/utilities/base/purge_dict.py
@@ -1,43 +1,42 @@
# 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.find_dict_str_key import findDictStrKey
from rrompy.utilities.base.types import ListAny, DictAny
-from rrompy.utilities.warning_manager import warn
+from rrompy.utilities.exception_manager import RROMPyWarning
__all__ = ['purgeDict']
def purgeDict(dct:DictAny, allowedKeys : ListAny = [], silent : bool = False,
complement : bool = False, dictname : str = "",
baselevel : int = 0) -> DictAny:
if dictname != "":
dictname = " in " + dictname
dctcp = {}
for key in dct.keys():
akey = findDictStrKey(key, allowedKeys)
if (akey is None) != complement:
if not silent:
- warn("Ignoring key {0}{2} with value {1}.".format(key,
- dct[key],
- dictname),
- baselevel)
+ RROMPyWarning(("Ignoring key {0}{2} with value "
+ "{1}.").format(key, dct[key], dictname),
+ baselevel)
else:
if akey is None:
akey = key
dctcp[akey] = dct[key]
return dctcp
diff --git a/rrompy/utilities/base/purge_list.py b/rrompy/utilities/base/purge_list.py
index 70cf5c0..8cd28b2 100644
--- a/rrompy/utilities/base/purge_list.py
+++ b/rrompy/utilities/base/purge_list.py
@@ -1,39 +1,40 @@
# 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.find_dict_str_key import findDictStrKey
from rrompy.utilities.base.types import ListAny
-from rrompy.utilities.warning_manager import warn
+from rrompy.utilities.exception_manager import RROMPyWarning
__all__ = ['purgeList']
def purgeList(lst:ListAny, allowedEntries : ListAny = [],
silent : bool = False, complement : bool = False,
listname : str = "", baselevel : int = 0) -> ListAny:
if listname != "":
listname = " in " + listname
lstcp = []
for x in lst:
ax = findDictStrKey(x, allowedEntries)
if (ax is None) != complement:
if not silent:
- warn("Ignoring entry {0}{1}.".format(x, listname), baselevel)
+ RROMPyWarning("Ignoring entry {0}{1}.".format(x, listname),
+ baselevel)
else:
lstcp = lstcp + [ax]
return lstcp
diff --git a/rrompy/utilities/base/sobol.py b/rrompy/utilities/base/sobol.py
index 69afeae..593b840 100644
--- a/rrompy/utilities/base/sobol.py
+++ b/rrompy/utilities/base/sobol.py
@@ -1,79 +1,80 @@
# 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.exception_manager import RROMPyException
__all__ = ['sobolGenerate']
def low2bit(n):
if n < 0:
- raise Exception("Only positive integers allowed.")
+ raise RROMPyException("Only positive integers allowed.")
j = 2
while (n % j) // (j / 2) == 1: j = j * 2
return int(np.log2(j)) - 1
def sobolGenerate(dim_num, n, seed = 0):
SEEDBITS = 30
v = np.zeros((dim_num, SEEDBITS), dtype = int)
v[:, 0] = 1
v[2:, 1] = np.array([1,3,1,3,1,3,3,1,3,1,3,1,3,1,1,3,1,3,1,3,1,3,3,1,3,1,3,
1,3,1,1,3,1,3,1,3,1,3])[:max(0,dim_num-2)]
v[3:, 2] = np.array([7,5,1,3,3,7,5,5,7,7,1,3,3,7,5,1,1,5,3,3,1,7,5,1,3,3,7,
5,1,1,5,7,7,5,1,3,3])[:max(0,dim_num-3)]
v[5:, 3] = np.array([1,7,9,13,11,1,3,7,9,5,13,13,11,3,15,5,3,15,7,9,13,9,1,
11,7,5,15,1,15,11,5,3,1,7,9])[:max(0,dim_num-5)]
v[7:, 4] = np.array([9,3,27,15,29,21,23,19,11,25,7,13,17,1,25,29,3,31,11,5,
23,27,19,21,5,1,17,13,7,15,9,31,9])[:max(0,dim_num-7)]
v[13:, 5] = np.array([37,33,7,5,11,39,63,27,17,15,23,29,3,21,13,31,25,9,49,
33,19,29,11,19,27,15,25])[:max(0,dim_num-13)]
v[19:, 6] = np.array([13,33,115,41,79,17,29,119,75,73,105,7,59,65,21,3,113,
61,89,45,107])[:max(0,dim_num-19)]
v[37:, 7] = np.array([7,23,39])[:max(0,dim_num-37)]
poly = [1,3,7,11,13,19,25,37,59,47,61,55,41,67,97,91,109,103,115,131,193,
137,145,143,241,157,185,167,229,171,213,191,253,203,211,239,247,
285,369,299]
v[0, :] = 1
for i in range(1, dim_num):
j = poly[i]
m = int(1 + np.floor(np.log2(j)))
includ = np.array([int(x) for x in "{0:b}".format(j)], dtype = int)
for j in range(m - 1, SEEDBITS):
newv = v[i, j - m + 1]
l = 1
for k in range(1, m):
l *= 2
if includ[k]:
newv = np.bitwise_xor(newv, l * v[i, j - k])
v[i, j] = newv
v = np.multiply(v, np.power(2, np.arange(SEEDBITS)[::-1]))
recipd = np.power(2., - SEEDBITS)
lastq = np.zeros(dim_num, dtype = int)
####
if seed < 0:
seed = 0
for seed_temp in range(seed):
l = low2bit(seed_temp)
lastq = np.bitwise_xor(lastq, v[:, l])
r = np.empty((n, dim_num))
for seed_temp in range(seed, seed + n):
l = low2bit(seed_temp)
r[seed_temp - seed, :] = lastq * recipd
lastq = np.bitwise_xor(lastq, v[:, l])
return r
diff --git a/rrompy/utilities/base/verbosity_depth.py b/rrompy/utilities/base/verbosity_depth.py
index 904c6d4..5032238 100644
--- a/rrompy/utilities/base/verbosity_depth.py
+++ b/rrompy/utilities/base/verbosity_depth.py
@@ -1,63 +1,49 @@
# 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 import getTimestamp
+from rrompy.utilities.exception_manager import RROMPyException
__all__ = ["verbosityDepth"]
-def verbosityDepth(vdtype:str, message:str, inline : bool = False,
- end : str = "\n", timestamp : bool = True):
- global _verbosity_depth
+def verbosityDepth(vdtype:str, message:str, end : str = "\n",
+ timestamp : bool = True):
+ global RROMPy_verbosity_depth
assert isinstance(vdtype, str)
if vdtype.upper() not in ["INIT", "MAIN", "DEL"]:
- raise Exception("Verbosity depth type not recognized.")
+ raise RROMPyException("Verbosity depth type not recognized.")
+ out = "{} ".format(getTimestamp()) if timestamp else ""
if vdtype == "INIT":
- if "_verbosity_depth" in globals():
- _verbosity_depth += 1
- else:
- _verbosity_depth = 1
- if inline:
- out = ""
- else:
- if timestamp: out = "{} ".format(getTimestamp())
- else: out = ""
- out += "--" * (_verbosity_depth - 2)
- out += "> " * (_verbosity_depth > 1)
+ if "RROMPy_verbosity_depth" not in globals():
+ RROMPy_verbosity_depth = 0
+ RROMPy_verbosity_depth += 1
+ out += "│" * (RROMPy_verbosity_depth - 1)
+ out += "┌"
else:
- assert "_verbosity_depth" in globals()
+ assert "RROMPy_verbosity_depth" in globals()
if vdtype == "MAIN":
- if inline:
- out = ""
- else:
- if timestamp: out = "{} ".format(getTimestamp())
- else: out = ""
- out += "--" * (_verbosity_depth - 1)
- out += "> " * (_verbosity_depth > 1)
+ out += "│" * (RROMPy_verbosity_depth - 1)
+ out += "├"
elif vdtype == "DEL":
- _verbosity_depth -= 1
- if inline:
- out = ""
- else:
- if timestamp: out = "{} ".format(getTimestamp())
- else: out = ""
- out += "--" * (_verbosity_depth - 1)
- out += "> " * (_verbosity_depth > 0)
- if _verbosity_depth <= 0:
- del _verbosity_depth
- print("{}{}".format(out, message), end = end)
+ RROMPy_verbosity_depth -= 1
+ out += "│" * RROMPy_verbosity_depth
+ out += "└"
+ if RROMPy_verbosity_depth <= 0: del RROMPy_verbosity_depth
+ if message != "":
+ print("{}{}".format(out, message), end = end)
return
diff --git a/rrompy/reduction_methods/base/__init__.py b/rrompy/utilities/exception_manager/__init__.py
similarity index 72%
copy from rrompy/reduction_methods/base/__init__.py
copy to rrompy/utilities/exception_manager/__init__.py
index 853116c..ed0c56f 100644
--- a/rrompy/reduction_methods/base/__init__.py
+++ b/rrompy/utilities/exception_manager/__init__.py
@@ -1,29 +1,31 @@
# 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
+from .exception_manager import RROMPyException
+from .mode_assert import RROMPy_READY, RROMPy_FRAGILE, modeAssert
+from .warning_manager import RROMPyWarning
__all__ = [
- 'GenericApproximant',
- 'setupFitCallables',
- 'checkRobustTolerance'
+ 'RROMPyException',
+ 'RROMPy_READY',
+ 'RROMPy_FRAGILE',
+ 'modeAssert',
+ 'RROMPyWarning'
]
diff --git a/rrompy/utilities/warning_manager/__init__.py b/rrompy/utilities/exception_manager/exception_manager.py
similarity index 90%
rename from rrompy/utilities/warning_manager/__init__.py
rename to rrompy/utilities/exception_manager/exception_manager.py
index 7138de5..89de98e 100644
--- a/rrompy/utilities/warning_manager/__init__.py
+++ b/rrompy/utilities/exception_manager/exception_manager.py
@@ -1,25 +1,22 @@
# 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 .warning_manager import warn
-
-__all__ = [
- 'warn'
- ]
-
+__all__ = ["RROMPyException"]
+class RROMPyException(Exception):
+ pass
diff --git a/rrompy/reduction_methods/base/__init__.py b/rrompy/utilities/exception_manager/mode_assert.py
similarity index 52%
copy from rrompy/reduction_methods/base/__init__.py
copy to rrompy/utilities/exception_manager/mode_assert.py
index 853116c..1050376 100644
--- a/rrompy/reduction_methods/base/__init__.py
+++ b/rrompy/utilities/exception_manager/mode_assert.py
@@ -1,29 +1,35 @@
# 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
+from rrompy.utilities.exception_manager import RROMPyException
-__all__ = [
- 'GenericApproximant',
- 'setupFitCallables',
- 'checkRobustTolerance'
- ]
+__all__ = ['RROMPy_READY', 'RROMPy_FRAGILE', 'purgeList']
+RROMPy_READY = "ready"
+RROMPy_FRAGILE = "fragile"
+
+def modeAssert(mode, check = RROMPy_READY, message = ""):
+ if isinstance(check, (tuple, list,)):
+ if mode not in check:
+ raise RROMPyException("Current mode not in {}. {}".format(check,
+ message))
+ else:
+ if mode != check:
+ raise RROMPyException("Current mode not {}. {}".format(check,
+ message))
diff --git a/rrompy/utilities/warning_manager/warning_manager.py b/rrompy/utilities/exception_manager/warning_manager.py
similarity index 69%
rename from rrompy/utilities/warning_manager/warning_manager.py
rename to rrompy/utilities/exception_manager/warning_manager.py
index 867d677..70b996b 100644
--- a/rrompy/utilities/warning_manager/warning_manager.py
+++ b/rrompy/utilities/exception_manager/warning_manager.py
@@ -1,36 +1,35 @@
# 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 traceback as tb
from rrompy.utilities.base import getTimestamp
-__all__ = ['warn']
+__all__ = ['RROMPyWarning']
-def warn(msg : str = "", stacklevel : int = 0, timestamp : bool = True):
+def RROMPyWarning(msg : str = "", stacklevel : int = 0):
frameSummary = tb.extract_stack()[- 3 - stacklevel]
- if timestamp: timestamp = "{} ".format(getTimestamp())
- else: timestamp = ""
+ timestamp = getTimestamp()
if frameSummary.name == "": name = ""
else: name = ", within {}".format(frameSummary.name)
- print("{}\n\x1b[3m Warning at {}:{}{}:\x1b[0m".format(timestamp,
- frameSummary.filename,
- frameSummary.lineno,
- name))
+ print("{}\x1b[3m Warning at {}:{}{}:\x1b[0m".format(timestamp,
+ frameSummary.filename,
+ frameSummary.lineno,
+ name))
print("> \x1b[31m{}\x1b[0m".format(frameSummary.line))
if len(msg) > 0:
print("\x1b[3m {}\x1b[0m\n".format(msg))
diff --git a/rrompy/utilities/parameter_sampling/fft_sampler.py b/rrompy/utilities/parameter_sampling/fft_sampler.py
index 57c6a88..7c87e68 100644
--- a/rrompy/utilities/parameter_sampling/fft_sampler.py
+++ b/rrompy/utilities/parameter_sampling/fft_sampler.py
@@ -1,97 +1,98 @@
# 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.parameter_sampling.generic_sampler import GenericSampler
from rrompy.utilities.base.types import Np1D, List, Tuple
+from rrompy.utilities.exception_manager import RROMPyException
__all__ = ['FFTSampler']
class FFTSampler(GenericSampler):
"""Generator of FFT-type sample points on scaled roots of unity."""
def generatePoints(self, n:Np1D) -> Tuple[List[Np1D], Np1D]:
"""Array of sample points and array of weights."""
super().generatePoints(n)
d = len(self.lims[0])
try:
len(n)
except:
n = np.array([n])
if len(n) != d:
- raise Exception(("Numbers of points must have same dimension as "
- "limits."))
+ raise RROMPyException(("Numbers of points must have same "
+ "dimension as limits."))
for j in range(d):
a, b = self.lims[0][j], self.lims[1][j]
if self.scaling is not None:
a, b = self.scaling[j](a), self.scaling[j](b)
c, r = (a + b) / 2., np.abs(a - b) / 2.
xj = c + r * np.exp(1.j *
np.linspace(0, 2 * np.pi, n[j] + 1)[:-1, None])
wj = r / n[j] * np.ones(n[j])
if self.scalingInv is not None:
xj = self.scalingInv[j](xj)
if j == 0:
x = xj
w = wj
xsize = n[0]
else:
x = np.concatenate((np.kron(np.ones(n[j])[:, None], x),
np.kron(xj, np.ones(xsize)[:, None])),
axis = 1)
w = np.multiply(np.kron(np.ones(n[j]), w),
np.kron(wj, np.ones(xsize)))
xsize = xsize * n[j]
return [y.flatten() for y in np.split(x, xsize)], w
def refine(self, n:int) -> Tuple[List[Np1D], Np1D]:
"""
Apply refinement. If points are not nested, equivalent to
generatePoints([2 * x for x in n]).
"""
super().generatePoints(n)
d = len(self.lims[0])
try:
len(n)
except:
n = np.array([n])
if len(n) != d:
- raise Exception(("Numbers of points must have same dimension as "
- "limits."))
+ raise RROMPyException(("Numbers of points must have same "
+ "dimension as limits."))
for j in range(d):
a, b = self.lims[0][j], self.lims[1][j]
if self.scaling is not None:
a, b = self.scaling[j](a), self.scaling[j](b)
c, r = (a + b) / 2., np.abs(a - b) / 2.
xj = c + r * np.exp(1.j * (np.pi / n[j]
+ np.linspace(0, 2 * np.pi, n[j] + 1)[:-1, None]))
wj = r / n[j] * np.ones(n[j])
if self.scalingInv is not None:
xj = self.scalingInv[j](xj)
if j == 0:
x = xj
w = wj
xsize = n[0]
else:
x = np.concatenate((np.kron(np.ones(n[j])[:, None], x),
np.kron(xj, np.ones(xsize)[:, None])),
axis = 1)
w = np.multiply(np.kron(np.ones(n[j]), w),
np.kron(wj, np.ones(xsize)))
xsize = xsize * n[j]
return [y.flatten() for y in np.split(x, xsize)], w
diff --git a/rrompy/utilities/parameter_sampling/generic_sampler.py b/rrompy/utilities/parameter_sampling/generic_sampler.py
index 766d3cb..ac3bf00 100644
--- a/rrompy/utilities/parameter_sampling/generic_sampler.py
+++ b/rrompy/utilities/parameter_sampling/generic_sampler.py
@@ -1,107 +1,108 @@
# 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 rrompy.utilities.base.types import Np1D, Tuple, GenExpr, List
+from rrompy.utilities.exception_manager import RROMPyException
__all__ = ['GenericSampler']
class GenericSampler:
"""ABSTRACT. Generic generator of sample points."""
def __init__(self, lims:Tuple[Np1D, Np1D], scaling : List[callable] = None,
scalingInv : List[callable] = None):
self.lims = lims
self.scaling = scaling
self.scalingInv = scalingInv
def name(self) -> str:
return self.__class__.__name__
def __str__(self) -> str:
return "{}[{}{}]".format(self.name(),
np.array2string(self.lims[0], separator = '_'),
np.array2string(self.lims[1], separator = '_'))
def __repr__(self) -> str:
return self.__str__() + " at " + hex(id(self))
def __eq__(self, other) -> bool:
return self.__dict__ == other.__dict__
@property
def lims(self):
"""Value of lims."""
return self._lims
@lims.setter
def lims(self, lims):
if len(lims) != 2:
- raise Exception("2 limits must be specified.")
+ raise RROMPyException("2 limits must be specified.")
try:
lims = lims.tolist()
except:
lims = list(lims)
for j in range(2):
try:
len(lims[j])
except:
lims[j] = np.array([lims[j]])
if len(lims[0]) != len(lims[1]):
- raise Exception("The limits must have the same length.")
+ raise RROMPyException("The limits must have the same length.")
self._lims = lims
@property
def scaling(self):
"""Value of scaling."""
return self._scaling
@scaling.setter
def scaling(self, scaling):
if scaling is not None and not isinstance(scaling, list):
scaling = [scaling]
self._scaling = scaling
@property
def scalingInv(self):
"""Value of scalingInv."""
return self._scalingInv
@scalingInv.setter
def scalingInv(self, scalingInv):
if scalingInv is not None and not isinstance(scalingInv, list):
scalingInv = [scalingInv]
self._scalingInv = scalingInv
@abstractmethod
def generatePoints(self, n:GenExpr) -> Tuple[List[Np1D], Np1D]:
"""Array of points and array of weights."""
assert ((self.scaling is None
or len(self.scaling) == len(self.lims[0]))
and (self.scalingInv is None
or len(self.scalingInv) == len(self.lims[0])))
pass
def refine(self, n:int) -> Tuple[List[Np1D], Np1D]:
"""
Apply refinement. If points are not nested, equivalent to
generatePoints([2 * x - 1 for x in n]).
"""
try:
len(n)
except:
n = np.array([n])
return self.generatePoints([2 * nj - 1 for nj in n])
diff --git a/rrompy/utilities/parameter_sampling/manual_sampler.py b/rrompy/utilities/parameter_sampling/manual_sampler.py
index c069357..9dc76c3 100644
--- a/rrompy/utilities/parameter_sampling/manual_sampler.py
+++ b/rrompy/utilities/parameter_sampling/manual_sampler.py
@@ -1,81 +1,81 @@
# 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.parameter_sampling.generic_sampler import GenericSampler
from rrompy.utilities.base.types import Np1D, Tuple, List
-from rrompy.utilities.warning_manager import warn
+from rrompy.utilities.exception_manager import RROMPyWarning
__all__ = ['ManualSampler']
class ManualSampler(GenericSampler):
"""Manual generator of sample points."""
def __init__(self, lims:Tuple[Np1D, Np1D], points:Np1D,
scaling : List[callable] = None,
scalingInv : List[callable] = None):
super().__init__(lims = lims, scaling = scaling,
scalingInv = scalingInv)
self.points = points
def __str__(self) -> str:
return "{}[{}]".format(self.name(), "_".join(map(str, self.points)))
def __repr__(self) -> str:
return self.__str__() + " at " + hex(id(self))
def generatePoints(self, n:int) -> Tuple[List[Np1D], Np1D]:
"""Array of quadrature points and array of weights."""
super().generatePoints(None)
size = 1. / n
for j in range(len(self.lims[0])):
a, b = self.lims[0][j], self.lims[1][j]
if self.scaling is not None:
a, b = self.scaling[j](a), self.scaling[j](b)
size *= np.abs(a - b)
if n > len(self.points):
- warn(("Requested more points than given. Looping over first "
- "points."))
+ RROMPyWarning(("Requested more points than given. Looping over "
+ "first points."))
pts = np.tile(self.points,
[np.int(np.ceil(len(self.points) / n))])[: n]
else:
pts = self.points[: n]
return pts, np.ones(n) * size
def refine(self, n:int) -> Tuple[List[Np1D], Np1D]:
"""
Apply refinement. If points are not nested, equivalent to
generatePoints(2 * n - 1).
"""
super().generatePoints(None)
size = 1. / (n - 1)
for j in range(len(self.lims[0])):
a, b = self.lims[0][j], self.lims[1][j]
if self.scaling is not None:
a, b = self.scaling[j](a), self.scaling[j](b)
size *= np.abs(a - b)
if 2 * n - 1 > len(self.points):
- warn(("Requested more points than given. Looping over first "
- "points."))
+ RROMPyWarning(("Requested more points than given. Looping over "
+ "first points."))
pts = np.tile(self.points,
[np.int(np.ceil(len(self.points) / (2 * n - 1)))]
)[n : 2 * n - 1]
else:
pts = self.points[n : 2 * n - 1]
return pts, np.ones(n - 1) * size
diff --git a/rrompy/utilities/parameter_sampling/quadrature_sampler.py b/rrompy/utilities/parameter_sampling/quadrature_sampler.py
index 0984173..40b2852 100644
--- a/rrompy/utilities/parameter_sampling/quadrature_sampler.py
+++ b/rrompy/utilities/parameter_sampling/quadrature_sampler.py
@@ -1,130 +1,131 @@
# 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.parameter_sampling.generic_sampler import GenericSampler
from rrompy.utilities.base.types import Np1D, Tuple, List
+from rrompy.utilities.exception_manager import RROMPyException
__all__ = ['QuadratureSampler']
class QuadratureSampler(GenericSampler):
"""Generator of quadrature sample points."""
allowedKinds = ["UNIFORM", "CHEBYSHEV", "GAUSSLEGENDRE"]
def __init__(self, lims:Tuple[Np1D, Np1D], kind : str = "UNIFORM",
scaling : List[callable] = None,
scalingInv : List[callable] = None):
super().__init__(lims = lims, scaling = scaling,
scalingInv = scalingInv)
self.kind = kind
def __str__(self) -> str:
return "{}_{}".format(super().__str__(), self.kind)
def __repr__(self) -> str:
return self.__str__() + " at " + hex(id(self))
@property
def kind(self):
"""Value of kind."""
return self._kind
@kind.setter
def kind(self, kind):
if kind.upper() not in self.allowedKinds:
- raise Exception("Generator kind not recognized.")
+ raise RROMPyException("Generator kind not recognized.")
self._kind = kind.upper()
def generatePoints(self, n:Np1D) -> Tuple[List[Np1D], Np1D]:
"""Array of quadrature points and array of weights."""
super().generatePoints(n)
d = len(self.lims[0])
try:
len(n)
except:
n = np.array([n])
if len(n) != d:
- raise Exception(("Numbers of points must have same dimension as"
- "limits."))
+ raise RROMPyException(("Numbers of points must have same "
+ "dimension as limits."))
for j in range(d):
a, b = self.lims[0][j], self.lims[1][j]
if self.scaling is not None:
a, b = self.scaling[j](a), self.scaling[j](b)
if self.kind == "UNIFORM":
xj = np.linspace(a, b, n[j])[:, None]
wj = np.abs(a - b) / n[j] * np.ones(n[j])
elif self.kind == "CHEBYSHEV":
nodes, weights = np.polynomial.chebyshev.chebgauss(n[j])
xj = (a + b) / 2 + (a - b) / 2 * nodes[:, None]
wj = np.abs(a - b) / np.pi * weights
elif self.kind == "GAUSSLEGENDRE":
nodes, weights = np.polynomial.legendre.leggauss(n[j])
xj = (a + b) / 2 + (a - b) / 2 * nodes[:, None]
wj = np.abs(a - b) * weights
if self.scalingInv is not None:
xj = self.scalingInv[j](xj)
if j == 0:
x = xj
w = wj
xsize = n[0]
else:
x = np.concatenate((np.kron(np.ones(n[j])[:, None], x),
np.kron(xj, np.ones(xsize)[:, None])),
axis = 1)
w = np.multiply(np.kron(np.ones(n[j]), w),
np.kron(wj, np.ones(xsize)))
xsize = xsize * n[j]
return [y.flatten() for y in np.split(x, xsize)], w
def refine(self, n:int) -> Tuple[List[Np1D], Np1D]:
"""
Apply refinement. If points are not nested, equivalent to
generatePoints([2 * x - 1 for x in n]).
"""
if self.kind != "UNIFORM": return super().refine(n)
super().generatePoints(n)
d = len(self.lims[0])
try:
len(n)
except:
n = np.array([n])
if len(n) != d:
- raise Exception(("Numbers of points must have same dimension as"
- "limits."))
+ raise RROMPyException(("Numbers of points must have same "
+ "dimension as limits."))
for j in range(d):
a, b = self.lims[0][j], self.lims[1][j]
if self.scaling is not None:
a, b = self.scaling[j](a), self.scaling[j](b)
xj = np.linspace(a + (b - a) / 2. / (n[j] - 1),
b + (a - b) / 2. / (n[j] - 1), n[j] - 1)[:, None]
wj = np.abs(a - b) / (n[j] - 1) * np.ones(n[j] - 1)
if self.scalingInv is not None:
xj = self.scalingInv[j](xj)
if j == 0:
x = xj
w = wj
xsize = n[0] - 1
else:
x = np.concatenate((np.kron(np.ones(n[j] - 1)[:, None], x),
np.kron(xj, np.ones(xsize)[:, None])),
axis = 1)
w = np.multiply(np.kron(np.ones(n[j] - 1), w),
np.kron(wj, np.ones(xsize)))
xsize = xsize * (n[j] - 1)
return [y.flatten() for y in np.split(x, xsize)], w
diff --git a/rrompy/utilities/parameter_sampling/random_sampler.py b/rrompy/utilities/parameter_sampling/random_sampler.py
index a61763d..801bb34 100644
--- a/rrompy/utilities/parameter_sampling/random_sampler.py
+++ b/rrompy/utilities/parameter_sampling/random_sampler.py
@@ -1,80 +1,81 @@
# 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.sobol import sobolGenerate
from rrompy.utilities.parameter_sampling.generic_sampler import GenericSampler
from rrompy.utilities.base.types import Np1D, Tuple, List
+from rrompy.utilities.exception_manager import RROMPyException
__all__ = ['RandomSampler']
class RandomSampler(GenericSampler):
"""Generator of quadrature sample points."""
allowedKinds = ["UNIFORM", "SOBOL"]
def __init__(self, lims:Tuple[Np1D, Np1D], kind : str = "UNIFORM",
scaling : callable = None, scalingInv : callable = None):
super().__init__(lims = lims, scaling = scaling,
scalingInv = scalingInv)
self.kind = kind
def __str__(self) -> str:
return "{}_{}".format(super().__str__(), self.kind)
def __repr__(self) -> str:
return self.__str__() + " at " + hex(id(self))
@property
def kind(self):
"""Value of kind."""
return self._kind
@kind.setter
def kind(self, kind):
if kind.upper() not in self.allowedKinds:
- raise Exception("Generator kind not recognized.")
+ raise RROMPyException("Generator kind not recognized.")
self._kind = kind.upper()
def generatePoints(self, n:int, seed : int = 0) -> Tuple[List[Np1D], Np1D]:
"""Array of quadrature points and array of weights."""
super().generatePoints(n)
d = len(self.lims[0])
if self.kind == "UNIFORM":
np.random.seed(seed)
x = np.random.uniform(size = (n, d))
else:
x = sobolGenerate(d, n, seed)
for j in range(d):
a, b = self.lims[0][j], self.lims[1][j]
if self.scaling is not None:
a, b = self.scaling[j](a), self.scaling[j](b)
x[:, j] = a + (b - a) * x[:, j]
if self.scalingInv is not None:
x[:, j] = self.scalingInv[j](x[:, j])
return [y.flatten() for y in np.split(x, n)], np.ones(n)
def refine(self, n:int, seed : int = 420) -> Tuple[List[Np1D], Np1D]:
"""
Apply refinement. If points are not nested, equivalent to
generatePoints([2 * x - 1 for x in n]).
"""
try:
len(n)
except:
n = np.array([n])
return self.generatePoints([nj - 1 for nj in n], seed)
diff --git a/rrompy/utilities/parameter_sampling/warping_sampler.py b/rrompy/utilities/parameter_sampling/warping_sampler.py
index 0f14302..39d27be 100644
--- a/rrompy/utilities/parameter_sampling/warping_sampler.py
+++ b/rrompy/utilities/parameter_sampling/warping_sampler.py
@@ -1,153 +1,154 @@
# 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.parameter_sampling.generic_sampler import GenericSampler
from rrompy.utilities.base.types import Np1D, Tuple, List
+from rrompy.utilities.exception_manager import RROMPyException
__all__ = ['WarpingSampler', 'WarpingFunction']
class WarpingFunction:
"""Wrapper for warping function."""
def __init__(self, other : "WarpingFunction" = None, **kwargs):
if other is not None:
self._call_ = other._call_
self._repr_ = other._repr_
else:
self._call_ = kwargs["call"]
self._repr_ = kwargs["repr"]
if not callable(self._call_):
self._call_ = lambda x: self._call_
if callable(self._repr_):
self._repr_ = self._repr_()
def __call__(self, x):
return self._call_(x)
def __str__(self):
return self._repr_
def __repr__(self) -> str:
return self.__str__() + " at " + hex(id(self))
class WarpingSampler(GenericSampler):
"""Generator of sample points from warping of uniform ones."""
def __init__(self, lims:Tuple[Np1D, Np1D], warping : List[callable],
scaling : List[callable] = None,
scalingInv : List[callable] = None):
super().__init__(lims = lims, scaling = scaling,
scalingInv = scalingInv)
self.warping = warping
def __str__(self) -> str:
return "{}_{}".format(super().__str__(), self.warping)
def __repr__(self) -> str:
return self.__str__() + " at " + hex(id(self))
@property
def warping(self):
"""Value of warping."""
return self._warping
@warping.setter
def warping(self, warping):
if not isinstance(warping, (list, tuple,)):
warping = [warping] * len(self.lims[0])
for j in range(len(warping)):
if not isinstance(warping[j], (WarpingFunction,)):
warping[j] = WarpingFunction(call = warping[j].__call__,
repr = warping[j].__repr__)
self._warping = warping
def generatePoints(self, n:Np1D) -> Tuple[List[Np1D], Np1D]:
"""Array of quadrature points and array of weights."""
super().generatePoints(n)
assert len(self.warping) == len(self.lims[0])
d = len(self.lims[0])
try:
len(n)
except:
n = np.array([n])
if len(n) != d:
- raise Exception(("Numbers of points must have same dimension as"
- "limits."))
+ raise RROMPyException(("Numbers of points must have same "
+ "dimension as limits."))
for j in range(d):
a, b = self.lims[0][j], self.lims[1][j]
if self.scaling is not None:
a, b = self.scaling[j](a), self.scaling[j](b)
x0, sigma = (a + b) / 2, (a - b) / 2
xj0 = np.linspace(- 1., 1., n[j])[:, None]
xj = x0 + sigma * self.warping[j](xj0)
wj = np.abs(a - b) / (n[j] - 1) * np.ones(n[j])
if self.scalingInv is not None:
xj = self.scalingInv[j](xj)
if j == 0:
x = xj
w = wj
xsize = n[0]
else:
x = np.concatenate((np.kron(np.ones(n[j])[:, None], x),
np.kron(xj, np.ones(xsize)[:, None])),
axis = 1)
w = np.multiply(np.kron(np.ones(n[j]), w),
np.kron(wj, np.ones(xsize)))
xsize = xsize * n[j]
return [y.flatten() for y in np.split(x, xsize)], w
def refine(self, n:int) -> Tuple[List[Np1D], Np1D]:
"""
Apply refinement. If points are not nested, equivalent to
generatePoints([2 * x - 1 for x in n]).
"""
super().generatePoints(n)
assert len(self.warping) == len(self.lims[0])
d = len(self.lims[0])
try:
len(n)
except:
n = np.array([n])
if len(n) != d:
- raise Exception(("Numbers of points must have same dimension as"
- "limits."))
+ raise RROMPyException(("Numbers of points must have same "
+ "dimension as limits."))
for j in range(d):
a, b = self.lims[0][j], self.lims[1][j]
if self.scaling is not None:
a, b = self.scaling[j](a), self.scaling[j](b)
x0, sigma = (a + b) / 2, (a - b) / 2
xj0 = np.linspace(1. / (n[j] - 1) - 1., 1. - 1. / (n[j] - 1),
n[j] - 1)[:, None]
xj = x0 + sigma * self.warping[j](xj0)
wj = np.abs(a - b) / (n[j] - 2) * np.ones(n[j] - 1)
if self.scalingInv is not None:
xj = self.scalingInv[j](xj)
if j == 0:
x = xj
w = wj
xsize = n[0] - 1
else:
x = np.concatenate((np.kron(np.ones(n[j] - 1)[:, None], x),
np.kron(xj, np.ones(xsize)[:, None])),
axis = 1)
w = np.multiply(np.kron(np.ones(n[j] - 1), w),
np.kron(wj, np.ones(xsize)))
xsize = xsize * (n[j] - 1)
return [y.flatten() for y in np.split(x, xsize)], w
diff --git a/rrompy/utilities/parameter_sweeper/parameter_sweeper.py b/rrompy/utilities/parameter_sweeper/parameter_sweeper.py
index 2049059..d84a720 100644
--- a/rrompy/utilities/parameter_sweeper/parameter_sweeper.py
+++ b/rrompy/utilities/parameter_sweeper/parameter_sweeper.py
@@ -1,499 +1,504 @@
# 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 itertools
import csv
import numpy as np
from matplotlib import pyplot as plt
from rrompy.utilities.base.types import Np1D, DictAny, List, ROMEng
from rrompy.utilities.base import purgeList, getNewFilename, verbosityDepth
-from rrompy.utilities.warning_manager import warn
+from rrompy.utilities.exception_manager import RROMPyWarning
+from rrompy.utilities.exception_manager import RROMPyException
__all__ = ['ParameterSweeper']
def C2R2csv(x):
x = np.ravel(x)
y = np.concatenate((np.real(x), np.imag(x)))
z = np.ravel(np.reshape(y, [2, np.size(x)]).T)
return np.array2string(z, separator = '_', suppress_small = False,
max_line_width = np.inf, sign = '+',
formatter = {'all' : lambda x : "{:.15E}".format(x)}
)[1 : -1]
class ParameterSweeper:
"""
ROM approximant parameter sweeper.
Args:
ROMEngine(optional): Generic approximant class. Defaults to None.
mutars(optional): Array of parameter values to sweep. Defaults to empty
array.
params(optional): List of parameter settings (each as a dict) to
explore. Defaults to single empty set.
mostExpensive(optional): String containing label of most expensive
step, to be executed fewer times. Allowed options are 'HF' and
'Approx'. Defaults to 'HF'.
Attributes:
ROMEngine: Generic approximant class.
mutars: Array of parameter values to sweep.
params: List of parameter settings (each as a dict) to explore.
mostExpensive: String containing label of most expensive step, to be
executed fewer times.
"""
allowedOutputsStandard = ["normHF", "normApprox", "normRes", "normResRel",
"normErr", "normErrRel"]
allowedOutputs = allowedOutputsStandard + ["HFFunc", "ApproxFunc",
"ErrFunc", "ErrFuncRel"]
allowedOutputsFull = allowedOutputs + ["poles"]
def __init__(self, ROMEngine : ROMEng = None, mutars : Np1D = np.array([]),
params : List[DictAny] = [{}], mostExpensive : str = "HF"):
self.ROMEngine = ROMEngine
self.mutars = mutars
self.params = params
self.mostExpensive = mostExpensive
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))
@property
def mostExpensive(self):
"""Value of mostExpensive."""
return self._mostExpensive
@mostExpensive.setter
def mostExpensive(self, mostExpensive:str):
mostExpensive = mostExpensive.upper()
if mostExpensive not in ["HF", "APPROX"]:
- warn(("Value of mostExpensive not recognized. Overriding to "
- "'APPROX'."))
+ RROMPyWarning(("Value of mostExpensive not recognized. Overriding "
+ "to 'APPROX'."))
mostExpensive = "APPROX"
self._mostExpensive = mostExpensive
def checkValues(self) -> bool:
"""Check if sweep can be performed."""
if self.ROMEngine is None:
- raise Exception("ROMEngine is missing. Aborting.")
+ raise RROMPyException("ROMEngine is missing. Aborting.")
if len(self.mutars) == 0:
- raise Exception("Empty target parameter vector. Aborting.")
+ raise RROMPyException("Empty target parameter vector. Aborting.")
if len(self.params) == 0:
- raise Exception("Empty method parameters vector. Aborting.")
+ raise RROMPyException("Empty method parameters vector. Aborting.")
def sweep(self, filename : str = "out.dat", outputs : List[str] = [],
- verbose : int = 10):
+ verbose : int = 10, timestamp : bool = True):
self.checkValues()
try:
if outputs.upper() == "ALL":
outputs = self.allowedOutputsFull
except:
if len(outputs) == 0:
outputs = self.allowedOutputsStandard
outputs = purgeList(outputs, self.allowedOutputsFull,
listname = self.name() + ".outputs",
baselevel = 1)
poles = ("poles" in outputs)
if len(outputs) == 0:
- raise Exception("Empty outputs. Aborting.")
+ raise RROMPyException("Empty outputs. Aborting.")
outParList = self.ROMEngine.parameterList
Nparams = len(self.params)
if poles: polesCheckList = []
allowedParams = self.ROMEngine.parameterList
dotPos = filename.rfind('.')
if dotPos in [-1, len(filename) - 1]:
filename = getNewFilename(filename[:dotPos])
else:
filename = getNewFilename(filename[:dotPos], filename[dotPos + 1:])
append_write = "w"
initial_row = (outParList + ["muRe", "muIm"]
+ [x for x in self.allowedOutputs if x in outputs]
+ ["type"] + ["poles"] * poles)
with open(filename, append_write, buffering = 1) as fout:
writer = csv.writer(fout, delimiter=",")
writer.writerow(initial_row)
if self.mostExpensive == "HF":
outerSet = self.mutars
innerSet = self.params
elif self.mostExpensive == "APPROX":
outerSet = self.params
innerSet = self.mutars
for outerIdx, outerPar in enumerate(outerSet):
if self.mostExpensive == "HF":
i, mutar = outerIdx, outerPar
elif self.mostExpensive == "APPROX":
j, par = outerIdx, outerPar
self.ROMEngine.approxParameters = {k: par[k] for k in\
par.keys() & allowedParams}
self.ROMEngine.setupApprox()
for innerIdx, innerPar in enumerate(innerSet):
if self.mostExpensive == "APPROX":
i, mutar = innerIdx, innerPar
elif self.mostExpensive == "HF":
j, par = innerIdx, innerPar
self.ROMEngine.approxParameters = {k: par[k] for k in\
par.keys() & allowedParams}
self.ROMEngine.setupApprox()
if verbose >= 5:
- verbosityDepth("INIT", "Set {}/{}\tmu_{} = {:.10f}"\
- .format(j + 1, Nparams, i, mutar))
+ verbosityDepth("INIT", ("Set {}/{}\tmu_{} = "
+ "{:.10f}").format(j + 1,
+ Nparams, i,
+ mutar),
+ timestamp = timestamp)
outData = []
if "normHF" in outputs:
valNorm = self.ROMEngine.normHF(mutar)
outData = outData + [valNorm]
if "normApprox" in outputs:
val = self.ROMEngine.normApprox(mutar)
outData = outData + [val]
if "normRes" in outputs:
valNRes = self.ROMEngine.normRes(mutar)
outData = outData + [valNRes]
if "normResRel" in outputs:
if "normRes" not in outputs:
valNRes = self.ROMEngine.normRes(mutar)
val = self.ROMEngine.normRHS(mutar)
outData = outData + [valNRes / val]
if "normErr" in outputs:
valNErr = self.ROMEngine.normErr(mutar)
outData = outData + [valNErr]
if "normErrRel" in outputs:
if "normHF" not in outputs:
valNorm = self.ROMEngine.normHF(mutar)
if "normErr" not in outputs:
valNErr = self.ROMEngine.normErr(mutar)
outData = outData + [valNErr / valNorm]
if "HFFunc" in outputs:
valFunc = self.ROMEngine.HFEngine.functional(
self.ROMEngine.getHF(mutar))
outData = outData + [valFunc]
if "ApproxFunc" in outputs:
valFApp = self.ROMEngine.HFEngine.functional(
self.ROMEngine.getApprox(mutar))
outData = outData + [valFApp]
if "ErrFunc" in outputs:
if "HFFunc" not in outputs:
valFunc = self.ROMEngine.HFEngine.functional(
self.ROMEngine.getHF(mutar))
if "ApproxFunc" not in outputs:
valFApp = self.ROMEngine.HFEngine.functional(
self.ROMEngine.getApprox(mutar))
valFErr = np.abs(valFApp - valFunc)
outData = outData + [valFErr]
if "ErrFuncRel" in outputs:
if not ("HFFunc" in outputs or "ErrFunc" in outputs):
valFunc = self.ROMEngine.HFEngine.functional(
self.ROMEngine.getHF(mutar))
if not ("AppFunc" in outputs or "ErrFunc" in outputs):
valFApp = self.ROMEngine.HFEngine.functional(
self.ROMEngine.getApprox(mutar))
val = np.nan
if not np.isclose(valFunc, 0.):
val = valFApp / valFunc
outData = outData + [val]
writeData = []
for parn in outParList:
writeData = (writeData
+ [self.ROMEngine.approxParameters[parn]])
writeData = (writeData + [mutar.real, mutar.imag]
+ outData + [self.ROMEngine.name()])
if poles:
if j not in polesCheckList:
polesCheckList += [j]
writeData = writeData + [C2R2csv(
self.ROMEngine.getPoles())]
else:
writeData = writeData + [""]
writer.writerow(str(x) for x in writeData)
if verbose >= 5:
- verbosityDepth("DEL", "", end = "", inline = "")
+ verbosityDepth("DEL", "", timestamp = timestamp)
if verbose >= 5:
if self.mostExpensive == "APPROX":
- out = "Set {}/{}\tdone.\n".format(j + 1, Nparams)
+ out = "Set {}/{}\tdone.".format(j + 1, Nparams)
elif self.mostExpensive == "HF":
- out = "Point mu_{} = {:.10f}\tdone.\n".format(i, mutar)
- verbosityDepth("INIT", out)
- verbosityDepth("DEL", "", end = "", inline = "")
+ out = "Point mu_{} = {:.10f}\tdone.".format(i, mutar)
+ verbosityDepth("INIT", out, timestamp = timestamp)
+ verbosityDepth("DEL", "", timestamp = timestamp)
self.filename = filename
return self.filename
def read(self, filename:str, restrictions : DictAny = {},
outputs : List[str] = []) -> DictAny:
"""
Execute a query on a custom format CSV.
Args:
filename: CSV filename.
restrictions(optional): Parameter configurations to output.
Defaults to empty dictionary, i.e. output all.
outputs(optional): Values to output. Defaults to empty list, i.e.
no output.
Returns:
Dictionary of desired results, with a key for each entry of
outputs, and a numpy 1D array as corresponding value.
"""
with open(filename, 'r') as f:
reader = csv.reader(f, delimiter=',')
header = next(reader)
restrIndices, outputIndices, outputData = {}, {}, {}
for key in restrictions.keys():
try:
restrIndices[key] = header.index(key)
if not isinstance(restrictions[key], list):
restrictions[key] = [restrictions[key]]
restrictions[key] = copy(restrictions[key])
except:
- warn("Ignoring key {} from restrictions.".format(key))
+ RROMPyWarning("Ignoring key {} from restrictions.".format(
+ key))
for key in outputs:
try:
outputIndices[key] = header.index(key)
outputData[key] = np.array([])
except:
- warn("Ignoring key {} from outputs.".format(key))
+ RROMPyWarning("Ignoring key {} from outputs.".format(key))
for row in reader:
restrTrue = True
for key in restrictions.keys():
if row[restrIndices[key]] == restrictions[key]:
continue
try:
if np.any(np.isclose(float(row[restrIndices[key]]),
[float(x) for x in restrictions[key]])):
continue
except: pass
restrTrue = False
if restrTrue:
for key in outputIndices.keys():
try:
val = row[outputIndices[key]]
val = float(val)
finally:
outputData[key] = np.append(outputData[key], val)
return outputData
def plot(self, filename:str, xs:List[str], ys:List[str], zs:List[str],
onePlot : bool = False, save : str = None,
saveFormat : str = "eps", saveDPI : int = 100, **figspecs):
"""
Perform plots from data in filename.
Args:
filename: CSV filename.
xs: Values to put on x axes.
ys: Values to put on y axes.
zs: Meta-values for constraints.
onePlot(optional): Whether to create a single figure per x.
Defaults to False.
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.
"""
if save is not None:
save = save.strip()
zsVals = self.read(filename, outputs = zs)
zs = list(zsVals.keys())
zss = None
for key in zs:
vals = np.unique(zsVals[key])
if zss is None:
zss = copy(vals)
else:
zss = list(itertools.product(zss, vals))
lzs = len(zs)
for z in zss:
if lzs <= 1:
constr = {zs[0] : z}
else:
constr = {zs[j] : z[j] for j in range(len(zs))}
data = self.read(filename, restrictions = constr, outputs = xs+ys)
if onePlot:
for x in xs:
xVals = data[x]
p = plt.figure(**figspecs)
logScale = False
for y in ys:
yVals = data[y]
label = '{} vs {} for {}'.format(y, x, constr)
if np.min(yVals) <= - np.finfo(float).eps:
plt.plot(xVals, yVals, label = label)
else:
plt.plot(xVals, yVals, label = label)
if np.log10(np.max(yVals) / np.min(yVals)) > 1.:
logScale = True
if logScale:
ax = p.get_axes()[0]
ax.set_yscale('log')
plt.legend()
plt.grid()
if save is not None:
prefix = "{}_{}_vs_{}_{}".format(save, ys, x, constr)
plt.savefig(getNewFilename(prefix, saveFormat),
format = saveFormat, dpi = saveDPI)
plt.show()
plt.close()
else:
for x, y in itertools.product(xs, ys):
xVals, yVals = data[x], data[y]
label = '{} vs {} for {}'.format(y, x, constr)
p = plt.figure(**figspecs)
if np.min(yVals) <= - np.finfo(float).eps:
plt.plot(xVals, yVals, label = label)
else:
plt.plot(xVals, yVals, label = label)
if np.log10(np.max(yVals) / np.min(yVals)) > 1.:
ax = p.get_axes()[0]
ax.set_yscale('log')
plt.legend()
plt.grid()
if save is not None:
prefix = "{}_{}_vs_{}_{}".format(save, y, x, constr)
plt.savefig(getNewFilename(prefix, saveFormat),
format = saveFormat, dpi = saveDPI)
plt.show()
plt.close()
def plotCompare(self, filenames:List[str], xs:List[str], ys:List[str],
zs:List[str], onePlot : bool = False, save : str = None,
ylims : dict = None, saveFormat : str = "eps",
saveDPI : int = 100, labels : List[str] = None,
**figspecs):
"""
Perform plots from data in filename1 and filename2.
Args:
filenames: CSV filenames.
xs: Values to put on x axes.
ys: Values to put on y axes.
zs: Meta-values for constraints.
onePlot(optional): Whether to create a single figure per x.
Defaults to False.
save(optional): Where to save plot(s). Defaults to None, i.e. no
saving.
clip(optional): Custom y axis limits. If None, automatic values are
kept. Defaults to None.
saveFormat(optional): Format for saved plot(s). Defaults to "eps".
saveDPI(optional): DPI for saved plot(s). Defaults to 100.
labels: Label for each dataset.
figspecs(optional key args): Optional arguments for matplotlib
figure creation.
"""
nfiles = len(filenames)
if save is not None:
save = save.strip()
if labels is None:
labels = ["{}".format(j + 1) for j in range(nfiles)]
zsVals = self.read(filenames[0], outputs = zs)
zs = list(zsVals.keys())
zss = None
for key in zs:
vals = np.unique(zsVals[key])
if zss is None:
zss = copy(vals)
else:
zss = list(itertools.product(zss, vals))
lzs = len(zs)
for z in zss:
if lzs <= 1:
constr = {zs[0] : z}
else:
constr = {zs[j] : z[j] for j in range(len(zs))}
data = [None] * nfiles
for j in range(nfiles):
data[j] = self.read(filenames[j], restrictions = constr,
outputs = xs + ys)
if onePlot:
for x in xs:
xVals = [None] * nfiles
for j in range(nfiles):
try:
xVals[j] = data[j][x]
except:
pass
p = plt.figure(**figspecs)
logScale = False
for y in ys:
for j in range(nfiles):
try:
yVals = data[j][y]
except:
pass
l = '{} vs {} for {}, {}'.format(y, x, constr,
labels[j])
if np.min(yVals) <= - np.finfo(float).eps:
plt.plot(xVals[j], yVals, label = l)
else:
plt.plot(xVals[j], yVals, label = l)
if np.log10(np.max(yVals)/np.min(yVals)) > 1.:
logScale = True
if logScale:
ax = p.get_axes()[0]
ax.set_yscale('log')
if ylims is not None:
plt.ylim(**ylims)
plt.legend()
plt.grid()
if save is not None:
prefix = "{}_{}_vs_{}_{}".format(save, ys, x, constr)
plt.savefig(getNewFilename(prefix, saveFormat),
format = saveFormat, dpi = saveDPI)
plt.show()
plt.close()
else:
for x, y in itertools.product(xs, ys):
p = plt.figure(**figspecs)
logScale = False
for j in range(nfiles):
xVals, yVals = data[j][x], data[j][y]
l = '{} vs {} for {}, {}'.format(y, x, constr,
labels[j])
if np.min(yVals) <= - np.finfo(float).eps:
plt.plot(xVals, yVals, label = l)
else:
plt.plot(xVals, yVals, label = l)
if np.log10(np.max(yVals)/np.min(yVals)) > 1.:
logScale = True
if logScale:
ax = p.get_axes()[0]
ax.set_yscale('log')
if ylims is not None:
plt.ylim(**ylims)
plt.legend()
plt.grid()
if save is not None:
prefix = "{}_{}_vs_{}_{}".format(save, y, x, constr)
plt.savefig(getNewFilename(prefix, saveFormat),
format = saveFormat, dpi = saveDPI)
plt.show()
plt.close()