diff --git a/rrompy/hfengines/base/boundary_conditions.py b/rrompy/hfengines/base/boundary_conditions.py index 6993762..4ed01fe 100644 --- a/rrompy/hfengines/base/boundary_conditions.py +++ b/rrompy/hfengines/base/boundary_conditions.py @@ -1,126 +1,129 @@ # Copyright (C) 2018 by the RROMPy authors # # This file is part of RROMPy. # # RROMPy is free software: you can redistribute it and/or modify # it under the terms of the GNU Lesser General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # RROMPy is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU Lesser General Public License for more details. # # You should have received a copy of the GNU Lesser General Public License # along with RROMPy. If not, see <http://www.gnu.org/licenses/>. # +from copy import copy from fenics import SubDomain, AutoSubDomain from rrompy.utilities.base.types import GenExpr from rrompy.solver.fenics import bdrFalse from rrompy.utilities.exception_manager import RROMPyException __all__ = ['BoundaryConditions'] class BoundaryConditions: """ Boundary conditions manager. Attributes: DirichletBoundary: Callable returning True when on Dirichlet boundary. NeumannBoundary: Callable returning True when on Neumann boundary. RobinBoundary: Callable returning True when on Robin boundary. """ allowedKinds = ["Dirichlet", "Neumann", "Robin"] def __init__(self, kind : str = None): if kind is None: return kind = kind[0].upper() + kind[1:].lower() if kind in self.allowedKinds: getattr(self.__class__, kind + "Boundary", None).fset(self, "ALL") else: raise RROMPyException("BC kind not recognized.") def name(self) -> str: return self.__class__.__name__ def __str__(self) -> str: return self.name() + def __deepcopy__(self, memo): + return copy(self) + def __repr__(self) -> str: return self.__str__() + " at " + hex(id(self)) def _generalManagement(self, kind:str, value:GenExpr): if isinstance(value, (str,)): value = value.upper() if value.upper() == "ALL": self._complementaryManagementAll(kind) elif value.upper() == "REST": self._complementaryManagementRest(kind) else: raise RROMPyException("Wildcard not recognized.") elif callable(value): self._standardManagementCallable(kind, value) elif isinstance(value, (SubDomain,)): self._standardManagement(kind, value) else: raise RROMPyException(kind + "Boundary type not recognized.") def _complementaryManagementAll(self, kind:str): if kind not in self.allowedKinds: raise RROMPyException("BC kind not recognized.") for k in self.allowedKinds: if k != kind: self._standardManagementCallable(k, bdrFalse) self._complementaryManagementRest(kind) def _complementaryManagementRest(self, kind:str): if kind not in self.allowedKinds: raise RROMPyException("BC kind not recognized.") otherBCs = [] for k in self.allowedKinds: if k != kind: if hasattr(self, "_" + k + "Rest"): self._standardManagementCallable(k, bdrFalse) otherBCs += [getattr(self, k + "Boundary")] - def restCall(x, on_boundary): - return (on_boundary - and not any([bc.inside(x, on_boundary) for bc in otherBCs])) + restCall = lambda x, on_boundary: (on_boundary + and not any([bc.inside(x, on_boundary) for bc in otherBCs])) self._standardManagementCallable(kind, restCall) super().__setattr__("_" + kind + "Rest", 1) def _standardManagementCallable(self, kind:str, bc:callable): bcSD = AutoSubDomain(bc) self._standardManagement(kind, bcSD) def _standardManagement(self, kind:str, bc:SubDomain): super().__setattr__("_" + kind + "Boundary", bc) if hasattr(self, "_" + kind + "Rest"): super().__delattr__("_" + kind + "Rest") @property def DirichletBoundary(self): """Function handle to DirichletBoundary.""" return self._DirichletBoundary @DirichletBoundary.setter def DirichletBoundary(self, DirichletBoundary): self._generalManagement("Dirichlet", DirichletBoundary) @property def NeumannBoundary(self): """Function handle to NeumannBoundary.""" return self._NeumannBoundary @NeumannBoundary.setter def NeumannBoundary(self, NeumannBoundary): self._generalManagement("Neumann", NeumannBoundary) @property def RobinBoundary(self): """Function handle to RobinBoundary.""" return self._RobinBoundary @RobinBoundary.setter def RobinBoundary(self, RobinBoundary): self._generalManagement("Robin", RobinBoundary) diff --git a/rrompy/hfengines/base/matrix_engine_base.py b/rrompy/hfengines/base/matrix_engine_base.py index 664e74e..dbd7308 100644 --- a/rrompy/hfengines/base/matrix_engine_base.py +++ b/rrompy/hfengines/base/matrix_engine_base.py @@ -1,441 +1,446 @@ # 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 <http://www.gnu.org/licenses/>. # from abc import abstractmethod import numpy as np import scipy.sparse as scsp from matplotlib import pyplot as plt -from copy import deepcopy as copy +from copy import deepcopy as copy, copy as softcopy from rrompy.utilities.base.types import (Np1D, Np2D, ScOp, strLst, Tuple, List, DictAny, paramVal, paramList, sampList) from rrompy.utilities.base import (purgeList, getNewFilename, verbosityDepth, multibinom) from rrompy.utilities.poly_fitting.polynomial import ( hashDerivativeToIdx as hashD, hashIdxToDerivative as hashI) from rrompy.utilities.exception_manager import RROMPyException, RROMPyAssert from rrompy.parameter import checkParameter, checkParameterList from rrompy.sampling import sampleList, emptySampleList from rrompy.solver import setupSolver __all__ = ['MatrixEngineBase'] - + class MatrixEngineBase: """ Generic solver for parametric matrix problems. Attributes: verbosity: Verbosity level. As: Scipy sparse array representation (in CSC format) of As. bs: Numpy array representation of bs. bsH: Numpy array representation of homogeneized bs. energyNormMatrix: Scipy sparse matrix representing inner product. """ def __init__(self, verbosity : int = 10, timestamp : bool = True): self.verbosity = verbosity self.timestamp = timestamp self.nAs, self.nbs = 1, 1 self.setSolver("SPSOLVE", {"use_umfpack" : False}) self.npar = 0 def name(self) -> str: return self.__class__.__name__ def __str__(self) -> str: return self.name() def __repr__(self) -> str: return self.__str__() + " at " + hex(id(self)) def __dir_base__(self): return [x for x in self.__dir__() if x[:2] != "__"] + def __deepcopy__(self, memo): + return softcopy(self) + @property def npar(self): """Value of npar.""" return self._npar @npar.setter def npar(self, npar): nparOld = self._npar if hasattr(self, "_npar") else -1 if npar != nparOld: self.rescalingExp = [1.] * npar self._npar = npar @property def nAs(self): """Value of nAs.""" return self._nAs @nAs.setter def nAs(self, nAs): self._nAs = nAs self.resetAs() @property def nbs(self): """Value of nbs.""" return self._nbs @nbs.setter def nbs(self, nbs): self._nbs = nbs self.resetbs() @property def nbsH(self) -> int: return max(self.nbs, self.nAs) def spacedim(self): return self.As[0].shape[1] def checkParameter(self, mu:paramList): return checkParameter(mu, self.npar) def checkParameterList(self, mu:paramList): return checkParameterList(mu, self.npar) def buildEnergyNormForm(self): # eye """ Build sparse matrix (in CSR format) representative of scalar product. """ self.energyNormMatrix = np.eye(self.spacedim()) def innerProduct(self, u:Np2D, v:Np2D, onlyDiag : bool = False) -> Np2D: """Scalar product.""" if not hasattr(self, "energyNormMatrix"): if self.verbosity >= 20: verbosityDepth("INIT", "Assembling energy matrix.", timestamp = self.timestamp) self.buildEnergyNormForm() if self.verbosity >= 20: verbosityDepth("DEL", "Done assembling energy matrix.", timestamp = self.timestamp) if not isinstance(u, (np.ndarray,)): u = u.data if not isinstance(v, (np.ndarray,)): v = v.data if onlyDiag: return np.sum(self.energyNormMatrix.dot(u) * v.conj(), axis = 0) return v.T.conj().dot(self.energyNormMatrix.dot(u)) def norm(self, u:Np2D) -> Np1D: return np.abs(self.innerProduct(u, u, onlyDiag = True)) ** .5 def checkAInBounds(self, derI : int = 0): """Check if derivative index is oob for operator of linear system.""" if derI < 0 or derI >= self.nAs: d = self.spacedim() return scsp.csr_matrix((np.zeros(0), np.zeros(0), np.zeros(d + 1)), shape = (d, d), dtype = np.complex) def checkbInBounds(self, derI : int = 0, homogeneized : bool = False): """Check if derivative index is oob for RHS of linear system.""" nbs = self.nbsH if homogeneized else self.nbs if derI < 0 or derI >= nbs: return np.zeros(self.spacedim(), dtype = np.complex) def resetAs(self): """Reset (derivatives of) operator of linear system.""" self.setAs([None] * self.nAs) if hasattr(self, "_nbs"): self.resetbsH() def resetbs(self): """Reset (derivatives of) RHS of linear system.""" self.setbs([None] * self.nbs) if hasattr(self, "_nAs"): self.resetbsH() def resetbsH(self): """Reset (derivatives of) homogeneized RHS of linear system.""" self.setbsH([None] * self.nbsH) def setAs(self, As:List[Np2D]): """Assign terms of operator of linear system.""" if len(As) != self.nAs: raise RROMPyException(("Expected number {} of terms of As not " "matching given list length {}.").format(self.nAs, len(As))) self.As = [copy(A) for A in As] def setbs(self, bs:List[Np1D]): """Assign terms of RHS of linear system.""" if len(bs) != self.nbs: raise RROMPyException(("Expected number {} of terms of bs not " "matching given list length {}.").format(self.nbs, len(bs))) self.bs = [copy(b) for b in bs] def setbsH(self, bsH:List[Np1D]): """Assign terms of homogeneized RHS of linear system.""" if len(bsH) != self.nbsH: raise RROMPyException(("Expected number {} of terms of bsH not " "matching given list length {}.").format(self.nbsH, len(bsH))) self.bsH = [copy(bH) for bH in bsH] def _assembleA(self, mu : paramVal = [], der : List[int] = 0, derI : int = None, muBase : paramVal = None) -> ScOp: """Assemble (derivative of) operator of linear system.""" mu = self.checkParameter(mu) if muBase is None: muBase = [0.] * self.npar muBase = self.checkParameter(muBase) if self.npar > 0: mu, muBase = mu[0], muBase[0] if not hasattr(der, "__len__"): der = [der] * self.npar if derI is None: derI = hashD(der) Anull = self.checkAInBounds(derI) if Anull is not None: return Anull rExp = self.rescalingExp A = copy(self.As[derI]) for j in range(derI + 1, self.nAs): derIdx = hashI(j, self.npar) diffIdx = [x - y for (x, y) in zip(derIdx, der)] if np.all([x >= 0 for x in diffIdx]): A = A + (np.prod((mu ** rExp - muBase ** rExp) ** diffIdx) * multibinom(derIdx, diffIdx) * self.As[j]) return A @abstractmethod def A(self, mu : paramVal = [], der : List[int] = 0) -> ScOp: """ Assemble terms of operator of linear system and return it (or its derivative) at a given parameter. """ if not hasattr(der, "__len__"): der = [der] * self.npar derI = hashD(der) for j in range(derI, self.nAs): - if self.As[j] is None: self.As[j] = 0. + if self.As[j] is None: self.As[j] = self.checkAInBounds(-1) return self._assembleA(mu, der, derI) def affineLinearSystemA(self, mu : paramVal = []) -> List[Np2D]: """ Assemble affine blocks of operator of linear system (just linear blocks). """ As = [None] * self.nAs for j in range(self.nAs): As[j] = self.A(mu, hashI(j, self.npar)) return As def affineWeightsA(self, mu : paramVal = []) -> List[str]: """ Assemble affine blocks of operator of linear system (just affine weights). Stored as strings for the sake of pickling. """ mu = self.checkParameter(mu) lambdasA = ["1."] mu0Eff = mu ** self.rescalingExp for j in range(1, self.nAs): lambdasA += ["np.prod((mu ** ({1}) - [{0}]) ** ({2}))".format( ','.join([str(x) for x in mu0Eff[0]]), self.rescalingExp, hashI(j, self.npar))] return lambdasA def affineBlocksA(self, mu : paramVal = [])\ -> Tuple[List[Np2D], List[str]]: """Assemble affine blocks of operator of linear system.""" return self.affineLinearSystemA(mu), self.affineWeightsA(mu) def _assembleb(self, mu : paramVal = [], der : List[int] = 0, derI : int = None, homogeneized : bool = False, muBase : paramVal = None) -> ScOp: """Assemble (derivative of) (homogeneized) RHS of linear system.""" mu = self.checkParameter(mu) if muBase is None: muBase = [0.] * self.npar muBase = self.checkParameter(muBase) if self.npar > 0: mu, muBase = mu[0], muBase[0] if not hasattr(der, "__len__"): der = [der] * self.npar if derI is None: derI = hashD(der) bnull = self.checkbInBounds(derI, homogeneized) if bnull is not None: return bnull bs = self.bsH if homogeneized else self.bs rExp = self.rescalingExp b = copy(bs[derI]) for j in range(derI + 1, len(bs)): derIdx = hashI(j, self.npar) diffIdx = [x - y for (x, y) in zip(derIdx, der)] if np.all([x >= 0 for x in diffIdx]): b = b + (np.prod((mu ** rExp - muBase ** rExp) ** diffIdx) * multibinom(derIdx, diffIdx) * bs[j]) return b @abstractmethod def b(self, mu : paramVal = [], der : List[int] = 0, homogeneized : bool = False) -> Np1D: """ Assemble terms of (homogeneized) RHS of linear system and return it (or its derivative) at a given parameter. """ if not hasattr(der, "__len__"): der = [der] * self.npar derI = hashD(der) if homogeneized: for j in range(derI, self.nbsH): - if self.bsH[j] is None: self.bsH[j] = 0. + if self.bsH[j] is None: self.bsH[j] = self.checkbInBounds(-1) else: for j in range(derI, self.nbs): - if self.bs[j] is None: self.bs[j] = 0. + if self.bs[j] is None: self.bs[j] = self.checkbInBounds(-1) return self._assembleb(mu, der, derI, homogeneized) def affineLinearSystemb(self, mu : paramVal = [], homogeneized : bool = False) -> List[Np1D]: """ Assemble affine blocks of RHS of linear system (just linear blocks). """ nbs = self.nbsH if homogeneized else self.nbs bs = [None] * nbs for j in range(nbs): bs[j] = self.b(mu, hashI(j, self.npar), homogeneized) return bs def affineWeightsb(self, mu : paramVal = [], homogeneized : bool = False) -> List[str]: """ Assemble affine blocks of RHS of linear system (just affine weights). Stored as strings for the sake of pickling. """ mu = self.checkParameter(mu) nbs = self.nbsH if homogeneized else self.nbs lambdasb = ["1."] mu0Eff = mu ** self.rescalingExp for j in range(1, nbs): lambdasb += ["np.prod((mu ** ({1}) - [{0}]) ** ({2}))".format( ','.join([str(x) for x in mu0Eff[0]]), self.rescalingExp, hashI(j, self.npar))] return lambdasb def affineBlocksb(self, mu : paramVal = [], homogeneized : bool = False)\ -> Tuple[List[Np1D], List[str]]: """Assemble affine blocks of RHS of linear system.""" return (self.affineLinearSystemb(mu, homogeneized), self.affineWeightsb(mu, homogeneized)) def setSolver(self, solverType:str, solverArgs : DictAny = {}): """Choose solver type and parameters.""" self._solver, self._solverArgs = setupSolver(solverType, solverArgs) def solve(self, mu : paramList = [], RHS : sampList = None, homogeneized : bool = False) -> sampList: """ Find solution of linear system. Args: mu: parameter value. RHS: RHS of linear system. If None, defaults to that of parametric system. Defaults to None. """ mu, _ = self.checkParameterList(mu) if mu.shape[0] == 0: mu.reset((1, self.npar), mu.dtype) - if RHS is None: - RHS = [self.b(m, homogeneized = homogeneized) for m in mu] - RHS = sampleList(RHS) - mult = 0 if len(RHS) == 1 else 1 - RROMPyAssert(mult * (len(mu) - 1) + 1, len(RHS), "Sample size") sol = emptySampleList() - for j in range(len(mu)): - u = self._solver(self.A(mu[j]), RHS[mult * j], self._solverArgs) - if j == 0: - sol.reset((len(u), len(mu)), dtype = u.dtype) - sol[j] = u + if len(mu) > 0: + if RHS is None: + RHS = [self.b(m, homogeneized = homogeneized) for m in mu] + RHS = sampleList(RHS) + mult = 0 if len(RHS) == 1 else 1 + RROMPyAssert(mult * (len(mu) - 1) + 1, len(RHS), "Sample size") + u = self._solver(self.A(mu[0]), RHS[0], self._solverArgs) + sol.reset((len(u), len(mu)), dtype = u.dtype) + sol[0] = u + for j in range(1, len(mu)): + sol[j] = self._solver(self.A(mu[j]), RHS[mult * j], + self._solverArgs) return sol def residual(self, u:sampList, mu : paramList = [], homogeneized : bool = False) -> sampList: """ Find residual of linear system for given approximate solution. Args: u: numpy complex array with function dofs. If None, set to 0. mu: parameter value. """ mu, _ = self.checkParameterList(mu) if mu.shape[0] == 0: mu.reset((1, self.npar), mu.dtype) if u is not None: u = sampleList(u) mult = 0 if len(u) == 1 else 1 RROMPyAssert(mult * (len(mu) - 1) + 1, len(u), "Sample size") res = emptySampleList() for j in range(len(mu)): b = self.b(mu[j], homogeneized = homogeneized) if u is None: r = b else: r = b - self.A(mu[j]).dot(u[mult * j]) if j == 0: res.reset((len(r), len(mu)), dtype = r.dtype) res[j] = r return res def plot(self, u:Np1D, name : str = "u", save : str = None, what : strLst = 'all', saveFormat : str = "eps", saveDPI : int = 100, show : bool = True, **figspecs): """ Do some nice plots of the complex-valued function with given dofs. Args: u: numpy complex array with function dofs. name(optional): Name to be shown as title of the plots. Defaults to 'u'. save(optional): Where to save plot(s). Defaults to None, i.e. no saving. what(optional): Which plots to do. If list, can contain 'ABS', 'PHASE', 'REAL', 'IMAG'. If str, same plus wildcard 'ALL'. Defaults to 'ALL'. saveFormat(optional): Format for saved plot(s). Defaults to "eps". saveDPI(optional): DPI for saved plot(s). Defaults to 100. show(optional): Whether to show figure. Defaults to True. figspecs(optional key args): Optional arguments for matplotlib figure creation. """ if isinstance(what, (str,)): if what.upper() == 'ALL': what = ['ABS', 'PHASE', 'REAL', 'IMAG'] else: what = [what] what = purgeList(what, ['ABS', 'PHASE', 'REAL', 'IMAG'], listname = self.name() + ".what", baselevel = 1) if len(what) == 0: return if 'figsize' not in figspecs.keys(): figspecs['figsize'] = (13. * len(what) / 4, 3) subplotcode = 100 + len(what) * 10 idxs = np.arange(self.spacedim()) plt.figure(**figspecs) plt.jet() if 'ABS' in what: subplotcode = subplotcode + 1 plt.subplot(subplotcode) plt.plot(idxs, np.abs(u)) plt.title("|{0}|".format(name)) if 'PHASE' in what: subplotcode = subplotcode + 1 plt.subplot(subplotcode) plt.plot(idxs, np.angle(u)) plt.title("phase({0})".format(name)) if 'REAL' in what: subplotcode = subplotcode + 1 plt.subplot(subplotcode) plt.plot(idxs, np.real(u)) plt.title("Re({0})".format(name)) if 'IMAG' in what: subplotcode = subplotcode + 1 plt.subplot(subplotcode) plt.plot(idxs, np.imag(u)) 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/linear_problem/bidimensional/helmholtz_square_domain_problem_engine.py b/rrompy/hfengines/linear_problem/bidimensional/helmholtz_square_domain_problem_engine.py index 04a296b..eafce7b 100644 --- a/rrompy/hfengines/linear_problem/bidimensional/helmholtz_square_domain_problem_engine.py +++ b/rrompy/hfengines/linear_problem/bidimensional/helmholtz_square_domain_problem_engine.py @@ -1,229 +1,228 @@ # 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 <http://www.gnu.org/licenses/>. # import numpy as np import scipy.sparse as scsp -from scipy.special import factorial as fact import fenics as fen from rrompy.utilities.base.types import (ScOp, List, Tuple, paramVal, Np1D, FenExpr) from rrompy.solver.fenics import fenZERO, fenONE from rrompy.hfengines.linear_problem.helmholtz_problem_engine import ( HelmholtzProblemEngine) from rrompy.utilities.base import verbosityDepth from rrompy.utilities.poly_fitting.polynomial import ( hashDerivativeToIdx as hashD, hashIdxToDerivative as hashI) __all__ = ['HelmholtzSquareDomainProblemEngine'] class HelmholtzSquareDomainProblemEngine(HelmholtzProblemEngine): """ Solver for square Helmholtz problems with parametric laplacian. - \dxx u - mu_2**2 \dyy u - mu_1**2 * u = f(mu_2) in \Omega = [0,\pi]^2 u = 0 on \partial\Omega """ def __init__(self, kappa:float, theta:float, n:int, mu0 : paramVal = [12. ** .5, 1.], degree_threshold : int = np.inf, verbosity : int = 10, timestamp : bool = True): super().__init__(mu0 = mu0, degree_threshold = degree_threshold, verbosity = verbosity, timestamp = timestamp) self.nAs, self.nbs = 6, 11 * 12 // 2 self.npar = 2 self.rescalingExp = [2., 1.] pi = np.pi mesh = fen.RectangleMesh(fen.Point(0, 0), fen.Point(pi, pi), 3 * n, 3 * n) self.V = fen.FunctionSpace(mesh, "P", 1) c, s = np.cos(theta), np.sin(theta) x, y = fen.SpatialCoordinate(mesh)[:] self.forcingTerm = [fen.cos(kappa * (c * x + s / self.mu0(0, 1) * y)), fen.sin(kappa * (c * x + s / self.mu0(0, 1) * y))] self.forcingTermDer = kappa * s * y def getExtraFactorB(self, mu : paramVal = [], derI : int = 0) -> Tuple[FenExpr, FenExpr]: """Compute extra expression in RHS.""" mu = self.checkParameter(mu) def getPowMinusj(x, power): powR = x ** power powI = fenZERO if power % 2 == 1: powR, powI = powI, powR if power % 4 > 1: powR, powI = - powR, - powI return powR, powI if self.verbosity >= 25: verbosityDepth("INIT", ("Assembling auxiliary expression for " "forcing term derivative."), timestamp = self.timestamp) if derI == 0: return fenONE, fenZERO coeffs = np.zeros(derI + 1) coeffs[1] = - 2. for j in range(2, derI + 1): coeffs[1 :] = (-2. / j * coeffs[1 :] - (3 - (1 + 2 * np.arange(derI)) / j) * coeffs[: -1]) for j in range(derI): powR, powI = getPowMinusj(self.forcingTermDer, derI - j) mupBase = coeffs[j + 1] * mu(0, 1) ** (- 3 * derI + 2 * j) mupR, mupI = np.real(mupBase), np.imag(mupBase) if j == 0: exprR = mupR * powR - mupI * powI exprI = mupI * powR + mupR * powI else: exprR += mupR * powR - mupI * powI exprI += mupI * powR + mupR * powI if self.verbosity >= 25: verbosityDepth("DEL", "Done assembling auxiliary expression.", timestamp = self.timestamp) return exprR, exprI def A(self, mu : paramVal = [], der : List[int] = 0) -> ScOp: """Assemble (derivative of) operator of linear system.""" mu = self.checkParameter(mu) if not hasattr(der, "__len__"): der = [der] * self.npar derI = hashD(der) self.autoSetDS() + for j in range(2, 5): + if derI <= j and self.As[j] is None: + self.As[j] = self.checkAInBounds(-1) if derI <= 0 and self.As[0] is None: if self.verbosity >= 20: verbosityDepth("INIT", "Assembling operator term A0.", timestamp = self.timestamp) DirichletBC0 = fen.DirichletBC(self.V, fenZERO, self.DirichletBoundary) a0Re = fen.dot(self.u.dx(0), self.v.dx(0)) * fen.dx A0Re = fen.assemble(a0Re) DirichletBC0.apply(A0Re) A0ReMat = fen.as_backend_type(A0Re).mat() A0Rer, A0Rec, A0Rev = A0ReMat.getValuesCSR() self.As[0] = scsp.csr_matrix((A0Rev, A0Rec, A0Rer), shape = A0ReMat.size, dtype = np.complex) if self.verbosity >= 20: verbosityDepth("DEL", "Done assembling operator term.", timestamp = self.timestamp) if derI <= 1 and self.As[1] is None: if self.verbosity >= 20: verbosityDepth("INIT", "Assembling operator term A1.", timestamp = self.timestamp) DirichletBC0 = fen.DirichletBC(self.V, fenZERO, self.DirichletBoundary) nRe, nIm = self.refractionIndex n2Re, n2Im = nRe * nRe - nIm * nIm, 2 * nRe * nIm parsRe = self.iterReduceQuadratureDegree(zip([n2Re], ["refractionIndexSquaredReal"])) parsIm = self.iterReduceQuadratureDegree(zip([n2Im], ["refractionIndexSquaredImag"])) a1Re = - n2Re * fen.dot(self.u, self.v) * fen.dx a1Im = - n2Im * fen.dot(self.u, self.v) * fen.dx A1Re = fen.assemble(a1Re, form_compiler_parameters = parsRe) A1Im = fen.assemble(a1Im, form_compiler_parameters = parsIm) DirichletBC0.zero(A1Re) DirichletBC0.zero(A1Im) A1ReMat = fen.as_backend_type(A1Re).mat() A1ImMat = fen.as_backend_type(A1Im).mat() A1Rer, A1Rec, A1Rev = A1ReMat.getValuesCSR() A1Imr, A1Imc, A1Imv = A1ImMat.getValuesCSR() self.As[1] = (scsp.csr_matrix((A1Rev, A1Rec, A1Rer), shape = A1ReMat.size) + 1.j * scsp.csr_matrix((A1Imv, A1Imc, A1Imr), shape = A1ImMat.size)) if self.verbosity >= 20: verbosityDepth("DEL", "Done assembling operator term.", timestamp = self.timestamp) - if derI <= 2 and self.As[2] is None: self.As[2] = 0. - if derI <= 3 and self.As[3] is None: self.As[3] = 0. - if derI <= 4 and self.As[4] is None: self.As[4] = 0. if derI <= 5 and self.As[5] is None: if self.verbosity >= 20: verbosityDepth("INIT", "Assembling operator term A5.", timestamp = self.timestamp) DirichletBC0 = fen.DirichletBC(self.V, fenZERO, self.DirichletBoundary) a5Re = fen.dot(self.u.dx(1), self.v.dx(1)) * fen.dx A5Re = fen.assemble(a5Re) DirichletBC0.zero(A5Re) A5ReMat = fen.as_backend_type(A5Re).mat() A5Rer, A5Rec, A5Rev = A5ReMat.getValuesCSR() self.As[5] = scsp.csr_matrix((A5Rev, A5Rec, A5Rer), shape = A5ReMat.size, dtype = np.complex) if self.verbosity >= 20: verbosityDepth("DEL", "Done assembling operator term.", timestamp = self.timestamp) return self._assembleA(mu, der, derI) def b(self, mu : paramVal = [], der : List[int] = 0, homogeneized : bool = False) -> Np1D: """Assemble (derivative of) RHS of linear system.""" mu = self.checkParameter(mu) if not hasattr(der, "__len__"): der = [der] * self.npar derI = hashD(der) nbsTot = self.nbsH if homogeneized else self.nbs bs = self.bsH if homogeneized else self.bs if homogeneized and self.mu0 != self.mu0BC: self.liftDirichletData(self.mu0) for j in range(derI, nbsTot): derH = hashI(j, self.npar) if bs[j] is None: if np.sum(derH) != derH[-1]: if homogeneized: - self.bsH[j] = 0. + self.bsH[j] = self.checkbInBounds(-1) else: - self.bs[j] = 0. + self.bs[j] = self.checkbInBounds(-1) continue if self.verbosity >= 20: verbosityDepth("INIT", ("Assembling forcing term " "b{}.").format(j), timestamp = self.timestamp) if j == 0: u0Re, u0Im = self.DirichletDatum else: u0Re, u0Im = fenZERO, fenZERO if j < self.nbs: fRe, fIm = self.forcingTerm cRe, cIm = self.getExtraFactorB(self.mu0, derH[-1]) cfRe, cfIm = cRe * fRe - cIm * fIm, cRe * fIm + cIm * fRe else: cfRe, cfIm = fenZERO, fenZERO parsRe = self.iterReduceQuadratureDegree(zip([cfRe], ["forcingTermDer{}Real".format(j)])) parsIm = self.iterReduceQuadratureDegree(zip([cfIm], ["forcingTermDer{}Imag".format(j)])) L0Re = fen.dot(cfRe, self.v) * fen.dx L0Im = fen.dot(cfIm, self.v) * fen.dx b0Re = fen.assemble(L0Re, form_compiler_parameters = parsRe) b0Im = fen.assemble(L0Im, form_compiler_parameters = parsIm) DBCR = fen.DirichletBC(self.V, u0Re, self.DirichletBoundary) DBCI = fen.DirichletBC(self.V, u0Im, self.DirichletBoundary) DBCR.apply(b0Re) DBCI.apply(b0Im) b = np.array(b0Re[:] + 1.j * b0Im[:], dtype = np.complex) if homogeneized: Ader = self.A(self.mu0, derH) b -= Ader.dot(self.liftedDirichletDatum) if homogeneized: self.bsH[j] = b else: self.bs[j] = b if self.verbosity >= 20: verbosityDepth("DEL", "Done assembling forcing term.", timestamp = self.timestamp) return self._assembleb(mu, der, derI, homogeneized, self.mu0) diff --git a/rrompy/hfengines/linear_problem/bidimensional/laplace_disk_gaussian_2.py b/rrompy/hfengines/linear_problem/bidimensional/laplace_disk_gaussian_2.py index db0ed1d..ef85fa7 100644 --- a/rrompy/hfengines/linear_problem/bidimensional/laplace_disk_gaussian_2.py +++ b/rrompy/hfengines/linear_problem/bidimensional/laplace_disk_gaussian_2.py @@ -1,125 +1,125 @@ # 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 <http://www.gnu.org/licenses/>. # import numpy as np import fenics as fen from rrompy.utilities.base.types import Np1D, Tuple, List, FenExpr, paramVal from rrompy.hfengines.linear_problem.laplace_disk_gaussian import ( LaplaceDiskGaussian) from rrompy.solver.fenics import fenZERO, fenONE from rrompy.utilities.base import verbosityDepth from rrompy.utilities.poly_fitting.polynomial import ( hashDerivativeToIdx as hashD, hashIdxToDerivative as hashI) from rrompy.utilities.exception_manager import RROMPyException __all__ = ['LaplaceDiskGaussian2'] class LaplaceDiskGaussian2(LaplaceDiskGaussian): """ Solver for disk Laplace problems with parametric forcing term center. - \Delta u = C exp(-.5 * ||\cdot - (mu1, mu2)||^2) in \Omega = B(0, 5) u = 0 on \partial\Omega. """ def __init__(self, n:int, mu0 : paramVal = [0., 0.], degree_threshold : int = np.inf, verbosity : int = 10, timestamp : bool = True): super().__init__(n = n, mu0 = mu0, degree_threshold = degree_threshold, verbosity = verbosity, timestamp = timestamp) self.nbs = 1 self.npar = 2 def getForcingTerm(self, mu : paramVal = []) -> Tuple[FenExpr, FenExpr]: """Compute forcing term.""" mu = self.checkParameter(mu) if mu != self.forcingTermMu: if self.verbosity >= 25: verbosityDepth("INIT", ("Assembling base expression for " "forcing term."), timestamp = self.timestamp) x, y = fen.SpatialCoordinate(self.V.mesh())[:] C = np.exp(-.5 * (mu(0, 0) ** 2. + mu(0, 1) ** 2.)) CR, CI = np.real(C), np.imag(C) f0 = (2 * np.pi) ** -.5 * fen.exp(-.5 * (x ** 2. + y ** 2.)) muxR, muxI = np.real(mu(0, 0)), np.imag(mu(0, 0)) muyR, muyI = np.real(mu(0, 1)), np.imag(mu(0, 1)) f1R = fen.exp(muxR * x + muyR * y) * fen.cos(muxI * x + muyI * y) f1I = fen.exp(muxR * x + muyR * y) * fen.sin(muxI * x + muyI * y) self.forcingTerm = [f0 * (CR * f1R - CI * f1I), f0 * (CR * f1I + CI * f1R)] self.forcingTermMu = mu if self.verbosity >= 25: verbosityDepth("DEL", "Done assembling base expression.", timestamp = self.timestamp) return self.forcingTerm def computebsFactors(self): pass def getExtraFactorB(self, mu : paramVal = [], derI : int = 0) -> Tuple[FenExpr, FenExpr]: if derI == 0: return [fenONE, fenZERO] raise RROMPyException("Not implemented.") def b(self, mu : paramVal = [], der : List[int] = 0, homogeneized : bool = False) -> Np1D: """Assemble (derivative of) RHS of linear system.""" mu = self.checkParameter(mu) if not hasattr(der, "__len__"): der = [der] * self.npar derI = hashD(der) nbsTot = self.nbsH if homogeneized else self.nbs bs = self.bsH if homogeneized else self.bs if homogeneized and self.mu0 != self.mu0BC: self.liftDirichletData(self.mu0) for j in range(derI, nbsTot): - if True or bs[j] is None: + if bs[j] is None: if self.verbosity >= 20: verbosityDepth("INIT", ("Assembling forcing term " "b{}.").format(j), timestamp = self.timestamp) if j < self.nbs: fRe, fIm = self.getForcingTerm(mu) cRe, cIm = self.getExtraFactorB(mu, j) cfRe, cfIm = cRe * fRe - cIm * fIm, cRe * fIm + cIm * fRe else: cfRe, cfIm = fenZERO, fenZERO parsRe = self.iterReduceQuadratureDegree(zip([cfRe], ["forcingTermDer{}Real".format(j)])) parsIm = self.iterReduceQuadratureDegree(zip([cfIm], ["forcingTermDer{}Imag".format(j)])) L0Re = fen.dot(cfRe, self.v) * fen.dx L0Im = fen.dot(cfIm, self.v) * fen.dx b0Re = fen.assemble(L0Re, form_compiler_parameters = parsRe) b0Im = fen.assemble(L0Im, form_compiler_parameters = parsIm) DirichletBC0 = fen.DirichletBC(self.V, fenZERO, self.DirichletBoundary) DirichletBC0.apply(b0Re) DirichletBC0.apply(b0Im) b = np.array(b0Re[:] + 1.j * b0Im[:], dtype = np.complex) if homogeneized: Ader = self.A(self.mu0, hashI(j, self.npar)) b -= Ader.dot(self.liftedDirichletDatum) if homogeneized: self.bsH[j] = b else: self.bs[j] = b if self.verbosity >= 20: verbosityDepth("DEL", "Done assembling forcing term.", timestamp = self.timestamp) return self._assembleb(mu, der, derI, homogeneized, self.mu0) diff --git a/rrompy/hfengines/linear_problem/helmholtz_square_bubble_domain_problem_engine.py b/rrompy/hfengines/linear_problem/helmholtz_square_bubble_domain_problem_engine.py index b29b809..6babb7c 100644 --- a/rrompy/hfengines/linear_problem/helmholtz_square_bubble_domain_problem_engine.py +++ b/rrompy/hfengines/linear_problem/helmholtz_square_bubble_domain_problem_engine.py @@ -1,245 +1,246 @@ # 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 <http://www.gnu.org/licenses/>. # import numpy as np import scipy.sparse as scsp import fenics as fen from rrompy.utilities.base.types import (Np1D, ScOp, Tuple, List, FenExpr, paramVal) from rrompy.solver.fenics import fenZERO from .helmholtz_problem_engine import HelmholtzProblemEngine from rrompy.utilities.base import verbosityDepth from rrompy.utilities.poly_fitting.polynomial import ( hashDerivativeToIdx as hashD, hashIdxToDerivative as hashI) __all__ = ['HelmholtzSquareBubbleDomainProblemEngine'] class HelmholtzSquareBubbleDomainProblemEngine(HelmholtzProblemEngine): """ Solver for square bubble Helmholtz problems with parametric domain heigth. - \Delta u - kappa^2 * u = f in \Omega_mu = [0,\pi] x [0,\mu\pi] u = 0 on \Gamma_mu = \partial\Omega_mu with exact solution square bubble times plane wave. """ def __init__(self, kappa:float, theta:float, n:int, mu0 : paramVal = [1.], degree_threshold : int = np.inf, verbosity : int = 10, timestamp : bool = True): super().__init__(mu0 = mu0, degree_threshold = degree_threshold, verbosity = verbosity, timestamp = timestamp) self.nAs, self.nbs = 3, 20 self.kappa = kappa self.theta = theta self.forcingTermMu = np.nan mesh = fen.RectangleMesh(fen.Point(0, 0), fen.Point(np.pi, np.pi), 3 * n, 3 * n) self.V = fen.FunctionSpace(mesh, "P", 1) self.rescalingExp = [1.] def buildEnergyNormForm(self): # H1 """ Build sparse matrix (in CSR format) representative of scalar product. """ mudxM = np.abs(self.mu0(0, 0)) * (fen.dot(self.u.dx(0), self.v.dx(0)) + fen.dot(self.u, self.v)) imudy = 1. / np.abs(self.mu0(0, 0)) * fen.dot(self.u.dx(1), self.v.dx(1)) normMatFen = fen.assemble((mudxM + imudy) * fen.dx) normMat = fen.as_backend_type(normMatFen).mat() self.energyNormMatrix = scsp.csr_matrix(normMat.getValuesCSR()[::-1], shape = normMat.size) def getForcingTerm(self, mu : paramVal = []) -> Tuple[FenExpr, FenExpr]: """Compute forcing term.""" mu = self.checkParameter(mu) if mu != self.forcingTermMu: if self.verbosity >= 25: verbosityDepth("INIT", ("Assembling base expression for " "forcing term."), timestamp = self.timestamp) pi = np.pi c, s = np.cos(self.theta), np.sin(self.theta) x, y = fen.SpatialCoordinate(self.V.mesh())[:] muR, muI = np.real(mu(0, 0)), np.imag(mu(0, 0)) mu2R, mu2I = np.real(mu(0, 0) ** 2.), np.imag(mu(0, 0) ** 2.) C = 16. / pi ** 4. bR = C * (2 * (x * (pi - x) + y * (pi - y)) + (self.kappa * s) ** 2. * (mu2R - 1.) * x * (pi - x) * y * (pi - y)) bI = C * (2 * self.kappa * (c * (pi - 2 * x) * y * (pi - y) + s * x * (pi - x) * (pi - 2 * y)) + (self.kappa * s) ** 2. * mu2I * x * (pi - x) * y * (pi - y)) wR = (fen.cos(self.kappa * (c * x + s * muR * y)) * fen.exp(self.kappa * s * muI * y)) wI = (fen.sin(self.kappa * (c * x + s * muR * y)) * fen.exp(self.kappa * s * muI * y)) self.forcingTerm = [bR * wR + bI * wI, bI * wR - bR * wI] self.forcingTermMu = mu if self.verbosity >= 25: verbosityDepth("DEL", "Done assembling base expression.", timestamp = self.timestamp) return self.forcingTerm def getExtraFactorB(self, mu : paramVal = [], derI : int = 0) -> Tuple[FenExpr, FenExpr]: """Compute extra expression in RHS.""" mu = self.checkParameter(mu) def getPowMinusj(x, power): powR = x ** power powI = fenZERO if power % 2 == 1: powR, powI = powI, powR if (power + 3) % 4 < 2: powR, powI = - powR, - powI return powR, powI if self.verbosity >= 25: verbosityDepth("INIT", ("Assembling auxiliary expression for " "forcing term derivative."), timestamp = self.timestamp) from scipy.special import factorial as fact y = fen.SpatialCoordinate(self.V.mesh())[1] powR, powI = [(self.kappa * np.sin(self.theta)) ** derI * k\ for k in getPowMinusj(y, derI)] mu2R, mu2I = np.real(mu(0, 0) ** 2.), np.imag(mu(0, 0) ** 2.) exprR = mu2R * powR - mu2I * powI exprI = mu2I * powR + mu2R * powI if derI >= 1: muR, muI = np.real(2. * mu(0, 0)), np.imag(2. * mu(0, 0)) powR, powI = [(self.kappa * np.sin(self.theta)) ** (derI - 1) * k\ * derI for k in getPowMinusj(y, derI - 1)] exprR += muR * powR - muI * powI exprI += muI * powR + muR * powI if derI >= 2: powR, powI = [(self.kappa * np.sin(self.theta)) ** (derI - 2) * k\ * derI * (derI - 1) for k in getPowMinusj(y, derI - 2)] exprR += powR exprI += powI fac = fact(derI) if self.verbosity >= 25: verbosityDepth("DEL", "Done assembling auxiliary expression.", timestamp = self.timestamp) return [exprR / fac, exprI / fac] def A(self, mu : paramVal = [], der : List[int] = 0) -> ScOp: """Assemble (derivative of) operator of linear system.""" mu = self.checkParameter(mu) if not hasattr(der, "__len__"): der = [der] * self.npar derI = hashD(der) self.autoSetDS() if derI <= 0 and self.As[0] is None: if self.verbosity >= 20: verbosityDepth("INIT", "Assembling operator term A0.", timestamp = self.timestamp) DirichletBC0 = fen.DirichletBC(self.V, fenZERO, self.DirichletBoundary) a0Re = fen.dot(self.u.dx(1), self.v.dx(1)) * fen.dx A0Re = fen.assemble(a0Re) DirichletBC0.apply(A0Re) A0ReMat = fen.as_backend_type(A0Re).mat() A0Rer, A0Rec, A0Rev = A0ReMat.getValuesCSR() self.As[0] = scsp.csr_matrix((A0Rev, A0Rec, A0Rer), shape = A0ReMat.size, dtype = np.complex) if self.verbosity >= 20: verbosityDepth("DEL", "Done assembling operator term.", timestamp = self.timestamp) - if derI <= 1 and self.As[1] is None: self.As[1] = 0. + if derI <= 1 and self.As[1] is None: + self.As[1] = self.checkAInBounds(-1) if derI <= 2 and self.As[2] is None: if self.verbosity >= 20: verbosityDepth("INIT", "Assembling operator term A2.", timestamp = self.timestamp) DirichletBC0 = fen.DirichletBC(self.V, fenZERO, self.DirichletBoundary) nRe, nIm = self.refractionIndex n2Re, n2Im = nRe * nRe - nIm * nIm, 2 * nRe * nIm k2Re, k2Im = np.real(self.omega ** 2), np.imag(self.omega ** 2) k2n2Re = k2Re * n2Re - k2Im * n2Im k2n2Im = k2Re * n2Im + k2Im * n2Re parsRe = self.iterReduceQuadratureDegree(zip([k2n2Re], ["kappaSquaredRefractionIndexSquaredReal"])) parsIm = self.iterReduceQuadratureDegree(zip([k2n2Im], ["kappaSquaredRefractionIndexSquaredImag"])) a2Re = (fen.dot(self.u.dx(0), self.v.dx(0)) - k2n2Re * fen.dot(self.u, self.v)) * fen.dx a2Im = - k2n2Im * fen.dot(self.u, self.v) * fen.dx A2Re = fen.assemble(a2Re, form_compiler_parameters = parsRe) A2Im = fen.assemble(a2Im, form_compiler_parameters = parsIm) DirichletBC0.zero(A2Re) DirichletBC0.zero(A2Im) A2ReMat = fen.as_backend_type(A2Re).mat() A2ImMat = fen.as_backend_type(A2Im).mat() A2Rer, A2Rec, A2Rev = A2ReMat.getValuesCSR() A2Imr, A2Imc, A2Imv = A2ImMat.getValuesCSR() self.As[2] = (scsp.csr_matrix((A2Rev, A2Rec, A2Rer), shape = A2ReMat.size) + 1.j * scsp.csr_matrix((A2Imv, A2Imc, A2Imr), shape = A2ImMat.size)) if self.verbosity >= 20: verbosityDepth("DEL", "Done assembling operator term.", timestamp = self.timestamp) return self._assembleA(mu, der, derI) def b(self, mu : paramVal = [], der : List[int] = 0, homogeneized : bool = False) -> Np1D: """Assemble (derivative of) RHS of linear system.""" mu = self.checkParameter(mu) if not hasattr(der, "__len__"): der = [der] * self.npar derI = hashD(der) nbsTot = self.nbsH if homogeneized else self.nbs bs = self.bsH if homogeneized else self.bs if homogeneized and self.mu0 != self.mu0BC: self.liftDirichletData(self.mu0) for j in range(derI, nbsTot): if bs[j] is None: if self.verbosity >= 20: verbosityDepth("INIT", ("Assembling forcing term " "b{}.").format(j), timestamp = self.timestamp) if j < self.nbs: fRe, fIm = self.getForcingTerm(self.mu0) cRe, cIm = self.getExtraFactorB(self.mu0, j) cfRe, cfIm = cRe * fRe - cIm * fIm, cRe * fIm + cIm * fRe else: cfRe, cfIm = fenZERO, fenZERO parsRe = self.iterReduceQuadratureDegree(zip([cfRe], ["forcingTermDer{}Real".format(j)])) parsIm = self.iterReduceQuadratureDegree(zip([cfIm], ["forcingTermDer{}Imag".format(j)])) L0Re = fen.dot(cfRe, self.v) * fen.dx L0Im = fen.dot(cfIm, self.v) * fen.dx b0Re = fen.assemble(L0Re, form_compiler_parameters = parsRe) b0Im = fen.assemble(L0Im, form_compiler_parameters = parsIm) DirichletBC0 = fen.DirichletBC(self.V, fenZERO, self.DirichletBoundary) DirichletBC0.apply(b0Re) DirichletBC0.apply(b0Im) b = np.array(b0Re[:] + 1.j * b0Im[:], dtype = np.complex) if homogeneized: Ader = self.A(self.mu0, hashI(j, self.npar)) b -= Ader.dot(self.liftedDirichletDatum) if homogeneized: self.bsH[j] = b else: self.bs[j] = b if self.verbosity >= 20: verbosityDepth("DEL", "Done assembling forcing term.", timestamp = self.timestamp) return self._assembleb(mu, der, derI, homogeneized, self.mu0) diff --git a/rrompy/hfengines/vector_linear_problem/bidimensional/linear_elasticity_beam_elasticity_constants.py b/rrompy/hfengines/vector_linear_problem/bidimensional/linear_elasticity_beam_elasticity_constants.py index e43a097..4c3aa5d 100644 --- a/rrompy/hfengines/vector_linear_problem/bidimensional/linear_elasticity_beam_elasticity_constants.py +++ b/rrompy/hfengines/vector_linear_problem/bidimensional/linear_elasticity_beam_elasticity_constants.py @@ -1,149 +1,162 @@ # Copyright (C) 2018 by the RROMPy authors # # This file is part of RROMPy. # # RROMPy is free software: you can redistribute it and/or modify # it under the terms of the GNU Lesser General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # RROMPy is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU Lesser General Public License for more details. # # You should have received a copy of the GNU Lesser General Public License # along with RROMPy. If not, see <http://www.gnu.org/licenses/>. # import numpy as np import scipy.sparse as scsp import fenics as fen from rrompy.hfengines.vector_linear_problem.\ linear_elasticity_beam_poisson_ratio import LinearElasticityBeamPoissonRatio from rrompy.solver.fenics import fenZEROS from rrompy.utilities.base.types import Np1D, List, ScOp, paramVal from rrompy.utilities.base import verbosityDepth from rrompy.utilities.exception_manager import RROMPyException __all__ = ['LinearElasticityBeamElasticityConstants'] class LinearElasticityBeamElasticityConstants( LinearElasticityBeamPoissonRatio): """ Solver for linear elasticity problem of a beam subject to its own weight, with parametric Joung modulus and Poisson's ratio. - div(lambda_ * div(u) * I + 2 * mu_ * epsilon(u)) = rho_ * g in \Omega u = 0 on \Gamma_D \partial_nu = 0 on \Gamma_N """ def __init__(self, n:int, rho_:float, g:float, E0:float, nu0:float, length:float, degree_threshold : int = np.inf, verbosity : int = 10, timestamp : bool = True): super().__init__(mu0 = [nu0, E0], degree_threshold = degree_threshold, verbosity = verbosity, timestamp = timestamp) self.nAs, self.nbs = 5, 4 mesh = fen.RectangleMesh(fen.Point(0., 0.), fen.Point(length, 1), n, max(int(n / length), 1)) self.V = fen.VectorFunctionSpace(mesh, "P", 1) self.forcingTerm = [fen.Constant((0., - rho_ * g)), fenZEROS(2)] self.DirichletBoundary = lambda x, on_b: on_b and fen.near(x[0], 0.) self.NeumannBoundary = "REST" def A(self, mu : paramVal = [], der : List[int] = 0) -> ScOp: """Assemble (derivative of) operator of linear system.""" mu = self.checkParameter(mu) if not hasattr(der, "__len__"): der = [der] * self.npar derI = hashD(der) self.autoSetDS() - if derI <= 0 and self.As[0] is None: self.As[0] = 0. - if derI <= 1 and self.As[1] is None: self.As[1] = 0. + for j in [0, 1, 3]: + if derI <= j and self.As[j] is None: + self.As[j] = self.checkAInBounds(-1) if derI <= 4 and self.As[2] is None: if self.verbosity >= 20: verbosityDepth("INIT", "Assembling operator term A2.", timestamp = self.timestamp) DirichletBC0 = fen.DirichletBC(self.V, fenZEROS(2), self.DirichletBoundary) epsilon = lambda u: .5 * (fen.grad(u) + fen.nabla_grad(u)) a0Re = fen.inner(epsilon(self.u), epsilon(self.v)) * fen.dx A0Re = fen.assemble(a0Re) DirichletBC0.apply(A0Re) A0ReMat = fen.as_backend_type(A0Re).mat() A0Rer, A0Rec, A0Rev = A0ReMat.getValuesCSR() self.As[2] = scsp.csr_matrix((A0Rev, A0Rec, A0Rer), shape = A0ReMat.size, dtype = np.complex) if self.verbosity >= 20: verbosityDepth("DEL", "Done assembling operator term.", timestamp = self.timestamp) - if derI <= 3 and self.As[3] is None: self.As[3] = 0. if derI <= 4 and self.As[4] is None: if self.verbosity >= 20: verbosityDepth("INIT", "Assembling operator term A4.", timestamp = self.timestamp) DirichletBC0 = fen.DirichletBC(self.V, fenZEROS(2), self.DirichletBoundary) a1Re = fen.div(self.u) * fen.div(self.v) * fen.dx A1Re = fen.assemble(a1Re) - DirichletBC0.apply(A1Re) + DirichletBC0.zero(A1Re) A1ReMat = fen.as_backend_type(A1Re).mat() A1Rer, A1Rec, A1Rev = A1ReMat.getValuesCSR() self.As[4] = 2. * (scsp.csr_matrix((A1Rev, A1Rec, A1Rer), shape = A1ReMat.size, dtype = np.complex) - self.As[2]) if self.verbosity >= 20: verbosityDepth("DEL", "Done assembling operator term.", timestamp = self.timestamp) return self._assembleA(mu, der, derI) def b(self, mu : paramVal = [], der : List[int] = 0, homogeneized : bool = False) -> Np1D: """Assemble (derivative of) RHS of linear system.""" RROMPyAssert(homogeneized, False, "Homogeneized") mu = self.checkParameter(mu) if not hasattr(der, "__len__"): der = [der] * self.npar derI = hashD(der) if derI <= 3 and self.bs[0] is None: self.autoSetDS() if self.verbosity >= 20: verbosityDepth("INIT", "Assembling forcing term b0.", timestamp = self.timestamp) fRe, fIm = self.forcingTerm parsRe = self.iterReduceQuadratureDegree(zip([fRe], ["forcingTermReal"])) parsIm = self.iterReduceQuadratureDegree(zip([fIm], ["forcingTermImag"])) L0Re = fen.inner(fRe, self.v) * fen.dx L0Im = fen.inner(fIm, self.v) * fen.dx b0Re = fen.assemble(L0Re, form_compiler_parameters = parsRe) b0Im = fen.assemble(L0Im, form_compiler_parameters = parsIm) DBCR = fen.DirichletBC(self.V, self.DirichletDatum[0], self.DirichletBoundary) DBCI = fen.DirichletBC(self.V, self.DirichletDatum[1], self.DirichletBoundary) DBCR.apply(b0Re) DBCI.apply(b0Im) self.bs[0] = np.array(b0Re[:] + 1.j * b0Im[:], dtype = np.complex) - if derI <= 1 and self.bs[1] is None: + if derI <= 3 and self.bs[1] is None: if self.verbosity >= 20: verbosityDepth("INIT", "Assembling forcing term b1.", timestamp = self.timestamp) - self.bs[1] = - self.bs[0] + fRe, fIm = self.forcingTerm + parsRe = self.iterReduceQuadratureDegree(zip([fRe], + ["forcingTermReal"])) + parsIm = self.iterReduceQuadratureDegree(zip([fIm], + ["forcingTermImag"])) + L1Re = - fen.inner(fRe, self.v) * fen.dx + L1Im = - fen.inner(fIm, self.v) * fen.dx + b1Re = fen.assemble(L1Re, form_compiler_parameters = parsRe) + b1Im = fen.assemble(L1Im, form_compiler_parameters = parsIm) + DBC0 = fen.DirichletBC(self.V, fenZEROS(2), self.DirichletBoundary) + DBC0.apply(b1Re) + DBC1.apply(b1Im) + self.bs[1] = np.array(b1Re[:] + 1.j * b1Im[:], dtype = np.complex) if self.verbosity >= 20: verbosityDepth("DEL", "Done assembling forcing term.", timestamp = self.timestamp) - if derI <= 2 and self.bs[2] is None: self.bs[2] = 0. + if derI <= 2 and self.bs[2] is None: + self.bs[2] = self.checkbInBounds(-1) if derI <= 3 and self.bs[3] is None: if self.verbosity >= 20: verbosityDepth("INIT", "Assembling forcing term b3.", timestamp = self.timestamp) - self.bs[3] = - 2. * self.bs[0] + self.bs[3] = 2. * self.bs[1] if self.verbosity >= 20: verbosityDepth("DEL", "Done assembling forcing term.", timestamp = self.timestamp) return self._assembleb(mu, der, derI, homogeneized) diff --git a/rrompy/hfengines/vector_linear_problem/linear_elasticity_beam_poisson_ratio.py b/rrompy/hfengines/vector_linear_problem/linear_elasticity_beam_poisson_ratio.py index 8b80487..0b9515e 100644 --- a/rrompy/hfengines/vector_linear_problem/linear_elasticity_beam_poisson_ratio.py +++ b/rrompy/hfengines/vector_linear_problem/linear_elasticity_beam_poisson_ratio.py @@ -1,148 +1,161 @@ # Copyright (C) 2018 by the RROMPy authors # # This file is part of RROMPy. # # RROMPy is free software: you can redistribute it and/or modify # it under the terms of the GNU Lesser General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # RROMPy is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU Lesser General Public License for more details. # # You should have received a copy of the GNU Lesser General Public License # along with RROMPy. If not, see <http://www.gnu.org/licenses/>. # import numpy as np import scipy.sparse as scsp import fenics as fen from .linear_elasticity_problem_engine import LinearElasticityProblemEngine from rrompy.solver.fenics import fenZEROS from rrompy.utilities.base.types import Np1D, List, ScOp, paramVal from rrompy.utilities.base import verbosityDepth from rrompy.utilities.exception_manager import RROMPyAssert from rrompy.utilities.poly_fitting.polynomial import ( hashDerivativeToIdx as hashD) __all__ = ['LinearElasticityBeamPoissonRatio'] class LinearElasticityBeamPoissonRatio(LinearElasticityProblemEngine): """ Solver for linear elasticity problem of a beam subject to its own weight, with parametric Poisson's ratio. - div(lambda_ * div(u) * I + 2 * mu_ * epsilon(u)) = rho_ * g in \Omega u = 0 on \Gamma_D \partial_nu = 0 on \Gamma_N """ def __init__(self, n:int, rho_:float, g:float, E:float, nu0:float, length:float, degree_threshold : int = np.inf, verbosity : int = 10, timestamp : bool = True): super().__init__(mu0 = [nu0], degree_threshold = degree_threshold, verbosity = verbosity, timestamp = timestamp) self.nAs, self.nbs = 2, 3 self.E_ = E mesh = fen.RectangleMesh(fen.Point(0., 0.), fen.Point(length, 1), n, max(int(n / length), 1)) self.V = fen.VectorFunctionSpace(mesh, "P", 1) self.forcingTerm = [fen.Constant((0., - rho_ * g / E)), fenZEROS(2)] self.DirichletBoundary = lambda x, on_b: on_b and fen.near(x[0], 0.) self.NeumannBoundary = "REST" def A(self, mu : paramVal = [], der : List[int] = 0) -> ScOp: """Assemble (derivative of) operator of linear system.""" mu = self.checkParameter(mu) if not hasattr(der, "__len__"): der = [der] * self.npar derI = hashD(der) self.autoSetDS() if derI <= 1 and self.As[0] is None: if self.verbosity >= 20: verbosityDepth("INIT", "Assembling operator term A0.", timestamp = self.timestamp) DirichletBC0 = fen.DirichletBC(self.V, fenZEROS(2), self.DirichletBoundary) epsilon = lambda u: .5 * (fen.grad(u) + fen.nabla_grad(u)) a0Re = self.E_ * fen.inner(epsilon(self.u), epsilon(self.v)) * fen.dx A0Re = fen.assemble(a0Re) DirichletBC0.apply(A0Re) A0ReMat = fen.as_backend_type(A0Re).mat() A0Rer, A0Rec, A0Rev = A0ReMat.getValuesCSR() self.As[0] = scsp.csr_matrix((A0Rev, A0Rec, A0Rer), shape = A0ReMat.size, dtype = np.complex) if self.verbosity >= 20: verbosityDepth("DEL", "Done assembling operator term.", timestamp = self.timestamp) if derI <= 1 and self.As[1] is None: if self.verbosity >= 20: verbosityDepth("INIT", "Assembling operator term A1.", timestamp = self.timestamp) DirichletBC0 = fen.DirichletBC(self.V, fenZEROS(2), self.DirichletBoundary) a1Re = self.E_ * fen.div(self.u) * fen.div(self.v) * fen.dx A1Re = fen.assemble(a1Re) - DirichletBC0.apply(A1Re) + DirichletBC0.zero(A1Re) A1ReMat = fen.as_backend_type(A1Re).mat() A1Rer, A1Rec, A1Rev = A1ReMat.getValuesCSR() self.As[1] = 2. * (scsp.csr_matrix((A1Rev, A1Rec, A1Rer), shape = A1ReMat.size, dtype = np.complex) - self.As[0]) if self.verbosity >= 20: verbosityDepth("DEL", "Done assembling operator term.", timestamp = self.timestamp) return self._assembleA(mu, der, derI) def b(self, mu : paramVal = [], der : List[int] = 0, homogeneized : bool = False) -> Np1D: """Assemble (derivative of) RHS of linear system.""" RROMPyAssert(homogeneized, False, "Homogeneized") mu = self.checkParameter(mu) if not hasattr(der, "__len__"): der = [der] * self.npar derI = hashD(der) - if derI <= 2 and self.bs[0] is None: + if derI <= 0 and self.bs[0] is None: self.autoSetDS() if self.verbosity >= 20: verbosityDepth("INIT", "Assembling forcing term b0.", timestamp = self.timestamp) fRe, fIm = self.forcingTerm parsRe = self.iterReduceQuadratureDegree(zip([fRe], ["forcingTermReal"])) parsIm = self.iterReduceQuadratureDegree(zip([fIm], ["forcingTermImag"])) L0Re = fen.inner(fRe, self.v) * fen.dx L0Im = fen.inner(fIm, self.v) * fen.dx b0Re = fen.assemble(L0Re, form_compiler_parameters = parsRe) b0Im = fen.assemble(L0Im, form_compiler_parameters = parsIm) DBCR = fen.DirichletBC(self.V, self.DirichletDatum[0], self.DirichletBoundary) DBCI = fen.DirichletBC(self.V, self.DirichletDatum[1], self.DirichletBoundary) DBCR.apply(b0Re) DBCI.apply(b0Im) self.bs[0] = np.array(b0Re[:] + 1.j * b0Im[:], dtype = np.complex) - if derI <= 1 and self.bs[1] is None: + if derI <= 2 and self.bs[1] is None: if self.verbosity >= 20: verbosityDepth("INIT", "Assembling forcing term b1.", timestamp = self.timestamp) - self.bs[1] = - self.bs[0] + fRe, fIm = self.forcingTerm + parsRe = self.iterReduceQuadratureDegree(zip([fRe], + ["forcingTermReal"])) + parsIm = self.iterReduceQuadratureDegree(zip([fIm], + ["forcingTermImag"])) + L1Re = - fen.inner(fRe, self.v) * fen.dx + L1Im = - fen.inner(fIm, self.v) * fen.dx + b1Re = fen.assemble(L1Re, form_compiler_parameters = parsRe) + b1Im = fen.assemble(L1Im, form_compiler_parameters = parsIm) + DBC0 = fen.DirichletBC(self.V, fenZEROS(2), + self.DirichletBoundary) + DBC0.apply(b1Re) + DBC0.apply(b1Im) + self.bs[1] = np.array(b1Re[:] + 1.j * b1Im[:], dtype = np.complex) if self.verbosity >= 20: verbosityDepth("DEL", "Done assembling forcing term.", timestamp = self.timestamp) if derI <= 2 and self.bs[2] is None: if self.verbosity >= 20: verbosityDepth("INIT", "Assembling forcing term b2.", timestamp = self.timestamp) - self.bs[2] = - 2. * self.bs[0] + self.bs[2] = 2. * self.bs[1] if self.verbosity >= 20: verbosityDepth("DEL", "Done assembling forcing term.", timestamp = self.timestamp) return self._assembleb(mu, der, derI, homogeneized) diff --git a/rrompy/reduction_methods/base/generic_approximant.py b/rrompy/reduction_methods/base/generic_approximant.py index 0a604b8..5439cbf 100644 --- a/rrompy/reduction_methods/base/generic_approximant.py +++ b/rrompy/reduction_methods/base/generic_approximant.py @@ -1,909 +1,912 @@ # 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 <http://www.gnu.org/licenses/>. # from abc import abstractmethod import numpy as np from itertools import product as iterprod from copy import deepcopy as copy from os import remove as osrm from rrompy.sampling.linear_problem import (SamplingEngineLinear, SamplingEngineLinearPOD) from rrompy.utilities.base.types import (Np1D, DictAny, HFEng, List, ListAny, strLst, paramVal, paramList, sampList) from rrompy.utilities.base import purgeDict, verbosityDepth, getNewFilename from rrompy.utilities.exception_manager import (RROMPyException, RROMPyAssert, RROMPy_READY, RROMPy_FRAGILE) from rrompy.utilities.base import pickleDump, pickleLoad from rrompy.parameter import (emptyParameterList, checkParameter, checkParameterList) from rrompy.sampling import sampleList, emptySampleList __all__ = ['GenericApproximant'] def addNormFieldToClass(self, fieldName): def objFunc(self, mu:paramList, homogeneized : bool = False) -> float: uV = getattr(self.__class__, "get" + fieldName)(self, mu, homogeneized) val = self.HFEngine.norm(uV) return val setattr(self.__class__, "norm" + fieldName, objFunc) def addPlotFieldToClass(self, fieldName): def objFunc(self, mu:paramList, name : str = fieldName, save : str = None, what : strLst = 'all', saveFormat : str = "eps", saveDPI : int = 100, show : bool = True, homogeneized : bool = False, **figspecs): uV = getattr(self.__class__, "get" + fieldName)(self, mu, homogeneized) for j, u in enumerate(uV): self.HFEngine.plot(u, name = name + str(j), save = save, what = what, saveFormat = saveFormat, saveDPI = saveDPI, show = show, **figspecs) setattr(self.__class__, "plot" + fieldName, objFunc) def addOutParaviewFieldToClass(self, fieldName): def objFunc(self, mu:paramVal, name : str = fieldName, filename : str = "out", time : float = 0., what : strLst = 'all', forceNewFile : bool = True, folder : bool = False, filePW = None, homogeneized : bool = False): if not hasattr(self.HFEngine, "outParaview"): raise RROMPyException(("High fidelity engine cannot output to " "Paraview.")) uV = getattr(self.__class__, "get" + fieldName)(self, mu, homogeneized) for j, u in enumerate(uV): self.HFEngine.outParaview(u, name = name + str(j), filename = filename, time = time, what = what, forceNewFile = forceNewFile, folder = folder, filePW = filePW) setattr(self.__class__, "outParaview" + fieldName, objFunc) def addOutParaviewTimeDomainFieldToClass(self, fieldName): def objFunc(self, mu:paramVal, omega : float = None, timeFinal : float = None, periodResolution : int = 20, name : str = fieldName, filename : str = "out", forceNewFile : bool = True, folder : bool = False, homogeneized : bool = False): if not hasattr(self.HFEngine, "outParaviewTimeDomain"): raise RROMPyException(("High fidelity engine cannot output to " "Paraview.")) uV = getattr(self.__class__, "get" + fieldName)(self, mu, homogeneized) if omega is None: omega = np.real(mu) for j, u in enumerate(uV): self.HFEngine.outParaviewTimeDomain(u, omega = omega, timeFinal = timeFinal, periodResolution = periodResolution, name = name + str(j), filename = filename, forceNewFile = forceNewFile, folder = folder) setattr(self.__class__, "outParaviewTimeDomain" + fieldName, objFunc) class GenericApproximant: """ ABSTRACT ROM approximant computation for parametric problems. Args: HFEngine: HF problem solver. mu0(optional): Default parameter. Defaults to 0. approxParameters(optional): Dictionary containing values for main parameters of approximant. Recognized keys are: - 'POD': whether to compute POD of snapshots; defaults to True; - 'S': total number of samples current approximant relies upon. Defaults to empty dict. homogeneized(optional): Whether to homogeneize Dirichlet BCs. Defaults to False. verbosity(optional): Verbosity level. Defaults to 10. Attributes: HFEngine: HF problem solver. trainedModel: Trained model evaluator. mu0: Default parameter. homogeneized: Whether to homogeneize Dirichlet BCs. approxParameters: Dictionary containing values for main parameters of approximant. Recognized keys are in parameterList{Soft,Critical}. parameterListSoft: Recognized keys of soft approximant parameters: - 'POD': whether to compute POD of snapshots. parameterListCritical: Recognized keys of critical approximant parameters: - 'S': total number of samples current approximant relies upon. verbosity: Verbosity level. POD: Whether to compute POD of snapshots. S: Number of solution snapshots over which current approximant is based upon. samplingEngine: Sampling engine. uHF: High fidelity solution(s) with parameter(s) lastSolvedHF as sampleList. lastSolvedHF: Parameter(s) corresponding to last computed high fidelity solution(s) as parameterList. uAppReduced: Reduced approximate solution(s) with parameter(s) lastSolvedApp as sampleList. lastSolvedAppReduced: Parameter(s) corresponding to last computed reduced approximate solution(s) as parameterList. uApp: Approximate solution(s) with parameter(s) lastSolvedApp as sampleList. lastSolvedApp: Parameter(s) corresponding to last computed approximate solution(s) as parameterList. """ __all__ += [ftype + dtype for ftype, dtype in iterprod( ["norm", "plot", "outParaview", "outParaviewTimeDomain"], ["HF", "RHS", "Approx", "Res", "Err"])] def __init__(self, HFEngine:HFEng, mu0 : paramVal = None, approxParameters : DictAny = {}, homogeneized : bool = False, verbosity : int = 10, timestamp : bool = True): self._preInit() self._mode = RROMPy_READY self.verbosity = verbosity self.timestamp = timestamp if self.verbosity >= 10: verbosityDepth("INIT", ("Initializing approximant engine of " "type {}.").format(self.name()), timestamp = self.timestamp) self._HFEngine = HFEngine self.trainedModel = None self.lastSolvedHF = emptyParameterList() self.uHF = emptySampleList() self._addParametersToList(["POD"], [True], ["S"], [[1]]) if mu0 is None: if hasattr(self.HFEngine, "mu0"): self.mu0 = checkParameter(self.HFEngine.mu0) else: raise RROMPyException(("Center of approximation cannot be " "inferred from HF engine. Parameter " "required")) else: self.mu0 = checkParameter(mu0, self.HFEngine.npar) self.resetSamples() self.homogeneized = homogeneized self.approxParameters = approxParameters self._postInit() ### add norm{HF,RHS,Approx,Res,Err} methods """ Compute norm of * at arbitrary parameter. Args: mu: Target parameter. homogeneized(optional): Whether to remove Dirichlet BC. Defaults to False. Returns: Target norm of *. """ - for objName in ["HF", "RHS", "Approx", "Res", "Err"]: + for objName in ["HF", "RHS", "Res", "Err"]: addNormFieldToClass(self, objName) + if not hasattr(self, "normApprox"): + addNormFieldToClass(self, "Approx") ### add plot{HF,RHS,Approx,Res,Err} methods """ Do some nice plots of * at arbitrary parameter. Args: mu: Target parameter. name(optional): Name to be shown as title of the plots. Defaults to 'u'. what(optional): Which plots to do. If list, can contain 'ABS', 'PHASE', 'REAL', 'IMAG'. If str, same plus wildcard 'ALL'. Defaults to 'ALL'. save(optional): Where to save plot(s). Defaults to None, i.e. no saving. saveFormat(optional): Format for saved plot(s). Defaults to "eps". saveDPI(optional): DPI for saved plot(s). Defaults to 100. show(optional): Whether to show figure. Defaults to True. homogeneized(optional): Whether to remove Dirichlet BC. Defaults to False. figspecs(optional key args): Optional arguments for matplotlib figure creation. """ for objName in ["HF", "RHS", "Approx", "Res", "Err"]: addPlotFieldToClass(self, objName) ### add outParaview{HF,RHS,Approx,Res,Err} methods """ Output * to ParaView file. Args: mu: Target parameter. name(optional): Base name to be used for data output. filename(optional): Name of output file. time(optional): Timestamp. what(optional): Which plots to do. If list, can contain 'MESH', 'ABS', 'PHASE', 'REAL', 'IMAG'. If str, same plus wildcard 'ALL'. Defaults to 'ALL'. forceNewFile(optional): Whether to create new output file. filePW(optional): Fenics File entity (for time series). homogeneized(optional): Whether to remove Dirichlet BC. Defaults to False. """ for objName in ["HF", "RHS", "Approx", "Res", "Err"]: addOutParaviewFieldToClass(self, objName) ### add outParaviewTimeDomain{HF,RHS,Approx,Res,Err} methods """ Output * to ParaView file, converted to time domain. Args: mu: Target parameter. omega(optional): frequency. timeFinal(optional): final time of simulation. periodResolution(optional): number of time steps per period. name(optional): Base name to be used for data output. filename(optional): Name of output file. forceNewFile(optional): Whether to create new output file. homogeneized(optional): Whether to remove Dirichlet BC. Defaults to False. """ for objName in ["HF", "RHS", "Approx", "Res", "Err"]: addOutParaviewTimeDomainFieldToClass(self, objName) def _preInit(self): if not hasattr(self, "depth"): self.depth = 0 else: self.depth += 1 @property def parameterList(self): """Value of parameterListSoft + parameterListCritical.""" return self.parameterListSoft + self.parameterListCritical def _addParametersToList(self, whatSoft:strLst, defaultSoft:ListAny, whatCritical : strLst = [], defaultCritical : ListAny = [], toBeExcluded : strLst = []): if not hasattr(self, "parameterToBeExcluded"): self.parameterToBeExcluded = [] self.parameterToBeExcluded += toBeExcluded if not hasattr(self, "parameterListSoft"): self.parameterListSoft = [] if not hasattr(self, "parameterDefaultSoft"): self.parameterDefaultSoft = {} if not hasattr(self, "parameterListCritical"): self.parameterListCritical = [] if not hasattr(self, "parameterDefaultCritical"): self.parameterDefaultCritical = {} for j, what in enumerate(whatSoft): if what not in self.parameterToBeExcluded: self.parameterListSoft += [what] self.parameterDefaultSoft[what] = defaultSoft[j] for j, what in enumerate(whatCritical): if what not in self.parameterToBeExcluded: self.parameterListCritical += [what] self.parameterDefaultCritical[what] = defaultCritical[j] def _postInit(self): if self.depth == 0: if self.verbosity >= 10: verbosityDepth("DEL", "Done initializing.", timestamp = self.timestamp) del self.depth else: self.depth -= 1 def name(self) -> str: return self.__class__.__name__ def __str__(self) -> str: return self.name() def __repr__(self) -> str: return self.__str__() + " at " + hex(id(self)) def setupSampling(self): """Setup sampling engine.""" RROMPyAssert(self._mode, message = "Cannot setup sampling engine.") if not hasattr(self, "_POD") or self._POD is None: return if self.POD: SamplingEngine = SamplingEngineLinearPOD else: SamplingEngine = SamplingEngineLinear self.samplingEngine = SamplingEngine(self.HFEngine, - verbosity = self.verbosity) + verbosity = self.verbosity, + allowRepeatedSamples = True) @property def HFEngine(self): """Value of HFEngine.""" return self._HFEngine @HFEngine.setter def HFEngine(self, HFEngine): raise RROMPyException("Cannot change HFEngine.") @property def mu0(self): """Value of mu0.""" return self._mu0 @mu0.setter def mu0(self, mu0): mu0 = checkParameter(mu0) if not hasattr(self, "_mu0") or mu0 != self.mu0: self.resetSamples() self._mu0 = mu0 @property def npar(self): """Number of parameters.""" return self.mu0.shape[1] @property def approxParameters(self): """Value of approximant parameters.""" return self._approxParameters @approxParameters.setter def approxParameters(self, approxParams): if not hasattr(self, "approxParameters"): self._approxParameters = {} approxParameters = purgeDict(approxParams, self.parameterList, dictname = self.name() + ".approxParameters", baselevel = 1) keyList = list(approxParameters.keys()) for key in self.parameterListCritical: if key in keyList: setattr(self, "_" + key, self.parameterDefaultCritical[key]) for key in self.parameterListSoft: if key in keyList: setattr(self, "_" + key, self.parameterDefaultSoft[key]) fragile = False for key in self.parameterListCritical: if key in keyList: val = approxParameters[key] else: val = getattr(self, "_" + key, None) if val is None: val = self.parameterDefaultCritical[key] getattr(self.__class__, key, None).fset(self, val) fragile = fragile or val is None for key in self.parameterListSoft: if key in keyList: val = approxParameters[key] else: val = getattr(self, "_" + key, None) if val is None: val = self.parameterDefaultSoft[key] getattr(self.__class__, key, None).fset(self, val) if fragile: self._mode = RROMPy_FRAGILE @property def POD(self): """Value of POD.""" return self._POD @POD.setter def POD(self, POD): if hasattr(self, "_POD"): PODold = self.POD else: PODold = -1 self._POD = POD self._approxParameters["POD"] = self.POD if PODold != self.POD: self.samplingEngine = None self.resetSamples() @property def S(self): """Value of S.""" return self._S @S.setter def S(self, S): if not hasattr(S, "__len__"): S = [S] if any([s <= 0 for s in S]): raise RROMPyException("S must be positive.") if hasattr(self, "_S") and self._S is not None: Sold = tuple(self.S) else: Sold = -1 self._S = S self._approxParameters["S"] = self.S if Sold != tuple(self.S): self.resetSamples() @property def homogeneized(self): """Value of homogeneized.""" return self._homogeneized @homogeneized.setter def homogeneized(self, homogeneized): if not hasattr(self, "_homogeneized"): self._homogeneized = None if homogeneized != self.homogeneized: self._homogeneized = homogeneized self.resetSamples() @property def trainedModel(self): """Value of trainedModel.""" return self._trainedModel @trainedModel.setter def trainedModel(self, trainedModel): self._trainedModel = trainedModel if self._trainedModel is not None: self._trainedModel.lastSolvedAppReduced = emptyParameterList() self._trainedModel.lastSolvedApp = emptyParameterList() self.lastSolvedAppReduced = emptyParameterList() self.lastSolvedApp = emptyParameterList() self.uAppReduced = emptySampleList() self.uApp = emptySampleList() def resetSamples(self): if hasattr(self, "samplingEngine") and self.samplingEngine is not None: self.samplingEngine.resetHistory() else: self.setupSampling() self._mode = RROMPy_READY def plotSamples(self, name : str = "u", save : str = None, what : strLst = 'all', saveFormat : str = "eps", saveDPI : int = 100, **figspecs): """ Do some nice plots of the samples. Args: name(optional): Name to be shown as title of the plots. Defaults to 'u'. what(optional): Which plots to do. If list, can contain 'ABS', 'PHASE', 'REAL', 'IMAG'. If str, same plus wildcard 'ALL'. Defaults to 'ALL'. save(optional): Where to save plot(s). Defaults to None, i.e. no saving. saveFormat(optional): Format for saved plot(s). Defaults to "eps". saveDPI(optional): DPI for saved plot(s). Defaults to 100. figspecs(optional key args): Optional arguments for matplotlib figure creation. """ RROMPyAssert(self._mode, message = "Cannot plot samples.") self.samplingEngine.plotSamples(name = name, save = save, what = what, saveFormat = saveFormat, saveDPI = saveDPI, **figspecs) def outParaviewSamples(self, name : str = "u", filename : str = "out", times : Np1D = None, what : strLst = 'all', forceNewFile : bool = True, folders : bool = False, filePW = None): """ Output samples to ParaView file. Args: name(optional): Base name to be used for data output. filename(optional): Name of output file. times(optional): Timestamps. what(optional): Which plots to do. If list, can contain 'MESH', 'ABS', 'PHASE', 'REAL', 'IMAG'. If str, same plus wildcard 'ALL'. Defaults to 'ALL'. forceNewFile(optional): Whether to create new output file. folders(optional): Whether to split output in folders. filePW(optional): Fenics File entity (for time series). """ RROMPyAssert(self._mode, message = "Cannot output samples.") self.samplingEngine.outParaviewSamples(name = name, filename = filename, times = times, what = what, forceNewFile = forceNewFile, folders = folders, filePW = filePW) def outParaviewTimeDomainSamples(self, omegas : Np1D = None, timeFinal : Np1D = None, periodResolution : int = 20, name : str = "u", filename : str = "out", forceNewFile : bool = True, folders : bool = False): """ Output samples to ParaView file, converted to time domain. Args: omegas(optional): frequencies. timeFinal(optional): final time of simulation. periodResolution(optional): number of time steps per period. name(optional): Base name to be used for data output. filename(optional): Name of output file. forceNewFile(optional): Whether to create new output file. folders(optional): Whether to split output in folders. """ RROMPyAssert(self._mode, message = "Cannot output samples.") self.samplingEngine.outParaviewTimeDomainSamples(omegas = omegas, timeFinal = timeFinal, periodResolution = periodResolution, name = name, filename = filename, forceNewFile = forceNewFile, folders = folders) def setSamples(self, samplingEngine): """Copy samplingEngine and samples.""" if self.verbosity >= 10: verbosityDepth("INIT", "Transfering samples.", timestamp = self.timestamp) - self.samplingEngine = samplingEngine + self.samplingEngine = copy(samplingEngine) if self.verbosity >= 10: verbosityDepth("DEL", "Done transfering samples.", timestamp = self.timestamp) def setTrainedModel(self, model): """Deepcopy approximation from trained model.""" if hasattr(model, "storeTrainedModel"): verb = model.verbosity model.verbosity = 0 fileOut = model.storeTrainedModel() model.verbosity = verb else: try: fileOut = getNewFilename("trained_model", "pkl") pickleDump(model.data.__dict__, fileOut) except: raise RROMPyException(("Failed to store model data. Parameter " "model must have either " "storeTrainedModel or " "data.__dict__ properties.")) self.loadTrainedModel(fileOut) osrm(fileOut) @abstractmethod def setupApprox(self): """ Setup approximant. (ABSTRACT) Any specialization should include something like if self.checkComputedApprox(): return RROMPyAssert(self._mode, message = "Cannot setup approximant.") ... self.trainedModel = ... self.trainedModel.data = ... self.trainedModel.data.approxParameters = copy( self.approxParameters) """ pass def checkComputedApprox(self) -> bool: """ Check if setup of new approximant is not needed. Returns: True if new setup is not needed. False otherwise. """ return self._mode == RROMPy_FRAGILE or (self.trainedModel is not None and self.trainedModel.data.approxParameters == self.approxParameters) def setHF(self, muHF:paramList, uHF:sampleList, append : bool = False) -> List[int]: """Assign high fidelity solution.""" newSolvedHF, _ = checkParameterList(muHF, self.npar) newuHF = sampleList(uHF) if append: self.lastSolvedHF.append(newSolvedHF) self.uHF.append(newuHF) return list(range(len(self.uHF) - len(newuHF), len(self.uHF))) self.lastSolvedHF, _ = checkParameterList(newSolvedHF, self.npar) self.uHF = sampleList(newuHF) return list(range(len(self.uHF))) def evalHF(self, mu:paramList, append : bool = False, prune : bool = True) -> List[int]: """ Find high fidelity solution with original parameters and arbitrary parameter. Args: mu: Target parameter. append(optional): Whether to append new HF solutions to old ones. prune(optional): Whether to remove duplicates of already appearing HF solutions. """ mu, _ = checkParameterList(mu, self.npar) idx = np.empty(len(mu), dtype = np.int) if prune: jExtra = np.zeros(len(mu), dtype = bool) muKeep = emptyParameterList() muExtra = copy(muKeep) for j in range(len(mu)): jPos = self.lastSolvedHF.find(mu[j]) if jPos is not None: idx[j] = jPos muKeep.append(mu[j]) else: jExtra[j] = True muExtra.append(mu[j]) if len(muKeep) > 0 and not append: idx[~jExtra] = self.setHF(muKeep, self.uHF[idx[~jExtra]], append) append = True else: jExtra = np.ones(len(mu), dtype = bool) muExtra = mu if len(muExtra) > 0: newuHFs = self.samplingEngine.solveLS(muExtra, homogeneized = self.homogeneized) idx[jExtra] = self.setHF(muExtra, newuHFs, append) return list(idx) def setApproxReduced(self, muApp:paramList, uApp:sampleList, append : bool = False) -> List[int]: """Assign high fidelity solution.""" newSolvedApp, _ = checkParameterList(muApp, self.npar) newuApp = sampleList(uApp) if append: self.lastSolvedAppReduced.append(newSolvedApp) self.uAppReduced.append(newuApp) return list(range(len(self.uAppReduced) - len(newuApp), len(self.uAppReduced))) self.lastSolvedAppReduced, _ = checkParameterList(newSolvedApp, self.npar) self.uAppReduced = sampleList(newuApp) return list(range(len(self.uAppReduced))) 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. """ self.setupApprox() mu, _ = checkParameterList(mu, self.npar) idx = np.empty(len(mu), dtype = np.int) if prune: jExtra = np.zeros(len(mu), dtype = bool) muKeep = emptyParameterList() muExtra = copy(muKeep) for j in range(len(mu)): jPos = self.lastSolvedAppReduced.find(mu[j]) if jPos is not None: idx[j] = jPos muKeep.append(mu[j]) else: jExtra[j] = True muExtra.append(mu[j]) if len(muKeep) > 0 and not append: idx[~jExtra] = self.setApproxReduced(muKeep, self.uAppReduced[idx[~jExtra]], append) append = True else: jExtra = np.ones(len(mu), dtype = bool) muExtra = mu if len(muExtra) > 0: newuApps = self.trainedModel.getApproxReduced(muExtra) idx[jExtra] = self.setApproxReduced(muExtra, newuApps, append) return list(idx) def setApprox(self, muApp:paramList, uApp:sampleList, append : bool = False) -> List[int]: """Assign high fidelity solution.""" newSolvedApp, _ = checkParameterList(muApp, self.npar) newuApp = sampleList(uApp) if append: self.lastSolvedApp.append(newSolvedApp) self.uApp.append(newuApp) return list(range(len(self.uApp) - len(newuApp), len(self.uApp))) self.lastSolvedApp, _ = checkParameterList(newSolvedApp, self.npar) self.uApp = sampleList(newuApp) return list(range(len(self.uApp))) def evalApprox(self, mu:paramList, append : bool = False, prune : bool = True) -> List[int]: """ Evaluate approximant at arbitrary parameter. Args: mu: Target parameter. """ self.setupApprox() mu, _ = checkParameterList(mu, self.npar) idx = np.empty(len(mu), dtype = np.int) if prune: jExtra = np.zeros(len(mu), dtype = bool) muKeep = emptyParameterList() muExtra = copy(muKeep) for j in range(len(mu)): jPos = self.lastSolvedApp.find(mu[j]) if jPos is not None: idx[j] = jPos muKeep.append(mu[j]) else: jExtra[j] = True muExtra.append(mu[j]) if len(muKeep) > 0 and not append: idx[~jExtra] = self.setApprox(muKeep, self.uApp[idx[~jExtra]], append) append = True else: jExtra = np.ones(len(mu), dtype = bool) muExtra = mu if len(muExtra) > 0: newuApps = self.trainedModel.getApprox(muExtra) idx[jExtra] = self.setApprox(muExtra, newuApps, append) return list(idx) def getHF(self, mu:paramList, homogeneized : bool = False, append : bool = False, prune : bool = True) -> sampList: """ Get HF solution at arbitrary parameter. Args: mu: Target parameter. homogeneized(optional): Whether to remove Dirichlet BC. Defaults to False. Returns: HFsolution. """ mu, _ = checkParameterList(mu, self.npar) idx = self.evalHF(mu, append = append, prune = prune) uHFs = self.uHF(idx) if self.homogeneized and not homogeneized: for j, m in enumerate(mu): uHFs[j] += self.HFEngine.liftDirichletData(m) if not self.homogeneized and homogeneized: for j, m in enumerate(mu): uHFs[j] -= self.HFEngine.liftDirichletData(m) return uHFs def getRHS(self, mu:paramList, homogeneized : bool = False) -> sampList: """ Get linear system RHS at arbitrary parameter. Args: mu: Target parameter. homogeneized(optional): Whether to remove Dirichlet BC. Defaults to False. Returns: Linear system RHS. """ return self.HFEngine.residual(None, mu, homogeneized = homogeneized) def getApproxReduced(self, mu:paramList, append : bool = False, prune : bool = True) -> sampList: """ Get approximant at arbitrary parameter. Args: mu: Target parameter. Returns: Reduced approximant. """ mu, _ = checkParameterList(mu, self.npar) idx = self.evalApproxReduced(mu, append = append, prune = prune) uAppRs = self.uAppReduced(idx) return uAppRs def getApprox(self, mu:paramList, homogeneized : bool = False, append : bool = False, prune : bool = True) -> sampList: """ Get approximant at arbitrary parameter. Args: mu: Target parameter. homogeneized(optional): Whether to remove Dirichlet BC. Defaults to False. Returns: Approximant. """ mu, _ = checkParameterList(mu, self.npar) idx = self.evalApprox(mu, append = append, prune = prune) uApps = self.uApp(idx) if self.homogeneized and not homogeneized: for j, m in enumerate(mu): uApps[j] += self.HFEngine.liftDirichletData(m) if not self.homogeneized and homogeneized: for j, m in enumerate(mu): uApps[j] -= self.HFEngine.liftDirichletData(m) return uApps def getRes(self, mu:paramList, homogeneized : bool = False) -> sampList: """ Get residual at arbitrary parameter. Args: mu: Target parameter. homogeneized(optional): Whether to remove Dirichlet BC. Defaults to False. Returns: Approximant residual. """ return self.HFEngine.residual(self.getApprox(mu, homogeneized), mu, homogeneized = homogeneized) def getErr(self, mu:paramList, homogeneized : bool = False) -> sampList: """ Get error at arbitrary parameter. Args: mu: Target parameter. homogeneized(optional): Whether to remove Dirichlet BC. Defaults to False. Returns: Approximant error. """ return self.getApprox(mu, homogeneized) - self.getHF(mu, homogeneized) def getPoles(self) -> Np1D: """ Obtain approximant poles. Returns: Numpy complex vector of poles. """ self.setupApprox() if self.verbosity >= 20: verbosityDepth("INIT", "Computing poles of model.", timestamp = self.timestamp) poles = self.trainedModel.getPoles() if self.verbosity >= 20: verbosityDepth("DEL", "Done computing poles.", timestamp = self.timestamp) return poles def storeTrainedModel(self, filenameBase : str = "trained_model", forceNewFile : bool = True) -> str: """Store trained reduced model to file.""" self.setupApprox() if self.verbosity >= 20: verbosityDepth("INIT", "Storing trained model to file.", timestamp = self.timestamp) if forceNewFile: filename = getNewFilename(filenameBase, "pkl") else: filename = "{}.pkl".format(filenameBase) pickleDump(self.trainedModel.data.__dict__, filename) if self.verbosity >= 20: verbosityDepth("DEL", "Done storing trained model.", timestamp = self.timestamp) return filename def loadTrainedModel(self, filename:str): """Load trained reduced model from file.""" if self.verbosity >= 20: verbosityDepth("INIT", "Loading pre-trained model from file.", timestamp = self.timestamp) datadict = pickleLoad(filename) name = datadict.pop("name") if name == "TrainedModelPade": from rrompy.reduction_methods.trained_model import \ TrainedModelPade as tModel elif name == "TrainedModelRB": from rrompy.reduction_methods.trained_model import \ TrainedModelRB as tModel else: raise RROMPyException(("Trained model name not recognized. " "Loading failed.")) self.mu0 = datadict.pop("mu0") from rrompy.reduction_methods.trained_model import TrainedModelData trainedModel = tModel() trainedModel.verbosity = self.verbosity trainedModel.timestamp = self.timestamp data = TrainedModelData(name, self.mu0, datadict.pop("projMat"), datadict.pop("rescalingExp")) if "mus" in datadict: data.mus = datadict.pop("mus") approxParameters = datadict.pop("approxParameters") data.approxParameters = copy(approxParameters) if "sampler" in approxParameters: self._approxParameters["sampler"] = approxParameters.pop("sampler") self.approxParameters = copy(approxParameters) if "mus" in data.__dict__: self.mus = copy(data.mus) if name == "TrainedModelPade": self.scaleFactor = datadict.pop("scaleFactor") data.scaleFactor = self.scaleFactor for key in datadict: setattr(data, key, datadict[key]) trainedModel.data = data self.trainedModel = trainedModel self._mode = RROMPy_FRAGILE if self.verbosity >= 20: verbosityDepth("DEL", "Done loading pre-trained model.", timestamp = self.timestamp) diff --git a/rrompy/reduction_methods/centered/generic_centered_approximant.py b/rrompy/reduction_methods/centered/generic_centered_approximant.py index 48b9846..5d3949f 100644 --- a/rrompy/reduction_methods/centered/generic_centered_approximant.py +++ b/rrompy/reduction_methods/centered/generic_centered_approximant.py @@ -1,113 +1,113 @@ # Copyright (C) 2018 by the RROMPy authors # # This file is part of RROMPy. # # RROMPy is free software: you can redistribute it and/or modify # it under the terms of the GNU Lesser General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # RROMPy is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU Lesser General Public License for more details. # # You should have received a copy of the GNU Lesser General Public License # along with RROMPy. If not, see <http://www.gnu.org/licenses/>. # import numpy as np from rrompy.reduction_methods.base.generic_approximant import ( GenericApproximant) from rrompy.utilities.base.types import paramList from rrompy.utilities.base import verbosityDepth from rrompy.utilities.exception_manager import RROMPyAssert __all__ = ['GenericCenteredApproximant'] class GenericCenteredApproximant(GenericApproximant): """ ROM single-point approximant computation for parametric problems (ABSTRACT). Args: HFEngine: HF problem solver. mu0(optional): Default parameter. Defaults to 0. approxParameters(optional): Dictionary containing values for main parameters of approximant. Recognized keys are: - 'POD': whether to compute POD of snapshots; defaults to True; - 'S': total number of samples current approximant relies upon. Defaults to empty dict. homogeneized(optional): Whether to homogeneize Dirichlet BCs. Defaults to False. verbosity(optional): Verbosity level. Defaults to 10. Attributes: HFEngine: HF problem solver. mu0: Default parameter. homogeneized: Whether to homogeneize Dirichlet BCs. approxParameters: Dictionary containing values for main parameters of approximant. Recognized keys are in parameterList. parameterListSoft: Recognized keys of soft approximant parameters: - 'POD': whether to compute POD of snapshots. parameterListCritical: Recognized keys of critical approximant parameters: - 'S': total number of samples current approximant relies upon. POD: Whether to compute QR factorization of derivatives. S: Number of solution snapshots over which current approximant is based upon. initialHFData: HF problem initial data. samplingEngine: Sampling engine. uHF: High fidelity solution(s) with parameter(s) lastSolvedHF as sampleList. lastSolvedHF: Parameter(s) corresponding to last computed high fidelity solution(s) as parameterList. uAppReduced: Reduced approximate solution(s) with parameter(s) lastSolvedApp as sampleList. lastSolvedAppReduced: Parameter(s) corresponding to last computed reduced approximate solution(s) as parameterList. uApp: Approximate solution(s) with parameter(s) lastSolvedApp as sampleList. lastSolvedApp: Parameter(s) corresponding to last computed approximate solution(s) as parameterList. """ @property def S(self): """Value of S.""" return self._S @S.setter def S(self, S): GenericApproximant.S.fset(self, S) RROMPyAssert(len(self.S), 1, "Length of S") def computeDerivatives(self): """Compute derivatives of solution map starting from order 0.""" RROMPyAssert(self._mode, message = "Cannot start derivative computation.") if self.samplingEngine.nsamples != np.prod(self.S): if self.verbosity >= 5: verbosityDepth("INIT", "Starting computation of derivatives.", timestamp = self.timestamp) self.samplingEngine.iterSample([self.mu0[0]] * np.prod(self.S), homogeneized = self.homogeneized) if self.verbosity >= 5: verbosityDepth("DEL", "Done computing derivatives.", timestamp = self.timestamp) def normApprox(self, mu:paramList, homogeneized : bool = False) -> float: """ Compute norm of approximant at arbitrary parameter. Args: mu: Target parameter. homogeneized(optional): Whether to remove Dirichlet BC. Defaults to False. Returns: Target norm of approximant. """ if not self.POD or self.homogeneized != homogeneized: return super().normApprox(mu, homogeneized) - return np.linalg.norm(self.getApproxReduced(mu), axis = 0) + return np.linalg.norm(self.getApproxReduced(mu).data, axis = 0) diff --git a/rrompy/reduction_methods/distributed/generic_distributed_approximant.py b/rrompy/reduction_methods/distributed/generic_distributed_approximant.py index 13ac033..6f12eea 100644 --- a/rrompy/reduction_methods/distributed/generic_distributed_approximant.py +++ b/rrompy/reduction_methods/distributed/generic_distributed_approximant.py @@ -1,167 +1,167 @@ # Copyright (C) 2018 by the RROMPy authors # # This file is part of RROMPy. # # RROMPy is free software: you can redistribute it and/or modify # it under the terms of the GNU Lesser General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # RROMPy is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU Lesser General Public License for more details. # # You should have received a copy of the GNU Lesser General Public License # along with RROMPy. If not, see <http://www.gnu.org/licenses/>. # import numpy as np from copy import deepcopy as copy from rrompy.reduction_methods.base.generic_approximant import ( GenericApproximant) from rrompy.utilities.base.types import DictAny, HFEng, paramVal, paramList from rrompy.utilities.base import verbosityDepth from rrompy.utilities.exception_manager import RROMPyException, RROMPyAssert from rrompy.parameter import checkParameterList __all__ = ['GenericDistributedApproximant'] class GenericDistributedApproximant(GenericApproximant): """ ROM interpolant computation for parametric problems (ABSTRACT). Args: HFEngine: HF problem solver. mu0(optional): Default parameter. Defaults to 0. approxParameters(optional): Dictionary containing values for main parameters of approximant. Recognized keys are: - 'POD': whether to compute POD of snapshots; defaults to True; - 'S': total number of samples current approximant relies upon; - 'sampler': sample point generator. Defaults to empty dict. homogeneized(optional): Whether to homogeneize Dirichlet BCs. Defaults to False. verbosity(optional): Verbosity level. Defaults to 10. Attributes: HFEngine: HF problem solver. mu0: Default parameter. mus: Array of snapshot parameters. homogeneized: Whether to homogeneize Dirichlet BCs. approxParameters: Dictionary containing values for main parameters of approximant. Recognized keys are in parameterList. parameterListSoft: Recognized keys of soft approximant parameters: - 'POD': whether to compute POD of snapshots. parameterListCritical: Recognized keys of critical approximant parameters: - 'S': total number of samples current approximant relies upon; - 'sampler': sample point generator. POD: Whether to compute POD of snapshots. S: Number of solution snapshots over which current approximant is based upon. sampler: Sample point generator. muBounds: list of bounds for parameter values. samplingEngine: Sampling engine. uHF: High fidelity solution(s) with parameter(s) lastSolvedHF as sampleList. lastSolvedHF: Parameter(s) corresponding to last computed high fidelity solution(s) as parameterList. uAppReduced: Reduced approximate solution(s) with parameter(s) lastSolvedApp as sampleList. lastSolvedAppReduced: Parameter(s) corresponding to last computed reduced approximate solution(s) as parameterList. uApp: Approximate solution(s) with parameter(s) lastSolvedApp as sampleList. lastSolvedApp: Parameter(s) corresponding to last computed approximate solution(s) as parameterList. """ def __init__(self, HFEngine:HFEng, mu0 : paramVal = None, approxParameters : DictAny = {}, homogeneized : bool = False, verbosity : int = 10, timestamp : bool = True): self._preInit() from rrompy.parameter.parameter_sampling import QuadratureSampler as QS self._addParametersToList([], [], ["sampler"], [QS([[0], [1]], "UNIFORM")]) del QS super().__init__(HFEngine = HFEngine, mu0 = mu0, approxParameters = approxParameters, homogeneized = homogeneized, verbosity = verbosity, timestamp = timestamp) self._postInit() @property def mus(self): """Value of mus. Its assignment may reset snapshots.""" return self._mus @mus.setter def mus(self, mus): mus, _ = checkParameterList(mus, self.npar) musOld = copy(self.mus) if hasattr(self, '_mus') else None if (musOld is None or len(mus) != len(musOld) or not mus == musOld): self.resetSamples() self._mus = mus @property def muBounds(self): """Value of muBounds.""" return self.sampler.lims @property def sampler(self): """Value of sampler.""" return self._sampler @sampler.setter def sampler(self, sampler): if 'generatePoints' not in dir(sampler): raise RROMPyException("Sampler type not recognized.") if hasattr(self, '_sampler') and self._sampler is not None: samplerOld = self.sampler self._sampler = sampler self._approxParameters["sampler"] = self.sampler.__str__() if not 'samplerOld' in locals() or samplerOld != self.sampler: self.resetSamples() def setSamples(self, samplingEngine): """Copy samplingEngine and samples.""" super().setSamples(samplingEngine) self.mus = copy(self.samplingEngine.mus) def computeSnapshots(self): """Compute snapshots of solution map.""" RROMPyAssert(self._mode, message = "Cannot start snapshot computation.") if self.samplingEngine.nsamples != np.prod(self.S): if self.verbosity >= 5: verbosityDepth("INIT", "Starting computation of snapshots.", timestamp = self.timestamp) self.mus = self.sampler.generatePoints(self.S) self.samplingEngine.iterSample(self.mus, homogeneized = self.homogeneized) if self.verbosity >= 5: verbosityDepth("DEL", "Done computing snapshots.", timestamp = self.timestamp) def normApprox(self, mu:paramList, homogeneized : bool = False) -> float: """ Compute norm of approximant at arbitrary parameter. Args: mu: Target parameter. homogeneized(optional): Whether to remove Dirichlet BC. Defaults to False. Returns: Target norm of approximant. """ if not self.POD or self.homogeneized != homogeneized: return super().normApprox(mu, homogeneized) - return np.linalg.norm(self.getApproxReduced(mu), axis = 0) + return np.linalg.norm(self.getApproxReduced(mu).data, axis = 0) def computeScaleFactor(self): """Compute parameter rescaling factor.""" RROMPyAssert(self._mode, message = "Cannot compute rescaling factor.") self.scaleFactor = .5 * np.abs( self.muBounds[0] ** self.HFEngine.rescalingExp - self.muBounds[1] ** self.HFEngine.rescalingExp)