diff --git a/VERSION b/VERSION
index 400122e..dc39e58 100644
--- a/VERSION
+++ b/VERSION
@@ -1 +1 @@
-1.5
\ No newline at end of file
+1.6
\ No newline at end of file
diff --git a/examples/2d/base/cookie_single.py b/examples/2d/base/cookie_single.py
new file mode 100644
index 0000000..f153e01
--- /dev/null
+++ b/examples/2d/base/cookie_single.py
@@ -0,0 +1,29 @@
+import numpy as np
+import fenics as fen
+from rrompy.hfengines.linear_problem.bidimensional import \
+ CookieEngineSingle as CES
+
+verb = 100
+
+kappa = 15. ** .5
+theta = - np.pi / 6.
+n = 30
+R = 1.
+L = np.pi
+nX = 3
+nY = 2
+
+mu0 = [25. ** .5, 1.]
+mutar = [25. ** .5, 1.5]
+
+solver = CES(kappa = kappa, theta = theta, n = n, R = R, L = L, nX = nX,
+ nY = nY, mu0 = mu0, verbosity = verb)
+uh = solver.solve(mutar)[0]
+solver.plotmesh(figsize = (7.5, 4.5))
+fen.plot(fen.project(solver.cookieIn,
+ fen.FunctionSpace(solver.V.mesh(), "DG", 0)))
+print(solver.norm(uh))
+solver.plot(uh)
+solver.outParaviewTimeDomain(uh, mutar[0], filename = 'out', folder = True,
+ forceNewFile = False)
+
diff --git a/examples/2d/base/fracture.py b/examples/2d/base/fracture.py
new file mode 100644
index 0000000..74d18b9
--- /dev/null
+++ b/examples/2d/base/fracture.py
@@ -0,0 +1,47 @@
+import numpy as np
+import ufl
+import fenics as fen
+from rrompy.hfengines.linear_problem.bidimensional import \
+ MembraneFractureEngine as MFE
+from rrompy.solver.fenics import affine_warping
+
+verb = 100
+
+mu0 = [45. ** .5, .6]
+
+H = 1.
+L = .75
+delta = .05
+n = 50
+
+solver = MFE(mu0 = mu0, H = H, L = L, delta = delta,
+ n = n, verbosity = verb)
+u0 = solver.liftDirichletData(mu0)
+uh = solver.solve(mu0)[0]
+#solver.plotmesh(figsize = (7.5, 4.5))
+#solver.plot(u0, what = 'REAL', figsize = (8, 5))
+print(solver.norm(uh))
+#solver.plot(uh, what = 'REAL', figsize = (8, 5))
+#solver.plot(solver.residual(uh, mu0)[0], name = 'res',
+# what = 'REAL', figsize = (8, 5))
+#solver.outParaviewTimeDomain(uh, mu0[0], filename = 'out', folder = True,
+# forceNewFile = False)
+
+L = mu0[1]
+y = fen.SpatialCoordinate(solver.V.mesh())[1]
+warp1, warpI1 = affine_warping(solver.V.mesh(),
+ np.array([[1, 0], [0, 2. * L]]))
+warp2, warpI2 = affine_warping(solver.V.mesh(),
+ np.array([[1, 0], [0, 2. - 2. * L]]))
+
+warp = ufl.conditional(ufl.ge(y, 0.), warp1, warp2)
+warpI = ufl.conditional(ufl.ge(y, 0.), warpI1, warpI2)
+
+#solver.plotmesh([warp, warpI], figsize = (7.5, 4.5))
+#solver.plot(u0, [warp, warpI], what = 'REAL', figsize = (8, 5))
+solver.plot(uh, [warp, warpI], what = 'REAL', figsize = (8, 5))
+#solver.plot(solver.residual(uh, mu0)[0], [warp, warpI], name = 'res',
+# what = 'REAL', figsize = (8, 5))
+#solver.outParaviewTimeDomain(uh, mu0[0], [warp, warpI],
+# filename = 'outW', folder = True,
+# forceNewFile = False)
diff --git a/examples/2d/base/fracture_nodomain.py b/examples/2d/base/fracture_nodomain.py
new file mode 100644
index 0000000..7be9125
--- /dev/null
+++ b/examples/2d/base/fracture_nodomain.py
@@ -0,0 +1,47 @@
+import numpy as np
+import ufl
+import fenics as fen
+from rrompy.hfengines.linear_problem import MembraneFractureEngineNoDomain \
+ as MFEND
+from rrompy.solver.fenics import affine_warping
+
+verb = 100
+
+mu0Aug = [45. ** .5, .6]
+mu0Aug = [45. ** .5, .1]
+mu0 = mu0Aug[0]
+H = 1.
+L = .75
+delta = .05
+n = 50
+
+solver = MFEND(mu0 = mu0Aug, H = H, L = L, delta = delta,
+ n = n, verbosity = verb)
+u0 = solver.liftDirichletData(mu0)
+uh = solver.solve(mu0)[0]
+solver.plotmesh(figsize = (7.5, 4.5))
+solver.plot(u0, what = 'REAL', figsize = (8, 5))
+print(solver.norm(uh))
+solver.plot(uh, what = 'REAL', figsize = (8, 5))
+solver.plot(solver.residual(uh, mu0)[0], name = 'res',
+ what = 'REAL', figsize = (8, 5))
+solver.outParaviewTimeDomain(uh, mu0, filename = 'outND', folder = True)
+
+##
+L = mu0Aug[1]
+y = fen.SpatialCoordinate(solver.V.mesh())[1]
+warp1, warpI1 = affine_warping(solver.V.mesh(),
+ np.array([[1, 0], [0, 2. * L]]))
+warp2, warpI2 = affine_warping(solver.V.mesh(),
+ np.array([[1, 0], [0, 2. - 2. * L]]))
+
+warp = ufl.conditional(ufl.ge(y, 0.), warp1, warp2)
+warpI = ufl.conditional(ufl.ge(y, 0.), warpI1, warpI2)
+
+solver.plotmesh([warp, warpI], figsize = (7.5, 4.5))
+solver.plot(u0, [warp, warpI], what = 'REAL', figsize = (8, 5))
+solver.plot(uh, [warp, warpI], what = 'REAL', figsize = (8, 5))
+solver.plot(solver.residual(uh, mu0)[0], [warp, warpI], name = 'res',
+ what = 'REAL', figsize = (8, 5))
+solver.outParaviewTimeDomain(uh, mu0, [warp, warpI],
+ filename = 'outNDW', folder = True)
diff --git a/examples/2d/base/synthetic_solve.py b/examples/2d/base/synthetic_solve.py
new file mode 100644
index 0000000..6e58ce7
--- /dev/null
+++ b/examples/2d/base/synthetic_solve.py
@@ -0,0 +1,26 @@
+import numpy as np
+import fenics as fen
+from rrompy.hfengines.linear_problem.bidimensional import \
+ SyntheticBivariateEngine as SBE
+
+verb = 100
+
+kappa = 15. ** .5
+theta = - np.pi / 6.
+n = 30
+L = np.pi
+
+mu0 = [15. ** .5, 20. ** .5]
+mutar = [15. ** .5, 20. ** .5]
+
+solver = SBE(kappa = kappa, theta = theta, n = n, L = L,
+ mu0 = mu0, verbosity = verb)
+uh = solver.solve(mutar)[0]
+solver.plotmesh(figsize = (7.5, 4.5))
+fen.plot(fen.project(solver._above,
+ fen.FunctionSpace(solver.V.mesh(), "DG", 0)))
+print(solver.norm(uh))
+solver.plot(uh)
+#solver.outParaviewTimeDomain(uh, mutar[0], filename = 'out', folder = True,
+# forceNewFile = False)
+
diff --git a/examples/2d/pod/square_pod.py b/examples/2d/pod/cookie_single_pod.py
similarity index 56%
copy from examples/2d/pod/square_pod.py
copy to examples/2d/pod/cookie_single_pod.py
index 56de10d..d6d190b 100644
--- a/examples/2d/pod/square_pod.py
+++ b/examples/2d/pod/cookie_single_pod.py
@@ -1,144 +1,153 @@
import numpy as np
from rrompy.hfengines.linear_problem.bidimensional import \
- HelmholtzSquareDomainProblemEngine as HSDPE
+ CookieEngineSingle as CES
from rrompy.reduction_methods.centered import RationalPade as RP
from rrompy.reduction_methods.distributed import RationalInterpolant as RI
from rrompy.reduction_methods.centered import RBCentered as RBC
from rrompy.reduction_methods.distributed import RBDistributed as RBD
from rrompy.parameter.parameter_sampling import (QuadratureSampler as QS,
QuadratureSamplerTotal as QST,
ManualSampler as MS,
RandomSampler as RS)
verb = 5
size = 1
-show_sample = False
-ignore_forcing = True
-ignore_forcing = False
+show_sample = True
+show_norm = True
clip = -1
#clip = .4
#clip = .6
-MN = 6
+Delta = -10
+MN = 15
R = (MN + 2) * (MN + 1) // 2
S = [int(np.ceil(R ** .5))] * 2
+PODTol = 1e-6
samples = "centered"
samples = "centered_fake"
samples = "distributed"
algo = "rational"
#algo = "RB"
sampling = "quadrature"
sampling = "quadrature_total"
-#sampling = "random"
-
-if size == 1: # small
- mu0 = [4 ** .5, 1.5 ** .5]
- mutar = [5 ** .5, 1.75 ** .5]
- murange = [[2 ** .5, 1. ** .5], [6 ** .5, 2. ** .5]]
-elif size == 2: # medium
- mu0 = [4 ** .5, 1.75 ** .5]
- mutar = [5 ** .5, 1.25 ** .5]
- murange = [[1 ** .5, 1. ** .5], [7 ** .5, 2.5 ** .5]]
-elif size == 3: # fat
- mu0 = [6 ** .5, 4 ** .5]
- mutar = [2 ** .5, 2.5 ** .5]
- murange = [[0 ** .5, 2 ** .5], [12 ** .5, 6 ** .5]]
-elif size == 4: # crowded
- mu0 = [10 ** .5, 2 ** .5]
- mutar = [9 ** .5, 2.25 ** .5]
- murange = [[8 ** .5, 1.5 ** .5], [12 ** .5, 2.5 ** .5]]
-elif size == 5: # tall
- mu0 = [11 ** .5, 2.25 ** .5]
- mutar = [10.5 ** .5, 2.5 ** .5]
- murange = [[10 ** .5, 1.5 ** .5], [12 ** .5, 3 ** .5]]
-elif size == 6: # taller
- mu0 = [11 ** .5, 2.25 ** .5]
- mutar = [10.5 ** .5, 2.5 ** .5]
- murange = [[10 ** .5, 1.25 ** .5], [12 ** .5, 3.25 ** .5]]
-elif size == 7: # low
- mu0 = [7 ** .5, .75 ** .5]
- mutar = [6.5 ** .5, .9 ** .5]
- murange = [[6 ** .5, .5 ** .5], [8 ** .5, 1. ** .5]]
-
-aEff = 1.25
+sampling = "random"
+
+if samples == "distributed":
+ radial = 0
+# radial = "gaussian"
+# radial = "thinplate"
+# radial = "multiquadric"
+ rW0 = 1.
+ radialWeight = [rW0] * 2
+
+assert Delta <= 0
+
+if size == 1: # below
+ mu0 = [20 ** .5, 1. ** .5]
+ mutar = [20.5 ** .5, 1.05 ** .5]
+ murange = [[18.5 ** .5, .85 ** .5], [21.5 ** .5, 1.15 ** .5]]
+
+aEff = 1.#25
bEff = 1. - aEff
murangeEff = [[(aEff*murange[0][0]**2. + bEff*murange[1][0]**2.) ** .5,
- (aEff*murange[0][1]**2. + bEff*murange[1][1]**2.) ** .5],
+ aEff*murange[0][1] + bEff*murange[1][1]],
[(aEff*murange[1][0]**2. + bEff*murange[0][0]**2.) ** .5,
- (aEff*murange[1][1]**2. + bEff*murange[0][1]**2.) ** .5]]
+ aEff*murange[1][1] + bEff*murange[0][1]]]
-solver = HSDPE(kappa = 2.5, theta = np.pi / 3, mu0 = mu0, n = 20,
- verbosity = verb)
-if ignore_forcing: solver.nbs = 1
+kappa = 20. ** .5
+theta = - np.pi / 6.
+n = 30
+Rad = 1.
+L = np.pi
+nX = 2
+nY = 1
-rescaling = [lambda x: np.power(x, 2.), lambda x: x]
-rescalingInv = [lambda x: np.power(x, .5), lambda x: x]
+solver = CES(kappa = kappa, theta = theta, n = n, R = Rad, L = L, nX = nX,
+ nY = nY, mu0 = mu0, verbosity = verb)
+
+rescaling = [lambda x: np.power(x, 2.)] * 2
+rescalingInv = [lambda x: np.power(x, .5)] * 2
if algo == "rational":
- params = {'N':MN, 'M':MN, 'S':S, 'POD':True}
+ params = {'N':MN, 'M':MN + Delta, 'S':S, 'POD':True}
if samples == "distributed":
params['polybasis'] = "CHEBYSHEV"
# params['polybasis'] = "LEGENDRE"
# params['polybasis'] = "MONOMIAL"
params['E'] = MN
+ params['radialBasis'] = radial
+ params['radialBasisWeights'] = radialWeight
method = RI
elif samples == "centered_fake":
params['polybasis'] = "MONOMIAL"
params['S'] = R
method = RI
else:
params['S'] = R
method = RP
else: #if algo == "RB":
- params = {'R':R, 'S':S, 'POD':True}
+ params = {'R':(MN + 2 + Delta) * (MN + 1 + Delta) // 2, 'S':S,
+ 'POD':True, 'PODTolerance':PODTol}
if samples == "distributed":
method = RBD
elif samples == "centered_fake":
params['S'] = R
method = RBD
else:
params['S'] = R
method = RBC
if samples == "distributed":
if sampling == "quadrature":
params['sampler'] = QS(murange, "CHEBYSHEV", scaling = rescaling,
scalingInv = rescalingInv)
# params['sampler'] = QS(murange, "GAUSSLEGENDRE", scaling = rescaling,
# scalingInv = rescalingInv)
# params['sampler'] = QS(murange, "UNIFORM", scaling = rescaling,
# scalingInv = rescalingInv)
params['S'] = [max(j, MN + 1) for j in params['S']]
elif sampling == "quadrature_total":
params['sampler'] = QST(murange, "CHEBYSHEV", scaling = rescaling,
scalingInv = rescalingInv)
params['S'] = R
else: # if sampling == "random":
params['sampler'] = RS(murange, "HALTON", scaling = rescaling,
scalingInv = rescalingInv)
params['S'] = R
elif samples == "centered_fake":
params['sampler'] = MS(murange, points = [mu0], scaling = rescaling,
scalingInv = rescalingInv)
-approx = method(solver, mu0 = mu0, approxParameters = params,
- verbosity = verb)
+approx = method(solver, mu0 = mu0, approxParameters = params, verbosity = verb)
+if samples == "distributed": approx.samplingEngine.allowRepeatedSamples = False
approx.setupApprox()
if show_sample:
- approx.plotApprox(mutar, name = 'u_app')
- approx.plotHF(mutar, name = 'u_HF')
- approx.plotErr(mutar, name = 'err')
- approx.plotRes(mutar, name = 'res')
- appErr, solNorm = approx.normErr(mutar), approx.normHF(mutar)
- resNorm, RHSNorm = approx.normRes(mutar), approx.normRHS(mutar)
+ L = mutar[1]
+ approx.plotApprox(mutar, name = 'u_app', homogeneized = False,
+ what = "REAL")
+ approx.plotHF(mutar, name = 'u_HF', homogeneized = False, what = "REAL")
+ approx.plotErr(mutar, name = 'err', homogeneized = False, what = "REAL")
+# approx.plotRes(mutar, name = 'res', homogeneized = False, what = "REAL")
+ appErr = approx.normErr(mutar)
+ solNorm = approx.normHF(mutar)
+ resNorm = approx.normRes(mutar)
+ RHSNorm = approx.normRHS(mutar)
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 algo == "rational":
- from plot_zero_set import plotZeroSet
- plotZeroSet(murange, murangeEff, approx, mu0,
- 200, [2., 1.], clip = clip)
\ No newline at end of file
+if algo == "rational" and approx.N > 0:
+ from plot_zero_set import plotZeroSet2
+ muZeroVals, Qvals = plotZeroSet2(murange, murangeEff, approx, mu0,
+ 200, [2., 2.], clip = clip)
+
+if show_norm:
+ solver._solveBatchSize = 100
+ from plot_inf_set import plotInfSet2
+ muInfVals, normEx, normApp, normRes, normErr, beta = plotInfSet2(
+ murange, murangeEff, approx, mu0, 25,
+ [2., 2.], clip = clip, relative = False)
+
diff --git a/examples/2d/pod/fracture_pod.py b/examples/2d/pod/fracture_pod.py
new file mode 100644
index 0000000..1aff9fa
--- /dev/null
+++ b/examples/2d/pod/fracture_pod.py
@@ -0,0 +1,194 @@
+import numpy as np
+from rrompy.hfengines.linear_problem.bidimensional import \
+ MembraneFractureEngine as MFE
+from rrompy.reduction_methods.centered import RationalPade as RP
+from rrompy.reduction_methods.distributed import RationalInterpolant as RI
+from rrompy.reduction_methods.centered import RBCentered as RBC
+from rrompy.reduction_methods.distributed import RBDistributed as RBD
+from rrompy.parameter.parameter_sampling import (QuadratureSampler as QS,
+ QuadratureSamplerTotal as QST,
+ ManualSampler as MS,
+ RandomSampler as RS)
+
+verb = 5
+size = 4
+show_sample = False
+show_norm = True
+clip = -1
+#clip = .4
+#clip = .6
+homogeneize = False
+#homogeneize = True
+
+Delta = 0
+MN = 20
+R = (MN + 2) * (MN + 1) // 2
+S = [int(np.ceil(R ** .5))] * 2
+PODTol = 1e-8
+
+samples = "centered"
+samples = "centered_fake"
+samples = "distributed"
+algo = "rational"
+#algo = "RB"
+sampling = "quadrature"
+sampling = "quadrature_total"
+#sampling = "random"
+
+if samples == "distributed":
+ radial = 0
+# radial = "gaussian"
+# radial = "thinplate"
+# radial = "multiquadric"
+ rW0 = 5.
+ radialWeight = [rW0] * 2
+
+assert Delta <= 0
+
+if size == 1: # below
+ mu0 = [40 ** .5, .4]
+ mutar = [45 ** .5, .4]
+ murange = [[30 ** .5, .3], [50 ** .5, .5]]
+elif size == 2: # top
+ mu0 = [40 ** .5, .6]
+ mutar = [45 ** .5, .6]
+ murange = [[30 ** .5, .5], [50 ** .5, .7]]
+elif size == 3: # interesting
+ mu0 = [40 ** .5, .5]
+ mutar = [45 ** .5, .5]
+ murange = [[30 ** .5, .3], [50 ** .5, .7]]
+elif size == 4: # wide_low
+ mu0 = [40 ** .5, .2]
+ mutar = [45 ** .5, .2]
+ murange = [[10 ** .5, .1], [70 ** .5, .3]]
+elif size == 5: # wide_hi
+ mu0 = [40 ** .5, .8]
+ mutar = [45 ** .5, .8]
+ murange = [[10 ** .5, .7], [70 ** .5, .9]]
+elif size == 6: # top_zoom
+ mu0 = [50 ** .5, .8]
+ mutar = [55 ** .5, .8]
+ murange = [[40 ** .5, .7], [60 ** .5, .9]]
+elif size == 7: # huge
+ mu0 = [50 ** .5, .5]
+ mutar = [55 ** .5, .5]
+ murange = [[10 ** .5, .2], [90 ** .5, .8]]
+elif size == 100: # tiny
+ mu0 = [32.5 ** .5, .5]
+ mutar = [34 ** .5, .5]
+ murange = [[30 ** .5, .3], [35 ** .5, .7]]
+
+aEff = 1.#25
+bEff = 1. - aEff
+murangeEff = [[(aEff*murange[0][0]**2. + bEff*murange[1][0]**2.) ** .5,
+ aEff*murange[0][1] + bEff*murange[1][1]],
+ [(aEff*murange[1][0]**2. + bEff*murange[0][0]**2.) ** .5,
+ aEff*murange[1][1] + bEff*murange[0][1]]]
+
+H = 1.
+L = .75
+delta = .05
+n = 20
+
+solver = MFE(mu0 = mu0, H = H, L = L, delta = delta, n = n, verbosity = verb)
+
+rescaling = [lambda x: np.power(x, 2.), lambda x: x]
+rescalingInv = [lambda x: np.power(x, .5), lambda x: x]
+if algo == "rational":
+ params = {'N':MN, 'M':MN + Delta, 'S':S, 'POD':True}
+ if samples == "distributed":
+ params['polybasis'] = "CHEBYSHEV"
+# params['polybasis'] = "LEGENDRE"
+# params['polybasis'] = "MONOMIAL"
+ params['E'] = MN
+ params['radialBasis'] = radial
+ params['radialBasisWeights'] = radialWeight
+ method = RI
+ elif samples == "centered_fake":
+ params['polybasis'] = "MONOMIAL"
+ params['S'] = R
+ method = RI
+ else:
+ params['S'] = R
+ method = RP
+else: #if algo == "RB":
+ params = {'R':(MN + 2 + Delta) * (MN + 1 + Delta) // 2, 'S':S,
+ 'POD':True, 'PODTolerance':PODTol}
+ if samples == "distributed":
+ method = RBD
+ elif samples == "centered_fake":
+ params['S'] = R
+ method = RBD
+ else:
+ params['S'] = R
+ method = RBC
+
+if samples == "distributed":
+ if sampling == "quadrature":
+ params['sampler'] = QS(murange, "CHEBYSHEV", scaling = rescaling,
+ scalingInv = rescalingInv)
+# params['sampler'] = QS(murange, "GAUSSLEGENDRE", scaling = rescaling,
+# scalingInv = rescalingInv)
+# params['sampler'] = QS(murange, "UNIFORM", scaling = rescaling,
+# scalingInv = rescalingInv)
+ params['S'] = [max(j, MN + 1) for j in params['S']]
+ elif sampling == "quadrature_total":
+ params['sampler'] = QST(murange, "CHEBYSHEV", scaling = rescaling,
+ scalingInv = rescalingInv)
+ params['S'] = R
+ else: # if sampling == "random":
+ params['sampler'] = RS(murange, "HALTON", scaling = rescaling,
+ scalingInv = rescalingInv)
+ params['S'] = R
+
+elif samples == "centered_fake":
+ params['sampler'] = MS(murange, points = [mu0], scaling = rescaling,
+ scalingInv = rescalingInv)
+
+approx = method(solver, mu0 = mu0, approxParameters = params,
+ verbosity = verb, homogeneized = homogeneize)
+if samples == "distributed": approx.samplingEngine.allowRepeatedSamples = False
+
+approx.setupApprox()
+if show_sample:
+ import ufl
+ import fenics as fen
+ from rrompy.solver.fenics import affine_warping
+ L = mutar[1]
+ y = fen.SpatialCoordinate(solver.V.mesh())[1]
+ warp1, warpI1 = affine_warping(solver.V.mesh(),
+ np.array([[1, 0], [0, 2. * L]]))
+ warp2, warpI2 = affine_warping(solver.V.mesh(),
+ np.array([[1, 0], [0, 2. - 2. * L]]))
+ warp = ufl.conditional(ufl.ge(y, 0.), warp1, warp2)
+ warpI = ufl.conditional(ufl.ge(y, 0.), warpI1, warpI2)
+
+ approx.plotApprox(mutar, [warp, warpI], name = 'u_app',
+ homogeneized = False, what = "REAL")
+ approx.plotHF(mutar, [warp, warpI], name = 'u_HF',
+ homogeneized = False, what = "REAL")
+ approx.plotErr(mutar, [warp, warpI], name = 'err',
+ homogeneized = False, what = "REAL")
+# approx.plotRes(mutar, [warp, warpI], name = 'res',
+# homogeneized = False, what = "REAL")
+ appErr = approx.normErr(mutar, homogeneized = homogeneize)
+ solNorm = approx.normHF(mutar, homogeneized = homogeneize)
+ resNorm = approx.normRes(mutar, homogeneized = homogeneize)
+ RHSNorm = approx.normRHS(mutar, homogeneized = homogeneize)
+ 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 algo == "rational" and approx.N > 0:
+ from plot_zero_set import plotZeroSet2
+ muZeroVals, Qvals = plotZeroSet2(murange, murangeEff, approx, mu0,
+ 50, [2., 1.], clip = clip)
+
+if show_norm:
+ solver._solveBatchSize = 100
+ from plot_inf_set import plotInfSet2
+ muInfVals, normEx, normApp, normRes, normErr, beta = plotInfSet2(
+ murange, murangeEff, approx, mu0, 50,
+ [2., 1.], clip = clip, relative = False,
+ normalizeDen = True)
diff --git a/examples/2d/pod/fracture_pod_nodomain.py b/examples/2d/pod/fracture_pod_nodomain.py
new file mode 100644
index 0000000..b9211f4
--- /dev/null
+++ b/examples/2d/pod/fracture_pod_nodomain.py
@@ -0,0 +1,190 @@
+import numpy as np
+from rrompy.hfengines.linear_problem import MembraneFractureEngineNoDomain \
+ as MFEND
+from rrompy.reduction_methods.centered import RationalPade as RP
+from rrompy.reduction_methods.distributed import RationalInterpolant as RI
+from rrompy.reduction_methods.centered import RBCentered as RBC
+from rrompy.reduction_methods.distributed import RBDistributed as RBD
+from rrompy.parameter.parameter_sampling import (QuadratureSampler as QS,
+ QuadratureSamplerTotal as QST,
+ ManualSampler as MS,
+ RandomSampler as RS)
+
+verb = 5
+size = 1
+show_sample = True
+show_norm = True
+ignore_forcing = True
+ignore_forcing = False
+clip = -1
+#clip = .4
+#clip = .6
+homogeneize = False
+#homogeneize = True
+
+Delta = 0
+MN = 6
+R = MN + 1
+S = R
+
+samples = "centered"
+samples = "centered_fake"
+samples = "distributed"
+algo = "rational"
+#algo = "RB"
+sampling = "quadrature"
+sampling = "quadrature_total"
+sampling = "random"
+
+if samples == "distributed":
+ radial = 0
+# radial = "gaussian"
+# radial = "thinplate"
+# radial = "multiquadric"
+ radialWeight = [2.]
+
+assert Delta <= 0
+
+if size == 1: # below
+ mu0Aug = [40 ** .5, .4]
+ mu0 = mu0Aug[0]
+ mutar = 45 ** .5
+ murange = [[30 ** .5], [50 ** .5]]
+elif size == 2: # top
+ mu0Aug = [40 ** .5, .6]
+ mu0 = mu0Aug[0]
+ mutar = 45 ** .5
+ murange = [[30 ** .5], [50 ** .5]]
+elif size == 3: # interesting
+ mu0Aug = [40 ** .5, .5]
+ mu0 = mu0Aug[0]
+ mutar = 45 ** .5
+ murange = [[30 ** .5], [50 ** .5]]
+elif size == 4: # wide_low
+ mu0Aug = [40 ** .5, .2]
+ mu0 = mu0Aug[0]
+ mutar = 45 ** .5
+ murange = [[10 ** .5], [70 ** .5]]
+elif size == 5: # wide_hi
+ mu0Aug = [40 ** .5, .8]
+ mu0 = mu0Aug[0]
+ mutar = 45 ** .5
+ murange = [[10 ** .5], [70 ** .5]]
+elif size == 6: # top_zoom
+ mu0Aug = [50 ** .5, .8]
+ mu0 = mu0Aug[0]
+ mutar = 55 ** .5
+ murange = [[40 ** .5], [60 ** .5]]
+
+aEff = 1.#25
+bEff = 1. - aEff
+murangeEff = [[(aEff*murange[0][0]**2. + bEff*murange[1][0]**2.) ** .5],
+ [(aEff*murange[1][0]**2. + bEff*murange[0][0]**2.) ** .5]]
+
+H = 1.
+L = .75
+delta = .05
+n = 20
+
+solver = MFEND(mu0 = mu0Aug, H = H, L = L, delta = delta, n = n,
+ verbosity = verb)
+
+rescaling = lambda x: np.power(x, 2.)
+rescalingInv = lambda x: np.power(x, .5)
+if algo == "rational":
+ params = {'N':MN, 'M':MN + Delta, 'S':S, 'POD':True}
+ if samples == "distributed":
+ params['polybasis'] = "CHEBYSHEV"
+# params['polybasis'] = "LEGENDRE"
+# params['polybasis'] = "MONOMIAL"
+ params['E'] = MN
+ params['radialBasis'] = radial
+ params['radialBasisWeights'] = radialWeight
+ method = RI
+ elif samples == "centered_fake":
+ params['polybasis'] = "MONOMIAL"
+ params['S'] = R
+ method = RI
+ else:
+ params['S'] = R
+ method = RP
+else: #if algo == "RB":
+ params = {'R':R, 'S':S, 'POD':True}
+ if samples == "distributed":
+ method = RBD
+ elif samples == "centered_fake":
+ params['S'] = R
+ method = RBD
+ else:
+ params['S'] = R
+ method = RBC
+
+if samples == "distributed":
+ if sampling == "quadrature":
+ params['sampler'] = QS(murange, "CHEBYSHEV", scaling = rescaling,
+ scalingInv = rescalingInv)
+# params['sampler'] = QS(murange, "GAUSSLEGENDRE", scaling = rescaling,
+# scalingInv = rescalingInv)
+# params['sampler'] = QS(murange, "UNIFORM", scaling = rescaling,
+# scalingInv = rescalingInv)
+ params['S'] = [max(j, MN + 1) for j in params['S']]
+ elif sampling == "quadrature_total":
+ params['sampler'] = QST(murange, "CHEBYSHEV", scaling = rescaling,
+ scalingInv = rescalingInv)
+ params['S'] = R
+ else: # if sampling == "random":
+ params['sampler'] = RS(murange, "HALTON", scaling = rescaling,
+ scalingInv = rescalingInv)
+ params['S'] = R
+
+elif samples == "centered_fake":
+ params['sampler'] = MS(murange, points = [mu0], scaling = rescaling,
+ scalingInv = rescalingInv)
+
+approx = method(solver, mu0 = mu0, approxParameters = params,
+ verbosity = verb, homogeneized = homogeneize)
+if samples == "distributed": approx.samplingEngine.allowRepeatedSamples = False
+
+approx.setupApprox()
+if show_sample:
+ import ufl
+ import fenics as fen
+ from rrompy.solver.fenics import affine_warping
+ L = solver.lFrac
+ y = fen.SpatialCoordinate(solver.V.mesh())[1]
+ warp1, warpI1 = affine_warping(solver.V.mesh(),
+ np.array([[1, 0], [0, 2. * L]]))
+ warp2, warpI2 = affine_warping(solver.V.mesh(),
+ np.array([[1, 0], [0, 2. - 2. * L]]))
+ warp = ufl.conditional(ufl.ge(y, 0.), warp1, warp2)
+ warpI = ufl.conditional(ufl.ge(y, 0.), warpI1, warpI2)
+
+ approx.plotApprox(mutar, [warp, warpI], name = 'u_app',
+ homogeneized = False, what = "REAL")
+ approx.plotHF(mutar, [warp, warpI], name = 'u_HF',
+ homogeneized = False, what = "REAL")
+ approx.plotErr(mutar, [warp, warpI], name = 'err',
+ homogeneized = False, what = "REAL")
+# approx.plotRes(mutar, [warp, warpI], name = 'res',
+# homogeneized = False, what = "REAL")
+ appErr = approx.normErr(mutar, homogeneized = homogeneize)
+ solNorm = approx.normHF(mutar, homogeneized = homogeneize)
+ resNorm = approx.normRes(mutar, homogeneized = homogeneize)
+ RHSNorm = approx.normRHS(mutar, homogeneized = homogeneize)
+ 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 algo == "rational":
+ from plot_zero_set import plotZeroSet1
+ muZeroVals, Qvals = plotZeroSet1(murange, murangeEff, approx, mu0,
+ 1000, 2.)
+
+if show_norm:
+ solver._solveBatchSize = 10
+ from plot_inf_set import plotInfSet1
+ muInfVals, normEx, normApp, normRes, normErr, beta = plotInfSet1(
+ murange, murangeEff, approx, mu0,
+ 200, 2., relative = False,
+ normalizeDen = True)
diff --git a/examples/2d/pod/plot_inf_set.py b/examples/2d/pod/plot_inf_set.py
new file mode 100644
index 0000000..8c01fc2
--- /dev/null
+++ b/examples/2d/pod/plot_inf_set.py
@@ -0,0 +1,323 @@
+import warnings
+import numpy as np
+from matplotlib import pyplot as plt
+
+def plotInfSet1FromData(mus, Z, T, R, E, beta, murange, approx, mu0,
+ exp = 2., normalizeDen = False):
+ if hasattr(approx, "mus"):
+ mu2x = approx.mus(0) ** exp
+ else:
+ mu2x = mu0[0] ** exp
+ murangeExp = [[murange[0][0] ** exp], [murange[1][0] ** exp]]
+ mu1 = np.real(np.power(mus, exp))
+ eta = R / beta / E
+ ZTmin, ZTmax = min(np.min(Z), np.min(T)), max(np.max(Z), np.max(T))
+ Rmin, Rmax = np.min(R), np.max(R)
+ Emin, Emax = np.min(E), np.max(E)
+ betamin, betamax = np.min(beta), np.max(beta)
+ etamin, etamax = np.min(eta), np.max(eta)
+ plt.figure(figsize = (15, 7))
+ plt.jet()
+ plt.semilogy(mu1, Z)
+ plt.semilogy(mu1, T, '--')
+ for l_ in approx.trainedModel.getPoles():
+ plt.plot([np.real(l_ ** exp)] * 2, [ZTmin, ZTmax], 'b:')
+ plt.plot(mu2x, [ZTmin] * len(mu2x), 'kx')
+ plt.plot([murangeExp[0][0]] * 2, [ZTmin, ZTmax], 'm:')
+ plt.plot([murangeExp[1][0]] * 2, [ZTmin, ZTmax], 'm:')
+ plt.xlim(mu1[0], mu1[-1])
+ plt.title("|u(mu)|, |u_app(mu)|")
+ plt.grid()
+ plt.show()
+
+ plt.figure(figsize = (15, 7))
+ plt.jet()
+ plt.semilogy(mu1, R)
+ for l_ in approx.trainedModel.getPoles():
+ plt.plot([np.real(l_ ** exp)] * 2, [Rmin, Rmax], 'b:')
+ plt.plot(mu2x, [Rmax] * len(mu2x), 'kx')
+ plt.plot([murangeExp[0][0]] * 2, [Rmin, Rmax], 'm:')
+ plt.plot([murangeExp[1][0]] * 2, [Rmin, Rmax], 'm:')
+ plt.xlim(mu1[0], mu1[-1])
+ if normalizeDen:
+ plt.title("|Q(mu)res(mu)|")
+ else:
+ plt.title("|res(mu)|")
+ plt.grid()
+ plt.show()
+
+ plt.figure(figsize = (15, 7))
+ plt.jet()
+ plt.semilogy(mu1, E)
+ for l_ in approx.trainedModel.getPoles():
+ plt.plot([np.real(l_ ** exp)] * 2, [Emin, Emax], 'b:')
+ plt.plot(mu2x, [Emax] * len(mu2x), 'kx')
+ plt.plot([murangeExp[0][0]] * 2, [Emin, Emax], 'm:')
+ plt.plot([murangeExp[1][0]] * 2, [Emin, Emax], 'm:')
+ plt.xlim(mu1[0], mu1[-1])
+ if normalizeDen:
+ plt.title("|Q(mu)err(mu)|")
+ else:
+ plt.title("|err(mu)|")
+ plt.grid()
+ plt.show()
+
+ plt.figure(figsize = (15, 7))
+ plt.jet()
+ plt.semilogy(mu1, beta)
+ for l_ in approx.trainedModel.getPoles():
+ plt.plot([np.real(l_ ** exp)] * 2, [betamin, betamax], 'b:')
+ plt.plot(mu2x, [betamax] * len(mu2x), 'kx')
+ plt.plot([murangeExp[0][0]] * 2, [betamin, betamax], 'm:')
+ plt.plot([murangeExp[1][0]] * 2, [betamin, betamax], 'm:')
+ plt.xlim(mu1[0], mu1[-1])
+ plt.title("beta(mu)")
+ plt.grid()
+ plt.show()
+
+ plt.figure(figsize = (15, 7))
+ plt.jet()
+ plt.semilogy(mu1, R / beta)
+ plt.semilogy(mu1, E, '--')
+ for l_ in approx.trainedModel.getPoles():
+ plt.plot([np.real(l_ ** exp)] * 2, [Emin, Emax], 'b:')
+ plt.plot(mu2x, [Emax] * len(mu2x), 'kx')
+ plt.plot([murangeExp[0][0]] * 2, [Emin, Emax], 'm:')
+ plt.plot([murangeExp[1][0]] * 2, [Emin, Emax], 'm:')
+ plt.xlim(mu1[0], mu1[-1])
+ if normalizeDen:
+ plt.title("|Q(mu)res(mu)/beta(mu)|")
+ else:
+ plt.title("|res(mu)/beta(mu)|")
+ plt.grid()
+ plt.show()
+
+ plt.figure(figsize = (15, 7))
+ plt.jet()
+ plt.semilogy(mu1, eta)
+ for l_ in approx.trainedModel.getPoles():
+ plt.plot([np.real(l_ ** exp)] * 2, [etamin, etamax], 'b:')
+ plt.plot(mu2x, [etamax] * len(mu2x), 'kx')
+ plt.plot([murangeExp[0][0]] * 2, [etamin, etamax], 'm:')
+ plt.plot([murangeExp[1][0]] * 2, [etamin, etamax], 'm:')
+ plt.xlim(mu1[0], mu1[-1])
+ plt.title("eta(mu)")
+ plt.grid()
+ plt.show()
+
+def plotInfSet1(murange, murangeEff, approx, mu0, nSamples = 20, exp = 2.,
+ relative = True, normalizeDen = False):
+ mu1 = np.linspace(murangeEff[0][0] ** exp, murangeEff[1][0] ** exp,
+ nSamples)
+ mus = np.power(mu1, 1. / exp)
+ Z = approx.normHF(mus)
+ T = approx.normApprox(mus)
+ R = approx.normRes(mus)
+ E = approx.normErr(mus)
+ if relative:
+ F = approx.normRHS(mus)
+ R /= F
+ E /= Z
+ if normalizeDen:
+ Qvals = np.abs(approx.trainedModel.getQVal(mus))
+ R *= Qvals
+ E *= Qvals
+ beta = approx.HFEngine.stabilityFactor(approx.getHF(mus), mus)
+ plotInfSet1FromData(mus, Z, T, R, E, beta, murange, approx, mu0,
+ exp, normalizeDen)
+ return mus, Z, T, R, E, beta
+
+def plotInfSet2FromData(mus, Ze, Te, Re, Ee, betae, murange, approx, mu0,
+ exps = [2., 2.], clip = -1, normalizeDen = False):
+ if hasattr(approx, "mus"):
+ mu2x, mu2y = approx.mus(0) ** exps[0], approx.mus(1) ** exps[1]
+ else:
+ mu2x, mu2y = mu0[0] ** exps[0], mu0[1] ** exps[1]
+ murangeExp = [[murange[0][0] ** exps[0], murange[0][1] ** exps[1]],
+ [murange[1][0] ** exps[0], murange[1][1] ** exps[1]]]
+ mu1s = np.unique([m[0] for m in mus])
+ mu2s = np.unique([m[1] for m in mus])
+ mu1 = np.power(mu1s, exps[0])
+ mu2 = np.power(mu2s, exps[1])
+ Mu1, Mu2 = np.meshgrid(np.real(mu1), np.real(mu2))
+
+ Z = np.log10(Ze)
+ T = np.log10(Te)
+ R = np.log10(Re)
+ E = np.log10(Ee)
+ beta = np.log10(betae)
+ ZTmin, ZTmax = min(np.min(Z), np.min(T)), max(np.max(Z), np.max(T))
+ Rmin, Rmax = np.min(R), np.max(R)
+ Emin, Emax = np.min(E), np.max(E)
+ betamin, betamax = np.min(beta), np.max(beta)
+ if clip > 0:
+ ZTmax -= clip * (ZTmax - ZTmin)
+ cmap = plt.cm.bone
+ else:
+ cmap = plt.cm.jet
+ warnings.simplefilter("ignore", category = UserWarning)
+ plt.figure(figsize = (15, 7))
+ plt.jet()
+ p = plt.contourf(Mu1, Mu2, Z, cmap = cmap,
+ levels = np.linspace(ZTmin, ZTmax, 50))
+ if clip > 0:
+ plt.contour(Mu1, Mu2, Z, [ZTmin])
+ plt.plot(mu2x, mu2y, 'kx')
+ plt.plot([murangeExp[0][0]] * 2,
+ [murangeExp[0][1], murangeExp[1][1]], 'm:')
+ plt.plot([murangeExp[0][0], murangeExp[1][0]],
+ [murangeExp[1][1]] * 2, 'm:')
+ plt.plot([murangeExp[1][0]] * 2,
+ [murangeExp[1][1], murangeExp[0][1]], 'm:')
+ plt.plot([murangeExp[1][0], murangeExp[0][0]],
+ [murangeExp[0][1]] * 2, 'm:')
+ plt.colorbar(p)
+ plt.title("log10|u(mu)|")
+ plt.grid()
+ plt.show()
+
+ plt.figure(figsize = (15, 7))
+ plt.jet()
+ p = plt.contourf(Mu1, Mu2, T, cmap = cmap,
+ levels = np.linspace(ZTmin, ZTmax, 50))
+ if clip > 0:
+ plt.contour(Mu1, Mu2, T, [ZTmin])
+ plt.plot(mu2x, mu2y, 'kx')
+ plt.plot([murangeExp[0][0]] * 2,
+ [murangeExp[0][1], murangeExp[1][1]], 'm:')
+ plt.plot([murangeExp[0][0], murangeExp[1][0]],
+ [murangeExp[1][1]] * 2, 'm:')
+ plt.plot([murangeExp[1][0]] * 2,
+ [murangeExp[1][1], murangeExp[0][1]], 'm:')
+ plt.plot([murangeExp[1][0], murangeExp[0][0]],
+ [murangeExp[0][1]] * 2, 'm:')
+ plt.title("log10|u_app(mu)|")
+ plt.colorbar(p)
+ plt.grid()
+ plt.show()
+
+ plt.figure(figsize = (15, 7))
+ plt.jet()
+ p = plt.contourf(Mu1, Mu2, R, levels = np.linspace(Rmin, Rmax, 50))
+ plt.plot(mu2x, mu2y, 'kx')
+ plt.plot([murangeExp[0][0]] * 2,
+ [murangeExp[0][1], murangeExp[1][1]], 'm:')
+ plt.plot([murangeExp[0][0], murangeExp[1][0]],
+ [murangeExp[1][1]] * 2, 'm:')
+ plt.plot([murangeExp[1][0]] * 2,
+ [murangeExp[1][1], murangeExp[0][1]], 'm:')
+ plt.plot([murangeExp[1][0], murangeExp[0][0]],
+ [murangeExp[0][1]] * 2, 'm:')
+ if normalizeDen:
+ plt.title("log10|Q(mu)res(mu)|")
+ else:
+ plt.title("log10|res(mu)|")
+ plt.colorbar(p)
+ plt.grid()
+ plt.show()
+
+ plt.figure(figsize = (15, 7))
+ plt.jet()
+ p = plt.contourf(Mu1, Mu2, E, levels = np.linspace(Emin, Emax, 50))
+ plt.plot(mu2x, mu2y, 'kx')
+ plt.plot([murangeExp[0][0]] * 2,
+ [murangeExp[0][1], murangeExp[1][1]], 'm:')
+ plt.plot([murangeExp[0][0], murangeExp[1][0]],
+ [murangeExp[1][1]] * 2, 'm:')
+ plt.plot([murangeExp[1][0]] * 2,
+ [murangeExp[1][1], murangeExp[0][1]], 'm:')
+ plt.plot([murangeExp[1][0], murangeExp[0][0]],
+ [murangeExp[0][1]] * 2, 'm:')
+ if normalizeDen:
+ plt.title("log10|Q(mu)err(mu)|")
+ else:
+ plt.title("log10|err(mu)|")
+ plt.colorbar(p)
+ plt.grid()
+ plt.show()
+
+ plt.figure(figsize = (15, 7))
+ plt.jet()
+ p = plt.contourf(Mu1, Mu2, beta,
+ levels = np.linspace(betamin, betamax, 50))
+ plt.plot(mu2x, mu2y, 'kx')
+ plt.plot([murangeExp[0][0]] * 2,
+ [murangeExp[0][1], murangeExp[1][1]], 'm:')
+ plt.plot([murangeExp[0][0], murangeExp[1][0]],
+ [murangeExp[1][1]] * 2, 'm:')
+ plt.plot([murangeExp[1][0]] * 2,
+ [murangeExp[1][1], murangeExp[0][1]], 'm:')
+ plt.plot([murangeExp[1][0], murangeExp[0][0]],
+ [murangeExp[0][1]] * 2, 'm:')
+ plt.title("log10(beta(mu))")
+ plt.colorbar(p)
+ plt.grid()
+ plt.show()
+
+ plt.figure(figsize = (15, 7))
+ plt.jet()
+ p = plt.contourf(Mu1, Mu2, R - beta, levels = np.linspace(Emin, Emax, 50))
+ plt.plot(mu2x, mu2y, 'kx')
+ plt.plot([murangeExp[0][0]] * 2,
+ [murangeExp[0][1], murangeExp[1][1]], 'm:')
+ plt.plot([murangeExp[0][0], murangeExp[1][0]],
+ [murangeExp[1][1]] * 2, 'm:')
+ plt.plot([murangeExp[1][0]] * 2,
+ [murangeExp[1][1], murangeExp[0][1]], 'm:')
+ plt.plot([murangeExp[1][0], murangeExp[0][0]],
+ [murangeExp[0][1]] * 2, 'm:')
+ if normalizeDen:
+ plt.title("log10|Q(mu)res(mu)/beta(mu)|")
+ else:
+ plt.title("log10|res(mu)/beta(mu)|")
+ plt.colorbar(p)
+ plt.grid()
+ plt.show()
+
+ plt.figure(figsize = (15, 7))
+ plt.jet()
+ p = plt.contourf(Mu1, Mu2, R - beta - E, 50)
+ plt.plot(mu2x, mu2y, 'kx')
+ plt.plot([murangeExp[0][0]] * 2,
+ [murangeExp[0][1], murangeExp[1][1]], 'm:')
+ plt.plot([murangeExp[0][0], murangeExp[1][0]],
+ [murangeExp[1][1]] * 2, 'm:')
+ plt.plot([murangeExp[1][0]] * 2,
+ [murangeExp[1][1], murangeExp[0][1]], 'm:')
+ plt.plot([murangeExp[1][0], murangeExp[0][0]],
+ [murangeExp[0][1]] * 2, 'm:')
+ plt.title("log10|eta(mu)|")
+ plt.colorbar(p)
+ plt.grid()
+ plt.show()
+
+def plotInfSet2(murange, murangeEff, approx, mu0, nSamples = 200,
+ exps = [2., 2.], clip = -1, relative = True,
+ normalizeDen = False):
+ mu1 = np.linspace(murangeEff[0][0] ** exps[0], murangeEff[1][0] ** exps[0],
+ nSamples)
+ mu2 = np.linspace(murangeEff[0][1] ** exps[1], murangeEff[1][1] ** exps[1],
+ nSamples)
+ mu1s = np.power(mu1, 1. / exps[0])
+ mu2s = np.power(mu2, 1. / exps[1])
+ mus = [(m1, m2) for m2 in mu2s for m1 in mu1s]
+
+ Ze = approx.normHF(mus).reshape((nSamples, nSamples))
+ Te = approx.normApprox(mus).reshape((nSamples, nSamples))
+ Re = approx.normRes(mus).reshape((nSamples, nSamples))
+ Ee = approx.normErr(mus).reshape((nSamples, nSamples))
+ if relative:
+ Fe = approx.normRHS(mus).reshape((nSamples, nSamples))
+ Re /= Fe
+ Ee /= Ze
+ if normalizeDen:
+ Qvals = np.abs(approx.trainedModel.getQVal(mus).reshape(
+ (nSamples, nSamples)))
+ Re *= Qvals
+ Ee *= Qvals
+ betae = approx.HFEngine.stabilityFactor(approx.getHF(mus), mus).reshape(
+ (nSamples, nSamples))
+ plotInfSet2FromData(mus, Ze, Te, Re, Ee, betae, murange,
+ approx, mu0, exps, clip, normalizeDen)
+ return mus, Ze, Te, Re, Ee, betae
+
diff --git a/examples/2d/pod/plot_zero_set.py b/examples/2d/pod/plot_zero_set.py
index 7281164..69b32ed 100644
--- a/examples/2d/pod/plot_zero_set.py
+++ b/examples/2d/pod/plot_zero_set.py
@@ -1,49 +1,80 @@
+import warnings
import numpy as np
from matplotlib import pyplot as plt
-def plotZeroSet(murange, murangeEff, approx, mu0, nSamples = 200,
- exps = [2., 2.], clip = -1):
+def plotZeroSet1(murange, murangeEff, approx, mu0, nSamples = 200, exp = 2.):
+ if hasattr(approx, "mus"):
+ mu2x = approx.mus(0) ** exp
+ else:
+ mu2x = mu0[0] ** exp
+ murangeExp = [[murange[0][0] ** exp], [murange[1][0] ** exp]]
+ mu1 = np.linspace(murangeEff[0][0] ** exp, murangeEff[1][0] ** exp,
+ nSamples)
+ mus = np.power(mu1, 1. / exp)
+ mu1 = np.real(mu1)
+ Z = approx.trainedModel.getQVal(mus)
+ Zabs = np.abs(Z)
+ Zmin, Zmax = np.min(Zabs), np.max(Zabs)
+ plt.figure(figsize = (15, 7))
+ plt.jet()
+ plt.semilogy(mu1, Zabs)
+ for l_ in approx.trainedModel.getPoles():
+ plt.plot([np.real(l_ ** exp)] * 2, [Zmin, Zmax], 'b--')
+ plt.plot(mu2x, [Zmax] * len(mu2x), 'kx')
+ plt.plot([murangeExp[0][0]] * 2, [Zmin, Zmax], 'm:')
+ plt.plot([murangeExp[1][0]] * 2, [Zmin, Zmax], 'm:')
+ plt.xlim(mu1[0], mu1[-1])
+ plt.title("|Q(mu)|")
+ plt.grid()
+ plt.show()
+ return mus, Z
+
+def plotZeroSet2(murange, murangeEff, approx, mu0, nSamples = 200,
+ exps = [2., 2.], clip = -1):
if hasattr(approx, "mus"):
mu2x, mu2y = approx.mus(0) ** exps[0], approx.mus(1) ** exps[1]
else:
mu2x, mu2y = mu0[0] ** exps[0], mu0[1] ** exps[1]
murangeExp = [[murange[0][0] ** exps[0], murange[0][1] ** exps[1]],
[murange[1][0] ** exps[0], murange[1][1] ** exps[1]]]
mu1 = np.linspace(murangeEff[0][0] ** exps[0], murangeEff[1][0] ** exps[0],
nSamples)
mu2 = np.linspace(murangeEff[0][1] ** exps[1], murangeEff[1][1] ** exps[1],
nSamples)
mu1s = np.power(mu1, 1. / exps[0])
mu2s = np.power(mu2, 1. / exps[1])
Mu1, Mu2 = np.meshgrid(np.real(mu1), np.real(mu2))
mus = [(m1, m2) for m2 in mu2s for m1 in mu1s]
Z = approx.trainedModel.getQVal(mus).reshape(Mu1.shape)
- Zabs = np.log(np.abs(Z))
+ Zabs = np.log10(np.abs(Z))
Zabsmin, Zabsmax = np.min(Zabs), np.max(Zabs)
if clip > 0:
Zabsmin += clip * (Zabsmax - Zabsmin)
cmap = plt.cm.bone_r
else:
cmap = plt.cm.jet
+ warnings.simplefilter("ignore", category = UserWarning)
plt.figure(figsize = (15, 7))
plt.jet()
p = plt.contourf(Mu1, Mu2, Zabs, cmap = cmap,
levels = np.linspace(Zabsmin, Zabsmax, 50))
if clip > 0:
plt.contour(Mu1, Mu2, Zabs, [Zabsmin])
plt.contour(Mu1, Mu2, np.real(Z), [0.], linestyles = 'dashed')
plt.contour(Mu1, Mu2, np.imag(Z), [0.], linewidths = 1,
linestyles = 'dotted')
plt.plot(mu2x, mu2y, 'kx')
plt.plot([murangeExp[0][0]] * 2,
[murangeExp[0][1], murangeExp[1][1]], 'm:')
plt.plot([murangeExp[0][0], murangeExp[1][0]],
[murangeExp[1][1]] * 2, 'm:')
plt.plot([murangeExp[1][0]] * 2,
[murangeExp[1][1], murangeExp[0][1]], 'm:')
plt.plot([murangeExp[1][0], murangeExp[0][0]],
[murangeExp[0][1]] * 2, 'm:')
plt.colorbar(p)
+ plt.title("log10|Q(mu)|")
plt.grid()
plt.show()
-
+ return mus, Z
+
diff --git a/examples/2d/pod/square_pod.py b/examples/2d/pod/square_pod.py
index 56de10d..c9768d0 100644
--- a/examples/2d/pod/square_pod.py
+++ b/examples/2d/pod/square_pod.py
@@ -1,144 +1,152 @@
import numpy as np
from rrompy.hfengines.linear_problem.bidimensional import \
HelmholtzSquareDomainProblemEngine as HSDPE
from rrompy.reduction_methods.centered import RationalPade as RP
from rrompy.reduction_methods.distributed import RationalInterpolant as RI
from rrompy.reduction_methods.centered import RBCentered as RBC
from rrompy.reduction_methods.distributed import RBDistributed as RBD
from rrompy.parameter.parameter_sampling import (QuadratureSampler as QS,
QuadratureSamplerTotal as QST,
ManualSampler as MS,
RandomSampler as RS)
verb = 5
size = 1
show_sample = False
+show_norm = True
ignore_forcing = True
ignore_forcing = False
clip = -1
#clip = .4
#clip = .6
MN = 6
R = (MN + 2) * (MN + 1) // 2
S = [int(np.ceil(R ** .5))] * 2
samples = "centered"
samples = "centered_fake"
samples = "distributed"
algo = "rational"
#algo = "RB"
sampling = "quadrature"
sampling = "quadrature_total"
#sampling = "random"
if size == 1: # small
mu0 = [4 ** .5, 1.5 ** .5]
mutar = [5 ** .5, 1.75 ** .5]
murange = [[2 ** .5, 1. ** .5], [6 ** .5, 2. ** .5]]
elif size == 2: # medium
mu0 = [4 ** .5, 1.75 ** .5]
mutar = [5 ** .5, 1.25 ** .5]
murange = [[1 ** .5, 1. ** .5], [7 ** .5, 2.5 ** .5]]
elif size == 3: # fat
mu0 = [6 ** .5, 4 ** .5]
mutar = [2 ** .5, 2.5 ** .5]
murange = [[0 ** .5, 2 ** .5], [12 ** .5, 6 ** .5]]
elif size == 4: # crowded
mu0 = [10 ** .5, 2 ** .5]
mutar = [9 ** .5, 2.25 ** .5]
murange = [[8 ** .5, 1.5 ** .5], [12 ** .5, 2.5 ** .5]]
elif size == 5: # tall
mu0 = [11 ** .5, 2.25 ** .5]
mutar = [10.5 ** .5, 2.5 ** .5]
murange = [[10 ** .5, 1.5 ** .5], [12 ** .5, 3 ** .5]]
elif size == 6: # taller
mu0 = [11 ** .5, 2.25 ** .5]
mutar = [10.5 ** .5, 2.5 ** .5]
murange = [[10 ** .5, 1.25 ** .5], [12 ** .5, 3.25 ** .5]]
elif size == 7: # low
mu0 = [7 ** .5, .75 ** .5]
mutar = [6.5 ** .5, .9 ** .5]
murange = [[6 ** .5, .5 ** .5], [8 ** .5, 1. ** .5]]
aEff = 1.25
bEff = 1. - aEff
murangeEff = [[(aEff*murange[0][0]**2. + bEff*murange[1][0]**2.) ** .5,
(aEff*murange[0][1]**2. + bEff*murange[1][1]**2.) ** .5],
[(aEff*murange[1][0]**2. + bEff*murange[0][0]**2.) ** .5,
(aEff*murange[1][1]**2. + bEff*murange[0][1]**2.) ** .5]]
solver = HSDPE(kappa = 2.5, theta = np.pi / 3, mu0 = mu0, n = 20,
verbosity = verb)
if ignore_forcing: solver.nbs = 1
rescaling = [lambda x: np.power(x, 2.), lambda x: x]
rescalingInv = [lambda x: np.power(x, .5), lambda x: x]
if algo == "rational":
params = {'N':MN, 'M':MN, 'S':S, 'POD':True}
if samples == "distributed":
params['polybasis'] = "CHEBYSHEV"
# params['polybasis'] = "LEGENDRE"
# params['polybasis'] = "MONOMIAL"
params['E'] = MN
method = RI
elif samples == "centered_fake":
params['polybasis'] = "MONOMIAL"
params['S'] = R
method = RI
else:
params['S'] = R
method = RP
else: #if algo == "RB":
params = {'R':R, 'S':S, 'POD':True}
if samples == "distributed":
method = RBD
elif samples == "centered_fake":
params['S'] = R
method = RBD
else:
params['S'] = R
method = RBC
if samples == "distributed":
if sampling == "quadrature":
params['sampler'] = QS(murange, "CHEBYSHEV", scaling = rescaling,
scalingInv = rescalingInv)
# params['sampler'] = QS(murange, "GAUSSLEGENDRE", scaling = rescaling,
# scalingInv = rescalingInv)
# params['sampler'] = QS(murange, "UNIFORM", scaling = rescaling,
# scalingInv = rescalingInv)
params['S'] = [max(j, MN + 1) for j in params['S']]
elif sampling == "quadrature_total":
params['sampler'] = QST(murange, "CHEBYSHEV", scaling = rescaling,
scalingInv = rescalingInv)
params['S'] = R
else: # if sampling == "random":
params['sampler'] = RS(murange, "HALTON", scaling = rescaling,
scalingInv = rescalingInv)
params['S'] = R
elif samples == "centered_fake":
params['sampler'] = MS(murange, points = [mu0], scaling = rescaling,
scalingInv = rescalingInv)
approx = method(solver, mu0 = mu0, approxParameters = params,
verbosity = verb)
approx.setupApprox()
if show_sample:
approx.plotApprox(mutar, name = 'u_app')
approx.plotHF(mutar, name = 'u_HF')
approx.plotErr(mutar, name = 'err')
approx.plotRes(mutar, name = 'res')
appErr, solNorm = approx.normErr(mutar), approx.normHF(mutar)
resNorm, RHSNorm = approx.normRes(mutar), approx.normRHS(mutar)
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 algo == "rational":
- from plot_zero_set import plotZeroSet
- plotZeroSet(murange, murangeEff, approx, mu0,
- 200, [2., 1.], clip = clip)
\ No newline at end of file
+ from plot_zero_set import plotZeroSet2
+ muZeroVals, Qvals = plotZeroSet2(murange, murangeEff, approx, mu0,
+ 200, [2., 1.])
+
+if show_norm:
+ solver._solveBatchSize = 100
+ from plot_inf_set import plotInfSet2
+ muInfVals, normEx, normApp, normRes, normErr, beta = plotInfSet2(
+ murange, murangeEff, approx, mu0, 25, [2., 1.], clip = clip)
+
diff --git a/examples/2d/pod/square_pod_hermite.py b/examples/2d/pod/square_pod_hermite.py
index 9fc5401..f512b92 100644
--- a/examples/2d/pod/square_pod_hermite.py
+++ b/examples/2d/pod/square_pod_hermite.py
@@ -1,100 +1,109 @@
import numpy as np
from rrompy.hfengines.linear_problem.bidimensional import \
HelmholtzSquareDomainProblemEngine as HSDPE
from rrompy.reduction_methods.centered import RationalPade as RP
from rrompy.reduction_methods.distributed import RationalInterpolant as RI
from rrompy.reduction_methods.centered import RBCentered as RBC
from rrompy.reduction_methods.distributed import RBDistributed as RBD
from rrompy.parameter.parameter_sampling import (QuadratureSampler as QS,
RandomSampler as RS,
ManualSampler as MS)
verb = 0
size = 1
show_sample = False
+show_norm = True
MN = 4
R = (MN + 2) * (MN + 1) // 2
S0 = [3] * 2
S = [25]
assert R < np.prod(S)
samples = "centered"
samples = "distributed"
algo = "rational"
#algo = "RB"
sampling = "quadrature"
sampling = "random"
if size == 1: # small
mu0 = [4 ** .5, 1.5 ** .5]
mutar = [5 ** .5, 1.75 ** .5]
murange = [[2 ** .5, 1. ** .5], [6 ** .5, 2. ** .5]]
elif size == 2: # medium
mu0 = [4 ** .5, 1.75 ** .5]
mutar = [4.5 ** .5, 1.25 ** .5]
murange = [[1 ** .5, 1. ** .5], [7 ** .5, 2.5 ** .5]]
elif size == 3: # large
mu0 = [6 ** .5, 4 ** .5]
mutar = [2 ** .5, 2.5 ** .5]
murange = [[0 ** .5, 2 ** .5], [12 ** .5, 6 ** .5]]
elif size == 4: # crowded
mu0 = [10 ** .5, 2 ** .5]
mutar = [9 ** .5, 2.25 ** .5]
murange = [[8 ** .5, 1.5 ** .5], [12 ** .5, 2.5 ** .5]]
aEff = 1.25
bEff = 1. - aEff
murangeEff = [[(aEff*murange[0][0]**2. + bEff*murange[1][0]**2.) ** .5,
(aEff*murange[0][1]**2. + bEff*murange[1][1]**2.) ** .5],
[(aEff*murange[1][0]**2. + bEff*murange[0][0]**2.) ** .5,
(aEff*murange[1][1]**2. + bEff*murange[0][1]**2.) ** .5]]
solver = HSDPE(kappa = 2.5, theta = np.pi / 3, mu0 = mu0, n = 20,
verbosity = verb)
rescaling = [lambda x: np.power(x, 2.), lambda x: x]
rescalingInv = [lambda x: np.power(x, .5), lambda x: x]
if algo == "rational":
params = {'N':MN, 'M':MN, 'S':S, 'POD':True}
if samples == "distributed":
params['polybasis'] = "CHEBYSHEV"
method = RI
else:
method = RP
else: #if algo == "RB":
params = {'R':R, 'S':S, 'POD':True}
if samples == "distributed":
method = RBD
else:
method = RBC
if samples == "distributed":
if sampling == "quadrature":
sampler0 = QS(murange, "CHEBYSHEV", scaling = rescaling,
scalingInv = rescalingInv)
else: # if sampling == "random":
sampler0 = RS(murange, "SOBOL", scaling = rescaling,
scalingInv = rescalingInv)
S0 = np.prod(S0)
params['sampler'] = MS(murange, points = sampler0.generatePoints(S0))
approx = method(solver, mu0 = mu0, approxParameters = params,
verbosity = verb)
approx.setupApprox()
if show_sample:
approx.plotApprox(mutar, name = 'u_app')
approx.plotHF(mutar, name = 'u_HF')
approx.plotErr(mutar, name = 'err')
approx.plotRes(mutar, name = 'res')
appErr, solNorm = approx.normErr(mutar), approx.normHF(mutar)
resNorm, RHSNorm = approx.normRes(mutar), approx.normRHS(mutar)
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 algo == "rational":
- from plot_zero_set import plotZeroSet
- plotZeroSet(murange, murangeEff, approx, mu0, 200, [2., 2.])
+ from plot_zero_set import plotZeroSet2
+ muZeroVals, Qvals = plotZeroSet2(murange, murangeEff, approx, mu0,
+ 200, [2., 2.])
+
+if show_norm:
+ solver._solveBatchSize = 100
+ from plot_inf_set import plotInfSet2
+ muInfVals, normEx, normApp, normRes, normErr, beta = plotInfSet2(
+ murange, murangeEff, approx, mu0, 25, [2., 2.])
+
diff --git a/examples/2d/pod/square_simplified_pod.py b/examples/2d/pod/square_simplified_pod.py
index bd0d439..41ac4d1 100644
--- a/examples/2d/pod/square_simplified_pod.py
+++ b/examples/2d/pod/square_simplified_pod.py
@@ -1,138 +1,146 @@
import numpy as np
from rrompy.hfengines.linear_problem.bidimensional import \
HelmholtzSquareSimplifiedDomainProblemEngine as HSSDPE
from rrompy.reduction_methods.centered import RationalPade as RP
from rrompy.reduction_methods.distributed import RationalInterpolant as RI
from rrompy.reduction_methods.centered import RBCentered as RBC
from rrompy.reduction_methods.distributed import RBDistributed as RBD
from rrompy.parameter.parameter_sampling import (QuadratureSampler as QS,
QuadratureSamplerTotal as QST,
ManualSampler as MS,
RandomSampler as RS)
verb = 5
size = 1
show_sample = False
+show_norm = True
MN = 5
R = (MN + 2) * (MN + 1) // 2
S = [int(np.ceil(R ** .5))] * 2
samples = "centered"
samples = "centered_fake"
samples = "distributed"
algo = "rational"
#algo = "RB"
sampling = "quadrature"
sampling = "quadrature_total"
sampling = "random"
if size == 1: # small
mu0 = [4 ** .5, 1.5 ** .5]
mutar = [5 ** .5, 1.75 ** .5]
murange = [[2 ** .5, 1. ** .5], [6 ** .5, 2. ** .5]]
elif size == 2: # medium
mu0 = [4 ** .5, 1.75 ** .5]
mutar = [5 ** .5, 1.25 ** .5]
murange = [[1 ** .5, 1. ** .5], [7 ** .5, 2.5 ** .5]]
elif size == 3: # fat
mu0 = [6 ** .5, 4 ** .5]
mutar = [2 ** .5, 2.5 ** .5]
murange = [[0 ** .5, 2 ** .5], [12 ** .5, 6 ** .5]]
elif size == 4: # crowded
mu0 = [10 ** .5, 2 ** .5]
mutar = [9 ** .5, 2.25 ** .5]
murange = [[8 ** .5, 1.5 ** .5], [12 ** .5, 2.5 ** .5]]
elif size == 5: # tall
mu0 = [11 ** .5, 2.25 ** .5]
mutar = [10.5 ** .5, 2.5 ** .5]
murange = [[10 ** .5, 1.5 ** .5], [12 ** .5, 3 ** .5]]
elif size == 6: # taller
mu0 = [11 ** .5, 2.25 ** .5]
mutar = [10.5 ** .5, 2.5 ** .5]
murange = [[10 ** .5, 1.25 ** .5], [12 ** .5, 3.25 ** .5]]
elif size == 7: # low
mu0 = [7 ** .5, .75 ** .5]
mutar = [8 ** .5, 1 ** .5]
murange = [[6 ** .5, .25 ** .5], [8 ** .5, 1.25 ** .5]]
aEff = 1.25
bEff = 1. - aEff
murangeEff = [[(aEff*murange[0][0]**2. + bEff*murange[1][0]**2.) ** .5,
(aEff*murange[0][1]**2. + bEff*murange[1][1]**2.) ** .5],
[(aEff*murange[1][0]**2. + bEff*murange[0][0]**2.) ** .5,
(aEff*murange[1][1]**2. + bEff*murange[0][1]**2.) ** .5]]
solver = HSSDPE(kappa = 2.5, theta = np.pi / 3, mu0 = mu0, n = 20,
verbosity = verb)
rescaling = [lambda x: np.power(x, 2.)] * 2
rescalingInv = [lambda x: np.power(x, .5)] * 2
if algo == "rational":
params = {'N':MN, 'M':MN, 'S':S, 'POD':True}
if samples == "distributed":
params['polybasis'] = "CHEBYSHEV"
params['polybasis'] = "LEGENDRE"
params['polybasis'] = "MONOMIAL"
params['E'] = MN
method = RI
elif samples == "centered_fake":
params['polybasis'] = "MONOMIAL"
params['S'] = R
method = RI
else:
params['S'] = R
method = RP
else: #if algo == "RB":
params = {'R':R, 'S':S, 'POD':True}
if samples == "distributed":
method = RBD
elif samples == "centered_fake":
params['S'] = R
method = RBD
else:
params['S'] = R
method = RBC
if samples == "distributed":
if sampling == "quadrature":
params['sampler'] = QS(murange, "CHEBYSHEV", scaling = rescaling,
scalingInv = rescalingInv)
# params['sampler'] = QS(murange, "GAUSSLEGENDRE", scaling = rescaling,
# scalingInv = rescalingInv)
# params['sampler'] = QS(murange, "UNIFORM", scaling = rescaling,
# scalingInv = rescalingInv)
params['S'] = [max(j, MN + 1) for j in params['S']]
elif sampling == "quadrature_total":
params['sampler'] = QST(murange, "CHEBYSHEV", scaling = rescaling,
scalingInv = rescalingInv)
params['S'] = R
else: # if sampling == "random":
params['sampler'] = RS(murange, "HALTON", scaling = rescaling,
scalingInv = rescalingInv)
params['S'] = R
elif samples == "centered_fake":
params['sampler'] = MS(murange, points = [mu0], scaling = rescaling,
scalingInv = rescalingInv)
approx = method(solver, mu0 = mu0, approxParameters = params,
verbosity = verb)
approx.setupApprox()
if show_sample:
approx.plotApprox(mutar, name = 'u_app')
approx.plotHF(mutar, name = 'u_HF')
approx.plotErr(mutar, name = 'err')
approx.plotRes(mutar, name = 'res')
appErr, solNorm = approx.normErr(mutar), approx.normHF(mutar)
resNorm, RHSNorm = approx.normRes(mutar), approx.normRHS(mutar)
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 algo == "rational":
- from plot_zero_set import plotZeroSet
- plotZeroSet(murange, murangeEff, approx, mu0, 200, [2., 2.])
-
\ No newline at end of file
+ from plot_zero_set import plotZeroSet2
+ muZeroVals, Qvals = plotZeroSet2(murange, murangeEff, approx, mu0,
+ 200, [2., 2.])
+
+if show_norm:
+ solver._solveBatchSize = 100
+ from plot_inf_set import plotInfSet2
+ muInfVals, normEx, normApp, normRes, normErr, beta = plotInfSet2(
+ murange, murangeEff, approx, mu0, 25, [2., 2.])
+
diff --git a/examples/2d/pod/square_simplified_pod_hermite.py b/examples/2d/pod/square_simplified_pod_hermite.py
index ce63c07..d7bf757 100644
--- a/examples/2d/pod/square_simplified_pod_hermite.py
+++ b/examples/2d/pod/square_simplified_pod_hermite.py
@@ -1,100 +1,109 @@
import numpy as np
from rrompy.hfengines.linear_problem.bidimensional import \
HelmholtzSquareSimplifiedDomainProblemEngine as HSSDPE
from rrompy.reduction_methods.centered import RationalPade as RP
from rrompy.reduction_methods.distributed import RationalInterpolant as RI
from rrompy.reduction_methods.centered import RBCentered as RBC
from rrompy.reduction_methods.distributed import RBDistributed as RBD
from rrompy.parameter.parameter_sampling import (QuadratureSampler as QS,
RandomSampler as RS,
ManualSampler as MS)
verb = 0
size = 1
show_sample = False
+show_norm = True
MN = 4
R = (MN + 2) * (MN + 1) // 2
S0 = [3] * 2
S = [25]
assert R < np.prod(S)
samples = "centered"
samples = "distributed"
algo = "rational"
#algo = "RB"
sampling = "quadrature"
#sampling = "random"
if size == 1: # small
mu0 = [4 ** .5, 1.5 ** .5]
mutar = [5 ** .5, 1.75 ** .5]
murange = [[2 ** .5, 1. ** .5], [6 ** .5, 2. ** .5]]
elif size == 2: # medium
mu0 = [4 ** .5, 1.75 ** .5]
mutar = [4.5 ** .5, 1.25 ** .5]
murange = [[1 ** .5, 1. ** .5], [7 ** .5, 2.5 ** .5]]
elif size == 3: # large
mu0 = [6 ** .5, 4 ** .5]
mutar = [2 ** .5, 2.5 ** .5]
murange = [[0 ** .5, 2 ** .5], [12 ** .5, 6 ** .5]]
elif size == 4: # crowded
mu0 = [10 ** .5, 2 ** .5]
mutar = [9 ** .5, 2.25 ** .5]
murange = [[8 ** .5, 1.5 ** .5], [12 ** .5, 2.5 ** .5]]
aEff = 1.25
bEff = 1. - aEff
murangeEff = [[(aEff*murange[0][0]**2. + bEff*murange[1][0]**2.) ** .5,
(aEff*murange[0][1]**2. + bEff*murange[1][1]**2.) ** .5],
[(aEff*murange[1][0]**2. + bEff*murange[0][0]**2.) ** .5,
(aEff*murange[1][1]**2. + bEff*murange[0][1]**2.) ** .5]]
solver = HSSDPE(kappa = 2.5, theta = np.pi / 3, mu0 = mu0, n = 20,
verbosity = verb)
rescaling = [lambda x: np.power(x, 2.)] * 2
rescalingInv = [lambda x: np.power(x, .5)] * 2
if algo == "rational":
params = {'N':MN, 'M':MN, 'S':S, 'POD':True}
if samples == "distributed":
params['polybasis'] = "CHEBYSHEV"
method = RI
else:
method = RP
else: #if algo == "RB":
params = {'R':R, 'S':S, 'POD':True}
if samples == "distributed":
method = RBD
else:
method = RBC
if samples == "distributed":
if sampling == "quadrature":
sampler0 = QS(murange, "CHEBYSHEV", scaling = rescaling,
scalingInv = rescalingInv)
else: # if sampling == "random":
sampler0 = RS(murange, "SOBOL", scaling = rescaling,
scalingInv = rescalingInv)
S0 = np.prod(S0)
params['sampler'] = MS(murange, points = sampler0.generatePoints(S0))
approx = method(solver, mu0 = mu0, approxParameters = params,
verbosity = verb)
approx.setupApprox()
if show_sample:
approx.plotApprox(mutar, name = 'u_app')
approx.plotHF(mutar, name = 'u_HF')
approx.plotErr(mutar, name = 'err')
approx.plotRes(mutar, name = 'res')
appErr, solNorm = approx.normErr(mutar), approx.normHF(mutar)
resNorm, RHSNorm = approx.normRes(mutar), approx.normRHS(mutar)
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 algo == "rational":
- from plot_zero_set import plotZeroSet
- plotZeroSet(murange, murangeEff, approx, mu0, 200, [2., 2.])
+ from plot_zero_set import plotZeroSet2
+ muZeroVals, Qvals = plotZeroSet2(murange, murangeEff, approx, mu0,
+ 200, [2., 2.])
+
+if show_norm:
+ solver._solveBatchSize = 100
+ from plot_inf_set import plotInfSet2
+ muInfVals, normEx, normApp, normRes, normErr, beta = plotInfSet2(
+ murange, murangeEff, approx, mu0, 50, [2., 2.])
+
diff --git a/examples/2d/pod/square_pod.py b/examples/2d/pod/synthetic_pod.py
similarity index 54%
copy from examples/2d/pod/square_pod.py
copy to examples/2d/pod/synthetic_pod.py
index 56de10d..a466a0d 100644
--- a/examples/2d/pod/square_pod.py
+++ b/examples/2d/pod/synthetic_pod.py
@@ -1,144 +1,158 @@
import numpy as np
from rrompy.hfengines.linear_problem.bidimensional import \
- HelmholtzSquareDomainProblemEngine as HSDPE
+ SyntheticBivariateEngine as SBE
from rrompy.reduction_methods.centered import RationalPade as RP
from rrompy.reduction_methods.distributed import RationalInterpolant as RI
from rrompy.reduction_methods.centered import RBCentered as RBC
from rrompy.reduction_methods.distributed import RBDistributed as RBD
from rrompy.parameter.parameter_sampling import (QuadratureSampler as QS,
QuadratureSamplerTotal as QST,
ManualSampler as MS,
RandomSampler as RS)
verb = 5
size = 1
-show_sample = False
-ignore_forcing = True
-ignore_forcing = False
+show_sample = True
+show_norm = True
clip = -1
#clip = .4
#clip = .6
-MN = 6
+Delta = 0
+MN = 10
R = (MN + 2) * (MN + 1) // 2
S = [int(np.ceil(R ** .5))] * 2
+PODTol = 1e-6
samples = "centered"
samples = "centered_fake"
samples = "distributed"
algo = "rational"
#algo = "RB"
sampling = "quadrature"
-sampling = "quadrature_total"
+#sampling = "quadrature_total"
#sampling = "random"
+if samples == "distributed":
+ radial = 0
+ radial = "gaussian"
+# radial = "thinplate"
+# radial = "multiquadric"
+ rW0 = 10.
+ radialWeight = [rW0] * 2
+
if size == 1: # small
- mu0 = [4 ** .5, 1.5 ** .5]
- mutar = [5 ** .5, 1.75 ** .5]
- murange = [[2 ** .5, 1. ** .5], [6 ** .5, 2. ** .5]]
-elif size == 2: # medium
- mu0 = [4 ** .5, 1.75 ** .5]
- mutar = [5 ** .5, 1.25 ** .5]
- murange = [[1 ** .5, 1. ** .5], [7 ** .5, 2.5 ** .5]]
-elif size == 3: # fat
- mu0 = [6 ** .5, 4 ** .5]
- mutar = [2 ** .5, 2.5 ** .5]
- murange = [[0 ** .5, 2 ** .5], [12 ** .5, 6 ** .5]]
-elif size == 4: # crowded
- mu0 = [10 ** .5, 2 ** .5]
- mutar = [9 ** .5, 2.25 ** .5]
- murange = [[8 ** .5, 1.5 ** .5], [12 ** .5, 2.5 ** .5]]
-elif size == 5: # tall
- mu0 = [11 ** .5, 2.25 ** .5]
- mutar = [10.5 ** .5, 2.5 ** .5]
- murange = [[10 ** .5, 1.5 ** .5], [12 ** .5, 3 ** .5]]
-elif size == 6: # taller
- mu0 = [11 ** .5, 2.25 ** .5]
- mutar = [10.5 ** .5, 2.5 ** .5]
- murange = [[10 ** .5, 1.25 ** .5], [12 ** .5, 3.25 ** .5]]
-elif size == 7: # low
- mu0 = [7 ** .5, .75 ** .5]
- mutar = [6.5 ** .5, .9 ** .5]
- murange = [[6 ** .5, .5 ** .5], [8 ** .5, 1. ** .5]]
-
-aEff = 1.25
+ mu0 = [10. ** .5, 15. ** .5]
+ mutar = [12. ** .5, 14. ** .5]
+ murange = [[5. ** .5, 10. ** .5], [15 ** .5, 20 ** .5]]
+if size == 2: # large
+ mu0 = [15. ** .5, 17.5 ** .5]
+ mutar = [18. ** .5, 22. ** .5]
+ murange = [[5. ** .5, 10. ** .5], [25 ** .5, 25 ** .5]]
+if size == 3: # medium
+ mu0 = [17.5 ** .5, 15 ** .5]
+ mutar = [20. ** .5, 18. ** .5]
+ murange = [[10. ** .5, 10. ** .5], [25 ** .5, 20 ** .5]]
+
+assert Delta <= 0
+
+aEff = 1.#25
bEff = 1. - aEff
murangeEff = [[(aEff*murange[0][0]**2. + bEff*murange[1][0]**2.) ** .5,
- (aEff*murange[0][1]**2. + bEff*murange[1][1]**2.) ** .5],
+ aEff*murange[0][1] + bEff*murange[1][1]],
[(aEff*murange[1][0]**2. + bEff*murange[0][0]**2.) ** .5,
- (aEff*murange[1][1]**2. + bEff*murange[0][1]**2.) ** .5]]
+ aEff*murange[1][1] + bEff*murange[0][1]]]
-solver = HSDPE(kappa = 2.5, theta = np.pi / 3, mu0 = mu0, n = 20,
- verbosity = verb)
-if ignore_forcing: solver.nbs = 1
+kappa = 20. ** .5
+theta = - np.pi / 6.
+n = 20
+L = np.pi
-rescaling = [lambda x: np.power(x, 2.), lambda x: x]
-rescalingInv = [lambda x: np.power(x, .5), lambda x: x]
+solver = SBE(kappa = kappa, theta = theta, n = n, L = L,
+ mu0 = mu0, verbosity = verb)
+
+rescaling = [lambda x: np.power(x, 2.)] * 2
+rescalingInv = [lambda x: np.power(x, .5)] * 2
if algo == "rational":
- params = {'N':MN, 'M':MN, 'S':S, 'POD':True}
+ params = {'N':MN, 'M':MN + Delta, 'S':S, 'POD':True}
if samples == "distributed":
params['polybasis'] = "CHEBYSHEV"
# params['polybasis'] = "LEGENDRE"
# params['polybasis'] = "MONOMIAL"
params['E'] = MN
+ params['radialBasis'] = radial
+ params['radialBasisWeights'] = radialWeight
method = RI
elif samples == "centered_fake":
params['polybasis'] = "MONOMIAL"
params['S'] = R
method = RI
else:
params['S'] = R
method = RP
else: #if algo == "RB":
- params = {'R':R, 'S':S, 'POD':True}
+ params = {'R':(MN + 2 + Delta) * (MN + 1 + Delta) // 2, 'S':S,
+ 'POD':True, 'PODTolerance':PODTol}
if samples == "distributed":
method = RBD
elif samples == "centered_fake":
params['S'] = R
method = RBD
else:
params['S'] = R
method = RBC
if samples == "distributed":
if sampling == "quadrature":
params['sampler'] = QS(murange, "CHEBYSHEV", scaling = rescaling,
scalingInv = rescalingInv)
# params['sampler'] = QS(murange, "GAUSSLEGENDRE", scaling = rescaling,
# scalingInv = rescalingInv)
# params['sampler'] = QS(murange, "UNIFORM", scaling = rescaling,
# scalingInv = rescalingInv)
params['S'] = [max(j, MN + 1) for j in params['S']]
elif sampling == "quadrature_total":
params['sampler'] = QST(murange, "CHEBYSHEV", scaling = rescaling,
scalingInv = rescalingInv)
params['S'] = R
else: # if sampling == "random":
params['sampler'] = RS(murange, "HALTON", scaling = rescaling,
scalingInv = rescalingInv)
params['S'] = R
elif samples == "centered_fake":
params['sampler'] = MS(murange, points = [mu0], scaling = rescaling,
scalingInv = rescalingInv)
-approx = method(solver, mu0 = mu0, approxParameters = params,
- verbosity = verb)
+approx = method(solver, mu0 = mu0, approxParameters = params, verbosity = verb)
+if samples == "distributed": approx.samplingEngine.allowRepeatedSamples = False
approx.setupApprox()
if show_sample:
- approx.plotApprox(mutar, name = 'u_app')
- approx.plotHF(mutar, name = 'u_HF')
- approx.plotErr(mutar, name = 'err')
- approx.plotRes(mutar, name = 'res')
- appErr, solNorm = approx.normErr(mutar), approx.normHF(mutar)
- resNorm, RHSNorm = approx.normRes(mutar), approx.normRHS(mutar)
+ L = mutar[1]
+ approx.plotApprox(mutar, name = 'u_app', homogeneized = False,
+ what = "REAL")
+ approx.plotHF(mutar, name = 'u_HF', homogeneized = False, what = "REAL")
+ approx.plotErr(mutar, name = 'err', homogeneized = False, what = "REAL")
+# approx.plotRes(mutar, name = 'res', homogeneized = False, what = "REAL")
+ appErr = approx.normErr(mutar)
+ solNorm = approx.normHF(mutar)
+ resNorm = approx.normRes(mutar)
+ RHSNorm = approx.normRHS(mutar)
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 algo == "rational":
- from plot_zero_set import plotZeroSet
- plotZeroSet(murange, murangeEff, approx, mu0,
- 200, [2., 1.], clip = clip)
\ No newline at end of file
+if algo == "rational" and approx.N > 0:
+ from plot_zero_set import plotZeroSet2
+ muZeroVals, Qvals = plotZeroSet2(murange, murangeEff, approx, mu0,
+ 200, [2., 2.], clip = clip)
+
+if show_norm:
+ solver._solveBatchSize = 100
+ from plot_inf_set import plotInfSet2
+ muInfVals, normEx, normApp, normRes, normErr, beta = plotInfSet2(
+ murange, murangeEff, approx, mu0, 50,
+ [2., 2.], clip = clip, relative = False)
+
diff --git a/rrompy/hfengines/base/boundary_conditions.py b/rrompy/hfengines/base/boundary_conditions.py
index 6993762..4ed01fe 100644
--- a/rrompy/hfengines/base/boundary_conditions.py
+++ b/rrompy/hfengines/base/boundary_conditions.py
@@ -1,126 +1,129 @@
# 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
from fenics import SubDomain, AutoSubDomain
from rrompy.utilities.base.types import GenExpr
from rrompy.solver.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 RROMPyException("BC kind not recognized.")
def name(self) -> str:
return self.__class__.__name__
def __str__(self) -> str:
return self.name()
+ def __deepcopy__(self, memo):
+ return copy(self)
+
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 RROMPyException("Wildcard not recognized.")
elif callable(value):
self._standardManagementCallable(kind, value)
elif isinstance(value, (SubDomain,)):
self._standardManagement(kind, value)
else:
raise RROMPyException(kind + "Boundary type not recognized.")
def _complementaryManagementAll(self, kind:str):
if kind not in self.allowedKinds:
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 RROMPyException("BC kind not recognized.")
otherBCs = []
for k in self.allowedKinds:
if k != kind:
if hasattr(self, "_" + k + "Rest"):
self._standardManagementCallable(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]))
+ restCall = lambda x, on_boundary: (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/matrix_engine_base.py b/rrompy/hfengines/base/matrix_engine_base.py
index 664e74e..d4dc0c7 100644
--- a/rrompy/hfengines/base/matrix_engine_base.py
+++ b/rrompy/hfengines/base/matrix_engine_base.py
@@ -1,441 +1,568 @@
# 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
import scipy.sparse as scsp
from matplotlib import pyplot as plt
-from copy import deepcopy as copy
+from copy import deepcopy as copy, copy as softcopy
from rrompy.utilities.base.types import (Np1D, Np2D, ScOp, strLst, Tuple, List,
DictAny, paramVal, paramList,
sampList)
from rrompy.utilities.base import (purgeList, getNewFilename, verbosityDepth,
multibinom)
from rrompy.utilities.poly_fitting.polynomial import (
hashDerivativeToIdx as hashD, hashIdxToDerivative as hashI)
from rrompy.utilities.exception_manager import RROMPyException, RROMPyAssert
from rrompy.parameter import checkParameter, checkParameterList
from rrompy.sampling import sampleList, emptySampleList
-from rrompy.solver import setupSolver
+from rrompy.solver import setupSolver, Np2DLikeEye
+from rrompy.solver.scipy import tensorizeLS, detensorizeLS
__all__ = ['MatrixEngineBase']
-
+
class MatrixEngineBase:
"""
Generic solver for parametric matrix problems.
Attributes:
verbosity: Verbosity level.
As: Scipy sparse array representation (in CSC format) of As.
bs: Numpy array representation of bs.
bsH: Numpy array representation of homogeneized bs.
energyNormMatrix: Scipy sparse matrix representing inner product.
+ energyNormDualMatrix: Scipy sparse matrix representing dual inner
+ product.
+ dualityMatrix: Scipy sparse matrix representing duality.
+ energyNormPartialDualMatrix: Scipy sparse matrix representing dual
+ inner product without duality.
"""
+
+ _solveBatchSize = 1
def __init__(self, verbosity : int = 10, timestamp : bool = True):
self.verbosity = verbosity
self.timestamp = timestamp
self.nAs, self.nbs = 1, 1
self.setSolver("SPSOLVE", {"use_umfpack" : False})
self.npar = 0
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] != "__"]
+ def __deepcopy__(self, memo):
+ return softcopy(self)
+
@property
def npar(self):
"""Value of npar."""
return self._npar
@npar.setter
def npar(self, npar):
nparOld = self._npar if hasattr(self, "_npar") else -1
if npar != nparOld:
self.rescalingExp = [1.] * npar
self._npar = npar
@property
def nAs(self):
"""Value of nAs."""
return self._nAs
@nAs.setter
def nAs(self, nAs):
self._nAs = nAs
self.resetAs()
@property
def nbs(self):
"""Value of nbs."""
return self._nbs
@nbs.setter
def nbs(self, nbs):
self._nbs = nbs
self.resetbs()
@property
def nbsH(self) -> int:
return max(self.nbs, self.nAs)
def spacedim(self):
return self.As[0].shape[1]
def checkParameter(self, mu:paramList):
return checkParameter(mu, self.npar)
def checkParameterList(self, mu:paramList):
return checkParameterList(mu, self.npar)
def buildEnergyNormForm(self): # eye
"""
Build sparse matrix (in CSR format) representative of scalar product.
"""
- self.energyNormMatrix = np.eye(self.spacedim())
-
- def innerProduct(self, u:Np2D, v:Np2D, onlyDiag : bool = False) -> Np2D:
- """Scalar product."""
+ if self.verbosity >= 20:
+ verbosityDepth("INIT", "Assembling energy matrix.",
+ timestamp = self.timestamp)
+ self.energyNormMatrix = Np2DLikeEye()
+ if self.verbosity >= 20:
+ verbosityDepth("DEL", "Done assembling energy matrix.",
+ timestamp = self.timestamp)
+
+ def buildEnergyNormDualForm(self):
+ """
+ Build sparse matrix (in CSR format) representative of dual scalar
+ product.
+ """
+ if not hasattr(self, "energyNormMatrix"):
+ self.buildEnergyNormForm()
+ if self.verbosity >= 20:
+ verbosityDepth("INIT", "Assembling energy dual matrix.",
+ timestamp = self.timestamp)
+ self.energyNormDualMatrix = self.energyNormMatrix
+ if self.verbosity >= 20:
+ verbosityDepth("DEL", "Done assembling energy dual matrix.",
+ timestamp = self.timestamp)
+
+ def buildDualityPairingForm(self):
+ """Build sparse matrix (in CSR format) representative of duality."""
if not hasattr(self, "energyNormMatrix"):
- if self.verbosity >= 20:
- verbosityDepth("INIT", "Assembling energy matrix.",
- timestamp = self.timestamp)
self.buildEnergyNormForm()
- if self.verbosity >= 20:
- verbosityDepth("DEL", "Done assembling energy matrix.",
- timestamp = self.timestamp)
+ if self.verbosity >= 20:
+ verbosityDepth("INIT", "Assembling duality matrix.",
+ timestamp = self.timestamp)
+ self.dualityMatrix = self.energyNormMatrix
+ if self.verbosity >= 20:
+ verbosityDepth("DEL", "Done assembling duality matrix.",
+ timestamp = self.timestamp)
+
+ def buildEnergyNormPartialDualForm(self):
+ """
+ Build sparse matrix (in CSR format) representative of dual scalar
+ product without duality.
+ """
+ if not hasattr(self, "energyNormDualMatrix"):
+ self.buildEnergyNormDualForm()
+ if self.verbosity >= 20:
+ verbosityDepth("INIT", "Assembling energy partial dual matrix.",
+ timestamp = self.timestamp)
+ self.energyNormPartialDualMatrix = self.energyNormDualMatrix
+ if self.verbosity >= 20:
+ verbosityDepth("DEL",
+ "Done assembling energy partial dual matrix.",
+ timestamp = self.timestamp)
+
+ def innerProduct(self, u:Np2D, v:Np2D, onlyDiag : bool = False,
+ dual : bool = False, duality : bool = True) -> Np2D:
+ """Scalar product."""
+ if dual:
+ if duality:
+ if not hasattr(self, "energyNormDualMatrix"):
+ self.buildEnergyNormDualForm()
+ energyMat = self.energyNormDualMatrix
+ else:
+ if not hasattr(self, "energyNormPartialDualMatrix"):
+ self.buildEnergyNormPartialDualForm()
+ energyMat = self.energyNormPartialDualMatrix
+ else:
+ if not hasattr(self, "energyNormMatrix"):
+ self.buildEnergyNormForm()
+ energyMat = self.energyNormMatrix
if not isinstance(u, (np.ndarray,)): u = u.data
if not isinstance(v, (np.ndarray,)): v = v.data
if onlyDiag:
- return np.sum(self.energyNormMatrix.dot(u) * v.conj(), axis = 0)
- return v.T.conj().dot(self.energyNormMatrix.dot(u))
+ return np.sum(energyMat.dot(u) * v.conj(), axis = 0)
+ return v.T.conj().dot(energyMat.dot(u))
- def norm(self, u:Np2D) -> Np1D:
- return np.abs(self.innerProduct(u, u, onlyDiag = True)) ** .5
+ def norm(self, u:Np2D, dual : bool = False, duality : bool = True) -> Np1D:
+ return np.abs(self.innerProduct(u, u, onlyDiag = True, dual = dual,
+ duality = duality)) ** .5
def checkAInBounds(self, derI : int = 0):
"""Check if derivative index is oob for operator of linear system."""
if derI < 0 or derI >= self.nAs:
d = self.spacedim()
return scsp.csr_matrix((np.zeros(0), np.zeros(0), np.zeros(d + 1)),
shape = (d, d), dtype = np.complex)
def checkbInBounds(self, derI : int = 0, homogeneized : bool = False):
"""Check if derivative index is oob for RHS of linear system."""
nbs = self.nbsH if homogeneized else self.nbs
if derI < 0 or derI >= nbs:
return np.zeros(self.spacedim(), dtype = np.complex)
def resetAs(self):
"""Reset (derivatives of) operator of linear system."""
self.setAs([None] * self.nAs)
if hasattr(self, "_nbs"): self.resetbsH()
def resetbs(self):
"""Reset (derivatives of) RHS of linear system."""
self.setbs([None] * self.nbs)
if hasattr(self, "_nAs"): self.resetbsH()
def resetbsH(self):
"""Reset (derivatives of) homogeneized RHS of linear system."""
self.setbsH([None] * self.nbsH)
def setAs(self, As:List[Np2D]):
"""Assign terms of operator of linear system."""
if len(As) != self.nAs:
raise RROMPyException(("Expected number {} of terms of As not "
"matching given list length {}.").format(self.nAs,
len(As)))
self.As = [copy(A) for A in As]
def setbs(self, bs:List[Np1D]):
"""Assign terms of RHS of linear system."""
if len(bs) != self.nbs:
raise RROMPyException(("Expected number {} of terms of bs not "
"matching given list length {}.").format(self.nbs,
len(bs)))
self.bs = [copy(b) for b in bs]
def setbsH(self, bsH:List[Np1D]):
"""Assign terms of homogeneized RHS of linear system."""
if len(bsH) != self.nbsH:
raise RROMPyException(("Expected number {} of terms of bsH not "
"matching given list length {}.").format(self.nbsH,
len(bsH)))
self.bsH = [copy(bH) for bH in bsH]
def _assembleA(self, mu : paramVal = [], der : List[int] = 0,
derI : int = None, muBase : paramVal = None) -> ScOp:
"""Assemble (derivative of) operator of linear system."""
mu = self.checkParameter(mu)
if muBase is None: muBase = [0.] * self.npar
muBase = self.checkParameter(muBase)
if self.npar > 0: mu, muBase = mu[0], muBase[0]
if not hasattr(der, "__len__"): der = [der] * self.npar
if derI is None: derI = hashD(der)
Anull = self.checkAInBounds(derI)
if Anull is not None: return Anull
rExp = self.rescalingExp
A = copy(self.As[derI])
for j in range(derI + 1, self.nAs):
derIdx = hashI(j, self.npar)
diffIdx = [x - y for (x, y) in zip(derIdx, der)]
if np.all([x >= 0 for x in diffIdx]):
A = A + (np.prod((mu ** rExp - muBase ** rExp) ** diffIdx)
* multibinom(derIdx, diffIdx) * self.As[j])
return A
@abstractmethod
def A(self, mu : paramVal = [], der : List[int] = 0) -> ScOp:
"""
Assemble terms of operator of linear system and return it (or its
derivative) at a given parameter.
"""
if not hasattr(der, "__len__"): der = [der] * self.npar
derI = hashD(der)
for j in range(derI, self.nAs):
- if self.As[j] is None: self.As[j] = 0.
+ if self.As[j] is None: self.As[j] = self.checkAInBounds(-1)
return self._assembleA(mu, der, derI)
def affineLinearSystemA(self, mu : paramVal = []) -> 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, hashI(j, self.npar))
return As
def affineWeightsA(self, mu : paramVal = []) -> List[str]:
"""
Assemble affine blocks of operator of linear system (just affine
weights). Stored as strings for the sake of pickling.
"""
mu = self.checkParameter(mu)
lambdasA = ["1."]
mu0Eff = mu ** self.rescalingExp
for j in range(1, self.nAs):
lambdasA += ["np.prod((mu ** ({1}) - [{0}]) ** ({2}))".format(
','.join([str(x) for x in mu0Eff[0]]),
self.rescalingExp, hashI(j, self.npar))]
return lambdasA
def affineBlocksA(self, mu : paramVal = [])\
-> Tuple[List[Np2D], List[str]]:
"""Assemble affine blocks of operator of linear system."""
return self.affineLinearSystemA(mu), self.affineWeightsA(mu)
def _assembleb(self, mu : paramVal = [], der : List[int] = 0,
derI : int = None, homogeneized : bool = False,
muBase : paramVal = None) -> ScOp:
"""Assemble (derivative of) (homogeneized) RHS of linear system."""
mu = self.checkParameter(mu)
if muBase is None: muBase = [0.] * self.npar
muBase = self.checkParameter(muBase)
if self.npar > 0: mu, muBase = mu[0], muBase[0]
if not hasattr(der, "__len__"): der = [der] * self.npar
if derI is None: derI = hashD(der)
bnull = self.checkbInBounds(derI, homogeneized)
if bnull is not None: return bnull
bs = self.bsH if homogeneized else self.bs
rExp = self.rescalingExp
b = copy(bs[derI])
for j in range(derI + 1, len(bs)):
derIdx = hashI(j, self.npar)
diffIdx = [x - y for (x, y) in zip(derIdx, der)]
if np.all([x >= 0 for x in diffIdx]):
b = b + (np.prod((mu ** rExp - muBase ** rExp) ** diffIdx)
* multibinom(derIdx, diffIdx) * bs[j])
return b
@abstractmethod
def b(self, mu : paramVal = [], der : List[int] = 0,
homogeneized : bool = False) -> Np1D:
"""
Assemble terms of (homogeneized) RHS of linear system and return it (or
its derivative) at a given parameter.
"""
if not hasattr(der, "__len__"): der = [der] * self.npar
derI = hashD(der)
if homogeneized:
for j in range(derI, self.nbsH):
- if self.bsH[j] is None: self.bsH[j] = 0.
+ if self.bsH[j] is None: self.bsH[j] = self.checkbInBounds(-1)
else:
for j in range(derI, self.nbs):
- if self.bs[j] is None: self.bs[j] = 0.
+ if self.bs[j] is None: self.bs[j] = self.checkbInBounds(-1)
return self._assembleb(mu, der, derI, homogeneized)
def affineLinearSystemb(self, mu : paramVal = [],
homogeneized : bool = False) -> List[Np1D]:
"""
Assemble affine blocks of RHS of linear system (just linear blocks).
"""
nbs = self.nbsH if homogeneized else self.nbs
bs = [None] * nbs
for j in range(nbs):
bs[j] = self.b(mu, hashI(j, self.npar), homogeneized)
return bs
def affineWeightsb(self, mu : paramVal = [],
homogeneized : bool = False) -> List[str]:
"""
Assemble affine blocks of RHS of linear system (just affine weights).
Stored as strings for the sake of pickling.
"""
mu = self.checkParameter(mu)
nbs = self.nbsH if homogeneized else self.nbs
lambdasb = ["1."]
mu0Eff = mu ** self.rescalingExp
for j in range(1, nbs):
lambdasb += ["np.prod((mu ** ({1}) - [{0}]) ** ({2}))".format(
','.join([str(x) for x in mu0Eff[0]]),
self.rescalingExp, hashI(j, self.npar))]
return lambdasb
def affineBlocksb(self, mu : paramVal = [], homogeneized : bool = False)\
-> Tuple[List[Np1D], List[str]]:
"""Assemble affine blocks of RHS of linear system."""
return (self.affineLinearSystemb(mu, homogeneized),
self.affineWeightsb(mu, homogeneized))
def setSolver(self, solverType:str, solverArgs : DictAny = {}):
"""Choose solver type and parameters."""
self._solver, self._solverArgs = setupSolver(solverType, solverArgs)
def solve(self, mu : paramList = [], RHS : sampList = None,
homogeneized : bool = False) -> sampList:
"""
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.
"""
mu, _ = self.checkParameterList(mu)
if mu.shape[0] == 0: mu.reset((1, self.npar), mu.dtype)
- if RHS is None:
- RHS = [self.b(m, homogeneized = homogeneized) for m in mu]
- RHS = sampleList(RHS)
- mult = 0 if len(RHS) == 1 else 1
- RROMPyAssert(mult * (len(mu) - 1) + 1, len(RHS), "Sample size")
sol = emptySampleList()
- for j in range(len(mu)):
- u = self._solver(self.A(mu[j]), RHS[mult * j], self._solverArgs)
- if j == 0:
- sol.reset((len(u), len(mu)), dtype = u.dtype)
- sol[j] = u
+ if len(mu) > 0:
+ if RHS is None:
+ RHS = [self.b(m, homogeneized = homogeneized) for m in mu]
+ RHS = sampleList(RHS)
+ mult = 0 if len(RHS) == 1 else 1
+ RROMPyAssert(mult * (len(mu) - 1) + 1, len(RHS), "Sample size")
+ u = self._solver(self.A(mu[0]), RHS[0], self._solverArgs)
+ sol.reset((len(u), len(mu)), dtype = u.dtype)
+ sol[0] = u
+ for j in range(1, len(mu), self._solveBatchSize):
+ if self._solveBatchSize != 1:
+ iRange = list(range(j, min(j + self._solveBatchSize,
+ len(mu))))
+ As = [self.A(mu[i]) for i in iRange]
+ bs = [RHS[mult * i] for i in iRange]
+ A, b = tensorizeLS(As, bs)
+ else:
+ A, b = self.A(mu[j]), RHS[mult * j]
+ solStack = self._solver(A, b, self._solverArgs)
+ if self._solveBatchSize != 1:
+ sol[iRange] = detensorizeLS(solStack, len(iRange))
+ else:
+ sol[j] = solStack
return sol
def residual(self, u:sampList, mu : paramList = [],
- homogeneized : bool = False) -> sampList:
+ homogeneized : bool = False,
+ duality : bool = True) -> sampList:
"""
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.
"""
mu, _ = self.checkParameterList(mu)
if mu.shape[0] == 0: mu.reset((1, self.npar), mu.dtype)
if u is not None:
u = sampleList(u)
mult = 0 if len(u) == 1 else 1
RROMPyAssert(mult * (len(mu) - 1) + 1, len(u), "Sample size")
res = emptySampleList()
+ if duality and not hasattr(self, "dualityMatrix"):
+ self.buildDualityPairingForm()
for j in range(len(mu)):
b = self.b(mu[j], homogeneized = homogeneized)
if u is None:
r = b
else:
r = b - self.A(mu[j]).dot(u[mult * j])
if j == 0:
res.reset((len(r), len(mu)), dtype = r.dtype)
+ if duality:
+ r = self.dualityMatrix.dot(r)
res[j] = r
return res
+ def _rayleighQuotient(self, A:Np2D, v0:Np1D, M:Np2D, sigma : float = 0.,
+ nIterP : int = 10, nIterR : int = 10) -> float:
+ nIterP = min(nIterP, len(v0) // 2)
+ nIterR = min(nIterR, (len(v0) + 1) // 2)
+ v0 /= v0.T.conj().dot(M.dot(v0)) ** .5
+ for j in range(nIterP):
+ v0 = self._solver(A - sigma * M, M.dot(v0), self._solverArgs)
+ v0 /= v0.T.conj().dot(M.dot(v0)) ** .5
+ l0 = v0.T.conj().dot(A.dot(v0))
+ for j in range(nIterR):
+ v0 = self._solver(A - l0 * M, M.dot(v0), self._solverArgs)
+ v0 /= v0.T.conj().dot(M.dot(v0)) ** .5
+ l0 = v0.T.conj().dot(A.dot(v0))
+ if np.isnan(l0): l0 = np.finfo(float).eps
+ return np.abs(l0)
+
+ def stabilityFactor(self, u:sampList, mu : paramList = [],
+ nIterP : int = 10, nIterR : int = 10) -> sampList:
+ """
+ Find stability factor of matrix of linear system using iterative
+ inverse power iteration- and Rayleigh quotient-based procedure.
+
+ Args:
+ u: numpy complex arrays with function dofs.
+ mu: parameter values.
+ nIterP: number of iterations of power method.
+ nIterR: number of iterations of Rayleigh quotient method.
+ """
+ mu, _ = self.checkParameterList(mu)
+ if mu.shape[0] == 0: mu.reset((1, self.npar), mu.dtype)
+ u = sampleList(u)
+ mult = 0 if len(u) == 1 else 1
+ RROMPyAssert(mult * (len(mu) - 1) + 1, len(u), "Sample size")
+ stabFact = np.empty(len(mu), dtype = float)
+ if not hasattr(self, "energyNormMatrix"):
+ self.buildEnergyNormForm()
+ for j in range(len(mu)):
+ stabFact[j] = self._rayleighQuotient(self.A(mu[j]), u[mult * j],
+ self.energyNormMatrix,
+ 0., nIterP, nIterR)
+ return stabFact
+
def plot(self, u:Np1D, name : str = "u", save : str = None,
what : strLst = 'all', saveFormat : str = "eps",
saveDPI : int = 100, show : bool = True, **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.
show(optional): Whether to show figure. Defaults to True.
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
idxs = np.arange(self.spacedim())
plt.figure(**figspecs)
plt.jet()
if 'ABS' in what:
subplotcode = subplotcode + 1
plt.subplot(subplotcode)
- plt.plot(idxs, np.abs(u))
+ plt.plot(idxs, np.abs(u).flatten())
plt.title("|{0}|".format(name))
if 'PHASE' in what:
subplotcode = subplotcode + 1
plt.subplot(subplotcode)
- plt.plot(idxs, np.angle(u))
+ plt.plot(idxs, np.angle(u).flatten())
plt.title("phase({0})".format(name))
if 'REAL' in what:
subplotcode = subplotcode + 1
plt.subplot(subplotcode)
- plt.plot(idxs, np.real(u))
+ plt.plot(idxs, np.real(u).flatten())
plt.title("Re({0})".format(name))
if 'IMAG' in what:
subplotcode = subplotcode + 1
plt.subplot(subplotcode)
- plt.plot(idxs, np.imag(u))
+ plt.plot(idxs, np.imag(u).flatten())
plt.title("Im({0})".format(name))
if save is not None:
save = save.strip()
plt.savefig(getNewFilename("{}_fig_".format(save), saveFormat),
format = saveFormat, dpi = saveDPI)
if show:
plt.show()
plt.close()
diff --git a/rrompy/hfengines/base/problem_engine_base.py b/rrompy/hfengines/base/problem_engine_base.py
index fa33798..d0c61c0 100644
--- a/rrompy/hfengines/base/problem_engine_base.py
+++ b/rrompy/hfengines/base/problem_engine_base.py
@@ -1,330 +1,367 @@
# 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 os import path, mkdir
import fenics as fen
import numpy as np
from matplotlib import pyplot as plt
from copy import deepcopy as copy
from rrompy.utilities.base.types import (Np1D, strLst, FenFunc, Tuple, List,
paramVal)
from rrompy.utilities.base import purgeList, getNewFilename, verbosityDepth
-from rrompy.solver.fenics import L2NormMatrix
+from rrompy.solver import Np2DLikeEye
+from rrompy.solver.fenics import L2NormMatrix, fenplot, interp_project
from .boundary_conditions import BoundaryConditions
from .matrix_engine_base import MatrixEngineBase
from rrompy.utilities.exception_manager import RROMPyException
__all__ = ['ProblemEngineBase']
class ProblemEngineBase(MatrixEngineBase):
"""
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.
+ energyNormDualMatrix: Scipy sparse matrix representing inner product
+ over V'.
+ dualityMatrix: Scipy sparse matrix representing duality V-V'.
+ energyNormPartialDualMatrix: Scipy sparse matrix representing dual
+ inner product between Riesz representers V-V.
liftedDirichletDatum: Dofs of Dirichlet datum lifting.
mu0BC: Mu value of last Dirichlet datum lifting.
degree_threshold: Threshold for ufl expression interpolation degree.
"""
+ _dualityCompress = None
+
def __init__(self, degree_threshold : int = np.inf, verbosity : int = 10,
timestamp : bool = True):
super().__init__(verbosity = verbosity, timestamp = timestamp)
self.BCManager = BoundaryConditions("Dirichlet")
self.V = fen.FunctionSpace(fen.UnitSquareMesh(10, 10), "P", 1)
self.mu0BC = np.nan
self.degree_threshold = degree_threshold
self.npar = 0
@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 RROMPyException("V type not recognized.")
self._V = V
self.u = fen.TrialFunction(V)
self.v = fen.TestFunction(V)
def spacedim(self):
return self.V.dim()
def buildEnergyNormForm(self):
"""
Build sparse matrix (in CSR format) representative of scalar product.
"""
+ if self.verbosity >= 20:
+ verbosityDepth("INIT", "Assembling energy matrix.",
+ timestamp = self.timestamp)
self.energyNormMatrix = L2NormMatrix(self.V)
+ if self.verbosity >= 20:
+ verbosityDepth("DEL", "Done assembling energy matrix.",
+ timestamp = self.timestamp)
+
+ def buildDualityPairingForm(self):
+ """Build sparse matrix (in CSR format) representative of duality."""
+ if self.verbosity >= 20:
+ verbosityDepth("INIT", "Assembling duality matrix.",
+ timestamp = self.timestamp)
+ self.dualityMatrix = Np2DLikeEye()
+ if self.verbosity >= 20:
+ verbosityDepth("DEL", "Done assembling duality matrix.",
+ timestamp = self.timestamp)
def liftDirichletData(self, mu : paramVal = []) -> Np1D:
"""Lift Dirichlet datum."""
mu = self.checkParameter(mu)
if mu != self.mu0BC:
self.mu0BC = copy(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)
+ liftRe = interp_project(self.DirichletDatum[0], self.V)
+ liftIm = interp_project(self.DirichletDatum[1], self.V)
self.liftedDirichletDatum = (np.array(liftRe.vector())
+ 1.j * np.array(liftIm.vector()))
return self.liftedDirichletDatum
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),
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 {}
- def plot(self, u:Np1D, name : str = "u", save : str = None,
- what : strLst = 'all', saveFormat : str = "eps",
- saveDPI : int = 100, show : bool = True, **figspecs):
+ def plot(self, u:Np1D, warping : List[callable] = None, name : str = "u",
+ save : str = None, what : strLst = 'all',
+ saveFormat : str = "eps", saveDPI : int = 100, show : bool = True,
+ **figspecs):
"""
Do some nice plots of the complex-valued function with given dofs.
Args:
u: numpy complex array with function dofs.
+ warping(optional): Domain warping functions.
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.
show(optional): Whether to show figure. Defaults to True.
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))
+ p = fenplot(uAb, warping = warping, 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))
+ p = fenplot(uPh, warping = warping,
+ 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))
+ p = fenplot(uRe, warping = warping, 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))
+ p = fenplot(uIm, warping = warping, 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)
if show:
plt.show()
plt.close()
- def plotmesh(self, name : str = "Mesh", save : str = None,
- saveFormat : str = "eps", saveDPI : int = 100,
- show : bool = True, **figspecs):
+ def plotmesh(self, warping : List[callable] = None, name : str = "Mesh",
+ save : str = None, saveFormat : str = "eps",
+ saveDPI : int = 100, show : bool = True, **figspecs):
"""
Do a nice plot of the mesh.
Args:
u: numpy complex array with function dofs.
+ warping(optional): Domain warping functions.
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.
show(optional): Whether to show figure. Defaults to True.
figspecs(optional key args): Optional arguments for matplotlib
figure creation.
"""
plt.figure(**figspecs)
- fen.plot(self.V.mesh())
+ fenplot(self.V.mesh(), warping = warping)
if save is not None:
save = save.strip()
plt.savefig(getNewFilename("{}_msh_".format(save), saveFormat),
format = saveFormat, dpi = saveDPI)
if show:
plt.show()
plt.close()
- def outParaview(self, u:Np1D, name : str = "u", filename : str = "out",
+ def outParaview(self, u:Np1D, warping : List[callable] = None,
+ name : str = "u", filename : str = "out",
time : float = 0., what : strLst = 'all',
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.
+ warping(optional): Domain warping functions.
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 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 warping is not None:
+ fen.ALE.move(self.V.mesh(),
+ interp_project(warping[0], self.V.mesh()))
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)
+ if warping is not None:
+ fen.ALE.move(self.V.mesh(),
+ interp_project(warping[1], self.V.mesh()))
return filePW
def outParaviewTimeDomain(self, u:Np1D, omega:float,
+ warping : List[callable] = None,
timeFinal : float = None,
periodResolution : int = 20, name : str = "u",
filename : str = "out",
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.
+ warping(optional): Domain warping functions.
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))
omega = np.abs(omega)
t = 0.
dt = 2. * np.pi / omega / periodResolution
if timeFinal is None: timeFinal = 2. * np.pi / omega - dt
+ if warping is not None:
+ fen.ALE.move(self.V.mesh(),
+ interp_project(warping[0], self.V.mesh()))
for j in range(int(np.ceil(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
+ if warping is not None:
+ fen.ALE.move(self.V.mesh(),
+ interp_project(warping[1], self.V.mesh()))
return filePW
-
diff --git a/rrompy/hfengines/base/vector_problem_engine_base.py b/rrompy/hfengines/base/vector_problem_engine_base.py
index 8fba867..92c55da 100644
--- a/rrompy/hfengines/base/vector_problem_engine_base.py
+++ b/rrompy/hfengines/base/vector_problem_engine_base.py
@@ -1,197 +1,211 @@
# 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.types import Np1D, List, strLst
from rrompy.utilities.base import purgeList, getNewFilename
+from rrompy.solver.fenics import fenplot
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.
+ energyNormDualMatrix: Scipy sparse matrix representing inner product
+ over V'.
+ dualityMatrix: Scipy sparse matrix representing duality V-V'.
+ energyNormPartialDualMatrix: Scipy sparse matrix representing dual
+ inner product between Riesz representers V-V.
liftedDirichletDatum: Dofs of Dirichlet datum lifting.
mu0BC: Mu value of last Dirichlet datum lifting.
degree_threshold: Threshold for ufl expression interpolation degree.
"""
def __init__(self, degree_threshold : int = np.inf, verbosity : int = 10,
timestamp : bool = True):
super().__init__(degree_threshold = degree_threshold,
verbosity = verbosity, timestamp = timestamp)
self.V = fen.VectorFunctionSpace(fen.UnitSquareMesh(10, 10), "P", 1)
self.npar = 0
- def plot(self, u:Np1D, name : str = "u", save : str = None,
- what : strLst = 'all', saveFormat : str = "eps",
- saveDPI : int = 100, show : bool = True, **figspecs):
+ def plot(self, u:Np1D, warping : List[callable] = None, name : str = "u",
+ save : str = None, what : strLst = 'all',
+ saveFormat : str = "eps", saveDPI : int = 100, show : bool = True,
+ **figspecs):
"""
Do some nice plots of the complex-valued function with given dofs.
Args:
u: numpy complex array with function dofs.
+ warping(optional): Domain warping functions.
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.
show(optional): Whether to show figure. Defaults to True.
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))
+ p = fenplot(uAb, warping = warping,
+ 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))
+ p = fenplot(uPh, warping = warping,
+ 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))
+ p = fenplot(uRe, warping = warping,
+ 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))
+ p = fenplot(uIm, warping = warping,
+ 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)
if show:
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")
+ p = fenplot(uVRe, warping = warping,
+ 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")
+ p = fenplot(uVIm, warping = warping,
+ 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)
if show:
plt.show()
plt.close()
except:
pass
- def plotmesh(self, name : str = "Mesh", save : str = None,
- saveFormat : str = "eps", saveDPI : int = 100,
- show : bool = True, **figspecs):
+ def plotmesh(self, warping : List[callable] = None, name : str = "Mesh",
+ save : str = None, saveFormat : str = "eps",
+ saveDPI : int = 100, show : bool = True, **figspecs):
"""
Do a nice plot of the mesh.
Args:
u: numpy complex array with function dofs.
+ warping(optional): Domain warping functions.
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.
show(optional): Whether to show figure. Defaults to True.
figspecs(optional key args): Optional arguments for matplotlib
figure creation.
"""
plt.figure(**figspecs)
- fen.plot(self.V.mesh())
+ fenplot(self.V.mesh(), warping = warping)
if save is not None:
save = save.strip()
plt.savefig(getNewFilename("{}_msh_".format(save), saveFormat),
format = saveFormat, dpi = saveDPI)
if show:
plt.show()
plt.close()
diff --git a/rrompy/hfengines/linear_problem/__init__.py b/rrompy/hfengines/linear_problem/__init__.py
index e5a035c..3f7fb0b 100644
--- a/rrompy/hfengines/linear_problem/__init__.py
+++ b/rrompy/hfengines/linear_problem/__init__.py
@@ -1,47 +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 .laplace_base_problem_engine import LaplaceBaseProblemEngine
from .helmholtz_problem_engine import HelmholtzProblemEngine
from .scattering_problem_engine import ScatteringProblemEngine
from .helmholtz_box_scattering_problem_engine import \
HelmholtzBoxScatteringProblemEngine
from .helmholtz_cavity_scattering_problem_engine import \
HelmholtzCavityScatteringProblemEngine
from .helmholtz_square_bubble_problem_engine import \
HelmholtzSquareBubbleProblemEngine
from .helmholtz_square_bubble_domain_problem_engine import \
HelmholtzSquareBubbleDomainProblemEngine
from .helmholtz_square_transmission_problem_engine import \
HelmholtzSquareTransmissionProblemEngine
from .laplace_disk_gaussian import LaplaceDiskGaussian
+from .membrane_fracture_engine_nodomain import MembraneFractureEngineNoDomain
__all__ = [
'LaplaceBaseProblemEngine',
'HelmholtzProblemEngine',
'ScatteringProblemEngine',
'HelmholtzBoxScatteringProblemEngine',
'HelmholtzCavityScatteringProblemEngine',
'HelmholtzSquareBubbleProblemEngine',
'HelmholtzSquareBubbleDomainProblemEngine',
'HelmholtzSquareTransmissionProblemEngine',
- 'LaplaceDiskGaussian'
+ 'LaplaceDiskGaussian',
+ 'MembraneFractureEngineNoDomain'
]
diff --git a/rrompy/hfengines/linear_problem/bidimensional/__init__.py b/rrompy/hfengines/linear_problem/bidimensional/__init__.py
index 8e2d377..54ca3ff 100644
--- a/rrompy/hfengines/linear_problem/bidimensional/__init__.py
+++ b/rrompy/hfengines/linear_problem/bidimensional/__init__.py
@@ -1,32 +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 .cookie_engine_single import CookieEngineSingle
+from .membrane_fracture_engine import MembraneFractureEngine
from .laplace_disk_gaussian_2 import LaplaceDiskGaussian2
from .helmholtz_square_simplified_domain_problem_engine import \
HelmholtzSquareSimplifiedDomainProblemEngine
from .helmholtz_square_domain_problem_engine import \
HelmholtzSquareDomainProblemEngine
+from .synthetic_bivariate_engine import SyntheticBivariateEngine
__all__ = [
+ 'CookieEngineSingle',
+ 'MembraneFractureEngine',
'LaplaceDiskGaussian2',
'HelmholtzSquareSimplifiedDomainProblemEngine',
- 'HelmholtzSquareDomainProblemEngine'
+ 'HelmholtzSquareDomainProblemEngine',
+ 'SyntheticBivariateEngine'
]
diff --git a/rrompy/hfengines/linear_problem/bidimensional/helmholtz_square_simplified_domain_problem_engine.py b/rrompy/hfengines/linear_problem/bidimensional/cookie_engine_single.py
similarity index 57%
copy from rrompy/hfengines/linear_problem/bidimensional/helmholtz_square_simplified_domain_problem_engine.py
copy to rrompy/hfengines/linear_problem/bidimensional/cookie_engine_single.py
index d310906..49e4acd 100644
--- a/rrompy/hfengines/linear_problem/bidimensional/helmholtz_square_simplified_domain_problem_engine.py
+++ b/rrompy/hfengines/linear_problem/bidimensional/cookie_engine_single.py
@@ -1,129 +1,137 @@
# Copyright (C) 2018 by the RROMPy authors
#
# This file is part of RROMPy.
#
# RROMPy is free software: you can redistribute it and/or modify
# it under the terms of the GNU Lesser General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# RROMPy is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public License
# along with RROMPy. If not, see .
#
import numpy as np
-import scipy.sparse as scsp
import fenics as fen
+import ufl
from rrompy.utilities.base.types import ScOp, List, paramVal
-from rrompy.solver.fenics import fenZERO
+from rrompy.solver.fenics import fenONE, fenZERO
from rrompy.hfengines.linear_problem.helmholtz_problem_engine import (
HelmholtzProblemEngine)
from rrompy.utilities.base import verbosityDepth
from rrompy.utilities.poly_fitting.polynomial import (
hashDerivativeToIdx as hashD)
+from rrompy.solver.fenics import fenics2Sparse
-__all__ = ['HelmholtzSquareSimplifiedDomainProblemEngine']
+__all__ = ['CookieEngineSingle']
-class HelmholtzSquareSimplifiedDomainProblemEngine(HelmholtzProblemEngine):
- """
- Solver for square Helmholtz problems with parametric laplacian.
- - \dxx u - mu_2**2 \dyy u - mu_1**2 * u = f in \Omega_mu = [0,\pi]^2
- u = 0 on \partial\Omega
- """
+class CookieEngineSingle(HelmholtzProblemEngine):
- def __init__(self, kappa:float, theta:float, n:int,
+ def __init__(self, kappa:float, theta:float, n:int, R : int = 1.,
+ L : int = 2., nX : int = 1, nY : int = 1,
mu0 : paramVal = [12. ** .5, 1.],
degree_threshold : int = np.inf, verbosity : int = 10,
timestamp : bool = True):
super().__init__(mu0 = mu0, degree_threshold = degree_threshold,
verbosity = verbosity, timestamp = timestamp)
- self.nAs = 3
+ self.nAs = 5
self.npar = 2
self.rescalingExp = [2., 2.]
- pi = np.pi
- mesh = fen.RectangleMesh(fen.Point(0, 0), fen.Point(pi, pi),
- 3 * n, 3 * n)
+
+ mesh = fen.RectangleMesh(fen.Point(0., 0.), fen.Point(L * nX, L * nY),
+ n * nX, n * nY)
self.V = fen.FunctionSpace(mesh, "P", 1)
- c, s = np.cos(theta), np.sin(theta)
x, y = fen.SpatialCoordinate(mesh)[:]
- C = 16. / pi ** 4.
+ cxs = np.linspace(0, L * nX, 2 * nX + 1)[1::2]
+ cys = np.linspace(0, L * nY, 2 * nY + 1)[1::2]
+ self.cookieIn = fenZERO
+ for cx in cxs:
+ for cy in cys:
+ self.cookieIn += ufl.conditional(
+ ufl.le((x-cx)**2. + (y-cy)**2., R**2.),
+ fenONE, fenZERO)
+ self.cookieOut = fenONE - self.cookieIn
+ c, s = np.cos(theta), np.sin(theta)
self.forcingTerm = [fen.cos(kappa * (c * x + s * y)),
fen.sin(kappa * (c * x + s * y))]
def A(self, mu : paramVal = [], der : List[int] = 0) -> ScOp:
"""Assemble (derivative of) operator of linear system."""
mu = self.checkParameter(mu)
if not hasattr(der, "__len__"): der = [der] * self.npar
derI = hashD(der)
self.autoSetDS()
if derI <= 0 and self.As[0] is None:
if self.verbosity >= 20:
verbosityDepth("INIT", "Assembling operator term A0.",
timestamp = self.timestamp)
DirichletBC0 = fen.DirichletBC(self.V, fenZERO,
self.DirichletBoundary)
- a0Re = fen.dot(self.u.dx(0), self.v.dx(0)) * 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)
+ 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))
+ self.As[0] = (fenics2Sparse(a0Re, parsRe, DirichletBC0, 1)
+ + 1.j * fenics2Sparse(a0Im, parsIm, DirichletBC0, 0))
if self.verbosity >= 20:
verbosityDepth("DEL", "Done assembling operator term.",
timestamp = self.timestamp)
if derI <= 1 and self.As[1] is None:
if self.verbosity >= 20:
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))
+ a1Re = - n2Re * self.cookieOut * fen.dot(self.u, self.v) * fen.dx
+ a1Im = - n2Im * self.cookieOut * fen.dot(self.u, self.v) * fen.dx
+ self.As[1] = (fenics2Sparse(a1Re, parsRe, DirichletBC0, 0)
+ + 1.j * fenics2Sparse(a1Im, parsIm, DirichletBC0, 0))
if self.verbosity >= 20:
verbosityDepth("DEL", "Done assembling operator term.",
timestamp = self.timestamp)
if derI <= 2 and self.As[2] is None:
+ self.As[2] = self.checkAInBounds(-1)
+ if derI <= 3 and self.As[3] is None:
+ self.As[3] = self.checkAInBounds(-1)
+ if derI <= 4 and self.As[4] is None:
if self.verbosity >= 20:
- verbosityDepth("INIT", "Assembling operator term A2.",
+ verbosityDepth("INIT", "Assembling operator term A4.",
timestamp = self.timestamp)
DirichletBC0 = fen.DirichletBC(self.V, fenZERO,
self.DirichletBoundary)
- a2Re = fen.dot(self.u.dx(1), self.v.dx(1)) * fen.dx
- A2Re = fen.assemble(a2Re)
- DirichletBC0.zero(A2Re)
- A2ReMat = fen.as_backend_type(A2Re).mat()
- A2Rer, A2Rec, A2Rev = A2ReMat.getValuesCSR()
- self.As[2] = scsp.csr_matrix((A2Rev, A2Rec, A2Rer),
- shape = A2ReMat.size,
- dtype = np.complex)
+ 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"]))
+ a4Re = - n2Re * self.cookieIn * fen.dot(self.u, self.v) * fen.dx
+ a4Im = - n2Im * self.cookieIn * fen.dot(self.u, self.v) * fen.dx
+ self.As[4] = (fenics2Sparse(a4Re, parsRe, DirichletBC0, 0)
+ + 1.j * fenics2Sparse(a4Im, parsIm, DirichletBC0, 0))
if self.verbosity >= 20:
verbosityDepth("DEL", "Done assembling operator term.",
timestamp = self.timestamp)
return self._assembleA(mu, der, derI)
diff --git a/rrompy/hfengines/linear_problem/bidimensional/helmholtz_square_domain_problem_engine.py b/rrompy/hfengines/linear_problem/bidimensional/helmholtz_square_domain_problem_engine.py
index 04a296b..cf05c29 100644
--- a/rrompy/hfengines/linear_problem/bidimensional/helmholtz_square_domain_problem_engine.py
+++ b/rrompy/hfengines/linear_problem/bidimensional/helmholtz_square_domain_problem_engine.py
@@ -1,229 +1,203 @@
# 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
-from scipy.special import factorial as fact
import fenics as fen
from rrompy.utilities.base.types import (ScOp, List, Tuple, paramVal, Np1D,
FenExpr)
from rrompy.solver.fenics import fenZERO, fenONE
from rrompy.hfengines.linear_problem.helmholtz_problem_engine import (
HelmholtzProblemEngine)
from rrompy.utilities.base import verbosityDepth
from rrompy.utilities.poly_fitting.polynomial import (
hashDerivativeToIdx as hashD, hashIdxToDerivative as hashI)
+from rrompy.solver.fenics import fenics2Sparse, fenics2Vector
__all__ = ['HelmholtzSquareDomainProblemEngine']
class HelmholtzSquareDomainProblemEngine(HelmholtzProblemEngine):
"""
Solver for square Helmholtz problems with parametric laplacian.
- \dxx u - mu_2**2 \dyy u - mu_1**2 * u = f(mu_2) in \Omega = [0,\pi]^2
u = 0 on \partial\Omega
"""
def __init__(self, kappa:float, theta:float, n:int,
mu0 : paramVal = [12. ** .5, 1.],
degree_threshold : int = np.inf, verbosity : int = 10,
timestamp : bool = True):
super().__init__(mu0 = mu0, degree_threshold = degree_threshold,
verbosity = verbosity, timestamp = timestamp)
self.nAs, self.nbs = 6, 11 * 12 // 2
self.npar = 2
self.rescalingExp = [2., 1.]
pi = np.pi
mesh = fen.RectangleMesh(fen.Point(0, 0), fen.Point(pi, pi),
3 * n, 3 * n)
self.V = fen.FunctionSpace(mesh, "P", 1)
c, s = np.cos(theta), np.sin(theta)
x, y = fen.SpatialCoordinate(mesh)[:]
self.forcingTerm = [fen.cos(kappa * (c * x + s / self.mu0(0, 1) * y)),
fen.sin(kappa * (c * x + s / self.mu0(0, 1) * y))]
self.forcingTermDer = kappa * s * y
def getExtraFactorB(self, mu : paramVal = [],
derI : int = 0) -> Tuple[FenExpr, FenExpr]:
"""Compute extra expression in RHS."""
mu = self.checkParameter(mu)
def getPowMinusj(x, power):
powR = x ** power
powI = fenZERO
if power % 2 == 1:
powR, powI = powI, powR
if power % 4 > 1:
powR, powI = - powR, - powI
return powR, powI
if self.verbosity >= 25:
verbosityDepth("INIT", ("Assembling auxiliary expression for "
"forcing term derivative."),
timestamp = self.timestamp)
if derI == 0: return fenONE, fenZERO
coeffs = np.zeros(derI + 1)
coeffs[1] = - 2.
for j in range(2, derI + 1):
coeffs[1 :] = (-2. / j * coeffs[1 :]
- (3 - (1 + 2 * np.arange(derI)) / j) * coeffs[: -1])
for j in range(derI):
powR, powI = getPowMinusj(self.forcingTermDer, derI - j)
mupBase = coeffs[j + 1] * mu(0, 1) ** (- 3 * derI + 2 * j)
mupR, mupI = np.real(mupBase), np.imag(mupBase)
if j == 0:
exprR = mupR * powR - mupI * powI
exprI = mupI * powR + mupR * powI
else:
exprR += mupR * powR - mupI * powI
exprI += mupI * powR + mupR * powI
if self.verbosity >= 25:
verbosityDepth("DEL", "Done assembling auxiliary expression.",
timestamp = self.timestamp)
return exprR, exprI
def A(self, mu : paramVal = [], der : List[int] = 0) -> ScOp:
"""Assemble (derivative of) operator of linear system."""
mu = self.checkParameter(mu)
if not hasattr(der, "__len__"): der = [der] * self.npar
derI = hashD(der)
self.autoSetDS()
+ for j in range(2, 5):
+ if derI <= j and self.As[j] is None:
+ self.As[j] = self.checkAInBounds(-1)
if derI <= 0 and self.As[0] is None:
if self.verbosity >= 20:
verbosityDepth("INIT", "Assembling operator term A0.",
timestamp = self.timestamp)
DirichletBC0 = fen.DirichletBC(self.V, fenZERO,
self.DirichletBoundary)
a0Re = fen.dot(self.u.dx(0), self.v.dx(0)) * 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)
+ self.As[0] = fenics2Sparse(a0Re, {}, DirichletBC0, 1)
if self.verbosity >= 20:
verbosityDepth("DEL", "Done assembling operator term.",
timestamp = self.timestamp)
if derI <= 1 and self.As[1] is None:
if self.verbosity >= 20:
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))
+ self.As[1] = (fenics2Sparse(a1Re, parsRe, DirichletBC0, 0)
+ + 1.j * fenics2Sparse(a1Im, parsIm, DirichletBC0, 0))
if self.verbosity >= 20:
verbosityDepth("DEL", "Done assembling operator term.",
timestamp = self.timestamp)
- if derI <= 2 and self.As[2] is None: self.As[2] = 0.
- if derI <= 3 and self.As[3] is None: self.As[3] = 0.
- if derI <= 4 and self.As[4] is None: self.As[4] = 0.
if derI <= 5 and self.As[5] is None:
if self.verbosity >= 20:
verbosityDepth("INIT", "Assembling operator term A5.",
timestamp = self.timestamp)
DirichletBC0 = fen.DirichletBC(self.V, fenZERO,
self.DirichletBoundary)
a5Re = fen.dot(self.u.dx(1), self.v.dx(1)) * fen.dx
- A5Re = fen.assemble(a5Re)
- DirichletBC0.zero(A5Re)
- A5ReMat = fen.as_backend_type(A5Re).mat()
- A5Rer, A5Rec, A5Rev = A5ReMat.getValuesCSR()
- self.As[5] = scsp.csr_matrix((A5Rev, A5Rec, A5Rer),
- shape = A5ReMat.size,
- dtype = np.complex)
+ self.As[5] = fenics2Sparse(a5Re, {}, DirichletBC0, 0)
if self.verbosity >= 20:
verbosityDepth("DEL", "Done assembling operator term.",
timestamp = self.timestamp)
return self._assembleA(mu, der, derI)
def b(self, mu : paramVal = [], der : List[int] = 0,
homogeneized : bool = False) -> Np1D:
"""Assemble (derivative of) RHS of linear system."""
mu = self.checkParameter(mu)
if not hasattr(der, "__len__"): der = [der] * self.npar
derI = hashD(der)
nbsTot = self.nbsH if homogeneized else self.nbs
bs = self.bsH if homogeneized else self.bs
if homogeneized and self.mu0 != self.mu0BC:
self.liftDirichletData(self.mu0)
for j in range(derI, nbsTot):
derH = hashI(j, self.npar)
if bs[j] is None:
if np.sum(derH) != derH[-1]:
if homogeneized:
- self.bsH[j] = 0.
+ self.bsH[j] = self.checkbInBounds(-1)
else:
- self.bs[j] = 0.
+ self.bs[j] = self.checkbInBounds(-1)
continue
if self.verbosity >= 20:
verbosityDepth("INIT", ("Assembling forcing term "
"b{}.").format(j),
timestamp = self.timestamp)
if j == 0:
u0Re, u0Im = self.DirichletDatum
else:
u0Re, u0Im = fenZERO, fenZERO
if j < self.nbs:
fRe, fIm = self.forcingTerm
cRe, cIm = self.getExtraFactorB(self.mu0, derH[-1])
cfRe, cfIm = cRe * fRe - cIm * fIm, cRe * fIm + cIm * fRe
else:
cfRe, cfIm = fenZERO, fenZERO
parsRe = self.iterReduceQuadratureDegree(zip([cfRe],
["forcingTermDer{}Real".format(j)]))
parsIm = self.iterReduceQuadratureDegree(zip([cfIm],
["forcingTermDer{}Imag".format(j)]))
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)
DBCR = fen.DirichletBC(self.V, u0Re, self.DirichletBoundary)
DBCI = fen.DirichletBC(self.V, u0Im, self.DirichletBoundary)
- DBCR.apply(b0Re)
- DBCI.apply(b0Im)
- b = np.array(b0Re[:] + 1.j * b0Im[:], dtype = np.complex)
+ b = (fenics2Vector(L0Re, parsRe, DBCR, 1)
+ + 1.j * fenics2Vector(L0Im, parsIm, DBCI, 1))
if homogeneized:
Ader = self.A(self.mu0, derH)
b -= Ader.dot(self.liftedDirichletDatum)
if homogeneized:
self.bsH[j] = b
else:
self.bs[j] = b
if self.verbosity >= 20:
verbosityDepth("DEL", "Done assembling forcing term.",
timestamp = self.timestamp)
return self._assembleb(mu, der, derI, homogeneized, self.mu0)
diff --git a/rrompy/hfengines/linear_problem/bidimensional/helmholtz_square_simplified_domain_problem_engine.py b/rrompy/hfengines/linear_problem/bidimensional/helmholtz_square_simplified_domain_problem_engine.py
index d310906..68a0f00 100644
--- a/rrompy/hfengines/linear_problem/bidimensional/helmholtz_square_simplified_domain_problem_engine.py
+++ b/rrompy/hfengines/linear_problem/bidimensional/helmholtz_square_simplified_domain_problem_engine.py
@@ -1,129 +1,106 @@
# 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 ScOp, List, paramVal
from rrompy.solver.fenics import fenZERO
from rrompy.hfengines.linear_problem.helmholtz_problem_engine import (
HelmholtzProblemEngine)
from rrompy.utilities.base import verbosityDepth
from rrompy.utilities.poly_fitting.polynomial import (
hashDerivativeToIdx as hashD)
+from rrompy.solver.fenics import fenics2Sparse
__all__ = ['HelmholtzSquareSimplifiedDomainProblemEngine']
class HelmholtzSquareSimplifiedDomainProblemEngine(HelmholtzProblemEngine):
"""
Solver for square Helmholtz problems with parametric laplacian.
- \dxx u - mu_2**2 \dyy u - mu_1**2 * u = f in \Omega_mu = [0,\pi]^2
u = 0 on \partial\Omega
"""
def __init__(self, kappa:float, theta:float, n:int,
mu0 : paramVal = [12. ** .5, 1.],
degree_threshold : int = np.inf, verbosity : int = 10,
timestamp : bool = True):
super().__init__(mu0 = mu0, degree_threshold = degree_threshold,
verbosity = verbosity, timestamp = timestamp)
self.nAs = 3
self.npar = 2
self.rescalingExp = [2., 2.]
pi = np.pi
mesh = fen.RectangleMesh(fen.Point(0, 0), fen.Point(pi, pi),
3 * n, 3 * n)
self.V = fen.FunctionSpace(mesh, "P", 1)
c, s = np.cos(theta), np.sin(theta)
x, y = fen.SpatialCoordinate(mesh)[:]
- C = 16. / pi ** 4.
self.forcingTerm = [fen.cos(kappa * (c * x + s * y)),
fen.sin(kappa * (c * x + s * y))]
def A(self, mu : paramVal = [], der : List[int] = 0) -> ScOp:
"""Assemble (derivative of) operator of linear system."""
mu = self.checkParameter(mu)
if not hasattr(der, "__len__"): der = [der] * self.npar
derI = hashD(der)
self.autoSetDS()
if derI <= 0 and self.As[0] is None:
if self.verbosity >= 20:
verbosityDepth("INIT", "Assembling operator term A0.",
timestamp = self.timestamp)
DirichletBC0 = fen.DirichletBC(self.V, fenZERO,
self.DirichletBoundary)
a0Re = fen.dot(self.u.dx(0), self.v.dx(0)) * 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)
+ self.As[0] = fenics2Sparse(a0Re, {}, DirichletBC0, 1)
if self.verbosity >= 20:
verbosityDepth("DEL", "Done assembling operator term.",
timestamp = self.timestamp)
if derI <= 1 and self.As[1] is None:
if self.verbosity >= 20:
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))
+ self.As[1] = (fenics2Sparse(a1Re, parsRe, DirichletBC0, 0)
+ + 1.j * fenics2Sparse(a1Im, parsIm, DirichletBC0, 0))
if self.verbosity >= 20:
verbosityDepth("DEL", "Done assembling operator term.",
timestamp = self.timestamp)
if derI <= 2 and self.As[2] is None:
if self.verbosity >= 20:
verbosityDepth("INIT", "Assembling operator term A2.",
timestamp = self.timestamp)
DirichletBC0 = fen.DirichletBC(self.V, fenZERO,
self.DirichletBoundary)
a2Re = fen.dot(self.u.dx(1), self.v.dx(1)) * fen.dx
- A2Re = fen.assemble(a2Re)
- DirichletBC0.zero(A2Re)
- A2ReMat = fen.as_backend_type(A2Re).mat()
- A2Rer, A2Rec, A2Rev = A2ReMat.getValuesCSR()
- self.As[2] = scsp.csr_matrix((A2Rev, A2Rec, A2Rer),
- shape = A2ReMat.size,
- dtype = np.complex)
+ self.As[2] = fenics2Sparse(a2Re, {}, DirichletBC0, 0)
if self.verbosity >= 20:
verbosityDepth("DEL", "Done assembling operator term.",
timestamp = self.timestamp)
return self._assembleA(mu, der, derI)
diff --git a/rrompy/hfengines/linear_problem/bidimensional/laplace_disk_gaussian_2.py b/rrompy/hfengines/linear_problem/bidimensional/laplace_disk_gaussian_2.py
index db0ed1d..cd3ec26 100644
--- a/rrompy/hfengines/linear_problem/bidimensional/laplace_disk_gaussian_2.py
+++ b/rrompy/hfengines/linear_problem/bidimensional/laplace_disk_gaussian_2.py
@@ -1,125 +1,123 @@
# Copyright (C) 2018 by the RROMPy authors
#
# This file is part of RROMPy.
#
# RROMPy is free software: you can redistribute it and/or modify
# it under the terms of the GNU Lesser General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# RROMPy is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public License
# along with RROMPy. If not, see .
#
import numpy as np
import fenics as fen
from rrompy.utilities.base.types import Np1D, Tuple, List, FenExpr, paramVal
from rrompy.hfengines.linear_problem.laplace_disk_gaussian import (
LaplaceDiskGaussian)
from rrompy.solver.fenics import fenZERO, fenONE
from rrompy.utilities.base import verbosityDepth
from rrompy.utilities.poly_fitting.polynomial import (
hashDerivativeToIdx as hashD, hashIdxToDerivative as hashI)
from rrompy.utilities.exception_manager import RROMPyException
+from rrompy.solver.fenics import fenics2Vector
__all__ = ['LaplaceDiskGaussian2']
class LaplaceDiskGaussian2(LaplaceDiskGaussian):
"""
Solver for disk Laplace problems with parametric forcing term center.
- \Delta u = C exp(-.5 * ||\cdot - (mu1, mu2)||^2) in \Omega = B(0, 5)
u = 0 on \partial\Omega.
"""
def __init__(self, n:int, mu0 : paramVal = [0., 0.],
degree_threshold : int = np.inf, verbosity : int = 10,
timestamp : bool = True):
super().__init__(n = n, mu0 = mu0, degree_threshold = degree_threshold,
verbosity = verbosity, timestamp = timestamp)
self.nbs = 1
self.npar = 2
def getForcingTerm(self, mu : paramVal = []) -> Tuple[FenExpr, FenExpr]:
"""Compute forcing term."""
mu = self.checkParameter(mu)
if mu != self.forcingTermMu:
if self.verbosity >= 25:
verbosityDepth("INIT", ("Assembling base expression for "
"forcing term."),
timestamp = self.timestamp)
x, y = fen.SpatialCoordinate(self.V.mesh())[:]
C = np.exp(-.5 * (mu(0, 0) ** 2. + mu(0, 1) ** 2.))
CR, CI = np.real(C), np.imag(C)
f0 = (2 * np.pi) ** -.5 * fen.exp(-.5 * (x ** 2. + y ** 2.))
muxR, muxI = np.real(mu(0, 0)), np.imag(mu(0, 0))
muyR, muyI = np.real(mu(0, 1)), np.imag(mu(0, 1))
f1R = fen.exp(muxR * x + muyR * y) * fen.cos(muxI * x + muyI * y)
f1I = fen.exp(muxR * x + muyR * y) * fen.sin(muxI * x + muyI * y)
self.forcingTerm = [f0 * (CR * f1R - CI * f1I),
f0 * (CR * f1I + CI * f1R)]
self.forcingTermMu = mu
if self.verbosity >= 25:
verbosityDepth("DEL", "Done assembling base expression.",
timestamp = self.timestamp)
return self.forcingTerm
def computebsFactors(self):
pass
def getExtraFactorB(self, mu : paramVal = [],
derI : int = 0) -> Tuple[FenExpr, FenExpr]:
if derI == 0: return [fenONE, fenZERO]
raise RROMPyException("Not implemented.")
def b(self, mu : paramVal = [], der : List[int] = 0,
homogeneized : bool = False) -> Np1D:
"""Assemble (derivative of) RHS of linear system."""
mu = self.checkParameter(mu)
if not hasattr(der, "__len__"): der = [der] * self.npar
derI = hashD(der)
nbsTot = self.nbsH if homogeneized else self.nbs
bs = self.bsH if homogeneized else self.bs
if homogeneized and self.mu0 != self.mu0BC:
self.liftDirichletData(self.mu0)
for j in range(derI, nbsTot):
- if True or bs[j] is None:
+ if bs[j] is None:
if self.verbosity >= 20:
verbosityDepth("INIT", ("Assembling forcing term "
"b{}.").format(j),
timestamp = self.timestamp)
if j < self.nbs:
fRe, fIm = self.getForcingTerm(mu)
cRe, cIm = self.getExtraFactorB(mu, j)
cfRe, cfIm = cRe * fRe - cIm * fIm, cRe * fIm + cIm * fRe
else:
cfRe, cfIm = fenZERO, fenZERO
parsRe = self.iterReduceQuadratureDegree(zip([cfRe],
["forcingTermDer{}Real".format(j)]))
parsIm = self.iterReduceQuadratureDegree(zip([cfIm],
["forcingTermDer{}Imag".format(j)]))
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)
DirichletBC0 = fen.DirichletBC(self.V, fenZERO,
self.DirichletBoundary)
- DirichletBC0.apply(b0Re)
- DirichletBC0.apply(b0Im)
- b = np.array(b0Re[:] + 1.j * b0Im[:], dtype = np.complex)
+ b = (fenics2Vector(L0Re, parsRe, DirichletBC0, 1)
+ + 1.j * fenics2Vector(L0Im, parsIm, DirichletBC0, 1))
if homogeneized:
Ader = self.A(self.mu0, hashI(j, self.npar))
b -= Ader.dot(self.liftedDirichletDatum)
if homogeneized:
self.bsH[j] = b
else:
self.bs[j] = b
if self.verbosity >= 20:
verbosityDepth("DEL", "Done assembling forcing term.",
timestamp = self.timestamp)
return self._assembleb(mu, der, derI, homogeneized, self.mu0)
diff --git a/rrompy/hfengines/linear_problem/bidimensional/membrane_fracture_engine.py b/rrompy/hfengines/linear_problem/bidimensional/membrane_fracture_engine.py
new file mode 100644
index 0000000..98c1119
--- /dev/null
+++ b/rrompy/hfengines/linear_problem/bidimensional/membrane_fracture_engine.py
@@ -0,0 +1,188 @@
+# 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 mshr, ufl
+from rrompy.utilities.base.types import ScOp, List, paramVal
+from rrompy.solver.fenics import fenZERO, fenONE
+from rrompy.hfengines.linear_problem.helmholtz_problem_engine import (
+ HelmholtzProblemEngine)
+from rrompy.utilities.base import verbosityDepth
+from rrompy.utilities.poly_fitting.polynomial import (
+ hashDerivativeToIdx as hashD)
+from rrompy.solver.fenics import fenics2Sparse
+
+__all__ = ['MembraneFractureEngine']
+
+class MembraneFractureEngine(HelmholtzProblemEngine):
+
+ def __init__(self, mu0 : paramVal = [20. ** .5, .6], H : float = 1.,
+ L : float = .75, delta : float = .05, n : int = 50,
+ degree_threshold : int = np.inf, verbosity : int = 10,
+ timestamp : bool = True):
+ super().__init__(mu0 = mu0, degree_threshold = degree_threshold,
+ verbosity = verbosity, timestamp = timestamp)
+ self.nAs = 20
+ self.npar = 2
+ self.H = H
+ self.rescalingExp = [2., 1.]
+ domain = (mshr.Rectangle(fen.Point(0., - H / 2.),
+ fen.Point(2. * L + delta, H / 2.))
+ - mshr.Rectangle(fen.Point(L, 0.),
+ fen.Point(L + delta, H / 2.)))
+ mesh = mshr.generate_mesh(domain, n)
+ self.V = fen.FunctionSpace(mesh, "P", 1)
+ self.NeumannBoundary = lambda x, on_b: (on_b and x[1] >= - H / 4.
+ and x[0] >= L
+ and x[0] <= L + delta)
+ self.DirichletBoundary = "REST"
+
+ x, y = fen.SpatialCoordinate(mesh)[:]
+ self._belowIndicator = ufl.conditional(ufl.le(y, 0.), fenONE, fenZERO)
+ self._aboveIndicator = fenONE - self._belowIndicator
+ self.DirichletDatum = [fen.exp(- 10. * (H / 2. + y) / H
+ - .5 * ((x - .6 * L) / (.1 * L)) ** 2.
+ ) * self._belowIndicator, fenZERO]
+
+ def A(self, mu : paramVal = [], der : List[int] = 0) -> ScOp:
+ """Assemble (derivative of) operator of linear system."""
+ mu = self.checkParameter(mu)
+ if not hasattr(der, "__len__"): der = [der] * self.npar
+ derI = hashD(der)
+ self.autoSetDS()
+ for j in [1, 3, 4, 6, 7, 10, 11, 12, 15, 16, 17, 18]:
+ if derI <= j and self.As[j] is None:
+ self.As[j] = self.checkAInBounds(-1)
+ if derI <= 0 and self.As[0] is None:
+ if self.verbosity >= 20:
+ verbosityDepth("INIT", "Assembling operator term A0.",
+ timestamp = self.timestamp)
+ DirichletBC0 = fen.DirichletBC(self.V, fenZERO,
+ self.DirichletBoundary)
+ a0Re = (self.H ** 4 / 4. * self._aboveIndicator
+ * fen.dot(self.u.dx(1), self.v.dx(1)) * fen.dx)
+ self.As[0] = fenics2Sparse(a0Re, {}, DirichletBC0, 1)
+ if self.verbosity >= 20:
+ verbosityDepth("DEL", "Done assembling operator term.",
+ timestamp = self.timestamp)
+ if derI <= 2 and self.As[2] is None:
+ if self.verbosity >= 20:
+ verbosityDepth("INIT", "Assembling operator term A2.",
+ timestamp = self.timestamp)
+ DirichletBC0 = fen.DirichletBC(self.V, fenZERO,
+ self.DirichletBoundary)
+ a2Re = (- self.H ** 3 / 2. * self._aboveIndicator
+ * fen.dot(self.u.dx(1), self.v.dx(1)) * fen.dx)
+ self.As[2] = fenics2Sparse(a2Re, {}, DirichletBC0, 0)
+ if self.verbosity >= 20:
+ verbosityDepth("DEL", "Done assembling operator term.",
+ timestamp = self.timestamp)
+ if derI <= 5 and self.As[5] is None:
+ if self.verbosity >= 20:
+ verbosityDepth("INIT", "Assembling operator term A6.",
+ timestamp = self.timestamp)
+ DirichletBC0 = fen.DirichletBC(self.V, fenZERO,
+ self.DirichletBoundary)
+ a5Re = self.H ** 2 * (fen.dot(self.u.dx(0), self.v.dx(0))
+ + .25 * fen.dot(self.u.dx(1), self.v.dx(1))) * fen.dx
+ self.As[5] = fenics2Sparse(a5Re, {}, DirichletBC0, 0)
+ if self.verbosity >= 20:
+ verbosityDepth("DEL", "Done assembling operator term.",
+ timestamp = self.timestamp)
+ if derI <= 8 and self.As[8] is None:
+ if self.verbosity >= 20:
+ verbosityDepth("INIT", "Assembling operator term A8.",
+ 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"]))
+ a8Re = - self.H ** 2. * n2Re * fen.dot(self.u, self.v) * fen.dx
+ a8Im = - self.H ** 2. * n2Im * fen.dot(self.u, self.v) * fen.dx
+ self.As[8] = (fenics2Sparse(a8Re, parsRe, DirichletBC0, 0)
+ + 1.j * fenics2Sparse(a8Im, parsIm, DirichletBC0, 0))
+ if self.verbosity >= 20:
+ verbosityDepth("DEL", "Done assembling operator term.",
+ timestamp = self.timestamp)
+ if derI <= 9 and self.As[9] is None:
+ if self.verbosity >= 20:
+ verbosityDepth("INIT", "Assembling operator term A9.",
+ timestamp = self.timestamp)
+ DirichletBC0 = fen.DirichletBC(self.V, fenZERO,
+ self.DirichletBoundary)
+ a9Re = - 2. * self.H * fen.dot(self.u.dx(0), self.v.dx(0)) * fen.dx
+ self.As[9] = fenics2Sparse(a9Re, {}, DirichletBC0, 0)
+ if self.verbosity >= 20:
+ verbosityDepth("DEL", "Done assembling operator term.",
+ timestamp = self.timestamp)
+ if derI <= 13 and self.As[13] is None:
+ if self.verbosity >= 20:
+ verbosityDepth("INIT", "Assembling operator term A13.",
+ 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"]))
+ a13Re = 2. * self.H * n2Re * fen.dot(self.u, self.v) * fen.dx
+ a13Im = 2. * self.H * n2Im * fen.dot(self.u, self.v) * fen.dx
+ self.As[13] = (fenics2Sparse(a13Re, parsRe, DirichletBC0, 0)
+ + 1.j * fenics2Sparse(a13Im, parsIm, DirichletBC0, 0))
+ if self.verbosity >= 20:
+ verbosityDepth("DEL", "Done assembling operator term.",
+ timestamp = self.timestamp)
+ if derI <= 14 and self.As[14] is None:
+ if self.verbosity >= 20:
+ verbosityDepth("INIT", "Assembling operator term A14.",
+ timestamp = self.timestamp)
+ DirichletBC0 = fen.DirichletBC(self.V, fenZERO,
+ self.DirichletBoundary)
+ a14Re = fen.dot(self.u.dx(0), self.v.dx(0)) * fen.dx
+ self.As[14] = fenics2Sparse(a14Re, {}, DirichletBC0, 0)
+ if self.verbosity >= 20:
+ verbosityDepth("DEL", "Done assembling operator term.",
+ timestamp = self.timestamp)
+ if derI <= 19 and self.As[19] is None:
+ if self.verbosity >= 20:
+ verbosityDepth("INIT", "Assembling operator term A19.",
+ 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"]))
+ a19Re = - n2Re * fen.dot(self.u, self.v) * fen.dx
+ a19Im = - n2Im * fen.dot(self.u, self.v) * fen.dx
+ self.As[19] = (fenics2Sparse(a19Re, parsRe, DirichletBC0, 0)
+ + 1.j * fenics2Sparse(a19Im, parsIm, DirichletBC0, 0))
+ if self.verbosity >= 20:
+ verbosityDepth("DEL", "Done assembling operator term.",
+ timestamp = self.timestamp)
+ return self._assembleA(mu, der, derI)
+
diff --git a/rrompy/hfengines/linear_problem/bidimensional/helmholtz_square_simplified_domain_problem_engine.py b/rrompy/hfengines/linear_problem/bidimensional/synthetic_bivariate_engine.py
similarity index 62%
copy from rrompy/hfengines/linear_problem/bidimensional/helmholtz_square_simplified_domain_problem_engine.py
copy to rrompy/hfengines/linear_problem/bidimensional/synthetic_bivariate_engine.py
index d310906..2fb7ca5 100644
--- a/rrompy/hfengines/linear_problem/bidimensional/helmholtz_square_simplified_domain_problem_engine.py
+++ b/rrompy/hfengines/linear_problem/bidimensional/synthetic_bivariate_engine.py
@@ -1,129 +1,123 @@
# 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
+import ufl
from rrompy.utilities.base.types import ScOp, List, paramVal
-from rrompy.solver.fenics import fenZERO
+from rrompy.solver.fenics import fenONE, fenZERO
from rrompy.hfengines.linear_problem.helmholtz_problem_engine import (
HelmholtzProblemEngine)
from rrompy.utilities.base import verbosityDepth
from rrompy.utilities.poly_fitting.polynomial import (
hashDerivativeToIdx as hashD)
+from rrompy.solver.fenics import fenics2Sparse
-__all__ = ['HelmholtzSquareSimplifiedDomainProblemEngine']
+__all__ = ['SyntheticBivariateEngine']
-class HelmholtzSquareSimplifiedDomainProblemEngine(HelmholtzProblemEngine):
- """
- Solver for square Helmholtz problems with parametric laplacian.
- - \dxx u - mu_2**2 \dyy u - mu_1**2 * u = f in \Omega_mu = [0,\pi]^2
- u = 0 on \partial\Omega
- """
+class SyntheticBivariateEngine(HelmholtzProblemEngine):
- def __init__(self, kappa:float, theta:float, n:int,
- mu0 : paramVal = [12. ** .5, 1.],
+ def __init__(self, kappa:float, theta:float, n:int, L : int = 2.,
+ mu0 : paramVal = [12. ** .5, 15. ** .5],
degree_threshold : int = np.inf, verbosity : int = 10,
timestamp : bool = True):
super().__init__(mu0 = mu0, degree_threshold = degree_threshold,
verbosity = verbosity, timestamp = timestamp)
self.nAs = 3
self.npar = 2
self.rescalingExp = [2., 2.]
- pi = np.pi
- mesh = fen.RectangleMesh(fen.Point(0, 0), fen.Point(pi, pi),
- 3 * n, 3 * n)
+
+ mesh = fen.RectangleMesh(fen.Point(0., 0.), fen.Point(L, L), n, n)
self.V = fen.FunctionSpace(mesh, "P", 1)
- c, s = np.cos(theta), np.sin(theta)
x, y = fen.SpatialCoordinate(mesh)[:]
- C = 16. / pi ** 4.
+ self._above = ufl.conditional(ufl.ge(y, .5 * L), fenONE, fenZERO)
+ self._below = fenONE - self._above
+ c, s = np.cos(theta), np.sin(theta)
self.forcingTerm = [fen.cos(kappa * (c * x + s * y)),
fen.sin(kappa * (c * x + s * y))]
def A(self, mu : paramVal = [], der : List[int] = 0) -> ScOp:
"""Assemble (derivative of) operator of linear system."""
mu = self.checkParameter(mu)
if not hasattr(der, "__len__"): der = [der] * self.npar
derI = hashD(der)
self.autoSetDS()
if derI <= 0 and self.As[0] is None:
if self.verbosity >= 20:
verbosityDepth("INIT", "Assembling operator term A0.",
timestamp = self.timestamp)
DirichletBC0 = fen.DirichletBC(self.V, fenZERO,
self.DirichletBoundary)
- a0Re = fen.dot(self.u.dx(0), self.v.dx(0)) * 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)
+ 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))
+ self.As[0] = (fenics2Sparse(a0Re, parsRe, DirichletBC0, 1)
+ + 1.j * fenics2Sparse(a0Im, parsIm, DirichletBC0, 0))
if self.verbosity >= 20:
verbosityDepth("DEL", "Done assembling operator term.",
timestamp = self.timestamp)
if derI <= 1 and self.As[1] is None:
if self.verbosity >= 20:
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))
+ a1Re = - n2Re * self._above * fen.dot(self.u, self.v) * fen.dx
+ a1Im = - n2Im * self._above * fen.dot(self.u, self.v) * fen.dx
+ self.As[1] = (fenics2Sparse(a1Re, parsRe, DirichletBC0, 0)
+ + 1.j * fenics2Sparse(a1Im, parsIm, DirichletBC0, 0))
if self.verbosity >= 20:
verbosityDepth("DEL", "Done assembling operator term.",
timestamp = self.timestamp)
if derI <= 2 and self.As[2] is None:
if self.verbosity >= 20:
verbosityDepth("INIT", "Assembling operator term A2.",
timestamp = self.timestamp)
DirichletBC0 = fen.DirichletBC(self.V, fenZERO,
self.DirichletBoundary)
- a2Re = fen.dot(self.u.dx(1), self.v.dx(1)) * fen.dx
- A2Re = fen.assemble(a2Re)
- DirichletBC0.zero(A2Re)
- A2ReMat = fen.as_backend_type(A2Re).mat()
- A2Rer, A2Rec, A2Rev = A2ReMat.getValuesCSR()
- self.As[2] = scsp.csr_matrix((A2Rev, A2Rec, A2Rer),
- shape = A2ReMat.size,
- dtype = np.complex)
+ 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 * self._below * fen.dot(self.u, self.v) * fen.dx
+ a2Im = - n2Im * self._below * fen.dot(self.u, self.v) * fen.dx
+ self.As[2] = (fenics2Sparse(a2Re, parsRe, DirichletBC0, 0)
+ + 1.j * fenics2Sparse(a2Im, parsIm, DirichletBC0, 0))
if self.verbosity >= 20:
verbosityDepth("DEL", "Done assembling operator term.",
timestamp = self.timestamp)
return self._assembleA(mu, der, derI)
-
diff --git a/rrompy/hfengines/linear_problem/helmholtz_problem_engine.py b/rrompy/hfengines/linear_problem/helmholtz_problem_engine.py
index a77dd46..e65c100 100644
--- a/rrompy/hfengines/linear_problem/helmholtz_problem_engine.py
+++ b/rrompy/hfengines/linear_problem/helmholtz_problem_engine.py
@@ -1,160 +1,145 @@
# 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 List, ScOp, paramVal
from rrompy.solver.fenics import fenZERO, fenONE
from rrompy.utilities.base import verbosityDepth
from rrompy.utilities.poly_fitting.polynomial import (
hashDerivativeToIdx as hashD)
+from rrompy.solver.fenics import fenics2Sparse
__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.
bsH: Numpy array representation of homogeneized bs.
energyNormMatrix: Scipy sparse matrix representing inner product over
V.
+ energyNormDualMatrix: Scipy sparse matrix representing inner product
+ over V'.
+ dualityMatrix: Scipy sparse matrix representing duality V-V'.
+ energyNormPartialDualMatrix: Scipy sparse matrix representing dual
+ inner product between Riesz representers V-V.
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.
"""
def __init__(self, mu0 : paramVal = [0.], degree_threshold : int = np.inf,
verbosity : int = 10, timestamp : bool = True):
super().__init__(mu0 = mu0, degree_threshold = degree_threshold,
verbosity = verbosity, timestamp = timestamp)
self.nAs = 2
self.rescalingExp = [2.]
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 A(self, mu : paramVal = [], der : List[int] = 0) -> ScOp:
"""Assemble (derivative of) operator of linear system."""
mu = self.checkParameter(mu)
if not hasattr(der, "__len__"): der = [der] * self.npar
derI = hashD(der)
if derI <= 0 and self.As[0] is None:
self.autoSetDS()
if self.verbosity >= 20:
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))
+ self.As[0] = (fenics2Sparse(a0Re, parsRe, DirichletBC0, 1)
+ + 1.j * fenics2Sparse(a0Im, parsIm, DirichletBC0, 0))
if self.verbosity >= 20:
verbosityDepth("DEL", "Done assembling operator term.",
timestamp = self.timestamp)
if derI <= 1 and self.As[1] is None:
if self.verbosity >= 20:
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))
+ self.As[1] = (fenics2Sparse(a1Re, parsRe, DirichletBC0, 0)
+ + 1.j * fenics2Sparse(a1Im, parsIm, DirichletBC0, 0))
if self.verbosity >= 20:
verbosityDepth("DEL", "Done assembling operator term.",
timestamp = self.timestamp)
return self._assembleA(mu, der, derI)
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 b29b809..7106dae 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,245 +1,214 @@
# Copyright (C) 2018 by the RROMPy authors
#
# This file is part of RROMPy.
#
# RROMPy is free software: you can redistribute it and/or modify
# it under the terms of the GNU Lesser General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# RROMPy is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public License
# along with RROMPy. If not, see .
#
import numpy as np
import scipy.sparse as scsp
import fenics as fen
from rrompy.utilities.base.types import (Np1D, ScOp, Tuple, List, FenExpr,
paramVal)
from rrompy.solver.fenics import fenZERO
from .helmholtz_problem_engine import HelmholtzProblemEngine
from rrompy.utilities.base import verbosityDepth
from rrompy.utilities.poly_fitting.polynomial import (
hashDerivativeToIdx as hashD, hashIdxToDerivative as hashI)
+from rrompy.solver.fenics import fenics2Sparse, fenics2Vector
__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.
"""
def __init__(self, kappa:float, theta:float, n:int, mu0 : paramVal = [1.],
degree_threshold : int = np.inf, verbosity : int = 10,
timestamp : bool = True):
super().__init__(mu0 = mu0, degree_threshold = degree_threshold,
verbosity = verbosity, timestamp = timestamp)
self.nAs, self.nbs = 3, 20
self.kappa = kappa
self.theta = theta
self.forcingTermMu = np.nan
mesh = fen.RectangleMesh(fen.Point(0, 0), fen.Point(np.pi, np.pi),
3 * n, 3 * n)
self.V = fen.FunctionSpace(mesh, "P", 1)
self.rescalingExp = [1.]
- def buildEnergyNormForm(self): # H1
- """
- Build sparse matrix (in CSR format) representative of scalar product.
- """
- mudxM = np.abs(self.mu0(0, 0)) * (fen.dot(self.u.dx(0), self.v.dx(0))
- + fen.dot(self.u, self.v))
- imudy = 1. / np.abs(self.mu0(0, 0)) * fen.dot(self.u.dx(1),
- self.v.dx(1))
- normMatFen = fen.assemble((mudxM + imudy) * fen.dx)
- normMat = fen.as_backend_type(normMatFen).mat()
- self.energyNormMatrix = scsp.csr_matrix(normMat.getValuesCSR()[::-1],
- shape = normMat.size)
-
def getForcingTerm(self, mu : paramVal = []) -> Tuple[FenExpr, FenExpr]:
"""Compute forcing term."""
mu = self.checkParameter(mu)
if mu != self.forcingTermMu:
if self.verbosity >= 25:
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(0, 0)), np.imag(mu(0, 0))
mu2R, mu2I = np.real(mu(0, 0) ** 2.), np.imag(mu(0, 0) ** 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 assembling base expression.",
timestamp = self.timestamp)
return self.forcingTerm
def getExtraFactorB(self, mu : paramVal = [],
derI : int = 0) -> Tuple[FenExpr, FenExpr]:
"""Compute extra expression in RHS."""
mu = self.checkParameter(mu)
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."),
timestamp = self.timestamp)
from scipy.special import factorial as fact
y = fen.SpatialCoordinate(self.V.mesh())[1]
powR, powI = [(self.kappa * np.sin(self.theta)) ** derI * k\
for k in getPowMinusj(y, derI)]
mu2R, mu2I = np.real(mu(0, 0) ** 2.), np.imag(mu(0, 0) ** 2.)
exprR = mu2R * powR - mu2I * powI
exprI = mu2I * powR + mu2R * powI
if derI >= 1:
muR, muI = np.real(2. * mu(0, 0)), np.imag(2. * mu(0, 0))
powR, powI = [(self.kappa * np.sin(self.theta)) ** (derI - 1) * k\
* derI for k in getPowMinusj(y, derI - 1)]
exprR += muR * powR - muI * powI
exprI += muI * powR + muR * powI
if derI >= 2:
powR, powI = [(self.kappa * np.sin(self.theta)) ** (derI - 2) * k\
* derI * (derI - 1) for k in getPowMinusj(y, derI - 2)]
exprR += powR
exprI += powI
fac = fact(derI)
if self.verbosity >= 25:
verbosityDepth("DEL", "Done assembling auxiliary expression.",
timestamp = self.timestamp)
return [exprR / fac, exprI / fac]
def A(self, mu : paramVal = [], der : List[int] = 0) -> ScOp:
"""Assemble (derivative of) operator of linear system."""
mu = self.checkParameter(mu)
if not hasattr(der, "__len__"): der = [der] * self.npar
derI = hashD(der)
self.autoSetDS()
if derI <= 0 and self.As[0] is None:
if self.verbosity >= 20:
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)
+ self.As[0] = fenics2Sparse(a0Re, {}, DirichletBC0, 1)
if self.verbosity >= 20:
verbosityDepth("DEL", "Done assembling operator term.",
timestamp = self.timestamp)
- if derI <= 1 and self.As[1] is None: self.As[1] = 0.
+ if derI <= 1 and self.As[1] is None:
+ self.As[1] = self.checkAInBounds(-1)
if derI <= 2 and self.As[2] is None:
if self.verbosity >= 20:
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))
+ self.As[2] = (fenics2Sparse(a2Re, parsRe, DirichletBC0, 0)
+ + 1.j * fenics2Sparse(a2Im, parsIm, DirichletBC0, 0))
if self.verbosity >= 20:
verbosityDepth("DEL", "Done assembling operator term.",
timestamp = self.timestamp)
return self._assembleA(mu, der, derI)
def b(self, mu : paramVal = [], der : List[int] = 0,
homogeneized : bool = False) -> Np1D:
"""Assemble (derivative of) RHS of linear system."""
mu = self.checkParameter(mu)
if not hasattr(der, "__len__"): der = [der] * self.npar
derI = hashD(der)
nbsTot = self.nbsH if homogeneized else self.nbs
bs = self.bsH if homogeneized else self.bs
if homogeneized and self.mu0 != self.mu0BC:
self.liftDirichletData(self.mu0)
for j in range(derI, nbsTot):
if bs[j] is None:
if self.verbosity >= 20:
verbosityDepth("INIT", ("Assembling forcing term "
"b{}.").format(j),
timestamp = self.timestamp)
if j < self.nbs:
fRe, fIm = self.getForcingTerm(self.mu0)
cRe, cIm = self.getExtraFactorB(self.mu0, j)
cfRe, cfIm = cRe * fRe - cIm * fIm, cRe * fIm + cIm * fRe
else:
cfRe, cfIm = fenZERO, fenZERO
parsRe = self.iterReduceQuadratureDegree(zip([cfRe],
["forcingTermDer{}Real".format(j)]))
parsIm = self.iterReduceQuadratureDegree(zip([cfIm],
["forcingTermDer{}Imag".format(j)]))
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)
-
DirichletBC0 = fen.DirichletBC(self.V, fenZERO,
self.DirichletBoundary)
- DirichletBC0.apply(b0Re)
- DirichletBC0.apply(b0Im)
- b = np.array(b0Re[:] + 1.j * b0Im[:], dtype = np.complex)
+ b = (fenics2Vector(L0Re, parsRe, DirichletBC0, 1)
+ + 1.j * fenics2Vector(L0Im, parsIm, DirichletBC0, 1))
if homogeneized:
Ader = self.A(self.mu0, hashI(j, self.npar))
b -= Ader.dot(self.liftedDirichletDatum)
if homogeneized:
self.bsH[j] = b
else:
self.bs[j] = b
if self.verbosity >= 20:
verbosityDepth("DEL", "Done assembling forcing term.",
timestamp = self.timestamp)
return self._assembleb(mu, der, derI, homogeneized, self.mu0)
diff --git a/rrompy/hfengines/linear_problem/laplace_base_problem_engine.py b/rrompy/hfengines/linear_problem/laplace_base_problem_engine.py
index b2bbe18..eecea62 100644
--- a/rrompy/hfengines/linear_problem/laplace_base_problem_engine.py
+++ b/rrompy/hfengines/linear_problem/laplace_base_problem_engine.py
@@ -1,320 +1,366 @@
# Copyright (C) 2018 by the RROMPy authors
#
# This file is part of RROMPy.
#
# RROMPy is free software: you can redistribute it and/or modify
# it under the terms of the GNU Lesser General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# RROMPy is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public License
# along with RROMPy. If not, see .
#
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, List, ScOp, paramVal, paramList
-from rrompy.solver.fenics import fenZERO, fenONE, H1NormMatrix
+from rrompy.utilities.base.types import Np1D, List, ScOp, paramVal
+from rrompy.solver.fenics import (fenZERO, fenONE, L2InverseNormMatrix,
+ H1NormMatrix, Hminus1NormMatrix)
from rrompy.utilities.base import verbosityDepth
from rrompy.utilities.poly_fitting.polynomial import (
hashDerivativeToIdx as hashD, hashIdxToDerivative as hashI)
from rrompy.parameter import checkParameter
+from rrompy.solver.fenics import fenics2Sparse, fenics2Vector
__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.
bsH: Numpy array representation of homogeneized bs.
energyNormMatrix: Scipy sparse matrix representing inner product over
V.
+ energyNormDualMatrix: Scipy sparse matrix representing inner product
+ over V'.
+ dualityMatrix: Scipy sparse matrix representing duality V-V'.
+ energyNormPartialDualMatrix: Scipy sparse matrix representing dual
+ inner product between Riesz representers V-V.
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.
"""
-
+
+ _energyDualNormCompress = None
+
def __init__(self, mu0 : paramVal = [], degree_threshold : int = np.inf,
verbosity : int = 10, timestamp : bool = True):
super().__init__(degree_threshold = degree_threshold,
verbosity = verbosity, timestamp = timestamp)
self.mu0 = checkParameter(mu0)
self.npar = self.mu0.shape[1]
self.omega = np.abs(self.mu0(0, 0)) if self.npar > 0 else 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.",
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 initializing boundary measures.",
timestamp = self.timestamp)
def buildEnergyNormForm(self):
"""
Build sparse matrix (in CSR format) representative of scalar product.
"""
+ if self.verbosity >= 20:
+ verbosityDepth("INIT", "Assembling energy matrix.",
+ timestamp = self.timestamp)
self.energyNormMatrix = H1NormMatrix(self.V, np.abs(self.omega)**2)
+ if self.verbosity >= 20:
+ verbosityDepth("DEL", "Done assembling energy matrix.",
+ timestamp = self.timestamp)
+
+ def buildEnergyNormDualForm(self):
+ """
+ Build sparse matrix (in CSR format) representative of dual scalar
+ product.
+ """
+ if self.verbosity >= 20:
+ verbosityDepth("INIT", "Assembling energy dual matrix.",
+ timestamp = self.timestamp)
+ self.energyNormDualMatrix = Hminus1NormMatrix(
+ self.V, np.abs(self.omega)**2,
+ compressRank = self._energyDualNormCompress)
+ if self.verbosity >= 20:
+ verbosityDepth("DEL", "Done assembling energy dual matrix.",
+ timestamp = self.timestamp)
+
+ def buildDualityPairingForm(self):
+ """Build sparse matrix (in CSR format) representative of duality."""
+ if self.verbosity >= 20:
+ verbosityDepth("INIT", "Assembling duality matrix.",
+ timestamp = self.timestamp)
+ self.dualityMatrix = L2InverseNormMatrix(
+ self.V, solverType = self._solver,
+ solverArgs = self._solverArgs,
+ compressRank = self._dualityCompress)
+ if self.verbosity >= 20:
+ verbosityDepth("DEL", "Done assembling duality matrix.",
+ timestamp = self.timestamp)
+
+ def buildEnergyNormPartialDualForm(self):
+ """
+ Build sparse matrix (in CSR format) representative of dual scalar
+ product without duality.
+ """
+ if self.verbosity >= 20:
+ verbosityDepth("INIT", "Assembling energy partial dual matrix.",
+ timestamp = self.timestamp)
+ self.energyNormPartialDualMatrix = Hminus1NormMatrix(
+ self.V, np.abs(self.omega)**2,
+ compressRank = self._energyDualNormCompress,
+ duality = False)
+ if self.verbosity >= 20:
+ verbosityDepth("DEL",
+ "Done assembling energy partial dual matrix.",
+ timestamp = self.timestamp)
def A(self, mu : paramVal = [], der : List[int] = 0) -> ScOp:
"""Assemble (derivative of) operator of linear system."""
mu = self.checkParameter(mu)
if not hasattr(der, "__len__"): der = [der] * self.npar
derI = hashD(der)
if derI <= 0 and self.As[0] is None:
self.autoSetDS()
if self.verbosity >= 20:
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))
+ self.As[0] = (fenics2Sparse(a0Re, parsRe, DirichletBC0, 1)
+ + 1.j * fenics2Sparse(a0Im, parsIm, DirichletBC0, 0))
if self.verbosity >= 20:
verbosityDepth("DEL", "Done assembling operator term.",
timestamp = self.timestamp)
return self._assembleA(mu, der, derI)
def b(self, mu : paramVal = [], der : List[int] = 0,
homogeneized : bool = False) -> Np1D:
"""Assemble (derivative of) RHS of linear system."""
mu = self.checkParameter(mu)
if not hasattr(der, "__len__"): der = [der] * self.npar
derI = hashD(der)
nbsTot = self.nbsH if homogeneized else self.nbs
bs = self.bsH if homogeneized else self.bs
if homogeneized and self.mu0 != self.mu0BC:
self.liftDirichletData(self.mu0)
for j in range(derI, nbsTot):
if bs[j] is None:
self.autoSetDS()
if self.verbosity >= 20:
verbosityDepth("INIT", ("Assembling forcing term "
"b{}.").format(j),
timestamp = self.timestamp)
termNames, terms = [], []
if j == 0:
u0Re, u0Im = self.DirichletDatum
fRe, fIm = self.forcingTerm
g1Re, g1Im = self.NeumannDatum
g2Re, g2Im = self.RobinDatumG
termNames += ["forcingTerm", "NeumannDatum", "RobinDatumG"]
terms += [[fRe, fIm], [g1Re, g1Im], [g2Re, g2Im]]
else:
u0Re, u0Im = fenZERO, fenZERO
fRe, fIm = fenZERO, fenZERO
g1Re, g1Im = fenZERO, fenZERO
g2Re, g2Im = fenZERO, fenZERO
if len(termNames) > 0:
parsRe = self.iterReduceQuadratureDegree(zip(
[term[0] for term in terms],
[x + "Real" for x in termNames]))
parsIm = self.iterReduceQuadratureDegree(zip(
[term[1] for term in terms],
[x + "Imag" for x in termNames]))
else:
parsRe, parsIm = {}, {}
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)
DBCR = fen.DirichletBC(self.V, u0Re, self.DirichletBoundary)
DBCI = fen.DirichletBC(self.V, u0Im, self.DirichletBoundary)
- DBCR.apply(b0Re)
- DBCI.apply(b0Im)
- b = np.array(b0Re[:] + 1.j * b0Im[:], dtype = np.complex)
+ b = (fenics2Vector(L0Re, parsRe, DBCR, 1)
+ + 1.j * fenics2Vector(L0Im, parsIm, DBCI, 1))
if homogeneized:
Ader = self.A(self.mu0, hashI(j, self.npar))
b -= Ader.dot(self.liftedDirichletDatum)
if homogeneized:
self.bsH[j] = b
else:
self.bs[j] = b
if self.verbosity >= 20:
verbosityDepth("DEL", "Done assembling forcing term.",
timestamp = self.timestamp)
return self._assembleb(mu, der, derI, homogeneized, self.mu0)
diff --git a/rrompy/hfengines/linear_problem/laplace_disk_gaussian.py b/rrompy/hfengines/linear_problem/laplace_disk_gaussian.py
index 4da30a2..227c446 100644
--- a/rrompy/hfengines/linear_problem/laplace_disk_gaussian.py
+++ b/rrompy/hfengines/linear_problem/laplace_disk_gaussian.py
@@ -1,160 +1,158 @@
# 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, paramVal
from .laplace_base_problem_engine import LaplaceBaseProblemEngine
from rrompy.solver.fenics import fenZERO, fenONE
from rrompy.utilities.base import verbosityDepth
from rrompy.utilities.poly_fitting.polynomial import (
hashDerivativeToIdx as hashD, hashIdxToDerivative as hashI)
+from rrompy.solver.fenics import fenics2Vector
__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.
"""
def __init__(self, n:int, mu0 : paramVal = [0.], degree_threshold : int = np.inf,
verbosity : int = 10, timestamp : bool = True):
super().__init__(mu0 = mu0, degree_threshold = degree_threshold,
verbosity = verbosity, timestamp = timestamp)
self.nbs = 20
self.computebsFactors()
self.forcingTermMu = np.nan
import mshr
mesh = mshr.generate_mesh(mshr.Circle(fen.Point(0., 0.), 5.), 3 * n)
self.V = fen.FunctionSpace(mesh, "P", 1)
def getForcingTerm(self, mu : paramVal = []) -> Tuple[FenExpr, FenExpr]:
"""Compute forcing term."""
mu = self.checkParameter(mu)
if mu != self.forcingTermMu:
if self.verbosity >= 25:
verbosityDepth("INIT", ("Assembling base expression for "
"forcing term."),
timestamp = self.timestamp)
x, y = fen.SpatialCoordinate(self.V.mesh())[:]
C = np.exp(-.5 * mu(0, 0) ** 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(0, 0)), np.imag(mu(0, 0))
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 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 : paramVal = [],
derI : int = 0) -> Tuple[FenExpr, FenExpr]:
"""Compute extra expression in RHS."""
mu = self.checkParameter(mu)
if self.verbosity >= 25:
verbosityDepth("INIT", ("Assembling auxiliary expression for "
"forcing term derivative."),
timestamp = self.timestamp)
muR, muI = np.real(mu(0, 0)), np.imag(mu(0, 0))
x = fen.SpatialCoordinate(self.V.mesh())[0]
l = derI % 2
if l == 0:
powR, powI = fenONE, fenZERO
else:
powR, powI = x - muR, fen.Constant(muI)
exprR, exprI = [self.bsFactors[derI, l] * k for k in [powR, powI]]
for j in range(l + 2, derI + 1, 2):
for _ in range(2):
powR, powI = (powR * (x - muR) - powI * muI,
powR * muI + powI * (x - muR))
exprR += self.bsFactors[derI, j] * powR
exprI += self.bsFactors[derI, j] * powI
if self.verbosity >= 25:
verbosityDepth("DEL", "Done assembling auxiliary expression.",
timestamp = self.timestamp)
return[exprR, exprI]
def b(self, mu : paramVal = [], der : int = 0,
homogeneized : bool = False) -> Np1D:
"""Assemble (derivative of) RHS of linear system."""
mu = self.checkParameter(mu)
if not hasattr(der, "__len__"): der = [der] * self.npar
derI = hashD(der)
nbsTot = self.nbsH if homogeneized else self.nbs
bs = self.bsH if homogeneized else self.bs
if homogeneized and self.mu0 != self.mu0BC:
self.liftDirichletData(self.mu0)
for j in range(derI, nbsTot):
if bs[j] is None:
if self.verbosity >= 20:
verbosityDepth("INIT", ("Assembling forcing term "
"b{}.").format(j),
timestamp = self.timestamp)
if j < self.nbs:
fRe, fIm = self.getForcingTerm(self.mu0)
cRe, cIm = self.getExtraFactorB(self.mu0, j)
cfRe, cfIm = cRe * fRe - cIm * fIm, cRe * fIm + cIm * fRe
else:
cfRe, cfIm = fenZERO, fenZERO
parsRe = self.iterReduceQuadratureDegree(zip([cfRe],
["forcingTermDer{}Real".format(j)]))
parsIm = self.iterReduceQuadratureDegree(zip([cfIm],
["forcingTermDer{}Imag".format(j)]))
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)
DirichletBC0 = fen.DirichletBC(self.V, fenZERO,
self.DirichletBoundary)
- DirichletBC0.apply(b0Re)
- DirichletBC0.apply(b0Im)
- b = np.array(b0Re[:] + 1.j * b0Im[:], dtype = np.complex)
+ b = (fenics2Vector(L0Re, parsRe, DirichletBC0, 1)
+ + 1.j * fenics2Vector(L0Im, parsIm, DirichletBC0, 1))
if homogeneized:
Ader = self.A(self.mu0, hashI(j, self.npar))
b -= Ader.dot(self.liftedDirichletDatum)
if homogeneized:
self.bsH[j] = b
else:
self.bs[j] = b
if self.verbosity >= 20:
verbosityDepth("DEL", "Done assembling forcing term.",
timestamp = self.timestamp)
return self._assembleb(mu, der, derI, homogeneized, self.mu0)
diff --git a/rrompy/hfengines/linear_problem/bidimensional/helmholtz_square_simplified_domain_problem_engine.py b/rrompy/hfengines/linear_problem/membrane_fracture_engine_nodomain.py
similarity index 51%
copy from rrompy/hfengines/linear_problem/bidimensional/helmholtz_square_simplified_domain_problem_engine.py
copy to rrompy/hfengines/linear_problem/membrane_fracture_engine_nodomain.py
index d310906..a7ad7bb 100644
--- a/rrompy/hfengines/linear_problem/bidimensional/helmholtz_square_simplified_domain_problem_engine.py
+++ b/rrompy/hfengines/linear_problem/membrane_fracture_engine_nodomain.py
@@ -1,129 +1,104 @@
# 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
+import mshr, ufl
from rrompy.utilities.base.types import ScOp, List, paramVal
-from rrompy.solver.fenics import fenZERO
+from rrompy.solver.fenics import fenZERO, fenONE
from rrompy.hfengines.linear_problem.helmholtz_problem_engine import (
HelmholtzProblemEngine)
from rrompy.utilities.base import verbosityDepth
from rrompy.utilities.poly_fitting.polynomial import (
hashDerivativeToIdx as hashD)
+from rrompy.solver.fenics import fenics2Sparse
-__all__ = ['HelmholtzSquareSimplifiedDomainProblemEngine']
+__all__ = ['MembraneFractureEngineNoDomain']
-class HelmholtzSquareSimplifiedDomainProblemEngine(HelmholtzProblemEngine):
- """
- Solver for square Helmholtz problems with parametric laplacian.
- - \dxx u - mu_2**2 \dyy u - mu_1**2 * u = f in \Omega_mu = [0,\pi]^2
- u = 0 on \partial\Omega
- """
+class MembraneFractureEngineNoDomain(HelmholtzProblemEngine):
- def __init__(self, kappa:float, theta:float, n:int,
- mu0 : paramVal = [12. ** .5, 1.],
+ def __init__(self, mu0 : paramVal = [20. ** .5, .6], H : float = 1.,
+ L : float = .75, delta : float = .05, n : int = 50,
degree_threshold : int = np.inf, verbosity : int = 10,
timestamp : bool = True):
- super().__init__(mu0 = mu0, degree_threshold = degree_threshold,
+ super().__init__(mu0 = mu0[0], degree_threshold = degree_threshold,
verbosity = verbosity, timestamp = timestamp)
- self.nAs = 3
- self.npar = 2
- self.rescalingExp = [2., 2.]
- pi = np.pi
- mesh = fen.RectangleMesh(fen.Point(0, 0), fen.Point(pi, pi),
- 3 * n, 3 * n)
+ self.npar = 1
+ self.lFrac = mu0[1]
+ self.H = H
+ self.rescalingExp = [2.]
+ domain = (mshr.Rectangle(fen.Point(0., - H / 2.),
+ fen.Point(2. * L + delta, H / 2.))
+ - mshr.Rectangle(fen.Point(L, 0.),
+ fen.Point(L + delta, H / 2.)))
+ mesh = mshr.generate_mesh(domain, n)
self.V = fen.FunctionSpace(mesh, "P", 1)
-
- c, s = np.cos(theta), np.sin(theta)
+ self.NeumannBoundary = lambda x, on_b: (on_b and x[1] >= - H / 4.
+ and x[0] >= L
+ and x[0] <= L + delta)
+ self.DirichletBoundary = "REST"
+
x, y = fen.SpatialCoordinate(mesh)[:]
- C = 16. / pi ** 4.
- self.forcingTerm = [fen.cos(kappa * (c * x + s * y)),
- fen.sin(kappa * (c * x + s * y))]
+ self._belowIndicator = ufl.conditional(ufl.le(y, 0.), fenONE, fenZERO)
+ self._aboveIndicator = fenONE - self._belowIndicator
+ self.DirichletDatum = [fen.exp(- 10. * (H / 2. + y) / H
+ - .5 * ((x - .6 * L) / (.1 * L)) ** 2.
+ ) * self._belowIndicator, fenZERO]
def A(self, mu : paramVal = [], der : List[int] = 0) -> ScOp:
"""Assemble (derivative of) operator of linear system."""
mu = self.checkParameter(mu)
if not hasattr(der, "__len__"): der = [der] * self.npar
derI = hashD(der)
- self.autoSetDS()
if derI <= 0 and self.As[0] is None:
+ self.autoSetDS()
if self.verbosity >= 20:
verbosityDepth("INIT", "Assembling operator term A0.",
timestamp = self.timestamp)
DirichletBC0 = fen.DirichletBC(self.V, fenZERO,
self.DirichletBoundary)
- a0Re = fen.dot(self.u.dx(0), self.v.dx(0)) * 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)
+ a0Re = (fen.dot(self.u.dx(0), self.v.dx(0))
+ + self.H ** 4 / 4. * (self.lFrac ** -2. * self._aboveIndicator
+ + (self.H - self.lFrac) ** -2. * self._belowIndicator)
+ * fen.dot(self.u.dx(1), self.v.dx(1))
+ ) * fen.dx
+ self.As[0] = fenics2Sparse(a0Re, {}, DirichletBC0, 1)
if self.verbosity >= 20:
verbosityDepth("DEL", "Done assembling operator term.",
timestamp = self.timestamp)
if derI <= 1 and self.As[1] is None:
if self.verbosity >= 20:
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.",
- timestamp = self.timestamp)
- if derI <= 2 and self.As[2] is None:
- if self.verbosity >= 20:
- verbosityDepth("INIT", "Assembling operator term A2.",
- timestamp = self.timestamp)
- DirichletBC0 = fen.DirichletBC(self.V, fenZERO,
- self.DirichletBoundary)
- a2Re = fen.dot(self.u.dx(1), self.v.dx(1)) * fen.dx
- A2Re = fen.assemble(a2Re)
- DirichletBC0.zero(A2Re)
- A2ReMat = fen.as_backend_type(A2Re).mat()
- A2Rer, A2Rec, A2Rev = A2ReMat.getValuesCSR()
- self.As[2] = scsp.csr_matrix((A2Rev, A2Rec, A2Rer),
- shape = A2ReMat.size,
- dtype = np.complex)
+ self.As[1] = (fenics2Sparse(a1Re, parsRe, DirichletBC0, 0)
+ + 1.j * fenics2Sparse(a1Im, parsIm, DirichletBC0, 0))
if self.verbosity >= 20:
verbosityDepth("DEL", "Done assembling operator term.",
timestamp = self.timestamp)
return self._assembleA(mu, der, derI)
diff --git a/rrompy/hfengines/linear_problem/scattering_problem_engine.py b/rrompy/hfengines/linear_problem/scattering_problem_engine.py
index 2b62cea..2bd8229 100644
--- a/rrompy/hfengines/linear_problem/scattering_problem_engine.py
+++ b/rrompy/hfengines/linear_problem/scattering_problem_engine.py
@@ -1,174 +1,155 @@
# 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 List, ScOp, paramVal
from rrompy.solver.fenics import fenZERO
from rrompy.utilities.base import verbosityDepth
from .helmholtz_problem_engine import HelmholtzProblemEngine
from rrompy.utilities.poly_fitting.polynomial import (
hashDerivativeToIdx as hashD)
from rrompy.utilities.exception_manager import RROMPyWarning
+from rrompy.solver.fenics import fenics2Sparse
__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.
bsH: Numpy array representation of homogeneized bs.
energyNormMatrix: Scipy sparse matrix representing inner product over
V.
+ energyNormDualMatrix: Scipy sparse matrix representing inner product
+ over V'.
+ dualityMatrix: Scipy sparse matrix representing duality V-V'.
+ energyNormPartialDualMatrix: Scipy sparse matrix representing dual
+ inner product between Riesz representers V-V.
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.
"""
signR = - 1.
def __init__(self, mu0 : paramVal = [0.], degree_threshold : int = inf,
verbosity : int = 10, timestamp : bool = True):
self.silenceWarnings = True
super().__init__(mu0 = mu0, degree_threshold = degree_threshold,
verbosity = verbosity, timestamp = timestamp)
del self.silenceWarnings
self.nAs = 3
self.rescalingExp = [1.]
@property
def RobinDatumH(self):
"""Value of h."""
return self.signR * self.omega
@RobinDatumH.setter
def RobinDatumH(self, RobinDatumH):
if not hasattr(self, "silenceWarnings"):
RROMPyWarning(("Scattering problems do not allow changes of h. "
"Ignoring assignment."))
return
def A(self, mu : paramVal = [], der : List[int] = 0) -> ScOp:
"""Assemble (derivative of) operator of linear system."""
mu = self.checkParameter(mu)
if not hasattr(der, "__len__"): der = [der] * self.npar
derI = hashD(der)
if derI <= 0 and self.As[0] is None:
if self.verbosity >= 20:
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))
+ self.As[0] = (fenics2Sparse(a0Re, parsRe, DirichletBC0, 1)
+ + 1.j * fenics2Sparse(a0Im, parsIm, DirichletBC0, 0))
if self.verbosity >= 20:
verbosityDepth("DEL", "Done assembling operator term.",
timestamp = self.timestamp)
if derI <= 1 and self.As[1] is None:
self.autoSetDS()
if self.verbosity >= 20:
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)
+ self.As[1] = (self.signR * 1.j
+ * fenics2Sparse(a1, {}, DirichletBC0, 0))
if self.verbosity >= 20:
verbosityDepth("DEL", "Done assembling operator term.",
timestamp = self.timestamp)
if derI <= 2 and self.As[2] is None:
if self.verbosity >= 20:
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))
+ self.As[2] = (fenics2Sparse(a2Re, parsRe, DirichletBC0, 0)
+ + 1.j * fenics2Sparse(a2Im, parsIm, DirichletBC0, 0))
if self.verbosity >= 20:
verbosityDepth("DEL", "Done assembling operator term.",
timestamp = self.timestamp)
return self._assembleA(mu, der, derI)
diff --git a/rrompy/hfengines/vector_linear_problem/bidimensional/linear_elasticity_beam_elasticity_constants.py b/rrompy/hfengines/vector_linear_problem/bidimensional/linear_elasticity_beam_elasticity_constants.py
index e43a097..80c1a2b 100644
--- a/rrompy/hfengines/vector_linear_problem/bidimensional/linear_elasticity_beam_elasticity_constants.py
+++ b/rrompy/hfengines/vector_linear_problem/bidimensional/linear_elasticity_beam_elasticity_constants.py
@@ -1,149 +1,157 @@
# 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.vector_linear_problem.\
linear_elasticity_beam_poisson_ratio import LinearElasticityBeamPoissonRatio
from rrompy.solver.fenics import fenZEROS
from rrompy.utilities.base.types import Np1D, List, ScOp, paramVal
from rrompy.utilities.base import verbosityDepth
-from rrompy.utilities.exception_manager import RROMPyException
+from rrompy.utilities.exception_manager import RROMPyAssert
+from rrompy.utilities.poly_fitting.polynomial import (
+ hashDerivativeToIdx as hashD)
+from rrompy.solver.fenics import fenics2Sparse, fenics2Vector
__all__ = ['LinearElasticityBeamElasticityConstants']
class LinearElasticityBeamElasticityConstants(
LinearElasticityBeamPoissonRatio):
"""
Solver for linear elasticity problem of a beam subject to its own weight,
with parametric Joung modulus and 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
"""
def __init__(self, n:int, rho_:float, g:float, E0:float, nu0:float,
length:float, degree_threshold : int = np.inf,
verbosity : int = 10, timestamp : bool = True):
super().__init__(mu0 = [nu0, E0], degree_threshold = degree_threshold,
verbosity = verbosity, timestamp = timestamp)
self.nAs, self.nbs = 5, 4
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)), fenZEROS(2)]
self.DirichletBoundary = lambda x, on_b: on_b and fen.near(x[0], 0.)
self.NeumannBoundary = "REST"
def A(self, mu : paramVal = [], der : List[int] = 0) -> ScOp:
"""Assemble (derivative of) operator of linear system."""
mu = self.checkParameter(mu)
if not hasattr(der, "__len__"): der = [der] * self.npar
derI = hashD(der)
self.autoSetDS()
- if derI <= 0 and self.As[0] is None: self.As[0] = 0.
- if derI <= 1 and self.As[1] is None: self.As[1] = 0.
+ for j in [1, 3]:
+ if derI <= j and self.As[j] is None:
+ self.As[j] = self.checkAInBounds(-1)
+ if derI <= 0 and self.As[0] is None:
+ if self.verbosity >= 20:
+ verbosityDepth("INIT", "Assembling operator term A0.",
+ timestamp = self.timestamp)
+ DirichletBC0 = fen.DirichletBC(self.V, fenZEROS(2),
+ self.DirichletBoundary)
+ a0Re = fen.inner(fenZEROS(2), self.v) * fen.dx
+ self.As[0] = fenics2Sparse(a0Re, {}, DirichletBC0, 1)
+ if self.verbosity >= 20:
+ verbosityDepth("DEL", "Done assembling operator term.",
+ timestamp = self.timestamp)
if derI <= 4 and self.As[2] is None:
if self.verbosity >= 20:
verbosityDepth("INIT", "Assembling operator term A2.",
timestamp = self.timestamp)
DirichletBC0 = fen.DirichletBC(self.V, fenZEROS(2),
self.DirichletBoundary)
epsilon = lambda u: .5 * (fen.grad(u) + fen.nabla_grad(u))
- a0Re = 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[2] = scsp.csr_matrix((A0Rev, A0Rec, A0Rer),
- shape = A0ReMat.size,
- dtype = np.complex)
+ a2Re = 2. * fen.inner(epsilon(self.u), epsilon(self.v)) * fen.dx
+ self.As[2] = fenics2Sparse(a2Re, {}, DirichletBC0, 0)
if self.verbosity >= 20:
verbosityDepth("DEL", "Done assembling operator term.",
timestamp = self.timestamp)
- if derI <= 3 and self.As[3] is None: self.As[3] = 0.
if derI <= 4 and self.As[4] is None:
if self.verbosity >= 20:
verbosityDepth("INIT", "Assembling operator term A4.",
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[4] = 2. * (scsp.csr_matrix((A1Rev, A1Rec, A1Rer),
- shape = A1ReMat.size,
- dtype = np.complex)
- - self.As[2])
+ a4Re = fen.div(self.u) * fen.div(self.v) * fen.dx
+ self.As[4] = (fenics2Sparse(a4Re, {}, DirichletBC0, 0)
+ - 2. * self.As[2])
if self.verbosity >= 20:
verbosityDepth("DEL", "Done assembling operator term.",
timestamp = self.timestamp)
return self._assembleA(mu, der, derI)
def b(self, mu : paramVal = [], der : List[int] = 0,
homogeneized : bool = False) -> Np1D:
"""Assemble (derivative of) RHS of linear system."""
RROMPyAssert(homogeneized, False, "Homogeneized")
mu = self.checkParameter(mu)
if not hasattr(der, "__len__"): der = [der] * self.npar
derI = hashD(der)
- if derI <= 3 and self.bs[0] is None:
+ if derI <= 0 and self.bs[0] is None:
self.autoSetDS()
if self.verbosity >= 20:
verbosityDepth("INIT", "Assembling forcing term b0.",
timestamp = self.timestamp)
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.bs[0] = np.array(b0Re[:] + 1.j * b0Im[:], dtype = np.complex)
- if derI <= 1 and self.bs[1] is None:
+ self.bs[0] = (fenics2Vector(L0Re, parsRe, DBCR, 1)
+ + 1.j * fenics2Vector(L0Im, parsIm, DBCI, 1))
+ if derI <= 3 and self.bs[1] is None:
if self.verbosity >= 20:
verbosityDepth("INIT", "Assembling forcing term b1.",
timestamp = self.timestamp)
- self.bs[1] = - self.bs[0]
+ fRe, fIm = self.forcingTerm
+ parsRe = self.iterReduceQuadratureDegree(zip([fRe],
+ ["forcingTermReal"]))
+ parsIm = self.iterReduceQuadratureDegree(zip([fIm],
+ ["forcingTermImag"]))
+ L1Re = - fen.inner(fRe, self.v) * fen.dx
+ L1Im = - fen.inner(fIm, self.v) * fen.dx
+ DBC0 = fen.DirichletBC(self.V, fenZEROS(2), self.DirichletBoundary)
+ self.bs[1] = (fenics2Vector(L1Re, parsRe, DBC0, 1)
+ + 1.j * fenics2Vector(L1Im, parsIm, DBC0, 1))
if self.verbosity >= 20:
verbosityDepth("DEL", "Done assembling forcing term.",
timestamp = self.timestamp)
- if derI <= 2 and self.bs[2] is None: self.bs[2] = 0.
+ if derI <= 2 and self.bs[2] is None:
+ self.bs[2] = self.checkbInBounds(-1)
if derI <= 3 and self.bs[3] is None:
if self.verbosity >= 20:
verbosityDepth("INIT", "Assembling forcing term b3.",
timestamp = self.timestamp)
- self.bs[3] = - 2. * self.bs[0]
+ self.bs[3] = 2. * self.bs[1]
if self.verbosity >= 20:
verbosityDepth("DEL", "Done assembling forcing term.",
timestamp = self.timestamp)
return self._assembleb(mu, der, derI, homogeneized)
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 8b80487..096c4ce 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,148 +1,145 @@
# 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.solver.fenics import fenZEROS
from rrompy.utilities.base.types import Np1D, List, ScOp, paramVal
from rrompy.utilities.base import verbosityDepth
from rrompy.utilities.exception_manager import RROMPyAssert
from rrompy.utilities.poly_fitting.polynomial import (
hashDerivativeToIdx as hashD)
+from rrompy.solver.fenics import fenics2Sparse, fenics2Vector
__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
"""
def __init__(self, n:int, rho_:float, g:float, E:float, nu0:float,
length:float, degree_threshold : int = np.inf,
verbosity : int = 10, timestamp : bool = True):
super().__init__(mu0 = [nu0], degree_threshold = degree_threshold,
verbosity = verbosity, timestamp = timestamp)
self.nAs, self.nbs = 2, 3
self.E_ = E
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 : paramVal = [], der : List[int] = 0) -> ScOp:
"""Assemble (derivative of) operator of linear system."""
mu = self.checkParameter(mu)
if not hasattr(der, "__len__"): der = [der] * self.npar
derI = hashD(der)
self.autoSetDS()
- if derI <= 1 and self.As[0] is None:
+ if derI <= 0 and self.As[0] is None:
if self.verbosity >= 20:
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 = self.E_ * 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)
+ a0Re = 2. * self.E_ * fen.inner(epsilon(self.u),
+ epsilon(self.v)) * fen.dx
+ self.As[0] = fenics2Sparse(a0Re, {}, DirichletBC0, 1)
if self.verbosity >= 20:
verbosityDepth("DEL", "Done assembling operator term.",
timestamp = self.timestamp)
if derI <= 1 and self.As[1] is None:
if self.verbosity >= 20:
verbosityDepth("INIT", "Assembling operator term A1.",
timestamp = self.timestamp)
DirichletBC0 = fen.DirichletBC(self.V, fenZEROS(2),
self.DirichletBoundary)
- a1Re = self.E_ * 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] = 2. * (scsp.csr_matrix((A1Rev, A1Rec, A1Rer),
- shape = A1ReMat.size,
- dtype = np.complex)
- - self.As[0])
+ epsilon = lambda u: .5 * (fen.grad(u) + fen.nabla_grad(u))
+ a1Re = self.E_ * (fen.div(self.u) * fen.div(self.v)
+ - 4. * fen.inner(epsilon(self.u),
+ epsilon(self.v))) * fen.dx
+ self.As[1] = fenics2Sparse(a1Re, {}, DirichletBC0, 0)
if self.verbosity >= 20:
verbosityDepth("DEL", "Done assembling operator term.",
timestamp = self.timestamp)
return self._assembleA(mu, der, derI)
def b(self, mu : paramVal = [], der : List[int] = 0,
homogeneized : bool = False) -> Np1D:
"""Assemble (derivative of) RHS of linear system."""
RROMPyAssert(homogeneized, False, "Homogeneized")
mu = self.checkParameter(mu)
if not hasattr(der, "__len__"): der = [der] * self.npar
derI = hashD(der)
- if derI <= 2 and self.bs[0] is None:
+ if derI <= 0 and self.bs[0] is None:
self.autoSetDS()
if self.verbosity >= 20:
verbosityDepth("INIT", "Assembling forcing term b0.",
timestamp = self.timestamp)
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.bs[0] = np.array(b0Re[:] + 1.j * b0Im[:], dtype = np.complex)
- if derI <= 1 and self.bs[1] is None:
+ self.bs[0] = (fenics2Vector(L0Re, parsRe, DBCR, 1)
+ + 1.j * fenics2Vector(L0Im, parsIm, DBCI, 1))
+ if derI <= 2 and self.bs[1] is None:
if self.verbosity >= 20:
verbosityDepth("INIT", "Assembling forcing term b1.",
timestamp = self.timestamp)
- self.bs[1] = - self.bs[0]
+ fRe, fIm = self.forcingTerm
+ parsRe = self.iterReduceQuadratureDegree(zip([fRe],
+ ["forcingTermReal"]))
+ parsIm = self.iterReduceQuadratureDegree(zip([fIm],
+ ["forcingTermImag"]))
+ L1Re = - fen.inner(fRe, self.v) * fen.dx
+ L1Im = - fen.inner(fIm, self.v) * fen.dx
+ DBC0 = fen.DirichletBC(self.V, fenZEROS(2),
+ self.DirichletBoundary)
+ self.bs[1] = (fenics2Vector(L1Re, parsRe, DBC0, 1)
+ + 1.j * fenics2Vector(L1Im, parsIm, DBC0, 1))
if self.verbosity >= 20:
verbosityDepth("DEL", "Done assembling forcing term.",
timestamp = self.timestamp)
if derI <= 2 and self.bs[2] is None:
if self.verbosity >= 20:
verbosityDepth("INIT", "Assembling forcing term b2.",
timestamp = self.timestamp)
- self.bs[2] = - 2. * self.bs[0]
+ self.bs[2] = 2. * self.bs[1]
if self.verbosity >= 20:
verbosityDepth("DEL", "Done assembling forcing term.",
timestamp = self.timestamp)
return self._assembleb(mu, der, derI, homogeneized)
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 0290795..8f68161 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,180 +1,204 @@
# 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 List, ScOp, paramVal
-from rrompy.solver.fenics import fenZERO, fenZEROS, fenONE, elasticNormMatrix
+from rrompy.solver.fenics import (fenZERO, fenZEROS, fenONE, L2NormMatrix,
+ elasticNormMatrix, elasticDualNormMatrix)
from rrompy.utilities.base import verbosityDepth
from rrompy.utilities.poly_fitting.polynomial import (
hashDerivativeToIdx as hashD)
+from rrompy.solver.fenics import fenics2Sparse
__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.
+ energyNormDualMatrix: Scipy sparse matrix representing inner product
+ over V'.
+ dualityMatrix: Scipy sparse matrix representing duality V-V'.
+ energyNormPartialDualMatrix: Scipy sparse matrix representing dual
+ inner product between Riesz representers V-V.
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.
"""
def __init__(self, mu0 : paramVal = [0.], degree_threshold : int = np.inf,
verbosity : int = 10, timestamp : bool = True):
super().__init__(mu0 = [mu0], degree_threshold = degree_threshold,
verbosity = verbosity, timestamp = timestamp)
self.nAs = 2
self.omega = np.abs(self.mu0(0, 0))
self.rho_ = fenONE
self.rescalingExp = [2.]
@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.
"""
- lambda_Re, _ = self.lambda_
- mu_Re, _ = self.mu_
- r_Re, _ = self.rho_
- self.energyNormMatrix = elasticNormMatrix(self.V, lambda_Re, mu_Re,
- np.abs(self.omega)**2 * r_Re)
+ if self.verbosity >= 20:
+ verbosityDepth("INIT", "Assembling energy matrix.",
+ timestamp = self.timestamp)
+ self.energyNormMatrix = elasticNormMatrix(
+ self.V, self.lambda_[0], self.mu_[0],
+ np.abs(self.omega)**2 * self.rho_[0])
+ if self.verbosity >= 20:
+ verbosityDepth("DEL", "Done assembling energy matrix.",
+ timestamp = self.timestamp)
+
+ def buildEnergyNormDualForm(self):
+ """
+ Build sparse matrix (in CSR format) representative of dual scalar
+ product.
+ """
+ if self.verbosity >= 20:
+ verbosityDepth("INIT", "Assembling energy dual matrix.",
+ timestamp = self.timestamp)
+ self.energyNormDualMatrix = elasticDualNormMatrix(
+ self.V, self.lambda_[0], self.mu_[0],
+ np.abs(self.omega)**2 * self.rho_[0],
+ compressRank = self._energyDualNormCompress)
+ if self.verbosity >= 20:
+ verbosityDepth("DEL", "Done assembling energy dual matrix.",
+ timestamp = self.timestamp)
+
+ def buildEnergyNormPartialDualForm(self):
+ """
+ Build sparse matrix (in CSR format) representative of dual scalar
+ product without duality.
+ """
+ if self.verbosity >= 20:
+ verbosityDepth("INIT", "Assembling energy partial dual matrix.",
+ timestamp = self.timestamp)
+ self.energyNormPartialDualMatrix = elasticDualNormMatrix(
+ self.V, self.lambda_[0], self.mu_[0],
+ np.abs(self.omega)**2 * self.rho_[0],
+ compressRank = self._energyDualNormCompress,
+ duality = False)
+ if self.verbosity >= 20:
+ verbosityDepth("DEL",
+ "Done assembling energy partial dual matrix.",
+ timestamp = self.timestamp)
def A(self, mu : paramVal = [], der : List[int] = 0) -> ScOp:
"""Assemble (derivative of) operator of linear system."""
mu = self.checkParameter(mu)
if not hasattr(der, "__len__"): der = [der] * self.npar
derI = hashD(der)
self.autoSetDS()
if derI <= 0 and self.As[0] is None:
if self.verbosity >= 20:
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))
+ self.As[0] = (fenics2Sparse(a0Re, parsRe, DirichletBC0, 1)
+ + 1.j * fenics2Sparse(a0Im, parsIm, DirichletBC0, 0))
if self.verbosity >= 20:
verbosityDepth("DEL", "Done assembling operator term.",
timestamp = self.timestamp)
if derI <= 1 and self.As[1] is None:
if self.verbosity >= 20:
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))
+ self.As[1] = (fenics2Sparse(a1Re, parsRe, DirichletBC0, 0)
+ + 1.j * fenics2Sparse(a1Im, parsIm, DirichletBC0, 0))
if self.verbosity >= 20:
verbosityDepth("DEL", "Done assembling operator term.",
timestamp = self.timestamp)
return self._assembleA(mu, der, derI)
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 898e32d..2642941 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,205 +1,180 @@
# 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 List, ScOp, paramVal
from rrompy.solver.fenics import fenZERO, fenZEROS
from rrompy.utilities.base import verbosityDepth
from rrompy.utilities.poly_fitting.polynomial import (
hashDerivativeToIdx as hashD)
+from rrompy.solver.fenics import fenics2Sparse
__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.
+ energyNormDualMatrix: Scipy sparse matrix representing inner product
+ over V'.
+ dualityMatrix: Scipy sparse matrix representing duality V-V'.
+ energyNormPartialDualMatrix: Scipy sparse matrix representing dual
+ inner product between Riesz representers V-V.
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.
"""
def __init__(self, mu0 : paramVal = [0.], degree_threshold : int = np.inf,
verbosity : int = 10, timestamp : bool = True):
super().__init__(mu0 = [mu0], degree_threshold = degree_threshold,
verbosity = verbosity, timestamp = timestamp)
self.nAs = 3
self.eta = fenZERO
self.rescalingExp = [1.]
@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 A(self, mu : paramVal = [], der : List[int] = 0) -> ScOp:
"""Assemble (derivative of) operator of linear system."""
mu = self.checkParameter(mu)
if not hasattr(der, "__len__"): der = [der] * self.npar
derI = hashD(der)
self.autoSetDS()
if derI <= 0 and self.As[0] is None:
if self.verbosity >= 20:
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))
+ self.As[0] = (fenics2Sparse(a0Re, parsRe, DirichletBC0, 1)
+ + 1.j * fenics2Sparse(a0Im, parsIm, DirichletBC0, 0))
if self.verbosity >= 20:
verbosityDepth("DEL", "Done assembling operator term.",
timestamp = self.timestamp)
if derI <= 1 and self.As[1] is None:
if self.verbosity >= 20:
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))
+ self.As[1] = (fenics2Sparse(a1Re, parsRe, DirichletBC0, 0)
+ + 1.j * fenics2Sparse(a1Im, parsIm, DirichletBC0, 0))
if self.verbosity >= 20:
verbosityDepth("DEL", "Done assembling operator term.",
timestamp = self.timestamp)
if derI <= 2 and self.As[2] is None:
if self.verbosity >= 20:
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))
+ self.As[2] = (fenics2Sparse(a2Re, parsRe, DirichletBC0, 0)
+ + 1.j * fenics2Sparse(a2Im, parsIm, DirichletBC0, 0))
if self.verbosity >= 20:
verbosityDepth("DEL", "Done assembling operator term.",
timestamp = self.timestamp)
return self._assembleA(mu, der, derI)
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 5fe95b0..28c8886 100644
--- a/rrompy/hfengines/vector_linear_problem/linear_elasticity_problem_engine.py
+++ b/rrompy/hfengines/vector_linear_problem/linear_elasticity_problem_engine.py
@@ -1,346 +1,392 @@
# 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, List, ScOp, paramVal
-from rrompy.solver.fenics import fenZERO, fenZEROS, fenONE, elasticNormMatrix
+from rrompy.solver.fenics import (fenZERO, fenZEROS, fenONE,
+ L2InverseNormMatrix, elasticNormMatrix,
+ elasticDualNormMatrix)
from rrompy.utilities.base import verbosityDepth
from rrompy.utilities.poly_fitting.polynomial import (
hashDerivativeToIdx as hashD, hashIdxToDerivative as hashI)
from rrompy.parameter import checkParameter
+from rrompy.solver.fenics import fenics2Sparse, fenics2Vector
__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.
+ energyNormDualMatrix: Scipy sparse matrix representing inner product
+ over V'.
+ dualityMatrix: Scipy sparse matrix representing duality V-V'.
+ energyNormPartialDualMatrix: Scipy sparse matrix representing dual
+ inner product between Riesz representers V-V.
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.
"""
-
+
+ _energyDualNormCompress = None
+
def __init__(self, mu0 : paramVal = [], degree_threshold : int = np.inf,
verbosity : int = 10, timestamp : bool = True):
super().__init__(degree_threshold = degree_threshold,
verbosity = verbosity, timestamp = timestamp)
self.lambda_ = fenONE
self.mu_ = fenONE
self.mu0 = checkParameter(mu0)
self.npar = self.mu0.shape[1]
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.",
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 initializing boundary measures.",
timestamp = self.timestamp)
def buildEnergyNormForm(self):
"""
Build sparse matrix (in CSR format) representative of scalar product.
"""
- lambda_Re, _ = self.lambda_
- mu_Re, _ = self.mu_
- self.energyNormMatrix = elasticNormMatrix(self.V, lambda_Re, mu_Re)
+ if self.verbosity >= 20:
+ verbosityDepth("INIT", "Assembling energy matrix.",
+ timestamp = self.timestamp)
+ self.energyNormMatrix = elasticNormMatrix(self.V, self.lambda_[0],
+ self.mu_[0])
+ if self.verbosity >= 20:
+ verbosityDepth("DEL", "Done assembling energy matrix.",
+ timestamp = self.timestamp)
+
+ def buildEnergyNormDualForm(self):
+ """
+ Build sparse matrix (in CSR format) representative of dual scalar
+ product.
+ """
+ if self.verbosity >= 20:
+ verbosityDepth("INIT", "Assembling energy dual matrix.",
+ timestamp = self.timestamp)
+ self.energyNormDualMatrix = elasticDualNormMatrix(
+ self.V, self.lambda_[0], self.mu_[0],
+ compressRank = self._energyDualNormCompress)
+ if self.verbosity >= 20:
+ verbosityDepth("DEL", "Done assembling energy dual matrix.",
+ timestamp = self.timestamp)
+
+ def buildDualityPairingForm(self):
+ """Build sparse matrix (in CSR format) representative of duality."""
+ if self.verbosity >= 20:
+ verbosityDepth("INIT", "Assembling duality matrix.",
+ timestamp = self.timestamp)
+ self.dualityMatrix = L2InverseNormMatrix(
+ self.V, solverType = self._solver,
+ solverArgs = self._solverArgs,
+ compressRank = self._dualityCompress)
+ if self.verbosity >= 20:
+ verbosityDepth("DEL", "Done assembling duality matrix.",
+ timestamp = self.timestamp)
+
+ def buildEnergyNormPartialDualForm(self):
+ """
+ Build sparse matrix (in CSR format) representative of dual scalar
+ product without duality.
+ """
+ if self.verbosity >= 20:
+ verbosityDepth("INIT", "Assembling energy partial dual matrix.",
+ timestamp = self.timestamp)
+ self.energyNormPartialDualMatrix = elasticDualNormMatrix(
+ self.V, self.lambda_[0], self.mu_[0],
+ compressRank = self._energyDualNormCompress,
+ duality = False)
+ if self.verbosity >= 20:
+ verbosityDepth("DEL",
+ "Done assembling energy partial dual matrix.",
+ timestamp = self.timestamp)
def A(self, mu : paramVal = [], der : List[int] = 0) -> ScOp:
"""Assemble (derivative of) operator of linear system."""
mu = self.checkParameter(mu)
if not hasattr(der, "__len__"): der = [der] * self.npar
derI = hashD(der)
self.autoSetDS()
if derI <= 0 and self.As[0] is None:
if self.verbosity >= 20:
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))
+ self.As[0] = (fenics2Sparse(a0Re, parsRe, DirichletBC0, 1)
+ + 1.j * fenics2Sparse(a0Im, parsIm, DirichletBC0, 0))
if self.verbosity >= 20:
verbosityDepth("DEL", "Done assembling operator term.",
timestamp = self.timestamp)
return self._assembleA(mu, der, derI)
def b(self, mu : paramVal = [], der : List[int] = 0,
homogeneized : bool = False) -> Np1D:
"""Assemble (derivative of) RHS of linear system."""
mu = self.checkParameter(mu)
if not hasattr(der, "__len__"): der = [der] * self.npar
derI = hashD(der)
nbsTot = self.nbsH if homogeneized else self.nbs
bs = self.bsH if homogeneized else self.bs
if homogeneized and self.mu != self.mu0BC:
self.liftDirichletData(self.mu)
fenZEROSEff = fenZEROS(self.V.mesh().topology().dim())
for j in range(derI, nbsTot):
if bs[j] is None:
self.autoSetDS()
if self.verbosity >= 20:
verbosityDepth("INIT", ("Assembling forcing term "
"b{}.").format(j),
timestamp = self.timestamp)
if j == 0:
u0Re, u0Im = self.DirichletDatum
fRe, fIm = self.forcingTerm
g1Re, g1Im = self.NeumannDatum
g2Re, g2Im = self.RobinDatumG
else:
u0Re, u0Im = fenZEROSEff, fenZEROSEff
fRe, fIm = fenZEROSEff, fenZEROSEff
g1Re, g1Im = fenZEROSEff, fenZEROSEff
g2Re, g2Im = fenZEROSEff, fenZEROSEff
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)
DBCR = fen.DirichletBC(self.V, u0Re, self.DirichletBoundary)
DBCI = fen.DirichletBC(self.V, u0Im, self.DirichletBoundary)
- DBCR.apply(b0Re)
- DBCI.apply(b0Im)
- b = np.array(b0Re[:] + 1.j * b0Im[:], dtype = np.complex)
+ b = (fenics2Vector(L0Re, parsRe, DBCR, 1)
+ + 1.j * fenics2Vector(L0Im, parsIm, DBCI, 1))
if homogeneized:
Ader = self.A(self.mu0, hashI(j, self.npar))
b -= Ader.dot(self.liftedDirichletDatum)
if homogeneized:
self.bsH[j] = b
else:
self.bs[j] = b
if self.verbosity >= 20:
verbosityDepth("DEL", "Done assembling forcing term.",
timestamp = self.timestamp)
return self._assembleb(mu, der, derI, homogeneized, self.mu0)
diff --git a/rrompy/reduction_methods/base/generic_approximant.py b/rrompy/reduction_methods/base/generic_approximant.py
index 0a604b8..25f2a72 100644
--- a/rrompy/reduction_methods/base/generic_approximant.py
+++ b/rrompy/reduction_methods/base/generic_approximant.py
@@ -1,909 +1,884 @@
# 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 itertools import product as iterprod
from copy import deepcopy as copy
from os import remove as osrm
from rrompy.sampling.linear_problem import (SamplingEngineLinear,
SamplingEngineLinearPOD)
-from rrompy.utilities.base.types import (Np1D, DictAny, HFEng, List, ListAny,
- strLst, paramVal, paramList, sampList)
+from rrompy.utilities.base.types import (Np1D, DictAny, HFEng, List, Tuple,
+ ListAny, strLst, paramVal, paramList,
+ sampList)
from rrompy.utilities.base import purgeDict, verbosityDepth, getNewFilename
from rrompy.utilities.exception_manager import (RROMPyException, RROMPyAssert,
RROMPy_READY, RROMPy_FRAGILE)
from rrompy.utilities.base import pickleDump, pickleLoad
from rrompy.parameter import (emptyParameterList, checkParameter,
checkParameterList)
from rrompy.sampling import sampleList, emptySampleList
__all__ = ['GenericApproximant']
def addNormFieldToClass(self, fieldName):
- def objFunc(self, mu:paramList, homogeneized : bool = False) -> float:
+ def objFunc(self, mu:paramList, homogeneized : bool = False) -> Np1D:
uV = getattr(self.__class__, "get" + fieldName)(self, mu, homogeneized)
val = self.HFEngine.norm(uV)
return val
setattr(self.__class__, "norm" + fieldName, objFunc)
def addPlotFieldToClass(self, fieldName):
- def objFunc(self, mu:paramList, name : str = fieldName, save : str = None,
- what : strLst = 'all', saveFormat : str = "eps",
- saveDPI : int = 100, show : bool = True,
- homogeneized : bool = False, **figspecs):
+ def objFunc(self, mu:paramList, *args, homogeneized : bool = False,
+ **kwargs):
uV = getattr(self.__class__, "get" + fieldName)(self, mu, homogeneized)
+ kwargsCopy = copy(kwargs)
for j, u in enumerate(uV):
- self.HFEngine.plot(u, name = name + str(j), save = save,
- what = what, saveFormat = saveFormat,
- saveDPI = saveDPI, show = show, **figspecs)
+ if "name" in kwargs.keys():
+ kwargsCopy["name"] = kwargs["name"] + str(j)
+ self.HFEngine.plot(u, *args, **kwargs)
setattr(self.__class__, "plot" + fieldName, objFunc)
def addOutParaviewFieldToClass(self, fieldName):
- def objFunc(self, mu:paramVal, name : str = fieldName,
- filename : str = "out", time : float = 0.,
- what : strLst = 'all', forceNewFile : bool = True,
- folder : bool = False, filePW = None,
- homogeneized : bool = False):
+ def objFunc(self, mu:paramVal, *args, homogeneized : bool = False,
+ **kwargs):
if not hasattr(self.HFEngine, "outParaview"):
raise RROMPyException(("High fidelity engine cannot output to "
"Paraview."))
uV = getattr(self.__class__, "get" + fieldName)(self, mu, homogeneized)
+ kwargsCopy = copy(kwargs)
for j, u in enumerate(uV):
- self.HFEngine.outParaview(u, name = name + str(j),
- filename = filename, time = time,
- what = what, forceNewFile = forceNewFile,
- folder = folder, filePW = filePW)
+ if "name" in kwargs.keys():
+ kwargsCopy["name"] = kwargs["name"] + str(j)
+ self.HFEngine.outParaview(u, *args, **kwargsCopy)
setattr(self.__class__, "outParaview" + fieldName, objFunc)
def addOutParaviewTimeDomainFieldToClass(self, fieldName):
- def objFunc(self, mu:paramVal, omega : float = None,
- timeFinal : float = None, periodResolution : int = 20,
- name : str = fieldName, filename : str = "out",
- forceNewFile : bool = True, folder : bool = False,
- homogeneized : bool = False):
+ def objFunc(self, mu:paramVal, *args,
+ homogeneized : bool = False, **kwargs):
if not hasattr(self.HFEngine, "outParaviewTimeDomain"):
raise RROMPyException(("High fidelity engine cannot output to "
"Paraview."))
uV = getattr(self.__class__, "get" + fieldName)(self, mu, homogeneized)
- if omega is None: omega = np.real(mu)
+ omega = args.pop(0) if len(args) > 0 else np.real(mu)
+ kwargsCopy = copy(kwargs)
for j, u in enumerate(uV):
- self.HFEngine.outParaviewTimeDomain(u, omega = omega,
- timeFinal = timeFinal,
- periodResolution = periodResolution,
- name = name + str(j),
- filename = filename,
- forceNewFile = forceNewFile,
- folder = folder)
+ if "name" in kwargs.keys():
+ kwargsCopy["name"] = kwargs["name"] + str(j)
+ self.HFEngine.outParaviewTimeDomain(u, omega, *args,
+ **kwargsCopy)
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;
- 'S': total number of samples current approximant relies upon.
Defaults to empty dict.
homogeneized(optional): 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{Soft,Critical}.
parameterListSoft: Recognized keys of soft approximant parameters:
- 'POD': whether to compute POD of snapshots.
parameterListCritical: Recognized keys of critical approximant
parameters:
- 'S': total number of samples current approximant relies upon.
verbosity: Verbosity level.
POD: Whether to compute POD of snapshots.
S: Number of solution snapshots over which current approximant is
based upon.
samplingEngine: Sampling engine.
uHF: High fidelity solution(s) with parameter(s) lastSolvedHF as
sampleList.
lastSolvedHF: Parameter(s) corresponding to last computed high fidelity
solution(s) as parameterList.
- uAppReduced: Reduced approximate solution(s) with parameter(s)
- lastSolvedApp as sampleList.
- lastSolvedAppReduced: Parameter(s) corresponding to last computed
+ uApproxReduced: Reduced approximate solution(s) with parameter(s)
+ lastSolvedApprox as sampleList.
+ lastSolvedApproxReduced: Parameter(s) corresponding to last computed
reduced approximate solution(s) as parameterList.
- uApp: Approximate solution(s) with parameter(s) lastSolvedApp as
+ uApprox: Approximate solution(s) with parameter(s) lastSolvedApprox as
sampleList.
- lastSolvedApp: Parameter(s) corresponding to last computed approximate
- solution(s) as parameterList.
+ lastSolvedApprox: Parameter(s) corresponding to last computed
+ approximate solution(s) as parameterList.
"""
__all__ += [ftype + dtype for ftype, dtype in iterprod(
["norm", "plot", "outParaview", "outParaviewTimeDomain"],
["HF", "RHS", "Approx", "Res", "Err"])]
def __init__(self, HFEngine:HFEng, mu0 : paramVal = None,
approxParameters : DictAny = {}, homogeneized : bool = False,
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()),
timestamp = self.timestamp)
self._HFEngine = HFEngine
self.trainedModel = None
self.lastSolvedHF = emptyParameterList()
self.uHF = emptySampleList()
self._addParametersToList(["POD"], [True], ["S"], [[1]])
if mu0 is None:
if hasattr(self.HFEngine, "mu0"):
self.mu0 = checkParameter(self.HFEngine.mu0)
else:
raise RROMPyException(("Center of approximation cannot be "
"inferred from HF engine. Parameter "
"required"))
else:
self.mu0 = checkParameter(mu0, self.HFEngine.npar)
self.resetSamples()
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"]:
+ for objName in ["HF", "RHS", "Err"]:
addNormFieldToClass(self, objName)
+ def objFunc(self, mu:paramList, homogeneized : bool = False) -> Np1D:
+# uV = getattr(self.__class__, "getRes")(self, mu, homogeneized,
+# duality = False)
+ uV = self.getRes(mu, homogeneized, duality = False)
+ val = self.HFEngine.norm(uV, dual = True, duality = False)
+ return val
+ setattr(self.__class__, "normRes", objFunc)
+ if not hasattr(self, "normApprox"):
+ addNormFieldToClass(self, "Approx")
### 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.
show(optional): Whether to show figure. Defaults to True.
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
@property
def parameterList(self):
"""Value of parameterListSoft + parameterListCritical."""
return self.parameterListSoft + self.parameterListCritical
def _addParametersToList(self, whatSoft:strLst, defaultSoft:ListAny,
whatCritical : strLst = [],
defaultCritical : ListAny = [],
toBeExcluded : strLst = []):
if not hasattr(self, "parameterToBeExcluded"):
self.parameterToBeExcluded = []
self.parameterToBeExcluded += toBeExcluded
if not hasattr(self, "parameterListSoft"):
self.parameterListSoft = []
if not hasattr(self, "parameterDefaultSoft"):
self.parameterDefaultSoft = {}
if not hasattr(self, "parameterListCritical"):
self.parameterListCritical = []
if not hasattr(self, "parameterDefaultCritical"):
self.parameterDefaultCritical = {}
for j, what in enumerate(whatSoft):
if what not in self.parameterToBeExcluded:
self.parameterListSoft += [what]
self.parameterDefaultSoft[what] = defaultSoft[j]
for j, what in enumerate(whatCritical):
if what not in self.parameterToBeExcluded:
self.parameterListCritical += [what]
self.parameterDefaultCritical[what] = defaultCritical[j]
def _postInit(self):
if self.depth == 0:
if self.verbosity >= 10:
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):
"""Setup sampling engine."""
RROMPyAssert(self._mode, message = "Cannot setup sampling engine.")
if not hasattr(self, "_POD") or self._POD is None: return
if self.POD:
SamplingEngine = SamplingEngineLinearPOD
else:
SamplingEngine = SamplingEngineLinear
self.samplingEngine = SamplingEngine(self.HFEngine,
- verbosity = self.verbosity)
+ verbosity = self.verbosity,
+ allowRepeatedSamples = True)
@property
def HFEngine(self):
"""Value of HFEngine."""
return self._HFEngine
@HFEngine.setter
def HFEngine(self, HFEngine):
raise RROMPyException("Cannot change HFEngine.")
@property
def mu0(self):
"""Value of mu0."""
return self._mu0
@mu0.setter
def mu0(self, mu0):
mu0 = checkParameter(mu0)
if not hasattr(self, "_mu0") or mu0 != self.mu0:
self.resetSamples()
self._mu0 = mu0
@property
def npar(self):
"""Number of parameters."""
return self.mu0.shape[1]
@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())
for key in self.parameterListCritical:
if key in keyList:
setattr(self, "_" + key, self.parameterDefaultCritical[key])
for key in self.parameterListSoft:
if key in keyList:
setattr(self, "_" + key, self.parameterDefaultSoft[key])
fragile = False
for key in self.parameterListCritical:
if key in keyList:
val = approxParameters[key]
else:
val = getattr(self, "_" + key, None)
if val is None:
val = self.parameterDefaultCritical[key]
getattr(self.__class__, key, None).fset(self, val)
fragile = fragile or val is None
for key in self.parameterListSoft:
if key in keyList:
val = approxParameters[key]
else:
val = getattr(self, "_" + key, None)
if val is None:
val = self.parameterDefaultSoft[key]
getattr(self.__class__, key, None).fset(self, val)
if fragile:
self._mode = RROMPy_FRAGILE
@property
def POD(self):
"""Value of POD."""
return self._POD
@POD.setter
def POD(self, POD):
if hasattr(self, "_POD"): PODold = self.POD
else: PODold = -1
self._POD = POD
self._approxParameters["POD"] = self.POD
if PODold != self.POD:
self.samplingEngine = None
self.resetSamples()
@property
def S(self):
"""Value of S."""
return self._S
@S.setter
def S(self, S):
if not hasattr(S, "__len__"): S = [S]
if any([s <= 0 for s in S]):
raise RROMPyException("S must be positive.")
if hasattr(self, "_S") and self._S is not None: Sold = tuple(self.S)
else: Sold = -1
self._S = S
self._approxParameters["S"] = self.S
if Sold != tuple(self.S):
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()
@property
def trainedModel(self):
"""Value of trainedModel."""
return self._trainedModel
@trainedModel.setter
def trainedModel(self, trainedModel):
self._trainedModel = trainedModel
if self._trainedModel is not None:
- self._trainedModel.lastSolvedAppReduced = emptyParameterList()
- self._trainedModel.lastSolvedApp = emptyParameterList()
- self.lastSolvedAppReduced = emptyParameterList()
- self.lastSolvedApp = emptyParameterList()
- self.uAppReduced = emptySampleList()
- self.uApp = emptySampleList()
+ self._trainedModel.lastSolvedApproxReduced = emptyParameterList()
+ self._trainedModel.lastSolvedApprox = emptyParameterList()
+ self.lastSolvedApproxReduced = emptyParameterList()
+ self.lastSolvedApprox = emptyParameterList()
+ self.uApproxReduced = emptySampleList()
+ self.uApprox = emptySampleList()
def resetSamples(self):
if hasattr(self, "samplingEngine") and self.samplingEngine is not None:
self.samplingEngine.resetHistory()
else:
self.setupSampling()
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.
"""
RROMPyAssert(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, 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).
"""
RROMPyAssert(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,
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.
"""
RROMPyAssert(self._mode, message = "Cannot output samples.")
self.samplingEngine.outParaviewTimeDomainSamples(omegas = omegas,
timeFinal = timeFinal,
periodResolution = periodResolution,
name = name, filename = filename,
forceNewFile = forceNewFile,
folders = folders)
def setSamples(self, samplingEngine):
"""Copy samplingEngine and samples."""
if self.verbosity >= 10:
verbosityDepth("INIT", "Transfering samples.",
timestamp = self.timestamp)
- self.samplingEngine = samplingEngine
+ self.samplingEngine = copy(samplingEngine)
if self.verbosity >= 10:
verbosityDepth("DEL", "Done transfering samples.",
timestamp = self.timestamp)
def setTrainedModel(self, model):
"""Deepcopy approximation from trained model."""
if hasattr(model, "storeTrainedModel"):
verb = model.verbosity
model.verbosity = 0
fileOut = model.storeTrainedModel()
model.verbosity = verb
else:
try:
fileOut = getNewFilename("trained_model", "pkl")
pickleDump(model.data.__dict__, fileOut)
except:
raise RROMPyException(("Failed to store model data. Parameter "
"model must have either "
"storeTrainedModel or "
"data.__dict__ properties."))
self.loadTrainedModel(fileOut)
osrm(fileOut)
@abstractmethod
def setupApprox(self):
"""
Setup approximant. (ABSTRACT)
Any specialization should include something like
if self.checkComputedApprox():
return
RROMPyAssert(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 self._mode == RROMPy_FRAGILE or (self.trainedModel is not None
and self.trainedModel.data.approxParameters == self.approxParameters)
- def setHF(self, muHF:paramList, uHF:sampleList,
- append : bool = False) -> List[int]:
- """Assign high fidelity solution."""
- newSolvedHF, _ = checkParameterList(muHF, self.npar)
- newuHF = sampleList(uHF)
- if append:
- self.lastSolvedHF.append(newSolvedHF)
- self.uHF.append(newuHF)
- return list(range(len(self.uHF) - len(newuHF), len(self.uHF)))
- self.lastSolvedHF, _ = checkParameterList(newSolvedHF, self.npar)
- self.uHF = sampleList(newuHF)
- return list(range(len(self.uHF)))
-
- def evalHF(self, mu:paramList, append : bool = False,
- prune : bool = True) -> List[int]:
- """
- Find high fidelity solution with original parameters and arbitrary
- parameter.
-
- Args:
- mu: Target parameter.
- append(optional): Whether to append new HF solutions to old ones.
- prune(optional): Whether to remove duplicates of already appearing
- HF solutions.
- """
+ def _pruneBeforeEval(self, mu:paramList, field:str, append:bool,
+ prune:bool) -> Tuple[paramList, Np1D, Np1D, bool]:
mu, _ = checkParameterList(mu, self.npar)
idx = np.empty(len(mu), dtype = np.int)
if prune:
jExtra = np.zeros(len(mu), dtype = bool)
- muKeep = emptyParameterList()
- muExtra = copy(muKeep)
+ muExtra = emptyParameterList()
+ lastSolvedMus = getattr(self, "lastSolved" + field)
+ if (len(mu) > 0 and len(mu) == len(lastSolvedMus)
+ and mu == lastSolvedMus):
+ idx = np.arange(len(mu), dtype = np.int)
+ return muExtra, jExtra, idx, True
+ muKeep = copy(muExtra)
for j in range(len(mu)):
- jPos = self.lastSolvedHF.find(mu[j])
+ jPos = lastSolvedMus.find(mu[j])
if jPos is not None:
idx[j] = jPos
muKeep.append(mu[j])
else:
jExtra[j] = True
muExtra.append(mu[j])
if len(muKeep) > 0 and not append:
- idx[~jExtra] = self.setHF(muKeep, self.uHF[idx[~jExtra]],
- append)
+ lastSolvedu = getattr(self, "u" + field)
+ idx[~jExtra] = getattr(self.__class__, "set" + field)(self,
+ muKeep, lastSolvedu[idx[~jExtra]], append)
append = True
else:
jExtra = np.ones(len(mu), dtype = bool)
muExtra = mu
+ return muExtra, jExtra, idx, append
+
+ def _setObject(self, mu:paramList, field:str, object:sampList,
+ append:bool) -> List[int]:
+ newMus, _ = checkParameterList(mu, self.npar)
+ newObj = sampleList(object)
+ if append:
+ getattr(self, "lastSolved" + field).append(newMus)
+ getattr(self, "u" + field).append(newObj)
+ Ltot = len(getattr(self, "u" + field))
+ return list(range(Ltot - len(newObj), Ltot))
+ setattr(self, "lastSolved" + field, copy(newMus))
+ setattr(self, "u" + field, copy(newObj))
+ return list(range(len(getattr(self, "u" + field))))
+
+ def setHF(self, muHF:paramList, uHF:sampleList,
+ append : bool = False) -> List[int]:
+ """Assign high fidelity solution."""
+ return self._setObject(muHF, "HF", uHF, append)
+
+ def evalHF(self, mu:paramList, append : bool = False,
+ prune : bool = True) -> List[int]:
+ """
+ Find high fidelity solution with original parameters and arbitrary
+ parameter.
+
+ Args:
+ mu: Target parameter.
+ append(optional): Whether to append new HF solutions to old ones.
+ prune(optional): Whether to remove duplicates of already appearing
+ HF solutions.
+ """
+ muExtra, jExtra, idx, append = self._pruneBeforeEval(mu, "HF", append,
+ prune)
if len(muExtra) > 0:
newuHFs = self.samplingEngine.solveLS(muExtra,
homogeneized = self.homogeneized)
idx[jExtra] = self.setHF(muExtra, newuHFs, append)
return list(idx)
- def setApproxReduced(self, muApp:paramList, uApp:sampleList,
+ def setApproxReduced(self, muApproxR:paramList, uApproxR:sampleList,
append : bool = False) -> List[int]:
"""Assign high fidelity solution."""
- newSolvedApp, _ = checkParameterList(muApp, self.npar)
- newuApp = sampleList(uApp)
- if append:
- self.lastSolvedAppReduced.append(newSolvedApp)
- self.uAppReduced.append(newuApp)
- return list(range(len(self.uAppReduced) - len(newuApp),
- len(self.uAppReduced)))
- self.lastSolvedAppReduced, _ = checkParameterList(newSolvedApp,
- self.npar)
- self.uAppReduced = sampleList(newuApp)
- return list(range(len(self.uAppReduced)))
+ return self._setObject(muApproxR, "ApproxReduced", uApproxR, append)
def evalApproxReduced(self, mu:paramList, append : bool = False,
prune : bool = True) -> List[int]:
"""
Evaluate reduced representation of approximant at arbitrary parameter.
Args:
mu: Target parameter.
+ append(optional): Whether to append new HF solutions to old ones.
+ prune(optional): Whether to remove duplicates of already appearing
+ HF solutions.
"""
self.setupApprox()
- mu, _ = checkParameterList(mu, self.npar)
- idx = np.empty(len(mu), dtype = np.int)
- if prune:
- jExtra = np.zeros(len(mu), dtype = bool)
- muKeep = emptyParameterList()
- muExtra = copy(muKeep)
- for j in range(len(mu)):
- jPos = self.lastSolvedAppReduced.find(mu[j])
- if jPos is not None:
- idx[j] = jPos
- muKeep.append(mu[j])
- else:
- jExtra[j] = True
- muExtra.append(mu[j])
- if len(muKeep) > 0 and not append:
- idx[~jExtra] = self.setApproxReduced(muKeep,
- self.uAppReduced[idx[~jExtra]],
- append)
- append = True
- else:
- jExtra = np.ones(len(mu), dtype = bool)
- muExtra = mu
+ muExtra, jExtra, idx, append = self._pruneBeforeEval(mu,
+ "ApproxReduced",
+ append, prune)
if len(muExtra) > 0:
- newuApps = self.trainedModel.getApproxReduced(muExtra)
- idx[jExtra] = self.setApproxReduced(muExtra, newuApps, append)
+ newuApproxs = self.trainedModel.getApproxReduced(muExtra)
+ idx[jExtra] = self.setApproxReduced(muExtra, newuApproxs, append)
return list(idx)
- def setApprox(self, muApp:paramList, uApp:sampleList,
+ def setApprox(self, muApprox:paramList, uApprox:sampleList,
append : bool = False) -> List[int]:
"""Assign high fidelity solution."""
- newSolvedApp, _ = checkParameterList(muApp, self.npar)
- newuApp = sampleList(uApp)
- if append:
- self.lastSolvedApp.append(newSolvedApp)
- self.uApp.append(newuApp)
- return list(range(len(self.uApp) - len(newuApp), len(self.uApp)))
- self.lastSolvedApp, _ = checkParameterList(newSolvedApp, self.npar)
- self.uApp = sampleList(newuApp)
- return list(range(len(self.uApp)))
+ return self._setObject(muApprox, "Approx", uApprox, append)
def evalApprox(self, mu:paramList, append : bool = False,
prune : bool = True) -> List[int]:
"""
Evaluate approximant at arbitrary parameter.
Args:
mu: Target parameter.
+ append(optional): Whether to append new HF solutions to old ones.
+ prune(optional): Whether to remove duplicates of already appearing
+ HF solutions.
"""
self.setupApprox()
- mu, _ = checkParameterList(mu, self.npar)
- idx = np.empty(len(mu), dtype = np.int)
- if prune:
- jExtra = np.zeros(len(mu), dtype = bool)
- muKeep = emptyParameterList()
- muExtra = copy(muKeep)
- for j in range(len(mu)):
- jPos = self.lastSolvedApp.find(mu[j])
- if jPos is not None:
- idx[j] = jPos
- muKeep.append(mu[j])
- else:
- jExtra[j] = True
- muExtra.append(mu[j])
- if len(muKeep) > 0 and not append:
- idx[~jExtra] = self.setApprox(muKeep, self.uApp[idx[~jExtra]],
- append)
- append = True
- else:
- jExtra = np.ones(len(mu), dtype = bool)
- muExtra = mu
+ muExtra, jExtra, idx, append = self._pruneBeforeEval(mu, "Approx",
+ append, prune)
if len(muExtra) > 0:
- newuApps = self.trainedModel.getApprox(muExtra)
- idx[jExtra] = self.setApprox(muExtra, newuApps, append)
+ newuApproxs = self.trainedModel.getApprox(muExtra)
+ idx[jExtra] = self.setApprox(muExtra, newuApproxs, append)
return list(idx)
def getHF(self, mu:paramList, homogeneized : bool = False,
append : bool = False, prune : bool = True) -> sampList:
"""
Get HF solution at arbitrary parameter.
Args:
mu: Target parameter.
homogeneized(optional): Whether to remove Dirichlet BC. Defaults to
False.
Returns:
HFsolution.
"""
mu, _ = checkParameterList(mu, self.npar)
idx = self.evalHF(mu, append = append, prune = prune)
uHFs = self.uHF(idx)
if self.homogeneized and not homogeneized:
for j, m in enumerate(mu):
uHFs[j] += self.HFEngine.liftDirichletData(m)
if not self.homogeneized and homogeneized:
for j, m in enumerate(mu):
uHFs[j] -= self.HFEngine.liftDirichletData(m)
return uHFs
- def getRHS(self, mu:paramList, homogeneized : bool = False) -> sampList:
+ def getRHS(self, mu:paramList, homogeneized : bool = False,
+ duality : bool = True) -> sampList:
"""
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)
+ return self.HFEngine.residual(None, mu, homogeneized = homogeneized,
+ duality = duality)
def getApproxReduced(self, mu:paramList, append : bool = False,
prune : bool = True) -> sampList:
"""
Get approximant at arbitrary parameter.
Args:
mu: Target parameter.
Returns:
Reduced approximant.
"""
mu, _ = checkParameterList(mu, self.npar)
idx = self.evalApproxReduced(mu, append = append, prune = prune)
- uAppRs = self.uAppReduced(idx)
- return uAppRs
+ uApproxRs = self.uApproxReduced(idx)
+ return uApproxRs
def getApprox(self, mu:paramList, homogeneized : bool = False,
append : bool = False, prune : bool = True) -> sampList:
"""
Get approximant at arbitrary parameter.
Args:
mu: Target parameter.
homogeneized(optional): Whether to remove Dirichlet BC. Defaults to
False.
Returns:
Approximant.
"""
mu, _ = checkParameterList(mu, self.npar)
idx = self.evalApprox(mu, append = append, prune = prune)
- uApps = self.uApp(idx)
+ uApproxs = self.uApprox(idx)
if self.homogeneized and not homogeneized:
for j, m in enumerate(mu):
- uApps[j] += self.HFEngine.liftDirichletData(m)
+ uApproxs[j] += self.HFEngine.liftDirichletData(m)
if not self.homogeneized and homogeneized:
for j, m in enumerate(mu):
- uApps[j] -= self.HFEngine.liftDirichletData(m)
- return uApps
+ uApproxs[j] -= self.HFEngine.liftDirichletData(m)
+ return uApproxs
- def getRes(self, mu:paramList, homogeneized : bool = False) -> sampList:
+ def getRes(self, mu:paramList, homogeneized : bool = False,
+ duality : bool = True) -> sampList:
"""
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)
+ homogeneized = homogeneized,
+ duality = duality)
- def getErr(self, mu:paramList, homogeneized : bool = False) -> sampList:
+ def getErr(self, mu:paramList, homogeneized : bool = False,
+ append : bool = False, prune : bool = True) -> sampList:
"""
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)
+ return (self.getApprox(mu, homogeneized, append = append, prune =prune)
+ - self.getHF(mu, homogeneized, append = append, prune = prune))
def getPoles(self) -> Np1D:
"""
Obtain approximant poles.
Returns:
Numpy complex vector of poles.
"""
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) -> str:
"""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)
pickleDump(self.trainedModel.data.__dict__, filename)
if self.verbosity >= 20:
verbosityDepth("DEL", "Done storing trained model.",
timestamp = self.timestamp)
return filename
def loadTrainedModel(self, filename:str):
"""Load trained reduced model from file."""
if self.verbosity >= 20:
verbosityDepth("INIT", "Loading pre-trained model from file.",
timestamp = self.timestamp)
datadict = pickleLoad(filename)
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:
data.mus = datadict.pop("mus")
approxParameters = datadict.pop("approxParameters")
data.approxParameters = copy(approxParameters)
if "sampler" in approxParameters:
self._approxParameters["sampler"] = approxParameters.pop("sampler")
self.approxParameters = copy(approxParameters)
if "mus" in data.__dict__:
self.mus = copy(data.mus)
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/centered/generic_centered_approximant.py b/rrompy/reduction_methods/centered/generic_centered_approximant.py
index 48b9846..aea1c71 100644
--- a/rrompy/reduction_methods/centered/generic_centered_approximant.py
+++ b/rrompy/reduction_methods/centered/generic_centered_approximant.py
@@ -1,113 +1,113 @@
# 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 paramList
from rrompy.utilities.base import verbosityDepth
from rrompy.utilities.exception_manager import RROMPyAssert
__all__ = ['GenericCenteredApproximant']
class GenericCenteredApproximant(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;
- 'S': total number of samples current approximant relies upon.
Defaults to empty dict.
homogeneized(optional): 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.
parameterListSoft: Recognized keys of soft approximant parameters:
- 'POD': whether to compute POD of snapshots.
parameterListCritical: Recognized keys of critical approximant
parameters:
- 'S': total number of samples current approximant relies upon.
POD: Whether to compute QR factorization of derivatives.
S: Number of solution snapshots over which current approximant is
based upon.
initialHFData: HF problem initial data.
samplingEngine: Sampling engine.
uHF: High fidelity solution(s) with parameter(s) lastSolvedHF as
sampleList.
lastSolvedHF: Parameter(s) corresponding to last computed high fidelity
solution(s) as parameterList.
- uAppReduced: Reduced approximate solution(s) with parameter(s)
- lastSolvedApp as sampleList.
- lastSolvedAppReduced: Parameter(s) corresponding to last computed
+ uApproxReduced: Reduced approximate solution(s) with parameter(s)
+ lastSolvedApprox as sampleList.
+ lastSolvedApproxReduced: Parameter(s) corresponding to last computed
reduced approximate solution(s) as parameterList.
- uApp: Approximate solution(s) with parameter(s) lastSolvedApp as
+ uApprox: Approximate solution(s) with parameter(s) lastSolvedApprox as
sampleList.
- lastSolvedApp: Parameter(s) corresponding to last computed approximate
- solution(s) as parameterList.
+ lastSolvedApprox: Parameter(s) corresponding to last computed
+ approximate solution(s) as parameterList.
"""
@property
def S(self):
"""Value of S."""
return self._S
@S.setter
def S(self, S):
GenericApproximant.S.fset(self, S)
RROMPyAssert(len(self.S), 1, "Length of S")
def computeDerivatives(self):
"""Compute derivatives of solution map starting from order 0."""
RROMPyAssert(self._mode,
message = "Cannot start derivative computation.")
if self.samplingEngine.nsamples != np.prod(self.S):
if self.verbosity >= 5:
verbosityDepth("INIT", "Starting computation of derivatives.",
timestamp = self.timestamp)
self.samplingEngine.iterSample([self.mu0[0]] * np.prod(self.S),
homogeneized = self.homogeneized)
if self.verbosity >= 5:
verbosityDepth("DEL", "Done computing derivatives.",
timestamp = self.timestamp)
def normApprox(self, mu:paramList, 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), axis = 0)
+ return np.linalg.norm(self.getApproxReduced(mu).data, axis = 0)
diff --git a/rrompy/reduction_methods/centered/rational_pade.py b/rrompy/reduction_methods/centered/rational_pade.py
index 5356430..d02a3e6 100644
--- a/rrompy/reduction_methods/centered/rational_pade.py
+++ b/rrompy/reduction_methods/centered/rational_pade.py
@@ -1,440 +1,443 @@
# 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 import checkRobustTolerance
from rrompy.reduction_methods.trained_model import (TrainedModelData,
TrainedModelPade as tModel)
from .generic_centered_approximant import GenericCenteredApproximant
from rrompy.utilities.base.types import (Np1D, Np2D, Tuple, DictAny, HFEng,
paramVal, paramList, sampList)
from rrompy.utilities.base import verbosityDepth
from rrompy.utilities.poly_fitting.polynomial import (nextDerivativeIndices,
- hashDerivativeToIdx as hashD, hashIdxToDerivative as hashI)
+ hashDerivativeToIdx as hashD,
+ hashIdxToDerivative as hashI,
+ homogeneizedToFull)
from rrompy.utilities.exception_manager import (RROMPyException, RROMPyAssert,
RROMPyWarning)
__all__ = ['RationalPade']
class RationalPade(GenericCenteredApproximant):
"""
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;
- 'S': total number of samples current approximant relies upon;
- 'E': number of derivatives used to compute Pade'; defaults to 0;
- 'M': degree of Pade' approximant numerator; defaults to 0;
- 'N': degree of Pade' approximant denominator; defaults to 0;
- 'robustTol': tolerance for robust Pade' denominator management;
defaults to 0.
Defaults to empty dict.
homogeneized(optional): 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.
parameterListSoft: Recognized keys of soft approximant parameters:
- 'POD': whether to compute POD of snapshots;
- 'E': number of derivatives used to compute Pade';
- 'M': degree of Pade' approximant numerator;
- 'N': degree of Pade' approximant denominator;
- 'robustTol': tolerance for robust Pade' denominator management.
parameterListCritical: Recognized keys of critical approximant
parameters:
- 'S': total number of samples current approximant relies upon.
POD: Whether to compute QR factorization of derivatives.
S: Number of solution snapshots over which current approximant is
based upon.
M: Numerator degree of approximant.
N: Denominator degree of approximant.
robustTol: Tolerance for robust Pade' denominator management.
E: Complete derivative depth level of S.
uHF: High fidelity solution(s) with parameter(s) lastSolvedHF as
sampleList.
lastSolvedHF: Parameter(s) corresponding to last computed high fidelity
solution(s) as parameterList.
- uAppReduced: Reduced approximate solution(s) with parameter(s)
- lastSolvedApp as sampleList.
- lastSolvedAppReduced: Parameter(s) corresponding to last computed
+ uApproxReduced: Reduced approximate solution(s) with parameter(s)
+ lastSolvedApprox as sampleList.
+ lastSolvedApproxReduced: Parameter(s) corresponding to last computed
reduced approximate solution(s) as parameterList.
- uApp: Approximate solution(s) with parameter(s) lastSolvedApp as
+ uApprox: Approximate solution(s) with parameter(s) lastSolvedApprox as
sampleList.
- lastSolvedApp: Parameter(s) corresponding to last computed approximate
- solution(s) as parameterList.
+ lastSolvedApprox: Parameter(s) corresponding to last computed
+ approximate solution(s) as parameterList.
G: Square Numpy 2D vector of size (N+1) corresponding to Pade'
denominator matrix (see paper).
"""
def __init__(self, HFEngine:HFEng, mu0 : paramVal = None,
approxParameters : DictAny = {}, homogeneized : bool = False,
verbosity : int = 10, timestamp : bool = True):
self._preInit()
self._addParametersToList(["E", "M", "N", "robustTol"], [-1, 0, 0, 0])
super().__init__(HFEngine = HFEngine, mu0 = mu0,
approxParameters = approxParameters,
homogeneized = homogeneized,
verbosity = verbosity, timestamp = timestamp)
self._postInit()
@property
def M(self):
"""Value of M.."""
return self._M
@M.setter
def M(self, M):
if M < 0: raise RROMPyException("M must be non-negative.")
self._M = M
self._approxParameters["M"] = self.M
if hasattr(self, "_E") and self.E >= 0 and self.E < self.M:
RROMPyWarning("Prescribed E is too small. Decreasing M.")
self.M = self.E
@property
def N(self):
"""Value of N."""
return self._N
@N.setter
def N(self, N):
if N < 0: raise RROMPyException("N must be non-negative.")
self._N = N
self._approxParameters["N"] = self.N
if hasattr(self, "_E") and self.E >= 0 and self.E < self.N:
RROMPyWarning("Prescribed E is too small. Decreasing N.")
self.N = self.E
@property
def E(self):
"""Value of E."""
return self._E
@E.setter
def E(self, E):
if E < 0:
if not hasattr(self, "_S"):
raise RROMPyException(("Value of E must be positive if S is "
"not yet assigned."))
E = np.sum(hashI(np.prod(self.S), self.npar)) - 1
self._E = E
self._approxParameters["E"] = self.E
if (hasattr(self, "_S")
and self.E >= np.sum(hashI(np.prod(self.S), self.npar))):
RROMPyWarning("Prescribed S is too small. Decreasing E.")
self.E = -1
if hasattr(self, "_M"): self.M = self.M
if hasattr(self, "_N"): self.N = self.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.:
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):
GenericCenteredApproximant.S.fset(self, S)
if hasattr(self, "_M"): self.M = self.M
if hasattr(self, "_N"): self.N = self.N
if hasattr(self, "_E"): self.E = self.E
def _setupDenominator(self):
"""Compute Pade' denominator."""
RROMPyAssert(self._mode, message = "Cannot setup denominator.")
if self.verbosity >= 7:
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.N, self.robustTol)
if not newParameters:
break
self.approxParameters = newParameters
if self.N <= 0:
eV = np.ones((1, 1))
- q = np.zeros(tuple([self.N + 1] * self.npar), dtype = np.complex)
- for j in range(eV.shape[0]):
- q[tuple(hashI(j, self.npar))] = eV[j, 0]
+ q = homogeneizedToFull(tuple([self.N + 1] * self.npar), self.npar,
+ eV[:, 0])
if self.verbosity >= 7:
verbosityDepth("DEL", "Done computing denominator.",
timestamp = self.timestamp)
return q
def _setupNumerator(self):
"""Compute Pade' numerator."""
RROMPyAssert(self._mode, message = "Cannot setup numerator.")
if self.verbosity >= 7:
verbosityDepth("INIT", "Starting computation of numerator.",
timestamp = self.timestamp)
P = np.zeros(tuple([self.M + 1] * self.npar) + (np.prod(self.S),),
dtype = np.complex)
mEnd = hashD([self.M + 1] + [0] * (self.npar - 1))
nEnd = hashD([self.N + 1] + [0] * (self.npar - 1))
mnIdxs = nextDerivativeIndices([], self.npar, max(mEnd, nEnd))
for j in range(mEnd):
mIdx = mnIdxs[j]
for n in range(nEnd):
diffIdx = [x - y for (x, y) in zip(mIdx, mnIdxs[n])]
if all([x >= 0 for x in diffIdx]):
P[tuple(mIdx) + (hashD(diffIdx),)] = (
self.trainedModel.data.Q[tuple(mnIdxs[n])])
+ Pr = self.rescaleByParameter(P)
+ if self.POD:
+ Pr = np.tensordot(Pr, self.samplingEngine.RPOD.T,
+ axes = ([-1], [0]))
if self.verbosity >= 7:
verbosityDepth("DEL", "Done computation numerator.",
timestamp = self.timestamp)
- return self.rescaleByParameter(P).T
+ return Pr
def setupApprox(self):
"""
Compute Pade' approximant. SVD-based robust eigenvalue management.
"""
if self.checkComputedApprox():
return
RROMPyAssert(self._mode, message = "Cannot setup approximant.")
if self.verbosity >= 5:
verbosityDepth("INIT", "Setting up {}.". format(self.name()),
timestamp = self.timestamp)
self.computeDerivatives()
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,
None, self.HFEngine.rescalingExp)
data.polytype = "MONOMIAL"
+ data.polytypeP = "MONOMIAL"
self.trainedModel.data = data
else:
self.trainedModel = self.trainedModel
if self.N > 0:
Q = self._setupDenominator()
else:
self.setScaleParameter()
- Q = np.ones(1, dtype = np.complex)
+ Q = np.ones(tuple([1] * self.npar), dtype = np.complex)
self.trainedModel.data.Q = copy(Q)
self.trainedModel.data.scaleFactor = self.scaleFactor
self.trainedModel.data.projMat = copy(self.samplingEngine.samples(
list(range(np.prod(self.S)))))
- P = self._setupNumerator()
- if self.POD:
- P = np.tensordot(self.samplingEngine.RPOD, P, axes = ([-1], [0]))
- self.trainedModel.data.P = copy(P)
+ self.trainedModel.data.P = copy(self._setupNumerator())
self.trainedModel.data.approxParameters = copy(self.approxParameters)
if self.verbosity >= 5:
verbosityDepth("DEL", "Done setting up approximant.",
timestamp = self.timestamp)
def setScaleParameter(self) -> Np2D:
"""Compute parameter for rescaling."""
RROMPyAssert(self._mode, message = "Cannot compute rescaling factor.")
self.computeDerivatives()
self.scaleFactor = [1.] * self.npar
for d in range(self.npar):
hashesd = [0]
for n in range(1, self.E + 1):
hashesd += [hashD([0] * (d - 1) + [n]
+ [0] * (self.npar - d - 1))]
if self.POD:
Rd = self.samplingEngine.RPOD[: hashesd[-1] + 1, hashesd]
Gd = np.diag(Rd.T.conj().dot(Rd))
else:
DerEd = self.samplingEngine.samples(hashesd)
Gd = self.HFEngine.norm(DerEd)
if len(Gd) > 1:
scaleCoeffs = np.polyfit(np.arange(len(Gd)), np.log(Gd), 1)
self.scaleFactor[d] = np.exp(- scaleCoeffs[0] / 2.)
def rescaleByParameter(self, R:Np2D) -> Np2D:
"""
Rescale by scale parameter.
Args:
R: Matrix whose columns need rescaling.
Returns:
Rescaled matrix.
"""
RIdxs = nextDerivativeIndices([], self.npar, R.shape[-1])
Rscaled = copy(R)
for j, RIdx in enumerate(RIdxs):
Rscaled[..., j] *= np.prod([x ** y for (x, y) in
zip(self.scaleFactor, RIdx)])
return Rscaled
def buildG(self):
"""Assemble Pade' denominator matrix."""
RROMPyAssert(self._mode, message = "Cannot compute G matrix.")
self.computeDerivatives()
if self.verbosity >= 10:
verbosityDepth("INIT", "Building gramian matrix.",
timestamp = self.timestamp)
eStart = hashD([self.E] + [0] * (self.npar - 1))
eEnd = hashD([self.E + 1] + [0] * (self.npar - 1))
eIdxs = [hashI(e, self.npar) for e in range(eStart, eEnd)]
nEnd = hashD([self.N + 1] + [0] * (self.npar - 1))
nIdxs = nextDerivativeIndices([], self.npar, nEnd)
self.setScaleParameter()
if self.POD:
RPODE = self.rescaleByParameter(self.samplingEngine.RPOD[: eEnd,
: eEnd])
else:
DerE = self.rescaleByParameter(self.samplingEngine.samples(
list(range(eEnd))).data)
self.G = np.zeros((nEnd, nEnd), dtype = np.complex)
for eIdx in eIdxs:
nLoc = []
samplesIdxs = []
for n, nIdx in enumerate(nIdxs):
diffIdx = [x - y for (x, y) in zip(eIdx, nIdx)]
if all([x >= 0 for x in diffIdx]):
nLoc += [n]
samplesIdxs += [hashD(diffIdx)]
if self.POD:
RPODELoc = RPODE[: samplesIdxs[-1] + 1, samplesIdxs]
GLoc = RPODELoc.T.conj().dot(RPODELoc)
else:
DerELoc = DerE[:, samplesIdxs]
GLoc = self.HFEngine.innerProduct(DerELoc, DerELoc)
for j in range(len(nLoc)):
self.G[nLoc[j], nLoc] = self.G[nLoc[j], nLoc] + GLoc[j]
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.
"""
RROMPyAssert(self._mode, message = "Cannot solve eigenvalue problem.")
self.buildG()
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 >= 5:
try: condev = ev[-1] / ev[0]
except: condev = np.inf
verbosityDepth("MAIN", ("Solved eigenvalue problem of size {} "
"with condition number {:.4e}.").format(
self.G.shape[0],
condev),
timestamp = self.timestamp)
if self.verbosity >= 7:
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.
Returns:
Eigenvalues in ascending order and corresponding eigenvector
matrix.
"""
RROMPyAssert(self._mode, message = "Cannot solve eigenvalue problem.")
RROMPyAssert(self.POD, True, "POD value")
self.computeDerivatives()
eStart = hashD([self.E] + [0] * (self.npar - 1))
eEnd = hashD([self.E + 1] + [0] * (self.npar - 1))
eIdxs = [hashI(e, self.npar) for e in range(eStart, eEnd)]
nEnd = hashD([self.N + 1] + [0] * (self.npar - 1))
nIdxs = nextDerivativeIndices([], self.npar, nEnd)
self.setScaleParameter()
RPODE = self.rescaleByParameter(self.samplingEngine.RPOD[: eEnd,
: eEnd])
Rstack = np.zeros((RPODE.shape[0] * (eEnd - eStart), nEnd),
dtype = np.complex)
for k, eIdx in enumerate(eIdxs):
nLoc = []
samplesIdxs = []
for n, nIdx in enumerate(nIdxs):
diffIdx = [x - y for (x, y) in zip(eIdx, nIdx)]
if all([x >= 0 for x in diffIdx]):
nLoc += [n]
samplesIdxs += [hashD(diffIdx)]
RPODELoc = RPODE[:, samplesIdxs]
for j in range(len(nLoc)):
Rstack[k * RPODE.shape[0] : (k + 1) * RPODE.shape[0],
nLoc[j]] = RPODELoc[:, j]
if self.verbosity >= 7:
verbosityDepth("INIT", ("Solving svd for square root of "
"gramian matrix."),
timestamp = self.timestamp)
sizeI = Rstack.shape
_, s, V = np.linalg.svd(Rstack, full_matrices = False)
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,
condev),
timestamp = self.timestamp)
if self.verbosity >= 7:
verbosityDepth("DEL", "Done solving eigenvalue problem.",
timestamp = self.timestamp)
return s[::-1], eV
def centerNormalize(self, mu : paramList = [],
mu0 : paramVal = None) -> paramList:
"""
Compute normalized parameter to be plugged into approximant.
Args:
mu: Parameter(s) 1.
mu0: Parameter(s) 2. If None, set to self.mu0.
Returns:
Normalized parameter.
"""
return self.trainedModel.centerNormalize(mu, mu0)
def getResidues(self) -> sampList:
"""
Obtain approximant residues.
Returns:
Matrix with residues as columns.
"""
return self.trainedModel.getResidues()
diff --git a/rrompy/reduction_methods/centered/rb_centered.py b/rrompy/reduction_methods/centered/rb_centered.py
index c690b2a..a646478 100644
--- a/rrompy/reduction_methods/centered/rb_centered.py
+++ b/rrompy/reduction_methods/centered/rb_centered.py
@@ -1,191 +1,222 @@
# 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 .generic_centered_approximant import GenericCenteredApproximant
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, Tuple, List, DictAny,
HFEng, paramVal, sampList)
from rrompy.utilities.base import verbosityDepth
from rrompy.utilities.exception_manager import (RROMPyException, RROMPyWarning,
RROMPyAssert)
__all__ = ['RBCentered']
class RBCentered(GenericCenteredApproximant):
"""
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;
- 'S': total number of samples current approximant relies upon;
- - 'R': rank for Galerkin projection; defaults to prod(S).
+ - 'R': rank for Galerkin projection; defaults to prod(S);
+ - 'PODTolerance': tolerance for snapshots POD; defaults to -1.
Defaults to empty dict.
homogeneized(optional): 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.
parameterListSoft: Recognized keys of soft approximant parameters:
- 'POD': whether to compute POD of snapshots;
- - 'R': rank for Galerkin projection.
+ - 'R': rank for Galerkin projection;
+ - 'PODTolerance': tolerance for snapshots POD.
parameterListCritical: Recognized keys of critical approximant
parameters:
- 'S': total number of samples current approximant relies upon.
POD: Whether to compute QR factorization of derivatives.
R: Rank for Galerkin projection.
+ PODTolerance: Tolerance for snapshots POD.
S: Number of solution snapshots over which current approximant is
based upon.
uHF: High fidelity solution(s) with parameter(s) lastSolvedHF as
sampleList.
lastSolvedHF: Parameter(s) corresponding to last computed high fidelity
solution(s) as parameterList.
- uAppReduced: Reduced approximate solution(s) with parameter(s)
- lastSolvedApp as sampleList.
- lastSolvedAppReduced: Parameter(s) corresponding to last computed
+ uApproxReduced: Reduced approximate solution(s) with parameter(s)
+ lastSolvedApprox as sampleList.
+ lastSolvedApproxReduced: Parameter(s) corresponding to last computed
reduced approximate solution(s) as parameterList.
- uApp: Approximate solution(s) with parameter(s) lastSolvedApp as
+ uApprox: Approximate solution(s) with parameter(s) lastSolvedApprox as
sampleList.
- lastSolvedApp: Parameter(s) corresponding to last computed approximate
- solution(s) as parameterList.
- 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.
+ lastSolvedApprox: Parameter(s) corresponding to last computed
+ approximate solution(s) as parameterList.
+ 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 : paramVal = None,
approxParameters : DictAny = {}, homogeneized : bool = False,
verbosity : int = 10, timestamp : bool = True):
self._preInit()
- self._addParametersToList(["R"], [1])
+ self._addParametersToList(["R", "PODTolerance"], [1, -1])
super().__init__(HFEngine = HFEngine, mu0 = mu0,
approxParameters = approxParameters,
homogeneized = homogeneized,
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):
GenericCenteredApproximant.S.fset(self, S)
if not hasattr(self, "_R"): self._R = np.prod(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 RROMPyException("R must be non-negative.")
self._R = R
self._approxParameters["R"] = self.R
if hasattr(self, "_S") and np.prod(self.S) < self.R:
- RROMPyWarning("Prescribed S is too small. Reducing R.")
+ RROMPyWarning("Prescribed S is too small. Decreasing R.")
self.R = np.prod(self.S)
+ @property
+ def PODTolerance(self):
+ """Value of PODTolerance."""
+ return self._PODTolerance
+ @PODTolerance.setter
+ def PODTolerance(self, PODTolerance):
+ self._PODTolerance = PODTolerance
+ self._approxParameters["PODTolerance"] = self.PODTolerance
+
def setupApprox(self):
"""Setup RB system."""
if self.checkComputedApprox():
return
RROMPyAssert(self._mode, message = "Cannot setup approximant.")
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:
- Sprod = np.prod(self.S)
- U, _, _ = np.linalg.svd(self.samplingEngine.RPOD[: Sprod,: Sprod])
- pMat = self.samplingEngine.samples(list(range(Sprod))).dot(
- U[:, : self.R])
+ U, s, _ = np.linalg.svd(self.samplingEngine.RPOD)
+ s = s ** 2.
else:
- pMat = self.samplingEngine.samples(list(range(self.R)))
-
+ Gramian = self.HFEngine.innerProduct(self.samplingEngine.samples,
+ self.samplingEngine.samples)
+ U, s, _ = np.linalg.svd(Gramian)
+ nsamples = self.samplingEngine.nsamples
+ snorm = np.cumsum(s[::-1]) / np.sum(s)
+ nPODTrunc = min(nsamples - np.argmax(snorm > self.PODTolerance),
+ self.R)
+ pMat = self.samplingEngine.samples.dot(U[:, : nPODTrunc])
+ if self.verbosity >= 5:
+ verbosityDepth("MAIN", ("Assembling {}x{} projection matrix from "
+ "{} samples.").format(*(pMat.shape),
+ nsamples),
+ 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,
pMat, self.HFEngine.rescalingExp)
data.thetaAs = self.HFEngine.affineWeightsA(self.mu0)
data.thetabs = self.HFEngine.affineWeightsb(self.mu0,
self.homogeneized)
self.trainedModel.data = data
else:
self.trainedModel = self.trainedModel
self.trainedModel.data.projMat = copy(pMat)
ARBs, bRBs = self.assembleReducedSystem(pMat)
self.trainedModel.data.ARBs = ARBs
self.trainedModel.data.bRBs = bRBs
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, pMat : sampList = None,
pMatOld : sampList = 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)
- 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)
+ 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/distributed/generic_distributed_approximant.py b/rrompy/reduction_methods/distributed/generic_distributed_approximant.py
index 13ac033..11c620f 100644
--- a/rrompy/reduction_methods/distributed/generic_distributed_approximant.py
+++ b/rrompy/reduction_methods/distributed/generic_distributed_approximant.py
@@ -1,167 +1,167 @@
# 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 deepcopy as copy
from rrompy.reduction_methods.base.generic_approximant import (
GenericApproximant)
from rrompy.utilities.base.types import DictAny, HFEng, paramVal, paramList
from rrompy.utilities.base import verbosityDepth
from rrompy.utilities.exception_manager import RROMPyException, RROMPyAssert
from rrompy.parameter import checkParameterList
__all__ = ['GenericDistributedApproximant']
class GenericDistributedApproximant(GenericApproximant):
"""
ROM 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 empty dict.
homogeneized(optional): 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.
parameterListSoft: Recognized keys of soft approximant parameters:
- 'POD': whether to compute POD of snapshots.
parameterListCritical: Recognized keys of critical approximant
parameters:
- 'S': total number of samples current approximant relies upon;
- 'sampler': sample point generator.
POD: Whether to compute POD of snapshots.
S: Number of solution snapshots over which current approximant is
based upon.
sampler: Sample point generator.
muBounds: list of bounds for parameter values.
samplingEngine: Sampling engine.
uHF: High fidelity solution(s) with parameter(s) lastSolvedHF as
sampleList.
lastSolvedHF: Parameter(s) corresponding to last computed high fidelity
solution(s) as parameterList.
- uAppReduced: Reduced approximate solution(s) with parameter(s)
- lastSolvedApp as sampleList.
- lastSolvedAppReduced: Parameter(s) corresponding to last computed
+ uApproxReduced: Reduced approximate solution(s) with parameter(s)
+ lastSolvedApprox as sampleList.
+ lastSolvedApproxReduced: Parameter(s) corresponding to last computed
reduced approximate solution(s) as parameterList.
- uApp: Approximate solution(s) with parameter(s) lastSolvedApp as
+ uApprox: Approximate solution(s) with parameter(s) lastSolvedApprox as
sampleList.
- lastSolvedApp: Parameter(s) corresponding to last computed approximate
- solution(s) as parameterList.
+ lastSolvedApprox: Parameter(s) corresponding to last computed
+ approximate solution(s) as parameterList.
"""
def __init__(self, HFEngine:HFEng, mu0 : paramVal = None,
approxParameters : DictAny = {}, homogeneized : bool = False,
verbosity : int = 10, timestamp : bool = True):
self._preInit()
from rrompy.parameter.parameter_sampling import QuadratureSampler as QS
self._addParametersToList([], [], ["sampler"],
[QS([[0], [1]], "UNIFORM")])
del QS
super().__init__(HFEngine = HFEngine, mu0 = mu0,
approxParameters = approxParameters,
homogeneized = homogeneized,
verbosity = verbosity, timestamp = timestamp)
self._postInit()
@property
def mus(self):
"""Value of mus. Its assignment may reset snapshots."""
return self._mus
@mus.setter
def mus(self, mus):
mus, _ = checkParameterList(mus, self.npar)
musOld = copy(self.mus) if hasattr(self, '_mus') else None
if (musOld is None or len(mus) != len(musOld) or not mus == musOld):
self.resetSamples()
self._mus = mus
@property
def muBounds(self):
"""Value of muBounds."""
return self.sampler.lims
@property
def sampler(self):
"""Value of sampler."""
return self._sampler
@sampler.setter
def sampler(self, sampler):
if 'generatePoints' not in dir(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.__str__()
if not 'samplerOld' in locals() or samplerOld != self.sampler:
self.resetSamples()
def setSamples(self, samplingEngine):
"""Copy samplingEngine and samples."""
super().setSamples(samplingEngine)
self.mus = copy(self.samplingEngine.mus)
def computeSnapshots(self):
"""Compute snapshots of solution map."""
RROMPyAssert(self._mode,
message = "Cannot start snapshot computation.")
if self.samplingEngine.nsamples != np.prod(self.S):
if self.verbosity >= 5:
verbosityDepth("INIT", "Starting computation of snapshots.",
timestamp = self.timestamp)
self.mus = self.sampler.generatePoints(self.S)
self.samplingEngine.iterSample(self.mus,
homogeneized = self.homogeneized)
if self.verbosity >= 5:
verbosityDepth("DEL", "Done computing snapshots.",
timestamp = self.timestamp)
def normApprox(self, mu:paramList, 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), axis = 0)
+ return np.linalg.norm(self.getApproxReduced(mu).data, axis = 0)
def computeScaleFactor(self):
"""Compute parameter rescaling factor."""
RROMPyAssert(self._mode, message = "Cannot compute rescaling factor.")
self.scaleFactor = .5 * np.abs(
self.muBounds[0] ** self.HFEngine.rescalingExp
- self.muBounds[1] ** self.HFEngine.rescalingExp)
diff --git a/rrompy/reduction_methods/distributed/rational_interpolant.py b/rrompy/reduction_methods/distributed/rational_interpolant.py
index 85a6af9..64846fc 100644
--- a/rrompy/reduction_methods/distributed/rational_interpolant.py
+++ b/rrompy/reduction_methods/distributed/rational_interpolant.py
@@ -1,540 +1,611 @@
# 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 import checkRobustTolerance
from .generic_distributed_approximant import GenericDistributedApproximant
from rrompy.utilities.poly_fitting import customFit, customPInv
from rrompy.utilities.poly_fitting.polynomial import (polybases, polyfitname,
- nextDerivativeIndices,
- hashDerivativeToIdx as hashD,
- hashIdxToDerivative as hashI,
- homogeneizedpolyvander)
+ nextDerivativeIndices,
+ hashDerivativeToIdx as hashD,
+ hashIdxToDerivative as hashI,
+ homogeneizedpolyvander as hpvP,
+ homogeneizedToFull)
+from rrompy.utilities.poly_fitting.radial_basis import (rbbases,
+ radialFunction,
+ polyfitname as polyfitnameR,
+ homogeneizedpolyvander as hpvR)
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,
List, paramVal, paramList, sampList)
from rrompy.utilities.base import verbosityDepth, multifactorial
from rrompy.utilities.exception_manager import (RROMPyException, RROMPyAssert,
RROMPyWarning)
__all__ = ['RationalInterpolant']
class RationalInterpolant(GenericDistributedApproximant):
"""
ROM rational 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;
- 'sampler': sample point generator;
- 'polybasis': type of polynomial basis for interpolation; allowed
values include 'MONOMIAL', 'CHEBYSHEV' and 'LEGENDRE'; defaults
to 'MONOMIAL';
- 'E': number of derivatives used to compute interpolant; defaults
to 0;
- 'M': degree of Pade' interpolant numerator; defaults to 0;
- 'N': degree of Pade' interpolant denominator; defaults to 0;
+ - 'radialBasis': radial basis family for interpolant numerator;
+ defaults to 0, i.e. no radial basis;
+ - 'radialBasisWeights': radial basis weights for interpolant
+ numerator; defaults to 0, i.e. identity;
- 'interpRcond': tolerance for interpolation; defaults to None;
- 'robustTol': tolerance for robust Pade' denominator management;
defaults to 0.
Defaults to empty dict.
homogeneized(optional): 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.
parameterListSoft: Recognized keys of soft approximant parameters:
- 'POD': whether to compute POD of snapshots;
- 'polybasis': type of polynomial basis for interpolation;
- 'E': number of derivatives used to compute interpolant;
- 'M': degree of Pade' interpolant numerator;
- 'N': degree of Pade' interpolant denominator;
+ - 'radialBasis': radial basis family for interpolant numerator;
+ - 'radialBasisWeights': radial basis weights for interpolant
+ numerator;
- 'interpRcond': tolerance for interpolation via numpy.polyfit;
- 'robustTol': tolerance for robust Pade' denominator management.
parameterListCritical: Recognized keys of critical approximant
parameters:
- 'S': total number of samples current approximant relies upon;
- 'sampler': sample point generator.
POD: Whether to compute POD of snapshots.
S: Number of solution snapshots over which current approximant is
based upon.
sampler: Sample point generator.
polybasis: type of polynomial basis for interpolation.
M: Numerator degree of approximant.
N: Denominator degree of approximant.
+ radialBasis: Radial basis family for interpolant numerator.
+ radialBasisWeights: Radial basis weights for interpolant numerator.
interpRcond: Tolerance for interpolation via numpy.polyfit.
robustTol: Tolerance for robust Pade' denominator management.
E: Complete derivative depth level of S.
muBounds: list of bounds for parameter values.
samplingEngine: Sampling engine.
uHF: High fidelity solution(s) with parameter(s) lastSolvedHF as
sampleList.
lastSolvedHF: Parameter(s) corresponding to last computed high fidelity
solution(s) as parameterList.
- uAppReduced: Reduced approximate solution(s) with parameter(s)
- lastSolvedApp as sampleList.
- lastSolvedAppReduced: Parameter(s) corresponding to last computed
+ uApproxReduced: Reduced approximate solution(s) with parameter(s)
+ lastSolvedApprox as sampleList.
+ lastSolvedApproxReduced: Parameter(s) corresponding to last computed
reduced approximate solution(s) as parameterList.
- uApp: Approximate solution(s) with parameter(s) lastSolvedApp as
+ uApprox: Approximate solution(s) with parameter(s) lastSolvedApprox as
sampleList.
- lastSolvedApp: Parameter(s) corresponding to last computed approximate
- solution(s) as parameterList.
+ lastSolvedApprox: Parameter(s) corresponding to last computed
+ approximate solution(s) as parameterList.
Q: Numpy 1D vector containing complex coefficients of approximant
denominator.
P: Numpy 2D vector whose columns are FE dofs of coefficients of
approximant numerator.
"""
def __init__(self, HFEngine:HFEng, mu0 : paramVal = None,
approxParameters : DictAny = {}, homogeneized : bool = False,
verbosity : int = 10, timestamp : bool = True):
self._preInit()
- self._addParametersToList(["polybasis", "E", "M", "N", "interpRcond",
- "robustTol"], ["MONOMIAL", -1, 0, 0, -1, 0])
+ self._addParametersToList(["polybasis", "E", "M", "N", "radialBasis",
+ "radialBasisWeights", "interpRcond",
+ "robustTol"],
+ ["MONOMIAL", -1, 0, 0, 0, 1, -1, 0])
super().__init__(HFEngine = HFEngine, mu0 = mu0,
approxParameters = approxParameters,
homogeneized = homogeneized,
verbosity = verbosity, timestamp = timestamp)
self._postInit()
@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._polybasis = "MONOMIAL"
self._approxParameters["polybasis"] = self.polybasis
+ @property
+ def radialBasis(self):
+ """Value of radialBasis."""
+ return self._radialBasis
+ @radialBasis.setter
+ def radialBasis(self, radialBasis):
+ try:
+ if radialBasis != 0:
+ radialBasis = radialBasis.upper().strip().replace(" ","")
+ if radialBasis not in rbbases:
+ raise RROMPyException(("Prescribed radialBasis not "
+ "recognized."))
+ self._radialBasis = radialBasis
+ except:
+ RROMPyWarning(("Prescribed radialBasis not recognized. Overriding "
+ "to 0."))
+ self._radialBasis = 0
+ self._approxParameters["radialBasis"] = self.radialBasis
+
+ @property
+ def polybasisP(self):
+ if self.radialBasis == 0:
+ return self._polybasis
+ return self._polybasis + "_" + self.radialBasis
+
@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 radialBasisWeights(self):
+ """Value of radialBasisWeights."""
+ return self._radialBasisWeights
+ @radialBasisWeights.setter
+ def radialBasisWeights(self, radialBasisWeights):
+ self._radialBasisWeights = radialBasisWeights
+ self._approxParameters["radialBasisWeights"] = self.radialBasisWeights
+
@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 RROMPyException("M must be non-negative.")
self._M = M
self._approxParameters["M"] = self.M
if hasattr(self, "_E") and self.E >= 0 and self.E < self.M:
RROMPyWarning("Prescribed S is too small. Decreasing M.")
self.M = self.E
@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 RROMPyException("N must be non-negative.")
self._N = N
self._approxParameters["N"] = self.N
if hasattr(self, "_E") and self.E >= 0 and self.E < self.N:
RROMPyWarning("Prescribed S is too small. Decreasing N.")
self.N = self.E
@property
def E(self):
"""Value of E."""
return self._E
@E.setter
def E(self, E):
if E < 0:
if not hasattr(self, "_S"):
raise RROMPyException(("Value of E must be positive if S is "
"not yet assigned."))
E = np.sum(hashI(np.prod(self.S), self.npar)) - 1
self._E = E
self._approxParameters["E"] = self.E
if (hasattr(self, "_S")
and self.E >= np.sum(hashI(np.prod(self.S), self.npar))):
RROMPyWarning("Prescribed S is too small. Decreasing E.")
self.E = -1
if hasattr(self, "_M"): self.M = self.M
if hasattr(self, "_N"): self.N = self.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.:
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):
GenericDistributedApproximant.S.fset(self, S)
if hasattr(self, "_M"): self.M = self.M
if hasattr(self, "_N"): self.N = self.N
if hasattr(self, "_E"): self.E = self.E
def resetSamples(self):
"""Reset samples."""
super().resetSamples()
self._musUniqueCN = None
self._derIdxs = None
self._reorder = None
def _setupInterpolationIndices(self):
"""Setup parameters for polyvander."""
RROMPyAssert(self._mode,
message = "Cannot setup interpolation indices.")
if self._musUniqueCN is None or len(self._reorder) != len(self.mus):
self._musUniqueCN, musIdxsTo, musIdxs, musCount = (
self.centerNormalize(self.mus).unique(return_index = True,
return_inverse = True,
return_counts = True))
self._musUnique = self.mus[musIdxsTo]
self._derIdxs = [None] * len(self._musUniqueCN)
self._reorder = np.empty(len(musIdxs), dtype = int)
filled = 0
for j, cnt in enumerate(musCount):
self._derIdxs[j] = nextDerivativeIndices([], self.mus.shape[1],
cnt)
jIdx = np.nonzero(musIdxs == j)[0]
self._reorder[jIdx] = np.arange(filled, filled + cnt)
filled += cnt
def _setupDenominator(self):
"""Compute Pade' denominator."""
RROMPyAssert(self._mode, message = "Cannot setup denominator.")
if self.verbosity >= 7:
verbosityDepth("INIT", "Starting computation of denominator.",
timestamp = self.timestamp)
while self.N > 0:
invD = self._computeInterpolantInverseBlocks()
if self.POD:
ev, eV = self.findeveVGQR(self.samplingEngine.RPOD, invD)
else:
ev, eV = self.findeveVGExplicit(self.samplingEngine.samples,
invD)
newParams = checkRobustTolerance(ev, self.N, self.robustTol)
if not newParams:
break
self.approxParameters = newParams
if self.N <= 0:
self._N = 0
eV = np.ones((1, 1))
+ q = homogeneizedToFull(tuple([self.N + 1] * self.npar), self.npar,
+ eV[:, 0])
if self.verbosity >= 7:
verbosityDepth("DEL", "Done computing denominator.",
timestamp = self.timestamp)
- q = np.zeros(tuple([self.N + 1] * self.npar), dtype = eV.dtype)
- for j in range(eV.shape[0]):
- q[tuple(hashI(j, self.npar))] = eV[j, 0]
return q
def _setupNumerator(self):
"""Compute Pade' numerator."""
RROMPyAssert(self._mode, message = "Cannot setup numerator.")
if self.verbosity >= 7:
verbosityDepth("INIT", "Starting computation of numerator.",
timestamp = self.timestamp)
Qevaldiag = np.zeros((len(self.mus), len(self.mus)),
dtype = np.complex)
verb = self.trainedModel.verbosity
self.trainedModel.verbosity = 0
self._setupInterpolationIndices()
idxGlob = 0
for j, derIdxs in enumerate(self._derIdxs):
nder = len(derIdxs)
idxLoc = np.arange(len(self.mus))[(self._reorder >= idxGlob)
* (self._reorder < idxGlob + nder)]
idxGlob += nder
Qval = [0] * nder
for der in range(nder):
derIdx = hashI(der, self.npar)
Qval[der] = (self.trainedModel.getQVal(
self._musUnique[j], derIdx,
scl = np.power(self.scaleFactor, -1.))
/ multifactorial(derIdx))
for derU, derUIdx in enumerate(derIdxs):
for derQ, derQIdx in enumerate(derIdxs):
diffIdx = [x - y for (x, y) in zip(derUIdx, derQIdx)]
if all([x >= 0 for x in diffIdx]):
diffj = hashD(diffIdx)
Qevaldiag[idxLoc[derU], idxLoc[derQ]] = Qval[diffj]
+ if self.POD:
+ Qevaldiag = Qevaldiag.dot(self.samplingEngine.RPOD.T)
self.trainedModel.verbosity = verb
while self.M >= 0:
- fitVander, _, argIdxs = homogeneizedpolyvander(self._musUniqueCN,
- self.M, self.polybasis,
- self._derIdxs, self._reorder,
+ if self.radialBasis == 0:
+ fitVander, _, argIdxs = hpvP(self._musUniqueCN, self.M,
+ self.polybasisP, self._derIdxs,
+ self._reorder,
scl = np.power(self.scaleFactor, -1.))
+ fitnameEff = polyfitname(self.polybasisP)
+ nsamplesPrint = "{}".format(len(fitVander))
+ else:
+ fitVander, _, argIdxs = hpvR(self._musUniqueCN, self.M,
+ self.polybasisP, self._derIdxs,
+ self._reorder, self.radialBasisWeights,
+ scl = np.power(self.scaleFactor, -1.))
+ fitnameEff = polyfitnameR(self.polybasisP)
+ nConstraints = len(fitVander) - len(Qevaldiag)
+ if nConstraints > 0:
+ Qevaldiag = np.pad(Qevaldiag, ((0, nConstraints), (0, 0)),
+ "constant")
+ elif nConstraints < 0:
+ Qevaldiag = Qevaldiag[: len(fitVander)]
+ fitVander = fitVander[argIdxs]
+ nsamplesPrint = "{}+{}".format(len(self.mus),
+ len(fitVander) - len(self.mus))
fitVander = fitVander[:, argIdxs]
fitOut = customFit(fitVander, Qevaldiag, 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(
- fitVander.shape[0], self.M,
- polyfitname(self.polybasis),
- condfit),
+ nsamplesPrint, self.M,
+ fitnameEff, condfit),
timestamp = self.timestamp)
if fitOut[1][1] == fitVander.shape[1]:
P = fitOut[0]
break
RROMPyWarning("Polyfit is poorly conditioned. Reducing M by 1.")
self.M -= 1
if self.M < 0:
raise RROMPyException(("Instability in computation of numerator. "
"Aborting."))
+ if self.radialBasis == 0:
+ p = homogeneizedToFull(tuple([self.M + 1] * self.npar)
+ + (P.shape[1],), self.npar, P)
+ else:
+ pGlob = homogeneizedToFull(
+ tuple([self.M + 1] * self.npar) + (P.shape[1],),
+ self.npar, P[len(self.mus) :])
+ p = radialFunction(self._musUniqueCN[self._reorder],
+ self.radialBasisWeights,
+ P[: len(self.mus)], pGlob)
if self.verbosity >= 7:
verbosityDepth("DEL", "Done computing numerator.",
timestamp = self.timestamp)
- p = np.zeros(tuple([self.M + 1] * self.npar) + (P.shape[1],),
- dtype = P.dtype)
- for j in range(P.shape[0]):
- p[tuple(hashI(j, self.npar))] = P[j, :]
- return p.T
+ return p
def setupApprox(self):
"""
Compute Pade' interpolant.
SVD-based robust eigenvalue management.
"""
if self.checkComputedApprox():
return
RROMPyAssert(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,
self.samplingEngine.samples,
self.HFEngine.rescalingExp)
data.polytype = self.polybasis
+ data.polytypeP = self.polybasisP
data.scaleFactor = self.scaleFactor
data.mus = copy(self.mus)
self.trainedModel.data = data
else:
self.trainedModel = self.trainedModel
self.trainedModel.data.projMat = copy(self.samplingEngine.samples)
if self.N > 0:
Q = self._setupDenominator()
else:
- Q = np.ones(1, dtype = np.complex)
+ Q = np.ones(tuple([1] * self.npar), dtype = np.complex)
self.trainedModel.data.Q = copy(Q)
- P = self._setupNumerator()
- if self.POD:
- P = np.tensordot(self.samplingEngine.RPOD, P, axes = ([-1], [0]))
- self.trainedModel.data.P = copy(P)
+ self.trainedModel.data.P = copy(self._setupNumerator())
self.trainedModel.data.approxParameters = copy(self.approxParameters)
if self.verbosity >= 5:
verbosityDepth("DEL", "Done setting up approximant.",
timestamp = self.timestamp)
def _computeInterpolantInverseBlocks(self) -> List[Np2D]:
"""
Compute inverse factors for minimal interpolant target functional.
"""
RROMPyAssert(self._mode, message = "Cannot solve eigenvalue problem.")
self._setupInterpolationIndices()
while self.E >= 0:
eWidth = (hashD([self.E + 1] + [0] * (self.npar - 1))
- hashD([self.E] + [0] * (self.npar - 1)))
- TE, _, argIdxs = homogeneizedpolyvander(self._musUniqueCN,
- self.E, self.polybasis,
- self._derIdxs, self._reorder,
- scl = np.power(self.scaleFactor, -1.))
+ TE, _, argIdxs = hpvP(self._musUniqueCN, self.E, self.polybasis,
+ self._derIdxs, self._reorder,
+ scl = np.power(self.scaleFactor, -1.))
fitOut = customPInv(TE[:, argIdxs], rcond = self.interpRcond,
full = True)
if self.verbosity >= 5:
condfit = fitOut[1][1][0] / fitOut[1][1][-1]
verbosityDepth("MAIN", ("Fitting {} samples with degree {} "
"through {}... Conditioning of "
"pseudoinverse system: {:.4e}.")\
.format(TE.shape[0], self.E,
polyfitname(self.polybasis),
condfit),
timestamp = self.timestamp)
if fitOut[1][0] == len(argIdxs):
self._fitinv = fitOut[0][- eWidth : , :]
break
RROMPyWarning("Polyfit is poorly conditioned. Reducing E by 1.")
self.E -= 1
if self.E < 0:
raise RROMPyException(("Instability in computation of "
"denominator. Aborting."))
- TN, _, argIdxs = homogeneizedpolyvander(self._musUniqueCN, self.N,
- self.polybasis, self._derIdxs, self._reorder,
- scl = np.power(self.scaleFactor, -1.))
+ TN, _, argIdxs = hpvP(self._musUniqueCN, self.N, self.polybasis,
+ self._derIdxs, self._reorder,
+ scl = np.power(self.scaleFactor, -1.))
TN = TN[:, argIdxs]
invD = [None] * (eWidth)
for k in range(eWidth):
pseudoInv = np.diag(self._fitinv[k, :])
idxGlob = 0
for j, derIdxs in enumerate(self._derIdxs):
nder = len(derIdxs)
idxGlob += nder
if nder > 1:
idxLoc = np.arange(len(self.mus))[
(self._reorder >= idxGlob - nder)
* (self._reorder < idxGlob)]
invLoc = self._fitinv[k, idxLoc]
pseudoInv[np.ix_(idxLoc, idxLoc)] = 0.
for diffj, diffjIdx in enumerate(derIdxs):
for derQ, derQIdx in enumerate(derIdxs):
derUIdx = [x - y for (x, y) in
zip(diffjIdx, derQIdx)]
if all([x >= 0 for x in derUIdx]):
derU = hashD(derUIdx)
pseudoInv[idxLoc[derU], idxLoc[derQ]] = (
invLoc[diffj])
invD[k] = pseudoInv.dot(TN)
return invD
def findeveVGExplicit(self, sampleE:sampList,
invD:List[Np2D]) -> Tuple[Np1D, Np2D]:
"""
Compute explicitly eigenvalues and eigenvectors of Pade' denominator
matrix.
"""
RROMPyAssert(self._mode, message = "Cannot solve eigenvalue problem.")
nEnd = invD[0].shape[1]
eWidth = len(invD)
if self.verbosity >= 10:
verbosityDepth("INIT", "Building gramian matrix.",
timestamp = self.timestamp)
gramian = self.HFEngine.innerProduct(sampleE, sampleE)
G = np.zeros((nEnd, nEnd), dtype = np.complex)
for k in range(eWidth):
G += invD[k].T.conj().dot(gramian.dot(invD[k]))
if self.verbosity >= 10:
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(G)
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(nEnd, condev),
timestamp = self.timestamp)
if self.verbosity >= 7:
verbosityDepth("DEL", "Done solving eigenvalue problem.",
timestamp = self.timestamp)
return ev, eV
def findeveVGQR(self, RPODE:Np2D, invD:List[Np2D]) -> Tuple[Np1D, Np2D]:
"""
Compute eigenvalues and eigenvectors of Pade' denominator matrix
through SVD of R factor.
"""
RROMPyAssert(self._mode, message = "Cannot solve eigenvalue problem.")
nEnd = invD[0].shape[1]
S = RPODE.shape[0]
eWidth = len(invD)
if self.verbosity >= 10:
verbosityDepth("INIT", "Building half-gramian matrix stack.",
timestamp = self.timestamp)
Rstack = np.zeros((S * eWidth, nEnd), dtype = np.complex)
for k in range(eWidth):
Rstack[k * S : (k + 1) * S, :] = RPODE.dot(invD[k])
if self.verbosity >= 10:
verbosityDepth("DEL", "Done building half-gramian.",
timestamp = self.timestamp)
if self.verbosity >= 7:
verbosityDepth("INIT", ("Solving svd for square root of "
"gramian matrix."),
timestamp = self.timestamp)
_, s, eV = np.linalg.svd(Rstack, full_matrices = False)
ev = s[::-1]
eV = eV[::-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(*Rstack.shape, condev),
timestamp = self.timestamp)
if self.verbosity >= 7:
verbosityDepth("DEL", "Done solving svd.",
timestamp = self.timestamp)
return ev, eV
def centerNormalize(self, mu : paramList = [],
mu0 : paramVal = None) -> paramList:
"""
Compute normalized parameter to be plugged into approximant.
Args:
mu: Parameter(s) 1.
mu0: Parameter(s) 2. If None, set to self.mu0.
Returns:
Normalized parameter.
"""
return self.trainedModel.centerNormalize(mu, mu0)
def getResidues(self) -> Np1D:
"""
Obtain approximant residues.
Returns:
Matrix with residues as columns.
"""
return self.trainedModel.getResidues()
diff --git a/rrompy/reduction_methods/distributed/rb_distributed.py b/rrompy/reduction_methods/distributed/rb_distributed.py
index a7dfa03..bc4a326 100644
--- a/rrompy/reduction_methods/distributed/rb_distributed.py
+++ b/rrompy/reduction_methods/distributed/rb_distributed.py
@@ -1,206 +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 deepcopy as copy
import numpy as np
from .generic_distributed_approximant import GenericDistributedApproximant
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, paramVal)
+ HFEng, paramVal, sampList)
from rrompy.utilities.base import verbosityDepth
from rrompy.utilities.exception_manager import (RROMPyWarning, RROMPyException,
RROMPyAssert)
__all__ = ['RBDistributed']
class RBDistributed(GenericDistributedApproximant):
"""
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;
- 'sampler': sample point generator;
- - 'R': rank for Galerkin projection; defaults to prod(S).
+ - 'R': rank for Galerkin projection; defaults to prod(S);
+ - 'PODTolerance': tolerance for snapshots POD; defaults to -1.
Defaults to empty dict.
homogeneized(optional): 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.
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.
parameterListSoft: Recognized keys of soft approximant parameters:
- 'POD': whether to compute POD of snapshots.
- - 'R': rank for Galerkin projection.
+ - 'R': rank for Galerkin projection;
+ - 'PODTolerance': tolerance for snapshots POD.
parameterListCritical: Recognized keys of critical approximant
parameters:
- 'S': total number of samples current approximant relies upon;
- 'sampler': sample point generator;
POD: Whether to compute POD of snapshots.
S: Number of solution snapshots over which current approximant is
based upon.
sampler: Sample point generator.
R: Rank for Galerkin projection.
muBounds: list of bounds for parameter values.
samplingEngine: Sampling engine.
uHF: High fidelity solution(s) with parameter(s) lastSolvedHF as
sampleList.
lastSolvedHF: Parameter(s) corresponding to last computed high fidelity
solution(s) as parameterList.
- uAppReduced: Reduced approximate solution(s) with parameter(s)
- lastSolvedApp as sampleList.
- lastSolvedAppReduced: Parameter(s) corresponding to last computed
+ uApproxReduced: Reduced approximate solution(s) with parameter(s)
+ lastSolvedApprox as sampleList.
+ lastSolvedApproxReduced: Parameter(s) corresponding to last computed
reduced approximate solution(s) as parameterList.
- uApp: Approximate solution(s) with parameter(s) lastSolvedApp as
+ uApprox: Approximate solution(s) with parameter(s) lastSolvedApprox as
sampleList.
- lastSolvedApp: Parameter(s) corresponding to last computed approximate
- solution(s) as parameterList.
+ lastSolvedApprox: Parameter(s) corresponding to last computed
+ approximate solution(s) as parameterList.
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 : paramVal = None,
approxParameters : DictAny = {}, homogeneized : bool = False,
verbosity : int = 10, timestamp : bool = True):
self._preInit()
- self._addParametersToList(["R"], [1])
+ self._addParametersToList(["R", "PODTolerance"], [1, -1])
super().__init__(HFEngine = HFEngine, mu0 = mu0,
approxParameters = approxParameters,
homogeneized = homogeneized,
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):
GenericDistributedApproximant.S.fset(self, S)
if not hasattr(self, "_R"): self._R = np.prod(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 RROMPyException("R must be non-negative.")
self._R = R
self._approxParameters["R"] = self.R
if hasattr(self, "_S") and np.prod(self.S) < self.R:
RROMPyWarning("Prescribed S is too small. Decreasing R.")
self.R = np.prod(self.S)
+ @property
+ def PODTolerance(self):
+ """Value of PODTolerance."""
+ return self._PODTolerance
+ @PODTolerance.setter
+ def PODTolerance(self, PODTolerance):
+ self._PODTolerance = PODTolerance
+ self._approxParameters["PODTolerance"] = self.PODTolerance
+
def setupApprox(self):
"""Compute RB projection matrix."""
if self.checkComputedApprox():
return
RROMPyAssert(self._mode, message = "Cannot setup approximant.")
if self.verbosity >= 5:
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])
+ U, s, _ = np.linalg.svd(self.samplingEngine.RPOD)
+ s = s ** 2.
else:
- pMat = self.samplingEngine.samples[: self.R]
+ Gramian = self.HFEngine.innerProduct(self.samplingEngine.samples,
+ self.samplingEngine.samples)
+ U, s, _ = np.linalg.svd(Gramian)
+ nsamples = self.samplingEngine.nsamples
+ snorm = np.cumsum(s[::-1]) / np.sum(s)
+ nPODTrunc = min(nsamples - np.argmax(snorm > self.PODTolerance),
+ self.R)
+ pMat = self.samplingEngine.samples.dot(U[:, : nPODTrunc])
+ if self.verbosity >= 5:
+ verbosityDepth("MAIN", ("Assembling {}x{} projection matrix from "
+ "{} samples.").format(*(pMat.shape),
+ nsamples),
+ 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,
pMat, self.HFEngine.rescalingExp)
data.thetaAs = self.HFEngine.affineWeightsA(self.mu0)
data.thetabs = self.HFEngine.affineWeightsb(self.mu0,
self.homogeneized)
data.mus = copy(self.mus)
self.trainedModel.data = data
else:
self.trainedModel = self.trainedModel
self.trainedModel.data.projMat = copy(pMat)
ARBs, bRBs = self.assembleReducedSystem(pMat)
self.trainedModel.data.ARBs = ARBs
self.trainedModel.data.bRBs = bRBs
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, pMat : Np2D = None, pMatOld : Np2D = None)\
+ def assembleReducedSystem(self, pMat : sampList = None,
+ pMatOld : sampList = 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/distributed_greedy/generic_distributed_greedy_approximant.py b/rrompy/reduction_methods/distributed_greedy/generic_distributed_greedy_approximant.py
index f4c223c..9817e7c 100644
--- a/rrompy/reduction_methods/distributed_greedy/generic_distributed_greedy_approximant.py
+++ b/rrompy/reduction_methods/distributed_greedy/generic_distributed_greedy_approximant.py
@@ -1,591 +1,597 @@
# 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.distributed.generic_distributed_approximant \
import GenericDistributedApproximant
from rrompy.utilities.base.types import (Np1D, Np2D, DictAny, HFEng, Tuple,
List, normEng, paramVal, paramList,
sampList)
from rrompy.utilities.base import verbosityDepth
from rrompy.solver import normEngine
from rrompy.utilities.exception_manager import (RROMPyException, RROMPyAssert,
RROMPyWarning)
from rrompy.parameter import checkParameterList, emptyParameterList
__all__ = ['GenericDistributedGreedyApproximant']
def pruneSamples(mus:paramList, badmus:paramList,
tol : float = 1e-8) -> paramList:
"""Remove from mus all the elements which are too close to badmus."""
if len(badmus) == 0: return mus
musNp = np.array(mus(0))
badmus = np.array(badmus(0))
proximity = np.min(np.abs(musNp.reshape(-1, 1)
- np.tile(badmus.reshape(1, -1), [len(mus), 1])),
axis = 1).flatten()
idxPop = np.arange(len(mus))[proximity <= tol]
for i, j in enumerate(idxPop):
mus.pop(j - i)
return mus
class GenericDistributedGreedyApproximant(GenericDistributedApproximant):
"""
ROM greedy 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': number of starting training points;
- 'sampler': sample point generator;
- '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 test points to be exhausted before
test set refinement; defaults to 0.2;
- 'nTestPoints': number of test points; defaults to 5e2;
- 'trainSetGenerator': training sample points generator.
Defaults to empty dict.
homogeneized(optional): 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.
parameterListSoft: Recognized keys of soft approximant parameters:
- 'POD': whether to compute POD of snapshots.
- 'greedyTol': uniform error tolerance for greedy algorithm;
- 'interactive': whether to interactively terminate greedy
algorithm;
- 'maxIter': maximum number of greedy steps;
- 'refinementRatio': ratio of test points to be exhausted before
test set refinement;
- 'nTestPoints': number of test points.
parameterListCritical: Recognized keys of critical approximant
parameters:
- 'S': total number of samples current approximant relies upon;
- 'sampler': sample point generator;
- 'trainSetGenerator': training sample points generator.
POD: whether to compute POD of snapshots.
S: number of test points.
sampler: Sample point generator.
greedyTol: uniform error tolerance for greedy algorithm.
interactive: whether to interactively terminate greedy algorithm.
maxIter: maximum number of greedy steps.
refinementRatio: ratio of training points to be exhausted before
training set refinement.
nTestPoints: number of starting training points.
trainSetGenerator: training sample points generator.
muBounds: list of bounds for parameter values.
samplingEngine: Sampling engine.
estimatorNormEngine: Engine for estimator norm computation.
uHF: High fidelity solution(s) with parameter(s) lastSolvedHF as
sampleList.
lastSolvedHF: Parameter(s) corresponding to last computed high fidelity
solution(s) as parameterList.
- uAppReduced: Reduced approximate solution(s) with parameter(s)
- lastSolvedApp as sampleList.
- lastSolvedAppReduced: Parameter(s) corresponding to last computed
+ uApproxReduced: Reduced approximate solution(s) with parameter(s)
+ lastSolvedApprox as sampleList.
+ lastSolvedApproxReduced: Parameter(s) corresponding to last computed
reduced approximate solution(s) as parameterList.
- uApp: Approximate solution(s) with parameter(s) lastSolvedApp as
+ uApprox: Approximate solution(s) with parameter(s) lastSolvedApprox as
sampleList.
- lastSolvedApp: Parameter(s) corresponding to last computed approximate
- solution(s) as parameterList.
+ lastSolvedApprox: Parameter(s) corresponding to last computed
+ approximate solution(s) as parameterList.
"""
TOL_INSTABILITY = 1e-6
def __init__(self, HFEngine:HFEng, mu0 : paramVal = None,
approxParameters : DictAny = {}, homogeneized : bool = False,
verbosity : int = 10, timestamp : bool = True):
self._preInit()
from rrompy.parameter.parameter_sampling import QuadratureSampler as QS
self._addParametersToList(["greedyTol", "interactive", "maxIter",
"refinementRatio", "nTestPoints"],
[1e-2, False, 1e2, .2, 5e2],
["trainSetGenerator"],
[QS([[0], [1]], "UNIFORM")])
del QS
super().__init__(HFEngine = HFEngine, mu0 = mu0,
approxParameters = approxParameters,
homogeneized = homogeneized, verbosity = verbosity,
timestamp = timestamp)
RROMPyAssert(self.HFEngine.npar, 1, "Parameter dimension")
self._postInit()
@property
def greedyTol(self):
"""Value of greedyTol."""
return self._greedyTol
@greedyTol.setter
def greedyTol(self, greedyTol):
if greedyTol < 0:
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 interactive(self):
"""Value of interactive."""
return self._interactive
@interactive.setter
def interactive(self, interactive):
self._interactive = interactive
@property
def maxIter(self):
"""Value of maxIter."""
return self._maxIter
@maxIter.setter
def maxIter(self, maxIter):
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 RROMPyException(("refinementRatio must be between 0 "
"(excluded) and 1."))
if (hasattr(self, "_refinementRatio")
and self.refinementRatio is not None):
refinementRatioold = self.refinementRatio
else:
refinementRatioold = -1
self._refinementRatio = refinementRatio
self._approxParameters["refinementRatio"] = self.refinementRatio
if refinementRatioold != self.refinementRatio:
self.resetSamples()
@property
def nTestPoints(self):
"""Value of nTestPoints."""
return self._nTestPoints
@nTestPoints.setter
def nTestPoints(self, nTestPoints):
if nTestPoints <= 0:
raise RROMPyException("nTestPoints must be positive.")
if not np.isclose(nTestPoints, np.int(nTestPoints)):
raise RROMPyException("nTestPoints must be an integer.")
nTestPoints = np.int(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()
@property
def trainSetGenerator(self):
"""Value of trainSetGenerator."""
return self._trainSetGenerator
@trainSetGenerator.setter
def trainSetGenerator(self, trainSetGenerator):
if 'generatePoints' not in dir(trainSetGenerator):
raise RROMPyException("trainSetGenerator type not recognized.")
if (hasattr(self, '_trainSetGenerator')
and self.trainSetGenerator is not None):
trainSetGeneratorOld = self.trainSetGenerator
self._trainSetGenerator = trainSetGenerator
self._approxParameters["trainSetGenerator"] = self.trainSetGenerator
if (not 'trainSetGeneratorOld' in locals()
or trainSetGeneratorOld != self.trainSetGenerator):
self.resetSamples()
def resetSamples(self):
"""Reset samples."""
super().resetSamples()
self._mus = emptyParameterList()
def initEstimatorNormEngine(self, normEngn : normEng = None):
"""Initialize estimator norm engine."""
if (normEngn is not None or not hasattr(self, "estimatorNormEngine")
or self.estimatorNormEngine is None):
if normEngn is None:
- if not hasattr(self.HFEngine, "energyNormMatrix"):
- self.HFEngine.buildEnergyNormForm()
- estimatorEnergyMatrix = self.HFEngine.energyNormMatrix
+ if not hasattr(self.HFEngine, "energyNormPartialDualMatrix"):
+ self.HFEngine.buildEnergyNormPartialDualForm()
+ estimatorEnergyMatrix = (
+ self.HFEngine.energyNormPartialDualMatrix)
else:
- if hasattr(normEngn, "buildEnergyNormForm"):
- if not hasattr(normEngn, "energyNormMatrix"):
- normEngn.buildEnergyNormForm()
- estimatorEnergyMatrix = normEngn.energyNormMatrix
+ if hasattr(normEngn, "buildEnergyNormPartialDualForm"):
+ if not hasattr(normEngn, "energyNormPartialDualMatrix"):
+ normEngn.buildEnergyNormPartialDualForm()
+ estimatorEnergyMatrix = (
+ normEngn.energyNormPartialDualMatrix)
else:
estimatorEnergyMatrix = normEngn
self.estimatorNormEngine = normEngine(estimatorEnergyMatrix)
def errorEstimator(self, mus:paramList) -> List[complex]:
"""
Standard residual-based error estimator with explicit residual
computation.
"""
self.setupApprox()
if self.HFEngine.nbs == 1:
- RHS = self.getRHS(mus[0], homogeneized = self.homogeneized)
+ RHS = self.getRHS(mus[0], homogeneized = self.homogeneized,
+ duality = False)
RHSNorm = self.estimatorNormEngine.norm(RHS)
- res = self.getRes(mus, homogeneized = self.homogeneized)
+ res = self.getRes(mus, homogeneized = self.homogeneized,
+ duality = False)
err = self.estimatorNormEngine.norm(res) / RHSNorm
else:
- res = self.getRes(mus, homogeneized = self.homogeneized)
- RHS = self.getRHS(mus, homogeneized = self.homogeneized)
+ res = self.getRes(mus, homogeneized = self.homogeneized,
+ duality = False)
+ RHS = self.getRHS(mus, homogeneized = self.homogeneized,
+ duality = False)
err = (self.estimatorNormEngine.norm(res)
/ self.estimatorNormEngine.norm(RHS))
return np.abs(err)
def getMaxErrorEstimator(self, mus:paramList,
plot : bool = False) -> Tuple[Np1D, int, float]:
"""
Compute maximum of (and index of maximum of) error estimator over given
parameters.
"""
errorEstTest = self.errorEstimator(mus)
idxMaxEst = np.argmax(errorEstTest)
maxEst = errorEstTest[idxMaxEst]
if plot and not np.all(np.isinf(errorEstTest)):
musre = mus.re(0)
from matplotlib import pyplot as plt
plt.figure()
plt.semilogy(musre, errorEstTest, 'k')
plt.semilogy([musre[0], musre[-1]], [self.greedyTol] * 2, 'r--')
plt.semilogy(self.mus.re(0),
2. * self.greedyTol * np.ones(len(self.mus)), '*m')
plt.semilogy(musre[idxMaxEst], maxEst, 'xr')
plt.grid()
plt.show()
plt.close()
return errorEstTest, idxMaxEst, maxEst
def greedyNextSample(self, muidx:int, plotEst : bool = False)\
-> Tuple[Np1D, int, float, paramVal]:
"""Compute next greedy snapshot of solution map."""
RROMPyAssert(self._mode, message = "Cannot add greedy sample.")
mu = copy(self.muTest[muidx])
self.muTest.pop(muidx)
if self.verbosity >= 2:
verbosityDepth("MAIN", ("Adding {}-th sample point at {} to "
"training set.").format(
self.samplingEngine.nsamples + 1, mu),
timestamp = self.timestamp)
self.mus.append(mu)
self.samplingEngine.nextSample(mu, homogeneized = self.homogeneized)
errorEstTest, muidx, maxErrorEst = self.getMaxErrorEstimator(
self.muTest, plotEst)
return errorEstTest, muidx, maxErrorEst, self.muTest[muidx]
def _preliminaryTraining(self):
"""Initialize starting snapshots of solution map."""
RROMPyAssert(self._mode, message = "Cannot start greedy algorithm.")
self.computeScaleFactor()
if self.samplingEngine.nsamples > 0:
return
if self.verbosity >= 2:
verbosityDepth("INIT", "Starting computation of snapshots.",
timestamp = self.timestamp)
self.resetSamples()
self.mus = self.trainSetGenerator.generatePoints(self.S)
muLast = copy(self.mus[-1])
self.mus.pop()
muTestBase = self.sampler.generatePoints(self.nTestPoints)
if len(self.mus) > 0:
if self.verbosity >= 2:
verbosityDepth("MAIN",
("Adding first {} samples point at {} to "
"training set.").format(np.prod(self.S) - 1,
self.mus),
timestamp = self.timestamp)
self.samplingEngine.iterSample(self.mus,
homogeneized = self.homogeneized)
muTestBase = pruneSamples(muTestBase, self.mus,
1e-10 * self.scaleFactor[0]).sort()
self.muTest = emptyParameterList()
self.muTest.reset((len(muTestBase) + 1, self.mus.shape[1]))
self.muTest[: -1] = muTestBase
self.muTest[-1] = muLast
def _enrichTestSet(self, nTest:int):
"""Add extra elements to test set."""
RROMPyAssert(self._mode, message = "Cannot enrich test set.")
muTestExtra = self.sampler.generatePoints(2 * nTest)
muTotal = copy(self.mus)
muTotal.append(self.muTest)
muTestExtra = pruneSamples(muTestExtra, muTotal,
1e-10 * self.scaleFactor[0])
muTestNew = np.empty(len(self.muTest) + len(muTestExtra),
dtype = np.complex)
muTestNew[: len(self.muTest)] = self.muTest(0)
muTestNew[len(self.muTest) :] = muTestExtra(0)
self.muTest = checkParameterList(muTestNew.sort(), self.npar)
if self.verbosity >= 5:
verbosityDepth("MAIN", "Enriching test set by {} elements.".format(
len(muTestExtra)),
timestamp = self.timestamp)
def greedy(self, plotEst : bool = False):
"""Compute greedy snapshots of solution map."""
RROMPyAssert(self._mode, message = "Cannot start greedy algorithm.")
if self.samplingEngine.nsamples > 0:
return
self._preliminaryTraining()
nTest = self.nTestPoints
errorEstTest, muidx, maxErrorEst, mu = self.greedyNextSample(-1,
plotEst)
if self.verbosity >= 2:
verbosityDepth("MAIN", ("Uniform testing 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) * nTest > len(self.muTest):
self._enrichTestSet(nTest)
nTest = len(self.muTest)
muTestOld, maxErrorEstOld = self.muTest, maxErrorEst
errorEstTest, muidx, maxErrorEst, mu = self.greedyNextSample(
muidx, plotEst)
if self.verbosity >= 2:
verbosityDepth("MAIN", ("Uniform testing 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.muTest = muTestOld
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 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", "Reducing value of greedyTol...",
timestamp = self.timestamp)
while maxErrorEst <= self._greedyTol:
self._greedyTol *= .5
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.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 (super().checkComputedApprox()
and len(self.mus) == self.trainedModel.data.projMat.shape[1])
def assembleReducedResidualGramian(self, pMat:sampList):
"""
Build residual gramian of reduced linear system through projections.
"""
self.initEstimatorNormEngine()
if (not hasattr(self.trainedModel.data, "gramian")
or self.trainedModel.data.gramian is None):
gramian = self.estimatorNormEngine.innerProduct(pMat, pMat)
else:
Sold = self.trainedModel.data.gramian.shape[0]
S = len(self.mus)
if Sold > S:
gramian = self.trainedModel.data.gramian[: S, : S]
else:
idxOld = list(range(Sold))
idxNew = list(range(Sold, S))
gramian = np.empty((S, S), dtype = np.complex)
gramian[: Sold, : Sold] = self.trainedModel.data.gramian
gramian[: Sold, Sold :] = (
self.estimatorNormEngine.innerProduct(pMat(idxNew),
pMat(idxOld)))
gramian[Sold :, : Sold] = gramian[: Sold, Sold :].T.conj()
gramian[Sold :, Sold :] = (
self.estimatorNormEngine.innerProduct(pMat(idxNew),
pMat(idxNew)))
self.trainedModel.data.gramian = gramian
def assembleReducedResidualBlocksbb(self, bs:List[Np1D],
scaling : float = 1.):
"""
Build blocks (of type bb) of reduced linear system through projections.
"""
self.initEstimatorNormEngine()
nbs = len(bs)
if (not hasattr(self.trainedModel.data, "resbb")
or self.trainedModel.data.resbb is None):
resbb = np.empty((nbs, nbs), dtype = np.complex)
for i in range(nbs):
Mbi = scaling ** i * bs[i]
resbb[i, i] = self.estimatorNormEngine.innerProduct(Mbi, Mbi)
for j in range(i):
Mbj = scaling ** j * bs[j]
resbb[i, j] = self.estimatorNormEngine.innerProduct(Mbj,
Mbi)
for i in range(nbs):
for j in range(i + 1, nbs):
resbb[i, j] = resbb[j, i].conj()
self.trainedModel.data.resbb = resbb
def assembleReducedResidualBlocksAb(self, As:List[Np2D], bs:List[Np1D],
pMat:sampList, scaling : float = 1.):
"""
Build blocks (of type Ab) of reduced linear system through projections.
"""
self.initEstimatorNormEngine()
nAs = len(As)
nbs = len(bs)
S = len(self.mus)
if (not hasattr(self.trainedModel.data, "resAb")
or self.trainedModel.data.resAb is None):
if not isinstance(pMat, (np.ndarray,)): pMat = pMat.data
resAb = np.empty((nbs, 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]
resAb[i, :, j] = self.estimatorNormEngine.innerProduct(MAj,
Mbi)
else:
Sold = self.trainedModel.data.resAb.shape[1]
if Sold == S: return
if Sold > S:
resAb = self.trainedModel.data.resAb[:, : S, :]
else:
if not isinstance(pMat, (np.ndarray,)): pMat = pMat.data
resAb = np.empty((nbs, S, nAs), dtype = np.complex)
resAb[:, : Sold, :] = self.trainedModel.data.resAb
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]
resAb[i, Sold :, j] = (
self.estimatorNormEngine.innerProduct(MAj, Mbi))
self.trainedModel.data.resAb = resAb
def assembleReducedResidualBlocksAA(self, As:List[Np2D], pMat:sampList,
scaling : float = 1.):
"""
Build blocks (of type AA) of reduced linear system through projections.
"""
self.initEstimatorNormEngine()
nAs = len(As)
S = len(self.mus)
if (not hasattr(self.trainedModel.data, "resAA")
or self.trainedModel.data.resAA is None):
if not isinstance(pMat, (np.ndarray,)): pMat = pMat.data
resAA = np.empty((S, nAs, S, nAs), dtype = np.complex)
for i in range(nAs):
MAi = scaling ** (i + 1) * As[i].dot(pMat)
resAA[:, i, :, i] = (
self.estimatorNormEngine.innerProduct(MAi, MAi))
for j in range(i):
MAj = scaling ** (j + 1) * As[j].dot(pMat)
resAA[:, i, :, j] = (
self.estimatorNormEngine.innerProduct(MAj, MAi))
for i in range(nAs):
for j in range(i + 1, nAs):
resAA[:, i, :, j] = resAA[:, j, :, i].T.conj()
else:
Sold = self.trainedModel.data.resAA.shape[0]
if Sold == S: return
if Sold > S:
resAA = self.trainedModel.data.resAA[: S, :, : S, :]
else:
if not isinstance(pMat, (np.ndarray,)): pMat = pMat.data
resAA = np.empty((S, nAs, S, nAs), dtype = np.complex)
resAA[: Sold, :, : Sold, :] = self.trainedModel.data.resAA
for i in range(nAs):
MAi = scaling ** (i + 1) * As[i].dot(pMat)
resAA[: Sold, i, Sold :, i] = (
self.estimatorNormEngine.innerProduct(MAi[:, Sold :],
MAi[:, : Sold]))
resAA[Sold :, i, : Sold, i] = resAA[: Sold, i,
Sold :, i].T.conj()
resAA[Sold :, i, Sold :, i] = (
self.estimatorNormEngine.innerProduct(MAi[:, Sold :],
MAi[:, Sold :]))
for j in range(i):
MAj = scaling ** (j + 1) * As[j].dot(pMat)
resAA[: Sold, i, Sold :, j] = (
self.estimatorNormEngine.innerProduct(MAj[:, Sold :],
MAi[:, : Sold]))
resAA[Sold :, i, : Sold, j] = (
self.estimatorNormEngine.innerProduct(MAj[:, : Sold],
MAi[:, Sold :]))
resAA[Sold :, i, Sold :, j] = (
self.estimatorNormEngine.innerProduct(MAj[:, Sold :],
MAi[:, Sold :]))
for i in range(nAs):
for j in range(i + 1, nAs):
resAA[: Sold, i, Sold :, j] = (
resAA[Sold :, j, : Sold, i].T.conj())
resAA[Sold :, i, : Sold, j] = (
resAA[: Sold, j, Sold :, i].T.conj())
resAA[Sold :, i, Sold :, j] = (
resAA[Sold :, j, Sold :, i].T.conj())
self.trainedModel.data.resAA = resAA
diff --git a/rrompy/reduction_methods/distributed_greedy/rational_interpolant_greedy.py b/rrompy/reduction_methods/distributed_greedy/rational_interpolant_greedy.py
index 0ec150b..6a697a6 100644
--- a/rrompy/reduction_methods/distributed_greedy/rational_interpolant_greedy.py
+++ b/rrompy/reduction_methods/distributed_greedy/rational_interpolant_greedy.py
@@ -1,416 +1,426 @@
# 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 .generic_distributed_greedy_approximant import \
GenericDistributedGreedyApproximant
from rrompy.utilities.poly_fitting.polynomial import polybases, polydomcoeff
from rrompy.reduction_methods.distributed import RationalInterpolant
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, DictAny, HFEng, paramVal
from rrompy.utilities.base import verbosityDepth
from rrompy.utilities.exception_manager import RROMPyWarning
from rrompy.utilities.exception_manager import RROMPyException, RROMPyAssert
__all__ = ['RationalInterpolantGreedy']
class RationalInterpolantGreedy(GenericDistributedGreedyApproximant,
RationalInterpolant):
"""
ROM greedy rational 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': number of starting training points;
- 'sampler': sample point generator;
+ - 'radialBasis': radial basis family for interpolant numerator;
+ defaults to 0, i.e. no radial basis;
+ - 'radialBasisWeights': radial basis weights for interpolant
+ numerator; defaults to 0, i.e. identity;
- '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;
- 'nTestPoints': number of test points; defaults to 5e2;
- 'trainSetGenerator': training sample points generator; defaults
to Chebyshev sampler within muBounds;
- 'polybasis': 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;
- 'errorEstimatorKind': kind of error estimator; available values
include 'EXACT', 'BASIC', and 'BARE'; defaults to 'EXACT';
- 'interpRcond': tolerance for interpolation; defaults to None;
- 'robustTol': tolerance for robust Pade' denominator management;
defaults to 0.
Defaults to empty dict.
homogeneized(optional): 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.
parameterListSoft: Recognized keys of soft approximant parameters:
- 'POD': whether to compute POD of snapshots.
+ - 'radialBasis': radial basis family for interpolant numerator;
+ defaults to 0, i.e. no radial basis;
+ - 'radialBasisWeights': radial basis weights for interpolant
+ numerator; defaults to 0, i.e. identity;
- 'greedyTol': uniform error tolerance for greedy algorithm;
- 'interactive': whether to interactively terminate greedy
algorithm;
- 'maxIter': maximum number of greedy steps;
- 'refinementRatio': ratio of test points to be exhausted before
test set refinement;
- 'nTestPoints': number of test points;
- 'trainSetGenerator': training sample points generator;
- 'Delta': difference between M and N in rational approximant;
- 'errorEstimatorKind': kind of error estimator;
- 'interpRcond': tolerance for interpolation;
- 'robustTol': tolerance for robust Pade' denominator management.
parameterListCritical: Recognized keys of critical approximant
parameters:
- 'S': total number of samples current approximant relies upon;
- 'sampler': sample point generator.
POD: whether to compute POD of snapshots.
S: number of test points.
sampler: Sample point generator.
+ radialBasis: Radial basis family for interpolant numerator.
+ radialBasisWeights: Radial basis weights for interpolant numerator.
greedyTol: uniform error tolerance for greedy algorithm.
interactive: whether to interactively terminate greedy algorithm.
maxIter: maximum number of greedy steps.
refinementRatio: ratio of training points to be exhausted before
training set refinement.
nTestPoints: number of starting training points.
trainSetGenerator: training sample points generator.
robustTol: tolerance for robust Pade' denominator management.
Delta: difference between M and N in rational approximant.
errorEstimatorKind: kind of error estimator.
interpRcond: tolerance for interpolation.
robustTol: tolerance for robust Pade' denominator management.
muBounds: list of bounds for parameter values.
samplingEngine: Sampling engine.
estimatorNormEngine: Engine for estimator norm computation.
uHF: High fidelity solution(s) with parameter(s) lastSolvedHF as
sampleList.
lastSolvedHF: Parameter(s) corresponding to last computed high fidelity
solution(s) as parameterList.
- uAppReduced: Reduced approximate solution(s) with parameter(s)
- lastSolvedApp as sampleList.
- lastSolvedAppReduced: Parameter(s) corresponding to last computed
+ uApproxReduced: Reduced approximate solution(s) with parameter(s)
+ lastSolvedApprox as sampleList.
+ lastSolvedApproxReduced: Parameter(s) corresponding to last computed
reduced approximate solution(s) as parameterList.
- uApp: Approximate solution(s) with parameter(s) lastSolvedApp as
+ uApprox: Approximate solution(s) with parameter(s) lastSolvedApprox as
sampleList.
- lastSolvedApp: Parameter(s) corresponding to last computed approximate
- solution(s) as parameterList.
+ lastSolvedApprox: Parameter(s) corresponding to last computed
+ approximate solution(s) as parameterList.
"""
_allowedEstimatorKinds = ["EXACT", "BASIC", "BARE"]
def __init__(self, HFEngine:HFEng, mu0 : paramVal = None,
approxParameters : DictAny = {}, homogeneized : bool = False,
verbosity : int = 10, timestamp : bool = True):
self._preInit()
self._addParametersToList(["Delta", "polybasis", "errorEstimatorKind",
"interpRcond", "robustTol"],
[0, "MONOMIAL", "EXACT", -1, 0],
toBeExcluded = ["E"])
super().__init__(HFEngine = HFEngine, mu0 = mu0,
approxParameters = approxParameters,
homogeneized = homogeneized,
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 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 RROMPyException("Delta must be an integer.")
if Delta < 0:
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:
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 self._allowedEstimatorKinds:
RROMPyWarning(("Error estimator kind not recognized. Overriding "
"to 'EXACT'."))
errorEstimatorKind = "EXACT"
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):
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 RROMPyException("nTestPoints must be an integer.")
nTestPoints = np.int(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 _errorSamplingRatio(self, mus:Np1D, muRTest:Np1D,
muRTrain:Np1D) -> Np1D:
"""Scalar ratio in explicit error estimator."""
self.setupApprox()
testTile = np.tile(np.reshape(muRTest, (-1, 1)), [1, len(muRTrain)])
nodalVals = np.prod(testTile - np.reshape(muRTrain, (1, -1)), axis = 1)
denVals = self.trainedModel.getQVal(mus)
return np.abs(nodalVals / denVals)
def _RHSNorms(self, radiusb0:Np2D) -> Np1D:
"""High fidelity system RHS norms."""
self.assembleReducedResidualBlocks(full = False)
# 'ij,jk,ik->k', resbb, radiusb0, radiusb0.conj()
b0resb0 = np.sum(self.trainedModel.data.resbb.dot(radiusb0)
* radiusb0.conj(), axis = 0)
RHSnorms = np.power(np.abs(b0resb0), .5)
return RHSnorms
def _errorEstimatorBare(self) -> Np1D:
"""Bare residual-based error estimator."""
self.setupApprox()
self.assembleReducedResidualGramian(self.trainedModel.data.projMat)
- pDom = self.trainedModel.data.P[:, -1]
+ pDom = self.trainedModel.data.P[-1, :]
LL = pDom.conj().dot(self.trainedModel.data.gramian.dot(pDom))
Adiag = self.As[0].diagonal()
LL = ((self.scaleFactor[0] * np.linalg.norm(Adiag)) ** 2. * LL
/ np.size(Adiag))
scalingDom = polydomcoeff(len(self.mus) - 1, self.polybasis)
return scalingDom * np.power(np.abs(LL), .5)
def _errorEstimatorBasic(self, muTest:paramVal, ratioTest:complex) -> Np1D:
"""Basic residual-based error estimator."""
resmu = self.HFEngine.residual(self.trainedModel.getApprox(muTest),
- muTest, self.homogeneized)[0]
+ muTest, self.homogeneized,
+ duality = False)[0]
return np.abs(self.estimatorNormEngine.norm(resmu) / ratioTest)
def _errorEstimatorExact(self, muRTrain:Np1D, vanderBase:Np2D) -> Np1D:
"""Exact residual-based error estimator."""
self.setupApprox()
self.assembleReducedResidualBlocks(full = True)
nAs = self.HFEngine.nAs - 1
nbs = max(self.HFEngine.nbs - 1, nAs * self.homogeneized)
delta = len(self.mus) - len(self.trainedModel.data.Q)
nbsEff = max(0, nbs - delta)
momentQ = np.zeros(nbsEff, dtype = np.complex)
momentQu = np.zeros((len(self.mus), nAs), dtype = np.complex)
radiusbTen = np.zeros((nbsEff, nbsEff, vanderBase.shape[1]),
dtype = np.complex)
radiusATen = np.zeros((nAs, nAs, vanderBase.shape[1]),
dtype = np.complex)
if nbsEff > 0:
momentQ[0] = self.trainedModel.data.Q[-1]
radiusbTen[0, :, :] = vanderBase[: nbsEff, :]
- momentQu[:, 0] = self.trainedModel.data.P[:, -1]
+ momentQu[:, 0] = self.trainedModel.data.P[-1, :]
radiusATen[0, :, :] = vanderBase[: nAs, :]
Qvals = self.trainedModel.getQVal(self.mus)
for k in range(1, max(nAs, nbs * (nbsEff > 0))):
Qvals = Qvals * muRTrain
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)
# 'ij,jk,ik->k', resbb, radiusb, radiusb.conj()
ff = np.sum(self.trainedModel.data.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.trainedModel.data.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.trainedModel.data.resAA, radiusA, 2)
* radiusA.conj(), axis = (0, 1))
scalingDom = polydomcoeff(len(self.mus) - 1, self.polybasis)
return scalingDom * np.power(np.abs(ff - 2. * np.real(Lf) + LL), .5)
def errorEstimator(self, mus:Np1D) -> Np1D:
"""Standard residual-based error estimator."""
self.setupApprox()
- if (np.any(np.isnan(self.trainedModel.data.P[:, -1]))
- or np.any(np.isinf(self.trainedModel.data.P[:, -1]))):
+ if (np.any(np.isnan(self.trainedModel.data.P[-1, :]))
+ or np.any(np.isinf(self.trainedModel.data.P[-1, :]))):
err = np.empty(len(mus))
err[:] = np.inf
return err
nAs = self.HFEngine.nAs - 1
nbs = max(self.HFEngine.nbs - 1, nAs * self.homogeneized)
muRTest = self.centerNormalize(mus)(0)
muRTrain = self.centerNormalize(self.mus)(0)
samplingRatio = self._errorSamplingRatio(mus, muRTest, muRTrain)
vanderBase = np.polynomial.polynomial.polyvander(muRTest,
max(nAs, nbs)).T
RHSnorms = self._RHSNorms(vanderBase[: nbs + 1, :])
if self.errorEstimatorKind == "BARE":
jOpt = self._errorEstimatorBare()
elif self.errorEstimatorKind == "BASIC":
idx_muTestSample = np.argmax(samplingRatio)
jOpt = self._errorEstimatorBasic(mus[idx_muTestSample],
samplingRatio[idx_muTestSample])
else: #if self.errorEstimatorKind == "EXACT":
jOpt = self._errorEstimatorExact(muRTrain, vanderBase[: -1, :])
return jOpt * samplingRatio / RHSnorms
def setupApprox(self, plotEst : bool = False):
"""
Compute Pade' interpolant.
SVD-based robust eigenvalue management.
"""
if self.checkComputedApprox():
return
RROMPyAssert(self._mode, message = "Cannot setup approximant.")
if self.verbosity >= 5:
verbosityDepth("INIT", "Setting up {}.". format(self.name()),
timestamp = self.timestamp)
self.computeScaleFactor()
self.greedy(plotEst)
self._S = len(self.mus)
self._N, self._M, self._E = [self._S - 1] * 3
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,
self.samplingEngine.samples,
self.HFEngine.rescalingExp)
data.polytype = self.polybasis
+ data.polytypeP = self.polybasisP
data.scaleFactor = self.scaleFactor
data.mus = copy(self.mus)
self.trainedModel.data = data
else:
self.trainedModel = self.trainedModel
self.trainedModel.data.projMat = copy(self.samplingEngine.samples)
self.trainedModel.data.mus = copy(self.mus)
if min(self.M, self.N) < 0:
if self.verbosity >= 5:
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),
+ Q = np.empty(tuple([max(self.N, 0) + 1] * self.npar),
dtype = np.complex)
+ P = np.empty(tuple([max(self.M, 0) + 1] * self.npar)
+ + (len(self.mus),), dtype = np.complex)
Q[:] = np.nan
P[:] = np.nan
self.trainedModel.data.Q = copy(Q)
self.trainedModel.data.P = 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:
Q = self._setupDenominator()
else:
- Q = np.ones(1, dtype = np.complex)
+ Q = np.ones(tuple([1] * self.npar), dtype = np.complex)
self.trainedModel.data.Q = copy(Q)
- P = self._setupNumerator()
- if self.POD:
- P = np.tensordot(self.samplingEngine.RPOD, P, axes = ([-1], [0]))
- self.trainedModel.data.P = copy(P)
+ self.trainedModel.data.P = copy(self._setupNumerator())
self.trainedModel.data.approxParameters = copy(self.approxParameters)
if self.verbosity >= 5:
verbosityDepth("DEL", "Done setting up approximant.",
timestamp = self.timestamp)
def assembleReducedResidualBlocks(self, full : bool = False):
"""Build affine blocks of reduced linear system through projections."""
scaling = self.trainedModel.data.scaleFactor[0]
self.assembleReducedResidualBlocksbb(self.bs, scaling)
if full:
pMat = self.trainedModel.data.projMat
self.assembleReducedResidualBlocksAb(self.As, self.bs[1 :],
pMat, scaling)
self.assembleReducedResidualBlocksAA(self.As, pMat, scaling)
diff --git a/rrompy/reduction_methods/distributed_greedy/rb_distributed_greedy.py b/rrompy/reduction_methods/distributed_greedy/rb_distributed_greedy.py
index fc03fd1..d7bab29 100644
--- a/rrompy/reduction_methods/distributed_greedy/rb_distributed_greedy.py
+++ b/rrompy/reduction_methods/distributed_greedy/rb_distributed_greedy.py
@@ -1,245 +1,255 @@
# 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 deepcopy as copy
from .generic_distributed_greedy_approximant import \
GenericDistributedGreedyApproximant
from rrompy.reduction_methods.distributed import RBDistributed
from rrompy.reduction_methods.trained_model import TrainedModelRB as tModel
from rrompy.reduction_methods.trained_model import TrainedModelData
from rrompy.utilities.base.types import Np1D, DictAny, HFEng, paramVal
from rrompy.utilities.base import verbosityDepth
from rrompy.utilities.exception_manager import RROMPyWarning, RROMPyAssert
from rrompy.parameter import checkParameterList
__all__ = ['RBDistributedGreedy']
class RBDistributedGreedy(GenericDistributedGreedyApproximant, RBDistributed):
"""
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;
- 'S': number of starting training points;
- 'sampler': sample point generator;
- '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 test points to be exhausted before
test set refinement; defaults to 0.2;
- 'nTestPoints': number of test points; defaults to 5e2;
- 'trainSetGenerator': training sample points generator; defaults
to Chebyshev sampler within muBounds.
Defaults to empty dict.
homogeneized(optional): 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.
parameterListSoft: Recognized keys of soft approximant parameters:
- 'POD': whether to compute POD of snapshots.
- 'greedyTol': uniform error tolerance for greedy algorithm;
- 'interactive': whether to interactively terminate greedy
algorithm;
- 'maxIter': maximum number of greedy steps;
- 'refinementRatio': ratio of test points to be exhausted before
test set refinement;
- 'nTestPoints': number of test points;
- 'trainSetGenerator': training sample points generator.
parameterListCritical: Recognized keys of critical approximant
parameters:
- 'S': total number of samples current approximant relies upon;
- 'sampler': sample point generator.
POD: whether to compute POD of snapshots.
S: number of test points.
sampler: Sample point generator.
greedyTol: uniform error tolerance for greedy algorithm.
interactive: whether to interactively terminate greedy algorithm.
maxIter: maximum number of greedy steps.
refinementRatio: ratio of training points to be exhausted before
training set refinement.
nTestPoints: number of starting training points.
trainSetGenerator: training sample points generator.
muBounds: list of bounds for parameter values.
samplingEngine: Sampling engine.
estimatorNormEngine: Engine for estimator norm computation.
uHF: High fidelity solution(s) with parameter(s) lastSolvedHF as
sampleList.
lastSolvedHF: Parameter(s) corresponding to last computed high fidelity
solution(s) as parameterList.
- uAppReduced: Reduced approximate solution(s) with parameter(s)
- lastSolvedApp as sampleList.
- lastSolvedAppReduced: Parameter(s) corresponding to last computed
+ uApproxReduced: Reduced approximate solution(s) with parameter(s)
+ lastSolvedApprox as sampleList.
+ lastSolvedApproxReduced: Parameter(s) corresponding to last computed
reduced approximate solution(s) as parameterList.
- uApp: Approximate solution(s) with parameter(s) lastSolvedApp as
+ uApprox: Approximate solution(s) with parameter(s) lastSolvedApprox as
sampleList.
- lastSolvedApp: Parameter(s) corresponding to last computed approximate
- solution(s) as parameterList.
+ lastSolvedApprox: Parameter(s) corresponding to last computed
+ approximate solution(s) as parameterList.
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 : paramVal = None,
approxParameters : DictAny = {}, homogeneized : bool = False,
verbosity : int = 10, timestamp : bool = True):
self._preInit()
super().__init__(HFEngine = HFEngine, mu0 = mu0,
approxParameters = approxParameters,
homogeneized = homogeneized,
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 R(self):
"""Value of R."""
self._R = np.prod(self._S)
return self._R
@R.setter
def R(self, R):
RROMPyWarning(("R is used just to simplify inheritance, and its value "
"cannot be changed from that of prod(S)."))
+ @property
+ def PODTolerance(self):
+ """Value of PODTolerance."""
+ self._PODTolerance = -1
+ return self._PODTolerance
+ @PODTolerance.setter
+ def PODTolerance(self, PODTolerance):
+ RROMPyWarning(("PODTolerance is used just to simplify inheritance, "
+ "and its value cannot be changed from -1."))
+
def errorEstimator(self, mus:Np1D) -> Np1D:
"""
Standard residual-based error estimator. Unreliable for unstable
problems (inf-sup constant is missing).
"""
self.setupApprox()
self.assembleReducedResidualBlocks(full = True)
nmus = len(mus)
nAs = self.trainedModel.data.resAA.shape[1]
nbs = self.trainedModel.data.resbb.shape[0]
thetaAs = self.trainedModel.data.thetaAs
thetabs = self.trainedModel.data.thetabs
radiusA = np.empty((len(self.mus), 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)
parmus, _ = checkParameterList(mus, self.npar)
- uApps = self.getApproxReduced(parmus)
+ uApproxRs = self.getApproxReduced(parmus)
for j, muPL in enumerate(parmus):
mu = muPL[0]
- uApp = uApps[j]
+ uApproxR = uApproxRs[j]
for i in range(nAs):
- radiusA[:, i, j] = eval(thetaAs[i]) * uApp
+ radiusA[:, i, j] = eval(thetaAs[i]) * uApproxR
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.trainedModel.data.resbb.dot(radiusb) * radiusb.conj(),
axis = 0)
# 'ijk,jkl,il->l', resAb, radiusA, radiusb.conj()
Lf = np.sum(np.tensordot(self.trainedModel.data.resAb, radiusA, 2)
* radiusb.conj(), axis = 0)
# 'ijkl,klt,ijt->t', resAA, radiusA, radiusA.conj()
LL = np.sum(np.tensordot(self.trainedModel.data.resAA, radiusA, 2)
* radiusA.conj(), axis = (0, 1))
return np.abs((LL - 2. * np.real(Lf) + ff) / ff) ** .5
def setupApprox(self, plotEst : bool = False):
"""Compute RB projection matrix."""
if self.checkComputedApprox():
return
RROMPyAssert(self._mode, message = "Cannot setup approximant.")
if self.verbosity >= 5:
verbosityDepth("INIT", "Setting up {}.". format(self.name()),
timestamp = self.timestamp)
self.greedy(plotEst)
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,
pMat, self.HFEngine.rescalingExp)
data.thetaAs = self.HFEngine.affineWeightsA(self.mu0)
data.thetabs = self.HFEngine.affineWeightsb(self.mu0,
self.homogeneized)
ARBs, bRBs = self.assembleReducedSystem(pMat)
self.trainedModel.data = data
else:
self.trainedModel = self.trainedModel
pMatOld = self.trainedModel.data.projMat
Sold = pMatOld.shape[1]
idxNew = list(range(Sold, pMat.shape[1]))
ARBs, bRBs = self.assembleReducedSystem(pMat(idxNew), pMatOld)
self.trainedModel.data.projMat = copy(pMat)
self.trainedModel.data.mus = copy(self.mus)
self.trainedModel.data.ARBs = ARBs
self.trainedModel.data.bRBs = bRBs
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, full : bool = False):
"""Build affine blocks of RB linear system through projections."""
self.assembleReducedResidualBlocksbb(self.bs)
if full:
pMat = self.trainedModel.data.projMat
self.assembleReducedResidualBlocksAb(self.As, self.bs, pMat)
self.assembleReducedResidualBlocksAA(self.As, pMat)
diff --git a/rrompy/reduction_methods/trained_model/trained_model.py b/rrompy/reduction_methods/trained_model/trained_model.py
index eb310e9..9a2b190 100644
--- a/rrompy/reduction_methods/trained_model/trained_model.py
+++ b/rrompy/reduction_methods/trained_model/trained_model.py
@@ -1,87 +1,89 @@
# 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 rrompy.utilities.base.types import Np1D, paramList, sampList
from rrompy.parameter import checkParameterList
from rrompy.sampling import sampleList, emptySampleList
__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 : paramList = []) -> sampList:
"""
Evaluate reduced representation of approximant at arbitrary parameter.
(ABSTRACT)
Args:
mu: Target parameter.
"""
pass
def getApprox(self, mu : paramList = []) -> sampList:
"""
Evaluate approximant at arbitrary parameter.
Args:
mu: Target parameter.
"""
mu, _ = checkParameterList(mu, self.data.npar)
- if not hasattr(self, "lastSolvedApp") or self.lastSolvedApp != mu:
- uAppRed = self.getApproxReduced(mu)
- self.uApp = emptySampleList()
- self.uApp.reset((self.data.projMat.shape[0], len(mu)),
- self.data.projMat.dtype)
+ if (not hasattr(self, "lastSolvedApprox")
+ or self.lastSolvedApprox != mu):
+ uApproxR = self.getApproxReduced(mu)
+ self.uApprox = emptySampleList()
+ self.uApprox.reset((self.data.projMat.shape[0], len(mu)),
+ self.data.projMat.dtype)
for i in range(len(mu)):
if isinstance(self.data.projMat, (list, sampleList,)):
- self.uApp[i] = uAppRed[i][0] * self.data.projMat[0]
- for j in range(1, uAppRed.shape[0]):
- self.uApp[i] += uAppRed[i][j] * self.data.projMat[j]
+ self.uApprox[i] = uApproxR[i][0] * self.data.projMat[0]
+ for j in range(1, uApproxR.shape[0]):
+ self.uApprox[i] += (uApproxR[i][j]
+ * self.data.projMat[j])
else:
- self.uApp[i] = self.data.projMat.dot(uAppRed[i])
- self.lastSolvedApp = mu
- return self.uApp
+ self.uApprox[i] = self.data.projMat.dot(uApproxR[i])
+ self.lastSolvedApprox = mu
+ return self.uApprox
@abstractmethod
def getPoles(self) -> Np1D:
"""
Obtain approximant poles.
Returns:
Numpy complex vector of poles.
"""
pass
diff --git a/rrompy/reduction_methods/trained_model/trained_model_pade.py b/rrompy/reduction_methods/trained_model/trained_model_pade.py
index 5891875..86a3d4f 100644
--- a/rrompy/reduction_methods/trained_model/trained_model_pade.py
+++ b/rrompy/reduction_methods/trained_model/trained_model_pade.py
@@ -1,145 +1,150 @@
# Copyright (C) 2018 by the RROMPy authors
#
# This file is part of RROMPy.
#
# RROMPy is free software: you can redistribute it and/or modify
# it under the terms of the GNU Lesser General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# RROMPy is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public License
# along with RROMPy. If not, see .
#
import numpy as np
from . import TrainedModel
from rrompy.utilities.base.types import (Np1D, List, paramVal, paramList,
sampList)
from rrompy.utilities.base import verbosityDepth
from rrompy.utilities.poly_fitting.polynomial import polyval, polyroots
+from rrompy.utilities.poly_fitting.radial_basis import polyval as polyvalR
from rrompy.utilities.exception_manager import RROMPyAssert
from rrompy.parameter import checkParameterList
from rrompy.sampling import sampleList
__all__ = ['TrainedModelPade']
class TrainedModelPade(TrainedModel):
"""
ROM approximant evaluation for Pade' approximant.
Attributes:
Data: dictionary with all that can be pickled.
"""
def centerNormalize(self, mu : paramList = [],
mu0 : paramVal = None) -> paramList:
"""
Compute normalized parameter to be plugged into approximant.
Args:
mu: Parameter(s) 1.
mu0: Parameter(s) 2. If None, set to self.data.mu0.
Returns:
Normalized parameter.
"""
mu, _ = checkParameterList(mu, self.data.npar)
if mu0 is None: mu0 = self.data.mu0
rad = ((mu ** self.data.rescalingExp - mu0 ** self.data.rescalingExp)
/ self.data.scaleFactor)
return rad
def getPVal(self, mu : paramList = [], der : List[int] = None) -> sampList:
"""
Evaluate Pade' numerator at arbitrary parameter.
Args:
mu: Target parameter.
der(optional): Derivatives to take before evaluation.
"""
mu, _ = checkParameterList(mu, self.data.npar)
if self.verbosity >= 17:
verbosityDepth("INIT", ("Evaluating numerator at mu = "
"{}.").format(mu),
timestamp = self.timestamp)
muCenter = self.centerNormalize(mu)
- p = sampleList(polyval(muCenter, self.data.P.T,
- self.data.polytype, der))
+ if "_" in self.data.polytypeP:
+ p = sampleList(polyvalR(muCenter, self.data.P,
+ self.data.polytypeP, der))
+ else:
+ p = sampleList(polyval(muCenter, self.data.P,
+ self.data.polytypeP, der))
if self.verbosity >= 17:
verbosityDepth("DEL", "Done evaluating numerator.",
timestamp = self.timestamp)
return p
def getQVal(self, mu:Np1D, der : List[int] = None,
scl : Np1D = None) -> Np1D:
"""
Evaluate Pade' denominator at arbitrary parameter.
Args:
mu: Target parameter.
der(optional): Derivatives to take before evaluation.
"""
mu, _ = checkParameterList(mu, self.data.npar)
if self.verbosity >= 17:
verbosityDepth("INIT", ("Evaluating denominator at mu = "
"{}.").format(mu),
timestamp = self.timestamp)
muCenter = self.centerNormalize(mu)
q = polyval(muCenter, self.data.Q, self.data.polytype, der, scl)
if self.verbosity >= 17:
verbosityDepth("DEL", "Done evaluating denominator.",
timestamp = self.timestamp)
return q
def getApproxReduced(self, mu : paramList = []) -> sampList:
"""
Evaluate reduced representation of approximant at arbitrary parameter.
Args:
mu: Target parameter.
"""
mu, _ = checkParameterList(mu, self.data.npar)
- if (not hasattr(self, "lastSolvedAppReduced")
- or self.lastSolvedAppReduced != mu):
+ if (not hasattr(self, "lastSolvedApproxReduced")
+ or self.lastSolvedApproxReduced != mu):
if self.verbosity >= 12:
verbosityDepth("INIT", ("Evaluating approximant at mu = "
"{}.").format(mu),
timestamp = self.timestamp)
- self.uAppReduced = self.getPVal(mu) / self.getQVal(mu)
+ self.uApproxReduced = self.getPVal(mu) / self.getQVal(mu)
if self.verbosity >= 12:
verbosityDepth("DEL", "Done evaluating approximant.",
timestamp = self.timestamp)
- self.lastSolvedAppReduced = mu
- return self.uAppReduced
+ self.lastSolvedApproxReduced = mu
+ return self.uApproxReduced
def getPoles(self) -> Np1D:
"""
Obtain approximant poles.
Returns:
Numpy complex vector of poles.
"""
RROMPyAssert(self.data.npar, 1, "Number of parameters")
return np.power(self.data.mu0(0) ** self.data.rescalingExp[0]
+ self.data.scaleFactor
* polyroots(self.data.Q, self.data.polytype),
1. / self.data.rescalingExp[0])
def getResidues(self) -> Np1D:
"""
Obtain approximant residues.
Returns:
Numpy matrix with residues as columns.
"""
pls = self.getPoles()
poles, _ = checkParameterList(pls, 1)
res = (self.data.projMat.dot(self.getPVal(poles).data)
/ self.getQVal(poles, 1))
return pls, res
diff --git a/rrompy/reduction_methods/trained_model/trained_model_rb.py b/rrompy/reduction_methods/trained_model/trained_model_rb.py
index 29bf5a9..9a7ae0b 100644
--- a/rrompy/reduction_methods/trained_model/trained_model_rb.py
+++ b/rrompy/reduction_methods/trained_model/trained_model_rb.py
@@ -1,113 +1,113 @@
# 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 .trained_model import TrainedModel
from rrompy.utilities.base.types import Np1D, paramList, sampList
from rrompy.utilities.base import verbosityDepth
from rrompy.utilities.exception_manager import RROMPyWarning, RROMPyAssert
from rrompy.parameter import checkParameterList
from rrompy.sampling import emptySampleList
__all__ = ['TrainedModelRB']
class TrainedModelRB(TrainedModel):
"""
ROM approximant evaluation for RB approximant.
Attributes:
Data: dictionary with all that can be pickled.
"""
def getApproxReduced(self, mu : paramList = []) -> sampList:
"""
Evaluate reduced representation of approximant at arbitrary parameter.
Args:
mu: Target parameter.
"""
mus, _ = checkParameterList(mu, self.data.npar)
- if (not hasattr(self, "lastSolvedAppReduced")
- or self.lastSolvedAppReduced != mus):
+ if (not hasattr(self, "lastSolvedApproxReduced")
+ or self.lastSolvedApproxReduced != mus):
if self.verbosity >= 12:
verbosityDepth("INIT", ("Computing RB solution at mu = "
"{}.").format(mus),
timestamp = self.timestamp)
thetaAs, thetabs = self.data.thetaAs, self.data.thetabs
ARBs, bRBs = self.data.ARBs, self.data.bRBs
- self.uAppReduced = emptySampleList()
- self.uAppReduced.reset((ARBs[0].shape[0], len(mu)),
- self.data.projMat.dtype)
+ self.uApproxReduced = emptySampleList()
+ self.uApproxReduced.reset((ARBs[0].shape[0], len(mu)),
+ self.data.projMat.dtype)
for i, muPL in enumerate(mus):
mu = muPL[0]
if self.verbosity >= 17:
verbosityDepth("INIT", ("Assembling reduced model for mu "
"= {}.").format(mu),
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 >= 17:
verbosityDepth("DEL", "Done assembling reduced model.",
timestamp = self.timestamp)
if self.verbosity >= 15:
verbosityDepth("INIT", ("Solving reduced model for mu = "
"{}.").format(mu),
timestamp = self.timestamp)
- self.uAppReduced[i] = np.linalg.solve(ARBmu, bRBmu)
+ self.uApproxReduced[i] = np.linalg.solve(ARBmu, bRBmu)
if self.verbosity >= 15:
verbosityDepth("DEL", "Done solving reduced model.",
timestamp = self.timestamp)
if self.verbosity >= 12:
verbosityDepth("DEL", "Done computing RB solution.",
timestamp = self.timestamp)
- self.lastSolvedAppReduced = mus
- return self.uAppReduced
+ self.lastSolvedApproxReduced = mus
+ return self.uApproxReduced
def getPoles(self) -> Np1D:
"""
Obtain approximant poles.
Returns:
Numpy complex vector of poles.
"""
RROMPyAssert(self.data.npar, 1, "Number of parameters")
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)
+ self.data.mu0(0, 0) ** self.data.rescalingExp[0],
1. / self.data.rescalingExp[0])
diff --git a/rrompy/sampling/base/pod_engine.py b/rrompy/sampling/base/pod_engine.py
index a81aed1..82a8699 100644
--- a/rrompy/sampling/base/pod_engine.py
+++ b/rrompy/sampling/base/pod_engine.py
@@ -1,149 +1,124 @@
# 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 deepcopy as copy
-from rrompy.utilities.base.types import Np1D, Tuple, HFEng, sampList
+from rrompy.utilities.base.types import Np1D, Np2D, Tuple, HFEng, sampList
from rrompy.sampling import sampleList
-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 GS(self, a:Np1D, Q:sampList, n : int = None,
- aA:Np1D = None, QA:sampList = None) -> Tuple[Np1D, Np1D, Np1D]:
+ def GS(self, a:Np1D, Q:sampList, n : int = -1) -> Tuple[Np1D, Np1D, bool]:
"""
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.
+ basis, whether computation is ill-conditioned.
"""
- if n is None:
+ if n == -1:
n = Q.shape[1]
- if aA is None != QA is None:
- raise RROMPyException(("Either both or none of augmented "
- "components must be provided."))
- r = np.zeros((n + 1,), dtype = a.dtype)
+ r = np.zeros((n + 1,), dtype = Q.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.flatten()
r[-1] = self.HFEngine.norm(a)
- if np.isclose(np.abs(r[-1]), 0.):
+ ill_cond = False
+ if np.isclose(np.abs(r[-1]) / np.linalg.norm(r), 0.):
+ ill_cond = True
r[-1] = 1.
a = a / r[-1]
- if aA is not None:
- aA = aA / r[-1]
- return a, r, aA
+ return a, r, ill_cond
- def QRGramSchmidt(self, A:sampList,
- only_R : bool = False) -> Tuple[sampList, Np1D]:
+ def generalizedQR(self, A:sampList, Q0 : sampList = None,
+ only_R : bool = False) -> Tuple[sampList, Np2D]:
"""
- 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 = copy(A)
- 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:sampList, Q0 : sampList = None,
- only_R : bool = False) -> Tuple[sampList, Np1D]:
- """
- Compute QR decomposition of a matrix through Householder method.
+ Compute generalized 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).
"""
- N = A.shape[1]
+ Nh, N = A.shape
B = copy(A)
V = copy(A)
R = np.zeros((N, N), dtype = A.dtype)
if Q0 is None:
Q = sampleList(np.zeros(A.shape, dtype = A.dtype)
+ np.random.randn(*(A.shape)))
else:
Q = copy(Q0)
for k in range(N):
- if Q0 is None:
- Q[k], _, _ = self.GS(Q[k], Q, k)
+ if k <= Nh:
+ if Q0 is None:
+ illC = True
+ while illC:
+ Q[k], _, illC = self.GS(np.random.randn(Nh), Q, k)
+ else:
+ Q[k] = np.zeros(Nh, dtype = Q.dtype)
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)
+ B.data[:, 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])
+ B.data[:, 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)
+ J = list(range(k, N))
vtQ = self.HFEngine.innerProduct(Q[J], V[k])
- Q[J] = Q[J] - 2 * np.outer(V[k], vtQ)
+ Q.data[:, 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 292204f..4619855 100644
--- a/rrompy/sampling/base/sampling_engine_base.py
+++ b/rrompy/sampling/base/sampling_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 numpy as np
from rrompy.utilities.base.types import (Np1D, HFEng, strLst, paramVal,
paramList, sampList)
from rrompy.utilities.base import verbosityDepth
from rrompy.utilities.exception_manager import RROMPyWarning
from rrompy.parameter import (emptyParameterList, checkParameter,
checkParameterList)
from rrompy.sampling import emptySampleList
__all__ = ['SamplingEngineBase']
class SamplingEngineBase:
"""HERE"""
def __init__(self, HFEngine:HFEng, verbosity : int = 10,
- timestamp : bool = True):
+ timestamp : bool = True, allowRepeatedSamples : bool = True):
self.verbosity = verbosity
self.timestamp = timestamp
+ self.allowRepeatedSamples = allowRepeatedSamples
if self.verbosity >= 10:
verbosityDepth("INIT",
"Initializing sampling engine of type {}.".format(
self.name()),
timestamp = self.timestamp)
self.HFEngine = HFEngine
if self.verbosity >= 10:
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 = emptySampleList()
self.nsamples = 0
self.mus = emptyParameterList()
self._derIdxs = []
def popSample(self):
if hasattr(self, "nsamples") and self.nsamples > 1:
if self.samples.shape[1] > self.nsamples:
RROMPyWarning(("More than 'nsamples' memory allocated for "
"samples. Popping empty sample column."))
self.nsamples += 1
self.nsamples -= 1
self.samples.pop()
self.mus.pop()
else:
self.resetHistory()
def preallocateSamples(self, u:sampList, mu:paramVal, n:int):
self.samples.reset((u.shape[0], n), u.dtype)
self.samples[0] = u
mu = checkParameter(mu, self.HFEngine.npar)
self.mus.reset((n, self.HFEngine.npar))
self.mus[0] = mu[0]
@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 : paramList = [], RHS : sampList = None,
homogeneized : bool = False) -> sampList:
"""
Solve linear system.
Args:
mu: Parameter value.
Returns:
Solution of system.
"""
mu, _ = checkParameterList(mu, self.HFEngine.npar)
if self.verbosity >= 15:
verbosityDepth("INIT", "Solving HF model for mu = {}.".format(mu),
timestamp = self.timestamp)
u = self.HFEngine.solve(mu, RHS, homogeneized)
if self.verbosity >= 15:
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, show : bool = True, **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.
show(optional): Whether to show figure. Defaults to True.
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),
save = save, what = what,
saveFormat = saveFormat, saveDPI = saveDPI,
show = show, **figspecs)
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),
filename = "{}_{}".format(filename, j),
time = times[j], what = what,
forceNewFile = forceNewFile,
folder = folders, filePW = filePW)
def outParaviewTimeDomainSamples(self, omegas : Np1D = None,
timeFinal : Np1D = None,
periodResolution : int = 20,
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
for j in range(self.nsamples):
self.HFEngine.outParaviewTimeDomain(self.samples[j],
omega = omegas[j],
timeFinal = timeFinal[j],
periodResolution = periodResolution,
name = "{}_{}".format(name, j),
filename = "{}_{}".format(filename, j),
forceNewFile = forceNewFile,
folder = folders)
diff --git a/rrompy/sampling/linear_problem/sampling_engine_linear.py b/rrompy/sampling/linear_problem/sampling_engine_linear.py
index f0ac79c..cdd7246 100644
--- a/rrompy/sampling/linear_problem/sampling_engine_linear.py
+++ b/rrompy/sampling/linear_problem/sampling_engine_linear.py
@@ -1,105 +1,118 @@
# 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.sampling.base.sampling_engine_base import SamplingEngineBase
from rrompy.utilities.base.types import Np1D, paramVal, paramList, sampList
from rrompy.utilities.base import verbosityDepth
from rrompy.utilities.exception_manager import RROMPyException
from rrompy.utilities.poly_fitting.polynomial import nextDerivativeIndices
from rrompy.parameter import checkParameter, checkParameterList
from rrompy.sampling import sampleList
__all__ = ['SamplingEngineLinear']
class SamplingEngineLinear(SamplingEngineBase):
"""HERE"""
def preprocesssamples(self, idxs:Np1D) -> sampList:
if self.samples is None or len(self.samples) == 0: return
return self.samples(idxs)
def postprocessu(self, u:sampList, overwrite : bool = False) -> Np1D:
- return u
+ return copy(u)
+
+ def postprocessuBulk(self, u:sampList) -> sampList:
+ return copy(u)
+
+ def lastSampleManagement(self):
+ pass
def _getSampleConcurrence(self, mu:paramVal, previous:Np1D,
homogeneized : bool = False) -> sampList:
if len(previous) >= len(self._derIdxs):
self._derIdxs += nextDerivativeIndices(self._derIdxs,
self.HFEngine.npar,
len(previous) + 1 - len(self._derIdxs))
derIdx = self._derIdxs[len(previous)]
mu = checkParameter(mu, self.HFEngine.npar)
samplesOld = self.preprocesssamples(previous)
RHS = self.HFEngine.b(mu, derIdx, homogeneized = homogeneized)
for j, derP in enumerate(self._derIdxs[: len(previous)]):
diffP = [x - y for (x, y) in zip(derIdx, derP)]
if np.all([x >= 0 for x in diffP]):
RHS -= self.HFEngine.A(mu, diffP).dot(samplesOld[j])
return self.solveLS(mu, RHS = RHS, homogeneized = homogeneized)
def nextSample(self, mu : paramVal = [], overwrite : bool = False,
- homogeneized : bool = False) -> Np1D:
+ homogeneized : bool = False,
+ lastSample : bool = True) -> Np1D:
mu = checkParameter(mu, self.HFEngine.npar)
ns = self.nsamples
muidxs = self.mus.findall(mu[0])
if len(muidxs) > 0:
u = self._getSampleConcurrence(mu, np.sort(muidxs), homogeneized)
else:
u = self.solveLS(mu, homogeneized = homogeneized)
u = self.postprocessu(u, overwrite = overwrite)
if overwrite:
self.samples[ns] = u
self.mus[ns] = mu[0]
else:
if ns == 0:
self.samples = sampleList(u)
else:
self.samples.append(u)
self.mus.append(mu)
self.nsamples += 1
+ if lastSample: self.lastSampleManagement()
return u
def iterSample(self, mus:paramList,
homogeneized : bool = False) -> sampList:
mus, _ = checkParameterList(mus, self.HFEngine.npar)
if self.verbosity >= 5:
verbosityDepth("INIT", "Starting sampling iterations.",
timestamp = self.timestamp)
n = len(mus)
if n <= 0:
raise RROMPyException(("Number of samples must be positive."))
self.resetHistory()
- if self.verbosity >= 7:
- verbosityDepth("MAIN", "Computing sample {}/{}.".format(1, n),
- timestamp = self.timestamp)
- u = self.nextSample(mus[0], homogeneized = homogeneized)
- if n > 1:
- self.preallocateSamples(u, mus[0], n)
- for j in range(1, n):
+
+ if self.allowRepeatedSamples:
+ for j in range(n):
if self.verbosity >= 7:
- verbosityDepth("MAIN",
- "Computing sample {}/{}.".format(j + 1, n),
+ verbosityDepth("MAIN", ("Computing sample "
+ "{} / {}.").format(j + 1, n),
timestamp = self.timestamp)
- self.nextSample(mus[j], overwrite = True,
- homogeneized = homogeneized)
+ self.nextSample(mus[j], overwrite = (j > 0),
+ homogeneized = homogeneized,
+ lastSample = (n == j + 1))
+ if j == 0:
+ self.preallocateSamples(self.samples[0], mus[0], n)
+ else:
+ self.samples = self.postprocessuBulk(self.solveLS(mus,
+ homogeneized = homogeneized))
+ self.mus = copy(mus)
+ self.nsamples = n
if self.verbosity >= 5:
verbosityDepth("DEL", "Finished sampling iterations.",
timestamp = self.timestamp)
return self.samples
diff --git a/rrompy/sampling/linear_problem/sampling_engine_linear_pod.py b/rrompy/sampling/linear_problem/sampling_engine_linear_pod.py
index 2f4a100..bcfea23 100644
--- a/rrompy/sampling/linear_problem/sampling_engine_linear_pod.py
+++ b/rrompy/sampling/linear_problem/sampling_engine_linear_pod.py
@@ -1,83 +1,84 @@
# 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 deepcopy as copy
from rrompy.sampling.base.pod_engine import PODEngine
from .sampling_engine_linear import SamplingEngineLinear
from rrompy.utilities.base.types import Np1D, paramVal, sampList
from rrompy.utilities.base import verbosityDepth
+from rrompy.sampling import sampleList
__all__ = ['SamplingEngineLinearPOD']
class SamplingEngineLinearPOD(SamplingEngineLinear):
"""HERE"""
def resetHistory(self):
super().resetHistory()
+ self.samples_full = None
self.RPOD = None
def popSample(self):
if hasattr(self, "nsamples") and self.nsamples > 1:
self.RPOD = self.RPOD[: -1, : -1]
+ self.samples_full.pop()
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, idxs:Np1D) -> sampList:
- idxMax = np.max(idxs) + 1
- sampleBase = super().preprocesssamples(np.arange(idxMax))
- RPODBase = self.RPOD[: idxMax, idxs]
- return sampleBase.dot(RPODBase)
+ if self.samples_full is None or len(self.samples_full) == 0: return
+ return self.samples_full(idxs)
def postprocessu(self, u:sampList, overwrite : bool = False) -> Np1D:
- if self.verbosity >= 10:
- 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(np.arange(ns)), ns)
if overwrite:
- self.RPOD[: ns + 1, ns] = r
+ self.samples_full[ns] = copy(u)
else:
if ns == 0:
- self.RPOD = r.reshape((1, 1))
+ self.samples_full = sampleList(u)
else:
- self.RPOD=np.block([[ self.RPOD, r[:-1, None]],
- [np.zeros((1, ns)), r[-1]]])
+ self.samples_full.append(u)
+ return u
+
+ def postprocessuBulk(self, u:sampList) -> sampList:
+ self.samples_full = copy(u)
+ if self.verbosity >= 10:
+ verbosityDepth("INIT", "Starting orthogonalization.",
+ timestamp = self.timestamp)
+ u, self.RPOD = self.PODEngine.generalizedQR(self.samples_full)
if self.verbosity >= 10:
verbosityDepth("DEL", "Done orthogonalizing.",
timestamp = self.timestamp)
return u
-
+
+ def lastSampleManagement(self):
+ self.samples = self.postprocessuBulk(self.samples_full)
+
def preallocateSamples(self, u:Np1D, mu:paramVal, 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]
-
+ self.samples_full.reset((u.shape[0], n), u.dtype)
+ self.samples_full[0] = u
diff --git a/rrompy/solver/fenics/__init__.py b/rrompy/solver/fenics/__init__.py
index 1e1a506..6936f3c 100644
--- a/rrompy/solver/fenics/__init__.py
+++ b/rrompy/solver/fenics/__init__.py
@@ -1,38 +1,48 @@
# 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_constants import (fenZERO, fenZEROS, fenONE, fenONES,
bdrTrue, bdrFalse)
-from .fenics_norms import (L2NormMatrix, H1NormMatrix, Hminus1NormMatrix,
- elasticNormMatrix, elasticDualNormMatrix)
+from .fenics_la import fenics2Sparse, fenics2Vector
+from .fenics_norms import (L2NormMatrix, L2InverseNormMatrix, H1NormMatrix,
+ Hminus1NormMatrix, elasticNormMatrix,
+ elasticDualNormMatrix)
+from .fenics_plotting import fenplot, affine_warping
+from .fenics_projecting import interp_project
__all__ = [
'fenZERO',
'fenZEROS',
'fenONE',
'fenONES',
'bdrTrue',
'bdrFalse',
+ 'fenics2Sparse',
+ 'fenics2Vector',
'L2NormMatrix',
+ 'L2InverseNormMatrix',
'H1NormMatrix',
'Hminus1NormMatrix',
'elasticNormMatrix',
- 'elasticDualNormMatrix'
+ 'elasticDualNormMatrix',
+ 'fenplot',
+ 'affine_warping',
+ 'interp_project'
]
diff --git a/rrompy/solver/fenics/fenics_la.py b/rrompy/solver/fenics/fenics_la.py
new file mode 100644
index 0000000..d2bace5
--- /dev/null
+++ b/rrompy/solver/fenics/fenics_la.py
@@ -0,0 +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 array, complex
+from scipy.sparse import csr_matrix
+import fenics as fen
+from rrompy.utilities.base.types import Np2D, FenBC, FenExpr
+
+__all__ = ['fenics2Sparse', 'fenics2Vector']
+
+def fenics2Sparse(expr:FenExpr, formCompPars : dict = {}, DBC : FenBC = None,
+ BCType : int = -1, dtype = complex) -> Np2D:
+ assembled = fen.assemble(expr, form_compiler_parameters = formCompPars)
+ if BCType == 0:
+ DBC.zero(assembled)
+ elif BCType >= 1:
+ DBC.apply(assembled)
+ emat = fen.as_backend_type(assembled).mat()
+ er, ec, ev = emat.getValuesCSR()
+ return csr_matrix((ev, ec, er), shape = emat.size, dtype = dtype)
+
+def fenics2Vector(expr:FenExpr, formCompPars : dict = {}, DBC : FenBC = None,
+ BCType : int = -1, dtype = complex) -> Np2D:
+ assembled = fen.assemble(expr, form_compiler_parameters = formCompPars)
+ if BCType == 0:
+ DBC.zero(assembled)
+ elif BCType >= 1:
+ DBC.apply(assembled)
+ return array(assembled[:], dtype = dtype)
+
diff --git a/rrompy/solver/fenics/fenics_norms.py b/rrompy/solver/fenics/fenics_norms.py
index a4e36c8..5c5d7c7 100644
--- a/rrompy/solver/fenics/fenics_norms.py
+++ b/rrompy/solver/fenics/fenics_norms.py
@@ -1,76 +1,89 @@
# 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 scipy.sparse import csr_matrix
import fenics as fen
from rrompy.utilities.base.types import Np2D, FenFunc, DictAny, FenFuncSpace
-from rrompy.solver import Np2DLikeInv, Np2DLikeInvLowRank
+from rrompy.solver.norm_utilities import (Np2DLikeEye, Np2DLikeInv,
+ Np2DLikeInvLowRank)
+from .fenics_la import fenics2Sparse
-__all__ = ['L2NormMatrix', 'H1NormMatrix', 'Hminus1NormMatrix',
- 'elasticNormMatrix', 'elasticDualNormMatrix']
-
-def _fen2sp(expr):
- matFen = fen.as_backend_type(fen.assemble(expr)).mat()
- return csr_matrix(matFen.getValuesCSR()[::-1], shape = matFen.size)
+__all__ = ['L2NormMatrix', 'L2InverseNormMatrix', 'H1NormMatrix',
+ 'Hminus1NormMatrix', 'elasticNormMatrix', 'elasticDualNormMatrix']
def L2NormMatrix(V:FenFuncSpace, r_ : FenFunc = 1.) -> Np2D:
u = fen.TrialFunction(V)
v = fen.TestFunction(V)
- return _fen2sp(r_ * fen.dot(u, v) * fen.dx)
+ return fenics2Sparse(r_ * fen.dot(u, v) * fen.dx)
+
+def L2InverseNormMatrix(V:FenFuncSpace, r_ : FenFunc = 1.,
+ solverType : str = "SPSOLVE",
+ solverArgs : DictAny = {}, compressRank : int = None,
+ compressOversampling : int = 10,
+ compressSeed : int = 420) -> Np2D:
+ if compressRank is None:
+ return Np2DLikeInv(L2NormMatrix(V, r_), Np2DLikeEye(), solverType,
+ solverArgs)
+ return Np2DLikeInvLowRank(L2NormMatrix(V, r_), Np2DLikeEye(), solverType,
+ solverArgs, compressRank, compressOversampling,
+ compressSeed)
def H1NormMatrix(V:FenFuncSpace, w : float = 0., r_ : FenFunc = 1.,
a_ : FenFunc = 1.) -> Np2D:
u = fen.TrialFunction(V)
v = fen.TestFunction(V)
- return _fen2sp((w * r_ * fen.dot(u, v)
- + fen.dot(a_ * fen.grad(u), fen.grad(v))) * fen.dx)
+ return fenics2Sparse((w * r_ * fen.dot(u, v)
+ + fen.dot(a_ * fen.grad(u), fen.grad(v))) * fen.dx)
def Hminus1NormMatrix(V:FenFuncSpace, w : float = 0., r_ : FenFunc = 1.,
a_ : FenFunc = 1., solverType : str = "SPSOLVE",
solverArgs : DictAny = {}, compressRank : int = None,
compressOversampling : int = 10,
- compressSeed : int = 420) -> Np2D:
+ compressSeed : int = 420, duality : bool = True) -> Np2D:
+ identity = L2NormMatrix(V, r_) if duality else Np2DLikeEye()
if compressRank is None:
- return Np2DLikeInv(H1NormMatrix(V, w, r_, a_), L2NormMatrix(V, r_),
- solverType, solverArgs)
- return Np2DLikeInvLowRank(H1NormMatrix(V, w, r_, a_), L2NormMatrix(V, r_),
- solverType, solverArgs, compressRank,
- compressOversampling, compressSeed)
+ return Np2DLikeInv(H1NormMatrix(V, w, r_, a_), identity, solverType,
+ solverArgs)
+ return Np2DLikeInvLowRank(H1NormMatrix(V, w, r_, a_), identity, solverType,
+ solverArgs, compressRank, compressOversampling,
+ compressSeed)
def elasticNormMatrix(V:FenFuncSpace, l_:FenFunc, m_:FenFunc,
w : float = 0., r_ : FenFunc = 1.) -> Np2D:
u = fen.TrialFunction(V)
v = fen.TestFunction(V)
epsilon = lambda f: 0.5 * (fen.grad(f) + fen.nabla_grad(f))
sigma = (l_ * fen.div(u) * fen.Identity(u.geometric_dimension())
+ 2. * m_ * epsilon(u))
- return _fen2sp((w * r_ * fen.dot(u, v)
- + fen.inner(sigma, epsilon(v))) * fen.dx)
+ return fenics2Sparse((w * r_ * fen.dot(u, v)
+ + fen.inner(sigma, epsilon(v))) * fen.dx)
def elasticDualNormMatrix(V:FenFuncSpace, l_:FenFunc, m_:FenFunc,
w : float = 0., solverType : str = "SPSOLVE",
solverArgs : DictAny = {}, r_ : FenFunc = 1.,
compressRank : int = None,
compressOversampling : int = 10,
- compressSeed : int = 420) -> Np2D:
+ compressSeed : int = 420,
+ duality : bool = True) -> Np2D:
+ identity = L2NormMatrix(V, r_) if duality else Np2DLikeEye()
if compressRank is None:
- return Np2DLikeInv(elasticNormMatrix(V, l_, m_, w, r_),
- L2NormMatrix(V, r_), solverType, solverArgs)
- return Np2DLikeInvLowRank(elasticNormMatrix(V, l_, m_, w, r_),
- L2NormMatrix(V, r_), solverType, solverArgs,
- compressRank, compressOversampling, compressSeed)
+ return Np2DLikeInv(elasticNormMatrix(V, l_, m_, w, r_), identity,
+ solverType, solverArgs)
+ return Np2DLikeInvLowRank(elasticNormMatrix(V, l_, m_, w, r_), identity,
+ solverType, solverArgs, compressRank,
+ compressOversampling, compressSeed)
+
diff --git a/rrompy/solver/fenics/fenics_plotting.py b/rrompy/solver/fenics/fenics_plotting.py
new file mode 100644
index 0000000..d6de450
--- /dev/null
+++ b/rrompy/solver/fenics/fenics_plotting.py
@@ -0,0 +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
+import dolfin.cpp as cpp
+import ufl
+import fenics as fen
+from rrompy.utilities.base.types import Np1D, Np2D
+from .fenics_projecting import interp_project
+
+_all_plottable_types = (cpp.function.Function, cpp.function.Expression,
+ cpp.mesh.Mesh, cpp.fem.DirichletBC,
+ cpp.mesh.MeshFunctionBool, cpp.mesh.MeshFunctionInt,
+ cpp.mesh.MeshFunctionDouble,
+ cpp.mesh.MeshFunctionSizet)
+
+__all__ = ['fenplot', 'affine_warping']
+
+def fenplot(object, *args, warping = None, **kwargs):
+ "See dolfin.common.plot for more details."
+ mesh = kwargs.pop('mesh', None)
+ if isinstance(object, cpp.mesh.Mesh):
+ if mesh is not None and mesh.id() != object.id():
+ raise RuntimeError("Got different mesh in plot object and keyword "
+ "argument")
+ mesh = object
+ if mesh is None:
+ if isinstance(object, cpp.function.Function):
+ mesh = object.function_space().mesh()
+ elif hasattr(object, "mesh"):
+ mesh = object.mesh()
+ if not isinstance(object, _all_plottable_types):
+ from dolfin.fem.projection import project
+ try:
+ #cpp.log.info("Object cannot be plotted directly, projecting to "
+ # "piecewise linears.")
+ object = project(object, mesh = mesh)
+ mesh = object.function_space().mesh()
+ object = object._cpp_object
+ except Exception as e:
+ msg = "Don't know how to plot given object:\n %s\n" \
+ "and projection failed:\n %s" % (str(object), str(e))
+ raise RuntimeError(msg)
+
+ if warping is not None:
+ fen.ALE.move(mesh, interp_project(warping[0], mesh))
+ out = fen.plot(object, *args, mesh = mesh, **kwargs)
+ if warping is not None:
+ fen.ALE.move(mesh, interp_project(warping[1], mesh))
+ return out
+
+def affine_warping(mesh, A:Np2D, b : Np1D = None):
+ coords = fen.SpatialCoordinate(mesh)[:]
+ ndim = mesh.topology().dim()
+ if b is None: b = [0.] * ndim
+ assert A.shape[0] == ndim and A.shape[1] == ndim and len(b) == ndim
+ Ainv = np.linalg.inv(A)
+ warp = [- 1. * c for c in coords]
+ warpInv = [- 1. * c for c in coords]
+ for i in range(ndim):
+ warp[i] = warp[i] + b[i]
+ for j in range(ndim):
+ warp[i] = warp[i] + A[i, j] * coords[j]
+ warpInv[i] = warpInv[i] + Ainv[i, j] * (coords[j] - b[j])
+ return tuple([ufl.as_vector(tuple(w)) for w in [warp, warpInv]])
diff --git a/rrompy/utilities/poly_fitting/radial_basis/__init__.py b/rrompy/solver/fenics/fenics_projecting.py
similarity index 63%
copy from rrompy/utilities/poly_fitting/radial_basis/__init__.py
copy to rrompy/solver/fenics/fenics_projecting.py
index 387db9e..fe20165 100644
--- a/rrompy/utilities/poly_fitting/radial_basis/__init__.py
+++ b/rrompy/solver/fenics/fenics_projecting.py
@@ -1,29 +1,32 @@
# 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 .radial_basis_fitter import (radialBasisFitter, radialGaussian,
- thinPlateSpline, multiQuadric)
-__all__ = [
- 'radialBasisFitter',
- 'radialGaussian',
- 'thinPlateSpline',
- 'multiQuadric'
- ]
+import fenics as fen
+import dolfin.cpp as cpp
+__all__ = ['interp_project']
+def interp_project(object, destination):
+ "See dolfin.common.plot for more details."
+ if isinstance(destination, cpp.mesh.Mesh):
+ destination = fen.VectorFunctionSpace(destination, "Lagrange", 1)
+ try:
+ return fen.interpolate(object, destination)
+ except:
+ return fen.project(object, destination)
diff --git a/rrompy/solver/norm_utilities.py b/rrompy/solver/norm_utilities.py
index d8b0293..96b151f 100644
--- a/rrompy/solver/norm_utilities.py
+++ b/rrompy/solver/norm_utilities.py
@@ -1,78 +1,89 @@
# Copyright (C) 2018 by the RROMPy authors
#
# This file is part of RROMPy.
#
# RROMPy is free software: you can redistribute it and/or modify
# it under the terms of the GNU Lesser General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# RROMPy is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public License
# along with RROMPy. If not, see .
#
from abc import abstractmethod
import numpy as np
from copy import deepcopy as copy
from rrompy.utilities.base.types import Np1D, Np2D, DictAny
from rrompy.solver.linear_solver import setupSolver
from rrompy.utilities.exception_manager import RROMPyException
__all__ = ['Np2DLike', 'Np2DLikeEye', 'Np2DLikeInv', 'Np2DLikeInvLowRank',
'normEngine']
@abstractmethod
class Np2DLike:
def dot(self, u:Np2D) -> Np2D:
pass
class Np2DLikeEye(Np2DLike):
+ @property
+ def T(self):
+ return self
+
+ def conj(self):
+ return self
+
def dot(self, u:Np2D) -> Np2D:
return u
class Np2DLikeInv(Np2DLike):
def __init__(self, K:Np2D, M:Np2D, solverType:str, solverArgs:DictAny):
self.K, self.M, self.MH = K, M, M.T.conj()
- self.solver, self.solverArgs = setupSolver(solverType, solverArgs)
+ try:
+ self.solver, self.solverArgs = setupSolver(solverType, solverArgs)
+ except:
+ self.solver, self.solverArgs = solverType, solverArgs
def dot(self, u:Np2D) -> Np2D:
- return self.MH.dot(self.solver(self.K, self.M.dot(u), self.solverArgs))
+ return self.MH.dot(self.solver(self.K, self.M.dot(u),
+ self.solverArgs)).reshape(u.shape)
class Np2DLikeInvLowRank(Np2DLike):
def __init__(self, K:Np2D, M:Np2D, solverType:str, solverArgs:DictAny,
rank:int, oversampling : int = 10, seed : int = 420):
if rank > M.shape[1]:
raise RROMPyException(("Cannot select compressed rank larger than "
"original size."))
if oversampling < 0:
raise RROMPyException("Oversampling parameter must be positive.")
HF = Np2DLikeInv(K, M, solverType, solverArgs)
np.random.seed(seed)
xs = np.random.randn(M.shape[1], rank + oversampling)
samples = HF.dot(xs)
Q, _ = np.linalg.qr(samples, mode = "reduced")
R = HF.dot(Q).T.conj() # assuming HF (i.e. K) hermitian...
U, s, Vh = np.linalg.svd(R)
self.L = Q.dot(U[:, : rank]) * s[: rank]
self.R = Vh[: rank, :]
def dot(self, u:Np2D) -> Np2D:
- return self.L.dot(self.R.dot(u))
+ return self.L.dot(self.R.dot(u)).reshape(u.shape)
class normEngine:
def __init__(self, energyNormMatrix:Np2D):
self.energyNormMatrix = copy(energyNormMatrix)
def innerProduct(self, u:Np2D, v:Np2D, onlyDiag : bool = False) -> Np2D:
if not isinstance(u, (np.ndarray,)): u = u.data
if not isinstance(v, (np.ndarray,)): v = v.data
if onlyDiag:
return np.sum(self.energyNormMatrix.dot(u) * v.conj(), axis = 0)
return v.T.conj().dot(self.energyNormMatrix.dot(u))
def norm(self, u:Np2D) -> Np1D:
return np.power(np.abs(self.innerProduct(u, u, onlyDiag = True)), .5)
diff --git a/tests/test_1_utilities/fenics_const.py b/rrompy/solver/scipy/__init__.py
similarity index 84%
copy from tests/test_1_utilities/fenics_const.py
copy to rrompy/solver/scipy/__init__.py
index 96d611e..c126e95 100644
--- a/tests/test_1_utilities/fenics_const.py
+++ b/rrompy/solver/scipy/__init__.py
@@ -1,20 +1,25 @@
# 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 .
#
-def test_fenics_loads():
- from rrompy.solver.fenics import fenZERO, fenZEROS, fenONE, fenONES
+from .scipy_tensorize import tensorizeLS, detensorizeLS
+
+__all__ = [
+ 'tensorizeLS',
+ 'detensorizeLS'
+ ]
+
diff --git a/rrompy/solver/scipy/scipy_tensorize.py b/rrompy/solver/scipy/scipy_tensorize.py
new file mode 100644
index 0000000..3eaa14d
--- /dev/null
+++ b/rrompy/solver/scipy/scipy_tensorize.py
@@ -0,0 +1,51 @@
+# 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
+from rrompy.utilities.base.types import Np1D, Np2D, List
+from rrompy.utilities.exception_manager import RROMPyException
+
+__all__ = ['tensorizeLS', 'detensorizeLS']
+
+def tensorizeLS(As : List[Np2D] = [], bs : List[Np1D] = [],
+ AFormat : str = "csr"):
+ if len(As) > 0:
+ A = scsp.block_diag(As, format = AFormat)
+ else:
+ A = None
+ if len(bs) > 0:
+ b = np.concatenate(bs, axis = None)
+ else:
+ b = None
+ return A, b
+
+def detensorizeLS(x:Np1D, n : int = 0, sizes : List[int] = []):
+ if n > 0 and len(sizes) > 0 and n != len(sizes):
+ raise RROMPyException("Specified n and sizes are inconsistent.")
+ if n == 0 and len(sizes) == 0:
+ raise RROMPyException("Must specify either n or sizes.")
+ if len(sizes) == 0:
+ sizes = [len(x) // n] * n
+ if n * sizes[0] != len(x):
+ raise RROMPyException(("Number of chunks must divide vector "
+ "length."))
+ n = len(sizes)
+ sEnd = np.cumsum(sizes)
+ sStart = sEnd - sizes[0]
+ return [x[sStart[j] : sEnd[j]] for j in range(n)]
diff --git a/rrompy/utilities/base/types.py b/rrompy/utilities/base/types.py
index 2cb7343..b84e189 100644
--- a/rrompy/utilities/base/types.py
+++ b/rrompy/utilities/base/types.py
@@ -1,57 +1,61 @@
# 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 typing import TypeVar, List, Tuple, Dict, Any
__all__ = ['TupleAny','ListAny','DictAny','ScOp','Np1D','Np2D','Np1DLst',
- 'N2FSExpr','FenExpr','FenFunc','FenFuncSpace','HFEng','ROMEng',
- 'sampleEng','normEng','paramVal','paramList', 'sampList',
+ 'N2FSExpr','FenExpr','FenFunc','FenFuncSpace','FenBC','HFEng',
+ 'ROMEng','sampleEng','normEng','paramVal','paramList', 'sampList',
'GenExpr','strLst', 'BfSExpr']
# ANY
TupleAny = Tuple[Any]
ListAny = List[Any]
DictAny = Dict[Any, Any]
# SCIPY
ScOp = TypeVar("Scipy sparse matrix for space operator")
# NUMPY
Np1D = TypeVar("NumPy 1D array")
Np2D = TypeVar("NumPy 2D array-like")
Np1DLst = TypeVar("NumPy 1D array or list of NumPy 1D array")
N2FSExpr = TypeVar("NumPy 2D array, float or str")
# FENICS
FenExpr = TypeVar("FEniCS expression")
FenFunc = TypeVar("FEniCS function")
FenFuncSpace = TypeVar("FEniCS function space")
+FenBC = TypeVar("FEniCS boundary condition")
# ENGINES
HFEng = TypeVar("High fidelity engine")
ROMEng = TypeVar("ROM engine")
sampleEng = TypeVar("Sampling engine")
normEng = TypeVar("Norm engine")
-# OTHERS
+# CUSTOM TYPES
paramVal = TypeVar("Parameter value tuple")
paramList = TypeVar("Parameter value tuple list")
sampList = TypeVar("Sample list")
+radialFun = TypeVar("Radial basis function")
+
+# OTHERS
GenExpr = TypeVar("Generic expression")
strLst = TypeVar("str or list of str")
BfSExpr = TypeVar("Boolean function or string")
diff --git a/rrompy/utilities/poly_fitting/polynomial/__init__.py b/rrompy/utilities/poly_fitting/polynomial/__init__.py
index 0b50bad..5f6eade 100644
--- a/rrompy/utilities/poly_fitting/polynomial/__init__.py
+++ b/rrompy/utilities/poly_fitting/polynomial/__init__.py
@@ -1,43 +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 .base import (polybases, polyfitname, polydomcoeff)
from .der import polyder
from .val import polyval
from .vander import polyvander
from .roots import polyroots
from .derivative import nextDerivativeIndices
from .hash_derivative import hashDerivativeToIdx, hashIdxToDerivative
-from .homogeneization import homogeneizationMask, homogeneizedpolyvander
+from .homogeneization import (homogeneizationMask, homogeneizedpolyvander,
+ homogeneizedToFull)
__all__ = [
'polybases',
'polyfitname',
'polydomcoeff',
'polyder',
'polyval',
'polyvander',
'polyroots',
'nextDerivativeIndices',
'hashDerivativeToIdx',
'hashIdxToDerivative',
'homogeneizationMask',
- 'homogeneizedpolyvander'
+ 'homogeneizedpolyvander',
+ 'homogeneizedToFull'
]
diff --git a/rrompy/utilities/poly_fitting/polynomial/homogeneization.py b/rrompy/utilities/poly_fitting/polynomial/homogeneization.py
index f42d5e1..b4fbb6f 100644
--- a/rrompy/utilities/poly_fitting/polynomial/homogeneization.py
+++ b/rrompy/utilities/poly_fitting/polynomial/homogeneization.py
@@ -1,55 +1,62 @@
# 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, paramList
-from rrompy.utilities.poly_fitting.polynomial import polyvander
+from rrompy.utilities.poly_fitting.polynomial import (polyvander,
+ hashIdxToDerivative as hashI)
from rrompy.parameter import checkParameterList
-__all__ = ['homogeneizationMask', 'homogeneizedpolyvander']
+__all__ = ['homogeneizationMask', 'homogeneizedpolyvander',
+ 'homogeneizedToFull']
def homogeneizationMask(degs:List[int]) -> Tuple[List[int], List[bool]]:
dim = len(degs)
N = np.prod([d + 1 for d in degs])
maskN = np.arange(N, dtype = int)
remN = np.zeros((N, dim), dtype = int)
for j in range(dim - 1, -1, -1):
remN[:, j] = maskN % (degs[j] + 1)
maskN //= degs[j] + 1
mask = np.sum(remN, axis = 1) <= min(degs)
return remN[mask], mask
def homogeneizedpolyvander(x:paramList, deg:int, basis:str,
derIdxs : List[List[List[int]]] = None,
reorder : List[int] = None, scl : Np1D = None)\
-> Tuple[Np2D, List[List[int]], List[int]]:
x, _ = checkParameterList(x)
degs = [deg] * x.shape[1]
VanBase = polyvander(x, degs, basis, derIdxs, reorder, scl)
derIdxs, mask = homogeneizationMask(degs)
ordIdxs = np.empty(len(derIdxs), dtype = int)
derTotal = np.array([np.sum(y) for y in derIdxs])
idxPrev = 0
rangeAux = np.arange(len(derIdxs))
for j in range(deg + 1):
idxLocal = rangeAux[derTotal == j][::-1]
idxPrev += len(idxLocal)
ordIdxs[idxPrev - len(idxLocal) : idxPrev] = idxLocal
return VanBase[:, mask], derIdxs, ordIdxs
+def homogeneizedToFull(shapeFull:Tuple[int], dim:int, coeffs:Np2D) -> Np2D:
+ full = np.zeros(shapeFull, dtype = coeffs.dtype)
+ for j in range(len(coeffs)):
+ full[tuple(hashI(j, dim))] = coeffs[j]
+ return full
\ No newline at end of file
diff --git a/rrompy/utilities/poly_fitting/radial_basis/__init__.py b/rrompy/utilities/poly_fitting/radial_basis/__init__.py
index 387db9e..e4d799d 100644
--- a/rrompy/utilities/poly_fitting/radial_basis/__init__.py
+++ b/rrompy/utilities/poly_fitting/radial_basis/__init__.py
@@ -1,29 +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 .radial_basis_fitter import (radialBasisFitter, radialGaussian,
- thinPlateSpline, multiQuadric)
+from .kernel import radialGaussian, thinPlateSpline, multiQuadric
+from .base import rbbases, polybases, polyfitname, polydomcoeff, radialFunction
+from .der import polyder
+from .val import polyval
+from .vander import rbvander, polyvander
+from .homogeneization import homogeneizedpolyvander
__all__ = [
- 'radialBasisFitter',
'radialGaussian',
'thinPlateSpline',
- 'multiQuadric'
+ 'multiQuadric',
+ 'rbbases',
+ 'polybases',
+ 'polyfitname',
+ 'polydomcoeff',
+ 'radialFunction',
+ 'polyder',
+ 'polyval',
+ 'rbvander',
+ 'polyvander',
+ 'homogeneizedpolyvander'
]
diff --git a/rrompy/utilities/poly_fitting/radial_basis/base.py b/rrompy/utilities/poly_fitting/radial_basis/base.py
new file mode 100644
index 0000000..d5ace7d
--- /dev/null
+++ b/rrompy/utilities/poly_fitting/radial_basis/base.py
@@ -0,0 +1,55 @@
+# 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 itertools import product
+from rrompy.utilities.base.types import Np1D, Np2D, paramList
+from rrompy.utilities.exception_manager import RROMPyException
+from rrompy.utilities.poly_fitting.polynomial.base import polydomcoeff as \
+ polydomcoeffB
+
+__all__ = ['rbbases', 'polybases', 'polyfitname', 'polydomcoeff',
+ 'radialFunction']
+
+rbbases = ["GAUSSIAN", "THINPLATE", "MULTIQUADRIC"]
+
+polybases = [x + "_" + y for x, y in product(
+ ["CHEBYSHEV", "LEGENDRE", "MONOMIAL"], rbbases)]
+
+def polyfitname(basis:str) -> str:
+ fitpnames = {"CHEBYSHEV" : "chebfit", "LEGENDRE" : "legfit",
+ "MONOMIAL" : "polyfit"}
+ fitrnames = {"GAUSSIAN" : "gaussian", "THINPLATE" : "thinplate",
+ "MULTIQUADRIC" : "multiquadric"}
+ basisp, basisr = basis.split("_")
+ try:
+ return fitpnames[basisp] + "_" + fitrnames[basisr]
+ except:
+ raise RROMPyException("Polynomial-radial basis combination not "
+ "recognized.")
+
+def polydomcoeff(n:int, basis:str) -> float:
+ return polydomcoeffB(n, basis.split("_")[0])
+
+class radialFunction:
+ def __init__(self, supportPoints : paramList = None,
+ directionalWeights : Np1D = None, localCoeffs : Np1D = None,
+ globalCoeffs : Np2D = None):
+ self.supportPoints = supportPoints
+ self.directionalWeights = directionalWeights
+ self.localCoeffs = localCoeffs
+ self.globalCoeffs = globalCoeffs
diff --git a/rrompy/utilities/poly_fitting/radial_basis/__init__.py b/rrompy/utilities/poly_fitting/radial_basis/der.py
similarity index 61%
copy from rrompy/utilities/poly_fitting/radial_basis/__init__.py
copy to rrompy/utilities/poly_fitting/radial_basis/der.py
index 387db9e..89f2aed 100644
--- a/rrompy/utilities/poly_fitting/radial_basis/__init__.py
+++ b/rrompy/utilities/poly_fitting/radial_basis/der.py
@@ -1,29 +1,30 @@
# Copyright (C) 2018 by the RROMPy authors
#
# This file is part of RROMPy.
#
# RROMPy is free software: you can redistribute it and/or modify
# it under the terms of the GNU Lesser General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# RROMPy is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public License
# along with RROMPy. If not, see .
#
-from .radial_basis_fitter import (radialBasisFitter, radialGaussian,
- thinPlateSpline, multiQuadric)
-
-__all__ = [
- 'radialBasisFitter',
- 'radialGaussian',
- 'thinPlateSpline',
- 'multiQuadric'
- ]
+import numpy as np
+from rrompy.utilities.base.types import Np1D, List, radialFun
+from rrompy.utilities.exception_manager import RROMPyException
+__all__ = ['polyder']
+def polyder(c:radialFun, basis:str, m : List[int] = None,
+ scl : Np1D = None) -> radialFun:
+ if m is not None and np.sum(m) > 0:
+ raise RROMPyException(("Cannot take derivatives of radial basis "
+ "function."))
+ return c
diff --git a/rrompy/utilities/poly_fitting/radial_basis/homogeneization.py b/rrompy/utilities/poly_fitting/radial_basis/homogeneization.py
new file mode 100644
index 0000000..c3769d2
--- /dev/null
+++ b/rrompy/utilities/poly_fitting/radial_basis/homogeneization.py
@@ -0,0 +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 .
+#
+
+import numpy as np
+from rrompy.utilities.base.types import Np1D, Np2D, Tuple, List, paramList
+from rrompy.utilities.poly_fitting.polynomial.homogeneization import (
+ homogeneizedpolyvander as hpvP)
+from rrompy.utilities.exception_manager import RROMPyException
+from .vander import rbvander
+
+__all__ = ['homogeneizedpolyvander']
+
+def homogeneizedpolyvander(x:paramList, deg:int, basis:str,
+ derIdxs : List[List[List[int]]] = None,
+ reorder : List[int] = None,
+ directionalWeights : Np1D = None,
+ scl : Np1D = None)\
+ -> Tuple[Np2D, List[List[int]], List[int]]:
+ """
+ Compute radial-basis-inclusive homogeneized Hermite-Vandermonde matrix with
+ specified derivative directions.
+ """
+ if derIdxs is not None and np.sum(np.sum(derIdxs)) > 0:
+ raise RROMPyException(("Cannot take derivatives of radial basis "
+ "function."))
+ basisp, basisr = basis.split("_")
+ VanR = rbvander(x, basisr, reorder = reorder,
+ directionalWeights = directionalWeights)
+ VanP, derIdxs, ordIdxs = hpvP(x, deg, basisp, derIdxs = derIdxs,
+ reorder = reorder, scl = scl)
+ ordIdxsEff = np.concatenate((np.arange(len(VanR)), ordIdxs + len(VanR)))
+ return (np.block([[VanR, VanP],
+ [VanP.T.conj(), np.zeros(tuple([VanP.shape[1]] * 2))]]),
+ derIdxs, ordIdxsEff)
diff --git a/tests/test_3_reduction_methods_1D/matrix_fft.py b/rrompy/utilities/poly_fitting/radial_basis/kernel.py
similarity index 55%
copy from tests/test_3_reduction_methods_1D/matrix_fft.py
copy to rrompy/utilities/poly_fitting/radial_basis/kernel.py
index d5ea4c0..d0e1db2 100644
--- a/tests/test_3_reduction_methods_1D/matrix_fft.py
+++ b/rrompy/utilities/poly_fitting/radial_basis/kernel.py
@@ -1,35 +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 numpy as np
-from rrompy.hfengines.base import MatrixEngineBase as MEB
+from rrompy.utilities.base.types import Np1D
+from rrompy.utilities.exception_manager import RROMPyAssert
-def matrixFFT():
- N = 100
- solver = MEB(verbosity = 0)
- np.random.seed(420)
- solver.setSolver("SOLVE")
- fftB = np.fft.fft(np.eye(N)) * N**-.5
- solver.npar = 1
- solver.mu0 = 0.
- solver.nAs = 2
- solver.As = [fftB.dot(np.multiply(np.arange(1, 1 + N), fftB.conj()).T),
- - np.eye(N)]
- solver.nbs = 1
- solver.bs = [np.random.randn(N) + 1.j * np.random.randn(N)]
- return solver
+__all__ = ['radialGaussian', 'thinPlateSpline', 'multiQuadric']
+
+def radialGaussian(r2:Np1D, der : int = 0) -> Np1D:
+ RROMPyAssert(der, 0, "Radial basis derivative")
+ return np.exp(- .5 * r2)
+
+def thinPlateSpline(r2:Np1D, der : int = 0) -> Np1D:
+ RROMPyAssert(der, 0, "Radial basis derivative")
+ return .5 * r2 * np.log(np.finfo(float).eps + r2)
+
+def multiQuadric(r2:Np1D, der : int = 0) -> Np1D:
+ RROMPyAssert(der, 0, "Radial basis derivative")
+ return np.power(r2 + 1., -.5)
diff --git a/rrompy/utilities/poly_fitting/radial_basis/radial_basis_fitter.py b/rrompy/utilities/poly_fitting/radial_basis/radial_basis_fitter.py
deleted file mode 100644
index dec76f5..0000000
--- a/rrompy/utilities/poly_fitting/radial_basis/radial_basis_fitter.py
+++ /dev/null
@@ -1,229 +0,0 @@
-# Copyright (C) 2018 by the RROMPy authors
-#
-# This file is part of RROMPy.
-#
-# RROMPy is free software: you can redistribute it and/or modify
-# it under the terms of the GNU Lesser General Public License as published by
-# the Free Software Foundation, either version 3 of the License, or
-# (at your option) any later version.
-#
-# RROMPy is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-# GNU Lesser General Public License for more details.
-#
-# You should have received a copy of the GNU Lesser General Public License
-# along with RROMPy. If not, see .
-#
-
-import numpy as np
-from rrompy.utilities.base.types import Np1D, Np2D, List, ListAny, paramList
-from rrompy.solver import Np2DLikeEye, normEngine
-from rrompy.parameter import checkParameterList
-from rrompy.utilities.exception_manager import RROMPyException, RROMPyAssert
-
-__all__ = ['radialBasisFitter', 'radialGaussian', 'thinPlateSpline',
- 'multiQuadric']
-
-def radialGaussian(r2):
- return np.exp(- r2)
-
-def thinPlateSpline(r2):
- return .5 * r2 * np.log(np.finfo(float).eps + r2)
-
-def multiQuadric(r2):
- return np.power(r2 + 1, .5)
-
-class radialBasisFitter:
- """HERE"""
-
- allowedModes = ["PARAMETERS", "VALUES"]
-
- def __init__(self, mus:paramList, basisFun : callable = radialGaussian,
- massMatrix : Np2D = None, mode : str = "PARAMETERS",
- scl : float = 1.):
- self.mus = mus
- self.basisFun = basisFun
- if massMatrix is None: massMatrix = normEngine(Np2DLikeEye())
- self.massMatrix = massMatrix
- self.mode = mode
- self.scl = scl
-
- @property
- def d(self):
- """Number of parameters."""
- return self.mus.shape[1]
-
- @property
- def n(self):
- """Number of parameter points."""
- return len(self.mus)
-
- @property
- def basisFun(self):
- """Value of basisFun. Its assignment resets all."""
- return self._basisFun
- @basisFun.setter
- def basisFun(self, basisFun):
- self.reset()
- self._basisFun = basisFun
-
- @property
- def mus(self):
- """Value of mus. Its assignment resets all."""
- return self._mus
- @mus.setter
- def mus(self, mus):
- mus, _ = checkParameterList(mus)
- self.reset()
- self._mus = mus
-
- @property
- def massMatrix(self):
- """Value of massMatrix. Its assignment resets all."""
- return self._massMatrix
- @massMatrix.setter
- def massMatrix(self, massMatrix):
- self.reset()
- self._massMatrix = massMatrix
-
- @property
- def mode(self):
- """Value of mode. Its assignment resets all."""
- return self._mode
- @mode.setter
- def mode(self, mode):
- self.reset()
- self._mode = mode.upper()
-
- @property
- def scl(self):
- """Value of scl. Its assignment resets all."""
- return self._scl
- @scl.setter
- def scl(self, scl):
- self.reset()
- self._scl = scl
-
- def reset(self):
- self.vander = None
- self.offDiag = None
- self.offDiagT = None
- self.matrixInv = None
- self.probeParameters = None
- self.probeValues = None
-
- def buildMatrixBlocks(self):
- if self.offDiag is None:
- self.reset()
- self.offDiagT = np.array([[1] + list(x[0]) for x in self.mus])
- self.offDiag = self.offDiagT.T
- muDiff = np.empty((self.d, self.n * (self.n - 1) // 2 + 1),
- dtype = self.mus.dtype)
- muDiff[:, 0] = 0.
- idxInv = np.zeros(self.n ** 2, dtype = int)
- for j in range(self.n):
- idx = j * (self.n - 1) - j * (j + 1) // 2
- for i in range(j + 1, self.n):
- muDiff[:, idx + i] = (self.offDiag[1:, j]
- - self.offDiag[1:, i])
- idxInv[j * self.n + i] = idx + i
- idxInv[i * self.n + j] = idx + i
- self.vander = self.basisFun(np.power(self.scl *
- self.massMatrix.norm(muDiff), 2.))[idxInv]
- self.vander = self.vander.reshape((self.n, -1))
- self.vanderProj = self.offDiag.dot(self.vander.dot(self.offDiag.T))
-
- def buildMatrixInvBlocks(self):
- if self.matrixInv is None:
- self.buildMatrixBlocks()
- vanderInv = np.linalg.inv(self.vander)
- vanderProjInv = np.linalg.solve(self.vanderProj,
- self.offDiag.dot(vanderInv))
- self.matrixInv = np.empty((self.n + self.d + 1, self.n),
- dtype = vanderProjInv.dtype)
- self.matrixInv[self.n :, :] = vanderProjInv
- self.matrixInv[: self.n, :] = vanderInv - vanderInv.dot(
- self.offDiagT.dot(vanderProjInv))
-
- def setupProbeParameters(self, mu : paramList = []) -> List[Np1D]:
- mu, _ = checkParameterList(mu, self.d)
- self.buildMatrixInvBlocks()
- self.probeParameters = [None] * len(mu)
- for j, m in enumerate(mu):
- probe = np.ones(self.n + self.d + 1, dtype = m.dtype)
- probe[self.n + 1 :] = m.data # flatten?
- mDiff = (probe[self.n + 1:] - self.offDiagT[:, 1:]).T
- probe[: self.n] = self.basisFun(np.power(self.scl *
- self.massMatrix.norm(mDiff), 2.))
- self.probeParameters[j] = probe.dot(self.matrixInv)
-
- def setupProbeValues(self, vals:ListAny) -> ListAny:
- RROMPyAssert(len(vals), self.n, "Number of samples")
- self.buildMatrixInvBlocks()
- if isinstance(vals, (np.ndarray,)):
- self.probeValues = np.tensordot(self.matrixInv, vals,
- axes = ([-1], [0]))
- else:
- self.probeValues = [None] * (self.n + self.d + 1)
- for j in range(self.n + self.d + 1):
- self.probeValues[j] = self.matrixInv[j, 0] * vals[0]
- for i in range(1, self.n):
- self.probeValues[j] += self.matrixInv[j, i] * vals[i]
-
- def interpolateParameters(self, vals:ListAny) -> ListAny:
- if self.probeParameters is None:
- raise RROMPyException(("Parameter probe must be set up before "
- "interpolation."))
- RROMPyAssert(len(vals), self.n, "Number of samples")
- interpolated = [None] * len(self.probeParameters)
- if isinstance(vals, (np.ndarray,)):
- if vals.ndim == 1:
- for j, pUp in enumerate(self.probeParameters):
- interpolated[j] = pUp.dot(vals)
- else:
- for j, pUp in enumerate(self.probeParameters):
- interpolated[j] = np.tensordot(pUp, vals,
- axes = ([0], [0]))
- else:
- for j, pUp in enumerate(self.probeParameters):
- interpolated[j] = self.probeParameters[j][0] * vals[0]
- for i in range(1, self.n):
- interpolated[j] += self.probeParameters[j][i] * vals[i]
- return interpolated
-
- def interpolateValues(self, mu : paramList = []) -> ListAny:
- if self.probeValues is None:
- raise RROMPyException(("Value probe must be set up before "
- "interpolation."))
- mu, _ = checkParameterList(mu, self.d)
- probeLs = [None] * len(mu)
- for j, m in enumerate(mu):
- probeLs[j] = np.ones(self.n + self.d + 1, dtype = m.dtype)
- probeLs[j][self.n + 1 :] = m.data # flatten?
- mDiff = (probeLs[j][self.n + 1:] - self.offDiagT[:, 1:]).T
- probeLs[j][: self.n] = self.basisFun(np.power(self.scl *
- self.massMatrix.norm(mDiff), 2.))
- interpolated = [None] * len(mu)
- if isinstance(self.probeValues, (np.ndarray,)):
- if self.probeValues.ndim == 1:
- for j, pL in enumerate(probeLs):
- interpolated[j] = pL.dot(self.probeValues)
- else:
- for j, pL in enumerate(probeLs):
- interpolated[j] = np.tensordot(pL, self.probeValues,
- axes = ([-1], [0]))
- else:
- for j, pL in enumerate(probeLs):
- interpolated[j] = pL[0] * self.probeValues[0]
- for i in range(1, self.n + self.d + 1):
- interpolated[j] += pL[i] * self.probeValues[i]
- return interpolated
-
- def interpolate(self, x) -> ListAny:
- if self.mode == "PARAMETERS":
- return self.interpolateParameters(x)
- if self.mode == "VALUES":
- return self.interpolateValues(x)
- raise RROMPyException("Not implemented")
-
diff --git a/rrompy/utilities/poly_fitting/radial_basis/val.py b/rrompy/utilities/poly_fitting/radial_basis/val.py
new file mode 100644
index 0000000..51f4c5b
--- /dev/null
+++ b/rrompy/utilities/poly_fitting/radial_basis/val.py
@@ -0,0 +1,54 @@
+# 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.utilities.base.types import Np1D, Np2D, List, paramList, radialFun
+from rrompy.parameter import checkParameterList
+from rrompy.utilities.exception_manager import RROMPyException
+from rrompy.utilities.poly_fitting.polynomial import polyval as pvP
+from .der import polyder
+from .kernel import radialGaussian, thinPlateSpline, multiQuadric
+
+__all__ = ['polyval']
+
+def polyval(x:paramList, c:radialFun, basis:str, m : List[int] = None,
+ scl : Np1D = None) -> Np2D:
+ cFun = polyder(c, basis, m = m, scl = scl)
+ x, _ = checkParameterList(x)
+ basisp, basisr = basis.split("_")
+ c = pvP(x, cFun.globalCoeffs, basisp, m, scl)
+ try:
+ radialvalbase = {"GAUSSIAN" : radialGaussian,
+ "THINPLATE" : thinPlateSpline,
+ "MULTIQUADRIC" : multiQuadric
+ }[basisr.upper()]
+ except:
+ raise RROMPyException("Radial basis not recognized.")
+ csh = copy(c.shape)
+ if len(csh) == 1: c = c.reshape(1, -1)
+ for j in range(len(x)):
+ muDiff = (cFun.supportPoints.data - x[j]) * cFun.directionalWeights
+ r2j = np.sum(np.abs(muDiff) ** 2., axis = 1).reshape(1, -1)
+ val = radialvalbase(r2j).dot(cFun.localCoeffs)
+ try:
+ c[..., j] += val
+ except:
+ c[..., j] += val.flatten()
+ if len(csh) == 1: c = c.flatten()
+ return c
diff --git a/rrompy/utilities/poly_fitting/radial_basis/vander.py b/rrompy/utilities/poly_fitting/radial_basis/vander.py
new file mode 100644
index 0000000..5e9d6d0
--- /dev/null
+++ b/rrompy/utilities/poly_fitting/radial_basis/vander.py
@@ -0,0 +1,79 @@
+# 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.poly_fitting.polynomial.vander import polyvander as pvP
+from rrompy.utilities.base.types import Np1D, Np2D, List, paramList
+from rrompy.parameter import checkParameterList
+from rrompy.utilities.exception_manager import RROMPyException, RROMPyAssert
+from .kernel import radialGaussian, thinPlateSpline, multiQuadric
+
+__all__ = ['rbvander', 'polyvander']
+
+def rbvander(x:paramList, basis:str, reorder : List[int] = None,
+ directionalWeights : Np1D = None) -> Np2D:
+ """Compute radial-basis-Vandermonde matrix."""
+ x, _ = checkParameterList(x)
+ x_un, idx_un = x.unique(return_inverse = True)
+ nx = len(x)
+ if len(x_un) < nx:
+ raise RROMPyException("Sample points must be distinct.")
+ del x_un
+ x = x.data
+ if directionalWeights is None:
+ directionalWeights = np.ones(x.shape[1])
+ RROMPyAssert(len(directionalWeights), x.shape[1],
+ "Number of directional weights")
+ try:
+ radialkernel = {"GAUSSIAN" : radialGaussian,
+ "THINPLATE" : thinPlateSpline,
+ "MULTIQUADRIC" : multiQuadric
+ }[basis.upper()]
+ except:
+ raise RROMPyException("Radial basis not recognized.")
+ r2 = np.zeros((nx * (nx - 1) // 2 + 1), dtype = float)
+ idxInv = np.zeros(nx ** 2, dtype = int)
+ if reorder is not None: x = x[reorder]
+ for j in range(nx):
+ idx = j * (nx - 1) - j * (j + 1) // 2
+ II = np.arange(j + 1, nx)
+ r2[idx + II] = np.sum(np.abs((x[II] - x[j])
+ * directionalWeights) ** 2., axis = 1)
+ idxInv[j * nx + II] = idx + II
+ idxInv[II * nx + j] = idx + II
+ Van = radialkernel(r2)[idxInv].reshape((nx, nx))
+ return Van
+
+def polyvander(x:paramList, degs:List[int], basis:str,
+ derIdxs : List[List[List[int]]] = None,
+ reorder : List[int] = None, directionalWeights : Np1D = None,
+ scl : Np1D = None) -> Np2D:
+ """
+ Compute radial-basis-inclusive Hermite-Vandermonde matrix with specified
+ derivative directions.
+ """
+ if derIdxs is not None and np.sum(np.sum(derIdxs)) > 0:
+ raise RROMPyException(("Cannot take derivatives of radial basis "
+ "function."))
+ basisp, basisr = basis.split("_")
+ VanR = rbvander(x, basisr, reorder = reorder,
+ directionalWeights = directionalWeights)
+ VanP = pvP(x, degs, basisp, derIdxs = derIdxs, reorder = reorder,
+ scl = scl)
+ return np.block([[VanR, VanP],
+ [VanP.T.conj(), np.zeros(tuple([VanP.shape[1]] * 2))]])
diff --git a/setup.cfg b/setup.cfg
index a25ec20..a95dc41 100644
--- a/setup.cfg
+++ b/setup.cfg
@@ -1,10 +1,10 @@
[aliases]
test=pytest
[tool:pytest]
-addopts = --verbose --tb=short
+addopts = --tb=short
python_files = tests/*/*.py
filterwarnings =
error
ignore::DeprecationWarning
ignore::FutureWarning
diff --git a/setup.py b/setup.py
index 248956a..32b1d45 100644
--- a/setup.py
+++ b/setup.py
@@ -1,52 +1,52 @@
# Copyright (C) 2015-2018 by the RBniCS authors
#
# This file is part of RBniCS.
#
# RBniCS 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.
#
# RBniCS 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 RBniCS. If not, see .
#
import os
from setuptools import find_packages, setup
rrompy_directory = os.path.abspath(os.path.dirname(os.path.realpath(__file__)))
#rrompy_directory = os.path.join(rrompy_directory, 'rrompy')
setup(name="RROMPy",
description="Rational reduced order modelling in Python",
long_description="Rational reduced order modelling in Python",
author="Davide Pradovera",
author_email="davide.pradovera@epfl.ch",
- version="1.5",
+ version="1.6",
license="GNU Library or Lesser General Public License (LGPL)",
classifiers=[
"Development Status :: 1 - Planning"
"Intended Audience :: Developers",
"Intended Audience :: Science/Research",
"Programming Language :: Python :: 3",
"Programming Language :: Python :: 3.4",
"Programming Language :: Python :: 3.5",
"Programming Language :: Python :: 3.6",
"License :: OSI Approved :: GNU Library or Lesser General Public License (LGPL)",
"Topic :: Scientific/Engineering :: Mathematics",
"Topic :: Software Development :: Libraries :: Python Modules",
],
packages=find_packages(rrompy_directory),
setup_requires=[
"pytest-runner"
],
tests_require=[
"pytest"
],
zip_safe=False
)
diff --git a/tests/hfengines/fracture.py b/tests/hfengines/fracture.py
new file mode 100644
index 0000000..a7624a2
--- /dev/null
+++ b/tests/hfengines/fracture.py
@@ -0,0 +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 pytest
+import numpy as np
+import ufl
+import fenics as fen
+from rrompy.hfengines.linear_problem.bidimensional import \
+ MembraneFractureEngine as MFE
+from rrompy.hfengines.linear_problem import MembraneFractureEngineNoDomain \
+ as MFEND
+from rrompy.solver.fenics import affine_warping
+
+@pytest.mark.xfail(reason = "no graphical interface")
+def test_fracture():
+ mu0 = [45. ** .5, .6]
+ solver2 = MFE(mu0 = mu0, H = 1., L = .75, delta = .05, n = 20,
+ verbosity = 0)
+ uh2 = solver2.solve(mu0)[0]
+
+ solver1 = MFEND(mu0 = mu0, H = 1., L = .75, delta = .05, n = 20,
+ verbosity = 0)
+ uh1 = solver1.solve(mu0[0])[0]
+
+ L = mu0[1]
+ y = fen.SpatialCoordinate(solver1.V.mesh())[1]
+ warp1, warpI1 = affine_warping(solver1.V.mesh(),
+ np.array([[1, 0], [0, 2. * L]]))
+ warp2, warpI2 = affine_warping(solver1.V.mesh(),
+ np.array([[1, 0], [0, 2. - 2. * L]]))
+ warp = ufl.conditional(ufl.ge(y, 0.), warp1, warp2)
+ warpI = ufl.conditional(ufl.ge(y, 0.), warpI1, warpI2)
+
+ solver1.plotmesh([warp, warpI], show = False, figsize = (7, 7))
+ solver1.plot(uh1, [warp, warpI], what = 'REAL', show = False)
+ from matplotlib import pyplot as plt
+ plt.close('all')
+
+ assert np.isclose(
+ solver1.norm(solver1.residual(uh1, mu0[0]), dual = True)[0],
+ solver2.norm(solver2.residual(uh2, mu0), dual = True)[0],
+ atol = 1e-5)
+ assert np.isclose(solver1.norm(uh1 - uh2), 0., atol = 1e-6)
+
diff --git a/tests/test_2_hfengines/helmholtz_elasticity.py b/tests/hfengines/helmholtz_elasticity.py
similarity index 80%
rename from tests/test_2_hfengines/helmholtz_elasticity.py
rename to tests/hfengines/helmholtz_elasticity.py
index 85a2b14..6d3279c 100644
--- a/tests/test_2_hfengines/helmholtz_elasticity.py
+++ b/tests/hfengines/helmholtz_elasticity.py
@@ -1,66 +1,69 @@
# 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.hfengines.vector_linear_problem import (
LinearElasticityHelmholtzProblemEngine,
LinearElasticityHelmholtzProblemEngineDamped,
LinearElasticityHelmholtzArchwayFrequency)
from rod_3d import rod3Dsolver
def test_helmholtz_elastic_arch():
solver = LinearElasticityHelmholtzArchwayFrequency(kappa = 10, n = 30,
rho_ = 1e4, T = 1e5, lambda_ = 4e6, mu_ = 7e5,
R = 2e1, r = 1.5e1, verbosity = 0)
mu = 10
uh = solver.solve(mu)[0]
assert np.isclose(solver.norm(uh), 3188.9960782143194, rtol = 1e-5)
- assert np.isclose(solver.norm(solver.residual(uh, mu)[0]), 3.025504915e-05,
- rtol = 1e-1)
+ assert np.isclose(solver.norm(solver.residual(uh, mu)[0], dual = True),
+ 3.6771479e-12, rtol = 1e-1)
def test_helmholtz_elastic_rod():
solverBase = rod3Dsolver()
solver = LinearElasticityHelmholtzProblemEngine()
solver.V = solverBase.V
solver.lambda_ = solverBase.lambda_
solver.mu_ = solverBase.mu_
solver.forcingTerm = solverBase.forcingTerm
solver.DirichletBoundary = solverBase.DirichletBoundary
solver.NeumannBoundary = solverBase.NeumannBoundary
mu = 10
uh = solver.solve(mu)[0]
assert np.isclose(solver.norm(uh), 0.17836028624665373, rtol = 1e-5)
- assert np.isclose(solver.norm(solver.residual(uh, 10)[0]), 7.030048088e-08,
- rtol = 1e-1)
+ assert np.isclose(solver.norm(solver.residual(uh, mu)[0], dual = True),
+ 8.070977e-07, rtol = 1e-1)
+ assert np.isclose(solver.norm(solver.residual(uh, mu)[0], dual = True),
+ solver.norm(solver.residual(uh, mu, duality = False)[0],
+ dual = True, duality = False), rtol = 1e-1)
def test_helmholtz_elastic_rod_damped():
solverBase = rod3Dsolver()
solver = LinearElasticityHelmholtzProblemEngineDamped()
solver.V = solverBase.V
solver.lambda_ = solverBase.lambda_
solver.mu_ = solverBase.mu_
solver.forcingTerm = solverBase.forcingTerm
solver.DirichletBoundary = solverBase.DirichletBoundary
solver.NeumannBoundary = solverBase.NeumannBoundary
solver.eta = 10
mu = 10
uh = solver.solve(mu)[0]
assert np.isclose(solver.norm(uh), 0.17646530119044376, rtol = 1e-2)
- assert np.isclose(solver.norm(solver.residual(uh, 10)[0]), 6.802444e-08,
- rtol = 1e-1)
+ assert np.isclose(solver.norm(solver.residual(uh, 10)[0], dual = True),
+ 6.7057338e-07, rtol = 1e-1)
diff --git a/tests/test_2_hfengines/helmholtz_external.py b/tests/hfengines/helmholtz_external.py
similarity index 81%
rename from tests/test_2_hfengines/helmholtz_external.py
rename to tests/hfengines/helmholtz_external.py
index 009ab89..3e74d16 100644
--- a/tests/test_2_hfengines/helmholtz_external.py
+++ b/tests/hfengines/helmholtz_external.py
@@ -1,67 +1,69 @@
# 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 pytest
import numpy as np
from rrompy.hfengines.linear_problem import (
HelmholtzCavityScatteringProblemEngine, HelmholtzBoxScatteringProblemEngine)
def test_helmholtz_square_scattering():
solver = HelmholtzCavityScatteringProblemEngine(kappa = 4, gamma = 2.,
n = 20, verbosity = 0)
mu = 5
uh = solver.solve(mu)[0]
assert np.isclose(solver.norm(uh), 19.9362, rtol = 1e-2)
- assert np.isclose(solver.norm(solver.residual(uh, mu)[0]), 4.25056407e-13,
- rtol = 1e-1)
+ assert np.isclose(solver.norm(solver.residual(uh, mu)[0], dual = True),
+ 4.25056407e-13, rtol = 1e-1)
+ assert np.isclose(solver.norm(solver.residual(uh, mu)[0], dual = True),
+ solver.norm(solver.residual(uh, mu, duality = False)[0],
+ dual = True, duality = False), rtol = 1e-1)
def test_helmholtz_scattering_copy(capsys):
solver1 = HelmholtzCavityScatteringProblemEngine(kappa = 4, gamma = 2.,
n = 20, verbosity = 0)
mu = 5
uh1 = solver1.solve(mu)[0]
solver2 = HelmholtzCavityScatteringProblemEngine(kappa = 4, gamma = 2.,
n = 20, verbosity = 100)
assert solver1.As[0] is not None and solver1.bs[0] is not None
assert solver2.As[0] is None and solver2.bs[0] is None
solver2.setAs(solver1.As)
solver2.setbs(solver1.bs)
uh2 = solver2.solve(mu)[0]
assert np.allclose(uh1, uh2, rtol = 1e-8)
out, err = capsys.readouterr()
assert ("Assembling operator term" not in out
and "Assembling forcing term" not in out)
assert len(err) == 0
-@pytest.mark.xfail(raises = RuntimeError('Invalid DISPLAY variable'),
- reason = "no graphical interface")
+@pytest.mark.xfail(reason = "no graphical interface")
def test_helmholtz_box_scattering():
solver = HelmholtzBoxScatteringProblemEngine(R = 2, kappa = 10.,
theta = np.pi * 30 / 180, n = 20, verbosity = 0)
mu = 15
uh = solver.solve(mu)[0]
solver.plotmesh(show = False, figsize = (7, 7))
assert np.isclose(solver.norm(uh), 62.113, rtol = 1e-2)
- assert np.isclose(solver.norm(solver.residual(uh, mu)[0]), 9.62989935e-13,
- rtol = 1e-1)
+ assert np.isclose(solver.norm(solver.residual(uh, mu)[0], dual = True),
+ 9.62989935e-13, rtol = 1e-1)
from matplotlib import pyplot as plt
plt.close('all')
diff --git a/tests/test_2_hfengines/helmholtz_internal.py b/tests/hfengines/helmholtz_internal.py
similarity index 79%
rename from tests/test_2_hfengines/helmholtz_internal.py
rename to tests/hfengines/helmholtz_internal.py
index 00724bb..920dfcb 100644
--- a/tests/test_2_hfengines/helmholtz_internal.py
+++ b/tests/hfengines/helmholtz_internal.py
@@ -1,98 +1,99 @@
# 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 pytest
import os, shutil
import numpy as np
from rrompy.hfengines.linear_problem import (
HelmholtzSquareBubbleDomainProblemEngine, HelmholtzSquareBubbleProblemEngine,
HelmholtzSquareTransmissionProblemEngine)
def test_helmholtz_square_io():
- solver = HelmholtzSquareBubbleProblemEngine(kappa = 4, theta = 1., n = 50,
+ solver = HelmholtzSquareBubbleProblemEngine(kappa = 4, theta = 1., n = 20,
verbosity = 0)
mu = 5
uh = solver.solve(mu)[0]
- assert np.isclose(solver.norm(uh), 913.396, rtol = 1e-3)
- assert np.isclose(solver.norm(solver.residual(uh, mu)[0]), 1.19934e-11,
- rtol = 1e-1)
+ assert np.isclose(solver.norm(uh), 145.0115, rtol = 1e-3)
+ assert np.isclose(solver.norm(solver.residual(uh, mu)[0], dual = True),
+ 1.19934e-11, rtol = 1e-1)
+ assert np.isclose(solver.norm(solver.residual(uh, mu)[0], dual = True),
+ solver.norm(solver.residual(uh, mu, duality = False)[0],
+ dual = True, duality = False), rtol = 1e-1)
if not os.path.isdir("./.pytest_cache"): os.mkdir("./.pytest_cache")
filesOut = [x for x in os.listdir("./.pytest_cache") if
(x[-4:] == ".pvd" and x[:9] == "outSquare")]
filesOutData = [x for x in os.listdir("./.pytest_cache") if
(x[-4:] == ".vtu" and x[:9] == "outSquare")]
for fileOut in filesOut:
os.remove("./.pytest_cache/" + fileOut)
for fileOut in filesOutData:
os.remove("./.pytest_cache/" + fileOut)
solver.outParaview(uh, what = ["MESH", "ABS"],
filename = ".pytest_cache/outSquare",
forceNewFile = False)
filesOut = [x for x in os.listdir("./.pytest_cache") if
(x[-4:] == ".pvd" and x[:9] == "outSquare")]
filesOutData = [x for x in os.listdir("./.pytest_cache") if
(x[-4:] == ".vtu" and x[:9] == "outSquare")]
assert len(filesOut) == 1
assert len(filesOutData) == 1
os.remove("./.pytest_cache/" + filesOut[0])
os.remove("./.pytest_cache/" + filesOutData[0])
def test_helmholtz_transmission_io():
solver = HelmholtzSquareTransmissionProblemEngine(nT = 1, nB = 2,
- theta = np.pi * 40 / 180, kappa = 4., n = 50, verbosity = 0)
+ theta = np.pi * 40 / 180, kappa = 4., n = 20, verbosity = 0)
mu = 5.
uh = solver.solve(mu)[0]
- assert np.isclose(solver.norm(uh), 43.9268, rtol = 1e-2)
- assert np.isclose(solver.norm(solver.residual(uh, mu)[0]), 3.7288565e-12,
- rtol = 1e-1)
+ assert np.isclose(solver.norm(uh), 138.6609, rtol = 1e-2)
+ assert np.isclose(solver.norm(solver.residual(uh, mu)[0], dual = True),
+ 3.7288565e-12, rtol = 1e-1)
if not os.path.isdir("./.pytest_cache"): os.mkdir("./.pytest_cache")
solver.outParaviewTimeDomain(uh, omega = mu,
filename = ".pytest_cache/outTrans",
forceNewFile = False, folder = True)
filesOut = [x for x in os.listdir("./.pytest_cache/outTrans") if
(x[-4:] == ".pvd" and x[:8] == "outTrans")]
filesOutData = [x for x in os.listdir("./.pytest_cache/outTrans") if
(x[-4:] == ".vtu" and x[:8] == "outTrans")]
assert len(filesOut) == 1
assert len(filesOutData) == 20
shutil.rmtree("./.pytest_cache/outTrans")
-@pytest.mark.xfail(raises = RuntimeError('Invalid DISPLAY variable'),
- reason = "no graphical interface")
+@pytest.mark.xfail(reason = "no graphical interface")
def test_helmholtz_domain_io():
solver = HelmholtzSquareBubbleDomainProblemEngine(kappa = 4, theta = 1.,
n = 10, mu0 = 1.5, verbosity = 0)
mu = 1.5
uh = solver.solve(mu)[0]
if not os.path.isdir("./.pytest_cache"): os.mkdir("./.pytest_cache")
solver.plot(uh, save = "./.pytest_cache/outDomain", show = False)
filesOut = [x for x in os.listdir("./.pytest_cache") if
(x[-4:] == ".eps" and x[:9] == "outDomain")]
assert len(filesOut) == 1
os.remove("./.pytest_cache/" + filesOut[0])
- assert np.isclose(solver.norm(uh), 8.9947, rtol = 1e-2)
- assert np.isclose(solver.norm(solver.residual(uh, mu)[0]), 6.14454989e-13,
- rtol = 1e-1)
-
+ assert np.isclose(solver.norm(uh), 10.07843, rtol = 1e-2)
+ assert np.isclose(solver.norm(solver.residual(uh, mu)[0], dual = True),
+ 6.14454989e-13, rtol = 1e-1)
diff --git a/tests/test_2_hfengines/laplace.py b/tests/hfengines/laplace.py
similarity index 73%
rename from tests/test_2_hfengines/laplace.py
rename to tests/hfengines/laplace.py
index 8bd21f7..a76dbc1 100644
--- a/tests/test_2_hfengines/laplace.py
+++ b/tests/hfengines/laplace.py
@@ -1,39 +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 .
#
import numpy as np
from rrompy.hfengines.linear_problem import LaplaceDiskGaussian
from rrompy.hfengines.linear_problem.bidimensional import LaplaceDiskGaussian2
def test_laplace_disk():
solver = LaplaceDiskGaussian(n = 20, verbosity = 0)
mu = 1.5
solver.setSolver("BICG", {"tol" : 1e-15})
uh = solver.solve(mu)[0]
assert np.isclose(solver.norm(uh), 1.053403077447029, rtol = 1e-2)
- assert np.isclose(solver.norm(solver.residual(uh, mu)[0]), 5.27345e-13,
- rtol = 1e-1)
+ assert np.isclose(solver.norm(solver.residual(uh, mu)[0], dual = True),
+ 3.4591353e-08, rtol = 1e-1)
+ assert np.isclose(solver.norm(solver.residual(uh, mu)[0], dual = True),
+ solver.norm(solver.residual(uh, mu, duality = False)[0],
+ dual = True, duality = False), rtol = 1e-1)
def test_laplace_disk_2():
solver = LaplaceDiskGaussian2(n = 20, verbosity = 0)
mu = [[0., 1.5]]
mu = [0., 1.5]
uh = solver.solve(mu)[0]
assert np.isclose(solver.norm(uh), 1.0534030774205372, rtol = 1e-2)
- assert np.isclose(solver.norm(solver.residual(uh, mu)[0]), 5.27345e-13,
- rtol = 1e-1)
+ assert np.isclose(solver.norm(solver.residual(uh, mu)[0], dual = True),
+ 2.40043363e-08, rtol = 1e-1)
diff --git a/tests/test_2_hfengines/linear_elasticity.py b/tests/hfengines/linear_elasticity.py
similarity index 70%
rename from tests/test_2_hfengines/linear_elasticity.py
rename to tests/hfengines/linear_elasticity.py
index 0ef3355..309716a 100644
--- a/tests/test_2_hfengines/linear_elasticity.py
+++ b/tests/hfengines/linear_elasticity.py
@@ -1,38 +1,41 @@
# Copyright (C) 2018 by the RROMPy authors
#
# This file is part of RROMPy.
#
# RROMPy is free software: you can redistribute it and/or modify
# it under the terms of the GNU Lesser General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# RROMPy is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public License
# along with RROMPy. If not, see .
#
import numpy as np
from rrompy.hfengines.vector_linear_problem import (
LinearElasticityBeamPoissonRatio)
from rod_3d import rod3Dsolver
def test_elastic_beam():
solver = LinearElasticityBeamPoissonRatio(n = 10, rho_ = 1e3, g = 3,
E = 1e6, nu0 = .45, length = 5, verbosity = 0)
mu = .45
uh = solver.solve(mu)[0]
- assert np.isclose(solver.norm(uh), 7.637337113310191e-08, rtol = 1e-1)
- assert np.isclose(solver.norm(solver.residual(uh, mu)[0]), 8.4545952e-13,
- rtol = 1e-1)
+ assert np.isclose(solver.norm(uh), 5.866888537228743e-08, rtol = 1e-1)
+ assert np.isclose(solver.norm(solver.residual(uh, mu)[0], dual = True),
+ 8.4545952e-13, rtol = 1e-1)
+ assert np.isclose(solver.norm(solver.residual(uh, mu)[0], dual = True),
+ solver.norm(solver.residual(uh, mu, duality = False)[0],
+ dual = True, duality = False), rtol = 1e-1)
def test_elastic_rod():
solver = rod3Dsolver()
uh = solver.solve()[0]
assert np.isclose(solver.norm(uh), 0.15563476339534466, rtol = 1e-5)
- assert np.isclose(solver.norm(solver.residual(uh)[0]), 5.708389944e-08,
- rtol = 1e-1)
+ assert np.isclose(solver.norm(solver.residual(uh)[0], dual = True),
+ 1.2210129e-07, rtol = 1e-1)
diff --git a/tests/test_2_hfengines/matrix.py b/tests/hfengines/matrix.py
similarity index 77%
rename from tests/test_2_hfengines/matrix.py
rename to tests/hfengines/matrix.py
index db2f54a..d0959e5 100644
--- a/tests/test_2_hfengines/matrix.py
+++ b/tests/hfengines/matrix.py
@@ -1,62 +1,65 @@
# 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 sp
from rrompy.hfengines.base import MatrixEngineBase as MEB
def test_deterministic():
N = 100
verb = 0
solver = MEB(verbosity = verb)
solver.npar = 1
solver.nAs = 2
mu = 10. + .5j
solver.As = [sp.spdiags([np.arange(1, 1 + N)], [0], N, N),
- sp.eye(N)]
solver.nbs = 1
solver.bs = [np.exp(1.j * np.linspace(0, -np.pi, N))]
uh = solver.solve(mu)[0]
- assert np.isclose(np.linalg.norm(solver.residual(uh, mu)[0]), 1.088e-15,
- rtol = 1e-1)
+ assert np.isclose(solver.norm(solver.residual(uh, mu)[0], dual = True),
+ 1.088e-15, rtol = 1e-1)
+ assert np.isclose(solver.norm(solver.residual(uh, mu)[0], dual = True),
+ solver.norm(solver.residual(uh, mu, duality = False)[0],
+ dual = True, duality = False), rtol = 1e-1)
def test_random():
N = 100
verb = 0
solver = MEB(verbosity = verb)
solver.npar = 1
solver.nAs = 2
mu = 1. + .5j
np.random.seed(420)
solver.setSolver("SOLVE")
fftB = np.fft.fft(np.eye(N)) * N**-.5
solver.As = [fftB.dot(np.multiply(np.arange(1, 1 + N), fftB.conj()).T),
- np.eye(N)]
solver.nbs = 1
solver.bs = [np.random.randn(N) + 1.j * np.random.randn(N)]
uh = solver.solve(mu)[0]
- assert np.isclose(np.linalg.norm(solver.residual(uh, mu)[0]), 7.18658e-14,
- rtol = 1e-1)
+ assert np.isclose(solver.norm(solver.residual(uh, mu)[0], dual = True),
+ 7.18658e-14, rtol = 1e-1)
diff --git a/tests/test_2_hfengines/rod.xml b/tests/hfengines/rod.xml
similarity index 100%
rename from tests/test_2_hfengines/rod.xml
rename to tests/hfengines/rod.xml
diff --git a/tests/test_2_hfengines/rod_3d.py b/tests/hfengines/rod_3d.py
similarity index 96%
rename from tests/test_2_hfengines/rod_3d.py
rename to tests/hfengines/rod_3d.py
index 164c56b..528e0f1 100644
--- a/tests/test_2_hfengines/rod_3d.py
+++ b/tests/hfengines/rod_3d.py
@@ -1,37 +1,37 @@
# 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
from rrompy.hfengines.vector_linear_problem import (
LinearElasticityProblemEngine)
def rod3Dsolver():
- mesh = fen.Mesh("./tests/test_2_hfengines/rod.xml")
+ mesh = fen.Mesh("./tests/hfengines/rod.xml")
rho_ = 1e3
nu = .45
E = 1e6
g = 1e3
solver = LinearElasticityProblemEngine(verbosity = 0)
solver.V = fen.VectorFunctionSpace(mesh, "P", 1)
solver.lambda_ = E * nu / (1. + nu) / (1. - 2 * nu)
solver.mu_ = E / (1. + nu)
solver.forcingTerm = [fen.Constant((0., - rho_ * g / E, 0.)),
fen.Constant((0., 0., 0.))]
solver.DirichletBoundary = lambda x, on_b: on_b and fen.near(x[2], 0.)
solver.NeumannBoundary = "REST"
return solver
diff --git a/tests/test_3_reduction_methods_1D/matrix_fft.py b/tests/reduction_methods_1D/matrix_fft.py
similarity index 100%
rename from tests/test_3_reduction_methods_1D/matrix_fft.py
rename to tests/reduction_methods_1D/matrix_fft.py
diff --git a/tests/test_3_reduction_methods_1D/rational_interpolant_1d.py b/tests/reduction_methods_1D/rational_interpolant_1d.py
similarity index 100%
rename from tests/test_3_reduction_methods_1D/rational_interpolant_1d.py
rename to tests/reduction_methods_1D/rational_interpolant_1d.py
diff --git a/tests/test_3_reduction_methods_1D/rational_interpolant_greedy_1d.py b/tests/reduction_methods_1D/rational_interpolant_greedy_1d.py
similarity index 100%
rename from tests/test_3_reduction_methods_1D/rational_interpolant_greedy_1d.py
rename to tests/reduction_methods_1D/rational_interpolant_greedy_1d.py
diff --git a/tests/test_3_reduction_methods_1D/rational_pade_1d.py b/tests/reduction_methods_1D/rational_pade_1d.py
similarity index 100%
rename from tests/test_3_reduction_methods_1D/rational_pade_1d.py
rename to tests/reduction_methods_1D/rational_pade_1d.py
diff --git a/tests/test_3_reduction_methods_1D/rb_centered_1d.py b/tests/reduction_methods_1D/rb_centered_1d.py
similarity index 100%
rename from tests/test_3_reduction_methods_1D/rb_centered_1d.py
rename to tests/reduction_methods_1D/rb_centered_1d.py
diff --git a/tests/test_3_reduction_methods_1D/rb_distributed_1d.py b/tests/reduction_methods_1D/rb_distributed_1d.py
similarity index 100%
rename from tests/test_3_reduction_methods_1D/rb_distributed_1d.py
rename to tests/reduction_methods_1D/rb_distributed_1d.py
diff --git a/tests/test_3_reduction_methods_1D/rb_distributed_greedy_1d.py b/tests/reduction_methods_1D/rb_distributed_greedy_1d.py
similarity index 100%
rename from tests/test_3_reduction_methods_1D/rb_distributed_greedy_1d.py
rename to tests/reduction_methods_1D/rb_distributed_greedy_1d.py
diff --git a/tests/test_4_reduction_methods_multiD/matrix_random.py b/tests/reduction_methods_multiD/matrix_random.py
similarity index 100%
rename from tests/test_4_reduction_methods_multiD/matrix_random.py
rename to tests/reduction_methods_multiD/matrix_random.py
diff --git a/tests/test_4_reduction_methods_multiD/rational_interpolant_2d.py b/tests/reduction_methods_multiD/rational_interpolant_2d.py
similarity index 100%
rename from tests/test_4_reduction_methods_multiD/rational_interpolant_2d.py
rename to tests/reduction_methods_multiD/rational_interpolant_2d.py
diff --git a/tests/test_4_reduction_methods_multiD/rational_pade_2d.py b/tests/reduction_methods_multiD/rational_pade_2d.py
similarity index 100%
rename from tests/test_4_reduction_methods_multiD/rational_pade_2d.py
rename to tests/reduction_methods_multiD/rational_pade_2d.py
diff --git a/tests/test_4_reduction_methods_multiD/rb_centered_2d.py b/tests/reduction_methods_multiD/rb_centered_2d.py
similarity index 100%
rename from tests/test_4_reduction_methods_multiD/rb_centered_2d.py
rename to tests/reduction_methods_multiD/rb_centered_2d.py
diff --git a/tests/test_4_reduction_methods_multiD/rb_distributed_2d.py b/tests/reduction_methods_multiD/rb_distributed_2d.py
similarity index 100%
rename from tests/test_4_reduction_methods_multiD/rb_distributed_2d.py
rename to tests/reduction_methods_multiD/rb_distributed_2d.py
diff --git a/tests/test_1_utilities/basic_routines.py b/tests/utilities/basic_routines.py
similarity index 100%
rename from tests/test_1_utilities/basic_routines.py
rename to tests/utilities/basic_routines.py
diff --git a/tests/test_1_utilities/fenics_const.py b/tests/utilities/fenics_const.py
similarity index 100%
rename from tests/test_1_utilities/fenics_const.py
rename to tests/utilities/fenics_const.py
diff --git a/tests/test_1_utilities/fenics_norms.py b/tests/utilities/fenics_norms.py
similarity index 100%
rename from tests/test_1_utilities/fenics_norms.py
rename to tests/utilities/fenics_norms.py
diff --git a/tests/test_1_utilities/parameter_sampling.py b/tests/utilities/parameter_sampling.py
similarity index 100%
rename from tests/test_1_utilities/parameter_sampling.py
rename to tests/utilities/parameter_sampling.py
diff --git a/tests/test_1_utilities/fitting.py b/tests/utilities/poly_fitting.py
similarity index 100%
rename from tests/test_1_utilities/fitting.py
rename to tests/utilities/poly_fitting.py
diff --git a/tests/utilities/radial_fitting.py b/tests/utilities/radial_fitting.py
new file mode 100644
index 0000000..766e0a9
--- /dev/null
+++ b/tests/utilities/radial_fitting.py
@@ -0,0 +1,161 @@
+# 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.poly_fitting import customFit
+from rrompy.utilities.poly_fitting.radial_basis import (radialGaussian,
+ thinPlateSpline,
+ multiQuadric,
+ polybases, polyfitname,
+ polydomcoeff,
+ radialFunction,
+ polyval, polyvander,
+ homogeneizedpolyvander)
+from rrompy.utilities.poly_fitting.polynomial import homogeneizedToFull
+from rrompy.parameter import checkParameterList
+
+def test_monomial_gaussian():
+ polyrbname = "MONOMIAL_GAUSSIAN"
+ assert polyrbname in polybases
+ fitname = polyfitname(polyrbname)
+ domcoeff = polydomcoeff(5, polyrbname)
+ assert fitname == "polyfit_gaussian"
+ assert np.isclose(domcoeff, 1., rtol = 1e-5)
+
+ directionalWeights = np.array([5.])
+ xSupp = checkParameterList(np.arange(-1, 3), 1)[0]
+ cRBCoeffs = np.array([-1., 3., -3., 1., 1., 2., -.5])
+ cRB = radialFunction(directionalWeights = directionalWeights,
+ supportPoints = xSupp, localCoeffs = cRBCoeffs[: 4],
+ globalCoeffs = cRBCoeffs[4 :])
+ ySupp = 1 + 2. * xSupp.data - .5 * xSupp.data ** 2.
+ xx = np.linspace(-2., 3., 100)
+ yy = polyval(checkParameterList(xx, 1)[0], cRB, polyrbname)
+ yyman = 1. + 2. * xx - .5 * xx ** 2.
+ for j, xc in enumerate(np.arange(-1, 3)):
+ r2j = (5. * (xx - xc)) ** 2.
+ rbj = radialGaussian(r2j)
+ assert np.allclose(rbj, np.exp(-.5 * r2j))
+ yyman += cRB.localCoeffs[j] * rbj
+ ySupp += cRB.localCoeffs[j] * radialGaussian((directionalWeights[0]
+ * (xSupp.data - xc)) ** 2.)
+ assert np.allclose(yy, yyman, atol = 1e-5)
+
+ VanT = polyvander(xSupp, [2], polyrbname,
+ directionalWeights = directionalWeights)
+ ySupp = np.pad(ySupp.flatten(), (0, len(VanT) - len(xSupp)), "constant")
+ out = customFit(VanT, ySupp)
+ assert np.allclose(out, cRBCoeffs, atol = 1e-8)
+
+def test_legendre_thinplate():
+ polyrbname = "LEGENDRE_THINPLATE"
+ assert polyrbname in polybases
+ fitname = polyfitname(polyrbname)
+ domcoeff = polydomcoeff(5, polyrbname)
+ assert fitname == "legfit_thinplate"
+ assert np.isclose(domcoeff, 63. / 8, rtol = 1e-5)
+
+ directionalWeights = np.array([.5])
+ xSupp = checkParameterList(np.arange(-1, 3), 1)[0]
+ cRBCoeffs = np.array([-1., 3., -3., 1., 1., 2., -.5])
+ cRB = radialFunction(directionalWeights = directionalWeights,
+ supportPoints = xSupp, localCoeffs = cRBCoeffs[: 4],
+ globalCoeffs = cRBCoeffs[4 :])
+ ySupp = 1 + 2. * xSupp.data - .5 * (.5 * (3. * xSupp.data ** 2. - 1.))
+ xx = np.linspace(-2., 3., 100)
+ yy = polyval(checkParameterList(xx, 1)[0], cRB, polyrbname)
+ yyman = 1. + 2. * xx - .5 * (.5 * (3. * xx ** 2. - 1.))
+ for j, xc in enumerate(np.arange(-1, 3)):
+ r2j = (directionalWeights[0] * (xx - xc)) ** 2.
+ rbj = thinPlateSpline(r2j)
+ assert np.allclose(rbj, .5 * r2j * np.log(np.finfo(float).eps + r2j))
+ yyman += cRB.localCoeffs[j] * rbj
+ ySupp += cRB.localCoeffs[j] * thinPlateSpline((directionalWeights[0]
+ * (xSupp.data - xc)) ** 2.)
+ assert np.allclose(yy, yyman, atol = 1e-5)
+
+ VanT = polyvander(xSupp, [2], polyrbname,
+ directionalWeights = directionalWeights)
+ ySupp = np.pad(ySupp.flatten(), (0, len(VanT) - len(xSupp)), "constant")
+ out = customFit(VanT, ySupp)
+ assert np.allclose(out, cRBCoeffs, atol = 1e-8)
+
+def test_chebyshev_multiquadric():
+ polyrbname = "CHEBYSHEV_MULTIQUADRIC"
+ assert polyrbname in polybases
+ fitname = polyfitname(polyrbname)
+ domcoeff = polydomcoeff(5, polyrbname)
+ assert fitname == "chebfit_multiquadric"
+ assert np.isclose(domcoeff, 16, rtol = 1e-5)
+
+ directionalWeights = np.array([1.])
+ xSupp = checkParameterList(np.arange(-1, 3), 1)[0]
+ cRBCoeffs = np.array([-1., 3., -3., 1., 1., 2., -.5])
+ cRB = radialFunction(directionalWeights = directionalWeights,
+ supportPoints = xSupp, localCoeffs = cRBCoeffs[: 4],
+ globalCoeffs = cRBCoeffs[4 :])
+ ySupp = 1 + 2. * xSupp.data - .5 * (2. * xSupp.data ** 2. - 1.)
+ xx = np.linspace(-2., 3., 100)
+ yy = polyval(checkParameterList(xx, 1)[0], cRB, polyrbname)
+ yyman = 1. + 2. * xx - .5 * (2. * xx ** 2. - 1.)
+ for j, xc in enumerate(np.arange(-1, 3)):
+ r2j = (directionalWeights[0] * (xx - xc)) ** 2.
+ rbj = multiQuadric(r2j)
+ assert np.allclose(rbj, np.power(r2j + 1, -.5))
+ yyman += cRB.localCoeffs[j] * rbj
+ ySupp += cRB.localCoeffs[j] * multiQuadric((directionalWeights[0]
+ * (xSupp.data - xc)) ** 2.)
+ assert np.allclose(yy, yyman, atol = 1e-5)
+
+ VanT = polyvander(xSupp, [2], polyrbname,
+ directionalWeights = directionalWeights)
+ ySupp = np.pad(ySupp.flatten(), (0, len(VanT) - len(xSupp)), "constant")
+ out = customFit(VanT, ySupp)
+ assert np.allclose(out, cRBCoeffs, atol = 1e-8)
+
+def test_total_degree_2d():
+ values = lambda x, y: (x - 3.) ** 2. * y - (x + 1.) * y ** 2.
+
+ polyrbname = "CHEBYSHEV_GAUSSIAN"
+
+ xs, ys = np.meshgrid(np.linspace(0., 4., 5), np.linspace(0., 4., 4))
+ xySupp = np.concatenate((xs.reshape(-1, 1), ys.reshape(-1, 1)), axis = 1)
+ zs = values(xs, ys)
+ zSupp = zs.flatten()
+
+ deg = 3
+ directionalWeights = [2., 1.]
+ VanT, _, reidxs = homogeneizedpolyvander(xySupp, deg, polyrbname,
+ directionalWeights = directionalWeights)
+ VanT = VanT[reidxs]
+ VanT = VanT[:, reidxs]
+ cFit = np.linalg.solve(VanT, np.pad(zSupp, (0, len(VanT) - len(zSupp)),
+ 'constant'))
+ globCoeff = homogeneizedToFull([deg + 1] * 2, 2, cFit[len(zSupp) :])
+ cRB = radialFunction(directionalWeights = directionalWeights,
+ supportPoints = xySupp,
+ localCoeffs = cFit[: len(zSupp)],
+ globalCoeffs = globCoeff)
+ xx, yy = np.meshgrid(np.linspace(0., 4., 100), np.linspace(0., 4., 100))
+ xxyy = np.concatenate((xx.reshape(-1, 1), yy.reshape(-1, 1)), axis = 1)
+ zz = polyval(xxyy, cRB, polyrbname).reshape(xx.shape)
+ zzex = values(xx, yy)
+
+ error = np.abs(zz - zzex)
+ print(np.max(error))
+ assert np.max(error) < 1e-10
diff --git a/tests/test_1_utilities/sampling.py b/tests/utilities/sampling.py
similarity index 100%
rename from tests/test_1_utilities/sampling.py
rename to tests/utilities/sampling.py
diff --git a/tests/utilities/scipy_tensorize.py b/tests/utilities/scipy_tensorize.py
new file mode 100644
index 0000000..037cdf5
--- /dev/null
+++ b/tests/utilities/scipy_tensorize.py
@@ -0,0 +1,57 @@
+# 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
+from rrompy.solver.scipy import tensorizeLS, detensorizeLS
+
+def test_dense():
+ N = 5
+ A1 = np.random.rand(N, N)
+ x1 = np.random.rand(N)
+ b1 = A1.dot(x1)
+ A2 = np.diag(np.arange(1, N + 1) * 2.)
+ x2 = np.ones(N)
+ b2 = A2.dot(x2)
+ A3 = np.eye(N)
+ x3 = np.ones(N)
+ b3 = A3.dot(x3)
+ A, b = tensorizeLS([A1, A2, A3], [b1, b2, b3])
+ assert np.allclose(A.shape, (3 * N, 3 * N))
+ assert np.allclose(b.shape, (3 * N,))
+ x = scsp.linalg.spsolve(A, b)
+ x1O, x2O, x3O = detensorizeLS(x, 3)
+ assert np.allclose(x1O, x1, rtol = 1e-8)
+ assert np.allclose(x2O, x2, rtol = 1e-8)
+ assert np.allclose(x3O, x3, rtol = 1e-8)
+
+def test_sparse():
+ N = 5
+ A1 = scsp.eye(N, format = "csr")
+ x1 = np.random.rand(N)
+ b1 = A1.dot(x1)
+ A2 = scsp.diags(np.arange(1, N + 1) * 2., 0, format = "csr")
+ x2 = np.ones(N)
+ b2 = A2.dot(x2)
+ A, b = tensorizeLS([A1, A2], [b1, b2])
+ assert np.allclose(A.shape, (2 * N, 2 * N))
+ assert np.allclose(b.shape, (2 * N,))
+ x = scsp.linalg.spsolve(A, b)
+ x1O, x2O = detensorizeLS(x, sizes = [5, 5])
+ assert np.allclose(x1O, x1, rtol = 1e-8)
+ assert np.allclose(x2O, x2, rtol = 1e-8)