diff --git a/VERSION b/VERSION index 4684374..872765e 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -1.8 \ No newline at end of file +1.9 \ No newline at end of file diff --git a/examples/2d/base/cookie_single.py b/examples/2d/base/cookie_single.py deleted file mode 100644 index f153e01..0000000 --- a/examples/2d/base/cookie_single.py +++ /dev/null @@ -1,29 +0,0 @@ -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 index b98b5e5..8c660db 100644 --- a/examples/2d/base/fracture.py +++ b/examples/2d/base/fracture.py @@ -1,46 +1,45 @@ import numpy as np import ufl import fenics as fen -from rrompy.hfengines.linear_problem.bidimensional import \ - MembraneFractureEngine as MFE +from membrane_fracture_engine import MembraneFractureEngine as MFE from rrompy.solver.fenics import affine_warping verb = 100 mu0 = [45. ** .5, .7] 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() 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(mu0, uh)[0], name = 'res', # what = 'REAL', figsize = (8, 5)) #solver.outParaviewTimeDomain(uh, mu0[0], filename = 'out', folder = True, # forceNewFile = False) y = fen.SpatialCoordinate(solver.V.mesh())[1] warp1, warpI1 = affine_warping(solver.V.mesh(), np.array([[1, 0], [0, 2. * mu0[1]]])) warp2, warpI2 = affine_warping(solver.V.mesh(), np.array([[1, 0], [0, 2. - 2. * mu0[1]]])) 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(mu0, uh)[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 index 400c730..ffbc047 100644 --- a/examples/2d/base/fracture_nodomain.py +++ b/examples/2d/base/fracture_nodomain.py @@ -1,47 +1,47 @@ import numpy as np import ufl import fenics as fen -from rrompy.hfengines.linear_problem import MembraneFractureEngineNoDomain \ +from membrane_fracture_nodomain_engine import MembraneFractureNoDomainEngine \ 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() 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(mu0, uh)[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(mu0, uh)[0], [warp, warpI], name = 'res', what = 'REAL', figsize = (8, 5)) solver.outParaviewTimeDomain(uh, mu0, [warp, warpI], filename = 'outNDW', folder = True) diff --git a/rrompy/hfengines/linear_problem/bidimensional/helmholtz_square_domain_problem_engine.py b/examples/2d/base/helmholtz_square_domain_problem_engine.py similarity index 90% rename from rrompy/hfengines/linear_problem/bidimensional/helmholtz_square_domain_problem_engine.py rename to examples/2d/base/helmholtz_square_domain_problem_engine.py index f9d521a..c12868a 100644 --- a/rrompy/hfengines/linear_problem/bidimensional/helmholtz_square_domain_problem_engine.py +++ b/examples/2d/base/helmholtz_square_domain_problem_engine.py @@ -1,123 +1,122 @@ # 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 numpy.polynomial.polynomial import polyfit as fit import fenics as fen from rrompy.utilities.base.types import paramVal from rrompy.solver.fenics import fenZERO from rrompy.hfengines.linear_problem.helmholtz_problem_engine import ( HelmholtzProblemEngine) from rrompy.utilities.base import verbosityManager as vbMng 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, homogeneized : bool = False, - verbosity : int = 10, timestamp : bool = True): + degree_threshold : int = np.inf, verbosity : int = 10, + timestamp : bool = True): super().__init__(mu0 = mu0, degree_threshold = degree_threshold, - homogeneized = homogeneized, verbosity = verbosity, - timestamp = timestamp) + verbosity = verbosity, timestamp = timestamp) self._affinePoly = False self.nAs, self.nbs = 3, 11 self.npar = 2 self.rescalingExp = [2., 1.] self._theta = theta self._kappa = kappa 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) def buildA(self): """Build terms of operator of linear system.""" if self.As[0] is None: vbMng(self, "INIT", "Assembling operator term A0.", 20) DirichletBC0 = fen.DirichletBC(self.V, fenZERO, self.DirichletBoundary) a0Re = fen.dot(self.u.dx(0), self.v.dx(0)) * fen.dx self.As[0] = fenics2Sparse(a0Re, {}, DirichletBC0, 1) self.thAs[0] = self.getMonomialSingleWeight([0, 0]) vbMng(self, "DEL", "Done assembling operator term.", 20) if self.As[1] is None: vbMng(self, "INIT", "Assembling operator term A1.", 20) 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 self.As[1] = (fenics2Sparse(a1Re, parsRe, DirichletBC0, 0) + 1.j * fenics2Sparse(a1Im, parsIm, DirichletBC0, 0)) self.thAs[1] = self.getMonomialSingleWeight([1, 0]) vbMng(self, "DEL", "Done assembling operator term.", 20) if self.As[2] is None: vbMng(self, "INIT", "Assembling operator term A2.", 20) DirichletBC0 = fen.DirichletBC(self.V, fenZERO, self.DirichletBoundary) a2Re = fen.dot(self.u.dx(1), self.v.dx(1)) * fen.dx self.As[2] = fenics2Sparse(a2Re, {}, DirichletBC0, 0) self.thAs[2] = self.getMonomialSingleWeight([0, 2]) vbMng(self, "DEL", "Done assembling operator term.", 20) def buildb(self): """Build terms of operator of linear system.""" if self.thbs[0] is None: - self.thbs = [None] * (self.nbs + self.homogeneized * self.nAs) + self.thbs = [None] * self.nbs bDEIMCoeffs = None for j in range(self.nbs): if self.bs[j] is None: vbMng(self, "INIT", "Assembling forcing term b{}.".format(j), 20) if bDEIMCoeffs is None: - bDEIM = np.empty((self.nbs, self.spacedim()), - dtype = np.complex) muDEIM = np.linspace(.5, 4., bDEIM.shape[0]) for jj, muD in enumerate(muDEIM): c, s = np.cos(self._theta), np.sin(self._theta) x, y = fen.SpatialCoordinate(self.V.mesh())[:] fRe = fen.cos(self._kappa * (c * x + s / muD * y)) fIm = fen.sin(self._kappa * (c * x + s / muD * y)) parsRe = self.iterReduceQuadratureDegree(zip([fRe], ["forcingTerm{}Real".format(jj)])) parsIm = self.iterReduceQuadratureDegree(zip([fIm], ["forcingTerm{}Imag".format(jj)])) LR = fen.dot(fRe, self.v) * fen.dx LI = fen.dot(fIm, self.v) * fen.dx DBC0 = fen.DirichletBC(self.V, fenZERO, self.DirichletBoundary) - bDEIM[jj] = (fenics2Vector(LR, parsRe, DBC0, 1) + bjj = (fenics2Vector(LR, parsRe, DBC0, 1) + 1.j * fenics2Vector(LI, parsIm, DBC0, 1)) + if jj == 0: + bDEIM = np.empty((self.nbs, len(bjj)), + dtype = np.complex) + bDEIM[jj] = bjj bDEIMCoeffs = fit(muDEIM, bDEIM, len(muDEIM) - 1) self.bs[j] = bDEIMCoeffs[j] self.thbs[j] = self.getMonomialSingleWeight([0, j]) vbMng(self, "DEL", "Done assembling forcing term.", 20) - self.setbHomogeneized() + diff --git a/rrompy/hfengines/linear_problem/bidimensional/helmholtz_square_simplified_domain_problem_engine.py b/examples/2d/base/helmholtz_square_simplified_domain_problem_engine.py similarity index 92% rename from rrompy/hfengines/linear_problem/bidimensional/helmholtz_square_simplified_domain_problem_engine.py rename to examples/2d/base/helmholtz_square_simplified_domain_problem_engine.py index c0c3476..0022352 100644 --- a/rrompy/hfengines/linear_problem/bidimensional/helmholtz_square_simplified_domain_problem_engine.py +++ b/examples/2d/base/helmholtz_square_simplified_domain_problem_engine.py @@ -1,89 +1,86 @@ # 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 paramVal from rrompy.solver.fenics import fenZERO from rrompy.hfengines.linear_problem.helmholtz_problem_engine import ( HelmholtzProblemEngine) from rrompy.utilities.base import verbosityManager as vbMng 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, homogeneized : bool = False, - verbosity : int = 10, timestamp : bool = True): + degree_threshold : int = np.inf, verbosity : int = 10, + timestamp : bool = True): super().__init__(mu0 = mu0, degree_threshold = degree_threshold, - homogeneized = homogeneized, verbosity = verbosity, - timestamp = timestamp) + verbosity = verbosity, timestamp = timestamp) self._affinePoly = True 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)[:] self.forcingTerm = [fen.cos(kappa * (c * x + s * y)), fen.sin(kappa * (c * x + s * y))] def buildA(self): """Build terms of operator of linear system.""" if self.thAs[0] is None: self.thAs = self.getMonomialWeights(self.nAs) if self.As[0] is None: vbMng(self, "INIT", "Assembling operator term A0.", 20) DirichletBC0 = fen.DirichletBC(self.V, fenZERO, self.DirichletBoundary) a0Re = fen.dot(self.u.dx(0), self.v.dx(0)) * fen.dx self.As[0] = fenics2Sparse(a0Re, {}, DirichletBC0, 1) vbMng(self, "DEL", "Done assembling operator term.", 20) if self.As[1] is None: vbMng(self, "INIT", "Assembling operator term A1.", 20) 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 self.As[1] = (fenics2Sparse(a1Re, parsRe, DirichletBC0, 0) + 1.j * fenics2Sparse(a1Im, parsIm, DirichletBC0, 0)) vbMng(self, "DEL", "Done assembling operator term.", 20) if self.As[2] is None: vbMng(self, "INIT", "Assembling operator term A2.", 20) DirichletBC0 = fen.DirichletBC(self.V, fenZERO, self.DirichletBoundary) a2Re = fen.dot(self.u.dx(1), self.v.dx(1)) * fen.dx self.As[2] = fenics2Sparse(a2Re, {}, DirichletBC0, 0) vbMng(self, "DEL", "Done assembling operator term.", 20) diff --git a/rrompy/hfengines/linear_problem/tridimensional/matrix_dynamical_passive.py b/examples/2d/base/matrix_dynamical_passive_engine.py similarity index 84% rename from rrompy/hfengines/linear_problem/tridimensional/matrix_dynamical_passive.py rename to examples/2d/base/matrix_dynamical_passive_engine.py index fcec560..33e2095 100644 --- a/rrompy/hfengines/linear_problem/tridimensional/matrix_dynamical_passive.py +++ b/examples/2d/base/matrix_dynamical_passive_engine.py @@ -1,61 +1,60 @@ # Copyright (C) 2018 by the RROMPy authors # # This file is part of RROMPy. # # RROMPy is free software: you can redistribute it and/or modify # it under the terms of the GNU Lesser General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # RROMPy is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU Lesser General Public License for more details. # # You should have received a copy of the GNU Lesser General Public License # along with RROMPy. If not, see . # import numpy as np import scipy.sparse as sp from rrompy.utilities.base.types import ListAny, paramVal -from rrompy.hfengines.base.matrix_engine_base import MatrixEngineBase +from rrompy.hfengines.base.linear_affine_engine import LinearAffineEngine +from rrompy.hfengines.base.numpy_engine_base import NumpyEngineBase from rrompy.parameter import checkParameter -__all__ = ['MatrixDynamicalPassive'] - -class MatrixDynamicalPassive(MatrixEngineBase): +class MatrixDynamicalPassiveEngine(LinearAffineEngine, NumpyEngineBase): def __init__(self, mu0 : paramVal = [0., 10.], n : int = 1000, omega0 : ListAny = [1.], domega0 : ListAny = [1.], b : float = 10., verbosity : int = 10, timestamp : bool = True): super().__init__(verbosity = verbosity, timestamp = timestamp) self._affinePoly = True self.npar = 1 + len(omega0) self.mu0 = checkParameter(mu0, self.npar) self.nAs, self.nbs = 1 + self.npar, 1 Asize = 2 + 2 * self.npar + n dataA0 = np.concatenate((*tuple([tuple([np.imag(o), - np.real(o), np.real(o), np.imag(o)]) \ for o in omega0]), [1., -200., 200., 1., 1., -400., 400., 1.], 1. + np.arange(n))) rowA0 = np.concatenate((np.repeat(np.arange(2 * len(omega0) + 4), 2), 2 + 2 * self.npar + np.arange(n))) cB0 = np.repeat(np.arange(0, 2 * len(omega0) + 4, 2), 4) cB1 = np.tile([0, 1], [1, 2 * len(omega0) + 4]).reshape(-1) colA0 = np.concatenate((cB0 + cB1, 2 + 2 * self.npar + np.arange(n))) self.As = [sp.csr_matrix((dataA0, (rowA0, colA0)), dtype = np.complex, - shape = (Asize, Asize))] + shape = (Asize, Asize))] self.As = self.As + [1.j * sp.eye(Asize, dtype = np.complex)] for j, do0 in enumerate(domega0): self.As = self.As + [sp.csr_matrix(([- do0, do0], ([2*j, 2*j+1], [2*j+1, 2*j])), dtype = np.complex, shape = (Asize, Asize))] - self.bs = [np.concatenate((b * np.ones(2 + 2 * self.npar), - np.ones(n)))] - self.thAs = self.getMonomialWeights(self.nAs) - self.thbs = self.getMonomialWeights(self.nbs) + self.setbs([np.concatenate((b * np.ones(2 + 2 * self.npar), + np.ones(n)))]) + self.setthAs(self.getMonomialWeights(self.nAs)) + self.setthbs(self.getMonomialWeights(self.nbs)) diff --git a/examples/2d/base/matrix_passive.py b/examples/2d/base/matrix_passive.py index cc1331a..abbbf8e 100644 --- a/examples/2d/base/matrix_passive.py +++ b/examples/2d/base/matrix_passive.py @@ -1,13 +1,12 @@ -from rrompy.hfengines.linear_problem.tridimensional import \ - MatrixDynamicalPassive as MDP +from matrix_dynamical_passive_engine import MatrixDynamicalPassiveEngine as MDP verb = 100 n = 100 b = 10 mu0 = [0., 10] mutar = [4., 15] solver = MDP(mu0 = mu0, n = n, b = b, verbosity = verb) uh = solver.solve(mutar)[0] solver.plot(uh) diff --git a/examples/2d/base/membrane_fracture_engine.py b/examples/2d/base/membrane_fracture_engine.py new file mode 120000 index 0000000..9e382c7 --- /dev/null +++ b/examples/2d/base/membrane_fracture_engine.py @@ -0,0 +1 @@ +/home/pradovera/Desktop/Repos/RROMPy/tests/hfengines/membrane_fracture_engine.py \ No newline at end of file diff --git a/examples/2d/base/membrane_fracture_nodomain_engine.py b/examples/2d/base/membrane_fracture_nodomain_engine.py new file mode 120000 index 0000000..45ce4c8 --- /dev/null +++ b/examples/2d/base/membrane_fracture_nodomain_engine.py @@ -0,0 +1 @@ +/home/pradovera/Desktop/Repos/RROMPy/tests/hfengines/membrane_fracture_nodomain_engine.py \ No newline at end of file diff --git a/examples/2d/base/square.py b/examples/2d/base/square.py index 856cf1a..139831e 100644 --- a/examples/2d/base/square.py +++ b/examples/2d/base/square.py @@ -1,13 +1,14 @@ import numpy as np -from rrompy.hfengines.linear_problem.bidimensional import \ +from helmholtz_square_domain_problem_engine import \ HelmholtzSquareDomainProblemEngine as HSDPE + verb = 100 mu0 = [4 ** .5, 1.5 ** .5] solver = HSDPE(kappa = 2.5, theta = np.pi / 3, mu0 = mu0, n = 50, verbosity = verb) uh = solver.solve(mu0)[0] solver.plotmesh() print(solver.norm(uh)) solver.plot(uh) -solver.plot(solver.residual(mu0, uh)[0], 'res') +solver.plot(solver.residual(mu0, uh)[0], name = 'res') diff --git a/examples/2d/base/square_simplified.py b/examples/2d/base/square_simplified.py index 6188ee7..7e7b836 100644 --- a/examples/2d/base/square_simplified.py +++ b/examples/2d/base/square_simplified.py @@ -1,13 +1,13 @@ import numpy as np -from rrompy.hfengines.linear_problem.bidimensional import \ +from helmholtz_square_simplified_domain_problem_engine import \ HelmholtzSquareSimplifiedDomainProblemEngine as HSSDPE verb = 0 mu0 = [4 ** .5, 1.5 ** .5] solver = HSSDPE(kappa = 2.5, theta = np.pi / 3, mu0 = mu0, n = 50, verbosity = verb) uh = solver.solve(mu0)[0] solver.plotmesh() print(solver.norm(uh)) solver.plot(uh) -solver.plot(solver.residual(mu0, uh)[0], 'res') +solver.plot(solver.residual(mu0, uh)[0], name = 'res') diff --git a/rrompy/hfengines/linear_problem/bidimensional/synthetic_bivariate_engine.py b/examples/2d/base/synthetic_bivariate_engine.py similarity index 94% rename from rrompy/hfengines/linear_problem/bidimensional/synthetic_bivariate_engine.py rename to examples/2d/base/synthetic_bivariate_engine.py index 1b1637f..7758a90 100644 --- a/rrompy/hfengines/linear_problem/bidimensional/synthetic_bivariate_engine.py +++ b/examples/2d/base/synthetic_bivariate_engine.py @@ -1,108 +1,105 @@ # Copyright (C) 2018 by the RROMPy authors # # This file is part of RROMPy. # # RROMPy is free software: you can redistribute it and/or modify # it under the terms of the GNU Lesser General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # RROMPy is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU Lesser General Public License for more details. # # You should have received a copy of the GNU Lesser General Public License # along with RROMPy. If not, see . # import numpy as np import fenics as fen import ufl from rrompy.utilities.base.types import paramVal from rrompy.solver.fenics import fenONE, fenZERO from rrompy.hfengines.linear_problem.helmholtz_problem_engine import ( HelmholtzProblemEngine) from rrompy.utilities.base import verbosityManager as vbMng from rrompy.solver.fenics import fenics2Sparse -__all__ = ['SyntheticBivariateEngine'] - class SyntheticBivariateEngine(HelmholtzProblemEngine): def __init__(self, kappa:float, theta:float, n:int, L : int = 2., mu0 : paramVal = [12. ** .5, 15. ** .5], - degree_threshold : int = np.inf, homogeneized : bool = False, - verbosity : int = 10, timestamp : bool = True): + degree_threshold : int = np.inf, verbosity : int = 10, + timestamp : bool = True): super().__init__(mu0 = mu0, degree_threshold = degree_threshold, - homogeneized = homogeneized, verbosity = verbosity, - timestamp = timestamp) + verbosity = verbosity, timestamp = timestamp) self._affinePoly = True self.nAs = 3 self.npar = 2 self.rescalingExp = [2., 2.] mesh = fen.RectangleMesh(fen.Point(0., 0.), fen.Point(L, L), n, n) self.V = fen.FunctionSpace(mesh, "P", 1) x, y = fen.SpatialCoordinate(mesh)[:] 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 buildA(self): """Build terms of operator of linear system.""" if self.thAs[0] is None: self.thAs = self.getMonomialWeights(self.nAs) if self.As[0] is None: self.autoSetDS() vbMng(self, "INIT", "Assembling operator term A0.", 20) 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)) self.As[0] = (fenics2Sparse(a0Re, parsRe, DirichletBC0, 1) + 1.j * fenics2Sparse(a0Im, parsIm, DirichletBC0, 0)) vbMng(self, "DEL", "Done assembling operator term.", 20) if self.As[1] is None: vbMng(self, "INIT", "Assembling operator term A1.", 20) 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 * 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)) vbMng(self, "DEL", "Done assembling operator term.", 20) if self.As[2] is None: vbMng(self, "INIT", "Assembling operator term A2.", 20) 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 * 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)) vbMng(self, "DEL", "Done assembling operator term.", 20) diff --git a/examples/2d/base/synthetic_solve.py b/examples/2d/base/synthetic_solve.py index 6e58ce7..9c574e3 100644 --- a/examples/2d/base/synthetic_solve.py +++ b/examples/2d/base/synthetic_solve.py @@ -1,26 +1,25 @@ import numpy as np import fenics as fen -from rrompy.hfengines.linear_problem.bidimensional import \ - SyntheticBivariateEngine as SBE +from synthetic_bivariate_engine 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/greedy/fracture_pod.py b/examples/2d/greedy/fracture_pod.py index 1732615..f752364 100644 --- a/examples/2d/greedy/fracture_pod.py +++ b/examples/2d/greedy/fracture_pod.py @@ -1,221 +1,220 @@ import numpy as np -from rrompy.hfengines.linear_problem.bidimensional import \ - MembraneFractureEngine as MFE +from membrane_fracture_engine import MembraneFractureEngine as MFE from rrompy.reduction_methods.greedy import RationalInterpolantGreedy as RIG from rrompy.reduction_methods.greedy import ReducedBasisGreedy as RBG from rrompy.parameter.parameter_sampling import (QuadratureSampler as QS, QuadratureSamplerTotal as QST) verb = 15 size = 16 show_sample = False show_norm = True MN = 1 S = (MN + 2) * (MN + 1) // 2 greedyTol = 1e-1 collinearityTol = 0. nTestPoints = 900 algo = "rational" #algo = "RB" if algo == "rational": radial = "" #radial = "_gaussian" #radial = "_thinplate" #radial = "_multiquadric" rW0 = 5. radialWeight = [rW0] * 2 polybasis = "CHEBYSHEV" polybasis = "LEGENDRE" #polybasis = "MONOMIAL" errorEstimatorKind = 'AFFINE' errorEstimatorKind = 'DISCREPANCY' errorEstimatorKind = 'INTERPOLATORY' # errorEstimatorKind = 'EIM_DIAGONAL' errorEstimatorKind = 'EIM_INTERPOLATORY' 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 == 11: #L below mu0 = [110 ** .5, .4] mutar = [115 ** .5, .4] murange = [[90 ** .5, .3], [130 ** .5, .5]] elif size == 12: #L top mu0 = [110 ** .5, .6] mutar = [115 ** .5, .6] murange = [[90 ** .5, .5], [130 ** .5, .7]] elif size == 13: #L interesting mu0 = [110 ** .5, .5] mutar = [115 ** .5, .5] murange = [[90 ** .5, .3], [130 ** .5, .7]] elif size == 14: #L belowbelow mu0 = [110 ** .5, .2] mutar = [115 ** .5, .2] murange = [[90 ** .5, .1], [130 ** .5, .3]] elif size == 15: #L toptop mu0 = [110 ** .5, .8] mutar = [115 ** .5, .8] murange = [[90 ** .5, .7], [130 ** .5, .9]] elif size == 16: #L interestinginteresting mu0 = [110 ** .5, .5] mutar = [115 ** .5, .6] murange = [[90 ** .5, .1], [130 ** .5, .9]] elif size == 17: #L interestingtop mu0 = [110 ** .5, .7] mutar = [115 ** .5, .6] murange = [[90 ** .5, .5], [130 ** .5, .9]] elif size == 18: #L interestingbelow mu0 = [110 ** .5, .3] mutar = [115 ** .5, .4] murange = [[90 ** .5, .1], [130 ** .5, .5]] elif size == 100: # tiny mu0 = [32.5 ** .5, .5] mutar = [34 ** .5, .5] murange = [[30 ** .5, .3], [35 ** .5, .7]] aEff = 1.05 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) rescalingExp = [2., 1.] if algo == "rational": params = {'S':S, 'POD':True, 'greedyTol':greedyTol, 'sampler':QS(murange, 'UNIFORM', scalingExp = rescalingExp), 'nTestPoints':nTestPoints, 'polybasis':polybasis + radial, 'radialDirectionalWeights':radialWeight, 'errorEstimatorKind':errorEstimatorKind, 'trainSetGenerator':QST(murange, 'CHEBYSHEV', scalingExp = rescalingExp)} method = RIG else: #if algo == "RB": params = {'S':S, 'POD':True, 'greedyTol':greedyTol, 'sampler':QS(murange, 'UNIFORM', scalingExp = rescalingExp), 'nTestPoints':nTestPoints, 'trainSetGenerator':QST(murange, 'CHEBYSHEV', scalingExp = rescalingExp)} method = RBG approx = method(solver, mu0 = mu0, approxParameters = params, verbosity = verb) approx.greedy(True) 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', what = "REAL") approx.plotHF(mutar, [warp, warpI], name = 'u_HF', what = "REAL") approx.plotErr(mutar, [warp, warpI], name = 'err', what = "REAL") # approx.plotRes(mutar, [warp, warpI], name = 'res', 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))) verb = approx.trainedModel.verbosity approx.trainedModel.verbosity = 5 from plot_zero_set import plotZeroSet2 muZeroVals, Qvals = plotZeroSet2(murange, murangeEff, approx, mu0, 200, [2., 1.]) if show_norm: from plot_inf_set import plotInfSet2 muInfVals, normEx, normApp, normRes, normErr, beta = plotInfSet2( murange, murangeEff, approx, mu0, 50, [2., 1.], relative = True, nobeta = True) try: QV = approx.trainedModel.getQVal(muInfVals) import warnings from matplotlib import pyplot as plt mu2x, mu2y = approx.mus(0) ** 2., approx.mus(1) ** 1. murangeExp = [[murange[0][0] ** 2., murange[0][1]], [murange[1][0] ** 2., murange[1][1]]] mu1s = np.unique([m[0] for m in muInfVals]) mu2s = np.unique([m[1] for m in muInfVals]) mu1 = np.power(mu1s, 2.) mu2 = np.power(mu2s, 1.) Mu1, Mu2 = np.meshgrid(np.real(mu1), np.real(mu2)) Reste = (approx.errorEstimator(muInfVals) * QV).reshape(normEx.shape) Rest = np.log10(Reste) Restmin, Restmax = np.min(Rest), np.max(Rest) cmap = plt.cm.jet warnings.simplefilter("ignore", category = (UserWarning, np.ComplexWarning)) plt.figure(figsize = (15, 7)) plt.jet() p = plt.contourf(Mu1, Mu2, Rest, levels = np.linspace(Restmin, Restmax, 50)) plt.plot(np.real(mu2x), np.real(mu2y), 'kx') for j, xy in enumerate(zip(np.real(mu2x), np.real(mu2y))): plt.annotate("{}".format(j), xy) 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|res_est(mu)|") plt.colorbar(p) plt.grid() plt.show() except: pass approx.trainedModel.verbosity = verb try: print(np.sort(approx.getPoles([None, .5]) ** 2.)) except: pass diff --git a/examples/2d/greedy/membrane_fracture_engine.py b/examples/2d/greedy/membrane_fracture_engine.py new file mode 120000 index 0000000..9e382c7 --- /dev/null +++ b/examples/2d/greedy/membrane_fracture_engine.py @@ -0,0 +1 @@ +/home/pradovera/Desktop/Repos/RROMPy/tests/hfengines/membrane_fracture_engine.py \ No newline at end of file diff --git a/examples/2d/pod/cookie_single_pod.py b/examples/2d/pod/cookie_single_pod.py deleted file mode 100644 index 98a4f3c..0000000 --- a/examples/2d/pod/cookie_single_pod.py +++ /dev/null @@ -1,124 +0,0 @@ -import numpy as np -from rrompy.hfengines.linear_problem.bidimensional import \ - CookieEngineSingle as CES -from rrompy.reduction_methods.standard import RationalInterpolant as RI -from rrompy.reduction_methods.standard import ReducedBasis as RB -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 -clip = -1 -#clip = .4 -#clip = .6 - -Delta = -10 -MN = 5 -R = (MN + 2) * (MN + 1) // 2 -STensorized = (MN + 1) ** 2 -PODTol = 1e-6 - -samples = "centered" -samples = "standard" -algo = "rational" -#algo = "RB" -sampling = "quadrature" -sampling = "quadrature_total" -sampling = "random" - -if samples == "standard": - radial = "" -# 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] + bEff*murange[1][1]], - [(aEff*murange[1][0]**2. + bEff*murange[0][0]**2.) ** .5, - aEff*murange[1][1] + bEff*murange[0][1]]] - -kappa = 20. ** .5 -theta = - np.pi / 6. -n = 30 -Rad = 1. -L = np.pi -nX = 2 -nY = 1 - -solver = CES(kappa = kappa, theta = theta, n = n, R = Rad, L = L, nX = nX, - nY = nY, mu0 = mu0, verbosity = verb) - -rescalingExp = [2.] * 2 -if algo == "rational": - params = {'N':MN, 'M':MN + Delta, 'S':R, 'POD':True} - if samples == "standard": - params['polybasis'] = "CHEBYSHEV" + radial -# params['polybasis'] = "LEGENDRE" + radial -# params['polybasis'] = "MONOMIAL" + radial - params['radialDirectionalWeights'] = radialWeight - elif samples == "centered": - params['polybasis'] = "MONOMIAL" - method = RI -else: #if algo == "RB": - params = {'R':(MN + 2 + Delta) * (MN + 1 + Delta) // 2, 'S':R, - 'POD':True, 'PODTolerance':PODTol} - method = RB - -if samples == "standard": - if sampling == "quadrature": - params['sampler'] = QS(murange, "CHEBYSHEV", scalingExp = rescalingExp) -# params['sampler'] = QS(murange, "GAUSSLEGENDRE", scalingExp = rescalingExp) -# params['sampler'] = QS(murange, "UNIFORM", scalingExp = rescalingExp) - params['S'] = STensorized - elif sampling == "quadrature_total": - params['sampler'] = QST(murange, "CHEBYSHEV", scalingExp = rescalingExp) - else: # if sampling == "random": - params['sampler'] = RS(murange, "HALTON", scalingExp = rescalingExp) - -elif samples == "centered": - params['sampler'] = MS(murange, points = [mu0], scalingExp = rescalingExp) - -approx = method(solver, mu0 = mu0, approxParameters = params, verbosity = verb) - -approx.setupApprox() -if show_sample: - L = mutar[1] - approx.plotApprox(mutar, name = 'u_app', what = "REAL") - approx.plotHF(mutar, name = 'u_HF', what = "REAL") - approx.plotErr(mutar, name = 'err', what = "REAL") -# approx.plotRes(mutar, name = 'res', 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" 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: - 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 index c32c8a7..24a2046 100644 --- a/examples/2d/pod/fracture_pod.py +++ b/examples/2d/pod/fracture_pod.py @@ -1,265 +1,264 @@ import numpy as np -from rrompy.hfengines.linear_problem.bidimensional import \ - MembraneFractureEngine as MFE +from membrane_fracture_engine import MembraneFractureEngine as MFE from rrompy.reduction_methods.standard import RationalInterpolant as RI from rrompy.reduction_methods.standard import ReducedBasis as RB from rrompy.reduction_methods.pivoted import RationalInterpolantPivoted as RIP #from rrompy.reduction_methods.pivoted import ReducedBasisPivoted as RBP from rrompy.parameter.parameter_sampling import (QuadratureSampler as QS, QuadratureSamplerTotal as QST, ManualSampler as MS, RandomSampler as RS) verb = 10 size = 2 show_sample = True show_norm = True Delta = 0 MN = 5 R = (MN + 2) * (MN + 1) // 2 STensorized = (MN + 1) ** 2 PODTol = 1e-10 SPivot = MN + 1 SMarginal = 4 MMarginal = SMarginal - 1 matchingWeight = 1. cutOffTolerance = 1. * np.inf cutOffType = "MAGNITUDE" samples = "centered" samples = "standard" #samples = "pivoted" algo = "rational" algo = "RB" sampling = "quadrature" #sampling = "quadrature_total" #sampling = "random" samplingM = "quadrature" #samplingM = "quadrature_total" #samplingM = "random" polydegreetype = "TOTAL" polydegreetype = "FULL" if samples in ["standard", "pivoted"]: radial = "" # radial = "_gaussian" # radial = "_thinplate" # radial = "_multiquadric" rW0 = 5. radialWeight = [rW0] if samples == "pivoted": radialM = "" # radialM = "_gaussian" # radialM = "_thinplate" # radialM = "_multiquadric" rMW0 = 2. radialWeightM = [rMW0] 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 == 11: #L below mu0 = [110 ** .5, .4] mutar = [115 ** .5, .4] murange = [[90 ** .5, .3], [130 ** .5, .5]] elif size == 12: #L top mu0 = [110 ** .5, .6] mutar = [115 ** .5, .6] murange = [[90 ** .5, .5], [130 ** .5, .7]] elif size == 13: #L interesting mu0 = [110 ** .5, .5] mutar = [115 ** .5, .5] murange = [[90 ** .5, .3], [130 ** .5, .7]] elif size == 14: #L belowbelow mu0 = [110 ** .5, .2] mutar = [115 ** .5, .2] murange = [[90 ** .5, .1], [130 ** .5, .3]] elif size == 15: #L toptop mu0 = [110 ** .5, .8] mutar = [115 ** .5, .8] murange = [[90 ** .5, .7], [130 ** .5, .9]] elif size == 16: #L interestinginteresting mu0 = [110 ** .5, .5] mutar = [115 ** .5, .6] murange = [[90 ** .5, .1], [130 ** .5, .9]] elif size == 17: #L interestingtop mu0 = [110 ** .5, .7] mutar = [115 ** .5, .6] murange = [[90 ** .5, .5], [130 ** .5, .9]] elif size == 18: #L interestingbelow mu0 = [110 ** .5, .3] mutar = [115 ** .5, .4] murange = [[90 ** .5, .1], [130 ** .5, .5]] 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) rescalingExp = [2., 1.] if algo == "rational": params = {'N':MN, 'M':MN + Delta, 'S':R, 'POD':True, 'polydegreetype':polydegreetype} if samples == "standard": params['polybasis'] = "CHEBYSHEV" + radial # params['polybasis'] = "LEGENDRE" + radial # params['polybasis'] = "MONOMIAL" + radial params['radialDirectionalWeights'] = radialWeight * 2 method = RI elif samples == "centered": params['polybasis'] = "MONOMIAL" method = RI elif samples == "pivoted": params['S'] = SPivot params['SMarginal'] = SMarginal params['MMarginal'] = MMarginal params['polybasisPivot'] = "CHEBYSHEV" + radial params['polybasisMarginal'] = "MONOMIAL" + radialM params['radialDirectionalWeightsPivot'] = radialWeight params['radialDirectionalWeightsMarginal'] = radialWeightM params['matchingWeight'] = matchingWeight params['cutOffTolerance'] = cutOffTolerance params["cutOffType"] = cutOffType method = RIP else: #if algo == "RB": params = {'R':(MN + 2 + Delta) * (MN + 1 + Delta) // 2, 'S':R, 'POD':True, 'PODTolerance':PODTol} if samples == "standard": method = RB elif samples == "centered": method = RB elif samples == "pivoted": raise # params['S'] = SPivot # params['R'] = SPivot # params['SMarginal'] = SMarginal # params['MMarginal'] = MMarginal # params['polybasisMarginal'] = "MONOMIAL" + radialM # params['radialDirectionalWeightsMarginal'] = radialWeightM # params['matchingWeight'] = matchingWeight # params['cutOffTolerance'] = cutOffTolerance # params["cutOffType"] = cutOffType # method = RBP if samples == "standard": if sampling == "quadrature": params['sampler'] = QS(murange, "CHEBYSHEV", scalingExp = rescalingExp) # params['sampler'] = QS(murange, "GAUSSLEGENDRE", scalingExp = rescalingExp) # params['sampler'] = QS(murange, "UNIFORM", scalingExp = rescalingExp) params['S'] = STensorized elif sampling == "quadrature_total": params['sampler'] = QST(murange, "CHEBYSHEV", scalingExp = rescalingExp) else: # if sampling == "random": params['sampler'] = RS(murange, "HALTON", scalingExp = rescalingExp) elif samples == "centered": params['sampler'] = MS(murange, points = [mu0], scalingExp = rescalingExp) elif samples == "pivoted": if sampling == "quadrature": params['samplerPivot'] = QS([murange[0][0], murange[1][0]], "CHEBYSHEV") # params['samplerPivot'] = QS([murange[0][0], murange[1][0]], "GAUSSLEGENDRE") # params['samplerPivot'] = QS([murange[0][0], murange[1][0]], "UNIFORM") elif sampling == "quadrature_total": params['samplerPivot'] = QST([murange[0][0], murange[1][0]], "CHEBYSHEV") else: # if sampling == "random": params['samplerPivot'] = RS([murange[0][0], murange[1][0]], "HALTON") if samplingM == "quadrature": params['samplerMarginal'] = QS([murange[0][1], murange[1][1]], "UNIFORM") elif samplingM == "quadrature_total": params['samplerMarginal'] = QST([murange[0][1], murange[1][1]], "CHEBYSHEV") else: # if samplingM == "random": params['samplerMarginal'] = RS([murange[0][1], murange[1][1]], "HALTON") if samples != "pivoted": approx = method(solver, mu0 = mu0, approxParameters = params, verbosity = verb) else: approx = method(solver, mu0 = mu0, directionPivot = [0], approxParameters = params, verbosity = verb) 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', what = "REAL") approx.plotHF(mutar, [warp, warpI], name = 'u_HF', what = "REAL") approx.plotErr(mutar, [warp, warpI], name = 'err', what = "REAL") # approx.plotRes(mutar, [warp, warpI], name = 'res', 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))) approx.verbosity = 5 try: from plot_zero_set import plotZeroSet2 muZeroVals, Qvals = plotZeroSet2(murange, murangeEff, approx, mu0, 200, [2., 1.]) except: pass if show_norm: from plot_inf_set import plotInfSet2 muInfVals, normEx, normApp, normRes, normErr, beta = plotInfSet2( murange, murangeEff, approx, mu0, 25, [2., 1.], relative = True, nobeta = True) try: print(np.sort(approx.getPoles([None, .5]) ** 2.)) except: pass diff --git a/examples/2d/pod/fracture_pod_nodomain.py b/examples/2d/pod/fracture_pod_nodomain.py index 516dccd..da958d7 100644 --- a/examples/2d/pod/fracture_pod_nodomain.py +++ b/examples/2d/pod/fracture_pod_nodomain.py @@ -1,157 +1,155 @@ import numpy as np -from rrompy.hfengines.linear_problem import MembraneFractureEngineNoDomain \ +from membrane_fracture_nodomain_engine import MembraneFractureNoDomainEngine \ as MFEND from rrompy.reduction_methods.standard import RationalInterpolant as RI from rrompy.reduction_methods.standard import ReducedBasis as RB 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 samples = "centered" samples = "standard" algo = "rational" #algo = "RB" sampling = "quadrature" sampling = "quadrature_total" sampling = "random" if samples == "standard": radial = "" # 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, homogeneized = homogeneize) + verbosity = verb) rescalingExp = 2. if algo == "rational": params = {'N':MN, 'M':MN + Delta, 'S':R, 'POD':True} if samples == "standard": params['polybasis'] = "CHEBYSHEV" + radial # params['polybasis'] = "LEGENDRE" + radial # params['polybasis'] = "MONOMIAL" + radial params['radialDirectionalWeights'] = radialWeight elif samples == "centered": params['polybasis'] = "MONOMIAL" method = RI else: #if algo == "RB": params = {'R':R, 'S':R, 'POD':True} method = RB if samples == "standard": if sampling == "quadrature": params['sampler'] = QS(murange, "CHEBYSHEV", scalingExp = rescalingExp) # params['sampler'] = QS(murange, "GAUSSLEGENDRE", scalingExp = rescalingExp) # params['sampler'] = QS(murange, "UNIFORM", scalingExp = rescalingExp) elif sampling == "quadrature_total": params['sampler'] = QST(murange, "CHEBYSHEV", scalingExp = rescalingExp) else: # if sampling == "random": params['sampler'] = RS(murange, "HALTON", scalingExp = rescalingExp) elif samples == "centered": params['sampler'] = MS(murange, points = [mu0], scalingExp = rescalingExp) approx = method(solver, mu0 = mu0, approxParameters = params, verbosity = verb) 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', what = "REAL") approx.plotHF(mutar, [warp, warpI], name = 'u_HF', what = "REAL") approx.plotErr(mutar, [warp, warpI], name = 'err', what = "REAL") # approx.plotRes(mutar, [warp, warpI], name = 'res', 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 plotZeroSet1 muZeroVals, Qvals = plotZeroSet1(murange, murangeEff, approx, mu0, 1000, 2.) if show_norm: from plot_inf_set import plotInfSet1 muInfVals, normEx, normApp, normRes, normErr, beta = plotInfSet1( murange, murangeEff, approx, mu0, 200, 2., relative = False, normalizeDen = True) print(approx.getPoles() ** 2.) diff --git a/examples/2d/pod/helmholtz_square_domain_problem_engine.py b/examples/2d/pod/helmholtz_square_domain_problem_engine.py new file mode 120000 index 0000000..fc97c18 --- /dev/null +++ b/examples/2d/pod/helmholtz_square_domain_problem_engine.py @@ -0,0 +1 @@ +/home/pradovera/Desktop/Repos/RROMPy/examples/2d/base/helmholtz_square_domain_problem_engine.py \ No newline at end of file diff --git a/examples/2d/pod/helmholtz_square_simplified_domain_problem_engine.py b/examples/2d/pod/helmholtz_square_simplified_domain_problem_engine.py new file mode 120000 index 0000000..1d2e2e0 --- /dev/null +++ b/examples/2d/pod/helmholtz_square_simplified_domain_problem_engine.py @@ -0,0 +1 @@ +/home/pradovera/Desktop/Repos/RROMPy/examples/2d/base/helmholtz_square_simplified_domain_problem_engine.py \ No newline at end of file diff --git a/examples/2d/pod/matrix_dynamical_passive_engine.py b/examples/2d/pod/matrix_dynamical_passive_engine.py new file mode 120000 index 0000000..baf88ed --- /dev/null +++ b/examples/2d/pod/matrix_dynamical_passive_engine.py @@ -0,0 +1 @@ +/home/pradovera/Desktop/Repos/RROMPy/examples/2d/base/matrix_dynamical_passive_engine.py \ No newline at end of file diff --git a/examples/2d/pod/matrix_passive_pod.py b/examples/2d/pod/matrix_passive_pod.py index 294c429..9b70b6d 100644 --- a/examples/2d/pod/matrix_passive_pod.py +++ b/examples/2d/pod/matrix_passive_pod.py @@ -1,191 +1,190 @@ import numpy as np -from rrompy.hfengines.linear_problem.tridimensional import \ - MatrixDynamicalPassive as MDP +from matrix_dynamical_passive_engine import MatrixDynamicalPassiveEngine as MDP from rrompy.reduction_methods.standard import RationalInterpolant as RI from rrompy.reduction_methods.standard import ReducedBasis as RB from rrompy.reduction_methods.pivoted import RationalInterpolantPivoted as RIP #from rrompy.reduction_methods.pivoted import ReducedBasisPivoted as RBP from rrompy.parameter.parameter_sampling import (QuadratureSampler as QS, QuadratureSamplerTotal as QST, ManualSampler as MS, RandomSampler as RS) verb = 10 size = 3 show_sample = True show_norm = True Delta = 0 MN = 5 R = (MN + 2) * (MN + 1) // 2 STensorized = (MN + 1) ** 2 PODTol = 1e-6 SPivot = MN + 1 SMarginal = 3 MMarginal = SMarginal - 1 samples = "centered" samples = "standard" samples = "pivoted" algo = "rational" algo = "RB" sampling = "quadrature" #sampling = "quadrature_total" #sampling = "random" samplingM = "quadrature" #samplingM = "quadrature_total" #samplingM = "random" if samples in ["standard", "pivoted"]: radial = "" # radial = "_gaussian" # radial = "_thinplate" # radial = "_multiquadric" rW0 = 10. radialWeight = [rW0] if samples == "pivoted": radialM = "" # radialM = "_gaussian" # radialM = "_thinplate" # radialM = "_multiquadric" rMW0 = 2. radialWeightM = [rMW0] matchingWeight = 1. cutOffTolerance = 5. cutOffType = "POTENTIAL" if size == 1: mu0 = [2.7e2, 20] mutar = [3e2, 25] murange = [[20., 10], [5.2e2, 30]] elif size == 2: mu0 = [2.7e2, 60] mutar = [3e2, 75] murange = [[20., 10], [5.2e2, 110]] elif size == 3: mu0 = [2.7e2, 160] mutar = [3e2, 105] murange = [[20., 10], [5.2e2, 310]] assert Delta <= 0 aEff = 1.#25 bEff = 1. - aEff murangeEff = [[aEff*murange[0][0] + bEff*murange[1][0], aEff*murange[0][1] + bEff*murange[1][1]], [aEff*murange[1][0] + bEff*murange[0][0], aEff*murange[1][1] + bEff*murange[0][1]]] n = 100 b = 10 solver = MDP(mu0 = mu0, n = n, b = b, verbosity = verb) if algo == "rational": params = {'N':MN, 'M':MN + Delta, 'S':R, 'POD':True} if samples == "standard": params['polybasis'] = "CHEBYSHEV" + radial # params['polybasis'] = "LEGENDRE" + radial # params['polybasis'] = "MONOMIAL" + radial params['radialDirectionalWeights'] = radialWeight * 2 method = RI elif samples == "centered": params['polybasis'] = "MONOMIAL" method = RI elif samples == "pivoted": params['S'] = SPivot params['SMarginal'] = SMarginal params['MMarginal'] = MMarginal params['polybasisPivot'] = "CHEBYSHEV" + radial params['polybasisMarginal'] = "MONOMIAL" + radialM params['radialDirectionalWeightsPivot'] = radialWeight params['radialDirectionalWeightsMarginal'] = radialWeightM params['matchingWeight'] = matchingWeight params['cutOffTolerance'] = cutOffTolerance params["cutOffType"] = cutOffType method = RIP else: #if algo == "RB": params = {'R':(MN + 2 + Delta) * (MN + 1 + Delta) // 2, 'S':R, 'POD':True, 'PODTolerance':PODTol} if samples == "standard": method = RB elif samples == "centered": method = RB elif samples == "pivoted": raise # params['R'] = SPivot # params['S'] = SPivot # params['SMarginal'] = SMarginal # params['MMarginal'] = MMarginal # params['polybasisMarginal'] = "MONOMIAL" + radialM # params['radialDirectionalWeightsMarginal'] = radialWeightM # params['matchingWeight'] = matchingWeight # params['cutOffTolerance'] = cutOffTolerance # params["cutOffType"] = cutOffType # method = RBP if samples == "standard": if sampling == "quadrature": params['sampler'] = QS(murange, "CHEBYSHEV") # params['sampler'] = QS(murange, "GAUSSLEGENDRE") # params['sampler'] = QS(murange, "UNIFORM") params["S"] = STensorized elif sampling == "quadrature_total": params['sampler'] = QST(murange, "CHEBYSHEV") else: # if sampling == "random": params['sampler'] = RS(murange, "HALTON") elif samples == "centered": params['sampler'] = MS(murange, points = [mu0]) elif samples == "pivoted": if sampling == "quadrature": params['samplerPivot'] = QS([murange[0][0], murange[1][0]], "CHEBYSHEV") # params['samplerPivot'] = QS([murange[0][0], murange[1][0]], "GAUSSLEGENDRE") # params['samplerPivot'] = QS([murange[0][0], murange[1][0]], "UNIFORM") elif sampling == "quadrature_total": params['samplerPivot'] = QST([murange[0][0], murange[1][0]], "CHEBYSHEV") else: # if sampling == "random": params['samplerPivot'] = RS([murange[0][0], murange[1][0]], "HALTON") if samplingM == "quadrature": params['samplerMarginal'] = QS([murange[0][1], murange[1][1]], "UNIFORM") elif samplingM == "quadrature_total": params['samplerMarginal'] = QST([murange[0][1], murange[1][1]], "CHEBYSHEV") else: # if samplingM == "random": params['samplerMarginal'] = RS([murange[0][1], murange[1][1]], "HALTON") if samples != "pivoted": approx = method(solver, mu0 = mu0, approxParameters = params, verbosity = verb) else: approx = method(solver, mu0 = mu0, directionPivot = [0], approxParameters = params, verbosity = verb) approx.setupApprox() if show_sample: L = mutar[1] approx.plotApprox(mutar, name = 'u_app', what = "REAL") approx.plotHF(mutar, name = 'u_HF', what = "REAL") approx.plotErr(mutar, name = 'err', what = "REAL") # approx.plotRes(mutar, name = 'res', 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))) try: from plot_zero_set import plotZeroSet2 muZeroVals, Qvals = plotZeroSet2(murange, murangeEff, approx, mu0, 200, [1., 1], polesImTol = 2.) except: pass if show_norm: from plot_inf_set import plotInfSet2 muInfVals, normEx, normApp, normRes, normErr, beta = plotInfSet2( murange, murangeEff, approx, mu0, 50, [1., 1.], relative = False, nobeta = True) print(1.j * approx.getPoles([None, 50.])) diff --git a/examples/2d/pod/membrane_fracture_engine.py b/examples/2d/pod/membrane_fracture_engine.py new file mode 120000 index 0000000..9e382c7 --- /dev/null +++ b/examples/2d/pod/membrane_fracture_engine.py @@ -0,0 +1 @@ +/home/pradovera/Desktop/Repos/RROMPy/tests/hfengines/membrane_fracture_engine.py \ No newline at end of file diff --git a/examples/2d/pod/membrane_fracture_nodomain_engine.py b/examples/2d/pod/membrane_fracture_nodomain_engine.py new file mode 120000 index 0000000..45ce4c8 --- /dev/null +++ b/examples/2d/pod/membrane_fracture_nodomain_engine.py @@ -0,0 +1 @@ +/home/pradovera/Desktop/Repos/RROMPy/tests/hfengines/membrane_fracture_nodomain_engine.py \ No newline at end of file diff --git a/examples/2d/pod/square_pod.py b/examples/2d/pod/square_pod.py index c089941..1999b22 100644 --- a/examples/2d/pod/square_pod.py +++ b/examples/2d/pod/square_pod.py @@ -1,190 +1,190 @@ import numpy as np -from rrompy.hfengines.linear_problem.bidimensional import \ +from helmholtz_square_domain_problem_engine import \ HelmholtzSquareDomainProblemEngine as HSDPE from rrompy.reduction_methods.standard import RationalInterpolant as RI from rrompy.reduction_methods.standard import ReducedBasis as RB from rrompy.reduction_methods.pivoted import RationalInterpolantPivoted as RIP #from rrompy.reduction_methods.pivoted import ReducedBasisPivoted as RBP from rrompy.parameter.parameter_sampling import (QuadratureSampler as QS, QuadratureSamplerTotal as QST, ManualSampler as MS, RandomSampler as RS) verb = 5 size = 6 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 STensorized = (MN + 1) ** 2 PODTol = 1e-6 SPivot = MN + 1 SMarginal = 4 MMarginal = SMarginal - 1 samples = "centered" samples = "standard" samples = "pivoted" algo = "rational" #algo = "RB" sampling = "quadrature" sampling = "quadrature_total" #sampling = "random" samplingM = "quadrature" #samplingM = "quadrature_total" #samplingM = "random" if samples == "pivoted": radialM = "" radialM = "_gaussian" # radialM = "_thinplate" # radialM = "_multiquadric" rMW0 = 2. radialWeightM = [rMW0] cutOffTolerance = 1. * np.inf cutOffType = "MAGNITUDE" matchingWeight = 10. 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.#1 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 rescalingExp = [2., 1.] if algo == "rational": params = {'N':MN, 'M':MN, 'S':R, 'POD':True} if samples == "standard": params['polybasis'] = "CHEBYSHEV" # params['polybasis'] = "LEGENDRE" # params['polybasis'] = "MONOMIAL" method = RI elif samples == "centered": params['polybasis'] = "MONOMIAL" method = RI elif samples == "pivoted": params['S'] = SPivot params['SMarginal'] = SMarginal params['MMarginal'] = MMarginal params['polybasisPivot'] = "CHEBYSHEV" params['polybasisMarginal'] = "MONOMIAL" + radialM params['matchingWeight'] = matchingWeight params['radialDirectionalWeightsMarginal'] = radialWeightM params['cutOffTolerance'] = cutOffTolerance params["cutOffType"] = cutOffType method = RIP else: #if algo == "RB": params = {'R':R, 'S':R, 'POD':True} if samples in ["standard", "centered"]: method = RB elif samples == "pivoted": raise # params['S'] = SPivot # params['SMarginal'] = SMarginal # params['MMarginal'] = MMarginal # params['polybasisMarginal'] = "MONOMIAL" + radialM # params['matchingWeight'] = matchingWeight # params['radialDirectionalWeightsMarginal'] = radialWeightM # params['cutOffTolerance'] = cutOffTolerance # params["cutOffType"] = cutOffType # method = RBP if samples == "standard": if sampling == "quadrature": params['sampler'] = QS(murange, "CHEBYSHEV", scalingExp = rescalingExp) # params['sampler'] = QS(murange, "GAUSSLEGENDRE", scalingExp = rescalingExp) # params['sampler'] = QS(murange, "UNIFORM", scalingExp = rescalingExp) params['S'] = STensorized elif sampling == "quadrature_total": params['sampler'] = QST(murange, "CHEBYSHEV", scalingExp = rescalingExp) else: # if sampling == "random": params['sampler'] = RS(murange, "HALTON", scalingExp = rescalingExp) elif samples == "centered": params['sampler'] = MS(murange, points = [mu0], scalingExp = rescalingExp) elif samples == "pivoted": if sampling == "quadrature": params['samplerPivot'] = QS([murange[0][0], murange[1][0]], "CHEBYSHEV") # params['samplerPivot'] = QS([murange[0][0], murange[1][0]], "GAUSSLEGENDRE") # params['samplerPivot'] = QS([murange[0][0], murange[1][0]], "UNIFORM") elif sampling == "quadrature_total": params['samplerPivot'] = QST([murange[0][0], murange[1][0]], "CHEBYSHEV") else: # if sampling == "random": params['samplerPivot'] = RS([murange[0][0], murange[1][0]], "HALTON") if samplingM == "quadrature": params['samplerMarginal'] = QS([murange[0][1], murange[1][1]], "UNIFORM") elif samplingM == "quadrature_total": params['samplerMarginal'] = QST([murange[0][1], murange[1][1]], "CHEBYSHEV") else: # if samplingM == "random": params['samplerMarginal'] = RS([murange[0][1], murange[1][1]], "HALTON") if samples != "pivoted": approx = method(solver, mu0 = mu0, approxParameters = params, verbosity = verb) else: approx = method(solver, mu0 = mu0, directionPivot = [0], 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 plotZeroSet2 muZeroVals, Qvals = plotZeroSet2(murange, murangeEff, approx, mu0, 200, [2., 1.]) if show_norm: from plot_inf_set import plotInfSet2 muInfVals, normEx, normApp, normRes, normErr, beta = plotInfSet2( murange, murangeEff, approx, mu0, 20, [2., 1.], clip = clip, relative = True, nobeta = True) diff --git a/examples/2d/pod/square_pod_hermite.py b/examples/2d/pod/square_pod_hermite.py index e3d5851..9fc6517 100644 --- a/examples/2d/pod/square_pod_hermite.py +++ b/examples/2d/pod/square_pod_hermite.py @@ -1,95 +1,95 @@ import numpy as np -from rrompy.hfengines.linear_problem.bidimensional import \ +from helmholtz_square_domain_problem_engine import \ HelmholtzSquareDomainProblemEngine as HSDPE from rrompy.reduction_methods.standard import RationalInterpolant as RI from rrompy.reduction_methods.standard import ReducedBasis as RB 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 MN0 = 2 R0 = (MN0 + 2) * (MN0 + 1) // 2 S0Tensorized = (MN0 + 1) ** 2 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) rescalingExp = [2., 1.] if algo == "rational": params = {'N':MN, 'M':MN, 'S':R, 'POD':True} params['polybasis'] = "CHEBYSHEV" method = RI else: #if algo == "RB": params = {'R':R, 'S':R, 'POD':True} method = RB if sampling == "quadrature": sampler0 = QS(murange, "CHEBYSHEV", scalingExp = rescalingExp) S0 = S0Tensorized else: # if sampling == "random": sampler0 = RS(murange, "SOBOL", scalingExp = rescalingExp) S0 = R0 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 plotZeroSet2 muZeroVals, Qvals = plotZeroSet2(murange, murangeEff, approx, mu0, 200, [2., 2.]) if show_norm: 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 0b3bde9..5dca5e5 100644 --- a/examples/2d/pod/square_simplified_pod.py +++ b/examples/2d/pod/square_simplified_pod.py @@ -1,120 +1,120 @@ import numpy as np -from rrompy.hfengines.linear_problem.bidimensional import \ +from helmholtz_square_simplified_domain_problem_engine import \ HelmholtzSquareSimplifiedDomainProblemEngine as HSSDPE from rrompy.reduction_methods.standard import RationalInterpolant as RI from rrompy.reduction_methods.standard import ReducedBasis as RB 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 STensorized = (MN + 1) ** 2 samples = "centered" samples = "standard" 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) rescalingExp = [2.] * 2 if algo == "rational": params = {'N':MN, 'M':MN, 'S':R, 'POD':True} if samples == "standard": params['polybasis'] = "CHEBYSHEV" params['polybasis'] = "LEGENDRE" params['polybasis'] = "MONOMIAL" elif samples == "centered": params['polybasis'] = "MONOMIAL" method = RI else: #if algo == "RB": params = {'R':R, 'S':R, 'POD':True} method = RB if samples == "standard": if sampling == "quadrature": params['sampler'] = QS(murange, "CHEBYSHEV", scalingExp = rescalingExp) # params['sampler'] = QS(murange, "GAUSSLEGENDRE", scalingExp = rescalingExp) # params['sampler'] = QS(murange, "UNIFORM", scalingExp = rescalingExp) params['S'] = STensorized elif sampling == "quadrature_total": params['sampler'] = QST(murange, "CHEBYSHEV", scalingExp = rescalingExp) else: # if sampling == "random": params['sampler'] = RS(murange, "HALTON", scalingExp = rescalingExp) elif samples == "centered": params['sampler'] = MS(murange, points = [mu0], scalingExp = rescalingExp) 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 plotZeroSet2 muZeroVals, Qvals = plotZeroSet2(murange, murangeEff, approx, mu0, 200, [2., 2.]) if show_norm: 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 69b0bb4..5c6e302 100644 --- a/examples/2d/pod/square_simplified_pod_hermite.py +++ b/examples/2d/pod/square_simplified_pod_hermite.py @@ -1,95 +1,95 @@ import numpy as np -from rrompy.hfengines.linear_problem.bidimensional import \ +from helmholtz_square_simplified_domain_problem_engine import \ HelmholtzSquareSimplifiedDomainProblemEngine as HSSDPE from rrompy.reduction_methods.standard import RationalInterpolant as RI from rrompy.reduction_methods.standard import ReducedBasis as RB 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 MN0 = 2 R0 = (MN0 + 2) * (MN0 + 1) // 2 S0Tensorized = (MN0 + 1) ** 2 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) rescalingExp = [2.] * 2 if algo == "rational": params = {'N':MN, 'M':MN, 'S':R, 'POD':True} params['polybasis'] = "CHEBYSHEV" method = RI else: #if algo == "RB": params = {'R':R, 'S':R, 'POD':True} method = RB if sampling == "quadrature": sampler0 = QS(murange, "CHEBYSHEV", scalingExp = rescalingExp) S0 = S0Tensorized else: # if sampling == "random": sampler0 = RS(murange, "SOBOL", scalingExp = rescalingExp) S0 = R0 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 plotZeroSet2 muZeroVals, Qvals = plotZeroSet2(murange, murangeEff, approx, mu0, 200, [2., 2.]) if show_norm: 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/synthetic_bivariate_engine.py b/examples/2d/pod/synthetic_bivariate_engine.py new file mode 120000 index 0000000..989399c --- /dev/null +++ b/examples/2d/pod/synthetic_bivariate_engine.py @@ -0,0 +1 @@ +/home/pradovera/Desktop/Repos/RROMPy/examples/2d/base/synthetic_bivariate_engine.py \ No newline at end of file diff --git a/examples/2d/pod/synthetic_pod.py b/examples/2d/pod/synthetic_pod.py index 074169d..92d6d3c 100644 --- a/examples/2d/pod/synthetic_pod.py +++ b/examples/2d/pod/synthetic_pod.py @@ -1,129 +1,128 @@ import numpy as np -from rrompy.hfengines.linear_problem.bidimensional import \ - SyntheticBivariateEngine as SBE +from synthetic_bivariate_engine import SyntheticBivariateEngine as SBE from rrompy.reduction_methods.standard import RationalInterpolant as RI from rrompy.reduction_methods.standard import ReducedBasis as RB 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 clip = -1 #clip = .4 #clip = .6 Delta = 0 MN = 10 R = (MN + 2) * (MN + 1) // 2 STensorized = (MN + 1) ** 2 PODTol = 1e-6 samples = "centered" samples = "standard" algo = "rational" #algo = "RB" sampling = "quadrature" #sampling = "quadrature_total" #sampling = "random" if samples == "standard": radial = "" radial = "_gaussian" # radial = "_thinplate" # radial = "_multiquadric" rW0 = 10. radialWeight = [rW0] * 2 if size == 1: # small 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] + bEff*murange[1][1]], [(aEff*murange[1][0]**2. + bEff*murange[0][0]**2.) ** .5, aEff*murange[1][1] + bEff*murange[0][1]]] kappa = 20. ** .5 theta = - np.pi / 6. n = 20 L = np.pi solver = SBE(kappa = kappa, theta = theta, n = n, L = L, mu0 = mu0, verbosity = verb) rescalingExp = [2.] * 2 if algo == "rational": params = {'N':MN, 'M':MN + Delta, 'S':R, 'POD':True} if samples == "standard": params['polybasis'] = "CHEBYSHEV" + radial # params['polybasis'] = "LEGENDRE" + radial # params['polybasis'] = "MONOMIAL" + radial params['radialDirectionalWeights'] = radialWeight method = RI elif samples == "centered": params['polybasis'] = "MONOMIAL" method = RI else: #if algo == "RB": params = {'R':(MN + 2 + Delta) * (MN + 1 + Delta) // 2, 'S':R, 'POD':True, 'PODTolerance':PODTol} method = RB if samples == "standard": if sampling == "quadrature": params['sampler'] = QS(murange, "CHEBYSHEV", scalingExp = rescalingExp) # params['sampler'] = QS(murange, "GAUSSLEGENDRE", scalingExp = rescalingExp) # params['sampler'] = QS(murange, "UNIFORM", scalingExp = rescalingExp) params['S'] = STensorized elif sampling == "quadrature_total": params['sampler'] = QST(murange, "CHEBYSHEV", scalingExp = rescalingExp) else: # if sampling == "random": params['sampler'] = RS(murange, "HALTON", scalingExp = rescalingExp) elif samples == "centered": params['sampler'] = MS(murange, points = [mu0], scalingExp = rescalingExp) approx = method(solver, mu0 = mu0, approxParameters = params, verbosity = verb) approx.setupApprox() if show_sample: L = mutar[1] approx.plotApprox(mutar, name = 'u_app', what = "REAL") approx.plotHF(mutar, name = 'u_HF', what = "REAL") approx.plotErr(mutar, name = 'err', what = "REAL") # approx.plotRes(mutar, name = 'res', 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" 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: 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/examples/3d/base/fracture3.py b/examples/3d/base/fracture3.py index 13b6a32..7c96293 100644 --- a/examples/3d/base/fracture3.py +++ b/examples/3d/base/fracture3.py @@ -1,35 +1,34 @@ -from rrompy.hfengines.linear_problem.tridimensional import \ - MembraneFractureEngine3 as MFE +from membrane_fracture_engine3 import MembraneFractureEngine3 as MFE from fracture3_warping import fracture3_warping verb = 100 mu0 = [45. ** .5, .7, .075] 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() 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(mu0, uh)[0], name = 'res', # what = 'REAL', figsize = (8, 5)) #solver.outParaviewTimeDomain(uh, mu0[0], filename = 'out', folder = True, # forceNewFile = False) warps = fracture3_warping(solver.V.mesh(), L, mu0[1], delta, mu0[2]) #solver.plotmesh(warps, figsize = (7.5, 4.5)) #solver.plot(u0, warps, what = 'REAL', figsize = (8, 5)) solver.plot(uh, warps, what = 'REAL', figsize = (8, 5)) #solver.plot(solver.residual(mu0, uh)[0], warps, name = 'res', # what = 'REAL', figsize = (8, 5)) #solver.outParaviewTimeDomain(uh, mu0[0], warps, filename = 'outW', # folder = True, forceNewFile = False) diff --git a/examples/3d/base/matrix_3.py b/examples/3d/base/matrix_3.py index 6f885e6..a5ba885 100644 --- a/examples/3d/base/matrix_3.py +++ b/examples/3d/base/matrix_3.py @@ -1,77 +1,80 @@ import scipy.sparse as sp import numpy as np -from rrompy.hfengines.base import MatrixEngineBase as MEB +from rrompy.hfengines.base import LinearAffineEngine, NumpyEngineBase verb = 100 deg = 2 +class MatrixEngineBase(LinearAffineEngine, NumpyEngineBase): + pass + N = 150 exp = 1.05 assert exp > 1. empty = sp.csr_matrix((np.zeros(0), np.zeros(0), np.zeros(N + 1)), shape = (N, N)) -solver = MEB(verbosity = verb) +solver = MatrixEngineBase(verbosity = verb) solver.npar = 3 A0 = sp.spdiags([np.arange(1, 1 + 2 * N, 2) ** exp - (2 * (N // 4)) ** exp], [0], N, N) if deg == 1: solver.nAs = 4 solver.As = [A0, sp.eye(N), - sp.eye(N), - sp.eye(N)] elif deg == 2: solver.nAs = 7 solver.As = ([A0] + [empty] + [- sp.eye(N)] + [empty] * 3 + [sp.eye(N)]) elif deg == 3: solver.nAs = 13 solver.As = ([A0] + [empty] + [- sp.eye(N)] + [empty] * 9 + [sp.eye(N)]) np.random.seed(420) solver.nbs = 1 solver.bs = [np.random.randn(N) + 1.j * np.random.randn(N)] solver._affinePoly = True solver.thAs = solver.getMonomialWeights(solver.nAs) solver.thbs = solver.getMonomialWeights(solver.nbs) mu0 = [.8, 0., .2] uh = solver.solve(mu0)[0] solver.plot(uh, what = 'REAL', figsize = (8, 5)) murange = np.linspace(-1., 1., 20) Mu1, Mu2, Mu3 = np.meshgrid(murange, murange, murange, indexing = 'ij') mus = [] if deg == 1: f = Mu1 - Mu2 - Mu3 elif deg == 2: f = Mu1 * Mu3 - Mu2 elif deg == 3: f = Mu3 * Mu1**2 - Mu2 from matplotlib import pyplot as plt pts = [] for jdir in range(len(Mu3)): z = Mu3[0, 0, jdir] xis, yis, fis = Mu1[:, :, jdir], Mu2[:, :, jdir], f[:, :, jdir] plt.figure() CS = plt.contour(xis, yis, np.real(fis), levels = [0.]) plt.close() for i, CSc in enumerate(CS.allsegs): if np.isclose(CS.cvalues[i], 0.): for j, CScj in enumerate(CSc): for k in range(len(CScj)): pts += [[CScj[k, 0], CScj[k, 1], z]] pts += [[np.nan] * 3] pts = np.array(pts) from mpl_toolkits.mplot3d import Axes3D fig = plt.figure(figsize = (8, 6)) ax = Axes3D(fig) ax.plot(pts[:, 0], pts[:, 1], pts[:, 2], 'k-') ax.set_xlim(-1., 1.) ax.set_ylim(-1., 1.) ax.set_zlim(-1., 1.) ax.set_xlabel('x') ax.set_ylabel('y') ax.set_zlabel('z') plt.show() plt.close() diff --git a/rrompy/hfengines/linear_problem/tridimensional/membrane_fracture_engine3.py b/examples/3d/base/membrane_fracture_engine3.py similarity index 94% rename from rrompy/hfengines/linear_problem/tridimensional/membrane_fracture_engine3.py rename to examples/3d/base/membrane_fracture_engine3.py index 1c3ca4a..29db01c 100644 --- a/rrompy/hfengines/linear_problem/tridimensional/membrane_fracture_engine3.py +++ b/examples/3d/base/membrane_fracture_engine3.py @@ -1,265 +1,262 @@ # Copyright (C) 2018 by the RROMPy authors # # This file is part of RROMPy. # # RROMPy is free software: you can redistribute it and/or modify # it under the terms of the GNU Lesser General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # RROMPy is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU Lesser General Public License for more details. # # You should have received a copy of the GNU Lesser General Public License # along with RROMPy. If not, see . # import numpy as np import fenics as fen import ufl from rrompy.utilities.base.types import paramVal from rrompy.solver.fenics import fenZERO, fenONE from rrompy.hfengines.linear_problem.helmholtz_problem_engine import ( HelmholtzProblemEngine) from rrompy.utilities.base import verbosityManager as vbMng from rrompy.utilities.numerical import (nextDerivativeIndices, hashDerivativeToIdx as hashD) from rrompy.solver.fenics import fenics2Sparse -__all__ = ['MembraneFractureEngine3'] - def gen_mesh_fracture(W, delta, H, n): n = 2 * (n // 2) + 1 xL = np.linspace(-.5 * W, - .5 * delta, n // 2 + 1) xR = - xL[::-1] nEff = int(np.ceil(delta / (xL[1] - xL[0]))) xC = np.linspace(-.5 * delta, .5 * delta, nEff + 1)[1:-1] yA = np.linspace(-.5 * H, .5 * H, n) yL = np.linspace(-.5 * H, 0., n // 2 + 1) editor = fen.MeshEditor() mesh = fen.Mesh() editor.open(mesh, "triangle", 2, 2) editor.init_vertices((2 * n + nEff - 1) * (n // 2 + 1)) editor.init_cells(2 * (2 * n - 2 + nEff) * (n // 2)) for j, y in enumerate(yA): for i, x in enumerate(xL): editor.add_vertex(i + j * (n // 2 + 1), np.array([x, y])) for j in range(n - 1): for i in range(n // 2): editor.add_cell(2 * (i + j * (n // 2)), np.array([i + j * (n // 2 + 1), i + 1 + j * (n // 2 + 1), i + (j + 1) * (n // 2 + 1)], dtype=np.uintp)) editor.add_cell(2 * (i + j * (n // 2)) + 1, np.array([i + 1 + j * (n // 2 + 1), i + 1 + (j + 1) * (n // 2 + 1), i + (j + 1) * (n // 2 + 1)], dtype=np.uintp)) vBase1, cBase1 = n * (n // 2 + 1), 2 * (n - 1) * (n // 2) for j, y in enumerate(yA): for i, x in enumerate(xR): editor.add_vertex(vBase1 + i + j * (n // 2 + 1), np.array([x, y])) for j in range(n - 1): for i in range(n // 2): editor.add_cell(cBase1 + 2 * (i + j * (n // 2)), np.array([vBase1 + i + j * (n // 2 + 1), vBase1 + i + 1 + j * (n // 2 + 1), vBase1 + i + (j + 1) * (n // 2 + 1)], dtype=np.uintp)) editor.add_cell(cBase1 + 2 * (i + j * (n // 2)) + 1, np.array([vBase1 + i + 1 + j * (n // 2 + 1), vBase1 + i + 1 + (j + 1) * (n // 2 + 1), vBase1 + i + (j + 1) * (n // 2 + 1)], dtype=np.uintp)) vBase2, cBase2 = 2 * n * (n // 2 + 1), 4 * (n - 1) * (n // 2) for j, y in enumerate(yL): for i, x in enumerate(xC): editor.add_vertex(vBase2 + i + j * (nEff - 1), np.array([x, y])) for j in range(n // 2): for i in range(nEff - 2): editor.add_cell(cBase2 + 2 * (i + j * (nEff - 2)), np.array([vBase2 + i + j * (nEff - 1), vBase2 + i + 1 + j * (nEff - 1), vBase2 + i + (j + 1) * (nEff - 1)], dtype=np.uintp)) editor.add_cell(cBase2 + 2 * (i + j * (nEff - 2)) + 1, np.array([vBase2 + i + 1 + j * (nEff - 1), vBase2 + i + 1 + (j + 1) * (nEff - 1), vBase2 + i + (j + 1) * (nEff - 1)], dtype=np.uintp)) if nEff == 1: for j in range(n // 2): editor.add_cell(cBase2 + 2 * j, np.array([(j + 1) * (n // 2 + 1) - 1, vBase1 + j * (n // 2 + 1), (j + 2) * (n // 2 + 1) - 1], dtype=np.uintp)) editor.add_cell(cBase2 + 2 * j + 1, np.array([vBase1 + j * (n // 2 + 1), vBase1 + (j + 1) * (n // 2 + 1), (j + 2) * (n // 2 + 1) - 1], dtype=np.uintp)) else: cBase3 = 2 * (2 * n + nEff - 4) * (n // 2) for j in range(n // 2): editor.add_cell(cBase3 + 2 * j, np.array([(j + 1) * (n // 2 + 1) - 1, vBase2 + j * (nEff - 1), (j + 2) * (n // 2 + 1) - 1], dtype=np.uintp)) editor.add_cell(cBase3 + 2 * j + 1, np.array([vBase2 + j * (nEff - 1), vBase2 + (j + 1) * (nEff - 1), (j + 2) * (n // 2 + 1) - 1], dtype=np.uintp)) cBase4 = 2 * (2 * n + nEff - 3) * (n // 2) for j in range(n // 2): editor.add_cell(cBase4 + 2 * j, np.array([vBase2 + (j + 1) * (nEff - 1) - 1, vBase1 + j * (n // 2 + 1), vBase2 + (j + 2) * (nEff - 1) - 1], dtype=np.uintp)) editor.add_cell(cBase4 + 2 * j + 1, np.array([vBase1 + j * (n // 2 + 1), vBase1 + (j + 1) * (n // 2 + 1), vBase2 + (j + 2) * (nEff - 1) - 1], dtype=np.uintp)) editor.close() return mesh class MembraneFractureEngine3(HelmholtzProblemEngine): def __init__(self, mu0 : paramVal = [20. ** .5, .6, .08], H : float = 1., L : float = .75, delta : float = .05, n : int = 50, - degree_threshold : int = np.inf, homogeneized : bool = False, - verbosity : int = 10, timestamp : bool = True): + degree_threshold : int = np.inf, verbosity : int = 10, + timestamp : bool = True): super().__init__(mu0 = mu0, degree_threshold = degree_threshold, - homogeneized = homogeneized, verbosity = verbosity, - timestamp = timestamp) + verbosity = verbosity, timestamp = timestamp) self._affinePoly = False self.nAs = 6 self.npar = 3 self._H = H self._delta = delta self._L = L self._W = 2 * L + delta self.rescalingExp = [2., 1., 1.] mesh = gen_mesh_fracture(self._W, delta, H, n) self.V = fen.FunctionSpace(mesh, "P", 1) self.NeumannBoundary = lambda x, on_b: (on_b and x[1] >= - H / 4. and x[0] >= - .5 * delta and x[0] <= .5 * delta) self.DirichletBoundary = "REST" x, y = fen.SpatialCoordinate(mesh)[:] self._aboveIndicator = ufl.conditional(ufl.gt(y, 0.), fenONE, fenZERO) self._belowCenterIndicator = ufl.conditional( ufl.And(ufl.ge(x, - .5 * delta), ufl.le(x, .5 * delta)), fenONE, fenZERO) self._belowSidesIndicator = (fenONE - self._aboveIndicator - self._belowCenterIndicator) self.DirichletDatum = [fen.exp(- 10. * (H / 2. + y) / H - .5 * ((x + .4 * L + .5 * delta) / (.1 * L)) ** 2. ) * self._belowSidesIndicator, fenZERO] def buildA(self): """Build terms of operator of linear system.""" if self.As[1] is None or self.As[2] is None or self.As[5] is None: thy2 = [(1.,), ('x', '()', 1, '*', 4., '-', 2. * self._H), ('x', '()', 1, '**', 2., '*', 6., '-', ('x', '()', 1, '*', 6. * self._H), '+', self._H ** 2.), ('x', '()', 1, '**', 2., '*', 4., '-', ('x', '()', 1, '*', 3. * self._H), '+', self._H ** 2., '*', ('x', '()', 1), '*', 2.), ('x', '()', 1, '*', -1., '+', self._H, '*', ('x', '()', 1), '**', 2.)] if self.As[3] is None or self.As[4] is None or self.As[5] is None: thz2 = [(1.,), ('x', '()', 2, '*', 4., '-', 2. * self._W), ('x', '()', 2, '**', 2., '*', 6., '-', ('x', '()', 2, '*', 6. * self._W), '+', self._W ** 2.), ('x', '()', 2, '**', 2., '*', 4., '-', ('x', '()', 2, '*', 3. * self._W), '+', self._W ** 2., '*', ('x', '()', 2), '*', 2.), ('x', '()', 2, '*', -1., '+', self._W, '*', ('x', '()', 2), '**', 2.)] if self.As[0] is None: vbMng(self, "INIT", "Assembling operator term A0.", 20) DirichletBC0 = fen.DirichletBC(self.V, fenZERO, self.DirichletBoundary) a0Re = fenZERO * fen.dot(self.u, self.v) * fen.dx self.As[0] = fenics2Sparse(a0Re, {}, DirichletBC0, 1) self.thAs[0] = self.getMonomialSingleWeight([0]) vbMng(self, "DEL", "Done assembling operator term.", 20) if self.As[1] is None: vbMng(self, "INIT", "Assembling operator term A1.", 20) DirichletBC0 = fen.DirichletBC(self.V, fenZERO, self.DirichletBoundary) ajRe = ((self._aboveIndicator + self._belowSidesIndicator) * 16. * self._L ** 2. * self.u.dx(0) * self.v.dx(0) * fen.dx) self.As[1] = fenics2Sparse(ajRe, {}, DirichletBC0, 0) thz0 = [(1.,), ('x', '()', 2, '*', 2.), ('x', '()', 2, '**', 2.)] self.thAs[1] = [] idxs = nextDerivativeIndices([], 3, hashD([0, 4, 2]) + 1) for idx in idxs: dy, dz = 4 - idx[1], 2 - idx[2] if idx[0] == 0 and dy >= 0 and dz >= 0: - self.thAs[1] += [thy2[dy] + ('*') + (thz0[dz],)] + self.thAs[1] += [thy2[dy] + ('*',) + (thz0[dz],)] else: self.thAs[1] += [(0.,)] self.thAs[1] += [None] vbMng(self, "DEL", "Done assembling operator term.", 20) if self.As[2] is None: vbMng(self, "INIT", "Assembling operator term A2.", 20) DirichletBC0 = fen.DirichletBC(self.V, fenZERO, self.DirichletBoundary) ajRe = (4. * self._delta ** 2. * self._belowCenterIndicator * self.u.dx(0) * self.v.dx(0) * fen.dx) self.As[2] = fenics2Sparse(ajRe, {}, DirichletBC0, 0) thz1 = [(1.,), ('x', '()', 2, '*', 2., '-', 2. * self._W), ('x', '()', 2, '*', -1., '+', self._W, '**', 2.)] self.thAs[2] = [] idxs = nextDerivativeIndices([], 3, hashD([0, 4, 2]) + 1) for idx in idxs: dy, dz = 4 - idx[1], 2 - idx[2] if idx[0] == 0 and dy >= 0 and dz >= 0: - self.thAs[2] += [thy2[dy] + ('*') + (thz1[dz],)] + self.thAs[2] += [thy2[dy] + ('*',) + (thz1[dz],)] else: self.thAs[2] += [(0.,)] self.thAs[2] += [None] vbMng(self, "DEL", "Done assembling operator term.", 20) if self.As[3] is None: vbMng(self, "INIT", "Assembling operator term A3.", 20) DirichletBC0 = fen.DirichletBC(self.V, fenZERO, self.DirichletBoundary) ajRe = (self._H ** 2. * self._aboveIndicator * self.u.dx(1) * self.v.dx(1) * fen.dx) self.As[3] = fenics2Sparse(ajRe, {}, DirichletBC0, 0) thy1 = [(1.,), ('x', '()', 1, '*', 2., '-', 2. * self._H), (self._H, '-', ('x', '()', 1), '**', 2.)] self.thAs[3] = [] idxs = nextDerivativeIndices([], 3, hashD([0, 2, 4]) + 1) for idx in idxs: dy, dz = 2 - idx[1], 4 - idx[2] if idx[0] == 0 and dy >= 0 and dz >= 0: - self.thAs[3] += [thy1[dy] + ('*') + (thz2[dz],)] + self.thAs[3] += [thy1[dy] + ('*',) + (thz2[dz],)] else: self.thAs[3] += [(0.,)] self.thAs[3] += [None] vbMng(self, "DEL", "Done assembling operator term.", 20) if self.As[4] is None: vbMng(self, "INIT", "Assembling operator term A4.", 20) DirichletBC0 = fen.DirichletBC(self.V, fenZERO, self.DirichletBoundary) ajRe = ((self._belowSidesIndicator + self._belowCenterIndicator) * self._H ** 2. * self.u.dx(1) * self.v.dx(1) * fen.dx) self.As[4] = fenics2Sparse(ajRe, {}, DirichletBC0, 0) thy0 = [(1.,), ('x', '()', 1, '*', 2.), ('x', '()', 1, '**', 2.)] self.thAs[4] = [] idxs = nextDerivativeIndices([], 3, hashD([0, 2, 4]) + 1) for idx in idxs: dy, dz = 2 - idx[1], 4 - idx[2] if idx[0] == 0 and dy >= 0 and dz >= 0: - self.thAs[4] += [thy0[dy] + ('*') + (thz2[dz],)] + self.thAs[4] += [thy0[dy] + ('*',) + (thz2[dz],)] else: self.thAs[4] += [(0.,)] self.thAs[4] += [None] vbMng(self, "DEL", "Done assembling operator term.", 20) if self.As[5] is None: vbMng(self, "INIT", "Assembling operator term A5.", 20) DirichletBC0 = fen.DirichletBC(self.V, fenZERO, self.DirichletBoundary) ajRe = - 4. * self.u * self.v * fen.dx self.As[5] = fenics2Sparse(ajRe, {}, DirichletBC0, 0) thx = [(1.,), ('x', '()', 0)] self.thAs[5] = [] idxs = nextDerivativeIndices([], 3, hashD([1, 4, 4]) + 1) for idx in idxs: dx, dy, dz = 1 - idx[0], 4 - idx[1], 4 - idx[2] if dx >= 0 and dy >= 0 and dz >= 0: - self.thAs[5] += [thx[dx] + ('*') + (thy2[dy],) - + ('*') + (thz2[dz],)] + self.thAs[5] += [thx[dx] + ('*',) + (thy2[dy],) + + ('*',) + (thz2[dz],)] else: self.thAs[5] += [(0.,)] self.thAs[5] += [None] vbMng(self, "DEL", "Done assembling operator term.", 20) diff --git a/examples/3d/base/scattering1.py b/examples/3d/base/scattering1.py index f9a0c2d..b9805c4 100644 --- a/examples/3d/base/scattering1.py +++ b/examples/3d/base/scattering1.py @@ -1,29 +1,29 @@ -from rrompy.hfengines.linear_problem.tridimensional import Scattering1d as S1D +from scattering_1d import Scattering1d as S1D import fenics as fen import numpy as np verb = 100 mu0 = [4., np.pi, 0.] mu = [4.5, np.pi, 1.] n = 100 solver = S1D(mu0 = mu0, n = n, verbosity = verb) u0 = solver.liftDirichletData() uh = solver.solve(mu)[0] #solver.plotmesh(figsize = (12, 2)) #solver.plot(u0, what = ['REAL', 'IMAG'], figsize = (12, 4)) print(solver.norm(uh)) #solver.plot(uh, what = ['REAL', 'IMAG'], figsize = (12, 4)) #solver.plot(solver.residual(mu0, uh)[0], name = 'res', # what = ['REAL', 'IMAG'], figsize = (12, 4)) x = fen.SpatialCoordinate(solver.V.mesh()) warps = [x * mu[1] / solver._L - x, x * solver._L / mu[1] - x] #solver.plotmesh(warps, figsize = (12, 2)) #solver.plot(u0, warps, what = ['REAL', 'IMAG'], figsize = (12, 4)) solver.plot(uh, warps, what = ['REAL', 'IMAG'], figsize = (12, 4)) #solver.plot(solver.residual(mu0, uh)[0], warps, name = 'res', # what = ['REAL', 'IMAG'], figsize = (12, 4)) diff --git a/rrompy/hfengines/linear_problem/tridimensional/scattering_1d.py b/examples/3d/base/scattering_1d.py similarity index 91% rename from rrompy/hfengines/linear_problem/tridimensional/scattering_1d.py rename to examples/3d/base/scattering_1d.py index 4d97599..cd7f6ac 100644 --- a/rrompy/hfengines/linear_problem/tridimensional/scattering_1d.py +++ b/examples/3d/base/scattering_1d.py @@ -1,74 +1,71 @@ # Copyright (C) 2018 by the RROMPy authors # # This file is part of RROMPy. # # RROMPy is free software: you can redistribute it and/or modify # it under the terms of the GNU Lesser General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # RROMPy is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU Lesser General Public License for more details. # # You should have received a copy of the GNU Lesser General Public License # along with RROMPy. If not, see . # import numpy as np import fenics as fen from rrompy.utilities.base.types import paramVal from rrompy.solver.fenics import fenZERO, fenONE from rrompy.hfengines.linear_problem.scattering_problem_engine import ( ScatteringProblemEngine) from rrompy.utilities.base import verbosityManager as vbMng from rrompy.solver.fenics import fenics2Sparse -__all__ = ['Scattering1d'] - class Scattering1d(ScatteringProblemEngine): def __init__(self, mu0 : paramVal = [5.5, np.pi, 0.], n : int = 50, - degree_threshold : int = np.inf, homogeneized : bool = False, - verbosity : int = 10, timestamp : bool = True): + degree_threshold : int = np.inf, verbosity : int = 10, + timestamp : bool = True): super().__init__(mu0 = mu0, degree_threshold = degree_threshold, - homogeneized = homogeneized, verbosity = verbosity, - timestamp = timestamp) + verbosity = verbosity, timestamp = timestamp) self._affinePoly = False self.nAs = 3 self.npar = 3 self.rescalingExp = [1., 1., 1.] self._L = np.real(mu0[1]) self.V = fen.FunctionSpace(fen.IntervalMesh(n, 0., self._L), "P", 1) self.RobinBoundary = lambda x, on_b: (on_b and x[0] >= .5 * np.pi) self.DirichletBoundary = "REST" self.DirichletDatum = fenONE def buildA(self): """Build terms of operator of linear system.""" if self.As[0] is None: vbMng(self, "INIT", "Assembling operator term A0.", 20) DirichletBC0 = fen.DirichletBC(self.V, fenZERO, self.DirichletBoundary) a0 = fen.dot(fen.grad(self.u), fen.grad(self.v)) * fen.dx self.As[0] = fenics2Sparse(a0, {}, DirichletBC0, 1) self.thAs[0] = self.getMonomialSingleWeight([0, 0, 0]) vbMng(self, "DEL", "Done assembling operator term.", 20) if self.As[1] is None: self.autoSetDS() vbMng(self, "INIT", "Assembling operator term A1.", 20) DirichletBC0 = fen.DirichletBC(self.V, fenZERO, self.DirichletBoundary) a1 = - self._L ** -1. * fen.dot(self.u, self.v) * self.ds(1) self.As[1] = 1.j * fenics2Sparse(a1, {}, DirichletBC0, 0) self.thAs[1] = self.getMonomialSingleWeight([1, 1, 1]) vbMng(self, "DEL", "Done assembling operator term.", 20) if self.As[2] is None: vbMng(self, "INIT", "Assembling operator term A2.", 20) DirichletBC0 = fen.DirichletBC(self.V, fenZERO, self.DirichletBoundary) a2 = - self._L ** -2. * fen.dot(self.u, self.v) * fen.dx self.As[2] = fenics2Sparse(a2, {}, DirichletBC0, 0) self.thAs[2] = self.getMonomialSingleWeight([2, 2, 0]) vbMng(self, "DEL", "Done assembling operator term.", 20) diff --git a/examples/3d/pod/fracture3_pod.py b/examples/3d/pod/fracture3_pod.py index a4e4b74..eadd736 100644 --- a/examples/3d/pod/fracture3_pod.py +++ b/examples/3d/pod/fracture3_pod.py @@ -1,241 +1,240 @@ import numpy as np from mpl_toolkits.mplot3d import Axes3D from matplotlib import pyplot as plt -from rrompy.hfengines.linear_problem.tridimensional import \ - MembraneFractureEngine3 as MFE +from membrane_fracture_engine3 import MembraneFractureEngine3 as MFE from rrompy.reduction_methods.standard import RationalInterpolant as RI from rrompy.reduction_methods.standard import ReducedBasis as RB from rrompy.reduction_methods.pivoted import RationalInterpolantPivoted as RIP #from rrompy.reduction_methods.pivoted import ReducedBasisPivoted as RBP from rrompy.parameter.parameter_sampling import (QuadratureSampler as QS, QuadratureSamplerTotal as QST, ManualSampler as MS, RandomSampler as RS) verb = 70 size = 4 show_sample = False show_norm = False clip = -1 #clip = .4 #clip = .6 Delta = 0 MN = 5 R = (MN + 3) * (MN + 2) * (MN + 1) // 6 STensorized = (MN + 1) ** 3 PODTol = 1e-8 SPivot = MN + 1 SMarg1d = 5 MMarginal = SMarg1d - 1 SMarginal = (MMarginal + 2) * (MMarginal + 1) // 2 SMarginalTensorized = SMarg1d ** 2 matchingWeight = 10. samples = "centered" samples = "standard" #samples = "pivoted" algo = "rational" #algo = "RB" sampling = "quadrature" #sampling = "quadrature_total" #sampling = "random" samplingM = "quadrature" samplingM = "quadrature_total" #samplingM = "random" polydegreetype = "TOTAL" #polydegreetype = "FULL" if samples == "standard": radial = "" # radial = "_gaussian" # radial = "_thinplate" # radial = "_multiquadric" rW0 = 5. radialWeight = [rW0] * 3 if samples == "pivoted": radial = "" # radial = "_gaussian" # radial = "_thinplate" # radial = "_multiquadric" rW0 = 5. radialWeight = [rW0] radialM = "" # radialM = "_gaussian" # radialM = "_thinplate" # radialM = "_multiquadric" rMW0 = 2. radialWeightM = [rMW0] * 2 assert Delta <= 0 if size == 1: mu0 = [47.5 ** .5, .4, .05] mutar = [50 ** .5, .45, .07] murange = [[40 ** .5, .3, .025], [55 ** .5, .5, .075]] if size == 2: mu0 = [50 ** .5, .3, .02] mutar = [55 ** .5, .4, .03] murange = [[40 ** .5, .1, .005], [60 ** .5, .5, .035]] if size == 3: mu0 = [45 ** .5, .5, .05] mutar = [47 ** .5, .4, .045] murange = [[40 ** .5, .3, .04], [50 ** .5, .7, .06]] if size == 4: mu0 = [45 ** .5, .4, .05] mutar = [47 ** .5, .45, .045] murange = [[40 ** .5, .3, .04], [50 ** .5, .5, .06]] if size == 5: mu0 = [45 ** .5, .5, .05] mutar = [47 ** .5, .45, .045] murange = [[40 ** .5, .3, .04], [50 ** .5, .7, .06]] 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[0][2] + bEff*murange[1][2]], [(aEff*murange[1][0]**2. + bEff*murange[0][0]**2.) ** .5, aEff*murange[1][1] + bEff*murange[0][1], aEff*murange[1][2] + bEff*murange[0][2]]] H = 1. L = .75 delta = .05 n = 50 solver = MFE(mu0 = mu0, H = H, L = L, delta = delta, n = n, verbosity = verb) rescalingExp = [2., 1., 1.] if algo == "rational": params = {'N':MN, 'M':MN + Delta, 'S':R, 'POD':True, 'polydegreetype':polydegreetype} if samples == "standard": params['polybasis'] = "CHEBYSHEV" + radial # params['polybasis'] = "LEGENDRE" + radial # params['polybasis'] = "MONOMIAL" + radial params['radialDirectionalWeights'] = radialWeight method = RI elif samples == "centered": params['polybasis'] = "MONOMIAL" method = RI elif samples == "pivoted": params['S'] = SPivot params['SMarginal'] = SMarginal params['MMarginal'] = MMarginal params['polybasisPivot'] = "CHEBYSHEV" + radial params['polybasisMarginal'] = "MONOMIAL" + radialM params['matchingWeight'] = matchingWeight params['radialDirectionalWeightsPivot'] = radialWeight params['radialDirectionalWeightsMarginal'] = radialWeightM method = RIP else: #if algo == "RB": params = {'R':(MN + 3 + Delta) * (MN + 2 + Delta) * (MN + 1 + Delta) // 6, 'S':R, 'POD':True, 'PODTolerance':PODTol} if samples in ["standard", "centered"]: method = RB elif samples == "pivoted": raise # params['S'] = SPivot # params['SMarginal'] = SMarginal # params['MMarginal'] = MMarginal # params['polybasisMarginal'] = "MONOMIAL" + radialM # params['matchingWeight'] = matchingWeight # params['radialDirectionalWeightsMarginal'] = radialWeightM # method = RBP if samples == "standard": if sampling == "quadrature": params['sampler'] = QS(murange, "CHEBYSHEV", scalingExp = rescalingExp) # params['sampler'] = QS(murange, "GAUSSLEGENDRE",scalingExp = rescalingExp) # params['sampler'] = QS(murange, "UNIFORM", scalingExp = rescalingExp) params['S'] = STensorized elif sampling == "quadrature_total": params['sampler'] = QST(murange, "CHEBYSHEV", scalingExp = rescalingExp) else: # if sampling == "random": params['sampler'] = RS(murange, "HALTON", scalingExp = rescalingExp) elif samples == "centered": params['sampler'] = MS(murange, points = [mu0], scalingExp = rescalingExp) elif samples == "pivoted": if sampling == "quadrature": params['samplerPivot'] = QS([murange[0][0], murange[1][0]], "CHEBYSHEV") # params['samplerPivot'] = QS([murange[0][0], murange[1][0]], "GAUSSLEGENDRE") # params['samplerPivot'] = QS([murange[0][0], murange[1][0]], "UNIFORM") elif sampling == "quadrature_total": params['samplerPivot'] = QST([murange[0][0], murange[1][0]], "CHEBYSHEV") else: # if sampling == "random": params['samplerPivot'] = RS([murange[0][0], murange[1][0]], "HALTON") if samplingM == "quadrature": params['samplerMarginal'] = QS([murange[0][1:], murange[1][1:]], "UNIFORM") params['SMarginal'] = SMarginalTensorized elif samplingM == "quadrature_total": params['samplerMarginal'] = QST([murange[0][1:], murange[1][1:]], "CHEBYSHEV") params['polybasisMarginal'] = "CHEBYSHEV" + radialM else: # if samplingM == "random": params['samplerMarginal'] = RS([murange[0][1:], murange[1][1:]], "HALTON") if samples != "pivoted": approx = method(solver, mu0 = mu0, approxParameters = params, verbosity = verb) else: approx = method(solver, mu0 = mu0, directionPivot = [0], approxParameters = params, verbosity = verb) approx.setupApprox() if show_sample: from fracture3_warping import fracture3_warping warps = fracture3_warping(solver.V.mesh(), L, mutar[1], delta, mutar[2]) approx.plotApprox(mutar, warps, name = 'u_app', what = "REAL") approx.plotHF(mutar, warps, name = 'u_HF', what = "REAL") approx.plotErr(mutar, warps, name = 'err', what = "REAL") # approx.plotRes(mutar, warps, name = 'res', 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))) fig = plt.figure(figsize = (8, 6)) ax = Axes3D(fig) ax.scatter(np.real(approx.mus(0) ** 2.), np.real(approx.mus(1)), np.real(approx.mus(2)), '.') plt.show() plt.close() approx.verbosity = 0 approx.trainedModel.verbosity = 0 from plot_zero_set_3 import plotZeroSet3 #muZeroVals, Qvals = plotZeroSet3(murange, murangeEff, approx, mu0, 200, # [None, mu0[1], mu0[2]], [2., 1., 1.], # clip = clip) #muZeroVals, Qvals = plotZeroSet3(murange, murangeEff, approx, mu0, 200, # [None, None, mu0[2]], [2., 1., 1.], # clip = clip) #muZeroVals, Qvals = plotZeroSet3(murange, murangeEff, approx, mu0, 200, # [None, mu0[1], None], [2., 1., 1.], # clip = clip) muZeroScatter = plotZeroSet3(murange, murangeEff, approx, mu0, 50, [None, None, None], [2., 1., 1.], clip = clip) if show_norm: from plot_inf_set_3 import plotInfSet3 muInfVals, normEx, normApp, normRes, normErr, beta = plotInfSet3( murange, murangeEff, approx, mu0, 25, [None, mu0[1], mu0[2]], [2., 1., 1.], clip = clip, relative = False, normalizeDen = True) muInfVals, normEx, normApp, normRes, normErr, beta = plotInfSet3( murange, murangeEff, approx, mu0, 25, [None, None, mu0[2]], [2., 1., 1.], clip = clip, relative = False, normalizeDen = True) muInfVals, normEx, normApp, normRes, normErr, beta = plotInfSet3( murange, murangeEff, approx, mu0, 25, [None, mu0[1], None], [2., 1., 1.], clip = clip, relative = False, normalizeDen = True) print(approx.getPoles([None, .5, .05]) ** 2.) diff --git a/examples/3d/pod/matrix_3_pod.py b/examples/3d/pod/matrix_3_pod.py index 6cf3d40..4faebb7 100644 --- a/examples/3d/pod/matrix_3_pod.py +++ b/examples/3d/pod/matrix_3_pod.py @@ -1,178 +1,181 @@ import numpy as np import scipy.sparse as sp from mpl_toolkits.mplot3d import Axes3D from matplotlib import pyplot as plt -from rrompy.hfengines.base import MatrixEngineBase as MEB +from rrompy.hfengines.base import LinearAffineEngine, NumpyEngineBase from rrompy.reduction_methods.standard import RationalInterpolant as RI from rrompy.reduction_methods.standard import ReducedBasis as RB from rrompy.parameter.parameter_sampling import (QuadratureSampler as QS, QuadratureSamplerTotal as QST, ManualSampler as MS, RandomSampler as RS) verb = 50 deg = 2 size = 1 show_sample = True show_norm = False Delta = 0 MN = 15 R = (MN + 3) * (MN + 2) * (MN + 1) // 6 STensorized = (MN + 1) ** 3 PODTol = 1e-8 samples = "centered" samples = "standard" samples, nDer = "standard_MMM", 2 algo = "rational" #algo = "RB" sampling = "quadrature" sampling = "quadrature_total" sampling = "random" if samples == "standard": radial = "" # radial = "_gaussian" # radial = "_thinplate" # radial = "_multiquadric" rW0 = 5. radialWeight = [rW0] * 2 assert Delta <= 0 if size == 1: mu0 = [0.] * 3 mutar = [.8, .8, .8] murange = [[-1.] * 3, [1.] * 3] aEff = 1.#25 bEff = 1. - aEff murangeEff = [[aEff*murange[0][0] + bEff*murange[1][0], aEff*murange[0][1] + bEff*murange[1][1], aEff*murange[0][2] + bEff*murange[1][2]], [aEff*murange[1][0] + bEff*murange[0][0], aEff*murange[1][1] + bEff*murange[0][1], aEff*murange[1][2] + bEff*murange[0][2]]] +class MatrixEngineBase(LinearAffineEngine, NumpyEngineBase): + pass + N = 150 exp = 1.05 assert exp > 1. empty = sp.csr_matrix((np.zeros(0), np.zeros(0), np.zeros(N + 1)), shape = (N, N)) -solver = MEB(verbosity = verb) +solver = MatrixEngineBase(verbosity = verb) solver.npar = 3 A0 = sp.spdiags([np.arange(1, 1 + 2 * N, 2) ** exp - (2 * (N // 4)) ** exp], [0], N, N) if deg == 1: solver.nAs = 4 solver.As = [A0, sp.eye(N), - sp.eye(N), - sp.eye(N)] elif deg == 2: solver.nAs = 7 solver.As = [A0] + [empty] + [- sp.eye(N)] + [empty] * 3 + [sp.eye(N)] elif deg == 3: solver.nAs = 13 solver.As = [A0] + [empty] + [- sp.eye(N)] + [empty] * 9 + [sp.eye(N)] np.random.seed(420) solver.nbs = 1 solver.bs = [np.random.randn(N) + 1.j * np.random.randn(N)] solver._affinePoly = True solver.thAs = solver.getMonomialWeights(solver.nAs) solver.thbs = solver.getMonomialWeights(solver.nbs) if algo == "rational": params = {'N':MN, 'M':MN + Delta, 'S':R, 'POD':True} if samples == "standard": params['polybasis'] = "CHEBYSHEV" + radial # params['polybasis'] = "LEGENDRE" + radial # params['polybasis'] = "MONOMIAL" + radial params['radialDirectionalWeights'] = radialWeight elif samples == "centered": params['polybasis'] = "MONOMIAL" else: #MMM params['S'] = nDer * int(np.ceil(R / nDer)) method = RI else: #if algo == "RB": params = {'R':(MN + 3 + Delta) * (MN + 2 + Delta) * (MN + 1 + Delta) // 6, 'S':R, 'POD':True, 'PODTolerance':PODTol} if samples == "MMM": params['S'] = nDer * int(np.ceil(R / nDer)) method = RB if samples == "standard": if sampling == "quadrature": params['sampler'] = QS(murange, "CHEBYSHEV") # params['sampler'] = QS(murange, "GAUSSLEGENDRE") # params['sampler'] = QS(murange, "UNIFORM") params['S'] = STensorized elif sampling == "quadrature_total": params['sampler'] = QST(murange, "CHEBYSHEV") else: # if sampling == "random": params['sampler'] = RS(murange, "HALTON") elif samples == "centered": params['sampler'] = MS(murange, points = [mu0]) elif samples == "standard_MMM": if sampling == "quadrature": SdirEff = int(np.ceil(int(np.ceil(R / nDer)) ** (1. / 3))) ** 3 pts = QS(murange, "CHEBYSHEV").generatePoints(SdirEff) elif sampling == "quadrature_total": pts = QST(murange, "CHEBYSHEV").generatePoints(int(np.ceil(R / nDer))) else: # if sampling == "random": pts = RS(murange, "HALTON").generatePoints(int(np.ceil(R / nDer))) params['sampler'] = MS(murange, points = pts) approx = method(solver, mu0 = mu0, approxParameters = params, verbosity = verb) approx.setupApprox() if show_sample: approx.plotApprox(mutar, name = 'u_app', what = "REAL") approx.plotHF(mutar, name = 'u_HF', what = "REAL") approx.plotErr(mutar, name = 'err', what = "REAL") # approx.plotRes(mutar, name = 'res', 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))) fig = plt.figure(figsize = (8, 6)) ax = Axes3D(fig) ax.scatter(approx.trainedModel.data.mus(0), approx.trainedModel.data.mus(1), approx.trainedModel.data.mus(2), '.') ax.set_xlim3d(murangeEff[0][0], murangeEff[1][0]) ax.set_ylim3d(murangeEff[0][1], murangeEff[1][1]) ax.set_zlim3d(murangeEff[0][2], murangeEff[1][2]) plt.show() plt.close() approx.verbosity = 0 approx.trainedModel.verbosity = 0 if algo == "rational" and approx.N > 0: from plot_zero_set_3 import plotZeroSet3 # muZeroVals, Qvals = plotZeroSet3(murange, murangeEff, approx, mu0, 200, # [None, mu0[1], mu0[2]], [1., 1., 1.]) # muZeroVals, Qvals = plotZeroSet3(murange, murangeEff, approx, mu0, 200, # [None, None, mu0[2]], [1., 1., 1.]) # muZeroVals, Qvals = plotZeroSet3(murange, murangeEff, approx, mu0, 200, # [None, mu0[1], None], [1., 1., 1.]) plotZeroSet3(murange, murangeEff, approx, mu0, 25, [None, None, None], [1., 1., 1.], imagTol = 1e-2) if show_norm: from plot_inf_set_3 import plotInfSet3 muInfVals, normEx, normApp, normRes, normErr, beta = plotInfSet3( murange, murangeEff, approx, mu0, 25, [None, mu0[1], mu0[2]], [1., 1., 1.], relative = False, normalizeDen = True) muInfVals, normEx, normApp, normRes, normErr, beta = plotInfSet3( murange, murangeEff, approx, mu0, 25, [None, None, mu0[2]], [1., 1., 1.], relative = False, normalizeDen = True) muInfVals, normEx, normApp, normRes, normErr, beta = plotInfSet3( murange, murangeEff, approx, mu0, 25, [None, mu0[1], None], [1., 1., 1.], relative = False, normalizeDen = True) diff --git a/examples/3d/pod/membrane_fracture_engine3.py b/examples/3d/pod/membrane_fracture_engine3.py new file mode 120000 index 0000000..e970941 --- /dev/null +++ b/examples/3d/pod/membrane_fracture_engine3.py @@ -0,0 +1 @@ +/home/pradovera/Desktop/Repos/RROMPy/examples/3d/base/membrane_fracture_engine3.py \ No newline at end of file diff --git a/examples/3d/pod/scattering1_pod.py b/examples/3d/pod/scattering1_pod.py index ac50f5c..a4e1da8 100644 --- a/examples/3d/pod/scattering1_pod.py +++ b/examples/3d/pod/scattering1_pod.py @@ -1,175 +1,173 @@ import numpy as np from mpl_toolkits.mplot3d import Axes3D from matplotlib import pyplot as plt -from rrompy.hfengines.linear_problem.tridimensional import Scattering1d as S1D +from scattering_1d import Scattering1d as S1D from rrompy.reduction_methods.standard import RationalInterpolant as RI from rrompy.reduction_methods.standard import ReducedBasis as RB from rrompy.parameter.parameter_sampling import (QuadratureSampler as QS, QuadratureSamplerTotal as QST, ManualSampler as MS, RandomSampler as RS) verb = 50 size = 2 show_sample = True show_norm = False clip = -1 #clip = .4 #clip = .6 -homogeneize = False -#homogeneize = True Delta = 0 MN = 15 R = (MN + 3) * (MN + 2) * (MN + 1) // 6 STensorized = (MN + 1) ** 3 PODTol = 1e-8 samples = "centered" samples = "standard" samples, nDer = "standard_MMM", 15 algo = "rational" #algo = "RB" sampling = "quadrature" sampling = "quadrature_total" sampling = "random" if samples == "standard": radial = "" # radial = "_gaussian" # radial = "_thinplate" # radial = "_multiquadric" rW0 = 5. radialWeight = [rW0] * 3 assert Delta <= 0 if size == 1: mu0 = [4., np.pi, 0.] mutar = [4.05, .95 * np.pi, .2] murange = [[2., .9 * np.pi, -.5], [6., 1.1 * np.pi, .5]] if size == 2: mu0 = [4., np.pi, .375] mutar = [4.05, .95 * np.pi, .2] murange = [[2., .9 * np.pi, 0.], [6., 1.1 * np.pi, .75]] aEff = 1.#25 bEff = 1. - aEff murangeEff = [[aEff*murange[0][0] + bEff*murange[1][0], aEff*murange[0][1] + bEff*murange[1][1], aEff*murange[0][2] + bEff*murange[1][2]], [aEff*murange[1][0] + bEff*murange[0][0], aEff*murange[1][1] + bEff*murange[0][1], aEff*murange[1][2] + bEff*murange[0][2]]] n = 100 -solver = S1D(mu0 = mu0, n = n, verbosity = verb, homogeneized = homogeneize) +solver = S1D(mu0 = mu0, n = n, verbosity = verb) if algo == "rational": params = {'N':MN, 'M':MN + Delta, 'S':R, 'POD':True} if samples == "standard": params['polybasis'] = "CHEBYSHEV" + radial # params['polybasis'] = "LEGENDRE" + radial # params['polybasis'] = "MONOMIAL" + radial params['radialDirectionalWeights'] = radialWeight elif samples == "centered": params['polybasis'] = "MONOMIAL" else: #MMM params['S'] = nDer * int(np.ceil(R / nDer)) method = RI else: #if algo == "RB": params = {'R':(MN + 3 + Delta) * (MN + 2 + Delta) * (MN + 1 + Delta) // 6, 'S':R, 'POD':True, 'PODTolerance':PODTol} if samples == "MMM": params['S'] = nDer * int(np.ceil(R / nDer)) method = RB if samples == "standard": if sampling == "quadrature": params['sampler'] = QS(murange, "CHEBYSHEV") # params['sampler'] = QS(murange, "GAUSSLEGENDRE") # params['sampler'] = QS(murange, "UNIFORM") params['S'] = STensorized elif sampling == "quadrature_total": params['sampler'] = QST(murange, "CHEBYSHEV") else: # if sampling == "random": params['sampler'] = RS(murange, "HALTON") elif samples == "centered": params['sampler'] = MS(murange, points = [mu0]) elif samples == "standard_MMM": if sampling == "quadrature": SdirEff = int(np.ceil(int(np.ceil(R / nDer)) ** (1. / 3))) ** 3 pts = QS(murange, "CHEBYSHEV").generatePoints(SdirEff) elif sampling == "quadrature_total": pts = QST(murange, "CHEBYSHEV").generatePoints(int(np.ceil(R / nDer))) else: # if sampling == "random": pts = RS(murange, "HALTON").generatePoints(int(np.ceil(R / nDer))) params['sampler'] = MS(murange, points = pts) approx = method(solver, mu0 = mu0, approxParameters = params, verbosity = verb) approx.setupApprox() if show_sample: import fenics as fen x = fen.SpatialCoordinate(solver.V.mesh()) warps = [x * mutar[1] / solver._L - x, x * solver._L / mutar[1] - x] approx.plotApprox(mutar, warps, name = 'u_app', what = "REAL") approx.plotHF(mutar, warps, name = 'u_HF', what = "REAL") approx.plotErr(mutar, warps, name = 'err', what = "REAL") # approx.plotRes(mutar, warps, name = 'res', 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))) fig = plt.figure(figsize = (8, 6)) ax = Axes3D(fig) ax.scatter(approx.trainedModel.data.mus(0), approx.trainedModel.data.mus(1), approx.trainedModel.data.mus(2), '.') ax.set_xlim3d(murangeEff[0][0], murangeEff[1][0]) ax.set_ylim3d(murangeEff[0][1], murangeEff[1][1]) ax.set_zlim3d(murangeEff[0][2], murangeEff[1][2]) plt.show() plt.close() approx.verbosity = 0 approx.trainedModel.verbosity = 0 if algo == "rational" and approx.N > 0: from plot_zero_set_3 import plotZeroSet3 muZeroVals, Qvals = plotZeroSet3(murange, murangeEff, approx, mu0, 200, [None, mu0[1], mu0[2]], [1., 1., 1.], clip = clip) muZeroVals, Qvals = plotZeroSet3(murange, murangeEff, approx, mu0, 200, [None, None, mu0[2]], [1., 1., 1.], clip = clip) muZeroVals, Qvals = plotZeroSet3(murange, murangeEff, approx, mu0, 200, [None, mu0[1], None], [1., 1., 1.], clip = clip) plotZeroSet3(murange, murangeEff, approx, mu0, 100, [None, None, None], [1., 1., 1.], clip = clip, imagTol = 1e-2) if show_norm: from plot_inf_set_3 import plotInfSet3 muInfVals, normEx, normApp, normRes, normErr, beta = plotInfSet3( murange, murangeEff, approx, mu0, 25, [None, mu0[1], mu0[2]], [1., 1., 1.], clip = clip, relative = False, normalizeDen = True) muInfVals, normEx, normApp, normRes, normErr, beta = plotInfSet3( murange, murangeEff, approx, mu0, 25, [None, None, mu0[2]], [1., 1., 1.], clip = clip, relative = False, normalizeDen = True) muInfVals, normEx, normApp, normRes, normErr, beta = plotInfSet3( murange, murangeEff, approx, mu0, 25, [None, mu0[1], None], [1., 1., 1.], clip = clip, relative = False, normalizeDen = True) print(approx.getPoles([None, np.pi, 0.])) diff --git a/examples/3d/pod/scattering_1d.py b/examples/3d/pod/scattering_1d.py new file mode 120000 index 0000000..ed22763 --- /dev/null +++ b/examples/3d/pod/scattering_1d.py @@ -0,0 +1 @@ +/home/pradovera/Desktop/Repos/RROMPy/examples/3d/base/scattering_1d.py \ No newline at end of file diff --git a/examples/airfoil/airfoil_engine.py b/examples/airfoil/airfoil_engine.py index f63b57d..a521257 100644 --- a/examples/airfoil/airfoil_engine.py +++ b/examples/airfoil/airfoil_engine.py @@ -1,50 +1,50 @@ import numpy as np import fenics as fen import ufl -from rrompy.hfengines.linear_problem import \ +from helmholtz_box_scattering_problem_engine import \ HelmholtzBoxScatteringProblemEngine as HSP from rrompy.solver.fenics import fenONE PI = np.pi class AirfoilScatteringEngine(HSP): def __init__(self, kappa:float, theta:float, degree_threshold : int = np.inf, verbosity : int = 10, timestamp : bool = True): super().__init__(R = 5, kappa = kappa, theta = theta, n = 1, degree_threshold = degree_threshold, verbosity = verbosity, timestamp = timestamp) mesh = fen.Mesh('../data/mesh/airfoil2412_1.xml') self.V = fen.FunctionSpace(mesh, "P", 1) c, s = np.cos(theta), np.sin(theta) x, y = fen.SpatialCoordinate(mesh)[:] u0R = - fen.cos(kappa * (c * x + s * y)) u0I = - fen.sin(kappa * (c * x + s * y)) self.DirichletDatum = [u0R, u0I] mu = 1.1 epsilon = .1 checkReal = x**2-x+y**2 rhop5 = ((x**2+y**2)/((x-1)**2+y**2))**.25 phiroot1 = fen.atan(-y/(x**2-x+y**2)) / 2 phiroot2 = fen.atan(-y/(x**2-x+y**2)) / 2 - PI * ufl.sign(-y/(x**2-x+y**2)) / 2 kappam1 = (((rhop5*fen.cos(phiroot1)+.5)**2.+(rhop5*fen.sin(phiroot1))**2.)/ ((rhop5*fen.cos(phiroot1)-1.)**2.+(rhop5*fen.sin(phiroot1))**2.) )**.5 - mu kappam2 = (((rhop5*fen.cos(phiroot2)+.5)**2.+(rhop5*fen.sin(phiroot2))**2.)/ ((rhop5*fen.cos(phiroot2)-1.)**2.+(rhop5*fen.sin(phiroot2))**2.) )**.5 - mu Heps1 = .9 * .5 * (1 + kappam1/epsilon + fen.sin(PI*kappam1/epsilon) / PI) + .1 Heps2 = .9 * .5 * (1 + kappam2/epsilon + fen.sin(PI*kappam2/epsilon) / PI) + .1 cTT = ufl.conditional(ufl.le(kappam1, epsilon), Heps1, fenONE) c_F = fen.Constant(.1) cFT = ufl.conditional(ufl.le(kappam2, epsilon), Heps2, fenONE) c_F = fen.Constant(.1) cT = ufl.conditional(ufl.ge(kappam1, - epsilon), cTT, c_F) cF = ufl.conditional(ufl.ge(kappam2, - epsilon), cFT, c_F) a = ufl.conditional(ufl.ge(checkReal, 0.), cT, cF) self.diffusivity = a diff --git a/examples/airfoil/greedy.py b/examples/airfoil/greedy.py index ada4d7e..2f38d0a 100644 --- a/examples/airfoil/greedy.py +++ b/examples/airfoil/greedy.py @@ -1,107 +1,103 @@ import numpy as np from airfoil_engine import AirfoilScatteringEngine from rrompy.reduction_methods.greedy import RationalInterpolantGreedy as RI from rrompy.reduction_methods.greedy import ReducedBasisGreedy as RB from rrompy.solver.fenics import L2NormMatrix from rrompy.parameter.parameter_sampling import QuadratureSampler as QS verb = 5 timed = False algo = "RI" #algo = "RB" polyBasis = "LEGENDRE" #polyBasis = "CHEBYSHEV" #polyBasis = "MONOMIAL" -homog = True -homog = False k0s = np.linspace(5, 10, 100) k0 = np.mean(k0s) kl, kr = min(k0s), max(k0s) params = {'S':5, 'sampler':QS([kl, kr], "UNIFORM"), 'nTestPoints':500, 'greedyTol':1e-2, 'polybasis':polyBasis, 'errorEstimatorKind':'INTERPOLATORY'} ######### kappa = 10 theta = np.pi * - 45 / 180. solver = AirfoilScatteringEngine(kappa, theta, verbosity = verb, - degree_threshold = 8, homogeneized = homog) + degree_threshold = 8) ######### if algo == "RI": approx = RI(solver, mu0 = k0, approxParameters = params, verbosity = verb) else: params.pop("polybasis") params.pop("errorEstimatorKind") approx = RB(solver, mu0 = k0, approxParameters = params, verbosity = verb) approx.initEstimatorNormEngine(L2NormMatrix(solver.V)) if timed: from time import clock start_time = clock() approx.greedy() print("Time: ", clock() - start_time) else: approx.greedy(True) approx.samplingEngine.verbosity = 0 approx.verbosity = 0 kl, kr = np.real(kl), np.real(kr) from matplotlib import pyplot as plt normApp = np.zeros(len(k0s)) norm = np.zeros_like(normApp) res = np.zeros_like(normApp) err = np.zeros_like(normApp) for j in range(len(k0s)): normApp[j] = approx.normApprox(k0s[j]) norm[j] = approx.normHF(k0s[j]) - res[j] = (approx.estimatorNormEngine.norm( - approx.getRes(k0s[j], duality = False)) - / approx.estimatorNormEngine.norm( - approx.getRHS(k0s[j], duality = False))) + res[j] = (approx.estimatorNormEngine.norm(approx.getRes(k0s[j])) + / approx.estimatorNormEngine.norm(approx.getRHS(k0s[j]))) err[j] = approx.normErr(k0s[j]) / approx.normHF(k0s[j]) resApp = approx.errorEstimator(k0s) plt.figure() plt.plot(k0s, norm) plt.plot(k0s, normApp, '--') plt.plot(np.real(approx.mus.data), 1.05*np.max(norm)*np.ones_like(approx.mus.data, dtype = float), 'rx') plt.xlim([kl, kr]) plt.grid() plt.show() plt.close() plt.figure() plt.semilogy(k0s, res) plt.semilogy(k0s, resApp, '--') plt.semilogy(np.real(approx.mus.data), 4.*np.max(resApp)*np.ones_like(approx.mus.data, dtype = float), 'rx') plt.xlim([kl, kr]) plt.grid() plt.show() plt.close() plt.figure() plt.semilogy(k0s, err) plt.xlim([kl, kr]) plt.grid() plt.show() plt.close() polesApp = approx.getPoles() mask = (np.real(polesApp) < kl) | (np.real(polesApp) > kr) print("Outliers:", polesApp[mask]) polesApp = polesApp[~mask] plt.figure() plt.plot(np.real(polesApp), np.imag(polesApp), 'kx') plt.axis('equal') plt.grid() plt.show() plt.close() diff --git a/examples/airfoil/helmholtz_box_scattering_problem_engine.py b/examples/airfoil/helmholtz_box_scattering_problem_engine.py new file mode 120000 index 0000000..265edd2 --- /dev/null +++ b/examples/airfoil/helmholtz_box_scattering_problem_engine.py @@ -0,0 +1 @@ +/home/pradovera/Desktop/Repos/RROMPy/tests/hfengines/helmholtz_box_scattering_problem_engine.py \ No newline at end of file diff --git a/examples/airfoil/pod.py b/examples/airfoil/pod.py index c51576b..aefefba 100644 --- a/examples/airfoil/pod.py +++ b/examples/airfoil/pod.py @@ -1,108 +1,106 @@ import numpy as np from airfoil_engine import AirfoilScatteringEngine from rrompy.reduction_methods.standard import RationalInterpolant as RI from rrompy.reduction_methods.standard import ReducedBasis as RB from rrompy.parameter.parameter_sampling import QuadratureSampler as QS verb = 100 -homog = True -homog = False sol = "single" sol = "sweep" algo = "RI" algo = "RB" polyBasis = "LEGENDRE" polyBasis = "CHEBYSHEV" #polyBasis = "MONOMIAL" Nsweep = 100 k0s = [x * 2 * np.pi / 340 for x in [1.0e2, 5.0e2]] k0 = np.mean(np.power(k0s, 2.)) ** .5 ktar = k0s[0] + (k0s[1] - k0s[0]) * .7 params = {'N':29, 'M':29, 'R':30, 'S':30, 'POD':True, 'polybasis':polyBasis, 'sampler':QS(k0s, "CHEBYSHEV"), 'robustTol':1e-14} theta = - 45. * np.pi / 180 solver = AirfoilScatteringEngine(k0, theta, verbosity = verb, degree_threshold = 8) if algo == "RI": params.pop('R') approx = RI(solver, mu0 = k0, approxParameters = params, verbosity = verb) else: params.pop('N') params.pop('M') params.pop('polybasis') params.pop('robustTol') approx = RB(solver, mu0 = k0, approxParameters = params, verbosity = verb) approx.setupApprox() if sol == "single": # approx.outParaviewTimeDomainSamples(filename = "out/outSamples", # forceNewFile = False, folders = True) approx.outParaviewTimeDomainApprox(ktar, omega = 2. * np.pi * ktar, filename = "out/outTApp{}".format(ktar), forceNewFile = False, folder = True) approx.outParaviewTimeDomainHF(ktar, omega = 2. * np.pi * ktar, filename = "out/outTHF{}".format(ktar), forceNewFile = False, folder = True) approx.outParaviewTimeDomainErr(ktar, omega = 2. * np.pi * ktar, filename = "out/outTErr{}".format(ktar), forceNewFile = False, folder = True) approx.outParaviewTimeDomainRes(ktar, omega = 2. * np.pi * ktar, filename = "out/outTRes{}".format(ktar), forceNewFile = False, folder = True) appErr, solNorm = approx.normErr(ktar), approx.normHF(ktar) resNorm, RHSNorm = approx.normRes(ktar), approx.normRHS(ktar) print(('SolNorm:\t{}\nErr:\t{}\nErrRel:\t{}').format(solNorm, appErr, np.divide(appErr, solNorm))) print(('RHSNorm:\t{}\nRes:\t{}\nResRel:\t{}').format(RHSNorm, resNorm, np.divide(resNorm, RHSNorm))) print('Poles:', approx.getPoles()) if sol == "sweep": k0s = np.linspace(k0s[0], k0s[1], Nsweep) kl, kr = min(k0s), max(k0s) approx.samplingEngine.verbosity = 0 approx.trainedModel.verbosity = 0 approx.verbosity = 0 kl, kr = np.real(kl), np.real(kr) from matplotlib import pyplot as plt normApp = np.zeros(len(k0s)) norm = np.zeros_like(normApp) err = np.zeros_like(normApp) for j in range(len(k0s)): normApp[j] = approx.normApprox(k0s[j]) norm[j] = approx.normHF(k0s[j]) err[j] = approx.normErr(k0s[j]) / norm[j] plt.figure() plt.semilogy(k0s, norm) plt.semilogy(k0s, normApp, '--') plt.semilogy(np.real(approx.mus), 1.05*np.max(norm)*np.ones_like(approx.mus, dtype = float), 'rx') plt.xlim([kl, kr]) plt.grid() plt.show() plt.close() plt.figure() plt.semilogy(k0s, err) plt.xlim([kl, kr]) plt.grid() plt.show() plt.close() polesApp = approx.getPoles() mask = (np.real(polesApp) < kl) | (np.real(polesApp) > kr) print("Outliers:", polesApp[mask]) polesApp = polesApp[~mask] plt.figure() plt.plot(np.real(polesApp), np.imag(polesApp), 'kx') plt.axis('equal') plt.grid() plt.show() plt.close() diff --git a/examples/airfoil/solver.py b/examples/airfoil/solver.py index ff31e73..5cf40b4 100644 --- a/examples/airfoil/solver.py +++ b/examples/airfoil/solver.py @@ -1,41 +1,29 @@ import numpy as np import fenics as fen from airfoil_engine import AirfoilScatteringEngine verb = 0 -#################### - -homog = True -homog = False - -#################### - k0 = 5e2 * 2 * np.pi / 340 kappa = 5e2 * 2 * np.pi / 340 theta = np.pi * - 45 / 180. mesh = fen.Mesh('../data/mesh/airfoil2412_1.xml') solver = AirfoilScatteringEngine(kappa, theta, verbosity = verb, - degree_threshold = 8, - homogeneized = homog) + degree_threshold = 8) uinc = solver.liftDirichletData() -if homog: - uhtot = solver.solve(kappa)[0] - uh = uhtot + uinc -else: - uh = solver.solve(kappa)[0] - uhtot = uh - uinc +uh = solver.solve(kappa)[0] +uhtot = uh - uinc print(solver.norm(uh)) print(solver.norm(uhtot)) solver.plot(fen.project(solver.diffusivity[0], solver.V).vector(), what = 'Real', name = 'a') solver.plot(uinc, what = 'Real', name = 'u_inc') solver.plot(uh, what = 'ABS') solver.plot(uhtot, what = 'ABS', name = 'u + u_inc') solver.outParaviewTimeDomain(uh, omega = kappa, forceNewFile = False, filename = "out/out_{}_".format(kappa)) solver.outParaviewTimeDomain(uhtot, omega = kappa, forceNewFile = False, filename = "out/outT_{}_".format(kappa)) diff --git a/examples/all_forcing/all_forcing_engine.py b/examples/all_forcing/all_forcing_engine.py index 0074346..cd118d7 100644 --- a/examples/all_forcing/all_forcing_engine.py +++ b/examples/all_forcing/all_forcing_engine.py @@ -1,43 +1,41 @@ import numpy as np import fenics as fen from rrompy.hfengines.linear_problem import LaplaceBaseProblemEngine as LBPE from rrompy.utilities.base import verbosityManager as vbMng from rrompy.solver.fenics import fenics2Vector class AllForcingEngine(LBPE): def __init__(self, mu0:float, n : int = 30, degree_threshold : int = np.inf, verbosity : int = 10, timestamp : bool = True): super().__init__(mu0 = mu0, degree_threshold = degree_threshold, - homogeneized = False, verbosity = verbosity, - timestamp = timestamp) + verbosity = verbosity, timestamp = timestamp) mesh = fen.RectangleMesh(fen.Point(-5., -5.), fen.Point(5., 5.), n, n) self.V = fen.FunctionSpace(mesh, "P", 1) self.nAs, self.nbs = 1, 4 x, y = fen.SpatialCoordinate(mesh)[:] scaling = (2. * np.pi) ** -1. r2 = x ** 2. + y ** 2. self.forcingCoeffs = [ scaling * fen.exp(- (r2 + 1. - 2. * x + 1. - 2. * y) / 2. / 4.) / 2., scaling * fen.exp(- (r2 + 1. + 2. * x + 1. + 2. * y) / 2. / 16.) / 4., - scaling * fen.exp(- (r2 + 1. + 2. * x + 1. - 2. * y) / 2. / 9.) / 30., scaling * fen.exp(- (r2 + 1. - 2. * x + 1. + 2. * y) / 2. / 25.) / 120.] def buildb(self): """Build terms of operator of linear system.""" if self.thbs[0] is None: - self.thbs = (self.getMonomialWeights(self.nbs) - + [None] * (self.homogeneized * self.nAs)) + self.thbs = self.getMonomialWeights(self.nbs) for j in range(self.nbs): if self.bs[j] is None: vbMng(self, "INIT", "Assembling forcing term b{}.".format(j), 20) parsRe = self.iterReduceQuadratureDegree([( self.forcingCoeffs[j], "forcingCoefficient")]) u0Re = self.DirichletDatum[0] L0Re = fen.dot(self.forcingCoeffs[j], self.v) * fen.dx DBCR = fen.DirichletBC(self.V, u0Re, self.DirichletBoundary) self.bs[j] = fenics2Vector(L0Re, parsRe, DBCR, 1) vbMng(self, "DEL", "Done assembling forcing term.", 20) - self.setbHomogeneized() + diff --git a/examples/all_forcing/greedy.py b/examples/all_forcing/greedy.py index 3351a66..94e5446 100644 --- a/examples/all_forcing/greedy.py +++ b/examples/all_forcing/greedy.py @@ -1,96 +1,94 @@ import numpy as np from all_forcing_engine import AllForcingEngine from rrompy.reduction_methods.greedy import RationalInterpolantGreedy as RI from rrompy.reduction_methods.greedy import ReducedBasisGreedy as RB from rrompy.solver.fenics import L2NormMatrix from rrompy.parameter.parameter_sampling import QuadratureSampler as QS verb = 5 timed = False algo = "RI" #algo = "RB" polyBasis = "LEGENDRE" #polyBasis = "CHEBYSHEV" #polyBasis = "MONOMIAL" if timed: verb = 0 z0s = np.linspace(-3., 3., 100) z0 = np.mean(z0s) zl, zr = min(z0s), max(z0s) n = 30 solver = AllForcingEngine(mu0 = z0, n = n, degree_threshold = 8, verbosity = 0) params = {'sampler':QS([zl, zr], "UNIFORM"), 'nTestPoints':500, 'greedyTol':1e-2, 'S':2, 'polybasis':polyBasis, # 'errorEstimatorKind':'DIAGONAL'} # 'errorEstimatorKind':'INTERPOLATORY'} 'errorEstimatorKind':'AFFINE'} if algo == "RI": approx = RI(solver, mu0 = solver.mu0, approxParameters = params, verbosity = verb) else: params.pop("polybasis") params.pop("errorEstimatorKind") approx = RB(solver, mu0 = solver.mu0, approxParameters = params, verbosity = verb) approx.initEstimatorNormEngine(L2NormMatrix(solver.V)) if timed: from time import clock start_time = clock() approx.greedy() print("Time: ", clock() - start_time) else: approx.greedy(True) polesApp = approx.getPoles() print("Poles:\n", polesApp) approx.samplingEngine.verbosity = 0 approx.trainedModel.verbosity = 0 approx.verbosity = 0 zl, zr = np.real(zl), np.real(zr) from matplotlib import pyplot as plt normApp = np.zeros(len(z0s)) norm = np.zeros_like(normApp) res = np.zeros_like(normApp) err = np.zeros_like(normApp) for j in range(len(z0s)): normApp[j] = approx.normApprox(z0s[j]) norm[j] = approx.normHF(z0s[j]) - res[j] = (approx.estimatorNormEngine.norm( - approx.getRes(z0s[j], duality = False)) - / approx.estimatorNormEngine.norm( - approx.getRHS(z0s[j], duality = False))) + res[j] = (approx.estimatorNormEngine.norm(approx.getRes(z0s[j])) + / approx.estimatorNormEngine.norm(approx.getRHS(z0s[j]))) err[j] = approx.normErr(z0s[j]) / norm[j] resApp = approx.errorEstimator(z0s) plt.figure() plt.semilogy(z0s, norm) plt.semilogy(z0s, normApp, '--') plt.semilogy(np.real(approx.mus.data), 1.05*np.max(norm)*np.ones_like(approx.mus.data, dtype = float), 'rx') plt.xlim([zl, zr]) plt.grid() plt.show() plt.close() plt.figure() plt.semilogy(z0s, res) plt.semilogy(z0s, resApp, '--') plt.semilogy(np.real(approx.mus.data), approx.greedyTol*np.ones_like(approx.mus.data, dtype = float), 'rx') plt.xlim([zl, zr]) plt.grid() plt.show() plt.close() plt.figure() plt.semilogy(z0s, err) plt.xlim([zl, zr]) plt.grid() plt.show() plt.close() diff --git a/examples/base/helmholtz_box_scattering_problem_engine.py b/examples/base/helmholtz_box_scattering_problem_engine.py new file mode 120000 index 0000000..265edd2 --- /dev/null +++ b/examples/base/helmholtz_box_scattering_problem_engine.py @@ -0,0 +1 @@ +/home/pradovera/Desktop/Repos/RROMPy/tests/hfengines/helmholtz_box_scattering_problem_engine.py \ No newline at end of file diff --git a/examples/base/helmholtz_cavity_scattering_problem_engine.py b/examples/base/helmholtz_cavity_scattering_problem_engine.py new file mode 120000 index 0000000..de6f867 --- /dev/null +++ b/examples/base/helmholtz_cavity_scattering_problem_engine.py @@ -0,0 +1 @@ +/home/pradovera/Desktop/Repos/RROMPy/tests/hfengines/helmholtz_cavity_scattering_problem_engine.py \ No newline at end of file diff --git a/examples/base/helmholtz_square_bubble_problem_engine.py b/examples/base/helmholtz_square_bubble_problem_engine.py new file mode 120000 index 0000000..29e8a1c --- /dev/null +++ b/examples/base/helmholtz_square_bubble_problem_engine.py @@ -0,0 +1 @@ +/home/pradovera/Desktop/Repos/RROMPy/tests/hfengines/helmholtz_square_bubble_problem_engine.py \ No newline at end of file diff --git a/examples/base/helmholtz_square_transmission_problem_engine.py b/examples/base/helmholtz_square_transmission_problem_engine.py new file mode 120000 index 0000000..61f47ea --- /dev/null +++ b/examples/base/helmholtz_square_transmission_problem_engine.py @@ -0,0 +1 @@ +/home/pradovera/Desktop/Repos/RROMPy/tests/hfengines/helmholtz_square_transmission_problem_engine.py \ No newline at end of file diff --git a/examples/base/matrix_solver.py b/examples/base/matrix_solver.py index 4ebd883..67b393c 100644 --- a/examples/base/matrix_solver.py +++ b/examples/base/matrix_solver.py @@ -1,32 +1,35 @@ import numpy as np import scipy.sparse as sp -from rrompy.hfengines.base import MatrixEngineBase as MEB +from rrompy.hfengines.base import LinearAffineEngine, NumpyEngineBase test = 2 N = 100 verb = 0 -solver = MEB(verbosity = verb) +class MatrixEngineBase(LinearAffineEngine, NumpyEngineBase): + pass + +solver = MatrixEngineBase(verbosity = verb) solver.npar = 1 solver.nAs = 2 mu = 5.001 if test == 1: solver.As = [sp.spdiags([np.arange(1, 1 + N)], [0], N, N), - sp.eye(N)] elif test == 2: 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)] solver._affinePoly = True solver.thAs = solver.getMonomialWeights(solver.nAs) solver.thbs = solver.getMonomialWeights(solver.nbs) uh = solver.solve(mu)[0] print(solver.norm(uh)) solver.plot(uh) -solver.plot(solver.residual(mu, uh)[0], 'res') +solver.plot(solver.residual(mu, uh)[0], name = 'res') diff --git a/examples/base/solver.py b/examples/base/solver.py index a0be1bd..b52905c 100644 --- a/examples/base/solver.py +++ b/examples/base/solver.py @@ -1,75 +1,67 @@ import numpy as np -from rrompy.hfengines.linear_problem import \ +from helmholtz_square_bubble_problem_engine import \ HelmholtzSquareBubbleProblemEngine as HSBPE -from rrompy.hfengines.linear_problem import \ +from helmholtz_square_transmission_problem_engine import \ HelmholtzSquareTransmissionProblemEngine as HSTPE -from rrompy.hfengines.linear_problem import \ +from helmholtz_box_scattering_problem_engine import \ HelmholtzBoxScatteringProblemEngine as HBSPE -from rrompy.hfengines.linear_problem import \ +from helmholtz_cavity_scattering_problem_engine import \ HelmholtzCavityScatteringProblemEngine as HCSPE testNo = 2 verb = 0 if testNo == 1: solver = HSBPE(kappa = 12 ** .5, theta = np.pi / 3, n = 20, verbosity = verb) mu = 12.**.5 solver.setSolver("BICG", {"tol" : 1e-15}) uh = solver.solve(mu)[0] solver.plotmesh() print(solver.norm(uh)) solver.plot(uh) solver.plot(solver.residual(mu, uh)[0], name = 'res') ########### -elif testNo in [2, -2]: +elif testNo == 2: solver = HSTPE(nT = 1, nB = 2, theta = np.pi * 20 / 180, kappa = 4., n = 50, verbosity = verb) mu = 4. uref = solver.liftDirichletData() - if testNo > 0: - uh = solver.solve(mu)[0] - utot = uh - uref - else: - utot = solver.solve(mu)[0] - uh = utot + uref + uh = solver.solve(mu)[0] + utot = uh - uref print(solver.norm(uh)) print(solver.norm(uref)) solver.plot(uh) solver.plot(uref, name = 'u_Dir') solver.plot(utot, name = 'u_tot') solver.plot(solver.residual(mu, uh)[0], name = 'res') solver.plot(solver.residual(mu, utot)[0], name = 'res_tot') ########### -elif testNo in [3, -3]: +elif testNo == 3: solver = HBSPE(R = 5, kappa = 12**.5, theta = - np.pi * 60 / 180, n = 30, - verbosity = verb, homogeneized = (testNo < 0)) + verbosity = verb) mu = 12**.5 uref = solver.liftDirichletData() - if testNo > 0: - uh = solver.solve(mu)[0] - utot = uh - uref - else: - utot = solver.solve(mu)[0] - uh = utot + uref + uh = solver.solve(mu)[0] + utot = uh - uref solver.plotmesh() print(solver.norm(uh)) print(solver.norm(utot)) solver.plot(uh) solver.plot(utot, name = 'u_tot') - solver.plot(solver.residual(mu, uh)[0], 'res') - solver.plot(solver.residual(mu, utot)[0], 'res_tot') + solver.plot(solver.residual(mu, uh)[0], name = 'res') + solver.plot(solver.residual(mu, utot)[0], name = 'res_tot') ########### elif testNo == 4: solver = HCSPE(kappa = 5, n = 30, verbosity = verb) mu = 10 uh = solver.solve(mu)[0] solver.plotmesh() print(solver.norm(uh)) solver.plot(uh) - solver.plot(solver.residual(mu, uh)[0], 'res') + solver.plot(solver.residual(mu, uh)[0], name = 'res') diff --git a/examples/diapason/diapason_engine.py b/examples/diapason/diapason_engine.py index 645509a..355781d 100644 --- a/examples/diapason/diapason_engine.py +++ b/examples/diapason/diapason_engine.py @@ -1,134 +1,131 @@ import numpy as np import fenics as fen import ufl from rrompy.hfengines.vector_linear_problem import \ LinearElasticityHelmholtzProblemEngine as LEHPE from rrompy.hfengines.vector_linear_problem import \ LinearElasticityHelmholtzProblemEngineDamped as LEHPED class DiapasonEngine(LEHPE): def __init__(self, kappa:float, c:float, rho:float, E:float, nu:float, T:float, theta:float, phi:float, meshNo : int = 1, deg : int = 1, degree_threshold : int = np.inf, - homogeneized : bool = False, verbosity : int = 10, - timestamp : bool = True): + verbosity : int = 10, timestamp : bool = True): super().__init__(mu0 = kappa, degree_threshold = degree_threshold, - homogeneized = homogeneized, verbosity = verbosity, - timestamp = timestamp) + verbosity = verbosity, timestamp = timestamp) mesh = fen.Mesh("../data/mesh/diapason_{}.xml".format(meshNo)) subdomains = fen.MeshFunction("size_t", mesh, ("../data/mesh/diapason_{}_physical_" "region.xml").format(meshNo)) meshBall = fen.SubMesh(mesh, subdomains, 2) meshFork = fen.SubMesh(mesh, subdomains, 1) Hball = np.max(meshBall.coordinates()[:, 1]) #.00257 Ltot = np.max(mesh.coordinates()[:, 1]) #.1022 Lhandle = np.max(meshFork.coordinates()[:, 1]) #.026 Lrod = Ltot - Lhandle #.0762 L2var = (Lrod / 4.) ** 2. Ehandle_ratio = 3. rhohandle_ratio = 1.5 kWave = (np.cos(theta) * np.cos(phi), np.sin(phi), np.sin(theta) * np.cos(phi)) x, y, z = fen.SpatialCoordinate(mesh)[:] yCorr = y - Ltot compPlane = kWave[0] * x + kWave[1] * y + kWave[2] * z xPlane, yPlane, zPlane = (xx - compPlane * xx for xx in (x, y, z)) xOrtho, yOrtho, zOrtho = (compPlane * xx for xx in (x, y, z)) forcingBase = (T / (2. * np.pi * L2var)**.5 * fen.exp(- (xPlane**2. + yPlane**2. + zPlane**2.) / (2.*L2var))) forcingWeight = np.real(kappa) / c * (xOrtho + yOrtho + zOrtho) neumannDatum = [ufl.as_vector(tuple(forcingBase * fen.cos(forcingWeight) * kWavedir for kWavedir in kWave)), ufl.as_vector(tuple(forcingBase * fen.sin(forcingWeight) * kWavedir for kWavedir in kWave))] lambda_ = E * nu / (1. + nu) / (1. - 2. * nu) mu_ = E / (1. + nu) lambda_eff = ufl.conditional(ufl.ge(y, Lhandle), fen.Constant(lambda_), fen.Constant(Ehandle_ratio * lambda_)) mu_eff = ufl.conditional(ufl.ge(y, Lhandle), fen.Constant(mu_), fen.Constant(Ehandle_ratio * mu_)) rho_eff = ufl.conditional(ufl.ge(y, Lhandle), fen.Constant(rho), fen.Constant(rhohandle_ratio * rho)) self.lambda_ = lambda_eff self.mu_ = mu_eff self.rho_ = rho_eff self.V = fen.VectorFunctionSpace(mesh, "P", deg) self.DirichletBoundary = lambda x, on_b: on_b and x[1] < Hball self.NeumannBoundary = "REST" self.forcingTerm = neumannDatum class DiapasonEngineDamped(LEHPED): def __init__(self, kappa:float, c:float, rho:float, E:float, nu:float, T:float, theta:float, phi:float, dampingEta:float, meshNo : int = 1, deg : int = 1, - degree_threshold : int = np.inf, homogeneized : bool = False, - verbosity : int = 10, timestamp : bool = True): + degree_threshold : int = np.inf, verbosity : int = 10, + timestamp : bool = True): super().__init__(mu0 = kappa, degree_threshold = degree_threshold, - homogeneized = homogeneized, verbosity = verbosity, - timestamp = timestamp) + verbosity = verbosity, timestamp = timestamp) self.eta = dampingEta mesh = fen.Mesh("../data/mesh/diapason_{}.xml".format(meshNo)) subdomains = fen.MeshFunction("size_t", mesh, ("../data/mesh/diapason_{}_physical_" "region.xml").format(meshNo)) meshBall = fen.SubMesh(mesh, subdomains, 2) meshFork = fen.SubMesh(mesh, subdomains, 1) Hball = np.max(meshBall.coordinates()[:, 1]) #.00257 Ltot = np.max(mesh.coordinates()[:, 1]) #.1022 Lhandle = np.max(meshFork.coordinates()[:, 1]) #.026 Lrod = Ltot - Lhandle #.0762 L2var = (Lrod / 4.) ** 2. Ehandle_ratio = 3. rhohandle_ratio = 1.5 kWave = (np.cos(theta) * np.cos(phi), np.sin(phi), np.sin(theta) * np.cos(phi)) x, y, z = fen.SpatialCoordinate(mesh)[:] yCorr = y - Ltot compPlane = kWave[0] * x + kWave[1] * y + kWave[2] * z xPlane, yPlane, zPlane = (xx - compPlane * xx for xx in (x, y, z)) xOrtho, yOrtho, zOrtho = (compPlane * xx for xx in (x, y, z)) forcingBase = (T / (2. * np.pi * L2var)**.5 * fen.exp(- (xPlane**2. + yPlane**2. + zPlane**2.) / (2.*L2var))) forcingWeight = np.real(kappa) / c * (xOrtho + yOrtho + zOrtho) neumannDatum = [ufl.as_vector(tuple(forcingBase * fen.cos(forcingWeight) * kWavedir for kWavedir in kWave)), ufl.as_vector(tuple(forcingBase * fen.sin(forcingWeight) * kWavedir for kWavedir in kWave))] lambda_ = E * nu / (1. + nu) / (1. - 2. * nu) mu_ = E / (1. + nu) lambda_eff = ufl.conditional(ufl.ge(y, Lhandle), fen.Constant(lambda_), fen.Constant(Ehandle_ratio * lambda_)) mu_eff = ufl.conditional(ufl.ge(y, Lhandle), fen.Constant(mu_), fen.Constant(Ehandle_ratio * mu_)) rho_eff = ufl.conditional(ufl.ge(y, Lhandle), fen.Constant(rho), fen.Constant(rhohandle_ratio * rho)) self.lambda_ = lambda_eff self.mu_ = mu_eff self.rho_ = rho_eff self.V = fen.VectorFunctionSpace(mesh, "P", deg) self.DirichletBoundary = lambda x, on_b: on_b and x[1] < Hball self.NeumannBoundary = "REST" self.forcingTerm = neumannDatum diff --git a/examples/diapason/greedy.py b/examples/diapason/greedy.py index 7debb24..cc85744 100644 --- a/examples/diapason/greedy.py +++ b/examples/diapason/greedy.py @@ -1,129 +1,127 @@ import numpy as np from diapason_engine import DiapasonEngine, DiapasonEngineDamped from rrompy.reduction_methods.greedy import RationalInterpolantGreedy as RI from rrompy.reduction_methods.greedy import ReducedBasisGreedy as RB from rrompy.solver.fenics import L2NormMatrix from rrompy.parameter.parameter_sampling import QuadratureSampler as QS verb = 5 timed = False algo = "RI" #algo = "RB" polyBasis = "LEGENDRE" #polyBasis = "CHEBYSHEV" #polyBasis = "MONOMIAL" if timed: verb = 0 dampingEta = 0 * 1e4 / 2. / np.pi k0s = np.linspace(2.5e2, 7.5e3, 100) #k0s = np.linspace(2.5e3, 1.5e4, 100) #k0s = np.linspace(5.0e4, 1.0e5, 100) k0s = np.linspace(2.0e5, 2.5e5, 100) kl, kr = min(k0s), max(k0s) theta = 20. * np.pi / 180. phi = 10. * np.pi / 180. c = 3.e2 rho = 8e3 * (2. * np.pi) ** 2. E = 1.93e11 nu = .3 T = 1e6 ### if np.isclose(dampingEta, 0.): solver = DiapasonEngine(kappa = np.mean(np.power(k0s, 2.)) ** .5, c = c, rho = rho, E = E, nu = nu, T = T, theta = theta, phi = phi, meshNo = 1, degree_threshold = 8, verbosity = 0) else: solver = DiapasonEngineDamped(kappa = np.mean(k0s), c = c, rho = rho, E = E, nu = nu, T = T, theta = theta, phi = phi, dampingEta = dampingEta, meshNo = 1, degree_threshold = 8, verbosity = 0) params = {'sampler':QS([kl, kr], "UNIFORM"),#, solver.rescalingExp), 'nTestPoints':500, 'greedyTol':1e-2, 'S':5, 'polybasis':polyBasis, # 'errorEstimatorKind':'DIAGONAL'} # 'errorEstimatorKind':'INTERPOLATORY'} 'errorEstimatorKind':'AFFINE'} if algo == "RI": approx = RI(solver, mu0 = solver.mu0, approxParameters = params, verbosity = verb) else: params.pop("polybasis") params.pop("errorEstimatorKind") approx = RB(solver, mu0 = solver.mu0, approxParameters = params, verbosity = verb) approx.initEstimatorNormEngine(L2NormMatrix(solver.V)) if timed: from time import clock start_time = clock() approx.greedy() print("Time: ", clock() - start_time) else: approx.greedy(True) polesApp = approx.getPoles() print("Poles:\n", polesApp) approx.samplingEngine.verbosity = 0 approx.trainedModel.verbosity = 0 approx.verbosity = 0 kl, kr = np.real(kl), np.real(kr) from matplotlib import pyplot as plt normApp = np.zeros(len(k0s)) norm = np.zeros_like(normApp) res = np.zeros_like(normApp) err = np.zeros_like(normApp) for j in range(len(k0s)): normApp[j] = approx.normApprox(k0s[j]) norm[j] = approx.normHF(k0s[j]) - res[j] = (approx.estimatorNormEngine.norm( - approx.getRes(k0s[j], duality = False)) - / approx.estimatorNormEngine.norm( - approx.getRHS(k0s[j], duality = False))) + res[j] = (approx.estimatorNormEngine.norm(approx.getRes(k0s[j])) + / approx.estimatorNormEngine.norm(approx.getRHS(k0s[j]))) err[j] = approx.normErr(k0s[j]) / norm[j] resApp = approx.errorEstimator(k0s) plt.figure() plt.semilogy(k0s, norm) plt.semilogy(k0s, normApp, '--') plt.semilogy(np.real(approx.mus.data), 1.05*np.max(norm)*np.ones_like(approx.mus.data, dtype = float), 'rx') plt.xlim([kl, kr]) plt.grid() plt.show() plt.close() plt.figure() plt.semilogy(k0s, res) plt.semilogy(k0s, resApp, '--') plt.semilogy(np.real(approx.mus.data), approx.greedyTol*np.ones_like(approx.mus.data, dtype = float), 'rx') plt.xlim([kl, kr]) plt.grid() plt.show() plt.close() plt.figure() plt.semilogy(k0s, err) plt.xlim([kl, kr]) plt.grid() plt.show() plt.close() mask = (np.real(polesApp) < kl) | (np.real(polesApp) > kr) print("Outliers:", polesApp[mask]) polesAppEff = polesApp[~mask] plt.figure() plt.plot(np.real(polesAppEff), np.imag(polesAppEff), 'kx') plt.axis('equal') plt.grid() plt.show() plt.close() diff --git a/examples/from_papers/greedy_internalBox.py b/examples/from_papers/greedy_internalBox.py index adc9ea4..f7c4322 100644 --- a/examples/from_papers/greedy_internalBox.py +++ b/examples/from_papers/greedy_internalBox.py @@ -1,117 +1,115 @@ import numpy as np import fenics as fen from rrompy.hfengines.linear_problem import HelmholtzProblemEngine as HPE from rrompy.reduction_methods.greedy import RationalInterpolantGreedy as RI from rrompy.reduction_methods.greedy import ReducedBasisGreedy as RB from rrompy.solver.fenics import L2NormMatrix from rrompy.parameter.parameter_sampling import QuadratureSampler as QS dim = 2 verb = 5 timed = False algo = "RI" #algo = "RB" polyBasis = "LEGENDRE" polyBasis = "CHEBYSHEV" #polyBasis = "MONOMIAL" k0s = np.power(np.linspace(500 ** 2., 2250 ** 2., 200), .5) k0 = np.mean(np.power(k0s, 2.)) ** .5 kl, kr = min(k0s), max(k0s) params = {'sampler':QS([kl, kr], "UNIFORM", 2.), 'nTestPoints':500, 'greedyTol':1e-2, 'S':2, 'polybasis':polyBasis, # 'errorEstimatorKind':'AFFINE'} 'errorEstimatorKind':'DISCREPANCY'} # 'errorEstimatorKind':'INTERPOLATORY'} # 'errorEstimatorKind':'LOCALL2'} if dim == 2: mesh = fen.RectangleMesh(fen.Point(0., 0.), fen.Point(.1, .15), 10, 15) x, y = fen.SpatialCoordinate(mesh)[:] f = fen.exp(- 1e2 * (x + y)) else:#if dim == 3: mesh = fen.BoxMesh(fen.Point(0., 0., 0.), fen.Point(.1, .15, .25), 4, 6,10) x, y, z = fen.SpatialCoordinate(mesh)[:] f = fen.exp(- 1e2 * (x + y + z)) solver = HPE(np.real(k0), verbosity = verb) solver.V = fen.FunctionSpace(mesh, "P", 3) solver.refractionIndex = fen.Constant(1. / 54.6) solver.forcingTerm = f solver.NeumannBoundary = "ALL" ######### if algo == "RI": approx = RI(solver, mu0 = k0, approxParameters = params, verbosity = verb) else: params.pop("polybasis") params.pop("errorEstimatorKind") approx = RB(solver, mu0 = k0, approxParameters = params, verbosity = verb) approx.initEstimatorNormEngine(L2NormMatrix(solver.V)) if timed: from time import clock start_time = clock() approx.greedy() print("Time: ", clock() - start_time) else: approx.greedy(True) approx.samplingEngine.verbosity = 0 approx.verbosity = 0 kl, kr = np.real(kl), np.real(kr) from matplotlib import pyplot as plt normApp = np.zeros(len(k0s)) norm = np.zeros_like(normApp) res = np.zeros_like(normApp) err = np.zeros_like(normApp) for j in range(len(k0s)): normApp[j] = approx.normApprox(k0s[j]) norm[j] = approx.normHF(k0s[j]) - res[j] = (approx.estimatorNormEngine.norm( - approx.getRes(k0s[j], duality = False)) - / approx.estimatorNormEngine.norm( - approx.getRHS(k0s[j], duality = False))) + res[j] = (approx.estimatorNormEngine.norm(approx.getRes(k0s[j])) + / approx.estimatorNormEngine.norm(approx.getRHS(k0s[j]))) err[j] = approx.normErr(k0s[j]) / approx.normHF(k0s[j]) resApp = approx.errorEstimator(k0s) plt.figure() plt.plot(k0s, norm) plt.plot(k0s, normApp, '--') plt.plot(np.real(approx.mus(0)), 1.05*np.max(norm)*np.ones(approx.mus.shape, dtype = float), 'rx') plt.xlim([kl, kr]) plt.grid() plt.show() plt.close() plt.figure() plt.semilogy(k0s, res) plt.semilogy(k0s, resApp, '--') plt.semilogy(np.real(approx.mus(0)), 4.*np.max(resApp)*np.ones(approx.mus.shape, dtype = float), 'rx') plt.xlim([kl, kr]) plt.grid() plt.show() plt.close() plt.figure() plt.semilogy(k0s, err) plt.xlim([kl, kr]) plt.grid() plt.show() plt.close() polesApp = approx.getPoles() mask = (np.real(polesApp) < kl) | (np.real(polesApp) > kr) print("Outliers:", polesApp[mask]) polesApp = polesApp[~mask] plt.figure() plt.plot(np.real(polesApp), np.imag(polesApp), 'kx') plt.axis('equal') plt.grid() plt.show() plt.close() diff --git a/examples/from_papers/greedy_scatteringAirfoil.py b/examples/from_papers/greedy_scatteringAirfoil.py index 46c8187..00dafc2 100644 --- a/examples/from_papers/greedy_scatteringAirfoil.py +++ b/examples/from_papers/greedy_scatteringAirfoil.py @@ -1,151 +1,147 @@ import numpy as np import fenics as fen import ufl -from rrompy.hfengines.linear_problem import \ +from helmholtz_box_scattering_problem_engine import \ HelmholtzBoxScatteringProblemEngine as HSP from rrompy.reduction_methods.greedy import RationalInterpolantGreedy as RI from rrompy.reduction_methods.greedy import ReducedBasisGreedy as RB from rrompy.solver.fenics import fenONE, L2NormMatrix from rrompy.parameter.parameter_sampling import QuadratureSampler as QS verb = 2 timed = False algo = "RI" #algo = "RB" polyBasis = "LEGENDRE" #polyBasis = "CHEBYSHEV" #polyBasis = "MONOMIAL" -homog = True -homog = False k0s = np.linspace(5, 20, 25) k0 = np.mean(k0s) kl, kr = min(k0s), max(k0s) params = {'sampler':QS([kl, kr], "UNIFORM"), 'nTestPoints':500, 'greedyTol':1e-2, 'S':10, 'polybasis':polyBasis, 'errorEstimatorKind':'DIAGONAL'} # 'errorEstimatorKind':'INTERPOLATORY'} # 'errorEstimatorKind':'AFFINE'} ######### PI = np.pi R = 2 def Dboundary(x, on_boundary): return on_boundary and (x[0]**2+x[1]**2)**.5 < .95 * R kappa = 10 theta = PI * - 45 / 180. mu = 1.1 epsilon = .1 mesh = fen.Mesh('../data/mesh/airfoil.xml') c, s = np.cos(theta), np.sin(theta) x, y = fen.SpatialCoordinate(mesh)[:] u0R = - fen.cos(kappa * (c * x + s * y)) u0I = - fen.sin(kappa * (c * x + s * y)) checkReal = x**2-x+y**2 rhop5 = ((x**2+y**2)/((x-1)**2+y**2))**.25 phiroot1 = fen.atan(-y/(x**2-x+y**2)) / 2 phiroot2 = fen.atan(-y/(x**2-x+y**2)) / 2 - PI * ufl.sign(-y/(x**2-x+y**2)) / 2 kappam1 = (((rhop5*fen.cos(phiroot1)+.5)**2.+(rhop5*fen.sin(phiroot1))**2.)/ ((rhop5*fen.cos(phiroot1)-1.)**2.+(rhop5*fen.sin(phiroot1))**2.) )**.5 - mu kappam2 = (((rhop5*fen.cos(phiroot2)+.5)**2.+(rhop5*fen.sin(phiroot2))**2.)/ ((rhop5*fen.cos(phiroot2)-1.)**2.+(rhop5*fen.sin(phiroot2))**2.) )**.5 - mu Heps1 = .9 * .5 * (1 + kappam1/epsilon + fen.sin(PI*kappam1/epsilon) / PI) + .1 Heps2 = .9 * .5 * (1 + kappam2/epsilon + fen.sin(PI*kappam2/epsilon) / PI) + .1 cTT = ufl.conditional(ufl.le(kappam1, epsilon), Heps1, fenONE) c_F = fen.Constant(.1) cFT = ufl.conditional(ufl.le(kappam2, epsilon), Heps2, fenONE) c_F = fen.Constant(.1) cT = ufl.conditional(ufl.ge(kappam1, - epsilon), cTT, c_F) cF = ufl.conditional(ufl.ge(kappam2, - epsilon), cFT, c_F) a = ufl.conditional(ufl.ge(checkReal, 0.), cT, cF) solver = HSP(R, np.real(k0), theta, n = 1, verbosity = verb, - degree_threshold = 8, homogeneized = homog) + degree_threshold = 8) solver.omega = np.real(k0) solver.V = fen.FunctionSpace(mesh, "P", 2) solver.diffusivity = a solver.DirichletBoundary = Dboundary solver.RobinBoundary = "REST" solver.DirichletDatum = [u0R, u0I] ######### if algo == "RI": approx = RI(solver, mu0 = k0, approxParameters = params, verbosity = verb) else: params.pop("polybasis") params.pop("errorEstimatorKind") approx = RB(solver, mu0 = k0, approxParameters = params, verbosity = verb) approx.initEstimatorNormEngine(L2NormMatrix(solver.V)) if timed: from time import clock start_time = clock() approx.greedy() print("Time: ", clock() - start_time) else: approx.greedy(True) approx.samplingEngine.verbosity = 0 approx.verbosity = 0 kl, kr = np.real(kl), np.real(kr) from matplotlib import pyplot as plt normApp = np.zeros(len(k0s)) norm = np.zeros_like(normApp) res = np.zeros_like(normApp) err = np.zeros_like(normApp) for j in range(len(k0s)): normApp[j] = approx.normApprox(k0s[j]) norm[j] = approx.normHF(k0s[j]) - res[j] = (approx.estimatorNormEngine.norm( - approx.getRes(k0s[j], duality = False)) - / approx.estimatorNormEngine.norm( - approx.getRHS(k0s[j], duality = False))) + res[j] = (approx.estimatorNormEngine.norm(approx.getRes(k0s[j])) + / approx.estimatorNormEngine.norm(approx.getRHS(k0s[j]))) err[j] = approx.normErr(k0s[j]) / approx.normHF(k0s[j]) resApp = approx.errorEstimator(k0s) plt.figure() plt.plot(k0s, norm) plt.plot(k0s, normApp, '--') plt.plot(np.real(approx.mus(0)), 1.05*np.max(norm)*np.ones(approx.mus.shape, dtype = float), 'rx') plt.xlim([kl, kr]) plt.grid() plt.show() plt.close() plt.figure() plt.semilogy(k0s, res) plt.semilogy(k0s, resApp, '--') plt.semilogy(np.real(approx.mus(0)), 4.*np.max(resApp)*np.ones(approx.mus.shape, dtype = float), 'rx') plt.xlim([kl, kr]) plt.grid() plt.show() plt.close() plt.figure() plt.semilogy(k0s, err) plt.xlim([kl, kr]) plt.grid() plt.show() plt.close() polesApp = approx.getPoles() mask = (np.real(polesApp) < kl) | (np.real(polesApp) > kr) print("Outliers:", polesApp[mask]) polesApp = polesApp[~mask] plt.figure() plt.plot(np.real(polesApp), np.imag(polesApp), 'kx') plt.axis('equal') plt.grid() plt.show() plt.close() diff --git a/examples/from_papers/helmholtz_box_scattering_problem_engine.py b/examples/from_papers/helmholtz_box_scattering_problem_engine.py new file mode 120000 index 0000000..265edd2 --- /dev/null +++ b/examples/from_papers/helmholtz_box_scattering_problem_engine.py @@ -0,0 +1 @@ +/home/pradovera/Desktop/Repos/RROMPy/tests/hfengines/helmholtz_box_scattering_problem_engine.py \ No newline at end of file diff --git a/examples/from_papers/pod_scatteringAirfoil.py b/examples/from_papers/pod_scatteringAirfoil.py index dbb8297..2e05d7a 100644 --- a/examples/from_papers/pod_scatteringAirfoil.py +++ b/examples/from_papers/pod_scatteringAirfoil.py @@ -1,130 +1,119 @@ import numpy as np import fenics as fen import ufl -from rrompy.hfengines.linear_problem import \ +from helmholtz_box_scattering_problem_engine import \ HelmholtzBoxScatteringProblemEngine as HSP from rrompy.reduction_methods.standard import RationalInterpolant as RI from rrompy.reduction_methods.standard import ReducedBasis as RB from rrompy.parameter.parameter_sampling import QuadratureSampler as QS from rrompy.solver.fenics import fenONE from operator import itemgetter def subdict(d, ks): return dict(zip(ks, itemgetter(*ks)(d))) verb = 0 -#################### - -homog = True -#homog = False - -#################### - test = "solve" test = "approx" plotSamples = True k0 = 10 kLeft, kRight = 8 + 0.j, 12 + 0.j ktar = 11 ktars = np.linspace(8, 12, 21) + 0.j PI = np.pi R = 2 def Dboundary(x, on_boundary): return on_boundary and (x[0]**2+x[1]**2)**.5 < .95 * R kappa = 10 theta = PI * - 45 / 180. mu = 1.1 epsilon = .1 mesh = fen.Mesh('../data/mesh/airfoil.xml') c, s = np.cos(theta), np.sin(theta) x, y = fen.SpatialCoordinate(mesh)[:] u0R = - fen.cos(kappa * (c * x + s * y)) u0I = - fen.sin(kappa * (c * x + s * y)) checkReal = x**2-x+y**2 rhop5 = ((x**2+y**2)/((x-1)**2+y**2))**.25 phiroot1 = fen.atan(-y/(x**2-x+y**2)) / 2 phiroot2 = fen.atan(-y/(x**2-x+y**2)) / 2 - PI * ufl.sign(-y/(x**2-x+y**2)) / 2 kappam1 = (((rhop5*fen.cos(phiroot1)+.5)**2.+(rhop5*fen.sin(phiroot1))**2.)/ ((rhop5*fen.cos(phiroot1)-1.)**2.+(rhop5*fen.sin(phiroot1))**2.) )**.5 - mu kappam2 = (((rhop5*fen.cos(phiroot2)+.5)**2.+(rhop5*fen.sin(phiroot2))**2.)/ ((rhop5*fen.cos(phiroot2)-1.)**2.+(rhop5*fen.sin(phiroot2))**2.) )**.5 - mu Heps1 = .9 * .5 * (1 + kappam1/epsilon + fen.sin(PI*kappam1/epsilon) / PI) + .1 Heps2 = .9 * .5 * (1 + kappam2/epsilon + fen.sin(PI*kappam2/epsilon) / PI) + .1 cTT = ufl.conditional(ufl.le(kappam1, epsilon), Heps1, fenONE) c_F = fen.Constant(.1) cFT = ufl.conditional(ufl.le(kappam2, epsilon), Heps2, fenONE) c_F = fen.Constant(.1) cT = ufl.conditional(ufl.ge(kappam1, - epsilon), cTT, c_F) cF = ufl.conditional(ufl.ge(kappam2, - epsilon), cFT, c_F) a = ufl.conditional(ufl.ge(checkReal, 0.), cT, cF) ### solver = HSP(R, np.abs(k0), theta, n = 1, verbosity = verb, - degree_threshold = 8, homogeneized = homog) + degree_threshold = 8) solver.V = fen.FunctionSpace(mesh, "P", 3) solver.diffusivity = a solver.DirichletBoundary = Dboundary solver.RobinBoundary = "REST" solver.DirichletDatum = [u0R, u0I] ### if test == "solve": uinc = solver.liftDirichletData() - if homog: - uhtot = solver.solve(k0)[0] - uh = uhtot + uinc - else: - uh = solver.solve(k0)[0] - uhtot = uh - uinc + uh = solver.solve(k0)[0] + uhtot = uh - uinc print(solver.norm(uh)) print(solver.norm(uhtot)) solver.plot(fen.project(a, solver.V).vector(), what = 'Real', name = 'a') solver.plot(uinc, what = 'Real', name = 'u_inc') solver.plot(uh, what = 'ABS') solver.plot(uhtot, what = 'ABS', name = 'u + u_inc') elif test == "approx": params = {'N':8, 'M':8, 'R':9, 'S':9, 'POD':True, 'polybasis':"CHEBYSHEV", 'sampler':QS([kLeft, kRight], "CHEBYSHEV")} parRI = subdict(params, ['N', 'M', 'S', 'POD', 'polybasis', 'sampler']) parRB = subdict(params, ['R', 'S', 'POD', 'sampler']) approxRI = RI(solver, mu0 = np.mean([kLeft, kRight]), approxParameters = parRI, verbosity = verb) approxRB = RB(solver, mu0 = np.mean([kLeft, kRight]), approxParameters = parRB, verbosity = verb) approxRI.setupApprox() approxRB.setupApprox() if plotSamples: approxRI.plotSamples() approxRI.plotHF(ktar, name = 'u_HF') approxRI.plotApprox(ktar, name = 'u_RI''') approxRI.plotErr(ktar, name = 'err_RI''') approxRI.plotRes(ktar, name = 'res_RI''') approxRB.plotApprox(ktar, name = 'u_RB') approxRB.plotErr(ktar, name = 'err_RB') approxRB.plotRes(ktar, name = 'res_RB') HFNorm, RHSNorm = approxRI.normHF(ktar), approxRI.normRHS(ktar) RIRes, RIErr = approxRI.normRes(ktar), approxRI.normErr(ktar) RBRes, RBErr = approxRB.normRes(ktar), approxRB.normErr(ktar) print('HFNorm:\t{}\nRHSNorm:\t{}'.format(HFNorm, RHSNorm)) print('RIRes:\t{}\nRIErr:\t{}'.format(RIRes, RIErr)) print('RBRes:\t{}\nRBErr:\t{}'.format(RBRes, RBErr)) print('\nPoles RI'':') print(approxRI.getPoles()) diff --git a/examples/pod/PolesCentered.py b/examples/pod/PolesCentered.py index 02ca328..9810a5b 100644 --- a/examples/pod/PolesCentered.py +++ b/examples/pod/PolesCentered.py @@ -1,70 +1,70 @@ import numpy as np -from rrompy.hfengines.linear_problem import \ +from helmholtz_square_bubble_problem_engine import \ HelmholtzSquareBubbleProblemEngine as HSBPE from rrompy.reduction_methods.standard import RationalInterpolant as RI from rrompy.reduction_methods.standard import ReducedBasis as RB from rrompy.utilities.numerical import squareResonances from rrompy.parameter.parameter_sampling import ManualSampler as MS verb = 0 k0 = (12+0.j) ** .5 krange = [[9. ** .5], [15. ** .5]] Nmin, Nmax = 2, 10 Nvals = np.arange(Nmin, Nmax + 1, 2) params = {'N':Nmin, 'M':0, 'S':Nmin + 1, 'POD':True, 'sampler': MS(krange, points = [k0], scalingExp = 2.)} #boolCon = lambda x : np.abs(np.imag(x)) < 1e-1 * np.abs(np.real(x) # - np.real(z0)) #cleanupParameters = {'boolCondition':boolCon, 'residueCheck':True} solver = HSBPE(kappa = 12 ** .5, theta = np.pi / 3, n = 25, verbosity = verb) solver.omega = np.real(k0) approxP = RI(solver, mu0 = k0, approxParameters = params, verbosity = verb) approxR = RB(solver, mu0 = k0, approxParameters = params, verbosity = verb) rP, rE = [None] * len(Nvals), [None] * len(Nvals) verbose = 1 for j, N in enumerate(Nvals): if verbose > 0: print('N = E = {}'.format(N)) approxP.approxParameters = {'N':N, 'S':N+1} approxR.approxParameters = {'R':N, 'S':N+1} if verbose > 1: print(approxP.approxParameters) print(approxR.approxParameters) rP[j] = approxP.getPoles() rE[j] = approxR.getPoles() if verbose > 2: print(rP) print(rE) from matplotlib import pyplot as plt plotRows = int(np.ceil(len(Nvals) / 3)) fig, axes = plt.subplots(plotRows, 3, figsize = (15, 3.5 * plotRows)) for j, N in enumerate(Nvals): i1, i2 = int(np.floor(j / 3)), j % 3 axes[i1, i2].set_title('N = E = {}'.format(N)) axes[i1, i2].plot(np.real(rP[j]), np.imag(rP[j]), 'Xb', label="Pade'", markersize = 8) axes[i1, i2].plot(np.real(rE[j]), np.imag(rE[j]), 'Pr', label="RB", markersize = 8) axes[i1, i2].axhline(linewidth=1, color='k') xmin, xmax = axes[i1, i2].get_xlim() height = (xmax - xmin) / 2. res = np.power(squareResonances(xmin**2., xmax**2., False), .5) axes[i1, i2].plot(res, np.zeros_like(res), 'ok', markersize = 4) axes[i1, i2].plot(np.real(k0), np.imag(k0), 'om', markersize = 5) axes[i1, i2].plot(np.real(k0) * np.ones(2), 1.5 * height * np.arange(-1, 3, 2), '--m') axes[i1, i2].grid() axes[i1, i2].set_xlim(xmin, xmax) axes[i1, i2].set_ylim(- height, height) p = axes[i1, i2].legend() plt.tight_layout() for j in range((len(Nvals) - 1) % 3 + 1, 3): axes[plotRows - 1, j].axis('off') diff --git a/examples/pod/PolesDistributed.py b/examples/pod/PolesDistributed.py index 6dd37aa..d4bfc5e 100644 --- a/examples/pod/PolesDistributed.py +++ b/examples/pod/PolesDistributed.py @@ -1,45 +1,45 @@ from matplotlib import pyplot as plt import numpy as np -from rrompy.hfengines.linear_problem import \ +from helmholtz_square_bubble_problem_engine import \ HelmholtzSquareBubbleProblemEngine as HSBPE from rrompy.reduction_methods.standard import RationalInterpolant as RI from rrompy.parameter.parameter_sampling import QuadratureSampler as QS from rrompy.utilities.numerical import squareResonances verb = 0 ks = [1., 46. ** .5] solver = HSBPE(kappa = 12 ** .5, theta = np.pi / 3, n = 20, verbosity = verb) k0 = np.mean(np.power(ks, 2.)) ** .5 k0 = 3.46104724 solver.omega = np.real(k0) nsets = 12 paramsPade = {'S':2, 'POD':True, 'polybasis':"LEGENDRE", 'sampler':QS(ks, "UNIFORM", rescaleExp = 2.)} approx = RI(solver, mu0 = k0, approxParameters = paramsPade, verbosity = verb) poles = [None] * nsets samples = [None] * nsets polesexact = np.unique(np.power( squareResonances(ks[0]**2., ks[1]**2., False), .5)) for i in range(1, nsets + 1): print("N = {}".format(4 * i)) approx.approxParameters = {'N': 4 * i, 'M': 4 * i, 'S': 4 * i + 1} approx.setupApprox() poles[i - 1] = approx.getPoles() samples[i - 1] = (approx.mus ** 2.).data for i in range(1, nsets + 1): plt.figure() plt.plot(np.real(poles[i - 1]), np.imag(poles[i - 1]), 'kx') plt.plot(polesexact, np.zeros_like(polesexact), 'm.') plt.plot(np.real(samples[i - 1]), np.imag(samples[i - 1]), 'r*') plt.xlim(ks) plt.ylim((ks[0] - ks[1]) / 2., (ks[1] - ks[0]) / 2.) plt.title("N = {}, Neff = {}".format(4 * i, len(poles[i - 1]))) plt.grid() plt.show() plt.close() diff --git a/examples/pod/RationalHermiteInterpolant.py b/examples/pod/RationalHermiteInterpolant.py index 87d8f60..133e32e 100644 --- a/examples/pod/RationalHermiteInterpolant.py +++ b/examples/pod/RationalHermiteInterpolant.py @@ -1,133 +1,131 @@ import numpy as np -from rrompy.hfengines.linear_problem import \ +from helmholtz_square_bubble_problem_engine import \ HelmholtzSquareBubbleProblemEngine as HSBPE -from rrompy.hfengines.linear_problem import \ +from helmholtz_square_transmission_problem_engine import \ HelmholtzSquareTransmissionProblemEngine as HSTPE -from rrompy.hfengines.linear_problem import \ +from helmholtz_box_scattering_problem_engine import \ HelmholtzBoxScatteringProblemEngine as HBSPE from rrompy.reduction_methods.standard import RationalInterpolant as RI from rrompy.parameter.parameter_sampling import (QuadratureSampler as QS, ManualSampler as MS) testNo = 3 verb = 100 polyBasis = "CHEBYSHEV" #polyBasis = "LEGENDRE" #polyBasis = "MONOMIAL" rep = "REPEAT" #rep = "TILE" -homog = True -#homog = False if testNo == 1: k0s = np.power([10 + 0.j, 14 + 0.j], .5) k0 = np.mean(np.power(k0s, 2.)) ** .5 samplerBase = QS(k0s, "CHEBYSHEV", 2.) S = 8 if rep == "REPEAT": points = np.repeat(samplerBase.generatePoints(2).data, int(np.ceil(S / 2))) else: # if rep == "TILE": points = np.tile(samplerBase.generatePoints(2).data, int(np.ceil(S / 2))) params = {'N':7, 'M':6, 'S':S, 'POD':True, 'polybasis':polyBasis, 'sampler':MS(k0s, points = points, 2.)} ktar = (11 + .5j) ** .5 solver = HSBPE(kappa = 12 ** .5, theta = np.pi / 3, n = 40, verbosity = verb) solver.omega = np.real(k0) approx = RI(solver, mu0 = k0, approxParameters = params, verbosity = verb) approx.setupApprox() approx.plotApprox(ktar, name = 'u_Pade''') approx.plotHF(ktar, name = 'u_HF') approx.plotErr(ktar, name = 'err') approx.plotRes(ktar, name = 'res') appErr, solNorm = approx.normErr(ktar), approx.normHF(ktar) resNorm, RHSNorm = approx.normRes(ktar), approx.normRHS(ktar) print(('SolNorm:\t{}\nErr:\t{}\nErrRel:\t{}').format(solNorm, appErr, np.divide(appErr, solNorm))) print(('RHSNorm:\t{}\nRes:\t{}\nResRel:\t{}').format(RHSNorm, resNorm, np.divide(resNorm, RHSNorm))) print('\nPoles Pade'':') print(approx.getPoles()) ############ elif testNo == 2: k0s = [3.85 + 0.j, 4.15 + 0.j] k0 = np.mean(k0s) ktar = 4 + 0.j samplerBase = QS(k0s, "CHEBYSHEV", 2.) S = 10 if rep == "REPEAT": points = np.repeat(samplerBase.generatePoints(5).data, int(np.ceil(S / 5))) else: # if rep == "TILE": points = np.tile(samplerBase.generatePoints(5).data, int(np.ceil(S / 5))) params = {'N':8, 'M':9, 'S':S, 'POD':True, 'polybasis':polyBasis, 'sampler':MS(k0s, points = points, 2.)} solver = HSTPE(nT = 2, nB = 1, theta = np.pi * 45/180, kappa = 4., n = 50, - verbosity = verb, homogeneized = homog) + verbosity = verb) solver.omega = np.real(k0) approx = RI(solver, mu0 = k0, approxParameters = params, verbosity = verb) approx.setupApprox() # approx.plotSamples() approx.plotApprox(ktar, name = 'u_Pade''') approx.plotHF(ktar, name = 'u_HF') approx.plotErr(ktar, name = 'err') approx.plotRes(ktar, name = 'res') appErr, solNorm = approx.normErr(ktar), approx.normHF(ktar) resNorm, RHSNorm = approx.normRes(ktar), approx.normRHS(ktar) print(('SolNorm:\t{}\nErr:\t{}\nErrRel:\t{}').format(solNorm, appErr, np.divide(appErr, solNorm))) print(('RHSNorm:\t{}\nRes:\t{}\nResRel:\t{}').format(RHSNorm, resNorm, np.divide(resNorm, RHSNorm))) print('\nPoles Pade'':') print(approx.getPoles()) ############ elif testNo == 3: k0s = [2., 5.] k0 = np.mean(k0s) ktar = 4.5 - 0.j samplerBase = QS(k0s, "CHEBYSHEV") S = 15 if rep == "REPEAT": points = np.repeat(samplerBase.generatePoints(5).data, int(np.ceil(S / 5))) else: # if rep == "TILE": points = np.tile(samplerBase.generatePoints(5).data, int(np.ceil(S / 5))) params = {'N':14, 'M':14, 'S':S, 'POD':True, 'polybasis':polyBasis, 'sampler':MS(k0s, points = points)} solver = HBSPE(R = 7, kappa = 3, theta = - np.pi * 75 / 180, n = 40, - verbosity = verb, homogeneized = homog) + verbosity = verb) solver.omega = np.real(k0) approx = RI(solver, mu0 = k0, approxParameters = params, verbosity = verb) approx.setupApprox() # approx.plotSamples() approx.plotApprox(ktar, name = 'u_Pade''') approx.plotHF(ktar, name = 'u_HF') approx.plotErr(ktar, name = 'err') approx.plotRes(ktar, name = 'res') appErr, solNorm = approx.normErr(ktar), approx.normHF(ktar) resNorm, RHSNorm = approx.normRes(ktar), approx.normRHS(ktar) print(('SolNorm:\t{}\nErr:\t{}\nErrRel:\t{}').format(solNorm, appErr, np.divide(appErr, solNorm))) print(('RHSNorm:\t{}\nRes:\t{}\nResRel:\t{}').format(RHSNorm, resNorm, np.divide(resNorm, RHSNorm))) print('\nPoles Pade'':') print(approx.getPoles()) diff --git a/examples/pod/RationalInterpolant.py b/examples/pod/RationalInterpolant.py index 68e9b72..7e214c4 100644 --- a/examples/pod/RationalInterpolant.py +++ b/examples/pod/RationalInterpolant.py @@ -1,121 +1,119 @@ import numpy as np -from rrompy.hfengines.linear_problem import \ +from helmholtz_square_bubble_problem_engine import \ HelmholtzSquareBubbleProblemEngine as HSBPE -from rrompy.hfengines.linear_problem import \ +from helmholtz_square_transmission_problem_engine import \ HelmholtzSquareTransmissionProblemEngine as HSTPE -from rrompy.hfengines.linear_problem import \ +from helmholtz_box_scattering_problem_engine import \ HelmholtzBoxScatteringProblemEngine as HBSPE from rrompy.reduction_methods.standard import RationalInterpolant as RI from rrompy.parameter.parameter_sampling import QuadratureSampler as QS testNo = 3 verb = 100 polyBasis = "CHEBYSHEV" polyBasis = "LEGENDRE" #polyBasis = "MONOMIAL" -homog = True -#homog = False loadName = "RationalInterpolantModel.pkl" if testNo in [1, -1]: if testNo > 0: k0s = np.power([10 + 0.j, 14 + 0.j], .5) k0 = np.mean(np.power(k0s, 2.)) ** .5 params = {'N':4, 'M':3, 'S':5, 'POD':True, 'polybasis':polyBasis, 'sampler':QS(k0s, "CHEBYSHEV", 2.)} ktar = (11 + .5j) ** .5 solver = HSBPE(kappa = 12 ** .5, theta = np.pi / 3, n = 40, verbosity = verb) if testNo > 0: solver.omega = np.real(k0) approx = RI(solver, mu0 = k0, approxParameters = params, verbosity = verb) approx.setupApprox() # approx.plotSamples() else: approx = RI(solver, mu0 = 0, approxParameters = {'S':1, 'muBounds':[0, 1]}, verbosity = verb) approx.loadTrainedModel(loadName) approx.plotApprox(ktar, name = 'u_Pade''') approx.plotHF(ktar, name = 'u_HF') approx.plotErr(ktar, name = 'err') approx.plotRes(ktar, name = 'res') appErr, solNorm = approx.normErr(ktar), approx.normHF(ktar) resNorm, RHSNorm = approx.normRes(ktar), approx.normRHS(ktar) print(('SolNorm:\t{}\nErr:\t{}\nErrRel:\t{}').format(solNorm, appErr, np.divide(appErr, solNorm))) print(('RHSNorm:\t{}\nRes:\t{}\nResRel:\t{}').format(RHSNorm, resNorm, np.divide(resNorm, RHSNorm))) print('\nPoles Pade'':') print(approx.getPoles()) if testNo > 0: approx.storeTrainedModel("RationalInterpolantModel", forceNewFile = False) print(approx.trainedModel.data.__dict__) ############ elif testNo == 2: k0s = [3.85 + 0.j, 4.15 + 0.j] k0 = np.mean(k0s) ktar = 4 + 0.j params = {'N':8, 'M':9, 'S':10, 'POD':True, 'polybasis':polyBasis, 'sampler':QS(k0s, "CHEBYSHEV", 2.)} solver = HSTPE(nT = 2, nB = 1, theta = np.pi * 45/180, kappa = 4., n = 50, - verbosity = verb, homogeneized = homog) + verbosity = verb) solver.omega = np.real(k0) approx = RI(solver, mu0 = k0, approxParameters = params, verbosity = verb) approx.setupApprox() # approx.plotSamples() approx.plotApprox(ktar, name = 'u_Pade''') approx.plotHF(ktar, name = 'u_HF') approx.plotErr(ktar, name = 'err') approx.plotRes(ktar, name = 'res') appErr, solNorm = approx.normErr(ktar), approx.normHF(ktar) resNorm, RHSNorm = approx.normRes(ktar), approx.normRHS(ktar) print(('SolNorm:\t{}\nErr:\t{}\nErrRel:\t{}').format(solNorm, appErr, np.divide(appErr, solNorm))) print(('RHSNorm:\t{}\nRes:\t{}\nResRel:\t{}').format(RHSNorm, resNorm, np.divide(resNorm, RHSNorm))) print('\nPoles Pade'':') print(approx.getPoles()) ############ elif testNo == 3: k0s = [2., 5.] k0 = np.mean(k0s) ktar = 4.5 - 0.j params = {'N':10, 'M':9, 'S':15, 'POD':True, 'polybasis':polyBasis, 'sampler':QS(k0s, "CHEBYSHEV")} solver = HBSPE(R = 7, kappa = 3, theta = - np.pi * 75 / 180, n = 40, - verbosity = verb, homogeneized = homog) + verbosity = verb) solver.omega = np.real(k0) approx = RI(solver, mu0 = k0, approxParameters = params, verbosity = verb) approx.setupApprox() # approx.plotSamples() approx.plotApprox(ktar, name = 'u_Pade''') approx.plotHF(ktar, name = 'u_HF') approx.plotErr(ktar, name = 'err') approx.plotRes(ktar, name = 'res') appErr, solNorm = approx.normErr(ktar), approx.normHF(ktar) resNorm, RHSNorm = approx.normRes(ktar), approx.normRHS(ktar) print(('SolNorm:\t{}\nErr:\t{}\nErrRel:\t{}').format(solNorm, appErr, np.divide(appErr, solNorm))) print(('RHSNorm:\t{}\nRes:\t{}\nResRel:\t{}').format(RHSNorm, resNorm, np.divide(resNorm, RHSNorm))) print('\nPoles Pade'':') print(approx.getPoles()) diff --git a/examples/pod/ReducedBasis.py b/examples/pod/ReducedBasis.py index 327d5fd..14a82b0 100644 --- a/examples/pod/ReducedBasis.py +++ b/examples/pod/ReducedBasis.py @@ -1,108 +1,106 @@ import numpy as np -from rrompy.hfengines.linear_problem import \ +from helmholtz_square_bubble_problem_engine import \ HelmholtzSquareBubbleProblemEngine as HSBPE -from rrompy.hfengines.linear_problem import \ +from helmholtz_square_transmission_problem_engine import \ HelmholtzSquareTransmissionProblemEngine as HSTPE -from rrompy.hfengines.linear_problem import \ +from helmholtz_box_scattering_problem_engine import \ HelmholtzBoxScatteringProblemEngine as HBSPE from rrompy.reduction_methods.standard import ReducedBasis as RB from rrompy.parameter.parameter_sampling import QuadratureSampler as QS testNo = 1 verb = 100 -homog = True -#homog = False loadName = "ReducedBasisModel.pkl" if testNo in [1, -1]: if testNo > 0: k0s = np.power([10 + 0.j, 14 + 0.j], .5) k0 = np.mean(np.power(k0s, 2.)) ** .5 params = {'S':5, 'R':4, 'POD':True, 'sampler':QS(k0s, "CHEBYSHEV", 2.)} ktar = (11 + .5j) ** .5 solver = HSBPE(kappa = 12 ** .5, theta = np.pi / 3, n = 40, verbosity = verb) if testNo > 0: approx = RB(solver, mu0 = k0, approxParameters = params, verbosity = verb) approx.setupApprox() # approx.plotSamples() else: approx = RB(solver, mu0 = 0, approxParameters = {'S':1, 'muBounds':[0, 1]}, verbosity = verb) approx.loadTrainedModel(loadName) approx.plotApprox(ktar, name = 'u_RB') approx.plotHF(ktar, name = 'u_HF') approx.plotErr(ktar, name = 'err') approx.plotRes(ktar, name = 'res') appErr, solNorm = approx.normErr(ktar), approx.normHF(ktar) resNorm, RHSNorm = approx.normRes(ktar), approx.normRHS(ktar) print(('SolNorm:\t{}\nErr:\t{}\nErrRel:\t{}').format(solNorm, appErr, np.divide(appErr, solNorm))) print(('RHSNorm:\t{}\nRes:\t{}\nResRel:\t{}').format(RHSNorm, resNorm, np.divide(resNorm, RHSNorm))) if testNo > 0: approx.storeTrainedModel("ReducedBasisModel", forceNewFile = False) print(approx.trainedModel.data.__dict__) ############ elif testNo == 2: k0s = [3.85 + 0.j, 4.15 + 0.j] k0 = np.mean(k0s) ktar = 4 + .15j params = {'S':10, 'R':9, 'POD':True, 'sampler':QS(k0s, "CHEBYSHEV", 2.)} solver = HSTPE(nT = 2, nB = 1, theta = np.pi * 45/180, kappa = 4., n = 50, - verbosity = verb, homogeneized = homog) + verbosity = verb) solver.omega = np.real(k0) approx = RB(solver, mu0 = k0, approxParameters = params, verbosity = verb) approx.setupApprox() # approx.plotSamples() approx.plotApprox(ktar, name = 'u_RB') approx.plotHF(ktar, name = 'u_HF') approx.plotErr(ktar, name = 'err') approx.plotRes(ktar, name = 'res') appErr, solNorm = approx.normErr(ktar), approx.normHF(ktar) resNorm, RHSNorm = approx.normRes(ktar), approx.normRHS(ktar) print(('SolNorm:\t{}\nErr:\t{}\nErrRel:\t{}').format(solNorm, appErr, np.divide(appErr, solNorm))) print(('RHSNorm:\t{}\nRes:\t{}\nResRel:\t{}').format(RHSNorm, resNorm, np.divide(resNorm, RHSNorm))) ############ elif testNo == 3: k0s = [2., 5.] k0 = np.mean(k0s) ktar = 4.5 - 0.j params = {'S':15, 'R':10, 'POD':True, 'sampler':QS(k0s, "CHEBYSHEV")} solver = HBSPE(R = 7, kappa = 3, theta = - np.pi * 75 / 180, n = 40, - verbosity = verb, homogeneized = homog) + verbosity = verb) solver.omega = np.real(k0) approx = RB(solver, mu0 = k0, approxParameters = params, verbosity = verb) approx.setupApprox() # approx.plotSamples() approx.plotApprox(ktar, name = 'u_RB') approx.plotHF(ktar, name = 'u_HF') approx.plotErr(ktar, name = 'err') approx.plotRes(ktar, name = 'res') appErr, solNorm = approx.normErr(ktar), approx.normHF(ktar) resNorm, RHSNorm = approx.normRes(ktar), approx.normRHS(ktar) print(('SolNorm:\t{}\nErr:\t{}\nErrRel:\t{}').format(solNorm, appErr, np.divide(appErr, solNorm))) print(('RHSNorm:\t{}\nRes:\t{}\nResRel:\t{}').format(RHSNorm, resNorm, np.divide(resNorm, RHSNorm))) diff --git a/examples/pod/helmholtz_box_scattering_problem_engine.py b/examples/pod/helmholtz_box_scattering_problem_engine.py new file mode 120000 index 0000000..265edd2 --- /dev/null +++ b/examples/pod/helmholtz_box_scattering_problem_engine.py @@ -0,0 +1 @@ +/home/pradovera/Desktop/Repos/RROMPy/tests/hfengines/helmholtz_box_scattering_problem_engine.py \ No newline at end of file diff --git a/examples/pod/helmholtz_cavity_scattering_problem_engine.py b/examples/pod/helmholtz_cavity_scattering_problem_engine.py new file mode 120000 index 0000000..de6f867 --- /dev/null +++ b/examples/pod/helmholtz_cavity_scattering_problem_engine.py @@ -0,0 +1 @@ +/home/pradovera/Desktop/Repos/RROMPy/tests/hfengines/helmholtz_cavity_scattering_problem_engine.py \ No newline at end of file diff --git a/examples/pod/helmholtz_square_bubble_problem_engine.py b/examples/pod/helmholtz_square_bubble_problem_engine.py new file mode 120000 index 0000000..29e8a1c --- /dev/null +++ b/examples/pod/helmholtz_square_bubble_problem_engine.py @@ -0,0 +1 @@ +/home/pradovera/Desktop/Repos/RROMPy/tests/hfengines/helmholtz_square_bubble_problem_engine.py \ No newline at end of file diff --git a/examples/pod/helmholtz_square_transmission_problem_engine.py b/examples/pod/helmholtz_square_transmission_problem_engine.py new file mode 120000 index 0000000..61f47ea --- /dev/null +++ b/examples/pod/helmholtz_square_transmission_problem_engine.py @@ -0,0 +1 @@ +/home/pradovera/Desktop/Repos/RROMPy/tests/hfengines/helmholtz_square_transmission_problem_engine.py \ No newline at end of file diff --git a/examples/pod/matrix_pod.py b/examples/pod/matrix_pod.py index 3160c56..7f3b531 100644 --- a/examples/pod/matrix_pod.py +++ b/examples/pod/matrix_pod.py @@ -1,71 +1,74 @@ import numpy as np import scipy.sparse as sp from matplotlib import pyplot as plt -from rrompy.hfengines.base import MatrixEngineBase as MEB +from rrompy.hfengines.base import LinearAffineEngine, NumpyEngineBase from rrompy.reduction_methods.standard import RationalInterpolant as RI from rrompy.reduction_methods.standard import ReducedBasis as RB from rrompy.parameter.parameter_sampling import QuadratureSampler as QS test = 2 method = "RationalInterpolant" #method = "ReducedBasis" verb = 0 +class MatrixEngineBase(LinearAffineEngine, NumpyEngineBase): + pass + N = 100 -solver = MEB(verbosity = verb) +solver = MatrixEngineBase(verbosity = verb) solver.npar = 1 solver.nAs = 2 if test == 1: solver.As = [sp.spdiags([np.arange(1, 1 + N)], [0], N, N), - sp.eye(N)] elif test == 2: 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)] np.random.seed(420) solver.nbs = 1 solver.bs = [np.random.randn(N) + 1.j * np.random.randn(N)] solver._affinePoly = True solver.thAs = solver.getMonomialWeights(solver.nAs) solver.thbs = solver.getMonomialWeights(solver.nbs) mu0 = 10.25 mutar = 12.5 murange = [5.25, 15.25] if method == "RationalInterpolant": params = {'N':10, 'M':9, 'S':11, 'POD':True, 'polybasis':"CHEBYSHEV", 'sampler':QS(murange, "CHEBYSHEV")} approx = RI(solver, mu0 = mu0, approxParameters = params, verbosity = verb) elif method == "ReducedBasis": params = {'R':10, 'S':11, 'POD':True, 'sampler':QS(murange, "CHEBYSHEV")} approx = RB(solver, mu0 = mu0, approxParameters = params, verbosity = verb) approx.setupApprox() 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))) polesTrue = np.arange(1, 1 + N) polesTrue = polesTrue[polesTrue >= murange[0]] polesTrue = polesTrue[polesTrue <= murange[1]] polesApp = approx.getPoles() mask = (np.real(polesApp) < murange[0]) | (np.real(polesApp) > murange[1]) print("Outliers:", polesApp[mask]) polesApp = polesApp[~mask] plt.figure() plt.plot(np.real(polesApp), np.imag(polesApp), 'kx') plt.plot(polesTrue, np.zeros_like(polesTrue), 'm.') plt.axis('equal') plt.grid() plt.show() plt.close() diff --git a/examples/pod/scatteringSquare.py b/examples/pod/scatteringSquare.py index 163f778..a5b49c9 100644 --- a/examples/pod/scatteringSquare.py +++ b/examples/pod/scatteringSquare.py @@ -1,72 +1,72 @@ import numpy as np -from rrompy.hfengines.linear_problem import \ +from helmholtz_cavity_scattering_problem_engine import \ HelmholtzCavityScatteringProblemEngine as CSPE from rrompy.reduction_methods.standard import RationalInterpolant as PD from rrompy.reduction_methods.standard import ReducedBasis as RB from rrompy.parameter.parameter_sampling import QuadratureSampler as QS from operator import itemgetter def subdict(d, ks): return dict(zip(ks, itemgetter(*ks)(d))) verb = 0 #################### test = "solve" #test = "approx" plotSamples = True k0 = 10 kLeft, kRight = 9, 11 ktar = 9.5 ktars = np.linspace(8.5, 11.5, 125) #ktars = np.array([k0]) kappa = 5 n = 50 solver = CSPE(kappa = kappa, n = n, verbosity = verb) solver.omega = k0 if test == "solve": uh = solver.solve(k0)[0] print(solver.norm(uh)) solver.plot(uh, what = ['ABS', 'REAL']) elif test == "approx": params = {'N':8, 'M':8, 'R':9, 'S':9, 'POD':True, 'polybasis':"CHEBYSHEV", 'sampler':QS([kLeft, kRight], "CHEBYSHEV")} parPade = subdict(params, ['N', 'M', 'S', 'POD', 'polybasis', 'sampler']) parRB = subdict(params, ['R', 'S', 'POD', 'sampler']) approxPade = PD(solver, mu0 = np.mean([kLeft, kRight]), approxParameters = parPade, verbosity = verb) approxRB = RB(solver, mu0 = np.mean([kLeft, kRight]), approxParameters = parRB, verbosity = verb) approxPade.setupApprox() approxRB.setupApprox() if plotSamples: approxPade.plotSamples() PadeErr, solNorm = approxPade.normErr(ktar), approxPade.normHF(ktar) RBErr = approxRB.normErr(ktar) print(('SolNorm:\t{}\nErrPade:\t{}\nErrRelPade:\t{}\nErrRB:\t\t{}' '\nErrRelRB:\t{}').format(solNorm, PadeErr, np.divide(PadeErr, solNorm), RBErr, np.divide(RBErr, solNorm))) print('\nPoles Pade'':') print(approxPade.getPoles()) print('\nPoles RB:') print(approxRB.getPoles()) approxPade.plotHF(ktar, name = 'u_ex') approxPade.plotApprox(ktar, name = 'u_Pade''') approxRB.plotApprox(ktar, name = 'u_RB') approxPade.plotErr(ktar, name = 'errPade''') approxRB.plotErr(ktar, name = 'errRB') diff --git a/examples/pod/with_error_plot.py b/examples/pod/with_error_plot.py index c436d68..f95cc61 100644 --- a/examples/pod/with_error_plot.py +++ b/examples/pod/with_error_plot.py @@ -1,111 +1,111 @@ import numpy as np -from rrompy.hfengines.linear_problem import \ +from helmholtz_square_bubble_problem_engine import \ HelmholtzSquareBubbleProblemEngine as HSBPE from rrompy.reduction_methods.standard import RationalInterpolant as RI from rrompy.reduction_methods.standard import RationalMovingLeastSquares as RIM from rrompy.reduction_methods.standard import ReducedBasis as RB from rrompy.parameter.parameter_sampling import QuadratureSampler as QS verb = 100 sol = "single" sol = "sweep" algo = "RI" algo = "RIM" #algo = "RB" polyBasis = "LEGENDRE" polyBasis = "CHEBYSHEV" #polyBasis = "MONOMIAL" radialBasis = "GAUSSIAN" #radialBasis = "THINPLATE" #radialBasis = "MULTIQUADRIC" #radialBasis = "NEARESTNEIGHBOR" radialBasisDen = "GAUSSIAN" radialBasisDen = "THINPLATE" radialBasisDen = "MULTIQUADRIC" radialBasisDen = "NEARESTNEIGHBOR" k0sC = np.power([7 + 0.j, 55 + 0.j], .5) k0 = np.mean(k0sC ** 2.) ** .5 ktar = 14 ** .5 n = 20 solver = HSBPE(kappa = 12 ** .5, theta = np.pi / 3, n = 40, verbosity = verb) params = {'N':1, 'M':1, 'S':30, 'POD':True, 'polybasis':polyBasis, 'sampler':QS(k0sC, "CHEBYSHEV", 2.)} if algo == "RI": approx = RI(solver, mu0 = k0, approxParameters = params, verbosity = verb) elif algo == "RIM": params["radialBasis"] = radialBasis params["radialDirectionalWeights"] = [.75 * params["S"]] params["radialBasisDen"] = radialBasisDen params["nNearestNeighborDen"] = params["N"] + 1 approx = RIM(solver, mu0 = k0, approxParameters = params, verbosity = verb) else: params.pop("N") params.pop("M") params.pop("polybasis") approx = RB(solver, mu0 = k0, approxParameters = params, verbosity = verb) approx.setupApprox() if sol == "single": approx.plotSamples(what = "REAL") approx.plotApprox(ktar, what = "REAL", name = "uApp") approx.plotHF(ktar, what = "REAL", name = "uHF") approx.plotErr(ktar, what = "REAL", name = "err") approx.plotRes(ktar, what = "REAL", name = "res") appErr, solNorm = approx.normErr(ktar), approx.normHF(ktar) resNorm, RHSNorm = approx.normRes(ktar), approx.normRHS(ktar) print(('SolNorm:\t{}\nErr:\t{}\nErrRel:\t{}').format(solNorm, appErr, np.divide(appErr, solNorm))) print(('RHSNorm:\t{}\nRes:\t{}\nResRel:\t{}').format(RHSNorm, resNorm, np.divide(resNorm, RHSNorm))) poles = approx.getPoles() try: print('Poles:', poles ** 2.) except: pass if sol == "sweep": z0s = np.real(np.linspace(k0sC[0] ** 2., k0sC[1] ** 2., 100)) k0s = z0s ** .5 zl, zr = min(z0s), max(z0s) approx.samplingEngine.verbosity = 0 approx.trainedModel.verbosity = 0 approx.verbosity = 0 from matplotlib import pyplot as plt # normRHS = approx.normRHS(k0s) norm = approx.normHF(k0s) normApp = approx.normApprox(k0s) # res = approx.normRes(k0s) / normRHS # err = approx.normErr(k0s) / norm plt.figure() plt.semilogy(z0s, norm) plt.semilogy(z0s, normApp, '--') plt.semilogy(np.real(approx.mus.data) ** 2., 1.05*np.max(norm)*np.ones_like(approx.mus.data, dtype = float), 'rx') plt.xlim([zl, zr]) plt.grid() plt.show() plt.close() # plt.figure() # plt.semilogy(z0s, res) # plt.xlim([zl, zr]) # plt.grid() # plt.show() # plt.close() # # plt.figure() # plt.semilogy(z0s, err) # plt.xlim([zl, zr]) # plt.grid() # plt.show() # plt.close() #for j, k in enumerate(k0s): # print(k ** 2., approx.getPoles(mu = k) ** 2., norm[j], normApp[j]) diff --git a/rrompy/hfengines/base/__init__.py b/rrompy/hfengines/base/__init__.py index 78d84fc..8fb603b 100644 --- a/rrompy/hfengines/base/__init__.py +++ b/rrompy/hfengines/base/__init__.py @@ -1,34 +1,41 @@ # Copyright (C) 2018 by the RROMPy authors # # This file is part of RROMPy. # # RROMPy is free software: you can redistribute it and/or modify # it under the terms of the GNU Lesser General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # RROMPy is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU Lesser General Public License for more details. # # You should have received a copy of the GNU Lesser General Public License # along with RROMPy. If not, see . # -from .matrix_engine_base import MatrixEngineBase -from .marginal_proxy_engine import MarginalProxyEngine -from .problem_engine_base import ProblemEngineBase -from .vector_problem_engine_base import VectorProblemEngineBase from .boundary_conditions import BoundaryConditions +from .fenics_engine_base import FenicsEngineBase +from .hfengine_base import HFEngineBase +from .linear_affine_engine import LinearAffineEngine, checkIfAffine +from .linear_nonaffine_engine import LinearNonAffineEngine +from .marginal_proxy_engine import MarginalProxyEngine +from .numpy_engine_base import NumpyEngineBase +from .vector_fenics_engine_base import VectorFenicsEngineBase __all__ = [ - 'MatrixEngineBase', + 'BoundaryConditions', + 'FenicsEngineBase', + 'HFEngineBase', + 'LinearAffineEngine', + 'checkIfAffine', + 'LinearNonAffineEngine', 'MarginalProxyEngine', - 'ProblemEngineBase', - 'VectorProblemEngineBase', - 'BoundaryConditions' + 'NumpyEngineBase', + 'VectorFenicsEngineBase' ] diff --git a/rrompy/hfengines/base/problem_engine_base.py b/rrompy/hfengines/base/fenics_engine_base.py similarity index 78% rename from rrompy/hfengines/base/problem_engine_base.py rename to rrompy/hfengines/base/fenics_engine_base.py index bb22b19..f1caf80 100644 --- a/rrompy/hfengines/base/problem_engine_base.py +++ b/rrompy/hfengines/base/fenics_engine_base.py @@ -1,410 +1,398 @@ # 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 .hfengine_base import HFEngineBase from rrompy.utilities.base.types import Np1D, strLst, FenFunc, Tuple, List from rrompy.utilities.base import (purgeList, getNewFilename, verbosityManager as vbMng) -from rrompy.utilities.numerical import dot -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'] +__all__ = ['FenicsEngineBase'] -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. - degree_threshold: Threshold for ufl expression interpolation degree. - """ - - _dualityCompress = None +class FenicsEngineBase(HFEngineBase): + """Generic solver for parametric fenics problems.""" def __init__(self, degree_threshold : int = np.inf, - homogeneized : bool = False, verbosity : int = 10, - timestamp : bool = True): - self.homogeneized = homogeneized + 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.degree_threshold = degree_threshold - self.npar = 0 - - @property - def homogeneized(self): - """Value of homogeneized.""" - return self._homogeneized - @homogeneized.setter - def homogeneized(self, homogeneized): - if (not hasattr(self, "_homogeneized") - or homogeneized != self.homogeneized): - self._homogeneized = homogeneized - if hasattr(self, "_nbs"): self.resetbs() @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.dsToBeSet = True self._V = V self.u = fen.TrialFunction(V) self.v = fen.TestFunction(V) + @property def spacedim(self): - return self.V.dim() + if hasattr(self, "_V"): return self.V.dim() + return super().spacedim def autoSetDS(self): """Set FEniCS boundary measure based on boundary function handles.""" if self.dsToBeSet: vbMng(self, "INIT", "Initializing boundary measures.", 20) 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 vbMng(self, "DEL", "Done assembling boundary measures.", 20) def buildEnergyNormForm(self): """ Build sparse matrix (in CSR format) representative of scalar product. """ vbMng(self, "INIT", "Assembling energy matrix.", 20) self.energyNormMatrix = L2NormMatrix(self.V) vbMng(self, "DEL", "Done assembling energy matrix.", 20) - def buildDualityPairingForm(self): - """Build sparse matrix (in CSR format) representative of duality.""" - vbMng(self, "INIT", "Assembling duality matrix.", 20) - self.dualityMatrix = Np2DLikeEye(self.spacedim()) - vbMng(self, "DEL", "Done assembling duality matrix.", 20) + def buildEnergyNormDualForm(self): + """ + Build sparse matrix (in CSR format) representative of dual scalar + product. + """ + if not hasattr(self, "energyNormMatrix"): + self.buildEnergyNormForm() + self.energyNormDualMatrix = self.energyNormMatrix + + def buildEnergyNormPartialDualForm(self): + """ + Build sparse matrix (in CSR format) representative of dual scalar + product without duality. + """ + if not hasattr(self, "energyNormMatrix"): + self.buildEnergyNormForm() + self.energyNormPartialDualMatrix = self.energyNormMatrix def liftDirichletData(self) -> Np1D: """Lift Dirichlet datum.""" if not hasattr(self, "_liftedDirichletDatum"): 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: vbMng(self, "MAIN", ("Reducing quadrature degree from {} to {} for " "{}.").format(deg, self.degree_threshold, name), 15) 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 setbHomogeneized(self): - if not self.homogeneized: return - uLifted = None - for j in range(len(self.bs) - self.nbs - 1, -1, -1): - if self.bs[self.nbs + j] is None: - if uLifted is None: uLifted = - self.liftDirichletData() - if self.As[j] is None: self.buildA() - vbMng(self, "INIT", "Assembling forcing term bH{}.".format(j), - 20) - bH = dot(self.As[j], uLifted) - thbH = self.thAs[j] - for i in range(self.nbs): - if self.thbs[i][0] == thbH[0]: - self.bs[i] = self.bs[i] + bH - _ = self.bs.pop(self.nbs + j) - _ = self.thbs.pop(self.nbs + j) - break - else: - self.bs[self.nbs + j], self.thbs[self.nbs + j] = bH, thbH - vbMng(self, "DEL", "Done assembling forcing term.", 20) - - 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, - fenplotArgs : dict = {}, **figspecs): + def plot(self, u:Np1D, warping : List[callable] = None, + is_state : bool = False, name : str = "u", save : str = None, + what : strLst = 'all', saveFormat : str = "eps", + saveDPI : int = 100, show : bool = True, fenplotArgs : dict = {}, + **figspecs) -> str: """ 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. + is_state(optional): whether given u is value before multiplication + by c. Defaults to False. 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. fenplotArgs(optional): Optional arguments for fenplot. figspecs(optional key args): Optional arguments for matplotlib figure creation. + + Returns: + Output filename. """ + if not is_state and not self.isCEye: + return super().plot(u, warping, name, save, what, saveFormat, + saveDPI, show, fenplotArgs, **figspecs) 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 = fenplot(uAb, warping = warping, title = "|{0}|".format(name), **fenplotArgs) if self.V.mesh().geometric_dimension() > 1: 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 = fenplot(uPh, warping = warping, title = "phase({0})".format(name), **fenplotArgs) if self.V.mesh().geometric_dimension() > 1: 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 = fenplot(uRe, warping = warping, title = "Re({0})".format(name), **fenplotArgs) if self.V.mesh().geometric_dimension() > 1: 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 = fenplot(uIm, warping = warping, title = "Im({0})".format(name), **fenplotArgs) if self.V.mesh().geometric_dimension() > 1: plt.colorbar(p) if save is not None: save = save.strip() - plt.savefig(getNewFilename("{}_fig_".format(save), saveFormat), - format = saveFormat, dpi = saveDPI) + fileOut = getNewFilename("{}_fig_".format(save), saveFormat) + plt.savefig(fileOut, format = saveFormat, dpi = saveDPI) + else: fileOut = None if show: plt.show() plt.close() + return fileOut def plotmesh(self, warping : List[callable] = None, name : str = "Mesh", save : str = None, saveFormat : str = "eps", saveDPI : int = 100, show : bool = True, - fenplotArgs : dict = {}, **figspecs): + fenplotArgs : dict = {}, **figspecs) -> str: """ 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. fenplotArgs(optional): Optional arguments for fenplot. figspecs(optional key args): Optional arguments for matplotlib figure creation. + + Returns: + Output filename. """ plt.figure(**figspecs) fenplot(self.V.mesh(), warping = warping, **fenplotArgs) if save is not None: save = save.strip() - plt.savefig(getNewFilename("{}_msh_".format(save), saveFormat), - format = saveFormat, dpi = saveDPI) + fileOut = getNewFilename("{}_msh_".format(save), saveFormat) + plt.savefig(fileOut, format = saveFormat, dpi = saveDPI) + else: fileOut = None if show: plt.show() plt.close() + return fileOut 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): + is_state : bool = False, name : str = "u", + filename : str = "out", time : float = 0., + what : strLst = 'all', forceNewFile : bool = True, + folder : bool = False, filePW = None) -> str: """ Output complex-valued function with given dofs to ParaView file. Args: u: numpy complex array with function dofs. warping(optional): Domain warping functions. + is_state(optional): whether given u is value before multiplication + by c. Defaults to False. 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). + + Returns: + Output filename. """ + if not is_state and not self.isCEye: + raise RROMPyException(("Cannot output to Paraview non-state " + "object.")) 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, + is_state : bool = False, timeFinal : float = None, periodResolution : int = 20, name : str = "u", filename : str = "out", forceNewFile : bool = True, - folder : bool = False): + folder : bool = False) -> str: """ 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. + is_state(optional): whether given u is value before multiplication + by c. Defaults to False. 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. + + Returns: + Output filename. """ + if not is_state and not self.isCEye: + raise RROMPyException(("Cannot output to Paraview non-state " + "object.")) 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/hfengine_base.py b/rrompy/hfengines/base/hfengine_base.py new file mode 100644 index 0000000..f2c94c2 --- /dev/null +++ b/rrompy/hfengines/base/hfengine_base.py @@ -0,0 +1,285 @@ +# 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 numbers import Number +from copy import copy as softcopy +from rrompy.utilities.base.types import (Np1D, Np2D, List, DictAny, paramVal, + paramList, sampList) +from rrompy.utilities.numerical import (solve as tsolve, dot, customPInv, + rayleighQuotientIteration) +from rrompy.utilities.exception_manager import RROMPyAssert +from rrompy.parameter import checkParameter, checkParameterList +from rrompy.sampling import sampleList, emptySampleList +from rrompy.solver import setupSolver + +__all__ = ['HFEngineBase'] + +class HFEngineBase: + """Generic solver for parametric problems.""" + + def __init__(self, verbosity : int = 10, timestamp : bool = True): + self.verbosity = verbosity + self.timestamp = timestamp + self.setSolver("SPSOLVE", {"use_umfpack" : False}) + self.npar = 0 + self._C = None + self.outputNormMatrix = 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 __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 + @abstractmethod + def spacedim(self): + return 1 + + def checkParameter(self, mu:paramVal): + return checkParameter(mu, self.npar) + + def checkParameterList(self, mu:paramList): + return checkParameterList(mu, self.npar) + + def buildEnergyNormForm(self): + """ + Build sparse matrix (in CSR format) representative of scalar product. + """ + self.energyNormMatrix = 1. + + def buildEnergyNormDualForm(self): + """ + Build sparse matrix (in CSR format) representative of dual scalar + product. + """ + self.energyNormDualMatrix = 1. + + def buildEnergyNormPartialDualForm(self): + """ + Build sparse matrix (in CSR format) representative of dual scalar + product without duality. + """ + self.energyNormPartialDualMatrix = 1. + + def innerProduct(self, u:Np2D, v:Np2D, onlyDiag : bool = False, + dual : bool = False, is_state : bool = True) -> Np2D: + """Scalar product.""" + if is_state or self.isCEye: + if dual: + if not hasattr(self, "energyNormPartialDualMatrix"): + self.buildEnergyNormPartialDualForm() + energyMat = self.energyNormPartialDualMatrix + else: + if not hasattr(self, "energyNormMatrix"): + self.buildEnergyNormForm() + energyMat = self.energyNormMatrix + else: + energyMat = self.outputNormMatrix + if not isinstance(u, (np.ndarray,)): u = u.data + if not isinstance(v, (np.ndarray,)): v = v.data + if onlyDiag: + return np.sum(dot(energyMat, u) * v.conj(), axis = 0) + return dot(dot(energyMat, u).T, v.conj()).T + + def norm(self, u:Np2D, dual : bool = False, + is_state : bool = True) -> Np1D: + return np.abs(self.innerProduct(u, u, onlyDiag = True, dual = dual, + is_state = is_state)) ** .5 + + def baselineA(self): + """Return 0 of shape consistent with operator of linear system.""" + d = self.As[0].shape[0] + return scsp.csr_matrix((np.zeros(0), np.zeros(0), np.zeros(d + 1)), + shape = (d, d), dtype = np.complex) + + def baselineb(self): + """Return 0 of shape consistent with RHS of linear system.""" + return np.zeros(self.spacedim, dtype = np.complex) + + @abstractmethod + def A(self, mu : paramVal = [], der : List[int] = 0) -> Np2D: + """ + Assemble terms of operator of linear system and return it (or its + derivative) at a given parameter. + """ + return + + @abstractmethod + def b(self, mu : paramVal = [], der : List[int] = 0) -> Np1D: + """ + Assemble terms of RHS of linear system and return it (or its + derivative) at a given parameter. + """ + return + + @property + def C(self): + """Value of C.""" + if self._C is None: self._C = 1. + return self._C + + @property + def isCEye(self): + return isinstance(self.C, Number) + + def applyC(self, u:sampList): + """Apply LHS of linear system.""" + return dot(self.C, u) + + def applyCpInv(self, u:sampList): + """Apply pseudoinverse of LHS of linear system.""" + return dot(customPInv(self.C), u) + + _isStateShiftZero = True + def stateShift(self, mu : paramVal = []) -> Np1D: + return np.zeros((self.spacedim, len(mu))) + + _isOutputShiftZero = True + def outputShift(self, mu : paramVal = []) -> Np1D: + return self.applyC(self.stateShift(mu)) + + 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, + return_state : bool = False, verbose : 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. + return_state: whether to return state before multiplication by c. + Defaults to False. + verbose: whether to notify for each solution computed. Defaults to + False. + """ + if mu == []: mu = self.mu0 + mu = self.checkParameterList(mu)[0] + if self.npar == 0: mu.reset((1, self.npar), mu.dtype) + if len(mu) == 0: return emptySampleList() + if RHS is None: RHS = [self.b(m) 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") + for j in range(len(mu)): + u = tsolve(self.A(mu[j]), RHS[mult * j], self._solver, + self._solverArgs) + if return_state: + if j == 0: + sol = emptySampleList() + sol.reset((len(u), len(mu)), dtype = u.dtype) + sol[j] = u + else: + if j == 0: sol = np.empty((len(u), len(mu)), dtype = u.dtype) + sol[:, j] = u + if verbose: + print("." * (j % 5 != 4) + "*" * (j % 5 == 4), end = "") + if not return_state: + sol = sampleList(self.applyC(sol) - self.outputShift(mu)) + else: + sol = sampleList(sol - self.stateShift(mu)) + if verbose: print() + return sol + + def residual(self, mu : paramList = [], u : sampList = None, + post_c : bool = True) -> sampList: + """ + Find residual of linear system for given approximate solution. + + Args: + mu: parameter value. + u: numpy complex array with function dofs. If None, set to 0. + post_c: whether to post-process using c. Defaults to True. + """ + if mu == []: mu = self.mu0 + mu = self.checkParameterList(mu)[0] + if self.npar == 0: mu.reset((1, self.npar), mu.dtype) + if len(mu) == 0: return emptySampleList() + v = sampleList(self.stateShift(mu)) + if u is not None: v = v + sampleList(u) + for j in range(len(mu)): + r = self.b(mu[j]) - dot(self.A(mu[j]), v[j]) + if post_c: + if j == 0: res = np.empty((len(r), len(mu)), dtype = r.dtype) + res[:, j] = r + else: + if j == 0: + res = emptySampleList() + res.reset((len(r), len(mu)), dtype = r.dtype) + res[j] = r + if post_c: res = sampleList(self.applyC(res)) + return res + + def stabilityFactor(self, mu : paramList = [], u : sampList = None, + 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: + mu: parameter values. + u: numpy complex arrays with function dofs. + nIterP: number of iterations of power method. + nIterR: number of iterations of Rayleigh quotient method. + """ + if mu == []: mu = self.mu0 + mu = self.checkParameterList(mu)[0] + if self.npar == 0: mu.reset((1, self.npar), mu.dtype) + u = sampleList(u) + solShift = self.stateShift(mu) + if len(u) == len(mu): + u = u + solShift + else: + u = sampleList(solShift) + np.tile(u.data, (1, len(mu))) + stabFact = np.empty(len(mu), dtype = float) + if not hasattr(self, "energyNormMatrix"): + self.buildEnergyNormForm() + for j in range(len(mu)): + stabFact[j] = rayleighQuotientIteration(self.A(mu[j]), u[j], + self.energyNormMatrix, + 0., nIterP, nIterR, + self._solver, + self._solverArgs) + return stabFact diff --git a/rrompy/hfengines/base/linear_affine_engine.py b/rrompy/hfengines/base/linear_affine_engine.py new file mode 100644 index 0000000..acec106 --- /dev/null +++ b/rrompy/hfengines/base/linear_affine_engine.py @@ -0,0 +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 . +# + +from abc import abstractmethod +import numpy as np +import scipy.sparse as scsp +from copy import deepcopy as copy +from rrompy.utilities.base.types import (Np1D, Np2D, List, ListAny, TupleAny, + paramVal) +from rrompy.utilities.expression import (expressionEvaluator, createMonomial, + createMonomialList) +from rrompy.utilities.numerical import hashDerivativeToIdx as hashD +from rrompy.utilities.exception_manager import RROMPyException + +__all__ = ['LinearAffineEngine', 'checkIfAffine'] + +class LinearAffineEngine: + """Generic solver for affine parametric problems.""" + + def __init__(self, *args, **kwargs): + super().__init__(*args, **kwargs) + self._affinePoly = True + self.nAs, self.nbs = 1, 1 + + @property + def affinePoly(self): + return self._affinePoly + + @property + def nAs(self): + """Value of nAs.""" + return self._nAs + @nAs.setter + def nAs(self, nAs): + nAsOld = self._nAs if hasattr(self, "_nAs") else -1 + if nAs != nAsOld: + self._nAs = nAs + self.resetAs() + + @property + def nbs(self): + """Value of nbs.""" + return self._nbs + @nbs.setter + def nbs(self, nbs): + nbsOld = self._nbs if hasattr(self, "_nbs") else -1 + if nbs != nbsOld: + self._nbs = nbs + self.resetbs() + + @property + def spacedim(self): + if (hasattr(self, "bs") and hasattr(self.bs, "__len__") + and self.bs[0] is not None): + return len(self.bs[0]) + return super().spacedim + + def getMonomialSingleWeight(self, deg:List[int]): + return createMonomial(deg, True) + + def getMonomialWeights(self, n:int): + return createMonomialList(n, self.npar, True) + + 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 setthAs(self, thAs:List[List[TupleAny]]): + """Assign terms of operator of linear system.""" + if len(thAs) != self.nAs: + raise RROMPyException(("Expected number {} of terms of thAs not " + "matching given list length {}.").format(self.nAs, + len(thAs))) + self.thAs = copy(thAs) + + 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 setthbs(self, thbs:List[List[TupleAny]]): + """Assign terms of RHS of linear system.""" + if len(thbs) != self.nbs: + raise RROMPyException(("Expected number {} of terms of thbs not " + "matching given list length {}.").format(self.nbs, + len(thbs))) + self.thbs = copy(thbs) + + def resetAs(self): + """Reset (derivatives of) operator of linear system.""" + if hasattr(self, "_nAs"): + self.setAs([None] * self.nAs) + self.setthAs([None] * self.nAs) + + def resetbs(self): + """Reset (derivatives of) RHS of linear system.""" + if hasattr(self, "_nbs"): + self.setbs([None] * self.nbs) + self.setthbs([None] * self.nbs) + + def _assembleObject(self, mu:paramVal, objs:ListAny, th:ListAny, + derI:int) -> Np2D: + """Assemble (derivative of) object from list of derivatives.""" + mu = self.checkParameter(mu) + rExp = self.rescalingExp + muE = mu ** rExp + obj = None + for j in range(len(objs)): + if len(th[j]) <= derI and th[j][-1] is not None: + raise RROMPyException(("Cannot assemble operator. Non enough " + "derivatives of theta provided.")) + if len(th[j]) > derI and th[j][derI] is not None: + expr = expressionEvaluator(th[j][derI], muE) + if hasattr(expr, "__len__"): + if len(expr) > 1: + raise RROMPyException(("Size mismatch in value of " + "theta function. Only scalars " + "allowed.")) + expr = expr[0] + if obj is None: + obj = expr * objs[j] + else: + obj = obj + expr * objs[j] + return obj + + @abstractmethod + def buildA(self): + """Build terms of operator of linear system.""" + if self.thAs[0] is None: self.thAs = self.getMonomialWeights(self.nAs) + if self.As[0] is None: + self.As[0] = scsp.eye(self.spacedim, dtype = np.complex, + format = "csr") + for j in range(1, self.nAs): + if self.As[j] is None: self.As[j] = self.baselineA() + + def A(self, mu : paramVal = [], der : List[int] = 0) -> Np2D: + """ + Assemble terms of operator of linear system and return it (or its + derivative) at a given parameter. + """ + derI = hashD(der) if hasattr(der, "__len__") else der + if derI < 0 or derI > self.nAs - 1: return self.baselineA() + self.buildA() + assembledA = self._assembleObject(mu, self.As, self.thAs, derI) + if assembledA is None: return self.baselineA() + return assembledA + + @abstractmethod + def buildb(self): + """Build terms of RHS of linear system.""" + if self.thbs[0] is None: self.thbs = self.getMonomialWeights(self.nbs) + for j in range(self.nbs): + if self.bs[j] is None: self.bs[j] = self.baselineb() + + def b(self, mu : paramVal = [], der : List[int] = 0) -> Np1D: + """ + Assemble terms of RHS of linear system and return it (or its + derivative) at a given parameter. + """ + derI = hashD(der) if hasattr(der, "__len__") else der + if derI < 0 or derI > self.nbs - 1: return self.baselineb() + self.buildb() + assembledb = self._assembleObject(mu, self.bs, self.thbs, derI) + if assembledb is None: return self.baselineb() + return assembledb + +def checkIfAffine(engine, msg : str = "apply method", justb : bool = False): + msg = ("Cannot {} because of non-affine parametric dependence{}. Consider " + "using DEIM to define a new engine.").format(msg, " of RHS" * justb) + isAffine = (issubclass(engine.__class__, LinearAffineEngine) + or ((justb and hasattr(engine, "buildA")) + and hasattr(engine, "buildb"))) + if not isAffine: raise RROMPyException(msg) diff --git a/rrompy/hfengines/base/linear_nonaffine_engine.py b/rrompy/hfengines/base/linear_nonaffine_engine.py new file mode 100644 index 0000000..9d4af74 --- /dev/null +++ b/rrompy/hfengines/base/linear_nonaffine_engine.py @@ -0,0 +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 abc import abstractmethod +from rrompy.utilities.base.types import Np1D, Np2D, List, paramVal +from rrompy.utilities.numerical import hashDerivativeToIdx as hashD + +__all__ = ['LinearNonAffineEngine'] + +class LinearNonAffineEngine: + """Generic solver for non-affine parametric problems.""" + + @abstractmethod + def A(self, mu : paramVal = [], der : List[int] = 0) -> Np2D: + """ + Assemble terms of operator of linear system and return it (or its + derivative) at a given parameter. + """ + derI = hashD(der) if hasattr(der, "__len__") else der + A = self.baselineA() + if derI < 0: return A + return A + + @abstractmethod + def b(self, mu : paramVal = [], der : List[int] = 0) -> Np1D: + """ + Assemble terms of RHS of linear system and return it (or its + derivative) at a given parameter. + """ + derI = hashD(der) if hasattr(der, "__len__") else der + b = self.baselineb() + if derI < 0: return b + return b diff --git a/rrompy/hfengines/base/marginal_proxy_engine.py b/rrompy/hfengines/base/marginal_proxy_engine.py index 2837328..4e4b762 100644 --- a/rrompy/hfengines/base/marginal_proxy_engine.py +++ b/rrompy/hfengines/base/marginal_proxy_engine.py @@ -1,138 +1,143 @@ # 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 inspect import numpy as np from copy import copy as softcopy from rrompy.utilities.base.types import Np1D, paramVal, paramList, HFEng from rrompy.utilities.base import freepar as fp from rrompy.utilities.exception_manager import RROMPyException from rrompy.parameter import checkParameter, checkParameterList __all__ = ['MarginalProxyEngine'] class MarginalProxyEngine: """ Marginalized should prescribe fixed value for the marginalized parameters and leave freepar/None elsewhere. """ _allowedMuDependencies = ["A", "b", "checkParameter", "checkParameterList", "_assembleObject", "solve", "residual", - "stabilityFactor"] + "stabilityFactor", "stateShift", "outputShift"] def __init__(self, HFEngine:HFEng, marginalized:Np1D): self.baseHF = HFEngine self.marg = marginalized for name in HFEngine.__dir_base__(): att = getattr(HFEngine, name) if inspect.ismethod(att): attargs = inspect.getfullargspec(att).args if "mu" not in attargs: setattr(self.__class__, name, getattr(HFEngine, name)) else: if name not in self._allowedMuDependencies: raise RROMPyException(("Function {} depends on mu " "and was not accounted for. " "Must override.").format(name)) @property def affinePoly(self): return self.nparFixed == 0 and self.baseHF.affinePoly @property def freeLocations(self): - return tuple([x for x in range(self.baseHF.npar) \ - if self.marg[x] == fp]) + return [x for x in range(self.baseHF.npar) if self.marg[x] == fp] @property def fixedLocations(self): - return tuple([x for x in range(self.baseHF.npar) \ - if self.marg[x] != fp]) + return [x for x in range(self.baseHF.npar) if self.marg[x] != fp] @property def _freeLocationsInsert(self): return np.cumsum([m == fp for m in self.marg])[self.fixedLocations] @property def muFixed(self): muF = checkParameter([m for m in self.marg if m != fp]) if self.baseHF.npar - self.nparFree > 0: muF = muF[0] return muF @property def nparFree(self): """Value of nparFree.""" return len(self.freeLocations) @property def nparFixed(self): """Value of nparFixed.""" return len(self.fixedLocations) def name(self) -> str: return "{}-proxy for {}".format(self.freeLocations, self.baseHF.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) def completeMu(self, mu:paramVal) -> paramVal: mu = checkParameter(mu, self.nparFree) return np.insert(mu.data, self._freeLocationsInsert, self.muFixed, axis = 1) def completeMuList(self, mu:paramList) -> paramList: mu = checkParameterList(mu, self.nparFree)[0] return np.insert(mu.data, self._freeLocationsInsert, self.muFixed, axis = 1) def A(self, mu : paramVal = [], *args, **kwargs): return self.baseHF.A(self.completeMu(mu), *args, **kwargs) def b(self, mu : paramVal = [], *args, **kwargs): return self.baseHF.b(self.completeMu(mu), *args, **kwargs) def checkParameter(self, mu : paramVal = [], *args, **kwargs): return self.baseHF.checkParameter(self.completeMu(mu), *args, **kwargs) def checkParameterList(self, mu : paramList = [], *args, **kwargs): return self.baseHF.checkParameterList(self.completeMuList(mu), *args, **kwargs) def _assembleObject(self, mu : paramVal = [], *args, **kwargs): return self.baseHF._assembleObject(self.completeMu(mu), *args, **kwargs) + def stateShift(self, mu : paramVal = [], *args, **kwargs): + return self.baseHF.stateShift(self.completeMuList(mu), *args, **kwargs) + + def outputShift(self, mu : paramVal = [], *args, **kwargs): + return self.baseHF.outputShift(self.completeMuList(mu), + *args, **kwargs) + def solve(self, mu : paramList = [], *args, **kwargs): return self.baseHF.solve(self.completeMuList(mu), *args, **kwargs) def residual(self, mu : paramList = [], *args, **kwargs): return self.baseHF.residual(self.completeMuList(mu), *args, **kwargs) def stabilityFactor(self, mu : paramList = [], *args, **kwargs): return self.baseHF.stabilityFactor(self.completeMuList(mu), *args, **kwargs) diff --git a/rrompy/hfengines/base/matrix_engine_base.py b/rrompy/hfengines/base/matrix_engine_base.py deleted file mode 100644 index c6c63fe..0000000 --- a/rrompy/hfengines/base/matrix_engine_base.py +++ /dev/null @@ -1,479 +0,0 @@ -# Copyright (C) 2018 by the RROMPy authors -# -# This file is part of RROMPy. -# -# RROMPy is free software: you can redistribute it and/or modify -# it under the terms of the GNU Lesser General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. -# -# RROMPy is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU Lesser General Public License for more details. -# -# You should have received a copy of the GNU Lesser General Public License -# along with RROMPy. If not, see . -# - -from abc import abstractmethod -import numpy as np -import scipy.sparse as scsp -from matplotlib import pyplot as plt -from copy import deepcopy as copy, copy as softcopy -from rrompy.utilities.base.types import (Np1D, Np2D, ScOp, strLst, TupleAny, - List, ListAny, DictAny, paramVal, - paramList, sampList) -from rrompy.utilities.base import (purgeList, getNewFilename, - verbosityManager as vbMng) -from rrompy.utilities.expression import (expressionEvaluator, createMonomial, - createMonomialList) -from rrompy.utilities.numerical import (hashDerivativeToIdx as hashD, - solve, dot) -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, Np2DLikeEye - -__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. - 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. - """ - - def __init__(self, verbosity : int = 10, timestamp : bool = True): - self.verbosity = verbosity - self.timestamp = timestamp - self._affinePoly = True - 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 affinePoly(self): - return self._affinePoly - - def spacedim(self): - return self.As[0].shape[1] - - def checkParameter(self, mu:paramVal): - 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. - """ - vbMng(self, "INIT", "Assembling energy matrix.", 20) - self.energyNormMatrix = Np2DLikeEye(self.spacedim()) - vbMng(self, "DEL", "Done assembling energy matrix.", 20) - - def buildEnergyNormDualForm(self): - """ - Build sparse matrix (in CSR format) representative of dual scalar - product. - """ - if not hasattr(self, "energyNormMatrix"): - self.buildEnergyNormForm() - vbMng(self, "INIT", "Assembling energy dual matrix.", 20) - self.energyNormDualMatrix = self.energyNormMatrix - vbMng(self, "DEL", "Done assembling energy dual matrix.", 20) - - def buildDualityPairingForm(self): - """Build sparse matrix (in CSR format) representative of duality.""" - if not hasattr(self, "energyNormMatrix"): - self.buildEnergyNormForm() - vbMng(self, "INIT", "Assembling duality matrix.", 20) - self.dualityMatrix = self.energyNormMatrix - vbMng(self, "DEL", "Done assembling duality matrix.", 20) - - def buildEnergyNormPartialDualForm(self): - """ - Build sparse matrix (in CSR format) representative of dual scalar - product without duality. - """ - if not hasattr(self, "energyNormDualMatrix"): - self.buildEnergyNormDualForm() - vbMng(self, "INIT", "Assembling energy partial dual matrix.", 20) - self.energyNormPartialDualMatrix = self.energyNormDualMatrix - vbMng(self, "DEL", "Done assembling energy partial dual matrix.", 20) - - 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(dot(energyMat, u) * v.conj(), axis = 0) - return dot(dot(energyMat, u).T, v.conj()).T - - 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: - 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): - """Check if derivative index is oob for RHS of linear system.""" - if derI < 0: - return np.zeros(self.spacedim(), dtype = np.complex) - - def resetAs(self): - """Reset (derivatives of) operator of linear system.""" - self.setAs([None] * self.nAs) - self.setthAs([None] * self.nAs) - - def resetbs(self): - """Reset (derivatives of) RHS of linear system.""" - self.setbs([None] * self.nbs) - self.setthbs([None] * self.nbs) - - def getMonomialSingleWeight(self, deg:List[int]): - return createMonomial(deg, True) - - def getMonomialWeights(self, n:int): - return createMonomialList(n, self.npar, True) - - 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 setthAs(self, thAs:List[List[TupleAny]]): - """Assign terms of operator of linear system.""" - if len(thAs) != self.nAs: - raise RROMPyException(("Expected number {} of terms of thAs not " - "matching given list length {}.").format(self.nAs, - len(thAs))) - self.thAs = copy(thAs) - - 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 setthbs(self, thbs:List[List[TupleAny]]): - """Assign terms of RHS of linear system.""" - if len(thbs) != self.nbs: - raise RROMPyException(("Expected number {} of terms of thbs not " - "matching given list length {}.").format(self.nbs, - len(thbs))) - self.thbs = copy(thbs) - - def _assembleObject(self, mu:paramVal, objs:ListAny, th:ListAny, - derI:int) -> ScOp: - """Assemble (derivative of) object from list of derivatives.""" - mu = self.checkParameter(mu) - rExp = self.rescalingExp - muE = mu ** rExp - obj = None - for j in range(len(objs)): - if len(th[j]) <= derI and th[j][-1] is not None: - raise RROMPyException(("Cannot assemble operator. Non enough " - "derivatives of theta provided.")) - if len(th[j]) > derI and th[j][derI] is not None: - expr = expressionEvaluator(th[j][derI], muE) - if hasattr(expr, "__len__"): - if len(expr) > 1: - raise RROMPyException(("Size mismatch in value of " - "theta function. Only scalars " - "allowed.")) - expr = expr[0] - if obj is None: - obj = expr * objs[j] - else: - obj = obj + expr * objs[j] - return obj - - @abstractmethod - def buildA(self): - """Build terms of operator of linear system.""" - if self.thAs[0] is None: self.thAs = self.getMonomialWeights(self.nAs) - for j in range(self.nAs): - if self.As[j] is None: self.As[j] = self.checkAInBounds(-1) - - 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. - """ - derI = hashD(der) if hasattr(der, "__len__") else der - Anull = self.checkAInBounds(derI) - if Anull is not None: return Anull - self.buildA() - assembledA = self._assembleObject(mu, self.As, self.thAs, derI) - if assembledA is None: return self.checkAInBounds(-1) - return assembledA - - @abstractmethod - def buildb(self): - """Build terms of operator of linear system.""" - if self.thbs[0] is None: self.thbs = self.getMonomialWeights(self.nbs) - for j in range(self.nbs): - if self.bs[j] is None: self.bs[j] = self.checkbInBounds(-1) - - def b(self, mu : paramVal = [], der : List[int] = 0) -> Np1D: - """ - Assemble terms of RHS of linear system and return it (or its - derivative) at a given parameter. - """ - derI = hashD(der) if hasattr(der, "__len__") else der - bnull = self.checkbInBounds(derI) - if bnull is not None: return bnull - self.buildb() - assembledb = self._assembleObject(mu, self.bs, self.thbs, derI) - if assembledb is None: return self.checkbInBounds(-1) - return assembledb - - 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) -> 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)[0] - if mu.shape[0] == 0: mu.reset((1, self.npar), mu.dtype) - sol = emptySampleList() - if len(mu) > 0: - if RHS is None: - RHS = [self.b(m) 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") - for j in range(len(mu)): - u = solve(self.A(mu[j]), RHS[mult * j], self._solver, - self._solverArgs) - if j == 0: - sol.reset((len(u), len(mu)), dtype = u.dtype) - sol[j] = u - return sol - - def residual(self, mu : paramList = [], u : sampList = None, - duality : bool = True) -> sampList: - """ - Find residual of linear system for given approximate solution. - - Args: - mu: parameter value. - u: numpy complex array with function dofs. If None, set to 0. - """ - mu = self.checkParameterList(mu)[0] - 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)): - r = self.b(mu[j]) - if u is not None: - r = r - dot(self.A(mu[j]), u[mult * j]) - if j == 0: - res.reset((len(r), len(mu)), dtype = r.dtype) - if duality: - r = dot(self.dualityMatrix, 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 /= dot(dot(M, v0).T, v0.conj()) ** .5 - for j in range(nIterP): - v0 = solve(A - sigma * M, dot(M, v0), self._solver, - self._solverArgs) - v0 /= dot(dot(M, v0).T, v0.conj()) ** .5 - l0 = dot(A.dot(v0).T, v0.conj()) - for j in range(nIterR): - v0 = solve(A - l0 * M, dot(M, v0), self._solver, self._solverArgs) - v0 /= dot(dot(M, v0).T, v0.conj()) ** .5 - l0 = dot(A.dot(v0).T, v0.conj()) - if np.isnan(l0): l0 = np.finfo(float).eps - return np.abs(l0) - - def stabilityFactor(self, mu : paramList = [], u : sampList = None, - 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: - mu: parameter values. - u: numpy complex arrays with function dofs. - nIterP: number of iterations of power method. - nIterR: number of iterations of Rayleigh quotient method. - """ - mu = self.checkParameterList(mu)[0] - 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, warping : List[callable] = None, name : str = "u", - save : str = None, what : strLst = 'all', - saveFormat : str = "eps", saveDPI : int = 100, show : bool = True, - pyplotArgs : dict = {}, **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. - pyplotArgs(optional): Optional arguments for pyplot. - 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()) - if warping is not None: - idxs = warping[0](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).flatten(), **pyplotArgs) - plt.title("|{0}|".format(name)) - if 'PHASE' in what: - subplotcode = subplotcode + 1 - plt.subplot(subplotcode) - plt.plot(idxs, np.angle(u).flatten(), **pyplotArgs) - plt.title("phase({0})".format(name)) - if 'REAL' in what: - subplotcode = subplotcode + 1 - plt.subplot(subplotcode) - plt.plot(idxs, np.real(u).flatten(), **pyplotArgs) - plt.title("Re({0})".format(name)) - if 'IMAG' in what: - subplotcode = subplotcode + 1 - plt.subplot(subplotcode) - plt.plot(idxs, np.imag(u).flatten(), **pyplotArgs) - 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/numpy_engine_base.py b/rrompy/hfengines/base/numpy_engine_base.py new file mode 100644 index 0000000..99879db --- /dev/null +++ b/rrompy/hfengines/base/numpy_engine_base.py @@ -0,0 +1,101 @@ +# 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 matplotlib import pyplot as plt +from .hfengine_base import HFEngineBase +from rrompy.utilities.base.types import Np1D, strLst, List +from rrompy.utilities.base import purgeList, getNewFilename + +__all__ = ['NumpyEngineBase'] + +class NumpyEngineBase(HFEngineBase): + """Generic solver for parametric matricial problems.""" + + 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, + pyplotArgs : dict = {}, **figspecs) -> str: + """ + 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. + pyplotArgs(optional): Optional arguments for pyplot. + figspecs(optional key args): Optional arguments for matplotlib + figure creation. + + Returns: + Output filename. + """ + 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) + if warping is not None: + idxs = warping[0](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).flatten(), **pyplotArgs) + plt.title("|{0}|".format(name)) + if 'PHASE' in what: + subplotcode = subplotcode + 1 + plt.subplot(subplotcode) + plt.plot(idxs, np.angle(u).flatten(), **pyplotArgs) + plt.title("phase({0})".format(name)) + if 'REAL' in what: + subplotcode = subplotcode + 1 + plt.subplot(subplotcode) + plt.plot(idxs, np.real(u).flatten(), **pyplotArgs) + plt.title("Re({0})".format(name)) + if 'IMAG' in what: + subplotcode = subplotcode + 1 + plt.subplot(subplotcode) + plt.plot(idxs, np.imag(u).flatten(), **pyplotArgs) + plt.title("Im({0})".format(name)) + if save is not None: + save = save.strip() + fileOut = getNewFilename("{}_fig_".format(save), saveFormat) + plt.savefig(fileOut, format = saveFormat, dpi = saveDPI) + else: fileOut = None + if show: + plt.show() + plt.close() + return fileOut diff --git a/rrompy/hfengines/base/vector_problem_engine_base.py b/rrompy/hfengines/base/vector_fenics_engine_base.py similarity index 71% rename from rrompy/hfengines/base/vector_problem_engine_base.py rename to rrompy/hfengines/base/vector_fenics_engine_base.py index 0c57b08..44b877b 100644 --- a/rrompy/hfengines/base/vector_problem_engine_base.py +++ b/rrompy/hfengines/base/vector_fenics_engine_base.py @@ -1,224 +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 fenics as fen import numpy as np from matplotlib import pyplot as plt +from .fenics_engine_base import FenicsEngineBase 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'] +__all__ = ['VectorFenicsEngineBase'] -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. - degree_threshold: Threshold for ufl expression interpolation degree. - """ +class VectorFenicsEngineBase(FenicsEngineBase): + """Generic solver for parametric vector fenics problems.""" def __init__(self, degree_threshold : int = np.inf, - homogeneized : bool = False, verbosity : int = 10, - timestamp : bool = True): + verbosity : int = 10, timestamp : bool = True): super().__init__(degree_threshold = degree_threshold, - homogeneized = homogeneized, verbosity = verbosity, - timestamp = timestamp) + verbosity = verbosity, timestamp = timestamp) self.V = fen.VectorFunctionSpace(fen.UnitSquareMesh(10, 10), "P", 1) - self.npar = 0 - 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, - fenplotArgs : dict = {}, **figspecs): + def plot(self, u:Np1D, warping : List[callable] = None, + is_state : bool = False, name : str = "u", save : str = None, + what : strLst = 'all', saveFormat : str = "eps", + saveDPI : int = 100, show : bool = True, fenplotArgs : dict = {}, + **figspecs) -> str: """ 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. + is_state(optional): whether given u is value before multiplication + by c. Defaults to False. 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. fenplotArgs(optional): Optional arguments for fenplot. figspecs(optional key args): Optional arguments for matplotlib figure creation. + + Returns: + Output filename. """ + if not is_state and not self.isCEye: + return super().plot(u, warping, False, name, save, what, + saveFormat, saveDPI, show, fenplotArgs, + **figspecs) 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 = fenplot(uAb, warping = warping, title = "|{}_comp{}|".format(name, j, **fenplotArgs)) if self.V.mesh().geometric_dimension() > 1: 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 = fenplot(uPh, warping = warping, title = "phase({}_comp{})".format(name, j), **fenplotArgs) if self.V.mesh().geometric_dimension() > 1: 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 = fenplot(uRe, warping = warping, title = "Re({}_comp{})".format(name, j), **fenplotArgs) if self.V.mesh().geometric_dimension() > 1: 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 = fenplot(uIm, warping = warping, title = "Im({}_comp{})".format(name, j), **fenplotArgs) if self.V.mesh().geometric_dimension() > 1: 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 = fenplot(uVRe, warping = warping, title = "{}_Re".format(name), mode = "displacement", **fenplotArgs) if self.V.mesh().geometric_dimension() > 1: 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 = fenplot(uVIm, warping = warping, title = "{}_Im".format(name), mode = "displacement", **fenplotArgs) if self.V.mesh().geometric_dimension() > 1: 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, warping : List[callable] = None, name : str = "Mesh", - save : str = None, saveFormat : str = "eps", - saveDPI : int = 100, show : bool = True, - fenplotArgs : dict = {}, **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. - fenplotArgs(optional): Optional arguments for fenplot. - figspecs(optional key args): Optional arguments for matplotlib - figure creation. - """ - plt.figure(**figspecs) - fenplot(self.V.mesh(), warping = warping, **fenplotArgs) - 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 3f7fb0b..6dab090 100644 --- a/rrompy/hfengines/linear_problem/__init__.py +++ b/rrompy/hfengines/linear_problem/__init__.py @@ -1,49 +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 .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', - 'MembraneFractureEngineNoDomain' + 'ScatteringProblemEngine' ] diff --git a/rrompy/hfengines/linear_problem/bidimensional/__init__.py b/rrompy/hfengines/linear_problem/bidimensional/__init__.py deleted file mode 100644 index 54ca3ff..0000000 --- a/rrompy/hfengines/linear_problem/bidimensional/__init__.py +++ /dev/null @@ -1,38 +0,0 @@ -# Copyright (C) 2018 by the RROMPy authors -# -# This file is part of RROMPy. -# -# RROMPy is free software: you can redistribute it and/or modify -# it under the terms of the GNU Lesser General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. -# -# RROMPy is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU Lesser General Public License for more details. -# -# You should have received a copy of the GNU Lesser General Public License -# along with RROMPy. If not, see . -# - -from .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', - 'SyntheticBivariateEngine' - ] - - - diff --git a/rrompy/hfengines/linear_problem/bidimensional/cookie_engine_single.py b/rrompy/hfengines/linear_problem/bidimensional/cookie_engine_single.py deleted file mode 100644 index f304d45..0000000 --- a/rrompy/hfengines/linear_problem/bidimensional/cookie_engine_single.py +++ /dev/null @@ -1,118 +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 -import fenics as fen -import ufl -from rrompy.utilities.base.types import ScOp, List, paramVal -from rrompy.solver.fenics import fenONE, fenZERO -from rrompy.hfengines.linear_problem.helmholtz_problem_engine import ( - HelmholtzProblemEngine) -from rrompy.utilities.base import verbosityManager as vbMng -from rrompy.utilities.numerical import hashDerivativeToIdx as hashD -from rrompy.solver.fenics import fenics2Sparse - -__all__ = ['CookieEngineSingle'] - -class CookieEngineSingle(HelmholtzProblemEngine): - - 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, homogeneized : bool = False, - verbosity : int = 10, timestamp : bool = True): - super().__init__(mu0 = mu0, degree_threshold = degree_threshold, - homogeneized = homogeneized, verbosity = verbosity, - timestamp = timestamp) - self._affinePoly = False - self.nAs = 3 - self.npar = 2 - self.rescalingExp = [2., 2.] - mesh = fen.RectangleMesh(fen.Point(0., 0.), fen.Point(L * nX, L * nY), - n * nX, n * nY) - self.V = fen.FunctionSpace(mesh, "P", 1) - x, y = fen.SpatialCoordinate(mesh)[:] - 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 buildA(self): - """Build terms of operator of linear system.""" - if self.As[0] is None: - self.autoSetDS() - vbMng(self, "INIT", "Assembling operator term A0.", 20) - 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)) - self.As[0] = (fenics2Sparse(a0Re, parsRe, DirichletBC0, 1) - + 1.j * fenics2Sparse(a0Im, parsIm, DirichletBC0, 0)) - self.thAs[0] = self.getMonomialSingleWeight([0, 0]) - vbMng(self, "DEL", "Done assembling operator term.", 20) - if self.As[1] is None: - vbMng(self, "INIT", "Assembling operator term A1.", 20) - 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 * 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)) - self.thAs[1] = self.getMonomialSingleWeight([1, 0]) - vbMng(self, "DEL", "Done assembling operator term.", 20) - if self.As[2] is None: - vbMng(self, "INIT", "Assembling operator term A2.", 20) - 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 * self.cookieIn * fen.dot(self.u, self.v) * fen.dx - a2Im = - n2Im * self.cookieIn * fen.dot(self.u, self.v) * fen.dx - self.As[2] = (fenics2Sparse(a2Re, parsRe, DirichletBC0, 0) - + 1.j * fenics2Sparse(a2Im, parsIm, DirichletBC0, 0)) - self.thAs[2] = self.getMonomialSingleWeight([1, 1]) - vbMng(self, "DEL", "Done assembling operator term.", 20) diff --git a/rrompy/hfengines/linear_problem/helmholtz_problem_engine.py b/rrompy/hfengines/linear_problem/helmholtz_problem_engine.py index e470d63..685c819 100644 --- a/rrompy/hfengines/linear_problem/helmholtz_problem_engine.py +++ b/rrompy/hfengines/linear_problem/helmholtz_problem_engine.py @@ -1,132 +1,126 @@ # Copyright (C) 2018 by the RROMPy authors # # This file is part of RROMPy. # # RROMPy is free software: you can redistribute it and/or modify # it under the terms of the GNU Lesser General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # RROMPy is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU Lesser General Public License for more details. # # You should have received a copy of the GNU Lesser General Public License # along with RROMPy. If not, see . # import numpy as np import fenics as fen from .laplace_base_problem_engine import LaplaceBaseProblemEngine from rrompy.utilities.base.types import paramVal from rrompy.solver.fenics import fenZERO, fenONE from rrompy.utilities.base import verbosityManager as vbMng 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: - homogeneized: Whether to homogeneize Dirichlet BCs. 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. + cs: Numpy array representation of cs. 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. 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, - homogeneized : bool = False, verbosity : int = 10, - timestamp : bool = True): + verbosity : int = 10, timestamp : bool = True): super().__init__(mu0 = mu0, degree_threshold = degree_threshold, - homogeneized = homogeneized, verbosity = verbosity, - timestamp = timestamp) + verbosity = verbosity, timestamp = timestamp) self._affinePoly = True 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 buildA(self): """Build terms of operator of linear system.""" if self.thAs[0] is None: self.thAs = self.getMonomialWeights(self.nAs) if self.As[0] is None: self.autoSetDS() vbMng(self, "INIT", "Assembling operator term A0.", 20) 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)) self.As[0] = (fenics2Sparse(a0Re, parsRe, DirichletBC0, 1) + 1.j * fenics2Sparse(a0Im, parsIm, DirichletBC0, 0)) vbMng(self, "DEL", "Done assembling operator term.", 20) if self.As[1] is None: vbMng(self, "INIT", "Assembling operator term A1.", 20) 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 self.As[1] = (fenics2Sparse(a1Re, parsRe, DirichletBC0, 0) + 1.j * fenics2Sparse(a1Im, parsIm, DirichletBC0, 0)) vbMng(self, "DEL", "Done assembling operator term.", 20) diff --git a/rrompy/hfengines/linear_problem/laplace_base_problem_engine.py b/rrompy/hfengines/linear_problem/laplace_base_problem_engine.py index 48f8a21..bdad067 100644 --- a/rrompy/hfengines/linear_problem/laplace_base_problem_engine.py +++ b/rrompy/hfengines/linear_problem/laplace_base_problem_engine.py @@ -1,289 +1,265 @@ # Copyright (C) 2018 by the RROMPy authors # # This file is part of RROMPy. # # RROMPy is free software: you can redistribute it and/or modify # it under the terms of the GNU Lesser General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # RROMPy is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU Lesser General Public License for more details. # # You should have received a copy of the GNU Lesser General Public License # along with RROMPy. If not, see . # import numpy as np import fenics as fen -from rrompy.hfengines.base.problem_engine_base import ProblemEngineBase +from rrompy.hfengines.base.linear_affine_engine import LinearAffineEngine +from rrompy.hfengines.base.fenics_engine_base import FenicsEngineBase from rrompy.utilities.base.types import paramVal -from rrompy.solver.fenics import (fenZERO, fenONE, L2InverseNormMatrix, - H1NormMatrix, Hminus1NormMatrix) +from rrompy.solver.fenics import (fenZERO, fenONE, H1NormMatrix, + Hminus1NormMatrix) from rrompy.utilities.base import verbosityManager as vbMng from rrompy.parameter import checkParameter from rrompy.solver.fenics import fenics2Sparse, fenics2Vector __all__ = ['LaplaceBaseProblemEngine'] -class LaplaceBaseProblemEngine(ProblemEngineBase): +class LaplaceBaseProblemEngine(LinearAffineEngine, FenicsEngineBase): """ 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: - homogeneized: Whether to homogeneize Dirichlet BCs. 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. + cs: Numpy array representation of cs. 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. 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, - homogeneized : bool = False, verbosity : int = 10, - timestamp : bool = True): + verbosity : int = 10, timestamp : bool = True): super().__init__(degree_threshold = degree_threshold, - homogeneized = homogeneized, verbosity = verbosity, - timestamp = timestamp) + verbosity = verbosity, timestamp = timestamp) self._affinePoly = True 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 buildEnergyNormForm(self): """ Build sparse matrix (in CSR format) representative of scalar product. """ vbMng(self, "INIT", "Assembling energy matrix.", 20) self.energyNormMatrix = H1NormMatrix(self.V, np.abs(self.omega)**2) vbMng(self, "DEL", "Done assembling energy matrix.", 20) def buildEnergyNormDualForm(self): """ Build sparse matrix (in CSR format) representative of dual scalar product. """ vbMng(self, "INIT", "Assembling energy dual matrix.", 20) self.energyNormDualMatrix = Hminus1NormMatrix( self.V, np.abs(self.omega)**2, compressRank = self._energyDualNormCompress) vbMng(self, "DEL", "Done assembling energy dual matrix.", 20) - def buildDualityPairingForm(self): - """Build sparse matrix (in CSR format) representative of duality.""" - vbMng(self, "INIT", "Assembling duality matrix.", 20) - self.dualityMatrix = L2InverseNormMatrix( - self.V, solverType = self._solver, - solverArgs = self._solverArgs, - compressRank = self._dualityCompress) - vbMng(self, "DEL", "Done assembling duality matrix.", 20) - def buildEnergyNormPartialDualForm(self): """ Build sparse matrix (in CSR format) representative of dual scalar product without duality. """ vbMng(self, "INIT", "Assembling energy partial dual matrix.", 20) self.energyNormPartialDualMatrix = Hminus1NormMatrix( self.V, np.abs(self.omega)**2, - compressRank = self._energyDualNormCompress, - duality = False) + compressRank = self._energyDualNormCompress) vbMng(self, "DEL", "Done assembling energy partial dual matrix.", 20) def buildA(self): """Build terms of operator of linear system.""" if self.thAs[0] is None: self.thAs = self.getMonomialWeights(self.nAs) if self.As[0] is None: self.autoSetDS() vbMng(self, "INIT", "Assembling operator term A0.", 20) 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)) self.As[0] = (fenics2Sparse(a0Re, parsRe, DirichletBC0, 1) + 1.j * fenics2Sparse(a0Im, parsIm, DirichletBC0, 0)) vbMng(self, "DEL", "Done assembling operator term.", 20) def buildb(self): """Build terms of operator of linear system.""" if self.thbs[0] is None: - self.thbs = (self.getMonomialWeights(self.nbs) - + [None] * (self.homogeneized * self.nAs)) + self.thbs = self.getMonomialWeights(self.nbs) if self.bs[0] is None: self.autoSetDS() vbMng(self, "INIT", "Assembling forcing term b0.", 20) u0Re, u0Im = self.DirichletDatum fRe, fIm = self.forcingTerm g1Re, g1Im = self.NeumannDatum g2Re, g2Im = self.RobinDatumG termNames = ["forcingTerm", "NeumannDatum", "RobinDatumG"] parsRe = self.iterReduceQuadratureDegree(zip([fRe, g1Re, g2Re], [x + "Real" for x in termNames])) parsIm = self.iterReduceQuadratureDegree(zip([fIm, g1Im, g2Im], [x + "Imag" for x in termNames])) L0Re = (fen.dot(fRe, self.v) * fen.dx + fen.dot(g1Re, self.v) * self.ds(0) + fen.dot(g2Re, self.v) * self.ds(1)) L0Im = (fen.dot(fIm, self.v) * fen.dx + fen.dot(g1Im, self.v) * self.ds(0) + fen.dot(g2Im, self.v) * self.ds(1)) DBCR = fen.DirichletBC(self.V, u0Re, self.DirichletBoundary) DBCI = fen.DirichletBC(self.V, u0Im, self.DirichletBoundary) self.bs[0] = (fenics2Vector(L0Re, parsRe, DBCR, 1) + 1.j * fenics2Vector(L0Im, parsIm, DBCI, 1)) vbMng(self, "DEL", "Done assembling forcing term.", 20) - self.setbHomogeneized() + diff --git a/rrompy/hfengines/linear_problem/laplace_disk_gaussian.py b/rrompy/hfengines/linear_problem/laplace_disk_gaussian.py deleted file mode 100644 index 1cd6410..0000000 --- a/rrompy/hfengines/linear_problem/laplace_disk_gaussian.py +++ /dev/null @@ -1,88 +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 numpy.polynomial.polynomial import polyfit as fit -import fenics as fen -from rrompy.utilities.base.types import paramVal -from .laplace_base_problem_engine import LaplaceBaseProblemEngine -from rrompy.solver.fenics import fenZERO -from rrompy.utilities.base import verbosityManager as vbMng -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, homogeneized : bool = False, - verbosity : int = 10, timestamp : bool = True): - super().__init__(mu0 = mu0, degree_threshold = degree_threshold, - homogeneized = homogeneized, verbosity = verbosity, - timestamp = timestamp) - self.nbs = 19 - import mshr - mesh = mshr.generate_mesh(mshr.Circle(fen.Point(0., 0.), 5.), 3 * n) - self.V = fen.FunctionSpace(mesh, "P", 1) - - def buildb(self): - """Build terms of operator of linear system.""" - if self.thbs[0] is None: - self.thbs = (self.getMonomialWeights(self.nbs) - + [None] * (self.homogeneized * self.nAs)) - bDEIMCoeffs = None - for j in range(self.nbs): - if self.bs[j] is None: - vbMng(self, "INIT", "Assembling forcing term b{}.".format(j), - 20) - if bDEIMCoeffs is None: - bDEIM = np.empty((self.nbs, self.spacedim()), - dtype = np.complex) - muDEIM = 3. * np.linspace(0., 1., self.nbs // 2 + 1) ** 2. - muDEIM = np.concatenate((-muDEIM[:0:-1], muDEIM)) - for jj, muD in enumerate(muDEIM): - x, y = fen.SpatialCoordinate(self.V.mesh())[:] - C = np.exp(-.5 * muD ** 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(muD), np.imag(muD) - f1R = fen.exp(muR * x) * fen.cos(muI * x) - f1I = fen.exp(muR * x) * fen.sin(muI * x) - fRe = f0 * (CR * f1R - CI * f1I) + fenZERO - fIm = f0 * (CR * f1I + CI * f1R) + fenZERO - parsRe = self.iterReduceQuadratureDegree(zip([fRe], - ["forcingTerm{}Real".format(jj)])) - parsIm = self.iterReduceQuadratureDegree(zip([fIm], - ["forcingTerm{}Imag".format(jj)])) - LR = fen.dot(fRe, self.v) * fen.dx - LI = fen.dot(fIm, self.v) * fen.dx - DBC0 = fen.DirichletBC(self.V, fenZERO, - self.DirichletBoundary) - bDEIM[jj] = (fenics2Vector(LR, parsRe, DBC0, 1) - + 1.j * fenics2Vector(LI, parsIm, DBC0, 1)) - bDEIMCoeffs = (fit(muDEIM / 3., bDEIM, self.nbs - 1).T - * np.power(3., - np.arange(self.nbs))).T - self.bs[j] = bDEIMCoeffs[j] - vbMng(self, "DEL", "Done assembling forcing term.", 20) - self.setbHomogeneized() diff --git a/rrompy/hfengines/linear_problem/scattering_problem_engine.py b/rrompy/hfengines/linear_problem/scattering_problem_engine.py index 57c98ff..cc9d9f5 100644 --- a/rrompy/hfengines/linear_problem/scattering_problem_engine.py +++ b/rrompy/hfengines/linear_problem/scattering_problem_engine.py @@ -1,146 +1,139 @@ # 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 fenics as fen from rrompy.utilities.base.types import paramVal from rrompy.solver.fenics import fenZERO from rrompy.utilities.base import verbosityManager as vbMng from .helmholtz_problem_engine import HelmholtzProblemEngine 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: - homogeneized: Whether to homogeneize Dirichlet BCs. 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. + cs: Numpy array representation of cs. 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. 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. """ def __init__(self, mu0 : paramVal = [0.], degree_threshold : int = inf, - homogeneized : bool = False, verbosity : int = 10, - timestamp : bool = True): + verbosity : int = 10, timestamp : bool = True): self.silenceWarnings = True super().__init__(mu0 = mu0, degree_threshold = degree_threshold, - homogeneized = homogeneized, verbosity = verbosity, - timestamp = timestamp) + verbosity = verbosity, timestamp = timestamp) self._affinePoly = True del self.silenceWarnings self.nAs = 3 self.rescalingExp = [1.] self.signR = - 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 @property def signR(self): """Value of signR.""" return self._signR @signR.setter def signR(self, signR): self.resetAs() self._signR = signR def buildA(self): """Build terms of operator of linear system.""" if self.thAs[0] is None: self.thAs = self.getMonomialWeights(self.nAs) if self.As[0] is None: vbMng(self, "INIT", "Assembling operator term A0.", 20) 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 self.As[0] = (fenics2Sparse(a0Re, parsRe, DirichletBC0, 1) + 1.j * fenics2Sparse(a0Im, parsIm, DirichletBC0, 0)) vbMng(self, "DEL", "Done assembling operator term.", 20) if self.As[1] is None: self.autoSetDS() vbMng(self, "INIT", "Assembling operator term A1.", 20) DirichletBC0 = fen.DirichletBC(self.V, fenZERO, self.DirichletBoundary) a1 = fen.dot(self.u, self.v) * self.ds(1) self.As[1] = (self.signR * 1.j * fenics2Sparse(a1, {}, DirichletBC0, 0)) vbMng(self, "DEL", "Done assembling operator term.", 20) if self.As[2] is None: vbMng(self, "INIT", "Assembling operator term A2.", 20) 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 self.As[2] = (fenics2Sparse(a2Re, parsRe, DirichletBC0, 0) + 1.j * fenics2Sparse(a2Im, parsIm, DirichletBC0, 0)) vbMng(self, "DEL", "Done assembling operator term.", 20) diff --git a/rrompy/hfengines/linear_problem/tridimensional/__init__.py b/rrompy/hfengines/linear_problem/tridimensional/__init__.py deleted file mode 100644 index f0493e4..0000000 --- a/rrompy/hfengines/linear_problem/tridimensional/__init__.py +++ /dev/null @@ -1,30 +0,0 @@ -# Copyright (C) 2018 by the RROMPy authors -# -# This file is part of RROMPy. -# -# RROMPy is free software: you can redistribute it and/or modify -# it under the terms of the GNU Lesser General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. -# -# RROMPy is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU Lesser General Public License for more details. -# -# You should have received a copy of the GNU Lesser General Public License -# along with RROMPy. If not, see . -# - -from .membrane_fracture_engine3 import MembraneFractureEngine3 -from .scattering_1d import Scattering1d -from .matrix_dynamical_passive import MatrixDynamicalPassive - -__all__ = [ - 'MembraneFractureEngine3', - 'Scattering1d', - 'MatrixDynamicalPassive' - ] - - - diff --git a/rrompy/hfengines/vector_linear_problem/__init__.py b/rrompy/hfengines/vector_linear_problem/__init__.py index 805fc8b..689207b 100644 --- a/rrompy/hfengines/vector_linear_problem/__init__.py +++ b/rrompy/hfengines/vector_linear_problem/__init__.py @@ -1,32 +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 .linear_elasticity_problem_engine import LinearElasticityProblemEngine from .linear_elasticity_helmholtz_problem_engine import LinearElasticityHelmholtzProblemEngine from .linear_elasticity_helmholtz_problem_engine_damped import LinearElasticityHelmholtzProblemEngineDamped -from .linear_elasticity_beam_poisson_ratio import LinearElasticityBeamPoissonRatio __all__ = [ 'LinearElasticityProblemEngine', 'LinearElasticityHelmholtzProblemEngine', - 'LinearElasticityHelmholtzProblemEngineDamped', - 'LinearElasticityBeamPoissonRatio' + 'LinearElasticityHelmholtzProblemEngineDamped' ] diff --git a/rrompy/hfengines/vector_linear_problem/bidimensional/__init__.py b/rrompy/hfengines/vector_linear_problem/bidimensional/__init__.py deleted file mode 100644 index 3311e14..0000000 --- a/rrompy/hfengines/vector_linear_problem/bidimensional/__init__.py +++ /dev/null @@ -1,26 +0,0 @@ -# Copyright (C) 2018 by the RROMPy authors -# -# This file is part of RROMPy. -# -# RROMPy is free software: you can redistribute it and/or modify -# it under the terms of the GNU Lesser General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. -# -# RROMPy is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU Lesser General Public License for more details. -# -# You should have received a copy of the GNU Lesser General Public License -# along with RROMPy. If not, see . -# - -from .linear_elasticity_beam_elasticity_constants import LinearElasticityBeamElasticityConstants - -__all__ = [ - 'LinearElasticityBeamElasticityConstants' - ] - - - 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 deleted file mode 100644 index a91b95f..0000000 --- a/rrompy/hfengines/vector_linear_problem/bidimensional/linear_elasticity_beam_elasticity_constants.py +++ /dev/null @@ -1,120 +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 -import fenics as fen -from rrompy.hfengines.vector_linear_problem.\ - linear_elasticity_beam_poisson_ratio import LinearElasticityBeamPoissonRatio -from rrompy.solver.fenics import fenZERO, fenZEROS -from rrompy.utilities.base import verbosityManager as vbMng -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, - homogeneized = False, verbosity = verbosity, - timestamp = timestamp) - self._affinePoly = False - self.nAs, self.nbs = 3, 2 - self.npar = 2 - - 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 buildA(self): - """Build terms of operator of linear system.""" - if self.As[0] is None: - vbMng(self, "INIT", "Assembling operator term A0.", 20) - DirichletBC0 = fen.DirichletBC(self.V, fenZEROS(2), - self.DirichletBoundary) - a0Re = fenZERO * fen.inner(self.u, self.v) * fen.dx - self.As[0] = fenics2Sparse(a0Re, {}, DirichletBC0, 1) - self.thAs[0] = self.getMonomialSingleWeight([0, 0]) - vbMng(self, "DEL", "Done assembling operator term.", 20) - if self.As[1] is None: - vbMng(self, "INIT", "Assembling operator term A1.", 20) - DirichletBC0 = fen.DirichletBC(self.V, fenZEROS(2), - self.DirichletBoundary) - epsilon = lambda u: .5 * (fen.grad(u) + fen.nabla_grad(u)) - a1Re = fen.inner(epsilon(self.u), epsilon(self.v)) * fen.dx - self.As[1] = fenics2Sparse(a1Re, {}, DirichletBC0, 0) - self.thAs[1] = [('x', '()', 0, '*', -2., '+', 1., - '*', ('x', '()', 1)), - ('x', '()', 1, '*', -2.), - ('x', '()', 0, '*', -2.), - (0.,), (-2.,), None] - vbMng(self, "DEL", "Done assembling operator term.", 20) - if self.As[2] is None: - vbMng(self, "INIT", "Assembling operator term A2.", 20) - DirichletBC0 = fen.DirichletBC(self.V, fenZEROS(2), - self.DirichletBoundary) - a4Re = fen.div(self.u) * fen.div(self.v) * fen.dx - self.As[2] = fenics2Sparse(a4Re, {}, DirichletBC0, 0) - self.thAs[2] = self.getMonomialSingleWeight([1, 1]) - vbMng(self, "DEL", "Done assembling operator term.", 20) - - def buildb(self): - """Build terms of operator of linear system.""" - if self.thbs[0] is None: - self.thbs = [None] * (self.nbs + self.homogeneized * self.nAs) - if self.bs[0] is None: - vbMng(self, "INIT", "Assembling forcing term b0.", 20) - L0 = fen.inner(fenZEROS(2), self.v) * fen.dx - DBCR = fen.DirichletBC(self.V, self.DirichletDatum[0], - self.DirichletBoundary) - DBCI = fen.DirichletBC(self.V, self.DirichletDatum[1], - self.DirichletBoundary) - self.bs[0] = (fenics2Vector(L0, {}, DBCR, 1) - + 1.j * fenics2Vector(L0, {}, DBCI, 1)) - self.thbs[0] = self.getMonomialSingleWeight([0, 0]) - if self.bs[1] is None: - vbMng(self, "INIT", "Assembling forcing term b1.", 20) - 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)) - self.thbs[1] = [('x', '()', 0, '**', 2., '*', -2., '-', - ('x', '()', 0), '+', 1.), - ('x', '()', 0, '*', -4., '-', 1.), - (0.,), (-2.0,), None] - vbMng(self, "DEL", "Done assembling forcing term.", 20) - self.setbHomogeneized() 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 0f699f2..c6da9a8 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,178 +1,172 @@ # Copyright (C) 2018 by the RROMPy authors # # This file is part of RROMPy. # # RROMPy is free software: you can redistribute it and/or modify # it under the terms of the GNU Lesser General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # RROMPy is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU Lesser General Public License for more details. # # You should have received a copy of the GNU Lesser General Public License # along with RROMPy. If not, see . # import numpy as np import fenics as fen from .linear_elasticity_problem_engine import LinearElasticityProblemEngine from rrompy.utilities.base.types import paramVal from rrompy.solver.fenics import (fenZERO, fenZEROS, fenONE, elasticNormMatrix, elasticDualNormMatrix) from rrompy.utilities.base import verbosityManager as vbMng 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. + cs: Numpy array representation of cs. 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. 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, - homogeneized : bool = False, verbosity : int = 10, - timestamp : bool = True): + verbosity : int = 10, timestamp : bool = True): super().__init__(mu0 = [mu0], degree_threshold = degree_threshold, - homogeneized = homogeneized, verbosity = verbosity, - timestamp = timestamp) + verbosity = verbosity, timestamp = timestamp) self._affinePoly = True 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. """ vbMng(self, "INIT", "Assembling energy matrix.", 20) self.energyNormMatrix = elasticNormMatrix( self.V, self.lambda_[0], self.mu_[0], np.abs(self.omega)**2 * self.rho_[0]) vbMng(self, "DEL", "Done assembling energy matrix.", 20) def buildEnergyNormDualForm(self): """ Build sparse matrix (in CSR format) representative of dual scalar product. """ vbMng(self, "INIT", "Assembling energy dual matrix.", 20) self.energyNormDualMatrix = elasticDualNormMatrix( self.V, self.lambda_[0], self.mu_[0], np.abs(self.omega)**2 * self.rho_[0], compressRank = self._energyDualNormCompress) vbMng(self, "DEL", "Done assembling energy dual matrix.", 20) def buildEnergyNormPartialDualForm(self): """ Build sparse matrix (in CSR format) representative of dual scalar product without duality. """ vbMng(self, "INIT", "Assembling energy partial dual matrix.", 20) self.energyNormPartialDualMatrix = elasticDualNormMatrix( self.V, self.lambda_[0], self.mu_[0], np.abs(self.omega)**2 * self.rho_[0], - compressRank = self._energyDualNormCompress, - duality = False) + compressRank = self._energyDualNormCompress) vbMng(self, "DEL", "Done assembling energy partial dual matrix.", 20) def buildA(self): """Build terms of operator of linear system.""" if self.thAs[0] is None: self.thAs = self.getMonomialWeights(self.nAs) if self.As[0] is None: self.autoSetDS() vbMng(self, "INIT", "Assembling operator term A0.", 20) 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)) self.As[0] = (fenics2Sparse(a0Re, parsRe, DirichletBC0, 1) + 1.j * fenics2Sparse(a0Im, parsIm, DirichletBC0, 0)) vbMng(self, "DEL", "Done assembling operator term.", 20) if self.As[1] is None: vbMng(self, "INIT", "Assembling operator term A1.", 20) 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 self.As[1] = (fenics2Sparse(a1Re, parsRe, DirichletBC0, 0) + 1.j * fenics2Sparse(a1Im, parsIm, DirichletBC0, 0)) vbMng(self, "DEL", "Done assembling operator term.", 20) 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 6329722..f36becf 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,163 +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 .linear_elasticity_helmholtz_problem_engine import \ LinearElasticityHelmholtzProblemEngine from rrompy.utilities.base.types import paramVal from rrompy.solver.fenics import fenZERO, fenZEROS from rrompy.utilities.base import verbosityManager as vbMng 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. + cs: Numpy array representation of cs. 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. 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, - homogeneized : bool = False, verbosity : int = 10, - timestamp : bool = True): + verbosity : int = 10, timestamp : bool = True): super().__init__(mu0 = [mu0], degree_threshold = degree_threshold, - homogeneized = homogeneized, verbosity = verbosity, - timestamp = timestamp) + verbosity = verbosity, timestamp = timestamp) self._affinePoly = True 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 buildA(self): """Build terms of operator of linear system.""" if self.thAs[0] is None: self.thAs = self.getMonomialWeights(self.nAs) if self.As[0] is None: self.autoSetDS() vbMng(self, "INIT", "Assembling operator term A0.", 20) 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)) self.As[0] = (fenics2Sparse(a0Re, parsRe, DirichletBC0, 1) + 1.j * fenics2Sparse(a0Im, parsIm, DirichletBC0, 0)) vbMng(self, "DEL", "Done assembling operator term.", 20) if self.As[1] is None: vbMng(self, "INIT", "Assembling operator term A1.", 20) 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 self.As[1] = (fenics2Sparse(a1Re, parsRe, DirichletBC0, 0) + 1.j * fenics2Sparse(a1Im, parsIm, DirichletBC0, 0)) vbMng(self, "DEL", "Done assembling operator term.", 20) if self.As[2] is None: vbMng(self, "INIT", "Assembling operator term A2.", 20) 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 self.As[2] = (fenics2Sparse(a2Re, parsRe, DirichletBC0, 0) + 1.j * fenics2Sparse(a2Im, parsIm, DirichletBC0, 0)) vbMng(self, "DEL", "Done assembling operator term.", 20) 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 60cc8a0..66ca4fd 100644 --- a/rrompy/hfengines/vector_linear_problem/linear_elasticity_problem_engine.py +++ b/rrompy/hfengines/vector_linear_problem/linear_elasticity_problem_engine.py @@ -1,321 +1,306 @@ # Copyright (C) 2018 by the RROMPy authors # # This file is part of RROMPy. # # RROMPy is free software: you can redistribute it and/or modify # it under the terms of the GNU Lesser General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # RROMPy is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU Lesser General Public License for more details. # # You should have received a copy of the GNU Lesser General Public License # along with RROMPy. If not, see . # import numpy as np import fenics as fen -from rrompy.hfengines.base.vector_problem_engine_base import \ - VectorProblemEngineBase +from rrompy.hfengines.base.linear_affine_engine import LinearAffineEngine +from rrompy.hfengines.base.vector_fenics_engine_base import \ + VectorFenicsEngineBase from rrompy.utilities.base.types import paramVal from rrompy.solver.fenics import (fenZERO, fenZEROS, fenONE, - L2InverseNormMatrix, elasticNormMatrix, - elasticDualNormMatrix) + elasticNormMatrix, elasticDualNormMatrix) from rrompy.utilities.base import verbosityManager as vbMng from rrompy.parameter import checkParameter from rrompy.solver.fenics import fenics2Sparse, fenics2Vector __all__ = ['LinearElasticityProblemEngine'] -class LinearElasticityProblemEngine(VectorProblemEngineBase): +class LinearElasticityProblemEngine(LinearAffineEngine, + VectorFenicsEngineBase): """ 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: - homogeneized: Whether to homogeneize Dirichlet BCs. 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. + cs: Numpy array representation of cs. 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. 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, - homogeneized : bool = False, verbosity : int = 10, - timestamp : bool = True): + verbosity : int = 10, timestamp : bool = True): super().__init__(degree_threshold = degree_threshold, - homogeneized = homogeneized, verbosity = verbosity, - timestamp = timestamp) + verbosity = verbosity, timestamp = timestamp) self._affinePoly = True 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) + VectorFenicsEngineBase.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 buildEnergyNormForm(self): """ Build sparse matrix (in CSR format) representative of scalar product. """ vbMng(self, "INIT", "Assembling energy matrix.", 20) self.energyNormMatrix = elasticNormMatrix(self.V, self.lambda_[0], self.mu_[0]) vbMng(self, "DEL", "Done assembling energy matrix.", 20) def buildEnergyNormDualForm(self): """ Build sparse matrix (in CSR format) representative of dual scalar product. """ vbMng(self, "INIT", "Assembling energy dual matrix.", 20) self.energyNormDualMatrix = elasticDualNormMatrix( self.V, self.lambda_[0], self.mu_[0], compressRank = self._energyDualNormCompress) vbMng(self, "DEL", "Done assembling energy dual matrix.", 20) - def buildDualityPairingForm(self): - """Build sparse matrix (in CSR format) representative of duality.""" - vbMng(self, "INIT", "Assembling duality matrix.", 20) - self.dualityMatrix = L2InverseNormMatrix( - self.V, solverType = self._solver, - solverArgs = self._solverArgs, - compressRank = self._dualityCompress) - vbMng(self, "DEL", "Done assembling duality matrix.", 20) - def buildEnergyNormPartialDualForm(self): """ Build sparse matrix (in CSR format) representative of dual scalar product without duality. """ vbMng(self, "INIT", "Assembling energy partial dual matrix.", 20) self.energyNormPartialDualMatrix = elasticDualNormMatrix( self.V, self.lambda_[0], self.mu_[0], - compressRank = self._energyDualNormCompress, - duality = False) + compressRank = self._energyDualNormCompress) vbMng(self, "DEL", "Done assembling energy partial dual matrix.", 20) def buildA(self): """Build terms of operator of linear system.""" if self.thAs[0] is None: self.thAs = self.getMonomialWeights(self.nAs) if self.As[0] is None: self.autoSetDS() vbMng(self, "INIT", "Assembling operator term A0.", 20) 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)) self.As[0] = (fenics2Sparse(a0Re, parsRe, DirichletBC0, 1) + 1.j * fenics2Sparse(a0Im, parsIm, DirichletBC0, 0)) vbMng(self, "DEL", "Done assembling operator term.", 20) def buildb(self): """Build terms of operator of linear system.""" if self.thbs[0] is None: - self.thbs = (self.getMonomialWeights(self.nbs) - + [None] * (self.homogeneized * self.nAs)) + self.thbs = self.getMonomialWeights(self.nbs) if self.bs[0] is None: self.autoSetDS() vbMng(self, "INIT", "Assembling forcing term b0.", 20) u0Re, u0Im = self.DirichletDatum fRe, fIm = self.forcingTerm g1Re, g1Im = self.NeumannDatum g2Re, g2Im = self.RobinDatumG 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)) DBCR = fen.DirichletBC(self.V, u0Re, self.DirichletBoundary) DBCI = fen.DirichletBC(self.V, u0Im, self.DirichletBoundary) self.bs[0] = (fenics2Vector(L0Re, parsRe, DBCR, 1) + 1.j * fenics2Vector(L0Im, parsIm, DBCI, 1)) vbMng(self, "DEL", "Done assembling forcing term.", 20) - self.setbHomogeneized() + diff --git a/rrompy/parameter/parameter_list.py b/rrompy/parameter/parameter_list.py index 93d514a..caa2536 100644 --- a/rrompy/parameter/parameter_list.py +++ b/rrompy/parameter/parameter_list.py @@ -1,223 +1,227 @@ # 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 itertools import product as iterprod from copy import deepcopy as copy from rrompy.utilities.exception_manager import RROMPyException, RROMPyAssert from rrompy.utilities.base.types import Np2D __all__ = ['parameterList', 'emptyParameterList', 'checkParameterList'] def checkParameterList(mu, npar = None): if not isinstance(mu, (parameterList,)): mu = parameterList(mu, npar) else: if npar is not None: RROMPyAssert(mu.shape[1], npar, "Number of parameters") mu = copy(mu) return mu, len(mu) <= 1 def checkParameter(mu, npar = None): muL, wasPar = checkParameterList(mu, npar) if not wasPar: muL, wasPar = checkParameterList([mu], npar) if not wasPar: raise RROMPyException(("Only single parameter allowed. No " "parameter lists here.")) return muL def emptyParameterList(): return parameterList([[]]) def addMemberFromNumpyArray(self, fieldName): def objFunc(self, other): if not isinstance(other, (self.__class__,)): other = parameterList(other, self.shape[1]) return parameterList(getattr(np.ndarray, fieldName)(self.data, other.data)) setattr(self.__class__, fieldName, objFunc) def objIFunc(self, other): self.data = getattr(self.__class__, fieldName)(self, other).data setattr(self.__class__, "__i" + fieldName[2:], objIFunc) class parameterList: """HERE""" __all__ += [pre + post for pre, post in iterprod(["__", "__i"], ["__add__", "__sub__", "__mul__", "__div__", "__truediv__", "__floordiv__", "__pow__"])] def __init__(self, data:Np2D, lengthCheck : int = None): if not hasattr(data, "__len__"): data = [data] elif isinstance(data, (self.__class__,)): data = data.data elif isinstance(data, (tuple,)): data = list(data) if (isinstance(data, (list,)) and len(data) > 0 and isinstance(data[0], (tuple,))): data = [list(x) for x in data] self.data = np.array(data, ndmin = 1, copy = 1) if self.data.ndim == 1: self.data = self.data[:, None] if np.size(self.data) > 0: self.data = self.data.reshape((len(self), -1)) if self.shape[0] * self.shape[1] == 0: lenEff = 0 if lengthCheck is None else lengthCheck self.reset((0, lenEff), self.dtype) if lengthCheck is not None: if lengthCheck != 1 and self.shape == (lengthCheck, 1): self.data = self.data.T RROMPyAssert(self.shape[1], lengthCheck, "Number of parameters") for fieldName in ["__add__", "__sub__", "__mul__", "__div__", "__truediv__", "__floordiv__", "__pow__"]: addMemberFromNumpyArray(self, fieldName) def __len__(self): return self.shape[0] def __str__(self): if len(self) == 0: selfstr = "[]" elif len(self) <= 3: selfstr = "[{}]".format(" ".join([str(x) for x in self.data])) else: selfstr = "[{} ..({}).. {}]".format(self[0], len(self) - 2, self[-1]) return selfstr def __repr__(self): return repr(self.data) @property def shape(self): return self.data.shape + @property + def size(self): + return self.data.size + @property def re(self): return parameterList(np.real(self.data)) @property def im(self): return parameterList(np.imag(self.data)) @property def abs(self): return parameterList(np.abs(self.data)) @property def angle(self): return parameterList(np.angle(self.data)) @property def conj(self): return parameterList(np.conj(self.data)) @property def dtype(self): return self.data.dtype def __getitem__(self, key): return self.data[key] def __call__(self, key, idx = None): if idx is None: return self.data[:, key] return self[key, idx] def __setitem__(self, key, value): if isinstance(key, (tuple, list,)): RROMPyAssert(len(key), len(value), "Slice length") for k, val in zip(key, value): self[k] = val else: self.data[key] = value def __eq__(self, other): if not hasattr(other, "shape") or self.shape != other.shape: return False if isinstance(other, self.__class__): other = other.data return np.allclose(self.data, other) def __contains__(self, item): return next((x for x in self if np.allclose(x[0], item)), -1) != -1 def __iter__(self): return iter([parameterList([x]) for x in self.data]) def __copy__(self): return parameterList(self.data) def __deepcopy__(self, memo): return parameterList(copy(self.data, memo)) def __neg__(self): return parameterList(-self.data) def __pos__(self): return copy(self) def reset(self, size, dtype = complex): self.data = np.empty(size, dtype = dtype) self.data[:] = np.nan def append(self, items): if isinstance(items, self.__class__): items = items.data else: items = np.array(items, ndmin = 2) if len(self) == 0: self.data = parameterList(items).data else: self.data = np.append(self.data, items, axis = 0) def pop(self, idx = -1): self.data = np.delete(self.data, idx, axis = 0) def find(self, item): if len(self) == 0: return None return next((j for j in range(len(self)) if np.allclose(self[j], item)), None) def findall(self, item): if len(self) == 0: return [] return [j for j in range(len(self)) if np.allclose(self[j], item)] def sort(self, overwrite = False, *args, **kwargs): dataT = np.array([tuple(x[0]) for x in self], dtype = [(str(j), self.dtype) for j in range(self.shape[1])]) sortedP = parameterList([list(x) for x in np.sort(dataT, *args, **kwargs)]) if overwrite: self.data = sortedP.data return sortedP def unique(self, overwrite = False, *args, **kwargs): dataT = np.array([tuple(x[0]) for x in self], dtype = [(str(j), self.dtype) for j in range(self.shape[1])]) uniqueT = np.unique(dataT, *args, **kwargs) if isinstance(uniqueT, (tuple,)): extraT = uniqueT[1:] uniqueT = uniqueT[0] else: extraT = () uniqueP = parameterList([list(x) for x in uniqueT]) if overwrite: self.data = uniqueP.data uniqueP = (uniqueP,) + extraT if len(uniqueP) == 1: return uniqueP[0] return uniqueP diff --git a/rrompy/reduction_methods/base/generic_approximant.py b/rrompy/reduction_methods/base/generic_approximant.py index 7351d4d..43f6e12 100644 --- a/rrompy/reduction_methods/base/generic_approximant.py +++ b/rrompy/reduction_methods/base/generic_approximant.py @@ -1,853 +1,907 @@ # 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.standard import (SamplingEngineStandard, SamplingEngineStandardPOD) from rrompy.utilities.base.types import (Np1D, DictAny, HFEng, List, Tuple, ListAny, strLst, paramVal, paramList, sampList) from rrompy.utilities.base import (purgeDict, verbosityManager as vbMng, 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, *args, **kwargs) -> Np1D: uV = getattr(self.__class__, "get" + fieldName)(self, mu) + kwargs["is_state"] = False val = self.HFEngine.norm(uV, *args, **kwargs) return val setattr(self.__class__, "norm" + fieldName, objFunc) def addNormDualFieldToClass(self, fieldName): def objFunc(self, mu:paramList, *args, **kwargs) -> Np1D: uV = getattr(self.__class__, "get" + fieldName)(self, mu) + kwargs["is_state"] = True if "dual" not in kwargs.keys(): kwargs["dual"] = True val = self.HFEngine.norm(uV, *args, **kwargs) return val setattr(self.__class__, "norm" + fieldName, objFunc) def addPlotFieldToClass(self, fieldName): def objFunc(self, mu:paramList, *args, **kwargs): uV = getattr(self.__class__, "get" + fieldName)(self, mu) kwargsCopy = copy(kwargs) + filesOut = [] for j, u in enumerate(uV): if "name" in kwargs.keys(): kwargsCopy["name"] = kwargs["name"] + str(j) - self.HFEngine.plot(u, *args, **kwargs) + filesOut += [self.HFEngine.plot(u, *args, **kwargsCopy)] + if filesOut[0] is None: return None + return filesOut setattr(self.__class__, "plot" + fieldName, objFunc) def addPlotDualFieldToClass(self, fieldName): def objFunc(self, mu:paramList, *args, **kwargs): - uV = getattr(self.__class__, "get" + fieldName)(self, mu, - duality = False) + uV = getattr(self.__class__, "get" + fieldName)(self, mu) kwargsCopy = copy(kwargs) + filesOut = [] for j, u in enumerate(uV): if "name" in kwargs.keys(): kwargsCopy["name"] = kwargs["name"] + str(j) - self.HFEngine.plot(u, *args, **kwargs) + filesOut += [self.HFEngine.plot(u, *args, **kwargsCopy)] + if filesOut[0] is None: return None + return filesOut setattr(self.__class__, "plot" + fieldName, objFunc) def addOutParaviewFieldToClass(self, fieldName): def objFunc(self, mu:paramVal, *args, **kwargs): if not hasattr(self.HFEngine, "outParaview"): raise RROMPyException(("High fidelity engine cannot output to " "Paraview.")) uV = getattr(self.__class__, "get" + fieldName)(self, mu) kwargsCopy = copy(kwargs) + filesOut = [] for j, u in enumerate(uV): if "name" in kwargs.keys(): kwargsCopy["name"] = kwargs["name"] + str(j) - self.HFEngine.outParaview(u, *args, **kwargsCopy) + filesOut += [self.HFEngine.outParaview(u, *args, **kwargsCopy)] + if filesOut[0] is None: return None + return filesOut setattr(self.__class__, "outParaview" + fieldName, objFunc) def addOutParaviewTimeDomainFieldToClass(self, fieldName): def objFunc(self, mu:paramVal, *args, **kwargs): if not hasattr(self.HFEngine, "outParaviewTimeDomain"): raise RROMPyException(("High fidelity engine cannot output to " "Paraview.")) uV = getattr(self.__class__, "get" + fieldName)(self, mu) omega = args.pop(0) if len(args) > 0 else np.real(mu) kwargsCopy = copy(kwargs) + filesOut = [] for j, u in enumerate(uV): if "name" in kwargs.keys(): kwargsCopy["name"] = kwargs["name"] + str(j) - self.HFEngine.outParaviewTimeDomain(u, omega, *args, - **kwargsCopy) + filesOut += [self.HFEngine.outParaviewTimeDomain(u, omega, *args, + **kwargsCopy)] + if filesOut[0] is None: return None + return filesOut setattr(self.__class__, "outParaviewTimeDomain" + fieldName, objFunc) +def getTrainedModelClass(name): + from importlib import import_module as im + try: + return getattr(im("rrompy.reduction_methods.trained_model"), name) + except: + raise RROMPyException("Trained model name not recognized.") + 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. + approx_state(optional): Whether to approximate state. Defaults to + False. verbosity(optional): Verbosity level. Defaults to 10. Attributes: HFEngine: HF problem solver. trainedModel: Trained model evaluator. mu0: Default parameter. 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. + approx_state: Whether to approximate state. 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. uApproxReduced: Reduced approximate solution(s) with parameter(s) lastSolvedApprox as sampleList. lastSolvedApproxReduced: Parameter(s) corresponding to last computed reduced approximate solution(s) as parameterList. uApprox: Approximate solution(s) with parameter(s) lastSolvedApprox as sampleList. 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 = {}, verbosity : int = 10, - timestamp : bool = True): + approxParameters : DictAny = {}, approx_state : bool = False, + verbosity : int = 10, timestamp : bool = True): self._preInit() self._mode = RROMPy_READY + self.approx_state = approx_state self.verbosity = verbosity self.timestamp = timestamp vbMng(self, "INIT", "Initializing engine of type {}.".format(self.name()), 10) 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.approxParameters = approxParameters self._postInit() - ### add norm{HF,Approx,Err} methods + ### add norm{HF,Err} methods """ Compute norm of * at arbitrary parameter. Args: mu: Target parameter. Returns: Target norm of *. """ for objName in ["HF", "Err"]: addNormFieldToClass(self, objName) - if not hasattr(self, "normApprox"): - addNormFieldToClass(self, "Approx") ### add norm{RHS,Res} methods """ Compute norm of * at arbitrary parameter. Args: mu: Target parameter. Returns: Target norm of *. """ for objName in ["RHS", "Res"]: addNormDualFieldToClass(self, objName) ### add plot{HF,Approx,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. figspecs(optional key args): Optional arguments for matplotlib figure creation. """ for objName in ["HF", "Approx", "Err"]: addPlotFieldToClass(self, objName) ### add plot{RHS,Res} 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. figspecs(optional key args): Optional arguments for matplotlib figure creation. """ for objName in ["RHS", "Res"]: addPlotDualFieldToClass(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). """ 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. """ 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 tModelType(self): + raise RROMPyException("No trainedModel type assigned.") + + def initializeModelData(self, datadict): + from rrompy.reduction_methods.trained_model import TrainedModelData + return (TrainedModelData(datadict["mu0"], datadict.pop("projMat"), + datadict["scaleFactor"], + datadict.pop("rescalingExp")), + ["mu0", "scaleFactor", "mus"]) + @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 + self.parameterToBeExcluded 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.parameterListSoft self.parameterDefaultSoft[what] = defaultSoft[j] for j, what in enumerate(whatCritical): if what not in self.parameterToBeExcluded: self.parameterListCritical = ([what] + self.parameterListCritical) self.parameterDefaultCritical[what] = defaultCritical[j] def _postInit(self): if self.depth == 0: vbMng(self, "DEL", "Done initializing.", 10) 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 = SamplingEngineStandardPOD else: SamplingEngine = SamplingEngineStandard self.samplingEngine = SamplingEngine(self.HFEngine, + sample_state = self.approx_state, verbosity = self.verbosity) @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 approx_state(self): + """Value of approx_state.""" + return self._approx_state + @approx_state.setter + def approx_state(self, approx_state): + if hasattr(self, "_approx_state"): approx_stateold = self.approx_state + else: approx_stateold = -1 + self._approx_state = approx_state + if approx_stateold != self.approx_state: + self.samplingEngine = None + self.resetSamples() + @property def S(self): """Value of S.""" return self._S @S.setter def S(self, S): if S <= 0: raise RROMPyException("S must be positive.") if hasattr(self, "_S") and self._S is not None: Sold = self.S else: Sold = -1 self._S = S self._approxParameters["S"] = self.S if Sold != self.S: self.resetSamples() @property def 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.lastSolvedApproxReduced = emptyParameterList() - self._trainedModel.lastSolvedApprox = emptyParameterList() + self._trainedModel.reset() 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, warping : List[callable] = None, name : str = "u", save : str = None, what : strLst = 'all', saveFormat : str = "eps", saveDPI : int = 100, - show : bool = True, plotArgs : dict = {}, **figspecs): + show : bool = True, plotArgs : dict = {}, + **figspecs) -> List[str]: """ Do some nice plots of the samples. Args: warping(optional): Domain warping functions. 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. plotArgs(optional): Optional arguments for fen/pyplot. figspecs(optional key args): Optional arguments for matplotlib figure creation. + + Returns: + Output filenames. """ RROMPyAssert(self._mode, message = "Cannot plot samples.") - self.samplingEngine.plotSamples(warping, name, save, what, saveFormat, - saveDPI, show, plotArgs, **figspecs) + return self.samplingEngine.plotSamples(warping, name, save, what, + saveFormat, saveDPI, show, + plotArgs, **figspecs) def outParaviewSamples(self, name : str = "u", filename : str = "out", times : Np1D = None, what : strLst = 'all', forceNewFile : bool = True, folders : bool = False, - filePW = None): + filePW = None) -> List[str]: """ 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). + + Returns: + Output filenames. """ RROMPyAssert(self._mode, message = "Cannot output samples.") - self.samplingEngine.outParaviewSamples(name = name, - filename = filename, - times = times, what = what, - forceNewFile = forceNewFile, - folders = folders, - filePW = filePW) + return self.samplingEngine.outParaviewSamples(name, folders, filename, + times, what, + forceNewFile, 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): + folders : bool = False) -> List[str]: """ 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. + + Returns: + Output filenames. """ RROMPyAssert(self._mode, message = "Cannot output samples.") - self.samplingEngine.outParaviewTimeDomainSamples(omegas = omegas, - timeFinal = timeFinal, - periodResolution = periodResolution, - name = name, filename = filename, - forceNewFile = forceNewFile, - folders = folders) + return self.samplingEngine.outParaviewTimeDomainSamples( + omegas, timeFinal, + periodResolution, + name, folders, filename, + forceNewFile) def setSamples(self, samplingEngine): """Copy samplingEngine and samples.""" vbMng(self, "INIT", "Transfering samples.", 10) self.samplingEngine = copy(samplingEngine) vbMng(self, "DEL", "Done transfering samples.", 10) 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 _pruneBeforeEval(self, mu:paramList, field:str, append:bool, prune:bool) -> Tuple[paramList, Np1D, Np1D, bool]: mu = checkParameterList(mu, self.npar)[0] idx = np.empty(len(mu), dtype = np.int) if prune: jExtra = np.zeros(len(mu), dtype = bool) 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 = 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: 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)[0] 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: vbMng(self, "INIT", "Solving HF model for mu = {}.".format(mu), 15) newuHFs = self.HFEngine.solve(muExtra) vbMng(self, "DEL", "Done solving HF model.", 15) idx[jExtra] = self.setHF(muExtra, newuHFs, append) return list(idx) def setApproxReduced(self, muApproxR:paramList, uApproxR:sampleList, append : bool = False) -> List[int]: """Assign high fidelity solution.""" 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() muExtra, jExtra, idx, append = self._pruneBeforeEval(mu, "ApproxReduced", append, prune) if len(muExtra) > 0: newuApproxs = self.trainedModel.getApproxReduced(muExtra) idx[jExtra] = self.setApproxReduced(muExtra, newuApproxs, append) return list(idx) def setApprox(self, muApprox:paramList, uApprox:sampleList, append : bool = False) -> List[int]: """Assign high fidelity solution.""" 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() muExtra, jExtra, idx, append = self._pruneBeforeEval(mu, "Approx", append, prune) if len(muExtra) > 0: newuApproxs = self.trainedModel.getApprox(muExtra) idx[jExtra] = self.setApprox(muExtra, newuApproxs, append) return list(idx) def getHF(self, mu:paramList, append : bool = False, prune : bool = True) -> sampList: """ Get HF solution at arbitrary parameter. Args: mu: Target parameter. Returns: HFsolution. """ mu = checkParameterList(mu, self.npar)[0] idx = self.evalHF(mu, append = append, prune = prune) return self.uHF(idx) - def getRHS(self, mu:paramList, duality : bool = True) -> sampList: + def getRHS(self, mu:paramList) -> sampList: """ Get linear system RHS at arbitrary parameter. Args: mu: Target parameter. Returns: Linear system RHS. """ - return self.HFEngine.residual(mu, None, duality = duality) + return self.HFEngine.residual(mu, None) 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)[0] idx = self.evalApproxReduced(mu, append = append, prune = prune) return self.uApproxReduced(idx) def getApprox(self, mu:paramList, append : bool = False, prune : bool = True) -> sampList: """ Get approximant at arbitrary parameter. Args: mu: Target parameter. Returns: Approximant. """ mu = checkParameterList(mu, self.npar)[0] idx = self.evalApprox(mu, append = append, prune = prune) return self.uApprox(idx) - def getRes(self, mu:paramList, duality : bool = True) -> sampList: + def getRes(self, mu:paramList) -> sampList: """ Get residual at arbitrary parameter. Args: mu: Target parameter. Returns: Approximant residual. """ - return self.HFEngine.residual(mu, self.getApprox(mu), - duality = duality) + if not (self.approx_state or self.HFEngine.isCEye): + raise RROMPyException(("Residual of solution with non-scalar C " + "not computable.")) + return self.HFEngine.residual(mu, self.getApprox(mu) / self.HFEngine.C) def getErr(self, mu:paramList, append : bool = False, prune : bool = True) -> sampList: """ Get error at arbitrary parameter. Args: mu: Target parameter. Returns: Approximant error. """ return (self.getApprox(mu, append = append, prune =prune) - self.getHF(mu, append = append, prune = prune)) + def normApprox(self, mu:paramList) -> float: + """ + Compute norm of approximant at arbitrary parameter. + + Args: + mu: Target parameter. + + Returns: + Target norm of approximant. + """ + if not (self.POD and self.HFEngine.isCEye): + return self.HFEngine.norm(self.getApprox(mu), is_state = False) + return np.linalg.norm(self.HFEngine.C * self.getApproxReduced(mu).data, + axis = 0) + def getPoles(self, *args, **kwargs) -> Np1D: """ Obtain approximant poles. Returns: Numpy complex vector of poles. """ self.setupApprox() vbMng(self, "INIT", "Computing poles of model.", 20) poles = self.trainedModel.getPoles(*args, **kwargs) vbMng(self, "DEL", "Done computing poles.", 20) return poles def storeTrainedModel(self, filenameBase : str = "trained_model", forceNewFile : bool = True) -> str: """Store trained reduced model to file.""" self.setupApprox() vbMng(self, "INIT", "Storing trained model to file.", 20) if forceNewFile: filename = getNewFilename(filenameBase, "pkl") else: filename = "{}.pkl".format(filenameBase) pickleDump(self.trainedModel.data.__dict__, filename) vbMng(self, "DEL", "Done storing trained model.", 20) return filename def loadTrainedModel(self, filename:str): """Load trained reduced model from file.""" vbMng(self, "INIT", "Loading pre-trained model from file.", 20) datadict = pickleLoad(filename) - name = datadict.pop("name") - if name == "TrainedModelRational": - from rrompy.reduction_methods.trained_model import \ - TrainedModelRational as tModel - elif name == "TrainedModelReducedBasis": - from rrompy.reduction_methods.trained_model import \ - TrainedModelReducedBasis 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() + self.mu0 = datadict["mu0"] + self.scaleFactor = datadict["scaleFactor"] + self.mus = datadict["mus"] + trainedModel = self.tModelType() trainedModel.verbosity = self.verbosity trainedModel.timestamp = self.timestamp - self.scaleFactor = datadict.pop("scaleFactor") - data = TrainedModelData(name, self.mu0, datadict.pop("projMat"), - self.scaleFactor, datadict.pop("rescalingExp")) - if "mus" in datadict: - data.mus = datadict.pop("mus") + data, selfkeys = self.initializeModelData(datadict) + for key in selfkeys: setattr(self, key, datadict.pop(key)) 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) - for key in datadict: - setattr(data, key, datadict[key]) + for apkey in data.approxParameters.keys(): + self._approxParameters[apkey] = approxParameters.pop(apkey) + setattr(self, "_" + apkey, self._approxParameters[apkey]) + for key in datadict: setattr(data, key, datadict[key]) trainedModel.data = data self.trainedModel = trainedModel self._mode = RROMPy_FRAGILE vbMng(self, "DEL", "Done loading pre-trained model.", 20) - diff --git a/rrompy/reduction_methods/greedy/generic_greedy_approximant.py b/rrompy/reduction_methods/greedy/generic_greedy_approximant.py index 9f7ce60..6ead3a8 100644 --- a/rrompy/reduction_methods/greedy/generic_greedy_approximant.py +++ b/rrompy/reduction_methods/greedy/generic_greedy_approximant.py @@ -1,672 +1,702 @@ # 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.hfengines.base.linear_affine_engine import checkIfAffine from rrompy.reduction_methods.standard.generic_standard_approximant \ import GenericStandardApproximant from rrompy.utilities.base.types import (Np1D, Np2D, DictAny, HFEng, Tuple, List, normEng, paramVal, paramList, sampList) from rrompy.utilities.base import verbosityManager as vbMng from rrompy.utilities.numerical import dot from rrompy.utilities.expression import expressionEvaluator from rrompy.solver import normEngine from rrompy.utilities.exception_manager import (RROMPyException, RROMPyAssert, RROMPyWarning) from rrompy.parameter import checkParameterList, emptyParameterList __all__ = ['GenericGreedyApproximant'] def localL2Distance(mus:Np2D, badmus:Np2D) -> Np2D: return np.linalg.norm(np.tile(mus[..., np.newaxis], [1, 1, len(badmus)]) - badmus[..., np.newaxis].T, axis = 1) def pruneSamples(mus:paramList, badmus:paramList, tol : float = 1e-8) -> Np1D: """Remove from mus all the elements which are too close to badmus.""" if len(badmus) == 0: return mus proximity = np.min(localL2Distance(mus.data, badmus.data), axis = 1) return np.arange(len(mus))[proximity <= tol] class GenericGreedyApproximant(GenericStandardApproximant): """ 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; - 'collinearityTol': collinearity tolerance for greedy algorithm; defaults to 0.; - '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 sampler. Defaults to empty dict. + approx_state(optional): Whether to approximate state. Defaults to + False. verbosity(optional): Verbosity level. Defaults to 10. Attributes: HFEngine: HF problem solver. mu0: Default parameter. mus: Array of snapshot parameters. approxParameters: Dictionary containing values for main parameters of approximant. Recognized keys are in parameterList. parameterListSoft: Recognized keys of soft approximant parameters: - 'POD': whether to compute POD of snapshots. - 'greedyTol': uniform error tolerance for greedy algorithm; - 'collinearityTol': collinearity 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. + approx_state: Whether to approximate state. + verbosity: Verbosity level. POD: whether to compute POD of snapshots. S: number of test points. sampler: Sample point generator. greedyTol: Uniform error tolerance for greedy algorithm. collinearityTol: Collinearity 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. uApproxReduced: Reduced approximate solution(s) with parameter(s) lastSolvedApprox as sampleList. lastSolvedApproxReduced: Parameter(s) corresponding to last computed reduced approximate solution(s) as parameterList. uApprox: Approximate solution(s) with parameter(s) lastSolvedApprox as sampleList. 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 = {}, verbosity : int = 10, - timestamp : bool = True): + approxParameters : DictAny = {}, approx_state : bool = False, + verbosity : int = 10, timestamp : bool = True): self._preInit() self._addParametersToList(["greedyTol", "collinearityTol", "interactive", "maxIter", "refinementRatio", "nTestPoints"], [1e-2, 0., False, 1e2, .2, 5e2], ["trainSetGenerator"], ["AUTO"]) super().__init__(HFEngine = HFEngine, mu0 = mu0, approxParameters = approxParameters, - verbosity = verbosity, timestamp = timestamp) + approx_state = approx_state, verbosity = verbosity, + timestamp = timestamp) 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 collinearityTol(self): """Value of collinearityTol.""" return self._collinearityTol @collinearityTol.setter def collinearityTol(self, collinearityTol): if collinearityTol < 0: raise RROMPyException("collinearityTol must be non-negative.") if (hasattr(self, "_collinearityTol") and self.collinearityTol is not None): collinearityTolold = self.collinearityTol else: collinearityTolold = -1 self._collinearityTol = collinearityTol self._approxParameters["collinearityTol"] = self.collinearityTol if collinearityTolold != self.collinearityTol: 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 (isinstance(trainSetGenerator, (str,)) and trainSetGenerator.upper() == "AUTO"): trainSetGenerator = self.sampler if 'generatePoints' not in dir(trainSetGenerator): raise RROMPyException("trainSetGenerator type not recognized.") if (hasattr(self, '_trainSetGenerator') and self.trainSetGenerator not in [None, "AUTO"]): 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, "energyNormPartialDualMatrix"): self.HFEngine.buildEnergyNormPartialDualForm() estimatorEnergyMatrix = ( self.HFEngine.energyNormPartialDualMatrix) else: if hasattr(normEngn, "buildEnergyNormPartialDualForm"): if not hasattr(normEngn, "energyNormPartialDualMatrix"): normEngn.buildEnergyNormPartialDualForm() estimatorEnergyMatrix = ( normEngn.energyNormPartialDualMatrix) else: estimatorEnergyMatrix = normEngn self.estimatorNormEngine = normEngine(estimatorEnergyMatrix) def _affineResidualMatricesContraction(self, rb:Np2D, rA : Np2D = None) \ -> Tuple[Np1D, Np1D, Np1D]: - self.assembleReducedResidualBlocks(full = True) + self.assembleReducedResidualBlocks(full = rA is not None) # 'ij,jk,ik->k', resbb, radiusb, radiusb.conj() ff = np.sum(self.trainedModel.data.resbb.dot(rb) * rb.conj(), axis = 0) if rA is None: return ff # 'ijk,jkl,il->l', resAb, radiusA, radiusb.conj() Lf = np.sum(np.tensordot(self.trainedModel.data.resAb, rA, 2) * rb.conj(), axis = 0) # 'ijkl,klt,ijt->t', resAA, radiusA, radiusA.conj() LL = np.sum(np.tensordot(self.trainedModel.data.resAA, rA, 2) * rA.conj(), axis = (0, 1)) return ff, Lf, LL def errorEstimator(self, mus:Np1D) -> Np1D: """Standard residual-based error estimator.""" self.setupApprox() + checkIfAffine(self.HFEngine, "apply affinity-based error estimator") + self.HFEngine.buildA() + self.HFEngine.buildb() mus = checkParameterList(mus, self.npar)[0] vbMng(self.trainedModel, "INIT", "Evaluating error estimator at mu = {}.".format(mus), 10) verb = self.trainedModel.verbosity self.trainedModel.verbosity = 0 uApproxRs = self.getApproxReduced(mus) muTestEff = mus ** self.HFEngine.rescalingExp radiusA = np.empty((len(self.HFEngine.thAs), len(mus)), dtype = np.complex) radiusb = np.empty((len(self.HFEngine.thbs), len(mus)), dtype = np.complex) for j, thA in enumerate(self.HFEngine.thAs): radiusA[j] = expressionEvaluator(thA[0], muTestEff) for j, thb in enumerate(self.HFEngine.thbs): radiusb[j] = expressionEvaluator(thb[0], muTestEff) radiusA = np.expand_dims(uApproxRs.data, 1) * radiusA ff, Lf, LL = self._affineResidualMatricesContraction(radiusb, radiusA) err = np.abs((LL - 2. * np.real(Lf) + ff) / ff) ** .5 self.trainedModel.verbosity = verb vbMng(self.trainedModel, "DEL", "Done evaluating error estimator", 10) return err def getMaxErrorEstimator(self, mus:paramList) -> 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)] return errorEstTest, idxMaxEst, errorEstTest[idxMaxEst] def _isLastSampleCollinear(self) -> bool: """Check collinearity of last sample.""" if self.collinearityTol <= 0.: return False if self.POD: reff = self.samplingEngine.RPOD[:, -1] else: RROMPyWarning(("Repeated orthogonalization of the samples for " "collinearity check. Consider setting POD to " "True.")) if not hasattr(self, "_PODEngine"): from rrompy.sampling.base.pod_engine import PODEngine self._PODEngine = PODEngine(self.HFEngine) reff = self._PODEngine.generalizedQR(self.samplingEngine.samples, - only_R = True)[:, -1] + only_R = True, + is_state = True)[:, -1] cLevel = np.abs(reff[-1]) / np.linalg.norm(reff) vbMng(self, "MAIN", "Collinearity indicator {:.4e}.".format(cLevel), 5) return cLevel < self.collinearityTol 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.") mus = copy(self.muTest[muidx]) self.muTest.pop(muidx) for mu in mus: vbMng(self, "MAIN", ("Adding sample point no. {} at {} to training " "set.").format(self.samplingEngine.nsamples + 1, mu), 2) self.mus.append(mu) + self._S = len(self.mus) + self._approxParameters["S"] = self.S self.samplingEngine.nextSample(mu) if self._isLastSampleCollinear(): RROMPyWarning("Collinearity above tolerance detected.") errorEstTest = np.empty(len(self.muTest)) errorEstTest[:] = np.nan return errorEstTest, [-1], np.nan, np.nan errorEstTest, muidx, maxErrorEst = self.getMaxErrorEstimator( self.muTest) if (plotEst and not np.any(np.isnan(errorEstTest)) and not np.any(np.isinf(errorEstTest))): musre = copy(self.muTest.re.data) from matplotlib import pyplot as plt plt.figure() errCP = copy(errorEstTest) while len(musre) > 0: if self.npar == 1: currIdx = np.arange(len(musre)) else: currIdx = np.where(np.isclose(np.sum( np.abs(musre[:, 1 :] - musre[0, 1 :]), 1), 0.))[0] plt.semilogy(musre[currIdx, 0], errCP[currIdx], 'k', linewidth = 1) musre = np.delete(musre, currIdx, 0) errCP = np.delete(errCP, currIdx) plt.semilogy([self.muTest.re(0, 0), self.muTest.re(-1, 0)], [self.greedyTol] * 2, 'r--') plt.semilogy(self.mus.re(0), 2. * self.greedyTol * np.ones(len(self.mus)), '*m') plt.semilogy(self.muTest.re(muidx, 0), maxErrorEst, 'xr') plt.grid() plt.show() plt.close() return errorEstTest, muidx, maxErrorEst, self.muTest[muidx] def _preliminaryTraining(self): """Initialize starting snapshots of solution map.""" RROMPyAssert(self._mode, message = "Cannot start greedy algorithm.") if self.samplingEngine.nsamples > 0: return self.computeScaleFactor() self.resetSamples() self.mus = self.trainSetGenerator.generatePoints(self.S)[ list(range(self.S))] muTestBase = self.sampler.generatePoints(self.nTestPoints) idxPop = pruneSamples(muTestBase ** self.HFEngine.rescalingExp, self.mus ** self.HFEngine.rescalingExp, 1e-10 * self.scaleFactor[0]) muTestBase.pop(idxPop) muTestBase = muTestBase.sort() muLast = copy(self.mus[-1]) self.mus.pop() if len(self.mus) > 0: vbMng(self, "MAIN", ("Adding first {} sample point{} at {} to training " "set.").format(self.S - 1, "" + "s" * (self.S > 2), self.mus), 2) self.samplingEngine.iterSample(self.mus) + self._S = len(self.mus) + self._approxParameters["S"] = self.S self.muTest = emptyParameterList() self.muTest.reset((len(muTestBase) + 1, self.mus.shape[1])) self.muTest[: -1] = muTestBase.data self.muTest[-1] = muLast.data 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) idxPop = pruneSamples(muTestExtra ** self.HFEngine.rescalingExp, muTotal ** self.HFEngine.rescalingExp, 1e-10 * self.scaleFactor[0]) muTestExtra.pop(idxPop) muTestNew = np.empty((len(self.muTest) + len(muTestExtra), self.muTest.shape[1]), dtype = np.complex) muTestNew[: len(self.muTest)] = self.muTest.data muTestNew[len(self.muTest) :] = muTestExtra.data self.muTest = checkParameterList(muTestNew, self.npar)[0].sort() vbMng(self, "MAIN", "Enriching test set by {} elements.".format(len(muTestExtra)), 5) 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 vbMng(self, "INIT", "Starting computation of snapshots.", 2) self._preliminaryTraining() nTest = self.nTestPoints muT0 = copy(self.muTest[-1]) errorEstTest, muidx, maxErrorEst, mu = self.greedyNextSample( [len(self.muTest) - 1], plotEst) if np.any(np.isnan(maxErrorEst)): RROMPyWarning(("Instability in a posteriori estimator. " "Starting preemptive greedy loop termination.")) self.muTest.append(muT0) self.mus.pop(-1) self.samplingEngine.popSample() self.setupApprox() else: - vbMng(self, "MAIN", ("Uniform testing error estimate " - "{:.4e}.").format(np.max(maxErrorEst)), 2) + if maxErrorEst is not None: + max2ErrorEst = np.max(maxErrorEst) + vbMng(self, "MAIN", ("Uniform testing error estimate " + "{:.4e}.").format(max2ErrorEst), 2) trainedModelOld = copy(self.trainedModel) while (self.samplingEngine.nsamples < self.maxIter - and np.max(maxErrorEst) > self.greedyTol): + and (maxErrorEst is None or max2ErrorEst > self.greedyTol)): if (1. - self.refinementRatio) * nTest > len(self.muTest): self._enrichTestSet(nTest) nTest = len(self.muTest) - muTestOld, maxErrorEstOld = self.muTest, np.max(maxErrorEst) + muTestOld = self.muTest + if maxErrorEst is not None: + max2ErrorEstOld = max2ErrorEst errorEstTest, muidx, maxErrorEst, mu = self.greedyNextSample( muidx, plotEst) - vbMng(self, "MAIN", ("Uniform testing error estimate " - "{:.4e}.").format(np.max(maxErrorEst)), 2) + if maxErrorEst is not None: + max2ErrorEst = np.max(maxErrorEst) + vbMng(self, "MAIN", ("Uniform testing error estimate " + "{:.4e}.").format(max2ErrorEst), 2) if (np.any(np.isnan(maxErrorEst)) or np.any(np.isinf(maxErrorEst)) - or maxErrorEstOld < (np.max(maxErrorEst) - * self.TOL_INSTABILITY)): + or (maxErrorEst is not None + and max2ErrorEstOld < (max2ErrorEst * self.TOL_INSTABILITY))): RROMPyWarning(("Instability in a posteriori estimator. " "Starting preemptive greedy loop " "termination.")) self.muTest = muTestOld + self._approxParameters = ( + trainedModelOld.data.approxParameters) + self._S -= 1 self.mus.pop(-1) self.samplingEngine.popSample() self.trainedModel.data = copy(trainedModelOld.data) break trainedModelOld.data = copy(self.trainedModel.data) - if (self.interactive and np.max(maxErrorEst) <= self.greedyTol): + if (self.interactive and maxErrorEst is not None + and max2ErrorEst <= self.greedyTol): vbMng(self, "MAIN", ("Required tolerance {} achieved. Want to decrease " "greedyTol and continue? " "Y/N").format(self.greedyTol), 0, end = "") increasemaxIter = input() if increasemaxIter.upper() == "Y": vbMng(self, "MAIN", "Reducing value of greedyTol...", 0) - while np.max(maxErrorEst) <= self._greedyTol: + while max2ErrorEst <= self._greedyTol: self._greedyTol *= .5 if (self.interactive and self.samplingEngine.nsamples >= self.maxIter): vbMng(self, "MAIN", ("Maximum number of iterations {} reached. Want to " "increase maxIter and continue? " "Y/N").format(self.maxIter), 0, end = "") increasemaxIter = input() if increasemaxIter.upper() == "Y": vbMng(self, "MAIN", "Doubling value of maxIter...", 0) self._maxIter *= 2 vbMng(self, "DEL", ("Done computing snapshots (final snapshot count: " "{}).").format(self.samplingEngine.nsamples), 2) 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]): """ 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 = bs[i] resbb[i, i] = self.estimatorNormEngine.innerProduct(Mbi, Mbi) for j in range(i): Mbj = 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): """ 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 = dot(As[j], pMat) for i in range(nbs): Mbi = 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 = dot(As[j], pMat[:, Sold :]) for i in range(nbs): Mbi = bs[i] resAb[i, Sold :, j] = ( self.estimatorNormEngine.innerProduct(MAj, Mbi)) self.trainedModel.data.resAb = resAb def assembleReducedResidualBlocksAA(self, As:List[Np2D], pMat:sampList): """ 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 = dot(As[i], pMat) resAA[:, i, :, i] = ( self.estimatorNormEngine.innerProduct(MAi, MAi)) for j in range(i): MAj = dot(As[j], 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 = dot(As[i], 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 = dot(As[j], 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 def assembleReducedResidualBlocks(self, full : bool = False): """Build affine blocks of affine decomposition of residual.""" + if full: + checkIfAffine(self.HFEngine, "assemble reduced residual blocks") + else: + checkIfAffine(self.HFEngine, "assemble reduced RHS blocks", True) + self.HFEngine.buildb() self.assembleReducedResidualBlocksbb(self.HFEngine.bs) if full: - pMat = self.trainedModel.data.projMat + pMat = self.samplingEngine.samples + self.HFEngine.buildA() self.assembleReducedResidualBlocksAb(self.HFEngine.As, self.HFEngine.bs, pMat) self.assembleReducedResidualBlocksAA(self.HFEngine.As, pMat) diff --git a/rrompy/reduction_methods/greedy/rational_interpolant_greedy.py b/rrompy/reduction_methods/greedy/rational_interpolant_greedy.py index 01514cb..e0979c8 100644 --- a/rrompy/reduction_methods/greedy/rational_interpolant_greedy.py +++ b/rrompy/reduction_methods/greedy/rational_interpolant_greedy.py @@ -1,490 +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 .generic_greedy_approximant import (GenericGreedyApproximant, - localL2Distance as lL2D) -from rrompy.utilities.poly_fitting.polynomial import (polybases, polydomcoeff, +from rrompy.hfengines.base.linear_affine_engine import checkIfAffine +from .generic_greedy_approximant import GenericGreedyApproximant +from rrompy.utilities.poly_fitting.polynomial import (polybases, PolynomialInterpolator as PI, polyvanderTotal as pvT) -from rrompy.utilities.numerical import totalDegreeN +from rrompy.utilities.numerical import totalDegreeN, dot from rrompy.utilities.expression import expressionEvaluator from rrompy.reduction_methods.standard import RationalInterpolant -from rrompy.reduction_methods.trained_model import ( - TrainedModelRational as tModel) -from rrompy.reduction_methods.trained_model import TrainedModelData from rrompy.utilities.base.types import (Np1D, Tuple, DictAny, HFEng, paramVal, paramList) from rrompy.utilities.base import verbosityManager as vbMng from rrompy.utilities.poly_fitting import customFit from rrompy.utilities.exception_manager import (RROMPyWarning, RROMPyException, - RROMPyAssert) + RROMPyAssert, RROMPy_FRAGILE) from rrompy.parameter import checkParameterList __all__ = ['RationalInterpolantGreedy'] class RationalInterpolantGreedy(GenericGreedyApproximant, 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; - 'greedyTol': uniform error tolerance for greedy algorithm; defaults to 1e-2; - 'collinearityTol': collinearity tolerance for greedy algorithm; defaults to 0.; - '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 sampler; - 'polybasis': type of basis for interpolation; defaults to 'MONOMIAL'; - 'errorEstimatorKind': kind of error estimator; available values include 'AFFINE', 'DISCREPANCY', 'INTERPOLATORY', 'EIM_INTERPOLATORY', and 'EIM_DIAGONAL'; defaults to 'AFFINE'; - 'interpRcond': tolerance for interpolation; defaults to None; - 'robustTol': tolerance for robust rational denominator management; defaults to 0. Defaults to empty dict. + approx_state(optional): Whether to approximate state. Defaults and must + be True. verbosity(optional): Verbosity level. Defaults to 10. Attributes: HFEngine: HF problem solver. mu0: Default parameter. mus: Array of snapshot parameters. approxParameters: Dictionary containing values for main parameters of approximant. Recognized keys are in parameterList. parameterListSoft: Recognized keys of soft approximant parameters: - 'POD': whether to compute POD of snapshots. - 'greedyTol': uniform error tolerance for greedy algorithm; - 'collinearityTol': collinearity 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; - 'errorEstimatorKind': kind of error estimator; - 'interpRcond': tolerance for interpolation; - 'robustTol': tolerance for robust rational denominator management. parameterListCritical: Recognized keys of critical approximant parameters: - 'S': total number of samples current approximant relies upon; - 'sampler': sample point generator. + approx_state: Whether to approximate state. + verbosity: Verbosity level. POD: whether to compute POD of snapshots. S: number of test points. sampler: Sample point generator. greedyTol: uniform error tolerance for greedy algorithm. collinearityTol: Collinearity 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 rational denominator management. errorEstimatorKind: kind of error estimator. interpRcond: tolerance for interpolation. robustTol: tolerance for robust rational 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. uApproxReduced: Reduced approximate solution(s) with parameter(s) lastSolvedApprox as sampleList. lastSolvedApproxReduced: Parameter(s) corresponding to last computed reduced approximate solution(s) as parameterList. uApprox: Approximate solution(s) with parameter(s) lastSolvedApprox as sampleList. lastSolvedApprox: Parameter(s) corresponding to last computed approximate solution(s) as parameterList. """ - _allowedEstimatorKinds = ["AFFINE", "DISCREPANCY", "INTERPOLATORY", - "EIM_INTERPOLATORY", "EIM_DIAGONAL"] + _allowedEstimatorKinds = ["AFFINE", "DISCREPANCY", "INTERPOLATORY", "NONE"] def __init__(self, HFEngine:HFEng, mu0 : paramVal = None, - approxParameters : DictAny = {}, verbosity : int = 10, - timestamp : bool = True): + approxParameters : DictAny = {}, approx_state : bool = True, + verbosity : int = 10, timestamp : bool = True): + if not approx_state: RROMPyWarning("Overriding approx_state to True.") self._preInit() - self._addParametersToList(["errorEstimatorKind"], ["AFFINE"], + self._addParametersToList(["errorEstimatorKind"], ["DISCREPANCY"], toBeExcluded = ["M", "N", "polydegreetype", "radialDirectionalWeights", "nNearestNeighbor"]) super().__init__(HFEngine = HFEngine, mu0 = mu0, approxParameters = approxParameters, - verbosity = verbosity, timestamp = timestamp) + approx_state = True, verbosity = verbosity, + timestamp = timestamp) self._postInit() @property def E(self): """Value of E.""" self._E = self.sampleBatchIdx - 1 return self._E @E.setter def E(self, E): RROMPyWarning(("E is used just to simplify inheritance, and its value " "cannot be changed from that of sampleBatchIdx - 1.")) @property def polydegreetype(self): """Value of polydegreetype.""" return "TOTAL" @polydegreetype.setter def polydegreetype(self, polydegreetype): RROMPyWarning(("polydegreetype is used just to simplify inheritance, " "and its value cannot be changed from 'TOTAL'.")) @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 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 'AFFINE'.")) - errorEstimatorKind = "AFFINE" + "to 'DISCREPANCY'.")) + errorEstimatorKind = "DISCREPANCY" self._errorEstimatorKind = errorEstimatorKind self._approxParameters["errorEstimatorKind"] = self.errorEstimatorKind def errorEstimator(self, mus:Np1D) -> Np1D: """Standard residual-based error estimator.""" if self.errorEstimatorKind == "AFFINE": return super().errorEstimator(mus) setupOK = self.setupApprox() if not setupOK: err = np.empty(len(mus)) err[:] = np.nan return err - if self.errorEstimatorKind == "DIAGONAL": - return self.errorEstimatorEIM(mus) + self._setupInterpolationIndices() mus = checkParameterList(mus, self.npar)[0] muCTest = self.trainedModel.centerNormalize(mus) vbMng(self.trainedModel, "INIT", "Evaluating error estimator at mu = {}.".format(mus), 10) verb = self.trainedModel.verbosity self.trainedModel.verbosity = 0 QTest = self.trainedModel.getQVal(mus) if self.errorEstimatorKind == "DISCREPANCY": - nAs, nbs = len(self.HFEngine.thAs), len(self.HFEngine.thbs) + checkIfAffine(self.HFEngine, + "apply discrepancy-based error estimator") + self.HFEngine.buildA() + self.HFEngine.buildb() + nAs, nbs = self.HFEngine.nAs, self.HFEngine.nbs muTrainEff = self.mus ** self.HFEngine.rescalingExp muTestEff = mus ** self.HFEngine.rescalingExp PTrain = self.trainedModel.getPVal(self.mus).data.T QTrain = self.trainedModel.getQVal(self.mus) PTest = self.trainedModel.getPVal(mus).data radiusAbTrain = np.empty((self.S, nAs * self.S + nbs), dtype = np.complex) radiusA = np.empty((self.S, nAs, len(mus)), dtype = np.complex) radiusb = np.empty((nbs, len(mus)), dtype = np.complex) for j, thA in enumerate(self.HFEngine.thAs): idxs = j * self.S + np.arange(self.S) radiusAbTrain[:, idxs] = expressionEvaluator(thA[0], muTrainEff, (self.S, 1)) * PTrain radiusA[:, j] = PTest * expressionEvaluator(thA[0], muTestEff, (len(mus),)) for j, thb in enumerate(self.HFEngine.thbs): idx = nAs * self.S + j radiusAbTrain[:, idx] = QTrain * expressionEvaluator(thb[0], muTrainEff, (self.S,)) radiusb[j] = QTest * expressionEvaluator(thb[0], muTestEff, (len(mus),)) QRHSNorm2 = self._affineResidualMatricesContraction(radiusb) - vanTrain, _, vanTrainIdxs = pvT(self._musUniqueCN, self.N, - self.polybasis0, self._derIdxs, - self._reorder) - interpPQ = customFit(vanTrain[:, vanTrainIdxs], radiusAbTrain, + vanTrain = pvT(self._musUniqueCN, self.E, self.polybasis0, + self._derIdxs, self._reorder) + interpPQ = customFit(vanTrain, radiusAbTrain, rcond = self.interpRcond) - vanTest, _, vanTestIdxs = pvT(muCTest, self.N, self.polybasis0) - DradiusAb = vanTest[:, vanTestIdxs].dot(interpPQ) + vanTest = pvT(muCTest, self.E, self.polybasis0) + DradiusAb = vanTest.dot(interpPQ) radiusA = (radiusA - DradiusAb[:, : - nbs].reshape(len(mus), -1, self.S).T) radiusb = radiusb - DradiusAb[:, - nbs :].T ff, Lf, LL = self._affineResidualMatricesContraction(radiusb, radiusA) err = np.abs((LL - 2. * np.real(Lf) + ff) / QRHSNorm2) ** .5 - else: #if self.errorEstimatorKind == "INTERPOLATORY": - muCTrain = self.trainedModel.centerNormalize(self.mus) - samplingRatio = np.prod(lL2D(muCTest.data, muCTrain.data), - axis = 1) / np.abs(QTest) - self.initEstimatorNormEngine() + else: #if self.errorEstimatorKind in ["INTERPOLATORY", "NONE"]: QTest = np.abs(QTest) - sampRCP = copy(samplingRatio) - idx_muTestSample = np.empty(self.sampleBatchSize, - dtype = int) - for j in range(self.sampleBatchSize): - k = np.argmax(sampRCP) - idx_muTestSample[j] = k - if j + 1 < self.sampleBatchSize: - musZero = self.trainedModel.centerNormalize(mus, mus[k]) - sampRCP *= np.linalg.norm(musZero.data, axis = 1) - mu_muTestSample = mus[idx_muTestSample] - app_muTestSample = self.getApprox(mu_muTestSample) - resmus = self.HFEngine.residual(mu_muTestSample, app_muTestSample, - duality = False) - RHSmus = self.HFEngine.residual(mu_muTestSample, None, - duality = False) - ressamples = (self.estimatorNormEngine.norm(resmus) - / self.estimatorNormEngine.norm(RHSmus)) - musT = copy(self.mus) - musT.append(mu_muTestSample) - musT = self.trainedModel.centerNormalize(musT) - musC = self.trainedModel.centerNormalize(mus) - resT = np.zeros(len(musT), dtype = np.complex) - err = np.zeros(len(mus)) - for l in range(len(mu_muTestSample)): - resT[len(self.mus) + l] = (ressamples[l] - * QTest[idx_muTestSample[l]]) - p = PI() - wellCond, msg = p.setupByInterpolation(musT, resT, self.M + 1, - self.polybasis, self.verbosity >= 15, - True, {}, {"rcond": self.interpRcond}) - err += np.abs(p(musC)) - resT[len(self.mus) + l] = 0. - err /= QTest - vbMng(self, "MAIN", msg, 15) - self.trainedModel.verbosity = verb - vbMng(self.trainedModel, "DEL", "Done evaluating error estimator", 10) - return err - - def errorEstimatorEIM(self, mus:Np1D, - return_max_idxs : bool = False) -> Np1D: - """EIM-like interpolation error estimator.""" - setupOK = self.setupApprox() - if not setupOK: - err = np.empty(len(mus)) - err[:] = np.nan - return err - mus = checkParameterList(mus, self.npar)[0] - vbMng(self.trainedModel, "INIT", - "Evaluating error estimator at mu = {}.".format(mus), 10) - verb = self.trainedModel.verbosity - self.trainedModel.verbosity = 0 - QTest = self.trainedModel.getQVal(mus) - muCTest = self.trainedModel.centerNormalize(mus) - muCTrain = self.trainedModel.centerNormalize(self.mus) - vanderTest, _, vanderTestR = pvT(muCTest, self.E, self.polybasis) - vanderTest = vanderTest[:, vanderTestR] - vanderTestNext, _, vanderTestNextR = pvT(muCTest, self.E + 1, - self.polybasis) - vanderTestNext = vanderTestNext[:, vanderTestNextR[ - vanderTest.shape[1] :]] - idxsTest = np.arange(vanderTestNext.shape[1]) - basis = np.zeros((len(idxsTest), 0), dtype = float) - idxMaxEst = [] - err = None - while len(idxsTest) > 0: - vanderTrial, _, vanderTrialR = pvT(muCTrain, self.E, - self.polybasis) - vanderTrial = vanderTrial[:, vanderTrialR] - vanderTrialNext, _, vanderTrialNextR = pvT(muCTrain, self.E + 1, - self.polybasis) - vanderTrialNext = vanderTrialNext[:, vanderTrialNextR[ - vanderTrial.shape[1] :]] - vanderTrial = np.hstack((vanderTrial, - vanderTrialNext.dot(basis).reshape( - len(vanderTrialNext), - basis.shape[1]))) - valuesTrial = vanderTrialNext[:, idxsTest] - vanderTestEff = np.hstack((vanderTest, - vanderTestNext.dot(basis).reshape( - len(vanderTestNext), - basis.shape[1]))) - vanderTestNextEff = vanderTestNext[:, idxsTest] - coeffTest = np.linalg.solve(vanderTrial, valuesTrial) - errTest = np.abs((vanderTestNextEff - vanderTestEff.dot(coeffTest)) - / np.expand_dims(QTest, 1)) - idxMaxErr = np.unravel_index(np.argmax(errTest), errTest.shape) - idxMaxEst += [idxMaxErr[0]] - if err is None: err = np.max(errTest, axis = 1) - if not return_max_idxs: break - muCTrain.append(muCTest[idxMaxErr[0]]) - basis = np.pad(basis, [(0, 0), (0, 1)], "constant") - basis[idxsTest[idxMaxErr[1]], -1] = 1. - idxsTest = np.delete(idxsTest, idxMaxErr[1]) - if self.errorEstimatorKind == "EIM_DIAGONAL": - self.assembleReducedResidualBlocks(full = False) - muTestEff = mus ** self.HFEngine.rescalingExp - radiusb = np.empty((len(self.HFEngine.thbs), len(mus)), - dtype = np.complex) - for j, thb in enumerate(self.HFEngine.thbs): - radiusb[j] = expressionEvaluator(thb[0], muTestEff) - bresb = self._affineResidualMatricesContraction(radiusb) - self.assembleReducedResidualGramian(self.trainedModel.data.projMat) - pDom = (polydomcoeff(self.E, self.polybasis) - * self.trainedModel.data.P[(-1,) + (0,) * (self.npar - 1)]) - LL = pDom.conj().dot(self.trainedModel.data.gramian.dot(pDom)) - if not hasattr(self, "Anorm2Approx"): - if self.HFEngine.nAs > 1: - Ader = self.HFEngine.A(self.mu0, - [1] + [0] * (self.npar - 1)) - try: - Adiag = self.scaleFactor[0] * Ader.diagonal() - except: - Adiag = self.scaleFactor[0] * np.diagonal(Ader) - self.Anorm2Approx = np.mean(np.abs(Adiag) ** 2.) - if (np.isclose(self.Anorm2Approx, 0.) - or self.HFEngine.nAs <= 1): - self.Anorm2Approx = 1 - jOpt = np.abs(self.Anorm2Approx * LL / bresb) ** .5 - err = jOpt * err - else: #if self.errorEstimatorKind == "EIM_INTERPOLATORY": - self.initEstimatorNormEngine() - mu_muTestSample = mus[idxMaxEst[0]] - app_muTestSample = self.getApprox(mu_muTestSample) - resmu = self.HFEngine.residual(mu_muTestSample, - app_muTestSample, - duality = False) - RHSmu = self.HFEngine.residual(mu_muTestSample, None, - duality = False) - jOpt = np.abs(self.estimatorNormEngine.norm(resmu)[0] - / err[idxMaxEst[0]] - / self.estimatorNormEngine.norm(RHSmu)[0]) - err = jOpt * err + muCTest = self.trainedModel.centerNormalize(mus) + muCTrain = self.trainedModel.centerNormalize(self.mus) + vanTest = pvT(muCTest, self.E, self.polybasis) + vanTestNext = pvT(muCTest, self.E + 1, self.polybasis)[:, + vanTest.shape[1] :] + idxsTest = np.arange(vanTestNext.shape[1]) + basis = np.zeros((len(idxsTest), 0), dtype = float) + idxMaxEst = [] + while len(idxsTest) > 0: + vanTrial = pvT(muCTrain, self.E, self.polybasis) + vanTrialNext = pvT(muCTrain, self.E + 1, self.polybasis)[:, + vanTrial.shape[1] :] + vanTrial = np.hstack((vanTrial, + vanTrialNext.dot(basis).reshape( + len(vanTrialNext), basis.shape[1]))) + valuesTrial = vanTrialNext[:, idxsTest] + vanTestEff = np.hstack((vanTest, + vanTestNext.dot(basis).reshape( + len(vanTestNext), basis.shape[1]))) + vanTestNextEff = vanTestNext[:, idxsTest] + coeffTest = np.linalg.solve(vanTrial, valuesTrial) + errTest = (np.abs(vanTestNextEff - vanTestEff.dot(coeffTest)) + / np.expand_dims(QTest, 1)) + if self.errorEstimatorKind == "NONE": break + idxMaxErr = np.unravel_index(np.argmax(errTest), errTest.shape) + idxMaxEst += [idxMaxErr[0]] + muCTrain.append(muCTest[idxMaxErr[0]]) + basis = np.pad(basis, [(0, 0), (0, 1)], "constant") + basis[idxsTest[idxMaxErr[1]], -1] = 1. + idxsTest = np.delete(idxsTest, idxMaxErr[1]) + if self.errorEstimatorKind == "INTERPOLATORY": + self.initEstimatorNormEngine() + mu_muTestSample = mus[idxMaxEst] + app_muTestSample = self.getApproxReduced(mu_muTestSample) + if self._mode == RROMPy_FRAGILE: + if not self.HFEngine.isCEye: + raise RROMPyException(("Cannot compute INTERPOLATORY " + "residual estimator in fragile " + "mode for non-scalar C.")) + app_muTestSample = dot(self.trainedModel.data.projMat, + app_muTestSample.data) + else: + app_muTestSample = dot(self.samplingEngine.samples, + app_muTestSample) + resmu = self.HFEngine.residual(mu_muTestSample, + app_muTestSample, + post_c = False) + RHSmu = self.HFEngine.residual(mu_muTestSample, None, + post_c = False) + ressamples = (self.estimatorNormEngine.norm(resmu) + / self.estimatorNormEngine.norm(RHSmu)) + # improve the following by explicitly constructing (tensorised) + # interpolant as in while loop + musT = copy(self.mus) + musT.append(mu_muTestSample) + musT = self.trainedModel.centerNormalize(musT) + musC = self.trainedModel.centerNormalize(mus) + resT = np.zeros(len(musT), dtype = np.complex) + err = np.zeros(len(mus)) + for l in range(len(mu_muTestSample)): + resT[len(self.mus) + l] = (ressamples[l] + * QTest[idxMaxEst[l]]) + p = PI() + wellCond, msg = p.setupByInterpolation(musT, resT, + self.E + 1, self.polybasis, + self.verbosity >= 15, True, {}, + {"rcond": self.interpRcond}) + err += np.abs(p(musC)) + resT[len(self.mus) + l] = 0. + err /= QTest + vbMng(self, "MAIN", msg, 15) + else: #if self.errorEstimatorKind == "NONE": + err = np.max(errTest, axis = 1) + err *= self.greedyTol / np.mean(err) self.trainedModel.verbosity = verb vbMng(self.trainedModel, "DEL", "Done evaluating error estimator", 10) - if return_max_idxs: return err, idxMaxEst return err def getMaxErrorEstimator(self, mus:paramList) -> Tuple[Np1D, int, float]: """ Compute maximum of (and index of maximum of) error estimator over given parameters. """ - if self.errorEstimatorKind[: 4] == "EIM_": - errorEstTest, idxMaxEst = self.errorEstimatorEIM(mus, True) - else: - errorEstTest = self.errorEstimator(mus) - idxMaxEst = np.empty(self.sampleBatchSize, dtype = int) - errCP = copy(errorEstTest) - for j in range(self.sampleBatchSize): - k = np.argmax(errCP) - idxMaxEst[j] = k - if j + 1 < self.sampleBatchSize: - musZero = self.trainedModel.centerNormalize(mus, mus[k]) - errCP *= np.linalg.norm(musZero.data, axis = 1) + errorEstTest = self.errorEstimator(mus) + idxMaxEst = np.empty(self.sampleBatchSize, dtype = int) + errCP = copy(errorEstTest) + for j in range(self.sampleBatchSize): + k = np.argmax(errCP) + idxMaxEst[j] = k + if j + 1 < self.sampleBatchSize: + musZero = self.trainedModel.centerNormalize(mus, mus[k]) + errCP *= np.linalg.norm(musZero.data, axis = 1) maxEst = errorEstTest[idxMaxEst] 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.") self.sampleBatchIdx += 1 self.sampleBatchSize = totalDegreeN(self.npar - 1, self.sampleBatchIdx) - return super().greedyNextSample(muidx, plotEst) + err, muidx, maxErr, muNext = super().greedyNextSample(muidx, plotEst) + if self.errorEstimatorKind == "NONE": maxErr = None + return err, muidx, maxErr, muNext def _preliminaryTraining(self): """Initialize starting snapshots of solution map.""" RROMPyAssert(self._mode, message = "Cannot start greedy algorithm.") if self.samplingEngine.nsamples > 0: return S = self.S self.sampleBatchIdx, self.sampleBatchSize, self._S = -1, 0, 0 nextBatchSize = 1 while self._S + nextBatchSize <= S: self.sampleBatchIdx += 1 self.sampleBatchSize = nextBatchSize self._S += self.sampleBatchSize nextBatchSize = totalDegreeN(self.npar - 1, self.sampleBatchIdx + 1) super()._preliminaryTraining() def setupApprox(self, plotEst : bool = False): """ Compute rational interpolant. SVD-based robust eigenvalue management. """ if self.checkComputedApprox(): return True RROMPyAssert(self._mode, message = "Cannot setup approximant.") vbMng(self, "INIT", "Setting up {}.".format(self.name()), 5) self.greedy(plotEst) - self._S = len(self.mus) self._N, self._M = [self.E] * 2 + pMat = self.samplingEngine.samples.data + pMatEff = dot(self.HFEngine.C, pMat) if self.trainedModel is None: - self.trainedModel = tModel() + self.trainedModel = self.tModelType() self.trainedModel.verbosity = self.verbosity self.trainedModel.timestamp = self.timestamp - data = TrainedModelData(self.trainedModel.name(), self.mu0, - self.samplingEngine.samples, - self.scaleFactor, - self.HFEngine.rescalingExp) - data.mus = copy(self.mus) - self.trainedModel.data = data + datadict = {"mu0": self.mu0, "projMat": pMatEff, + "scaleFactor": self.scaleFactor, + "rescalingExp": self.HFEngine.rescalingExp} + self.trainedModel.data = self.initializeModelData(datadict)[0] else: self.trainedModel = self.trainedModel - self.trainedModel.data.projMat = copy(self.samplingEngine.samples) + self.trainedModel.data.projMat = copy(pMatEff) self.trainedModel.data.mus = copy(self.mus) + self.trainedModel.data.mus = copy(self.mus) self.catchInstability = True if self.N > 0: try: Q = self._setupDenominator()[0] except RROMPyException as RE: - RROMPyWarning(RE) + RROMPyWarning(RE._msg) vbMng(self, "DEL", "Done setting up approximant.", 5) return False else: Q = PI() Q.coeffs = np.ones(1, dtype = np.complex) Q.npar = 1 Q.polybasis = self.polybasis self.trainedModel.data.Q = copy(Q) try: self.trainedModel.data.P = copy(self._setupNumerator()) except RROMPyException as RE: - RROMPyWarning(RE) + RROMPyWarning(RE._msg) vbMng(self, "DEL", "Done setting up approximant.", 5) return False self.trainedModel.data.approxParameters = copy(self.approxParameters) vbMng(self, "DEL", "Done setting up approximant.", 5) return True + + def loadTrainedModel(self, filename:str): + """Load trained reduced model from file.""" + super().loadTrainedModel(filename) + self.sampleBatchIdx, self.sampleBatchSize, _S = -1, 0, 0 + nextBatchSize = 1 + while _S + nextBatchSize <= self.S + 1: + self.sampleBatchIdx += 1 + self.sampleBatchSize = nextBatchSize + _S += self.sampleBatchSize + nextBatchSize = totalDegreeN(self.npar - 1, + self.sampleBatchIdx + 1) + diff --git a/rrompy/reduction_methods/greedy/reduced_basis_greedy.py b/rrompy/reduction_methods/greedy/reduced_basis_greedy.py index 2e53456..4d67e8a 100644 --- a/rrompy/reduction_methods/greedy/reduced_basis_greedy.py +++ b/rrompy/reduction_methods/greedy/reduced_basis_greedy.py @@ -1,178 +1,184 @@ # 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 from .generic_greedy_approximant import GenericGreedyApproximant from rrompy.reduction_methods.standard import ReducedBasis -from rrompy.reduction_methods.trained_model import \ - TrainedModelReducedBasis as tModel -from rrompy.reduction_methods.trained_model import TrainedModelData from rrompy.utilities.base.types import DictAny, HFEng, paramVal from rrompy.utilities.base import verbosityManager as vbMng +from rrompy.utilities.numerical import dot from rrompy.utilities.exception_manager import RROMPyWarning, RROMPyAssert __all__ = ['ReducedBasisGreedy'] class ReducedBasisGreedy(GenericGreedyApproximant, ReducedBasis): """ 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; - 'collinearityTol': collinearity tolerance for greedy algorithm; defaults to 0.; - '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 sampler. Defaults to empty dict. + approx_state(optional): Whether to approximate state. Defaults and must + be True. verbosity(optional): Verbosity level. Defaults to 10. Attributes: HFEngine: HF problem solver. mu0: Default parameter. mus: Array of snapshot parameters. approxParameters: Dictionary containing values for main parameters of approximant. Recognized keys are in parameterList. parameterListSoft: Recognized keys of soft approximant parameters: - 'POD': whether to compute POD of snapshots. - 'greedyTol': uniform error tolerance for greedy algorithm; - 'collinearityTol': collinearity 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. + approx_state: Whether to approximate state. + verbosity: Verbosity level. POD: whether to compute POD of snapshots. S: number of test points. sampler: Sample point generator. greedyTol: uniform error tolerance for greedy algorithm. collinearityTol: Collinearity 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. uApproxReduced: Reduced approximate solution(s) with parameter(s) lastSolvedApprox as sampleList. lastSolvedApproxReduced: Parameter(s) corresponding to last computed reduced approximate solution(s) as parameterList. uApprox: Approximate solution(s) with parameter(s) lastSolvedApprox as sampleList. 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. bs: List of numpy vectors representing coefficients of linear system RHS. ARBs: List of sparse matrices (in CSC format) representing coefficients of compressed linear system matrix. bRBs: List of numpy vectors representing coefficients of compressed linear system RHS. """ def __init__(self, HFEngine:HFEng, mu0 : paramVal = None, - approxParameters : DictAny = {}, verbosity : int = 10, - timestamp : bool = True): + approxParameters : DictAny = {}, approx_state : bool = True, + verbosity : int = 10, timestamp : bool = True): + if not approx_state: RROMPyWarning("Overriding approx_state to True.") self._preInit() self._addParametersToList([], [], toBeExcluded = ["R", "PODTolerance"]) super().__init__(HFEngine = HFEngine, mu0 = mu0, approxParameters = approxParameters, - verbosity = verbosity, timestamp = timestamp) + approx_state = True, verbosity = verbosity, + timestamp = timestamp) self._postInit() @property def R(self): """Value of R.""" self._R = 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 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 setupApprox(self, plotEst : bool = False): """Compute RB projection matrix.""" if self.checkComputedApprox(): return RROMPyAssert(self._mode, message = "Cannot setup approximant.") vbMng(self, "INIT", "Setting up {}.". format(self.name()), 5) self.greedy(plotEst) vbMng(self, "INIT", "Computing projection matrix.", 7) - pMat = self.samplingEngine.samples + pMat = self.samplingEngine.samples.data + pMatEff = dot(self.HFEngine.C, pMat) if self.trainedModel is None: - self.trainedModel = tModel() + self.trainedModel = self.tModelType() self.trainedModel.verbosity = self.verbosity self.trainedModel.timestamp = self.timestamp - data = TrainedModelData(self.trainedModel.name(), self.mu0, - pMat, self.scaleFactor, - self.HFEngine.rescalingExp) + datadict = {"mu0": self.mu0, "projMat": pMatEff, + "scaleFactor": self.scaleFactor, + "rescalingExp": self.HFEngine.rescalingExp} + data = self.initializeModelData(datadict)[0] ARBs, bRBs = self.assembleReducedSystem(pMat) data.affinePoly = self.HFEngine.affinePoly + self.HFEngine.buildA() + self.HFEngine.buildb() data.thAs, data.thbs = self.HFEngine.thAs, self.HFEngine.thbs 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) + Sold = self.trainedModel.data.projMat.shape[1] + ARBs, bRBs = self.assembleReducedSystem(pMat[:, Sold :], + pMat[:, : Sold]) + self.trainedModel.data.projMat = copy(pMatEff) self.trainedModel.data.mus = copy(self.mus) self.trainedModel.data.ARBs = ARBs self.trainedModel.data.bRBs = bRBs vbMng(self, "DEL", "Done computing projection matrix.", 7) self.trainedModel.data.approxParameters = copy(self.approxParameters) vbMng(self, "DEL", "Done setting up approximant.", 5) - diff --git a/rrompy/reduction_methods/pivoted/generic_pivoted_approximant.py b/rrompy/reduction_methods/pivoted/generic_pivoted_approximant.py index 5e30e3d..37bb2e8 100644 --- a/rrompy/reduction_methods/pivoted/generic_pivoted_approximant.py +++ b/rrompy/reduction_methods/pivoted/generic_pivoted_approximant.py @@ -1,547 +1,537 @@ # 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.poly_fitting.polynomial import (polybases as ppb, PolynomialInterpolator as PI) from rrompy.utilities.poly_fitting.radial_basis import (polybases as rbpb, RadialBasisInterpolator as RBI) from rrompy.utilities.poly_fitting.moving_least_squares import ( polybases as mlspb, MovingLeastSquaresInterpolator as MLSI) from rrompy.sampling.pivoted import (SamplingEnginePivoted, SamplingEnginePivotedPOD) -from rrompy.utilities.base.types import (ListAny, DictAny, HFEng, paramVal, - paramList) +from rrompy.utilities.base.types import ListAny, DictAny, HFEng, paramVal from rrompy.utilities.base import verbosityManager as vbMng from rrompy.utilities.numerical import (fullDegreeN, totalDegreeN, nextDerivativeIndices) from rrompy.utilities.exception_manager import (RROMPyException, RROMPyAssert, RROMPyWarning) from rrompy.parameter import emptyParameterList __all__ = ['GenericPivotedApproximant'] class GenericPivotedApproximant(GenericApproximant): """ ROM pivoted approximant (with pole matching) computation for parametric problems (ABSTRACT). Args: HFEngine: HF problem solver. mu0(optional): Default parameter. Defaults to 0. directionPivot(optional): Pivot components. 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; - 'matchingWeight': weight for pole matching optimization; defaults to 1; - 'cutOffTolerance': tolerance for ignoring parasitic poles; defaults to np.inf; - 'cutOffType': rule for tolerance computation for parasitic poles; defaults to 'MAGNITUDE'; - 'S': total number of pivot samples current approximant relies upon; - 'samplerPivot': pivot sample point generator; - 'SMarginal': total number of marginal samples current approximant relies upon; - 'samplerMarginal': marginal sample point generator; - 'polybasisMarginal': type of polynomial basis for marginal interpolation; allowed values include 'MONOMIAL', 'CHEBYSHEV' and 'LEGENDRE'; defaults to 'MONOMIAL'; - 'MMarginal': degree of marginal interpolant; defaults to 0; - 'polydegreetypeMarginal': type of polynomial degree for marginal; defaults to 'TOTAL'; - 'radialDirectionalWeightsMarginal': radial basis weights for marginal interpolant; defaults to 0, i.e. identity; - 'nNearestNeighborMarginal': number of marginal nearest neighbors considered if polybasisMarginal allows; defaults to -1; - 'interpRcondMarginal': tolerance for marginal interpolation; defaults to None. Defaults to empty dict. + approx_state(optional): Whether to approximate state. Defaults to + False. verbosity(optional): Verbosity level. Defaults to 10. Attributes: HFEngine: HF problem solver. mu0: Default parameter. directionPivot: Pivot components. mus: Array of snapshot parameters. musPivot: Array of pivot snapshot parameters. musMarginal: Array of marginal snapshot parameters. 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; - 'matchingWeight': weight for pole matching optimization; - 'cutOffTolerance': tolerance for ignoring parasitic poles; - 'cutOffType': rule for tolerance computation for parasitic poles; - 'polybasisMarginal': type of polynomial basis for marginal interpolation; - 'MMarginal': degree of marginal interpolant; - 'polydegreetypeMarginal': type of polynomial degree for marginal; - 'radialDirectionalWeightsMarginal': radial basis weights for marginal interpolant; - 'nNearestNeighborMarginal': number of marginal nearest neighbors considered if polybasisMarginal allows; - 'interpRcondMarginal': tolerance for marginal interpolation. parameterListCritical: Recognized keys of critical approximant parameters: - 'S': total number of pivot samples current approximant relies upon; - 'samplerPivot': pivot sample point generator; - 'SMarginal': total number of marginal samples current approximant relies upon; - 'samplerMarginal': marginal sample point generator. + approx_state: Whether to approximate state. + verbosity: Verbosity level. POD: Whether to compute POD of snapshots. matchingWeight: Weight for pole matching optimization. cutOffTolerance: Tolerance for ignoring parasitic poles. cutOffType: Rule for tolerance computation for parasitic poles. S: Total number of pivot samples current approximant relies upon. samplerPivot: Pivot sample point generator. SMarginal: Total number of marginal samples current approximant relies upon. samplerMarginal: Marginal sample point generator. polybasisMarginal: Type of polynomial basis for marginal interpolation. MMarginal: Degree of marginal interpolant. polydegreetypeMarginal: Type of polynomial degree for marginal. radialDirectionalWeightsMarginal: Radial basis weights for marginal interpolant. nNearestNeighborMarginal: Number of marginal nearest neighbors considered if polybasisMarginal allows. interpRcondMarginal: Tolerance for marginal interpolation. muBoundsPivot: list of bounds for pivot parameter values. muBoundsMarginal: list of bounds for marginal 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. uApproxReduced: Reduced approximate solution(s) with parameter(s) lastSolvedApprox as sampleList. lastSolvedApproxReduced: Parameter(s) corresponding to last computed reduced approximate solution(s) as parameterList. uApprox: Approximate solution(s) with parameter(s) lastSolvedApprox as sampleList. lastSolvedApprox: Parameter(s) corresponding to last computed approximate solution(s) as parameterList. """ def __init__(self, HFEngine:HFEng, mu0 : paramVal = None, directionPivot : ListAny = [0], - approxParameters : DictAny = {}, verbosity : int = 10, - timestamp : bool = True): + approxParameters : DictAny = {}, approx_state : bool = False, + verbosity : int = 10, timestamp : bool = True): self._preInit() if len(directionPivot) > 1: raise RROMPyException(("Exactly 1 pivot parameter allowed in pole " "matching.")) from rrompy.parameter.parameter_sampling import QuadratureSampler as QS QSBase = QS([[0], [1]], "UNIFORM") self._addParametersToList(["matchingWeight", "cutOffTolerance", "cutOffType", "polybasisMarginal", "MMarginal", "polydegreetypeMarginal", "radialDirectionalWeightsMarginal", "nNearestNeighborMarginal", "interpRcondMarginal"], [1, np.inf, "MAGNITUDE", "MONOMIAL", 0, "TOTAL", 1, -1, -1], ["samplerPivot", "SMarginal", "samplerMarginal"], [QSBase, [1], QSBase]) del QS self._directionPivot = directionPivot super().__init__(HFEngine = HFEngine, mu0 = mu0, approxParameters = approxParameters, - verbosity = verbosity, timestamp = timestamp) + approx_state = approx_state, verbosity = verbosity, + timestamp = timestamp) self._postInit() 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 = SamplingEnginePivotedPOD else: SamplingEngine = SamplingEnginePivoted self.samplingEngine = SamplingEngine(self.HFEngine, self.directionPivot, + sample_state = self.approx_state, verbosity = self.verbosity) @property def matchingWeight(self): """Value of matchingWeight.""" return self._matchingWeight @matchingWeight.setter def matchingWeight(self, matchingWeight): self._matchingWeight = matchingWeight self._approxParameters["matchingWeight"] = self.matchingWeight @property def cutOffTolerance(self): """Value of cutOffTolerance.""" return self._cutOffTolerance @cutOffTolerance.setter def cutOffTolerance(self, cutOffTolerance): self._cutOffTolerance = cutOffTolerance self._approxParameters["cutOffTolerance"] = self.cutOffTolerance @property def cutOffType(self): """Value of cutOffType.""" return self._cutOffType @cutOffType.setter def cutOffType(self, cutOffType): try: cutOffType = cutOffType.upper().strip().replace(" ","") if cutOffType not in ["MAGNITUDE", "POTENTIAL"]: raise RROMPyException("Prescribed cutOffType not recognized.") self._cutOffType = cutOffType except: RROMPyWarning(("Prescribed cutOffType not recognized. Overriding " "to 'MAGNITUDE'.")) self._cutOffType = "MAGNITUDE" self._approxParameters["cutOffType"] = self.cutOffType @property def SMarginal(self): """Value of SMarginal.""" return self._SMarginal @SMarginal.setter def SMarginal(self, SMarginal): if SMarginal <= 0: raise RROMPyException("SMarginal must be positive.") if hasattr(self, "_SMarginal") and self._SMarginal is not None: Sold = self.SMarginal else: Sold = -1 self._SMarginal = SMarginal self._approxParameters["SMarginal"] = self.SMarginal if Sold != self.SMarginal: self.resetSamples() @property def polybasisMarginal(self): """Value of polybasisMarginal.""" return self._polybasisMarginal @polybasisMarginal.setter def polybasisMarginal(self, polybasisMarginal): try: polybasisMarginal = polybasisMarginal.upper().strip().replace(" ", "") if polybasisMarginal not in ppb + rbpb + mlspb: raise RROMPyException( "Prescribed marginal polybasis not recognized.") self._polybasisMarginal = polybasisMarginal except: RROMPyWarning(("Prescribed marginal polybasis not recognized. " "Overriding to 'MONOMIAL'.")) self._polybasisMarginal = "MONOMIAL" self._approxParameters["polybasisMarginal"] = self.polybasisMarginal @property def MMarginal(self): """Value of MMarginal.""" return self._MMarginal @MMarginal.setter def MMarginal(self, MMarginal): if MMarginal < 0: raise RROMPyException("MMarginal must be non-negative.") self._MMarginal = MMarginal self._approxParameters["MMarginal"] = self.MMarginal @property def polydegreetypeMarginal(self): """Value of polydegreetypeMarginal.""" return self._polydegreetypeMarginal @polydegreetypeMarginal.setter def polydegreetypeMarginal(self, polydegreetypeM): try: polydegreetypeM = polydegreetypeM.upper().strip().replace(" ","") if polydegreetypeM not in ["TOTAL", "FULL"]: raise RROMPyException(("Prescribed polydegreetypeMarginal not " "recognized.")) self._polydegreetypeMarginal = polydegreetypeM except: RROMPyWarning(("Prescribed polydegreetypeMarginal not recognized. " "Overriding to 'TOTAL'.")) self._polydegreetypeMarginal = "TOTAL" self._approxParameters["polydegreetypeMarginal"] = ( self.polydegreetypeMarginal) @property def radialDirectionalWeightsMarginal(self): """Value of radialDirectionalWeightsMarginal.""" return self._radialDirectionalWeightsMarginal @radialDirectionalWeightsMarginal.setter def radialDirectionalWeightsMarginal(self, radialDirWeightsMarginal): self._radialDirectionalWeightsMarginal = radialDirWeightsMarginal self._approxParameters["radialDirectionalWeightsMarginal"] = ( self.radialDirectionalWeightsMarginal) @property def nNearestNeighborMarginal(self): """Value of nNearestNeighborMarginal.""" return self._nNearestNeighborMarginal @nNearestNeighborMarginal.setter def nNearestNeighborMarginal(self, nNearestNeighborMarginal): self._nNearestNeighborMarginal = nNearestNeighborMarginal self._approxParameters["nNearestNeighborMarginal"] = ( self.nNearestNeighborMarginal) @property def interpRcondMarginal(self): """Value of interpRcondMarginal.""" return self._interpRcondMarginal @interpRcondMarginal.setter def interpRcondMarginal(self, interpRcondMarginal): self._interpRcondMarginal = interpRcondMarginal self._approxParameters["interpRcondMarginal"] = ( self.interpRcondMarginal) @property def directionPivot(self): """Value of directionPivot. Its assignment may reset snapshots.""" return self._directionPivot @directionPivot.setter def directionPivot(self, directionPivot): if hasattr(self, '_directionPivot'): directionPivotOld = copy(self.directionPivot) else: directionPivotOld = None if (directionPivotOld is None or len(directionPivot) != len(directionPivotOld) or not directionPivot == directionPivotOld): self.resetSamples() self._directionPivot = directionPivot @property def directionMarginal(self): return [x for x in range(self.HFEngine.npar) \ if x not in self.directionPivot] @property def nparPivot(self): return len(self.directionPivot) @property def nparMarginal(self): return self.npar - self.nparPivot @property def rescalingExpPivot(self): return [self.HFEngine.rescalingExp[x] for x in self.directionPivot] @property def rescalingExpMarginal(self): return [self.HFEngine.rescalingExp[x] for x in self.directionMarginal] @property def muBoundsPivot(self): """Value of muBoundsPivot.""" return self.samplerPivot.lims @property def muBoundsMarginal(self): """Value of muBoundsMarginal.""" return self.samplerMarginal.lims @property def samplerPivot(self): """Value of samplerPivot.""" return self._samplerPivot @samplerPivot.setter def samplerPivot(self, samplerPivot): if 'generatePoints' not in dir(samplerPivot): raise RROMPyException("Pivot sampler type not recognized.") if hasattr(self, '_samplerPivot') and self._samplerPivot is not None: samplerOld = self.samplerPivot self._samplerPivot = samplerPivot self._approxParameters["samplerPivot"] = self.samplerPivot.__str__() if not 'samplerOld' in locals() or samplerOld != self.samplerPivot: self.resetSamples() @property def samplerMarginal(self): """Value of samplerMarginal.""" return self._samplerMarginal @samplerMarginal.setter def samplerMarginal(self, samplerMarginal): if 'generatePoints' not in dir(samplerMarginal): raise RROMPyException("Marginal sampler type not recognized.") if (hasattr(self, '_samplerMarginal') and self._samplerMarginal is not None): samplerOld = self.samplerMarginal self._samplerMarginal = samplerMarginal self._approxParameters["samplerMarginal"] = ( self.samplerMarginal.__str__()) if not 'samplerOld' in locals() or samplerOld != self.samplerMarginal: self.resetSamples() def resetSamples(self): """Reset samples.""" super().resetSamples() self._musMUniqueCN = None self._derMIdxs = None self._reorderM = None def setSamples(self, samplingEngine): """Copy samplingEngine and samples.""" super().setSamples(samplingEngine) self.mus = copy(self.samplingEngine[0].mus) for sEj in self.samplingEngine[1:]: self.mus.append(sEj.mus) def computeSnapshots(self): """Compute snapshots of solution map.""" RROMPyAssert(self._mode, message = "Cannot start snapshot computation.") if self.samplingEngine.nsamplesTot != self.S * self.SMarginal: self.computeScaleFactor() self.resetSamples() vbMng(self, "INIT", "Starting computation of snapshots.", 5) - self.HFEngine.buildA() - self.HFEngine.buildb() self.musPivot = self.samplerPivot.generatePoints(self.S) self.musMarginal = self.samplerMarginal.generatePoints( self.SMarginal) self.mus = emptyParameterList() self.mus.reset((self.S * self.SMarginal, self.HFEngine.npar)) self.samplingEngine.resetHistory(self.SMarginal) for j, muMarg in enumerate(self.musMarginal): for k in range(j * self.S, (j + 1) * self.S): self.mus.data[k, self.directionPivot] = ( self.musPivot[k - j * self.S].data) self.mus.data[k, self.directionMarginal] = muMarg.data self.samplingEngine.iterSample(self.musPivot, self.musMarginal) if self.POD: self.samplingEngine.coalesceSamples(self.interpRcondMarginal) else: self.samplingEngine.coalesceSamples() vbMng(self, "DEL", "Done computing snapshots.", 5) def _setupMarginalInterpolationIndices(self): """Setup parameters for polyvander.""" RROMPyAssert(self._mode, message = "Cannot setup interpolation indices.") if (self._musMUniqueCN is None or len(self._reorderM) != len(self.musMarginal)): self._musMUniqueCN, musMIdxsTo, musMIdxs, musMCount = ( self.trainedModel.centerNormalizeMarginal(self.musMarginal)\ .unique(return_index = True, return_inverse = True, return_counts = True)) self._musMUnique = self.musMarginal[musMIdxsTo] self._derMIdxs = [None] * len(self._musMUniqueCN) self._reorderM = np.empty(len(musMIdxs), dtype = int) filled = 0 for j, cnt in enumerate(musMCount): self._derMIdxs[j] = nextDerivativeIndices([], self.nparMarginal, cnt) jIdx = np.nonzero(musMIdxs == j)[0] self._reorderM[jIdx] = np.arange(filled, filled + cnt) filled += cnt def _setupMarginalInterp(self): """Compute marginal interpolator.""" RROMPyAssert(self._mode, message = "Cannot setup numerator.") vbMng(self, "INIT", "Starting computation of marginal interpolator.", 7) self._setupMarginalInterpolationIndices() if self.polydegreetypeMarginal == "TOTAL": cfun = totalDegreeN else: cfun = fullDegreeN MM = copy(self.MMarginal) while len(self.musMarginal) < cfun(MM, self.nparMarginal): MM -= 1 if MM < self.MMarginal: RROMPyWarning( ("MMarginal too large compared to SMarginal. " "Reducing MMarginal by {}").format(self.MMarginal - MM)) self.MMarginal = MM mI = [] for j in range(len(self.musMarginal)): canonicalj = 1. * (np.arange(len(self.musMarginal)) == j) self._MMarginal = MM while self.MMarginal >= 0: if self.polybasisMarginal in ppb: p = PI() wellCond, msg = p.setupByInterpolation( self._musMUniqueCN, canonicalj, self.MMarginal, self.polybasisMarginal, self.verbosity >= 5, self.polydegreetypeMarginal == "TOTAL", {"derIdxs": self._derMIdxs, "reorder": self._reorderM, "scl": np.power(self.scaleFactorMarginal, -1.)}, {"rcond": self.interpRcondMarginal}) elif self.polybasisMarginal in rbpb: p = RBI() wellCond, msg = p.setupByInterpolation( self._musMUniqueCN, canonicalj, self.MMarginal, self.polybasisMarginal, self.radialDirectionalWeightsMarginal, self.verbosity >= 5, self.polydegreetypeMarginal == "TOTAL", {"derIdxs": self._derMIdxs, "reorder": self._reorderM, "scl": np.power(self.scaleFactorMarginal, -1.), "nNearestNeighbor" : self.nNearestNeighborMarginal}, {"rcond": self.interpRcondMarginal}) else:# if self.polybasisMarginal in mlspb: p = MLSI() wellCond, msg = p.setupByInterpolation( self._musMUniqueCN, canonicalj, self.MMarginal, self.polybasisMarginal, self.radialDirectionalWeightsMarginal, self.verbosity >= 5, self.polydegreetypeMarginal == "TOTAL", {"derIdxs": self._derMIdxs, "reorder": self._reorderM, "scl": np.power(self.scaleFactorMarginal, -1.), "nNearestNeighbor" : self.nNearestNeighborMarginal}) vbMng(self, "MAIN", msg, 5) if wellCond: break RROMPyWarning(("Polyfit is poorly conditioned. Reducing " "MMarginal by 1.")) self.MMarginal = self.MMarginal - 1 mI = mI + [copy(p)] vbMng(self, "DEL", "Done computing marginal interpolator.", 7) return mI - def normApprox(self, mu:paramList) -> float: - """ - Compute norm of approximant at arbitrary parameter. - - Args: - mu: Target parameter. - - Returns: - Target norm of approximant. - """ - if not self.POD: return super().normApprox(mu) - 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.scaleFactorPivot = .5 * np.abs( self.muBoundsPivot[0] ** self.rescalingExpPivot - self.muBoundsPivot[1] ** self.rescalingExpPivot) self.scaleFactorMarginal = .5 * np.abs( self.muBoundsMarginal[0] ** self.rescalingExpMarginal - self.muBoundsMarginal[1] ** self.rescalingExpMarginal) self.scaleFactor = np.empty(self.npar) self.scaleFactor[self.directionPivot] = self.scaleFactorPivot self.scaleFactor[self.directionMarginal] = self.scaleFactorMarginal diff --git a/rrompy/reduction_methods/pivoted/rational_interpolant_pivoted.py b/rrompy/reduction_methods/pivoted/rational_interpolant_pivoted.py index fccec16..60ffa70 100644 --- a/rrompy/reduction_methods/pivoted/rational_interpolant_pivoted.py +++ b/rrompy/reduction_methods/pivoted/rational_interpolant_pivoted.py @@ -1,624 +1,641 @@ # 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_pivoted_approximant import GenericPivotedApproximant from rrompy.reduction_methods.standard.rational_interpolant import ( RationalInterpolant as RI) from rrompy.utilities.poly_fitting.polynomial import ( polybases as ppb, polyfitname, polyvander as pvP, polyvanderTotal as pvTP, PolynomialInterpolator as PI) from rrompy.utilities.poly_fitting.radial_basis import (polybases as rbpb, RadialBasisInterpolator as RBI) from rrompy.utilities.poly_fitting.moving_least_squares import ( polybases as mlspb, MovingLeastSquaresInterpolator as MLSI) -from rrompy.reduction_methods.trained_model import (TrainedModelPivotedData, - TrainedModelPivotedRational as tModel) from rrompy.utilities.base.types import (Np1D, Np2D, HFEng, DictAny, Tuple, List, ListAny, paramVal) from rrompy.utilities.base import verbosityManager as vbMng, freepar as fp from rrompy.utilities.numerical import (multifactorial, customPInv, dot, fullDegreeN, totalDegreeN, degreeTotalToFull, fullDegreeMaxMask, totalDegreeMaxMask, nextDerivativeIndices, hashDerivativeToIdx as hashD, hashIdxToDerivative as hashI) from rrompy.utilities.exception_manager import (RROMPyException, RROMPyAssert, RROMPyWarning) from rrompy.parameter import checkParameter __all__ = ['RationalInterpolantPivoted'] class RationalInterpolantPivoted(GenericPivotedApproximant): """ ROM pivoted rational interpolant (with pole matching) computation for parametric problems. Args: HFEngine: HF problem solver. mu0(optional): Default parameter. Defaults to 0. directionPivot(optional): Pivot components. 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; - 'matchingWeight': weight for pole matching optimization; defaults to 1; - 'cutOffTolerance': tolerance for ignoring parasitic poles; defaults to np.inf; - 'cutOffType': rule for tolerance computation for parasitic poles; defaults to 'MAGNITUDE'; - 'S': total number of pivot samples current approximant relies upon; - 'samplerPivot': pivot sample point generator; - 'SMarginal': total number of marginal samples current approximant relies upon; - 'samplerMarginal': marginal sample point generator; - 'polybasisPivot': type of polynomial basis for pivot interpolation; defaults to 'MONOMIAL'; - 'polybasisMarginal': type of polynomial basis for marginal interpolation; allowed values include 'MONOMIAL', 'CHEBYSHEV' and 'LEGENDRE'; defaults to 'MONOMIAL'; - 'M': degree of rational interpolant numerator; defaults to 0; - 'N': degree of rational interpolant denominator; defaults to 0; - 'polydegreetype': type of polynomial degree; defaults to 'TOTAL'; - 'MMarginal': degree of marginal interpolant; defaults to 0; - 'polydegreetypeMarginal': type of polynomial degree for marginal; defaults to 'TOTAL'; - 'radialDirectionalWeightsPivot': radial basis weights for pivot numerator; defaults to 0, i.e. identity; - 'radialDirectionalWeightsMarginal': radial basis weights for marginal interpolant; defaults to 0, i.e. identity; - 'nNearestNeighborPivot': number of pivot nearest neighbors considered if polybasisPivot allows; defaults to -1; - 'nNearestNeighborMarginal': number of marginal nearest neighbors considered if polybasisMarginal allows; defaults to -1; - 'interpRcondPivot': tolerance for pivot interpolation; defaults to None; - 'interpRcondMarginal': tolerance for marginal interpolation; defaults to None; - 'robustTol': tolerance for robust rational denominator management; defaults to 0. Defaults to empty dict. + approx_state(optional): Whether to approximate state. Defaults to + False. verbosity(optional): Verbosity level. Defaults to 10. Attributes: HFEngine: HF problem solver. mu0: Default parameter. directionPivot: Pivot components. mus: Array of snapshot parameters. musPivot: Array of pivot snapshot parameters. musMarginal: Array of marginal snapshot parameters. 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; - 'matchingWeight': weight for pole matching optimization; - 'cutOffTolerance': tolerance for ignoring parasitic poles; - 'cutOffType': rule for tolerance computation for parasitic poles; - 'polybasisPivot': type of polynomial basis for pivot interpolation; - 'polybasisMarginal': type of polynomial basis for marginal interpolation; - 'M': degree of rational interpolant numerator; - 'N': degree of rational interpolant denominator; - 'polydegreetype': type of polynomial degree; - 'MMarginal': degree of marginal interpolant; - 'polydegreetypeMarginal': type of polynomial degree for marginal; - 'radialDirectionalWeightsPivot': radial basis weights for pivot numerator; - 'radialDirectionalWeightsMarginal': radial basis weights for marginal interpolant; - 'nNearestNeighborPivot': number of pivot nearest neighbors considered if polybasisPivot allows; - 'nNearestNeighborMarginal': number of marginal nearest neighbors considered if polybasisMarginal allows; - 'interpRcondPivot': tolerance for pivot interpolation; - 'interpRcondMarginal': tolerance for marginal interpolation; - 'robustTol': tolerance for robust rational denominator management. parameterListCritical: Recognized keys of critical approximant parameters: - 'S': total number of pivot samples current approximant relies upon; - 'samplerPivot': pivot sample point generator; - 'SMarginal': total number of marginal samples current approximant relies upon; - 'samplerMarginal': marginal sample point generator. + approx_state: Whether to approximate state. + verbosity: Verbosity level. POD: Whether to compute POD of snapshots. matchingWeight: Weight for pole matching optimization. cutOffTolerance: Tolerance for ignoring parasitic poles. cutOffType: Rule for tolerance computation for parasitic poles. S: Total number of pivot samples current approximant relies upon. sampler: Pivot sample point generator. polybasisPivot: Type of polynomial basis for pivot interpolation. polybasisMarginal: Type of polynomial basis for marginal interpolation. M: Numerator degree of approximant. N: Denominator degree of approximant. polydegreetype: Type of polynomial degree. MMarginal: Degree of marginal interpolant. polydegreetypeMarginal: Type of polynomial degree for marginal. radialDirectionalWeightsPivot: Radial basis weights for pivot numerator. radialDirectionalWeightsMarginal: Radial basis weights for marginal interpolant. nNearestNeighborPivot: Number of pivot nearest neighbors considered if polybasisPivot allows. nNearestNeighborMarginal: Number of marginal nearest neighbors considered if polybasisMarginal allows. interpRcondPivot: Tolerance for pivot interpolation. interpRcondMarginal: Tolerance for marginal interpolation. robustTol: Tolerance for robust rational denominator management. muBoundsPivot: list of bounds for pivot parameter values. muBoundsMarginal: list of bounds for marginal 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. uApproxReduced: Reduced approximate solution(s) with parameter(s) lastSolvedApprox as sampleList. lastSolvedApproxReduced: Parameter(s) corresponding to last computed reduced approximate solution(s) as parameterList. uApprox: Approximate solution(s) with parameter(s) lastSolvedApprox as sampleList. 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, directionPivot : ListAny = [0], - approxParameters : DictAny = {}, verbosity : int = 10, - timestamp : bool = True): + approxParameters : DictAny = {}, approx_state : bool = False, + verbosity : int = 10, timestamp : bool = True): self._preInit() self._addParametersToList(["polybasisPivot", "M", "N", "polydegreetype", "radialDirectionalWeightsPivot", "nNearestNeighborPivot", "interpRcondPivot", "robustTol"], ["MONOMIAL", 0, 0, "TOTAL", 1, -1, -1, 0]) super().__init__(HFEngine = HFEngine, mu0 = mu0, directionPivot = directionPivot, approxParameters = approxParameters, - verbosity = verbosity, timestamp = timestamp) + approx_state = approx_state, verbosity = verbosity, + timestamp = timestamp) self._postInit() + @property + def tModelType(self): + from rrompy.reduction_methods.trained_model import \ + TrainedModelPivotedRational + return TrainedModelPivotedRational + + def initializeModelData(self, datadict): + from rrompy.reduction_methods.trained_model import \ + TrainedModelPivotedData + return (TrainedModelPivotedData(datadict["mu0"], + datadict.pop("projMat"), + datadict["scaleFactor"], + datadict.pop("rescalingExp"), + datadict["directionPivot"]), + ["mu0", "scaleFactor", "directionPivot", "mus"]) + @property def polybasisPivot(self): """Value of polybasisPivot.""" return self._polybasisPivot @polybasisPivot.setter def polybasisPivot(self, polybasisPivot): try: polybasisPivot = polybasisPivot.upper().strip().replace(" ","") if polybasisPivot not in ppb + rbpb + mlspb: raise RROMPyException( "Prescribed pivot polybasis not recognized.") self._polybasisPivot = polybasisPivot except: RROMPyWarning(("Prescribed pivot polybasis not recognized. " "Overriding to 'MONOMIAL'.")) self._polybasisPivot = "MONOMIAL" self._approxParameters["polybasisPivot"] = self.polybasisPivot @property def polybasisPivot0(self): if "_" in self.polybasisPivot: return self.polybasisPivot.split("_")[0] return self.polybasisPivot @property def radialDirectionalWeightsPivot(self): """Value of radialDirectionalWeightsPivot.""" return self._radialDirectionalWeightsPivot @radialDirectionalWeightsPivot.setter def radialDirectionalWeightsPivot(self, radialDirectionalWeightsPivot): self._radialDirectionalWeightsPivot = radialDirectionalWeightsPivot self._approxParameters["radialDirectionalWeightsPivot"] = ( self.radialDirectionalWeightsPivot) @property def nNearestNeighborPivot(self): """Value of nNearestNeighborPivot.""" return self._nNearestNeighborPivot @nNearestNeighborPivot.setter def nNearestNeighborPivot(self, nNearestNeighborPivot): self._nNearestNeighborPivot = nNearestNeighborPivot self._approxParameters["nNearestNeighborPivot"] = ( self.nNearestNeighborPivot) @property def interpRcondPivot(self): """Value of interpRcondPivot.""" return self._interpRcondPivot @interpRcondPivot.setter def interpRcondPivot(self, interpRcondPivot): self._interpRcondPivot = interpRcondPivot self._approxParameters["interpRcondPivot"] = self.interpRcondPivot @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 @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 @property def polydegreetype(self): """Value of polydegreetype.""" return self._polydegreetype @polydegreetype.setter def polydegreetype(self, polydegreetype): try: polydegreetype = polydegreetype.upper().strip().replace(" ","") if polydegreetype not in ["TOTAL", "FULL"]: raise RROMPyException(("Prescribed polydegreetype not " "recognized.")) self._polydegreetype = polydegreetype except: RROMPyWarning(("Prescribed polydegreetype not recognized. " "Overriding to 'TOTAL'.")) self._polydegreetype = "TOTAL" self._approxParameters["polydegreetype"] = self.polydegreetype @property def robustTol(self): """Value of tolerance for robust rational 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 def resetSamples(self): """Reset samples.""" super().resetSamples() self._musPUniqueCN = None self._derPIdxs = None self._reorderP = None def _setupPivotInterpolationIndices(self): """Setup parameters for polyvander.""" RROMPyAssert(self._mode, message = "Cannot setup interpolation indices.") if (self._musPUniqueCN is None or len(self._reorderP) != len(self.musPivot)): self._musPUniqueCN, musPIdxsTo, musPIdxs, musPCount = ( self.trainedModel.centerNormalizePivot(self.musPivot).unique( return_index = True, return_inverse = True, return_counts = True)) self._musPUnique = self.mus[musPIdxsTo] self._derPIdxs = [None] * len(self._musPUniqueCN) self._reorderP = np.empty(len(musPIdxs), dtype = int) filled = 0 for j, cnt in enumerate(musPCount): self._derPIdxs[j] = nextDerivativeIndices([], self.nparPivot, cnt) jIdx = np.nonzero(musPIdxs == j)[0] self._reorderP[jIdx] = np.arange(filled, filled + cnt) filled += cnt def _setupDenominator(self): """Compute rational denominator.""" RROMPyAssert(self._mode, message = "Cannot setup denominator.") vbMng(self, "INIT", "Starting computation of denominator.", 7) NinvD = None N0 = copy(self.N) qs = [] self.verbosity -= 10 for j in range(len(self.musMarginal)): self._N = N0 while self.N > 0: if NinvD != self.N: invD, fitinvP = self._computeInterpolantInverseBlocks() NinvD = self.N if self.POD: ev, eV = RI.findeveVGQR(self, self.samplingEngine.RPOD[j], invD) else: ev, eV = RI.findeveVGExplicit(self, self.samplingEngine.samples[j], invD) nevBad = checkRobustTolerance(ev, self.robustTol) if nevBad <= 1: break if self.catchInstability: raise RROMPyException(("Instability in denominator " "computation: eigenproblem is " "poorly conditioned.")) RROMPyWarning(("Smallest {} eigenvalues below tolerance. " "Reducing N by 1.").format(nevBad)) self.N = self.N - 1 if self.N <= 0: self._N = 0 eV = np.ones((1, 1)) q = PI() q.npar = self.nparPivot q.polybasis = self.polybasisPivot0 if self.polydegreetype == "TOTAL": q.coeffs = degreeTotalToFull(tuple([self.N + 1] * q.npar), q.npar, eV[:, 0]) else: q.coeffs = eV[:, 0].reshape([self.N + 1] * q.npar) qs = qs + [copy(q)] self.verbosity += 10 vbMng(self, "DEL", "Done computing denominator.", 7) return qs, fitinvP def _setupNumerator(self): """Compute rational numerator.""" RROMPyAssert(self._mode, message = "Cannot setup numerator.") vbMng(self, "INIT", "Starting computation of numerator.", 7) Qevaldiag = np.zeros((len(self.musPivot), len(self.musPivot)), dtype = np.complex) verb = self.trainedModel.verbosity self.trainedModel.verbosity = 0 self._setupPivotInterpolationIndices() cfun = totalDegreeN if self.polydegreetype == "TOTAL" else fullDegreeN M = copy(self.M) while len(self.musPivot) < cfun(M, self.nparPivot): M -= 1 if M < self.M: RROMPyWarning(("M too large compared to S. Reducing M by " "{}").format(self.M - M)) self.M = M tensor_idx = 0 ps = [] for k, muM in enumerate(self.musMarginal): self._M = M idxGlob = 0 for j, derIdxs in enumerate(self._derPIdxs): mujEff = [fp] * self.npar for jj, kk in enumerate(self.directionPivot): mujEff[kk] = self._musPUnique[j, jj] for jj, kk in enumerate(self.directionMarginal): mujEff[kk] = muM(0, jj) mujEff = checkParameter(mujEff, self.npar) nder = len(derIdxs) idxLoc = np.arange(len(self.musPivot))[ (self._reorderP >= idxGlob) * (self._reorderP < idxGlob + nder)] idxGlob += nder Qval = [0] * nder for der in range(nder): derIdx = hashI(der, self.nparPivot) derIdxEff = [0] * self.npar sclEff = [0] * self.npar for jj, kk in enumerate(self.directionPivot): derIdxEff[kk] = derIdx[jj] sclEff[kk] = self.scaleFactorPivot[jj] ** -1. Qval[der] = (self.trainedModel.getQVal(mujEff, derIdxEff, scl = sclEff) / 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] while self.M >= 0: if self.polybasisPivot in ppb: p = PI() wellCond, msg = p.setupByInterpolation( self._musPUniqueCN, Qevaldiag, self.M, self.polybasisPivot, self.verbosity >= 5, self.polydegreetype == "TOTAL", {"derIdxs": self._derPIdxs, "reorder": self._reorderP, "scl": np.power(self.scaleFactorPivot, -1.)}, {"rcond": self.interpRcondPivot}) elif self.polybasisPivot in rbpb: p = RBI() wellCond, msg = p.setupByInterpolation( self._musPUniqueCN, Qevaldiag, self.M, self.polybasisPivot, self.radialDirectionalWeightsPivot, self.verbosity >= 5, self.polydegreetype == "TOTAL", {"derIdxs": self._derPIdxs, "reorder": self._reorderP, "scl": np.power(self.scaleFactorPivot, -1.), "nNearestNeighbor" : self.nNearestNeighborPivot}, {"rcond": self.interpRcondPivot}) else:# if self.polybasisPivot in mlspb: p = MLSI() wellCond, msg = p.setupByInterpolation( self._musPUniqueCN, Qevaldiag, self.M, self.polybasisPivot, self.radialDirectionalWeightsPivot, self.verbosity >= 5, self.polydegreetype == "TOTAL", {"derIdxs": self._derPIdxs, "reorder": self._reorderP, "scl": np.power(self.scaleFactorPivot, -1.), "nNearestNeighbor" : self.nNearestNeighborPivot}) vbMng(self, "MAIN", msg, 5) if wellCond: break if self.catchInstability: raise RROMPyException(("Instability in numerator " "computation: polyfit is " "poorly conditioned.")) RROMPyWarning(("Polyfit is poorly conditioned. " "Reducing M by 1.")) self.M = self.M - 1 tensor_idx_new = tensor_idx + Qevaldiag.shape[1] if self.POD: p.postmultiplyTensorize(self.samplingEngine.RPODCoalesced.T[ tensor_idx : tensor_idx_new, :]) else: p.pad(tensor_idx, len(self.mus) - tensor_idx_new) tensor_idx = tensor_idx_new ps = ps + [copy(p)] self.trainedModel.verbosity = verb vbMng(self, "DEL", "Done computing numerator.", 7) return ps def setupApprox(self): """ Compute rational interpolant. SVD-based robust eigenvalue management. """ if self.checkComputedApprox(): return RROMPyAssert(self._mode, message = "Cannot setup approximant.") vbMng(self, "INIT", "Setting up {}.". format(self.name()), 5) self.computeSnapshots() + pMat = self.samplingEngine.samplesCoalesced.data + pMatEff = dot(self.HFEngine.C, pMat) if self.approx_state else pMat if self.trainedModel is None: - self.trainedModel = tModel() + self.trainedModel = self.tModelType() self.trainedModel.verbosity = self.verbosity self.trainedModel.timestamp = self.timestamp - data = TrainedModelPivotedData(self.trainedModel.name(), self.mu0, - self.samplingEngine.samplesCoalesced, - self.scaleFactor, - self.HFEngine.rescalingExp, - self.directionPivot) - data.musPivot = copy(self.musPivot) - data.musMarginal = copy(self.musMarginal) - self.trainedModel.data = data + datadict = {"mu0": self.mu0, "projMat": pMatEff, + "scaleFactor": self.scaleFactor, + "rescalingExp": self.HFEngine.rescalingExp, + "directionPivot": self.directionPivot} + self.trainedModel.data = self.initializeModelData(datadict)[0] else: self.trainedModel = self.trainedModel - self.trainedModel.data.projMat = copy( - self.samplingEngine.samplesCoalesced) + self.trainedModel.data.projMat = copy(pMatEff) + self.trainedModel.data.musPivot = copy(self.musPivot) + self.trainedModel.data.musMarginal = copy(self.musMarginal) self.trainedModel.data.marginalInterp = self._setupMarginalInterp() if self.N > 0: Qs = self._setupDenominator()[0] else: Q = PI() Q.npar = self.nparPivot Q.coeffs = np.ones(tuple([1] * Q.npar), dtype = self.musMarginal.dtype) Q.polybasis = self.polybasisPivot0 Qs = [Q for _ in range(len(self.musMarginal))] - self.trainedModel.data._temporary = True + self.trainedModel.data._temporary = 1 self.trainedModel.data.Qs = Qs self.trainedModel.data.Ps = self._setupNumerator() vbMng(self, "INIT", "Matching poles.", 10) self.trainedModel.initializeFromRational(self.HFEngine, - self.matchingWeight, self.POD) + self.matchingWeight, self.POD, + self.approx_state) + del self.trainedModel.data._temporary vbMng(self, "DEL", "Done matching poles.", 10) if not np.isinf(self.cutOffTolerance): vbMng(self, "INIT", "Recompressing by cut-off.", 10) msg = self.trainedModel.recompressByCutOff([-1., 1.], self.cutOffTolerance, self.cutOffType) vbMng(self, "DEL", "Done recompressing." + msg, 10) self.trainedModel.data.approxParameters = copy(self.approxParameters) vbMng(self, "DEL", "Done setting up approximant.", 5) def _computeInterpolantInverseBlocks(self) -> Tuple[List[Np2D], Np2D]: """ Compute inverse factors for minimal interpolant target functional. """ RROMPyAssert(self._mode, message = "Cannot solve eigenvalue problem.") self._setupPivotInterpolationIndices() cfun = totalDegreeN if self.polydegreetype == "TOTAL" else fullDegreeN N = copy(self.N) while len(self.musPivot) < cfun(N, self.nparPivot): N -= 1 if N < self.N: RROMPyWarning(("N too large compared to S. Reducing N by " "{}").format(self.N - N)) self.N = N while self.N >= 0: if self.polydegreetype == "TOTAL": - TE, _, argIdxs = pvTP(self._musPUniqueCN, self.N, - self.polybasisPivot0, self._derPIdxs, - self._reorderP, - scl = np.power(self.scaleFactorPivot, -1.)) - TE = TE[:, argIdxs] + TE = pvTP(self._musPUniqueCN, self.N, self.polybasisPivot0, + self._derPIdxs, self._reorderP, + scl = np.power(self.scaleFactorPivot, -1.)) idxsB = totalDegreeMaxMask(self.N, self.nparPivot) else: #if self.polydegreetype == "FULL": TE = pvP(self._musPUniqueCN, [self.N] * self.nparPivot, self.polybasisPivot0, self._derPIdxs, self._reorderP, scl = np.power(self.scaleFactorPivot, -1.)) idxsB = fullDegreeMaxMask(self.N, self.nparPivot) fitOut = customPInv(TE, rcond = self.interpRcondPivot, full = True) vbMng(self, "MAIN", ("Fitting {} samples with degree {} through {}... " "Conditioning of pseudoinverse system: {:.4e}.").format( TE.shape[0], self.N, polyfitname(self.polybasisPivot0), fitOut[1][1][0] / fitOut[1][1][-1]), 5) if fitOut[1][0] == TE.shape[1]: fitinvP = fitOut[0][idxsB, :] break RROMPyWarning("Polyfit is poorly conditioned. Reducing N by 1.") self.N -= 1 if self.N < 0: raise RROMPyException(("Instability in computation of " "denominator. Aborting.")) - TN, _, argIdxs = pvTP(self._musPUniqueCN, self.N, self.polybasisPivot0, - self._derPIdxs, self._reorderP, - scl = np.power(self.scaleFactorPivot, -1.)) - TN = TN[:, argIdxs] + TN = pvTP(self._musPUniqueCN, self.N, self.polybasisPivot0, + self._derPIdxs, self._reorderP, + scl = np.power(self.scaleFactorPivot, -1.)) invD = [None] * (len(idxsB)) for k in range(len(idxsB)): pseudoInv = np.diag(fitinvP[k, :]) idxGlob = 0 for j, derIdxs in enumerate(self._derPIdxs): nder = len(derIdxs) idxGlob += nder if nder > 1: idxLoc = np.arange(len(self.musPivot))[ (self._reorderP >= idxGlob - nder) * (self._reorderP < idxGlob)] invLoc = fitinvP[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] = dot(pseudoInv, TN) return invD, fitinvP def getResidues(self, *args, **kwargs) -> Np1D: """ Obtain approximant residues. Returns: Matrix with residues as columns. """ return self.trainedModel.getResidues(*args, **kwargs) - diff --git a/rrompy/reduction_methods/standard/generic_standard_approximant.py b/rrompy/reduction_methods/standard/generic_standard_approximant.py index cd2bb5d..a403e4c 100644 --- a/rrompy/reduction_methods/standard/generic_standard_approximant.py +++ b/rrompy/reduction_methods/standard/generic_standard_approximant.py @@ -1,156 +1,148 @@ # 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.types import DictAny, HFEng, paramVal from rrompy.utilities.base import verbosityManager as vbMng from rrompy.utilities.exception_manager import RROMPyException, RROMPyAssert from rrompy.parameter import checkParameterList __all__ = ['GenericStandardApproximant'] class GenericStandardApproximant(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. + approx_state(optional): Whether to approximate state. Defaults to + False. verbosity(optional): Verbosity level. Defaults to 10. Attributes: HFEngine: HF problem solver. mu0: Default parameter. mus: Array of snapshot parameters. approxParameters: Dictionary containing values for main parameters of approximant. Recognized keys are in parameterList. 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. + approx_state: Whether to approximate state. + verbosity: Verbosity level. 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. uApproxReduced: Reduced approximate solution(s) with parameter(s) lastSolvedApprox as sampleList. lastSolvedApproxReduced: Parameter(s) corresponding to last computed reduced approximate solution(s) as parameterList. uApprox: Approximate solution(s) with parameter(s) lastSolvedApprox as sampleList. lastSolvedApprox: Parameter(s) corresponding to last computed approximate solution(s) as parameterList. """ def __init__(self, HFEngine:HFEng, mu0 : paramVal = None, - approxParameters : DictAny = {}, verbosity : int = 10, - timestamp : bool = True): + approxParameters : DictAny = {}, approx_state : 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, - verbosity = verbosity, timestamp = timestamp) + approx_state = approx_state, 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)[0] 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 != self.S: self.computeScaleFactor() vbMng(self, "INIT", "Starting computation of snapshots.", 5) self.mus = self.sampler.generatePoints(self.S) self.samplingEngine.iterSample(self.mus) vbMng(self, "DEL", "Done computing snapshots.", 5) - def normApprox(self, mu:paramList) -> float: - """ - Compute norm of approximant at arbitrary parameter. - - Args: - mu: Target parameter. - - Returns: - Target norm of approximant. - """ - if not self.POD: return super().normApprox(mu) - 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/standard/rational_interpolant.py b/rrompy/reduction_methods/standard/rational_interpolant.py index 0f50c33..2a4e2c3 100644 --- a/rrompy/reduction_methods/standard/rational_interpolant.py +++ b/rrompy/reduction_methods/standard/rational_interpolant.py @@ -1,561 +1,613 @@ # 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_standard_approximant import GenericStandardApproximant from rrompy.utilities.poly_fitting.polynomial import ( polybases as ppb, polyfitname, polyvander as pvP, polyvanderTotal as pvTP, PolynomialInterpolator as PI) from rrompy.utilities.poly_fitting.radial_basis import (polybases as rbpb, RadialBasisInterpolator as RBI) from rrompy.utilities.poly_fitting.moving_least_squares import ( polybases as mlspb, MovingLeastSquaresInterpolator as MLSI) -from rrompy.reduction_methods.trained_model import ( - TrainedModelRational as tModel) -from rrompy.reduction_methods.trained_model import TrainedModelData from rrompy.utilities.base.types import (Np1D, Np2D, HFEng, DictAny, Tuple, List, paramVal, sampList) from rrompy.utilities.base import verbosityManager as vbMng from rrompy.utilities.numerical import (multifactorial, customPInv, dot, fullDegreeN, totalDegreeN, degreeTotalToFull, fullDegreeMaxMask, totalDegreeMaxMask, nextDerivativeIndices, hashDerivativeToIdx as hashD, hashIdxToDerivative as hashI) from rrompy.utilities.exception_manager import (RROMPyException, RROMPyAssert, RROMPyWarning) __all__ = ['RationalInterpolant'] class RationalInterpolant(GenericStandardApproximant): """ 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; defaults to 'MONOMIAL'; - 'M': degree of rational interpolant numerator; defaults to 0; - 'N': degree of rational interpolant denominator; defaults to 0; - 'polydegreetype': type of polynomial degree; defaults to 'TOTAL'; - 'radialDirectionalWeights': radial basis weights for interpolant numerator; defaults to 0, i.e. identity; - 'nNearestNeighbor': mumber of nearest neighbors considered in numerator if polybasis allows; defaults to -1; - 'interpRcond': tolerance for interpolation; defaults to None; - 'robustTol': tolerance for robust rational denominator - management; defaults to 0. + management; defaults to 0; + - 'centeredLike': whether samples should be managed as if centered; + involves making svd and interpolation problems square; defaults + to False. Defaults to empty dict. + approx_state(optional): Whether to approximate state. Defaults to + False. verbosity(optional): Verbosity level. Defaults to 10. Attributes: HFEngine: HF problem solver. mu0: Default parameter. mus: Array of snapshot parameters. approxParameters: Dictionary containing values for main parameters of approximant. Recognized keys are in parameterList. parameterListSoft: Recognized keys of soft approximant parameters: - 'POD': whether to compute POD of snapshots; - 'polybasis': type of polynomial basis for interpolation; - 'M': degree of rational interpolant numerator; - 'N': degree of rational interpolant denominator; - 'polydegreetype': type of polynomial degree; - 'radialDirectionalWeights': radial basis weights for interpolant numerator; - 'nNearestNeighbor': mumber of nearest neighbors considered in numerator if polybasis allows; - 'interpRcond': tolerance for interpolation via numpy.polyfit; - 'robustTol': tolerance for robust rational denominator - management. + management; + - 'centeredLike': whether samples should be managed as if centered; + involves making svd and interpolation problems square. parameterListCritical: Recognized keys of critical approximant parameters: - 'S': total number of samples current approximant relies upon; - 'sampler': sample point generator. + approx_state: Whether to approximate state. + verbosity: Verbosity level. 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. polydegreetype: Type of polynomial degree. radialDirectionalWeights: Radial basis weights for interpolant numerator. nNearestNeighbor: Number of nearest neighbors considered in numerator if polybasis allows. interpRcond: Tolerance for interpolation via numpy.polyfit. robustTol: Tolerance for robust rational denominator management. + centeredLike: Whether samples should be managed as if centered; + involves making svd and interpolation problems square. 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. uApproxReduced: Reduced approximate solution(s) with parameter(s) lastSolvedApprox as sampleList. lastSolvedApproxReduced: Parameter(s) corresponding to last computed reduced approximate solution(s) as parameterList. uApprox: Approximate solution(s) with parameter(s) lastSolvedApprox as sampleList. 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 = {}, verbosity : int = 10, - timestamp : bool = True): + approxParameters : DictAny = {}, approx_state : bool = False, + verbosity : int = 10, timestamp : bool = True): self._preInit() self._addParametersToList(["polybasis", "M", "N", "polydegreetype", "radialDirectionalWeights", "nNearestNeighbor", "interpRcond", - "robustTol"], - ["MONOMIAL", 0, 0, "TOTAL", 1, -1, -1, 0]) + "robustTol", "centeredLike"], + ["MONOMIAL", 0, 0, "TOTAL", 1, -1, -1, 0, + False]) super().__init__(HFEngine = HFEngine, mu0 = mu0, approxParameters = approxParameters, - verbosity = verbosity, timestamp = timestamp) + approx_state = approx_state, verbosity = verbosity, + timestamp = timestamp) self.catchInstability = False self._postInit() + @property + def tModelType(self): + from rrompy.reduction_methods.trained_model import TrainedModelRational + return TrainedModelRational + @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 ppb + rbpb + mlspb: 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 polybasis0(self): if "_" in self.polybasis: return self.polybasis.split("_")[0] return self.polybasis @property def interpRcond(self): """Value of interpRcond.""" return self._interpRcond @interpRcond.setter def interpRcond(self, interpRcond): self._interpRcond = interpRcond self._approxParameters["interpRcond"] = self.interpRcond @property def radialDirectionalWeights(self): """Value of radialDirectionalWeights.""" return self._radialDirectionalWeights @radialDirectionalWeights.setter def radialDirectionalWeights(self, radialDirectionalWeights): self._radialDirectionalWeights = radialDirectionalWeights self._approxParameters["radialDirectionalWeights"] = ( self.radialDirectionalWeights) @property def nNearestNeighbor(self): """Value of nNearestNeighbor.""" return self._nNearestNeighbor @nNearestNeighbor.setter def nNearestNeighbor(self, nNearestNeighbor): self._nNearestNeighbor = nNearestNeighbor self._approxParameters["nNearestNeighbor"] = self.nNearestNeighbor @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 @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 @property def polydegreetype(self): """Value of polydegreetype.""" return self._polydegreetype @polydegreetype.setter def polydegreetype(self, polydegreetype): try: polydegreetype = polydegreetype.upper().strip().replace(" ","") if polydegreetype not in ["TOTAL", "FULL"]: raise RROMPyException(("Prescribed polydegreetype not " "recognized.")) self._polydegreetype = polydegreetype except: RROMPyWarning(("Prescribed polydegreetype not recognized. " "Overriding to 'TOTAL'.")) self._polydegreetype = "TOTAL" self._approxParameters["polydegreetype"] = self.polydegreetype @property def robustTol(self): """Value of tolerance for robust rational 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 centeredLike(self): + """Whether samples should be managed as if centered.""" + return self._centeredLike + @centeredLike.setter + def centeredLike(self, centeredLike): + if centeredLike and not hasattr(self, "_centeredLike"): + RROMPyWarning(("Centered-like method is unstable for more than " + "one parameter.")) + self._centeredLike = centeredLike + self._approxParameters["centeredLike"] = self.centeredLike + 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.trainedModel.centerNormalize(self.mus).unique( return_index = True, return_inverse = True, return_counts = True)) + if self.centeredLike and len(self._musUniqueCN) > 1: + raise RROMPyException(("Cannot apply centered-like method " + "with more than one distinct sample " + "point.")) 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 rational denominator.""" RROMPyAssert(self._mode, message = "Cannot setup denominator.") vbMng(self, "INIT", "Starting computation of denominator.", 7) while self.N > 0: invD, fitinv = self._computeInterpolantInverseBlocks() + if self.centeredLike: + if self.polydegreetype == "TOTAL": + Seff = totalDegreeN(self.N, self.npar) + else: + Seff = fullDegreeN(self.N, self.npar) + else: + Seff = self.S + idxSamplesEff = list(range(self.S - Seff, self.S)) if self.POD: - ev, eV = self.findeveVGQR(self.samplingEngine.RPOD, invD) + ev, eV = self.findeveVGQR( + self.samplingEngine.RPOD[:, idxSamplesEff], invD) else: - ev, eV = self.findeveVGExplicit(self.samplingEngine.samples, - invD) + ev, eV = self.findeveVGExplicit( + self.samplingEngine.samples(idxSamplesEff), invD) nevBad = checkRobustTolerance(ev, self.robustTol) if nevBad <= 1: break if self.catchInstability: raise RROMPyException(("Instability in denominator " "computation: eigenproblem is poorly " "conditioned.")) RROMPyWarning(("Smallest {} eigenvalues below tolerance. Reducing " "N by 1.").format(nevBad)) self.N = self.N - 1 if self.N <= 0: self._N = 0 eV = np.ones((1, 1)) q = PI() q.npar = self.npar q.polybasis = self.polybasis0 if self.polydegreetype == "TOTAL": q.coeffs = degreeTotalToFull(tuple([self.N + 1] * self.npar), self.npar, eV[:, 0]) else: q.coeffs = eV[:, 0].reshape([self.N + 1] * self.npar) vbMng(self, "DEL", "Done computing denominator.", 7) return q, fitinv def _setupNumerator(self): """Compute rational numerator.""" RROMPyAssert(self._mode, message = "Cannot setup numerator.") vbMng(self, "INIT", "Starting computation of numerator.", 7) 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 cfun = totalDegreeN if self.polydegreetype == "TOTAL" else fullDegreeN M = copy(self.M) while len(self.mus) < cfun(M, self.npar): M -= 1 if M < self.M: RROMPyWarning(("M too large compared to S. Reducing M by " "{}").format(self.M - M)) self.M = M while self.M >= 0: + if self.centeredLike: + Seff = cfun(self.M, self.npar) + derIdxsEff = [self._derIdxs[0][: Seff]] + reorder = self._reorder[: Seff] + QevaldiagEff = Qevaldiag[: Seff, : Seff] + else: + derIdxsEff = self._derIdxs + reorder = self._reorder + QevaldiagEff = Qevaldiag if self.polybasis in ppb: p = PI() wellCond, msg = p.setupByInterpolation( - self._musUniqueCN, Qevaldiag, self.M, + self._musUniqueCN, QevaldiagEff, self.M, self.polybasis, self.verbosity >= 5, self.polydegreetype == "TOTAL", - {"derIdxs": self._derIdxs, - "reorder": self._reorder, + {"derIdxs": derIdxsEff, + "reorder": reorder, "scl": np.power(self.scaleFactor, -1.)}, {"rcond": self.interpRcond}) elif self.polybasis in rbpb: p = RBI() wellCond, msg = p.setupByInterpolation( - self._musUniqueCN, Qevaldiag, self.M, self.polybasis, - self.radialDirectionalWeights, self.verbosity >= 5, - self.polydegreetype == "TOTAL", - {"derIdxs": self._derIdxs, "reorder": self._reorder, + self._musUniqueCN, QevaldiagEff, self.M, + self.polybasis, self.radialDirectionalWeights, + self.verbosity >= 5, self.polydegreetype == "TOTAL", + {"derIdxs": derIdxs, "reorder": reorder, "scl": np.power(self.scaleFactor, -1.), "nNearestNeighbor": self.nNearestNeighbor}, {"rcond": self.interpRcond}) else:# if self.polybasis in mlspb: p = MLSI() wellCond, msg = p.setupByInterpolation( - self._musUniqueCN, Qevaldiag, self.M, self.polybasis, - self.radialDirectionalWeights, self.verbosity >= 5, - self.polydegreetype == "TOTAL", - {"derIdxs": self._derIdxs, "reorder": self._reorder, + self._musUniqueCN, QevaldiagEff, self.M, + self.polybasis, self.radialDirectionalWeights, + self.verbosity >= 5, self.polydegreetype == "TOTAL", + {"derIdxs": derIdxs, "reorder": reorder, "scl": np.power(self.scaleFactor, -1.), "nNearestNeighbor": self.nNearestNeighbor}) vbMng(self, "MAIN", msg, 5) if wellCond: break if self.catchInstability: raise RROMPyException(("Instability in numerator computation: " "polyfit is poorly conditioned.")) RROMPyWarning("Polyfit is poorly conditioned. Reducing M by 1.") self.M = self.M - 1 if self.M < 0: raise RROMPyException(("Instability in computation of numerator. " "Aborting.")) vbMng(self, "DEL", "Done computing numerator.", 7) return p def setupApprox(self): """ Compute rational interpolant. SVD-based robust eigenvalue management. """ if self.checkComputedApprox(): return RROMPyAssert(self._mode, message = "Cannot setup approximant.") vbMng(self, "INIT", "Setting up {}.". format(self.name()), 5) self.computeSnapshots() + pMat = self.samplingEngine.samples.data + pMatEff = dot(self.HFEngine.C, pMat) if self.approx_state else pMat if self.trainedModel is None: - self.trainedModel = tModel() + self.trainedModel = self.tModelType() self.trainedModel.verbosity = self.verbosity self.trainedModel.timestamp = self.timestamp - data = TrainedModelData(self.trainedModel.name(), self.mu0, - self.samplingEngine.samples, - self.scaleFactor, - self.HFEngine.rescalingExp) - data.mus = copy(self.mus) - self.trainedModel.data = data + datadict = {"mu0": self.mu0, "projMat": pMatEff, + "scaleFactor": self.scaleFactor, + "rescalingExp": self.HFEngine.rescalingExp} + self.trainedModel.data = self.initializeModelData(datadict)[0] else: self.trainedModel = self.trainedModel - self.trainedModel.data.projMat = copy(self.samplingEngine.samples) + self.trainedModel.data.projMat = copy(pMatEff) if self.N > 0: Q = self._setupDenominator()[0] else: Q = PI() Q.coeffs = np.ones(tuple([1] * self.npar), dtype = np.complex) Q.npar = self.npar Q.polybasis = self.polybasis + self.trainedModel.data.mus = copy(self.mus) self.trainedModel.data.Q = Q self.trainedModel.data.P = self._setupNumerator() self.trainedModel.data.approxParameters = copy(self.approxParameters) vbMng(self, "DEL", "Done setting up approximant.", 5) def _computeInterpolantInverseBlocks(self) -> Tuple[List[Np2D], Np2D]: """ Compute inverse factors for minimal interpolant target functional. """ RROMPyAssert(self._mode, message = "Cannot solve eigenvalue problem.") self._setupInterpolationIndices() cfun = totalDegreeN if self.polydegreetype == "TOTAL" else fullDegreeN N = copy(self.N) while len(self.mus) < cfun(N, self.npar): N -= 1 if N < self.N: RROMPyWarning(("N too large compared to S. Reducing N by " "{}").format(self.N - N)) self.N = N while self.N >= 0: + if self.centeredLike: + Seff = cfun(self.N, self.npar) + #derIdxsEff = [self._derIdxs[0][- Seff :]] + derIdxsEff = [self._derIdxs[0][: Seff]] + reorder = self._reorder[: Seff] + else: + Seff = len(self.mus) + derIdxsEff = self._derIdxs + reorder = self._reorder if self.polydegreetype == "TOTAL": - TE, _, argIdxs = pvTP(self._musUniqueCN, self.N, - self.polybasis0, self._derIdxs, - self._reorder, - scl = np.power(self.scaleFactor, -1.)) - TE = TE[:, argIdxs] + TE = pvTP(self._musUniqueCN, self.N, self.polybasis0, + derIdxsEff, reorder, + scl = np.power(self.scaleFactor, -1.)) idxsB = totalDegreeMaxMask(self.N, self.npar) else: #if self.polydegreetype == "FULL": TE = pvP(self._musUniqueCN, [self.N] * self.npar, - self.polybasis0, self._derIdxs, self._reorder, + self.polybasis0, derIdxsEff, reorder, scl = np.power(self.scaleFactor, -1.)) idxsB = fullDegreeMaxMask(self.N, self.npar) fitOut = customPInv(TE, rcond = self.interpRcond, full = True) vbMng(self, "MAIN", ("Fitting {} samples with degree {} through {}... " "Conditioning of pseudoinverse system: {:.4e}.").format( TE.shape[0], self.N, polyfitname(self.polybasis0), fitOut[1][1][0] / fitOut[1][1][-1]), 5) if fitOut[1][0] == TE.shape[1]: fitinv = fitOut[0][idxsB, :] break if self.catchInstability: raise RROMPyException(("Instability in denominator " "computation: polyfit is poorly " "conditioned.")) RROMPyWarning("Polyfit is poorly conditioned. Reducing N by 1.") self.N = self.N - 1 if self.polydegreetype == "TOTAL": - TN, _, argIdxs = pvTP(self._musUniqueCN, self.N, self.polybasis0, - self._derIdxs, self._reorder, - scl = np.power(self.scaleFactor, -1.)) - TN = TN[:, argIdxs] + TN = pvTP(self._musUniqueCN, self.N, self.polybasis0, derIdxsEff, + reorder, scl = np.power(self.scaleFactor, -1.)) else: #if self.polydegreetype == "FULL": - TN = pvP(self._musUniqueCN, [self.N] * self.npar, - self.polybasis0, self._derIdxs, self._reorder, + TN = pvP(self._musUniqueCN, [self.N] * self.npar, self.polybasis0, + derIdxsEff, reorder, scl = np.power(self.scaleFactor, -1.)) invD = [None] * (len(idxsB)) for k in range(len(idxsB)): pseudoInv = np.diag(fitinv[k, :]) idxGlob = 0 - for j, derIdxs in enumerate(self._derIdxs): + for j, derIdxs in enumerate(derIdxsEff): nder = len(derIdxs) idxGlob += nder if nder > 1: - idxLoc = np.arange(len(self.mus))[ - (self._reorder >= idxGlob - nder) - * (self._reorder < idxGlob)] + idxLoc = np.arange(Seff)[(reorder >= idxGlob - nder) + * (reorder < idxGlob)] invLoc = 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] = dot(pseudoInv, TN) return invD, fitinv def findeveVGExplicit(self, sampleE:sampList, invD:List[Np2D]) -> Tuple[Np1D, Np2D]: """ Compute explicitly eigenvalues and eigenvectors of rational denominator matrix. """ RROMPyAssert(self._mode, message = "Cannot solve eigenvalue problem.") nEnd = invD[0].shape[1] eWidth = len(invD) vbMng(self, "INIT", "Building gramian matrix.", 10) - gramian = self.HFEngine.innerProduct(sampleE, sampleE) + gramian = self.HFEngine.innerProduct(sampleE, sampleE, + is_state = self.approx_state) G = np.zeros((nEnd, nEnd), dtype = np.complex) for k in range(eWidth): G += dot(dot(gramian, invD[k]).T, invD[k].conj()).T vbMng(self, "DEL", "Done building gramian.", 10) vbMng(self, "INIT", "Solving eigenvalue problem for gramian matrix.", 7) ev, eV = np.linalg.eigh(G) vbMng(self, "MAIN", ("Solved eigenvalue problem of size {} with condition number " "{:.4e}.").format(nEnd, ev[-1] / ev[0]), 5) vbMng(self, "DEL", "Done solving eigenvalue problem.", 7) return ev, eV def findeveVGQR(self, RPODE:Np2D, invD:List[Np2D]) -> Tuple[Np1D, Np2D]: """ Compute eigenvalues and eigenvectors of rational 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) vbMng(self, "INIT", "Building half-gramian matrix stack.", 10) Rstack = np.zeros((S * eWidth, nEnd), dtype = np.complex) for k in range(eWidth): Rstack[k * S : (k + 1) * S, :] = dot(RPODE, invD[k]) vbMng(self, "DEL", "Done building half-gramian.", 10) vbMng(self, "INIT", "Solving svd for square root of gramian matrix.", 7) _, s, eV = np.linalg.svd(Rstack, full_matrices = False) ev = s[::-1] eV = eV[::-1, :].T.conj() vbMng(self, "MAIN", ("Solved svd problem of size {} x {} with condition number " "{:.4e}.").format(*Rstack.shape, s[0] / s[-1]), 5) vbMng(self, "DEL", "Done solving svd.", 7) return ev, eV def getResidues(self, *args, **kwargs) -> Np1D: """ Obtain approximant residues. Returns: Matrix with residues as columns. """ return self.trainedModel.getResidues(*args, **kwargs) - diff --git a/rrompy/reduction_methods/standard/rational_moving_least_squares.py b/rrompy/reduction_methods/standard/rational_moving_least_squares.py index 7ce711d..373a080 100644 --- a/rrompy/reduction_methods/standard/rational_moving_least_squares.py +++ b/rrompy/reduction_methods/standard/rational_moving_least_squares.py @@ -1,301 +1,310 @@ # 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 .rational_interpolant import RationalInterpolant from rrompy.utilities.poly_fitting.polynomial import (polybases as ppb, polyvander as pvP, polyvanderTotal as pvTP) -from rrompy.reduction_methods.trained_model import ( - TrainedModelRationalMLS as tModel) -from rrompy.reduction_methods.trained_model import TrainedModelData from rrompy.utilities.base.types import Np2D, HFEng, DictAny, paramVal from rrompy.utilities.base import verbosityManager as vbMng from rrompy.utilities.numerical import (fullDegreeMaxMask, totalDegreeMaxMask, dot) from rrompy.utilities.exception_manager import (RROMPyException, RROMPyAssert, RROMPyWarning) __all__ = ['RationalMovingLeastSquares'] class RationalMovingLeastSquares(RationalInterpolant): """ ROM rational moving LS 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; defaults to 'MONOMIAL'; - 'M': degree of rational interpolant numerator; defaults to 0; - 'N': degree of rational interpolant denominator; defaults to 0; - 'polydegreetype': type of polynomial degree; defaults to 'TOTAL'; - 'radialBasis': numerator radial basis type; defaults to 'GAUSSIAN'; - 'radialDirectionalWeights': radial basis weights for interpolant numerator; defaults to 0, i.e. identity; - 'nNearestNeighbor': number of nearest neighbors considered in numerator if radialBasis allows; defaults to -1; - 'radialBasisDen': denominator radial basis type; defaults to 'GAUSSIAN'; - 'radialDirectionalWeightsDen': radial basis weights for interpolant denominator; defaults to 0, i.e. identity; - 'nNearestNeighborDen': number of nearest neighbors considered in denominator if radialBasisDen allows; defaults to -1; - 'interpRcond': tolerance for interpolation; defaults to None; - 'robustTol': tolerance for robust rational denominator management; defaults to 0. Defaults to empty dict. + approx_state(optional): Whether to approximate state. Defaults to + False. verbosity(optional): Verbosity level. Defaults to 10. Attributes: HFEngine: HF problem solver. mu0: Default parameter. mus: Array of snapshot parameters. approxParameters: Dictionary containing values for main parameters of approximant. Recognized keys are in parameterList. parameterListSoft: Recognized keys of soft approximant parameters: - 'POD': whether to compute POD of snapshots; - 'polybasis': type of polynomial basis for interpolation; - 'M': degree of rational interpolant numerator; - 'N': degree of rational interpolant denominator; - 'polydegreetype': type of polynomial degree; - 'radialBasis': numerator radial basis type; - 'radialDirectionalWeights': radial basis weights for interpolant numerator; - 'nNearestNeighbor': number of nearest neighbors considered in numerator if radialBasis allows; - 'radialBasisDen': denominator radial basis type; - 'radialDirectionalWeightsDen': radial basis weights for interpolant denominator; - 'nNearestNeighborDen': number of nearest neighbors considered in denominator if radialBasisDen allows; - 'interpRcond': tolerance for interpolation via numpy.polyfit; - 'robustTol': tolerance for robust rational denominator management. parameterListCritical: Recognized keys of critical approximant parameters: - 'S': total number of samples current approximant relies upon; - 'sampler': sample point generator. + approx_state: Whether to approximate state. + verbosity: Verbosity level. 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. polydegreetype: Type of polynomial degree. radialBasis: Numerator radial basis type. radialDirectionalWeights: Radial basis weights for interpolant numerator. nNearestNeighbor: Number of nearest neighbors considered in numerator if radialBasis allows. radialBasisDen: Denominator radial basis type. radialDirectionalWeightsDen: Radial basis weights for interpolant denominator. nNearestNeighborDen: Number of nearest neighbors considered in denominator if radialBasisDen allows. interpRcond: Tolerance for interpolation via numpy.polyfit. robustTol: Tolerance for robust rational denominator management. 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. uApproxReduced: Reduced approximate solution(s) with parameter(s) lastSolvedApprox as sampleList. lastSolvedApproxReduced: Parameter(s) corresponding to last computed reduced approximate solution(s) as parameterList. uApprox: Approximate solution(s) with parameter(s) lastSolvedApprox as sampleList. 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 = {}, verbosity : int = 10, - timestamp : bool = True): + approxParameters : DictAny = {}, approx_state : bool = False, + verbosity : int = 10, timestamp : bool = True): self._preInit() self._addParametersToList(["radialBasis", "radialBasisDen", "radialDirectionalWeightsDen", "nNearestNeighborDen"], ["GAUSSIAN", "GAUSSIAN", 1, -1]) super().__init__(HFEngine = HFEngine, mu0 = mu0, approxParameters = approxParameters, - verbosity = verbosity, timestamp = timestamp) + approx_state = approx_state, verbosity = verbosity, + timestamp = timestamp) self.catchInstability = False self._postInit() + @property + def tModelType(self): + from rrompy.reduction_methods.trained_model import \ + TrainedModelRationalMLS + return TrainedModelRationalMLS + @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 ppb: 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): self._radialBasis = radialBasis self._approxParameters["radialBasis"] = self.radialBasis @property def radialBasisDen(self): """Value of radialBasisDen.""" return self._radialBasisDen @radialBasisDen.setter def radialBasisDen(self, radialBasisDen): self._radialBasisDen = radialBasisDen self._approxParameters["radialBasisDen"] = self.radialBasisDen @property def radialDirectionalWeightsDen(self): """Value of radialDirectionalWeightsDen.""" return self._radialDirectionalWeightsDen @radialDirectionalWeightsDen.setter def radialDirectionalWeightsDen(self, radialDirectionalWeightsDen): self._radialDirectionalWeightsDen = radialDirectionalWeightsDen self._approxParameters["radialDirectionalWeightsDen"] = ( self.radialDirectionalWeightsDen) @property def nNearestNeighborDen(self): """Value of nNearestNeighborDen.""" return self._nNearestNeighborDen @nNearestNeighborDen.setter def nNearestNeighborDen(self, nNearestNeighborDen): self._nNearestNeighborDen = nNearestNeighborDen self._approxParameters["nNearestNeighborDen"] = ( self.nNearestNeighborDen) def _setupDenominator(self) -> Np2D: """Compute rational denominator.""" RROMPyAssert(self._mode, message = "Cannot setup denominator.") vbMng(self, "INIT", "Starting computation of denominator-related blocks.", 7) self._setupInterpolationIndices() if self.polydegreetype == "TOTAL": - TN, _, argIdxs = pvTP(self._musUniqueCN, self.N, self.polybasis0, - self._derIdxs, self._reorder, - scl = np.power(self.scaleFactor, -1.)) - TN = TN[:, argIdxs] + TN = pvTP(self._musUniqueCN, self.N, self.polybasis0, + self._derIdxs, self._reorder, + scl = np.power(self.scaleFactor, -1.)) else: #if self.polydegreetype == "FULL": TN = pvP(self._musUniqueCN, [self.N] * self.npar, self.polybasis0, self._derIdxs, self._reorder, scl = np.power(self.scaleFactor, -1.)) TNTen = np.zeros((self.S, self.S, TN.shape[1]), dtype = TN.dtype) TNTen[np.arange(self.S), np.arange(self.S)] = TN if self.POD: TNTen = dot(self.samplingEngine.RPOD, TNTen) vbMng(self, "DEL", "Done computing denominator-related blocks.", 7) return TN, TNTen def _setupNumerator(self) -> Np2D: """Compute rational numerator.""" RROMPyAssert(self._mode, message = "Cannot setup numerator.") vbMng(self, "INIT", "Starting computation of denominator-related blocks.", 7) self._setupInterpolationIndices() if self.polydegreetype == "TOTAL": - TM, _, argIdxs = pvTP(self._musUniqueCN, self.M, self.polybasis0, - self._derIdxs, self._reorder, - scl = np.power(self.scaleFactor, -1.)) - TM = TM[:, argIdxs] + TM = pvTP(self._musUniqueCN, self.M, self.polybasis0, + self._derIdxs, self._reorder, + scl = np.power(self.scaleFactor, -1.)) else: #if self.polydegreetype == "FULL": TM = pvP(self._musUniqueCN, [self.M] * self.npar, self.polybasis0, self._derIdxs, self._reorder, scl = np.power(self.scaleFactor, -1.)) vbMng(self, "DEL", "Done computing denominator-related blocks.", 7) return TM def setupApprox(self): """ Compute rational interpolant. SVD-based robust eigenvalue management. """ if self.checkComputedApprox(): return RROMPyAssert(self._mode, message = "Cannot setup approximant.") vbMng(self, "INIT", "Setting up {}.". format(self.name()), 5) self.computeSnapshots() + pMat = self.samplingEngine.samples.data + pMatEff = dot(self.HFEngine.C, pMat) if self.approx_state else pMat if self.trainedModel is None: - self.trainedModel = tModel() + self.trainedModel = self.tModelType() self.trainedModel.verbosity = self.verbosity self.trainedModel.timestamp = self.timestamp - data = TrainedModelData(self.trainedModel.name(), self.mu0, - self.samplingEngine.samples, - self.scaleFactor, - self.HFEngine.rescalingExp) + datadict = {"mu0": self.mu0, "projMat": pMatEff, + "scaleFactor": self.scaleFactor, + "rescalingExp": self.HFEngine.rescalingExp} + data = self.initializeModelData(datadict)[0] data.POD = self.POD data.polybasis = self.polybasis data.polydegreetype = self.polydegreetype data.radialBasis = self.radialBasis data.radialWeights = self.radialDirectionalWeights data.nNearestNeighbor = self.nNearestNeighbor data.radialBasisDen = self.radialBasisDen data.radialWeightsDen = self.radialDirectionalWeightsDen data.nNearestNeighborDen = self.nNearestNeighborDen data.interpRcond = self.interpRcond self.trainedModel.data = data else: self.trainedModel = self.trainedModel - self.trainedModel.data.projMat = copy(self.samplingEngine.samples) + self.trainedModel.data.projMat = copy(pMatEff) if not self.POD: self.trainedModel.data.gramian = self.HFEngine.innerProduct( - self.samplingEngine.samples, - self.samplingEngine.samples) + self.samplingEngine.samples, + self.samplingEngine.samples, + is_state = self.approx_state) self.trainedModel.data.mus = copy(self.mus) self.trainedModel.data.M = self.M self.trainedModel.data.N = self.N QVan, self.trainedModel.data.QBlocks = self._setupDenominator() self.trainedModel.data.PVan = self._setupNumerator() if self.polydegreetype == "TOTAL": degreeMaxMask = totalDegreeMaxMask else: #if self.polydegreetype == "FULL": degreeMaxMask = fullDegreeMaxMask if self.N > self.M: self.trainedModel.data.QVan = QVan self.trainedModel.data.domQIdxs = degreeMaxMask(self.N, self.npar) else: self.trainedModel.data.domQIdxs = degreeMaxMask(self.M, self.npar) self.trainedModel.data.approxParameters = copy(self.approxParameters) vbMng(self, "DEL", "Done setting up approximant.", 5) diff --git a/rrompy/reduction_methods/standard/reduced_basis.py b/rrompy/reduction_methods/standard/reduced_basis.py index efcb121..ac6de3f 100644 --- a/rrompy/reduction_methods/standard/reduced_basis.py +++ b/rrompy/reduction_methods/standard/reduced_basis.py @@ -1,202 +1,218 @@ # 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_standard_approximant import GenericStandardApproximant -from rrompy.reduction_methods.trained_model import \ - TrainedModelReducedBasis as tModel -from rrompy.reduction_methods.trained_model import TrainedModelData +from rrompy.hfengines.base.linear_affine_engine import checkIfAffine from rrompy.reduction_methods.base.reduced_basis_utils import \ projectAffineDecomposition from rrompy.utilities.base.types import (Np1D, Np2D, List, Tuple, DictAny, HFEng, paramVal, sampList) from rrompy.utilities.base import verbosityManager as vbMng from rrompy.utilities.numerical import dot from rrompy.utilities.exception_manager import (RROMPyWarning, RROMPyException, RROMPyAssert) __all__ = ['ReducedBasis'] class ReducedBasis(GenericStandardApproximant): """ 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 S; - 'PODTolerance': tolerance for snapshots POD; defaults to -1. Defaults to empty dict. + approx_state(optional): Whether to approximate state. Defaults and must + be True. verbosity(optional): Verbosity level. Defaults to 10. Attributes: HFEngine: HF problem solver. mu0: Default parameter. mus: Array of snapshot parameters. 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; - '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; + approx_state: Whether to approximate state. + verbosity: Verbosity level. 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. uApproxReduced: Reduced approximate solution(s) with parameter(s) lastSolvedApprox as sampleList. lastSolvedApproxReduced: Parameter(s) corresponding to last computed reduced approximate solution(s) as parameterList. uApprox: Approximate solution(s) with parameter(s) lastSolvedApprox as sampleList. 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. bs: List of numpy vectors representing coefficients of linear system RHS. ARBs: List of sparse matrices (in CSC format) representing coefficients of compressed linear system matrix. bRBs: List of numpy vectors representing coefficients of compressed linear system RHS. """ def __init__(self, HFEngine:HFEng, mu0 : paramVal = None, - approxParameters : DictAny = {}, verbosity : int = 10, - timestamp : bool = True): + approxParameters : DictAny = {}, approx_state : bool = True, + verbosity : int = 10, timestamp : bool = True): + if not approx_state: RROMPyWarning("Overriding approx_state to True.") self._preInit() self._addParametersToList(["R", "PODTolerance"], ["AUTO", -1]) + checkIfAffine(HFEngine, "apply RB method") super().__init__(HFEngine = HFEngine, mu0 = mu0, approxParameters = approxParameters, - verbosity = verbosity, timestamp = timestamp) + approx_state = True, verbosity = verbosity, + timestamp = timestamp) self._postInit() + @property + def tModelType(self): + from rrompy.reduction_methods.trained_model import \ + TrainedModelReducedBasis + return TrainedModelReducedBasis + @property def R(self): """Value of R. Its assignment may change S.""" return self._R @R.setter def R(self, R): if R == "AUTO": if not hasattr(self, "_S"): raise RROMPyException(("Cannot assign R automatically without " "S.")) R = self.S if R < 0: raise RROMPyException("R must be non-negative.") self._R = R self._approxParameters["R"] = self.R @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 _setupProjectionMatrix(self): """Compute projection matrix.""" RROMPyAssert(self._mode, message = "Cannot setup numerator.") vbMng(self, "INIT", "Starting computation of projection matrix.", 7) nsamples = self.samplingEngine.nsamples if self.R > nsamples: RROMPyWarning(("R too large compared to S. Reducing R by " "{}").format(self.R - nsamples)) self.R = nsamples if self.POD: U, s, _ = np.linalg.svd(self.samplingEngine.RPOD) s = s ** 2. else: Gramian = self.HFEngine.innerProduct(self.samplingEngine.samples, - self.samplingEngine.samples) + self.samplingEngine.samples, + is_state = True) U, s, _ = np.linalg.svd(Gramian) snorm = np.cumsum(s[::-1]) / np.sum(s) nPODTrunc = min(nsamples - np.argmax(snorm > self.PODTolerance), self.R) pMat = dot(self.samplingEngine.samples, U[:, : nPODTrunc]) vbMng(self, "MAIN", ("Assembling {}x{} projection matrix from {} " "samples.").format(*(pMat.shape), nsamples), 5) vbMng(self, "DEL", "Done computing projection matrix.", 7) return pMat def setupApprox(self): """Compute RB projection matrix.""" if self.checkComputedApprox(): return RROMPyAssert(self._mode, message = "Cannot setup approximant.") vbMng(self, "INIT", "Setting up {}.". format(self.name()), 5) self.computeSnapshots() - pMat = self._setupProjectionMatrix() + pMat = self._setupProjectionMatrix().data + pMatEff = dot(self.HFEngine.C, pMat) if self.trainedModel is None: - self.trainedModel = tModel() + self.trainedModel = self.tModelType() self.trainedModel.verbosity = self.verbosity self.trainedModel.timestamp = self.timestamp - data = TrainedModelData(self.trainedModel.name(), self.mu0, - pMat, self.scaleFactor, - self.HFEngine.rescalingExp) + datadict = {"mu0": self.mu0, "projMat": pMatEff, + "scaleFactor": self.scaleFactor, + "rescalingExp": self.HFEngine.rescalingExp} + data = self.initializeModelData(datadict)[0] data.affinePoly = self.HFEngine.affinePoly - data.mus = copy(self.mus) data.thAs, data.thbs = self.HFEngine.thAs, self.HFEngine.thbs self.trainedModel.data = data else: self.trainedModel = self.trainedModel - self.trainedModel.data.projMat = copy(pMat) + self.trainedModel.data.projMat = copy(pMatEff) + self.trainedModel.data.mus = copy(self.mus) ARBs, bRBs = self.assembleReducedSystem(pMat) self.trainedModel.data.ARBs = ARBs self.trainedModel.data.bRBs = bRBs self.trainedModel.data.approxParameters = copy(self.approxParameters) vbMng(self, "DEL", "Done setting up approximant.", 5) 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: + self.HFEngine.buildA() + self.HFEngine.buildb() vbMng(self, "INIT", "Projecting affine terms of HF model.", 10) 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.HFEngine.As, self.HFEngine.bs, pMat, ARBsOld, bRBsOld, pMatOld) vbMng(self, "DEL", "Done projecting affine terms.", 10) return ARBs, bRBs diff --git a/rrompy/reduction_methods/trained_model/trained_model.py b/rrompy/reduction_methods/trained_model/trained_model.py index 0c6a4bb..12108b3 100644 --- a/rrompy/reduction_methods/trained_model/trained_model.py +++ b/rrompy/reduction_methods/trained_model/trained_model.py @@ -1,96 +1,100 @@ # 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)) + def reset(self): + self.lastSolvedApproxReduced = None + self.lastSolvedApprox = None + @property def npar(self): """Number of parameters.""" return self.data.mu0.shape[1] @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)[0] if (not hasattr(self, "lastSolvedApprox") or self.lastSolvedApprox != mu): uApproxR = self.getApproxReduced(mu) self.uApprox = emptySampleList() for i in range(len(mu)): if isinstance(self.data.projMat, (list, sampleList,)): uApp = uApproxR[i][0] * self.data.projMat[0] for j in range(1, uApproxR.shape[0]): uApp += uApproxR[i][j] * self.data.projMat[j] else: uApp = self.data.projMat.dot(uApproxR[i]) if i == 0: #self.data.projMat.shape[0], len(mu) self.uApprox.reset((len(uApp), len(mu)), dtype = uApp.dtype) self.uApprox[i] = uApp 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_data.py b/rrompy/reduction_methods/trained_model/trained_model_data.py index 766f236..00dd780 100644 --- a/rrompy/reduction_methods/trained_model/trained_model_data.py +++ b/rrompy/reduction_methods/trained_model/trained_model_data.py @@ -1,37 +1,36 @@ # 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 from rrompy.utilities.base.types import Np2D, List, paramVal from rrompy.utilities.exception_manager import RROMPyAssert __all__ = ['TrainedModelData'] class TrainedModelData: """ROM approximant evaluation data (must be pickle-able).""" - def __init__(self, name:str, mu0:paramVal, projMat:Np2D, + def __init__(self, mu0:paramVal, projMat:Np2D, scaleFactor : List[float] = [1.], rescalingExp : List[float] = [1.]): self.npar = len(rescalingExp) RROMPyAssert(mu0.shape[1], self.npar, "Number of parameters") - self.name = name self.mu0 = mu0 self.projMat = copy(projMat) self.scaleFactor = scaleFactor self.rescalingExp = rescalingExp diff --git a/rrompy/reduction_methods/trained_model/trained_model_pivoted_data.py b/rrompy/reduction_methods/trained_model/trained_model_pivoted_data.py index 79b8803..27b7a58 100644 --- a/rrompy/reduction_methods/trained_model/trained_model_pivoted_data.py +++ b/rrompy/reduction_methods/trained_model/trained_model_pivoted_data.py @@ -1,72 +1,72 @@ # 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 .trained_model_data import TrainedModelData from rrompy.utilities.base.types import Np2D, List, ListAny, paramVal from rrompy.parameter import checkParameterList __all__ = ['TrainedModelPivotedData'] class TrainedModelPivotedData(TrainedModelData): """ROM approximant evaluation data (must be pickle-able).""" - def __init__(self, name:str, mu0:paramVal, projMat:Np2D, + def __init__(self, mu0:paramVal, projMat:Np2D, scaleFactor : ListAny = [1.], rescalingExp : List[float] = [1.], directionPivot : ListAny = [0]): - super().__init__(name, mu0, projMat, scaleFactor, rescalingExp) + super().__init__(mu0, projMat, scaleFactor, rescalingExp) self.directionPivot = directionPivot @property def directionMarginal(self): return tuple([x for x in range(self.npar) \ if x not in self.directionPivot]) @property def mu0Pivot(self): return checkParameterList(self.mu0(0, self.directionPivot), self.nparPivot)[0] @property def mu0Marginal(self): return checkParameterList(self.mu0(0, self.directionMarginal), self.nparMarginal)[0] @property def nparPivot(self): return len(self.directionPivot) @property def nparMarginal(self): return self.npar - self.nparPivot @property def rescalingExpPivot(self): return [self.rescalingExp[x] for x in self.directionPivot] @property def rescalingExpMarginal(self): return [self.rescalingExp[x] for x in self.directionMarginal] @property def scaleFactorPivot(self): return [self.scaleFactor[x] for x in self.directionPivot] @property def scaleFactorMarginal(self): return [self.scaleFactor[x] for x in self.directionMarginal] diff --git a/rrompy/reduction_methods/trained_model/trained_model_pivoted_general.py b/rrompy/reduction_methods/trained_model/trained_model_pivoted_general.py index 9cddbce..e5f9311 100644 --- a/rrompy/reduction_methods/trained_model/trained_model_pivoted_general.py +++ b/rrompy/reduction_methods/trained_model/trained_model_pivoted_general.py @@ -1,375 +1,379 @@ # 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.special import factorial as fact from itertools import combinations from .trained_model import TrainedModel from rrompy.utilities.base.types import (Np1D, Tuple, List, ListAny, paramVal, paramList, sampList, HFEng) from rrompy.utilities.base import verbosityManager as vbMng, freepar as fp from rrompy.utilities.numerical import pointMatching from rrompy.utilities.poly_fitting.heaviside import HeavisideInterpolator as HI from rrompy.utilities.exception_manager import RROMPyException, RROMPyAssert from rrompy.parameter import checkParameter, checkParameterList from rrompy.sampling import emptySampleList, sampleList __all__ = ['TrainedModelPivotedGeneral'] class TrainedModelPivotedGeneral(TrainedModel): """ ROM approximant evaluation for pivoted approximants (with pole matching). Attributes: Data: dictionary with all that can be pickled. """ def centerNormalizePivot(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.mu0Pivot. Returns: Normalized parameter. """ mu = checkParameterList(mu, self.data.nparPivot)[0] if mu0 is None: mu0 = self.data.mu0Pivot rad = ((mu ** self.data.rescalingExpPivot - mu0 ** self.data.rescalingExpPivot) / self.data.scaleFactorPivot) return rad def centerNormalizeMarginal(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.mu0Marginal. Returns: Normalized parameter. """ mu = checkParameterList(mu, self.data.nparMarginal)[0] if mu0 is None: mu0 = self.data.mu0Marginal rad = ((mu ** self.data.rescalingExpMarginal - mu0 ** self.data.rescalingExpMarginal) / self.data.scaleFactorMarginal) return rad def initializeFromLists(self, poles:ListAny, coeffs:ListAny, basis:str, HFEngine:HFEng, matchingWeight : float = 1., - POD : bool = True): + POD : bool = True, is_state : bool = True): """Initialize Heaviside representation.""" musM = self.data.musMarginal margAbsDist = np.sum(np.abs(np.repeat(musM.data, len(musM), 0) - np.tile(musM.data, [len(musM), 1]) ), axis = 1).reshape(len(musM), len(musM)) N = len(poles[0]) explored = [0] unexplored = list(range(1, len(musM))) for _ in range(1, len(musM)): minIdx = np.argmin(np.concatenate([margAbsDist[ex, unexplored] \ for ex in explored])) minIex = explored[minIdx // len(unexplored)] minIunex = unexplored[minIdx % len(unexplored)] dist = np.abs(np.tile(poles[minIex].reshape(-1, 1), N) - poles[minIunex].reshape(1, -1)) if matchingWeight != 0: resex = coeffs[minIex][: N] resunex = coeffs[minIunex][: N] if POD: distR = resex.dot(resunex.T.conj()) distR = (distR.T / np.linalg.norm(resex, axis = 1)).T distR = distR / np.linalg.norm(resunex, axis = 1) else: resex = self.data.projMat.dot(resex.T) resunex = self.data.projMat.dot(resunex.T) - distR = HFEngine.innerProduct(resex, resunex).T - distR = (distR.T / HFEngine.norm(resex)).T - distR = distR / HFEngine.norm(resunex) + distR = HFEngine.innerProduct(resex, resunex, + is_state = is_state).T + distR = (distR.T / HFEngine.norm(resex, + is_state = is_state)).T + distR = distR / HFEngine.norm(resunex, is_state = is_state) distR = np.abs(distR) distR[distR > 1.] = 1. dist += 2. / np.pi * matchingWeight * np.arccos(distR) reordering = pointMatching(dist) poles[minIunex] = poles[minIunex][reordering] coeffs[minIunex][: N] = coeffs[minIunex][reordering] explored += [minIunex] unexplored.remove(minIunex) HIs = [] for pls, cfs in zip(poles, coeffs): hsi = HI() hsi.poles = pls hsi.coeffs = cfs hsi.npar = 1 hsi.polybasis = basis HIs += [hsi] self.data.HIs = HIs def recompressByCutOff(self, murange : Tuple[float, float] = [- 1., 1.], tol : float = np.inf, rtype : str = "MAGNITUDE"): if np.isinf(tol): return " No poles erased." N = len(self.data.HIs[0].poles) mu0 = np.mean(murange) musig = murange[0] - mu0 if np.isclose(musig, 0.): radius = lambda x: np.abs(x - mu0) else: if rtype == "MAGNITUDE": murdir = (murange[0] - mu0) / np.abs(musig) def radius(x): scalprod = (x - mu0) * murdir.conj() / np.abs(musig) rescalepar = np.abs(np.real(scalprod)) rescaleort = np.abs(np.imag(scalprod)) return ((rescalepar - 1.) ** 2. * (rescalepar > 1.) + rescaleort ** 2.) ** .5 else:#if rtype == "POTENTIAL": def radius(x): rescale = (x - mu0) / musig return np.max(np.abs(rescale * np.array([-1., 1.]) + (rescale ** 2. - 1) ** .5)) - 1. keepPole, removePole = [], [] for j in range(N): for hi in self.data.HIs: if radius(hi.poles[j]) <= tol: keepPole += [j] break if len(keepPole) == 0 or keepPole[-1] != j: removePole += [j] if len(keepPole) == N: return " No poles erased." keepCoeff = keepPole + [N] keepCoeff = keepCoeff + list(range(N + 1,len(self.data.HIs[0].coeffs))) for hi in self.data.HIs: polyCorrection = np.zeros_like(hi.coeffs[0, :]) for j in removePole: polyCorrection += hi.coeffs[j, :] / (mu0 - hi.poles[j]) if len(hi.coeffs) == N: hi.coeffs = np.vstack((hi.coeffs, polyCorrection)) else: hi.coeffs[N, :] += polyCorrection hi.poles = hi.poles[keepPole] hi.coeffs = hi.coeffs[keepCoeff, :] return (" Erased {} pole".format(len(removePole)) + "s" * (len(removePole) > 1) + ".") def interpolateMarginal(self, mu : paramList = [], samples : ListAny = [], der : List[int] = None, scl : Np1D = None) -> sampList: """ Evaluate marginal interpolator at arbitrary marginal parameter. Args: mu: Target parameter. samples: Objects to interpolate. der(optional): Derivatives to take before evaluation. """ mu = checkParameterList(mu, self.data.nparMarginal)[0] sList = isinstance(samples[0], sampleList) sEff = [None] * len(samples) for j in range(len(samples)): if sList: sEff[j] = samples[j].data else: sEff[j] = samples[j] try: dtype = sEff[0].dtype except: dtype = sEff[0][0].dtype vbMng(self, "INIT", "Interpolating marginal at mu = {}.".format(mu), 95) muC = self.centerNormalizeMarginal(mu) p = emptySampleList() p.reset((len(sEff[0]), len(muC)), dtype = dtype) p.data[:] = 0. if len(sEff[0]) > 0: for mIj, spj in zip(self.data.marginalInterp, sEff): p = p + spj.reshape(len(sEff[0]), - 1) * mIj(muC, der, scl) vbMng(self, "DEL", "Done interpolating marginal.", 95) if not sList: p = p.data.flatten() return p def interpolateMarginalInterpolator(self, mu : paramVal = []) -> Np1D: """Obtain interpolated approximant interpolator.""" mu = checkParameter(mu, self.data.nparMarginal)[0] hsi = HI() hsi.poles = self.interpolateMarginalPoles(mu) hsi.coeffs = self.interpolateMarginalCoeffs(mu) hsi.npar = 1 hsi.polybasis = self.data.HIs[0].polybasis return hsi def interpolateMarginalPoles(self, mu : paramList = []) -> Np1D: """Obtain interpolated approximant poles.""" mu = checkParameterList(mu, self.data.nparMarginal)[0] return self.interpolateMarginal(mu, [hi.poles for hi in self.data.HIs]) def interpolateMarginalCoeffs(self, mu : paramList = []) -> Np1D: """Obtain interpolated approximant coefficients.""" mu = checkParameterList(mu, self.data.nparMarginal)[0] cs = self.interpolateMarginal(mu, [hi.coeffs for hi in self.data.HIs]) if isinstance(cs, (list, tuple,)): cs = np.array(cs) return cs.reshape(self.data.HIs[0].coeffs.shape) def getApproxReduced(self, mu : paramList = []) -> sampList: """ Evaluate reduced representation of approximant at arbitrary parameter. Args: mu: Target parameter. """ RROMPyAssert(self.data.nparPivot, 1, "Number of pivot parameters") mu = checkParameterList(mu, self.data.npar)[0] if (not hasattr(self, "lastSolvedApproxReduced") or self.lastSolvedApproxReduced != mu): vbMng(self, "INIT", "Evaluating approximant at mu = {}.".format(mu), 12) self.uApproxReduced = emptySampleList() for i, muPL in enumerate(mu): muL = self.centerNormalizePivot([muPL(0, x) \ for x in self.data.directionPivot]) muM = [muPL(0, x) for x in self.data.directionMarginal] vbMng(self, "INIT", "Assembling reduced model for mu = {}.".format(muPL), 87) hsL = self.interpolateMarginalInterpolator(muM) vbMng(self, "DEL", "Done assembling reduced model.", 87) uAppR = hsL(muL) if i == 0: #self.data.HIs[0].coeffs.shape[1], len(mu) self.uApproxReduced.reset((len(uAppR), len(mu)), dtype = uAppR.dtype) self.uApproxReduced[i] = uAppR vbMng(self, "DEL", "Done evaluating approximant.", 12) self.lastSolvedApproxReduced = mu return self.uApproxReduced def getPVal(self, mu : paramList = []) -> sampList: """ Evaluate rational numerator at arbitrary parameter. Args: mu: Target parameter. """ RROMPyAssert(self.data.nparPivot, 1, "Number of pivot parameters") mu = checkParameterList(mu, self.data.npar)[0] p = emptySampleList() p.reset((len(self.data.HIs[0].coeffs.shape[1]), len(mu))) for i, muPL in enumerate(mu): muL = self.centerNormalizePivot([muPL(0, x) \ for x in self.data.directionPivot]) muM = [muPL(0, x) for x in self.data.directionMarginal] hsL = self.interpolateMarginalInterpolator(muM) p[i] = hsL(muL) * np.prod(muL(0, 0) - hsL.poles) return p def getQVal(self, mu:Np1D, der : List[int] = None, scl : Np1D = None) -> Np1D: """ Evaluate rational denominator at arbitrary parameter. Args: mu: Target parameter. der(optional): Derivatives to take before evaluation. """ RROMPyAssert(self.data.nparPivot, 1, "Number of pivot parameters") mu = checkParameterList(mu, self.data.npar)[0] muP = self.centerNormalizePivot(checkParameterList( mu.data[:, self.data.directionPivot], self.data.nparPivot)[0]) muM = checkParameterList(mu.data[:, self.data.directionMarginal], self.data.nparMarginal)[0] if der is None: derP, derM = 0, [0] else: derP = der[self.data.directionPivot[0]] derM = [der[x] for x in self.data.directionMarginal] if np.any(np.array(derM) != 0): raise RROMPyException(("Derivatives of Q with respect to marginal " "parameters not allowed.")) sclP = 1 if scl is None else scl[self.data.directionPivot[0]] derVal = np.zeros(len(mu), dtype = np.complex) N = len(self.data.HIs[0].poles) if derP == N: derVal[:] = 1. elif derP >= 0 and derP < N: pls = self.interpolateMarginalPoles(muM).reshape(-1, len(mu)).T plsDist = muP.data.reshape(-1, 1) - pls for terms in combinations(np.arange(N), N - derP): derVal += np.prod(plsDist[:, list(terms)], axis = 1) return sclP ** derP * fact(derP) * derVal def getPoles(self, *args, **kwargs) -> Np1D: """ Obtain approximant poles. Returns: Numpy complex vector of poles. """ RROMPyAssert(self.data.nparPivot, 1, "Number of pivot parameters") if len(args) + len(kwargs) > 1: raise RROMPyException(("Wrong number of parameters passed. " "Only 1 available.")) elif len(args) + len(kwargs) == 1: if len(args) == 1: mVals = args[0] else: mVals = kwargs["marginalVals"] if not hasattr(mVals, "__len__"): mVals = [mVals] mVals = list(mVals) else: mVals = [fp] try: rDim = mVals.index(fp) if rDim < len(mVals) - 1 and fp in mVals[rDim + 1 :]: raise except: raise RROMPyException(("Exactly 1 'freepar' entry in " "marginalVals must be provided.")) if rDim != self.data.directionPivot[0]: raise RROMPyException(("'freepar' entry in marginalVals must " "coincide with pivot direction.")) mVals[rDim] = self.data.mu0(rDim) mMarg = [mVals[j] for j in range(len(mVals)) if j != rDim] roots = np.sort(np.array(self.interpolateMarginalPoles(mMarg))) return np.power(self.data.mu0(rDim) ** self.data.rescalingExp[rDim] + self.data.scaleFactor[rDim] * roots, 1. / self.data.rescalingExp[rDim]) def getResidues(self, *args, **kwargs) -> Np1D: """ Obtain approximant residues. Returns: Numpy matrix with residues as columns. """ pls = self.getPoles(*args, **kwargs) if len(args) == 1: mVals = args[0] + elif len(args) == 0: + mVals = [None] else: mVals = kwargs["marginalVals"] if not hasattr(mVals, "__len__"): mVals = [mVals] mVals = list(mVals) rDim = mVals.index(fp) mMarg = [mVals[j] for j in range(len(mVals)) if j != rDim] residues = self.interpolateMarginalCoeffs(mMarg)[: len(pls)] res = self.data.projMat.dot(residues.T) return pls, res diff --git a/rrompy/reduction_methods/trained_model/trained_model_pivoted_rational.py b/rrompy/reduction_methods/trained_model/trained_model_pivoted_rational.py index e8c519f..0e22cbe 100644 --- a/rrompy/reduction_methods/trained_model/trained_model_pivoted_rational.py +++ b/rrompy/reduction_methods/trained_model/trained_model_pivoted_rational.py @@ -1,106 +1,107 @@ # 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 .trained_model_pivoted_general import TrainedModelPivotedGeneral from .trained_model_rational import TrainedModelRational from rrompy.utilities.base.types import Np1D, List, paramList, sampList, HFEng from rrompy.utilities.base import verbosityManager as vbMng from rrompy.utilities.poly_fitting.heaviside import rational2heaviside from rrompy.utilities.exception_manager import RROMPyAssert from rrompy.parameter import checkParameterList from rrompy.sampling import sampleList __all__ = ['TrainedModelPivotedRational'] class TrainedModelPivotedRational(TrainedModelPivotedGeneral, TrainedModelRational): """ ROM approximant evaluation for pivoted Rational approximant (with pole matching). Attributes: Data: dictionary with all that can be pickled. """ def initializeFromRational(self, HFEngine:HFEng, - matchingWeight : float = 1., POD : bool = True): + matchingWeight : float = 1., POD : bool = True, + is_state : bool = True): """Initialize Heaviside representation.""" RROMPyAssert(self.data.nparPivot, 1, "Number of pivot parameters") poles, coeffs = [], [] for Q, P in zip(self.data.Qs, self.data.Ps): cfs, pls, basis = rational2heaviside(P, Q) poles += [pls] coeffs += [cfs] self.initializeFromLists(poles, coeffs, basis, HFEngine, - matchingWeight, POD) - self.data._temporary = False + matchingWeight, POD, is_state) def getPVal(self, mu : paramList = []) -> sampList: """ Evaluate rational numerator at arbitrary parameter. Args: mu: Target parameter. """ RROMPyAssert(self.data.nparPivot, 1, "Number of pivot parameters") - if not self.data._temporary: return super().getPVal(mu) + if not hasattr(self.data, "_temporary"): return super().getPVal(mu) mu = checkParameterList(mu, self.data.npar)[0] muP = checkParameterList(mu.data[:, self.data.directionPivot], self.data.nparPivot)[0] muM = checkParameterList(mu.data[:, self.data.directionMarginal], self.data.nparMarginal)[0] muP = checkParameterList(muP, self.data.nparPivot)[0] vbMng(self, "INIT", "Evaluating numerator at mu = {}.".format(muP), 17) muPC = self.centerNormalizePivot(muP) pP = [sampleList(P(muPC)) for P in self.data.Ps] vbMng(self, "DEL", "Done evaluating numerator.", 17) return self.interpolateMarginal(muM, pP) def getQVal(self, mu:Np1D, der : List[int] = None, scl : Np1D = None) -> Np1D: """ Evaluate rational denominator at arbitrary parameter. Args: mu: Target parameter. der(optional): Derivatives to take before evaluation. """ RROMPyAssert(self.data.nparPivot, 1, "Number of pivot parameters") - if not self.data._temporary: return super().getQVal(mu, der, scl) + if not hasattr(self.data, "_temporary"): + return super().getQVal(mu, der, scl) mu = checkParameterList(mu, self.data.npar)[0] muP = checkParameterList(mu.data[:, self.data.directionPivot], self.data.nparPivot)[0] muM = checkParameterList(mu.data[:, self.data.directionMarginal], self.data.nparMarginal)[0] if der is None: derP, derM = None, None else: derP = [der[x] for x in self.data.directionPivot] derM = [der[x] for x in self.data.directionMarginal] if scl is None: sclP, sclM = None, None else: sclP = [scl[x] for x in self.data.directionPivot] sclM = [scl[x] for x in self.data.directionMarginal] muP = checkParameterList(muP, self.data.nparPivot)[0] vbMng(self, "INIT", "Evaluating denominator at mu = {}.".format(muP), 17) muPC = self.centerNormalizePivot(muP) qP = [Q(muPC, derP, sclP).reshape(1, -1) for Q in self.data.Qs] vbMng(self, "DEL", "Done evaluating denominator.", 17) return self.interpolateMarginal(muM, qP, derM, sclM) diff --git a/rrompy/reduction_methods/trained_model/trained_model_rational.py b/rrompy/reduction_methods/trained_model/trained_model_rational.py index c7dcebc..3837eb9 100644 --- a/rrompy/reduction_methods/trained_model/trained_model_rational.py +++ b/rrompy/reduction_methods/trained_model/trained_model_rational.py @@ -1,164 +1,166 @@ # 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 .trained_model import TrainedModel from rrompy.utilities.base.types import (Np1D, List, paramVal, paramList, sampList) from rrompy.utilities.base import verbosityManager as vbMng, freepar as fp from rrompy.utilities.exception_manager import RROMPyException from rrompy.parameter import (checkParameter, checkParameterList, emptyParameterList) from rrompy.sampling import sampleList __all__ = ['TrainedModelRational'] class TrainedModelRational(TrainedModel): """ ROM approximant evaluation for rational 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)[0] 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 = []) -> sampList: """ Evaluate rational numerator at arbitrary parameter. Args: mu: Target parameter. """ mu = checkParameterList(mu, self.data.npar)[0] vbMng(self, "INIT", "Evaluating numerator at mu = {}.".format(mu), 17) muCenter = self.centerNormalize(mu) p = sampleList(self.data.P(muCenter)) vbMng(self, "DEL", "Done evaluating numerator.", 17) return p def getQVal(self, mu:Np1D, der : List[int] = None, scl : Np1D = None) -> Np1D: """ Evaluate rational denominator at arbitrary parameter. Args: mu: Target parameter. der(optional): Derivatives to take before evaluation. """ mu = checkParameterList(mu, self.data.npar)[0] vbMng(self, "INIT", "Evaluating denominator at mu = {}.".format(mu), 17) muCenter = self.centerNormalize(mu) q = self.data.Q(muCenter, der, scl) vbMng(self, "DEL", "Done evaluating denominator.", 17) 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)[0] if (not hasattr(self, "lastSolvedApproxReduced") or self.lastSolvedApproxReduced != mu): vbMng(self, "INIT", "Evaluating approximant at mu = {}.".format(mu), 12) self.uApproxReduced = self.getPVal(mu) / self.getQVal(mu) vbMng(self, "DEL", "Done evaluating approximant.", 12) self.lastSolvedApproxReduced = mu return self.uApproxReduced def getPoles(self, *args, **kwargs) -> Np1D: """ Obtain approximant poles. Returns: Numpy complex vector of poles. """ if len(args) + len(kwargs) > 1: raise RROMPyException(("Wrong number of parameters passed. " "Only 1 available.")) elif len(args) + len(kwargs) == 1: if len(args) == 1: mVals = args[0] else: mVals = kwargs["marginalVals"] if not hasattr(mVals, "__len__"): mVals = [mVals] mVals = list(mVals) else: mVals = [fp] try: rDim = mVals.index(fp) if rDim < len(mVals) - 1 and fp in mVals[rDim + 1 :]: raise except: raise RROMPyException(("Exactly 1 'freepar' entry in " "marginalVals must be provided.")) mVals[rDim] = self.data.mu0(rDim) mVals = self.centerNormalize(checkParameter(mVals, len(mVals))) mVals = list(mVals.data.flatten()) mVals[rDim] = fp return np.power(self.data.mu0(rDim) ** self.data.rescalingExp[rDim] + self.data.scaleFactor[rDim] * np.sort(self.data.Q.roots(mVals)), 1. / self.data.rescalingExp[rDim]) def getResidues(self, *args, **kwargs) -> Np1D: """ Obtain approximant residues. Returns: Numpy matrix with residues as columns. """ pls = self.getPoles(*args, **kwargs) if len(args) == 1: mVals = args[0] + elif len(args) == 0: + mVals = [None] else: mVals = kwargs["marginalVals"] if not hasattr(mVals, "__len__"): mVals = [mVals] mVals = list(mVals) rDim = mVals.index(fp) poles = emptyParameterList() poles.reset((len(pls), self.data.npar), dtype = pls.dtype) for k, pl in enumerate(pls): poles[k] = mVals poles.data[k, rDim] = pl res = (self.data.projMat.dot(self.getPVal(poles).data) / self.getQVal(poles, list(1 * (np.arange(self.data.npar) == rDim)))) return pls, res diff --git a/rrompy/reduction_methods/trained_model/trained_model_rational_mls.py b/rrompy/reduction_methods/trained_model/trained_model_rational_mls.py index f012a55..7d8cc36 100644 --- a/rrompy/reduction_methods/trained_model/trained_model_rational_mls.py +++ b/rrompy/reduction_methods/trained_model/trained_model_rational_mls.py @@ -1,174 +1,179 @@ # 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 .trained_model_rational import TrainedModelRational from rrompy.utilities.base.types import Np1D, paramVal, paramList, sampList from rrompy.utilities.base import verbosityManager as vbMng from rrompy.utilities.poly_fitting.moving_least_squares import mlsweights from rrompy.utilities.poly_fitting.polynomial import ( PolynomialInterpolator as PI) from rrompy.utilities.numerical import customPInv, degreeTotalToFull from rrompy.parameter import checkParameterList from rrompy.sampling import emptySampleList __all__ = ['TrainedModelRationalMLS'] class TrainedModelRationalMLS(TrainedModelRational): """ ROM approximant evaluation for rational moving least squares approximant. Attributes: Data: dictionary with all that can be pickled. """ + def reset(self): + super().reset() + self.lastSetupMu = None + def assembleReducedModel(self, mu:paramVal): - if not hasattr(self, "lastSetupMu") or self.lastSetupMu != mu: + if not (hasattr(self.data, "lastSetupMu") + and self.data.lastSetupMu == mu): vbMng(self, "INIT", "Assembling reduced model for mu = {}."\ .format(mu), 17) vbMng(self, "INIT", "Starting computation of denominator.", 35) muC = self.centerNormalize(mu) muSC = self.centerNormalize(self.data.mus) wQ = mlsweights(muC, muSC, self.data.radialBasisDen, directionalWeights = self.data.radialWeightsDen, nNearestNeighbor = self.data.nNearestNeighborDen) if self.data.N > self.data.M: PQVan = self.data.QVan else: PQVan = self.data.PVan VQAdjW = PQVan.conj().T * wQ VQAdjWVQ = VQAdjW.dot(PQVan) interpPseudoInverse, info = customPInv(VQAdjWVQ, full = True, rcond = self.data.interpRcond) interpPseudoInverse = interpPseudoInverse.dot(VQAdjW).dot( self.data.QBlocks) if info[0] < interpPseudoInverse.shape[-1]: q = np.zeros(interpPseudoInverse.shape[-1], dtype = np.complex) q[0] = 1. else: halfGram = interpPseudoInverse[self.data.domQIdxs] if self.data.POD: Rstack = halfGram.reshape(-1, halfGram.shape[-1]) vbMng(self, "INIT", "Solving svd for square root of gramian matrix.", 67) _, s, eV = np.linalg.svd(Rstack, full_matrices = False) condN = s[0] / s[-1] q = eV[-1, :].T.conj() vbMng(self, "MAIN", ("Solved svd problem of size {} x {} with condition " "number {:.4e}.").format(*Rstack.shape, condN), 55) vbMng(self, "DEL", "Done solving svd.", 67) else: RRstack = np.tensordot(self.trainedModel.gramian, halfGram, 1).reshape(-1, halfGram.shape[-1]) RLstack = halfGram.reshape(-1, halfGram.shape[-1]) gram = RLstack.T.conj().dot(RRstack) vbMng(self, "INIT", "Solving eigenvalue problem for gramian matrix.", 67) ev, eV = np.linalg.eigh(gram) condN = ev[-1] / ev[0] q = eV[:, 0] vbMng(self, "MAIN", ("Solved eigenvalue problem of size {} with " "condition number {:.4e}.").format(gram.shape[0], condN), 55) vbMng(self, "DEL", "Done solving eigenvalue problem.", 67) self.data.Q = PI() self.data.Q.npar = self.npar self.data.Q.polybasis = self.data.polybasis if self.data.polydegreetype == "TOTAL": self.data.Q.coeffs = degreeTotalToFull( (self.data.N + 1,) * self.npar, self.npar, q) else: self.data.Q.coeffs = q.reshape((self.data.N + 1,) * self.npar) vbMng(self, "DEL", "Done computing denominator.", 35) vbMng(self, "INIT", "Starting computation of numerator.", 35) self.data.P = PI() self.data.P.npar = self.npar self.data.P.polybasis = self.data.polybasis wP = mlsweights(muC, muSC, self.data.radialBasis, directionalWeights = self.data.radialWeights, nNearestNeighbor = self.data.nNearestNeighbor) VAdjW = self.data.PVan.conj().T * wP VAdjWV = VAdjW.dot(self.data.PVan) interpPPseudoInverse = customPInv(VAdjWV, self.data.interpRcond) Pcoeffs = np.tensordot(interpPPseudoInverse.dot(VAdjW), self.data.QBlocks.dot(q), ([1], [1])) if self.data.polydegreetype == "TOTAL": self.data.P.coeffs = degreeTotalToFull( (self.data.M + 1,) * self.npar + (self.data.QBlocks.shape[0],), self.npar, Pcoeffs) else: self.data.P.coeffs = Pcoeffs.reshape( (self.data.M + 1,) * self.npar + (self.data.QBlocks.shape[0],)) vbMng(self, "DEL", "Done computing numerator.", 35) vbMng(self, "DEL", "Done assembling reduced model.", 17) - self.lastSetupMu = mu + self.data.lastSetupMu = mu def getApproxReduced(self, mu : paramList = []) -> sampList: """ Evaluate reduced representation of approximant at arbitrary parameter. Args: mu: Target parameter. """ mu = checkParameterList(mu, self.data.npar)[0] if (not hasattr(self, "lastSolvedApproxReduced") or self.lastSolvedApproxReduced != mu): vbMng(self, "INIT", "Evaluating approximant at mu = {}.".format(mu), 12) self.uApproxReduced = emptySampleList() for i in range(len(mu)): self.assembleReducedModel(mu[i]) vbMng(self, "INIT", "Solving reduced model for mu = {}.".format(mu[i]), 15) uAppR = self.getPVal(mu[i]) / self.getQVal(mu[i]) if i == 0: #self.data.P.shape[-1], len(mu) self.uApproxReduced.reset((len(uAppR), len(mu)), dtype = uAppR.dtype) self.uApproxReduced[i] = uAppR vbMng(self, "DEL", "Done solving reduced model.", 15) vbMng(self, "DEL", "Done evaluating approximant.", 12) self.lastSolvedApproxReduced = mu return self.uApproxReduced def getPoles(self, *args, mu : paramVal = None, **kwargs) -> Np1D: """ Obtain approximant poles. Returns: Numpy complex vector of poles. """ if mu is None: mu = self.data.mu0 self.assembleReducedModel(mu) return super().getPoles(*args, **kwargs) def getResidues(self, *args, mu : paramVal = None, **kwargs) -> Np1D: """ Obtain approximant residues. Returns: Numpy matrix with residues as columns. """ if mu is None: mu = self.data.mu0 self.assembleReducedModel(mu) return super().getResidues(*args, **kwargs) diff --git a/rrompy/reduction_methods/trained_model/trained_model_reduced_basis.py b/rrompy/reduction_methods/trained_model/trained_model_reduced_basis.py index 33e8364..9be201f 100644 --- a/rrompy/reduction_methods/trained_model/trained_model_reduced_basis.py +++ b/rrompy/reduction_methods/trained_model/trained_model_reduced_basis.py @@ -1,115 +1,120 @@ # Copyright (C) 2018 by the RROMPy authors # # This file is part of RROMPy. # # RROMPy is free software: you can redistribute it and/or modify # it under the terms of the GNU Lesser General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # RROMPy is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU Lesser General Public License for more details. # # You should have received a copy of the GNU Lesser General Public License # along with RROMPy. If not, see . # import numpy as np from .trained_model import TrainedModel from rrompy.utilities.base.types import (Np1D, ListAny, paramVal, paramList, sampList) from rrompy.utilities.base import verbosityManager as vbMng, freepar as fp from rrompy.utilities.numerical import (eigvalsNonlinearDense, marginalizePolyList) from rrompy.utilities.expression import expressionEvaluator from rrompy.utilities.exception_manager import RROMPyException, RROMPyWarning from rrompy.parameter import checkParameter, checkParameterList from rrompy.sampling import emptySampleList __all__ = ['TrainedModelReducedBasis'] class TrainedModelReducedBasis(TrainedModel): """ ROM approximant evaluation for RB approximant. Attributes: Data: dictionary with all that can be pickled. """ + def reset(self): + super().reset() + self.lastSetupMu = None + def assembleReducedModel(self, mu:paramVal): mu = checkParameter(mu, self.data.npar) - if not hasattr(self, "lastSetupMu") or self.lastSetupMu != mu: + if not (hasattr(self.data, "lastSetupMu") + and self.data.lastSetupMu == mu): vbMng(self, "INIT", "Assembling reduced model for mu = {}."\ .format(mu), 17) muEff = mu ** self.data.rescalingExp self.data.ARBmu, self.data.bRBmu = 0., 0. for thA, ARB in zip(self.data.thAs, self.data.ARBs): self.data.ARBmu = (expressionEvaluator(thA[0], muEff) * ARB + self.data.ARBmu) for thb, bRB in zip(self.data.thbs, self.data.bRBs): self.data.bRBmu = (expressionEvaluator(thb[0], muEff) * bRB + self.data.bRBmu) vbMng(self, "DEL", "Done assembling reduced model.", 17) - self.lastSetupMu = mu + self.data.lastSetupMu = mu def getApproxReduced(self, mu : paramList = []) -> sampList: """ Evaluate reduced representation of approximant at arbitrary parameter. Args: mu: Target parameter. """ mu = checkParameterList(mu, self.data.npar)[0] if (not hasattr(self, "lastSolvedApproxReduced") or self.lastSolvedApproxReduced != mu): vbMng(self, "INIT", "Computing RB solution at mu = {}.".format(mu), 12) self.uApproxReduced = emptySampleList() for i in range(len(mu)): self.assembleReducedModel(mu[i]) vbMng(self, "INIT", "Solving reduced model for mu = {}.".format(mu[i]), 15) uAppR = np.linalg.solve(self.data.ARBmu, self.data.bRBmu) if i == 0: #self.data.ARBs[0].shape[-1], len(mu) self.uApproxReduced.reset((len(uAppR), len(mu)), dtype = uAppR.dtype) self.uApproxReduced[i] = uAppR vbMng(self, "DEL", "Done solving reduced model.", 15) vbMng(self, "DEL", "Done computing RB solution.", 12) self.lastSolvedApproxReduced = mu return self.uApproxReduced def getPoles(self, marginalVals : ListAny = [fp], jSupp : int = 1, **kwargs) -> Np1D: """ Obtain approximant poles. Returns: Numpy complex vector of poles. """ if not self.data.affinePoly: RROMPyWarning(("Unable to compute approximate poles due " "to parametric dependence (detected non-" "polynomial). Change HFEngine.affinePoly to True " "if necessary.")) return if not hasattr(marginalVals, "__len__"): marginalVals = [marginalVals] mVals = list(marginalVals) try: rDim = mVals.index(fp) if rDim < len(mVals) - 1 and fp in mVals[rDim + 1 :]: raise except: raise RROMPyException(("Exactly 1 'freepar' entry in " "marginalVals must be provided.")) ARBs = self.data.ARBs if self.data.npar > 1: mVals[rDim] = self.data.mu0(rDim) mVals = checkParameter(mVals).data.flatten() mVals[rDim] = fp ARBs = marginalizePolyList(ARBs, mVals, "auto") ev = eigvalsNonlinearDense(ARBs, jSupp = jSupp, **kwargs) return np.sort(np.power(ev, 1. / self.data.rescalingExp[rDim])) diff --git a/rrompy/sampling/base/pod_engine.py b/rrompy/sampling/base/pod_engine.py index b3298a4..b30db32 100644 --- a/rrompy/sampling/base/pod_engine.py +++ b/rrompy/sampling/base/pod_engine.py @@ -1,134 +1,141 @@ # 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, Np2D, Tuple, HFEng, sampList from rrompy.utilities.numerical import dot from rrompy.sampling import sampleList __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 = -1) -> Tuple[Np1D, Np1D, bool]: + def GS(self, a:Np1D, Q:sampList, n : int = -1, + is_state : bool = True) -> 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; + is_state: whether state-norm should be used. Returns: Resulting normalized vector, coefficients of a wrt the updated basis, whether computation is ill-conditioned. """ if n == -1: n = Q.shape[1] 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) + nu = self.HFEngine.innerProduct(a, Q, is_state = is_state) a = a - dot(Q, nu) r[:-1] = r[:-1] + nu.flatten() - r[-1] = self.HFEngine.norm(a) + r[-1] = self.HFEngine.norm(a, is_state = is_state) 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] return a, r, ill_cond def generalizedQR(self, A:sampList, Q0 : sampList = None, - only_R : bool = False, - genTrials : int = 10) -> Tuple[sampList, Np2D]: + only_R : bool = False, genTrials : int = 10, + is_state : bool = True) -> Tuple[sampList, Np2D]: """ 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. genTrials(optional): number of trials of generation of linearly independent vector; defaults to 10. + is_state(optional): whether state-norm should be used; defaults to + True. Returns: Resulting (orthogonal and )upper-triangular factor(s). """ 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): a = B[k] - R[k, k] = self.HFEngine.norm(a) + R[k, k] = self.HFEngine.norm(a, is_state = is_state) if Q0 is None: for _ in range(genTrials): - Q[k], _, illC = self.GS(np.random.randn(Nh), Q, k) + Q[k], _, illC = self.GS(np.random.randn(Nh), Q, k, + is_state) if not illC: break else: illC = False if illC: Q[k] = np.zeros(Nh, dtype = Q.dtype) alpha = 0. else: - alpha = self.HFEngine.innerProduct(a, Q[k]) + alpha = self.HFEngine.innerProduct(a, Q[k], + is_state = is_state) 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) + V[k], _, _ = self.GS(R[k, k] * Q[k] - a, Q, k, is_state) J = np.arange(k + 1, N) - vtB = self.HFEngine.innerProduct(B[J], V[k]) + vtB = self.HFEngine.innerProduct(B[J], V[k], is_state = is_state) B.data[:, J] -= 2 * np.outer(V[k], vtB) if illC: R[k, J] = 0. else: - R[k, J] = self.HFEngine.innerProduct(B[J], Q[k]) + R[k, J] = self.HFEngine.innerProduct(B[J], Q[k], + is_state = is_state) B.data[:, J] -= np.outer(Q[k], R[k, J]) if only_R: return R for k in range(N - 1, -1, -1): J = list(range(k, N)) - vtQ = self.HFEngine.innerProduct(Q[J], V[k]) + vtQ = self.HFEngine.innerProduct(Q[J], V[k], is_state = is_state) 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 c61b3fd..d34c933 100644 --- a/rrompy/sampling/base/sampling_engine_base.py +++ b/rrompy/sampling/base/sampling_engine_base.py @@ -1,187 +1,206 @@ # 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, List, strLst, paramVal, paramList, sampList) from rrompy.utilities.base import verbosityManager as vbMng 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): + def __init__(self, HFEngine:HFEng, sample_state : bool = False, + verbosity : int = 10, timestamp : bool = True): + self.sample_state = sample_state self.verbosity = verbosity self.timestamp = timestamp vbMng(self, "INIT", "Initializing sampling engine of type {}.".format(self.name()), 10) self.HFEngine = HFEngine vbMng(self, "DEL", "Done initializing sampling engine.", 10) 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) -> sampList: """ Solve linear system. Args: mu: Parameter value. Returns: Solution of system. """ mu = checkParameterList(mu, self.HFEngine.npar)[0] vbMng(self, "INIT", "Solving HF model for mu = {}.".format(mu), 15) - u = self.HFEngine.solve(mu, RHS) + u = self.HFEngine.solve(mu, RHS, return_state = self.sample_state, + verbose = (self.verbosity >= 20)) vbMng(self, "DEL", "Done solving HF model.", 15) return u def plotSamples(self, warping : List[callable] = None, name : str = "u", save : str = None, what : strLst = 'all', saveFormat : str = "eps", saveDPI : int = 100, - show : bool = True, plotArgs : dict = {}, **figspecs): + show : bool = True, plotArgs : dict = {}, + **figspecs) -> List[str]: """ Do some nice plots of the samples. Args: 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. plotArgs(optional): Optional arguments for fen/pyplot. figspecs(optional key args): Optional arguments for matplotlib figure creation. + + Returns: + Output filenames. """ + filesOut = [None] * self.nsamples for j in range(self.nsamples): - self.HFEngine.plot(self.samples[j], warping, - "{}_{}".format(name, j), save, what, saveFormat, - saveDPI, show, plotArgs, **figspecs) + filesOut[j] = self.HFEngine.plot(self.samples[j], warping, False, + "{}_{}".format(name, j), save, + what, saveFormat, saveDPI, show, + plotArgs, **figspecs) + if filesOut[0] is None: return None + return filesOut def outParaviewSamples(self, name : str = "u", folders : bool = True, filename : str = "out", times : Np1D = None, what : strLst = 'all', forceNewFile : bool = True, - filePW = None): + filePW = None) -> List[str]: """ 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). + + Returns: + Output filenames. """ if times is None: times = [0.] * self.nsamples + filesOut = [None] * 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) + filesOut[j] = self.HFEngine.outParaview(self.samples[j], None, + False, "{}_{}".format(name, j), + "{}_{}".format(filename, j), + times[j], what, forceNewFile, + folders, filePW) + if filesOut[0] is None: return None + return filesOut def outParaviewTimeDomainSamples(self, omegas : Np1D = None, timeFinal : Np1D = None, periodResolution : int = 20, name : str = "u", folders : bool = True, filename : str = "out", - forceNewFile : bool = True): + forceNewFile : bool = True) -> List[str]: """ 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. + + Returns: + Output filename. """ if omegas is None: omegas = np.real(self.mus) if not isinstance(timeFinal, (list, tuple,)): timeFinal = [timeFinal] * self.nsamples + filesOut = [None] * 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) - + filesOut[j] = self.HFEngine.outParaviewTimeDomain(self.samples[j], + omegas[j], None, False, + timeFinal[j], + periodResolution, + "{}_{}".format(name, j), + "{}_{}".format(filename, j), + forceNewFile, folders) + if filesOut[0] is None: return None + return filesOut diff --git a/rrompy/sampling/base/sampling_engine_base_pivoted.py b/rrompy/sampling/base/sampling_engine_base_pivoted.py index 1d0a6da..2df5180 100644 --- a/rrompy/sampling/base/sampling_engine_base_pivoted.py +++ b/rrompy/sampling/base/sampling_engine_base_pivoted.py @@ -1,206 +1,233 @@ # 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, List, ListAny, strLst, paramVal, paramList, sampList) from rrompy.utilities.base import verbosityManager as vbMng from rrompy.utilities.exception_manager import RROMPyWarning from rrompy.parameter import (emptyParameterList, checkParameter, checkParameterList) from rrompy.sampling import emptySampleList from .sampling_engine_base import SamplingEngineBase __all__ = ['SamplingEngineBasePivoted'] class SamplingEngineBasePivoted(SamplingEngineBase): """HERE""" def __init__(self, HFEngine:HFEng, directionPivot:ListAny, - verbosity : int = 10, timestamp : bool = True): - super().__init__(HFEngine, verbosity, timestamp) + sample_state : bool = False, verbosity : int = 10, + timestamp : bool = True): + super().__init__(HFEngine, sample_state, verbosity, timestamp) self.directionPivot = directionPivot self.HFEngineMarginalized = None self.resetHistory() @property def directionMarginal(self): return tuple([x for x in range(self.HFEngine.npar) \ if x not in self.directionPivot]) @property def nPivot(self): return len(self.directionPivot) @property def nMarginal(self): return len(self.directionMarginal) @property def nsamplesTot(self): return np.sum(self.nsamples) def resetHistory(self, j : int = 1): self.samples = [emptySampleList() for _ in range(j)] self.nsamples = [0] * j self.mus = [emptyParameterList() for _ in range(j)] self._derIdxs = [[] for _ in range(j)] def popSample(self, j:int): if hasattr(self, "nsamples") and self.nsamples[j] > 1: if self.samples[j].shape[1] > self.nsamples[j]: RROMPyWarning(("More than 'nsamples' memory allocated for " "samples. Popping empty sample column.")) self.nsamples[j] += 1 self.nsamples[j] -= 1 self.samples[j].pop() self.mus[j].pop() else: self.resetHistory() def preallocateSamples(self, u:sampList, mu:paramVal, n:int, j:int): self.samples[j].reset((u.shape[0], n), u.dtype) self.samples[j][0] = u mu = checkParameter(mu, self.nPivot) self.mus[j].reset((n, self.nPivot)) self.mus[j][0] = mu[0] def coalesceSamples(self): self.samplesCoalesced = emptySampleList() self.samplesCoalesced.reset((self.samples[0].shape[0], np.sum([samp.shape[1] \ for samp in self.samples])), self.samples[0].dtype) run_idx = 0 for samp in self.samples: slen = samp.shape[1] self.samplesCoalesced.data[:, run_idx : run_idx + slen] = samp.data run_idx += slen def solveLS(self, mu : paramList = [], RHS : sampList = None) -> sampList: """ Solve linear system. Args: mu: Parameter value. Returns: Solution of system. """ mu = checkParameterList(mu, self.nPivot)[0] vbMng(self, "INIT", ("Solving HF model for muPivot = {} and muMarginal = " "{}.").format(mu, self.HFEngineMarginalized.muFixed), 15) - u = self.HFEngineMarginalized.solve(mu, RHS) + u = self.HFEngineMarginalized.solve(mu, RHS, + return_state = self.sample_state, + verbose = (self.verbosity >= 20)) vbMng(self, "DEL", "Done solving HF model.", 15) return u def plotSamples(self, warping : List[callable] = None, name : str = "u", save : str = None, what : strLst = 'all', saveFormat : str = "eps", saveDPI : int = 100, - show : bool = True, plotArgs : dict = {}, **figspecs): + show : bool = True, plotArgs : dict = {}, + **figspecs) -> List[str]: """ Do some nice plots of the samples. Args: 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. plotArgs(optional): Optional arguments for fen/pyplot. figspecs(optional key args): Optional arguments for matplotlib figure creation. + + Returns: + Output filenames. """ + filesOut = [] for i in range(len(self.nsamples)): + filesOuti = [None] * self.nsamples[i] for j in range(self.nsamples[i]): - self.HFEngine.plot(self.samples[i][j], warping, - "{}_{}_{}".format(name, i, j), save, what, - saveFormat, saveDPI, show, plotArgs, - **figspecs) + filesOuti[j] = self.HFEngine.plot(self.samples[i][j], + warping, False, + "{}_{}_{}".format(name, i, j), + save, what, saveFormat, + saveDPI, show, plotArgs, + **figspecs) + filesOut += [filesOuti] + if filesOut[0][0] is None: return None + return filesOut def outParaviewSamples(self, name : str = "u", folders : bool = True, filename : str = "out", times : Np1D = None, what : strLst = 'all', forceNewFile : bool = True, - filePW = None): + filePW = None) -> List[str]: """ 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). + + Returns: + Output filenames. """ if times is None: times = [[0.] * self.nsamples[i] \ for i in range(len(self.nsamples))] + filesOut = [] for i in range(len(self.nsamples)): + filesOuti = [None] * self.nsamples[i] for j in range(self.nsamples[i]): - self.HFEngine.outParaview(self.samples[i][j], - name = "{}_{}_{}".format(name, i, j), - filename = "{}_{}_{}".format(filename, - i, j), - time = times[i][j], what = what, - forceNewFile = forceNewFile, - folder = folders, filePW = filePW) + filesOuti[j] = self.HFEngine.outParaview( + self.samples[i][j], None, False, + "{}_{}_{}".format(name, i, j), + "{}_{}_{}".format(filename, i, j), + times[i][j], what, forceNewFile, + folders, filePW) + filesOut += [filesOuti] + if filesOut[0][0] is None: return None + return filesOut def outParaviewTimeDomainSamples(self, omegas : Np1D = None, timeFinal : Np1D = None, periodResolution : int = 20, name : str = "u", folders : bool = True, filename : str = "out", - forceNewFile : bool = True): + forceNewFile : bool = True) -> List[str]: """ 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. + + Returns: + Output filenames. """ if omegas is None: omegas = [[np.real(self.mus[i])] \ for i in range(len(self.nsamples))] if not isinstance(timeFinal, (list, tuple,)): timeFinal = [[timeFinal] * self.nsamples[i] \ for i in range(len(self.nsamples))] + filesOut = [] for i in range(len(self.nsamples)): + filesOuti = [None] * self.nsamples[i] for j in range(self.nsamples[i]): - self.HFEngine.outParaviewTimeDomain(self.samples[i][j], - omega = omegas[i][j], - timeFinal = timeFinal[i][j], - periodResolution = periodResolution, - name = "{}_{}_{}".format(name, i, j), - filename = "{}_{}_{}".format(filename, - i, j), - forceNewFile = forceNewFile, - folder = folders) + filesOuti[j] = self.HFEngine.outParaviewTimeDomain( + self.samples[i][j], omegas[i][j], + None, False, timeFinal[i][j], + periodResolution, + "{}_{}_{}".format(name, i, j), + "{}_{}_{}".format(filename, i, j), + forceNewFile, folders) + filesOut += [filesOuti] + if filesOut[0][0] is None: return None + return filesOut diff --git a/rrompy/sampling/pivoted/sampling_engine_pivoted.py b/rrompy/sampling/pivoted/sampling_engine_pivoted.py index c10d0dc..58d539d 100644 --- a/rrompy/sampling/pivoted/sampling_engine_pivoted.py +++ b/rrompy/sampling/pivoted/sampling_engine_pivoted.py @@ -1,123 +1,131 @@ # Copyright (C) 2018 by the RROMPy authors # # This file is part of RROMPy. # # RROMPy is free software: you can redistribute it and/or modify # it under the terms of the GNU Lesser General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # RROMPy is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU Lesser General Public License for more details. # # You should have received a copy of the GNU Lesser General Public License # along with RROMPy. If not, see . # from copy import deepcopy as copy import numpy as np from rrompy.sampling.base.sampling_engine_base_pivoted import ( SamplingEngineBasePivoted) from rrompy.hfengines.base import MarginalProxyEngine from rrompy.utilities.base.types import Np1D, paramVal, paramList, sampList from rrompy.utilities.base import verbosityManager as vbMng, freepar as fp from rrompy.utilities.exception_manager import RROMPyException from rrompy.utilities.numerical import nextDerivativeIndices, dot from rrompy.parameter import checkParameter, checkParameterList from rrompy.sampling import sampleList __all__ = ['SamplingEnginePivoted'] class SamplingEnginePivoted(SamplingEngineBasePivoted): """HERE""" def preprocesssamples(self, idxs:Np1D, j:int) -> sampList: if self.samples[j] is None or len(self.samples[j]) == 0: return return self.samples[j](idxs) - def postprocessu(self, u:sampList, j:int, - overwrite : bool = False) -> Np1D: - return copy(u) + def setsample(self, u:sampList, j:int, overwrite : bool = False) -> Np1D: + if overwrite: + self.samples[j][self.nsamples[j]] = u + else: + if self.nsamples[j] == 0: + self.samples[j] = sampleList(u) + else: + self.samples[j].append(u) - def postprocessuBulk(self, u:sampList, j:int) -> sampList: - return copy(u) + def postprocessu(self, u:sampList, j:int, overwrite : bool = False): + self.setsample(u, j, overwrite) - def lastSampleManagement(self, j:int): + def postprocessuBulk(self, j:int): pass def _getSampleConcurrence(self, mu:paramVal, j:int, previous:Np1D) -> sampList: + if not (self.sample_state or self.HFEngine.isCEye): + raise RROMPyException(("Derivatives of solution with non-scalar " + "C not computable.")) + if not self.HFEngine._isStateShiftZero: + raise RROMPyException(("Derivatives of solution with non-zero " + "solution shift not computable.")) if len(previous) >= len(self._derIdxs[j]): self._derIdxs[j] += nextDerivativeIndices( self._derIdxs[j], self.nPivot, len(previous) + 1 - len(self._derIdxs[j])) derIdx = self._derIdxs[j][len(previous)] mu = checkParameter(mu, self.nPivot) samplesOld = self.preprocesssamples(previous, j) RHS = self.HFEngineMarginalized.b(mu, derIdx) for j, derP in enumerate(self._derIdxs[j][: len(previous)]): diffP = [x - y for (x, y) in zip(derIdx, derP)] if np.all([x >= 0 for x in diffP]): RHS -= dot(self.HFEngineMarginalized.A(mu, diffP), samplesOld[j]) return self.solveLS(mu, RHS = RHS) def nextSample(self, mu:paramVal, j:int, overwrite : bool = False, - lastSample : bool = True) -> Np1D: + postprocess : bool = True) -> Np1D: mu = checkParameter(mu, self.nPivot) - ns = self.nsamples[j] muidxs = self.mus[j].findall(mu[0]) if len(muidxs) > 0: u = self._getSampleConcurrence(mu, j, np.sort(muidxs)) else: u = self.solveLS(mu) - u = self.postprocessu(u, j, overwrite = overwrite) + if postprocess: + self.postprocessu(u, j, overwrite = overwrite) + else: + self.setsample(u, j, overwrite) if overwrite: - self.samples[j][ns] = u - self.mus[j][ns] = mu[0] + self.mus[j][self.nsamples[j]] = mu[0] else: - if ns == 0: - self.samples[j] = sampleList(u) - else: - self.samples[j].append(u) self.mus[j].append(mu) self.nsamples[j] += 1 - if lastSample: self.lastSampleManagement(j) - return u + return self.samples[j][self.nsamples[j] - 1] def iterSample(self, mus:paramList, musM:paramList) -> sampList: mus = checkParameterList(mus, self.nPivot)[0] musM = checkParameterList(musM, self.nMarginal)[0] vbMng(self, "INIT", "Starting sampling iterations.", 5) n = len(mus) m = len(musM) if n <= 0: raise RROMPyException("Number of samples must be positive.") if m <= 0: raise RROMPyException(("Number of marginal samples must be " "positive.")) repeatedSamples = len(mus.unique()) != n for j in range(m): muMEff = [fp] * self.HFEngine.npar for k, x in enumerate(self.directionMarginal): muMEff[x] = musM(j, k) self.HFEngineMarginalized = MarginalProxyEngine(self.HFEngine, list(muMEff)) if repeatedSamples: for k in range(n): vbMng(self, "MAIN", "Computing sample {} / {} for marginal {} / {}."\ .format(k + 1, n, j, m), 10) self.nextSample(mus[k], j, overwrite = (k > 0), - lastSample = (n == k + 1)) + postprocess = False) if n > 1 and k == 0: self.preallocateSamples(self.samples[j][0], mus[0], n, j) else: - self.samples[j] = self.postprocessuBulk(self.solveLS(mus), j) + self.setsample(self.solveLS(mus), j, overwrite = False) self.mus[j] = copy(mus) self.nsamples[j] = n + self.postprocessuBulk(j) vbMng(self, "DEL", "Finished sampling iterations.", 5) return self.samples[j] diff --git a/rrompy/sampling/pivoted/sampling_engine_pivoted_pod.py b/rrompy/sampling/pivoted/sampling_engine_pivoted_pod.py index 32c9a4c..815b033 100644 --- a/rrompy/sampling/pivoted/sampling_engine_pivoted_pod.py +++ b/rrompy/sampling/pivoted/sampling_engine_pivoted_pod.py @@ -1,114 +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.sampling.base.pod_engine import PODEngine from .sampling_engine_pivoted import SamplingEnginePivoted from rrompy.utilities.base.types import Np1D, paramVal, sampList from rrompy.utilities.base import verbosityManager as vbMng from rrompy.sampling import sampleList, emptySampleList __all__ = ['SamplingEnginePivotedPOD'] class SamplingEnginePivotedPOD(SamplingEnginePivoted): """HERE""" def resetHistory(self, j : int = 1): super().resetHistory(j) - self.samples_full = [None] * j - self.RPOD = [None] * j + self.samples_full = [emptySampleList() for _ in range(j)] + self.RPOD = [np.zeros((0, 0), dtype = np.complex) for _ in range(j)] def popSample(self, j:int): if hasattr(self, "nsamples") and self.nsamples[j] > 1: self.RPOD[j] = self.RPOD[j][: -1, : -1] self.samples_full[j].pop() super().popSample(j) def coalesceSamples(self, tol : float = 1e-12): super().coalesceSamples() - self.samplesCoalesced, RPODC = ( - self.PODEngine.generalizedQR(self.samplesCoalesced)) + self.samplesCoalesced, RPODC = self.PODEngine.generalizedQR( + self.samplesCoalesced, + is_state = self.sample_state) self.RPODCoalesced = np.zeros((self.samplesCoalesced.shape[1], self.samplesCoalesced.shape[1]), dtype = self.RPOD[0].dtype) self.samples_fullCoalesced = emptySampleList() self.samples_fullCoalesced.reset((self.samples_full[0].shape[0], self.samplesCoalesced.shape[1]), self.samples_full[0].dtype) ci = 0 for j, (Rloc, samp) in enumerate(zip(self.RPOD, self.samples_full)): ri = 0 Rheg = Rloc.shape[1] for k, Rloc2 in enumerate(self.RPOD[: j + 1]): Rlen = Rloc2.shape[1] self.RPODCoalesced[ri : ri + Rlen, ci : ci + Rheg] = ( RPODC[ri : ri + Rlen, ci : ci + Rheg].dot(Rloc)) ri += Rlen self.samples_fullCoalesced.data[:, ci : ci + Rheg] = samp.data ci += Rheg RCdiag = np.abs(np.diag(self.RPODCoalesced)) RCdiag /= RCdiag[0] ntrunc = np.nonzero(RCdiag < tol)[0] if len(ntrunc) == 0: return self.samplesCoalesced.data = self.samplesCoalesced.data[:, : ntrunc[0]] self.RPODCoalesced = self.RPODCoalesced[: ntrunc[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() self.PODEngine = PODEngine(self._HFEngine) def preprocesssamples(self, idxs:Np1D, j:int) -> sampList: if self.samples_full[j] is None or len(self.samples_full[j]) == 0: return return self.samples_full[j](idxs) - def postprocessu(self, u:sampList, j:int, - overwrite : bool = False) -> Np1D: - ns = self.nsamples[j] + def setsample(self, u:sampList, j:int, overwrite : bool = False): + super().setsample(u, j, overwrite) if overwrite: - self.samples_full[j][ns] = copy(u) + self.samples_full[j][self.nsamples[j]] = u else: - if ns == 0: + if self.nsamples[j] == 0: self.samples_full[j] = sampleList(u) else: self.samples_full[j].append(u) - return u - - def postprocessuBulk(self, u:sampList, j:int) -> sampList: - self.samples_full[j] = copy(u) + + def postprocessu(self, u:sampList, j:int, overwrite : bool = False): + if overwrite: + self.samples_full[j][self.nsamples[j]] = u + else: + if self.nsamples[j] == 0: + self.samples_full[j] = sampleList(u) + else: + self.samples_full[j].append(u) + vbMng(self, "INIT", "Starting orthogonalization.", 20) + u, r, _ = self.PODEngine.GS(u, self.samples[j], + is_state = self.sample_state) + self.RPOD[j] = np.pad(self.RPOD[j], ((0, 1), (0, 1)), 'constant') + self.RPOD[j][:, -1] = r + vbMng(self, "DEL", "Done orthogonalizing.", 20) + super().setsample(u, j, overwrite) + + def postprocessuBulk(self, j:int): vbMng(self, "INIT", "Starting orthogonalization for marginal no {}.".format(j), 40) - u, self.RPOD[j] = self.PODEngine.generalizedQR(self.samples_full[j]) + u, self.RPOD[j] = self.PODEngine.generalizedQR(self.samples_full[j], + is_state = self.sample_state) vbMng(self, "DEL", "Done orthogonalizing.", 40) - return u - - def lastSampleManagement(self, j:int): - self.samples[j] = self.postprocessuBulk(self.samples_full[j], j) + self.samples[j] = sampleList(u) def preallocateSamples(self, u:Np1D, mu:paramVal, n:int, j:int): super().preallocateSamples(u, mu, n, j) self.samples_full[j].reset((u.shape[0], n), u.dtype) self.samples_full[j][0] = u - diff --git a/rrompy/sampling/standard/sampling_engine_standard.py b/rrompy/sampling/standard/sampling_engine_standard.py index 3cfc21b..82a08f1 100644 --- a/rrompy/sampling/standard/sampling_engine_standard.py +++ b/rrompy/sampling/standard/sampling_engine_standard.py @@ -1,106 +1,115 @@ # 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 verbosityManager as vbMng from rrompy.utilities.exception_manager import RROMPyException from rrompy.utilities.numerical import nextDerivativeIndices, dot from rrompy.parameter import checkParameter, checkParameterList from rrompy.sampling import sampleList __all__ = ['SamplingEngineStandard'] class SamplingEngineStandard(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 copy(u) + def setsample(self, u:sampList, overwrite : bool = False): + if overwrite: + self.samples[self.nsamples] = u + else: + if self.nsamples == 0: + self.samples = sampleList(u) + else: + self.samples.append(u) - def postprocessuBulk(self, u:sampList) -> sampList: - return copy(u) + def postprocessu(self, u:sampList, overwrite : bool = False): + self.setsample(u, overwrite) - def lastSampleManagement(self): + def postprocessuBulk(self): pass def _getSampleConcurrence(self, mu:paramVal, previous:Np1D) -> sampList: + if not (self.sample_state or self.HFEngine.isCEye): + raise RROMPyException(("Derivatives of solution with non-scalar " + "C not computable.")) + if not self.HFEngine._isStateShiftZero: + raise RROMPyException(("Derivatives of solution with non-zero " + "solution shift not computable.")) 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) 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 -= dot(self.HFEngine.A(mu, diffP), samplesOld[j]) return self.solveLS(mu, RHS = RHS) def nextSample(self, mu : paramVal = [], overwrite : bool = False, - lastSample : bool = True) -> Np1D: + postprocess : 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)) else: u = self.solveLS(mu) - u = self.postprocessu(u, overwrite = overwrite) + if postprocess: + self.postprocessu(u, overwrite = overwrite) + else: + self.setsample(u, overwrite) if overwrite: - self.samples[ns] = u - self.mus[ns] = mu[0] + self.mus[self.nsamples] = 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 + return self.samples[self.nsamples - 1] def iterSample(self, mus:paramList) -> sampList: mus = checkParameterList(mus, self.HFEngine.npar)[0] vbMng(self, "INIT", "Starting sampling iterations.", 5) n = len(mus) if n <= 0: raise RROMPyException(("Number of samples must be positive.")) self.resetHistory() if len(mus.unique()) != n: for j in range(n): vbMng(self, "MAIN", "Computing sample {} / {}.".format(j + 1, n), 7) self.nextSample(mus[j], overwrite = (j > 0), - lastSample = (n == j + 1)) + postprocess = False) if n > 1 and j == 0: self.preallocateSamples(self.samples[0], mus[0], n) else: - self.samples = self.postprocessuBulk(self.solveLS(mus)) + self.setsample(self.solveLS(mus), overwrite = False) self.mus = copy(mus) self.nsamples = n + self.postprocessuBulk() vbMng(self, "DEL", "Finished sampling iterations.", 5) return self.samples diff --git a/rrompy/sampling/standard/sampling_engine_standard_pod.py b/rrompy/sampling/standard/sampling_engine_standard_pod.py index 1b53583..3c9a3b9 100644 --- a/rrompy/sampling/standard/sampling_engine_standard_pod.py +++ b/rrompy/sampling/standard/sampling_engine_standard_pod.py @@ -1,80 +1,91 @@ # Copyright (C) 2018 by the RROMPy authors # # This file is part of RROMPy. # # RROMPy is free software: you can redistribute it and/or modify # it under the terms of the GNU Lesser General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # RROMPy is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU Lesser General Public License for more details. # # You should have received a copy of the GNU Lesser General Public License # along with RROMPy. If not, see . # -from copy import deepcopy as copy +import numpy as np from rrompy.sampling.base.pod_engine import PODEngine from .sampling_engine_standard import SamplingEngineStandard from rrompy.utilities.base.types import Np1D, paramVal, sampList from rrompy.utilities.base import verbosityManager as vbMng -from rrompy.sampling import sampleList +from rrompy.sampling import sampleList, emptySampleList __all__ = ['SamplingEngineStandardPOD'] class SamplingEngineStandardPOD(SamplingEngineStandard): """HERE""" def resetHistory(self): super().resetHistory() - self.samples_full = None - self.RPOD = None + self.samples_full = emptySampleList() + self.RPOD = np.zeros((0, 0), dtype = np.complex) 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() + SamplingEngineStandard.HFEngine.fset(self, HFEngine) self.PODEngine = PODEngine(self._HFEngine) def preprocesssamples(self, idxs:Np1D) -> sampList: 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: - ns = self.nsamples + def setsample(self, u:sampList, overwrite : bool = False): + super().setsample(u, overwrite) if overwrite: - self.samples_full[ns] = copy(u) + self.samples_full[self.nsamples] = u else: - if ns == 0: + if self.nsamples == 0: self.samples_full = sampleList(u) else: self.samples_full.append(u) - return u + + def postprocessu(self, u:sampList, overwrite : bool = False): + if overwrite: + self.samples_full[self.nsamples] = u + else: + if self.nsamples == 0: + self.samples_full = sampleList(u) + else: + self.samples_full.append(u) + vbMng(self, "INIT", "Starting orthogonalization.", 20) + u, r, _ = self.PODEngine.GS(u, self.samples, + is_state = self.sample_state) + self.RPOD = np.pad(self.RPOD, ((0, 1), (0, 1)), 'constant') + self.RPOD[:, -1] = r + vbMng(self, "DEL", "Done orthogonalizing.", 20) + super().setsample(u, overwrite) - def postprocessuBulk(self, u:sampList) -> sampList: - self.samples_full = copy(u) + def postprocessuBulk(self): vbMng(self, "INIT", "Starting orthogonalization.", 10) - u, self.RPOD = self.PODEngine.generalizedQR(self.samples_full) + u, self.RPOD = self.PODEngine.generalizedQR(self.samples_full, + is_state = self.sample_state) vbMng(self, "DEL", "Done orthogonalizing.", 10) - return u - - def lastSampleManagement(self): - self.samples = self.postprocessuBulk(self.samples_full) + self.samples = sampleList(u) def preallocateSamples(self, u:Np1D, mu:paramVal, n:int): super().preallocateSamples(u, mu, n) self.samples_full.reset((u.shape[0], n), u.dtype) self.samples_full[0] = u diff --git a/rrompy/solver/__init__.py b/rrompy/solver/__init__.py index 8883a39..391cfa5 100644 --- a/rrompy/solver/__init__.py +++ b/rrompy/solver/__init__.py @@ -1,35 +1,34 @@ # 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 .linear_solver import RROMPyLinearSolvers, setupSolver -from .norm_utilities import (Np2DLike, Np2DLikeEye, Np2DLikeInv, - Np2DLikeInvLowRank, normEngine) +from .norm_utilities import (Np2DLike, Np2DLikeInv, Np2DLikeInvLowRank, + normEngine) __all__ = [ 'RROMPyLinearSolvers', 'setupSolver', 'Np2DLike', - 'Np2DLikeEye', 'Np2DLikeInv', 'Np2DLikeInvLowRank', 'normEngine' ] diff --git a/rrompy/solver/fenics/fenics_norms.py b/rrompy/solver/fenics/fenics_norms.py index 8f53414..56b4939 100644 --- a/rrompy/solver/fenics/fenics_norms.py +++ b/rrompy/solver/fenics/fenics_norms.py @@ -1,88 +1,82 @@ # Copyright (C) 2018 by the RROMPy authors # # This file is part of RROMPy. # # RROMPy is free software: you can redistribute it and/or modify # it under the terms of the GNU Lesser General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # RROMPy is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU Lesser General Public License for more details. # # You should have received a copy of the GNU Lesser General Public License # along with RROMPy. If not, see . # import fenics as fen from rrompy.utilities.base.types import Np2D, FenFunc, DictAny, FenFuncSpace -from rrompy.solver.norm_utilities import (Np2DLikeEye, Np2DLikeInv, - Np2DLikeInvLowRank) +from rrompy.solver.norm_utilities import (Np2DLikeInv, Np2DLikeInvLowRank) from .fenics_la import fenics2Sparse __all__ = ['L2NormMatrix', 'L2InverseNormMatrix', 'H1NormMatrix', 'Hminus1NormMatrix', 'elasticNormMatrix', 'elasticDualNormMatrix'] def L2NormMatrix(V:FenFuncSpace, r_ : FenFunc = 1.) -> Np2D: u = fen.TrialFunction(V) v = fen.TestFunction(V) 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: L2Mat = L2NormMatrix(V, r_) if compressRank is None: - return Np2DLikeInv(L2Mat, Np2DLikeEye(L2Mat.shape[0]), solverType, - solverArgs) - return Np2DLikeInvLowRank(L2Mat, Np2DLikeEye(L2Mat.shape[0]), solverType, - solverArgs, compressRank, compressOversampling, - compressSeed) + return Np2DLikeInv(L2Mat, 1., solverType, solverArgs) + return Np2DLikeInvLowRank(L2Mat, 1., 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 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, duality : bool = True) -> Np2D: + compressSeed : int = 420) -> Np2D: H1Mat = H1NormMatrix(V, w, r_, a_) - identity = L2NormMatrix(V, r_) if duality else Np2DLikeEye(H1Mat.shape[0]) if compressRank is None: - return Np2DLikeInv(H1Mat, identity, solverType, solverArgs) - return Np2DLikeInvLowRank(H1Mat, identity, solverType, solverArgs, - compressRank, compressOversampling, compressSeed) + return Np2DLikeInv(H1Mat, 1., solverType, solverArgs) + return Np2DLikeInvLowRank(H1Mat, 1., 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 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, - duality : bool = True) -> Np2D: + compressSeed : int = 420) -> Np2D: elMat = elasticNormMatrix(V, l_, m_, w, r_) - identity = L2NormMatrix(V, r_) if duality else Np2DLikeEye(elMat.shape[0]) if compressRank is None: - return Np2DLikeInv(elMat, identity, solverType, solverArgs) - return Np2DLikeInvLowRank(elMat, identity, solverType, solverArgs, - compressRank, compressOversampling, compressSeed) + return Np2DLikeInv(elMat, 1., solverType, solverArgs) + return Np2DLikeInvLowRank(elMat, 1., solverType, solverArgs, compressRank, + compressOversampling, compressSeed) diff --git a/rrompy/solver/norm_utilities.py b/rrompy/solver/norm_utilities.py index 244ec67..5134238 100644 --- a/rrompy/solver/norm_utilities.py +++ b/rrompy/solver/norm_utilities.py @@ -1,100 +1,88 @@ # 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 numbers import Number from copy import deepcopy as copy from rrompy.utilities.base.types import Np1D, Np2D, DictAny from rrompy.utilities.numerical import dot as tdot, solve as tsolve from rrompy.solver.linear_solver import setupSolver from rrompy.utilities.exception_manager import RROMPyException -__all__ = ['Np2DLike', 'Np2DLikeEye', 'Np2DLikeInv', 'Np2DLikeInvLowRank', - 'normEngine'] +__all__ = ['Np2DLike', 'Np2DLikeInv', 'Np2DLikeInvLowRank', 'normEngine'] @abstractmethod class Np2DLike: def dot(self, u:Np2D) -> Np2D: pass -class Np2DLikeEye(Np2DLike): - def __init__(self, n:int): - self.n = n - @property - def T(self): - return self - @property - def shape(self): - return (self.n, self.n) - 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.K, self.M = K, M + self.MH = np.conj(M) if isinstance(self.M, Number) else M.T.conj() try: self.solver, self.solverArgs = setupSolver(solverType, solverArgs) except: self.solver, self.solverArgs = solverType, solverArgs def dot(self, u:Np2D) -> Np2D: return tdot(self.MH, tsolve(self.K, tdot(self.M, u), self.solver, self.solverArgs)).reshape(u.shape) @property def shape(self): + if isinstance(self.M, Number): return self.K.shape return (self.MH.shape[0], self.M.shape[1]) class Np2DLikeInvLowRank(Np2DLike): def __init__(self, K:Np2D, M:Np2D, solverType:str, solverArgs:DictAny, rank:int, oversampling : int = 10, seed : int = 420): sizeO = K.shape[1] if hasattr(K, "shape") else M.shape[1] if rank > sizeO: 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(sizeO, 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, full_matrices = False) self.L = Q.dot(U[:, : rank]) * s[: rank] self.R = Vh[: rank, :] def dot(self, u:Np2D) -> Np2D: return tdot(self.L, tdot(self.R, u)).reshape(u.shape) @property def shape(self): return (self.L.shape[0], self.R.shape[1]) class normEngine: - def __init__(self, energyNormMatrix:Np2D): - self.energyNormMatrix = copy(energyNormMatrix) + def __init__(self, normMatrix:Np2D): + self.normMatrix = copy(normMatrix) 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(tdot(self.energyNormMatrix, u) * v.conj(), axis = 0) - return tdot(tdot(self.energyNormMatrix, u).T, v.conj()).T + return np.sum(tdot(self.normMatrix, u) * v.conj(), axis = 0) + return tdot(tdot(self.normMatrix, u).T, v.conj()).T def norm(self, u:Np2D) -> Np1D: return np.power(np.abs(self.innerProduct(u, u, onlyDiag = True)), .5) diff --git a/rrompy/utilities/base/types.py b/rrompy/utilities/base/types.py index ced3eef..5aef1bd 100644 --- a/rrompy/utilities/base/types.py +++ b/rrompy/utilities/base/types.py @@ -1,61 +1,58 @@ # Copyright (C) 2018 by the RROMPy authors # # This file is part of RROMPy. # # RROMPy is free software: you can redistribute it and/or modify # it under the terms of the GNU Lesser General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # RROMPy is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU Lesser General Public License for more details. # # You should have received a copy of the GNU Lesser General Public License # along with RROMPy. If not, see . # from typing import TypeVar, List, Tuple, Dict, Any -__all__ = ['TupleAny','ListAny','DictAny','ScOp','Np1D','Np2D','Np1DLst', - 'N2FSExpr','FenExpr','FenFunc','FenFuncSpace','FenBC','HFEng', - 'ROMEng','sampleEng','normEng','interpEng','paramVal','paramList', - 'sampList','GenExpr','strLst', 'BfSExpr'] +__all__ = ['TupleAny','ListAny','DictAny','Np1D','Np2D','Np1DLst','N2FSExpr', + 'FenExpr','FenFunc','FenFuncSpace','FenBC','HFEng','ROMEng', + 'sampleEng','normEng','interpEng','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") interpEng = TypeVar("Interpolator engine") # CUSTOM TYPES paramVal = TypeVar("Parameter value tuple") paramList = TypeVar("Parameter value tuple list") sampList = TypeVar("Sample list") # OTHERS GenExpr = TypeVar("Generic expression") strLst = TypeVar("str or list of str") BfSExpr = TypeVar("Boolean function or string") diff --git a/rrompy/utilities/base/verbosity_depth.py b/rrompy/utilities/base/verbosity_depth.py index 483b2dc..055eb13 100644 --- a/rrompy/utilities/base/verbosity_depth.py +++ b/rrompy/utilities/base/verbosity_depth.py @@ -1,59 +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 . # from rrompy.utilities.exception_manager import RROMPyException __all__ = ["verbosityDepth", "verbosityManager"] from datetime import datetime def getTimestamp() -> str: - return "\x1b[42m{}\x1b[0m".format(datetime.now().strftime("%H:%M:%S.%f")) + global RROMPy_verbosity_no_color + time = datetime.now().strftime("%H:%M:%S.%f") + if "RROMPy_verbosity_no_color" in globals(): return time + return "\x1b[42m{}\x1b[0m".format(time) def verbosityDepth(vdtype:str, message:str, end : str = "\n", timestamp : bool = True): global RROMPy_verbosity_depth assert isinstance(vdtype, str) if vdtype.upper() not in ["INIT", "MAIN", "DEL"]: raise RROMPyException("Verbosity depth type not recognized.") out = "{} ".format(getTimestamp()) if timestamp else "" if vdtype == "INIT": if "RROMPy_verbosity_depth" not in globals(): RROMPy_verbosity_depth = 0 RROMPy_verbosity_depth += 1 out += "│" * (RROMPy_verbosity_depth - 1) out += "┌" else: assert "RROMPy_verbosity_depth" in globals() if vdtype == "MAIN": out += "│" * (RROMPy_verbosity_depth - 1) out += "├" elif vdtype == "DEL": RROMPy_verbosity_depth -= 1 out += "│" * RROMPy_verbosity_depth out += "└" if RROMPy_verbosity_depth <= 0: del RROMPy_verbosity_depth if message != "": print("{}{}".format(out, message), end = end) return def verbosityManager(object, vdtype:str, message:str, vlvl : int = 0, end : str = "\n"): if object.verbosity >= vlvl: return verbosityDepth(vdtype, message, end, object.timestamp) diff --git a/rrompy/utilities/exception_manager/exception_manager.py b/rrompy/utilities/exception_manager/exception_manager.py index c019850..c9c4f1b 100644 --- a/rrompy/utilities/exception_manager/exception_manager.py +++ b/rrompy/utilities/exception_manager/exception_manager.py @@ -1,32 +1,33 @@ # 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 . # __all__ = ["RROMPyException"] def purgeVerbosityDepth(): from rrompy.utilities.base.verbosity_depth import verbosityDepth while True: try: verbosityDepth("DEL", "", "", False) except: break class RROMPyException(Exception): def __init__(self, message): purgeVerbosityDepth() + self._msg = message super().__init__(message) diff --git a/rrompy/utilities/exception_manager/warning_manager.py b/rrompy/utilities/exception_manager/warning_manager.py index b9c44d4..dc4c3df 100644 --- a/rrompy/utilities/exception_manager/warning_manager.py +++ b/rrompy/utilities/exception_manager/warning_manager.py @@ -1,38 +1,47 @@ # Copyright (C) 2018 by the RROMPy authors # # This file is part of RROMPy. # # RROMPy is free software: you can redistribute it and/or modify # it under the terms of the GNU Lesser General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # RROMPy is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU Lesser General Public License for more details. # # You should have received a copy of the GNU Lesser General Public License # along with RROMPy. If not, see . # import traceback as tb from datetime import datetime __all__ = ['RROMPyWarning'] def getTimestamp() -> str: - return "\x1b[42m{}\x1b[0m".format(datetime.now().strftime("%H:%M:%S.%f")) + global RROMPy_verbosity_no_color + time = datetime.now().strftime("%H:%M:%S.%f") + if "RROMPy_verbosity_no_color" in globals(): return time + return "\x1b[42m{}\x1b[0m".format(time) def RROMPyWarning(msg : str = "", stacklevel : int = 0): + global RROMPy_verbosity_no_color + if "RROMPy_verbosity_no_color" in globals(): + copen, copen1, cclose = "", "", "" + else: + copen, copen1, cclose = "\x1b[3m", "\x1b[31m", "\x1b[0m" + frameSummary = tb.extract_stack()[- 3 - stacklevel] timestamp = getTimestamp() if frameSummary.name == "": name = "" else: name = ", within {}".format(frameSummary.name) - print("{}\x1b[3m Warning at {}:{}{}:\x1b[0m".format(timestamp, - frameSummary.filename, - frameSummary.lineno, - name)) - print("> \x1b[31m{}\x1b[0m".format(frameSummary.line)) + print("{}{} Warning at {}:{}{}:{}".format(timestamp, copen, + frameSummary.filename, + frameSummary.lineno, + name, cclose)) + print("> {}{}{}".format(copen1, frameSummary.line, cclose)) if len(msg) > 0: - print("\x1b[3m {}\x1b[0m\n".format(msg)) + print("{} {}{}\n".format(copen, msg, cclose)) diff --git a/rrompy/utilities/numerical/__init__.py b/rrompy/utilities/numerical/__init__.py index 10f6b57..d015ec8 100644 --- a/rrompy/utilities/numerical/__init__.py +++ b/rrompy/utilities/numerical/__init__.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 . # from .custom_pinv import customPInv from .degree import (fullDegreeN, totalDegreeN, fullDegreeSet, totalDegreeSet, degreeTotalToFull, fullDegreeMaxMask, totalDegreeMaxMask) from .factorials import multibinom, multifactorial from .halton import haltonGenerate from .hash_derivative import (nextDerivativeIndices, hashDerivativeToIdx, hashIdxToDerivative) from .kroneckerer import kroneckerer from .low_discrepancy import lowDiscrepancy from .marginalize_poly_list import marginalizePolyList from .nonlinear_eigenproblem import (linearizeDense, eigNonlinearDense, eigvalsNonlinearDense) from .number_theory import squareResonances from .point_matching import pointMatching +from .rayleigh_quotient_iteration import rayleighQuotientIteration from .sobol import sobolGenerate from .tensor_la import dot, solve freepar = None __all__ = [ 'freepar', 'customPInv', 'fullDegreeN', 'totalDegreeN', 'fullDegreeSet', 'totalDegreeSet', 'degreeTotalToFull', 'fullDegreeMaxMask', 'totalDegreeMaxMask', 'multibinom', 'multifactorial', 'haltonGenerate', 'nextDerivativeIndices', 'hashDerivativeToIdx', 'hashIdxToDerivative', 'kroneckerer', 'lowDiscrepancy', 'marginalizePolyList', 'linearizeDense', 'eigNonlinearDense', 'eigvalsNonlinearDense', 'squareResonances', 'pointMatching', + 'rayleighQuotientIteration', 'sobolGenerate', 'dot', 'solve' ] diff --git a/rrompy/utilities/numerical/custom_pinv.py b/rrompy/utilities/numerical/custom_pinv.py index 3cda020..4a1e8da 100644 --- a/rrompy/utilities/numerical/custom_pinv.py +++ b/rrompy/utilities/numerical/custom_pinv.py @@ -1,48 +1,52 @@ # Copyright (C) 2018 by the RROMPy authors # # This file is part of RROMPy. # # RROMPy is free software: you can redistribute it and/or modify # it under the terms of the GNU Lesser General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # RROMPy is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU Lesser General Public License for more details. # # You should have received a copy of the GNU Lesser General Public License # along with RROMPy. If not, see . # from copy import deepcopy as copy import numpy as np +from numbers import Number __all__ = ["customPInv"] def customPInv(A, rcond=-1, full=False): """ Compute the (Moore-Penrose) pseudo-inverse of a matrix. Calculate the generalized inverse of a matrix using its singular-value decomposition (SVD) and including all *large* singular values. """ + if isinstance(A, Number): + if np.isclose(A, 0.): return np.inf + return 1. / A A = A.conjugate() u, s, vt = np.linalg.svd(A, full_matrices=False) if rcond < 0: rcond = len(A) * np.finfo(A.dtype).eps cutoff = rcond * np.amax(s) large = s > cutoff sinv = copy(s) sinv = np.divide(1, s, where = large, out = sinv) sinv[~large] = 0 res = (vt.T * sinv) @ u.T if full: return res, [np.sum(large), s, rcond] else: return res diff --git a/rrompy/utilities/numerical/rayleigh_quotient_iteration.py b/rrompy/utilities/numerical/rayleigh_quotient_iteration.py new file mode 100644 index 0000000..e395918 --- /dev/null +++ b/rrompy/utilities/numerical/rayleigh_quotient_iteration.py @@ -0,0 +1,40 @@ +# Copyright (C) 2018 by the RROMPy authors +# +# This file is part of RROMPy. +# +# RROMPy is free software: you can redistribute it and/or modify +# it under the terms of the GNU Lesser General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# RROMPy is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with RROMPy. If not, see . +# + +import numpy as np +from rrompy.utilities.base.types import Np1D, Np2D, DictAny +from .tensor_la import dot, solve + +__all__ = ['rayleighQuotientIteration'] + +def rayleighQuotientIteration(A:Np2D, v0:Np1D, M:Np2D, solver:callable, + solverArgs:DictAny, sigma : float = 0., + nIterP : int = 10, nIterR : int = 10) -> float: + nIterP = min(nIterP, len(v0) // 2) + nIterR = min(nIterR, (len(v0) + 1) // 2) + v0 /= dot(dot(M, v0).T, v0.conj()) ** .5 + for j in range(nIterP): + v0 = solve(A - sigma * M, dot(M, v0), solver, solverArgs) + v0 /= dot(dot(M, v0).T, v0.conj()) ** .5 + l0 = dot(A.dot(v0).T, v0.conj()) + for j in range(nIterR): + v0 = solve(A - l0 * M, dot(M, v0), solver, solverArgs) + v0 /= dot(dot(M, v0).T, v0.conj()) ** .5 + l0 = dot(A.dot(v0).T, v0.conj()) + if np.isnan(l0): l0 = np.finfo(float).eps + return np.abs(l0) diff --git a/rrompy/utilities/numerical/tensor_la.py b/rrompy/utilities/numerical/tensor_la.py index 9900771..bc7097d 100644 --- a/rrompy/utilities/numerical/tensor_la.py +++ b/rrompy/utilities/numerical/tensor_la.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 numbers import Number __all__ = ['dot', 'solve'] def dot(u, v): + if isinstance(u, Number) or isinstance(v, Number): return u * v if u.shape[-1] == v.shape[0]: if isinstance(u, np.ndarray): return np.tensordot(u, v, 1) else: return u.dot(v) M = u.shape[-1] N = v.shape[0] // M rshape = u.shape[: -2] + (N * u.shape[-2],) + v.shape[1 :] return u.dot(v.reshape(M, -1)).reshape(rshape) def solve(A, b, solver, kwargs): + if isinstance(A, Number): return b / A if A.shape[-1] == b.shape[0]: return solver(A, b, kwargs) M = A.shape[-1] N = b.shape[0] // M rshape = A.shape[: -2] + (N * A.shape[-2],) + b.shape[1 :] return solver(A, b.reshape(M, -1), kwargs).reshape(rshape) diff --git a/rrompy/utilities/poly_fitting/heaviside/vander.py b/rrompy/utilities/poly_fitting/heaviside/vander.py index ce816d3..3d0a2f7 100644 --- a/rrompy/utilities/poly_fitting/heaviside/vander.py +++ b/rrompy/utilities/poly_fitting/heaviside/vander.py @@ -1,89 +1,87 @@ # 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, polyvanderTotal as pvTP) from rrompy.utilities.base.types import Np1D, Np2D, Tuple, List, paramList from rrompy.parameter import checkParameterList from rrompy.utilities.exception_manager import RROMPyException __all__ = ['heavisidevander', 'polyvander', 'polyvanderTotal'] def heavisidevander(x:paramList, poles:Np1D, reorder : List[int] = None) -> Np2D: """Compute Heaviside-Vandermonde matrix.""" x = checkParameterList(x, 1)[0] 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.flatten() if reorder is not None: x = x[reorder] poles = poles.flatten() Van = np.empty((len(x), len(poles)), dtype = np.complex) for j in range(len(x)): Van[j, :] = 1. / (x[j] - poles) return Van def polyvander(x:paramList, poles:Np1D, degs : List[int] = None, basis : str = "MONOMIAL_HEAVISIDE", derIdxs : List[List[List[int]]] = None, reorder : List[int] = None, scl : Np1D = None) -> Np2D: """ Compute full 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 heaviside " "function.")) basisp = basis.split("_")[0] VanH = heavisidevander(x, poles, reorder = reorder) if degs is None or np.sum(degs) < 0: VanP = np.empty((len(x), 0)) else: VanP = pvP(x, degs, basisp, derIdxs = derIdxs, reorder = reorder, scl = scl) return np.block([[VanH, VanP]]) def polyvanderTotal(x:paramList, poles:Np1D, deg : int = None, basis : str = "MONOMIAL_HEAVISIDE", derIdxs : List[List[List[int]]] = None, - reorder : List[int] = None, scl : Np1D = None)\ - -> Tuple[Np2D, List[List[int]], List[int]]: + reorder : List[int] = None, scl : Np1D = None) -> Np2D: """ Compute full total degree 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 = basis.split("_")[0] VanR = heavisidevander(x, poles, reorder = reorder) if deg is None or deg < 0: VanP = np.empty((len(x), 0)) derIdxs, ordIdxs = np.zeros(0, dtype = int), np.zeros(0, dtype = int) else: - VanP, derIdxs, ordIdxs = pvTP(x, deg, basisp, derIdxs = derIdxs, - reorder = reorder, scl = scl) + VanP = pvTP(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) + return np.block([[VanR, VanP], + [VanP.T.conj(), np.zeros(tuple([VanP.shape[1]] * 2))]]) diff --git a/rrompy/utilities/poly_fitting/moving_least_squares/moving_least_squares_interpolator.py b/rrompy/utilities/poly_fitting/moving_least_squares/moving_least_squares_interpolator.py index bab415d..5dd78d1 100644 --- a/rrompy/utilities/poly_fitting/moving_least_squares/moving_least_squares_interpolator.py +++ b/rrompy/utilities/poly_fitting/moving_least_squares/moving_least_squares_interpolator.py @@ -1,144 +1,144 @@ # 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 (List, ListAny, DictAny, Np1D, Np2D, paramList) from rrompy.utilities.numerical import customPInv, dot from .vander import mlsweights from rrompy.utilities.poly_fitting.interpolator import GenericInterpolator from rrompy.utilities.poly_fitting.polynomial.vander import (polyvander as pv, polyvanderTotal as pvT) from rrompy.utilities.exception_manager import RROMPyException, RROMPyAssert from rrompy.parameter import checkParameterList __all__ = ['MovingLeastSquaresInterpolator'] class MovingLeastSquaresInterpolator(GenericInterpolator): """HERE""" def __init__(self, other = None): if other is None: return self.support = other.support self.localProjector = other.localProjector self.localVanders = other.localVanders self.suppValues = other.suppValues self.directionalWeights = other.directionalWeights self.degree = other.degree self.npar = other.npar self.radialbasis = other.radialbasis self.polybasis = other.polybasis self.evalParams = other.evalParams self.totalDegree = other.totalDegree @property def shape(self): sh = self.suppValues.shape[1 :] if self.suppValues.ndim > 1 else 1 return sh @property def deg(self): return self.degree def __call__(self, mu:paramList, der : List[int] = None, scl : Np1D = None): if der is not None and np.sum(der) > 0: raise RROMPyException(("Cannot take derivatives of moving least " "squares function.")) mu = checkParameterList(mu, self.npar)[0] sh = self.shape if sh == 1: sh = tuple([]) values = np.empty((len(mu),) + sh, dtype = np.complex) for i, m in enumerate(mu): weights = mlsweights(m, self.support, self.radialbasis, directionalWeights = self.directionalWeights, nNearestNeighbor = self.evalParams["nNearestNeighbor"]) weights /= np.linalg.norm(weights) vanderLS = np.sum(self.localVanders * weights, axis = 2) RHSLS = dot(self.localProjector * weights, self.suppValues) if self.totalDegree: - vanderEval, _, _ = pvT(m, self.deg[0], self.polybasis, - **self.evalParams) + vanderEval = pvT(m, self.deg[0], self.polybasis, + **self.evalParams) else: vanderEval = pv(m, self.deg, self.polybasis, **self.evalParams) vanderEval = vanderEval.flatten() values[i] = dot(vanderEval, dot(customPInv(vanderLS), RHSLS)) return values def __copy__(self): return MovingLeastSquaresInterpolator(self) def __deepcopy__(self, memo): other = MovingLeastSquaresInterpolator() (other.support, other.localProjector, other.localVanders, other.suppValues, other.directionalWeights, other.degree, other.npar, other.radialbasis, other.polybasis, other.evalParams, other.totalDegree) = copy( (self.support, self.localProjector, self.localVanders, self.suppValues, self.directionalWeights, self.degree, self.npar, self.radialbasis, self.polybasis, self.evalParams, self.totalDegree), memo) return other def postmultiplyTensorize(self, A:Np2D): RROMPyAssert(A.shape[0], self.shape[-1], "Shape of output") self.suppValues = self.suppValues.dot(A) def pad(self, nleft : List[int] = None, nright : List[int] = None): if nleft is None: nleft = [0] * len(self.shape) if nright is None: nright = [0] * len(self.shape) if not hasattr(nleft, "__len__"): nleft = [nleft] if not hasattr(nright, "__len__"): nright = [nright] RROMPyAssert(len(self.shape), len(nleft), "Shape of output") RROMPyAssert(len(self.shape), len(nright), "Shape of output") padwidth = [(0, 0)] + [(l, r) for l, r in zip(nleft, nright)] self.suppValues = np.pad(self.suppValues, padwidth, "constant", constant_values = (0., 0.)) def setupByInterpolation(self, support:paramList, values:ListAny, deg:int, polybasis : str = "MONOMIAL_GAUSSIAN", directionalWeights : Np1D = None, totalDegree : bool = True, vanderCoeffs : DictAny = {}): support = checkParameterList(support)[0] self.support = copy(support) if "reorder" in vanderCoeffs.keys(): self.support = self.support[vanderCoeffs["reorder"]] if "nNearestNeighbor" not in vanderCoeffs.keys(): vanderCoeffs["nNearestNeighbor"] = -1 self.npar = support.shape[1] if directionalWeights is None: directionalWeights = np.ones(self.npar) self.directionalWeights = directionalWeights self.polybasis, self.radialbasis, _ = polybasis.split("_") self.totalDegree = totalDegree self.evalParams = vanderCoeffs if totalDegree: - vander, _, _ = pvT(support, deg, self.polybasis, **vanderCoeffs) + vander = pvT(support, deg, self.polybasis, **vanderCoeffs) if not hasattr(deg, "__len__"): deg = [deg] * self.npar else: if not hasattr(deg, "__len__"): deg = [deg] * self.npar vander = pv(support, deg, self.polybasis, **vanderCoeffs) self.degree = deg self.localProjector = vander.T.conj() self.localVanders = np.array([np.outer(van, van.conj()) \ for van in vander]) self.localVanders = np.swapaxes(self.localVanders, 0, 2) self.suppValues = np.array(values) diff --git a/rrompy/utilities/poly_fitting/moving_least_squares/vander.py b/rrompy/utilities/poly_fitting/moving_least_squares/vander.py index 04094e1..1ab35c8 100644 --- a/rrompy/utilities/poly_fitting/moving_least_squares/vander.py +++ b/rrompy/utilities/poly_fitting/moving_least_squares/vander.py @@ -1,97 +1,96 @@ # 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 .kernel import (radialGaussian, thinPlateSpline, multiQuadric, nearestNeighbor) from rrompy.utilities.poly_fitting.polynomial.vander import (polyvander as pvP, polyvanderTotal as pvTP) from rrompy.utilities.base.types import (Np1D, Np2D, Tuple, List, paramVal, paramList) from rrompy.parameter import checkParameter, checkParameterList from rrompy.utilities.exception_manager import RROMPyException, RROMPyAssert __all__ = ['mlsweights', 'polyvander', 'polyvanderTotal'] def mlsweights(x:paramVal, xSupp:paramList, basis:str, reorder : List[int] = None, directionalWeights : Np1D = None, nNearestNeighbor : int = -1) -> Np2D: """Compute moving least squares weight vector.""" x = checkParameter(x) xSupp = checkParameterList(xSupp)[0] x = x.data xSupp = xSupp.data if directionalWeights is None: directionalWeights = np.ones(x.shape[1]) elif not hasattr(directionalWeights, "__len__"): directionalWeights = directionalWeights * np.ones(x.shape[1]) RROMPyAssert(len(directionalWeights), x.shape[1], "Number of directional weights") try: radialkernel = {"GAUSSIAN" : radialGaussian, "THINPLATE" : thinPlateSpline, "MULTIQUADRIC" : multiQuadric, "NEARESTNEIGHBOR" : nearestNeighbor}[basis.upper()] except: raise RROMPyException("Radial basis not recognized.") if reorder is not None: xSupp = xSupp[reorder] muDiff = (xSupp.data - x) * directionalWeights r2 = np.sum(np.abs(muDiff) ** 2., axis = 1).reshape(1, -1) if basis.upper() == "NEARESTNEIGHBOR": if nNearestNeighbor > 0 and nNearestNeighbor < len(xSupp): cutoffValue = np.partition(r2, nNearestNeighbor - 1)[0, nNearestNeighbor - 1] r2 /= cutoffValue else: r2[0, :] = 1. * (nNearestNeighbor == 0) return radialkernel(r2)[0] def polyvander(x:paramVal, xSupp:paramList, degs:List[int], basis:str, derIdxs : List[List[List[int]]] = None, reorder : List[int] = None, directionalWeights : Np1D = None, scl : Np1D = None, nNearestNeighbor : int = -1) -> Tuple[Np2D, Np2D]: """ Compute full Hermite-Vandermonde matrix with specified derivative directions. """ basisp, basisr, _ = basis.split("_") Weights = mlsweights(x, xSupp, basisr, reorder, directionalWeights, nNearestNeighbor) VanP = pvP(xSupp, degs, basisp, derIdxs = derIdxs, reorder = reorder, scl = scl) RHP = VanP.T.conj() * Weights return RHP.dot(VanP), RHP def polyvanderTotal(x:paramList, xSupp:paramList, deg:int, basis:str, derIdxs : List[List[List[int]]] = None, reorder : List[int] = None, directionalWeights : Np1D = None, scl : Np1D = None, - nNearestNeighbor : int = -1)\ - -> Tuple[Np2D, Np2D, List[List[int]], List[int]]: + nNearestNeighbor : int = -1) -> Tuple[Np2D, Np2D]: """ Compute full total degree Hermite-Vandermonde matrix with specified derivative directions. """ basisp, basisr, _ = basis.split("_") Weights = mlsweights(x, xSupp, basisr, reorder, directionalWeights, nNearestNeighbor) - VanP, derIdxs, ordIdxs = pvTP(x, deg, basisp, derIdxs = derIdxs, - reorder = reorder, scl = scl) + VanP = pvTP(x, deg, basisp, derIdxs = derIdxs, + reorder = reorder, scl = scl) RHP = VanP.T.conj() * Weights - return RHP.dot(VanP), RHP, derIdxs, ordIdxs + return RHP.dot(VanP), RHP diff --git a/rrompy/utilities/poly_fitting/polynomial/polynomial_interpolator.py b/rrompy/utilities/poly_fitting/polynomial/polynomial_interpolator.py index 3b46e64..0eb05ff 100644 --- a/rrompy/utilities/poly_fitting/polynomial/polynomial_interpolator.py +++ b/rrompy/utilities/poly_fitting/polynomial/polynomial_interpolator.py @@ -1,131 +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 . # import numpy as np from copy import deepcopy as copy from rrompy.utilities.base.types import (List, ListAny, DictAny, Np1D, Np2D, paramList) from rrompy.utilities.base import freepar as fp from rrompy.utilities.poly_fitting.interpolator import GenericInterpolator from rrompy.utilities.poly_fitting.custom_fit import customFit from .base import polyfitname from .val import polyval from .roots import polyroots from .vander import polyvander as pv, polyvanderTotal as pvT from rrompy.utilities.numerical import degreeTotalToFull, dot from rrompy.utilities.exception_manager import RROMPyAssert, RROMPyException from rrompy.parameter import checkParameterList __all__ = ['PolynomialInterpolator'] class PolynomialInterpolator(GenericInterpolator): """HERE""" def __init__(self, other = None): if other is None: return self.coeffs = other.coeffs self.npar = other.npar self.polybasis = other.polybasis @property def shape(self): if self.coeffs.ndim > self.npar: sh = self.coeffs.shape[self.npar :] else: sh = tuple([1]) return sh @property def deg(self): return [x - 1 for x in self.coeffs.shape[: self.npar]] def __getitem__(self, key): return self.coeffs[key] def __call__(self, mu:paramList, der : List[int] = None, scl : Np1D = None): return polyval(mu, self.coeffs, self.polybasis, der, scl) def __copy__(self): return PolynomialInterpolator(self) def __deepcopy__(self, memo): other = PolynomialInterpolator() other.coeffs, other.npar, other.polybasis = copy( (self.coeffs, self.npar, self.polybasis), memo) return other def pad(self, nleft : List[int] = None, nright : List[int] = None): if nleft is None: nleft = [0] * len(self.shape) if nright is None: nright = [0] * len(self.shape) if not hasattr(nleft, "__len__"): nleft = [nleft] if not hasattr(nright, "__len__"): nright = [nright] RROMPyAssert(len(self.shape), len(nleft), "Shape of output") RROMPyAssert(len(self.shape), len(nright), "Shape of output") padwidth = [(0, 0)] * self.npar padwidth = padwidth + [(l, r) for l, r in zip(nleft, nright)] self.coeffs = np.pad(self.coeffs, padwidth, "constant", constant_values = (0., 0.)) def postmultiplyTensorize(self, A:Np2D): RROMPyAssert(A.shape[0], self.shape[-1], "Shape of output") self.coeffs = dot(self.coeffs, A) def setupByInterpolation(self, support:paramList, values:ListAny, deg:int, polybasis : str = "MONOMIAL", verbose : bool = True, totalDegree : bool = True, vanderCoeffs : DictAny = {}, fitCoeffs : DictAny = {}): support = checkParameterList(support)[0] self.npar = support.shape[1] self.polybasis = polybasis if totalDegree: - vander, _, reorder = pvT(support, deg, basis = polybasis, - **vanderCoeffs) - vander = vander[:, reorder] + vander = pvT(support, deg, basis = polybasis, **vanderCoeffs) else: if not hasattr(deg, "__len__"): deg = [deg] * self.npar vander = pv(support, deg, basis = polybasis, **vanderCoeffs) outDim = values.shape[1:] values = values.reshape(values.shape[0], -1) fitOut = customFit(vander, values, full = True, **fitCoeffs) P = fitOut[0] if verbose: msg = ("Fitting {} samples with degree {} through {}... " "Conditioning of LS system: {:.4e}.").format( len(vander), deg, polyfitname(self.polybasis), fitOut[1][2][0] / fitOut[1][2][-1]) else: msg = None if totalDegree: self.coeffs = degreeTotalToFull(tuple([deg + 1] * self.npar) + outDim, self.npar, P) else: self.coeffs = P.reshape(tuple([d + 1 for d in deg]) + outDim) return fitOut[1][1] == vander.shape[1], msg def roots(self, marginalVals : ListAny = [fp]): RROMPyAssert(self.shape, (1,), "Shape of output") RROMPyAssert(len(marginalVals), self.npar, "Number of parameters") try: rDim = marginalVals.index(fp) if rDim < len(marginalVals) - 1 and fp in marginalVals[rDim + 1 :]: raise except: raise RROMPyException(("Exactly 1 'freepar' entry in " "marginalVals must be provided.")) return polyroots(self.coeffs, self.polybasis, marginalVals) diff --git a/rrompy/utilities/poly_fitting/polynomial/vander.py b/rrompy/utilities/poly_fitting/polynomial/vander.py index 4f20015..e3e1e2b 100644 --- a/rrompy/utilities/poly_fitting/polynomial/vander.py +++ b/rrompy/utilities/poly_fitting/polynomial/vander.py @@ -1,138 +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 from .der import polyder from rrompy.utilities.base.types import Np1D, Np2D, Tuple, List, paramList from rrompy.utilities.numerical import totalDegreeSet from rrompy.parameter import checkParameterList from rrompy.utilities.exception_manager import RROMPyException, RROMPyAssert __all__ = ['polyvander', 'polyvanderTotal'] def firstDerTransition(dim:int, TDirac:List[Np2D], basis:str, scl : Np1D = None) -> Np2D: C_m = np.zeros((dim, len(TDirac), len(TDirac)), dtype = float) for j, Tj in enumerate(TDirac): m, om = [0] * dim, [(0, 0)] * dim for idx in range(dim): m[idx], om[idx] = 1, (0, 1) J_der = polyder(Tj, basis, m, scl) C_m[idx, :, j] = np.ravel(np.pad(J_der, mode = "constant", pad_width = om)) m[idx], om[idx] = 0, (0, 0) return C_m def countDerDirections(n:int, base:int, digits:int, idx:int): if digits == 0: return [] dig = n % base return [(idx, dig)] * (dig > 0) + countDerDirections( (n - dig) // base, base, digits - 1, idx + 1) def polyvander(x:paramList, degs:List[int], basis:str, derIdxs : List[List[List[int]]] = None, reorder : List[int] = None, scl : Np1D = None) -> Np2D: """ Compute full Hermite-Vandermonde matrix with specified derivative directions. E.g. assume that we want to obtain the Vandermonde matrix for (value, derx, derx2) at x = [0, 0], (value, dery) at x = [1, 0], (dery, derxy) at x = [0, 0], of degree 3 in x and 1 in y, using Chebyshev polynomials. This can be done by polyvander([[0, 0], [1, 0]], # unique sample points [3, 1], # polynomial degree "chebyshev", # polynomial family [ [[0, 0], [1, 0], [2, 0], [0, 1], [1, 1]], # derivative directions at first point [[0, 0], [0, 1]] # derivative directions at second point ], [0, 1, 2, 5, 6, 3, 4] # reorder indices ) """ if not isinstance(degs, (list,tuple,np.ndarray,)): degs = [degs] dim = len(degs) x = checkParameterList(x, dim)[0] x_un, idx_un = x.unique(return_inverse = True) if len(x_un) < len(x): raise RROMPyException("Sample points must be distinct.") del x_un try: vanderbase = {"CHEBYSHEV" : np.polynomial.chebyshev.chebvander, "LEGENDRE" : np.polynomial.legendre.legvander, "MONOMIAL" : np.polynomial.polynomial.polyvander }[basis.upper()] except: raise RROMPyException("Polynomial basis not recognized.") VanBase = vanderbase(x(0), degs[0]) for j in range(1, dim): VNext = vanderbase(x(j), degs[j]) for jj in range(j): VNext = np.expand_dims(VNext, 1) VanBase = VanBase[..., None] * VNext VanBase = VanBase.reshape((len(x), -1)) if derIdxs is None or VanBase.shape[-1] <= 1: Van = VanBase else: derFlat, idxRep = [], [] for j, derIdx in enumerate(derIdxs): derFlat += derIdx[:] idxRep += [j] * len(derIdx[:]) for j in range(len(derFlat)): if not hasattr(derFlat[j], "__len__"): derFlat[j] = [derFlat[j]] RROMPyAssert(len(derFlat[j]), dim, "Number of dimensions") TDirac = [y.reshape([d + 1 for d in degs]) for y in np.eye(VanBase.shape[-1], dtype = int)] Cs_loc = firstDerTransition(dim, TDirac, basis, scl) Van = np.empty((len(derFlat), VanBase.shape[-1]), dtype = VanBase.dtype) for j in range(len(derFlat)): Van[j, :] = VanBase[idxRep[j], :] for k in range(dim): for der in range(derFlat[j][k]): Van[j, :] = Van[j, :].dot(Cs_loc[k]) / (der + 1) if reorder is not None: Van = Van[reorder, :] return Van def polyvanderTotal(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]]: + reorder : List[int] = None, scl : Np1D = None) -> Np2D: """ Compute full total degree Hermite-Vandermonde matrix with specified derivative directions. """ x = checkParameterList(x)[0] degs = [deg] * x.shape[1] VanBase = polyvander(x, degs, basis, derIdxs, reorder, scl) derIdxs, mask = totalDegreeSet(deg, x.shape[1], return_mask = True) 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 + return VanBase[:, mask][:, ordIdxs] diff --git a/rrompy/utilities/poly_fitting/radial_basis/radial_basis_interpolator.py b/rrompy/utilities/poly_fitting/radial_basis/radial_basis_interpolator.py index e1e0023..23134ec 100644 --- a/rrompy/utilities/poly_fitting/radial_basis/radial_basis_interpolator.py +++ b/rrompy/utilities/poly_fitting/radial_basis/radial_basis_interpolator.py @@ -1,147 +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 from copy import deepcopy as copy from rrompy.utilities.base.types import (List, ListAny, DictAny, Np1D, Np2D, paramList) from rrompy.utilities.poly_fitting.interpolator import GenericInterpolator from rrompy.utilities.poly_fitting.custom_fit import customFit from .base import polyfitname from .val import polyval from .vander import polyvander as pv, polyvanderTotal as pvT from rrompy.utilities.numerical import degreeTotalToFull, dot from rrompy.utilities.exception_manager import RROMPyException, RROMPyAssert from rrompy.parameter import checkParameterList __all__ = ['RadialBasisInterpolator'] class RadialBasisInterpolator(GenericInterpolator): """HERE""" def __init__(self, other = None): if other is None: return self.support = other.support self.coeffsGlobal = other.coeffsGlobal self.coeffsLocal = other.coeffsLocal self.directionalWeights = other.directionalWeights self.npar = other.npar self.polybasis = other.polybasis self.nNearestNeighbor = other.nNearestNeighbor @property def shape(self): sh = self.coeffsLocal.shape[1 :] if self.coeffsLocal.ndim > 1 else 1 return sh @property def deg(self): return [x - 1 for x in self.coeffsGlobal.shape[: self.npar]] def __call__(self, mu:paramList, der : List[int] = None, scl : Np1D = None): if der is not None and np.sum(der) > 0: raise RROMPyException(("Cannot take derivatives of radial basis " "function.")) return polyval(mu, self.coeffsGlobal, self.coeffsLocal, self.support, self.directionalWeights, self.polybasis, self.nNearestNeighbor) def __copy__(self): return RadialBasisInterpolator(self) def __deepcopy__(self, memo): other = RadialBasisInterpolator() (other.support, other.coeffsGlobal, other.coeffsLocal, other.directionalWeights, other.npar, other.polybasis, other.nNearestNeighbor) = copy( (self.support, self.coeffsGlobal, self.coeffsLocal, self.directionalWeights, self.npar, self.polybasis, self.nNearestNeighbor), memo) return other def postmultiplyTensorize(self, A:Np2D): RROMPyAssert(A.shape[0], self.shape[-1], "Shape of output") self.coeffsLocal = dot(self.coeffsLocal, A) self.coeffsGlobal = dot(self.coeffsGlobal, A) def pad(self, nleft : List[int] = None, nright : List[int] = None): if nleft is None: nleft = [0] * len(self.shape) if nright is None: nright = [0] * len(self.shape) if not hasattr(nleft, "__len__"): nleft = [nleft] if not hasattr(nright, "__len__"): nright = [nright] RROMPyAssert(len(self.shape), len(nleft), "Shape of output") RROMPyAssert(len(self.shape), len(nright), "Shape of output") padwidth = [(0, 0)] + [(l, r) for l, r in zip(nleft, nright)] self.coeffsLocal = np.pad(self.coeffsLocal, padwidth, "constant", constant_values = (0., 0.)) padwidth = [(0, 0)] * (self.npar - 1) + padwidth self.coeffsGlobal = np.pad(self.coeffsGlobal, padwidth, "constant", constant_values = (0., 0.)) def setupByInterpolation(self, support:paramList, values:ListAny, deg:int, polybasis : str = "MONOMIAL_GAUSSIAN", directionalWeights : Np1D = None, verbose : bool = True, totalDegree : bool = True, vanderCoeffs : DictAny = {}, fitCoeffs : DictAny = {}): support = checkParameterList(support)[0] self.support = copy(support) if "reorder" in vanderCoeffs.keys(): self.support = self.support[vanderCoeffs["reorder"]] if "nNearestNeighbor" in vanderCoeffs.keys(): self.nNearestNeighbor = vanderCoeffs["nNearestNeighbor"] else: self.nNearestNeighbor = -1 self.npar = support.shape[1] if directionalWeights is None: directionalWeights = np.ones(self.npar) self.directionalWeights = directionalWeights self.polybasis = polybasis if totalDegree: - vander, _, reorder = pvT(support, deg, basis = polybasis, - directionalWeights = self.directionalWeights, - **vanderCoeffs) - vander = vander[reorder] - vander = vander[:, reorder] + vander = pvT(support, deg, basis = polybasis, + directionalWeights = self.directionalWeights, + **vanderCoeffs) else: if not hasattr(deg, "__len__"): deg = [deg] * self.npar vander = pv(support, deg, basis = polybasis, directionalWeights = self.directionalWeights, **vanderCoeffs) outDim = values.shape[1:] values = values.reshape(values.shape[0], -1) values = np.pad(values, ((0, len(vander) - len(values)), (0, 0)), "constant") fitOut = customFit(vander, values, full = True, **fitCoeffs) P = fitOut[0][len(support) :] if verbose: msg = ("Fitting {}+{} samples with degree {} through {}... " "Conditioning of LS system: {:.4e}.").format( len(support), len(vander) - len(support), deg, polyfitname(self.polybasis), fitOut[1][2][0] / fitOut[1][2][-1]) else: msg = None self.coeffsLocal = fitOut[0][: len(support)] if totalDegree: self.coeffsGlobal = degreeTotalToFull(tuple([deg + 1] * self.npar) + outDim, self.npar, P) else: self.coeffsGlobal = P.reshape(tuple([d + 1 for d in deg]) + outDim) return fitOut[1][1] == vander.shape[1], msg diff --git a/rrompy/utilities/poly_fitting/radial_basis/vander.py b/rrompy/utilities/poly_fitting/radial_basis/vander.py index 9738162..a504ac5 100644 --- a/rrompy/utilities/poly_fitting/radial_basis/vander.py +++ b/rrompy/utilities/poly_fitting/radial_basis/vander.py @@ -1,111 +1,107 @@ # 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 .kernel import (radialGaussian, thinPlateSpline, multiQuadric, nearestNeighbor) from rrompy.utilities.poly_fitting.polynomial.vander import (polyvander as pvP, polyvanderTotal as pvTP) from rrompy.utilities.base.types import Np1D, Np2D, Tuple, List, paramList from rrompy.parameter import checkParameterList from rrompy.utilities.exception_manager import RROMPyException, RROMPyAssert __all__ = ['rbvander', 'polyvander', 'polyvanderTotal'] def rbvander(x:paramList, basis:str, reorder : List[int] = None, directionalWeights : Np1D = None, nNearestNeighbor : int = -1) -> Np2D: """Compute radial-basis-Vandermonde matrix.""" x = checkParameterList(x)[0] x_un = x.unique() 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]) elif not hasattr(directionalWeights, "__len__"): directionalWeights = directionalWeights * np.ones(x.shape[1]) RROMPyAssert(len(directionalWeights), x.shape[1], "Number of directional weights") try: radialkernel = {"GAUSSIAN" : radialGaussian, "THINPLATE" : thinPlateSpline, "MULTIQUADRIC" : multiQuadric, "NEARESTNEIGHBOR" : nearestNeighbor}[basis.upper()] except: raise RROMPyException("Radial basis not recognized.") isnearestneighbor = basis.upper() == "NEARESTNEIGHBOR" Van = np.zeros((nx, nx)) for j in range(nx): muDiff = (x.data - x[j]) * directionalWeights r2j = np.sum(np.abs(muDiff) ** 2., axis = 1).reshape(1, -1) if isnearestneighbor: if nNearestNeighbor > 0 and nNearestNeighbor < len(x): cutoffValue = np.partition(r2j, nNearestNeighbor - 1)[0, nNearestNeighbor - 1] r2j /= cutoffValue else: r2j[0, :] = 1. * (nNearestNeighbor == 0) Van[j] = radialkernel(r2j) 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, nNearestNeighbor : int = -1) -> Np2D: """ Compute full 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, nNearestNeighbor = nNearestNeighbor) 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))]]) def polyvanderTotal(x:paramList, deg:int, basis:str, derIdxs : List[List[List[int]]] = None, reorder : List[int] = None, directionalWeights : Np1D = None, scl : Np1D = None, - nNearestNeighbor : int = -1)\ - -> Tuple[Np2D, List[List[int]], List[int]]: + nNearestNeighbor : int = -1) -> Np2D: """ Compute full total degree 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, nNearestNeighbor = nNearestNeighbor) - VanP, derIdxs, ordIdxs = pvTP(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) + VanP = pvTP(x, deg, 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.py b/setup.py index 931fdd3..f4ccbc2 100644 --- a/setup.py +++ b/setup.py @@ -1,52 +1,52 @@ # Copyright (C) 2018 by the RROMPy authors # # This file is part of RROMPy. # # RROMPy is free software: you can redistribute it and/or modify # it under the terms of the GNU Lesser General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # RROMPy is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU Lesser General Public License for more details. # # You should have received a copy of the GNU Lesser General Public License # along with RROMPy. If not, see . # 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.8", + version="1.9", 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 index 576f130..2a96b1f 100644 --- a/tests/hfengines/fracture.py +++ b/tests/hfengines/fracture.py @@ -1,58 +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 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 \ +from membrane_fracture_engine import MembraneFractureEngine as MFE +from membrane_fracture_nodomain_engine import MembraneFractureNoDomainEngine \ 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(mu0[0], uh1), dual = True)[0], solver2.norm(solver2.residual(mu0, uh2), dual = True)[0], atol = 1e-5) assert np.isclose(solver1.norm(uh1 - uh2), 0., atol = 1e-6) diff --git a/rrompy/hfengines/linear_problem/helmholtz_box_scattering_problem_engine.py b/tests/hfengines/helmholtz_box_scattering_problem_engine.py similarity index 85% rename from rrompy/hfengines/linear_problem/helmholtz_box_scattering_problem_engine.py rename to tests/hfengines/helmholtz_box_scattering_problem_engine.py index 916a8c1..7b57010 100644 --- a/rrompy/hfengines/linear_problem/helmholtz_box_scattering_problem_engine.py +++ b/tests/hfengines/helmholtz_box_scattering_problem_engine.py @@ -1,58 +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 . # import numpy as np import fenics as fen -from .scattering_problem_engine import ScatteringProblemEngine - -__all__ = ['HelmholtzBoxScatteringProblemEngine'] +from rrompy.hfengines.linear_problem import ScatteringProblemEngine class HelmholtzBoxScatteringProblemEngine(ScatteringProblemEngine): """ Solver for scattering problem outside a box with parametric wavenumber. - \Delta u - omega^2 * n^2 * u = 0 in \Omega u = 0 on \Gamma_D \partial_nu - i k u = 0 on \Gamma_R with exact solution a transmitted plane wave. """ def __init__(self, R:float, kappa:float, theta:float, n:int, - degree_threshold : int = np.inf, homogeneized : bool = False, - verbosity : int = 10, timestamp : bool = True): + degree_threshold : int = np.inf, verbosity : int = 10, + timestamp : bool = True): super().__init__(mu0 = [kappa], degree_threshold = degree_threshold, - homogeneized = homogeneized, verbosity = verbosity, - timestamp = timestamp) + verbosity = verbosity, timestamp = timestamp) import mshr scatterer = mshr.Polygon([fen.Point(-1, -.5), fen.Point(1, -.5), fen.Point(1, .5), fen.Point(.8, .5), fen.Point(.8, -.3), fen.Point(-.8, -.3), fen.Point(-.8, .5), fen.Point(-1, .5),]) mesh = mshr.generate_mesh(mshr.Circle(fen.Point(0, 0), R) - scatterer, 3 * n) self.V = fen.FunctionSpace(mesh, "P", 1) self.DirichletBoundary = (lambda x, on_boundary: on_boundary and (x[0]**2+x[1]**2)**.5 < .95 * R) self.RobinBoundary = "REST" c, s = np.cos(theta), np.sin(theta) x, y = fen.SpatialCoordinate(mesh)[:] u0R = - fen.cos(kappa * (c * x + s * y)) u0I = - fen.sin(kappa * (c * x + s * y)) self.DirichletDatum = [u0R, u0I] diff --git a/rrompy/hfengines/linear_problem/helmholtz_cavity_scattering_problem_engine.py b/tests/hfengines/helmholtz_cavity_scattering_problem_engine.py similarity index 86% rename from rrompy/hfengines/linear_problem/helmholtz_cavity_scattering_problem_engine.py rename to tests/hfengines/helmholtz_cavity_scattering_problem_engine.py index 48873bd..62e288a 100644 --- a/rrompy/hfengines/linear_problem/helmholtz_cavity_scattering_problem_engine.py +++ b/tests/hfengines/helmholtz_cavity_scattering_problem_engine.py @@ -1,59 +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 . # import numpy as np import fenics as fen -from .scattering_problem_engine import ScatteringProblemEngine - -__all__ = ['HelmholtzCavityScatteringProblemEngine'] +from rrompy.hfengines.linear_problem import ScatteringProblemEngine class HelmholtzCavityScatteringProblemEngine(ScatteringProblemEngine): """ Solver for scattering problem inside a cavity with parametric wavenumber. - \Delta u - omega^2 * n^2 * u = 0 in \Omega u = 0 on \Gamma_D \partial_nu - i k u = 0 on \Gamma_R with exact solution a transmitted plane wave. """ def __init__(self, kappa:float, n:int, gamma : float = 0., signR : int = -1, degree_threshold : int = np.inf, - homogeneized : bool = False, verbosity : int = 10, - timestamp : bool = True): + verbosity : int = 10, timestamp : bool = True): super().__init__(mu0 = [kappa], degree_threshold = degree_threshold, - homogeneized = homogeneized, verbosity = verbosity, - timestamp = timestamp) + verbosity = verbosity, timestamp = timestamp) self.signR = signR 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) self.RobinBoundary = (lambda x, on_boundary: on_boundary and np.isclose(x[1], np.pi)) self.DirichletBoundary = "REST" x, y = fen.SpatialCoordinate(mesh)[:] C = 4. / pi ** 4. bR = C * (2 * (x * (pi - x) + y * (2 * pi - y)) + (kappa * gamma) ** 2. * x * (pi - x) * y * (2 * pi - y)) bI = C * signR * 2 * kappa * (gamma * (pi - 2 * x) * y * (pi - y) + 2 * x * (pi - x) * (pi - y)) wR = fen.cos(kappa * signR * (gamma * x + y)) wI = fen.sin(kappa * signR * (gamma * x + y)) self.forcingTerm = [bR * wR + bI * wI, bI * wR - bR * wI] diff --git a/tests/hfengines/helmholtz_elasticity.py b/tests/hfengines/helmholtz_elasticity.py index 1ced33e..8d1a819 100644 --- a/tests/hfengines/helmholtz_elasticity.py +++ b/tests/hfengines/helmholtz_elasticity.py @@ -1,58 +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 . # import numpy as np from rrompy.hfengines.vector_linear_problem import ( LinearElasticityHelmholtzProblemEngine, LinearElasticityHelmholtzProblemEngineDamped) from rod_3d import rod3Dsolver 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(mu, uh)[0], dual = True), 8.070977e-07, rtol = 1e-1) - assert np.isclose(solver.norm(solver.residual(mu, uh)[0], dual = True), - solver.norm(solver.residual(mu, uh, 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(10, uh)[0], dual = True), 6.7057338e-07, rtol = 1e-1) diff --git a/tests/hfengines/helmholtz_external.py b/tests/hfengines/helmholtz_external.py index c126c9f..9a4493a 100644 --- a/tests/hfengines/helmholtz_external.py +++ b/tests/hfengines/helmholtz_external.py @@ -1,71 +1,70 @@ # Copyright (C) 2018 by the RROMPy authors # # This file is part of RROMPy. # # RROMPy is free software: you can redistribute it and/or modify # it under the terms of the GNU Lesser General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # RROMPy is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU Lesser General Public License for more details. # # You should have received a copy of the GNU Lesser General Public License # along with RROMPy. If not, see . # import pytest import numpy as np -from rrompy.hfengines.linear_problem import ( - HelmholtzCavityScatteringProblemEngine, HelmholtzBoxScatteringProblemEngine) +from helmholtz_box_scattering_problem_engine import \ + HelmholtzBoxScatteringProblemEngine +from helmholtz_cavity_scattering_problem_engine import \ + HelmholtzCavityScatteringProblemEngine 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(mu, uh)[0], dual = True), 4.25056407e-13, rtol = 1e-1) - assert np.isclose(solver.norm(solver.residual(mu, uh)[0], dual = True), - solver.norm(solver.residual(mu, uh, 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.setthAs(solver1.thAs) solver2.setbs(solver1.bs) solver2.setthbs(solver1.thbs) 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(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(mu, uh)[0], dual = True), 9.62989935e-13, rtol = 1e-1) from matplotlib import pyplot as plt plt.close('all') diff --git a/tests/hfengines/helmholtz_internal.py b/tests/hfengines/helmholtz_internal.py index 7414310..0a13eae 100644 --- a/tests/hfengines/helmholtz_internal.py +++ b/tests/hfengines/helmholtz_internal.py @@ -1,97 +1,96 @@ # 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 os, shutil import numpy as np -from rrompy.hfengines.linear_problem import ( - HelmholtzSquareBubbleDomainProblemEngine, HelmholtzSquareBubbleProblemEngine, - HelmholtzSquareTransmissionProblemEngine) +from helmholtz_square_bubble_domain_problem_engine import \ + HelmholtzSquareBubbleDomainProblemEngine +from helmholtz_square_bubble_problem_engine import \ + HelmholtzSquareBubbleProblemEngine +from helmholtz_square_transmission_problem_engine import \ + HelmholtzSquareTransmissionProblemEngine def test_helmholtz_square_io(): solver = HelmholtzSquareBubbleProblemEngine(kappa = 4, theta = 1., n = 20, verbosity = 0) mu = 5 uh = solver.solve(mu)[0] assert np.isclose(solver.norm(uh), 145.0115, rtol = 1e-3) assert np.isclose(solver.norm(solver.residual(mu, uh)[0], dual = True), 1.19934e-11, rtol = 1e-1) - assert np.isclose(solver.norm(solver.residual(mu, uh)[0], dual = True), - solver.norm(solver.residual(mu, uh, 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 = 20, verbosity = 0) mu = 5. uh = solver.solve(mu)[0] assert np.isclose(solver.norm(uh), 138.6609, rtol = 1e-2) assert np.isclose(solver.norm(solver.residual(mu, uh)[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") 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), 10.07843, rtol = 1e-2) assert np.isclose(solver.norm(solver.residual(mu, uh)[0], dual = True), 6.14454989e-13, rtol = 1e-1) diff --git a/rrompy/hfengines/linear_problem/helmholtz_square_bubble_domain_problem_engine.py b/tests/hfengines/helmholtz_square_bubble_domain_problem_engine.py similarity index 89% rename from rrompy/hfengines/linear_problem/helmholtz_square_bubble_domain_problem_engine.py rename to tests/hfengines/helmholtz_square_bubble_domain_problem_engine.py index a0c2441..0ac331d 100644 --- a/rrompy/hfengines/linear_problem/helmholtz_square_bubble_domain_problem_engine.py +++ b/tests/hfengines/helmholtz_square_bubble_domain_problem_engine.py @@ -1,133 +1,131 @@ # Copyright (C) 2018 by the RROMPy authors # # This file is part of RROMPy. # # RROMPy is free software: you can redistribute it and/or modify # it under the terms of the GNU Lesser General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # RROMPy is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU Lesser General Public License for more details. # # You should have received a copy of the GNU Lesser General Public License # along with RROMPy. If not, see . # import numpy as np from numpy.polynomial.polynomial import polyfit as fit import fenics as fen from rrompy.utilities.base.types import paramVal from rrompy.solver.fenics import fenZERO -from .helmholtz_problem_engine import HelmholtzProblemEngine +from rrompy.hfengines.linear_problem import HelmholtzProblemEngine from rrompy.utilities.base import verbosityManager as vbMng 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, homogeneized : bool = False, - verbosity : int = 10, timestamp : bool = True): + degree_threshold : int = np.inf, verbosity : int = 10, + timestamp : bool = True): super().__init__(mu0 = mu0, degree_threshold = degree_threshold, - homogeneized = homogeneized, verbosity = verbosity, - timestamp = timestamp) + verbosity = verbosity, timestamp = timestamp) self._affinePoly = False self.nAs, self.nbs = 2, 15 self.kappa = kappa self.theta = theta 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 buildA(self): """Build terms of operator of linear system.""" if self.As[0] is None: vbMng(self, "INIT", "Assembling operator term A0.", 20) DirichletBC0 = fen.DirichletBC(self.V, fenZERO, self.DirichletBoundary) a0Re = fen.dot(self.u.dx(1), self.v.dx(1)) * fen.dx self.As[0] = fenics2Sparse(a0Re, {}, DirichletBC0, 1) self.thAs[0] = self.getMonomialSingleWeight([0]) vbMng(self, "DEL", "Done assembling operator term.", 20) if self.As[1] is None: vbMng(self, "INIT", "Assembling operator term A2.", 20) 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 self.As[1] = (fenics2Sparse(a2Re, parsRe, DirichletBC0, 0) + 1.j * fenics2Sparse(a2Im, parsIm, DirichletBC0, 0)) self.thAs[1] = self.getMonomialSingleWeight([2]) vbMng(self, "DEL", "Done assembling operator term.", 20) def buildb(self): """Build terms of operator of linear system.""" if self.thbs[0] is None: - self.thbs = (self.getMonomialWeights(self.nbs) - + [None] * (self.homogeneized * self.nAs)) + self.thbs = self.getMonomialWeights(self.nbs) bDEIMCoeffs = None for j in range(self.nbs): if self.bs[j] is None: vbMng(self, "INIT", "Assembling forcing term b{}.".format(j), 20) if bDEIMCoeffs is None: - bDEIM = np.empty((self.nbs, self.spacedim()), - dtype = np.complex) muDEIM = np.linspace(.5, 4., self.nbs) for jj, muD in enumerate(muDEIM): pi = np.pi c, s = np.cos(self.theta), np.sin(self.theta) x, y = fen.SpatialCoordinate(self.V.mesh())[:] muR, muI = np.real(muD), np.imag(muD) mu2R, mu2I = np.real(muD ** 2.), np.imag(muD ** 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)) fRe, fIm = bR * wR + bI * wI, bI * wR - bR * wI fRe = mu2R * fRe - mu2I * fIm + fenZERO fIm = mu2R * fIm + mu2I * fRe + fenZERO parsRe = self.iterReduceQuadratureDegree(zip([fRe], ["forcingTerm{}Real".format(jj)])) parsIm = self.iterReduceQuadratureDegree(zip([fIm], ["forcingTerm{}Imag".format(jj)])) LR = fen.dot(fRe, self.v) * fen.dx LI = fen.dot(fIm, self.v) * fen.dx DBC0 = fen.DirichletBC(self.V, fenZERO, self.DirichletBoundary) - bDEIM[jj] = (fenics2Vector(LR, parsRe, DBC0, 1) + bjj = (fenics2Vector(LR, parsRe, DBC0, 1) + 1.j * fenics2Vector(LI, parsIm, DBC0, 1)) + if jj == 0: + bDEIM = np.empty((self.nbs, len(bjj)), + dtype = np.complex) + bDEIM[jj] = bjj bDEIMCoeffs = fit(muDEIM, bDEIM, self.nbs - 1) self.bs[j] = bDEIMCoeffs[j] vbMng(self, "DEL", "Done assembling forcing term.", 20) - self.setbHomogeneized() + diff --git a/rrompy/hfengines/linear_problem/helmholtz_square_bubble_problem_engine.py b/tests/hfengines/helmholtz_square_bubble_problem_engine.py similarity index 82% rename from rrompy/hfengines/linear_problem/helmholtz_square_bubble_problem_engine.py rename to tests/hfengines/helmholtz_square_bubble_problem_engine.py index 35df096..f0a6fdd 100644 --- a/rrompy/hfengines/linear_problem/helmholtz_square_bubble_problem_engine.py +++ b/tests/hfengines/helmholtz_square_bubble_problem_engine.py @@ -1,52 +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 import fenics as fen -from .helmholtz_problem_engine import HelmholtzProblemEngine - -__all__ = ['HelmholtzSquareBubbleProblemEngine'] +from rrompy.hfengines.linear_problem import HelmholtzProblemEngine class HelmholtzSquareBubbleProblemEngine(HelmholtzProblemEngine): """ Solver for square bubble Helmholtz problems with parametric wavenumber. - \Delta u - omega^2 * u = f in \Omega u = 0 on \Gamma_D with exact solution square bubble times plane wave. """ def __init__(self, kappa:float, theta:float, n:int, - degree_threshold : int = np.inf, homogeneized : bool = False, - verbosity : int = 10, timestamp : bool = True): + degree_threshold : int = np.inf, verbosity : int = 10, + timestamp : bool = True): super().__init__(mu0 = [kappa], degree_threshold = degree_threshold, - homogeneized = homogeneized, verbosity = verbosity, - timestamp = timestamp) + verbosity = verbosity, timestamp = timestamp) 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. bR = C * 2 * (x * (pi - x) + y * (pi - y)) bI = C * 2 * kappa * (c * (pi - 2 * x) * y * (pi - y) + s * x * (pi - x) * (pi - 2 * y)) wR = fen.cos(kappa * (c * x + s * y)) wI = fen.sin(kappa * (c * x + s * y)) self.forcingTerm = [bR * wR + bI * wI, bI * wR - bR * wI] diff --git a/rrompy/hfengines/linear_problem/helmholtz_square_transmission_problem_engine.py b/tests/hfengines/helmholtz_square_transmission_problem_engine.py similarity index 88% rename from rrompy/hfengines/linear_problem/helmholtz_square_transmission_problem_engine.py rename to tests/hfengines/helmholtz_square_transmission_problem_engine.py index 6ea8c5d..5460ef0 100644 --- a/rrompy/hfengines/linear_problem/helmholtz_square_transmission_problem_engine.py +++ b/tests/hfengines/helmholtz_square_transmission_problem_engine.py @@ -1,73 +1,70 @@ # Copyright (C) 2018 by the RROMPy authors # # This file is part of RROMPy. # # RROMPy is free software: you can redistribute it and/or modify # it under the terms of the GNU Lesser General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # RROMPy is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU Lesser General Public License for more details. # # You should have received a copy of the GNU Lesser General Public License # along with RROMPy. If not, see . # import numpy as np import fenics as fen import ufl -from .helmholtz_problem_engine import HelmholtzProblemEngine - -__all__ = ['HelmholtzSquareTransmissionProblemEngine'] +from rrompy.hfengines.linear_problem import HelmholtzProblemEngine class HelmholtzSquareTransmissionProblemEngine(HelmholtzProblemEngine): """ Solver for square transmission Helmholtz problems with parametric wavenumber. - \Delta u - omega^2 * n^2 * u = 0 in \Omega u = 0 on \Gamma_D with exact solution a transmitted plane wave. """ def __init__(self, nT:float, nB:float, kappa:float, theta:float, n:int, - degree_threshold : int = np.inf, homogeneized : bool = False, - verbosity : int = 10, timestamp : bool = True): + degree_threshold : int = np.inf, verbosity : int = 10, + timestamp : bool = True): super().__init__(mu0 = [kappa], degree_threshold = degree_threshold, - homogeneized = homogeneized, verbosity = verbosity, - timestamp = timestamp) + verbosity = verbosity, timestamp = timestamp) mesh = fen.RectangleMesh(fen.Point(-np.pi/2, -np.pi/2), fen.Point(np.pi/2, np.pi/2), 3 * n, 3 * n) self.V = fen.FunctionSpace(mesh, "P", 1) dx, dy = np.cos(theta), np.sin(theta) Kx = kappa * nB * dx Ky = kappa * (nT**2. - (nB * dx)**2. + 0.j)**.5 T = 2 * kappa * nB * dy / (Ky + kappa * nB * dy) x, y = fen.SpatialCoordinate(mesh)[:] TR, TI = np.real(T), np.imag(T) if np.isclose(np.imag(Ky), 0.): u0RT = (TR * fen.cos(Kx * x + np.real(Ky) * y) - TI * fen.sin(Kx * x + np.real(Ky) * y)) u0IT = (TR * fen.sin(Kx * x + np.real(Ky) * y) + TI * fen.cos(Kx * x + np.real(Ky) * y)) else: u0RT = fen.exp(- np.imag(Ky) * y) * (TR * fen.cos(Kx * x) - TI * fen.sin(Kx * x)) u0IT = fen.exp(- np.imag(Ky) * y) * (TR * fen.sin(Kx * x) + TI * fen.cos(Kx * x)) u0RB = (fen.cos(kappa * nB * (dx * x + dy * y)) + (TR - 1) * fen.cos(kappa * nB * (dx*x - dy*y)) - TI * fen.sin(kappa * nB * (dx*x - dy*y))) u0IB = (fen.sin(kappa * nB * (dx * x + dy * y)) + (TR - 1) * fen.sin(kappa * nB * (dx*x - dy*y)) + TI * fen.cos(kappa * nB * (dx*x - dy*y))) u0R = ufl.conditional(ufl.ge(y, 0.), u0RT, u0RB) u0I = ufl.conditional(ufl.ge(y, 0.), u0IT, u0IB) self.refractionIndex = ufl.conditional(ufl.ge(y, 0.), fen.Constant(nT), fen.Constant(nB)) self.DirichletDatum = [u0R, u0I] diff --git a/tests/hfengines/laplace.py b/tests/hfengines/laplace.py index 0a3bed5..f6f9fef 100644 --- a/tests/hfengines/laplace.py +++ b/tests/hfengines/laplace.py @@ -1,42 +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 . # import numpy as np -from rrompy.hfengines.linear_problem import LaplaceDiskGaussian -from rrompy.hfengines.linear_problem.bidimensional import LaplaceDiskGaussian2 +from laplace_disk_gaussian import LaplaceDiskGaussian, 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(mu, uh)[0], dual = True), 4.66728e-10, atol = 1e-7) - assert np.isclose(solver.norm(solver.residual(mu, uh)[0], dual = True), - solver.norm(solver.residual(mu, uh, 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(mu, uh)[0], dual = True), 2.40043363e-08, atol = 1e-7) diff --git a/rrompy/hfengines/linear_problem/bidimensional/laplace_disk_gaussian_2.py b/tests/hfengines/laplace_disk_gaussian.py similarity index 53% rename from rrompy/hfengines/linear_problem/bidimensional/laplace_disk_gaussian_2.py rename to tests/hfengines/laplace_disk_gaussian.py index d3b49c2..bb39579 100644 --- a/rrompy/hfengines/linear_problem/bidimensional/laplace_disk_gaussian_2.py +++ b/tests/hfengines/laplace_disk_gaussian.py @@ -1,98 +1,156 @@ # 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 numpy.polynomial.polynomial import polyfit as fit import fenics as fen from rrompy.utilities.base.types import paramVal -from rrompy.hfengines.linear_problem.laplace_disk_gaussian import ( - LaplaceDiskGaussian) +from rrompy.hfengines.linear_problem import LaplaceBaseProblemEngine from rrompy.solver.fenics import fenZERO from rrompy.utilities.base import verbosityManager as vbMng from rrompy.utilities.poly_fitting.polynomial import ( PolynomialInterpolator as PI) from rrompy.solver.fenics import fenics2Vector -__all__ = ['LaplaceDiskGaussian2'] +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 = 19 + import mshr + mesh = mshr.generate_mesh(mshr.Circle(fen.Point(0., 0.), 5.), 3 * n) + self.V = fen.FunctionSpace(mesh, "P", 1) + + def buildb(self): + """Build terms of operator of linear system.""" + if self.thbs[0] is None: + self.thbs = self.getMonomialWeights(self.nbs) + bDEIMCoeffs = None + for j in range(self.nbs): + if self.bs[j] is None: + vbMng(self, "INIT", "Assembling forcing term b{}.".format(j), + 20) + if bDEIMCoeffs is None: + muDEIM = 3. * np.linspace(0., 1., self.nbs // 2 + 1) ** 2. + muDEIM = np.concatenate((-muDEIM[:0:-1], muDEIM)) + for jj, muD in enumerate(muDEIM): + x, y = fen.SpatialCoordinate(self.V.mesh())[:] + C = np.exp(-.5 * muD ** 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(muD), np.imag(muD) + f1R = fen.exp(muR * x) * fen.cos(muI * x) + f1I = fen.exp(muR * x) * fen.sin(muI * x) + fRe = f0 * (CR * f1R - CI * f1I) + fenZERO + fIm = f0 * (CR * f1I + CI * f1R) + fenZERO + parsRe = self.iterReduceQuadratureDegree(zip([fRe], + ["forcingTerm{}Real".format(jj)])) + parsIm = self.iterReduceQuadratureDegree(zip([fIm], + ["forcingTerm{}Imag".format(jj)])) + LR = fen.dot(fRe, self.v) * fen.dx + LI = fen.dot(fIm, self.v) * fen.dx + DBC0 = fen.DirichletBC(self.V, fenZERO, + self.DirichletBoundary) + bjj = (fenics2Vector(LR, parsRe, DBC0, 1) + + 1.j * fenics2Vector(LI, parsIm, DBC0, 1)) + if jj == 0: + bDEIM = np.empty((self.nbs, len(bjj)), + dtype = np.complex) + bDEIM[jj] = bjj + bDEIMCoeffs = (fit(muDEIM / 3., bDEIM, self.nbs - 1).T + * np.power(3., - np.arange(self.nbs))).T + self.bs[j] = bDEIMCoeffs[j] + vbMng(self, "DEL", "Done assembling forcing term.", 20) 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, homogeneized : bool = False, - verbosity : int = 10, timestamp : bool = True): + degree_threshold : int = np.inf, verbosity : int = 10, + timestamp : bool = True): super().__init__(n = n, mu0 = mu0, degree_threshold = degree_threshold, - homogeneized = homogeneized, verbosity = verbosity, - timestamp = timestamp) + verbosity = verbosity, timestamp = timestamp) self.nbs = 16 self.npar = 2 def buildb(self): """Build terms of operator of linear system.""" if self.thbs[0] is None: - self.thbs = [None] * (self.nbs + self.homogeneized * self.nAs) + self.thbs = [None] * self.nbs bDEIMCoeffs = None for j in range(self.nbs): j1, j2 = j % int(self.nbs ** .5), j // int(self.nbs ** .5) if self.bs[j] is None: vbMng(self, "INIT", "Assembling forcing term b{}.".format(j), 20) if bDEIMCoeffs is None: - bDEIM = np.empty((self.nbs, self.spacedim()), - dtype = np.complex) muD1 = np.linspace(-2., 2., int(self.nbs ** .5)) muDEIM = np.empty((self.nbs, 2)) muDEIM[:, 0] = np.repeat(muD1, int(self.nbs ** .5)) muDEIM[:, 1] = np.tile(muD1, int(self.nbs ** .5)) for jj, muD in enumerate(muDEIM): x, y = fen.SpatialCoordinate(self.V.mesh())[:] C = np.exp(-.5 * (muD[0] ** 2. + muD[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(muD[0]), np.imag(muD[0]) muyR, muyI = np.real(muD[1]), np.imag(muD[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)) fRe = f0 * (CR * f1R - CI * f1I) + fenZERO fIm = f0 * (CR * f1I + CI * f1R) + fenZERO parsRe = self.iterReduceQuadratureDegree(zip([fRe], ["forcingTerm{}Real".format(jj)])) parsIm = self.iterReduceQuadratureDegree(zip([fIm], ["forcingTerm{}Imag".format(jj)])) LR = fen.dot(fRe, self.v) * fen.dx LI = fen.dot(fIm, self.v) * fen.dx DBC0 = fen.DirichletBC(self.V, fenZERO, self.DirichletBoundary) - bDEIM[jj] = (fenics2Vector(LR, parsRe, DBC0, 1) + bjj = (fenics2Vector(LR, parsRe, DBC0, 1) + 1.j * fenics2Vector(LI, parsIm, DBC0, 1)) + if jj == 0: + bDEIM = np.empty((self.nbs, len(bjj)), + dtype = np.complex) + bDEIM[jj] = bjj p = PI() wellCond, _ = p.setupByInterpolation(muDEIM, bDEIM, int(self.nbs ** .5) - 1, "MONOMIAL", False, False) bDEIMCoeffs = p.coeffs self.bs[j1 + int(self.nbs ** .5) * j2] = bDEIMCoeffs[j1, j2] self.thbs[j1 + int(self.nbs ** .5) * j2] = ( self.getMonomialSingleWeight([j1, j2])) vbMng(self, "DEL", "Done assembling forcing term.", 20) - self.setbHomogeneized() + diff --git a/tests/hfengines/linear_elasticity.py b/tests/hfengines/linear_elasticity.py index c4b294d..85578ef 100644 --- a/tests/hfengines/linear_elasticity.py +++ b/tests/hfengines/linear_elasticity.py @@ -1,41 +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 . # import numpy as np -from rrompy.hfengines.vector_linear_problem import ( - LinearElasticityBeamPoissonRatio) +from linear_elasticity_beam_poisson_ratio_engine import \ + LinearElasticityBeamPoissonRatioEngine from rod_3d import rod3Dsolver def test_elastic_beam(): - solver = LinearElasticityBeamPoissonRatio(n = 10, rho_ = 1e3, g = 3, + solver = LinearElasticityBeamPoissonRatioEngine(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), 9.33957e-08, rtol = 1e-1) assert np.isclose(solver.norm(solver.residual(mu, uh)[0], dual = True), 8.4545952e-13, rtol = 1e-1) - assert np.isclose(solver.norm(solver.residual(mu, uh)[0], dual = True), - solver.norm(solver.residual(mu, uh, 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], dual = True), 1.2210129e-07, rtol = 1e-1) diff --git a/rrompy/hfengines/vector_linear_problem/linear_elasticity_beam_poisson_ratio.py b/tests/hfengines/linear_elasticity_beam_poisson_ratio_engine.py similarity index 93% rename from rrompy/hfengines/vector_linear_problem/linear_elasticity_beam_poisson_ratio.py rename to tests/hfengines/linear_elasticity_beam_poisson_ratio_engine.py index 2ab683b..1a6ed59 100644 --- a/rrompy/hfengines/vector_linear_problem/linear_elasticity_beam_poisson_ratio.py +++ b/tests/hfengines/linear_elasticity_beam_poisson_ratio_engine.py @@ -1,117 +1,115 @@ # Copyright (C) 2018 by the RROMPy authors # # This file is part of RROMPy. # # RROMPy is free software: you can redistribute it and/or modify # it under the terms of the GNU Lesser General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # RROMPy is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU Lesser General Public License for more details. # # You should have received a copy of the GNU Lesser General Public License # along with RROMPy. If not, see . # import numpy as np import fenics as fen -from .linear_elasticity_problem_engine import LinearElasticityProblemEngine +from rrompy.hfengines.vector_linear_problem import \ + LinearElasticityProblemEngine from rrompy.solver.fenics import fenZERO, fenZEROS from rrompy.utilities.base import verbosityManager as vbMng from rrompy.solver.fenics import fenics2Sparse, fenics2Vector -__all__ = ['LinearElasticityBeamPoissonRatio'] - -class LinearElasticityBeamPoissonRatio(LinearElasticityProblemEngine): +class LinearElasticityBeamPoissonRatioEngine(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, - homogeneized = False, verbosity = verbosity, - timestamp = timestamp) + verbosity = verbosity, timestamp = timestamp) self._affinePoly = False self.nAs, self.nbs = 3, 2 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 buildA(self): """Build terms of operator of linear system.""" if self.As[0] is None: vbMng(self, "INIT", "Assembling operator term A0.", 20) DirichletBC0 = fen.DirichletBC(self.V, fenZEROS(2), self.DirichletBoundary) a0Re = fenZERO * fen.inner(self.u, self.v) * fen.dx self.As[0] = fenics2Sparse(a0Re, {}, DirichletBC0, 1) self.thAs[0] = self.getMonomialSingleWeight([0]) vbMng(self, "DEL", "Done assembling operator term.", 20) if self.As[1] is None: vbMng(self, "INIT", "Assembling operator term A1.", 20) DirichletBC0 = fen.DirichletBC(self.V, fenZEROS(2), self.DirichletBoundary) epsilon = lambda u: .5 * (fen.grad(u) + fen.nabla_grad(u)) a1Re = self.E_ * fen.inner(epsilon(self.u), epsilon(self.v)) * fen.dx self.As[1] = fenics2Sparse(a1Re, {}, DirichletBC0, 0) self.thAs[1] = [('x', '()', 0, '*', -2., '+', 1.), (-2.0,), None] epsilon = lambda u: .5 * (fen.grad(u) + fen.nabla_grad(u)) vbMng(self, "DEL", "Done assembling operator term.", 20) if self.As[2] is None: vbMng(self, "INIT", "Assembling operator term A2.", 20) DirichletBC0 = fen.DirichletBC(self.V, fenZEROS(2), self.DirichletBoundary) a2Re = self.E_ * fen.div(self.u) * fen.div(self.v) * fen.dx self.As[2] = fenics2Sparse(a2Re, {}, DirichletBC0, 0) self.thAs[2] = self.getMonomialSingleWeight([1]) vbMng(self, "DEL", "Done assembling operator term.", 20) def buildb(self): """Build terms of operator of linear system.""" if self.thbs[0] is None: - self.thbs = [None] * (self.nbs + self.homogeneized * self.nAs) + self.thbs = [None] * self.nbs if self.bs[0] is None: vbMng(self, "INIT", "Assembling forcing term b0.", 20) L0Re = fen.inner(fenZEROS(2), self.v) * fen.dx DBCR = fen.DirichletBC(self.V, self.DirichletDatum[0], self.DirichletBoundary) DBCI = fen.DirichletBC(self.V, self.DirichletDatum[1], self.DirichletBoundary) self.bs[0] = (fenics2Vector(L0Re, {}, DBCR, 1) + 1.j * fenics2Vector(L0Re, {}, DBCI, 1)) self.thbs[0] = self.getMonomialSingleWeight([0]) if self.bs[1] is None: vbMng(self, "INIT", "Assembling forcing term b1.", 20) 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)) self.thbs[1] = [('x', '()', 0, '**', 2., '*', -2., '-', ('x', '()', 0), '+', 1.), ('x', '()', 0, '*', -4., '-', 1.), (-2.0,), None] vbMng(self, "DEL", "Done assembling forcing term.", 20) - self.setbHomogeneized() + diff --git a/tests/hfengines/matrix.py b/tests/hfengines/matrix.py index 572b7d0..9b376dd 100644 --- a/tests/hfengines/matrix.py +++ b/tests/hfengines/matrix.py @@ -1,58 +1,58 @@ # Copyright (C) 2018 by the RROMPy authors # # This file is part of RROMPy. # # RROMPy is free software: you can redistribute it and/or modify # it under the terms of the GNU Lesser General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # RROMPy is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU Lesser General Public License for more details. # # You should have received a copy of the GNU Lesser General Public License # along with RROMPy. If not, see . # import numpy as np import scipy.sparse as sp -from rrompy.hfengines.base import MatrixEngineBase +from rrompy.hfengines.base import LinearAffineEngine, NumpyEngineBase + +class MatrixEngineBase(LinearAffineEngine, NumpyEngineBase): + pass def test_deterministic(): solver = MatrixEngineBase(verbosity = 0) N = 100 solver._affinePoly = True solver.npar = 1 solver.nAs, solver.nbs = 2, 1 - solver.As = [sp.spdiags([np.arange(1, 1 + N)], [0], N, N), - - sp.eye(N)] - solver.bs = [np.exp(1.j * np.linspace(0, -np.pi, N))] - solver.thAs = solver.getMonomialWeights(solver.nAs) - solver.thbs = solver.getMonomialWeights(solver.nbs) + solver.setAs([sp.spdiags([np.arange(1, 1 + N)], [0], N, N), + - sp.eye(N)]) + solver.setbs([np.exp(1.j * np.linspace(0, -np.pi, N))]) + solver.setthAs(solver.getMonomialWeights(solver.nAs)) + solver.setthbs(solver.getMonomialWeights(solver.nbs)) mu = 10. + .5j uh = solver.solve(mu)[0] assert np.isclose(solver.norm(solver.residual(mu, uh)[0], dual = True), 1.088e-15, rtol = 1e-1) - assert np.isclose(solver.norm(solver.residual(mu, uh)[0], dual = True), - solver.norm(solver.residual(mu, uh, duality = False)[0], - dual = True, duality = False), rtol = 1e-1) def test_random(): solver = MatrixEngineBase(verbosity = 0) N = 100 solver.setSolver("SOLVE") solver.npar = 1 solver.nAs, solver.nbs = 2, 1 np.random.seed(420) - 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.bs = [np.random.randn(N) + 1.j * np.random.randn(N)] - solver.thAs = solver.getMonomialWeights(solver.nAs) - solver.thbs = solver.getMonomialWeights(solver.nbs) + fftB = np.fft.fft(np.eye(N)) * N ** -.5 + solver.setAs([fftB.dot(np.multiply(np.arange(1, 1 + N), fftB.conj()).T), + - np.eye(N)]) + solver.setbs([np.random.randn(N) + 1.j * np.random.randn(N)]) + solver.setthAs(solver.getMonomialWeights(solver.nAs)) + solver.setthbs(solver.getMonomialWeights(solver.nbs)) mu = 1. + .5j uh = solver.solve(mu)[0] assert np.isclose(solver.norm(solver.residual(mu, uh)[0], dual = True), 7.18658e-14, rtol = 1e-1) diff --git a/rrompy/hfengines/linear_problem/bidimensional/membrane_fracture_engine.py b/tests/hfengines/membrane_fracture_engine.py similarity index 94% rename from rrompy/hfengines/linear_problem/bidimensional/membrane_fracture_engine.py rename to tests/hfengines/membrane_fracture_engine.py index ae8bac0..59e03f3 100644 --- a/rrompy/hfengines/linear_problem/bidimensional/membrane_fracture_engine.py +++ b/tests/hfengines/membrane_fracture_engine.py @@ -1,146 +1,142 @@ # Copyright (C) 2018 by the RROMPy authors # # This file is part of RROMPy. # # RROMPy is free software: you can redistribute it and/or modify # it under the terms of the GNU Lesser General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # RROMPy is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU Lesser General Public License for more details. # # You should have received a copy of the GNU Lesser General Public License # along with RROMPy. If not, see . # import numpy as np import fenics as fen import mshr, ufl from rrompy.utilities.base.types import paramVal from rrompy.solver.fenics import fenZERO, fenONE -from rrompy.hfengines.linear_problem.helmholtz_problem_engine import ( - HelmholtzProblemEngine) +from rrompy.hfengines.linear_problem import HelmholtzProblemEngine from rrompy.utilities.base import verbosityManager as vbMng 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, homogeneized : bool = False, - verbosity : int = 10, timestamp : bool = True): + degree_threshold : int = np.inf, verbosity : int = 10, + timestamp : bool = True): super().__init__(mu0 = mu0, degree_threshold = degree_threshold, - homogeneized = homogeneized, verbosity = verbosity, - timestamp = timestamp) + verbosity = verbosity, timestamp = timestamp) self._affinePoly = False self.nAs = 5 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 buildA(self): """Build terms of operator of linear system.""" if self.As[0] is None: vbMng(self, "INIT", "Assembling operator term A0.", 20) DirichletBC0 = fen.DirichletBC(self.V, fenZERO, self.DirichletBoundary) a0Re = fenZERO * fen.dot(self.u, self.v) * fen.dx self.As[0] = fenics2Sparse(a0Re, {}, DirichletBC0, 1) self.thAs[0] = self.getMonomialSingleWeight([0, 0]) vbMng(self, "DEL", "Done assembling operator term.", 20) if self.As[1] is None: vbMng(self, "INIT", "Assembling operator term A1.", 20) DirichletBC0 = fen.DirichletBC(self.V, fenZERO, self.DirichletBoundary) a1Re = (self.H ** 3 / 4. * self._aboveIndicator * fen.dot(self.u.dx(1), self.v.dx(1)) * fen.dx) self.As[1] = fenics2Sparse(a1Re, {}, DirichletBC0, 0) self.thAs[1] = [('x', '()', 1, '*', -2., '+', self.H), (0.,), (-2.,), None] vbMng(self, "DEL", "Done assembling operator term.", 20) if self.As[2] is None: vbMng(self, "INIT", "Assembling operator term A2.", 20) DirichletBC0 = fen.DirichletBC(self.V, fenZERO, self.DirichletBoundary) a2Re = self.H ** 2 * fen.dot(self.u.dx(0), self.v.dx(0)) * fen.dx self.As[2] = fenics2Sparse(a2Re, {}, DirichletBC0, 0) self.thAs[2] = [('x', '()', 1, '*', -1., '+', self.H, '*', ('x', '()', 1), '**', 2.), (0.,), ('x', '()', 1, '**', 2., '*', 4., '-', ('x', '()', 1, '*', 6. * self.H), '+', 2. * self.H ** 2., '*', ('x', '()', 1)), (0.,), (0.,), ('x', '()', 1, '**', 2., '*', 6., '-', ('x', '()', 1, '*', 6. * self.H), '+', self.H ** 2.), (0.,), (0.,), (0.,), ('x', '()', 1, '*', 4., '-', 2. * self.H), (0.,), (0.,), (0.,), (0.,), (1.,), None] vbMng(self, "DEL", "Done assembling operator term.", 20) if self.As[3] is None: vbMng(self, "INIT", "Assembling operator term A3.", 20) 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"])) a3Re = - n2Re * fen.dot(self.u, self.v) * fen.dx a3Im = - n2Im * fen.dot(self.u, self.v) * fen.dx self.As[3] = (fenics2Sparse(a3Re, parsRe, DirichletBC0, 0) + 1.j * fenics2Sparse(a3Im, parsIm, DirichletBC0, 0)) self.thAs[3] = [('x', '()', 1, '*', -1., '+', self.H, '*', ('x', '()', 1), '**', 2., '*', ('x', '()', 0)), ('x', '()', 1, '*', -1., '+', self.H, '*', ('x', '()', 1), '**', 2.), (2. * self.H ** 2., '-', ('x', '()', 1, '*', 6. * self.H), '+', ('x', '()', 1, '**', 2., '*', 4.), '*', ('x', '()', 1), '*', ('x', '()', 0)), (0.,), ('x', '()', 1, '**', 2., '*', 4., '-', ('x', '()', 1, '*', 6. * self.H), '+', 2. * self.H ** 2., '*', ('x', '()', 1)), ('x', '()', 1, '**', 2., '*', 6., '-', ('x', '()', 1, '*', 6. * self.H), '+', self.H ** 2., '*', ('x', '()', 0)), (0.,), (0.,), ('x', '()', 1, '**', 2., '*', 6., '-', ('x', '()', 1, '*', 6. * self.H), '+', self.H ** 2.), ('x', '()', 1, '*', 4., '-', 2. * self.H, '*', ('x', '()', 0)), (0.,), (0.,), (0.,), ('x', '()', 1, '*', 4., '-', 2. * self.H), ('x', '()', 0), (0.,), (0.,), (0.,), (0.,), (1.,), None] vbMng(self, "DEL", "Done assembling operator term.", 20) if self.As[4] is None: vbMng(self, "INIT", "Assembling operator term A4.", 20) DirichletBC0 = fen.DirichletBC(self.V, fenZERO, self.DirichletBoundary) a4Re = .25 * self.H ** 2 * fen.dot(self.u.dx(1), self.v.dx(1)) * fen.dx self.As[4] = fenics2Sparse(a4Re, {}, DirichletBC0, 0) self.thAs[4] = self.getMonomialSingleWeight([0, 2]) vbMng(self, "DEL", "Done assembling operator term.", 20) diff --git a/rrompy/hfengines/linear_problem/membrane_fracture_engine_nodomain.py b/tests/hfengines/membrane_fracture_nodomain_engine.py similarity index 91% rename from rrompy/hfengines/linear_problem/membrane_fracture_engine_nodomain.py rename to tests/hfengines/membrane_fracture_nodomain_engine.py index 60cd7ea..c471691 100644 --- a/rrompy/hfengines/linear_problem/membrane_fracture_engine_nodomain.py +++ b/tests/hfengines/membrane_fracture_nodomain_engine.py @@ -1,92 +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 . # import numpy as np import fenics as fen import mshr, ufl from rrompy.utilities.base.types import paramVal from rrompy.solver.fenics import fenZERO, fenONE from rrompy.hfengines.linear_problem.helmholtz_problem_engine import ( HelmholtzProblemEngine) from rrompy.utilities.base import verbosityManager as vbMng from rrompy.solver.fenics import fenics2Sparse -__all__ = ['MembraneFractureEngineNoDomain'] - -class MembraneFractureEngineNoDomain(HelmholtzProblemEngine): +class MembraneFractureNoDomainEngine(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, homogeneized : bool = False, - verbosity : int = 10, timestamp : bool = True): + degree_threshold : int = np.inf, verbosity : int = 10, + timestamp : bool = True): super().__init__(mu0 = mu0[0], degree_threshold = degree_threshold, - homogeneized = homogeneized, verbosity = verbosity, - timestamp = timestamp) + verbosity = verbosity, timestamp = timestamp) self._affinePoly = True 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) 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 buildA(self): """Build terms of operator of linear system.""" if self.thAs[0] is None: self.thAs = self.getMonomialWeights(self.nAs) if self.As[0] is None: self.autoSetDS() vbMng(self, "INIT", "Assembling operator term A0.", 20) DirichletBC0 = fen.DirichletBC(self.V, fenZERO, self.DirichletBoundary) 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) vbMng(self, "DEL", "Done assembling operator term.", 20) if self.As[1] is None: vbMng(self, "INIT", "Assembling operator term A1.", 20) 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 self.As[1] = (fenics2Sparse(a1Re, parsRe, DirichletBC0, 0) + 1.j * fenics2Sparse(a1Im, parsIm, DirichletBC0, 0)) vbMng(self, "DEL", "Done assembling operator term.", 20) diff --git a/tests/reduction_methods_1D/matrix_fft.py b/tests/reduction_methods_1D/matrix_fft.py index e72b612..9370e37 100644 --- a/tests/reduction_methods_1D/matrix_fft.py +++ b/tests/reduction_methods_1D/matrix_fft.py @@ -1,38 +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 . # import numpy as np -from rrompy.hfengines.base import MatrixEngineBase +from rrompy.hfengines.base import LinearAffineEngine, NumpyEngineBase -class matrixFFT(MatrixEngineBase): +class matrixFFT(LinearAffineEngine, NumpyEngineBase): def __init__(self): super().__init__(verbosity = 0) self._affinePoly = True N = 100 np.random.seed(420) self.setSolver("SOLVE") self.npar = 1 self.nAs, self.nbs = 2, 1 self.mu0 = 0. fftB = np.fft.fft(np.eye(N)) * N**-.5 - self.As = [fftB.dot(np.multiply(np.arange(1, 1 + N), fftB.conj()).T), - - np.eye(N)] - self.bs = [np.random.randn(N) + 1.j * np.random.randn(N)] - self.thAs = self.getMonomialWeights(self.nAs) - self.thbs = self.getMonomialWeights(self.nbs) + self.setAs([fftB.dot(np.multiply(np.arange(1, 1 + N), fftB.conj()).T), + - np.eye(N)]) + self.setbs([np.random.randn(N) + 1.j * np.random.randn(N)]) + self.setthAs(self.getMonomialWeights(self.nAs)) + self.setthbs(self.getMonomialWeights(self.nbs)) diff --git a/tests/reduction_methods_multiD/matrix_random.py b/tests/reduction_methods_multiD/matrix_random.py index 79a5d83..1bac706 100644 --- a/tests/reduction_methods_multiD/matrix_random.py +++ b/tests/reduction_methods_multiD/matrix_random.py @@ -1,41 +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.base import MatrixEngineBase +from rrompy.hfengines.base import LinearAffineEngine, NumpyEngineBase -class matrixRandom(MatrixEngineBase): +class matrixRandom(LinearAffineEngine, NumpyEngineBase): def __init__(self): super().__init__(verbosity = 0) self._affinePoly = True N = 100 np.random.seed(420) self.setSolver("SOLVE") self.npar = 2 self.nAs, self.nbs = 3, 1 self.mu0 = [0., 0.] d1 = np.random.randn(N) Q1, _ = np.linalg.qr(np.random.randn(N, N)) d2 = np.random.randn(N) Q2, _ = np.linalg.qr(np.random.randn(N, N)) - self.As = [np.eye(N), Q1.dot(np.multiply(d1, Q1.conj()).T), - Q2.dot(np.multiply(d2, Q2.conj()).T)] - self.bs = [np.random.randn(N) + 1.j * np.random.randn(N)] - self.thAs = self.getMonomialWeights(self.nAs) - self.thbs = self.getMonomialWeights(self.nbs) + self.setAs([np.eye(N), Q1.dot(np.multiply(d1, Q1.conj()).T), + Q2.dot(np.multiply(d2, Q2.conj()).T)]) + self.setbs([np.random.randn(N) + 1.j * np.random.randn(N)]) + self.setthAs(self.getMonomialWeights(self.nAs)) + self.setthbs(self.getMonomialWeights(self.nbs)) diff --git a/tests/utilities/fenics_norms.py b/tests/utilities/fenics_norms.py index 8340afb..61896d1 100644 --- a/tests/utilities/fenics_norms.py +++ b/tests/utilities/fenics_norms.py @@ -1,95 +1,92 @@ # 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.solver.fenics import (L2NormMatrix, H1NormMatrix, Hminus1NormMatrix, elasticNormMatrix, elasticDualNormMatrix) def test_fenics_L2(): V = fen.FunctionSpace(fen.UnitSquareMesh(3, 3), "P", 3) u = fen.interpolate(fen.Constant(3.), V) v = fen.interpolate(fen.Expression("x[0]*x[0]+x[1]", degree = 2), V) uv = u.vector()[:] vv = v.vector()[:] mass = L2NormMatrix(V) inner = fen.assemble(fen.dot(u, v) * fen.dx) assert np.isclose(uv.T.dot(mass.dot(vv)), 2.5, rtol = 1e-8) assert np.isclose(inner, 2.5, rtol = 1e-8) def test_fenics_H1(): V = fen.FunctionSpace(fen.UnitSquareMesh(3, 3), "P", 2) u = fen.interpolate(fen.Expression("x[0]+exp(x[1])", degree = 4), V) v = fen.interpolate(fen.Expression("x[0]*x[0]+x[1]", degree = 2), V) uv = u.vector()[:] vv = v.vector()[:] stiffness = H1NormMatrix(V) helmholtz = H1NormMatrix(V, 12) inners = fen.assemble(fen.dot(fen.grad(u), fen.grad(v)) * fen.dx) innerh = 12 * fen.assemble(fen.dot(u, v) * fen.dx) assert np.isclose(uv.T.dot(stiffness.dot(vv)), np.exp(1), rtol = 1e-6) assert np.isclose(inners, np.exp(1), rtol = 1e-6) assert np.isclose(uv.T.dot(helmholtz.dot(vv)), 5 * np.exp(1) + 14, rtol = 1e-3) assert np.isclose(inners + innerh, 5 * np.exp(1) + 14, rtol = 1e-3) def test_fenics_Hminus1(): V = fen.FunctionSpace(fen.UnitSquareMesh(3, 3), "P", 2) u = fen.interpolate(fen.Expression("x[0]+exp(x[1])", degree = 4), V) v = fen.interpolate(fen.Expression("x[0]*x[0]+x[1]", degree = 2), V) uv = u.vector()[:] vv = v.vector()[:] energyFull = Hminus1NormMatrix(V, 12) energyLR = Hminus1NormMatrix(V, 12, compressRank = 20) assert np.isclose(uv.T.dot(energyFull.dot(vv)), uv.T.dot(energyLR.dot(vv)), rtol = 1e-2) - assert np.isclose(uv.T.dot(energyFull.dot(vv)), .1641618355, rtol = 1e-6) + assert np.isclose(uv.T.dot(energyFull.dot(vv)), 457.9913548, rtol = 1e-2) def test_fenics_elastic(): V = fen.VectorFunctionSpace(fen.UnitCubeMesh(5, 5, 5), "P", 1) l_ = 1. m_ = fen.Expression(".5*x[0]+1.", degree = 1) u = fen.interpolate(fen.Expression(("exp(x[1])", "x[0]-x[2]", "3."), degree = 4), V) v = fen.interpolate(fen.Expression(("x[0]*x[0]+x[2]", "1.", "-1. * x[1]"), degree = 2), V) uv = u.vector()[:] vv = v.vector()[:] energyMat = elasticNormMatrix(V, l_, m_, 10) epsilon = lambda f: 0.5 * (fen.grad(f) + fen.nabla_grad(f)) sigma = (l_ * fen.div(u) * fen.Identity(3) + 2. * m_ * epsilon(u)) energy = fen.assemble((10 * fen.dot(u, v) + fen.inner(sigma, epsilon(v))) * fen.dx) assert np.isclose(uv.T.dot(energyMat.dot(vv)), energy, rtol = 1e-8) def test_fenics_elastic_dual(): V = fen.VectorFunctionSpace(fen.UnitCubeMesh(5, 5, 5), "P", 1) l_ = 1. m_ = fen.Expression(".5*x[0]+1.", degree = 1) u = fen.interpolate(fen.Expression(("exp(x[1])", "x[0]-x[2]", "3."), degree = 4), V) v = fen.interpolate(fen.Expression(("x[0]*x[0]+x[2]", "1.", "-1. * x[1]"), degree = 2), V) uv = u.vector()[:] vv = v.vector()[:] energyFull = elasticDualNormMatrix(V, l_, m_, 10) - energyLR = elasticDualNormMatrix(V, l_, m_, 10, compressRank = 50) - assert np.isclose(uv.T.dot(energyFull.dot(vv)), - uv.T.dot(energyLR.dot(vv)), rtol = 1e-1) - assert np.isclose(uv.T.dot(energyFull.dot(vv)), -.00804628936, rtol = 1e-6) + assert np.isclose(uv.T.dot(energyFull.dot(vv)), -134.6099529, rtol = 1e-3) diff --git a/tests/utilities/radial_fitting.py b/tests/utilities/radial_fitting.py index dd996f0..ec346c8 100644 --- a/tests/utilities/radial_fitting.py +++ b/tests/utilities/radial_fitting.py @@ -1,167 +1,165 @@ # 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, polyval, polyvander, polyvanderTotal) from rrompy.utilities.numerical import degreeTotalToFull 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]) globalCoeffs = cRBCoeffs[4 :] localCoeffs = cRBCoeffs[: 4] ySupp = 1 + 2. * xSupp.data - .5 * xSupp.data ** 2. xx = np.linspace(-2., 3., 100) yy = polyval(checkParameterList(xx, 1)[0], globalCoeffs, localCoeffs, xSupp, directionalWeights, 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 += localCoeffs[j] * rbj ySupp += 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]) 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], globalCoeffs, localCoeffs, xSupp, directionalWeights, 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 += localCoeffs[j] * rbj ySupp += 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]) 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], globalCoeffs, localCoeffs, xSupp, directionalWeights, 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 += localCoeffs[j] * rbj ySupp += 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 = polyvanderTotal(xySupp, deg, polyrbname, - directionalWeights = directionalWeights) - VanT = VanT[reidxs] - VanT = VanT[:, reidxs] + VanT = polyvanderTotal(xySupp, deg, polyrbname, + directionalWeights = directionalWeights) cFit = np.linalg.solve(VanT, np.pad(zSupp, (0, len(VanT) - len(zSupp)), 'constant')) globCoeff = degreeTotalToFull([deg + 1] * 2, 2, cFit[len(zSupp) :]) 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, globalCoeffs, localCoeffs, xySupp, directionalWeights, 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/utilities/sampling.py b/tests/utilities/sampling.py index e843740..1e64a43 100644 --- a/tests/utilities/sampling.py +++ b/tests/utilities/sampling.py @@ -1,65 +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 +from rrompy.hfengines.base import LinearAffineEngine, NumpyEngineBase from rrompy.sampling.standard import (SamplingEngineStandard, SamplingEngineStandardPOD) from rrompy.parameter import parameterList -class matrixEngine(MatrixEngineBase): +class matrixEngine(LinearAffineEngine, NumpyEngineBase): def __init__(self): super().__init__(verbosity = 0) self._affinePoly = True N = 100 self.npar = 1 self.nAs, self.nbs = 2, 1 - self.As = [sp.spdiags([np.arange(1, 1 + N)], [0], N, N), - - sp.eye(N)] - self.bs = [np.exp(1.j * np.linspace(0, -np.pi, N))] - self.thAs = self.getMonomialWeights(self.nAs) - self.thbs = self.getMonomialWeights(self.nbs) + self.setAs([sp.spdiags([np.arange(1, 1 + N)], [0], N, N), + - sp.eye(N)]) + self.setbs([np.exp(1.j * np.linspace(0, -np.pi, N))]) + self.setthAs(self.getMonomialWeights(self.nAs)) + self.setthbs(self.getMonomialWeights(self.nbs)) def test_krylov(): mu = 10. + .5j solver = matrixEngine() samplingEngine = SamplingEngineStandard(solver, verbosity = 0) samples = samplingEngine.iterSample([mu] * 5).data assert samples.shape == (100, 5) assert np.isclose(np.linalg.norm(samples), 37.02294804524299, rtol = 1e-5) def test_distributed(): mus = parameterList(np.linspace(5, 15, 11) + .5j) solver = matrixEngine() samplingEngine = SamplingEngineStandard(solver, verbosity = 0) samples = samplingEngine.iterSample(mus).data assert samples.shape == (100, 11) assert np.isclose(np.linalg.norm(samples), 8.59778606421386, rtol = 1e-5) def test_distributed_pod(): mus = np.linspace(5, 15, 11) + .5j solver = matrixEngine() samplingEngine = SamplingEngineStandardPOD(solver, verbosity = 0) samples = samplingEngine.iterSample(mus).data assert samples.shape == (100, 11) assert np.isclose(np.linalg.norm(samples), 3.3166247903553994, rtol = 1e-5) assert np.isclose(np.linalg.cond(samples.conj().T.dot(samples)), 1., rtol = 1e-5)