diff --git a/examples/airfoil/airfoil_engine.py b/examples/airfoil/airfoil_engine.py index b0b651f..17bcce9 100644 --- a/examples/airfoil/airfoil_engine.py +++ b/examples/airfoil/airfoil_engine.py @@ -1,50 +1,50 @@ import numpy as np import fenics as fen import ufl from rrompy.hfengines.linear_problem import \ HelmholtzBoxScatteringProblemEngine as HSP -from rrompy.utilities.base.fenics import fenONE +from rrompy.utilities.fenics import fenONE PI = np.pi class AirfoilScatteringEngine(HSP): def __init__(self, kappa:float, theta:float, degree_threshold : int = np.inf, verbosity : int = 10, timestamp : bool = True): super().__init__(R = 5, kappa = kappa, theta = theta, n = 1, degree_threshold = degree_threshold, verbosity = verbosity, timestamp = timestamp) mesh = fen.Mesh('../data/mesh/airfoil2412_1.xml') self.V = fen.FunctionSpace(mesh, "P", 1) c, s = np.cos(theta), np.sin(theta) x, y = fen.SpatialCoordinate(mesh)[:] u0R = - fen.cos(kappa * (c * x + s * y)) u0I = - fen.sin(kappa * (c * x + s * y)) self.DirichletDatum = [u0R, u0I] mu = 1.1 epsilon = .1 checkReal = x**2-x+y**2 rhop5 = ((x**2+y**2)/((x-1)**2+y**2))**.25 phiroot1 = fen.atan(-y/(x**2-x+y**2)) / 2 phiroot2 = fen.atan(-y/(x**2-x+y**2)) / 2 - PI * ufl.sign(-y/(x**2-x+y**2)) / 2 kappam1 = (((rhop5*fen.cos(phiroot1)+.5)**2.+(rhop5*fen.sin(phiroot1))**2.)/ ((rhop5*fen.cos(phiroot1)-1.)**2.+(rhop5*fen.sin(phiroot1))**2.) )**.5 - mu kappam2 = (((rhop5*fen.cos(phiroot2)+.5)**2.+(rhop5*fen.sin(phiroot2))**2.)/ ((rhop5*fen.cos(phiroot2)-1.)**2.+(rhop5*fen.sin(phiroot2))**2.) )**.5 - mu Heps1 = .9 * .5 * (1 + kappam1/epsilon + fen.sin(PI*kappam1/epsilon) / PI) + .1 Heps2 = .9 * .5 * (1 + kappam2/epsilon + fen.sin(PI*kappam2/epsilon) / PI) + .1 cTT = ufl.conditional(ufl.le(kappam1, epsilon), Heps1, fenONE) c_F = fen.Constant(.1) cFT = ufl.conditional(ufl.le(kappam2, epsilon), Heps2, fenONE) c_F = fen.Constant(.1) cT = ufl.conditional(ufl.ge(kappam1, - epsilon), cTT, c_F) cF = ufl.conditional(ufl.ge(kappam2, - epsilon), cFT, c_F) a = ufl.conditional(ufl.ge(checkReal, 0.), cT, cF) self.diffusivity = a diff --git a/examples/airfoil/greedy.py b/examples/airfoil/greedy.py index 5e3b321..eb1a55d 100644 --- a/examples/airfoil/greedy.py +++ b/examples/airfoil/greedy.py @@ -1,102 +1,102 @@ import numpy as np from airfoil_engine import AirfoilScatteringEngine -from rrompy.reduction_methods.lagrange_greedy import \ - ApproximantLagrangePadeGreedy as Pade -from rrompy.reduction_methods.lagrange_greedy import \ - ApproximantLagrangeRBGreedy as RB +from rrompy.reduction_methods.distributed_greedy import \ + RationalInterpolantGreedy as Pade +from rrompy.reduction_methods.distributed_greedy import \ + RBDistributedGreedy as RB verb = 2 timed = False algo = "Pade" #algo = "RB" polyBasis = "LEGENDRE" #polyBasis = "CHEBYSHEV" #polyBasis = "MONOMIAL" homog = True homog = False k0s = np.linspace(5, 20, 100) k0 = np.mean(k0s) kl, kr = min(k0s), max(k0s) params = {'muBounds':[kl, kr], 'nTestPoints':500, 'Delta':0, - 'greedyTol':1e-2, 'nTrainPoints':2, 'basis':polyBasis} + 'greedyTol':1e-2, 'S':2, 'basis':polyBasis} ######### kappa = 10 theta = np.pi * - 45 / 180. solver = AirfoilScatteringEngine(kappa, theta, verbosity = verb, degree_threshold = 8) ######### if algo == "Pade": approx = Pade(solver, mu0 = k0, approxParameters = params, verbosity = verb, homogeneized = homog) else: approx = RB(solver, mu0 = k0, approxParameters = params, verbosity = verb, homogeneized = homog) if timed: from time import clock start_time = clock() approx.greedy() print("Time: ", clock() - start_time) else: approx.greedy(True) approx.samplingEngine.verbosity = 0 approx.verbosity = 0 kl, kr = np.real(kl), np.real(kr) from matplotlib import pyplot as plt normApp = np.zeros(len(k0s)) norm = np.zeros_like(normApp) res = np.zeros_like(normApp) err = np.zeros_like(normApp) for j in range(len(k0s)): normApp[j] = approx.normApprox(k0s[j]) norm[j] = approx.normHF(k0s[j]) res[j] = (approx.estNormer.norm(approx.getRes(k0s[j], homogeneized=homog)) / approx.estNormer.norm(approx.getRHS(k0s[j], homogeneized=homog))) err[j] = approx.normErr(k0s[j]) / approx.normHF(k0s[j]) resApp = approx.errorEstimator(k0s) plt.figure() plt.plot(k0s, norm) plt.plot(k0s, normApp, '--') plt.plot(np.real(approx.mus), 1.05*np.max(norm)*np.ones_like(approx.mus, dtype = float), 'rx') plt.xlim([kl, kr]) plt.grid() plt.show() plt.close() plt.figure() plt.semilogy(k0s, res) plt.semilogy(k0s, resApp, '--') plt.semilogy(np.real(approx.mus), 4.*np.max(resApp)*np.ones_like(approx.mus, dtype = float), 'rx') plt.xlim([kl, kr]) plt.grid() plt.show() plt.close() plt.figure() plt.semilogy(k0s, err) plt.xlim([kl, kr]) plt.grid() plt.show() plt.close() polesApp = approx.getPoles() mask = (np.real(polesApp) < kl) | (np.real(polesApp) > kr) print("Outliers:", polesApp[mask]) polesApp = polesApp[~mask] plt.figure() plt.plot(np.real(polesApp), np.imag(polesApp), 'kx') plt.axis('equal') plt.grid() plt.show() plt.close() diff --git a/examples/airfoil/pod.py b/examples/airfoil/pod.py index bdd0375..0091708 100644 --- a/examples/airfoil/pod.py +++ b/examples/airfoil/pod.py @@ -1,110 +1,110 @@ import numpy as np from airfoil_engine import AirfoilScatteringEngine -from rrompy.reduction_methods.lagrange import ApproximantLagrangePade as Pade -from rrompy.reduction_methods.lagrange import ApproximantLagrangeRB as RB +from rrompy.reduction_methods.distributed import RationalInterpolant as Pade +from rrompy.reduction_methods.distributed import RBDistributed as RB from rrompy.utilities.parameter_sampling import QuadratureSampler as QS verb = 100 homog = True homog = False sol = "single" sol = "sweep" algo = "Pade" algo = "RB" polyBasis = "LEGENDRE" polyBasis = "CHEBYSHEV" #polyBasis = "MONOMIAL" Nsweep = 100 k0s = [x * 2 * np.pi / 340 for x in [1.0e2, 5.0e2]] k0 = np.mean(np.power(k0s, 2.)) ** .5 ktar = k0s[0] + (k0s[1] - k0s[0]) * .7 -params = {'N':29, 'M':29, 'S':30, 'POD':True, 'polybasis':polyBasis, - 'robustTol':1e-14} +params = {'N':29, 'M':29, 'R':30, 'S':30, 'POD':True, 'polybasis':polyBasis, + 'sampler':QS(k0s, "CHEBYSHEV"), 'robustTol':1e-14} theta = - 45. * np.pi / 180 solver = AirfoilScatteringEngine(k0, theta, verbosity = verb, degree_threshold = 8) if algo == "Pade": + params.pop('R') approx = Pade(solver, mu0 = k0, approxParameters = params, verbosity = verb) else: params.pop('N') params.pop('M') params.pop('polybasis') params.pop('robustTol') approx = RB(solver, mu0 = k0, approxParameters = params, verbosity = verb) -approx.sampler = QS(k0s, "CHEBYSHEV") approx.setupApprox() if sol == "single": # approx.outParaviewTimeDomainSamples(filename = "out/outSamples", # forceNewFile = False, folders = True) approx.outParaviewTimeDomainApprox(ktar, omega = 2. * np.pi * ktar, filename = "out/outTApp{}".format(ktar), forceNewFile = False, folder = True) approx.outParaviewTimeDomainHF(ktar, omega = 2. * np.pi * ktar, filename = "out/outTHF{}".format(ktar), forceNewFile = False, folder = True) approx.outParaviewTimeDomainErr(ktar, omega = 2. * np.pi * ktar, filename = "out/outTErr{}".format(ktar), forceNewFile = False, folder = True) approx.outParaviewTimeDomainRes(ktar, omega = 2. * np.pi * ktar, filename = "out/outTRes{}".format(ktar), forceNewFile = False, folder = True) appErr, solNorm = approx.normErr(ktar), approx.normHF(ktar) resNorm, RHSNorm = approx.normRes(ktar), approx.normRHS(ktar) print(('SolNorm:\t{}\nErr:\t{}\nErrRel:\t{}').format(solNorm, appErr, np.divide(appErr, solNorm))) print(('RHSNorm:\t{}\nRes:\t{}\nResRel:\t{}').format(RHSNorm, resNorm, np.divide(resNorm, RHSNorm))) print('Poles:', approx.getPoles()) if sol == "sweep": k0s = np.linspace(k0s[0], k0s[1], Nsweep) kl, kr = min(k0s), max(k0s) approx.samplingEngine.verbosity = 0 approx.trainedModel.verbosity = 0 approx.verbosity = 0 kl, kr = np.real(kl), np.real(kr) from matplotlib import pyplot as plt normApp = np.zeros(len(k0s)) norm = np.zeros_like(normApp) err = np.zeros_like(normApp) for j in range(len(k0s)): normApp[j] = approx.normApprox(k0s[j]) norm[j] = approx.normHF(k0s[j]) err[j] = approx.normErr(k0s[j]) / norm[j] plt.figure() plt.semilogy(k0s, norm) plt.semilogy(k0s, normApp, '--') plt.semilogy(np.real(approx.mus), 1.05*np.max(norm)*np.ones_like(approx.mus, dtype = float), 'rx') plt.xlim([kl, kr]) plt.grid() plt.show() plt.close() plt.figure() plt.semilogy(k0s, err) plt.xlim([kl, kr]) plt.grid() plt.show() plt.close() polesApp = approx.getPoles() mask = (np.real(polesApp) < kl) | (np.real(polesApp) > kr) print("Outliers:", polesApp[mask]) polesApp = polesApp[~mask] plt.figure() plt.plot(np.real(polesApp), np.imag(polesApp), 'kx') plt.axis('equal') plt.grid() plt.show() - plt.close() \ No newline at end of file + plt.close() diff --git a/examples/diapason/greedy.py b/examples/diapason/greedy.py index b85da41..647c512 100644 --- a/examples/diapason/greedy.py +++ b/examples/diapason/greedy.py @@ -1,177 +1,177 @@ import numpy as np import fenics as fen import ufl from rrompy.hfengines.vector_linear_problem import \ LinearElasticityHelmholtzProblemEngine as LEHPE from rrompy.hfengines.vector_linear_problem import \ LinearElasticityHelmholtzProblemEngineDamped as LEHPED -from rrompy.reduction_methods.lagrange_greedy import \ - ApproximantLagrangePadeGreedy as Pade -from rrompy.reduction_methods.lagrange_greedy import \ - ApproximantLagrangeRBGreedy as RB +from rrompy.reduction_methods.distributed_greedy import \ + RationalInterpolantGreedy as Pade +from rrompy.reduction_methods.distributed_greedy import \ + RBDistributedGreedy as RB verb = 2 timed = False algo = "Pade" #algo = "RB" polyBasis = "LEGENDRE" polyBasis = "CHEBYSHEV" #polyBasis = "MONOMIAL" if timed: verb = 0 dampingEta = 0 * 1e4 / 2. / np.pi k0s = np.linspace(2.5e2, 7.5e3, 100) k0s = np.linspace(2.5e3, 1.5e4, 100) k0s = np.linspace(5.0e4, 1.0e5, 100) k0s = np.linspace(2.0e5, 2.5e5, 100) k0 = np.mean(np.power(k0s, 2.)) ** .5 # [Hz] kl, kr = min(k0s), max(k0s) params = {'muBounds':[kl, kr], 'nTestPoints':500, 'Delta':0, - 'greedyTol':1e-2, 'nTrainPoints':2, 'polybasis':polyBasis, + 'greedyTol':1e-2, 'S':2, 'polybasis':polyBasis, 'robustTol':2e-16, 'interpRcond':None, 'errorEstimatorKind':'EXACT'} theta = 20. * np.pi / 180. phi = 10. * np.pi / 180. mesh = fen.Mesh("../data/mesh/diapason_1.xml") subdomains = fen.MeshFunction("size_t", mesh, "../data/mesh/diapason_1_physical_region.xml") meshBall = fen.SubMesh(mesh, subdomains, 2) meshFork = fen.SubMesh(mesh, subdomains, 1) Hball = np.max(meshBall.coordinates()[:, 1]) #.00257 Ltot = np.max(mesh.coordinates()[:, 1]) #.1022 Lhandle = np.max(meshFork.coordinates()[:, 1]) #.026 Lrod = Ltot - Lhandle #.0762 L2var = (Lrod / 4.) ** 2. Ehandle_ratio = 3. rhohandle_ratio = 1.5 c = 3.e2 rho = 8e3 * (2. * np.pi) ** 2. E = 1.93e11 nu = .3 T = 1e6 lambda_ = E * nu / (1. + nu) / (1. - 2. * nu) mu_ = E / (1. + nu) kWave = (np.cos(theta) * np.cos(phi), np.sin(phi), np.sin(theta) * np.cos(phi)) x, y, z = fen.SpatialCoordinate(mesh)[:] yCorr = y - Ltot compPlane = kWave[0] * x + kWave[1] * y + kWave[2] * z xPlane, yPlane, zPlane = (xx - compPlane * xx for xx in (x, y, z)) xOrtho, yOrtho, zOrtho = (compPlane * xx for xx in (x, y, z)) forcingBase = (T / (2. * np.pi * L2var)**.5 * fen.exp(- (xPlane**2. + yPlane**2. + zPlane**2.) / (2.*L2var))) forcingWeight = np.real(k0) / c * (xOrtho + yOrtho + zOrtho) neumannDatum = [ufl.as_vector( tuple(forcingBase * fen.cos(forcingWeight) * kWavedir for kWavedir in kWave)), ufl.as_vector( tuple(forcingBase * fen.sin(forcingWeight) * kWavedir for kWavedir in kWave))] lambda_eff = ufl.conditional(ufl.ge(y, Lhandle), fen.Constant(lambda_), fen.Constant(Ehandle_ratio * lambda_)) mu_eff = ufl.conditional(ufl.ge(y, Lhandle), fen.Constant(mu_), fen.Constant(Ehandle_ratio * mu_)) rho_eff = ufl.conditional(ufl.ge(y, Lhandle), fen.Constant(rho), fen.Constant(rhohandle_ratio * rho)) ### if dampingEta > 0: solver = LEHPED(degree_threshold = 8, verbosity = 0) solver.eta = dampingEta else: solver = LEHPE(degree_threshold = 8, verbosity = 0) solver.omega = np.real(k0) solver.lambda_ = lambda_eff solver.mu_ = mu_eff solver.rho_ = rho_eff solver.V = fen.VectorFunctionSpace(mesh, "P", 1) solver.DirichletBoundary = lambda x, on_b: on_b and x[1] < Hball solver.NeumannBoundary = "REST" solver.forcingTerm = neumannDatum if algo == "Pade": approx = Pade(solver, mu0 = k0, approxParameters = params, verbosity = verb) else: params.pop("Delta") params.pop("polybasis") params.pop("robustTol") params.pop("interpRcond") params.pop("errorEstimatorKind") approx = RB(solver, mu0 = k0, approxParameters = params, verbosity = verb) if timed: from time import clock start_time = clock() approx.greedy() print("Time: ", clock() - start_time) else: approx.greedy(True) polesApp = approx.getPoles() print("Poles:\n", polesApp) approx.samplingEngine.verbosity = 0 approx.verbosity = 0 kl, kr = np.real(kl), np.real(kr) from matplotlib import pyplot as plt normApp = np.zeros(len(k0s)) norm = np.zeros_like(normApp) res = np.zeros_like(normApp) err = np.zeros_like(normApp) for j in range(len(k0s)): normApp[j] = approx.normApprox(k0s[j]) norm[j] = approx.normHF(k0s[j]) res[j] = (approx.estNormer.norm(approx.getRes(k0s[j])) / approx.estNormer.norm(approx.getRHS(k0s[j]))) err[j] = approx.normErr(k0s[j]) / norm[j] resApp = approx.errorEstimator(k0s) res[res < 1e-5 * approx.greedyTol] = np.nan resApp[resApp < 1e-5 * approx.greedyTol] = np.nan err[err < 1e-8 * approx.greedyTol] = np.nan plt.figure() plt.semilogy(k0s, norm) plt.semilogy(k0s, normApp, '--') plt.semilogy(np.real(approx.mus), 1.05*np.max(norm)*np.ones_like(approx.mus, dtype = float), 'rx') plt.xlim([kl, kr]) plt.grid() plt.show() plt.close() plt.figure() plt.semilogy(k0s, res) plt.semilogy(k0s, resApp, '--') plt.semilogy(np.real(approx.mus), approx.greedyTol*np.ones_like(approx.mus, dtype = float), 'rx') plt.xlim([kl, kr]) plt.grid() plt.show() plt.close() plt.figure() plt.semilogy(k0s, err) plt.xlim([kl, kr]) plt.grid() plt.show() plt.close() mask = (np.real(polesApp) < kl) | (np.real(polesApp) > kr) print("Outliers:", polesApp[mask]) polesAppEff = polesApp[~mask] plt.figure() plt.plot(np.real(polesAppEff), np.imag(polesAppEff), 'kx') plt.axis('equal') plt.grid() plt.show() plt.close() diff --git a/examples/diapason/pod.py b/examples/diapason/pod.py index 9b847b2..659f67c 100644 --- a/examples/diapason/pod.py +++ b/examples/diapason/pod.py @@ -1,150 +1,150 @@ import numpy as np from diapason_engine import DiapasonEngine, DiapasonEngineDamped -from rrompy.reduction_methods.lagrange import ApproximantLagrangePade as Pade -from rrompy.reduction_methods.lagrange import ApproximantLagrangeRB as RB +from rrompy.reduction_methods.distributed import RationalInterpolant as Pade +from rrompy.reduction_methods.distributed import RBDistributed as RB from rrompy.utilities.parameter_sampling import QuadratureSampler as QS verb = 100 sol = "single" sol = "sweep" algo = "Pade" #algo = "RB" polyBasis = "LEGENDRE" polyBasis = "CHEBYSHEV" #polyBasis = "MONOMIAL" dampingEta = 0. * 1e4 / 2. / np.pi ktar = 1.e4 # [Hz] k0s = [2.5e2, 1.0e4] #k0s = np.array([2.5e3, 1.5e4]) #k0s = np.array([5.0e4, 1.0e5]) k0s = [2.0e5, 3.0e5] k0 = np.mean(np.power(k0s, 2.)) ** .5 theta = 20. * np.pi / 180. phi = 10. * np.pi / 180. c = 3.e2 rho = 8e3 * (2. * np.pi) ** 2. E = 1.93e11 nu = .3 T = 1e6 ### if np.isclose(dampingEta, 0.): rescaling = lambda x: np.power(x, 2.) rescalingInv = lambda x: np.power(x, .5) solver = DiapasonEngine(kappa = k0, c = c, rho = rho, E = E, nu = nu, T = T, theta = theta, phi = phi, meshNo = 1, degree_threshold = 8, verbosity = 0) else: rescaling = lambda x: x rescalingInv = lambda x: x solver = DiapasonEngineDamped(kappa = k0, c = c, rho = rho, E = E, nu = nu, T = T, theta = theta, phi = phi, dampingEta = dampingEta, meshNo = 1, degree_threshold = 8, verbosity = 0) -params = {'N':39, 'M':39, 'S':40, 'POD':True, 'polybasis':polyBasis}#, +params = {'N':39, 'M':39, 'S':40, 'POD':True, 'polybasis':polyBasis, + 'sampler':QS(k0s, "CHEBYSHEV", rescaling, rescalingInv)}#, # 'robustTol':1e-16} if algo == "Pade": approx = Pade(solver, mu0 = k0, approxParameters = params, verbosity = verb) else: params.pop("N") params.pop("M") params.pop("polybasis") # params.pop("robustTol") approx = RB(solver, mu0 = k0, approxParameters = params, verbosity = verb) -approx.sampler = QS(k0s, "CHEBYSHEV", rescaling, rescalingInv) approx.setupApprox() if sol == "single": approx.outParaviewTimeDomainSamples( filename = "out/outSamples{}".format(dampingEta), forceNewFile = False, folders = True) nameBase = "{}_{}".format(ktar, dampingEta) approx.outParaviewTimeDomainApprox(ktar, omega = 2. * np.pi * ktar, filename = "out/outTApp{}".format(nameBase), forceNewFile = False, folder = True) approx.outParaviewTimeDomainHF(ktar, omega = 2. * np.pi * ktar, filename = "out/outTHF{}".format(nameBase), forceNewFile = False, folder = True) approx.outParaviewTimeDomainErr(ktar, omega = 2. * np.pi * ktar, filename = "out/outTErr{}".format(nameBase), forceNewFile = False, folder = True) approx.outParaviewTimeDomainRes(ktar, omega = 2. * np.pi * ktar, filename = "out/outTRes{}".format(nameBase), forceNewFile = False, folder = True) appErr, solNorm = approx.normErr(ktar), approx.normHF(ktar) resNorm, RHSNorm = approx.normRes(ktar), approx.normRHS(ktar) print(('SolNorm:\t{}\nErr:\t{}\nErrRel:\t{}').format(solNorm, appErr, np.divide(appErr, solNorm))) print(('RHSNorm:\t{}\nRes:\t{}\nResRel:\t{}').format(RHSNorm, resNorm, np.divide(resNorm, RHSNorm))) poles = approx.getPoles() print('Poles:', poles) if sol == "sweep": k0s = np.linspace(k0s[0], k0s[1], 100) kl, kr = min(k0s), max(k0s) approx.samplingEngine.verbosity = 0 approx.trainedModel.verbosity = 0 approx.verbosity = 0 kl, kr = np.real(kl), np.real(kr) from matplotlib import pyplot as plt normApp = np.zeros(len(k0s)) norm = np.zeros_like(normApp) err = np.zeros_like(normApp) res = np.zeros_like(normApp) # errApp = np.zeros_like(normApp) fNorm = approx.normRHS(k0s[0]) for j in range(len(k0s)): normApp[j] = approx.normApprox(k0s[j]) norm[j] = approx.normHF(k0s[j]) err[j] = approx.normErr(k0s[j]) / norm[j] res[j] = approx.normRes(k0s[j]) / fNorm # errApp[j] = res[j] / np.min(np.abs(k0s[j] - poles)) # errApp *= np.mean(err) / np.mean(errApp) plt.figure() plt.semilogy(k0s, norm) plt.semilogy(k0s, normApp, '--') plt.semilogy(np.real(approx.mus), 1.05*np.max(norm)*np.ones_like(approx.mus, dtype = float), 'rx') plt.xlim([kl, kr]) plt.grid() plt.show() plt.close() plt.figure() plt.semilogy(k0s, res) plt.xlim([kl, kr]) plt.grid() plt.show() plt.close() plt.figure() plt.semilogy(k0s, err) # plt.semilogy(k0s, errApp) plt.xlim([kl, kr]) plt.grid() plt.show() plt.close() polesApp = approx.getPoles() mask = (np.real(polesApp) < kl) | (np.real(polesApp) > kr) print("Outliers:", polesApp[mask]) polesApp = polesApp[~mask] plt.figure() plt.plot(np.real(polesApp), np.imag(polesApp), 'kx') plt.axis('equal') plt.grid() plt.show() plt.close() diff --git a/examples/from_papers/greedy_internalBox.py b/examples/from_papers/greedy_internalBox.py index 2615fe7..ed95218 100644 --- a/examples/from_papers/greedy_internalBox.py +++ b/examples/from_papers/greedy_internalBox.py @@ -1,110 +1,110 @@ import numpy as np import fenics as fen from rrompy.hfengines.linear_problem import HelmholtzProblemEngine as HPE -from rrompy.reduction_methods.lagrange_greedy import \ - ApproximantLagrangePadeGreedy as Pade -from rrompy.reduction_methods.lagrange_greedy import \ - ApproximantLagrangeRBGreedy as RB +from rrompy.reduction_methods.distributed_greedy import \ + RationalInterpolantGreedy as Pade +from rrompy.reduction_methods.distributed_greedy import \ + RBDistributedGreedy as RB dim = 3 verb = 2 timed = False algo = "Pade" #algo = "RB" polyBasis = "LEGENDRE" #polyBasis = "CHEBYSHEV" #polyBasis = "MONOMIAL" k0s = np.power(np.linspace(500 ** 2., 2250 ** 2., 200), .5) k0 = np.mean(np.power(k0s, 2.)) ** .5 kl, kr = min(k0s), max(k0s) params = {'muBounds':[kl, kr], 'nTestPoints':500, 'Delta':0, - 'greedyTol':1e-2, 'nTrainPoints':2, 'basis':polyBasis} + 'greedyTol':1e-2, 'S':2, 'basis':polyBasis} if dim == 2: mesh = fen.RectangleMesh(fen.Point(0., 0.), fen.Point(.1, .15), 10, 15) x, y = fen.SpatialCoordinate(mesh)[:] f = fen.exp(- 1e2 * (x + y)) else:#if dim == 3: mesh = fen.BoxMesh(fen.Point(0., 0., 0.), fen.Point(.1, .15, .25), 4, 6,10) x, y, z = fen.SpatialCoordinate(mesh)[:] f = fen.exp(- 1e2 * (x + y + z)) solver = HPE(verbosity = verb) solver.omega = np.real(k0) solver.V = fen.FunctionSpace(mesh, "P", 3) solver.refractionIndex = fen.Constant(1. / 54.6) solver.forcingTerm = f solver.NeumannBoundary = "ALL" ######### if algo == "Pade": approx = Pade(solver, mu0 = k0, approxParameters = params, verbosity = verb) else: approx = RB(solver, mu0 = k0, approxParameters = params, verbosity = verb) if timed: from time import clock start_time = clock() approx.greedy() print("Time: ", clock() - start_time) else: approx.greedy(True) approx.samplingEngine.verbosity = 0 approx.verbosity = 0 kl, kr = np.real(kl), np.real(kr) from matplotlib import pyplot as plt normApp = np.zeros(len(k0s)) norm = np.zeros_like(normApp) res = np.zeros_like(normApp) err = np.zeros_like(normApp) for j in range(len(k0s)): normApp[j] = approx.normApprox(k0s[j]) norm[j] = approx.normHF(k0s[j]) res[j] = (approx.estNormer.norm(approx.getRes(k0s[j])) / approx.estNormer.norm(approx.getRHS(k0s[j]))) err[j] = approx.normErr(k0s[j]) / approx.normHF(k0s[j]) resApp = approx.errorEstimator(k0s) plt.figure() plt.plot(k0s, norm) plt.plot(k0s, normApp, '--') plt.plot(np.real(approx.mus), 1.05*np.max(norm)*np.ones_like(approx.mus, dtype = float), 'rx') plt.xlim([kl, kr]) plt.grid() plt.show() plt.close() plt.figure() plt.semilogy(k0s, res) plt.semilogy(k0s, resApp, '--') plt.semilogy(np.real(approx.mus), 4.*np.max(resApp)*np.ones_like(approx.mus, dtype = float), 'rx') plt.xlim([kl, kr]) plt.grid() plt.show() plt.close() plt.figure() plt.semilogy(k0s, err) plt.xlim([kl, kr]) plt.grid() plt.show() plt.close() polesApp = approx.getPoles() mask = (np.real(polesApp) < kl) | (np.real(polesApp) > kr) print("Outliers:", polesApp[mask]) polesApp = polesApp[~mask] plt.figure() plt.plot(np.real(polesApp), np.imag(polesApp), 'kx') plt.axis('equal') plt.grid() plt.show() plt.close() diff --git a/examples/from_papers/greedy_scatteringAirfoil.py b/examples/from_papers/greedy_scatteringAirfoil.py index 09e6a59..4688ac7 100644 --- a/examples/from_papers/greedy_scatteringAirfoil.py +++ b/examples/from_papers/greedy_scatteringAirfoil.py @@ -1,146 +1,146 @@ import numpy as np import fenics as fen import ufl from rrompy.hfengines.linear_problem import \ HelmholtzBoxScatteringProblemEngine as HSP -from rrompy.reduction_methods.lagrange_greedy import \ - ApproximantLagrangePadeGreedy as Pade -from rrompy.reduction_methods.lagrange_greedy import \ - ApproximantLagrangeRBGreedy as RB -from rrompy.utilities.base.fenics import fenONE +from rrompy.reduction_methods.distributed_greedy import \ + RationalInterpolantGreedy as Pade +from rrompy.reduction_methods.distributed_greedy import \ + RBDistributedGreedy as RB +from rrompy.utilities.fenics import fenONE verb = 2 timed = False algo = "Pade" #algo = "RB" polyBasis = "LEGENDRE" #polyBasis = "CHEBYSHEV" #polyBasis = "MONOMIAL" homog = True homog = False k0s = np.linspace(5, 20, 25) k0 = np.mean(k0s) kl, kr = min(k0s), max(k0s) params = {'muBounds':[kl, kr], 'nTestPoints':500, 'Delta':0, - 'greedyTol':1e-2, 'nTrainPoints':2, 'basis':polyBasis} + 'greedyTol':1e-2, 'S':2, 'basis':polyBasis} ######### PI = np.pi R = 2 def Dboundary(x, on_boundary): return on_boundary and (x[0]**2+x[1]**2)**.5 < .95 * R kappa = 10 theta = PI * - 45 / 180. mu = 1.1 epsilon = .1 mesh = fen.Mesh('../data/mesh/airfoil.xml') c, s = np.cos(theta), np.sin(theta) x, y = fen.SpatialCoordinate(mesh)[:] u0R = - fen.cos(kappa * (c * x + s * y)) u0I = - fen.sin(kappa * (c * x + s * y)) checkReal = x**2-x+y**2 rhop5 = ((x**2+y**2)/((x-1)**2+y**2))**.25 phiroot1 = fen.atan(-y/(x**2-x+y**2)) / 2 phiroot2 = fen.atan(-y/(x**2-x+y**2)) / 2 - PI * ufl.sign(-y/(x**2-x+y**2)) / 2 kappam1 = (((rhop5*fen.cos(phiroot1)+.5)**2.+(rhop5*fen.sin(phiroot1))**2.)/ ((rhop5*fen.cos(phiroot1)-1.)**2.+(rhop5*fen.sin(phiroot1))**2.) )**.5 - mu kappam2 = (((rhop5*fen.cos(phiroot2)+.5)**2.+(rhop5*fen.sin(phiroot2))**2.)/ ((rhop5*fen.cos(phiroot2)-1.)**2.+(rhop5*fen.sin(phiroot2))**2.) )**.5 - mu Heps1 = .9 * .5 * (1 + kappam1/epsilon + fen.sin(PI*kappam1/epsilon) / PI) + .1 Heps2 = .9 * .5 * (1 + kappam2/epsilon + fen.sin(PI*kappam2/epsilon) / PI) + .1 cTT = ufl.conditional(ufl.le(kappam1, epsilon), Heps1, fenONE) c_F = fen.Constant(.1) cFT = ufl.conditional(ufl.le(kappam2, epsilon), Heps2, fenONE) c_F = fen.Constant(.1) cT = ufl.conditional(ufl.ge(kappam1, - epsilon), cTT, c_F) cF = ufl.conditional(ufl.ge(kappam2, - epsilon), cFT, c_F) a = ufl.conditional(ufl.ge(checkReal, 0.), cT, cF) solver = HSP(R, np.real(k0), theta, n = 1, verbosity = verb, degree_threshold = 8) solver.omega = np.real(k0) solver.V = fen.FunctionSpace(mesh, "P", 3) solver.diffusivity = a solver.DirichletBoundary = Dboundary solver.RobinBoundary = "REST" solver.DirichletDatum = [u0R, u0I] ######### if algo == "Pade": approx = Pade(solver, mu0 = k0, approxParameters = params, verbosity = verb, homogeneized = homog) else: approx = RB(solver, mu0 = k0, approxParameters = params, verbosity = verb, homogeneized = homog) if timed: from time import clock start_time = clock() approx.greedy() print("Time: ", clock() - start_time) else: approx.greedy(True) approx.samplingEngine.verbosity = 0 approx.verbosity = 0 kl, kr = np.real(kl), np.real(kr) from matplotlib import pyplot as plt normApp = np.zeros(len(k0s)) norm = np.zeros_like(normApp) res = np.zeros_like(normApp) err = np.zeros_like(normApp) for j in range(len(k0s)): normApp[j] = approx.normApprox(k0s[j]) norm[j] = approx.normHF(k0s[j]) res[j] = (approx.estNormer.norm(approx.getRes(k0s[j], homogeneized=homog)) / approx.estNormer.norm(approx.getRHS(k0s[j], homogeneized=homog))) err[j] = approx.normErr(k0s[j]) / approx.normHF(k0s[j]) resApp = approx.errorEstimator(k0s) plt.figure() plt.plot(k0s, norm) plt.plot(k0s, normApp, '--') plt.plot(np.real(approx.mus), 1.05*np.max(norm)*np.ones_like(approx.mus, dtype = float), 'rx') plt.xlim([kl, kr]) plt.grid() plt.show() plt.close() plt.figure() plt.semilogy(k0s, res) plt.semilogy(k0s, resApp, '--') plt.semilogy(np.real(approx.mus), 4.*np.max(resApp)*np.ones_like(approx.mus, dtype = float), 'rx') plt.xlim([kl, kr]) plt.grid() plt.show() plt.close() plt.figure() plt.semilogy(k0s, err) plt.xlim([kl, kr]) plt.grid() plt.show() plt.close() polesApp = approx.getPoles() mask = (np.real(polesApp) < kl) | (np.real(polesApp) > kr) print("Outliers:", polesApp[mask]) polesApp = polesApp[~mask] plt.figure() plt.plot(np.real(polesApp), np.imag(polesApp), 'kx') plt.axis('equal') plt.grid() plt.show() plt.close() diff --git a/examples/from_papers/pod_membraneTaylor.py b/examples/from_papers/pod_membrane_centered.py similarity index 97% rename from examples/from_papers/pod_membraneTaylor.py rename to examples/from_papers/pod_membrane_centered.py index e19950e..965c695 100644 --- a/examples/from_papers/pod_membraneTaylor.py +++ b/examples/from_papers/pod_membrane_centered.py @@ -1,96 +1,96 @@ import fenics as fen import numpy as np from rrompy.hfengines.linear_problem import HelmholtzProblemEngine as HPE -from rrompy.reduction_methods.taylor import ApproximantTaylorPade as TP +from rrompy.reduction_methods.centered import RationalPade as TP from rrompy.utilities.parameter_sweeper import ParameterSweeper as Sweeper verb = 0 test = "poles" test = "error" k0 = 10 ktars = np.linspace(78**.5, 122**.5, 50) def boundaryNeumann(x, on_boundary): return on_boundary and x[1] > .25 and x[0] > 0.995 and x[0] < 1.005 meshname = '../data/mesh/crack_coarse.xml' #meshname = '../data/mesh/crack_fine.xml' mesh = fen.Mesh(meshname) x, y = fen.SpatialCoordinate(mesh)[:] x0, y0 = .5, .5 Rr, Ri = .1, .1 forcingTerm = fen.exp(- ((x - x0)**2 + (y - y0)**2) / 2 / Rr**2) solver = HPE(verbosity = verb) solver.omega = np.real(k0) solver.V = fen.FunctionSpace(mesh, "P", 3) solver.forcingTerm = forcingTerm solver.NeumannBoundary = boundaryNeumann solver.DirichletBoundary = 'rest' if test == "poles": appPoles = {} Emax = 13 params = {'N':6, 'M':0, 'E':6, 'sampleType':'Arnoldi', 'POD':True} approxPade = TP(solver, mu0 = k0, approxParameters = params, verbosity = verb) for E in range(6, Emax + 1): approxPade.E = E appPoles[E] = np.sort(approxPade.getPoles()) a = fen.dot(fen.grad(solver.u), fen.grad(solver.v)) * fen.dx A = fen.assemble(a) fen.DirichletBC(solver.V, fen.Constant(0.), solver.DirichletBoundary).apply(A) AMat = fen.as_backend_type(A).mat() Ar, Ac, Av = AMat.getValuesCSR() import scipy.sparse as scsp A = scsp.csr_matrix((Av, Ac, Ar), shape = AMat.size) m = fen.dot(solver.u, solver.v) * fen.dx M = fen.assemble(m) fen.DirichletBC(solver.V, fen.Constant(0.), solver.DirichletBoundary).apply(M) MMat = fen.as_backend_type(M).mat() Mr, Mc, Mv = MMat.getValuesCSR() import scipy.sparse as scsp M = scsp.csr_matrix((Mv, Mc, Mr), shape = MMat.size) poles = scsp.linalg.eigs(A, k = 7, M = M, sigma = 100., return_eigenvectors = False) II = np.argsort(np.abs(poles - k0)) poles = poles[II] print('Exact', end = ': ') [print('{},{}'.format(np.real(x), np.imag(x)), end = ',') for x in poles] print() for E in range(6, Emax + 1): print(E, end = ': ') [print('{},{}'.format(np.real(x), np.imag(x)), end = ',')\ for x in np.sort(appPoles[E])] print() elif test == "error": M0 = 5 Emax = 8 params = {'E':Emax, 'sampleType':'Arnoldi', 'POD':True} paramsSetsPade = [None] * (Emax - M0 + 1) for M in range(M0, Emax + 1): paramsSetsPade[M - M0] = {'N':M0 + 1, 'M':M, 'E':max(M, M0 + 1)} approxPade = TP(solver, mu0 = k0, approxParameters = params, verbosity = verb) sweeper = Sweeper(mutars = ktars, mostExpensive = 'Approx') sweeper.ROMEngine = approxPade sweeper.params = paramsSetsPade filenamePade = sweeper.sweep('membrane_error.dat', outputs = 'ALL') sweeper.plot(filenamePade, ['muRe'], ['normHF', 'normApprox'], ['M'], onePlot = True) sweeper.plot(filenamePade, ['muRe'], ['normErr'], ['M']) diff --git a/examples/from_papers/pod_scatteringAirfoil.py b/examples/from_papers/pod_scatteringAirfoil.py index 9696ce5..2f11c01 100644 --- a/examples/from_papers/pod_scatteringAirfoil.py +++ b/examples/from_papers/pod_scatteringAirfoil.py @@ -1,215 +1,144 @@ from copy import deepcopy as copy import numpy as np import fenics as fen import ufl from rrompy.hfengines.linear_problem import \ HelmholtzBoxScatteringProblemEngine as HSP -from rrompy.reduction_methods.taylor import ApproximantTaylorPade as TP -from rrompy.reduction_methods.taylor import ApproximantTaylorRB as TRB -from rrompy.reduction_methods.lagrange import ApproximantLagrangePade as LP -from rrompy.reduction_methods.lagrange import ApproximantLagrangeRB as LRB -from rrompy.utilities.parameter_sweeper import ParameterSweeper as Sweeper +from rrompy.reduction_methods.centered import RationalPade as PC +from rrompy.reduction_methods.centered import RBCentered as RBC +from rrompy.reduction_methods.distributed import RationalInterpolant as PD +from rrompy.reduction_methods.distributed import RBDistributed as RBD from rrompy.utilities.parameter_sampling import QuadratureSampler as QS -from rrompy.utilities.base.fenics import fenONE +from rrompy.utilities.fenics import fenONE from operator import itemgetter def subdict(d, ks): return dict(zip(ks, itemgetter(*ks)(d))) verb = 0 #################### homog = True #homog = False #################### test = "solve" -test = "Taylor" -test = "Lagrange" -test = "TaylorSweep" -test = "LagrangeSweep" +test = "Centered" +test = "Distributed" plotSamples = True k0 = 10 kLeft, kRight = 8 + 0.j, 12 + 0.j ktar = 11 ktars = np.linspace(8, 12, 21) + 0.j PI = np.pi R = 2 def Dboundary(x, on_boundary): return on_boundary and (x[0]**2+x[1]**2)**.5 < .95 * R kappa = 10 theta = PI * - 45 / 180. mu = 1.1 epsilon = .1 mesh = fen.Mesh('../data/mesh/airfoil.xml') c, s = np.cos(theta), np.sin(theta) x, y = fen.SpatialCoordinate(mesh)[:] u0R = - fen.cos(kappa * (c * x + s * y)) u0I = - fen.sin(kappa * (c * x + s * y)) checkReal = x**2-x+y**2 rhop5 = ((x**2+y**2)/((x-1)**2+y**2))**.25 phiroot1 = fen.atan(-y/(x**2-x+y**2)) / 2 phiroot2 = fen.atan(-y/(x**2-x+y**2)) / 2 - PI * ufl.sign(-y/(x**2-x+y**2)) / 2 kappam1 = (((rhop5*fen.cos(phiroot1)+.5)**2.+(rhop5*fen.sin(phiroot1))**2.)/ ((rhop5*fen.cos(phiroot1)-1.)**2.+(rhop5*fen.sin(phiroot1))**2.) )**.5 - mu kappam2 = (((rhop5*fen.cos(phiroot2)+.5)**2.+(rhop5*fen.sin(phiroot2))**2.)/ ((rhop5*fen.cos(phiroot2)-1.)**2.+(rhop5*fen.sin(phiroot2))**2.) )**.5 - mu Heps1 = .9 * .5 * (1 + kappam1/epsilon + fen.sin(PI*kappam1/epsilon) / PI) + .1 Heps2 = .9 * .5 * (1 + kappam2/epsilon + fen.sin(PI*kappam2/epsilon) / PI) + .1 cTT = ufl.conditional(ufl.le(kappam1, epsilon), Heps1, fenONE) c_F = fen.Constant(.1) cFT = ufl.conditional(ufl.le(kappam2, epsilon), Heps2, fenONE) c_F = fen.Constant(.1) cT = ufl.conditional(ufl.ge(kappam1, - epsilon), cTT, c_F) cF = ufl.conditional(ufl.ge(kappam2, - epsilon), cFT, c_F) a = ufl.conditional(ufl.ge(checkReal, 0.), cT, cF) ### solver = HSP(R, np.abs(k0), theta, n = 1, verbosity = verb, degree_threshold = 8) solver.V = fen.FunctionSpace(mesh, "P", 3) solver.diffusivity = a solver.DirichletBoundary = Dboundary solver.RobinBoundary = "REST" solver.DirichletDatum = [u0R, u0I] ### if test == "solve": uinc = solver.liftDirichletData(k0) if homog: uhtot = solver.solve(k0, homogeneized = homog) uh = uhtot + uinc else: uh = solver.solve(k0, homogeneized = homog) uhtot = uh - uinc print(solver.norm(uh)) print(solver.norm(uhtot)) solver.plot(fen.project(a, solver.V).vector(), what = 'Real', name = 'a') solver.plot(uinc, what = 'Real', name = 'u_inc') solver.plot(uh, what = 'ABS') solver.plot(uhtot, what = 'ABS', name = 'u + u_inc') -elif test in ["Taylor", "Lagrange"]: - if test == "Taylor": - params = {'N':8, 'M':8, 'R':8, 'E':8, - 'sampleType':'Arnoldi', 'POD':True} +elif test in ["Centered", "Distributed"]: + if test == "Centered": + params = {'N':8, 'M':8, 'R':8, 'E':8, 'sampleType':'Arnoldi', + 'POD':True} parPade = subdict(params, ['N', 'M', 'E', 'sampleType', 'POD']) parRB = subdict(params, ['R', 'E', 'sampleType', 'POD']) - approxPade = TP(solver, mu0 = k0, approxParameters = parPade, + approxPade = PC(solver, mu0 = k0, approxParameters = parPade, verbosity = verb, homogeneized = homog) - approxRB = TRB(solver, mu0 = k0, approxParameters = parRB, + approxRB = RBC(solver, mu0 = k0, approxParameters = parRB, verbosity = verb, homogeneized = homog) else: - params = {'N':8, 'M':8, 'R':9, 'S':9, 'POD':True, 'basis':"CHEBYSHEV"} - parPade = subdict(params, ['N', 'M', 'S', 'POD', 'basis']) - parRB = subdict(params, ['R', 'S', 'POD']) - approxPade = LP(solver, mu0 = np.mean([kLeft, kRight]), + params = {'N':8, 'M':8, 'R':9, 'S':9, 'POD':True, 'basis':"CHEBYSHEV", + 'sampler':QS([kLeft, kRight], "CHEBYSHEV")} + parPade = subdict(params, ['N', 'M', 'S', 'POD', 'basis', 'sampler']) + parRB = subdict(params, ['R', 'S', 'POD', 'sampler']) + approxPade = PD(solver, mu0 = np.mean([kLeft, kRight]), approxParameters = parPade, verbosity = verb, homogeneized = homog) - approxRB = LRB(solver, mu0 = np.mean([kLeft, kRight]), + approxRB = RBD(solver, mu0 = np.mean([kLeft, kRight]), approxParameters = parRB, verbosity = verb, homogeneized = homog) - approxPade.sampler = QS([kLeft, kRight], "CHEBYSHEV") - approxRB.sampler = QS([kLeft, kRight], "CHEBYSHEV") approxPade.setupApprox() approxRB.setupApprox() if plotSamples: approxPade.plotSamples() approxPade.plotHF(ktar, name = 'u_HF') approxPade.plotApprox(ktar, name = 'u_Pade''') approxPade.plotErr(ktar, name = 'err_Pade''') approxPade.plotRes(ktar, name = 'res_Pade''') approxRB.plotApprox(ktar, name = 'u_RB') approxRB.plotErr(ktar, name = 'err_RB') approxRB.plotRes(ktar, name = 'res_RB') HFNorm, RHSNorm = approxPade.normHF(ktar), approxPade.normRHS(ktar) PadeRes, PadeErr = approxPade.normRes(ktar), approxPade.normErr(ktar) RBRes, RBErr = approxRB.normRes(ktar), approxRB.normErr(ktar) print('HFNorm:\t{}\nRHSNorm:\t{}'.format(HFNorm, RHSNorm)) print('PadeRes:\t{}\nPadeErr:\t{}'.format(PadeRes, PadeErr)) print('RBRes:\t{}\nRBErr:\t{}'.format(RBRes, RBErr)) print('\nPoles Pade'':') print(approxPade.getPoles()) -elif test in ["TaylorSweep", "LagrangeSweep"]: - if test == "TaylorSweep": - shift = 10 - nsets = 2 - stride = 3 - Emax = stride * (nsets - 1) + shift + 1 - params = {'Emax':Emax, 'sampleType':'Arnoldi', 'POD':True} - paramsSetsPade = [None] * nsets - paramsSetsRB = [None] * nsets - for i in range(nsets): - paramsSetsPade[i] = {'N':stride*i+shift + 1, 'M':stride*i+shift+1, - 'E':stride*i+shift + 1} - paramsSetsRB[i] = {'E':stride*i+shift + 1,'R':stride*i+shift + 2} - approxPade = TP(solver, mu0 = kappa,approxParameters = params, - verbosity = verb, homogeneized = homog) - approxRB = TRB(solver, mu0 = kappa, approxParameters = params, - verbosity = verb, homogeneized = homog) - else: - shift = 10 - nsets = 2 - stride = 3 - Smax = stride * (nsets - 1) + shift + 2 - paramsPade = {'S':Smax, 'POD':True, 'basis':"CHEBYSHEV"} - paramsRB = copy(paramsPade) - paramsSetsPade = [None] * nsets - paramsSetsRB = [None] * nsets - for i in range(nsets): - paramsSetsPade[i] = {'N': stride*i+shift+1, 'M': stride*i+shift+1, - 'S': stride*i+shift+2} - paramsSetsRB[i] = {'R': stride*i+shift+2, 'S': stride*i+shift+2} - approxPade = LP(solver, mu0 = np.mean([kLeft, kRight]), - approxParameters = paramsPade, verbosity = verb, - homogeneized = homog) - approxPade.sampler = QS([kLeft, kRight], "CHEBYSHEV") - approxRB = LRB(solver, mu0 = np.mean([kLeft, kRight]), - approxParameters = paramsRB, verbosity = verb, - homogeneized = homog) - approxRB.sampler = QS([kLeft, kRight], "CHEBYSHEV") - - sweeper = Sweeper(mutars = ktars, mostExpensive = 'Approx') - - homogMSG = "" - if not homog: homogMSG = "Non" - filenamebase = '../data/output/airfoil' + test[:-5] + homogMSG + "Homog" - - sweeper.ROMEngine = approxPade - sweeper.params = paramsSetsPade - filenamePade = sweeper.sweep(filenamebase +'Pade.dat') - - sweeper.ROMEngine = approxRB - sweeper.params = paramsSetsRB - filenameRB = sweeper.sweep(filenamebase +'RB.dat') - - if test == "TaylorSweep": - constr = ['E'] - else: - constr = ['S'] - sweeper.plotCompare([filenamePade, filenameRB], ['muRe'], - ['normHF', 'normApp'], constr, onePlot = True, - save = filenamebase + 'Norm', - saveFormat = "png", labels = ["Pade'", "RB"]) - sweeper.plotCompare([filenamePade, filenameRB], ['muRe'], ['normResRel'], - constr, save = filenamebase + 'Res', - saveFormat = "png", labels = ["Pade'", "RB"]) - sweeper.plotCompare([filenamePade, filenameRB], ['muRe'], ['normErrRel'], - constr, save = filenamebase + 'Err', - saveFormat = "png", labels = ["Pade'", "RB"]) diff --git a/examples/greedy/matrix_greedy.py b/examples/greedy/matrix_greedy.py index 4d4918a..c5807c9 100644 --- a/examples/greedy/matrix_greedy.py +++ b/examples/greedy/matrix_greedy.py @@ -1,114 +1,113 @@ import numpy as np import scipy.sparse as sp from matplotlib import pyplot as plt from rrompy.hfengines.base import MatrixEngineBase as MEB -from rrompy.reduction_methods.lagrange_greedy import \ - ApproximantLagrangePadeGreedy as Pade -from rrompy.reduction_methods.lagrange_greedy import \ - ApproximantLagrangeRBGreedy as RB +from rrompy.reduction_methods.distributed_greedy import \ + RationalInterpolantGreedy as Pade +from rrompy.reduction_methods.distributed_greedy import \ + RBDistributedGreedy as RB test = 1 timed = False method = "Pade" #method = "RB" verb = 2 errorEstimatorKind = "BARE" #errorEstimatorKind = "BASIC" #errorEstimatorKind = "SIMPLIFIED" #errorEstimatorKind = "EXACT" N = 100 solver = MEB(verbosity = verb) solver.nAs = 2 if test == 1: solver.As = [sp.spdiags([np.arange(1, 1 + N)], [0], N, N), - sp.eye(N)] elif test == 2: solver.setSolver("SOLVE") fftB = np.fft.fft(np.eye(N)) * N**-.5 solver.As = [fftB.dot(np.multiply(np.arange(1, 1 + N), fftB.conj()).T), - np.eye(N)] np.random.seed(420) solver.nbs = 1 solver.bs = [np.random.randn(N) + 1.j * np.random.randn(N)] mu0 = 10.25 murange = [1.25, 19.25] mutars = np.linspace(murange[0], murange[1], 500) if method == "Pade": - params = {'muBounds':murange, 'nTestPoints':200, 'Delta':0, - 'greedyTol':1e-2, 'nTrainPoints':5, 'basis':"CHEBYSHEV", + params = {'muBounds':murange, 'nTestPoints':200, 'Delta':0, 'S':5, + 'greedyTol':1e-2, 'basis':"CHEBYSHEV", 'errorEstimatorKind':errorEstimatorKind} approx = Pade(solver, mu0 = mu0, approxParameters = params, verbosity = verb) elif method == "RB": - params = {'muBounds':murange, 'nTestPoints':500, 'greedyTol':1e-2, - 'nTrainPoints':5} + params = {'muBounds':murange, 'nTestPoints':500, 'greedyTol':1e-2, 'S':5} approx = RB(solver, mu0 = mu0, approxParameters = params, verbosity = verb) approx.estNormer = solver if timed: from time import clock start_time = clock() approx.greedy() print("Time: ", clock() - start_time) else: approx.greedy(True) approx.samplingEngine.verbosity = 0 approx.verbosity = 0 normApp = np.zeros(len(mutars)) norm = np.zeros_like(normApp) res = np.zeros_like(normApp) err = np.zeros_like(normApp) for j in range(len(mutars)): normApp[j] = approx.normApprox(mutars[j]) norm[j] = approx.normHF(mutars[j]) res[j] = (approx.estNormer.norm(approx.getRes(mutars[j])) / approx.estNormer.norm(approx.getRHS(mutars[j]))) err[j] = approx.normErr(mutars[j]) / approx.normHF(mutars[j]) resApp = approx.errorEstimator(mutars) plt.figure() plt.semilogy(mutars, norm) plt.semilogy(mutars, normApp, '--') plt.semilogy(np.real(approx.mus), 1.25*np.max(norm)*np.ones_like(approx.mus, dtype = float), 'rx') plt.xlim(murange) plt.grid() plt.show() plt.close() plt.figure() plt.semilogy(mutars, res) plt.semilogy(mutars, resApp, '--') plt.semilogy(np.real(approx.mus), 4.*np.max(resApp)*np.ones_like(approx.mus, dtype = float), 'rx') plt.xlim(murange) plt.grid() plt.show() plt.close() plt.figure() plt.semilogy(mutars, err) plt.xlim(murange) plt.grid() plt.show() plt.close() polesTrue = np.arange(1, 1 + N) polesTrue = polesTrue[polesTrue >= murange[0]] polesTrue = polesTrue[polesTrue <= murange[1]] polesApp = approx.getPoles() mask = (np.real(polesApp) < murange[0]) | (np.real(polesApp) > murange[1]) print("Outliers:", polesApp[mask]) polesApp = polesApp[~mask] plt.figure() plt.plot(np.real(polesApp), np.imag(polesApp), 'kx') plt.plot(polesTrue, np.zeros_like(polesTrue), 'm.') plt.axis('equal') plt.grid() plt.show() plt.close() diff --git a/examples/greedy/parametricDomain.py b/examples/greedy/parametricDomain.py index 8724384..f478e15 100644 --- a/examples/greedy/parametricDomain.py +++ b/examples/greedy/parametricDomain.py @@ -1,99 +1,99 @@ import numpy as np from rrompy.hfengines.linear_problem import \ HelmholtzSquareBubbleDomainProblemEngine as HSBDPE -from rrompy.reduction_methods.lagrange_greedy import \ - ApproximantLagrangePadeGreedy as Pade -from rrompy.reduction_methods.lagrange_greedy import \ - ApproximantLagrangeRBGreedy as RB +from rrompy.reduction_methods.distributed_greedy import \ + RationalInterpolantGreedy as Pade +from rrompy.reduction_methods.distributed_greedy import \ + RBDistributedGreedy as RB verb = 2 timed = True algo = "Pade" #algo = "RB" polyBasis = "LEGENDRE" #polyBasis = "CHEBYSHEV" #polyBasis = "MONOMIAL" errorEstimatorKind = "SIMPLIFIED" errorEstimatorKind = "EXACT" k0s = np.power(np.linspace(9, 19, 100), .5) k0 = np.mean(np.power(k0s, 2.)) ** .5 kl, kr = min(k0s), max(k0s) params = {'muBounds':[kl, kr], 'nTestPoints':500, 'Delta':0, - 'greedyTol':1e-2, 'nTrainPoints':2, 'basis':polyBasis, + 'greedyTol':1e-2, 'S':2, 'basis':polyBasis, 'errorEstimatorKind':errorEstimatorKind} if timed: verb = 0 solver = HSBDPE(kappa = 12 ** .5, theta = np.pi / 3, n = 20, mu0 = k0, degree_threshold = 15, verbosity = verb) solver.omega = np.real(k0) if algo == "Pade": approx = Pade(solver, mu0 = k0, approxParameters = params, verbosity = verb) else: approx = RB(solver, mu0 = k0, approxParameters = params, verbosity = verb) if timed: from time import clock start_time = clock() approx.greedy() print("Time: ", clock() - start_time) else: approx.greedy(True) approx.samplingEngine.verbosity = 0 approx.verbosity = 0 from matplotlib import pyplot as plt normApp = np.zeros(len(k0s)) norm = np.zeros_like(normApp) res = np.zeros_like(normApp) err = np.zeros_like(normApp) for j in range(len(k0s)): normApp[j] = approx.normApprox(k0s[j]) norm[j] = approx.normHF(k0s[j]) res[j] = (approx.estNormer.norm(approx.getRes(k0s[j])) / approx.estNormer.norm(approx.getRHS(k0s[j]))) err[j] = approx.normErr(k0s[j]) / approx.normHF(k0s[j]) resApp = approx.errorEstimator(k0s) plt.figure() plt.semilogy(k0s, norm) plt.semilogy(k0s, normApp, '--') plt.semilogy(np.real(approx.mus), 1.25*np.max(norm)*np.ones_like(approx.mus, dtype = float), 'rx') plt.xlim([kl, kr]) plt.grid() plt.show() plt.close() plt.figure() plt.semilogy(k0s, res) plt.semilogy(k0s, resApp, '--') plt.semilogy(np.real(approx.mus), 4.*np.max(resApp)*np.ones_like(approx.mus, dtype = float), 'rx') plt.xlim([kl, kr]) plt.grid() plt.show() plt.close() plt.figure() plt.semilogy(k0s, err) plt.xlim([kl, kr]) plt.grid() plt.show() plt.close() polesApp = approx.getPoles() mask = (np.real(polesApp) < kl) | (np.real(polesApp) > kr) print("Outliers:", polesApp[mask]) polesApp = polesApp[~mask] plt.figure() plt.plot(np.real(polesApp), np.imag(polesApp), 'kx') plt.axis('equal') plt.grid() plt.show() plt.close() diff --git a/examples/greedy/squareBubbleHomog.py b/examples/greedy/squareBubbleHomog.py index 0824d63..cb6473b 100644 --- a/examples/greedy/squareBubbleHomog.py +++ b/examples/greedy/squareBubbleHomog.py @@ -1,113 +1,113 @@ import numpy as np from rrompy.hfengines.linear_problem import \ HelmholtzSquareBubbleProblemEngine as HSBPE -from rrompy.reduction_methods.lagrange_greedy import \ - ApproximantLagrangePadeGreedy as Pade -from rrompy.reduction_methods.lagrange_greedy import \ - ApproximantLagrangeRBGreedy as RB +from rrompy.reduction_methods.distributed_greedy import \ + RationalInterpolantGreedy as Pade +from rrompy.reduction_methods.distributed_greedy import \ + RBDistributedGreedy as RB from rrompy.utilities.base import squareResonances verb = 2 timed = False algo = "Pade" #algo = "RB" polyBasis = "LEGENDRE" #polyBasis = "CHEBYSHEV" #polyBasis = "MONOMIAL" errorEstimatorKind = "BARE" #errorEstimatorKind = "BASIC" #errorEstimatorKind = "SIMPLIFIED" #errorEstimatorKind = "EXACT" k0s = np.power(np.linspace(95, 149, 250), .5) #k0s = np.power(np.linspace(95, 129, 100), .5) #k0s = np.power(np.linspace(95, 109, 100), .5) k0 = np.mean(np.power(k0s, 2.)) ** .5 kl, kr = min(k0s), max(k0s) polesexact = np.unique(np.power(squareResonances(kl**2., kr**2., False), .5)) params = {'muBounds':[kl, kr], 'nTestPoints':500, 'Delta':0, - 'greedyTol':1e-2, 'nTrainPoints':10, 'polybasis':polyBasis, + 'greedyTol':1e-2, 'S':10, 'polybasis':polyBasis, 'errorEstimatorKind':errorEstimatorKind, 'interactive':True} if timed: verb = 0 solver = HSBPE(kappa = 12 ** .5, theta = np.pi / 3, n = 20, verbosity = verb) solver.omega = np.real(k0) if algo == "Pade": approx = Pade(solver, mu0 = k0, approxParameters = params, verbosity = verb) else: approx = RB(solver, mu0 = k0, approxParameters = params, verbosity = verb) if timed: from time import clock start_time = clock() approx.greedy() print("Time: ", clock() - start_time) else: approx.greedy(True) approx.samplingEngine.verbosity = 0 approx.trainedModel.verbosity = 0 approx.verbosity = 0 from matplotlib import pyplot as plt normApp = np.zeros_like(k0s) norm = np.zeros_like(k0s) res = np.zeros_like(k0s) err = np.zeros_like(k0s) for j in range(len(k0s)): normApp[j] = approx.normApprox(k0s[j]) norm[j] = approx.normHF(k0s[j]) res[j] = (approx.estNormer.norm(approx.getRes(k0s[j])) / approx.estNormer.norm(approx.getRHS(k0s[j]))) err[j] = approx.normErr(k0s[j]) / approx.normHF(k0s[j]) resApp = approx.errorEstimator(k0s) plt.figure() plt.semilogy(k0s, norm) plt.semilogy(k0s, normApp, '--') plt.semilogy(polesexact, 2.*np.max(norm)*np.ones_like(polesexact, dtype = float), 'm.') plt.semilogy(np.real(approx.mus), 4.*np.max(norm)*np.ones_like(approx.mus, dtype = float), 'rx') plt.xlim([kl, kr]) plt.grid() plt.show() plt.close() plt.figure() plt.semilogy(k0s, res) plt.semilogy(k0s, resApp, '--') plt.semilogy(polesexact, 2.*np.max(resApp)*np.ones_like(polesexact, dtype = float), 'm.') plt.semilogy(np.real(approx.mus), 4.*np.max(resApp)*np.ones_like(approx.mus, dtype = float), 'rx') plt.xlim([kl, kr]) plt.grid() plt.show() plt.close() plt.figure() plt.semilogy(k0s, err) plt.semilogy(polesexact, 2.*np.max(err)*np.ones_like(polesexact, dtype = float), 'm.') plt.xlim([kl, kr]) plt.grid() plt.show() plt.close() polesApp = approx.getPoles() mask = (np.real(polesApp) < kl) | (np.real(polesApp) > kr) print("Outliers:", polesApp[mask]) polesApp = polesApp[~mask] plt.figure() plt.plot(np.real(polesApp), np.imag(polesApp), 'kx') plt.plot(np.real(polesexact), np.imag(polesexact), 'm.') plt.axis('equal') plt.grid() plt.show() plt.close() diff --git a/examples/greedy/squareScatteringHomog.py b/examples/greedy/squareScatteringHomog.py index dc6ff4e..1faa60c 100644 --- a/examples/greedy/squareScatteringHomog.py +++ b/examples/greedy/squareScatteringHomog.py @@ -1,103 +1,103 @@ import numpy as np from rrompy.hfengines.linear_problem import \ HelmholtzCavityScatteringProblemEngine as HCSPE -from rrompy.reduction_methods.lagrange_greedy import \ - ApproximantLagrangePadeGreedy as Pade -from rrompy.reduction_methods.lagrange_greedy import \ - ApproximantLagrangeRBGreedy as RB +from rrompy.reduction_methods.distributed_greedy import \ + RationalInterpolantGreedy as Pade +from rrompy.reduction_methods.distributed_greedy import \ + RBDistributedGreedy as RB verb = 2 timed = True algo = "Pade" #algo = "RB" polyBasis = "LEGENDRE" polyBasis = "CHEBYSHEV" #polyBasis = "MONOMIAL" errorEstimatorKind = "BARE" #errorEstimatorKind = "BASIC" #errorEstimatorKind = "SIMPLIFIED" #errorEstimatorKind = "EXACT" k0s = np.linspace(10, 15, 100) k0 = np.mean(k0s) kl, kr = min(k0s), max(k0s) params = {'muBounds':[kl, kr], 'nTestPoints':500, 'Delta':0, - 'greedyTol':1e-3, 'nTrainPoints':2, 'polybasis':polyBasis, + 'greedyTol':1e-3, 'S':2, 'polybasis':polyBasis, 'errorEstimatorKind':errorEstimatorKind} if timed: verb = 0 solver = HCSPE(kappa = 5, n = 20, verbosity = verb) solver.omega = np.real(k0) if algo == "Pade": approx = Pade(solver, mu0 = k0, approxParameters = params, verbosity = verb) else: params.pop('Delta') params.pop('polybasis') params.pop('errorEstimatorKind') approx = RB(solver, mu0 = k0, approxParameters = params, verbosity = verb) if timed: from time import clock start_time = clock() approx.greedy() print("Time: ", clock() - start_time) else: approx.greedy(True) approx.samplingEngine.verbosity = 0 approx.trainedModel.verbosity = 0 approx.verbosity = 0 from matplotlib import pyplot as plt normApp = np.zeros(len(k0s)) norm = np.zeros_like(normApp) res = np.zeros_like(normApp) err = np.zeros_like(normApp) for j in range(len(k0s)): normApp[j] = approx.normApprox(k0s[j]) norm[j] = approx.normHF(k0s[j]) res[j] = (approx.estNormer.norm(approx.getRes(k0s[j])) / approx.estNormer.norm(approx.getRHS(k0s[j]))) err[j] = approx.normErr(k0s[j]) / approx.normHF(k0s[j]) resApp = approx.errorEstimator(k0s) plt.figure() plt.plot(k0s, norm) plt.plot(k0s, normApp, '--') plt.plot(np.real(approx.mus), 1.25*np.max(norm)*np.ones_like(approx.mus, dtype = float), 'rx') plt.xlim([kl, kr]) plt.grid() plt.show() plt.close() plt.figure() plt.semilogy(k0s, res) plt.semilogy(k0s, resApp, '--') plt.semilogy(np.real(approx.mus), 4.*np.max(resApp)*np.ones_like(approx.mus, dtype = float), 'rx') plt.xlim([kl, kr]) plt.grid() plt.show() plt.close() plt.figure() plt.semilogy(k0s, err) plt.xlim([kl, kr]) plt.grid() plt.show() plt.close() polesApp = approx.getPoles() mask = (np.real(polesApp) < kl) | (np.real(polesApp) > kr) print("Outliers:", polesApp[mask]) polesApp = polesApp[~mask] plt.figure() plt.plot(np.real(polesApp), np.imag(polesApp), 'kx') plt.axis('equal') plt.grid() plt.show() plt.close() diff --git a/examples/greedy/squareTransmissionNonHomog.py b/examples/greedy/squareTransmissionNonHomog.py index fe9b8c8..66bcdc2 100644 --- a/examples/greedy/squareTransmissionNonHomog.py +++ b/examples/greedy/squareTransmissionNonHomog.py @@ -1,108 +1,108 @@ import numpy as np from rrompy.hfengines.linear_problem import \ HelmholtzSquareTransmissionProblemEngine as HSTPE -from rrompy.reduction_methods.lagrange_greedy import \ - ApproximantLagrangeRBGreedy as RB -from rrompy.reduction_methods.lagrange_greedy import \ - ApproximantLagrangePadeGreedy as Pade +from rrompy.reduction_methods.distributed_greedy import \ + RationalInterpolantGreedy as Pade +from rrompy.reduction_methods.distributed_greedy import \ + RBDistributedGreedy as RB timed = False verb = 2 algo = "Pade" #algo = "RB" polyBasis = "LEGENDRE" #polyBasis = "CHEBYSHEV" #polyBasis = "MONOMIAL" homog = True #homog = False errorEstimatorKind = "SIMPLIFIED" errorEstimatorKind = "EXACT" k0s = np.power(np.linspace(4, 15, 100), .5) k0 = np.mean(np.power(k0s, 2.)) ** .5 kl, kr = min(k0s), max(k0s) rescaling = lambda x: np.power(x, 2.) rescalingInv = lambda x: np.power(x, .5) params = {'muBounds':[kl, kr], 'nTestPoints':500, 'Delta':0, - 'greedyTol':1e-2, 'nTrainPoints':5, 'basis':polyBasis, + 'greedyTol':1e-2, 'S':5, 'basis':polyBasis, 'errorEstimatorKind':errorEstimatorKind} solver = HSTPE(nT = 1, nB = 2, theta = np.pi * 20 / 180, kappa = 4., n = 20, verbosity = verb) solver.omega = np.real(k0) if algo == "Pade": approx = Pade(solver, mu0 = k0, approxParameters = params, verbosity = verb, homogeneized = homog) else: approx = RB(solver, mu0 = k0, approxParameters = params, verbosity = verb, homogeneized = homog) if timed: from time import clock start_time = clock() approx.greedy() print("Time: ", clock() - start_time) else: approx.greedy(True) approx.samplingEngine.verbosity = 0 approx.verbosity = 0 from matplotlib import pyplot as plt normApp = np.zeros(len(k0s)) norm = np.zeros_like(normApp) res = np.zeros_like(normApp) err = np.zeros_like(normApp) for j in range(len(k0s)): normApp[j] = approx.normApprox(k0s[j]) norm[j] = approx.normHF(k0s[j]) res[j] = (approx.estNormer.norm(approx.getRes(k0s[j], homogeneized=homog)) / approx.estNormer.norm(approx.getRHS(k0s[j], homogeneized=homog))) err[j] = approx.normErr(k0s[j]) / approx.normHF(k0s[j]) resApp = approx.errorEstimator(k0s) polesApp = approx.getPoles() polesApp = polesApp[np.abs(np.imag(polesApp)) < 1e-3] plt.figure() plt.semilogy(k0s, norm) plt.semilogy(k0s, normApp, '--') plt.semilogy(np.real(approx.mus), 4.*np.max(norm)*np.ones_like(approx.mus, dtype = float), 'rx') plt.semilogy(np.real(polesApp), 2.*np.max(norm)*np.ones_like(polesApp, dtype = float), 'k.') plt.xlim([kl, kr]) plt.grid() plt.show() plt.close() plt.figure() plt.semilogy(k0s, res) plt.semilogy(k0s, resApp, '--') plt.semilogy(np.real(approx.mus), 4.*np.max(resApp)*np.ones_like(approx.mus, dtype = float), 'rx') plt.semilogy(np.real(polesApp), 2.*np.max(resApp)*np.ones_like(polesApp, dtype = float), 'k.') plt.xlim([kl, kr]) plt.grid() plt.show() plt.close() plt.figure() plt.semilogy(k0s, err) plt.semilogy(np.real(polesApp), 2.*np.max(err)*np.ones_like(polesApp, dtype = float), 'k.') plt.xlim([kl, kr]) plt.grid() plt.show() plt.close() polesApp = approx.getPoles() mask = (np.real(polesApp) < kl) | (np.real(polesApp) > kr) print("Outliers:", polesApp[mask]) polesApp = polesApp[~mask] plt.figure() plt.plot(np.real(polesApp), np.imag(polesApp), 'kx') plt.axis('equal') plt.grid() plt.show() plt.close() diff --git a/examples/pod/LagrangeSweep.py b/examples/pod/LagrangeSweep.py deleted file mode 100644 index 43b6c6c..0000000 --- a/examples/pod/LagrangeSweep.py +++ /dev/null @@ -1,110 +0,0 @@ -from copy import copy -import numpy as np -#from rrompy.hfengines.linear_problem import \ -# HelmholtzBoxScatteringProblemEngine as HBSPE -from rrompy.hfengines.linear_problem import \ - HelmholtzCavityScatteringProblemEngine as HCSPE -from rrompy.reduction_methods.lagrange import ApproximantLagrangePade as Pade -from rrompy.reduction_methods.lagrange import ApproximantLagrangeRB as RB -from rrompy.utilities.parameter_sweeper import ParameterSweeper as Sweeper -from rrompy.utilities.parameter_sampling import QuadratureSampler as QS -from rrompy.utilities.parameter_sampling import WarpingFunction as WF -from rrompy.utilities.parameter_sampling import WarpingSampler as WS - -verb = 0 -npoints = 100 -homog = True -homog = False -LSratio = 2. / 3. -sampling = "Uniform" -sampling = "Cheby" -#sampling = "SinC" - -assert LSratio <= 1. + np.finfo(float).eps - -ks = [10 + 0.j, 14 + 0.j] -solver = HCSPE(kappa = 5, n = 20) -solver.omega = np.real(np.mean(ks)) -mutars = np.linspace(9, 15, npoints) -homogMSG = "Homog" -if not homog: homogMSG = "Non" + homogMSG -filenamebase = '../data/output/ScatteringSquareLSweep' -#filenamebase = '../data/plots/LagrangeScatteringSquare1p5' -#filenamebase = filenamebase + sampling + "/HelmholtzBoxLSweep" + homogMSG - -k0 = np.mean(ks) -shift = 7 -shift = np.int(8 / LSratio - 1) -nsets = 3 -stride = np.int(8 / LSratio) -Smax = stride * (nsets - 1) + shift + 2 - -rescaling = lambda x: np.power(x, 2.) -rescalingInv = lambda x: np.power(x, .5) -if sampling == "Uniform": - polyBasis = "MONOMIAL" - sampler = QS(ks, "UNIFORM", rescaling, rescalingInv) -if sampling == "Cheby": - polyBasis = "CHEBYSHEV" - sampler = QS(ks, "CHEBYSHEV", rescaling, rescalingInv) -if sampling == "SinC": - polyBasis = "MONOMIAL" - warping = WF(call = lambda x: (x - 2. * (1. - LSratio) / np.pi - * np.sin(np.pi * x)), - repr = "x-{}*sin(pi*x)".format(2. * (1. - LSratio) / np.pi)) - sampler = WS(ks, warping, rescaling, rescalingInv) - -paramsPade = {'S':Smax, 'POD':True, 'polybasis':polyBasis} -paramsRB = copy(paramsPade) -paramsPoly = copy(paramsPade) -paramsSetsPade = [None] * nsets -paramsSetsRB = [None] * nsets -paramsSetsPoly = [None] * nsets -for i in range(nsets): - paramsSetsPade[i] = {'N': np.int(LSratio * (stride * i + shift + 1)), - 'M': np.int(LSratio * (stride * i + shift + 1)), - 'S': stride * i + shift + 2} - paramsSetsRB[i] = {'R': np.int(LSratio * (stride * i + shift + 1)), - 'S': stride * i + shift + 2} - paramsSetsPoly[i] = {'N': 0, - 'M': np.int(LSratio * (stride * i + shift + 1)), - 'S': stride * i + shift + 2} - -appPade = Pade(solver, mu0 = k0, approxParameters = paramsPade, - verbosity = verb, homogeneized = homog) -appPade.sampler = sampler -appRB = RB(solver, mu0 = k0, approxParameters = paramsRB, - verbosity = verb, homogeneized = homog) -appRB.sampler = sampler -appPoly = Pade(solver, mu0 = k0, approxParameters = paramsPoly, - verbosity = verb, homogeneized = homog) - -sweeper = Sweeper(mutars = mutars, mostExpensive = 'Approx') -sweeper.ROMEngine = appPade -sweeper.params = paramsSetsPade -filenamePade = sweeper.sweep(filenamebase + 'Pade.dat') -sweeper.ROMEngine = appRB -sweeper.params = paramsSetsRB -filenameRB = sweeper.sweep(filenamebase + 'RB.dat') -sweeper.ROMEngine = appPoly -sweeper.params = paramsSetsPoly -filenamePoly = sweeper.sweep(filenamebase + 'Poly.dat') - -sweeper.plotCompare([filenamePade, filenameRB, filenamePoly], ['muRe'], - ['normHF', 'normApprox'], ['S'], onePlot = True, - save = filenamebase + 'Norm', ylims = {'top' : 1e1}, - saveFormat = "png", labels = ["Pade'", "RB", "Poly"], -# figsize = (5, 3.75)) - figsize = (10, 7.5)) -sweeper.plotCompare([filenamePade, filenameRB, filenamePoly], ['muRe'], - ['normResRel'], ['S'], save = filenamebase + 'Res', - ylims = {'top' : 1e1}, saveFormat = "png", - labels = ["Pade'", "RB", "Poly"], -# figsize = (5, 3.75)) - figsize = (10, 7.5)) -sweeper.plotCompare([filenamePade, filenameRB, filenamePoly], ['muRe'], - ['normErrRel'], ['S'], save = filenamebase + 'Err', - ylims = {'top' : 1e1}, saveFormat = "png", - labels = ["Pade'", "RB", "Poly"], -# figsize = (5, 3.75)) - figsize = (10, 7.5)) diff --git a/examples/pod/LagrangeSweepUnstable.py b/examples/pod/LagrangeSweepUnstable.py deleted file mode 100644 index a68c848..0000000 --- a/examples/pod/LagrangeSweepUnstable.py +++ /dev/null @@ -1,77 +0,0 @@ -from copy import copy -import numpy as np -from rrompy.hfengines.linear_problem import \ - HelmholtzSquareBubbleProblemEngine as HSBPE -from rrompy.reduction_methods.lagrange import ApproximantLagrangePade as Pade -from rrompy.reduction_methods.lagrange import ApproximantLagrangeRB as RB -from rrompy.utilities.parameter_sweeper import ParameterSweeper as Sweeper -from rrompy.utilities.parameter_sampling import ManualSampler as MS - -npoints = 50 - -solver = HSBPE(kappa = 12 ** .5, theta = np.pi / 3, n = 30, verbosity = 0) -mutars = np.linspace(6**.5, 16**.5, npoints) -filenamebase = '../data/output/HelmholtzBubbleLagrange' - -k0 = np.mean(mutars) - -rescaling = lambda x: np.power(x, 2.) -rescalingInv = lambda x: np.power(x, .5) - -paramsChoices = { - 'Stable':{'S':4, 'POD':True, 'basis':"MONOMIAL", - 'sampler':MS([6**.5, 16**.5], [14**.5, 12**.5, 9**.5, 11**.5])}, - 'Unstable':{'S':4, 'POD':True, 'basis':"MONOMIAL", - 'sampler':MS([6**.5, 16**.5], [8**.5, 10**.5, 13**.5, 11**.5])} - } - -j = 0 -filenamePade = [None] * len(paramsChoices.keys()) -filenameRB = [None] * len(paramsChoices.keys()) -for typeP in paramsChoices.keys(): - paramsPade = paramsChoices[typeP] - sampler = paramsPade.pop("sampler") - paramsRB = copy(paramsPade) - paramsSetsPade = [{'N': 3, 'M': 3}] - paramsSetsRB = [{'R': 4}] - - appPade = Pade(solver, mu0 = k0, approxParameters = paramsPade, - verbosity = 0) - appPade.sampler = sampler - appRB = RB(solver, mu0 = k0, approxParameters = paramsRB, - verbosity = 0) - appRB.sampler = sampler - - sweeper = Sweeper(mutars = mutars, mostExpensive = 'Approx') - sweeper.ROMEngine = appPade - sweeper.params = paramsSetsPade - filenamePade[j] = sweeper.sweep(filenamebase + 'Pade{}.dat'.format(typeP), - verbose = 0) - sweeper.ROMEngine = appRB - sweeper.params = paramsSetsRB - filenameRB[j] = sweeper.sweep(filenamebase + 'RB{}.dat'.format(typeP), - verbose = 0) - j += 1 - -print("Pade'") -sweeper.plotCompare(filenamePade, ['muRe'], - ['normHF', 'normApp'], ['S'], onePlot = True, - save = filenamebase + 'PadeNorm', saveFormat = "png", - labels = list(paramsChoices.keys())) -sweeper.plotCompare(filenamePade, ['muRe'], ['normResRel'], - ['S'], save = filenamebase + 'PadeRes', saveFormat = "png", - labels = list(paramsChoices.keys())) -sweeper.plotCompare(filenamePade, ['muRe'], ['normErrRel'], - ['S'], save = filenamebase + 'PadeErr', saveFormat = "png", - labels = list(paramsChoices.keys())) -print("RB") -sweeper.plotCompare(filenameRB, ['muRe'], - ['normHF', 'normApp'], ['S'], onePlot = True, - save = filenamebase + 'RBNorm', saveFormat = "png", - labels = list(paramsChoices.keys())) -sweeper.plotCompare(filenameRB, ['muRe'], ['normResRel'], - ['S'], save = filenamebase + 'RBRes', saveFormat = "png", - labels = list(paramsChoices.keys())) -sweeper.plotCompare(filenameRB, ['muRe'], ['normErrRel'], - ['S'], save = filenamebase + 'RBErr', saveFormat = "png", - labels = list(paramsChoices.keys())) diff --git a/examples/pod/TaylorPoles.py b/examples/pod/PolesCentered.py similarity index 94% rename from examples/pod/TaylorPoles.py rename to examples/pod/PolesCentered.py index d2c5c71..b15f44a 100644 --- a/examples/pod/TaylorPoles.py +++ b/examples/pod/PolesCentered.py @@ -1,69 +1,69 @@ import numpy as np from rrompy.hfengines.linear_problem import \ HelmholtzSquareBubbleProblemEngine as HSBPE -from rrompy.reduction_methods.taylor import ApproximantTaylorPade as Pade -from rrompy.reduction_methods.taylor import ApproximantTaylorRB as RB +from rrompy.reduction_methods.centered import RationalPade as Pade +from rrompy.reduction_methods.centered import RBCentered as RB from rrompy.utilities.base import squareResonances verb = 0 k0 = (12+0.j) ** .5 Nmin, Nmax = 2, 10 Nvals = np.arange(Nmin, Nmax + 1, 2) params = {'N':Nmin, 'M':0, 'Emax':Nmax, 'POD':True, 'sampleType':'Arnoldi'} #, 'robustTol':1e-14} #boolCon = lambda x : np.abs(np.imag(x)) < 1e-1 * np.abs(np.real(x) # - np.real(z0)) #cleanupParameters = {'boolCondition':boolCon, 'residueCheck':True} solver = HSBPE(kappa = 12 ** .5, theta = np.pi / 3, n = 25, verbosity = verb) solver.omega = np.real(k0) approxP = Pade(solver, mu0 = k0, approxParameters = params, verbosity = verb)#, # equilibration = True, cleanupParameters = cleanupParameters) approxR = RB(solver, mu0 = k0, approxParameters = params, verbosity = verb) rP, rE = [None] * len(Nvals), [None] * len(Nvals) verbose = 1 for j, N in enumerate(Nvals): if verbose > 0: print('N = E = {}'.format(N)) approxP.approxParameters = {'N':N, 'E':N} approxR.approxParameters = {'R':N, 'E':N} if verbose > 1: print(approxP.approxParameters) print(approxR.approxParameters) rP[j] = approxP.getPoles() rE[j] = approxR.getPoles() if verbose > 2: print(rP) print(rE) from matplotlib import pyplot as plt plotRows = int(np.ceil(len(Nvals) / 3)) fig, axes = plt.subplots(plotRows, 3, figsize = (15, 3.5 * plotRows)) for j, N in enumerate(Nvals): i1, i2 = int(np.floor(j / 3)), j % 3 axes[i1, i2].set_title('N = E = {}'.format(N)) axes[i1, i2].plot(np.real(rP[j]), np.imag(rP[j]), 'Xb', label="Pade'", markersize = 8) axes[i1, i2].plot(np.real(rE[j]), np.imag(rE[j]), 'Pr', label="RB", markersize = 8) axes[i1, i2].axhline(linewidth=1, color='k') xmin, xmax = axes[i1, i2].get_xlim() height = (xmax - xmin) / 2. res = np.power(squareResonances(xmin**2., xmax**2., False), .5) axes[i1, i2].plot(res, np.zeros_like(res), 'ok', markersize = 4) axes[i1, i2].plot(np.real(k0), np.imag(k0), 'om', markersize = 5) axes[i1, i2].plot(np.real(k0) * np.ones(2), 1.5 * height * np.arange(-1, 3, 2), '--m') axes[i1, i2].grid() axes[i1, i2].set_xlim(xmin, xmax) axes[i1, i2].set_ylim(- height, height) p = axes[i1, i2].legend() plt.tight_layout() for j in range((len(Nvals) - 1) % 3 + 1, 3): axes[plotRows - 1, j].axis('off') diff --git a/examples/pod/LagrangePoles.py b/examples/pod/PolesDistributed.py similarity index 70% rename from examples/pod/LagrangePoles.py rename to examples/pod/PolesDistributed.py index 799c3cc..77779ab 100644 --- a/examples/pod/LagrangePoles.py +++ b/examples/pod/PolesDistributed.py @@ -1,47 +1,47 @@ from matplotlib import pyplot as plt import numpy as np from rrompy.hfengines.linear_problem import \ HelmholtzSquareBubbleProblemEngine as HSBPE -from rrompy.reduction_methods.lagrange import ApproximantLagrangePade as Pade +from rrompy.reduction_methods.distributed import RationalInterpolant as RI from rrompy.utilities.parameter_sampling import QuadratureSampler as QS from rrompy.utilities.base import squareResonances verb = 0 ks = [1, 46 ** .5] solver = HSBPE(kappa = 12 ** .5, theta = np.pi / 3, n = 20, verbosity = verb) k0 = np.mean(np.power(ks, 2.)) ** .5 k0 = 3.46104724 solver.omega = np.real(k0) rescaling = lambda x: np.power(x, 2.) rescalingInv = lambda x: np.power(x, .5) nsets = 15 -paramsPade = {'S':2, 'POD':True, 'basis':"LEGENDRE"} -approx = Pade(solver, mu0 = k0, approxParameters = paramsPade, - verbosity = verb) -approx.sampler = QS(ks, "UNIFORM", rescaling, rescalingInv) +paramsPade = {'S':2, 'POD':True, 'basis':"LEGENDRE", + 'sampler':QS(ks, "UNIFORM", rescaling, rescalingInv)} +approx = RI(solver, mu0 = k0, approxParameters = paramsPade, + verbosity = verb) -poles = [None] * (nsets - 1) -polesexact = np.unique(np.power(squareResonances(ks[0]**2., ks[1]**2., False), - .5)) -for i in range(1, nsets): +poles = [None] * nsets +polesexact = np.unique(np.power( + squareResonances(ks[0]**2., ks[1]**2., False), .5)) +for i in range(1, nsets + 1): print("N = {}".format(4 * i)) approx.approxParameters = {'N': 4 * i, 'M': 4 * i, 'S': 4 * i + 1} approx.setupApprox() poles[i - 1] = approx.getPoles() -for i in range(1, nsets): +for i in range(1, nsets + 1): plt.figure() plt.plot(np.real(poles[i - 1]), np.imag(poles[i - 1]), 'kx') plt.plot(polesexact, np.zeros_like(polesexact), 'm.') plt.plot(k0, 0, 'r*') plt.xlim(ks) plt.ylim((ks[0] - ks[1]) / 2., (ks[1] - ks[0]) / 2.) plt.title("N = {}, Neff = {}".format(4 * i, len(poles[i - 1]))) plt.grid() plt.show() plt.close() diff --git a/examples/pod/RBTaylor.py b/examples/pod/RBCentered.py similarity index 95% rename from examples/pod/RBTaylor.py rename to examples/pod/RBCentered.py index 0eb0019..f65f31b 100644 --- a/examples/pod/RBTaylor.py +++ b/examples/pod/RBCentered.py @@ -1,102 +1,102 @@ import numpy as np from rrompy.hfengines.linear_problem import \ HelmholtzSquareBubbleProblemEngine as HSBPE from rrompy.hfengines.linear_problem import \ HelmholtzSquareTransmissionProblemEngine as HSTPE from rrompy.hfengines.linear_problem import \ HelmholtzBoxScatteringProblemEngine as HBSPE -from rrompy.reduction_methods.taylor import ApproximantTaylorRB as RB +from rrompy.reduction_methods.centered import RBCentered as RB testNo = -1 verb = 100 homog = True #homog = False -loadName = "RBTaylorModel.pkl" +loadName = "RBCenteredModel.pkl" if testNo in [1, -1]: if testNo > 0: params = {'E':4, 'R':4, 'sampleType':'Arnoldi', 'POD':True} k0 = 12 ** .5 ktar = 10.5 ** .5 solver = HSBPE(kappa = 12 ** .5, theta = np.pi / 3, n = 40, verbosity = verb) if testNo > 0: approx = RB(solver, mu0 = k0, approxParameters = params, verbosity = verb) approx.setupApprox() # approx.plotSamples() else: approx = RB(solver, mu0 = 0, verbosity = verb) approx.loadTrainedModel(loadName) approx.plotApprox(ktar, name = 'u_RB') approx.plotHF(ktar, name = 'u_HF') approx.plotErr(ktar, name = 'err') approx.plotRes(ktar, name = 'res') appErr, solNorm = approx.normErr(ktar), approx.normHF(ktar) resNorm, RHSNorm = approx.normRes(ktar), approx.normRHS(ktar) print(('SolNorm:\t{}\nErr:\t{}\nErrRel:\t{}').format(solNorm, appErr, np.divide(appErr, solNorm))) print(('RHSNorm:\t{}\nRes:\t{}\nResRel:\t{}').format(RHSNorm, resNorm, np.divide(resNorm, RHSNorm))) if testNo > 0: - approx.storeTrainedModel("RBTaylorModel", forceNewFile = False) + approx.storeTrainedModel("RBCenteredModel", forceNewFile = False) print(approx.trainedModel.data.__dict__) ############ elif testNo == 2: params = {'E':7, 'R':7, 'sampleType':'Arnoldi', 'POD':True} k0 = 16**.5 ktar = 15**.5 solver = HSTPE(nT = 2, nB = 1, theta = np.pi * 45/180, kappa = 3., n = 50, verbosity = verb) solver.omega = np.real(k0) approx = RB(solver, mu0 = k0, approxParameters = params, verbosity = verb, homogeneized = homog) approx.setupApprox() # approx.plotSamples() approx.plotApprox(ktar, name = 'u_RB') approx.plotHF(ktar, name = 'u_HF') approx.plotErr(ktar, name = 'err') approx.plotRes(ktar, name = 'res') appErr, solNorm = approx.normErr(ktar), approx.normHF(ktar) resNorm, RHSNorm = approx.normRes(ktar), approx.normRHS(ktar) print(('SolNorm:\t{}\nErr:\t{}\nErrRel:\t{}').format(solNorm, appErr, np.divide(appErr, solNorm))) print(('RHSNorm:\t{}\nRes:\t{}\nResRel:\t{}').format(RHSNorm, resNorm, np.divide(resNorm, RHSNorm))) ############ elif testNo in [3, 4]: if testNo == 3: params = {'E':8, 'sampleType':'Krylov', 'POD':True} else: params = {'E':8, 'sampleType':'Arnoldi', 'POD':True} k0 = 3 ktar = 4.25+.5j solver = HBSPE(R = 5, kappa = 3, theta = - np.pi * 75 / 180, n = 30, verbosity = verb) solver.omega = np.real(k0) approx = RB(solver, mu0 = k0, approxParameters = params, verbosity = verb, homogeneized = homog) approx.setupApprox() # approx.plotSamples() approx.plotApprox(ktar, name = 'u_RB') approx.plotHF(ktar, name = 'u_HF') approx.plotErr(ktar, name = 'err') approx.plotRes(ktar, name = 'res') appErr, solNorm = approx.normErr(ktar), approx.normHF(ktar) resNorm, RHSNorm = approx.normRes(ktar), approx.normRHS(ktar) print(('SolNorm:\t{}\nErr:\t{}\nErrRel:\t{}').format(solNorm, appErr, np.divide(appErr, solNorm))) print(('RHSNorm:\t{}\nRes:\t{}\nResRel:\t{}').format(RHSNorm, resNorm, np.divide(resNorm, RHSNorm))) diff --git a/examples/pod/RBLagrange.py b/examples/pod/RBDistributed.py similarity index 89% rename from examples/pod/RBLagrange.py rename to examples/pod/RBDistributed.py index b684858..060440b 100644 --- a/examples/pod/RBLagrange.py +++ b/examples/pod/RBDistributed.py @@ -1,111 +1,110 @@ import numpy as np from rrompy.hfengines.linear_problem import \ HelmholtzSquareBubbleProblemEngine as HSBPE from rrompy.hfengines.linear_problem import \ HelmholtzSquareTransmissionProblemEngine as HSTPE from rrompy.hfengines.linear_problem import \ HelmholtzBoxScatteringProblemEngine as HBSPE -from rrompy.reduction_methods.lagrange import ApproximantLagrangeRB as RB +from rrompy.reduction_methods.distributed import RBDistributed as RB from rrompy.utilities.parameter_sampling import QuadratureSampler as QS testNo = -1 verb = 100 homog = True #homog = False -loadName = "RBLagrangeModel.pkl" +loadName = "RBDistributedModel.pkl" if testNo in [1, -1]: if testNo > 0: k0s = np.power([10 + 0.j, 14 + 0.j], .5) k0 = np.mean(np.power(k0s, 2.)) ** .5 rescaling = lambda x: np.power(x, 2.) rescalingInv = lambda x: np.power(x, .5) - params = {'S':5, 'R':4, 'POD':True} + params = {'S':5, 'R':4, 'POD':True, + 'sampler':QS(k0s, "CHEBYSHEV", rescaling, rescalingInv)} ktar = (11 + .5j) ** .5 solver = HSBPE(kappa = 12 ** .5, theta = np.pi / 3, n = 40, verbosity = verb) if testNo > 0: approx = RB(solver, mu0 = k0, approxParameters = params, verbosity = verb) - approx.sampler = QS(k0s, "CHEBYSHEV", rescaling, rescalingInv) approx.setupApprox() # approx.plotSamples() else: approx = RB(solver, mu0 = 0, verbosity = verb) approx.loadTrainedModel(loadName) approx.plotApprox(ktar, name = 'u_RB') approx.plotHF(ktar, name = 'u_HF') approx.plotErr(ktar, name = 'err') approx.plotRes(ktar, name = 'res') appErr, solNorm = approx.normErr(ktar), approx.normHF(ktar) resNorm, RHSNorm = approx.normRes(ktar), approx.normRHS(ktar) print(('SolNorm:\t{}\nErr:\t{}\nErrRel:\t{}').format(solNorm, appErr, np.divide(appErr, solNorm))) print(('RHSNorm:\t{}\nRes:\t{}\nResRel:\t{}').format(RHSNorm, resNorm, np.divide(resNorm, RHSNorm))) if testNo > 0: - approx.storeTrainedModel("RBLagrangeModel", forceNewFile = False) + approx.storeTrainedModel("RBDistributedModel", forceNewFile = False) print(approx.trainedModel.data.__dict__) ############ elif testNo == 2: k0s = [3.85 + 0.j, 4.15 + 0.j] k0 = np.mean(k0s) ktar = 4 + .15j rescaling = lambda x: np.power(x, 2.) rescalingInv = lambda x: np.power(x, .5) - params = {'S':10, 'R':9, 'POD':True} + params = {'S':10, 'R':9, 'POD':True, + 'sampler':QS(k0s, "CHEBYSHEV", rescaling, rescalingInv)} solver = HSTPE(nT = 2, nB = 1, theta = np.pi * 45/180, kappa = 4., n = 50, verbosity = verb) solver.omega = np.real(k0) approx = RB(solver, mu0 = k0, approxParameters = params, verbosity = verb, homogeneized = homog) - approx.sampler = QS(k0s, "CHEBYSHEV", rescaling, rescalingInv) approx.setupApprox() # approx.plotSamples() approx.plotApprox(ktar, name = 'u_RB') approx.plotHF(ktar, name = 'u_HF') approx.plotErr(ktar, name = 'err') approx.plotRes(ktar, name = 'res') appErr, solNorm = approx.normErr(ktar), approx.normHF(ktar) resNorm, RHSNorm = approx.normRes(ktar), approx.normRHS(ktar) print(('SolNorm:\t{}\nErr:\t{}\nErrRel:\t{}').format(solNorm, appErr, np.divide(appErr, solNorm))) print(('RHSNorm:\t{}\nRes:\t{}\nResRel:\t{}').format(RHSNorm, resNorm, np.divide(resNorm, RHSNorm))) ############ elif testNo == 3: k0s = [2, 5] k0 = np.mean(k0s) ktar = 4.5 - 0.j - params = {'S':15, 'R':10, 'POD':True} + params = {'S':15, 'R':10, 'POD':True, 'sampler':QS(k0s, "CHEBYSHEV")} solver = HBSPE(R = 7, kappa = 3, theta = - np.pi * 75 / 180, n = 40, verbosity = verb) solver.omega = np.real(k0) approx = RB(solver, mu0 = k0, approxParameters = params, verbosity = verb, homogeneized = homog) - approx.sampler = QS(k0s, "CHEBYSHEV") approx.setupApprox() # approx.plotSamples() approx.plotApprox(ktar, name = 'u_RB') approx.plotHF(ktar, name = 'u_HF') approx.plotErr(ktar, name = 'err') approx.plotRes(ktar, name = 'res') appErr, solNorm = approx.normErr(ktar), approx.normHF(ktar) resNorm, RHSNorm = approx.normRes(ktar), approx.normRHS(ktar) print(('SolNorm:\t{}\nErr:\t{}\nErrRel:\t{}').format(solNorm, appErr, np.divide(appErr, solNorm))) print(('RHSNorm:\t{}\nRes:\t{}\nResRel:\t{}').format(RHSNorm, resNorm, np.divide(resNorm, RHSNorm))) diff --git a/examples/pod/PadeHermite.py b/examples/pod/RationalHermiteInterpolant.py similarity index 84% rename from examples/pod/PadeHermite.py rename to examples/pod/RationalHermiteInterpolant.py index f4dc21d..128d165 100644 --- a/examples/pod/PadeHermite.py +++ b/examples/pod/RationalHermiteInterpolant.py @@ -1,137 +1,134 @@ import numpy as np from rrompy.hfengines.linear_problem import \ HelmholtzSquareBubbleProblemEngine as HSBPE from rrompy.hfengines.linear_problem import \ HelmholtzSquareTransmissionProblemEngine as HSTPE from rrompy.hfengines.linear_problem import \ HelmholtzBoxScatteringProblemEngine as HBSPE -from rrompy.reduction_methods.lagrange import ApproximantLagrangePade as Pade +from rrompy.reduction_methods.distributed import RationalInterpolant as RI from rrompy.utilities.parameter_sampling import (QuadratureSampler as QS, ManualSampler as MS) testNo = 1 verb = 100 polyBasis = "CHEBYSHEV" polyBasis = "LEGENDRE" polyBasis = "MONOMIAL" rep = "REPEAT" #rep = "TILE" homog = True #homog = False -loadName = "PadeLagrangeModel.pkl" if testNo == 1: - if testNo > 0: - k0s = np.power([10 + 0.j, 14 + 0.j], .5) - k0 = np.mean(np.power(k0s, 2.)) ** .5 - rescaling = lambda x: np.power(x, 2.) - rescalingInv = lambda x: np.power(x, .5) - params = {'N':7, 'M':6, 'S':8, 'POD':True, 'polybasis':polyBasis} - ktar = (11 + .5j) ** .5 - - solver = HSBPE(kappa = 12 ** .5, theta = np.pi / 3, n = 40, - verbosity = verb) - solver.omega = np.real(k0) - approx = Pade(solver, mu0 = k0, approxParameters = params, - verbosity = verb) + k0s = np.power([10 + 0.j, 14 + 0.j], .5) + k0 = np.mean(np.power(k0s, 2.)) ** .5 + rescaling = lambda x: np.power(x, 2.) + rescalingInv = lambda x: np.power(x, .5) samplerBase = QS(k0s, "CHEBYSHEV", rescaling, rescalingInv) if rep == "REPEAT": points = np.repeat(samplerBase.generatePoints(2)[0], int(np.ceil(params["S"] / 2))) else: # if rep == "TILE": points = np.tile(samplerBase.generatePoints(2)[0], int(np.ceil(params["S"] / 2))) - approx.sampler = MS(k0s, points = points) + params = {'N':7, 'M':6, 'S':8, 'POD':True, 'polybasis':polyBasis, + 'sampler':MS(k0s, points = points)} + ktar = (11 + .5j) ** .5 + + solver = HSBPE(kappa = 12 ** .5, theta = np.pi / 3, n = 40, + verbosity = verb) + solver.omega = np.real(k0) + approx = RI(solver, mu0 = k0, approxParameters = params, + verbosity = verb) approx.setupApprox() approx.plotApprox(ktar, name = 'u_Pade''') approx.plotHF(ktar, name = 'u_HF') approx.plotErr(ktar, name = 'err') approx.plotRes(ktar, name = 'res') - + appErr, solNorm = approx.normErr(ktar), approx.normHF(ktar) resNorm, RHSNorm = approx.normRes(ktar), approx.normRHS(ktar) print(('SolNorm:\t{}\nErr:\t{}\nErrRel:\t{}').format(solNorm, appErr, np.divide(appErr, solNorm))) print(('RHSNorm:\t{}\nRes:\t{}\nResRel:\t{}').format(RHSNorm, resNorm, np.divide(resNorm, RHSNorm))) print('\nPoles Pade'':') print(approx.getPoles()) ############ elif testNo == 2: k0s = [3.85 + 0.j, 4.15 + 0.j] k0 = np.mean(k0s) ktar = 4 + 0.j - rescaling = lambda x: np.power(x, 2.) rescalingInv = lambda x: np.power(x, .5) - params = {'N':8, 'M':9, 'S':10, 'POD':True, 'polybasis':polyBasis}, - - solver = HSTPE(nT = 2, nB = 1, theta = np.pi * 45/180, kappa = 4., n = 50, - verbosity = verb) - solver.omega = np.real(k0) - approx = Pade(solver, mu0 = k0, approxParameters = params, - verbosity = verb, homogeneized = homog) samplerBase = QS(k0s, "CHEBYSHEV", rescaling, rescalingInv) if rep == "REPEAT": points = np.repeat(samplerBase.generatePoints(5)[0], int(np.ceil(params["S"] / 5))) else: # if rep == "TILE": points = np.tile(samplerBase.generatePoints(5)[0], int(np.ceil(params["S"] / 5))) - approx.sampler = MS(k0s, points = points) + params = {'N':8, 'M':9, 'S':10, 'POD':True, 'polybasis':polyBasis, + 'sampler':MS(k0s, points = points)} + + solver = HSTPE(nT = 2, nB = 1, theta = np.pi * 45/180, kappa = 4., n = 50, + verbosity = verb) + solver.omega = np.real(k0) + approx = RI(solver, mu0 = k0, approxParameters = params, + verbosity = verb, homogeneized = homog) approx.setupApprox() # approx.plotSamples() approx.plotApprox(ktar, name = 'u_Pade''') approx.plotHF(ktar, name = 'u_HF') approx.plotErr(ktar, name = 'err') approx.plotRes(ktar, name = 'res') appErr, solNorm = approx.normErr(ktar), approx.normHF(ktar) resNorm, RHSNorm = approx.normRes(ktar), approx.normRHS(ktar) print(('SolNorm:\t{}\nErr:\t{}\nErrRel:\t{}').format(solNorm, appErr, np.divide(appErr, solNorm))) print(('RHSNorm:\t{}\nRes:\t{}\nResRel:\t{}').format(RHSNorm, resNorm, np.divide(resNorm, RHSNorm))) print('\nPoles Pade'':') print(approx.getPoles()) ############ elif testNo == 3: k0s = [2, 5] k0 = np.mean(k0s) ktar = 4.5 - .1j - params = {'N':14, 'M':14, 'S':15, 'POD':True, 'polybasis':polyBasis} - - solver = HBSPE(R = 7, kappa = 3, theta = - np.pi * 75 / 180, n = 40, - verbosity = verb) - solver.omega = np.real(k0) - approx = Pade(solver, mu0 = k0, approxParameters = params, - verbosity = verb, homogeneized = homog) samplerBase = QS(k0s, "CHEBYSHEV") if rep == "REPEAT": points = np.repeat(samplerBase.generatePoints(5)[0], int(np.ceil(params["S"] / 5))) else: # if rep == "TILE": points = np.tile(samplerBase.generatePoints(5)[0], int(np.ceil(params["S"] / 5))) - approx.sampler = MS(k0s, points = points) + params = {'N':14, 'M':14, 'S':15, 'POD':True, 'polybasis':polyBasis, + 'sampler':MS(k0s, points = points)} + + solver = HBSPE(R = 7, kappa = 3, theta = - np.pi * 75 / 180, n = 40, + verbosity = verb) + solver.omega = np.real(k0) + approx = RI(solver, mu0 = k0, approxParameters = params, + verbosity = verb, homogeneized = homog) approx.setupApprox() # approx.plotSamples() approx.plotApprox(ktar, name = 'u_Pade''') approx.plotHF(ktar, name = 'u_HF') approx.plotErr(ktar, name = 'err') approx.plotRes(ktar, name = 'res') appErr, solNorm = approx.normErr(ktar), approx.normHF(ktar) resNorm, RHSNorm = approx.normRes(ktar), approx.normRHS(ktar) print(('SolNorm:\t{}\nErr:\t{}\nErrRel:\t{}').format(solNorm, appErr, np.divide(appErr, solNorm))) print(('RHSNorm:\t{}\nRes:\t{}\nResRel:\t{}').format(RHSNorm, resNorm, np.divide(resNorm, RHSNorm))) print('\nPoles Pade'':') print(approx.getPoles()) - + diff --git a/examples/pod/PadeLagrange.py b/examples/pod/RationalInterpolant.py similarity index 83% rename from examples/pod/PadeLagrange.py rename to examples/pod/RationalInterpolant.py index 38b7190..b87422e 100644 --- a/examples/pod/PadeLagrange.py +++ b/examples/pod/RationalInterpolant.py @@ -1,122 +1,122 @@ import numpy as np from rrompy.hfengines.linear_problem import \ HelmholtzSquareBubbleProblemEngine as HSBPE from rrompy.hfengines.linear_problem import \ HelmholtzSquareTransmissionProblemEngine as HSTPE from rrompy.hfengines.linear_problem import \ HelmholtzBoxScatteringProblemEngine as HBSPE -from rrompy.reduction_methods.lagrange import ApproximantLagrangePade as Pade +from rrompy.reduction_methods.distributed import RationalInterpolant as RI from rrompy.utilities.parameter_sampling import QuadratureSampler as QS testNo = 1 verb = 100 polyBasis = "CHEBYSHEV" polyBasis = "LEGENDRE" #polyBasis = "MONOMIAL" homog = True #homog = False -loadName = "PadeLagrangeModel.pkl" +loadName = "RationalInterpolantModel.pkl" if testNo in [1, -1]: if testNo > 0: k0s = np.power([10 + 0.j, 14 + 0.j], .5) k0 = np.mean(np.power(k0s, 2.)) ** .5 rescaling = lambda x: np.power(x, 2.) rescalingInv = lambda x: np.power(x, .5) - params = {'N':4, 'M':3, 'S':5, 'POD':True, 'polybasis':polyBasis} + params = {'N':4, 'M':3, 'S':5, 'POD':True, 'polybasis':polyBasis, + 'sampler':QS(k0s, "CHEBYSHEV", rescaling, rescalingInv)} ktar = (11 + .5j) ** .5 solver = HSBPE(kappa = 12 ** .5, theta = np.pi / 3, n = 40, verbosity = verb) if testNo > 0: solver.omega = np.real(k0) - approx = Pade(solver, mu0 = k0, approxParameters = params, - verbosity = verb) - approx.sampler = QS(k0s, "CHEBYSHEV", rescaling, rescalingInv) + approx = RI(solver, mu0 = k0, approxParameters = params, + verbosity = verb) approx.setupApprox() # approx.plotSamples() else: - approx = Pade(solver, mu0 = 0, verbosity = verb) + approx = RI(solver, mu0 = 0, verbosity = verb) approx.loadTrainedModel(loadName) approx.plotApprox(ktar, name = 'u_Pade''') approx.plotHF(ktar, name = 'u_HF') approx.plotErr(ktar, name = 'err') approx.plotRes(ktar, name = 'res') appErr, solNorm = approx.normErr(ktar), approx.normHF(ktar) resNorm, RHSNorm = approx.normRes(ktar), approx.normRHS(ktar) print(('SolNorm:\t{}\nErr:\t{}\nErrRel:\t{}').format(solNorm, appErr, np.divide(appErr, solNorm))) print(('RHSNorm:\t{}\nRes:\t{}\nResRel:\t{}').format(RHSNorm, resNorm, np.divide(resNorm, RHSNorm))) print('\nPoles Pade'':') print(approx.getPoles()) if testNo > 0: - approx.storeTrainedModel("PadeLagrangeModel", forceNewFile = False) + approx.storeTrainedModel("RationalInterpolantModel", forceNewFile = False) print(approx.trainedModel.data.__dict__) ############ elif testNo == 2: k0s = [3.85 + 0.j, 4.15 + 0.j] k0 = np.mean(k0s) ktar = 4 + 0.j rescaling = lambda x: np.power(x, 2.) rescalingInv = lambda x: np.power(x, .5) - params = {'N':8, 'M':9, 'S':10, 'POD':True, 'polybasis':polyBasis}, + params = {'N':8, 'M':9, 'S':10, 'POD':True, 'polybasis':polyBasis, + 'sampler':QS(k0s, "CHEBYSHEV", rescaling, rescalingInv)} solver = HSTPE(nT = 2, nB = 1, theta = np.pi * 45/180, kappa = 4., n = 50, verbosity = verb) solver.omega = np.real(k0) - approx = Pade(solver, mu0 = k0, approxParameters = params, - verbosity = verb, homogeneized = homog) - approx.sampler = QS(k0s, "CHEBYSHEV", rescaling, rescalingInv) + approx = RI(solver, mu0 = k0, approxParameters = params, + verbosity = verb, homogeneized = homog) approx.setupApprox() # approx.plotSamples() approx.plotApprox(ktar, name = 'u_Pade''') approx.plotHF(ktar, name = 'u_HF') approx.plotErr(ktar, name = 'err') approx.plotRes(ktar, name = 'res') appErr, solNorm = approx.normErr(ktar), approx.normHF(ktar) resNorm, RHSNorm = approx.normRes(ktar), approx.normRHS(ktar) print(('SolNorm:\t{}\nErr:\t{}\nErrRel:\t{}').format(solNorm, appErr, np.divide(appErr, solNorm))) print(('RHSNorm:\t{}\nRes:\t{}\nResRel:\t{}').format(RHSNorm, resNorm, np.divide(resNorm, RHSNorm))) print('\nPoles Pade'':') print(approx.getPoles()) ############ elif testNo == 3: k0s = [2, 5] k0 = np.mean(k0s) ktar = 4.5 - .1j - params = {'N':10, 'M':10, 'S':11, 'POD':True, 'polybasis':polyBasis} + params = {'N':10, 'M':10, 'S':11, 'POD':True, 'polybasis':polyBasis, + 'sampler':QS(k0s, "CHEBYSHEV")} solver = HBSPE(R = 7, kappa = 3, theta = - np.pi * 75 / 180, n = 40, verbosity = verb) solver.omega = np.real(k0) - approx = Pade(solver, mu0 = k0, approxParameters = params, - verbosity = verb, homogeneized = homog) - approx.sampler = QS(k0s, "CHEBYSHEV") + approx = RI(solver, mu0 = k0, approxParameters = params, + verbosity = verb, homogeneized = homog) approx.setupApprox() # approx.plotSamples() approx.plotApprox(ktar, name = 'u_Pade''') approx.plotHF(ktar, name = 'u_HF') approx.plotErr(ktar, name = 'err') approx.plotRes(ktar, name = 'res') appErr, solNorm = approx.normErr(ktar), approx.normHF(ktar) resNorm, RHSNorm = approx.normRes(ktar), approx.normRHS(ktar) print(('SolNorm:\t{}\nErr:\t{}\nErrRel:\t{}').format(solNorm, appErr, np.divide(appErr, solNorm))) print(('RHSNorm:\t{}\nRes:\t{}\nResRel:\t{}').format(RHSNorm, resNorm, np.divide(resNorm, RHSNorm))) print('\nPoles Pade'':') print(approx.getPoles()) diff --git a/examples/pod/PadeTaylor.py b/examples/pod/RationalPade.py similarity index 95% rename from examples/pod/PadeTaylor.py rename to examples/pod/RationalPade.py index 1e23e98..14c5151 100644 --- a/examples/pod/PadeTaylor.py +++ b/examples/pod/RationalPade.py @@ -1,108 +1,108 @@ import numpy as np from rrompy.hfengines.linear_problem import \ HelmholtzSquareBubbleProblemEngine as HSBPE from rrompy.hfengines.linear_problem import \ HelmholtzSquareTransmissionProblemEngine as HSTPE from rrompy.hfengines.linear_problem import \ HelmholtzBoxScatteringProblemEngine as HBSPE -from rrompy.reduction_methods.taylor import ApproximantTaylorPade as Pade +from rrompy.reduction_methods.centered import RationalPade as Pade testNo = -1 verb = 100 homog = True #homog = False -loadName = "PadeTaylorModel.pkl" +loadName = "RationalPadeModel.pkl" if testNo in [1, -1]: if testNo > 0: params = {'N':4, 'M':3, 'E':4, 'sampleType':'Arnoldi', 'POD':True} k0 = 12 ** .5 ktar = 10.5 ** .5 solver = HSBPE(kappa = 12 ** .5, theta = np.pi / 3, n = 40, verbosity = verb) if testNo > 0: approx = Pade(solver, mu0 = k0, approxParameters = params, verbosity = verb) approx.setupApprox() # approx.plotSamples() else: approx = Pade(solver, mu0 = 0, verbosity = verb) approx.loadTrainedModel(loadName) approx.plotApprox(ktar, name = 'u_Pade''') approx.plotHF(ktar, name = 'u_HF') approx.plotErr(ktar, name = 'err') approx.plotRes(ktar, name = 'res') appErr, solNorm = approx.normErr(ktar), approx.normHF(ktar) resNorm, RHSNorm = approx.normRes(ktar), approx.normRHS(ktar) print(('SolNorm:\t{}\nErr:\t{}\nErrRel:\t{}').format(solNorm, appErr, np.divide(appErr, solNorm))) print(('RHSNorm:\t{}\nRes:\t{}\nResRel:\t{}').format(RHSNorm, resNorm, np.divide(resNorm, RHSNorm))) print('\nPoles Pade'':') print(approx.getPoles()) if testNo > 0: - approx.storeTrainedModel("PadeTaylorModel", forceNewFile = False) + approx.storeTrainedModel("RationalPadeModel", forceNewFile = False) print(approx.trainedModel.data.__dict__) ############ elif testNo == 2: params = {'N':6, 'M':7, 'E':7, 'sampleType':'Arnoldi', 'POD':True} k0 = 16 ** .5 ktar = 15 ** .5 solver = HSTPE(nT = 2, nB = 1, theta = np.pi * 45/180, kappa = 4., n = 50, verbosity = verb) solver.omega = np.real(k0) approx = Pade(solver, mu0 = k0, approxParameters = params, verbosity = verb, homogeneized = homog) approx.setupApprox() # approx.plotSamples() approx.plotApprox(ktar, name = 'u_Pade''') approx.plotHF(ktar, name = 'u_HF') approx.plotErr(ktar, name = 'err') approx.plotRes(ktar, name = 'res') appErr, solNorm = approx.normErr(ktar), approx.normHF(ktar) resNorm, RHSNorm = approx.normRes(ktar), approx.normRHS(ktar) print(('SolNorm:\t{}\nErr:\t{}\nErrRel:\t{}').format(solNorm, appErr, np.divide(appErr, solNorm))) print(('RHSNorm:\t{}\nRes:\t{}\nResRel:\t{}').format(RHSNorm, resNorm, np.divide(resNorm, RHSNorm))) print('\nPoles Pade'':') print(approx.getPoles()) ############ elif testNo in [3, 4]: if testNo == 3: params = {'N':7, 'M':8, 'E':8, 'sampleType':'Krylov', 'POD':True} else: params = {'N':7, 'M':8, 'E':8, 'sampleType':'Arnoldi', 'POD':True} k0 = 3 ktar = 4.+0.j solver = HBSPE(R = 5, kappa = 3, theta = - np.pi * 75 / 180, n = 30, verbosity = verb) solver.omega = np.real(k0) approx = Pade(solver, mu0 = k0, approxParameters = params, verbosity = verb, homogeneized = homog) approx.setupApprox() approx.plotSamples() approx.plotApprox(ktar, name = 'u_Pade''') approx.plotHF(ktar, name = 'u_HF') approx.plotErr(ktar, name = 'err') approx.plotRes(ktar, name = 'res') appErr, solNorm = approx.normErr(ktar), approx.normHF(ktar) resNorm, RHSNorm = approx.normRes(ktar), approx.normRHS(ktar) print(('SolNorm:\t{}\nErr:\t{}\nErrRel:\t{}').format(solNorm, appErr, np.divide(appErr, solNorm))) print(('RHSNorm:\t{}\nRes:\t{}\nResRel:\t{}').format(RHSNorm, resNorm, np.divide(resNorm, RHSNorm))) print('\nPoles Pade'':') print(approx.getPoles()) diff --git a/examples/pod/TaylorSweep.py b/examples/pod/TaylorSweep.py deleted file mode 100644 index 32878d3..0000000 --- a/examples/pod/TaylorSweep.py +++ /dev/null @@ -1,79 +0,0 @@ -import numpy as np -from rrompy.hfengines.linear_problem import \ - HelmholtzSquareBubbleProblemEngine as HSBPE -from rrompy.hfengines.linear_problem import \ - HelmholtzBoxScatteringProblemEngine as HBSPE -from rrompy.reduction_methods.taylor import ApproximantTaylorPade as Pade -from rrompy.reduction_methods.taylor import ApproximantTaylorRB as RB -from rrompy.utilities.parameter_sweeper import ParameterSweeper as Sweeper - -testNo = 1 -verb = 0 -homog = True -homog = False - -k0 = (12 + 0.j) ** .5 -npoints = 100 - -shift = 5 -nsets = 2 -stride = 2 -Emax = stride * (nsets - 1) + shift + 1 - -if testNo == 1: - solver = HSBPE(kappa = 12 ** .5, theta = np.pi / 3, n = 15, - verbosity = verb) - solver.omega = np.real(k0) - params = {'Emax':Emax, 'sampleType':'ARNOLDI', 'POD':True} - ktars = np.linspace(7**.5, 16**.5, npoints) - filenamebase = '../data/output/HelmholtzBubbleTSweep' - homog = False -elif testNo == 2: - solver = HBSPE(R = 5, kappa = 3, theta = - np.pi * 75 / 180, n = 10, - verbosity = verb) - solver.omega = np.real(k0) - params = {'Emax':Emax, 'sampleType':'Arnoldi', 'POD':True} - ktars = np.linspace(7**.5, 17**.5, npoints) - homogMSG = "" - if not homog: homogMSG = "Non" - filenamebase = '../data/output/HelmholtzBoxTSweep' + homogMSG + "Homog" - -paramsSetsPade = [None] * nsets -paramsSetsRB = [None] * nsets -paramsSetsPoly = [None] * nsets -for i in range(nsets): - paramsSetsPade[i] = {'N':stride*i+shift+1, 'M':stride*i+shift+1, - 'E':stride*i+shift+1} - paramsSetsRB[i] = {'E':stride*i+shift+1,'R':stride*i+shift+2} - paramsSetsPoly[i] = {'N':0, 'M':stride*i+shift+1, - 'E':stride*i+shift+1} - -appPade = Pade(solver, mu0 = k0, approxParameters = params, - verbosity = verb, homogeneized = homog) -appRB = RB(solver, mu0 = k0, approxParameters = params, - verbosity = verb, homogeneized = homog) -appPoly = Pade(solver, mu0 = k0, approxParameters = params, - verbosity = verb, homogeneized = homog) - -sweeper = Sweeper(mutars = ktars, mostExpensive = 'Approx') -sweeper.ROMEngine = appPade -sweeper.params = paramsSetsPade -filenamePade = sweeper.sweep(filenamebase + 'Pade.dat') -sweeper.ROMEngine = appRB -sweeper.params = paramsSetsRB -filenameRB = sweeper.sweep(filenamebase + 'RB.dat') -sweeper.ROMEngine = appPoly -sweeper.params = paramsSetsPoly -filenamePoly = sweeper.sweep(filenamebase + 'Poly.dat') - -sweeper.plotCompare([filenamePade, filenameRB, filenamePoly], ['muRe'], - ['normHF', 'normApp'], ['E'], onePlot = True, - save = filenamebase + 'Norm', - saveFormat = "png", labels = ["Pade'", "RB", "Poly"]) -sweeper.plotCompare([filenamePade, filenameRB, filenamePoly], ['muRe'], - ['normResRel'], ['E'], save = filenamebase + 'Res', - saveFormat = "png", labels = ["Pade'", "RB", "Poly"]) -sweeper.plotCompare([filenamePade, filenameRB, filenamePoly], ['muRe'], - ['normErrRel'], ['E'], save = filenamebase + 'Err', - saveFormat = "png", labels = ["Pade'", "RB", "Poly"]) - diff --git a/examples/pod/laplaceGaussianCentered.py b/examples/pod/laplaceGaussianCentered.py new file mode 100644 index 0000000..6098e26 --- /dev/null +++ b/examples/pod/laplaceGaussianCentered.py @@ -0,0 +1,49 @@ +import numpy as np +from rrompy.hfengines.linear_problem import LaplaceDiskGaussian as LDG +from rrompy.reduction_methods.centered import RationalPade as Pade +from rrompy.reduction_methods.centered import RBCentered as RBC +from operator import itemgetter +def subdict(d, ks): + return dict(zip(ks, itemgetter(*ks)(d))) + +testNo = 2 +verb = 0 + +if testNo == 1: + mu = 4. + solver = LDG(n = 40, verbosity = verb) + + uh = solver.solve(mu) + solver.plotmesh() + print(solver.norm(uh)) + solver.plot(uh) + +############ +if testNo == 2: + params = {'N':8, 'M':8, 'E':8, 'sampleType':'Arnoldi', 'POD':True} +# params = {'N':8, 'M':8, 'E':8, 'sampleType':'Krylov', 'POD':True} + mu0 = 0. + solver = LDG(n = 20, degree_threshold = 15, verbosity = verb) + approxP = Pade(solver, mu0 = mu0, approxParameters = params, + verbosity = verb) + paramsRB = subdict(params, ['E', 'sampleType', 'POD']) + approxR = RB(solver, mu0 = mu0, approxParameters = paramsRB, + verbosity = verb) + + approxP.setupApprox() + approxR.setupApprox() +# approxP.plotSamples() + + mutar = 3.25 + approxP.plotHF(mutar, name = 'u_HF') + approxP.plotApprox(mutar, name = 'u_Pade''') + approxR.plotApprox(mutar, name = 'u_RB') + approxP.plotErr(mutar, name = 'err_Pade''') + approxR.plotErr(mutar, name = 'err_RB') + + solNorm = approxP.normHF(mutar) + appPErr = approxP.normErr(mutar) + appRErr = approxR.normErr(mutar) + print(('SolNorm:\t{}\nErrRelP:\t{}\nErrRelR:\t{}').format(solNorm, + appPErr / solNorm, appRErr / solNorm)) + diff --git a/examples/pod/laplaceGaussianTaylor.py b/examples/pod/laplaceGaussianTaylor.py deleted file mode 100644 index e6d4034..0000000 --- a/examples/pod/laplaceGaussianTaylor.py +++ /dev/null @@ -1,100 +0,0 @@ -import numpy as np -from rrompy.hfengines.linear_problem import LaplaceDiskGaussian as LDG -from rrompy.reduction_methods.taylor import ApproximantTaylorPade as Pade -from rrompy.reduction_methods.taylor import ApproximantTaylorRB as RB -from rrompy.utilities.parameter_sweeper import ParameterSweeper as Sweeper -from operator import itemgetter -def subdict(d, ks): - return dict(zip(ks, itemgetter(*ks)(d))) - -testNo = 3 -verb = 0 - -if testNo == 1: - mu = 4. - solver = LDG(n = 40, verbosity = verb) - - uh = solver.solve(mu) - solver.plotmesh() - print(solver.norm(uh)) - solver.plot(uh) - -############ -if testNo == 2: - params = {'N':8, 'M':8, 'E':8, 'sampleType':'Arnoldi', 'POD':True} -# params = {'N':8, 'M':8, 'E':8, 'sampleType':'Krylov', 'POD':True} - mu0 = 0. - solver = LDG(n = 20, degree_threshold = 15, verbosity = verb) - approxP = Pade(solver, mu0 = mu0, approxParameters = params, - verbosity = verb) - paramsRB = subdict(params, ['E', 'sampleType', 'POD']) - approxR = RB(solver, mu0 = mu0, approxParameters = paramsRB, - verbosity = verb) - - approxP.setupApprox() - approxR.setupApprox() -# approxP.plotSamples() - - mutar = 3.25 - approxP.plotHF(mutar, name = 'u_HF') - approxP.plotApprox(mutar, name = 'u_Pade''') - approxR.plotApprox(mutar, name = 'u_RB') - approxP.plotErr(mutar, name = 'err_Pade''') - approxR.plotErr(mutar, name = 'err_RB') - - solNorm = approxP.normHF(mutar) - appPErr = approxP.normErr(mutar) - appRErr = approxR.normErr(mutar) - print(('SolNorm:\t{}\nErrRelP:\t{}\nErrRelR:\t{}').format(solNorm, - appPErr / solNorm, appRErr / solNorm)) - -############ -elif testNo == 3: - mu0 = 0. - mutars = np.linspace(0., 4., 60) - solver = LDG(n = 10, degree_threshold = 15, verbosity = verb) - shift = 2 - nsets = 6 - stride = 2 - Emax = stride * (nsets - 1) + shift - params = {'Emax':Emax, 'sampleType':'Arnoldi', 'POD':True} - params = {'Emax':Emax, 'sampleType':'Krylov', 'POD':False} - paramsSetsPade = [None] * nsets - paramsSetsRB = [None] * nsets - paramsSetsPoly = [None] * nsets - for i in range(nsets): - paramsSetsPade[i] = {'N':stride*i+shift, 'M':stride*i+shift, - 'E':stride*i+shift} - paramsSetsRB[i] = {'E':stride*i+shift} - paramsSetsPoly[i] = {'N':0, 'M':stride*i+shift, - 'E':stride*i+shift} - approxPade = Pade(solver, mu0 = mu0, approxParameters = params, - verbosity = verb) - approxRB = RB(solver, mu0 = mu0, approxParameters = params, - verbosity = verb) - approxPoly = Pade(solver, mu0 = mu0, approxParameters = params, - verbosity = verb) - - filenamebase = '../data/output/LapGTaylor' - - sweeper = Sweeper(mutars = mutars, mostExpensive = 'Approx') - sweeper.ROMEngine = approxPade - sweeper.params = paramsSetsPade - filenamePade = sweeper.sweep(filenamebase + 'Pade.dat') - sweeper.ROMEngine = approxRB - sweeper.params = paramsSetsRB - filenameRB = sweeper.sweep(filenamebase + 'RB.dat') - sweeper.ROMEngine = approxPoly - sweeper.params = paramsSetsPoly - filenamePoly = sweeper.sweep(filenamebase + 'Poly.dat') - -sweeper.plotCompare([filenamePade, filenameRB, filenamePoly], ['muRe'], - ['normHF', 'normApp'], ['E'], onePlot = True, - save = filenamebase + 'Norm', - saveFormat = "png", labels = ["Pade'", "RB", "Poly"]) -sweeper.plotCompare([filenamePade, filenameRB, filenamePoly], ['muRe'], - ['normResRel'], ['E'], save = filenamebase + 'Res', - saveFormat = "png", labels = ["Pade'", "RB", "Poly"]) -sweeper.plotCompare([filenamePade, filenameRB, filenamePoly], ['muRe'], - ['normErrRel'], ['E'], save = filenamebase + 'Err', - saveFormat = "png", labels = ["Pade'", "RB", "Poly"]) diff --git a/examples/pod/matrix_pod.py b/examples/pod/matrix_pod.py index f57482a..a945503 100644 --- a/examples/pod/matrix_pod.py +++ b/examples/pod/matrix_pod.py @@ -1,80 +1,79 @@ import numpy as np import scipy.sparse as sp from matplotlib import pyplot as plt from rrompy.hfengines.base import MatrixEngineBase as MEB -from rrompy.reduction_methods.taylor import ApproximantTaylorPade as PadeT -from rrompy.reduction_methods.lagrange import ApproximantLagrangePade as PadeL -from rrompy.reduction_methods.taylor import ApproximantTaylorRB as RBT -from rrompy.reduction_methods.lagrange import ApproximantLagrangeRB as RBL +from rrompy.reduction_methods.centered import RationalPade as RP +from rrompy.reduction_methods.distributed import RationalInterpolant as RI +from rrompy.reduction_methods.centered import RBCentered as RBC +from rrompy.reduction_methods.distributed import RBDistributed as RBD from rrompy.utilities.parameter_sampling import QuadratureSampler as QS test = 2 -method = "PadeTaylor" -method = "PadeLagrange" -method = "RBTaylor" -method = "RBLagrange" +method = "RationalPade" +method = "RationalInterpolant" +method = "RBCentered" +method = "RBDistributed" verb = 0 N = 100 solver = MEB(verbosity = verb) solver.nAs = 2 if test == 1: solver.As = [sp.spdiags([np.arange(1, 1 + N)], [0], N, N), - sp.eye(N)] elif test == 2: solver.setSolver("SOLVE") fftB = np.fft.fft(np.eye(N)) * N**-.5 solver.As = [fftB.dot(np.multiply(np.arange(1, 1 + N), fftB.conj()).T), - np.eye(N)] np.random.seed(420) solver.nbs = 1 solver.bs = [np.random.randn(N) + 1.j * np.random.randn(N)] mu0 = 10.25 mutar = 12.5 murange = [5.25, 15.25] -if method == "PadeTaylor": +if method == "RationalPade": params = {'N':10, 'M':9, 'E':10, 'sampleType':'Arnoldi', 'POD':True} - approx = PadeT(solver, mu0 = mu0, approxParameters = params, - verbosity = verb) -elif method == "PadeLagrange": - params = {'N':10, 'M':9, 'S':11, 'POD':True, 'polybasis':"CHEBYSHEV"} - approx = PadeL(solver, mu0 = mu0, approxParameters = params, - verbosity = verb) - approx.sampler = QS(murange, "CHEBYSHEV") -elif method == "RBTaylor": + approx = RP(solver, mu0 = mu0, approxParameters = params, + verbosity = verb) +elif method == "RationalInterpolant": + params = {'N':10, 'M':9, 'S':11, 'POD':True, 'polybasis':"CHEBYSHEV", + 'sampler':QS(murange, "CHEBYSHEV")} + approx = RI(solver, mu0 = mu0, approxParameters = params, + verbosity = verb) +elif method == "RBCentered": params = {'R':10, 'E':10, 'sampleType':'Arnoldi', 'POD':True} - approx = RBT(solver, mu0 = mu0, approxParameters = params, + approx = RBC(solver, mu0 = mu0, approxParameters = params, verbosity = verb) -elif method == "RBLagrange": - params = {'R':10, 'S':11, 'POD':True} - approx = RBL(solver, mu0 = mu0, approxParameters = params, +elif method == "RBDistributed": + params = {'R':10, 'S':11, 'POD':True, 'sampler':QS(murange, "CHEBYSHEV")} + approx = RBD(solver, mu0 = mu0, approxParameters = params, verbosity = verb) - approx.sampler = QS(murange, "CHEBYSHEV") approx.setupApprox() approx.plotApprox(mutar, name = 'u_app') approx.plotHF(mutar, name = 'u_HF') approx.plotErr(mutar, name = 'err') approx.plotRes(mutar, name = 'res') appErr, solNorm = approx.normErr(mutar), approx.normHF(mutar) resNorm, RHSNorm = approx.normRes(mutar), approx.normRHS(mutar) print(('SolNorm:\t{}\nErr:\t{}\nErrRel:\t{}').format(solNorm, appErr, np.divide(appErr, solNorm))) print(('RHSNorm:\t{}\nRes:\t{}\nResRel:\t{}').format(RHSNorm, resNorm, np.divide(resNorm, RHSNorm))) polesTrue = np.arange(1, 1 + N) polesTrue = polesTrue[polesTrue >= murange[0]] polesTrue = polesTrue[polesTrue <= murange[1]] polesApp = approx.getPoles() mask = (np.real(polesApp) < murange[0]) | (np.real(polesApp) > murange[1]) print("Outliers:", polesApp[mask]) polesApp = polesApp[~mask] plt.figure() plt.plot(np.real(polesApp), np.imag(polesApp), 'kx') plt.plot(polesTrue, np.zeros_like(polesTrue), 'm.') plt.axis('equal') plt.grid() plt.show() plt.close() diff --git a/examples/pod/parametricDomain.py b/examples/pod/parametricDomain.py index e0129db..77c7438 100644 --- a/examples/pod/parametricDomain.py +++ b/examples/pod/parametricDomain.py @@ -1,104 +1,53 @@ import numpy as np from rrompy.hfengines.linear_problem import \ HelmholtzSquareBubbleDomainProblemEngine as HSBDPE -from rrompy.reduction_methods.taylor import ApproximantTaylorPade as Pade -from rrompy.reduction_methods.taylor import ApproximantTaylorRB as RB -from rrompy.utilities.parameter_sweeper import ParameterSweeper as Sweeper +from rrompy.reduction_methods.centered import RationalPade as Pade +from rrompy.reduction_methods.centered import RBCentered as RB from operator import itemgetter def subdict(d, ks): return dict(zip(ks, itemgetter(*ks)(d))) -testNo = 3 +testNo = 2 verb = 0 if testNo == 1: mu = 7 ** .5 solver = HSBDPE(kappa = 12 ** .5, theta = np.pi / 3, n = 40, mu0 = mu, degree_threshold = 15, verbosity = verb) uh = solver.solve(mu) solver.plotmesh() print(solver.norm(uh)) solver.plot(uh) ############ if testNo == 2: params = {'N':8, 'M':8, 'E':8, 'sampleType':'Arnoldi', 'POD':True} # params = {'N':7, 'M':8, 'E':8, 'sampleType':'Krylov', 'POD':True} mu0 = 7 ** .5 mutar = (7. + .1j) ** .5 solver = HSBDPE(kappa = 12 ** .5, theta = np.pi / 3, n = 40, mu0 = mu0, degree_threshold = 15, verbosity = verb) approxP = Pade(solver, mu0 = mu0, approxParameters = params, verbosity = verb) paramsRB = subdict(params, ['E', 'sampleType', 'POD']) approxR = RB(solver, mu0 = mu0, approxParameters = paramsRB, verbosity = verb) approxP.setupApprox() approxR.setupApprox() # approxP.plotSamples() approxP.plotHF(mutar, name = 'u_HF') approxP.plotApprox(mutar, name = 'u_Pade''') approxR.plotApprox(mutar, name = 'u_RB') approxP.plotErr(mutar, name = 'err_Pade''') approxR.plotErr(mutar, name = 'err_RB') solNorm = approxP.normHF(mutar) appPErr = approxP.normErr(mutar) appRErr = approxR.normErr(mutar) print(('SolNorm:\t{}\nErrRelP:\t{}\nErrRelR:\t{}').format(solNorm, appPErr / solNorm, appRErr / solNorm)) print('\nPoles Pade'':') print(approxP.getPoles()) -############ -elif testNo == 3: - mu0 = 14 ** .5 - mutars = np.linspace(9**.5, 19**.5, 100) - solver = HSBDPE(kappa = 12 ** .5, theta = np.pi / 3, n = 20, mu0 = mu0, - degree_threshold = 15, verbosity = verb) - shift = 10 - nsets = 1 - stride = 2 - Emax = stride * (nsets - 1) + shift - params = {'Emax':Emax, 'sampleType':'Arnoldi', 'POD':True} - paramsSetsPade = [None] * nsets - paramsSetsRB = [None] * nsets - paramsSetsPoly = [None] * nsets - for i in range(nsets): - paramsSetsPade[i] = {'N':stride*i+shift, 'M':stride*i+shift, - 'E':stride*i+shift} - paramsSetsRB[i] = {'E':stride*i+shift} - paramsSetsPoly[i] = {'N':0, 'M':stride*i+shift, - 'E':stride*i+shift} - approxPade = Pade(solver, mu0 = mu0, approxParameters = params, - verbosity = verb) - approxRB = RB(solver, mu0 = mu0, approxParameters = params, - verbosity = verb) - approxPoly = Pade(solver, mu0 = mu0, approxParameters = params, - verbosity = verb) - - filenamebase = '../data/output/domainTaylor' - - sweeper = Sweeper(mutars = mutars, mostExpensive = 'Approx') - sweeper.ROMEngine = approxPade - sweeper.params = paramsSetsPade - filenamePade = sweeper.sweep(filenamebase + 'Pade.dat') - sweeper.ROMEngine = approxRB - sweeper.params = paramsSetsRB - filenameRB = sweeper.sweep(filenamebase + 'RB.dat') - sweeper.ROMEngine = approxPoly - sweeper.params = paramsSetsPoly - filenamePoly = sweeper.sweep(filenamebase + 'Poly.dat') - - sweeper.plotCompare([filenamePade, filenameRB, filenamePoly], ['muRe'], - ['normHF', 'normApp'], ['E'], onePlot = True, - save = filenamebase + 'Norm', - saveFormat = "png", labels = ["Pade'", "RB", "Poly"]) - sweeper.plotCompare([filenamePade, filenameRB, filenamePoly], ['muRe'], - ['normResRel'], ['E'], save = filenamebase + 'Res', - saveFormat = "png", labels = ["Pade'", "RB", "Poly"]) - sweeper.plotCompare([filenamePade, filenameRB, filenamePoly], ['muRe'], - ['normErrRel'], ['E'], save = filenamebase + 'Err', - saveFormat = "png", labels = ["Pade'", "RB", "Poly"]) diff --git a/examples/pod/scatteringSquare.py b/examples/pod/scatteringSquare.py index cfc7ece..1e1659f 100644 --- a/examples/pod/scatteringSquare.py +++ b/examples/pod/scatteringSquare.py @@ -1,172 +1,89 @@ from copy import copy import numpy as np from rrompy.hfengines.linear_problem import \ HelmholtzCavityScatteringProblemEngine as CSPE -from rrompy.reduction_methods.taylor import ApproximantTaylorPade as TP -from rrompy.reduction_methods.lagrange import ApproximantLagrangePade as LP -from rrompy.reduction_methods.taylor import ApproximantTaylorRB as TRB -from rrompy.reduction_methods.lagrange import ApproximantLagrangeRB as LRB -from rrompy.utilities.parameter_sweeper import ParameterSweeper as Sweeper +from rrompy.reduction_methods.centered import RationalPade as PC +from rrompy.reduction_methods.distributed import RationalInterpolant as PD +from rrompy.reduction_methods.centered import RBCentered as RBC +from rrompy.reduction_methods.distributed import RBDistributed as RBD from rrompy.utilities.parameter_sampling import QuadratureSampler as QS from operator import itemgetter def subdict(d, ks): return dict(zip(ks, itemgetter(*ks)(d))) verb = 0 #################### test = "solve" -test = "Taylor" -test = "Lagrange" -test = "TaylorSweep" -#test = "LagrangeSweep" +test = "Centered" +test = "Distributed" plotSamples = True k0 = 10 kLeft, kRight = 9, 11 ktar = 9.5 ktars = np.linspace(8.5, 11.5, 125) #ktars = np.array([k0]) kappa = 5 n = 50 solver = CSPE(kappa = kappa, n = n, verbosity = verb) solver.omega = k0 if test == "solve": uh = solver.solve(k0) print(solver.norm(uh)) solver.plot(uh, what = ['ABS', 'REAL']) -elif test in ["Taylor", "Lagrange"]: - if test == "Taylor": +elif test in ["Centered", "Distributed"]: + if test == "Centered": params = {'N':8, 'M':7, 'R':8, 'E':8, 'sampleType':'Krylov', 'POD':True} params = {'N':8, 'M':7, 'R':8, 'E':8, 'sampleType':'Arnoldi', 'POD':True} parPade = subdict(params, ['N', 'M', 'E', 'sampleType', 'POD']) parRB = subdict(params, ['R', 'E', 'sampleType', 'POD']) - approxPade = TP(solver, mu0 = k0, approxParameters = parPade, + approxPade = PC(solver, mu0 = k0, approxParameters = parPade, verbosity = verb) - approxRB = TRB(solver, mu0 = k0, approxParameters = parRB, + approxRB = RBC(solver, mu0 = k0, approxParameters = parRB, verbosity = verb) else: - params = {'N':8, 'M':8, 'R':9, 'S':9, 'POD':True, 'basis':"MONOMIAL"} - params = {'N':8, 'M':8, 'R':9, 'S':9, 'POD':True, 'basis':"CHEBYSHEV"} + params = {'N':8, 'M':8, 'R':9, 'S':9, 'POD':True, 'basis':"MONOMIAL", + 'sampler':QS([kLeft, kRight], "CHEBYSHEV")} + params = {'N':8, 'M':8, 'R':9, 'S':9, 'POD':True, 'basis':"CHEBYSHEV", + 'sampler':QS([kLeft, kRight], "CHEBYSHEV")} parPade = subdict(params, ['N', 'M', 'S', 'POD', 'basis']) parRB = subdict(params, ['R', 'S', 'POD']) - approxPade = LP(solver, mu0 = np.mean([kLeft, kRight]), + approxPade = PD(solver, mu0 = np.mean([kLeft, kRight]), approxParameters = parPade, verbosity = verb) - approxPade.sampler = QS([kLeft, kRight], "CHEBYSHEV") - approxRB = LRB(solver, mu0 = np.mean([kLeft, kRight]), + approxRB = RBD(solver, mu0 = np.mean([kLeft, kRight]), approxParameters = parRB, verbosity = verb) - approxRB.sampler = QS([kLeft, kRight], "CHEBYSHEV") approxPade.setupApprox() approxRB.setupApprox() if plotSamples: approxPade.plotSamples() PadeErr, solNorm = approxPade.normErr(ktar), approxPade.normHF(ktar) RBErr = approxRB.normErr(ktar) print(('SolNorm:\t{}\nErrPade:\t{}\nErrRelPade:\t{}\nErrRB:\t\t{}' '\nErrRelRB:\t{}').format(solNorm, PadeErr, np.divide(PadeErr, solNorm), RBErr, np.divide(RBErr, solNorm))) print('\nPoles Pade'':') print(approxPade.getPoles()) print('\nPoles RB:') print(approxRB.getPoles()) approxPade.plotHF(ktar, name = 'u_ex') approxPade.plotApprox(ktar, name = 'u_Pade''') approxRB.plotApprox(ktar, name = 'u_RB') approxPade.plotErr(ktar, name = 'errPade''') approxRB.plotErr(ktar, name = 'errRB') -elif test in ["TaylorSweep", "LagrangeSweep"]: - if test == "TaylorSweep": - shift = 5 - nsets = 4 - stride = 3 - Emax = stride * (nsets - 1) + shift + 1 - params = {'Emax':Emax, 'sampleType':'Krylov', 'POD':True} - params = {'Emax':Emax, 'sampleType':'Arnoldi', 'POD':True} - paramsSetsPade = [None] * nsets - paramsSetsRB = [None] * nsets - for i in range(nsets): - paramsSetsPade[i] = {'N': stride*i+shift+1, - 'M': stride*i+shift,#+1, - 'E': stride*i+shift+1} - paramsSetsRB[i] = {'R': stride*i+shift+1,#+1, - 'E': stride*i+shift+1} - approxPade = TP(solver, mu0 = k0,approxParameters = params, - verbosity = verb) - approxRB = TRB(solver, mu0 = k0, approxParameters = params, - verbosity = verb) - else: - shift = 7 - nsets = 4 - stride = 3 - Smax = stride * (nsets - 1) + shift + 2 - paramsPade = {'S':Smax, 'POD':True, 'basis':"MONOMIAL"} - paramsPade = {'S':Smax, 'POD':True, 'basis':"CHEBYSHEV"} - paramsRB = copy(paramsPade) - paramsPoly = copy(paramsPade) - paramsSetsPade = [None] * nsets - paramsSetsRB = [None] * nsets - paramsSetsPoly = [None] * nsets - for i in range(nsets): - paramsSetsPade[i] = {'N': stride*i+shift+1, - 'M': stride*i+shift+1, - 'S': stride*i+shift+2} - paramsSetsRB[i] = {'R': stride*i+shift+2, - 'S': stride*i+shift+2} - paramsSetsPoly[i] = {'N': 0, - 'M': stride*i+shift+1, - 'S': stride*i+shift+2} - approxPade = LP(solver, mu0 = np.mean([kLeft, kRight]), - approxParameters = paramsPade, - verbosity = verb) - approxPade.sampler = QS([kLeft, kRight], "CHEBYSHEV") - approxRB = LRB(solver, mu0 = np.mean([kLeft, kRight]), - approxParameters = paramsRB, - verbosity = verb) - approxRB.sampler = QS([kLeft, kRight], "CHEBYSHEV") - approxPoly = LP(solver, mu0 = np.mean([kLeft, kRight]), - approxParameters = paramsPoly, - verbosity = verb) - - filenamebase = '../data/output/scatSquare' + test[:-5] - - sweeper = Sweeper(mutars = ktars, mostExpensive = 'Approx') - sweeper.ROMEngine = approxPade - sweeper.params = paramsSetsPade - filenamePade = sweeper.sweep(filenamebase + 'Pade.dat') - sweeper.ROMEngine = approxRB - sweeper.params = paramsSetsRB - filenameRB = sweeper.sweep(filenamebase + 'RB.dat') - sweeper.ROMEngine = approxPoly - sweeper.params = paramsSetsPoly - filenamePoly = sweeper.sweep(filenamebase + 'Poly.dat') - - if test == "TaylorSweep": - constr = ['E'] - else: - constr = ['S'] -sweeper.plotCompare([filenamePade, filenameRB, filenamePoly], ['muRe'], - ['normHF', 'normApp'], constr, onePlot = True, - save = filenamebase + 'Norm', - saveFormat = "png", labels = ["Pade'", "RB", "Poly"]) -sweeper.plotCompare([filenamePade, filenameRB, filenamePoly], ['muRe'], - ['normResRel'], constr, save = filenamebase + 'Res', - saveFormat = "png", labels = ["Pade'", "RB", "Poly"]) -sweeper.plotCompare([filenamePade, filenameRB, filenamePoly], ['muRe'], - ['normErrRel'], constr, save = filenamebase + 'Err', - saveFormat = "png", labels = ["Pade'", "RB", "Poly"]) diff --git a/rrompy/hfengines/base/boundary_conditions.py b/rrompy/hfengines/base/boundary_conditions.py index fffe262..7ca334f 100644 --- a/rrompy/hfengines/base/boundary_conditions.py +++ b/rrompy/hfengines/base/boundary_conditions.py @@ -1,126 +1,126 @@ # Copyright (C) 2018 by the RROMPy authors # # This file is part of RROMPy. # # RROMPy is free software: you can redistribute it and/or modify # it under the terms of the GNU Lesser General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # RROMPy is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU Lesser General Public License for more details. # # You should have received a copy of the GNU Lesser General Public License # along with RROMPy. If not, see . # from fenics import SubDomain, AutoSubDomain from rrompy.utilities.base.types import GenExpr -from rrompy.utilities.base.fenics import bdrFalse +from rrompy.utilities.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 __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._standardManagement(k, bdrFalse) otherBCs += [getattr(self, k + "Boundary")] def restCall(x, on_boundary): return (on_boundary and not any([bc.inside(x, on_boundary) for bc in otherBCs])) self._standardManagementCallable(kind, restCall) super().__setattr__("_" + kind + "Rest", 1) def _standardManagementCallable(self, kind:str, bc:callable): bcSD = AutoSubDomain(bc) self._standardManagement(kind, bcSD) def _standardManagement(self, kind:str, bc:SubDomain): super().__setattr__("_" + kind + "Boundary", bc) if hasattr(self, "_" + kind + "Rest"): super().__delattr__("_" + kind + "Rest") @property def DirichletBoundary(self): """Function handle to DirichletBoundary.""" return self._DirichletBoundary @DirichletBoundary.setter def DirichletBoundary(self, DirichletBoundary): self._generalManagement("Dirichlet", DirichletBoundary) @property def NeumannBoundary(self): """Function handle to NeumannBoundary.""" return self._NeumannBoundary @NeumannBoundary.setter def NeumannBoundary(self, NeumannBoundary): self._generalManagement("Neumann", NeumannBoundary) @property def RobinBoundary(self): """Function handle to RobinBoundary.""" return self._RobinBoundary @RobinBoundary.setter def RobinBoundary(self, RobinBoundary): self._generalManagement("Robin", RobinBoundary) diff --git a/rrompy/hfengines/base/matrix_engine_base.py b/rrompy/hfengines/base/matrix_engine_base.py index aa31c70..f213b9a 100644 --- a/rrompy/hfengines/base/matrix_engine_base.py +++ b/rrompy/hfengines/base/matrix_engine_base.py @@ -1,287 +1,299 @@ # Copyright (C) 2018 by the RROMPy authors # # This file is part of RROMPy. # # RROMPy is free software: you can redistribute it and/or modify # it under the terms of the GNU Lesser General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # RROMPy is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU Lesser General Public License for more details. # # You should have received a copy of the GNU Lesser General Public License # along with RROMPy. If not, see . # import numpy as np import scipy.sparse as scsp from matplotlib import pyplot as plt from rrompy.utilities.base.types import (Np1D, Np2D, ScOp, strLst, Tuple, List, DictAny) from rrompy.utilities.base import purgeList, getNewFilename 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. """ nAs, nbs = 1, 1 rescalingExp = 1. functional = lambda self, u: 0. def __init__(self, verbosity : int = 10, timestamp : bool = True): self.verbosity = verbosity self.timestamp = timestamp self.resetAs() self.resetbs() self.setSolver("SPSOLVE", {"use_umfpack" : False}) def name(self) -> str: return self.__class__.__name__ def __str__(self) -> str: return self.name() def __repr__(self) -> str: return self.__str__() + " at " + hex(id(self)) def __dir_base__(self): return [x for x in self.__dir__() if x[:2] != "__"] @property def nbsH(self) -> int: return max(self.nbs, self.nAs) def spacedim(self): return self.As[0].shape[1] + 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"): - self.energyNormMatrix = np.eye(self.spacedim()) + 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 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, der : int = 0): """Check if derivative index is oob for operator of linear system.""" if der < 0 or der >= 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, der : 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 der < 0 or der >= nbs: return np.zeros(self.spacedim(), dtype = np.complex) def resetAs(self): """Reset (derivatives of) operator of linear system.""" self.resetbsH() self.As = [None] * self.nAs def resetbs(self): """Reset (derivatives of) RHS of linear system.""" self.resetbsH() self.bs = [None] * self.nbs def resetbsH(self): """Reset (derivatives of) homogeneized RHS of linear system.""" self.bsH = [None] * self.nbsH def A(self, mu:complex, der : int = 0) -> ScOp: """Return (derivative of) operator of linear system.""" Anull = self.checkAInBounds(der) if Anull is not None: return Anull As0 = self.As[der] coeff = 1. for j in range(der + 1, self.nAs): coeff = coeff * mu * j / (j - der) As0 = As0 + coeff * self.As[j] return As0 def affineLinearSystemA(self, mu : complex = 0.) -> List[Np2D]: """ Assemble affine blocks of operator of linear system (just linear blocks). """ As = [None] * self.nAs for j in range(self.nAs): As[j] = self.A(mu, j) return As def affineWeightsA(self, mu : complex = 0.) -> callable: """ Assemble affine blocks of operator of linear system (just affine weights). Stored as strings for the sake of pickling. """ lambdasA = ["np.ones_like(mu)"] mu0Eff = np.power(mu, self.rescalingExp) for j in range(1, self.nAs): lambdasA += ["np.power(np.power(mu, {1}) - {2}, {0})".format(j, self.rescalingExp, mu0Eff)] return lambdasA def affineBlocksA(self, mu : complex = 0.) -> Tuple[List[Np2D], callable]: """Assemble affine blocks of operator of linear system.""" return self.affineLinearSystemA(mu), self.affineWeightsA(mu) def b(self, mu:complex, der : int = 0, homogeneized : bool = False) -> Np1D: """Return (derivative of) (homogeneized) RHS of linear system.""" bnull = self.checkbInBounds(der, homogeneized) if bnull is not None: return bnull bs = self.bsH if homogeneized else self.bs b = bs[der] coeff = 1. for j in range(der + 1, len(bs)): coeff = coeff * mu * j / (j - der) b = b + coeff * bs[j] return b def affineLinearSystemb(self, mu : complex = 0., 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, j, homogeneized) return bs def affineWeightsb(self, mu : complex = 0., homogeneized : bool = False)\ -> callable: """ Assemble affine blocks of RHS of linear system (just affine weights). Stored as strings for the sake of pickling. """ nbs = self.nbsH if homogeneized else self.nbs lambdasb = ["np.ones_like(mu)"] mu0Eff = np.power(mu, self.rescalingExp) for j in range(1, nbs): lambdasb += ["np.power(np.power(mu, {1}) - {2}, {0})".format(j, self.rescalingExp, mu0Eff)] return lambdasb def affineBlocksb(self, mu : complex = 0., homogeneized : bool = False)\ -> Tuple[List[Np1D], callable]: """Assemble affine blocks of RHS of linear system.""" return (self.affineLinearSystemb(mu, homogeneized), self.affineWeightsb(mu, homogeneized)) def setSolver(self, solverType:str, solverArgs : DictAny = {}): """Choose solver type and parameters.""" self._solver, self._solverArgs = setupSolver(solverType, solverArgs) def solve(self, mu:complex, RHS : Np1D = None, homogeneized : bool = False) -> Np1D: """ Find solution of linear system. Args: mu: parameter value. RHS: RHS of linear system. If None, defaults to that of parametric system. Defaults to None. """ A = self.A(mu) if RHS is None: RHS = self.b(mu, homogeneized = homogeneized) return self._solver(A, RHS, self._solverArgs) def residual(self, u:Np1D, mu:complex, homogeneized : bool = False) -> Np1D: """ Find residual of linear system for given approximate solution. Args: u: numpy complex array with function dofs. If None, set to 0. mu: parameter value. """ A = self.A(mu) RHS = self.b(mu, homogeneized = homogeneized) if u is None: return RHS return RHS - A.dot(u) def plot(self, u:Np1D, name : str = "u", save : str = None, what : strLst = 'all', saveFormat : str = "eps", saveDPI : int = 100, 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/base/problem_engine_base.py b/rrompy/hfengines/base/problem_engine_base.py index b031076..8658551 100644 --- a/rrompy/hfengines/base/problem_engine_base.py +++ b/rrompy/hfengines/base/problem_engine_base.py @@ -1,376 +1,361 @@ # Copyright (C) 2018 by the RROMPy authors # # This file is part of RROMPy. # # RROMPy is free software: you can redistribute it and/or modify # it under the terms of the GNU Lesser General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # RROMPy is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU Lesser General Public License for more details. # # You should have received a copy of the GNU Lesser General Public License # along with RROMPy. If not, see . # from abc import abstractmethod from os import path, mkdir import fenics as fen import numpy as np -from scipy.sparse import csr_matrix from matplotlib import pyplot as plt -from rrompy.utilities.base.types import (Np1D, Np2D, ScOp, strLst, FenFunc, - Tuple, List) +from rrompy.utilities.base.types import (Np1D, ScOp, strLst, FenFunc, Tuple, + List) from rrompy.utilities.base import purgeList, getNewFilename, verbosityDepth +from rrompy.utilities.fenics import L2NormMatrix from .boundary_conditions import BoundaryConditions from .matrix_engine_base import MatrixEngineBase from rrompy.utilities.exception_manager import RROMPyException __all__ = ['ProblemEngineBase'] class ProblemEngineBase(MatrixEngineBase): """ Generic solver for parametric problems. Attributes: verbosity: Verbosity level. BCManager: Boundary condition manager. V: Real FE space. u: Generic trial functions for variational form evaluation. v: Generic test functions for variational form evaluation. As: Scipy sparse array representation (in CSC format) of As. bs: Numpy array representation of bs. energyNormMatrix: Scipy sparse matrix representing inner product over V. bsmu: Mu value of last bs evaluation. liftDirichletDatamu: Mu value of last Dirichlet datum evaluation. liftedDirichletDatum: Dofs of Dirichlet datum lifting. mu0BC: Mu value of last Dirichlet datum lifting. degree_threshold: Threshold for ufl expression interpolation degree. """ def __init__(self, degree_threshold : int = np.inf, verbosity : int = 10, timestamp : bool = True): super().__init__(verbosity = verbosity, timestamp = timestamp) self.BCManager = BoundaryConditions("Dirichlet") self.V = fen.FunctionSpace(fen.UnitSquareMesh(10, 10), "P", 1) self.bsmu = np.nan self.liftDirichletDatamu = np.nan self.mu0BC = np.nan self.degree_threshold = degree_threshold @property def V(self): """Value of V.""" return self._V @V.setter def V(self, V): self.resetAs() self.resetbs() if not type(V).__name__ == 'FunctionSpace': raise RROMPyException("V type not recognized.") self._V = V self.u = fen.TrialFunction(V) self.v = fen.TestFunction(V) def spacedim(self): return self.V.dim() - def innerProduct(self, u:Np2D, v:Np2D, onlyDiag : bool = False) -> Np2D: - """Hilbert space scalar product.""" - if not hasattr(self, "energyNormMatrix"): - self.buildEnergyNormForm() - return super().innerProduct(u, v, onlyDiag) - - def buildEnergyNormForm(self): # L2 + def buildEnergyNormForm(self): """ Build sparse matrix (in CSR format) representative of scalar product. """ - if self.verbosity >= 20: - verbosityDepth("INIT", "Assembling energy matrix.", - timestamp = self.timestamp) - normMatFen = fen.assemble(fen.dot(self.u, self.v) * fen.dx) - normMat = fen.as_backend_type(normMatFen).mat() - self.energyNormMatrix = csr_matrix(normMat.getValuesCSR()[::-1], - shape = normMat.size) - if self.verbosity >= 20: - verbosityDepth("DEL", "Done assembling energy matrix.", - timestamp = self.timestamp) + self.energyNormMatrix = L2NormMatrix(self.V) def setDirichletDatum(self, mu:complex): """Set Dirichlet datum if parametric.""" if hasattr(self, "liftedDirichletDatum"): self.liftDirichletDatamu = mu def liftDirichletData(self, mu:complex) -> Np1D: """Lift Dirichlet datum.""" self.setDirichletDatum(mu) if not np.isclose(self.liftDirichletDatamu, mu): try: liftRe = fen.interpolate(self.DirichletDatum[0], self.V) except: liftRe = fen.project(self.DirichletDatum[0], self.V) try: liftIm = fen.interpolate(self.DirichletDatum[1], self.V) except: liftIm = fen.project(self.DirichletDatum[1], self.V) self.liftedDirichletDatum = (np.array(liftRe.vector()) + 1.j * np.array(liftIm.vector())) return self.liftedDirichletDatum def reduceQuadratureDegree(self, fun:FenFunc, name:str): """Check whether to reduce compiler parameters to degree threshold.""" if not np.isinf(self.degree_threshold): from ufl.algorithms.estimate_degrees import ( estimate_total_polynomial_degree as ETPD) try: deg = ETPD(fun) except: return False if deg > self.degree_threshold: if self.verbosity >= 15: verbosityDepth("MAIN", ("Reducing quadrature degree from " "{} to {} for {}.").format( deg, self.degree_threshold, name), timestamp = self.timestamp) return True return False def iterReduceQuadratureDegree(self, funsNames:List[Tuple[FenFunc, str]]): """ Iterate reduceQuadratureDegree over list and define reduce compiler parameters. """ if funsNames is not None: for fun, name in funsNames: if self.reduceQuadratureDegree(fun, name): return {"quadrature_degree" : self.degree_threshold} return {} @abstractmethod def A(self, mu:complex, der : int = 0) -> ScOp: """Assemble (derivative of) operator of linear system.""" Anull = self.checkAInBounds(der) if Anull is not None: return Anull if self.As[der] is None: self.As[der] = 0. return self.As[der] @abstractmethod def b(self, mu:complex, der : int = 0, homogeneized : bool = False) -> Np1D: """Assemble (derivative of) RHS of linear system.""" bnull = self.checkbInBounds(der, homogeneized) if bnull is not None: return bnull b = self.bsH[der] if homogeneized else self.bs[der] if b is None: if homogeneized: self.bsH[der] = 0. else: self.bs[der] = 0. b = 0. return b 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 plt.figure(**figspecs) plt.jet() if 'ABS' in what: uAb = fen.Function(self.V) uAb.vector().set_local(np.abs(u)) subplotcode = subplotcode + 1 plt.subplot(subplotcode) p = fen.plot(uAb, title = "|{0}|".format(name)) plt.colorbar(p) if 'PHASE' in what: uPh = fen.Function(self.V) uPh.vector().set_local(np.angle(u)) subplotcode = subplotcode + 1 plt.subplot(subplotcode) p = fen.plot(uPh, title = "phase({0})".format(name)) plt.colorbar(p) if 'REAL' in what: uRe = fen.Function(self.V) uRe.vector().set_local(np.real(u)) subplotcode = subplotcode + 1 plt.subplot(subplotcode) p = fen.plot(uRe, title = "Re({0})".format(name)) plt.colorbar(p) if 'IMAG' in what: uIm = fen.Function(self.V) uIm.vector().set_local(np.imag(u)) subplotcode = subplotcode + 1 plt.subplot(subplotcode) p = fen.plot(uIm, title = "Im({0})".format(name)) plt.colorbar(p) if save is not None: save = save.strip() plt.savefig(getNewFilename("{}_fig_".format(save), saveFormat), format = saveFormat, dpi = saveDPI) if show: plt.show() plt.close() def plotmesh(self, name : str = "Mesh", save : str = None, saveFormat : str = "eps", saveDPI : int = 100, show : bool = True, **figspecs): """ Do a nice plot of the mesh. Args: u: numpy complex array with function dofs. name(optional): Name to be shown as title of the plots. Defaults to 'u'. save(optional): Where to save plot(s). Defaults to None, i.e. no saving. saveFormat(optional): Format for saved plot(s). Defaults to "eps". saveDPI(optional): DPI for saved plot(s). Defaults to 100. show(optional): Whether to show figure. Defaults to True. figspecs(optional key args): Optional arguments for matplotlib figure creation. """ plt.figure(**figspecs) fen.plot(self.V.mesh()) if save is not None: save = save.strip() plt.savefig(getNewFilename("{}_msh_".format(save), saveFormat), format = saveFormat, dpi = saveDPI) if show: plt.show() plt.close() def outParaview(self, u:Np1D, name : str = "u", filename : str = "out", time : float = 0., what : strLst = 'all', forceNewFile : bool = True, folder : bool = False, filePW = None): """ Output complex-valued function with given dofs to ParaView file. Args: u: numpy complex array with function dofs. name(optional): Base name to be used for data output. filename(optional): Name of output file. time(optional): Timestamp. what(optional): Which plots to do. If list, can contain 'MESH', 'ABS', 'PHASE', 'REAL', 'IMAG'. If str, same plus wildcard 'ALL'. Defaults to 'ALL'. forceNewFile(optional): Whether to create new output file. folder(optional): Whether to create an additional folder layer. filePW(optional): Fenics File entity (for time series). """ if isinstance(what, (str,)): if what.upper() == 'ALL': what = ['MESH', 'ABS', 'PHASE', 'REAL', 'IMAG'] else: what = [what] what = purgeList(what, ['MESH', 'ABS', 'PHASE', 'REAL', 'IMAG'], listname = self.name() + ".what", baselevel = 1) if len(what) == 0: return if filePW is None: if folder: if not path.exists(filename + "/"): mkdir(filename) idxpath = filename.rfind("/") filename += "/" + filename[idxpath + 1 :] if forceNewFile: filePW = fen.File(getNewFilename(filename, "pvd")) else: filePW = fen.File("{}.pvd".format(filename)) if what == ['MESH']: filePW << (self.V.mesh(), time) if 'ABS' in what: uAb = fen.Function(self.V, name = "{}_ABS".format(name)) uAb.vector().set_local(np.abs(u)) filePW << (uAb, time) if 'PHASE' in what: uPh = fen.Function(self.V, name = "{}_PHASE".format(name)) uPh.vector().set_local(np.angle(u)) filePW << (uPh, time) if 'REAL' in what: uRe = fen.Function(self.V, name = "{}_REAL".format(name)) uRe.vector().set_local(np.real(u)) filePW << (uRe, time) if 'IMAG' in what: uIm = fen.Function(self.V, name = "{}_IMAG".format(name)) uIm.vector().set_local(np.imag(u)) filePW << (uIm, time) return filePW def outParaviewTimeDomain(self, u:Np1D, omega:float, timeFinal : float = None, periodResolution : int = 20, name : str = "u", filename : str = "out", forceNewFile : bool = True, folder : bool = False): """ Output complex-valued function with given dofs to ParaView file, converted to time domain. Args: u: numpy complex array with function dofs. omega: frequency. timeFinal(optional): final time of simulation. periodResolution(optional): number of time steps per period. name(optional): Base name to be used for data output. filename(optional): Name of output file. forceNewFile(optional): Whether to create new output file. folder(optional): Whether to create an additional folder layer. """ if folder: if not path.exists(filename + "/"): mkdir(filename) idxpath = filename.rfind("/") filename += "/" + filename[idxpath + 1 :] if forceNewFile: filePW = fen.File(getNewFilename(filename, "pvd")) else: filePW = fen.File("{}.pvd".format(filename)) omega = np.abs(omega) t = 0. dt = 2. * np.pi / omega / periodResolution if timeFinal is None: timeFinal = 2. * np.pi / omega - dt for j in range(int(np.ceil(timeFinal / dt)) + 1): ut = fen.Function(self.V, name = name) ut.vector().set_local(np.real(u) * np.cos(omega * t) + np.imag(u) * np.sin(omega * t)) filePW << (ut, t) t += dt return filePW diff --git a/rrompy/hfengines/linear_problem/helmholtz_problem_engine.py b/rrompy/hfengines/linear_problem/helmholtz_problem_engine.py index c6059cd..a515bc7 100644 --- a/rrompy/hfengines/linear_problem/helmholtz_problem_engine.py +++ b/rrompy/hfengines/linear_problem/helmholtz_problem_engine.py @@ -1,163 +1,163 @@ # Copyright (C) 2018 by the RROMPy authors # # This file is part of RROMPy. # # RROMPy is free software: you can redistribute it and/or modify # it under the terms of the GNU Lesser General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # RROMPy is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU Lesser General Public License for more details. # # You should have received a copy of the GNU Lesser General Public License # along with RROMPy. If not, see . # import numpy as np import scipy.sparse as scsp import fenics as fen from .laplace_base_problem_engine import LaplaceBaseProblemEngine from rrompy.utilities.base.types import ScOp -from rrompy.utilities.base.fenics import fenZERO, fenONE +from rrompy.utilities.fenics import fenZERO, fenONE from rrompy.utilities.base import verbosityDepth __all__ = ['HelmholtzProblemEngine'] class HelmholtzProblemEngine(LaplaceBaseProblemEngine): """ Solver for generic Helmholtz problems with parametric wavenumber. - \nabla \cdot (a \nabla u) - omega^2 * n**2 * u = f in \Omega u = u0 on \Gamma_D \partial_nu = g1 on \Gamma_N \partial_nu + h u = g2 on \Gamma_R Attributes: verbosity: Verbosity level. BCManager: Boundary condition manager. V: Real FE space. u: Generic trial functions for variational form evaluation. v: Generic test functions for variational form evaluation. As: Scipy sparse array representation (in CSC format) of As. bs: Numpy array representation of bs. bsH: Numpy array representation of homogeneized bs. energyNormMatrix: Scipy sparse matrix representing inner product over V. bsmu: Mu value of last bs evaluation. liftDirichletDatamu: Mu value of last Dirichlet datum evaluation. liftedDirichletDatum: Dofs of Dirichlet datum lifting. mu0BC: Mu value of last Dirichlet datum lifting. degree_threshold: Threshold for ufl expression interpolation degree. omega: Value of omega. diffusivity: Value of a. refractionIndex: Value of n. forcingTerm: Value of f. DirichletDatum: Value of u0. NeumannDatum: Value of g1. RobinDatumG: Value of g2. RobinDatumH: Value of h. DirichletBoundary: Function handle to \Gamma_D. NeumannBoundary: Function handle to \Gamma_N. RobinBoundary: Function handle to \Gamma_R. ds: Boundary measure 2-tuple (resp. for Neumann and Robin boundaries). A0: Scipy sparse array representation (in CSC format) of A0. A1: Scipy sparse array representation (in CSC format) of A1. b0: Numpy array representation of b0. dsToBeSet: Whether ds needs to be set. """ nAs = 2 rescalingExp = 2. def __init__(self, degree_threshold : int = np.inf, verbosity : int = 10, timestamp : bool = True): super().__init__(degree_threshold = degree_threshold, verbosity = verbosity, timestamp = timestamp) self.omega = 1. self.refractionIndex = fenONE @property def refractionIndex(self): """Value of n.""" return self._refractionIndex @refractionIndex.setter def refractionIndex(self, refractionIndex): self.resetAs() if not isinstance(refractionIndex, (list, tuple,)): refractionIndex = [refractionIndex, fenZERO] self._refractionIndex = refractionIndex def A(self, mu:complex, der : int = 0) -> ScOp: """Assemble (derivative of) operator of linear system.""" Anull = self.checkAInBounds(der) if Anull is not None: return Anull self.autoSetDS() if der <= 0 and self.As[0] is None: if self.verbosity >= 20: verbosityDepth("INIT", "Assembling operator term A0.", timestamp = self.timestamp) DirichletBC0 = fen.DirichletBC(self.V, fenZERO, self.DirichletBoundary) aRe, aIm = self.diffusivity hRe, hIm = self.RobinDatumH termNames = ["diffusivity", "RobinDatumH"] parsRe = self.iterReduceQuadratureDegree(zip( [aRe, hRe], [x + "Real" for x in termNames])) parsIm = self.iterReduceQuadratureDegree(zip( [aIm, hIm], [x + "Imag" for x in termNames])) a0Re = (aRe * fen.dot(fen.grad(self.u), fen.grad(self.v)) * fen.dx + hRe * fen.dot(self.u, self.v) * self.ds(1)) a0Im = (aIm * fen.dot(fen.grad(self.u), fen.grad(self.v)) * fen.dx + hIm * fen.dot(self.u, self.v) * self.ds(1)) A0Re = fen.assemble(a0Re, form_compiler_parameters = parsRe) A0Im = fen.assemble(a0Im, form_compiler_parameters = parsIm) DirichletBC0.apply(A0Re) DirichletBC0.zero(A0Im) A0ReMat = fen.as_backend_type(A0Re).mat() A0ImMat = fen.as_backend_type(A0Im).mat() A0Rer, A0Rec, A0Rev = A0ReMat.getValuesCSR() A0Imr, A0Imc, A0Imv = A0ImMat.getValuesCSR() self.As[0] = (scsp.csr_matrix((A0Rev, A0Rec, A0Rer), shape = A0ReMat.size) + 1.j * scsp.csr_matrix((A0Imv, A0Imc, A0Imr), shape = A0ImMat.size)) if self.verbosity >= 20: verbosityDepth("DEL", "Done assembling operator term.", timestamp = self.timestamp) if der <= 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 der == 0: return self.As[0] + mu**2 * self.As[1] return self.As[1] diff --git a/rrompy/hfengines/linear_problem/helmholtz_square_bubble_domain_problem_engine.py b/rrompy/hfengines/linear_problem/helmholtz_square_bubble_domain_problem_engine.py index a2e18c1..9d00525 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,251 +1,245 @@ # Copyright (C) 2018 by the RROMPy authors # # This file is part of RROMPy. # # RROMPy is free software: you can redistribute it and/or modify # it under the terms of the GNU Lesser General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # RROMPy is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU Lesser General Public License for more details. # # You should have received a copy of the GNU Lesser General Public License # along with RROMPy. If not, see . # import numpy as np import scipy.sparse as scsp import fenics as fen from rrompy.utilities.base.types import Np1D, ScOp, Tuple, FenExpr -from rrompy.utilities.base.fenics import fenZERO +from rrompy.utilities.fenics import fenZERO from .helmholtz_problem_engine import HelmholtzProblemEngine from rrompy.utilities.base import verbosityDepth __all__ = ['HelmholtzSquareBubbleDomainProblemEngine'] class HelmholtzSquareBubbleDomainProblemEngine(HelmholtzProblemEngine): """ Solver for square bubble Helmholtz problems with parametric domain heigth. - \Delta u - kappa^2 * u = f in \Omega_mu = [0,\pi] x [0,\mu\pi] u = 0 on \Gamma_mu = \partial\Omega_mu with exact solution square bubble times plane wave. """ nAs, nbs = 3, 20 rescalingExp = 1. def __init__(self, kappa:float, theta:float, n:int, mu0 : np.complex = 1., degree_threshold : int = np.inf, verbosity : int = 10, timestamp : bool = True): super().__init__(degree_threshold = degree_threshold, verbosity = verbosity, timestamp = timestamp) self.omega = kappa self.kappa = kappa self.theta = theta self.mu0 = mu0 self.forcingTermMu = np.nan mesh = fen.RectangleMesh(fen.Point(0,0), fen.Point(np.pi,np.pi), n, n) self.V = fen.FunctionSpace(mesh, "P", 3) def buildEnergyNormForm(self): # H1 """ Build sparse matrix (in CSR format) representative of scalar product. """ - if self.verbosity >= 20: - verbosityDepth("INIT", "Assembling energy matrix.", - timestamp = self.timestamp) mudx = np.abs(self.mu0) * fen.dot(self.u.dx(0), self.v.dx(0)) * fen.dx muM = np.abs(self.mu0) * fen.dot(self.u, self.v) * fen.dx imudy = 1. / np.abs(self.mu0) * (fen.dot(self.u.dx(1), self.v.dx(1)) * fen.dx) normMatFen = fen.assemble(mudx + imudy + muM) normMat = fen.as_backend_type(normMatFen).mat() self.energyNormMatrix = scsp.csr_matrix(normMat.getValuesCSR()[::-1], shape = normMat.size) - if self.verbosity >= 20: - verbosityDepth("DEL", "Done assembling energy matrix.", - timestamp = self.timestamp) def getForcingTerm(self, mu:complex) -> Tuple[FenExpr, FenExpr]: """Compute forcing term.""" if not np.isclose(mu, self.forcingTermMu): if self.verbosity >= 25: verbosityDepth("INIT", ("Assembling base expression for " "forcing term."), timestamp = self.timestamp) pi = np.pi c, s = np.cos(self.theta), np.sin(self.theta) x, y = fen.SpatialCoordinate(self.V.mesh())[:] muR, muI = np.real(mu), np.imag(mu) mu2R, mu2I = np.real(mu ** 2.), np.imag(mu ** 2.) C = 16. / pi ** 4. bR = C * (2 * (x * (pi - x) + y * (pi - y)) + (self.kappa * s) ** 2. * (mu2R - 1.) * x * (pi - x) * y * (pi - y)) bI = C * (2 * self.kappa * (c * (pi - 2 * x) * y * (pi - y) + s * x * (pi - x) * (pi - 2 * y)) + (self.kappa * s) ** 2. * mu2I * x * (pi - x) * y * (pi - y)) wR = (fen.cos(self.kappa * (c * x + s * muR * y)) * fen.exp(self.kappa * s * muI * y)) wI = (fen.sin(self.kappa * (c * x + s * muR * y)) * fen.exp(self.kappa * s * muI * y)) self.forcingTerm = [bR * wR + bI * wI, bI * wR - bR * wI] self.forcingTermMu = mu if self.verbosity >= 25: verbosityDepth("DEL", "Done assembling base expression.", timestamp = self.timestamp) return self.forcingTerm def getExtraFactorB(self, mu:complex, der:int) -> Tuple[FenExpr, FenExpr]: """Compute extra expression in RHS.""" def getPowMinusj(x, power): powR = x ** power powI = fenZERO if power % 2 == 1: powR, powI = powI, powR if (power + 3) % 4 < 2: powR, powI = - powR, - powI return powR, powI if self.verbosity >= 25: verbosityDepth("INIT", ("Assembling auxiliary expression for " "forcing term derivative."), timestamp = self.timestamp) from math import factorial as fact y = fen.SpatialCoordinate(self.V.mesh())[1] powR, powI = [(self.kappa * np.sin(self.theta)) ** der * k\ for k in getPowMinusj(y, der)] mu2R, mu2I = np.real(mu ** 2.), np.imag(mu ** 2.) exprR = mu2R * powR - mu2I * powI exprI = mu2I * powR + mu2R * powI if der >= 1: muR, muI = np.real(2. * mu), np.imag(2. * mu) powR, powI = [(self.kappa * np.sin(self.theta)) ** (der - 1) * k\ * der for k in getPowMinusj(y, der - 1)] exprR += muR * powR - muI * powI exprI += muI * powR + muR * powI if der >= 2: powR, powI = [(self.kappa * np.sin(self.theta)) ** (der - 2) * k\ * der * (der - 1) for k in getPowMinusj(y, der - 2)] exprR += powR exprI += powI fac = fact(der) if self.verbosity >= 25: verbosityDepth("DEL", "Done assembling auxiliary expression.", timestamp = self.timestamp) return [exprR / fac, exprI / fac] def A(self, mu:complex, der : int = 0) -> ScOp: """Assemble (derivative of) operator of linear system.""" Anull = self.checkAInBounds(der) if Anull is not None: return Anull self.autoSetDS() if der <= 0 and self.As[0] is None: if self.verbosity >= 20: verbosityDepth("INIT", "Assembling operator term A0.", 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 der <= 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) if der == 0: return self.As[0] + mu ** 2 * self.As[2] if der == 1: return 2. * mu * self.As[2] return self.As[2] def b(self, mu:complex, der : int = 0, homogeneized : bool = False) -> Np1D: """Assemble (derivative of) RHS of linear system.""" bnull = self.checkbInBounds(der, homogeneized) if bnull is not None: return bnull if homogeneized and not np.isclose(self.mu0BC, mu): self.u0BC = self.liftDirichletData(mu) if not np.isclose(self.bsmu, mu): self.bsmu = mu self.resetbs() b = self.bsH[der] if homogeneized else self.bs[der] if b is None: if self.verbosity >= 20: verbosityDepth("INIT", ("Assembling forcing term " "b{}.").format(der), timestamp = self.timestamp) if der < self.nbs: fRe, fIm = self.getForcingTerm(mu) cRe, cIm = self.getExtraFactorB(mu, der) cfRe = cRe * fRe - cIm * fIm cfIm = cRe * fIm + cIm * fRe else: cfRe, cfIm = fenZERO, fenZERO parsRe = self.iterReduceQuadratureDegree(zip([cfRe], ["forcingTermDer{}Real".format(der)])) parsIm = self.iterReduceQuadratureDegree(zip([cfIm], ["forcingTermDer{}Imag".format(der)])) L0Re = fen.dot(cfRe, self.v) * fen.dx L0Im = fen.dot(cfIm, self.v) * fen.dx b0Re = fen.assemble(L0Re, form_compiler_parameters = parsRe) b0Im = fen.assemble(L0Im, form_compiler_parameters = parsIm) if homogeneized: Ader = self.A(mu, der) b0Re[:] -= np.real(Ader.dot(self.u0BC)) b0Im[:] -= np.imag(Ader.dot(self.u0BC)) DirichletBC0 = fen.DirichletBC(self.V, fenZERO, self.DirichletBoundary) DirichletBC0.apply(b0Re) DirichletBC0.apply(b0Im) b = np.array(b0Re[:] + 1.j * b0Im[:], dtype = np.complex) if homogeneized: self.bsH[der] = b else: self.bs[der] = b if self.verbosity >= 20: verbosityDepth("DEL", "Done assembling forcing term.", timestamp = self.timestamp) return b diff --git a/rrompy/hfengines/linear_problem/laplace_base_problem_engine.py b/rrompy/hfengines/linear_problem/laplace_base_problem_engine.py index bc85bce..3196353 100644 --- a/rrompy/hfengines/linear_problem/laplace_base_problem_engine.py +++ b/rrompy/hfengines/linear_problem/laplace_base_problem_engine.py @@ -1,328 +1,318 @@ # Copyright (C) 2018 by the RROMPy authors # # This file is part of RROMPy. # # RROMPy is free software: you can redistribute it and/or modify # it under the terms of the GNU Lesser General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # RROMPy is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU Lesser General Public License for more details. # # You should have received a copy of the GNU Lesser General Public License # along with RROMPy. If not, see . # import numpy as np import scipy.sparse as scsp import fenics as fen from rrompy.hfengines.base.problem_engine_base import ProblemEngineBase from rrompy.utilities.base.types import Np1D, ScOp -from rrompy.utilities.base.fenics import fenZERO, fenONE +from rrompy.utilities.fenics import fenZERO, fenONE, H1NormMatrix from rrompy.utilities.base import verbosityDepth __all__ = ['LaplaceBaseProblemEngine'] class LaplaceBaseProblemEngine(ProblemEngineBase): """ Solver for generic Laplace problems. - \nabla \cdot (a \nabla u) = f in \Omega u = u0 on \Gamma_D \partial_nu = g1 on \Gamma_N \partial_nu + h u = g2 on \Gamma_R Attributes: verbosity: Verbosity level. BCManager: Boundary condition manager. V: Real FE space. u: Generic trial functions for variational form evaluation. v: Generic test functions for variational form evaluation. As: Scipy sparse array representation (in CSC format) of As. bs: Numpy array representation of bs. bsH: Numpy array representation of homogeneized bs. energyNormMatrix: Scipy sparse matrix representing inner product over V. bsmu: Mu value of last bs evaluation. liftDirichletDatamu: Mu value of last Dirichlet datum evaluation. liftedDirichletDatum: Dofs of Dirichlet datum lifting. mu0BC: Mu value of last Dirichlet datum lifting. degree_threshold: Threshold for ufl expression interpolation degree. omega: Value of omega. diffusivity: Value of a. forcingTerm: Value of f. DirichletDatum: Value of u0. NeumannDatum: Value of g1. RobinDatumG: Value of g2. RobinDatumH: Value of h. DirichletBoundary: Function handle to \Gamma_D. NeumannBoundary: Function handle to \Gamma_N. RobinBoundary: Function handle to \Gamma_R. ds: Boundary measure 2-tuple (resp. for Neumann and Robin boundaries). A0: Scipy sparse array representation (in CSC format) of A0. b0: Numpy array representation of b0. dsToBeSet: Whether ds needs to be set. """ def __init__(self, degree_threshold : int = np.inf, verbosity : int = 10, timestamp : bool = True): super().__init__(degree_threshold = degree_threshold, verbosity = verbosity, timestamp = timestamp) self.omega = 0. self.diffusivity = fenONE self.forcingTerm = fenZERO self.DirichletDatum = fenZERO self.NeumannDatum = fenZERO self.RobinDatumG = fenZERO self.RobinDatumH = fenZERO @property def V(self): """Value of V.""" return self._V @V.setter def V(self, V): ProblemEngineBase.V.fset(self, V) self.dsToBeSet = True @property def diffusivity(self): """Value of a.""" return self._diffusivity @diffusivity.setter def diffusivity(self, diffusivity): self.resetAs() if not isinstance(diffusivity, (list, tuple,)): diffusivity = [diffusivity, fenZERO] self._diffusivity = diffusivity @property def forcingTerm(self): """Value of f.""" return self._forcingTerm @forcingTerm.setter def forcingTerm(self, forcingTerm): self.resetbs() if not isinstance(forcingTerm, (list, tuple,)): forcingTerm = [forcingTerm, fenZERO] self._forcingTerm = forcingTerm @property def DirichletDatum(self): """Value of u0.""" return self._DirichletDatum @DirichletDatum.setter def DirichletDatum(self, DirichletDatum): self.resetbs() if not isinstance(DirichletDatum, (list, tuple,)): DirichletDatum = [DirichletDatum, fenZERO] self._DirichletDatum = DirichletDatum @property def NeumannDatum(self): """Value of g1.""" return self._NeumannDatum @NeumannDatum.setter def NeumannDatum(self, NeumannDatum): self.resetbs() if not isinstance(NeumannDatum, (list, tuple,)): NeumannDatum = [NeumannDatum, fenZERO] self._NeumannDatum = NeumannDatum @property def RobinDatumG(self): """Value of g2.""" return self._RobinDatumG @RobinDatumG.setter def RobinDatumG(self, RobinDatumG): self.resetbs() if not isinstance(RobinDatumG, (list, tuple,)): RobinDatumG = [RobinDatumG, fenZERO] self._RobinDatumG = RobinDatumG @property def RobinDatumH(self): """Value of h.""" return self._RobinDatumH @RobinDatumH.setter def RobinDatumH(self, RobinDatumH): self.resetAs() if not isinstance(RobinDatumH, (list, tuple,)): RobinDatumH = [RobinDatumH, fenZERO] self._RobinDatumH = RobinDatumH @property def DirichletBoundary(self): """Function handle to DirichletBoundary.""" return self.BCManager.DirichletBoundary @DirichletBoundary.setter def DirichletBoundary(self, DirichletBoundary): self.resetAs() self.resetbs() self.BCManager.DirichletBoundary = DirichletBoundary @property def NeumannBoundary(self): """Function handle to NeumannBoundary.""" return self.BCManager.NeumannBoundary @NeumannBoundary.setter def NeumannBoundary(self, NeumannBoundary): self.resetAs() self.resetbs() self.dsToBeSet = True self.BCManager.NeumannBoundary = NeumannBoundary @property def RobinBoundary(self): """Function handle to RobinBoundary.""" return self.BCManager.RobinBoundary @RobinBoundary.setter def RobinBoundary(self, RobinBoundary): self.resetAs() self.resetbs() self.dsToBeSet = True self.BCManager.RobinBoundary = RobinBoundary def autoSetDS(self): """Set FEniCS boundary measure based on boundary function handles.""" if self.dsToBeSet: if self.verbosity >= 20: verbosityDepth("INIT", "Initializing boundary measures.", timestamp = self.timestamp) mesh = self.V.mesh() NB = self.NeumannBoundary RB = self.RobinBoundary boundary_markers = fen.MeshFunction("size_t", mesh, mesh.topology().dim() - 1) NB.mark(boundary_markers, 0) RB.mark(boundary_markers, 1) self.ds = fen.Measure("ds", domain = mesh, subdomain_data = boundary_markers) self.dsToBeSet = False if self.verbosity >= 20: verbosityDepth("DEL", "Done initializing boundary measures.", timestamp = self.timestamp) - def buildEnergyNormForm(self): # H1_omega + def buildEnergyNormForm(self): """ Build sparse matrix (in CSR format) representative of scalar product. """ - if self.verbosity >= 20: - verbosityDepth("INIT", "Assembling energy matrix.", - timestamp = self.timestamp) - normMatFen = fen.assemble((fen.dot(fen.grad(self.u), fen.grad(self.v)) - + np.abs(self.omega)**2 * fen.dot(self.u, self.v)) *fen.dx) - normMat = fen.as_backend_type(normMatFen).mat() - self.energyNormMatrix = scsp.csr_matrix(normMat.getValuesCSR()[::-1], - shape = normMat.size) - if self.verbosity >= 20: - verbosityDepth("DEL", "Done assembling energy matrix.", - timestamp = self.timestamp) + self.energyNormMatrix = H1NormMatrix(self.V, np.abs(self.omega)**2) def A(self, mu:complex, der : int = 0) -> ScOp: """Assemble (derivative of) operator of linear system.""" Anull = self.checkAInBounds(der) if Anull is not None: return Anull self.autoSetDS() if self.As[0] is None: if self.verbosity >= 20: verbosityDepth("INIT", "Assembling operator term A0.", timestamp = self.timestamp) DirichletBC0 = fen.DirichletBC(self.V, fenZERO, self.DirichletBoundary) aRe, aIm = self.diffusivity hRe, hIm = self.RobinDatumH termNames = ["diffusivity", "RobinDatumH"] parsRe = self.iterReduceQuadratureDegree(zip( [aRe, hRe], [x + "Real" for x in termNames])) parsIm = self.iterReduceQuadratureDegree(zip( [aIm, hIm], [x + "Imag" for x in termNames])) a0Re = (aRe * fen.dot(fen.grad(self.u), fen.grad(self.v)) * fen.dx + hRe * fen.dot(self.u, self.v) * self.ds(1)) a0Im = (aIm * fen.dot(fen.grad(self.u), fen.grad(self.v)) * fen.dx + hIm * fen.dot(self.u, self.v) * self.ds(1)) A0Re = fen.assemble(a0Re, form_compiler_parameters = parsRe) A0Im = fen.assemble(a0Im, form_compiler_parameters = parsIm) DirichletBC0.apply(A0Re) DirichletBC0.zero(A0Im) A0ReMat = fen.as_backend_type(A0Re).mat() A0ImMat = fen.as_backend_type(A0Im).mat() A0Rer, A0Rec, A0Rev = A0ReMat.getValuesCSR() A0Imr, A0Imc, A0Imv = A0ImMat.getValuesCSR() self.As[0] = (scsp.csr_matrix((A0Rev, A0Rec, A0Rer), shape = A0ReMat.size) + 1.j * scsp.csr_matrix((A0Imv, A0Imc, A0Imr), shape = A0ImMat.size)) if self.verbosity >= 20: verbosityDepth("DEL", "Done assembling operator term.", timestamp = self.timestamp) return self.As[0] def b(self, mu:complex, der : int = 0, homogeneized : bool = False) -> Np1D: """Assemble (derivative of) RHS of linear system.""" bnull = self.checkbInBounds(der, homogeneized) if bnull is not None: return bnull if homogeneized and not np.isclose(self.mu0BC, mu): self.u0BC = self.liftDirichletData(mu) if (max(self.nbs, self.nAs * homogeneized) > 1 and not np.isclose(self.bsmu, mu)): self.bsmu = mu self.resetbs() b = self.bsH[der] if homogeneized else self.bs[der] if b is None: self.autoSetDS() if self.verbosity >= 20: verbosityDepth("INIT", ("Assembling forcing term " "b{}.").format(der), timestamp = self.timestamp) if der == 0: fRe, fIm = self.forcingTerm g1Re, g1Im = self.NeumannDatum g2Re, g2Im = self.RobinDatumG else: fRe, fIm = fenZERO, fenZERO g1Re, g1Im = fenZERO, fenZERO g2Re, g2Im = fenZERO, fenZERO termNames = ["forcingTerm", "NeumannDatum", "RobinDatumG"] parsRe = self.iterReduceQuadratureDegree(zip( [fRe, g1Re, g2Re], [x + "Real" for x in termNames])) parsIm = self.iterReduceQuadratureDegree(zip( [fIm, g1Im, g2Im], [x + "Imag" for x in termNames])) L0Re = (fen.dot(fRe, self.v) * fen.dx + fen.dot(g1Re, self.v) * self.ds(0) + fen.dot(g2Re, self.v) * self.ds(1)) L0Im = (fen.dot(fIm, self.v) * fen.dx + fen.dot(g1Im, self.v) * self.ds(0) + fen.dot(g2Im, self.v) * self.ds(1)) b0Re = fen.assemble(L0Re, form_compiler_parameters = parsRe) b0Im = fen.assemble(L0Im, form_compiler_parameters = parsIm) if homogeneized: Ader = self.A(mu, der) b0Re[:] -= np.real(Ader.dot(self.u0BC)) b0Im[:] -= np.imag(Ader.dot(self.u0BC)) DBCR = fen.DirichletBC(self.V, fenZERO, self.DirichletBoundary) DBCI = fen.DirichletBC(self.V, fenZERO, self.DirichletBoundary) else: DBCR = fen.DirichletBC(self.V, self.DirichletDatum[0], self.DirichletBoundary) DBCI = fen.DirichletBC(self.V, self.DirichletDatum[1], self.DirichletBoundary) DBCR.apply(b0Re) DBCI.apply(b0Im) b = np.array(b0Re[:] + 1.j * b0Im[:], dtype = np.complex) if homogeneized: self.bsH[der] = b else: self.bs[der] = b if self.verbosity >= 20: verbosityDepth("DEL", "Done assembling forcing term.", timestamp = self.timestamp) return b diff --git a/rrompy/hfengines/linear_problem/laplace_disk_gaussian.py b/rrompy/hfengines/linear_problem/laplace_disk_gaussian.py index ce0d9cd..e65f308 100644 --- a/rrompy/hfengines/linear_problem/laplace_disk_gaussian.py +++ b/rrompy/hfengines/linear_problem/laplace_disk_gaussian.py @@ -1,158 +1,158 @@ # Copyright (C) 2018 by the RROMPy authors # # This file is part of RROMPy. # # RROMPy is free software: you can redistribute it and/or modify # it under the terms of the GNU Lesser General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # RROMPy is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU Lesser General Public License for more details. # # You should have received a copy of the GNU Lesser General Public License # along with RROMPy. If not, see . # import numpy as np import fenics as fen from rrompy.utilities.base.types import Np1D, Tuple, FenExpr from .laplace_base_problem_engine import LaplaceBaseProblemEngine -from rrompy.utilities.base.fenics import fenZERO, fenONE +from rrompy.utilities.fenics import fenZERO, fenONE from rrompy.utilities.base import verbosityDepth __all__ = ['LaplaceDiskGaussian'] class LaplaceDiskGaussian(LaplaceBaseProblemEngine): """ Solver for disk Laplace problems with parametric forcing term center. - \Delta u = C exp(-.5 * ||\cdot - (mu, 0)||^2) in \Omega = B(0, 5) u = 0 on \partial\Omega. """ nbs = 20 def __init__(self, n:int, degree_threshold : int = np.inf, verbosity : int = 10, timestamp : bool = True): super().__init__(degree_threshold = degree_threshold, verbosity = verbosity, timestamp = timestamp) self.computebsFactors() self.forcingTermMu = np.nan import mshr mesh = mshr.generate_mesh(mshr.Circle(fen.Point(0., 0.), 5.), n) self.V = fen.FunctionSpace(mesh, "P", 3) def getForcingTerm(self, mu:complex) -> Tuple[FenExpr, FenExpr]: """Compute forcing term.""" if not np.isclose(mu, self.forcingTermMu): if self.verbosity >= 25: verbosityDepth("INIT", ("Assembling base expression for " "forcing term."), timestamp = self.timestamp) x, y = fen.SpatialCoordinate(self.V.mesh())[:] C = np.exp(-.5 * mu ** 2.) CR, CI = np.real(C), np.imag(C) f0 = (2 * np.pi) ** -.5 * fen.exp(-.5 * (x ** 2. + y ** 2.)) muR, muI = np.real(mu), np.imag(mu) f1R = fen.exp(muR * x) * fen.cos(muI * x) f1I = fen.exp(muR * x) * fen.sin(muI * x) self.forcingTerm = [f0 * (CR * f1R - CI * f1I), f0 * (CR * f1I + CI * f1R)] self.forcingTermMu = mu if self.verbosity >= 25: verbosityDepth("DEL", "Done assembling base expression.", timestamp = self.timestamp) return self.forcingTerm def computebsFactors(self): self.bsFactors = np.zeros((self.nbs, self.nbs), dtype = float) self.bsFactors[0, 0] = 1. self.bsFactors[1, 1] = 1. for j in range(2, self.nbs): l = (j + 1) % 2 + 1 J = np.arange(l, j + 1, 2) self.bsFactors[j, J] = self.bsFactors[j - 1, J - 1] if l == 2: l = 0 J = np.arange(l, j, 2) self.bsFactors[j, J] += np.multiply(- 1 - J, self.bsFactors[j - 1, J + 1]) self.bsFactors[j, l : j + 2 : 2] /= j def getExtraFactorB(self, mu:complex, der:int) -> Tuple[FenExpr, FenExpr]: """Compute extra expression in RHS.""" if self.verbosity >= 25: verbosityDepth("INIT", ("Assembling auxiliary expression for " "forcing term derivative."), timestamp = self.timestamp) muR, muI = np.real(mu), np.imag(mu) x = fen.SpatialCoordinate(self.V.mesh())[0] l = der % 2 if l == 0: powR, powI = fenONE, fenZERO else: powR, powI = x - muR, fen.Constant(muI) exprR, exprI = [self.bsFactors[der, l] * k for k in [powR, powI]] for j in range(l + 2, der + 1, 2): for _ in range(2): powR, powI = (powR * (x - muR) - powI * muI, powR * muI + powI * (x - muR)) exprR += self.bsFactors[der, j] * powR exprI += self.bsFactors[der, j] * powI if self.verbosity >= 25: verbosityDepth("DEL", "Done assembling auxiliary expression.", timestamp = self.timestamp) return[exprR, exprI] def b(self, mu:complex, der : int = 0, homogeneized : bool = False) -> Np1D: """Assemble (derivative of) RHS of linear system.""" bnull = self.checkbInBounds(der, homogeneized) if bnull is not None: return bnull if homogeneized and not np.isclose(self.mu0BC, mu): self.u0BC = self.liftDirichletData(mu) if not np.isclose(self.bsmu, mu): self.bsmu = mu self.resetbs() b = self.bsH[der] if homogeneized else self.bs[der] if b is None: if self.verbosity >= 20: verbosityDepth("INIT", "Assembling forcing term b{}.".format( der), timestamp = self.timestamp) if der < self.nbs: fRe, fIm = self.getForcingTerm(mu) cRe, cIm = self.getExtraFactorB(mu, der) cfRe = cRe * fRe - cIm * fIm cfIm = cRe * fIm + cIm * fRe else: cfRe, cfIm = fenZERO, fenZERO parsRe = self.iterReduceQuadratureDegree(zip([cfRe], ["forcingTermDer{}Real".format(der)])) parsIm = self.iterReduceQuadratureDegree(zip([cfIm], ["forcingTermDer{}Imag".format(der)])) L0Re = fen.dot(cfRe, self.v) * fen.dx L0Im = fen.dot(cfIm, self.v) * fen.dx b0Re = fen.assemble(L0Re, form_compiler_parameters = parsRe) b0Im = fen.assemble(L0Im, form_compiler_parameters = parsIm) if homogeneized: Ader = self.A(mu, der) b0Re[:] -= np.real(Ader.dot(self.u0BC)) b0Im[:] -= np.imag(Ader.dot(self.u0BC)) DirichletBC0 = fen.DirichletBC(self.V, fenZERO, self.DirichletBoundary) DirichletBC0.apply(b0Re) DirichletBC0.apply(b0Im) b = np.array(b0Re[:] + 1.j * b0Im[:], dtype = np.complex) if homogeneized: self.bsH[der] = b else: self.bs[der] = b if self.verbosity >= 20: verbosityDepth("DEL", "Done assembling forcing term.", timestamp = self.timestamp) return b diff --git a/rrompy/hfengines/linear_problem/scattering_problem_engine.py b/rrompy/hfengines/linear_problem/scattering_problem_engine.py index afd8413..88f82d4 100644 --- a/rrompy/hfengines/linear_problem/scattering_problem_engine.py +++ b/rrompy/hfengines/linear_problem/scattering_problem_engine.py @@ -1,177 +1,177 @@ # Copyright (C) 2018 by the RROMPy authors # # This file is part of RROMPy. # # RROMPy is free software: you can redistribute it and/or modify # it under the terms of the GNU Lesser General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # RROMPy is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU Lesser General Public License for more details. # # You should have received a copy of the GNU Lesser General Public License # along with RROMPy. If not, see . # from numpy import inf import scipy.sparse as scsp import fenics as fen from rrompy.utilities.base.types import ScOp -from rrompy.utilities.base.fenics import fenZERO +from rrompy.utilities.fenics import fenZERO from rrompy.utilities.base import verbosityDepth from .helmholtz_problem_engine import HelmholtzProblemEngine from rrompy.utilities.exception_manager import RROMPyWarning __all__ = ['ScatteringProblemEngine'] class ScatteringProblemEngine(HelmholtzProblemEngine): """ Solver for scattering problems with parametric wavenumber. - \nabla \cdot (a \nabla u) - omega^2 * n**2 * u = f in \Omega u = u0 on \Gamma_D \partial_nu = g1 on \Gamma_N \partial_nu +- i omega u = g2 on \Gamma_R Attributes: verbosity: Verbosity level. BCManager: Boundary condition manager. V: Real FE space. u: Generic trial functions for variational form evaluation. v: Generic test functions for variational form evaluation. As: Scipy sparse array representation (in CSC format) of As. bs: Numpy array representation of bs. bsH: Numpy array representation of homogeneized bs. energyNormMatrix: Scipy sparse matrix representing inner product over V. bsmu: Mu value of last bs evaluation. liftDirichletDatamu: Mu value of last Dirichlet datum evaluation. liftedDirichletDatum: Dofs of Dirichlet datum lifting. mu0BC: Mu value of last Dirichlet datum lifting. degree_threshold: Threshold for ufl expression interpolation degree. signR: Sign in ABC. omega: Value of omega. diffusivity: Value of a. forcingTerm: Value of f. DirichletDatum: Value of u0. NeumannDatum: Value of g1. RobinDatumG: Value of g2. DirichletBoundary: Function handle to \Gamma_D. NeumannBoundary: Function handle to \Gamma_N. RobinBoundary: Function handle to \Gamma_R. ds: Boundary measure 2-tuple (resp. for Neumann and Robin boundaries). A0: Scipy sparse array representation (in CSC format) of A0. A1: Scipy sparse array representation (in CSC format) of A1. A2: Scipy sparse array representation (in CSC format) of A2. b0: Numpy array representation of b0. dsToBeSet: Whether ds needs to be set. """ nAs = 3 rescalingExp = 1. signR = - 1. def __init__(self, degree_threshold : int = inf, verbosity : int = 10, timestamp : bool = True): self.silenceWarnings = True super().__init__(degree_threshold = degree_threshold, verbosity = verbosity, timestamp = timestamp) del self.silenceWarnings @property def RobinDatumH(self): """Value of h.""" return self.signR * self.omega @RobinDatumH.setter def RobinDatumH(self, RobinDatumH): if not hasattr(self, "silenceWarnings"): RROMPyWarning(("Scattering problems do not allow changes of h. " "Ignoring assignment.")) return def A(self, mu:complex, der : int = 0) -> ScOp: """Assemble (derivative of) operator of linear system.""" Anull = self.checkAInBounds(der) if Anull is not None: return Anull self.autoSetDS() if der <= 0 and self.As[0] is None: if self.verbosity >= 20: verbosityDepth("INIT", "Assembling operator term A0.", timestamp = self.timestamp) DirichletBC0 = fen.DirichletBC(self.V, fenZERO, self.DirichletBoundary) aRe, aIm = self.diffusivity parsRe = self.iterReduceQuadratureDegree(zip([aRe], ["diffusivityReal"])) parsIm = self.iterReduceQuadratureDegree(zip([aIm], ["diffusivityImag"])) a0Re = aRe * fen.dot(fen.grad(self.u), fen.grad(self.v)) * fen.dx a0Im = aIm * fen.dot(fen.grad(self.u), fen.grad(self.v)) * fen.dx A0Re = fen.assemble(a0Re, form_compiler_parameters = parsRe) A0Im = fen.assemble(a0Im, form_compiler_parameters = parsIm) DirichletBC0.apply(A0Re) DirichletBC0.zero(A0Im) A0ReMat = fen.as_backend_type(A0Re).mat() A0ImMat = fen.as_backend_type(A0Im).mat() A0Rer, A0Rec, A0Rev = A0ReMat.getValuesCSR() A0Imr, A0Imc, A0Imv = A0ImMat.getValuesCSR() self.As[0] = (scsp.csr_matrix((A0Rev, A0Rec, A0Rer), shape = A0ReMat.size) + 1.j * scsp.csr_matrix((A0Imv, A0Imc, A0Imr), shape = A0ImMat.size)) if self.verbosity >= 20: verbosityDepth("DEL", "Done assembling operator term.", timestamp = self.timestamp) if der <= 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) a1 = fen.dot(self.u, self.v) * self.ds(1) A1 = fen.assemble(a1) DirichletBC0.zero(A1) A1Mat = fen.as_backend_type(A1).mat() A1r, A1c, A1v = A1Mat.getValuesCSR() self.As[1] = self.signR * 1.j * scsp.csr_matrix((A1v, A1c, A1r), shape = A1Mat.size) if self.verbosity >= 20: verbosityDepth("DEL", "Done assembling operator term.", timestamp = self.timestamp) if der <= 2 and self.As[2] is None: if self.verbosity >= 20: verbosityDepth("INIT", "Assembling operator term A2.", timestamp = self.timestamp) DirichletBC0 = fen.DirichletBC(self.V, fenZERO, self.DirichletBoundary) nRe, nIm = self.refractionIndex n2Re, n2Im = nRe * nRe - nIm * nIm, 2 * nRe * nIm parsRe = self.iterReduceQuadratureDegree(zip([n2Re], ["refractionIndexSquaredReal"])) parsIm = self.iterReduceQuadratureDegree(zip([n2Im], ["refractionIndexSquaredImag"])) a2Re = - n2Re * fen.dot(self.u, self.v) * fen.dx a2Im = - n2Im * fen.dot(self.u, self.v) * fen.dx A2Re = fen.assemble(a2Re, form_compiler_parameters = parsRe) A2Im = fen.assemble(a2Im, form_compiler_parameters = parsIm) DirichletBC0.zero(A2Re) DirichletBC0.zero(A2Im) A2ReMat = fen.as_backend_type(A2Re).mat() A2ImMat = fen.as_backend_type(A2Im).mat() A2Rer, A2Rec, A2Rev = A2ReMat.getValuesCSR() A2Imr, A2Imc, A2Imv = A2ImMat.getValuesCSR() self.As[2] = (scsp.csr_matrix((A2Rev, A2Rec, A2Rer), shape = A2ReMat.size) + 1.j * scsp.csr_matrix((A2Imv, A2Imc, A2Imr), shape = A2ImMat.size)) if self.verbosity >= 20: verbosityDepth("DEL", "Done assembling operator term.", timestamp = self.timestamp) if der == 0: return self.As[0] + mu * self.As[1] + mu**2. * self.As[2] if der == 1: return self.As[1] + 2 * mu * self.As[2] return self.As[2] diff --git a/rrompy/hfengines/vector_linear_problem/linear_elasticity_beam_poisson_ratio.py b/rrompy/hfengines/vector_linear_problem/linear_elasticity_beam_poisson_ratio.py index 1c0ba54..de82104 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,151 +1,151 @@ # Copyright (C) 2018 by the RROMPy authors # # This file is part of RROMPy. # # RROMPy is free software: you can redistribute it and/or modify # it under the terms of the GNU Lesser General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # RROMPy is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU Lesser General Public License for more details. # # You should have received a copy of the GNU Lesser General Public License # along with RROMPy. If not, see . # import numpy as np import scipy.sparse as scsp import fenics as fen from .linear_elasticity_problem_engine import LinearElasticityProblemEngine -from rrompy.utilities.base.fenics import fenZEROS +from rrompy.utilities.fenics import fenZEROS from rrompy.utilities.base.types import Np1D, ScOp from rrompy.utilities.base import verbosityDepth __all__ = ['LinearElasticityBeamPoissonRatio'] class LinearElasticityBeamPoissonRatio(LinearElasticityProblemEngine): """ Solver for linear elasticity problem of a beam subject to its own weight, with parametric Poisson's ratio. - div(lambda_ * div(u) * I + 2 * mu_ * epsilon(u)) = rho_ * g in \Omega u = 0 on \Gamma_D \partial_nu = 0 on \Gamma_N """ nAs = 2 nbs = 3 def __init__(self, n:int, rho_:float, g:float, E:float, nu0:float, length:float, degree_threshold : int = np.inf, verbosity : int = 10, timestamp : bool = True): super().__init__(degree_threshold = degree_threshold, verbosity = verbosity, timestamp = timestamp) self.lambda_ = E * nu0 / (1. + nu0) / (1. - 2 * nu0) self.mu_ = E / (1. + nu0) mesh = fen.RectangleMesh(fen.Point(0., 0.), fen.Point(length, 1), n, max(int(n / length), 1)) self.V = fen.VectorFunctionSpace(mesh, "P", 1) self.forcingTerm = [fen.Constant((0., - rho_ * g / E)), fenZEROS(2)] self.DirichletBoundary = lambda x, on_b: on_b and fen.near(x[0], 0.) self.NeumannBoundary = "REST" def A(self, mu:complex, der : int = 0) -> ScOp: """Assemble (derivative of) operator of linear system.""" Anull = self.checkAInBounds(der) if Anull is not None: return Anull self.autoSetDS() if der <= 1 and self.As[0] is None: if self.verbosity >= 20: verbosityDepth("INIT", "Assembling operator term A0.", timestamp = self.timestamp) DirichletBC0 = fen.DirichletBC(self.V, fenZEROS(2), self.DirichletBoundary) epsilon = lambda u: .5 * (fen.grad(u) + fen.nabla_grad(u)) a0Re = 2 * fen.inner(epsilon(self.u), epsilon(self.v)) * fen.dx A0Re = fen.assemble(a0Re) DirichletBC0.apply(A0Re) A0ReMat = fen.as_backend_type(A0Re).mat() A0Rer, A0Rec, A0Rev = A0ReMat.getValuesCSR() self.As[0] = scsp.csr_matrix((A0Rev, A0Rec, A0Rer), shape = A0ReMat.size, dtype = np.complex) if self.verbosity >= 20: verbosityDepth("DEL", "Done assembling operator term.", timestamp = self.timestamp) if der <= 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 = fen.div(self.u) * fen.div(self.v) * fen.dx A1Re = fen.assemble(a1Re) DirichletBC0.apply(A1Re) A1ReMat = fen.as_backend_type(A1Re).mat() A1Rer, A1Rec, A1Rev = A1ReMat.getValuesCSR() self.As[1] = (scsp.csr_matrix((A1Rev, A1Rec, A1Rer), shape = A1ReMat.size, dtype = np.complex) - 2. * self.As[0]) if self.verbosity >= 20: verbosityDepth("DEL", "Done assembling operator term.", timestamp = self.timestamp) if der == 0: return self.As[0] + mu * self.As[1] return self.As[1] def b(self, mu:complex, der : int = 0, homogeneized : bool = False) -> Np1D: """Assemble (derivative of) RHS of linear system.""" assert homogeneized == False bnull = self.checkbInBounds(der) if bnull is not None: return bnull if (self.nbs > 1 and not np.isclose(self.bsmu, mu)): self.bsmu = mu self.resetbs() b = self.bsH[der] if homogeneized else self.bs[der] if b is None: self.autoSetDS() if self.verbosity >= 20: verbosityDepth("INIT", ("Assembling forcing term " "b{}.").format(der), timestamp = self.timestamp) if self.bs[0] is None and der > 0: self.b(mu, 0) if der == 0: fRe, fIm = self.forcingTerm parsRe = self.iterReduceQuadratureDegree(zip( [fRe], ["forcingTermReal"])) parsIm = self.iterReduceQuadratureDegree(zip( [fIm], ["forcingTermImag"])) L0Re = fen.inner(fRe, self.v) * fen.dx L0Im = fen.inner(fIm, self.v) * fen.dx b0Re = fen.assemble(L0Re, form_compiler_parameters = parsRe) b0Im = fen.assemble(L0Im, form_compiler_parameters = parsIm) DBCR = fen.DirichletBC(self.V, self.DirichletDatum[0], self.DirichletBoundary) DBCI = fen.DirichletBC(self.V, self.DirichletDatum[1], self.DirichletBoundary) DBCR.apply(b0Re) DBCI.apply(b0Im) b = (1. - mu - 2 * mu ** 2) * np.array(b0Re[:] + 1.j * b0Im[:], dtype = np.complex) elif der == 1: b = (- 1. - 4 * mu) / (1. - mu - 2 * mu ** 2) * self.bs[0] elif der == 2: b = - 2. / (1. - mu - 2 * mu ** 2) * self.bs[0] if homogeneized: self.bsH[der] = b else: self.bs[der] = b if self.verbosity >= 20: verbosityDepth("DEL", "Done assembling forcing term.", timestamp = self.timestamp) return b diff --git a/rrompy/hfengines/vector_linear_problem/linear_elasticity_helmholtz_archway_frequency.py b/rrompy/hfengines/vector_linear_problem/linear_elasticity_helmholtz_archway_frequency.py index 8939113..a143faa 100644 --- a/rrompy/hfengines/vector_linear_problem/linear_elasticity_helmholtz_archway_frequency.py +++ b/rrompy/hfengines/vector_linear_problem/linear_elasticity_helmholtz_archway_frequency.py @@ -1,67 +1,67 @@ # Copyright (C) 2018 by the RROMPy authors # # This file is part of RROMPy. # # RROMPy is free software: you can redistribute it and/or modify # it under the terms of the GNU Lesser General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # RROMPy is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU Lesser General Public License for more details. # # You should have received a copy of the GNU Lesser General Public License # along with RROMPy. If not, see . # import numpy as np import fenics as fen from .linear_elasticity_helmholtz_problem_engine import \ LinearElasticityHelmholtzProblemEngine -from rrompy.utilities.base.fenics import fenZEROS +from rrompy.utilities.fenics import fenZEROS __all__ = ['LinearElasticityHelmholtzArchwayFrequency'] class LinearElasticityHelmholtzArchwayFrequency( LinearElasticityHelmholtzProblemEngine): """ Solver for archway linear elasticity Helmholtz problem with parametric wavenumber. - div(lambda_ * div(u) * I + 2 * mu_ * epsilon(u)) - rho_ * omega^2 * u = rho_ * g / omega in \Omega u = 0 on \Gamma_D \partial_nu = 0 on \Gamma_N """ def __init__(self, kappa:float, n:int, rho_:float, T:float, lambda_:float, mu_:float, R:float, r:float, degree_threshold : int = np.inf, verbosity : int = 10, timestamp : bool = True): super().__init__(degree_threshold = degree_threshold, verbosity = verbosity, timestamp = timestamp) self.omega = kappa self.lambda_ = lambda_ self.mu_ = mu_ self.rho_ = rho_ import mshr domain = (mshr.Circle(fen.Point(0, 0), R) - mshr.Circle(fen.Point(0, 0), r) - mshr.Rectangle(fen.Point(-1.05*R, -1.05*R), fen.Point(1.05*R, 0))) mesh = mshr.generate_mesh(domain, n) self.V = fen.VectorFunctionSpace(mesh, "P", 1) import ufl x, y = fen.SpatialCoordinate(mesh)[:] NeumannNonZero = ufl.And(ufl.gt(y, r), ufl.And(ufl.ge(x, -.25 * R), ufl.le(x, .25 * R))) self.NeumannDatum = [ufl.as_vector((0., ufl.conditional(NeumannNonZero, fen.Constant(T), 0.))), fenZEROS(2)] self.DirichletBoundary = lambda x, on_b: on_b and fen.near(x[1], 0.) self.NeumannBoundary = "REST" diff --git a/rrompy/hfengines/vector_linear_problem/linear_elasticity_helmholtz_problem_engine.py b/rrompy/hfengines/vector_linear_problem/linear_elasticity_helmholtz_problem_engine.py index 87bb44e..a31d6ec 100644 --- a/rrompy/hfengines/vector_linear_problem/linear_elasticity_helmholtz_problem_engine.py +++ b/rrompy/hfengines/vector_linear_problem/linear_elasticity_helmholtz_problem_engine.py @@ -1,201 +1,183 @@ # Copyright (C) 2018 by the RROMPy authors # # This file is part of RROMPy. # # RROMPy is free software: you can redistribute it and/or modify # it under the terms of the GNU Lesser General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # RROMPy is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU Lesser General Public License for more details. # # You should have received a copy of the GNU Lesser General Public License # along with RROMPy. If not, see . # import numpy as np from scipy.sparse import csr_matrix import fenics as fen from .linear_elasticity_problem_engine import LinearElasticityProblemEngine from rrompy.utilities.base.types import ScOp -from rrompy.utilities.base.fenics import fenZERO, fenZEROS, fenONE +from rrompy.utilities.fenics import (fenZERO, fenZEROS, fenONE, + elasticNormMatrix) from rrompy.utilities.base import verbosityDepth __all__ = ['LinearElasticityHelmholtzProblemEngine'] class LinearElasticityHelmholtzProblemEngine(LinearElasticityProblemEngine): """ Solver for generic linear elasticity Helmholtz problems with parametric wavenumber. - div(lambda_ * div(u) * I + 2 * mu_ * epsilon(u)) - rho_ * mu^2 * u = f in \Omega u = u0 on \Gamma_D \partial_nu = g1 on \Gamma_N \partial_nu + h u = g2 on \Gamma_R Attributes: verbosity: Verbosity level. BCManager: Boundary condition manager. V: Real vector FE space. u: Generic vector trial functions for variational form evaluation. v: Generic vector test functions for variational form evaluation. As: Scipy sparse array representation (in CSC format) of As. bs: Numpy array representation of bs. energyNormMatrix: Scipy sparse matrix representing inner product over V. bsmu: Mu value of last bs evaluation. liftDirichletDatamu: Mu value of last Dirichlet datum evaluation. liftedDirichletDatum: Dofs of Dirichlet datum lifting. mu0BC: Mu value of last Dirichlet datum lifting. degree_threshold: Threshold for ufl expression interpolation degree. omega: Value of omega. lambda_: Value of lambda_. mu_: Value of mu_. forcingTerm: Value of f. DirichletDatum: Value of u0. NeumannDatum: Value of g1. RobinDatumG: Value of g2. RobinDatumH: Value of h. DirichletBoundary: Function handle to \Gamma_D. NeumannBoundary: Function handle to \Gamma_N. RobinBoundary: Function handle to \Gamma_R. ds: Boundary measure 2-tuple (resp. for Neumann and Robin boundaries). A0: Scipy sparse array representation (in CSC format) of A0. A1: Scipy sparse array representation (in CSC format) of A1. b0: Numpy array representation of b0. dsToBeSet: Whether ds needs to be set. """ nAs = 2 rescalingExp = 2. def __init__(self, degree_threshold : int = np.inf, verbosity : int = 10, timestamp : bool = True): super().__init__(degree_threshold = degree_threshold, verbosity = verbosity, timestamp = timestamp) self.omega = 1. self.rho_ = fenONE @property def rho_(self): """Value of rho_.""" return self._rho_ @rho_.setter def rho_(self, rho_): self.resetAs() if not isinstance(rho_, (list, tuple,)): rho_ = [rho_, fenZERO] self._rho_ = rho_ def buildEnergyNormForm(self): # energy + omega norm """ Build sparse matrix (in CSR format) representative of scalar product. """ - if self.verbosity >= 20: - verbosityDepth("INIT", "Assembling energy matrix.", - timestamp = self.timestamp) - l_Re, _ = self.lambda_ - m_Re, _ = self.mu_ + lambda_Re, _ = self.lambda_ + mu_Re, _ = self.mu_ r_Re, _ = self.rho_ - termNames = ["lambda_", "mu_", "rho_"] - pars = self.iterReduceQuadratureDegree(zip( - [l_Re, m_Re, r_Re], - [x + "Real" for x in termNames])) - epsilon = lambda u: 0.5 * (fen.grad(u) + fen.nabla_grad(u)) - sigma = lambda u, l_, m_: ( - l_ * fen.div(u) * fen.Identity(u.geometric_dimension()) - + 2. * m_ * epsilon(u)) - normMatFen = (fen.inner(sigma(self.u, l_Re, m_Re), epsilon(self.v)) - + np.abs(self.omega)**2 * r_Re * fen.inner(self.u, self.v) - ) * fen.dx - normMatFen = fen.assemble(normMatFen, form_compiler_parameters = pars) - normMat = fen.as_backend_type(normMatFen).mat() - self.energyNormMatrix = csr_matrix(normMat.getValuesCSR()[::-1], - shape = normMat.size) - if self.verbosity >= 20: - verbosityDepth("DEL", "Done assembling energy matrix.", - timestamp = self.timestamp) + self.energyNormMatrix = elasticNormMatrix(self.V, lambda_Re, mu_Re, + np.abs(self.omega)**2 * r_Re) def A(self, mu:complex, der : int = 0) -> ScOp: """Assemble (derivative of) operator of linear system.""" Anull = self.checkAInBounds(der) if Anull is not None: return Anull self.autoSetDS() if der <= 0 and self.As[0] is None: if self.verbosity >= 20: verbosityDepth("INIT", "Assembling operator term A0.", timestamp = self.timestamp) DirichletBC0 = fen.DirichletBC(self.V, fenZEROS(self.V.mesh().topology().dim()), self.DirichletBoundary) lambda_Re, lambda_Im = self.lambda_ mu_Re, mu_Im = self.mu_ hRe, hIm = self.RobinDatumH termNames = ["lambda_", "mu_", "RobinDatumH"] parsRe = self.iterReduceQuadratureDegree(zip( [lambda_Re, mu_Re, hRe], [x + "Real" for x in termNames])) parsIm = self.iterReduceQuadratureDegree(zip( [lambda_Im, mu_Re, hIm], [x + "Imag" for x in termNames])) epsilon = lambda u: 0.5 * (fen.grad(u) + fen.nabla_grad(u)) sigma = lambda u, l_, m_: ( l_ * fen.div(u) * fen.Identity(u.geometric_dimension()) + 2. * m_ * epsilon(u)) a0Re = (fen.inner(sigma(self.u, lambda_Re, mu_Re), epsilon(self.v)) * fen.dx + hRe * fen.inner(self.u, self.v) * self.ds(1)) a0Im = (fen.inner(sigma(self.u, lambda_Im, mu_Im), epsilon(self.v)) * fen.dx + hIm * fen.inner(self.u, self.v) * self.ds(1)) A0Re = fen.assemble(a0Re, form_compiler_parameters = parsRe) A0Im = fen.assemble(a0Im, form_compiler_parameters = parsIm) DirichletBC0.apply(A0Re) DirichletBC0.zero(A0Im) A0ReMat = fen.as_backend_type(A0Re).mat() A0ImMat = fen.as_backend_type(A0Im).mat() A0Rer, A0Rec, A0Rev = A0ReMat.getValuesCSR() A0Imr, A0Imc, A0Imv = A0ImMat.getValuesCSR() self.As[0] = (csr_matrix((A0Rev, A0Rec, A0Rer), shape = A0ReMat.size) + 1.j * csr_matrix((A0Imv, A0Imc, A0Imr), shape = A0ImMat.size)) if self.verbosity >= 20: verbosityDepth("DEL", "Done assembling operator term.", timestamp = self.timestamp) if der <= 1 and self.As[1] is None: if self.verbosity >= 20: verbosityDepth("INIT", "Assembling operator term A1.", timestamp = self.timestamp) DirichletBC0 = fen.DirichletBC(self.V, fenZEROS(self.V.mesh().topology().dim()), self.DirichletBoundary) rho_Re, rho_Im = self.rho_ parsRe = self.iterReduceQuadratureDegree(zip([rho_Re], ["rho_Real"])) parsIm = self.iterReduceQuadratureDegree(zip([rho_Im], ["rho_Imag"])) a1Re = - rho_Re * fen.inner(self.u, self.v) * fen.dx a1Im = - rho_Im * fen.inner(self.u, self.v) * fen.dx A1Re = fen.assemble(a1Re, form_compiler_parameters = parsRe) A1Im = fen.assemble(a1Im, form_compiler_parameters = parsIm) DirichletBC0.zero(A1Re) DirichletBC0.zero(A1Im) A1ReMat = fen.as_backend_type(A1Re).mat() A1ImMat = fen.as_backend_type(A1Im).mat() A1Rer, A1Rec, A1Rev = A1ReMat.getValuesCSR() A1Imr, A1Imc, A1Imv = A1ImMat.getValuesCSR() self.As[1] = (csr_matrix((A1Rev, A1Rec, A1Rer), shape = A1ReMat.size) + 1.j * csr_matrix((A1Imv, A1Imc, A1Imr), shape = A1ImMat.size)) if self.verbosity >= 20: verbosityDepth("DEL", "Done assembling operator term.", timestamp = self.timestamp) if der == 0: return self.As[0] + mu**2 * self.As[1] return self.As[1] diff --git a/rrompy/hfengines/vector_linear_problem/linear_elasticity_helmholtz_problem_engine_damped.py b/rrompy/hfengines/vector_linear_problem/linear_elasticity_helmholtz_problem_engine_damped.py index 8c1b07d..fcc6bf2 100644 --- a/rrompy/hfengines/vector_linear_problem/linear_elasticity_helmholtz_problem_engine_damped.py +++ b/rrompy/hfengines/vector_linear_problem/linear_elasticity_helmholtz_problem_engine_damped.py @@ -1,209 +1,209 @@ # Copyright (C) 2018 by the RROMPy authors # # This file is part of RROMPy. # # RROMPy is free software: you can redistribute it and/or modify # it under the terms of the GNU Lesser General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # RROMPy is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU Lesser General Public License for more details. # # You should have received a copy of the GNU Lesser General Public License # along with RROMPy. If not, see . # import numpy as np from scipy.sparse import csr_matrix import fenics as fen from .linear_elasticity_helmholtz_problem_engine import \ LinearElasticityHelmholtzProblemEngine from rrompy.utilities.base.types import ScOp -from rrompy.utilities.base.fenics import fenZERO, fenZEROS +from rrompy.utilities.fenics import fenZERO, fenZEROS from rrompy.utilities.base import verbosityDepth __all__ = ['LinearElasticityHelmholtzProblemEngineDamped'] class LinearElasticityHelmholtzProblemEngineDamped( LinearElasticityHelmholtzProblemEngine): """ Solver for generic linear elasticity Helmholtz problems with parametric wavenumber. - div(lambda_ * div(u) * I + 2 * mu_ * epsilon(u)) - rho_ * (mu^2 - i * eta * mu) * u = f in \Omega u = u0 on \Gamma_D \partial_nu = g1 on \Gamma_N \partial_nu + h u = g2 on \Gamma_R Attributes: verbosity: Verbosity level. BCManager: Boundary condition manager. V: Real vector FE space. u: Generic vector trial functions for variational form evaluation. v: Generic vector test functions for variational form evaluation. As: Scipy sparse array representation (in CSC format) of As. bs: Numpy array representation of bs. energyNormMatrix: Scipy sparse matrix representing inner product over V. bsmu: Mu value of last bs evaluation. liftDirichletDatamu: Mu value of last Dirichlet datum evaluation. liftedDirichletDatum: Dofs of Dirichlet datum lifting. mu0BC: Mu value of last Dirichlet datum lifting. degree_threshold: Threshold for ufl expression interpolation degree. omega: Value of omega. lambda_: Value of lambda_. mu_: Value of mu_. eta: Value of eta. forcingTerm: Value of f. DirichletDatum: Value of u0. NeumannDatum: Value of g1. RobinDatumG: Value of g2. RobinDatumH: Value of h. DirichletBoundary: Function handle to \Gamma_D. NeumannBoundary: Function handle to \Gamma_N. RobinBoundary: Function handle to \Gamma_R. ds: Boundary measure 2-tuple (resp. for Neumann and Robin boundaries). A0: Scipy sparse array representation (in CSC format) of A0. A1: Scipy sparse array representation (in CSC format) of A1. b0: Numpy array representation of b0. dsToBeSet: Whether ds needs to be set. """ nAs = 3 rescalingExp = 1. def __init__(self, degree_threshold : int = np.inf, verbosity : int = 10, timestamp : bool = True): super().__init__(degree_threshold = degree_threshold, verbosity = verbosity, timestamp = timestamp) self.eta = fenZERO @property def eta(self): """Value of eta.""" return self._eta @eta.setter def eta(self, eta): self.resetAs() if not isinstance(eta, (list, tuple,)): eta = [eta, fenZERO] self._eta = eta def A(self, mu:complex, der : int = 0) -> ScOp: """Assemble (derivative of) operator of linear system.""" Anull = self.checkAInBounds(der) if Anull is not None: return Anull self.autoSetDS() if der <= 0 and self.As[0] is None: if self.verbosity >= 20: verbosityDepth("INIT", "Assembling operator term A0.", timestamp = self.timestamp) DirichletBC0 = fen.DirichletBC(self.V, fenZEROS(self.V.mesh().topology().dim()), self.DirichletBoundary) lambda_Re, lambda_Im = self.lambda_ mu_Re, mu_Im = self.mu_ hRe, hIm = self.RobinDatumH termNames = ["lambda_", "mu_", "RobinDatumH"] parsRe = self.iterReduceQuadratureDegree(zip( [lambda_Re, mu_Re, hRe], [x + "Real" for x in termNames])) parsIm = self.iterReduceQuadratureDegree(zip( [lambda_Im, mu_Re, hIm], [x + "Imag" for x in termNames])) epsilon = lambda u: 0.5 * (fen.grad(u) + fen.nabla_grad(u)) sigma = lambda u, l_, m_: ( l_ * fen.div(u) * fen.Identity(u.geometric_dimension()) + 2. * m_ * epsilon(u)) a0Re = (fen.inner(sigma(self.u, lambda_Re, mu_Re), epsilon(self.v)) * fen.dx + hRe * fen.inner(self.u, self.v) * self.ds(1)) a0Im = (fen.inner(sigma(self.u, lambda_Im, mu_Im), epsilon(self.v)) * fen.dx + hIm * fen.inner(self.u, self.v) * self.ds(1)) A0Re = fen.assemble(a0Re, form_compiler_parameters = parsRe) A0Im = fen.assemble(a0Im, form_compiler_parameters = parsIm) DirichletBC0.apply(A0Re) DirichletBC0.zero(A0Im) A0ReMat = fen.as_backend_type(A0Re).mat() A0ImMat = fen.as_backend_type(A0Im).mat() A0Rer, A0Rec, A0Rev = A0ReMat.getValuesCSR() A0Imr, A0Imc, A0Imv = A0ImMat.getValuesCSR() self.As[0] = (csr_matrix((A0Rev, A0Rec, A0Rer), shape = A0ReMat.size) + 1.j * csr_matrix((A0Imv, A0Imc, A0Imr), shape = A0ImMat.size)) if self.verbosity >= 20: verbosityDepth("DEL", "Done assembling operator term.", timestamp = self.timestamp) if der <= 1 and self.As[1] is None: if self.verbosity >= 20: verbosityDepth("INIT", "Assembling operator term A1.", timestamp = self.timestamp) DirichletBC0 = fen.DirichletBC(self.V, fenZEROS(self.V.mesh().topology().dim()), self.DirichletBoundary) rho_Re, rho_Im = self.rho_ eta_Re, eta_Im = self.eta termNames = ["rho_", "eta"] parsRe = self.iterReduceQuadratureDegree(zip([rho_Re, eta_Re], [x + "Real" for x in termNames])) parsIm = self.iterReduceQuadratureDegree(zip([rho_Im, eta_Im], [x + "Imag" for x in termNames])) a1Re = - ((eta_Re * rho_Im + eta_Im * rho_Re) * fen.inner(self.u, self.v)) * fen.dx a1Im = ((eta_Re * rho_Re - eta_Im * rho_Im) * fen.inner(self.u, self.v)) * fen.dx A1Re = fen.assemble(a1Re, form_compiler_parameters = parsRe) A1Im = fen.assemble(a1Im, form_compiler_parameters = parsIm) DirichletBC0.zero(A1Re) DirichletBC0.zero(A1Im) A1ReMat = fen.as_backend_type(A1Re).mat() A1ImMat = fen.as_backend_type(A1Im).mat() A1Rer, A1Rec, A1Rev = A1ReMat.getValuesCSR() A1Imr, A1Imc, A1Imv = A1ImMat.getValuesCSR() self.As[1] = (csr_matrix((A1Rev, A1Rec, A1Rer), shape = A1ReMat.size) + 1.j * csr_matrix((A1Imv, A1Imc, A1Imr), shape = A1ImMat.size)) if self.verbosity >= 20: verbosityDepth("DEL", "Done assembling operator term.", timestamp = self.timestamp) if der <= 2 and self.As[2] is None: if self.verbosity >= 20: verbosityDepth("INIT", "Assembling operator term A2.", timestamp = self.timestamp) DirichletBC0 = fen.DirichletBC(self.V, fenZEROS(self.V.mesh().topology().dim()), self.DirichletBoundary) rho_Re, rho_Im = self.rho_ parsRe = self.iterReduceQuadratureDegree(zip([rho_Re], ["rho_Real"])) parsIm = self.iterReduceQuadratureDegree(zip([rho_Im], ["rho_Imag"])) a2Re = - rho_Re * fen.inner(self.u, self.v) * fen.dx a2Im = - rho_Im * fen.inner(self.u, self.v) * fen.dx A2Re = fen.assemble(a2Re, form_compiler_parameters = parsRe) A2Im = fen.assemble(a2Im, form_compiler_parameters = parsIm) DirichletBC0.zero(A2Re) DirichletBC0.zero(A2Im) A2ReMat = fen.as_backend_type(A2Re).mat() A2ImMat = fen.as_backend_type(A2Im).mat() A2Rer, A2Rec, A2Rev = A2ReMat.getValuesCSR() A2Imr, A2Imc, A2Imv = A2ImMat.getValuesCSR() self.As[2] = (csr_matrix((A2Rev, A2Rec, A2Rer), shape = A2ReMat.size) + 1.j * csr_matrix((A2Imv, A2Imc, A2Imr), shape = A2ImMat.size)) if self.verbosity >= 20: verbosityDepth("DEL", "Done assembling operator term.", timestamp = self.timestamp) if der == 0: return self.As[0] + mu * self.As[1] + mu**2 * self.As[2] if der == 1: return self.As[1] + 2 * mu * self.As[2] return self.As[2] diff --git a/rrompy/hfengines/vector_linear_problem/linear_elasticity_problem_engine.py b/rrompy/hfengines/vector_linear_problem/linear_elasticity_problem_engine.py index 25c9f18..1efc734 100644 --- a/rrompy/hfengines/vector_linear_problem/linear_elasticity_problem_engine.py +++ b/rrompy/hfengines/vector_linear_problem/linear_elasticity_problem_engine.py @@ -1,372 +1,354 @@ # Copyright (C) 2018 by the RROMPy authors # # This file is part of RROMPy. # # RROMPy is free software: you can redistribute it and/or modify # it under the terms of the GNU Lesser General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # RROMPy is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU Lesser General Public License for more details. # # You should have received a copy of the GNU Lesser General Public License # along with RROMPy. If not, see . # import numpy as np from scipy.sparse import csr_matrix import fenics as fen from rrompy.hfengines.base.vector_problem_engine_base import \ VectorProblemEngineBase from rrompy.utilities.base.types import Np1D, ScOp -from rrompy.utilities.base.fenics import fenZERO, fenZEROS, fenONE +from rrompy.utilities.fenics import (fenZERO, fenZEROS, fenONE, + elasticNormMatrix) from rrompy.utilities.base import verbosityDepth __all__ = ['LinearElasticityProblemEngine'] class LinearElasticityProblemEngine(VectorProblemEngineBase): """ Solver for generic linear elasticity problems. - div(lambda_ * div(u) * I + 2 * mu_ * epsilon(u)) = f in \Omega u = u0 on \Gamma_D \partial_nu = g1 on \Gamma_N \partial_nu + h u = g2 on \Gamma_R Attributes: verbosity: Verbosity level. BCManager: Boundary condition manager. V: Real vector FE space. u: Generic vector trial functions for variational form evaluation. v: Generic vector test functions for variational form evaluation. As: Scipy sparse array representation (in CSC format) of As. bs: Numpy array representation of bs. energyNormMatrix: Scipy sparse matrix representing inner product over V. bsmu: Mu value of last bs evaluation. liftDirichletDatamu: Mu value of last Dirichlet datum evaluation. liftedDirichletDatum: Dofs of Dirichlet datum lifting. mu0BC: Mu value of last Dirichlet datum lifting. degree_threshold: Threshold for ufl expression interpolation degree. lambda_: Value of lambda_. mu_: Value of mu_. forcingTerm: Value of f. DirichletDatum: Value of u0. NeumannDatum: Value of g1. RobinDatumG: Value of g2. RobinDatumH: Value of h. DirichletBoundary: Function handle to \Gamma_D. NeumannBoundary: Function handle to \Gamma_N. RobinBoundary: Function handle to \Gamma_R. ds: Boundary measure 2-tuple (resp. for Neumann and Robin boundaries). A0: Scipy sparse array representation (in CSC format) of A0. b0: Numpy array representation of b0. dsToBeSet: Whether ds needs to be set. """ def __init__(self, degree_threshold : int = np.inf, verbosity : int = 10, timestamp : bool = True): super().__init__(degree_threshold = degree_threshold, verbosity = verbosity, timestamp = timestamp) self.lambda_ = fenONE self.mu_ = fenONE self.forcingTerm = fenZEROS(self.V.mesh().topology().dim()) self.DirichletDatum = fenZEROS(self.V.mesh().topology().dim()) self.NeumannDatum = fenZEROS(self.V.mesh().topology().dim()) self.RobinDatumG = fenZEROS(self.V.mesh().topology().dim()) self.RobinDatumH = fenZERO @property def V(self): """Value of V.""" return self._V @V.setter def V(self, V): VectorProblemEngineBase.V.fset(self, V) self.forcingTerm = fenZEROS(self.V.mesh().topology().dim()) self.DirichletDatum = fenZEROS(self.V.mesh().topology().dim()) self.NeumannDatum = fenZEROS(self.V.mesh().topology().dim()) self.RobinDatumG = fenZEROS(self.V.mesh().topology().dim()) self.dsToBeSet = True @property def lambda_(self): """Value of lambda_.""" return self._lambda_ @lambda_.setter def lambda_(self, lambda_): self.resetAs() if not isinstance(lambda_, (list, tuple,)): lambda_ = [lambda_, fenZERO] self._lambda_ = lambda_ @property def mu_(self): """Value of mu_.""" return self._mu_ @mu_.setter def mu_(self, mu_): self.resetAs() if not isinstance(mu_, (list, tuple,)): mu_ = [mu_, fenZERO] self._mu_ = mu_ @property def forcingTerm(self): """Value of f.""" return self._forcingTerm @forcingTerm.setter def forcingTerm(self, forcingTerm): self.resetbs() if not isinstance(forcingTerm, (list, tuple,)): forcingTerm = [forcingTerm, fenZEROS(self.V.mesh().topology().dim())] self._forcingTerm = forcingTerm @property def DirichletDatum(self): """Value of u0.""" return self._DirichletDatum @DirichletDatum.setter def DirichletDatum(self, DirichletDatum): self.resetbs() if not isinstance(DirichletDatum, (list, tuple,)): DirichletDatum = [DirichletDatum, fenZEROS(self.V.mesh().topology().dim())] self._DirichletDatum = DirichletDatum @property def NeumannDatum(self): """Value of g1.""" return self._NeumannDatum @NeumannDatum.setter def NeumannDatum(self, NeumannDatum): self.resetbs() if not isinstance(NeumannDatum, (list, tuple,)): NeumannDatum = [NeumannDatum, fenZEROS(self.V.mesh().topology().dim())] self._NeumannDatum = NeumannDatum @property def RobinDatumG(self): """Value of g2.""" return self._RobinDatumG @RobinDatumG.setter def RobinDatumG(self, RobinDatumG): self.resetbs() if not isinstance(RobinDatumG, (list, tuple,)): RobinDatumG = [RobinDatumG, fenZEROS(self.V.mesh().topology().dim())] self._RobinDatumG = RobinDatumG @property def RobinDatumH(self): """Value of h.""" return self._RobinDatumH @RobinDatumH.setter def RobinDatumH(self, RobinDatumH): self.resetAs() if not isinstance(RobinDatumH, (list, tuple,)): RobinDatumH = [RobinDatumH, fenZERO] self._RobinDatumH = RobinDatumH @property def DirichletBoundary(self): """Function handle to DirichletBoundary.""" return self.BCManager.DirichletBoundary @DirichletBoundary.setter def DirichletBoundary(self, DirichletBoundary): self.resetAs() self.resetbs() self.BCManager.DirichletBoundary = DirichletBoundary @property def NeumannBoundary(self): """Function handle to NeumannBoundary.""" return self.BCManager.NeumannBoundary @NeumannBoundary.setter def NeumannBoundary(self, NeumannBoundary): self.resetAs() self.resetbs() self.dsToBeSet = True self.BCManager.NeumannBoundary = NeumannBoundary @property def RobinBoundary(self): """Function handle to RobinBoundary.""" return self.BCManager.RobinBoundary @RobinBoundary.setter def RobinBoundary(self, RobinBoundary): self.resetAs() self.resetbs() self.dsToBeSet = True self.BCManager.RobinBoundary = RobinBoundary def autoSetDS(self): """Set FEniCS boundary measure based on boundary function handles.""" if self.dsToBeSet: if self.verbosity >= 20: verbosityDepth("INIT", "Initializing boundary measures.", timestamp = self.timestamp) NB = self.NeumannBoundary RB = self.RobinBoundary boundary_markers = fen.MeshFunction("size_t", self.V.mesh(), self.V.mesh().topology().dim() - 1) NB.mark(boundary_markers, 0) RB.mark(boundary_markers, 1) self.ds = fen.Measure("ds", domain = self.V.mesh(), subdomain_data = boundary_markers) self.dsToBeSet = False if self.verbosity >= 20: verbosityDepth("DEL", "Done initializing boundary measures.", timestamp = self.timestamp) - def buildEnergyNormForm(self): # energy norm + def buildEnergyNormForm(self): """ Build sparse matrix (in CSR format) representative of scalar product. """ - if self.verbosity >= 20: - verbosityDepth("INIT", "Assembling energy matrix.", - timestamp = self.timestamp) lambda_Re, _ = self.lambda_ mu_Re, _ = self.mu_ - termNames = ["lambda_", "mu_"] - pars = self.iterReduceQuadratureDegree(zip( - [lambda_Re, mu_Re], - [x + "Real" for x in termNames])) - epsilon = lambda u: 0.5 * (fen.grad(u) + fen.nabla_grad(u)) - sigma = lambda u, l_, m_: ( - l_ * fen.div(u) * fen.Identity(u.geometric_dimension()) - + 2. * m_ * epsilon(u)) - normMatFen = fen.inner(sigma(self.u, lambda_Re, mu_Re), - epsilon(self.v)) * fen.dx - normMatFen = fen.assemble(normMatFen, form_compiler_parameters = pars) - normMat = fen.as_backend_type(normMatFen).mat() - self.energyNormMatrix = csr_matrix(normMat.getValuesCSR()[::-1], - shape = normMat.size) - if self.verbosity >= 20: - verbosityDepth("DEL", "Done assembling energy matrix.", - timestamp = self.timestamp) + self.energyNormMatrix = elasticNormMatrix(self.V, lambda_Re, mu_Re) def A(self, mu:complex, der : int = 0) -> ScOp: """Assemble (derivative of) operator of linear system.""" Anull = self.checkAInBounds(der) if Anull is not None: return Anull self.autoSetDS() if self.As[0] is None: if self.verbosity >= 20: verbosityDepth("INIT", "Assembling operator term A0.", timestamp = self.timestamp) DirichletBC0 = fen.DirichletBC(self.V, fenZEROS(self.V.mesh().topology().dim()), self.DirichletBoundary) lambda_Re, lambda_Im = self.lambda_ mu_Re, mu_Im = self.mu_ hRe, hIm = self.RobinDatumH termNames = ["lambda_", "mu_", "RobinDatumH"] parsRe = self.iterReduceQuadratureDegree(zip( [lambda_Re, mu_Re, hRe], [x + "Real" for x in termNames])) parsIm = self.iterReduceQuadratureDegree(zip( [lambda_Im, mu_Re, hIm], [x + "Imag" for x in termNames])) epsilon = lambda u: 0.5 * (fen.grad(u) + fen.nabla_grad(u)) sigma = lambda u, l_, m_: ( l_ * fen.div(u) * fen.Identity(u.geometric_dimension()) + 2. * m_ * epsilon(u)) a0Re = (fen.inner(sigma(self.u, lambda_Re, mu_Re), epsilon(self.v)) * fen.dx + hRe * fen.inner(self.u, self.v) * self.ds(1)) a0Im = (fen.inner(sigma(self.u, lambda_Im, mu_Im), epsilon(self.v)) * fen.dx + hIm * fen.inner(self.u, self.v) * self.ds(1)) A0Re = fen.assemble(a0Re, form_compiler_parameters = parsRe) A0Im = fen.assemble(a0Im, form_compiler_parameters = parsIm) DirichletBC0.apply(A0Re) DirichletBC0.zero(A0Im) A0ReMat = fen.as_backend_type(A0Re).mat() A0ImMat = fen.as_backend_type(A0Im).mat() A0Rer, A0Rec, A0Rev = A0ReMat.getValuesCSR() A0Imr, A0Imc, A0Imv = A0ImMat.getValuesCSR() self.As[0] = (csr_matrix((A0Rev, A0Rec, A0Rer), shape = A0ReMat.size) + 1.j * csr_matrix((A0Imv, A0Imc, A0Imr), shape = A0ImMat.size)) if self.verbosity >= 20: verbosityDepth("DEL", "Done assembling operator term.", timestamp = self.timestamp) return self.As[0] def b(self, mu:complex, der : int = 0, homogeneized : bool = False) -> Np1D: """Assemble (derivative of) RHS of linear system.""" bnull = self.checkbInBounds(der, homogeneized) if bnull is not None: return bnull if homogeneized and not np.isclose(self.mu0BC, mu): self.u0BC = self.liftDirichletData(mu) if (max(self.nbs, self.nAs * homogeneized) > 1 and not np.isclose(self.bsmu, mu)): self.bsmu = mu self.resetbs() b = self.bsH[der] if homogeneized else self.bs[der] if b is None: self.autoSetDS() if self.verbosity >= 20: verbosityDepth("INIT", ("Assembling forcing term " "b{}.").format(der), timestamp = self.timestamp) if der == 0: fRe, fIm = self.forcingTerm g1Re, g1Im = self.NeumannDatum g2Re, g2Im = self.RobinDatumG else: fRe = fenZEROS(self.V.mesh().topology().dim()) fIm = fenZEROS(self.V.mesh().topology().dim()) g1Re = fenZEROS(self.V.mesh().topology().dim()) g1Im = fenZEROS(self.V.mesh().topology().dim()) g2Re = fenZEROS(self.V.mesh().topology().dim()) g2Im = fenZEROS(self.V.mesh().topology().dim()) termNames = ["forcingTerm", "NeumannDatum", "RobinDatumG"] parsRe = self.iterReduceQuadratureDegree(zip( [fRe, g1Re, g2Re], [x + "Real" for x in termNames])) parsIm = self.iterReduceQuadratureDegree(zip( [fIm, g1Im, g2Im], [x + "Imag" for x in termNames])) L0Re = (fen.inner(fRe, self.v) * fen.dx + fen.inner(g1Re, self.v) * self.ds(0) + fen.inner(g2Re, self.v) * self.ds(1)) L0Im = (fen.inner(fIm, self.v) * fen.dx + fen.inner(g1Im, self.v) * self.ds(0) + fen.inner(g2Im, self.v) * self.ds(1)) b0Re = fen.assemble(L0Re, form_compiler_parameters = parsRe) b0Im = fen.assemble(L0Im, form_compiler_parameters = parsIm) if homogeneized: Ader = self.A(mu, der) b0Re[:] -= np.real(Ader.dot(self.u0BC)) b0Im[:] -= np.imag(Ader.dot(self.u0BC)) DBCR = fen.DirichletBC(self.V, fenZEROS(self.V.mesh().topology().dim()), self.DirichletBoundary) DBCI = fen.DirichletBC(self.V, fenZEROS(self.V.mesh().topology().dim()), self.DirichletBoundary) else: DBCR = fen.DirichletBC(self.V, self.DirichletDatum[0], self.DirichletBoundary) DBCI = fen.DirichletBC(self.V, self.DirichletDatum[1], self.DirichletBoundary) DBCR.apply(b0Re) DBCI.apply(b0Im) b = np.array(b0Re[:] + 1.j * b0Im[:], dtype = np.complex) if homogeneized: self.bsH[der] = b else: self.bs[der] = b if self.verbosity >= 20: verbosityDepth("DEL", "Done assembling forcing term.", timestamp = self.timestamp) return b diff --git a/rrompy/reduction_methods/base/generic_approximant.py b/rrompy/reduction_methods/base/generic_approximant.py index ac6b75e..2b43bb4 100644 --- a/rrompy/reduction_methods/base/generic_approximant.py +++ b/rrompy/reduction_methods/base/generic_approximant.py @@ -1,616 +1,617 @@ # Copyright (C) 2018 by the RROMPy authors # # This file is part of RROMPy. # # RROMPy is free software: you can redistribute it and/or modify # it under the terms of the GNU Lesser General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # RROMPy is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU Lesser General Public License for more details. # # You should have received a copy of the GNU Lesser General Public License # along with RROMPy. If not, see . # from abc import abstractmethod import pickle import numpy as np from itertools import product as iterprod from copy import deepcopy as copy from rrompy.sampling.base.sampling_engine_base import SamplingEngineBase from rrompy.utilities.base.types import Np1D, DictAny, HFEng, sampleEng, strLst from rrompy.utilities.base import purgeDict, verbosityDepth, getNewFilename from rrompy.utilities.exception_manager import (RROMPyException, modeAssert, RROMPy_READY, RROMPy_FRAGILE) __all__ = ['GenericApproximant'] def addNormFieldToClass(self, fieldName): def objFunc(self, mu:complex, homogeneized : bool = False) -> float: getObj = getattr(self.__class__, "get" + fieldName) return self.HFEngine.norm(getObj(self, mu, homogeneized)) setattr(self.__class__, "norm" + fieldName, objFunc) def addPlotFieldToClass(self, fieldName): def objFunc(self, mu:complex, name : str = fieldName, save : str = None, what : strLst = 'all', saveFormat : str = "eps", saveDPI : int = 100, show : bool = True, homogeneized : bool = False, **figspecs): uV = getattr(self.__class__, "get" + fieldName)(self, mu, homogeneized) self.HFEngine.plot(uV, name = name, save = save, what = what, saveFormat = saveFormat, saveDPI = saveDPI, show = show, **figspecs) setattr(self.__class__, "plot" + fieldName, objFunc) def addOutParaviewFieldToClass(self, fieldName): def objFunc(self, mu:complex, name : str = fieldName, filename : str = "out", time : float = 0., what : strLst = 'all', forceNewFile : bool = True, folder : bool = False, filePW = None, homogeneized : bool = False): uV = getattr(self.__class__, "get" + fieldName)(self, mu, homogeneized) self.HFEngine.outParaview(uV, name = name, filename = filename, time = time, what = what, forceNewFile = forceNewFile, folder = folder, filePW = filePW) setattr(self.__class__, "outParaview" + fieldName, objFunc) def addOutParaviewTimeDomainFieldToClass(self, fieldName): def objFunc(self, mu:complex, omega : float = None, timeFinal : float = None, periodResolution : int = 20, name : str = fieldName, filename : str = "out", forceNewFile : bool = True, folder : bool = False, homogeneized : bool = False): uV = getattr(self.__class__, "get" + fieldName)(self, mu, homogeneized) if omega is None: omega = np.real(mu) self.HFEngine.outParaviewTimeDomain(uV, omega = omega, timeFinal = timeFinal, periodResolution = periodResolution, name = name, filename = filename, forceNewFile = forceNewFile, folder = folder) setattr(self.__class__, "outParaviewTimeDomain" + fieldName, objFunc) class GenericApproximant: """ ABSTRACT ROM approximant computation for parametric problems. Args: HFEngine: HF problem solver. mu0(optional): Default parameter. Defaults to 0. approxParameters(optional): Dictionary containing values for main parameters of approximant. Recognized keys are: - 'POD': whether to compute POD of snapshots; defaults to True. Defaults to empty dict. - homogeneized: Whether to homogeneize Dirichlet BCs. Defaults to False. + 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. parameterList: Recognized keys of approximant parameters: - 'POD': whether to compute POD of snapshots. verbosity: Verbosity level. POD: Whether to compute POD of snapshots. samplingEngine: Sampling engine. uHF: High fidelity solution with wavenumber lastSolvedHF as numpy complex vector. lastSolvedHF: Wavenumber corresponding to last computed high fidelity solution. uApp: Last evaluated approximant as numpy complex vector. """ __all__ += [ftype + dtype for ftype, dtype in iterprod( ["norm", "plot", "outParaview", "outParaviewTimeDomain"], ["HF", "RHS", "Approx", "Res", "Err"])] def __init__(self, HFEngine:HFEng, mu0 : complex = 0, 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._addParametersToList(["POD"]) self.mu0 = mu0 self.homogeneized = homogeneized self.approxParameters = approxParameters self._postInit() ### add norm{HF,RHS,Approx,Res,Err} methods """ Compute norm of * at arbitrary parameter. Args: mu: Target parameter. homogeneized(optional): Whether to remove Dirichlet BC. Defaults to False. Returns: Target norm of *. """ for objName in ["HF", "RHS", "Approx", "Res", "Err"]: addNormFieldToClass(self, objName) ### add plot{HF,RHS,Approx,Res,Err} methods """ Do some nice plots of * at arbitrary parameter. Args: mu: Target parameter. name(optional): Name to be shown as title of the plots. Defaults to 'u'. what(optional): Which plots to do. If list, can contain 'ABS', 'PHASE', 'REAL', 'IMAG'. If str, same plus wildcard 'ALL'. Defaults to 'ALL'. save(optional): Where to save plot(s). Defaults to None, i.e. no saving. saveFormat(optional): Format for saved plot(s). Defaults to "eps". saveDPI(optional): DPI for saved plot(s). Defaults to 100. 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 def _addParametersToList(self, what:strLst): if not hasattr(self, "parameterList"): self.parameterList = [] self.parameterList += what def _postInit(self): if self.depth == 0: if self.verbosity >= 10: verbosityDepth("DEL", "Done initializing.", timestamp = self.timestamp) del self.depth else: self.depth -= 1 def name(self) -> str: return self.__class__.__name__ def __str__(self) -> str: return self.name() def __repr__(self) -> str: return self.__str__() + " at " + hex(id(self)) def setupSampling(self, SamplingEngine : sampleEng = SamplingEngineBase): """Setup sampling engine.""" modeAssert(self._mode, message = "Cannot setup sampling engine.") self.samplingEngine = SamplingEngine(self.HFEngine, verbosity = self.verbosity) @property def mu0(self): """Value of mu0.""" return self._mu0 @mu0.setter def mu0(self, mu0): if not (hasattr(self, "_mu0") and np.isclose(mu0, self.mu0)): self.resetSamples() self._mu0 = mu0 @property def approxParameters(self): """Value of approximant parameters.""" return self._approxParameters @approxParameters.setter def approxParameters(self, approxParams): if not hasattr(self, "approxParameters"): self._approxParameters = {} approxParameters = purgeDict(approxParams, self.parameterList, dictname = self.name() + ".approxParameters", baselevel = 1) keyList = list(approxParameters.keys()) if "POD" in keyList: self.POD = approxParameters["POD"] elif not hasattr(self, "_POD") or self._POD is None: self.POD = True @property def POD(self): """Value of POD.""" return self._POD @POD.setter def POD(self, POD): if hasattr(self, "_POD"): PODold = self.POD else: PODold = -1 self._POD = POD self._approxParameters["POD"] = self.POD if PODold != self.POD: self.samplingEngine = None self.resetSamples() @property def homogeneized(self): """Value of homogeneized.""" return self._homogeneized @homogeneized.setter def homogeneized(self, homogeneized): if not hasattr(self, "_homogeneized"): self._homogeneized = None if homogeneized != self.homogeneized: self._homogeneized = homogeneized self.resetSamples() def solveHF(self, mu : complex = None): """ Find high fidelity solution with original parameters and arbitrary parameter. Args: mu: Target parameter. """ if mu is None: mu = self.mu0 if (not hasattr(self, "lastSolvedHF") or not np.isclose(self.lastSolvedHF, mu)): self.uHF = self.samplingEngine.solveLS(mu, homogeneized = self.homogeneized) self.lastSolvedHF = mu def resetSamples(self): """Reset samples.""" if hasattr(self, "samplingEngine") and self.samplingEngine is not None: self.samplingEngine.resetHistory() else: self.setupSampling() self.trainedModel = None self._mode = RROMPy_READY def plotSamples(self, name : str = "u", save : str = None, what : strLst = 'all', saveFormat : str = "eps", saveDPI : int = 100, **figspecs): """ Do some nice plots of the samples. Args: name(optional): Name to be shown as title of the plots. Defaults to 'u'. what(optional): Which plots to do. If list, can contain 'ABS', 'PHASE', 'REAL', 'IMAG'. If str, same plus wildcard 'ALL'. Defaults to 'ALL'. save(optional): Where to save plot(s). Defaults to None, i.e. no saving. saveFormat(optional): Format for saved plot(s). Defaults to "eps". saveDPI(optional): DPI for saved plot(s). Defaults to 100. figspecs(optional key args): Optional arguments for matplotlib figure creation. """ modeAssert(self._mode, message = "Cannot plot samples.") self.samplingEngine.plotSamples(name = name, save = save, what = what, saveFormat = saveFormat, saveDPI = saveDPI, **figspecs) def outParaviewSamples(self, name : str = "u", filename : str = "out", times : Np1D = None, what : strLst = 'all', forceNewFile : bool = True, folders : bool = False, filePW = None): """ Output samples to ParaView file. Args: name(optional): Base name to be used for data output. filename(optional): Name of output file. times(optional): Timestamps. what(optional): Which plots to do. If list, can contain 'MESH', 'ABS', 'PHASE', 'REAL', 'IMAG'. If str, same plus wildcard 'ALL'. Defaults to 'ALL'. forceNewFile(optional): Whether to create new output file. folders(optional): Whether to split output in folders. filePW(optional): Fenics File entity (for time series). """ modeAssert(self._mode, message = "Cannot output samples.") self.samplingEngine.outParaviewSamples(name = name, filename = filename, times = times, what = what, forceNewFile = forceNewFile, folders = folders, filePW = filePW) def outParaviewTimeDomainSamples(self, omegas : Np1D = None, timeFinal : Np1D = None, periodResolution : int = 20, name : str = "u", filename : str = "out", forceNewFile : bool = True, folders : bool = False): """ Output samples to ParaView file, converted to time domain. Args: omegas(optional): frequencies. timeFinal(optional): final time of simulation. periodResolution(optional): number of time steps per period. name(optional): Base name to be used for data output. filename(optional): Name of output file. forceNewFile(optional): Whether to create new output file. folders(optional): Whether to split output in folders. """ modeAssert(self._mode, message = "Cannot output samples.") self.samplingEngine.outParaviewTimeDomainSamples(omegas = omegas, timeFinal = timeFinal, periodResolution = periodResolution, name = name, filename = filename, forceNewFile = forceNewFile, folders = folders) @abstractmethod def setupApprox(self): """ Setup approximant. (ABSTRACT) Any specialization should include something like if self.checkComputedApprox(): return modeAssert(self._mode, message = "Cannot setup approximant.") ... self.trainedModel = ... self.trainedModel.data = ... self.trainedModel.data.approxParameters = copy( self.approxParameters) """ pass def checkComputedApprox(self) -> bool: """ Check if setup of new approximant is not needed. Returns: True if new setup is not needed. False otherwise. """ return self._mode == RROMPy_FRAGILE or (self.trainedModel is not None and self.trainedModel.data.approxParameters == self.approxParameters) def evalApproxReduced(self, mu:complex): """ Evaluate reduced representation of approximant at arbitrary parameter. Args: mu: Target parameter. """ self.setupApprox() self.uAppReduced = self.trainedModel.getApproxReduced(mu) def evalApprox(self, mu:complex): """ Evaluate approximant at arbitrary parameter. Args: mu: Target parameter. """ self.setupApprox() self.uApp = self.trainedModel.getApprox(mu) def getHF(self, mu:complex, homogeneized : bool = False) -> Np1D: """ Get HF solution at arbitrary parameter. Args: mu: Target parameter. homogeneized(optional): Whether to remove Dirichlet BC. Defaults to False. Returns: HFsolution. """ self.solveHF(mu) if self.homogeneized and not homogeneized: return self.uHF + self.HFEngine.liftDirichletData(mu) if not self.homogeneized and homogeneized: return self.uHF - self.HFEngine.liftDirichletData(mu) return self.uHF def getRHS(self, mu:complex, homogeneized : bool = False) -> Np1D: """ Get linear system RHS at arbitrary parameter. Args: mu: Target parameter. homogeneized(optional): Whether to remove Dirichlet BC. Defaults to False. Returns: Linear system RHS. """ return self.HFEngine.residual(None, mu, homogeneized = homogeneized) def getApproxReduced(self, mu:complex) -> Np1D: """ Get approximant at arbitrary parameter. Args: mu: Target parameter. Returns: Reduced approximant. """ self.evalApproxReduced(mu) return self.uAppReduced def getApprox(self, mu:complex, homogeneized : bool = False) -> Np1D: """ Get approximant at arbitrary parameter. Args: mu: Target parameter. homogeneized(optional): Whether to remove Dirichlet BC. Defaults to False. Returns: Approximant. """ self.evalApprox(mu) if self.homogeneized and not homogeneized: return self.uApp + self.HFEngine.liftDirichletData(mu) if not self.homogeneized and homogeneized: return self.uApp - self.HFEngine.liftDirichletData(mu) return self.uApp def getRes(self, mu:complex, homogeneized : bool = False) -> Np1D: """ Get residual at arbitrary parameter. Args: mu: Target parameter. homogeneized(optional): Whether to remove Dirichlet BC. Defaults to False. Returns: Approximant residual. """ return self.HFEngine.residual(self.getApprox(mu, homogeneized), mu, homogeneized = homogeneized) def getErr(self, mu:complex, homogeneized : bool = False) -> Np1D: """ Get error at arbitrary parameter. Args: mu: Target parameter. homogeneized(optional): Whether to remove Dirichlet BC. Defaults to False. Returns: Approximant error. """ return self.getApprox(mu, homogeneized) - self.getHF(mu, homogeneized) 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): """Store trained reduced model to file.""" self.setupApprox() if self.verbosity >= 20: verbosityDepth("INIT", "Storing trained model to file.", timestamp = self.timestamp) if forceNewFile: filename = getNewFilename(filenameBase, "pkl") else: filename = "{}.pkl".format(filenameBase) with open(filename, "wb") as fileOut: pickle.dump(self.trainedModel.data.__dict__, fileOut) if self.verbosity >= 20: verbosityDepth("DEL", "Done storing trained model.", timestamp = self.timestamp) return filename def loadTrainedModel(self, filename:str): """Load trained reduced model from file.""" if self.verbosity >= 20: verbosityDepth("INIT", "Loading pre-trained model from file.", timestamp = self.timestamp) with open(filename, "rb") as fileIn: datadict = pickle.load(fileIn) name = datadict.pop("name") if name == "TrainedModelPade": from rrompy.reduction_methods.trained_model import \ TrainedModelPade as tModel elif name == "TrainedModelRB": from rrompy.reduction_methods.trained_model import \ TrainedModelRB as tModel else: raise RROMPyException(("Trained model name not recognized. " "Loading failed.")) self.mu0 = datadict.pop("mu0") from rrompy.reduction_methods.trained_model import TrainedModelData trainedModel = tModel() trainedModel.verbosity = self.verbosity trainedModel.timestamp = self.timestamp data = TrainedModelData(name, self.mu0, datadict.pop("projMat"), datadict.pop("rescalingExp")) if "mus" in datadict: 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 = np.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/taylor/__init__.py b/rrompy/reduction_methods/centered/__init__.py similarity index 72% rename from rrompy/reduction_methods/taylor/__init__.py rename to rrompy/reduction_methods/centered/__init__.py index 09d9bec..633b633 100644 --- a/rrompy/reduction_methods/taylor/__init__.py +++ b/rrompy/reduction_methods/centered/__init__.py @@ -1,29 +1,29 @@ # Copyright (C) 2018 by the RROMPy authors # # This file is part of RROMPy. # # RROMPy is free software: you can redistribute it and/or modify # it under the terms of the GNU Lesser General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # RROMPy is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU Lesser General Public License for more details. # # You should have received a copy of the GNU Lesser General Public License # along with RROMPy. If not, see . # -from .generic_approximant_taylor import GenericApproximantTaylor -from .approximant_taylor_pade import ApproximantTaylorPade -from .approximant_taylor_rb import ApproximantTaylorRB +from .generic_centered_approximant import GenericCenteredApproximant +from .rational_pade import RationalPade +from .rb_centered import RBCentered __all__ = [ - 'GenericApproximantTaylor', - 'ApproximantTaylorPade', - 'ApproximantTaylorRB' + 'GenericCenteredApproximant', + 'RationalPade', + 'RBCentered' ] diff --git a/rrompy/reduction_methods/taylor/generic_approximant_taylor.py b/rrompy/reduction_methods/centered/generic_centered_approximant.py similarity index 97% rename from rrompy/reduction_methods/taylor/generic_approximant_taylor.py rename to rrompy/reduction_methods/centered/generic_centered_approximant.py index 33658fa..898d5e0 100644 --- a/rrompy/reduction_methods/taylor/generic_approximant_taylor.py +++ b/rrompy/reduction_methods/centered/generic_centered_approximant.py @@ -1,185 +1,186 @@ # Copyright (C) 2018 by the RROMPy authors # # This file is part of RROMPy. # # RROMPy is free software: you can redistribute it and/or modify # it under the terms of the GNU Lesser General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # RROMPy is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU Lesser General Public License for more details. # # You should have received a copy of the GNU Lesser General Public License # along with RROMPy. If not, see . # import numpy as np from rrompy.reduction_methods.base.generic_approximant import ( GenericApproximant) from rrompy.utilities.base.types import DictAny, HFEng from rrompy.utilities.base import purgeDict, verbosityDepth from rrompy.utilities.exception_manager import (RROMPyException, modeAssert, RROMPyWarning) -__all__ = ['GenericApproximantTaylor'] +__all__ = ['GenericCenteredApproximant'] -class GenericApproximantTaylor(GenericApproximant): +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; - 'E': total number of derivatives current approximant relies upon; defaults to 1; - 'sampleType': label of sampling type; available values are: - 'ARNOLDI': orthogonalization of solution derivatives through Arnoldi algorithm; - 'KRYLOV': standard computation of solution derivatives. Defaults to 'KRYLOV'. Defaults to empty dict. - homogeneized: Whether to homogeneize Dirichlet BCs. Defaults to False. + 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. parameterList: Recognized keys of approximant parameters: - 'POD': whether to compute POD of snapshots; - 'E': total number of derivatives current approximant relies upon; - 'sampleType': label of sampling type. POD: Whether to compute QR factorization of derivatives. E: Number of solution derivatives over which current approximant is based upon. sampleType: Label of sampling type. initialHFData: HF problem initial data. samplingEngine: Sampling engine. uHF: High fidelity solution with wavenumber lastSolvedHF as numpy complex vector. lastSolvedHF: Wavenumber corresponding to last computed high fidelity solution. uApp: Last evaluated approximant as numpy complex vector. """ def __init__(self, HFEngine:HFEng, mu0 : complex = 0, approxParameters : DictAny = {}, homogeneized : bool = False, verbosity : int = 10, timestamp : bool = True): self._preInit() self._addParametersToList(["E", "sampleType"]) super().__init__(HFEngine = HFEngine, mu0 = mu0, approxParameters = approxParameters, homogeneized = homogeneized, verbosity = verbosity, timestamp = timestamp) self._postInit() def setupSampling(self): """Setup sampling engine.""" modeAssert(self._mode, message = "Cannot setup sampling engine.") if not hasattr(self, "_sampleType"): return if self.sampleType == "ARNOLDI": from rrompy.sampling.linear_problem.sampling_engine_arnoldi \ import SamplingEngineArnoldi super().setupSampling(SamplingEngineArnoldi) elif self.sampleType == "KRYLOV": from rrompy.sampling.linear_problem.sampling_engine_krylov \ import SamplingEngineKrylov super().setupSampling(SamplingEngineKrylov) else: raise RROMPyException("Sample type not recognized.") @property def approxParameters(self): """Value of approximant parameters. Its assignment may change E.""" return self._approxParameters @approxParameters.setter def approxParameters(self, approxParams): approxParameters = purgeDict(approxParams, self.parameterList, dictname = self.name() + ".approxParameters", baselevel = 1) approxParametersCopy = purgeDict(approxParameters, ["E", "sampleType"], True, True, baselevel = 1) GenericApproximant.approxParameters.fset(self, approxParametersCopy) keyList = list(approxParameters.keys()) if "E" in keyList: self.E = approxParameters["E"] elif hasattr(self, "_E") and self._E is not None: self.E = self.E else: self.E = 1 if "sampleType" in keyList: self.sampleType = approxParameters["sampleType"] elif not hasattr(self, "_sampleType") or self._sampleType is None: self.sampleType = "KRYLOV" @property def E(self): """Value of E.""" return self._E @E.setter def E(self, E): if E < 0: raise RROMPyException("E must be non-negative.") self._E = E self._approxParameters["E"] = self.E @property def sampleType(self): """Value of sampleType.""" return self._sampleType @sampleType.setter def sampleType(self, sampleType): if hasattr(self, "_sampleType") and self._sampleType is not None: sampleTypeOld = self.sampleType else: sampleTypeOld = -1 try: sampleType = sampleType.upper().strip().replace(" ","") if sampleType not in ["ARNOLDI", "KRYLOV"]: raise RROMPyException("Sample type not recognized.") self._sampleType = sampleType except: RROMPyWarning(("Prescribed sampleType not recognized. Overriding " "to 'KRYLOV'.")) self._sampleType = "KRYLOV" self._approxParameters["sampleType"] = self.sampleType if sampleTypeOld != self.sampleType: self.resetSamples() def computeDerivatives(self): """Compute derivatives of solution map starting from order 0.""" modeAssert(self._mode, message = "Cannot start derivative computation.") if self.samplingEngine.nsamples <= self.E: if self.verbosity >= 5: verbosityDepth("INIT", "Starting computation of derivatives.", timestamp = self.timestamp) self.samplingEngine.iterSample(self.mu0, self.E + 1, homogeneized = self.homogeneized) if self.verbosity >= 5: verbosityDepth("DEL", "Done computing derivatives.", timestamp = self.timestamp) def normApprox(self, mu:complex, homogeneized : bool = False) -> float: """ Compute norm of approximant at arbitrary parameter. Args: mu: Target parameter. homogeneized(optional): Whether to remove Dirichlet BC. Defaults to False. Returns: Target norm of approximant. """ if self.sampleType != "ARNOLDI" or self.homogeneized != homogeneized: return super().normApprox(mu, homogeneized) return np.linalg.norm(self.getApproxReduced(mu, homogeneized)) diff --git a/rrompy/reduction_methods/taylor/approximant_taylor_pade.py b/rrompy/reduction_methods/centered/rational_pade.py similarity index 97% rename from rrompy/reduction_methods/taylor/approximant_taylor_pade.py rename to rrompy/reduction_methods/centered/rational_pade.py index 6dd34af..58b51a9 100644 --- a/rrompy/reduction_methods/taylor/approximant_taylor_pade.py +++ b/rrompy/reduction_methods/centered/rational_pade.py @@ -1,455 +1,456 @@ # Copyright (C) 2018 by the RROMPy authors # # This file is part of RROMPy. # # RROMPy is free software: you can redistribute it and/or modify # it under the terms of the GNU Lesser General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # RROMPy is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU Lesser General Public License for more details. # # You should have received a copy of the GNU Lesser General Public License # along with RROMPy. If not, see . # from copy import copy import numpy as np from rrompy.reduction_methods.base import checkRobustTolerance from rrompy.reduction_methods.trained_model import (TrainedModelData, TrainedModelPade as tModel) -from .generic_approximant_taylor import GenericApproximantTaylor +from .generic_centered_approximant import GenericCenteredApproximant from rrompy.sampling.base.pod_engine import PODEngine from rrompy.utilities.base.types import Np1D, Np2D, Tuple, DictAny, HFEng from rrompy.utilities.base import verbosityDepth, purgeDict from rrompy.utilities.exception_manager import (RROMPyException, modeAssert, RROMPyWarning) -__all__ = ['ApproximantTaylorPade'] +__all__ = ['RationalPade'] -class ApproximantTaylorPade(GenericApproximantTaylor): +class RationalPade(GenericCenteredApproximant): """ ROM single-point fast Pade' approximant computation for parametric problems. Args: HFEngine: HF problem solver. mu0(optional): Default parameter. Defaults to 0. approxParameters(optional): Dictionary containing values for main parameters of approximant. Recognized keys are: - 'POD': whether to compute POD of snapshots; defaults to True; - 'rho': weight for computation of original Pade' approximant; defaults to np.inf, i.e. fast approximant; - 'M': degree of Pade' approximant numerator; defaults to 0; - 'N': degree of Pade' approximant denominator; defaults to 0; - 'E': total number of derivatives current approximant relies upon; defaults to 1; - 'robustTol': tolerance for robust Pade' denominator management; defaults to 0; - 'sampleType': label of sampling type; available values are: - 'ARNOLDI': orthogonalization of solution derivatives through Arnoldi algorithm; - 'KRYLOV': standard computation of solution derivatives. Defaults to 'KRYLOV'. Defaults to empty dict. - homogeneized: Whether to homogeneize Dirichlet BCs. Defaults to False. + 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. parameterList: Recognized keys of approximant parameters: - 'POD': whether to compute POD of snapshots; - 'rho': weight for computation of original Pade' approximant; - 'M': degree of Pade' approximant numerator; - 'N': degree of Pade' approximant denominator; - 'E': total number of derivatives current approximant relies upon; - 'robustTol': tolerance for robust Pade' denominator management; - 'sampleType': label of sampling type. POD: Whether to compute QR factorization of derivatives. rho: Weight of approximant. M: Numerator degree of approximant. N: Denominator degree of approximant. E: Number of solution derivatives over which current approximant is based upon. robustTol: Tolerance for robust Pade' denominator management. sampleType: Label of sampling type. initialHFData: HF problem initial data. uHF: High fidelity solution with wavenumber lastSolvedHF as numpy complex vector. lastSolvedHF: Wavenumber corresponding to last computed high fidelity solution. G: Square Numpy 2D vector of size (N+1) corresponding to Pade' denominator matrix (see paper). uApp: Last evaluated approximant as numpy complex vector. """ def __init__(self, HFEngine:HFEng, mu0 : complex = 0, approxParameters : DictAny = {}, homogeneized : bool = False, verbosity : int = 10, timestamp : bool = True): self._preInit() self._addParametersToList(["M", "N", "robustTol", "rho"]) super().__init__(HFEngine = HFEngine, mu0 = mu0, approxParameters = approxParameters, homogeneized = homogeneized, verbosity = verbosity, timestamp = timestamp) self._postInit() @property def approxParameters(self): """Value of approximant parameters.""" return self._approxParameters @approxParameters.setter def approxParameters(self, approxParams): approxParameters = purgeDict(approxParams, self.parameterList, dictname = self.name() + ".approxParameters", baselevel = 1) approxParametersCopy = purgeDict(approxParameters, ["M", "N", "robustTol", "rho"], True, True, baselevel = 1) keyList = list(approxParameters.keys()) if "rho" in keyList: self._rho = approxParameters["rho"] elif not hasattr(self, "_rho") or self.rho is None: self._rho = np.inf - GenericApproximantTaylor.approxParameters.fset(self, - approxParametersCopy) + GenericCenteredApproximant.approxParameters.fset(self, + approxParametersCopy) self.rho = self._rho if "robustTol" in keyList: self.robustTol = approxParameters["robustTol"] elif not hasattr(self, "_robustTol") or self._robustTol is None: self.robustTol = 0 self._ignoreParWarnings = True if "M" in keyList: self.M = approxParameters["M"] elif hasattr(self, "_M") and self._M is not None: self.M = self.M else: self.M = 0 del self._ignoreParWarnings if "N" in keyList: self.N = approxParameters["N"] elif hasattr(self, "_N") and self._N is not None: self.N = self.N else: self.N = 0 @property def rho(self): """Value of rho.""" return self._rho @rho.setter def rho(self, rho): self._rho = np.abs(rho) self._approxParameters["rho"] = self.rho @property def M(self): """Value of M. Its assignment may change E.""" return self._M @M.setter def M(self, M): if M < 0: raise RROMPyException("M must be non-negative.") self._M = M self._approxParameters["M"] = self.M if not hasattr(self, "_ignoreParWarnings"): self.checkMNE() @property def N(self): """Value of N. Its assignment may change E.""" return self._N @N.setter def N(self, N): if N < 0: raise RROMPyException("N must be non-negative.") self._N = N self._approxParameters["N"] = self.N if not hasattr(self, "_ignoreParWarnings"): self.checkMNE() def checkMNE(self): """Check consistency of M, N, and E.""" if not hasattr(self, "_E") or self.E is None: return M = self.M if (hasattr(self, "_M") and self.M is not None) else 0 N = self.N if (hasattr(self, "_N") and self.N is not None) else 0 msg = "max(M, N)" if self.rho == np.inf else "M + N" bound = eval(msg) if self.E < bound: RROMPyWarning(("Prescribed E is too small. Updating E to " "{}.").format(msg)) self.E = bound del M, N @property def robustTol(self): """Value of tolerance for robust Pade' denominator management.""" return self._robustTol @robustTol.setter def robustTol(self, robustTol): if robustTol < 0.: RROMPyWarning(("Overriding prescribed negative robustness " "tolerance to 0.")) robustTol = 0. self._robustTol = robustTol self._approxParameters["robustTol"] = self.robustTol @property def E(self): """Value of E.""" return self._E @E.setter def E(self, E): - GenericApproximantTaylor.E.fset(self, E) + GenericCenteredApproximant.E.fset(self, E) self.checkMNE() def _setupDenominator(self): """Compute Pade' denominator.""" if self.verbosity >= 7: verbosityDepth("INIT", "Starting computation of denominator.", timestamp = self.timestamp) while self.N > 0: if self.POD: ev, eV = self.findeveVGQR() else: ev, eV = self.findeveVGExplicit() newParameters = checkRobustTolerance(ev, self.E, self.robustTol) if not newParameters: break self.approxParameters = newParameters if self.N <= 0: eV = np.ones((1, 1)) if self.verbosity >= 7: verbosityDepth("DEL", "Done computing denominator.", timestamp = self.timestamp) return eV[::-1, 0] def _setupNumerator(self): """Compute Pade' numerator.""" if self.verbosity >= 7: verbosityDepth("INIT", "Starting computation of numerator.", timestamp = self.timestamp) P = np.zeros((self.E + 1, self.M + 1), dtype = np.complex) for i in range(self.E + 1): l = min(self.M + 1, i + self.N + 1) if i < l: P[i, i : l] = self.trainedModel.data.Q[: l - i] if self.verbosity >= 7: verbosityDepth("DEL", "Done computing numerator.", timestamp = self.timestamp) return self.rescaleParameter(P.T).T def setupApprox(self): """ Compute Pade' approximant. SVD-based robust eigenvalue management. """ if self.checkComputedApprox(): return if self.verbosity >= 5: verbosityDepth("INIT", "Setting up {}.". format(self.name()), timestamp = self.timestamp) self.computeDerivatives() if self.trainedModel is None: self.trainedModel = tModel() self.trainedModel.verbosity = self.verbosity self.trainedModel.timestamp = self.timestamp data = TrainedModelData(self.trainedModel.name(), self.mu0, None, self.HFEngine.rescalingExp) data.polytype = "MONOMIAL" self.trainedModel.data = data if self.N > 0: Q = self._setupDenominator() else: Q = np.ones(1, dtype = np.complex) self.trainedModel.data.Q = np.copy(Q) self.trainedModel.data.scaleFactor = self.scaleFactor self.trainedModel.data.projMat = ( self.samplingEngine.samples[:, : self.E + 1]) P = self._setupNumerator() if self.sampleType == "ARNOLDI": P = self.samplingEngine.RArnoldi.dot(P) self.trainedModel.data.P = np.copy(P) self.trainedModel.data.approxParameters = copy(self.approxParameters) if self.verbosity >= 5: verbosityDepth("DEL", "Done setting up approximant.", timestamp = self.timestamp) def rescaleParameter(self, R:Np2D, A : Np2D = None, exponent : float = 1.) -> Np2D: """ Prepare parameter rescaling. Args: R: Matrix whose columns need rescaling. A(optional): Matrix whose diagonal defines scaling factor. If None, previous value of scaleFactor is used. Defaults to None. exponent(optional): Exponent of scaling factor in matrix diagonal. Defaults to 1. Returns: Rescaled matrix. """ modeAssert(self._mode, message = "Cannot compute rescaling factor.") if A is not None: aDiag = np.diag(A) scaleCoeffs = np.polyfit(np.arange(A.shape[1]), np.log(aDiag), 1) self.scaleFactor = np.exp(- scaleCoeffs[0] / exponent) return np.multiply(R, np.power(self.scaleFactor,np.arange(R.shape[1]))) def buildG(self): """Assemble Pade' denominator matrix.""" modeAssert(self._mode, message = "Cannot compute G matrix.") self.computeDerivatives() if self.verbosity >= 10: verbosityDepth("INIT", "Building gramian matrix.", timestamp = self.timestamp) if self.rho == np.inf: Nmin = self.E - self.N else: Nmin = self.M - self.N + 1 if self.sampleType == "KRYLOV": DerE = self.samplingEngine.samples[:, Nmin : self.E + 1] G = self.HFEngine.innerProduct(DerE, DerE) DerE = self.rescaleParameter(DerE, G, 2.) G = self.HFEngine.innerProduct(DerE, DerE) else: RArnE = self.samplingEngine.RArnoldi[: self.E + 1, Nmin : self.E + 1] RArnE = self.rescaleParameter(RArnE, RArnE[Nmin :, :]) G = RArnE.T.conj().dot(RArnE) if self.rho == np.inf: self.G = G else: Gbig = G self.G = np.zeros((self.N + 1, self.N + 1), dtype = np.complex) for k in range(self.E - self.M): self.G += self.rho ** (2 * k) * Gbig[k : k + self.N + 1, k : k + self.N + 1] if self.verbosity >= 10: verbosityDepth("DEL", "Done building gramian.", timestamp = self.timestamp) def findeveVGExplicit(self) -> Tuple[Np1D, Np2D]: """ Compute explicitly eigenvalues and eigenvectors of Pade' denominator matrix. """ modeAssert(self._mode, message = "Cannot solve eigenvalue problem.") self.buildG() if self.verbosity >= 7: verbosityDepth("INIT", "Solving eigenvalue problem for gramian matrix.", timestamp = self.timestamp) ev, eV = np.linalg.eigh(self.G) if self.verbosity >= 5: try: condev = ev[-1] / ev[0] except: condev = np.inf verbosityDepth("MAIN", ("Solved eigenvalue problem of size {} " "with condition number {:.4e}.").format( self.N + 1, condev), timestamp = self.timestamp) if self.verbosity >= 7: verbosityDepth("DEL", "Done solving eigenvalue problem.", timestamp = self.timestamp) return ev, eV def findeveVGQR(self) -> Tuple[Np1D, Np2D]: """ Compute eigenvalues and eigenvectors of Pade' denominator matrix through SVD of R factor. See ``Householder triangularization of a quasimatrix'', L.Trefethen, 2008 for QR algorithm. Returns: Eigenvalues in ascending order and corresponding eigenvector matrix. """ modeAssert(self._mode, message = "Cannot solve eigenvalue problem.") self.computeDerivatives() if self.rho == np.inf: Nmin = self.E - self.N else: Nmin = self.M - self.N + 1 if self.sampleType == "KRYLOV": A = copy(self.samplingEngine.samples[:, Nmin : self.E + 1]) self.PODEngine = PODEngine(self.HFEngine) if self.verbosity >= 10: verbosityDepth("INIT", "Orthogonalizing samples.", timestamp = self.timestamp) R = self.PODEngine.QRHouseholder(A, only_R = True) if self.verbosity >= 10: verbosityDepth("DEL", "Done orthogonalizing samples.", timestamp = self.timestamp) else: R = self.samplingEngine.RArnoldi[: self.E + 1, Nmin : self.E + 1] R = self.rescaleParameter(R, R[R.shape[0] - R.shape[1] :, :]) if self.rho == np.inf: if self.verbosity >= 7: verbosityDepth("INIT", ("Solving svd for square root of " "gramian matrix."), timestamp = self.timestamp) sizeI = R.shape[0] _, s, V = np.linalg.svd(R, full_matrices = False) else: if self.verbosity >= 10: verbosityDepth("INIT", ("Building matrix stack for square " "root of gramian."), timestamp = self.timestamp) Rtower = np.zeros((R.shape[0] * (self.E - self.M), self.N + 1), dtype = np.complex) for k in range(self.E - self.M): RTleft = max(0, self.N - self.M - k) Rleft = max(0, self.M - self.N + k) Rtower[k * R.shape[0] : (k + 1) * R.shape[0], RTleft :] = ( self.rho ** k * R[:, Rleft : self.M + 1 + k]) if self.verbosity >= 10: verbosityDepth("DEL", "Done building matrix stack.", timestamp = self.timestamp) if self.verbosity >= 7: verbosityDepth("INIT", ("Solving svd for square root of " "gramian matrix."), timestamp = self.timestamp) sizeI = Rtower.shape[0] _, s, V = np.linalg.svd(Rtower, full_matrices = False) eV = V[::-1, :].T.conj() if self.verbosity >= 5: try: condev = s[0] / s[-1] except: condev = np.inf verbosityDepth("MAIN", ("Solved svd problem of size {} x {} with " "condition number {:.4e}.").format(sizeI, self.N + 1, condev), timestamp = self.timestamp) if self.verbosity >= 7: verbosityDepth("DEL", "Done solving eigenvalue problem.", timestamp = self.timestamp) return s[::-1], eV def radiusPade(self, mu:Np1D, mu0 : float = None) -> float: """ Compute translated radius to be plugged into Pade' approximant. Args: mu: Parameter(s) 1. mu0: Parameter(s) 2. If None, set to self.mu0. Returns: Translated radius to be plugged into Pade' approximant. """ return self.trainedModel.radiusPade(mu, mu0) def getResidues(self) -> Np1D: """ Obtain approximant residues. Returns: Matrix with residues as columns. """ return self.trainedModel.getResidues() diff --git a/rrompy/reduction_methods/taylor/approximant_taylor_rb.py b/rrompy/reduction_methods/centered/rb_centered.py similarity index 95% rename from rrompy/reduction_methods/taylor/approximant_taylor_rb.py rename to rrompy/reduction_methods/centered/rb_centered.py index 2d8a7e8..7d8a1c0 100644 --- a/rrompy/reduction_methods/taylor/approximant_taylor_rb.py +++ b/rrompy/reduction_methods/centered/rb_centered.py @@ -1,229 +1,230 @@ # Copyright (C) 2018 by the RROMPy authors # # This file is part of RROMPy. # # RROMPy is free software: you can redistribute it and/or modify # it under the terms of the GNU Lesser General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # RROMPy is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU Lesser General Public License for more details. # # You should have received a copy of the GNU Lesser General Public License # along with RROMPy. If not, see . # from copy import copy import numpy as np -from .generic_approximant_taylor import GenericApproximantTaylor +from .generic_centered_approximant import GenericCenteredApproximant from rrompy.reduction_methods.trained_model import TrainedModelRB as tModel from rrompy.reduction_methods.trained_model import TrainedModelData from rrompy.reduction_methods.base.rb_utils import projectAffineDecomposition from rrompy.sampling.base.pod_engine import PODEngine from rrompy.utilities.base.types import Np1D, Np2D, Tuple, List, DictAny, HFEng from rrompy.utilities.base import purgeDict, verbosityDepth from rrompy.utilities.exception_manager import RROMPyWarning -__all__ = ['ApproximantTaylorRB'] +__all__ = ['RBCentered'] -class ApproximantTaylorRB(GenericApproximantTaylor): +class RBCentered(GenericCenteredApproximant): """ ROM single-point fast RB approximant computation for parametric problems with polynomial dependence up to degree 2. Args: HFEngine: HF problem solver. mu0(optional): Default parameter. Defaults to 0. approxParameters(optional): Dictionary containing values for main parameters of approximant. Recognized keys are: - 'POD': whether to compute POD of snapshots; defaults to True; - 'R': rank for Galerkin projection; defaults to E + 1; - 'E': total number of derivatives current approximant relies upon; defaults to 1; - 'sampleType': label of sampling type; available values are: - 'ARNOLDI': orthogonalization of solution derivatives through Arnoldi algorithm; - 'KRYLOV': standard computation of solution derivatives. Defaults to 'KRYLOV'. Defaults to empty dict. - homogeneized: Whether to homogeneize Dirichlet BCs. Defaults to False. + 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. parameterList: Recognized keys of approximant parameters: - 'POD': whether to compute POD of snapshots; - 'R': rank for Galerkin projection; - 'E': total number of derivatives current approximant relies upon; - 'sampleType': label of sampling type. POD: Whether to compute QR factorization of derivatives. R: Rank for Galerkin projection. E: Number of solution derivatives over which current approximant is based upon. sampleType: Label of sampling type, i.e. 'KRYLOV'. uHF: High fidelity solution with wavenumber lastSolvedHF as numpy complex vector. lastSolvedHF: Wavenumber corresponding to last computed high fidelity solution. uApp: Last evaluated approximant as numpy complex vector. ARBs: List of sparse matrices (in CSC format) representing RB coefficients of linear system matrix wrt mu. bRBs: List of numpy vectors representing RB coefficients of linear system RHS wrt mu. """ def __init__(self, HFEngine:HFEng, mu0 : complex = 0, approxParameters : DictAny = {}, homogeneized : bool = False, verbosity : int = 10, timestamp : bool = True): self._preInit() self._addParametersToList(["R"]) super().__init__(HFEngine = HFEngine, mu0 = mu0, approxParameters = approxParameters, homogeneized = homogeneized, verbosity = verbosity, timestamp = timestamp) if self.verbosity >= 10: verbosityDepth("INIT", "Computing affine blocks of system.", timestamp = self.timestamp) if self.verbosity >= 10: verbosityDepth("DEL", "Done computing affine blocks.", timestamp = self.timestamp) self._postInit() @property def approxParameters(self): """ Value of approximant parameters. Its assignment may change M, N and S. """ return self._approxParameters @approxParameters.setter def approxParameters(self, approxParams): approxParameters = purgeDict(approxParams, self.parameterList, dictname = self.name() + ".approxParameters", baselevel = 1) approxParametersCopy = purgeDict(approxParameters, ["R"], True, True, baselevel = 1) - GenericApproximantTaylor.approxParameters.fset(self, - approxParametersCopy) + GenericCenteredApproximant.approxParameters.fset(self, + approxParametersCopy) keyList = list(approxParameters.keys()) if "R" in keyList: self.R = approxParameters["R"] else: self.R = self.E + 1 @property def POD(self): """Value of POD.""" return self._POD @POD.setter def POD(self, POD): - GenericApproximantTaylor.POD.fset(self, POD) + GenericCenteredApproximant.POD.fset(self, POD) if (hasattr(self, "_sampleType") and self.sampleType == "ARNOLDI" and not self.POD): RROMPyWarning(("Arnoldi sampling implicitly forces POD-type " "derivative management.")) @property def sampleType(self): """Value of sampleType.""" return self._sampleType @sampleType.setter def sampleType(self, sampleType): - GenericApproximantTaylor.sampleType.fset(self, sampleType) + GenericCenteredApproximant.sampleType.fset(self, sampleType) if (hasattr(self, "_POD") and not self.POD and self.sampleType == "ARNOLDI"): RROMPyWarning(("Arnoldi sampling implicitly forces POD-type " "derivative management.")) @property def R(self): """Value of R. Its assignment may change S.""" return self._R @R.setter def R(self, R): if R < 0: raise RROMPyException("R must be non-negative.") self._R = R self._approxParameters["R"] = self.R if hasattr(self, "_E") and self.E + 1 < self.R: RROMPyWarning("Prescribed E is too small. Updating E to R - 1.") self.E = self.R - 1 def setupApprox(self): """Setup RB system.""" if self.checkComputedApprox(): return if self.verbosity >= 5: verbosityDepth("INIT", "Setting up {}.". format(self.name()), timestamp = self.timestamp) self.computeDerivatives() if self.verbosity >= 7: verbosityDepth("INIT", "Computing projection matrix.", timestamp = self.timestamp) if self.POD and not self.sampleType == "ARNOLDI": self.PODEngine = PODEngine(self.HFEngine) pMatQ, pMatR = self.PODEngine.QRHouseholder( self.samplingEngine.samples) if self.POD: if self.sampleType == "ARNOLDI": pMatR = self.samplingEngine.RArnoldi pMatQ = self.samplingEngine.samples U, _, _ = np.linalg.svd(pMatR[: self.E + 1, : self.E + 1]) pMat = pMatQ[:, : self.E + 1].dot(U[:, : self.R]) else: pMat = self.samplingEngine.samples[:, : self.R] if self.trainedModel is None: self.trainedModel = tModel() self.trainedModel.verbosity = self.verbosity self.trainedModel.timestamp = self.timestamp data = TrainedModelData(self.trainedModel.name(), self.mu0, np.copy(pMat), self.HFEngine.rescalingExp) data.thetaAs = self.HFEngine.affineWeightsA(self.mu0) data.thetabs = self.HFEngine.affineWeightsb(self.mu0, self.homogeneized) data.ARBs, data.bRBs = self.assembleReducedSystem(pMat) self.trainedModel.data = data else: pMatOld = self.trainedModel.data.projMat Sold = pMatOld.shape[1] ARBs, bRBs = self.assembleReducedSystem(pMat[:, Sold :], pMatOld) self.trainedModel.data.ARBs = ARBs self.trainedModel.data.bRBs = bRBs self.trainedModel.data.projMat = np.copy(pMat) if self.verbosity >= 7: verbosityDepth("DEL", "Done computing projection matrix.", timestamp = self.timestamp) self.trainedModel.data.approxParameters = copy(self.approxParameters) if self.verbosity >= 5: verbosityDepth("DEL", "Done setting up approximant.", timestamp = self.timestamp) def assembleReducedSystem(self, pMat : Np2D = None, pMatOld : Np2D = None)\ -> Tuple[List[Np2D], List[Np1D]]: """Build affine blocks of RB linear system through projections.""" if pMat is None: self.setupApprox() ARBs = self.trainedModel.data.ARBs bRBs = self.trainedModel.data.bRBs else: if self.verbosity >= 10: verbosityDepth("INIT", "Projecting affine terms of HF model.", timestamp = self.timestamp) As = self.HFEngine.affineLinearSystemA(self.mu0) bs = self.HFEngine.affineLinearSystemb(self.mu0, self.homogeneized) ARBsOld = None if pMatOld is None else self.trainedModel.data.ARBs bRBsOld = None if pMatOld is None else self.trainedModel.data.bRBs ARBs, bRBs = projectAffineDecomposition(As, bs, pMat, ARBsOld, bRBsOld, pMatOld) if self.verbosity >= 10: verbosityDepth("DEL", "Done projecting affine terms.", timestamp = self.timestamp) return ARBs, bRBs diff --git a/rrompy/reduction_methods/lagrange/__init__.py b/rrompy/reduction_methods/distributed/__init__.py similarity index 71% copy from rrompy/reduction_methods/lagrange/__init__.py copy to rrompy/reduction_methods/distributed/__init__.py index 822bd2a..1d7c1fc 100644 --- a/rrompy/reduction_methods/lagrange/__init__.py +++ b/rrompy/reduction_methods/distributed/__init__.py @@ -1,29 +1,29 @@ # Copyright (C) 2018 by the RROMPy authors # # This file is part of RROMPy. # # RROMPy is free software: you can redistribute it and/or modify # it under the terms of the GNU Lesser General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # RROMPy is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU Lesser General Public License for more details. # # You should have received a copy of the GNU Lesser General Public License # along with RROMPy. If not, see . # -from .generic_approximant_lagrange import GenericApproximantLagrange -from .approximant_lagrange_pade import ApproximantLagrangePade -from .approximant_lagrange_rb import ApproximantLagrangeRB +from .generic_distributed_approximant import GenericDistributedApproximant +from .rational_interpolant import RationalInterpolant +from .rb_distributed import RBDistributed __all__ = [ - 'GenericApproximantLagrange', - 'ApproximantLagrangePade', - 'ApproximantLagrangeRB' + 'GenericDistributedApproximant', + 'RationalInterpolant', + 'RBDistributed' ] diff --git a/rrompy/reduction_methods/lagrange/generic_approximant_lagrange.py b/rrompy/reduction_methods/distributed/generic_distributed_approximant.py similarity index 74% rename from rrompy/reduction_methods/lagrange/generic_approximant_lagrange.py rename to rrompy/reduction_methods/distributed/generic_distributed_approximant.py index b5239b7..bee5200 100644 --- a/rrompy/reduction_methods/lagrange/generic_approximant_lagrange.py +++ b/rrompy/reduction_methods/distributed/generic_distributed_approximant.py @@ -1,195 +1,218 @@ # Copyright (C) 2018 by the RROMPy authors # # This file is part of RROMPy. # # RROMPy is free software: you can redistribute it and/or modify # it under the terms of the GNU Lesser General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # RROMPy is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU Lesser General Public License for more details. # # You should have received a copy of the GNU Lesser General Public License # along with RROMPy. If not, see . # import numpy as np from rrompy.reduction_methods.base.generic_approximant import ( GenericApproximant) +from rrompy.sampling.linear_problem.sampling_engine_distributed import \ + SamplingEngineDistributed +from rrompy.sampling.linear_problem.sampling_engine_distributed_pod import \ + SamplingEngineDistributedPOD from rrompy.utilities.base.types import DictAny, HFEng from rrompy.utilities.base import purgeDict, verbosityDepth from rrompy.utilities.exception_manager import RROMPyException, modeAssert -__all__ = ['GenericApproximantLagrange'] +__all__ = ['GenericDistributedApproximant'] -class GenericApproximantLagrange(GenericApproximant): +class GenericDistributedApproximant(GenericApproximant): """ - ROM Lagrange interpolant computation for parametric problems (ABSTRACT). + 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. + - 'muBounds': list of bounds for parameter values; defaults to + [0, 1]; + - 'S': total number of samples current approximant relies upon; + defaults to 2; + - 'sampler': sample point generator; defaults to uniform sampler on + muBounds. Defaults to empty dict. - homogeneized: Whether to homogeneize Dirichlet BCs. Defaults to False. + 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. ws: Array of snapshot weigths. homogeneized: Whether to homogeneize Dirichlet BCs. approxParameters: Dictionary containing values for main parameters of approximant. Recognized keys are in parameterList. parameterList: Recognized keys of approximant parameters: - 'POD': whether to compute POD of snapshots; - - 'S': total number of snapshots current approximant relies upon. + - 'muBounds': list of bounds for parameter values; + - 'S': total number of samples current approximant relies upon; + - 'sampler': sample point generator. extraApproxParameters: List of approxParameters keys in addition to mother class's. + POD: Whether to compute POD of snapshots. + muBounds: list of bounds for parameter values. S: Number of solution snapshots over which current approximant is based upon. sampler: Sample point generator. - POD: Whether to compute POD of snapshots. samplingEngine: Sampling engine. uHF: High fidelity solution with wavenumber lastSolvedHF as numpy complex vector. lastSolvedHF: Wavenumber corresponding to last computed high fidelity solution. uApp: Last evaluated approximant as numpy complex vector. """ def __init__(self, HFEngine:HFEng, mu0 : complex = 0., approxParameters : DictAny = {}, homogeneized : bool = False, verbosity : int = 10, timestamp : bool = True): self._preInit() - self._addParametersToList(["S"]) + self._addParametersToList(["S", "muBounds", "sampler"]) super().__init__(HFEngine = HFEngine, mu0 = mu0, approxParameters = approxParameters, homogeneized = homogeneized, verbosity = verbosity, timestamp = timestamp) - from rrompy.utilities.parameter_sampling import QuadratureSampler - self.sampler = QuadratureSampler([0., 1.], "UNIFORM") - del QuadratureSampler self._postInit() def setupSampling(self): """Setup sampling engine.""" modeAssert(self._mode, message = "Cannot setup sampling engine.") if not hasattr(self, "_POD") or self._POD is None: return if self.POD: - from rrompy.sampling.linear_problem.sampling_engine_lagrange_pod \ - import SamplingEngineLagrangePOD - super().setupSampling(SamplingEngineLagrangePOD) + super().setupSampling(SamplingEngineDistributedPOD) else: - from rrompy.sampling.linear_problem.sampling_engine_lagrange \ - import SamplingEngineLagrange - super().setupSampling(SamplingEngineLagrange) + super().setupSampling(SamplingEngineDistributed) @property def mus(self): """Value of mus. Its assignment may reset snapshots.""" return self._mus @mus.setter def mus(self, mus): musOld = self.mus if hasattr(self, '_mus') else None self._mus = np.array(mus) -# _, musCounts = np.unique(self._mus, return_counts = True) -# if len(np.where(musCounts > 1)[0]) > 0: -# raise RROMPyException("Repeated sample points not allowed.") if (musOld is None or len(self.mus) != len(musOld) or not np.allclose(self.mus, musOld, 1e-14)): self.resetSamples() self.autoNode = None @property def approxParameters(self): """Value of approximant parameters. Its assignment may change S.""" return self._approxParameters @approxParameters.setter def approxParameters(self, approxParams): approxParameters = purgeDict(approxParams, self.parameterList, dictname = self.name() + ".approxParameters", baselevel = 1) - approxParametersCopy = purgeDict(approxParameters, ["S"], + approxParametersCopy = purgeDict(approxParameters, + ["S", "muBounds", "sampler"], True, True, baselevel = 1) GenericApproximant.approxParameters.fset(self, approxParametersCopy) keyList = list(approxParameters.keys()) if "S" in keyList: self.S = approxParameters["S"] elif not hasattr(self, "_S") or self._S is None: self.S = 2 + if "muBounds" in keyList: + self.muBounds = approxParameters["muBounds"] + elif not hasattr(self, "_muBounds") or self.muBounds is None: + self.muBounds = [0., 1.] + if "sampler" in keyList: + self.sampler = approxParameters["sampler"] + elif (not hasattr(self, "_sampler") or self.sampler is None): + from rrompy.utilities.parameter_sampling import QuadratureSampler + self.sampler = QuadratureSampler(self.muBounds, "UNIFORM") + del QuadratureSampler @property def S(self): """Value of S.""" return self._S @S.setter def S(self, S): if S <= 0: raise RROMPyException("S must be positive.") if hasattr(self, "_S") and self._S is not None: Sold = self.S else: Sold = -1 self._S = S self._approxParameters["S"] = self.S if Sold != self.S: self.resetSamples() + @property + def muBounds(self): + """Value of muBounds.""" + return self._muBounds + @muBounds.setter + def muBounds(self, muBounds): + if len(muBounds) != 2: + raise RROMPyException("2 limits must be specified.") + self._muBounds = list(muBounds) + @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 computeSnapshots(self): """Compute snapshots of solution map.""" modeAssert(self._mode, message = "Cannot start snapshot computation.") if self.samplingEngine.samples is None: if self.verbosity >= 5: verbosityDepth("INIT", "Starting computation of snapshots.", timestamp = self.timestamp) self.mus, self.ws = 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:complex, homogeneized : bool = False) -> float: """ Compute norm of approximant at arbitrary parameter. Args: mu: Target parameter. homogeneized(optional): Whether to remove Dirichlet BC. Defaults to False. Returns: Target norm of approximant. """ if not self.POD or self.homogeneized != homogeneized: return super().normApprox(mu, homogeneized) return np.linalg.norm(self.getApproxReduced(mu)) def computeScaleFactor(self): """Compute parameter rescaling factor.""" modeAssert(self._mode, message = "Cannot compute rescaling factor.") - self.scaleFactor = .5 * np.abs(np.power(self.sampler.lims[0], - self.HFEngine.rescalingExp) - - np.power(self.sampler.lims[1], - self.HFEngine.rescalingExp)) + self.scaleFactor = .5 * np.abs( + np.power(self.muBounds[0], self.HFEngine.rescalingExp) + - np.power(self.muBounds[1], self.HFEngine.rescalingExp)) diff --git a/rrompy/reduction_methods/lagrange/approximant_lagrange_pade.py b/rrompy/reduction_methods/distributed/rational_interpolant.py similarity index 96% rename from rrompy/reduction_methods/lagrange/approximant_lagrange_pade.py rename to rrompy/reduction_methods/distributed/rational_interpolant.py index 8a1a2e1..554c8c0 100644 --- a/rrompy/reduction_methods/lagrange/approximant_lagrange_pade.py +++ b/rrompy/reduction_methods/distributed/rational_interpolant.py @@ -1,519 +1,525 @@ # Copyright (C) 2018 by the RROMPy authors # # This file is part of RROMPy. # # RROMPy is free software: you can redistribute it and/or modify # it under the terms of the GNU Lesser General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # RROMPy is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU Lesser General Public License for more details. # # You should have received a copy of the GNU Lesser General Public License # along with RROMPy. If not, see . # from copy import copy import numpy as np from scipy.special import factorial as fact from rrompy.reduction_methods.base import checkRobustTolerance -from .generic_approximant_lagrange import GenericApproximantLagrange +from .generic_distributed_approximant import GenericDistributedApproximant from rrompy.utilities.poly_fitting import (polybases, polyvander, polyfitname, customFit) from rrompy.reduction_methods.trained_model import TrainedModelPade as tModel from rrompy.reduction_methods.trained_model import TrainedModelData from rrompy.utilities.base.types import Np1D, Np2D, HFEng, DictAny, Tuple from rrompy.utilities.base import verbosityDepth, purgeDict from rrompy.utilities.exception_manager import (RROMPyException, modeAssert, RROMPyWarning) -__all__ = ['ApproximantLagrangePade'] +__all__ = ['RationalInterpolant'] -class ApproximantLagrangePade(GenericApproximantLagrange): +class RationalInterpolant(GenericDistributedApproximant): """ - ROM Lagrange Pade' interpolant computation for parametric problems. + ROM rational interpolant computation for parametric problems. Args: HFEngine: HF problem solver. mu0(optional): Default parameter. Defaults to 0. approxParameters(optional): Dictionary containing values for main parameters of approximant. Recognized keys are: - 'POD': whether to compute POD of snapshots; defaults to True; + - 'muBounds': list of bounds for parameter values; defaults to + [0, 1]; - 'S': total number of samples current approximant relies upon; defaults to 2; - 'sampler': sample point generator; defaults to uniform sampler on - [0, 1]; + muBounds; - 'polybasis': type of polynomial basis for interpolation; allowed values include 'MONOMIAL', 'CHEBYSHEV' and 'LEGENDRE'; defaults to 'MONOMIAL'; - 'E': coefficient of interpolant to be minimized; defaults to min(S, M + 1); - 'M': degree of Pade' interpolant numerator; defaults to 0; - 'N': degree of Pade' interpolant denominator; defaults to 0; - 'interpRcond': tolerance for interpolation; defaults to None; - 'robustTol': tolerance for robust Pade' denominator management; defaults to 0. Defaults to empty dict. - homogeneized: Whether to homogeneize Dirichlet BCs. Defaults to False. + 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. ws: Array of snapshot weigths. homogeneized: Whether to homogeneize Dirichlet BCs. approxParameters: Dictionary containing values for main parameters of approximant. Recognized keys are in parameterList. parameterList: Recognized keys of approximant parameters: - 'POD': whether to compute POD of snapshots; + - 'muBounds': list of bounds for parameter values; - 'S': total number of samples current approximant relies upon; - 'sampler': sample point generator; - 'polybasis': type of polynomial basis for interpolation; - 'E': coefficient of interpolant to be minimized; - 'M': degree of Pade' interpolant numerator; - 'N': degree of Pade' interpolant denominator; - 'interpRcond': tolerance for interpolation via numpy.polyfit; - 'robustTol': tolerance for robust Pade' denominator management. extraApproxParameters: List of approxParameters keys in addition to mother class's. + POD: Whether to compute POD of snapshots. + muBounds: list of bounds for parameter values. S: Number of solution snapshots over which current approximant is based upon. sampler: Sample point generator. + polybasis: type of polynomial basis for interpolation. M: Numerator degree of approximant. N: Denominator degree of approximant. - POD: Whether to compute POD of snapshots. interpRcond: Tolerance for interpolation via numpy.polyfit. robustTol: Tolerance for robust Pade' denominator management. samplingEngine: Sampling engine. uHF: High fidelity solution with wavenumber lastSolvedHF as numpy complex vector. lastSolvedHF: Wavenumber corresponding to last computed high fidelity solution. Q: Numpy 1D vector containing complex coefficients of approximant denominator. P: Numpy 2D vector whose columns are FE dofs of coefficients of approximant numerator. uApp: Last evaluated approximant as numpy complex vector. """ def __init__(self, HFEngine:HFEng, mu0 : complex = 0., approxParameters : DictAny = {}, homogeneized : bool = False, verbosity : int = 10, timestamp : bool = True): self._preInit() self._addParametersToList(["polybasis", "E", "M", "N", "interpRcond", "robustTol"]) super().__init__(HFEngine = HFEngine, mu0 = mu0, approxParameters = approxParameters, homogeneized = homogeneized, verbosity = verbosity, timestamp = timestamp) self._postInit() @property def approxParameters(self): """ Value of approximant parameters. """ return self._approxParameters @approxParameters.setter def approxParameters(self, approxParams): approxParameters = purgeDict(approxParams, self.parameterList, dictname = self.name() + ".approxParameters", baselevel = 1) approxParametersCopy = purgeDict(approxParameters, ["polybasis", "E", "M", "N", "interpRcond", "robustTol"], True, True, baselevel = 1) if hasattr(self, "_M") and self.M is not None: Mold = self.M self._M = 0 if hasattr(self, "_N") and self.N is not None: Nold = self.N self._N = 0 if hasattr(self, "_E") and self.E is not None: self._E = 0 - GenericApproximantLagrange.approxParameters.fset(self, - approxParametersCopy) + GenericDistributedApproximant.approxParameters.fset(self, + approxParametersCopy) keyList = list(approxParameters.keys()) if "polybasis" in keyList: self.polybasis = approxParameters["polybasis"] elif not hasattr(self, "_polybasis") or self._polybasis is None: self.polybasis = "MONOMIAL" if "interpRcond" in keyList: self.interpRcond = approxParameters["interpRcond"] elif not hasattr(self, "interpRcond") or self.interpRcond is None: self.interpRcond = None if "robustTol" in keyList: self.robustTol = approxParameters["robustTol"] elif not hasattr(self, "_robustTol") or self._robustTol is None: self.robustTol = 0 if "M" in keyList: self.M = approxParameters["M"] elif hasattr(self, "_M") and self.M is not None: self.M = Mold else: self.M = 0 if "N" in keyList: self.N = approxParameters["N"] elif hasattr(self, "_N") and self.N is not None: self.N = Nold else: self.N = 0 if "E" in keyList: self.E = approxParameters["E"] else: self.E = min(self.S - 1, self.M + 1) @property def polybasis(self): """Value of polybasis.""" return self._polybasis @polybasis.setter def polybasis(self, polybasis): try: polybasis = polybasis.upper().strip().replace(" ","") if polybasis not in polybases: raise RROMPyException("Prescribed polybasis not recognized.") self._polybasis = polybasis except: RROMPyWarning(("Prescribed polybasis not recognized. Overriding " "to 'MONOMIAL'.")) self._sampleType = "MONOMIAL" self._approxParameters["polybasis"] = self.polybasis @property def interpRcond(self): """Value of interpRcond.""" return self._interpRcond @interpRcond.setter def interpRcond(self, interpRcond): self._interpRcond = interpRcond self._approxParameters["interpRcond"] = self.interpRcond @property def M(self): """Value of M. Its assignment may change S.""" return self._M @M.setter def M(self, M): if M < 0: raise RROMPyException("M must be non-negative.") self._M = M self._approxParameters["M"] = self.M if hasattr(self, "_S") and self.S < self.M + 1: RROMPyWarning("Prescribed S is too small. Updating S to M + 1.") self.S = self.M + 1 @property def N(self): """Value of N. Its assignment may change S.""" return self._N @N.setter def N(self, N): if N < 0: raise RROMPyException("N must be non-negative.") self._N = N self._approxParameters["N"] = self.N if hasattr(self, "_S") and self.S < self.N + 1: RROMPyWarning("Prescribed S is too small. Updating S to N + 1.") self.S = self.N + 1 @property def E(self): """Value of E. Its assignment may change S.""" return self._E @E.setter def E(self, E): if E < 0: raise RROMPyException("E must be non-negative.") self._E = E self._approxParameters["E"] = self.E if hasattr(self, "_S") and self.S < self.E + 1: RROMPyWarning("Prescribed S is too small. Updating S to E + 1.") self.S = self.E + 1 @property def robustTol(self): """Value of tolerance for robust Pade' denominator management.""" return self._robustTol @robustTol.setter def robustTol(self, robustTol): if robustTol < 0.: RROMPyWarning(("Overriding prescribed negative robustness " "tolerance to 0.")) robustTol = 0. self._robustTol = robustTol self._approxParameters["robustTol"] = self.robustTol @property def S(self): """Value of S.""" return self._S @S.setter def S(self, S): if S <= 0: raise RROMPyException("S must be positive.") if hasattr(self, "_S"): Sold = self.S else: Sold = -1 vals, label = [0] * 3, {0:"M", 1:"N", 2:"E"} if hasattr(self, "_M") and self._M is not None: vals[0] = self.M if hasattr(self, "_N") and self._N is not None: vals[1] = self.N if hasattr(self, "_E") and self._E is not None: vals[2] = self.E idxmax = np.argmax(vals) if vals[idxmax] + 1 > S: RROMPyWarning(("Prescribed S is too small. Updating S to {} + " "1.").format(label[idxmax])) self.S = vals[idxmax] + 1 else: self._S = S self._approxParameters["S"] = self.S if Sold != self.S: self.resetSamples() def _setupDenominator(self): """Compute Pade' denominator.""" if self.verbosity >= 7: verbosityDepth("INIT", "Starting computation of denominator.", timestamp = self.timestamp) while self.N > 0: TE = polyvander[self.polybasis](self.radiusPade(self.mus), self.E, scl = 1. / self.scaleFactor) TE = (TE.T * self.ws).T RHS = np.zeros(self.E + 1) RHS[-1] = 1. fitOut = customFit(TE.T, RHS, full = True, rcond = self.interpRcond) if self.verbosity >= 5: condfit = fitOut[1][2][0] / fitOut[1][2][-1] verbosityDepth("MAIN", ("Fitting {} samples with degree {} " "through {}... Conditioning of LS " "system: {:.4e}.").format( self.S, self.E, polyfitname[self.polybasis], condfit), timestamp = self.timestamp) if fitOut[1][1] < self.E + 1: Enew = fitOut[1][1] - 1 Nnew = min(self.N, Enew) Mnew = min(self.M, Enew) if Nnew == self.N: strN = "" else: strN = "N from {} to {} and ".format(self.N, Nnew) if Mnew == self.M: strM = "" else: strM = "M from {} to {} and ".format(self.M, Mnew) RROMPyWarning(("Polyfit is poorly conditioned.\nReducing {}{}" "E from {} to {}.").format(strN, strM, self.E, Enew)) newParams = {"N" : Nnew, "M" : Mnew, "E" : Enew} self.approxParameters = newParams continue mus_un, idx_un, cnt_un = np.unique(self.mus, return_inverse = True, return_counts = True) TE = polyvander[self.polybasis](self.radiusPade(self.mus), self.N, scl = 1. / self.scaleFactor) TE = (TE.T * self.ws).T if len(mus_un) == len(self.mus): Ghalf = (TE.T * fitOut[0]).T else: pseudoInv = np.zeros((len(self.mus), len(self.mus)), dtype = np.complex) for j in range(len(mus_un)): pseudoInv_loc = np.zeros((cnt_un[j], cnt_un[j]), dtype = np.complex) mask = np.arange(len(self.mus))[idx_un == j] for der in range(cnt_un[j]): fitderj = fitOut[0][mask[der]] pseudoInv_loc = (pseudoInv_loc + fitderj * np.diag(np.ones(1 + der), k = der - cnt_un[j] + 1)) I = np.ix_(mask, mask) pseudoInv[I] = np.flipud(pseudoInv_loc) Ghalf = pseudoInv.dot(TE) if self.POD: self.Ghalf = self.samplingEngine.RPOD.dot(Ghalf) ev, eV = self.findeveVGQR() else: self.Ghalf = self.samplingEngine.samples.dot(Ghalf) ev, eV = self.findeveVGExplicit() newParams = checkRobustTolerance(ev, self.E, self.robustTol) if not newParams: break self.approxParameters = newParams if self.N <= 0: self._N = 0 eV = np.ones((1, 1)) if self.verbosity >= 7: verbosityDepth("DEL", "Done computing denominator.", timestamp = self.timestamp) return eV[:, 0] def _setupNumerator(self): """Compute Pade' numerator.""" if self.verbosity >= 7: verbosityDepth("INIT", "Starting computation of numerator.", timestamp = self.timestamp) Qevaldiag = np.diag(self.trainedModel.getQVal(self.mus)) verb = self.trainedModel.verbosity self.trainedModel.verbosity = 0 mus_un, idx_un, cnt_un = np.unique(self.mus, return_inverse = True, return_counts = True) for j in range(len(mus_un)): if cnt_un[j] > 1: Q_loc = np.zeros((cnt_un[j], cnt_un[j]), dtype = np.complex) for der in range(1, cnt_un[j]): Qderj = (self.trainedModel.getQVal(mus_un[j], der, scl = 1. / self.scaleFactor) / fact(der)) Q_loc = Q_loc + Qderj * np.diag(np.ones(cnt_un[j] - der), k = - der) I = np.ix_(idx_un == j, idx_un == j) Qevaldiag[I] = Qevaldiag[I] + Q_loc self.trainedModel.verbosity = verb while self.M >= 0: fitVander = polyvander[self.polybasis](self.radiusPade(self.mus), self.M, scl = 1. / self.scaleFactor) fitOut = customFit(fitVander, Qevaldiag, w = self.ws, full = True, rcond = self.interpRcond) if self.verbosity >= 5: condfit = fitOut[1][2][0] / fitOut[1][2][-1] verbosityDepth("MAIN", ("Fitting {} samples with degree {} " "through {}... Conditioning of LS " "system: {:.4e}.").format( self.S, self.M, polyfitname[self.polybasis], condfit), timestamp = self.timestamp) if fitOut[1][1] == self.M + 1: P = fitOut[0].T break RROMPyWarning(("Polyfit is poorly conditioned. Reducing M from {} " "to {}. Exact snapshot interpolation not " "guaranteed.").format(self.M, fitOut[1][1] - 1)) self.M = fitOut[1][1] - 1 if self.M <= 0: raise RROMPyException(("Instability in computation of numerator. " "Aborting.")) if self.verbosity >= 7: verbosityDepth("DEL", "Done computing numerator.", timestamp = self.timestamp) return np.atleast_2d(P) def setupApprox(self): """ Compute Pade' interpolant. SVD-based robust eigenvalue management. """ if self.checkComputedApprox(): return modeAssert(self._mode, message = "Cannot setup approximant.") if self.verbosity >= 5: verbosityDepth("INIT", "Setting up {}.". format(self.name()), timestamp = self.timestamp) self.computeScaleFactor() self.computeSnapshots() if self.trainedModel is None: self.trainedModel = tModel() self.trainedModel.verbosity = self.verbosity self.trainedModel.timestamp = self.timestamp data = TrainedModelData(self.trainedModel.name(), self.mu0, np.copy(self.samplingEngine.samples), self.HFEngine.rescalingExp) data.polytype = self.polybasis data.scaleFactor = self.scaleFactor data.mus = np.copy(self.mus) self.trainedModel.data = data else: self.trainedModel.data.projMat = np.copy( self.samplingEngine.samples) if self.N > 0: Q = self._setupDenominator() else: Q = np.ones(1, dtype = np.complex) self.trainedModel.data.Q = np.copy(Q) P = self._setupNumerator() if self.POD: P = self.samplingEngine.RPOD.dot(P) self.trainedModel.data.P = np.copy(P) self.trainedModel.data.approxParameters = copy(self.approxParameters) if self.verbosity >= 5: verbosityDepth("DEL", "Done setting up approximant.", timestamp = self.timestamp) def findeveVGExplicit(self, verbOutput : int = 5) -> Tuple[Np1D, Np2D]: """ Compute explicitly eigenvalues and eigenvectors of Pade' denominator matrix. """ modeAssert(self._mode, message = "Cannot solve eigenvalue problem.") if self.verbosity >= 10: verbosityDepth("INIT", "Building gramian matrix.", timestamp = self.timestamp) self.G = self.HFEngine.innerProduct(self.Ghalf, self.Ghalf) if self.verbosity >= 10: verbosityDepth("DEL", "Done building gramian.", timestamp = self.timestamp) if self.verbosity >= 7: verbosityDepth("INIT", ("Solving eigenvalue problem for gramian " "matrix."), timestamp = self.timestamp) ev, eV = np.linalg.eigh(self.G) if self.verbosity >= verbOutput: try: condev = ev[-1] / ev[0] except: condev = np.inf verbosityDepth("MAIN", ("Solved eigenvalue problem of size {} " "with condition number {:.4e}.").format( self.N + 1, condev), timestamp = self.timestamp) if self.verbosity >= 7: verbosityDepth("DEL", "Done solving eigenvalue problem.", timestamp = self.timestamp) return ev, eV def findeveVGQR(self, verbOutput : int = 5) -> Tuple[Np1D, Np2D]: """ Compute eigenvalues and eigenvectors of Pade' denominator matrix through SVD of R factor. """ if self.verbosity >= 7: verbosityDepth("INIT", ("Solving svd for square root of gramian " "matrix."), timestamp = self.timestamp) _, s, eV = np.linalg.svd(self.Ghalf, full_matrices = False) ev = s[::-1] eV = eV[::-1, :].T.conj() if self.verbosity >= verbOutput: try: condev = s[0] / s[-1] except: condev = np.inf verbosityDepth("MAIN", ("Solved svd problem of size {} x {} with " "condition number {:.4e}.").format( self.S, self.N + 1, condev), timestamp = self.timestamp) if self.verbosity >= 7: verbosityDepth("DEL", "Done solving eigenvalue problem.", timestamp = self.timestamp) return ev, eV def radiusPade(self, mu:Np1D, mu0 : float = None) -> float: """ Compute translated radius to be plugged into Pade' approximant. Args: mu: Parameter(s) 1. mu0: Parameter(s) 2. If None, set to self.mu0. Returns: Translated radius to be plugged into Pade' approximant. """ return self.trainedModel.radiusPade(mu, mu0) def getResidues(self) -> Np1D: """ Obtain approximant residues. Returns: Matrix with residues as columns. """ return self.trainedModel.getResidues() diff --git a/rrompy/reduction_methods/lagrange/approximant_lagrange_rb.py b/rrompy/reduction_methods/distributed/rb_distributed.py similarity index 93% rename from rrompy/reduction_methods/lagrange/approximant_lagrange_rb.py rename to rrompy/reduction_methods/distributed/rb_distributed.py index 978f620..7369320 100644 --- a/rrompy/reduction_methods/lagrange/approximant_lagrange_rb.py +++ b/rrompy/reduction_methods/distributed/rb_distributed.py @@ -1,212 +1,216 @@ # Copyright (C) 2018 by the RROMPy authors # # This file is part of RROMPy. # # RROMPy is free software: you can redistribute it and/or modify # it under the terms of the GNU Lesser General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # RROMPy is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU Lesser General Public License for more details. # # You should have received a copy of the GNU Lesser General Public License # along with RROMPy. If not, see . # from copy import copy import numpy as np -from .generic_approximant_lagrange import GenericApproximantLagrange +from .generic_distributed_approximant import GenericDistributedApproximant from rrompy.reduction_methods.trained_model import TrainedModelRB as tModel from rrompy.reduction_methods.trained_model import TrainedModelData from rrompy.reduction_methods.base.rb_utils import projectAffineDecomposition from rrompy.utilities.base.types import Np1D, Np2D, List, Tuple, DictAny, HFEng from rrompy.utilities.base import purgeDict, verbosityDepth from rrompy.utilities.exception_manager import RROMPyWarning, RROMPyException -__all__ = ['ApproximantLagrangeRB'] +__all__ = ['RBDistributed'] -class ApproximantLagrangeRB(GenericApproximantLagrange): +class RBDistributed(GenericDistributedApproximant): """ ROM RB approximant computation for parametric problems. Args: HFEngine: HF problem solver. mu0(optional): Default parameter. Defaults to 0. approxParameters(optional): Dictionary containing values for main parameters of approximant. Recognized keys are: - - 'POD': whether to compute POD of snapshots; defaults to True; + - 'muBounds': list of bounds for parameter values; defaults to + [0, 1]; - 'S': total number of samples current approximant relies upon; defaults to 2; - 'sampler': sample point generator; defaults to uniform sampler on - [0, 1]; + muBounds; - 'R': rank for Galerkin projection; defaults to S. Defaults to empty dict. - homogeneized: Whether to homogeneize Dirichlet BCs. Defaults to False. + 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. ws: Array of snapshot weigths (unused). homogeneized: Whether to homogeneize Dirichlet BCs. approxRadius: Dummy radius of approximant (i.e. distance from mu0 to farthest sample point). approxParameters: Dictionary containing values for main parameters of approximant. Recognized keys are in parameterList. parameterList: Recognized keys of approximant parameters: - - 'POD': whether to compute POD of snapshots; + - 'POD': whether to compute POD of snapshots; + - 'muBounds': list of bounds for parameter values; - 'S': total number of samples current approximant relies upon; - 'sampler': sample point generator; - 'R': rank for Galerkin projection. extraApproxParameters: List of approxParameters keys in addition to mother class's. + POD: Whether to compute POD of snapshots. + muBounds: list of bounds for parameter values. S: Number of solution snapshots over which current approximant is based upon. sampler: Sample point generator. R: Rank for Galerkin projection. - POD: Whether to compute POD of snapshots. samplingEngine: Sampling engine. uHF: High fidelity solution with wavenumber lastSolvedHF as numpy complex vector. lastSolvedHF: Wavenumber corresponding to last computed high fidelity solution. uApp: Last evaluated approximant as numpy complex vector. As: List of sparse matrices (in CSC format) representing coefficients of linear system matrix wrt theta(mu). bs: List of numpy vectors representing coefficients of linear system RHS wrt theta(mu). thetaAs: List of callables representing coefficients of linear system matrix wrt mu. thetabs: List of callables representing coefficients of linear system RHS wrt mu. ARBs: List of sparse matrices (in CSC format) representing coefficients of compressed linear system matrix wrt theta(mu). bRBs: List of numpy vectors representing coefficients of compressed linear system RHS wrt theta(mu). """ def __init__(self, HFEngine:HFEng, mu0 : complex = 0., approxParameters : DictAny = {}, homogeneized : bool = False, verbosity : int = 10, timestamp : bool = True): self._preInit() self._addParametersToList(["R"]) super().__init__(HFEngine = HFEngine, mu0 = mu0, approxParameters = approxParameters, homogeneized = homogeneized, verbosity = verbosity, timestamp = timestamp) if self.verbosity >= 10: verbosityDepth("INIT", "Computing affine blocks of system.", timestamp = self.timestamp) self.As = self.HFEngine.affineLinearSystemA(self.mu0) self.bs = self.HFEngine.affineLinearSystemb(self.mu0, self.homogeneized) if self.verbosity >= 10: verbosityDepth("DEL", "Done computing affine blocks.", timestamp = self.timestamp) self._postInit() @property def approxParameters(self): """ Value of approximant parameters. Its assignment may change M, N and S. """ return self._approxParameters @approxParameters.setter def approxParameters(self, approxParams): approxParameters = purgeDict(approxParams, self.parameterList, dictname = self.name() + ".approxParameters", baselevel = 1) approxParametersCopy = purgeDict(approxParameters, ["R"], True, True, baselevel = 1) - GenericApproximantLagrange.approxParameters.fset(self, - approxParametersCopy) + GenericDistributedApproximant.approxParameters.fset(self, + approxParametersCopy) keyList = list(approxParameters.keys()) if "R" in keyList: self.R = approxParameters["R"] elif hasattr(self, "_R") and self._R is not None: self.R = self.R else: self.R = self.S @property def R(self): """Value of R. Its assignment may change S.""" return self._R @R.setter def R(self, R): if R < 0: raise RROMPyException("R must be non-negative.") self._R = R self._approxParameters["R"] = self.R if hasattr(self, "_S") and self.S < self.R: RROMPyWarning("Prescribed S is too small. Updating S to R.") self.S = self.R def setupApprox(self): """Compute RB projection matrix.""" if self.checkComputedApprox(): return if self.verbosity >= 5: verbosityDepth("INIT", "Setting up {}.". format(self.name()), timestamp = self.timestamp) self.computeSnapshots() if self.verbosity >= 7: verbosityDepth("INIT", "Computing projection matrix.", timestamp = self.timestamp) if self.POD: U, _, _ = np.linalg.svd(self.samplingEngine.RPOD, full_matrices = False) pMat = self.samplingEngine.samples.dot(U[:, : self.R]) else: pMat = self.samplingEngine.samples[:, : self.R] if self.trainedModel is None: self.trainedModel = tModel() self.trainedModel.verbosity = self.verbosity self.trainedModel.timestamp = self.timestamp data = TrainedModelData(self.trainedModel.name(), self.mu0, np.copy(pMat), self.HFEngine.rescalingExp) data.thetaAs = self.HFEngine.affineWeightsA(self.mu0) data.thetabs = self.HFEngine.affineWeightsb(self.mu0, self.homogeneized) data.ARBs, data.bRBs = self.assembleReducedSystem(pMat) data.mus = np.copy(self.mus) self.trainedModel.data = data else: pMatOld = self.trainedModel.data.projMat Sold = pMatOld.shape[1] ARBs, bRBs = self.assembleReducedSystem(pMat[:, Sold :], pMatOld) self.trainedModel.data.ARBs = ARBs self.trainedModel.data.bRBs = bRBs self.trainedModel.data.projMat = np.copy(pMat) if self.verbosity >= 7: verbosityDepth("DEL", "Done computing projection matrix.", timestamp = self.timestamp) self.trainedModel.data.approxParameters = copy(self.approxParameters) if self.verbosity >= 5: verbosityDepth("DEL", "Done setting up approximant.", timestamp = self.timestamp) def assembleReducedSystem(self, pMat : Np2D = None, pMatOld : Np2D = None)\ -> Tuple[List[Np2D], List[Np1D]]: """Build affine blocks of RB linear system through projections.""" if pMat is None: self.setupApprox() ARBs = self.trainedModel.data.ARBs bRBs = self.trainedModel.data.bRBs else: if self.verbosity >= 10: verbosityDepth("INIT", "Projecting affine terms of HF model.", timestamp = self.timestamp) ARBsOld = None if pMatOld is None else self.trainedModel.data.ARBs bRBsOld = None if pMatOld is None else self.trainedModel.data.bRBs ARBs, bRBs = projectAffineDecomposition(self.As, self.bs, pMat, ARBsOld, bRBsOld, pMatOld) if self.verbosity >= 10: verbosityDepth("DEL", "Done projecting affine terms.", timestamp = self.timestamp) return ARBs, bRBs diff --git a/rrompy/reduction_methods/lagrange_greedy/__init__.py b/rrompy/reduction_methods/distributed_greedy/__init__.py similarity index 64% rename from rrompy/reduction_methods/lagrange_greedy/__init__.py rename to rrompy/reduction_methods/distributed_greedy/__init__.py index f926ff6..ba0008b 100644 --- a/rrompy/reduction_methods/lagrange_greedy/__init__.py +++ b/rrompy/reduction_methods/distributed_greedy/__init__.py @@ -1,30 +1,30 @@ # Copyright (C) 2018 by the RROMPy authors # # This file is part of RROMPy. # # RROMPy is free software: you can redistribute it and/or modify # it under the terms of the GNU Lesser General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # RROMPy is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU Lesser General Public License for more details. # # You should have received a copy of the GNU Lesser General Public License # along with RROMPy. If not, see . # -from .generic_approximant_lagrange_greedy import ( - GenericApproximantLagrangeGreedy) -from .approximant_lagrange_greedy_pade import ApproximantLagrangePadeGreedy -from .approximant_lagrange_greedy_rb import ApproximantLagrangeRBGreedy +from .generic_distributed_greedy_approximant import \ + GenericDistributedGreedyApproximant +from .rational_interpolant_greedy import RationalInterpolantGreedy +from .rb_distributed_greedy import RBDistributedGreedy __all__ = [ - 'GenericApproximantLagrangeGreedy', - 'ApproximantLagrangePadeGreedy', - 'ApproximantLagrangeRBGreedy' + 'GenericDistributedGreedyApproximant', + 'RationalInterpolantGreedy', + 'RBDistributedGreedy' ] diff --git a/rrompy/reduction_methods/lagrange_greedy/generic_approximant_lagrange_greedy.py b/rrompy/reduction_methods/distributed_greedy/generic_distributed_greedy_approximant.py similarity index 73% rename from rrompy/reduction_methods/lagrange_greedy/generic_approximant_lagrange_greedy.py rename to rrompy/reduction_methods/distributed_greedy/generic_distributed_greedy_approximant.py index e3a8796..5987e6b 100644 --- a/rrompy/reduction_methods/lagrange_greedy/generic_approximant_lagrange_greedy.py +++ b/rrompy/reduction_methods/distributed_greedy/generic_distributed_greedy_approximant.py @@ -1,721 +1,648 @@ # Copyright (C) 2018 by the RROMPy authors # # This file is part of RROMPy. # # RROMPy is free software: you can redistribute it and/or modify # it under the terms of the GNU Lesser General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # RROMPy is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU Lesser General Public License for more details. # # You should have received a copy of the GNU Lesser General Public License # along with RROMPy. If not, see . # from copy import deepcopy as copy import numpy as np -from rrompy.reduction_methods.base.generic_approximant import ( - GenericApproximant) -from rrompy.reduction_methods.lagrange.generic_approximant_lagrange import ( - GenericApproximantLagrange) +from rrompy.reduction_methods.distributed.generic_distributed_approximant \ + import GenericDistributedApproximant from rrompy.utilities.base.types import Np1D, Np2D, DictAny, HFEng, Tuple, List from rrompy.utilities.base import purgeDict, verbosityDepth from rrompy.utilities.exception_manager import (RROMPyException, modeAssert, RROMPyWarning) -__all__ = ['GenericApproximantLagrangeGreedy'] +__all__ = ['GenericDistributedGreedyApproximant'] -class GenericApproximantLagrangeGreedy(GenericApproximantLagrange): +class estNormer: + def __init__(self, energyNormMatrix:Np2D): + self.energyNormMatrix = energyNormMatrix + + def innerProduct(self, u:Np2D, v:Np2D, onlyDiag : bool = False) -> Np2D: + """Scalar product.""" + 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 + + +class GenericDistributedGreedyApproximant(GenericDistributedApproximant): """ - ROM greedy Lagrange interpolant computation for parametric problems + ROM greedy interpolant computation for parametric problems (ABSTRACT). Args: HFEngine: HF problem solver. mu0(optional): Default parameter. Defaults to 0. approxParameters(optional): Dictionary containing values for main parameters of approximant. Recognized keys are: - 'POD': whether to compute POD of snapshots; defaults to True; - 'muBounds': list of bounds for parameter values; defaults to - [[0], [1]]; + [0, 1]; + - 'S': number of starting training points; defaults to 2; + - 'sampler': sample point generator; defaults to uniform sampler on + muBounds; - 'greedyTol': uniform error tolerance for greedy algorithm; defaults to 1e-2; - 'interactive': whether to interactively terminate greedy algorithm; defaults to False; - 'maxIter': maximum number of greedy steps; defaults to 1e2; - 'refinementRatio': ratio of test points to be exhausted before test set refinement; defaults to 0.2; - 'nTestPoints': number of test points; defaults to maxIter / refinementRatio; - - 'nTrainPoints': number of starting training points; defaults to - 1; - 'trainSetGenerator': training sample points generator; defaults - to Chebyshev sampler within muBounds; - - 'testSetGenerator': test sample points generator; defaults to - uniform sampler within muBounds. + to Chebyshev sampler within muBounds. Defaults to empty dict. - homogeneized: Whether to homogeneize Dirichlet BCs. Defaults to False. + 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. parameterList: Recognized keys of approximant parameters: - 'POD': whether to compute POD of snapshots; - 'muBounds': list of bounds for parameter values; + - 'S': number of starting training points; + - 'sampler': sample point generator; - 'greedyTol': uniform error tolerance for greedy algorithm; - 'interactive': whether to interactively terminate greedy algorithm; - 'maxIter': maximum number of greedy steps; - 'refinementRatio': ratio of test points to be exhausted before test set refinement; - 'nTestPoints': number of test points; - - 'nTrainPoints': number of starting training points; - - 'trainSetGenerator': training sample points generator; - - 'testSetGenerator': test sample points generator. + - 'trainSetGenerator': training sample points generator. extraApproxParameters: List of approxParameters keys in addition to mother class's. POD: whether to compute POD of snapshots. muBounds: list of bounds for parameter values. + S: number of test points. + sampler: Sample point generator. greedyTol: uniform error tolerance for greedy algorithm. maxIter: maximum number of greedy steps. refinementRatio: ratio of training points to be exhausted before training set refinement. - nTrainPoints: number of test points. nTestPoints: number of starting training points. trainSetGenerator: training sample points generator. - testSetGenerator: test sample points generator. robustTol: tolerance for robust Pade' denominator management. + estimatorEnergyMatrix: matrix representing inner product for error + estimation. samplingEngine: Sampling engine. uHF: High fidelity solution with wavenumber lastSolvedHF as numpy complex vector. lastSolvedHF: Wavenumber corresponding to last computed high fidelity solution. uApp: Last evaluated approximant as numpy complex vector. """ TOL_INSTABILITY = 1e-6 def __init__(self, HFEngine:HFEng, mu0 : complex = 0., approxParameters : DictAny = {}, homogeneized : bool = False, verbosity : int = 10, timestamp : bool = True): self._preInit() - self._addParametersToList(["muBounds", "greedyTol", "interactive", - "maxIter", "refinementRatio", - "nTestPoints", "nTrainPoints", - "trainSetGenerator", "testSetGenerator"]) - super(GenericApproximantLagrange, self).__init__( - HFEngine = HFEngine, mu0 = mu0, - approxParameters = approxParameters, - homogeneized = homogeneized, - verbosity = verbosity, - timestamp = timestamp) + self._addParametersToList(["greedyTol", "interactive", "maxIter", + "refinementRatio", "nTestPoints", + "trainSetGenerator"]) + super().__init__(HFEngine = HFEngine, mu0 = mu0, + approxParameters = approxParameters, + homogeneized = homogeneized, verbosity = verbosity, + timestamp = timestamp) self._postInit() @property def approxParameters(self): """Value of approximant parameters. Its assignment may change S.""" return self._approxParameters @approxParameters.setter def approxParameters(self, approxParams): approxParameters = purgeDict(approxParams, self.parameterList, dictname = self.name() + ".approxParameters", baselevel = 1) approxParametersCopy = purgeDict(approxParameters, - ["muBounds", "greedyTol", - "interactive", "maxIter", - "refinementRatio", "nTestPoints", - "nTrainPoints", "trainSetGenerator", - "testSetGenerator"], + ["greedyTol", "interactive", + "maxIter", "refinementRatio", + "nTestPoints", "trainSetGenerator"], True, True, baselevel = 1) - GenericApproximant.approxParameters.fset(self, approxParametersCopy) + GenericDistributedApproximant.approxParameters.fset(self, + approxParametersCopy) keyList = list(approxParameters.keys()) - if "muBounds" in keyList: - self.muBounds = approxParameters["muBounds"] - elif not hasattr(self, "_muBounds") or self.muBounds is None: - self.muBounds = [[0.], [1.]] if "greedyTol" in keyList: self.greedyTol = approxParameters["greedyTol"] elif not hasattr(self, "_greedyTol") or self.greedyTol is None: self.greedyTol = 1e-2 if "interactive" in keyList: self.interactive = approxParameters["interactive"] elif not hasattr(self, "interactive") or self.interactive is None: self.interactive = False if "maxIter" in keyList: self.maxIter = approxParameters["maxIter"] elif not hasattr(self, "_maxIter") or self.maxIter is None: self.maxIter = 1e2 if "refinementRatio" in keyList: self.refinementRatio = approxParameters["refinementRatio"] elif (not hasattr(self, "_refinementRatio") or self.refinementRatio is None): self.refinementRatio = 0.2 if "nTestPoints" in keyList: self.nTestPoints = approxParameters["nTestPoints"] elif (not hasattr(self, "_nTestPoints") or self.nTestPoints is None): self.nTestPoints = np.int(np.ceil(self.maxIter / self.refinementRatio)) - if "nTrainPoints" in keyList: - self.nTrainPoints = approxParameters["nTrainPoints"] - elif not hasattr(self, "_nTrainPoints") or self.nTrainPoints is None: - self.nTrainPoints = 1 if "trainSetGenerator" in keyList: self.trainSetGenerator = approxParameters["trainSetGenerator"] elif (not hasattr(self, "_trainSetGenerator") or self.trainSetGenerator is None): from rrompy.utilities.parameter_sampling import QuadratureSampler self.trainSetGenerator = QuadratureSampler(self.muBounds, "CHEBYSHEV") del QuadratureSampler - if "testSetGenerator" in keyList: - self.testSetGenerator = approxParameters["testSetGenerator"] - elif (not hasattr(self, "_testSetGenerator") - or self.testSetGenerator is None): - from rrompy.utilities.parameter_sampling import QuadratureSampler - self.testSetGenerator = QuadratureSampler(self.muBounds, - "UNIFORM") - del QuadratureSampler - - @property - def S(self): - """Value of S.""" - if not hasattr(self, "_mus") or self.mus is None: return 0 - return len(self.mus) - @S.setter - def S(self, S): - raise RROMPyException(("S is used just to simplify inheritance, and " - "its value cannot be changed.")) @property def mus(self): """Value of mus.""" return self._mus @mus.setter def mus(self, mus): self._mus = np.array(mus, dtype = np.complex) - @property - def muBounds(self): - """Value of muBounds.""" - return self._muBounds - @muBounds.setter - def muBounds(self, muBounds): - if len(muBounds) != 2: - raise RROMPyException("2 limits must be specified.") - try: - muBounds = muBounds.tolist() - except: - muBounds = list(muBounds) - for j in range(2): - try: - len(muBounds[j]) - except: - muBounds[j] = np.array([muBounds[j]]) - if len(muBounds[0]) != len(muBounds[1]): - raise RROMPyException("The bounds must have the same length.") - self._muBounds = muBounds - @property def greedyTol(self): """Value of greedyTol.""" return self._greedyTol @greedyTol.setter def greedyTol(self, greedyTol): if greedyTol < 0: raise RROMPyException("greedyTol must be non-negative.") if hasattr(self, "_greedyTol") and self.greedyTol is not None: greedyTolold = self.greedyTol else: greedyTolold = -1 self._greedyTol = greedyTol self._approxParameters["greedyTol"] = self.greedyTol if greedyTolold != self.greedyTol: self.resetSamples() @property def maxIter(self): """Value of maxIter.""" return self._maxIter @maxIter.setter def maxIter(self, maxIter): if maxIter <= 0: raise RROMPyException("maxIter must be positive.") if hasattr(self, "_maxIter") and self.maxIter is not None: maxIterold = self.maxIter else: maxIterold = -1 self._maxIter = maxIter self._approxParameters["maxIter"] = self.maxIter if maxIterold != self.maxIter: self.resetSamples() @property def refinementRatio(self): """Value of refinementRatio.""" return self._refinementRatio @refinementRatio.setter def refinementRatio(self, refinementRatio): if refinementRatio <= 0. or refinementRatio > 1.: raise RROMPyException(("refinementRatio must be between 0 " "(excluded) and 1.")) if (hasattr(self, "_refinementRatio") and self.refinementRatio is not None): refinementRatioold = self.refinementRatio else: refinementRatioold = -1 self._refinementRatio = refinementRatio self._approxParameters["refinementRatio"] = self.refinementRatio if refinementRatioold != self.refinementRatio: self.resetSamples() @property def nTestPoints(self): """Value of nTestPoints.""" return self._nTestPoints @nTestPoints.setter def nTestPoints(self, nTestPoints): if nTestPoints <= 0: raise RROMPyException("nTestPoints must be positive.") if not np.isclose(nTestPoints, np.int(nTestPoints)): raise RROMPyException("nTestPoints must be an integer.") nTestPoints = np.int(nTestPoints) if hasattr(self, "_nTestPoints") and self.nTestPoints is not None: nTestPointsold = self.nTestPoints else: nTestPointsold = -1 self._nTestPoints = nTestPoints self._approxParameters["nTestPoints"] = self.nTestPoints if nTestPointsold != self.nTestPoints: self.resetSamples() - @property - def nTrainPoints(self): - """Value of nTrainPoints.""" - return self._nTrainPoints - @nTrainPoints.setter - def nTrainPoints(self, nTrainPoints): - if nTrainPoints <= 1: - raise RROMPyException("nTrainPoints must be greater than 1.") - if not np.isclose(nTrainPoints, np.int(nTrainPoints)): - raise RROMPyException("nTrainPoints must be an integer.") - nTrainPoints = np.int(nTrainPoints) - if (hasattr(self, "_nTrainPoints") - and self.nTrainPoints is not None): - nTrainPointsOld = self.nTrainPoints - else: - nTrainPointsOld = -1 - self._nTrainPoints = nTrainPoints - self._approxParameters["nTrainPoints"] = self.nTrainPoints - if nTrainPointsOld != self.nTrainPoints: - self.resetSamples() - @property def trainSetGenerator(self): """Value of trainSetGenerator.""" return self._trainSetGenerator @trainSetGenerator.setter def trainSetGenerator(self, trainSetGenerator): if 'generatePoints' not in dir(trainSetGenerator): raise RROMPyException("trainSetGenerator type not recognized.") if (hasattr(self, '_trainSetGenerator') and self.trainSetGenerator is not None): trainSetGeneratorOld = self.trainSetGenerator self._trainSetGenerator = trainSetGenerator self._approxParameters["trainSetGenerator"] = self.trainSetGenerator if (not 'trainSetGeneratorOld' in locals() or trainSetGeneratorOld != self.trainSetGenerator): self.resetSamples() - @property - def testSetGenerator(self): - """Value of testSetGenerator.""" - return self._testSetGenerator - @testSetGenerator.setter - def testSetGenerator(self, testSetGenerator): - if 'generatePoints' not in dir(testSetGenerator): - raise RROMPyException("testSetGenerator type not recognized.") - if (hasattr(self, '_testSetGenerator') - and self.testSetGenerator is not None): - testSetGeneratorOld = self.testSetGenerator - self._testSetGenerator = testSetGenerator - self._approxParameters["testSetGenerator"] = self.testSetGenerator - if (not 'testSetGeneratorOld' in locals() - or testSetGeneratorOld != self.testSetGenerator): - self.resetSamples() - def resetSamples(self): """Reset samples.""" super().resetSamples() self._mus = [] + self._estNormer = None def initEstNormer(self): """Initialize estimator norm engine.""" - if not hasattr(self, "estNormer"): - from rrompy.hfengines.base import ProblemEngineBase as PEB - self.estNormer = PEB() # L2 norm - self.estNormer.V = self.HFEngine.V - self.estNormer.buildEnergyNormForm() + if not hasattr(self, "_estNormer") or self._estNormer is None: + if not hasattr(self, "estimatorEnergyMatrix"): + if not hasattr(self.HFEngine, "energyNormMatrix"): + self.HFEngine.buildEnergyNormForm() + self.estimatorEnergyMatrix = self.HFEngine.energyNormMatrix + self._estNormer = estNormer(self.estimatorEnergyMatrix) def errorEstimator(self, mus:List[np.complex]) -> List[np.complex]: """ Standard residual-based error estimator with explicit residual computation. """ self.setupApprox() nmus = len(mus) err = np.empty(nmus) if self.HFEngine.nbs == 1: RHS = self.getRHS(mus[0], homogeneized = self.homogeneized) - RHSNorm = self.estNormer.norm(RHS) + RHSNorm = self._estNormer.norm(RHS) for j in range(nmus): res = self.getRes(mus[j], homogeneized = self.homogeneized) - err[j] = self.estNormer.norm(res) / RHSNorm + err[j] = self._estNormer.norm(res) / RHSNorm else: for j in range(nmus): res = self.getRes(mus[j], homogeneized = self.homogeneized) RHS = self.getRHS(mus[j], homogeneized = self.homogeneized) - err[j] = self.estNormer.norm(res) / self.estNormer.norm(RHS) + err[j] = self._estNormer.norm(res) / self._estNormer.norm(RHS) return np.abs(err) - def getMaxErrorEstimator(self, mus, plot : bool = False)\ - -> Tuple[Np1D, int, float]: + def getMaxErrorEstimator(self, mus:List[np.complex], + plot : bool = False) -> Tuple[Np1D, int, float]: """ Compute maximum of (and index of maximum of) error estimator over given parameters. """ errorEstTest = self.errorEstimator(mus) idxMaxEst = np.argmax(errorEstTest) maxEst = errorEstTest[idxMaxEst] if plot and not np.all(np.isinf(errorEstTest)): from matplotlib import pyplot as plt - onemus = np.ones(self.S) plt.figure() plt.semilogy(np.real(mus), errorEstTest, 'k') plt.semilogy(np.real(mus[[0, -1]]), [self.greedyTol] * 2, 'r--') - plt.semilogy(np.real(self.mus), 2. * self.greedyTol * onemus, '*m') + plt.semilogy(np.real(self.mus), + 2. * self.greedyTol * np.ones(len(self.mus)), '*m') plt.semilogy(np.real(mus[idxMaxEst]), maxEst, 'xr') plt.grid() plt.show() plt.close() return errorEstTest, idxMaxEst, maxEst def greedyNextSample(self, muidx:int, plotEst : bool = False)\ -> Tuple[Np1D, int, float, complex]: """Compute next greedy snapshot of solution map.""" modeAssert(self._mode, message = "Cannot add greedy sample.") mu = self.muTest[muidx] if self.verbosity >= 2: verbosityDepth("MAIN", ("Adding {}-th sample point at {} to " "training set.").format( self.samplingEngine.nsamples + 1, mu), timestamp = self.timestamp) self.mus = np.append(self.mus, mu) idxs = np.arange(len(self.muTest)) mask = np.ones_like(idxs, dtype = bool) mask[muidx] = False idxs = idxs[mask] self.muTest = self.muTest[idxs] self.samplingEngine.nextSample(mu, homogeneized = self.homogeneized) errorEstTest, muidx, maxErrorEst = self.getMaxErrorEstimator( self.muTest, plotEst) return errorEstTest, muidx, maxErrorEst, self.muTest[muidx] def _preliminaryTraining(self): """Initialize starting snapshots of solution map.""" modeAssert(self._mode, message = "Cannot start greedy algorithm.") if self.samplingEngine.samples is not None: return if self.verbosity >= 2: verbosityDepth("INIT", "Starting computation of snapshots.", timestamp = self.timestamp) self.resetSamples() - self.mus, _ = self.trainSetGenerator.generatePoints(self.nTrainPoints) - muTestBase, _ = self.testSetGenerator.generatePoints(self.nTestPoints) + self.mus, _ = self.trainSetGenerator.generatePoints(self.S) + muTestBase, _ = self.sampler.generatePoints(self.nTestPoints) proxVal = np.min(np.abs(muTestBase.reshape(-1, 1) - np.tile(self.mus.reshape(1, -1), [self.nTestPoints, 1])), axis = 1) proxMask = ~(proxVal < 1e-12 * np.abs(muTestBase[0] - muTestBase[-1])) self.muTest = np.empty(np.sum(proxMask) + 1, dtype = np.complex) self.muTest[:-1] = np.sort(muTestBase[proxMask]).flatten() self.muTest[-1] = self.mus[-1] self.mus = self.mus[:-1] for j in range(len(self.mus)): if self.verbosity >= 2: verbosityDepth("MAIN", ("Adding {}-th sample point at {} to training " "set.").format(self.samplingEngine.nsamples+ 1, self.mus[j]), timestamp = self.timestamp) self.samplingEngine.nextSample(self.mus[j], homogeneized = self.homogeneized) def _enrichTestSet(self, nTest:int): """Double number of elements of test set.""" - muTestExtra, _ = self.testSetGenerator.generatePoints(2 * nTest) + muTestExtra, _ = self.sampler.generatePoints(2 * nTest) muGiven = np.append(self.mus, self.muTest).reshape(1, -1) proxVal = np.min(np.abs(muTestExtra.reshape(-1, 1) - np.tile(muGiven, [2 * nTest, 1])), axis = 1) proxMask = ~(proxVal < 1e-12 * np.abs(muTestExtra[0]-muTestExtra[-1])) muTestNew = np.empty(len(self.muTest) + np.sum(proxMask), dtype = np.complex) muTestNew[: len(self.muTest)] = self.muTest muTestNew[len(self.muTest) :] = muTestExtra[proxMask] self.muTest = np.sort(muTestNew) if self.verbosity >= 5: verbosityDepth("MAIN", "Enriching test set by {} elements.".format( np.sum(proxMask)), timestamp = self.timestamp) def greedy(self, plotEst : bool = False): """Compute greedy snapshots of solution map.""" modeAssert(self._mode, message = "Cannot start greedy algorithm.") if self.samplingEngine.samples is not None: return self._preliminaryTraining() nTest = self.nTestPoints errorEstTest, muidx, maxErrorEst, mu = self.greedyNextSample(-1, plotEst) if self.verbosity >= 2: verbosityDepth("MAIN", ("Uniform testing error estimate " "{:.4e}.").format(maxErrorEst), timestamp = self.timestamp) trainedModelOld = copy(self.trainedModel) while (self.samplingEngine.nsamples < self.maxIter and maxErrorEst > self.greedyTol): if (1. - self.refinementRatio) * nTest > len(self.muTest): self._enrichTestSet(nTest) nTest = len(self.muTest) muTestOld, maxErrorEstOld = self.muTest, maxErrorEst errorEstTest, muidx, maxErrorEst, mu = self.greedyNextSample( muidx, plotEst) if self.verbosity >= 2: verbosityDepth("MAIN", ("Uniform testing error estimate " "{:.4e}.").format(maxErrorEst), timestamp = self.timestamp) if (np.isnan(maxErrorEst) or np.isinf(maxErrorEst) or maxErrorEstOld < maxErrorEst * self.TOL_INSTABILITY): RROMPyWarning(("Instability in a posteriori estimator. " "Starting preemptive greedy loop termination.")) maxErrorEst = maxErrorEstOld self.muTest = muTestOld self.mus = self.mus[:-1] self.samplingEngine.popSample() self.trainedModel.data = copy(trainedModelOld.data) break trainedModelOld.data = copy(self.trainedModel.data) if (self.interactive and maxErrorEst <= self.greedyTol): verbosityDepth("MAIN", ("Required tolerance {} achieved. Want " "to decrease greedyTol and continue? " "Y/N").format(self.greedyTol), timestamp = self.timestamp, end = "") increasemaxIter = input() if increasemaxIter.upper() == "Y": verbosityDepth("MAIN", "Reducing value of greedyTol...", timestamp = self.timestamp) while maxErrorEst <= self._greedyTol: self._greedyTol *= .5 if (self.interactive and self.samplingEngine.nsamples >= self.maxIter): verbosityDepth("MAIN", ("Maximum number of iterations {} " "reached. Want to increase maxIter " "and continue? Y/N").format( self.maxIter), timestamp = self.timestamp, end = "") increasemaxIter = input() if increasemaxIter.upper() == "Y": verbosityDepth("MAIN", "Doubling value of maxIter...", timestamp = self.timestamp) self._maxIter *= 2 if self.verbosity >= 2: verbosityDepth("DEL", ("Done computing snapshots (final snapshot " "count: {}).").format( self.samplingEngine.nsamples), timestamp = self.timestamp) def checkComputedApprox(self) -> bool: """ Check if setup of new approximant is not needed. Returns: True if new setup is not needed. False otherwise. """ return (super().checkComputedApprox() - and self.S == self.trainedModel.data.projMat.shape[1]) + and len(self.mus) == self.trainedModel.data.projMat.shape[1]) def computeScaleFactor(self): """Compute parameter rescaling factor.""" modeAssert(self._mode, message = "Cannot compute rescaling factor.") - self.scaleFactor= np.abs(np.power(self.trainSetGenerator.lims[0], - self.HFEngine.rescalingExp) - - np.power(self.trainSetGenerator.lims[1], - self.HFEngine.rescalingExp)) / 2. + self.scaleFactor= .5 * np.abs( + np.power(self.muBounds[0], self.HFEngine.rescalingExp) + - np.power(self.muBounds[1], self.HFEngine.rescalingExp)) def assembleReducedResidualGramian(self, pMat:Np2D): """ Build residual gramian of reduced linear system through projections. """ self.initEstNormer() if (not hasattr(self.trainedModel.data, "gramian") or self.trainedModel.data.gramian is None): - gramian = self.estNormer.innerProduct(pMat, pMat) + gramian = self._estNormer.innerProduct(pMat, pMat) else: Sold = self.trainedModel.data.gramian.shape[0] - if Sold > self.S: - gramian = self.trainedModel.data.gramian[: self.S, : self.S] + S = len(self.mus) + if Sold > S: + gramian = self.trainedModel.data.gramian[: S, : S] else: - gramian = np.empty((self.S, self.S), dtype = np.complex) + gramian = np.empty((S, S), dtype = np.complex) gramian[: Sold, : Sold] = self.trainedModel.data.gramian gramian[: Sold, Sold :] = ( - self.estNormer.innerProduct(pMat[:, Sold :], + self._estNormer.innerProduct(pMat[:, Sold :], pMat[:, : Sold])) gramian[Sold :, : Sold] = gramian[: Sold, Sold :].T.conj() gramian[Sold :, Sold :] = ( - self.estNormer.innerProduct(pMat[:, Sold :], + self._estNormer.innerProduct(pMat[:, Sold :], pMat[:, Sold :])) self.trainedModel.data.gramian = gramian def assembleReducedResidualBlocksbb(self, bs:List[Np1D], pMat:Np2D, scaling : float = 1.): """ Build blocks (of type bb) of reduced linear system through projections. """ self.initEstNormer() nbs = len(bs) if (not hasattr(self.trainedModel.data, "resbb") or self.trainedModel.data.resbb is None): resbb = np.empty((nbs, nbs), dtype = np.complex) for i in range(nbs): Mbi = scaling ** i * bs[i] - resbb[i, i] = self.estNormer.innerProduct(Mbi, Mbi) + resbb[i, i] = self._estNormer.innerProduct(Mbi, Mbi) for j in range(i): Mbj = scaling ** j * bs[j] - resbb[i, j] = self.estNormer.innerProduct(Mbj, Mbi) + resbb[i, j] = self._estNormer.innerProduct(Mbj, Mbi) for i in range(nbs): for j in range(i + 1, nbs): resbb[i, j] = resbb[j, i].conj() self.trainedModel.data.resbb = resbb def assembleReducedResidualBlocksAb(self, As:List[Np2D], bs:List[Np1D], pMat:Np2D, scaling : float = 1.): """ Build blocks (of type Ab) of reduced linear system through projections. """ self.initEstNormer() nAs = len(As) nbs = len(bs) + S = len(self.mus) if (not hasattr(self.trainedModel.data, "resAb") or self.trainedModel.data.resAb is None): - resAb = np.empty((nbs, self.S, nAs), dtype = np.complex) + resAb = np.empty((nbs, S, nAs), dtype = np.complex) for j in range(nAs): MAj = scaling ** (j + 1) * As[j].dot(pMat) for i in range(nbs): Mbi = scaling ** (i + 1) * bs[i] - resAb[i, :, j] = self.estNormer.innerProduct(MAj, Mbi) + resAb[i, :, j] = self._estNormer.innerProduct(MAj, Mbi) else: Sold = self.trainedModel.data.resAb.shape[1] - if Sold == self.S: return - if Sold > self.S: - resAb = self.trainedModel.data.resAb[:, : self.S, :] + if Sold == S: return + if Sold > S: + resAb = self.trainedModel.data.resAb[:, : S, :] else: - resAb = np.empty((nbs, self.S, nAs), dtype = np.complex) + resAb = np.empty((nbs, S, nAs), dtype = np.complex) resAb[:, : Sold, :] = self.trainedModel.data.resAb for j in range(nAs): MAj = scaling ** (j + 1) * As[j].dot(pMat[:, Sold :]) for i in range(nbs): Mbi = scaling ** (i + 1) * bs[i] - resAb[i, Sold :, j] = self.estNormer.innerProduct(MAj, - Mbi) + resAb[i, Sold :, j] = self._estNormer.innerProduct(MAj, + Mbi) self.trainedModel.data.resAb = resAb def assembleReducedResidualBlocksAA(self, As:List[Np2D], pMat:Np2D, scaling : float = 1., basic : bool = False): """ Build blocks (of type AA) of reduced linear system through projections. """ self.initEstNormer() nAs = len(As) + S = len(self.mus) if (not hasattr(self.trainedModel.data, "resAA") or self.trainedModel.data.resAA is None): if basic: MAEnd = scaling ** nAs * As[-1].dot(pMat) - resAA = self.estNormer.innerProduct(MAEnd, MAEnd) + resAA = self._estNormer.innerProduct(MAEnd, MAEnd) else: - resAA = np.empty((self.S, nAs, self.S, nAs), - dtype = np.complex) + resAA = np.empty((S, nAs, S, nAs), dtype = np.complex) for i in range(nAs): MAi = scaling ** (i + 1) * As[i].dot(pMat) - resAA[:, i, :, i] = self.estNormer.innerProduct(MAi, MAi) + resAA[:, i, :, i] = self._estNormer.innerProduct(MAi, MAi) for j in range(i): MAj = scaling ** (j + 1) * As[j].dot(pMat) - resAA[:, i, :, j] = self.estNormer.innerProduct(MAj, - MAi) + resAA[:, i, :, j] = self._estNormer.innerProduct(MAj, + MAi) for i in range(nAs): for j in range(i + 1, nAs): resAA[:, i, :, j] = resAA[:, j, :, i].T.conj() else: Sold = self.trainedModel.data.resAA.shape[0] - if Sold == self.S: return - if Sold > self.S: + if Sold == S: return + if Sold > S: if basic: - resAA = self.trainedModel.data.resAA[: self.S, : self.S] + resAA = self.trainedModel.data.resAA[: S, : S] else: - resAA = self.trainedModel.data.resAA[: self.S, :, - : self.S, :] + resAA = self.trainedModel.data.resAA[: S, :, : S, :] else: if basic: - resAA = np.empty((self.S, self.S), dtype = np.complex) + resAA = np.empty((S, S), dtype = np.complex) resAA[: Sold, : Sold] = self.trainedModel.data.resAA MAi = scaling ** nAs * As[-1].dot(pMat) resAA[: Sold, Sold :] = ( - self.estNormer.innerProduct(MAi[:, Sold :], + self._estNormer.innerProduct(MAi[:, Sold :], MAi[:, : Sold])) resAA[Sold :, : Sold] = resAA[: Sold, Sold :].T.conj() resAA[Sold :, Sold :] = ( - self.estNormer.innerProduct(MAi[:, Sold :], + self._estNormer.innerProduct(MAi[:, Sold :], MAi[:, Sold :])) else: - resAA = np.empty((self.S, nAs, self.S, nAs), - dtype = np.complex) + resAA = np.empty((S, nAs, S, nAs), dtype = np.complex) resAA[: Sold, :, : Sold, :] = self.trainedModel.data.resAA for i in range(nAs): MAi = scaling ** (i + 1) * As[i].dot(pMat) resAA[: Sold, i, Sold :, i] = ( - self.estNormer.innerProduct(MAi[:, Sold :], + self._estNormer.innerProduct(MAi[:, Sold :], MAi[:, : Sold])) resAA[Sold :, i, : Sold, i] = resAA[: Sold, i, Sold :, i].T.conj() resAA[Sold :, i, Sold :, i] = ( - self.estNormer.innerProduct(MAi[:, Sold :], + self._estNormer.innerProduct(MAi[:, Sold :], MAi[:, Sold :])) for j in range(i): MAj = scaling ** (j + 1) * As[j].dot(pMat) resAA[: Sold, i, Sold :, j] = ( - self.estNormer.innerProduct(MAj[:, Sold :], + self._estNormer.innerProduct(MAj[:, Sold :], MAi[:, : Sold])) resAA[Sold :, i, : Sold, j] = ( - self.estNormer.innerProduct(MAj[:, : Sold], + self._estNormer.innerProduct(MAj[:, : Sold], MAi[:, Sold :])) resAA[Sold :, i, Sold :, j] = ( - self.estNormer.innerProduct(MAj[:, Sold :], + self._estNormer.innerProduct(MAj[:, Sold :], MAi[:, Sold :])) for i in range(nAs): for j in range(i + 1, nAs): resAA[: Sold, i, Sold :, j] = ( resAA[Sold :, j, : Sold, i].T.conj()) resAA[Sold :, i, : Sold, j] = ( resAA[: Sold, j, Sold :, i].T.conj()) resAA[Sold :, i, Sold :, j] = ( resAA[Sold :, j, Sold :, i].T.conj()) self.trainedModel.data.resAA = resAA + diff --git a/rrompy/reduction_methods/lagrange_greedy/approximant_lagrange_greedy_pade.py b/rrompy/reduction_methods/distributed_greedy/rational_interpolant_greedy.py similarity index 93% rename from rrompy/reduction_methods/lagrange_greedy/approximant_lagrange_greedy_pade.py rename to rrompy/reduction_methods/distributed_greedy/rational_interpolant_greedy.py index b37d5da..3cd83b4 100644 --- a/rrompy/reduction_methods/lagrange_greedy/approximant_lagrange_greedy_pade.py +++ b/rrompy/reduction_methods/distributed_greedy/rational_interpolant_greedy.py @@ -1,549 +1,555 @@ # Copyright (C) 2018 by the RROMPy authors # # This file is part of RROMPy. # # RROMPy is free software: you can redistribute it and/or modify # it under the terms of the GNU Lesser General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # RROMPy is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU Lesser General Public License for more details. # # You should have received a copy of the GNU Lesser General Public License # along with RROMPy. If not, see . # from copy import copy import numpy as np from scipy.special import factorial as fact -from .generic_approximant_lagrange_greedy import ( - GenericApproximantLagrangeGreedy) +from .generic_distributed_greedy_approximant import \ + GenericDistributedGreedyApproximant from rrompy.utilities.poly_fitting import (polybases, polyvander, polydomcoeff, polyfitname, customFit) -from rrompy.reduction_methods.lagrange import ApproximantLagrangePade +from rrompy.reduction_methods.distributed import RationalInterpolant from rrompy.reduction_methods.trained_model import TrainedModelPade as tModel from rrompy.reduction_methods.trained_model import TrainedModelData from rrompy.utilities.base.types import DictAny, List, HFEng from rrompy.utilities.base import purgeDict, verbosityDepth from rrompy.utilities.exception_manager import RROMPyWarning from rrompy.utilities.exception_manager import RROMPyException -__all__ = ['ApproximantLagrangePadeGreedy'] +__all__ = ['RationalInterpolantGreedy'] -class ApproximantLagrangePadeGreedy(GenericApproximantLagrangeGreedy, - ApproximantLagrangePade): +class RationalInterpolantGreedy(GenericDistributedGreedyApproximant, + RationalInterpolant): """ - ROM greedy Pade' interpolant computation for parametric problems. + ROM greedy rational interpolant computation for parametric problems. Args: HFEngine: HF problem solver. mu0(optional): Default parameter. Defaults to 0. approxParameters(optional): Dictionary containing values for main parameters of approximant. Recognized keys are: - 'POD': whether to compute POD of snapshots; defaults to True; - 'muBounds': list of bounds for parameter values; defaults to - [[0], [1]]; + [0, 1]; + - 'S': number of starting training points; defaults to 2; + - 'sampler': sample point generator; defaults to uniform sampler on + muBounds; - 'basis': type of basis for interpolation; allowed values include 'MONOMIAL', 'CHEBYSHEV' and 'LEGENDRE'; defaults to 'MONOMIAL'; - 'Delta': difference between M and N in rational approximant; defaults to 0; - 'greedyTol': uniform error tolerance for greedy algorithm; defaults to 1e-2; - 'errorEstimatorKind': kind of error estimator; available values include 'EXACT', 'SIMPLIFIED', 'BASIC', and 'BARE'; defaults to 'SIMPLIFIED'; - 'maxIter': maximum number of greedy steps; defaults to 1e2; - 'refinementRatio': ratio of training points to be exhausted before training set refinement; defaults to 0.2; - 'nTestPoints': number of test points; defaults to maxIter / refinementRatio; - - 'nTrainPoints': number of starting training points; defaults to - 1; - 'trainSetGenerator': training sample points generator; defaults to Chebyshev sampler within muBounds; - - 'testSetGenerator': test sample points generator; defaults to - uniform sampler within muBounds; - 'interpRcond': tolerance for interpolation via numpy.polyfit; defaults to None; - 'robustTol': tolerance for robust Pade' denominator management; defaults to 0. Defaults to empty dict. - homogeneized: Whether to homogeneize Dirichlet BCs. Defaults to False. + 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. parameterList: Recognized keys of approximant parameters: - 'POD': whether to compute POD of snapshots; - 'muBounds': list of bounds for parameter values; + - 'S': number of starting training points; + - 'sampler': sample point generator; - 'basis': type of basis for interpolation; - 'Delta': difference between M and N in rational approximant; - 'greedyTol': uniform error tolerance for greedy algorithm; - 'errorEstimatorKind': kind of error estimator; - 'maxIter': maximum number of greedy steps; - 'refinementRatio': ratio of training points to be exhausted before training set refinement; - 'nTestPoints': number of test points; - - 'nTrainPoints': number of starting training points; - 'trainSetGenerator': training sample points generator; - - 'testSetGenerator': test sample points generator; - 'interpRcond': tolerance for interpolation via numpy.polyfit; - 'robustTol': tolerance for robust Pade' denominator management. extraApproxParameters: List of approxParameters keys in addition to mother class's. POD: whether to compute POD of snapshots. muBounds: list of bounds for parameter values. + S: number of starting training points. + sampler: Sample point generator. greedyTol: uniform error tolerance for greedy algorithm. errorEstimatorKind: kind of error estimator. maxIter: maximum number of greedy steps. refinementRatio: ratio of training points to be exhausted before training set refinement. - nTrainPoints: number of test points. nTestPoints: number of starting training points. trainSetGenerator: training sample points generator. - testSetGenerator: test sample points generator. interpRcond: Tolerance for interpolation via numpy.polyfit. robustTol: Tolerance for robust Pade' denominator management. + estimatorEnergyMatrix: matrix representing inner product for error + estimation. samplingEngine: Sampling engine. uHF: High fidelity solution with wavenumber lastSolvedHF as numpy complex vector. lastSolvedHF: Wavenumber corresponding to last computed high fidelity solution. uApp: Last evaluated approximant as numpy complex vector. """ _allowedEstimatorKinds = ["EXACT", "SIMPLIFIED", "BASIC", "BARE"] def __init__(self, HFEngine:HFEng, mu0 : complex = 0., approxParameters : DictAny = {}, homogeneized : bool = False, verbosity : int = 10, timestamp : bool = True): self._preInit() self._addParametersToList(["polybasis", "Delta", "errorEstimatorKind", - "interpRcond", "robustTol"]) + "interpRcond", "robustTol"]) super().__init__(HFEngine = HFEngine, mu0 = mu0, approxParameters = approxParameters, homogeneized = homogeneized, verbosity = verbosity, timestamp = timestamp) if self.verbosity >= 7: verbosityDepth("INIT", "Computing Taylor blocks of system.", timestamp = self.timestamp) nAs = self.HFEngine.nAs - 1 nbs = max(self.HFEngine.nbs, (nAs + 1) * self.homogeneized) self.As = [self.HFEngine.A(self.mu0, j + 1) for j in range(nAs)] self.bs = [self.HFEngine.b(self.mu0, j, self.homogeneized) for j in range(nbs)] if self.verbosity >= 7: verbosityDepth("DEL", "Done computing Taylor blocks.", timestamp = self.timestamp) self._postInit() @property def approxParameters(self): """ Value of approximant parameters. Its assignment may change robustTol. """ return self._approxParameters @approxParameters.setter def approxParameters(self, approxParams): approxParameters = purgeDict(approxParams, self.parameterList, dictname = self.name() + ".approxParameters", baselevel = 1) approxParametersCopy = purgeDict(approxParameters, ["polybasis", "Delta", "errorEstimatorKind", "interpRcond", "robustTol"], True, True, baselevel = 1) if "Delta" in list(approxParameters.keys()): self._Delta = approxParameters["Delta"] elif not hasattr(self, "_Delta") or self._Delta is None: self._Delta = 0 - GenericApproximantLagrangeGreedy.approxParameters.fset(self, + GenericDistributedGreedyApproximant.approxParameters.fset(self, approxParametersCopy) keyList = list(approxParameters.keys()) self.Delta = self.Delta if "polybasis" in keyList: self.polybasis = approxParameters["polybasis"] elif not hasattr(self, "_polybasis") or self._polybasis is None: self.polybasis = "MONOMIAL" if "errorEstimatorKind" in keyList: self.errorEstimatorKind = approxParameters["errorEstimatorKind"] elif (not hasattr(self, "_errorEstimatorKind") or self.errorEstimatorKind is None): self.errorEstimatorKind = "SIMPLIFIED" if "interpRcond" in keyList: self.interpRcond = approxParameters["interpRcond"] elif not hasattr(self, "interpRcond") or self.interpRcond is None: self.interpRcond = None if "robustTol" in keyList: self.robustTol = approxParameters["robustTol"] elif not hasattr(self, "_robustTol") or self._robustTol is None: self.robustTol = 0 @property def polybasis(self): """Value of polybasis.""" return self._polybasis @polybasis.setter def polybasis(self, polybasis): try: polybasis = polybasis.upper().strip().replace(" ","") if polybasis not in polybases: raise RROMPyException("Sample type not recognized.") self._polybasis = polybasis except: RROMPyWarning(("Prescribed polybasis not recognized. Overriding " "to 'MONOMIAL'.")) self._polybasis = "MONOMIAL" self._approxParameters["polybasis"] = self.polybasis @property def Delta(self): """Value of Delta.""" return self._Delta @Delta.setter def Delta(self, Delta): if not np.isclose(Delta, np.floor(Delta)): raise RROMPyException("Delta must be an integer.") if Delta < 0: RROMPyWarning(("Error estimator unreliable for Delta < 0. " "Overloading of errorEstimator is suggested.")) else: Deltamin = (max(self.HFEngine.nbs, self.HFEngine.nAs * self.homogeneized) - 1 - 1 * (self.HFEngine.nAs > 1)) if Delta < Deltamin: RROMPyWarning(("Method may be unreliable for selected Delta. " "Suggested minimal value of Delta: {}.").format( Deltamin)) self._Delta = Delta self._approxParameters["Delta"] = self.Delta @property def errorEstimatorKind(self): """Value of errorEstimatorKind.""" return self._errorEstimatorKind @errorEstimatorKind.setter def errorEstimatorKind(self, errorEstimatorKind): errorEstimatorKind = errorEstimatorKind.upper() if errorEstimatorKind not in self._allowedEstimatorKinds: RROMPyWarning(("Error estimator kind not recognized. Overriding " "to 'SIMPLIFIED'.")) errorEstimatorKind = "SIMPLIFIED" self._errorEstimatorKind = errorEstimatorKind self._approxParameters["errorEstimatorKind"] = self.errorEstimatorKind @property def nTestPoints(self): """Value of nTestPoints.""" return self._nTestPoints @nTestPoints.setter def nTestPoints(self, nTestPoints): if nTestPoints <= np.abs(self.Delta): RROMPyWarning(("nTestPoints must be at least abs(Delta) + 1. " "Increasing value to abs(Delta) + 1.")) nTestPoints = np.abs(self.Delta) + 1 if not np.isclose(nTestPoints, np.int(nTestPoints)): raise RROMPyException("nTestPoints must be an integer.") nTestPoints = np.int(nTestPoints) if hasattr(self, "_nTestPoints") and self.nTestPoints is not None: nTestPointsold = self.nTestPoints else: nTestPointsold = -1 self._nTestPoints = nTestPoints self._approxParameters["nTestPoints"] = self.nTestPoints if nTestPointsold != self.nTestPoints: self.resetSamples() def errorEstimator(self, mus:List[np.complex]) -> List[np.complex]: """Standard residual-based error estimator.""" self.setupApprox() PM = self.trainedModel.data.P[:, -1] if np.any(np.isnan(PM)) or np.any(np.isinf(PM)): err = np.empty(len(mus)) err[:] = np.inf return err nAs = self.HFEngine.nAs - 1 nbs = max(self.HFEngine.nbs - 1, nAs * self.homogeneized) + S = len(self.mus) muRTest = self.radiusPade(mus) muRTrain = self.radiusPade(self.mus) - nodalVals = np.prod(np.tile(muRTest.reshape(-1, 1), [1, self.S]) + nodalVals = np.prod(np.tile(muRTest.reshape(-1, 1), [1, S]) - muRTrain.reshape(1, -1), axis = 1) denVals = self.trainedModel.getQVal(mus) self.assembleReducedResidualBlocks(kind = self.errorEstimatorKind) vanderBase = np.polynomial.polynomial.polyvander(muRTest, max(nAs, nbs)).T radiusb0 = vanderBase[: nbs + 1, :] # 'ij,jk,ik->k', resbb, radiusb0, radiusb0.conj() b0resb0 = np.sum(self.trainedModel.data.resbb.dot(radiusb0) * radiusb0.conj(), axis = 0) RHSnorms = np.power(np.abs(b0resb0), .5) if self.errorEstimatorKind == "BARE": self.assembleReducedResidualGramian(self.trainedModel.data.projMat) pDom = self.trainedModel.data.P[:, -1] LL = pDom.conj().dot(self.trainedModel.data.gramian.dot(pDom)) Adiag = self.As[0].diagonal() LL = ((self.scaleFactor * np.linalg.norm(Adiag)) ** 2. / np.size(Adiag) * LL) elif self.errorEstimatorKind == "BASIC": pDom = self.trainedModel.data.P[:, -1] LL = pDom.conj().dot(self.trainedModel.data.resAA.dot(pDom)) else: vanderBase = vanderBase[: -1, :] - delta = self.S - len(self.trainedModel.data.Q) + delta = S - len(self.trainedModel.data.Q) nbsEff = max(0, nbs - delta) if self.errorEstimatorKind == "SIMPLIFIED": radiusA = np.tensordot(PM, vanderBase[: nAs, :], 0) if delta == 0: radiusb = (np.abs(self.trainedModel.data.Q[-1]) * radiusb0[: -1, :]) else: #if self.errorEstimatorKind == "EXACT": momentQ = np.zeros(nbsEff, dtype = np.complex) - momentQu = np.zeros((self.S, nAs), dtype = np.complex) + momentQu = np.zeros((S, nAs), dtype = np.complex) radiusbTen = np.zeros((nbsEff, nbsEff, len(mus)), dtype = np.complex) radiusATen = np.zeros((nAs, nAs, len(mus)), dtype = np.complex) if nbsEff > 0: momentQ[0] = self.trainedModel.data.Q[-1] radiusbTen[0, :, :] = vanderBase[: nbsEff, :] momentQu[:, 0] = self.trainedModel.data.P[:, -1] radiusATen[0, :, :] = vanderBase[: nAs, :] Qvals = self.trainedModel.getQVal(self.mus) for k in range(1, max(nAs, nbs * (nbsEff > 0))): Qvals = Qvals * muRTrain if k > delta and k < nbs: momentQ[k - delta] = self._fitinv.dot(Qvals) radiusbTen[k - delta, k :, :] = ( radiusbTen[0, : delta - k, :]) if k < nAs: momentQu[:, k] = Qvals * self._fitinv radiusATen[k, k :, :] = radiusATen[0, : - k, :] if self.POD and nAs > 1: momentQu[:, 1 :] = self.samplingEngine.RPOD.dot( momentQu[:, 1 :]) radiusA = np.tensordot(momentQu, radiusATen, 1) if nbsEff > 0: radiusb = np.tensordot(momentQ, radiusbTen, 1) if ((self.errorEstimatorKind == "SIMPLIFIED" and delta == 0) or (self.errorEstimatorKind == "EXACT" and nbsEff > 0)): # 'ij,jk,ik->k', resbb, radiusb, radiusb.conj() ff = np.sum(self.trainedModel.data.resbb[delta + 1 :, delta + 1 :]\ .dot(radiusb) * radiusb.conj(), axis = 0) # 'ijk,jkl,il->l', resAb, radiusA, radiusb.conj() Lf = np.sum(np.tensordot( self.trainedModel.data.resAb[delta :, :, :], radiusA, 2) * radiusb.conj(), axis = 0) else: ff, Lf = 0., 0. if self.errorEstimatorKind not in ["BARE", "BASIC"]: # 'ijkl,klt,ijt->t', resAA, radiusA, radiusA.conj() LL = np.sum(np.tensordot(self.trainedModel.data.resAA, radiusA, 2) * radiusA.conj(), axis = (0, 1)) jOpt = np.power(np.abs(ff - 2. * np.real(Lf) + LL), .5) - return (polydomcoeff[self.polybasis](self.S - 1) * jOpt + return (polydomcoeff[self.polybasis](S - 1) * jOpt * np.abs(nodalVals / denVals) / RHSnorms) def _setupDenominator(self): """Compute Pade' denominator.""" if self.verbosity >= 7: verbosityDepth("INIT", "Starting computation of denominator.", timestamp = self.timestamp) - TS = polyvander[self.polybasis](self.radiusPade(self.mus),self.S - 1).T - RHS = np.zeros(self.S) + S = len(self.mus) + TS = polyvander[self.polybasis](self.radiusPade(self.mus), S - 1).T + RHS = np.zeros(S) RHS[-1] = 1. fitOut = customFit(TS, RHS, full = True, rcond = self.interpRcond) if self.verbosity >= 2: condfit = fitOut[1][2][0] / fitOut[1][2][-1] verbosityDepth("MAIN", ("Fitting {} samples with degree {} " "through {}... Conditioning of system: " - "{:.4e}.").format(self.S, self.S - 1, + "{:.4e}.").format(S, S - 1, polyfitname[self.polybasis], condfit), timestamp = self.timestamp) - if fitOut[1][1] < self.S: + if fitOut[1][1] < S: RROMPyWarning(("Polyfit is poorly conditioned. Starting " "preemptive termination of computation of " "approximant.")) Q = np.empty(max(self.N, 0) + 1, dtype = np.complex) P = np.empty((len(self.mus), max(self.M, 0) + 1), dtype = np.complex) Q[:] = np.nan P[:] = np.nan self.trainedModel.data.Q = np.copy(Q) self.trainedModel.data.P = np.copy(P) self.trainedModel.data.approxParameters = copy( self.approxParameters) if self.verbosity >= 7: verbosityDepth("DEL", "Aborting computation of denominator.", timestamp = self.timestamp) return self._fitinv = fitOut[0] while self.N > 0: Ghalf = (TS[: self.N + 1, :] * self._fitinv).T if self.POD: self.Ghalf = self.samplingEngine.RPOD.dot(Ghalf) ev, eV = self.findeveVGQR(2) else: self.Ghalf = self.samplingEngine.samples.dot(Ghalf) ev, eV = self.findeveVGQR(2) Nstable = np.sum(np.abs(ev) >= self.robustTol * np.linalg.norm(ev)) if self.N <= Nstable: break if self.verbosity >= 2: verbosityDepth("MAIN", ("Smallest {} eigenvalues below " "tolerance. Reducing N to {}.")\ .format(self.N - Nstable + 1, Nstable), timestamp = self.timestamp) self._N = Nstable if self.N <= 0: self._N = 0 eV = np.ones((1, 1)) if self.verbosity >= 7: verbosityDepth("DEL", "Done computing denominator.", timestamp = self.timestamp) return eV[:, 0] def _setupNumerator(self): """Compute Pade' numerator.""" if self.verbosity >= 7: verbosityDepth("INIT", "Starting computation of numerator.", timestamp = self.timestamp) Qevaldiag = np.diag(self.trainedModel.getQVal(self.mus)) verb = self.trainedModel.verbosity self.trainedModel.verbosity = 0 mus_un, idx_un, cnt_un = np.unique(self.mus, return_inverse = True, return_counts = True) for j in range(len(mus_un)): if cnt_un[j] > 1: Q_loc = np.zeros((cnt_un[j], cnt_un[j]), dtype = np.complex) for der in range(1, cnt_un[j]): Qderj = (self.trainedModel.getQVal(mus_un[j], der) / fact(der)) Q_loc = Q_loc + Qderj * np.diag(np.ones(cnt_un[j] - der), k = - der) I = idx_un == j I = np.arange(len(self.mus))[I] I = np.ix_(I, I) Qevaldiag[I] = Qevaldiag[I] + Q_loc self.trainedModel.verbosity = verb while self.M >= 0: fitVander = polyvander[self.polybasis](self.radiusPade(self.mus), self.M) w = None - if self.M == self.S - 1: w = "AUTO" + S = len(self.mus) + if self.M == S - 1: w = "AUTO" fitOut = customFit(fitVander, Qevaldiag, full = True, w = w, rcond = self.interpRcond) if self.verbosity >= 2: condfit = fitOut[1][2][0] / fitOut[1][2][-1] verbosityDepth("MAIN", ("Fitting {} samples with degree {} " "through {}... Conditioning of " "system: {:.4e}.").format( - self.S, self.M, + S, self.M, polyfitname[self.polybasis], condfit), timestamp = self.timestamp) if fitOut[1][1] == self.M + 1: P = fitOut[0].T break RROMPyWarning(("Polyfit is poorly conditioned. Reducing M from {} " "to {}. Exact snapshot interpolation not " "guaranteed.").format(self.M, fitOut[1][1] - 1)) self._M = fitOut[1][1] - 1 if self.M < 0: raise RROMPyException(("Instability in computation of numerator. " "Aborting.")) return np.atleast_2d(P) def setupApprox(self, plotEst : bool = False): """ Compute Pade' interpolant. SVD-based robust eigenvalue management. """ if self.checkComputedApprox(): return if self.verbosity >= 5: verbosityDepth("INIT", "Setting up {}.". format(self.name()), timestamp = self.timestamp) self.computeScaleFactor() self.greedy(plotEst) - self._M = self.S - 1 - self._N = self.S - 1 + S = len(self.mus) + self._M = S - 1 + self._N = S - 1 if self.Delta < 0: self._M += self.Delta else: self._N -= self.Delta if self.trainedModel is None: self.trainedModel = tModel() self.trainedModel.verbosity = self.verbosity self.trainedModel.timestamp = self.timestamp data = TrainedModelData(self.trainedModel.name(), self.mu0, np.copy(self.samplingEngine.samples), self.HFEngine.rescalingExp) data.polytype = self.polybasis data.scaleFactor = self.scaleFactor data.mus = np.copy(self.mus) self.trainedModel.data = data else: self.trainedModel.data.projMat = np.copy( self.samplingEngine.samples) self.trainedModel.data.mus = np.copy(self.mus) if min(self.M, self.N) < 0: if self.verbosity >= 5: verbosityDepth("MAIN", "Minimal sample size not achieved.", timestamp = self.timestamp) Q = np.empty(max(self.N, 0) + 1, dtype = np.complex) P = np.empty((len(self.mus), max(self.M, 0) + 1), dtype = np.complex) Q[:] = np.nan P[:] = np.nan self.trainedModel.data.Q = np.copy(Q) self.trainedModel.data.P = np.copy(P) self.trainedModel.data.approxParameters = copy( self.approxParameters) if self.verbosity >= 5: verbosityDepth("DEL", "Aborting computation of approximant.", timestamp = self.timestamp) return if self.N > 0: Q = self._setupDenominator() if Q is None: if self.verbosity >= 5: verbosityDepth("DEL", "Aborting computation of approximant.", timestamp = self.timestamp) return else: Q = np.ones((1,), dtype = np.complex) self.trainedModel.data.Q = np.copy(Q) P = self._setupNumerator() if self.POD: P = self.samplingEngine.RPOD.dot(P) self.trainedModel.data.P = np.copy(P) if self.verbosity >= 7: verbosityDepth("DEL", "Done computing numerator.", timestamp = self.timestamp) self.trainedModel.data.approxParameters = copy(self.approxParameters) if self.verbosity >= 5: verbosityDepth("DEL", "Done setting up approximant.", timestamp = self.timestamp) def assembleReducedResidualBlocks(self, kind : str = "EXACT"): """Build affine blocks of reduced linear system through projections.""" pMat = self.trainedModel.data.projMat scaling = self.trainedModel.data.scaleFactor self.assembleReducedResidualBlocksbb(self.bs, pMat, scaling) if kind in ["EXACT", "SIMPLIFIED"]: self.assembleReducedResidualBlocksAb(self.As, self.bs[1 :], pMat, scaling) if kind != "BARE": self.assembleReducedResidualBlocksAA(self.As, pMat, scaling, basic = (kind == "BASIC")) diff --git a/rrompy/reduction_methods/lagrange_greedy/approximant_lagrange_greedy_rb.py b/rrompy/reduction_methods/distributed_greedy/rb_distributed_greedy.py similarity index 90% rename from rrompy/reduction_methods/lagrange_greedy/approximant_lagrange_greedy_rb.py rename to rrompy/reduction_methods/distributed_greedy/rb_distributed_greedy.py index b4a8720..54fead4 100644 --- a/rrompy/reduction_methods/lagrange_greedy/approximant_lagrange_greedy_rb.py +++ b/rrompy/reduction_methods/distributed_greedy/rb_distributed_greedy.py @@ -1,250 +1,250 @@ # Copyright (C) 2018 by the RROMPy authors # # This file is part of RROMPy. # # RROMPy is free software: you can redistribute it and/or modify # it under the terms of the GNU Lesser General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # RROMPy is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU Lesser General Public License for more details. # # You should have received a copy of the GNU Lesser General Public License # along with RROMPy. If not, see . # import numpy as np from copy import copy -from .generic_approximant_lagrange_greedy import ( - GenericApproximantLagrangeGreedy) -from rrompy.reduction_methods.lagrange import ApproximantLagrangeRB +from .generic_distributed_greedy_approximant import \ + GenericDistributedGreedyApproximant +from rrompy.reduction_methods.distributed import RBDistributed from rrompy.reduction_methods.trained_model import TrainedModelRB as tModel from rrompy.reduction_methods.trained_model import TrainedModelData from rrompy.utilities.base.types import DictAny, HFEng, List from rrompy.utilities.base import verbosityDepth from rrompy.utilities.exception_manager import RROMPyException -__all__ = ['ApproximantLagrangeRBGreedy'] +__all__ = ['RBDistributedGreedy'] -class ApproximantLagrangeRBGreedy(GenericApproximantLagrangeGreedy, - ApproximantLagrangeRB): +class RBDistributedGreedy(GenericDistributedGreedyApproximant, RBDistributed): """ ROM greedy RB approximant computation for parametric problems. Args: HFEngine: HF problem solver. mu0(optional): Default parameter. Defaults to 0. approxParameters(optional): Dictionary containing values for main parameters of approximant. Recognized keys are: - 'POD': whether to compute POD of snapshots; defaults to True; - 'muBounds': list of bounds for parameter values; defaults to - [[0], [1]]; + [0, 1]; + - 'S': number of starting training points; defaults to 2; + - 'sampler': sample point generator; defaults to uniform sampler on + muBounds; - 'greedyTol': uniform error tolerance for greedy algorithm; defaults to 1e-2; - 'maxIter': maximum number of greedy steps; defaults to 1e2; - 'refinementRatio': ratio of training points to be exhausted before training set refinement; defaults to 0.2; - 'nTestPoints': number of test points; defaults to maxIter / refinementRatio; - - 'nTrainPoints': number of starting training points; defaults to - 1; - 'trainSetGenerator': training sample points generator; defaults - to Chebyshev sampler within muBounds; - - 'testSetGenerator': test sample points generator; defaults to - uniform sampler within muBounds. + to Chebyshev sampler within muBounds. Defaults to empty dict. - homogeneized: Whether to homogeneize Dirichlet BCs. Defaults to False. + 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. parameterList: Recognized keys of approximant parameters: - 'POD': whether to compute POD of snapshots; - 'muBounds': list of bounds for parameter values; + - 'S': number of starting training points; + - 'sampler': sample point generator; - 'greedyTol': uniform error tolerance for greedy algorithm; - 'maxIter': maximum number of greedy steps; - 'refinementRatio': ratio of training points to be exhausted before training set refinement; - 'nTestPoints': number of test points; - - 'nTrainPoints': number of starting training points; - - 'trainSetGenerator': training sample points generator; - - 'testSetGenerator': test sample points generator; - - 'robustTol': tolerance for robust Pade' denominator management. + - 'trainSetGenerator': training sample points generator. extraApproxParameters: List of approxParameters keys in addition to mother class's. POD: whether to compute POD of snapshots. muBounds: list of bounds for parameter values. + S: number of test points. + sampler: Sample point generator. greedyTol: uniform error tolerance for greedy algorithm. maxIter: maximum number of greedy steps. refinementRatio: ratio of training points to be exhausted before training set refinement. - nTrainPoints: number of test points. nTestPoints: number of starting training points. trainSetGenerator: training sample points generator. - testSetGenerator: test sample points generator. + estimatorEnergyMatrix: matrix representing inner product for error + estimation. samplingEngine: Sampling engine. uHF: High fidelity solution with wavenumber lastSolvedHF as numpy complex vector. lastSolvedHF: Wavenumber corresponding to last computed high fidelity solution. uApp: Last evaluated approximant as numpy complex vector. As: List of sparse matrices (in CSC format) representing coefficients of linear system matrix wrt theta(mu). bs: List of numpy vectors representing coefficients of linear system RHS wrt theta(mu). thetaAs: List of callables representing coefficients of linear system matrix wrt mu. thetabs: List of callables representing coefficients of linear system RHS wrt mu. ARBs: List of sparse matrices (in CSC format) representing coefficients of compressed linear system matrix wrt theta(mu). bRBs: List of numpy vectors representing coefficients of compressed linear system RHS wrt theta(mu). """ def __init__(self, HFEngine:HFEng, mu0 : complex = 0., approxParameters : DictAny = {}, homogeneized : bool = False, verbosity : int = 10, timestamp : bool = True): self._preInit() super().__init__(HFEngine = HFEngine, mu0 = mu0, approxParameters = approxParameters, homogeneized = homogeneized, verbosity = verbosity, timestamp = timestamp) if self.verbosity >= 10: verbosityDepth("INIT", "Computing affine blocks of system.", timestamp = self.timestamp) self.As = self.HFEngine.affineLinearSystemA(self.mu0) self.bs = self.HFEngine.affineLinearSystemb(self.mu0, self.homogeneized) if self.verbosity >= 10: verbosityDepth("DEL", "Done computing affine blocks.", timestamp = self.timestamp) self._postInit() @property def R(self): """Value of R.""" return self._S @R.setter def R(self, R): raise RROMPyException(("R is used just to simplify inheritance, and " "its value cannot be changed from that of S.")) def errorEstimator(self, mus:List[np.complex]) -> List[np.complex]: """ Standard residual-based error estimator. Unreliable for unstable problems (inf-sup constant is missing). """ self.setupApprox() self.assembleReducedResidualBlocks() nmus = len(mus) nAs = self.trainedModel.data.resAA.shape[1] nbs = self.trainedModel.data.resbb.shape[0] thetaAs = self.trainedModel.data.thetaAs thetabs = self.trainedModel.data.thetabs - radiusA = np.empty((self.S, nAs, nmus), dtype = np.complex) + radiusA = np.empty((len(self.mus), nAs, nmus), dtype = np.complex) radiusb = np.empty((nbs, nmus), dtype = np.complex) verb = self.trainedModel.verbosity self.trainedModel.verbosity = 0 if verb >= 5: mustr = mus if nmus > 2: mustr = "[{} ..({}).. {}]".format(mus[0], nmus - 2, mus[-1]) verbosityDepth("INIT", ("Computing RB solution at mu = " "{}.").format(mustr), timestamp = self.timestamp) for j in range(nmus): mu = mus[j] uApp = self.getApproxReduced(mu) for i in range(nAs): radiusA[:, i, j] = eval(thetaAs[i]) * uApp for i in range(nbs): radiusb[i, j] = eval(thetabs[i]) if verb >= 5: verbosityDepth("DEL", "Done computing RB solution.", timestamp = self.timestamp) self.trainedModel.verbosity = verb # 'ij,jk,ik->k', resbb, radiusb, radiusb.conj() ff = np.sum(self.trainedModel.data.resbb.dot(radiusb) * radiusb.conj(), axis = 0) # 'ijk,jkl,il->l', resAb, radiusA, radiusb.conj() Lf = np.sum(np.tensordot(self.trainedModel.data.resAb, radiusA, 2) * radiusb.conj(), axis = 0) # 'ijkl,klt,ijt->t', resAA, radiusA, radiusA.conj() LL = np.sum(np.tensordot(self.trainedModel.data.resAA, radiusA, 2) * radiusA.conj(), axis = (0, 1)) return np.abs((LL - 2. * np.real(Lf) + ff) / ff) ** .5 def setupApprox(self, plotEst : bool = False): """Compute RB projection matrix.""" if self.checkComputedApprox(): return if self.verbosity >= 5: verbosityDepth("INIT", "Setting up {}.". format(self.name()), timestamp = self.timestamp) self.greedy(plotEst) if self.verbosity >= 7: verbosityDepth("INIT", "Computing projection matrix.", timestamp = self.timestamp) pMat = self.samplingEngine.samples if self.trainedModel is None: self.trainedModel = tModel() self.trainedModel.verbosity = self.verbosity self.trainedModel.timestamp = self.timestamp data = TrainedModelData(self.trainedModel.name(), self.mu0, np.copy(pMat), self.HFEngine.rescalingExp) data.thetaAs = self.HFEngine.affineWeightsA(self.mu0) data.thetabs = self.HFEngine.affineWeightsb(self.mu0, self.homogeneized) data.ARBs, data.bRBs = self.assembleReducedSystem(pMat) data.mus = np.copy(self.mus) self.trainedModel.data = data else: pMatOld = self.trainedModel.data.projMat Sold = pMatOld.shape[1] ARBs, bRBs = self.assembleReducedSystem(pMat[:, Sold :], pMatOld) self.trainedModel.data.ARBs = ARBs self.trainedModel.data.bRBs = bRBs self.trainedModel.data.projMat = np.copy(pMat) self.trainedModel.data.mus = np.copy(self.mus) if self.verbosity >= 7: verbosityDepth("DEL", "Done computing projection matrix.", timestamp = self.timestamp) self.trainedModel.data.approxParameters = copy(self.approxParameters) if self.verbosity >= 5: verbosityDepth("DEL", "Done setting up approximant.", timestamp = self.timestamp) def assembleReducedResidualBlocks(self): """Build affine blocks of RB linear system through projections.""" computeResbb = not hasattr(self.trainedModel.data, "resbb") computeResAb = (not hasattr(self.trainedModel.data, "resAb") - or self.trainedModel.data.resAb.shape[1] != self.S) + or self.trainedModel.data.resAb.shape[1] != len(self.mus)) computeResAA = (not hasattr(self.trainedModel.data, "resAA") - or self.trainedModel.data.resAA.shape[0] != self.S) + or self.trainedModel.data.resAA.shape[0] != len(self.mus)) if computeResbb or computeResAb or computeResAA: pMat = self.trainedModel.data.projMat if self.verbosity >= 7: verbosityDepth("INIT", "Projecting affine terms of residual.", timestamp = self.timestamp) if computeResbb: self.assembleReducedResidualBlocksbb(self.bs, pMat) if computeResAb: self.assembleReducedResidualBlocksAb(self.As, self.bs, pMat) if computeResAA: self.assembleReducedResidualBlocksAA(self.As, pMat) if self.verbosity >= 7: verbosityDepth("DEL", ("Done setting up affine decomposition " "of residual."), timestamp = self.timestamp) diff --git a/rrompy/sampling/linear_problem/__init__.py b/rrompy/sampling/linear_problem/__init__.py index 31e0790..bd43e04 100644 --- a/rrompy/sampling/linear_problem/__init__.py +++ b/rrompy/sampling/linear_problem/__init__.py @@ -1,31 +1,31 @@ # Copyright (C) 2018 by the RROMPy authors # # This file is part of RROMPy. # # RROMPy is free software: you can redistribute it and/or modify # it under the terms of the GNU Lesser General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # RROMPy is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU Lesser General Public License for more details. # # You should have received a copy of the GNU Lesser General Public License # along with RROMPy. If not, see . # from .sampling_engine_krylov import SamplingEngineKrylov from .sampling_engine_arnoldi import SamplingEngineArnoldi -from .sampling_engine_lagrange import SamplingEngineLagrange -from .sampling_engine_lagrange_pod import SamplingEngineLagrangePOD +from .sampling_engine_distributed import SamplingEngineDistributed +from .sampling_engine_distributed_pod import SamplingEngineDistributedPOD __all__ = [ 'SamplingEngineKrylov', 'SamplingEngineArnoldi', - 'SamplingEngineLagrange', - 'SamplingEngineLagrangePOD' + 'SamplingEngineDistributed', + 'SamplingEngineDistributedPOD' ] diff --git a/rrompy/sampling/linear_problem/sampling_engine_lagrange.py b/rrompy/sampling/linear_problem/sampling_engine_distributed.py similarity index 97% rename from rrompy/sampling/linear_problem/sampling_engine_lagrange.py rename to rrompy/sampling/linear_problem/sampling_engine_distributed.py index fde4253..0d76e9d 100644 --- a/rrompy/sampling/linear_problem/sampling_engine_lagrange.py +++ b/rrompy/sampling/linear_problem/sampling_engine_distributed.py @@ -1,93 +1,93 @@ # Copyright (C) 2018 by the RROMPy authors # # This file is part of RROMPy. # # RROMPy is free software: you can redistribute it and/or modify # it under the terms of the GNU Lesser General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # RROMPy is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU Lesser General Public License for more details. # # You should have received a copy of the GNU Lesser General Public License # along with RROMPy. If not, see . # import numpy as np from rrompy.sampling.base.sampling_engine_base import SamplingEngineBase from rrompy.utilities.base.types import Np1D, Np2D from rrompy.utilities.base import verbosityDepth from rrompy.utilities.exception_manager import RROMPyException -__all__ = ['SamplingEngineLagrange'] +__all__ = ['SamplingEngineDistributed'] -class SamplingEngineLagrange(SamplingEngineBase): +class SamplingEngineDistributed(SamplingEngineBase): """HERE""" nameBase = 1 def preprocesssamples(self, idxs:Np1D): if self.samples is None: return return self.samples[:, idxs] def postprocessu(self, u:Np1D, overwrite : bool = False): return u def _getSampleConcurrence(self, mu:complex, previous:Np1D, homogeneized : bool = False) -> Np1D: samplesOld = self.preprocesssamples(previous) RHS = self.HFEngine.b(mu, len(previous), homogeneized = homogeneized) for i in range(1, len(previous) + 1): RHS -= self.HFEngine.A(mu, i).dot(samplesOld[:, - i]) return self.solveLS(mu, RHS = RHS, homogeneized = homogeneized) def nextSample(self, mu:complex, overwrite : bool = False, homogeneized : bool = False) -> Np1D: ns = self.nsamples muidxs = np.nonzero(self.mus[:ns] == mu)[0] if len(muidxs) > 0: u = self._getSampleConcurrence(mu, np.sort(muidxs), homogeneized) else: u = self.solveLS(mu, homogeneized = homogeneized) u = self.postprocessu(u, overwrite = overwrite) if overwrite: self.samples[:, ns] = u self.mus[ns] = mu else: if ns == 0: self.samples = u[:, None] else: self.samples = np.hstack((self.samples, u[:, None])) self.mus = self.mus + [mu] self.nsamples += 1 return u def iterSample(self, mus:Np1D, homogeneized : bool = False) -> Np2D: if self.verbosity >= 5: verbosityDepth("INIT", "Starting sampling iterations.", timestamp = self.timestamp) n = mus.size if n <= 0: raise RROMPyException(("Number of samples must be positive.")) self.resetHistory() if self.verbosity >= 7: verbosityDepth("MAIN", "Computing sample {}/{}.".format(1, n), timestamp = self.timestamp) u = self.nextSample(mus[0], homogeneized = homogeneized) if n > 1: self.preallocateSamples(u, mus[0], n) for j in range(1, n): if self.verbosity >= 7: verbosityDepth("MAIN", "Computing sample {}/{}.".format(j + 1, n), timestamp = self.timestamp) self.nextSample(mus[j], overwrite = True, homogeneized = homogeneized) if self.verbosity >= 5: verbosityDepth("DEL", "Finished sampling iterations.", timestamp = self.timestamp) return self.samples diff --git a/rrompy/sampling/linear_problem/sampling_engine_lagrange_pod.py b/rrompy/sampling/linear_problem/sampling_engine_distributed_pod.py similarity index 94% rename from rrompy/sampling/linear_problem/sampling_engine_lagrange_pod.py rename to rrompy/sampling/linear_problem/sampling_engine_distributed_pod.py index e203883..a1624d2 100644 --- a/rrompy/sampling/linear_problem/sampling_engine_lagrange_pod.py +++ b/rrompy/sampling/linear_problem/sampling_engine_distributed_pod.py @@ -1,83 +1,83 @@ # Copyright (C) 2018 by the RROMPy authors # # This file is part of RROMPy. # # RROMPy is free software: you can redistribute it and/or modify # it under the terms of the GNU Lesser General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # RROMPy is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU Lesser General Public License for more details. # # You should have received a copy of the GNU Lesser General Public License # along with RROMPy. If not, see . # import numpy as np from rrompy.sampling.base.pod_engine import PODEngine -from .sampling_engine_lagrange import SamplingEngineLagrange +from .sampling_engine_distributed import SamplingEngineDistributed from rrompy.utilities.base.types import Np1D from rrompy.utilities.base import verbosityDepth -__all__ = ['SamplingEngineLagrangePOD'] +__all__ = ['SamplingEngineDistributedPOD'] -class SamplingEngineLagrangePOD(SamplingEngineLagrange): +class SamplingEngineDistributedPOD(SamplingEngineDistributed): """HERE""" def resetHistory(self): super().resetHistory() self.RPOD = None def popSample(self): if hasattr(self, "nsamples") and self.nsamples > 1: self.RPOD = self.RPOD[: -1, : -1] super().popSample() @property def HFEngine(self): """Value of HFEngine. Its assignment resets history.""" return self._HFEngine @HFEngine.setter def HFEngine(self, HFEngine): self._HFEngine = HFEngine self.resetHistory() self.PODEngine = PODEngine(self._HFEngine) def preprocesssamples(self, idxs:Np1D): idxMax = np.max(idxs) + 1 sampleBase = super().preprocesssamples(np.arange(idxMax)) RPODBase = self.RPOD[: idxMax, idxs] return sampleBase.dot(RPODBase) def postprocessu(self, u:Np1D, overwrite : bool = False): if self.verbosity >= 10: verbosityDepth("INIT", "Starting orthogonalization.", timestamp = self.timestamp) ns = self.nsamples if ns == 0: u, r, _ = self.PODEngine.GS(u, np.empty((0, 0))) r = r[0] else: u, r, _ = self.PODEngine.GS(u, self.samples[:, : ns], ns) if overwrite: self.RPOD[: ns + 1, ns] = r else: if ns == 0: self.RPOD = r.reshape((1, 1)) else: self.RPOD=np.block([[ self.RPOD, r[:-1, None]], [np.zeros((1, ns)), r[-1]]]) if self.verbosity >= 10: verbosityDepth("DEL", "Done orthogonalizing.", timestamp = self.timestamp) return u def preallocateSamples(self, u:Np1D, mu:np.complex, n:int): super().preallocateSamples(u, mu, n) r = self.RPOD self.RPOD = np.zeros((n, n), dtype = u.dtype) self.RPOD[0, 0] = r[0, 0] diff --git a/rrompy/utilities/base/types.py b/rrompy/utilities/base/types.py index e93358d..4c8b273 100644 --- a/rrompy/utilities/base/types.py +++ b/rrompy/utilities/base/types.py @@ -1,51 +1,52 @@ # Copyright (C) 2018 by the RROMPy authors # # This file is part of RROMPy. # # RROMPy is free software: you can redistribute it and/or modify # it under the terms of the GNU Lesser General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # RROMPy is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU Lesser General Public License for more details. # # You should have received a copy of the GNU Lesser General Public License # along with RROMPy. If not, see . # from typing import TypeVar, List, Tuple, Dict, Any __all__ = ['TupleAny','ListAny','DictAny','ScOp','Np1D','Np2D','Np1DLst', - 'N2FSExpr','FenExpr','FenFunc','HFEng','ROMEng','sampleEng', - 'GenExpr','strLst','BfSExpr'] + 'N2FSExpr','FenExpr','FenFunc','FenFuncSpace','HFEng','ROMEng', + 'sampleEng','GenExpr','strLst','BfSExpr'] # ANY TupleAny = Tuple[Any] ListAny = List[Any] DictAny = Dict[Any, Any] # SCIPY ScOp = TypeVar("Scipy sparse matrix for space operator") # NUMPY Np1D = TypeVar("NumPy 1D array") Np2D = TypeVar("NumPy 2D array") Np1DLst = TypeVar("NumPy 1D array or list of NumPy 1D array") N2FSExpr = TypeVar("NumPy 2D array, float or str") # FENICS FenExpr = TypeVar("FEniCS expression") FenFunc = TypeVar("FEniCS function") +FenFuncSpace = TypeVar("FEniCS function space") # ENGINES HFEng = TypeVar("High fidelity engine") ROMEng = TypeVar("ROM engine") sampleEng = TypeVar("Sampling engine") # OTHERS GenExpr = TypeVar("Generic expression") strLst = TypeVar("str or list of str") BfSExpr = TypeVar("Boolean function or string") diff --git a/rrompy/reduction_methods/lagrange/__init__.py b/rrompy/utilities/fenics/__init__.py similarity index 67% rename from rrompy/reduction_methods/lagrange/__init__.py rename to rrompy/utilities/fenics/__init__.py index 822bd2a..6b2e26a 100644 --- a/rrompy/reduction_methods/lagrange/__init__.py +++ b/rrompy/utilities/fenics/__init__.py @@ -1,29 +1,34 @@ # Copyright (C) 2018 by the RROMPy authors # # This file is part of RROMPy. # # RROMPy is free software: you can redistribute it and/or modify # it under the terms of the GNU Lesser General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # RROMPy is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU Lesser General Public License for more details. # # You should have received a copy of the GNU Lesser General Public License # along with RROMPy. If not, see . # -from .generic_approximant_lagrange import GenericApproximantLagrange -from .approximant_lagrange_pade import ApproximantLagrangePade -from .approximant_lagrange_rb import ApproximantLagrangeRB +from .fenics_constants import fenZERO, fenZEROS, fenONE, fenONES, bdrTrue, bdrFalse +from .fenics_norms import L2NormMatrix, H1NormMatrix, elasticNormMatrix __all__ = [ - 'GenericApproximantLagrange', - 'ApproximantLagrangePade', - 'ApproximantLagrangeRB' - ] + 'fenZERO', + 'fenZEROS', + 'fenONE', + 'fenONES', + 'bdrTrue', + 'bdrFalse', + 'L2NormMatrix', + 'H1NormMatrix', + 'elasticNormMatrix' + ] diff --git a/rrompy/utilities/base/fenics.py b/rrompy/utilities/fenics/fenics_constants.py similarity index 100% rename from rrompy/utilities/base/fenics.py rename to rrompy/utilities/fenics/fenics_constants.py diff --git a/rrompy/utilities/fenics/fenics_norms.py b/rrompy/utilities/fenics/fenics_norms.py new file mode 100644 index 0000000..f8af47d --- /dev/null +++ b/rrompy/utilities/fenics/fenics_norms.py @@ -0,0 +1,48 @@ +# Copyright (C) 2018 by the RROMPy authors +# +# This file is part of RROMPy. +# +# RROMPy is free software: you can redistribute it and/or modify +# it under the terms of the GNU Lesser General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# RROMPy is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with RROMPy. If not, see . +# + +import fenics as fen +from rrompy.utilities.base.types import Np2D, FenFunc, FenFuncSpace +from scipy.sparse import csr_matrix + +__all__ = ['L2NormMatrix', 'H1NormMatrix', 'elasticNormMatrix'] + +def _fen2np(expr): + matFen = fen.as_backend_type(fen.assemble(expr)).mat() + return csr_matrix(matFen.getValuesCSR()[::-1], shape = matFen.size) + +def L2NormMatrix(V:FenFuncSpace) -> Np2D: + u = fen.TrialFunction(V) + v = fen.TestFunction(V) + return _fen2np(fen.dot(u, v) * fen.dx) + +def H1NormMatrix(V:FenFuncSpace, w : FenFunc = 0.) -> Np2D: + u = fen.TrialFunction(V) + v = fen.TestFunction(V) + return _fen2np((w * fen.dot(u, v) + + fen.dot(fen.grad(u), fen.grad(v))) * fen.dx) + +def elasticNormMatrix(V:FenFuncSpace, l_:FenFunc, m_:FenFunc, + w : FenFunc = 0.) -> Np2D: + u = fen.TrialFunction(V) + v = fen.TestFunction(V) + epsilon = lambda f: 0.5 * (fen.grad(f) + fen.nabla_grad(f)) + sigma = (l_ * fen.div(u) * fen.Identity(u.geometric_dimension()) + + 2. * m_ * epsilon(u)) + return _fen2np((w * fen.dot(u, v) + fen.inner(sigma, epsilon(v))) * fen.dx) + diff --git a/tests/test_1_utilities/fenics_const.py b/tests/test_1_utilities/fenics_const.py index 1481b7c..54e834b 100644 --- a/tests/test_1_utilities/fenics_const.py +++ b/tests/test_1_utilities/fenics_const.py @@ -1,21 +1,23 @@ # Copyright (C) 2018 by the RROMPy authors # # This file is part of RROMPy. # # RROMPy is free software: you can redistribute it and/or modify # it under the terms of the GNU Lesser General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # RROMPy is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU Lesser General Public License for more details. # # You should have received a copy of the GNU Lesser General Public License # along with RROMPy. If not, see . # def test_fenics_loads(): - from rrompy.utilities.base.fenics import fenZERO, fenZEROS, fenONE, fenONES + from rrompy.utilities.fenics import fenZERO, fenZEROS, fenONE, fenONES + from rrompy.utilities.fenics import (L2NormMatrix, H1NormMatrix, + elasticNormMatrix) diff --git a/tests/test_1_utilities/sampling_multi_point.py b/tests/test_1_utilities/sampling_multi_point.py index b7ffd2e..d059c73 100644 --- a/tests/test_1_utilities/sampling_multi_point.py +++ b/tests/test_1_utilities/sampling_multi_point.py @@ -1,58 +1,58 @@ # Copyright (C) 2018 by the RROMPy authors # # This file is part of RROMPy. # # RROMPy is free software: you can redistribute it and/or modify # it under the terms of the GNU Lesser General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # RROMPy is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU Lesser General Public License for more details. # # You should have received a copy of the GNU Lesser General Public License # along with RROMPy. If not, see . # import numpy as np import scipy.sparse as sp from rrompy.hfengines.base import MatrixEngineBase as MEB -from rrompy.sampling.linear_problem import (SamplingEngineLagrange, - SamplingEngineLagrangePOD) +from rrompy.sampling.linear_problem import (SamplingEngineDistributed, + SamplingEngineDistributedPOD) -def test_lagrange(): +def test_distributed(): N = 100 mus = np.linspace(5, 15, 11) + .5j solver = MEB(verbosity = 0) solver.nAs = 2 solver.As = [sp.spdiags([np.arange(1, 1 + N)], [0], N, N), - sp.eye(N)] solver.nbs = 1 solver.bs = [np.exp(1.j * np.linspace(0, -np.pi, N))] - samplingEngine = SamplingEngineLagrange(solver, verbosity = 0) + samplingEngine = SamplingEngineDistributed(solver, verbosity = 0) samples = samplingEngine.iterSample(mus) assert samples.shape == (100, 11) assert np.isclose(np.linalg.norm(samples), 8.59778606421386, rtol = 1e-5) -def test_lagrange_pod(): +def test_distributed_pod(): N = 100 mus = np.linspace(5, 15, 11) + .5j solver = MEB(verbosity = 0) solver.nAs = 2 solver.As = [sp.spdiags([np.arange(1, 1 + N)], [0], N, N), - sp.eye(N)] solver.nbs = 1 solver.bs = [np.exp(1.j * np.linspace(0, -np.pi, N))] - samplingEngine = SamplingEngineLagrangePOD(solver, verbosity = 0) + samplingEngine = SamplingEngineDistributedPOD(solver, verbosity = 0) samples = samplingEngine.iterSample(mus) assert samples.shape == (100, 11) assert np.isclose(np.linalg.norm(samples), 3.3166247903553994, rtol = 1e-5) assert np.isclose(np.linalg.cond(samples.conj().T.dot(samples)), 1., rtol = 1e-5) diff --git a/tests/test_3_reduction_methods/lagrange_pade.py b/tests/test_3_reduction_methods/rational_interpolant.py similarity index 73% rename from tests/test_3_reduction_methods/lagrange_pade.py rename to tests/test_3_reduction_methods/rational_interpolant.py index 7d62e72..3402bd3 100644 --- a/tests/test_3_reduction_methods/lagrange_pade.py +++ b/tests/test_3_reduction_methods/rational_interpolant.py @@ -1,68 +1,68 @@ # Copyright (C) 2018 by the RROMPy authors # # This file is part of RROMPy. # # RROMPy is free software: you can redistribute it and/or modify # it under the terms of the GNU Lesser General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # RROMPy is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU Lesser General Public License for more details. # # You should have received a copy of the GNU Lesser General Public License # along with RROMPy. If not, see . # import numpy as np from matrix_fft import matrixFFT -from rrompy.reduction_methods.lagrange import ApproximantLagrangePade as ALP +from rrompy.reduction_methods.distributed import RationalInterpolant as RI from rrompy.utilities.parameter_sampling import (QuadratureSampler as QS, ManualSampler as MS) def test_monomials(capsys): mu = 1.5 solver = matrixFFT() params = {"POD": False, "M": 9, "N": 9, "S": 10, "robustTol": 1e-6, - "interpRcond": 1e-3, "polybasis": "MONOMIAL"} - approx = ALP(solver, 4., params, verbosity = 0) - approx.sampler = QS([1.5, 6.5], "UNIFORM") + "interpRcond": 1e-3, "polybasis": "MONOMIAL", + "sampler": QS([1.5, 6.5], "UNIFORM")} + approx = RI(solver, 4., params, verbosity = 0) approx.setupApprox() out, err = capsys.readouterr() - assert (("poorly conditioned.\nReducing N from 9 to 8 and M" in out) + assert (("poorly conditioned.\nReducing N from 9 to" in out) and ("eigenvalues below tolerance. Reducing N from" in out)) assert len(err) == 0 - assert np.isclose(approx.normErr(mu), .032042037, rtol = 1e-3) + assert np.isclose(approx.normErr(mu), .00773727, rtol = 1e-3) def test_well_cond(): mu = 1.5 solver = matrixFFT() - params = {"POD": False, "M": 9, "N": 9, "S": 10, "robustTol": 1e-6, - "interpRcond": 1e-3, "polybasis": "CHEBYSHEV"} - approx = ALP(solver, 4., params, verbosity = 0) - approx.sampler = QS([1., 7.], "CHEBYSHEV") + params = {"POD": True, "M": 9, "N": 9, "S": 10, "robustTol": 1e-14, + "interpRcond": 1e-10, "polybasis": "CHEBYSHEV", + "sampler": QS([1., 7.], "CHEBYSHEV")} + approx = RI(solver, 4., params, verbosity = 0) approx.setupApprox() for mu in approx.mus: assert np.isclose(approx.normErr(mu), 0., atol = 1e-8) poles = approx.getPoles() for lambda_ in np.arange(1, 8): assert np.isclose(np.min(np.abs(poles - lambda_)), 0., atol = 1e-4) def test_hermite(): mu = 1.5 solver = matrixFFT() - params = {"POD": True, "M": 11, "N": 11, "S": 12, "polybasis": "CHEBYSHEV"} - approx = ALP(solver, 4., params, verbosity = 0) sampler0 = QS([1., 7.], "CHEBYSHEV") points = np.tile(sampler0.generatePoints(4)[0], 3) - approx.sampler = MS([1., 7.], points = points) + params = {"POD": True, "M": 11, "N": 11, "S": 12, "polybasis": "CHEBYSHEV", + "sampler": MS([1., 7.], points = points)} + approx = RI(solver, 4., params, verbosity = 0) approx.setupApprox() for mu in approx.mus: assert np.isclose(approx.normErr(mu), 0., atol = 1e-8) poles = approx.getPoles() for lambda_ in np.arange(1, 8): assert np.isclose(np.min(np.abs(poles - lambda_)), 0., atol = 1e-4) - \ No newline at end of file + diff --git a/tests/test_3_reduction_methods/rational_interpolant_greedy.py b/tests/test_3_reduction_methods/rational_interpolant_greedy.py new file mode 100644 index 0000000..1c5fa4e --- /dev/null +++ b/tests/test_3_reduction_methods/rational_interpolant_greedy.py @@ -0,0 +1,63 @@ +# Copyright (C) 2018 by the RROMPy authors +# +# This file is part of RROMPy. +# +# RROMPy is free software: you can redistribute it and/or modify +# it under the terms of the GNU Lesser General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# RROMPy is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with RROMPy. If not, see . +# + +import numpy as np +from matrix_fft import matrixFFT +from rrompy.reduction_methods.distributed_greedy import \ + RationalInterpolantGreedy as RIG + +def test_lax_tolerance(capsys): + solver = matrixFFT() + params = {"POD": True, "muBounds": [1.5, 6.5], "S": 4, + "polybasis": "CHEBYSHEV", "greedyTol": 1e-2} + approx = RIG(solver, 4., params, verbosity = 10) + approx.greedy() + + out, err = capsys.readouterr() + assert "Done computing snapshots (final snapshot count: 10)." in out + assert len(err) == 0 + assert np.isclose(approx.normErr(0), .0077041389, rtol = 1e-3) + +def test_samples_at_poles(): + solver = matrixFFT() + params = {"POD": True, "muBounds": [1.5, 6.5], "S": 4, "nTestPoints": 100, + "polybasis": "CHEBYSHEV", "greedyTol": 1e-5} + approx = RIG(solver, 4., params, verbosity = 0) + approx.greedy() + + for mu in approx.mus: + assert np.isclose(approx.normErr(mu) / (1e-15 + approx.normHF(mu)), + 0., atol = 1e-4) + + poles = approx.getPoles() + for lambda_ in range(2, 7): + assert np.isclose(np.min(np.abs(poles - lambda_)), 0., atol = 1e-3) + assert np.isclose(np.min(np.abs(approx.mus - lambda_)), 0., + atol = 1e-1) + +def test_maxIter(): + solver = matrixFFT() + params = {"POD": True, "muBounds": [1.5, 6.5], "S": 5, "nTestPoints": 500, + "polybasis": "CHEBYSHEV", "greedyTol": 1e-6, "maxIter": 10} + approx = RIG(solver, 4., params, verbosity = 0) + approx.input = lambda: "N" + approx.greedy() + + assert len(approx.mus) == 10 + _, _, maxEst = approx.getMaxErrorEstimator(approx.muTest) + assert maxEst > 1e-6 diff --git a/tests/test_3_reduction_methods/taylor_pade.py b/tests/test_3_reduction_methods/rational_pade.py similarity index 93% rename from tests/test_3_reduction_methods/taylor_pade.py rename to tests/test_3_reduction_methods/rational_pade.py index 83176c8..3d741a1 100644 --- a/tests/test_3_reduction_methods/taylor_pade.py +++ b/tests/test_3_reduction_methods/rational_pade.py @@ -1,93 +1,93 @@ # Copyright (C) 2018 by the RROMPy authors # # This file is part of RROMPy. # # RROMPy is free software: you can redistribute it and/or modify # it under the terms of the GNU Lesser General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # RROMPy is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU Lesser General Public License for more details. # # You should have received a copy of the GNU Lesser General Public License # along with RROMPy. If not, see . # import os import numpy as np from matrix_fft import matrixFFT -from rrompy.reduction_methods.taylor import ApproximantTaylorPade as ATP +from rrompy.reduction_methods.centered import RationalPade as RP def test_rho(capsys): mu = 1.5 mu0 = 2. + 1.j solver = matrixFFT() uh = solver.solve(mu) params = {"POD": False, "rho": 3., "M": 4, "N": 5, "E": 10, "robustTol": 1e-6, "sampleType": "Krylov"} - approx = ATP(solver, mu0, params, verbosity = 0) + approx = RP(solver, mu0, params, verbosity = 0) approx.setupApprox() out, err = capsys.readouterr() assert ("Smallest 2 eigenvalues below tolerance. Reducing N from 5 to 4 " "and E from 10 to 9.") in out assert len(err) == 0 if not os.path.isdir("./.pytest_cache"): os.mkdir("./.pytest_cache") filesOut = [x for x in os.listdir("./.pytest_cache") if (x[-4:] == ".pkl" and x[:6] == "outRho")] for fileOut in filesOut: os.remove("./.pytest_cache/" + fileOut) fileStored = approx.storeTrainedModel(".pytest_cache/outRho") filesOut = [x for x in os.listdir("./.pytest_cache") if (x[-4:] == ".pkl" and x[:6] == "outRho")] assert len(filesOut) == 1 assert filesOut[0] == fileStored[- len(filesOut[0]) :] uhP1 = approx.getApprox(mu) errP = approx.getErr(mu) errNP = approx.normErr(mu) assert np.allclose(np.abs(errP - (uhP1 - uh)), 0., rtol = 1e-3) assert np.isclose(solver.norm(errP), errNP, rtol = 1e-3) resP = approx.getRes(mu) resNP = approx.normRes(mu) assert np.isclose(solver.norm(resP), resNP, rtol = 1e-3) assert np.allclose(np.abs(resP - (solver.b(mu) - solver.A(mu).dot(uhP1))), 0., rtol = 1e-3) del approx - approx = ATP(solver, mu0, {"E": 3}, verbosity = 0) + approx = RP(solver, mu0, {"E": 3}, verbosity = 0) approx.loadTrainedModel(fileStored) for fileOut in filesOut: os.remove("./.pytest_cache/" + fileOut) uhP2 = approx.getApprox(mu) assert np.allclose(np.abs(uhP1 - uhP2), 0., rtol = 1e-3) def test_E_warn(capsys): mu = 1.5 mu0 = 2. + 1.j solver = matrixFFT() uh = solver.solve(mu) params = {"POD": True, "rho": 3., "M": 4, "N": 5, "E": 2} - approx = ATP(solver, mu0, params, verbosity = 0) + approx = RP(solver, mu0, params, verbosity = 0) approx.setupApprox() out, err = capsys.readouterr() assert "Prescribed E is too small. Updating E to M + N." in out assert len(err) == 0 uhP = approx.getApprox(mu) errP = approx.getErr(mu) errNP = approx.normErr(mu) assert np.allclose(np.abs(errP - (uhP - uh)), 0., rtol = 1e-3) assert np.isclose(errNP, 0.1372966, rtol = 1e-1) ress = approx.getResidues() condres = np.linalg.cond(solver.innerProduct(ress, ress)) assert np.isclose(condres, 36.63625, rtol = 1e-3) poles = approx.getPoles() assert np.isclose(np.min(np.abs(poles - 2.)), 0., atol = 1e-5) assert np.isclose(np.min(np.abs(poles - 1.)), 0., atol = 1e-3) assert np.isclose(np.min(np.abs(poles - 3.)), 0., atol = 1e-3) \ No newline at end of file diff --git a/tests/test_3_reduction_methods/taylor_rb.py b/tests/test_3_reduction_methods/rb_centered.py similarity index 90% rename from tests/test_3_reduction_methods/taylor_rb.py rename to tests/test_3_reduction_methods/rb_centered.py index 42f0f9d..dcc0abe 100644 --- a/tests/test_3_reduction_methods/taylor_rb.py +++ b/tests/test_3_reduction_methods/rb_centered.py @@ -1,50 +1,50 @@ # Copyright (C) 2018 by the RROMPy authors # # This file is part of RROMPy. # # RROMPy is free software: you can redistribute it and/or modify # it under the terms of the GNU Lesser General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # RROMPy is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU Lesser General Public License for more details. # # You should have received a copy of the GNU Lesser General Public License # along with RROMPy. If not, see . # import numpy as np from matrix_fft import matrixFFT -from rrompy.reduction_methods.taylor import ApproximantTaylorRB as ATR +from rrompy.reduction_methods.centered import RBCentered as RBC def test_R(): mu = 1.5 mu0 = 2. + 1.j solver = matrixFFT() uh = solver.solve(mu) params = {"POD": True, "R": 5, "E": 10, "sampleType": "Krylov"} - approx = ATR(solver, mu0, params, verbosity = 0) + approx = RBC(solver, mu0, params, verbosity = 0) approx.setupApprox() uhP = approx.getApprox(mu) errP = approx.getErr(mu) errNP = approx.normErr(mu) assert np.allclose(np.abs(errP - (uhP - uh)), 0., rtol = 1e-3) assert np.isclose(errNP, 0.023691832, rtol = 1e-1) poles = approx.getPoles() assert np.isclose(np.min(np.abs(poles - 2.)), 0., atol = 1e-4) assert np.isclose(np.min(np.abs(poles - 1.)), 0., atol = 1e-2) assert np.isclose(np.min(np.abs(poles - 3.)), 0., atol = 1e-2) def test_moments(): mu0 = 2. + 1.j solver = matrixFFT() params = {"POD": True, "E": 10, "sampleType": "Krylov"} - approx = ATR(solver, mu0, params, verbosity = 0) + approx = RBC(solver, mu0, params, verbosity = 0) approx.setupApprox() assert np.isclose(approx.normErr(mu0), 0., atol = 1e-10) diff --git a/tests/test_3_reduction_methods/lagrange_rb.py b/tests/test_3_reduction_methods/rb_distributed.py similarity index 77% rename from tests/test_3_reduction_methods/lagrange_rb.py rename to tests/test_3_reduction_methods/rb_distributed.py index db275bd..d2c8ed3 100644 --- a/tests/test_3_reduction_methods/lagrange_rb.py +++ b/tests/test_3_reduction_methods/rb_distributed.py @@ -1,58 +1,56 @@ # Copyright (C) 2018 by the RROMPy authors # # This file is part of RROMPy. # # RROMPy is free software: you can redistribute it and/or modify # it under the terms of the GNU Lesser General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # RROMPy is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU Lesser General Public License for more details. # # You should have received a copy of the GNU Lesser General Public License # along with RROMPy. If not, see . # import numpy as np from matrix_fft import matrixFFT -from rrompy.reduction_methods.lagrange import ApproximantLagrangeRB as ALR +from rrompy.reduction_methods.distributed import RBDistributed as RBD from rrompy.utilities.parameter_sampling import (QuadratureSampler as QS, ManualSampler as MS) def test_LS(capsys): solver = matrixFFT() - params = {"POD": True, "R": 5, "S": 10} - approx = ALR(solver, 4., params, verbosity = 0) - approx.sampler = QS([1., 7.], "CHEBYSHEV") + params = {"POD": True, "R": 5, "S": 10, + "sampler": QS([1., 7.], "CHEBYSHEV")} + approx = RBD(solver, 4., params, verbosity = 0) approx.setupApprox() for mu in approx.mus: assert not np.isclose(approx.normErr(mu), 0., atol = 1e-7) approx.POD = False approx.setupApprox() for mu in approx.mus[approx.R :]: assert not np.isclose(approx.normErr(mu), 0., atol = 1e-3) def test_interp(): solver = matrixFFT() - params = {"POD": False, "S": 10} - approx = ALR(solver, 4., params, verbosity = 0) - approx.sampler = QS([1., 7.], "CHEBYSHEV") + params = {"POD": False, "S": 10, "sampler": QS([1., 7.], "CHEBYSHEV")} + approx = RBD(solver, 4., params, verbosity = 0) approx.setupApprox() for mu in approx.mus: assert np.isclose(approx.normErr(mu), 0., atol = 1e-7) def test_hermite(): mu = 1.5 solver = matrixFFT() - params = {"POD": True, "S": 12} - approx = ALR(solver, 4., params, verbosity = 0) sampler0 = QS([1., 7.], "CHEBYSHEV") points = np.tile(sampler0.generatePoints(4)[0], 3) - approx.sampler = MS([1., 7.], points = points) + params = {"POD": True, "S": 12, "sampler": MS([1., 7.], points = points)} + approx = RBD(solver, 4., params, verbosity = 0) approx.setupApprox() for mu in approx.mus: assert np.isclose(approx.normErr(mu), 0., atol = 1e-8) diff --git a/tests/test_3_reduction_methods/rb_distributed_greedy.py b/tests/test_3_reduction_methods/rb_distributed_greedy.py new file mode 100644 index 0000000..3691931 --- /dev/null +++ b/tests/test_3_reduction_methods/rb_distributed_greedy.py @@ -0,0 +1,55 @@ +# Copyright (C) 2018 by the RROMPy authors +# +# This file is part of RROMPy. +# +# RROMPy is free software: you can redistribute it and/or modify +# it under the terms of the GNU Lesser General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# RROMPy is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with RROMPy. If not, see . +# + +import numpy as np +from matrix_fft import matrixFFT +from rrompy.reduction_methods.distributed_greedy import RBDistributedGreedy \ + as RBDG + +def test_lax_tolerance(capsys): + solver = matrixFFT() + params = {"POD": True, "muBounds": [1.5, 6.5], "S": 4, "greedyTol": 1e-2} + approx = RBDG(solver, 4., params, verbosity = 10) + approx.greedy() + + out, err = capsys.readouterr() + assert "Done computing snapshots (final snapshot count: 10)." in out + assert len(err) == 0 + + assert len(approx.mus) == 10 + _, _, maxEst = approx.getMaxErrorEstimator(approx.muTest) + assert maxEst < 1e-2 + assert np.isclose(approx.normErr(0), .001776801, rtol = 1e-3) + +def test_samples_at_poles(): + solver = matrixFFT() + params = {"POD": True, "muBounds": [1.5, 6.5], "S": 4, "nTestPoints": 100, + "greedyTol": 1e-5} + approx = RBDG(solver, 4., params, verbosity = 0) + approx.greedy() + + for mu in approx.mus: + assert np.isclose(approx.normErr(mu) / (1e-15 + approx.normHF(mu)), + 0., atol = 1e-4) + + poles = approx.getPoles() + for lambda_ in range(2, 7): + assert np.isclose(np.min(np.abs(poles - lambda_)), 0., atol = 1e-3) + assert np.isclose(np.min(np.abs(approx.mus - lambda_)), 0., + atol = 1e-1) +