Page MenuHomec4science

generic_approximant.py
No OneTemporary

File Metadata

Created
Fri, May 17, 15:35

generic_approximant.py

# Copyright (C) 2018 by the RROMPy authors
#
# This file is part of RROMPy.
#
# RROMPy is free software: you can redistribute it and/or modify
# it under the terms of the GNU Lesser General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# RROMPy is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Lesser General 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 copy import copy
import scipy.sparse.linalg as spla
from rrompy.utilities.types import Np1D, Np2D, DictAny, N2FSExpr
from rrompy.utilities.types import HFEng, HSEng, Tuple, List
from rrompy.utilities import purgeDict
__all__ = ['GenericApproximant']
class GenericApproximant:
"""
ABSTRACT
ROM approximant computation for parametric problems.
Args:
HFEngine: HF problem solver. Should have members:
- energyNormMatrix: assemble sparse matrix (in CSC format)
associated to weighted H10 norm;
- problemData: list of HF problem data (excluding k);
- setProblemData: set HF problem data and k to prescribed values;
- getLSBlocks: get algebraic system blocks.
HSEngine: Hilbert space general purpose engine. Should have members:
- norm: compute Hilbert norm of expression represented as complex
numpy vector;
- plot: plot Hilbert expression represented as complex numpy vector.
mu0(optional): Default parameter. Defaults to 0.
approxParameters(optional): Dictionary containing values for main
parameters of approximant. Recognized keys are:
- 'POD': whether to compute POD of snapshots; defaults to True.
Defaults to empty dict.
Attributes:
HFEngine: HF problem solver.
HSEngine: Hilbert space general purpose engine.
mu0: Default parameter.
w: Weight for norm computation.
approxParameters: Dictionary containing values for main parameters of
approximant. Recognized keys are in parameterList.
parameterList: Recognized keys of approximant parameters:
- 'POD': whether to compute POD of snapshots.
POD: Whether to compute POD of snapshots.
energyNormMatrix: Sparse matrix (in CSC format) assoociated to
w-weighted H10 norm.
uHF: High fidelity solution with wavenumber lastSolvedHF as numpy
complex vector.
lastSolvedHF: Wavenumber corresponding to last computed high fidelity
solution.
solLifting: Lifting of Dirichlet boundary data as numpy vector.
uApp: Last evaluated approximant as numpy complex vector.
lastApproxParameters: List of parameters corresponding to last
computed approximant.
As: List of sparse matrices (in CSC format) representing coefficients
of linear system matrix wrt mu.
bs: List of numpy vectors representing coefficients of linear system
RHS wrt mu.
"""
def __init__(self, HFEngine:HFEng, HSEngine:HSEng, mu0 : complex = 0,
approxParameters : DictAny = {}):
self.HFEngine = HFEngine
self.HSEngine = HSEngine
self._HFEngine0 = copy(HFEngine)
if not hasattr(self, "parameterList"):
self.parameterList = []
self.parameterList += ["POD"]
self.solLifting = self.HFEngine.liftDirichletData()
self.mu0 = mu0
self.approxParameters = approxParameters
self.energyNormMatrix = self.HFEngine.energyNormMatrix()
self.As, self.bs = self.HFEngine.getLSBlocks()
def name(self) -> str:
"""Approximant label."""
return self.__class__.__name__
@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")
keyList = list(approxParameters.keys())
if "POD" in keyList:
self.POD = approxParameters["POD"]
elif hasattr(self, "POD"):
self.POD = self.POD
else:
self.POD = True
@property
def POD(self):
"""Value of POD."""
return self._POD
@POD.setter
def POD(self, POD):
if hasattr(self, "POD"): PODold = self.POD
else: PODold = -1
self._POD = POD
self._approxParameters["POD"] = self.POD
if PODold != self.POD:
self.resetSamples()
def solveHF(self, mu : complex = None):
"""
Find high fidelity solution with original parameters and arbitrary
parameter.
Args:
mu: Target parameter.
"""
if mu is None: mu = self.mu0
if (not hasattr(self, "lastSolvedHF")
or not np.isclose(self.lastSolvedHF, mu)):
A, b = self.buildLS(mu)
self.uHF = spla.spsolve(A, b)
self.lastSolvedHF = mu
return self.uHF
def buildLS(self, mu:complex, As : List[Np2D] = None,
bs : List[Np1D] = None) -> Tuple[Np2D, Np1D]:
"""
Build linear system from polynomial blocks.
Args:
mu: Parameter value.
As: Matrix blocks. If None, set to self.As. Defaults to None.
bs: RHS blocks. If None, set to self.bs. Defaults to None.
Returns:
Matrix and RHS of system.
"""
if As is None: As = self.As
if bs is None: bs = self.bs
A = copy(As[0])
b = copy(bs[0])
for j in range(1, len(As)):
A = A + np.power(mu, j) * As[j]
for j in range(1, len(bs)):
b = b + np.power(mu, j) * bs[j]
return A, b
@abstractmethod
def resetSamples(self):
"""Reset samples. (ABSTRACT)"""
pass
@abstractmethod
def setupApprox(self):
"""
Setup approximant. (ABSTRACT)
Any specialization should include something like
self.computeDerivatives()
if not self.checkComputedApprox():
...
self.lastApproxParameters = copy(self.approxParameters)
"""
pass
def checkComputedApprox(self) -> bool:
"""
Check if setup of new approximant is not needed.
Returns:
True if new setup is not needed. False otherwise.
"""
return (hasattr(self, "lastApproxParameters")
and self.approxParameters == self.lastApproxParameters)
@abstractmethod
def evalApprox(self, mu:complex) -> Np1D:
"""
Evaluate approximant at arbitrary parameter. (ABSTRACT)
Any specialization should include something like
self.setupApprox()
self.uApp = ...
Args:
mu: Target parameter.
Returns:
Approximant as numpy complex vector.
"""
pass
@abstractmethod
def getPoles(self) -> Np1D:
"""
Obtain approximant poles.
Returns:
Numpy complex vector of poles.
"""
pass
def HFNorm(self, mu:complex, normType : N2FSExpr = "H10") -> float:
"""
Compute norm of HF solution at arbitrary wavenumber.
Args:
mu: Target parameter.
normType(optional): Target norm identifier. Must be recognizable
by HSEngine norm command. If None, set to w. Defaults to None.
Returns:
Target norm of HFsolution.
"""
self.solveHF(mu)
return self.HSEngine.norm(self.uHF, normType)
def approxNorm(self, mu:complex, normType : N2FSExpr = "H10") -> float:
"""
Compute norm of (translated) approximant at arbitrary wavenumber.
Args:
mu: Target parameter.
normType(optional): Target norm identifier. Must be recognizable
by HSEngine norm command. If None, set to w. Defaults to None.
Returns:
Target norm of approximant.
"""
self.evalApprox(mu)
return self.HSEngine.norm(self.uApp, normType)
def approxError(self, mu:complex, normType : N2FSExpr = "H10") -> float:
"""
Compute norm of approximant at arbitrary wavenumber.
Args:
mu: Target parameter.
normType(optional): Target norm identifier. Must be recognizable
by HSEngine norm command. If None, set to w. Defaults to None.
Returns:
Target norm of (approximant - HFsolution).
"""
self.solveHF(mu)
self.evalApprox(mu)
return self.HSEngine.norm(self.uApp - self.uHF, normType)
def getHF(self, mu:complex) -> Np1D:
"""
Get HF solution at arbitrary wavenumber.
Args:
mu: Target parameter.
Returns:
HFsolution as numpy complex vector.
"""
self.solveHF(mu)
return self.uHF
def getApp(self, mu:complex) -> Np1D:
"""
Get approximant at arbitrary wavenumber.
Args:
mu: Target parameter.
Returns:
Approximant as numpy complex vector.
"""
self.evalApprox(mu)
return self.uApp
def plotHF(self, mu:complex, name : str = "u", **figspecs):
"""
Do some nice plots of the HF solution at arbitrary wavenumber.
Args:
mu: Target parameter.
name(optional): Name to be shown as title of the plots. Defaults to
'u'.
figspecs(optional key args): Optional arguments for matplotlib
figure creation.
"""
self.solveHF(mu)
self.HSEngine.plot(self.uHF, name = name, **figspecs)
def plotApp(self, mu:complex, name : str = "u", **figspecs):
"""
Do some nice plots of approximant at arbitrary wavenumber.
Args:
mu: Target parameter.
name(optional): Name to be shown as title of the plots. Defaults to
'u'.
figspecs(optional key args): Optional arguments for matplotlib
figure creation.
"""
self.evalApprox(mu)
self.HSEngine.plot(self.uApp, name = name, **figspecs)
def plotErr(self, mu:complex, name : str = "u", **figspecs):
"""
Do some nice plots of approximation error at arbitrary wavenumber.
Args:
mu: Target parameter.
name(optional): Name to be shown as title of the plots. Defaults to
'u'.
figspecs(optional key args): Optional arguments for matplotlib
figure creation.
"""
self.evalApprox(mu)
self.solveHF(mu)
self.HSEngine.plot(self.uApp - self.uHF, name = name, **figspecs)

Event Timeline